summaryrefslogtreecommitdiffstats
path: root/libexec/rtld-elf/rtld.c
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2012-07-15 10:53:48 +0000
committerkib <kib@FreeBSD.org>2012-07-15 10:53:48 +0000
commita78a2e3c36e31109393e2a502b2f8f54f3a7f8d0 (patch)
treebbdaeedcb61722e97f659e43f80ef46bc3c33597 /libexec/rtld-elf/rtld.c
parent4d945ad412911425ef9b9dcb54ae11c825cd2bf7 (diff)
downloadFreeBSD-src-a78a2e3c36e31109393e2a502b2f8f54f3a7f8d0.zip
FreeBSD-src-a78a2e3c36e31109393e2a502b2f8f54f3a7f8d0.tar.gz
Import the DragonFly BSD commit 4f0bc915b65fcf5a23214f6d221d65c80be68ad4
by John Marino <draco@marino.st>, 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
Diffstat (limited to 'libexec/rtld-elf/rtld.c')
-rw-r--r--libexec/rtld-elf/rtld.c285
1 files changed, 220 insertions, 65 deletions
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);
}
OpenPOWER on IntegriCloud