rom_patching.html
13.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Patching N64 ROMs for the BB Player</title>
<meta http-equiv="content-type"
content="text/html; charset=ISO-8859-1">
</head>
<body>
<h2>Patching N64 ROMs for the BB Player</h2>
November 2002<br>
<br>
This document is a collection of notes describing the issues and possible
solutions for patching existing N64 ROMs to work on the BB Player. Many
of these issues are related to the details of system compatibility as discussed
in the <a href="../hwimpact.html">porting issues</a> document. <br>
<br>
The main idea is that most differences in software compatibility are encapsulated
by the libultra library. The main areas of incompatibility from an
application software perspective are state saving, the manipulating the controller
and the rumble pack, and processor clock speed differences. Other incompatabilities
such as the use of flash memory as cartridge memory should be transparent
to game application once the hardware has been configured and the game application
launched. <br>
<br>
The idea behind trying to reuse existing ROMs is that it may be difficult
to gain access to source or even object files for all games, therefore it
is in our interest to try to use a modified version of the ROM image. If
the incompatibilities are encapuslated by libultra, then all that should
be required is to build a small library with replacement versions of the
libultra routines, insert that into some available space in the ROM image
such that it is loaded with the rest of the game and then patch references
from the game to the libultra routines to point to this new patch library.
This is probably a slight over-simplification of the problem.
We'll describe issues in more detail below:<br>
<br>
<h3>References from Patch libultra to ROM libultra</h3>
The small patch library itself may need to 'know' addresses of other routines
or shared data structures (e.g., to manipulate interrupts, etc), so there
is a step where these addresses are determined and inserted into the patch
library.<br>
<h3>Locating Call Sites in the ROM</h3>
To change the references to the original libultra to the new patch library,
every reference in the ROM for each function must be determined and replaced.
The best way to do this is to find the memory address of each function
entry to be patched and search for the references in the remainder of the
ROM code. Ideally, the game symbol table would give us this
information, but it is unlikely that we will have access to it. This
necessitates some reverse engineering of the ROM image to determine the addresses
of the function entries.<br>
<br>
One way to do this is by search the binary ROM image for an instruction sequence
pattern corresponding to each function. The patterns are determined
by extracting them from an <b>identical</b> libultra.a. This means
that we will need to know which release of libultra.a the game was linked
against and which development tools were used to produce the final image
so that we have the correct patterns. The search itself shouldn't be
very difficult since the instructions are fixed size and are 4-byte aligned.
The only special requirement is that bits holding addresses or displacements
need to be masked since the addresses/displacements will depend on the layout
of code in the ROM image. As an example here is the first part
of osEepromRead:<br>
<br>
<tt>0: 27bdffb0
addiu $sp,$sp,-80<br>
4: afb20038
sw $s2,56($sp)<br>
8: 00c09021
move $s2,$a2<br>
c: afb10034
sw $s1,52($sp)<br>
10: 3c110000
lui $s1,0x0<br>
10: R_MIPS_HI16 __osEepPifRam<br>
14: 26310000
addiu $s1,$s1,0<br>
14: R_MIPS_LO16 __osEepPifRam<br>
18: afb3003c
sw $s3,60($sp)<br>
1c: 00809821
move $s3,$a0<br>
20: afb40040
sw $s4,64($sp)<br>
24: 30b400ff
andi $s4,$a1,0xff<br>
28: afbf0048
sw $ra,72($sp)<br>
2c: afbc0044
sw $gp,68($sp)<br>
30: afb00030
sw $s0,48($sp)<br>
34: 3c190000
lui $t9,0x0<br>
34: R_MIPS_HI16 __osSiGetAccess<br>
38: 27390000
addiu $t9,$t9,0<br>
38: R_MIPS_LO16 __osSiGetAccess<br>
3c: 0320f809
jalr $t9<br>
40: 00000000
nop<br>
44: 02602021
move $a0,$s3<br>
48: 27a50018
addiu $a1,$sp,24<br>
4c: 3c190000
lui $t9,0x0<br>
4c: R_MIPS_HI16 __osEepStatus<br>
50: 27390000
addiu $t9,$t9,0<br>
50: R_MIPS_LO16 __osEepStatus<br>
54: 0320f809
jalr $t9<br>
58: 00000000
nop<br>
5c: 97a30018
lhu $v1,24($sp)<br>
60: 00408021
move $s0,$v0<br>
64: 1600005e
bnez $s0,1e0 <osEepromRead+0x1e0><br>
68: 3063c000
andi $v1,$v1,0xc000<br>
6c: 34028000
li $v0,0x8000<br>
70: 10620005
beq $v1,$v0,88 <osEepromRead+0x88><br>
74: 3402c000
li $v0,0xc000<br>
78: 10620008
beq $v1,$v0,9c <osEepromRead+0x9c><br>
7c: 24020001
li $v0,1<br>
80: 08000029
j a4 <osEepromRead+0xa4><br>
80: R_MIPS_26 .text<br>
84: 24100008
li $s0,8<br>
88: 2e820040
sltiu $v0,$s4,64<br>
8c: 50400005
beqzl $v0,a4 <osEepromRead+0xa4><br>
90: 2410ffff
li $s0,-1<br>
94: 08000029
j a4 <osEepromRead+0xa4><br>
94: R_MIPS_26 .text<br>
98: 00000000
nop<br>
9c: 3c010000
lui $at,0x0<br>
9c: R_MIPS_HI16 __osEepromRead16K<br>
a0: ac220000
sw $v0,0($at)</tt><br>
<br>
The binary encodings of the instruction are in the second column. Any
instruction which has associated relocation information (e.g., R_MIPS_xxxx),
needs to have an appropriate mask to avoid comparing the relocated information.<br>
<br>
So to do the pattern matching the relevant version of libultra needs to be
converted to a set of patterns. Once the patterns have been produced,
the addresses of the function entries are located (typically the address
of the first word in the pattern), then all jumps to each address need to
be located. The jumps will likely use either the jump absolute or jump
register instructions, so these need to be located and then analyzed to determine
if they are to a target address. The jal instructions should be simple
enough since the address is encoded in the instruction, the jalr instructions
are more complicated since the contents of the register needs to be determined
before the match can be done. Depending on the compiler used, the jump
idiom might be easy to analyze, e.g., for the version of gcc we are using
it looks like:<br>
<br>
<tt>lui $t9,addr_hi<br>
addiu $t9,$t9,addr_lo<br>
jalr $t9</tt><br>
<br>
For the most part we will assume that we are only looking for subroutine
jumps and that the application does not store pointers to functions in libultra.<br>
<h3>Placing The Patch Library</h3>
The patch library needs to be loaded into a predefined ram location.
However, we need to understand some basics concerning how memory is
used by the game in order to locate some free memory. In addition,
the game has to be modified to load the patch library code into memory. If
necesary, we could have the BB Browser app perform this step as part of game
loading. The real issue is locating a good place in memory to put the
code without knowing any details of the memory map.<br>
<h3>Multiple Code Segments and Compression</h3>
Many games use multiple code segments. Often the bootsegment contains
all of the libultra code used by the other segments, however; all of the
segments need to be modified to use the patch library. This requires
identifying all of the code segments in the ROM image and patching them.
The brute force way to do this is to search all of ROM for patterns
that look like MIPS instruction sequences. Another approach is to try
to snoop transfers from cartridge space and try to build a segment map, and
then look at individual segments. One additional complication is that
segments other than the boot segment are likely to be compressed with an
abitrary compression scheme and are only decompressed when they are loaded
to memory. This makes the task extremely complicated unless we have
a priori knowledge of the compression scheme. Again, trying to determine
the segment boundaries in advance by snooping cartridge accesses may help,
however once a compressed segment has been located it still may be difficult
to determine the compression scheme. So far we are aware of two schemes
from two different games. One tool that may help with this task is
instrumenting one of the existing N64 PC emulators to help dump information
about the game while it is executing.<br>
<h3>Summary of Issues</h3>
<ul>
<li>Obtaining correct version of libultra_rom.a for a given game - shouldn't
be very difficult - need OS2.0D to OS2.0L releases</li>
<li>Identifying all code segments in decompressed form for patching - could
be arbitrarily difficult</li>
<li>Find free space in memory map for patch code - for 4M games, easy;
for anything else need extra knowledge or snoop ram addresses</li>
<li>Hook to load patch code into ram - can make part of game launch if
necessary.<br>
</li>
</ul>
<br>
<br>
</body>
</html>