@@ -103,6 +103,7 @@ struct Insn {
103103 uint32_t jtbl_addr;
104104 uint32_t num_cases;
105105 rabbitizer::Registers::Cpu::GprO32 index_reg;
106+ bool jtbl_is_pic;
106107
107108 // graph
108109 vector<Edge> successors;
@@ -126,6 +127,7 @@ struct Insn {
126127 this ->jtbl_addr = 0 ;
127128 this ->num_cases = 0 ;
128129 this ->index_reg = rabbitizer::Registers::Cpu::GprO32::GPR_O32_zero;
130+ this ->jtbl_is_pic = false ;
129131
130132 this ->b_liveout = 0 ;
131133 this ->b_livein = 0 ;
@@ -531,6 +533,27 @@ rabbitizer::Registers::Cpu::GprO32 get_dest_reg(const Insn& insn) {
531533 return rabbitizer::Registers::Cpu::GprO32::GPR_O32_zero;
532534}
533535
536+ uint32_t jump_table_target (Insn& insn, uint32_t case_index) {
537+ assert (insn.jtbl_addr != 0 );
538+
539+ uint32_t case_addr = insn.jtbl_addr + case_index * 4 ;
540+ uint32_t target_addr;
541+
542+ if (rodata_vaddr <= case_addr && case_addr < rodata_vaddr + rodata_section_len) {
543+ target_addr = read_u32_be (rodata_section + (case_addr - rodata_vaddr));
544+ } else if (srdata_vaddr <= case_addr && case_addr < srdata_vaddr + srdata_section_len) {
545+ target_addr = read_u32_be (srdata_section + (case_addr - srdata_vaddr));
546+ } else {
547+ assert (0 && " jump table not in .rodata or .srdata" );
548+ }
549+
550+ if (insn.jtbl_is_pic ) {
551+ target_addr += gp_value;
552+ }
553+
554+ return target_addr;
555+ }
556+
534557// try to find a matching LUI for a given register
535558void link_with_lui (int offset, rabbitizer::Registers::Cpu::GprO32 reg, int mem_imm) {
536559#define MAX_LOOKBACK 128
@@ -852,6 +875,7 @@ void pass1(void) {
852875 insn.jtbl_addr = jtbl_addr;
853876 insn.num_cases = num_cases;
854877 insn.index_reg = insns[addu_index - 1 ].instruction .GetO32_rt ();
878+ insn.jtbl_is_pic = is_pic;
855879 insns[lw].patchInstruction (rabbitizer::InstrId::UniqueId::cpu_nop);
856880
857881 insns[addu_index].patchInstruction (rabbitizer::InstrId::UniqueId::cpu_nop);
@@ -862,19 +886,8 @@ void pass1(void) {
862886 insns[addu_index - 2 ].patchInstruction (rabbitizer::InstrId::UniqueId::cpu_nop);
863887 }
864888
865- if (jtbl_addr < rodata_vaddr ||
866- jtbl_addr + num_cases * sizeof (uint32_t ) > rodata_vaddr + rodata_section_len) {
867- fprintf (stderr, " jump table outside rodata\n " );
868- exit (EXIT_FAILURE);
869- }
870-
871889 for (uint32_t case_index = 0 ; case_index < num_cases; case_index++) {
872- uint32_t target_addr = read_u32_be (rodata_section + (jtbl_addr - rodata_vaddr) +
873- case_index * sizeof (uint32_t ));
874-
875- target_addr += gp_value;
876- // printf("%08X\n", target_addr);
877- label_addresses.insert (target_addr);
890+ label_addresses.insert (jump_table_target (insn, case_index));
878891 }
879892 }
880893 skip:;
@@ -1232,13 +1245,8 @@ void pass3(void) {
12321245 add_edge (i, i + 1 );
12331246
12341247 if (insn.jtbl_addr != 0 ) {
1235- uint32_t jtbl_pos = insn.jtbl_addr - rodata_vaddr;
1236-
1237- assert (jtbl_pos < rodata_section_len &&
1238- jtbl_pos + insn.num_cases * sizeof (uint32_t ) <= rodata_section_len);
1239-
12401248 for (uint32_t j = 0 ; j < insn.num_cases ; j++) {
1241- uint32_t dest_addr = read_u32_be (rodata_section + jtbl_pos + j * sizeof ( uint32_t )) + gp_value ;
1249+ uint32_t dest_addr = jump_table_target (insn, j) ;
12421250
12431251 add_edge (i + 1 , addr_to_i (dest_addr));
12441252 }
@@ -2756,43 +2764,17 @@ void dump_instr(int i) {
27562764 break ;
27572765
27582766 case rabbitizer::InstrId::UniqueId::cpu_jr:
2759- // TODO: understand why the switch version fails, and why only it needs the nop
27602767 if (insn.jtbl_addr != 0 ) {
2761- uint32_t jtbl_pos = insn.jtbl_addr - rodata_vaddr;
2762-
2763- assert (jtbl_pos < rodata_section_len &&
2764- jtbl_pos + insn.num_cases * sizeof (uint32_t ) <= rodata_section_len);
2765- #if 1
2766- printf (" ;static void *const Lswitch%x[] = {\n " , insn.jtbl_addr );
2767-
2768- for (uint32_t case_index = 0 ; case_index < insn.num_cases ; case_index++) {
2769- uint32_t dest_addr =
2770- read_u32_be (rodata_section + jtbl_pos + case_index * sizeof (uint32_t )) + gp_value;
2771- printf (" &&L%x,\n " , dest_addr);
2772- label_addresses.insert (dest_addr);
2773- }
2774-
2775- printf (" };\n " );
2776- printf (" dest = Lswitch%x[(uint32_t)%s];\n " , insn.jtbl_addr , r ((int )insn.index_reg ));
2768+ printf (" jtbl_index = %s;\n " , r ((int )insn.index_reg ));
27772769 dump_instr (i + 1 );
2778- printf (" goto *dest;\n " );
2779- #else
2780- // This block produces a switch instead of an array of labels.
2781- // It is not being used because currently it is a bit bugged.
2782- // It has been keep as a reference and with the main intention to fix it
2783-
2784- assert(insns[i + 1].id == MIPS_INS_NOP);
2785- printf("switch ((uint32_t)%s) {\n", r(insn.index_reg));
2770+ printf (" switch (jtbl_index) {\n " );
27862771
27872772 for (uint32_t case_index = 0 ; case_index < insn.num_cases ; case_index++) {
2788- uint32_t dest_addr =
2789- read_u32_be(rodata_section + jtbl_pos + case_index * sizeof(uint32_t)) + gp_value;
2790- printf("case %u: goto L%x;\n", case_index, dest_addr);
2791- label_addresses.insert(dest_addr);
2773+ printf (" case %u: goto L%x;\n " , case_index, jump_table_target (insn, case_index));
27922774 }
27932775
2776+ printf (" default: __builtin_unreachable();\n " );
27942777 printf (" }\n " );
2795- #endif
27962778 } else {
27972779 if (insn.instruction .GetO32_rs () != rabbitizer::Registers::Cpu::GprO32::GPR_O32_ra) {
27982780 printf (" UNSUPPORTED JR %s (no jumptable available)\n " , r ((int )insn.instruction .GetO32_rs ()));
@@ -3459,7 +3441,7 @@ void dump_c(void) {
34593441 printf (" struct ReturnValue tempret;\n " );
34603442 printf (" double tempf64;\n " );
34613443 printf (" uint32_t fp_dest;\n " );
3462- printf (" void *dest ;\n " );
3444+ printf (" uint32_t jtbl_index ;\n " );
34633445
34643446 if (!f.v0_in ) {
34653447 printf (" uint64_t v0 = 0;\n " );
0 commit comments