Serenity Operating System
1/*
2 * Copyright (c) 2019-2020, Andrew Kaster <andrewdkaster@gmail.com>
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 are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <AK/StringBuilder.h>
28#include <LibELF/ELFDynamicLoader.h>
29
30#include <assert.h>
31#include <dlfcn.h>
32#include <mman.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36
37#define DYNAMIC_LOAD_DEBUG
38//#define DYNAMIC_LOAD_VERBOSE
39
40#ifdef DYNAMIC_LOAD_VERBOSE
41# define VERBOSE(fmt, ...) dbgprintf(fmt, ##__VA_ARGS__)
42#else
43# define VERBOSE(fmt, ...) \
44 do { \
45 } while (0)
46#endif
47
48static bool s_always_bind_now = false;
49
50NonnullRefPtr<ELFDynamicLoader> ELFDynamicLoader::construct(const char* filename, int fd, size_t size)
51{
52 return adopt(*new ELFDynamicLoader(filename, fd, size));
53}
54
55ELFDynamicLoader::ELFDynamicLoader(const char* filename, int fd, size_t size)
56 : m_filename(filename)
57 , m_file_size(size)
58 , m_image_fd(fd)
59{
60 String file_mmap_name = String::format("ELF_DYN: %s", m_filename.characters());
61
62 m_file_mapping = mmap_with_name(nullptr, size, PROT_READ, MAP_PRIVATE, m_image_fd, 0, file_mmap_name.characters());
63 if (MAP_FAILED == m_file_mapping) {
64 m_valid = false;
65 }
66}
67
68ELFDynamicLoader::~ELFDynamicLoader()
69{
70 if (MAP_FAILED != m_file_mapping)
71 munmap(m_file_mapping, m_file_size);
72}
73
74void* ELFDynamicLoader::symbol_for_name(const char* name)
75{
76 auto symbol = m_dynamic_object->hash_section().lookup_symbol(name);
77
78 if (symbol.is_undefined())
79 return nullptr;
80
81 return m_dynamic_object->base_address().offset(symbol.value()).as_ptr();
82}
83
84bool ELFDynamicLoader::load_from_image(unsigned flags)
85{
86 ELFImage elf_image((u8*)m_file_mapping, m_file_size);
87
88 m_valid = elf_image.is_valid() && elf_image.is_dynamic();
89
90 if (!m_valid) {
91 return false;
92 }
93
94#ifdef DYNAMIC_LOAD_VERBOSE
95 m_image->dump();
96#endif
97
98 load_program_headers(elf_image);
99
100 // Don't need this private mapping anymore
101 munmap(m_file_mapping, m_file_size);
102 m_file_mapping = MAP_FAILED;
103
104 m_dynamic_object = AK::make<ELFDynamicObject>(m_text_segment_load_address, m_dynamic_section_address);
105
106 return load_stage_2(flags);
107}
108
109bool ELFDynamicLoader::load_stage_2(unsigned flags)
110{
111 ASSERT(flags & RTLD_GLOBAL);
112 ASSERT(flags & RTLD_LAZY);
113
114#ifdef DYNAMIC_LOAD_DEBUG
115 m_dynamic_object->dump();
116#endif
117
118 if (m_dynamic_object->has_text_relocations()) {
119 dbg() << "Someone linked non -fPIC code into " << m_filename << " :(";
120 ASSERT(m_text_segment_load_address.get() != 0);
121 if (0 > mprotect(m_text_segment_load_address.as_ptr(), m_text_segment_size, PROT_READ | PROT_WRITE)) {
122 perror("mprotect .text: PROT_READ | PROT_WRITE"); // FIXME: dlerror?
123 return false;
124 }
125 }
126
127 do_relocations();
128 setup_plt_trampoline();
129
130 // Clean up our setting of .text to PROT_READ | PROT_WRITE
131 if (m_dynamic_object->has_text_relocations()) {
132 if (0 > mprotect(m_text_segment_load_address.as_ptr(), m_text_segment_size, PROT_READ | PROT_EXEC)) {
133 perror("mprotect .text: PROT_READ | PROT_EXEC"); // FIXME: dlerror?
134 return false;
135 }
136 }
137
138 call_object_init_functions();
139
140#ifdef DYNAMIC_LOAD_DEBUG
141 dbgprintf("Loaded %s\n", m_filename.characters());
142#endif
143 return true;
144}
145
146void ELFDynamicLoader::load_program_headers(const ELFImage& elf_image)
147{
148 Vector<ProgramHeaderRegion> program_headers;
149
150 ProgramHeaderRegion* text_region_ptr = nullptr;
151 ProgramHeaderRegion* data_region_ptr = nullptr;
152 ProgramHeaderRegion* tls_region_ptr = nullptr;
153 VirtualAddress dynamic_region_desired_vaddr;
154
155 elf_image.for_each_program_header([&](const ELFImage::ProgramHeader& program_header) {
156 ProgramHeaderRegion new_region;
157 new_region.set_program_header(program_header.raw_header());
158 program_headers.append(move(new_region));
159 auto& region = program_headers.last();
160 if (region.is_tls_template())
161 tls_region_ptr = ®ion;
162 else if (region.is_load()) {
163 if (region.is_executable())
164 text_region_ptr = ®ion;
165 else
166 data_region_ptr = ®ion;
167 } else if (region.is_dynamic()) {
168 dynamic_region_desired_vaddr = region.desired_load_address();
169 }
170 });
171
172 ASSERT(text_region_ptr && data_region_ptr);
173
174 // Process regions in order: .text, .data, .tls
175 auto* region = text_region_ptr;
176 void* text_segment_begin = mmap_with_name(nullptr, region->required_load_size(), region->mmap_prot(), MAP_PRIVATE, m_image_fd, region->offset(), String::format(".text: %s", m_filename.characters()).characters());
177 if (MAP_FAILED == text_segment_begin) {
178 ASSERT_NOT_REACHED();
179 }
180 m_text_segment_size = region->required_load_size();
181 m_text_segment_load_address = VirtualAddress { (u32)text_segment_begin };
182
183 m_dynamic_section_address = dynamic_region_desired_vaddr.offset(m_text_segment_load_address.get());
184
185 region = data_region_ptr;
186 void* data_segment_begin = mmap_with_name((u8*)text_segment_begin + m_text_segment_size, region->required_load_size(), region->mmap_prot(), MAP_ANONYMOUS | MAP_PRIVATE, 0, 0, String::format(".data: %s", m_filename.characters()).characters());
187 if (MAP_FAILED == data_segment_begin) {
188 ASSERT_NOT_REACHED();
189 }
190 VirtualAddress data_segment_actual_addr = region->desired_load_address().offset((u32)text_segment_begin);
191 memcpy(data_segment_actual_addr.as_ptr(), (u8*)m_file_mapping + region->offset(), region->size_in_image());
192
193 // FIXME: Do some kind of 'allocate TLS section' or some such from a per-application pool
194 if (tls_region_ptr) {
195 region = tls_region_ptr;
196 // FIXME: This can't be right either. TLS needs some real work i'd say :)
197 m_tls_segment_address = tls_region_ptr->desired_load_address();
198 VirtualAddress tls_segment_actual_addr = region->desired_load_address().offset((u32)text_segment_begin);
199 memcpy(tls_segment_actual_addr.as_ptr(), (u8*)m_file_mapping + region->offset(), region->size_in_image());
200 }
201}
202
203void ELFDynamicLoader::do_relocations()
204{
205 u32 load_base_address = m_dynamic_object->base_address().get();
206
207 // FIXME: We should really bail on undefined symbols here.
208
209 auto main_relocation_section = m_dynamic_object->relocation_section();
210
211 main_relocation_section.for_each_relocation([&](const ELFDynamicObject::Relocation& relocation) {
212 VERBOSE("====== RELOCATION %d: offset 0x%08X, type %d, symidx %08X\n", relocation.offset_in_section() / main_relocation_section.entry_size(), relocation.offset(), relocation.type(), relocation.symbol_index());
213 u32* patch_ptr = (u32*)(load_base_address + relocation.offset());
214 switch (relocation.type()) {
215 case R_386_NONE:
216 // Apparently most loaders will just skip these?
217 // Seems if the 'link editor' generates one something is funky with your code
218 VERBOSE("None relocation. No symbol, no nothin.\n");
219 break;
220 case R_386_32: {
221 auto symbol = relocation.symbol();
222 VERBOSE("Absolute relocation: name: '%s', value: %p\n", symbol.name(), symbol.value());
223 u32 symbol_address = symbol.value() + load_base_address;
224 *patch_ptr += symbol_address;
225 VERBOSE(" Symbol address: %p\n", *patch_ptr);
226 break;
227 }
228 case R_386_PC32: {
229 auto symbol = relocation.symbol();
230 VERBOSE("PC-relative relocation: '%s', value: %p\n", symbol.name(), symbol.value());
231 u32 relative_offset = (symbol.value() - relocation.offset());
232 *patch_ptr += relative_offset;
233 VERBOSE(" Symbol address: %p\n", *patch_ptr);
234 break;
235 }
236 case R_386_GLOB_DAT: {
237 auto symbol = relocation.symbol();
238 VERBOSE("Global data relocation: '%s', value: %p\n", symbol.name(), symbol.value());
239 u32 symbol_location = load_base_address + symbol.value();
240 *patch_ptr = symbol_location;
241 VERBOSE(" Symbol address: %p\n", *patch_ptr);
242 break;
243 }
244 case R_386_RELATIVE: {
245 // FIXME: According to the spec, R_386_relative ones must be done first.
246 // We could explicitly do them first using m_number_of_relocatoins from DT_RELCOUNT
247 // However, our compiler is nice enough to put them at the front of the relocations for us :)
248 VERBOSE("Load address relocation at offset %X\n", relocation.offset());
249 VERBOSE(" patch ptr == %p, adding load base address (%p) to it and storing %p\n", *patch_ptr, load_base_address, *patch_ptr + load_base_address);
250 *patch_ptr += load_base_address; // + addend for RelA (addend for Rel is stored at addr)
251 break;
252 }
253 case R_386_TLS_TPOFF: {
254 VERBOSE("Relocation type: R_386_TLS_TPOFF at offset %X\n", relocation.offset());
255 // FIXME: this can't be right? I have no idea what "negative offset into TLS storage" means...
256 // FIXME: Check m_has_static_tls and do something different for dynamic TLS
257 *patch_ptr = relocation.offset() - (u32)m_tls_segment_address.as_ptr() - *patch_ptr;
258 break;
259 }
260 default:
261 // Raise the alarm! Someone needs to implement this relocation type
262 dbgprintf("Found a new exciting relocation type %d\n", relocation.type());
263 printf("ELFDynamicLoader: Found unknown relocation type %d\n", relocation.type());
264 ASSERT_NOT_REACHED();
265 break;
266 }
267 return IterationDecision::Continue;
268 });
269
270 // Handle PLT Global offset table relocations.
271 m_dynamic_object->plt_relocation_section().for_each_relocation([&](const ELFDynamicObject::Relocation& relocation) {
272 // FIXME: Or BIND_NOW flag passed in?
273 if (m_dynamic_object->must_bind_now() || s_always_bind_now) {
274 // Eagerly BIND_NOW the PLT entries, doing all the symbol looking goodness
275 // The patch method returns the address for the LAZY fixup path, but we don't need it here
276 (void)patch_plt_entry(relocation.offset_in_section());
277 } else {
278 // LAZY-ily bind the PLT slots by just adding the base address to the offsets stored there
279 // This avoids doing symbol lookup, which might be expensive
280 ASSERT(relocation.type() == R_386_JMP_SLOT);
281
282 u8* relocation_address = relocation.address().as_ptr();
283
284 *(u32*)relocation_address += load_base_address;
285 }
286 return IterationDecision::Continue;
287 });
288
289#ifdef DYNAMIC_LOAD_DEBUG
290 dbgprintf("Done relocating!\n");
291#endif
292}
293
294// Defined in <arch>/plt_trampoline.S
295extern "C" void _plt_trampoline(void) __attribute__((visibility("hidden")));
296
297void ELFDynamicLoader::setup_plt_trampoline()
298{
299 VirtualAddress got_address = m_dynamic_object->plt_got_base_address();
300
301 u32* got_u32_ptr = (u32*)got_address.as_ptr();
302 got_u32_ptr[1] = (u32)this;
303 got_u32_ptr[2] = (u32)&_plt_trampoline;
304
305#ifdef DYNAMIC_LOAD_DEBUG
306 dbgprintf("Set GOT PLT entries at %p: [0] = %p [1] = %p, [2] = %p\n", got_u32_ptr, got_u32_ptr[0], got_u32_ptr[1], got_u32_ptr[2]);
307#endif
308}
309
310// Called from our ASM routine _plt_trampoline
311extern "C" Elf32_Addr _fixup_plt_entry(ELFDynamicLoader* object, u32 relocation_offset)
312{
313 return object->patch_plt_entry(relocation_offset);
314}
315
316// offset is in PLT relocation table
317Elf32_Addr ELFDynamicLoader::patch_plt_entry(u32 relocation_offset)
318{
319 auto relocation = m_dynamic_object->plt_relocation_section().relocation_at_offset(relocation_offset);
320
321 ASSERT(relocation.type() == R_386_JMP_SLOT);
322
323 auto sym = relocation.symbol();
324
325 u8* relocation_address = relocation.address().as_ptr();
326 u32 symbol_location = sym.address().get();
327
328 VERBOSE("ELFDynamicLoader: Jump slot relocation: putting %s (%p) into PLT at %p\n", sym.name(), symbol_location, relocation_address);
329
330 *(u32*)relocation_address = symbol_location;
331
332 return symbol_location;
333}
334
335void ELFDynamicLoader::call_object_init_functions()
336{
337 typedef void (*InitFunc)();
338 auto init_function = (InitFunc)(m_dynamic_object->init_section().address().as_ptr());
339
340#ifdef DYNAMIC_LOAD_DEBUG
341 dbgprintf("Calling DT_INIT at %p\n", init_function);
342#endif
343 (init_function)();
344
345 auto init_array_section = m_dynamic_object->init_array_section();
346
347 InitFunc* init_begin = (InitFunc*)(init_array_section.address().as_ptr());
348 InitFunc* init_end = init_begin + init_array_section.entry_count();
349 while (init_begin != init_end) {
350 // Android sources claim that these can be -1, to be ignored.
351 // 0 definitely shows up. Apparently 0/-1 are valid? Confusing.
352 if (!*init_begin || ((i32)*init_begin == -1))
353 continue;
354#ifdef DYNAMIC_LOAD_DEBUG
355 dbgprintf("Calling DT_INITARRAY entry at %p\n", *init_begin);
356#endif
357 (*init_begin)();
358 ++init_begin;
359 }
360}
361
362u32 ELFDynamicLoader::ProgramHeaderRegion::mmap_prot() const
363{
364 int prot = 0;
365 prot |= is_executable() ? PROT_EXEC : 0;
366 prot |= is_readable() ? PROT_READ : 0;
367 prot |= is_writable() ? PROT_WRITE : 0;
368 return prot;
369}