+#ifdef SHARED
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
static void error(const char *, ...);
-#ifdef SHARED
-
#define MAXP2(a,b) (-(-(a)&-(b)))
#define ALIGN(x,y) ((x)+(y)-1 & -(y))
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;
+ struct tls_module tls;
+ size_t tls_id;
size_t relro_start, relro_end;
void **new_dtv;
unsigned char *new_tls;
int __init_tp(void *);
void __init_libc(char **, char *);
+void *__copy_tls(unsigned char *);
+__attribute__((__visibility__("hidden")))
const char *__libc_get_version(void);
static struct builtin_tls {
static jmp_buf *rtld_fail;
static pthread_rwlock_t lock;
static struct debug debug;
+static struct tls_module *tls_tail;
static size_t tls_cnt, tls_offset, tls_align = MIN_TLS_ALIGN;
static size_t static_tls_cnt;
static pthread_mutex_t init_fini_lock = { ._m_type = PTHREAD_MUTEX_RECURSIVE };
struct debug *_dl_debug_addr = &debug;
+__attribute__((__visibility__("hidden")))
+void (*const __init_array_start)(void)=0, (*const __fini_array_start)(void)=0;
+
+__attribute__((__visibility__("hidden")))
+extern void (*const __init_array_end)(void), (*const __fini_array_end)(void);
+
+weak_alias(__init_array_start, __init_array_end);
+weak_alias(__fini_array_start, __fini_array_end);
+
static int dl_strcmp(const char *l, const char *r)
{
for (; *l==*r && *l; l++, r++);
break;
#ifdef TLS_ABOVE_TP
case REL_TPOFF:
- *reloc_addr = tls_val + def.dso->tls_offset + TPOFF_K + addend;
+ *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;
+ *reloc_addr = tls_val - def.dso->tls.offset + addend;
break;
case REL_TPOFF_NEG:
- *reloc_addr = def.dso->tls_offset - tls_val + addend;
+ *reloc_addr = def.dso->tls.offset - tls_val + addend;
break;
#endif
case REL_TLSDESC:
} else {
reloc_addr[0] = (size_t)__tlsdesc_static;
#ifdef TLS_ABOVE_TP
- reloc_addr[1] = tls_val + def.dso->tls_offset
+ reloc_addr[1] = tls_val + def.dso->tls.offset
+ TPOFF_K + addend;
#else
- reloc_addr[1] = tls_val - def.dso->tls_offset
+ reloc_addr[1] = tls_val - def.dso->tls.offset
+ addend;
#endif
}
static void *mmap_fixed(void *p, size_t n, int prot, int flags, int fd, off_t off)
{
- char *q = mmap(p, n, prot, flags, fd, off);
- if (q != MAP_FAILED || errno != EINVAL) return q;
+ static int no_map_fixed;
+ char *q;
+ if (!no_map_fixed) {
+ q = mmap(p, n, prot, flags|MAP_FIXED, fd, off);
+ if (!DL_NOMMU_SUPPORT || q != MAP_FAILED || errno != EINVAL)
+ return q;
+ no_map_fixed = 1;
+ }
/* Fallbacks for MAP_FIXED failure on NOMMU kernels. */
if (flags & MAP_ANONYMOUS) {
memset(p, 0, n);
dyn = ph->p_vaddr;
} else if (ph->p_type == PT_TLS) {
tls_image = ph->p_vaddr;
- dso->tls_align = ph->p_align;
- dso->tls_len = ph->p_filesz;
- dso->tls_size = ph->p_memsz;
+ dso->tls.align = ph->p_align;
+ dso->tls.len = ph->p_filesz;
+ dso->tls.size = ph->p_memsz;
} else if (ph->p_type == PT_GNU_RELRO) {
dso->relro_start = ph->p_vaddr & -PAGE_SIZE;
dso->relro_end = (ph->p_vaddr + ph->p_memsz) & -PAGE_SIZE;
((ph->p_flags&PF_W) ? PROT_WRITE: 0) |
((ph->p_flags&PF_X) ? PROT_EXEC : 0));
map = mmap(0, ph->p_memsz + (ph->p_vaddr & PAGE_SIZE-1),
- prot, (prot&PROT_WRITE) ? MAP_PRIVATE : MAP_SHARED,
+ prot, MAP_PRIVATE,
fd, ph->p_offset & -PAGE_SIZE);
if (map == MAP_FAILED) {
unmap_library(dso);
dso->loadmap->segs[i].p_vaddr = ph->p_vaddr;
dso->loadmap->segs[i].p_memsz = ph->p_memsz;
i++;
+ if (prot & PROT_WRITE) {
+ size_t brk = (ph->p_vaddr & PAGE_SIZE-1)
+ + ph->p_filesz;
+ size_t pgbrk = brk + PAGE_SIZE-1 & -PAGE_SIZE;
+ size_t pgend = brk + ph->p_memsz - ph->p_filesz
+ + PAGE_SIZE-1 & -PAGE_SIZE;
+ if (pgend > pgbrk && mmap_fixed(map+pgbrk,
+ pgend-pgbrk, prot,
+ MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS,
+ -1, off_start) == MAP_FAILED)
+ goto error;
+ memset(map + brk, 0, pgbrk-brk);
+ }
}
map = (void *)dso->loadmap->segs[0].addr;
map_len = 0;
* the length of the file. This is okay because we will not
* use the invalid part; we just need to reserve the right
* amount of virtual address space to map over later. */
- map = mmap((void *)addr_min, map_len, prot, MAP_PRIVATE, fd, off_start);
+ map = DL_NOMMU_SUPPORT
+ ? mmap((void *)addr_min, map_len, PROT_READ|PROT_WRITE|PROT_EXEC,
+ MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
+ : mmap((void *)addr_min, map_len, prot,
+ MAP_PRIVATE, fd, off_start);
if (map==MAP_FAILED) goto error;
dso->map = map;
dso->map_len = map_len;
dso->phentsize = eh->e_phentsize;
}
/* Reuse the existing mapping for the lowest-address LOAD */
- if ((ph->p_vaddr & -PAGE_SIZE) == addr_min) continue;
+ if ((ph->p_vaddr & -PAGE_SIZE) == addr_min && !DL_NOMMU_SUPPORT)
+ continue;
this_min = ph->p_vaddr & -PAGE_SIZE;
this_max = ph->p_vaddr+ph->p_memsz+PAGE_SIZE-1 & -PAGE_SIZE;
off_start = ph->p_offset & -PAGE_SIZE;
done_mapping:
dso->base = base;
dso->dynv = laddr(dso, dyn);
- if (dso->tls_size) dso->tls_image = laddr(dso, tls_image);
+ if (dso->tls.size) dso->tls.image = laddr(dso, tls_image);
if (!runtime) reclaim_gaps(dso);
free(allocated_buf);
return map;
* extended DTV capable of storing an additional slot for
* the newly-loaded DSO. */
alloc_size = sizeof *p + strlen(pathname) + 1;
- if (runtime && temp_dso.tls_image) {
- size_t per_th = temp_dso.tls_size + temp_dso.tls_align
+ if (runtime && temp_dso.tls.image) {
+ size_t per_th = temp_dso.tls.size + temp_dso.tls.align
+ sizeof(void *) * (tls_cnt+3);
n_th = libc.threads_minus_1 + 1;
if (n_th > SSIZE_MAX / per_th) alloc_size = SIZE_MAX;
strcpy(p->name, pathname);
/* 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 (p->tls.image) {
p->tls_id = ++tls_cnt;
- tls_align = MAXP2(tls_align, p->tls_align);
+ tls_align = MAXP2(tls_align, p->tls.align);
#ifdef TLS_ABOVE_TP
- p->tls_offset = tls_offset + ( (tls_align-1) &
- -(tls_offset + (uintptr_t)p->tls_image) );
- tls_offset += p->tls_size;
+ p->tls.offset = tls_offset + ( (tls_align-1) &
+ -(tls_offset + (uintptr_t)p->tls.image) );
+ tls_offset += p->tls.size;
#else
- tls_offset += p->tls_size + p->tls_align - 1;
- tls_offset -= (tls_offset + (uintptr_t)p->tls_image)
- & (p->tls_align-1);
- p->tls_offset = tls_offset;
+ tls_offset += p->tls.size + p->tls.align - 1;
+ tls_offset -= (tls_offset + (uintptr_t)p->tls.image)
+ & (p->tls.align-1);
+ p->tls.offset = tls_offset;
#endif
p->new_dtv = (void *)(-sizeof(size_t) &
(uintptr_t)(p->name+strlen(p->name)+sizeof(size_t)));
p->new_tls = (void *)(p->new_dtv + n_th*(tls_cnt+1));
+ if (tls_tail) tls_tail->next = &p->tls;
+ else libc.tls_head = &p->tls;
+ tls_tail = &p->tls;
}
tail->next = p;
p->kernel_mapped = 1;
}
-static void do_fini()
+void __libc_exit_fini()
{
struct dso *p;
size_t dyn[DYN_CNT];
weak_alias(dl_debug_state, _dl_debug_state);
-void __reset_tls()
-{
- pthread_t self = __pthread_self();
- struct dso *p;
- for (p=head; p; p=p->next) {
- if (!p->tls_id || !self->dtv[p->tls_id]) continue;
- memcpy(self->dtv[p->tls_id], p->tls_image, p->tls_len);
- memset((char *)self->dtv[p->tls_id]+p->tls_len, 0,
- p->tls_size - p->tls_len);
- if (p->tls_id == (size_t)self->dtv[0]) break;
- }
-}
-
-void *__copy_tls(unsigned char *mem)
+void __init_tls(size_t *auxv)
{
- pthread_t td;
- struct dso *p;
- void **dtv;
-
-#ifdef TLS_ABOVE_TP
- 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);
-
- for (p=head; p; p=p->next) {
- if (!p->tls_id) continue;
- dtv[p->tls_id] = mem + p->tls_offset;
- 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;
-
- for (p=head; p; p=p->next) {
- if (!p->tls_id) continue;
- dtv[p->tls_id] = mem - p->tls_offset;
- 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")))
/* Get new DTV space from new DSO if needed */
if (v[0] > (size_t)self->dtv[0]) {
void **newdtv = p->new_dtv +
- (v[0]+1)*sizeof(void *)*a_fetch_add(&p->new_dtv_idx,1);
+ (v[0]+1)*a_fetch_add(&p->new_dtv_idx,1);
memcpy(newdtv, self->dtv,
((size_t)self->dtv[0]+1) * sizeof(void *));
newdtv[0] = (void *)v[0];
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)
+ 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);
+ 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);
+ memcpy(mem, p->tls.image, p->tls.len);
if (p->tls_id == v[0]) break;
}
__restore_sigs(&set);
static void update_tls_size()
{
+ libc.tls_cnt = tls_cnt;
+ libc.tls_align = tls_align;
libc.tls_size = ALIGN(
(1+tls_cnt) * sizeof(void *) +
tls_offset +
* use during dynamic linking. If possible it will also serve as the
* thread pointer at runtime. */
libc.tls_size = sizeof builtin_tls;
+ libc.tls_align = tls_align;
if (__init_tp(__copy_tls((void *)builtin_tls)) < 0) {
a_crash();
}
interp_off = (size_t)phdr->p_vaddr;
else if (phdr->p_type == PT_TLS) {
tls_image = phdr->p_vaddr;
- app.tls_len = phdr->p_filesz;
- app.tls_size = phdr->p_memsz;
- app.tls_align = phdr->p_align;
+ app.tls.len = phdr->p_filesz;
+ app.tls.size = phdr->p_memsz;
+ app.tls.align = phdr->p_align;
}
}
if (DL_FDPIC) app.loadmap = app_loadmap;
- if (app.tls_size) app.tls_image = laddr(&app, tls_image);
+ if (app.tls.size) app.tls.image = laddr(&app, tls_image);
if (interp_off) ldso.name = laddr(&app, interp_off);
if ((aux[0] & (1UL<<AT_EXECFN))
&& strncmp((char *)aux[AT_EXECFN], "/proc/", 6))
}
argv[-1] = (void *)(argc - (argv-argv_orig));
if (!argv[0]) {
- dprintf(2, "musl libc\n"
+ dprintf(2, "musl libc (" LDSO_ARCH ")\n"
"Version %s\n"
"Dynamic Program Loader\n"
"Usage: %s [options] [--] pathname%s\n",
dprintf(1, "\t%s (%p)\n", ldso.name, ldso.base);
}
}
- if (app.tls_size) {
+ if (app.tls.size) {
+ libc.tls_head = &app.tls;
app.tls_id = tls_cnt = 1;
#ifdef TLS_ABOVE_TP
- app.tls_offset = 0;
- tls_offset = app.tls_size
- + ( -((uintptr_t)app.tls_image + app.tls_size)
- & (app.tls_align-1) );
+ app.tls.offset = 0;
+ tls_offset = app.tls.size
+ + ( -((uintptr_t)app.tls.image + app.tls.size)
+ & (app.tls.align-1) );
#else
- tls_offset = app.tls_offset = app.tls_size
- + ( -((uintptr_t)app.tls_image + app.tls_size)
- & (app.tls_align-1) );
+ tls_offset = app.tls.offset = app.tls.size
+ + ( -((uintptr_t)app.tls.image + app.tls.size)
+ & (app.tls.align-1) );
#endif
- tls_align = MAXP2(tls_align, app.tls_align);
+ tls_align = MAXP2(tls_align, app.tls.align);
}
app.global = 1;
decode_dyn(&app);
debug.state = 0;
_dl_debug_state();
- __init_libc(envp, argv[0]);
- atexit(do_fini);
errno = 0;
CRTJMP((void *)aux[AT_ENTRY], argv-1);
void *dlopen(const char *file, int mode)
{
struct dso *volatile p, *orig_tail, *next;
+ struct tls_module *orig_tls_tail;
size_t orig_tls_cnt, orig_tls_offset, orig_tls_align;
size_t i;
int cs;
__inhibit_ptc();
p = 0;
+ orig_tls_tail = tls_tail;
orig_tls_cnt = tls_cnt;
orig_tls_offset = tls_offset;
orig_tls_align = tls_align;
unmap_library(p);
free(p);
}
+ if (!orig_tls_tail) libc.tls_head = 0;
+ tls_tail = orig_tls_tail;
tls_cnt = orig_tls_cnt;
tls_offset = orig_tls_offset;
tls_align = orig_tls_align;
return p;
}
-static int invalid_dso_handle(void *h)
+__attribute__((__visibility__("hidden")))
+int __dl_invalid_handle(void *h)
{
struct dso *p;
for (p=head; p; p=p->next) if (h==p) return 0;
static void *addr2dso(size_t a)
{
struct dso *p;
+ size_t i;
+ if (DL_FDPIC) for (p=head; p; p=p->next) {
+ i = count_syms(p);
+ if (a-(size_t)p->funcdescs < i*sizeof(*p->funcdescs))
+ return p;
+ }
for (p=head; p; p=p->next) {
if (DL_FDPIC && p->loadmap) {
- size_t i;
for (i=0; i<p->loadmap->nsegs; i++) {
if (a-p->loadmap->segs[i].p_vaddr
< p->loadmap->segs[i].p_memsz)
return p;
}
- i = count_syms(p);
- if (a-(size_t)p->funcdescs < i*sizeof(*p->funcdescs))
- return p;
} else {
if (a-(size_t)p->map < p->map_len)
return p;
return def.dso->funcdescs + (def.sym - def.dso->syms);
return laddr(def.dso, def.sym->st_value);
}
- if (invalid_dso_handle(p))
+ if (__dl_invalid_handle(p))
return 0;
if ((ght = p->ghashtab)) {
gh = gnu_hash(s);
return 0;
}
-int __dladdr(const void *addr, Dl_info *info)
+int dladdr(const void *addr, Dl_info *info)
{
struct dso *p;
- Sym *sym;
+ Sym *sym, *bestsym;
uint32_t nsym;
char *strings;
void *best = 0;
- char *bestname;
pthread_rwlock_rdlock(&lock);
p = addr2dso((size_t)addr);
strings = p->strings;
nsym = count_syms(p);
- for (; nsym; nsym--, sym++) {
+ if (DL_FDPIC) {
+ size_t idx = ((size_t)addr-(size_t)p->funcdescs)
+ / sizeof(*p->funcdescs);
+ if (idx < nsym && (sym[idx].st_info&0xf) == STT_FUNC) {
+ best = p->funcdescs + idx;
+ bestsym = sym + idx;
+ }
+ }
+
+ if (!best) for (; nsym; nsym--, sym++) {
if (sym->st_value
&& (1<<(sym->st_info&0xf) & OK_TYPES)
&& (1<<(sym->st_info>>4) & OK_BINDS)) {
if (symaddr > addr || symaddr < best)
continue;
best = symaddr;
- bestname = strings + sym->st_name;
+ bestsym = sym;
if (addr == symaddr)
break;
}
if (!best) return 0;
+ if (DL_FDPIC && (bestsym->st_info&0xf) == STT_FUNC)
+ best = p->funcdescs + (bestsym - p->syms);
+
info->dli_fname = p->name;
info->dli_fbase = p->base;
- info->dli_sname = bestname;
+ info->dli_sname = strings + bestsym->st_name;
info->dli_saddr = best;
return 1;
info.dlpi_adds = gencnt;
info.dlpi_subs = 0;
info.dlpi_tls_modid = current->tls_id;
- info.dlpi_tls_data = current->tls_image;
+ info.dlpi_tls_data = current->tls.image;
ret = (callback)(&info, sizeof (info), data);
}
return ret;
}
-#else
-static int invalid_dso_handle(void *h)
-{
- error("Invalid library handle %p", (void *)h);
- return 1;
-}
-void *dlopen(const char *file, int mode)
-{
- error("Dynamic loading not supported");
- return 0;
-}
-void *__dlsym(void *restrict p, const char *restrict s, void *restrict ra)
-{
- error("Symbol not found: %s", s);
- return 0;
-}
-int __dladdr (const void *addr, Dl_info *info)
-{
- return 0;
-}
-#endif
-
-int __dlinfo(void *dso, int req, void *res)
-{
- if (invalid_dso_handle(dso)) return -1;
- if (req != RTLD_DI_LINKMAP) {
- error("Unsupported request %d", req);
- return -1;
- }
- *(struct link_map **)res = dso;
- return 0;
-}
-char *dlerror()
-{
- 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);
-}
+__attribute__((__visibility__("hidden")))
+void __dl_vseterr(const char *, va_list);
static void error(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
-#ifdef SHARED
if (!runtime) {
vdprintf(2, fmt, ap);
dprintf(2, "\n");
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);
+ __dl_vseterr(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;
}
+#endif