PS2SDK
PS2 Homebrew Libraries
Loading...
Searching...
No Matches
srxfixup.c
1/*
2# _____ ___ ____ ___ ____
3# ____| | ____| | | |____|
4# | ___| |____ ___| ____| | \ PS2DEV Open Source Project.
5#-----------------------------------------------------------------------
6# Copyright ps2dev - http://www.ps2dev.org
7# Licenced under Academic Free License version 2.0
8# Review ps2sdk README & LICENSE files for further details.
9*/
10
11#include "srxfixup_internal.h"
12#include <stdint.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16
17static const char *conffile = NULL;
18static const char *ofile = NULL;
19static const char *rfile = NULL;
20static const char *ffile = NULL;
21static unsigned int startaddr;
22static const char *entrysym = NULL;
23static unsigned int verbose = 0;
24static unsigned int dumpflag = 0;
25static unsigned int dispmod_flag = 0;
26static int irx1_flag = 0;
27static int br_conv = 0;
28static int print_config = 0;
29static int allow_zero_text = 0;
30// clang-format off
31static const Opttable opttable[] =
32{
33 { "-v", ARG_HAVEARG_NONE, 'f', &verbose },
34 { "-d", ARG_HAVEARG_NONE, 'f', &dumpflag },
35 { "-r", ARG_HAVEARG_REQUIRED, 's', &rfile },
36 { "-o", ARG_HAVEARG_REQUIRED, 's', &ofile },
37 { "-c", ARG_HAVEARG_REQUIRED, 's', &conffile },
38 { "-f", ARG_HAVEARG_REQUIRED, 's', &ffile },
39 { "-t", ARG_HAVEARG_REQUIRED, 'h', &startaddr },
40 { "-e", ARG_HAVEARG_REQUIRED, 's', &entrysym },
41 { "-m", ARG_HAVEARG_NONE, 'f', &dispmod_flag },
42 { "--irx1", ARG_HAVEARG_NONE, 'f', &irx1_flag },
43 { "--rb", ARG_HAVEARG_NONE, 'f', &br_conv },
44 { "--relative-branch", ARG_HAVEARG_NONE, 'f', &br_conv },
45 { "--print-internal-config", ARG_HAVEARG_NONE, 'f', &print_config },
46 { "--allow-zero-text", ARG_HAVEARG_NONE, 'f', &allow_zero_text },
47 { NULL, 0, '\0', NULL },
48};
49static const Opttable stripopttable[] =
50{
51 { "-v", ARG_HAVEARG_NONE, 'f', &verbose },
52 { "-d", ARG_HAVEARG_NONE, 'f', &dumpflag },
53 { "-o", ARG_HAVEARG_REQUIRED, 's', &ofile },
54 { "-c", ARG_HAVEARG_REQUIRED, 's', &conffile },
55 { "-e", ARG_HAVEARG_REQUIRED, 's', &entrysym },
56 { "-m", ARG_HAVEARG_NONE, 'f', &dispmod_flag },
57 { "--irx1", ARG_HAVEARG_NONE, 'f', &irx1_flag },
58 { "--rb", ARG_HAVEARG_NONE, 'f', &br_conv },
59 { "--relative-branch", ARG_HAVEARG_NONE, 'f', &br_conv },
60 { "--print-internal-config", ARG_HAVEARG_NONE, 'f', &print_config },
61 { NULL, 0, '\0', NULL },
62};
63// clang-format on
64
65static void display_module_info(elf_file *elf);
66static void convert_relative_branch_an_section(elf_section *relsect);
67static void convert_relative_branch(elf_file *elf);
68static int check_zero_text_symbols(const char *source, const elf_file *elf);
69
70void usage(const char *myname)
71{
72 printf(
73 "IOP/EE relocatable object converter\n"
74 "%s\n"
75 "usage: %s [options] <elf_input_file>\n",
76 myname,
77 myname);
78 printf(" options:\n"
79 " -v\n"
80 " -m\n"
81 " --irx1\n"
82 " -o <elf_relocatable_nosymbol_output_file>\n"
83 " -r <elf_relocatable_output_file>\n"
84 " -e <entry_point_symbol>\n"
85 " --relative-branch or --rb\n"
86 " --allow-zero-text (skip check for .text symbols with value 0)\n");
87 if ( verbose )
88 {
89 printf(" -t <.text start address>\n"
90 " -f <elf_fixedaddress_output_file>\n"
91 " -d<hex_flag>\n"
92 " hex_flag bits:\n"
93 " bit0: dump section table\n"
94 " bit1: dump relocation record\n"
95 " bit2: dump symbol table\n"
96 " bit3: disassemble program code\n"
97 " bit4: dump .data/.rodata/.sdata... sections by byte\n"
98 " bit5: dump .data/.rodata/.sdata... sections by half word\n"
99 " bit6: dump .data/.rodata/.sdata... sections by word\n"
100 " bit7: dump .data/.rodata/.sdata... sections by word with relocation data\n"
101 " bit8: dump file layout\n"
102 " bit9: dump .mdebug section\n");
103 }
104 if ( verbose > 1 )
105 {
106 printf(" bit12: dump srx genaration table\n"
107 " -c <config_file>\n"
108 " --print-internal-config\n");
109 }
110}
111
112void stripusage(const char *myname)
113{
114 printf(
115 "%s\n"
116 "usage: %s [options] <elf_file>\n",
117 myname,
118 myname);
119 printf(" options:\n"
120 " -v\n"
121 " -m\n"
122 " -o <elf_relocatable_nosymbol_output_file>\n"
123 " --relative-branch or --rb\n");
124}
125
126int main(int argc, char **argv)
127{
128 Srx_gen_table *srxgen_1;
129 const char *defaultconf;
130 elf_file *elf;
131 const char *myname_1;
132 const char *myname_2;
133 const char *source;
134
135 myname_1 = strrchr(*argv, '/');
136 if ( !myname_1 )
137 {
138 myname_1 = strrchr(*argv, '\\');
139 }
140 if ( myname_1 )
141 {
142 myname_2 = myname_1 + 1;
143 }
144 else
145 {
146 myname_2 = *argv;
147 }
148 if ( (strncmp(myname_2, "ee", 2) != 0) && (strncmp(myname_2, "EE", 2) != 0) )
149 {
150 defaultconf = iop_defaultconf;
151 }
152 else
153 {
154 defaultconf = ee_defaultconf;
155 }
156 if ( strlen(*argv) > 5 && !strcmp(&(*argv)[strlen(*argv) - 5], "strip") )
157 {
158 int argca;
159
160 argca = analize_arguments(stripopttable, argc, argv);
161 if ( argca != 2 )
162 {
163 Srx_gen_table *srxgen_2;
164
165 srxgen_2 = read_conf(defaultconf, conffile, print_config);
166 if ( !srxgen_2 )
167 {
168 exit(1);
169 }
170 if ( (dumpflag & 0x1000) != 0 )
171 {
172 dump_srx_gen_table(srxgen_2);
173 exit(0);
174 }
175 }
176 if ( argca <= 1 )
177 {
178 stripusage(*argv);
179 exit(1);
180 }
181 if ( argca > 2 )
182 {
183 fprintf(stderr, "Too many input file\n");
184 stripusage(*argv);
185 exit(1);
186 }
187 if ( !ofile )
188 {
189 ofile = argv[1];
190 }
191 }
192 else
193 {
194 int argcb;
195
196 argcb = analize_arguments(opttable, argc, argv);
197 if ( argcb != 2 )
198 {
199 Srx_gen_table *srxgen_3;
200
201 srxgen_3 = read_conf(defaultconf, conffile, print_config);
202 if ( !srxgen_3 )
203 {
204 exit(1);
205 }
206 if ( (dumpflag & 0x1000) != 0 )
207 {
208 dump_srx_gen_table(srxgen_3);
209 exit(0);
210 }
211 }
212 if ( argcb <= 1 )
213 {
214 usage(*argv);
215 exit(1);
216 }
217 if ( argcb > 2 )
218 {
219 fprintf(stderr, "Too many input file\n");
220 usage(*argv);
221 exit(1);
222 }
223 }
224 source = argv[1];
225 elf = read_elf(source);
226 if ( !elf )
227 {
228 exit(1);
229 }
230 if (
231 ((elf->ehp->e_flags & EF_MIPS_MACH) == EF_MIPS_MACH_5900)
232 && ((elf->ehp->e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_3) )
233 {
234 srxgen_1 = read_conf(ee_defaultconf, conffile, print_config);
235 }
236 else
237 {
238 srxgen_1 = read_conf(iop_defaultconf, conffile, print_config);
239 }
240 if ( !srxgen_1 )
241 {
242 exit(1);
243 }
244 if ( (dumpflag & 0x1000) != 0 )
245 {
246 dump_srx_gen_table(srxgen_1);
247 exit(0);
248 }
249 if ( (dumpflag & 0xFFF) != 0 )
250 {
251 print_elf(elf, dumpflag & 0xFFF);
252 exit(0);
253 }
254 elf->optdata = (void *)srxgen_1;
255 switch ( elf->ehp->e_type )
256 {
257 case ET_REL:
258 if ( !allow_zero_text && check_zero_text_symbols(source, elf) )
259 {
260 exit(1);
261 }
262 if ( convert_rel2srx(elf, entrysym, (rfile || ofile || ffile) ? 1 : 0, irx1_flag) )
263 {
264 exit(1);
265 }
266 break;
267 case ET_SCE_IOPRELEXEC:
268 case ET_SCE_IOPRELEXEC2:
269 case ET_SCE_EERELEXEC2:
270 case ET_EXEC:
271 break;
272 default:
273 fprintf(stderr, "Error: '%s' is unsupport Type Elf file(type=%x)\n", source, elf->ehp->e_type);
274 exit(1);
275 break;
276 }
277 if ( dispmod_flag )
278 {
279 display_module_info(elf);
280 }
281 switch ( elf->ehp->e_type )
282 {
283 case ET_SCE_IOPRELEXEC:
284 case ET_SCE_IOPRELEXEC2:
285 case ET_SCE_EERELEXEC2:
286 if ( br_conv )
287 {
288 convert_relative_branch(elf);
289 }
290 if ( rfile )
291 {
292 if ( layout_srx_file(elf) )
293 {
294 exit(1);
295 }
296 write_elf(elf, rfile);
297 }
298 if ( ofile )
299 {
300 strip_elf(elf);
301 if ( layout_srx_file(elf) )
302 {
303 exit(1);
304 }
305 write_elf(elf, ofile);
306 }
307 break;
308 default:
309 if ( rfile || ofile )
310 {
311 fprintf(stderr, "Error: Cannot generate IRX/ERX file. '%s' file type is ET_EXEC.\n", source);
312 exit(1);
313 }
314 break;
315 }
316 if ( ffile || startaddr != (unsigned int)(-1) )
317 {
318 switch ( elf->ehp->e_type )
319 {
320 case ET_SCE_IOPRELEXEC:
321 case ET_SCE_IOPRELEXEC2:
322 case ET_SCE_EERELEXEC2:
323 elf->ehp->e_type = ET_EXEC;
324 fixlocation_elf(elf, startaddr);
325 break;
326 default:
327 break;
328 }
329 if ( ffile )
330 {
331 if ( layout_srx_file(elf) )
332 {
333 exit(1);
334 }
335 write_elf(elf, ffile);
336 }
337 }
338 return 0;
339}
340
341static void display_module_info(elf_file *elf)
342{
343 elf_section *modsect_1;
344 elf_section *modsect_2;
345
346 modsect_1 = search_section(elf, SHT_SCE_IOPMOD);
347 if ( modsect_1 )
348 {
349 const Elf32_IopMod *iopmodinfo;
350
351 iopmodinfo = (Elf32_IopMod *)modsect_1->data;
352 if ( iopmodinfo->moduleinfo != (Elf32_Word)(-1) )
353 {
354 printf(
355 "name:%s version:%d.%d\n",
356 iopmodinfo->modulename,
357 (uint8_t)(iopmodinfo->moduleversion >> 24),
358 (uint8_t)iopmodinfo->moduleversion);
359 }
360 }
361 modsect_2 = search_section(elf, SHT_SCE_EEMOD);
362 if ( modsect_2 )
363 {
364 const Elf32_EeMod *eemodinfo;
365
366 eemodinfo = (Elf32_EeMod *)modsect_2->data;
367 if ( eemodinfo->moduleinfo != (Elf32_Word)(-1) )
368 {
369 printf(
370 "name:%s version:%d.%d\n",
371 eemodinfo->modulename,
372 (uint8_t)(eemodinfo->moduleversion >> 24),
373 (uint8_t)eemodinfo->moduleversion);
374 }
375 }
376}
377
378static void convert_relative_branch_an_section(elf_section *relsect)
379{
380 elf_syment **symp;
381 elf_rel *rp;
382 int rmcount;
383 unsigned int entrise;
384 unsigned int i;
385
386 entrise = relsect->shr.sh_size / relsect->shr.sh_entsize;
387 rp = (elf_rel *)relsect->data;
388 symp = (elf_syment **)relsect->link->data;
389 rmcount = 0;
390 for ( i = 0; i < entrise; i += 1 )
391 {
392 unsigned int type;
393 uint8_t *daddr;
394
395 if ( rp->symptr && *symp != rp->symptr )
396 {
397 fprintf(stderr, "Internal error: Illegal relocation entry\n");
398 exit(1);
399 }
400 if (
401 relsect->info->shr.sh_addr > rp->rel.r_offset
402 || rp->rel.r_offset >= relsect->info->shr.sh_size + relsect->info->shr.sh_addr )
403 {
404 fprintf(
405 stderr,
406 "Panic !! relocation #%u offset=0x%x range out (section limit addr=0x%x-0x%x)\n",
407 i,
408 rp->rel.r_offset,
409 relsect->info->shr.sh_addr,
410 relsect->info->shr.sh_size + relsect->info->shr.sh_addr);
411 exit(1);
412 }
413 daddr = &relsect->info->data[rp->rel.r_offset - relsect->info->shr.sh_addr];
414 type = rp->type;
415 if ( type )
416 {
417 if ( type == R_MIPS_26 )
418 {
419 uint32_t raddr;
420 unsigned int data;
421
422 data = *(uint32_t *)daddr;
423 if ( rp->symptr && rp->symptr->bind != STB_LOCAL )
424 {
425 fprintf(stderr, "R_MIPS_26 Unexcepted bind\n");
426 exit(1);
427 }
428 raddr = rp->rel.r_offset + relsect->info->shr.sh_addr;
429 if (
430 !((((rp->rel.r_offset & 0xF0000000) | (4 * (data & 0x3FFFFFF))) - 4 - raddr) >> 18)
431 || (((rp->rel.r_offset & 0xF0000000) | (4 * (data & 0x3FFFFFF))) - 4 - raddr) >> 18 == 0x3FFF )
432 {
433 int jaddr;
434
435 jaddr = (uint16_t)((((rp->rel.r_offset & 0xF0000000) | (4 * (data & 0x3FFFFFF))) - 4 - raddr) >> 2);
436 if ( data >> 26 == 2 )
437 {
438 *(uint32_t *)daddr = jaddr | 0x10000000;
439 rp->type = R_MIPS_NONE;
440 rmcount += 1;
441 }
442 else if ( data >> 26 == 3 )
443 {
444 *(uint32_t *)daddr = jaddr | 0x4110000;
445 rp->type = R_MIPS_NONE;
446 rmcount += 1;
447 }
448 }
449 }
450 }
451 else
452 {
453 rmcount += 1;
454 }
455 rp += 1;
456 }
457 if ( rmcount > 0 && (entrise - rmcount) > 0 )
458 {
459 elf_rel *s;
460 elf_rel *d;
461 elf_rel *newtab;
462 unsigned int j;
463
464 newtab = (elf_rel *)calloc(entrise - rmcount, sizeof(elf_rel));
465 d = newtab;
466 s = (elf_rel *)relsect->data;
467 for ( j = 0; j < entrise; j += 1 )
468 {
469 if ( s->type )
470 {
471 memcpy(d, s, sizeof(elf_rel));
472 d += 1;
473 }
474 s += 1;
475 }
476 free(relsect->data);
477 relsect->data = (uint8_t *)newtab;
478 relsect->shr.sh_size = relsect->shr.sh_entsize * (entrise - rmcount);
479 }
480}
481
482static void convert_relative_branch(elf_file *elf)
483{
484 int i;
485
486 if ( elf->scp == NULL )
487 {
488 return;
489 }
490 for ( i = 1; i < elf->ehp->e_shnum; i += 1 )
491 {
492 if ( elf->scp[i]->shr.sh_type == SHT_REL )
493 {
494 convert_relative_branch_an_section(elf->scp[i]);
495 }
496 }
497}
498
499static int check_zero_text_symbols(const char *source, const elf_file *elf)
500{
501 int text_idx;
502 int i;
503 int found;
504
505 if ( elf->scp == NULL )
506 {
507 return 0;
508 }
509
510 /* Find the index of the .text section by name */
511 text_idx = -1;
512 for ( i = 1; i < elf->ehp->e_shnum; i += 1 )
513 {
514 if ( elf->scp[i]->name && strcmp(elf->scp[i]->name, ".text") == 0 )
515 {
516 text_idx = i;
517 break;
518 }
519 }
520 if ( text_idx < 0 )
521 {
522 return 0; /* No .text section — nothing to check */
523 }
524
525 found = 0;
526 for ( i = 1; i < elf->ehp->e_shnum; i += 1 )
527 {
528 elf_section *sect;
529 unsigned int nsyms;
530 unsigned int j;
531 elf_syment **syms;
532
533 sect = elf->scp[i];
534 if ( sect->shr.sh_type != SHT_SYMTAB || sect->data == NULL || sect->shr.sh_entsize == 0 )
535 {
536 continue;
537 }
538 nsyms = sect->shr.sh_size / sect->shr.sh_entsize;
539 syms = (elf_syment **)sect->data;
540 for ( j = 1; j < nsyms; j += 1 ) /* skip sym[0]: ELF null symbol */
541 {
542 elf_syment *sym;
543
544 sym = syms[j];
545 /* _start is the IOP module entry point called via the .iopmod
546 * header, never via a jal from within the module.
547 * _ftext is a linker label at the start of .text, also never
548 * the target of an intra-module jal. Both are safe at .text+0. */
549 if ( sym->name
550 && (strcmp(sym->name, "_start") == 0 || strcmp(sym->name, "_ftext") == 0) )
551 {
552 continue;
553 }
554 if ( (unsigned int)sym->sym.st_shndx == (unsigned int)text_idx
555 && (sym->type == STT_FUNC || sym->type == STT_NOTYPE)
556 && sym->sym.st_value == 0 )
557 {
558 fprintf(
559 stderr,
560 "ERROR: %s: symbol '%s' (sym#%u) is in .text with value 0"
561 " -- if this module has an export table, put exports.o"
562 " first in IOP_OBJS and move _retonly after"
563 " DECLARE_EXPORT_TABLE in exports.tab;"
564 " otherwise use --allow-zero-text.\n",
565 source,
566 (sym->name && sym->name[0]) ? sym->name : "<unnamed>",
567 j);
568 found = 1;
569 }
570 }
571 }
572 return found;
573}