Skip to content

Commit 862b56d

Browse files
committed
Change jump tables to use switch instead of array
1 parent acd7e82 commit 862b56d

File tree

1 file changed

+31
-49
lines changed

1 file changed

+31
-49
lines changed

recomp.cpp

Lines changed: 31 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -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
535558
void 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

Comments
 (0)