| Commit | Line | Data |
|---|---|---|
| 984263bc MD |
1 | /*- |
| 2 | * Copyright 1996-1998 John D. Polstra. | |
| 3 | * All rights reserved. | |
| 4 | * | |
| 5 | * Redistribution and use in source and binary forms, with or without | |
| 6 | * modification, are permitted provided that the following conditions | |
| 7 | * are met: | |
| 8 | * 1. Redistributions of source code must retain the above copyright | |
| 9 | * notice, this list of conditions and the following disclaimer. | |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 11 | * notice, this list of conditions and the following disclaimer in the | |
| 12 | * documentation and/or other materials provided with the distribution. | |
| 13 | * | |
| 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
| 15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
| 16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
| 17 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
| 18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
| 19 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 20 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 21 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
| 23 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 24 | * | |
| fcf53d9b | 25 | * $FreeBSD: src/libexec/rtld-elf/map_object.c,v 1.25 2011/01/25 21:12:31 kib Exp $ |
| 984263bc MD |
26 | */ |
| 27 | ||
| 28 | #include <sys/param.h> | |
| 29 | #include <sys/mman.h> | |
| 30 | #include <sys/stat.h> | |
| 31 | ||
| 32 | #include <errno.h> | |
| 33 | #include <stddef.h> | |
| 34 | #include <stdlib.h> | |
| 35 | #include <string.h> | |
| 36 | #include <unistd.h> | |
| 37 | ||
| fcf53d9b | 38 | #include "debug.h" |
| 984263bc MD |
39 | #include "rtld.h" |
| 40 | ||
| 1c76efe5 | 41 | static Elf_Ehdr *get_elf_header (int, const char *); |
| 984263bc MD |
42 | static int convert_prot(int); /* Elf flags -> mmap protection */ |
| 43 | static int convert_flags(int); /* Elf flags -> mmap flags */ | |
| 44 | ||
| 45 | /* | |
| 46 | * Map a shared object into memory. The "fd" argument is a file descriptor, | |
| 47 | * which must be open on the object and positioned at its beginning. | |
| 48 | * The "path" argument is a pathname that is used only for error messages. | |
| 49 | * | |
| 50 | * The return value is a pointer to a newly-allocated Obj_Entry structure | |
| 51 | * for the shared object. Returns NULL on failure. | |
| 52 | */ | |
| 53 | Obj_Entry * | |
| 54 | map_object(int fd, const char *path, const struct stat *sb) | |
| 55 | { | |
| 56 | Obj_Entry *obj; | |
| 1c76efe5 MD |
57 | Elf_Ehdr *hdr; |
| 58 | int i; | |
| 984263bc MD |
59 | Elf_Phdr *phdr; |
| 60 | Elf_Phdr *phlimit; | |
| 61 | Elf_Phdr **segs; | |
| 62 | int nsegs; | |
| 63 | Elf_Phdr *phdyn; | |
| 984263bc | 64 | Elf_Phdr *phinterp; |
| 55b88cae | 65 | Elf_Phdr *phtls; |
| 984263bc MD |
66 | caddr_t mapbase; |
| 67 | size_t mapsize; | |
| 68 | Elf_Off base_offset; | |
| 69 | Elf_Addr base_vaddr; | |
| 70 | Elf_Addr base_vlimit; | |
| 71 | caddr_t base_addr; | |
| 72 | Elf_Off data_offset; | |
| 73 | Elf_Addr data_vaddr; | |
| 74 | Elf_Addr data_vlimit; | |
| 75 | caddr_t data_addr; | |
| 76 | int data_prot; | |
| 77 | int data_flags; | |
| 78 | Elf_Addr clear_vaddr; | |
| 79 | caddr_t clear_addr; | |
| 80 | caddr_t clear_page; | |
| fcf53d9b JM |
81 | Elf_Addr phdr_vaddr; |
| 82 | size_t nclear, phsize; | |
| 984263bc MD |
83 | Elf_Addr bss_vaddr; |
| 84 | Elf_Addr bss_vlimit; | |
| 85 | caddr_t bss_addr; | |
| 007f494e JM |
86 | Elf_Addr relro_page; |
| 87 | size_t relro_size; | |
| 984263bc | 88 | |
| 1c76efe5 MD |
89 | hdr = get_elf_header(fd, path); |
| 90 | if (hdr == NULL) | |
| fcf53d9b | 91 | return (NULL); |
| 984263bc MD |
92 | |
| 93 | /* | |
| 94 | * Scan the program header entries, and save key information. | |
| 95 | * | |
| fcf53d9b | 96 | * We expect that the loadable segments are ordered by load address. |
| 984263bc | 97 | */ |
| 1c76efe5 | 98 | phdr = (Elf_Phdr *) ((char *)hdr + hdr->e_phoff); |
| fcf53d9b | 99 | phsize = hdr->e_phnum * sizeof (phdr[0]); |
| 1c76efe5 | 100 | phlimit = phdr + hdr->e_phnum; |
| 984263bc | 101 | nsegs = -1; |
| fcf53d9b JM |
102 | phdyn = phinterp = phtls = NULL; |
| 103 | phdr_vaddr = 0; | |
| 007f494e JM |
104 | relro_page = 0; |
| 105 | relro_size = 0; | |
| 1c76efe5 | 106 | segs = alloca(sizeof(segs[0]) * hdr->e_phnum); |
| 984263bc MD |
107 | while (phdr < phlimit) { |
| 108 | switch (phdr->p_type) { | |
| 109 | ||
| 110 | case PT_INTERP: | |
| 111 | phinterp = phdr; | |
| 112 | break; | |
| 113 | ||
| 114 | case PT_LOAD: | |
| 115 | segs[++nsegs] = phdr; | |
| fcf53d9b | 116 | if ((segs[nsegs]->p_align & (PAGE_SIZE - 1)) != 0) { |
| 984263bc MD |
117 | _rtld_error("%s: PT_LOAD segment %d not page-aligned", |
| 118 | path, nsegs); | |
| 119 | return NULL; | |
| 120 | } | |
| 121 | break; | |
| 122 | ||
| 123 | case PT_PHDR: | |
| fcf53d9b JM |
124 | phdr_vaddr = phdr->p_vaddr; |
| 125 | phsize = phdr->p_memsz; | |
| 984263bc MD |
126 | break; |
| 127 | ||
| 128 | case PT_DYNAMIC: | |
| 129 | phdyn = phdr; | |
| 130 | break; | |
| 139b8f34 | 131 | |
| 55b88cae DX |
132 | case PT_TLS: |
| 133 | phtls = phdr; | |
| 134 | break; | |
| 007f494e JM |
135 | |
| 136 | case PT_GNU_RELRO: | |
| 137 | relro_page = phdr->p_vaddr; | |
| 138 | relro_size = phdr->p_memsz; | |
| 139 | break; | |
| 984263bc MD |
140 | } |
| 141 | ||
| 142 | ++phdr; | |
| 143 | } | |
| 144 | if (phdyn == NULL) { | |
| 145 | _rtld_error("%s: object is not dynamically-linked", path); | |
| 146 | return NULL; | |
| 147 | } | |
| 148 | ||
| 149 | if (nsegs < 0) { | |
| 150 | _rtld_error("%s: too few PT_LOAD segments", path); | |
| 151 | return NULL; | |
| 152 | } | |
| 153 | ||
| 154 | /* | |
| 155 | * Map the entire address space of the object, to stake out our | |
| 156 | * contiguous region, and to establish the base address for relocation. | |
| 157 | */ | |
| 158 | base_offset = trunc_page(segs[0]->p_offset); | |
| 159 | base_vaddr = trunc_page(segs[0]->p_vaddr); | |
| 160 | base_vlimit = round_page(segs[nsegs]->p_vaddr + segs[nsegs]->p_memsz); | |
| 161 | mapsize = base_vlimit - base_vaddr; | |
| 1c76efe5 | 162 | base_addr = hdr->e_type == ET_EXEC ? (caddr_t) base_vaddr : NULL; |
| 984263bc | 163 | |
| fcf53d9b JM |
164 | mapbase = mmap(base_addr, mapsize, PROT_NONE, MAP_ANON | MAP_PRIVATE | |
| 165 | MAP_NOCORE, -1, 0); | |
| 984263bc MD |
166 | if (mapbase == (caddr_t) -1) { |
| 167 | _rtld_error("%s: mmap of entire address space failed: %s", | |
| 168 | path, strerror(errno)); | |
| 169 | return NULL; | |
| 170 | } | |
| 171 | if (base_addr != NULL && mapbase != base_addr) { | |
| 172 | _rtld_error("%s: mmap returned wrong address: wanted %p, got %p", | |
| 173 | path, base_addr, mapbase); | |
| 174 | munmap(mapbase, mapsize); | |
| 175 | return NULL; | |
| 176 | } | |
| 177 | ||
| fcf53d9b | 178 | for (i = 0; i <= nsegs; i++) { |
| 984263bc MD |
179 | /* Overlay the segment onto the proper region. */ |
| 180 | data_offset = trunc_page(segs[i]->p_offset); | |
| 181 | data_vaddr = trunc_page(segs[i]->p_vaddr); | |
| 182 | data_vlimit = round_page(segs[i]->p_vaddr + segs[i]->p_filesz); | |
| 183 | data_addr = mapbase + (data_vaddr - base_vaddr); | |
| 184 | data_prot = convert_prot(segs[i]->p_flags); | |
| 185 | data_flags = convert_flags(segs[i]->p_flags) | MAP_FIXED; | |
| fcf53d9b | 186 | if (mmap(data_addr, data_vlimit - data_vaddr, data_prot, |
| 984263bc MD |
187 | data_flags, fd, data_offset) == (caddr_t) -1) { |
| 188 | _rtld_error("%s: mmap of data failed: %s", path, strerror(errno)); | |
| 189 | return NULL; | |
| 190 | } | |
| 191 | ||
| fcf53d9b JM |
192 | /* Do BSS setup */ |
| 193 | if (segs[i]->p_filesz != segs[i]->p_memsz) { | |
| 194 | ||
| 195 | /* Clear any BSS in the last page of the segment. */ | |
| 196 | clear_vaddr = segs[i]->p_vaddr + segs[i]->p_filesz; | |
| 197 | clear_addr = mapbase + (clear_vaddr - base_vaddr); | |
| 198 | clear_page = mapbase + (trunc_page(clear_vaddr) - base_vaddr); | |
| 199 | ||
| 200 | if ((nclear = data_vlimit - clear_vaddr) > 0) { | |
| 201 | /* Make sure the end of the segment is writable */ | |
| 9b53619b JM |
202 | if ((data_prot & PROT_WRITE) == 0 && -1 == |
| 203 | mprotect(clear_page, PAGE_SIZE, data_prot|PROT_WRITE)) { | |
| 984263bc MD |
204 | _rtld_error("%s: mprotect failed: %s", path, |
| 205 | strerror(errno)); | |
| 206 | return NULL; | |
| 4b89341e | 207 | } |
| 984263bc | 208 | |
| fcf53d9b | 209 | memset(clear_addr, 0, nclear); |
| 984263bc | 210 | |
| fcf53d9b JM |
211 | /* |
| 212 | * reset the data protection back, enable the segment to be | |
| 213 | * coredumped since we modified it. | |
| 214 | */ | |
| 215 | if ((data_prot & PROT_WRITE) == 0) { | |
| 216 | madvise(clear_page, PAGE_SIZE, MADV_CORE); | |
| 217 | mprotect(clear_page, PAGE_SIZE, data_prot); | |
| 218 | } | |
| 4b89341e | 219 | } |
| 984263bc | 220 | |
| fcf53d9b JM |
221 | /* Overlay the BSS segment onto the proper region. */ |
| 222 | bss_vaddr = data_vlimit; | |
| 223 | bss_vlimit = round_page(segs[i]->p_vaddr + segs[i]->p_memsz); | |
| 224 | bss_addr = mapbase + (bss_vaddr - base_vaddr); | |
| 225 | if (bss_vlimit > bss_vaddr) { /* There is something to do */ | |
| 55071f75 JM |
226 | if (mmap(bss_addr, bss_vlimit - bss_vaddr, data_prot, |
| 227 | data_flags | MAP_ANON, -1, 0) == (caddr_t)-1) { | |
| 228 | _rtld_error("%s: mmap of bss failed: %s", path, | |
| 984263bc | 229 | strerror(errno)); |
| fcf53d9b JM |
230 | return NULL; |
| 231 | } | |
| 984263bc MD |
232 | } |
| 233 | } | |
| fcf53d9b JM |
234 | |
| 235 | if (phdr_vaddr == 0 && data_offset <= hdr->e_phoff && | |
| 236 | (data_vlimit - data_vaddr + data_offset) >= | |
| 237 | (hdr->e_phoff + hdr->e_phnum * sizeof (Elf_Phdr))) { | |
| 238 | phdr_vaddr = data_vaddr + hdr->e_phoff - data_offset; | |
| 239 | } | |
| 984263bc MD |
240 | } |
| 241 | ||
| 242 | obj = obj_new(); | |
| 243 | if (sb != NULL) { | |
| 244 | obj->dev = sb->st_dev; | |
| 245 | obj->ino = sb->st_ino; | |
| 246 | } | |
| 247 | obj->mapbase = mapbase; | |
| 248 | obj->mapsize = mapsize; | |
| 249 | obj->textsize = round_page(segs[0]->p_vaddr + segs[0]->p_memsz) - | |
| 250 | base_vaddr; | |
| 251 | obj->vaddrbase = base_vaddr; | |
| 252 | obj->relocbase = mapbase - base_vaddr; | |
| 253 | obj->dynamic = (const Elf_Dyn *) (obj->relocbase + phdyn->p_vaddr); | |
| 1c76efe5 MD |
254 | if (hdr->e_entry != 0) |
| 255 | obj->entry = (caddr_t) (obj->relocbase + hdr->e_entry); | |
| fcf53d9b JM |
256 | if (phdr_vaddr != 0) { |
| 257 | obj->phdr = (const Elf_Phdr *) (obj->relocbase + phdr_vaddr); | |
| 258 | } else { | |
| 259 | obj->phdr = malloc(phsize); | |
| 260 | if (obj->phdr == NULL) { | |
| 261 | obj_free(obj); | |
| 262 | _rtld_error("%s: cannot allocate program header", path); | |
| 263 | return NULL; | |
| 264 | } | |
| 265 | memcpy((char *)obj->phdr, (char *)hdr + hdr->e_phoff, phsize); | |
| 266 | obj->phdr_alloc = true; | |
| 984263bc | 267 | } |
| fcf53d9b | 268 | obj->phsize = phsize; |
| 984263bc MD |
269 | if (phinterp != NULL) |
| 270 | obj->interp = (const char *) (obj->relocbase + phinterp->p_vaddr); | |
| 55b88cae DX |
271 | if (phtls != NULL) { |
| 272 | tls_dtv_generation++; | |
| 273 | obj->tlsindex = ++tls_max_index; | |
| 274 | obj->tlssize = phtls->p_memsz; | |
| 275 | obj->tlsalign = phtls->p_align; | |
| 276 | obj->tlsinitsize = phtls->p_filesz; | |
| 277 | obj->tlsinit = mapbase + phtls->p_vaddr; | |
| 278 | } | |
| 007f494e JM |
279 | |
| 280 | if (relro_size) { | |
| 281 | obj->relro_page = obj->relocbase + trunc_page(relro_page); | |
| 282 | obj->relro_size = round_page(relro_size); | |
| 283 | } | |
| 984263bc MD |
284 | return obj; |
| 285 | } | |
| 286 | ||
| 1c76efe5 MD |
287 | static Elf_Ehdr * |
| 288 | get_elf_header (int fd, const char *path) | |
| 289 | { | |
| 290 | static union { | |
| 291 | Elf_Ehdr hdr; | |
| 292 | char buf[PAGE_SIZE]; | |
| 293 | } u; | |
| 294 | ssize_t nbytes; | |
| 295 | ||
| fcf53d9b | 296 | if ((nbytes = pread(fd, u.buf, PAGE_SIZE, 0)) == -1) { |
| 1c76efe5 MD |
297 | _rtld_error("%s: read error: %s", path, strerror(errno)); |
| 298 | return NULL; | |
| 299 | } | |
| 300 | ||
| 301 | /* Make sure the file is valid */ | |
| 302 | if (nbytes < (ssize_t)sizeof(Elf_Ehdr) || !IS_ELF(u.hdr)) { | |
| 303 | _rtld_error("%s: invalid file format", path); | |
| 304 | return NULL; | |
| 305 | } | |
| 306 | if (u.hdr.e_ident[EI_CLASS] != ELF_TARG_CLASS | |
| 307 | || u.hdr.e_ident[EI_DATA] != ELF_TARG_DATA) { | |
| 308 | _rtld_error("%s: unsupported file layout", path); | |
| 309 | return NULL; | |
| 310 | } | |
| 311 | if (u.hdr.e_ident[EI_VERSION] != EV_CURRENT | |
| 312 | || u.hdr.e_version != EV_CURRENT) { | |
| 313 | _rtld_error("%s: unsupported file version", path); | |
| 314 | return NULL; | |
| 315 | } | |
| 316 | if (u.hdr.e_type != ET_EXEC && u.hdr.e_type != ET_DYN) { | |
| 317 | _rtld_error("%s: unsupported file type", path); | |
| 318 | return NULL; | |
| 319 | } | |
| 320 | if (u.hdr.e_machine != ELF_TARG_MACH) { | |
| 321 | _rtld_error("%s: unsupported machine", path); | |
| 322 | return NULL; | |
| 323 | } | |
| 324 | ||
| 325 | /* | |
| 326 | * We rely on the program header being in the first page. This is | |
| 327 | * not strictly required by the ABI specification, but it seems to | |
| 328 | * always true in practice. And, it simplifies things considerably. | |
| 329 | */ | |
| 330 | if (u.hdr.e_phentsize != sizeof(Elf_Phdr)) { | |
| 331 | _rtld_error( | |
| 332 | "%s: invalid shared object: e_phentsize != sizeof(Elf_Phdr)", path); | |
| 333 | return NULL; | |
| 334 | } | |
| 335 | if (u.hdr.e_phoff + u.hdr.e_phnum * sizeof(Elf_Phdr) > (size_t)nbytes) { | |
| 336 | _rtld_error("%s: program header too large", path); | |
| 337 | return NULL; | |
| 338 | } | |
| 339 | ||
| 340 | return (&u.hdr); | |
| 341 | } | |
| 342 | ||
| 984263bc MD |
343 | void |
| 344 | obj_free(Obj_Entry *obj) | |
| 345 | { | |
| 346 | Objlist_Entry *elm; | |
| 347 | ||
| fcf53d9b | 348 | if (obj->tls_done) |
| 55b88cae | 349 | free_tls_offset(obj); |
| 984263bc MD |
350 | while (obj->needed != NULL) { |
| 351 | Needed_Entry *needed = obj->needed; | |
| 352 | obj->needed = needed->next; | |
| 353 | free(needed); | |
| 354 | } | |
| fcf53d9b JM |
355 | while (!STAILQ_EMPTY(&obj->names)) { |
| 356 | Name_Entry *entry = STAILQ_FIRST(&obj->names); | |
| 357 | STAILQ_REMOVE_HEAD(&obj->names, link); | |
| 358 | free(entry); | |
| 359 | } | |
| 984263bc MD |
360 | while (!STAILQ_EMPTY(&obj->dldags)) { |
| 361 | elm = STAILQ_FIRST(&obj->dldags); | |
| 362 | STAILQ_REMOVE_HEAD(&obj->dldags, link); | |
| 363 | free(elm); | |
| 364 | } | |
| 365 | while (!STAILQ_EMPTY(&obj->dagmembers)) { | |
| 366 | elm = STAILQ_FIRST(&obj->dagmembers); | |
| 367 | STAILQ_REMOVE_HEAD(&obj->dagmembers, link); | |
| 368 | free(elm); | |
| 369 | } | |
| fcf53d9b JM |
370 | if (obj->vertab) |
| 371 | free(obj->vertab); | |
| 372 | if (obj->origin_path) | |
| 373 | free(obj->origin_path); | |
| 374 | if (obj->z_origin) | |
| 375 | free(obj->rpath); | |
| 376 | if (obj->priv) | |
| 377 | free(obj->priv); | |
| 378 | if (obj->path) | |
| 379 | free(obj->path); | |
| 380 | if (obj->phdr_alloc) | |
| 381 | free((void *)obj->phdr); | |
| 984263bc MD |
382 | free(obj); |
| 383 | } | |
| 384 | ||
| 385 | Obj_Entry * | |
| 386 | obj_new(void) | |
| 387 | { | |
| 388 | Obj_Entry *obj; | |
| 389 | ||
| 390 | obj = CNEW(Obj_Entry); | |
| 391 | STAILQ_INIT(&obj->dldags); | |
| 392 | STAILQ_INIT(&obj->dagmembers); | |
| fcf53d9b | 393 | STAILQ_INIT(&obj->names); |
| 984263bc MD |
394 | return obj; |
| 395 | } | |
| 396 | ||
| 397 | /* | |
| 398 | * Given a set of ELF protection flags, return the corresponding protection | |
| 399 | * flags for MMAP. | |
| 400 | */ | |
| 401 | static int | |
| 402 | convert_prot(int elfflags) | |
| 403 | { | |
| 404 | int prot = 0; | |
| 405 | if (elfflags & PF_R) | |
| 406 | prot |= PROT_READ; | |
| 407 | if (elfflags & PF_W) | |
| 408 | prot |= PROT_WRITE; | |
| 409 | if (elfflags & PF_X) | |
| 410 | prot |= PROT_EXEC; | |
| 411 | return prot; | |
| 412 | } | |
| 413 | ||
| 414 | static int | |
| 415 | convert_flags(int elfflags) | |
| 416 | { | |
| 417 | int flags = MAP_PRIVATE; /* All mappings are private */ | |
| 418 | ||
| 419 | /* | |
| 420 | * Readonly mappings are marked "MAP_NOCORE", because they can be | |
| 421 | * reconstructed by a debugger. | |
| 422 | */ | |
| 423 | if (!(elfflags & PF_W)) | |
| 424 | flags |= MAP_NOCORE; | |
| 425 | return flags; | |
| 426 | } |