add recursive rpath support to dynamic linker

previously, rpath was only honored for direct dependencies. in other
words, if A depends on B and B depends on C, only B's rpath (if any),
not A's rpath, was being searched for C. this limitation made
rpath-based deployment difficult in the presence of multiple levels of
library dependency.

at present, $ORIGIN processing in rpath is still unsupported.
This commit is contained in:
Rich Felker 2013-08-23 11:15:40 -04:00
parent 8b491f1499
commit 709355e1f6

View File

@ -72,7 +72,8 @@ struct dso {
signed char global; signed char global;
char relocated; char relocated;
char constructed; char constructed;
struct dso **deps; struct dso **deps, *needed_by;
char *rpath;
void *tls_image; void *tls_image;
size_t tls_len, tls_size, tls_align, tls_id, tls_offset; size_t tls_len, tls_size, tls_align, tls_id, tls_offset;
void **new_dtv; void **new_dtv;
@ -95,7 +96,7 @@ void *__install_initial_tls(void *);
void __init_libc(char **, char *); void __init_libc(char **, char *);
static struct dso *head, *tail, *ldso, *fini_head; static struct dso *head, *tail, *ldso, *fini_head;
static char *env_path, *sys_path, *r_path; static char *env_path, *sys_path;
static unsigned long long gencnt; static unsigned long long gencnt;
static int ssp_used; static int ssp_used;
static int runtime; static int runtime;
@ -455,13 +456,15 @@ static void decode_dyn(struct dso *p)
p->strings = (void *)(p->base + dyn[DT_STRTAB]); p->strings = (void *)(p->base + dyn[DT_STRTAB]);
if (dyn[0]&(1<<DT_HASH)) if (dyn[0]&(1<<DT_HASH))
p->hashtab = (void *)(p->base + dyn[DT_HASH]); p->hashtab = (void *)(p->base + dyn[DT_HASH]);
if (dyn[0]&(1<<DT_RPATH))
p->rpath = (void *)(p->strings + dyn[DT_RPATH]);
if (search_vec(p->dynv, dyn, DT_GNU_HASH)) if (search_vec(p->dynv, dyn, DT_GNU_HASH))
p->ghashtab = (void *)(p->base + *dyn); p->ghashtab = (void *)(p->base + *dyn);
if (search_vec(p->dynv, dyn, DT_VERSYM)) if (search_vec(p->dynv, dyn, DT_VERSYM))
p->versym = (void *)(p->base + *dyn); p->versym = (void *)(p->base + *dyn);
} }
static struct dso *load_library(const char *name) static struct dso *load_library(const char *name, struct dso *needed_by)
{ {
char buf[2*NAME_MAX+2]; char buf[2*NAME_MAX+2];
const char *pathname; const char *pathname;
@ -521,7 +524,9 @@ static struct dso *load_library(const char *name)
if (strlen(name) > NAME_MAX) return 0; if (strlen(name) > NAME_MAX) return 0;
fd = -1; fd = -1;
if (env_path) fd = path_open(name, env_path, buf, sizeof buf); if (env_path) fd = path_open(name, env_path, buf, sizeof buf);
if (fd < 0 && r_path) fd = path_open(name, r_path, buf, sizeof buf); for (p=needed_by; fd < 0 && p; p=p->needed_by)
if (p->rpath)
fd = path_open(name, p->rpath, buf, sizeof buf);
if (fd < 0) { if (fd < 0) {
if (!sys_path) { if (!sys_path) {
char *prefix = 0; char *prefix = 0;
@ -601,6 +606,7 @@ static struct dso *load_library(const char *name)
p->dev = st.st_dev; p->dev = st.st_dev;
p->ino = st.st_ino; p->ino = st.st_ino;
p->refcnt = 1; p->refcnt = 1;
p->needed_by = needed_by;
p->name = p->buf; p->name = p->buf;
strcpy(p->name, pathname); strcpy(p->name, pathname);
/* Add a shortname only if name arg was not an explicit pathname. */ /* Add a shortname only if name arg was not an explicit pathname. */
@ -642,13 +648,9 @@ static void load_deps(struct dso *p)
size_t i, ndeps=0; size_t i, ndeps=0;
struct dso ***deps = &p->deps, **tmp, *dep; struct dso ***deps = &p->deps, **tmp, *dep;
for (; p; p=p->next) { for (; p; p=p->next) {
for (i=0; p->dynv[i]; i+=2) {
if (p->dynv[i] != DT_RPATH) continue;
r_path = (void *)(p->strings + p->dynv[i+1]);
}
for (i=0; p->dynv[i]; i+=2) { for (i=0; p->dynv[i]; i+=2) {
if (p->dynv[i] != DT_NEEDED) continue; if (p->dynv[i] != DT_NEEDED) continue;
dep = load_library(p->strings + p->dynv[i+1]); dep = load_library(p->strings + p->dynv[i+1], p);
if (!dep) { if (!dep) {
snprintf(errbuf, sizeof errbuf, snprintf(errbuf, sizeof errbuf,
"Error loading shared library %s: %m (needed by %s)", "Error loading shared library %s: %m (needed by %s)",
@ -666,7 +668,6 @@ static void load_deps(struct dso *p)
*deps = tmp; *deps = tmp;
} }
} }
r_path = 0;
} }
} }
@ -679,7 +680,7 @@ static void load_preload(char *s)
for (z=s; *z && !isspace(*z); z++); for (z=s; *z && !isspace(*z); z++);
tmp = *z; tmp = *z;
*z = 0; *z = 0;
load_library(s); load_library(s, 0);
*z = tmp; *z = tmp;
} }
} }
@ -1153,7 +1154,7 @@ void *dlopen(const char *file, int mode)
p = 0; p = 0;
errflag = 1; errflag = 1;
goto end; goto end;
} else p = load_library(file); } else p = load_library(file, 0);
if (!p) { if (!p) {
snprintf(errbuf, sizeof errbuf, noload ? snprintf(errbuf, sizeof errbuf, noload ?