From 4fd42c1dc002fa19349fa0d4ef97757eb1815032 Mon Sep 17 00:00:00 2001 From: Matt Fischer Date: Thu, 31 Dec 2009 12:09:10 -0600 Subject: [PATCH] Added support for LD_PRELOAD The LD_PRELOAD environment variable allows the user to specify a list of libraries which should be unconditionally loaded before any others. This makes possible some useful tricks, such as library interposers. Change-Id: I433d775ab08ef63a5fbe7b21f87a5642954fc32f --- linker/linker.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/linker/linker.c b/linker/linker.c index 451e96c2b..8dde9410b 100644 --- a/linker/linker.c +++ b/linker/linker.c @@ -58,6 +58,9 @@ #define LDPATH_BUFSIZE 512 #define LDPATH_MAX 8 +#define LDPRELOAD_BUFSIZE 512 +#define LDPRELOAD_MAX 8 + /* >>> IMPORTANT NOTE - READ ME BEFORE MODIFYING <<< * * Do NOT use malloc() and friends or pthread_*() code here. @@ -112,6 +115,11 @@ static inline int validate_soinfo(soinfo *si) static char ldpaths_buf[LDPATH_BUFSIZE]; static const char *ldpaths[LDPATH_MAX + 1]; +static char ldpreloads_buf[LDPRELOAD_BUFSIZE]; +static const char *ldpreload_names[LDPRELOAD_MAX + 1]; + +static soinfo *preloads[LDPRELOAD_MAX + 1]; + int debug_verbosity; static int pid; @@ -451,6 +459,7 @@ _do_lookup(soinfo *si, const char *name, unsigned *base) Elf32_Sym *s; unsigned *d; soinfo *lsi = si; + int i; /* Look for symbols in the local scope first (the object who is * searching). This happens with C++ templates on i386 for some @@ -459,6 +468,14 @@ _do_lookup(soinfo *si, const char *name, unsigned *base) if(s != NULL) goto done; + /* Next, look for it in the preloads list */ + for(i = 0; preloads[i] != NULL; i++) { + lsi = preloads[i]; + s = _do_lookup_in_so(lsi, name, &elf_hash); + if(s != NULL) + goto done; + } + for(d = si->dynamic; *d; d += 2) { if(d[0] == DT_NEEDED){ lsi = (soinfo *)d[1]; @@ -1866,6 +1883,23 @@ static int link_image(soinfo *si, unsigned wr_offset) goto fail; } + /* if this is the main executable, then load all of the preloads now */ + if(si->flags & FLAG_EXE) { + int i; + memset(preloads, 0, sizeof(preloads)); + for(i = 0; ldpreload_names[i] != NULL; i++) { + soinfo *lsi = find_library(ldpreload_names[i]); + if(lsi == 0) { + strlcpy(tmp_err_buf, linker_get_error(), sizeof(tmp_err_buf)); + DL_ERR("%5d could not load needed library '%s' for '%s' (%s)", + pid, ldpreload_names[i], si->name, tmp_err_buf); + goto fail; + } + lsi->refcount++; + preloads[i] = lsi; + } + } + for(d = si->dynamic; *d; d += 2) { if(d[0] == DT_NEEDED){ DEBUG("%5d %s needs %s\n", pid, si->name, si->strtab + d[1]); @@ -1982,6 +2016,30 @@ static void parse_library_path(char *path, char *delim) } } +static void parse_preloads(char *path, char *delim) +{ + size_t len; + char *ldpreloads_bufp = ldpreloads_buf; + int i = 0; + + len = strlcpy(ldpreloads_buf, path, sizeof(ldpreloads_buf)); + + while (i < LDPRELOAD_MAX && (ldpreload_names[i] = strsep(&ldpreloads_bufp, delim))) { + if (*ldpreload_names[i] != '\0') { + ++i; + } + } + + /* Forget the last path if we had to truncate; this occurs if the 2nd to + * last char isn't '\0' (i.e. not originally a delim). */ + if (i > 0 && len >= sizeof(ldpreloads_buf) && + ldpreloads_buf[sizeof(ldpreloads_buf) - 2] != '\0') { + ldpreload_names[i - 1] = NULL; + } else { + ldpreload_names[i] = NULL; + } +} + int main(int argc, char **argv) { return 0; @@ -2001,6 +2059,7 @@ unsigned __linker_init(unsigned **elfdata) soinfo *si; struct link_map * map; char *ldpath_env = NULL; + char *ldpreload_env = NULL; /* Setup a temporary TLS area that is used to get a working * errno for system calls. @@ -2032,6 +2091,8 @@ unsigned __linker_init(unsigned **elfdata) debug_verbosity = atoi(((char*) vecs[0]) + 6); } else if(!strncmp((char*) vecs[0], "LD_LIBRARY_PATH=", 16)) { ldpath_env = (char*) vecs[0] + 16; + } else if(!strncmp((char*) vecs[0], "LD_PRELOAD=", 11)) { + ldpreload_env = (char*) vecs[0] + 11; } vecs++; } @@ -2095,6 +2156,10 @@ unsigned __linker_init(unsigned **elfdata) if (ldpath_env && getuid() == geteuid() && getgid() == getegid()) parse_library_path(ldpath_env, ":"); + if (ldpreload_env && getuid() == geteuid() && getgid() == getegid()) { + parse_preloads(ldpreload_env, " :"); + } + if(link_image(si, 0)) { char errmsg[] = "CANNOT LINK EXECUTABLE\n"; write(2, __linker_dl_err_buf, strlen(__linker_dl_err_buf)); -- 2.11.0