blob: 048593b2540aa1aad7a77049292015ac90ec5f01 [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 Hughesfd49fe22025-06-05 07:36:44 -070034#include <sys/param.h>
Elliott Hughes99d54652018-08-22 10:36:23 -070035#include <sys/prctl.h>
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +000036#include <sys/types.h>
37#include <sys/stat.h>
38#include <unistd.h>
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +020039
Elliott Hughes650be4e2013-03-05 18:47:58 -080040#include "linker.h"
Florian Mayer4edc20d2024-10-30 14:24:26 -070041#include "linker_debug.h"
Elliott Hughes4cc5a602016-11-15 16:54:16 -080042#include "linker_dlwarning.h"
Dimitry Ivanov48ec2882016-08-04 11:50:36 -070043#include "linker_globals.h"
Florian Mayer4edc20d2024-10-30 14:24:26 -070044#include "linker_logger.h"
45#include "linker_main.h"
46#include "linker_soinfo.h"
Dmitriy Ivanov3c524812015-11-20 17:28:12 -080047#include "linker_utils.h"
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +020048
Kalesh Singhe065bd42025-07-21 16:34:39 -070049#include "private/CFIShadow.h" // For kLibraryAlignment
Kalesh Singh377f0b92024-01-31 20:23:39 -080050#include "private/bionic_asm_note.h"
Kalesh Singhe065bd42025-07-21 16:34:39 -070051#include "private/bionic_inline_raise.h"
Kalesh Singh377f0b92024-01-31 20:23:39 -080052#include "private/elf_note.h"
Dimitry Ivanov1b84afc2016-01-27 18:12:03 -080053
Kalesh Singhc5c1d192024-04-09 16:27:56 -070054#include <android-base/file.h>
Kalesh Singhe065bd42025-07-21 16:34:39 -070055#include <android-base/parsebool.h>
Kalesh Singhb23787f2024-09-05 08:22:06 +000056#include <android-base/properties.h>
Kalesh Singhe065bd42025-07-21 16:34:39 -070057#include <android-base/stringprintf.h>
58#include <android/set_abort_message.h>
Kalesh Singhc5c1d192024-04-09 16:27:56 -070059
Elliott Hughesb5140262014-12-02 16:16:29 -080060static int GetTargetElfMachine() {
61#if defined(__arm__)
62 return EM_ARM;
63#elif defined(__aarch64__)
64 return EM_AARCH64;
65#elif defined(__i386__)
66 return EM_386;
Elliott Hughes43462702022-10-10 19:21:44 +000067#elif defined(__riscv)
68 return EM_RISCV;
Elliott Hughesb5140262014-12-02 16:16:29 -080069#elif defined(__x86_64__)
70 return EM_X86_64;
71#endif
72}
73
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +020074/**
75 TECHNICAL NOTE ON ELF LOADING.
76
77 An ELF file's program header table contains one or more PT_LOAD
78 segments, which corresponds to portions of the file that need to
79 be mapped into the process' address space.
80
81 Each loadable segment has the following important properties:
82
83 p_offset -> segment file offset
84 p_filesz -> segment file size
85 p_memsz -> segment memory size (always >= p_filesz)
86 p_vaddr -> segment's virtual address
87 p_flags -> segment flags (e.g. readable, writable, executable)
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -070088 p_align -> segment's in-memory and in-file alignment
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +020089
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -070090 We will ignore the p_paddr field of ElfW(Phdr) for now.
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +020091
92 The loadable segments can be seen as a list of [p_vaddr ... p_vaddr+p_memsz)
93 ranges of virtual addresses. A few rules apply:
94
95 - the virtual address ranges should not overlap.
96
97 - if a segment's p_filesz is smaller than its p_memsz, the extra bytes
98 between them should always be initialized to 0.
99
100 - ranges do not necessarily start or end at page boundaries. Two distinct
101 segments can have their start and end on the same page. In this case, the
102 page inherits the mapping flags of the latter segment.
103
104 Finally, the real load addrs of each segment is not p_vaddr. Instead the
105 loader decides where to load the first segment, then will load all others
106 relative to the first one to respect the initial range layout.
107
108 For example, consider the following list:
109
110 [ offset:0, filesz:0x4000, memsz:0x4000, vaddr:0x30000 ],
111 [ offset:0x4000, filesz:0x2000, memsz:0x8000, vaddr:0x40000 ],
112
113 This corresponds to two segments that cover these virtual address ranges:
114
115 0x30000...0x34000
116 0x40000...0x48000
117
118 If the loader decides to load the first segment at address 0xa0000000
119 then the segments' load address ranges will be:
120
121 0xa0030000...0xa0034000
122 0xa0040000...0xa0048000
123
124 In other words, all segments must be loaded at an address that has the same
125 constant offset from their p_vaddr value. This offset is computed as the
126 difference between the first segment's load address, and its p_vaddr value.
127
128 However, in practice, segments do _not_ start at page boundaries. Since we
129 can only memory-map at page boundaries, this means that the bias is
130 computed as:
131
Peter Collingbournebb11ee62022-05-02 12:26:16 -0700132 load_bias = phdr0_load_address - page_start(phdr0->p_vaddr)
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200133
134 (NOTE: The value must be used as a 32-bit unsigned integer, to deal with
135 possible wrap around UINT32_MAX for possible large p_vaddr values).
136
137 And that the phdr0_load_address must start at a page boundary, with
138 the segment's real content starting at:
139
Peter Collingbournebb11ee62022-05-02 12:26:16 -0700140 phdr0_load_address + page_offset(phdr0->p_vaddr)
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200141
142 Note that ELF requires the following condition to make the mmap()-ing work:
143
Peter Collingbournebb11ee62022-05-02 12:26:16 -0700144 page_offset(phdr0->p_vaddr) == page_offset(phdr0->p_offset)
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200145
146 The load_bias must be added to any p_vaddr value read from the ELF file to
147 determine the corresponding memory address.
148
149 **/
150
Kalesh Singh1dd68582024-02-01 00:14:36 -0800151static const size_t kPageSize = page_size();
152
153/*
154 * Generic PMD size calculation:
155 * - Each page table (PT) is of size 1 page.
156 * - Each page table entry (PTE) is of size 64 bits.
157 * - Each PTE locates one physical page frame (PFN) of size 1 page.
158 * - A PMD entry locates 1 page table (PT)
159 *
160 * PMD size = Num entries in a PT * page_size
161 */
162static const size_t kPmdSize = (kPageSize / sizeof(uint64_t)) * kPageSize;
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700163
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700164ElfReader::ElfReader()
165 : did_read_(false), did_load_(false), fd_(-1), file_offset_(0), file_size_(0), phdr_num_(0),
166 phdr_table_(nullptr), shdr_table_(nullptr), shdr_num_(0), dynamic_(nullptr), strtab_(nullptr),
Elliott Hughesd9a97792024-12-10 20:32:44 +0000167 strtab_size_(0), load_start_(nullptr), load_size_(0), load_bias_(0), max_align_(0), min_align_(0),
168 loaded_phdr_(nullptr), mapped_by_caller_(false) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700169}
170
171bool ElfReader::Read(const char* name, int fd, off64_t file_offset, off64_t file_size) {
Jiyong Park02586a22017-05-20 01:01:24 +0900172 if (did_read_) {
173 return true;
174 }
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700175 name_ = name;
176 fd_ = fd;
177 file_offset_ = file_offset;
178 file_size_ = file_size;
179
180 if (ReadElfHeader() &&
181 VerifyElfHeader() &&
182 ReadProgramHeaders() &&
Elliott Hughesd9a97792024-12-10 20:32:44 +0000183 CheckProgramHeaderAlignment() &&
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700184 ReadSectionHeaders() &&
Kalesh Singh377f0b92024-01-31 20:23:39 -0800185 ReadDynamicSection() &&
186 ReadPadSegmentNote()) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700187 did_read_ = true;
188 }
189
Csanád Hajdú89032c42025-06-03 20:36:32 -0700190 if (kPageSize == 16 * 1024 && min_align_ < kPageSize) {
Kalesh Singhe065bd42025-07-21 16:34:39 -0700191 // This prop needs to be read on 16KiB devices for each ELF where min_align_ is less than
192 // 16KiB. It cannot be cached since the developer may toggle app compat on/off. This check will
193 // be removed once app compat is made the default on 16KiB devices.
194 auto compat_prop_val =
195 ::android::base::GetProperty("bionic.linker.16kb.app_compat.enabled", "false");
196
197 using ::android::base::ParseBool;
198 using ::android::base::ParseBoolResult;
199
Kalesh Singhb23787f2024-09-05 08:22:06 +0000200 should_use_16kib_app_compat_ =
Kalesh Singhe065bd42025-07-21 16:34:39 -0700201 ParseBool(compat_prop_val) == ParseBoolResult::kTrue || get_16kb_appcompat_mode();
202
203 if (compat_prop_val == "fatal") {
204 dlopen_16kib_err_is_fatal_ = true;
205 }
Kalesh Singhb23787f2024-09-05 08:22:06 +0000206 }
207
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700208 return did_read_;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200209}
210
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -0400211bool ElfReader::Load(address_space_params* address_space) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700212 CHECK(did_read_);
Jiyong Park02586a22017-05-20 01:01:24 +0900213 if (did_load_) {
214 return true;
215 }
huangchaochaobdc37962022-12-27 19:38:41 +0800216 bool reserveSuccess = ReserveAddressSpace(address_space);
217 if (reserveSuccess && LoadSegments() && FindPhdr() &&
Tamas Petz8d55d182020-02-24 14:15:25 +0100218 FindGnuPropertySection()) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700219 did_load_ = true;
Tamas Petz8d55d182020-02-24 14:15:25 +0100220#if defined(__aarch64__)
221 // For Armv8.5-A loaded executable segments may require PROT_BTI.
222 if (note_gnu_property_.IsBTICompatible()) {
Kalesh Singhb23787f2024-09-05 08:22:06 +0000223 did_load_ =
224 (phdr_table_protect_segments(phdr_table_, phdr_num_, load_bias_, should_pad_segments_,
225 should_use_16kib_app_compat_, &note_gnu_property_) == 0);
Tamas Petz8d55d182020-02-24 14:15:25 +0100226 }
227#endif
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700228 }
huangchaochaobdc37962022-12-27 19:38:41 +0800229 if (reserveSuccess && !did_load_) {
230 if (load_start_ != nullptr && load_size_ != 0) {
231 if (!mapped_by_caller_) {
232 munmap(load_start_, load_size_);
233 }
234 }
235 }
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700236
237 return did_load_;
238}
239
240const char* ElfReader::get_string(ElfW(Word) index) const {
241 CHECK(strtab_ != nullptr);
242 CHECK(index < strtab_size_);
243
244 return strtab_ + index;
Elliott Hughes650be4e2013-03-05 18:47:58 -0800245}
246
247bool ElfReader::ReadElfHeader() {
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000248 ssize_t rc = TEMP_FAILURE_RETRY(pread64(fd_, &header_, sizeof(header_), file_offset_));
249 if (rc < 0) {
250 DL_ERR("can't read file \"%s\": %s", name_.c_str(), strerror(errno));
251 return false;
252 }
253
254 if (rc != sizeof(header_)) {
Suren Baghdasaryanea5dd952024-07-19 17:12:16 -0700255 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 +0000256 static_cast<size_t>(rc));
Elliott Hughes650be4e2013-03-05 18:47:58 -0800257 return false;
258 }
Elliott Hughes650be4e2013-03-05 18:47:58 -0800259 return true;
260}
261
Elliott Hughes72007ee2017-04-19 17:44:57 -0700262static const char* EM_to_string(int em) {
263 if (em == EM_386) return "EM_386";
264 if (em == EM_AARCH64) return "EM_AARCH64";
265 if (em == EM_ARM) return "EM_ARM";
Ulya Trafimovichb973c752022-11-15 14:39:44 +0000266 if (em == EM_RISCV) return "EM_RISCV";
Elliott Hughes72007ee2017-04-19 17:44:57 -0700267 if (em == EM_X86_64) return "EM_X86_64";
268 return "EM_???";
269}
270
Elliott Hughes650be4e2013-03-05 18:47:58 -0800271bool ElfReader::VerifyElfHeader() {
Elliott Hughes625993d2014-07-15 16:53:13 -0700272 if (memcmp(header_.e_ident, ELFMAG, SELFMAG) != 0) {
Elliott Hughesa8971512018-06-27 14:39:06 -0700273 DL_ERR("\"%s\" has bad ELF magic: %02x%02x%02x%02x", name_.c_str(),
274 header_.e_ident[0], header_.e_ident[1], header_.e_ident[2], header_.e_ident[3]);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800275 return false;
276 }
277
Elliott Hughesc00f2cb2013-10-04 17:01:33 -0700278 // Try to give a clear diagnostic for ELF class mismatches, since they're
279 // an easy mistake to make during the 32-bit/64-bit transition period.
280 int elf_class = header_.e_ident[EI_CLASS];
281#if defined(__LP64__)
282 if (elf_class != ELFCLASS64) {
283 if (elf_class == ELFCLASS32) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700284 DL_ERR("\"%s\" is 32-bit instead of 64-bit", name_.c_str());
Elliott Hughesc00f2cb2013-10-04 17:01:33 -0700285 } else {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700286 DL_ERR("\"%s\" has unknown ELF class: %d", name_.c_str(), elf_class);
Elliott Hughesc00f2cb2013-10-04 17:01:33 -0700287 }
Elliott Hughes650be4e2013-03-05 18:47:58 -0800288 return false;
289 }
Elliott Hughesc00f2cb2013-10-04 17:01:33 -0700290#else
291 if (elf_class != ELFCLASS32) {
292 if (elf_class == ELFCLASS64) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700293 DL_ERR("\"%s\" is 64-bit instead of 32-bit", name_.c_str());
Elliott Hughesc00f2cb2013-10-04 17:01:33 -0700294 } else {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700295 DL_ERR("\"%s\" has unknown ELF class: %d", name_.c_str(), elf_class);
Elliott Hughesc00f2cb2013-10-04 17:01:33 -0700296 }
297 return false;
298 }
299#endif
300
Elliott Hughes650be4e2013-03-05 18:47:58 -0800301 if (header_.e_ident[EI_DATA] != ELFDATA2LSB) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700302 DL_ERR("\"%s\" not little-endian: %d", name_.c_str(), header_.e_ident[EI_DATA]);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800303 return false;
304 }
305
306 if (header_.e_type != ET_DYN) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700307 DL_ERR("\"%s\" has unexpected e_type: %d", name_.c_str(), header_.e_type);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800308 return false;
309 }
310
311 if (header_.e_version != EV_CURRENT) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700312 DL_ERR("\"%s\" has unexpected e_version: %d", name_.c_str(), header_.e_version);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800313 return false;
314 }
315
Elliott Hughesb5140262014-12-02 16:16:29 -0800316 if (header_.e_machine != GetTargetElfMachine()) {
Elliott Hughesd16cfac2018-09-17 15:50:09 -0700317 DL_ERR("\"%s\" is for %s (%d) instead of %s (%d)",
318 name_.c_str(),
319 EM_to_string(header_.e_machine), header_.e_machine,
320 EM_to_string(GetTargetElfMachine()), GetTargetElfMachine());
Elliott Hughes650be4e2013-03-05 18:47:58 -0800321 return false;
322 }
323
Dimitry Ivanovc73eec12016-07-22 12:50:59 -0700324 if (header_.e_shentsize != sizeof(ElfW(Shdr))) {
Elliott Hughesc14f5452025-01-30 13:33:04 -0800325 if (DL_ERROR_AFTER(26, "\"%s\" has unsupported e_shentsize: 0x%x (expected 0x%zx)",
326 name_.c_str(), header_.e_shentsize, sizeof(ElfW(Shdr)))) {
Dimitry Ivanov12b91872016-11-16 12:29:37 -0800327 return false;
328 }
Dimitry Ivanov12b91872016-11-16 12:29:37 -0800329 add_dlwarning(name_.c_str(), "has invalid ELF header");
Dimitry Ivanovc73eec12016-07-22 12:50:59 -0700330 }
331
332 if (header_.e_shstrndx == 0) {
Elliott Hughesc14f5452025-01-30 13:33:04 -0800333 if (DL_ERROR_AFTER(26, "\"%s\" has invalid e_shstrndx", name_.c_str())) {
Dimitry Ivanov12b91872016-11-16 12:29:37 -0800334 return false;
335 }
Dimitry Ivanov12b91872016-11-16 12:29:37 -0800336 add_dlwarning(name_.c_str(), "has invalid ELF header");
Dimitry Ivanovc73eec12016-07-22 12:50:59 -0700337 }
338
Elliott Hughes650be4e2013-03-05 18:47:58 -0800339 return true;
340}
341
Dimitry Ivanovbd906752016-08-08 17:12:18 -0700342bool ElfReader::CheckFileRange(ElfW(Addr) offset, size_t size, size_t alignment) {
Dmitriy Ivanov3c524812015-11-20 17:28:12 -0800343 off64_t range_start;
344 off64_t range_end;
345
Dimitry Ivanov0c9d30f2016-07-13 17:06:36 -0700346 // Only header can be located at the 0 offset... This function called to
347 // check DYNSYM and DYNAMIC sections and phdr/shdr - none of them can be
Dimitry Ivanovebe5af72016-07-14 11:15:44 -0700348 // at offset 0.
Dimitry Ivanov0c9d30f2016-07-13 17:06:36 -0700349
350 return offset > 0 &&
351 safe_add(&range_start, file_offset_, offset) &&
Dmitriy Ivanov3c524812015-11-20 17:28:12 -0800352 safe_add(&range_end, range_start, size) &&
Dimitry Ivanovbd906752016-08-08 17:12:18 -0700353 (range_start < file_size_) &&
354 (range_end <= file_size_) &&
355 ((offset % alignment) == 0);
Dmitriy Ivanov3c524812015-11-20 17:28:12 -0800356}
357
Elliott Hughes650be4e2013-03-05 18:47:58 -0800358// Loads the program header table from an ELF file into a read-only private
359// anonymous mmap-ed block.
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700360bool ElfReader::ReadProgramHeaders() {
Elliott Hughes650be4e2013-03-05 18:47:58 -0800361 phdr_num_ = header_.e_phnum;
362
363 // Like the kernel, we only accept program header tables that
364 // are smaller than 64KiB.
Elliott Hughes0266ae52014-02-10 17:46:57 -0800365 if (phdr_num_ < 1 || phdr_num_ > 65536/sizeof(ElfW(Phdr))) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700366 DL_ERR("\"%s\" has invalid e_phnum: %zd", name_.c_str(), phdr_num_);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800367 return false;
368 }
369
Dmitriy Ivanov3c524812015-11-20 17:28:12 -0800370 // Boundary checks
371 size_t size = phdr_num_ * sizeof(ElfW(Phdr));
Dimitry Ivanovbd906752016-08-08 17:12:18 -0700372 if (!CheckFileRange(header_.e_phoff, size, alignof(ElfW(Phdr)))) {
373 DL_ERR_AND_LOG("\"%s\" has invalid phdr offset/size: %zu/%zu",
374 name_.c_str(),
375 static_cast<size_t>(header_.e_phoff),
376 size);
Dmitriy Ivanov3c524812015-11-20 17:28:12 -0800377 return false;
378 }
379
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000380 if (!phdr_fragment_.Map(fd_, file_offset_, header_.e_phoff, size)) {
Elliott Hughesf5e21d92024-07-26 11:48:19 +0000381 DL_ERR("\"%s\" phdr mmap failed: %m", name_.c_str());
Elliott Hughes650be4e2013-03-05 18:47:58 -0800382 return false;
383 }
384
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000385 phdr_table_ = static_cast<ElfW(Phdr)*>(phdr_fragment_.data());
Elliott Hughes650be4e2013-03-05 18:47:58 -0800386 return true;
387}
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200388
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700389bool ElfReader::ReadSectionHeaders() {
390 shdr_num_ = header_.e_shnum;
391
Dmitriy Ivanovb76123f2015-11-20 10:42:02 -0800392 if (shdr_num_ == 0) {
Dimitry Ivanovbd906752016-08-08 17:12:18 -0700393 DL_ERR_AND_LOG("\"%s\" has no section headers", name_.c_str());
Dmitriy Ivanovb76123f2015-11-20 10:42:02 -0800394 return false;
395 }
396
Dmitriy Ivanov3c524812015-11-20 17:28:12 -0800397 size_t size = shdr_num_ * sizeof(ElfW(Shdr));
Dimitry Ivanovbd906752016-08-08 17:12:18 -0700398 if (!CheckFileRange(header_.e_shoff, size, alignof(const ElfW(Shdr)))) {
399 DL_ERR_AND_LOG("\"%s\" has invalid shdr offset/size: %zu/%zu",
400 name_.c_str(),
401 static_cast<size_t>(header_.e_shoff),
402 size);
Dmitriy Ivanov3c524812015-11-20 17:28:12 -0800403 return false;
404 }
405
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000406 if (!shdr_fragment_.Map(fd_, file_offset_, header_.e_shoff, size)) {
Elliott Hughesf5e21d92024-07-26 11:48:19 +0000407 DL_ERR("\"%s\" shdr mmap failed: %m", name_.c_str());
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700408 return false;
409 }
410
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000411 shdr_table_ = static_cast<const ElfW(Shdr)*>(shdr_fragment_.data());
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700412 return true;
413}
414
415bool ElfReader::ReadDynamicSection() {
416 // 1. Find .dynamic section (in section headers)
417 const ElfW(Shdr)* dynamic_shdr = nullptr;
418 for (size_t i = 0; i < shdr_num_; ++i) {
419 if (shdr_table_[i].sh_type == SHT_DYNAMIC) {
420 dynamic_shdr = &shdr_table_ [i];
421 break;
422 }
423 }
424
425 if (dynamic_shdr == nullptr) {
Dimitry Ivanovbd906752016-08-08 17:12:18 -0700426 DL_ERR_AND_LOG("\"%s\" .dynamic section header was not found", name_.c_str());
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700427 return false;
428 }
429
Dimitry Ivanov0c9d30f2016-07-13 17:06:36 -0700430 // Make sure dynamic_shdr offset and size matches PT_DYNAMIC phdr
431 size_t pt_dynamic_offset = 0;
432 size_t pt_dynamic_filesz = 0;
433 for (size_t i = 0; i < phdr_num_; ++i) {
434 const ElfW(Phdr)* phdr = &phdr_table_[i];
435 if (phdr->p_type == PT_DYNAMIC) {
436 pt_dynamic_offset = phdr->p_offset;
437 pt_dynamic_filesz = phdr->p_filesz;
438 }
439 }
440
441 if (pt_dynamic_offset != dynamic_shdr->sh_offset) {
Elliott Hughesc14f5452025-01-30 13:33:04 -0800442 if (DL_ERROR_AFTER(26, "\"%s\" .dynamic section has invalid offset: 0x%zx, "
443 "expected to match PT_DYNAMIC offset: 0x%zx",
444 name_.c_str(),
445 static_cast<size_t>(dynamic_shdr->sh_offset),
446 pt_dynamic_offset)) {
Dimitry Ivanove30c17f2016-12-28 16:21:49 -0800447 return false;
448 }
Dimitry Ivanove30c17f2016-12-28 16:21:49 -0800449 add_dlwarning(name_.c_str(), "invalid .dynamic section");
Dimitry Ivanov0c9d30f2016-07-13 17:06:36 -0700450 }
451
452 if (pt_dynamic_filesz != dynamic_shdr->sh_size) {
Elliott Hughesc14f5452025-01-30 13:33:04 -0800453 if (DL_ERROR_AFTER(26, "\"%s\" .dynamic section has invalid size: 0x%zx "
454 "(expected to match PT_DYNAMIC filesz 0x%zx)",
455 name_.c_str(),
456 static_cast<size_t>(dynamic_shdr->sh_size),
457 pt_dynamic_filesz)) {
Dimitry Ivanove30c17f2016-12-28 16:21:49 -0800458 return false;
459 }
Dimitry Ivanove30c17f2016-12-28 16:21:49 -0800460 add_dlwarning(name_.c_str(), "invalid .dynamic section");
Dimitry Ivanov0c9d30f2016-07-13 17:06:36 -0700461 }
462
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700463 if (dynamic_shdr->sh_link >= shdr_num_) {
Dimitry Ivanovbd906752016-08-08 17:12:18 -0700464 DL_ERR_AND_LOG("\"%s\" .dynamic section has invalid sh_link: %d",
465 name_.c_str(),
466 dynamic_shdr->sh_link);
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700467 return false;
468 }
469
470 const ElfW(Shdr)* strtab_shdr = &shdr_table_[dynamic_shdr->sh_link];
471
472 if (strtab_shdr->sh_type != SHT_STRTAB) {
Dimitry Ivanovbd906752016-08-08 17:12:18 -0700473 DL_ERR_AND_LOG("\"%s\" .dynamic section has invalid link(%d) sh_type: %d (expected SHT_STRTAB)",
474 name_.c_str(), dynamic_shdr->sh_link, strtab_shdr->sh_type);
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700475 return false;
476 }
477
Dimitry Ivanovbd906752016-08-08 17:12:18 -0700478 if (!CheckFileRange(dynamic_shdr->sh_offset, dynamic_shdr->sh_size, alignof(const ElfW(Dyn)))) {
479 DL_ERR_AND_LOG("\"%s\" has invalid offset/size of .dynamic section", name_.c_str());
Dmitriy Ivanov3c524812015-11-20 17:28:12 -0800480 return false;
481 }
482
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000483 if (!dynamic_fragment_.Map(fd_, file_offset_, dynamic_shdr->sh_offset, dynamic_shdr->sh_size)) {
Elliott Hughesf5e21d92024-07-26 11:48:19 +0000484 DL_ERR("\"%s\" dynamic section mmap failed: %m", name_.c_str());
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700485 return false;
486 }
487
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000488 dynamic_ = static_cast<const ElfW(Dyn)*>(dynamic_fragment_.data());
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700489
Dimitry Ivanovbd906752016-08-08 17:12:18 -0700490 if (!CheckFileRange(strtab_shdr->sh_offset, strtab_shdr->sh_size, alignof(const char))) {
491 DL_ERR_AND_LOG("\"%s\" has invalid offset/size of the .strtab section linked from .dynamic section",
492 name_.c_str());
Dmitriy Ivanov3c524812015-11-20 17:28:12 -0800493 return false;
494 }
495
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000496 if (!strtab_fragment_.Map(fd_, file_offset_, strtab_shdr->sh_offset, strtab_shdr->sh_size)) {
Elliott Hughesf5e21d92024-07-26 11:48:19 +0000497 DL_ERR("\"%s\" strtab section mmap failed: %m", name_.c_str());
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700498 return false;
499 }
500
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000501 strtab_ = static_cast<const char*>(strtab_fragment_.data());
502 strtab_size_ = strtab_fragment_.size();
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700503 return true;
504}
505
Brian Carlstrome7dffe12013-01-10 16:39:58 -0800506/* Returns the size of the extent of all the possibly non-contiguous
507 * loadable segments in an ELF program header table. This corresponds
508 * to the page-aligned size in bytes that needs to be reserved in the
509 * process' address space. If there are no loadable segments, 0 is
510 * returned.
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200511 *
Dmitriy Ivanov851135b2014-08-29 12:02:36 -0700512 * If out_min_vaddr or out_max_vaddr are not null, they will be
Brian Carlstrome7dffe12013-01-10 16:39:58 -0800513 * set to the minimum and maximum addresses of pages to be reserved,
514 * or 0 if there is nothing to load.
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200515 */
Elliott Hughes0266ae52014-02-10 17:46:57 -0800516size_t phdr_table_get_load_size(const ElfW(Phdr)* phdr_table, size_t phdr_count,
517 ElfW(Addr)* out_min_vaddr,
518 ElfW(Addr)* out_max_vaddr) {
519 ElfW(Addr) min_vaddr = UINTPTR_MAX;
520 ElfW(Addr) max_vaddr = 0;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200521
Elliott Hughes0266ae52014-02-10 17:46:57 -0800522 bool found_pt_load = false;
523 for (size_t i = 0; i < phdr_count; ++i) {
524 const ElfW(Phdr)* phdr = &phdr_table[i];
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200525
Elliott Hughes0266ae52014-02-10 17:46:57 -0800526 if (phdr->p_type != PT_LOAD) {
527 continue;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200528 }
Elliott Hughes0266ae52014-02-10 17:46:57 -0800529 found_pt_load = true;
530
531 if (phdr->p_vaddr < min_vaddr) {
532 min_vaddr = phdr->p_vaddr;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200533 }
534
Elliott Hughes0266ae52014-02-10 17:46:57 -0800535 if (phdr->p_vaddr + phdr->p_memsz > max_vaddr) {
536 max_vaddr = phdr->p_vaddr + phdr->p_memsz;
537 }
538 }
539 if (!found_pt_load) {
540 min_vaddr = 0;
541 }
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200542
Peter Collingbournebb11ee62022-05-02 12:26:16 -0700543 min_vaddr = page_start(min_vaddr);
544 max_vaddr = page_end(max_vaddr);
Elliott Hughes0266ae52014-02-10 17:46:57 -0800545
Dmitriy Ivanov851135b2014-08-29 12:02:36 -0700546 if (out_min_vaddr != nullptr) {
Elliott Hughes0266ae52014-02-10 17:46:57 -0800547 *out_min_vaddr = min_vaddr;
548 }
Dmitriy Ivanov851135b2014-08-29 12:02:36 -0700549 if (out_max_vaddr != nullptr) {
Elliott Hughes0266ae52014-02-10 17:46:57 -0800550 *out_max_vaddr = max_vaddr;
551 }
552 return max_vaddr - min_vaddr;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200553}
554
Elliott Hughesd9a97792024-12-10 20:32:44 +0000555bool ElfReader::CheckProgramHeaderAlignment() {
556 max_align_ = min_align_ = page_size();
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700557
Elliott Hughesd9a97792024-12-10 20:32:44 +0000558 for (size_t i = 0; i < phdr_num_; ++i) {
559 const ElfW(Phdr)* phdr = &phdr_table_[i];
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700560
Elliott Hughesc039e492025-01-28 13:56:25 -0800561 if (phdr->p_type != PT_LOAD) {
562 continue;
563 }
564
565 // For loadable segments, p_align must be 0, 1,
566 // or a positive, integral power of two.
567 // The kernel ignores loadable segments with other values,
568 // so we just warn rather than reject them.
Elliott Hughesfd49fe22025-06-05 07:36:44 -0700569 if (!powerof2(phdr->p_align)) {
Elliott Hughesc039e492025-01-28 13:56:25 -0800570 DL_WARN("\"%s\" has invalid p_align %zx in phdr %zu", name_.c_str(),
571 static_cast<size_t>(phdr->p_align), i);
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700572 continue;
573 }
574
Elliott Hughesd9a97792024-12-10 20:32:44 +0000575 max_align_ = std::max(max_align_, static_cast<size_t>(phdr->p_align));
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700576
Elliott Hughesd9a97792024-12-10 20:32:44 +0000577 if (phdr->p_align > 1) {
578 min_align_ = std::min(min_align_, static_cast<size_t>(phdr->p_align));
Steven Morelandfc89c8a2024-08-01 21:20:33 +0000579 }
Steven Morelandfc89c8a2024-08-01 21:20:33 +0000580 }
581
Csanád Hajdú89032c42025-06-03 20:36:32 -0700582 if (kPageSize == 16 * 1024) FixMinAlignFor16KiB();
583
Elliott Hughesd9a97792024-12-10 20:32:44 +0000584 return true;
Steven Morelandfc89c8a2024-08-01 21:20:33 +0000585}
586
Evgenii Stepanovd13e9a62016-07-15 16:31:42 -0700587// Reserve a virtual address range such that if it's limits were extended to the next 2**align
588// boundary, it would not overlap with any existing mappings.
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700589static void* ReserveWithAlignmentPadding(size_t size, size_t mapping_align, size_t start_align,
590 void** out_gap_start, size_t* out_gap_size) {
Evgenii Stepanovd13e9a62016-07-15 16:31:42 -0700591 int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700592 // Reserve enough space to properly align the library's start address.
593 mapping_align = std::max(mapping_align, start_align);
Peter Collingbournebb11ee62022-05-02 12:26:16 -0700594 if (mapping_align == page_size()) {
Elliott Hughes8178c412018-11-05 13:34:36 -0800595 void* mmap_ptr = mmap(nullptr, size, PROT_NONE, mmap_flags, -1, 0);
Evgenii Stepanovd13e9a62016-07-15 16:31:42 -0700596 if (mmap_ptr == MAP_FAILED) {
597 return nullptr;
598 }
599 return mmap_ptr;
600 }
601
Yi Kongc1eb27b2025-08-22 15:09:24 +0900602#if defined(__LP64__)
Evgenii Stepanove0848bb2020-07-14 16:44:57 -0700603 // Minimum alignment of shared library gap. For efficiency, this should match the second level
604 // page size of the platform.
Elliott Hughesc05035e2024-12-10 20:31:58 +0000605 constexpr size_t kGapAlignment = 2 * 1024 * 1024;
Evgenii Stepanove0848bb2020-07-14 16:44:57 -0700606 // Maximum gap size, in the units of kGapAlignment.
607 constexpr size_t kMaxGapUnits = 32;
Yi Kongc1eb27b2025-08-22 15:09:24 +0900608#endif
Evgenii Stepanovd13e9a62016-07-15 16:31:42 -0700609 // Allocate enough space so that the end of the desired region aligned up is still inside the
610 // mapping.
Elliott Hughesfed0ce92024-12-11 09:53:34 -0800611 size_t mmap_size = __builtin_align_up(size, mapping_align) + mapping_align - page_size();
Evgenii Stepanovd13e9a62016-07-15 16:31:42 -0700612 uint8_t* mmap_ptr =
613 reinterpret_cast<uint8_t*>(mmap(nullptr, mmap_size, PROT_NONE, mmap_flags, -1, 0));
614 if (mmap_ptr == MAP_FAILED) {
615 return nullptr;
616 }
Evgenii Stepanove0848bb2020-07-14 16:44:57 -0700617 size_t gap_size = 0;
Yi Kong6619f382025-08-24 03:48:59 +0900618#if defined(__LP64__)
Elliott Hughesfed0ce92024-12-11 09:53:34 -0800619 size_t first_byte = reinterpret_cast<size_t>(__builtin_align_up(mmap_ptr, mapping_align));
620 size_t last_byte = reinterpret_cast<size_t>(__builtin_align_down(mmap_ptr + mmap_size, mapping_align) - 1);
Elliott Hughesfed0ce92024-12-11 09:53:34 -0800621 if (first_byte / kGapAlignment != last_byte / kGapAlignment) {
Evgenii Stepanove0848bb2020-07-14 16:44:57 -0700622 // This library crosses a 2MB boundary and will fragment a new huge page.
623 // Lets take advantage of that and insert a random number of inaccessible huge pages before that
624 // to improve address randomization and make it harder to locate this library code by probing.
625 munmap(mmap_ptr, mmap_size);
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700626 mapping_align = std::max(mapping_align, kGapAlignment);
Evgenii Stepanove0848bb2020-07-14 16:44:57 -0700627 gap_size =
628 kGapAlignment * (is_first_stage_init() ? 1 : arc4random_uniform(kMaxGapUnits - 1) + 1);
Elliott Hughesfed0ce92024-12-11 09:53:34 -0800629 mmap_size = __builtin_align_up(size + gap_size, mapping_align) + mapping_align - page_size();
Evgenii Stepanove0848bb2020-07-14 16:44:57 -0700630 mmap_ptr = reinterpret_cast<uint8_t*>(mmap(nullptr, mmap_size, PROT_NONE, mmap_flags, -1, 0));
631 if (mmap_ptr == MAP_FAILED) {
632 return nullptr;
633 }
634 }
Elliott Hughesfed0ce92024-12-11 09:53:34 -0800635#endif
Evgenii Stepanove0848bb2020-07-14 16:44:57 -0700636
Elliott Hughesfed0ce92024-12-11 09:53:34 -0800637 uint8_t* gap_end = mmap_ptr + mmap_size;
638#if defined(__LP64__)
Evgenii Stepanove0848bb2020-07-14 16:44:57 -0700639 if (gap_size) {
Elliott Hughesfed0ce92024-12-11 09:53:34 -0800640 gap_end = __builtin_align_down(gap_end, kGapAlignment);
Evgenii Stepanove0848bb2020-07-14 16:44:57 -0700641 }
Elliott Hughesfed0ce92024-12-11 09:53:34 -0800642#endif
643 uint8_t* gap_start = gap_end - gap_size;
Evgenii Stepanovd13e9a62016-07-15 16:31:42 -0700644
Elliott Hughesfed0ce92024-12-11 09:53:34 -0800645 uint8_t* first = __builtin_align_up(mmap_ptr, mapping_align);
646 uint8_t* last = __builtin_align_down(gap_start, mapping_align) - size;
Jiyong Park31cd08f2018-06-01 19:18:56 +0900647
Tom Cherry66bc4282018-11-08 13:40:52 -0800648 // arc4random* is not available in first stage init because /dev/urandom hasn't yet been
Jiyong Park31cd08f2018-06-01 19:18:56 +0900649 // created. Don't randomize then.
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700650 size_t n = is_first_stage_init() ? 0 : arc4random_uniform((last - first) / start_align + 1);
651 uint8_t* start = first + n * start_align;
Evgenii Stepanove0848bb2020-07-14 16:44:57 -0700652 // Unmap the extra space around the allocation.
653 // Keep it mapped PROT_NONE on 64-bit targets where address space is plentiful to make it harder
654 // to defeat ASLR by probing for readable memory mappings.
Evgenii Stepanovd13e9a62016-07-15 16:31:42 -0700655 munmap(mmap_ptr, start - mmap_ptr);
Evgenii Stepanove0848bb2020-07-14 16:44:57 -0700656 munmap(start + size, gap_start - (start + size));
657 if (gap_end != mmap_ptr + mmap_size) {
658 munmap(gap_end, mmap_ptr + mmap_size - gap_end);
659 }
660 *out_gap_start = gap_start;
661 *out_gap_size = gap_size;
Evgenii Stepanovd13e9a62016-07-15 16:31:42 -0700662 return start;
663}
664
Elliott Hughes650be4e2013-03-05 18:47:58 -0800665// Reserve a virtual address range big enough to hold all loadable
666// segments of a program header table. This is done by creating a
667// private anonymous mmap() with PROT_NONE.
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -0400668bool ElfReader::ReserveAddressSpace(address_space_params* address_space) {
Elliott Hughes0266ae52014-02-10 17:46:57 -0800669 ElfW(Addr) min_vaddr;
Brian Carlstrome7dffe12013-01-10 16:39:58 -0800670 load_size_ = phdr_table_get_load_size(phdr_table_, phdr_num_, &min_vaddr);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800671 if (load_size_ == 0) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700672 DL_ERR("\"%s\" has no loadable segments", name_.c_str());
Elliott Hughes650be4e2013-03-05 18:47:58 -0800673 return false;
674 }
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200675
Kalesh Singhce1c3cf2024-09-30 13:26:23 -0700676 if (should_use_16kib_app_compat_) {
677 // Reserve additional space for aligning the permission boundary in compat loading
678 // Up to kPageSize-kCompatPageSize additional space is needed, but reservation
679 // is done with mmap which gives kPageSize multiple-sized reservations.
680 load_size_ += kPageSize;
681 }
682
Brian Carlstrome7dffe12013-01-10 16:39:58 -0800683 uint8_t* addr = reinterpret_cast<uint8_t*>(min_vaddr);
Torne (Richard Coles)12bbb912014-02-06 14:34:21 +0000684 void* start;
Torne (Richard Coles)12bbb912014-02-06 14:34:21 +0000685
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -0400686 if (load_size_ > address_space->reserved_size) {
687 if (address_space->must_use_address) {
Torne (Richard Coles)12bbb912014-02-06 14:34:21 +0000688 DL_ERR("reserved address space %zd smaller than %zd bytes needed for \"%s\"",
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -0400689 load_size_ - address_space->reserved_size, load_size_, name_.c_str());
Torne (Richard Coles)12bbb912014-02-06 14:34:21 +0000690 return false;
691 }
Peter Collingbournebb11ee62022-05-02 12:26:16 -0700692 size_t start_alignment = page_size();
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700693 if (get_transparent_hugepages_supported() && get_application_target_sdk_version() >= 31) {
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700694 // Limit alignment to PMD size as other alignments reduce the number of
695 // bits available for ASLR for no benefit.
Elliott Hughesd9a97792024-12-10 20:32:44 +0000696 start_alignment = max_align_ == kPmdSize ? kPmdSize : page_size();
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700697 }
698 start = ReserveWithAlignmentPadding(load_size_, kLibraryAlignment, start_alignment, &gap_start_,
699 &gap_size_);
Evgenii Stepanovd13e9a62016-07-15 16:31:42 -0700700 if (start == nullptr) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700701 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 +0000702 return false;
703 }
704 } else {
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -0400705 start = address_space->start_addr;
Evgenii Stepanove0848bb2020-07-14 16:44:57 -0700706 gap_start_ = nullptr;
707 gap_size_ = 0;
Dimitry Ivanovf45b0e92016-01-15 11:13:35 -0800708 mapped_by_caller_ = true;
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -0400709
710 // Update the reserved address space to subtract the space used by this library.
711 address_space->start_addr = reinterpret_cast<uint8_t*>(address_space->start_addr) + load_size_;
712 address_space->reserved_size -= load_size_;
Elliott Hughes650be4e2013-03-05 18:47:58 -0800713 }
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200714
Elliott Hughes650be4e2013-03-05 18:47:58 -0800715 load_start_ = start;
Brian Carlstrome7dffe12013-01-10 16:39:58 -0800716 load_bias_ = reinterpret_cast<uint8_t*>(start) - addr;
Kalesh Singhce1c3cf2024-09-30 13:26:23 -0700717
718 if (should_use_16kib_app_compat_) {
719 // In compat mode make the initial mapping RW since the ELF contents will be read
720 // into it; instead of mapped over it.
721 mprotect(reinterpret_cast<void*>(start), load_size_, PROT_READ | PROT_WRITE);
722 }
723
Elliott Hughes650be4e2013-03-05 18:47:58 -0800724 return true;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200725}
726
Kalesh Singhc5c1d192024-04-09 16:27:56 -0700727/*
Elliott Hughes25498992024-12-03 14:50:44 -0500728 * Returns true if the kernel supports page size migration for this process.
Kalesh Singhc5c1d192024-04-09 16:27:56 -0700729 */
730bool page_size_migration_supported() {
Elliott Hughes25498992024-12-03 14:50:44 -0500731#if defined(__LP64__)
Kalesh Singhc5c1d192024-04-09 16:27:56 -0700732 static bool pgsize_migration_enabled = []() {
733 std::string enabled;
734 if (!android::base::ReadFileToString("/sys/kernel/mm/pgsize_migration/enabled", &enabled)) {
735 return false;
736 }
737 return enabled.find("1") != std::string::npos;
738 }();
739 return pgsize_migration_enabled;
Elliott Hughes25498992024-12-03 14:50:44 -0500740#else
741 return false;
742#endif
Kalesh Singhc5c1d192024-04-09 16:27:56 -0700743}
744
Kalesh Singh377f0b92024-01-31 20:23:39 -0800745// Find the ELF note of type NT_ANDROID_TYPE_PAD_SEGMENT and check that the desc value is 1.
746bool ElfReader::ReadPadSegmentNote() {
Kalesh Singhc5c1d192024-04-09 16:27:56 -0700747 if (!page_size_migration_supported()) {
748 // Don't attempt to read the note, since segment extension isn't
749 // supported; but return true so that loading can continue normally.
750 return true;
751 }
752
Kalesh Singh377f0b92024-01-31 20:23:39 -0800753 // The ELF can have multiple PT_NOTE's, check them all
754 for (size_t i = 0; i < phdr_num_; ++i) {
755 const ElfW(Phdr)* phdr = &phdr_table_[i];
756
757 if (phdr->p_type != PT_NOTE) {
758 continue;
759 }
760
Kalesh Singh13fb3cf2024-02-08 14:58:04 -0800761 // Some obfuscated ELFs may contain "empty" PT_NOTE program headers that don't
762 // point to any part of the ELF (p_memsz == 0). Skip these since there is
763 // nothing to decode. See: b/324468126
764 if (phdr->p_memsz == 0) {
765 continue;
766 }
767
Elliott Hughes93d81942025-01-13 07:56:11 -0800768 // Reject notes that claim to extend past the end of the file.
769 off64_t note_end_off = file_offset_;
770 if (__builtin_add_overflow(note_end_off, phdr->p_offset, &note_end_off) ||
771 __builtin_add_overflow(note_end_off, phdr->p_filesz, &note_end_off) ||
772 phdr->p_filesz != phdr->p_memsz ||
773 note_end_off > file_size_) {
Elliott Hughesec79de02025-01-17 07:20:37 -0800774
775 if (get_application_target_sdk_version() < 37) {
776 // Some in-market apps have invalid ELF notes (http://b/390328213),
777 // so ignore them until/unless they bump their target sdk version.
778 continue;
779 }
780
Elliott Hughes21869a52025-01-21 08:44:52 -0800781 DL_ERR_AND_LOG("\"%s\": ELF note (phdr %zu) runs off end of file", name_.c_str(), i);
Elliott Hughes93d81942025-01-13 07:56:11 -0800782 return false;
Kalesh Singh751bb8a2024-03-29 17:55:37 -0700783 }
784
Elliott Hughes93d81942025-01-13 07:56:11 -0800785 // We scope note_fragment to within the loop so that there is
786 // at most one PT_NOTE mapped at any time.
Kalesh Singh377f0b92024-01-31 20:23:39 -0800787 MappedFileFragment note_fragment;
Elliott Hughes93d81942025-01-13 07:56:11 -0800788 if (!note_fragment.Map(fd_, file_offset_, phdr->p_offset, phdr->p_filesz)) {
Kalesh Singh32b6d8c2024-02-13 18:37:12 -0800789 DL_ERR("\"%s\": PT_NOTE mmap(nullptr, %p, PROT_READ, MAP_PRIVATE, %d, %p) failed: %m",
Elliott Hughes93d81942025-01-13 07:56:11 -0800790 name_.c_str(), reinterpret_cast<void*>(phdr->p_filesz), fd_,
Kalesh Singh32b6d8c2024-02-13 18:37:12 -0800791 reinterpret_cast<void*>(page_start(file_offset_ + phdr->p_offset)));
Kalesh Singh13fb3cf2024-02-08 14:58:04 -0800792 return false;
Kalesh Singh377f0b92024-01-31 20:23:39 -0800793 }
794
795 const ElfW(Nhdr)* note_hdr = nullptr;
796 const char* note_desc = nullptr;
797 if (!__get_elf_note(NT_ANDROID_TYPE_PAD_SEGMENT, "Android",
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000798 reinterpret_cast<ElfW(Addr)>(note_fragment.data()),
Kalesh Singh377f0b92024-01-31 20:23:39 -0800799 phdr, &note_hdr, &note_desc)) {
800 continue;
801 }
802
803 if (note_hdr->n_descsz != sizeof(ElfW(Word))) {
Elliott Hughes93d81942025-01-13 07:56:11 -0800804 DL_ERR("\"%s\": NT_ANDROID_TYPE_PAD_SEGMENT note has unexpected n_descsz: %u",
Kalesh Singh377f0b92024-01-31 20:23:39 -0800805 name_.c_str(), reinterpret_cast<unsigned int>(note_hdr->n_descsz));
806 return false;
807 }
808
809 // 1 == enabled, 0 == disabled
810 should_pad_segments_ = *reinterpret_cast<const ElfW(Word)*>(note_desc) == 1;
811 return true;
812 }
813
814 return true;
815}
816
Kalesh Singh4084b552024-03-13 13:35:49 -0700817static inline void _extend_load_segment_vma(const ElfW(Phdr)* phdr_table, size_t phdr_count,
Kalesh Singhb23787f2024-09-05 08:22:06 +0000818 size_t phdr_idx, ElfW(Addr)* p_memsz,
819 ElfW(Addr)* p_filesz, bool should_pad_segments,
820 bool should_use_16kib_app_compat) {
821 // NOTE: Segment extension is only applicable where the ELF's max-page-size > runtime page size;
822 // to save kernel VMA slab memory. 16KiB compat mode is the exact opposite scenario.
823 if (should_use_16kib_app_compat) {
824 return;
825 }
826
Kalesh Singh4084b552024-03-13 13:35:49 -0700827 const ElfW(Phdr)* phdr = &phdr_table[phdr_idx];
828 const ElfW(Phdr)* next = nullptr;
829 size_t next_idx = phdr_idx + 1;
830
Kalesh Singhe1e74792024-04-09 11:48:52 -0700831 // Don't do segment extension for p_align > 64KiB, such ELFs already existed in the
832 // field e.g. 2MiB p_align for THPs and are relatively small in number.
833 //
834 // The kernel can only represent padding for p_align up to 64KiB. This is because
835 // the kernel uses 4 available bits in the vm_area_struct to represent padding
836 // extent; and so cannot enable mitigations to avoid breaking app compatibility for
837 // p_aligns > 64KiB.
838 //
839 // Don't perform segment extension on these to avoid app compatibility issues.
840 if (phdr->p_align <= kPageSize || phdr->p_align > 64*1024 || !should_pad_segments) {
Kalesh Singh4084b552024-03-13 13:35:49 -0700841 return;
842 }
843
844 if (next_idx < phdr_count && phdr_table[next_idx].p_type == PT_LOAD) {
845 next = &phdr_table[next_idx];
846 }
847
848 // If this is the last LOAD segment, no extension is needed
849 if (!next || *p_memsz != *p_filesz) {
850 return;
851 }
852
853 ElfW(Addr) next_start = page_start(next->p_vaddr);
854 ElfW(Addr) curr_end = page_end(phdr->p_vaddr + *p_memsz);
855
856 // If adjacent segment mappings overlap, no extension is needed.
857 if (curr_end >= next_start) {
858 return;
859 }
860
861 // Extend the LOAD segment mapping to be contiguous with that of
862 // the next LOAD segment.
863 ElfW(Addr) extend = next_start - curr_end;
864 *p_memsz += extend;
865 *p_filesz += extend;
866}
867
Kalesh Singh86e04f62024-09-05 06:24:14 +0000868bool ElfReader::MapSegment(size_t seg_idx, size_t len) {
869 const ElfW(Phdr)* phdr = &phdr_table_[seg_idx];
870
871 void* start = reinterpret_cast<void*>(page_start(phdr->p_vaddr + load_bias_));
872
873 // The ELF could be being loaded directly from a zipped APK,
874 // the zip offset must be added to find the segment offset.
875 const ElfW(Addr) offset = file_offset_ + page_start(phdr->p_offset);
876
877 int prot = PFLAGS_TO_PROT(phdr->p_flags);
878
879 void* seg_addr = mmap64(start, len, prot, MAP_FIXED | MAP_PRIVATE, fd_, offset);
880
881 if (seg_addr == MAP_FAILED) {
882 DL_ERR("couldn't map \"%s\" segment %zd: %m", name_.c_str(), seg_idx);
883 return false;
884 }
885
886 // Mark segments as huge page eligible if they meet the requirements
887 if ((phdr->p_flags & PF_X) && phdr->p_align == kPmdSize &&
888 get_transparent_hugepages_supported()) {
889 madvise(seg_addr, len, MADV_HUGEPAGE);
890 }
891
892 return true;
893}
894
Kalesh Singh37bcaea2024-09-05 06:32:07 +0000895void ElfReader::ZeroFillSegment(const ElfW(Phdr)* phdr) {
Kalesh Singhb23787f2024-09-05 08:22:06 +0000896 // NOTE: In 16KiB app compat mode, the ELF mapping is anonymous, meaning that
897 // RW segments are COW-ed from the kernel's zero page. So there is no need to
898 // explicitly zero-fill until the last page's limit.
899 if (should_use_16kib_app_compat_) {
900 return;
901 }
902
Kalesh Singh37bcaea2024-09-05 06:32:07 +0000903 ElfW(Addr) seg_start = phdr->p_vaddr + load_bias_;
904 uint64_t unextended_seg_file_end = seg_start + phdr->p_filesz;
905
906 // If the segment is writable, and does not end on a page boundary,
907 // zero-fill it until the page limit.
908 //
909 // Do not attempt to zero the extended region past the first partial page,
910 // since doing so may:
911 // 1) Result in a SIGBUS, as the region is not backed by the underlying
912 // file.
913 // 2) Break the COW backing, faulting in new anon pages for a region
914 // that will not be used.
915 if ((phdr->p_flags & PF_W) != 0 && page_offset(unextended_seg_file_end) > 0) {
916 memset(reinterpret_cast<void*>(unextended_seg_file_end), 0,
917 kPageSize - page_offset(unextended_seg_file_end));
918 }
919}
920
Kalesh Singhe0f4a372024-09-05 07:07:21 +0000921void ElfReader::DropPaddingPages(const ElfW(Phdr)* phdr, uint64_t seg_file_end) {
Kalesh Singhb23787f2024-09-05 08:22:06 +0000922 // NOTE: Padding pages are only applicable where the ELF's max-page-size > runtime page size;
923 // 16KiB compat mode is the exact opposite scenario.
924 if (should_use_16kib_app_compat_) {
925 return;
926 }
927
Kalesh Singhe0f4a372024-09-05 07:07:21 +0000928 ElfW(Addr) seg_start = phdr->p_vaddr + load_bias_;
929 uint64_t unextended_seg_file_end = seg_start + phdr->p_filesz;
930
931 uint64_t pad_start = page_end(unextended_seg_file_end);
932 uint64_t pad_end = page_end(seg_file_end);
933 CHECK(pad_start <= pad_end);
934
935 uint64_t pad_len = pad_end - pad_start;
936 if (pad_len == 0 || !page_size_migration_supported()) {
937 return;
938 }
939
940 // Pages may be brought in due to readahead.
941 // Drop the padding (zero) pages, to avoid reclaim work later.
942 //
943 // NOTE: The madvise() here is special, as it also serves to hint to the
944 // kernel the portion of the LOAD segment that is padding.
945 //
946 // See: [1] https://android-review.googlesource.com/c/kernel/common/+/3032411
947 // [2] https://android-review.googlesource.com/c/kernel/common/+/3048835
948 if (madvise(reinterpret_cast<void*>(pad_start), pad_len, MADV_DONTNEED)) {
949 DL_WARN("\"%s\": madvise(0x%" PRIx64 ", 0x%" PRIx64 ", MADV_DONTNEED) failed: %m",
950 name_.c_str(), pad_start, pad_len);
951 }
952}
953
Kalesh Singh138a9552024-09-05 08:05:56 +0000954bool ElfReader::MapBssSection(const ElfW(Phdr)* phdr, ElfW(Addr) seg_page_end,
955 ElfW(Addr) seg_file_end) {
Kalesh Singhb23787f2024-09-05 08:22:06 +0000956 // NOTE: We do not need to handle .bss in 16KiB compat mode since the mapping
957 // reservation is anonymous and RW to begin with.
958 if (should_use_16kib_app_compat_) {
959 return true;
960 }
961
Kalesh Singh138a9552024-09-05 08:05:56 +0000962 // seg_file_end is now the first page address after the file content.
963 seg_file_end = page_end(seg_file_end);
964
965 if (seg_page_end <= seg_file_end) {
966 return true;
967 }
968
969 // If seg_page_end is larger than seg_file_end, we need to zero
970 // anything between them. This is done by using a private anonymous
971 // map for all extra pages
972 size_t zeromap_size = seg_page_end - seg_file_end;
973 void* zeromap =
974 mmap(reinterpret_cast<void*>(seg_file_end), zeromap_size, PFLAGS_TO_PROT(phdr->p_flags),
975 MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
976 if (zeromap == MAP_FAILED) {
977 DL_ERR("couldn't map .bss section for \"%s\": %m", name_.c_str());
978 return false;
979 }
980
981 // Set the VMA name using prctl
982 prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, zeromap, zeromap_size, ".bss");
983
984 return true;
985}
986
Elliott Hughes650be4e2013-03-05 18:47:58 -0800987bool ElfReader::LoadSegments() {
Kalesh Singhce1c3cf2024-09-30 13:26:23 -0700988 // NOTE: The compat(legacy) page size (4096) must be used when aligning
989 // the 4KiB segments for loading in compat mode. The larger 16KiB page size
990 // will lead to overwriting adjacent segments since the ELF's segment(s)
991 // are not 16KiB aligned.
992 size_t seg_align = should_use_16kib_app_compat_ ? kCompatPageSize : kPageSize;
Kalesh Singhb23787f2024-09-05 08:22:06 +0000993
Kalesh Singhb23787f2024-09-05 08:22:06 +0000994 // Only enforce this on 16 KB systems with app compat disabled.
995 // Apps may rely on undefined behavior here on 4 KB systems,
996 // which is the norm before this change is introduced
Elliott Hughesd9a97792024-12-10 20:32:44 +0000997 if (kPageSize >= 16384 && min_align_ < kPageSize && !should_use_16kib_app_compat_) {
Kalesh Singhe065bd42025-07-21 16:34:39 -0700998 std::string err_msg = android::base::StringPrintf(
999 "\"%s\" program alignment (%zu) cannot be smaller than system page size (%zu)",
1000 name_.c_str(), min_align_, kPageSize);
1001
1002 DL_ERR_AND_LOG("%s", err_msg.c_str());
1003
1004 if (dlopen_16kib_err_is_fatal_) {
1005 android_set_abort_message(err_msg.c_str());
1006 inline_raise(SIGABRT);
1007 }
1008
Steven Morelandfc89c8a2024-08-01 21:20:33 +00001009 return false;
1010 }
1011
Kalesh Singh120ac662025-07-16 22:20:20 -07001012 if (!Setup16KiBAppCompat()) return false;
Kalesh Singhce1c3cf2024-09-30 13:26:23 -07001013
Elliott Hughes650be4e2013-03-05 18:47:58 -08001014 for (size_t i = 0; i < phdr_num_; ++i) {
Elliott Hughes0266ae52014-02-10 17:46:57 -08001015 const ElfW(Phdr)* phdr = &phdr_table_[i];
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001016
Elliott Hughes650be4e2013-03-05 18:47:58 -08001017 if (phdr->p_type != PT_LOAD) {
1018 continue;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001019 }
Elliott Hughes650be4e2013-03-05 18:47:58 -08001020
Kalesh Singh4084b552024-03-13 13:35:49 -07001021 ElfW(Addr) p_memsz = phdr->p_memsz;
1022 ElfW(Addr) p_filesz = phdr->p_filesz;
Kalesh Singhb23787f2024-09-05 08:22:06 +00001023 _extend_load_segment_vma(phdr_table_, phdr_num_, i, &p_memsz, &p_filesz, should_pad_segments_,
1024 should_use_16kib_app_compat_);
Kalesh Singh4084b552024-03-13 13:35:49 -07001025
Elliott Hughes650be4e2013-03-05 18:47:58 -08001026 // Segment addresses in memory.
Elliott Hughes0266ae52014-02-10 17:46:57 -08001027 ElfW(Addr) seg_start = phdr->p_vaddr + load_bias_;
Kalesh Singh4084b552024-03-13 13:35:49 -07001028 ElfW(Addr) seg_end = seg_start + p_memsz;
Elliott Hughes650be4e2013-03-05 18:47:58 -08001029
Elliott Hughesfed0ce92024-12-11 09:53:34 -08001030 ElfW(Addr) seg_page_end = __builtin_align_up(seg_end, seg_align);
Elliott Hughes650be4e2013-03-05 18:47:58 -08001031
Kalesh Singh4084b552024-03-13 13:35:49 -07001032 ElfW(Addr) seg_file_end = seg_start + p_filesz;
Elliott Hughes650be4e2013-03-05 18:47:58 -08001033
1034 // File offsets.
Elliott Hughes0266ae52014-02-10 17:46:57 -08001035 ElfW(Addr) file_start = phdr->p_offset;
Kalesh Singh4084b552024-03-13 13:35:49 -07001036 ElfW(Addr) file_end = file_start + p_filesz;
Elliott Hughes650be4e2013-03-05 18:47:58 -08001037
Elliott Hughesfed0ce92024-12-11 09:53:34 -08001038 ElfW(Addr) file_page_start = __builtin_align_down(file_start, seg_align);
Elliott Hughes0266ae52014-02-10 17:46:57 -08001039 ElfW(Addr) file_length = file_end - file_page_start;
Elliott Hughes650be4e2013-03-05 18:47:58 -08001040
Dmitriy Ivanov3f987f52015-06-25 15:51:41 -07001041 if (file_size_ <= 0) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -07001042 DL_ERR("\"%s\" invalid file size: %" PRId64, name_.c_str(), file_size_);
Dmitriy Ivanov3f987f52015-06-25 15:51:41 -07001043 return false;
1044 }
1045
Kalesh Singh4084b552024-03-13 13:35:49 -07001046 if (file_start + phdr->p_filesz > static_cast<size_t>(file_size_)) {
Dmitriy Ivanov3f987f52015-06-25 15:51:41 -07001047 DL_ERR("invalid ELF file \"%s\" load segment[%zd]:"
1048 " p_offset (%p) + p_filesz (%p) ( = %p) past end of file (0x%" PRIx64 ")",
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -07001049 name_.c_str(), i, reinterpret_cast<void*>(phdr->p_offset),
Dmitriy Ivanov3f987f52015-06-25 15:51:41 -07001050 reinterpret_cast<void*>(phdr->p_filesz),
Kalesh Singh4084b552024-03-13 13:35:49 -07001051 reinterpret_cast<void*>(file_start + phdr->p_filesz), file_size_);
Dmitriy Ivanov3f987f52015-06-25 15:51:41 -07001052 return false;
1053 }
1054
Brian Carlstrom82dcc792013-05-21 16:49:24 -07001055 if (file_length != 0) {
Dimitry Ivanov9700bab2016-08-10 18:54:06 -07001056 int prot = PFLAGS_TO_PROT(phdr->p_flags);
Dimitry Ivanov9700bab2016-08-10 18:54:06 -07001057 if ((prot & (PROT_EXEC | PROT_WRITE)) == (PROT_EXEC | PROT_WRITE)) {
Elliott Hughesc14f5452025-01-30 13:33:04 -08001058 if (DL_ERROR_AFTER(26, "\"%s\" has load segments that are both writable and executable",
1059 name_.c_str())) {
Elliott Hughes4cc5a602016-11-15 16:54:16 -08001060 return false;
1061 }
Elliott Hughes4cc5a602016-11-15 16:54:16 -08001062 add_dlwarning(name_.c_str(), "W+E load segments");
Dimitry Ivanov9700bab2016-08-10 18:54:06 -07001063 }
1064
Kalesh Singh86e04f62024-09-05 06:24:14 +00001065 // Pass the file_length, since it may have been extended by _extend_load_segment_vma().
Kalesh Singhce1c3cf2024-09-30 13:26:23 -07001066 if (should_use_16kib_app_compat_) {
1067 if (!CompatMapSegment(i, file_length)) {
1068 return false;
1069 }
1070 } else {
1071 if (!MapSegment(i, file_length)) {
1072 return false;
1073 }
Brian Carlstrom82dcc792013-05-21 16:49:24 -07001074 }
Elliott Hughes650be4e2013-03-05 18:47:58 -08001075 }
1076
Kalesh Singh37bcaea2024-09-05 06:32:07 +00001077 ZeroFillSegment(phdr);
Kalesh Singh1d3ba112024-03-06 17:33:36 -08001078
Kalesh Singhe0f4a372024-09-05 07:07:21 +00001079 DropPaddingPages(phdr, seg_file_end);
Elliott Hughes650be4e2013-03-05 18:47:58 -08001080
Kalesh Singh138a9552024-09-05 08:05:56 +00001081 if (!MapBssSection(phdr, seg_page_end, seg_file_end)) {
1082 return false;
Elliott Hughes650be4e2013-03-05 18:47:58 -08001083 }
1084 }
1085 return true;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001086}
1087
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001088/* Used internally. Used to set the protection bits of all loaded segments
1089 * with optional extra flags (i.e. really PROT_WRITE). Used by
1090 * phdr_table_protect_segments and phdr_table_unprotect_segments.
1091 */
1092static int _phdr_table_set_load_prot(const ElfW(Phdr)* phdr_table, size_t phdr_count,
Kalesh Singh4084b552024-03-13 13:35:49 -07001093 ElfW(Addr) load_bias, int extra_prot_flags,
Csanád Hajdú1473ca62025-09-01 12:29:54 +02001094 bool should_pad_segments) {
Kalesh Singh4084b552024-03-13 13:35:49 -07001095 for (size_t i = 0; i < phdr_count; ++i) {
1096 const ElfW(Phdr)* phdr = &phdr_table[i];
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001097
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001098 if (phdr->p_type != PT_LOAD || (phdr->p_flags & PF_W) != 0) {
1099 continue;
1100 }
1101
Kalesh Singh4084b552024-03-13 13:35:49 -07001102 ElfW(Addr) p_memsz = phdr->p_memsz;
1103 ElfW(Addr) p_filesz = phdr->p_filesz;
Kalesh Singhb23787f2024-09-05 08:22:06 +00001104 _extend_load_segment_vma(phdr_table, phdr_count, i, &p_memsz, &p_filesz, should_pad_segments,
Csanád Hajdú1473ca62025-09-01 12:29:54 +02001105 /*should_use_16kib_app_compat=*/false);
Kalesh Singh4084b552024-03-13 13:35:49 -07001106
1107 ElfW(Addr) seg_page_start = page_start(phdr->p_vaddr + load_bias);
1108 ElfW(Addr) seg_page_end = page_end(phdr->p_vaddr + p_memsz + load_bias);
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001109
Tamas Petz8d55d182020-02-24 14:15:25 +01001110 int prot = PFLAGS_TO_PROT(phdr->p_flags) | extra_prot_flags;
1111 if ((prot & PROT_WRITE) != 0) {
Nick Kralevich8fdb3412015-04-01 16:57:50 -07001112 // make sure we're never simultaneously writable / executable
1113 prot &= ~PROT_EXEC;
1114 }
Tamas Petz8d55d182020-02-24 14:15:25 +01001115#if defined(__aarch64__)
1116 if ((prot & PROT_EXEC) == 0) {
1117 // Though it is not specified don't add PROT_BTI if segment is not
1118 // executable.
1119 prot &= ~PROT_BTI;
1120 }
1121#endif
Nick Kralevich8fdb3412015-04-01 16:57:50 -07001122
Tamas Petz8d55d182020-02-24 14:15:25 +01001123 int ret =
1124 mprotect(reinterpret_cast<void*>(seg_page_start), seg_page_end - seg_page_start, prot);
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001125 if (ret < 0) {
1126 return -1;
1127 }
1128 }
1129 return 0;
1130}
1131
1132/* Restore the original protection modes for all loadable segments.
1133 * You should only call this after phdr_table_unprotect_segments and
1134 * applying all relocations.
1135 *
Tamas Petz8d55d182020-02-24 14:15:25 +01001136 * AArch64: also called from linker_main and ElfReader::Load to apply
1137 * PROT_BTI for loaded main so and other so-s.
1138 *
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001139 * Input:
1140 * phdr_table -> program header table
1141 * phdr_count -> number of entries in tables
1142 * load_bias -> load bias
Kalesh Singh4084b552024-03-13 13:35:49 -07001143 * should_pad_segments -> Are segments extended to avoid gaps in the memory map
Kalesh Singhb23787f2024-09-05 08:22:06 +00001144 * should_use_16kib_app_compat -> Is the ELF being loaded in 16KiB app compat mode.
Tamas Petz8d55d182020-02-24 14:15:25 +01001145 * prop -> GnuPropertySection or nullptr
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001146 * Return:
Mitch Phillips117e45e2023-10-20 13:32:33 +00001147 * 0 on success, -1 on failure (error code in errno).
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001148 */
Tamas Petz8d55d182020-02-24 14:15:25 +01001149int phdr_table_protect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count,
Kalesh Singh4084b552024-03-13 13:35:49 -07001150 ElfW(Addr) load_bias, bool should_pad_segments,
Kalesh Singhb23787f2024-09-05 08:22:06 +00001151 bool should_use_16kib_app_compat,
Kalesh Singh4084b552024-03-13 13:35:49 -07001152 const GnuPropertySection* prop __unused) {
Csanád Hajdú1473ca62025-09-01 12:29:54 +02001153 // Segment permissions are handled separately in 16KiB compatibility mode.
1154 if (should_use_16kib_app_compat) {
1155 return 0;
1156 }
1157
Tamas Petz8d55d182020-02-24 14:15:25 +01001158 int prot = 0;
1159#if defined(__aarch64__)
1160 if ((prop != nullptr) && prop->IsBTICompatible()) {
1161 prot |= PROT_BTI;
1162 }
1163#endif
Csanád Hajdú1473ca62025-09-01 12:29:54 +02001164 return _phdr_table_set_load_prot(phdr_table, phdr_count, load_bias, prot, should_pad_segments);
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001165}
1166
Florian Mayer4edc20d2024-10-30 14:24:26 -07001167static bool segment_needs_memtag_globals_remapping(const ElfW(Phdr) * phdr) {
1168 // For now, MTE globals is only supported on writeable data segments.
1169 return phdr->p_type == PT_LOAD && !(phdr->p_flags & PF_X) && (phdr->p_flags & PF_W);
1170}
1171
1172/* When MTE globals are requested by the binary, and when the hardware supports
1173 * it, remap the executable's PT_LOAD data pages to have PROT_MTE.
1174 *
1175 * Returns 0 on success, -1 on failure (error code in errno).
1176 */
1177int remap_memtag_globals_segments(const ElfW(Phdr) * phdr_table __unused,
1178 size_t phdr_count __unused, ElfW(Addr) load_bias __unused) {
1179#if defined(__aarch64__)
1180 for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_table + phdr_count; phdr++) {
1181 if (!segment_needs_memtag_globals_remapping(phdr)) {
1182 continue;
1183 }
1184
1185 uintptr_t seg_page_start = page_start(phdr->p_vaddr) + load_bias;
1186 uintptr_t seg_page_end = page_end(phdr->p_vaddr + phdr->p_memsz) + load_bias;
1187 size_t seg_page_aligned_size = seg_page_end - seg_page_start;
1188
1189 int prot = PFLAGS_TO_PROT(phdr->p_flags);
1190 // For anonymous private mappings, it may be possible to simply mprotect()
1191 // the PROT_MTE flag over the top. For file-based mappings, this will fail,
1192 // and we'll need to fall back. We also allow PROT_WRITE here to allow
1193 // writing memory tags (in `soinfo::tag_globals()`), and set these sections
1194 // back to read-only after tags are applied (similar to RELRO).
1195 prot |= PROT_MTE;
1196 if (mprotect(reinterpret_cast<void*>(seg_page_start), seg_page_aligned_size,
1197 prot | PROT_WRITE) == 0) {
1198 continue;
1199 }
1200
1201 void* mapping_copy = mmap(nullptr, seg_page_aligned_size, PROT_READ | PROT_WRITE,
1202 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
1203 linker_memcpy(mapping_copy, reinterpret_cast<void*>(seg_page_start), seg_page_aligned_size);
1204
1205 void* seg_addr = mmap(reinterpret_cast<void*>(seg_page_start), seg_page_aligned_size,
1206 prot | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
1207 if (seg_addr == MAP_FAILED) return -1;
1208
1209 linker_memcpy(seg_addr, mapping_copy, seg_page_aligned_size);
1210 munmap(mapping_copy, seg_page_aligned_size);
1211 }
1212#endif // defined(__aarch64__)
1213 return 0;
1214}
1215
1216void protect_memtag_globals_ro_segments(const ElfW(Phdr) * phdr_table __unused,
1217 size_t phdr_count __unused, ElfW(Addr) load_bias __unused) {
1218#if defined(__aarch64__)
1219 for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_table + phdr_count; phdr++) {
1220 int prot = PFLAGS_TO_PROT(phdr->p_flags);
1221 if (!segment_needs_memtag_globals_remapping(phdr) || (prot & PROT_WRITE)) {
1222 continue;
1223 }
1224
1225 prot |= PROT_MTE;
1226
1227 uintptr_t seg_page_start = page_start(phdr->p_vaddr) + load_bias;
1228 uintptr_t seg_page_end = page_end(phdr->p_vaddr + phdr->p_memsz) + load_bias;
1229 size_t seg_page_aligned_size = seg_page_end - seg_page_start;
1230 mprotect(reinterpret_cast<void*>(seg_page_start), seg_page_aligned_size, prot);
1231 }
1232#endif // defined(__aarch64__)
1233}
1234
1235void name_memtag_globals_segments(const ElfW(Phdr) * phdr_table, size_t phdr_count,
1236 ElfW(Addr) load_bias, const char* soname,
1237 std::list<std::string>* vma_names) {
1238 for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_table + phdr_count; phdr++) {
1239 if (!segment_needs_memtag_globals_remapping(phdr)) {
1240 continue;
1241 }
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
1247 // For file-based mappings that we're now forcing to be anonymous mappings, set the VMA name to
1248 // make debugging easier.
1249 // Once we are targeting only devices that run kernel 5.10 or newer (and thus include
1250 // https://android-review.git.corp.google.com/c/kernel/common/+/1934723 which causes the
1251 // VMA_ANON_NAME to be copied into the kernel), we can get rid of the storage here.
1252 // For now, that is not the case:
1253 // https://source.android.com/docs/core/architecture/kernel/android-common#compatibility-matrix
Florian Mayer4edc20d2024-10-30 14:24:26 -07001254 std::string& vma_name = vma_names->emplace_back(kVmaNameLimit, '\0');
Csanád Hajdú69be8382025-05-21 08:08:17 -07001255 // 18 characters are enough for the '+' prefix, 16 hex digits, and the null terminator.
1256 char suffix_buffer[18] = {};
1257 async_safe_format_buffer(suffix_buffer, sizeof(suffix_buffer), "+%" PRIxPTR,
1258 page_start(phdr->p_vaddr));
1259 format_left_truncated_vma_anon_name(vma_name.data(), vma_name.size(), "mt:", soname,
1260 suffix_buffer);
Florian Mayer4edc20d2024-10-30 14:24:26 -07001261 if (prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, reinterpret_cast<void*>(seg_page_start),
1262 seg_page_aligned_size, vma_name.data()) != 0) {
1263 DL_WARN("Failed to rename memtag global segment: %m");
1264 }
1265 }
1266}
1267
Csanád Hajdú69be8382025-05-21 08:08:17 -07001268/* There's an upper limit of 80 characters, including the null terminator, on the anonymous VMA
1269 * name. This limit is easily exceeded when setting the mapping's name to a path. To stay within the
1270 * character limit, we must truncate the name to fit into 80 bytes. Since the most important part of
1271 * a path is the basename, we start truncating from the left side.
1272 *
1273 * Example (with prefix = "mt:", suffix = "+e000", and '#' as the null terminator):
1274 * - "mt:/data/nativetest64/bionic-unit-tests/bionic-loader-test-libs/libdlext_test.so+e000#"
1275 * This mapping name would have a length of 86, so we left-truncate (86 - 80 + 3) 9 characters from
1276 * the path in order to add "..." to the front and fit into the 80 character limit:
1277 * - "mt:...ivetest64/bionic-unit-tests/bionic-loader-test-libs/libdlext_test.so+e000#"
1278 */
1279void format_left_truncated_vma_anon_name(char* buffer, size_t buffer_size, const char* prefix,
1280 const char* name, const char* suffix) {
1281 size_t full_vma_name_length =
1282 async_safe_format_buffer(buffer, buffer_size, "%s%s%s", prefix, name, suffix) +
1283 /* null terminator */ 1;
1284 if (full_vma_name_length > buffer_size) {
1285 const char* truncation_prefix = "...";
1286 size_t truncation_prefix_length = strlen(truncation_prefix);
1287 size_t truncated_bytes = full_vma_name_length - buffer_size + truncation_prefix_length;
1288 async_safe_format_buffer(buffer, buffer_size, "%s%s%s%s", prefix, truncation_prefix,
1289 name + truncated_bytes, suffix);
1290 }
1291}
1292
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001293/* Change the protection of all loaded segments in memory to writable.
1294 * This is useful before performing relocations. Once completed, you
1295 * will have to call phdr_table_protect_segments to restore the original
1296 * protection flags on all segments.
1297 *
1298 * Note that some writable segments can also have their content turned
1299 * to read-only by calling phdr_table_protect_gnu_relro. This is no
1300 * performed here.
1301 *
1302 * Input:
1303 * phdr_table -> program header table
1304 * phdr_count -> number of entries in tables
1305 * load_bias -> load bias
Kalesh Singh4084b552024-03-13 13:35:49 -07001306 * should_pad_segments -> Are segments extended to avoid gaps in the memory map
Kalesh Singhb23787f2024-09-05 08:22:06 +00001307 * should_use_16kib_app_compat -> Is the ELF being loaded in 16KiB app compat mode.
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001308 * Return:
Mitch Phillips117e45e2023-10-20 13:32:33 +00001309 * 0 on success, -1 on failure (error code in errno).
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001310 */
Kalesh Singhb23787f2024-09-05 08:22:06 +00001311int phdr_table_unprotect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count,
1312 ElfW(Addr) load_bias, bool should_pad_segments,
1313 bool should_use_16kib_app_compat) {
Csanád Hajdú1473ca62025-09-01 12:29:54 +02001314 // Segment permissions are handled separately in 16KiB compatibility mode. Also in this case
1315 // binaries are mapped entirely RW until relro protection is applied, so they don't need to be
1316 // unprotected before performing dynamic relocations.
1317 if (should_use_16kib_app_compat) {
1318 return 0;
1319 }
1320
Kalesh Singh4084b552024-03-13 13:35:49 -07001321 return _phdr_table_set_load_prot(phdr_table, phdr_count, load_bias, PROT_WRITE,
Csanád Hajdú1473ca62025-09-01 12:29:54 +02001322 should_pad_segments);
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001323}
1324
Kalesh Singh702d9b02024-03-13 13:38:04 -07001325static inline void _extend_gnu_relro_prot_end(const ElfW(Phdr)* relro_phdr,
1326 const ElfW(Phdr)* phdr_table, size_t phdr_count,
1327 ElfW(Addr) load_bias, ElfW(Addr)* seg_page_end,
Csanád Hajdúbee0bbd2025-09-01 11:58:01 +02001328 bool should_pad_segments) {
Kalesh Singh702d9b02024-03-13 13:38:04 -07001329 // Find the index and phdr of the LOAD containing the GNU_RELRO segment
1330 for (size_t index = 0; index < phdr_count; ++index) {
1331 const ElfW(Phdr)* phdr = &phdr_table[index];
1332
1333 if (phdr->p_type == PT_LOAD && phdr->p_vaddr == relro_phdr->p_vaddr) {
1334 // If the PT_GNU_RELRO mem size is not at least as large as the corresponding
1335 // LOAD segment mem size, we need to protect only a partial region of the
1336 // LOAD segment and therefore cannot avoid a VMA split.
1337 //
1338 // Note: Don't check the page-aligned mem sizes since the extended protection
1339 // may incorrectly write protect non-relocation data.
1340 //
1341 // Example:
1342 //
1343 // |---- 3K ----|-- 1K --|---- 3K ---- |-- 1K --|
1344 // ----------------------------------------------------------------
1345 // | | | | |
1346 // SEG X | RO | RO | RW | | SEG Y
1347 // | | | | |
1348 // ----------------------------------------------------------------
1349 // | | |
1350 // | | |
1351 // | | |
1352 // relro_vaddr relro_vaddr relro_vaddr
1353 // (load_vaddr) + +
1354 // relro_memsz load_memsz
1355 //
1356 // ----------------------------------------------------------------
1357 // | PAGE | PAGE |
1358 // ----------------------------------------------------------------
1359 // | Potential |
1360 // |----- Extended RO ----|
1361 // | Protection |
1362 //
1363 // If the check below uses page aligned mem sizes it will cause incorrect write
1364 // protection of the 3K RW part of the LOAD segment containing the GNU_RELRO.
1365 if (relro_phdr->p_memsz < phdr->p_memsz) {
1366 return;
1367 }
1368
1369 ElfW(Addr) p_memsz = phdr->p_memsz;
1370 ElfW(Addr) p_filesz = phdr->p_filesz;
1371
1372 // Attempt extending the VMA (mprotect range). Without extending the range,
1373 // mprotect will only RO protect a part of the extended RW LOAD segment, which
1374 // will leave an extra split RW VMA (the gap).
1375 _extend_load_segment_vma(phdr_table, phdr_count, index, &p_memsz, &p_filesz,
Csanád Hajdúbee0bbd2025-09-01 11:58:01 +02001376 should_pad_segments, /*should_use_16kib_app_compat=*/false);
Kalesh Singh702d9b02024-03-13 13:38:04 -07001377
1378 *seg_page_end = page_end(phdr->p_vaddr + p_memsz + load_bias);
1379 return;
1380 }
1381 }
1382}
1383
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001384/* Used internally by phdr_table_protect_gnu_relro and
1385 * phdr_table_unprotect_gnu_relro.
1386 */
Elliott Hughes0266ae52014-02-10 17:46:57 -08001387static int _phdr_table_set_gnu_relro_prot(const ElfW(Phdr)* phdr_table, size_t phdr_count,
Kalesh Singh702d9b02024-03-13 13:38:04 -07001388 ElfW(Addr) load_bias, int prot_flags,
Csanád Hajdúbee0bbd2025-09-01 11:58:01 +02001389 bool should_pad_segments) {
Elliott Hughes0266ae52014-02-10 17:46:57 -08001390 const ElfW(Phdr)* phdr = phdr_table;
1391 const ElfW(Phdr)* phdr_limit = phdr + phdr_count;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001392
Elliott Hughes0266ae52014-02-10 17:46:57 -08001393 for (phdr = phdr_table; phdr < phdr_limit; phdr++) {
1394 if (phdr->p_type != PT_GNU_RELRO) {
1395 continue;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001396 }
Elliott Hughes0266ae52014-02-10 17:46:57 -08001397
1398 // Tricky: what happens when the relro segment does not start
1399 // or end at page boundaries? We're going to be over-protective
1400 // here and put every page touched by the segment as read-only.
1401
1402 // This seems to match Ian Lance Taylor's description of the
1403 // feature at http://www.airs.com/blog/archives/189.
1404
1405 // Extract:
1406 // Note that the current dynamic linker code will only work
1407 // correctly if the PT_GNU_RELRO segment starts on a page
1408 // boundary. This is because the dynamic linker rounds the
1409 // p_vaddr field down to the previous page boundary. If
1410 // there is anything on the page which should not be read-only,
1411 // the program is likely to fail at runtime. So in effect the
1412 // linker must only emit a PT_GNU_RELRO segment if it ensures
1413 // that it starts on a page boundary.
Zheng Pan9535c322024-02-14 00:04:10 +00001414 ElfW(Addr) seg_page_start = page_start(phdr->p_vaddr) + load_bias;
1415 ElfW(Addr) seg_page_end = page_end(phdr->p_vaddr + phdr->p_memsz) + load_bias;
Kalesh Singh702d9b02024-03-13 13:38:04 -07001416 _extend_gnu_relro_prot_end(phdr, phdr_table, phdr_count, load_bias, &seg_page_end,
Csanád Hajdúbee0bbd2025-09-01 11:58:01 +02001417 should_pad_segments);
Elliott Hughes0266ae52014-02-10 17:46:57 -08001418
Elliott Hughesfaf05ba2014-02-11 16:59:37 -08001419 int ret = mprotect(reinterpret_cast<void*>(seg_page_start),
Elliott Hughes0266ae52014-02-10 17:46:57 -08001420 seg_page_end - seg_page_start,
1421 prot_flags);
1422 if (ret < 0) {
1423 return -1;
1424 }
1425 }
1426 return 0;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001427}
1428
1429/* Apply GNU relro protection if specified by the program header. This will
1430 * turn some of the pages of a writable PT_LOAD segment to read-only, as
1431 * specified by one or more PT_GNU_RELRO segments. This must be always
1432 * performed after relocations.
1433 *
Ard Biesheuvel12c78bb2012-08-14 12:30:09 +02001434 * The areas typically covered are .got and .data.rel.ro, these are
1435 * read-only from the program's POV, but contain absolute addresses
1436 * that need to be relocated before use.
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001437 *
1438 * Input:
1439 * phdr_table -> program header table
Elliott Hughes105bc262012-08-15 16:56:00 -07001440 * phdr_count -> number of entries in tables
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001441 * load_bias -> load bias
Kalesh Singh702d9b02024-03-13 13:38:04 -07001442 * should_pad_segments -> Were segments extended to avoid gaps in the memory map
Kalesh Singhb23787f2024-09-05 08:22:06 +00001443 * should_use_16kib_app_compat -> Is the ELF being loaded in 16KiB app compat mode.
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001444 * Return:
Mitch Phillips117e45e2023-10-20 13:32:33 +00001445 * 0 on success, -1 on failure (error code in errno).
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001446 */
Kalesh Singh702d9b02024-03-13 13:38:04 -07001447int phdr_table_protect_gnu_relro(const ElfW(Phdr)* phdr_table, size_t phdr_count,
Csanád Hajdúbee0bbd2025-09-01 11:58:01 +02001448 ElfW(Addr) load_bias, bool should_pad_segments) {
Kalesh Singh702d9b02024-03-13 13:38:04 -07001449 return _phdr_table_set_gnu_relro_prot(phdr_table, phdr_count, load_bias, PROT_READ,
Csanád Hajdúbee0bbd2025-09-01 11:58:01 +02001450 should_pad_segments);
Kalesh Singhce1c3cf2024-09-30 13:26:23 -07001451}
1452
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001453/* Serialize the GNU relro segments to the given file descriptor. This can be
1454 * performed after relocations to allow another process to later share the
1455 * relocated segment, if it was loaded at the same address.
1456 *
1457 * Input:
1458 * phdr_table -> program header table
1459 * phdr_count -> number of entries in tables
1460 * load_bias -> load bias
1461 * fd -> writable file descriptor to use
Torne (Richard Coles)fa9f7f22019-04-02 17:04:42 -04001462 * file_offset -> pointer to offset into file descriptor to use/update
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001463 * Return:
Mitch Phillips117e45e2023-10-20 13:32:33 +00001464 * 0 on success, -1 on failure (error code in errno).
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001465 */
Dmitriy Ivanov20d89cb2015-03-30 18:43:38 -07001466int phdr_table_serialize_gnu_relro(const ElfW(Phdr)* phdr_table,
1467 size_t phdr_count,
1468 ElfW(Addr) load_bias,
Torne (Richard Coles)fa9f7f22019-04-02 17:04:42 -04001469 int fd,
1470 size_t* file_offset) {
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001471 const ElfW(Phdr)* phdr = phdr_table;
1472 const ElfW(Phdr)* phdr_limit = phdr + phdr_count;
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001473
1474 for (phdr = phdr_table; phdr < phdr_limit; phdr++) {
1475 if (phdr->p_type != PT_GNU_RELRO) {
1476 continue;
1477 }
1478
Peter Collingbournebb11ee62022-05-02 12:26:16 -07001479 ElfW(Addr) seg_page_start = page_start(phdr->p_vaddr) + load_bias;
1480 ElfW(Addr) seg_page_end = page_end(phdr->p_vaddr + phdr->p_memsz) + load_bias;
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001481 ssize_t size = seg_page_end - seg_page_start;
1482
1483 ssize_t written = TEMP_FAILURE_RETRY(write(fd, reinterpret_cast<void*>(seg_page_start), size));
1484 if (written != size) {
1485 return -1;
1486 }
1487 void* map = mmap(reinterpret_cast<void*>(seg_page_start), size, PROT_READ,
Torne (Richard Coles)fa9f7f22019-04-02 17:04:42 -04001488 MAP_PRIVATE|MAP_FIXED, fd, *file_offset);
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001489 if (map == MAP_FAILED) {
1490 return -1;
1491 }
Torne (Richard Coles)fa9f7f22019-04-02 17:04:42 -04001492 *file_offset += size;
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001493 }
1494 return 0;
1495}
1496
1497/* Where possible, replace the GNU relro segments with mappings of the given
1498 * file descriptor. This can be performed after relocations to allow a file
1499 * previously created by phdr_table_serialize_gnu_relro in another process to
1500 * replace the dirty relocated pages, saving memory, if it was loaded at the
1501 * same address. We have to compare the data before we map over it, since some
1502 * parts of the relro segment may not be identical due to other libraries in
1503 * the process being loaded at different addresses.
1504 *
1505 * Input:
1506 * phdr_table -> program header table
1507 * phdr_count -> number of entries in tables
1508 * load_bias -> load bias
1509 * fd -> readable file descriptor to use
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -04001510 * file_offset -> pointer to offset into file descriptor to use/update
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001511 * Return:
Mitch Phillips117e45e2023-10-20 13:32:33 +00001512 * 0 on success, -1 on failure (error code in errno).
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001513 */
Dmitriy Ivanov20d89cb2015-03-30 18:43:38 -07001514int phdr_table_map_gnu_relro(const ElfW(Phdr)* phdr_table,
1515 size_t phdr_count,
1516 ElfW(Addr) load_bias,
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -04001517 int fd,
1518 size_t* file_offset) {
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001519 // Map the file at a temporary location so we can compare its contents.
1520 struct stat file_stat;
1521 if (TEMP_FAILURE_RETRY(fstat(fd, &file_stat)) != 0) {
1522 return -1;
1523 }
1524 off_t file_size = file_stat.st_size;
Dmitriy Ivanov851135b2014-08-29 12:02:36 -07001525 void* temp_mapping = nullptr;
Torne (Richard Coles)26ec9672014-04-30 15:48:40 +01001526 if (file_size > 0) {
Dmitriy Ivanov851135b2014-08-29 12:02:36 -07001527 temp_mapping = mmap(nullptr, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
Torne (Richard Coles)26ec9672014-04-30 15:48:40 +01001528 if (temp_mapping == MAP_FAILED) {
1529 return -1;
1530 }
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001531 }
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001532
1533 // Iterate over the relro segments and compare/remap the pages.
1534 const ElfW(Phdr)* phdr = phdr_table;
1535 const ElfW(Phdr)* phdr_limit = phdr + phdr_count;
1536
1537 for (phdr = phdr_table; phdr < phdr_limit; phdr++) {
1538 if (phdr->p_type != PT_GNU_RELRO) {
1539 continue;
1540 }
1541
Peter Collingbournebb11ee62022-05-02 12:26:16 -07001542 ElfW(Addr) seg_page_start = page_start(phdr->p_vaddr) + load_bias;
1543 ElfW(Addr) seg_page_end = page_end(phdr->p_vaddr + phdr->p_memsz) + load_bias;
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001544
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -04001545 char* file_base = static_cast<char*>(temp_mapping) + *file_offset;
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001546 char* mem_base = reinterpret_cast<char*>(seg_page_start);
1547 size_t match_offset = 0;
1548 size_t size = seg_page_end - seg_page_start;
1549
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -04001550 if (file_size - *file_offset < size) {
Torne (Richard Coles)26ec9672014-04-30 15:48:40 +01001551 // File is too short to compare to this segment. The contents are likely
1552 // different as well (it's probably for a different library version) so
1553 // just don't bother checking.
1554 break;
1555 }
1556
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001557 while (match_offset < size) {
1558 // Skip over dissimilar pages.
1559 while (match_offset < size &&
Peter Collingbournebb11ee62022-05-02 12:26:16 -07001560 memcmp(mem_base + match_offset, file_base + match_offset, page_size()) != 0) {
1561 match_offset += page_size();
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001562 }
1563
1564 // Count similar pages.
1565 size_t mismatch_offset = match_offset;
1566 while (mismatch_offset < size &&
Peter Collingbournebb11ee62022-05-02 12:26:16 -07001567 memcmp(mem_base + mismatch_offset, file_base + mismatch_offset, page_size()) == 0) {
1568 mismatch_offset += page_size();
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001569 }
1570
1571 // Map over similar pages.
1572 if (mismatch_offset > match_offset) {
1573 void* map = mmap(mem_base + match_offset, mismatch_offset - match_offset,
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -04001574 PROT_READ, MAP_PRIVATE|MAP_FIXED, fd, *file_offset + match_offset);
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001575 if (map == MAP_FAILED) {
1576 munmap(temp_mapping, file_size);
1577 return -1;
1578 }
1579 }
1580
1581 match_offset = mismatch_offset;
1582 }
1583
1584 // Add to the base file offset in case there are multiple relro segments.
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -04001585 *file_offset += size;
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001586 }
1587 munmap(temp_mapping, file_size);
1588 return 0;
1589}
1590
1591
Elliott Hughes4eeb1f12013-10-25 17:38:02 -07001592#if defined(__arm__)
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001593/* Return the address and size of the .ARM.exidx section in memory,
1594 * if present.
1595 *
1596 * Input:
1597 * phdr_table -> program header table
Elliott Hughes105bc262012-08-15 16:56:00 -07001598 * phdr_count -> number of entries in tables
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001599 * load_bias -> load bias
1600 * Output:
Dmitriy Ivanov851135b2014-08-29 12:02:36 -07001601 * arm_exidx -> address of table in memory (null on failure).
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001602 * arm_exidx_count -> number of items in table (0 on failure).
1603 * Return:
Mitch Phillips117e45e2023-10-20 13:32:33 +00001604 * 0 on success, -1 on failure (_no_ error code in errno)
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001605 */
Elliott Hughes0266ae52014-02-10 17:46:57 -08001606int phdr_table_get_arm_exidx(const ElfW(Phdr)* phdr_table, size_t phdr_count,
1607 ElfW(Addr) load_bias,
Dmitriy Ivanov1649e7e2015-01-22 16:04:25 -08001608 ElfW(Addr)** arm_exidx, size_t* arm_exidx_count) {
Elliott Hughes0266ae52014-02-10 17:46:57 -08001609 const ElfW(Phdr)* phdr = phdr_table;
1610 const ElfW(Phdr)* phdr_limit = phdr + phdr_count;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001611
Elliott Hughes0266ae52014-02-10 17:46:57 -08001612 for (phdr = phdr_table; phdr < phdr_limit; phdr++) {
1613 if (phdr->p_type != PT_ARM_EXIDX) {
1614 continue;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001615 }
Elliott Hughes0266ae52014-02-10 17:46:57 -08001616
1617 *arm_exidx = reinterpret_cast<ElfW(Addr)*>(load_bias + phdr->p_vaddr);
Dmitriy Ivanov1649e7e2015-01-22 16:04:25 -08001618 *arm_exidx_count = phdr->p_memsz / 8;
Elliott Hughes0266ae52014-02-10 17:46:57 -08001619 return 0;
1620 }
Dmitriy Ivanov851135b2014-08-29 12:02:36 -07001621 *arm_exidx = nullptr;
Elliott Hughes0266ae52014-02-10 17:46:57 -08001622 *arm_exidx_count = 0;
1623 return -1;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001624}
Elliott Hughes4eeb1f12013-10-25 17:38:02 -07001625#endif
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001626
Ard Biesheuvel12c78bb2012-08-14 12:30:09 +02001627/* Return the address and size of the ELF file's .dynamic section in memory,
Dmitriy Ivanov851135b2014-08-29 12:02:36 -07001628 * or null if missing.
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001629 *
1630 * Input:
1631 * phdr_table -> program header table
Elliott Hughes105bc262012-08-15 16:56:00 -07001632 * phdr_count -> number of entries in tables
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001633 * load_bias -> load bias
Ard Biesheuvel12c78bb2012-08-14 12:30:09 +02001634 * Output:
Dmitriy Ivanov851135b2014-08-29 12:02:36 -07001635 * dynamic -> address of table in memory (null on failure).
Ningsheng Jiane93be992014-09-16 15:22:10 +08001636 * dynamic_flags -> protection flags for section (unset on failure)
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001637 * Return:
Ard Biesheuvel12c78bb2012-08-14 12:30:09 +02001638 * void
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001639 */
Elliott Hughes0266ae52014-02-10 17:46:57 -08001640void phdr_table_get_dynamic_section(const ElfW(Phdr)* phdr_table, size_t phdr_count,
Ningsheng Jiane93be992014-09-16 15:22:10 +08001641 ElfW(Addr) load_bias, ElfW(Dyn)** dynamic,
1642 ElfW(Word)* dynamic_flags) {
Dmitriy Ivanov498eb182014-09-05 14:57:59 -07001643 *dynamic = nullptr;
Dmitriy Ivanov20d89cb2015-03-30 18:43:38 -07001644 for (size_t i = 0; i<phdr_count; ++i) {
1645 const ElfW(Phdr)& phdr = phdr_table[i];
1646 if (phdr.p_type == PT_DYNAMIC) {
1647 *dynamic = reinterpret_cast<ElfW(Dyn)*>(load_bias + phdr.p_vaddr);
Ningsheng Jiane93be992014-09-16 15:22:10 +08001648 if (dynamic_flags) {
Dmitriy Ivanov20d89cb2015-03-30 18:43:38 -07001649 *dynamic_flags = phdr.p_flags;
Ningsheng Jiane93be992014-09-16 15:22:10 +08001650 }
Dmitriy Ivanov14669a92014-09-05 16:42:53 -07001651 return;
1652 }
Elliott Hughes0266ae52014-02-10 17:46:57 -08001653 }
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001654}
1655
Evgenii Stepanovd640b222015-07-10 17:54:01 -07001656/* Return the program interpreter string, or nullptr if missing.
1657 *
1658 * Input:
1659 * phdr_table -> program header table
1660 * phdr_count -> number of entries in tables
1661 * load_bias -> load bias
1662 * Return:
1663 * pointer to the program interpreter string.
1664 */
Tamas Petz8d55d182020-02-24 14:15:25 +01001665const char* phdr_table_get_interpreter_name(const ElfW(Phdr)* phdr_table, size_t phdr_count,
Evgenii Stepanovd640b222015-07-10 17:54:01 -07001666 ElfW(Addr) load_bias) {
1667 for (size_t i = 0; i<phdr_count; ++i) {
1668 const ElfW(Phdr)& phdr = phdr_table[i];
1669 if (phdr.p_type == PT_INTERP) {
1670 return reinterpret_cast<const char*>(load_bias + phdr.p_vaddr);
1671 }
1672 }
1673 return nullptr;
1674}
1675
Robert Grosse4544d9f2014-10-15 14:32:19 -07001676// Sets loaded_phdr_ to the address of the program header table as it appears
1677// in the loaded segments in memory. This is in contrast with phdr_table_,
1678// which is temporary and will be released before the library is relocated.
Elliott Hughes650be4e2013-03-05 18:47:58 -08001679bool ElfReader::FindPhdr() {
Elliott Hughes0266ae52014-02-10 17:46:57 -08001680 const ElfW(Phdr)* phdr_limit = phdr_table_ + phdr_num_;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001681
Elliott Hughes650be4e2013-03-05 18:47:58 -08001682 // If there is a PT_PHDR, use it directly.
Elliott Hughes0266ae52014-02-10 17:46:57 -08001683 for (const ElfW(Phdr)* phdr = phdr_table_; phdr < phdr_limit; ++phdr) {
Elliott Hughes650be4e2013-03-05 18:47:58 -08001684 if (phdr->p_type == PT_PHDR) {
1685 return CheckPhdr(load_bias_ + phdr->p_vaddr);
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001686 }
Elliott Hughes650be4e2013-03-05 18:47:58 -08001687 }
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001688
Elliott Hughes650be4e2013-03-05 18:47:58 -08001689 // Otherwise, check the first loadable segment. If its file offset
1690 // is 0, it starts with the ELF header, and we can trivially find the
1691 // loaded program header from it.
Elliott Hughes0266ae52014-02-10 17:46:57 -08001692 for (const ElfW(Phdr)* phdr = phdr_table_; phdr < phdr_limit; ++phdr) {
Elliott Hughes650be4e2013-03-05 18:47:58 -08001693 if (phdr->p_type == PT_LOAD) {
1694 if (phdr->p_offset == 0) {
Elliott Hughes0266ae52014-02-10 17:46:57 -08001695 ElfW(Addr) elf_addr = load_bias_ + phdr->p_vaddr;
Elliott Hughesfaf05ba2014-02-11 16:59:37 -08001696 const ElfW(Ehdr)* ehdr = reinterpret_cast<const ElfW(Ehdr)*>(elf_addr);
Elliott Hughes0266ae52014-02-10 17:46:57 -08001697 ElfW(Addr) offset = ehdr->e_phoff;
Dmitriy Ivanov1649e7e2015-01-22 16:04:25 -08001698 return CheckPhdr(reinterpret_cast<ElfW(Addr)>(ehdr) + offset);
Elliott Hughes650be4e2013-03-05 18:47:58 -08001699 }
1700 break;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001701 }
Elliott Hughes650be4e2013-03-05 18:47:58 -08001702 }
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001703
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -07001704 DL_ERR("can't find loaded phdr for \"%s\"", name_.c_str());
Elliott Hughes650be4e2013-03-05 18:47:58 -08001705 return false;
1706}
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001707
Tamas Petz8d55d182020-02-24 14:15:25 +01001708// Tries to find .note.gnu.property section.
1709// It is not considered an error if such section is missing.
1710bool ElfReader::FindGnuPropertySection() {
1711#if defined(__aarch64__)
Csanád Hajdú43ce88e2025-08-15 14:51:36 +02001712 note_gnu_property_ = GnuPropertySection(phdr_table_, phdr_num_, load_bias_, name_.c_str());
Tamas Petz8d55d182020-02-24 14:15:25 +01001713#endif
1714 return true;
1715}
1716
Elliott Hughes650be4e2013-03-05 18:47:58 -08001717// Ensures that our program header is actually within a loadable
1718// segment. This should help catch badly-formed ELF files that
1719// would cause the linker to crash later when trying to access it.
Elliott Hughes0266ae52014-02-10 17:46:57 -08001720bool ElfReader::CheckPhdr(ElfW(Addr) loaded) {
1721 const ElfW(Phdr)* phdr_limit = phdr_table_ + phdr_num_;
1722 ElfW(Addr) loaded_end = loaded + (phdr_num_ * sizeof(ElfW(Phdr)));
Dmitriy Ivanovcf1cbbe2015-10-19 16:57:46 -07001723 for (const ElfW(Phdr)* phdr = phdr_table_; phdr < phdr_limit; ++phdr) {
Elliott Hughes650be4e2013-03-05 18:47:58 -08001724 if (phdr->p_type != PT_LOAD) {
1725 continue;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001726 }
Elliott Hughes0266ae52014-02-10 17:46:57 -08001727 ElfW(Addr) seg_start = phdr->p_vaddr + load_bias_;
1728 ElfW(Addr) seg_end = phdr->p_filesz + seg_start;
Elliott Hughes650be4e2013-03-05 18:47:58 -08001729 if (seg_start <= loaded && loaded_end <= seg_end) {
Elliott Hughes0266ae52014-02-10 17:46:57 -08001730 loaded_phdr_ = reinterpret_cast<const ElfW(Phdr)*>(loaded);
Elliott Hughes650be4e2013-03-05 18:47:58 -08001731 return true;
1732 }
1733 }
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -07001734 DL_ERR("\"%s\" loaded phdr %p not in loadable segment",
1735 name_.c_str(), reinterpret_cast<void*>(loaded));
Elliott Hughes650be4e2013-03-05 18:47:58 -08001736 return false;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001737}