| Summary: | [rtdl] [patch] rtdl/dlopen() fails to merge common variables in 2 or more dlopen'ed libs | ||
|---|---|---|---|
| Product: | Base System | Reporter: | Steve Whiteley <stevew> |
| Component: | kern | Assignee: | freebsd-bugs (Nobody) <bugs> |
| Status: | Closed FIXED | ||
| Severity: | Affects Only Me | ||
| Priority: | Normal | ||
| Version: | Unspecified | ||
| Hardware: | Any | ||
| OS: | Any | ||
State Changed From-To: open->feedback To submitter: it appears that bin/25059, which this PR references, was committed years ago. Did it solve your problem? State Changed From-To: feedback->closed From submitter: This problem went away a long time ago, sometime in the mid-late 4.X releases. |
It seems that in FreeBSD 4.3, if two shared libraries, each of which define an exported pointer variable of the same name, are opened using dlopen(), each pointer retains a unique address. This is different from Solaris and Linux, where the pointers are merged into a common symbol. For example, both libtcl and libtk (8.3.3) define tclStubsPtr. If these two libraries are opened with dlopen(..., RTLD_GLOBAL), calling Tk_Init() causes a seg fault on the first call of a tcl function from libtk. The problem is that the libtcl tclStubsPtr struct is being initialized, but the separate address of the tclStubsPtr in libtk which is resolved for all references in libtk remains a null pointer. Here is a mod to rtld.c in the rtld source that seems to fix the problem: In rtld.c near line 1862 in symlook_default() /**/ /* Search all RTLD_GLOBAL objects. */ if (def == NULL || ELF_ST_BIND(def->st_info) == STB_WEAK) { symp = symlook_list(name, hash, &list_global, &obj, in_plt, &donelist); if (symp != NULL && (def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK)) { def = symp; defobj = obj; } } /* SRW ** block below was ahead of block above */ /* Search all dlopened DAGs containing the referencing object. */ STAILQ_FOREACH(elm, &refobj->dldags, link) { if (def != NULL && ELF_ST_BIND(def->st_info) != STB_WEAK) break; symp = symlook_list(name, hash, &elm->obj->dagmembers, &obj, in_plt, &donelist); if (symp != NULL && (def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK)) { def = symp; defobj = obj; } } /**/ This will cause linkage to an exported symbol from the other dlopen'ed library, ahead of the variable local to the library whose references are being resolved. This would seem to be better logic, but there may be a disadvantage I have overlooked. My program works in Linux and Solaris. For FreeBSD I'm using a work-around that uses dlsym() to grab the two addresses and then explicitly setting the unset pointer. There is also an open problem report #25059 relating to dlopen(), possibly related? Fix: See "Full Description" How-To-Repeat: See "Full Description"