Port and hook up ELF basic parser.

This commit is contained in:
Christian Speckner 2024-07-02 22:39:05 +02:00
parent cccc92020a
commit be80e6b0f7
13 changed files with 277 additions and 6 deletions

View File

@ -176,6 +176,7 @@ MODULES += \
src/emucore \
src/emucore/tia \
src/emucore/tia/frame-manager \
src/emucore/elf \
src/common/repository/sqlite
######################################################################

3
configure vendored
View File

@ -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

View File

@ -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

View File

@ -58,6 +58,7 @@ using uInt64 = uint64_t;
#include <numbers>
#include <utility>
#include <vector>
#include <optional>
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<Int32>;

View File

@ -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;
}

View File

@ -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<uInt8[]>(size);
std::memcpy(myImage.get(), image.get(), size);

View File

@ -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

View File

@ -0,0 +1,128 @@
#include "ElfParser.hxx"
#include <cstring>
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 &section : 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::Section> &ElfParser::getSections() const {
return sections;
}
const optional<ElfParser::Section>
ElfParser::getSection(const string &name) const {
for (const Section &section : sections)
if (section.name == name)
return section;
return optional<Section>();
}
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<const char *>(data + imageOffset);
if (data[imageOffset + strnlen(name, section.size - offset)] != '\0')
throw new EInvalidElf("unterminated section name");
return name;
}

View File

@ -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<Section>& getSections() const;
const optional<Section> 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<Section> sections;
};
#endif // ELF_PARSER

10
src/emucore/elf/module.mk Normal file
View File

@ -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

View File

@ -790,6 +790,7 @@
<ClCompile Include="..\..\emucore\CartUA.cxx" />
<ClCompile Include="..\..\emucore\CartX07.cxx" />
<ClCompile Include="..\..\emucore\CartELF.cxx" />
<ClCompile Include="..\..\emucore\elf\ElfParser.cxx" />
<ClCompile Include="..\..\emucore\Console.cxx" />
<ClCompile Include="..\..\emucore\Control.cxx" />
<ClCompile Include="..\..\emucore\Driving.cxx" />
@ -1788,6 +1789,7 @@
<ClInclude Include="..\..\emucore\CartUA.hxx" />
<ClInclude Include="..\..\emucore\CartX07.hxx" />
<ClInclude Include="..\..\emucore\CartELF.hxx" />
<ClInclude Include="..\..\emucore\elf\ElfParser.hxx" />
<ClInclude Include="..\..\emucore\Console.hxx" />
<ClInclude Include="..\..\emucore\Control.hxx" />
<ClInclude Include="..\..\emucore\DefProps.hxx" />

View File

@ -234,6 +234,9 @@
<ClCompile Include="..\..\emucore\CartELF.cxx">
<Filter>Source Files\emucore</Filter>
</ClCompile>
<ClCompile Include="..\..\emucore\ElfParser.cxx">
<Filter>Source Files\emucore</Filter>
</ClCompile>
<ClCompile Include="..\..\emucore\Console.cxx">
<Filter>Source Files\emucore</Filter>
</ClCompile>
@ -1271,6 +1274,9 @@
<ClInclude Include="..\..\emucore\CartELF.hxx">
<Filter>Header Files\emucore</Filter>
</ClInclude>
<ClInclude Include="..\..\emucore\elf\ElfParser.hxx">
<Filter>Header Files\emucore</Filter>
</ClInclude>
<ClInclude Include="..\..\emucore\CartAR.hxx">
<Filter>Header Files\emucore</Filter>
</ClInclude>