Konstantin Belousov
2018-Sep-02 12:06 UTC
Constraints in libmap(32).conf do not work as expected, possible bug in rtld-elf
On Sat, Sep 01, 2018 at 12:32:07AM +0200, Andreas Longwitz wrote:> On a FreeBSD 10.4-STABLE r337823 (amd64) server I have to run some old > php52 scripts from an FreeBSD 8.4-STABLE r284383 (i386) server. I have > copied the old php software to /usr/local/php52, installed the ports > misc/compat8x and misc/compat9x and have copied all missing 32-bit > libraries from the old machine to /usr/local/lib32. With the following > libmap32.conf everything works fine: > > ## php52 > /usr/local/lib /usr/local/lib32 > /usr/local/lib/mysql /usr/local/lib32/mysql > > Two examples: > > -> ldd /usr/local/php52/bin/php > /usr/local/php52/bin/php: > libcrypt.so.5 => /usr/lib32/libcrypt.so.5 (0x28273000) > librt.so.1 => /usr/lib32/librt.so.1 (0x28292000) > libm.so.5 => /usr/lib32/libm.so.5 (0x28298000) > libxml2.so.5 => /usr/local/lib32/libxml2.so.5 (0x282c2000) > libz.so.5 => /usr/local/lib32/compat/libz.so.5 (0x283ec000) > libiconv.so.3 => /usr/local/lib32/libiconv.so.3 (0x283fe000) > libc.so.7 => /usr/lib32/libc.so.7 (0x284f2000) > libthr.so.3 => /usr/lib32/libthr.so.3 (0x2866c000) > > -> ldd /usr/local/php52/lib/php/20060613/mysql.so > /usr/local/php52/lib/php/20060613/mysql.so: > libmysqlclient.so.16 => > /usr/local/lib32/mysql/libmysqlclient.so.16 (0x28206000) > libc.so.7 => /usr/lib32/libc.so.7 (0x28070000) > libcrypt.so.5 => /usr/lib32/libcrypt.so.5 (0x2835e000) > libm.so.5 => /usr/lib32/libm.so.5 (0x2837d000) > libz.so.5 => /usr/local/lib32/compat/libz.so.5 (0x283a7000) > librt.so.1 => /usr/lib32/librt.so.1 (0x283b9000) > libthr.so.3 => /usr/lib32/libthr.so.3 (0x283bf000) > > Because I like to use constraints in libmap32.conf I chenged the file to > > ## php52 > [/usr/local/php52/] > /usr/local/lib /usr/local/lib32 > > [/usr/local/php52/lib/php/20060613/mysql.so] > /usr/local/lib/mysql /usr/local/lib32/mysql > > The same examples as above shows that libmap does not work anymore: > > -> ldd /usr/local/php52/bin/php > /usr/local/php52/bin/php: > libcrypt.so.5 => /usr/lib32/libcrypt.so.5 (0x28273000) > librt.so.1 => /usr/lib32/librt.so.1 (0x28292000) > libm.so.5 => /usr/lib32/libm.so.5 (0x28298000) > libxml2.so.5 => not found (0) > libz.so.5 => /usr/local/lib32/compat/libz.so.5 (0x282c2000) > libiconv.so.3 => not found (0) > libc.so.7 => /usr/lib32/libc.so.7 (0x282d4000) > libthr.so.3 => /usr/lib32/libthr.so.3 (0x2844e000) > > -> ldd /usr/local/php52/lib/php/20060613/mysql.so > /usr/local/php52/lib/php/20060613/mysql.so: > libmysqlclient.so.16 => not found (0) > libc.so.7 => /usr/lib32/libc.so.7 (0x28070000) > > The constraints in libmap.conf are handled in rtld-elf with the help of > contexts, and especially a "$DEFAULT$" context is used for all entries > in libmap.conf before the first constraint statement. Now when rtld-elf > loads a program the mapping rules from libmap.conf are applied. In a > first step rtld-elf does a direct mapping using the correct context. But > in a second step called "Searching for ..." rtld-elf uses always the > "$DEFAULT$" context, which in the last example is empty. Therfore > rtld-elf does never find a library in his searching step and cannot load > programs where searching for libraries is necessary. > > I cannot see any reason why rtld-elf should change the context between > step1 and step2, The following patch provokes that rtld-elf uses the > context from step1 in step2 too:I am having problem understanding what do you mean by step1/step2. The refobj reference that you cache in the patch, comes into load_object() as the pointer to the object which initiate the load_object() call. It is NULL for preloaded objects, otherwise it is not. So, could you, please, explain where does it get passed as NULL in your case ? Also, your patch makes the ref_object stuck for all future invocations of the load_object(), so it cannot be correct for this reason alone. Another note is that libmap.conf use that you put it for, is quite the strengthen of the original purpose. You should just add the pathes with your libraries to LD_32_LIBRARY_PATH or configure them into /var/run/ld-elf32.so.hints using 'ldconfig -32'.> > --- rtld.c.orig 2018-03-20 16:56:48.000000000 +0100 > +++ rtld.c 2018-08-31 23:17:18.051206000 +0200 > @@ -186,6 +186,7 @@ > static Obj_Entry obj_rtld; /* The dynamic linker shared object */ > static unsigned int obj_count; /* Number of objects in obj_list */ > static unsigned int obj_loads; /* Number of loads of objects (gen count) */ > +static char *save_refobj_path; > > static Objlist list_global = /* Objects dlopened with RTLD_GLOBAL */ > STAILQ_HEAD_INITIALIZER(list_global); > @@ -1499,6 +1500,7 @@ > if (libmap_disable || !objgiven || > (name = lm_find(refobj->path, xname)) == NULL) > name = (char *)xname; > + save_refobj_path = refobj->path; > > dbg(" Searching for \"%s\"", name); > > @@ -2831,7 +2833,7 @@ > char *res; > > len = strcspn(path, ":;"); > - trans = lm_findn(NULL, path, len); > + trans = lm_findn(save_refobj_path, path, len); > if (trans) > res = callback(trans, strlen(trans), arg); > else > > This patch solves the described problem for me.
Andreas Longwitz
2018-Sep-11 20:47 UTC
Constraints in libmap(32).conf do not work as expected, possible bug in rtld-elf
Thanks very much for answer ! Now I use the following libmap32.conf: ## php52 [/usr/local/php52/lib/php/20060613/mysql.so] /usr/local/lib/mysql /usr/local/lib32/mysql [/usr/local/php52/] /usr/local/lib /usr/local/lib32> I am having problem understanding what do you mean by step1/step2. The > refobj reference that you cache in the patch, comes into load_object() > as the pointer to the object which initiate the load_object() call. It > is NULL for preloaded objects, otherwise it is not. > So, could you, please, explain where does it get passed as NULL in your > case ?Ok, I try to explain better: step1 means: call of lm_find() in rtld.c (line 1500) and first argument is refobj->path, which is used in libmap.c to find the correct entry in the lmp_list. step2 means: call of lm_findn() in rtld.c (line 2834) when called from search_library_path(). In this case the first argument is NULL and in libmap.c this means "$DEFAULT" entry in the lmp_list. Please notice that after reading libmap32.conf in lm_init() the entry $DEFAULT in the lmp_list does not exist, when all mappings are defined with constraints.> Also, your patch makes the ref_object stuck for all future invocations > of the load_object(), so it cannot be correct for this reason alone.I do not think so. The patch only caches refobj->path from step1 to use the same path in step2. I have updated my patch a little bit to clarify, that the patch only wants to change the call of lm_findn() in the case of search_library_path(): --- rtld.c.orig 2018-03-20 16:56:48.000000000 +0100 +++ rtld.c 2018-09-11 21:44:03.333739000 +0200 @@ -186,6 +186,7 @@ static Obj_Entry obj_rtld; /* The dynamic linker shared object */ static unsigned int obj_count; /* Number of objects in obj_list */ static unsigned int obj_loads; /* Number of loads of objects (gen count) */ +static char *lm_refobj_path = NULL; static Objlist list_global = /* Objects dlopened with RTLD_GLOBAL */ STAILQ_HEAD_INITIALIZER(list_global); @@ -1496,6 +1497,8 @@ __DECONST(char *, xname))); } + if (objgiven) + lm_refobj_path = refobj->path; if (libmap_disable || !objgiven || (name = lm_find(refobj->path, xname)) == NULL) name = (char *)xname; @@ -1513,8 +1516,10 @@ (refobj != NULL && (pathname = search_library_path(name, refobj->rpath)) != NULL) || (pathname = search_library_path(name, gethints(false))) !NULL || - (pathname = search_library_path(name, STANDARD_LIBRARY_PATH)) != NULL) + (pathname = search_library_path(name, STANDARD_LIBRARY_PATH)) != NULL) { + lm_refobj_path = NULL; return (pathname); + } } else { nodeflib = objgiven ? refobj->z_nodeflib : false; if ((objgiven && @@ -1526,10 +1531,13 @@ (pathname = search_library_path(name, refobj->runpath)) !NULL) || (pathname = search_library_path(name, gethints(nodeflib))) !NULL || (objgiven && !nodeflib && - (pathname = search_library_path(name, STANDARD_LIBRARY_PATH)) != NULL)) + (pathname = search_library_path(name, STANDARD_LIBRARY_PATH)) != NULL)) { + lm_refobj_path = NULL; return (pathname); + } } + lm_refobj_path = NULL; if (objgiven && refobj->path != NULL) { _rtld_error("Shared object \"%s\" not found, required by \"%s\"", name, basename(refobj->path)); @@ -2831,7 +2839,7 @@ char *res; len = strcspn(path, ":;"); - trans = lm_findn(NULL, path, len); + trans = lm_findn(lm_refobj_path, path, len); if (trans) res = callback(trans, strlen(trans), arg); else> Another note is that libmap.conf use that you put it for, is quite the > strengthen of the original purpose. You should just add the pathes > with your libraries to LD_32_LIBRARY_PATH or configure them into > /var/run/ld-elf32.so.hints using 'ldconfig -32'.I have tried this without success. With the original ld-elf32.so.1 and my constraints in libmap32.conf I got the following: -> /usr/local/php52/bin/php -v Shared object "libxml2.so.5" not found, required by "php" The error changed after setting LD_32_LIBRARY_PATH or (with same result) the file /var/run/ld-elf32.so.hints using 'ldconfig -32. -> export LD_32_LIBRARY_PATH="/usr/local/lib32:/usr/local/lib32/mysql" -> /usr/local/php52/bin/php -v /usr/local/lib/libiconv.so.3: unsupported file layout This error disappears when I move this 64-bit library libiconv.so away, but thats not a solution. For your information I append the beginning of the dynamic section from readelf output of /usr/local/php52/bin/php: Tag Type Name/Value 0x00000001 (NEEDED) Shared library: [libcrypt.so.5] 0x00000001 (NEEDED) Shared library: [librt.so.1] 0x00000001 (NEEDED) Shared library: [libm.so.5] 0x00000001 (NEEDED) Shared library: [libxml2.so.5] 0x00000001 (NEEDED) Shared library: [libz.so.5] 0x00000001 (NEEDED) Shared library: [libiconv.so.3] 0x00000001 (NEEDED) Shared library: [libc.so.7] 0x0000000f (RPATH) Library rpath: [/usr/local/lib] Andreas Longwitz