Home | History | Annotate | Line # | Download | only in ld.elf_so
map_object.c revision 1.1
      1 /*	$NetBSD: map_object.c,v 1.1 1996/12/16 20:38:01 cgd Exp $	*/
      2 
      3 /*
      4  * Copyright 1996 John D. Polstra.
      5  * Copyright 1996 Matt Thomas <matt (at) 3am-software.com>
      6  * All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  * 1. Redistributions of source code must retain the above copyright
     12  *    notice, this list of conditions and the following disclaimer.
     13  * 2. Redistributions in binary form must reproduce the above copyright
     14  *    notice, this list of conditions and the following disclaimer in the
     15  *    documentation and/or other materials provided with the distribution.
     16  * 3. All advertising materials mentioning features or use of this software
     17  *    must display the following acknowledgement:
     18  *      This product includes software developed by John Polstra.
     19  * 4. The name of the author may not be used to endorse or promote products
     20  *    derived from this software without specific prior written permission.
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     27  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     31  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     32  */
     33 
     34 #include <errno.h>
     35 #include <stddef.h>
     36 #include <string.h>
     37 #include <unistd.h>
     38 #include <sys/types.h>
     39 #include <sys/mman.h>
     40 
     41 #include "rtld.h"
     42 
     43 #define CONCAT(x,y)     __CONCAT(x,y)
     44 #define ELFNAME(x)      CONCAT(elf,CONCAT(ELFSIZE,CONCAT(_,x)))
     45 #define ELFNAME2(x,y)   CONCAT(x,CONCAT(_elf,CONCAT(ELFSIZE,CONCAT(_,y))))
     46 #define ELFNAMEEND(x)   CONCAT(x,CONCAT(_elf,ELFSIZE))
     47 #define ELFDEFNNAME(x)  CONCAT(ELF,CONCAT(ELFSIZE,CONCAT(_,x)))
     48 
     49 static int protflags(int);	/* Elf flags -> mmap protection */
     50 
     51 /*
     52  * Map a shared object into memory.  The argument is a file descriptor,
     53  * which must be open on the object and positioned at its beginning.
     54  *
     55  * The return value is a pointer to a newly-allocated Obj_Entry structure
     56  * for the shared object.  Returns NULL on failure.
     57  */
     58 Obj_Entry *
     59 _rtld_map_object(
     60     const char *path,
     61     int fd)
     62 {
     63     Obj_Entry *obj;
     64     union {
     65 	Elf_Ehdr hdr;
     66 	char buf[PAGESIZE];
     67     } u;
     68     int nbytes;
     69     Elf_Phdr *phdr;
     70     Elf_Phdr *phlimit;
     71     Elf_Phdr *segs[2];
     72     int nsegs;
     73     Elf_Phdr *phdyn;
     74     Elf_Phdr *phphdr;
     75     caddr_t mapbase;
     76     size_t mapsize;
     77     Elf_Off base_offset;
     78     Elf_Addr base_vaddr;
     79     Elf_Addr base_vlimit;
     80     caddr_t base_addr;
     81     Elf_Off data_offset;
     82     Elf_Addr data_vaddr;
     83     Elf_Addr data_vlimit;
     84     caddr_t data_addr;
     85 #ifdef RTLD_LOADER
     86     Elf_Addr clear_vaddr;
     87     caddr_t clear_addr;
     88     size_t nclear;
     89     Elf_Addr bss_vaddr;
     90     Elf_Addr bss_vlimit;
     91     caddr_t bss_addr;
     92 #endif
     93 
     94     if ((nbytes = read(fd, u.buf, PAGESIZE)) == -1) {
     95 	_rtld_error("%s: read error: %s", path, xstrerror(errno));
     96 	return NULL;
     97     }
     98 
     99     /* Make sure the file is valid */
    100     if (nbytes < sizeof(Elf_Ehdr)
    101        || memcmp(Elf_e_ident, u.hdr.e_ident, Elf_e_siz) != 0) {
    102 	_rtld_error("%s: unrecognized file format", path);
    103 	return NULL;
    104     }
    105     /* Elf_e_ident includes class */
    106     if (u.hdr.e_ident[Elf_ei_version] != Elf_ev_current
    107     || u.hdr.e_version != Elf_ev_current
    108     || u.hdr.e_ident[Elf_ei_data] != ELFDEFNNAME(MACHDEP_ENDIANNESS)) {
    109 	_rtld_error("%s: Unsupported file version", path);
    110 	return NULL;
    111     }
    112     if (u.hdr.e_type != Elf_et_exec && u.hdr.e_type != Elf_et_dyn) {
    113 	_rtld_error("%s: Unsupported file type", path);
    114 	return NULL;
    115     }
    116     switch (u.hdr.e_machine) {
    117     ELFDEFNNAME(MACHDEP_ID_CASES)
    118 
    119     default:
    120 	_rtld_error("%s: Unsupported machine", path);
    121 	return NULL;
    122     }
    123 
    124     /*
    125      * We rely on the program header being in the first page.  This is
    126      * not strictly required by the ABI specification, but it seems to
    127      * always true in practice.  And, it simplifies things considerably.
    128      */
    129     assert(u.hdr.e_phentsize == sizeof(Elf_Phdr));
    130     assert(u.hdr.e_phoff + u.hdr.e_phnum*sizeof(Elf_Phdr) <= PAGESIZE);
    131     assert(u.hdr.e_phoff + u.hdr.e_phnum*sizeof(Elf_Phdr) <= nbytes);
    132 
    133     /*
    134      * Scan the program header entries, and save key information.
    135      *
    136      * We rely on there being exactly two load segments, text and data,
    137      * in that order.
    138      */
    139     phdr = (Elf_Phdr *) (u.buf + u.hdr.e_phoff);
    140     phlimit = phdr + u.hdr.e_phnum;
    141     nsegs = 0;
    142     phdyn = NULL;
    143     phphdr = NULL;
    144     while(phdr < phlimit) {
    145 	switch(phdr->p_type) {
    146 
    147 	case Elf_pt_load:
    148 	    assert(nsegs < 2);
    149 	    segs[nsegs] = phdr;
    150 	    ++nsegs;
    151 	    break;
    152 
    153 	case Elf_pt_phdr:
    154 	    phphdr = phdr;
    155 	    break;
    156 
    157 	case Elf_pt_dynamic:
    158 	    phdyn = phdr;
    159 	    break;
    160 	}
    161 
    162 	++phdr;
    163     }
    164     if (phdyn == NULL) {
    165 	_rtld_error("%s: not dynamically-linked", path);
    166 	return NULL;
    167     }
    168 
    169     assert(nsegs == 2);
    170 #ifdef __i386__
    171     assert(segs[0]->p_align <= PAGESIZE);
    172     assert(segs[1]->p_align <= PAGESIZE);
    173 #endif
    174 
    175     /*
    176      * Map the entire address space of the object, to stake out our
    177      * contiguous region, and to establish the base address for relocation.
    178      */
    179     base_offset = round_down(segs[0]->p_offset);
    180     base_vaddr = round_down(segs[0]->p_vaddr);
    181     base_vlimit = round_up(segs[1]->p_vaddr + segs[1]->p_memsz);
    182     mapsize = base_vlimit - base_vaddr;
    183 #ifdef RTLD_LOADER
    184     base_addr = u.hdr.e_type == Elf_et_exec ? (caddr_t) base_vaddr : NULL;
    185 #else
    186     base_addr = NULL;
    187 #endif
    188 
    189     mapbase = mmap(base_addr, mapsize, protflags(segs[0]->p_flags),
    190 	MAP_PRIVATE, fd, base_offset);
    191     if (mapbase == (caddr_t) -1) {
    192 	_rtld_error("mmap of entire address space failed: %s", xstrerror(errno));
    193 	return NULL;
    194     }
    195     if (base_addr != NULL && mapbase != base_addr) {
    196 	_rtld_error("mmap returned wrong address: wanted %p, got %p", base_addr,
    197 	    mapbase);
    198 	munmap(mapbase, mapsize);
    199 	return NULL;
    200     }
    201 
    202     /* Overlay the data segment onto the proper region. */
    203     data_offset = round_down(segs[1]->p_offset);
    204     data_vaddr = round_down(segs[1]->p_vaddr);
    205     data_vlimit = round_up(segs[1]->p_vaddr + segs[1]->p_filesz);
    206     data_addr = mapbase + (data_vaddr - base_vaddr);
    207     if (mmap(data_addr, data_vlimit - data_vaddr, protflags(segs[1]->p_flags),
    208 	    MAP_PRIVATE|MAP_FIXED, fd, data_offset) == (caddr_t) -1) {
    209 	_rtld_error("mmap of data failed: %s", xstrerror(errno));
    210 	return NULL;
    211     }
    212 
    213 #ifdef RTLD_LOADER
    214     /* Clear any BSS in the last page of the data segment. */
    215     clear_vaddr = segs[1]->p_vaddr + segs[1]->p_filesz;
    216     clear_addr = mapbase + (clear_vaddr - base_vaddr);
    217     if ((nclear = data_vlimit - clear_vaddr) > 0)
    218 	memset(clear_addr, 0, nclear);
    219 
    220     /* Overlay the BSS segment onto the proper region. */
    221     bss_vaddr = data_vlimit;
    222     bss_vlimit = round_up(segs[1]->p_vaddr + segs[1]->p_memsz);
    223     bss_addr = mapbase +  (bss_vaddr - base_vaddr);
    224     if (bss_vlimit > bss_vaddr) {	/* There is something to do */
    225 	if (mmap(bss_addr, bss_vlimit - bss_vaddr, protflags(segs[1]->p_flags),
    226 		MAP_PRIVATE|MAP_FIXED|MAP_ANON, -1, 0) == (caddr_t) -1) {
    227 	    _rtld_error("mmap of bss failed: %s", xstrerror(errno));
    228 	    return NULL;
    229 	}
    230     }
    231 #endif
    232 
    233     obj = CNEW(Obj_Entry);
    234     obj->mapbase = mapbase;
    235     obj->mapsize = mapsize;
    236     obj->textsize = round_up(segs[0]->p_vaddr + segs[0]->p_memsz) - base_vaddr;
    237     obj->vaddrbase = base_vaddr;
    238     obj->relocbase = mapbase - base_vaddr;
    239     obj->dynamic = (Elf_Dyn *) (obj->relocbase + phdyn->p_vaddr);
    240     if (u.hdr.e_entry != 0)
    241 	obj->entry = (caddr_t) (obj->relocbase + u.hdr.e_entry);
    242     if (phphdr != NULL) {
    243 	obj->phdr = (const Elf_Phdr *) (obj->relocbase + phphdr->p_vaddr);
    244 	obj->phsize = phphdr->p_memsz;
    245     }
    246 
    247     return obj;
    248 }
    249 
    250 /*
    251  * Given a set of ELF protection flags, return the corresponding protection
    252  * flags for MMAP.
    253  */
    254 static int
    255 protflags(int elfflags)
    256 {
    257     int prot = 0;
    258     if (elfflags & Elf_pf_r) prot |= PROT_READ;
    259 #ifdef RTLD_LOADER
    260     if (elfflags & Elf_pf_w) prot |= PROT_WRITE;
    261 #endif
    262     if (elfflags & Elf_pf_x) prot |= PROT_EXEC;
    263     return prot;
    264 }
    265