blob: 5f2b2c1f6f048082fa1d99d80eb0dd2c60b2928d [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 !=
205#ifdef ANDROID_ARM_LINKER
206 EM_ARM
207#elif defined(ANDROID_MIPS_LINKER)
208 EM_MIPS
209#elif defined(ANDROID_X86_LINKER)
210 EM_386
Elliott Hughesc00f2cb2013-10-04 17:01:33 -0700211#elif defined(ANDROID_X86_64_LINKER)
212 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 -0800324// Map all loadable segments in process' address space.
325// This assumes you already called phdr_table_reserve_memory to
326// reserve the address space range for the library.
327// TODO: assert assumption.
328bool ElfReader::LoadSegments() {
329 for (size_t i = 0; i < phdr_num_; ++i) {
Elliott Hughesc6200592013-09-30 18:43:46 -0700330 const Elf_Phdr* phdr = &phdr_table_[i];
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200331
Elliott Hughes650be4e2013-03-05 18:47:58 -0800332 if (phdr->p_type != PT_LOAD) {
333 continue;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200334 }
Elliott Hughes650be4e2013-03-05 18:47:58 -0800335
336 // Segment addresses in memory.
Elliott Hughesc6200592013-09-30 18:43:46 -0700337 Elf_Addr seg_start = phdr->p_vaddr + load_bias_;
338 Elf_Addr seg_end = seg_start + phdr->p_memsz;
Elliott Hughes650be4e2013-03-05 18:47:58 -0800339
Elliott Hughesc6200592013-09-30 18:43:46 -0700340 Elf_Addr seg_page_start = PAGE_START(seg_start);
341 Elf_Addr seg_page_end = PAGE_END(seg_end);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800342
Elliott Hughesc6200592013-09-30 18:43:46 -0700343 Elf_Addr seg_file_end = seg_start + phdr->p_filesz;
Elliott Hughes650be4e2013-03-05 18:47:58 -0800344
345 // File offsets.
Elliott Hughesc6200592013-09-30 18:43:46 -0700346 Elf_Addr file_start = phdr->p_offset;
347 Elf_Addr file_end = file_start + phdr->p_filesz;
Elliott Hughes650be4e2013-03-05 18:47:58 -0800348
Elliott Hughesc6200592013-09-30 18:43:46 -0700349 Elf_Addr file_page_start = PAGE_START(file_start);
350 Elf_Addr file_length = file_end - file_page_start;
Elliott Hughes650be4e2013-03-05 18:47:58 -0800351
Brian Carlstrom82dcc792013-05-21 16:49:24 -0700352 if (file_length != 0) {
353 void* seg_addr = mmap((void*)seg_page_start,
354 file_length,
355 PFLAGS_TO_PROT(phdr->p_flags),
356 MAP_FIXED|MAP_PRIVATE,
357 fd_,
358 file_page_start);
359 if (seg_addr == MAP_FAILED) {
Elliott Hughesc6200592013-09-30 18:43:46 -0700360 DL_ERR("couldn't map \"%s\" segment %zd: %s", name_, i, strerror(errno));
Brian Carlstrom82dcc792013-05-21 16:49:24 -0700361 return false;
362 }
Elliott Hughes650be4e2013-03-05 18:47:58 -0800363 }
364
365 // if the segment is writable, and does not end on a page boundary,
366 // zero-fill it until the page limit.
367 if ((phdr->p_flags & PF_W) != 0 && PAGE_OFFSET(seg_file_end) > 0) {
368 memset((void*)seg_file_end, 0, PAGE_SIZE - PAGE_OFFSET(seg_file_end));
369 }
370
371 seg_file_end = PAGE_END(seg_file_end);
372
373 // seg_file_end is now the first page address after the file
374 // content. If seg_end is larger, we need to zero anything
375 // between them. This is done by using a private anonymous
376 // map for all extra pages.
377 if (seg_page_end > seg_file_end) {
378 void* zeromap = mmap((void*)seg_file_end,
379 seg_page_end - seg_file_end,
380 PFLAGS_TO_PROT(phdr->p_flags),
381 MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE,
382 -1,
383 0);
384 if (zeromap == MAP_FAILED) {
385 DL_ERR("couldn't zero fill \"%s\" gap: %s", name_, strerror(errno));
386 return false;
387 }
388 }
389 }
390 return true;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200391}
392
Elliott Hughes105bc262012-08-15 16:56:00 -0700393/* Used internally. Used to set the protection bits of all loaded segments
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200394 * with optional extra flags (i.e. really PROT_WRITE). Used by
395 * phdr_table_protect_segments and phdr_table_unprotect_segments.
396 */
Elliott Hughesc6200592013-09-30 18:43:46 -0700397static int _phdr_table_set_load_prot(const Elf_Phdr* phdr_table, size_t phdr_count,
398 Elf_Addr load_bias, int extra_prot_flags) {
399 const Elf_Phdr* phdr = phdr_table;
400 const Elf_Phdr* phdr_limit = phdr + phdr_count;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200401
402 for (; phdr < phdr_limit; phdr++) {
403 if (phdr->p_type != PT_LOAD || (phdr->p_flags & PF_W) != 0)
404 continue;
405
Elliott Hughesc6200592013-09-30 18:43:46 -0700406 Elf_Addr seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias;
407 Elf_Addr seg_page_end = PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200408
409 int ret = mprotect((void*)seg_page_start,
410 seg_page_end - seg_page_start,
411 PFLAGS_TO_PROT(phdr->p_flags) | extra_prot_flags);
412 if (ret < 0) {
413 return -1;
414 }
415 }
416 return 0;
417}
418
419/* Restore the original protection modes for all loadable segments.
420 * You should only call this after phdr_table_unprotect_segments and
421 * applying all relocations.
422 *
423 * Input:
424 * phdr_table -> program header table
Elliott Hughes105bc262012-08-15 16:56:00 -0700425 * phdr_count -> number of entries in tables
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200426 * load_bias -> load bias
427 * Return:
428 * 0 on error, -1 on failure (error code in errno).
429 */
Elliott Hughesc6200592013-09-30 18:43:46 -0700430int phdr_table_protect_segments(const Elf_Phdr* phdr_table, size_t phdr_count, Elf_Addr load_bias) {
431 return _phdr_table_set_load_prot(phdr_table, phdr_count, load_bias, 0);
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200432}
433
434/* Change the protection of all loaded segments in memory to writable.
435 * This is useful before performing relocations. Once completed, you
436 * will have to call phdr_table_protect_segments to restore the original
437 * protection flags on all segments.
438 *
439 * Note that some writable segments can also have their content turned
440 * to read-only by calling phdr_table_protect_gnu_relro. This is no
441 * performed here.
442 *
443 * Input:
444 * phdr_table -> program header table
Elliott Hughes105bc262012-08-15 16:56:00 -0700445 * phdr_count -> number of entries in tables
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200446 * load_bias -> load bias
447 * Return:
448 * 0 on error, -1 on failure (error code in errno).
449 */
Elliott Hughesc6200592013-09-30 18:43:46 -0700450int phdr_table_unprotect_segments(const Elf_Phdr* phdr_table, size_t phdr_count, Elf_Addr load_bias) {
451 return _phdr_table_set_load_prot(phdr_table, phdr_count, load_bias, PROT_WRITE);
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200452}
453
454/* Used internally by phdr_table_protect_gnu_relro and
455 * phdr_table_unprotect_gnu_relro.
456 */
Elliott Hughesc6200592013-09-30 18:43:46 -0700457static int _phdr_table_set_gnu_relro_prot(const Elf_Phdr* phdr_table, size_t phdr_count,
458 Elf_Addr load_bias, int prot_flags) {
459 const Elf_Phdr* phdr = phdr_table;
460 const Elf_Phdr* phdr_limit = phdr + phdr_count;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200461
462 for (phdr = phdr_table; phdr < phdr_limit; phdr++) {
463 if (phdr->p_type != PT_GNU_RELRO)
464 continue;
465
466 /* Tricky: what happens when the relro segment does not start
467 * or end at page boundaries?. We're going to be over-protective
468 * here and put every page touched by the segment as read-only.
469 *
470 * This seems to match Ian Lance Taylor's description of the
471 * feature at http://www.airs.com/blog/archives/189.
472 *
473 * Extract:
474 * Note that the current dynamic linker code will only work
475 * correctly if the PT_GNU_RELRO segment starts on a page
476 * boundary. This is because the dynamic linker rounds the
477 * p_vaddr field down to the previous page boundary. If
478 * there is anything on the page which should not be read-only,
479 * the program is likely to fail at runtime. So in effect the
480 * linker must only emit a PT_GNU_RELRO segment if it ensures
481 * that it starts on a page boundary.
482 */
Elliott Hughesc6200592013-09-30 18:43:46 -0700483 Elf_Addr seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias;
484 Elf_Addr seg_page_end = PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200485
486 int ret = mprotect((void*)seg_page_start,
487 seg_page_end - seg_page_start,
488 prot_flags);
489 if (ret < 0) {
490 return -1;
491 }
492 }
493 return 0;
494}
495
496/* Apply GNU relro protection if specified by the program header. This will
497 * turn some of the pages of a writable PT_LOAD segment to read-only, as
498 * specified by one or more PT_GNU_RELRO segments. This must be always
499 * performed after relocations.
500 *
Ard Biesheuvel12c78bb2012-08-14 12:30:09 +0200501 * The areas typically covered are .got and .data.rel.ro, these are
502 * read-only from the program's POV, but contain absolute addresses
503 * that need to be relocated before use.
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200504 *
505 * Input:
506 * phdr_table -> program header table
Elliott Hughes105bc262012-08-15 16:56:00 -0700507 * phdr_count -> number of entries in tables
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200508 * load_bias -> load bias
509 * Return:
510 * 0 on error, -1 on failure (error code in errno).
511 */
Elliott Hughesc6200592013-09-30 18:43:46 -0700512int phdr_table_protect_gnu_relro(const Elf_Phdr* phdr_table, size_t phdr_count, Elf_Addr load_bias) {
513 return _phdr_table_set_gnu_relro_prot(phdr_table, phdr_count, load_bias, PROT_READ);
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200514}
515
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200516#ifdef ANDROID_ARM_LINKER
517
518# ifndef PT_ARM_EXIDX
519# define PT_ARM_EXIDX 0x70000001 /* .ARM.exidx segment */
520# endif
521
522/* Return the address and size of the .ARM.exidx section in memory,
523 * if present.
524 *
525 * Input:
526 * phdr_table -> program header table
Elliott Hughes105bc262012-08-15 16:56:00 -0700527 * phdr_count -> number of entries in tables
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200528 * load_bias -> load bias
529 * Output:
530 * arm_exidx -> address of table in memory (NULL on failure).
531 * arm_exidx_count -> number of items in table (0 on failure).
532 * Return:
533 * 0 on error, -1 on failure (_no_ error code in errno)
534 */
Elliott Hughesc6200592013-09-30 18:43:46 -0700535int phdr_table_get_arm_exidx(const Elf_Phdr* phdr_table, size_t phdr_count,
536 Elf_Addr load_bias,
537 Elf_Addr** arm_exidx, unsigned* arm_exidx_count) {
538 const Elf_Phdr* phdr = phdr_table;
539 const Elf_Phdr* phdr_limit = phdr + phdr_count;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200540
541 for (phdr = phdr_table; phdr < phdr_limit; phdr++) {
542 if (phdr->p_type != PT_ARM_EXIDX)
543 continue;
544
Elliott Hughesc6200592013-09-30 18:43:46 -0700545 *arm_exidx = (Elf_Addr*)(load_bias + phdr->p_vaddr);
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200546 *arm_exidx_count = (unsigned)(phdr->p_memsz / 8);
547 return 0;
548 }
549 *arm_exidx = NULL;
550 *arm_exidx_count = 0;
551 return -1;
552}
553#endif /* ANDROID_ARM_LINKER */
554
Ard Biesheuvel12c78bb2012-08-14 12:30:09 +0200555/* Return the address and size of the ELF file's .dynamic section in memory,
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200556 * or NULL if missing.
557 *
558 * Input:
559 * phdr_table -> program header table
Elliott Hughes105bc262012-08-15 16:56:00 -0700560 * phdr_count -> number of entries in tables
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200561 * load_bias -> load bias
Ard Biesheuvel12c78bb2012-08-14 12:30:09 +0200562 * Output:
563 * dynamic -> address of table in memory (NULL on failure).
564 * dynamic_count -> number of items in table (0 on failure).
Chris Dearmancf239052013-01-11 15:32:20 -0800565 * dynamic_flags -> protection flags for section (unset on failure)
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200566 * Return:
Ard Biesheuvel12c78bb2012-08-14 12:30:09 +0200567 * void
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200568 */
Elliott Hughesc6200592013-09-30 18:43:46 -0700569void phdr_table_get_dynamic_section(const Elf_Phdr* phdr_table, size_t phdr_count,
570 Elf_Addr load_bias,
571 Elf_Dyn** dynamic, size_t* dynamic_count, Elf_Word* dynamic_flags) {
572 const Elf_Phdr* phdr = phdr_table;
573 const Elf_Phdr* phdr_limit = phdr + phdr_count;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200574
575 for (phdr = phdr_table; phdr < phdr_limit; phdr++) {
Ard Biesheuvel12c78bb2012-08-14 12:30:09 +0200576 if (phdr->p_type != PT_DYNAMIC) {
577 continue;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200578 }
Ard Biesheuvel12c78bb2012-08-14 12:30:09 +0200579
Elliott Hughesc6200592013-09-30 18:43:46 -0700580 *dynamic = reinterpret_cast<Elf_Dyn*>(load_bias + phdr->p_vaddr);
Ard Biesheuvel12c78bb2012-08-14 12:30:09 +0200581 if (dynamic_count) {
582 *dynamic_count = (unsigned)(phdr->p_memsz / 8);
583 }
Chris Dearmancf239052013-01-11 15:32:20 -0800584 if (dynamic_flags) {
585 *dynamic_flags = phdr->p_flags;
586 }
Ard Biesheuvel12c78bb2012-08-14 12:30:09 +0200587 return;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200588 }
Ard Biesheuvel12c78bb2012-08-14 12:30:09 +0200589 *dynamic = NULL;
590 if (dynamic_count) {
591 *dynamic_count = 0;
592 }
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200593}
594
Elliott Hughes650be4e2013-03-05 18:47:58 -0800595// Returns the address of the program header table as it appears in the loaded
596// segments in memory. This is in contrast with 'phdr_table_' which
597// is temporary and will be released before the library is relocated.
598bool ElfReader::FindPhdr() {
Elliott Hughesc6200592013-09-30 18:43:46 -0700599 const Elf_Phdr* phdr_limit = phdr_table_ + phdr_num_;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200600
Elliott Hughes650be4e2013-03-05 18:47:58 -0800601 // If there is a PT_PHDR, use it directly.
Elliott Hughesc6200592013-09-30 18:43:46 -0700602 for (const Elf_Phdr* phdr = phdr_table_; phdr < phdr_limit; ++phdr) {
Elliott Hughes650be4e2013-03-05 18:47:58 -0800603 if (phdr->p_type == PT_PHDR) {
604 return CheckPhdr(load_bias_ + phdr->p_vaddr);
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200605 }
Elliott Hughes650be4e2013-03-05 18:47:58 -0800606 }
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200607
Elliott Hughes650be4e2013-03-05 18:47:58 -0800608 // Otherwise, check the first loadable segment. If its file offset
609 // is 0, it starts with the ELF header, and we can trivially find the
610 // loaded program header from it.
Elliott Hughesc6200592013-09-30 18:43:46 -0700611 for (const Elf_Phdr* phdr = phdr_table_; phdr < phdr_limit; ++phdr) {
Elliott Hughes650be4e2013-03-05 18:47:58 -0800612 if (phdr->p_type == PT_LOAD) {
613 if (phdr->p_offset == 0) {
Elliott Hughesc6200592013-09-30 18:43:46 -0700614 Elf_Addr elf_addr = load_bias_ + phdr->p_vaddr;
615 const Elf_Ehdr* ehdr = (const Elf_Ehdr*)(void*)elf_addr;
616 Elf_Addr offset = ehdr->e_phoff;
617 return CheckPhdr((Elf_Addr)ehdr + offset);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800618 }
619 break;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200620 }
Elliott Hughes650be4e2013-03-05 18:47:58 -0800621 }
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200622
Elliott Hughes650be4e2013-03-05 18:47:58 -0800623 DL_ERR("can't find loaded phdr for \"%s\"", name_);
624 return false;
625}
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200626
Elliott Hughes650be4e2013-03-05 18:47:58 -0800627// Ensures that our program header is actually within a loadable
628// segment. This should help catch badly-formed ELF files that
629// would cause the linker to crash later when trying to access it.
Elliott Hughesc6200592013-09-30 18:43:46 -0700630bool ElfReader::CheckPhdr(Elf_Addr loaded) {
631 const Elf_Phdr* phdr_limit = phdr_table_ + phdr_num_;
632 Elf_Addr loaded_end = loaded + (phdr_num_ * sizeof(Elf_Phdr));
633 for (Elf_Phdr* phdr = phdr_table_; phdr < phdr_limit; ++phdr) {
Elliott Hughes650be4e2013-03-05 18:47:58 -0800634 if (phdr->p_type != PT_LOAD) {
635 continue;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200636 }
Elliott Hughesc6200592013-09-30 18:43:46 -0700637 Elf_Addr seg_start = phdr->p_vaddr + load_bias_;
638 Elf_Addr seg_end = phdr->p_filesz + seg_start;
Elliott Hughes650be4e2013-03-05 18:47:58 -0800639 if (seg_start <= loaded && loaded_end <= seg_end) {
Elliott Hughesc6200592013-09-30 18:43:46 -0700640 loaded_phdr_ = reinterpret_cast<const Elf_Phdr*>(loaded);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800641 return true;
642 }
643 }
Elliott Hughesc00f2cb2013-10-04 17:01:33 -0700644 DL_ERR("\"%s\" loaded phdr %p not in loadable segment", name_, reinterpret_cast<void*>(loaded));
Elliott Hughes650be4e2013-03-05 18:47:58 -0800645 return false;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200646}