From 5d0ab484573f1c5bb057129374cb1e7b56139393 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 15 Jul 2017 07:13:28 -0700 Subject: [PATCH 1/8] SDL: Fix crash when not in debugger mode --- src/platform/sdl/main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/platform/sdl/main.c b/src/platform/sdl/main.c index 128a44cf1..818bb61fa 100644 --- a/src/platform/sdl/main.c +++ b/src/platform/sdl/main.c @@ -184,10 +184,10 @@ int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args) { #endif mDebuggerAttach(debugger, renderer->core); mDebuggerEnter(debugger, DEBUGGER_ENTER_MANUAL, NULL); - } -#ifdef ENABLE_SCRIPTING - mScriptBridgeSetDebugger(bridge, debugger); + #ifdef ENABLE_SCRIPTING + mScriptBridgeSetDebugger(bridge, debugger); #endif + } #endif if (args->patch) { From 2d49a41a30bca9ad47457ad8825fc13b9e82de81 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 15 Jul 2017 07:14:42 -0700 Subject: [PATCH 2/8] SDL: Fix cheats not loading --- CHANGES | 1 + src/platform/sdl/main.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/CHANGES b/CHANGES index deb436775..5c29d3390 100644 --- a/CHANGES +++ b/CHANGES @@ -77,6 +77,7 @@ Bugfixes: - SDL: Fix game crash check - SDL: Fix race condition with audio thread when starting - SDL: Fix showing version number + - SDL: Fix cheats not loading - Test: Fix crash when loading invalid file - Test: Fix crash when fuzzing fails to load a file - Test: Don't rely on core for frames elapsed diff --git a/src/platform/sdl/main.c b/src/platform/sdl/main.c index 818bb61fa..3cdf30856 100644 --- a/src/platform/sdl/main.c +++ b/src/platform/sdl/main.c @@ -21,6 +21,7 @@ #endif #endif +#include #include #include #include @@ -104,6 +105,16 @@ int main(int argc, char** argv) { return 1; } + struct mCheatDevice* device = NULL; + if (args.cheatsFile && (device = renderer.core->cheatDevice(renderer.core))) { + struct VFile* vf = VFileOpen(args.cheatsFile, O_RDONLY); + if (vf) { + mCheatDeviceClear(device); + mCheatParseFile(device, vf); + vf->close(vf); + } + } + mInputMapInit(&renderer.core->inputMap, &GBAInputInfo); mCoreInitConfig(renderer.core, PORT); applyArguments(&args, &subparser, &renderer.core->config); @@ -148,6 +159,10 @@ int main(int argc, char** argv) { mSDLDetachPlayer(&renderer.events, &renderer.player); mInputMapDeinit(&renderer.core->inputMap); + if (device) { + mCheatDeviceDestroy(device); + } + mSDLDeinit(&renderer); freeArguments(&args); From 6a3002d398033cb74d623bf9bf13ed5c6330c10b Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 15 Jul 2017 07:30:32 -0700 Subject: [PATCH 3/8] Python: Add CLI debugger support --- include/mgba/debugger/debugger.h | 1 + src/debugger/cli-debugger.c | 1 + src/debugger/gdb-stub.c | 1 + src/platform/python/_builder.h | 1 + src/platform/python/_builder.py | 13 ++++++++++--- src/platform/python/mgba/debugger.py | 21 +++++++++++++++++++++ 6 files changed, 35 insertions(+), 3 deletions(-) diff --git a/include/mgba/debugger/debugger.h b/include/mgba/debugger/debugger.h index 34535b9d4..213236425 100644 --- a/include/mgba/debugger/debugger.h +++ b/include/mgba/debugger/debugger.h @@ -91,6 +91,7 @@ struct mDebugger { struct mCPUComponent d; struct mDebuggerPlatform* platform; enum mDebuggerState state; + enum mDebuggerType type; struct mCore* core; struct mScriptBridge* bridge; diff --git a/src/debugger/cli-debugger.c b/src/debugger/cli-debugger.c index c991f08ee..9eb9801bd 100644 --- a/src/debugger/cli-debugger.c +++ b/src/debugger/cli-debugger.c @@ -832,6 +832,7 @@ void CLIDebuggerCreate(struct CLIDebugger* debugger) { debugger->d.custom = _cliDebuggerCustom; debugger->d.paused = _commandLine; debugger->d.entered = _reportEntry; + debugger->d.type = DEBUGGER_CLI; debugger->system = NULL; debugger->backend = NULL; diff --git a/src/debugger/gdb-stub.c b/src/debugger/gdb-stub.c index 3c149c9fc..6e2d287fa 100644 --- a/src/debugger/gdb-stub.c +++ b/src/debugger/gdb-stub.c @@ -658,6 +658,7 @@ void GDBStubCreate(struct GDBStub* stub) { stub->d.paused = _gdbStubWait; stub->d.entered = _gdbStubEntered; stub->d.custom = _gdbStubPoll; + stub->d.type = DEBUGGER_GDB; stub->untilPoll = GDB_STUB_INTERVAL; stub->lineAck = GDB_ACK_PENDING; stub->shouldBlock = false; diff --git a/src/platform/python/_builder.h b/src/platform/python/_builder.h index 0af3b4124..19842c402 100644 --- a/src/platform/python/_builder.h +++ b/src/platform/python/_builder.h @@ -58,4 +58,5 @@ void free(void*); #endif #ifdef USE_DEBUGGERS #include +#include #endif diff --git a/src/platform/python/_builder.py b/src/platform/python/_builder.py index 05cbddb39..79947021e 100644 --- a/src/platform/python/_builder.py +++ b/src/platform/python/_builder.py @@ -28,6 +28,7 @@ ffi.set_source("mgba._pylib", """ #include #include #include +#include #include #include #include @@ -70,17 +71,23 @@ for line in preprocessed.splitlines(): ffi.embedding_api('\n'.join(lines)) ffi.embedding_init_code(""" - from mgba._pylib import ffi + from mgba._pylib import ffi, lib debugger = None pendingCode = [] @ffi.def_extern() def mPythonSetDebugger(_debugger): - from mgba.debugger import NativeDebugger + from mgba.debugger import NativeDebugger, CLIDebugger global debugger if debugger and debugger._native == _debugger: return - debugger = _debugger and NativeDebugger(_debugger) + if not _debugger: + debugger = None + return + if _debugger.type == lib.DEBUGGER_CLI: + debugger = CLIDebugger(_debugger) + else: + debugger = NativeDebugger(_debugger) @ffi.def_extern() def mPythonLoadScript(name, vf): diff --git a/src/platform/python/mgba/debugger.py b/src/platform/python/mgba/debugger.py index d6bf30e2e..2d597491c 100644 --- a/src/platform/python/mgba/debugger.py +++ b/src/platform/python/mgba/debugger.py @@ -5,6 +5,8 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. from ._pylib import ffi, lib from .core import IRunner, ICoreOwner, Core +import io +import sys class DebuggerCoreOwner(ICoreOwner): def __init__(self, debugger): @@ -78,3 +80,22 @@ class NativeDebugger(IRunner): def addCallback(self, cb): self._cbs.append(cb) + +class CLIBackend(object): + def __init__(self, backend): + self.backend = backend + + def write(self, string): + self.backend.printf(string) + +class CLIDebugger(NativeDebugger): + def __init__(self, native): + super(CLIDebugger, self).__init__(native) + self._cli = ffi.cast("struct CLIDebugger*", native) + + def printf(self, message, *args, **kwargs): + message = message.format(*args, **kwargs) + self._cli.backend.printf(ffi.new("char []", b"%s"), ffi.new("char []", message.encode('utf-8'))) + + def installPrint(self): + sys.stdout = CLIBackend(self) From 220b786c9ccd74d7126aa8e3255d94c770f2510e Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 15 Jul 2017 19:51:33 -0700 Subject: [PATCH 4/8] Core: Add symbol lookups from scripts --- include/mgba/core/scripting.h | 3 +++ src/core/scripting.c | 25 +++++++++++++++++ src/debugger/cli-debugger.c | 5 ++++ src/platform/python/_builder.py | 48 +++++++++++++++++++++++++-------- src/platform/python/engine.c | 7 +++++ src/platform/python/lib.h | 1 + 6 files changed, 78 insertions(+), 11 deletions(-) diff --git a/include/mgba/core/scripting.h b/include/mgba/core/scripting.h index ae1964186..ebf8f88cf 100644 --- a/include/mgba/core/scripting.h +++ b/include/mgba/core/scripting.h @@ -24,6 +24,7 @@ struct mScriptEngine { bool (*isScript)(struct mScriptEngine*, const char* name, struct VFile* vf); bool (*loadScript)(struct mScriptEngine*, const char* name, struct VFile* vf); void (*run)(struct mScriptEngine*); + bool (*lookupSymbol)(struct mScriptEngine*, const char* name, int32_t* out); #ifdef USE_DEBUGGERS void (*debuggerEntered)(struct mScriptEngine*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*); @@ -44,6 +45,8 @@ void mScriptBridgeDebuggerEntered(struct mScriptBridge*, enum mDebuggerEntryReas void mScriptBridgeRun(struct mScriptBridge*); bool mScriptBridgeLoadScript(struct mScriptBridge*, const char* name); +bool mScriptBridgeLookupSymbol(struct mScriptBridge*, const char* name, int32_t* out); + CXX_GUARD_END #endif diff --git a/src/core/scripting.c b/src/core/scripting.c index e176aa843..9a0ee7698 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -19,6 +19,12 @@ struct mScriptInfo { bool success; }; +struct mScriptSymbol { + const char* name; + int32_t* out; + bool success; +}; + static void _seDeinit(void* value) { struct mScriptEngine* se = value; se->deinit(se); @@ -33,6 +39,15 @@ static void _seTryLoad(const char* key, void* value, void* user) { } } +static void _seLookupSymbol(const char* key, void* value, void* user) { + UNUSED(key); + struct mScriptEngine* se = value; + struct mScriptSymbol* si = user; + if (!si->success) { + si->success = se->lookupSymbol(se, si->name, si->out); + } +} + static void _seRun(const char* key, void* value, void* user) { UNUSED(key); UNUSED(user); @@ -111,3 +126,13 @@ bool mScriptBridgeLoadScript(struct mScriptBridge* sb, const char* name) { vf->close(vf); return info.success; } + +bool mScriptBridgeLookupSymbol(struct mScriptBridge* sb, const char* name, int32_t* out) { + struct mScriptSymbol info = { + .name = name, + .out = out, + .success = false + }; + HashTableEnumerate(&sb->engines, _seLookupSymbol, &info); + return info.success; +} diff --git a/src/debugger/cli-debugger.c b/src/debugger/cli-debugger.c index 9eb9801bd..871143436 100644 --- a/src/debugger/cli-debugger.c +++ b/src/debugger/cli-debugger.c @@ -551,6 +551,11 @@ static void _lookupIdentifier(struct mDebugger* debugger, const char* name, stru struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger; if (cliDebugger->system) { uint32_t value; +#ifdef ENABLE_SCRIPTING + if (debugger->bridge && mScriptBridgeLookupSymbol(debugger->bridge, name, &dv->intValue)) { + return; + } +#endif if (debugger->core->symbolTable && mDebuggerSymbolLookup(debugger->core->symbolTable, name, &dv->intValue, &dv->segmentValue)) { return; } diff --git a/src/platform/python/_builder.py b/src/platform/python/_builder.py index 79947021e..4aa2116c1 100644 --- a/src/platform/python/_builder.py +++ b/src/platform/python/_builder.py @@ -72,22 +72,26 @@ ffi.embedding_api('\n'.join(lines)) ffi.embedding_init_code(""" from mgba._pylib import ffi, lib - debugger = None + symbols = {} + globalSyms = { + 'symbols': symbols + } pendingCode = [] @ffi.def_extern() - def mPythonSetDebugger(_debugger): + def mPythonSetDebugger(debugger): from mgba.debugger import NativeDebugger, CLIDebugger - global debugger - if debugger and debugger._native == _debugger: + oldDebugger = globalSyms.get('debugger') + if oldDebugger and oldDebugger._native == debugger: return - if not _debugger: - debugger = None + if oldDebugger and not debugger: + del globalSyms['debugger'] return - if _debugger.type == lib.DEBUGGER_CLI: - debugger = CLIDebugger(_debugger) + if debugger.type == lib.DEBUGGER_CLI: + debugger = CLIDebugger(debugger) else: - debugger = NativeDebugger(_debugger) + debugger = NativeDebugger(debugger) + globalSyms['debugger'] = debugger @ffi.def_extern() def mPythonLoadScript(name, vf): @@ -106,18 +110,40 @@ ffi.embedding_init_code(""" def mPythonRunPending(): global pendingCode for code in pendingCode: - exec(code) + exec(code, globalSyms, {}) pendingCode = [] @ffi.def_extern() def mPythonDebuggerEntered(reason, info): - global debugger + debugger = globalSyms['debugger'] if not debugger: return if info == ffi.NULL: info = None for cb in debugger._cbs: cb(reason, info) + + @ffi.def_extern() + def mPythonLookupSymbol(name, outptr): + name = ffi.string(name).decode('utf-8') + if name not in symbols: + return False + sym = symbols[name] + val = None + try: + val = int(sym) + except: + try: + val = sym() + except: + pass + if val is None: + return False + try: + outptr[0] = ffi.cast('int32_t', val) + return True + except: + return False """) if __name__ == "__main__": diff --git a/src/platform/python/engine.c b/src/platform/python/engine.c index d91db2a8f..19fe76c0c 100644 --- a/src/platform/python/engine.c +++ b/src/platform/python/engine.c @@ -21,6 +21,7 @@ static void mPythonScriptEngineDeinit(struct mScriptEngine*); static bool mPythonScriptEngineIsScript(struct mScriptEngine*, const char* name, struct VFile* vf); static bool mPythonScriptEngineLoadScript(struct mScriptEngine*, const char* name, struct VFile* vf); static void mPythonScriptEngineRun(struct mScriptEngine*); +static bool mPythonScriptEngineLookupSymbol(struct mScriptEngine*, const char* name, int32_t* out); #ifdef USE_DEBUGGERS static void mPythonScriptDebuggerEntered(struct mScriptEngine*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*); @@ -39,6 +40,7 @@ struct mPythonScriptEngine* mPythonCreateScriptEngine(void) { engine->d.isScript = mPythonScriptEngineIsScript; engine->d.loadScript = mPythonScriptEngineLoadScript; engine->d.run = mPythonScriptEngineRun; + engine->d.lookupSymbol = mPythonScriptEngineLookupSymbol; #ifdef USE_DEBUGGERS engine->d.debuggerEntered = mPythonScriptDebuggerEntered; #endif @@ -89,6 +91,11 @@ void mPythonScriptEngineRun(struct mScriptEngine* se) { mPythonRunPending(); } +bool mPythonScriptEngineLookupSymbol(struct mScriptEngine* se, const char* name, int32_t* out) { + struct mPythonScriptEngine* engine = (struct mPythonScriptEngine*) se; + return mPythonLookupSymbol(name, out); +} + #ifdef USE_DEBUGGERS void mPythonScriptDebuggerEntered(struct mScriptEngine* se, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) { struct mPythonScriptEngine* engine = (struct mPythonScriptEngine*) se; diff --git a/src/platform/python/lib.h b/src/platform/python/lib.h index d2b49d7e1..9c6046a51 100644 --- a/src/platform/python/lib.h +++ b/src/platform/python/lib.h @@ -4,6 +4,7 @@ struct VFile; extern bool mPythonLoadScript(const char*, struct VFile*); extern void mPythonRunPending(); +extern bool mPythonLookupSymbol(const char* name, int32_t* out); #ifdef USE_DEBUGGERS extern void mPythonSetDebugger(struct mDebugger*); From 9ed7c9129d63194b40fd3ae2453ea6d6f6bafe0f Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 15 Jul 2017 20:38:45 -0700 Subject: [PATCH 5/8] Qt: Fix screen background improperly stretching --- CHANGES | 1 + src/platform/qt/Window.cpp | 28 ++++++++++++++++++++-------- src/platform/qt/Window.h | 4 +++- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/CHANGES b/CHANGES index 5c29d3390..03ce886ce 100644 --- a/CHANGES +++ b/CHANGES @@ -74,6 +74,7 @@ Bugfixes: - Qt: Ensure CLI backend is attached when submitting commands (fixes mgba.io/i/662) - Qt: Disable "New multiplayer window" when MAX_GBAS is reached (fixes mgba.io/i/107) - Qt: Fix game unpausing after frame advancing and refocusing + - Qt: Fix screen background improperly stretching - SDL: Fix game crash check - SDL: Fix race condition with audio thread when starting - SDL: Fix showing version number diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 4c480a487..046983b81 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -132,8 +132,9 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent) resizeFrame(QSize(GB_VIDEO_HORIZONTAL_PIXELS * i, GB_VIDEO_VERTICAL_PIXELS * i)); #endif m_screenWidget->setPixmap(m_logo); - m_screenWidget->setLockAspectRatio(m_logo.width(), m_logo.height()); + m_screenWidget->setDimensions(m_logo.width(), m_logo.height()); m_screenWidget->setLockIntegerScaling(false); + m_screenWidget->setLockAspectRatio(true); setCentralWidget(m_screenWidget); connect(m_controller, &GameController::gameStarted, this, &Window::gameStarted); @@ -151,7 +152,6 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent) QPixmap pixmap; pixmap.convertFromImage(currentImage); m_screenWidget->setPixmap(pixmap); - m_screenWidget->setLockAspectRatio(width, height); }); connect(m_controller, &GameController::gamePaused, m_display, &Display::pauseDrawing); #ifndef Q_OS_MAC @@ -740,7 +740,9 @@ void Window::gameStarted(mCoreThread* context, const QString& fname) { context->core->desiredVideoDimensions(context->core, &width, &height); m_display->setMinimumSize(width, height); m_screenWidget->setMinimumSize(m_display->minimumSize()); + m_screenWidget->setDimensions(width, height); m_config->updateOption("lockIntegerScaling"); + m_config->updateOption("lockAspectRatio"); if (m_savedScale > 0) { resizeFrame(QSize(width, height) * m_savedScale); } @@ -802,8 +804,9 @@ void Window::gameStopped() { setWindowFilePath(QString()); updateTitle(); detachWidget(m_display); - m_screenWidget->setLockAspectRatio(m_logo.width(), m_logo.height()); + m_screenWidget->setDimensions(m_logo.width(), m_logo.height()); m_screenWidget->setLockIntegerScaling(false); + m_screenWidget->setLockAspectRatio(true); m_screenWidget->setPixmap(m_logo); m_screenWidget->unsetCursor(); #ifdef M_CORE_GB @@ -1272,6 +1275,9 @@ void Window::setupMenu(QMenuBar* menubar) { lockAspectRatio->addBoolean(tr("Lock aspect ratio"), avMenu); lockAspectRatio->connect([this](const QVariant& value) { m_display->lockAspectRatio(value.toBool()); + if (m_controller->isLoaded()) { + m_screenWidget->setLockAspectRatio(value.toBool()); + } }, this); m_config->updateOption("lockAspectRatio"); @@ -1662,7 +1668,7 @@ QSize WindowBackground::sizeHint() const { return m_sizeHint; } -void WindowBackground::setLockAspectRatio(int width, int height) { +void WindowBackground::setDimensions(int width, int height) { m_aspectWidth = width; m_aspectHeight = height; } @@ -1671,6 +1677,10 @@ void WindowBackground::setLockIntegerScaling(bool lock) { m_lockIntegerScaling = lock; } +void WindowBackground::setLockAspectRatio(bool lock) { + m_lockAspectRatio = lock; +} + void WindowBackground::paintEvent(QPaintEvent*) { const QPixmap* logo = pixmap(); if (!logo) { @@ -1681,10 +1691,12 @@ void WindowBackground::paintEvent(QPaintEvent*) { painter.fillRect(QRect(QPoint(), size()), Qt::black); QSize s = size(); QSize ds = s; - if (ds.width() * m_aspectHeight > ds.height() * m_aspectWidth) { - ds.setWidth(ds.height() * m_aspectWidth / m_aspectHeight); - } else if (ds.width() * m_aspectHeight < ds.height() * m_aspectWidth) { - ds.setHeight(ds.width() * m_aspectHeight / m_aspectWidth); + if (m_lockAspectRatio) { + if (ds.width() * m_aspectHeight > ds.height() * m_aspectWidth) { + ds.setWidth(ds.height() * m_aspectWidth / m_aspectHeight); + } else if (ds.width() * m_aspectHeight < ds.height() * m_aspectWidth) { + ds.setHeight(ds.width() * m_aspectHeight / m_aspectWidth); + } } if (m_lockIntegerScaling) { ds.setWidth(ds.width() - ds.width() % m_aspectWidth); diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index 7b1fec72a..c009c7ffa 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -214,8 +214,9 @@ public: void setSizeHint(const QSize& size); virtual QSize sizeHint() const override; - void setLockAspectRatio(int width, int height); + void setDimensions(int width, int height); void setLockIntegerScaling(bool lock); + void setLockAspectRatio(bool lock); protected: virtual void paintEvent(QPaintEvent*) override; @@ -224,6 +225,7 @@ private: QSize m_sizeHint; int m_aspectWidth; int m_aspectHeight; + bool m_lockAspectRatio; bool m_lockIntegerScaling; }; From 64409d9ca7b3af1e83f7fb68737935015739cd40 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 16 Jul 2017 09:44:58 -0700 Subject: [PATCH 6/8] Core: ELF support --- CHANGES | 2 + CMakeLists.txt | 10 +++ README.md | 1 + include/mgba-util/elf-read.h | 46 ++++++++++++ include/mgba/core/core.h | 8 ++ include/mgba/internal/gba/gba.h | 1 + src/core/core.c | 69 ++++++++++++++++++ src/gba/core.c | 35 ++++++++- src/gba/gba.c | 46 ++++++++++++ src/gba/memory.c | 5 +- src/util/elf-read.c | 125 ++++++++++++++++++++++++++++++++ 11 files changed, 345 insertions(+), 3 deletions(-) create mode 100644 include/mgba-util/elf-read.h create mode 100644 src/util/elf-read.c diff --git a/CHANGES b/CHANGES index 03ce886ce..a1ca736b9 100644 --- a/CHANGES +++ b/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: diff --git a/CMakeLists.txt b/CMakeLists.txt index 25c698881..2f3b42fc7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.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 @@ 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}") diff --git a/README.md b/README.md index 3b52e1573..aaa8fd3fa 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/include/mgba-util/elf-read.h b/include/mgba-util/elf-read.h new file mode 100644 index 000000000..e98ce0828 --- /dev/null +++ b/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 + +CXX_GUARD_START + +#ifdef USE_ELF + +#include +#include + +#include + +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 diff --git a/include/mgba/core/core.h b/include/mgba/core/core.h index b5a988b85..d54a5c985 100644 --- a/include/mgba/core/core.h +++ b/include/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 diff --git a/include/mgba/internal/gba/gba.h b/include/mgba/internal/gba/gba.h index 5710864e0..e8bff15ee 100644 --- a/include/mgba/internal/gba/gba.h +++ b/include/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); diff --git a/src/core/core.c b/src/core/core.c index d42ce15ce..d27cc48ef 100644 --- a/src/core/core.c +++ b/src/core/core.c @@ -8,6 +8,11 @@ #include #include #include +#include + +#ifdef USE_ELF +#include +#endif #ifdef M_CORE_GB #include @@ -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 diff --git a/src/gba/core.c b/src/gba/core.c index 10425e645..695f842b6 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -20,6 +21,9 @@ #include #include #include +#ifdef USE_ELF +#include +#endif #include #include #include @@ -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 diff --git a/src/gba/gba.c b/src/gba/gba.c index 06f39f71b..ae9176fd0 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -21,6 +21,10 @@ #include #include +#ifdef USE_ELF +#include +#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; } diff --git a/src/gba/memory.c b/src/gba/memory.c index 50da6f524..8b407fa09 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -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)); diff --git a/src/util/elf-read.c b/src/util/elf-read.c new file mode 100644 index 000000000..8ac38774d --- /dev/null +++ b/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 + +#ifdef USE_ELF + +#include + +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 From d78065e8232a67e63cc08f3612e683e16b7a0fcd Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 16 Jul 2017 10:51:20 -0700 Subject: [PATCH 7/8] Util: Use elf.h if elf_repl.h is not found --- CMakeLists.txt | 4 ++++ include/mgba-util/elf-read.h | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f3b42fc7..d5410d922 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -607,6 +607,10 @@ endif() if(USE_ELF) list(APPEND FEATURES ELF) include_directories(AFTER ${LIBELF_INCLUDE_DIRS}) + find_file(ELF_REPL_H elf_repl.h PATHS ${LIBELF_INCLUDE_DIRS}) + if (ELF_REPL_H) + add_definitions(-DUSE_ELF_REPL) + endif() list(APPEND DEPENDENCY_LIB ${LIBELF_LIBRARIES}) set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libelfg0") endif() diff --git a/include/mgba-util/elf-read.h b/include/mgba-util/elf-read.h index e98ce0828..e3886ac64 100644 --- a/include/mgba-util/elf-read.h +++ b/include/mgba-util/elf-read.h @@ -13,7 +13,12 @@ CXX_GUARD_START #ifdef USE_ELF #include + +#if USE_ELF_REPL #include +#else +#include +#endif #include From 0f5dab6514dfc3eab009c79bd2c754f72fc5ee2d Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 16 Jul 2017 12:04:50 -0700 Subject: [PATCH 8/8] All: Minor text fixes --- CHANGES | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index a1ca736b9..2da41dc12 100644 --- a/CHANGES +++ b/CHANGES @@ -8,7 +8,7 @@ Misc: - GBA: Extend oddly-sized ROMs to full address space (fixes mgba.io/i/722) - All: Make FIXED_ROM_BUFFER an option instead of 3DS-only -0.6.0: (Future) +0.6.0: (2017-07-16) Features: - Library view - Sprite viewer @@ -76,11 +76,9 @@ Bugfixes: - Qt: Ensure CLI backend is attached when submitting commands (fixes mgba.io/i/662) - Qt: Disable "New multiplayer window" when MAX_GBAS is reached (fixes mgba.io/i/107) - Qt: Fix game unpausing after frame advancing and refocusing - - Qt: Fix screen background improperly stretching - SDL: Fix game crash check - SDL: Fix race condition with audio thread when starting - SDL: Fix showing version number - - SDL: Fix cheats not loading - Test: Fix crash when loading invalid file - Test: Fix crash when fuzzing fails to load a file - Test: Don't rely on core for frames elapsed @@ -185,6 +183,8 @@ Bugfixes: - Core: Fix rewinding getting out of sync (fixes mgba.io/i/791) - Qt: Fix GL-less build - Qt: Fix Software renderer not handling alpha bits properly + - Qt: Fix screen background improperly stretching + - SDL: Fix cheats not loading Misc: - GB Serialize: Add MBC state serialization - GBA Memory: Call crash callbacks regardless of if hard crash is enabled