Core: ELF support

This commit is contained in:
Vicki Pfau 2017-07-16 09:44:58 -07:00
parent 9ed7c9129d
commit 64409d9ca7
11 changed files with 345 additions and 3 deletions

View File

@ -1,4 +1,6 @@
0.7.0: (Future) 0.7.0: (Future)
Features:
- ELF support
Bugfixes: Bugfixes:
- GB Audio: Make audio unsigned with bias (fixes mgba.io/i/749) - GB Audio: Make audio unsigned with bias (fixes mgba.io/i/749)
Misc: Misc:

View File

@ -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_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_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_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_GBA ON CACHE BOOL "Build Game Boy Advance core")
set(M_CORE_GB ON CACHE BOOL "Build Game Boy 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") 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_EPOXY "epoxy")
find_feature(USE_CMOCKA "cmocka") find_feature(USE_CMOCKA "cmocka")
find_feature(USE_SQLITE3 "sqlite3") find_feature(USE_SQLITE3 "sqlite3")
find_feature(USE_ELF "libelf")
find_feature(ENABLE_PYTHON "PythonLibs") find_feature(ENABLE_PYTHON "PythonLibs")
# Features # Features
@ -602,6 +604,13 @@ if(USE_SQLITE3)
list(APPEND FEATURE_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/feature/sqlite3/no-intro.c") list(APPEND FEATURE_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/feature/sqlite3/no-intro.c")
endif() 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) if(ENABLE_SCRIPTING)
list(APPEND ENABLES SCRIPTING) list(APPEND ENABLES SCRIPTING)
@ -935,6 +944,7 @@ if(NOT QUIET)
message(STATUS " ZIP support: ${SUMMARY_ZIP}") message(STATUS " ZIP support: ${SUMMARY_ZIP}")
message(STATUS " 7-Zip support: ${USE_LZMA}") message(STATUS " 7-Zip support: ${USE_LZMA}")
message(STATUS " SQLite3 game database: ${USE_SQLITE3}") message(STATUS " SQLite3 game database: ${USE_SQLITE3}")
message(STATUS " ELF loading support: ${USE_ELF}")
message(STATUS " OpenGL support: ${SUMMARY_GL}") message(STATUS " OpenGL support: ${SUMMARY_GL}")
message(STATUS "Frontends:") message(STATUS "Frontends:")
message(STATUS " Qt: ${BUILD_QT}") message(STATUS " Qt: ${BUILD_QT}")

View File

@ -142,6 +142,7 @@ mGBA has no hard dependencies, however, the following optional dependencies are
- libzip or zlib: for loading ROMs stored in zip files. - libzip or zlib: for loading ROMs stored in zip files.
- ImageMagick: for GIF recording. - ImageMagick: for GIF recording.
- SQLite3: for game databases. - 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. SQLite3, libpng, and zlib are included with the emulator, so they do not need to be externally compiled first.

View File

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

View File

