-
-
Notifications
You must be signed in to change notification settings - Fork 31.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
gh-65821: Fix ctypes.util.find_library with musl #18380
base: main
Are you sure you want to change the base?
Conversation
Hello, and thanks for your contribution! I'm a bot set up to make sure that the project can legally accept this contribution by verifying everyone involved has signed the PSF contributor agreement (CLA). CLA MissingOur records indicate the following people have not signed the CLA: For legal reasons we need all the people listed to sign the CLA before we can look at your contribution. Please follow the steps outlined in the CPython devguide to rectify this issue. If you have recently signed the CLA, please wait at least one business day You can check yourself to see if the CLA has been received. Thanks again for the contribution, we look forward to reviewing it! |
What's the status on this? As seen by the mention, this is required to make some stuff work on Musl systems. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although I do want to see this resolved, I don't believe the current implementation is correct.
Trying to duplicate the ld search logic is difficult, system dependent and can very easily get out of date. If there is no other option to solve this for musl, this new search routine should at least only be used when ldconfig
is not present, but even then I am not super comfortable with it.
It is some time since I worked on this, but I think my conclusion was that there is not really any way to implement this correctly (at least not in a portable way), so this is only an attempt to get it working on current known use cases. |
Right, and that is reasonable for a downstream patch, but I am not too comfortable having it in the upstream. I generally think it is better to have something not working, which forces users to work around it in a way that works for their use-case, than to have something with broken behavior. Anyway, regardless of if I think this is a good idea to have or not, as I said above, the new search mechanism you are introducing is not limited to musl. So, if this was ever to be merged, it should at least limit this search routine to musl. But I am not the maintainer, I am just giving my opinion. |
I find that unfair. Current That said, I started to look at how to fix the issues you have raised. I believe the Finding the correct #include <elf.h>
#include <link.h>
#include <stdio.h>
static int cb(struct dl_phdr_info *info, size_t size, void *data)
{
const char **ps = data;
const char *base = (const char *)info->dlpi_addr;
const ElfW(Phdr) *ph = info->dlpi_phdr;
int phn = info->dlpi_phnum;
for(int i=0; i < phn; i++) {
if (ph[i].p_type == PT_INTERP) {
*ps = base + ph[i].p_vaddr;
return 1;
}
}
return 0;
}
const char *get_interp()
{
const char *s = 0;
int r = dl_iterate_phdr(cb, &s);
return r == 1 ? s : 0;
}
int main()
{
printf("%s\n", get_interp());
} it will print the interpreter (eg However, using BTW, in the current implementation, running a 32 bit python binary on a 64 bit host will find 64 bit libraries:
So I'm thinking that making things simple by parsing all |
I don't know, why should we improve things? I will refrain from commenting further. I have made my suggestions, but they were just that. It is up to the maintainers to make the call. This code gives me anxiety and I would not merge it as is. Everybody is happy until things break and you are the one who has to fix them 🙃 Anyway, not my place. Perhaps you should write to the musl mailing list asking for advice on how to approach this. |
musl list won't help much. this is a bug python dig for itself: the exposed api has semantics that is impossible to implement on a posix system including glibc and musl. i've seen this fail on glibc too and it will cause more trouble as python is used in more custom environments. so it's up to the python community to figure out what they want find_library to mean. the dynamic linker search path logic is not a public api and can change (musl or glibc may introduce new mechanisms to configure it) and there is no guarantee that a library exists in the filesystem (which is e.g. the case for libpthread.so in musl: dlopen gives a handle to it but there is no libpthread.so in the filesystem or similar can happen with kernel mapped shared libraries like the vdso if they get exposed via dlopen or already loaded but then deleted files). |
@FFY00 Thank you for taking time to review and comment. I understand that the code gives you anxiety. The whole file gives me anxiety 😄 I think the I did get help from musl devs on IRC who came up with the |
I'd like to see this fixed upstream, but I don't believe this PR or any of the variants proposed in the comments above are correct. Can someone explain to me what the purpose of |
OK, to summarize what I've found/been-told (please feel free to correct any of this if it's not right) the purpose is mainly (only?) getting a string to pass to |
I have updated the patch to only use the fallback when the interpreter contains the string "ld-musl-", and will also honor the correct |
8e0ebcd
to
64272fe
Compare
As @richfelker mentions, there is no meaningful way to ever use I think we could have a cleaner solution/workaround if we solve https://bugs.python.org/issue43112 first, which would allow us to detect/set musl at compile time. |
Anything else I can do to get this merged? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am now in a position to merge this, and given the lack of reviewers, I think I can help push it through the finish line.
The current implementation seems okay to me. My main worry, IIRC, (the implementation changing the logic on non-musl systems) has been addressed, and I don't see anything else major. I just have a couple comments and considerations that I'd prefer to see addressed before we pull the trigger, and a couple non-blocking nitpicks.
except OSError: | ||
return False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the motivation behind this? Saying the file is not an elf if we get any kind of OSError
does not seem like a great idea to me 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this means to catch file does not exist and file is not readable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's what'd expect to, but I want to make sure returning false on all possible OSError
is actually the behavior we want, as it might introduce subtle bugs. For eg. I would expect a BrokenPipeError
or TimeoutError
to propagate, instead of not considering the file a valid library.
Looking at the usages of this function, I'd guess we only need PermissionError
, and I think we only need it on the musl specific code path, so IMO it'd be better off there.
Anyway, I think I am being overly careful here, but would like to hear from the author if they have time. This is not blocker.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The thinking here was: return True if we know it is an elf, return False if we know it is not or we don't know.
will current find_library
implementation for GNU libc or *BSD raise an error in those cases?
except OSError: | ||
paths.extend(['/lib', '/usr/local/lib', '/usr/lib']) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are the possible issues here? Would it make sense to be more specific with the exception? Would it make sense to let the user know that we are defaulting to /lib
, /usr/local/lib
, /usr/lib
(via a warning for eg.)?
Should /lib
actually come before /usr/local/lib
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIRC the order was copied from musl libc: http://git.musl-libc.org/cgit/musl/tree/ldso/dynlink.c?id=f5f55d6589940fd2c2188d76686efe3a530e64e0#n1160
Co-authored-by: Filipe Laíns <[email protected]>
Co-authored-by: Filipe Laíns <[email protected]>
Co-authored-by: Filipe Laíns <[email protected]>
@merwok has your feedback been acceptably addressed? Asking now to speed things up 😅 |
I never meant for the reveiw to be a blocker, it was from before I had the commit bit, so dismissing it now.
@@ -2015,6 +2066,7 @@ PyMethodDef _ctypes_module_methods[] = { | |||
"dlopen(name, flag={RTLD_GLOBAL|RTLD_LOCAL}) open a shared library"}, | |||
{"dlclose", py_dl_close, METH_VARARGS, "dlclose a library"}, | |||
{"dlsym", py_dl_sym, METH_VARARGS, "find symbol in shared library"}, | |||
{"get_interp", get_interp, METH_NOARGS}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Documentation is missing for this new API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, if you introduce a new API, please use Argument Clinic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there some way to add it as an internal function? So we don't expose it as a public API
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, you could prefix it with an underscore.
@@ -0,0 +1 @@ | |||
Fix :func:`ctypes.util.find_library` with musl libc. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does not mention the introduced API.
A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated. Once you have made the requested changes, please leave a comment on this pull request containing the phrase And if you don't make the requested changes, you will be poked with soft cushions! |
Co-authored-by: Erlend E. Aasland <[email protected]>
Co-authored-by: Erlend E. Aasland <[email protected]>
IMO, this is too much code for an unsupported (and thus untested) |
I think it might make sense to add musl as (at least) a tier 3 platform. It is widely used as the base of containers ( |
It's specified in https://peps.python.org/pep-0011/#support-tiers . |
Musl libc does not use any ld.cache and
ldconfig -r
does not work withmusl. It is also common that musl based container runtimes excludes
objdump, gcc and ld, which means that find_library() does not work at
all.
So as a last resort, if none of the previously mentioned methods works,
scan LD_LIBRARY_PATH and some standard locations for elf files that
matches the specified name, in a similar way that ld does.
We also update the tests so they work as expected with musl libc, which
does not have a separate libm or libcrypt.
Signed-off-by: Natanael Copa [email protected]
https://bugs.python.org/issue21622