Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion internal/dlopen/dlopen.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"errors"
"fmt"
"runtime"
"strings"
"unsafe"
)

Expand All @@ -41,9 +42,16 @@ type LibHandle struct {
// opened. Callers are responsible for closing the handler. If no library can
// be successfully opened, an error is returned.
func GetHandle(libs []string) (*LibHandle, error) {
// Locking the thread is critical here as the dlerror() is thread local so
// go should not reschedule this onto another thread.
runtime.LockOSThread()
defer runtime.UnlockOSThread()

msgs := make([]string, 0, len(libs))
for _, name := range libs {
libname := C.CString(name)
defer C.free(unsafe.Pointer(libname))
C.dlerror()
handle := C.dlopen(libname, C.RTLD_LAZY)
if handle != nil {
h := &LibHandle{
Expand All @@ -52,8 +60,14 @@ func GetHandle(libs []string) (*LibHandle, error) {
}
return h, nil
}
if e := C.dlerror(); e != nil {
msgs = append(msgs, C.GoString(e))
}
}
if len(msgs) == 0 {
return nil, ErrSoNotFound
}
return nil, ErrSoNotFound
return nil, fmt.Errorf("%w: %s", ErrSoNotFound, strings.Join(msgs, "; "))
}

// GetSymbolPointer takes a symbol name and returns a pointer to the symbol.
Expand Down
20 changes: 20 additions & 0 deletions internal/dlopen/dlopen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
package dlopen

import (
"errors"
"fmt"
"strings"
"sync"
"testing"
)
Expand Down Expand Up @@ -64,6 +66,24 @@ func TestDlopen(t *testing.T) {
}
}

func TestGetHandleError(t *testing.T) {
libs := []string{"libstrange1.so", "libstrange2.so"}

_, err := GetHandle(libs)
if err == nil {
t.Fatal("expected GetHandle to fail")
}
if !errors.Is(err, ErrSoNotFound) {
t.Errorf("expected ErrSoNotFound, got %v", err)
}
// the reason dlopen() gave for each name should be in the message
for _, name := range libs {
if !strings.Contains(err.Error(), name) {
t.Errorf("error does not mention %q: %v", name, err)
}
}
}

// Note this is not a reliable reproducer for the problem.
// It depends on the fact the it first generates some dlerror() errors
// by using non existent libraries.
Expand Down