@ -189,6 +189,14 @@ void mCoreLoadForeignConfig(struct mCore* core, const struct mCoreConfig* config
void mCoreSetRTC(struct mCore* core, struct mRTCSource* rtc); 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 CXX_GUARD_END
#endif #endif

View File

@ -168,6 +168,7 @@ void GBALoadBIOS(struct GBA* gba, struct VFile* vf);
void GBAApplyPatch(struct GBA* gba, struct Patch* patch); void GBAApplyPatch(struct GBA* gba, struct Patch* patch);
bool GBALoadMB(struct GBA* gba, struct VFile* vf); bool GBALoadMB(struct GBA* gba, struct VFile* vf);
bool GBALoadNull(struct GBA* gba);
bool GBAIsROM(struct VFile* vf); bool GBAIsROM(struct VFile* vf);
bool GBAIsMB(struct VFile* vf); bool GBAIsMB(struct VFile* vf);

View File

@ -8,6 +8,11 @@
#include <mgba/core/log.h> #include <mgba/core/log.h>
#include <mgba/core/serialize.h> #include <mgba/core/serialize.h>
#include <mgba-util/vfs.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 #ifdef M_CORE_GB
#include <mgba/gb/core.h> #include <mgba/gb/core.h>
@ -273,3 +278,67 @@ void mCoreSetRTC(struct mCore* core, struct mRTCSource* rtc) {
core->rtc.custom = rtc; core->rtc.custom = rtc;
core->rtc.override = RTC_CUSTOM_START; 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

View File

@ -8,6 +8,7 @@
#include <mgba/core/core.h> #include <mgba/core/core.h>
#include <mgba/core/log.h> #include <mgba/core/log.h>
#include <mgba/internal/arm/debugger/debugger.h> #include <mgba/internal/arm/debugger/debugger.h>
#include <mgba/internal/debugger/symbols.h>
#include <mgba/internal/gba/cheats.h> #include <mgba/internal/gba/cheats.h>
#include <mgba/internal/gba/gba.h> #include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/io.h> #include <mgba/internal/gba/io.h>
@ -20,6 +21,9 @@
#include <mgba/internal/gba/renderers/video-software.h> #include <mgba/internal/gba/renderers/video-software.h>
#include <mgba/internal/gba/savedata.h> #include <mgba/internal/gba/savedata.h>
#include <mgba/internal/gba/serialize.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/memory.h>
#include <mgba-util/patch.h> #include <mgba-util/patch.h>
#include <mgba-util/vfs.h> #include <mgba-util/vfs.h>
@ -307,6 +311,15 @@ static void _GBACoreSetAVStream(struct mCore* core, struct mAVStream* stream) {
} }
static bool _GBACoreLoadROM(struct mCore* core, struct VFile* vf) { 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)) { if (GBAIsMB(vf)) {
return GBALoadMB(core->board, vf); return GBALoadMB(core->board, vf);
} }
@ -696,7 +709,27 @@ static void _GBACoreDetachDebugger(struct mCore* core) {
} }
static void _GBACoreLoadSymbols(struct mCore* core, struct VFile* vf) { 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 #endif

View File

@ -21,6 +21,10 @@
#include <mgba-util/memory.h> #include <mgba-util/memory.h>
#include <mgba-util/vfs.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, "GBA", "gba");
mLOG_DEFINE_CATEGORY(GBA_DEBUG, "GBA Debug", "gba.debug"); mLOG_DEFINE_CATEGORY(GBA_DEBUG, "GBA Debug", "gba.debug");
@ -203,6 +207,10 @@ void GBAReset(struct ARMCore* cpu) {
gba->debug = false; gba->debug = false;
memset(gba->debugString, 0, sizeof(gba->debugString)); memset(gba->debugString, 0, sizeof(gba->debugString));
if (!gba->romVf) {
GBASkipBIOS(gba);
}
} }
void GBASkipBIOS(struct GBA* gba) { void GBASkipBIOS(struct GBA* gba) {
@ -288,6 +296,25 @@ void GBADetachDebugger(struct GBA* gba) {
} }
#endif #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) { bool GBALoadMB(struct GBA* gba, struct VFile* vf) {
GBAUnloadROM(gba); GBAUnloadROM(gba);
gba->romVf = vf; gba->romVf = vf;
@ -479,6 +506,17 @@ void GBADebug(struct GBA* gba, uint16_t flags) {
} }
bool GBAIsROM(struct VFile* vf) { 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) { if (vf->seek(vf, GBA_ROM_MAGIC_OFFSET, SEEK_SET) < 0) {
return false; return false;
} }
@ -496,6 +534,14 @@ bool GBAIsMB(struct VFile* vf) {
if (!GBAIsROM(vf)) { if (!GBAIsROM(vf)) {
return false; 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) { if (vf->size(vf) > SIZE_WORKING_RAM) {
return false; return false;
} }

View File

@ -80,6 +80,8 @@ void GBAMemoryInit(struct GBA* gba) {
gba->memory.biosPrefetch = 0; gba->memory.biosPrefetch = 0;
gba->memory.mirroring = false; gba->memory.mirroring = false;
gba->memory.iwram = anonymousMemoryMap(SIZE_WORKING_IRAM);
GBADMAInit(gba); GBADMAInit(gba);
GBAVFameInit(&gba->memory.vfame); GBAVFameInit(&gba->memory.vfame);
} }
@ -107,9 +109,8 @@ void GBAMemoryReset(struct GBA* gba) {
} }
if (gba->memory.iwram) { 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)); memset(gba->memory.io, 0, sizeof(gba->memory.io));

125
src/util/elf-read.c Normal file
View File

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