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