Serenity Operating System
at master 698 lines 26 kB view raw
1/* 2 * Copyright (c) 2020, Itamar S. <itamar8910@gmail.com> 3 * Copyright (c) 2021, Andreas Kling <kling@serenityos.org> 4 * Copyright (c) 2021, the SerenityOS developers. 5 * Copyright (c) 2022, Jesse Buhagiar <jooster669@gmail.com> 6 * 7 * SPDX-License-Identifier: BSD-2-Clause 8 */ 9 10#include <AK/ByteBuffer.h> 11#include <AK/Debug.h> 12#include <AK/HashMap.h> 13#include <AK/HashTable.h> 14#include <AK/LexicalPath.h> 15#include <AK/Platform.h> 16#include <AK/ScopeGuard.h> 17#include <AK/Vector.h> 18#include <Kernel/API/VirtualMemoryAnnotations.h> 19#include <Kernel/API/prctl_numbers.h> 20#include <LibC/bits/pthread_integration.h> 21#include <LibC/link.h> 22#include <LibC/sys/mman.h> 23#include <LibC/unistd.h> 24#include <LibELF/AuxiliaryVector.h> 25#include <LibELF/DynamicLinker.h> 26#include <LibELF/DynamicLoader.h> 27#include <LibELF/DynamicObject.h> 28#include <LibELF/Hashes.h> 29#include <bits/dlfcn_integration.h> 30#include <dlfcn.h> 31#include <fcntl.h> 32#include <pthread.h> 33#include <string.h> 34#include <sys/types.h> 35#include <syscall.h> 36 37namespace ELF { 38 39static HashMap<DeprecatedString, NonnullRefPtr<ELF::DynamicLoader>> s_loaders; 40static DeprecatedString s_main_program_path; 41static OrderedHashMap<DeprecatedString, NonnullRefPtr<ELF::DynamicObject>> s_global_objects; 42 43using EntryPointFunction = int (*)(int, char**, char**); 44using LibCExitFunction = void (*)(int); 45using DlIteratePhdrCallbackFunction = int (*)(struct dl_phdr_info*, size_t, void*); 46using DlIteratePhdrFunction = int (*)(DlIteratePhdrCallbackFunction, void*); 47 48extern "C" [[noreturn]] void _invoke_entry(int argc, char** argv, char** envp, EntryPointFunction entry); 49 50static size_t s_current_tls_offset = 0; 51static size_t s_total_tls_size = 0; 52static size_t s_allocated_tls_block_size = 0; 53static char** s_envp = nullptr; 54static LibCExitFunction s_libc_exit = nullptr; 55static __pthread_mutex_t s_loader_lock = __PTHREAD_MUTEX_INITIALIZER; 56static DeprecatedString s_cwd; 57 58static bool s_allowed_to_check_environment_variables { false }; 59static bool s_do_breakpoint_trap_before_entry { false }; 60static StringView s_ld_library_path; 61static StringView s_main_program_pledge_promises; 62static DeprecatedString s_loader_pledge_promises; 63 64static Result<void, DlErrorMessage> __dlclose(void* handle); 65static Result<void*, DlErrorMessage> __dlopen(char const* filename, int flags); 66static Result<void*, DlErrorMessage> __dlsym(void* handle, char const* symbol_name); 67static Result<void, DlErrorMessage> __dladdr(void* addr, Dl_info* info); 68 69Optional<DynamicObject::SymbolLookupResult> DynamicLinker::lookup_global_symbol(StringView name) 70{ 71 Optional<DynamicObject::SymbolLookupResult> weak_result; 72 73 auto symbol = DynamicObject::HashSymbol { name }; 74 75 for (auto& lib : s_global_objects) { 76 auto res = lib.value->lookup_symbol(symbol); 77 if (!res.has_value()) 78 continue; 79 if (res.value().bind == STB_GLOBAL) 80 return res; 81 if (res.value().bind == STB_WEAK && !weak_result.has_value()) 82 weak_result = res; 83 // We don't want to allow local symbols to be pulled in to other modules 84 } 85 return weak_result; 86} 87 88static Result<NonnullRefPtr<DynamicLoader>, DlErrorMessage> map_library(DeprecatedString const& filepath, int fd) 89{ 90 VERIFY(filepath.starts_with('/')); 91 92 auto loader = TRY(ELF::DynamicLoader::try_create(fd, filepath)); 93 94 s_loaders.set(filepath, *loader); 95 96 s_current_tls_offset -= loader->tls_size_of_current_object(); 97 if (loader->tls_alignment_of_current_object()) 98 s_current_tls_offset = align_down_to(s_current_tls_offset, loader->tls_alignment_of_current_object()); 99 loader->set_tls_offset(s_current_tls_offset); 100 101 // This actually maps the library at the intended and final place. 102 auto main_library_object = loader->map(); 103 s_global_objects.set(filepath, *main_library_object); 104 105 return loader; 106} 107 108Optional<DeprecatedString> DynamicLinker::resolve_library(DeprecatedString const& name, DynamicObject const& parent_object) 109{ 110 // Absolute and relative (to the current working directory) paths are already considered resolved. 111 // However, ensure that the returned path is absolute and canonical, so pass it through LexicalPath. 112 if (name.contains('/')) 113 return LexicalPath::absolute_path(s_cwd, name); 114 115 Vector<StringView> search_paths; 116 117 // Search RPATH values indicated by the ELF (only if RUNPATH is not present). 118 if (parent_object.runpath().is_empty()) 119 search_paths.extend(parent_object.rpath().split_view(':')); 120 121 // Scan the LD_LIBRARY_PATH environment variable if applicable. 122 search_paths.extend(s_ld_library_path.split_view(':')); 123 124 // Search RUNPATH values indicated by the ELF. 125 search_paths.extend(parent_object.runpath().split_view(':')); 126 127 // Last are the default search paths. 128 search_paths.append("/usr/lib"sv); 129 search_paths.append("/usr/local/lib"sv); 130 131 for (auto const& search_path : search_paths) { 132 LexicalPath library_path(search_path.replace("$ORIGIN"sv, LexicalPath::dirname(parent_object.filepath()), ReplaceMode::FirstOnly)); 133 DeprecatedString library_name = library_path.append(name).string(); 134 135 if (access(library_name.characters(), F_OK) == 0) { 136 if (!library_name.starts_with('/')) { 137 // FIXME: Non-absolute paths should resolve from the current working directory. However, 138 // since that's almost never the effect that is actually desired, let's print 139 // a warning and only implement it once something actually needs that behavior. 140 dbgln("\033[33mWarning:\033[0m Resolving library '{}' resulted in non-absolute path '{}'. Check your binary for relative RPATHs and RUNPATHs.", name, library_name); 141 } 142 143 return library_name; 144 } 145 } 146 147 return {}; 148} 149 150static Result<NonnullRefPtr<DynamicLoader>, DlErrorMessage> map_library(DeprecatedString const& path) 151{ 152 VERIFY(path.starts_with('/')); 153 154 int fd = open(path.characters(), O_RDONLY); 155 if (fd < 0) 156 return DlErrorMessage { DeprecatedString::formatted("Could not open shared library '{}': {}", path, strerror(errno)) }; 157 158 return map_library(path, fd); 159} 160 161static Vector<DeprecatedString> get_dependencies(DeprecatedString const& path) 162{ 163 VERIFY(path.starts_with('/')); 164 165 auto name = LexicalPath::basename(path); 166 auto lib = s_loaders.get(path).value(); 167 Vector<DeprecatedString> dependencies; 168 169 lib->for_each_needed_library([&dependencies, &name](auto needed_name) { 170 if (name == needed_name) 171 return; 172 dependencies.append(needed_name); 173 }); 174 return dependencies; 175} 176 177static Result<void, DlErrorMessage> map_dependencies(DeprecatedString const& path) 178{ 179 VERIFY(path.starts_with('/')); 180 181 dbgln_if(DYNAMIC_LOAD_DEBUG, "mapping dependencies for: {}", path); 182 183 auto const& parent_object = (*s_loaders.get(path))->dynamic_object(); 184 185 for (auto const& needed_name : get_dependencies(path)) { 186 dbgln_if(DYNAMIC_LOAD_DEBUG, "needed library: {}", needed_name.characters()); 187 188 auto dependency_path = DynamicLinker::resolve_library(needed_name, parent_object); 189 190 if (!dependency_path.has_value()) 191 return DlErrorMessage { DeprecatedString::formatted("Could not find required shared library: {}", needed_name) }; 192 193 if (!s_loaders.contains(dependency_path.value()) && !s_global_objects.contains(dependency_path.value())) { 194 auto loader = TRY(map_library(dependency_path.value())); 195 TRY(map_dependencies(loader->filepath())); 196 } 197 } 198 dbgln_if(DYNAMIC_LOAD_DEBUG, "mapped dependencies for {}", path); 199 return {}; 200} 201 202static void allocate_tls() 203{ 204 s_total_tls_size = 0; 205 for (auto const& data : s_loaders) { 206 dbgln_if(DYNAMIC_LOAD_DEBUG, "{}: TLS Size: {}, TLS Alignment: {}", data.key, data.value->tls_size_of_current_object(), data.value->tls_alignment_of_current_object()); 207 s_total_tls_size += data.value->tls_size_of_current_object() + data.value->tls_alignment_of_current_object(); 208 } 209 210 if (!s_total_tls_size) 211 return; 212 213 auto page_aligned_size = align_up_to(s_total_tls_size, PAGE_SIZE); 214 auto initial_tls_data_result = ByteBuffer::create_zeroed(page_aligned_size); 215 if (initial_tls_data_result.is_error()) { 216 dbgln("Failed to allocate initial TLS data"); 217 VERIFY_NOT_REACHED(); 218 } 219 220 auto& initial_tls_data = initial_tls_data_result.value(); 221 222 // Initialize TLS data 223 for (auto const& entry : s_loaders) { 224 entry.value->copy_initial_tls_data_into(initial_tls_data); 225 } 226 227 void* master_tls = ::allocate_tls((char*)initial_tls_data.data(), initial_tls_data.size()); 228 VERIFY(master_tls != (void*)-1); 229 dbgln_if(DYNAMIC_LOAD_DEBUG, "from userspace, master_tls: {:p}", master_tls); 230 231 s_allocated_tls_block_size = initial_tls_data.size(); 232} 233 234static int __dl_iterate_phdr(DlIteratePhdrCallbackFunction callback, void* data) 235{ 236 pthread_mutex_lock(&s_loader_lock); 237 ScopeGuard unlock_guard = [] { pthread_mutex_unlock(&s_loader_lock); }; 238 239 for (auto& it : s_global_objects) { 240 auto& object = it.value; 241 auto info = dl_phdr_info { 242 .dlpi_addr = (ElfW(Addr))object->base_address().as_ptr(), 243 .dlpi_name = object->filepath().characters(), 244 .dlpi_phdr = object->program_headers(), 245 .dlpi_phnum = object->program_header_count() 246 }; 247 248 auto res = callback(&info, sizeof(info), data); 249 if (res != 0) 250 return res; 251 } 252 253 return 0; 254} 255 256static void initialize_libc(DynamicObject& libc) 257{ 258 // Traditionally, `_start` of the main program initializes libc. 259 // However, since some libs use malloc() and getenv() in global constructors, 260 // we have to initialize libc just after it is loaded. 261 // Also, we can't just mark `__libc_init` with "__attribute__((constructor))" 262 // because it uses getenv() internally, so `environ` has to be initialized before we call `__libc_init`. 263 auto res = libc.lookup_symbol("environ"sv); 264 VERIFY(res.has_value()); 265 *((char***)res.value().address.as_ptr()) = s_envp; 266 267 // __stack_chk_guard should be initialized before anything significant (read: global constructors) is running. 268 // This is not done in __libc_init, as we definitely have to return from that, and it might affect Loader as well. 269 res = libc.lookup_symbol("__stack_chk_guard"sv); 270 VERIFY(res.has_value()); 271 void* stack_guard = res.value().address.as_ptr(); 272 arc4random_buf(stack_guard, sizeof(uintptr_t)); 273 274#ifdef AK_ARCH_64_BIT 275 // For 64-bit platforms we include an additional hardening: zero the first byte of the stack guard to avoid 276 // leaking or overwriting the stack guard with C-style string functions. 277 ((char*)stack_guard)[0] = 0; 278#endif 279 280 res = libc.lookup_symbol("__environ_is_malloced"sv); 281 VERIFY(res.has_value()); 282 *((bool*)res.value().address.as_ptr()) = false; 283 284 res = libc.lookup_symbol("exit"sv); 285 VERIFY(res.has_value()); 286 s_libc_exit = (LibCExitFunction)res.value().address.as_ptr(); 287 288 res = libc.lookup_symbol("__dl_iterate_phdr"sv); 289 VERIFY(res.has_value()); 290 *((DlIteratePhdrFunction*)res.value().address.as_ptr()) = __dl_iterate_phdr; 291 292 res = libc.lookup_symbol("__dlclose"sv); 293 VERIFY(res.has_value()); 294 *((DlCloseFunction*)res.value().address.as_ptr()) = __dlclose; 295 296 res = libc.lookup_symbol("__dlopen"sv); 297 VERIFY(res.has_value()); 298 *((DlOpenFunction*)res.value().address.as_ptr()) = __dlopen; 299 300 res = libc.lookup_symbol("__dlsym"sv); 301 VERIFY(res.has_value()); 302 *((DlSymFunction*)res.value().address.as_ptr()) = __dlsym; 303 304 res = libc.lookup_symbol("__dladdr"sv); 305 VERIFY(res.has_value()); 306 *((DlAddrFunction*)res.value().address.as_ptr()) = __dladdr; 307 308 res = libc.lookup_symbol("__libc_init"sv); 309 VERIFY(res.has_value()); 310 typedef void libc_init_func(); 311 ((libc_init_func*)res.value().address.as_ptr())(); 312} 313 314template<typename Callback> 315static void for_each_unfinished_dependency_of(DeprecatedString const& path, HashTable<DeprecatedString>& seen_names, Callback callback) 316{ 317 VERIFY(path.starts_with('/')); 318 319 auto loader = s_loaders.get(path); 320 321 if (!loader.has_value()) { 322 // Not having a loader here means that the library has already been loaded in at an earlier point, 323 // and the loader itself was cleared during the end of `linker_main`. 324 return; 325 } 326 327 if (loader.value()->is_fully_relocated()) { 328 if (!loader.value()->is_fully_initialized()) { 329 // If we are ending up here, that possibly means that this library either dlopens itself or a library that depends 330 // on it while running its initializers. Assuming that this is the only funny thing that the library does, there is 331 // a reasonable chance that nothing breaks, so just warn and continue. 332 dbgln("\033[33mWarning:\033[0m Querying for dependencies of '{}' while running its initializers", path); 333 } 334 335 return; 336 } 337 338 if (seen_names.contains(path)) 339 return; 340 seen_names.set(path); 341 342 for (auto const& needed_name : get_dependencies(path)) { 343 auto dependency_path = *DynamicLinker::resolve_library(needed_name, loader.value()->dynamic_object()); 344 for_each_unfinished_dependency_of(dependency_path, seen_names, callback); 345 } 346 347 callback(*s_loaders.get(path).value()); 348} 349 350static Vector<NonnullRefPtr<DynamicLoader>> collect_loaders_for_library(DeprecatedString const& path) 351{ 352 VERIFY(path.starts_with('/')); 353 354 HashTable<DeprecatedString> seen_names; 355 Vector<NonnullRefPtr<DynamicLoader>> loaders; 356 for_each_unfinished_dependency_of(path, seen_names, [&](auto& loader) { 357 loaders.append(loader); 358 }); 359 return loaders; 360} 361 362static void drop_loader_promise(StringView promise_to_drop) 363{ 364 if (s_main_program_pledge_promises.is_empty() || s_loader_pledge_promises.is_empty()) 365 return; 366 367 s_loader_pledge_promises = s_loader_pledge_promises.replace(promise_to_drop, ""sv, ReplaceMode::All); 368 369 auto extended_promises = DeprecatedString::formatted("{} {}", s_main_program_pledge_promises, s_loader_pledge_promises); 370 Syscall::SC_pledge_params params { 371 { extended_promises.characters(), extended_promises.length() }, 372 { nullptr, 0 }, 373 }; 374 int rc = syscall(SC_pledge, &params); 375 if (rc < 0 && rc > -EMAXERRNO) { 376 warnln("Failed to drop loader pledge promise: {}. errno={}", promise_to_drop, errno); 377 _exit(1); 378 } 379} 380 381static Result<void, DlErrorMessage> link_main_library(DeprecatedString const& path, int flags) 382{ 383 VERIFY(path.starts_with('/')); 384 385 auto loaders = collect_loaders_for_library(path); 386 387 for (auto& loader : loaders) { 388 auto dynamic_object = loader->map(); 389 if (dynamic_object) 390 s_global_objects.set(dynamic_object->filepath(), *dynamic_object); 391 } 392 393 for (auto& loader : loaders) { 394 bool success = loader->link(flags); 395 if (!success) { 396 return DlErrorMessage { DeprecatedString::formatted("Failed to link library {}", loader->filepath()) }; 397 } 398 } 399 400 for (auto& loader : loaders) { 401 auto result = loader->load_stage_3(flags); 402 VERIFY(!result.is_error()); 403 auto& object = result.value(); 404 405 if (loader->filepath().ends_with("/libc.so"sv)) { 406 initialize_libc(*object); 407 } 408 409 if (loader->filepath().ends_with("/libsystem.so"sv)) { 410 VERIFY(!loader->text_segments().is_empty()); 411 for (auto const& segment : loader->text_segments()) { 412 auto flags = static_cast<int>(VirtualMemoryRangeFlags::SyscallCode) | static_cast<int>(VirtualMemoryRangeFlags::Immutable); 413 if (syscall(SC_annotate_mapping, segment.address().get(), flags)) { 414 VERIFY_NOT_REACHED(); 415 } 416 } 417 } else { 418 for (auto const& segment : loader->text_segments()) { 419 auto flags = static_cast<int>(VirtualMemoryRangeFlags::Immutable); 420 if (syscall(SC_annotate_mapping, segment.address().get(), flags)) { 421 VERIFY_NOT_REACHED(); 422 } 423 } 424 } 425 } 426 427 drop_loader_promise("prot_exec"sv); 428 429 for (auto& loader : loaders) { 430 loader->load_stage_4(); 431 } 432 433 return {}; 434} 435 436static Result<void, DlErrorMessage> __dlclose(void* handle) 437{ 438 dbgln_if(DYNAMIC_LOAD_DEBUG, "__dlclose: {}", handle); 439 440 pthread_mutex_lock(&s_loader_lock); 441 ScopeGuard unlock_guard = [] { pthread_mutex_unlock(&s_loader_lock); }; 442 443 // FIXME: this will not currently destroy the dynamic object 444 // because we're intentionally holding a strong reference to it 445 // via s_global_objects until there's proper unload support. 446 auto object = static_cast<ELF::DynamicObject*>(handle); 447 object->unref(); 448 return {}; 449} 450 451static Optional<DlErrorMessage> verify_tls_for_dlopen(DynamicLoader const& loader) 452{ 453 if (loader.tls_size_of_current_object() == 0) 454 return {}; 455 456 if (s_total_tls_size + loader.tls_size_of_current_object() + loader.tls_alignment_of_current_object() > s_allocated_tls_block_size) 457 return DlErrorMessage("TLS size too large"); 458 459 bool tls_data_is_all_zero = true; 460 loader.image().for_each_program_header([&loader, &tls_data_is_all_zero](ELF::Image::ProgramHeader program_header) { 461 if (program_header.type() != PT_TLS) 462 return IterationDecision::Continue; 463 464 auto* tls_data = (const u8*)loader.image().base_address() + program_header.offset(); 465 for (size_t i = 0; i < program_header.size_in_image(); ++i) { 466 if (tls_data[i] != 0) { 467 tls_data_is_all_zero = false; 468 break; 469 } 470 } 471 return IterationDecision::Break; 472 }); 473 474 if (tls_data_is_all_zero) 475 return {}; 476 477 return DlErrorMessage("Using dlopen() with libraries that have non-zeroed TLS is currently not supported"); 478} 479 480static Result<void*, DlErrorMessage> __dlopen(char const* filename, int flags) 481{ 482 // FIXME: RTLD_NOW and RTLD_LOCAL are not supported 483 flags &= ~RTLD_NOW; 484 flags |= RTLD_LAZY; 485 flags &= ~RTLD_LOCAL; 486 flags |= RTLD_GLOBAL; 487 488 dbgln_if(DYNAMIC_LOAD_DEBUG, "__dlopen invoked, filename={}, flags={}", filename, flags); 489 490 if (pthread_mutex_trylock(&s_loader_lock) != 0) 491 return DlErrorMessage { "Nested calls to dlopen() are not permitted." }; 492 ScopeGuard unlock_guard = [] { pthread_mutex_unlock(&s_loader_lock); }; 493 494 auto const& parent_object = **s_global_objects.get(s_main_program_path); 495 496 auto library_path = (filename ? DynamicLinker::resolve_library(filename, parent_object) : s_main_program_path); 497 498 if (!library_path.has_value()) 499 return DlErrorMessage { DeprecatedString::formatted("Could not find required shared library: {}", filename) }; 500 501 auto existing_elf_object = s_global_objects.get(library_path.value()); 502 if (existing_elf_object.has_value()) { 503 // It's up to the caller to release the ref with dlclose(). 504 existing_elf_object.value()->ref(); 505 return *existing_elf_object; 506 } 507 508 auto loader = TRY(map_library(library_path.value())); 509 510 if (auto error = verify_tls_for_dlopen(loader); error.has_value()) 511 return error.value(); 512 513 TRY(map_dependencies(loader->filepath())); 514 515 TRY(link_main_library(loader->filepath(), flags)); 516 517 s_total_tls_size += loader->tls_size_of_current_object() + loader->tls_alignment_of_current_object(); 518 519 auto object = s_global_objects.get(library_path.value()); 520 if (!object.has_value()) 521 return DlErrorMessage { "Could not load ELF object." }; 522 523 // It's up to the caller to release the ref with dlclose(). 524 object.value()->ref(); 525 return *object; 526} 527 528static Result<void*, DlErrorMessage> __dlsym(void* handle, char const* symbol_name) 529{ 530 dbgln_if(DYNAMIC_LOAD_DEBUG, "__dlsym: {}, {}", handle, symbol_name); 531 532 pthread_mutex_lock(&s_loader_lock); 533 ScopeGuard unlock_guard = [] { pthread_mutex_unlock(&s_loader_lock); }; 534 535 StringView symbol_name_view { symbol_name, strlen(symbol_name) }; 536 Optional<DynamicObject::SymbolLookupResult> symbol; 537 538 if (handle) { 539 auto object = static_cast<DynamicObject*>(handle); 540 symbol = object->lookup_symbol(symbol_name_view); 541 } else { 542 // When handle is 0 (RTLD_DEFAULT) we should look up the symbol in all global modules 543 // https://pubs.opengroup.org/onlinepubs/009604499/functions/dlsym.html 544 symbol = DynamicLinker::lookup_global_symbol(symbol_name_view); 545 } 546 547 if (!symbol.has_value()) 548 return DlErrorMessage { DeprecatedString::formatted("Symbol {} not found", symbol_name_view) }; 549 550 if (symbol.value().type == STT_GNU_IFUNC) 551 return (void*)reinterpret_cast<DynamicObject::IfuncResolver>(symbol.value().address.as_ptr())(); 552 return symbol.value().address.as_ptr(); 553} 554 555static Result<void, DlErrorMessage> __dladdr(void* addr, Dl_info* info) 556{ 557 VirtualAddress user_addr { addr }; 558 pthread_mutex_lock(&s_loader_lock); 559 ScopeGuard unlock_guard = [] { pthread_mutex_unlock(&s_loader_lock); }; 560 561 RefPtr<DynamicObject> best_matching_library; 562 VirtualAddress best_library_offset; 563 for (auto& lib : s_global_objects) { 564 if (user_addr < lib.value->base_address()) 565 continue; 566 auto offset = user_addr - lib.value->base_address(); 567 if (!best_matching_library || offset < best_library_offset) { 568 best_matching_library = lib.value; 569 best_library_offset = offset; 570 } 571 } 572 573 if (!best_matching_library) { 574 return DlErrorMessage { "No library found which contains the specified address" }; 575 } 576 577 Optional<DynamicObject::Symbol> best_matching_symbol; 578 best_matching_library->for_each_symbol([&](auto const& symbol) { 579 if (user_addr < symbol.address() || user_addr > symbol.address().offset(symbol.size())) 580 return; 581 best_matching_symbol = symbol; 582 }); 583 584 info->dli_fbase = best_matching_library->base_address().as_ptr(); 585 // This works because we don't support unloading objects. 586 info->dli_fname = best_matching_library->filepath().characters(); 587 if (best_matching_symbol.has_value()) { 588 info->dli_saddr = best_matching_symbol.value().address().as_ptr(); 589 info->dli_sname = best_matching_symbol.value().raw_name(); 590 } else { 591 info->dli_saddr = nullptr; 592 info->dli_sname = nullptr; 593 } 594 return {}; 595} 596 597static void read_environment_variables() 598{ 599 for (char** env = s_envp; *env; ++env) { 600 StringView env_string { *env, strlen(*env) }; 601 if (env_string == "_LOADER_BREAKPOINT=1"sv) { 602 s_do_breakpoint_trap_before_entry = true; 603 } 604 605 constexpr auto library_path_string = "LD_LIBRARY_PATH="sv; 606 if (env_string.starts_with(library_path_string)) { 607 s_ld_library_path = env_string.substring_view(library_path_string.length()); 608 } 609 610 constexpr auto main_pledge_promises_key = "_LOADER_MAIN_PROGRAM_PLEDGE_PROMISES="sv; 611 if (env_string.starts_with(main_pledge_promises_key)) { 612 s_main_program_pledge_promises = env_string.substring_view(main_pledge_promises_key.length()); 613 } 614 615 constexpr auto loader_pledge_promises_key = "_LOADER_PLEDGE_PROMISES="sv; 616 if (env_string.starts_with(loader_pledge_promises_key)) { 617 s_loader_pledge_promises = env_string.substring_view(loader_pledge_promises_key.length()); 618 } 619 } 620} 621 622void ELF::DynamicLinker::linker_main(DeprecatedString&& main_program_path, int main_program_fd, bool is_secure, int argc, char** argv, char** envp) 623{ 624 VERIFY(main_program_path.starts_with('/')); 625 626 s_envp = envp; 627 628 char* raw_current_directory = getcwd(nullptr, 0); 629 s_cwd = raw_current_directory; 630 free(raw_current_directory); 631 632 s_allowed_to_check_environment_variables = !is_secure; 633 if (s_allowed_to_check_environment_variables) 634 read_environment_variables(); 635 636 s_main_program_path = main_program_path; 637 638 // NOTE: We always map the main library first, since it may require 639 // placement at a specific address. 640 auto result1 = map_library(main_program_path, main_program_fd); 641 if (result1.is_error()) { 642 warnln("{}", result1.error().text); 643 fflush(stderr); 644 _exit(1); 645 } 646 (void)result1.release_value(); 647 648 auto result2 = map_dependencies(main_program_path); 649 if (result2.is_error()) { 650 warnln("{}", result2.error().text); 651 fflush(stderr); 652 _exit(1); 653 } 654 655 dbgln_if(DYNAMIC_LOAD_DEBUG, "loaded all dependencies"); 656 for ([[maybe_unused]] auto& lib : s_loaders) { 657 dbgln_if(DYNAMIC_LOAD_DEBUG, "{} - tls size: {}, tls alignment: {}, tls offset: {}", lib.key, lib.value->tls_size_of_current_object(), lib.value->tls_alignment_of_current_object(), lib.value->tls_offset()); 658 } 659 660 allocate_tls(); 661 662 auto entry_point_function = [&main_program_path] { 663 auto result = link_main_library(main_program_path, RTLD_GLOBAL | RTLD_LAZY); 664 if (result.is_error()) { 665 warnln("{}", result.error().text); 666 _exit(1); 667 } 668 669 drop_loader_promise("rpath"sv); 670 671 auto& main_executable_loader = *s_loaders.get(main_program_path); 672 auto entry_point = main_executable_loader->image().entry(); 673 if (main_executable_loader->is_dynamic()) 674 entry_point = entry_point.offset(main_executable_loader->base_address().get()); 675 return (EntryPointFunction)(entry_point.as_ptr()); 676 }(); 677 678 s_loaders.clear(); 679 680 int rc = syscall(SC_prctl, PR_SET_NO_NEW_SYSCALL_REGION_ANNOTATIONS, 1, 0); 681 if (rc < 0) { 682 VERIFY_NOT_REACHED(); 683 } 684 685 dbgln_if(DYNAMIC_LOAD_DEBUG, "Jumping to entry point: {:p}", entry_point_function); 686 if (s_do_breakpoint_trap_before_entry) { 687#ifdef AK_ARCH_AARCH64 688 asm("brk #0"); 689#else 690 asm("int3"); 691#endif 692 } 693 694 _invoke_entry(argc, argv, envp, entry_point_function); 695 VERIFY_NOT_REACHED(); 696} 697 698}