#include <elf.h>
#include <sys/mman.h>
#include <limits.h>
-#include <stdint.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
-#include <limits.h>
-#include <elf.h>
#include <link.h>
#include <setjmp.h>
#include <pthread.h>
Sym *syms;
uint32_t *hashtab;
uint32_t *ghashtab;
+ int16_t *versym;
char *strings;
unsigned char *map;
size_t map_len;
signed char global;
char relocated;
char constructed;
- struct dso **deps;
+ char kernel_mapped;
+ struct dso **deps, *needed_by;
+ char *rpath_orig, *rpath;
void *tls_image;
size_t tls_len, tls_size, tls_align, tls_id, tls_offset;
void **new_dtv;
void *__install_initial_tls(void *);
void __init_libc(char **, char *);
+const char *__libc_get_version(void);
+
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 int ssp_used;
static int runtime;
uint32_t *hashtab = dso->hashtab;
char *strings = dso->strings;
for (i=hashtab[2+h%hashtab[0]]; i; i=hashtab[2+hashtab[0]+i]) {
- if (!strcmp(s, strings+syms[i].st_name))
+ if ((!dso->versym || dso->versym[i] >= 0)
+ && (!strcmp(s, strings+syms[i].st_name)))
return syms+i;
}
return 0;
static Sym *gnu_lookup(const char *s, uint32_t h1, struct dso *dso)
{
- Sym *sym;
- char *strings;
+ Sym *syms = dso->syms;
+ char *strings = dso->strings;
uint32_t *hashtab = dso->ghashtab;
uint32_t nbuckets = hashtab[0];
uint32_t *buckets = hashtab + 4 + hashtab[2]*(sizeof(size_t)/4);
uint32_t h2;
uint32_t *hashval;
- uint32_t n = buckets[h1 % nbuckets];
+ uint32_t i = buckets[h1 % nbuckets];
- if (!n) return 0;
+ if (!i) return 0;
- strings = dso->strings;
- sym = dso->syms + n;
- hashval = buckets + nbuckets + (n - hashtab[1]);
+ hashval = buckets + nbuckets + (i - hashtab[1]);
- for (h1 |= 1; ; sym++) {
+ for (h1 |= 1; ; i++) {
h2 = *hashval++;
- if ((h1 == (h2|1)) && !strcmp(s, strings + sym->st_name))
- return sym;
+ if ((!dso->versym || dso->versym[i] >= 0)
+ && (h1 == (h2|1)) && !strcmp(s, strings + syms[i].st_name))
+ return syms+i;
if (h2 & 1) break;
}
name = strings + sym->st_name;
ctx = IS_COPY(type) ? head->next : head;
def = find_sym(ctx, name, IS_PLT(type));
- if (!def.sym && sym->st_info>>4 != STB_WEAK) {
+ if (!def.sym && (sym->st_shndx != SHN_UNDEF
+ || sym->st_info>>4 != STB_WEAK)) {
snprintf(errbuf, sizeof errbuf,
"Error relocating %s: %s: symbol not found",
dso->name, name);
}
}
+static int fixup_rpath(struct dso *p, char *buf, size_t buf_size)
+{
+ size_t n, l;
+ const char *s, *t, *origin;
+ char *d;
+ if (p->rpath) return 0;
+ if (!p->rpath_orig) return -1;
+ if (!strchr(p->rpath_orig, '$')) {
+ p->rpath = p->rpath_orig;
+ return 0;
+ }
+ n = 0;
+ s = p->rpath_orig;
+ while ((t=strchr(s, '$'))) {
+ if (strncmp(t, "$ORIGIN", 7) && strncmp(t, "${ORIGIN}", 9))
+ return -1;
+ s = t+1;
+ n++;
+ }
+ if (n > SSIZE_MAX/PATH_MAX) return -1;
+
+ if (p->kernel_mapped) {
+ /* $ORIGIN searches cannot be performed for the main program
+ * when it is suid/sgid/AT_SECURE. This is because the
+ * pathname is under the control of the caller of execve.
+ * For libraries, however, $ORIGIN can be processed safely
+ * since the library's pathname came from a trusted source
+ * (either system paths or a call to dlopen). */
+ if (libc.secure)
+ return -1;
+ l = readlink("/proc/self/exe", buf, buf_size);
+ if (l >= buf_size)
+ return -1;
+ buf[l] = 0;
+ origin = buf;
+ } else {
+ origin = p->name;
+ }
+ t = strrchr(origin, '/');
+ l = t ? t-origin : 0;
+ p->rpath = malloc(strlen(p->rpath_orig) + n*l + 1);
+ if (!p->rpath) return -1;
+
+ d = p->rpath;
+ s = p->rpath_orig;
+ while ((t=strchr(s, '$'))) {
+ memcpy(d, s, t-s);
+ d += t-s;
+ memcpy(d, origin, l);
+ d += l;
+ /* It was determined previously that the '$' is followed
+ * either by "ORIGIN" or "{ORIGIN}". */
+ s = t + 7 + 2*(t[1]=='{');
+ }
+ strcpy(d, s);
+ return 0;
+}
+
static void decode_dyn(struct dso *p)
{
size_t dyn[DYN_CNT] = {0};
p->strings = (void *)(p->base + dyn[DT_STRTAB]);
if (dyn[0]&(1<<DT_HASH))
p->hashtab = (void *)(p->base + dyn[DT_HASH]);
+ if (dyn[0]&(1<<DT_RPATH))
+ p->rpath_orig = (void *)(p->strings + dyn[DT_RPATH]);
if (search_vec(p->dynv, dyn, DT_GNU_HASH))
p->ghashtab = (void *)(p->base + *dyn);
+ if (search_vec(p->dynv, dyn, DT_VERSYM))
+ 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];
const char *pathname;
if (strlen(name) > NAME_MAX) return 0;
fd = -1;
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 (!fixup_rpath(p, buf, sizeof buf))
+ fd = path_open(name, p->rpath, buf, sizeof buf);
if (fd < 0) {
if (!sys_path) {
char *prefix = 0;
sys_path = "";
}
fclose(f);
+ } else if (errno != ENOENT) {
+ sys_path = "";
}
}
if (!sys_path) sys_path = "/lib:/usr/local/lib:/usr/lib";
p->dev = st.st_dev;
p->ino = st.st_ino;
p->refcnt = 1;
+ p->needed_by = needed_by;
p->name = p->buf;
strcpy(p->name, pathname);
/* Add a shortname only if name arg was not an explicit pathname. */
size_t i, ndeps=0;
struct dso ***deps = &p->deps, **tmp, *dep;
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) {
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) {
snprintf(errbuf, sizeof errbuf,
"Error loading shared library %s: %m (needed by %s)",
*deps = tmp;
}
}
- r_path = 0;
}
}
for (z=s; *z && !isspace(*z); z++);
tmp = *z;
*z = 0;
- load_library(s);
+ load_library(s, 0);
*z = tmp;
}
}
|| aux[AT_GID]!=aux[AT_EGID] || aux[AT_SECURE]) {
env_path = 0;
env_preload = 0;
+ libc.secure = 1;
}
+ libc.page_size = aux[AT_PAGESZ];
/* If the dynamic linker was invoked as a program itself, AT_BASE
* will not be set. In that case, we assume the base address is
lib->base = (void *)aux[AT_BASE];
lib->name = lib->shortname = "libc.so";
lib->global = 1;
+ lib->kernel_mapped = 1;
ehdr = (void *)lib->base;
lib->phnum = ehdr->e_phnum;
lib->phdr = (void *)(aux[AT_BASE]+ehdr->e_phoff);
}
if (app->tls_size) app->tls_image = (char *)app->base + tls_image;
if (interp_off) lib->name = (char *)app->base + interp_off;
- app->name = argv[0];
+ if ((aux[0] & (1UL<<AT_EXECFN))
+ && strncmp((char *)aux[AT_EXECFN], "/proc/", 6))
+ app->name = (char *)aux[AT_EXECFN];
+ else
+ app->name = argv[0];
+ app->kernel_mapped = 1;
app->dynv = (void *)(app->base + find_dyn(
(void *)aux[AT_PHDR], aux[AT_PHNUM], aux[AT_PHENT]));
find_map_range((void *)aux[AT_PHDR],
*argv++ = (void *)-1;
if (argv[0] && !strcmp(argv[0], "--")) *argv++ = (void *)-1;
if (!argv[0]) {
- dprintf(2, "musl libc/dynamic program loader\n");
- dprintf(2, "usage: %s pathname%s\n", ldname,
+ dprintf(2, "musl libc\n"
+ "Version %s\n"
+ "Dynamic Program Loader\n"
+ "Usage: %s [--] pathname%s\n",
+ __libc_get_version(), ldname,
ldd_mode ? "" : " [args]");
_exit(1);
}
p = 0;
errflag = 1;
goto end;
- } else p = load_library(file);
+ } else p = load_library(file, head);
if (!p) {
snprintf(errbuf, sizeof errbuf, noload ?
return 0;
}
-int __dladdr(void *addr, Dl_info *info)
+int __dladdr(const void *addr, Dl_info *info)
{
struct dso *p;
Sym *sym;
uint32_t *hashval;
buckets = p->ghashtab + 4 + (p->ghashtab[2]*sizeof(size_t)/4);
sym += p->ghashtab[1];
- for (i = 0; i < p->ghashtab[0]; i++) {
+ for (i = nsym = 0; i < p->ghashtab[0]; i++) {
if (buckets[i] > nsym)
nsym = buckets[i];
}
{
return 0;
}
-int __dladdr (void *addr, Dl_info *info)
+int __dladdr (const void *addr, Dl_info *info)
{
return 0;
}