all repos — mgba @ 64409d9ca7b3af1e83f7fb68737935015739cd40

mGBA Game Boy Advance Emulator

Core: ELF support
Vicki Pfau vi@endrift.com
Sun, 16 Jul 2017 09:44:58 -0700
commit

64409d9ca7b3af1e83f7fb68737935015739cd40

parent

9ed7c9129d63194b40fd3ae2453ea6d6f6bafe0f

M CHANGESCHANGES

@@ -1,4 +1,6 @@

0.7.0: (Future) +Features: + - ELF support Bugfixes: - GB Audio: Make audio unsigned with bias (fixes mgba.io/i/749) Misc:
M CMakeLists.txtCMakeLists.txt

@@ -16,6 +16,7 @@ set(USE_PNG ON CACHE BOOL "Whether or not to enable PNG support")

set(USE_LIBZIP ON CACHE BOOL "Whether or not to enable LIBZIP support") set(USE_MAGICK ON CACHE BOOL "Whether or not to enable ImageMagick support") set(USE_SQLITE3 ON CACHE BOOL "Whether or not to enable SQLite3 support") +set(USE_ELF ON CACHE BOOL "Whether or not to enable ELF support") set(M_CORE_GBA ON CACHE BOOL "Build Game Boy Advance core") set(M_CORE_GB ON CACHE BOOL "Build Game Boy core") set(USE_LZMA ON CACHE BOOL "Whether or not to enable 7-Zip support")

@@ -397,6 +398,7 @@ find_feature(USE_MAGICK "MagickWand")

find_feature(USE_EPOXY "epoxy") find_feature(USE_CMOCKA "cmocka") find_feature(USE_SQLITE3 "sqlite3") +find_feature(USE_ELF "libelf") find_feature(ENABLE_PYTHON "PythonLibs") # Features

@@ -602,6 +604,13 @@ set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libsqlite3-0")

list(APPEND FEATURE_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/feature/sqlite3/no-intro.c") endif() +if(USE_ELF) + list(APPEND FEATURES ELF) + include_directories(AFTER ${LIBELF_INCLUDE_DIRS}) + list(APPEND DEPENDENCY_LIB ${LIBELF_LIBRARIES}) + set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libelfg0") +endif() + if(ENABLE_SCRIPTING) list(APPEND ENABLES SCRIPTING)

@@ -935,6 +944,7 @@ message(STATUS " Screenshot/advanced savestate support: ${USE_PNG}")

message(STATUS " ZIP support: ${SUMMARY_ZIP}") message(STATUS " 7-Zip support: ${USE_LZMA}") message(STATUS " SQLite3 game database: ${USE_SQLITE3}") + message(STATUS " ELF loading support: ${USE_ELF}") message(STATUS " OpenGL support: ${SUMMARY_GL}") message(STATUS "Frontends:") message(STATUS " Qt: ${BUILD_QT}")
M README.mdREADME.md

@@ -142,6 +142,7 @@ - ffmpeg or libav: for video recording.

- libzip or zlib: for loading ROMs stored in zip files. - ImageMagick: for GIF recording. - SQLite3: for game databases. +- libelf: for ELF loading. SQLite3, libpng, and zlib are included with the emulator, so they do not need to be externally compiled first.
A include/mgba-util/elf-read.h

@@ -0,0 +1,46 @@

