diff --git a/internal/dlopen/dlopen.go b/internal/dlopen/dlopen.go index 91527fd3..75c9d2ac 100644 --- a/internal/dlopen/dlopen.go +++ b/internal/dlopen/dlopen.go @@ -25,6 +25,7 @@ import ( "errors" "fmt" "runtime" + "strings" "unsafe" ) @@ -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{ @@ -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. diff --git a/internal/dlopen/dlopen_test.go b/internal/dlopen/dlopen_test.go index 60fbb57e..0fd8e476 100644 --- a/internal/dlopen/dlopen_test.go +++ b/internal/dlopen/dlopen_test.go @@ -15,7 +15,9 @@ package dlopen import ( + "errors" "fmt" + "strings" "sync" "testing" ) @@ -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.