13124c3e0SJohn Polstra /*- 2e6209940SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3e6209940SPedro F. Giffuni * 43124c3e0SJohn Polstra * Copyright 1996-1998 John D. Polstra. 53124c3e0SJohn Polstra * All rights reserved. 63124c3e0SJohn Polstra * 73124c3e0SJohn Polstra * Redistribution and use in source and binary forms, with or without 83124c3e0SJohn Polstra * modification, are permitted provided that the following conditions 93124c3e0SJohn Polstra * are met: 103124c3e0SJohn Polstra * 1. Redistributions of source code must retain the above copyright 113124c3e0SJohn Polstra * notice, this list of conditions and the following disclaimer. 123124c3e0SJohn Polstra * 2. Redistributions in binary form must reproduce the above copyright 133124c3e0SJohn Polstra * notice, this list of conditions and the following disclaimer in the 143124c3e0SJohn Polstra * documentation and/or other materials provided with the distribution. 153124c3e0SJohn Polstra * 163124c3e0SJohn Polstra * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 173124c3e0SJohn Polstra * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 183124c3e0SJohn Polstra * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 193124c3e0SJohn Polstra * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 203124c3e0SJohn Polstra * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 213124c3e0SJohn Polstra * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 223124c3e0SJohn Polstra * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 233124c3e0SJohn Polstra * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 243124c3e0SJohn Polstra * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 253124c3e0SJohn Polstra * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 263124c3e0SJohn Polstra * 277f3dea24SPeter Wemm * $FreeBSD$ 283124c3e0SJohn Polstra */ 293124c3e0SJohn Polstra 303124c3e0SJohn Polstra #include <sys/param.h> 313124c3e0SJohn Polstra #include <sys/mman.h> 327360ae0fSJohn Polstra #include <sys/stat.h> 333124c3e0SJohn Polstra 343124c3e0SJohn Polstra #include <errno.h> 353124c3e0SJohn Polstra #include <stddef.h> 36926ea445SJohn Polstra #include <stdlib.h> 373124c3e0SJohn Polstra #include <string.h> 383124c3e0SJohn Polstra #include <unistd.h> 393124c3e0SJohn Polstra 40b5393d9fSDoug Rabson #include "debug.h" 413124c3e0SJohn Polstra #include "rtld.h" 423124c3e0SJohn Polstra 43*5d6d106cSKonstantin Belousov static Elf_Ehdr *get_elf_header(int, const char *, const struct stat *, 44*5d6d106cSKonstantin Belousov Elf_Phdr **phdr); 45fa7dd9c5SMatthew Dillon static int convert_flags(int); /* Elf flags -> mmap flags */ 463124c3e0SJohn Polstra 47a36deee3SKonstantin Belousov int __getosreldate(void); 48a36deee3SKonstantin Belousov 49*5d6d106cSKonstantin Belousov static bool 50*5d6d106cSKonstantin Belousov phdr_in_zero_page(const Elf_Ehdr *hdr) 51*5d6d106cSKonstantin Belousov { 52*5d6d106cSKonstantin Belousov return (hdr->e_phoff + hdr->e_phnum * sizeof(Elf_Phdr) < 53*5d6d106cSKonstantin Belousov (size_t)PAGE_SIZE); 54*5d6d106cSKonstantin Belousov } 55*5d6d106cSKonstantin Belousov 563124c3e0SJohn Polstra /* 57bfb1ef60SJohn Polstra * Map a shared object into memory. The "fd" argument is a file descriptor, 583124c3e0SJohn Polstra * which must be open on the object and positioned at its beginning. 59bfb1ef60SJohn Polstra * The "path" argument is a pathname that is used only for error messages. 603124c3e0SJohn Polstra * 613124c3e0SJohn Polstra * The return value is a pointer to a newly-allocated Obj_Entry structure 623124c3e0SJohn Polstra * for the shared object. Returns NULL on failure. 633124c3e0SJohn Polstra */ 643124c3e0SJohn Polstra Obj_Entry * 657360ae0fSJohn Polstra map_object(int fd, const char *path, const struct stat *sb) 663124c3e0SJohn Polstra { 673124c3e0SJohn Polstra Obj_Entry *obj; 68341b3de6SMatthew N. Dodd Elf_Ehdr *hdr; 6978af18bdSDavid E. O'Brien int i; 7013575fc4SDoug Rabson Elf_Phdr *phdr; 7113575fc4SDoug Rabson Elf_Phdr *phlimit; 728b7f25d4SAlexander Kabaev Elf_Phdr **segs; 733124c3e0SJohn Polstra int nsegs; 7413575fc4SDoug Rabson Elf_Phdr *phdyn; 75a607e5d7SJohn Polstra Elf_Phdr *phinterp; 76017246d0SDoug Rabson Elf_Phdr *phtls; 773124c3e0SJohn Polstra caddr_t mapbase; 783124c3e0SJohn Polstra size_t mapsize; 7913575fc4SDoug Rabson Elf_Addr base_vaddr; 8013575fc4SDoug Rabson Elf_Addr base_vlimit; 813124c3e0SJohn Polstra caddr_t base_addr; 82ea8577c7SAlan Cox int base_flags; 8313575fc4SDoug Rabson Elf_Off data_offset; 8413575fc4SDoug Rabson Elf_Addr data_vaddr; 8513575fc4SDoug Rabson Elf_Addr data_vlimit; 863124c3e0SJohn Polstra caddr_t data_addr; 878b7f25d4SAlexander Kabaev int data_prot; 88fa7dd9c5SMatthew Dillon int data_flags; 8913575fc4SDoug Rabson Elf_Addr clear_vaddr; 903124c3e0SJohn Polstra caddr_t clear_addr; 918b7f25d4SAlexander Kabaev caddr_t clear_page; 9249f90ad2SAlexander Kabaev Elf_Addr phdr_vaddr; 9349f90ad2SAlexander Kabaev size_t nclear, phsize; 9413575fc4SDoug Rabson Elf_Addr bss_vaddr; 9513575fc4SDoug Rabson Elf_Addr bss_vlimit; 963124c3e0SJohn Polstra caddr_t bss_addr; 97212f264cSKonstantin Belousov Elf_Word stack_flags; 986d7610d7SKonstantin Belousov Elf_Addr relro_page; 996d7610d7SKonstantin Belousov size_t relro_size; 10083aa9cc0SKonstantin Belousov Elf_Addr note_start; 10183aa9cc0SKonstantin Belousov Elf_Addr note_end; 102bd56d410SKonstantin Belousov char *note_map; 103bd56d410SKonstantin Belousov size_t note_map_len; 104ca7e27bbSAlex Richardson Elf_Addr text_end; 1053124c3e0SJohn Polstra 106*5d6d106cSKonstantin Belousov hdr = get_elf_header(fd, path, sb, &phdr); 107341b3de6SMatthew N. Dodd if (hdr == NULL) 108341b3de6SMatthew N. Dodd return (NULL); 1093124c3e0SJohn Polstra 1103124c3e0SJohn Polstra /* 1113124c3e0SJohn Polstra * Scan the program header entries, and save key information. 11211e0093fSKonstantin Belousov * We expect that the loadable segments are ordered by load address. 1133124c3e0SJohn Polstra */ 11449f90ad2SAlexander Kabaev phsize = hdr->e_phnum * sizeof(phdr[0]); 115341b3de6SMatthew N. Dodd phlimit = phdr + hdr->e_phnum; 1168b7f25d4SAlexander Kabaev nsegs = -1; 11749f90ad2SAlexander Kabaev phdyn = phinterp = phtls = NULL; 11849f90ad2SAlexander Kabaev phdr_vaddr = 0; 1196d7610d7SKonstantin Belousov relro_page = 0; 1206d7610d7SKonstantin Belousov relro_size = 0; 12183aa9cc0SKonstantin Belousov note_start = 0; 12283aa9cc0SKonstantin Belousov note_end = 0; 123bd56d410SKonstantin Belousov note_map = NULL; 1243ab5b6bdSAlex Richardson note_map_len = 0; 125341b3de6SMatthew N. Dodd segs = alloca(sizeof(segs[0]) * hdr->e_phnum); 126cb38d494SKonstantin Belousov stack_flags = RTLD_DEFAULT_STACK_PF_EXEC | PF_R | PF_W; 127ca7e27bbSAlex Richardson text_end = 0; 1283124c3e0SJohn Polstra while (phdr < phlimit) { 1293124c3e0SJohn Polstra switch (phdr->p_type) { 1303124c3e0SJohn Polstra 131a607e5d7SJohn Polstra case PT_INTERP: 132a607e5d7SJohn Polstra phinterp = phdr; 133a607e5d7SJohn Polstra break; 134a607e5d7SJohn Polstra 1353124c3e0SJohn Polstra case PT_LOAD: 1368b7f25d4SAlexander Kabaev segs[++nsegs] = phdr; 13749f90ad2SAlexander Kabaev if ((segs[nsegs]->p_align & (PAGE_SIZE - 1)) != 0) { 1388b7f25d4SAlexander Kabaev _rtld_error("%s: PT_LOAD segment %d not page-aligned", 1398b7f25d4SAlexander Kabaev path, nsegs); 140e474e51eSKonstantin Belousov goto error; 141bfb1ef60SJohn Polstra } 142ca7e27bbSAlex Richardson if ((segs[nsegs]->p_flags & PF_X) == PF_X) { 143ca7e27bbSAlex Richardson text_end = MAX(text_end, 144ca7e27bbSAlex Richardson round_page(segs[nsegs]->p_vaddr + segs[nsegs]->p_memsz)); 145ca7e27bbSAlex Richardson } 1463124c3e0SJohn Polstra break; 1473124c3e0SJohn Polstra 1483124c3e0SJohn Polstra case PT_PHDR: 14949f90ad2SAlexander Kabaev phdr_vaddr = phdr->p_vaddr; 15049f90ad2SAlexander Kabaev phsize = phdr->p_memsz; 1513124c3e0SJohn Polstra break; 1523124c3e0SJohn Polstra 1533124c3e0SJohn Polstra case PT_DYNAMIC: 1543124c3e0SJohn Polstra phdyn = phdr; 1553124c3e0SJohn Polstra break; 156017246d0SDoug Rabson 157017246d0SDoug Rabson case PT_TLS: 158017246d0SDoug Rabson phtls = phdr; 159017246d0SDoug Rabson break; 160212f264cSKonstantin Belousov 161212f264cSKonstantin Belousov case PT_GNU_STACK: 162212f264cSKonstantin Belousov stack_flags = phdr->p_flags; 163212f264cSKonstantin Belousov break; 1646d7610d7SKonstantin Belousov 1656d7610d7SKonstantin Belousov case PT_GNU_RELRO: 1666d7610d7SKonstantin Belousov relro_page = phdr->p_vaddr; 1676d7610d7SKonstantin Belousov relro_size = phdr->p_memsz; 1686d7610d7SKonstantin Belousov break; 16983aa9cc0SKonstantin Belousov 17083aa9cc0SKonstantin Belousov case PT_NOTE: 1715eab36f2SKonstantin Belousov if (phdr->p_offset > PAGE_SIZE || 172bd56d410SKonstantin Belousov phdr->p_offset + phdr->p_filesz > PAGE_SIZE) { 173bd56d410SKonstantin Belousov note_map_len = round_page(phdr->p_offset + 174bd56d410SKonstantin Belousov phdr->p_filesz) - trunc_page(phdr->p_offset); 175bd56d410SKonstantin Belousov note_map = mmap(NULL, note_map_len, PROT_READ, 176bd56d410SKonstantin Belousov MAP_PRIVATE, fd, trunc_page(phdr->p_offset)); 177bd56d410SKonstantin Belousov if (note_map == MAP_FAILED) { 178bd56d410SKonstantin Belousov _rtld_error("%s: error mapping PT_NOTE (%d)", path, errno); 179bd56d410SKonstantin Belousov goto error; 180bd56d410SKonstantin Belousov } 181bd56d410SKonstantin Belousov note_start = (Elf_Addr)(note_map + phdr->p_offset - 182bd56d410SKonstantin Belousov trunc_page(phdr->p_offset)); 183bd56d410SKonstantin Belousov } else { 1845eab36f2SKonstantin Belousov note_start = (Elf_Addr)(char *)hdr + phdr->p_offset; 185bd56d410SKonstantin Belousov } 18683aa9cc0SKonstantin Belousov note_end = note_start + phdr->p_filesz; 18783aa9cc0SKonstantin Belousov break; 1883124c3e0SJohn Polstra } 1893124c3e0SJohn Polstra 1903124c3e0SJohn Polstra ++phdr; 1913124c3e0SJohn Polstra } 1923124c3e0SJohn Polstra if (phdyn == NULL) { 193bfb1ef60SJohn Polstra _rtld_error("%s: object is not dynamically-linked", path); 194e474e51eSKonstantin Belousov goto error; 1953124c3e0SJohn Polstra } 1963124c3e0SJohn Polstra 1978b7f25d4SAlexander Kabaev if (nsegs < 0) { 198bfb1ef60SJohn Polstra _rtld_error("%s: too few PT_LOAD segments", path); 199e474e51eSKonstantin Belousov goto error; 200bfb1ef60SJohn Polstra } 2013124c3e0SJohn Polstra 2023124c3e0SJohn Polstra /* 2033124c3e0SJohn Polstra * Map the entire address space of the object, to stake out our 2043124c3e0SJohn Polstra * contiguous region, and to establish the base address for relocation. 2053124c3e0SJohn Polstra */ 2063124c3e0SJohn Polstra base_vaddr = trunc_page(segs[0]->p_vaddr); 2078b7f25d4SAlexander Kabaev base_vlimit = round_page(segs[nsegs]->p_vaddr + segs[nsegs]->p_memsz); 2083124c3e0SJohn Polstra mapsize = base_vlimit - base_vaddr; 20915789513STijl Coosemans base_addr = (caddr_t) base_vaddr; 210a36deee3SKonstantin Belousov base_flags = __getosreldate() >= P_OSREL_MAP_GUARD ? MAP_GUARD : 211a36deee3SKonstantin Belousov MAP_PRIVATE | MAP_ANON | MAP_NOCORE; 212ea8577c7SAlan Cox if (npagesizes > 1 && round_page(segs[0]->p_filesz) >= pagesizes[1]) 213ea8577c7SAlan Cox base_flags |= MAP_ALIGNED_SUPER; 21491041919SKonstantin Belousov if (base_vaddr != 0) 21591041919SKonstantin Belousov base_flags |= MAP_FIXED | MAP_EXCL; 2163124c3e0SJohn Polstra 217ea8577c7SAlan Cox mapbase = mmap(base_addr, mapsize, PROT_NONE, base_flags, -1, 0); 218f5392eb6SKonstantin Belousov if (mapbase == MAP_FAILED) { 219bfb1ef60SJohn Polstra _rtld_error("%s: mmap of entire address space failed: %s", 2206fea10fbSKonstantin Belousov path, rtld_strerror(errno)); 221e474e51eSKonstantin Belousov goto error; 2223124c3e0SJohn Polstra } 2233124c3e0SJohn Polstra if (base_addr != NULL && mapbase != base_addr) { 224bfb1ef60SJohn Polstra _rtld_error("%s: mmap returned wrong address: wanted %p, got %p", 225bfb1ef60SJohn Polstra path, base_addr, mapbase); 226e474e51eSKonstantin Belousov goto error1; 2273124c3e0SJohn Polstra } 2283124c3e0SJohn Polstra 2298b7f25d4SAlexander Kabaev for (i = 0; i <= nsegs; i++) { 2308b7f25d4SAlexander Kabaev /* Overlay the segment onto the proper region. */ 2318b7f25d4SAlexander Kabaev data_offset = trunc_page(segs[i]->p_offset); 2328b7f25d4SAlexander Kabaev data_vaddr = trunc_page(segs[i]->p_vaddr); 2338b7f25d4SAlexander Kabaev data_vlimit = round_page(segs[i]->p_vaddr + segs[i]->p_filesz); 2343124c3e0SJohn Polstra data_addr = mapbase + (data_vaddr - base_vaddr); 235fa7dd9c5SMatthew Dillon data_prot = convert_prot(segs[i]->p_flags); 236fa7dd9c5SMatthew Dillon data_flags = convert_flags(segs[i]->p_flags) | MAP_FIXED; 23744c9aa49SBrandon Bergren if (data_vlimit != data_vaddr && 23844c9aa49SBrandon Bergren mmap(data_addr, data_vlimit - data_vaddr, data_prot, 23944c9aa49SBrandon Bergren data_flags | MAP_PREFAULT_READ, fd, data_offset) == MAP_FAILED) { 2406fea10fbSKonstantin Belousov _rtld_error("%s: mmap of data failed: %s", path, 2416fea10fbSKonstantin Belousov rtld_strerror(errno)); 242e474e51eSKonstantin Belousov goto error1; 2433124c3e0SJohn Polstra } 2443124c3e0SJohn Polstra 24569ca61baSKonstantin Belousov /* Do BSS setup */ 24669ca61baSKonstantin Belousov if (segs[i]->p_filesz != segs[i]->p_memsz) { 24769ca61baSKonstantin Belousov 2488b7f25d4SAlexander Kabaev /* Clear any BSS in the last page of the segment. */ 2498b7f25d4SAlexander Kabaev clear_vaddr = segs[i]->p_vaddr + segs[i]->p_filesz; 2503124c3e0SJohn Polstra clear_addr = mapbase + (clear_vaddr - base_vaddr); 2518b7f25d4SAlexander Kabaev clear_page = mapbase + (trunc_page(clear_vaddr) - base_vaddr); 25269ca61baSKonstantin Belousov 2538b7f25d4SAlexander Kabaev if ((nclear = data_vlimit - clear_vaddr) > 0) { 2548b7f25d4SAlexander Kabaev /* Make sure the end of the segment is writable */ 25569ca61baSKonstantin Belousov if ((data_prot & PROT_WRITE) == 0 && -1 == 25669ca61baSKonstantin Belousov mprotect(clear_page, PAGE_SIZE, data_prot|PROT_WRITE)) { 2578b7f25d4SAlexander Kabaev _rtld_error("%s: mprotect failed: %s", path, 2586fea10fbSKonstantin Belousov rtld_strerror(errno)); 259e474e51eSKonstantin Belousov goto error1; 2608b7f25d4SAlexander Kabaev } 2618b7f25d4SAlexander Kabaev 2623124c3e0SJohn Polstra memset(clear_addr, 0, nclear); 2633124c3e0SJohn Polstra 2648b7f25d4SAlexander Kabaev /* Reset the data protection back */ 2658b7f25d4SAlexander Kabaev if ((data_prot & PROT_WRITE) == 0) 2668b7f25d4SAlexander Kabaev mprotect(clear_page, PAGE_SIZE, data_prot); 2678b7f25d4SAlexander Kabaev } 2688b7f25d4SAlexander Kabaev 2693124c3e0SJohn Polstra /* Overlay the BSS segment onto the proper region. */ 2703124c3e0SJohn Polstra bss_vaddr = data_vlimit; 2718b7f25d4SAlexander Kabaev bss_vlimit = round_page(segs[i]->p_vaddr + segs[i]->p_memsz); 2723124c3e0SJohn Polstra bss_addr = mapbase + (bss_vaddr - base_vaddr); 2733124c3e0SJohn Polstra if (bss_vlimit > bss_vaddr) { /* There is something to do */ 274750b5e31SKonstantin Belousov if (mmap(bss_addr, bss_vlimit - bss_vaddr, data_prot, 275f5392eb6SKonstantin Belousov data_flags | MAP_ANON, -1, 0) == MAP_FAILED) { 276750b5e31SKonstantin Belousov _rtld_error("%s: mmap of bss failed: %s", path, 2776fea10fbSKonstantin Belousov rtld_strerror(errno)); 278e474e51eSKonstantin Belousov goto error1; 2793124c3e0SJohn Polstra } 2803124c3e0SJohn Polstra } 28169ca61baSKonstantin Belousov } 28269ca61baSKonstantin Belousov 28349f90ad2SAlexander Kabaev if (phdr_vaddr == 0 && data_offset <= hdr->e_phoff && 28449f90ad2SAlexander Kabaev (data_vlimit - data_vaddr + data_offset) >= 28549f90ad2SAlexander Kabaev (hdr->e_phoff + hdr->e_phnum * sizeof (Elf_Phdr))) { 28649f90ad2SAlexander Kabaev phdr_vaddr = data_vaddr + hdr->e_phoff - data_offset; 28749f90ad2SAlexander Kabaev } 2888b7f25d4SAlexander Kabaev } 2893124c3e0SJohn Polstra 290926ea445SJohn Polstra obj = obj_new(); 2917360ae0fSJohn Polstra if (sb != NULL) { 2927360ae0fSJohn Polstra obj->dev = sb->st_dev; 2937360ae0fSJohn Polstra obj->ino = sb->st_ino; 2947360ae0fSJohn Polstra } 2953124c3e0SJohn Polstra obj->mapbase = mapbase; 2963124c3e0SJohn Polstra obj->mapsize = mapsize; 2973124c3e0SJohn Polstra obj->vaddrbase = base_vaddr; 2983124c3e0SJohn Polstra obj->relocbase = mapbase - base_vaddr; 299a607e5d7SJohn Polstra obj->dynamic = (const Elf_Dyn *)(obj->relocbase + phdyn->p_vaddr); 300341b3de6SMatthew N. Dodd if (hdr->e_entry != 0) 301341b3de6SMatthew N. Dodd obj->entry = (caddr_t)(obj->relocbase + hdr->e_entry); 30249f90ad2SAlexander Kabaev if (phdr_vaddr != 0) { 30349f90ad2SAlexander Kabaev obj->phdr = (const Elf_Phdr *)(obj->relocbase + phdr_vaddr); 30449f90ad2SAlexander Kabaev } else { 30549f90ad2SAlexander Kabaev obj->phdr = malloc(phsize); 30649f90ad2SAlexander Kabaev if (obj->phdr == NULL) { 30749f90ad2SAlexander Kabaev obj_free(obj); 30849f90ad2SAlexander Kabaev _rtld_error("%s: cannot allocate program header", path); 309e474e51eSKonstantin Belousov goto error1; 3103124c3e0SJohn Polstra } 311903e0ffdSAlex Richardson memcpy(__DECONST(char *, obj->phdr), (char *)hdr + hdr->e_phoff, phsize); 31249f90ad2SAlexander Kabaev obj->phdr_alloc = true; 31349f90ad2SAlexander Kabaev } 31449f90ad2SAlexander Kabaev obj->phsize = phsize; 315a607e5d7SJohn Polstra if (phinterp != NULL) 316a607e5d7SJohn Polstra obj->interp = (const char *)(obj->relocbase + phinterp->p_vaddr); 317017246d0SDoug Rabson if (phtls != NULL) { 318017246d0SDoug Rabson tls_dtv_generation++; 319017246d0SDoug Rabson obj->tlsindex = ++tls_max_index; 320017246d0SDoug Rabson obj->tlssize = phtls->p_memsz; 321017246d0SDoug Rabson obj->tlsalign = phtls->p_align; 3222f06c66aSKonstantin Belousov obj->tlspoffset = phtls->p_offset; 323017246d0SDoug Rabson obj->tlsinitsize = phtls->p_filesz; 324017246d0SDoug Rabson obj->tlsinit = mapbase + phtls->p_vaddr; 325017246d0SDoug Rabson } 326212f264cSKonstantin Belousov obj->stack_flags = stack_flags; 3276d7610d7SKonstantin Belousov obj->relro_page = obj->relocbase + trunc_page(relro_page); 3286d7610d7SKonstantin Belousov obj->relro_size = round_page(relro_size); 329d958a71bSAlexander Kabaev if (note_start < note_end) 330d958a71bSAlexander Kabaev digest_notes(obj, note_start, note_end); 331bd56d410SKonstantin Belousov if (note_map != NULL) 332bd56d410SKonstantin Belousov munmap(note_map, note_map_len); 333e474e51eSKonstantin Belousov munmap(hdr, PAGE_SIZE); 334e474e51eSKonstantin Belousov return (obj); 335e474e51eSKonstantin Belousov 336e474e51eSKonstantin Belousov error1: 337e474e51eSKonstantin Belousov munmap(mapbase, mapsize); 338e474e51eSKonstantin Belousov error: 339bd56d410SKonstantin Belousov if (note_map != NULL && note_map != MAP_FAILED) 340bd56d410SKonstantin Belousov munmap(note_map, note_map_len); 341*5d6d106cSKonstantin Belousov if (!phdr_in_zero_page(hdr)) 342*5d6d106cSKonstantin Belousov munmap(phdr, hdr->e_phnum * sizeof(phdr[0])); 343e474e51eSKonstantin Belousov munmap(hdr, PAGE_SIZE); 344e474e51eSKonstantin Belousov return (NULL); 3453124c3e0SJohn Polstra } 3463124c3e0SJohn Polstra 347341b3de6SMatthew N. Dodd static Elf_Ehdr * 348*5d6d106cSKonstantin Belousov get_elf_header(int fd, const char *path, const struct stat *sbp, 349*5d6d106cSKonstantin Belousov Elf_Phdr **phdr_p) 350341b3de6SMatthew N. Dodd { 351e474e51eSKonstantin Belousov Elf_Ehdr *hdr; 352*5d6d106cSKonstantin Belousov Elf_Phdr *phdr; 353341b3de6SMatthew N. Dodd 3547fd852f8SMaxim Sobolev /* Make sure file has enough data for the ELF header */ 35578b64846SAlex Richardson if (sbp != NULL && sbp->st_size < (off_t)sizeof(Elf_Ehdr)) { 3567fd852f8SMaxim Sobolev _rtld_error("%s: invalid file format", path); 3577fd852f8SMaxim Sobolev return (NULL); 3587fd852f8SMaxim Sobolev } 3597fd852f8SMaxim Sobolev 360e474e51eSKonstantin Belousov hdr = mmap(NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE | MAP_PREFAULT_READ, 361e474e51eSKonstantin Belousov fd, 0); 362f5392eb6SKonstantin Belousov if (hdr == MAP_FAILED) { 3636fea10fbSKonstantin Belousov _rtld_error("%s: read error: %s", path, rtld_strerror(errno)); 364e474e51eSKonstantin Belousov return (NULL); 365341b3de6SMatthew N. Dodd } 366341b3de6SMatthew N. Dodd 367341b3de6SMatthew N. Dodd /* Make sure the file is valid */ 368e474e51eSKonstantin Belousov if (!IS_ELF(*hdr)) { 369341b3de6SMatthew N. Dodd _rtld_error("%s: invalid file format", path); 370e474e51eSKonstantin Belousov goto error; 371341b3de6SMatthew N. Dodd } 372e474e51eSKonstantin Belousov if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || 373e474e51eSKonstantin Belousov hdr->e_ident[EI_DATA] != ELF_TARG_DATA) { 374341b3de6SMatthew N. Dodd _rtld_error("%s: unsupported file layout", path); 375e474e51eSKonstantin Belousov goto error; 376341b3de6SMatthew N. Dodd } 377e474e51eSKonstantin Belousov if (hdr->e_ident[EI_VERSION] != EV_CURRENT || 378e474e51eSKonstantin Belousov hdr->e_version != EV_CURRENT) { 379341b3de6SMatthew N. Dodd _rtld_error("%s: unsupported file version", path); 380e474e51eSKonstantin Belousov goto error; 381341b3de6SMatthew N. Dodd } 382e474e51eSKonstantin Belousov if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN) { 383341b3de6SMatthew N. Dodd _rtld_error("%s: unsupported file type", path); 384e474e51eSKonstantin Belousov goto error; 385341b3de6SMatthew N. Dodd } 386e474e51eSKonstantin Belousov if (hdr->e_machine != ELF_TARG_MACH) { 387341b3de6SMatthew N. Dodd _rtld_error("%s: unsupported machine", path); 388e474e51eSKonstantin Belousov goto error; 389341b3de6SMatthew N. Dodd } 390341b3de6SMatthew N. Dodd 391341b3de6SMatthew N. Dodd /* 392341b3de6SMatthew N. Dodd * We rely on the program header being in the first page. This is 393341b3de6SMatthew N. Dodd * not strictly required by the ABI specification, but it seems to 394341b3de6SMatthew N. Dodd * always true in practice. And, it simplifies things considerably. 395341b3de6SMatthew N. Dodd */ 396e474e51eSKonstantin Belousov if (hdr->e_phentsize != sizeof(Elf_Phdr)) { 397341b3de6SMatthew N. Dodd _rtld_error( 398341b3de6SMatthew N. Dodd "%s: invalid shared object: e_phentsize != sizeof(Elf_Phdr)", path); 399e474e51eSKonstantin Belousov goto error; 400341b3de6SMatthew N. Dodd } 401*5d6d106cSKonstantin Belousov if (phdr_in_zero_page(hdr)) { 402*5d6d106cSKonstantin Belousov phdr = (Elf_Phdr *)((char *)hdr + hdr->e_phoff); 403*5d6d106cSKonstantin Belousov } else { 404*5d6d106cSKonstantin Belousov phdr = mmap(NULL, hdr->e_phnum * sizeof(phdr[0]), 405*5d6d106cSKonstantin Belousov PROT_READ, MAP_PRIVATE | MAP_PREFAULT_READ, fd, 406*5d6d106cSKonstantin Belousov hdr->e_phoff); 407*5d6d106cSKonstantin Belousov if (phdr == MAP_FAILED) { 408*5d6d106cSKonstantin Belousov _rtld_error("%s: error mapping phdr: %s", path, 409*5d6d106cSKonstantin Belousov rtld_strerror(errno)); 410e474e51eSKonstantin Belousov goto error; 411341b3de6SMatthew N. Dodd } 412*5d6d106cSKonstantin Belousov } 413*5d6d106cSKonstantin Belousov *phdr_p = phdr; 414e474e51eSKonstantin Belousov return (hdr); 415341b3de6SMatthew N. Dodd 416e474e51eSKonstantin Belousov error: 417e474e51eSKonstantin Belousov munmap(hdr, PAGE_SIZE); 418e474e51eSKonstantin Belousov return (NULL); 419341b3de6SMatthew N. Dodd } 420341b3de6SMatthew N. Dodd 421926ea445SJohn Polstra void 422926ea445SJohn Polstra obj_free(Obj_Entry *obj) 423926ea445SJohn Polstra { 424926ea445SJohn Polstra Objlist_Entry *elm; 425926ea445SJohn Polstra 4260eb88f20SAlexander Kabaev if (obj->tls_done) 427ddab7ee8SDoug Rabson free_tls_offset(obj); 428926ea445SJohn Polstra while (obj->needed != NULL) { 429926ea445SJohn Polstra Needed_Entry *needed = obj->needed; 430926ea445SJohn Polstra obj->needed = needed->next; 431926ea445SJohn Polstra free(needed); 432926ea445SJohn Polstra } 4330eb88f20SAlexander Kabaev while (!STAILQ_EMPTY(&obj->names)) { 4340eb88f20SAlexander Kabaev Name_Entry *entry = STAILQ_FIRST(&obj->names); 4350eb88f20SAlexander Kabaev STAILQ_REMOVE_HEAD(&obj->names, link); 4360eb88f20SAlexander Kabaev free(entry); 4370eb88f20SAlexander Kabaev } 438926ea445SJohn Polstra while (!STAILQ_EMPTY(&obj->dldags)) { 439926ea445SJohn Polstra elm = STAILQ_FIRST(&obj->dldags); 440926ea445SJohn Polstra STAILQ_REMOVE_HEAD(&obj->dldags, link); 441926ea445SJohn Polstra free(elm); 442926ea445SJohn Polstra } 443926ea445SJohn Polstra while (!STAILQ_EMPTY(&obj->dagmembers)) { 444926ea445SJohn Polstra elm = STAILQ_FIRST(&obj->dagmembers); 445926ea445SJohn Polstra STAILQ_REMOVE_HEAD(&obj->dagmembers, link); 446926ea445SJohn Polstra free(elm); 447926ea445SJohn Polstra } 44849f90ad2SAlexander Kabaev if (obj->vertab) 4490eb88f20SAlexander Kabaev free(obj->vertab); 45049f90ad2SAlexander Kabaev if (obj->origin_path) 451da9f2454SMatthew N. Dodd free(obj->origin_path); 45228551690SKonstantin Belousov if (obj->z_origin) 453903e0ffdSAlex Richardson free(__DECONST(void*, obj->rpath)); 45449f90ad2SAlexander Kabaev if (obj->priv) 45563c1e7cbSAlexander Kabaev free(obj->priv); 45649f90ad2SAlexander Kabaev if (obj->path) 45749f90ad2SAlexander Kabaev free(obj->path); 45849f90ad2SAlexander Kabaev if (obj->phdr_alloc) 459903e0ffdSAlex Richardson free(__DECONST(void *, obj->phdr)); 460926ea445SJohn Polstra free(obj); 461926ea445SJohn Polstra } 462926ea445SJohn Polstra 463926ea445SJohn Polstra Obj_Entry * 464926ea445SJohn Polstra obj_new(void) 465926ea445SJohn Polstra { 466926ea445SJohn Polstra Obj_Entry *obj; 467926ea445SJohn Polstra 468926ea445SJohn Polstra obj = CNEW(Obj_Entry); 469926ea445SJohn Polstra STAILQ_INIT(&obj->dldags); 470926ea445SJohn Polstra STAILQ_INIT(&obj->dagmembers); 4710eb88f20SAlexander Kabaev STAILQ_INIT(&obj->names); 472926ea445SJohn Polstra return obj; 473926ea445SJohn Polstra } 474926ea445SJohn Polstra 4753124c3e0SJohn Polstra /* 4763124c3e0SJohn Polstra * Given a set of ELF protection flags, return the corresponding protection 4773124c3e0SJohn Polstra * flags for MMAP. 4783124c3e0SJohn Polstra */ 479ca8c8dc3SKonstantin Belousov int 480fa7dd9c5SMatthew Dillon convert_prot(int elfflags) 4813124c3e0SJohn Polstra { 4823124c3e0SJohn Polstra int prot = 0; 4833124c3e0SJohn Polstra if (elfflags & PF_R) 4843124c3e0SJohn Polstra prot |= PROT_READ; 4853124c3e0SJohn Polstra if (elfflags & PF_W) 4863124c3e0SJohn Polstra prot |= PROT_WRITE; 4873124c3e0SJohn Polstra if (elfflags & PF_X) 4883124c3e0SJohn Polstra prot |= PROT_EXEC; 4893124c3e0SJohn Polstra return prot; 4903124c3e0SJohn Polstra } 491fa7dd9c5SMatthew Dillon 492fa7dd9c5SMatthew Dillon static int 493fa7dd9c5SMatthew Dillon convert_flags(int elfflags) 494fa7dd9c5SMatthew Dillon { 495fa7dd9c5SMatthew Dillon int flags = MAP_PRIVATE; /* All mappings are private */ 496fa7dd9c5SMatthew Dillon 497fa7dd9c5SMatthew Dillon /* 498fa7dd9c5SMatthew Dillon * Readonly mappings are marked "MAP_NOCORE", because they can be 499fa7dd9c5SMatthew Dillon * reconstructed by a debugger. 500fa7dd9c5SMatthew Dillon */ 501fa7dd9c5SMatthew Dillon if (!(elfflags & PF_W)) 502fa7dd9c5SMatthew Dillon flags |= MAP_NOCORE; 503fa7dd9c5SMatthew Dillon return flags; 504fa7dd9c5SMatthew Dillon } 505