PS2SDK
PS2 Homebrew Libraries
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 
17 static const char *conffile = NULL;
18 static const char *ofile = NULL;
19 static const char *rfile = NULL;
20 static const char *ffile = NULL;
21 static unsigned int startaddr;
22 static const char *entrysym = NULL;
23 static unsigned int verbose = 0;
24 static unsigned int dumpflag = 0;
25 static unsigned int dispmod_flag = 0;
26 static int irx1_flag = 0;
27 static int br_conv = 0;
28 static int print_config = 0;
29 static int allow_zero_text = 0;
30 // clang-format off
31 static 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 };
49 static 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 
65 static void display_module_info(elf_file *elf);
66 static void convert_relative_branch_an_section(elf_section *relsect);
67 static void convert_relative_branch(elf_file *elf);
68 static int check_zero_text_symbols(const char *source, const elf_file *elf);
69 
70 void 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 
112 void 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 
126 int 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 
341 static 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 
378 static 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 
482 static 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 
499 static 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 }
_Elf32_EeMod
Definition: srxfixup_internal.h:104
elf_rel
Definition: loadcore.c:71
_elffile
Definition: srxfixup_internal.h:285
_elf_section
Definition: srxfixup_internal.h:253
stdio.h
_Elf32_IopMod
Definition: srxfixup_internal.h:93
_opttable
Definition: srxfixup_internal.h:367
_srx_gen_table
Definition: srxfixup_internal.h:348
stdlib.h
_syment
Definition: srxfixup_internal.h:263