@@ -30,180 +30,6 @@ fn is_rel_abs_arg(arg: &Argument) -> bool {
3030
3131fn is_offset_arg ( arg : & Argument ) -> bool { matches ! ( arg, Argument :: Offset ( _) ) }
3232
33- fn guess_data_type_from_load_store_inst_op ( inst_op : Opcode ) -> Option < DataType > {
34- match inst_op {
35- Opcode :: Lbz | Opcode :: Lbzu | Opcode :: Lbzux | Opcode :: Lbzx => Some ( DataType :: Int8 ) ,
36- Opcode :: Lhz | Opcode :: Lhzu | Opcode :: Lhzux | Opcode :: Lhzx => Some ( DataType :: Int16 ) ,
37- Opcode :: Lha | Opcode :: Lhau | Opcode :: Lhaux | Opcode :: Lhax => Some ( DataType :: Int16 ) ,
38- Opcode :: Lwz | Opcode :: Lwzu | Opcode :: Lwzux | Opcode :: Lwzx => Some ( DataType :: Int32 ) ,
39- Opcode :: Lfs | Opcode :: Lfsu | Opcode :: Lfsux | Opcode :: Lfsx => Some ( DataType :: Float ) ,
40- Opcode :: Lfd | Opcode :: Lfdu | Opcode :: Lfdux | Opcode :: Lfdx => Some ( DataType :: Double ) ,
41-
42- Opcode :: Stb | Opcode :: Stbu | Opcode :: Stbux | Opcode :: Stbx => Some ( DataType :: Int8 ) ,
43- Opcode :: Sth | Opcode :: Sthu | Opcode :: Sthux | Opcode :: Sthx => Some ( DataType :: Int16 ) ,
44- Opcode :: Stw | Opcode :: Stwu | Opcode :: Stwux | Opcode :: Stwx => Some ( DataType :: Int32 ) ,
45- Opcode :: Stfs | Opcode :: Stfsu | Opcode :: Stfsux | Opcode :: Stfsx => Some ( DataType :: Float ) ,
46- Opcode :: Stfd | Opcode :: Stfdu | Opcode :: Stfdux | Opcode :: Stfdx => Some ( DataType :: Double ) ,
47- _ => None ,
48- }
49- }
50-
51- // Given an instruction, determine if it could accessing data at the address in a register.
52- // If so, return the offset added to the register's address, the register containing that address,
53- // and (optionally) which destination register the address is being copied into.
54- fn get_offset_and_addr_gpr_for_possible_pool_reference (
55- opcode : Opcode ,
56- simplified : & ParsedIns ,
57- ) -> Option < ( i16 , GPR , Option < GPR > ) > {
58- let args = & simplified. args ;
59- if guess_data_type_from_load_store_inst_op ( opcode) . is_some ( ) {
60- match ( args[ 1 ] , args[ 2 ] ) {
61- ( Argument :: Offset ( offset) , Argument :: GPR ( addr_src_gpr) ) => {
62- // e.g. lwz. Immediate offset.
63- Some ( ( offset. 0 , addr_src_gpr, None ) )
64- }
65- ( Argument :: GPR ( addr_src_gpr) , Argument :: GPR ( _offset_gpr) ) => {
66- // e.g. lwzx. The offset is in a register and was likely calculated from an index.
67- // Treat the offset as being 0 in this case to show the first element of the array.
68- // It may be possible to show all elements by figuring out the stride of the array
69- // from the calculations performed on the index before it's put into offset_gpr, but
70- // this would be much more complicated, so it's not currently done.
71- Some ( ( 0 , addr_src_gpr, None ) )
72- }
73- _ => None ,
74- }
75- } else {
76- // If it's not a load/store instruction, there's two more possibilities we need to handle.
77- // 1. It could be a reference to @stringBase.
78- // 2. It could be moving the relocation address plus an offset into a different register to
79- // load from later.
80- // If either of these match, we also want to return the destination register that the
81- // address is being copied into so that we can detect any future references to that new
82- // register as well.
83- match ( opcode, args[ 0 ] , args[ 1 ] , args[ 2 ] ) {
84- (
85- Opcode :: Addi ,
86- Argument :: GPR ( addr_dst_gpr) ,
87- Argument :: GPR ( addr_src_gpr) ,
88- Argument :: Simm ( simm) ,
89- ) => Some ( ( simm. 0 , addr_src_gpr, Some ( addr_dst_gpr) ) ) ,
90- (
91- Opcode :: Or ,
92- Argument :: GPR ( addr_dst_gpr) ,
93- Argument :: GPR ( addr_src_gpr) ,
94- Argument :: None ,
95- ) => Some ( ( 0 , addr_src_gpr, Some ( addr_dst_gpr) ) ) , // `mr` or `mr.`
96- _ => None ,
97- }
98- }
99- }
100-
101- // We create a fake relocation for an instruction, vaguely simulating what the actual relocation
102- // might have looked like if it wasn't pooled. This is so minimal changes are needed to display
103- // pooled accesses vs non-pooled accesses. We set the relocation type to R_PPC_NONE to indicate that
104- // there isn't really a relocation here, as copying the pool relocation's type wouldn't make sense.
105- // Also, if this instruction is accessing the middle of a symbol instead of the start, we add an
106- // addend to indicate that.
107- fn make_fake_pool_reloc (
108- offset : i16 ,
109- cur_addr : u32 ,
110- pool_reloc : & ObjReloc ,
111- sections : & [ ObjSection ] ,
112- ) -> Option < ObjReloc > {
113- let offset_from_pool = pool_reloc. addend + offset as i64 ;
114- let target_address = pool_reloc. target . address . checked_add_signed ( offset_from_pool) ?;
115- let orig_section_index = pool_reloc. target . orig_section_index ?;
116- let section = sections. iter ( ) . find ( |s| s. orig_index == orig_section_index) ?;
117- let target_symbol = section
118- . symbols
119- . iter ( )
120- . find ( |s| s. size > 0 && ( s. address ..s. address + s. size ) . contains ( & target_address) ) ?;
121- let addend = ( target_address - target_symbol. address ) as i64 ;
122- Some ( ObjReloc {
123- flags : RelocationFlags :: Elf { r_type : elf:: R_PPC_NONE } ,
124- address : cur_addr as u64 ,
125- target : target_symbol. clone ( ) ,
126- addend,
127- } )
128- }
129-
130- // Searches through all instructions in a function, determining which registers have the addresses
131- // of pooled data relocations in them, finding which instructions load data from those addresses,
132- // and constructing a mapping of the address of that instruction to a "fake pool relocation" that
133- // simulates what that instruction's relocation would look like if data hadn't been pooled.
134- // Limitations: This method currently only goes through the instructions in a function in linear
135- // order, from start to finish. It does *not* follow any branches. This means that it could have
136- // false positives or false negatives in determining which relocation is currently loaded in which
137- // register at any given point in the function, as control flow is not respected.
138- // There are currently no known examples of this method producing inaccurate results in reality, but
139- // if examples are found, it may be possible to update this method to also follow all branches so
140- // that it produces more accurate results.
141- fn generate_fake_pool_reloc_for_addr_mapping (
142- address : u64 ,
143- code : & [ u8 ] ,
144- relocations : & [ ObjReloc ] ,
145- sections : & [ ObjSection ] ,
146- ) -> HashMap < u32 , ObjReloc > {
147- let mut active_pool_relocs = HashMap :: new ( ) ;
148- let mut pool_reloc_for_addr = HashMap :: new ( ) ;
149- for ( cur_addr, ins) in InsIter :: new ( code, address as u32 ) {
150- let simplified = ins. simplified ( ) ;
151- let reloc = relocations. iter ( ) . find ( |r| ( r. address as u32 & !3 ) == cur_addr) ;
152-
153- if let Some ( reloc) = reloc {
154- // This instruction has a real relocation, so it may be a pool load we want to keep
155- // track of.
156- let args = & simplified. args ;
157- match ( ins. op , args[ 0 ] , args[ 1 ] , args[ 2 ] ) {
158- (
159- Opcode :: Addi ,
160- Argument :: GPR ( addr_dst_gpr) ,
161- Argument :: GPR ( _addr_src_gpr) ,
162- Argument :: Simm ( _simm) ,
163- ) => {
164- active_pool_relocs. insert ( addr_dst_gpr. 0 , reloc. clone ( ) ) ; // `lis` + `addi`
165- }
166- (
167- Opcode :: Ori ,
168- Argument :: GPR ( addr_dst_gpr) ,
169- Argument :: GPR ( _addr_src_gpr) ,
170- Argument :: Uimm ( _uimm) ,
171- ) => {
172- active_pool_relocs. insert ( addr_dst_gpr. 0 , reloc. clone ( ) ) ; // `lis` + `ori`
173- }
174- _ => { }
175- }
176- } else if let Some ( ( offset, addr_src_gpr, addr_dst_gpr) ) =
177- get_offset_and_addr_gpr_for_possible_pool_reference ( ins. op , & simplified)
178- {
179- // This instruction doesn't have a real relocation, so it may be a reference to one of
180- // the already-loaded pools.
181- if let Some ( pool_reloc) = active_pool_relocs. get ( & addr_src_gpr. 0 ) {
182- if let Some ( fake_pool_reloc) =
183- make_fake_pool_reloc ( offset, cur_addr, pool_reloc, sections)
184- {
185- pool_reloc_for_addr. insert ( cur_addr, fake_pool_reloc) ;
186- }
187- if let Some ( addr_dst_gpr) = addr_dst_gpr {
188- // If the address of the pool relocation got copied into another register, we
189- // need to keep track of it in that register too as future instructions may
190- // reference the symbol indirectly via this new register, instead of the
191- // register the symbol's address was originally loaded into.
192- // For example, the start of the function might `lis` + `addi` the start of the
193- // ...data pool into r25, and then later the start of a loop will `addi` r25
194- // with the offset within the .data section of an array variable into r21.
195- // Then the body of the loop will `lwzx` one of the array elements from r21.
196- let mut new_reloc = pool_reloc. clone ( ) ;
197- new_reloc. addend += offset as i64 ;
198- active_pool_relocs. insert ( addr_dst_gpr. 0 , new_reloc) ;
199- }
200- }
201- }
202- }
203-
204- pool_reloc_for_addr
205- }
206-
20733pub struct ObjArchPpc {
20834 /// Exception info
20935 pub extab : Option < BTreeMap < usize , ExceptionInfo > > ,
@@ -552,3 +378,177 @@ fn make_symbol_ref(symbol: &Symbol) -> Result<ExtabSymbolRef> {
552378 let demangled_name = cwdemangle:: demangle ( & name, & cwdemangle:: DemangleOptions :: default ( ) ) ;
553379 Ok ( ExtabSymbolRef { original_index : symbol. index ( ) . 0 , name, demangled_name } )
554380}
381+
382+ fn guess_data_type_from_load_store_inst_op ( inst_op : Opcode ) -> Option < DataType > {
383+ match inst_op {
384+ Opcode :: Lbz | Opcode :: Lbzu | Opcode :: Lbzux | Opcode :: Lbzx => Some ( DataType :: Int8 ) ,
385+ Opcode :: Lhz | Opcode :: Lhzu | Opcode :: Lhzux | Opcode :: Lhzx => Some ( DataType :: Int16 ) ,
386+ Opcode :: Lha | Opcode :: Lhau | Opcode :: Lhaux | Opcode :: Lhax => Some ( DataType :: Int16 ) ,
387+ Opcode :: Lwz | Opcode :: Lwzu | Opcode :: Lwzux | Opcode :: Lwzx => Some ( DataType :: Int32 ) ,
388+ Opcode :: Lfs | Opcode :: Lfsu | Opcode :: Lfsux | Opcode :: Lfsx => Some ( DataType :: Float ) ,
389+ Opcode :: Lfd | Opcode :: Lfdu | Opcode :: Lfdux | Opcode :: Lfdx => Some ( DataType :: Double ) ,
390+
391+ Opcode :: Stb | Opcode :: Stbu | Opcode :: Stbux | Opcode :: Stbx => Some ( DataType :: Int8 ) ,
392+ Opcode :: Sth | Opcode :: Sthu | Opcode :: Sthux | Opcode :: Sthx => Some ( DataType :: Int16 ) ,
393+ Opcode :: Stw | Opcode :: Stwu | Opcode :: Stwux | Opcode :: Stwx => Some ( DataType :: Int32 ) ,
394+ Opcode :: Stfs | Opcode :: Stfsu | Opcode :: Stfsux | Opcode :: Stfsx => Some ( DataType :: Float ) ,
395+ Opcode :: Stfd | Opcode :: Stfdu | Opcode :: Stfdux | Opcode :: Stfdx => Some ( DataType :: Double ) ,
396+ _ => None ,
397+ }
398+ }
399+
400+ // Given an instruction, determine if it could accessing data at the address in a register.
401+ // If so, return the offset added to the register's address, the register containing that address,
402+ // and (optionally) which destination register the address is being copied into.
403+ fn get_offset_and_addr_gpr_for_possible_pool_reference (
404+ opcode : Opcode ,
405+ simplified : & ParsedIns ,
406+ ) -> Option < ( i16 , GPR , Option < GPR > ) > {
407+ let args = & simplified. args ;
408+ if guess_data_type_from_load_store_inst_op ( opcode) . is_some ( ) {
409+ match ( args[ 1 ] , args[ 2 ] ) {
410+ ( Argument :: Offset ( offset) , Argument :: GPR ( addr_src_gpr) ) => {
411+ // e.g. lwz. Immediate offset.
412+ Some ( ( offset. 0 , addr_src_gpr, None ) )
413+ }
414+ ( Argument :: GPR ( addr_src_gpr) , Argument :: GPR ( _offset_gpr) ) => {
415+ // e.g. lwzx. The offset is in a register and was likely calculated from an index.
416+ // Treat the offset as being 0 in this case to show the first element of the array.
417+ // It may be possible to show all elements by figuring out the stride of the array
418+ // from the calculations performed on the index before it's put into offset_gpr, but
419+ // this would be much more complicated, so it's not currently done.
420+ Some ( ( 0 , addr_src_gpr, None ) )
421+ }
422+ _ => None ,
423+ }
424+ } else {
425+ // If it's not a load/store instruction, there's two more possibilities we need to handle.
426+ // 1. It could be a reference to @stringBase.
427+ // 2. It could be moving the relocation address plus an offset into a different register to
428+ // load from later.
429+ // If either of these match, we also want to return the destination register that the
430+ // address is being copied into so that we can detect any future references to that new
431+ // register as well.
432+ match ( opcode, args[ 0 ] , args[ 1 ] , args[ 2 ] ) {
433+ (
434+ Opcode :: Addi ,
435+ Argument :: GPR ( addr_dst_gpr) ,
436+ Argument :: GPR ( addr_src_gpr) ,
437+ Argument :: Simm ( simm) ,
438+ ) => Some ( ( simm. 0 , addr_src_gpr, Some ( addr_dst_gpr) ) ) ,
439+ (
440+ Opcode :: Or ,
441+ Argument :: GPR ( addr_dst_gpr) ,
442+ Argument :: GPR ( addr_src_gpr) ,
443+ Argument :: None ,
444+ ) => Some ( ( 0 , addr_src_gpr, Some ( addr_dst_gpr) ) ) , // `mr` or `mr.`
445+ _ => None ,
446+ }
447+ }
448+ }
449+
450+ // We create a fake relocation for an instruction, vaguely simulating what the actual relocation
451+ // might have looked like if it wasn't pooled. This is so minimal changes are needed to display
452+ // pooled accesses vs non-pooled accesses. We set the relocation type to R_PPC_NONE to indicate that
453+ // there isn't really a relocation here, as copying the pool relocation's type wouldn't make sense.
454+ // Also, if this instruction is accessing the middle of a symbol instead of the start, we add an
455+ // addend to indicate that.
456+ fn make_fake_pool_reloc (
457+ offset : i16 ,
458+ cur_addr : u32 ,
459+ pool_reloc : & ObjReloc ,
460+ sections : & [ ObjSection ] ,
461+ ) -> Option < ObjReloc > {
462+ let offset_from_pool = pool_reloc. addend + offset as i64 ;
463+ let target_address = pool_reloc. target . address . checked_add_signed ( offset_from_pool) ?;
464+ let orig_section_index = pool_reloc. target . orig_section_index ?;
465+ let section = sections. iter ( ) . find ( |s| s. orig_index == orig_section_index) ?;
466+ let target_symbol = section
467+ . symbols
468+ . iter ( )
469+ . find ( |s| s. size > 0 && ( s. address ..s. address + s. size ) . contains ( & target_address) ) ?;
470+ let addend = ( target_address - target_symbol. address ) as i64 ;
471+ Some ( ObjReloc {
472+ flags : RelocationFlags :: Elf { r_type : elf:: R_PPC_NONE } ,
473+ address : cur_addr as u64 ,
474+ target : target_symbol. clone ( ) ,
475+ addend,
476+ } )
477+ }
478+
479+ // Searches through all instructions in a function, determining which registers have the addresses
480+ // of pooled data relocations in them, finding which instructions load data from those addresses,
481+ // and constructing a mapping of the address of that instruction to a "fake pool relocation" that
482+ // simulates what that instruction's relocation would look like if data hadn't been pooled.
483+ // Limitations: This method currently only goes through the instructions in a function in linear
484+ // order, from start to finish. It does *not* follow any branches. This means that it could have
485+ // false positives or false negatives in determining which relocation is currently loaded in which
486+ // register at any given point in the function, as control flow is not respected.
487+ // There are currently no known examples of this method producing inaccurate results in reality, but
488+ // if examples are found, it may be possible to update this method to also follow all branches so
489+ // that it produces more accurate results.
490+ fn generate_fake_pool_reloc_for_addr_mapping (
491+ address : u64 ,
492+ code : & [ u8 ] ,
493+ relocations : & [ ObjReloc ] ,
494+ sections : & [ ObjSection ] ,
495+ ) -> HashMap < u32 , ObjReloc > {
496+ let mut active_pool_relocs = HashMap :: new ( ) ;
497+ let mut pool_reloc_for_addr = HashMap :: new ( ) ;
498+ for ( cur_addr, ins) in InsIter :: new ( code, address as u32 ) {
499+ let simplified = ins. simplified ( ) ;
500+ let reloc = relocations. iter ( ) . find ( |r| ( r. address as u32 & !3 ) == cur_addr) ;
501+
502+ if let Some ( reloc) = reloc {
503+ // This instruction has a real relocation, so it may be a pool load we want to keep
504+ // track of.
505+ let args = & simplified. args ;
506+ match ( ins. op , args[ 0 ] , args[ 1 ] , args[ 2 ] ) {
507+ (
508+ Opcode :: Addi ,
509+ Argument :: GPR ( addr_dst_gpr) ,
510+ Argument :: GPR ( _addr_src_gpr) ,
511+ Argument :: Simm ( _simm) ,
512+ ) => {
513+ active_pool_relocs. insert ( addr_dst_gpr. 0 , reloc. clone ( ) ) ; // `lis` + `addi`
514+ }
515+ (
516+ Opcode :: Ori ,
517+ Argument :: GPR ( addr_dst_gpr) ,
518+ Argument :: GPR ( _addr_src_gpr) ,
519+ Argument :: Uimm ( _uimm) ,
520+ ) => {
521+ active_pool_relocs. insert ( addr_dst_gpr. 0 , reloc. clone ( ) ) ; // `lis` + `ori`
522+ }
523+ _ => { }
524+ }
525+ } else if let Some ( ( offset, addr_src_gpr, addr_dst_gpr) ) =
526+ get_offset_and_addr_gpr_for_possible_pool_reference ( ins. op , & simplified)
527+ {
528+ // This instruction doesn't have a real relocation, so it may be a reference to one of
529+ // the already-loaded pools.
530+ if let Some ( pool_reloc) = active_pool_relocs. get ( & addr_src_gpr. 0 ) {
531+ if let Some ( fake_pool_reloc) =
532+ make_fake_pool_reloc ( offset, cur_addr, pool_reloc, sections)
533+ {
534+ pool_reloc_for_addr. insert ( cur_addr, fake_pool_reloc) ;
535+ }
536+ if let Some ( addr_dst_gpr) = addr_dst_gpr {
537+ // If the address of the pool relocation got copied into another register, we
538+ // need to keep track of it in that register too as future instructions may
539+ // reference the symbol indirectly via this new register, instead of the
540+ // register the symbol's address was originally loaded into.
541+ // For example, the start of the function might `lis` + `addi` the start of the
542+ // ...data pool into r25, and then later the start of a loop will `addi` r25
543+ // with the offset within the .data section of an array variable into r21.
544+ // Then the body of the loop will `lwzx` one of the array elements from r21.
545+ let mut new_reloc = pool_reloc. clone ( ) ;
546+ new_reloc. addend += offset as i64 ;
547+ active_pool_relocs. insert ( addr_dst_gpr. 0 , new_reloc) ;
548+ }
549+ }
550+ }
551+ }
552+
553+ pool_reloc_for_addr
554+ }
0 commit comments