@@ -30,180 +30,6 @@ fn is_rel_abs_arg(arg: &Argument) -> bool {
30
30
31
31
fn is_offset_arg ( arg : & Argument ) -> bool { matches ! ( arg, Argument :: Offset ( _) ) }
32
32
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
-
207
33
pub struct ObjArchPpc {
208
34
/// Exception info
209
35
pub extab : Option < BTreeMap < usize , ExceptionInfo > > ,
@@ -552,3 +378,177 @@ fn make_symbol_ref(symbol: &Symbol) -> Result<ExtabSymbolRef> {
552
378
let demangled_name = cwdemangle:: demangle ( & name, & cwdemangle:: DemangleOptions :: default ( ) ) ;
553
379
Ok ( ExtabSymbolRef { original_index : symbol. index ( ) . 0 , name, demangled_name } )
554
380
}
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