diff --git a/Makefile b/Makefile index 86285c9659..57f6126c71 100644 --- a/Makefile +++ b/Makefile @@ -8,14 +8,32 @@ COMPARE ?= 1 NON_MATCHING ?= 0 # If ORIG_COMPILER is 1, compile with QEMU_IRIX and the original compiler ORIG_COMPILER ?= 0 +# If COMPILER is "gcc", compile with GCC instead of IDO. +COMPILER ?= ido + +CFLAGS ?= +CPPFLAGS ?= + +# ORIG_COMPILER cannot be combined with a non-IDO compiler. Check for this case and error out if found. +ifneq ($(COMPILER),ido) + ifeq ($(ORIG_COMPILER),1) + $(error ORIG_COMPILER can only be used with the IDO compiler. Please check your Makefile variables and try again) + endif +endif + +ifeq ($(COMPILER),gcc) + CFLAGS += -DCOMPILER_GCC + CPPFLAGS += -DCOMPILER_GCC + NON_MATCHING := 1 +endif # Set prefix to mips binutils binaries (mips-linux-gnu-ld => 'mips-linux-gnu-') - Change at your own risk! # In nearly all cases, not having 'mips-linux-gnu-*' binaries on the PATH is indicative of missing dependencies MIPS_BINUTILS_PREFIX ?= mips-linux-gnu- ifeq ($(NON_MATCHING),1) - CFLAGS := -DNON_MATCHING - CPPFLAGS := -DNON_MATCHING + CFLAGS += -DNON_MATCHING + CPPFLAGS += -DNON_MATCHING COMPARE := 0 endif @@ -45,8 +63,17 @@ ifneq ($(shell type $(MIPS_BINUTILS_PREFIX)ld >/dev/null 2>/dev/null; echo $$?), $(error Please install or build $(MIPS_BINUTILS_PREFIX)) endif -CC := tools/ido_recomp/$(DETECTED_OS)/7.1/cc -CC_OLD := tools/ido_recomp/$(DETECTED_OS)/5.3/cc +# Detect compiler and set variables appropriately. +ifeq ($(COMPILER),gcc) + CC := $(MIPS_BINUTILS_PREFIX)gcc +else +ifeq ($(COMPILER),ido) + CC := tools/ido_recomp/$(DETECTED_OS)/7.1/cc + CC_OLD := tools/ido_recomp/$(DETECTED_OS)/5.3/cc +else +$(error Unsupported compiler. Please use either ido or gcc as the COMPILER variable.) +endif +endif # if ORIG_COMPILER is 1, check that either QEMU_IRIX is set or qemu-irix package installed ifeq ($(ORIG_COMPILER),1) @@ -71,7 +98,6 @@ INC := -Iinclude -Isrc -Iassets -Ibuild -I. # Check code syntax with host compiler CHECK_WARNINGS := -Wall -Wextra -Wno-format-security -Wno-unknown-pragmas -Wno-unused-parameter -Wno-unused-variable -Wno-missing-braces -Wno-int-conversion -CC_CHECK := gcc -fno-builtin -fsyntax-only -fsigned-char -std=gnu90 -D _LANGUAGE_C -D NON_MATCHING $(INC) $(CHECK_WARNINGS) CPP := cpp MKLDSCRIPT := tools/mkldscript @@ -80,19 +106,34 @@ ELF2ROM := tools/elf2rom ZAPD := tools/ZAPD/ZAPD.out FADO := tools/fado/fado.elf -OPTFLAGS := -O2 -ASFLAGS := -march=vr4300 -32 -Iinclude -MIPS_VERSION := -mips2 - -# we support Microsoft extensions such as anonymous structs, which the compiler does support but warns for their usage. Surpress the warnings with -woff. -CFLAGS += -G 0 -non_shared -Xfullwarn -Xcpluscomm $(INC) -Wab,-r4300_mul -woff 649,838,712 - -ifeq ($(shell getconf LONG_BIT), 32) - # Work around memory allocation bug in QEMU - export QEMU_GUEST_BASE := 1 +ifeq ($(COMPILER),gcc) + OPTFLAGS := -Os -ffast-math -fno-unsafe-math-optimizations else - # Ensure that gcc treats the code as 32-bit - CC_CHECK += -m32 + OPTFLAGS := -O2 +endif + +ASFLAGS := -march=vr4300 -32 -Iinclude + +ifeq ($(COMPILER),gcc) + CFLAGS += -G 0 -nostdinc $(INC) -DAVOID_UB -march=vr4300 -mfix4300 -mabi=32 -mno-abicalls -mdivide-breaks -fno-zero-initialized-in-bss -fno-toplevel-reorder -ffreestanding -fno-common -fno-merge-constants -mno-explicit-relocs -mno-split-addresses $(CHECK_WARNINGS) -funsigned-char + MIPS_VERSION := -mips3 +else + # we support Microsoft extensions such as anonymous structs, which the compiler does support but warns for their usage. Surpress the warnings with -woff. + CFLAGS += -G 0 -non_shared -Xfullwarn -Xcpluscomm $(INC) -Wab,-r4300_mul -woff 649,838,712 + MIPS_VERSION := -mips2 +endif + +ifeq ($(COMPILER),ido) + CC_CHECK = gcc -fno-builtin -fsyntax-only -funsigned-char -std=gnu90 -D_LANGUAGE_C -DNON_MATCHING $(INC) $(CHECK_WARNINGS) + ifeq ($(shell getconf LONG_BIT), 32) + # Work around memory allocation bug in QEMU + export QEMU_GUEST_BASE := 1 + else + # Ensure that gcc (warning check) treats the code as 32-bit + CC_CHECK += -m32 + endif +else + CC_CHECK = @: endif #### Files #### @@ -103,7 +144,12 @@ ELF := $(ROM:.z64=.elf) # description of ROM segments SPEC := spec +ifeq ($(COMPILER),ido) +SRC_DIRS := $(shell find src -type d -not -path src/gcc_fix) +else SRC_DIRS := $(shell find src -type d) +endif + ASM_DIRS := $(shell find asm -type d -not -path "asm/non_matchings*") $(shell find data -type d) ASSET_BIN_DIRS := $(shell find assets/* -type d -not -path "assets/xml*" -not -path "assets/text") ASSET_FILES_XML := $(foreach dir,$(ASSET_BIN_DIRS),$(wildcard $(dir)/*.xml)) @@ -134,6 +180,7 @@ TEXTURE_FILES_OUT := $(foreach f,$(TEXTURE_FILES_PNG:.png=.inc.c),build/$f) \ # create build directories $(shell mkdir -p build/baserom build/assets/text $(foreach dir,$(SRC_DIRS) $(ASM_DIRS) $(ASSET_BIN_DIRS),build/$(dir))) +ifeq ($(COMPILER),ido) build/src/code/fault.o: CFLAGS += -trapuv build/src/code/fault.o: OPTFLAGS := -O2 -g3 build/src/code/fault_drawer.o: CFLAGS += -trapuv @@ -172,6 +219,10 @@ build/src/code/%.o: CC := python3 tools/asm_processor/build.py $(CC) -- $(AS) $( build/src/overlays/%.o: CC := python3 tools/asm_processor/build.py $(CC) -- $(AS) $(ASFLAGS) -- build/assets/%.o: CC := python3 tools/asm_processor/build.py $(CC) -- $(AS) $(ASFLAGS) -- +else +build/src/libultra/libc/ll.o: OPTFLAGS := -Ofast +build/src/%.o: CC := $(CC) -fexec-charset=euc-jp +endif #### Main Targets ### diff --git a/README.md b/README.md index 3b538cf0c8..2ab4733861 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,10 @@ To install the Python dependencies simply run in a terminal: python3 -m pip install colorama ``` +If you are using GCC as the compiler for Ocarina of Time, you will also need: + +* gcc-mips-linux-gnu + #### 2. Clone the repository Clone `https://github.com/zeldaret/oot.git` where you wish to have the project, with a command such as: diff --git a/include/functions.h b/include/functions.h index cd5f12d320..d604da5e78 100644 --- a/include/functions.h +++ b/include/functions.h @@ -4,7 +4,15 @@ #include "z64.h" f32 fabsf(f32 f); +#ifndef __sgi +#define fabsf __builtin_fabsf +f32 __floatundisf(u32 c); +f64 __floatundidf(u32 c); +f32 __powisf2(f32 a, s32 b); +unsigned long __udivdi3(unsigned long a, unsigned long b); +#else #pragma intrinsic(fabsf) +#endif f32 sqrtf(f32 f); #pragma intrinsic(sqrtf) f64 sqrt(f64 d); diff --git a/spec b/spec index 6d144a5bd6..6a6938d17a 100644 --- a/spec +++ b/spec @@ -109,6 +109,9 @@ beginseg include "build/src/libultra/os/gethwintrroutine.o" include "build/asm/__osSetWatchLo.o" include "build/data/rsp_boot.text.o" +#ifdef COMPILER_GCC + include "build/src/gcc_fix/missing_gcc_functions.o" +#endif endseg beginseg diff --git a/src/boot/z_std_dma.c b/src/boot/z_std_dma.c index 6a9d546ce8..7c9edae9b4 100644 --- a/src/boot/z_std_dma.c +++ b/src/boot/z_std_dma.c @@ -210,6 +210,9 @@ const char* DmaMgr_GetFileNameImpl(u32 vrom) { } //! @bug Since there is no return, in case the file isn't found, the return value will be a pointer to the end // of gDmaDataTable +#ifdef AVOID_UB + return ""; +#endif } const char* DmaMgr_GetFileName(u32 vrom) { diff --git a/src/code/audio_heap.c b/src/code/audio_heap.c index b81a5fc761..4ba1cff41a 100644 --- a/src/code/audio_heap.c +++ b/src/code/audio_heap.c @@ -1019,9 +1019,12 @@ void* AudioHeap_AllocPermanent(s32 tableType, s32 id, u32 size) { gAudioContext.permanentCache[index].tableType = tableType; gAudioContext.permanentCache[index].id = id; gAudioContext.permanentCache[index].size = size; + //! @bug UB: missing return. "ret" is in v0 at this point, but doing an // explicit return uses an additional register. - // return ret; +#ifdef AVOID_UB + return ret; +#endif } void* AudioHeap_AllocSampleCache(u32 size, s32 fontId, void* sampleAddr, s8 medium, s32 cache) { diff --git a/src/code/z_player_lib.c b/src/code/z_player_lib.c index 3571f01824..cdf4c0abe7 100644 --- a/src/code/z_player_lib.c +++ b/src/code/z_player_lib.c @@ -655,6 +655,7 @@ u8 sEyeMouthIndexes[][2] = { * shiftability, and changes will need to be made in the code to account for this in a modding scenario. The symbols * from adult Link's object are used here. */ +#ifndef AVOID_UB void* sEyeTextures[] = { gLinkAdultEyesOpenTex, gLinkAdultEyesHalfTex, gLinkAdultEyesClosedfTex, gLinkAdultEyesRollLeftTex, gLinkAdultEyesRollRightTex, gLinkAdultEyesShockTex, gLinkAdultEyesUnk1Tex, gLinkAdultEyesUnk2Tex, @@ -666,6 +667,20 @@ void* sMouthTextures[] = { gLinkAdultMouth3Tex, gLinkAdultMouth4Tex, }; +#else +// Defining `AVOID_UB` will use a 2D array instead and properly use the child link pointers to allow for shifting. +void* sEyeTextures[][8] = { + { gLinkAdultEyesOpenTex, gLinkAdultEyesHalfTex, gLinkAdultEyesClosedfTex, gLinkAdultEyesRollLeftTex, + gLinkAdultEyesRollRightTex, gLinkAdultEyesShockTex, gLinkAdultEyesUnk1Tex, gLinkAdultEyesUnk2Tex }, + { gLinkChildEyesOpenTex, gLinkChildEyesHalfTex, gLinkChildEyesClosedfTex, gLinkChildEyesRollLeftTex, + gLinkChildEyesRollRightTex, gLinkChildEyesShockTex, gLinkChildEyesUnk1Tex, gLinkChildEyesUnk2Tex }, +}; + +void* sMouthTextures[][4] = { + { gLinkAdultMouth1Tex, gLinkAdultMouth2Tex, gLinkAdultMouth3Tex, gLinkAdultMouth4Tex }, + { gLinkChildMouth1Tex, gLinkChildMouth2Tex, gLinkChildMouth3Tex, gLinkChildMouth4Tex }, +}; +#endif Color_RGB8 sTunicColors[] = { { 30, 105, 27 }, @@ -696,13 +711,21 @@ void func_8008F470(GlobalContext* globalCtx, void** skeleton, Vec3s* jointTable, eyeIndex = sEyeMouthIndexes[face][0]; } +#ifndef AVOID_UB gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sEyeTextures[eyeIndex])); +#else + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sEyeTextures[gSaveContext.linkAge][eyeIndex])); +#endif if (mouthIndex < 0) { mouthIndex = sEyeMouthIndexes[face][1]; } +#ifndef AVOID_UB gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(sMouthTextures[mouthIndex])); +#else + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(sMouthTextures[gSaveContext.linkAge][mouthIndex])); +#endif color = &sTunicColors[tunic]; gDPSetEnvColor(POLY_OPA_DISP++, color->r, color->g, color->b, 0); diff --git a/src/gcc_fix/missing_gcc_functions.c b/src/gcc_fix/missing_gcc_functions.c new file mode 100644 index 0000000000..4c572a764b --- /dev/null +++ b/src/gcc_fix/missing_gcc_functions.c @@ -0,0 +1,211 @@ +/* --------------------------------------------------------------------------------*/ +/* Depending on the toolchain used, an appropriate precompiled libgcc library */ +/* may not exist and cannot be linked against. Until we have a better work around, */ +/* necessary gcc functions are hosted here in order to properly compile. */ +/* This file is NOT a part of the original game and only exists to help gcc work. */ +/* --------------------------------------------------------------------------------*/ + +#include "global.h" + +// Self-hosted memcmp. +int memcmp(void* s1, const void* s2, size_t n) { + u8* m1 = (u8*)s1; + u8* m2 = (u8*)s2; + u32 i; + + for (i = 0; i < n; i++) { + if (m1[i] < m2[i]) { + return -1; + } else if (m1[i] > m2[i]) { + return 1; + } + } + + return 0; +} + +void* memset(void* str, s32 c, size_t n) { + u8* m1 = (u8*)str; + u32 i; + + for (i = 0; i < n; i++) { + m1[i] = c; + } + + return str; +} + +// These functions convert c to an unsigned integer, rounding toward zero. Negative values +// all become zero. +u32 __fixunssfdi(f32 a) { + if (a < 0.0f) { + a = 0.0f; + } + + return (u32)a; +} + +u32 __fixunsdfdi(f64 a) { + if (a < 0.0) { + a = 0.0; + } + + return (u32)a; +} + +// These functions convert c to a signed integer, rounding toward zero. +s32 __fixsfdi(f32 c) { + return (s32)c; +} + +s32 __fixdfdi(f64 c) { + return (s32)c; +} + +// These functions convert c, a signed integer, to floating point. +f32 __floatdisf(s32 c) { + return (f32)c; +} + +f64 __floatdidf(s32 c) { + return (f64)c; +} + +// These functions convert c, an unsigned integer, to floating point. +f32 __floatundisf(u32 c) { + return (f32)c; +} + +f64 __floatundidf(u32 c) { + return (f64)c; +} + +f32 __powisf2(f32 a, s32 b) { + const s32 recip = b < 0; + f32 r = 1; + + while (1) { + if (b & 1) { + r *= a; + } + + b /= 2; + + if (b == 0) { + break; + } + + a *= a; + } + return recip ? 1 / r : r; +} + +__asm__(" \n\ + .set push \n\ + .set noat \n\ + .set noreorder \n\ + .set gp=64 \n\ + \n\ +.global __umoddi3 \n\ +__umoddi3: \n\ + .type __umoddi3, @function \n\ + .ent __umoddi3 \n\ + sw $a0, ($sp) \n\ + sw $a1, 4($sp) \n\ + sw $a2, 8($sp) \n\ + sw $a3, 0xc($sp) \n\ + ld $t7, 8($sp) \n\ + ld $t6, ($sp) \n\ + ddivu $zero, $t6, $t7 \n\ + bnez $t7, 1f \n\ + nop \n\ + break 7 \n\ +1: \n\ + mfhi $v0 \n\ + dsll32 $v1, $v0, 0 \n\ + dsra32 $v1, $v1, 0 \n\ + jr $ra \n\ + dsra32 $v0, $v0, 0 \n\ + .end __umoddi3 \n\ + .size __umoddi3, . - __umoddi3 \n\ + \n\ +.global __udivdi3 \n\ +__udivdi3: \n\ + .type __udivdi3, @function \n\ + .ent __udivdi3 \n\ + sw $a0, ($sp) \n\ + sw $a1, 4($sp) \n\ + sw $a2, 8($sp) \n\ + sw $a3, 0xc($sp) \n\ + ld $t7, 8($sp) \n\ + ld $t6, ($sp) \n\ + ddivu $zero, $t6, $t7 \n\ + bnez $t7, 1f \n\ + nop \n\ + break 7 \n\ +1: \n\ + mflo $v0 \n\ + dsll32 $v1, $v0, 0 \n\ + dsra32 $v1, $v1, 0 \n\ + jr $ra \n\ + dsra32 $v0, $v0, 0 \n\ + .end __udivdi3 \n\ + .size __udivdi3, . - __udivdi3 \n\ + \n\ +.global __moddi3 \n\ +__moddi3: \n\ + .type __moddi3, @function \n\ + .ent __moddi3 \n\ + sw $a0, ($sp) \n\ + sw $a1, 4($sp) \n\ + sw $a2, 8($sp) \n\ + sw $a3, 0xc($sp) \n\ + ld $t7, 8($sp) \n\ + ld $t6, ($sp) \n\ + ddivu $zero, $t6, $t7 \n\ + bnez $t7, 1f \n\ + nop \n\ + break 7 \n\ +1: \n\ + mfhi $v0 \n\ + dsll32 $v1, $v0, 0 \n\ + dsra32 $v1, $v1, 0 \n\ + jr $ra \n\ + dsra32 $v0, $v0, 0 \n\ + .end __moddi3 \n\ + .size __moddi3, . - __moddi3 \n\ + \n\ +.global __divdi3 \n\ +__divdi3: \n\ + .type __divdi3, @function \n\ + .ent __divdi3 \n\ + sw $a0, ($sp) \n\ + sw $a1, 4($sp) \n\ + sw $a2, 8($sp) \n\ + sw $a3, 0xc($sp) \n\ + ld $t7, 8($sp) \n\ + ld $t6, ($sp) \n\ + ddiv $zero, $t6, $t7 \n\ + nop \n\ + bnez $t7, 1f \n\ + nop \n\ + break 7 \n\ +1: \n\ + daddiu $at, $zero, -1 \n\ + bne $t7, $at, 2f \n\ + daddiu $at, $zero, 1 \n\ + dsll32 $at, $at, 0x1f \n\ + bne $t6, $at, 2f \n\ + nop \n\ + break 6 \n\ +2: \n\ + mflo $v0 \n\ + dsll32 $v1, $v0, 0 \n\ + dsra32 $v1, $v1, 0 \n\ + jr $ra \n\ + dsra32 $v0, $v0, 0 \n\ + .end __divdi3 \n\ + .size __divdi3, . - __divdi3 \n\ + \n\ + .set pop \n\ + \n"); diff --git a/src/overlays/actors/ovl_En_Tana/z_en_tana.c b/src/overlays/actors/ovl_En_Tana/z_en_tana.c index 6aa6488e29..81b21f44c2 100644 --- a/src/overlays/actors/ovl_En_Tana/z_en_tana.c +++ b/src/overlays/actors/ovl_En_Tana/z_en_tana.c @@ -33,6 +33,9 @@ const ActorInit En_Tana_InitVars = { static const char* sShelfTypes[] = { "木の棚", // "Wooden Shelves" "石の棚", // "Stone Shelves" +#ifdef AVOID_UB + "", +#endif }; static const ActorFunc sDrawFuncs[] = { diff --git a/src/overlays/actors/ovl_En_Zf/z_en_zf.c b/src/overlays/actors/ovl_En_Zf/z_en_zf.c index 2cd9dc280a..a511584f7c 100644 --- a/src/overlays/actors/ovl_En_Zf/z_en_zf.c +++ b/src/overlays/actors/ovl_En_Zf/z_en_zf.c @@ -452,9 +452,14 @@ s16 EnZf_FindNextPlatformAwayFromPlayer(Vec3f* pos, s16 curPlatform, s16 arg2, G } } - // These functions have no side effects, so these two calls do nothing + //! @bug `altNextPlatform` can be -1 in certain conditions and cause an out of bounds access. + //! Under normal conditions, this doesn't cause problems because the data before `sPlatformPositions` + //! is section padding between .text and .data, so 0 gets read as a float. + // These two function calls do nothing. Their return values aren't used and they have no side effects. +#ifndef AVOID_UB Math_Vec3f_DistXYZ(&player->actor.world.pos, &sPlatformPositions[nextPlatform]); Math_Vec3f_DistXYZ(&player->actor.world.pos, &sPlatformPositions[altNextPlatform]); +#endif if (altNextPlatform > 0) { s16 nextPlatformToPlayerYaw = diff --git a/tools/mkldscript.c b/tools/mkldscript.c index 2239e864e2..a2c992e3ae 100644 --- a/tools/mkldscript.c +++ b/tools/mkldscript.c @@ -9,6 +9,15 @@ #include "spec.h" #include "util.h" +// Note: *SECTION ALIGNMENT* Object files built with a compiler such as GCC can, by default, use narrower +// alignment for sections size, compared to IDO padding sections to a 0x10-aligned size. +// To properly generate relocations relative to section starts, sections currently need to be aligned +// explicitly (to 0x10 currently, a narrower alignment might work), otherwise the linker does implicit alignment +// and inserts padding between the address indicated by section start symbols (such as *SegmentRoDataStart) and +// the actual aligned start of the section. +// With IDO, the padding of sections to an aligned size makes the section start at aligned addresses out of the box, +// so the explicit alignment has no further effect. + struct Segment *g_segments; int g_segmentsCount; @@ -17,7 +26,8 @@ static void write_ld_script(FILE *fout) int i; int j; - fputs("SECTIONS {\n" + fputs("OUTPUT_ARCH (mips)\n\n" + "SECTIONS {\n" " _RomSize = 0;\n" " _RomStart = _RomSize;\n\n", fout); @@ -62,6 +72,7 @@ static void write_ld_script(FILE *fout) fprintf(fout, " %s (.text)\n", seg->includes[j].fpath); if (seg->includes[j].linkerPadding != 0) fprintf(fout, " . += 0x%X;\n", seg->includes[j].linkerPadding); + fprintf(fout, " . = ALIGN(0x10);\n"); } fprintf(fout, " _%sSegmentTextEnd = .;\n", seg->name); @@ -73,7 +84,8 @@ static void write_ld_script(FILE *fout) for (j = 0; j < seg->includesCount; j++) { if (!seg->includes[j].dataWithRodata) - fprintf(fout, " %s (.data)\n", seg->includes[j].fpath); + fprintf(fout, " %s (.data)\n" + " . = ALIGN(0x10);\n", seg->includes[j].fpath); } /* @@ -94,8 +106,11 @@ static void write_ld_script(FILE *fout) for (j = 0; j < seg->includesCount; j++) { if (seg->includes[j].dataWithRodata) - fprintf(fout, " %s (.data)\n", seg->includes[j].fpath); - fprintf(fout, " %s (.rodata)\n", seg->includes[j].fpath); + fprintf(fout, " %s (.data)\n" + " . = ALIGN(0x10);\n", seg->includes[j].fpath); + + fprintf(fout, " %s (.rodata)\n" + " . = ALIGN(0x10);\n", seg->includes[j].fpath); // Compilers other than IDO, such as GCC, produce different sections such as // the ones named directly below. These sections do not contain values that // need relocating, but we need to ensure that the base .rodata section @@ -104,13 +119,14 @@ static void write_ld_script(FILE *fout) // the beginning of the entire rodata area in order to remain consistent. // Inconsistencies will lead to various .rodata reloc crashes as a result of // either missing relocs or wrong relocs. - fprintf(fout, " %s (.rodata.str1.4)\n", seg->includes[j].fpath); - fprintf(fout, " %s (.rodata.cst4)\n", seg->includes[j].fpath); - fprintf(fout, " %s (.rodata.cst8)\n", seg->includes[j].fpath); + fprintf(fout, " %s (.rodata.str1.4)\n" + " . = ALIGN(0x10);\n", seg->includes[j].fpath); + fprintf(fout, " %s (.rodata.cst4)\n" + " . = ALIGN(0x10);\n", seg->includes[j].fpath); + fprintf(fout, " %s (.rodata.cst8)\n" + " . = ALIGN(0x10);\n", seg->includes[j].fpath); } - //fprintf(fout, " . = ALIGN(0x10);\n"); - fprintf(fout, " _%sSegmentRoDataEnd = .;\n", seg->name); fprintf(fout, " _%sSegmentRoDataSize = ABSOLUTE( _%sSegmentRoDataEnd - _%sSegmentRoDataStart );\n", seg->name, seg->name, seg->name); @@ -118,9 +134,8 @@ static void write_ld_script(FILE *fout) fprintf(fout, " _%sSegmentSDataStart = .;\n", seg->name); for (j = 0; j < seg->includesCount; j++) - fprintf(fout, " %s (.sdata)\n", seg->includes[j].fpath); - - fprintf(fout, " . = ALIGN(0x10);\n"); + fprintf(fout, " %s (.sdata)\n" + " . = ALIGN(0x10);\n", seg->includes[j].fpath); fprintf(fout, " _%sSegmentSDataEnd = .;\n", seg->name); @@ -129,23 +144,20 @@ static void write_ld_script(FILE *fout) for (j = 0; j < seg->includesCount; j++) fprintf(fout, " %s (.ovl)\n", seg->includes[j].fpath); - fprintf(fout, " . = ALIGN(0x10);\n"); - fprintf(fout, " _%sSegmentOvlEnd = .;\n", seg->name); if (seg->fields & (1 << STMT_increment)) fprintf(fout, " . += 0x%08X;\n", seg->increment); - fputs(" }\n", fout); - //fprintf(fout, " _RomSize += ( _%sSegmentDataEnd - _%sSegmentTextStart );\n", seg->name, seg->name); + fprintf(fout, " _RomSize += ( _%sSegmentOvlEnd - _%sSegmentTextStart );\n", seg->name, seg->name); fprintf(fout, " _%sSegmentRomEndTemp = _RomSize;\n" "_%sSegmentRomEnd = _%sSegmentRomEndTemp;\n\n", seg->name, seg->name, seg->name); - // algn end of ROM segment + // align end of ROM segment if (seg->fields & (1 << STMT_romalign)) fprintf(fout, " _RomSize = (_RomSize + %i) & ~ %i;\n", seg->romalign - 1, seg->romalign - 1); @@ -156,16 +168,26 @@ static void write_ld_script(FILE *fout) " . = ALIGN(0x10);\n" " _%sSegmentBssStart = .;\n", seg->name, seg->name, seg->name, seg->name); + if (seg->fields & (1 << STMT_align)) fprintf(fout, " . = ALIGN(0x%X);\n", seg->align); + for (j = 0; j < seg->includesCount; j++) - fprintf(fout, " %s (.sbss)\n", seg->includes[j].fpath); + fprintf(fout, " %s (.sbss)\n" + " . = ALIGN(0x10);\n", seg->includes[j].fpath); + for (j = 0; j < seg->includesCount; j++) - fprintf(fout, " %s (.scommon)\n", seg->includes[j].fpath); + fprintf(fout, " %s (.scommon)\n" + " . = ALIGN(0x10);\n", seg->includes[j].fpath); + for (j = 0; j < seg->includesCount; j++) - fprintf(fout, " %s (.bss)\n", seg->includes[j].fpath); + fprintf(fout, " %s (.bss)\n" + " . = ALIGN(0x10);\n", seg->includes[j].fpath); + for (j = 0; j < seg->includesCount; j++) - fprintf(fout, " %s (COMMON)\n", seg->includes[j].fpath); + fprintf(fout, " %s (COMMON)\n" + " . = ALIGN(0x10);\n", seg->includes[j].fpath); + fprintf(fout, " . = ALIGN(0x10);\n" " _%sSegmentBssEnd = .;\n" " _%sSegmentEnd = .;\n" @@ -193,13 +215,55 @@ static void write_ld_script(FILE *fout) //fprintf(fout, "\n }\n"); } + fputs(" _RomEnd = _RomSize;\n\n", fout); - fputs(" _RomEnd = _RomSize;\n}\n", fout); + // Debugging sections + fputs( + // mdebug debug sections + " .mdebug : { *(.mdebug) }" "\n" + " .mdebug.abi32 : { *(.mdebug.abi32) }" "\n" + // DWARF debug sections + // Symbols in the DWARF debugging sections are relative to the beginning of the section so we begin them at 0. + // DWARF 1 + " .debug 0 : { *(.debug) }" "\n" + " .line 0 : { *(.line) }" "\n" + // GNU DWARF 1 extensions + " .debug_srcinfo 0 : { *(.debug_srcinfo) }" "\n" + " .debug_sfnames 0 : { *(.debug_sfnames) }" "\n" + // DWARF 1.1 and DWARF 2 + " .debug_aranges 0 : { *(.debug_aranges) }" "\n" + " .debug_pubnames 0 : { *(.debug_pubnames) }" "\n" + // DWARF 2 + " .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }" "\n" + " .debug_abbrev 0 : { *(.debug_abbrev) }" "\n" + " .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end ) }" "\n" + " .debug_frame 0 : { *(.debug_frame) }" "\n" + " .debug_str 0 : { *(.debug_str) }" "\n" + " .debug_loc 0 : { *(.debug_loc) }" "\n" + " .debug_macinfo 0 : { *(.debug_macinfo) }" "\n" + // SGI/MIPS DWARF 2 extensions + " .debug_weaknames 0 : { *(.debug_weaknames) }" "\n" + " .debug_funcnames 0 : { *(.debug_funcnames) }" "\n" + " .debug_typenames 0 : { *(.debug_typenames) }" "\n" + " .debug_varnames 0 : { *(.debug_varnames) }" "\n" + // DWARF 3 + " .debug_pubtypes 0 : { *(.debug_pubtypes) }" "\n" + " .debug_ranges 0 : { *(.debug_ranges) }" "\n" + // DWARF Extension + " .debug_macro 0 : { *(.debug_macro) }" "\n" + " .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }" "\n", fout); + + // Discard all other sections not mentioned above + fputs(" /DISCARD/ :" "\n" + " {" "\n" + " *(*);" "\n" + " }" "\n", fout); + fputs("}\n", fout); } static void usage(const char *execname) { - fprintf(stderr, "Nintendo 64 linker script generation tool v0.02\n" + fprintf(stderr, "Nintendo 64 linker script generation tool v0.03\n" "usage: %s SPEC_FILE LD_SCRIPT\n" "SPEC_FILE file describing the organization of object files into segments\n" "LD_SCRIPT filename of output linker script\n",