1use alloc::{
2 boxed::Box,
3 collections::{BTreeMap, BTreeSet},
4 string::{String, ToString},
5 vec,
6 vec::Vec,
7};
8
9use anyhow::{Result, anyhow, bail, ensure};
10use cwextab::{ExceptionTableData, decode_extab};
11use flagset::Flags;
12use object::{Object as _, ObjectSection as _, ObjectSymbol as _, elf, pe};
13
14use crate::{
15 arch::{Arch, DataType, RelocationOverride, RelocationOverrideTarget},
16 diff::{
17 DiffObjConfig,
18 data::resolve_relocation,
19 display::{ContextItem, HoverItem, HoverItemColor, InstructionPart, SymbolNavigationKind},
20 },
21 obj::{
22 FlowAnalysisResult, InstructionRef, Object, Relocation, RelocationFlags,
23 ResolvedInstructionRef, ResolvedRelocation, Section, Symbol, SymbolFlag, SymbolFlagSet,
24 SymbolKind,
25 },
26};
27
28mod flow_analysis;
29
30fn is_relative_arg(arg: &powerpc::Argument) -> bool {
32 matches!(
33 arg,
34 powerpc::Argument::Simm(_)
35 | powerpc::Argument::Offset(_)
36 | powerpc::Argument::BranchDest(_)
37 )
38}
39
40fn is_rel_abs_arg(arg: &powerpc::Argument) -> bool {
42 matches!(
43 arg,
44 powerpc::Argument::Uimm(_) | powerpc::Argument::Simm(_) | powerpc::Argument::Offset(_)
45 )
46}
47
48fn is_offset_arg(arg: &powerpc::Argument) -> bool { matches!(arg, powerpc::Argument::Offset(_)) }
49
50#[derive(Debug)]
51pub struct ArchPpc {
52 pub extensions: powerpc::Extensions,
53 pub extab: Option<BTreeMap<usize, ExceptionInfo>>,
55}
56
57impl ArchPpc {
58 pub fn new(file: &object::File) -> Result<Self> {
59 let extensions = match file.flags() {
60 object::FileFlags::Coff { .. } => powerpc::Extensions::xenon(),
61 object::FileFlags::Elf { e_flags, .. }
62 if (e_flags & elf::EF_PPC_EMB) == elf::EF_PPC_EMB =>
63 {
64 powerpc::Extensions::gekko_broadway()
65 }
66 _ => {
67 if file.is_64() {
68 powerpc::Extension::Ppc64 | powerpc::Extension::AltiVec
69 } else {
70 powerpc::Extensions::gekko_broadway()
73 }
74 }
75 };
76 let extab = decode_exception_info(file)?;
77 Ok(Self { extensions, extab })
78 }
79
80 fn parse_ins_ref(&self, resolved: ResolvedInstructionRef) -> Result<powerpc::Ins> {
81 let mut code = u32::from_be_bytes(resolved.code.try_into()?);
82 if let Some(reloc) = resolved.relocation {
83 code = zero_reloc(code, reloc.relocation);
84 }
85 let op = powerpc::Opcode::from(resolved.ins_ref.opcode);
86 Ok(powerpc::Ins { code, op })
87 }
88
89 fn find_reloc_arg(
90 &self,
91 ins: &powerpc::ParsedIns,
92 resolved: Option<ResolvedRelocation>,
93 ) -> Option<usize> {
94 match resolved?.relocation.flags {
95 RelocationFlags::Elf(elf::R_PPC_EMB_SDA21) => Some(1),
96 RelocationFlags::Elf(elf::R_PPC_REL24 | elf::R_PPC_REL14)
97 | RelocationFlags::Coff(pe::IMAGE_REL_PPC_REL24 | pe::IMAGE_REL_PPC_REL14) => {
98 ins.args.iter().rposition(is_relative_arg)
99 }
100 RelocationFlags::Elf(
101 elf::R_PPC_ADDR16_HI | elf::R_PPC_ADDR16_HA | elf::R_PPC_ADDR16_LO,
102 )
103 | RelocationFlags::Coff(pe::IMAGE_REL_PPC_REFHI | pe::IMAGE_REL_PPC_REFLO) => {
104 ins.args.iter().rposition(is_rel_abs_arg)
105 }
106 RelocationFlags::Elf(elf::R_PPC64_TOC16) => ins.args.iter().rposition(is_offset_arg),
107 _ => None,
108 }
109 }
110}
111
112impl Arch for ArchPpc {
113 fn scan_instructions_internal(
114 &self,
115 address: u64,
116 code: &[u8],
117 _section_index: usize,
118 _relocations: &[Relocation],
119 _diff_config: &DiffObjConfig,
120 ) -> Result<Vec<InstructionRef>> {
121 ensure!(code.len() & 3 == 0, "Code length must be a multiple of 4");
122 let ins_count = code.len() / 4;
123 let mut insts = Vec::<InstructionRef>::with_capacity(ins_count);
124 for (cur_addr, ins) in powerpc::InsIter::new(code, address as u32, self.extensions) {
125 insts.push(InstructionRef {
126 address: cur_addr as u64,
127 size: 4,
128 opcode: u16::from(ins.op),
129 branch_dest: ins.branch_dest(cur_addr).map(u64::from),
130 });
131 }
132 Ok(insts)
133 }
134
135 fn display_instruction(
136 &self,
137 resolved: ResolvedInstructionRef,
138 _diff_config: &DiffObjConfig,
139 cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
140 ) -> Result<()> {
141 let ins = self.parse_ins_ref(resolved)?.simplified();
142
143 cb(InstructionPart::opcode(ins.mnemonic, resolved.ins_ref.opcode))?;
144
145 let reloc_arg = self.find_reloc_arg(&ins, resolved.relocation);
146
147 let mut writing_offset = false;
148 for (idx, arg) in ins.args_iter().enumerate() {
149 if idx > 0 && !writing_offset {
150 cb(InstructionPart::separator())?;
151 }
152
153 if reloc_arg == Some(idx) {
154 let reloc = resolved.relocation.unwrap();
155 display_reloc(reloc, cb)?;
156 if matches!(reloc.relocation.flags, RelocationFlags::Elf(elf::R_PPC_EMB_SDA21))
158 && matches!(ins.args.get(idx + 1), Some(powerpc::Argument::GPR(powerpc::GPR(0))))
160 {
161 break;
162 }
163 } else {
164 match arg {
165 powerpc::Argument::Simm(simm) => cb(InstructionPart::signed(simm.0)),
166 powerpc::Argument::Uimm(uimm) => cb(InstructionPart::unsigned(uimm.0)),
167 powerpc::Argument::Offset(offset) => cb(InstructionPart::signed(offset.0)),
168 powerpc::Argument::BranchDest(dest) => cb(InstructionPart::branch_dest(
169 (resolved.ins_ref.address as u32).wrapping_add_signed(dest.0),
170 )),
171 _ => cb(InstructionPart::opaque(arg.to_string())),
172 }?;
173 }
174
175 if writing_offset {
176 cb(InstructionPart::basic(")"))?;
177 writing_offset = false;
178 }
179 if is_offset_arg(arg) {
180 cb(InstructionPart::basic("("))?;
181 writing_offset = true;
182 }
183 }
184
185 Ok(())
186 }
187
188 fn generate_pooled_relocations(
190 &self,
191 address: u64,
192 code: &[u8],
193 relocations: &[Relocation],
194 symbols: &[Symbol],
195 ) -> Vec<Relocation> {
196 generate_fake_pool_relocations_for_function(
197 address,
198 code,
199 relocations,
200 symbols,
201 self.extensions,
202 )
203 }
204
205 fn data_flow_analysis(
206 &self,
207 obj: &Object,
208 symbol: &Symbol,
209 code: &[u8],
210 relocations: &[Relocation],
211 ) -> Option<Box<dyn FlowAnalysisResult>> {
212 Some(flow_analysis::ppc_data_flow_analysis(obj, symbol, code, relocations, self.extensions))
213 }
214
215 fn relocation_override(
216 &self,
217 file: &object::File<'_>,
218 section: &object::Section,
219 address: u64,
220 relocation: &object::Relocation,
221 ) -> Result<Option<RelocationOverride>> {
222 match relocation.flags() {
223 object::RelocationFlags::Coff {
225 typ: pe::IMAGE_REL_PPC_REFHI | pe::IMAGE_REL_PPC_REFLO,
226 } => section
227 .relocations()
228 .skip_while(|&(a, _)| a < address)
229 .take_while(|&(a, _)| a == address)
230 .find(|(_, reloc)| {
231 matches!(reloc.flags(), object::RelocationFlags::Coff {
232 typ: pe::IMAGE_REL_PPC_PAIR
233 })
234 })
235 .map_or(
236 Ok(Some(RelocationOverride {
237 target: RelocationOverrideTarget::Keep,
238 addend: 0,
239 })),
240 |(_, reloc)| match reloc.target() {
241 object::RelocationTarget::Symbol(_) => Ok(Some(RelocationOverride {
242 target: RelocationOverrideTarget::Keep,
243 addend: 0,
244 })),
245 target => Err(anyhow!("Unsupported IMAGE_REL_PPC_PAIR target {target:?}")),
246 },
247 ),
248 object::RelocationFlags::Coff { typ: pe::IMAGE_REL_PPC_PAIR } => {
250 Ok(Some(RelocationOverride { target: RelocationOverrideTarget::Skip, addend: 0 }))
251 }
252 object::RelocationFlags::Coff { .. } => {
254 Ok(Some(RelocationOverride { target: RelocationOverrideTarget::Keep, addend: 0 }))
255 }
256 flags @ object::RelocationFlags::Elf { r_type } => {
258 ensure!(
259 !relocation.has_implicit_addend(),
260 "Unsupported implicit relocation {:?}",
261 flags
262 );
263 match r_type {
264 elf::R_PPC64_TOC16 => {
265 let offset = u64::try_from(relocation.addend())
266 .map_err(|_| anyhow!("Negative addend for R_PPC64_TOC16 relocation"))?;
267 let Some(toc_section) = file.section_by_name(".toc") else {
268 bail!("Missing .toc section for R_PPC64_TOC16 relocation");
269 };
270 let Some((_, toc_relocation)) =
272 toc_section.relocations().find(|&(a, _)| a == offset)
273 else {
274 return Ok(None);
275 };
276 if toc_relocation.has_implicit_addend() {
277 log::warn!(
278 "Unsupported implicit addend for R_PPC64_TOC16 relocation: {toc_relocation:?}"
279 );
280 return Ok(None);
281 }
282 let addend = toc_relocation.addend();
283 match toc_relocation.target() {
284 object::RelocationTarget::Symbol(symbol_index) => {
285 Ok(Some(RelocationOverride {
286 target: RelocationOverrideTarget::Symbol(symbol_index),
287 addend,
288 }))
289 }
290 object::RelocationTarget::Section(section_index) => {
291 Ok(Some(RelocationOverride {
292 target: RelocationOverrideTarget::Section(section_index),
293 addend,
294 }))
295 }
296 target => {
297 log::warn!(
298 "Unsupported R_PPC64_TOC16 relocation target {target:?}"
299 );
300 Ok(None)
301 }
302 }
303 }
304 _ => Ok(None),
305 }
306 }
307 _ => Ok(None),
308 }
309 }
310
311 fn reloc_name(&self, flags: RelocationFlags) -> Option<&'static str> {
312 match flags {
313 RelocationFlags::Elf(r_type) => match r_type {
314 elf::R_PPC_NONE => Some("R_PPC_NONE"), elf::R_PPC_ADDR16_LO => Some("R_PPC_ADDR16_LO"),
316 elf::R_PPC_ADDR16_HI => Some("R_PPC_ADDR16_HI"),
317 elf::R_PPC_ADDR16_HA => Some("R_PPC_ADDR16_HA"),
318 elf::R_PPC_EMB_SDA21 => Some("R_PPC_EMB_SDA21"),
319 elf::R_PPC_ADDR32 => Some("R_PPC_ADDR32"),
320 elf::R_PPC_UADDR32 => Some("R_PPC_UADDR32"),
321 elf::R_PPC_REL24 => Some("R_PPC_REL24"),
322 elf::R_PPC_REL14 => Some("R_PPC_REL14"),
323 elf::R_PPC64_TOC16 => Some("R_PPC64_TOC16"),
324 _ => None,
325 },
326 RelocationFlags::Coff(r_type) => match r_type {
327 pe::IMAGE_REL_PPC_ADDR32 => Some("IMAGE_REL_PPC_ADDR32"),
328 pe::IMAGE_REL_PPC_REFHI => Some("IMAGE_REL_PPC_REFHI"),
329 pe::IMAGE_REL_PPC_REFLO => Some("IMAGE_REL_PPC_REFLO"),
330 pe::IMAGE_REL_PPC_REL24 => Some("IMAGE_REL_PPC_REL24"),
331 pe::IMAGE_REL_PPC_REL14 => Some("IMAGE_REL_PPC_REL14"),
332 pe::IMAGE_REL_PPC_PAIR => Some("IMAGE_REL_PPC_PAIR"),
333 _ => None,
334 },
335 }
336 }
337
338 fn data_reloc_size(&self, flags: RelocationFlags) -> usize {
339 match flags {
340 RelocationFlags::Elf(r_type) => match r_type {
341 elf::R_PPC_ADDR32 => 4,
342 elf::R_PPC_UADDR32 => 4,
343 _ => 1,
344 },
345 RelocationFlags::Coff(r_type) => match r_type {
346 pe::IMAGE_REL_PPC_ADDR32 => 4,
347 _ => 1,
348 },
349 }
350 }
351
352 fn extra_symbol_flags(&self, symbol: &object::Symbol) -> SymbolFlagSet {
353 if self.extab.as_ref().is_some_and(|extab| extab.contains_key(&(symbol.index().0 - 1))) {
354 SymbolFlag::HasExtra.into()
355 } else {
356 SymbolFlag::none()
357 }
358 }
359
360 fn guess_data_type(&self, resolved: ResolvedInstructionRef, bytes: &[u8]) -> Option<DataType> {
361 if resolved.relocation.is_some_and(|r| {
362 r.symbol.name.starts_with("@stringBase")
363 || r.symbol.name.starts_with("@wstringBase")
364 || r.symbol.name.starts_with("$SG")
365 || r.symbol.demangled_name == Some("`string'".to_string())
366 }) {
367 return Some(DataType::String);
369 }
370 let opcode = powerpc::Opcode::from(resolved.ins_ref.opcode);
371 if let Some(ty) = flow_analysis::guess_data_type_from_load_store_inst_op(opcode) {
372 return Some(ty);
374 }
375 if bytes.len() >= 2 && bytes.iter().position(|&c| c == b'\0') == Some(bytes.len() - 1) {
376 return Some(DataType::String);
378 }
379 None
380 }
381
382 fn symbol_hover(&self, _obj: &Object, symbol_index: usize) -> Vec<HoverItem> {
383 let mut out = Vec::new();
384 if let Some(extab) = self.extab_for_symbol(symbol_index) {
385 out.push(HoverItem::Text {
386 label: "extab symbol".into(),
387 value: extab.etb_symbol.name.clone(),
388 color: HoverItemColor::Special,
389 });
390 out.push(HoverItem::Text {
391 label: "extabindex symbol".into(),
392 value: extab.eti_symbol.name.clone(),
393 color: HoverItemColor::Special,
394 });
395 }
396 out
397 }
398
399 fn symbol_context(&self, _obj: &Object, symbol_index: usize) -> Vec<ContextItem> {
400 let mut out = Vec::new();
401 if let Some(_extab) = self.extab_for_symbol(symbol_index) {
402 out.push(ContextItem::Navigate {
403 label: "Decode exception table".to_string(),
404 symbol_index,
405 kind: SymbolNavigationKind::Extab,
406 });
407 }
408 out
409 }
410
411 fn instruction_hover(&self, _obj: &Object, resolved: ResolvedInstructionRef) -> Vec<HoverItem> {
412 let Ok(ins) = self.parse_ins_ref(resolved) else {
413 return Vec::new();
414 };
415 let orig = ins.basic().to_string();
416 let simplified = ins.simplified().to_string();
417 let show_orig = orig != simplified;
418 let rlwinm_decoded = rlwinmdec::decode(&orig);
419 let mut out = Vec::with_capacity(2);
420 if show_orig {
421 out.push(HoverItem::Text {
422 label: "Original".into(),
423 value: orig,
424 color: HoverItemColor::Normal,
425 });
426 }
427 if let Some(decoded) = rlwinm_decoded {
428 for line in decoded.lines() {
429 out.push(HoverItem::Text {
430 label: Default::default(),
431 value: line.to_string(),
432 color: HoverItemColor::Special,
433 });
434 }
435 }
436 out
437 }
438
439 fn instruction_context(
440 &self,
441 _obj: &Object,
442 resolved: ResolvedInstructionRef,
443 ) -> Vec<ContextItem> {
444 let Ok(ins) = self.parse_ins_ref(resolved) else {
445 return Vec::new();
446 };
447 let orig = ins.basic().to_string();
448 let simplified = ins.simplified().to_string();
449 let show_orig = orig != simplified;
450 let mut out = Vec::with_capacity(2);
451 out.push(ContextItem::Copy { value: simplified, label: None, copy_string: None });
452 if show_orig {
453 out.push(ContextItem::Copy {
454 value: orig,
455 label: Some("original".to_string()),
456 copy_string: None,
457 });
458 }
459 out
460 }
461
462 fn infer_function_size(
463 &self,
464 symbol: &Symbol,
465 section: &Section,
466 mut next_address: u64,
467 ) -> Result<u64> {
468 while next_address >= symbol.address + 4
470 && let Some(data) = section.data_range(next_address - 4, 4)
471 && data == [0u8; 4]
472 && section.relocation_at(next_address - 4, 4).is_none()
473 {
474 next_address -= 4;
475 }
476 Ok(next_address.saturating_sub(symbol.address))
477 }
478
479 fn post_init(&mut self, _sections: &[Section], _symbols: &[Symbol], symbol_indices: &[usize]) {
480 self.extab = Self::convert_extab_map_indices(self, symbol_indices);
482 }
483}
484
485impl ArchPpc {
486 pub fn extab_for_symbol(&self, symbol_index: usize) -> Option<&ExceptionInfo> {
487 self.extab.as_ref()?.get(&symbol_index)
488 }
489
490 pub fn convert_extab_map_indices(
491 &self,
492 symbol_indices: &[usize],
493 ) -> Option<BTreeMap<usize, ExceptionInfo>> {
494 let new_map: BTreeMap<usize, ExceptionInfo> =
495 self.extab.as_ref()?.iter().map(|e| (symbol_indices[*e.0 + 1], e.1.clone())).collect();
496
497 Some(new_map)
498 }
499}
500
501fn zero_reloc(code: u32, reloc: &Relocation) -> u32 {
502 match reloc.flags {
503 RelocationFlags::Elf(elf::R_PPC_EMB_SDA21) => code & !0x1FFFFF,
504 RelocationFlags::Elf(elf::R_PPC_REL24) | RelocationFlags::Coff(pe::IMAGE_REL_PPC_REL24) => {
505 code & !0x3FFFFFC
506 }
507 RelocationFlags::Elf(elf::R_PPC_REL14) | RelocationFlags::Coff(pe::IMAGE_REL_PPC_REL14) => {
508 code & !0xFFFC
509 }
510 RelocationFlags::Elf(
511 elf::R_PPC_ADDR16_HI | elf::R_PPC_ADDR16_HA | elf::R_PPC_ADDR16_LO,
512 )
513 | RelocationFlags::Coff(pe::IMAGE_REL_PPC_REFHI | pe::IMAGE_REL_PPC_REFLO) => {
514 code & !0xFFFF
515 }
516 _ => code,
517 }
518}
519
520fn display_reloc(
521 resolved: ResolvedRelocation,
522 cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
523) -> Result<()> {
524 match resolved.relocation.flags {
525 RelocationFlags::Elf(elf::R_PPC_ADDR16_LO)
526 | RelocationFlags::Coff(pe::IMAGE_REL_PPC_REFLO) => {
527 cb(InstructionPart::reloc())?;
528 cb(InstructionPart::basic("@l"))?;
529 }
530 RelocationFlags::Elf(elf::R_PPC_ADDR16_HI)
531 | RelocationFlags::Coff(pe::IMAGE_REL_PPC_REFHI) => {
532 cb(InstructionPart::reloc())?;
533 cb(InstructionPart::basic("@h"))?;
534 }
535 RelocationFlags::Elf(elf::R_PPC_ADDR16_HA) => {
536 cb(InstructionPart::reloc())?;
537 cb(InstructionPart::basic("@ha"))?;
538 }
539 RelocationFlags::Elf(elf::R_PPC_EMB_SDA21) => {
540 cb(InstructionPart::reloc())?;
541 cb(InstructionPart::basic("@sda21"))?;
542 }
543 RelocationFlags::Elf(
544 elf::R_PPC_ADDR32 | elf::R_PPC_UADDR32 | elf::R_PPC_REL24 | elf::R_PPC_REL14,
545 )
546 | RelocationFlags::Coff(
547 pe::IMAGE_REL_PPC_ADDR32 | pe::IMAGE_REL_PPC_REL24 | pe::IMAGE_REL_PPC_REL14,
548 ) => {
549 cb(InstructionPart::reloc())?;
550 }
551 RelocationFlags::Elf(elf::R_PPC_NONE) => {
552 cb(InstructionPart::basic("<"))?;
554 cb(InstructionPart::reloc())?;
555 cb(InstructionPart::basic(">"))?;
556 }
557 _ => cb(InstructionPart::reloc())?,
558 };
559 Ok(())
560}
561
562#[derive(Debug, Clone)]
563pub struct ExtabSymbolRef {
564 pub original_index: usize,
565 pub name: String,
566 pub demangled_name: Option<String>,
567}
568
569#[derive(Debug, Clone)]
570pub struct ExceptionInfo {
571 pub eti_symbol: ExtabSymbolRef,
572 pub etb_symbol: ExtabSymbolRef,
573 pub data: ExceptionTableData,
574 pub dtors: Vec<ExtabSymbolRef>,
575}
576
577fn decode_exception_info(
578 file: &object::File<'_>,
579) -> Result<Option<BTreeMap<usize, ExceptionInfo>>> {
580 let Some(extab_section) = file.section_by_name("extab") else {
581 return Ok(None);
582 };
583 let Some(extabindex_section) = file.section_by_name("extabindex") else {
584 return Ok(None);
585 };
586
587 let mut result = BTreeMap::new();
588 let extab_relocations =
589 extab_section.relocations().collect::<BTreeMap<u64, object::Relocation>>();
590 let extabindex_relocations =
591 extabindex_section.relocations().collect::<BTreeMap<u64, object::Relocation>>();
592
593 for extabindex in file.symbols().filter(|symbol| {
594 symbol.section_index() == Some(extabindex_section.index())
595 && symbol.kind() == object::SymbolKind::Data
596 }) {
597 if extabindex.size() != 12 {
598 log::warn!("Invalid extabindex entry size {}", extabindex.size());
599 continue;
600 }
601
602 let Some(extab_func_reloc) = extabindex_relocations.get(&extabindex.address()) else {
606 log::warn!("Failed to find function relocation for extabindex entry");
607 continue;
608 };
609 let Some(extab_reloc) = extabindex_relocations.get(&(extabindex.address() + 8)) else {
610 log::warn!("Failed to find extab relocation for extabindex entry");
611 continue;
612 };
613
614 let Some(extab_func) = relocation_symbol(file, extab_func_reloc)? else {
616 log::warn!("Failed to find function symbol for extabindex entry");
617 continue;
618 };
619 let extab_func_name = extab_func.name()?;
620 let Some(extab) = relocation_symbol(file, extab_reloc)? else {
621 log::warn!("Failed to find extab symbol for extabindex entry");
622 continue;
623 };
624
625 let extab_start_addr = extab.address() - extab_section.address();
626 let extab_end_addr = extab_start_addr + extab.size();
627
628 let mut dtors: Vec<ExtabSymbolRef> = vec![];
630 for (_, reloc) in extab_relocations.range(extab_start_addr..extab_end_addr) {
631 let Some(symbol) = relocation_symbol(file, reloc)? else {
632 log::warn!("Failed to find symbol for extab relocation");
633 continue;
634 };
635 dtors.push(make_symbol_ref(&symbol)?);
636 }
637
638 let Some(extab_data) = extab_section.data_range(extab_start_addr, extab.size())? else {
640 log::warn!("Failed to get extab data for function {extab_func_name}");
641 continue;
642 };
643 let data = match decode_extab(extab_data) {
644 Ok(decoded_data) => decoded_data,
645 Err(e) => {
646 log::warn!(
647 "Exception table decoding failed for function {extab_func_name}, reason: {e}"
648 );
649 return Ok(None);
650 }
651 };
652
653 result.insert(extab_func.index().0 - 1, ExceptionInfo {
655 eti_symbol: make_symbol_ref(&extabindex)?,
656 etb_symbol: make_symbol_ref(&extab)?,
657 data,
658 dtors,
659 });
660 }
661
662 Ok(Some(result))
663}
664
665fn relocation_symbol<'data, 'file>(
666 file: &'file object::File<'data>,
667 relocation: &object::Relocation,
668) -> Result<Option<object::Symbol<'data, 'file>>> {
669 let addend = relocation.addend();
670 match relocation.target() {
671 object::RelocationTarget::Symbol(idx) => {
672 ensure!(addend == 0, "Symbol relocations must have zero addend");
673 Ok(Some(file.symbol_by_index(idx)?))
674 }
675 object::RelocationTarget::Section(idx) => {
676 ensure!(addend >= 0, "Section relocations must have non-negative addend");
677 let addend = addend as u64;
678 Ok(file
679 .symbols()
680 .find(|symbol| symbol.section_index() == Some(idx) && symbol.address() == addend))
681 }
682 target => bail!("Unsupported relocation target: {target:?}"),
683 }
684}
685
686fn make_symbol_ref(symbol: &object::Symbol) -> Result<ExtabSymbolRef> {
687 let name = symbol.name()?.to_string();
688 let demangled_name = cwdemangle::demangle(&name, &cwdemangle::DemangleOptions::default());
689 Ok(ExtabSymbolRef { original_index: symbol.index().0 - 1, name, demangled_name })
690}
691
692#[derive(Debug)]
693struct PoolReference {
694 addr_src_gpr: powerpc::GPR,
695 addr_offset: i64,
696 addr_dst_gpr: Option<powerpc::GPR>,
697}
698
699fn get_pool_reference_for_inst(
703 ins: powerpc::Ins,
704 simplified: &powerpc::ParsedIns,
705) -> Option<PoolReference> {
706 use powerpc::{Argument, Opcode};
707 let args = &simplified.args;
708 if flow_analysis::guess_data_type_from_load_store_inst_op(ins.op).is_some() {
709 match (args[1], args[2]) {
710 (Argument::Offset(offset), Argument::GPR(addr_src_gpr)) => {
711 Some(PoolReference {
713 addr_src_gpr,
714 addr_offset: offset.0 as i64,
715 addr_dst_gpr: None,
716 })
717 }
718 (Argument::GPR(addr_src_gpr), Argument::GPR(_offset_gpr)) => {
719 Some(PoolReference { addr_src_gpr, addr_offset: 0, addr_dst_gpr: None })
725 }
726 _ => None,
727 }
728 } else {
729 match (ins.op, args[0], args[1], args[2]) {
737 (
738 Opcode::Addi,
740 Argument::GPR(addr_dst_gpr),
741 Argument::GPR(addr_src_gpr),
742 Argument::Simm(simm),
743 ) => {
744 let offset = if simplified.mnemonic == "addi" { simm.0 } else { -simm.0 };
745 Some(PoolReference {
746 addr_src_gpr,
747 addr_offset: offset as i64,
748 addr_dst_gpr: Some(addr_dst_gpr),
749 })
750 }
751 (
752 Opcode::Addis,
754 Argument::GPR(addr_dst_gpr),
755 Argument::GPR(addr_src_gpr),
756 Argument::Uimm(uimm), ) => {
758 assert_eq!(simplified.mnemonic, "addis");
759 let offset = (uimm.0 as i64) << 16;
760 Some(PoolReference {
761 addr_src_gpr,
762 addr_offset: offset,
763 addr_dst_gpr: Some(addr_dst_gpr),
764 })
765 }
766 (
767 Opcode::Addis,
769 Argument::GPR(addr_dst_gpr),
770 Argument::GPR(addr_src_gpr),
771 Argument::Simm(simm),
772 ) => {
773 assert_eq!(simplified.mnemonic, "subis");
774 let offset = (simm.0 as i64) << 16;
775 Some(PoolReference {
776 addr_src_gpr,
777 addr_offset: offset,
778 addr_dst_gpr: Some(addr_dst_gpr),
779 })
780 }
781 (
782 Opcode::Or,
784 Argument::GPR(addr_dst_gpr),
785 Argument::GPR(addr_src_gpr),
786 Argument::None,
787 ) => Some(PoolReference {
788 addr_src_gpr,
789 addr_offset: 0,
790 addr_dst_gpr: Some(addr_dst_gpr),
791 }),
792 (
793 Opcode::Add,
794 Argument::GPR(addr_dst_gpr),
795 Argument::GPR(addr_src_gpr),
796 Argument::GPR(_offset_gpr),
797 ) => Some(PoolReference {
798 addr_src_gpr,
799 addr_offset: 0,
800 addr_dst_gpr: Some(addr_dst_gpr),
801 }),
802 _ => None,
803 }
804 }
805}
806
807fn clear_overwritten_gprs(ins: powerpc::Ins, gpr_pool_relocs: &mut BTreeMap<u8, Relocation>) {
810 use powerpc::{Argument, Arguments, Opcode};
811 let mut def_args = Arguments::default();
812 ins.parse_defs(&mut def_args);
813 for arg in def_args {
814 if let Argument::GPR(gpr) = arg {
815 if ins.op == Opcode::Lmw {
816 for reg in gpr.0..31 {
819 gpr_pool_relocs.remove(®);
820 }
821 break;
822 }
823 gpr_pool_relocs.remove(&gpr.0);
824 }
825 }
826}
827
828fn make_fake_pool_reloc(
835 offset: i64,
836 cur_addr: u32,
837 pool_reloc: &Relocation,
838 symbols: &[Symbol],
839) -> Option<Relocation> {
840 let pool_reloc = resolve_relocation(symbols, pool_reloc);
841 let offset_from_pool = pool_reloc.relocation.addend + offset;
842 let target_address = pool_reloc.symbol.address.checked_add_signed(offset_from_pool)?;
843 let target_symbol;
844 let addend;
845 if let Some(section_index) = pool_reloc.symbol.section {
846 target_symbol = symbols.iter().position(|s| {
848 s.section == Some(section_index)
849 && s.size > 0
850 && !s.flags.contains(SymbolFlag::Hidden)
851 && !s.flags.contains(SymbolFlag::Ignored)
852 && s.kind != SymbolKind::Section
853 && (s.address..s.address + s.size).contains(&target_address)
854 })?;
855 addend = target_address.checked_sub(symbols[target_symbol].address)? as i64;
856 } else {
857 target_symbol = pool_reloc.relocation.target_symbol;
866 addend = offset_from_pool;
867 }
868 Some(Relocation {
869 flags: RelocationFlags::Elf(elf::R_PPC_NONE),
870 address: cur_addr as u64,
871 target_symbol,
872 addend,
873 })
874}
875
876fn generate_fake_pool_relocations_for_function(
896 func_address: u64,
897 code: &[u8],
898 relocations: &[Relocation],
899 symbols: &[Symbol],
900 extensions: powerpc::Extensions,
901) -> Vec<Relocation> {
902 use powerpc::{Argument, InsIter, Opcode};
903 let mut visited_ins_addrs = BTreeSet::new();
904 let mut pool_reloc_for_addr = BTreeMap::new();
905 let mut ins_iters_with_gpr_state =
906 vec![(InsIter::new(code, func_address as u32, extensions), BTreeMap::new())];
907 let mut gpr_state_at_bctr = BTreeMap::new();
908 while let Some((ins_iter, mut gpr_pool_relocs)) = ins_iters_with_gpr_state.pop() {
909 for (cur_addr, ins) in ins_iter {
910 if visited_ins_addrs.contains(&cur_addr) {
911 break;
913 }
914 visited_ins_addrs.insert(cur_addr);
915
916 let simplified = ins.simplified();
917
918 let mut branch_dest = None;
920 for arg in simplified.args_iter() {
921 if let Argument::BranchDest(dest) = arg {
922 let dest = cur_addr.wrapping_add_signed(dest.0);
923 branch_dest = Some(dest);
924 break;
925 }
926 }
927 if let Some(branch_dest) = branch_dest
928 && branch_dest >= func_address as u32
929 && (branch_dest - func_address as u32) < code.len() as u32
930 {
931 let dest_offset_into_func = branch_dest - func_address as u32;
932 let dest_code_slice = &code[dest_offset_into_func as usize..];
933 match ins.op {
934 Opcode::Bc => {
935 ins_iters_with_gpr_state.push((
938 InsIter::new(dest_code_slice, branch_dest, extensions),
939 gpr_pool_relocs.clone(),
940 ));
941 }
943 Opcode::B => {
944 if simplified.mnemonic != "bl" {
945 ins_iters_with_gpr_state.push((
948 InsIter::new(dest_code_slice, branch_dest, extensions),
949 gpr_pool_relocs.clone(),
950 ));
951 break;
953 }
954 }
955 _ => unreachable!(),
956 }
957 }
958 if let Opcode::Bcctr = ins.op
959 && simplified.mnemonic == "bctr"
960 {
961 gpr_state_at_bctr.insert(cur_addr, gpr_pool_relocs.clone());
964 }
965
966 let reloc = relocations.iter().find(|r| (r.address as u32 & !3) == cur_addr);
968 if let Some(reloc) = reloc {
969 let args = &simplified.args;
972 match (ins.op, args[0], args[1], args[2]) {
973 (
974 Opcode::Addi,
976 Argument::GPR(addr_dst_gpr),
977 Argument::GPR(_addr_src_gpr),
978 Argument::Simm(_simm),
979 ) => {
980 gpr_pool_relocs.insert(addr_dst_gpr.0, reloc.clone());
981 }
982 (
983 Opcode::Ori,
985 Argument::GPR(addr_dst_gpr),
986 Argument::GPR(_addr_src_gpr),
987 Argument::Uimm(_uimm),
988 ) => {
989 gpr_pool_relocs.insert(addr_dst_gpr.0, reloc.clone());
990 }
991 (Opcode::B, _, _, _) => {
992 if simplified.mnemonic == "bl" {
993 gpr_pool_relocs.remove(&0);
996 for gpr in 3..12 {
997 gpr_pool_relocs.remove(&gpr);
998 }
999 }
1000 }
1001 _ => {
1002 clear_overwritten_gprs(ins, &mut gpr_pool_relocs);
1003 }
1004 }
1005 } else if let Some(pool_ref) = get_pool_reference_for_inst(ins, &simplified) {
1006 if let Some(pool_reloc) = gpr_pool_relocs.get(&pool_ref.addr_src_gpr.0) {
1009 if let Some(fake_pool_reloc) =
1010 make_fake_pool_reloc(pool_ref.addr_offset, cur_addr, pool_reloc, symbols)
1011 {
1012 pool_reloc_for_addr.insert(cur_addr, fake_pool_reloc);
1013 }
1014 if let Some(addr_dst_gpr) = pool_ref.addr_dst_gpr {
1015 let mut new_reloc = pool_reloc.clone();
1024 new_reloc.addend += pool_ref.addr_offset;
1025 gpr_pool_relocs.insert(addr_dst_gpr.0, new_reloc);
1026 } else {
1027 clear_overwritten_gprs(ins, &mut gpr_pool_relocs);
1028 }
1029 } else {
1030 clear_overwritten_gprs(ins, &mut gpr_pool_relocs);
1031 }
1032 } else {
1033 clear_overwritten_gprs(ins, &mut gpr_pool_relocs);
1034 }
1035 }
1036
1037 if ins_iters_with_gpr_state.is_empty() {
1042 let unseen_addrs = (func_address as u32..func_address as u32 + code.len() as u32)
1043 .step_by(4)
1044 .filter(|addr| !visited_ins_addrs.contains(addr));
1045 for unseen_addr in unseen_addrs {
1046 let prev_bctr_gpr_state = gpr_state_at_bctr
1047 .iter()
1048 .filter(|&(&addr, _)| addr < unseen_addr)
1049 .min_by_key(|&(&addr, _)| addr)
1050 .map(|(_, gpr_state)| gpr_state);
1051 if let Some(gpr_pool_relocs) = prev_bctr_gpr_state {
1052 let dest_offset_into_func = unseen_addr - func_address as u32;
1053 let dest_code_slice = &code[dest_offset_into_func as usize..];
1054 ins_iters_with_gpr_state.push((
1055 InsIter::new(dest_code_slice, unseen_addr, extensions),
1056 gpr_pool_relocs.clone(),
1057 ));
1058 break;
1059 }
1060 }
1061 }
1062 }
1063
1064 pool_reloc_for_addr.values().cloned().collect()
1065}