1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-18 21:45:08 +00:00
serenity/ELFLoader/ELFLoader.cpp
Andreas Kling aa6d06b47e Use ELF program headers to load executables smarter.
This turned out way better than the old code. ELF loading is now quite
straightforward, and we don't need the weird concept of subregions anymore.

Next step is to respect the is_writable flag.
2018-11-03 11:29:30 +01:00

190 lines
5.8 KiB
C++

#include "ELFLoader.h"
#include <AK/kstdio.h>
//#define ELFLOADER_DEBUG
#ifdef SERENITY
ELFLoader::ELFLoader(ExecSpace& execSpace, ByteBuffer&& file)
#else
ELFLoader::ELFLoader(ExecSpace& execSpace, MappedFile&& file)
#endif
: m_execSpace(execSpace)
{
m_image = make<ELFImage>(move(file));
}
ELFLoader::~ELFLoader()
{
}
bool ELFLoader::load()
{
#ifdef ELFLOADER_DEBUG
m_image->dump();
#endif
if (!m_image->isValid())
return false;
if (!layout())
return false;
exportSymbols();
if (!performRelocations())
return false;
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
m_execSpace.allocate_section(program_header.laddr(), program_header.size_in_memory(), program_header.alignment(), program_header.is_readable(), program_header.is_writable());
});
m_image->forEachSectionOfType(SHT_PROGBITS, [this, &failed] (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) {
kprintf("ELFLoader: failed to allocate section '%s'\n", section.name());
failed = true;
return false;
}
memcpy(ptr, section.rawData(), section.size());
m_sections.set(section.name(), move(ptr));
return true;
});
m_image->forEachSectionOfType(SHT_NOBITS, [this, &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());
m_sections.set(section.name(), move(ptr));
return true;
});
return !failed;
}
void* ELFLoader::lookup(const ELFImage::Symbol& symbol)
{
if (symbol.section().isUndefined())
return m_execSpace.symbolPtr(symbol.name());
return areaForSection(symbol.section()) + symbol.value();
}
char* ELFLoader::areaForSection(const ELFImage::Section& section)
{
return areaForSectionName(section.name());
}
char* ELFLoader::areaForSectionName(const char* name)
{
if (auto it = m_sections.find(name); it != m_sections.end())
return (*it).value;
ASSERT_NOT_REACHED();
return nullptr;
}
bool ELFLoader::performRelocations()
{
#ifdef ELFLOADER_DEBUG
kprintf("ELFLoader: Performing relocations\n");
#endif
bool failed = false;
m_image->forEachSectionOfType(SHT_PROGBITS, [this, &failed] (const ELFImage::Section& section) -> bool {
auto& relocations = section.relocations();
if (relocations.isUndefined())
return true;
relocations.forEachRelocation([this, section, &failed] (const ELFImage::Relocation& relocation) {
auto symbol = relocation.symbol();
auto& patchPtr = *reinterpret_cast<ptrdiff_t*>(areaForSection(section) + relocation.offset());
switch (relocation.type()) {
case R_386_PC32: {
char* targetPtr = (char*)lookup(symbol);
if (!targetPtr) {
kprintf("ELFLoader: unresolved symbol '%s'\n", symbol.name());
failed = true;
return false;
}
ptrdiff_t relativeOffset = (char*)targetPtr - ((char*)&patchPtr + 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(),
targetPtr,
relativeOffset
);
#endif
patchPtr = 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* targetPtr = areaForSection(symbol.section()) + symbol.value();
patchPtr += (ptrdiff_t)targetPtr;
break;
}
default:
ASSERT_NOT_REACHED();
break;
}
return true;
});
return !failed;
});
return !failed;
}
void ELFLoader::exportSymbols()
{
m_image->forEachSymbol([&] (const ELFImage::Symbol symbol) {
#ifdef ELFLOADER_DEBUG
kprintf("symbol: %u, type=%u, name=%s, section=%u\n", symbol.index(), symbol.type(), symbol.name(), symbol.sectionIndex());
#endif
if (symbol.type() == STT_FUNC) {
char* ptr;
if (m_image->isExecutable())
ptr = (char*)symbol.value();
else if (m_image->isRelocatable())
ptr = areaForSection(symbol.section()) + symbol.value();
else
ASSERT_NOT_REACHED();
m_execSpace.addSymbol(symbol.name(), ptr, symbol.size());
}
// FIXME: What about other symbol types?
return true;
});
}