#include "libc.h"
#include "dynlink.h"
-static int errflag;
-static char errbuf[128];
+static void error(const char *, ...);
#ifdef SHARED
volatile int new_dtv_idx, new_tls_idx;
struct td_index *td_index;
struct dso *fini_next;
- int rel_early_relative, rel_update_got;
char *shortname;
char buf[];
};
} builtin_tls[1];
#define MIN_TLS_ALIGN offsetof(struct builtin_tls, pt)
+#define ADDEND_LIMIT 4096
+static size_t *saved_addends, *apply_addends_to;
+
static struct dso ldso;
static struct dso *head, *tail, *fini_head;
static char *env_path, *sys_path;
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;
return def;
}
+__attribute__((__visibility__("hidden")))
ptrdiff_t __tlsdesc_static(), __tlsdesc_dynamic();
static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stride)
size_t sym_val;
size_t tls_val;
size_t addend;
+ int skip_relative = 0, reuse_addends = 0, save_slot = 0;
+
+ if (dso == &ldso) {
+ /* Only ldso's REL table needs addend saving/reuse. */
+ if (rel == apply_addends_to)
+ reuse_addends = 1;
+ skip_relative = 1;
+ }
for (; rel_size; rel+=stride, rel_size-=stride*sizeof(size_t)) {
- if (dso->rel_early_relative && IS_RELATIVE(rel[1])) continue;
+ if (skip_relative && IS_RELATIVE(rel[1])) continue;
type = R_TYPE(rel[1]);
+ if (type == REL_NONE) continue;
sym_index = R_SYM(rel[1]);
reloc_addr = (void *)(base + rel[0]);
if (sym_index) {
|| sym->st_info>>4 != STB_WEAK)) {
error("Error relocating %s: %s: symbol not found",
dso->name, name);
+ if (runtime) longjmp(*rtld_fail, 1);
continue;
}
} else {
def.dso = dso;
}
- int gotplt = (type == REL_GOT || type == REL_PLT);
- if (dso->rel_update_got && !gotplt) continue;
-
- addend = stride>2 ? rel[2]
- : gotplt || type==REL_COPY ? 0
- : *reloc_addr;
+ if (stride > 2) {
+ addend = rel[2];
+ } else if (type==REL_GOT || type==REL_PLT|| type==REL_COPY) {
+ addend = 0;
+ } else if (reuse_addends) {
+ /* Save original addend in stage 2 where the dso
+ * chain consists of just ldso; otherwise read back
+ * saved addend since the inline one was clobbered. */
+ if (head==&ldso)
+ saved_addends[save_slot] = *reloc_addr;
+ addend = saved_addends[save_slot++];
+ } else {
+ addend = *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;
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(
+ if (!new) {
+ error(
"Error relocating %s: cannot allocate TLSDESC for %s",
dso->name, sym ? name : "(local)" );
+ longjmp(*rtld_fail, 1);
+ }
new->next = dso->td_index;
dso->td_index = new;
new->args[0] = def.dso->tls_id;
default:
error("Error relocating %s: unsupported relocation type %d",
dso->name, type);
+ if (runtime) longjmp(*rtld_fail, 1);
continue;
}
}
static void decode_dyn(struct dso *p)
{
- size_t dyn[DYN_CNT] = {0};
+ size_t dyn[DYN_CNT];
decode_vec(p->dynv, dyn, DYN_CNT);
p->syms = (void *)(p->base + dyn[DT_SYMTAB]);
p->strings = (void *)(p->base + dyn[DT_STRTAB]);
/* Add a shortname only if name arg was not an explicit pathname. */
if (pathname != name) p->shortname = strrchr(p->name, '/')+1;
if (p->tls_image) {
- if (runtime && !libc.has_thread_pointer) {
- munmap(map, p->map_len);
- free(p);
- errno = ENOSYS;
- return 0;
- }
p->tls_id = ++tls_cnt;
tls_align = MAXP2(tls_align, p->tls_align);
#ifdef TLS_ABOVE_TP
if (!dep) {
error("Error loading shared library %s: %m (needed by %s)",
p->strings + p->dynv[i+1], p->name);
+ if (runtime) longjmp(*rtld_fail, 1);
continue;
}
if (runtime) {
size_t i, j, rel[2];
unsigned char *base = p->base;
i=0; search_vec(p->dynv, &i, DT_MIPS_LOCAL_GOTNO);
- if (p->rel_early_relative) {
+ if (p==&ldso) {
got += i;
} else {
while (i--) *got++ += (size_t)base;
static void reloc_all(struct dso *p)
{
- size_t dyn[DYN_CNT] = {0};
+ size_t dyn[DYN_CNT];
for (; p; p=p->next) {
if (p->relocated) continue;
decode_vec(p->dynv, dyn, DYN_CNT);
mprotect(p->base+p->relro_start, p->relro_end-p->relro_start, PROT_READ) < 0) {
error("Error relocating %s: RELRO protection failed: %m",
p->name);
+ if (runtime) longjmp(*rtld_fail, 1);
}
p->relocated = 1;
static void do_fini()
{
struct dso *p;
- size_t dyn[DYN_CNT] = {0};
+ size_t dyn[DYN_CNT];
for (p=fini_head; p; p=p->fini_next) {
if (!p->constructed) continue;
decode_vec(p->dynv, dyn, DYN_CNT);
static void do_init_fini(struct dso *p)
{
- size_t dyn[DYN_CNT] = {0};
+ size_t dyn[DYN_CNT];
int need_locking = libc.threads_minus_1;
/* Allow recursive calls that arise when a library calls
* dlopen from one of its constructors, but block any
if (need_locking) pthread_mutex_unlock(&init_fini_lock);
}
-void _dl_debug_state(void)
+static void dl_debug_state(void)
{
}
+weak_alias(dl_debug_state, _dl_debug_state);
+
void __reset_tls()
{
pthread_t self = __pthread_self();
{
pthread_t td;
struct dso *p;
-
- void **dtv = (void *)mem;
- dtv[0] = (void *)tls_cnt;
- if (!tls_cnt) {
- td = (void *)(dtv+1);
- td->dtv = td->dtv_copy = dtv;
- return td;
- }
+ void **dtv;
#ifdef TLS_ABOVE_TP
- mem += sizeof(void *) * (tls_cnt+1);
+ dtv = (void **)(mem + libc.tls_size) - (tls_cnt + 1);
+
mem += -((uintptr_t)mem + sizeof(struct pthread)) & (tls_align-1);
td = (pthread_t)mem;
mem += sizeof(struct pthread);
memcpy(dtv[p->tls_id], p->tls_image, p->tls_len);
}
#else
+ dtv = (void **)mem;
+
mem += libc.tls_size - sizeof(struct pthread);
mem -= (uintptr_t)mem & (tls_align-1);
td = (pthread_t)mem;
memcpy(dtv[p->tls_id], p->tls_image, p->tls_len);
}
#endif
+ dtv[0] = (void *)tls_cnt;
td->dtv = td->dtv_copy = dtv;
return td;
}
+__attribute__((__visibility__("hidden")))
void *__tls_get_new(size_t *v)
{
pthread_t self = __pthread_self();
* linker itself, but some of the relocations performed may need to be
* replaced later due to copy relocations in the main program. */
-void __dls2(unsigned char *base)
+void __dls2(unsigned char *base, size_t *sp)
{
Ehdr *ehdr = (void *)base;
ldso.base = base;
ldso.phnum = ehdr->e_phnum;
ldso.phdr = (void *)(base + ehdr->e_phoff);
ldso.phentsize = ehdr->e_phentsize;
- ldso.rel_early_relative = 1;
kernel_mapped_dso(&ldso);
decode_dyn(&ldso);
+ /* Prepare storage for to save clobbered REL addends so they
+ * can be reused in stage 3. There should be very few. If
+ * something goes wrong and there are a huge number, abort
+ * instead of risking stack overflow. */
+ size_t dyn[DYN_CNT];
+ decode_vec(ldso.dynv, dyn, DYN_CNT);
+ size_t *rel = (void *)(base+dyn[DT_REL]);
+ size_t rel_size = dyn[DT_RELSZ];
+ size_t symbolic_rel_cnt = 0;
+ apply_addends_to = rel;
+ for (; rel_size; rel+=2, rel_size-=2*sizeof(size_t))
+ if (!IS_RELATIVE(rel[1])) symbolic_rel_cnt++;
+ if (symbolic_rel_cnt >= ADDEND_LIMIT) a_crash();
+ size_t addends[symbolic_rel_cnt+1];
+ saved_addends = addends;
+
head = &ldso;
reloc_all(&ldso);
ldso.relocated = 0;
- ldso.rel_update_got = 1;
+
+ /* Call dynamic linker stage-3, __dls3, looking it up
+ * symbolically as a barrier against moving the address
+ * load across the above relocation processing. */
+ struct symdef dls3_def = find_sym(&ldso, "__dls3", 0);
+ ((stage3_func)(ldso.base+dls3_def.sym->st_value))(sp);
}
/* Stage 3 of the dynamic linker is called with the dynamic linker/libc
_Noreturn void __dls3(size_t *sp)
{
static struct dso app, vdso;
- size_t aux[AUX_CNT] = {0}, *auxv;
+ size_t aux[AUX_CNT], *auxv;
size_t i;
char *env_preload=0;
size_t vdso_base;
char **argv = (void *)(sp+1);
char **argv_orig = argv;
char **envp = argv+argc+1;
- void *initial_tls;
-
- /* 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);
+ /* Find aux vector just past environ[] and use it to initialize
+ * global data that may be needed before we can make syscalls. */
+ __environ = envp;
+ for (i=argc+1; argv[i]; i++);
+ libc.auxv = auxv = (void *)(argv+i+1);
decode_vec(auxv, aux, AUX_CNT);
+ __hwcap = aux[AT_HWCAP];
+ libc.page_size = aux[AT_PAGESZ];
+ libc.secure = ((aux[0]&0x7800)!=0x7800 || aux[AT_UID]!=aux[AT_EUID]
+ || aux[AT_GID]!=aux[AT_EGID] || aux[AT_SECURE]);
+
+ /* Setup early thread pointer in builtin_tls for ldso/libc itself to
+ * use during dynamic linking. If possible it will also serve as the
+ * thread pointer at runtime. */
+ libc.tls_size = sizeof builtin_tls;
+ if (__init_tp(__copy_tls((void *)builtin_tls)) < 0) {
+ a_crash();
+ }
/* 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_SECURE]) {
- env_path = 0;
- env_preload = 0;
- libc.secure = 1;
+ if (!libc.secure) {
+ env_path = getenv("LD_LIBRARY_PATH");
+ env_preload = getenv("LD_PRELOAD");
}
- libc.page_size = aux[AT_PAGESZ];
- libc.auxv = auxv;
/* If the main program was already loaded by the kernel,
* AT_PHDR will point to some location other than the dynamic
reloc_all(&app);
update_tls_size();
- if (libc.tls_size > sizeof builtin_tls) {
- initial_tls = calloc(libc.tls_size, 1);
+ if (libc.tls_size > sizeof builtin_tls || tls_align > MIN_TLS_ALIGN) {
+ void *initial_tls = calloc(libc.tls_size, 1);
if (!initial_tls) {
dprintf(2, "%s: Error getting %zu bytes thread-local storage: %m\n",
argv[0], libc.tls_size);
_exit(127);
}
+ if (__init_tp(__copy_tls(initial_tls)) < 0) {
+ a_crash();
+ }
} else {
- initial_tls = builtin_tls;
- }
- if (__init_tp(__copy_tls(initial_tls)) < 0 && tls_cnt) {
- dprintf(2, "%s: Thread-local storage not supported by kernel.\n", argv[0]);
- _exit(127);
+ size_t tmp_tls_size = libc.tls_size;
+ pthread_t self = __pthread_self();
+ /* Temporarily set the tls size to the full size of
+ * builtin_tls so that __copy_tls will use the same layout
+ * as it did for before. Then check, just to be safe. */
+ libc.tls_size = sizeof builtin_tls;
+ if (__copy_tls((void*)builtin_tls) != self) a_crash();
+ libc.tls_size = tmp_tls_size;
}
static_tls_cnt = tls_cnt;
runtime = 1;
debug.ver = 1;
- debug.bp = _dl_debug_state;
+ debug.bp = dl_debug_state;
debug.head = head;
debug.base = ldso.base;
debug.state = 0;
tail = orig_tail;
tail->next = 0;
p = 0;
- errflag = 1;
goto end;
} else p = load_library(file, head);
if (!p) {
- snprintf(errbuf, sizeof errbuf, noload ?
+ error(noload ?
"Library %s is not already loaded" :
"Error loading shared library %s: %m",
file);
- errflag = 1;
goto end;
}
{
struct dso *p;
for (p=head; p; p=p->next) if (h==p) return 0;
- snprintf(errbuf, sizeof errbuf, "Invalid library handle %p", (void *)h);
- errflag = 1;
+ error("Invalid library handle %p", (void *)h);
return 1;
}
return __tls_get_addr((size_t []){def.dso->tls_id, def.sym->st_value});
return def.dso->base + def.sym->st_value;
}
- if (p != RTLD_DEFAULT && p != RTLD_NEXT && invalid_dso_handle(p))
+ if (invalid_dso_handle(p))
return 0;
if (p->ghashtab) {
gh = gnu_hash(s);
return p->deps[i]->base + sym->st_value;
}
failed:
- errflag = 1;
- snprintf(errbuf, sizeof errbuf, "Symbol not found: %s", s);
+ error("Symbol not found: %s", s);
return 0;
}
return 1;
}
+__attribute__((__visibility__("hidden")))
void *__dlsym(void *restrict p, const char *restrict s, void *restrict ra)
{
void *res;
#else
static int invalid_dso_handle(void *h)
{
- snprintf(errbuf, sizeof errbuf, "Invalid library handle %p", (void *)h);
- errflag = 1;
+ error("Invalid library handle %p", (void *)h);
return 1;
}
void *dlopen(const char *file, int mode)
{
- strcpy(errbuf, "Dynamic loading not supported");
- errflag = 1;
+ error("Dynamic loading not supported");
return 0;
}
void *__dlsym(void *restrict p, const char *restrict s, void *restrict ra)
{
- errflag = 1;
- snprintf(errbuf, sizeof errbuf, "Symbol not found: %s", s);
+ error("Symbol not found: %s", s);
return 0;
}
int __dladdr (const void *addr, Dl_info *info)
{
if (invalid_dso_handle(dso)) return -1;
if (req != RTLD_DI_LINKMAP) {
- snprintf(errbuf, sizeof errbuf, "Unsupported request %d", req);
- errflag = 1;
+ error("Unsupported request %d", req);
return -1;
}
*(struct link_map **)res = dso;
char *dlerror()
{
- if (!errflag) return 0;
- errflag = 0;
- return errbuf;
+ pthread_t self = __pthread_self();
+ if (!self->dlerror_flag) return 0;
+ self->dlerror_flag = 0;
+ char *s = self->dlerror_buf;
+ if (s == (void *)-1)
+ return "Dynamic linker failed to allocate memory for error message";
+ else
+ return s;
}
int dlclose(void *p)
{
return invalid_dso_handle(p);
}
+
+void __dl_thread_cleanup(void)
+{
+ pthread_t self = __pthread_self();
+ if (self->dlerror_buf != (void *)-1)
+ free(self->dlerror_buf);
+}
+
+static void error(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+#ifdef SHARED
+ if (!runtime) {
+ vdprintf(2, fmt, ap);
+ dprintf(2, "\n");
+ ldso_fail = 1;
+ va_end(ap);
+ return;
+ }
+#endif
+ pthread_t self = __pthread_self();
+ if (self->dlerror_buf != (void *)-1)
+ free(self->dlerror_buf);
+ size_t len = vsnprintf(0, 0, fmt, ap);
+ va_end(ap);
+ char *buf = malloc(len+1);
+ if (buf) {
+ va_start(ap, fmt);
+ vsnprintf(buf, len+1, fmt, ap);
+ va_end(ap);
+ } else {
+ buf = (void *)-1;
+ }
+ self->dlerror_buf = buf;
+ self->dlerror_flag = 1;
+}