diff --git a/libexec/rtld-elf/rtld.1 b/libexec/rtld-elf/rtld.1 --- a/libexec/rtld-elf/rtld.1 +++ b/libexec/rtld-elf/rtld.1 @@ -28,7 +28,7 @@ .\" .\" $FreeBSD$ .\" -.Dd June 2, 2021 +.Dd August 15, 2021 .Dt RTLD 1 .Os .Sh NAME @@ -143,6 +143,24 @@ .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 . diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -209,6 +209,8 @@ 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 @@ 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 @@ 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 @@ } 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 @@ 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 @@ * 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_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 @@ /* 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 @@ /* 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 @@ 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 @@ 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; } }