#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
+#include <stdarg.h>
+#include <stddef.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
void *base;
};
+struct td_index {
+ size_t args[2];
+ struct td_index *next;
+};
+
struct dso {
unsigned char *base;
char *name;
void **new_dtv;
unsigned char *new_tls;
int new_dtv_idx, new_tls_idx;
+ struct td_index *td_index;
struct dso *fini_next;
char *shortname;
char buf[];
struct dso *dso;
};
+enum {
+ REL_ERR,
+ REL_SYMBOLIC,
+ REL_GOT,
+ REL_PLT,
+ REL_RELATIVE,
+ REL_OFFSET,
+ REL_OFFSET32,
+ REL_COPY,
+ REL_SYM_OR_REL,
+ REL_TLS, /* everything past here is TLS */
+ REL_DTPMOD,
+ REL_DTPOFF,
+ REL_TPOFF,
+ REL_TPOFF_NEG,
+ REL_TLSDESC,
+};
+
#include "reloc.h"
-void __init_ssp(size_t *);
int __init_tp(void *);
void __init_libc(char **, char *);
static struct dso *head, *tail, *ldso, *fini_head;
static char *env_path, *sys_path;
static unsigned long long gencnt;
-static int ssp_used;
static int runtime;
static int ldd_mode;
static int ldso_fail;
static pthread_rwlock_t lock;
static struct debug debug;
static size_t tls_cnt, tls_offset, tls_align = 4*sizeof(size_t);
+static size_t static_tls_cnt;
static pthread_mutex_t init_fini_lock = { ._m_type = PTHREAD_MUTEX_RECURSIVE };
static long long builtin_tls[(sizeof(struct pthread) + 64)/sizeof(long long)];
return 1;
}
+static void error(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(errbuf, sizeof errbuf, fmt, ap);
+ va_end(ap);
+ if (runtime) longjmp(*rtld_fail, 1);
+ dprintf(2, "%s\n", errbuf);
+ ldso_fail = 1;
+}
+
static uint32_t sysv_hash(const char *s0)
{
const unsigned char *s = (void *)s0;
#define OK_TYPES (1<<STT_NOTYPE | 1<<STT_OBJECT | 1<<STT_FUNC | 1<<STT_COMMON | 1<<STT_TLS)
#define OK_BINDS (1<<STB_GLOBAL | 1<<STB_WEAK | 1<<STB_GNU_UNIQUE)
+#ifndef ARCH_SYM_REJECT_UND
+#define ARCH_SYM_REJECT_UND(s) 0
+#endif
+
static struct symdef find_sym(struct dso *dso, const char *s, int need_def)
{
uint32_t h = 0, gh = 0;
struct symdef def = {0};
- if (dso->ghashtab) {
- gh = gnu_hash(s);
- if (gh == 0x1f4039c9 && !strcmp(s, "__stack_chk_fail")) ssp_used = 1;
- } else {
- h = sysv_hash(s);
- if (h == 0x595a4cc && !strcmp(s, "__stack_chk_fail")) ssp_used = 1;
- }
for (; dso; dso=dso->next) {
Sym *sym;
if (!dso->global) continue;
}
if (!sym) continue;
if (!sym->st_shndx)
- if (need_def || (sym->st_info&0xf) == STT_TLS)
+ if (need_def || (sym->st_info&0xf) == STT_TLS
+ || ARCH_SYM_REJECT_UND(sym))
continue;
if (!sym->st_value)
if ((sym->st_info&0xf) != STT_TLS)
return def;
}
+#define NO_INLINE_ADDEND (1<<REL_COPY | 1<<REL_GOT | 1<<REL_PLT)
+
+ptrdiff_t __tlsdesc_static(), __tlsdesc_dynamic();
+
static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stride)
{
unsigned char *base = dso->base;
Sym *sym;
const char *name;
void *ctx;
- int type;
+ int astype, type;
int sym_index;
struct symdef def;
+ size_t *reloc_addr;
+ size_t sym_val;
+ size_t tls_val;
+ size_t addend;
for (; rel_size; rel+=stride, rel_size-=stride*sizeof(size_t)) {
- type = R_TYPE(rel[1]);
+ astype = R_TYPE(rel[1]);
+ if (!astype) continue;
+ type = remap_rel(astype);
+ if (!type) {
+ error("Error relocating %s: unsupported relocation type %d",
+ dso->name, astype);
+ continue;
+ }
sym_index = R_SYM(rel[1]);
+ reloc_addr = (void *)(base + rel[0]);
if (sym_index) {
sym = syms + sym_index;
name = strings + sym->st_name;
- ctx = IS_COPY(type) ? head->next : head;
- def = find_sym(ctx, name, IS_PLT(type));
+ ctx = type==REL_COPY ? head->next : head;
+ def = find_sym(ctx, name, type==REL_PLT);
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",
+ error("Error relocating %s: %s: symbol not found",
dso->name, name);
- if (runtime) longjmp(*rtld_fail, 1);
- dprintf(2, "%s\n", errbuf);
- ldso_fail = 1;
continue;
}
} else {
sym = 0;
def.sym = 0;
- def.dso = 0;
+ def.dso = dso;
+ }
+
+ addend = stride>2 ? rel[2]
+ : (1<<type & NO_INLINE_ADDEND) ? 0
+ : *reloc_addr;
+
+ sym_val = def.sym ? (size_t)def.dso->base+def.sym->st_value : 0;
+ tls_val = def.sym ? def.sym->st_value : 0;
+
+ switch(type) {
+ case REL_OFFSET:
+ addend -= (size_t)reloc_addr;
+ case REL_SYMBOLIC:
+ case REL_GOT:
+ case REL_PLT:
+ *reloc_addr = sym_val + addend;
+ break;
+ case REL_RELATIVE:
+ *reloc_addr = (size_t)base + addend;
+ break;
+ case REL_SYM_OR_REL:
+ if (sym) *reloc_addr = sym_val + addend;
+ else *reloc_addr = (size_t)base + addend;
+ break;
+ case REL_COPY:
+ memcpy(reloc_addr, (void *)sym_val, sym->st_size);
+ break;
+ case REL_OFFSET32:
+ *(uint32_t *)reloc_addr = sym_val + addend
+ - (size_t)reloc_addr;
+ break;
+ case REL_DTPMOD:
+ *reloc_addr = def.dso->tls_id;
+ break;
+ case REL_DTPOFF:
+ *reloc_addr = tls_val + addend;
+ break;
+#ifdef TLS_ABOVE_TP
+ case REL_TPOFF:
+ *reloc_addr = tls_val + def.dso->tls_offset + TPOFF_K + addend;
+ break;
+#else
+ case REL_TPOFF:
+ *reloc_addr = tls_val - def.dso->tls_offset + addend;
+ break;
+ case REL_TPOFF_NEG:
+ *reloc_addr = def.dso->tls_offset - tls_val + addend;
+ break;
+#endif
+ case REL_TLSDESC:
+ if (stride<3) addend = reloc_addr[1];
+ if (runtime && def.dso->tls_id >= static_tls_cnt) {
+ struct td_index *new = malloc(sizeof *new);
+ if (!new) error(
+ "Error relocating %s: cannot allocate TLSDESC for %s",
+ dso->name, sym ? name : "(local)" );
+ new->next = dso->td_index;
+ dso->td_index = new;
+ new->args[0] = def.dso->tls_id;
+ new->args[1] = tls_val + addend;
+ reloc_addr[0] = (size_t)__tlsdesc_dynamic;
+ reloc_addr[1] = (size_t)new;
+ } else {
+ reloc_addr[0] = (size_t)__tlsdesc_static;
+#ifdef TLS_ABOVE_TP
+ reloc_addr[1] = tls_val + def.dso->tls_offset
+ + TPOFF_K + addend;
+#else
+ reloc_addr[1] = tls_val - def.dso->tls_offset
+ + addend;
+#endif
+ }
+ break;
}
- do_single_reloc(dso, base, (void *)(base + rel[0]), type,
- stride>2 ? rel[2] : 0, sym, sym?sym->st_size:0, def,
- def.sym?(size_t)(def.dso->base+def.sym->st_value):0);
}
}
if (p->dynv[i] != DT_NEEDED) continue;
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)",
+ error("Error loading shared library %s: %m (needed by %s)",
p->strings + p->dynv[i+1], p->name);
- if (runtime) longjmp(*rtld_fail, 1);
- dprintf(2, "%s\n", errbuf);
- ldso_fail = 1;
continue;
}
if (runtime) {
if (p->relro_start != p->relro_end &&
mprotect(p->base+p->relro_start, p->relro_end-p->relro_start, PROT_READ) < 0) {
- snprintf(errbuf, sizeof errbuf,
- "Error relocating %s: RELRO protection failed",
+ error("Error relocating %s: RELRO protection failed: %m",
p->name);
- if (runtime) longjmp(*rtld_fail, 1);
- dprintf(2, "%s\n", errbuf);
- ldso_fail = 1;
}
p->relocated = 1;
return td;
}
-void *__tls_get_addr(size_t *v)
+void *__tls_get_new(size_t *v)
{
pthread_t self = __pthread_self();
- if (v[0]<=(size_t)self->dtv[0] && self->dtv[v[0]])
- return (char *)self->dtv[v[0]]+v[1];
/* Block signals to make accessing new TLS async-signal-safe */
sigset_t set;
- pthread_sigmask(SIG_BLOCK, SIGALL_SET, &set);
- if (v[0]<=(size_t)self->dtv[0] && self->dtv[v[0]]) {
- pthread_sigmask(SIG_SETMASK, &set, 0);
+ __block_all_sigs(&set);
+ if (v[0]<=(size_t)self->dtv[0]) {
+ __restore_sigs(&set);
return (char *)self->dtv[v[0]]+v[1];
}
self->dtv = newdtv;
}
- /* Get new TLS memory from new DSO */
- unsigned char *mem = p->new_tls +
- (p->tls_size + p->tls_align) * a_fetch_add(&p->new_tls_idx,1);
- mem += ((uintptr_t)p->tls_image - (uintptr_t)mem) & (p->tls_align-1);
- self->dtv[v[0]] = mem;
- memcpy(mem, p->tls_image, p->tls_len);
- pthread_sigmask(SIG_SETMASK, &set, 0);
+ /* Get new TLS memory from all new DSOs up to the requested one */
+ unsigned char *mem;
+ for (p=head; ; p=p->next) {
+ if (!p->tls_id || self->dtv[p->tls_id]) continue;
+ mem = p->new_tls + (p->tls_size + p->tls_align)
+ * a_fetch_add(&p->new_tls_idx,1);
+ mem += ((uintptr_t)p->tls_image - (uintptr_t)mem)
+ & (p->tls_align-1);
+ self->dtv[p->tls_id] = mem;
+ memcpy(mem, p->tls_image, p->tls_len);
+ if (p->tls_id == v[0]) break;
+ }
+ __restore_sigs(&set);
return mem + v[1];
}
size_t l = strlen(ldname);
if (l >= 3 && !strcmp(ldname+l-3, "ldd")) ldd_mode = 1;
*argv++ = (void *)-1;
- if (argv[0] && !strcmp(argv[0], "--")) *argv++ = (void *)-1;
+ while (argv[0] && argv[0][0]=='-' && argv[0][1]=='-') {
+ char *opt = argv[0]+2;
+ *argv++ = (void *)-1;
+ if (!*opt) {
+ break;
+ } else if (!memcmp(opt, "list", 5)) {
+ ldd_mode = 1;
+ } else if (!memcmp(opt, "library-path", 12)) {
+ if (opt[12]=='=') env_path = opt+13;
+ else if (opt[12]) *argv = 0;
+ else if (*argv) env_path = *argv++;
+ } else if (!memcmp(opt, "preload", 7)) {
+ if (opt[7]=='=') env_preload = opt+8;
+ else if (opt[7]) *argv = 0;
+ else if (*argv) env_preload = *argv++;
+ } else {
+ argv[0] = 0;
+ }
+ argv[-1] = (void *)-1;
+ }
if (!argv[0]) {
dprintf(2, "musl libc\n"
"Version %s\n"
"Dynamic Program Loader\n"
- "Usage: %s [--] pathname%s\n",
+ "Usage: %s [options] [--] pathname%s\n",
__libc_get_version(), ldname,
ldd_mode ? "" : " [args]");
_exit(1);
dprintf(2, "%s: Thread-local storage not supported by kernel.\n", argv[0]);
_exit(127);
}
+ static_tls_cnt = tls_cnt;
if (ldso_fail) _exit(127);
if (ldd_mode) _exit(0);
debug.state = 0;
_dl_debug_state();
- if (ssp_used) __init_ssp((void *)aux[AT_RANDOM]);
__init_libc(envp, argv[0]);
atexit(do_fini);
errno = 0;
for (p=orig_tail->next; p; p=next) {
next = p->next;
munmap(p->map, p->map_len);
+ while (p->td_index) {
+ void *tmp = p->td_index->next;
+ free(p->td_index);
+ p->td_index = tmp;
+ }
free(p->deps);
free(p);
}
}
update_tls_size();
-
- if (ssp_used) __init_ssp(libc.auxv);
-
_dl_debug_state();
orig_tail = tail;
end:
return 1;
}
+void *__tls_get_addr(size_t *);
+
static void *do_dlsym(struct dso *p, const char *s, void *ra)
{
size_t i;