From 35b2b265aa300727e840990e41e604edb8b25314 Mon Sep 17 00:00:00 2001 From: John Marino Date: Sat, 21 Jan 2012 18:31:30 +0100 Subject: [PATCH] rtld: Implement ELF filters (DT_FILTER and DT_AUXILIARY) Filtrees are loaded on demand unless LD_LOADFLTR environment variable is set or -z loadfltr was specified during the linking. This forces rtld to upgrade read-locked rtld_bind_lock to write lock when it encounters an object with filter during symbol lookup. Consolidate common arguments of the symbol lookup functions in the SymLook structure. Taken-from: FreeBSD SVN 216728 (27 DEC 2010) --- libexec/rtld-elf/i386/reloc.c | 48 +-- libexec/rtld-elf/rtld.1 | 10 +- libexec/rtld-elf/rtld.c | 604 ++++++++++++++++++++++---------- libexec/rtld-elf/rtld.h | 44 ++- libexec/rtld-elf/x86_64/reloc.c | 46 +-- 5 files changed, 511 insertions(+), 241 deletions(-) diff --git a/libexec/rtld-elf/i386/reloc.c b/libexec/rtld-elf/i386/reloc.c index 85b3509e1d..5b0c68ae50 100644 --- a/libexec/rtld-elf/i386/reloc.c +++ b/libexec/rtld-elf/i386/reloc.c @@ -65,29 +65,34 @@ do_copy_relocations(Obj_Entry *dstobj) assert(dstobj->mainprog); /* COPY relocations are invalid elsewhere */ - rellim = (const Elf_Rel *) ((c_caddr_t) dstobj->rel + dstobj->relsize); + rellim = (const Elf_Rel *) ((caddr_t) dstobj->rel + dstobj->relsize); for (rel = dstobj->rel; rel < rellim; rel++) { if (ELF_R_TYPE(rel->r_info) == R_386_COPY) { void *dstaddr; const Elf_Sym *dstsym; const char *name; - unsigned long hash; size_t size; const void *srcaddr; const Elf_Sym *srcsym; - const Ver_Entry *ve; - Obj_Entry *srcobj; + const Obj_Entry *srcobj, *defobj; + SymLook req; + int res; dstaddr = (void *) (dstobj->relocbase + rel->r_offset); dstsym = dstobj->symtab + ELF_R_SYM(rel->r_info); name = dstobj->strtab + dstsym->st_name; - hash = elf_hash(name); size = dstsym->st_size; - ve = fetch_ventry(dstobj, ELF_R_SYM(rel->r_info)); - - for (srcobj = dstobj->next; srcobj != NULL; srcobj = srcobj->next) - if ((srcsym = symlook_obj(name, hash, srcobj, ve, 0)) != NULL) + symlook_init(&req, name); + req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rel->r_info)); + + for (srcobj = dstobj->next; srcobj != NULL; srcobj = srcobj->next) { + res = symlook_obj(&req, srcobj); + if (res == 0) { + srcsym = req.sym_out; + defobj = req.defobj_out; break; + } + } if (srcobj == NULL) { _rtld_error("Undefined symbol \"%s\" referenced from COPY" @@ -95,7 +100,7 @@ do_copy_relocations(Obj_Entry *dstobj) return -1; } - srcaddr = (const void *) (srcobj->relocbase + srcsym->st_value); + srcaddr = (const void *) (defobj->relocbase + srcsym->st_value); memcpy(dstaddr, srcaddr, size); } } @@ -115,7 +120,7 @@ init_pltgot(Obj_Entry *obj) /* Process the non-PLT relocations. */ int -reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld) +reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, RtldLockState *lockstate) { const Elf_Rel *rellim; const Elf_Rel *rel; @@ -132,7 +137,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld) } else cache = NULL; - rellim = (const Elf_Rel *) ((c_caddr_t) obj->rel + obj->relsize); + rellim = (const Elf_Rel *) ((caddr_t) obj->rel + obj->relsize); for (rel = obj->rel; rel < rellim; rel++) { Elf_Addr *where = (Elf_Addr *) (obj->relocbase + rel->r_offset); @@ -147,7 +152,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld) const Obj_Entry *defobj; def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, - false, cache); + false, cache, lockstate); if (def == NULL) goto done; @@ -166,7 +171,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld) const Obj_Entry *defobj; def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, - false, cache); + false, cache, lockstate); if (def == NULL) goto done; @@ -196,7 +201,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld) const Obj_Entry *defobj; def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, - false, cache); + false, cache, lockstate); if (def == NULL) goto done; @@ -214,7 +219,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld) const Obj_Entry *defobj; def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, - false, cache); + false, cache, lockstate); if (def == NULL) goto done; @@ -274,7 +279,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld) const Obj_Entry *defobj; def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, - false, cache); + false, cache, lockstate); if (def == NULL) goto done; @@ -288,7 +293,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld) const Obj_Entry *defobj; def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, - false, cache); + false, cache, lockstate); if (def == NULL) goto done; @@ -339,14 +344,14 @@ reloc_plt(Obj_Entry *obj) /* Relocate the jump slots in an object. */ int -reloc_jmpslots(Obj_Entry *obj) +reloc_jmpslots(Obj_Entry *obj, RtldLockState *lockstate) { const Elf_Rel *rellim; const Elf_Rel *rel; if (obj->jmpslots_done) return 0; - rellim = (const Elf_Rel *)((const char *)obj->pltrel + obj->pltrelsize); + rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize); for (rel = obj->pltrel; rel < rellim; rel++) { Elf_Addr *where, target; const Elf_Sym *def; @@ -355,7 +360,8 @@ reloc_jmpslots(Obj_Entry *obj) switch (ELF_R_TYPE(rel->r_info)) { case R_386_JMP_SLOT: where = (Elf_Addr *)(obj->relocbase + rel->r_offset); - def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, true, NULL); + def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, true, NULL, + lockstate); if (def == NULL) return (-1); target = (Elf_Addr)(defobj->relocbase + def->st_value); diff --git a/libexec/rtld-elf/rtld.1 b/libexec/rtld-elf/rtld.1 index fb035fd754..edb79c56ba 100644 --- a/libexec/rtld-elf/rtld.1 +++ b/libexec/rtld-elf/rtld.1 @@ -28,7 +28,7 @@ .\" .\" $FreeBSD$ .\" -.Dd February 21, 2011 +.Dd January 20, 2012 .Dt RTLD 1 .Os .Sh NAME @@ -220,6 +220,14 @@ If set, .Nm will log events such as the loading and unloading of shared objects via .Xr utrace 2 . +.Pp +.It Ev LD_LOADFLTR +If set, +.Nm +will process the filtee dependencies of the loaded objects immediately, +instead of postponing it until required. +Normally, the filtees are opened at the time of the first symbol resolution +from the filter object. .El .Pp If a shared object preloaded by the diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index ce457e381a..a165c6402c 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -72,16 +72,6 @@ typedef void (*func_ptr_type)(); typedef void * (*path_enum_proc) (const char *path, size_t len, void *arg); -/* - * This structure provides a reentrant way to keep a list of objects and - * check which ones have already been processed in some way. - */ -typedef struct Struct_DoneList { - const Obj_Entry **objs; /* Array of object pointers */ - unsigned int num_alloc; /* Allocated size of the array */ - unsigned int num_used; /* Number of array slots used */ -} DoneList; - /* * Function declarations. */ @@ -93,6 +83,8 @@ static void digest_dynamic2(Obj_Entry *, const Elf_Dyn *, const Elf_Dyn *); static void digest_dynamic(Obj_Entry *, int); static Obj_Entry *digest_phdr(const Elf_Phdr *, int, caddr_t, const char *); static Obj_Entry *dlcheck(void *); +static Obj_Entry *dlopen_object(const char *name, Obj_Entry *refobj, + int lo_flags, int mode); static Obj_Entry *do_load_object(int, const char *, char *, struct stat *, int); static int do_search_info(const Obj_Entry *obj, int, struct dl_serinfo *); static bool donelist_check(DoneList *, const Obj_Entry *); @@ -109,6 +101,8 @@ static void initlist_add_objects(Obj_Entry *, Obj_Entry **, Objlist *); static bool is_exported(const Elf_Sym *); static void linkmap_add(Obj_Entry *); static void linkmap_delete(Obj_Entry *); +static void load_filtees(Obj_Entry *, int flags, RtldLockState *); +static void unload_filtees(Obj_Entry *); static int load_needed_objects(Obj_Entry *, int); static int load_preload_objects(void); static Obj_Entry *load_object(const char *, const Obj_Entry *, int); @@ -122,20 +116,18 @@ static void objlist_push_head(Objlist *, Obj_Entry *); static void objlist_push_tail(Objlist *, Obj_Entry *); static void objlist_remove(Objlist *, Obj_Entry *); static void *path_enumerate(const char *, path_enum_proc, void *); -static int relocate_objects(Obj_Entry *, bool, Obj_Entry *); +static int relocate_objects(Obj_Entry *, bool, Obj_Entry *, RtldLockState *); static int rtld_dirname(const char *, char *); static int rtld_dirname_abs(const char *, char *); static void rtld_exit(void); static char *search_library_path(const char *, const char *); static const void **get_program_var_addr(const char *); static void set_program_var(const char *, const void *); -static const Elf_Sym *symlook_default(const char *, unsigned long, - const Obj_Entry *, const Obj_Entry **, const Ver_Entry *, int); -static const Elf_Sym *symlook_list(const char *, unsigned long, const Objlist *, - const Obj_Entry **, const Ver_Entry *, int, DoneList *); -static const Elf_Sym *symlook_needed(const char *, unsigned long, - const Needed_Entry *, const Obj_Entry **, const Ver_Entry *, - int, DoneList *); +static int symlook_default(SymLook *, const Obj_Entry *refobj); +static void symlook_init_from_req(SymLook *, const SymLook *); +static int symlook_list(SymLook *, const Objlist *, DoneList *); +static int symlook_needed(SymLook *, const Needed_Entry *, DoneList *); +static int symlook_obj1(SymLook *, const Obj_Entry *); static void trace_loaded_objects(Obj_Entry *); static void unlink_object(Obj_Entry *); static void unload_object(Obj_Entry *); @@ -160,6 +152,7 @@ void r_debug_state(struct r_debug *, struct link_map *) __noinline; static char *error_message; /* Message for dlerror(), or NULL */ struct r_debug r_debug; /* for GDB; */ static bool libmap_disable; /* Disable libmap */ +static bool ld_loadfltr; /* Immediate filters processing */ static char *libmap_override; /* Maps to use in addition to libmap.conf */ static bool trust; /* False for setuid and setgid programs */ static bool dangerous_ld_env; /* True if environment variables have been @@ -177,7 +170,7 @@ static int (*rtld_functrace)( /* Optional function call tracing hook */ const char *callee_obj, const char *callee_func, void *stack); -static Obj_Entry *rtld_functrace_obj; /* Object thereof */ +static const Obj_Entry *rtld_functrace_obj; /* Object thereof */ static Obj_Entry *obj_list; /* Head of linked list of shared objects */ static Obj_Entry **obj_tail; /* Link field of last object in list */ static Obj_Entry **preload_tail; @@ -348,7 +341,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) /* marino: DO NOT MOVE THESE VARIABLES TO _rtld Obj_Entry **preload_tail; Objlist initlist; - from global to here. It will break the DRAWF2 unwind scheme. + from global to here. It will break the DWARF2 unwind scheme. The system compilers were unaffected, but not gcc 4.6 */ @@ -413,20 +406,23 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) || unsetenv("LD_ELF_HINTS_PATH") || unsetenv("LD_LIBMAP") || unsetenv("LD_LIBMAP_DISABLE") + || unsetenv("LD_LOADFLTR") ) { _rtld_error("environment corrupt; aborting"); die(); } } ld_debug = _getenv_ld("LD_DEBUG"); + libmap_disable = _getenv_ld("LD_LIBMAP_DISABLE") != NULL; + libmap_override = (char *)_getenv_ld("LD_LIBMAP"); ld_library_path = _getenv_ld("LD_LIBRARY_PATH"); ld_preload = (char *)_getenv_ld("LD_PRELOAD"); ld_elf_hints_path = _getenv_ld("LD_ELF_HINTS_PATH"); - libmap_override = (char *)_getenv_ld("LD_LIBMAP"); - libmap_disable = _getenv_ld("LD_LIBMAP_DISABLE") != NULL; + ld_loadfltr = _getenv_ld("LD_LOADFLTR") != NULL; dangerous_ld_env = (ld_library_path != NULL) || (ld_preload != NULL) || (ld_elf_hints_path != NULL) + || ld_loadfltr || (libmap_override != NULL) || libmap_disable ; @@ -606,7 +602,7 @@ resident_skip1: */ if (relocate_objects(obj_main, - ld_bind_now != NULL && *ld_bind_now != '\0', &obj_rtld) == -1) + ld_bind_now != NULL && *ld_bind_now != '\0', &obj_rtld, NULL) == -1) die(); dbg("doing copy relocations"); @@ -672,10 +668,16 @@ void _rtld_call_init(void) { RtldLockState lockstate; + Obj_Entry *obj; wlock_acquire(rtld_bind_lock, &lockstate); objlist_call_init(&initlist, &lockstate); objlist_clear(&initlist); + dbg("loading filtees"); + for (obj = obj_list->next; obj != NULL; obj = obj->next) { + if (ld_loadfltr || obj->z_loadfltr) + load_filtees(obj, 0, &lockstate); + } lock_release(rtld_bind_lock, &lockstate); } @@ -688,7 +690,6 @@ _rtld_bind(Obj_Entry *obj, Elf_Size reloff, void *stack) Elf_Addr *where; Elf_Addr target; RtldLockState lockstate; - int do_reloc = 1; rlock_acquire(rtld_bind_lock, &lockstate); if (sigsetjmp(lockstate.env, 0) != 0) @@ -699,7 +700,8 @@ _rtld_bind(Obj_Entry *obj, Elf_Size reloff, void *stack) rel = (const Elf_Rel *) ((caddr_t) obj->pltrela + reloff); where = (Elf_Addr *) (obj->relocbase + rel->r_offset); - def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, true, NULL); + def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, true, NULL, + &lockstate); if (def == NULL) die(); @@ -723,11 +725,18 @@ _rtld_bind(Obj_Entry *obj, Elf_Size reloff, void *stack) defobj->path, defobj->strtab + def->st_name, stack)) - do_reloc = 0; + lock_release(rtld_bind_lock, &lockstate); + return target; } - if (do_reloc) - target = reloc_jmpslot(where, target, defobj, obj, rel); + /* + * Write the new contents for the jmpslot. Note that depending on + * architecture, the value which we need to return back to the + * lazy binding trampoline may or may not be the target + * address. The value returned from reloc_jmpslot() is the value + * that the trampoline needs. + */ + target = reloc_jmpslot(where, target, defobj, obj, rel); lock_release(rtld_bind_lock, &lockstate); return target; } @@ -878,6 +887,8 @@ digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath, { const Elf_Dyn *dynp; Needed_Entry **needed_tail = &obj->needed; + Needed_Entry **needed_filtees_tail = &obj->needed_filtees; + Needed_Entry **needed_aux_filtees_tail = &obj->needed_aux_filtees; int plttype = DT_REL; *dyn_rpath = NULL; @@ -988,6 +999,30 @@ digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath, } break; + case DT_FILTER: + if (!obj->rtld) { + Needed_Entry *nep = NEW(Needed_Entry); + nep->name = dynp->d_un.d_val; + nep->obj = NULL; + nep->next = NULL; + + *needed_filtees_tail = nep; + needed_filtees_tail = &nep->next; + } + break; + + case DT_AUXILIARY: + if (!obj->rtld) { + Needed_Entry *nep = NEW(Needed_Entry); + nep->name = dynp->d_un.d_val; + nep->obj = NULL; + nep->next = NULL; + + *needed_aux_filtees_tail = nep; + needed_aux_filtees_tail = &nep->next; + } + break; + case DT_PLTGOT: obj->pltgot = (Elf_Addr *) (obj->relocbase + dynp->d_un.d_ptr); break; @@ -1052,6 +1087,8 @@ digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath, obj->bind_now = true; if (dynp->d_un.d_val & DF_1_NODELETE) obj->z_nodelete = true; + if (dynp->d_un.d_val & DF_1_LOADFLTR) + obj->z_loadfltr = true; break; default: @@ -1297,14 +1334,15 @@ find_library(const char *xname, const Obj_Entry *refobj) */ const Elf_Sym * find_symdef(unsigned long symnum, const Obj_Entry *refobj, - const Obj_Entry **defobj_out, int flags, SymCache *cache) + const Obj_Entry **defobj_out, int flags, SymCache *cache, + RtldLockState *lockstate) { const Elf_Sym *ref; const Elf_Sym *def; const Obj_Entry *defobj; - const Ver_Entry *ventry; + SymLook req; const char *name; - unsigned long hash; + int res; /* * If we have already found this symbol, get the information from @@ -1319,6 +1357,7 @@ find_symdef(unsigned long symnum, const Obj_Entry *refobj, ref = refobj->symtab + symnum; name = refobj->strtab + ref->st_name; + def = NULL; defobj = NULL; /* @@ -1336,9 +1375,15 @@ find_symdef(unsigned long symnum, const Obj_Entry *refobj, _rtld_error("%s: Bogus symbol table entry %lu", refobj->path, symnum); } - ventry = fetch_ventry(refobj, symnum); - hash = elf_hash(name); - def = symlook_default(name, hash, refobj, &defobj, ventry, flags); + symlook_init(&req, name); + req.flags = flags; + req.ventry = fetch_ventry(refobj, symnum); + req.lockstate = lockstate; + res = symlook_default(&req, refobj); + if (res == 0) { + def = req.sym_out; + defobj = req.defobj_out; + } } else { def = ref; defobj = refobj; @@ -1415,6 +1460,7 @@ init_dag(Obj_Entry *root) return; donelist_init(&donelist); init_dag1(root, root, &donelist); + root->dag_inited = true; } static void @@ -1430,7 +1476,6 @@ init_dag1(Obj_Entry *root, Obj_Entry *obj, DoneList *dlp) for (needed = obj->needed; needed != NULL; needed = needed->next) if (needed->obj != NULL) init_dag1(root, needed->obj, dlp); - root->dag_inited = true; } /* @@ -1469,7 +1514,7 @@ init_rtld(caddr_t mapbase, Elf_Auxinfo **aux_info) * that symbols can be found. */ - relocate_objects(&objtmp, true, &objtmp); + relocate_objects(&objtmp, true, &objtmp, NULL); } /* Initialize the object list. */ @@ -1562,6 +1607,77 @@ is_exported(const Elf_Sym *def) return false; } +static void +free_needed_filtees(Needed_Entry *n) +{ + Needed_Entry *needed, *needed1; + + for (needed = n; needed != NULL; needed = needed->next) { + if (needed->obj != NULL) { + dlclose(needed->obj); + needed->obj = NULL; + } + } + for (needed = n; needed != NULL; needed = needed1) { + needed1 = needed->next; + free(needed); + } +} + +static void +unload_filtees(Obj_Entry *obj) +{ + + free_needed_filtees(obj->needed_filtees); + obj->needed_filtees = NULL; + free_needed_filtees(obj->needed_aux_filtees); + obj->needed_aux_filtees = NULL; + obj->filtees_loaded = false; +} + +static void +load_filtee1(Obj_Entry *obj, Needed_Entry *needed, int flags) +{ + + for (; needed != NULL; needed = needed->next) { + needed->obj = dlopen_object(obj->strtab + needed->name, obj, + flags, ((ld_loadfltr || obj->z_loadfltr) ? RTLD_NOW : RTLD_LAZY) | + RTLD_LOCAL); + } +} + +static void +load_filtees(Obj_Entry *obj, int flags, RtldLockState *lockstate) +{ + + lock_restart_for_upgrade(lockstate); + if (!obj->filtees_loaded) { + load_filtee1(obj, obj->needed_filtees, flags); + load_filtee1(obj, obj->needed_aux_filtees, flags); + obj->filtees_loaded = true; + } +} + +static int +process_needed(Obj_Entry *obj, Needed_Entry *needed, int flags) +{ + Obj_Entry *obj1; + + for (; needed != NULL; needed = needed->next) { + obj1 = needed->obj = load_object(obj->strtab + needed->name, obj, + flags & ~RTLD_LO_NOLOAD); + if (obj1 == NULL && !ld_tracing && (flags & RTLD_LO_FILTEES) == 0) + return (-1); + if (obj1 != NULL && obj1->z_nodelete && !obj1->ref_nodel) { + dbg("obj %s nodelete", obj1->path); + init_dag(obj1); + ref_dag(obj1); + obj1->ref_nodel = true; + } + } + return (0); +} + /* * Given a shared object, traverse its list of needed objects, and load * each of them. Returns 0 on success. Generates an error message and @@ -1570,29 +1686,15 @@ is_exported(const Elf_Sym *def) static int load_needed_objects(Obj_Entry *first, int flags) { - Obj_Entry *obj, *obj1; + Obj_Entry *obj; for (obj = first; obj != NULL; obj = obj->next) { - Needed_Entry *needed; - - for (needed = obj->needed; needed != NULL; needed = needed->next) { - obj1 = needed->obj = load_object(obj->strtab + needed->name, obj, - flags & ~RTLD_LO_NOLOAD); - if (obj1 == NULL && !ld_tracing) - return -1; - if (obj1 != NULL && obj1->z_nodelete && !obj1->ref_nodel) { - dbg("obj %s nodelete", obj1->path); - init_dag(obj1); - ref_dag(obj1); - obj1->ref_nodel = true; - } - } + if (process_needed(obj, obj->needed, flags) == -1) + return (-1); } return (0); } -#define RTLD_FUNCTRACE "_rtld_functrace" - static int load_preload_objects(void) { @@ -1607,7 +1709,8 @@ load_preload_objects(void) size_t len = strcspn(p, delim); char savech; Obj_Entry *obj; - const Elf_Sym *sym; + SymLook req; + int res; savech = p[len]; p[len] = '\0'; @@ -1619,10 +1722,12 @@ load_preload_objects(void) p += strspn(p, delim); /* Check for the magic tracing function */ - sym = symlook_obj(RTLD_FUNCTRACE, elf_hash(RTLD_FUNCTRACE), obj, NULL, 1); - if (sym != NULL) { - rtld_functrace = (void *)(obj->relocbase + sym->st_value); - rtld_functrace_obj = obj; + symlook_init(&req, RTLD_FUNCTRACE); + res = symlook_obj(&req, obj); + if (res == 0) { + rtld_functrace = (void *)(req.defobj_out->relocbase + + req.sym_out->st_value); + rtld_functrace_obj = req.defobj_out; } } LD_UTRACE(UTRACE_PRELOAD_FINISHED, NULL, NULL, 0, 0, NULL); @@ -1929,7 +2034,8 @@ objlist_remove(Objlist *list, Obj_Entry *obj) * or -1 on failure. */ static int -relocate_objects(Obj_Entry *first, bool bind_now, Obj_Entry *rtldobj) +relocate_objects(Obj_Entry *first, bool bind_now, Obj_Entry *rtldobj, + RtldLockState *lockstate) { Obj_Entry *obj; @@ -1954,7 +2060,7 @@ relocate_objects(Obj_Entry *first, bool bind_now, Obj_Entry *rtldobj) } /* Process the non-PLT relocations. */ - if (reloc_non_plt(obj, rtldobj)) + if (reloc_non_plt(obj, rtldobj, lockstate)) return -1; /* @@ -1985,7 +2091,7 @@ relocate_objects(Obj_Entry *first, bool bind_now, Obj_Entry *rtldobj) return -1; /* Relocate the jump slots if we are doing immediate binding. */ if (obj->bind_now || bind_now) - if (reloc_jmpslots(obj) == -1) + if (reloc_jmpslots(obj, lockstate) == -1) return -1; /* Set the special PLT or GOT entries. */ @@ -2162,11 +2268,7 @@ dlerror(void) void * dlopen(const char *name, int mode) { - Obj_Entry **old_obj_tail; - Obj_Entry *obj; - Objlist initlist; - RtldLockState lockstate; - int result, lo_flags; + int lo_flags; LD_UTRACE(UTRACE_DLOPEN_START, NULL, NULL, 0, mode, name); ld_tracing = (mode & RTLD_TRACE) == 0 ? NULL : "1"; @@ -2180,6 +2282,19 @@ dlopen(const char *name, int mode) if (ld_tracing != NULL) lo_flags |= RTLD_LO_TRACE; + return (dlopen_object(name, obj_main, lo_flags, + mode & (RTLD_MODEMASK | RTLD_GLOBAL))); +} + +static Obj_Entry * +dlopen_object(const char *name, Obj_Entry *refobj, int lo_flags, int mode) +{ + Obj_Entry **old_obj_tail; + Obj_Entry *obj; + Objlist initlist; + RtldLockState lockstate; + int result; + objlist_init(&initlist); wlock_acquire(rtld_bind_lock, &lockstate); @@ -2191,25 +2306,24 @@ dlopen(const char *name, int mode) obj = obj_main; obj->refcount++; } else { - obj = load_object(name, obj_main, lo_flags); + obj = load_object(name, refobj, lo_flags); } if (obj) { obj->dl_refcount++; if (mode & RTLD_GLOBAL && objlist_find(&list_global, obj) == NULL) objlist_push_tail(&list_global, obj); - mode &= RTLD_MODEMASK; if (*old_obj_tail != NULL) { /* We loaded something new. */ assert(*old_obj_tail == obj); - result = load_needed_objects(obj, RTLD_LO_DLOPEN); + result = load_needed_objects(obj, lo_flags & RTLD_LO_DLOPEN); init_dag(obj); ref_dag(obj); if (result != -1) result = rtld_verify_versions(&obj->dagmembers); if (result != -1 && ld_tracing) goto trace; - if (result == -1 || - (relocate_objects(obj, mode == RTLD_NOW, &obj_rtld)) == -1) { + if (result == -1 || (relocate_objects(obj, (mode & RTLD_MODEMASK) + == RTLD_NOW, &obj_rtld, &lockstate)) == -1) { obj->dl_refcount--; unref_dag(obj); if (obj->refcount == 0) @@ -2262,14 +2376,17 @@ do_dlsym(void *handle, const char *name, void *retaddr, const Ver_Entry *ve, { DoneList donelist; const Obj_Entry *obj, *defobj; - const Elf_Sym *def, *symp; - unsigned long hash; + const Elf_Sym *def; + SymLook req; RtldLockState lockstate; + int res; - hash = elf_hash(name); def = NULL; defobj = NULL; - flags |= SYMLOOK_IN_PLT; + symlook_init(&req, name); + req.ventry = ve; + req.flags = flags | SYMLOOK_IN_PLT; + req.lockstate = &lockstate; rlock_acquire(rtld_bind_lock, &lockstate); if (sigsetjmp(lockstate.env, 0) != 0) @@ -2283,17 +2400,22 @@ do_dlsym(void *handle, const char *name, void *retaddr, const Ver_Entry *ve, return NULL; } if (handle == NULL) { /* Just the caller's shared object. */ - def = symlook_obj(name, hash, obj, ve, flags); - defobj = obj; + res = symlook_obj(&req, obj); + if (res == 0) { + def = req.sym_out; + defobj = req.defobj_out; + } } else if (handle == RTLD_NEXT || /* Objects after caller's */ handle == RTLD_SELF) { /* ... caller included */ if (handle == RTLD_NEXT) obj = obj->next; for (; obj != NULL; obj = obj->next) { - if ((symp = symlook_obj(name, hash, obj, ve, flags)) != NULL) { - if (def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK) { - def = symp; - defobj = obj; + res = symlook_obj(&req, obj); + if (res == 0) { + if (def == NULL || + 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) break; } @@ -2305,15 +2427,19 @@ do_dlsym(void *handle, const char *name, void *retaddr, const Ver_Entry *ve, * dynamic linker services such as dlopen. */ if (def == NULL || ELF_ST_BIND(def->st_info) == STB_WEAK) { - symp = symlook_obj(name, hash, &obj_rtld, ve, flags); - if (symp != NULL) { - def = symp; - defobj = &obj_rtld; + res = symlook_obj(&req, &obj_rtld); + if (res == 0 && is_exported(req.sym_out)) { + def = req.sym_out; + defobj = req.defobj_out; } } } else { assert(handle == RTLD_DEFAULT); - def = symlook_default(name, hash, obj, &defobj, ve, flags); + res = symlook_default(&req, obj); + if (res == 0) { + defobj = req.defobj_out; + def = req.sym_out; + } } } else { if ((obj = dlcheck(handle)) == NULL) { @@ -2324,18 +2450,23 @@ do_dlsym(void *handle, const char *name, void *retaddr, const Ver_Entry *ve, donelist_init(&donelist); if (obj->mainprog) { /* Search main program and all libraries loaded by it. */ - def = symlook_list(name, hash, &list_main, &defobj, ve, flags, - &donelist); - + res = symlook_list(&req, &list_main, &donelist); + if (res == 0) { + def = req.sym_out; + defobj = req.defobj_out; + } else { /* - * We do not distinguish between 'main' object and global scope. - * If symbol is not defined by objects loaded at startup, continue - * search among dynamically loaded objects with RTLD_GLOBAL - * scope. + * We do not distinguish between 'main' object and + * global scope. If symbol is not defined by objects + * loaded at startup, continue search among + * dynamically loaded objects with RTLD_GLOBAL scope. */ - if (def == NULL) - def = symlook_list(name, hash, &list_global, &defobj, ve, - flags, &donelist); + res = symlook_list(&req, &list_global, &donelist); + if (res == 0) { + def = req.sym_out; + defobj = req.defobj_out; + } + } } else { Needed_Entry fake; @@ -2343,8 +2474,11 @@ do_dlsym(void *handle, const char *name, void *retaddr, const Ver_Entry *ve, fake.next = NULL; fake.obj = (Obj_Entry *)obj; fake.name = 0; - def = symlook_needed(name, hash, &fake, &defobj, ve, flags, - &donelist); + res = symlook_needed(&req, &fake, &donelist); + if (res == 0) { + def = req.sym_out; + defobj = req.defobj_out; + } } } @@ -2795,17 +2929,13 @@ static const void ** get_program_var_addr(const char *name) { const Obj_Entry *obj; - unsigned long hash; + SymLook req; - hash = elf_hash(name); + symlook_init(&req, name); for (obj = obj_main; obj != NULL; obj = obj->next) { - const Elf_Sym *def; - - if ((def = symlook_obj(name, hash, obj, NULL, 0)) != NULL) { - const void **addr; - - addr = (const void **)(obj->relocbase + def->st_value); - return addr; + if (symlook_obj(&req, obj) == 0) { + return ((const void **)(req.defobj_out->relocbase + + req.sym_out->st_value)); } } return (NULL); @@ -2863,37 +2993,38 @@ _getenv_ld(const char *id) * no definition was found. Returns a pointer to the Obj_Entry of the * defining object via the reference parameter DEFOBJ_OUT. */ -static const Elf_Sym * -symlook_default(const char *name, unsigned long hash, const Obj_Entry *refobj, - const Obj_Entry **defobj_out, const Ver_Entry *ventry, int flags) +static int +symlook_default(SymLook *req, const Obj_Entry *refobj) { DoneList donelist; const Elf_Sym *def; - const Elf_Sym *symp; - const Obj_Entry *obj; const Obj_Entry *defobj; const Objlist_Entry *elm; + SymLook req1; + int res; def = NULL; defobj = NULL; donelist_init(&donelist); + symlook_init_from_req(&req1, req); /* Look first in the referencing object if linked symbolically. */ if (refobj->symbolic && !donelist_check(&donelist, refobj)) { - symp = symlook_obj(name, hash, refobj, ventry, flags); - if (symp != NULL) { - def = symp; - defobj = refobj; + res = symlook_obj(&req1, refobj); + if (res == 0) { + def = req1.sym_out; + defobj = req1.defobj_out; + assert(defobj != NULL); } } /* Search all objects loaded at program start up. */ if (def == NULL || ELF_ST_BIND(def->st_info) == STB_WEAK) { - symp = symlook_list(name, hash, &list_main, &obj, ventry, flags, - &donelist); - if (symp != NULL && - (def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK)) { - def = symp; - defobj = obj; + res = symlook_list(&req1, &list_main, &donelist); + if (res == 0 && + (def == NULL || ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK)) { + def = req1.sym_out; + defobj = req1.defobj_out; + assert(defobj != NULL); } } @@ -2901,12 +3032,12 @@ symlook_default(const char *name, unsigned long hash, const Obj_Entry *refobj, STAILQ_FOREACH(elm, &list_global, link) { if (def != NULL && ELF_ST_BIND(def->st_info) != STB_WEAK) break; - symp = symlook_list(name, hash, &elm->obj->dagmembers, &obj, ventry, - flags, &donelist); - if (symp != NULL && - (def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK)) { - def = symp; - defobj = obj; + res = symlook_list(&req1, &elm->obj->dagmembers, &donelist); + if (res == 0 && + (def == NULL || ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK)) { + def = req1.sym_out; + defobj = req1.defobj_out; + assert(defobj != NULL); } } @@ -2914,12 +3045,12 @@ symlook_default(const char *name, unsigned long hash, const Obj_Entry *refobj, 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, ventry, - flags, &donelist); - if (symp != NULL && - (def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK)) { - def = symp; - defobj = obj; + res = symlook_list(&req1, &elm->obj->dagmembers, &donelist); + if (res == 0 && + (def == NULL || ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK)) { + def = req1.sym_out; + defobj = req1.defobj_out; + assert(defobj != NULL); } } @@ -2930,45 +3061,53 @@ symlook_default(const char *name, unsigned long hash, const Obj_Entry *refobj, * in the "exports" array can be resolved from the dynamic linker. */ if (def == NULL || ELF_ST_BIND(def->st_info) == STB_WEAK) { - symp = symlook_obj(name, hash, &obj_rtld, ventry, flags); - if (symp != NULL && is_exported(symp)) { - def = symp; - defobj = &obj_rtld; + res = symlook_obj(&req1, &obj_rtld); + if (res == 0 && is_exported(req1.sym_out)) { + def = req1.sym_out; + defobj = req1.defobj_out; + assert(defobj != NULL); } } - if (def != NULL) - *defobj_out = defobj; - return def; + if (def != NULL) { + assert(defobj != NULL); + req->defobj_out = defobj; + req->sym_out = def; + return (0); + } + return (ESRCH); } -static const Elf_Sym * -symlook_list(const char *name, unsigned long hash, const Objlist *objlist, - const Obj_Entry **defobj_out, const Ver_Entry *ventry, int flags, - DoneList *dlp) +static int +symlook_list(SymLook *req, const Objlist *objlist, DoneList *dlp) { - const Elf_Sym *symp; const Elf_Sym *def; const Obj_Entry *defobj; const Objlist_Entry *elm; + SymLook req1; + int res; def = NULL; defobj = NULL; STAILQ_FOREACH(elm, objlist, link) { if (donelist_check(dlp, elm->obj)) continue; - if ((symp = symlook_obj(name, hash, elm->obj, ventry, flags)) != NULL) { - if (def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK) { - def = symp; - defobj = elm->obj; + 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) { + def = req1.sym_out; + defobj = req1.defobj_out; if (ELF_ST_BIND(def->st_info) != STB_WEAK) break; } } } - if (def != NULL) - *defobj_out = defobj; - return def; + if (def != NULL) { + req->sym_out = def; + req->defobj_out = defobj; + return (0); + } + return (ESRCH); } /* @@ -2977,26 +3116,28 @@ symlook_list(const char *name, unsigned long hash, const Objlist *objlist, * breadth-first. Returns a pointer to the symbol, or NULL if no * definition was found. */ -static const Elf_Sym * -symlook_needed(const char *name, unsigned long hash, const Needed_Entry *needed, - const Obj_Entry **defobj_out, const Ver_Entry *ventry, int flags, - DoneList *dlp) +static int +symlook_needed(SymLook *req, const Needed_Entry *needed, DoneList *dlp) { const Elf_Sym *def, *def_w; const Needed_Entry *n; - const Obj_Entry *obj, *defobj, *defobj1; + const Obj_Entry *defobj, *defobj1; + SymLook req1; + int res; def = def_w = NULL; defobj = NULL; + symlook_init_from_req(&req1, req); for (n = needed; n != NULL; n = n->next) { - if ((obj = n->obj) == NULL || - donelist_check(dlp, obj) || - (def = symlook_obj(name, hash, obj, ventry, flags)) == NULL) + if (n->obj == NULL || donelist_check(dlp, n->obj) || + (res = symlook_obj(&req1, n->obj)) != 0) continue; - defobj = obj; + def = req1.sym_out; + defobj = req1.defobj_out; if (ELF_ST_BIND(def->st_info) != STB_WEAK) { - *defobj_out = defobj; - return (def); + req->defobj_out = defobj; + req->sym_out = def; + return (0); } } /* @@ -3004,12 +3145,13 @@ symlook_needed(const char *name, unsigned long hash, const Needed_Entry *needed, * directly needed objects, or found symbol is weak. */ for (n = needed; n != NULL; n = n->next) { - if ((obj = n->obj) == NULL) + if (n->obj == NULL) continue; - def_w = symlook_needed(name, hash, obj->needed, &defobj1, - ventry, flags, dlp); - if (def_w == NULL) + res = symlook_needed(&req1, n->obj->needed, dlp); + if (res != 0) continue; + def_w = req1.sym_out; + defobj1 = req1.defobj_out; if (def == NULL || ELF_ST_BIND(def_w->st_info) != STB_WEAK) { def = def_w; defobj = defobj1; @@ -3017,22 +3159,60 @@ symlook_needed(const char *name, unsigned long hash, const Needed_Entry *needed, if (ELF_ST_BIND(def_w->st_info) != STB_WEAK) break; } - if (def != NULL) - *defobj_out = defobj; - return (def); + if (def != NULL) { + req->sym_out = def; + req->defobj_out = defobj; + return (0); + } + return (ESRCH); } /* * Search the symbol table of a single shared object for a symbol of * the given name and version, if requested. Returns a pointer to the - * symbol, or NULL if no definition was found. + * symbol, or NULL if no definition was found. If the object is + * filter, return filtered symbol from filtee. * * The symbol's hash value is passed in for efficiency reasons; that * eliminates many recomputations of the hash value. */ -const Elf_Sym * -symlook_obj(const char *name, unsigned long hash, const Obj_Entry *obj, - const Ver_Entry *ventry, int flags) +int +symlook_obj(SymLook *req, const Obj_Entry *obj) +{ + DoneList donelist; + SymLook req1; + int res, mres; + + mres = symlook_obj1(req, obj); + if (mres == 0) { + if (obj->needed_filtees != NULL) { + load_filtees(__DECONST(Obj_Entry *, obj), 0, req->lockstate); + donelist_init(&donelist); + symlook_init_from_req(&req1, req); + res = symlook_needed(&req1, obj->needed_filtees, &donelist); + if (res == 0) { + req->sym_out = req1.sym_out; + req->defobj_out = req1.defobj_out; + } + return (res); + } + if (obj->needed_aux_filtees != NULL) { + load_filtees(__DECONST(Obj_Entry *, obj), 0, req->lockstate); + donelist_init(&donelist); + symlook_init_from_req(&req1, req); + res = symlook_needed(&req1, obj->needed_aux_filtees, &donelist); + if (res == 0) { + req->sym_out = req1.sym_out; + req->defobj_out = req1.defobj_out; + return (res); + } + } + } + return (mres); +} + +static int +symlook_obj1(SymLook *req, const Obj_Entry *obj) { unsigned long symnum; const Elf_Sym *vsymp; @@ -3040,18 +3220,18 @@ symlook_obj(const char *name, unsigned long hash, const Obj_Entry *obj, int vcount; if (obj->buckets == NULL) - return NULL; + return (ESRCH); vsymp = NULL; vcount = 0; - symnum = obj->buckets[hash % obj->nbuckets]; + symnum = obj->buckets[req->hash % obj->nbuckets]; for (; symnum != STN_UNDEF; symnum = obj->chains[symnum]) { const Elf_Sym *symp; const char *strp; if (symnum >= obj->nchains) - return NULL; /* Bad object */ + return (ESRCH); /* Bad object */ symp = obj->symtab + symnum; strp = obj->strtab + symp->st_name; @@ -3066,17 +3246,17 @@ symlook_obj(const char *name, unsigned long hash, const Obj_Entry *obj, case STT_TLS: if (symp->st_shndx != SHN_UNDEF) break; - else if (((flags & SYMLOOK_IN_PLT) == 0) && + else if (((req->flags & SYMLOOK_IN_PLT) == 0) && (ELF_ST_TYPE(symp->st_info) == STT_FUNC)) break; /* fallthrough */ default: continue; } - if (name[0] != strp[0] || strcmp(name, strp) != 0) + if (req->name[0] != strp[0] || strcmp(req->name, strp) != 0) continue; - if (ventry == NULL) { + if (req->ventry == NULL) { if (obj->versyms != NULL) { verndx = VER_NDX(obj->versyms[symnum]); if (verndx > obj->vernum) { @@ -3095,8 +3275,12 @@ symlook_obj(const char *name, unsigned long hash, const Obj_Entry *obj, * end of the function. If symbol is global (verndx < 2) * accept it unconditionally. */ - if ((flags & SYMLOOK_DLSYM) == 0 && verndx == VER_NDX_GIVEN) - return symp; + if ((req->flags & SYMLOOK_DLSYM) == 0 && + verndx == VER_NDX_GIVEN) { + req->sym_out = symp; + req->defobj_out = obj; + return (0); + } else if (verndx >= VER_NDX_GIVEN) { if ((obj->versyms[symnum] & VER_NDX_HIDDEN) == 0) { if (vsymp == NULL) @@ -3106,13 +3290,15 @@ symlook_obj(const char *name, unsigned long hash, const Obj_Entry *obj, continue; } } - return symp; + req->sym_out = symp; + req->defobj_out = obj; + return (0); } else { if (obj->versyms == NULL) { - if (object_match_name(obj, ventry->name)) { + if (object_match_name(obj, req->ventry->name)) { _rtld_error("%s: object %s should provide version %s for " - "symbol %s", obj_rtld.path, obj->path, ventry->name, - obj->strtab + symnum); + "symbol %s", obj_rtld.path, obj->path, + req->ventry->name, obj->strtab + symnum); continue; } } else { @@ -3122,8 +3308,8 @@ symlook_obj(const char *name, unsigned long hash, const Obj_Entry *obj, obj->path, obj->strtab + symnum, verndx); continue; } - if (obj->vertab[verndx].hash != ventry->hash || - strcmp(obj->vertab[verndx].name, ventry->name)) { + if (obj->vertab[verndx].hash != req->ventry->hash || + strcmp(obj->vertab[verndx].name, req->ventry->name)) { /* * Version does not match. Look if this is a global symbol * and if it is not hidden. If global symbol (verndx < 2) @@ -3131,16 +3317,23 @@ symlook_obj(const char *name, unsigned long hash, const Obj_Entry *obj, * called by dlvsym, because dlvsym looks for a specific * version and default one is not what dlvsym wants. */ - if ((flags & SYMLOOK_DLSYM) || + if ((req->flags & SYMLOOK_DLSYM) || (obj->versyms[symnum] & VER_NDX_HIDDEN) || (verndx >= VER_NDX_GIVEN)) continue; } } - return symp; + req->sym_out = symp; + req->defobj_out = obj; + return (0); } } - return (vcount == 1) ? vsymp : NULL; + if (vcount == 1) { + req->sym_out = vsymp; + req->defobj_out = obj; + return (0); + } + return (ESRCH); } static void @@ -3257,6 +3450,7 @@ unload_object(Obj_Entry *root) LD_UTRACE(UTRACE_UNLOAD_OBJECT, obj, obj->mapbase, obj->mapsize, 0, obj->path); dbg("unloading \"%s\"", obj->path); + unload_filtees(root); munmap(obj->mapbase, obj->mapsize); linkmap_delete(obj); *linkp = obj->next; @@ -3780,7 +3974,33 @@ fetch_ventry(const Obj_Entry *obj, unsigned long symnum) return NULL; } +void +symlook_init(SymLook *dst, const char *name) +{ + + bzero(dst, sizeof(*dst)); + dst->name = name; + dst->hash = elf_hash(name); +} + +static void +symlook_init_from_req(SymLook *dst, const SymLook *src) +{ + + dst->name = src->name; + dst->hash = src->hash; + dst->ventry = src->ventry; + dst->flags = src->flags; + dst->defobj_out = NULL; + dst->sym_out = NULL; + dst->lockstate = src->lockstate; +} + #ifdef ENABLE_OSRELDATE +/* + * Overrides for libc_pic-provided functions. + */ + int __getosreldate(void) { diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h index 9b4eea86ee..9b58185434 100644 --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -184,6 +184,8 @@ typedef struct Struct_Obj_Entry { char *rpath; /* Search path specified in object */ Needed_Entry *needed; /* Shared objects needed by this one (%) */ + Needed_Entry *needed_filtees; + Needed_Entry *needed_aux_filtees; STAILQ_HEAD(, Struct_Name_Entry) names; /* List of names for this object we know about. */ @@ -206,10 +208,12 @@ typedef struct Struct_Obj_Entry { bool z_origin : 1; /* Process rpath and soname tokens */ bool z_nodelete : 1; /* Do not unload the object and dependencies */ bool z_noopen : 1; /* Do not load on dlopen */ + bool z_loadfltr : 1; /* Immediately load filtees */ bool ref_nodel : 1; /* Refcount increased to prevent dlclose */ bool init_scanned: 1; /* Object is already on init list. */ bool on_fini_list: 1; /* Object is already on fini list. */ bool dag_inited : 1; /* Object has its DAG initialized. */ + bool filtees_loaded : 1; /* Filtees loaded */ struct link_map linkmap; /* For GDB and dlinfo() */ Objlist dldags; /* Object belongs to these dlopened DAGs (%) */ @@ -222,6 +226,8 @@ typedef struct Struct_Obj_Entry { #define RTLD_MAGIC 0xd550b87a #define RTLD_VERSION 1 +#define RTLD_FUNCTRACE "_rtld_functrace" + /* Flags to be passed into symlook_ family of functions. */ #define SYMLOOK_IN_PLT 0x01 /* Lookup for PLT symbol */ #define SYMLOOK_DLSYM 0x02 /* Return newest versioned symbol. Used by @@ -230,8 +236,9 @@ typedef struct Struct_Obj_Entry { /* Flags for load_object(). */ #define RTLD_LO_NOLOAD 0x01 /* dlopen() specified RTLD_NOLOAD. */ #define RTLD_LO_DLOPEN 0x02 /* Load_object() called from dlopen(). */ -#define RTLD_LO_TRACE 0x04 /* Only tracing. */ -#define RTLD_LO_NODELETE 0x08 /* Loaded object cannot be closed. */ +#define RTLD_LO_TRACE 0x04 /* Only tracing. */ +#define RTLD_LO_NODELETE 0x08 /* Loaded object cannot be closed. */ +#define RTLD_LO_FILTEES 0x10 /* Loading filtee. */ /* * Symbol cache entry used during relocation to avoid multiple lookups @@ -242,11 +249,34 @@ typedef struct Struct_SymCache { const Obj_Entry *obj; /* Shared object which defines it */ } SymCache; +/* + * This structure provides a reentrant way to keep a list of objects and + * check which ones have already been processed in some way. + */ +typedef struct Struct_DoneList { + const Obj_Entry **objs; /* Array of object pointers */ + unsigned int num_alloc; /* Allocated size of the array */ + unsigned int num_used; /* Number of array slots used */ +} DoneList; + struct Struct_RtldLockState { int lockstate; sigjmp_buf env; }; +/* + * The pack of arguments and results for the symbol lookup functions. + */ +typedef struct Struct_SymLook { + const char *name; + unsigned long hash; + const Ver_Entry *ventry; + int flags; + const Obj_Entry *defobj_out; + const Elf_Sym *sym_out; + struct Struct_RtldLockState *lockstate; +} SymLook; + void _rtld_error(const char *, ...) __printflike(1, 2); Obj_Entry *map_object(int, const char *, const struct stat *); void *xcalloc(size_t); @@ -265,14 +295,14 @@ void dump_Elf_Rela(Obj_Entry *, const Elf_Rela *, u_long); const char *basename(const char *); unsigned long elf_hash(const char *); const Elf_Sym *find_symdef(unsigned long, const Obj_Entry *, - const Obj_Entry **, int, SymCache *); + const Obj_Entry **, int, SymCache *, struct Struct_RtldLockState *); void init_pltgot(Obj_Entry *); void lockdflt_init(void); void obj_free(Obj_Entry *); Obj_Entry *obj_new(void); void _rtld_bind_start(void); -const Elf_Sym *symlook_obj(const char *, unsigned long, const Obj_Entry *, - const Ver_Entry *, int); +void symlook_init(SymLook *, const char *); +int symlook_obj(SymLook *, const Obj_Entry *); void *tls_get_addr_common(Elf_Addr** dtvp, int index, size_t offset); struct tls_tcb *allocate_tls(Obj_Entry *); void free_tls(struct tls_tcb *); @@ -285,9 +315,9 @@ const Ver_Entry *fetch_ventry(const Obj_Entry *obj, unsigned long); * MD function declarations. */ int do_copy_relocations(Obj_Entry *); -int reloc_non_plt(Obj_Entry *, Obj_Entry *); +int reloc_non_plt(Obj_Entry *, Obj_Entry *, struct Struct_RtldLockState *); int reloc_plt(Obj_Entry *); -int reloc_jmpslots(Obj_Entry *); +int reloc_jmpslots(Obj_Entry *, struct Struct_RtldLockState *); void allocate_initial_tls(Obj_Entry *); #endif /* } */ diff --git a/libexec/rtld-elf/x86_64/reloc.c b/libexec/rtld-elf/x86_64/reloc.c index 4bd3c33ab5..d82e0d61ce 100644 --- a/libexec/rtld-elf/x86_64/reloc.c +++ b/libexec/rtld-elf/x86_64/reloc.c @@ -72,23 +72,28 @@ do_copy_relocations(Obj_Entry *dstobj) void *dstaddr; const Elf_Sym *dstsym; const char *name; - unsigned long hash; size_t size; const void *srcaddr; const Elf_Sym *srcsym; - Obj_Entry *srcobj; - const Ver_Entry *ve; + const Obj_Entry *srcobj, *defobj; + SymLook req; + int res; dstaddr = (void *) (dstobj->relocbase + rela->r_offset); dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info); name = dstobj->strtab + dstsym->st_name; - hash = elf_hash(name); size = dstsym->st_size; - ve = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info)); - - for (srcobj = dstobj->next; srcobj != NULL; srcobj = srcobj->next) - if ((srcsym = symlook_obj(name, hash, srcobj, ve, 0)) != NULL) + symlook_init(&req, name); + req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info)); + + for (srcobj = dstobj->next; srcobj != NULL; srcobj = srcobj->next) { + res = symlook_obj(&req, srcobj); + if (res == 0) { + srcsym = req.sym_out; + defobj = req.defobj_out; break; + } + } if (srcobj == NULL) { _rtld_error("Undefined symbol \"%s\" referenced from COPY" @@ -96,7 +101,7 @@ do_copy_relocations(Obj_Entry *dstobj) return -1; } - srcaddr = (const void *) (srcobj->relocbase + srcsym->st_value); + srcaddr = (const void *) (defobj->relocbase + srcsym->st_value); memcpy(dstaddr, srcaddr, size); } } @@ -116,7 +121,7 @@ init_pltgot(Obj_Entry *obj) /* Process the non-PLT relocations. */ int -reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld) +reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, RtldLockState *lockstate) { const Elf_Rela *relalim; const Elf_Rela *rela; @@ -149,7 +154,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld) const Obj_Entry *defobj; def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, - false, cache); + false, cache, lockstate); if (def == NULL) goto done; @@ -168,7 +173,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld) const Obj_Entry *defobj; def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, - false, cache); + false, cache, lockstate); if (def == NULL) goto done; @@ -198,7 +203,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld) const Obj_Entry *defobj; def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, - false, cache); + false, cache, lockstate); if (def == NULL) goto done; @@ -212,7 +217,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld) const Obj_Entry *defobj; def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, - false, cache); + false, cache, lockstate); if (def == NULL) goto done; @@ -243,7 +248,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld) const Obj_Entry *defobj; def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, - false, cache); + false, cache, lockstate); if (def == NULL) goto done; @@ -275,7 +280,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld) const Obj_Entry *defobj; def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, - false, cache); + false, cache, lockstate); if (def == NULL) goto done; @@ -289,7 +294,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld) const Obj_Entry *defobj; def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, - false, cache); + false, cache, lockstate); if (def == NULL) goto done; @@ -303,7 +308,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld) const Obj_Entry *defobj; def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, - false, cache); + false, cache, lockstate); if (def == NULL) goto done; @@ -360,7 +365,7 @@ reloc_plt(Obj_Entry *obj) /* Relocate the jump slots in an object. */ int -reloc_jmpslots(Obj_Entry *obj) +reloc_jmpslots(Obj_Entry *obj, RtldLockState *lockstate) { const Elf_Rela *relalim; const Elf_Rela *rela; @@ -376,7 +381,8 @@ reloc_jmpslots(Obj_Entry *obj) switch (ELF_R_TYPE(rela->r_info)) { case R_X86_64_JMP_SLOT: where = (Elf_Addr *)(obj->relocbase + rela->r_offset); - def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, true, NULL); + def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, true, NULL, + lockstate); if (def == NULL) return (-1); target = (Elf_Addr)(defobj->relocbase + def->st_value + rela->r_addend); -- 2.41.0