Skip to content
Closed
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
36 changes: 29 additions & 7 deletions purego_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"os"
"runtime"
"strings"

"github.com/ebitengine/purego"
)
Expand All @@ -17,7 +18,7 @@ const (
)

func loadLibrary(name string) (uintptr, error) {
if runtime.GOOS == "linux" && !isDynamicBinary() {
if runtime.GOOS == "linux" && !canDlopen() {
return 0, fmt.Errorf("not a dynamic binary")
}

Expand All @@ -29,23 +30,44 @@ func loadLibrary(name string) (uintptr, error) {
return handle, nil
}

func isDynamicBinary() bool {
// canDlopen reports whether dlopen is safe to call from this binary.
// Dynamic binaries can always dlopen. Static binaries can only dlopen
// safely if they were linked against musl libc, which has a fully
// functional dlopen implementation even in static executables.
// On glibc, dlopen from static binaries is not supported and may crash.
func canDlopen() bool {
fileName, err := os.Executable()
if err != nil {
panic(err)
return false
}

fl, err := elf.Open(fileName)
if err != nil {
panic(err)
return false
}

defer fl.Close()

_, err = fl.DynamicSymbols()
if err == nil {
// Dynamic binaries can always dlopen
if _, err := fl.DynamicSymbols(); err == nil {
return true
}

// Static binary — check if it was linked with musl by reading the
// ELF .comment section, which contains the compiler/linker identification.
// Musl-linked binaries typically have no .comment or have "GCC" without
// glibc markers. More reliably, we look for "musl" in the ELF interpreter
// (PT_INTERP program header), which musl's linker sets even for static-pie.
for _, prog := range fl.Progs {
if prog.Type == elf.PT_INTERP {
interp := make([]byte, prog.Filesz)
if _, err := prog.ReadAt(interp, 0); err == nil {
if strings.Contains(string(interp), "musl") {
return true
}
}
break
}
}

return false
}
Loading