Changeset View
Standalone View
libexec/rtld-elf/map_object.c
Show All 34 Lines | |||||
#include <stddef.h> | #include <stddef.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <unistd.h> | #include <unistd.h> | ||||
#include "debug.h" | #include "debug.h" | ||||
#include "rtld.h" | #include "rtld.h" | ||||
static Elf_Ehdr *get_elf_header(int, const char *, const struct stat *); | static Elf_Ehdr *get_elf_header(int, const char *, const struct stat *, | ||||
Elf_Phdr **phdr); | |||||
static int convert_flags(int); /* Elf flags -> mmap flags */ | static int convert_flags(int); /* Elf flags -> mmap flags */ | ||||
int __getosreldate(void); | int __getosreldate(void); | ||||
static bool | |||||
phdr_in_zero_page(const Elf_Ehdr *hdr) | |||||
{ | |||||
return (hdr->e_phoff + hdr->e_phnum * sizeof(Elf_Phdr) < | |||||
(size_t)PAGE_SIZE); | |||||
} | |||||
/* | /* | ||||
* Map a shared object into memory. The "fd" argument is a file descriptor, | * Map a shared object into memory. The "fd" argument is a file descriptor, | ||||
* which must be open on the object and positioned at its beginning. | * which must be open on the object and positioned at its beginning. | ||||
* The "path" argument is a pathname that is used only for error messages. | * The "path" argument is a pathname that is used only for error messages. | ||||
* | * | ||||
* The return value is a pointer to a newly-allocated Obj_Entry structure | * The return value is a pointer to a newly-allocated Obj_Entry structure | ||||
* for the shared object. Returns NULL on failure. | * for the shared object. Returns NULL on failure. | ||||
*/ | */ | ||||
Show All 34 Lines | map_object(int fd, const char *path, const struct stat *sb) | ||||
Elf_Addr relro_page; | Elf_Addr relro_page; | ||||
size_t relro_size; | size_t relro_size; | ||||
Elf_Addr note_start; | Elf_Addr note_start; | ||||
Elf_Addr note_end; | Elf_Addr note_end; | ||||
char *note_map; | char *note_map; | ||||
size_t note_map_len; | size_t note_map_len; | ||||
Elf_Addr text_end; | Elf_Addr text_end; | ||||
hdr = get_elf_header(fd, path, sb); | hdr = get_elf_header(fd, path, sb, &phdr); | ||||
if (hdr == NULL) | if (hdr == NULL) | ||||
return (NULL); | return (NULL); | ||||
/* | /* | ||||
* Scan the program header entries, and save key information. | * Scan the program header entries, and save key information. | ||||
* | |||||
* We expect that the loadable segments are ordered by load address. | * We expect that the loadable segments are ordered by load address. | ||||
*/ | */ | ||||
phdr = (Elf_Phdr *)((char *)hdr + hdr->e_phoff); | |||||
phsize = hdr->e_phnum * sizeof (phdr[0]); | phsize = hdr->e_phnum * sizeof(phdr[0]); | ||||
phlimit = phdr + hdr->e_phnum; | phlimit = phdr + hdr->e_phnum; | ||||
nsegs = -1; | nsegs = -1; | ||||
phdyn = phinterp = phtls = NULL; | phdyn = phinterp = phtls = NULL; | ||||
phdr_vaddr = 0; | phdr_vaddr = 0; | ||||
relro_page = 0; | relro_page = 0; | ||||
relro_size = 0; | relro_size = 0; | ||||
note_start = 0; | note_start = 0; | ||||
note_end = 0; | note_end = 0; | ||||
▲ Show 20 Lines • Show All 210 Lines • ▼ Show 20 Lines | munmap(note_map, note_map_len); | ||||
munmap(hdr, PAGE_SIZE); | munmap(hdr, PAGE_SIZE); | ||||
return (obj); | return (obj); | ||||
error1: | error1: | ||||
munmap(mapbase, mapsize); | munmap(mapbase, mapsize); | ||||
error: | error: | ||||
if (note_map != NULL && note_map != MAP_FAILED) | if (note_map != NULL && note_map != MAP_FAILED) | ||||
munmap(note_map, note_map_len); | munmap(note_map, note_map_len); | ||||
if (!phdr_in_zero_page(hdr)) | |||||
munmap(phdr, hdr->e_phnum * sizeof(phdr[0])); | |||||
emaste: This seems a bit magical | |||||
Not Done Inline ActionsAh yes, from this diff you pretty easily see that the condition matches the one below where the pages are mapped, but maybe not if somebody is later looking at the whole .c file. I guess a comment might be nice, or otherwise putting the hdr->e_phoff + hdr->e_phnum * sizeof(Elf_Phdr) > (size_t)PAGE_SIZE expression into a macro, maybe? dim: Ah yes, from this diff you pretty easily see that the condition matches the one below where the… | |||||
Not Done Inline ActionsYeah, in the context of this diff it's clear why this is like this but might not be clear for someone looking at this file in the future. emaste: Yeah, in the context of this diff it's clear why this is like this but might not be clear for… | |||||
emasteUnsubmitted Not Done Inline ActionsOh, one more thing, what happens if the phdr starts in the first mapped page but crosses the boundary into the next one? emaste: Oh, one more thing, what happens if the phdr starts in the first mapped page but crosses the… | |||||
dimUnsubmitted Not Done Inline ActionsAs far as I can see, the first page would be mapped twice, once with offset 0 (line 360 in the new version), and once with offset hdr->e_phoff. It's not a big issue, but it may waste a page. AFAIK you can map pages multiple times just fine, certainly when they're read-only? dim: As far as I can see, the first page would be mapped twice, once with offset 0 (line 360 in the… | |||||
kibAuthorUnsubmitted Done Inline ActionsIt happens only during parsing, map_object() unmaps both special mappings. In the case Ed noted, we must map two pages. Test checks that the end of the supposed mapping fits into zero page. If it not, we indeed would map zero page twice, and it must work. For instance, we map shared libraries at different bases. kib: It happens only during parsing, map_object() unmaps both special mappings.
In the case Ed… | |||||
munmap(hdr, PAGE_SIZE); | munmap(hdr, PAGE_SIZE); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
static Elf_Ehdr * | static Elf_Ehdr * | ||||
get_elf_header(int fd, const char *path, const struct stat *sbp) | get_elf_header(int fd, const char *path, const struct stat *sbp, | ||||
Elf_Phdr **phdr_p) | |||||
{ | { | ||||
Elf_Ehdr *hdr; | Elf_Ehdr *hdr; | ||||
Elf_Phdr *phdr; | |||||
/* Make sure file has enough data for the ELF header */ | /* Make sure file has enough data for the ELF header */ | ||||
if (sbp != NULL && sbp->st_size < (off_t)sizeof(Elf_Ehdr)) { | if (sbp != NULL && sbp->st_size < (off_t)sizeof(Elf_Ehdr)) { | ||||
_rtld_error("%s: invalid file format", path); | _rtld_error("%s: invalid file format", path); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
hdr = mmap(NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE | MAP_PREFAULT_READ, | hdr = mmap(NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE | MAP_PREFAULT_READ, | ||||
Show All 32 Lines | get_elf_header(int fd, const char *path, const struct stat *sbp, | ||||
* not strictly required by the ABI specification, but it seems to | * not strictly required by the ABI specification, but it seems to | ||||
* always true in practice. And, it simplifies things considerably. | * always true in practice. And, it simplifies things considerably. | ||||
*/ | */ | ||||
if (hdr->e_phentsize != sizeof(Elf_Phdr)) { | if (hdr->e_phentsize != sizeof(Elf_Phdr)) { | ||||
_rtld_error( | _rtld_error( | ||||
"%s: invalid shared object: e_phentsize != sizeof(Elf_Phdr)", path); | "%s: invalid shared object: e_phentsize != sizeof(Elf_Phdr)", path); | ||||
goto error; | goto error; | ||||
} | } | ||||
if (hdr->e_phoff + hdr->e_phnum * sizeof(Elf_Phdr) > | if (phdr_in_zero_page(hdr)) { | ||||
(size_t)PAGE_SIZE) { | phdr = (Elf_Phdr *)((char *)hdr + hdr->e_phoff); | ||||
_rtld_error("%s: program header too large", path); | } else { | ||||
phdr = mmap(NULL, hdr->e_phnum * sizeof(phdr[0]), | |||||
PROT_READ, MAP_PRIVATE | MAP_PREFAULT_READ, fd, | |||||
hdr->e_phoff); | |||||
if (phdr == MAP_FAILED) { | |||||
_rtld_error("%s: error mapping phdr: %s", path, | |||||
rtld_strerror(errno)); | |||||
goto error; | goto error; | ||||
} | } | ||||
} | |||||
*phdr_p = phdr; | |||||
return (hdr); | return (hdr); | ||||
error: | error: | ||||
munmap(hdr, PAGE_SIZE); | munmap(hdr, PAGE_SIZE); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
void | void | ||||
▲ Show 20 Lines • Show All 83 Lines • Show Last 20 Lines |
This seems a bit magical