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