1*8d67ca89SAndroid Build Coastguard Worker /*
2*8d67ca89SAndroid Build Coastguard Worker * Copyright (C) 2012 The Android Open Source Project
3*8d67ca89SAndroid Build Coastguard Worker * All rights reserved.
4*8d67ca89SAndroid Build Coastguard Worker *
5*8d67ca89SAndroid Build Coastguard Worker * Redistribution and use in source and binary forms, with or without
6*8d67ca89SAndroid Build Coastguard Worker * modification, are permitted provided that the following conditions
7*8d67ca89SAndroid Build Coastguard Worker * are met:
8*8d67ca89SAndroid Build Coastguard Worker * * Redistributions of source code must retain the above copyright
9*8d67ca89SAndroid Build Coastguard Worker * notice, this list of conditions and the following disclaimer.
10*8d67ca89SAndroid Build Coastguard Worker * * Redistributions in binary form must reproduce the above copyright
11*8d67ca89SAndroid Build Coastguard Worker * notice, this list of conditions and the following disclaimer in
12*8d67ca89SAndroid Build Coastguard Worker * the documentation and/or other materials provided with the
13*8d67ca89SAndroid Build Coastguard Worker * distribution.
14*8d67ca89SAndroid Build Coastguard Worker *
15*8d67ca89SAndroid Build Coastguard Worker * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16*8d67ca89SAndroid Build Coastguard Worker * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17*8d67ca89SAndroid Build Coastguard Worker * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18*8d67ca89SAndroid Build Coastguard Worker * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19*8d67ca89SAndroid Build Coastguard Worker * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20*8d67ca89SAndroid Build Coastguard Worker * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21*8d67ca89SAndroid Build Coastguard Worker * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22*8d67ca89SAndroid Build Coastguard Worker * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23*8d67ca89SAndroid Build Coastguard Worker * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24*8d67ca89SAndroid Build Coastguard Worker * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25*8d67ca89SAndroid Build Coastguard Worker * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26*8d67ca89SAndroid Build Coastguard Worker * SUCH DAMAGE.
27*8d67ca89SAndroid Build Coastguard Worker */
28*8d67ca89SAndroid Build Coastguard Worker
29*8d67ca89SAndroid Build Coastguard Worker #include "linker_phdr.h"
30*8d67ca89SAndroid Build Coastguard Worker
31*8d67ca89SAndroid Build Coastguard Worker #include <linux/prctl.h>
32*8d67ca89SAndroid Build Coastguard Worker #include <sys/mman.h>
33*8d67ca89SAndroid Build Coastguard Worker #include <sys/prctl.h>
34*8d67ca89SAndroid Build Coastguard Worker #include <unistd.h>
35*8d67ca89SAndroid Build Coastguard Worker
36*8d67ca89SAndroid Build Coastguard Worker #include "linker_debug.h"
37*8d67ca89SAndroid Build Coastguard Worker #include "linker_dlwarning.h"
38*8d67ca89SAndroid Build Coastguard Worker #include "linker_globals.h"
39*8d67ca89SAndroid Build Coastguard Worker
40*8d67ca89SAndroid Build Coastguard Worker #include "platform/bionic/macros.h"
41*8d67ca89SAndroid Build Coastguard Worker #include "platform/bionic/page.h"
42*8d67ca89SAndroid Build Coastguard Worker
43*8d67ca89SAndroid Build Coastguard Worker #include <string>
44*8d67ca89SAndroid Build Coastguard Worker
45*8d67ca89SAndroid Build Coastguard Worker static bool g_enable_16kb_app_compat;
46*8d67ca89SAndroid Build Coastguard Worker
segment_contains_prefix(const ElfW (Phdr)* segment,const ElfW (Phdr)* prefix)47*8d67ca89SAndroid Build Coastguard Worker static inline bool segment_contains_prefix(const ElfW(Phdr)* segment, const ElfW(Phdr)* prefix) {
48*8d67ca89SAndroid Build Coastguard Worker return segment && prefix && segment->p_vaddr == prefix->p_vaddr;
49*8d67ca89SAndroid Build Coastguard Worker }
50*8d67ca89SAndroid Build Coastguard Worker
set_16kb_appcompat_mode(bool enable_app_compat)51*8d67ca89SAndroid Build Coastguard Worker void set_16kb_appcompat_mode(bool enable_app_compat) {
52*8d67ca89SAndroid Build Coastguard Worker g_enable_16kb_app_compat = enable_app_compat;
53*8d67ca89SAndroid Build Coastguard Worker }
54*8d67ca89SAndroid Build Coastguard Worker
get_16kb_appcompat_mode()55*8d67ca89SAndroid Build Coastguard Worker bool get_16kb_appcompat_mode() {
56*8d67ca89SAndroid Build Coastguard Worker return g_enable_16kb_app_compat;
57*8d67ca89SAndroid Build Coastguard Worker }
58*8d67ca89SAndroid Build Coastguard Worker
59*8d67ca89SAndroid Build Coastguard Worker /*
60*8d67ca89SAndroid Build Coastguard Worker * Returns true if the ELF contains at most 1 RELRO segment; and populates @relro_phdr
61*8d67ca89SAndroid Build Coastguard Worker * with the relro phdr or nullptr if none.
62*8d67ca89SAndroid Build Coastguard Worker *
63*8d67ca89SAndroid Build Coastguard Worker * Returns false if more than 1 RELRO segments are found.
64*8d67ca89SAndroid Build Coastguard Worker */
HasAtMostOneRelroSegment(const ElfW (Phdr)** relro_phdr)65*8d67ca89SAndroid Build Coastguard Worker bool ElfReader::HasAtMostOneRelroSegment(const ElfW(Phdr)** relro_phdr) {
66*8d67ca89SAndroid Build Coastguard Worker const ElfW(Phdr)* relro = nullptr;
67*8d67ca89SAndroid Build Coastguard Worker for (size_t i = 0; i < phdr_num_; ++i) {
68*8d67ca89SAndroid Build Coastguard Worker const ElfW(Phdr)* phdr = &phdr_table_[i];
69*8d67ca89SAndroid Build Coastguard Worker
70*8d67ca89SAndroid Build Coastguard Worker if (phdr->p_type != PT_GNU_RELRO) {
71*8d67ca89SAndroid Build Coastguard Worker continue;
72*8d67ca89SAndroid Build Coastguard Worker }
73*8d67ca89SAndroid Build Coastguard Worker
74*8d67ca89SAndroid Build Coastguard Worker if (relro == nullptr) {
75*8d67ca89SAndroid Build Coastguard Worker relro = phdr;
76*8d67ca89SAndroid Build Coastguard Worker } else {
77*8d67ca89SAndroid Build Coastguard Worker return false;
78*8d67ca89SAndroid Build Coastguard Worker }
79*8d67ca89SAndroid Build Coastguard Worker }
80*8d67ca89SAndroid Build Coastguard Worker
81*8d67ca89SAndroid Build Coastguard Worker *relro_phdr = relro;
82*8d67ca89SAndroid Build Coastguard Worker
83*8d67ca89SAndroid Build Coastguard Worker return true;
84*8d67ca89SAndroid Build Coastguard Worker }
85*8d67ca89SAndroid Build Coastguard Worker
86*8d67ca89SAndroid Build Coastguard Worker /*
87*8d67ca89SAndroid Build Coastguard Worker * In 16KiB compatibility mode ELFs with the following segment layout
88*8d67ca89SAndroid Build Coastguard Worker * can be loaded successfully:
89*8d67ca89SAndroid Build Coastguard Worker *
90*8d67ca89SAndroid Build Coastguard Worker * ┌────────────┬─────────────────────────┬────────────┐
91*8d67ca89SAndroid Build Coastguard Worker * │ │ │ │
92*8d67ca89SAndroid Build Coastguard Worker * │ (RO|RX)* │ (RW - RELRO prefix)? │ (RW)* │
93*8d67ca89SAndroid Build Coastguard Worker * │ │ │ │
94*8d67ca89SAndroid Build Coastguard Worker * └────────────┴─────────────────────────┴────────────┘
95*8d67ca89SAndroid Build Coastguard Worker *
96*8d67ca89SAndroid Build Coastguard Worker * In other words, compatible layouts have:
97*8d67ca89SAndroid Build Coastguard Worker * - zero or more RO or RX segments;
98*8d67ca89SAndroid Build Coastguard Worker * - followed by zero or one RELRO prefix;
99*8d67ca89SAndroid Build Coastguard Worker * - followed by zero or more RW segments (this can include the RW
100*8d67ca89SAndroid Build Coastguard Worker * suffix from the segment containing the RELRO prefix, if any)
101*8d67ca89SAndroid Build Coastguard Worker *
102*8d67ca89SAndroid Build Coastguard Worker * In 16KiB compat mode, after relocation, the ELF is layout in virtual
103*8d67ca89SAndroid Build Coastguard Worker * memory is as shown below:
104*8d67ca89SAndroid Build Coastguard Worker * ┌──────────────────────────────────────┬────────────┐
105*8d67ca89SAndroid Build Coastguard Worker * │ │ │
106*8d67ca89SAndroid Build Coastguard Worker * │ (RX)? │ (RW)? │
107*8d67ca89SAndroid Build Coastguard Worker * │ │ │
108*8d67ca89SAndroid Build Coastguard Worker * └──────────────────────────────────────┴────────────┘
109*8d67ca89SAndroid Build Coastguard Worker *
110*8d67ca89SAndroid Build Coastguard Worker * In compat mode:
111*8d67ca89SAndroid Build Coastguard Worker * - the RO and RX segments along with the RELRO prefix are protected
112*8d67ca89SAndroid Build Coastguard Worker * as RX;
113*8d67ca89SAndroid Build Coastguard Worker * - and the RW segments along with RW suffix from the relro segment,
114*8d67ca89SAndroid Build Coastguard Worker * if any; are RW protected.
115*8d67ca89SAndroid Build Coastguard Worker *
116*8d67ca89SAndroid Build Coastguard Worker * This allows for the single RX|RW permission boundary to be aligned with
117*8d67ca89SAndroid Build Coastguard Worker * a 16KiB page boundary; since a single page cannot share multiple
118*8d67ca89SAndroid Build Coastguard Worker * permissions.
119*8d67ca89SAndroid Build Coastguard Worker *
120*8d67ca89SAndroid Build Coastguard Worker * IsEligibleFor16KiBAppCompat() identifies compatible ELFs and populates @vaddr
121*8d67ca89SAndroid Build Coastguard Worker * with the boundary between RX|RW portions.
122*8d67ca89SAndroid Build Coastguard Worker *
123*8d67ca89SAndroid Build Coastguard Worker * Returns true if the ELF can be loaded in compat mode, else false.
124*8d67ca89SAndroid Build Coastguard Worker */
IsEligibleFor16KiBAppCompat(ElfW (Addr)* vaddr)125*8d67ca89SAndroid Build Coastguard Worker bool ElfReader::IsEligibleFor16KiBAppCompat(ElfW(Addr)* vaddr) {
126*8d67ca89SAndroid Build Coastguard Worker const ElfW(Phdr)* relro_phdr = nullptr;
127*8d67ca89SAndroid Build Coastguard Worker if (!HasAtMostOneRelroSegment(&relro_phdr)) {
128*8d67ca89SAndroid Build Coastguard Worker DL_WARN("\"%s\": Compat loading failed: Multiple RELRO segments found", name_.c_str());
129*8d67ca89SAndroid Build Coastguard Worker return false;
130*8d67ca89SAndroid Build Coastguard Worker }
131*8d67ca89SAndroid Build Coastguard Worker
132*8d67ca89SAndroid Build Coastguard Worker const ElfW(Phdr)* last_rw = nullptr;
133*8d67ca89SAndroid Build Coastguard Worker const ElfW(Phdr)* first_rw = nullptr;
134*8d67ca89SAndroid Build Coastguard Worker
135*8d67ca89SAndroid Build Coastguard Worker for (size_t i = 0; i < phdr_num_; ++i) {
136*8d67ca89SAndroid Build Coastguard Worker const ElfW(Phdr)* curr = &phdr_table_[i];
137*8d67ca89SAndroid Build Coastguard Worker const ElfW(Phdr)* prev = (i > 0) ? &phdr_table_[i - 1] : nullptr;
138*8d67ca89SAndroid Build Coastguard Worker
139*8d67ca89SAndroid Build Coastguard Worker if (curr->p_type != PT_LOAD) {
140*8d67ca89SAndroid Build Coastguard Worker continue;
141*8d67ca89SAndroid Build Coastguard Worker }
142*8d67ca89SAndroid Build Coastguard Worker
143*8d67ca89SAndroid Build Coastguard Worker int prot = PFLAGS_TO_PROT(curr->p_flags);
144*8d67ca89SAndroid Build Coastguard Worker
145*8d67ca89SAndroid Build Coastguard Worker if ((prot & PROT_WRITE) && (prot & PROT_READ)) {
146*8d67ca89SAndroid Build Coastguard Worker if (!first_rw) {
147*8d67ca89SAndroid Build Coastguard Worker first_rw = curr;
148*8d67ca89SAndroid Build Coastguard Worker }
149*8d67ca89SAndroid Build Coastguard Worker
150*8d67ca89SAndroid Build Coastguard Worker if (last_rw && last_rw != prev) {
151*8d67ca89SAndroid Build Coastguard Worker DL_WARN("\"%s\": Compat loading failed: ELF contains multiple non-adjacent RW segments",
152*8d67ca89SAndroid Build Coastguard Worker name_.c_str());
153*8d67ca89SAndroid Build Coastguard Worker return false;
154*8d67ca89SAndroid Build Coastguard Worker }
155*8d67ca89SAndroid Build Coastguard Worker
156*8d67ca89SAndroid Build Coastguard Worker last_rw = curr;
157*8d67ca89SAndroid Build Coastguard Worker }
158*8d67ca89SAndroid Build Coastguard Worker }
159*8d67ca89SAndroid Build Coastguard Worker
160*8d67ca89SAndroid Build Coastguard Worker if (!relro_phdr) {
161*8d67ca89SAndroid Build Coastguard Worker *vaddr = align_down(first_rw->p_vaddr, kCompatPageSize);
162*8d67ca89SAndroid Build Coastguard Worker return true;
163*8d67ca89SAndroid Build Coastguard Worker }
164*8d67ca89SAndroid Build Coastguard Worker
165*8d67ca89SAndroid Build Coastguard Worker // The RELRO segment is present, it must be the prefix of the first RW segment.
166*8d67ca89SAndroid Build Coastguard Worker if (!segment_contains_prefix(first_rw, relro_phdr)) {
167*8d67ca89SAndroid Build Coastguard Worker DL_WARN("\"%s\": Compat loading failed: RELRO is not in the first RW segment",
168*8d67ca89SAndroid Build Coastguard Worker name_.c_str());
169*8d67ca89SAndroid Build Coastguard Worker return false;
170*8d67ca89SAndroid Build Coastguard Worker }
171*8d67ca89SAndroid Build Coastguard Worker
172*8d67ca89SAndroid Build Coastguard Worker uint64_t end;
173*8d67ca89SAndroid Build Coastguard Worker if (__builtin_add_overflow(relro_phdr->p_vaddr, relro_phdr->p_memsz, &end)) {
174*8d67ca89SAndroid Build Coastguard Worker DL_WARN("\"%s\": Compat loading failed: relro vaddr + memsz overflowed", name_.c_str());
175*8d67ca89SAndroid Build Coastguard Worker return false;
176*8d67ca89SAndroid Build Coastguard Worker }
177*8d67ca89SAndroid Build Coastguard Worker
178*8d67ca89SAndroid Build Coastguard Worker *vaddr = align_up(end, kCompatPageSize);
179*8d67ca89SAndroid Build Coastguard Worker return true;
180*8d67ca89SAndroid Build Coastguard Worker }
181*8d67ca89SAndroid Build Coastguard Worker
182*8d67ca89SAndroid Build Coastguard Worker /*
183*8d67ca89SAndroid Build Coastguard Worker * Returns the offset/shift needed to align @vaddr to a page boundary.
184*8d67ca89SAndroid Build Coastguard Worker */
perm_boundary_offset(const ElfW (Addr)addr)185*8d67ca89SAndroid Build Coastguard Worker static inline ElfW(Addr) perm_boundary_offset(const ElfW(Addr) addr) {
186*8d67ca89SAndroid Build Coastguard Worker ElfW(Addr) offset = page_offset(addr);
187*8d67ca89SAndroid Build Coastguard Worker
188*8d67ca89SAndroid Build Coastguard Worker return offset ? page_size() - offset : 0;
189*8d67ca89SAndroid Build Coastguard Worker }
190*8d67ca89SAndroid Build Coastguard Worker
Setup16KiBAppCompat()191*8d67ca89SAndroid Build Coastguard Worker bool ElfReader::Setup16KiBAppCompat() {
192*8d67ca89SAndroid Build Coastguard Worker if (!should_use_16kib_app_compat_) {
193*8d67ca89SAndroid Build Coastguard Worker return true;
194*8d67ca89SAndroid Build Coastguard Worker }
195*8d67ca89SAndroid Build Coastguard Worker
196*8d67ca89SAndroid Build Coastguard Worker ElfW(Addr) rx_rw_boundary; // Permission bounadry for compat mode
197*8d67ca89SAndroid Build Coastguard Worker if (!IsEligibleFor16KiBAppCompat(&rx_rw_boundary)) {
198*8d67ca89SAndroid Build Coastguard Worker return false;
199*8d67ca89SAndroid Build Coastguard Worker }
200*8d67ca89SAndroid Build Coastguard Worker
201*8d67ca89SAndroid Build Coastguard Worker // Adjust the load_bias to position the RX|RW boundary on a page boundary
202*8d67ca89SAndroid Build Coastguard Worker load_bias_ += perm_boundary_offset(rx_rw_boundary);
203*8d67ca89SAndroid Build Coastguard Worker
204*8d67ca89SAndroid Build Coastguard Worker // RW region (.data, .bss ...)
205*8d67ca89SAndroid Build Coastguard Worker ElfW(Addr) rw_start = load_bias_ + rx_rw_boundary;
206*8d67ca89SAndroid Build Coastguard Worker ElfW(Addr) rw_size = load_size_ - (rw_start - reinterpret_cast<ElfW(Addr)>(load_start_));
207*8d67ca89SAndroid Build Coastguard Worker
208*8d67ca89SAndroid Build Coastguard Worker CHECK(rw_start % getpagesize() == 0);
209*8d67ca89SAndroid Build Coastguard Worker CHECK(rw_size % getpagesize() == 0);
210*8d67ca89SAndroid Build Coastguard Worker
211*8d67ca89SAndroid Build Coastguard Worker // Compat RELRO (RX) region (.text, .data.relro, ...)
212*8d67ca89SAndroid Build Coastguard Worker compat_relro_start_ = reinterpret_cast<ElfW(Addr)>(load_start_);
213*8d67ca89SAndroid Build Coastguard Worker compat_relro_size_ = load_size_ - rw_size;
214*8d67ca89SAndroid Build Coastguard Worker
215*8d67ca89SAndroid Build Coastguard Worker // Label the ELF VMA, since compat mode uses anonymous mappings.
216*8d67ca89SAndroid Build Coastguard Worker std::string compat_name = name_ + " (compat loaded)";
217*8d67ca89SAndroid Build Coastguard Worker prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, load_start_, load_size_, compat_name.c_str());
218*8d67ca89SAndroid Build Coastguard Worker
219*8d67ca89SAndroid Build Coastguard Worker return true;
220*8d67ca89SAndroid Build Coastguard Worker }
221*8d67ca89SAndroid Build Coastguard Worker
CompatMapSegment(size_t seg_idx,size_t len)222*8d67ca89SAndroid Build Coastguard Worker bool ElfReader::CompatMapSegment(size_t seg_idx, size_t len) {
223*8d67ca89SAndroid Build Coastguard Worker const ElfW(Phdr)* phdr = &phdr_table_[seg_idx];
224*8d67ca89SAndroid Build Coastguard Worker
225*8d67ca89SAndroid Build Coastguard Worker // NOTE: The compat(legacy) page size (4096) must be used when aligning
226*8d67ca89SAndroid Build Coastguard Worker // the 4KiB segments for loading (reading). The larger 16KiB page size
227*8d67ca89SAndroid Build Coastguard Worker // will lead to overwriting adjacent segments since the ELF's segment(s)
228*8d67ca89SAndroid Build Coastguard Worker // are not 16KiB aligned.
229*8d67ca89SAndroid Build Coastguard Worker
230*8d67ca89SAndroid Build Coastguard Worker void* start = reinterpret_cast<void*>(align_down(phdr->p_vaddr + load_bias_, kCompatPageSize));
231*8d67ca89SAndroid Build Coastguard Worker
232*8d67ca89SAndroid Build Coastguard Worker // The ELF could be being loaded directly from a zipped APK,
233*8d67ca89SAndroid Build Coastguard Worker // the zip offset must be added to find the segment offset.
234*8d67ca89SAndroid Build Coastguard Worker const ElfW(Addr) offset = file_offset_ + align_down(phdr->p_offset, kCompatPageSize);
235*8d67ca89SAndroid Build Coastguard Worker
236*8d67ca89SAndroid Build Coastguard Worker CHECK(should_use_16kib_app_compat_);
237*8d67ca89SAndroid Build Coastguard Worker
238*8d67ca89SAndroid Build Coastguard Worker // Since the 4KiB max-page-size ELF is not properly aligned, loading it by
239*8d67ca89SAndroid Build Coastguard Worker // directly mmapping the ELF file is not feasible.
240*8d67ca89SAndroid Build Coastguard Worker // Instead, read the ELF contents into the anonymous RW mapping.
241*8d67ca89SAndroid Build Coastguard Worker if (TEMP_FAILURE_RETRY(pread64(fd_, start, len, offset)) == -1) {
242*8d67ca89SAndroid Build Coastguard Worker DL_ERR("Compat loading: \"%s\" failed to read LOAD segment %zu: %m", name_.c_str(), seg_idx);
243*8d67ca89SAndroid Build Coastguard Worker return false;
244*8d67ca89SAndroid Build Coastguard Worker }
245*8d67ca89SAndroid Build Coastguard Worker
246*8d67ca89SAndroid Build Coastguard Worker return true;
247*8d67ca89SAndroid Build Coastguard Worker }
248