1*ce716eebSriastradh /* $NetBSD: map_object.c,v 1.69 2024/08/03 21:59:57 riastradh Exp $ */ 241fe218bScgd 341fe218bScgd /* 441fe218bScgd * Copyright 1996 John D. Polstra. 541fe218bScgd * Copyright 1996 Matt Thomas <matt@3am-software.com> 6ad8ccd62Smycroft * Copyright 2002 Charles M. Hannum <root@ihack.net> 741fe218bScgd * All rights reserved. 841fe218bScgd * 941fe218bScgd * Redistribution and use in source and binary forms, with or without 1041fe218bScgd * modification, are permitted provided that the following conditions 1141fe218bScgd * are met: 1241fe218bScgd * 1. Redistributions of source code must retain the above copyright 1341fe218bScgd * notice, this list of conditions and the following disclaimer. 1441fe218bScgd * 2. Redistributions in binary form must reproduce the above copyright 1541fe218bScgd * notice, this list of conditions and the following disclaimer in the 1641fe218bScgd * documentation and/or other materials provided with the distribution. 1741fe218bScgd * 3. All advertising materials mentioning features or use of this software 1841fe218bScgd * must display the following acknowledgement: 1941fe218bScgd * This product includes software developed by John Polstra. 2041fe218bScgd * 4. The name of the author may not be used to endorse or promote products 2141fe218bScgd * derived from this software without specific prior written permission. 2241fe218bScgd * 2341fe218bScgd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2441fe218bScgd * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2541fe218bScgd * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2641fe218bScgd * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2741fe218bScgd * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2841fe218bScgd * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2941fe218bScgd * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3041fe218bScgd * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3141fe218bScgd * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3241fe218bScgd * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3341fe218bScgd */ 3441fe218bScgd 352728318eSskrll #include <sys/cdefs.h> 362728318eSskrll #ifndef lint 37*ce716eebSriastradh __RCSID("$NetBSD: map_object.c,v 1.69 2024/08/03 21:59:57 riastradh Exp $"); 382728318eSskrll #endif /* not lint */ 392728318eSskrll 4041fe218bScgd #include <errno.h> 4141fe218bScgd #include <stddef.h> 42305c9497Smycroft #include <stdlib.h> 4341fe218bScgd #include <string.h> 4441fe218bScgd #include <unistd.h> 45305c9497Smycroft #include <sys/stat.h> 4641fe218bScgd #include <sys/types.h> 4741fe218bScgd #include <sys/mman.h> 4841fe218bScgd 49e6cdac9cSskrll #include "debug.h" 5041fe218bScgd #include "rtld.h" 5141fe218bScgd 52acf7fb3aSchristos static int convert_prot(int); /* Elf flags -> mmap protection */ 53acf7fb3aSchristos static int convert_flags(int); /* Elf flags -> mmap flags */ 5441fe218bScgd 55e6cdac9cSskrll #define EA_UNDEF (~(Elf_Addr)0) 56e6cdac9cSskrll 5741fe218bScgd /* 5841fe218bScgd * Map a shared object into memory. The argument is a file descriptor, 5941fe218bScgd * which must be open on the object and positioned at its beginning. 6041fe218bScgd * 6141fe218bScgd * The return value is a pointer to a newly-allocated Obj_Entry structure 6241fe218bScgd * for the shared object. Returns NULL on failure. 6341fe218bScgd */ 6441fe218bScgd Obj_Entry * 650339fe66Schristos _rtld_map_object(const char *path, int fd, const struct stat *sb) 6641fe218bScgd { 6741fe218bScgd Obj_Entry *obj; 6893f2d902Sjunyoung Elf_Ehdr *ehdr; 6941fe218bScgd Elf_Phdr *phdr; 70aad59997Sjoerg #if defined(__HAVE_TLS_VARIANT_I) || defined(__HAVE_TLS_VARIANT_II) 71aad59997Sjoerg Elf_Phdr *phtls; 72aad59997Sjoerg #endif 7341fe218bScgd Elf_Phdr *phlimit; 74acf7fb3aSchristos Elf_Phdr **segs = NULL; 7541fe218bScgd int nsegs; 766f56cdc9Smycroft caddr_t mapbase = MAP_FAILED; 77a12d1749Slukem size_t mapsize = 0; 787b28c560Smatt int mapflags; 797b28c560Smatt Elf_Addr base_alignment; 8041fe218bScgd Elf_Addr base_vaddr; 8141fe218bScgd Elf_Addr base_vlimit; 825c5817eeSthorpej Elf_Addr text_vlimit; 83acf7fb3aSchristos Elf_Addr text_end; 8406c9aa11Sjoerg void *base_addr; 8541fe218bScgd Elf_Off data_offset; 8641fe218bScgd Elf_Addr data_vaddr; 8741fe218bScgd Elf_Addr data_vlimit; 886f56cdc9Smycroft int data_flags; 89acf7fb3aSchristos int data_prot; 9041fe218bScgd caddr_t data_addr; 91acf7fb3aSchristos Elf_Addr bss_vaddr; 92acf7fb3aSchristos Elf_Addr bss_vlimit; 93acf7fb3aSchristos caddr_t bss_addr; 94aad59997Sjoerg #if defined(__HAVE_TLS_VARIANT_I) || defined(__HAVE_TLS_VARIANT_II) 95aad59997Sjoerg Elf_Addr tls_vaddr = 0; /* Noise GCC */ 96aad59997Sjoerg #endif 97e6cdac9cSskrll Elf_Addr phdr_vaddr; 982f8ed368Schristos size_t phdr_memsz, phsize; 99e6cdac9cSskrll int i; 10041fe218bScgd #ifdef RTLD_LOADER 10141fe218bScgd Elf_Addr clear_vaddr; 102acf7fb3aSchristos caddr_t clear_page; 10341fe218bScgd caddr_t clear_addr; 1042f8ed368Schristos size_t nclear; 10541fe218bScgd #endif 1060e6265fcSchristos #ifdef GNU_RELRO 1070e6265fcSchristos Elf_Addr relro_page; 1080e6265fcSchristos size_t relro_size; 1090e6265fcSchristos #endif 110acf7fb3aSchristos #ifdef notyet 111acf7fb3aSchristos int stack_flags; 112acf7fb3aSchristos #endif 11341fe218bScgd 114fa64a5bfSchristos if (sb != NULL && sb->st_size < (off_t)sizeof (Elf_Ehdr)) { 1159d2c0068Sdholland _rtld_error("%s: not ELF file (too short)", path); 116c8ba6436Sfvdl return NULL; 117c8ba6436Sfvdl } 118c8ba6436Sfvdl 1196f56cdc9Smycroft obj = _rtld_obj_new(); 1200339fe66Schristos obj->path = xstrdup(path); 121fd1f5e8fSjunyoung obj->pathlen = strlen(path); 1226f56cdc9Smycroft if (sb != NULL) { 1236f56cdc9Smycroft obj->dev = sb->st_dev; 1246f56cdc9Smycroft obj->ino = sb->st_ino; 1256f56cdc9Smycroft } 1266f56cdc9Smycroft 1273c16ffb1Smycroft ehdr = mmap(NULL, _rtld_pagesz, PROT_READ, MAP_FILE | MAP_SHARED, fd, 12886103e2fSmycroft (off_t)0); 12953c5ea5dSad obj->ehdr = ehdr; 1303c16ffb1Smycroft if (ehdr == MAP_FAILED) { 13141fe218bScgd _rtld_error("%s: read error: %s", path, xstrerror(errno)); 132acf7fb3aSchristos goto error; 13341fe218bScgd } 13441fe218bScgd /* Make sure the file is valid */ 1359d2c0068Sdholland if (memcmp(ELFMAG, ehdr->e_ident, SELFMAG) != 0) { 1369d2c0068Sdholland _rtld_error("%s: not ELF file (magic number bad)", path); 137acf7fb3aSchristos goto error; 1389d2c0068Sdholland } 1399d2c0068Sdholland if (ehdr->e_ident[EI_CLASS] != ELFCLASS) { 1409d2c0068Sdholland _rtld_error("%s: invalid ELF class %x; expected %x", path, 14107666b63Sskrll ehdr->e_ident[EI_CLASS], ELFCLASS); 142acf7fb3aSchristos goto error; 14341fe218bScgd } 14441fe218bScgd /* Elf_e_ident includes class */ 14593f2d902Sjunyoung if (ehdr->e_ident[EI_VERSION] != EV_CURRENT || 14693f2d902Sjunyoung ehdr->e_version != EV_CURRENT || 14793f2d902Sjunyoung ehdr->e_ident[EI_DATA] != ELFDEFNNAME(MACHDEP_ENDIANNESS)) { 14886103e2fSmycroft _rtld_error("%s: unsupported file version", path); 149acf7fb3aSchristos goto error; 15041fe218bScgd } 15193f2d902Sjunyoung if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) { 15286103e2fSmycroft _rtld_error("%s: unsupported file type", path); 153acf7fb3aSchristos goto error; 15441fe218bScgd } 15593f2d902Sjunyoung switch (ehdr->e_machine) { 15641fe218bScgd ELFDEFNNAME(MACHDEP_ID_CASES) 15741fe218bScgd default: 15886103e2fSmycroft _rtld_error("%s: unsupported machine", path); 159acf7fb3aSchristos goto error; 16041fe218bScgd } 16141fe218bScgd 16241fe218bScgd /* 16341fe218bScgd * We rely on the program header being in the first page. This is 16441fe218bScgd * not strictly required by the ABI specification, but it seems to 16541fe218bScgd * always true in practice. And, it simplifies things considerably. 16641fe218bScgd */ 16793f2d902Sjunyoung assert(ehdr->e_phentsize == sizeof(Elf_Phdr)); 1684b4a707cSjunyoung assert(ehdr->e_phoff + ehdr->e_phnum * sizeof(Elf_Phdr) <= 1694b4a707cSjunyoung _rtld_pagesz); 17041fe218bScgd 17141fe218bScgd /* 17241fe218bScgd * Scan the program header entries, and save key information. 17341fe218bScgd * 17441fe218bScgd * We rely on there being exactly two load segments, text and data, 17541fe218bScgd * in that order. 17641fe218bScgd */ 17793f2d902Sjunyoung phdr = (Elf_Phdr *) ((caddr_t)ehdr + ehdr->e_phoff); 178aad59997Sjoerg #if defined(__HAVE_TLS_VARIANT_I) || defined(__HAVE_TLS_VARIANT_II) 179aad59997Sjoerg phtls = NULL; 180aad59997Sjoerg #endif 18155509064Schristos phsize = ehdr->e_phnum * sizeof(phdr[0]); 182e6cdac9cSskrll obj->phdr = NULL; 1830e6265fcSchristos #ifdef GNU_RELRO 1840e6265fcSchristos relro_page = 0; 1850e6265fcSchristos relro_size = 0; 1860e6265fcSchristos #endif 187e6cdac9cSskrll phdr_vaddr = EA_UNDEF; 18855509064Schristos phdr_memsz = 0; 18993f2d902Sjunyoung phlimit = phdr + ehdr->e_phnum; 190acf7fb3aSchristos segs = xmalloc(sizeof(segs[0]) * ehdr->e_phnum); 191acf7fb3aSchristos if (segs == NULL) { 192acf7fb3aSchristos _rtld_error("No memory for segs"); 193acf7fb3aSchristos goto error; 194acf7fb3aSchristos } 195acf7fb3aSchristos #ifdef notyet 196acf7fb3aSchristos stack_flags = PF_R | PF_W; 197acf7fb3aSchristos #endif 198acf7fb3aSchristos nsegs = -1; 19941fe218bScgd while (phdr < phlimit) { 20041fe218bScgd switch (phdr->p_type) { 201305c9497Smycroft case PT_INTERP: 202fb3cc85fSmrg obj->interp = (void *)(uintptr_t)phdr->p_vaddr; 203e6cdac9cSskrll dbg(("%s: PT_INTERP %p", obj->path, obj->interp)); 204305c9497Smycroft break; 20541fe218bScgd 206522cbf02Skleink case PT_LOAD: 207acf7fb3aSchristos segs[++nsegs] = phdr; 208acf7fb3aSchristos if ((segs[nsegs]->p_align & (_rtld_pagesz - 1)) != 0) { 209acf7fb3aSchristos _rtld_error( 210acf7fb3aSchristos "%s: PT_LOAD segment %d not page-aligned", 211acf7fb3aSchristos path, nsegs); 212acf7fb3aSchristos goto error; 213acf7fb3aSchristos } 214acf7fb3aSchristos if ((segs[nsegs]->p_flags & PF_X) == PF_X) { 215acf7fb3aSchristos text_end = MAX(text_end, 216acf7fb3aSchristos round_up(segs[nsegs]->p_vaddr + 217acf7fb3aSchristos segs[nsegs]->p_memsz)); 218acf7fb3aSchristos } 219972dd84dSmartin 220acf7fb3aSchristos dbg(("%s: %s %p phsize %" PRImemsz, obj->path, 221acf7fb3aSchristos "PT_LOAD", 222c5e4118aSchristos (void *)(uintptr_t)phdr->p_vaddr, phdr->p_memsz)); 223e6cdac9cSskrll break; 224e6cdac9cSskrll 225e6cdac9cSskrll case PT_PHDR: 226e6cdac9cSskrll phdr_vaddr = phdr->p_vaddr; 22755509064Schristos phdr_memsz = phdr->p_memsz; 228acf7fb3aSchristos dbg(("%s: %s %p phsize %" PRImemsz, obj->path, 229acf7fb3aSchristos "PT_PHDR", 230c5e4118aSchristos (void *)(uintptr_t)phdr->p_vaddr, phdr->p_memsz)); 23141fe218bScgd break; 23241fe218bScgd 233acf7fb3aSchristos #ifdef notyet 234acf7fb3aSchristos case PT_GNU_STACK: 235acf7fb3aSchristos stack_flags = phdr->p_flags; 236acf7fb3aSchristos break; 237acf7fb3aSchristos #endif 238acf7fb3aSchristos 2390e6265fcSchristos #ifdef GNU_RELRO 2400e6265fcSchristos case PT_GNU_RELRO: 2410e6265fcSchristos relro_page = phdr->p_vaddr; 2420e6265fcSchristos relro_size = phdr->p_memsz; 2430e6265fcSchristos break; 2440e6265fcSchristos #endif 2450e6265fcSchristos 246522cbf02Skleink case PT_DYNAMIC: 247fb3cc85fSmrg obj->dynamic = (void *)(uintptr_t)phdr->p_vaddr; 248acf7fb3aSchristos dbg(("%s: %s %p phsize %" PRImemsz, obj->path, 249acf7fb3aSchristos "PT_DYNAMIC", 250c5e4118aSchristos (void *)(uintptr_t)phdr->p_vaddr, phdr->p_memsz)); 25141fe218bScgd break; 252aad59997Sjoerg 253aad59997Sjoerg #if defined(__HAVE_TLS_VARIANT_I) || defined(__HAVE_TLS_VARIANT_II) 254aad59997Sjoerg case PT_TLS: 255aad59997Sjoerg phtls = phdr; 256972dd84dSmartin dbg(("%s: %s %p phsize %" PRImemsz, obj->path, "PT_TLS", 257c5e4118aSchristos (void *)(uintptr_t)phdr->p_vaddr, phdr->p_memsz)); 258aad59997Sjoerg break; 259aad59997Sjoerg #endif 260d4d42c7fSskrll #ifdef __ARM_EABI__ 261d4d42c7fSskrll case PT_ARM_EXIDX: 262d4d42c7fSskrll obj->exidx_start = (void *)(uintptr_t)phdr->p_vaddr; 263d4d42c7fSskrll obj->exidx_sz = phdr->p_memsz; 264d4d42c7fSskrll break; 265d4d42c7fSskrll #endif 26641fe218bScgd } 26741fe218bScgd 26841fe218bScgd ++phdr; 26941fe218bScgd } 270e6cdac9cSskrll phdr = (Elf_Phdr *) ((caddr_t)ehdr + ehdr->e_phoff); 271fb3cc85fSmrg obj->entry = (void *)(uintptr_t)ehdr->e_entry; 2726f56cdc9Smycroft if (!obj->dynamic) { 2736ec13b9dSmycroft _rtld_error("%s: not dynamically linked", path); 274acf7fb3aSchristos goto error; 2756ec13b9dSmycroft } 27641fe218bScgd 27741fe218bScgd /* 278871e0c15Schs * Map the entire address space of the object as a file 2795c5817eeSthorpej * region to stake out our contiguous region and establish a 280871e0c15Schs * base for relocation. We use a file mapping so that 281871e0c15Schs * the kernel will give us whatever alignment is appropriate 282871e0c15Schs * for the platform we're running on. 2835c5817eeSthorpej * 284871e0c15Schs * We map it using the text protection, map the data segment 285871e0c15Schs * into the right place, then map an anon segment for the bss 286871e0c15Schs * and unmap the gaps left by padding to alignment. 28741fe218bScgd */ 288871e0c15Schs 2897b28c560Smatt base_alignment = segs[0]->p_align; 29041fe218bScgd base_vaddr = round_down(segs[0]->p_vaddr); 291acf7fb3aSchristos base_vlimit = round_up(segs[nsegs]->p_vaddr + segs[nsegs]->p_memsz); 292871e0c15Schs text_vlimit = round_up(segs[0]->p_vaddr + segs[0]->p_memsz); 293acf7fb3aSchristos data_offset = round_down(segs[nsegs]->p_offset); 294acf7fb3aSchristos data_vaddr = round_down(segs[nsegs]->p_vaddr); 295acf7fb3aSchristos data_vlimit = round_up(segs[nsegs]->p_vaddr + segs[nsegs]->p_filesz); 296acf7fb3aSchristos data_flags = convert_prot(segs[nsegs]->p_flags); 2975d4bc1adSmycroft #ifdef RTLD_LOADER 298acf7fb3aSchristos clear_vaddr = segs[nsegs]->p_vaddr + segs[nsegs]->p_filesz; 2995d4bc1adSmycroft #endif 3006f56cdc9Smycroft 3016f56cdc9Smycroft obj->textsize = text_vlimit - base_vaddr; 3026f56cdc9Smycroft obj->vaddrbase = base_vaddr; 3036f56cdc9Smycroft obj->isdynamic = ehdr->e_type == ET_DYN; 3046f56cdc9Smycroft 305aad59997Sjoerg #if defined(__HAVE_TLS_VARIANT_I) || defined(__HAVE_TLS_VARIANT_II) 306aad59997Sjoerg if (phtls != NULL) { 307aad59997Sjoerg ++_rtld_tls_dtv_generation; 308aad59997Sjoerg obj->tlsindex = ++_rtld_tls_max_index; 309aad59997Sjoerg obj->tlssize = phtls->p_memsz; 310aad59997Sjoerg obj->tlsalign = phtls->p_align; 311aad59997Sjoerg obj->tlsinitsize = phtls->p_filesz; 312aad59997Sjoerg tls_vaddr = phtls->p_vaddr; 3133e105dc2Sriastradh dbg(("%s: tls index %zu size %zu align %zu initsize %zu", 3143e105dc2Sriastradh obj->path, obj->tlsindex, obj->tlssize, obj->tlsalign, 3153e105dc2Sriastradh obj->tlsinitsize)); 316aad59997Sjoerg } 317aad59997Sjoerg #endif 318aad59997Sjoerg 3197b28c560Smatt /* 3207b28c560Smatt * Calculate log2 of the base section alignment. 3217b28c560Smatt */ 322acf7fb3aSchristos mapflags = MAP_PRIVATE | MAP_ANON; 3237b28c560Smatt if (base_alignment > _rtld_pagesz) { 3247b28c560Smatt unsigned int log2 = 0; 3257b28c560Smatt for (; base_alignment > 1; base_alignment >>= 1) 3267b28c560Smatt log2++; 327acf7fb3aSchristos mapflags |= MAP_ALIGNED(log2); 3287b28c560Smatt } 3297b28c560Smatt 33041fe218bScgd base_addr = NULL; 33106c9aa11Sjoerg #ifdef RTLD_LOADER 33206c9aa11Sjoerg if (!obj->isdynamic) { 33306c9aa11Sjoerg mapflags |= MAP_TRYFIXED; 33406c9aa11Sjoerg base_addr = (void *)(uintptr_t)base_vaddr; 33506c9aa11Sjoerg } 33641fe218bScgd #endif 3376f56cdc9Smycroft mapsize = base_vlimit - base_vaddr; 338acf7fb3aSchristos mapbase = mmap(base_addr, mapsize, PROT_NONE, mapflags, -1, 0); 3395c5817eeSthorpej if (mapbase == MAP_FAILED) { 34026475619Schristos _rtld_error("mmap of entire address space failed: %s", 34126475619Schristos xstrerror(errno)); 342acf7fb3aSchristos goto error; 34341fe218bScgd } 34406c9aa11Sjoerg #ifdef RTLD_LOADER 34506c9aa11Sjoerg if (!obj->isdynamic && mapbase != base_addr) { 34606c9aa11Sjoerg _rtld_error("mmap of executable at correct address failed"); 347acf7fb3aSchristos goto error; 34806c9aa11Sjoerg } 34906c9aa11Sjoerg #endif 3505c5817eeSthorpej 35155509064Schristos obj->phdr_loaded = false; 352acf7fb3aSchristos for (i = 0; i <= nsegs; i++) { 353acf7fb3aSchristos /* Overlay the segment onto the proper region. */ 354acf7fb3aSchristos data_offset = round_down(segs[i]->p_offset); 355acf7fb3aSchristos data_vaddr = round_down(segs[i]->p_vaddr); 356acf7fb3aSchristos data_vlimit = round_up(segs[i]->p_vaddr 357acf7fb3aSchristos + segs[i]->p_filesz); 35841fe218bScgd data_addr = mapbase + (data_vaddr - base_vaddr); 359acf7fb3aSchristos data_prot = convert_prot(segs[i]->p_flags); 360acf7fb3aSchristos data_flags = convert_flags(segs[i]->p_flags) | MAP_FIXED; 361acf7fb3aSchristos if (data_vlimit != data_vaddr && 362acf7fb3aSchristos mmap(data_addr, data_vlimit - data_vaddr, data_prot, 363acf7fb3aSchristos data_flags, fd, data_offset) == MAP_FAILED) { 364acf7fb3aSchristos _rtld_error("%s: mmap of data failed: %s", path, 3655c5817eeSthorpej xstrerror(errno)); 366acf7fb3aSchristos goto error; 3675c5817eeSthorpej } 3685c5817eeSthorpej 369acf7fb3aSchristos /* Do BSS setup */ 370acf7fb3aSchristos if (segs[i]->p_filesz != segs[i]->p_memsz) { 37141fe218bScgd #ifdef RTLD_LOADER 372acf7fb3aSchristos /* Clear any BSS in the last page of the segment. */ 373acf7fb3aSchristos clear_vaddr = segs[i]->p_vaddr + segs[i]->p_filesz; 37441fe218bScgd clear_addr = mapbase + (clear_vaddr - base_vaddr); 375acf7fb3aSchristos clear_page = mapbase + (round_down(clear_vaddr) 376acf7fb3aSchristos - base_vaddr); 377acf7fb3aSchristos 378acf7fb3aSchristos if ((nclear = data_vlimit - clear_vaddr) > 0) { 379a7db91b9Sskrll /* 380a7db91b9Sskrll * Make sure the end of the segment is 381a7db91b9Sskrll * writable. 382acf7fb3aSchristos */ 383acf7fb3aSchristos if ((data_prot & PROT_WRITE) == 0 && -1 == 384acf7fb3aSchristos mprotect(clear_page, _rtld_pagesz, 385acf7fb3aSchristos data_prot|PROT_WRITE)) { 386acf7fb3aSchristos _rtld_error("%s: mprotect failed: %s", 387acf7fb3aSchristos path, xstrerror(errno)); 388acf7fb3aSchristos goto error; 389acf7fb3aSchristos } 390acf7fb3aSchristos 39141fe218bScgd memset(clear_addr, 0, nclear); 39241fe218bScgd 393acf7fb3aSchristos /* Reset the data protection back */ 394acf7fb3aSchristos if ((data_prot & PROT_WRITE) == 0) 395acf7fb3aSchristos mprotect(clear_page, _rtld_pagesz, 396acf7fb3aSchristos data_prot); 397acf7fb3aSchristos } 39841fe218bScgd #endif 39941fe218bScgd 400acf7fb3aSchristos /* Overlay the BSS segment onto the proper region. */ 401acf7fb3aSchristos bss_vaddr = data_vlimit; 402acf7fb3aSchristos bss_vlimit = round_up(segs[i]->p_vaddr + 403acf7fb3aSchristos segs[i]->p_memsz); 404acf7fb3aSchristos bss_addr = mapbase + (bss_vaddr - base_vaddr); 405acf7fb3aSchristos if (bss_vlimit > bss_vaddr) { 406acf7fb3aSchristos /* There is something to do */ 407acf7fb3aSchristos if (mmap(bss_addr, bss_vlimit - bss_vaddr, 408acf7fb3aSchristos data_prot, data_flags | MAP_ANON, -1, 0) 409acf7fb3aSchristos == MAP_FAILED) { 410acf7fb3aSchristos _rtld_error( 411acf7fb3aSchristos "%s: mmap of bss failed: %s", 412acf7fb3aSchristos path, xstrerror(errno)); 413acf7fb3aSchristos goto error; 414acf7fb3aSchristos } 415acf7fb3aSchristos } 416acf7fb3aSchristos } 417acf7fb3aSchristos 41855509064Schristos if (phdr_vaddr != EA_UNDEF && 41955509064Schristos segs[i]->p_vaddr <= phdr_vaddr && 42055509064Schristos segs[i]->p_memsz >= phdr_memsz) { 42155509064Schristos obj->phdr_loaded = true; 422acf7fb3aSchristos } 42355509064Schristos if (segs[i]->p_offset <= ehdr->e_phoff && 42455509064Schristos segs[i]->p_memsz >= phsize) { 42555509064Schristos phdr_vaddr = segs[i]->p_vaddr + ehdr->e_phoff; 42655509064Schristos phdr_memsz = phsize; 42755509064Schristos obj->phdr_loaded = true; 42855509064Schristos } 42955509064Schristos } 43055509064Schristos if (obj->phdr_loaded) { 43155509064Schristos obj->phdr = (void *)(uintptr_t)phdr_vaddr; 43255509064Schristos obj->phsize = phdr_memsz; 43355509064Schristos } else { 43455509064Schristos Elf_Phdr *buf = xmalloc(phsize); 43555509064Schristos if (buf == NULL) { 43655509064Schristos _rtld_error("%s: cannot allocate program header", path); 43755509064Schristos goto error; 43855509064Schristos } 43955509064Schristos memcpy(buf, phdr, phsize); 44055509064Schristos obj->phdr = buf; 44155509064Schristos obj->phsize = phsize; 442acf7fb3aSchristos } 443acf7fb3aSchristos 444aad59997Sjoerg #if defined(__HAVE_TLS_VARIANT_I) || defined(__HAVE_TLS_VARIANT_II) 4453e105dc2Sriastradh if (phtls != NULL) { 446aad59997Sjoerg obj->tlsinit = mapbase + tls_vaddr; 4473e105dc2Sriastradh dbg(("%s: tls init = %p + %"PRImemsz" = %p", obj->path, 4483e105dc2Sriastradh mapbase, tls_vaddr, obj->tlsinit)); 4493e105dc2Sriastradh } 450aad59997Sjoerg #endif 451aad59997Sjoerg 45241fe218bScgd obj->mapbase = mapbase; 45341fe218bScgd obj->mapsize = mapsize; 45441fe218bScgd obj->relocbase = mapbase - base_vaddr; 455305c9497Smycroft 4564910b5ceSchristos #ifdef GNU_RELRO 457da570a62Sthorpej /* rounding happens later. */ 458da570a62Sthorpej obj->relro_page = obj->relocbase + relro_page; 459da570a62Sthorpej obj->relro_size = relro_size; 4604910b5ceSchristos #endif 4614910b5ceSchristos 4626f56cdc9Smycroft if (obj->dynamic) 463fb3cc85fSmrg obj->dynamic = (void *)(obj->relocbase + (Elf_Addr)(uintptr_t)obj->dynamic); 4646f56cdc9Smycroft if (obj->entry) 465fb3cc85fSmrg obj->entry = (void *)(obj->relocbase + (Elf_Addr)(uintptr_t)obj->entry); 4666f56cdc9Smycroft if (obj->interp) 467fb3cc85fSmrg obj->interp = (void *)(obj->relocbase + (Elf_Addr)(uintptr_t)obj->interp); 468e6cdac9cSskrll if (obj->phdr_loaded) 469e6cdac9cSskrll obj->phdr = (void *)(obj->relocbase + (Elf_Addr)(uintptr_t)obj->phdr); 470afe1984dSskrll #ifdef __ARM_EABI__ 471afe1984dSskrll if (obj->exidx_start) 472afe1984dSskrll obj->exidx_start = (void *)(obj->relocbase + (Elf_Addr)(uintptr_t)obj->exidx_start); 473afe1984dSskrll #endif 474acf7fb3aSchristos xfree(segs); 4756f56cdc9Smycroft 476305c9497Smycroft return obj; 47786103e2fSmycroft 478acf7fb3aSchristos error: 4796f56cdc9Smycroft if (mapbase != MAP_FAILED) 4806f56cdc9Smycroft munmap(mapbase, mapsize); 481acf7fb3aSchristos if (obj->ehdr != MAP_FAILED) 482acf7fb3aSchristos munmap(obj->ehdr, _rtld_pagesz); 4836f56cdc9Smycroft _rtld_obj_free(obj); 484acf7fb3aSchristos xfree(segs); 48586103e2fSmycroft return NULL; 486305c9497Smycroft } 487305c9497Smycroft 488305c9497Smycroft void 4895f573ab6Sskrll _rtld_obj_free(Obj_Entry *obj) 490305c9497Smycroft { 491305c9497Smycroft Objlist_Entry *elm; 4923c085045Schristos Name_Entry *entry; 493305c9497Smycroft 494aad59997Sjoerg #if defined(__HAVE_TLS_VARIANT_I) || defined(__HAVE_TLS_VARIANT_II) 4953caa8dc7Sjoerg if (obj->tls_static) 496aad59997Sjoerg _rtld_tls_offset_free(obj); 497aad59997Sjoerg #endif 498bf4b000dSad xfree(obj->path); 499305c9497Smycroft while (obj->needed != NULL) { 500305c9497Smycroft Needed_Entry *needed = obj->needed; 501305c9497Smycroft obj->needed = needed->next; 502bf4b000dSad xfree(needed); 503305c9497Smycroft } 5043c085045Schristos while ((entry = SIMPLEQ_FIRST(&obj->names)) != NULL) { 5053c085045Schristos SIMPLEQ_REMOVE_HEAD(&obj->names, link); 506e269a214Schristos xfree(entry); 507a3fedff4Schristos } 50806de4264Slukem while ((elm = SIMPLEQ_FIRST(&obj->dldags)) != NULL) { 50906de4264Slukem SIMPLEQ_REMOVE_HEAD(&obj->dldags, link); 510bf4b000dSad xfree(elm); 511305c9497Smycroft } 51206de4264Slukem while ((elm = SIMPLEQ_FIRST(&obj->dagmembers)) != NULL) { 51306de4264Slukem SIMPLEQ_REMOVE_HEAD(&obj->dagmembers, link); 514bf4b000dSad xfree(elm); 515305c9497Smycroft } 516e6cdac9cSskrll if (!obj->phdr_loaded) 517e6cdac9cSskrll xfree((void *)(uintptr_t)obj->phdr); 5182782e828Smartin xfree(obj); 519305c9497Smycroft } 520305c9497Smycroft 521305c9497Smycroft Obj_Entry * 522305c9497Smycroft _rtld_obj_new(void) 523305c9497Smycroft { 524305c9497Smycroft Obj_Entry *obj; 525305c9497Smycroft 526305c9497Smycroft obj = CNEW(Obj_Entry); 5273c085045Schristos SIMPLEQ_INIT(&obj->names); 528305c9497Smycroft SIMPLEQ_INIT(&obj->dldags); 529305c9497Smycroft SIMPLEQ_INIT(&obj->dagmembers); 53041fe218bScgd return obj; 53141fe218bScgd } 53241fe218bScgd 53341fe218bScgd /* 53441fe218bScgd * Given a set of ELF protection flags, return the corresponding protection 53541fe218bScgd * flags for MMAP. 53641fe218bScgd */ 53741fe218bScgd static int 538acf7fb3aSchristos convert_prot(int elfflags) 53941fe218bScgd { 54041fe218bScgd int prot = 0; 541a9f5b3f8Ssimonb 542522cbf02Skleink if (elfflags & PF_R) 54326475619Schristos prot |= PROT_READ; 54441fe218bScgd #ifdef RTLD_LOADER 545522cbf02Skleink if (elfflags & PF_W) 54626475619Schristos prot |= PROT_WRITE; 54741fe218bScgd #endif 548522cbf02Skleink if (elfflags & PF_X) 54926475619Schristos prot |= PROT_EXEC; 55041fe218bScgd return prot; 55141fe218bScgd } 552acf7fb3aSchristos 553acf7fb3aSchristos static int 554acf7fb3aSchristos convert_flags(int elfflags __unused) 555acf7fb3aSchristos { 556acf7fb3aSchristos int flags = MAP_PRIVATE; /* All mappings are private */ 557acf7fb3aSchristos 558acf7fb3aSchristos #ifdef MAP_NOCORE 559acf7fb3aSchristos /* 560acf7fb3aSchristos * Readonly mappings are marked "MAP_NOCORE", because they can be 561acf7fb3aSchristos * reconstructed by a debugger. 562acf7fb3aSchristos */ 563acf7fb3aSchristos if (!(elfflags & PF_W)) 564acf7fb3aSchristos flags |= MAP_NOCORE; 565acf7fb3aSchristos #endif 566acf7fb3aSchristos return flags; 567acf7fb3aSchristos } 568