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;
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) {
def.dso = dso;
}
- int gotplt = (type == REL_GOT || type == REL_PLT);
- if (dso->rel_update_got && !gotplt && stride==2) 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;
}
}
+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;
+ /* Fallbacks for MAP_FIXED failure on NOMMU kernels. */
+ if (flags & MAP_ANONYMOUS) {
+ memset(p, 0, n);
+ return p;
+ }
+ ssize_t r;
+ if (lseek(fd, off, SEEK_SET) < 0) return MAP_FAILED;
+ for (q=p; n; q+=r, off+=r, n-=r) {
+ r = read(fd, q, n);
+ if (r < 0 && errno != EINTR) return MAP_FAILED;
+ if (!r) {
+ memset(q, 0, n);
+ break;
+ }
+ }
+ return p;
+}
+
static void *map_library(int fd, struct dso *dso)
{
Ehdr buf[(896+sizeof(Ehdr))/sizeof(Ehdr)];
prot = (((ph->p_flags&PF_R) ? PROT_READ : 0) |
((ph->p_flags&PF_W) ? PROT_WRITE: 0) |
((ph->p_flags&PF_X) ? PROT_EXEC : 0));
- if (mmap(base+this_min, this_max-this_min, prot, MAP_PRIVATE|MAP_FIXED, fd, off_start) == MAP_FAILED)
+ if (mmap_fixed(base+this_min, this_max-this_min, prot, MAP_PRIVATE|MAP_FIXED, fd, off_start) == MAP_FAILED)
goto error;
if (ph->p_memsz > ph->p_filesz) {
size_t brk = (size_t)base+ph->p_vaddr+ph->p_filesz;
size_t pgbrk = brk+PAGE_SIZE-1 & -PAGE_SIZE;
memset((void *)brk, 0, pgbrk-brk & PAGE_SIZE-1);
- if (pgbrk-(size_t)base < this_max && mmap((void *)pgbrk, (size_t)base+this_max-pgbrk, prot, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) == MAP_FAILED)
+ if (pgbrk-(size_t)base < this_max && mmap_fixed((void *)pgbrk, (size_t)base+this_max-pgbrk, prot, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) == MAP_FAILED)
goto error;
}
}
for (i=0; ((size_t *)(base+dyn))[i]; i+=2)
if (((size_t *)(base+dyn))[i]==DT_TEXTREL) {
- if (mprotect(map, map_len, PROT_READ|PROT_WRITE|PROT_EXEC) < 0)
+ if (mprotect(map, map_len, PROT_READ|PROT_WRITE|PROT_EXEC)
+ && errno != ENOSYS)
goto error;
break;
}
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;
do_relocs(p, (void *)(p->base+dyn[DT_RELA]), dyn[DT_RELASZ], 3);
if (head != &ldso && p->relro_start != p->relro_end &&
- mprotect(p->base+p->relro_start, p->relro_end-p->relro_start, PROT_READ) < 0) {
+ mprotect(p->base+p->relro_start, p->relro_end-p->relro_start, PROT_READ)
+ && errno != ENOSYS) {
error("Error relocating %s: RELRO protection failed: %m",
p->name);
if (runtime) longjmp(*rtld_fail, 1);
* 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
char **argv_orig = argv;
char **envp = argv+argc+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. */
a_crash();
}
- /* 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_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