mirror of https://github.com/mgba-emu/mgba.git
Core: ELF support
This commit is contained in:
parent
9ed7c9129d
commit
64409d9ca7
2
CHANGES
2
CHANGES
|
@ -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:
|
||||
|
|
|
@ -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 @@ if(USE_SQLITE3)
|
|||
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 @@ if(NOT QUIET)
|
|||
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}")
|
||||
|
|
|
@ -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.
|
||||
- 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.
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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/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 void _GBACoreSetAVStream(struct mCore* core, struct mAVStream* stream) {
|
|||
}
|
||||
|
||||
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 @@ static void _GBACoreDetachDebugger(struct mCore* core) {
|
|||
}
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -21,6 +21,10 @@
|
|||
#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 @@ void GBAReset(struct ARMCore* cpu) {
|
|||
|
||||
gba->debug = false;
|
||||
memset(gba->debugString, 0, sizeof(gba->debugString));
|
||||
|
||||
if (!gba->romVf) {
|
||||
GBASkipBIOS(gba);
|
||||
}
|
||||
}
|
||||
|
||||
void GBASkipBIOS(struct GBA* gba) {
|
||||
|
@ -288,6 +296,25 @@ void GBADetachDebugger(struct GBA* gba) {
|
|||
}
|
||||
#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 @@ void GBADebug(struct GBA* gba, uint16_t flags) {
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -80,6 +80,8 @@ void GBAMemoryInit(struct GBA* gba) {
|
|||
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 @@ void GBAMemoryReset(struct GBA* gba) {
|
|||
}
|
||||
|
||||
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));
|
||||
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue