diff options
author | David 'Digit' Turner <digit@google.com> | 2012-06-19 11:21:29 +0200 |
---|---|---|
committer | David 'Digit' Turner <digit@google.com> | 2012-06-25 11:52:40 +0200 |
commit | c1bd559d5b0fdcc25db2b6ae2705914103b24699 (patch) | |
tree | 14593885df4f31db54a2c615febf4031cc8ea8d4 | |
parent | 20bc061dc7a03249c90f8765ae757395587ce4f1 (diff) | |
download | bionic-c1bd559d5b0fdcc25db2b6ae2705914103b24699.zip bionic-c1bd559d5b0fdcc25db2b6ae2705914103b24699.tar.gz bionic-c1bd559d5b0fdcc25db2b6ae2705914103b24699.tar.bz2 |
linker: New sources to manage the ELF program header table.
This patch introduces two new source files containing a set of functions
to manage the program header table in an ELF binary, including the ability
to load PT_LOAD segments, and apply PT_GNU_RELRO protection.
Note: the files are not used currently, this will appear in a series
of future patches that will gradually modify linker.c to use
the phdr_table_xxx functions properly.
Change-Id: Ia3d4c1ff5fc3e265d8258b64b492f4e643f51bdc
-rw-r--r-- | linker/Android.mk | 1 | ||||
-rw-r--r-- | linker/linker.h | 18 | ||||
-rw-r--r-- | linker/linker_phdr.c | 665 | ||||
-rw-r--r-- | linker/linker_phdr.h | 112 |
4 files changed, 795 insertions, 1 deletions
diff --git a/linker/Android.mk b/linker/Android.mk index 2f39cbe..a739b4f 100644 --- a/linker/Android.mk +++ b/linker/Android.mk @@ -6,6 +6,7 @@ LOCAL_SRC_FILES:= \ linker.c \ linker_environ.c \ linker_format.c \ + linker_phdr.c \ rt.c \ dlfcn.c \ debugger.c diff --git a/linker/linker.h b/linker/linker.h index e84458e..82cf3c1 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -37,7 +37,23 @@ #undef PAGE_MASK #undef PAGE_SIZE #define PAGE_SIZE 4096 -#define PAGE_MASK 4095 +#define PAGE_MASK (PAGE_SIZE-1) + +/* Convenience macros to make page address/offset computations more explicit */ + +/* Returns the address of the page starting at address 'x' */ +#define PAGE_START(x) ((x) & ~PAGE_MASK) + +/* Returns the offset of address 'x' in its memory page, i.e. this is the + * same than 'x' - PAGE_START(x) */ +#define PAGE_OFFSET(x) ((x) & PAGE_MASK) + +/* Returns the address of the next page after address 'x', unless 'x' is + * itself at the start of a page. Equivalent to: + * + * (x == PAGE_START(x)) ? x : PAGE_START(x)+PAGE_SIZE + */ +#define PAGE_END(x) PAGE_START((x) + (PAGE_SIZE-1)) void debugger_init(); const char *addr_to_name(unsigned addr); diff --git a/linker/linker_phdr.c b/linker/linker_phdr.c new file mode 100644 index 0000000..c9f194b --- /dev/null +++ b/linker/linker_phdr.c @@ -0,0 +1,665 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <errno.h> +#include <sys/mman.h> + +#include "linker_phdr.h" + +/** + TECHNICAL NOTE ON ELF LOADING. + + An ELF file's program header table contains one or more PT_LOAD + segments, which corresponds to portions of the file that need to + be mapped into the process' address space. + + Each loadable segment has the following important properties: + + p_offset -> segment file offset + p_filesz -> segment file size + p_memsz -> segment memory size (always >= p_filesz) + p_vaddr -> segment's virtual address + p_flags -> segment flags (e.g. readable, writable, executable) + + We will ignore the p_paddr and p_align fields of Elf32_Phdr for now. + + The loadable segments can be seen as a list of [p_vaddr ... p_vaddr+p_memsz) + ranges of virtual addresses. A few rules apply: + + - the virtual address ranges should not overlap. + + - if a segment's p_filesz is smaller than its p_memsz, the extra bytes + between them should always be initialized to 0. + + - ranges do not necessarily start or end at page boundaries. Two distinct + segments can have their start and end on the same page. In this case, the + page inherits the mapping flags of the latter segment. + + Finally, the real load addrs of each segment is not p_vaddr. Instead the + loader decides where to load the first segment, then will load all others + relative to the first one to respect the initial range layout. + + For example, consider the following list: + + [ offset:0, filesz:0x4000, memsz:0x4000, vaddr:0x30000 ], + [ offset:0x4000, filesz:0x2000, memsz:0x8000, vaddr:0x40000 ], + + This corresponds to two segments that cover these virtual address ranges: + + 0x30000...0x34000 + 0x40000...0x48000 + + If the loader decides to load the first segment at address 0xa0000000 + then the segments' load address ranges will be: + + 0xa0030000...0xa0034000 + 0xa0040000...0xa0048000 + + In other words, all segments must be loaded at an address that has the same + constant offset from their p_vaddr value. This offset is computed as the + difference between the first segment's load address, and its p_vaddr value. + + However, in practice, segments do _not_ start at page boundaries. Since we + can only memory-map at page boundaries, this means that the bias is + computed as: + + load_bias = phdr0_load_address - PAGE_START(phdr0->p_vaddr) + + (NOTE: The value must be used as a 32-bit unsigned integer, to deal with + possible wrap around UINT32_MAX for possible large p_vaddr values). + + And that the phdr0_load_address must start at a page boundary, with + the segment's real content starting at: + + phdr0_load_address + PAGE_OFFSET(phdr0->p_vaddr) + + Note that ELF requires the following condition to make the mmap()-ing work: + + PAGE_OFFSET(phdr0->p_vaddr) == PAGE_OFFSET(phdr0->p_offset) + + The load_bias must be added to any p_vaddr value read from the ELF file to + determine the corresponding memory address. + + **/ + +#define MAYBE_MAP_FLAG(x,from,to) (((x) & (from)) ? (to) : 0) +#define PFLAGS_TO_PROT(x) (MAYBE_MAP_FLAG((x), PF_X, PROT_EXEC) | \ + MAYBE_MAP_FLAG((x), PF_R, PROT_READ) | \ + MAYBE_MAP_FLAG((x), PF_W, PROT_WRITE)) + +/* Load the program header table from an ELF file into a read-only private + * anonymous mmap-ed block. + * + * Input: + * fd -> file descriptor + * phdr_offset -> file offset of phdr table + * phdr_num -> number of entries in the table. + * + * Output: + * phdr_mmap -> address of mmap block in memory. + * phdr_memsize -> size of mmap block in memory. + * phdr_table -> address of first entry in memory. + * + * Return: + * -1 on error, or 0 on success. + */ +int phdr_table_load(int fd, + Elf32_Addr phdr_offset, + Elf32_Half phdr_num, + void** phdr_mmap, + Elf32_Addr* phdr_size, + const Elf32_Phdr** phdr_table) +{ + Elf32_Addr page_min, page_max, page_offset; + void* mmap_result; + + /* Just like the kernel, we only accept program header tables that + * are smaller than 64KB. */ + if (phdr_num < 1 || phdr_num > 65536/sizeof(Elf32_Phdr)) { + errno = EINVAL; + return -1; + } + + page_min = PAGE_START(phdr_offset); + page_max = PAGE_END(phdr_offset + phdr_num*sizeof(Elf32_Phdr)); + page_offset = PAGE_OFFSET(phdr_offset); + + mmap_result = mmap(NULL, + page_max - page_min, + PROT_READ, + MAP_PRIVATE, + fd, + page_min); + + if (mmap_result == MAP_FAILED) { + return -1; + } + + *phdr_mmap = mmap_result; + *phdr_size = page_max - page_min; + *phdr_table = (Elf32_Phdr*)((char*)mmap_result + page_offset); + + return 0; +} + +void phdr_table_unload(void* phdr_mmap, Elf32_Addr phdr_memsize) +{ + munmap(phdr_mmap, phdr_memsize); +} + + +/* Compute the extent of all loadable segments in an ELF program header + * table. This corresponds to the page-aligned size in bytes that needs to be + * reserved in the process' address space + * + * This returns 0 if there are no loadable segments. + */ +Elf32_Addr phdr_table_get_load_size(const Elf32_Phdr* phdr_table, + int phdr_count) +{ + int nn; + + Elf32_Addr min_vaddr = 0xFFFFFFFFU; + Elf32_Addr max_vaddr = 0x00000000U; + + for (nn = 0; nn < phdr_count; nn++) { + const Elf32_Phdr* phdr = &phdr_table[nn]; + + if (phdr->p_type != PT_LOAD) + continue; + + if (phdr->p_vaddr < min_vaddr) + min_vaddr = phdr->p_vaddr; + + if (phdr->p_vaddr + phdr->p_memsz > max_vaddr) + max_vaddr = phdr->p_vaddr + phdr->p_memsz; + } + + if (min_vaddr > max_vaddr) { + return 0; + } + + min_vaddr = PAGE_START(min_vaddr); + max_vaddr = PAGE_END(max_vaddr); + + return max_vaddr - min_vaddr; +} + +/* Reserve a virtual address range big enough to hold all loadable + * segments of a program header table. This is done by creating a + * private anonymous mmap() with PROT_NONE. + * + * Input: + * phdr_table -> program header table + * phdr_count -> number of entries in the tables + * required_base -> for prelinked libraries, mandatory load address + * of the first loadable segment. 0 otherwise. + * Output: + * load_start -> first page of reserved address space range + * load_size -> size in bytes of reserved address space range + * load_bias -> load bias, as described in technical note above. + * + * Return: + * 0 on success, -1 otherwise. Error code in errno. + */ +int +phdr_table_reserve_memory(const Elf32_Phdr* phdr_table, + int phdr_count, + Elf32_Addr required_base, + void** load_start, + Elf32_Addr* load_size, + Elf32_Addr* load_bias) +{ + Elf32_Addr size = phdr_table_get_load_size(phdr_table, phdr_count); + void* start; + int nn, mmap_flags; + + if (size == 0) { + errno = EINVAL; + return -1; + } + + mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS; + if (required_base != 0) + mmap_flags |= MAP_FIXED; + + start = mmap((void*)required_base, size, PROT_NONE, mmap_flags, -1, 0); + if (start == MAP_FAILED) { + return -1; + } + + *load_start = start; + *load_size = size; + *load_bias = 0; + + for (nn = 0; nn < phdr_count; nn++) { + const Elf32_Phdr* phdr = &phdr_table[nn]; + if (phdr->p_type == PT_LOAD) { + *load_bias = (Elf32_Addr)start - PAGE_START(phdr->p_vaddr); + break; + } + } + return 0; +} + +/* Map all loadable segments in process' address space. + * This assumes you already called phdr_table_reserve_memory to + * reserve the address space range for the library. + * + * Input: + * phdr_table -> program header table + * phdr_count -> number of entries in the table + * load_start -> start address of reserved memory range. + * load_size -> size of reserved memory range. + * load_bias -> load offset. + * fd -> input file descriptor. + * + * Return: + * 0 on success, -1 otherwise. Error code in errno. + */ +int +phdr_table_load_segments(const Elf32_Phdr* phdr_table, + int phdr_count, + void* load_start, + Elf32_Addr load_size, + Elf32_Addr load_bias, + int fd) +{ + int nn; + + for (nn = 0; nn < phdr_count; nn++) { + const Elf32_Phdr* phdr = &phdr_table[nn]; + void* seg_addr; + + if (phdr->p_type != PT_LOAD) + continue; + + /* Segment addresses in memory */ + Elf32_Addr seg_start = phdr->p_vaddr + load_bias; + Elf32_Addr seg_end = seg_start + phdr->p_memsz; + + Elf32_Addr seg_page_start = PAGE_START(seg_start); + Elf32_Addr seg_page_end = PAGE_END(seg_end); + + Elf32_Addr seg_file_end = seg_start + phdr->p_filesz; + + /* File offsets */ + Elf32_Addr file_start = phdr->p_offset; + Elf32_Addr file_end = file_start + phdr->p_filesz; + + Elf32_Addr file_page_start = PAGE_START(file_start); + Elf32_Addr file_page_end = PAGE_END(file_end); + + seg_addr = mmap((void*)seg_page_start, + file_end - file_page_start, + PFLAGS_TO_PROT(phdr->p_flags), + MAP_FIXED|MAP_PRIVATE, + fd, + file_page_start); + + if (seg_addr == MAP_FAILED) { + return -1; + } + + /* if the segment is writable, and does not end on a page boundary, + * zero-fill it until the page limit. */ + if ((phdr->p_flags & PF_W) != 0 && PAGE_OFFSET(seg_file_end) > 0) { + memset((void*)seg_file_end, 0, PAGE_SIZE - PAGE_OFFSET(seg_file_end)); + } + + seg_file_end = PAGE_END(seg_file_end); + + /* seg_file_end is now the first page address after the file + * content. If seg_end is larger, we need to zero anything + * between them. This is done by using a private anonymous + * map for all extra pages. + */ + if (seg_page_end > seg_file_end) { + void* zeromap = mmap((void*)seg_file_end, + seg_page_end - seg_file_end, + PFLAGS_TO_PROT(phdr->p_flags), + MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, + -1, + 0); + if (zeromap == MAP_FAILED) { + return -1; + } + } + } + return 0; +} + +/* Used internally. Used to set the protection bits of all loaded segmments + * with optional extra flags (i.e. really PROT_WRITE). Used by + * phdr_table_protect_segments and phdr_table_unprotect_segments. + */ +static int +_phdr_table_set_load_prot(const Elf32_Phdr* phdr_table, + int phdr_count, + Elf32_Addr load_bias, + int extra_prot_flags) +{ + const Elf32_Phdr* phdr = phdr_table; + const Elf32_Phdr* phdr_limit = phdr + phdr_count; + + for (; phdr < phdr_limit; phdr++) { + if (phdr->p_type != PT_LOAD || (phdr->p_flags & PF_W) != 0) + continue; + + Elf32_Addr seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias; + Elf32_Addr seg_page_end = PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias; + + int ret = mprotect((void*)seg_page_start, + seg_page_end - seg_page_start, + PFLAGS_TO_PROT(phdr->p_flags) | extra_prot_flags); + if (ret < 0) { + return -1; + } + } + return 0; +} + +/* Restore the original protection modes for all loadable segments. + * You should only call this after phdr_table_unprotect_segments and + * applying all relocations. + * + * Input: + * phdr_table -> program header table + * phdr_count -> number of entires in tables + * load_bias -> load bias + * Return: + * 0 on error, -1 on failure (error code in errno). + */ +int +phdr_table_protect_segments(const Elf32_Phdr* phdr_table, + int phdr_count, + Elf32_Addr load_bias) +{ + return _phdr_table_set_load_prot(phdr_table, phdr_count, + load_bias, 0); +} + +/* Change the protection of all loaded segments in memory to writable. + * This is useful before performing relocations. Once completed, you + * will have to call phdr_table_protect_segments to restore the original + * protection flags on all segments. + * + * Note that some writable segments can also have their content turned + * to read-only by calling phdr_table_protect_gnu_relro. This is no + * performed here. + * + * Input: + * phdr_table -> program header table + * phdr_count -> number of entires in tables + * load_bias -> load bias + * Return: + * 0 on error, -1 on failure (error code in errno). + */ +int +phdr_table_unprotect_segments(const Elf32_Phdr* phdr_table, + int phdr_count, + Elf32_Addr load_bias) +{ + return _phdr_table_set_load_prot(phdr_table, phdr_count, + load_bias, PROT_WRITE); +} + +/* Used internally by phdr_table_protect_gnu_relro and + * phdr_table_unprotect_gnu_relro. + */ +static int +_phdr_table_set_gnu_relro_prot(const Elf32_Phdr* phdr_table, + int phdr_count, + Elf32_Addr load_bias, + int prot_flags) +{ + const Elf32_Phdr* phdr = phdr_table; + const Elf32_Phdr* phdr_limit = phdr + phdr_count; + + for (phdr = phdr_table; phdr < phdr_limit; phdr++) { + if (phdr->p_type != PT_GNU_RELRO) + continue; + + /* Tricky: what happens when the relro segment does not start + * or end at page boundaries?. We're going to be over-protective + * here and put every page touched by the segment as read-only. + * + * This seems to match Ian Lance Taylor's description of the + * feature at http://www.airs.com/blog/archives/189. + * + * Extract: + * Note that the current dynamic linker code will only work + * correctly if the PT_GNU_RELRO segment starts on a page + * boundary. This is because the dynamic linker rounds the + * p_vaddr field down to the previous page boundary. If + * there is anything on the page which should not be read-only, + * the program is likely to fail at runtime. So in effect the + * linker must only emit a PT_GNU_RELRO segment if it ensures + * that it starts on a page boundary. + */ + Elf32_Addr seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias; + Elf32_Addr seg_page_end = PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias; + + int ret = mprotect((void*)seg_page_start, + seg_page_end - seg_page_start, + prot_flags); + if (ret < 0) { + return -1; + } + } + return 0; +} + +/* Apply GNU relro protection if specified by the program header. This will + * turn some of the pages of a writable PT_LOAD segment to read-only, as + * specified by one or more PT_GNU_RELRO segments. This must be always + * performed after relocations. + * + * NOTE: One must call phdr_table_unprotect_gnu_relro() before calling + * the library's destructors, in order to ensure that the .dynamic + * section is writable (as well as the .data.relro section that + * might contain the content of static constant C++ objects that + * needs to be destroyed). + * + * Input: + * phdr_table -> program header table + * phdr_count -> number of entires in tables + * load_bias -> load bias + * Return: + * 0 on error, -1 on failure (error code in errno). + */ +int +phdr_table_protect_gnu_relro(const Elf32_Phdr* phdr_table, + int phdr_count, + Elf32_Addr load_bias) +{ + return _phdr_table_set_gnu_relro_prot(phdr_table, + phdr_count, + load_bias, + PROT_READ); +} + +/* Un-apply GNU relro protection if specified by the program header. + * See comment for phdr_table_protect_gnu_relro. + * + * Input: + * phdr_table -> program header table + * phdr_count -> number of entires in tables + * load_bias -> load bias + * Return: + * 0 on error, -1 on failure (error code in errno). + */ +int +phdr_table_unprotect_gnu_relro(const Elf32_Phdr* phdr_table, + int phdr_count, + Elf32_Addr load_bias) +{ + return _phdr_table_set_gnu_relro_prot(phdr_table, + phdr_count, + load_bias, + PROT_READ|PROT_WRITE); +} + +#ifdef ANDROID_ARM_LINKER + +# ifndef PT_ARM_EXIDX +# define PT_ARM_EXIDX 0x70000001 /* .ARM.exidx segment */ +# endif + +/* Return the address and size of the .ARM.exidx section in memory, + * if present. + * + * Input: + * phdr_table -> program header table + * phdr_count -> number of entires in tables + * load_bias -> load bias + * Output: + * arm_exidx -> address of table in memory (NULL on failure). + * arm_exidx_count -> number of items in table (0 on failure). + * Return: + * 0 on error, -1 on failure (_no_ error code in errno) + */ +int +phdr_table_get_arm_exidx(const Elf32_Phdr* phdr_table, + int phdr_count, + Elf32_Addr load_bias, + Elf32_Addr** arm_exidx, + unsigned* arm_exidx_count) +{ + const Elf32_Phdr* phdr = phdr_table; + const Elf32_Phdr* phdr_limit = phdr + phdr_count; + + for (phdr = phdr_table; phdr < phdr_limit; phdr++) { + if (phdr->p_type != PT_ARM_EXIDX) + continue; + + *arm_exidx = (Elf32_Addr*)(load_bias + phdr->p_vaddr); + *arm_exidx_count = (unsigned)(phdr->p_memsz / 8); + return 0; + } + *arm_exidx = NULL; + *arm_exidx_count = 0; + return -1; +} +#endif /* ANDROID_ARM_LINKER */ + +/* Return the address of the ELF file's .dynamic section in memory, + * or NULL if missing. + * + * Input: + * phdr_table -> program header table + * phdr_count -> number of entires in tables + * load_bias -> load bias + * Return: + * 0 on error, -1 on failure (_no_ error code in errno) + */ +Elf32_Addr* +phdr_table_get_dynamic_section(const Elf32_Phdr* phdr_table, + int phdr_count, + Elf32_Addr load_bias) +{ + const Elf32_Phdr* phdr = phdr_table; + const Elf32_Phdr* phdr_limit = phdr + phdr_count; + + for (phdr = phdr_table; phdr < phdr_limit; phdr++) { + if (phdr->p_type == PT_DYNAMIC) { + return (Elf32_Addr*)(load_bias + phdr->p_vaddr); + } + } + return NULL; +} + +/* Return the address of the program header table as it appears in the loaded + * segments in memory. This is in contrast with the input 'phdr_table' which + * is temporary and will be released before the library is relocated. + * + * Input: + * phdr_table -> program header table + * phdr_count -> number of entries in tables + * load_bias -> load bias + * Return: + * Address of loaded program header table on success (it has + * 'phdr_count' entries), or NULL on failure (no error code). + */ +const Elf32_Phdr* +phdr_table_get_loaded_phdr(const Elf32_Phdr* phdr_table, + int phdr_count, + Elf32_Addr load_bias) +{ + const Elf32_Phdr* phdr = phdr_table; + const Elf32_Phdr* phdr_limit = phdr + phdr_count; + Elf32_Addr loaded = 0; + Elf32_Addr loaded_end; + + /* If there is a PT_PHDR, use it directly */ + for (phdr = phdr_table; phdr < phdr_limit; phdr++) { + if (phdr->p_type == PT_PHDR) { + loaded = load_bias + phdr->p_vaddr; + goto CHECK; + } + } + + /* Otherwise, check the first loadable segment. If its file offset + * is 0, it starts with the ELF header, and we can trivially find the + * loaded program header from it. */ + for (phdr = phdr_table; phdr < phdr_limit; phdr++) { + if (phdr->p_type == PT_LOAD) { + if (phdr->p_offset == 0) { + Elf32_Addr elf_addr = load_bias + phdr->p_vaddr; + const Elf32_Ehdr* ehdr = (const Elf32_Ehdr*)(void*)elf_addr; + Elf32_Addr offset = ehdr->e_phoff; + loaded = (Elf32_Addr)ehdr + offset; + goto CHECK; + } + break; + } + } + + /* We didn't find it, let the client know. He may be able to + * keep a copy of the input phdr_table instead. */ + return NULL; + +CHECK: + /* Ensure that our program header is actually within a loadable + * segment. This should help catch badly-formed ELF files that + * would cause the linker to crash later when trying to access it. + */ + loaded_end = loaded + phdr_count*sizeof(Elf32_Phdr); + + for (phdr = phdr_table; phdr < phdr_limit; phdr++) { + if (phdr->p_type != PT_LOAD) + continue; + Elf32_Addr seg_start = phdr->p_vaddr + load_bias; + Elf32_Addr seg_end = phdr->p_filesz + seg_start; + + if (seg_start <= loaded && loaded_end <= seg_end) { + return (const Elf32_Phdr*)loaded; + } + } + return NULL; +} diff --git a/linker/linker_phdr.h b/linker/linker_phdr.h new file mode 100644 index 0000000..d542e46 --- /dev/null +++ b/linker/linker_phdr.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef LINKER_PHDR_H +#define LINKER_PHDR_H + +/* Declarations related to the ELF program header table and segments. + * + * The design goal is to provide an API that is as close as possible + * to the ELF spec, and does not depend on linker-specific data + * structures (e.g. the exact layout of struct soinfo). + */ + +#include "linker.h" + +/* See linker_phdr.c for all usage documentation */ + +int +phdr_table_load(int fd, + Elf32_Addr phdr_offset, + Elf32_Half phdr_num, + void** phdr_mmap, + Elf32_Addr* phdr_size, + const Elf32_Phdr** phdr_table); + +void +phdr_table_unload(void* phdr_mmap, Elf32_Addr phdr_memsize); + +Elf32_Addr +phdr_table_get_load_size(const Elf32_Phdr* phdr_table, + int phdr_count); + +int +phdr_table_reserve_memory(const Elf32_Phdr* phdr_table, + int phdr_count, + Elf32_Addr required_base, + void** load_start, + Elf32_Addr* load_size, + Elf32_Addr* load_bias); + +int +phdr_table_load_segments(const Elf32_Phdr* phdr_table, + int phdr_count, + void* load_start, + Elf32_Addr load_size, + Elf32_Addr load_bias, + int fd); + +int +phdr_table_protect_segments(const Elf32_Phdr* phdr_table, + int phdr_count, + Elf32_Addr load_bias); + +int +phdr_table_unprotect_segments(const Elf32_Phdr* phdr_table, + int phdr_count, + Elf32_Addr load_bias); + +int +phdr_table_protect_gnu_relro(const Elf32_Phdr* phdr_table, + int phdr_count, + Elf32_Addr load_bias); + +int +phdr_table_unprotect_gnu_relro(const Elf32_Phdr* phdr_table, + int phdr_count, + Elf32_Addr load_bias); + +const Elf32_Phdr* +phdr_table_get_loaded_phdr(const Elf32_Phdr* phdr_table, + int phdr_count, + Elf32_Addr load_bias); + +#ifdef ANDROID_ARM_LINKER +int +phdr_table_get_arm_exidx(const Elf32_Phdr* phdr_table, + int phdr_count, + Elf32_Addr load_bias, + Elf32_Addr** arm_exidx, + unsigned* arm_exidix_count); +#endif + +Elf32_Addr* +phdr_table_get_dynamic_section(const Elf32_Phdr* phdr_table, + int phdr_count, + Elf32_Addr load_bias); + +#endif /* LINKER_PHDR_H */ |