2 * Author: Humberto Naves (hsnaves@gmail.com)
14 int cmp_relocs (const void *p1
, const void *p2
)
16 const struct prx_reloc
*r1
= p1
;
17 const struct prx_reloc
*r2
= p2
;
18 if (r1
->target
< r2
->target
) return -1;
19 if (r1
->target
> r2
->target
) return 1;
24 int cmp_relocs_by_addr (const void *p1
, const void *p2
)
26 const struct prx_reloc
*r1
= p1
;
27 const struct prx_reloc
*r2
= p2
;
28 if (r1
->vaddr
< r2
->vaddr
) return -1;
29 if (r1
->vaddr
> r2
->vaddr
) return 1;
34 int check_apply_relocs (struct prx
*p
)
36 struct prx_reloc
*r
, *lastxhi
= NULL
;
37 struct elf_program
*offsbase
;
38 struct elf_program
*addrbase
;
39 uint32 index
, addend
, base
, temp
;
40 uint32 hiaddr
, loaddr
;
43 for (index
= 0; index
< p
->relocnum
; index
++) {
44 r
= &p
->relocs
[index
];
45 if (r
->offsbase
>= p
->phnum
) {
46 error (__FILE__
": invalid offset base for relocation (%d)", r
->offsbase
);
50 if (r
->addrbase
>= p
->phnum
) {
51 error (__FILE__
": invalid address base for relocation (%d)", r
->offsbase
);
55 offsbase
= &p
->programs
[r
->offsbase
];
56 addrbase
= &p
->programs
[r
->addrbase
];
58 r
->vaddr
= r
->offset
+ offsbase
->vaddr
;
59 if (!prx_inside_progfile (offsbase
, r
->vaddr
, 4)) {
60 error (__FILE__
": relocation points to invalid address (0x%08X)", r
->vaddr
);
65 for (index
= 0; index
< p
->relocnum
; index
++) {
66 r
= &p
->relocs
[index
];
67 offsbase
= &p
->programs
[r
->offsbase
];
68 addrbase
= &p
->programs
[r
->addrbase
];
70 addend
= read_uint32_le (&offsbase
->data
[r
->offset
]);
78 r
->target
= (r
->offset
+ offsbase
->vaddr
) & 0xF0000000;
79 r
->target
= (((addend
& 0x3FFFFFF) << 2) | r
->target
) + addrbase
->vaddr
;
80 addend
= (addend
& ~0x3FFFFFF) | (r
->target
>> 2);
81 if (!prx_inside_progfile (addrbase
, r
->target
, 8)) {
82 error (__FILE__
": mips26 reference out of range at 0x%08X (0x%08X)", r
->vaddr
, r
->target
);
84 write_uint32_le ((uint8
*)&offsbase
->data
[r
->offset
], addend
);
88 while (++index
< p
->relocnum
) {
89 if (p
->relocs
[index
].type
!= R_MIPS_HI16
) break;
90 if (p
->relocs
[index
].offsbase
!= r
->offsbase
) {
91 error (__FILE__
": changed offset base");
94 if (p
->relocs
[index
].addrbase
!= r
->addrbase
) {
95 error (__FILE__
": changed offset base");
98 temp
= read_uint32_le (&offsbase
->data
[p
->relocs
[index
].offset
]) & 0xFFFF;
99 if (temp
!= (addend
& 0xFFFF)) {
100 error (__FILE__
": changed hi");
105 if (index
== p
->relocnum
) {
106 error (__FILE__
": hi16 without matching lo16");
110 if (p
->relocs
[index
].type
!= R_MIPS_LO16
||
111 p
->relocs
[index
].offsbase
!= r
->offsbase
||
112 p
->relocs
[index
].addrbase
!= r
->addrbase
) {
113 error (__FILE__
": hi16 without matching lo16");
117 temp
= read_uint32_le (&offsbase
->data
[p
->relocs
[index
].offset
]) & 0xFFFF;
118 if (temp
& 0x8000) temp
|= ~0xFFFF;
120 r
->target
= ((addend
& 0xFFFF) << 16) + addrbase
->vaddr
+ temp
;
121 addend
= temp
& 0xFFFF;
122 if (!prx_inside_progmem (addrbase
, r
->target
, 1)) {
123 error (__FILE__
": hi16 reference out of range at 0x%08X (0x%08X)", r
->vaddr
, r
->target
);
126 loaddr
= r
->target
& 0xFFFF;
127 hiaddr
= (((r
->target
>> 15) + 1) >> 1) & 0xFFFF;
129 while (base
< index
) {
130 p
->relocs
[base
].target
= r
->target
;
131 temp
= (read_uint32_le (&offsbase
->data
[p
->relocs
[base
].offset
]) & ~0xFFFF) | hiaddr
;
132 write_uint32_le ((uint8
*) &offsbase
->data
[p
->relocs
[base
].offset
], temp
);
136 while (index
< p
->relocnum
) {
137 temp
= read_uint32_le (&offsbase
->data
[p
->relocs
[index
].offset
]);
138 if ((temp
& 0xFFFF) != addend
) break;
139 if (p
->relocs
[index
].type
!= R_MIPS_LO16
) break;
140 if (p
->relocs
[index
].offsbase
!= r
->offsbase
) break;
141 if (p
->relocs
[index
].addrbase
!= r
->addrbase
) break;
143 p
->relocs
[index
].target
= r
->target
;
145 temp
= (temp
& ~0xFFFF) | loaddr
;
146 write_uint32_le ((uint8
*) &offsbase
->data
[p
->relocs
[index
].offset
], temp
);
152 r
->target
= ((addend
& 0xFFFF) << 16) + addrbase
->vaddr
+ r
->addend
;
153 addend
= (addend
& ~0xFFFF) | ((((r
->target
>> 15) + 1) >> 1) & 0xFFFF);
154 if (!prx_inside_progmem (addrbase
, r
->target
, 1)) {
155 error (__FILE__
": xhi16 reference out of range at 0x%08X (0x%08X)", r
->vaddr
, r
->target
);
157 write_uint32_le ((uint8
*)&offsbase
->data
[r
->offset
], addend
);
163 r
->target
= (addend
& 0xFFFF) + addrbase
->vaddr
;
165 if ((lastxhi
->target
& 0xFFFF) == (r
->target
& 0xFFFF) &&
166 lastxhi
->addrbase
== r
->addrbase
&&
167 lastxhi
->offsbase
== r
->offsbase
) {
168 r
->target
= lastxhi
->target
;
171 addend
= (addend
& ~0xFFFF) | (r
->target
& 0xFFFF);
172 if (!prx_inside_progmem (addrbase
, r
->target
, 1)) {
173 error (__FILE__
": lo16 reference out of range at 0x%08X (0x%08X)", r
->vaddr
, r
->target
);
175 write_uint32_le ((uint8
*)&offsbase
->data
[r
->offset
], addend
);
179 r
->target
= addend
+ addrbase
->vaddr
;
181 /*if (!inside_progmem (addrbase, r->target, 1)) {
182 error (__FILE__ ": mips32 reference out of range at 0x%08X (0x%08X)", r->vaddr, r->target);
184 write_uint32_le ((uint8
*)&offsbase
->data
[r
->offset
], addend
);
188 error (__FILE__
": invalid reference type %d", r
->type
);
195 p
->relocsbyaddr
= xmalloc (p
->relocnum
* sizeof (struct prx_reloc
));
196 memcpy (p
->relocsbyaddr
, p
->relocs
, p
->relocnum
* sizeof (struct prx_reloc
));
198 qsort (p
->relocs
, p
->relocnum
, sizeof (struct prx_reloc
), &cmp_relocs
);
199 qsort (p
->relocsbyaddr
, p
->relocnum
, sizeof (struct prx_reloc
), &cmp_relocs_by_addr
);
206 uint32
count_relocs_b (uint32 prgidx
, const uint8
*data
, uint32 size
)
209 uint8 part1s
, part2s
;
210 uint32 block1s
, block2s
;
211 uint8 block1
[256], block2
[256];
212 uint32 temp1
, temp2
, part1
, part2
;
213 uint32 count
= 0, nbits
;
216 for (nbits
= 1; (1 << nbits
) < prgidx
; nbits
++) {
218 error (__FILE__
": invalid number of bits for indexes");
223 if (read_uint16_le (data
) != 0) {
224 error (__FILE__
": invalid header for relocation");
235 memcpy (block1
, data
, block1s
);
241 memcpy (block2
, data
, block2s
);
248 uint32 cmd
= read_uint16_le (data
);
249 temp1
= (cmd
<< (16 - part1s
)) & 0xFFFF;
250 temp1
= (temp1
>> (16 -part1s
)) & 0xFFFF;
253 if (temp1
>= block1s
) {
254 error (__FILE__
": invalid index for the first part");
257 part1
= block1
[temp1
];
258 if ((part1
& 0x06) == 0x06) {
259 error (__FILE__
": invalid size");
263 data
+= part1
& 0x06;
265 if ((part1
& 0x01) == 0) {
266 if ((part1
& 0x06) == 2) {
267 error (__FILE__
": invalid size of part1");
271 temp2
= (cmd
<< (16 - (part1s
+ nbits
+ part2s
))) & 0xFFFF;
272 temp2
= (temp2
>> (16 - part2s
)) & 0xFFFF;
273 if (temp2
>= block2s
) {
274 error (__FILE__
": invalid index for the second part");
278 part2
= block2
[temp2
];
280 switch (part1
& 0x38) {
289 error (__FILE__
": invalid addendum size");
294 case 1: case 2: case 3:
295 case 4: case 5: case 6: case 7:
301 error (__FILE__
": invalid relocation type %d", part2
);
311 int load_relocs_b (struct elf_program
*programs
, struct prx_reloc
*out
, uint32 prgidx
, const uint8
*data
, uint32 size
)
315 uint8 part1s
, part2s
;
316 uint32 block1s
, block2s
;
317 uint8 block1
[256], block2
[256];
319 uint32 part1
, part2
, lastpart2
;
320 uint32 addend
= 0, offset
= 0;
321 uint32 offsbase
= 0xFFFFFFFF;
326 for (nbits
= 1; (1 << nbits
) < prgidx
; nbits
++) {
336 memcpy (block1
, data
, block1s
);
342 memcpy (block2
, data
, block2s
);
350 uint32 cmd
= read_uint16_le (data
);
351 temp1
= (cmd
<< (16 - part1s
)) & 0xFFFF;
352 temp1
= (temp1
>> (16 -part1s
)) & 0xFFFF;
355 part1
= block1
[temp1
];
357 if ((part1
& 0x01) == 0) {
358 offsbase
= (cmd
<< (16 - part1s
- nbits
)) & 0xFFFF;
359 offsbase
= (offsbase
>> (16 - nbits
)) & 0xFFFF;
360 if (!(offsbase
< prgidx
)) {
361 error (__FILE__
": invalid offset base");
365 offset
= cmd
>> (part1s
+ nbits
);
366 if ((part1
& 0x06) == 0) continue;
367 offset
= read_uint32_le (data
);
370 temp2
= (cmd
<< (16 - (part1s
+ nbits
+ part2s
))) & 0xFFFF;
371 temp2
= (temp2
>> (16 - part2s
)) & 0xFFFF;
373 addrbase
= (cmd
<< (16 - part1s
- nbits
)) & 0xFFFF;
374 addrbase
= (addrbase
>> (16 - nbits
)) & 0xFFFF;
375 if (!(addrbase
< prgidx
)) {
376 error (__FILE__
": invalid address base");
379 part2
= block2
[temp2
];
381 switch (part1
& 0x06) {
385 cmd
>>= part1s
+ part2s
+ nbits
;
388 cmd
>>= part1s
+ part2s
+ nbits
;
393 if (cmd
& 0x8000) cmd
|= ~0xFFFF;
394 cmd
= (cmd
>> (part1s
+ part2s
+ nbits
)) << 16;
395 cmd
|= read_uint16_le (data
);
400 offset
= read_uint32_le (data
);
405 if (!(offset
< programs
[offsbase
].filesz
)) {
406 error (__FILE__
": invalid relocation offset");
410 switch (part1
& 0x38) {
415 if ((lastpart2
^ 0x04) != 0) {
420 addend
= read_uint16_le (data
);
427 out
[count
].addrbase
= addrbase
;
428 out
[count
].offsbase
= offsbase
;
429 out
[count
].offset
= offset
;
430 out
[count
].extra
= 0;
434 out
[count
++].type
= R_MIPS_32
;
439 out
[count
++].type
= R_MIPS_26
;
442 if (addend
& 0x8000) addend
|= ~0xFFFF;
443 out
[count
].addend
= addend
;
444 out
[count
++].type
= R_MIPSX_HI16
;
448 out
[count
++].type
= R_MIPS_LO16
;
451 out
[count
++].type
= R_MIPSX_J26
;
454 out
[count
++].type
= R_MIPSX_JAL26
;
464 int load_relocs (struct prx
*p
)
466 uint32 i
, ret
, count
= 0;
468 for (i
= 0; i
< p
->shnum
; i
++) {
469 struct elf_section
*section
= &p
->sections
[i
];
470 if (section
->type
== SHT_PRXRELOC
) {
471 count
+= section
->size
>> 3;
474 for (i
= 0; i
< p
->phnum
; i
++) {
475 struct elf_program
*program
= &p
->programs
[i
];
476 if (program
->type
== PT_PRXRELOC
) {
477 count
+= program
->filesz
>> 3;
478 } else if (program
->type
== PT_PRXRELOC2
) {
479 ret
= count_relocs_b (i
, program
->data
, program
->filesz
);
487 error (__FILE__
": no relocation found");
492 p
->relocs
= (struct prx_reloc
*) xmalloc (count
* sizeof (struct prx_reloc
));
493 memset (p
->relocs
, 0, count
* sizeof (struct prx_reloc
));
496 for (i
= 0; i
< p
->shnum
; i
++) {
497 struct elf_section
*section
= &p
->sections
[i
];
498 if (section
->type
== SHT_PRXRELOC
) {
501 offset
= section
->offset
;
502 secsize
= section
->size
>> 3;
503 for (j
= 0; j
< secsize
; j
++) {
504 p
->relocs
[count
].offset
= read_uint32_le (&p
->data
[offset
]);
505 p
->relocs
[count
].type
= p
->data
[offset
+ 4];
506 p
->relocs
[count
].offsbase
= p
->data
[offset
+ 5];
507 p
->relocs
[count
].addrbase
= p
->data
[offset
+ 6];
508 p
->relocs
[count
].extra
= p
->data
[offset
+ 7];
516 for (i
= 0; i
< p
->phnum
; i
++) {
517 struct elf_program
*program
= &p
->programs
[i
];
518 if (program
->type
== PT_PRXRELOC
) {
521 offset
= program
->offset
;
522 progsize
= program
->filesz
>> 3;
523 for (j
= 0; j
< progsize
; j
++) {
524 p
->relocs
[count
].offset
= read_uint32_le (&p
->data
[offset
]);
525 p
->relocs
[count
].type
= p
->data
[offset
+ 4];
526 p
->relocs
[count
].offsbase
= p
->data
[offset
+ 5];
527 p
->relocs
[count
].addrbase
= p
->data
[offset
+ 6];
528 p
->relocs
[count
].extra
= p
->data
[offset
+ 7];
533 } else if (program
->type
== PT_PRXRELOC2
) {
534 ret
= load_relocs_b (p
->programs
, &p
->relocs
[count
], i
, program
->data
, program
->filesz
);
542 if (!check_apply_relocs (p
)) return 0;
547 void free_relocs (struct prx
*p
)
554 free (p
->relocsbyaddr
);
555 p
->relocsbyaddr
= NULL
;
560 uint32
prx_findreloc (struct prx
*p
, uint32 target
)
562 uint32 first
, last
, i
;
566 while (first
< last
) {
567 i
= (first
+ last
) / 2;
568 if (p
->relocs
[i
].target
< target
) {
578 uint32
prx_findrelocbyaddr (struct prx
*p
, uint32 vaddr
)
580 uint32 first
, last
, i
;
584 while (first
< last
) {
585 i
= (first
+ last
) / 2;
586 if (p
->relocsbyaddr
[i
].vaddr
< vaddr
) {
597 void print_relocs (struct prx
*p
)
600 report ("\nRelocs:\n");
601 for (i
= 0; i
< p
->relocnum
; i
++) {
602 const char *type
= "unk";
604 switch (p
->relocs
[i
].type
) {
605 case R_MIPSX_HI16
: type
= "xhi16"; break;
606 case R_MIPSX_J26
: type
= "xj26"; break;
607 case R_MIPSX_JAL26
: type
= "xjal26"; break;
608 case R_MIPS_16
: type
= "mips16"; break;
609 case R_MIPS_26
: type
= "mips26"; break;
610 case R_MIPS_32
: type
= "mips32"; break;
611 case R_MIPS_HI16
: type
= "hi16"; break;
612 case R_MIPS_LO16
: type
= "lo16"; break;
613 case R_MIPS_NONE
: type
= "none"; break;
615 report (" Type: %8s Vaddr: 0x%08X Target: 0x%08X Addend: 0x%08X\n",
616 type
, p
->relocs
[i
].vaddr
, p
->relocs
[i
].target
, p
->relocs
[i
].addend
);