rtld: Switch to the standard symbol lookup behavior if LD_DYNAMIC_WEAK is set
The current lookup prefers a strong definition to a STB_WEAK definition (similar to glibc pre-2.2 behavior) which does not conform to the ELF specification. The non-compliant behavior provoked https://reviews.llvm.org/D4418 which was intended to fix -shared-libasan but introduced new problems (and caused some sanitizer tests (e.g. test/asan/TestCases/interception_failure_test.cpp) to fail): sanitizer interceptors are STB_GLOBAL instead of STB_WEAK, so defining a second STB_GLOBAL interceptor can lead to a multiple definition linker error. For example, in a -fsanitize={address,memory,...} build, libc functions like malloc/free/strtol/... cannot be provided by user object files. See https://docs.freebsd.org/cgi/getmsg.cgi?fetch=16483939+0+archive/2014/freebsd-current/20140716.freebsd-current for discussions. This patch implements the ELF-compliant behavior when LD_DYNAMIC_WEAK is set. STB_WEAK wrestling in symbol lookups in `Search the dynamic linker itself` are untouched. Reviewed by: kib MFC after: 1 week Differential revision: https://reviews.freebsd.org/D26352
This commit is contained in:
parent
300e08933e
commit
7da378f9de
@ -28,7 +28,7 @@
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd June 2, 2021
|
||||
.Dd August 15, 2021
|
||||
.Dt RTLD 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -143,6 +143,24 @@ If set,
|
||||
.Nm
|
||||
will print a table containing all relocations before symbol
|
||||
binding and relocation.
|
||||
.It Ev LD_DYNAMIC_WEAK
|
||||
If set, use the ELF standard-compliant symbol lookup behavior:
|
||||
resolve to the first found symbol definition.
|
||||
.Pp
|
||||
By default,
|
||||
.Fx
|
||||
provides the non-standard symbol lookup behavior:
|
||||
when a weak symbol definition is found, remember the definition and
|
||||
keep searching in the remaining shared objects for a non-weak definition.
|
||||
If found, the non-weak definition is preferred, otherwise the remembered
|
||||
weak definition is returned.
|
||||
.Pp
|
||||
Symbols exported by dynamic linker itself (see
|
||||
.Xr dlfcn 3 )
|
||||
are always resolved using
|
||||
.Fx
|
||||
rules regardless of the presence of the variable.
|
||||
This variable is unset for set-user-ID and set-group-ID programs.
|
||||
.It Ev LD_LIBMAP
|
||||
A library replacement list in the same format as
|
||||
.Xr libmap.conf 5 .
|
||||
|
@ -209,6 +209,8 @@ static bool dangerous_ld_env; /* True if environment variables have been
|
||||
bool ld_bind_not; /* Disable PLT update */
|
||||
static char *ld_bind_now; /* Environment variable for immediate binding */
|
||||
static char *ld_debug; /* Environment variable for debugging */
|
||||
static bool ld_dynamic_weak = true; /* True if non-weak definition overrides
|
||||
weak definition */
|
||||
static char *ld_library_path; /* Environment variable for search path */
|
||||
static char *ld_library_dirs; /* Environment variable for library descriptors */
|
||||
static char *ld_preload; /* Environment variable for libraries to
|
||||
@ -583,7 +585,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
|
||||
unsetenv(_LD("LIBMAP_DISABLE")) || unsetenv(_LD("BIND_NOT")) ||
|
||||
unsetenv(_LD("DEBUG")) || unsetenv(_LD("ELF_HINTS_PATH")) ||
|
||||
unsetenv(_LD("LOADFLTR")) || unsetenv(_LD("LIBRARY_PATH_RPATH")) ||
|
||||
unsetenv(_LD("PRELOAD_FDS"))) {
|
||||
unsetenv(_LD("PRELOAD_FDS")) || unsetenv(_LD("DYNAMIC_WEAK"))) {
|
||||
_rtld_error("environment corrupt; aborting");
|
||||
rtld_die();
|
||||
}
|
||||
@ -591,6 +593,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
|
||||
ld_debug = getenv(_LD("DEBUG"));
|
||||
if (ld_bind_now == NULL)
|
||||
ld_bind_not = getenv(_LD("BIND_NOT")) != NULL;
|
||||
ld_dynamic_weak = getenv(_LD("DYNAMIC_WEAK")) == NULL;
|
||||
libmap_disable = getenv(_LD("LIBMAP_DISABLE")) != NULL;
|
||||
libmap_override = getenv(_LD("LIBMAP"));
|
||||
ld_library_path = getenv(_LD("LIBRARY_PATH"));
|
||||
@ -610,7 +613,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
|
||||
}
|
||||
dangerous_ld_env = libmap_disable || (libmap_override != NULL) ||
|
||||
(ld_library_path != NULL) || (ld_preload != NULL) ||
|
||||
(ld_elf_hints_path != NULL) || ld_loadfltr;
|
||||
(ld_elf_hints_path != NULL) || ld_loadfltr || ld_dynamic_weak;
|
||||
ld_tracing = getenv(_LD("TRACE_LOADED_OBJECTS"));
|
||||
ld_utrace = getenv(_LD("UTRACE"));
|
||||
|
||||
@ -3673,11 +3676,12 @@ do_dlsym(void *handle, const char *name, void *retaddr, const Ver_Entry *ve,
|
||||
continue;
|
||||
res = symlook_obj(&req, obj);
|
||||
if (res == 0) {
|
||||
if (def == NULL ||
|
||||
ELF_ST_BIND(req.sym_out->st_info) != STB_WEAK) {
|
||||
if (def == NULL || (ld_dynamic_weak &&
|
||||
ELF_ST_BIND(req.sym_out->st_info) != STB_WEAK)) {
|
||||
def = req.sym_out;
|
||||
defobj = req.defobj_out;
|
||||
if (ELF_ST_BIND(def->st_info) != STB_WEAK)
|
||||
if (!ld_dynamic_weak ||
|
||||
ELF_ST_BIND(def->st_info) != STB_WEAK)
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -3686,6 +3690,8 @@ do_dlsym(void *handle, const char *name, void *retaddr, const Ver_Entry *ve,
|
||||
* Search the dynamic linker itself, and possibly resolve the
|
||||
* symbol from there. This is how the application links to
|
||||
* dynamic linker services such as dlopen.
|
||||
* Note that we ignore ld_dynamic_weak == false case,
|
||||
* always overriding weak symbols by rtld definitions.
|
||||
*/
|
||||
if (def == NULL || ELF_ST_BIND(def->st_info) == STB_WEAK) {
|
||||
res = symlook_obj(&req, &obj_rtld);
|
||||
@ -4288,10 +4294,10 @@ symlook_global(SymLook *req, DoneList *donelist)
|
||||
symlook_init_from_req(&req1, req);
|
||||
|
||||
/* Search all objects loaded at program start up. */
|
||||
if (req->defobj_out == NULL ||
|
||||
ELF_ST_BIND(req->sym_out->st_info) == STB_WEAK) {
|
||||
if (req->defobj_out == NULL || (ld_dynamic_weak &&
|
||||
ELF_ST_BIND(req->sym_out->st_info) == STB_WEAK)) {
|
||||
res = symlook_list(&req1, &list_main, donelist);
|
||||
if (res == 0 && (req->defobj_out == NULL ||
|
||||
if (res == 0 && (!ld_dynamic_weak || req->defobj_out == NULL ||
|
||||
ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK)) {
|
||||
req->sym_out = req1.sym_out;
|
||||
req->defobj_out = req1.defobj_out;
|
||||
@ -4301,8 +4307,8 @@ symlook_global(SymLook *req, DoneList *donelist)
|
||||
|
||||
/* Search all DAGs whose roots are RTLD_GLOBAL objects. */
|
||||
STAILQ_FOREACH(elm, &list_global, link) {
|
||||
if (req->defobj_out != NULL &&
|
||||
ELF_ST_BIND(req->sym_out->st_info) != STB_WEAK)
|
||||
if (req->defobj_out != NULL && (!ld_dynamic_weak ||
|
||||
ELF_ST_BIND(req->sym_out->st_info) != STB_WEAK))
|
||||
break;
|
||||
res = symlook_list(&req1, &elm->obj->dagmembers, donelist);
|
||||
if (res == 0 && (req->defobj_out == NULL ||
|
||||
@ -4351,8 +4357,8 @@ symlook_default(SymLook *req, const Obj_Entry *refobj)
|
||||
|
||||
/* Search all dlopened DAGs containing the referencing object. */
|
||||
STAILQ_FOREACH(elm, &refobj->dldags, link) {
|
||||
if (req->sym_out != NULL &&
|
||||
ELF_ST_BIND(req->sym_out->st_info) != STB_WEAK)
|
||||
if (req->sym_out != NULL && (!ld_dynamic_weak ||
|
||||
ELF_ST_BIND(req->sym_out->st_info) != STB_WEAK))
|
||||
break;
|
||||
res = symlook_list(&req1, &elm->obj->dagmembers, &donelist);
|
||||
if (res == 0 && (req->sym_out == NULL ||
|
||||
@ -4397,10 +4403,11 @@ symlook_list(SymLook *req, const Objlist *objlist, DoneList *dlp)
|
||||
continue;
|
||||
symlook_init_from_req(&req1, req);
|
||||
if ((res = symlook_obj(&req1, elm->obj)) == 0) {
|
||||
if (def == NULL || ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK) {
|
||||
if (def == NULL || (ld_dynamic_weak &&
|
||||
ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK)) {
|
||||
def = req1.sym_out;
|
||||
defobj = req1.defobj_out;
|
||||
if (ELF_ST_BIND(def->st_info) != STB_WEAK)
|
||||
if (!ld_dynamic_weak || ELF_ST_BIND(def->st_info) != STB_WEAK)
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -4435,10 +4442,11 @@ symlook_needed(SymLook *req, const Needed_Entry *needed, DoneList *dlp)
|
||||
if (n->obj == NULL ||
|
||||
(res = symlook_list(&req1, &n->obj->dagmembers, dlp)) != 0)
|
||||
continue;
|
||||
if (def == NULL || ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK) {
|
||||
if (def == NULL || (ld_dynamic_weak &&
|
||||
ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK)) {
|
||||
def = req1.sym_out;
|
||||
defobj = req1.defobj_out;
|
||||
if (ELF_ST_BIND(def->st_info) != STB_WEAK)
|
||||
if (!ld_dynamic_weak || ELF_ST_BIND(def->st_info) != STB_WEAK)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user