diff options
author | Roland McGrath <roland@hack.frob.com> | 2015-04-17 09:02:19 -0700 |
---|---|---|
committer | Roland McGrath <roland@hack.frob.com> | 2015-04-17 09:02:19 -0700 |
commit | d1e44df1fa5fefd8a083f6c1e909bbcdc97c6438 (patch) | |
tree | a76176eca62c0907dd7f135979312c8e55ad06a5 /sysdeps/nacl/dl-map-segments.h | |
parent | f70925993ada98039250d46c62fb89c168b8f9d6 (diff) | |
download | glibc-d1e44df1fa5fefd8a083f6c1e909bbcdc97c6438.tar.gz glibc-d1e44df1fa5fefd8a083f6c1e909bbcdc97c6438.tar.xz glibc-d1e44df1fa5fefd8a083f6c1e909bbcdc97c6438.zip |
Add arm-nacl port.
Diffstat (limited to 'sysdeps/nacl/dl-map-segments.h')
-rw-r--r-- | sysdeps/nacl/dl-map-segments.h | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/sysdeps/nacl/dl-map-segments.h b/sysdeps/nacl/dl-map-segments.h new file mode 100644 index 0000000000..f305da304a --- /dev/null +++ b/sysdeps/nacl/dl-map-segments.h @@ -0,0 +1,238 @@ +/* Map in a shared object's segments. NaCl version. + Copyright (C) 2015 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <assert.h> +#include <dl-load.h> +#include <errno.h> +#include <stdbool.h> +#include <unistd.h> +#include <libc-internal.h> + + +/* This is basically pread, but with iteration after short reads. */ +static bool +read_in_data (int fd, void *data, size_t len, off_t pos) +{ + if (__glibc_unlikely (__lseek (fd, pos, SEEK_SET) == (off_t) -1)) + return true; + while (len > 0) + { + ssize_t n = __read (fd, data, len); + if (__glibc_unlikely (n < 0)) + return true; + if (__glibc_unlikely (n == 0)) + { + errno = EFTYPE; + return true; + } + data += n; + len -= n; + } + return false; +} + +static const char * +_dl_map_segments (struct link_map *l, int fd, + const ElfW(Ehdr) *header, int type, + const struct loadcmd loadcmds[], size_t nloadcmds, + const size_t maplength, bool has_holes, + struct link_map *loader) +{ + if (__builtin_expect (type, ET_DYN) == ET_DYN) + { + /* This is a position-independent shared object. Let the system + choose where to place it. + + As a refinement, sometimes we have an address that we would + prefer to map such objects at; but this is only a preference, + the OS can do whatever it likes. */ + ElfW(Addr) mappref + = (ELF_PREFERRED_ADDRESS (loader, maplength, + loadcmds[0].mapstart & GLRO(dl_use_load_bias)) + - MAP_BASE_ADDR (l)); + + uintptr_t mapstart; + if (__glibc_likely (loadcmds[0].prot & PROT_EXEC)) + { + /* When there is a code segment, we must use the + allocate_code_data interface to choose a location. */ + + uintptr_t code_size = loadcmds[0].allocend - loadcmds[0].mapstart; + uintptr_t data_offset; + size_t data_size; + + if (__glibc_likely (nloadcmds > 1)) + { + data_offset = loadcmds[1].mapstart - loadcmds[0].mapstart; + data_size = ALIGN_UP (maplength - data_offset, + GLRO(dl_pagesize)); + } + else + { + data_offset = 0; + data_size = 0; + } + + int error = __nacl_irt_code_data_alloc.allocate_code_data + (mappref, code_size, data_offset, data_size, &mapstart); + if (__glibc_unlikely (error)) + { + errno = error; + return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT; + } + } + else + { + /* With no code pages involved, plain mmap works fine. */ + void *mapped = __mmap ((void *) mappref, maplength, + PROT_NONE, MAP_ANON, -1, 0); + if (__glibc_unlikely (mapped == MAP_FAILED)) + return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT; + mapstart = (uintptr_t) mapped; + } + + l->l_addr = mapstart - loadcmds[0].mapstart; + } + + /* Remember which part of the address space this object uses. */ + l->l_map_start = loadcmds[0].mapstart + l->l_addr; + l->l_map_end = l->l_map_start + maplength; + l->l_contiguous = !has_holes; + + /* Now actually map (or read) in each segment. */ + for (const struct loadcmd *c = loadcmds; c < &loadcmds[nloadcmds]; ++c) + if (__glibc_likely (c->mapend > c->mapstart)) + { + /* Unlike POSIX mmap, NaCl's mmap does not reliably handle COW + faults in the remainder of the final partial page. So to get + the expected behavior for the unaligned boundary between data + and bss, it's necessary to allocate the final partial page of + data as anonymous memory rather than mapping it from the file. */ + + size_t maplen = c->mapend - c->mapstart; + if (c->mapend > c->dataend && c->allocend > c->dataend) + maplen = (c->dataend & -GLRO(dl_pagesize)) - c->mapstart; + + /* Map the segment contents from the file. */ + if (__glibc_unlikely (__mmap ((void *) (l->l_addr + c->mapstart), + maplen, c->prot, + MAP_FIXED|MAP_COPY|MAP_FILE, + fd, c->mapoff) + == MAP_FAILED)) + { + switch (errno) + { + case EINVAL: + case ENOTSUP: + case ENOSYS: + break; + default: + return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT; + } + + /* No mmap support for this file. */ + if (c->prot & PROT_EXEC) + { + /* Read the data into a temporary buffer. */ + const size_t len = c->mapend - c->mapstart; + void *data = __mmap (NULL, len, PROT_READ | PROT_WRITE, + MAP_ANON|MAP_PRIVATE, -1, 0); + if (__glibc_unlikely (data == MAP_FAILED)) + return DL_MAP_SEGMENTS_ERROR_MAP_ZERO_FILL; + if (read_in_data (fd, data, len, c->mapoff)) + return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT; + /* Now validate and install the code. */ + int error = __nacl_irt_dyncode.dyncode_create + ((void *) (l->l_addr + c->mapstart), data, len); + __munmap (data, len); + if (__glibc_unlikely (error)) + { + errno = error; + return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT; + } + } + else + { + /* Allocate the pages. */ + if (__mmap ((void *) (l->l_addr + c->mapstart), + c->mapend - c->mapstart, c->prot | PROT_WRITE, + MAP_FIXED|MAP_ANON|MAP_PRIVATE, -1, 0) + == MAP_FAILED) + return DL_MAP_SEGMENTS_ERROR_MAP_ZERO_FILL; + /* Now read in the data. */ + if (read_in_data (fd, (void *) (l->l_addr + c->mapstart), + c->dataend - c->mapstart, c->mapoff)) + return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT; + /* Now that we've filled the pages, reset the page + protections to what they should be. */ + if (!(c->prot & PROT_WRITE) + && __mprotect ((void *) (l->l_addr + c->mapstart), + c->mapend - c->mapstart, c->prot) < 0) + return DL_MAP_SEGMENTS_ERROR_MPROTECT; + } + } + else if (c->allocend > c->dataend) + { + /* Extra zero pages should appear at the end of this segment, + after the data mapped from the file. */ + + uintptr_t allocend = c->mapend; + if (c->mapend > c->dataend) + { + /* The final data page was partial. So we didn't map it in. + Instead, we must allocate an anonymous page to fill. */ + if (c->prot & PROT_WRITE) + /* Do the whole allocation right here. */ + allocend = c->allocend; + if (__mmap ((void *) (l->l_addr + c->mapstart + maplen), + allocend - (c->mapstart + maplen), c->prot, + MAP_FIXED|MAP_ANON|MAP_PRIVATE, -1, 0) + == MAP_FAILED) + return DL_MAP_SEGMENTS_ERROR_MAP_ZERO_FILL; + if (read_in_data (fd, + (void *) (l->l_addr + c->mapstart + maplen), + c->dataend & (GLRO(dl_pagesize) - 1), + c->mapoff + maplen)) + return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT; + /* Now that we've filled the page, reset its + protections to what they should be. */ + if (!(c->prot & PROT_WRITE) + && __mprotect ((void *) (l->l_addr + c->mapstart + maplen), + c->mapend - (c->mapstart + maplen), + c->prot) < 0) + return DL_MAP_SEGMENTS_ERROR_MPROTECT; + } + + /* Now allocate the pure zero-fill pages. */ + if (allocend < c->allocend + && (__mmap ((void *) (l->l_addr + c->mapstart + allocend), + c->allocend - (c->mapstart + allocend), c->prot, + MAP_FIXED|MAP_ANON|MAP_PRIVATE, -1, 0) + == MAP_FAILED)) + return DL_MAP_SEGMENTS_ERROR_MAP_ZERO_FILL; + } + + _dl_postprocess_loadcmd (l, header, c); + } + + /* Notify ELF_PREFERRED_ADDRESS that we have to load this one + fixed. */ + ELF_FIXED_ADDRESS (loader, c->mapstart); + + return NULL; +} |