linker_phdr.cpp revision faf05bacd45719291b371f24b1b89543881b37f6
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 29650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes#include "linker_phdr.h" 30650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 31c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner#include <errno.h> 32e365f9d6543bc6607864ef61257505239dde15d1Marcus Oakland#include <machine/exec.h> 33c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner#include <sys/mman.h> 34c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 35650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes#include "linker.h" 36650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes#include "linker_debug.h" 37c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 38c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/** 39c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner TECHNICAL NOTE ON ELF LOADING. 40c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 41c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner An ELF file's program header table contains one or more PT_LOAD 42c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner segments, which corresponds to portions of the file that need to 43c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner be mapped into the process' address space. 44c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 45c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Each loadable segment has the following important properties: 46c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 47c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner p_offset -> segment file offset 48c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner p_filesz -> segment file size 49c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner p_memsz -> segment memory size (always >= p_filesz) 50c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner p_vaddr -> segment's virtual address 51c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner p_flags -> segment flags (e.g. readable, writable, executable) 52c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 530266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes We will ignore the p_paddr and p_align fields of ElfW(Phdr) for now. 54c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 55c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner The loadable segments can be seen as a list of [p_vaddr ... p_vaddr+p_memsz) 56c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner ranges of virtual addresses. A few rules apply: 57c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 58c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner - the virtual address ranges should not overlap. 59c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 60c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner - if a segment's p_filesz is smaller than its p_memsz, the extra bytes 61c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner between them should always be initialized to 0. 62c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 63c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner - ranges do not necessarily start or end at page boundaries. Two distinct 64c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner segments can have their start and end on the same page. In this case, the 65c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner page inherits the mapping flags of the latter segment. 66c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 67c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Finally, the real load addrs of each segment is not p_vaddr. Instead the 68c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner loader decides where to load the first segment, then will load all others 69c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner relative to the first one to respect the initial range layout. 70c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 71c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner For example, consider the following list: 72c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 73c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner [ offset:0, filesz:0x4000, memsz:0x4000, vaddr:0x30000 ], 74c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner [ offset:0x4000, filesz:0x2000, memsz:0x8000, vaddr:0x40000 ], 75c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 76c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner This corresponds to two segments that cover these virtual address ranges: 77c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 78c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 0x30000...0x34000 79c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 0x40000...0x48000 80c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 81c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner If the loader decides to load the first segment at address 0xa0000000 82c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner then the segments' load address ranges will be: 83c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 84c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 0xa0030000...0xa0034000 85c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 0xa0040000...0xa0048000 86c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 87c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner In other words, all segments must be loaded at an address that has the same 88c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner constant offset from their p_vaddr value. This offset is computed as the 89c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner difference between the first segment's load address, and its p_vaddr value. 90c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 91c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner However, in practice, segments do _not_ start at page boundaries. Since we 92c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner can only memory-map at page boundaries, this means that the bias is 93c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner computed as: 94c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 95c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner load_bias = phdr0_load_address - PAGE_START(phdr0->p_vaddr) 96c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 97c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner (NOTE: The value must be used as a 32-bit unsigned integer, to deal with 98c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner possible wrap around UINT32_MAX for possible large p_vaddr values). 99c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 100c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner And that the phdr0_load_address must start at a page boundary, with 101c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner the segment's real content starting at: 102c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 103c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner phdr0_load_address + PAGE_OFFSET(phdr0->p_vaddr) 104c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 105c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Note that ELF requires the following condition to make the mmap()-ing work: 106c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 107c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner PAGE_OFFSET(phdr0->p_vaddr) == PAGE_OFFSET(phdr0->p_offset) 108c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 109c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner The load_bias must be added to any p_vaddr value read from the ELF file to 110c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner determine the corresponding memory address. 111c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 112c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner **/ 113c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 114faf05bacd45719291b371f24b1b89543881b37f6Elliott Hughes#define MAYBE_MAP_FLAG(x, from, to) (((x) & (from)) ? (to) : 0) 115c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner#define PFLAGS_TO_PROT(x) (MAYBE_MAP_FLAG((x), PF_X, PROT_EXEC) | \ 116c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner MAYBE_MAP_FLAG((x), PF_R, PROT_READ) | \ 117c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner MAYBE_MAP_FLAG((x), PF_W, PROT_WRITE)) 118c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 119650be4e584eeab3591b9e273bfd6d169eea60853Elliott HughesElfReader::ElfReader(const char* name, int fd) 120650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes : name_(name), fd_(fd), 121650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes phdr_num_(0), phdr_mmap_(NULL), phdr_table_(NULL), phdr_size_(0), 122650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes load_start_(NULL), load_size_(0), load_bias_(0), 123650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes loaded_phdr_(NULL) { 124650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes} 125c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 126650be4e584eeab3591b9e273bfd6d169eea60853Elliott HughesElfReader::~ElfReader() { 127650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (fd_ != -1) { 128650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes close(fd_); 129650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 130650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (phdr_mmap_ != NULL) { 131650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes munmap(phdr_mmap_, phdr_size_); 132650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 133650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes} 134c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 135650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughesbool ElfReader::Load() { 136650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return ReadElfHeader() && 137650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes VerifyElfHeader() && 138650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes ReadProgramHeader() && 139650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes ReserveAddressSpace() && 140650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes LoadSegments() && 141650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes FindPhdr(); 142650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes} 143c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 144650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughesbool ElfReader::ReadElfHeader() { 145650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes ssize_t rc = TEMP_FAILURE_RETRY(read(fd_, &header_, sizeof(header_))); 146650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (rc < 0) { 147650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes DL_ERR("can't read file \"%s\": %s", name_, strerror(errno)); 148650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return false; 149650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 150650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (rc != sizeof(header_)) { 151c620059479c47a78d57086d73726c9adc2f337adElliott Hughes DL_ERR("\"%s\" is too small to be an ELF executable: only found %zd bytes", name_, 152c620059479c47a78d57086d73726c9adc2f337adElliott Hughes static_cast<size_t>(rc)); 153650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return false; 154650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 155650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return true; 156c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 157c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 158650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughesbool ElfReader::VerifyElfHeader() { 159650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (header_.e_ident[EI_MAG0] != ELFMAG0 || 160650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes header_.e_ident[EI_MAG1] != ELFMAG1 || 161650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes header_.e_ident[EI_MAG2] != ELFMAG2 || 162650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes header_.e_ident[EI_MAG3] != ELFMAG3) { 163650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes DL_ERR("\"%s\" has bad ELF magic", name_); 164650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return false; 165650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 166650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 167c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes // Try to give a clear diagnostic for ELF class mismatches, since they're 168c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes // an easy mistake to make during the 32-bit/64-bit transition period. 169c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes int elf_class = header_.e_ident[EI_CLASS]; 170c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes#if defined(__LP64__) 171c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes if (elf_class != ELFCLASS64) { 172c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes if (elf_class == ELFCLASS32) { 173c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes DL_ERR("\"%s\" is 32-bit instead of 64-bit", name_); 174c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes } else { 175c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes DL_ERR("\"%s\" has unknown ELF class: %d", name_, elf_class); 176c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes } 177c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes return false; 178c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes } 179c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes#else 180c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes if (elf_class != ELFCLASS32) { 181c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes if (elf_class == ELFCLASS64) { 182c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes DL_ERR("\"%s\" is 64-bit instead of 32-bit", name_); 183c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes } else { 184c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes DL_ERR("\"%s\" has unknown ELF class: %d", name_, elf_class); 185c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes } 186650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return false; 187650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 188c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes#endif 189c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes 190650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (header_.e_ident[EI_DATA] != ELFDATA2LSB) { 191650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes DL_ERR("\"%s\" not little-endian: %d", name_, header_.e_ident[EI_DATA]); 192650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return false; 193650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 194650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 195650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (header_.e_type != ET_DYN) { 196650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes DL_ERR("\"%s\" has unexpected e_type: %d", name_, header_.e_type); 197650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return false; 198650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 199650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 200650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (header_.e_version != EV_CURRENT) { 201650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes DL_ERR("\"%s\" has unexpected e_version: %d", name_, header_.e_version); 202650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return false; 203650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 204650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 205e365f9d6543bc6607864ef61257505239dde15d1Marcus Oakland if (header_.e_machine != ELF_TARG_MACH) { 206650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes DL_ERR("\"%s\" has unexpected e_machine: %d", name_, header_.e_machine); 207650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return false; 208650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 209650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 210650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return true; 211c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 212c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 213650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes// Loads the program header table from an ELF file into a read-only private 214650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes// anonymous mmap-ed block. 215650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughesbool ElfReader::ReadProgramHeader() { 216650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes phdr_num_ = header_.e_phnum; 217650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 218650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes // Like the kernel, we only accept program header tables that 219650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes // are smaller than 64KiB. 2200266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes if (phdr_num_ < 1 || phdr_num_ > 65536/sizeof(ElfW(Phdr))) { 221c620059479c47a78d57086d73726c9adc2f337adElliott Hughes DL_ERR("\"%s\" has invalid e_phnum: %zd", name_, phdr_num_); 222650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return false; 223650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 224650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 2250266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) page_min = PAGE_START(header_.e_phoff); 2260266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) page_max = PAGE_END(header_.e_phoff + (phdr_num_ * sizeof(ElfW(Phdr)))); 2270266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) page_offset = PAGE_OFFSET(header_.e_phoff); 228650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 229650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes phdr_size_ = page_max - page_min; 230650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 231650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes void* mmap_result = mmap(NULL, phdr_size_, PROT_READ, MAP_PRIVATE, fd_, page_min); 232650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (mmap_result == MAP_FAILED) { 233650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes DL_ERR("\"%s\" phdr mmap failed: %s", name_, strerror(errno)); 234650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return false; 235650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 236650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 237650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes phdr_mmap_ = mmap_result; 2380266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes phdr_table_ = reinterpret_cast<ElfW(Phdr)*>(reinterpret_cast<char*>(mmap_result) + page_offset); 239650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return true; 240650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes} 241c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 242e7dffe150b3c1c835c669ec03965e37fead13de7Brian Carlstrom/* Returns the size of the extent of all the possibly non-contiguous 243e7dffe150b3c1c835c669ec03965e37fead13de7Brian Carlstrom * loadable segments in an ELF program header table. This corresponds 244e7dffe150b3c1c835c669ec03965e37fead13de7Brian Carlstrom * to the page-aligned size in bytes that needs to be reserved in the 245e7dffe150b3c1c835c669ec03965e37fead13de7Brian Carlstrom * process' address space. If there are no loadable segments, 0 is 246e7dffe150b3c1c835c669ec03965e37fead13de7Brian Carlstrom * returned. 247c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 248e7dffe150b3c1c835c669ec03965e37fead13de7Brian Carlstrom * If out_min_vaddr or out_max_vaddr are non-NULL, they will be 249e7dffe150b3c1c835c669ec03965e37fead13de7Brian Carlstrom * set to the minimum and maximum addresses of pages to be reserved, 250e7dffe150b3c1c835c669ec03965e37fead13de7Brian Carlstrom * or 0 if there is nothing to load. 251c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 2520266ae5f884d72da58f33a072e865ba131234a5eElliott Hughessize_t phdr_table_get_load_size(const ElfW(Phdr)* phdr_table, size_t phdr_count, 2530266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr)* out_min_vaddr, 2540266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr)* out_max_vaddr) { 2550266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) min_vaddr = UINTPTR_MAX; 2560266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) max_vaddr = 0; 257c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 2580266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes bool found_pt_load = false; 2590266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes for (size_t i = 0; i < phdr_count; ++i) { 2600266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes const ElfW(Phdr)* phdr = &phdr_table[i]; 261c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 2620266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes if (phdr->p_type != PT_LOAD) { 2630266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes continue; 264e7dffe150b3c1c835c669ec03965e37fead13de7Brian Carlstrom } 2650266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes found_pt_load = true; 2660266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 2670266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes if (phdr->p_vaddr < min_vaddr) { 2680266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes min_vaddr = phdr->p_vaddr; 269e7dffe150b3c1c835c669ec03965e37fead13de7Brian Carlstrom } 2700266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 2710266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes if (phdr->p_vaddr + phdr->p_memsz > max_vaddr) { 2720266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes max_vaddr = phdr->p_vaddr + phdr->p_memsz; 2730266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes } 2740266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes } 2750266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes if (!found_pt_load) { 2760266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes min_vaddr = 0; 2770266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes } 2780266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 2790266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes min_vaddr = PAGE_START(min_vaddr); 2800266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes max_vaddr = PAGE_END(max_vaddr); 2810266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 2820266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes if (out_min_vaddr != NULL) { 2830266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes *out_min_vaddr = min_vaddr; 2840266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes } 2850266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes if (out_max_vaddr != NULL) { 2860266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes *out_max_vaddr = max_vaddr; 2870266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes } 2880266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes return max_vaddr - min_vaddr; 289c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 290c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 291650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes// Reserve a virtual address range big enough to hold all loadable 292650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes// segments of a program header table. This is done by creating a 293650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes// private anonymous mmap() with PROT_NONE. 294650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughesbool ElfReader::ReserveAddressSpace() { 2950266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) min_vaddr; 296e7dffe150b3c1c835c669ec03965e37fead13de7Brian Carlstrom load_size_ = phdr_table_get_load_size(phdr_table_, phdr_num_, &min_vaddr); 297650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (load_size_ == 0) { 298650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes DL_ERR("\"%s\" has no loadable segments", name_); 299650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return false; 300650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 301650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 302e7dffe150b3c1c835c669ec03965e37fead13de7Brian Carlstrom uint8_t* addr = reinterpret_cast<uint8_t*>(min_vaddr); 303650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS; 304e7dffe150b3c1c835c669ec03965e37fead13de7Brian Carlstrom void* start = mmap(addr, load_size_, PROT_NONE, mmap_flags, -1, 0); 305650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (start == MAP_FAILED) { 306c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes DL_ERR("couldn't reserve %zd bytes of address space for \"%s\"", load_size_, name_); 307650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return false; 308650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 309650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 310650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes load_start_ = start; 311e7dffe150b3c1c835c669ec03965e37fead13de7Brian Carlstrom load_bias_ = reinterpret_cast<uint8_t*>(start) - addr; 312650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return true; 313c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 314c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 315650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughesbool ElfReader::LoadSegments() { 316650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes for (size_t i = 0; i < phdr_num_; ++i) { 3170266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes const ElfW(Phdr)* phdr = &phdr_table_[i]; 318c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 319650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (phdr->p_type != PT_LOAD) { 320650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes continue; 321650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 322c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 323650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes // Segment addresses in memory. 3240266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) seg_start = phdr->p_vaddr + load_bias_; 3250266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) seg_end = seg_start + phdr->p_memsz; 326c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 3270266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) seg_page_start = PAGE_START(seg_start); 3280266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) seg_page_end = PAGE_END(seg_end); 329c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 3300266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) seg_file_end = seg_start + phdr->p_filesz; 331c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 332650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes // File offsets. 3330266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) file_start = phdr->p_offset; 3340266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) file_end = file_start + phdr->p_filesz; 335c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 3360266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) file_page_start = PAGE_START(file_start); 3370266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) file_length = file_end - file_page_start; 33882dcc7910d9c25c4fdf635d6132fa86ae3677363Brian Carlstrom 33982dcc7910d9c25c4fdf635d6132fa86ae3677363Brian Carlstrom if (file_length != 0) { 340faf05bacd45719291b371f24b1b89543881b37f6Elliott Hughes void* seg_addr = mmap(reinterpret_cast<void*>(seg_page_start), 34182dcc7910d9c25c4fdf635d6132fa86ae3677363Brian Carlstrom file_length, 34282dcc7910d9c25c4fdf635d6132fa86ae3677363Brian Carlstrom PFLAGS_TO_PROT(phdr->p_flags), 34382dcc7910d9c25c4fdf635d6132fa86ae3677363Brian Carlstrom MAP_FIXED|MAP_PRIVATE, 34482dcc7910d9c25c4fdf635d6132fa86ae3677363Brian Carlstrom fd_, 34582dcc7910d9c25c4fdf635d6132fa86ae3677363Brian Carlstrom file_page_start); 34682dcc7910d9c25c4fdf635d6132fa86ae3677363Brian Carlstrom if (seg_addr == MAP_FAILED) { 347c620059479c47a78d57086d73726c9adc2f337adElliott Hughes DL_ERR("couldn't map \"%s\" segment %zd: %s", name_, i, strerror(errno)); 34882dcc7910d9c25c4fdf635d6132fa86ae3677363Brian Carlstrom return false; 34982dcc7910d9c25c4fdf635d6132fa86ae3677363Brian Carlstrom } 350650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 351c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 352650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes // if the segment is writable, and does not end on a page boundary, 353650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes // zero-fill it until the page limit. 354650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if ((phdr->p_flags & PF_W) != 0 && PAGE_OFFSET(seg_file_end) > 0) { 355faf05bacd45719291b371f24b1b89543881b37f6Elliott Hughes memset(reinterpret_cast<void*>(seg_file_end), 0, PAGE_SIZE - PAGE_OFFSET(seg_file_end)); 356650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 357c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 358650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes seg_file_end = PAGE_END(seg_file_end); 359650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 360650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes // seg_file_end is now the first page address after the file 361650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes // content. If seg_end is larger, we need to zero anything 362650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes // between them. This is done by using a private anonymous 363650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes // map for all extra pages. 364650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (seg_page_end > seg_file_end) { 365faf05bacd45719291b371f24b1b89543881b37f6Elliott Hughes void* zeromap = mmap(reinterpret_cast<void*>(seg_file_end), 366650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes seg_page_end - seg_file_end, 367650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes PFLAGS_TO_PROT(phdr->p_flags), 368650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, 369650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes -1, 370650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 0); 371650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (zeromap == MAP_FAILED) { 372650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes DL_ERR("couldn't zero fill \"%s\" gap: %s", name_, strerror(errno)); 373650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return false; 374650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 375c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 376650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 377650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return true; 378c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 379c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 380105bc26fa6e5f6a946a2ff144ae316e69c6ce08eElliott Hughes/* Used internally. Used to set the protection bits of all loaded segments 381c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * with optional extra flags (i.e. really PROT_WRITE). Used by 382c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_table_protect_segments and phdr_table_unprotect_segments. 383c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 3840266ae5f884d72da58f33a072e865ba131234a5eElliott Hughesstatic int _phdr_table_set_load_prot(const ElfW(Phdr)* phdr_table, size_t phdr_count, 3850266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) load_bias, int extra_prot_flags) { 3860266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes const ElfW(Phdr)* phdr = phdr_table; 3870266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes const ElfW(Phdr)* phdr_limit = phdr + phdr_count; 3880266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 3890266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes for (; phdr < phdr_limit; phdr++) { 3900266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes if (phdr->p_type != PT_LOAD || (phdr->p_flags & PF_W) != 0) { 3910266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes continue; 392c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 3930266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 3940266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias; 3950266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) seg_page_end = PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias; 3960266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 397faf05bacd45719291b371f24b1b89543881b37f6Elliott Hughes int ret = mprotect(reinterpret_cast<void*>(seg_page_start), 3980266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes seg_page_end - seg_page_start, 3990266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes PFLAGS_TO_PROT(phdr->p_flags) | extra_prot_flags); 4000266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes if (ret < 0) { 4010266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes return -1; 4020266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes } 4030266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes } 4040266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes return 0; 405c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 406c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 407c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* Restore the original protection modes for all loadable segments. 408c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * You should only call this after phdr_table_unprotect_segments and 409c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * applying all relocations. 410c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 411c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Input: 412c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_table -> program header table 413105bc26fa6e5f6a946a2ff144ae316e69c6ce08eElliott Hughes * phdr_count -> number of entries in tables 414c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * load_bias -> load bias 415c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Return: 416c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 0 on error, -1 on failure (error code in errno). 417c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 4180266ae5f884d72da58f33a072e865ba131234a5eElliott Hughesint phdr_table_protect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count, ElfW(Addr) load_bias) { 4190266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes return _phdr_table_set_load_prot(phdr_table, phdr_count, load_bias, 0); 420c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 421c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 422c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* Change the protection of all loaded segments in memory to writable. 423c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * This is useful before performing relocations. Once completed, you 424c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * will have to call phdr_table_protect_segments to restore the original 425c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * protection flags on all segments. 426c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 427c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Note that some writable segments can also have their content turned 428c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * to read-only by calling phdr_table_protect_gnu_relro. This is no 429c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * performed here. 430c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 431c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Input: 432c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_table -> program header table 433105bc26fa6e5f6a946a2ff144ae316e69c6ce08eElliott Hughes * phdr_count -> number of entries in tables 434c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * load_bias -> load bias 435c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Return: 436c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 0 on error, -1 on failure (error code in errno). 437c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 4380266ae5f884d72da58f33a072e865ba131234a5eElliott Hughesint phdr_table_unprotect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count, ElfW(Addr) load_bias) { 4390266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes return _phdr_table_set_load_prot(phdr_table, phdr_count, load_bias, PROT_WRITE); 440c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 441c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 442c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* Used internally by phdr_table_protect_gnu_relro and 443c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_table_unprotect_gnu_relro. 444c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 4450266ae5f884d72da58f33a072e865ba131234a5eElliott Hughesstatic int _phdr_table_set_gnu_relro_prot(const ElfW(Phdr)* phdr_table, size_t phdr_count, 4460266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) load_bias, int prot_flags) { 4470266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes const ElfW(Phdr)* phdr = phdr_table; 4480266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes const ElfW(Phdr)* phdr_limit = phdr + phdr_count; 4490266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 4500266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes for (phdr = phdr_table; phdr < phdr_limit; phdr++) { 4510266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes if (phdr->p_type != PT_GNU_RELRO) { 4520266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes continue; 453c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 4540266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 4550266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes // Tricky: what happens when the relro segment does not start 4560266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes // or end at page boundaries? We're going to be over-protective 4570266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes // here and put every page touched by the segment as read-only. 4580266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 4590266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes // This seems to match Ian Lance Taylor's description of the 4600266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes // feature at http://www.airs.com/blog/archives/189. 4610266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 4620266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes // Extract: 4630266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes // Note that the current dynamic linker code will only work 4640266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes // correctly if the PT_GNU_RELRO segment starts on a page 4650266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes // boundary. This is because the dynamic linker rounds the 4660266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes // p_vaddr field down to the previous page boundary. If 4670266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes // there is anything on the page which should not be read-only, 4680266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes // the program is likely to fail at runtime. So in effect the 4690266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes // linker must only emit a PT_GNU_RELRO segment if it ensures 4700266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes // that it starts on a page boundary. 4710266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias; 4720266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) seg_page_end = PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias; 4730266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 474faf05bacd45719291b371f24b1b89543881b37f6Elliott Hughes int ret = mprotect(reinterpret_cast<void*>(seg_page_start), 4750266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes seg_page_end - seg_page_start, 4760266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes prot_flags); 4770266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes if (ret < 0) { 4780266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes return -1; 4790266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes } 4800266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes } 4810266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes return 0; 482c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 483c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 484c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* Apply GNU relro protection if specified by the program header. This will 485c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * turn some of the pages of a writable PT_LOAD segment to read-only, as 486c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * specified by one or more PT_GNU_RELRO segments. This must be always 487c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * performed after relocations. 488c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 48912c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel * The areas typically covered are .got and .data.rel.ro, these are 49012c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel * read-only from the program's POV, but contain absolute addresses 49112c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel * that need to be relocated before use. 492c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 493c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Input: 494c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_table -> program header table 495105bc26fa6e5f6a946a2ff144ae316e69c6ce08eElliott Hughes * phdr_count -> number of entries in tables 496c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * load_bias -> load bias 497c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Return: 498c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 0 on error, -1 on failure (error code in errno). 499c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 5000266ae5f884d72da58f33a072e865ba131234a5eElliott Hughesint phdr_table_protect_gnu_relro(const ElfW(Phdr)* phdr_table, size_t phdr_count, ElfW(Addr) load_bias) { 5010266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes return _phdr_table_set_gnu_relro_prot(phdr_table, phdr_count, load_bias, PROT_READ); 502c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 503c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 5044eeb1f12a8b63afc0d0ad4d466b16fbffb21cd5aElliott Hughes#if defined(__arm__) 505c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 506c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner# ifndef PT_ARM_EXIDX 507c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner# define PT_ARM_EXIDX 0x70000001 /* .ARM.exidx segment */ 508c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner# endif 509c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 510c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* Return the address and size of the .ARM.exidx section in memory, 511c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * if present. 512c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 513c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Input: 514c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_table -> program header table 515105bc26fa6e5f6a946a2ff144ae316e69c6ce08eElliott Hughes * phdr_count -> number of entries in tables 516c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * load_bias -> load bias 517c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Output: 518c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * arm_exidx -> address of table in memory (NULL on failure). 519c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * arm_exidx_count -> number of items in table (0 on failure). 520c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Return: 521c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 0 on error, -1 on failure (_no_ error code in errno) 522c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 5230266ae5f884d72da58f33a072e865ba131234a5eElliott Hughesint phdr_table_get_arm_exidx(const ElfW(Phdr)* phdr_table, size_t phdr_count, 5240266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) load_bias, 5250266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr)** arm_exidx, unsigned* arm_exidx_count) { 5260266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes const ElfW(Phdr)* phdr = phdr_table; 5270266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes const ElfW(Phdr)* phdr_limit = phdr + phdr_count; 5280266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 5290266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes for (phdr = phdr_table; phdr < phdr_limit; phdr++) { 5300266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes if (phdr->p_type != PT_ARM_EXIDX) { 5310266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes continue; 532c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 5330266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 5340266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes *arm_exidx = reinterpret_cast<ElfW(Addr)*>(load_bias + phdr->p_vaddr); 5350266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes *arm_exidx_count = (unsigned)(phdr->p_memsz / 8); 5360266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes return 0; 5370266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes } 5380266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes *arm_exidx = NULL; 5390266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes *arm_exidx_count = 0; 5400266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes return -1; 541c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 5424eeb1f12a8b63afc0d0ad4d466b16fbffb21cd5aElliott Hughes#endif 543c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 54412c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel/* Return the address and size of the ELF file's .dynamic section in memory, 545c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * or NULL if missing. 546c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 547c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Input: 548c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_table -> program header table 549105bc26fa6e5f6a946a2ff144ae316e69c6ce08eElliott Hughes * phdr_count -> number of entries in tables 550c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * load_bias -> load bias 55112c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel * Output: 55212c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel * dynamic -> address of table in memory (NULL on failure). 55312c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel * dynamic_count -> number of items in table (0 on failure). 554cf23905a4bcc7bfdd109be5b6d69ad06877aa217Chris Dearman * dynamic_flags -> protection flags for section (unset on failure) 555c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Return: 55612c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel * void 557c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 5580266ae5f884d72da58f33a072e865ba131234a5eElliott Hughesvoid phdr_table_get_dynamic_section(const ElfW(Phdr)* phdr_table, size_t phdr_count, 5590266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) load_bias, 5600266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Dyn)** dynamic, size_t* dynamic_count, ElfW(Word)* dynamic_flags) { 5610266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes const ElfW(Phdr)* phdr = phdr_table; 5620266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes const ElfW(Phdr)* phdr_limit = phdr + phdr_count; 5630266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 5640266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes for (phdr = phdr_table; phdr < phdr_limit; phdr++) { 5650266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes if (phdr->p_type != PT_DYNAMIC) { 5660266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes continue; 56712c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel } 5680266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 5690266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes *dynamic = reinterpret_cast<ElfW(Dyn)*>(load_bias + phdr->p_vaddr); 57012c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel if (dynamic_count) { 5710266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes *dynamic_count = (unsigned)(phdr->p_memsz / 8); 5720266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes } 5730266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes if (dynamic_flags) { 5740266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes *dynamic_flags = phdr->p_flags; 575c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 5760266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes return; 5770266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes } 5780266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes *dynamic = NULL; 5790266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes if (dynamic_count) { 5800266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes *dynamic_count = 0; 5810266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes } 582c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 583c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 584650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes// Returns the address of the program header table as it appears in the loaded 585650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes// segments in memory. This is in contrast with 'phdr_table_' which 586650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes// is temporary and will be released before the library is relocated. 587650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughesbool ElfReader::FindPhdr() { 5880266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes const ElfW(Phdr)* phdr_limit = phdr_table_ + phdr_num_; 589c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 590650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes // If there is a PT_PHDR, use it directly. 5910266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes for (const ElfW(Phdr)* phdr = phdr_table_; phdr < phdr_limit; ++phdr) { 592650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (phdr->p_type == PT_PHDR) { 593650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return CheckPhdr(load_bias_ + phdr->p_vaddr); 594c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 595650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 596650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 597650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes // Otherwise, check the first loadable segment. If its file offset 598650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes // is 0, it starts with the ELF header, and we can trivially find the 599650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes // loaded program header from it. 6000266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes for (const ElfW(Phdr)* phdr = phdr_table_; phdr < phdr_limit; ++phdr) { 601650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (phdr->p_type == PT_LOAD) { 602650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (phdr->p_offset == 0) { 6030266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) elf_addr = load_bias_ + phdr->p_vaddr; 604faf05bacd45719291b371f24b1b89543881b37f6Elliott Hughes const ElfW(Ehdr)* ehdr = reinterpret_cast<const ElfW(Ehdr)*>(elf_addr); 6050266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) offset = ehdr->e_phoff; 6060266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes return CheckPhdr((ElfW(Addr))ehdr + offset); 607650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 608650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes break; 609c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 610650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 611c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 612650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes DL_ERR("can't find loaded phdr for \"%s\"", name_); 613650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return false; 614650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes} 615c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 616650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes// Ensures that our program header is actually within a loadable 617650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes// segment. This should help catch badly-formed ELF files that 618650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes// would cause the linker to crash later when trying to access it. 6190266ae5f884d72da58f33a072e865ba131234a5eElliott Hughesbool ElfReader::CheckPhdr(ElfW(Addr) loaded) { 6200266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes const ElfW(Phdr)* phdr_limit = phdr_table_ + phdr_num_; 6210266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) loaded_end = loaded + (phdr_num_ * sizeof(ElfW(Phdr))); 6220266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes for (ElfW(Phdr)* phdr = phdr_table_; phdr < phdr_limit; ++phdr) { 623650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (phdr->p_type != PT_LOAD) { 624650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes continue; 625650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 6260266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) seg_start = phdr->p_vaddr + load_bias_; 6270266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) seg_end = phdr->p_filesz + seg_start; 628650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (seg_start <= loaded && loaded_end <= seg_end) { 6290266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes loaded_phdr_ = reinterpret_cast<const ElfW(Phdr)*>(loaded); 630650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return true; 631c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 632650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 633c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes DL_ERR("\"%s\" loaded phdr %p not in loadable segment", name_, reinterpret_cast<void*>(loaded)); 634650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return false; 635c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 636