Hi GCC folks,

We were working on a decompilation project for a Nintendo 64 title and 
attempting to enable support for using GCC instead of the emulated IRIX 
compiler and we ran into a big problem with how GCC generates relocations for 
the MIPS target which appears to show that GCC is generating non-compliant 
relocation data for MIPS target.

In summary: the Nintendo 64 title has a limited amount of RAM (4MB, 8MB if you 
add Expansion Pak, which our ROM target uses for debug reasons); in order to 
accomplish this, the codebase packs actors/objects into overlays which the game 
determines need to be loaded per room/system transition. Once loaded into RAM, 
the game applies the overlay's relocations generated at compile time to the 
code to move the data and code correctly and make sure the jumps and loads get 
recalculated correctly.

Unfortunately.. there's a problem. Here's the function that applies the relocs 
to MIPS: https://github.com/zeldaret/oot/blob/master/src/code/relocation.c

While enabling overlays to be recompiled with GCC instead of the IDO compiler, 
we have found the relocs generated did not guarantee 0x45/0x46 (Hi/lo pairs) 
pairs to be 1:1, and GCC would share any possible hi/lo in O2 mode. While O0 
and O1 gcc overlays will work, or even Og, this is not a solution for an N64 
ROM due to limited RAM and space will quickly run out since memory is so tight. 
While investigating why gcc will optimize relocs, we have found the following:

The MIPS ABI specified at https://refspecs.linuxfoundation.org/elf/mipsabi.pdf 
on pages 79-80 (page 82 regarding the GP caveat) demands that hi/los be in 
pairs). Thus, we have found that the reloc data generated erroneously applies 
the relocation twice. Several LOs following a HI seems to be in a spec, but 
several HIs following a LO does not. This is causing issues for our relocation 
due to the relocs being applied incorrectly as a result of non-compliant 
relocation data. It turned out this reloc optimization is caused by an 
unmentioned, undocumented GNU extension.

We have found the GNU extension was ONLY ever mentioned here: 
https://people.freebsd.org/~adrian/mips/20160819-mips-elf-reloc-gcc-5.3-3.diff

Here is the file we compiled: 
https://github.com/zeldaret/oot/blob/master/src/overlays/actors/ovl_En_Heishi4/z_en_heishi4.c

This is the line we used to compile it:

mips-linux-gnu-gcc -c -O2 -c -G 0 -nostdinc -Iinclude -Isrc -Iassets -Ibuild 
-I. -DNON_MATCHING=1 -DNON_EQUIVALENT=1 -DAVOID_UB=1 -mno-shared -march=vr4300 
-mfix4300 -mabi=32 -mhard-float -mdivide-breaks -fno-stack-protector 
-fno-common -fno-zero-initialized-in-bss -mno-abicalls -fno-strict-aliasing 
-fno-inline-functions -fno-inline-small-functions -fno-toplevel-reorder 
-ffreestanding -fwrapv -Wall -Wextra -g -fno-gcse -fno-cse-follow-jumps 
-mno-load-store-pairs -mno-explicit-relocs -fno-peephole2 -mips3 -o 
build/src/overlays/actors/ovl_En_Heishi4/z_en_heishi4.o 
src/overlays/actors/ovl_En_Heishi4/z_en_heishi4.c

To note, we have tried with and without explicit relocs and with and without 
peephole2 and with and without mips2/3 and it didnt make a difference: the 
relocs were still noncompliant per the PDF posted earlier. We need a way to 
turn this undocumented GNU extension off because it is causing relocs when 
optimized to not be processed correctly. To note, our use case is attempting to 
compile this repo with GCC (this file is a test case) but if you were to 
compile the ROM with the Heishi4 file being compiled as GCC using the above 
call (make any Makefile alterations to force the object to be GCC), spawn on 
the SPOT00 map at the start of the game and go inside the castle town area and 
observe the crash which takes like 60 seconds. This is ultimately what we're 
trying to fix which following this rabbit hole leads us to this GNU extension 
in a haystack hunt. Can you guys help us resolve this?

v/r,
Revo

Reply via email to