blob: e5369ac61927282ac69eedc944d2930ef9ce8ca5 [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>
Elliott Hughes05fc1d72015-01-28 18:02:33 -080032#include <string.h>
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +020033#include <sys/mman.h>
Elliott Hughes99d54652018-08-22 10:36:23 -070034#include <sys/prctl.h>
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +000035#include <sys/types.h>
36#include <sys/stat.h>
37#include <unistd.h>
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +020038
Elliott Hughes650be4e2013-03-05 18:47:58 -080039#include "linker.h"
Florian Mayer4edc20d2024-10-30 14:24:26 -070040#include "linker_debug.h"
Elliott Hughes4cc5a602016-11-15 16:54:16 -080041#include "linker_dlwarning.h"
Dimitry Ivanov48ec2882016-08-04 11:50:36 -070042#include "linker_globals.h"
Florian Mayer4edc20d2024-10-30 14:24:26 -070043#include "linker_logger.h"
44#include "linker_main.h"
45#include "linker_soinfo.h"
Dmitriy Ivanov3c524812015-11-20 17:28:12 -080046#include "linker_utils.h"
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +020047
Kalesh Singh377f0b92024-01-31 20:23:39 -080048#include "private/bionic_asm_note.h"
Evgenii Stepanov0a3637d2016-07-06 13:20:59 -070049#include "private/CFIShadow.h" // For kLibraryAlignment
Kalesh Singh377f0b92024-01-31 20:23:39 -080050#include "private/elf_note.h"
Dimitry Ivanov1b84afc2016-01-27 18:12:03 -080051
Kalesh Singhc5c1d192024-04-09 16:27:56 -070052#include <android-base/file.h>
Kalesh Singhb23787f2024-09-05 08:22:06 +000053#include <android-base/properties.h>
Kalesh Singhc5c1d192024-04-09 16:27:56 -070054
Elliott Hughesb5140262014-12-02 16:16:29 -080055static int GetTargetElfMachine() {
56#if defined(__arm__)
57 return EM_ARM;
58#elif defined(__aarch64__)
59 return EM_AARCH64;
60#elif defined(__i386__)
61 return EM_386;
Elliott Hughes43462702022-10-10 19:21:44 +000062#elif defined(__riscv)
63 return EM_RISCV;
Elliott Hughesb5140262014-12-02 16:16:29 -080064#elif defined(__x86_64__)
65 return EM_X86_64;
66#endif
67}
68
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +020069/**
70 TECHNICAL NOTE ON ELF LOADING.
71
72 An ELF file's program header table contains one or more PT_LOAD
73 segments, which corresponds to portions of the file that need to
74 be mapped into the process' address space.
75
76 Each loadable segment has the following important properties:
77
78 p_offset -> segment file offset
79 p_filesz -> segment file size
80 p_memsz -> segment memory size (always >= p_filesz)
81 p_vaddr -> segment's virtual address
82 p_flags -> segment flags (e.g. readable, writable, executable)
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -070083 p_align -> segment's in-memory and in-file alignment
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +020084
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -070085 We will ignore the p_paddr field of ElfW(Phdr) for now.
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +020086
87 The loadable segments can be seen as a list of [p_vaddr ... p_vaddr+p_memsz)
88 ranges of virtual addresses. A few rules apply:
89
90 - the virtual address ranges should not overlap.
91
92 - if a segment's p_filesz is smaller than its p_memsz, the extra bytes
93 between them should always be initialized to 0.
94
95 - ranges do not necessarily start or end at page boundaries. Two distinct
96 segments can have their start and end on the same page. In this case, the
97 page inherits the mapping flags of the latter segment.
98
99 Finally, the real load addrs of each segment is not p_vaddr. Instead the
100 loader decides where to load the first segment, then will load all others
101 relative to the first one to respect the initial range layout.
102
103 For example, consider the following list:
104
105 [ offset:0, filesz:0x4000, memsz:0x4000, vaddr:0x30000 ],
106 [ offset:0x4000, filesz:0x2000, memsz:0x8000, vaddr:0x40000 ],
107
108 This corresponds to two segments that cover these virtual address ranges:
109
110 0x30000...0x34000
111 0x40000...0x48000
112
113 If the loader decides to load the first segment at address 0xa0000000
114 then the segments' load address ranges will be:
115
116 0xa0030000...0xa0034000
117 0xa0040000...0xa0048000
118
119 In other words, all segments must be loaded at an address that has the same
120 constant offset from their p_vaddr value. This offset is computed as the
121 difference between the first segment's load address, and its p_vaddr value.
122
123 However, in practice, segments do _not_ start at page boundaries. Since we
124 can only memory-map at page boundaries, this means that the bias is
125 computed as:
126
Peter Collingbournebb11ee62022-05-02 12:26:16 -0700127 load_bias = phdr0_load_address - page_start(phdr0->p_vaddr)
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200128
129 (NOTE: The value must be used as a 32-bit unsigned integer, to deal with
130 possible wrap around UINT32_MAX for possible large p_vaddr values).
131
132 And that the phdr0_load_address must start at a page boundary, with
133 the segment's real content starting at:
134
Peter Collingbournebb11ee62022-05-02 12:26:16 -0700135 phdr0_load_address + page_offset(phdr0->p_vaddr)
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200136
137 Note that ELF requires the following condition to make the mmap()-ing work:
138
Peter Collingbournebb11ee62022-05-02 12:26:16 -0700139 page_offset(phdr0->p_vaddr) == page_offset(phdr0->p_offset)
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200140
141 The load_bias must be added to any p_vaddr value read from the ELF file to
142 determine the corresponding memory address.
143
144 **/
145
Kalesh Singh1dd68582024-02-01 00:14:36 -0800146static const size_t kPageSize = page_size();
147
148/*
149 * Generic PMD size calculation:
150 * - Each page table (PT) is of size 1 page.
151 * - Each page table entry (PTE) is of size 64 bits.
152 * - Each PTE locates one physical page frame (PFN) of size 1 page.
153 * - A PMD entry locates 1 page table (PT)
154 *
155 * PMD size = Num entries in a PT * page_size
156 */
157static const size_t kPmdSize = (kPageSize / sizeof(uint64_t)) * kPageSize;
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700158
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700159ElfReader::ElfReader()
160 : did_read_(false), did_load_(false), fd_(-1), file_offset_(0), file_size_(0), phdr_num_(0),
161 phdr_table_(nullptr), shdr_table_(nullptr), shdr_num_(0), dynamic_(nullptr), strtab_(nullptr),
Dimitry Ivanovf45b0e92016-01-15 11:13:35 -0800162 strtab_size_(0), load_start_(nullptr), load_size_(0), load_bias_(0), loaded_phdr_(nullptr),
163 mapped_by_caller_(false) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700164}
165
166bool ElfReader::Read(const char* name, int fd, off64_t file_offset, off64_t file_size) {
Jiyong Park02586a22017-05-20 01:01:24 +0900167 if (did_read_) {
168 return true;
169 }
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700170 name_ = name;
171 fd_ = fd;
172 file_offset_ = file_offset;
173 file_size_ = file_size;
174
175 if (ReadElfHeader() &&
176 VerifyElfHeader() &&
177 ReadProgramHeaders() &&
178 ReadSectionHeaders() &&
Kalesh Singh377f0b92024-01-31 20:23:39 -0800179 ReadDynamicSection() &&
180 ReadPadSegmentNote()) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700181 did_read_ = true;
182 }
183
Kalesh Singhb23787f2024-09-05 08:22:06 +0000184 if (kPageSize == 0x4000 && phdr_table_get_minimum_alignment(phdr_table_, phdr_num_) == 0x1000) {
185 // This prop needs to be read on 16KiB devices for each ELF where min_palign is 4KiB.
186 // It cannot be cached since the developer may toggle app compat on/off.
187 // This check will be removed once app compat is made the default on 16KiB devices.
188 should_use_16kib_app_compat_ =
Pawan Wagh8e5de062024-10-17 18:05:19 +0000189 ::android::base::GetBoolProperty("bionic.linker.16kb.app_compat.enabled", false) ||
190 get_16kb_appcompat_mode();
Kalesh Singhb23787f2024-09-05 08:22:06 +0000191 }
192
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700193 return did_read_;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200194}
195
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -0400196bool ElfReader::Load(address_space_params* address_space) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700197 CHECK(did_read_);
Jiyong Park02586a22017-05-20 01:01:24 +0900198 if (did_load_) {
199 return true;
200 }
huangchaochaobdc37962022-12-27 19:38:41 +0800201 bool reserveSuccess = ReserveAddressSpace(address_space);
202 if (reserveSuccess && LoadSegments() && FindPhdr() &&
Tamas Petz8d55d182020-02-24 14:15:25 +0100203 FindGnuPropertySection()) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700204 did_load_ = true;
Tamas Petz8d55d182020-02-24 14:15:25 +0100205#if defined(__aarch64__)
206 // For Armv8.5-A loaded executable segments may require PROT_BTI.
207 if (note_gnu_property_.IsBTICompatible()) {
Kalesh Singhb23787f2024-09-05 08:22:06 +0000208 did_load_ =
209 (phdr_table_protect_segments(phdr_table_, phdr_num_, load_bias_, should_pad_segments_,
210 should_use_16kib_app_compat_, &note_gnu_property_) == 0);
Tamas Petz8d55d182020-02-24 14:15:25 +0100211 }
212#endif
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700213 }
huangchaochaobdc37962022-12-27 19:38:41 +0800214 if (reserveSuccess && !did_load_) {
215 if (load_start_ != nullptr && load_size_ != 0) {
216 if (!mapped_by_caller_) {
217 munmap(load_start_, load_size_);
218 }
219 }
220 }
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700221
222 return did_load_;
223}
224
225const char* ElfReader::get_string(ElfW(Word) index) const {
226 CHECK(strtab_ != nullptr);
227 CHECK(index < strtab_size_);
228
229 return strtab_ + index;
Elliott Hughes650be4e2013-03-05 18:47:58 -0800230}
231
232bool ElfReader::ReadElfHeader() {
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000233 ssize_t rc = TEMP_FAILURE_RETRY(pread64(fd_, &header_, sizeof(header_), file_offset_));
234 if (rc < 0) {
235 DL_ERR("can't read file \"%s\": %s", name_.c_str(), strerror(errno));
236 return false;
237 }
238
239 if (rc != sizeof(header_)) {
Suren Baghdasaryanea5dd952024-07-19 17:12:16 -0700240 DL_ERR("\"%s\" is too small to be an ELF executable: only found %zd bytes", name_.c_str(),
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000241 static_cast<size_t>(rc));
Elliott Hughes650be4e2013-03-05 18:47:58 -0800242 return false;
243 }
Elliott Hughes650be4e2013-03-05 18:47:58 -0800244 return true;
245}
246
Elliott Hughes72007ee2017-04-19 17:44:57 -0700247static const char* EM_to_string(int em) {
248 if (em == EM_386) return "EM_386";
249 if (em == EM_AARCH64) return "EM_AARCH64";
250 if (em == EM_ARM) return "EM_ARM";
Ulya Trafimovichb973c752022-11-15 14:39:44 +0000251 if (em == EM_RISCV) return "EM_RISCV";
Elliott Hughes72007ee2017-04-19 17:44:57 -0700252 if (em == EM_X86_64) return "EM_X86_64";
253 return "EM_???";
254}
255
Elliott Hughes650be4e2013-03-05 18:47:58 -0800256bool ElfReader::VerifyElfHeader() {
Elliott Hughes625993d2014-07-15 16:53:13 -0700257 if (memcmp(header_.e_ident, ELFMAG, SELFMAG) != 0) {
Elliott Hughesa8971512018-06-27 14:39:06 -0700258 DL_ERR("\"%s\" has bad ELF magic: %02x%02x%02x%02x", name_.c_str(),
259 header_.e_ident[0], header_.e_ident[1], header_.e_ident[2], header_.e_ident[3]);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800260 return false;
261 }
262
Elliott Hughesc00f2cb2013-10-04 17:01:33 -0700263 // Try to give a clear diagnostic for ELF class mismatches, since they're
264 // an easy mistake to make during the 32-bit/64-bit transition period.
265 int elf_class = header_.e_ident[EI_CLASS];
266#if defined(__LP64__)
267 if (elf_class != ELFCLASS64) {
268 if (elf_class == ELFCLASS32) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700269 DL_ERR("\"%s\" is 32-bit instead of 64-bit", name_.c_str());
Elliott Hughesc00f2cb2013-10-04 17:01:33 -0700270 } else {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700271 DL_ERR("\"%s\" has unknown ELF class: %d", name_.c_str(), elf_class);
Elliott Hughesc00f2cb2013-10-04 17:01:33 -0700272 }
Elliott Hughes650be4e2013-03-05 18:47:58 -0800273 return false;
274 }
Elliott Hughesc00f2cb2013-10-04 17:01:33 -0700275#else
276 if (elf_class != ELFCLASS32) {
277 if (elf_class == ELFCLASS64) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700278 DL_ERR("\"%s\" is 64-bit instead of 32-bit", name_.c_str());
Elliott Hughesc00f2cb2013-10-04 17:01:33 -0700279 } else {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700280 DL_ERR("\"%s\" has unknown ELF class: %d", name_.c_str(), elf_class);
Elliott Hughesc00f2cb2013-10-04 17:01:33 -0700281 }
282 return false;
283 }
284#endif
285
Elliott Hughes650be4e2013-03-05 18:47:58 -0800286 if (header_.e_ident[EI_DATA] != ELFDATA2LSB) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700287 DL_ERR("\"%s\" not little-endian: %d", name_.c_str(), header_.e_ident[EI_DATA]);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800288 return false;
289 }
290
291 if (header_.e_type != ET_DYN) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700292 DL_ERR("\"%s\" has unexpected e_type: %d", name_.c_str(), header_.e_type);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800293 return false;
294 }
295
296 if (header_.e_version != EV_CURRENT) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700297 DL_ERR("\"%s\" has unexpected e_version: %d", name_.c_str(), header_.e_version);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800298 return false;
299 }
300
Elliott Hughesb5140262014-12-02 16:16:29 -0800301 if (header_.e_machine != GetTargetElfMachine()) {
Elliott Hughesd16cfac2018-09-17 15:50:09 -0700302 DL_ERR("\"%s\" is for %s (%d) instead of %s (%d)",
303 name_.c_str(),
304 EM_to_string(header_.e_machine), header_.e_machine,
305 EM_to_string(GetTargetElfMachine()), GetTargetElfMachine());
Elliott Hughes650be4e2013-03-05 18:47:58 -0800306 return false;
307 }
308
Dimitry Ivanovc73eec12016-07-22 12:50:59 -0700309 if (header_.e_shentsize != sizeof(ElfW(Shdr))) {
Elliott Hughes95c6cd72019-12-20 13:26:14 -0800310 if (get_application_target_sdk_version() >= 26) {
Dimitry Ivanov12b91872016-11-16 12:29:37 -0800311 DL_ERR_AND_LOG("\"%s\" has unsupported e_shentsize: 0x%x (expected 0x%zx)",
312 name_.c_str(), header_.e_shentsize, sizeof(ElfW(Shdr)));
313 return false;
314 }
Elliott Hughes95c6cd72019-12-20 13:26:14 -0800315 DL_WARN_documented_change(26,
Elliott Hughes9076b0c2018-02-28 11:29:45 -0800316 "invalid-elf-header_section-headers-enforced-for-api-level-26",
317 "\"%s\" has unsupported e_shentsize 0x%x (expected 0x%zx)",
318 name_.c_str(), header_.e_shentsize, sizeof(ElfW(Shdr)));
Dimitry Ivanov12b91872016-11-16 12:29:37 -0800319 add_dlwarning(name_.c_str(), "has invalid ELF header");
Dimitry Ivanovc73eec12016-07-22 12:50:59 -0700320 }
321
322 if (header_.e_shstrndx == 0) {
Elliott Hughes95c6cd72019-12-20 13:26:14 -0800323 if (get_application_target_sdk_version() >= 26) {
Dimitry Ivanov12b91872016-11-16 12:29:37 -0800324 DL_ERR_AND_LOG("\"%s\" has invalid e_shstrndx", name_.c_str());
325 return false;
326 }
Elliott Hughes95c6cd72019-12-20 13:26:14 -0800327 DL_WARN_documented_change(26,
Elliott Hughes9076b0c2018-02-28 11:29:45 -0800328 "invalid-elf-header_section-headers-enforced-for-api-level-26",
329 "\"%s\" has invalid e_shstrndx", name_.c_str());
Dimitry Ivanov12b91872016-11-16 12:29:37 -0800330 add_dlwarning(name_.c_str(), "has invalid ELF header");
Dimitry Ivanovc73eec12016-07-22 12:50:59 -0700331 }
332
Elliott Hughes650be4e2013-03-05 18:47:58 -0800333 return true;
334}
335
Dimitry Ivanovbd906752016-08-08 17:12:18 -0700336bool ElfReader::CheckFileRange(ElfW(Addr) offset, size_t size, size_t alignment) {
Dmitriy Ivanov3c524812015-11-20 17:28:12 -0800337 off64_t range_start;
338 off64_t range_end;
339
Dimitry Ivanov0c9d30f2016-07-13 17:06:36 -0700340 // Only header can be located at the 0 offset... This function called to
341 // check DYNSYM and DYNAMIC sections and phdr/shdr - none of them can be
Dimitry Ivanovebe5af72016-07-14 11:15:44 -0700342 // at offset 0.
Dimitry Ivanov0c9d30f2016-07-13 17:06:36 -0700343
344 return offset > 0 &&
345 safe_add(&range_start, file_offset_, offset) &&
Dmitriy Ivanov3c524812015-11-20 17:28:12 -0800346 safe_add(&range_end, range_start, size) &&
Dimitry Ivanovbd906752016-08-08 17:12:18 -0700347 (range_start < file_size_) &&
348 (range_end <= file_size_) &&
349 ((offset % alignment) == 0);
Dmitriy Ivanov3c524812015-11-20 17:28:12 -0800350}
351
Elliott Hughes650be4e2013-03-05 18:47:58 -0800352// Loads the program header table from an ELF file into a read-only private
353// anonymous mmap-ed block.
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700354bool ElfReader::ReadProgramHeaders() {
Elliott Hughes650be4e2013-03-05 18:47:58 -0800355 phdr_num_ = header_.e_phnum;
356
357 // Like the kernel, we only accept program header tables that
358 // are smaller than 64KiB.
Elliott Hughes0266ae52014-02-10 17:46:57 -0800359 if (phdr_num_ < 1 || phdr_num_ > 65536/sizeof(ElfW(Phdr))) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700360 DL_ERR("\"%s\" has invalid e_phnum: %zd", name_.c_str(), phdr_num_);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800361 return false;
362 }
363
Dmitriy Ivanov3c524812015-11-20 17:28:12 -0800364 // Boundary checks
365 size_t size = phdr_num_ * sizeof(ElfW(Phdr));
Dimitry Ivanovbd906752016-08-08 17:12:18 -0700366 if (!CheckFileRange(header_.e_phoff, size, alignof(ElfW(Phdr)))) {
367 DL_ERR_AND_LOG("\"%s\" has invalid phdr offset/size: %zu/%zu",
368 name_.c_str(),
369 static_cast<size_t>(header_.e_phoff),
370 size);
Dmitriy Ivanov3c524812015-11-20 17:28:12 -0800371 return false;
372 }
373
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000374 if (!phdr_fragment_.Map(fd_, file_offset_, header_.e_phoff, size)) {
Elliott Hughesf5e21d92024-07-26 11:48:19 +0000375 DL_ERR("\"%s\" phdr mmap failed: %m", name_.c_str());
Elliott Hughes650be4e2013-03-05 18:47:58 -0800376 return false;
377 }
378
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000379 phdr_table_ = static_cast<ElfW(Phdr)*>(phdr_fragment_.data());
Elliott Hughes650be4e2013-03-05 18:47:58 -0800380 return true;
381}
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200382
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700383bool ElfReader::ReadSectionHeaders() {
384 shdr_num_ = header_.e_shnum;
385
Dmitriy Ivanovb76123f2015-11-20 10:42:02 -0800386 if (shdr_num_ == 0) {
Dimitry Ivanovbd906752016-08-08 17:12:18 -0700387 DL_ERR_AND_LOG("\"%s\" has no section headers", name_.c_str());
Dmitriy Ivanovb76123f2015-11-20 10:42:02 -0800388 return false;
389 }
390
Dmitriy Ivanov3c524812015-11-20 17:28:12 -0800391 size_t size = shdr_num_ * sizeof(ElfW(Shdr));
Dimitry Ivanovbd906752016-08-08 17:12:18 -0700392 if (!CheckFileRange(header_.e_shoff, size, alignof(const ElfW(Shdr)))) {
393 DL_ERR_AND_LOG("\"%s\" has invalid shdr offset/size: %zu/%zu",
394 name_.c_str(),
395 static_cast<size_t>(header_.e_shoff),
396 size);
Dmitriy Ivanov3c524812015-11-20 17:28:12 -0800397 return false;
398 }
399
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000400 if (!shdr_fragment_.Map(fd_, file_offset_, header_.e_shoff, size)) {
Elliott Hughesf5e21d92024-07-26 11:48:19 +0000401 DL_ERR("\"%s\" shdr mmap failed: %m", name_.c_str());
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700402 return false;
403 }
404
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000405 shdr_table_ = static_cast<const ElfW(Shdr)*>(shdr_fragment_.data());
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700406 return true;
407}
408
409bool ElfReader::ReadDynamicSection() {
410 // 1. Find .dynamic section (in section headers)
411 const ElfW(Shdr)* dynamic_shdr = nullptr;
412 for (size_t i = 0; i < shdr_num_; ++i) {
413 if (shdr_table_[i].sh_type == SHT_DYNAMIC) {
414 dynamic_shdr = &shdr_table_ [i];
415 break;
416 }
417 }
418
419 if (dynamic_shdr == nullptr) {
Dimitry Ivanovbd906752016-08-08 17:12:18 -0700420 DL_ERR_AND_LOG("\"%s\" .dynamic section header was not found", name_.c_str());
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700421 return false;
422 }
423
Dimitry Ivanov0c9d30f2016-07-13 17:06:36 -0700424 // Make sure dynamic_shdr offset and size matches PT_DYNAMIC phdr
425 size_t pt_dynamic_offset = 0;
426 size_t pt_dynamic_filesz = 0;
427 for (size_t i = 0; i < phdr_num_; ++i) {
428 const ElfW(Phdr)* phdr = &phdr_table_[i];
429 if (phdr->p_type == PT_DYNAMIC) {
430 pt_dynamic_offset = phdr->p_offset;
431 pt_dynamic_filesz = phdr->p_filesz;
432 }
433 }
434
435 if (pt_dynamic_offset != dynamic_shdr->sh_offset) {
Elliott Hughes95c6cd72019-12-20 13:26:14 -0800436 if (get_application_target_sdk_version() >= 26) {
Dimitry Ivanove30c17f2016-12-28 16:21:49 -0800437 DL_ERR_AND_LOG("\"%s\" .dynamic section has invalid offset: 0x%zx, "
438 "expected to match PT_DYNAMIC offset: 0x%zx",
439 name_.c_str(),
440 static_cast<size_t>(dynamic_shdr->sh_offset),
441 pt_dynamic_offset);
442 return false;
443 }
Elliott Hughes95c6cd72019-12-20 13:26:14 -0800444 DL_WARN_documented_change(26,
Elliott Hughes9076b0c2018-02-28 11:29:45 -0800445 "invalid-elf-header_section-headers-enforced-for-api-level-26",
446 "\"%s\" .dynamic section has invalid offset: 0x%zx "
447 "(expected to match PT_DYNAMIC offset 0x%zx)",
448 name_.c_str(),
449 static_cast<size_t>(dynamic_shdr->sh_offset),
450 pt_dynamic_offset);
Dimitry Ivanove30c17f2016-12-28 16:21:49 -0800451 add_dlwarning(name_.c_str(), "invalid .dynamic section");
Dimitry Ivanov0c9d30f2016-07-13 17:06:36 -0700452 }
453
454 if (pt_dynamic_filesz != dynamic_shdr->sh_size) {
Elliott Hughes95c6cd72019-12-20 13:26:14 -0800455 if (get_application_target_sdk_version() >= 26) {
Dimitry Ivanove30c17f2016-12-28 16:21:49 -0800456 DL_ERR_AND_LOG("\"%s\" .dynamic section has invalid size: 0x%zx, "
457 "expected to match PT_DYNAMIC filesz: 0x%zx",
458 name_.c_str(),
459 static_cast<size_t>(dynamic_shdr->sh_size),
460 pt_dynamic_filesz);
461 return false;
462 }
Elliott Hughes95c6cd72019-12-20 13:26:14 -0800463 DL_WARN_documented_change(26,
Elliott Hughes9076b0c2018-02-28 11:29:45 -0800464 "invalid-elf-header_section-headers-enforced-for-api-level-26",
465 "\"%s\" .dynamic section has invalid size: 0x%zx "
466 "(expected to match PT_DYNAMIC filesz 0x%zx)",
467 name_.c_str(),
468 static_cast<size_t>(dynamic_shdr->sh_size),
469 pt_dynamic_filesz);
Dimitry Ivanove30c17f2016-12-28 16:21:49 -0800470 add_dlwarning(name_.c_str(), "invalid .dynamic section");
Dimitry Ivanov0c9d30f2016-07-13 17:06:36 -0700471 }
472
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700473 if (dynamic_shdr->sh_link >= shdr_num_) {
Dimitry Ivanovbd906752016-08-08 17:12:18 -0700474 DL_ERR_AND_LOG("\"%s\" .dynamic section has invalid sh_link: %d",
475 name_.c_str(),
476 dynamic_shdr->sh_link);
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700477 return false;
478 }
479
480 const ElfW(Shdr)* strtab_shdr = &shdr_table_[dynamic_shdr->sh_link];
481
482 if (strtab_shdr->sh_type != SHT_STRTAB) {
Dimitry Ivanovbd906752016-08-08 17:12:18 -0700483 DL_ERR_AND_LOG("\"%s\" .dynamic section has invalid link(%d) sh_type: %d (expected SHT_STRTAB)",
484 name_.c_str(), dynamic_shdr->sh_link, strtab_shdr->sh_type);
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700485 return false;
486 }
487
Dimitry Ivanovbd906752016-08-08 17:12:18 -0700488 if (!CheckFileRange(dynamic_shdr->sh_offset, dynamic_shdr->sh_size, alignof(const ElfW(Dyn)))) {
489 DL_ERR_AND_LOG("\"%s\" has invalid offset/size of .dynamic section", name_.c_str());
Dmitriy Ivanov3c524812015-11-20 17:28:12 -0800490 return false;
491 }
492
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000493 if (!dynamic_fragment_.Map(fd_, file_offset_, dynamic_shdr->sh_offset, dynamic_shdr->sh_size)) {
Elliott Hughesf5e21d92024-07-26 11:48:19 +0000494 DL_ERR("\"%s\" dynamic section mmap failed: %m", name_.c_str());
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700495 return false;
496 }
497
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000498 dynamic_ = static_cast<const ElfW(Dyn)*>(dynamic_fragment_.data());
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700499
Dimitry Ivanovbd906752016-08-08 17:12:18 -0700500 if (!CheckFileRange(strtab_shdr->sh_offset, strtab_shdr->sh_size, alignof(const char))) {
501 DL_ERR_AND_LOG("\"%s\" has invalid offset/size of the .strtab section linked from .dynamic section",
502 name_.c_str());
Dmitriy Ivanov3c524812015-11-20 17:28:12 -0800503 return false;
504 }
505
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000506 if (!strtab_fragment_.Map(fd_, file_offset_, strtab_shdr->sh_offset, strtab_shdr->sh_size)) {
Elliott Hughesf5e21d92024-07-26 11:48:19 +0000507 DL_ERR("\"%s\" strtab section mmap failed: %m", name_.c_str());
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700508 return false;
509 }
510
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000511 strtab_ = static_cast<const char*>(strtab_fragment_.data());
512 strtab_size_ = strtab_fragment_.size();
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700513 return true;
514}
515
Brian Carlstrome7dffe12013-01-10 16:39:58 -0800516/* Returns the size of the extent of all the possibly non-contiguous
517 * loadable segments in an ELF program header table. This corresponds
518 * to the page-aligned size in bytes that needs to be reserved in the
519 * process' address space. If there are no loadable segments, 0 is
520 * returned.
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200521 *
Dmitriy Ivanov851135b2014-08-29 12:02:36 -0700522 * If out_min_vaddr or out_max_vaddr are not null, they will be
Brian Carlstrome7dffe12013-01-10 16:39:58 -0800523 * set to the minimum and maximum addresses of pages to be reserved,
524 * or 0 if there is nothing to load.
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200525 */
Elliott Hughes0266ae52014-02-10 17:46:57 -0800526size_t phdr_table_get_load_size(const ElfW(Phdr)* phdr_table, size_t phdr_count,
527 ElfW(Addr)* out_min_vaddr,
528 ElfW(Addr)* out_max_vaddr) {
529 ElfW(Addr) min_vaddr = UINTPTR_MAX;
530 ElfW(Addr) max_vaddr = 0;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200531
Elliott Hughes0266ae52014-02-10 17:46:57 -0800532 bool found_pt_load = false;
533 for (size_t i = 0; i < phdr_count; ++i) {
534 const ElfW(Phdr)* phdr = &phdr_table[i];
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200535
Elliott Hughes0266ae52014-02-10 17:46:57 -0800536 if (phdr->p_type != PT_LOAD) {
537 continue;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200538 }
Elliott Hughes0266ae52014-02-10 17:46:57 -0800539 found_pt_load = true;
540
541 if (phdr->p_vaddr < min_vaddr) {
542 min_vaddr = phdr->p_vaddr;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200543 }
544
Elliott Hughes0266ae52014-02-10 17:46:57 -0800545 if (phdr->p_vaddr + phdr->p_memsz > max_vaddr) {
546 max_vaddr = phdr->p_vaddr + phdr->p_memsz;
547 }
548 }
549 if (!found_pt_load) {
550 min_vaddr = 0;
551 }
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200552
Peter Collingbournebb11ee62022-05-02 12:26:16 -0700553 min_vaddr = page_start(min_vaddr);
554 max_vaddr = page_end(max_vaddr);
Elliott Hughes0266ae52014-02-10 17:46:57 -0800555
Dmitriy Ivanov851135b2014-08-29 12:02:36 -0700556 if (out_min_vaddr != nullptr) {
Elliott Hughes0266ae52014-02-10 17:46:57 -0800557 *out_min_vaddr = min_vaddr;
558 }
Dmitriy Ivanov851135b2014-08-29 12:02:36 -0700559 if (out_max_vaddr != nullptr) {
Elliott Hughes0266ae52014-02-10 17:46:57 -0800560 *out_max_vaddr = max_vaddr;
561 }
562 return max_vaddr - min_vaddr;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200563}
564
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700565// Returns the maximum p_align associated with a loadable segment in the ELF
566// program header table. Used to determine whether the file should be loaded at
567// a specific virtual address alignment for use with huge pages.
568size_t phdr_table_get_maximum_alignment(const ElfW(Phdr)* phdr_table, size_t phdr_count) {
Peter Collingbournebb11ee62022-05-02 12:26:16 -0700569 size_t maximum_alignment = page_size();
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700570
571 for (size_t i = 0; i < phdr_count; ++i) {
572 const ElfW(Phdr)* phdr = &phdr_table[i];
573
574 // p_align must be 0, 1, or a positive, integral power of two.
575 if (phdr->p_type != PT_LOAD || ((phdr->p_align & (phdr->p_align - 1)) != 0)) {
576 continue;
577 }
578
Steven Morelandfc89c8a2024-08-01 21:20:33 +0000579 maximum_alignment = std::max(maximum_alignment, static_cast<size_t>(phdr->p_align));
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700580 }
581
582#if defined(__LP64__)
583 return maximum_alignment;
584#else
Peter Collingbournebb11ee62022-05-02 12:26:16 -0700585 return page_size();
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700586#endif
587}
588
Steven Morelandfc89c8a2024-08-01 21:20:33 +0000589// Returns the minimum p_align associated with a loadable segment in the ELF
590// program header table. Used to determine if the program alignment is compatible
591// with the page size of this system.
592size_t phdr_table_get_minimum_alignment(const ElfW(Phdr)* phdr_table, size_t phdr_count) {
593 size_t minimum_alignment = page_size();
594
595 for (size_t i = 0; i < phdr_count; ++i) {
596 const ElfW(Phdr)* phdr = &phdr_table[i];
597
598 // p_align must be 0, 1, or a positive, integral power of two.
599 if (phdr->p_type != PT_LOAD || ((phdr->p_align & (phdr->p_align - 1)) != 0)) {
600 continue;
601 }
602
603 if (phdr->p_align <= 1) {
604 continue;
605 }
606
607 minimum_alignment = std::min(minimum_alignment, static_cast<size_t>(phdr->p_align));
608 }
609
610 return minimum_alignment;
611}
612
Evgenii Stepanovd13e9a62016-07-15 16:31:42 -0700613// Reserve a virtual address range such that if it's limits were extended to the next 2**align
614// boundary, it would not overlap with any existing mappings.
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700615static void* ReserveWithAlignmentPadding(size_t size, size_t mapping_align, size_t start_align,
616 void** out_gap_start, size_t* out_gap_size) {
Evgenii Stepanovd13e9a62016-07-15 16:31:42 -0700617 int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700618 // Reserve enough space to properly align the library's start address.
619 mapping_align = std::max(mapping_align, start_align);
Peter Collingbournebb11ee62022-05-02 12:26:16 -0700620 if (mapping_align == page_size()) {
Elliott Hughes8178c412018-11-05 13:34:36 -0800621 void* mmap_ptr = mmap(nullptr, size, PROT_NONE, mmap_flags, -1, 0);
Evgenii Stepanovd13e9a62016-07-15 16:31:42 -0700622 if (mmap_ptr == MAP_FAILED) {
623 return nullptr;
624 }
625 return mmap_ptr;
626 }
627
Evgenii Stepanove0848bb2020-07-14 16:44:57 -0700628 // Minimum alignment of shared library gap. For efficiency, this should match the second level
629 // page size of the platform.
630#if defined(__LP64__)
631 constexpr size_t kGapAlignment = 1ul << 21; // 2MB
632#else
633 constexpr size_t kGapAlignment = 0;
634#endif
635 // Maximum gap size, in the units of kGapAlignment.
636 constexpr size_t kMaxGapUnits = 32;
Evgenii Stepanovd13e9a62016-07-15 16:31:42 -0700637 // Allocate enough space so that the end of the desired region aligned up is still inside the
638 // mapping.
Peter Collingbournebb11ee62022-05-02 12:26:16 -0700639 size_t mmap_size = align_up(size, mapping_align) + mapping_align - page_size();
Evgenii Stepanovd13e9a62016-07-15 16:31:42 -0700640 uint8_t* mmap_ptr =
641 reinterpret_cast<uint8_t*>(mmap(nullptr, mmap_size, PROT_NONE, mmap_flags, -1, 0));
642 if (mmap_ptr == MAP_FAILED) {
643 return nullptr;
644 }
Evgenii Stepanove0848bb2020-07-14 16:44:57 -0700645 size_t gap_size = 0;
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700646 size_t first_byte = reinterpret_cast<size_t>(align_up(mmap_ptr, mapping_align));
647 size_t last_byte = reinterpret_cast<size_t>(align_down(mmap_ptr + mmap_size, mapping_align) - 1);
Evgenii Stepanove0848bb2020-07-14 16:44:57 -0700648 if (kGapAlignment && first_byte / kGapAlignment != last_byte / kGapAlignment) {
649 // This library crosses a 2MB boundary and will fragment a new huge page.
650 // Lets take advantage of that and insert a random number of inaccessible huge pages before that
651 // to improve address randomization and make it harder to locate this library code by probing.
652 munmap(mmap_ptr, mmap_size);
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700653 mapping_align = std::max(mapping_align, kGapAlignment);
Evgenii Stepanove0848bb2020-07-14 16:44:57 -0700654 gap_size =
655 kGapAlignment * (is_first_stage_init() ? 1 : arc4random_uniform(kMaxGapUnits - 1) + 1);
Peter Collingbournebb11ee62022-05-02 12:26:16 -0700656 mmap_size = align_up(size + gap_size, mapping_align) + mapping_align - page_size();
Evgenii Stepanove0848bb2020-07-14 16:44:57 -0700657 mmap_ptr = reinterpret_cast<uint8_t*>(mmap(nullptr, mmap_size, PROT_NONE, mmap_flags, -1, 0));
658 if (mmap_ptr == MAP_FAILED) {
659 return nullptr;
660 }
661 }
662
663 uint8_t *gap_end, *gap_start;
664 if (gap_size) {
665 gap_end = align_down(mmap_ptr + mmap_size, kGapAlignment);
666 gap_start = gap_end - gap_size;
667 } else {
668 gap_start = gap_end = mmap_ptr + mmap_size;
669 }
Evgenii Stepanovd13e9a62016-07-15 16:31:42 -0700670
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700671 uint8_t* first = align_up(mmap_ptr, mapping_align);
672 uint8_t* last = align_down(gap_start, mapping_align) - size;
Jiyong Park31cd08f2018-06-01 19:18:56 +0900673
Tom Cherry66bc4282018-11-08 13:40:52 -0800674 // arc4random* is not available in first stage init because /dev/urandom hasn't yet been
Jiyong Park31cd08f2018-06-01 19:18:56 +0900675 // created. Don't randomize then.
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700676 size_t n = is_first_stage_init() ? 0 : arc4random_uniform((last - first) / start_align + 1);
677 uint8_t* start = first + n * start_align;
Evgenii Stepanove0848bb2020-07-14 16:44:57 -0700678 // Unmap the extra space around the allocation.
679 // Keep it mapped PROT_NONE on 64-bit targets where address space is plentiful to make it harder
680 // to defeat ASLR by probing for readable memory mappings.
Evgenii Stepanovd13e9a62016-07-15 16:31:42 -0700681 munmap(mmap_ptr, start - mmap_ptr);
Evgenii Stepanove0848bb2020-07-14 16:44:57 -0700682 munmap(start + size, gap_start - (start + size));
683 if (gap_end != mmap_ptr + mmap_size) {
684 munmap(gap_end, mmap_ptr + mmap_size - gap_end);
685 }
686 *out_gap_start = gap_start;
687 *out_gap_size = gap_size;
Evgenii Stepanovd13e9a62016-07-15 16:31:42 -0700688 return start;
689}
690
Elliott Hughes650be4e2013-03-05 18:47:58 -0800691// Reserve a virtual address range big enough to hold all loadable
692// segments of a program header table. This is done by creating a
693// private anonymous mmap() with PROT_NONE.
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -0400694bool ElfReader::ReserveAddressSpace(address_space_params* address_space) {
Elliott Hughes0266ae52014-02-10 17:46:57 -0800695 ElfW(Addr) min_vaddr;
Brian Carlstrome7dffe12013-01-10 16:39:58 -0800696 load_size_ = phdr_table_get_load_size(phdr_table_, phdr_num_, &min_vaddr);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800697 if (load_size_ == 0) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700698 DL_ERR("\"%s\" has no loadable segments", name_.c_str());
Elliott Hughes650be4e2013-03-05 18:47:58 -0800699 return false;
700 }
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200701
Kalesh Singhce1c3cf2024-09-30 13:26:23 -0700702 if (should_use_16kib_app_compat_) {
703 // Reserve additional space for aligning the permission boundary in compat loading
704 // Up to kPageSize-kCompatPageSize additional space is needed, but reservation
705 // is done with mmap which gives kPageSize multiple-sized reservations.
706 load_size_ += kPageSize;
707 }
708
Brian Carlstrome7dffe12013-01-10 16:39:58 -0800709 uint8_t* addr = reinterpret_cast<uint8_t*>(min_vaddr);
Torne (Richard Coles)12bbb912014-02-06 14:34:21 +0000710 void* start;
Torne (Richard Coles)12bbb912014-02-06 14:34:21 +0000711
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -0400712 if (load_size_ > address_space->reserved_size) {
713 if (address_space->must_use_address) {
Torne (Richard Coles)12bbb912014-02-06 14:34:21 +0000714 DL_ERR("reserved address space %zd smaller than %zd bytes needed for \"%s\"",
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -0400715 load_size_ - address_space->reserved_size, load_size_, name_.c_str());
Torne (Richard Coles)12bbb912014-02-06 14:34:21 +0000716 return false;
717 }
Peter Collingbournebb11ee62022-05-02 12:26:16 -0700718 size_t start_alignment = page_size();
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700719 if (get_transparent_hugepages_supported() && get_application_target_sdk_version() >= 31) {
720 size_t maximum_alignment = phdr_table_get_maximum_alignment(phdr_table_, phdr_num_);
721 // Limit alignment to PMD size as other alignments reduce the number of
722 // bits available for ASLR for no benefit.
Peter Collingbournebb11ee62022-05-02 12:26:16 -0700723 start_alignment = maximum_alignment == kPmdSize ? kPmdSize : page_size();
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700724 }
725 start = ReserveWithAlignmentPadding(load_size_, kLibraryAlignment, start_alignment, &gap_start_,
726 &gap_size_);
Evgenii Stepanovd13e9a62016-07-15 16:31:42 -0700727 if (start == nullptr) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700728 DL_ERR("couldn't reserve %zd bytes of address space for \"%s\"", load_size_, name_.c_str());
Torne (Richard Coles)12bbb912014-02-06 14:34:21 +0000729 return false;
730 }
731 } else {
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -0400732 start = address_space->start_addr;
Evgenii Stepanove0848bb2020-07-14 16:44:57 -0700733 gap_start_ = nullptr;
734 gap_size_ = 0;
Dimitry Ivanovf45b0e92016-01-15 11:13:35 -0800735 mapped_by_caller_ = true;
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -0400736
737 // Update the reserved address space to subtract the space used by this library.
738 address_space->start_addr = reinterpret_cast<uint8_t*>(address_space->start_addr) + load_size_;
739 address_space->reserved_size -= load_size_;
Elliott Hughes650be4e2013-03-05 18:47:58 -0800740 }
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200741
Elliott Hughes650be4e2013-03-05 18:47:58 -0800742 load_start_ = start;
Brian Carlstrome7dffe12013-01-10 16:39:58 -0800743 load_bias_ = reinterpret_cast<uint8_t*>(start) - addr;
Kalesh Singhce1c3cf2024-09-30 13:26:23 -0700744
745 if (should_use_16kib_app_compat_) {
746 // In compat mode make the initial mapping RW since the ELF contents will be read
747 // into it; instead of mapped over it.
748 mprotect(reinterpret_cast<void*>(start), load_size_, PROT_READ | PROT_WRITE);
749 }
750
Elliott Hughes650be4e2013-03-05 18:47:58 -0800751 return true;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200752}
753
Kalesh Singhc5c1d192024-04-09 16:27:56 -0700754/*
Elliott Hughes25498992024-12-03 14:50:44 -0500755 * Returns true if the kernel supports page size migration for this process.
Kalesh Singhc5c1d192024-04-09 16:27:56 -0700756 */
757bool page_size_migration_supported() {
Elliott Hughes25498992024-12-03 14:50:44 -0500758#if defined(__LP64__)
Kalesh Singhc5c1d192024-04-09 16:27:56 -0700759 static bool pgsize_migration_enabled = []() {
760 std::string enabled;
761 if (!android::base::ReadFileToString("/sys/kernel/mm/pgsize_migration/enabled", &enabled)) {
762 return false;
763 }
764 return enabled.find("1") != std::string::npos;
765 }();
766 return pgsize_migration_enabled;
Elliott Hughes25498992024-12-03 14:50:44 -0500767#else
768 return false;
769#endif
Kalesh Singhc5c1d192024-04-09 16:27:56 -0700770}
771
Kalesh Singh377f0b92024-01-31 20:23:39 -0800772// Find the ELF note of type NT_ANDROID_TYPE_PAD_SEGMENT and check that the desc value is 1.
773bool ElfReader::ReadPadSegmentNote() {
Kalesh Singhc5c1d192024-04-09 16:27:56 -0700774 if (!page_size_migration_supported()) {
775 // Don't attempt to read the note, since segment extension isn't
776 // supported; but return true so that loading can continue normally.
777 return true;
778 }
779
Kalesh Singh377f0b92024-01-31 20:23:39 -0800780 // The ELF can have multiple PT_NOTE's, check them all
781 for (size_t i = 0; i < phdr_num_; ++i) {
782 const ElfW(Phdr)* phdr = &phdr_table_[i];
783
784 if (phdr->p_type != PT_NOTE) {
785 continue;
786 }
787
Kalesh Singh13fb3cf2024-02-08 14:58:04 -0800788 // Some obfuscated ELFs may contain "empty" PT_NOTE program headers that don't
789 // point to any part of the ELF (p_memsz == 0). Skip these since there is
790 // nothing to decode. See: b/324468126
791 if (phdr->p_memsz == 0) {
792 continue;
793 }
794
Kalesh Singh751bb8a2024-03-29 17:55:37 -0700795 // If the PT_NOTE extends beyond the file. The ELF is doing something
796 // strange -- obfuscation, embedding hidden loaders, ...
797 //
798 // It doesn't contain the pad_segment note. Skip it to avoid SIGBUS
799 // by accesses beyond the file.
800 off64_t note_end_off = file_offset_ + phdr->p_offset + phdr->p_filesz;
801 if (note_end_off > file_size_) {
802 continue;
803 }
804
Kalesh Singh377f0b92024-01-31 20:23:39 -0800805 // note_fragment is scoped to within the loop so that there is
806 // at most 1 PT_NOTE mapped at anytime during this search.
807 MappedFileFragment note_fragment;
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000808 if (!note_fragment.Map(fd_, file_offset_, phdr->p_offset, phdr->p_memsz)) {
Kalesh Singh32b6d8c2024-02-13 18:37:12 -0800809 DL_ERR("\"%s\": PT_NOTE mmap(nullptr, %p, PROT_READ, MAP_PRIVATE, %d, %p) failed: %m",
810 name_.c_str(), reinterpret_cast<void*>(phdr->p_memsz), fd_,
811 reinterpret_cast<void*>(page_start(file_offset_ + phdr->p_offset)));
Kalesh Singh13fb3cf2024-02-08 14:58:04 -0800812 return false;
Kalesh Singh377f0b92024-01-31 20:23:39 -0800813 }
814
815 const ElfW(Nhdr)* note_hdr = nullptr;
816 const char* note_desc = nullptr;
817 if (!__get_elf_note(NT_ANDROID_TYPE_PAD_SEGMENT, "Android",
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000818 reinterpret_cast<ElfW(Addr)>(note_fragment.data()),
Kalesh Singh377f0b92024-01-31 20:23:39 -0800819 phdr, &note_hdr, &note_desc)) {
820 continue;
821 }
822
823 if (note_hdr->n_descsz != sizeof(ElfW(Word))) {
824 DL_ERR("\"%s\" NT_ANDROID_TYPE_PAD_SEGMENT note has unexpected n_descsz: %u",
825 name_.c_str(), reinterpret_cast<unsigned int>(note_hdr->n_descsz));
826 return false;
827 }
828
829 // 1 == enabled, 0 == disabled
830 should_pad_segments_ = *reinterpret_cast<const ElfW(Word)*>(note_desc) == 1;
831 return true;
832 }
833
834 return true;
835}
836
Kalesh Singh4084b552024-03-13 13:35:49 -0700837static inline void _extend_load_segment_vma(const ElfW(Phdr)* phdr_table, size_t phdr_count,
Kalesh Singhb23787f2024-09-05 08:22:06 +0000838 size_t phdr_idx, ElfW(Addr)* p_memsz,
839 ElfW(Addr)* p_filesz, bool should_pad_segments,
840 bool should_use_16kib_app_compat) {
841 // NOTE: Segment extension is only applicable where the ELF's max-page-size > runtime page size;
842 // to save kernel VMA slab memory. 16KiB compat mode is the exact opposite scenario.
843 if (should_use_16kib_app_compat) {
844 return;
845 }
846
Kalesh Singh4084b552024-03-13 13:35:49 -0700847 const ElfW(Phdr)* phdr = &phdr_table[phdr_idx];
848 const ElfW(Phdr)* next = nullptr;
849 size_t next_idx = phdr_idx + 1;
850
Kalesh Singhe1e74792024-04-09 11:48:52 -0700851 // Don't do segment extension for p_align > 64KiB, such ELFs already existed in the
852 // field e.g. 2MiB p_align for THPs and are relatively small in number.
853 //
854 // The kernel can only represent padding for p_align up to 64KiB. This is because
855 // the kernel uses 4 available bits in the vm_area_struct to represent padding
856 // extent; and so cannot enable mitigations to avoid breaking app compatibility for
857 // p_aligns > 64KiB.
858 //
859 // Don't perform segment extension on these to avoid app compatibility issues.
860 if (phdr->p_align <= kPageSize || phdr->p_align > 64*1024 || !should_pad_segments) {
Kalesh Singh4084b552024-03-13 13:35:49 -0700861 return;
862 }
863
864 if (next_idx < phdr_count && phdr_table[next_idx].p_type == PT_LOAD) {
865 next = &phdr_table[next_idx];
866 }
867
868 // If this is the last LOAD segment, no extension is needed
869 if (!next || *p_memsz != *p_filesz) {
870 return;
871 }
872
873 ElfW(Addr) next_start = page_start(next->p_vaddr);
874 ElfW(Addr) curr_end = page_end(phdr->p_vaddr + *p_memsz);
875
876 // If adjacent segment mappings overlap, no extension is needed.
877 if (curr_end >= next_start) {
878 return;
879 }
880
881 // Extend the LOAD segment mapping to be contiguous with that of
882 // the next LOAD segment.
883 ElfW(Addr) extend = next_start - curr_end;
884 *p_memsz += extend;
885 *p_filesz += extend;
886}
887
Kalesh Singh86e04f62024-09-05 06:24:14 +0000888bool ElfReader::MapSegment(size_t seg_idx, size_t len) {
889 const ElfW(Phdr)* phdr = &phdr_table_[seg_idx];
890
891 void* start = reinterpret_cast<void*>(page_start(phdr->p_vaddr + load_bias_));
892
893 // The ELF could be being loaded directly from a zipped APK,
894 // the zip offset must be added to find the segment offset.
895 const ElfW(Addr) offset = file_offset_ + page_start(phdr->p_offset);
896
897 int prot = PFLAGS_TO_PROT(phdr->p_flags);
898
899 void* seg_addr = mmap64(start, len, prot, MAP_FIXED | MAP_PRIVATE, fd_, offset);
900
901 if (seg_addr == MAP_FAILED) {
902 DL_ERR("couldn't map \"%s\" segment %zd: %m", name_.c_str(), seg_idx);
903 return false;
904 }
905
906 // Mark segments as huge page eligible if they meet the requirements
907 if ((phdr->p_flags & PF_X) && phdr->p_align == kPmdSize &&
908 get_transparent_hugepages_supported()) {
909 madvise(seg_addr, len, MADV_HUGEPAGE);
910 }
911
912 return true;
913}
914
Kalesh Singh37bcaea2024-09-05 06:32:07 +0000915void ElfReader::ZeroFillSegment(const ElfW(Phdr)* phdr) {
Kalesh Singhb23787f2024-09-05 08:22:06 +0000916 // NOTE: In 16KiB app compat mode, the ELF mapping is anonymous, meaning that
917 // RW segments are COW-ed from the kernel's zero page. So there is no need to
918 // explicitly zero-fill until the last page's limit.
919 if (should_use_16kib_app_compat_) {
920 return;
921 }
922
Kalesh Singh37bcaea2024-09-05 06:32:07 +0000923 ElfW(Addr) seg_start = phdr->p_vaddr + load_bias_;
924 uint64_t unextended_seg_file_end = seg_start + phdr->p_filesz;
925
926 // If the segment is writable, and does not end on a page boundary,
927 // zero-fill it until the page limit.
928 //
929 // Do not attempt to zero the extended region past the first partial page,
930 // since doing so may:
931 // 1) Result in a SIGBUS, as the region is not backed by the underlying
932 // file.
933 // 2) Break the COW backing, faulting in new anon pages for a region
934 // that will not be used.
935 if ((phdr->p_flags & PF_W) != 0 && page_offset(unextended_seg_file_end) > 0) {
936 memset(reinterpret_cast<void*>(unextended_seg_file_end), 0,
937 kPageSize - page_offset(unextended_seg_file_end));
938 }
939}
940
Kalesh Singhe0f4a372024-09-05 07:07:21 +0000941void ElfReader::DropPaddingPages(const ElfW(Phdr)* phdr, uint64_t seg_file_end) {
Kalesh Singhb23787f2024-09-05 08:22:06 +0000942 // NOTE: Padding pages are only applicable where the ELF's max-page-size > runtime page size;
943 // 16KiB compat mode is the exact opposite scenario.
944 if (should_use_16kib_app_compat_) {
945 return;
946 }
947
Kalesh Singhe0f4a372024-09-05 07:07:21 +0000948 ElfW(Addr) seg_start = phdr->p_vaddr + load_bias_;
949 uint64_t unextended_seg_file_end = seg_start + phdr->p_filesz;
950
951 uint64_t pad_start = page_end(unextended_seg_file_end);
952 uint64_t pad_end = page_end(seg_file_end);
953 CHECK(pad_start <= pad_end);
954
955 uint64_t pad_len = pad_end - pad_start;
956 if (pad_len == 0 || !page_size_migration_supported()) {
957 return;
958 }
959
960 // Pages may be brought in due to readahead.
961 // Drop the padding (zero) pages, to avoid reclaim work later.
962 //
963 // NOTE: The madvise() here is special, as it also serves to hint to the
964 // kernel the portion of the LOAD segment that is padding.
965 //
966 // See: [1] https://android-review.googlesource.com/c/kernel/common/+/3032411
967 // [2] https://android-review.googlesource.com/c/kernel/common/+/3048835
968 if (madvise(reinterpret_cast<void*>(pad_start), pad_len, MADV_DONTNEED)) {
969 DL_WARN("\"%s\": madvise(0x%" PRIx64 ", 0x%" PRIx64 ", MADV_DONTNEED) failed: %m",
970 name_.c_str(), pad_start, pad_len);
971 }
972}
973
Kalesh Singh138a9552024-09-05 08:05:56 +0000974bool ElfReader::MapBssSection(const ElfW(Phdr)* phdr, ElfW(Addr) seg_page_end,
975 ElfW(Addr) seg_file_end) {
Kalesh Singhb23787f2024-09-05 08:22:06 +0000976 // NOTE: We do not need to handle .bss in 16KiB compat mode since the mapping
977 // reservation is anonymous and RW to begin with.
978 if (should_use_16kib_app_compat_) {
979 return true;
980 }
981
Kalesh Singh138a9552024-09-05 08:05:56 +0000982 // seg_file_end is now the first page address after the file content.
983 seg_file_end = page_end(seg_file_end);
984
985 if (seg_page_end <= seg_file_end) {
986 return true;
987 }
988
989 // If seg_page_end is larger than seg_file_end, we need to zero
990 // anything between them. This is done by using a private anonymous
991 // map for all extra pages
992 size_t zeromap_size = seg_page_end - seg_file_end;
993 void* zeromap =
994 mmap(reinterpret_cast<void*>(seg_file_end), zeromap_size, PFLAGS_TO_PROT(phdr->p_flags),
995 MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
996 if (zeromap == MAP_FAILED) {
997 DL_ERR("couldn't map .bss section for \"%s\": %m", name_.c_str());
998 return false;
999 }
1000
1001 // Set the VMA name using prctl
1002 prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, zeromap, zeromap_size, ".bss");
1003
1004 return true;
1005}
1006
Elliott Hughes650be4e2013-03-05 18:47:58 -08001007bool ElfReader::LoadSegments() {
Kalesh Singhce1c3cf2024-09-30 13:26:23 -07001008 // NOTE: The compat(legacy) page size (4096) must be used when aligning
1009 // the 4KiB segments for loading in compat mode. The larger 16KiB page size
1010 // will lead to overwriting adjacent segments since the ELF's segment(s)
1011 // are not 16KiB aligned.
1012 size_t seg_align = should_use_16kib_app_compat_ ? kCompatPageSize : kPageSize;
Kalesh Singhb23787f2024-09-05 08:22:06 +00001013
Steven Morelandfc89c8a2024-08-01 21:20:33 +00001014 size_t min_palign = phdr_table_get_minimum_alignment(phdr_table_, phdr_num_);
Kalesh Singhb23787f2024-09-05 08:22:06 +00001015 // Only enforce this on 16 KB systems with app compat disabled.
1016 // Apps may rely on undefined behavior here on 4 KB systems,
1017 // which is the norm before this change is introduced
1018 if (kPageSize >= 16384 && min_palign < kPageSize && !should_use_16kib_app_compat_) {
Steven Morelandfc89c8a2024-08-01 21:20:33 +00001019 DL_ERR("\"%s\" program alignment (%zu) cannot be smaller than system page size (%zu)",
1020 name_.c_str(), min_palign, kPageSize);
1021 return false;
1022 }
1023
Kalesh Singhce1c3cf2024-09-30 13:26:23 -07001024 if (!Setup16KiBAppCompat()) {
1025 DL_ERR("\"%s\" failed to setup 16KiB App Compat", name_.c_str());
1026 return false;
1027 }
1028
Elliott Hughes650be4e2013-03-05 18:47:58 -08001029 for (size_t i = 0; i < phdr_num_; ++i) {
Elliott Hughes0266ae52014-02-10 17:46:57 -08001030 const ElfW(Phdr)* phdr = &phdr_table_[i];
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001031
Elliott Hughes650be4e2013-03-05 18:47:58 -08001032 if (phdr->p_type != PT_LOAD) {
1033 continue;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001034 }
Elliott Hughes650be4e2013-03-05 18:47:58 -08001035
Kalesh Singh4084b552024-03-13 13:35:49 -07001036 ElfW(Addr) p_memsz = phdr->p_memsz;
1037 ElfW(Addr) p_filesz = phdr->p_filesz;
Kalesh Singhb23787f2024-09-05 08:22:06 +00001038 _extend_load_segment_vma(phdr_table_, phdr_num_, i, &p_memsz, &p_filesz, should_pad_segments_,
1039 should_use_16kib_app_compat_);
Kalesh Singh4084b552024-03-13 13:35:49 -07001040
Elliott Hughes650be4e2013-03-05 18:47:58 -08001041 // Segment addresses in memory.
Elliott Hughes0266ae52014-02-10 17:46:57 -08001042 ElfW(Addr) seg_start = phdr->p_vaddr + load_bias_;
Kalesh Singh4084b552024-03-13 13:35:49 -07001043 ElfW(Addr) seg_end = seg_start + p_memsz;
Elliott Hughes650be4e2013-03-05 18:47:58 -08001044
Kalesh Singhb23787f2024-09-05 08:22:06 +00001045 ElfW(Addr) seg_page_end = align_up(seg_end, seg_align);
Elliott Hughes650be4e2013-03-05 18:47:58 -08001046
Kalesh Singh4084b552024-03-13 13:35:49 -07001047 ElfW(Addr) seg_file_end = seg_start + p_filesz;
Elliott Hughes650be4e2013-03-05 18:47:58 -08001048
1049 // File offsets.
Elliott Hughes0266ae52014-02-10 17:46:57 -08001050 ElfW(Addr) file_start = phdr->p_offset;
Kalesh Singh4084b552024-03-13 13:35:49 -07001051 ElfW(Addr) file_end = file_start + p_filesz;
Elliott Hughes650be4e2013-03-05 18:47:58 -08001052
Kalesh Singhb23787f2024-09-05 08:22:06 +00001053 ElfW(Addr) file_page_start = align_down(file_start, seg_align);
Elliott Hughes0266ae52014-02-10 17:46:57 -08001054 ElfW(Addr) file_length = file_end - file_page_start;
Elliott Hughes650be4e2013-03-05 18:47:58 -08001055
Dmitriy Ivanov3f987f52015-06-25 15:51:41 -07001056 if (file_size_ <= 0) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -07001057 DL_ERR("\"%s\" invalid file size: %" PRId64, name_.c_str(), file_size_);
Dmitriy Ivanov3f987f52015-06-25 15:51:41 -07001058 return false;
1059 }
1060
Kalesh Singh4084b552024-03-13 13:35:49 -07001061 if (file_start + phdr->p_filesz > static_cast<size_t>(file_size_)) {
Dmitriy Ivanov3f987f52015-06-25 15:51:41 -07001062 DL_ERR("invalid ELF file \"%s\" load segment[%zd]:"
1063 " p_offset (%p) + p_filesz (%p) ( = %p) past end of file (0x%" PRIx64 ")",
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -07001064 name_.c_str(), i, reinterpret_cast<void*>(phdr->p_offset),
Dmitriy Ivanov3f987f52015-06-25 15:51:41 -07001065 reinterpret_cast<void*>(phdr->p_filesz),
Kalesh Singh4084b552024-03-13 13:35:49 -07001066 reinterpret_cast<void*>(file_start + phdr->p_filesz), file_size_);
Dmitriy Ivanov3f987f52015-06-25 15:51:41 -07001067 return false;
1068 }
1069
Brian Carlstrom82dcc792013-05-21 16:49:24 -07001070 if (file_length != 0) {
Dimitry Ivanov9700bab2016-08-10 18:54:06 -07001071 int prot = PFLAGS_TO_PROT(phdr->p_flags);
Dimitry Ivanov9700bab2016-08-10 18:54:06 -07001072 if ((prot & (PROT_EXEC | PROT_WRITE)) == (PROT_EXEC | PROT_WRITE)) {
Elliott Hughes4cc5a602016-11-15 16:54:16 -08001073 // W + E PT_LOAD segments are not allowed in O.
Elliott Hughes95c6cd72019-12-20 13:26:14 -08001074 if (get_application_target_sdk_version() >= 26) {
Elliott Hughes9076b0c2018-02-28 11:29:45 -08001075 DL_ERR_AND_LOG("\"%s\": W+E load segments are not allowed", name_.c_str());
Elliott Hughes4cc5a602016-11-15 16:54:16 -08001076 return false;
1077 }
Elliott Hughes95c6cd72019-12-20 13:26:14 -08001078 DL_WARN_documented_change(26,
Elliott Hughes9076b0c2018-02-28 11:29:45 -08001079 "writable-and-executable-segments-enforced-for-api-level-26",
1080 "\"%s\" has load segments that are both writable and executable",
1081 name_.c_str());
Elliott Hughes4cc5a602016-11-15 16:54:16 -08001082 add_dlwarning(name_.c_str(), "W+E load segments");
Dimitry Ivanov9700bab2016-08-10 18:54:06 -07001083 }
1084
Kalesh Singh86e04f62024-09-05 06:24:14 +00001085 // Pass the file_length, since it may have been extended by _extend_load_segment_vma().
Kalesh Singhce1c3cf2024-09-30 13:26:23 -07001086 if (should_use_16kib_app_compat_) {
1087 if (!CompatMapSegment(i, file_length)) {
1088 return false;
1089 }
1090 } else {
1091 if (!MapSegment(i, file_length)) {
1092 return false;
1093 }
Brian Carlstrom82dcc792013-05-21 16:49:24 -07001094 }
Elliott Hughes650be4e2013-03-05 18:47:58 -08001095 }
1096
Kalesh Singh37bcaea2024-09-05 06:32:07 +00001097 ZeroFillSegment(phdr);
Kalesh Singh1d3ba112024-03-06 17:33:36 -08001098
Kalesh Singhe0f4a372024-09-05 07:07:21 +00001099 DropPaddingPages(phdr, seg_file_end);
Elliott Hughes650be4e2013-03-05 18:47:58 -08001100
Kalesh Singh138a9552024-09-05 08:05:56 +00001101 if (!MapBssSection(phdr, seg_page_end, seg_file_end)) {
1102 return false;
Elliott Hughes650be4e2013-03-05 18:47:58 -08001103 }
1104 }
1105 return true;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001106}
1107
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001108/* Used internally. Used to set the protection bits of all loaded segments
1109 * with optional extra flags (i.e. really PROT_WRITE). Used by
1110 * phdr_table_protect_segments and phdr_table_unprotect_segments.
1111 */
1112static int _phdr_table_set_load_prot(const ElfW(Phdr)* phdr_table, size_t phdr_count,
Kalesh Singh4084b552024-03-13 13:35:49 -07001113 ElfW(Addr) load_bias, int extra_prot_flags,
Kalesh Singhb23787f2024-09-05 08:22:06 +00001114 bool should_pad_segments, bool should_use_16kib_app_compat) {
Kalesh Singh4084b552024-03-13 13:35:49 -07001115 for (size_t i = 0; i < phdr_count; ++i) {
1116 const ElfW(Phdr)* phdr = &phdr_table[i];
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001117
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001118 if (phdr->p_type != PT_LOAD || (phdr->p_flags & PF_W) != 0) {
1119 continue;
1120 }
1121
Kalesh Singh4084b552024-03-13 13:35:49 -07001122 ElfW(Addr) p_memsz = phdr->p_memsz;
1123 ElfW(Addr) p_filesz = phdr->p_filesz;
Kalesh Singhb23787f2024-09-05 08:22:06 +00001124 _extend_load_segment_vma(phdr_table, phdr_count, i, &p_memsz, &p_filesz, should_pad_segments,
1125 should_use_16kib_app_compat);
Kalesh Singh4084b552024-03-13 13:35:49 -07001126
1127 ElfW(Addr) seg_page_start = page_start(phdr->p_vaddr + load_bias);
1128 ElfW(Addr) seg_page_end = page_end(phdr->p_vaddr + p_memsz + load_bias);
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001129
Tamas Petz8d55d182020-02-24 14:15:25 +01001130 int prot = PFLAGS_TO_PROT(phdr->p_flags) | extra_prot_flags;
1131 if ((prot & PROT_WRITE) != 0) {
Nick Kralevich8fdb3412015-04-01 16:57:50 -07001132 // make sure we're never simultaneously writable / executable
1133 prot &= ~PROT_EXEC;
1134 }
Tamas Petz8d55d182020-02-24 14:15:25 +01001135#if defined(__aarch64__)
1136 if ((prot & PROT_EXEC) == 0) {
1137 // Though it is not specified don't add PROT_BTI if segment is not
1138 // executable.
1139 prot &= ~PROT_BTI;
1140 }
1141#endif
Nick Kralevich8fdb3412015-04-01 16:57:50 -07001142
Tamas Petz8d55d182020-02-24 14:15:25 +01001143 int ret =
1144 mprotect(reinterpret_cast<void*>(seg_page_start), seg_page_end - seg_page_start, prot);
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001145 if (ret < 0) {
1146 return -1;
1147 }
1148 }
1149 return 0;
1150}
1151
1152/* Restore the original protection modes for all loadable segments.
1153 * You should only call this after phdr_table_unprotect_segments and
1154 * applying all relocations.
1155 *
Tamas Petz8d55d182020-02-24 14:15:25 +01001156 * AArch64: also called from linker_main and ElfReader::Load to apply
1157 * PROT_BTI for loaded main so and other so-s.
1158 *
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001159 * Input:
1160 * phdr_table -> program header table
1161 * phdr_count -> number of entries in tables
1162 * load_bias -> load bias
Kalesh Singh4084b552024-03-13 13:35:49 -07001163 * should_pad_segments -> Are segments extended to avoid gaps in the memory map
Kalesh Singhb23787f2024-09-05 08:22:06 +00001164 * should_use_16kib_app_compat -> Is the ELF being loaded in 16KiB app compat mode.
Tamas Petz8d55d182020-02-24 14:15:25 +01001165 * prop -> GnuPropertySection or nullptr
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001166 * Return:
Mitch Phillips117e45e2023-10-20 13:32:33 +00001167 * 0 on success, -1 on failure (error code in errno).
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001168 */
Tamas Petz8d55d182020-02-24 14:15:25 +01001169int phdr_table_protect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count,
Kalesh Singh4084b552024-03-13 13:35:49 -07001170 ElfW(Addr) load_bias, bool should_pad_segments,
Kalesh Singhb23787f2024-09-05 08:22:06 +00001171 bool should_use_16kib_app_compat,
Kalesh Singh4084b552024-03-13 13:35:49 -07001172 const GnuPropertySection* prop __unused) {
Tamas Petz8d55d182020-02-24 14:15:25 +01001173 int prot = 0;
1174#if defined(__aarch64__)
1175 if ((prop != nullptr) && prop->IsBTICompatible()) {
1176 prot |= PROT_BTI;
1177 }
1178#endif
Kalesh Singhb23787f2024-09-05 08:22:06 +00001179 return _phdr_table_set_load_prot(phdr_table, phdr_count, load_bias, prot, should_pad_segments,
1180 should_use_16kib_app_compat);
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001181}
1182
Florian Mayer4edc20d2024-10-30 14:24:26 -07001183static bool segment_needs_memtag_globals_remapping(const ElfW(Phdr) * phdr) {
1184 // For now, MTE globals is only supported on writeable data segments.
1185 return phdr->p_type == PT_LOAD && !(phdr->p_flags & PF_X) && (phdr->p_flags & PF_W);
1186}
1187
1188/* When MTE globals are requested by the binary, and when the hardware supports
1189 * it, remap the executable's PT_LOAD data pages to have PROT_MTE.
1190 *
1191 * Returns 0 on success, -1 on failure (error code in errno).
1192 */
1193int remap_memtag_globals_segments(const ElfW(Phdr) * phdr_table __unused,
1194 size_t phdr_count __unused, ElfW(Addr) load_bias __unused) {
1195#if defined(__aarch64__)
1196 for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_table + phdr_count; phdr++) {
1197 if (!segment_needs_memtag_globals_remapping(phdr)) {
1198 continue;
1199 }
1200
1201 uintptr_t seg_page_start = page_start(phdr->p_vaddr) + load_bias;
1202 uintptr_t seg_page_end = page_end(phdr->p_vaddr + phdr->p_memsz) + load_bias;
1203 size_t seg_page_aligned_size = seg_page_end - seg_page_start;
1204
1205 int prot = PFLAGS_TO_PROT(phdr->p_flags);
1206 // For anonymous private mappings, it may be possible to simply mprotect()
1207 // the PROT_MTE flag over the top. For file-based mappings, this will fail,
1208 // and we'll need to fall back. We also allow PROT_WRITE here to allow
1209 // writing memory tags (in `soinfo::tag_globals()`), and set these sections
1210 // back to read-only after tags are applied (similar to RELRO).
1211 prot |= PROT_MTE;
1212 if (mprotect(reinterpret_cast<void*>(seg_page_start), seg_page_aligned_size,
1213 prot | PROT_WRITE) == 0) {
1214 continue;
1215 }
1216
1217 void* mapping_copy = mmap(nullptr, seg_page_aligned_size, PROT_READ | PROT_WRITE,
1218 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
1219 linker_memcpy(mapping_copy, reinterpret_cast<void*>(seg_page_start), seg_page_aligned_size);
1220
1221 void* seg_addr = mmap(reinterpret_cast<void*>(seg_page_start), seg_page_aligned_size,
1222 prot | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
1223 if (seg_addr == MAP_FAILED) return -1;
1224
1225 linker_memcpy(seg_addr, mapping_copy, seg_page_aligned_size);
1226 munmap(mapping_copy, seg_page_aligned_size);
1227 }
1228#endif // defined(__aarch64__)
1229 return 0;
1230}
1231
1232void protect_memtag_globals_ro_segments(const ElfW(Phdr) * phdr_table __unused,
1233 size_t phdr_count __unused, ElfW(Addr) load_bias __unused) {
1234#if defined(__aarch64__)
1235 for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_table + phdr_count; phdr++) {
1236 int prot = PFLAGS_TO_PROT(phdr->p_flags);
1237 if (!segment_needs_memtag_globals_remapping(phdr) || (prot & PROT_WRITE)) {
1238 continue;
1239 }
1240
1241 prot |= PROT_MTE;
1242
1243 uintptr_t seg_page_start = page_start(phdr->p_vaddr) + load_bias;
1244 uintptr_t seg_page_end = page_end(phdr->p_vaddr + phdr->p_memsz) + load_bias;
1245 size_t seg_page_aligned_size = seg_page_end - seg_page_start;
1246 mprotect(reinterpret_cast<void*>(seg_page_start), seg_page_aligned_size, prot);
1247 }
1248#endif // defined(__aarch64__)
1249}
1250
1251void name_memtag_globals_segments(const ElfW(Phdr) * phdr_table, size_t phdr_count,
1252 ElfW(Addr) load_bias, const char* soname,
1253 std::list<std::string>* vma_names) {
1254 for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_table + phdr_count; phdr++) {
1255 if (!segment_needs_memtag_globals_remapping(phdr)) {
1256 continue;
1257 }
1258
1259 uintptr_t seg_page_start = page_start(phdr->p_vaddr) + load_bias;
1260 uintptr_t seg_page_end = page_end(phdr->p_vaddr + phdr->p_memsz) + load_bias;
1261 size_t seg_page_aligned_size = seg_page_end - seg_page_start;
1262
1263 // For file-based mappings that we're now forcing to be anonymous mappings, set the VMA name to
1264 // make debugging easier.
1265 // Once we are targeting only devices that run kernel 5.10 or newer (and thus include
1266 // https://android-review.git.corp.google.com/c/kernel/common/+/1934723 which causes the
1267 // VMA_ANON_NAME to be copied into the kernel), we can get rid of the storage here.
1268 // For now, that is not the case:
1269 // https://source.android.com/docs/core/architecture/kernel/android-common#compatibility-matrix
1270 constexpr int kVmaNameLimit = 80;
1271 std::string& vma_name = vma_names->emplace_back(kVmaNameLimit, '\0');
1272 int full_vma_length =
1273 async_safe_format_buffer(vma_name.data(), kVmaNameLimit, "mt:%s+%" PRIxPTR, soname,
1274 page_start(phdr->p_vaddr)) +
1275 /* include the null terminator */ 1;
1276 // There's an upper limit of 80 characters, including the null terminator, in the anonymous VMA
1277 // name. If we run over that limit, we end up truncating the segment offset and parts of the
1278 // DSO's name, starting on the right hand side of the basename. Because the basename is the most
1279 // important thing, chop off the soname from the left hand side first.
1280 //
1281 // Example (with '#' as the null terminator):
1282 // - "mt:/data/nativetest64/bionic-unit-tests/bionic-loader-test-libs/libdlext_test.so+e000#"
1283 // is a `full_vma_length` == 86.
1284 //
1285 // We need to left-truncate (86 - 80) 6 characters from the soname, plus the
1286 // `vma_truncation_prefix`, so 9 characters total.
1287 if (full_vma_length > kVmaNameLimit) {
1288 const char vma_truncation_prefix[] = "...";
1289 int soname_truncated_bytes =
1290 full_vma_length - kVmaNameLimit + sizeof(vma_truncation_prefix) - 1;
1291 async_safe_format_buffer(vma_name.data(), kVmaNameLimit, "mt:%s%s+%" PRIxPTR,
1292 vma_truncation_prefix, soname + soname_truncated_bytes,
1293 page_start(phdr->p_vaddr));
1294 }
1295 if (prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, reinterpret_cast<void*>(seg_page_start),
1296 seg_page_aligned_size, vma_name.data()) != 0) {
1297 DL_WARN("Failed to rename memtag global segment: %m");
1298 }
1299 }
1300}
1301
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001302/* Change the protection of all loaded segments in memory to writable.
1303 * This is useful before performing relocations. Once completed, you
1304 * will have to call phdr_table_protect_segments to restore the original
1305 * protection flags on all segments.
1306 *
1307 * Note that some writable segments can also have their content turned
1308 * to read-only by calling phdr_table_protect_gnu_relro. This is no
1309 * performed here.
1310 *
1311 * Input:
1312 * phdr_table -> program header table
1313 * phdr_count -> number of entries in tables
1314 * load_bias -> load bias
Kalesh Singh4084b552024-03-13 13:35:49 -07001315 * should_pad_segments -> Are segments extended to avoid gaps in the memory map
Kalesh Singhb23787f2024-09-05 08:22:06 +00001316 * should_use_16kib_app_compat -> Is the ELF being loaded in 16KiB app compat mode.
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001317 * Return:
Mitch Phillips117e45e2023-10-20 13:32:33 +00001318 * 0 on success, -1 on failure (error code in errno).
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001319 */
Kalesh Singhb23787f2024-09-05 08:22:06 +00001320int phdr_table_unprotect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count,
1321 ElfW(Addr) load_bias, bool should_pad_segments,
1322 bool should_use_16kib_app_compat) {
Kalesh Singh4084b552024-03-13 13:35:49 -07001323 return _phdr_table_set_load_prot(phdr_table, phdr_count, load_bias, PROT_WRITE,
Kalesh Singhb23787f2024-09-05 08:22:06 +00001324 should_pad_segments, should_use_16kib_app_compat);
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001325}
1326
Kalesh Singh702d9b02024-03-13 13:38:04 -07001327static inline void _extend_gnu_relro_prot_end(const ElfW(Phdr)* relro_phdr,
1328 const ElfW(Phdr)* phdr_table, size_t phdr_count,
1329 ElfW(Addr) load_bias, ElfW(Addr)* seg_page_end,
Kalesh Singhb23787f2024-09-05 08:22:06 +00001330 bool should_pad_segments,
1331 bool should_use_16kib_app_compat) {
Kalesh Singh702d9b02024-03-13 13:38:04 -07001332 // Find the index and phdr of the LOAD containing the GNU_RELRO segment
1333 for (size_t index = 0; index < phdr_count; ++index) {
1334 const ElfW(Phdr)* phdr = &phdr_table[index];
1335
1336 if (phdr->p_type == PT_LOAD && phdr->p_vaddr == relro_phdr->p_vaddr) {
1337 // If the PT_GNU_RELRO mem size is not at least as large as the corresponding
1338 // LOAD segment mem size, we need to protect only a partial region of the
1339 // LOAD segment and therefore cannot avoid a VMA split.
1340 //
1341 // Note: Don't check the page-aligned mem sizes since the extended protection
1342 // may incorrectly write protect non-relocation data.
1343 //
1344 // Example:
1345 //
1346 // |---- 3K ----|-- 1K --|---- 3K ---- |-- 1K --|
1347 // ----------------------------------------------------------------
1348 // | | | | |
1349 // SEG X | RO | RO | RW | | SEG Y
1350 // | | | | |
1351 // ----------------------------------------------------------------
1352 // | | |
1353 // | | |
1354 // | | |
1355 // relro_vaddr relro_vaddr relro_vaddr
1356 // (load_vaddr) + +
1357 // relro_memsz load_memsz
1358 //
1359 // ----------------------------------------------------------------
1360 // | PAGE | PAGE |
1361 // ----------------------------------------------------------------
1362 // | Potential |
1363 // |----- Extended RO ----|
1364 // | Protection |
1365 //
1366 // If the check below uses page aligned mem sizes it will cause incorrect write
1367 // protection of the 3K RW part of the LOAD segment containing the GNU_RELRO.
1368 if (relro_phdr->p_memsz < phdr->p_memsz) {
1369 return;
1370 }
1371
1372 ElfW(Addr) p_memsz = phdr->p_memsz;
1373 ElfW(Addr) p_filesz = phdr->p_filesz;
1374
1375 // Attempt extending the VMA (mprotect range). Without extending the range,
1376 // mprotect will only RO protect a part of the extended RW LOAD segment, which
1377 // will leave an extra split RW VMA (the gap).
1378 _extend_load_segment_vma(phdr_table, phdr_count, index, &p_memsz, &p_filesz,
Kalesh Singhb23787f2024-09-05 08:22:06 +00001379 should_pad_segments, should_use_16kib_app_compat);
Kalesh Singh702d9b02024-03-13 13:38:04 -07001380
1381 *seg_page_end = page_end(phdr->p_vaddr + p_memsz + load_bias);
1382 return;
1383 }
1384 }
1385}
1386
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001387/* Used internally by phdr_table_protect_gnu_relro and
1388 * phdr_table_unprotect_gnu_relro.
1389 */
Elliott Hughes0266ae52014-02-10 17:46:57 -08001390static int _phdr_table_set_gnu_relro_prot(const ElfW(Phdr)* phdr_table, size_t phdr_count,
Kalesh Singh702d9b02024-03-13 13:38:04 -07001391 ElfW(Addr) load_bias, int prot_flags,
Kalesh Singhb23787f2024-09-05 08:22:06 +00001392 bool should_pad_segments,
1393 bool should_use_16kib_app_compat) {
Elliott Hughes0266ae52014-02-10 17:46:57 -08001394 const ElfW(Phdr)* phdr = phdr_table;
1395 const ElfW(Phdr)* phdr_limit = phdr + phdr_count;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001396
Elliott Hughes0266ae52014-02-10 17:46:57 -08001397 for (phdr = phdr_table; phdr < phdr_limit; phdr++) {
1398 if (phdr->p_type != PT_GNU_RELRO) {
1399 continue;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001400 }
Elliott Hughes0266ae52014-02-10 17:46:57 -08001401
1402 // Tricky: what happens when the relro segment does not start
1403 // or end at page boundaries? We're going to be over-protective
1404 // here and put every page touched by the segment as read-only.
1405
1406 // This seems to match Ian Lance Taylor's description of the
1407 // feature at http://www.airs.com/blog/archives/189.
1408
1409 // Extract:
1410 // Note that the current dynamic linker code will only work
1411 // correctly if the PT_GNU_RELRO segment starts on a page
1412 // boundary. This is because the dynamic linker rounds the
1413 // p_vaddr field down to the previous page boundary. If
1414 // there is anything on the page which should not be read-only,
1415 // the program is likely to fail at runtime. So in effect the
1416 // linker must only emit a PT_GNU_RELRO segment if it ensures
1417 // that it starts on a page boundary.
Zheng Pan9535c322024-02-14 00:04:10 +00001418 ElfW(Addr) seg_page_start = page_start(phdr->p_vaddr) + load_bias;
1419 ElfW(Addr) seg_page_end = page_end(phdr->p_vaddr + phdr->p_memsz) + load_bias;
Kalesh Singh702d9b02024-03-13 13:38:04 -07001420 _extend_gnu_relro_prot_end(phdr, phdr_table, phdr_count, load_bias, &seg_page_end,
Kalesh Singhb23787f2024-09-05 08:22:06 +00001421 should_pad_segments, should_use_16kib_app_compat);
Elliott Hughes0266ae52014-02-10 17:46:57 -08001422
Elliott Hughesfaf05ba2014-02-11 16:59:37 -08001423 int ret = mprotect(reinterpret_cast<void*>(seg_page_start),
Elliott Hughes0266ae52014-02-10 17:46:57 -08001424 seg_page_end - seg_page_start,
1425 prot_flags);
1426 if (ret < 0) {
1427 return -1;
1428 }
1429 }
1430 return 0;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001431}
1432
1433/* Apply GNU relro protection if specified by the program header. This will
1434 * turn some of the pages of a writable PT_LOAD segment to read-only, as
1435 * specified by one or more PT_GNU_RELRO segments. This must be always
1436 * performed after relocations.
1437 *
Ard Biesheuvel12c78bb2012-08-14 12:30:09 +02001438 * The areas typically covered are .got and .data.rel.ro, these are
1439 * read-only from the program's POV, but contain absolute addresses
1440 * that need to be relocated before use.
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001441 *
1442 * Input:
1443 * phdr_table -> program header table
Elliott Hughes105bc262012-08-15 16:56:00 -07001444 * phdr_count -> number of entries in tables
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001445 * load_bias -> load bias
Kalesh Singh702d9b02024-03-13 13:38:04 -07001446 * should_pad_segments -> Were segments extended to avoid gaps in the memory map
Kalesh Singhb23787f2024-09-05 08:22:06 +00001447 * should_use_16kib_app_compat -> Is the ELF being loaded in 16KiB app compat mode.
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001448 * Return:
Mitch Phillips117e45e2023-10-20 13:32:33 +00001449 * 0 on success, -1 on failure (error code in errno).
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001450 */
Kalesh Singh702d9b02024-03-13 13:38:04 -07001451int phdr_table_protect_gnu_relro(const ElfW(Phdr)* phdr_table, size_t phdr_count,
Kalesh Singhb23787f2024-09-05 08:22:06 +00001452 ElfW(Addr) load_bias, bool should_pad_segments,
1453 bool should_use_16kib_app_compat) {
Kalesh Singh702d9b02024-03-13 13:38:04 -07001454 return _phdr_table_set_gnu_relro_prot(phdr_table, phdr_count, load_bias, PROT_READ,
Kalesh Singhb23787f2024-09-05 08:22:06 +00001455 should_pad_segments, should_use_16kib_app_compat);
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001456}
1457
Kalesh Singhce1c3cf2024-09-30 13:26:23 -07001458/*
1459 * Apply RX protection to the compat relro region of the ELF being loaded in
1460 * 16KiB compat mode.
1461 *
1462 * Input:
1463 * start -> start address of the compat relro region.
1464 * size -> size of the compat relro region in bytes.
1465 * Return:
1466 * 0 on success, -1 on failure (error code in errno).
1467 */
1468int phdr_table_protect_gnu_relro_16kib_compat(ElfW(Addr) start, ElfW(Addr) size) {
1469 return mprotect(reinterpret_cast<void*>(start), size, PROT_READ | PROT_EXEC);
1470}
1471
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001472/* Serialize the GNU relro segments to the given file descriptor. This can be
1473 * performed after relocations to allow another process to later share the
1474 * relocated segment, if it was loaded at the same address.
1475 *
1476 * Input:
1477 * phdr_table -> program header table
1478 * phdr_count -> number of entries in tables
1479 * load_bias -> load bias
1480 * fd -> writable file descriptor to use
Torne (Richard Coles)fa9f7f22019-04-02 17:04:42 -04001481 * file_offset -> pointer to offset into file descriptor to use/update
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001482 * Return:
Mitch Phillips117e45e2023-10-20 13:32:33 +00001483 * 0 on success, -1 on failure (error code in errno).
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001484 */
Dmitriy Ivanov20d89cb2015-03-30 18:43:38 -07001485int phdr_table_serialize_gnu_relro(const ElfW(Phdr)* phdr_table,
1486 size_t phdr_count,
1487 ElfW(Addr) load_bias,
Torne (Richard Coles)fa9f7f22019-04-02 17:04:42 -04001488 int fd,
1489 size_t* file_offset) {
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001490 const ElfW(Phdr)* phdr = phdr_table;
1491 const ElfW(Phdr)* phdr_limit = phdr + phdr_count;
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001492
1493 for (phdr = phdr_table; phdr < phdr_limit; phdr++) {
1494 if (phdr->p_type != PT_GNU_RELRO) {
1495 continue;
1496 }
1497
Peter Collingbournebb11ee62022-05-02 12:26:16 -07001498 ElfW(Addr) seg_page_start = page_start(phdr->p_vaddr) + load_bias;
1499 ElfW(Addr) seg_page_end = page_end(phdr->p_vaddr + phdr->p_memsz) + load_bias;
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001500 ssize_t size = seg_page_end - seg_page_start;
1501
1502 ssize_t written = TEMP_FAILURE_RETRY(write(fd, reinterpret_cast<void*>(seg_page_start), size));
1503 if (written != size) {
1504 return -1;
1505 }
1506 void* map = mmap(reinterpret_cast<void*>(seg_page_start), size, PROT_READ,
Torne (Richard Coles)fa9f7f22019-04-02 17:04:42 -04001507 MAP_PRIVATE|MAP_FIXED, fd, *file_offset);
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001508 if (map == MAP_FAILED) {
1509 return -1;
1510 }
Torne (Richard Coles)fa9f7f22019-04-02 17:04:42 -04001511 *file_offset += size;
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001512 }
1513 return 0;
1514}
1515
1516/* Where possible, replace the GNU relro segments with mappings of the given
1517 * file descriptor. This can be performed after relocations to allow a file
1518 * previously created by phdr_table_serialize_gnu_relro in another process to
1519 * replace the dirty relocated pages, saving memory, if it was loaded at the
1520 * same address. We have to compare the data before we map over it, since some
1521 * parts of the relro segment may not be identical due to other libraries in
1522 * the process being loaded at different addresses.
1523 *
1524 * Input:
1525 * phdr_table -> program header table
1526 * phdr_count -> number of entries in tables
1527 * load_bias -> load bias
1528 * fd -> readable file descriptor to use
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -04001529 * file_offset -> pointer to offset into file descriptor to use/update
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001530 * Return:
Mitch Phillips117e45e2023-10-20 13:32:33 +00001531 * 0 on success, -1 on failure (error code in errno).
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001532 */
Dmitriy Ivanov20d89cb2015-03-30 18:43:38 -07001533int phdr_table_map_gnu_relro(const ElfW(Phdr)* phdr_table,
1534 size_t phdr_count,
1535 ElfW(Addr) load_bias,
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -04001536 int fd,
1537 size_t* file_offset) {
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001538 // Map the file at a temporary location so we can compare its contents.
1539 struct stat file_stat;
1540 if (TEMP_FAILURE_RETRY(fstat(fd, &file_stat)) != 0) {
1541 return -1;
1542 }
1543 off_t file_size = file_stat.st_size;
Dmitriy Ivanov851135b2014-08-29 12:02:36 -07001544 void* temp_mapping = nullptr;
Torne (Richard Coles)26ec9672014-04-30 15:48:40 +01001545 if (file_size > 0) {
Dmitriy Ivanov851135b2014-08-29 12:02:36 -07001546 temp_mapping = mmap(nullptr, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
Torne (Richard Coles)26ec9672014-04-30 15:48:40 +01001547 if (temp_mapping == MAP_FAILED) {
1548 return -1;
1549 }
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001550 }
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001551
1552 // Iterate over the relro segments and compare/remap the pages.
1553 const ElfW(Phdr)* phdr = phdr_table;
1554 const ElfW(Phdr)* phdr_limit = phdr + phdr_count;
1555
1556 for (phdr = phdr_table; phdr < phdr_limit; phdr++) {
1557 if (phdr->p_type != PT_GNU_RELRO) {
1558 continue;
1559 }
1560
Peter Collingbournebb11ee62022-05-02 12:26:16 -07001561 ElfW(Addr) seg_page_start = page_start(phdr->p_vaddr) + load_bias;
1562 ElfW(Addr) seg_page_end = page_end(phdr->p_vaddr + phdr->p_memsz) + load_bias;
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001563
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -04001564 char* file_base = static_cast<char*>(temp_mapping) + *file_offset;
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001565 char* mem_base = reinterpret_cast<char*>(seg_page_start);
1566 size_t match_offset = 0;
1567 size_t size = seg_page_end - seg_page_start;
1568
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -04001569 if (file_size - *file_offset < size) {
Torne (Richard Coles)26ec9672014-04-30 15:48:40 +01001570 // File is too short to compare to this segment. The contents are likely
1571 // different as well (it's probably for a different library version) so
1572 // just don't bother checking.
1573 break;
1574 }
1575
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001576 while (match_offset < size) {
1577 // Skip over dissimilar pages.
1578 while (match_offset < size &&
Peter Collingbournebb11ee62022-05-02 12:26:16 -07001579 memcmp(mem_base + match_offset, file_base + match_offset, page_size()) != 0) {
1580 match_offset += page_size();
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001581 }
1582
1583 // Count similar pages.
1584 size_t mismatch_offset = match_offset;
1585 while (mismatch_offset < size &&
Peter Collingbournebb11ee62022-05-02 12:26:16 -07001586 memcmp(mem_base + mismatch_offset, file_base + mismatch_offset, page_size()) == 0) {
1587 mismatch_offset += page_size();
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001588 }
1589
1590 // Map over similar pages.
1591 if (mismatch_offset > match_offset) {
1592 void* map = mmap(mem_base + match_offset, mismatch_offset - match_offset,
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -04001593 PROT_READ, MAP_PRIVATE|MAP_FIXED, fd, *file_offset + match_offset);
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001594 if (map == MAP_FAILED) {
1595 munmap(temp_mapping, file_size);
1596 return -1;
1597 }
1598 }
1599
1600 match_offset = mismatch_offset;
1601 }
1602
1603 // Add to the base file offset in case there are multiple relro segments.
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -04001604 *file_offset += size;
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001605 }
1606 munmap(temp_mapping, file_size);
1607 return 0;
1608}
1609
1610
Elliott Hughes4eeb1f12013-10-25 17:38:02 -07001611#if defined(__arm__)
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001612/* Return the address and size of the .ARM.exidx section in memory,
1613 * if present.
1614 *
1615 * Input:
1616 * phdr_table -> program header table
Elliott Hughes105bc262012-08-15 16:56:00 -07001617 * phdr_count -> number of entries in tables
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001618 * load_bias -> load bias
1619 * Output:
Dmitriy Ivanov851135b2014-08-29 12:02:36 -07001620 * arm_exidx -> address of table in memory (null on failure).
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001621 * arm_exidx_count -> number of items in table (0 on failure).
1622 * Return:
Mitch Phillips117e45e2023-10-20 13:32:33 +00001623 * 0 on success, -1 on failure (_no_ error code in errno)
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001624 */
Elliott Hughes0266ae52014-02-10 17:46:57 -08001625int phdr_table_get_arm_exidx(const ElfW(Phdr)* phdr_table, size_t phdr_count,
1626 ElfW(Addr) load_bias,
Dmitriy Ivanov1649e7e2015-01-22 16:04:25 -08001627 ElfW(Addr)** arm_exidx, size_t* arm_exidx_count) {
Elliott Hughes0266ae52014-02-10 17:46:57 -08001628 const ElfW(Phdr)* phdr = phdr_table;
1629 const ElfW(Phdr)* phdr_limit = phdr + phdr_count;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001630
Elliott Hughes0266ae52014-02-10 17:46:57 -08001631 for (phdr = phdr_table; phdr < phdr_limit; phdr++) {
1632 if (phdr->p_type != PT_ARM_EXIDX) {
1633 continue;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001634 }
Elliott Hughes0266ae52014-02-10 17:46:57 -08001635
1636 *arm_exidx = reinterpret_cast<ElfW(Addr)*>(load_bias + phdr->p_vaddr);
Dmitriy Ivanov1649e7e2015-01-22 16:04:25 -08001637 *arm_exidx_count = phdr->p_memsz / 8;
Elliott Hughes0266ae52014-02-10 17:46:57 -08001638 return 0;
1639 }
Dmitriy Ivanov851135b2014-08-29 12:02:36 -07001640 *arm_exidx = nullptr;
Elliott Hughes0266ae52014-02-10 17:46:57 -08001641 *arm_exidx_count = 0;
1642 return -1;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001643}
Elliott Hughes4eeb1f12013-10-25 17:38:02 -07001644#endif
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001645
Ard Biesheuvel12c78bb2012-08-14 12:30:09 +02001646/* Return the address and size of the ELF file's .dynamic section in memory,
Dmitriy Ivanov851135b2014-08-29 12:02:36 -07001647 * or null if missing.
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001648 *
1649 * Input:
1650 * phdr_table -> program header table
Elliott Hughes105bc262012-08-15 16:56:00 -07001651 * phdr_count -> number of entries in tables
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001652 * load_bias -> load bias
Ard Biesheuvel12c78bb2012-08-14 12:30:09 +02001653 * Output:
Dmitriy Ivanov851135b2014-08-29 12:02:36 -07001654 * dynamic -> address of table in memory (null on failure).
Ningsheng Jiane93be992014-09-16 15:22:10 +08001655 * dynamic_flags -> protection flags for section (unset on failure)
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001656 * Return:
Ard Biesheuvel12c78bb2012-08-14 12:30:09 +02001657 * void
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001658 */
Elliott Hughes0266ae52014-02-10 17:46:57 -08001659void phdr_table_get_dynamic_section(const ElfW(Phdr)* phdr_table, size_t phdr_count,
Ningsheng Jiane93be992014-09-16 15:22:10 +08001660 ElfW(Addr) load_bias, ElfW(Dyn)** dynamic,
1661 ElfW(Word)* dynamic_flags) {
Dmitriy Ivanov498eb182014-09-05 14:57:59 -07001662 *dynamic = nullptr;
Dmitriy Ivanov20d89cb2015-03-30 18:43:38 -07001663 for (size_t i = 0; i<phdr_count; ++i) {
1664 const ElfW(Phdr)& phdr = phdr_table[i];
1665 if (phdr.p_type == PT_DYNAMIC) {
1666 *dynamic = reinterpret_cast<ElfW(Dyn)*>(load_bias + phdr.p_vaddr);
Ningsheng Jiane93be992014-09-16 15:22:10 +08001667 if (dynamic_flags) {
Dmitriy Ivanov20d89cb2015-03-30 18:43:38 -07001668 *dynamic_flags = phdr.p_flags;
Ningsheng Jiane93be992014-09-16 15:22:10 +08001669 }
Dmitriy Ivanov14669a92014-09-05 16:42:53 -07001670 return;
1671 }
Elliott Hughes0266ae52014-02-10 17:46:57 -08001672 }
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001673}
1674
Evgenii Stepanovd640b222015-07-10 17:54:01 -07001675/* Return the program interpreter string, or nullptr if missing.
1676 *
1677 * Input:
1678 * phdr_table -> program header table
1679 * phdr_count -> number of entries in tables
1680 * load_bias -> load bias
1681 * Return:
1682 * pointer to the program interpreter string.
1683 */
Tamas Petz8d55d182020-02-24 14:15:25 +01001684const char* phdr_table_get_interpreter_name(const ElfW(Phdr)* phdr_table, size_t phdr_count,
Evgenii Stepanovd640b222015-07-10 17:54:01 -07001685 ElfW(Addr) load_bias) {
1686 for (size_t i = 0; i<phdr_count; ++i) {
1687 const ElfW(Phdr)& phdr = phdr_table[i];
1688 if (phdr.p_type == PT_INTERP) {
1689 return reinterpret_cast<const char*>(load_bias + phdr.p_vaddr);
1690 }
1691 }
1692 return nullptr;
1693}
1694
Robert Grosse4544d9f2014-10-15 14:32:19 -07001695// Sets loaded_phdr_ to the address of the program header table as it appears
1696// in the loaded segments in memory. This is in contrast with phdr_table_,
1697// which is temporary and will be released before the library is relocated.
Elliott Hughes650be4e2013-03-05 18:47:58 -08001698bool ElfReader::FindPhdr() {
Elliott Hughes0266ae52014-02-10 17:46:57 -08001699 const ElfW(Phdr)* phdr_limit = phdr_table_ + phdr_num_;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001700
Elliott Hughes650be4e2013-03-05 18:47:58 -08001701 // If there is a PT_PHDR, use it directly.
Elliott Hughes0266ae52014-02-10 17:46:57 -08001702 for (const ElfW(Phdr)* phdr = phdr_table_; phdr < phdr_limit; ++phdr) {
Elliott Hughes650be4e2013-03-05 18:47:58 -08001703 if (phdr->p_type == PT_PHDR) {
1704 return CheckPhdr(load_bias_ + phdr->p_vaddr);
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001705 }
Elliott Hughes650be4e2013-03-05 18:47:58 -08001706 }
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001707
Elliott Hughes650be4e2013-03-05 18:47:58 -08001708 // Otherwise, check the first loadable segment. If its file offset
1709 // is 0, it starts with the ELF header, and we can trivially find the
1710 // loaded program header from it.
Elliott Hughes0266ae52014-02-10 17:46:57 -08001711 for (const ElfW(Phdr)* phdr = phdr_table_; phdr < phdr_limit; ++phdr) {
Elliott Hughes650be4e2013-03-05 18:47:58 -08001712 if (phdr->p_type == PT_LOAD) {
1713 if (phdr->p_offset == 0) {
Elliott Hughes0266ae52014-02-10 17:46:57 -08001714 ElfW(Addr) elf_addr = load_bias_ + phdr->p_vaddr;
Elliott Hughesfaf05ba2014-02-11 16:59:37 -08001715 const ElfW(Ehdr)* ehdr = reinterpret_cast<const ElfW(Ehdr)*>(elf_addr);
Elliott Hughes0266ae52014-02-10 17:46:57 -08001716 ElfW(Addr) offset = ehdr->e_phoff;
Dmitriy Ivanov1649e7e2015-01-22 16:04:25 -08001717 return CheckPhdr(reinterpret_cast<ElfW(Addr)>(ehdr) + offset);
Elliott Hughes650be4e2013-03-05 18:47:58 -08001718 }
1719 break;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001720 }
Elliott Hughes650be4e2013-03-05 18:47:58 -08001721 }
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001722
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -07001723 DL_ERR("can't find loaded phdr for \"%s\"", name_.c_str());
Elliott Hughes650be4e2013-03-05 18:47:58 -08001724 return false;
1725}
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001726
Tamas Petz8d55d182020-02-24 14:15:25 +01001727// Tries to find .note.gnu.property section.
1728// It is not considered an error if such section is missing.
1729bool ElfReader::FindGnuPropertySection() {
1730#if defined(__aarch64__)
1731 note_gnu_property_ = GnuPropertySection(phdr_table_, phdr_num_, load_start(), name_.c_str());
1732#endif
1733 return true;
1734}
1735
Elliott Hughes650be4e2013-03-05 18:47:58 -08001736// Ensures that our program header is actually within a loadable
1737// segment. This should help catch badly-formed ELF files that
1738// would cause the linker to crash later when trying to access it.
Elliott Hughes0266ae52014-02-10 17:46:57 -08001739bool ElfReader::CheckPhdr(ElfW(Addr) loaded) {
1740 const ElfW(Phdr)* phdr_limit = phdr_table_ + phdr_num_;
1741 ElfW(Addr) loaded_end = loaded + (phdr_num_ * sizeof(ElfW(Phdr)));
Dmitriy Ivanovcf1cbbe2015-10-19 16:57:46 -07001742 for (const ElfW(Phdr)* phdr = phdr_table_; phdr < phdr_limit; ++phdr) {
Elliott Hughes650be4e2013-03-05 18:47:58 -08001743 if (phdr->p_type != PT_LOAD) {
1744 continue;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001745 }
Elliott Hughes0266ae52014-02-10 17:46:57 -08001746 ElfW(Addr) seg_start = phdr->p_vaddr + load_bias_;
1747 ElfW(Addr) seg_end = phdr->p_filesz + seg_start;
Elliott Hughes650be4e2013-03-05 18:47:58 -08001748 if (seg_start <= loaded && loaded_end <= seg_end) {
Elliott Hughes0266ae52014-02-10 17:46:57 -08001749 loaded_phdr_ = reinterpret_cast<const ElfW(Phdr)*>(loaded);
Elliott Hughes650be4e2013-03-05 18:47:58 -08001750 return true;
1751 }
1752 }
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -07001753 DL_ERR("\"%s\" loaded phdr %p not in loadable segment",
1754 name_.c_str(), reinterpret_cast<void*>(loaded));
Elliott Hughes650be4e2013-03-05 18:47:58 -08001755 return false;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001756}