+/* Copyright (c) 2013-2017 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef ELF_READ_H +#define ELF_READ_H + +#include <mgba-util/common.h> + +CXX_GUARD_START + +#ifdef USE_ELF + +#include <libelf.h> +#include <elf_repl.h> + +#include <mgba-util/vector.h> + +struct ELF; +struct VFile; + +DECLARE_VECTOR(ELFProgramHeaders, Elf32_Phdr); +DECLARE_VECTOR(ELFSectionHeaders, Elf32_Shdr); + +struct ELF* ELFOpen(struct VFile*); +void ELFClose(struct ELF*); + +void* ELFBytes(struct ELF*, size_t* size); + +uint16_t ELFMachine(struct ELF*); +uint32_t ELFEntry(struct ELF*); + +void ELFGetProgramHeaders(struct ELF*, struct ELFProgramHeaders*); + +size_t ELFFindSection(struct ELF*, const char* name); +void ELFGetSectionHeaders(struct ELF*, struct ELFSectionHeaders*); +Elf32_Shdr* ELFGetSectionHeader(struct ELF*, size_t index); + +const char* ELFGetString(struct ELF*, size_t section, size_t string); + +#endif + +CXX_GUARD_END + +#endif
M include/mgba/core/core.hinclude/mgba/core/core.h

@@ -189,6 +189,14 @@ void mCoreLoadForeignConfig(struct mCore* core, const struct mCoreConfig* config);

void mCoreSetRTC(struct mCore* core, struct mRTCSource* rtc); +void* mCoreGetMemoryBlock(struct mCore* core, uint32_t start, size_t* size); + +#ifdef USE_ELF +struct ELF; +bool mCoreLoadELF(struct mCore* core, struct ELF* elf); +void mCoreLoadELFSymbols(struct mDebuggerSymbols* symbols, struct ELF*); +#endif + CXX_GUARD_END #endif
M include/mgba/internal/gba/gba.hinclude/mgba/internal/gba/gba.h

@@ -168,6 +168,7 @@ void GBALoadBIOS(struct GBA* gba, struct VFile* vf);

void GBAApplyPatch(struct GBA* gba, struct Patch* patch); bool GBALoadMB(struct GBA* gba, struct VFile* vf); +bool GBALoadNull(struct GBA* gba); bool GBAIsROM(struct VFile* vf); bool GBAIsMB(struct VFile* vf);
M src/core/core.csrc/core/core.c

@@ -8,6 +8,11 @@

#include <mgba/core/log.h> #include <mgba/core/serialize.h> #include <mgba-util/vfs.h> +#include <mgba/internal/debugger/symbols.h> + +#ifdef USE_ELF +#include <mgba-util/elf-read.h> +#endif #ifdef M_CORE_GB #include <mgba/gb/core.h>

@@ -273,3 +278,67 @@ void mCoreSetRTC(struct mCore* core, struct mRTCSource* rtc) {

core->rtc.custom = rtc; core->rtc.override = RTC_CUSTOM_START; } + +void* mCoreGetMemoryBlock(struct mCore* core, uint32_t start, size_t* size) { + const struct mCoreMemoryBlock* blocks; + size_t nBlocks = core->listMemoryBlocks(core, &blocks); + size_t i; + for (i = 0; i < nBlocks; ++i) { + if (!(blocks[i].flags & mCORE_MEMORY_MAPPED)) { + continue; + } + if (start < blocks[i].start) { + continue; + } + if (start >= blocks[i].start + blocks[i].size) { + continue; + } + uint8_t* out = core->getMemoryBlock(core, blocks[i].id, size); + out += start - blocks[i].start; + *size -= start - blocks[i].start; + return out; + } + return NULL; +} + +#ifdef USE_ELF +bool mCoreLoadELF(struct mCore* core, struct ELF* elf) { + struct ELFProgramHeaders ph; + ELFProgramHeadersInit(&ph, 0); + ELFGetProgramHeaders(elf, &ph); + size_t i; + for (i = 0; i < ELFProgramHeadersSize(&ph); ++i) { + size_t bsize, esize; + Elf32_Phdr* phdr = ELFProgramHeadersGetPointer(&ph, i); + void* block = mCoreGetMemoryBlock(core, phdr->p_paddr, &bsize); + char* bytes = ELFBytes(elf, &esize); + if (block && bsize >= phdr->p_filesz && esize >= phdr->p_filesz + phdr->p_offset) { + memcpy(block, &bytes[phdr->p_offset], phdr->p_filesz); + } else { + return false; + } + } + return true; +} + +void mCoreLoadELFSymbols(struct mDebuggerSymbols* symbols, struct ELF* elf) { + size_t symIndex = ELFFindSection(elf, ".symtab"); + size_t names = ELFFindSection(elf, ".strtab"); + Elf32_Shdr* symHeader = ELFGetSectionHeader(elf, symIndex); + char* bytes = ELFBytes(elf, NULL); + + Elf32_Sym* syms = (Elf32_Sym*) &bytes[symHeader->sh_offset]; + size_t i; + for (i = 0; i * sizeof(*syms) < symHeader->sh_size; ++i) { + if (!syms[i].st_name || ELF32_ST_TYPE(syms[i].st_info) == STT_FILE) { + continue; + } + const char* name = ELFGetString(elf, names, syms[i].st_name); + if (name[0] == '$') { + continue; + } + mDebuggerSymbolAdd(symbols, name, syms[i].st_value, -1); + } +} + +#endif
M src/gba/core.csrc/gba/core.c

@@ -8,6 +8,7 @@

#include <mgba/core/core.h> #include <mgba/core/log.h> #include <mgba/internal/arm/debugger/debugger.h> +#include <mgba/internal/debugger/symbols.h> #include <mgba/internal/gba/cheats.h> #include <mgba/internal/gba/gba.h> #include <mgba/internal/gba/io.h>

@@ -20,6 +21,9 @@ #include <mgba/internal/gba/renderers/proxy.h>

#include <mgba/internal/gba/renderers/video-software.h> #include <mgba/internal/gba/savedata.h> #include <mgba/internal/gba/serialize.h> +#ifdef USE_ELF +#include <mgba-util/elf-read.h> +#endif #include <mgba-util/memory.h> #include <mgba-util/patch.h> #include <mgba-util/vfs.h>

@@ -307,6 +311,15 @@ }

} static bool _GBACoreLoadROM(struct mCore* core, struct VFile* vf) { +#ifdef USE_ELF + struct ELF* elf = ELFOpen(vf); + if (elf) { + GBALoadNull(core->board); + bool success = mCoreLoadELF(core, elf); + ELFClose(elf); + return success; + } +#endif if (GBAIsMB(vf)) { return GBALoadMB(core->board, vf); }

@@ -696,7 +709,27 @@ core->debugger = NULL;

} static void _GBACoreLoadSymbols(struct mCore* core, struct VFile* vf) { - // TODO +#ifdef USE_ELF + bool closeAfter = false; + core->symbolTable = mDebuggerSymbolTableCreate(); +#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2 + if (!vf) { + closeAfter = true; + vf = mDirectorySetOpenSuffix(&core->dirs, core->dirs.base, ".elf", O_RDONLY); + } +#endif + if (!vf) { + return; + } + struct ELF* elf = ELFOpen(vf); + if (elf) { + mCoreLoadELFSymbols(core->symbolTable, elf); + ELFClose(elf); + } + if (closeAfter) { + vf->close(vf); + } +#endif } #endif
M src/gba/gba.csrc/gba/gba.c

@@ -21,6 +21,10 @@ #include <mgba-util/math.h>

#include <mgba-util/memory.h> #include <mgba-util/vfs.h> +#ifdef USE_ELF +#include <mgba-util/elf-read.h> +#endif + mLOG_DEFINE_CATEGORY(GBA, "GBA", "gba"); mLOG_DEFINE_CATEGORY(GBA_DEBUG, "GBA Debug", "gba.debug");

@@ -203,6 +207,10 @@ gba->idleDetectionFailures = 0;

gba->debug = false; memset(gba->debugString, 0, sizeof(gba->debugString)); + + if (!gba->romVf) { + GBASkipBIOS(gba); + } } void GBASkipBIOS(struct GBA* gba) {

@@ -288,6 +296,25 @@ gba->debugger = NULL;

} #endif +bool GBALoadNull(struct GBA* gba) { + GBAUnloadROM(gba); + gba->romVf = NULL; + gba->pristineRomSize = 0; + gba->memory.wram = anonymousMemoryMap(SIZE_WORKING_RAM); +#ifndef FIXED_ROM_BUFFER + gba->memory.rom = anonymousMemoryMap(SIZE_CART0); +#else + gba->memory.rom = romBuffer; +#endif + gba->isPristine = false; + gba->yankedRomSize = 0; + gba->memory.romSize = SIZE_CART0; + gba->memory.romMask = SIZE_CART0 - 1; + gba->memory.mirroring = false; + gba->romCrc32 = 0; + return true; +} + bool GBALoadMB(struct GBA* gba, struct VFile* vf) { GBAUnloadROM(gba); gba->romVf = vf;

@@ -479,6 +506,17 @@ gba->debugFlags = GBADebugFlagsClearSend(gba->debugFlags);

} bool GBAIsROM(struct VFile* vf) { +#ifdef USE_ELF + struct ELF* elf = ELFOpen(vf); + if (elf) { + uint32_t entry = ELFEntry(elf); + bool isGBA = true; + isGBA = isGBA && ELFMachine(elf) == EM_ARM; + isGBA = isGBA && (entry == BASE_CART0 || entry == BASE_WORKING_RAM); + ELFClose(elf); + return isGBA; + } +#endif if (vf->seek(vf, GBA_ROM_MAGIC_OFFSET, SEEK_SET) < 0) { return false; }

@@ -496,6 +534,14 @@ bool GBAIsMB(struct VFile* vf) {

if (!GBAIsROM(vf)) { return false; } +#ifdef USE_ELF + struct ELF* elf = ELFOpen(vf); + if (elf) { + bool isMB = ELFEntry(elf) == BASE_WORKING_RAM; + ELFClose(elf); + return isMB; + } +#endif if (vf->size(vf) > SIZE_WORKING_RAM) { return false; }
M src/gba/memory.csrc/gba/memory.c

@@ -80,6 +80,8 @@ cpu->memory.activeNonseqCycles16 = 0;

gba->memory.biosPrefetch = 0; gba->memory.mirroring = false; + gba->memory.iwram = anonymousMemoryMap(SIZE_WORKING_IRAM); + GBADMAInit(gba); GBAVFameInit(&gba->memory.vfame); }

@@ -107,9 +109,8 @@ gba->memory.wram = anonymousMemoryMap(SIZE_WORKING_RAM);

} if (gba->memory.iwram) { - mappedMemoryFree(gba->memory.iwram, SIZE_WORKING_IRAM); + memset(gba->memory.iwram, 0, SIZE_WORKING_IRAM); } - gba->memory.iwram = anonymousMemoryMap(SIZE_WORKING_IRAM); memset(gba->memory.io, 0, sizeof(gba->memory.io));
A src/util/elf-read.c

@@ -0,0 +1,125 @@

+/* Copyright (c) 2013-2017 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include <mgba-util/elf-read.h> + +#ifdef USE_ELF + +#include <mgba-util/vfs.h> + +DEFINE_VECTOR(ELFProgramHeaders, Elf32_Phdr); +DEFINE_VECTOR(ELFSectionHeaders, Elf32_Shdr); + +static bool _elfInit = false; + +struct ELF { + Elf* e; + struct VFile* vf; + size_t size; + char* memory; +}; + +struct ELF* ELFOpen(struct VFile* vf) { + if (!_elfInit) { + _elfInit = elf_version(EV_CURRENT) != EV_NONE; + if (!_elfInit) { + return NULL; + } + } + if (!vf) { + return NULL; + } + size_t size = vf->size(vf); + char* memory = vf->map(vf, size, MAP_READ); + if (!memory) { + return NULL; + } + + Elf* e = elf_memory(memory, size); + if (!e || elf_kind(e) != ELF_K_ELF) { + elf_end(e); + vf->unmap(vf, memory, size); + return false; + } + struct ELF* elf = malloc(sizeof(*elf)); + elf->e = e; + elf->vf = vf; + elf->size = size; + elf->memory = memory; + return elf; +} + +void ELFClose(struct ELF* elf) { + elf_end(elf->e); + elf->vf->unmap(elf->vf, elf->memory, elf->size); + free(elf); +} + +void* ELFBytes(struct ELF* elf, size_t* size) { + if (size) { + *size = elf->size; + } + return elf->memory; +} + +uint16_t ELFMachine(struct ELF* elf) { + Elf32_Ehdr* hdr = elf32_getehdr(elf->e); + if (!hdr) { + return 0; + } + return hdr->e_machine; +} + +uint32_t ELFEntry(struct ELF* elf) { + Elf32_Ehdr* hdr = elf32_getehdr(elf->e); + if (!hdr) { + return 0; + } + return hdr->e_entry; +} + +void ELFGetProgramHeaders(struct ELF* elf, struct ELFProgramHeaders* ph) { + ELFProgramHeadersClear(ph); + Elf32_Ehdr* hdr = elf32_getehdr(elf->e); + Elf32_Phdr* phdr = elf32_getphdr(elf->e); + ELFProgramHeadersResize(ph, hdr->e_phnum); + memcpy(ELFProgramHeadersGetPointer(ph, 0), phdr, sizeof(*phdr) * hdr->e_phnum); +} + +void ELFGetSectionHeaders(struct ELF* elf, struct ELFSectionHeaders* sh) { + ELFSectionHeadersClear(sh); + Elf_Scn* section = elf_getscn(elf->e, 0); + do { + *ELFSectionHeadersAppend(sh) = *elf32_getshdr(section); + } while ((section = elf_nextscn(elf->e, section))); +} + +Elf32_Shdr* ELFGetSectionHeader(struct ELF* elf, size_t index) { + Elf_Scn* section = elf_getscn(elf->e, index); + return elf32_getshdr(section); +} + +size_t ELFFindSection(struct ELF* elf, const char* name) { + Elf32_Ehdr* hdr = elf32_getehdr(elf->e); + size_t shstrtab = hdr->e_shstrndx; + if (strcmp(name, ".shstrtab") == 0) { + return shstrtab; + } + Elf_Scn* section = NULL; + while ((section = elf_nextscn(elf->e, section))) { + Elf32_Shdr* shdr = elf32_getshdr(section); + const char* sname = elf_strptr(elf->e, shstrtab, shdr->sh_name); + if (strcmp(sname, name) == 0) { + return elf_ndxscn(section); + } + } + return 0; +} + +const char* ELFGetString(struct ELF* elf, size_t section, size_t string) { + return elf_strptr(elf->e, section, string); +} + +#endif