@@ -533,9 +533,13 @@ fn make_fake_pool_reloc(offset: i16, cur_addr: u32, pool_reloc: &ObjReloc) -> Op
533533// to the queue. Conditional branches will traverse both the path where the branch is taken and the
534534// one where it's not. Unconditional branches only follow the branch, ignoring any code immediately
535535// after the branch instruction.
536- // Limitations: This method cannot follow jump tables. This is because the jump table is located in
537- // the .data section, but ObjArch.process_code only has access to the .text section. This means that
538- // it will miss most of the cases in a switch statement that uses a jump table.
536+ // Limitations: This method cannot read jump tables. This is because the jump tables are located in
537+ // the .data section, but ObjArch.process_code only has access to the .text section. In order to
538+ // work around this limitation and avoid completely missing most code inside switch statements that
539+ // use jump tables, we instead guess that any parts of a function we missed were switch cases, and
540+ // traverse them as if the last `bctr` before that address had branched there. This should be fairly
541+ // accurate in practice - in testing the only instructions it seems to miss are double branches that
542+ // the compiler generates in error which can never be reached during normal execution anyway.
539543fn generate_fake_pool_reloc_for_addr_mapping (
540544 func_address : u64 ,
541545 code : & [ u8 ] ,
@@ -545,6 +549,7 @@ fn generate_fake_pool_reloc_for_addr_mapping(
545549 let mut pool_reloc_for_addr = HashMap :: new ( ) ;
546550 let mut ins_iters_with_gpr_state =
547551 vec ! [ ( InsIter :: new( code, func_address as u32 ) , HashMap :: new( ) ) ] ;
552+ let mut gpr_state_at_bctr = BTreeMap :: new ( ) ;
548553 while let Some ( ( ins_iter, mut gpr_pool_relocs) ) = ins_iters_with_gpr_state. pop ( ) {
549554 for ( cur_addr, ins) in ins_iter {
550555 if visited_ins_addrs. contains ( & cur_addr) {
@@ -554,8 +559,8 @@ fn generate_fake_pool_reloc_for_addr_mapping(
554559 visited_ins_addrs. insert ( cur_addr) ;
555560
556561 let simplified = ins. simplified ( ) ;
557- let reloc = relocations. iter ( ) . find ( |r| ( r. address as u32 & !3 ) == cur_addr) ;
558562
563+ // First handle traversing the function's control flow.
559564 let mut branch_dest = None ;
560565 for arg in simplified. args_iter ( ) {
561566 if let Argument :: BranchDest ( dest) = arg {
@@ -596,7 +601,19 @@ fn generate_fake_pool_reloc_for_addr_mapping(
596601 }
597602 }
598603 }
604+ match ins. op {
605+ Opcode :: Bcctr => {
606+ if simplified. mnemonic == "bctr" {
607+ // Unconditional branch to count register.
608+ // Likely a jump table.
609+ gpr_state_at_bctr. insert ( cur_addr, gpr_pool_relocs. clone ( ) ) ;
610+ }
611+ }
612+ _ => { }
613+ }
599614
615+ // Then handle keeping track of which GPR contains which pool relocation.
616+ let reloc = relocations. iter ( ) . find ( |r| ( r. address as u32 & !3 ) == cur_addr) ;
600617 if let Some ( reloc) = reloc {
601618 // This instruction has a real relocation, so it may be a pool load we want to keep
602619 // track of.
@@ -667,6 +684,32 @@ fn generate_fake_pool_reloc_for_addr_mapping(
667684 clear_overwritten_gprs ( ins, & mut gpr_pool_relocs) ;
668685 }
669686 }
687+
688+ // Finally, if we're about to finish the outer loop and don't have any more control flow to
689+ // follow, we check if there are any instruction addresses in this function that we missed.
690+ // If so, and if there were any `bctr` instructions before those points in this function,
691+ // then we try to traverse those missing spots as switch cases.
692+ if ins_iters_with_gpr_state. is_empty ( ) {
693+ let unseen_addrs = ( func_address as u32 ..func_address as u32 + code. len ( ) as u32 )
694+ . step_by ( 4 )
695+ . filter ( |addr| !visited_ins_addrs. contains ( & addr) ) ;
696+ for unseen_addr in unseen_addrs {
697+ let prev_bctr_gpr_state = gpr_state_at_bctr
698+ . iter ( )
699+ . filter ( |( & addr, _) | addr < unseen_addr)
700+ . min_by_key ( |( & addr, _) | addr)
701+ . and_then ( |( _, gpr_state) | Some ( gpr_state) ) ;
702+ if let Some ( gpr_pool_relocs) = prev_bctr_gpr_state {
703+ let dest_offset_into_func = unseen_addr - func_address as u32 ;
704+ let dest_code_slice = & code[ dest_offset_into_func as usize ..] ;
705+ ins_iters_with_gpr_state. push ( (
706+ InsIter :: new ( dest_code_slice, unseen_addr) ,
707+ gpr_pool_relocs. clone ( ) ,
708+ ) ) ;
709+ break ;
710+ }
711+ }
712+ }
670713 }
671714
672715 pool_reloc_for_addr
0 commit comments