xref: /aosp_15_r20/bionic/linker/linker_phdr_16kib_compat.cpp (revision 8d67ca893c1523eb926b9080dbe4e2ffd2a27ba1)
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