From 3dfac5704760fd270561d570965edecddfe32743 Mon Sep 17 00:00:00 2001 From: Joshua Smith Date: Sun, 28 Jul 2024 13:27:35 -0500 Subject: [PATCH 01/12] add tooling to fixup debugging info --- build.sh | 3 +++ fixup_elf2.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 fixup_elf2.py diff --git a/build.sh b/build.sh index f73cc08b9e..c0ea70dffc 100755 --- a/build.sh +++ b/build.sh @@ -25,3 +25,6 @@ elif [ "$target" = rom ]; then else "${MESON:-meson}" compile -C build "$target" "$@" fi + +# remap incorrect source paths due to Wine build process +python3 fixup_elf2.py diff --git a/fixup_elf2.py b/fixup_elf2.py new file mode 100644 index 0000000000..792b22b247 --- /dev/null +++ b/fixup_elf2.py @@ -0,0 +1,32 @@ +import subprocess +import os + +try: + os.remove("build/sources.txt") +except: + pass + +print("Retrieving source file list via debugedit") +subprocess.run(["debugedit", "-l", "build/sources.txt", "build/main.nef"]) + +with open("build/sources.txt") as f: + content = f.read() + all_sources = content.split('\0') + +print("Identifying unique source directories") +source_paths = [] +for source in all_sources: + source_parts = source.split(":") + if len(source_parts) != 3: continue + + source_original = source_parts[0] + ":" + source_parts[1] + source_original = source_original[:-2] + + if source_original not in source_paths: + source_paths.append(source_original) + +print(f"Identified {len(source_paths)} unique source directories from source file list") +for source in source_paths: + remapped = source.replace("\\", "/")[2:] + print(f"Remapping source path from {source} to {remapped} using debugedit.") + subprocess.run(["debugedit", "-b", source, "-d", remapped, "build/main.nef"]) \ No newline at end of file From 851e3ab17b66c6553ed8c74c2c00b5f93dd020df Mon Sep 17 00:00:00 2001 From: Joshua Smith Date: Fri, 4 Oct 2024 08:59:24 -0500 Subject: [PATCH 02/12] backbone for overlay debugging, does not quite work --- include/game_overlay.h | 15 +++++++++++++++ src/game_overlay.c | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/include/game_overlay.h b/include/game_overlay.h index d876ffdde5..6b4774f39f 100644 --- a/include/game_overlay.h +++ b/include/game_overlay.h @@ -13,4 +13,19 @@ void Overlay_UnloadByID(const FSOverlayID param0); int Overlay_GetLoadDestination(const FSOverlayID param0); BOOL Overlay_LoadByID(const FSOverlayID param0, int param1); +typedef struct { + unsigned long vma; + unsigned long size; + unsigned long lma; + unsigned long mapped; +} struct_overlayTable; + +#define MAX_OVERLAYS 128 + +extern unsigned long _novlys; +extern const char _ovly_name[]; +extern struct_overlayTable _ovly_table[MAX_OVERLAYS]; + +static void _ovly_debug_event(void); + #endif // POKEPLATINUM_UNK_020064F0_H diff --git a/src/game_overlay.c b/src/game_overlay.c index 8321142e1e..7152857045 100644 --- a/src/game_overlay.c +++ b/src/game_overlay.c @@ -34,6 +34,38 @@ BOOL Overlay_LoadByID(const FSOverlayID param0, int param1); static UnkStruct_021BF370 Unk_021BF370; +/* Added to support GDB overlay debugging. */ +const char _ovly_name[] = "overlay%d"; +unsigned long _novlys = MAX_OVERLAYS; +struct_overlayTable _ovly_table[MAX_OVERLAYS] = {}; +static void _ovly_debug_event(void) {} +void UnloadOverlayGDB(const FSOverlayID overlayID) +{ + GF_ASSERT(overlayID < _novlys); + _ovly_table[overlayID].mapped = 0; + _ovly_debug_event(); +} +void LoadOverlayGDB(const FSOverlayID overlayID) +{ + FSOverlayInfo overlayInfo; + + // dummies to force symbols to exist + GF_ASSERT(overlayID < _novlys); + strcmp(_ovly_name, _ovly_name); + + if(_ovly_table[overlayID].mapped != 0) return; + + // 1. fetch overlay info to identify vma/lma + GF_ASSERT(FS_LoadOverlayInfo(&overlayInfo, MI_PROCESSOR_ARM9, overlayID) == TRUE); + + // 2. add entry to _ovly_table + _ovly_table[overlayID].vma = overlayInfo.header.ram_address; + _ovly_table[overlayID].lma = 0; + _ovly_table[overlayID].size = overlayInfo.header.ram_size; + _ovly_table[overlayID].mapped = 1; + _ovly_debug_event(); +} + static void FreeOverlayAllocation(PMiLoadedOverlay *param0) { GF_ASSERT(param0->isActive == 1); @@ -52,6 +84,7 @@ void Overlay_UnloadByID(const FSOverlayID overlayID) for (i = 0; i < 8; i++) { if ((table[i].isActive == 1) && (table[i].id == overlayID)) { FreeOverlayAllocation(&table[i]); + UnloadOverlayGDB(overlayID); return; } } @@ -195,6 +228,7 @@ static BOOL GetOverlayRamBounds(const FSOverlayID overlayID, u32 *start, u32 *en static BOOL LoadOverlayNormal(MIProcessor proc, FSOverlayID overlayID) { + LoadOverlayGDB(overlayID); return FS_LoadOverlay(proc, overlayID); } @@ -210,6 +244,8 @@ static BOOL LoadOverlayNoInit(MIProcessor proc, FSOverlayID overlayID) return FALSE; } + LoadOverlayGDB(overlayID); + FS_StartOverlay(&info); return TRUE; } @@ -223,6 +259,8 @@ static BOOL LoadOverlayNoInitAsync(MIProcessor proc, FSOverlayID overlayID) return FALSE; } + LoadOverlayGDB(overlayID); + FS_InitFile(&file); FS_LoadOverlayImageAsync(&info, &file); FS_WaitAsync(&file); From 33ac4aa14640802e300023cc86dfec34cc570a20 Mon Sep 17 00:00:00 2001 From: Joshua Smith Date: Fri, 4 Oct 2024 11:58:13 -0500 Subject: [PATCH 03/12] integrate debugedit tool into meson build process --- build.sh | 5 +---- meson.build | 16 ++++++++++++++++ src/game_overlay.c | 7 ++++++- tools/scripts/meson.build | 1 + fixup_elf2.py => tools/scripts/nef_fixer.py | 17 +++++++++++++---- 5 files changed, 37 insertions(+), 9 deletions(-) rename fixup_elf2.py => tools/scripts/nef_fixer.py (69%) diff --git a/build.sh b/build.sh index c0ea70dffc..5b2625aa69 100755 --- a/build.sh +++ b/build.sh @@ -21,10 +21,7 @@ export MESON_RSP_THRESHOLD=16387 if [ "$target" = test ]; then "${MESON:-meson}" test -C build "$@" elif [ "$target" = rom ]; then - "${MESON:-meson}" compile -C build "pokeplatinum.us.nds" + "${MESON:-meson}" compile -C build "pokeplatinum.us.nds" "debug.nef" else "${MESON:-meson}" compile -C build "$target" "$@" fi - -# remap incorrect source paths due to Wine build process -python3 fixup_elf2.py diff --git a/meson.build b/meson.build index bde780a575..fb2d02080e 100644 --- a/meson.build +++ b/meson.build @@ -228,6 +228,22 @@ pokeplatinum_nds = custom_target('pokeplatinum.us.nds', build_by_default: true ) +############################################################ +### NEF FIXER ### +############################################################ +nef_fixer = custom_target('debug.nef', + output: [ + 'debug.nef' + ], + input: [ + main + ], + command : [ + nef_fixer_py, '@INPUT@', '@OUTPUT@' + ], + build_by_default: true +) + ############################################################ ### TESTS ### diff --git a/src/game_overlay.c b/src/game_overlay.c index 7152857045..b37d2d3e31 100644 --- a/src/game_overlay.c +++ b/src/game_overlay.c @@ -59,9 +59,14 @@ void LoadOverlayGDB(const FSOverlayID overlayID) GF_ASSERT(FS_LoadOverlayInfo(&overlayInfo, MI_PROCESSOR_ARM9, overlayID) == TRUE); // 2. add entry to _ovly_table + // note that this is a little hacky. the VMA is correct but the LMA is not exposed by the OverlayManager + // and the size field is not correct compared to what's stored in the NEF. + // the standard overlay manager in GDB bases comparisons on VMA and LMA, so it's not viable here. + // requires a custom GDB build which uses section name and which can override section size. + // see _ovly_table[overlayID].vma = overlayInfo.header.ram_address; _ovly_table[overlayID].lma = 0; - _ovly_table[overlayID].size = overlayInfo.header.ram_size; + _ovly_table[overlayID].size = overlayInfo.header.ram_size; _ovly_table[overlayID].mapped = 1; _ovly_debug_event(); } diff --git a/tools/scripts/meson.build b/tools/scripts/meson.build index 5c20a881d9..86826b06f1 100644 --- a/tools/scripts/meson.build +++ b/tools/scripts/meson.build @@ -10,3 +10,4 @@ make_species_tables_py = find_program('make_species_tables.py', native: true) make_tutorable_moves_py = find_program('make_tutorable_moves.py', native: true) make_pokedex_data_py = find_program('make_pokedex_data.py', native: true) make_pokedex_message_banks_py = find_program('make_pokedex_message_banks.py', native: true) +nef_fixer_py = find_program('nef_fixer.py', native: true) \ No newline at end of file diff --git a/fixup_elf2.py b/tools/scripts/nef_fixer.py similarity index 69% rename from fixup_elf2.py rename to tools/scripts/nef_fixer.py index 792b22b247..713856c1ae 100644 --- a/fixup_elf2.py +++ b/tools/scripts/nef_fixer.py @@ -1,15 +1,24 @@ +#!/usr/bin/env python3 import subprocess import os +import shutil +import sys try: - os.remove("build/sources.txt") + os.remove("sources.txt") except: pass +source = sys.argv[1] +dest = sys.argv[2] + +print(f"Copying {source} to {dest}") +shutil.copyfile(source, dest) + print("Retrieving source file list via debugedit") -subprocess.run(["debugedit", "-l", "build/sources.txt", "build/main.nef"]) +subprocess.run(["debugedit", "-l", "sources.txt", dest]) -with open("build/sources.txt") as f: +with open("sources.txt") as f: content = f.read() all_sources = content.split('\0') @@ -29,4 +38,4 @@ for source in source_paths: remapped = source.replace("\\", "/")[2:] print(f"Remapping source path from {source} to {remapped} using debugedit.") - subprocess.run(["debugedit", "-b", source, "-d", remapped, "build/main.nef"]) \ No newline at end of file + subprocess.run(["debugedit", "-b", source, "-d", remapped, dest]) \ No newline at end of file From 7f68c4e93d8d4be37e13f30db0ed024360103419 Mon Sep 17 00:00:00 2001 From: Joshua Smith Date: Fri, 4 Oct 2024 12:02:00 -0500 Subject: [PATCH 04/12] documentation for gdb --- README.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ src/game_overlay.c | 2 +- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fa27baa608..2ba656092c 100644 --- a/README.md +++ b/README.md @@ -7,3 +7,49 @@ This repository builds the following ROMs: * [**pokeplatinum.us.nds**](https://datomatic.no-intro.org/index.php?page=show_record&s=28&n=3541) `sha1: ce81046eda7d232513069519cb2085349896dec7` For contacts and other pret projects, see [pret.github.io](https://pret.github.io/). In addition to the pret Discord, also see the [VoidMatrix Discord (#decomp)](https://discord.gg/prUAgd5). + +## Debugging with GDB + overlays + +1. Download and build custom GDB debugger from https://github.com/joshua-smith-12/binutils-gdb-nds - this build includes changes to help aid overlay debugging. (Regular gdb-multiarch works as well but will handle overlays poorly) + +2. Create a `.vscode/launch.json` with the below contents, updating `miDebuggerPath`: + +``` +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug in melonDS", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/debug.nef", + "MIMode": "gdb", + "cwd": "${workspaceFolder}", + "externalConsole": true, + "miDebuggerServerAddress": "localhost:3333", + "miDebuggerPath": "/path/to/custom/build/gdb/gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set architecture", + "text": "set architecture auto" + }, + { + "description": "Enable overlays", + "text": "overlay auto" + } + ], + "stopAtConnect": false, + "stopAtEntry": false + } + ] +} +``` + +3. Launch melonDS with GDB stub enabled and launch the pokeplatinum ROM (you may need to fiddle with the config file to make this work, melonDS GDB stub config is off and may not actually enable properly through the UI) + +4. Run debugger through VS Code, it should connect to melonDS automatically. \ No newline at end of file diff --git a/src/game_overlay.c b/src/game_overlay.c index b37d2d3e31..f76d13b4a0 100644 --- a/src/game_overlay.c +++ b/src/game_overlay.c @@ -63,7 +63,7 @@ void LoadOverlayGDB(const FSOverlayID overlayID) // and the size field is not correct compared to what's stored in the NEF. // the standard overlay manager in GDB bases comparisons on VMA and LMA, so it's not viable here. // requires a custom GDB build which uses section name and which can override section size. - // see + // see https://github.com/joshua-smith-12/binutils-gdb-nds _ovly_table[overlayID].vma = overlayInfo.header.ram_address; _ovly_table[overlayID].lma = 0; _ovly_table[overlayID].size = overlayInfo.header.ram_size; From 133f80ac2ba2a37e1fc7198652a6309d78860339 Mon Sep 17 00:00:00 2001 From: Joshua Smith Date: Tue, 8 Oct 2024 11:20:16 -0500 Subject: [PATCH 05/12] partial overlay breakpoint support via GDB --- README.md | 6 ++-- build.sh | 2 +- include/game_overlay.h | 3 +- meson.build | 15 ++++++++- src/game_overlay.c | 14 ++++---- tools/scripts/meson.build | 3 +- tools/scripts/overlay_mapper.py | 58 +++++++++++++++++++++++++++++++++ 7 files changed, 87 insertions(+), 14 deletions(-) create mode 100644 tools/scripts/overlay_mapper.py diff --git a/README.md b/README.md index 2ba656092c..422f0b30ef 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ For contacts and other pret projects, see [pret.github.io](https://pret.github.i }, { "description": "Set architecture", - "text": "set architecture auto" + "text": "set architecture armv5te" }, { "description": "Enable overlays", @@ -52,4 +52,6 @@ For contacts and other pret projects, see [pret.github.io](https://pret.github.i 3. Launch melonDS with GDB stub enabled and launch the pokeplatinum ROM (you may need to fiddle with the config file to make this work, melonDS GDB stub config is off and may not actually enable properly through the UI) -4. Run debugger through VS Code, it should connect to melonDS automatically. \ No newline at end of file +4. Run debugger through VS Code, it should connect to melonDS automatically. + +5. Pause the debugger and run `-exec overlay section-map build/overlay.map` (this cannot currently be included in setupCommands due to issues with architecture loading). \ No newline at end of file diff --git a/build.sh b/build.sh index 5b2625aa69..55e69fd51f 100755 --- a/build.sh +++ b/build.sh @@ -21,7 +21,7 @@ export MESON_RSP_THRESHOLD=16387 if [ "$target" = test ]; then "${MESON:-meson}" test -C build "$@" elif [ "$target" = rom ]; then - "${MESON:-meson}" compile -C build "pokeplatinum.us.nds" "debug.nef" + "${MESON:-meson}" compile -C build "pokeplatinum.us.nds" "debug.nef" "overlay.map" else "${MESON:-meson}" compile -C build "$target" "$@" fi diff --git a/include/game_overlay.h b/include/game_overlay.h index 6b4774f39f..aac8a52a68 100644 --- a/include/game_overlay.h +++ b/include/game_overlay.h @@ -16,14 +16,13 @@ BOOL Overlay_LoadByID(const FSOverlayID param0, int param1); typedef struct { unsigned long vma; unsigned long size; - unsigned long lma; + FSOverlayID ovly_id; unsigned long mapped; } struct_overlayTable; #define MAX_OVERLAYS 128 extern unsigned long _novlys; -extern const char _ovly_name[]; extern struct_overlayTable _ovly_table[MAX_OVERLAYS]; static void _ovly_debug_event(void); diff --git a/meson.build b/meson.build index fb2d02080e..0b02ad4d40 100644 --- a/meson.build +++ b/meson.build @@ -229,7 +229,7 @@ pokeplatinum_nds = custom_target('pokeplatinum.us.nds', ) ############################################################ -### NEF FIXER ### +### GDB HELPERS ### ############################################################ nef_fixer = custom_target('debug.nef', output: [ @@ -244,6 +244,19 @@ nef_fixer = custom_target('debug.nef', build_by_default: true ) +ovly_mapper = custom_target('overlay.map', + output: [ + 'overlay.map' + ], + input: [ + main_lsf + ], + command : [ + overlay_mapper_py, '@INPUT@', '@OUTPUT@' + ], + build_by_default: true +) + ############################################################ ### TESTS ### diff --git a/src/game_overlay.c b/src/game_overlay.c index f76d13b4a0..78064c817c 100644 --- a/src/game_overlay.c +++ b/src/game_overlay.c @@ -35,7 +35,6 @@ BOOL Overlay_LoadByID(const FSOverlayID param0, int param1); static UnkStruct_021BF370 Unk_021BF370; /* Added to support GDB overlay debugging. */ -const char _ovly_name[] = "overlay%d"; unsigned long _novlys = MAX_OVERLAYS; struct_overlayTable _ovly_table[MAX_OVERLAYS] = {}; static void _ovly_debug_event(void) {} @@ -48,25 +47,26 @@ void UnloadOverlayGDB(const FSOverlayID overlayID) void LoadOverlayGDB(const FSOverlayID overlayID) { FSOverlayInfo overlayInfo; + FSFileID fileInfo; - // dummies to force symbols to exist + // dummy to force symbols to exist GF_ASSERT(overlayID < _novlys); - strcmp(_ovly_name, _ovly_name); if(_ovly_table[overlayID].mapped != 0) return; - // 1. fetch overlay info to identify vma/lma + // 1. fetch overlay info to identify vma GF_ASSERT(FS_LoadOverlayInfo(&overlayInfo, MI_PROCESSOR_ARM9, overlayID) == TRUE); + fileInfo = FS_GetOverlayFileID(&overlayInfo); // 2. add entry to _ovly_table // note that this is a little hacky. the VMA is correct but the LMA is not exposed by the OverlayManager // and the size field is not correct compared to what's stored in the NEF. // the standard overlay manager in GDB bases comparisons on VMA and LMA, so it's not viable here. - // requires a custom GDB build which uses section name and which can override section size. + // requires a custom GDB build which uses overlay mappings and which can override section size. // see https://github.com/joshua-smith-12/binutils-gdb-nds _ovly_table[overlayID].vma = overlayInfo.header.ram_address; - _ovly_table[overlayID].lma = 0; - _ovly_table[overlayID].size = overlayInfo.header.ram_size; + _ovly_table[overlayID].ovly_id = overlayID; + _ovly_table[overlayID].size = overlayInfo.header.ram_size; _ovly_table[overlayID].mapped = 1; _ovly_debug_event(); } diff --git a/tools/scripts/meson.build b/tools/scripts/meson.build index 86826b06f1..91557eac66 100644 --- a/tools/scripts/meson.build +++ b/tools/scripts/meson.build @@ -10,4 +10,5 @@ make_species_tables_py = find_program('make_species_tables.py', native: true) make_tutorable_moves_py = find_program('make_tutorable_moves.py', native: true) make_pokedex_data_py = find_program('make_pokedex_data.py', native: true) make_pokedex_message_banks_py = find_program('make_pokedex_message_banks.py', native: true) -nef_fixer_py = find_program('nef_fixer.py', native: true) \ No newline at end of file +nef_fixer_py = find_program('nef_fixer.py', native: true) +overlay_mapper_py = find_program('overlay_mapper.py', native: true) \ No newline at end of file diff --git a/tools/scripts/overlay_mapper.py b/tools/scripts/overlay_mapper.py new file mode 100644 index 0000000000..ca582a3f4c --- /dev/null +++ b/tools/scripts/overlay_mapper.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +import subprocess +import os +import shutil +import sys + +try: + os.remove("overlay.map") +except: + pass + +source = sys.argv[1] +dest = sys.argv[2] + +# TERRIBLE but necessary to allow using the LSF file +def base_path(path): + splitted = path.split("_") + maybe_path = "" + found_dir = ".." # need to escape builddir because this sucks + while len(splitted) > 1: + segment = splitted[0] + splitted = splitted[1:] + maybe_path += segment + if os.path.isdir(found_dir + "/" + maybe_path): + found_dir = found_dir + "/" + maybe_path + maybe_path = "" + else: + maybe_path += "_" + maybe_path += splitted[0] + return maybe_path[:-2] # remove '.o' + +# VERY inflexible parsing! +overlays = {} +sources = {} +overlay_index = 0 +current_overlay = None +in_block = False +with open(source) as f: + for line in f.readlines(): + if current_overlay == None and not in_block and line.strip().startswith("Overlay"): + current_overlay = line.strip().replace("Overlay ", "") + overlays[current_overlay] = overlay_index + overlay_index += 1 + elif not in_block and line.strip() == "{": + in_block = True + elif in_block and line.strip() == "}": + in_block = False + current_overlay = None + elif current_overlay != None and in_block and line.strip().startswith("Object"): + obj_name = base_path(line.strip().replace("Object ", "").split("/")[-1]) + sources[obj_name] = current_overlay + +with open(dest, mode="w") as f: + for ovly in overlays: + f.write(f"OVERLAY {ovly}:{overlays[ovly]}\n") + f.write("\n") + for s in sources: + f.write(f"SOURCE {s}:{sources[s]}\n") \ No newline at end of file From 207d85fdffeb99f94442883cc8e4a9686050ac24 Mon Sep 17 00:00:00 2001 From: Joshua Smith Date: Mon, 21 Oct 2024 14:41:42 -0500 Subject: [PATCH 06/12] some code cleanup and commenting to make decisions more clear --- .gitignore | 3 +- .vscode/launch.json | 39 ++++++++++++++++++++++ include/game_overlay.h | 12 ++++++- src/game_overlay.c | 28 +++++++++++----- tools/debug/meson.build | 2 ++ tools/{scripts => debug}/nef_fixer.py | 4 +++ tools/{scripts => debug}/overlay_mapper.py | 4 +++ tools/meson.build | 1 + tools/scripts/meson.build | 4 +-- 9 files changed, 83 insertions(+), 14 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 tools/debug/meson.build rename tools/{scripts => debug}/nef_fixer.py (74%) rename tools/{scripts => debug}/overlay_mapper.py (84%) diff --git a/.gitignore b/.gitignore index ccfa5a58c7..0b997e9a28 100644 --- a/.gitignore +++ b/.gitignore @@ -78,7 +78,8 @@ cmake-build-* diff.txt .vs/ -.vscode/ +.vscode/* +!.vscode/launch.json temp_asm/ asm_old/ diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000..45ac133153 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,39 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug in melonDS", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/debug.nef", + "MIMode": "gdb", + "cwd": "${workspaceFolder}", + "externalConsole": true, + "miDebuggerServerAddress": "localhost:3333", + // point this to your own gdb path... + "miDebuggerPath": "/home/ziddia/Desktop/PokePlat/binutils-gdb/gdb/gdb", + //"miDebuggerPath": "gdb-multiarch", + "setupCommands": [ + { + "description": "Enable pretty-printing", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set architecture", + "text": "set architecture armv5te" + }, + { + "description": "Enable overlays", + "text": "overlay auto" + }, + { + "description": "Enable overlay map", + "text": "overlay map build/overlay.map" + } + ], + "stopAtConnect": false, + "stopAtEntry": false + } + ] +} \ No newline at end of file diff --git a/include/game_overlay.h b/include/game_overlay.h index aac8a52a68..a55ad17c73 100644 --- a/include/game_overlay.h +++ b/include/game_overlay.h @@ -13,18 +13,28 @@ void Overlay_UnloadByID(const FSOverlayID param0); int Overlay_GetLoadDestination(const FSOverlayID param0); BOOL Overlay_LoadByID(const FSOverlayID param0, int param1); +// TODO: this should integrate into build system instead of a manual define here. +// to turn off overlay debugging and build a matching ROM, undefine this. +#define GDB_DEBUGGING + +#ifdef GDB_DEBUGGING +// describes a single overlay entry, which GDB can inspect to determine which overlays are loaded. typedef struct { unsigned long vma; unsigned long size; - FSOverlayID ovly_id; + FSOverlayID id; unsigned long mapped; } struct_overlayTable; +// this is set based on the current number of overlays, other projects might need more! #define MAX_OVERLAYS 128 +// externs required for GDB to access overlay state extern unsigned long _novlys; extern struct_overlayTable _ovly_table[MAX_OVERLAYS]; +// event callback which GDB will hook and use to refresh overlay state static void _ovly_debug_event(void); +#endif // GDB_DEBUGGING #endif // POKEPLATINUM_UNK_020064F0_H diff --git a/src/game_overlay.c b/src/game_overlay.c index 78064c817c..63d737ed92 100644 --- a/src/game_overlay.c +++ b/src/game_overlay.c @@ -34,42 +34,44 @@ BOOL Overlay_LoadByID(const FSOverlayID param0, int param1); static UnkStruct_021BF370 Unk_021BF370; +#ifdef GDB_DEBUGGING + /* Added to support GDB overlay debugging. */ unsigned long _novlys = MAX_OVERLAYS; struct_overlayTable _ovly_table[MAX_OVERLAYS] = {}; +// this does nothing, but needs to be defined for GDB to refresh overlay state automatically. static void _ovly_debug_event(void) {} + +// helper function to mark a specific overlay as unmapped. void UnloadOverlayGDB(const FSOverlayID overlayID) { GF_ASSERT(overlayID < _novlys); - _ovly_table[overlayID].mapped = 0; + _ovly_table[overlayID].mapped--; _ovly_debug_event(); } +// helper function to mark a specific overlay as mapped, and provide its RAM address and size to GDB. void LoadOverlayGDB(const FSOverlayID overlayID) { FSOverlayInfo overlayInfo; - FSFileID fileInfo; - // dummy to force symbols to exist GF_ASSERT(overlayID < _novlys); - if(_ovly_table[overlayID].mapped != 0) return; - // 1. fetch overlay info to identify vma GF_ASSERT(FS_LoadOverlayInfo(&overlayInfo, MI_PROCESSOR_ARM9, overlayID) == TRUE); - fileInfo = FS_GetOverlayFileID(&overlayInfo); // 2. add entry to _ovly_table // note that this is a little hacky. the VMA is correct but the LMA is not exposed by the OverlayManager // and the size field is not correct compared to what's stored in the NEF. // the standard overlay manager in GDB bases comparisons on VMA and LMA, so it's not viable here. - // requires a custom GDB build which uses overlay mappings and which can override section size. + // requires a custom GDB build which maps based on section ID and can override section size. // see https://github.com/joshua-smith-12/binutils-gdb-nds _ovly_table[overlayID].vma = overlayInfo.header.ram_address; - _ovly_table[overlayID].ovly_id = overlayID; + _ovly_table[overlayID].id = overlayID; _ovly_table[overlayID].size = overlayInfo.header.ram_size; - _ovly_table[overlayID].mapped = 1; + _ovly_table[overlayID].mapped++; _ovly_debug_event(); } +#endif // GDB_DEBUGGING static void FreeOverlayAllocation(PMiLoadedOverlay *param0) { @@ -89,7 +91,9 @@ void Overlay_UnloadByID(const FSOverlayID overlayID) for (i = 0; i < 8; i++) { if ((table[i].isActive == 1) && (table[i].id == overlayID)) { FreeOverlayAllocation(&table[i]); + #ifdef GDB_DEBUGGING UnloadOverlayGDB(overlayID); + #endif return; } } @@ -233,7 +237,9 @@ static BOOL GetOverlayRamBounds(const FSOverlayID overlayID, u32 *start, u32 *en static BOOL LoadOverlayNormal(MIProcessor proc, FSOverlayID overlayID) { + #ifdef GDB_DEBUGGING LoadOverlayGDB(overlayID); + #endif return FS_LoadOverlay(proc, overlayID); } @@ -249,7 +255,9 @@ static BOOL LoadOverlayNoInit(MIProcessor proc, FSOverlayID overlayID) return FALSE; } + #ifdef GDB_DEBUGGING LoadOverlayGDB(overlayID); + #endif FS_StartOverlay(&info); return TRUE; @@ -264,7 +272,9 @@ static BOOL LoadOverlayNoInitAsync(MIProcessor proc, FSOverlayID overlayID) return FALSE; } + #ifdef GDB_DEBUGGING LoadOverlayGDB(overlayID); + #endif FS_InitFile(&file); FS_LoadOverlayImageAsync(&info, &file); diff --git a/tools/debug/meson.build b/tools/debug/meson.build new file mode 100644 index 0000000000..6288989a70 --- /dev/null +++ b/tools/debug/meson.build @@ -0,0 +1,2 @@ +nef_fixer_py = find_program('nef_fixer.py', native: true) +overlay_mapper_py = find_program('overlay_mapper.py', native: true) \ No newline at end of file diff --git a/tools/scripts/nef_fixer.py b/tools/debug/nef_fixer.py similarity index 74% rename from tools/scripts/nef_fixer.py rename to tools/debug/nef_fixer.py index 713856c1ae..9db2d11769 100644 --- a/tools/scripts/nef_fixer.py +++ b/tools/debug/nef_fixer.py @@ -1,4 +1,8 @@ #!/usr/bin/env python3 + +### This tool is responsible for using `debugedit` to fix Wine paths in the debuginfo file to real filesystem paths. +### VSC requires the debuginfo paths to map to real filesystem paths in order for VSC to correctly open sources during a debugging session. +### (This works fine on my Linux system, but may or may not work on WSL, and will certainly not work on other systems) import subprocess import os import shutil diff --git a/tools/scripts/overlay_mapper.py b/tools/debug/overlay_mapper.py similarity index 84% rename from tools/scripts/overlay_mapper.py rename to tools/debug/overlay_mapper.py index ca582a3f4c..4b5d7d8015 100644 --- a/tools/scripts/overlay_mapper.py +++ b/tools/debug/overlay_mapper.py @@ -1,4 +1,8 @@ #!/usr/bin/env python3 + +### This tool creates a file which maps overlay section names and source file names to overlay IDs. +### custom GDB build then processes this source map and uses it to identify what overlay a source file belongs to. +### (This makes it a lot easier to identify which overlapping overlay is used for a given input location). import subprocess import os import shutil diff --git a/tools/meson.build b/tools/meson.build index 91540a2ca9..ae80914f7b 100644 --- a/tools/meson.build +++ b/tools/meson.build @@ -5,6 +5,7 @@ subdir('json2bin') subdir('msgenc') subdir('postconf') subdir('scripts') +subdir('debug') # Prebuilt tools mwrap_exe = find_program('cw/mwrap', native: true) diff --git a/tools/scripts/meson.build b/tools/scripts/meson.build index 91557eac66..cd93ead08f 100644 --- a/tools/scripts/meson.build +++ b/tools/scripts/meson.build @@ -9,6 +9,4 @@ make_pl_growtbl_py = find_program('make_pl_growtbl.py', native: true) make_species_tables_py = find_program('make_species_tables.py', native: true) make_tutorable_moves_py = find_program('make_tutorable_moves.py', native: true) make_pokedex_data_py = find_program('make_pokedex_data.py', native: true) -make_pokedex_message_banks_py = find_program('make_pokedex_message_banks.py', native: true) -nef_fixer_py = find_program('nef_fixer.py', native: true) -overlay_mapper_py = find_program('overlay_mapper.py', native: true) \ No newline at end of file +make_pokedex_message_banks_py = find_program('make_pokedex_message_banks.py', native: true) \ No newline at end of file From b8d87b25cc56cab70f0581d9258bfb7605c4fb6a Mon Sep 17 00:00:00 2001 From: Joshua Smith Date: Mon, 21 Oct 2024 14:44:30 -0500 Subject: [PATCH 07/12] update README with simplified mapping instructions --- README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 422f0b30ef..53e167a9f7 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,9 @@ For contacts and other pret projects, see [pret.github.io](https://pret.github.i ## Debugging with GDB + overlays -1. Download and build custom GDB debugger from https://github.com/joshua-smith-12/binutils-gdb-nds - this build includes changes to help aid overlay debugging. (Regular gdb-multiarch works as well but will handle overlays poorly) +1. Download and build custom GDB debugger from https://github.com/joshua-smith-12/binutils-gdb-nds - this build includes changes to help aid overlay debugging. There is a release available from the repo, which was built for Ubuntu 24.04. (Regular gdb-multiarch works for basic debugging but will handle overlays poorly) -2. Create a `.vscode/launch.json` with the below contents, updating `miDebuggerPath`: +2. Create or update a `.vscode/launch.json` with the below contents, updating `miDebuggerPath`: ``` { @@ -41,6 +41,10 @@ For contacts and other pret projects, see [pret.github.io](https://pret.github.i { "description": "Enable overlays", "text": "overlay auto" + }, + { + "description": "Enable overlay map", + "text": "overlay map build/overlay.map" } ], "stopAtConnect": false, @@ -50,8 +54,6 @@ For contacts and other pret projects, see [pret.github.io](https://pret.github.i } ``` -3. Launch melonDS with GDB stub enabled and launch the pokeplatinum ROM (you may need to fiddle with the config file to make this work, melonDS GDB stub config is off and may not actually enable properly through the UI) - -4. Run debugger through VS Code, it should connect to melonDS automatically. +3. Launch melonDS with GDB stub enabled and launch the pokeplatinum ROM (you may need to fiddle with the config file to make this work, melonDS GDB stub config is odd and may not actually enable properly through the UI) -5. Pause the debugger and run `-exec overlay section-map build/overlay.map` (this cannot currently be included in setupCommands due to issues with architecture loading). \ No newline at end of file +4. Run debugger through VS Code, it should connect to melonDS automatically and configure overlay debugging for you. \ No newline at end of file From 1dd3fc8c6731c10064a54e39a191cb9f79b8d9dc Mon Sep 17 00:00:00 2001 From: Joshua Smith Date: Tue, 22 Oct 2024 19:08:37 -0500 Subject: [PATCH 08/12] use debug as build target, remove unneeded README change --- README.md | 50 -------------------------------------------------- build.sh | 2 ++ 2 files changed, 2 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index 53e167a9f7..fa27baa608 100644 --- a/README.md +++ b/README.md @@ -7,53 +7,3 @@ This repository builds the following ROMs: * [**pokeplatinum.us.nds**](https://datomatic.no-intro.org/index.php?page=show_record&s=28&n=3541) `sha1: ce81046eda7d232513069519cb2085349896dec7` For contacts and other pret projects, see [pret.github.io](https://pret.github.io/). In addition to the pret Discord, also see the [VoidMatrix Discord (#decomp)](https://discord.gg/prUAgd5). - -## Debugging with GDB + overlays - -1. Download and build custom GDB debugger from https://github.com/joshua-smith-12/binutils-gdb-nds - this build includes changes to help aid overlay debugging. There is a release available from the repo, which was built for Ubuntu 24.04. (Regular gdb-multiarch works for basic debugging but will handle overlays poorly) - -2. Create or update a `.vscode/launch.json` with the below contents, updating `miDebuggerPath`: - -``` -{ - "version": "0.2.0", - "configurations": [ - { - "name": "Debug in melonDS", - "type": "cppdbg", - "request": "launch", - "program": "${workspaceFolder}/build/debug.nef", - "MIMode": "gdb", - "cwd": "${workspaceFolder}", - "externalConsole": true, - "miDebuggerServerAddress": "localhost:3333", - "miDebuggerPath": "/path/to/custom/build/gdb/gdb", - "setupCommands": [ - { - "description": "Enable pretty-printing", - "text": "-enable-pretty-printing", - "ignoreFailures": true - }, - { - "description": "Set architecture", - "text": "set architecture armv5te" - }, - { - "description": "Enable overlays", - "text": "overlay auto" - }, - { - "description": "Enable overlay map", - "text": "overlay map build/overlay.map" - } - ], - "stopAtConnect": false, - "stopAtEntry": false - } - ] -} -``` - -3. Launch melonDS with GDB stub enabled and launch the pokeplatinum ROM (you may need to fiddle with the config file to make this work, melonDS GDB stub config is odd and may not actually enable properly through the UI) - -4. Run debugger through VS Code, it should connect to melonDS automatically and configure overlay debugging for you. \ No newline at end of file diff --git a/build.sh b/build.sh index 55e69fd51f..440ac591a1 100755 --- a/build.sh +++ b/build.sh @@ -21,6 +21,8 @@ export MESON_RSP_THRESHOLD=16387 if [ "$target" = test ]; then "${MESON:-meson}" test -C build "$@" elif [ "$target" = rom ]; then + "${MESON:-meson}" compile -C build "pokeplatinum.us.nds" +elif [ "$target" = debug ]; then "${MESON:-meson}" compile -C build "pokeplatinum.us.nds" "debug.nef" "overlay.map" else "${MESON:-meson}" compile -C build "$target" "$@" From 16e598909b7f6b4e2cc2063fa203456c6a963618 Mon Sep 17 00:00:00 2001 From: Joshua Smith Date: Tue, 22 Oct 2024 19:09:34 -0500 Subject: [PATCH 09/12] unreference my local filesystem for gdb path --- .vscode/launch.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 45ac133153..7d0293d46b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -11,7 +11,7 @@ "externalConsole": true, "miDebuggerServerAddress": "localhost:3333", // point this to your own gdb path... - "miDebuggerPath": "/home/ziddia/Desktop/PokePlat/binutils-gdb/gdb/gdb", + "miDebuggerPath": "/path/to/binutils-gdb/gdb/gdb", //"miDebuggerPath": "gdb-multiarch", "setupCommands": [ { From 9fbb2f9a8ce00748d6219bf8fc7283be2bff5a61 Mon Sep 17 00:00:00 2001 From: Joshua Smith Date: Tue, 22 Oct 2024 19:11:05 -0500 Subject: [PATCH 10/12] disable overlay debugging by default to fix build --- include/game_overlay.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/game_overlay.h b/include/game_overlay.h index a55ad17c73..72526cd08c 100644 --- a/include/game_overlay.h +++ b/include/game_overlay.h @@ -15,7 +15,7 @@ BOOL Overlay_LoadByID(const FSOverlayID param0, int param1); // TODO: this should integrate into build system instead of a manual define here. // to turn off overlay debugging and build a matching ROM, undefine this. -#define GDB_DEBUGGING +//#define GDB_DEBUGGING #ifdef GDB_DEBUGGING // describes a single overlay entry, which GDB can inspect to determine which overlays are loaded. From 37222bb7af06e4c7f431441e183807a4b6cf7653 Mon Sep 17 00:00:00 2001 From: Joshua Smith Date: Tue, 22 Oct 2024 19:12:41 -0500 Subject: [PATCH 11/12] pass format across the changed files --- include/game_overlay.h | 2 +- src/game_overlay.c | 20 +++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/include/game_overlay.h b/include/game_overlay.h index 72526cd08c..6939ec0dcb 100644 --- a/include/game_overlay.h +++ b/include/game_overlay.h @@ -15,7 +15,7 @@ BOOL Overlay_LoadByID(const FSOverlayID param0, int param1); // TODO: this should integrate into build system instead of a manual define here. // to turn off overlay debugging and build a matching ROM, undefine this. -//#define GDB_DEBUGGING +// #define GDB_DEBUGGING #ifdef GDB_DEBUGGING // describes a single overlay entry, which GDB can inspect to determine which overlays are loaded. diff --git a/src/game_overlay.c b/src/game_overlay.c index 63d737ed92..11aa6620bb 100644 --- a/src/game_overlay.c +++ b/src/game_overlay.c @@ -40,7 +40,9 @@ static UnkStruct_021BF370 Unk_021BF370; unsigned long _novlys = MAX_OVERLAYS; struct_overlayTable _ovly_table[MAX_OVERLAYS] = {}; // this does nothing, but needs to be defined for GDB to refresh overlay state automatically. -static void _ovly_debug_event(void) {} +static void _ovly_debug_event(void) +{ +} // helper function to mark a specific overlay as unmapped. void UnloadOverlayGDB(const FSOverlayID overlayID) @@ -91,9 +93,9 @@ void Overlay_UnloadByID(const FSOverlayID overlayID) for (i = 0; i < 8; i++) { if ((table[i].isActive == 1) && (table[i].id == overlayID)) { FreeOverlayAllocation(&table[i]); - #ifdef GDB_DEBUGGING +#ifdef GDB_DEBUGGING UnloadOverlayGDB(overlayID); - #endif +#endif return; } } @@ -237,9 +239,9 @@ static BOOL GetOverlayRamBounds(const FSOverlayID overlayID, u32 *start, u32 *en static BOOL LoadOverlayNormal(MIProcessor proc, FSOverlayID overlayID) { - #ifdef GDB_DEBUGGING +#ifdef GDB_DEBUGGING LoadOverlayGDB(overlayID); - #endif +#endif return FS_LoadOverlay(proc, overlayID); } @@ -255,9 +257,9 @@ static BOOL LoadOverlayNoInit(MIProcessor proc, FSOverlayID overlayID) return FALSE; } - #ifdef GDB_DEBUGGING +#ifdef GDB_DEBUGGING LoadOverlayGDB(overlayID); - #endif +#endif FS_StartOverlay(&info); return TRUE; @@ -272,9 +274,9 @@ static BOOL LoadOverlayNoInitAsync(MIProcessor proc, FSOverlayID overlayID) return FALSE; } - #ifdef GDB_DEBUGGING +#ifdef GDB_DEBUGGING LoadOverlayGDB(overlayID); - #endif +#endif FS_InitFile(&file); FS_LoadOverlayImageAsync(&info, &file); From d43f2c4e67f903a3bcbd5b800a6bb50b2a8b157f Mon Sep 17 00:00:00 2001 From: Joshua Smith Date: Wed, 23 Oct 2024 13:52:36 -0500 Subject: [PATCH 12/12] add build option to enable or disable debugging code --- build.sh | 2 ++ include/game_overlay.h | 4 ---- meson.build | 20 +++++++++++++++----- meson.options | 1 + 4 files changed, 18 insertions(+), 9 deletions(-) create mode 100644 meson.options diff --git a/build.sh b/build.sh index 440ac591a1..b8b8c87ec8 100755 --- a/build.sh +++ b/build.sh @@ -18,11 +18,13 @@ export NINJA_STATUS="[%p %f/%t] " export MESON_RSP_THRESHOLD=16387 # Build the project +"${MESON:-meson}" configure build "-Dgdb_debugging=false" if [ "$target" = test ]; then "${MESON:-meson}" test -C build "$@" elif [ "$target" = rom ]; then "${MESON:-meson}" compile -C build "pokeplatinum.us.nds" elif [ "$target" = debug ]; then + "${MESON:-meson}" configure build "-Dgdb_debugging=true" "${MESON:-meson}" compile -C build "pokeplatinum.us.nds" "debug.nef" "overlay.map" else "${MESON:-meson}" compile -C build "$target" "$@" diff --git a/include/game_overlay.h b/include/game_overlay.h index 6939ec0dcb..a869c3972e 100644 --- a/include/game_overlay.h +++ b/include/game_overlay.h @@ -13,10 +13,6 @@ void Overlay_UnloadByID(const FSOverlayID param0); int Overlay_GetLoadDestination(const FSOverlayID param0); BOOL Overlay_LoadByID(const FSOverlayID param0, int param1); -// TODO: this should integrate into build system instead of a manual define here. -// to turn off overlay debugging and build a matching ROM, undefine this. -// #define GDB_DEBUGGING - #ifdef GDB_DEBUGGING // describes a single overlay entry, which GDB can inspect to determine which overlays are loaded. typedef struct { diff --git a/meson.build b/meson.build index 0b02ad4d40..00b3a4ec48 100644 --- a/meson.build +++ b/meson.build @@ -19,7 +19,6 @@ public_includes = include_directories('include', 'asm', 'res') ### COMPILER FLAGS ### ############################################################ c_args = [ - '-O4,p', '-proc', 'arm946e', '-enum', 'int', '-lang', 'c99', @@ -35,6 +34,15 @@ c_args = [ '-sym', 'on' ] +if get_option('gdb_debugging') + c_args += [ + '-O1,p', + '-inline', 'off' + ] +else + c_args += '-O4,p' +endif + add_global_arguments(c_args, language: 'c', native: false @@ -46,6 +54,10 @@ pokeplatinum_args = [ '-DGAME_LANGUAGE=ENGLISH' ] +if get_option('gdb_debugging') + pokeplatinum_args += '-DGDB_DEBUGGING' +endif + asm_args = [ '-proc', 'arm5TE', '-16', @@ -240,8 +252,7 @@ nef_fixer = custom_target('debug.nef', ], command : [ nef_fixer_py, '@INPUT@', '@OUTPUT@' - ], - build_by_default: true + ] ) ovly_mapper = custom_target('overlay.map', @@ -253,8 +264,7 @@ ovly_mapper = custom_target('overlay.map', ], command : [ overlay_mapper_py, '@INPUT@', '@OUTPUT@' - ], - build_by_default: true + ] ) diff --git a/meson.options b/meson.options new file mode 100644 index 0000000000..fd78cd4ee3 --- /dev/null +++ b/meson.options @@ -0,0 +1 @@ +option('gdb_debugging', type : 'boolean', value : false) \ No newline at end of file