Home | History | Annotate | Line # | Download | only in ld.elf_so
map_object.c revision 1.2
      1 /*	$NetBSD: map_object.c,v 1.2 1996/12/17 03:42:44 jonathan 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 #ifdef __mips__
    149 	    /* NetBSD/pmax 1.1 elf toolchain peculiarity */
    150 	    if (nsegs >= 2) {
    151 		    _rtld_error("%s: too many sections\n", path);
    152 		    return NULL;
    153 	    }
    154 #endif
    155 	    assert(nsegs < 2);
    156 	    segs[nsegs] = phdr;
    157 	    ++nsegs;
    158 	    break;
    159 
    160 	case Elf_pt_phdr:
    161 	    phphdr = phdr;
    162 	    break;
    163 
    164 	case Elf_pt_dynamic:
    165 	    phdyn = phdr;
    166 	    break;
    167 	}
    168 
    169 	++phdr;
    170     }
    171     if (phdyn == NULL) {
    172 	_rtld_error("%s: not dynamically-linked", path);
    173 	return NULL;
    174     }
    175 
    176     assert(nsegs == 2);
    177 #ifdef __i386__
    178     assert(segs[0]->p_align <= PAGESIZE);
    179     assert(segs[1]->p_align <= PAGESIZE);
    180 #endif
    181 
    182     /*
    183      * Map the entire address space of the object, to stake out our
    184      * contiguous region, and to establish the base address for relocation.
    185      */
    186     base_offset = round_down(segs[0]->p_offset);
    187     base_vaddr = round_down(segs[0]->p_vaddr);
    188     base_vlimit = round_up(segs[1]->p_vaddr + segs[1]->p_memsz);
    189     mapsize = base_vlimit - base_vaddr;
    190 #ifdef RTLD_LOADER
    191     base_addr = u.hdr.e_type == Elf_et_exec ? (caddr_t) base_vaddr : NULL;
    192 #else
    193     base_addr = NULL;
    194 #endif
    195 
    196     mapbase = mmap(base_addr, mapsize, protflags(segs[0]->p_flags),
    197 	MAP_PRIVATE, fd, base_offset);
    198     if (mapbase == (caddr_t) -1) {
    199 	_rtld_error("mmap of entire address space failed: %s", xstrerror(errno));
    200 	return NULL;
    201     }
    202     if (base_addr != NULL && mapbase != base_addr) {
    203 	_rtld_error("mmap returned wrong address: wanted %p, got %p", base_addr,
    204 	    mapbase);
    205 	munmap(mapbase, mapsize);
    206 	return NULL;
    207     }
    208 
    209     /* Overlay the data segment onto the proper region. */
    210     data_offset = round_down(segs[1]->p_offset);
    211     data_vaddr = round_down(segs[1]->p_vaddr);
    212     data_vlimit = round_up(segs[1]->p_vaddr + segs[1]->p_filesz);
    213     data_addr = mapbase + (data_vaddr - base_vaddr);
    214     if (mmap(data_addr, data_vlimit - data_vaddr, protflags(segs[1]->p_flags),
    215 	    MAP_PRIVATE|MAP_FIXED, fd, data_offset) == (caddr_t) -1) {
    216 	_rtld_error("mmap of data failed: %s", xstrerror(errno));
    217 	return NULL;
    218     }
    219 
    220 #ifdef RTLD_LOADER
    221     /* Clear any BSS in the last page of the data segment. */
    222     clear_vaddr = segs[1]->p_vaddr + segs[1]->p_filesz;
    223     clear_addr = mapbase + (clear_vaddr - base_vaddr);
    224     if ((nclear = data_vlimit - clear_vaddr) > 0)
    225 	memset(clear_addr, 0, nclear);
    226 
    227     /* Overlay the BSS segment onto the proper region. */
    228     bss_vaddr = data_vlimit;
    229     bss_vlimit = round_up(segs[1]->p_vaddr + segs[1]->p_memsz);
    230     bss_addr = mapbase +  (bss_vaddr - base_vaddr);
    231     if (bss_vlimit > bss_vaddr) {	/* There is something to do */
    232 	if (mmap(bss_addr, bss_vlimit - bss_vaddr, protflags(segs[1]->p_flags),
    233 		MAP_PRIVATE|MAP_FIXED|MAP_ANON, -1, 0) == (caddr_t) -1) {
    234 	    _rtld_error("mmap of bss failed: %s", xstrerror(errno));
    235 	    return NULL;
    236 	}
    237     }
    238 #endif
    239 
    240     obj = CNEW(Obj_Entry);
    241     obj->mapbase = mapbase;
    242     obj->mapsize = mapsize;
    243     obj->textsize = round_up(segs[0]->p_vaddr + segs[0]->p_memsz) - base_vaddr;
    244     obj->vaddrbase = base_vaddr;
    245     obj->relocbase = mapbase - base_vaddr;
    246     obj->dynamic = (Elf_Dyn *) (obj->relocbase + phdyn->p_vaddr);
    247     if (u.hdr.e_entry != 0)
    248 	obj->entry = (caddr_t) (obj->relocbase + u.hdr.e_entry);
    249     if (phphdr != NULL) {
    250 	obj->phdr = (const Elf_Phdr *) (obj->relocbase + phphdr->p_vaddr);
    251 	obj->phsize = phphdr->p_memsz;
    252     }
    253 
    254     return obj;
    255 }
    256 
    257 /*
    258  * Given a set of ELF protection flags, return the corresponding protection
    259  * flags for MMAP.
    260  */
    261 static int
    262 protflags(int elfflags)
    263 {
    264     int prot = 0;
    265     if (elfflags & Elf_pf_r) prot |= PROT_READ;
    266 #ifdef RTLD_LOADER
    267     if (elfflags & Elf_pf_w) prot |= PROT_WRITE;
    268 #endif
    269     if (elfflags & Elf_pf_x) prot |= PROT_EXEC;
    270     return prot;
    271 }
    272