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> 34183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles)#include <sys/types.h> 35183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles)#include <sys/stat.h> 36183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles)#include <unistd.h> 37c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 38650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes#include "linker.h" 39650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes#include "linker_debug.h" 40c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 41c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/** 42c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner TECHNICAL NOTE ON ELF LOADING. 43c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 44c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner An ELF file's program header table contains one or more PT_LOAD 45c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner segments, which corresponds to portions of the file that need to 46c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner be mapped into the process' address space. 47c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 48c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Each loadable segment has the following important properties: 49c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 50c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner p_offset -> segment file offset 51c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner p_filesz -> segment file size 52c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner p_memsz -> segment memory size (always >= p_filesz) 53c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner p_vaddr -> segment's virtual address 54c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner p_flags -> segment flags (e.g. readable, writable, executable) 55c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 560266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes We will ignore the p_paddr and p_align fields of ElfW(Phdr) for now. 57c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 58c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner The loadable segments can be seen as a list of [p_vaddr ... p_vaddr+p_memsz) 59c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner ranges of virtual addresses. A few rules apply: 60c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 61c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner - the virtual address ranges should not overlap. 62c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 63c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner - if a segment's p_filesz is smaller than its p_memsz, the extra bytes 64c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner between them should always be initialized to 0. 65c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 66c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner - ranges do not necessarily start or end at page boundaries. Two distinct 67c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner segments can have their start and end on the same page. In this case, the 68c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner page inherits the mapping flags of the latter segment. 69c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 70c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Finally, the real load addrs of each segment is not p_vaddr. Instead the 71c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner loader decides where to load the first segment, then will load all others 72c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner relative to the first one to respect the initial range layout. 73c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 74c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner For example, consider the following list: 75c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 76c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner [ offset:0, filesz:0x4000, memsz:0x4000, vaddr:0x30000 ], 77c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner [ offset:0x4000, filesz:0x2000, memsz:0x8000, vaddr:0x40000 ], 78c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 79c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner This corresponds to two segments that cover these virtual address ranges: 80c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 81c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 0x30000...0x34000 82c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 0x40000...0x48000 83c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 84c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner If the loader decides to load the first segment at address 0xa0000000 85c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner then the segments' load address ranges will be: 86c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 87c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 0xa0030000...0xa0034000 88c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 0xa0040000...0xa0048000 89c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 90c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner In other words, all segments must be loaded at an address that has the same 91c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner constant offset from their p_vaddr value. This offset is computed as the 92c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner difference between the first segment's load address, and its p_vaddr value. 93c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 94c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner However, in practice, segments do _not_ start at page boundaries. Since we 95c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner can only memory-map at page boundaries, this means that the bias is 96c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner computed as: 97c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 98c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner load_bias = phdr0_load_address - PAGE_START(phdr0->p_vaddr) 99c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 100c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner (NOTE: The value must be used as a 32-bit unsigned integer, to deal with 101c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner possible wrap around UINT32_MAX for possible large p_vaddr values). 102c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 103c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner And that the phdr0_load_address must start at a page boundary, with 104c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner the segment's real content starting at: 105c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 106c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner phdr0_load_address + PAGE_OFFSET(phdr0->p_vaddr) 107c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 108c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner Note that ELF requires the following condition to make the mmap()-ing work: 109c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 110c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner PAGE_OFFSET(phdr0->p_vaddr) == PAGE_OFFSET(phdr0->p_offset) 111c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 112c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner The load_bias must be added to any p_vaddr value read from the ELF file to 113c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner determine the corresponding memory address. 114c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 115c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner **/ 116c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 117faf05bacd45719291b371f24b1b89543881b37f6Elliott Hughes#define MAYBE_MAP_FLAG(x, from, to) (((x) & (from)) ? (to) : 0) 118c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner#define PFLAGS_TO_PROT(x) (MAYBE_MAP_FLAG((x), PF_X, PROT_EXEC) | \ 119c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner MAYBE_MAP_FLAG((x), PF_R, PROT_READ) | \ 120c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner MAYBE_MAP_FLAG((x), PF_W, PROT_WRITE)) 121c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 122650be4e584eeab3591b9e273bfd6d169eea60853Elliott HughesElfReader::ElfReader(const char* name, int fd) 123650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes : name_(name), fd_(fd), 124650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes phdr_num_(0), phdr_mmap_(NULL), phdr_table_(NULL), phdr_size_(0), 125650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes load_start_(NULL), load_size_(0), load_bias_(0), 126650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes loaded_phdr_(NULL) { 127650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes} 128c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 129650be4e584eeab3591b9e273bfd6d169eea60853Elliott HughesElfReader::~ElfReader() { 130650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (phdr_mmap_ != NULL) { 131650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes munmap(phdr_mmap_, phdr_size_); 132650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 133650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes} 134c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 13512bbb9164578b6512b8b07a3fb093858244b7c7bTorne (Richard Coles)bool ElfReader::Load(const android_dlextinfo* extinfo) { 136650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return ReadElfHeader() && 137650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes VerifyElfHeader() && 138650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes ReadProgramHeader() && 13912bbb9164578b6512b8b07a3fb093858244b7c7bTorne (Richard Coles) ReserveAddressSpace(extinfo) && 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() { 1593002131da33401cf1b45abbdbec58b7c751fc43aElliott Hughes if (memcmp(header_.e_ident, ELFMAG, SELFMAG) != 0) { 160650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes DL_ERR("\"%s\" has bad ELF magic", name_); 161650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return false; 162650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 163650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 164c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes // Try to give a clear diagnostic for ELF class mismatches, since they're 165c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes // an easy mistake to make during the 32-bit/64-bit transition period. 166c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes int elf_class = header_.e_ident[EI_CLASS]; 167c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes#if defined(__LP64__) 168c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes if (elf_class != ELFCLASS64) { 169c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes if (elf_class == ELFCLASS32) { 170c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes DL_ERR("\"%s\" is 32-bit instead of 64-bit", name_); 171c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes } else { 172c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes DL_ERR("\"%s\" has unknown ELF class: %d", name_, elf_class); 173c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes } 174c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes return false; 175c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes } 176c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes#else 177c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes if (elf_class != ELFCLASS32) { 178c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes if (elf_class == ELFCLASS64) { 179c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes DL_ERR("\"%s\" is 64-bit instead of 32-bit", name_); 180c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes } else { 181c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes DL_ERR("\"%s\" has unknown ELF class: %d", name_, elf_class); 182c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes } 183650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return false; 184650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 185c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes#endif 186c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes 187650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (header_.e_ident[EI_DATA] != ELFDATA2LSB) { 188650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes DL_ERR("\"%s\" not little-endian: %d", name_, header_.e_ident[EI_DATA]); 189650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return false; 190650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 191650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 192650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (header_.e_type != ET_DYN) { 193650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes DL_ERR("\"%s\" has unexpected e_type: %d", name_, header_.e_type); 194650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return false; 195650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 196650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 197650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (header_.e_version != EV_CURRENT) { 198650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes DL_ERR("\"%s\" has unexpected e_version: %d", name_, header_.e_version); 199650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return false; 200650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 201650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 202e365f9d6543bc6607864ef61257505239dde15d1Marcus Oakland if (header_.e_machine != ELF_TARG_MACH) { 203650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes DL_ERR("\"%s\" has unexpected e_machine: %d", name_, header_.e_machine); 204650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return false; 205650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 206650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 207650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return true; 208c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 209c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 210650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes// Loads the program header table from an ELF file into a read-only private 211650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes// anonymous mmap-ed block. 212650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughesbool ElfReader::ReadProgramHeader() { 213650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes phdr_num_ = header_.e_phnum; 214650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 215650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes // Like the kernel, we only accept program header tables that 216650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes // are smaller than 64KiB. 2170266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes if (phdr_num_ < 1 || phdr_num_ > 65536/sizeof(ElfW(Phdr))) { 218c620059479c47a78d57086d73726c9adc2f337adElliott Hughes DL_ERR("\"%s\" has invalid e_phnum: %zd", name_, phdr_num_); 219650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return false; 220650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 221650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 2220266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) page_min = PAGE_START(header_.e_phoff); 2230266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) page_max = PAGE_END(header_.e_phoff + (phdr_num_ * sizeof(ElfW(Phdr)))); 2240266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) page_offset = PAGE_OFFSET(header_.e_phoff); 225650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 226650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes phdr_size_ = page_max - page_min; 227650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 228650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes void* mmap_result = mmap(NULL, phdr_size_, PROT_READ, MAP_PRIVATE, fd_, page_min); 229650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (mmap_result == MAP_FAILED) { 230650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes DL_ERR("\"%s\" phdr mmap failed: %s", name_, strerror(errno)); 231650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return false; 232650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 233650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 234650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes phdr_mmap_ = mmap_result; 2350266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes phdr_table_ = reinterpret_cast<ElfW(Phdr)*>(reinterpret_cast<char*>(mmap_result) + page_offset); 236650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return true; 237650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes} 238c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 239e7dffe150b3c1c835c669ec03965e37fead13de7Brian Carlstrom/* Returns the size of the extent of all the possibly non-contiguous 240e7dffe150b3c1c835c669ec03965e37fead13de7Brian Carlstrom * loadable segments in an ELF program header table. This corresponds 241e7dffe150b3c1c835c669ec03965e37fead13de7Brian Carlstrom * to the page-aligned size in bytes that needs to be reserved in the 242e7dffe150b3c1c835c669ec03965e37fead13de7Brian Carlstrom * process' address space. If there are no loadable segments, 0 is 243e7dffe150b3c1c835c669ec03965e37fead13de7Brian Carlstrom * returned. 244c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 245e7dffe150b3c1c835c669ec03965e37fead13de7Brian Carlstrom * If out_min_vaddr or out_max_vaddr are non-NULL, they will be 246e7dffe150b3c1c835c669ec03965e37fead13de7Brian Carlstrom * set to the minimum and maximum addresses of pages to be reserved, 247e7dffe150b3c1c835c669ec03965e37fead13de7Brian Carlstrom * or 0 if there is nothing to load. 248c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 2490266ae5f884d72da58f33a072e865ba131234a5eElliott Hughessize_t phdr_table_get_load_size(const ElfW(Phdr)* phdr_table, size_t phdr_count, 2500266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr)* out_min_vaddr, 2510266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr)* out_max_vaddr) { 2520266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) min_vaddr = UINTPTR_MAX; 2530266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) max_vaddr = 0; 254c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 2550266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes bool found_pt_load = false; 2560266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes for (size_t i = 0; i < phdr_count; ++i) { 2570266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes const ElfW(Phdr)* phdr = &phdr_table[i]; 258c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 2590266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes if (phdr->p_type != PT_LOAD) { 2600266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes continue; 261e7dffe150b3c1c835c669ec03965e37fead13de7Brian Carlstrom } 2620266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes found_pt_load = true; 2630266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 2640266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes if (phdr->p_vaddr < min_vaddr) { 2650266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes min_vaddr = phdr->p_vaddr; 266e7dffe150b3c1c835c669ec03965e37fead13de7Brian Carlstrom } 2670266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 2680266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes if (phdr->p_vaddr + phdr->p_memsz > max_vaddr) { 2690266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes max_vaddr = phdr->p_vaddr + phdr->p_memsz; 2700266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes } 2710266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes } 2720266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes if (!found_pt_load) { 2730266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes min_vaddr = 0; 2740266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes } 2750266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 2760266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes min_vaddr = PAGE_START(min_vaddr); 2770266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes max_vaddr = PAGE_END(max_vaddr); 2780266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 2790266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes if (out_min_vaddr != NULL) { 2800266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes *out_min_vaddr = min_vaddr; 2810266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes } 2820266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes if (out_max_vaddr != NULL) { 2830266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes *out_max_vaddr = max_vaddr; 2840266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes } 2850266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes return max_vaddr - min_vaddr; 286c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 287c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 288650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes// Reserve a virtual address range big enough to hold all loadable 289650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes// segments of a program header table. This is done by creating a 290650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes// private anonymous mmap() with PROT_NONE. 29112bbb9164578b6512b8b07a3fb093858244b7c7bTorne (Richard Coles)bool ElfReader::ReserveAddressSpace(const android_dlextinfo* extinfo) { 2920266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) min_vaddr; 293e7dffe150b3c1c835c669ec03965e37fead13de7Brian Carlstrom load_size_ = phdr_table_get_load_size(phdr_table_, phdr_num_, &min_vaddr); 294650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (load_size_ == 0) { 295650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes DL_ERR("\"%s\" has no loadable segments", name_); 296650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return false; 297650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 298650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 299e7dffe150b3c1c835c669ec03965e37fead13de7Brian Carlstrom uint8_t* addr = reinterpret_cast<uint8_t*>(min_vaddr); 30012bbb9164578b6512b8b07a3fb093858244b7c7bTorne (Richard Coles) void* start; 30112bbb9164578b6512b8b07a3fb093858244b7c7bTorne (Richard Coles) size_t reserved_size = 0; 30212bbb9164578b6512b8b07a3fb093858244b7c7bTorne (Richard Coles) bool reserved_hint = true; 30312bbb9164578b6512b8b07a3fb093858244b7c7bTorne (Richard Coles) 30412bbb9164578b6512b8b07a3fb093858244b7c7bTorne (Richard Coles) if (extinfo != NULL) { 30512bbb9164578b6512b8b07a3fb093858244b7c7bTorne (Richard Coles) if (extinfo->flags & ANDROID_DLEXT_RESERVED_ADDRESS) { 30612bbb9164578b6512b8b07a3fb093858244b7c7bTorne (Richard Coles) reserved_size = extinfo->reserved_size; 30712bbb9164578b6512b8b07a3fb093858244b7c7bTorne (Richard Coles) reserved_hint = false; 30812bbb9164578b6512b8b07a3fb093858244b7c7bTorne (Richard Coles) } else if (extinfo->flags & ANDROID_DLEXT_RESERVED_ADDRESS_HINT) { 30912bbb9164578b6512b8b07a3fb093858244b7c7bTorne (Richard Coles) reserved_size = extinfo->reserved_size; 31012bbb9164578b6512b8b07a3fb093858244b7c7bTorne (Richard Coles) } 31112bbb9164578b6512b8b07a3fb093858244b7c7bTorne (Richard Coles) } 31212bbb9164578b6512b8b07a3fb093858244b7c7bTorne (Richard Coles) 31312bbb9164578b6512b8b07a3fb093858244b7c7bTorne (Richard Coles) if (load_size_ > reserved_size) { 31412bbb9164578b6512b8b07a3fb093858244b7c7bTorne (Richard Coles) if (!reserved_hint) { 31512bbb9164578b6512b8b07a3fb093858244b7c7bTorne (Richard Coles) DL_ERR("reserved address space %zd smaller than %zd bytes needed for \"%s\"", 31612bbb9164578b6512b8b07a3fb093858244b7c7bTorne (Richard Coles) reserved_size - load_size_, load_size_, name_); 31712bbb9164578b6512b8b07a3fb093858244b7c7bTorne (Richard Coles) return false; 31812bbb9164578b6512b8b07a3fb093858244b7c7bTorne (Richard Coles) } 31912bbb9164578b6512b8b07a3fb093858244b7c7bTorne (Richard Coles) int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS; 32012bbb9164578b6512b8b07a3fb093858244b7c7bTorne (Richard Coles) start = mmap(addr, load_size_, PROT_NONE, mmap_flags, -1, 0); 32112bbb9164578b6512b8b07a3fb093858244b7c7bTorne (Richard Coles) if (start == MAP_FAILED) { 32212bbb9164578b6512b8b07a3fb093858244b7c7bTorne (Richard Coles) DL_ERR("couldn't reserve %zd bytes of address space for \"%s\"", load_size_, name_); 32312bbb9164578b6512b8b07a3fb093858244b7c7bTorne (Richard Coles) return false; 32412bbb9164578b6512b8b07a3fb093858244b7c7bTorne (Richard Coles) } 32512bbb9164578b6512b8b07a3fb093858244b7c7bTorne (Richard Coles) } else { 32612bbb9164578b6512b8b07a3fb093858244b7c7bTorne (Richard Coles) start = extinfo->reserved_addr; 327650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 328650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 329650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes load_start_ = start; 330e7dffe150b3c1c835c669ec03965e37fead13de7Brian Carlstrom load_bias_ = reinterpret_cast<uint8_t*>(start) - addr; 331650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return true; 332c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 333c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 334650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughesbool ElfReader::LoadSegments() { 335650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes for (size_t i = 0; i < phdr_num_; ++i) { 3360266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes const ElfW(Phdr)* phdr = &phdr_table_[i]; 337c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 338650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (phdr->p_type != PT_LOAD) { 339650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes continue; 340650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 341c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 342650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes // Segment addresses in memory. 3430266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) seg_start = phdr->p_vaddr + load_bias_; 3440266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) seg_end = seg_start + phdr->p_memsz; 345c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 3460266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) seg_page_start = PAGE_START(seg_start); 3470266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) seg_page_end = PAGE_END(seg_end); 348c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 3490266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) seg_file_end = seg_start + phdr->p_filesz; 350c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 351650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes // File offsets. 3520266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) file_start = phdr->p_offset; 3530266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) file_end = file_start + phdr->p_filesz; 354c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 3550266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) file_page_start = PAGE_START(file_start); 3560266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) file_length = file_end - file_page_start; 35782dcc7910d9c25c4fdf635d6132fa86ae3677363Brian Carlstrom 35882dcc7910d9c25c4fdf635d6132fa86ae3677363Brian Carlstrom if (file_length != 0) { 359faf05bacd45719291b371f24b1b89543881b37f6Elliott Hughes void* seg_addr = mmap(reinterpret_cast<void*>(seg_page_start), 36082dcc7910d9c25c4fdf635d6132fa86ae3677363Brian Carlstrom file_length, 36182dcc7910d9c25c4fdf635d6132fa86ae3677363Brian Carlstrom PFLAGS_TO_PROT(phdr->p_flags), 36282dcc7910d9c25c4fdf635d6132fa86ae3677363Brian Carlstrom MAP_FIXED|MAP_PRIVATE, 36382dcc7910d9c25c4fdf635d6132fa86ae3677363Brian Carlstrom fd_, 36482dcc7910d9c25c4fdf635d6132fa86ae3677363Brian Carlstrom file_page_start); 36582dcc7910d9c25c4fdf635d6132fa86ae3677363Brian Carlstrom if (seg_addr == MAP_FAILED) { 366c620059479c47a78d57086d73726c9adc2f337adElliott Hughes DL_ERR("couldn't map \"%s\" segment %zd: %s", name_, i, strerror(errno)); 36782dcc7910d9c25c4fdf635d6132fa86ae3677363Brian Carlstrom return false; 36882dcc7910d9c25c4fdf635d6132fa86ae3677363Brian Carlstrom } 369650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 370c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 371650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes // if the segment is writable, and does not end on a page boundary, 372650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes // zero-fill it until the page limit. 373650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if ((phdr->p_flags & PF_W) != 0 && PAGE_OFFSET(seg_file_end) > 0) { 374faf05bacd45719291b371f24b1b89543881b37f6Elliott Hughes memset(reinterpret_cast<void*>(seg_file_end), 0, PAGE_SIZE - PAGE_OFFSET(seg_file_end)); 375650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 376c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 377650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes seg_file_end = PAGE_END(seg_file_end); 378650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 379650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes // seg_file_end is now the first page address after the file 380650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes // content. If seg_end is larger, we need to zero anything 381650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes // between them. This is done by using a private anonymous 382650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes // map for all extra pages. 383650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (seg_page_end > seg_file_end) { 384faf05bacd45719291b371f24b1b89543881b37f6Elliott Hughes void* zeromap = mmap(reinterpret_cast<void*>(seg_file_end), 385650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes seg_page_end - seg_file_end, 386650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes PFLAGS_TO_PROT(phdr->p_flags), 387650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, 388650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes -1, 389650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 0); 390650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (zeromap == MAP_FAILED) { 391650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes DL_ERR("couldn't zero fill \"%s\" gap: %s", name_, strerror(errno)); 392650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return false; 393650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 394c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 395650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 396650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return true; 397c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 398c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 399105bc26fa6e5f6a946a2ff144ae316e69c6ce08eElliott Hughes/* Used internally. Used to set the protection bits of all loaded segments 400c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * with optional extra flags (i.e. really PROT_WRITE). Used by 401c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_table_protect_segments and phdr_table_unprotect_segments. 402c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 4030266ae5f884d72da58f33a072e865ba131234a5eElliott Hughesstatic int _phdr_table_set_load_prot(const ElfW(Phdr)* phdr_table, size_t phdr_count, 4040266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) load_bias, int extra_prot_flags) { 4050266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes const ElfW(Phdr)* phdr = phdr_table; 4060266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes const ElfW(Phdr)* phdr_limit = phdr + phdr_count; 4070266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 4080266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes for (; phdr < phdr_limit; phdr++) { 4090266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes if (phdr->p_type != PT_LOAD || (phdr->p_flags & PF_W) != 0) { 4100266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes continue; 411c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 4120266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 4130266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias; 4140266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) seg_page_end = PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias; 4150266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 416faf05bacd45719291b371f24b1b89543881b37f6Elliott Hughes int ret = mprotect(reinterpret_cast<void*>(seg_page_start), 4170266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes seg_page_end - seg_page_start, 4180266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes PFLAGS_TO_PROT(phdr->p_flags) | extra_prot_flags); 4190266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes if (ret < 0) { 4200266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes return -1; 4210266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes } 4220266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes } 4230266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes return 0; 424c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 425c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 426c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* Restore the original protection modes for all loadable segments. 427c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * You should only call this after phdr_table_unprotect_segments and 428c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * applying all relocations. 429c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 430c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Input: 431c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_table -> program header table 432105bc26fa6e5f6a946a2ff144ae316e69c6ce08eElliott Hughes * phdr_count -> number of entries in tables 433c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * load_bias -> load bias 434c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Return: 435c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 0 on error, -1 on failure (error code in errno). 436c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 4370266ae5f884d72da58f33a072e865ba131234a5eElliott Hughesint phdr_table_protect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count, ElfW(Addr) load_bias) { 4380266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes return _phdr_table_set_load_prot(phdr_table, phdr_count, load_bias, 0); 439c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 440c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 441c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* Change the protection of all loaded segments in memory to writable. 442c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * This is useful before performing relocations. Once completed, you 443c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * will have to call phdr_table_protect_segments to restore the original 444c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * protection flags on all segments. 445c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 446c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Note that some writable segments can also have their content turned 447c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * to read-only by calling phdr_table_protect_gnu_relro. This is no 448c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * performed here. 449c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 450c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Input: 451c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_table -> program header table 452105bc26fa6e5f6a946a2ff144ae316e69c6ce08eElliott Hughes * phdr_count -> number of entries in tables 453c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * load_bias -> load bias 454c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Return: 455c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 0 on error, -1 on failure (error code in errno). 456c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 4570266ae5f884d72da58f33a072e865ba131234a5eElliott Hughesint phdr_table_unprotect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count, ElfW(Addr) load_bias) { 4580266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes return _phdr_table_set_load_prot(phdr_table, phdr_count, load_bias, PROT_WRITE); 459c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 460c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 461c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* Used internally by phdr_table_protect_gnu_relro and 462c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_table_unprotect_gnu_relro. 463c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 4640266ae5f884d72da58f33a072e865ba131234a5eElliott Hughesstatic int _phdr_table_set_gnu_relro_prot(const ElfW(Phdr)* phdr_table, size_t phdr_count, 4650266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) load_bias, int prot_flags) { 4660266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes const ElfW(Phdr)* phdr = phdr_table; 4670266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes const ElfW(Phdr)* phdr_limit = phdr + phdr_count; 4680266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 4690266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes for (phdr = phdr_table; phdr < phdr_limit; phdr++) { 4700266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes if (phdr->p_type != PT_GNU_RELRO) { 4710266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes continue; 472c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 4730266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 4740266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes // Tricky: what happens when the relro segment does not start 4750266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes // or end at page boundaries? We're going to be over-protective 4760266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes // here and put every page touched by the segment as read-only. 4770266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 4780266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes // This seems to match Ian Lance Taylor's description of the 4790266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes // feature at http://www.airs.com/blog/archives/189. 4800266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 4810266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes // Extract: 4820266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes // Note that the current dynamic linker code will only work 4830266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes // correctly if the PT_GNU_RELRO segment starts on a page 4840266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes // boundary. This is because the dynamic linker rounds the 4850266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes // p_vaddr field down to the previous page boundary. If 4860266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes // there is anything on the page which should not be read-only, 4870266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes // the program is likely to fail at runtime. So in effect the 4880266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes // linker must only emit a PT_GNU_RELRO segment if it ensures 4890266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes // that it starts on a page boundary. 4900266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias; 4910266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) seg_page_end = PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias; 4920266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 493faf05bacd45719291b371f24b1b89543881b37f6Elliott Hughes int ret = mprotect(reinterpret_cast<void*>(seg_page_start), 4940266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes seg_page_end - seg_page_start, 4950266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes prot_flags); 4960266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes if (ret < 0) { 4970266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes return -1; 4980266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes } 4990266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes } 5000266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes return 0; 501c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 502c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 503c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* Apply GNU relro protection if specified by the program header. This will 504c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * turn some of the pages of a writable PT_LOAD segment to read-only, as 505c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * specified by one or more PT_GNU_RELRO segments. This must be always 506c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * performed after relocations. 507c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 50812c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel * The areas typically covered are .got and .data.rel.ro, these are 50912c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel * read-only from the program's POV, but contain absolute addresses 51012c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel * that need to be relocated before use. 511c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 512c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Input: 513c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_table -> program header table 514105bc26fa6e5f6a946a2ff144ae316e69c6ce08eElliott Hughes * phdr_count -> number of entries in tables 515c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * load_bias -> load bias 516c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Return: 517c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 0 on error, -1 on failure (error code in errno). 518c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 5190266ae5f884d72da58f33a072e865ba131234a5eElliott Hughesint phdr_table_protect_gnu_relro(const ElfW(Phdr)* phdr_table, size_t phdr_count, ElfW(Addr) load_bias) { 5200266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes return _phdr_table_set_gnu_relro_prot(phdr_table, phdr_count, load_bias, PROT_READ); 521c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 522c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 523183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles)/* Serialize the GNU relro segments to the given file descriptor. This can be 524183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) * performed after relocations to allow another process to later share the 525183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) * relocated segment, if it was loaded at the same address. 526183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) * 527183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) * Input: 528183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) * phdr_table -> program header table 529183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) * phdr_count -> number of entries in tables 530183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) * load_bias -> load bias 531183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) * fd -> writable file descriptor to use 532183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) * Return: 533183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) * 0 on error, -1 on failure (error code in errno). 534183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) */ 535183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles)int phdr_table_serialize_gnu_relro(const ElfW(Phdr)* phdr_table, size_t phdr_count, ElfW(Addr) load_bias, 536183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) int fd) { 537183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) const ElfW(Phdr)* phdr = phdr_table; 538183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) const ElfW(Phdr)* phdr_limit = phdr + phdr_count; 539183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) ssize_t file_offset = 0; 540183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) 541183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) for (phdr = phdr_table; phdr < phdr_limit; phdr++) { 542183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) if (phdr->p_type != PT_GNU_RELRO) { 543183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) continue; 544183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) } 545183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) 546183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) ElfW(Addr) seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias; 547183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) ElfW(Addr) seg_page_end = PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias; 548183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) ssize_t size = seg_page_end - seg_page_start; 549183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) 550183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) ssize_t written = TEMP_FAILURE_RETRY(write(fd, reinterpret_cast<void*>(seg_page_start), size)); 551183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) if (written != size) { 552183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) return -1; 553183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) } 554183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) void* map = mmap(reinterpret_cast<void*>(seg_page_start), size, PROT_READ, 555183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) MAP_PRIVATE|MAP_FIXED, fd, file_offset); 556183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) if (map == MAP_FAILED) { 557183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) return -1; 558183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) } 559183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) file_offset += size; 560183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) } 561183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) return 0; 562183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles)} 563183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) 564183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles)/* Where possible, replace the GNU relro segments with mappings of the given 565183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) * file descriptor. This can be performed after relocations to allow a file 566183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) * previously created by phdr_table_serialize_gnu_relro in another process to 567183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) * replace the dirty relocated pages, saving memory, if it was loaded at the 568183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) * same address. We have to compare the data before we map over it, since some 569183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) * parts of the relro segment may not be identical due to other libraries in 570183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) * the process being loaded at different addresses. 571183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) * 572183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) * Input: 573183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) * phdr_table -> program header table 574183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) * phdr_count -> number of entries in tables 575183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) * load_bias -> load bias 576183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) * fd -> readable file descriptor to use 577183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) * Return: 578183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) * 0 on error, -1 on failure (error code in errno). 579183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) */ 580183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles)int phdr_table_map_gnu_relro(const ElfW(Phdr)* phdr_table, size_t phdr_count, ElfW(Addr) load_bias, 581183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) int fd) { 582183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) // Map the file at a temporary location so we can compare its contents. 583183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) struct stat file_stat; 584183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) if (TEMP_FAILURE_RETRY(fstat(fd, &file_stat)) != 0) { 585183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) return -1; 586183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) } 587183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) off_t file_size = file_stat.st_size; 58826ec9679ff01fb155ae21015f31cc95bed24f670Torne (Richard Coles) void* temp_mapping = NULL; 58926ec9679ff01fb155ae21015f31cc95bed24f670Torne (Richard Coles) if (file_size > 0) { 59026ec9679ff01fb155ae21015f31cc95bed24f670Torne (Richard Coles) temp_mapping = mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, fd, 0); 59126ec9679ff01fb155ae21015f31cc95bed24f670Torne (Richard Coles) if (temp_mapping == MAP_FAILED) { 59226ec9679ff01fb155ae21015f31cc95bed24f670Torne (Richard Coles) return -1; 59326ec9679ff01fb155ae21015f31cc95bed24f670Torne (Richard Coles) } 594183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) } 595183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) size_t file_offset = 0; 596183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) 597183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) // Iterate over the relro segments and compare/remap the pages. 598183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) const ElfW(Phdr)* phdr = phdr_table; 599183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) const ElfW(Phdr)* phdr_limit = phdr + phdr_count; 600183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) 601183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) for (phdr = phdr_table; phdr < phdr_limit; phdr++) { 602183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) if (phdr->p_type != PT_GNU_RELRO) { 603183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) continue; 604183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) } 605183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) 606183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) ElfW(Addr) seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias; 607183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) ElfW(Addr) seg_page_end = PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias; 608183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) 609183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) char* file_base = static_cast<char*>(temp_mapping) + file_offset; 610183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) char* mem_base = reinterpret_cast<char*>(seg_page_start); 611183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) size_t match_offset = 0; 612183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) size_t size = seg_page_end - seg_page_start; 613183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) 61426ec9679ff01fb155ae21015f31cc95bed24f670Torne (Richard Coles) if (file_size - file_offset < size) { 61526ec9679ff01fb155ae21015f31cc95bed24f670Torne (Richard Coles) // File is too short to compare to this segment. The contents are likely 61626ec9679ff01fb155ae21015f31cc95bed24f670Torne (Richard Coles) // different as well (it's probably for a different library version) so 61726ec9679ff01fb155ae21015f31cc95bed24f670Torne (Richard Coles) // just don't bother checking. 61826ec9679ff01fb155ae21015f31cc95bed24f670Torne (Richard Coles) break; 61926ec9679ff01fb155ae21015f31cc95bed24f670Torne (Richard Coles) } 62026ec9679ff01fb155ae21015f31cc95bed24f670Torne (Richard Coles) 621183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) while (match_offset < size) { 622183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) // Skip over dissimilar pages. 623183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) while (match_offset < size && 624183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) memcmp(mem_base + match_offset, file_base + match_offset, PAGE_SIZE) != 0) { 625183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) match_offset += PAGE_SIZE; 626183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) } 627183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) 628183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) // Count similar pages. 629183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) size_t mismatch_offset = match_offset; 630183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) while (mismatch_offset < size && 631183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) memcmp(mem_base + mismatch_offset, file_base + mismatch_offset, PAGE_SIZE) == 0) { 632183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) mismatch_offset += PAGE_SIZE; 633183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) } 634183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) 635183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) // Map over similar pages. 636183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) if (mismatch_offset > match_offset) { 637183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) void* map = mmap(mem_base + match_offset, mismatch_offset - match_offset, 638183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) PROT_READ, MAP_PRIVATE|MAP_FIXED, fd, match_offset); 639183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) if (map == MAP_FAILED) { 640183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) munmap(temp_mapping, file_size); 641183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) return -1; 642183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) } 643183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) } 644183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) 645183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) match_offset = mismatch_offset; 646183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) } 647183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) 648183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) // Add to the base file offset in case there are multiple relro segments. 649183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) file_offset += size; 650183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) } 651183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) munmap(temp_mapping, file_size); 652183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) return 0; 653183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles)} 654183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) 655183ad9df536ab04ef35a397a1f4724e4e401d11fTorne (Richard Coles) 6564eeb1f12a8b63afc0d0ad4d466b16fbffb21cd5aElliott Hughes#if defined(__arm__) 657c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 658c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner# ifndef PT_ARM_EXIDX 659c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner# define PT_ARM_EXIDX 0x70000001 /* .ARM.exidx segment */ 660c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner# endif 661c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 662c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* Return the address and size of the .ARM.exidx section in memory, 663c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * if present. 664c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 665c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Input: 666c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_table -> program header table 667105bc26fa6e5f6a946a2ff144ae316e69c6ce08eElliott Hughes * phdr_count -> number of entries in tables 668c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * load_bias -> load bias 669c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Output: 670c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * arm_exidx -> address of table in memory (NULL on failure). 671c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * arm_exidx_count -> number of items in table (0 on failure). 672c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Return: 673c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 0 on error, -1 on failure (_no_ error code in errno) 674c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 6750266ae5f884d72da58f33a072e865ba131234a5eElliott Hughesint phdr_table_get_arm_exidx(const ElfW(Phdr)* phdr_table, size_t phdr_count, 6760266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) load_bias, 6770266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr)** arm_exidx, unsigned* arm_exidx_count) { 6780266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes const ElfW(Phdr)* phdr = phdr_table; 6790266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes const ElfW(Phdr)* phdr_limit = phdr + phdr_count; 6800266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 6810266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes for (phdr = phdr_table; phdr < phdr_limit; phdr++) { 6820266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes if (phdr->p_type != PT_ARM_EXIDX) { 6830266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes continue; 684c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 6850266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 6860266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes *arm_exidx = reinterpret_cast<ElfW(Addr)*>(load_bias + phdr->p_vaddr); 6870266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes *arm_exidx_count = (unsigned)(phdr->p_memsz / 8); 6880266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes return 0; 6890266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes } 6900266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes *arm_exidx = NULL; 6910266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes *arm_exidx_count = 0; 6920266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes return -1; 693c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 6944eeb1f12a8b63afc0d0ad4d466b16fbffb21cd5aElliott Hughes#endif 695c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 69612c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel/* Return the address and size of the ELF file's .dynamic section in memory, 697c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * or NULL if missing. 698c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * 699c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Input: 700c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_table -> program header table 701105bc26fa6e5f6a946a2ff144ae316e69c6ce08eElliott Hughes * phdr_count -> number of entries in tables 702c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * load_bias -> load bias 70312c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel * Output: 70412c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel * dynamic -> address of table in memory (NULL on failure). 70512c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel * dynamic_count -> number of items in table (0 on failure). 706cf23905a4bcc7bfdd109be5b6d69ad06877aa217Chris Dearman * dynamic_flags -> protection flags for section (unset on failure) 707c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Return: 70812c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel * void 709c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */ 7100266ae5f884d72da58f33a072e865ba131234a5eElliott Hughesvoid phdr_table_get_dynamic_section(const ElfW(Phdr)* phdr_table, size_t phdr_count, 7110266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) load_bias, 7120266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Dyn)** dynamic, size_t* dynamic_count, ElfW(Word)* dynamic_flags) { 7130266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes const ElfW(Phdr)* phdr = phdr_table; 7140266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes const ElfW(Phdr)* phdr_limit = phdr + phdr_count; 7150266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 7160266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes for (phdr = phdr_table; phdr < phdr_limit; phdr++) { 7170266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes if (phdr->p_type != PT_DYNAMIC) { 7180266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes continue; 71912c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel } 7200266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes 7210266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes *dynamic = reinterpret_cast<ElfW(Dyn)*>(load_bias + phdr->p_vaddr); 72212c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel if (dynamic_count) { 7230266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes *dynamic_count = (unsigned)(phdr->p_memsz / 8); 7240266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes } 7250266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes if (dynamic_flags) { 7260266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes *dynamic_flags = phdr->p_flags; 727c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 7280266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes return; 7290266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes } 7300266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes *dynamic = NULL; 7310266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes if (dynamic_count) { 7320266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes *dynamic_count = 0; 7330266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes } 734c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 735c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 736650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes// Returns the address of the program header table as it appears in the loaded 737650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes// segments in memory. This is in contrast with 'phdr_table_' which 738650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes// is temporary and will be released before the library is relocated. 739650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughesbool ElfReader::FindPhdr() { 7400266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes const ElfW(Phdr)* phdr_limit = phdr_table_ + phdr_num_; 741c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 742650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes // If there is a PT_PHDR, use it directly. 7430266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes for (const ElfW(Phdr)* phdr = phdr_table_; phdr < phdr_limit; ++phdr) { 744650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (phdr->p_type == PT_PHDR) { 745650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return CheckPhdr(load_bias_ + phdr->p_vaddr); 746c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 747650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 748650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes 749650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes // Otherwise, check the first loadable segment. If its file offset 750650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes // is 0, it starts with the ELF header, and we can trivially find the 751650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes // loaded program header from it. 7520266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes for (const ElfW(Phdr)* phdr = phdr_table_; phdr < phdr_limit; ++phdr) { 753650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (phdr->p_type == PT_LOAD) { 754650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (phdr->p_offset == 0) { 7550266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) elf_addr = load_bias_ + phdr->p_vaddr; 756faf05bacd45719291b371f24b1b89543881b37f6Elliott Hughes const ElfW(Ehdr)* ehdr = reinterpret_cast<const ElfW(Ehdr)*>(elf_addr); 7570266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) offset = ehdr->e_phoff; 7580266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes return CheckPhdr((ElfW(Addr))ehdr + offset); 759650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 760650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes break; 761c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 762650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 763c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 764650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes DL_ERR("can't find loaded phdr for \"%s\"", name_); 765650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return false; 766650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes} 767c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner 768650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes// Ensures that our program header is actually within a loadable 769650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes// segment. This should help catch badly-formed ELF files that 770650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes// would cause the linker to crash later when trying to access it. 7710266ae5f884d72da58f33a072e865ba131234a5eElliott Hughesbool ElfReader::CheckPhdr(ElfW(Addr) loaded) { 7720266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes const ElfW(Phdr)* phdr_limit = phdr_table_ + phdr_num_; 7730266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) loaded_end = loaded + (phdr_num_ * sizeof(ElfW(Phdr))); 7740266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes for (ElfW(Phdr)* phdr = phdr_table_; phdr < phdr_limit; ++phdr) { 775650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (phdr->p_type != PT_LOAD) { 776650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes continue; 777650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 7780266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) seg_start = phdr->p_vaddr + load_bias_; 7790266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes ElfW(Addr) seg_end = phdr->p_filesz + seg_start; 780650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes if (seg_start <= loaded && loaded_end <= seg_end) { 7810266ae5f884d72da58f33a072e865ba131234a5eElliott Hughes loaded_phdr_ = reinterpret_cast<const ElfW(Phdr)*>(loaded); 782650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return true; 783c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner } 784650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes } 785c00f2cb587630d5e954c7f548749f1e3170b3cb1Elliott Hughes DL_ERR("\"%s\" loaded phdr %p not in loadable segment", name_, reinterpret_cast<void*>(loaded)); 786650be4e584eeab3591b9e273bfd6d169eea60853Elliott Hughes return false; 787c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner} 788