# HG changeset patch # User Tassilo Philipp # Date 1573039973 -3600 # Node ID b2e4e23d995369e05ce2a313dd2974fcaec5a454 # Parent 73b5b9e224e237377419049ff3b65db8356c0517 - stop using dlinfo() on glibc platforms but use dl_iterate_phdr() instead, as former's implementation is nothing more than a fancy cast and thus dangerously assuming that every provided handle is valid - dynload_plain test * now testing getting exec's path * workdir independent diff -r 73b5b9e224e2 -r b2e4e23d9953 ChangeLog --- a/ChangeLog Tue Nov 05 15:19:16 2019 +0100 +++ b/ChangeLog Wed Nov 06 12:32:53 2019 +0100 @@ -7,6 +7,9 @@ o support for soft-float MIPS o32 & n64 (big- and little-endian, each) dynload: o added UTF-8 support for pathnames on windows + o reliability/stability fix for dlGetLibraryPath() on glibc based platforms (avoiding + internal use of glibc's bad impl of dlinfo() which doesn't do any error checking at all) + o support for dlGetLibraryPath() on glibc platforms with glibc vesions <= 2.3.3 doc: o more detail in support matrix for bi-endian platforms tests: diff -r 73b5b9e224e2 -r b2e4e23d9953 dynload/dynload_unix.c --- a/dynload/dynload_unix.c Tue Nov 05 15:19:16 2019 +0100 +++ b/dynload/dynload_unix.c Wed Nov 06 12:32:53 2019 +0100 @@ -38,7 +38,12 @@ #include -#if defined(__GLIBC__) /* to access dlinfo */ +#if defined(__GLIBC__) +/* @@@ version check glibc correctly... dl_iterate_phdr(): glibc ver >= 2.2.4*/ +#if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 3) +# define DL_USE_GLIBC_ITER_PHDR +#endif +/* to access dl_iterate_phdr(), and related w/ glibc */ # define _GNU_SOURCE # define __USE_GNU #endif @@ -69,20 +74,23 @@ -/* for dlopen-based dlGetLibraryPath impls below, prefer RTLD_NOLOAD */ -/* that merely checks lib names */ +/* for dlopen-based dlGetLibraryPath impls below, prefer RTLD_NOLOAD that + * merely checks lib names */ #if defined(RTLD_NOLOAD) -# define RTLD_LIGHTEST RTLD_NOLOAD +# define RTLD_LIGHTEST RTLD_LAZY|RTLD_NOLOAD #else # define RTLD_LIGHTEST RTLD_LAZY #endif -/* code for dlGetLibraryPath is platform specific - if dlinfo() exists use */ -/* that: check for RTLD_DI_LINKMAP (#define for dlinfo()), or if GNU C Lib */ -/* is used (where RTLD_DI_LINKMAP is an enum), or by OS (dlinfo comes from */ -/* Solaris), etc. */ -#if defined(RTLD_DI_LINKMAP) || defined(OS_SunOS) || defined(__GLIBC__) /* @@@ dlinfo() was introduced in glibc 2.3.3 (in 2003), somehow check for that, also */ +/* code for dlGetLibraryPath() is platform specific */ + +/* if dlinfo() exists use it (except on glibc, where it exists since version + * 2.3.3, but its implementation is dangerous, as no checks are done whether + * the handle is valid, thus rendering the returned values useless): check for + * RTLD_DI_LINKMAP which is a #define for dlinfo() on most supported targets, + * or specifically check the OS (e.g. dlinfo() is originally from Solaris) */ +#if (defined(RTLD_DI_LINKMAP) || defined(OS_SunOS)) && !defined(DL_USE_GLIBC_ITER_PHDR) #include @@ -90,9 +98,7 @@ { struct link_map* p = NULL; int l = -1; - /* on some platforms dlinfo() "succeeds" for any handle, returning a */ - /* legit pointer to a struct w/o any fields set; fail if unset */ - if(dlinfo(pLib, RTLD_DI_LINKMAP, &p) == 0 && p && p->l_name) { + if(dlinfo(pLib, RTLD_DI_LINKMAP, &p) == 0 && p) { l = strlen(p->l_name); if(l < bufSize) /* l+'\0' <= bufSize */ strcpy(sOut, p->l_name); @@ -119,7 +125,7 @@ /* code. There doesn't seem to be a direct way to query the library path, */ /* so "double-load" temporarily all already loaded images (just increases */ /* ref count) and compare handles until we found ours. Return the name. */ - for(i=_dyld_image_count(); i>0;) /* iterate libs from end, more likely ours */ + for(i=_dyld_image_count(); i>0;) /* backwards, ours is more likely at end */ { const char* libPath = _dyld_get_image_name(--i); void* lib = dlopen(libPath, RTLD_LIGHTEST); @@ -140,9 +146,10 @@ } -/* OpenBSD >= 3.7 has dl_iterate_phdr(), use it if not explicitly requesting */ -/* to use dladdr()-based guessing (set by configure) -----> */ -#elif defined(OS_OpenBSD) && !defined(DL_DLADDR_TO_LIBPATH) +/* OpenBSD >= 3.7 has dl_iterate_phdr(), as well as glibc >= 2.2.4, however + * skip and use on dladdr()-based guessing if if explicitly requested, e.g. by + * ./configure */ +#elif !defined(DL_DLADDR_TO_LIBPATH) && (defined(OS_OpenBSD) || defined(DL_USE_GLIBC_ITER_PHDR)) #include #include @@ -179,9 +186,16 @@ } +/* glibc with neither dl_iterate_phdr() nor dlinfo() (latter introduced after former) @@@ +#elif defined(__GLIBC__) && !defined(DL_USE_GLIBC_ITER_PHDR) + +@@@impl */ + /* fallback to dladdr() hack */ #else +#warning "Using non-optimal code for dlGetLibraryPath() b/c of platform limitations." + /* if nothing else is available, fall back to guessing using dladdr() - this */ /* might not always work, as it's trying to getit via the _fini() symbol, */ /* which is usually defined in ELF files, but not guaranteed */ diff -r 73b5b9e224e2 -r b2e4e23d9953 test/dynload_plain/dynload_plain.c --- a/test/dynload_plain/dynload_plain.c Tue Nov 05 15:19:16 2019 +0100 +++ b/test/dynload_plain/dynload_plain.c Wed Nov 06 12:32:53 2019 +0100 @@ -34,6 +34,7 @@ #else # include #endif +#include int strlen_utf8(const char *s) @@ -58,7 +59,7 @@ #if defined(DEF_C_DYLIB) DEF_C_DYLIB, #endif - /* fallback guessing if not provided by Makefile */ + /* fallback guessing if not provided by Makefile */ "/lib/libc.so", "/lib32/libc.so", "/lib64/libc.so", @@ -137,14 +138,24 @@ printf("path lookup failed as expected with bad lib handle: %d\n", bs == 0); r += (bs == 0); + /* test getting own path */ + { + /* get own exec's path */ + bs = dlGetLibraryPath(NULL, queriedPath, 200); + printf("dynload_plain's own path is: %s\n", queriedPath); + r += (bs != 0 && strlen(queriedPath) > 0); + + /* change working dir to where our executable is, for following test */ + chdir(dirname(queriedPath)); + } + /* test UTF-8 path through dummy library that's created by this test's build */ { static const char* pathU8 = "./dynload_plain_\xc3\x9f_test"; - int nu8c, b; + int nu8c, b; - //cp(pathU8, "/lib/libz.so.6"); pLib = dlLoadLibrary(pathU8); /* check if we can load a lib with a UTF-8 path */ - printf("pLib (loaded w/ UTF-8 path %s) handle: %p\n", pathU8, pLib); + printf("pLib (loaded w/ UTF-8 path %s with wd being exec's dir) handle: %p\n", pathU8, pLib); r += (p != NULL); if(pLib) { @@ -220,7 +231,7 @@ } /* Check final score of right ones to see if all worked */ - r = (r == 15); + r = (r == 16); printf("result: dynload_plain: %d\n", r); return !r; }