SLLV, SRLV, SRAV and ROTV have reverse order of operands
[pspdecompiler.git] / relocs.c
blob59095ff6c9b3362ff124d1afd217b2e64956314c
1 /**
2 * Author: Humberto Naves (hsnaves@gmail.com)
3 */
5 #include <stdlib.h>
6 #include <string.h>
8 #include "prx.h"
9 #include "utils.h"
13 static
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;
20 return 0;
23 static
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;
30 return 0;
33 static
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);
47 return 0;
50 if (r->addrbase >= p->phnum) {
51 error (__FILE__ ": invalid address base for relocation (%d)", r->offsbase);
52 return 0;
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);
61 return 0;
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]);
72 switch (r->type) {
73 case R_MIPS_NONE:
74 break;
75 case R_MIPS_26:
76 case R_MIPSX_J26:
77 case R_MIPSX_JAL26:
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);
85 break;
86 case R_MIPS_HI16:
87 base = index;
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");
92 return 0;
94 if (p->relocs[index].addrbase != r->addrbase) {
95 error (__FILE__ ": changed offset base");
96 return 0;
98 temp = read_uint32_le (&offsbase->data[p->relocs[index].offset]) & 0xFFFF;
99 if (temp != (addend & 0xFFFF)) {
100 error (__FILE__ ": changed hi");
101 return 0;
105 if (index == p->relocnum) {
106 error (__FILE__ ": hi16 without matching lo16");
107 return 0;
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");
114 return 0;
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);
133 base++;
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);
147 index++;
149 index--;
150 break;
151 case R_MIPSX_HI16:
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);
158 lastxhi = r;
159 break;
161 case R_MIPS_16:
162 case R_MIPS_LO16:
163 r->target = (addend & 0xFFFF) + addrbase->vaddr;
164 if (lastxhi) {
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);
176 break;
178 case R_MIPS_32:
179 r->target = addend + addrbase->vaddr;
180 addend = r->target;
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);
185 break;
187 default:
188 error (__FILE__ ": invalid reference type %d", r->type);
189 return 0;
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);
201 return 1;
205 static
206 uint32 count_relocs_b (uint32 prgidx, const uint8 *data, uint32 size)
208 const uint8 *end;
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;
215 end = data + size;
216 for (nbits = 1; (1 << nbits) < prgidx; nbits++) {
217 if (nbits >= 33) {
218 error (__FILE__ ": invalid number of bits for indexes");
219 return 0;
223 if (read_uint16_le (data) != 0) {
224 error (__FILE__ ": invalid header for relocation");
225 return 0;
228 part1s = data[2];
229 part2s = data[3];
231 block1s = data[4];
232 data += 4;
234 if (block1s) {
235 memcpy (block1, data, block1s);
236 data += block1s;
239 block2s = *data;
240 if (block2s) {
241 memcpy (block2, data, block2s);
242 data += block2s;
246 count = 0;
247 while (data < end) {
248 uint32 cmd = read_uint16_le (data);
249 temp1 = (cmd << (16 - part1s)) & 0xFFFF;
250 temp1 = (temp1 >> (16 -part1s)) & 0xFFFF;
252 data = data + 2;
253 if (temp1 >= block1s) {
254 error (__FILE__ ": invalid index for the first part");
255 return 0;
257 part1 = block1[temp1];
258 if ((part1 & 0x06) == 0x06) {
259 error (__FILE__ ": invalid size");
260 return 0;
263 data += part1 & 0x06;
265 if ((part1 & 0x01) == 0) {
266 if ((part1 & 0x06) == 2) {
267 error (__FILE__ ": invalid size of part1");
268 return 0;
270 } else {
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");
275 return 0;
278 part2 = block2[temp2];
280 switch (part1 & 0x38) {
281 case 0x00:
282 break;
283 case 0x08:
284 break;
285 case 0x10:
286 data += 2;
287 break;
288 default:
289 error (__FILE__ ": invalid addendum size");
290 return 0;
293 switch (part2) {
294 case 1: case 2: case 3:
295 case 4: case 5: case 6: case 7:
296 count++;
297 break;
298 case 0:
299 break;
300 default:
301 error (__FILE__ ": invalid relocation type %d", part2);
302 return 0;
307 return count;
310 static
311 int load_relocs_b (struct elf_program *programs, struct prx_reloc *out, uint32 prgidx, const uint8 *data, uint32 size)
313 const uint8 *end;
314 uint32 nbits;
315 uint8 part1s, part2s;
316 uint32 block1s, block2s;
317 uint8 block1[256], block2[256];
318 uint32 temp1, temp2;
319 uint32 part1, part2, lastpart2;
320 uint32 addend = 0, offset = 0;
321 uint32 offsbase = 0xFFFFFFFF;
322 uint32 addrbase;
323 uint32 count;
325 end = data + size;
326 for (nbits = 1; (1 << nbits) < prgidx; nbits++) {
329 part1s = data[2];
330 part2s = data[3];
332 block1s = data[4];
333 data += 4;
335 if (block1s) {
336 memcpy (block1, data, block1s);
337 data += block1s;
340 block2s = *data;
341 if (block2s) {
342 memcpy (block2, data, block2s);
343 data += block2s;
347 count = 0;
348 lastpart2 = block2s;
349 while (data < end) {
350 uint32 cmd = read_uint16_le (data);
351 temp1 = (cmd << (16 - part1s)) & 0xFFFF;
352 temp1 = (temp1 >> (16 -part1s)) & 0xFFFF;
354 data += 2;
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");
362 return 0;
365 offset = cmd >> (part1s + nbits);
366 if ((part1 & 0x06) == 0) continue;
367 offset = read_uint32_le (data);
368 data += 4;
369 } else {
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");
377 return 0;
379 part2 = block2[temp2];
381 switch (part1 & 0x06) {
382 case 0:
383 if (cmd & 0x8000) {
384 cmd |= ~0xFFFF;
385 cmd >>= part1s + part2s + nbits;
386 cmd |= ~0xFFFF;
387 } else {
388 cmd >>= part1s + part2s + nbits;
390 offset += cmd;
391 break;
392 case 2:
393 if (cmd & 0x8000) cmd |= ~0xFFFF;
394 cmd = (cmd >> (part1s + part2s + nbits)) << 16;
395 cmd |= read_uint16_le (data);
396 offset += cmd;
397 data += 2;
398 break;
399 case 4:
400 offset = read_uint32_le (data);
401 data += 4;
402 break;
405 if (!(offset < programs[offsbase].filesz)) {
406 error (__FILE__ ": invalid relocation offset");
407 return 0;
410 switch (part1 & 0x38) {
411 case 0x00:
412 addend = 0;
413 break;
414 case 0x08:
415 if ((lastpart2 ^ 0x04) != 0) {
416 addend = 0;
418 break;
419 case 0x10:
420 addend = read_uint16_le (data);
421 data += 2;
422 break;
425 lastpart2 = part2;
427 out[count].addrbase = addrbase;
428 out[count].offsbase = offsbase;
429 out[count].offset = offset;
430 out[count].extra = 0;
432 switch (part2) {
433 case 2:
434 out[count++].type = R_MIPS_32;
435 break;
436 case 0:
437 break;
438 case 3:
439 out[count++].type = R_MIPS_26;
440 break;
441 case 4:
442 if (addend & 0x8000) addend |= ~0xFFFF;
443 out[count].addend = addend;
444 out[count++].type = R_MIPSX_HI16;
445 break;
446 case 1:
447 case 5:
448 out[count++].type = R_MIPS_LO16;
449 break;
450 case 6:
451 out[count++].type = R_MIPSX_J26;
452 break;
453 case 7:
454 out[count++].type = R_MIPSX_JAL26;
455 break;
460 return count;
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);
480 if (!ret) return 0;
481 count += ret;
485 p->relocs = NULL;
486 if (!count) {
487 error (__FILE__ ": no relocation found");
488 return 0;
491 p->relocnum = count;
492 p->relocs = (struct prx_reloc *) xmalloc (count * sizeof (struct prx_reloc));
493 memset (p->relocs, 0, count * sizeof (struct prx_reloc));
495 count = 0;
496 for (i = 0; i < p->shnum; i++) {
497 struct elf_section *section = &p->sections[i];
498 if (section->type == SHT_PRXRELOC) {
499 uint32 j, secsize;
500 uint32 offset;
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];
510 count++;
511 offset += 8;
516 for (i = 0; i < p->phnum; i++) {
517 struct elf_program *program = &p->programs[i];
518 if (program->type == PT_PRXRELOC) {
519 uint32 j, progsize;
520 uint32 offset;
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];
530 count++;
531 offset += 8;
533 } else if (program->type == PT_PRXRELOC2) {
534 ret = load_relocs_b (p->programs, &p->relocs[count], i, program->data, program->filesz);
535 if (!ret) {
536 return 0;
538 count += ret;
542 if (!check_apply_relocs (p)) return 0;
544 return 1;
547 void free_relocs (struct prx *p)
549 if (p->relocs)
550 free (p->relocs);
551 p->relocs = NULL;
553 if (p->relocsbyaddr)
554 free (p->relocsbyaddr);
555 p->relocsbyaddr = NULL;
560 uint32 prx_findreloc (struct prx *p, uint32 target)
562 uint32 first, last, i;
564 first = 0;
565 last = p->relocnum;
566 while (first < last) {
567 i = (first + last) / 2;
568 if (p->relocs[i].target < target) {
569 first = i + 1;
570 } else {
571 last = i;
575 return first;
578 uint32 prx_findrelocbyaddr (struct prx *p, uint32 vaddr)
580 uint32 first, last, i;
582 first = 0;
583 last = p->relocnum;
584 while (first < last) {
585 i = (first + last) / 2;
586 if (p->relocsbyaddr[i].vaddr < vaddr) {
587 first = i + 1;
588 } else {
589 last = i;
593 return first;
597 void print_relocs (struct prx *p)
599 uint32 i;
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);