diff options
Diffstat (limited to 'ldso')
-rw-r--r-- | ldso/dlstart.c | 15 | ||||
-rw-r--r-- | ldso/dynlink.c | 113 |
2 files changed, 110 insertions, 18 deletions
diff --git a/ldso/dlstart.c b/ldso/dlstart.c index 20d50f2c..259f5e18 100644 --- a/ldso/dlstart.c +++ b/ldso/dlstart.c @@ -140,6 +140,21 @@ hidden void _dlstart_c(size_t *sp, size_t *dynv) size_t *rel_addr = (void *)(base + rel[0]); *rel_addr = base + rel[2]; } + + rel = (void *)(base+dyn[DT_RELR]); + rel_size = dyn[DT_RELRSZ]; + size_t *relr_addr = 0; + for (; rel_size; rel++, rel_size-=sizeof(size_t)) { + if ((rel[0]&1) == 0) { + relr_addr = (void *)(base + rel[0]); + *relr_addr++ += base; + } else { + for (size_t i=0, bitmap=rel[0]; bitmap>>=1; i++) + if (bitmap&1) + relr_addr[i] += base; + relr_addr += 8*sizeof(size_t)-1; + } + } #endif stage2_func dls2; diff --git a/ldso/dynlink.c b/ldso/dynlink.c index 5b9c8be4..3b57c07f 100644 --- a/ldso/dynlink.c +++ b/ldso/dynlink.c @@ -24,12 +24,22 @@ #include "libc.h" #include "dynlink.h" +static size_t ldso_page_size; +/* libc.h may have defined a macro for dynamic PAGE_SIZE already, but + * PAGESIZE is only defined if it's constant for the arch. */ +#ifndef PAGESIZE +#undef PAGE_SIZE +#define PAGE_SIZE ldso_page_size +#endif + #define malloc __libc_malloc #define calloc __libc_calloc #define realloc __libc_realloc #define free __libc_free -static void error(const char *, ...); +static void error_impl(const char *, ...); +static void error_noop(const char *, ...); +static void (*error)(const char *, ...) = error_noop; #define MAXP2(a,b) (-(-(a)&-(b))) #define ALIGN(x,y) ((x)+(y)-1 & -(y)) @@ -149,6 +159,8 @@ static struct fdpic_dummy_loadmap app_dummy_loadmap; struct debug *_dl_debug_addr = &debug; +extern weak hidden char __ehdr_start[]; + extern hidden int __malloc_replaced; hidden void (*const __init_array_start)(void)=0, (*const __fini_array_start)(void)=0; @@ -208,7 +220,8 @@ static void decode_vec(size_t *v, size_t *a, size_t cnt) size_t i; for (i=0; i<cnt; i++) a[i] = 0; for (; v[0]; v+=2) if (v[0]-1<cnt-1) { - a[0] |= 1UL<<v[0]; + if (v[0] < 8*sizeof(long)) + a[0] |= 1UL<<v[0]; a[v[0]] = v[1]; } } @@ -334,6 +347,35 @@ static struct symdef find_sym(struct dso *dso, const char *s, int need_def) return find_sym2(dso, s, need_def, 0); } +static struct symdef get_lfs64(const char *name) +{ + const char *p; + static const char lfs64_list[] = + "aio_cancel\0aio_error\0aio_fsync\0aio_read\0aio_return\0" + "aio_suspend\0aio_write\0alphasort\0creat\0fallocate\0" + "fgetpos\0fopen\0freopen\0fseeko\0fsetpos\0fstat\0" + "fstatat\0fstatfs\0fstatvfs\0ftello\0ftruncate\0ftw\0" + "getdents\0getrlimit\0glob\0globfree\0lio_listio\0" + "lockf\0lseek\0lstat\0mkostemp\0mkostemps\0mkstemp\0" + "mkstemps\0mmap\0nftw\0open\0openat\0posix_fadvise\0" + "posix_fallocate\0pread\0preadv\0prlimit\0pwrite\0" + "pwritev\0readdir\0scandir\0sendfile\0setrlimit\0" + "stat\0statfs\0statvfs\0tmpfile\0truncate\0versionsort\0" + "__fxstat\0__fxstatat\0__lxstat\0__xstat\0"; + if (!strcmp(name, "readdir64_r")) + return find_sym(&ldso, "readdir_r", 1); + size_t l = strnlen(name, 18); + if (l<2 || name[l-2]!='6' || name[l-1]!='4' || name[l]) + goto nomatch; + for (p=lfs64_list; *p; p++) { + if (!strncmp(name, p, l-2) && !p[l-2]) + return find_sym(&ldso, p, 1); + while (*p) p++; + } +nomatch: + return (struct symdef){ 0 }; +} + static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stride) { unsigned char *base = dso->base; @@ -387,6 +429,7 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri def = (sym->st_info>>4) == STB_LOCAL ? (struct symdef){ .dso = dso, .sym = sym } : find_sym(ctx, name, type==REL_PLT); + if (!def.sym) def = get_lfs64(name); if (!def.sym && (sym->st_shndx != SHN_UNDEF || sym->st_info>>4 != STB_WEAK)) { if (dso->lazy && (type==REL_PLT || type==REL_GOT)) { @@ -471,7 +514,7 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri break; #endif case REL_TLSDESC: - if (stride<3) addend = reloc_addr[1]; + if (stride<3) addend = reloc_addr[!TLSDESC_BACKWARDS]; if (def.dso->tls_id > static_tls_cnt) { struct td_index *new = malloc(sizeof *new); if (!new) { @@ -496,13 +539,13 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri + addend; #endif } -#ifdef TLSDESC_BACKWARDS /* Some archs (32-bit ARM at least) invert the order of * the descriptor members. Fix them up here. */ - size_t tmp = reloc_addr[0]; - reloc_addr[0] = reloc_addr[1]; - reloc_addr[1] = tmp; -#endif + if (TLSDESC_BACKWARDS) { + size_t tmp = reloc_addr[0]; + reloc_addr[0] = reloc_addr[1]; + reloc_addr[1] = tmp; + } break; default: error("Error relocating %s: unsupported relocation type %d", @@ -513,6 +556,24 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri } } +static void do_relr_relocs(struct dso *dso, size_t *relr, size_t relr_size) +{ + if (dso == &ldso) return; /* self-relocation was done in _dlstart */ + unsigned char *base = dso->base; + size_t *reloc_addr; + for (; relr_size; relr++, relr_size-=sizeof(size_t)) + if ((relr[0]&1) == 0) { + reloc_addr = laddr(dso, relr[0]); + *reloc_addr++ += (size_t)base; + } else { + int i = 0; + for (size_t bitmap=relr[0]; (bitmap>>=1); i++) + if (bitmap&1) + reloc_addr[i] += (size_t)base; + reloc_addr += 8*sizeof(size_t)-1; + } +} + static void redo_lazy_relocs() { struct dso *p = lazy_head, *next; @@ -866,7 +927,7 @@ static int fixup_rpath(struct dso *p, char *buf, size_t buf_size) case ENOENT: case ENOTDIR: case EACCES: - break; + return 0; default: return -1; } @@ -1355,13 +1416,17 @@ static void reloc_all(struct dso *p) 2+(dyn[DT_PLTREL]==DT_RELA)); do_relocs(p, laddr(p, dyn[DT_REL]), dyn[DT_RELSZ], 2); do_relocs(p, laddr(p, dyn[DT_RELA]), dyn[DT_RELASZ], 3); - - if (head != &ldso && p->relro_start != p->relro_end && - mprotect(laddr(p, 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); + if (!DL_FDPIC) + do_relr_relocs(p, laddr(p, dyn[DT_RELR]), dyn[DT_RELRSZ]); + + if (head != &ldso && p->relro_start != p->relro_end) { + long ret = __syscall(SYS_mprotect, laddr(p, p->relro_start), + p->relro_end-p->relro_start, PROT_READ); + if (ret != 0 && ret != -ENOSYS) { + error("Error relocating %s: RELRO protection failed: %m", + p->name); + if (runtime) longjmp(*rtld_fail, 1); + } } p->relocated = 1; @@ -1659,11 +1724,12 @@ hidden void __dls2(unsigned char *base, size_t *sp) } else { ldso.base = base; } - Ehdr *ehdr = (void *)ldso.base; + Ehdr *ehdr = __ehdr_start ? (void *)__ehdr_start : (void *)ldso.base; ldso.name = ldso.shortname = "libc.so"; ldso.phnum = ehdr->e_phnum; ldso.phdr = laddr(&ldso, ehdr->e_phoff); ldso.phentsize = ehdr->e_phentsize; + search_vec(auxv, &ldso_page_size, AT_PAGESZ); kernel_mapped_dso(&ldso); decode_dyn(&ldso); @@ -1756,6 +1822,9 @@ void __dls3(size_t *sp, size_t *auxv) env_preload = getenv("LD_PRELOAD"); } + /* Activate error handler function */ + error = error_impl; + /* If the main program was already loaded by the kernel, * AT_PHDR will point to some location other than the dynamic * linker's program headers. */ @@ -1923,6 +1992,10 @@ void __dls3(size_t *sp, size_t *auxv) size_t *ptr = (size_t *) app.dynv[i+1]; *ptr = (size_t)&debug; } + if (app.dynv[i]==DT_DEBUG_INDIRECT_REL) { + size_t *ptr = (size_t *)((size_t)&app.dynv[i] + app.dynv[i+1]); + *ptr = (size_t)&debug; + } } /* This must be done before final relocations, since it calls @@ -2345,7 +2418,7 @@ int dl_iterate_phdr(int(*callback)(struct dl_phdr_info *info, size_t size, void return ret; } -static void error(const char *fmt, ...) +static void error_impl(const char *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -2359,3 +2432,7 @@ static void error(const char *fmt, ...) __dl_vseterr(fmt, ap); va_end(ap); } + +static void error_noop(const char *fmt, ...) +{ +} |