diff --git a/Makefile b/Makefile index d3bb9e83..4a3447ff 100644 --- a/Makefile +++ b/Makefile @@ -108,6 +108,8 @@ else ifeq ($(VERSION),sh) GRUCODE ?= f3dzex endif +DEBUG_MAP_STACKTRACE_FLAG := -D DEBUG_MAP_STACKTRACE + TARGET := sm64.$(VERSION) @@ -575,7 +577,7 @@ $(BUILD_DIR)/src/usb/usb.o: CFLAGS += -Wno-unused-variable -Wno-sign-compare -Wn $(BUILD_DIR)/src/usb/debug.o: OPT_FLAGS := -O0 $(BUILD_DIR)/src/usb/debug.o: CFLAGS += -Wno-unused-parameter -Wno-maybe-uninitialized -ALL_DIRS := $(BUILD_DIR) $(addprefix $(BUILD_DIR)/,$(SRC_DIRS) $(GODDARD_SRC_DIRS) $(LIBZ_SRC_DIRS) $(ULTRA_BIN_DIRS) $(BIN_DIRS) $(TEXTURE_DIRS) $(TEXT_DIRS) $(SOUND_SAMPLE_DIRS) $(addprefix levels/,$(LEVEL_DIRS)) rsp include) $(YAY0_DIR) $(addprefix $(YAY0_DIR)/,$(VERSION)) $(SOUND_BIN_DIR) $(SOUND_BIN_DIR)/sequences/$(VERSION) +ALL_DIRS := $(BUILD_DIR) $(addprefix $(BUILD_DIR)/,$(SRC_DIRS) asm/debug $(GODDARD_SRC_DIRS) $(LIBZ_SRC_DIRS) $(ULTRA_BIN_DIRS) $(BIN_DIRS) $(TEXTURE_DIRS) $(TEXT_DIRS) $(SOUND_SAMPLE_DIRS) $(addprefix levels/,$(LEVEL_DIRS)) rsp include) $(YAY0_DIR) $(addprefix $(YAY0_DIR)/,$(VERSION)) $(SOUND_BIN_DIR) $(SOUND_BIN_DIR)/sequences/$(VERSION) # Make sure build directory exists before compiling anything DUMMY != mkdir -p $(ALL_DIRS) @@ -766,7 +768,7 @@ $(BUILD_DIR)/rsp/%.bin $(BUILD_DIR)/rsp/%_data.bin: rsp/%.s # Run linker script through the C preprocessor $(BUILD_DIR)/$(LD_SCRIPT): $(LD_SCRIPT) $(BUILD_DIR)/goddard.txt $(call print,Preprocessing linker script:,$<,$@) - $(V)$(CPP) $(CPPFLAGS) -DBUILD_DIR=$(BUILD_DIR) -MMD -MP -MT $@ -MF $@.d -o $@ $< + $(V)$(CPP) $(CPPFLAGS) -DBUILD_DIR=$(BUILD_DIR) $(DEBUG_MAP_STACKTRACE_FLAG) -MMD -MP -MT $@ -MF $@.d -o $@ $< # Link libgoddard $(BUILD_DIR)/libgoddard.a: $(GODDARD_O_FILES) @@ -791,10 +793,15 @@ $(BUILD_DIR)/goddard.txt: $(BUILD_DIR)/sm64_prelim.elf $(call print,Getting Goddard size...) $(V)python3 tools/getGoddardSize.py $(BUILD_DIR)/sm64_prelim.map $(VERSION) +$(BUILD_DIR)/asm/debug/map.o: asm/debug/map.s $(BUILD_DIR)/sm64_prelim.elf + $(call print,Assembling:,$<,$@) + $(V)python3 tools/mapPacker.py $(BUILD_DIR)/sm64_prelim.map $(BUILD_DIR)/bin/addr.bin $(BUILD_DIR)/bin/name.bin + $(V)$(CROSS)gcc -c $(ASMFLAGS) $(foreach i,$(INCLUDE_DIRS),-Wa,-I$(i)) -x assembler-with-cpp -MMD -MF $(BUILD_DIR)/$*.d -o $@ $< + # Link SM64 ELF file -$(ELF): $(BUILD_DIR)/sm64_prelim.elf $(O_FILES) $(YAY0_OBJ_FILES) $(SEG_FILES) $(BUILD_DIR)/$(LD_SCRIPT) undefined_syms.txt $(BUILD_DIR)/libz.a $(BUILD_DIR)/libgoddard.a +$(ELF): $(BUILD_DIR)/sm64_prelim.elf $(BUILD_DIR)/asm/debug/map.o $(O_FILES) $(YAY0_OBJ_FILES) $(SEG_FILES) $(BUILD_DIR)/$(LD_SCRIPT) undefined_syms.txt $(BUILD_DIR)/libz.a $(BUILD_DIR)/libgoddard.a @$(PRINT) "$(GREEN)Linking ELF file: $(BLUE)$@ $(NO_COL)\n" - $(V)$(LD) --gc-sections -L $(BUILD_DIR) -T undefined_syms.txt -T $(BUILD_DIR)/$(LD_SCRIPT) -T goddard.txt -Map $(BUILD_DIR)/sm64.$(VERSION).map --no-check-sections $(addprefix -R ,$(SEG_FILES)) -o $@ $(O_FILES) -L$(LIBS_DIR) -l$(ULTRALIB) -Llib $(LINK_LIBRARIES) -u sprintf -u osMapTLB -Llib/gcclib/$(LIBGCCDIR) -lgcc -lnustd -lhvqm2 + $(V)$(LD) --gc-sections -L $(BUILD_DIR) -T undefined_syms.txt -T $(BUILD_DIR)/$(LD_SCRIPT) -T goddard.txt -Map $(BUILD_DIR)/sm64.$(VERSION).map --no-check-sections $(addprefix -R ,$(SEG_FILES)) -o $@ $(O_FILES) -L$(LIBS_DIR) -l$(ULTRALIB) -Llib $(LINK_LIBRARIES) -u sprintf -u osMapTLB -Llib/gcclib/$(LIBGCCDIR) -lgcc # Build ROM $(ROM): $(ELF) diff --git a/asm/debug/map.s b/asm/debug/map.s new file mode 100644 index 00000000..fc881942 --- /dev/null +++ b/asm/debug/map.s @@ -0,0 +1,17 @@ +.include "macros.inc" +.section .data +.balign 16 +glabel gMapEntries +.incbin "bin/addr.bin" +glabel gMapEntryEnd + +.balign 16 +glabel gMapStrings +.incbin "bin/name.bin" +glabel gMapStringsEnd + +.balign 16 +glabel gMapEntrySize +.word (gMapEntryEnd - gMapEntries) / 4 +glabel gMapStringSize +.word (gMapStringsEnd - gMapStrings) diff --git a/sm64.ld b/sm64.ld index 58d28918..16b79416 100755 --- a/sm64.ld +++ b/sm64.ld @@ -144,12 +144,23 @@ SECTIONS . = _hvqbufSegmentBssEnd; #endif +/* hardcoded symbols to satisfy preliminary link for map parser */ +#ifndef DEBUG_MAP_STACKTRACE + parse_map = 0x80345678; + find_function_in_stack = 0x80345678; + _mapDataSegmentRomStart = 0; + gMapEntries = 0; + gMapEntrySize = 0; + gMapStrings = 0; +#endif + BEGIN_SEG(main, .) SUBALIGN(16) { KEEP(BUILD_DIR/asm/entry.o(.text)); BUILD_DIR/src/boot*.o(.text); BUILD_DIR/src/hvqm*.o(.text); + BUILD_DIR/src/usb*.o(.text); BUILD_DIR/src/audio*.o(.text); #ifdef S2DEX_TEXT_ENGINE lib/libs2d_engine.a:*(.text); @@ -165,9 +176,11 @@ SECTIONS */libhvqm2.a:*.o(.text); BUILD_DIR/lib/rsp.o(.text); lib/PR/hvqm/hvqm2sp1.o(.text); + _mainSegmentTextEnd = .; /* data */ BUILD_DIR/src/boot*.o(.*data*); + BUILD_DIR/src/usb*.o(.*data*); BUILD_DIR/src/audio*.o(.*data*); #ifdef S2DEX_TEXT_ENGINE lib/libs2d_engine.a:*(.*data*); @@ -185,6 +198,7 @@ SECTIONS /* rodata */ BUILD_DIR/src/boot*.o(.rodata*); + BUILD_DIR/src/usb*.o(.rodata*); BUILD_DIR/src/audio*.o(.rodata*); #ifdef S2DEX_TEXT_ENGINE lib/libs2d_engine.a:*(.rodata*); @@ -201,13 +215,11 @@ SECTIONS lib/PR/hvqm/hvqm2sp1.o(.rodata*); } END_SEG(main) - #ifndef PRELIMINARY - ASSERT((_mainSegmentRomEnd <= 0x101000), "Error: Please shrink your main segment to under 1MB.") - #endif BEGIN_NOLOAD(main) { BUILD_DIR/src/boot*.o(.*bss*); BUILD_DIR/src/hvqm*.o(.*bss*); + BUILD_DIR/src/usb*.o(.*bss*); BUILD_DIR/src/audio*.o(.*bss*); #ifdef S2DEX_TEXT_ENGINE lib/libs2d_engine.a:*(.*bss*); @@ -233,23 +245,20 @@ SECTIONS { BUILD_DIR/src/game*.o(.text); BUILD_DIR/src/engine*.o(.text); - BUILD_DIR/src/usb*.o(.text); + _engineSegmentTextEnd = .; /* data */ BUILD_DIR/src/game*.o(.*data*); BUILD_DIR/src/engine*.o(.data*); BUILD_DIR/src/engine*.o(.sdata*); - BUILD_DIR/src/usb*.o(.*data*); /* rodata */ BUILD_DIR/src/game*.o(.rodata*); BUILD_DIR/src/engine*.o(.rodata*); - BUILD_DIR/src/usb*.o(.rodata*); } END_SEG(engine) BEGIN_NOLOAD(engine) { BUILD_DIR/src/game*.o(.*bss*); BUILD_DIR/src/engine*.o(.bss*); - BUILD_DIR/src/usb*.o(.*bss*); . = ALIGN(0x40); } END_NOLOAD(engine) @@ -330,6 +339,7 @@ SECTIONS BEGIN_SEG(goddard, RAM_END - GODDARD_SIZE) { KEEP(BUILD_DIR/src/menu*.o(.text)); + _goddardSegmentTextEnd = .; KEEP(BUILD_DIR/src/menu*.o(.data*)); KEEP(BUILD_DIR/src/menu*.o(.rodata*)); #ifdef KEEP_MARIO_HEAD @@ -497,6 +507,13 @@ SECTIONS } END_SEG(capcom) #endif + +#ifdef DEBUG_MAP_STACKTRACE + BEGIN_SEG(mapData, 0x80700000) { + KEEP(BUILD_DIR/asm/debug/map.o(.data*)); + } + END_SEG(mapData) +#endif /* DWARF debug sections. Symbols in the DWARF debugging sections are relative to the beginning of the section so we begin them at 0. */ diff --git a/src/game/crash_screen.c b/src/game/crash_screen.c index 5fd4749b..63bd5a6c 100644 --- a/src/game/crash_screen.c +++ b/src/game/crash_screen.c @@ -6,6 +6,7 @@ #include "types.h" #include "puppyprint.h" #include "audio/external.h" +#include "farcall.h" #include "game_init.h" #include "main.h" @@ -14,8 +15,9 @@ #include "printf.h" enum crashPages { - PAGE_STACK, + PAGE_CONTEXT, PAGE_LOG, + PAGE_STACKTRACE, PAGE_COUNT }; @@ -65,6 +67,9 @@ char *gFpcsrDesc[6] = { extern u64 osClockRate; +extern far char *parse_map(u32); +extern far void map_data_init(void); +extern far char *find_function_in_stack(u32 *); struct { OSThread thread; @@ -165,7 +170,7 @@ void crash_screen_print_float_reg(s32 x, s32 y, s32 regNum, void *addr) { if ((exponent >= -0x7E && exponent <= 0x7F) || bits == 0) { crash_screen_print(x, y, "F%02d:%.3e", regNum, *(f32 *) addr); } else { - crash_screen_print(x, y, "F%02d:---------", regNum); + crash_screen_print(x, y, "F%02d:%08XD", regNum, *(u32 *) addr); } } @@ -184,7 +189,7 @@ void crash_screen_print_fpcsr(u32 fpcsr) { } } -void draw_crash_stack(OSThread *thread, s32 cause) +void draw_crash_context(OSThread *thread, s32 cause) { __OSThreadContext *tc = &thread->context; @@ -193,7 +198,14 @@ void draw_crash_stack(OSThread *thread, s32 cause) crash_screen_print(30, 35, "PC:%08XH SR:%08XH VA:%08XH", tc->pc, tc->sr, tc->badvaddr); osWritebackDCacheAll(); crash_screen_draw_rect(25, 45, 270, 185); - crash_screen_print(30, 50, "AT:%08XH V0:%08XH V1:%08XH", (u32) tc->at, (u32) tc->v0, (u32) tc->v1); + if ((u32)parse_map == 0x80345678) { + crash_screen_print(30, 50, "AT:%08XH V0:%08XH V1:%08XH", (u32) tc->at, (u32) tc->v0, + (u32) tc->v1); + } else { + char *fname = parse_map(tc->pc); + crash_screen_print(30, 50, "CRASH AT: %s", fname == NULL ? "UNKNOWN" : fname); + } + // crash_screen_print(30, 50, "AT:%08XH V0:%08XH V1:%08XH", (u32) tc->at, (u32) tc->v0, (u32) tc->v1); crash_screen_print(30, 60, "A0:%08XH A1:%08XH A2:%08XH", (u32) tc->a0, (u32) tc->a1, (u32) tc->a2); crash_screen_print(30, 70, "A3:%08XH T0:%08XH T1:%08XH", (u32) tc->a3, (u32) tc->t0, (u32) tc->t1); crash_screen_print(30, 80, "T2:%08XH T3:%08XH T4:%08XH", (u32) tc->t2, (u32) tc->t3, (u32) tc->t4); @@ -241,6 +253,46 @@ void draw_crash_log(OSThread *thread, s32 cause) } +// prints any function pointers it finds in the stack +// format: +// SP address: function name +void draw_stacktrace(OSThread *thread, s32 cause) { + __OSThreadContext *tc = &thread->context; + u32 temp_sp = tc->sp + 0x14; + + crash_screen_draw_rect(25, 20, 270, 25); + crash_screen_print(30, 25, "STACK TRACE FROM %08X:", temp_sp); + if ((u32) parse_map == 0x80345678) { + crash_screen_print(30, 35, "CURRFUNC: NONE"); + } else { + crash_screen_print(30, 35, "CURRFUNC: %s", parse_map(tc->pc)); + } + + osWritebackDCacheAll(); + + for (int i = 0; i < 18; i++) { + if ((u32) find_function_in_stack == 0x80345678) { + crash_screen_print(30, 45 + (i * 10), "STACK TRACE DISABLED"); + break; + } else { + if ((u32) find_function_in_stack == 0x80345678) { + return; + } + + char *fname = find_function_in_stack(&temp_sp); + if (fname == NULL || (*(u32*)temp_sp & 0x80000000 == 0)) { + crash_screen_print(30, 45 + (i * 10), "%08X: UNKNOWN", temp_sp); + } else { + crash_screen_print(30, 45 + (i * 10), "%08X: %s", temp_sp, fname); + } + } + } + + +} + + + void draw_crash_screen(OSThread *thread) { s32 cause; @@ -282,8 +334,9 @@ void draw_crash_screen(OSThread *thread) crash_screen_print(15, 10, "Page:%d L/Z: Left R: Right", crashPage); switch (crashPage) { - case PAGE_STACK: draw_crash_stack(thread, cause); break; - case PAGE_LOG: draw_crash_log(thread, cause); break; + case PAGE_CONTEXT: draw_crash_context(thread, cause); break; + case PAGE_LOG: draw_crash_log(thread, cause); break; + case PAGE_STACKTRACE: draw_stacktrace(thread, cause); break; } osWritebackDCacheAll(); @@ -321,6 +374,9 @@ void thread2_crash_screen(UNUSED void *arg) { osSetEventMesg(OS_EVENT_FAULT, &gCrashScreen.mesgQueue, (OSMesg) 2); goto finished; reset: + if ((u32) map_data_init != 0x80345678) { + map_data_init(); + } gCrashScreen.thread.priority = 15; stop_sounds_in_continuous_banks(); stop_background_music(sBackgroundMusicQueue[0].seqId); diff --git a/src/game/map_parser.c b/src/game/map_parser.c new file mode 100644 index 00000000..c52df074 --- /dev/null +++ b/src/game/map_parser.c @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include "segments.h" + +#define STACK_TRAVERSAL_LIMIT 100 + +struct MapEntry { + u32 addr; + u32 nm_offset; + u32 nm_len; + u32 pad; +}; +extern u8 gMapStrings[]; +extern struct MapEntry gMapEntries[]; +extern u32 gMapEntrySize; +extern u8 _mapDataSegmentRomStart[]; + + +// code provided by Wiseguy +static s32 headless_dma(u32 devAddr, void *dramAddr, u32 size) +{ + register u32 stat; + stat = IO_READ(PI_STATUS_REG); + while (stat & (PI_STATUS_IO_BUSY | PI_STATUS_DMA_BUSY)) { + stat = IO_READ(PI_STATUS_REG); + } + IO_WRITE(PI_DRAM_ADDR_REG, K0_TO_PHYS(dramAddr)); + IO_WRITE(PI_CART_ADDR_REG, K1_TO_PHYS((u32)osRomBase | devAddr)); + IO_WRITE(PI_WR_LEN_REG, size - 1); + return 0; +} +static u32 headless_pi_status(void) +{ + return IO_READ(PI_STATUS_REG); +} +// end of code provided by Wiseguy + + +void map_data_init(void) { + headless_dma(_mapDataSegmentRomStart, 0x80700000, 0x100000); + while (headless_pi_status() & (PI_STATUS_DMA_BUSY | PI_STATUS_ERROR)); +} + +char *parse_map(u32 pc) { + u32 i; + + for (i = 0; i < gMapEntrySize; i++) { + if (gMapEntries[i].addr >= pc) break; + } + + if (i == gMapEntrySize - 1) { + return NULL; + } else { + return (char*) ((u32)gMapStrings + gMapEntries[i - 1].nm_offset); + } +} + +extern u8 _mainSegmentStart[]; +extern u8 _mainSegmentTextEnd[]; +extern u8 _engineSegmentStart[]; +extern u8 _engineSegmentTextEnd[]; +extern u8 _goddardSegmentStart[]; +extern u8 _goddardSegmentTextEnd[]; + +char *find_function_in_stack(u32 *sp) { + for (int i = 0; i < STACK_TRAVERSAL_LIMIT; i++) { + u32 val = *sp; + val = *(u32 *)val; + *sp = *sp + 4; + + if ((val >= (u32)_mainSegmentStart) && (val <= (u32)_mainSegmentTextEnd)) { + return parse_map(val); + } + else if ((val >= (u32)_engineSegmentStart) && (val <= (u32)_engineSegmentTextEnd)) { + return parse_map(val); + } + else if ((val >= (u32)_goddardSegmentStart) && (val <= (u32)_goddardSegmentTextEnd)) { + return parse_map(val); + } + + + } + return NULL; +} + + diff --git a/tools/mapPacker.py b/tools/mapPacker.py new file mode 100644 index 00000000..d9954522 --- /dev/null +++ b/tools/mapPacker.py @@ -0,0 +1,42 @@ +import sys, struct + +class MapEntry(): + def __init__(self, nm, addr): + self.name = nm + self.addr = addr + self.strlen = (len(nm) + 4) & (~3) + def __str__(self): + return "%s %s %d" % (self.addr, self.name, self.strlen) + def __repr__(self): + return "%s %s %d" % (self.addr, self.name, self.strlen) + + +structDef = ">LLLL" + +symNames = [] + +with open(sys.argv[1]) as f: + for line in f: + if "0x000000008" in line and "=" not in line and "." not in line and "*" not in line and "load address" not in line: + tokens = line.split() + symNames.append(MapEntry(tokens[1], int(tokens[0], 16))) + + + +f1 = open(sys.argv[2], "wb+") +f2 = open(sys.argv[3], "wb+") + +symNames.sort(key=lambda x: x.addr) + +off = 0 +for x in symNames: + f1.write(struct.pack(structDef, x.addr, off, len(x.name), 0)) + f2.write(struct.pack(">%ds" % x.strlen, bytes(x.name, encoding="ascii"))) + off += x.strlen + + +f1.close() +f2.close() + +# print('\n'.join([str(hex(x.addr)) + " " + x.name for x in symNames])) +