From 80c686fc6260808e9fef36591120b05652b6ed15 Mon Sep 17 00:00:00 2001 From: Christian Speckner Date: Tue, 2 Jul 2024 22:39:05 +0200 Subject: [PATCH] Port and hook up ELF basic parser. --- Makefile | 1 + configure | 3 +- src/common/audio/module.mk | 3 +- src/common/bspf.hxx | 2 + src/emucore/CartDetector.cxx | 7 +- src/emucore/CartELF.cxx | 6 ++ src/emucore/CartELF.hxx | 3 + src/emucore/elf/ElfParser.cxx | 128 ++++++++++++++++++++++++++ src/emucore/elf/ElfParser.hxx | 110 ++++++++++++++++++++++ src/emucore/elf/module.mk | 10 ++ src/emucore/module.mk | 2 +- src/os/windows/Stella.vcxproj | 2 + src/os/windows/Stella.vcxproj.filters | 6 ++ 13 files changed, 277 insertions(+), 6 deletions(-) create mode 100644 src/emucore/elf/ElfParser.cxx create mode 100644 src/emucore/elf/ElfParser.hxx create mode 100644 src/emucore/elf/module.mk diff --git a/Makefile b/Makefile index 8d438dcfe..4469c7584 100644 --- a/Makefile +++ b/Makefile @@ -176,6 +176,7 @@ MODULES += \ src/emucore \ src/emucore/tia \ src/emucore/tia/frame-manager \ + src/emucore/elf \ src/common/repository/sqlite ###################################################################### diff --git a/configure b/configure index 3d36c7105..e35ae9aca 100755 --- a/configure +++ b/configure @@ -799,6 +799,7 @@ SRC_LIB="$SRC/lib" CORE="$SRC/emucore" COMMON="$SRC/common" TIA="$SRC/emucore/tia" +ELF="$SRC/emucore/elf" TIA_FRAME_MANAGER="$SRC/emucore/tia/frame-manager" TV="$SRC/common/tv_filters" GUI="$SRC/gui" @@ -815,7 +816,7 @@ SQLITE_LIB="$SRC_LIB/sqlite" JSON="$SRC_LIB/json" HTTP_LIB="$SRC_LIB/httplib" -INCLUDES="-I$CORE -I$COMMON -I$TV -I$TIA -I$TIA_FRAME_MANAGER -I$JSON -I$SQLITE_REPO" +INCLUDES="-I$CORE -I$COMMON -I$TV -I$TIA -I$TIA_FRAME_MANAGER -I$ELF -I$JSON -I$SQLITE_REPO" INCLUDES="$INCLUDES `$_sdlconfig --cflags`" if test "$_build_static" = yes ; then diff --git a/src/common/audio/module.mk b/src/common/audio/module.mk index 6c51ab2a0..91961a13b 100644 --- a/src/common/audio/module.mk +++ b/src/common/audio/module.mk @@ -7,7 +7,8 @@ MODULE_OBJS := \ src/common/audio/HighPass.o MODULE_DIRS += \ - src/emucore/tia + src/emucore/tia \ + src/emucore/elf # Include common rules include $(srcdir)/common.rules diff --git a/src/common/bspf.hxx b/src/common/bspf.hxx index f3a155a7a..5ba64a197 100644 --- a/src/common/bspf.hxx +++ b/src/common/bspf.hxx @@ -58,6 +58,7 @@ using uInt64 = uint64_t; #include #include #include +#include using std::cin; using std::cout; @@ -78,6 +79,7 @@ using std::make_shared; using std::array; using std::vector; using std::runtime_error; +using std::optional; // Common array types using IntArray = std::vector; diff --git a/src/emucore/CartDetector.cxx b/src/emucore/CartDetector.cxx index 1d01b8ac0..11bf3d4c1 100755 --- a/src/emucore/CartDetector.cxx +++ b/src/emucore/CartDetector.cxx @@ -18,6 +18,7 @@ #include "bspf.hxx" #include "Logger.hxx" +#include "ElfParser.hxx" #include "CartDetector.hxx" #include "CartMVC.hxx" @@ -868,13 +869,13 @@ bool CartDetector::isProbablyELF(const ByteBuffer& image, size_t size) { if (!searchForBytes(image, 2 * sizeof(signature), signature, sizeof(signature), 1)) return false; // We require little endian - if (image[0x05] != 1) return false; + if (image[0x05] != ElfParser::ENDIAN_LITTLE_ENDIAN) return false; // Type must be ET_REL (relocatable ELF) - if (image[0x10] != 0x01) return false; + if (image[0x10] != ElfParser::ET_REL) return false; // Arch must be ARM - if (image[0x12] != 0x28) return false; + if (image[0x12] != ElfParser::ARCH_ARM32) return false; return true; } diff --git a/src/emucore/CartELF.cxx b/src/emucore/CartELF.cxx index 35372a83f..c7347e64e 100644 --- a/src/emucore/CartELF.cxx +++ b/src/emucore/CartELF.cxx @@ -65,6 +65,12 @@ CartridgeELF::CartridgeELF(const ByteBuffer& image, size_t size, string_view md5 const Settings& settings) : Cartridge(settings, md5), myImageSize(size) { + try { + elfParser.parse(image.get(), size); + } catch (ElfParser::EInvalidElf& e) { + throw runtime_error("failed to initialize ELF: " + e.getReason()); + } + myImage = make_unique(size); std::memcpy(myImage.get(), image.get(), size); diff --git a/src/emucore/CartELF.hxx b/src/emucore/CartELF.hxx index a643114e1..85a03f3e9 100644 --- a/src/emucore/CartELF.hxx +++ b/src/emucore/CartELF.hxx @@ -20,6 +20,7 @@ #include "bspf.hxx" #include "Cart.hxx" +#include "ElfParser.hxx" class CartridgeELF: public Cartridge { public: @@ -114,6 +115,8 @@ class CartridgeELF: public Cartridge { bool myIsBusDriven{false}; uInt8 myDriveBusValue{0}; + + ElfParser elfParser; }; #endif // CARTRIDGE_ELF diff --git a/src/emucore/elf/ElfParser.cxx b/src/emucore/elf/ElfParser.cxx new file mode 100644 index 000000000..8c0ea7776 --- /dev/null +++ b/src/emucore/elf/ElfParser.cxx @@ -0,0 +1,128 @@ +#include "ElfParser.hxx" + +#include + +namespace { + constexpr uInt32 ELF_MAGIC = 0x7f454c46; + constexpr uInt8 ELF_CLASS_32 = 1; + constexpr uInt8 ELF_VERSION = 1; +} // namespace + +ElfParser::EInvalidElf::EInvalidElf(const string &reason) : reason(reason) {} + +const string &ElfParser::EInvalidElf::getReason() const { return reason; } + +void ElfParser::parse(const uInt8 *elfData, size_t size) { + data = elfData; + this->size = size; + + sections.resize(0); + + try { + if (read32(0x00) != ELF_MAGIC) throw EInvalidElf("bad magic"); + if (read8(0x04) != ELF_CLASS_32) throw EInvalidElf("not 32bit ELF"); + if (read8(0x06) != ELF_VERSION) throw EInvalidElf("invalid ELF version"); + + header.endianess = read8(0x05); + bigEndian = header.endianess == ENDIAN_BIG_ENDIAN; + + if (read32(0x14) != ELF_VERSION) throw EInvalidElf("inconsistent ELF version"); + + header.type = read16(0x10); + header.arch = read16(0x12); + + header.shOffset = read32(0x20); + header.shSize = read16(0x2e); + header.shNum = read16(0x30); + header.shstrIndex = read16(0x32); + + if (header.shstrIndex >= header.shNum) throw EInvalidElf(".shstrtab out of range"); + + sections.reserve(header.shNum); + + for (uInt32 i = 0; i < header.shNum; i++) + sections.push_back( + readSection(header.shOffset + i * header.shSize)); + + const Section &shrstrtab(sections[header.shstrIndex]); + + if (shrstrtab.type != SHT_STRTAB) throw new EInvalidElf(".shstrtab has wrong type"); + + + for (Section §ion : sections) + section.name = getName(shrstrtab, section.nameOffset); + + } catch (const EInvalidElf &e) { + throw EInvalidElf("failed to parse ELF: " + e.getReason()); + } +} + +const uInt8 *ElfParser::getData() const { return data; } + +size_t ElfParser::getSize() const { return size; } + +const vector &ElfParser::getSections() const { + return sections; +} + +const optional +ElfParser::getSection(const string &name) const { + for (const Section §ion : sections) + if (section.name == name) + return section; + + return optional
(); +} + +uInt8 ElfParser::read8(uInt32 offset) { + if (offset >= size) + throw EInvalidElf("reference beyond bounds"); + + return data[offset]; +} + +uInt16 ElfParser::read16(uInt32 offset) { + return bigEndian ? ((read8(offset) << 8) | read8(offset + 1)) + : ((read8(offset + 1) << 8) | read8(offset)); +} + +uInt32 ElfParser::read32(uInt32 offset) { + return bigEndian ? ((read8(offset) << 24) | (read8(offset + 1) << 16) | + (read8(offset + 2) << 8) | read8(offset + 3)) + : ((read8(offset + 3) << 24) | (read8(offset + 2) << 16) | + (read8(offset + 1) << 8) | read8(offset)); +} + +ElfParser::Section ElfParser::readSection(uInt32 offset) { + Section section; + + try { + section.nameOffset = read32(offset); + section.type = read32(offset + 0x04); + section.flags = read32(offset + 0x08); + section.virtualAddress = read32(offset + 0x0c); + section.offset = read32(offset + 0x10); + section.size = read32(offset + 0x14); + section.align = read32(offset + 0x20); + + if (section.offset + section.size >= size) + throw EInvalidElf("section exceeds bounds"); + } catch (const EInvalidElf &e) { + throw EInvalidElf("failed to parse section: " + e.getReason()); + } + + return section; +} + +const char* ElfParser::getName(const Section& section, uInt32 offset) +{ + if (offset >= section.size) throw EInvalidElf("name out of bounds"); + const uInt32 imageOffset = offset + section.offset; + + const char *name = reinterpret_cast(data + imageOffset); + + if (data[imageOffset + strnlen(name, section.size - offset)] != '\0') + throw new EInvalidElf("unterminated section name"); + + return name; +} diff --git a/src/emucore/elf/ElfParser.hxx b/src/emucore/elf/ElfParser.hxx new file mode 100644 index 000000000..cb8d31439 --- /dev/null +++ b/src/emucore/elf/ElfParser.hxx @@ -0,0 +1,110 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ + +#ifndef ELF_PARSER +#define ELF_PARSER + +#include "bspf.hxx" + +class ElfParser { + public: + class EInvalidElf { + friend ElfParser; + + public: + const std::string &getReason() const; + + private: + EInvalidElf(const std::string &reason); + + private: + std::string reason; + }; + + struct Header { + uInt16 type; + uInt16 arch; + uInt8 endianess; + uInt32 shOffset; + + uInt16 shNum; + uInt16 shSize; + uInt16 shstrIndex; + }; + + struct Section { + uInt32 nameOffset; + string name; + + uInt32 type; + uInt32 flags; + + uInt32 virtualAddress; + uInt32 offset; + uInt32 size; + + uInt32 align; + }; + + public: + static constexpr uInt8 ENDIAN_LITTLE_ENDIAN = 0x01; + static constexpr uInt8 ENDIAN_BIG_ENDIAN = 0x02; + + static constexpr uInt16 ET_REL = 0x01; + + static constexpr uInt16 ARCH_ARM32 = 0x28; + + static constexpr uInt32 SHT_PROGBITS = 0x01; + static constexpr uInt32 SHT_SYMTAB = 0x02; + static constexpr uInt32 SHT_STRTAB = 0x03; + static constexpr uInt32 SHT_RELA = 0x04; + static constexpr uInt32 SHT_NOBITS = 0x08; + static constexpr uInt32 SHT_REL = 0x09; + static constexpr uInt32 SHT_INIT_ARRAY = 0x0e; + static constexpr uInt32 SHT_PREINIT_ARRAY = 0x10; + + public: + ElfParser() = default; + + void parse(const uInt8 *elfData, size_t size); + + const uInt8 *getData() const; + size_t getSize() const; + + const Header& getHeader() const; + const vector
& getSections() const; + const optional
getSection(const std::string &name) const; + + private: + uInt8 read8(uInt32 offset); + uInt16 read16(uInt32 offset); + uInt32 read32(uInt32 offset); + + Section readSection(uInt32 offset); + const char* getName(const Section& section, uInt32 offset); + + private: + const uInt8 *data{nullptr}; + size_t size; + + bool bigEndian{true}; + + Header header; + vector
sections; +}; + +#endif // ELF_PARSER diff --git a/src/emucore/elf/module.mk b/src/emucore/elf/module.mk new file mode 100644 index 000000000..fc3b44e64 --- /dev/null +++ b/src/emucore/elf/module.mk @@ -0,0 +1,10 @@ +MODULE := src/emucore/elf + +MODULE_OBJS = \ + src/emucore/elf/ElfParser.o + +MODULE_DIRS += \ + src/emucore/elf + +# Include common rules +include $(srcdir)/common.rules diff --git a/src/emucore/module.mk b/src/emucore/module.mk index 902eb1f15..b9e542dbd 100644 --- a/src/emucore/module.mk +++ b/src/emucore/module.mk @@ -2,7 +2,7 @@ MODULE := src/emucore MODULE_OBJS := \ src/emucore/AtariVox.o \ - src/emucore/Bankswitch.o \ + src/emucore/Bankswitch.o \ src/emucore/Booster.o \ src/emucore/Cart.o \ src/emucore/CartARM.o \ diff --git a/src/os/windows/Stella.vcxproj b/src/os/windows/Stella.vcxproj index a3c32b1c0..1333171a0 100755 --- a/src/os/windows/Stella.vcxproj +++ b/src/os/windows/Stella.vcxproj @@ -790,6 +790,7 @@ + @@ -1788,6 +1789,7 @@ + diff --git a/src/os/windows/Stella.vcxproj.filters b/src/os/windows/Stella.vcxproj.filters index 657fc4f46..471d31c65 100644 --- a/src/os/windows/Stella.vcxproj.filters +++ b/src/os/windows/Stella.vcxproj.filters @@ -234,6 +234,9 @@ Source Files\emucore + + Source Files\emucore + Source Files\emucore @@ -1271,6 +1274,9 @@ Header Files\emucore + + Header Files\emucore + Header Files\emucore