blob: 4884364a53bfa1303bdd8d367f1e82b43c5306f7 [file] [log] [blame]
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
Elliott Hughes650be4e2013-03-05 18:47:58 -080029#include "linker_phdr.h"
30
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +020031#include <errno.h>
32#include <sys/mman.h>
33
Elliott Hughes650be4e2013-03-05 18:47:58 -080034#include "linker.h"
35#include "linker_debug.h"
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +020036
37/**
38 TECHNICAL NOTE ON ELF LOADING.
39
40 An ELF file's program header table contains one or more PT_LOAD
41 segments, which corresponds to portions of the file that need to
42 be mapped into the process' address space.
43
44 Each loadable segment has the following important properties:
45
46 p_offset -> segment file offset
47 p_filesz -> segment file size
48 p_memsz -> segment memory size (always >= p_filesz)
49 p_vaddr -> segment's virtual address
50 p_flags -> segment flags (e.g. readable, writable, executable)
51
Elliott Hughesc6200592013-09-30 18:43:46 -070052 We will ignore the p_paddr and p_align fields of Elf_Phdr for now.
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +020053
54 The loadable segments can be seen as a list of [p_vaddr ... p_vaddr+p_memsz)
55 ranges of virtual addresses. A few rules apply:
56
57 - the virtual address ranges should not overlap.
58
59 - if a segment's p_filesz is smaller than its p_memsz, the extra bytes
60 between them should always be initialized to 0.
61
62 - ranges do not necessarily start or end at page boundaries. Two distinct
63 segments can have their start and end on the same page. In this case, the
64 page inherits the mapping flags of the latter segment.
65
66 Finally, the real load addrs of each segment is not p_vaddr. Instead the
67 loader decides where to load the first segment, then will load all others
68 relative to the first one to respect the initial range layout.
69
70 For example, consider the following list:
71
72 [ offset:0, filesz:0x4000, memsz:0x4000, vaddr:0x30000 ],
73 [ offset:0x4000, filesz:0x2000, memsz:0x8000, vaddr:0x40000 ],
74
75 This corresponds to two segments that cover these virtual address ranges:
76
77 0x30000...0x34000
78 0x40000...0x48000
79
80 If the loader decides to load the first segment at address 0xa0000000
81 then the segments' load address ranges will be:
82
83 0xa0030000...0xa0034000
84 0xa0040000...0xa0048000
85
86 In other words, all segments must be loaded at an address that has the same
87 constant offset from their p_vaddr value. This offset is computed as the
88 difference between the first segment's load address, and its p_vaddr value.
89
90 However, in practice, segments do _not_ start at page boundaries. Since we
91 can only memory-map at page boundaries, this means that the bias is
92 computed as:
93
94 load_bias = phdr0_load_address - PAGE_START(phdr0->p_vaddr)
95
96 (NOTE: The value must be used as a 32-bit unsigned integer, to deal with
97 possible wrap around UINT32_MAX for possible large p_vaddr values).
98
99 And that the phdr0_load_address must start at a page boundary, with
100 the segment's real content starting at:
101
102 phdr0_load_address + PAGE_OFFSET(phdr0->p_vaddr)
103
104 Note that ELF requires the following condition to make the mmap()-ing work:
105
106 PAGE_OFFSET(phdr0->p_vaddr) == PAGE_OFFSET(phdr0->p_offset)
107
108 The load_bias must be added to any p_vaddr value read from the ELF file to
109 determine the corresponding memory address.
110
111 **/
112
113#define MAYBE_MAP_FLAG(x,from,to) (((x) & (from)) ? (to) : 0)
114#define PFLAGS_TO_PROT(x) (MAYBE_MAP_FLAG((x), PF_X, PROT_EXEC) | \
115 MAYBE_MAP_FLAG((x), PF_R, PROT_READ) | \
116 MAYBE_MAP_FLAG((x), PF_W, PROT_WRITE))
117
Elliott Hughes650be4e2013-03-05 18:47:58 -0800118ElfReader::ElfReader(const char* name, int fd)
119 : name_(name), fd_(fd),
120 phdr_num_(0), phdr_mmap_(NULL), phdr_table_(NULL), phdr_size_(0),
121 load_start_(NULL), load_size_(0), load_bias_(0),
122 loaded_phdr_(NULL) {
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200123}
124
Elliott Hughes650be4e2013-03-05 18:47:58 -0800125ElfReader::~ElfReader() {
126 if (fd_ != -1) {
127 close(fd_);
128 }
129 if (phdr_mmap_ != NULL) {
130 munmap(phdr_mmap_, phdr_size_);
131 }
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200132}
133
Elliott Hughes650be4e2013-03-05 18:47:58 -0800134bool ElfReader::Load() {
135 return ReadElfHeader() &&
136 VerifyElfHeader() &&
137 ReadProgramHeader() &&
138 ReserveAddressSpace() &&
139 LoadSegments() &&
140 FindPhdr();
141}
142
143bool ElfReader::ReadElfHeader() {
144 ssize_t rc = TEMP_FAILURE_RETRY(read(fd_, &header_, sizeof(header_)));
145 if (rc < 0) {
146 DL_ERR("can't read file \"%s\": %s", name_, strerror(errno));
147 return false;
148 }
149 if (rc != sizeof(header_)) {
Elliott Hughesc6200592013-09-30 18:43:46 -0700150 DL_ERR("\"%s\" is too small to be an ELF executable: only found %zd bytes", name_,
151 static_cast<size_t>(rc));
Elliott Hughes650be4e2013-03-05 18:47:58 -0800152 return false;
153 }
154 return true;
155}
156
157bool ElfReader::VerifyElfHeader() {
158 if (header_.e_ident[EI_MAG0] != ELFMAG0 ||
159 header_.e_ident[EI_MAG1] != ELFMAG1 ||
160 header_.e_ident[EI_MAG2] != ELFMAG2 ||
161 header_.e_ident[EI_MAG3] != ELFMAG3) {
162 DL_ERR("\"%s\" has bad ELF magic", name_);
163 return false;
164 }
165
Elliott Hughesc00f2cb2013-10-04 17:01:33 -0700166 // Try to give a clear diagnostic for ELF class mismatches, since they're
167 // an easy mistake to make during the 32-bit/64-bit transition period.
168 int elf_class = header_.e_ident[EI_CLASS];
169#if defined(__LP64__)
170 if (elf_class != ELFCLASS64) {
171 if (elf_class == ELFCLASS32) {
172 DL_ERR("\"%s\" is 32-bit instead of 64-bit", name_);
173 } else {
174 DL_ERR("\"%s\" has unknown ELF class: %d", name_, elf_class);
175 }
Elliott Hughes650be4e2013-03-05 18:47:58 -0800176 return false;
177 }
Elliott Hughesc00f2cb2013-10-04 17:01:33 -0700178#else
179 if (elf_class != ELFCLASS32) {
180 if (elf_class == ELFCLASS64) {
181 DL_ERR("\"%s\" is 64-bit instead of 32-bit", name_);
182 } else {
183 DL_ERR("\"%s\" has unknown ELF class: %d", name_, elf_class);
184 }
185 return false;
186 }
187#endif
188
Elliott Hughes650be4e2013-03-05 18:47:58 -0800189 if (header_.e_ident[EI_DATA] != ELFDATA2LSB) {
190 DL_ERR("\"%s\" not little-endian: %d", name_, header_.e_ident[EI_DATA]);
191 return false;
192 }
193
194 if (header_.e_type != ET_DYN) {
195 DL_ERR("\"%s\" has unexpected e_type: %d", name_, header_.e_type);
196 return false;
197 }
198
199 if (header_.e_version != EV_CURRENT) {
200 DL_ERR("\"%s\" has unexpected e_version: %d", name_, header_.e_version);
201 return false;
202 }
203
204 if (header_.e_machine !=
Elliott Hughes4eeb1f12013-10-25 17:38:02 -0700205#if defined(__arm__)
Elliott Hughes650be4e2013-03-05 18:47:58 -0800206 EM_ARM
Elliott Hughes4eeb1f12013-10-25 17:38:02 -0700207#elif defined(__i386__)
Elliott Hughes650be4e2013-03-05 18:47:58 -0800208 EM_386
Elliott Hughes4eeb1f12013-10-25 17:38:02 -0700209#elif defined(__mips__)
210 EM_MIPS
211#elif defined(__x86_64__)
Elliott Hughesc00f2cb2013-10-04 17:01:33 -0700212 EM_X86_64
Elliott Hughes650be4e2013-03-05 18:47:58 -0800213#endif
214 ) {
215 DL_ERR("\"%s\" has unexpected e_machine: %d", name_, header_.e_machine);
216 return false;
217 }
218
219 return true;
220}
221
222// Loads the program header table from an ELF file into a read-only private
223// anonymous mmap-ed block.
224bool ElfReader::ReadProgramHeader() {
225 phdr_num_ = header_.e_phnum;
226
227 // Like the kernel, we only accept program header tables that
228 // are smaller than 64KiB.
Elliott Hughesc6200592013-09-30 18:43:46 -0700229 if (phdr_num_ < 1 || phdr_num_ > 65536/sizeof(Elf_Phdr)) {
230 DL_ERR("\"%s\" has invalid e_phnum: %zd", name_, phdr_num_);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800231 return false;
232 }
233
Elliott Hughesc6200592013-09-30 18:43:46 -0700234 Elf_Addr page_min = PAGE_START(header_.e_phoff);
235 Elf_Addr page_max = PAGE_END(header_.e_phoff + (phdr_num_ * sizeof(Elf_Phdr)));
236 Elf_Addr page_offset = PAGE_OFFSET(header_.e_phoff);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800237
238 phdr_size_ = page_max - page_min;
239
240 void* mmap_result = mmap(NULL, phdr_size_, PROT_READ, MAP_PRIVATE, fd_, page_min);
241 if (mmap_result == MAP_FAILED) {
242 DL_ERR("\"%s\" phdr mmap failed: %s", name_, strerror(errno));
243 return false;
244 }
245
246 phdr_mmap_ = mmap_result;
Elliott Hughesc6200592013-09-30 18:43:46 -0700247 phdr_table_ = reinterpret_cast<Elf_Phdr*>(reinterpret_cast<char*>(mmap_result) + page_offset);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800248 return true;
249}
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200250
Brian Carlstrome7dffe12013-01-10 16:39:58 -0800251/* Returns the size of the extent of all the possibly non-contiguous
252 * loadable segments in an ELF program header table. This corresponds
253 * to the page-aligned size in bytes that needs to be reserved in the
254 * process' address space. If there are no loadable segments, 0 is
255 * returned.
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200256 *
Brian Carlstrome7dffe12013-01-10 16:39:58 -0800257 * If out_min_vaddr or out_max_vaddr are non-NULL, they will be
258 * set to the minimum and maximum addresses of pages to be reserved,
259 * or 0 if there is nothing to load.
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200260 */
Elliott Hughesc6200592013-09-30 18:43:46 -0700261size_t phdr_table_get_load_size(const Elf_Phdr* phdr_table, size_t phdr_count,
262 Elf_Addr* out_min_vaddr,
263 Elf_Addr* out_max_vaddr) {
264 Elf_Addr min_vaddr = 0xFFFFFFFFU;
265 Elf_Addr max_vaddr = 0x00000000U;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200266
Brian Carlstrome7dffe12013-01-10 16:39:58 -0800267 bool found_pt_load = false;
Elliott Hughes46882792012-08-03 16:49:39 -0700268 for (size_t i = 0; i < phdr_count; ++i) {
Elliott Hughesc6200592013-09-30 18:43:46 -0700269 const Elf_Phdr* phdr = &phdr_table[i];
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200270
Elliott Hughes46882792012-08-03 16:49:39 -0700271 if (phdr->p_type != PT_LOAD) {
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200272 continue;
Elliott Hughes46882792012-08-03 16:49:39 -0700273 }
Brian Carlstrome7dffe12013-01-10 16:39:58 -0800274 found_pt_load = true;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200275
Elliott Hughes46882792012-08-03 16:49:39 -0700276 if (phdr->p_vaddr < min_vaddr) {
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200277 min_vaddr = phdr->p_vaddr;
Elliott Hughes46882792012-08-03 16:49:39 -0700278 }
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200279
Elliott Hughes46882792012-08-03 16:49:39 -0700280 if (phdr->p_vaddr + phdr->p_memsz > max_vaddr) {
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200281 max_vaddr = phdr->p_vaddr + phdr->p_memsz;
Elliott Hughes46882792012-08-03 16:49:39 -0700282 }
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200283 }
Brian Carlstrome7dffe12013-01-10 16:39:58 -0800284 if (!found_pt_load) {
285 min_vaddr = 0x00000000U;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200286 }
287
288 min_vaddr = PAGE_START(min_vaddr);
289 max_vaddr = PAGE_END(max_vaddr);
290
Brian Carlstrome7dffe12013-01-10 16:39:58 -0800291 if (out_min_vaddr != NULL) {
292 *out_min_vaddr = min_vaddr;
293 }
294 if (out_max_vaddr != NULL) {
295 *out_max_vaddr = max_vaddr;
296 }
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200297 return max_vaddr - min_vaddr;
298}
299
Elliott Hughes650be4e2013-03-05 18:47:58 -0800300// Reserve a virtual address range big enough to hold all loadable
301// segments of a program header table. This is done by creating a
302// private anonymous mmap() with PROT_NONE.
303bool ElfReader::ReserveAddressSpace() {
Elliott Hughesc6200592013-09-30 18:43:46 -0700304 Elf_Addr min_vaddr;
Brian Carlstrome7dffe12013-01-10 16:39:58 -0800305 load_size_ = phdr_table_get_load_size(phdr_table_, phdr_num_, &min_vaddr);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800306 if (load_size_ == 0) {
307 DL_ERR("\"%s\" has no loadable segments", name_);
308 return false;
309 }
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200310
Brian Carlstrome7dffe12013-01-10 16:39:58 -0800311 uint8_t* addr = reinterpret_cast<uint8_t*>(min_vaddr);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800312 int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
Brian Carlstrome7dffe12013-01-10 16:39:58 -0800313 void* start = mmap(addr, load_size_, PROT_NONE, mmap_flags, -1, 0);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800314 if (start == MAP_FAILED) {
Elliott Hughesc00f2cb2013-10-04 17:01:33 -0700315 DL_ERR("couldn't reserve %zd bytes of address space for \"%s\"", load_size_, name_);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800316 return false;
317 }
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200318
Elliott Hughes650be4e2013-03-05 18:47:58 -0800319 load_start_ = start;
Brian Carlstrome7dffe12013-01-10 16:39:58 -0800320 load_bias_ = reinterpret_cast<uint8_t*>(start) - addr;
Elliott Hughes650be4e2013-03-05 18:47:58 -0800321 return true;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200322}
323
Elliott Hughes650be4e2013-03-05 18:47:58 -0800324bool ElfReader::LoadSegments() {
325 for (size_t i = 0; i < phdr_num_; ++i) {
Elliott Hughesc6200592013-09-30 18:43:46 -0700326 const Elf_Phdr* phdr = &phdr_table_[i];
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200327
Elliott Hughes650be4e2013-03-05 18:47:58 -0800328 if (phdr->p_type != PT_LOAD) {
329 continue;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200330 }
Elliott Hughes650be4e2013-03-05 18:47:58 -0800331
332 // Segment addresses in memory.
Elliott Hughesc6200592013-09-30 18:43:46 -0700333 Elf_Addr seg_start = phdr->p_vaddr + load_bias_;
334 Elf_Addr seg_end = seg_start + phdr->p_memsz;
Elliott Hughes650be4e2013-03-05 18:47:58 -0800335
Elliott Hughesc6200592013-09-30 18:43:46 -0700336 Elf_Addr seg_page_start = PAGE_START(seg_start);
337 Elf_Addr seg_page_end = PAGE_END(seg_end);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800338
Elliott Hughesc6200592013-09-30 18:43:46 -0700339 Elf_Addr seg_file_end = seg_start + phdr->p_filesz;
Elliott Hughes650be4e2013-03-05 18:47:58 -0800340
341 // File offsets.
Elliott Hughesc6200592013-09-30 18:43:46 -0700342 Elf_Addr file_start = phdr->p_offset;
343 Elf_Addr file_end = file_start + phdr->p_filesz;
Elliott Hughes650be4e2013-03-05 18:47:58 -0800344
Elliott Hughesc6200592013-09-30 18:43:46 -0700345 Elf_Addr file_page_start = PAGE_START(file_start);
346 Elf_Addr file_length = file_end - file_page_start;
Elliott Hughes650be4e2013-03-05 18:47:58 -0800347
Brian Carlstrom82dcc792013-05-21 16:49:24 -0700348 if (file_length != 0) {
349 void* seg_addr = mmap((void*)seg_page_start,
350 file_length,
351 PFLAGS_TO_PROT(phdr->p_flags),
352 MAP_FIXED|MAP_PRIVATE,
353 fd_,
354 file_page_start);
355 if (seg_addr == MAP_FAILED) {
Elliott Hughesc6200592013-09-30 18:43:46 -0700356 DL_ERR("couldn't map \"%s\" segment %zd: %s", name_, i, strerror(errno));
Brian Carlstrom82dcc792013-05-21 16:49:24 -0700357 return false;
358 }
Elliott Hughes650be4e2013-03-05 18:47:58 -0800359 }
360
361 // if the segment is writable, and does not end on a page boundary,
362 // zero-fill it until the page limit.
363 if ((phdr->p_flags & PF_W) != 0 && PAGE_OFFSET(seg_file_end) > 0) {
364 memset((void*)seg_file_end, 0, PAGE_SIZE - PAGE_OFFSET(seg_file_end));
365 }
366
367 seg_file_end = PAGE_END(seg_file_end);
368
369 // seg_file_end is now the first page address after the file
370 // content. If seg_end is larger, we need to zero anything
371 // between them. This is done by using a private anonymous
372 // map for all extra pages.
373 if (seg_page_end > seg_file_end) {
374 void* zeromap = mmap((void*)seg_file_end,
375 seg_page_end - seg_file_end,
376 PFLAGS_TO_PROT(phdr->p_flags),
377 MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE,
378 -1,
379 0);
380 if (zeromap == MAP_FAILED) {
381 DL_ERR("couldn't zero fill \"%s\" gap: %s", name_, strerror(errno));
382 return false;
383 }
384 }
385 }
386 return true;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200387}
388
Elliott Hughes105bc262012-08-15 16:56:00 -0700389/* Used internally. Used to set the protection bits of all loaded segments
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200390 * with optional extra flags (i.e. really PROT_WRITE). Used by
391 * phdr_table_protect_segments and phdr_table_unprotect_segments.
392 */
Elliott Hughesc6200592013-09-30 18:43:46 -0700393static int _phdr_table_set_load_prot(const Elf_Phdr* phdr_table, size_t phdr_count,
394 Elf_Addr load_bias, int extra_prot_flags) {
395 const Elf_Phdr* phdr = phdr_table;
396 const Elf_Phdr* phdr_limit = phdr + phdr_count;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200397
398 for (; phdr < phdr_limit; phdr++) {
399 if (phdr->p_type != PT_LOAD || (phdr->p_flags & PF_W) != 0)
400 continue;
401
Elliott Hughesc6200592013-09-30 18:43:46 -0700402 Elf_Addr seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias;
403 Elf_Addr seg_page_end = PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200404
405 int ret = mprotect((void*)seg_page_start,
406 seg_page_end - seg_page_start,
407 PFLAGS_TO_PROT(phdr->p_flags) | extra_prot_flags);
408 if (ret < 0) {
409 return -1;
410 }
411 }
412 return 0;
413}
414
415/* Restore the original protection modes for all loadable segments.
416 * You should only call this after phdr_table_unprotect_segments and
417 * applying all relocations.
418 *
419 * Input:
420 * phdr_table -> program header table
Elliott Hughes105bc262012-08-15 16:56:00 -0700421 * phdr_count -> number of entries in tables
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200422 * load_bias -> load bias
423 * Return:
424 * 0 on error, -1 on failure (error code in errno).
425 */
Elliott Hughesc6200592013-09-30 18:43:46 -0700426int phdr_table_protect_segments(const Elf_Phdr* phdr_table, size_t phdr_count, Elf_Addr load_bias) {
427 return _phdr_table_set_load_prot(phdr_table, phdr_count, load_bias, 0);
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200428}
429
430/* Change the protection of all loaded segments in memory to writable.
431 * This is useful before performing relocations. Once completed, you
432 * will have to call phdr_table_protect_segments to restore the original
433 * protection flags on all segments.
434 *
435 * Note that some writable segments can also have their content turned
436 * to read-only by calling phdr_table_protect_gnu_relro. This is no
437 * performed here.
438 *
439 * Input:
440 * phdr_table -> program header table
Elliott Hughes105bc262012-08-15 16:56:00 -0700441 * phdr_count -> number of entries in tables
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200442 * load_bias -> load bias
443 * Return:
444 * 0 on error, -1 on failure (error code in errno).
445 */
Elliott Hughesc6200592013-09-30 18:43:46 -0700446int phdr_table_unprotect_segments(const Elf_Phdr* phdr_table, size_t phdr_count, Elf_Addr load_bias) {
447 return _phdr_table_set_load_prot(phdr_table, phdr_count, load_bias, PROT_WRITE);
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200448}
449
450/* Used internally by phdr_table_protect_gnu_relro and
451 * phdr_table_unprotect_gnu_relro.
452 */
Elliott Hughesc6200592013-09-30 18:43:46 -0700453static int _phdr_table_set_gnu_relro_prot(const Elf_Phdr* phdr_table, size_t phdr_count,
454 Elf_Addr load_bias, int prot_flags) {
455 const Elf_Phdr* phdr = phdr_table;
456 const Elf_Phdr* phdr_limit = phdr + phdr_count;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200457
458 for (phdr = phdr_table; phdr < phdr_limit; phdr++) {
459 if (phdr->p_type != PT_GNU_RELRO)
460 continue;
461
462 /* Tricky: what happens when the relro segment does not start
463 * or end at page boundaries?. We're going to be over-protective
464 * here and put every page touched by the segment as read-only.
465 *
466 * This seems to match Ian Lance Taylor's description of the
467 * feature at http://www.airs.com/blog/archives/189.
468 *
469 * Extract:
470 * Note that the current dynamic linker code will only work
471 * correctly if the PT_GNU_RELRO segment starts on a page
472 * boundary. This is because the dynamic linker rounds the
473 * p_vaddr field down to the previous page boundary. If
474 * there is anything on the page which should not be read-only,
475 * the program is likely to fail at runtime. So in effect the
476 * linker must only emit a PT_GNU_RELRO segment if it ensures
477 * that it starts on a page boundary.
478 */
Elliott Hughesc6200592013-09-30 18:43:46 -0700479 Elf_Addr seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias;
480 Elf_Addr seg_page_end = PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200481
482 int ret = mprotect((void*)seg_page_start,
483 seg_page_end - seg_page_start,
484 prot_flags);
485 if (ret < 0) {
486 return -1;
487 }
488 }
489 return 0;
490}
491
492/* Apply GNU relro protection if specified by the program header. This will
493 * turn some of the pages of a writable PT_LOAD segment to read-only, as
494 * specified by one or more PT_GNU_RELRO segments. This must be always
495 * performed after relocations.
496 *
Ard Biesheuvel12c78bb2012-08-14 12:30:09 +0200497 * The areas typically covered are .got and .data.rel.ro, these are
498 * read-only from the program's POV, but contain absolute addresses
499 * that need to be relocated before use.
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200500 *
501 * Input:
502 * phdr_table -> program header table
Elliott Hughes105bc262012-08-15 16:56:00 -0700503 * phdr_count -> number of entries in tables
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200504 * load_bias -> load bias
505 * Return:
506 * 0 on error, -1 on failure (error code in errno).
507 */
Elliott Hughesc6200592013-09-30 18:43:46 -0700508int phdr_table_protect_gnu_relro(const Elf_Phdr* phdr_table, size_t phdr_count, Elf_Addr load_bias) {
509 return _phdr_table_set_gnu_relro_prot(phdr_table, phdr_count, load_bias, PROT_READ);
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200510}
511
Elliott Hughes4eeb1f12013-10-25 17:38:02 -0700512#if defined(__arm__)
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200513
514# ifndef PT_ARM_EXIDX
515# define PT_ARM_EXIDX 0x70000001 /* .ARM.exidx segment */
516# endif
517
518/* Return the address and size of the .ARM.exidx section in memory,
519 * if present.
520 *
521 * Input:
522 * phdr_table -> program header table
Elliott Hughes105bc262012-08-15 16:56:00 -0700523 * phdr_count -> number of entries in tables
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200524 * load_bias -> load bias
525 * Output:
526 * arm_exidx -> address of table in memory (NULL on failure).
527 * arm_exidx_count -> number of items in table (0 on failure).
528 * Return:
529 * 0 on error, -1 on failure (_no_ error code in errno)
530 */
Elliott Hughesc6200592013-09-30 18:43:46 -0700531int phdr_table_get_arm_exidx(const Elf_Phdr* phdr_table, size_t phdr_count,
532 Elf_Addr load_bias,
533 Elf_Addr** arm_exidx, unsigned* arm_exidx_count) {
534 const Elf_Phdr* phdr = phdr_table;
535 const Elf_Phdr* phdr_limit = phdr + phdr_count;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200536
537 for (phdr = phdr_table; phdr < phdr_limit; phdr++) {
538 if (phdr->p_type != PT_ARM_EXIDX)
539 continue;
540
Elliott Hughesc6200592013-09-30 18:43:46 -0700541 *arm_exidx = (Elf_Addr*)(load_bias + phdr->p_vaddr);
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200542 *arm_exidx_count = (unsigned)(phdr->p_memsz / 8);
543 return 0;
544 }
545 *arm_exidx = NULL;
546 *arm_exidx_count = 0;
547 return -1;
548}
Elliott Hughes4eeb1f12013-10-25 17:38:02 -0700549#endif
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200550
Ard Biesheuvel12c78bb2012-08-14 12:30:09 +0200551/* Return the address and size of the ELF file's .dynamic section in memory,
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200552 * or NULL if missing.
553 *
554 * Input:
555 * phdr_table -> program header table
Elliott Hughes105bc262012-08-15 16:56:00 -0700556 * phdr_count -> number of entries in tables
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200557 * load_bias -> load bias
Ard Biesheuvel12c78bb2012-08-14 12:30:09 +0200558 * Output:
559 * dynamic -> address of table in memory (NULL on failure).
560 * dynamic_count -> number of items in table (0 on failure).
Chris Dearmancf239052013-01-11 15:32:20 -0800561 * dynamic_flags -> protection flags for section (unset on failure)
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200562 * Return:
Ard Biesheuvel12c78bb2012-08-14 12:30:09 +0200563 * void
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200564 */
Elliott Hughesc6200592013-09-30 18:43:46 -0700565void phdr_table_get_dynamic_section(const Elf_Phdr* phdr_table, size_t phdr_count,
566 Elf_Addr load_bias,
567 Elf_Dyn** dynamic, size_t* dynamic_count, Elf_Word* dynamic_flags) {
568 const Elf_Phdr* phdr = phdr_table;
569 const Elf_Phdr* phdr_limit = phdr + phdr_count;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200570
571 for (phdr = phdr_table; phdr < phdr_limit; phdr++) {
Ard Biesheuvel12c78bb2012-08-14 12:30:09 +0200572 if (phdr->p_type != PT_DYNAMIC) {
573 continue;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200574 }
Ard Biesheuvel12c78bb2012-08-14 12:30:09 +0200575
Elliott Hughesc6200592013-09-30 18:43:46 -0700576 *dynamic = reinterpret_cast<Elf_Dyn*>(load_bias + phdr->p_vaddr);
Ard Biesheuvel12c78bb2012-08-14 12:30:09 +0200577 if (dynamic_count) {
578 *dynamic_count = (unsigned)(phdr->p_memsz / 8);
579 }
Chris Dearmancf239052013-01-11 15:32:20 -0800580 if (dynamic_flags) {
581 *dynamic_flags = phdr->p_flags;
582 }
Ard Biesheuvel12c78bb2012-08-14 12:30:09 +0200583 return;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200584 }
Ard Biesheuvel12c78bb2012-08-14 12:30:09 +0200585 *dynamic = NULL;
586 if (dynamic_count) {
587 *dynamic_count = 0;
588 }
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200589}
590
Elliott Hughes650be4e2013-03-05 18:47:58 -0800591// Returns the address of the program header table as it appears in the loaded
592// segments in memory. This is in contrast with 'phdr_table_' which
593// is temporary and will be released before the library is relocated.
594bool ElfReader::FindPhdr() {
Elliott Hughesc6200592013-09-30 18:43:46 -0700595 const Elf_Phdr* phdr_limit = phdr_table_ + phdr_num_;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200596
Elliott Hughes650be4e2013-03-05 18:47:58 -0800597 // If there is a PT_PHDR, use it directly.
Elliott Hughesc6200592013-09-30 18:43:46 -0700598 for (const Elf_Phdr* phdr = phdr_table_; phdr < phdr_limit; ++phdr) {
Elliott Hughes650be4e2013-03-05 18:47:58 -0800599 if (phdr->p_type == PT_PHDR) {
600 return CheckPhdr(load_bias_ + phdr->p_vaddr);
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200601 }
Elliott Hughes650be4e2013-03-05 18:47:58 -0800602 }
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200603
Elliott Hughes650be4e2013-03-05 18:47:58 -0800604 // Otherwise, check the first loadable segment. If its file offset
605 // is 0, it starts with the ELF header, and we can trivially find the
606 // loaded program header from it.
Elliott Hughesc6200592013-09-30 18:43:46 -0700607 for (const Elf_Phdr* phdr = phdr_table_; phdr < phdr_limit; ++phdr) {
Elliott Hughes650be4e2013-03-05 18:47:58 -0800608 if (phdr->p_type == PT_LOAD) {
609 if (phdr->p_offset == 0) {
Elliott Hughesc6200592013-09-30 18:43:46 -0700610 Elf_Addr elf_addr = load_bias_ + phdr->p_vaddr;
611 const Elf_Ehdr* ehdr = (const Elf_Ehdr*)(void*)elf_addr;
612 Elf_Addr offset = ehdr->e_phoff;
613 return CheckPhdr((Elf_Addr)ehdr + offset);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800614 }
615 break;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200616 }
Elliott Hughes650be4e2013-03-05 18:47:58 -0800617 }
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200618
Elliott Hughes650be4e2013-03-05 18:47:58 -0800619 DL_ERR("can't find loaded phdr for \"%s\"", name_);
620 return false;
621}
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200622
Elliott Hughes650be4e2013-03-05 18:47:58 -0800623// Ensures that our program header is actually within a loadable
624// segment. This should help catch badly-formed ELF files that
625// would cause the linker to crash later when trying to access it.
Elliott Hughesc6200592013-09-30 18:43:46 -0700626bool ElfReader::CheckPhdr(Elf_Addr loaded) {
627 const Elf_Phdr* phdr_limit = phdr_table_ + phdr_num_;
628 Elf_Addr loaded_end = loaded + (phdr_num_ * sizeof(Elf_Phdr));
629 for (Elf_Phdr* phdr = phdr_table_; phdr < phdr_limit; ++phdr) {
Elliott Hughes650be4e2013-03-05 18:47:58 -0800630 if (phdr->p_type != PT_LOAD) {
631 continue;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200632 }
Elliott Hughesc6200592013-09-30 18:43:46 -0700633 Elf_Addr seg_start = phdr->p_vaddr + load_bias_;
634 Elf_Addr seg_end = phdr->p_filesz + seg_start;
Elliott Hughes650be4e2013-03-05 18:47:58 -0800635 if (seg_start <= loaded && loaded_end <= seg_end) {
Elliott Hughesc6200592013-09-30 18:43:46 -0700636 loaded_phdr_ = reinterpret_cast<const Elf_Phdr*>(loaded);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800637 return true;
638 }
639 }
Elliott Hughesc00f2cb2013-10-04 17:01:33 -0700640 DL_ERR("\"%s\" loaded phdr %p not in loadable segment", name_, reinterpret_cast<void*>(loaded));
Elliott Hughes650be4e2013-03-05 18:47:58 -0800641 return false;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200642}