13124c3e0SJohn Polstra /*- 23124c3e0SJohn Polstra * Copyright 1996-1998 John D. Polstra. 33124c3e0SJohn Polstra * All rights reserved. 43124c3e0SJohn Polstra * 53124c3e0SJohn Polstra * Redistribution and use in source and binary forms, with or without 63124c3e0SJohn Polstra * modification, are permitted provided that the following conditions 73124c3e0SJohn Polstra * are met: 83124c3e0SJohn Polstra * 1. Redistributions of source code must retain the above copyright 93124c3e0SJohn Polstra * notice, this list of conditions and the following disclaimer. 103124c3e0SJohn Polstra * 2. Redistributions in binary form must reproduce the above copyright 113124c3e0SJohn Polstra * notice, this list of conditions and the following disclaimer in the 123124c3e0SJohn Polstra * documentation and/or other materials provided with the distribution. 133124c3e0SJohn Polstra * 143124c3e0SJohn Polstra * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 153124c3e0SJohn Polstra * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 163124c3e0SJohn Polstra * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 173124c3e0SJohn Polstra * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 183124c3e0SJohn Polstra * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 193124c3e0SJohn Polstra * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 203124c3e0SJohn Polstra * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 213124c3e0SJohn Polstra * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 223124c3e0SJohn Polstra * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 233124c3e0SJohn Polstra * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 243124c3e0SJohn Polstra * 2513575fc4SDoug Rabson * $Id: map_object.c,v 1.1.1.1 1998/03/07 19:24:35 jdp Exp $ 263124c3e0SJohn Polstra */ 273124c3e0SJohn Polstra 283124c3e0SJohn Polstra #include <sys/param.h> 293124c3e0SJohn Polstra #include <sys/mman.h> 303124c3e0SJohn Polstra 313124c3e0SJohn Polstra #include <assert.h> 323124c3e0SJohn Polstra #include <errno.h> 333124c3e0SJohn Polstra #include <stddef.h> 343124c3e0SJohn Polstra #include <string.h> 353124c3e0SJohn Polstra #include <unistd.h> 363124c3e0SJohn Polstra 373124c3e0SJohn Polstra #include "rtld.h" 383124c3e0SJohn Polstra 393124c3e0SJohn Polstra static int protflags(int); /* Elf flags -> mmap protection */ 403124c3e0SJohn Polstra 413124c3e0SJohn Polstra /* 423124c3e0SJohn Polstra * Map a shared object into memory. The argument is a file descriptor, 433124c3e0SJohn Polstra * which must be open on the object and positioned at its beginning. 443124c3e0SJohn Polstra * 453124c3e0SJohn Polstra * The return value is a pointer to a newly-allocated Obj_Entry structure 463124c3e0SJohn Polstra * for the shared object. Returns NULL on failure. 473124c3e0SJohn Polstra */ 483124c3e0SJohn Polstra Obj_Entry * 493124c3e0SJohn Polstra map_object(int fd) 503124c3e0SJohn Polstra { 513124c3e0SJohn Polstra Obj_Entry *obj; 523124c3e0SJohn Polstra union { 5313575fc4SDoug Rabson Elf_Ehdr hdr; 543124c3e0SJohn Polstra char buf[PAGE_SIZE]; 553124c3e0SJohn Polstra } u; 563124c3e0SJohn Polstra int nbytes; 5713575fc4SDoug Rabson Elf_Phdr *phdr; 5813575fc4SDoug Rabson Elf_Phdr *phlimit; 5913575fc4SDoug Rabson Elf_Phdr *segs[2]; 603124c3e0SJohn Polstra int nsegs; 6113575fc4SDoug Rabson Elf_Phdr *phdyn; 6213575fc4SDoug Rabson Elf_Phdr *phphdr; 633124c3e0SJohn Polstra caddr_t mapbase; 643124c3e0SJohn Polstra size_t mapsize; 6513575fc4SDoug Rabson Elf_Off base_offset; 6613575fc4SDoug Rabson Elf_Addr base_vaddr; 6713575fc4SDoug Rabson Elf_Addr base_vlimit; 683124c3e0SJohn Polstra caddr_t base_addr; 6913575fc4SDoug Rabson Elf_Off data_offset; 7013575fc4SDoug Rabson Elf_Addr data_vaddr; 7113575fc4SDoug Rabson Elf_Addr data_vlimit; 723124c3e0SJohn Polstra caddr_t data_addr; 7313575fc4SDoug Rabson Elf_Addr clear_vaddr; 743124c3e0SJohn Polstra caddr_t clear_addr; 753124c3e0SJohn Polstra size_t nclear; 7613575fc4SDoug Rabson Elf_Addr bss_vaddr; 7713575fc4SDoug Rabson Elf_Addr bss_vlimit; 783124c3e0SJohn Polstra caddr_t bss_addr; 793124c3e0SJohn Polstra 803124c3e0SJohn Polstra if ((nbytes = read(fd, u.buf, PAGE_SIZE)) == -1) { 813124c3e0SJohn Polstra _rtld_error("Read error: %s", strerror(errno)); 823124c3e0SJohn Polstra return NULL; 833124c3e0SJohn Polstra } 843124c3e0SJohn Polstra 853124c3e0SJohn Polstra /* Make sure the file is valid */ 8613575fc4SDoug Rabson if (nbytes < sizeof(Elf_Ehdr) 873124c3e0SJohn Polstra || u.hdr.e_ident[EI_MAG0] != ELFMAG0 883124c3e0SJohn Polstra || u.hdr.e_ident[EI_MAG1] != ELFMAG1 893124c3e0SJohn Polstra || u.hdr.e_ident[EI_MAG2] != ELFMAG2 903124c3e0SJohn Polstra || u.hdr.e_ident[EI_MAG3] != ELFMAG3) { 913124c3e0SJohn Polstra _rtld_error("Invalid file format"); 923124c3e0SJohn Polstra return NULL; 933124c3e0SJohn Polstra } 9413575fc4SDoug Rabson if (u.hdr.e_ident[EI_CLASS] != ELF_TARG_CLASS 9513575fc4SDoug Rabson || u.hdr.e_ident[EI_DATA] != ELF_TARG_DATA) { 963124c3e0SJohn Polstra _rtld_error("Unsupported file layout"); 973124c3e0SJohn Polstra return NULL; 983124c3e0SJohn Polstra } 993124c3e0SJohn Polstra if (u.hdr.e_ident[EI_VERSION] != EV_CURRENT 1003124c3e0SJohn Polstra || u.hdr.e_version != EV_CURRENT) { 1013124c3e0SJohn Polstra _rtld_error("Unsupported file version"); 1023124c3e0SJohn Polstra return NULL; 1033124c3e0SJohn Polstra } 1043124c3e0SJohn Polstra if (u.hdr.e_type != ET_EXEC && u.hdr.e_type != ET_DYN) { 1053124c3e0SJohn Polstra _rtld_error("Unsupported file type"); 1063124c3e0SJohn Polstra return NULL; 1073124c3e0SJohn Polstra } 10813575fc4SDoug Rabson if (u.hdr.e_machine != ELF_TARG_MACH) { 1093124c3e0SJohn Polstra _rtld_error("Unsupported machine"); 1103124c3e0SJohn Polstra return NULL; 1113124c3e0SJohn Polstra } 1123124c3e0SJohn Polstra 1133124c3e0SJohn Polstra /* 1143124c3e0SJohn Polstra * We rely on the program header being in the first page. This is 1153124c3e0SJohn Polstra * not strictly required by the ABI specification, but it seems to 1163124c3e0SJohn Polstra * always true in practice. And, it simplifies things considerably. 1173124c3e0SJohn Polstra */ 11813575fc4SDoug Rabson assert(u.hdr.e_phentsize == sizeof(Elf_Phdr)); 11913575fc4SDoug Rabson assert(u.hdr.e_phoff + u.hdr.e_phnum*sizeof(Elf_Phdr) <= PAGE_SIZE); 12013575fc4SDoug Rabson assert(u.hdr.e_phoff + u.hdr.e_phnum*sizeof(Elf_Phdr) <= nbytes); 1213124c3e0SJohn Polstra 1223124c3e0SJohn Polstra /* 1233124c3e0SJohn Polstra * Scan the program header entries, and save key information. 1243124c3e0SJohn Polstra * 1253124c3e0SJohn Polstra * We rely on there being exactly two load segments, text and data, 1263124c3e0SJohn Polstra * in that order. 1273124c3e0SJohn Polstra */ 12813575fc4SDoug Rabson phdr = (Elf_Phdr *) (u.buf + u.hdr.e_phoff); 1293124c3e0SJohn Polstra phlimit = phdr + u.hdr.e_phnum; 1303124c3e0SJohn Polstra nsegs = 0; 1313124c3e0SJohn Polstra phdyn = NULL; 1323124c3e0SJohn Polstra phphdr = NULL; 1333124c3e0SJohn Polstra while (phdr < phlimit) { 1343124c3e0SJohn Polstra switch (phdr->p_type) { 1353124c3e0SJohn Polstra 1363124c3e0SJohn Polstra case PT_LOAD: 1373124c3e0SJohn Polstra assert(nsegs < 2); 1383124c3e0SJohn Polstra segs[nsegs] = phdr; 1393124c3e0SJohn Polstra ++nsegs; 1403124c3e0SJohn Polstra break; 1413124c3e0SJohn Polstra 1423124c3e0SJohn Polstra case PT_PHDR: 1433124c3e0SJohn Polstra phphdr = phdr; 1443124c3e0SJohn Polstra break; 1453124c3e0SJohn Polstra 1463124c3e0SJohn Polstra case PT_DYNAMIC: 1473124c3e0SJohn Polstra phdyn = phdr; 1483124c3e0SJohn Polstra break; 1493124c3e0SJohn Polstra } 1503124c3e0SJohn Polstra 1513124c3e0SJohn Polstra ++phdr; 1523124c3e0SJohn Polstra } 1533124c3e0SJohn Polstra if (phdyn == NULL) { 1543124c3e0SJohn Polstra _rtld_error("Object is not dynamically-linked"); 1553124c3e0SJohn Polstra return NULL; 1563124c3e0SJohn Polstra } 1573124c3e0SJohn Polstra 1583124c3e0SJohn Polstra assert(nsegs == 2); 15913575fc4SDoug Rabson assert(segs[0]->p_align >= PAGE_SIZE); 16013575fc4SDoug Rabson assert(segs[1]->p_align >= PAGE_SIZE); 1613124c3e0SJohn Polstra 1623124c3e0SJohn Polstra /* 1633124c3e0SJohn Polstra * Map the entire address space of the object, to stake out our 1643124c3e0SJohn Polstra * contiguous region, and to establish the base address for relocation. 1653124c3e0SJohn Polstra */ 1663124c3e0SJohn Polstra base_offset = trunc_page(segs[0]->p_offset); 1673124c3e0SJohn Polstra base_vaddr = trunc_page(segs[0]->p_vaddr); 1683124c3e0SJohn Polstra base_vlimit = round_page(segs[1]->p_vaddr + segs[1]->p_memsz); 1693124c3e0SJohn Polstra mapsize = base_vlimit - base_vaddr; 1703124c3e0SJohn Polstra base_addr = u.hdr.e_type == ET_EXEC ? (caddr_t) base_vaddr : NULL; 1713124c3e0SJohn Polstra 1723124c3e0SJohn Polstra mapbase = mmap(base_addr, mapsize, protflags(segs[0]->p_flags), 1733124c3e0SJohn Polstra MAP_PRIVATE, fd, base_offset); 1743124c3e0SJohn Polstra if (mapbase == (caddr_t) -1) { 1753124c3e0SJohn Polstra _rtld_error("mmap of entire address space failed: %s", 1763124c3e0SJohn Polstra strerror(errno)); 1773124c3e0SJohn Polstra return NULL; 1783124c3e0SJohn Polstra } 1793124c3e0SJohn Polstra if (base_addr != NULL && mapbase != base_addr) { 1803124c3e0SJohn Polstra _rtld_error("mmap returned wrong address: wanted %p, got %p", 1813124c3e0SJohn Polstra base_addr, mapbase); 1823124c3e0SJohn Polstra munmap(mapbase, mapsize); 1833124c3e0SJohn Polstra return NULL; 1843124c3e0SJohn Polstra } 1853124c3e0SJohn Polstra 1863124c3e0SJohn Polstra /* Overlay the data segment onto the proper region. */ 1873124c3e0SJohn Polstra data_offset = trunc_page(segs[1]->p_offset); 1883124c3e0SJohn Polstra data_vaddr = trunc_page(segs[1]->p_vaddr); 1893124c3e0SJohn Polstra data_vlimit = round_page(segs[1]->p_vaddr + segs[1]->p_filesz); 1903124c3e0SJohn Polstra data_addr = mapbase + (data_vaddr - base_vaddr); 1913124c3e0SJohn Polstra if (mmap(data_addr, data_vlimit - data_vaddr, protflags(segs[1]->p_flags), 1923124c3e0SJohn Polstra MAP_PRIVATE|MAP_FIXED, fd, data_offset) == (caddr_t) -1) { 1933124c3e0SJohn Polstra _rtld_error("mmap of data failed: %s", strerror(errno)); 1943124c3e0SJohn Polstra return NULL; 1953124c3e0SJohn Polstra } 1963124c3e0SJohn Polstra 1973124c3e0SJohn Polstra /* Clear any BSS in the last page of the data segment. */ 1983124c3e0SJohn Polstra clear_vaddr = segs[1]->p_vaddr + segs[1]->p_filesz; 1993124c3e0SJohn Polstra clear_addr = mapbase + (clear_vaddr - base_vaddr); 2003124c3e0SJohn Polstra if ((nclear = data_vlimit - clear_vaddr) > 0) 2013124c3e0SJohn Polstra memset(clear_addr, 0, nclear); 2023124c3e0SJohn Polstra 2033124c3e0SJohn Polstra /* Overlay the BSS segment onto the proper region. */ 2043124c3e0SJohn Polstra bss_vaddr = data_vlimit; 2053124c3e0SJohn Polstra bss_vlimit = round_page(segs[1]->p_vaddr + segs[1]->p_memsz); 2063124c3e0SJohn Polstra bss_addr = mapbase + (bss_vaddr - base_vaddr); 2073124c3e0SJohn Polstra if (bss_vlimit > bss_vaddr) { /* There is something to do */ 2083124c3e0SJohn Polstra if (mmap(bss_addr, bss_vlimit - bss_vaddr, protflags(segs[1]->p_flags), 2093124c3e0SJohn Polstra MAP_PRIVATE|MAP_FIXED|MAP_ANON, -1, 0) == (caddr_t) -1) { 2103124c3e0SJohn Polstra _rtld_error("mmap of bss failed: %s", strerror(errno)); 2113124c3e0SJohn Polstra return NULL; 2123124c3e0SJohn Polstra } 2133124c3e0SJohn Polstra } 2143124c3e0SJohn Polstra 2153124c3e0SJohn Polstra obj = CNEW(Obj_Entry); 2163124c3e0SJohn Polstra obj->mapbase = mapbase; 2173124c3e0SJohn Polstra obj->mapsize = mapsize; 2183124c3e0SJohn Polstra obj->textsize = round_page(segs[0]->p_vaddr + segs[0]->p_memsz) - 2193124c3e0SJohn Polstra base_vaddr; 2203124c3e0SJohn Polstra obj->vaddrbase = base_vaddr; 2213124c3e0SJohn Polstra obj->relocbase = mapbase - base_vaddr; 22213575fc4SDoug Rabson obj->dynamic = (const Elf_Dyn *) 2233124c3e0SJohn Polstra (mapbase + (phdyn->p_vaddr - base_vaddr)); 2243124c3e0SJohn Polstra if (u.hdr.e_entry != 0) 2253124c3e0SJohn Polstra obj->entry = (caddr_t) (mapbase + (u.hdr.e_entry - base_vaddr)); 2263124c3e0SJohn Polstra if (phphdr != NULL) { 22713575fc4SDoug Rabson obj->phdr = (const Elf_Phdr *) 2283124c3e0SJohn Polstra (mapbase + (phphdr->p_vaddr - base_vaddr)); 2293124c3e0SJohn Polstra obj->phsize = phphdr->p_memsz; 2303124c3e0SJohn Polstra } 2313124c3e0SJohn Polstra 2323124c3e0SJohn Polstra return obj; 2333124c3e0SJohn Polstra } 2343124c3e0SJohn Polstra 2353124c3e0SJohn Polstra /* 2363124c3e0SJohn Polstra * Given a set of ELF protection flags, return the corresponding protection 2373124c3e0SJohn Polstra * flags for MMAP. 2383124c3e0SJohn Polstra */ 2393124c3e0SJohn Polstra static int 2403124c3e0SJohn Polstra protflags(int elfflags) 2413124c3e0SJohn Polstra { 2423124c3e0SJohn Polstra int prot = 0; 2433124c3e0SJohn Polstra if (elfflags & PF_R) 2443124c3e0SJohn Polstra prot |= PROT_READ; 2453124c3e0SJohn Polstra if (elfflags & PF_W) 2463124c3e0SJohn Polstra prot |= PROT_WRITE; 2473124c3e0SJohn Polstra if (elfflags & PF_X) 2483124c3e0SJohn Polstra prot |= PROT_EXEC; 2493124c3e0SJohn Polstra return prot; 2503124c3e0SJohn Polstra } 251