Passo intermediario, ainda falta um longo caminho
[pspdecompiler.git] / relocs.c
blobb2892c3c66efe8aafe2690b0f91e21f45ff65b62
2 #include <stdlib.h>
3 #include <string.h>
5 #include "prx.h"
6 #include "utils.h"
10 static
11 int cmp_relocs (const void *p1, const void *p2)
13 const struct prx_reloc *r1 = p1;
14 const struct prx_reloc *r2 = p2;
15 if (r1->target < r2->target) return -1;
16 if (r1->target > r2->target) return 1;
17 return 0;
20 static
21 int cmp_relocs_by_addr (const void *p1, const void *p2)
23 const struct prx_reloc *r1 = p1;
24 const struct prx_reloc *r2 = p2;
25 if (r1->vaddr < r2->vaddr) return -1;
26 if (r1->vaddr > r2->vaddr) return 1;
27 return 0;
30 static
31 int check_apply_relocs (struct prx *p)
33 struct prx_reloc *r, *lastxhi = NULL;
34 struct elf_program *offsbase;
35 struct elf_program *addrbase;
36 uint32 index, addend, base, temp;
37 uint32 hiaddr, loaddr;
40 for (index = 0; index < p->relocnum; index++) {
41 r = &p->relocs[index];
42 if (r->offsbase >= p->phnum) {
43 error (__FILE__ ": invalid offset base for relocation (%d)", r->offsbase);
44 return 0;
47 if (r->addrbase >= p->phnum) {
48 error (__FILE__ ": invalid address base for relocation (%d)", r->offsbase);
49 return 0;
52 offsbase = &p->programs[r->offsbase];
53 addrbase = &p->programs[r->addrbase];
55 r->vaddr = r->offset + offsbase->vaddr;
56 if (!prx_inside_progfile (offsbase, r->vaddr, 4)) {
57 error (__FILE__ ": relocation points to invalid address (0x%08X)", r->vaddr);
58 return 0;
62 for (index = 0; index < p->relocnum; index++) {
63 r = &p->relocs[index];
64 offsbase = &p->programs[r->offsbase];
65 addrbase = &p->programs[r->addrbase];
67 addend = read_uint32_le (&offsbase->data[r->offset]);
69 switch (r->type) {
70 case R_MIPS_NONE:
71 break;
72 case R_MIPS_26:
73 case R_MIPSX_J26:
74 case R_MIPSX_JAL26:
75 r->target = (r->offset + offsbase->vaddr) & 0xF0000000;
76 r->target = (((addend & 0x3FFFFFF) << 2) | r->target) + addrbase->vaddr;
77 addend = (addend & ~0x3FFFFFF) | (r->target >> 2);
78 if (!prx_inside_progfile (addrbase, r->target, 8)) {
79 error (__FILE__ ": mips26 reference out of range at 0x%08X (0x%08X)", r->vaddr, r->target);
81 write_uint32_le ((uint8 *)&offsbase->data[r->offset], addend);
82 break;
83 case R_MIPS_HI16:
84 base = index;
85 while (++index < p->relocnum) {
86 if (p->relocs[index].type != R_MIPS_HI16) break;
87 if (p->relocs[index].offsbase != r->offsbase) {
88 error (__FILE__ ": changed offset base");
89 return 0;
91 if (p->relocs[index].addrbase != r->addrbase) {
92 error (__FILE__ ": changed offset base");
93 return 0;
95 temp = read_uint32_le (&offsbase->data[p->relocs[index].offset]) & 0xFFFF;
96 if (temp != (addend & 0xFFFF)) {
97 error (__FILE__ ": changed hi");
98 return 0;
102 if (index == p->relocnum) {
103 error (__FILE__ ": hi16 without matching lo16");
104 return 0;
107 if (p->relocs[index].type != R_MIPS_LO16 ||
108 p->relocs[index].offsbase != r->offsbase ||
109 p->relocs[index].addrbase != r->addrbase) {
110 error (__FILE__ ": hi16 without matching lo16");
111 return 0;
114 temp = read_uint32_le (&offsbase->data[p->relocs[index].offset]) & 0xFFFF;
115 if (temp & 0x8000) temp |= ~0xFFFF;
117 r->target = ((addend & 0xFFFF) << 16) + addrbase->vaddr + temp;
118 addend = temp & 0xFFFF;
119 if (!prx_inside_progmem (addrbase, r->target, 1)) {
120 error (__FILE__ ": hi16 reference out of range at 0x%08X (0x%08X)", r->vaddr, r->target);
123 loaddr = r->target & 0xFFFF;
124 hiaddr = (((r->target >> 15) + 1) >> 1) & 0xFFFF;
126 while (base < index) {
127 p->relocs[base].target = r->target;
128 temp = (read_uint32_le (&offsbase->data[p->relocs[base].offset]) & ~0xFFFF) | hiaddr;
129 write_uint32_le ((uint8 *) &offsbase->data[p->relocs[base].offset], temp);
130 base++;
133 while (index < p->relocnum) {
134 temp = read_uint32_le (&offsbase->data[p->relocs[index].offset]);
135 if ((temp & 0xFFFF) != addend) break;
136 if (p->relocs[index].type != R_MIPS_LO16) break;
137 if (p->relocs[index].offsbase != r->offsbase) break;
138 if (p->relocs[index].addrbase != r->addrbase) break;
140 p->relocs[index].target = r->target;
142 temp = (temp & ~0xFFFF) | loaddr;
143 write_uint32_le ((uint8 *) &offsbase->data[p->relocs[index].offset], temp);
144 index++;
146 index--;
147 break;
148 case R_MIPSX_HI16:
149 r->target = ((addend & 0xFFFF) << 16) + addrbase->vaddr + r->addend;
150 addend = (addend & ~0xFFFF) | ((((r->target >> 15) + 1) >> 1) & 0xFFFF);
151 if (!prx_inside_progmem (addrbase, r->target, 1)) {
152 error (__FILE__ ": xhi16 reference out of range at 0x%08X (0x%08X)", r->vaddr, r->target);
154 write_uint32_le ((uint8 *)&offsbase->data[r->offset], addend);
155 lastxhi = r;
156 break;
158 case R_MIPS_16:
159 case R_MIPS_LO16:
160 r->target = (addend & 0xFFFF) + addrbase->vaddr;
161 if (lastxhi) {
162 if ((lastxhi->target & 0xFFFF) == (r->target & 0xFFFF) &&
163 lastxhi->addrbase == r->addrbase &&
164 lastxhi->offsbase == r->offsbase) {
165 r->target = lastxhi->target;
168 addend = (addend & ~0xFFFF) | (r->target & 0xFFFF);
169 if (!prx_inside_progmem (addrbase, r->target, 1)) {
170 error (__FILE__ ": lo16 reference out of range at 0x%08X (0x%08X)", r->vaddr, r->target);
172 write_uint32_le ((uint8 *)&offsbase->data[r->offset], addend);
173 break;
175 case R_MIPS_32:
176 r->target = addend + addrbase->vaddr;
177 addend = r->target;
178 /*if (!inside_progmem (addrbase, r->target, 1)) {
179 error (__FILE__ ": mips32 reference out of range at 0x%08X (0x%08X)", r->vaddr, r->target);
181 write_uint32_le ((uint8 *)&offsbase->data[r->offset], addend);
182 break;
184 default:
185 error (__FILE__ ": invalid reference type %d", r->type);
186 return 0;
192 p->relocsbyaddr = xmalloc (p->relocnum * sizeof (struct prx_reloc));
193 memcpy (p->relocsbyaddr, p->relocs, p->relocnum * sizeof (struct prx_reloc));
195 qsort (p->relocs, p->relocnum, sizeof (struct prx_reloc), &cmp_relocs);
196 qsort (p->relocsbyaddr, p->relocnum, sizeof (struct prx_reloc), &cmp_relocs_by_addr);
198 return 1;
202 static
203 uint32 count_relocs_b (uint32 prgidx, const uint8 *data, uint32 size)
205 const uint8 *end;
206 uint8 part1s, part2s;
207 uint32 block1s, block2s;
208 uint8 block1[256], block2[256];
209 uint32 temp1, temp2, part1, part2;
210 uint32 count = 0, nbits;
212 end = data + size;
213 for (nbits = 1; (1 << nbits) < prgidx; nbits++) {
214 if (nbits >= 33) {
215 error (__FILE__ ": invalid number of bits for indexes");
216 return 0;
220 if (read_uint16_le (data) != 0) {
221 error (__FILE__ ": invalid header for relocation");
222 return 0;
225 part1s = data[2];
226 part2s = data[3];
228 block1s = data[4];
229 data += 4;
231 if (block1s) {
232 memcpy (block1, data, block1s);
233 data += block1s;
236 block2s = *data;
237 if (block2s) {
238 memcpy (block2, data, block2s);
239 data += block2s;
243 count = 0;
244 while (data < end) {
245 uint32 cmd = read_uint16_le (data);
246 temp1 = (cmd << (16 - part1s)) & 0xFFFF;
247 temp1 = (temp1 >> (16 -part1s)) & 0xFFFF;
249 data = data + 2;
250 if (temp1 >= block1s) {
251 error (__FILE__ ": invalid index for the first part");
252 return 0;
254 part1 = block1[temp1];
255 if ((part1 & 0x06) == 0x06) {
256 error (__FILE__ ": invalid size");
257 return 0;
260 data += part1 & 0x06;
262 if ((part1 & 0x01) == 0) {
263 if ((part1 & 0x06) == 2) {
264 error (__FILE__ ": invalid size of part1");
265 return 0;
267 } else {
268 temp2 = (cmd << (16 - (part1s + nbits + part2s))) & 0xFFFF;
269 temp2 = (temp2 >> (16 - part2s)) & 0xFFFF;
270 if (temp2 >= block2s) {
271 error (__FILE__ ": invalid index for the second part");
272 return 0;
275 part2 = block2[temp2];
277 switch (part1 & 0x38) {
278 case 0x00:
279 break;
280 case 0x08:
281 break;
282 case 0x10:
283 data += 2;
284 break;
285 default:
286 error (__FILE__ ": invalid addendum size");
287 return 0;
290 switch (part2) {
291 case 1: case 2: case 3:
292 case 4: case 5: case 6: case 7:
293 count++;
294 break;
295 case 0:
296 break;
297 default:
298 error (__FILE__ ": invalid relocation type %d", part2);
299 return 0;
304 return count;
307 static
308 int load_relocs_b (struct elf_program *programs, struct prx_reloc *out, uint32 prgidx, const uint8 *data, uint32 size)
310 const uint8 *end;
311 uint32 nbits;
312 uint8 part1s, part2s;
313 uint32 block1s, block2s;
314 uint8 block1[256], block2[256];
315 uint32 temp1, temp2;
316 uint32 part1, part2, lastpart2;
317 uint32 addend = 0, offset = 0;
318 uint32 offsbase = 0xFFFFFFFF;
319 uint32 addrbase;
320 uint32 count;
322 end = data + size;
323 for (nbits = 1; (1 << nbits) < prgidx; nbits++) {
326 part1s = data[2];
327 part2s = data[3];
329 block1s = data[4];
330 data += 4;
332 if (block1s) {
333 memcpy (block1, data, block1s);
334 data += block1s;
337 block2s = *data;
338 if (block2s) {
339 memcpy (block2, data, block2s);
340 data += block2s;
344 count = 0;
345 lastpart2 = block2s;
346 while (data < end) {
347 uint32 cmd = read_uint16_le (data);
348 temp1 = (cmd << (16 - part1s)) & 0xFFFF;
349 temp1 = (temp1 >> (16 -part1s)) & 0xFFFF;
351 data += 2;
352 part1= block1[temp1];
354 if ((part1 & 0x01) == 0) {
355 offsbase = (cmd << (16 - part1s - nbits)) & 0xFFFF;
356 offsbase = (offsbase >> (16 - nbits)) & 0xFFFF;
357 if (!(offsbase < prgidx)) {
358 error (__FILE__ ": invalid offset base");
359 return 0;
362 offset = cmd >> (part1s + nbits);
363 if ((part1 & 0x06) == 0) continue;
364 offset = read_uint32_le (data);
365 data += 4;
366 } else {
367 temp2 = (cmd << (16 - (part1s + nbits + part2s))) & 0xFFFF;
368 temp2 = (temp2 >> (16 - part2s)) & 0xFFFF;
370 addrbase = (cmd << (16 - part1s - nbits)) & 0xFFFF;
371 addrbase = (addrbase >> (16 - nbits)) & 0xFFFF;
372 if (!(addrbase < prgidx)) {
373 error (__FILE__ ": invalid address base");
374 return 0;
376 part2 = block2[temp2];
378 switch (part1 & 0x06) {
379 case 0:
380 if (cmd & 0x8000) {
381 cmd |= ~0xFFFF;
382 cmd >>= part1s + part2s + nbits;
383 cmd |= ~0xFFFF;
384 } else {
385 cmd >>= part1s + part2s + nbits;
387 offset += cmd;
388 break;
389 case 2:
390 if (cmd & 0x8000) cmd |= ~0xFFFF;
391 cmd = (cmd >> (part1s + part2s + nbits)) << 16;
392 cmd |= read_uint16_le (data);
393 offset += cmd;
394 data += 2;
395 break;
396 case 4:
397 offset = read_uint32_le (data);
398 data += 4;
399 break;
402 if (!(offset < programs[offsbase].filesz)) {
403 error (__FILE__ ": invalid relocation offset");
404 return 0;
407 switch (part1 & 0x38) {
408 case 0x00:
409 addend = 0;
410 break;
411 case 0x08:
412 if ((lastpart2 ^ 0x04) != 0) {
413 addend = 0;
415 break;
416 case 0x10:
417 addend = read_uint16_le (data);
418 data += 2;
419 break;
422 lastpart2 = part2;
424 out[count].addrbase = addrbase;
425 out[count].offsbase = offsbase;
426 out[count].offset = offset;
427 out[count].extra = 0;
429 switch (part2) {
430 case 2:
431 out[count++].type = R_MIPS_32;
432 break;
433 case 0:
434 break;
435 case 3:
436 out[count++].type = R_MIPS_26;
437 break;
438 case 4:
439 if (addend & 0x8000) addend |= ~0xFFFF;
440 out[count].addend = addend;
441 out[count++].type = R_MIPSX_HI16;
442 break;
443 case 1:
444 case 5:
445 out[count++].type = R_MIPS_LO16;
446 break;
447 case 6:
448 out[count++].type = R_MIPSX_J26;
449 break;
450 case 7:
451 out[count++].type = R_MIPSX_JAL26;
452 break;
457 return count;
461 int load_relocs (struct prx *p)
463 uint32 i, ret, count = 0;
465 for (i = 0; i < p->shnum; i++) {
466 struct elf_section *section = &p->sections[i];
467 if (section->type == SHT_PRXRELOC) {
468 count += section->size >> 3;
471 for (i = 0; i < p->phnum; i++) {
472 struct elf_program *program = &p->programs[i];
473 if (program->type == PT_PRXRELOC) {
474 count += program->filesz >> 3;
475 } else if (program->type == PT_PRXRELOC2) {
476 ret = count_relocs_b (i, program->data, program->filesz);
477 if (!ret) return 0;
478 count += ret;
482 p->relocs = NULL;
483 if (!count) {
484 error (__FILE__ ": no relocation found");
485 return 0;
488 p->relocnum = count;
489 p->relocs = (struct prx_reloc *) xmalloc (count * sizeof (struct prx_reloc));
490 memset (p->relocs, 0, count * sizeof (struct prx_reloc));
492 count = 0;
493 for (i = 0; i < p->shnum; i++) {
494 struct elf_section *section = &p->sections[i];
495 if (section->type == SHT_PRXRELOC) {
496 uint32 j, secsize;
497 uint32 offset;
498 offset = section->offset;
499 secsize = section->size >> 3;
500 for (j = 0; j < secsize; j++) {
501 p->relocs[count].offset = read_uint32_le (&p->data[offset]);
502 p->relocs[count].type = p->data[offset + 4];
503 p->relocs[count].offsbase = p->data[offset + 5];
504 p->relocs[count].addrbase = p->data[offset + 6];
505 p->relocs[count].extra = p->data[offset + 7];
507 count++;
508 offset += 8;
513 for (i = 0; i < p->phnum; i++) {
514 struct elf_program *program = &p->programs[i];
515 if (program->type == PT_PRXRELOC) {
516 uint32 j, progsize;
517 uint32 offset;
518 offset = program->offset;
519 progsize = program->filesz >> 3;
520 for (j = 0; j < progsize; j++) {
521 p->relocs[count].offset = read_uint32_le (&p->data[offset]);
522 p->relocs[count].type = p->data[offset + 4];
523 p->relocs[count].offsbase = p->data[offset + 5];
524 p->relocs[count].addrbase = p->data[offset + 6];
525 p->relocs[count].extra = p->data[offset + 7];
527 count++;
528 offset += 8;
530 } else if (program->type == PT_PRXRELOC2) {
531 ret = load_relocs_b (p->programs, &p->relocs[count], i, program->data, program->filesz);
532 if (!ret) {
533 return 0;
535 count += ret;
539 if (!check_apply_relocs (p)) return 0;
541 return 1;
544 void free_relocs (struct prx *p)
546 if (p->relocs)
547 free (p->relocs);
548 p->relocs = NULL;
550 if (p->relocsbyaddr)
551 free (p->relocsbyaddr);
552 p->relocsbyaddr = NULL;
557 uint32 prx_findreloc (struct prx *p, uint32 target)
559 uint32 first, last, i;
561 first = 0;
562 last = p->relocnum - 1;
563 while (first < last) {
564 i = (first + last) / 2;
565 if (p->relocs[i].target < target) {
566 first = i + 1;
567 } else {
568 last = i;
572 return first;
575 uint32 prx_findrelocbyaddr (struct prx *p, uint32 vaddr)
577 uint32 first, last, i;
579 first = 0;
580 last = p->relocnum - 1;
581 while (first < last) {
582 i = (first + last) / 2;
583 if (p->relocsbyaddr[i].vaddr < vaddr) {
584 first = i + 1;
585 } else {
586 last = i;
590 return first;
594 void print_relocs (struct prx *p)
596 uint32 i;
597 report ("\nRelocs:\n");
598 for (i = 0; i < p->relocnum; i++) {
599 const char *type = "unk";
601 switch (p->relocs[i].type) {
602 case R_MIPSX_HI16: type = "xhi16"; break;
603 case R_MIPSX_J26: type = "xj26"; break;
604 case R_MIPSX_JAL26: type = "xjal26"; break;
605 case R_MIPS_16: type = "mips16"; break;
606 case R_MIPS_26: type = "mips26"; break;
607 case R_MIPS_32: type = "mips32"; break;
608 case R_MIPS_HI16: type = "hi16"; break;
609 case R_MIPS_LO16: type = "lo16"; break;
610 case R_MIPS_NONE: type = "none"; break;
612 report (" Type: %8s Vaddr: 0x%08X Target: 0x%08X Addend: 0x%08X\n",
613 type, p->relocs[i].vaddr, p->relocs[i].target, p->relocs[i].addend);