mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 23:50:19 +00:00
dea474dfd5
The ELF loader was doing huge amounts of unnecessary work. Got rid of the "export symbols" and relocation passes since we don't need them. They were useful things when bringing up the ELF loading code. Also added a simple TSC-based Stopwatch RAII thingy to help debug performance issues.
219 lines
6.9 KiB
C++
219 lines
6.9 KiB
C++
#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);
|
|
}
|