From a78a2e3c36e31109393e2a502b2f8f54f3a7f8d0 Mon Sep 17 00:00:00 2001 From: kib Date: Sun, 15 Jul 2012 10:53:48 +0000 Subject: Import the DragonFly BSD commit 4f0bc915b65fcf5a23214f6d221d65c80be68ad4 by John Marino , with the following (edited) commit message Date: Sat, 24 Mar 2012 06:40:50 +0100 Subject: [PATCH 1/1] rtld: Implement DT_RUNPATH and -z nodefaultlib DT_RUNPATH is incorrectly being considered as an alias of DT_RPATH. The purpose of DT_RUNPATH is to have two different types of rpath: one that can be overridden by the environment variable LD_LIBRARY_PATH and one that can't. With the currently implementation, LD_LIBRARY_PATH will always trump any embedded rpath or runpath tags. Current path search order by rtld: ================================== LD_LIBRARY_PATH DT_RPATH / DT_RUNPATH (always the same) ldconfig hints file (default: /var/run/ld-elf.so.hints) /usr/lib New path search order by rtld: ============================== DT_RPATH of the calling object if no DT_RUNPATH DT_RPATH of the main binary if no DT_RUNPATH and binary isn't calling obj LD_LIBRARY_PATH DT_RUNPATH ldconfig hints file /usr/lib The new path search matches how the linux runtime loader works. The other major added feature is support for linker flag "-z nodefaultlib". When this flag is passed to the linker, rtld will skip all references to the standard library search path ("/usr/lib" in this case but it could handle more color delimited paths) except in DT_RPATH and DT_RUNPATH. New path search order by rtld with -z nodefaultlib flag set: ============================================================ DT_RPATH of the calling object if no DT_RUNPATH DT_RPATH of the main binary if no DT_RUNPATH and binary isn't calling obj LD_LIBRARY_PATH DT_RUNPATH ldconfig hints file (skips all references to /usr/lib) FreeBSD notes: - we fixed some bugs which were submitted to DragonFly and merged there as commit 1ff8a2bd3eb6e5587174c6a983303ea3a79e0002; - we added LD_LIBRARY_PATH_RPATH environment variable to switch to the previous behaviour of considering DT_RPATH a synonym for DT_RUNPATH; - the FreeBSD default search path is /lib:/usr/lib and not /usr/lib. Reviewed by: kan MFC after: 1 month MFC note: flip the ld_library_path_rpath default value for stable/9 --- libexec/rtld-elf/rtld.c | 285 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 220 insertions(+), 65 deletions(-) (limited to 'libexec/rtld-elf/rtld.c') diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index 723ba7c..95358aa 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -80,8 +80,9 @@ typedef void * (*path_enum_proc) (const char *path, size_t len, void *arg); static const char *basename(const char *); static void die(void) __dead2; static void digest_dynamic1(Obj_Entry *, int, const Elf_Dyn **, - const Elf_Dyn **); -static void digest_dynamic2(Obj_Entry *, const Elf_Dyn *, const Elf_Dyn *); + const Elf_Dyn **, const Elf_Dyn **); +static void digest_dynamic2(Obj_Entry *, const Elf_Dyn *, 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 *); @@ -94,7 +95,7 @@ static void errmsg_restore(char *); static char *errmsg_save(void); static void *fill_search_info(const char *, size_t, void *); static char *find_library(const char *, const Obj_Entry *); -static const char *gethints(void); +static const char *gethints(bool); static void init_dag(Obj_Entry *); static void init_rtld(caddr_t, Elf_Auxinfo **); static void initlist_add_neededs(Needed_Entry *, Objlist *); @@ -233,6 +234,8 @@ size_t tls_static_space; /* Static TLS space allocated */ int tls_dtv_generation = 1; /* Used to detect when dtv size changes */ int tls_max_index = 1; /* Largest module index allocated */ +bool ld_library_path_rpath = false; + /* * Fill in a DoneList with an allocation large enough to hold all of * the currently-loaded objects. Keep this as a macro since it calls @@ -323,6 +326,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) Obj_Entry **preload_tail; Objlist initlist; RtldLockState lockstate; + char *library_path_rpath; int mib[2]; size_t len; @@ -394,7 +398,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) if (unsetenv(LD_ "PRELOAD") || unsetenv(LD_ "LIBMAP") || unsetenv(LD_ "LIBRARY_PATH") || unsetenv(LD_ "LIBMAP_DISABLE") || unsetenv(LD_ "DEBUG") || unsetenv(LD_ "ELF_HINTS_PATH") || - unsetenv(LD_ "LOADFLTR")) { + unsetenv(LD_ "LOADFLTR") || unsetenv(LD_ "LIBRARY_PATH_RPATH")) { _rtld_error("environment corrupt; aborting"); die(); } @@ -406,6 +410,15 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) ld_preload = getenv(LD_ "PRELOAD"); ld_elf_hints_path = getenv(LD_ "ELF_HINTS_PATH"); ld_loadfltr = getenv(LD_ "LOADFLTR") != NULL; + library_path_rpath = getenv(LD_ "LIBRARY_PATH_RPATH"); + if (library_path_rpath != NULL) { + if (library_path_rpath[0] == 'y' || + library_path_rpath[0] == 'Y' || + library_path_rpath[0] == '1') + ld_library_path_rpath = true; + else + ld_library_path_rpath = false; + } dangerous_ld_env = libmap_disable || (libmap_override != NULL) || (ld_library_path != NULL) || (ld_preload != NULL) || (ld_elf_hints_path != NULL) || ld_loadfltr; @@ -828,7 +841,7 @@ die(void) */ static void digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath, - const Elf_Dyn **dyn_soname) + const Elf_Dyn **dyn_soname, const Elf_Dyn **dyn_runpath) { const Elf_Dyn *dynp; Needed_Entry **needed_tail = &obj->needed; @@ -843,6 +856,7 @@ digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath, *dyn_rpath = NULL; *dyn_soname = NULL; + *dyn_runpath = NULL; obj->bind_now = false; for (dynp = obj->dynamic; dynp->d_tag != DT_NULL; dynp++) { @@ -1009,7 +1023,6 @@ digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath, break; case DT_RPATH: - case DT_RUNPATH: /* XXX: process separately */ /* * We have to wait until later to process this, because we * might not have gotten the address of the string table yet. @@ -1021,6 +1034,10 @@ digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath, *dyn_soname = dynp; break; + case DT_RUNPATH: + *dyn_runpath = dynp; + break; + case DT_INIT: obj->init = (Elf_Addr) (obj->relocbase + dynp->d_un.d_ptr); break; @@ -1114,6 +1131,8 @@ digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath, obj->z_nodelete = true; if (dynp->d_un.d_val & DF_1_LOADFLTR) obj->z_loadfltr = true; + if (dynp->d_un.d_val & DF_1_NODEFLIB) + obj->z_nodeflib = true; break; default: @@ -1153,7 +1172,7 @@ digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath, static void digest_dynamic2(Obj_Entry *obj, const Elf_Dyn *dyn_rpath, - const Elf_Dyn *dyn_soname) + const Elf_Dyn *dyn_soname, const Elf_Dyn *dyn_runpath) { if (obj->z_origin && obj->origin_path == NULL) { @@ -1162,7 +1181,12 @@ digest_dynamic2(Obj_Entry *obj, const Elf_Dyn *dyn_rpath, die(); } - if (dyn_rpath != NULL) { + if (dyn_runpath != NULL) { + obj->runpath = (char *)obj->strtab + dyn_runpath->d_un.d_val; + if (obj->z_origin) + obj->runpath = origin_subst(obj->runpath, obj->origin_path); + } + else if (dyn_rpath != NULL) { obj->rpath = (char *)obj->strtab + dyn_rpath->d_un.d_val; if (obj->z_origin) obj->rpath = origin_subst(obj->rpath, obj->origin_path); @@ -1177,9 +1201,10 @@ digest_dynamic(Obj_Entry *obj, int early) { const Elf_Dyn *dyn_rpath; const Elf_Dyn *dyn_soname; + const Elf_Dyn *dyn_runpath; - digest_dynamic1(obj, early, &dyn_rpath, &dyn_soname); - digest_dynamic2(obj, dyn_rpath, dyn_soname); + digest_dynamic1(obj, early, &dyn_rpath, &dyn_soname, &dyn_runpath); + digest_dynamic2(obj, dyn_rpath, dyn_soname, dyn_runpath); } /* @@ -1389,43 +1414,71 @@ gnu_hash(const char *s) * loaded shared object, whose library search path will be searched. * * The search order is: + * DT_RPATH in the referencing file _unless_ DT_RUNPATH is present (1) + * DT_RPATH of the main object if DSO without defined DT_RUNPATH (1) * LD_LIBRARY_PATH - * rpath in the referencing file - * ldconfig hints - * /lib:/usr/lib + * DT_RUNPATH in the referencing file + * ldconfig hints (if -z nodefaultlib, filter out default library directories + * from list) + * /lib:/usr/lib _unless_ the referencing file is linked with -z nodefaultlib + * + * (1) Handled in digest_dynamic2 - rpath left NULL if runpath defined. */ static char * find_library(const char *xname, const Obj_Entry *refobj) { char *pathname; char *name; + bool objgiven; + objgiven = refobj != NULL; if (strchr(xname, '/') != NULL) { /* Hard coded pathname */ if (xname[0] != '/' && !trust) { _rtld_error("Absolute pathname required for shared object \"%s\"", xname); return NULL; } - if (refobj != NULL && refobj->z_origin) + if (objgiven && refobj->z_origin) return origin_subst(xname, refobj->origin_path); else return xstrdup(xname); } - if (libmap_disable || (refobj == NULL) || + if (libmap_disable || !objgiven || (name = lm_find(refobj->path, xname)) == NULL) name = (char *)xname; dbg(" Searching for \"%s\"", name); - if ((pathname = search_library_path(name, ld_library_path)) != NULL || - (refobj != NULL && - (pathname = search_library_path(name, refobj->rpath)) != NULL) || - (pathname = search_library_path(name, gethints())) != NULL || - (pathname = search_library_path(name, STANDARD_LIBRARY_PATH)) != NULL) - return pathname; + /* + * If refobj->rpath != NULL, then refobj->runpath is NULL. Fall + * back to pre-conforming behaviour if user requested so with + * LD_LIBRARY_PATH_RPATH environment variable and ignore -z + * nodeflib. + */ + if (objgiven && refobj->rpath != NULL && ld_library_path_rpath) { + if ((pathname = search_library_path(name, ld_library_path)) != NULL || + (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) + return (pathname); + } else { + if ((objgiven && + (pathname = search_library_path(name, refobj->rpath)) != NULL) || + (objgiven && refobj->runpath == NULL && refobj != obj_main && + (pathname = search_library_path(name, obj_main->rpath)) != NULL) || + (pathname = search_library_path(name, ld_library_path)) != NULL || + (objgiven && + (pathname = search_library_path(name, refobj->runpath)) != NULL) || + (pathname = search_library_path(name, gethints(refobj->z_nodeflib))) + != NULL || + (objgiven && !refobj->z_nodeflib && + (pathname = search_library_path(name, STANDARD_LIBRARY_PATH)) != NULL)) + return (pathname); + } - if(refobj != NULL && refobj->path != NULL) { + if (objgiven && refobj->path != NULL) { _rtld_error("Shared object \"%s\" not found, required by \"%s\"", name, basename(refobj->path)); } else { @@ -1520,41 +1573,142 @@ find_symdef(unsigned long symnum, const Obj_Entry *refobj, /* * Return the search path from the ldconfig hints file, reading it if - * necessary. Returns NULL if there are problems with the hints file, + * necessary. If nostdlib is true, then the default search paths are + * not added to result. + * + * Returns NULL if there are problems with the hints file, * or if the search path there is empty. */ static const char * -gethints(void) +gethints(bool nostdlib) { - static char *hints; - - if (hints == NULL) { - int fd; + static char *hints, *filtered_path; struct elfhints_hdr hdr; + struct fill_search_info_args sargs, hargs; + struct dl_serinfo smeta, hmeta, *SLPinfo, *hintinfo; + struct dl_serpath *SLPpath, *hintpath; char *p; + unsigned int SLPndx, hintndx, fndx, fcount; + int fd; + size_t flen; + bool skip; + + /* First call, read the hints file */ + if (hints == NULL) { + /* Keep from trying again in case the hints file is bad. */ + hints = ""; + + if ((fd = open(ld_elf_hints_path, O_RDONLY)) == -1) + return (NULL); + if (read(fd, &hdr, sizeof hdr) != sizeof hdr || + hdr.magic != ELFHINTS_MAGIC || + hdr.version != 1) { + close(fd); + return (NULL); + } + p = xmalloc(hdr.dirlistlen + 1); + if (lseek(fd, hdr.strtab + hdr.dirlist, SEEK_SET) == -1 || + read(fd, p, hdr.dirlistlen + 1) != + (ssize_t)hdr.dirlistlen + 1) { + free(p); + close(fd); + return (NULL); + } + hints = p; + close(fd); + } - /* Keep from trying again in case the hints file is bad. */ - hints = ""; + /* + * If caller agreed to receive list which includes the default + * paths, we are done. Otherwise, if we still did not + * calculated filtered result, do it now. + */ + if (!nostdlib) + return (hints[0] != '\0' ? hints : NULL); + if (filtered_path != NULL) + goto filt_ret; - if ((fd = open(ld_elf_hints_path, O_RDONLY)) == -1) - return NULL; - if (read(fd, &hdr, sizeof hdr) != sizeof hdr || - hdr.magic != ELFHINTS_MAGIC || - hdr.version != 1) { - close(fd); - return NULL; - } - p = xmalloc(hdr.dirlistlen + 1); - if (lseek(fd, hdr.strtab + hdr.dirlist, SEEK_SET) == -1 || - read(fd, p, hdr.dirlistlen + 1) != (ssize_t)hdr.dirlistlen + 1) { - free(p); - close(fd); - return NULL; + /* + * Obtain the list of all configured search paths, and the + * list of the default paths. + * + * First estimate the size of the results. + */ + smeta.dls_size = __offsetof(struct dl_serinfo, dls_serpath); + smeta.dls_cnt = 0; + hmeta.dls_size = __offsetof(struct dl_serinfo, dls_serpath); + hmeta.dls_cnt = 0; + + sargs.request = RTLD_DI_SERINFOSIZE; + sargs.serinfo = &smeta; + hargs.request = RTLD_DI_SERINFOSIZE; + hargs.serinfo = &hmeta; + + path_enumerate(STANDARD_LIBRARY_PATH, fill_search_info, &sargs); + path_enumerate(p, fill_search_info, &hargs); + + SLPinfo = xmalloc(smeta.dls_size); + hintinfo = xmalloc(hmeta.dls_size); + + /* + * Next fetch both sets of paths. + */ + sargs.request = RTLD_DI_SERINFO; + sargs.serinfo = SLPinfo; + sargs.serpath = &SLPinfo->dls_serpath[0]; + sargs.strspace = (char *)&SLPinfo->dls_serpath[smeta.dls_cnt]; + + hargs.request = RTLD_DI_SERINFO; + hargs.serinfo = hintinfo; + hargs.serpath = &hintinfo->dls_serpath[0]; + hargs.strspace = (char *)&hintinfo->dls_serpath[hmeta.dls_cnt]; + + path_enumerate(STANDARD_LIBRARY_PATH, fill_search_info, &sargs); + path_enumerate(p, fill_search_info, &hargs); + + /* + * Now calculate the difference between two sets, by excluding + * standard paths from the full set. + */ + fndx = 0; + fcount = 0; + filtered_path = xmalloc(hdr.dirlistlen + 1); + hintpath = &hintinfo->dls_serpath[0]; + for (hintndx = 0; hintndx < hmeta.dls_cnt; hintndx++, hintpath++) { + skip = false; + SLPpath = &SLPinfo->dls_serpath[0]; + /* + * Check each standard path against current. + */ + for (SLPndx = 0; SLPndx < smeta.dls_cnt; SLPndx++, SLPpath++) { + /* matched, skip the path */ + if (!strcmp(hintpath->dls_name, SLPpath->dls_name)) { + skip = true; + break; + } + } + if (skip) + continue; + /* + * Not matched against any standard path, add the path + * to result. Separate consequtive paths with ':'. + */ + if (fcount > 0) { + filtered_path[fndx] = ':'; + fndx++; + } + fcount++; + flen = strlen(hintpath->dls_name); + strncpy((filtered_path + fndx), hintpath->dls_name, flen); + fndx += flen; } - hints = p; - close(fd); - } - return hints[0] != '\0' ? hints : NULL; + filtered_path[fndx] = '\0'; + + free(SLPinfo); + free(hintinfo); + +filt_ret: + return (filtered_path[0] != '\0' ? filtered_path : NULL); } static void @@ -1600,6 +1754,7 @@ init_rtld(caddr_t mapbase, Elf_Auxinfo **aux_info) Obj_Entry objtmp; /* Temporary rtld object */ const Elf_Dyn *dyn_rpath; const Elf_Dyn *dyn_soname; + const Elf_Dyn *dyn_runpath; /* * Conjure up an Obj_Entry structure for the dynamic linker. @@ -1616,7 +1771,7 @@ init_rtld(caddr_t mapbase, Elf_Auxinfo **aux_info) #endif if (RTLD_IS_DYNAMIC()) { objtmp.dynamic = rtld_dynamic(&objtmp); - digest_dynamic1(&objtmp, 1, &dyn_rpath, &dyn_soname); + digest_dynamic1(&objtmp, 1, &dyn_rpath, &dyn_soname, &dyn_runpath); assert(objtmp.needed == NULL); #if !defined(__mips__) /* MIPS has a bogus DT_TEXTREL. */ @@ -1642,7 +1797,7 @@ init_rtld(caddr_t mapbase, Elf_Auxinfo **aux_info) if (aux_info[AT_OSRELDATE] != NULL) osreldate = aux_info[AT_OSRELDATE]->a_un.a_val; - digest_dynamic2(&obj_rtld, dyn_rpath, dyn_soname); + digest_dynamic2(&obj_rtld, dyn_rpath, dyn_soname, dyn_runpath); /* Replace the path with a dynamically allocated copy. */ obj_rtld.path = xstrdup(PATH_RTLD); @@ -3070,14 +3225,6 @@ dl_iterate_phdr(__dl_iterate_hdr_callback callback, void *param) return (error); } -struct fill_search_info_args { - int request; - unsigned int flags; - Dl_serinfo *serinfo; - Dl_serpath *serpath; - char *strspace; -}; - static void * fill_search_info(const char *dir, size_t dirlen, void *param) { @@ -3087,7 +3234,7 @@ fill_search_info(const char *dir, size_t dirlen, void *param) if (arg->request == RTLD_DI_SERINFOSIZE) { arg->serinfo->dls_cnt ++; - arg->serinfo->dls_size += sizeof(Dl_serpath) + dirlen + 1; + arg->serinfo->dls_size += sizeof(struct dl_serpath) + dirlen + 1; } else { struct dl_serpath *s_entry; @@ -3117,10 +3264,12 @@ do_search_info(const Obj_Entry *obj, int request, struct dl_serinfo *info) _info.dls_size = __offsetof(struct dl_serinfo, dls_serpath); _info.dls_cnt = 0; - path_enumerate(ld_library_path, fill_search_info, &args); path_enumerate(obj->rpath, fill_search_info, &args); - path_enumerate(gethints(), fill_search_info, &args); - path_enumerate(STANDARD_LIBRARY_PATH, fill_search_info, &args); + path_enumerate(ld_library_path, fill_search_info, &args); + path_enumerate(obj->runpath, fill_search_info, &args); + path_enumerate(gethints(obj->z_nodeflib), fill_search_info, &args); + if (!obj->z_nodeflib) + path_enumerate(STANDARD_LIBRARY_PATH, fill_search_info, &args); if (request == RTLD_DI_SERINFOSIZE) { @@ -3139,20 +3288,26 @@ do_search_info(const Obj_Entry *obj, int request, struct dl_serinfo *info) args.serpath = &info->dls_serpath[0]; args.strspace = (char *)&info->dls_serpath[_info.dls_cnt]; + args.flags = LA_SER_RUNPATH; + if (path_enumerate(obj->rpath, fill_search_info, &args) != NULL) + return (-1); + args.flags = LA_SER_LIBPATH; if (path_enumerate(ld_library_path, fill_search_info, &args) != NULL) return (-1); args.flags = LA_SER_RUNPATH; - if (path_enumerate(obj->rpath, fill_search_info, &args) != NULL) + if (path_enumerate(obj->runpath, fill_search_info, &args) != NULL) return (-1); args.flags = LA_SER_CONFIG; - if (path_enumerate(gethints(), fill_search_info, &args) != NULL) + if (path_enumerate(gethints(obj->z_nodeflib), fill_search_info, &args) + != NULL) return (-1); args.flags = LA_SER_DEFAULT; - if (path_enumerate(STANDARD_LIBRARY_PATH, fill_search_info, &args) != NULL) + if (!obj->z_nodeflib && + path_enumerate(STANDARD_LIBRARY_PATH, fill_search_info, &args) != NULL) return (-1); return (0); } -- cgit v1.1