diff --git a/CMakeLists.txt b/CMakeLists.txt index 74aa17df9d7..af0a4e8b575 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,7 +42,7 @@ add_custom_target(check-style USES_TERMINAL ) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-warning-option -Wall -Wextra -Werror -Wmissing-declarations -std=c++2a -fdiagnostics-color=always") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-warning-option -Wall -Wextra -Werror -Wmissing-declarations -std=c++2a -fdiagnostics-color=always -ftls-model=initial-exec") if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fconcepts") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") diff --git a/Demos/DynamicObject/lib.h b/Demos/DynamicObject/lib.h new file mode 100644 index 00000000000..eec49a1f4bf --- /dev/null +++ b/Demos/DynamicObject/lib.h @@ -0,0 +1,5 @@ + +int func(); + +extern __thread int g_tls1; +extern __thread int g_tls2; diff --git a/Libraries/LibC/dlfcn.cpp b/Libraries/LibC/dlfcn.cpp index 4fac569030f..04f742fcc30 100644 --- a/Libraries/LibC/dlfcn.cpp +++ b/Libraries/LibC/dlfcn.cpp @@ -101,7 +101,9 @@ void* dlopen(const char* filename, int flags) return nullptr; } - if (!loader->load_from_image(flags)) { + if (!loader->load_from_image(flags, + 0 // total_tls_size = 0, FIXME: Support TLS when using dlopen() + )) { g_dlerror_msg = String::format("Failed to load ELF object %s", filename); return nullptr; } diff --git a/Libraries/LibC/dlfcn.h b/Libraries/LibC/dlfcn.h index 619e6df5281..9363261ca68 100644 --- a/Libraries/LibC/dlfcn.h +++ b/Libraries/LibC/dlfcn.h @@ -31,10 +31,10 @@ __BEGIN_DECLS #define RTLD_DEFAULT 0 -#define RTLD_LAZY 1 -#define RTLD_NOW 2 -#define RTLD_GLOBAL 3 -#define RTLD_LOCAL 4 +#define RTLD_LAZY 2 +#define RTLD_NOW 4 +#define RTLD_GLOBAL 8 +#define RTLD_LOCAL 16 int dlclose(void*); char* dlerror(); diff --git a/Libraries/LibELF/DynamicLoader.cpp b/Libraries/LibELF/DynamicLoader.cpp index 1a4b4a6d110..ad5c486bc01 100644 --- a/Libraries/LibELF/DynamicLoader.cpp +++ b/Libraries/LibELF/DynamicLoader.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2019-2020, Andrew Kaster + * Copyright (c) 2020, Itamar S. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,7 +39,8 @@ #ifndef DYNAMIC_LOAD_DEBUG # define DYNAMIC_LOAD_DEBUG #endif -//#define DYNAMIC_LOAD_VERBOSE + +// #define DYNAMIC_LOAD_VERBOSE #ifdef DYNAMIC_LOAD_VERBOSE # define VERBOSE(fmt, ...) dbgprintf(fmt, ##__VA_ARGS__) @@ -57,31 +59,67 @@ static void* mmap_with_name(void* addr, size_t length, int prot, int flags, int namespace ELF { -static bool s_always_bind_now = false; +static bool s_always_bind_now = true; NonnullRefPtr DynamicLoader::construct(const char* filename, int fd, size_t size) { return adopt(*new DynamicLoader(filename, fd, size)); } +void* DynamicLoader::do_mmap(int fd, size_t size, const String& name) +{ + if (size < sizeof(Elf32_Ehdr)) + return MAP_FAILED; + + String file_mmap_name = String::format("ELF_DYN: %s", name.characters()); + return mmap_with_name(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0, file_mmap_name.characters()); +} + DynamicLoader::DynamicLoader(const char* filename, int fd, size_t size) : m_filename(filename) , m_file_size(size) , m_image_fd(fd) + , m_file_mapping(do_mmap(m_image_fd, m_file_size, m_filename)) + , m_elf_image((u8*)m_file_mapping, m_file_size) { - if (m_file_size < sizeof(Elf32_Ehdr)) { + + if (m_file_mapping == MAP_FAILED) { m_valid = false; return; } - String file_mmap_name = String::format("ELF_DYN: %s", m_filename.characters()); + m_tls_size = calculate_tls_size(); - m_file_mapping = mmap_with_name(nullptr, m_file_size, PROT_READ, MAP_PRIVATE, m_image_fd, 0, file_mmap_name.characters()); - if (MAP_FAILED == m_file_mapping) { - m_valid = false; - return; - } + m_valid = is_valid(); +} +RefPtr DynamicLoader::dynamic_object_from_image() const +{ + VirtualAddress dynamic_section_address; + + m_elf_image.for_each_program_header([&dynamic_section_address](auto program_header) { + if (program_header.type() == PT_DYNAMIC) { + dynamic_section_address = VirtualAddress(program_header.raw_data()); + } + }); + ASSERT(!dynamic_section_address.is_null()); + + return ELF::DynamicObject::construct(VirtualAddress(m_elf_image.base_address()), dynamic_section_address); +} + +size_t DynamicLoader::calculate_tls_size() const +{ + size_t tls_size = 0; + m_elf_image.for_each_program_header([&tls_size](auto program_header) { + if (program_header.type() == PT_TLS) { + tls_size = program_header.size_in_memory(); + } + }); + return tls_size; +} + +bool DynamicLoader::validate() +{ auto* elf_header = (Elf32_Ehdr*)m_file_mapping; if (!validate_elf_header(*elf_header, m_file_size) || !validate_program_headers(*elf_header, m_file_size, (u8*)m_file_mapping, m_file_size, &m_program_interpreter)) { @@ -105,35 +143,35 @@ void* DynamicLoader::symbol_for_name(const char* name) return m_dynamic_object->base_address().offset(symbol.value()).as_ptr(); } -bool DynamicLoader::load_from_image(unsigned flags) +RefPtr DynamicLoader::load_from_image(unsigned flags, size_t total_tls_size) { - Image elf_image((u8*)m_file_mapping, m_file_size); - m_valid = elf_image.is_valid() && elf_image.is_dynamic(); + m_valid = m_elf_image.is_valid() && m_elf_image.is_dynamic(); if (!m_valid) { - return false; + return nullptr; } #ifdef DYNAMIC_LOAD_VERBOSE - m_image->dump(); + // m_image->dump(); #endif - load_program_headers(elf_image); + load_program_headers(); - // Don't need this private mapping anymore - munmap(m_file_mapping, m_file_size); - m_file_mapping = MAP_FAILED; + m_dynamic_object = DynamicObject::construct(m_text_segment_load_address, m_dynamic_section_address); + m_dynamic_object->set_tls_offset(m_tls_offset); + m_dynamic_object->set_tls_size(m_tls_size); - m_dynamic_object = AK::make(m_text_segment_load_address, m_dynamic_section_address); - - return load_stage_2(flags); + auto rc = load_stage_2(flags, total_tls_size); + if (!rc) + return nullptr; + return m_dynamic_object; } -bool DynamicLoader::load_stage_2(unsigned flags) +bool DynamicLoader::load_stage_2(unsigned flags, size_t total_tls_size) { ASSERT(flags & RTLD_GLOBAL); - ASSERT(flags & RTLD_LAZY); + ASSERT(!(flags & RTLD_LAZY)); #ifdef DYNAMIC_LOAD_DEBUG m_dynamic_object->dump(); @@ -148,8 +186,12 @@ bool DynamicLoader::load_stage_2(unsigned flags) } } - do_relocations(); - setup_plt_trampoline(); + do_relocations(total_tls_size); + + if (flags & RTLD_LAZY) { + ASSERT_NOT_REACHED(); // TODO: Support lazy binding + setup_plt_trampoline(); + } // Clean up our setting of .text to PROT_READ | PROT_WRITE if (m_dynamic_object->has_text_relocations()) { @@ -167,7 +209,7 @@ bool DynamicLoader::load_stage_2(unsigned flags) return true; } -void DynamicLoader::load_program_headers(const Image& elf_image) +void DynamicLoader::load_program_headers() { Vector program_headers; @@ -176,7 +218,7 @@ void DynamicLoader::load_program_headers(const Image& elf_image) ProgramHeaderRegion* tls_region_ptr = nullptr; VirtualAddress dynamic_region_desired_vaddr; - elf_image.for_each_program_header([&](const Image::ProgramHeader& program_header) { + m_elf_image.for_each_program_header([&](const Image::ProgramHeader& program_header) { ProgramHeaderRegion new_region; new_region.set_program_header(program_header.raw_header()); program_headers.append(move(new_region)); @@ -214,27 +256,19 @@ void DynamicLoader::load_program_headers(const Image& elf_image) VirtualAddress data_segment_actual_addr = region->desired_load_address().offset((FlatPtr)text_segment_begin); memcpy(data_segment_actual_addr.as_ptr(), (u8*)m_file_mapping + region->offset(), region->size_in_image()); - // FIXME: Do some kind of 'allocate TLS section' or some such from a per-application pool - if (tls_region_ptr) { - region = tls_region_ptr; - // FIXME: This can't be right either. TLS needs some real work i'd say :) - m_tls_segment_address = tls_region_ptr->desired_load_address(); - VirtualAddress tls_segment_actual_addr = region->desired_load_address().offset((FlatPtr)text_segment_begin); - memcpy(tls_segment_actual_addr.as_ptr(), (u8*)m_file_mapping + region->offset(), region->size_in_image()); - } + // FIXME: Initialize the values in the TLS section. Currently, it will always be zeroed. } -void DynamicLoader::do_relocations() +void DynamicLoader::do_relocations(size_t total_tls_size) { - FlatPtr load_base_address = m_dynamic_object->base_address().get(); - - // FIXME: We should really bail on undefined symbols here. - auto main_relocation_section = m_dynamic_object->relocation_section(); - - main_relocation_section.for_each_relocation([&](const DynamicObject::Relocation& relocation) { - 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()); - u32* patch_ptr = (u32*)(load_base_address + relocation.offset()); + main_relocation_section.for_each_relocation([&](ELF::DynamicObject::Relocation relocation) { + VERBOSE("Relocation symbol: %s, type: %d\n", relocation.symbol().name(), relocation.type()); + u32* patch_ptr = (u32*)(m_dynamic_object->base_address().as_ptr() + relocation.offset()); + // VERBOSE("dynamic object name: %s\n", dynamic_object.object_name()); + VERBOSE("dynamic object base address: %p\n", m_dynamic_object->base_address()); + VERBOSE("relocation offset: 0x%x\n", relocation.offset()); + VERBOSE("patch_ptr: %p\n", patch_ptr); switch (relocation.type()) { case R_386_NONE: // Apparently most loaders will just skip these? @@ -244,7 +278,9 @@ void DynamicLoader::do_relocations() case R_386_32: { auto symbol = relocation.symbol(); VERBOSE("Absolute relocation: name: '%s', value: %p\n", symbol.name(), symbol.value()); - u32 symbol_address = symbol.value() + load_base_address; + auto res = lookup_symbol(symbol); + ASSERT(res.found); + u32 symbol_address = res.address; *patch_ptr += symbol_address; VERBOSE(" Symbol address: %p\n", *patch_ptr); break; @@ -252,15 +288,34 @@ void DynamicLoader::do_relocations() case R_386_PC32: { auto symbol = relocation.symbol(); VERBOSE("PC-relative relocation: '%s', value: %p\n", symbol.name(), symbol.value()); - u32 relative_offset = (symbol.value() - relocation.offset()); + auto res = lookup_symbol(symbol); + ASSERT(res.found); + u32 relative_offset = (res.address - (FlatPtr)(m_dynamic_object->base_address().as_ptr() + relocation.offset())); *patch_ptr += relative_offset; VERBOSE(" Symbol address: %p\n", *patch_ptr); break; } case R_386_GLOB_DAT: { auto symbol = relocation.symbol(); + if (!strcmp(symbol.name(), "__deregister_frame_info") || !strcmp(symbol.name(), "_ITM_registerTMCloneTable") + || !strcmp(symbol.name(), "_ITM_deregisterTMCloneTable") || !strcmp(symbol.name(), "__register_frame_info")) { + // We do not support these + break; + } VERBOSE("Global data relocation: '%s', value: %p\n", symbol.name(), symbol.value()); - u32 symbol_location = load_base_address + symbol.value(); + auto res = lookup_symbol(symbol); + VERBOSE("was symbol found? %d, address: 0x%x\n", res.found, res.address); + + if (!res.found) { + // TODO this is a hack + ASSERT(!strcmp(symbol.name(), "__deregister_frame_info") || !strcmp(symbol.name(), "_ITM_registerTMCloneTable") + || !strcmp(symbol.name(), "_ITM_deregisterTMCloneTable") || !strcmp(symbol.name(), "__register_frame_info")); + ASSERT_NOT_REACHED(); + return IterationDecision::Continue; + } + // ASSERT(res.found); + u32 symbol_location = res.address; + ASSERT(symbol_location != (FlatPtr)m_dynamic_object->base_address().as_ptr()); *patch_ptr = symbol_location; VERBOSE(" Symbol address: %p\n", *patch_ptr); break; @@ -270,33 +325,58 @@ void DynamicLoader::do_relocations() // We could explicitly do them first using m_number_of_relocatoins from DT_RELCOUNT // However, our compiler is nice enough to put them at the front of the relocations for us :) VERBOSE("Load address relocation at offset %X\n", relocation.offset()); - 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); - *patch_ptr += load_base_address; // + addend for RelA (addend for Rel is stored at addr) + VERBOSE(" patch ptr == %p, adding load base address (%p) to it and storing %p\n", *patch_ptr, m_dynamic_object->base_address().as_ptr(), *patch_ptr + m_dynamic_object->base_address().as_ptr()); + *patch_ptr += (FlatPtr)m_dynamic_object->base_address().as_ptr(); // + addend for RelA (addend for Rel is stored at addr) break; } + case R_386_TLS_TPOFF32: case R_386_TLS_TPOFF: { - VERBOSE("Relocation type: R_386_TLS_TPOFF at offset %X\n", relocation.offset()); - // FIXME: this can't be right? I have no idea what "negative offset into TLS storage" means... - // FIXME: Check m_has_static_tls and do something different for dynamic TLS - *patch_ptr = relocation.offset() - (FlatPtr)m_tls_segment_address.as_ptr() - *patch_ptr; + dbgprintf("Relocation type: R_386_TLS_TPOFF at offset %X\n", relocation.offset()); + auto symbol = relocation.symbol(); + // For some reason, LibC has a R_386_TLS_TPOFF that referes to the undefined symbol.. huh + if (relocation.symbol_index() == 0) + break; + dbgprintf("Symbol index: %d\n", symbol.index()); + dbgprintf("Symbol is_undefined?: %d\n", symbol.is_undefined()); + dbgprintf("TLS relocation: '%s', value: %p\n", symbol.name(), symbol.value()); + auto res = lookup_symbol(symbol); + if (!res.found) + break; + ASSERT(res.found); + u32 symbol_value = res.value; + dbgprintf("symbol value: %d\n", symbol_value); + const auto dynamic_object_of_symbol = res.dynamic_object; + ASSERT(dynamic_object_of_symbol); + size_t offset_of_tls_end = dynamic_object_of_symbol->tls_offset().value() + dynamic_object_of_symbol->tls_size().value(); + // size_t offset_of_tls_end = tls_offset() + tls_size(); + dbgprintf("patch ptr: 0x%x\n", patch_ptr); + dbgprintf("tls end offset: %d, total tls size: %d\n", offset_of_tls_end, total_tls_size); + *patch_ptr = (offset_of_tls_end - total_tls_size - symbol_value - sizeof(Elf32_Addr)); + dbgprintf("*patch ptr: %d\n", (i32)*patch_ptr); break; } default: // Raise the alarm! Someone needs to implement this relocation type - dbgprintf("Found a new exciting relocation type %d\n", relocation.type()); - printf("DynamicLoader: Found unknown relocation type %d\n", relocation.type()); + VERBOSE("Found a new exciting relocation type %d\n", relocation.type()); + // printf("DynamicLoader: Found unknown relocation type %d\n", relocation.type()); ASSERT_NOT_REACHED(); break; } return IterationDecision::Continue; }); + VERBOSE("plt relocations: 0x%x", m_dynamic_object->plt_relocation_section().address()); + VERBOSE("plt relocation count: 0x%x", m_dynamic_object->plt_relocation_section().address()); + VERBOSE("plt size: %d\n", m_dynamic_object->plt_relocation_section().size()); + VERBOSE("plt entry size: 0x%x\n", m_dynamic_object->plt_relocation_section().entry_size()); + // Handle PLT Global offset table relocations. m_dynamic_object->plt_relocation_section().for_each_relocation([&](const DynamicObject::Relocation& relocation) { // FIXME: Or BIND_NOW flag passed in? if (m_dynamic_object->must_bind_now() || s_always_bind_now) { // Eagerly BIND_NOW the PLT entries, doing all the symbol looking goodness // The patch method returns the address for the LAZY fixup path, but we don't need it here + VERBOSE("patching plt reloaction: 0x%x", relocation.offset_in_section()); (void)patch_plt_entry(relocation.offset_in_section()); } else { // LAZY-ily bind the PLT slots by just adding the base address to the offsets stored there @@ -305,7 +385,7 @@ void DynamicLoader::do_relocations() u8* relocation_address = relocation.address().as_ptr(); - *(u32*)relocation_address += load_base_address; + *(u32*)relocation_address += (FlatPtr)m_dynamic_object->base_address().as_ptr(); } return IterationDecision::Continue; }); @@ -349,7 +429,9 @@ Elf32_Addr DynamicLoader::patch_plt_entry(u32 relocation_offset) auto sym = relocation.symbol(); u8* relocation_address = relocation.address().as_ptr(); - u32 symbol_location = sym.address().get(); + auto res = lookup_symbol(sym); + ASSERT(res.found); + u32 symbol_location = res.address; VERBOSE("DynamicLoader: Jump slot relocation: putting %s (%p) into PLT at %p\n", sym.name(), symbol_location, relocation_address); @@ -360,28 +442,34 @@ Elf32_Addr DynamicLoader::patch_plt_entry(u32 relocation_offset) void DynamicLoader::call_object_init_functions() { + dbg() << "inside call_object_init_functions of " << m_filename; typedef void (*InitFunc)(); - auto init_function = (InitFunc)(m_dynamic_object->init_section().address().as_ptr()); -#ifdef DYNAMIC_LOAD_DEBUG - dbgprintf("Calling DT_INIT at %p\n", init_function); -#endif - (init_function)(); + if (m_dynamic_object->has_init_section()) { + auto init_function = (InitFunc)(m_dynamic_object->init_section().address().as_ptr()); - auto init_array_section = m_dynamic_object->init_array_section(); + // #ifdef DYNAMIC_LOAD_DEBUG + dbgprintf("Calling DT_INIT at %p\n", init_function); + // #endif + (init_function)(); + } - InitFunc* init_begin = (InitFunc*)(init_array_section.address().as_ptr()); - InitFunc* init_end = init_begin + init_array_section.entry_count(); - while (init_begin != init_end) { - // Android sources claim that these can be -1, to be ignored. - // 0 definitely shows up. Apparently 0/-1 are valid? Confusing. - if (!*init_begin || ((FlatPtr)*init_begin == (FlatPtr)-1)) - continue; -#ifdef DYNAMIC_LOAD_DEBUG - dbgprintf("Calling DT_INITARRAY entry at %p\n", *init_begin); -#endif - (*init_begin)(); - ++init_begin; + if (m_dynamic_object->has_init_array_section()) { + auto init_array_section = m_dynamic_object->init_array_section(); + + InitFunc* init_begin = (InitFunc*)(init_array_section.address().as_ptr()); + InitFunc* init_end = init_begin + init_array_section.entry_count(); + while (init_begin != init_end) { + // Android sources claim that these can be -1, to be ignored. + // 0 definitely shows up. Apparently 0/-1 are valid? Confusing. + if (!*init_begin || ((FlatPtr)*init_begin == (FlatPtr)-1)) + continue; + // #ifdef DYNAMIC_LOAD_DEBUG + dbgprintf("Calling DT_INITARRAY entry at %p\n", *init_begin); + // #endif + (*init_begin)(); + ++init_begin; + } } } @@ -394,4 +482,24 @@ u32 DynamicLoader::ProgramHeaderRegion::mmap_prot() const return prot; } +DynamicObject::SymbolLookupResult DynamicLoader::lookup_symbol(const ELF::DynamicObject::Symbol& symbol) const +{ + VERBOSE("looking up symbol: %s\n", symbol.name()); + if (!symbol.is_undefined()) { + VERBOSE("symbol is defiend in its object\n"); + return { true, symbol.value(), (FlatPtr)symbol.address().as_ptr(), &symbol.object() }; + } + ASSERT(m_global_symbol_lookup_func); + return m_global_symbol_lookup_func(symbol.name()); +} + +// Optional DynamicLoader::lookup_symbol(const char* name) const +// { +// ASSERT(m_dynamic_object); +// auto res = m_dynamic_object->hash_section().lookup_symbol(name); +// if (res.is_undefined()) +// return {}; +// return SymbolLookupResult { true, res.value(), (FlatPtr)res.address().as_ptr(), m_dynamic_object.ptr() }; +// } + } // end namespace ELF diff --git a/Libraries/LibELF/DynamicLoader.h b/Libraries/LibELF/DynamicLoader.h index 7bb2f396eb6..e812ab0d35e 100644 --- a/Libraries/LibELF/DynamicLoader.h +++ b/Libraries/LibELF/DynamicLoader.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2019-2020, Andrew Kaster + * Copyright (c) 2020, Itamar S. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -49,12 +50,12 @@ public: // Load a full ELF image from file into the current process and create an DynamicObject // from the SHT_DYNAMIC in the file. - bool load_from_image(unsigned flags); + RefPtr load_from_image(unsigned flags, size_t total_tls_size); // Stage 2 of loading: relocations and init functions // Assumes that the program headers have been loaded and that m_dynamic_object is initialized // Splitting loading like this allows us to use the same code to relocate a main executable as an elf binary - bool load_stage_2(unsigned flags); + bool load_stage_2(unsigned flags, size_t total_tls_size); // Intended for use by dlsym or other internal methods void* symbol_for_name(const char*); @@ -67,6 +68,21 @@ public: // Requested program interpreter from program headers. May be empty string StringView program_interpreter() const { return m_program_interpreter; } + VirtualAddress text_segment_load_addresss() const { return m_text_segment_load_address; } + + void set_tls_offset(size_t offset) { m_tls_offset = offset; }; + size_t tls_size() const { return m_tls_size; } + size_t tls_offset() const { return m_tls_offset; } + const ELF::Image& image() const { return m_elf_image; } + + template + void for_each_needed_library(F) const; + + using SymbolLookupFunction = DynamicObject::SymbolLookupResult (*)(const char*); + SymbolLookupFunction m_global_symbol_lookup_func { nullptr }; + + VirtualAddress text_segment_load_address() const { return m_text_segment_load_address; } + private: class ProgramHeaderRegion { public: @@ -94,31 +110,48 @@ private: Elf32_Phdr m_program_header; // Explicitly a copy of the PHDR in the image }; + static void* do_mmap(int fd, size_t size, const String& name); + RefPtr dynamic_object_from_image() const; + explicit DynamicLoader(const char* filename, int fd, size_t file_size); - explicit DynamicLoader(Elf32_Dyn* dynamic_location, Elf32_Addr load_address); // Stage 1 - void load_program_headers(const Image& elf_image); + void load_program_headers(); // Stage 2 - void do_relocations(); + void do_relocations(size_t total_tls_size); void setup_plt_trampoline(); void call_object_init_functions(); + bool validate(); + size_t calculate_tls_size() const; + + DynamicObject::SymbolLookupResult lookup_symbol(const ELF::DynamicObject::Symbol& symbol) const; + String m_filename; String m_program_interpreter; size_t m_file_size { 0 }; int m_image_fd { -1 }; void* m_file_mapping { MAP_FAILED }; + ELF::Image m_elf_image; bool m_valid { true }; - OwnPtr m_dynamic_object; + RefPtr m_dynamic_object; VirtualAddress m_text_segment_load_address; size_t m_text_segment_size { 0 }; VirtualAddress m_tls_segment_address; VirtualAddress m_dynamic_section_address; + + size_t m_tls_offset { 0 }; + size_t m_tls_size { 0 }; }; +template +void DynamicLoader::for_each_needed_library(F func) const +{ + dynamic_object_from_image()->for_each_needed_library(move(func)); +} + } // end namespace ELF diff --git a/Libraries/LibELF/DynamicObject.cpp b/Libraries/LibELF/DynamicObject.cpp index 9f104981cf5..bae2dd50245 100644 --- a/Libraries/LibELF/DynamicObject.cpp +++ b/Libraries/LibELF/DynamicObject.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2019-2020, Andrew Kaster + * Copyright (c) 2020, Itamar S. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -142,6 +143,13 @@ void DynamicObject::parse() m_soname_index = entry.val(); m_has_soname = true; break; + case DT_DEBUG: + break; + case DT_FLAGS_1: + break; + case DT_NEEDED: + // We handle these in for_each_needed_library + break; default: dbgprintf("DynamicObject: DYNAMIC tag handling not implemented for DT_%s\n", name_for_dtag(entry.tag())); printf("DynamicObject: DYNAMIC tag handling not implemented for DT_%s\n", name_for_dtag(entry.tag())); @@ -151,6 +159,13 @@ void DynamicObject::parse() return IterationDecision::Continue; }); + if (!m_size_of_relocation_entry) { + // TODO: FIXME, this shouldn't be hardcoded + // The reason we need this here is the for some reason, when there only PLT relocations, the compiler + // doesn't insert a 'PLTRELSZ' entry to the dynamic section + m_size_of_relocation_entry = sizeof(Elf32_Rel); + } + auto hash_section_address = hash_section().address().as_ptr(); auto num_hash_chains = ((u32*)hash_section_address)[1]; m_symbol_count = num_hash_chains; @@ -268,7 +283,7 @@ const DynamicObject::Symbol DynamicObject::HashSection::lookup_symbol(const char return symbol; } } - return m_dynamic.the_undefined_symbol(); + return Symbol::create_undefined(m_dynamic); } const char* DynamicObject::symbol_string_table_string(Elf32_Word index) const @@ -276,6 +291,12 @@ const char* DynamicObject::symbol_string_table_string(Elf32_Word index) const return (const char*)base_address().offset(m_string_table_offset + index).as_ptr(); } +DynamicObject::InitializationFunction DynamicObject::init_section_function() const +{ + ASSERT(has_init_section()); + return (InitializationFunction)init_section().address().as_ptr(); +} + static const char* name_for_dtag(Elf32_Sword d_tag) { switch (d_tag) { @@ -368,4 +389,17 @@ static const char* name_for_dtag(Elf32_Sword d_tag) } } +Optional DynamicObject::lookup_symbol(const char* name) const +{ + auto res = hash_section().lookup_symbol(name); + if (res.is_undefined()) + return {}; + return SymbolLookupResult { true, res.value(), (FlatPtr)res.address().as_ptr(), this }; +} + +NonnullRefPtr DynamicObject::construct(VirtualAddress base_address, VirtualAddress dynamic_section_address) +{ + return adopt(*new DynamicObject(base_address, dynamic_section_address)); +} + } // end namespace ELF diff --git a/Libraries/LibELF/DynamicObject.h b/Libraries/LibELF/DynamicObject.h index 0989c7d3ec1..5541f1fa3e2 100644 --- a/Libraries/LibELF/DynamicObject.h +++ b/Libraries/LibELF/DynamicObject.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2019-2020, Andrew Kaster + * Copyright (c) 2020, Itamar S. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,14 +28,16 @@ #pragma once #include +#include #include #include namespace ELF { -class DynamicObject { +class DynamicObject : public RefCounted { public: - explicit DynamicObject(VirtualAddress base_address, VirtualAddress dynamic_section_address); + static NonnullRefPtr construct(VirtualAddress base_address, VirtualAddress dynamic_section_address); + ~DynamicObject(); void dump() const; @@ -69,6 +72,23 @@ public: , m_sym(sym) , m_index(index) { + if (section_index() == 0) + m_is_undefined = true; + } + + Symbol(const Symbol& other) + : m_dynamic(other.m_dynamic) + , m_sym(other.m_sym) + , m_index(other.m_index) + , m_is_undefined(other.m_is_undefined) + { + } + + static Symbol create_undefined(const DynamicObject& dynamic) + { + auto s = Symbol(dynamic, 0, {}); + s.m_is_undefined = true; + return s; } ~Symbol() { } @@ -80,13 +100,19 @@ public: unsigned index() const { return m_index; } unsigned type() const { return ELF32_ST_TYPE(m_sym.st_info); } unsigned bind() const { return ELF32_ST_BIND(m_sym.st_info); } - bool is_undefined() const { return this == &m_dynamic.the_undefined_symbol(); } + + bool is_undefined() const + { + return m_is_undefined; + } VirtualAddress address() const { return m_dynamic.base_address().offset(value()); } + const DynamicObject& object() const { return m_dynamic; } private: const DynamicObject& m_dynamic; const Elf32_Sym& m_sym; const unsigned m_index; + bool m_is_undefined { false }; }; class Section { @@ -105,7 +131,10 @@ public: unsigned offset() const { return m_section_offset; } unsigned size() const { return m_section_size_bytes; } unsigned entry_size() const { return m_entry_size; } - unsigned entry_count() const { return !entry_size() ? 0 : size() / entry_size(); } + unsigned entry_count() const + { + return !entry_size() ? 0 : size() / entry_size(); + } VirtualAddress address() const { return m_dynamic.base_address().offset(m_section_offset); } protected: @@ -191,9 +220,13 @@ public: unsigned symbol_count() const { return m_symbol_count; } const Symbol symbol(unsigned) const; - const Symbol& the_undefined_symbol() const { return m_the_undefined_symbol; } + typedef void (*InitializationFunction)(); + + bool has_init_section() const { return m_init_offset != 0; } + bool has_init_array_section() const { return m_init_array_offset != 0; } const Section init_section() const; + InitializationFunction init_section_function() const; const Section fini_section() const; const Section init_array_section() const; const Section fini_array_section() const; @@ -215,7 +248,28 @@ public: const char* soname() const { return m_has_soname ? symbol_string_table_string(m_soname_index) : nullptr; } + Optional tls_offset() const { return m_tls_offset; } + Optional tls_size() const { return m_tls_size; } + void set_tls_offset(FlatPtr offset) { m_tls_offset = offset; } + void set_tls_size(FlatPtr size) { m_tls_size = size; } + + template + void for_each_needed_library(F) const; + + template + void for_each_initialization_array_function(F f) const; + + struct SymbolLookupResult { + bool found { false }; + FlatPtr value { 0 }; + FlatPtr address { 0 }; + const ELF::DynamicObject* dynamic_object { nullptr }; // The object in which the symbol is defined + }; + Optional lookup_symbol(const char* name) const; + private: + explicit DynamicObject(VirtualAddress base_address, VirtualAddress dynamic_section_address); + const char* symbol_string_table_string(Elf32_Word) const; void parse(); @@ -227,7 +281,6 @@ private: VirtualAddress m_base_address; VirtualAddress m_dynamic_address; - Symbol m_the_undefined_symbol { *this, 0, {} }; unsigned m_symbol_count { 0 }; @@ -264,6 +317,9 @@ private: bool m_has_soname { false }; Elf32_Word m_soname_index { 0 }; // Index into dynstr table for SONAME + + Optional m_tls_offset; + Optional m_tls_size; // End Section information from DT_* entries }; @@ -271,7 +327,10 @@ template inline void DynamicObject::RelocationSection::for_each_relocation(F func) const { for (unsigned i = 0; i < relocation_count(); ++i) { - if (func(relocation(i)) == IterationDecision::Break) + const auto reloc = relocation(i); + if (reloc.type() == 0) + continue; + if (func(reloc) == IterationDecision::Break) break; } } @@ -290,6 +349,7 @@ inline void DynamicObject::for_each_dynamic_entry(F func) const { auto* dyns = reinterpret_cast(m_dynamic_address.as_ptr()); for (unsigned i = 0;; ++i) { + // dbgprintf("%d\n", i); auto&& dyn = DynamicEntry(dyns[i]); if (dyn.tag() == DT_NULL) break; @@ -297,5 +357,30 @@ inline void DynamicObject::for_each_dynamic_entry(F func) const break; } } +template +inline void DynamicObject::for_each_needed_library(F func) const +{ + for_each_dynamic_entry([func, this](auto entry) { + if (entry.tag() != DT_NEEDED) + return IterationDecision::Continue; + Elf32_Word offset = entry.val(); + const char* name = (const char*)(m_base_address.offset(m_string_table_offset).offset(offset)).as_ptr(); + if (func(StringView(name)) == IterationDecision::Break) + return IterationDecision::Break; + return IterationDecision::Continue; + }); +} + +template +void DynamicObject::for_each_initialization_array_function(F f) const +{ + if (!has_init_array_section()) + return; + FlatPtr init_array = (FlatPtr)init_array_section().address().as_ptr(); + for (size_t i = 0; i < (m_init_array_size / sizeof(void*)); ++i) { + InitializationFunction current = ((InitializationFunction*)(init_array))[i]; + f(current); + } +} } // end namespace ELF diff --git a/Libraries/LibELF/Image.cpp b/Libraries/LibELF/Image.cpp index d2208234e86..8a5b7f56d88 100644 --- a/Libraries/LibELF/Image.cpp +++ b/Libraries/LibELF/Image.cpp @@ -259,6 +259,11 @@ const Image::ProgramHeader Image::program_header(unsigned index) const return ProgramHeader(*this, index); } +FlatPtr Image::program_header_table_offset() const +{ + return header().e_phoff; +} + const Image::Relocation Image::RelocationSection::relocation(unsigned index) const { ASSERT(index < relocation_count()); diff --git a/Libraries/LibELF/Image.h b/Libraries/LibELF/Image.h index 5f7540a3a60..5253ec8aa67 100644 --- a/Libraries/LibELF/Image.h +++ b/Libraries/LibELF/Image.h @@ -185,6 +185,7 @@ public: const Symbol symbol(unsigned) const; const Section section(unsigned) const; const ProgramHeader program_header(unsigned const) const; + FlatPtr program_header_table_offset() const; template void for_each_section(F) const; @@ -204,6 +205,8 @@ public: bool is_dynamic() const { return header().e_type == ET_DYN; } VirtualAddress entry() const { return VirtualAddress(header().e_entry); } + FlatPtr base_address() const { return (FlatPtr)m_buffer; } + size_t size() const { return m_size; } private: const char* raw_data(unsigned offset) const; diff --git a/Libraries/LibELF/exec_elf.h b/Libraries/LibELF/exec_elf.h index 03871e6e264..45a0e9401f4 100644 --- a/Libraries/LibELF/exec_elf.h +++ b/Libraries/LibELF/exec_elf.h @@ -778,15 +778,15 @@ struct elf_args { /* Relocation types */ #define R_386_NONE 0 -#define R_386_32 1 /* Symbol + Addend */ -#define R_386_PC32 2 /* Symbol + Addend - Section offset */ -#define R_386_GOT32 3 /* Used by build-time linker to create GOT entry */ -#define R_386_PLT32 4 /* Used by build-time linker to create PLT entry */ -#define R_386_COPY 5 /* https://docs.oracle.com/cd/E23824_01/html/819-0690/chapter4-10454.html#chapter4-84604 */ -#define R_386_GLOB_DAT 6 /* Relation b/w GOT entry and symbol */ -#define R_386_JMP_SLOT 7 /* Fixed up by dynamic loader */ -#define R_386_RELATIVE 8 /* Base address + Addned */ +#define R_386_32 1 /* Symbol + Addend */ +#define R_386_PC32 2 /* Symbol + Addend - Section offset */ +#define R_386_GOT32 3 /* Used by build-time linker to create GOT entry */ +#define R_386_PLT32 4 /* Used by build-time linker to create PLT entry */ +#define R_386_COPY 5 /* https://docs.oracle.com/cd/E23824_01/html/819-0690/chapter4-10454.html#chapter4-84604 */ +#define R_386_GLOB_DAT 6 /* Relation b/w GOT entry and symbol */ +#define R_386_JMP_SLOT 7 /* Fixed up by dynamic loader */ +#define R_386_RELATIVE 8 /* Base address + Addned */ #define R_386_TLS_TPOFF 14 /* Negative offset into the static TLS storage */ - +#define R_386_TLS_TPOFF32 37 #endif /* _SYS_EXEC_ELF_H_ */ diff --git a/Userland/CMakeLists.txt b/Userland/CMakeLists.txt index 77a338dd7c0..537180a29cf 100644 --- a/Userland/CMakeLists.txt +++ b/Userland/CMakeLists.txt @@ -48,3 +48,4 @@ target_link_libraries(grep LibRegex) target_link_libraries(gunzip LibCompress) add_subdirectory(Tests) +add_subdirectory(DynamicLoader) diff --git a/Userland/DynamicLoader/CMakeLists.txt b/Userland/DynamicLoader/CMakeLists.txt new file mode 100644 index 00000000000..2a89e994c6f --- /dev/null +++ b/Userland/DynamicLoader/CMakeLists.txt @@ -0,0 +1,22 @@ +set(LOADER_SOURCES + main.cpp + math.cpp + misc.cpp +) + +file(GLOB AK_SOURCES "../../AK/*.cpp") +file(GLOB ELF_SOURCES "../../Libraries/LibELF/*.cpp") +set(ELF_SOURCES ${ELF_SOURCES} ../../Libraries/LibELF/Arch/i386/plt_trampoline.S) +file(GLOB LIBC_SOURCES1 "../../Libraries/LibC/*.cpp") +file(GLOB LIBC_SOURCES2 "../../Libraries/LibC/*/*.cpp") +file(GLOB LIBC_SOURCES3 "../../Libraries/LibC/*.S") + +list(FILTER LIBC_SOURCES1 EXCLUDE REGEX ".+crt0.cpp") +list(FILTER LIBC_SOURCES1 EXCLUDE REGEX ".+crt0.+.cpp") + +set(SOURCES ${LOADER_SOURCES} ${AK_SOURCES} ${ELF_SOURCES} ${LIBC_SOURCES1} ${LIBC_SOURCES2} ${LIBC_SOURCES3}) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostdlib -pie -fpic -DNO_TLS") + +add_executable(Loader.so ${SOURCES}) +install(TARGETS Loader.so RUNTIME DESTINATION usr/lib/) diff --git a/Userland/DynamicLoader/main.cpp b/Userland/DynamicLoader/main.cpp new file mode 100644 index 00000000000..981ecf763d2 --- /dev/null +++ b/Userland/DynamicLoader/main.cpp @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2020, Itamar S. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// #define DYNAMIC_LOAD_VERBOSE + +#ifdef DYNAMIC_LOAD_VERBOSE +# define VERBOSE(fmt, ...) dbgprintf(fmt, ##__VA_ARGS__) +#else +# define VERBOSE(fmt, ...) \ + do { \ + } while (0) +#endif +#define TLS_VERBOSE(fmt, ...) dbgprintf(fmt, ##__VA_ARGS__) + +char* __static_environ[] = { nullptr }; // We don't get the environment without some libc workarounds.. + +static HashMap> g_loaders; +static HashMap> g_loaded_objects; + +static size_t g_current_tls_offset = 0; +static size_t g_total_tls_size = 0; + +static void init_libc() +{ + environ = __static_environ; + __environ_is_malloced = false; + __stdio_is_initialized = false; + __malloc_init(); +} + +static void perform_self_relocations() +{ + // We need to relocate ourselves. + // (these relocations seem to be generated because of our vtables) + + // TODO: Pass this address in the Auxiliary Vector + u32 base = 0x08000000; + Elf32_Ehdr* header = (Elf32_Ehdr*)(base); + Elf32_Phdr* pheader = (Elf32_Phdr*)(base + header->e_phoff); + u32 dynamic_section_addr = 0; + for (size_t i = 0; i < (size_t)header->e_phnum; ++i, ++pheader) { + if (pheader->p_type != PT_DYNAMIC) + continue; + dynamic_section_addr = pheader->p_vaddr + base; + } + if (!dynamic_section_addr) + exit(1); + + auto dynamic_object = ELF::DynamicObject::construct((VirtualAddress(base)), (VirtualAddress(dynamic_section_addr))); + + dynamic_object->relocation_section().for_each_relocation([base](auto& reloc) { + if (reloc.type() != R_386_RELATIVE) + return IterationDecision::Continue; + *(u32*)reloc.address().as_ptr() += base; + return IterationDecision::Continue; + }); +} + +static ELF::DynamicObject::SymbolLookupResult global_symbol_lookup(const char* symbol_name) +{ + VERBOSE("global symbol lookup: %s\n", symbol_name); + for (auto& lib : g_loaded_objects) { + VERBOSE("looking up in object: %s\n", lib.key.characters()); + auto res = lib.value->lookup_symbol(symbol_name); + if (!res.has_value()) + continue; + return res.value(); + } + ASSERT_NOT_REACHED(); + return {}; +} + +static void map_library(const String& name, int fd) +{ + struct stat lib_stat; + int rc = fstat(fd, &lib_stat); + ASSERT(!rc); + + auto loader = ELF::DynamicLoader::construct(name.characters(), fd, lib_stat.st_size); + loader->set_tls_offset(g_current_tls_offset); + loader->m_global_symbol_lookup_func = global_symbol_lookup; + + g_loaders.set(name, loader); + + g_current_tls_offset += loader->tls_size(); +} + +static void map_library(const String& name) +{ + // TODO: Do we want to also look for libs in other paths too? + String path = String::format("/usr/lib/%s", name.characters()); + int fd = open(path.characters(), O_RDONLY); + ASSERT(fd >= 0); + map_library(name, fd); +} + +static String get_library_name(const StringView& path) +{ + return LexicalPath(path).basename(); +} + +static void map_dependencies(const String& name) +{ + dbg() << "mapping dependencies for: " << name; + auto lib = g_loaders.get(name).value(); + lib->for_each_needed_library([](auto needed_name) { + dbg() << "needed library: " << needed_name; + String library_name = get_library_name(needed_name); + + if (!g_loaders.contains(library_name)) { + map_library(library_name); + map_dependencies(library_name); + } + return IterationDecision::Continue; + }); +} + +static void allocate_tls() +{ + size_t total_tls_size = 0; + for (const auto& data : g_loaders) { + total_tls_size += data.value->tls_size(); + } + if (total_tls_size) { + void* tls_address = allocate_tls(total_tls_size); + dbg() << "from userspace, tls_address: " << tls_address; + } + g_total_tls_size = total_tls_size; +} + +static void load_elf(const String& name) +{ + dbg() << "load_elf: " << name; + auto loader = g_loaders.get(name).value(); + loader->for_each_needed_library([](auto needed_name) { + dbg() << "needed library: " << needed_name; + String library_name = get_library_name(needed_name); + if (!g_loaded_objects.contains(library_name)) { + load_elf(library_name); + } + return IterationDecision::Continue; + }); + + auto dynamic_object = loader->load_from_image(RTLD_GLOBAL, g_total_tls_size); + ASSERT(!dynamic_object.is_null()); + g_loaded_objects.set(name, dynamic_object.release_nonnull()); +} + +static FlatPtr loader_main(auxv_t* auxvp) +{ + int main_program_fd = -1; + for (; auxvp->a_type != AT_NULL; ++auxvp) { + if (auxvp->a_type == AuxiliaryValue::ExecFileDescriptor) { + main_program_fd = auxvp->a_un.a_val; + } + } + ASSERT(main_program_fd >= 0); + + // TODO: Pass this in the auxiliary vector + const String main_program_name = "MainProgram"; + map_library(main_program_name, main_program_fd); + map_dependencies(main_program_name); + + dbg() << "loaded all dependencies"; + for (auto& lib : g_loaders) { + dbg() << lib.key << "- tls size: " << lib.value->tls_size() << ", tls offset: " << lib.value->tls_offset(); + } + + allocate_tls(); + + load_elf(main_program_name); + + auto main_program_lib = g_loaders.get(main_program_name).value(); + FlatPtr entry_point = reinterpret_cast(main_program_lib->image().entry().as_ptr() + (FlatPtr)main_program_lib->text_segment_load_address().as_ptr()); + dbg() << "entry point: " << (void*)entry_point; + + // This will unmap the temporary memory maps we had for loading the libraries + g_loaders.clear(); + + return entry_point; +} + +extern "C" { + +// The compiler expects a previous declaration +void _start(int, char**, char**); + +using MainFunction = int (*)(int, char**, char**); + +void _start(int argc, char** argv, char** envp) +{ + perform_self_relocations(); + init_libc(); + + char** env; + for (env = envp; *env; ++env) { + } + + auxv_t* auxvp = (auxv_t*)++env; + FlatPtr entry = loader_main(auxvp); + MainFunction main_function = (MainFunction)(entry); + dbg() << "jumping to main program entry point: " << (void*)main_function; + int rc = main_function(argc, argv, envp); + dbg() << "rc: " << rc; + sleep(100); + _exit(rc); +} +} diff --git a/Userland/DynamicLoader/math.cpp b/Userland/DynamicLoader/math.cpp new file mode 100644 index 00000000000..1953e79d6c4 --- /dev/null +++ b/Userland/DynamicLoader/math.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2020, the SerenityOS developers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// TODO: Is there a compiler flag we can use to still get these math functions? (and compile with -nostdlib) +// FIXME: What do we do with this regarding copyright stuff? +// code for math functions taken from here: +// https://gitlab.incom.co/CM-Shield/u-boot/commit/aa7839b39c2ee77f9ab8c393c56b8d812507dbb7 +// https://github.com/zayac/qemu-arm/blob/master/qemu/roms/ipxe/src/libgcc/__udivmoddi4.c +// https://code.woboq.org/llvm/compiler-rt/lib/builtins/divdi3.c.html + +#include "math.h" + +extern "C" { + +union overlay64 { + u64 longw; + struct { + u32 lower; + u32 higher; + } words; +}; + +u64 __ashldi3(u64 num, unsigned int shift) +{ + union overlay64 output; + + output.longw = num; + if (shift >= 32) { + output.words.higher = output.words.lower << (shift - 32); + output.words.lower = 0; + } else { + if (!shift) + return num; + output.words.higher = (output.words.higher << shift) | (output.words.lower >> (32 - shift)); + output.words.lower = output.words.lower << shift; + } + return output.longw; +} + +u64 __lshrdi3(u64 num, unsigned int shift) +{ + union overlay64 output; + + output.longw = num; + if (shift >= 32) { + output.words.lower = output.words.higher >> (shift - 32); + output.words.higher = 0; + } else { + if (!shift) + return num; + output.words.lower = output.words.lower >> shift | (output.words.higher << (32 - shift)); + output.words.higher = output.words.higher >> shift; + } + return output.longw; +} + +#define MAX_32BIT_UINT ((((u64)1) << 32) - 1) + +static u64 _64bit_divide(u64 dividend, u64 divider, u64* rem_p) +{ + u64 result = 0; + + /* + * If divider is zero - let the rest of the system care about the + * exception. + */ + if (!divider) + return 1 / (u32)divider; + + /* As an optimization, let's not use 64 bit division unless we must. */ + if (dividend <= MAX_32BIT_UINT) { + if (divider > MAX_32BIT_UINT) { + result = 0; + if (rem_p) + *rem_p = divider; + } else { + result = (u32)dividend / (u32)divider; + if (rem_p) + *rem_p = (u32)dividend % (u32)divider; + } + return result; + } + + while (divider <= dividend) { + u64 locald = divider; + u64 limit = __lshrdi3(dividend, 1); + int shifts = 0; + + while (locald <= limit) { + shifts++; + locald = locald + locald; + } + result |= __ashldi3(1, shifts); + dividend -= locald; + } + + if (rem_p) + *rem_p = dividend; + + return result; +} + +u64 __udivdi3(u64 num, u64 den) +{ + return _64bit_divide(num, den, nullptr); +} + +u64 __umoddi3(u64 num, u64 den) +{ + u64 v = 0; + + _64bit_divide(num, den, &v); + return v; +} + +uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t* rem_p) +{ + uint64_t quot = 0, qbit = 1; + + if (den == 0) { + return 1 / ((unsigned)den); /* Intentional divide by zero, without + triggering a compiler warning which + would abort the build */ + } + + /* Left-justify denominator and count shift */ + while ((int64_t)den >= 0) { + den <<= 1; + qbit <<= 1; + } + + while (qbit) { + if (den <= num) { + num -= den; + quot += qbit; + } + den >>= 1; + qbit >>= 1; + } + + if (rem_p) + *rem_p = num; + + return quot; +} +int64_t __divdi3(int64_t a, int64_t b) +{ + const int bits_in_dword_m1 = (int)(sizeof(int64_t) * 8) - 1; + int64_t s_a = a >> bits_in_dword_m1; // s_a = a < 0 ? -1 : 0 + int64_t s_b = b >> bits_in_dword_m1; // s_b = b < 0 ? -1 : 0 + a = (a ^ s_a) - s_a; // negate if s_a == -1 + b = (b ^ s_b) - s_b; // negate if s_b == -1 + s_a ^= s_b; // sign of quotient + return (__udivmoddi4(a, b, (uint64_t*)0) ^ s_a) - s_a; // negate if s_a == -1 +} +int64_t __moddi3(int64_t a, int64_t b) +{ + const int bits_in_dword_m1 = (int)(sizeof(int64_t) * 8) - 1; + int64_t s = b >> bits_in_dword_m1; // s = b < 0 ? -1 : 0 + b = (b ^ s) - s; // negate if s == -1 + s = a >> bits_in_dword_m1; // s = a < 0 ? -1 : 0 + a = (a ^ s) - s; // negate if s == -1 + uint64_t r; + __udivmoddi4(a, b, &r); + return ((int64_t)r ^ s) - s; // negate if s == -1 +} +} diff --git a/Userland/DynamicLoader/math.h b/Userland/DynamicLoader/math.h new file mode 100644 index 00000000000..4e272678c35 --- /dev/null +++ b/Userland/DynamicLoader/math.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020, the SerenityOS developers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +extern "C" { + +u64 __ashldi3(u64 num, unsigned int shift); + +u64 __lshrdi3(u64 num, unsigned int shift); + +u64 __udivdi3(u64 num, u64 den); + +u64 __umoddi3(u64 num, u64 den); + +uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t* rem_p); + +int64_t __divdi3(int64_t a, int64_t b); + +int64_t __moddi3(int64_t a, int64_t b); +} diff --git a/Userland/DynamicLoader/misc.cpp b/Userland/DynamicLoader/misc.cpp new file mode 100644 index 00000000000..18234a66367 --- /dev/null +++ b/Userland/DynamicLoader/misc.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2020, the SerenityOS developers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "misc.h" +#include "AK/LogStream.h" + +extern "C" { +const char* __cxa_demangle(const char*, void*, void*, int*) +{ + dbg() << "WARNING: __cxa_demangle not supported"; + return ""; +} + +// FIXME: Is this correct? +void* __dso_handle = nullptr; +} diff --git a/Userland/DynamicLoader/misc.h b/Userland/DynamicLoader/misc.h new file mode 100644 index 00000000000..6a23c396f31 --- /dev/null +++ b/Userland/DynamicLoader/misc.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2020, the SerenityOS developers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +extern "C" { +const char* __cxa_demangle(const char*, void*, void*, int*); + +extern void* __dso_handle; +}