ebitengine / purego

Apache License 2.0
2.16k stars 68 forks source link

Give the ability to perform dlopen(NULL) #134

Closed eliottness closed 1 year ago

eliottness commented 1 year ago

As per the man page, dlopen(NULL) is supposed to return the equivalent of the macro RTLD_DEFAULT which not accessible from go code obviously and is implementation defined. This function is especially useful to run already loaded symbols like the ones loaded with the pragma go:cgo_import_dynamic.

But since we pass a string to Dlopen, a nil value cannot be passed as parameter. How could we give access to this feature ?

TotallyGamerJet commented 1 year ago

This should be easy to implement. Given an empty string purego pass a null pointer. However, I think it's important to decide if the feature is needed. Have you noticed a bottleneck caused when searching for shared objects? Each feature has a cost so it's important to make sure we are okay with paying it.

eliottness commented 1 year ago

think it's important to decide if the feature is needed

I see 2 advantages over some other methods to perform symbol lookup on already loaded symbols.

The edge this technique has over a usual dlopen("libc.so.6") is the ability to be agnostic of the library name. For example if we already know that the libc (or its equivalent on other platform) is loaded in some way or another, we can be pretty sure that dlsym(RTLD_DEFAULT, "strlen") will succeed.

The edge this method has over go:cgo_import_dynamic is the ability to perform logic on the symbol lookup. For example if the symbol "strnlen" cannot be found, we fallback on "strlen".

This should be easy to implement. Given an empty string purego pass a null pointer

Concerning the implementation I was more inclined to add a new function to the api. Something like this:

func DefaultDSO() (uintptr, error) {
    u, _, _ := SyscallN(dlopenABI0, 0, uintptr(RTLD_GLOBAL)) // mode does not impact the behaviour 
    if u == 0 {
        return 0, Dlerror{fnDlerror()}
    }
    return u, nil
}

Do tell me if you have a better idea for the name of the function or its implementation @TotallyGamerJet

Have you noticed a bottleneck caused when searching for shared objects?

No, this issue is not about performances but about being feature-complete regarding what libdl has to offer.

TotallyGamerJet commented 1 year ago

I looked up the Posix spec. And it says that RTLD_DEFAULT is used in dlsym like your example. I think adding that constant for use in purego.Dlsym is fine. Why do you prefer DefaultDSO over matching the C API?

eliottness commented 1 year ago

Why do you prefer DefaultDSO over matching the C API?

Indeed, the ideal case is to directly run dlsym(RTLD_DEFAULT, "strlen"), directly skipping any call to dlopen. The initial issue is the following:

the macro RTLD_DEFAULT which not accessible from go code obviously and is implementation defined

Since the value of RTLD_DEFAULT is implementation defined, you can be sure this value will change over time and over distros. And I already know some older musl versions have a different value than the glibc. Which themselves have a different value that on darwin. Sure RTLD_DEFAULT is the matching C API, and would obviously be better, but in this case I do not see how we could manage to pull this off. @TotallyGamerJet

TotallyGamerJet commented 1 year ago

Since the value of RTLD_DEFAULT is implementation defined, you can be sure this value will change over time and over distros.

I don't think it is the case that it will change and be different across distros. The value must be the same for each dynamic linker. AFAIK, there is only one on Linux. As for the value being different on Darwin that is fine since purego already uses build tags to separate Linux and Darwin constants.

I looked it up and glibc and musl have the same value ((void *)0). This further supports my claim that it is the same for all Linux systems. Plus, if the value did change all existing C programs would break bc they would be providing the wrong value.

eliottness commented 1 year ago

I feel that you understood my concern. If you think that’s fine then I am also fine with it. I’ve done the PR