#include <elf.h>
#include <setjmp.h>
#include <pthread.h>
+#include <ctype.h>
#include <dlfcn.h>
#include "reloc.h"
};
static struct dso *head, *tail, *libc;
-static char *env_path, *sys_path;
+static char *env_path, *sys_path, *r_path;
static int rtld_used;
static int runtime;
static jmp_buf rtld_fail;
static pthread_rwlock_t lock;
-#define AUX_CNT 15
+#define AUX_CNT 24
#define DYN_CNT 34
static void decode_vec(size_t *v, size_t *a, size_t cnt)
}
#define OK_TYPES (1<<STT_NOTYPE | 1<<STT_OBJECT | 1<<STT_FUNC | 1<<STT_COMMON)
+#define OK_BINDS (1<<STB_GLOBAL | 1<<STB_WEAK)
static void *find_sym(struct dso *dso, const char *s, int need_def)
{
uint32_t h = hash(s);
+ void *def = 0;
if (h==0x6b366be && !strcmp(s, "dlopen")) rtld_used = 1;
if (h==0x6b3afd && !strcmp(s, "dlsym")) rtld_used = 1;
for (; dso; dso=dso->next) {
if (!dso->global) continue;
sym = lookup(s, h, dso->syms, dso->hashtab, dso->strings);
if (sym && (!need_def || sym->st_shndx) && sym->st_value
- && (1<<(sym->st_info&0xf) & OK_TYPES))
- return dso->base + sym->st_value;
+ && (1<<(sym->st_info&0xf) & OK_TYPES)
+ && (1<<(sym->st_info>>4) & OK_BINDS)) {
+ if (def && sym->st_info>>4 == STB_WEAK) continue;
+ def = dso->base + sym->st_value;
+ if (sym->st_info>>4 == STB_GLOBAL) break;
+ }
}
- return 0;
+ return def;
}
static void do_relocs(unsigned char *base, size_t *rel, size_t rel_size, size_t stride, Sym *syms, char *strings, struct dso *dso)
}
}
}
+ for (i=0; ((size_t *)(base+dyn))[i]; i+=2)
+ if (((size_t *)(base+dyn))[i]==DT_TEXTREL) {
+ mprotect(map, map_len, PROT_READ|PROT_WRITE|PROT_EXEC);
+ break;
+ }
if (!runtime) reclaim_gaps(base, (void *)((char *)buf + eh->e_phoff),
eh->e_phentsize, eh->e_phnum);
*lenp = map_len;
if (!libc->prev) {
tail->next = libc;
libc->prev = tail;
- tail = libc;
+ tail = libc->next ? libc->next : libc;
}
return libc;
}
} else {
if (strlen(name) > NAME_MAX) return 0;
fd = -1;
- if (env_path) fd = path_open(name, env_path);
+ if (r_path) fd = path_open(name, r_path);
+ if (fd < 0 && env_path) fd = path_open(name, env_path);
if (fd < 0) {
if (!sys_path) {
FILE *f = fopen(ETC_LDSO_PATH, "r");
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]);
*deps = tmp;
}
}
+ r_path = 0;
+ }
+}
+
+static void load_preload(char *s)
+{
+ int tmp;
+ char *z;
+ for (z=s; *z; s=z) {
+ for ( ; *s && isspace(*s); s++);
+ for (z=s; *z && !isspace(*z); z++);
+ tmp = *z;
+ *z = 0;
+ load_library(s);
+ *z = tmp;
}
}
size_t *auxv, aux[AUX_CNT] = {0};
size_t app_dyn[DYN_CNT] = {0};
size_t lib_dyn[DYN_CNT] = {0};
+ size_t vdso_dyn[DYN_CNT] = {0};
size_t i;
Phdr *phdr;
Ehdr *ehdr;
- struct dso lib, app;
+ static struct dso builtin_dsos[3];
+ struct dso *const app = builtin_dsos+0;
+ struct dso *const lib = builtin_dsos+1;
+ struct dso *const vdso = builtin_dsos+2;
+ size_t vdso_base=0;
+ char *env_preload=0;
/* Find aux vector just past environ[] */
for (i=argc+1; argv[i]; i++)
if (!memcmp(argv[i], "LD_LIBRARY_PATH=", 16))
env_path = argv[i]+16;
+ else if (!memcmp(argv[i], "LD_PRELOAD=", 11))
+ env_preload = argv[i]+11;
auxv = (void *)(argv+i+1);
decode_vec(auxv, aux, AUX_CNT);
/* Only trust user/env if kernel says we're not suid/sgid */
if ((aux[0]&0x7800)!=0x7800 || aux[AT_UID]!=aux[AT_EUID]
- || aux[AT_GID]!=aux[AT_EGID]) {
+ || aux[AT_GID]!=aux[AT_EGID] || aux[AT_SECURE]) {
env_path = 0;
+ env_preload = 0;
+ }
+
+ for (i=0; auxv[i]; i+=2) {
+ if (auxv[i]==AT_SYSINFO_EHDR) {
+ vdso_base = auxv[i+1];
+ break;
+ }
}
/* Relocate ldso's DYNAMIC pointer and load vector */
}
}
- app = (struct dso){
+ *app = (struct dso){
.base = 0,
.strings = (void *)(app_dyn[DT_STRTAB]),
.hashtab = (void *)(app_dyn[DT_HASH]),
.dynv = (void *)(phdr->p_vaddr),
.name = argv[0],
.global = 1,
- .next = &lib
+ .next = lib
};
- lib = (struct dso){
+ *lib = (struct dso){
.base = (void *)aux[AT_BASE],
.strings = (void *)(aux[AT_BASE]+lib_dyn[DT_STRTAB]),
.hashtab = (void *)(aux[AT_BASE]+lib_dyn[DT_HASH]),
.relocated = 1
};
+ if (vdso_base) {
+ ehdr = (void *)vdso_base;
+ phdr = (void *)(vdso_base + ehdr->e_phoff);
+ for (i=ehdr->e_phnum; i; i--, phdr=(void *)((char *)phdr + ehdr->e_phentsize)) {
+ if (phdr->p_type == PT_DYNAMIC)
+ vdso->dynv = (void *)(vdso_base + phdr->p_offset);
+ if (phdr->p_type == PT_LOAD)
+ vdso->base = (void *)(vdso_base - phdr->p_vaddr + phdr->p_offset);
+ }
+ decode_vec(vdso->dynv, vdso_dyn, DYN_CNT);
+ vdso->syms = (void *)(vdso->base + vdso_dyn[DT_SYMTAB]);
+ vdso->hashtab = (void *)(vdso->base + vdso_dyn[DT_HASH]);
+ vdso->strings = (void *)(vdso->base + vdso_dyn[DT_STRTAB]);
+ vdso->name = "linux-gate.so.1";
+ vdso->global = 1;
+
+ vdso->prev = lib;
+ lib->next = vdso;
+ }
+
/* Relocate the dynamic linker/libc */
do_relocs((void *)aux[AT_BASE], (void *)(aux[AT_BASE]+lib_dyn[DT_REL]),
- lib_dyn[DT_RELSZ], 2, lib.syms, lib.strings, &app);
+ lib_dyn[DT_RELSZ], 2, lib->syms, lib->strings, app);
do_relocs((void *)aux[AT_BASE], (void *)(aux[AT_BASE]+lib_dyn[DT_RELA]),
- lib_dyn[DT_RELASZ], 3, lib.syms, lib.strings, &app);
+ lib_dyn[DT_RELASZ], 3, lib->syms, lib->strings, app);
/* At this point the standard library is fully functional */
- reclaim_gaps(app.base, (void *)aux[AT_PHDR], aux[AT_PHENT], aux[AT_PHNUM]);
- ehdr = (void *)lib.base;
- reclaim_gaps(lib.base, (void *)(lib.base+ehdr->e_phoff),
+ reclaim_gaps(app->base, (void *)aux[AT_PHDR], aux[AT_PHENT], aux[AT_PHNUM]);
+ ehdr = (void *)lib->base;
+ reclaim_gaps(lib->base, (void *)(lib->base+ehdr->e_phoff),
ehdr->e_phentsize, ehdr->e_phnum);
- head = tail = &app;
- libc = &lib;
- app.next = 0;
+ head = tail = app;
+ libc = lib;
+ app->next = 0;
+ if (env_preload) load_preload(env_preload);
load_deps(head);
make_global(head);
reloc_all(head->next);
reloc_all(head);
- if (rtld_used) {
- runtime = 1;
- head->next->prev = malloc(sizeof *head);
- *head->next->prev = *head;
- head = head->next->prev;
- libc->prev->next = malloc(sizeof *libc);
- *libc->prev->next = *libc;
- libc = libc->prev->next;
- if (libc->next) libc->next->prev = libc;
- if (tail == &lib) tail = libc;
- } else {
+ runtime = 1;
+ if (!rtld_used) {
free_all(head);
free(sys_path);
+ reclaim((void *)builtin_dsos, 0, sizeof builtin_dsos);
}
errno = 0;
void *dlopen(const char *file, int mode)
{
- struct dso *p, *orig_tail = tail, *next;
+ struct dso *volatile p, *orig_tail = tail, *next;
size_t i;
if (!file) return head;
return p;
}
-static void *do_dlsym(struct dso *p, const char *s)
+static void *do_dlsym(struct dso *p, const char *s, void *ra)
{
size_t i;
uint32_t h;
Sym *sym;
+ if (p == RTLD_NEXT) {
+ for (p=head; p && (unsigned char *)ra-p->map>p->map_len; p=p->next);
+ if (!p) p=head;
+ p=p->next;
+ }
if (p == head || p == RTLD_DEFAULT)
return find_sym(head, s, 0);
h = hash(s);
return 0;
}
-void *dlsym(void *p, const char *s)
+void *__dlsym(void *p, const char *s, void *ra)
{
void *res;
pthread_rwlock_rdlock(&lock);
- res = do_dlsym(p, s);
+ res = do_dlsym(p, s, ra);
pthread_rwlock_unlock(&lock);
return res;
}