diff --git a/src/emucore/CartELF.cxx b/src/emucore/CartELF.cxx index 36ee834c9..6e757125a 100644 --- a/src/emucore/CartELF.cxx +++ b/src/emucore/CartELF.cxx @@ -60,6 +60,38 @@ namespace { 0xd0, 0xfb, // bne WaitForCart 0x4c, 0x00, 0x10 // jmp $1000 }; + +#ifdef DUMP_ELF + void dumpElf(const ElfParser& elfParser) { + cout << "ELF sections:" << std::endl << std::endl; + + size_t i = 0; + for (auto& section: elfParser.getSections()) { + if (section.type != 0x00) cout << i << " " << section << std::endl; + i++; + } + + auto symbols = elfParser.getSymbols(); + cout << std::endl << "ELF symbols:" << std::endl << std::endl; + if (symbols.size() > 0) { + i = 0; + for (auto& symbol: symbols) + cout << (i++) << " " << symbol << std::endl; + } + + i = 0; + for (auto& section: elfParser.getSections()) { + auto rels = elfParser.getRelocations(i++); + if (!rels) continue; + + cout + << std::endl << "ELF relocations for section " + << section.name << ":" << std::endl << std::endl; + + for (auto& rel: *rels) cout << rel << std::endl; + } + } +#endif } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -81,23 +113,9 @@ CartridgeELF::CartridgeELF(const ByteBuffer& image, size_t size, string_view md5 createRomAccessArrays(0x1000); - #ifdef DUMP_ELF - cout << "ELF sections:" << std::endl << std::endl; - - size_t i = 0; - for (auto& section: elfParser.getSections()) { - if (section.type != 0x00) cout << i << " " << section << std::endl; - i++; - } - - auto symbols = elfParser.getSymbols(); - cout << std::endl << "ELF symbols:" << std::endl << std::endl; - if (symbols.size() > 0) { - i = 0; - for (auto& symbol: symbols) - cout << (i++) << " " << symbol << std::endl; - } - #endif +#ifdef DUMP_ELF + dumpElf(elfParser); +#endif } diff --git a/src/emucore/elf/ElfParser.cxx b/src/emucore/elf/ElfParser.cxx index 5e00b9afa..109ed3291 100644 --- a/src/emucore/elf/ElfParser.cxx +++ b/src/emucore/elf/ElfParser.cxx @@ -7,6 +7,8 @@ namespace { constexpr uInt8 ELF_CLASS_32 = 1; constexpr uInt8 ELF_VERSION = 1; constexpr uInt32 SYMBOL_ENTRY_SIZE = 16; + constexpr uInt32 REL_ENTRY_SIZE = 8; + constexpr uInt32 RELA_ENTRY_SIZE = 12; } // namespace void ElfParser::parse(const uInt8 *elfData, size_t size) @@ -50,6 +52,7 @@ void ElfParser::parse(const uInt8 *elfData, size_t size) section.name = getName(shrstrtab, section.nameOffset); const Section* symtab = getSymtab(); + if (symtab) { const Section* strtab = getStrtab(); if (!strtab) EInvalidElf::raise("no string table to resolve symbol names"); @@ -59,6 +62,25 @@ void ElfParser::parse(const uInt8 *elfData, size_t size) for (size_t i = 0; i < symtab->size / SYMBOL_ENTRY_SIZE; i++) symbols.push_back(readSymbol(i, *symtab, *strtab)); } + + for (auto& section: sections) { + if (section.type != SHT_REL && section.type != SHT_RELA) continue; + if (section.info >= sections.size()) EInvalidElf::raise("relocation table for invalid section"); + + vector rels; + rels.reserve(section.size / (section.type == SHT_REL ? REL_ENTRY_SIZE : RELA_ENTRY_SIZE)); + + for (size_t i = 0; i < rels.capacity(); i++) { + Relocation rel = readRelocation(i, section); + + if (rel.symbol >= symbols.size()) EInvalidElf::raise("invalid relocation symbol"); + rel.symbolName = symbols[rel.symbol].name; + + rels.push_back(rel); + } + + relocations[section.info] = rels; + } } catch (const EInvalidElf &e) { EInvalidElf::raise("failed to parse ELF: " + string(e.what())); } @@ -87,6 +109,11 @@ ElfParser::getSection(const string &name) const { return optional
(); } +const optional> ElfParser::getRelocations(size_t section) const +{ + return relocations.contains(section) ? relocations.at(section) : optional>(); +} + uInt8 ElfParser::read8(uInt32 offset) const { if (offset >= size) @@ -133,12 +160,12 @@ ElfParser::Section ElfParser::readSection(uInt32 offset) const { ElfParser::Symbol ElfParser::readSymbol(uInt32 index, const Section& symSec, const Section& strSec) const { - Symbol sym; - uInt32 offset = index * SYMBOL_ENTRY_SIZE; if (offset + SYMBOL_ENTRY_SIZE > symSec.size) EInvalidElf::raise("symbol is beyond section"); offset += symSec.offset; + Symbol sym; + sym.nameOffset = read32(offset); sym.value = read32(offset + 0x04); sym.size = read32(offset + 0x08); @@ -146,13 +173,43 @@ ElfParser::Symbol ElfParser::readSymbol(uInt32 index, const Section& symSec, con sym.visibility = read8(offset + 0x0d); sym.section = read16(offset + 0x0e); - sym.name = getName(strSec, sym.nameOffset); + if ( + ((sym.section != SHN_ABS && sym.section != SHN_UND) || sym.type == STT_SECTION) && + sym.section >= sections.size() + ) + EInvalidElf::raise("symbol: section index out of range"); + sym.bind = sym.info >> 4; sym.type = sym.info & 0x0f; + sym.name = sym.type == STT_SECTION ? sections[sym.section].name : getName(strSec, sym.nameOffset); + return sym; } +ElfParser::Relocation ElfParser::readRelocation(uInt32 index, const Section& sec) const +{ + if (sec.type != SHT_REL && sec.type != SHT_RELA) + throw runtime_error("section is not RELA or REL"); + + const size_t size = sec.type == SHT_REL ? REL_ENTRY_SIZE : RELA_ENTRY_SIZE; + uInt32 offset = index * size; + + if (offset + size > sec.size) EInvalidElf::raise("relocation is beyond bounds"); + + offset += sec.offset; + Relocation rel; + + rel.address = read32(offset); + rel.info = read32(offset + 0x04); + rel.addend = sec.type == SHT_RELA ? read32(offset + 0x08) : 0; + + rel.symbol = rel.info >> 8; + rel.type = rel.info & 0x0f; + + return rel; +} + const char* ElfParser::getName(const Section& section, uInt32 offset) const { if (offset >= section.size) EInvalidElf::raise("name out of bounds"); @@ -220,6 +277,7 @@ ostream& operator<<(ostream& os, const ElfParser::Symbol symbol) reset.copyfmt(os); os + << symbol.nameOffset << " " << symbol.name << std::hex << std::setw(4) << std::setfill('0') << " value=0x" << symbol.value @@ -234,3 +292,23 @@ ostream& operator<<(ostream& os, const ElfParser::Symbol symbol) return os; } + +ostream& operator<<(ostream& os, const ElfParser::Relocation rel) +{ + std::ios reset(nullptr); + reset.copyfmt(os); + + os + << rel.symbolName << " :" + << std::hex << std::setw(4) << std::setfill('0') + << " address=0x" << rel.address + << " info=0x" << rel.info + << " addend=0x" << rel.addend + << " type=0x" << (int)rel.type; + + os.copyfmt(reset); + + os << " symbol=" << (int)rel.symbol; + + return os; +} diff --git a/src/emucore/elf/ElfParser.hxx b/src/emucore/elf/ElfParser.hxx index fca012c94..ca243b100 100644 --- a/src/emucore/elf/ElfParser.hxx +++ b/src/emucore/elf/ElfParser.hxx @@ -18,6 +18,8 @@ #ifndef ELF_PARSER #define ELF_PARSER +#include + #include "bspf.hxx" class ElfParser { @@ -78,6 +80,16 @@ class ElfParser { uInt8 type; }; + struct Relocation { + uInt32 address; + uInt32 info; + uInt32 addend; + + uInt32 symbol; + uInt8 type; + string symbolName; + }; + public: static constexpr uInt8 ENDIAN_LITTLE_ENDIAN = 0x01; static constexpr uInt8 ENDIAN_BIG_ENDIAN = 0x02; @@ -99,6 +111,8 @@ class ElfParser { static constexpr uInt32 SHN_ABS = 0xfff1; static constexpr uInt32 SHN_UND = 0x00; + static constexpr uInt32 STT_SECTION = 0x03; + public: ElfParser() = default; @@ -111,6 +125,7 @@ class ElfParser { const vector
& getSections() const; const vector& getSymbols() const; const optional
getSection(const std::string &name) const; + const optional> getRelocations(size_t section) const; private: uInt8 read8(uInt32 offset) const; @@ -119,6 +134,7 @@ class ElfParser { Section readSection(uInt32 offset) const; Symbol readSymbol(uInt32 index, const Section& symSec, const Section& strSec) const; + Relocation readRelocation(uInt32 index, const Section& sec) const; const char* getName(const Section& section, uInt32 offset) const; const Section* getSymtab() const; @@ -133,9 +149,11 @@ class ElfParser { Header header; vector
sections; vector symbols; + std::unordered_map> relocations; }; ostream& operator<<(ostream& os, const ElfParser::Section& section); ostream& operator<<(ostream& os, const ElfParser::Symbol symbol); +ostream& operator<<(ostream& os, const ElfParser::Relocation relocation); #endif // ELF_PARSER