blob: e0846356b254ddf1400ef5dd49cfd6790c2e0b95 [file] [log] [blame]
Kalesh Singhce1c3cf2024-09-30 13:26:23 -07001/*
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
29#include "linker_phdr.h"
30
31#include <linux/prctl.h>
32#include <sys/mman.h>
33#include <sys/prctl.h>
34#include <unistd.h>
35
36#include "linker_debug.h"
37#include "linker_dlwarning.h"
38#include "linker_globals.h"
39
40#include "platform/bionic/macros.h"
41#include "platform/bionic/page.h"
42
43#include <string>
44
45static inline bool segment_contains_prefix(const ElfW(Phdr)* segment, const ElfW(Phdr)* prefix) {
46 return segment && prefix && segment->p_vaddr == prefix->p_vaddr;
47}
48
49/*
50 * Returns true if the ELF contains at most 1 RELRO segment; and populates @relro_phdr
51 * with the relro phdr or nullptr if none.
52 *
53 * Returns false if more than 1 RELRO segments are found.
54 */
55bool ElfReader::HasAtMostOneRelroSegment(const ElfW(Phdr)** relro_phdr) {
56 const ElfW(Phdr)* relro = nullptr;
57 for (size_t i = 0; i < phdr_num_; ++i) {
58 const ElfW(Phdr)* phdr = &phdr_table_[i];
59
60 if (phdr->p_type != PT_GNU_RELRO) {
61 continue;
62 }
63
64 if (relro == nullptr) {
65 relro = phdr;
66 } else {
67 return false;
68 }
69 }
70
71 *relro_phdr = relro;
72
73 return true;
74}
75
76/*
77 * In 16KiB compatibility mode ELFs with the following segment layout
78 * can be loaded successfully:
79 *
80 * ┌────────────┬─────────────────────────┬────────────┐
81 * │ │ │ │
82 * │ (RO|RX)* │ (RW - RELRO prefix)? │ (RW)* │
83 * │ │ │ │
84 * └────────────┴─────────────────────────┴────────────┘
85 *
86 * In other words, compatible layouts have:
87 * - zero or more RO or RX segments;
88 * - followed by zero or one RELRO prefix;
89 * - followed by zero or more RW segments (this can include the RW
90 * suffix from the segment containing the RELRO prefix, if any)
91 *
92 * In 16KiB compat mode, after relocation, the ELF is layout in virtual
93 * memory is as shown below:
94 * ┌──────────────────────────────────────┬────────────┐
95 * │ │ │
96 * │ (RX)? │ (RW)? │
97 * │ │ │
98 * └──────────────────────────────────────┴────────────┘
99 *
100 * In compat mode:
101 * - the RO and RX segments along with the RELRO prefix are protected
102 * as RX;
103 * - and the RW segments along with RW suffix from the relro segment,
104 * if any; are RW protected.
105 *
106 * This allows for the single RX|RW permission boundary to be aligned with
107 * a 16KiB page boundary; since a single page cannot share multiple
108 * permissions.
109 *
110 * IsEligibleFor16KiBAppCompat() identifies compatible ELFs and populates @vaddr
111 * with the boundary between RX|RW portions.
112 *
113 * Returns true if the ELF can be loaded in compat mode, else false.
114 */
115bool ElfReader::IsEligibleFor16KiBAppCompat(ElfW(Addr)* vaddr) {
116 const ElfW(Phdr)* relro_phdr = nullptr;
117 if (!HasAtMostOneRelroSegment(&relro_phdr)) {
118 DL_WARN("\"%s\": Compat loading failed: Multiple RELRO segments found", name_.c_str());
119 return false;
120 }
121
122 const ElfW(Phdr)* last_rw = nullptr;
123 const ElfW(Phdr)* first_rw = nullptr;
124
125 for (size_t i = 0; i < phdr_num_; ++i) {
126 const ElfW(Phdr)* curr = &phdr_table_[i];
127 const ElfW(Phdr)* prev = (i > 0) ? &phdr_table_[i - 1] : nullptr;
128
129 if (curr->p_type != PT_LOAD) {
130 continue;
131 }
132
133 int prot = PFLAGS_TO_PROT(curr->p_flags);
134
135 if ((prot & PROT_WRITE) && (prot & PROT_READ)) {
136 if (!first_rw) {
137 first_rw = curr;
138 }
139
140 if (last_rw && last_rw != prev) {
141 DL_WARN("\"%s\": Compat loading failed: ELF contains multiple non-adjacent RW segments",
142 name_.c_str());
143 return false;
144 }
145
146 last_rw = curr;
147 }
148 }
149
150 if (!relro_phdr) {
151 *vaddr = align_down(first_rw->p_vaddr, kCompatPageSize);
152 return true;
153 }
154
155 // The RELRO segment is present, it must be the prefix of the first RW segment.
156 if (!segment_contains_prefix(first_rw, relro_phdr)) {
157 DL_WARN("\"%s\": Compat loading failed: RELRO is not in the first RW segment",
158 name_.c_str());
159 return false;
160 }
161
162 uint64_t end;
163 if (__builtin_add_overflow(relro_phdr->p_vaddr, relro_phdr->p_memsz, &end)) {
164 DL_WARN("\"%s\": Compat loading failed: relro vaddr + memsz overflowed", name_.c_str());
165 return false;
166 }
167
168 *vaddr = align_up(end, kCompatPageSize);
169 return true;
170}
171
172/*
173 * Returns the offset/shift needed to align @vaddr to a page boundary.
174 */
175static inline ElfW(Addr) perm_boundary_offset(const ElfW(Addr) addr) {
176 ElfW(Addr) offset = page_offset(addr);
177
178 return offset ? page_size() - offset : 0;
179}
180
181bool ElfReader::Setup16KiBAppCompat() {
182 if (!should_use_16kib_app_compat_) {
183 return true;
184 }
185
186 ElfW(Addr) rx_rw_boundary; // Permission bounadry for compat mode
187 if (!IsEligibleFor16KiBAppCompat(&rx_rw_boundary)) {
188 return false;
189 }
190
191 // Adjust the load_bias to position the RX|RW boundary on a page boundary
192 load_bias_ += perm_boundary_offset(rx_rw_boundary);
193
194 // RW region (.data, .bss ...)
195 ElfW(Addr) rw_start = load_bias_ + rx_rw_boundary;
196 ElfW(Addr) rw_size = load_size_ - (rw_start - reinterpret_cast<ElfW(Addr)>(load_start_));
197
198 CHECK(rw_start % getpagesize() == 0);
199 CHECK(rw_size % getpagesize() == 0);
200
201 // Compat RELRO (RX) region (.text, .data.relro, ...)
202 compat_relro_start_ = reinterpret_cast<ElfW(Addr)>(load_start_);
203 compat_relro_size_ = load_size_ - rw_size;
204
205 // Label the ELF VMA, since compat mode uses anonymous mappings.
206 std::string compat_name = name_ + " (compat loaded)";
207 prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, load_start_, load_size_, compat_name.c_str());
208
209 return true;
210}
211
212bool ElfReader::CompatMapSegment(size_t seg_idx, size_t len) {
213 const ElfW(Phdr)* phdr = &phdr_table_[seg_idx];
214
215 // NOTE: The compat(legacy) page size (4096) must be used when aligning
216 // the 4KiB segments for loading (reading). The larger 16KiB page size
217 // will lead to overwriting adjacent segments since the ELF's segment(s)
218 // are not 16KiB aligned.
219
220 void* start = reinterpret_cast<void*>(align_down(phdr->p_vaddr + load_bias_, kCompatPageSize));
221
222 // The ELF could be being loaded directly from a zipped APK,
223 // the zip offset must be added to find the segment offset.
224 const ElfW(Addr) offset = file_offset_ + align_down(phdr->p_offset, kCompatPageSize);
225
226 int prot = PFLAGS_TO_PROT(phdr->p_flags);
227
228 CHECK(should_use_16kib_app_compat_);
229
230 // Since the 4KiB max-page-size ELF is not properly aligned, loading it by
231 // directly mmapping the ELF file is not feasible.
232 // Instead, read the ELF contents into the anonymous RW mapping.
233 if (TEMP_FAILURE_RETRY(pread64(fd_, start, len, offset)) == -1) {
234 DL_ERR("Compat loading: \"%s\" failed to read LOAD segment %zu: %m", name_.c_str(), seg_idx);
235 return false;
236 }
237
238 return true;
239}