diff options
author | Andreas Kling <awesomekling@gmail.com> | 2018-12-02 20:27:08 +0100 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2018-12-02 20:27:08 +0100 |
commit | 44036f32bc8146b566b793cdc6d58c3370a3ee0f (patch) | |
tree | 6a17eaee0646d32d21c812fe1e682c05c244ccc3 /Kernel/ELFLoader.cpp | |
parent | ac7a60225e9c2024a167d6f3f440780cf7af304a (diff) | |
download | serenity-44036f32bc8146b566b793cdc6d58c3370a3ee0f.zip |
Move ELFLoader code into Kernel/.
Diffstat (limited to 'Kernel/ELFLoader.cpp')
-rw-r--r-- | Kernel/ELFLoader.cpp | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/Kernel/ELFLoader.cpp b/Kernel/ELFLoader.cpp new file mode 100644 index 0000000000..324e2dff4b --- /dev/null +++ b/Kernel/ELFLoader.cpp @@ -0,0 +1,219 @@ +#include "ELFLoader.h" +#include <AK/kstdio.h> + +//#define ELFLOADER_DEBUG +//#define SUPPORT_RELOCATIONS + +ELFLoader::ELFLoader(const byte* buffer) + : m_image(buffer) +{ +} + +ELFLoader::~ELFLoader() +{ +} + +bool ELFLoader::load() +{ +#ifdef ELFLOADER_DEBUG + m_image.dump(); +#endif + if (!m_image.is_valid()) + return false; + + if (!layout()) + return false; +#ifdef SUPPORT_RELOCATIONS + if (!perform_relocations()) + return false; +#endif + + return true; +} + +bool ELFLoader::layout() +{ +#ifdef ELFLOADER_DEBUG + kprintf("ELFLoader: Layout\n"); +#endif + + bool failed = false; + m_image.for_each_program_header([&] (const ELFImage::ProgramHeader& program_header) { + if (program_header.type() != PT_LOAD) + return; +#ifdef ELFLOADER_DEBUG + kprintf("PH: L%x %u r:%u w:%u\n", program_header.laddr().get(), program_header.size_in_memory(), program_header.is_readable(), program_header.is_writable()); +#endif + if (program_header.is_writable()) { + allocate_section(program_header.laddr(), program_header.size_in_memory(), program_header.alignment(), program_header.is_readable(), program_header.is_writable()); + } else { + map_section(program_header.laddr(), program_header.size_in_memory(), program_header.alignment(), program_header.offset(), program_header.is_readable(), program_header.is_writable()); + } + }); + + m_image.for_each_section_of_type(SHT_PROGBITS, [] (const ELFImage::Section& section) { +#ifdef ELFLOADER_DEBUG + kprintf("ELFLoader: Copying progbits section: %s\n", section.name()); +#endif + if (!section.size()) + return true; + char* ptr = (char*)section.address(); + if (!ptr) { +#ifdef ELFLOADER_DEBUG + kprintf("ELFLoader: ignoring section '%s' with null address\n", section.name()); +#endif + return true; + } + // If this section isn't writable, it's already mmapped. + if (section.is_writable()) + memcpy(ptr, section.raw_data(), section.size()); +#ifdef SUPPORT_RELOCATIONS + m_sections.set(section.name(), move(ptr)); +#endif + return true; + }); + m_image.for_each_section_of_type(SHT_NOBITS, [&failed] (const ELFImage::Section& section) { +#ifdef ELFLOADER_DEBUG + kprintf("ELFLoader: Copying nobits section: %s\n", section.name()); +#endif + if (!section.size()) + return true; + char* ptr = (char*)section.address(); + if (!ptr) { + kprintf("ELFLoader: failed to allocate section '%s'\n", section.name()); + failed = true; + return false; + } + memset(ptr, 0, section.size()); +#ifdef SUPPORT_RELOCATIONS + m_sections.set(section.name(), move(ptr)); +#endif + return true; + }); + return !failed; +} + +#ifdef SUPPORT_RELOCATIONS +void* ELFLoader::lookup(const ELFImage::Symbol& symbol) +{ + if (symbol.section().is_undefined()) + return symbol_ptr(symbol.name()); + return area_for_section(symbol.section()) + symbol.value(); +} +#endif + +#ifdef SUPPORT_RELOCATIONS +char* ELFLoader::area_for_section(const ELFImage::Section& section) +{ + return area_for_section_name(section.name()); +} + +char* ELFLoader::area_for_section_name(const char* name) +{ + if (auto it = m_sections.find(name); it != m_sections.end()) + return (*it).value; + ASSERT_NOT_REACHED(); + return nullptr; +} +#endif + +#ifdef SUPPORT_RELOCATIONS +bool ELFLoader::perform_relocations() +{ +#ifdef ELFLOADER_DEBUG + kprintf("ELFLoader: Performing relocations\n"); +#endif + + bool failed = false; + + m_image.for_each_section_of_type(SHT_PROGBITS, [this, &failed] (const ELFImage::Section& section) -> bool { + auto& relocations = section.relocations(); + if (relocations.is_undefined()) + return true; + relocations.for_each_relocation([this, section, &failed] (const ELFImage::Relocation& relocation) { + auto symbol = relocation.symbol(); + auto& patch_ptr = *reinterpret_cast<ptrdiff_t*>(area_for_section(section) + relocation.offset()); + + switch (relocation.type()) { + case R_386_PC32: { + char* target_ptr = (char*)lookup(symbol); + if (!target_ptr) { + kprintf("ELFLoader: unresolved symbol '%s'\n", symbol.name()); + failed = true; + return false; + } + ptrdiff_t relativeOffset = (char*)target_ptr - ((char*)&patch_ptr + 4); +#ifdef ELFLOADER_DEBUG + kprintf("ELFLoader: Relocate PC32: offset=%x, symbol=%u(%s) value=%x target=%p, offset=%d\n", + relocation.offset(), + symbol.index(), + symbol.name(), + symbol.value(), + target_ptr, + relativeOffset + ); +#endif + patch_ptr = relativeOffset; + break; + } + case R_386_32: { +#ifdef ELFLOADER_DEBUG + kprintf("ELFLoader: Relocate Abs32: symbol=%u(%s), value=%x, section=%s\n", + symbol.index(), + symbol.name(), + symbol.value(), + symbol.section().name() + ); +#endif + char* target_ptr = area_for_section(symbol.section()) + symbol.value(); + patch_ptr += (ptrdiff_t)target_ptr; + break; + } + default: + ASSERT_NOT_REACHED(); + break; + } + return true; + }); + return !failed; + }); + return !failed; +} +#endif + +char* ELFLoader::symbol_ptr(const char* name) +{ + char* found_ptr = nullptr; + m_image.for_each_symbol([&] (const ELFImage::Symbol symbol) { + if (symbol.type() != STT_FUNC) + return true; + if (strcmp(symbol.name(), name)) + return true; + if (m_image.is_executable()) + found_ptr = (char*)symbol.value(); +#ifdef SUPPORT_RELOCATIONS + else if (m_image.is_relocatable()) + found_ptr = area_for_section(symbol.section()) + symbol.value(); +#endif + else + ASSERT_NOT_REACHED(); + return false; + }); + return found_ptr; +} + +bool ELFLoader::allocate_section(LinearAddress laddr, size_t size, size_t alignment, bool is_readable, bool is_writable) +{ + ASSERT(alloc_section_hook); + char namebuf[16]; + ksprintf(namebuf, "elf-alloc-%s%s", is_readable ? "r" : "", is_writable ? "w" : ""); + return alloc_section_hook(laddr, size, alignment, is_readable, is_writable, namebuf); +} + +bool ELFLoader::map_section(LinearAddress laddr, size_t size, size_t alignment, size_t offset_in_image, bool is_readable, bool is_writable) +{ + ASSERT(alloc_section_hook); + char namebuf[16]; + ksprintf(namebuf, "elf-map-%s%s", is_readable ? "r" : "", is_writable ? "w" : ""); + return map_section_hook(laddr, size, alignment, offset_in_image, is_readable, is_writable, namebuf); +} |