1c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* 2c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Copyright (C) 2012 The Android Open Source Project 3c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * All rights reserved. 4c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 5c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Redistribution and use in source and binary forms, with or without 6c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * modification, are permitted provided that the following conditions 7c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * are met: 8c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * * Redistributions of source code must retain the above copyright 9c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * notice, this list of conditions and the following disclaimer. 10c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * * Redistributions in binary form must reproduce the above copyright 11c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * notice, this list of conditions and the following disclaimer in 12c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * the documentation and/or other materials provided with the 13c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * distribution. 14c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 15c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 22c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 25c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * SUCH DAMAGE. 27c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 28c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 29c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner#include <errno.h> 30c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner#include <sys/mman.h> 31c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 32c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner#include "linker_phdr.h" 33c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 34c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/** 35c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner TECHNICAL NOTE ON ELF LOADING. 36c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 37c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner An ELF file's program header table contains one or more PT_LOAD 38c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner segments, which corresponds to portions of the file that need to 39c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner be mapped into the process' address space. 40c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 41c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Each loadable segment has the following important properties: 42c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 43c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner p_offset -> segment file offset 44c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner p_filesz -> segment file size 45c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner p_memsz -> segment memory size (always >= p_filesz) 46c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner p_vaddr -> segment's virtual address 47c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner p_flags -> segment flags (e.g. readable, writable, executable) 48c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 49c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner We will ignore the p_paddr and p_align fields of Elf32_Phdr for now. 50c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 51c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner The loadable segments can be seen as a list of [p_vaddr ... p_vaddr+p_memsz) 52c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner ranges of virtual addresses. A few rules apply: 53c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 54c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner - the virtual address ranges should not overlap. 55c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 56c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner - if a segment's p_filesz is smaller than its p_memsz, the extra bytes 57c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner between them should always be initialized to 0. 58c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 59c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner - ranges do not necessarily start or end at page boundaries. Two distinct 60c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner segments can have their start and end on the same page. In this case, the 61c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner page inherits the mapping flags of the latter segment. 62c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 63c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Finally, the real load addrs of each segment is not p_vaddr. Instead the 64c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner loader decides where to load the first segment, then will load all others 65c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner relative to the first one to respect the initial range layout. 66c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 67c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner For example, consider the following list: 68c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 69c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner [ offset:0, filesz:0x4000, memsz:0x4000, vaddr:0x30000 ], 70c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner [ offset:0x4000, filesz:0x2000, memsz:0x8000, vaddr:0x40000 ], 71c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 72c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner This corresponds to two segments that cover these virtual address ranges: 73c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 74c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 0x30000...0x34000 75c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 0x40000...0x48000 76c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 77c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner If the loader decides to load the first segment at address 0xa0000000 78c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner then the segments' load address ranges will be: 79c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 80c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 0xa0030000...0xa0034000 81c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 0xa0040000...0xa0048000 82c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 83c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner In other words, all segments must be loaded at an address that has the same 84c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner constant offset from their p_vaddr value. This offset is computed as the 85c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner difference between the first segment's load address, and its p_vaddr value. 86c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 87c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner However, in practice, segments do _not_ start at page boundaries. Since we 88c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner can only memory-map at page boundaries, this means that the bias is 89c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner computed as: 90c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 91c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner load_bias = phdr0_load_address - PAGE_START(phdr0->p_vaddr) 92c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 93c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner (NOTE: The value must be used as a 32-bit unsigned integer, to deal with 94c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner possible wrap around UINT32_MAX for possible large p_vaddr values). 95c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 96c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner And that the phdr0_load_address must start at a page boundary, with 97c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner the segment's real content starting at: 98c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 99c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner phdr0_load_address + PAGE_OFFSET(phdr0->p_vaddr) 100c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 101c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Note that ELF requires the following condition to make the mmap()-ing work: 102c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 103c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner PAGE_OFFSET(phdr0->p_vaddr) == PAGE_OFFSET(phdr0->p_offset) 104c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 105c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner The load_bias must be added to any p_vaddr value read from the ELF file to 106c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner determine the corresponding memory address. 107c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 108c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner **/ 109c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 110c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner#define MAYBE_MAP_FLAG(x,from,to) (((x) & (from)) ? (to) : 0) 111c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner#define PFLAGS_TO_PROT(x) (MAYBE_MAP_FLAG((x), PF_X, PROT_EXEC) | \ 112c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner MAYBE_MAP_FLAG((x), PF_R, PROT_READ) | \ 113c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner MAYBE_MAP_FLAG((x), PF_W, PROT_WRITE)) 114c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 115c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* Load the program header table from an ELF file into a read-only private 116c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * anonymous mmap-ed block. 117c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 118c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Input: 119c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * fd -> file descriptor 120c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_offset -> file offset of phdr table 121c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_num -> number of entries in the table. 122c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 123c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Output: 124c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_mmap -> address of mmap block in memory. 125c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_memsize -> size of mmap block in memory. 126c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_table -> address of first entry in memory. 127c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 128c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Return: 129c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * -1 on error, or 0 on success. 130c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 131c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerint phdr_table_load(int fd, 132c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr phdr_offset, 133c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Half phdr_num, 134c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner void** phdr_mmap, 135c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr* phdr_size, 136c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner const Elf32_Phdr** phdr_table) 137c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner{ 138c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr page_min, page_max, page_offset; 139c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner void* mmap_result; 140c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 141c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner /* Just like the kernel, we only accept program header tables that 142c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * are smaller than 64KB. */ 143c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner if (phdr_num < 1 || phdr_num > 65536/sizeof(Elf32_Phdr)) { 144c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner errno = EINVAL; 145c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner return -1; 146c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 147c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 148c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner page_min = PAGE_START(phdr_offset); 149c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner page_max = PAGE_END(phdr_offset + phdr_num*sizeof(Elf32_Phdr)); 150c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner page_offset = PAGE_OFFSET(phdr_offset); 151c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 152c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner mmap_result = mmap(NULL, 153c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner page_max - page_min, 154c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner PROT_READ, 155c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner MAP_PRIVATE, 156c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner fd, 157c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner page_min); 158c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 159c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner if (mmap_result == MAP_FAILED) { 160c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner return -1; 161c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 162c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 163c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *phdr_mmap = mmap_result; 164c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *phdr_size = page_max - page_min; 165c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *phdr_table = (Elf32_Phdr*)((char*)mmap_result + page_offset); 166c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 167c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner return 0; 168c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 169c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 170c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnervoid phdr_table_unload(void* phdr_mmap, Elf32_Addr phdr_memsize) 171c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner{ 172c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner munmap(phdr_mmap, phdr_memsize); 173c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 174c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 175c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 176c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* Compute the extent of all loadable segments in an ELF program header 177c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * table. This corresponds to the page-aligned size in bytes that needs to be 178c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * reserved in the process' address space 179c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 180c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * This returns 0 if there are no loadable segments. 181c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 182c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' TurnerElf32_Addr phdr_table_get_load_size(const Elf32_Phdr* phdr_table, 1834688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes size_t phdr_count) 184c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner{ 185c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr min_vaddr = 0xFFFFFFFFU; 186c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr max_vaddr = 0x00000000U; 187c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 1884688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes for (size_t i = 0; i < phdr_count; ++i) { 1894688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes const Elf32_Phdr* phdr = &phdr_table[i]; 190c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 1914688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes if (phdr->p_type != PT_LOAD) { 192c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner continue; 1934688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes } 194c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 1954688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes if (phdr->p_vaddr < min_vaddr) { 196c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner min_vaddr = phdr->p_vaddr; 1974688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes } 198c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 1994688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes if (phdr->p_vaddr + phdr->p_memsz > max_vaddr) { 200c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner max_vaddr = phdr->p_vaddr + phdr->p_memsz; 2014688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes } 202c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 203c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 204c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner if (min_vaddr > max_vaddr) { 205c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner return 0; 206c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 207c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 208c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner min_vaddr = PAGE_START(min_vaddr); 209c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner max_vaddr = PAGE_END(max_vaddr); 210c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 211c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner return max_vaddr - min_vaddr; 212c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 213c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 214c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* Reserve a virtual address range big enough to hold all loadable 215c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * segments of a program header table. This is done by creating a 216c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * private anonymous mmap() with PROT_NONE. 217c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 218c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Input: 219c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_table -> program header table 220c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_count -> number of entries in the tables 221c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Output: 222c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * load_start -> first page of reserved address space range 223c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * load_size -> size in bytes of reserved address space range 224c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * load_bias -> load bias, as described in technical note above. 225c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 226c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Return: 227c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 0 on success, -1 otherwise. Error code in errno. 228c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 229c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerint 230c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerphdr_table_reserve_memory(const Elf32_Phdr* phdr_table, 2314688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes size_t phdr_count, 2324688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes void** load_start, 2334688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes Elf32_Addr* load_size, 2344688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes Elf32_Addr* load_bias) 235c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner{ 236c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr size = phdr_table_get_load_size(phdr_table, phdr_count); 237c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner if (size == 0) { 238c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner errno = EINVAL; 239c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner return -1; 240c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 241c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 2424688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS; 2434688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes void* start = mmap(NULL, size, PROT_NONE, mmap_flags, -1, 0); 244c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner if (start == MAP_FAILED) { 245c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner return -1; 246c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 247c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 248c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *load_start = start; 249c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *load_size = size; 250c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *load_bias = 0; 251c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 2524688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes for (size_t i = 0; i < phdr_count; ++i) { 2534688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes const Elf32_Phdr* phdr = &phdr_table[i]; 254c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner if (phdr->p_type == PT_LOAD) { 255c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *load_bias = (Elf32_Addr)start - PAGE_START(phdr->p_vaddr); 256c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner break; 257c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 258c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 259c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner return 0; 260c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 261c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 262c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* Map all loadable segments in process' address space. 263c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * This assumes you already called phdr_table_reserve_memory to 264c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * reserve the address space range for the library. 265c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 266c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Input: 267c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_table -> program header table 268c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_count -> number of entries in the table 269c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * load_bias -> load offset. 270c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * fd -> input file descriptor. 271c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 272c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Return: 273c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 0 on success, -1 otherwise. Error code in errno. 274c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 275c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerint 276c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerphdr_table_load_segments(const Elf32_Phdr* phdr_table, 277c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner int phdr_count, 278c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr load_bias, 279c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner int fd) 280c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner{ 281c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner int nn; 282c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 283c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner for (nn = 0; nn < phdr_count; nn++) { 284c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner const Elf32_Phdr* phdr = &phdr_table[nn]; 285c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner void* seg_addr; 286c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 287c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner if (phdr->p_type != PT_LOAD) 288c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner continue; 289c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 290c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner /* Segment addresses in memory */ 291c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr seg_start = phdr->p_vaddr + load_bias; 292c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr seg_end = seg_start + phdr->p_memsz; 293c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 294c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr seg_page_start = PAGE_START(seg_start); 295c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr seg_page_end = PAGE_END(seg_end); 296c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 297c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr seg_file_end = seg_start + phdr->p_filesz; 298c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 299c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner /* File offsets */ 300c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr file_start = phdr->p_offset; 301c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr file_end = file_start + phdr->p_filesz; 302c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 303c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr file_page_start = PAGE_START(file_start); 304c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr file_page_end = PAGE_END(file_end); 305c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 306c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner seg_addr = mmap((void*)seg_page_start, 307c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner file_end - file_page_start, 308c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner PFLAGS_TO_PROT(phdr->p_flags), 309c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner MAP_FIXED|MAP_PRIVATE, 310c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner fd, 311c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner file_page_start); 312c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 313c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner if (seg_addr == MAP_FAILED) { 314c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner return -1; 315c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 316c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 317c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner /* if the segment is writable, and does not end on a page boundary, 318c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * zero-fill it until the page limit. */ 319c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner if ((phdr->p_flags & PF_W) != 0 && PAGE_OFFSET(seg_file_end) > 0) { 320c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner memset((void*)seg_file_end, 0, PAGE_SIZE - PAGE_OFFSET(seg_file_end)); 321c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 322c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 323c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner seg_file_end = PAGE_END(seg_file_end); 324c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 325c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner /* seg_file_end is now the first page address after the file 326c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * content. If seg_end is larger, we need to zero anything 327c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * between them. This is done by using a private anonymous 328c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * map for all extra pages. 329c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 330c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner if (seg_page_end > seg_file_end) { 331c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner void* zeromap = mmap((void*)seg_file_end, 332c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner seg_page_end - seg_file_end, 333c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner PFLAGS_TO_PROT(phdr->p_flags), 334c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, 335c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner -1, 336c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 0); 337c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner if (zeromap == MAP_FAILED) { 338c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner return -1; 339c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 340c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 341c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 342c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner return 0; 343c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 344c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 345105bc26fa6e5f6a946a2ff144ae316e69c6ce08eElliott Hughes/* Used internally. Used to set the protection bits of all loaded segments 346c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * with optional extra flags (i.e. really PROT_WRITE). Used by 347c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_table_protect_segments and phdr_table_unprotect_segments. 348c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 349c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerstatic int 350c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner_phdr_table_set_load_prot(const Elf32_Phdr* phdr_table, 351c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner int phdr_count, 352c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr load_bias, 353c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner int extra_prot_flags) 354c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner{ 355c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner const Elf32_Phdr* phdr = phdr_table; 356c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner const Elf32_Phdr* phdr_limit = phdr + phdr_count; 357c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 358c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner for (; phdr < phdr_limit; phdr++) { 359c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner if (phdr->p_type != PT_LOAD || (phdr->p_flags & PF_W) != 0) 360c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner continue; 361c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 362c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias; 363c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr seg_page_end = PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias; 364c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 365c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner int ret = mprotect((void*)seg_page_start, 366c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner seg_page_end - seg_page_start, 367c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner PFLAGS_TO_PROT(phdr->p_flags) | extra_prot_flags); 368c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner if (ret < 0) { 369c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner return -1; 370c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 371c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 372c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner return 0; 373c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 374c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 375c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* Restore the original protection modes for all loadable segments. 376c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * You should only call this after phdr_table_unprotect_segments and 377c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * applying all relocations. 378c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 379c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Input: 380c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_table -> program header table 381105bc26fa6e5f6a946a2ff144ae316e69c6ce08eElliott Hughes * phdr_count -> number of entries in tables 382c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * load_bias -> load bias 383c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Return: 384c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 0 on error, -1 on failure (error code in errno). 385c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 386c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerint 387c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerphdr_table_protect_segments(const Elf32_Phdr* phdr_table, 388c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner int phdr_count, 389c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr load_bias) 390c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner{ 391c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner return _phdr_table_set_load_prot(phdr_table, phdr_count, 392c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner load_bias, 0); 393c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 394c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 395c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* Change the protection of all loaded segments in memory to writable. 396c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * This is useful before performing relocations. Once completed, you 397c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * will have to call phdr_table_protect_segments to restore the original 398c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * protection flags on all segments. 399c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 400c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Note that some writable segments can also have their content turned 401c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * to read-only by calling phdr_table_protect_gnu_relro. This is no 402c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * performed here. 403c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 404c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Input: 405c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_table -> program header table 406105bc26fa6e5f6a946a2ff144ae316e69c6ce08eElliott Hughes * phdr_count -> number of entries in tables 407c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * load_bias -> load bias 408c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Return: 409c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 0 on error, -1 on failure (error code in errno). 410c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 411c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerint 412c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerphdr_table_unprotect_segments(const Elf32_Phdr* phdr_table, 413c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner int phdr_count, 414c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr load_bias) 415c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner{ 416c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner return _phdr_table_set_load_prot(phdr_table, phdr_count, 417c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner load_bias, PROT_WRITE); 418c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 419c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 420c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* Used internally by phdr_table_protect_gnu_relro and 421c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_table_unprotect_gnu_relro. 422c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 423c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerstatic int 424c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner_phdr_table_set_gnu_relro_prot(const Elf32_Phdr* phdr_table, 425c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner int phdr_count, 426c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr load_bias, 427c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner int prot_flags) 428c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner{ 429c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner const Elf32_Phdr* phdr = phdr_table; 430c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner const Elf32_Phdr* phdr_limit = phdr + phdr_count; 431c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 432c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner for (phdr = phdr_table; phdr < phdr_limit; phdr++) { 433c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner if (phdr->p_type != PT_GNU_RELRO) 434c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner continue; 435c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 436c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner /* Tricky: what happens when the relro segment does not start 437c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * or end at page boundaries?. We're going to be over-protective 438c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * here and put every page touched by the segment as read-only. 439c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 440c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * This seems to match Ian Lance Taylor's description of the 441c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * feature at http://www.airs.com/blog/archives/189. 442c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 443c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Extract: 444c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Note that the current dynamic linker code will only work 445c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * correctly if the PT_GNU_RELRO segment starts on a page 446c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * boundary. This is because the dynamic linker rounds the 447c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * p_vaddr field down to the previous page boundary. If 448c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * there is anything on the page which should not be read-only, 449c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * the program is likely to fail at runtime. So in effect the 450c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * linker must only emit a PT_GNU_RELRO segment if it ensures 451c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * that it starts on a page boundary. 452c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 453c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias; 454c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr seg_page_end = PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias; 455c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 456c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner int ret = mprotect((void*)seg_page_start, 457c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner seg_page_end - seg_page_start, 458c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner prot_flags); 459c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner if (ret < 0) { 460c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner return -1; 461c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 462c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 463c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner return 0; 464c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 465c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 466c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* Apply GNU relro protection if specified by the program header. This will 467c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * turn some of the pages of a writable PT_LOAD segment to read-only, as 468c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * specified by one or more PT_GNU_RELRO segments. This must be always 469c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * performed after relocations. 470c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 47112c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel * The areas typically covered are .got and .data.rel.ro, these are 47212c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel * read-only from the program's POV, but contain absolute addresses 47312c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel * that need to be relocated before use. 474c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 475c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Input: 476c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_table -> program header table 477105bc26fa6e5f6a946a2ff144ae316e69c6ce08eElliott Hughes * phdr_count -> number of entries in tables 478c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * load_bias -> load bias 479c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Return: 480c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 0 on error, -1 on failure (error code in errno). 481c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 482c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerint 483c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerphdr_table_protect_gnu_relro(const Elf32_Phdr* phdr_table, 484c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner int phdr_count, 485c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr load_bias) 486c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner{ 487c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner return _phdr_table_set_gnu_relro_prot(phdr_table, 488c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner phdr_count, 489c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner load_bias, 490c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner PROT_READ); 491c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 492c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 493c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner#ifdef ANDROID_ARM_LINKER 494c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 495c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner# ifndef PT_ARM_EXIDX 496c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner# define PT_ARM_EXIDX 0x70000001 /* .ARM.exidx segment */ 497c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner# endif 498c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 499c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* Return the address and size of the .ARM.exidx section in memory, 500c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * if present. 501c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 502c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Input: 503c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_table -> program header table 504105bc26fa6e5f6a946a2ff144ae316e69c6ce08eElliott Hughes * phdr_count -> number of entries in tables 505c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * load_bias -> load bias 506c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Output: 507c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * arm_exidx -> address of table in memory (NULL on failure). 508c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * arm_exidx_count -> number of items in table (0 on failure). 509c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Return: 510c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 0 on error, -1 on failure (_no_ error code in errno) 511c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 512c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerint 513c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerphdr_table_get_arm_exidx(const Elf32_Phdr* phdr_table, 514c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner int phdr_count, 515c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr load_bias, 516c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr** arm_exidx, 517c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner unsigned* arm_exidx_count) 518c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner{ 519c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner const Elf32_Phdr* phdr = phdr_table; 520c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner const Elf32_Phdr* phdr_limit = phdr + phdr_count; 521c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 522c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner for (phdr = phdr_table; phdr < phdr_limit; phdr++) { 523c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner if (phdr->p_type != PT_ARM_EXIDX) 524c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner continue; 525c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 526c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *arm_exidx = (Elf32_Addr*)(load_bias + phdr->p_vaddr); 527c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *arm_exidx_count = (unsigned)(phdr->p_memsz / 8); 528c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner return 0; 529c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 530c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *arm_exidx = NULL; 531c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *arm_exidx_count = 0; 532c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner return -1; 533c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 534c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner#endif /* ANDROID_ARM_LINKER */ 535c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 53612c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel/* Return the address and size of the ELF file's .dynamic section in memory, 537c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * or NULL if missing. 538c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 539c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Input: 540c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_table -> program header table 541105bc26fa6e5f6a946a2ff144ae316e69c6ce08eElliott Hughes * phdr_count -> number of entries in tables 542c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * load_bias -> load bias 54312c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel * Output: 54412c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel * dynamic -> address of table in memory (NULL on failure). 54512c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel * dynamic_count -> number of items in table (0 on failure). 546c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Return: 54712c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel * void 548c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 54912c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvelvoid 550c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerphdr_table_get_dynamic_section(const Elf32_Phdr* phdr_table, 551c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner int phdr_count, 55212c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel Elf32_Addr load_bias, 55312c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel Elf32_Addr** dynamic, 55412c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel size_t* dynamic_count) 555c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner{ 556c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner const Elf32_Phdr* phdr = phdr_table; 557c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner const Elf32_Phdr* phdr_limit = phdr + phdr_count; 558c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 559c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner for (phdr = phdr_table; phdr < phdr_limit; phdr++) { 56012c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel if (phdr->p_type != PT_DYNAMIC) { 56112c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel continue; 562c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 56312c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel 56412c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel *dynamic = (Elf32_Addr*)(load_bias + phdr->p_vaddr); 56512c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel if (dynamic_count) { 56612c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel *dynamic_count = (unsigned)(phdr->p_memsz / 8); 56712c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel } 56812c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel return; 56912c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel } 57012c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel *dynamic = NULL; 57112c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel if (dynamic_count) { 57212c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel *dynamic_count = 0; 573c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 574c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 575c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 576c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* Return the address of the program header table as it appears in the loaded 577c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * segments in memory. This is in contrast with the input 'phdr_table' which 578c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * is temporary and will be released before the library is relocated. 579c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 580c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Input: 581c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_table -> program header table 582c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_count -> number of entries in tables 583c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * load_bias -> load bias 584c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Return: 585c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Address of loaded program header table on success (it has 586c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 'phdr_count' entries), or NULL on failure (no error code). 587c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 588c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerconst Elf32_Phdr* 589c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerphdr_table_get_loaded_phdr(const Elf32_Phdr* phdr_table, 590c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner int phdr_count, 591c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr load_bias) 592c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner{ 593c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner const Elf32_Phdr* phdr = phdr_table; 594c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner const Elf32_Phdr* phdr_limit = phdr + phdr_count; 595c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr loaded = 0; 596c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr loaded_end; 597c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 598c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner /* If there is a PT_PHDR, use it directly */ 599c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner for (phdr = phdr_table; phdr < phdr_limit; phdr++) { 600c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner if (phdr->p_type == PT_PHDR) { 601c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner loaded = load_bias + phdr->p_vaddr; 602c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner goto CHECK; 603c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 604c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 605c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 606c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner /* Otherwise, check the first loadable segment. If its file offset 607c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * is 0, it starts with the ELF header, and we can trivially find the 608c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * loaded program header from it. */ 609c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner for (phdr = phdr_table; phdr < phdr_limit; phdr++) { 610c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner if (phdr->p_type == PT_LOAD) { 611c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner if (phdr->p_offset == 0) { 612c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr elf_addr = load_bias + phdr->p_vaddr; 613c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner const Elf32_Ehdr* ehdr = (const Elf32_Ehdr*)(void*)elf_addr; 614c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr offset = ehdr->e_phoff; 615c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner loaded = (Elf32_Addr)ehdr + offset; 616c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner goto CHECK; 617c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 618c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner break; 619c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 620c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 621c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 622c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner /* We didn't find it, let the client know. He may be able to 623c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * keep a copy of the input phdr_table instead. */ 624c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner return NULL; 625c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 626c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' TurnerCHECK: 627c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner /* Ensure that our program header is actually within a loadable 628c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * segment. This should help catch badly-formed ELF files that 629c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * would cause the linker to crash later when trying to access it. 630c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 631c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner loaded_end = loaded + phdr_count*sizeof(Elf32_Phdr); 632c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 633c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner for (phdr = phdr_table; phdr < phdr_limit; phdr++) { 634c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner if (phdr->p_type != PT_LOAD) 635c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner continue; 636c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr seg_start = phdr->p_vaddr + load_bias; 637c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Elf32_Addr seg_end = phdr->p_filesz + seg_start; 638c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 639c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner if (seg_start <= loaded && loaded_end <= seg_end) { 640c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner return (const Elf32_Phdr*)loaded; 641c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 642c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 643c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner return NULL; 644c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 645