Skip to content

Guide to Linking TUs

JelleJurre edited this page Mar 27, 2026 · 11 revisions

Example of linking a TU

Note that, like decompiling, this journey will be different for every TU, this is just an example of what I did for itsonans.c

Step 1: Make sure all functions match

You can see that every function on the left has (100%) next to it.

Step 2: Remove unused imports

Remove all unused imports as they can add data. You can do this by removing them one by one and seeing if the game fails to compile.

In this case, all imports are needed, but this isn't always the case, so it is good to make sure that is true.

Step 3: Map the fields

We want to map all the fields we need (left) to all the fields that we have (right) so that we know what is actually missing/wrong.

You can do so by clicking a field if it doesn't have a percentage next to it, and then clicking the field on the right with the highest percentage. (Note that if the target is in a different section, it won't map. This is okay)

In this case I am trying to map something from the sdata2 section, and I see a matching target in the sdata2 section with 100%, so I click it to make sure objdiff knows that they are the same.

Step 4: Move data to the right sections

For info on what goes into which section, you can find that here.

After mapping, I can see that I have two values in the sdata section that should be in the sdata2 section

From the wiki on data sections we can see that sdata2 is read only floats, so we should turn some data into constants.

In this case it is these two numbers in the header file (I found this out by putting the hex in this tool and seeing what the numeric values of these numbers were):

I had to inline them, so that they are no longer mutable:

Step 5: Fix remaining issues

The goal to get the numbers next to the left sections all 100%, and now they are 97%, 100%, 100%, 100%.

So we have to fix the data section, and remove the sbss section from our version.

To check where the last stragglers are missing, I set "Function relocation diffs" to "Name or Address", which shows me where the issues lie.

In this case the sbss area was created by another non-inlined float.

After removing this, the sbss area went away

The last issue was one variable in the data table not matching perfectly:

This was fixed by turning the anim_id from 2 into -1

For some other examples of issues I encountered while working on other files and how to fix them, check the Other Issues section.

Step 6: Linking and applying

Now every section has (100%) next to its name (the remaining % in the [section]-0 fields is due to missing trailing zero bytes at the end, this is due to alignment and can be ignored).

This means we can try linking it.

We do this by finding its entry in configure.py and setting the entry from NonMatching to Matching.

After running ninja, it if everything is correct it will show output like any usual build. If it isn't correct, you can find out why by running ninja diff.

Congratulations, you now have linked a TU!

Afterwards you can run ninja apply to let it auto-generate the symbol names (I do this in a separate commit).

Other Issues

Function ordering:

If your functions all have 100% next to them, but the .text section still isn't 100%, that might be a function ordering issue. Make sure your functions are in the right order.

Data section not yet carved:

If you have one section that you think contains multiple variables, you might still have to carve them out. One example of this is how I had to handle grFZeroCar:

In the screenshot below, you can see that it expects the block of data, and then two strings. These strings have to be their separate variables. I find the string in symbols.txt:

grFZeroCar_803E0BD8 = .data:0x803E0BD8; // type:object size:0x188 scope:global

On the screenshot this should be one block of data of size 0x168, one string of size 0xD, and one string of size 0x9, so I expand this line into three lines:

grFZeroCar_803E0BD8 = .data:0x803E0BD8; // type:object size:0x168 scope:global
grFZeroCar_803E0D40 = .data:0x803E0D40; // type:object size:0xD data:string
grFZeroCar_803E0D50 = .data:0x803E0D50; // type:object size:0x9 data:string

(Helpful tip for this, if you just make one section smaller and save your file, it automatically generates a new line adding the newly generated block onto it.)

After this change, the data section matches

Data ordering:

If it is the data section that is the issue, you have to change the declaration order of these variables in your file.

If it is the sdata2 section, it is the order in the file that the floats are used in. This can be fixed preferably with an inline, where you pull certain floats up higher in the file, or if that doesn't work, with a new function that assigns these floats to a struct (but is never called or used)

Clone this wiki locally