blob: 14bf208a4aba96918a05a0d416db55fab1d1943b [file] [log] [blame]
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
Elliott Hughes650be4e2013-03-05 18:47:58 -080029#include "linker_phdr.h"
30
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +020031#include <errno.h>
Elliott Hughes05fc1d72015-01-28 18:02:33 -080032#include <string.h>
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +020033#include <sys/mman.h>
Elliott Hughes99d54652018-08-22 10:36:23 -070034#include <sys/prctl.h>
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +000035#include <sys/types.h>
36#include <sys/stat.h>
37#include <unistd.h>
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +020038
Elliott Hughes650be4e2013-03-05 18:47:58 -080039#include "linker.h"
Florian Mayer4edc20d2024-10-30 14:24:26 -070040#include "linker_debug.h"
Elliott Hughes4cc5a602016-11-15 16:54:16 -080041#include "linker_dlwarning.h"
Dimitry Ivanov48ec2882016-08-04 11:50:36 -070042#include "linker_globals.h"
Florian Mayer4edc20d2024-10-30 14:24:26 -070043#include "linker_logger.h"
44#include "linker_main.h"
45#include "linker_soinfo.h"
Dmitriy Ivanov3c524812015-11-20 17:28:12 -080046#include "linker_utils.h"
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +020047
Kalesh Singh377f0b92024-01-31 20:23:39 -080048#include "private/bionic_asm_note.h"
Evgenii Stepanov0a3637d2016-07-06 13:20:59 -070049#include "private/CFIShadow.h" // For kLibraryAlignment
Kalesh Singh377f0b92024-01-31 20:23:39 -080050#include "private/elf_note.h"
Dimitry Ivanov1b84afc2016-01-27 18:12:03 -080051
Kalesh Singhc5c1d192024-04-09 16:27:56 -070052#include <android-base/file.h>
Kalesh Singhb23787f2024-09-05 08:22:06 +000053#include <android-base/properties.h>
Kalesh Singhc5c1d192024-04-09 16:27:56 -070054
Elliott Hughesb5140262014-12-02 16:16:29 -080055static int GetTargetElfMachine() {
56#if defined(__arm__)
57 return EM_ARM;
58#elif defined(__aarch64__)
59 return EM_AARCH64;
60#elif defined(__i386__)
61 return EM_386;
Elliott Hughes43462702022-10-10 19:21:44 +000062#elif defined(__riscv)
63 return EM_RISCV;
Elliott Hughesb5140262014-12-02 16:16:29 -080064#elif defined(__x86_64__)
65 return EM_X86_64;
66#endif
67}
68
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +020069/**
70 TECHNICAL NOTE ON ELF LOADING.
71
72 An ELF file's program header table contains one or more PT_LOAD
73 segments, which corresponds to portions of the file that need to
74 be mapped into the process' address space.
75
76 Each loadable segment has the following important properties:
77
78 p_offset -> segment file offset
79 p_filesz -> segment file size
80 p_memsz -> segment memory size (always >= p_filesz)
81 p_vaddr -> segment's virtual address
82 p_flags -> segment flags (e.g. readable, writable, executable)
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -070083 p_align -> segment's in-memory and in-file alignment
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +020084
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -070085 We will ignore the p_paddr field of ElfW(Phdr) for now.
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +020086
87 The loadable segments can be seen as a list of [p_vaddr ... p_vaddr+p_memsz)
88 ranges of virtual addresses. A few rules apply:
89
90 - the virtual address ranges should not overlap.
91
92 - if a segment's p_filesz is smaller than its p_memsz, the extra bytes
93 between them should always be initialized to 0.
94
95 - ranges do not necessarily start or end at page boundaries. Two distinct
96 segments can have their start and end on the same page. In this case, the
97 page inherits the mapping flags of the latter segment.
98
99 Finally, the real load addrs of each segment is not p_vaddr. Instead the
100 loader decides where to load the first segment, then will load all others
101 relative to the first one to respect the initial range layout.
102
103 For example, consider the following list:
104
105 [ offset:0, filesz:0x4000, memsz:0x4000, vaddr:0x30000 ],
106 [ offset:0x4000, filesz:0x2000, memsz:0x8000, vaddr:0x40000 ],
107
108 This corresponds to two segments that cover these virtual address ranges:
109
110 0x30000...0x34000
111 0x40000...0x48000
112
113 If the loader decides to load the first segment at address 0xa0000000
114 then the segments' load address ranges will be:
115
116 0xa0030000...0xa0034000
117 0xa0040000...0xa0048000
118
119 In other words, all segments must be loaded at an address that has the same
120 constant offset from their p_vaddr value. This offset is computed as the
121 difference between the first segment's load address, and its p_vaddr value.
122
123 However, in practice, segments do _not_ start at page boundaries. Since we
124 can only memory-map at page boundaries, this means that the bias is
125 computed as:
126
Peter Collingbournebb11ee62022-05-02 12:26:16 -0700127 load_bias = phdr0_load_address - page_start(phdr0->p_vaddr)
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200128
129 (NOTE: The value must be used as a 32-bit unsigned integer, to deal with
130 possible wrap around UINT32_MAX for possible large p_vaddr values).
131
132 And that the phdr0_load_address must start at a page boundary, with
133 the segment's real content starting at:
134
Peter Collingbournebb11ee62022-05-02 12:26:16 -0700135 phdr0_load_address + page_offset(phdr0->p_vaddr)
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200136
137 Note that ELF requires the following condition to make the mmap()-ing work:
138
Peter Collingbournebb11ee62022-05-02 12:26:16 -0700139 page_offset(phdr0->p_vaddr) == page_offset(phdr0->p_offset)
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200140
141 The load_bias must be added to any p_vaddr value read from the ELF file to
142 determine the corresponding memory address.
143
144 **/
145
Kalesh Singh1dd68582024-02-01 00:14:36 -0800146static const size_t kPageSize = page_size();
147
148/*
149 * Generic PMD size calculation:
150 * - Each page table (PT) is of size 1 page.
151 * - Each page table entry (PTE) is of size 64 bits.
152 * - Each PTE locates one physical page frame (PFN) of size 1 page.
153 * - A PMD entry locates 1 page table (PT)
154 *
155 * PMD size = Num entries in a PT * page_size
156 */
157static const size_t kPmdSize = (kPageSize / sizeof(uint64_t)) * kPageSize;
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700158
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700159ElfReader::ElfReader()
160 : did_read_(false), did_load_(false), fd_(-1), file_offset_(0), file_size_(0), phdr_num_(0),
161 phdr_table_(nullptr), shdr_table_(nullptr), shdr_num_(0), dynamic_(nullptr), strtab_(nullptr),
Dimitry Ivanovf45b0e92016-01-15 11:13:35 -0800162 strtab_size_(0), load_start_(nullptr), load_size_(0), load_bias_(0), loaded_phdr_(nullptr),
163 mapped_by_caller_(false) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700164}
165
166bool ElfReader::Read(const char* name, int fd, off64_t file_offset, off64_t file_size) {
Jiyong Park02586a22017-05-20 01:01:24 +0900167 if (did_read_) {
168 return true;
169 }
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700170 name_ = name;
171 fd_ = fd;
172 file_offset_ = file_offset;
173 file_size_ = file_size;
174
175 if (ReadElfHeader() &&
176 VerifyElfHeader() &&
177 ReadProgramHeaders() &&
178 ReadSectionHeaders() &&
Kalesh Singh377f0b92024-01-31 20:23:39 -0800179 ReadDynamicSection() &&
180 ReadPadSegmentNote()) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700181 did_read_ = true;
182 }
183
Kalesh Singhb23787f2024-09-05 08:22:06 +0000184 if (kPageSize == 0x4000 && phdr_table_get_minimum_alignment(phdr_table_, phdr_num_) == 0x1000) {
185 // This prop needs to be read on 16KiB devices for each ELF where min_palign is 4KiB.
186 // It cannot be cached since the developer may toggle app compat on/off.
187 // This check will be removed once app compat is made the default on 16KiB devices.
188 should_use_16kib_app_compat_ =
Pawan Wagh8e5de062024-10-17 18:05:19 +0000189 ::android::base::GetBoolProperty("bionic.linker.16kb.app_compat.enabled", false) ||
190 get_16kb_appcompat_mode();
Kalesh Singhb23787f2024-09-05 08:22:06 +0000191 }
192
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700193 return did_read_;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200194}
195
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -0400196bool ElfReader::Load(address_space_params* address_space) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700197 CHECK(did_read_);
Jiyong Park02586a22017-05-20 01:01:24 +0900198 if (did_load_) {
199 return true;
200 }
huangchaochaobdc37962022-12-27 19:38:41 +0800201 bool reserveSuccess = ReserveAddressSpace(address_space);
202 if (reserveSuccess && LoadSegments() && FindPhdr() &&
Tamas Petz8d55d182020-02-24 14:15:25 +0100203 FindGnuPropertySection()) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700204 did_load_ = true;
Tamas Petz8d55d182020-02-24 14:15:25 +0100205#if defined(__aarch64__)
206 // For Armv8.5-A loaded executable segments may require PROT_BTI.
207 if (note_gnu_property_.IsBTICompatible()) {
Kalesh Singhb23787f2024-09-05 08:22:06 +0000208 did_load_ =
209 (phdr_table_protect_segments(phdr_table_, phdr_num_, load_bias_, should_pad_segments_,
210 should_use_16kib_app_compat_, &note_gnu_property_) == 0);
Tamas Petz8d55d182020-02-24 14:15:25 +0100211 }
212#endif
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700213 }
huangchaochaobdc37962022-12-27 19:38:41 +0800214 if (reserveSuccess && !did_load_) {
215 if (load_start_ != nullptr && load_size_ != 0) {
216 if (!mapped_by_caller_) {
217 munmap(load_start_, load_size_);
218 }
219 }
220 }
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700221
222 return did_load_;
223}
224
225const char* ElfReader::get_string(ElfW(Word) index) const {
226 CHECK(strtab_ != nullptr);
227 CHECK(index < strtab_size_);
228
229 return strtab_ + index;
Elliott Hughes650be4e2013-03-05 18:47:58 -0800230}
231
232bool ElfReader::ReadElfHeader() {
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000233 ssize_t rc = TEMP_FAILURE_RETRY(pread64(fd_, &header_, sizeof(header_), file_offset_));
234 if (rc < 0) {
235 DL_ERR("can't read file \"%s\": %s", name_.c_str(), strerror(errno));
236 return false;
237 }
238
239 if (rc != sizeof(header_)) {
Suren Baghdasaryanea5dd952024-07-19 17:12:16 -0700240 DL_ERR("\"%s\" is too small to be an ELF executable: only found %zd bytes", name_.c_str(),
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000241 static_cast<size_t>(rc));
Elliott Hughes650be4e2013-03-05 18:47:58 -0800242 return false;
243 }
Elliott Hughes650be4e2013-03-05 18:47:58 -0800244 return true;
245}
246
Elliott Hughes72007ee2017-04-19 17:44:57 -0700247static const char* EM_to_string(int em) {
248 if (em == EM_386) return "EM_386";
249 if (em == EM_AARCH64) return "EM_AARCH64";
250 if (em == EM_ARM) return "EM_ARM";
Ulya Trafimovichb973c752022-11-15 14:39:44 +0000251 if (em == EM_RISCV) return "EM_RISCV";
Elliott Hughes72007ee2017-04-19 17:44:57 -0700252 if (em == EM_X86_64) return "EM_X86_64";
253 return "EM_???";
254}
255
Elliott Hughes650be4e2013-03-05 18:47:58 -0800256bool ElfReader::VerifyElfHeader() {
Elliott Hughes625993d2014-07-15 16:53:13 -0700257 if (memcmp(header_.e_ident, ELFMAG, SELFMAG) != 0) {
Elliott Hughesa8971512018-06-27 14:39:06 -0700258 DL_ERR("\"%s\" has bad ELF magic: %02x%02x%02x%02x", name_.c_str(),
259 header_.e_ident[0], header_.e_ident[1], header_.e_ident[2], header_.e_ident[3]);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800260 return false;
261 }
262
Elliott Hughesc00f2cb2013-10-04 17:01:33 -0700263 // Try to give a clear diagnostic for ELF class mismatches, since they're
264 // an easy mistake to make during the 32-bit/64-bit transition period.
265 int elf_class = header_.e_ident[EI_CLASS];
266#if defined(__LP64__)
267 if (elf_class != ELFCLASS64) {
268 if (elf_class == ELFCLASS32) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700269 DL_ERR("\"%s\" is 32-bit instead of 64-bit", name_.c_str());
Elliott Hughesc00f2cb2013-10-04 17:01:33 -0700270 } else {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700271 DL_ERR("\"%s\" has unknown ELF class: %d", name_.c_str(), elf_class);
Elliott Hughesc00f2cb2013-10-04 17:01:33 -0700272 }
Elliott Hughes650be4e2013-03-05 18:47:58 -0800273 return false;
274 }
Elliott Hughesc00f2cb2013-10-04 17:01:33 -0700275#else
276 if (elf_class != ELFCLASS32) {
277 if (elf_class == ELFCLASS64) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700278 DL_ERR("\"%s\" is 64-bit instead of 32-bit", name_.c_str());
Elliott Hughesc00f2cb2013-10-04 17:01:33 -0700279 } else {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700280 DL_ERR("\"%s\" has unknown ELF class: %d", name_.c_str(), elf_class);
Elliott Hughesc00f2cb2013-10-04 17:01:33 -0700281 }
282 return false;
283 }
284#endif
285
Elliott Hughes650be4e2013-03-05 18:47:58 -0800286 if (header_.e_ident[EI_DATA] != ELFDATA2LSB) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700287 DL_ERR("\"%s\" not little-endian: %d", name_.c_str(), header_.e_ident[EI_DATA]);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800288 return false;
289 }
290
291 if (header_.e_type != ET_DYN) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700292 DL_ERR("\"%s\" has unexpected e_type: %d", name_.c_str(), header_.e_type);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800293 return false;
294 }
295
296 if (header_.e_version != EV_CURRENT) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700297 DL_ERR("\"%s\" has unexpected e_version: %d", name_.c_str(), header_.e_version);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800298 return false;
299 }
300
Elliott Hughesb5140262014-12-02 16:16:29 -0800301 if (header_.e_machine != GetTargetElfMachine()) {
Elliott Hughesd16cfac2018-09-17 15:50:09 -0700302 DL_ERR("\"%s\" is for %s (%d) instead of %s (%d)",
303 name_.c_str(),
304 EM_to_string(header_.e_machine), header_.e_machine,
305 EM_to_string(GetTargetElfMachine()), GetTargetElfMachine());
Elliott Hughes650be4e2013-03-05 18:47:58 -0800306 return false;
307 }
308
Dimitry Ivanovc73eec12016-07-22 12:50:59 -0700309 if (header_.e_shentsize != sizeof(ElfW(Shdr))) {
Elliott Hughes95c6cd72019-12-20 13:26:14 -0800310 if (get_application_target_sdk_version() >= 26) {
Dimitry Ivanov12b91872016-11-16 12:29:37 -0800311 DL_ERR_AND_LOG("\"%s\" has unsupported e_shentsize: 0x%x (expected 0x%zx)",
312 name_.c_str(), header_.e_shentsize, sizeof(ElfW(Shdr)));
313 return false;
314 }
Elliott Hughes95c6cd72019-12-20 13:26:14 -0800315 DL_WARN_documented_change(26,
Elliott Hughes9076b0c2018-02-28 11:29:45 -0800316 "invalid-elf-header_section-headers-enforced-for-api-level-26",
317 "\"%s\" has unsupported e_shentsize 0x%x (expected 0x%zx)",
318 name_.c_str(), header_.e_shentsize, sizeof(ElfW(Shdr)));
Dimitry Ivanov12b91872016-11-16 12:29:37 -0800319 add_dlwarning(name_.c_str(), "has invalid ELF header");
Dimitry Ivanovc73eec12016-07-22 12:50:59 -0700320 }
321
322 if (header_.e_shstrndx == 0) {
Elliott Hughes95c6cd72019-12-20 13:26:14 -0800323 if (get_application_target_sdk_version() >= 26) {
Dimitry Ivanov12b91872016-11-16 12:29:37 -0800324 DL_ERR_AND_LOG("\"%s\" has invalid e_shstrndx", name_.c_str());
325 return false;
326 }
Elliott Hughes95c6cd72019-12-20 13:26:14 -0800327 DL_WARN_documented_change(26,
Elliott Hughes9076b0c2018-02-28 11:29:45 -0800328 "invalid-elf-header_section-headers-enforced-for-api-level-26",
329 "\"%s\" has invalid e_shstrndx", name_.c_str());
Dimitry Ivanov12b91872016-11-16 12:29:37 -0800330 add_dlwarning(name_.c_str(), "has invalid ELF header");
Dimitry Ivanovc73eec12016-07-22 12:50:59 -0700331 }
332
Elliott Hughes650be4e2013-03-05 18:47:58 -0800333 return true;
334}
335
Dimitry Ivanovbd906752016-08-08 17:12:18 -0700336bool ElfReader::CheckFileRange(ElfW(Addr) offset, size_t size, size_t alignment) {
Dmitriy Ivanov3c524812015-11-20 17:28:12 -0800337 off64_t range_start;
338 off64_t range_end;
339
Dimitry Ivanov0c9d30f2016-07-13 17:06:36 -0700340 // Only header can be located at the 0 offset... This function called to
341 // check DYNSYM and DYNAMIC sections and phdr/shdr - none of them can be
Dimitry Ivanovebe5af72016-07-14 11:15:44 -0700342 // at offset 0.
Dimitry Ivanov0c9d30f2016-07-13 17:06:36 -0700343
344 return offset > 0 &&
345 safe_add(&range_start, file_offset_, offset) &&
Dmitriy Ivanov3c524812015-11-20 17:28:12 -0800346 safe_add(&range_end, range_start, size) &&
Dimitry Ivanovbd906752016-08-08 17:12:18 -0700347 (range_start < file_size_) &&
348 (range_end <= file_size_) &&
349 ((offset % alignment) == 0);
Dmitriy Ivanov3c524812015-11-20 17:28:12 -0800350}
351
Elliott Hughes650be4e2013-03-05 18:47:58 -0800352// Loads the program header table from an ELF file into a read-only private
353// anonymous mmap-ed block.
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700354bool ElfReader::ReadProgramHeaders() {
Elliott Hughes650be4e2013-03-05 18:47:58 -0800355 phdr_num_ = header_.e_phnum;
356
357 // Like the kernel, we only accept program header tables that
358 // are smaller than 64KiB.
Elliott Hughes0266ae52014-02-10 17:46:57 -0800359 if (phdr_num_ < 1 || phdr_num_ > 65536/sizeof(ElfW(Phdr))) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700360 DL_ERR("\"%s\" has invalid e_phnum: %zd", name_.c_str(), phdr_num_);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800361 return false;
362 }
363
Dmitriy Ivanov3c524812015-11-20 17:28:12 -0800364 // Boundary checks
365 size_t size = phdr_num_ * sizeof(ElfW(Phdr));
Dimitry Ivanovbd906752016-08-08 17:12:18 -0700366 if (!CheckFileRange(header_.e_phoff, size, alignof(ElfW(Phdr)))) {
367 DL_ERR_AND_LOG("\"%s\" has invalid phdr offset/size: %zu/%zu",
368 name_.c_str(),
369 static_cast<size_t>(header_.e_phoff),
370 size);
Dmitriy Ivanov3c524812015-11-20 17:28:12 -0800371 return false;
372 }
373
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000374 if (!phdr_fragment_.Map(fd_, file_offset_, header_.e_phoff, size)) {
Elliott Hughesf5e21d92024-07-26 11:48:19 +0000375 DL_ERR("\"%s\" phdr mmap failed: %m", name_.c_str());
Elliott Hughes650be4e2013-03-05 18:47:58 -0800376 return false;
377 }
378
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000379 phdr_table_ = static_cast<ElfW(Phdr)*>(phdr_fragment_.data());
Elliott Hughes650be4e2013-03-05 18:47:58 -0800380 return true;
381}
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200382
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700383bool ElfReader::ReadSectionHeaders() {
384 shdr_num_ = header_.e_shnum;
385
Dmitriy Ivanovb76123f2015-11-20 10:42:02 -0800386 if (shdr_num_ == 0) {
Dimitry Ivanovbd906752016-08-08 17:12:18 -0700387 DL_ERR_AND_LOG("\"%s\" has no section headers", name_.c_str());
Dmitriy Ivanovb76123f2015-11-20 10:42:02 -0800388 return false;
389 }
390
Dmitriy Ivanov3c524812015-11-20 17:28:12 -0800391 size_t size = shdr_num_ * sizeof(ElfW(Shdr));
Dimitry Ivanovbd906752016-08-08 17:12:18 -0700392 if (!CheckFileRange(header_.e_shoff, size, alignof(const ElfW(Shdr)))) {
393 DL_ERR_AND_LOG("\"%s\" has invalid shdr offset/size: %zu/%zu",
394 name_.c_str(),
395 static_cast<size_t>(header_.e_shoff),
396 size);
Dmitriy Ivanov3c524812015-11-20 17:28:12 -0800397 return false;
398 }
399
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000400 if (!shdr_fragment_.Map(fd_, file_offset_, header_.e_shoff, size)) {
Elliott Hughesf5e21d92024-07-26 11:48:19 +0000401 DL_ERR("\"%s\" shdr mmap failed: %m", name_.c_str());
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700402 return false;
403 }
404
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000405 shdr_table_ = static_cast<const ElfW(Shdr)*>(shdr_fragment_.data());
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700406 return true;
407}
408
409bool ElfReader::ReadDynamicSection() {
410 // 1. Find .dynamic section (in section headers)
411 const ElfW(Shdr)* dynamic_shdr = nullptr;
412 for (size_t i = 0; i < shdr_num_; ++i) {
413 if (shdr_table_[i].sh_type == SHT_DYNAMIC) {
414 dynamic_shdr = &shdr_table_ [i];
415 break;
416 }
417 }
418
419 if (dynamic_shdr == nullptr) {
Dimitry Ivanovbd906752016-08-08 17:12:18 -0700420 DL_ERR_AND_LOG("\"%s\" .dynamic section header was not found", name_.c_str());
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700421 return false;
422 }
423
Dimitry Ivanov0c9d30f2016-07-13 17:06:36 -0700424 // Make sure dynamic_shdr offset and size matches PT_DYNAMIC phdr
425 size_t pt_dynamic_offset = 0;
426 size_t pt_dynamic_filesz = 0;
427 for (size_t i = 0; i < phdr_num_; ++i) {
428 const ElfW(Phdr)* phdr = &phdr_table_[i];
429 if (phdr->p_type == PT_DYNAMIC) {
430 pt_dynamic_offset = phdr->p_offset;
431 pt_dynamic_filesz = phdr->p_filesz;
432 }
433 }
434
435 if (pt_dynamic_offset != dynamic_shdr->sh_offset) {
Elliott Hughes95c6cd72019-12-20 13:26:14 -0800436 if (get_application_target_sdk_version() >= 26) {
Dimitry Ivanove30c17f2016-12-28 16:21:49 -0800437 DL_ERR_AND_LOG("\"%s\" .dynamic section has invalid offset: 0x%zx, "
438 "expected to match PT_DYNAMIC offset: 0x%zx",
439 name_.c_str(),
440 static_cast<size_t>(dynamic_shdr->sh_offset),
441 pt_dynamic_offset);
442 return false;
443 }
Elliott Hughes95c6cd72019-12-20 13:26:14 -0800444 DL_WARN_documented_change(26,
Elliott Hughes9076b0c2018-02-28 11:29:45 -0800445 "invalid-elf-header_section-headers-enforced-for-api-level-26",
446 "\"%s\" .dynamic section has invalid offset: 0x%zx "
447 "(expected to match PT_DYNAMIC offset 0x%zx)",
448 name_.c_str(),
449 static_cast<size_t>(dynamic_shdr->sh_offset),
450 pt_dynamic_offset);
Dimitry Ivanove30c17f2016-12-28 16:21:49 -0800451 add_dlwarning(name_.c_str(), "invalid .dynamic section");
Dimitry Ivanov0c9d30f2016-07-13 17:06:36 -0700452 }
453
454 if (pt_dynamic_filesz != dynamic_shdr->sh_size) {
Elliott Hughes95c6cd72019-12-20 13:26:14 -0800455 if (get_application_target_sdk_version() >= 26) {
Dimitry Ivanove30c17f2016-12-28 16:21:49 -0800456 DL_ERR_AND_LOG("\"%s\" .dynamic section has invalid size: 0x%zx, "
457 "expected to match PT_DYNAMIC filesz: 0x%zx",
458 name_.c_str(),
459 static_cast<size_t>(dynamic_shdr->sh_size),
460 pt_dynamic_filesz);
461 return false;
462 }
Elliott Hughes95c6cd72019-12-20 13:26:14 -0800463 DL_WARN_documented_change(26,
Elliott Hughes9076b0c2018-02-28 11:29:45 -0800464 "invalid-elf-header_section-headers-enforced-for-api-level-26",
465 "\"%s\" .dynamic section has invalid size: 0x%zx "
466 "(expected to match PT_DYNAMIC filesz 0x%zx)",
467 name_.c_str(),
468 static_cast<size_t>(dynamic_shdr->sh_size),
469 pt_dynamic_filesz);
Dimitry Ivanove30c17f2016-12-28 16:21:49 -0800470 add_dlwarning(name_.c_str(), "invalid .dynamic section");
Dimitry Ivanov0c9d30f2016-07-13 17:06:36 -0700471 }
472
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700473 if (dynamic_shdr->sh_link >= shdr_num_) {
Dimitry Ivanovbd906752016-08-08 17:12:18 -0700474 DL_ERR_AND_LOG("\"%s\" .dynamic section has invalid sh_link: %d",
475 name_.c_str(),
476 dynamic_shdr->sh_link);
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700477 return false;
478 }
479
480 const ElfW(Shdr)* strtab_shdr = &shdr_table_[dynamic_shdr->sh_link];
481
482 if (strtab_shdr->sh_type != SHT_STRTAB) {
Dimitry Ivanovbd906752016-08-08 17:12:18 -0700483 DL_ERR_AND_LOG("\"%s\" .dynamic section has invalid link(%d) sh_type: %d (expected SHT_STRTAB)",
484 name_.c_str(), dynamic_shdr->sh_link, strtab_shdr->sh_type);
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700485 return false;
486 }
487
Dimitry Ivanovbd906752016-08-08 17:12:18 -0700488 if (!CheckFileRange(dynamic_shdr->sh_offset, dynamic_shdr->sh_size, alignof(const ElfW(Dyn)))) {
489 DL_ERR_AND_LOG("\"%s\" has invalid offset/size of .dynamic section", name_.c_str());
Dmitriy Ivanov3c524812015-11-20 17:28:12 -0800490 return false;
491 }
492
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000493 if (!dynamic_fragment_.Map(fd_, file_offset_, dynamic_shdr->sh_offset, dynamic_shdr->sh_size)) {
Elliott Hughesf5e21d92024-07-26 11:48:19 +0000494 DL_ERR("\"%s\" dynamic section mmap failed: %m", name_.c_str());
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700495 return false;
496 }
497
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000498 dynamic_ = static_cast<const ElfW(Dyn)*>(dynamic_fragment_.data());
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700499
Dimitry Ivanovbd906752016-08-08 17:12:18 -0700500 if (!CheckFileRange(strtab_shdr->sh_offset, strtab_shdr->sh_size, alignof(const char))) {
501 DL_ERR_AND_LOG("\"%s\" has invalid offset/size of the .strtab section linked from .dynamic section",
502 name_.c_str());
Dmitriy Ivanov3c524812015-11-20 17:28:12 -0800503 return false;
504 }
505
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000506 if (!strtab_fragment_.Map(fd_, file_offset_, strtab_shdr->sh_offset, strtab_shdr->sh_size)) {
Elliott Hughesf5e21d92024-07-26 11:48:19 +0000507 DL_ERR("\"%s\" strtab section mmap failed: %m", name_.c_str());
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700508 return false;
509 }
510
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000511 strtab_ = static_cast<const char*>(strtab_fragment_.data());
512 strtab_size_ = strtab_fragment_.size();
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700513 return true;
514}
515
Brian Carlstrome7dffe12013-01-10 16:39:58 -0800516/* Returns the size of the extent of all the possibly non-contiguous
517 * loadable segments in an ELF program header table. This corresponds
518 * to the page-aligned size in bytes that needs to be reserved in the
519 * process' address space. If there are no loadable segments, 0 is
520 * returned.
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200521 *
Dmitriy Ivanov851135b2014-08-29 12:02:36 -0700522 * If out_min_vaddr or out_max_vaddr are not null, they will be
Brian Carlstrome7dffe12013-01-10 16:39:58 -0800523 * set to the minimum and maximum addresses of pages to be reserved,
524 * or 0 if there is nothing to load.
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200525 */
Elliott Hughes0266ae52014-02-10 17:46:57 -0800526size_t phdr_table_get_load_size(const ElfW(Phdr)* phdr_table, size_t phdr_count,
527 ElfW(Addr)* out_min_vaddr,
528 ElfW(Addr)* out_max_vaddr) {
529 ElfW(Addr) min_vaddr = UINTPTR_MAX;
530 ElfW(Addr) max_vaddr = 0;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200531
Elliott Hughes0266ae52014-02-10 17:46:57 -0800532 bool found_pt_load = false;
533 for (size_t i = 0; i < phdr_count; ++i) {
534 const ElfW(Phdr)* phdr = &phdr_table[i];
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200535
Elliott Hughes0266ae52014-02-10 17:46:57 -0800536 if (phdr->p_type != PT_LOAD) {
537 continue;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200538 }
Elliott Hughes0266ae52014-02-10 17:46:57 -0800539 found_pt_load = true;
540
541 if (phdr->p_vaddr < min_vaddr) {
542 min_vaddr = phdr->p_vaddr;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200543 }
544
Elliott Hughes0266ae52014-02-10 17:46:57 -0800545 if (phdr->p_vaddr + phdr->p_memsz > max_vaddr) {
546 max_vaddr = phdr->p_vaddr + phdr->p_memsz;
547 }
548 }
549 if (!found_pt_load) {
550 min_vaddr = 0;
551 }
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200552
Peter Collingbournebb11ee62022-05-02 12:26:16 -0700553 min_vaddr = page_start(min_vaddr);
554 max_vaddr = page_end(max_vaddr);
Elliott Hughes0266ae52014-02-10 17:46:57 -0800555
Dmitriy Ivanov851135b2014-08-29 12:02:36 -0700556 if (out_min_vaddr != nullptr) {
Elliott Hughes0266ae52014-02-10 17:46:57 -0800557 *out_min_vaddr = min_vaddr;
558 }
Dmitriy Ivanov851135b2014-08-29 12:02:36 -0700559 if (out_max_vaddr != nullptr) {
Elliott Hughes0266ae52014-02-10 17:46:57 -0800560 *out_max_vaddr = max_vaddr;
561 }
562 return max_vaddr - min_vaddr;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200563}
564
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700565// Returns the maximum p_align associated with a loadable segment in the ELF
566// program header table. Used to determine whether the file should be loaded at
567// a specific virtual address alignment for use with huge pages.
568size_t phdr_table_get_maximum_alignment(const ElfW(Phdr)* phdr_table, size_t phdr_count) {
Peter Collingbournebb11ee62022-05-02 12:26:16 -0700569 size_t maximum_alignment = page_size();
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700570
571 for (size_t i = 0; i < phdr_count; ++i) {
572 const ElfW(Phdr)* phdr = &phdr_table[i];
573
574 // p_align must be 0, 1, or a positive, integral power of two.
575 if (phdr->p_type != PT_LOAD || ((phdr->p_align & (phdr->p_align - 1)) != 0)) {
576 continue;
577 }
578
Steven Morelandfc89c8a2024-08-01 21:20:33 +0000579 maximum_alignment = std::max(maximum_alignment, static_cast<size_t>(phdr->p_align));
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700580 }
581
582#if defined(__LP64__)
583 return maximum_alignment;
584#else
Peter Collingbournebb11ee62022-05-02 12:26:16 -0700585 return page_size();
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700586#endif
587}
588
Steven Morelandfc89c8a2024-08-01 21:20:33 +0000589// Returns the minimum p_align associated with a loadable segment in the ELF
590// program header table. Used to determine if the program alignment is compatible
591// with the page size of this system.
592size_t phdr_table_get_minimum_alignment(const ElfW(Phdr)* phdr_table, size_t phdr_count) {
593 size_t minimum_alignment = page_size();
594
595 for (size_t i = 0; i < phdr_count; ++i) {
596 const ElfW(Phdr)* phdr = &phdr_table[i];
597
598 // p_align must be 0, 1, or a positive, integral power of two.
599 if (phdr->p_type != PT_LOAD || ((phdr->p_align & (phdr->p_align - 1)) != 0)) {
600 continue;
601 }
602
603 if (phdr->p_align <= 1) {
604 continue;
605 }
606
607 minimum_alignment = std::min(minimum_alignment, static_cast<size_t>(phdr->p_align));
608 }
609
610 return minimum_alignment;
611}
612
Evgenii Stepanovd13e9a62016-07-15 16:31:42 -0700613// Reserve a virtual address range such that if it's limits were extended to the next 2**align
614// boundary, it would not overlap with any existing mappings.
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700615static void* ReserveWithAlignmentPadding(size_t size, size_t mapping_align, size_t start_align,
616 void** out_gap_start, size_t* out_gap_size) {
Evgenii Stepanovd13e9a62016-07-15 16:31:42 -0700617 int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700618 // Reserve enough space to properly align the library's start address.
619 mapping_align = std::max(mapping_align, start_align);
Peter Collingbournebb11ee62022-05-02 12:26:16 -0700620 if (mapping_align == page_size()) {
Elliott Hughes8178c412018-11-05 13:34:36 -0800621 void* mmap_ptr = mmap(nullptr, size, PROT_NONE, mmap_flags, -1, 0);
Evgenii Stepanovd13e9a62016-07-15 16:31:42 -0700622 if (mmap_ptr == MAP_FAILED) {
623 return nullptr;
624 }
625 return mmap_ptr;
626 }
627
Evgenii Stepanove0848bb2020-07-14 16:44:57 -0700628 // Minimum alignment of shared library gap. For efficiency, this should match the second level
629 // page size of the platform.
630#if defined(__LP64__)
631 constexpr size_t kGapAlignment = 1ul << 21; // 2MB
632#else
633 constexpr size_t kGapAlignment = 0;
634#endif
635 // Maximum gap size, in the units of kGapAlignment.
636 constexpr size_t kMaxGapUnits = 32;
Evgenii Stepanovd13e9a62016-07-15 16:31:42 -0700637 // Allocate enough space so that the end of the desired region aligned up is still inside the
638 // mapping.
Peter Collingbournebb11ee62022-05-02 12:26:16 -0700639 size_t mmap_size = align_up(size, mapping_align) + mapping_align - page_size();
Evgenii Stepanovd13e9a62016-07-15 16:31:42 -0700640 uint8_t* mmap_ptr =
641 reinterpret_cast<uint8_t*>(mmap(nullptr, mmap_size, PROT_NONE, mmap_flags, -1, 0));
642 if (mmap_ptr == MAP_FAILED) {
643 return nullptr;
644 }
Evgenii Stepanove0848bb2020-07-14 16:44:57 -0700645 size_t gap_size = 0;
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700646 size_t first_byte = reinterpret_cast<size_t>(align_up(mmap_ptr, mapping_align));
647 size_t last_byte = reinterpret_cast<size_t>(align_down(mmap_ptr + mmap_size, mapping_align) - 1);
Evgenii Stepanove0848bb2020-07-14 16:44:57 -0700648 if (kGapAlignment && first_byte / kGapAlignment != last_byte / kGapAlignment) {
649 // This library crosses a 2MB boundary and will fragment a new huge page.
650 // Lets take advantage of that and insert a random number of inaccessible huge pages before that
651 // to improve address randomization and make it harder to locate this library code by probing.
652 munmap(mmap_ptr, mmap_size);
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700653 mapping_align = std::max(mapping_align, kGapAlignment);
Evgenii Stepanove0848bb2020-07-14 16:44:57 -0700654 gap_size =
655 kGapAlignment * (is_first_stage_init() ? 1 : arc4random_uniform(kMaxGapUnits - 1) + 1);
Peter Collingbournebb11ee62022-05-02 12:26:16 -0700656 mmap_size = align_up(size + gap_size, mapping_align) + mapping_align - page_size();
Evgenii Stepanove0848bb2020-07-14 16:44:57 -0700657 mmap_ptr = reinterpret_cast<uint8_t*>(mmap(nullptr, mmap_size, PROT_NONE, mmap_flags, -1, 0));
658 if (mmap_ptr == MAP_FAILED) {
659 return nullptr;
660 }
661 }
662
663 uint8_t *gap_end, *gap_start;
664 if (gap_size) {
665 gap_end = align_down(mmap_ptr + mmap_size, kGapAlignment);
666 gap_start = gap_end - gap_size;
667 } else {
668 gap_start = gap_end = mmap_ptr + mmap_size;
669 }
Evgenii Stepanovd13e9a62016-07-15 16:31:42 -0700670
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700671 uint8_t* first = align_up(mmap_ptr, mapping_align);
672 uint8_t* last = align_down(gap_start, mapping_align) - size;
Jiyong Park31cd08f2018-06-01 19:18:56 +0900673
Tom Cherry66bc4282018-11-08 13:40:52 -0800674 // arc4random* is not available in first stage init because /dev/urandom hasn't yet been
Jiyong Park31cd08f2018-06-01 19:18:56 +0900675 // created. Don't randomize then.
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700676 size_t n = is_first_stage_init() ? 0 : arc4random_uniform((last - first) / start_align + 1);
677 uint8_t* start = first + n * start_align;
Evgenii Stepanove0848bb2020-07-14 16:44:57 -0700678 // Unmap the extra space around the allocation.
679 // Keep it mapped PROT_NONE on 64-bit targets where address space is plentiful to make it harder
680 // to defeat ASLR by probing for readable memory mappings.
Evgenii Stepanovd13e9a62016-07-15 16:31:42 -0700681 munmap(mmap_ptr, start - mmap_ptr);
Evgenii Stepanove0848bb2020-07-14 16:44:57 -0700682 munmap(start + size, gap_start - (start + size));
683 if (gap_end != mmap_ptr + mmap_size) {
684 munmap(gap_end, mmap_ptr + mmap_size - gap_end);
685 }
686 *out_gap_start = gap_start;
687 *out_gap_size = gap_size;
Evgenii Stepanovd13e9a62016-07-15 16:31:42 -0700688 return start;
689}
690
Elliott Hughes650be4e2013-03-05 18:47:58 -0800691// Reserve a virtual address range big enough to hold all loadable
692// segments of a program header table. This is done by creating a
693// private anonymous mmap() with PROT_NONE.
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -0400694bool ElfReader::ReserveAddressSpace(address_space_params* address_space) {
Elliott Hughes0266ae52014-02-10 17:46:57 -0800695 ElfW(Addr) min_vaddr;
Brian Carlstrome7dffe12013-01-10 16:39:58 -0800696 load_size_ = phdr_table_get_load_size(phdr_table_, phdr_num_, &min_vaddr);
Elliott Hughes650be4e2013-03-05 18:47:58 -0800697 if (load_size_ == 0) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700698 DL_ERR("\"%s\" has no loadable segments", name_.c_str());
Elliott Hughes650be4e2013-03-05 18:47:58 -0800699 return false;
700 }
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200701
Kalesh Singhce1c3cf2024-09-30 13:26:23 -0700702 if (should_use_16kib_app_compat_) {
703 // Reserve additional space for aligning the permission boundary in compat loading
704 // Up to kPageSize-kCompatPageSize additional space is needed, but reservation
705 // is done with mmap which gives kPageSize multiple-sized reservations.
706 load_size_ += kPageSize;
707 }
708
Brian Carlstrome7dffe12013-01-10 16:39:58 -0800709 uint8_t* addr = reinterpret_cast<uint8_t*>(min_vaddr);
Torne (Richard Coles)12bbb912014-02-06 14:34:21 +0000710 void* start;
Torne (Richard Coles)12bbb912014-02-06 14:34:21 +0000711
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -0400712 if (load_size_ > address_space->reserved_size) {
713 if (address_space->must_use_address) {
Torne (Richard Coles)12bbb912014-02-06 14:34:21 +0000714 DL_ERR("reserved address space %zd smaller than %zd bytes needed for \"%s\"",
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -0400715 load_size_ - address_space->reserved_size, load_size_, name_.c_str());
Torne (Richard Coles)12bbb912014-02-06 14:34:21 +0000716 return false;
717 }
Peter Collingbournebb11ee62022-05-02 12:26:16 -0700718 size_t start_alignment = page_size();
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700719 if (get_transparent_hugepages_supported() && get_application_target_sdk_version() >= 31) {
720 size_t maximum_alignment = phdr_table_get_maximum_alignment(phdr_table_, phdr_num_);
721 // Limit alignment to PMD size as other alignments reduce the number of
722 // bits available for ASLR for no benefit.
Peter Collingbournebb11ee62022-05-02 12:26:16 -0700723 start_alignment = maximum_alignment == kPmdSize ? kPmdSize : page_size();
Collin Fijalkovich47d27aa2021-03-24 10:17:39 -0700724 }
725 start = ReserveWithAlignmentPadding(load_size_, kLibraryAlignment, start_alignment, &gap_start_,
726 &gap_size_);
Evgenii Stepanovd13e9a62016-07-15 16:31:42 -0700727 if (start == nullptr) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -0700728 DL_ERR("couldn't reserve %zd bytes of address space for \"%s\"", load_size_, name_.c_str());
Torne (Richard Coles)12bbb912014-02-06 14:34:21 +0000729 return false;
730 }
731 } else {
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -0400732 start = address_space->start_addr;
Evgenii Stepanove0848bb2020-07-14 16:44:57 -0700733 gap_start_ = nullptr;
734 gap_size_ = 0;
Dimitry Ivanovf45b0e92016-01-15 11:13:35 -0800735 mapped_by_caller_ = true;
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -0400736
737 // Update the reserved address space to subtract the space used by this library.
738 address_space->start_addr = reinterpret_cast<uint8_t*>(address_space->start_addr) + load_size_;
739 address_space->reserved_size -= load_size_;
Elliott Hughes650be4e2013-03-05 18:47:58 -0800740 }
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200741
Elliott Hughes650be4e2013-03-05 18:47:58 -0800742 load_start_ = start;
Brian Carlstrome7dffe12013-01-10 16:39:58 -0800743 load_bias_ = reinterpret_cast<uint8_t*>(start) - addr;
Kalesh Singhce1c3cf2024-09-30 13:26:23 -0700744
745 if (should_use_16kib_app_compat_) {
746 // In compat mode make the initial mapping RW since the ELF contents will be read
747 // into it; instead of mapped over it.
748 mprotect(reinterpret_cast<void*>(start), load_size_, PROT_READ | PROT_WRITE);
749 }
750
Elliott Hughes650be4e2013-03-05 18:47:58 -0800751 return true;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +0200752}
753
Kalesh Singhc5c1d192024-04-09 16:27:56 -0700754/*
755 * Returns true if the kernel supports page size migration, else false.
756 */
757bool page_size_migration_supported() {
758 static bool pgsize_migration_enabled = []() {
759 std::string enabled;
760 if (!android::base::ReadFileToString("/sys/kernel/mm/pgsize_migration/enabled", &enabled)) {
761 return false;
762 }
763 return enabled.find("1") != std::string::npos;
764 }();
765 return pgsize_migration_enabled;
766}
767
Kalesh Singh377f0b92024-01-31 20:23:39 -0800768// Find the ELF note of type NT_ANDROID_TYPE_PAD_SEGMENT and check that the desc value is 1.
769bool ElfReader::ReadPadSegmentNote() {
Kalesh Singhc5c1d192024-04-09 16:27:56 -0700770 if (!page_size_migration_supported()) {
771 // Don't attempt to read the note, since segment extension isn't
772 // supported; but return true so that loading can continue normally.
773 return true;
774 }
775
Kalesh Singh377f0b92024-01-31 20:23:39 -0800776 // The ELF can have multiple PT_NOTE's, check them all
777 for (size_t i = 0; i < phdr_num_; ++i) {
778 const ElfW(Phdr)* phdr = &phdr_table_[i];
779
780 if (phdr->p_type != PT_NOTE) {
781 continue;
782 }
783
Kalesh Singh13fb3cf2024-02-08 14:58:04 -0800784 // Some obfuscated ELFs may contain "empty" PT_NOTE program headers that don't
785 // point to any part of the ELF (p_memsz == 0). Skip these since there is
786 // nothing to decode. See: b/324468126
787 if (phdr->p_memsz == 0) {
788 continue;
789 }
790
Kalesh Singh751bb8a2024-03-29 17:55:37 -0700791 // If the PT_NOTE extends beyond the file. The ELF is doing something
792 // strange -- obfuscation, embedding hidden loaders, ...
793 //
794 // It doesn't contain the pad_segment note. Skip it to avoid SIGBUS
795 // by accesses beyond the file.
796 off64_t note_end_off = file_offset_ + phdr->p_offset + phdr->p_filesz;
797 if (note_end_off > file_size_) {
798 continue;
799 }
800
Kalesh Singh377f0b92024-01-31 20:23:39 -0800801 // note_fragment is scoped to within the loop so that there is
802 // at most 1 PT_NOTE mapped at anytime during this search.
803 MappedFileFragment note_fragment;
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000804 if (!note_fragment.Map(fd_, file_offset_, phdr->p_offset, phdr->p_memsz)) {
Kalesh Singh32b6d8c2024-02-13 18:37:12 -0800805 DL_ERR("\"%s\": PT_NOTE mmap(nullptr, %p, PROT_READ, MAP_PRIVATE, %d, %p) failed: %m",
806 name_.c_str(), reinterpret_cast<void*>(phdr->p_memsz), fd_,
807 reinterpret_cast<void*>(page_start(file_offset_ + phdr->p_offset)));
Kalesh Singh13fb3cf2024-02-08 14:58:04 -0800808 return false;
Kalesh Singh377f0b92024-01-31 20:23:39 -0800809 }
810
811 const ElfW(Nhdr)* note_hdr = nullptr;
812 const char* note_desc = nullptr;
813 if (!__get_elf_note(NT_ANDROID_TYPE_PAD_SEGMENT, "Android",
Suren Baghdasaryanc16828b2024-08-01 00:14:15 +0000814 reinterpret_cast<ElfW(Addr)>(note_fragment.data()),
Kalesh Singh377f0b92024-01-31 20:23:39 -0800815 phdr, &note_hdr, &note_desc)) {
816 continue;
817 }
818
819 if (note_hdr->n_descsz != sizeof(ElfW(Word))) {
820 DL_ERR("\"%s\" NT_ANDROID_TYPE_PAD_SEGMENT note has unexpected n_descsz: %u",
821 name_.c_str(), reinterpret_cast<unsigned int>(note_hdr->n_descsz));
822 return false;
823 }
824
825 // 1 == enabled, 0 == disabled
826 should_pad_segments_ = *reinterpret_cast<const ElfW(Word)*>(note_desc) == 1;
827 return true;
828 }
829
830 return true;
831}
832
Kalesh Singh4084b552024-03-13 13:35:49 -0700833static inline void _extend_load_segment_vma(const ElfW(Phdr)* phdr_table, size_t phdr_count,
Kalesh Singhb23787f2024-09-05 08:22:06 +0000834 size_t phdr_idx, ElfW(Addr)* p_memsz,
835 ElfW(Addr)* p_filesz, bool should_pad_segments,
836 bool should_use_16kib_app_compat) {
837 // NOTE: Segment extension is only applicable where the ELF's max-page-size > runtime page size;
838 // to save kernel VMA slab memory. 16KiB compat mode is the exact opposite scenario.
839 if (should_use_16kib_app_compat) {
840 return;
841 }
842
Kalesh Singh4084b552024-03-13 13:35:49 -0700843 const ElfW(Phdr)* phdr = &phdr_table[phdr_idx];
844 const ElfW(Phdr)* next = nullptr;
845 size_t next_idx = phdr_idx + 1;
846
Kalesh Singhe1e74792024-04-09 11:48:52 -0700847 // Don't do segment extension for p_align > 64KiB, such ELFs already existed in the
848 // field e.g. 2MiB p_align for THPs and are relatively small in number.
849 //
850 // The kernel can only represent padding for p_align up to 64KiB. This is because
851 // the kernel uses 4 available bits in the vm_area_struct to represent padding
852 // extent; and so cannot enable mitigations to avoid breaking app compatibility for
853 // p_aligns > 64KiB.
854 //
855 // Don't perform segment extension on these to avoid app compatibility issues.
856 if (phdr->p_align <= kPageSize || phdr->p_align > 64*1024 || !should_pad_segments) {
Kalesh Singh4084b552024-03-13 13:35:49 -0700857 return;
858 }
859
860 if (next_idx < phdr_count && phdr_table[next_idx].p_type == PT_LOAD) {
861 next = &phdr_table[next_idx];
862 }
863
864 // If this is the last LOAD segment, no extension is needed
865 if (!next || *p_memsz != *p_filesz) {
866 return;
867 }
868
869 ElfW(Addr) next_start = page_start(next->p_vaddr);
870 ElfW(Addr) curr_end = page_end(phdr->p_vaddr + *p_memsz);
871
872 // If adjacent segment mappings overlap, no extension is needed.
873 if (curr_end >= next_start) {
874 return;
875 }
876
877 // Extend the LOAD segment mapping to be contiguous with that of
878 // the next LOAD segment.
879 ElfW(Addr) extend = next_start - curr_end;
880 *p_memsz += extend;
881 *p_filesz += extend;
882}
883
Kalesh Singh86e04f62024-09-05 06:24:14 +0000884bool ElfReader::MapSegment(size_t seg_idx, size_t len) {
885 const ElfW(Phdr)* phdr = &phdr_table_[seg_idx];
886
887 void* start = reinterpret_cast<void*>(page_start(phdr->p_vaddr + load_bias_));
888
889 // The ELF could be being loaded directly from a zipped APK,
890 // the zip offset must be added to find the segment offset.
891 const ElfW(Addr) offset = file_offset_ + page_start(phdr->p_offset);
892
893 int prot = PFLAGS_TO_PROT(phdr->p_flags);
894
895 void* seg_addr = mmap64(start, len, prot, MAP_FIXED | MAP_PRIVATE, fd_, offset);
896
897 if (seg_addr == MAP_FAILED) {
898 DL_ERR("couldn't map \"%s\" segment %zd: %m", name_.c_str(), seg_idx);
899 return false;
900 }
901
902 // Mark segments as huge page eligible if they meet the requirements
903 if ((phdr->p_flags & PF_X) && phdr->p_align == kPmdSize &&
904 get_transparent_hugepages_supported()) {
905 madvise(seg_addr, len, MADV_HUGEPAGE);
906 }
907
908 return true;
909}
910
Kalesh Singh37bcaea2024-09-05 06:32:07 +0000911void ElfReader::ZeroFillSegment(const ElfW(Phdr)* phdr) {
Kalesh Singhb23787f2024-09-05 08:22:06 +0000912 // NOTE: In 16KiB app compat mode, the ELF mapping is anonymous, meaning that
913 // RW segments are COW-ed from the kernel's zero page. So there is no need to
914 // explicitly zero-fill until the last page's limit.
915 if (should_use_16kib_app_compat_) {
916 return;
917 }
918
Kalesh Singh37bcaea2024-09-05 06:32:07 +0000919 ElfW(Addr) seg_start = phdr->p_vaddr + load_bias_;
920 uint64_t unextended_seg_file_end = seg_start + phdr->p_filesz;
921
922 // If the segment is writable, and does not end on a page boundary,
923 // zero-fill it until the page limit.
924 //
925 // Do not attempt to zero the extended region past the first partial page,
926 // since doing so may:
927 // 1) Result in a SIGBUS, as the region is not backed by the underlying
928 // file.
929 // 2) Break the COW backing, faulting in new anon pages for a region
930 // that will not be used.
931 if ((phdr->p_flags & PF_W) != 0 && page_offset(unextended_seg_file_end) > 0) {
932 memset(reinterpret_cast<void*>(unextended_seg_file_end), 0,
933 kPageSize - page_offset(unextended_seg_file_end));
934 }
935}
936
Kalesh Singhe0f4a372024-09-05 07:07:21 +0000937void ElfReader::DropPaddingPages(const ElfW(Phdr)* phdr, uint64_t seg_file_end) {
Kalesh Singhb23787f2024-09-05 08:22:06 +0000938 // NOTE: Padding pages are only applicable where the ELF's max-page-size > runtime page size;
939 // 16KiB compat mode is the exact opposite scenario.
940 if (should_use_16kib_app_compat_) {
941 return;
942 }
943
Kalesh Singhe0f4a372024-09-05 07:07:21 +0000944 ElfW(Addr) seg_start = phdr->p_vaddr + load_bias_;
945 uint64_t unextended_seg_file_end = seg_start + phdr->p_filesz;
946
947 uint64_t pad_start = page_end(unextended_seg_file_end);
948 uint64_t pad_end = page_end(seg_file_end);
949 CHECK(pad_start <= pad_end);
950
951 uint64_t pad_len = pad_end - pad_start;
952 if (pad_len == 0 || !page_size_migration_supported()) {
953 return;
954 }
955
956 // Pages may be brought in due to readahead.
957 // Drop the padding (zero) pages, to avoid reclaim work later.
958 //
959 // NOTE: The madvise() here is special, as it also serves to hint to the
960 // kernel the portion of the LOAD segment that is padding.
961 //
962 // See: [1] https://android-review.googlesource.com/c/kernel/common/+/3032411
963 // [2] https://android-review.googlesource.com/c/kernel/common/+/3048835
964 if (madvise(reinterpret_cast<void*>(pad_start), pad_len, MADV_DONTNEED)) {
965 DL_WARN("\"%s\": madvise(0x%" PRIx64 ", 0x%" PRIx64 ", MADV_DONTNEED) failed: %m",
966 name_.c_str(), pad_start, pad_len);
967 }
968}
969
Kalesh Singh138a9552024-09-05 08:05:56 +0000970bool ElfReader::MapBssSection(const ElfW(Phdr)* phdr, ElfW(Addr) seg_page_end,
971 ElfW(Addr) seg_file_end) {
Kalesh Singhb23787f2024-09-05 08:22:06 +0000972 // NOTE: We do not need to handle .bss in 16KiB compat mode since the mapping
973 // reservation is anonymous and RW to begin with.
974 if (should_use_16kib_app_compat_) {
975 return true;
976 }
977
Kalesh Singh138a9552024-09-05 08:05:56 +0000978 // seg_file_end is now the first page address after the file content.
979 seg_file_end = page_end(seg_file_end);
980
981 if (seg_page_end <= seg_file_end) {
982 return true;
983 }
984
985 // If seg_page_end is larger than seg_file_end, we need to zero
986 // anything between them. This is done by using a private anonymous
987 // map for all extra pages
988 size_t zeromap_size = seg_page_end - seg_file_end;
989 void* zeromap =
990 mmap(reinterpret_cast<void*>(seg_file_end), zeromap_size, PFLAGS_TO_PROT(phdr->p_flags),
991 MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
992 if (zeromap == MAP_FAILED) {
993 DL_ERR("couldn't map .bss section for \"%s\": %m", name_.c_str());
994 return false;
995 }
996
997 // Set the VMA name using prctl
998 prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, zeromap, zeromap_size, ".bss");
999
1000 return true;
1001}
1002
Elliott Hughes650be4e2013-03-05 18:47:58 -08001003bool ElfReader::LoadSegments() {
Kalesh Singhce1c3cf2024-09-30 13:26:23 -07001004 // NOTE: The compat(legacy) page size (4096) must be used when aligning
1005 // the 4KiB segments for loading in compat mode. The larger 16KiB page size
1006 // will lead to overwriting adjacent segments since the ELF's segment(s)
1007 // are not 16KiB aligned.
1008 size_t seg_align = should_use_16kib_app_compat_ ? kCompatPageSize : kPageSize;
Kalesh Singhb23787f2024-09-05 08:22:06 +00001009
Steven Morelandfc89c8a2024-08-01 21:20:33 +00001010 size_t min_palign = phdr_table_get_minimum_alignment(phdr_table_, phdr_num_);
Kalesh Singhb23787f2024-09-05 08:22:06 +00001011 // Only enforce this on 16 KB systems with app compat disabled.
1012 // Apps may rely on undefined behavior here on 4 KB systems,
1013 // which is the norm before this change is introduced
1014 if (kPageSize >= 16384 && min_palign < kPageSize && !should_use_16kib_app_compat_) {
Steven Morelandfc89c8a2024-08-01 21:20:33 +00001015 DL_ERR("\"%s\" program alignment (%zu) cannot be smaller than system page size (%zu)",
1016 name_.c_str(), min_palign, kPageSize);
1017 return false;
1018 }
1019
Kalesh Singhce1c3cf2024-09-30 13:26:23 -07001020 if (!Setup16KiBAppCompat()) {
1021 DL_ERR("\"%s\" failed to setup 16KiB App Compat", name_.c_str());
1022 return false;
1023 }
1024
Elliott Hughes650be4e2013-03-05 18:47:58 -08001025 for (size_t i = 0; i < phdr_num_; ++i) {
Elliott Hughes0266ae52014-02-10 17:46:57 -08001026 const ElfW(Phdr)* phdr = &phdr_table_[i];
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001027
Elliott Hughes650be4e2013-03-05 18:47:58 -08001028 if (phdr->p_type != PT_LOAD) {
1029 continue;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001030 }
Elliott Hughes650be4e2013-03-05 18:47:58 -08001031
Kalesh Singh4084b552024-03-13 13:35:49 -07001032 ElfW(Addr) p_memsz = phdr->p_memsz;
1033 ElfW(Addr) p_filesz = phdr->p_filesz;
Kalesh Singhb23787f2024-09-05 08:22:06 +00001034 _extend_load_segment_vma(phdr_table_, phdr_num_, i, &p_memsz, &p_filesz, should_pad_segments_,
1035 should_use_16kib_app_compat_);
Kalesh Singh4084b552024-03-13 13:35:49 -07001036
Elliott Hughes650be4e2013-03-05 18:47:58 -08001037 // Segment addresses in memory.
Elliott Hughes0266ae52014-02-10 17:46:57 -08001038 ElfW(Addr) seg_start = phdr->p_vaddr + load_bias_;
Kalesh Singh4084b552024-03-13 13:35:49 -07001039 ElfW(Addr) seg_end = seg_start + p_memsz;
Elliott Hughes650be4e2013-03-05 18:47:58 -08001040
Kalesh Singhb23787f2024-09-05 08:22:06 +00001041 ElfW(Addr) seg_page_end = align_up(seg_end, seg_align);
Elliott Hughes650be4e2013-03-05 18:47:58 -08001042
Kalesh Singh4084b552024-03-13 13:35:49 -07001043 ElfW(Addr) seg_file_end = seg_start + p_filesz;
Elliott Hughes650be4e2013-03-05 18:47:58 -08001044
1045 // File offsets.
Elliott Hughes0266ae52014-02-10 17:46:57 -08001046 ElfW(Addr) file_start = phdr->p_offset;
Kalesh Singh4084b552024-03-13 13:35:49 -07001047 ElfW(Addr) file_end = file_start + p_filesz;
Elliott Hughes650be4e2013-03-05 18:47:58 -08001048
Kalesh Singhb23787f2024-09-05 08:22:06 +00001049 ElfW(Addr) file_page_start = align_down(file_start, seg_align);
Elliott Hughes0266ae52014-02-10 17:46:57 -08001050 ElfW(Addr) file_length = file_end - file_page_start;
Elliott Hughes650be4e2013-03-05 18:47:58 -08001051
Dmitriy Ivanov3f987f52015-06-25 15:51:41 -07001052 if (file_size_ <= 0) {
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -07001053 DL_ERR("\"%s\" invalid file size: %" PRId64, name_.c_str(), file_size_);
Dmitriy Ivanov3f987f52015-06-25 15:51:41 -07001054 return false;
1055 }
1056
Kalesh Singh4084b552024-03-13 13:35:49 -07001057 if (file_start + phdr->p_filesz > static_cast<size_t>(file_size_)) {
Dmitriy Ivanov3f987f52015-06-25 15:51:41 -07001058 DL_ERR("invalid ELF file \"%s\" load segment[%zd]:"
1059 " p_offset (%p) + p_filesz (%p) ( = %p) past end of file (0x%" PRIx64 ")",
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -07001060 name_.c_str(), i, reinterpret_cast<void*>(phdr->p_offset),
Dmitriy Ivanov3f987f52015-06-25 15:51:41 -07001061 reinterpret_cast<void*>(phdr->p_filesz),
Kalesh Singh4084b552024-03-13 13:35:49 -07001062 reinterpret_cast<void*>(file_start + phdr->p_filesz), file_size_);
Dmitriy Ivanov3f987f52015-06-25 15:51:41 -07001063 return false;
1064 }
1065
Brian Carlstrom82dcc792013-05-21 16:49:24 -07001066 if (file_length != 0) {
Dimitry Ivanov9700bab2016-08-10 18:54:06 -07001067 int prot = PFLAGS_TO_PROT(phdr->p_flags);
Dimitry Ivanov9700bab2016-08-10 18:54:06 -07001068 if ((prot & (PROT_EXEC | PROT_WRITE)) == (PROT_EXEC | PROT_WRITE)) {
Elliott Hughes4cc5a602016-11-15 16:54:16 -08001069 // W + E PT_LOAD segments are not allowed in O.
Elliott Hughes95c6cd72019-12-20 13:26:14 -08001070 if (get_application_target_sdk_version() >= 26) {
Elliott Hughes9076b0c2018-02-28 11:29:45 -08001071 DL_ERR_AND_LOG("\"%s\": W+E load segments are not allowed", name_.c_str());
Elliott Hughes4cc5a602016-11-15 16:54:16 -08001072 return false;
1073 }
Elliott Hughes95c6cd72019-12-20 13:26:14 -08001074 DL_WARN_documented_change(26,
Elliott Hughes9076b0c2018-02-28 11:29:45 -08001075 "writable-and-executable-segments-enforced-for-api-level-26",
1076 "\"%s\" has load segments that are both writable and executable",
1077 name_.c_str());
Elliott Hughes4cc5a602016-11-15 16:54:16 -08001078 add_dlwarning(name_.c_str(), "W+E load segments");
Dimitry Ivanov9700bab2016-08-10 18:54:06 -07001079 }
1080
Kalesh Singh86e04f62024-09-05 06:24:14 +00001081 // Pass the file_length, since it may have been extended by _extend_load_segment_vma().
Kalesh Singhce1c3cf2024-09-30 13:26:23 -07001082 if (should_use_16kib_app_compat_) {
1083 if (!CompatMapSegment(i, file_length)) {
1084 return false;
1085 }
1086 } else {
1087 if (!MapSegment(i, file_length)) {
1088 return false;
1089 }
Brian Carlstrom82dcc792013-05-21 16:49:24 -07001090 }
Elliott Hughes650be4e2013-03-05 18:47:58 -08001091 }
1092
Kalesh Singh37bcaea2024-09-05 06:32:07 +00001093 ZeroFillSegment(phdr);
Kalesh Singh1d3ba112024-03-06 17:33:36 -08001094
Kalesh Singhe0f4a372024-09-05 07:07:21 +00001095 DropPaddingPages(phdr, seg_file_end);
Elliott Hughes650be4e2013-03-05 18:47:58 -08001096
Kalesh Singh138a9552024-09-05 08:05:56 +00001097 if (!MapBssSection(phdr, seg_page_end, seg_file_end)) {
1098 return false;
Elliott Hughes650be4e2013-03-05 18:47:58 -08001099 }
1100 }
1101 return true;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001102}
1103
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001104/* Used internally. Used to set the protection bits of all loaded segments
1105 * with optional extra flags (i.e. really PROT_WRITE). Used by
1106 * phdr_table_protect_segments and phdr_table_unprotect_segments.
1107 */
1108static int _phdr_table_set_load_prot(const ElfW(Phdr)* phdr_table, size_t phdr_count,
Kalesh Singh4084b552024-03-13 13:35:49 -07001109 ElfW(Addr) load_bias, int extra_prot_flags,
Kalesh Singhb23787f2024-09-05 08:22:06 +00001110 bool should_pad_segments, bool should_use_16kib_app_compat) {
Kalesh Singh4084b552024-03-13 13:35:49 -07001111 for (size_t i = 0; i < phdr_count; ++i) {
1112 const ElfW(Phdr)* phdr = &phdr_table[i];
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001113
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001114 if (phdr->p_type != PT_LOAD || (phdr->p_flags & PF_W) != 0) {
1115 continue;
1116 }
1117
Kalesh Singh4084b552024-03-13 13:35:49 -07001118 ElfW(Addr) p_memsz = phdr->p_memsz;
1119 ElfW(Addr) p_filesz = phdr->p_filesz;
Kalesh Singhb23787f2024-09-05 08:22:06 +00001120 _extend_load_segment_vma(phdr_table, phdr_count, i, &p_memsz, &p_filesz, should_pad_segments,
1121 should_use_16kib_app_compat);
Kalesh Singh4084b552024-03-13 13:35:49 -07001122
1123 ElfW(Addr) seg_page_start = page_start(phdr->p_vaddr + load_bias);
1124 ElfW(Addr) seg_page_end = page_end(phdr->p_vaddr + p_memsz + load_bias);
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001125
Tamas Petz8d55d182020-02-24 14:15:25 +01001126 int prot = PFLAGS_TO_PROT(phdr->p_flags) | extra_prot_flags;
1127 if ((prot & PROT_WRITE) != 0) {
Nick Kralevich8fdb3412015-04-01 16:57:50 -07001128 // make sure we're never simultaneously writable / executable
1129 prot &= ~PROT_EXEC;
1130 }
Tamas Petz8d55d182020-02-24 14:15:25 +01001131#if defined(__aarch64__)
1132 if ((prot & PROT_EXEC) == 0) {
1133 // Though it is not specified don't add PROT_BTI if segment is not
1134 // executable.
1135 prot &= ~PROT_BTI;
1136 }
1137#endif
Nick Kralevich8fdb3412015-04-01 16:57:50 -07001138
Tamas Petz8d55d182020-02-24 14:15:25 +01001139 int ret =
1140 mprotect(reinterpret_cast<void*>(seg_page_start), seg_page_end - seg_page_start, prot);
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001141 if (ret < 0) {
1142 return -1;
1143 }
1144 }
1145 return 0;
1146}
1147
1148/* Restore the original protection modes for all loadable segments.
1149 * You should only call this after phdr_table_unprotect_segments and
1150 * applying all relocations.
1151 *
Tamas Petz8d55d182020-02-24 14:15:25 +01001152 * AArch64: also called from linker_main and ElfReader::Load to apply
1153 * PROT_BTI for loaded main so and other so-s.
1154 *
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001155 * Input:
1156 * phdr_table -> program header table
1157 * phdr_count -> number of entries in tables
1158 * load_bias -> load bias
Kalesh Singh4084b552024-03-13 13:35:49 -07001159 * should_pad_segments -> Are segments extended to avoid gaps in the memory map
Kalesh Singhb23787f2024-09-05 08:22:06 +00001160 * should_use_16kib_app_compat -> Is the ELF being loaded in 16KiB app compat mode.
Tamas Petz8d55d182020-02-24 14:15:25 +01001161 * prop -> GnuPropertySection or nullptr
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001162 * Return:
Mitch Phillips117e45e2023-10-20 13:32:33 +00001163 * 0 on success, -1 on failure (error code in errno).
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001164 */
Tamas Petz8d55d182020-02-24 14:15:25 +01001165int phdr_table_protect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count,
Kalesh Singh4084b552024-03-13 13:35:49 -07001166 ElfW(Addr) load_bias, bool should_pad_segments,
Kalesh Singhb23787f2024-09-05 08:22:06 +00001167 bool should_use_16kib_app_compat,
Kalesh Singh4084b552024-03-13 13:35:49 -07001168 const GnuPropertySection* prop __unused) {
Tamas Petz8d55d182020-02-24 14:15:25 +01001169 int prot = 0;
1170#if defined(__aarch64__)
1171 if ((prop != nullptr) && prop->IsBTICompatible()) {
1172 prot |= PROT_BTI;
1173 }
1174#endif
Kalesh Singhb23787f2024-09-05 08:22:06 +00001175 return _phdr_table_set_load_prot(phdr_table, phdr_count, load_bias, prot, should_pad_segments,
1176 should_use_16kib_app_compat);
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001177}
1178
Florian Mayer4edc20d2024-10-30 14:24:26 -07001179static bool segment_needs_memtag_globals_remapping(const ElfW(Phdr) * phdr) {
1180 // For now, MTE globals is only supported on writeable data segments.
1181 return phdr->p_type == PT_LOAD && !(phdr->p_flags & PF_X) && (phdr->p_flags & PF_W);
1182}
1183
1184/* When MTE globals are requested by the binary, and when the hardware supports
1185 * it, remap the executable's PT_LOAD data pages to have PROT_MTE.
1186 *
1187 * Returns 0 on success, -1 on failure (error code in errno).
1188 */
1189int remap_memtag_globals_segments(const ElfW(Phdr) * phdr_table __unused,
1190 size_t phdr_count __unused, ElfW(Addr) load_bias __unused) {
1191#if defined(__aarch64__)
1192 for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_table + phdr_count; phdr++) {
1193 if (!segment_needs_memtag_globals_remapping(phdr)) {
1194 continue;
1195 }
1196
1197 uintptr_t seg_page_start = page_start(phdr->p_vaddr) + load_bias;
1198 uintptr_t seg_page_end = page_end(phdr->p_vaddr + phdr->p_memsz) + load_bias;
1199 size_t seg_page_aligned_size = seg_page_end - seg_page_start;
1200
1201 int prot = PFLAGS_TO_PROT(phdr->p_flags);
1202 // For anonymous private mappings, it may be possible to simply mprotect()
1203 // the PROT_MTE flag over the top. For file-based mappings, this will fail,
1204 // and we'll need to fall back. We also allow PROT_WRITE here to allow
1205 // writing memory tags (in `soinfo::tag_globals()`), and set these sections
1206 // back to read-only after tags are applied (similar to RELRO).
1207 prot |= PROT_MTE;
1208 if (mprotect(reinterpret_cast<void*>(seg_page_start), seg_page_aligned_size,
1209 prot | PROT_WRITE) == 0) {
1210 continue;
1211 }
1212
1213 void* mapping_copy = mmap(nullptr, seg_page_aligned_size, PROT_READ | PROT_WRITE,
1214 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
1215 linker_memcpy(mapping_copy, reinterpret_cast<void*>(seg_page_start), seg_page_aligned_size);
1216
1217 void* seg_addr = mmap(reinterpret_cast<void*>(seg_page_start), seg_page_aligned_size,
1218 prot | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
1219 if (seg_addr == MAP_FAILED) return -1;
1220
1221 linker_memcpy(seg_addr, mapping_copy, seg_page_aligned_size);
1222 munmap(mapping_copy, seg_page_aligned_size);
1223 }
1224#endif // defined(__aarch64__)
1225 return 0;
1226}
1227
1228void protect_memtag_globals_ro_segments(const ElfW(Phdr) * phdr_table __unused,
1229 size_t phdr_count __unused, ElfW(Addr) load_bias __unused) {
1230#if defined(__aarch64__)
1231 for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_table + phdr_count; phdr++) {
1232 int prot = PFLAGS_TO_PROT(phdr->p_flags);
1233 if (!segment_needs_memtag_globals_remapping(phdr) || (prot & PROT_WRITE)) {
1234 continue;
1235 }
1236
1237 prot |= PROT_MTE;
1238
1239 uintptr_t seg_page_start = page_start(phdr->p_vaddr) + load_bias;
1240 uintptr_t seg_page_end = page_end(phdr->p_vaddr + phdr->p_memsz) + load_bias;
1241 size_t seg_page_aligned_size = seg_page_end - seg_page_start;
1242 mprotect(reinterpret_cast<void*>(seg_page_start), seg_page_aligned_size, prot);
1243 }
1244#endif // defined(__aarch64__)
1245}
1246
1247void name_memtag_globals_segments(const ElfW(Phdr) * phdr_table, size_t phdr_count,
1248 ElfW(Addr) load_bias, const char* soname,
1249 std::list<std::string>* vma_names) {
1250 for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_table + phdr_count; phdr++) {
1251 if (!segment_needs_memtag_globals_remapping(phdr)) {
1252 continue;
1253 }
1254
1255 uintptr_t seg_page_start = page_start(phdr->p_vaddr) + load_bias;
1256 uintptr_t seg_page_end = page_end(phdr->p_vaddr + phdr->p_memsz) + load_bias;
1257 size_t seg_page_aligned_size = seg_page_end - seg_page_start;
1258
1259 // For file-based mappings that we're now forcing to be anonymous mappings, set the VMA name to
1260 // make debugging easier.
1261 // Once we are targeting only devices that run kernel 5.10 or newer (and thus include
1262 // https://android-review.git.corp.google.com/c/kernel/common/+/1934723 which causes the
1263 // VMA_ANON_NAME to be copied into the kernel), we can get rid of the storage here.
1264 // For now, that is not the case:
1265 // https://source.android.com/docs/core/architecture/kernel/android-common#compatibility-matrix
1266 constexpr int kVmaNameLimit = 80;
1267 std::string& vma_name = vma_names->emplace_back(kVmaNameLimit, '\0');
1268 int full_vma_length =
1269 async_safe_format_buffer(vma_name.data(), kVmaNameLimit, "mt:%s+%" PRIxPTR, soname,
1270 page_start(phdr->p_vaddr)) +
1271 /* include the null terminator */ 1;
1272 // There's an upper limit of 80 characters, including the null terminator, in the anonymous VMA
1273 // name. If we run over that limit, we end up truncating the segment offset and parts of the
1274 // DSO's name, starting on the right hand side of the basename. Because the basename is the most
1275 // important thing, chop off the soname from the left hand side first.
1276 //
1277 // Example (with '#' as the null terminator):
1278 // - "mt:/data/nativetest64/bionic-unit-tests/bionic-loader-test-libs/libdlext_test.so+e000#"
1279 // is a `full_vma_length` == 86.
1280 //
1281 // We need to left-truncate (86 - 80) 6 characters from the soname, plus the
1282 // `vma_truncation_prefix`, so 9 characters total.
1283 if (full_vma_length > kVmaNameLimit) {
1284 const char vma_truncation_prefix[] = "...";
1285 int soname_truncated_bytes =
1286 full_vma_length - kVmaNameLimit + sizeof(vma_truncation_prefix) - 1;
1287 async_safe_format_buffer(vma_name.data(), kVmaNameLimit, "mt:%s%s+%" PRIxPTR,
1288 vma_truncation_prefix, soname + soname_truncated_bytes,
1289 page_start(phdr->p_vaddr));
1290 }
1291 if (prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, reinterpret_cast<void*>(seg_page_start),
1292 seg_page_aligned_size, vma_name.data()) != 0) {
1293 DL_WARN("Failed to rename memtag global segment: %m");
1294 }
1295 }
1296}
1297
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001298/* Change the protection of all loaded segments in memory to writable.
1299 * This is useful before performing relocations. Once completed, you
1300 * will have to call phdr_table_protect_segments to restore the original
1301 * protection flags on all segments.
1302 *
1303 * Note that some writable segments can also have their content turned
1304 * to read-only by calling phdr_table_protect_gnu_relro. This is no
1305 * performed here.
1306 *
1307 * Input:
1308 * phdr_table -> program header table
1309 * phdr_count -> number of entries in tables
1310 * load_bias -> load bias
Kalesh Singh4084b552024-03-13 13:35:49 -07001311 * should_pad_segments -> Are segments extended to avoid gaps in the memory map
Kalesh Singhb23787f2024-09-05 08:22:06 +00001312 * should_use_16kib_app_compat -> Is the ELF being loaded in 16KiB app compat mode.
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001313 * Return:
Mitch Phillips117e45e2023-10-20 13:32:33 +00001314 * 0 on success, -1 on failure (error code in errno).
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001315 */
Kalesh Singhb23787f2024-09-05 08:22:06 +00001316int phdr_table_unprotect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count,
1317 ElfW(Addr) load_bias, bool should_pad_segments,
1318 bool should_use_16kib_app_compat) {
Kalesh Singh4084b552024-03-13 13:35:49 -07001319 return _phdr_table_set_load_prot(phdr_table, phdr_count, load_bias, PROT_WRITE,
Kalesh Singhb23787f2024-09-05 08:22:06 +00001320 should_pad_segments, should_use_16kib_app_compat);
Dimitry Ivanov56be6ed2015-04-01 21:18:48 +00001321}
1322
Kalesh Singh702d9b02024-03-13 13:38:04 -07001323static inline void _extend_gnu_relro_prot_end(const ElfW(Phdr)* relro_phdr,
1324 const ElfW(Phdr)* phdr_table, size_t phdr_count,
1325 ElfW(Addr) load_bias, ElfW(Addr)* seg_page_end,
Kalesh Singhb23787f2024-09-05 08:22:06 +00001326 bool should_pad_segments,
1327 bool should_use_16kib_app_compat) {
Kalesh Singh702d9b02024-03-13 13:38:04 -07001328 // Find the index and phdr of the LOAD containing the GNU_RELRO segment
1329 for (size_t index = 0; index < phdr_count; ++index) {
1330 const ElfW(Phdr)* phdr = &phdr_table[index];
1331
1332 if (phdr->p_type == PT_LOAD && phdr->p_vaddr == relro_phdr->p_vaddr) {
1333 // If the PT_GNU_RELRO mem size is not at least as large as the corresponding
1334 // LOAD segment mem size, we need to protect only a partial region of the
1335 // LOAD segment and therefore cannot avoid a VMA split.
1336 //
1337 // Note: Don't check the page-aligned mem sizes since the extended protection
1338 // may incorrectly write protect non-relocation data.
1339 //
1340 // Example:
1341 //
1342 // |---- 3K ----|-- 1K --|---- 3K ---- |-- 1K --|
1343 // ----------------------------------------------------------------
1344 // | | | | |
1345 // SEG X | RO | RO | RW | | SEG Y
1346 // | | | | |
1347 // ----------------------------------------------------------------
1348 // | | |
1349 // | | |
1350 // | | |
1351 // relro_vaddr relro_vaddr relro_vaddr
1352 // (load_vaddr) + +
1353 // relro_memsz load_memsz
1354 //
1355 // ----------------------------------------------------------------
1356 // | PAGE | PAGE |
1357 // ----------------------------------------------------------------
1358 // | Potential |
1359 // |----- Extended RO ----|
1360 // | Protection |
1361 //
1362 // If the check below uses page aligned mem sizes it will cause incorrect write
1363 // protection of the 3K RW part of the LOAD segment containing the GNU_RELRO.
1364 if (relro_phdr->p_memsz < phdr->p_memsz) {
1365 return;
1366 }
1367
1368 ElfW(Addr) p_memsz = phdr->p_memsz;
1369 ElfW(Addr) p_filesz = phdr->p_filesz;
1370
1371 // Attempt extending the VMA (mprotect range). Without extending the range,
1372 // mprotect will only RO protect a part of the extended RW LOAD segment, which
1373 // will leave an extra split RW VMA (the gap).
1374 _extend_load_segment_vma(phdr_table, phdr_count, index, &p_memsz, &p_filesz,
Kalesh Singhb23787f2024-09-05 08:22:06 +00001375 should_pad_segments, should_use_16kib_app_compat);
Kalesh Singh702d9b02024-03-13 13:38:04 -07001376
1377 *seg_page_end = page_end(phdr->p_vaddr + p_memsz + load_bias);
1378 return;
1379 }
1380 }
1381}
1382
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001383/* Used internally by phdr_table_protect_gnu_relro and
1384 * phdr_table_unprotect_gnu_relro.
1385 */
Elliott Hughes0266ae52014-02-10 17:46:57 -08001386static int _phdr_table_set_gnu_relro_prot(const ElfW(Phdr)* phdr_table, size_t phdr_count,
Kalesh Singh702d9b02024-03-13 13:38:04 -07001387 ElfW(Addr) load_bias, int prot_flags,
Kalesh Singhb23787f2024-09-05 08:22:06 +00001388 bool should_pad_segments,
1389 bool should_use_16kib_app_compat) {
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,
Kalesh Singhb23787f2024-09-05 08:22:06 +00001417 should_pad_segments, should_use_16kib_app_compat);
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,
Kalesh Singhb23787f2024-09-05 08:22:06 +00001448 ElfW(Addr) load_bias, bool should_pad_segments,
1449 bool should_use_16kib_app_compat) {
Kalesh Singh702d9b02024-03-13 13:38:04 -07001450 return _phdr_table_set_gnu_relro_prot(phdr_table, phdr_count, load_bias, PROT_READ,
Kalesh Singhb23787f2024-09-05 08:22:06 +00001451 should_pad_segments, should_use_16kib_app_compat);
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001452}
1453
Kalesh Singhce1c3cf2024-09-30 13:26:23 -07001454/*
1455 * Apply RX protection to the compat relro region of the ELF being loaded in
1456 * 16KiB compat mode.
1457 *
1458 * Input:
1459 * start -> start address of the compat relro region.
1460 * size -> size of the compat relro region in bytes.
1461 * Return:
1462 * 0 on success, -1 on failure (error code in errno).
1463 */
1464int phdr_table_protect_gnu_relro_16kib_compat(ElfW(Addr) start, ElfW(Addr) size) {
1465 return mprotect(reinterpret_cast<void*>(start), size, PROT_READ | PROT_EXEC);
1466}
1467
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001468/* Serialize the GNU relro segments to the given file descriptor. This can be
1469 * performed after relocations to allow another process to later share the
1470 * relocated segment, if it was loaded at the same address.
1471 *
1472 * Input:
1473 * phdr_table -> program header table
1474 * phdr_count -> number of entries in tables
1475 * load_bias -> load bias
1476 * fd -> writable file descriptor to use
Torne (Richard Coles)fa9f7f22019-04-02 17:04:42 -04001477 * file_offset -> pointer to offset into file descriptor to use/update
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001478 * Return:
Mitch Phillips117e45e2023-10-20 13:32:33 +00001479 * 0 on success, -1 on failure (error code in errno).
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001480 */
Dmitriy Ivanov20d89cb2015-03-30 18:43:38 -07001481int phdr_table_serialize_gnu_relro(const ElfW(Phdr)* phdr_table,
1482 size_t phdr_count,
1483 ElfW(Addr) load_bias,
Torne (Richard Coles)fa9f7f22019-04-02 17:04:42 -04001484 int fd,
1485 size_t* file_offset) {
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001486 const ElfW(Phdr)* phdr = phdr_table;
1487 const ElfW(Phdr)* phdr_limit = phdr + phdr_count;
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001488
1489 for (phdr = phdr_table; phdr < phdr_limit; phdr++) {
1490 if (phdr->p_type != PT_GNU_RELRO) {
1491 continue;
1492 }
1493
Peter Collingbournebb11ee62022-05-02 12:26:16 -07001494 ElfW(Addr) seg_page_start = page_start(phdr->p_vaddr) + load_bias;
1495 ElfW(Addr) seg_page_end = page_end(phdr->p_vaddr + phdr->p_memsz) + load_bias;
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001496 ssize_t size = seg_page_end - seg_page_start;
1497
1498 ssize_t written = TEMP_FAILURE_RETRY(write(fd, reinterpret_cast<void*>(seg_page_start), size));
1499 if (written != size) {
1500 return -1;
1501 }
1502 void* map = mmap(reinterpret_cast<void*>(seg_page_start), size, PROT_READ,
Torne (Richard Coles)fa9f7f22019-04-02 17:04:42 -04001503 MAP_PRIVATE|MAP_FIXED, fd, *file_offset);
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001504 if (map == MAP_FAILED) {
1505 return -1;
1506 }
Torne (Richard Coles)fa9f7f22019-04-02 17:04:42 -04001507 *file_offset += size;
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001508 }
1509 return 0;
1510}
1511
1512/* Where possible, replace the GNU relro segments with mappings of the given
1513 * file descriptor. This can be performed after relocations to allow a file
1514 * previously created by phdr_table_serialize_gnu_relro in another process to
1515 * replace the dirty relocated pages, saving memory, if it was loaded at the
1516 * same address. We have to compare the data before we map over it, since some
1517 * parts of the relro segment may not be identical due to other libraries in
1518 * the process being loaded at different addresses.
1519 *
1520 * Input:
1521 * phdr_table -> program header table
1522 * phdr_count -> number of entries in tables
1523 * load_bias -> load bias
1524 * fd -> readable file descriptor to use
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -04001525 * file_offset -> pointer to offset into file descriptor to use/update
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001526 * Return:
Mitch Phillips117e45e2023-10-20 13:32:33 +00001527 * 0 on success, -1 on failure (error code in errno).
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001528 */
Dmitriy Ivanov20d89cb2015-03-30 18:43:38 -07001529int phdr_table_map_gnu_relro(const ElfW(Phdr)* phdr_table,
1530 size_t phdr_count,
1531 ElfW(Addr) load_bias,
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -04001532 int fd,
1533 size_t* file_offset) {
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001534 // Map the file at a temporary location so we can compare its contents.
1535 struct stat file_stat;
1536 if (TEMP_FAILURE_RETRY(fstat(fd, &file_stat)) != 0) {
1537 return -1;
1538 }
1539 off_t file_size = file_stat.st_size;
Dmitriy Ivanov851135b2014-08-29 12:02:36 -07001540 void* temp_mapping = nullptr;
Torne (Richard Coles)26ec9672014-04-30 15:48:40 +01001541 if (file_size > 0) {
Dmitriy Ivanov851135b2014-08-29 12:02:36 -07001542 temp_mapping = mmap(nullptr, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
Torne (Richard Coles)26ec9672014-04-30 15:48:40 +01001543 if (temp_mapping == MAP_FAILED) {
1544 return -1;
1545 }
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001546 }
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001547
1548 // Iterate over the relro segments and compare/remap the pages.
1549 const ElfW(Phdr)* phdr = phdr_table;
1550 const ElfW(Phdr)* phdr_limit = phdr + phdr_count;
1551
1552 for (phdr = phdr_table; phdr < phdr_limit; phdr++) {
1553 if (phdr->p_type != PT_GNU_RELRO) {
1554 continue;
1555 }
1556
Peter Collingbournebb11ee62022-05-02 12:26:16 -07001557 ElfW(Addr) seg_page_start = page_start(phdr->p_vaddr) + load_bias;
1558 ElfW(Addr) seg_page_end = page_end(phdr->p_vaddr + phdr->p_memsz) + load_bias;
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001559
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -04001560 char* file_base = static_cast<char*>(temp_mapping) + *file_offset;
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001561 char* mem_base = reinterpret_cast<char*>(seg_page_start);
1562 size_t match_offset = 0;
1563 size_t size = seg_page_end - seg_page_start;
1564
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -04001565 if (file_size - *file_offset < size) {
Torne (Richard Coles)26ec9672014-04-30 15:48:40 +01001566 // File is too short to compare to this segment. The contents are likely
1567 // different as well (it's probably for a different library version) so
1568 // just don't bother checking.
1569 break;
1570 }
1571
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001572 while (match_offset < size) {
1573 // Skip over dissimilar pages.
1574 while (match_offset < size &&
Peter Collingbournebb11ee62022-05-02 12:26:16 -07001575 memcmp(mem_base + match_offset, file_base + match_offset, page_size()) != 0) {
1576 match_offset += page_size();
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001577 }
1578
1579 // Count similar pages.
1580 size_t mismatch_offset = match_offset;
1581 while (mismatch_offset < size &&
Peter Collingbournebb11ee62022-05-02 12:26:16 -07001582 memcmp(mem_base + mismatch_offset, file_base + mismatch_offset, page_size()) == 0) {
1583 mismatch_offset += page_size();
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001584 }
1585
1586 // Map over similar pages.
1587 if (mismatch_offset > match_offset) {
1588 void* map = mmap(mem_base + match_offset, mismatch_offset - match_offset,
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -04001589 PROT_READ, MAP_PRIVATE|MAP_FIXED, fd, *file_offset + match_offset);
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001590 if (map == MAP_FAILED) {
1591 munmap(temp_mapping, file_size);
1592 return -1;
1593 }
1594 }
1595
1596 match_offset = mismatch_offset;
1597 }
1598
1599 // Add to the base file offset in case there are multiple relro segments.
Torne (Richard Coles)efbe9a52018-10-17 15:59:38 -04001600 *file_offset += size;
Torne (Richard Coles)183ad9d2014-02-27 13:18:00 +00001601 }
1602 munmap(temp_mapping, file_size);
1603 return 0;
1604}
1605
1606
Elliott Hughes4eeb1f12013-10-25 17:38:02 -07001607#if defined(__arm__)
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001608/* Return the address and size of the .ARM.exidx section in memory,
1609 * if present.
1610 *
1611 * Input:
1612 * phdr_table -> program header table
Elliott Hughes105bc262012-08-15 16:56:00 -07001613 * phdr_count -> number of entries in tables
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001614 * load_bias -> load bias
1615 * Output:
Dmitriy Ivanov851135b2014-08-29 12:02:36 -07001616 * arm_exidx -> address of table in memory (null on failure).
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001617 * arm_exidx_count -> number of items in table (0 on failure).
1618 * Return:
Mitch Phillips117e45e2023-10-20 13:32:33 +00001619 * 0 on success, -1 on failure (_no_ error code in errno)
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001620 */
Elliott Hughes0266ae52014-02-10 17:46:57 -08001621int phdr_table_get_arm_exidx(const ElfW(Phdr)* phdr_table, size_t phdr_count,
1622 ElfW(Addr) load_bias,
Dmitriy Ivanov1649e7e2015-01-22 16:04:25 -08001623 ElfW(Addr)** arm_exidx, size_t* arm_exidx_count) {
Elliott Hughes0266ae52014-02-10 17:46:57 -08001624 const ElfW(Phdr)* phdr = phdr_table;
1625 const ElfW(Phdr)* phdr_limit = phdr + phdr_count;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001626
Elliott Hughes0266ae52014-02-10 17:46:57 -08001627 for (phdr = phdr_table; phdr < phdr_limit; phdr++) {
1628 if (phdr->p_type != PT_ARM_EXIDX) {
1629 continue;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001630 }
Elliott Hughes0266ae52014-02-10 17:46:57 -08001631
1632 *arm_exidx = reinterpret_cast<ElfW(Addr)*>(load_bias + phdr->p_vaddr);
Dmitriy Ivanov1649e7e2015-01-22 16:04:25 -08001633 *arm_exidx_count = phdr->p_memsz / 8;
Elliott Hughes0266ae52014-02-10 17:46:57 -08001634 return 0;
1635 }
Dmitriy Ivanov851135b2014-08-29 12:02:36 -07001636 *arm_exidx = nullptr;
Elliott Hughes0266ae52014-02-10 17:46:57 -08001637 *arm_exidx_count = 0;
1638 return -1;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001639}
Elliott Hughes4eeb1f12013-10-25 17:38:02 -07001640#endif
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001641
Ard Biesheuvel12c78bb2012-08-14 12:30:09 +02001642/* Return the address and size of the ELF file's .dynamic section in memory,
Dmitriy Ivanov851135b2014-08-29 12:02:36 -07001643 * or null if missing.
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001644 *
1645 * Input:
1646 * phdr_table -> program header table
Elliott Hughes105bc262012-08-15 16:56:00 -07001647 * phdr_count -> number of entries in tables
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001648 * load_bias -> load bias
Ard Biesheuvel12c78bb2012-08-14 12:30:09 +02001649 * Output:
Dmitriy Ivanov851135b2014-08-29 12:02:36 -07001650 * dynamic -> address of table in memory (null on failure).
Ningsheng Jiane93be992014-09-16 15:22:10 +08001651 * dynamic_flags -> protection flags for section (unset on failure)
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001652 * Return:
Ard Biesheuvel12c78bb2012-08-14 12:30:09 +02001653 * void
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001654 */
Elliott Hughes0266ae52014-02-10 17:46:57 -08001655void phdr_table_get_dynamic_section(const ElfW(Phdr)* phdr_table, size_t phdr_count,
Ningsheng Jiane93be992014-09-16 15:22:10 +08001656 ElfW(Addr) load_bias, ElfW(Dyn)** dynamic,
1657 ElfW(Word)* dynamic_flags) {
Dmitriy Ivanov498eb182014-09-05 14:57:59 -07001658 *dynamic = nullptr;
Dmitriy Ivanov20d89cb2015-03-30 18:43:38 -07001659 for (size_t i = 0; i<phdr_count; ++i) {
1660 const ElfW(Phdr)& phdr = phdr_table[i];
1661 if (phdr.p_type == PT_DYNAMIC) {
1662 *dynamic = reinterpret_cast<ElfW(Dyn)*>(load_bias + phdr.p_vaddr);
Ningsheng Jiane93be992014-09-16 15:22:10 +08001663 if (dynamic_flags) {
Dmitriy Ivanov20d89cb2015-03-30 18:43:38 -07001664 *dynamic_flags = phdr.p_flags;
Ningsheng Jiane93be992014-09-16 15:22:10 +08001665 }
Dmitriy Ivanov14669a92014-09-05 16:42:53 -07001666 return;
1667 }
Elliott Hughes0266ae52014-02-10 17:46:57 -08001668 }
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001669}
1670
Evgenii Stepanovd640b222015-07-10 17:54:01 -07001671/* Return the program interpreter string, or nullptr if missing.
1672 *
1673 * Input:
1674 * phdr_table -> program header table
1675 * phdr_count -> number of entries in tables
1676 * load_bias -> load bias
1677 * Return:
1678 * pointer to the program interpreter string.
1679 */
Tamas Petz8d55d182020-02-24 14:15:25 +01001680const char* phdr_table_get_interpreter_name(const ElfW(Phdr)* phdr_table, size_t phdr_count,
Evgenii Stepanovd640b222015-07-10 17:54:01 -07001681 ElfW(Addr) load_bias) {
1682 for (size_t i = 0; i<phdr_count; ++i) {
1683 const ElfW(Phdr)& phdr = phdr_table[i];
1684 if (phdr.p_type == PT_INTERP) {
1685 return reinterpret_cast<const char*>(load_bias + phdr.p_vaddr);
1686 }
1687 }
1688 return nullptr;
1689}
1690
Robert Grosse4544d9f2014-10-15 14:32:19 -07001691// Sets loaded_phdr_ to the address of the program header table as it appears
1692// in the loaded segments in memory. This is in contrast with phdr_table_,
1693// which is temporary and will be released before the library is relocated.
Elliott Hughes650be4e2013-03-05 18:47:58 -08001694bool ElfReader::FindPhdr() {
Elliott Hughes0266ae52014-02-10 17:46:57 -08001695 const ElfW(Phdr)* phdr_limit = phdr_table_ + phdr_num_;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001696
Elliott Hughes650be4e2013-03-05 18:47:58 -08001697 // If there is a PT_PHDR, use it directly.
Elliott Hughes0266ae52014-02-10 17:46:57 -08001698 for (const ElfW(Phdr)* phdr = phdr_table_; phdr < phdr_limit; ++phdr) {
Elliott Hughes650be4e2013-03-05 18:47:58 -08001699 if (phdr->p_type == PT_PHDR) {
1700 return CheckPhdr(load_bias_ + phdr->p_vaddr);
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
Elliott Hughes650be4e2013-03-05 18:47:58 -08001704 // Otherwise, check the first loadable segment. If its file offset
1705 // is 0, it starts with the ELF header, and we can trivially find the
1706 // loaded program header from it.
Elliott Hughes0266ae52014-02-10 17:46:57 -08001707 for (const ElfW(Phdr)* phdr = phdr_table_; phdr < phdr_limit; ++phdr) {
Elliott Hughes650be4e2013-03-05 18:47:58 -08001708 if (phdr->p_type == PT_LOAD) {
1709 if (phdr->p_offset == 0) {
Elliott Hughes0266ae52014-02-10 17:46:57 -08001710 ElfW(Addr) elf_addr = load_bias_ + phdr->p_vaddr;
Elliott Hughesfaf05ba2014-02-11 16:59:37 -08001711 const ElfW(Ehdr)* ehdr = reinterpret_cast<const ElfW(Ehdr)*>(elf_addr);
Elliott Hughes0266ae52014-02-10 17:46:57 -08001712 ElfW(Addr) offset = ehdr->e_phoff;
Dmitriy Ivanov1649e7e2015-01-22 16:04:25 -08001713 return CheckPhdr(reinterpret_cast<ElfW(Addr)>(ehdr) + offset);
Elliott Hughes650be4e2013-03-05 18:47:58 -08001714 }
1715 break;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001716 }
Elliott Hughes650be4e2013-03-05 18:47:58 -08001717 }
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001718
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -07001719 DL_ERR("can't find loaded phdr for \"%s\"", name_.c_str());
Elliott Hughes650be4e2013-03-05 18:47:58 -08001720 return false;
1721}
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001722
Tamas Petz8d55d182020-02-24 14:15:25 +01001723// Tries to find .note.gnu.property section.
1724// It is not considered an error if such section is missing.
1725bool ElfReader::FindGnuPropertySection() {
1726#if defined(__aarch64__)
1727 note_gnu_property_ = GnuPropertySection(phdr_table_, phdr_num_, load_start(), name_.c_str());
1728#endif
1729 return true;
1730}
1731
Elliott Hughes650be4e2013-03-05 18:47:58 -08001732// Ensures that our program header is actually within a loadable
1733// segment. This should help catch badly-formed ELF files that
1734// would cause the linker to crash later when trying to access it.
Elliott Hughes0266ae52014-02-10 17:46:57 -08001735bool ElfReader::CheckPhdr(ElfW(Addr) loaded) {
1736 const ElfW(Phdr)* phdr_limit = phdr_table_ + phdr_num_;
1737 ElfW(Addr) loaded_end = loaded + (phdr_num_ * sizeof(ElfW(Phdr)));
Dmitriy Ivanovcf1cbbe2015-10-19 16:57:46 -07001738 for (const ElfW(Phdr)* phdr = phdr_table_; phdr < phdr_limit; ++phdr) {
Elliott Hughes650be4e2013-03-05 18:47:58 -08001739 if (phdr->p_type != PT_LOAD) {
1740 continue;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001741 }
Elliott Hughes0266ae52014-02-10 17:46:57 -08001742 ElfW(Addr) seg_start = phdr->p_vaddr + load_bias_;
1743 ElfW(Addr) seg_end = phdr->p_filesz + seg_start;
Elliott Hughes650be4e2013-03-05 18:47:58 -08001744 if (seg_start <= loaded && loaded_end <= seg_end) {
Elliott Hughes0266ae52014-02-10 17:46:57 -08001745 loaded_phdr_ = reinterpret_cast<const ElfW(Phdr)*>(loaded);
Elliott Hughes650be4e2013-03-05 18:47:58 -08001746 return true;
1747 }
1748 }
Dmitriy Ivanov4f7a7ad2015-10-15 12:07:25 -07001749 DL_ERR("\"%s\" loaded phdr %p not in loadable segment",
1750 name_.c_str(), reinterpret_cast<void*>(loaded));
Elliott Hughes650be4e2013-03-05 18:47:58 -08001751 return false;
David 'Digit' Turnerc1bd5592012-06-19 11:21:29 +02001752}