Serenity Operating System
1/*
2 * Copyright (c) 2019-2020, Andrew Kaster <akaster@serenityos.org>
3 * Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
4 * Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
5 * Copyright (c) 2022, Daniel Bertalan <dani@danielbertalan.dev>
6 *
7 * SPDX-License-Identifier: BSD-2-Clause
8 */
9
10#include <AK/Optional.h>
11#include <AK/QuickSort.h>
12#include <AK/StringBuilder.h>
13#include <LibELF/DynamicLinker.h>
14#include <LibELF/DynamicLoader.h>
15#include <LibELF/Hashes.h>
16#include <LibELF/Validation.h>
17#include <assert.h>
18#include <bits/dlfcn_integration.h>
19#include <dlfcn.h>
20#include <errno.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <sys/mman.h>
25#include <sys/stat.h>
26#include <unistd.h>
27
28#ifndef AK_OS_SERENITY
29static void* mmap_with_name(void* addr, size_t length, int prot, int flags, int fd, off_t offset, char const*)
30{
31 return mmap(addr, length, prot, flags, fd, offset);
32}
33
34# define MAP_RANDOMIZED 0
35#endif
36
37namespace ELF {
38
39Result<NonnullRefPtr<DynamicLoader>, DlErrorMessage> DynamicLoader::try_create(int fd, DeprecatedString filepath)
40{
41 VERIFY(filepath.starts_with('/'));
42
43 struct stat stat;
44 if (fstat(fd, &stat) < 0) {
45 return DlErrorMessage { "DynamicLoader::try_create fstat" };
46 }
47
48 VERIFY(stat.st_size >= 0);
49 auto size = static_cast<size_t>(stat.st_size);
50 if (size < sizeof(ElfW(Ehdr)))
51 return DlErrorMessage { DeprecatedString::formatted("File {} has invalid ELF header", filepath) };
52
53 DeprecatedString file_mmap_name = DeprecatedString::formatted("ELF_DYN: {}", filepath);
54 auto* data = mmap_with_name(nullptr, size, PROT_READ, MAP_SHARED, fd, 0, file_mmap_name.characters());
55 if (data == MAP_FAILED) {
56 return DlErrorMessage { "DynamicLoader::try_create mmap" };
57 }
58
59 auto loader = adopt_ref(*new DynamicLoader(fd, move(filepath), data, size));
60 if (!loader->is_valid())
61 return DlErrorMessage { "ELF image validation failed" };
62 return loader;
63}
64
65DynamicLoader::DynamicLoader(int fd, DeprecatedString filepath, void* data, size_t size)
66 : m_filepath(move(filepath))
67 , m_file_size(size)
68 , m_image_fd(fd)
69 , m_file_data(data)
70{
71 m_elf_image = adopt_own(*new ELF::Image((u8*)m_file_data, m_file_size));
72 m_valid = validate();
73 if (m_valid)
74 find_tls_size_and_alignment();
75 else
76 dbgln("Image validation failed for file {}", m_filepath);
77}
78
79DynamicLoader::~DynamicLoader()
80{
81 if (munmap(m_file_data, m_file_size) < 0) {
82 perror("munmap");
83 VERIFY_NOT_REACHED();
84 }
85 if (close(m_image_fd) < 0) {
86 perror("close");
87 VERIFY_NOT_REACHED();
88 }
89}
90
91DynamicObject const& DynamicLoader::dynamic_object() const
92{
93 if (!m_cached_dynamic_object) {
94 VirtualAddress dynamic_section_address;
95
96 image().for_each_program_header([&dynamic_section_address](auto program_header) {
97 if (program_header.type() == PT_DYNAMIC) {
98 dynamic_section_address = VirtualAddress(program_header.raw_data());
99 }
100 });
101 VERIFY(!dynamic_section_address.is_null());
102
103 m_cached_dynamic_object = ELF::DynamicObject::create(m_filepath, VirtualAddress(image().base_address()), dynamic_section_address);
104 }
105 return *m_cached_dynamic_object;
106}
107
108void DynamicLoader::find_tls_size_and_alignment()
109{
110 image().for_each_program_header([this](auto program_header) {
111 if (program_header.type() == PT_TLS) {
112 m_tls_size_of_current_object = program_header.size_in_memory();
113 auto alignment = program_header.alignment();
114 VERIFY(!alignment || is_power_of_two(alignment));
115 m_tls_alignment_of_current_object = alignment > 1 ? alignment : 0; // No need to reserve extra space for single byte alignment
116 return IterationDecision::Break;
117 }
118 return IterationDecision::Continue;
119 });
120}
121
122bool DynamicLoader::validate()
123{
124 if (!image().is_valid())
125 return false;
126
127 auto* elf_header = (ElfW(Ehdr)*)m_file_data;
128 if (!validate_elf_header(*elf_header, m_file_size))
129 return false;
130 auto result_or_error = validate_program_headers(*elf_header, m_file_size, { m_file_data, m_file_size });
131 if (result_or_error.is_error() || !result_or_error.value())
132 return false;
133 return true;
134}
135
136RefPtr<DynamicObject> DynamicLoader::map()
137{
138 if (m_dynamic_object) {
139 // Already mapped.
140 return nullptr;
141 }
142
143 if (!m_valid) {
144 dbgln("DynamicLoader::map failed: image is invalid");
145 return nullptr;
146 }
147
148 load_program_headers();
149
150 VERIFY(!m_base_address.is_null());
151
152 m_dynamic_object = DynamicObject::create(m_filepath, m_base_address, m_dynamic_section_address);
153 m_dynamic_object->set_tls_offset(m_tls_offset);
154 m_dynamic_object->set_tls_size(m_tls_size_of_current_object);
155
156 return m_dynamic_object;
157}
158
159bool DynamicLoader::link(unsigned flags)
160{
161 return load_stage_2(flags);
162}
163
164bool DynamicLoader::load_stage_2(unsigned flags)
165{
166 VERIFY(flags & RTLD_GLOBAL);
167
168 if (m_dynamic_object->has_text_relocations()) {
169 dbgln("\033[33mWarning:\033[0m Dynamic object {} has text relocations", m_dynamic_object->filepath());
170 for (auto& text_segment : m_text_segments) {
171 VERIFY(text_segment.address().get() != 0);
172
173#ifndef AK_OS_MACOS
174 // Remap this text region as private.
175 if (mremap(text_segment.address().as_ptr(), text_segment.size(), text_segment.size(), MAP_PRIVATE) == MAP_FAILED) {
176 perror("mremap .text: MAP_PRIVATE");
177 return false;
178 }
179#endif
180
181 if (0 > mprotect(text_segment.address().as_ptr(), text_segment.size(), PROT_READ | PROT_WRITE)) {
182 perror("mprotect .text: PROT_READ | PROT_WRITE"); // FIXME: dlerror?
183 return false;
184 }
185 }
186 } else {
187 // .text needs to be executable while we process relocations because it might contain IFUNC resolvers.
188 // We don't allow IFUNC resolvers in objects with textrels.
189 for (auto& text_segment : m_text_segments) {
190 if (mprotect(text_segment.address().as_ptr(), text_segment.size(), PROT_READ | PROT_EXEC) < 0) {
191 perror("mprotect .text: PROT_READ | PROT_EXEC");
192 return false;
193 }
194 }
195 }
196 do_main_relocations();
197 return true;
198}
199
200void DynamicLoader::do_main_relocations()
201{
202 auto do_single_relocation = [&](const ELF::DynamicObject::Relocation& relocation) {
203 switch (do_relocation(relocation, ShouldInitializeWeak::No)) {
204 case RelocationResult::Failed:
205 dbgln("Loader.so: {} unresolved symbol '{}'", m_filepath, relocation.symbol().name());
206 VERIFY_NOT_REACHED();
207 case RelocationResult::ResolveLater:
208 m_unresolved_relocations.append(relocation);
209 break;
210 case RelocationResult::Success:
211 break;
212 }
213 };
214
215 do_relr_relocations();
216 m_dynamic_object->relocation_section().for_each_relocation(do_single_relocation);
217 m_dynamic_object->plt_relocation_section().for_each_relocation(do_single_relocation);
218}
219
220Result<NonnullRefPtr<DynamicObject>, DlErrorMessage> DynamicLoader::load_stage_3(unsigned flags)
221{
222 do_lazy_relocations();
223 if (flags & RTLD_LAZY) {
224 if (m_dynamic_object->has_plt())
225 setup_plt_trampoline();
226 }
227
228 if (m_dynamic_object->has_text_relocations()) {
229 // If we don't have textrels, .text has already been made executable by this point in load_stage_2.
230 for (auto& text_segment : m_text_segments) {
231 if (mprotect(text_segment.address().as_ptr(), text_segment.size(), PROT_READ | PROT_EXEC) < 0) {
232 return DlErrorMessage { DeprecatedString::formatted("mprotect .text: PROT_READ | PROT_EXEC: {}", strerror(errno)) };
233 }
234 }
235 }
236
237 if (m_relro_segment_size) {
238 if (mprotect(m_relro_segment_address.as_ptr(), m_relro_segment_size, PROT_READ) < 0) {
239 return DlErrorMessage { DeprecatedString::formatted("mprotect .relro: PROT_READ: {}", strerror(errno)) };
240 }
241
242#ifdef AK_OS_SERENITY
243 if (set_mmap_name(m_relro_segment_address.as_ptr(), m_relro_segment_size, DeprecatedString::formatted("{}: .relro", m_filepath).characters()) < 0) {
244 return DlErrorMessage { DeprecatedString::formatted("set_mmap_name .relro: {}", strerror(errno)) };
245 }
246#endif
247 }
248
249 m_fully_relocated = true;
250
251 return NonnullRefPtr<DynamicObject> { *m_dynamic_object };
252}
253
254void DynamicLoader::load_stage_4()
255{
256 call_object_init_functions();
257
258 m_fully_initialized = true;
259}
260
261void DynamicLoader::do_lazy_relocations()
262{
263 for (auto const& relocation : m_unresolved_relocations) {
264 if (auto res = do_relocation(relocation, ShouldInitializeWeak::Yes); res != RelocationResult::Success) {
265 dbgln("Loader.so: {} unresolved symbol '{}'", m_filepath, relocation.symbol().name());
266 VERIFY_NOT_REACHED();
267 }
268 }
269}
270
271void DynamicLoader::load_program_headers()
272{
273 FlatPtr ph_load_start = SIZE_MAX;
274 FlatPtr ph_load_end = 0;
275
276 // We walk the program header list once to find the requested address ranges of the program.
277 // We don't fill in the list of regions yet to keep malloc memory blocks from interfering with our reservation.
278 image().for_each_program_header([&](Image::ProgramHeader const& program_header) {
279 if (program_header.type() != PT_LOAD)
280 return;
281
282 FlatPtr section_start = program_header.vaddr().get();
283 FlatPtr section_end = section_start + program_header.size_in_memory();
284
285 if (ph_load_start > section_start)
286 ph_load_start = section_start;
287
288 if (ph_load_end < section_end)
289 ph_load_end = section_end;
290 });
291
292 void* requested_load_address = image().is_dynamic() ? nullptr : reinterpret_cast<void*>(ph_load_start);
293
294 int reservation_mmap_flags = MAP_ANON | MAP_PRIVATE | MAP_NORESERVE;
295 if (image().is_dynamic())
296 reservation_mmap_flags |= MAP_RANDOMIZED;
297#ifdef MAP_FIXED_NOREPLACE
298 else
299 reservation_mmap_flags |= MAP_FIXED_NOREPLACE;
300#endif
301
302 // First, we make a dummy reservation mapping, in order to allocate enough VM
303 // to hold all regions contiguously in the address space.
304
305 FlatPtr ph_load_base = ph_load_start & ~(FlatPtr)0xfffu;
306 ph_load_end = round_up_to_power_of_two(ph_load_end, PAGE_SIZE);
307
308 size_t total_mapping_size = ph_load_end - ph_load_base;
309
310 // Before we make our reservation, unmap our existing mapped ELF image that we used for reading header information.
311 // This leaves our pointers dangling momentarily, but it reduces the chance that we will conflict with ourselves.
312 if (munmap(m_file_data, m_file_size) < 0) {
313 perror("munmap old mapping");
314 VERIFY_NOT_REACHED();
315 }
316 m_elf_image = nullptr;
317 m_file_data = nullptr;
318
319 auto* reservation = mmap(requested_load_address, total_mapping_size, PROT_NONE, reservation_mmap_flags, 0, 0);
320 if (reservation == MAP_FAILED) {
321 perror("mmap reservation");
322 VERIFY_NOT_REACHED();
323 }
324
325 // Now that we can't accidentally block our requested space, re-map our ELF image.
326 DeprecatedString file_mmap_name = DeprecatedString::formatted("ELF_DYN: {}", m_filepath);
327 auto* data = mmap_with_name(nullptr, m_file_size, PROT_READ, MAP_SHARED, m_image_fd, 0, file_mmap_name.characters());
328 if (data == MAP_FAILED) {
329 perror("mmap new mapping");
330 VERIFY_NOT_REACHED();
331 }
332
333 m_file_data = data;
334 m_elf_image = adopt_own(*new ELF::Image((u8*)m_file_data, m_file_size));
335
336 VERIFY(requested_load_address == nullptr || reservation == requested_load_address);
337
338 m_base_address = VirtualAddress { reservation };
339
340 // Then we unmap the reservation.
341 if (munmap(reservation, total_mapping_size) < 0) {
342 perror("munmap reservation");
343 VERIFY_NOT_REACHED();
344 }
345
346 // Most binaries have four loadable regions, three of which are mapped
347 // (symbol tables/relocation information, executable instructions, read-only data)
348 // and one of which is copied (modifiable data).
349 // These are allocated in-line to cut down on the malloc calls.
350 Vector<ProgramHeaderRegion, 4> load_regions;
351 Vector<ProgramHeaderRegion, 3> map_regions;
352 Vector<ProgramHeaderRegion, 1> copy_regions;
353 Optional<ProgramHeaderRegion> relro_region;
354
355 VirtualAddress dynamic_region_desired_vaddr;
356
357 image().for_each_program_header([&](Image::ProgramHeader const& program_header) {
358 ProgramHeaderRegion region {};
359 region.set_program_header(program_header.raw_header());
360 if (region.is_tls_template()) {
361 // Skip, this is handled in DynamicLoader::copy_initial_tls_data_into.
362 } else if (region.is_load()) {
363 if (region.size_in_memory() == 0)
364 return;
365 load_regions.append(region);
366 if (region.is_writable()) {
367 copy_regions.append(region);
368 } else {
369 map_regions.append(region);
370 }
371 } else if (region.is_dynamic()) {
372 dynamic_region_desired_vaddr = region.desired_load_address();
373 } else if (region.is_relro()) {
374 VERIFY(!relro_region.has_value());
375 relro_region = region;
376 }
377 });
378
379 VERIFY(!map_regions.is_empty() || !copy_regions.is_empty());
380
381 auto compare_load_address = [](ProgramHeaderRegion& a, ProgramHeaderRegion& b) {
382 return a.desired_load_address().as_ptr() < b.desired_load_address().as_ptr();
383 };
384
385 quick_sort(load_regions, compare_load_address);
386 quick_sort(map_regions, compare_load_address);
387 quick_sort(copy_regions, compare_load_address);
388
389 // Process regions in order: .text, .data, .tls
390 for (auto& region : map_regions) {
391 FlatPtr ph_desired_base = region.desired_load_address().get();
392 FlatPtr ph_base = region.desired_load_address().page_base().get();
393 FlatPtr ph_end = ph_base + round_up_to_power_of_two(region.size_in_memory() + region.desired_load_address().get() - ph_base, PAGE_SIZE);
394
395 StringBuilder builder;
396 builder.append(m_filepath);
397 if (region.is_executable())
398 builder.append(": .text"sv);
399 else
400 builder.append(": .rodata"sv);
401
402 // Now we can map the text segment at the reserved address.
403 auto* segment_base = (u8*)mmap_with_name(
404 (u8*)reservation + ph_base - ph_load_base,
405 ph_desired_base - ph_base + region.size_in_image(),
406 PROT_READ,
407 MAP_FILE | MAP_SHARED | MAP_FIXED,
408 m_image_fd,
409 VirtualAddress { region.offset() }.page_base().get(),
410 builder.to_deprecated_string().characters());
411
412 if (segment_base == MAP_FAILED) {
413 perror("mmap non-writable");
414 VERIFY_NOT_REACHED();
415 }
416
417 if (region.is_executable())
418 m_text_segments.append({ VirtualAddress { segment_base }, ph_end - ph_base });
419 }
420
421 VERIFY(requested_load_address == nullptr || requested_load_address == reservation);
422
423 if (relro_region.has_value()) {
424 m_relro_segment_size = relro_region->size_in_memory();
425 m_relro_segment_address = VirtualAddress { (u8*)reservation + relro_region->desired_load_address().get() - ph_load_base };
426 }
427
428 if (image().is_dynamic())
429 m_dynamic_section_address = VirtualAddress { (u8*)reservation + dynamic_region_desired_vaddr.get() - ph_load_base };
430 else
431 m_dynamic_section_address = dynamic_region_desired_vaddr;
432
433 for (auto& region : copy_regions) {
434 FlatPtr ph_data_base = region.desired_load_address().page_base().get();
435 FlatPtr ph_data_end = ph_data_base + round_up_to_power_of_two(region.size_in_memory() + region.desired_load_address().get() - ph_data_base, PAGE_SIZE);
436
437 auto* data_segment_address = (u8*)reservation + ph_data_base - ph_load_base;
438 size_t data_segment_size = ph_data_end - ph_data_base;
439
440 // Finally, we make an anonymous mapping for the data segment. Contents are then copied from the file.
441 auto* data_segment = (u8*)mmap_with_name(
442 data_segment_address,
443 data_segment_size,
444 PROT_READ | PROT_WRITE,
445 MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED,
446 0,
447 0,
448 DeprecatedString::formatted("{}: .data", m_filepath).characters());
449
450 if (MAP_FAILED == data_segment) {
451 perror("mmap writable");
452 VERIFY_NOT_REACHED();
453 }
454
455 VirtualAddress data_segment_start;
456 if (image().is_dynamic())
457 data_segment_start = VirtualAddress { (u8*)reservation + region.desired_load_address().get() };
458 else
459 data_segment_start = region.desired_load_address();
460
461 VERIFY(data_segment_start.as_ptr() + region.size_in_memory() <= data_segment + data_segment_size);
462
463 memcpy(data_segment_start.as_ptr(), (u8*)m_file_data + region.offset(), region.size_in_image());
464 }
465}
466
467DynamicLoader::RelocationResult DynamicLoader::do_relocation(const ELF::DynamicObject::Relocation& relocation, ShouldInitializeWeak should_initialize_weak)
468{
469 FlatPtr* patch_ptr = nullptr;
470 if (is_dynamic())
471 patch_ptr = (FlatPtr*)(m_dynamic_object->base_address().as_ptr() + relocation.offset());
472 else
473 patch_ptr = (FlatPtr*)(FlatPtr)relocation.offset();
474
475 auto call_ifunc_resolver = [](VirtualAddress address) {
476 return VirtualAddress { reinterpret_cast<DynamicObject::IfuncResolver>(address.get())() };
477 };
478
479 switch (relocation.type()) {
480
481 case R_X86_64_NONE:
482 // Apparently most loaders will just skip these?
483 // Seems if the 'link editor' generates one something is funky with your code
484 break;
485 case R_AARCH64_ABS64:
486 case R_X86_64_64: {
487 auto symbol = relocation.symbol();
488 auto res = lookup_symbol(symbol);
489 if (!res.has_value()) {
490 if (symbol.bind() == STB_WEAK)
491 return RelocationResult::ResolveLater;
492 dbgln("ERROR: symbol not found: {}.", symbol.name());
493 return RelocationResult::Failed;
494 }
495 auto symbol_address = res.value().address;
496 if (relocation.addend_used())
497 *patch_ptr = symbol_address.get() + relocation.addend();
498 else
499 *patch_ptr += symbol_address.get();
500 if (res.value().type == STT_GNU_IFUNC)
501 *patch_ptr = call_ifunc_resolver(VirtualAddress { *patch_ptr }).get();
502 break;
503 }
504 case R_AARCH64_GLOB_DAT:
505 case R_X86_64_GLOB_DAT: {
506 auto symbol = relocation.symbol();
507 auto res = lookup_symbol(symbol);
508 VirtualAddress symbol_location;
509 if (!res.has_value()) {
510 if (symbol.bind() == STB_WEAK) {
511 if (should_initialize_weak == ShouldInitializeWeak::No)
512 return RelocationResult::ResolveLater;
513 } else {
514 // Symbol not found
515 return RelocationResult::Failed;
516 }
517
518 symbol_location = VirtualAddress { (FlatPtr)0 };
519 } else {
520 symbol_location = res.value().address;
521 if (res.value().type == STT_GNU_IFUNC) {
522 if (res.value().dynamic_object != nullptr && res.value().dynamic_object->has_text_relocations()) {
523 dbgln("\033[31mError:\033[0m Refusing to call IFUNC resolver defined in an object with text relocations.");
524 return RelocationResult::Failed;
525 }
526 symbol_location = call_ifunc_resolver(symbol_location);
527 }
528 }
529 VERIFY(symbol_location != m_dynamic_object->base_address());
530 *patch_ptr = symbol_location.get();
531 break;
532 }
533 case R_AARCH64_RELATIVE:
534 case R_X86_64_RELATIVE: {
535 if (!image().is_dynamic())
536 break;
537 // FIXME: According to the spec, R_386_relative ones must be done first.
538 // We could explicitly do them first using m_number_of_relocations from DT_RELCOUNT
539 // However, our compiler is nice enough to put them at the front of the relocations for us :)
540 if (relocation.addend_used())
541 *patch_ptr = m_dynamic_object->base_address().offset(relocation.addend()).get();
542 else
543 *patch_ptr += m_dynamic_object->base_address().get();
544 break;
545 }
546 case R_AARCH64_TLS_TPREL64:
547 case R_X86_64_TPOFF64: {
548 auto symbol = relocation.symbol();
549 FlatPtr symbol_value;
550 DynamicObject const* dynamic_object_of_symbol;
551 if (relocation.symbol_index() != 0) {
552 auto res = lookup_symbol(symbol);
553 if (!res.has_value())
554 break;
555 VERIFY(symbol.type() != STT_GNU_IFUNC);
556 symbol_value = res.value().value;
557 dynamic_object_of_symbol = res.value().dynamic_object;
558 } else {
559 symbol_value = 0;
560 dynamic_object_of_symbol = &relocation.dynamic_object();
561 }
562 VERIFY(dynamic_object_of_symbol);
563 size_t addend = relocation.addend_used() ? relocation.addend() : *patch_ptr;
564
565 *patch_ptr = addend + dynamic_object_of_symbol->tls_offset().value() + symbol_value;
566
567 // At offset 0 there's the thread's ThreadSpecificData structure, we don't want to collide with it.
568 VERIFY(static_cast<ssize_t>(*patch_ptr) < 0);
569
570 break;
571 }
572 case R_AARCH64_JUMP_SLOT:
573 case R_X86_64_JUMP_SLOT: {
574 // FIXME: Or BIND_NOW flag passed in?
575 if (m_dynamic_object->must_bind_now()) {
576 // Eagerly BIND_NOW the PLT entries, doing all the symbol looking goodness
577 // The patch method returns the address for the LAZY fixup path, but we don't need it here
578 m_dynamic_object->patch_plt_entry(relocation.offset_in_section());
579 } else {
580 auto relocation_address = (FlatPtr*)relocation.address().as_ptr();
581
582 if (image().is_dynamic())
583 *relocation_address += m_dynamic_object->base_address().get();
584 }
585 break;
586 }
587 case R_X86_64_IRELATIVE: {
588 VirtualAddress resolver;
589 if (relocation.addend_used())
590 resolver = m_dynamic_object->base_address().offset(relocation.addend());
591 else
592 resolver = m_dynamic_object->base_address().offset(*patch_ptr);
593
594 if (m_dynamic_object->has_text_relocations()) {
595 dbgln("\033[31mError:\033[0m Refusing to call IFUNC resolver defined in an object with text relocations.");
596 return RelocationResult::Failed;
597 }
598
599 *patch_ptr = call_ifunc_resolver(resolver).get();
600 break;
601 }
602 default:
603 // Raise the alarm! Someone needs to implement this relocation type
604 dbgln("Found a new exciting relocation type {}", relocation.type());
605 VERIFY_NOT_REACHED();
606 }
607 return RelocationResult::Success;
608}
609
610void DynamicLoader::do_relr_relocations()
611{
612 auto base_address = m_dynamic_object->base_address().get();
613 m_dynamic_object->for_each_relr_relocation([base_address](FlatPtr address) {
614 *(FlatPtr*)address += base_address;
615 });
616}
617
618void DynamicLoader::copy_initial_tls_data_into(ByteBuffer& buffer) const
619{
620 image().for_each_program_header([this, &buffer](ELF::Image::ProgramHeader program_header) {
621 if (program_header.type() != PT_TLS)
622 return IterationDecision::Continue;
623
624 // Note: The "size in image" is only concerned with initialized data. Uninitialized data (.tbss) is
625 // only included in the "size in memory" metric, and is expected to not be touched or read from, as
626 // it is not present in the image and zeroed out in-memory. We will still check that the buffer has
627 // space for both the initialized and the uninitialized data.
628 // Note: The m_tls_offset here is (of course) negative.
629 // TODO: Is the initialized data always in the beginning of the TLS segment, or should we walk the
630 // sections to figure that out?
631 size_t tls_start_in_buffer = buffer.size() + m_tls_offset;
632 VERIFY(program_header.size_in_image() <= program_header.size_in_memory());
633 VERIFY(program_header.size_in_memory() <= m_tls_size_of_current_object);
634 VERIFY(tls_start_in_buffer + program_header.size_in_memory() <= buffer.size());
635 memcpy(buffer.data() + tls_start_in_buffer, static_cast<const u8*>(m_file_data) + program_header.offset(), program_header.size_in_image());
636
637 return IterationDecision::Break;
638 });
639}
640
641// Defined in <arch>/plt_trampoline.S
642extern "C" void _plt_trampoline(void) __attribute__((visibility("hidden")));
643
644void DynamicLoader::setup_plt_trampoline()
645{
646 VERIFY(m_dynamic_object);
647 VERIFY(m_dynamic_object->has_plt());
648 VirtualAddress got_address = m_dynamic_object->plt_got_base_address();
649
650 auto* got_ptr = (FlatPtr*)got_address.as_ptr();
651 got_ptr[1] = (FlatPtr)m_dynamic_object.ptr();
652 got_ptr[2] = (FlatPtr)&_plt_trampoline;
653}
654
655// Called from our ASM routine _plt_trampoline.
656// Tell the compiler that it might be called from other places:
657extern "C" FlatPtr _fixup_plt_entry(DynamicObject* object, u32 relocation_offset);
658extern "C" FlatPtr _fixup_plt_entry(DynamicObject* object, u32 relocation_offset)
659{
660 return object->patch_plt_entry(relocation_offset).get();
661}
662
663void DynamicLoader::call_object_init_functions()
664{
665 typedef void (*InitFunc)();
666
667 if (m_dynamic_object->has_init_section()) {
668 auto init_function = (InitFunc)(m_dynamic_object->init_section().address().as_ptr());
669 (init_function)();
670 }
671
672 if (m_dynamic_object->has_init_array_section()) {
673 auto init_array_section = m_dynamic_object->init_array_section();
674
675 InitFunc* init_begin = (InitFunc*)(init_array_section.address().as_ptr());
676 InitFunc* init_end = init_begin + init_array_section.entry_count();
677 while (init_begin != init_end) {
678 // Android sources claim that these can be -1, to be ignored.
679 // 0 definitely shows up. Apparently 0/-1 are valid? Confusing.
680 if (!*init_begin || ((FlatPtr)*init_begin == (FlatPtr)-1))
681 continue;
682 (*init_begin)();
683 ++init_begin;
684 }
685 }
686}
687
688Optional<DynamicObject::SymbolLookupResult> DynamicLoader::lookup_symbol(const ELF::DynamicObject::Symbol& symbol)
689{
690 if (symbol.is_undefined() || symbol.bind() == STB_WEAK)
691 return DynamicLinker::lookup_global_symbol(symbol.name());
692
693 return DynamicObject::SymbolLookupResult { symbol.value(), symbol.size(), symbol.address(), symbol.bind(), symbol.type(), &symbol.object() };
694}
695
696} // end namespace ELF