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(
361 &self,
362 ins: Option<ResolvedInstructionRef>,
363 reloc: Option<ResolvedRelocation>,
364 bytes: &[u8],
365 ) -> Option<DataType> {
366 if reloc.is_some_and(|r| {
367 r.symbol.name.starts_with("@stringBase") || r.symbol.name.starts_with("@wstringBase") || r.symbol.name.starts_with("$SG")
370 || r.symbol.name.starts_with("??_C") }) {
372 return Some(DataType::String);
374 }
375 if let Some(ins) = ins {
376 let opcode = powerpc::Opcode::from(ins.ins_ref.opcode);
377 if let Some(ty) = flow_analysis::guess_data_type_from_load_store_inst_op(opcode) {
378 return Some(ty);
380 }
381 }
382 if reloc.is_some_and(|r| r.symbol.name.starts_with("$LC")) {
383 return Some(DataType::String);
386 }
387 if bytes.len() >= 2 && bytes.iter().position(|&c| c == b'\0') == Some(bytes.len() - 1) {
388 return Some(DataType::String);
390 }
391 None
392 }
393
394 fn symbol_hover(&self, _obj: &Object, symbol_index: usize) -> Vec<HoverItem> {
395 let mut out = Vec::new();
396 if let Some(extab) = self.extab_for_symbol(symbol_index) {
397 out.push(HoverItem::Text {
398 label: "extab symbol".into(),
399 value: extab.etb_symbol.name.clone(),
400 color: HoverItemColor::Special,
401 });
402 out.push(HoverItem::Text {
403 label: "extabindex symbol".into(),
404 value: extab.eti_symbol.name.clone(),
405 color: HoverItemColor::Special,
406 });
407 }
408 out
409 }
410
411 fn symbol_context(&self, _obj: &Object, symbol_index: usize) -> Vec<ContextItem> {
412 let mut out = Vec::new();
413 if let Some(_extab) = self.extab_for_symbol(symbol_index) {
414 out.push(ContextItem::Navigate {
415 label: "Decode exception table".to_string(),
416 symbol_index,
417 kind: SymbolNavigationKind::Extab,
418 });
419 }
420 out
421 }
422
423 fn instruction_hover(&self, _obj: &Object, resolved: ResolvedInstructionRef) -> Vec<HoverItem> {
424 let Ok(ins) = self.parse_ins_ref(resolved) else {
425 return Vec::new();
426 };
427 let orig = ins.basic().to_string();
428 let simplified = ins.simplified().to_string();
429 let show_orig = orig != simplified;
430 let rlwinm_decoded = rlwinmdec::decode(&orig);
431 let mut out = Vec::with_capacity(2);
432 if show_orig {
433 out.push(HoverItem::Text {
434 label: "Original".into(),
435 value: orig,
436 color: HoverItemColor::Normal,
437 });
438 }
439 if let Some(decoded) = rlwinm_decoded {
440 for line in decoded.lines() {
441 out.push(HoverItem::Text {
442 label: Default::default(),
443 value: line.to_string(),
444 color: HoverItemColor::Special,
445 });
446 }
447 }
448 out
449 }
450
451 fn instruction_context(
452 &self,
453 _obj: &Object,
454 resolved: ResolvedInstructionRef,
455 ) -> Vec<ContextItem> {
456 let Ok(ins) = self.parse_ins_ref(resolved) else {
457 return Vec::new();
458 };
459 let orig = ins.basic().to_string();
460 let simplified = ins.simplified().to_string();
461 let show_orig = orig != simplified;
462 let mut out = Vec::with_capacity(2);
463 out.push(ContextItem::Copy { value: simplified, label: None, copy_string: None });
464 if show_orig {
465 out.push(ContextItem::Copy {
466 value: orig,
467 label: Some("original".to_string()),
468 copy_string: None,
469 });
470 }
471 out
472 }
473
474 fn infer_function_size(
475 &self,
476 symbol: &Symbol,
477 section: &Section,
478 mut next_address: u64,
479 ) -> Result<u64> {
480 while next_address >= symbol.address + 4
482 && let Some(data) = section.data_range(next_address - 4, 4)
483 && data == [0u8; 4]
484 && section.relocation_at(next_address - 4, 4).is_none()
485 {
486 next_address -= 4;
487 }
488 Ok(next_address.saturating_sub(symbol.address))
489 }
490
491 fn post_init(&mut self, _sections: &[Section], _symbols: &[Symbol], symbol_indices: &[usize]) {
492 self.extab = Self::convert_extab_map_indices(self, symbol_indices);
494 }
495}
496
497impl ArchPpc {
498 pub fn extab_for_symbol(&self, symbol_index: usize) -> Option<&ExceptionInfo> {
499 self.extab.as_ref()?.get(&symbol_index)
500 }
501
502 pub fn convert_extab_map_indices(
503 &self,
504 symbol_indices: &[usize],
505 ) -> Option<BTreeMap<usize, ExceptionInfo>> {
506 let new_map: BTreeMap<usize, ExceptionInfo> =
507 self.extab.as_ref()?.iter().map(|e| (symbol_indices[*e.0 + 1], e.1.clone())).collect();
508
509 Some(new_map)
510 }
511}
512
513fn zero_reloc(code: u32, reloc: &Relocation) -> u32 {
514 match reloc.flags {
515 RelocationFlags::Elf(elf::R_PPC_EMB_SDA21) => code & !0x1FFFFF,
516 RelocationFlags::Elf(elf::R_PPC_REL24) | RelocationFlags::Coff(pe::IMAGE_REL_PPC_REL24) => {
517 code & !0x3FFFFFC
518 }
519 RelocationFlags::Elf(elf::R_PPC_REL14) | RelocationFlags::Coff(pe::IMAGE_REL_PPC_REL14) => {
520 code & !0xFFFC
521 }
522 RelocationFlags::Elf(
523 elf::R_PPC_ADDR16_HI | elf::R_PPC_ADDR16_HA | elf::R_PPC_ADDR16_LO,
524 )
525 | RelocationFlags::Coff(pe::IMAGE_REL_PPC_REFHI | pe::IMAGE_REL_PPC_REFLO) => {
526 code & !0xFFFF
527 }
528 _ => code,
529 }
530}
531
532fn display_reloc(
533 resolved: ResolvedRelocation,
534 cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
535) -> Result<()> {
536 match resolved.relocation.flags {
537 RelocationFlags::Elf(elf::R_PPC_ADDR16_LO)
538 | RelocationFlags::Coff(pe::IMAGE_REL_PPC_REFLO) => {
539 cb(InstructionPart::reloc())?;
540 cb(InstructionPart::basic("@l"))?;
541 }
542 RelocationFlags::Elf(elf::R_PPC_ADDR16_HI)
543 | RelocationFlags::Coff(pe::IMAGE_REL_PPC_REFHI) => {
544 cb(InstructionPart::reloc())?;
545 cb(InstructionPart::basic("@h"))?;
546 }
547 RelocationFlags::Elf(elf::R_PPC_ADDR16_HA) => {
548 cb(InstructionPart::reloc())?;
549 cb(InstructionPart::basic("@ha"))?;
550 }
551 RelocationFlags::Elf(elf::R_PPC_EMB_SDA21) => {
552 cb(InstructionPart::reloc())?;
553 cb(InstructionPart::basic("@sda21"))?;
554 }
555 RelocationFlags::Elf(
556 elf::R_PPC_ADDR32 | elf::R_PPC_UADDR32 | elf::R_PPC_REL24 | elf::R_PPC_REL14,
557 )
558 | RelocationFlags::Coff(
559 pe::IMAGE_REL_PPC_ADDR32 | pe::IMAGE_REL_PPC_REL24 | pe::IMAGE_REL_PPC_REL14,
560 ) => {
561 cb(InstructionPart::reloc())?;
562 }
563 RelocationFlags::Elf(elf::R_PPC_NONE) => {
564 cb(InstructionPart::basic("<"))?;
566 cb(InstructionPart::reloc())?;
567 cb(InstructionPart::basic(">"))?;
568 }
569 _ => cb(InstructionPart::reloc())?,
570 };
571 Ok(())
572}
573
574#[derive(Debug, Clone)]
575pub struct ExtabSymbolRef {
576 pub original_index: usize,
577 pub name: String,
578 pub demangled_name: Option<String>,
579}
580
581#[derive(Debug, Clone)]
582pub struct ExceptionInfo {
583 pub eti_symbol: ExtabSymbolRef,
584 pub etb_symbol: ExtabSymbolRef,
585 pub data: ExceptionTableData,
586 pub dtors: Vec<ExtabSymbolRef>,
587}
588
589fn decode_exception_info(
590 file: &object::File<'_>,
591) -> Result<Option<BTreeMap<usize, ExceptionInfo>>> {
592 let Some(extab_section) = file.section_by_name("extab") else {
593 return Ok(None);
594 };
595 let Some(extabindex_section) = file.section_by_name("extabindex") else {
596 return Ok(None);
597 };
598
599 let mut result = BTreeMap::new();
600 let extab_relocations =
601 extab_section.relocations().collect::<BTreeMap<u64, object::Relocation>>();
602 let extabindex_relocations =
603 extabindex_section.relocations().collect::<BTreeMap<u64, object::Relocation>>();
604
605 for extabindex in file.symbols().filter(|symbol| {
606 symbol.section_index() == Some(extabindex_section.index())
607 && symbol.kind() == object::SymbolKind::Data
608 }) {
609 if extabindex.size() != 12 {
610 log::warn!("Invalid extabindex entry size {}", extabindex.size());
611 continue;
612 }
613
614 let Some(extab_func_reloc) = extabindex_relocations.get(&extabindex.address()) else {
618 log::warn!("Failed to find function relocation for extabindex entry");
619 continue;
620 };
621 let Some(extab_reloc) = extabindex_relocations.get(&(extabindex.address() + 8)) else {
622 log::warn!("Failed to find extab relocation for extabindex entry");
623 continue;
624 };
625
626 let Some(extab_func) = relocation_symbol(file, extab_func_reloc)? else {
628 log::warn!("Failed to find function symbol for extabindex entry");
629 continue;
630 };
631 let extab_func_name = extab_func.name()?;
632 let Some(extab) = relocation_symbol(file, extab_reloc)? else {
633 log::warn!("Failed to find extab symbol for extabindex entry");
634 continue;
635 };
636
637 let extab_start_addr = extab.address() - extab_section.address();
638 let extab_end_addr = extab_start_addr + extab.size();
639
640 let mut dtors: Vec<ExtabSymbolRef> = vec![];
642 for (_, reloc) in extab_relocations.range(extab_start_addr..extab_end_addr) {
643 let Some(symbol) = relocation_symbol(file, reloc)? else {
644 log::warn!("Failed to find symbol for extab relocation");
645 continue;
646 };
647 dtors.push(make_symbol_ref(&symbol)?);
648 }
649
650 let Some(extab_data) = extab_section.data_range(extab_start_addr, extab.size())? else {
652 log::warn!("Failed to get extab data for function {extab_func_name}");
653 continue;
654 };
655 let data = match decode_extab(extab_data) {
656 Ok(decoded_data) => decoded_data,
657 Err(e) => {
658 log::warn!(
659 "Exception table decoding failed for function {extab_func_name}, reason: {e}"
660 );
661 return Ok(None);
662 }
663 };
664
665 result.insert(extab_func.index().0 - 1, ExceptionInfo {
667 eti_symbol: make_symbol_ref(&extabindex)?,
668 etb_symbol: make_symbol_ref(&extab)?,
669 data,
670 dtors,
671 });
672 }
673
674 Ok(Some(result))
675}
676
677fn relocation_symbol<'data, 'file>(
678 file: &'file object::File<'data>,
679 relocation: &object::Relocation,
680) -> Result<Option<object::Symbol<'data, 'file>>> {
681 let addend = relocation.addend();
682 match relocation.target() {
683 object::RelocationTarget::Symbol(idx) => {
684 ensure!(addend == 0, "Symbol relocations must have zero addend");
685 Ok(Some(file.symbol_by_index(idx)?))
686 }
687 object::RelocationTarget::Section(idx) => {
688 ensure!(addend >= 0, "Section relocations must have non-negative addend");
689 let addend = addend as u64;
690 Ok(file
691 .symbols()
692 .find(|symbol| symbol.section_index() == Some(idx) && symbol.address() == addend))
693 }
694 target => bail!("Unsupported relocation target: {target:?}"),
695 }
696}
697
698fn make_symbol_ref(symbol: &object::Symbol) -> Result<ExtabSymbolRef> {
699 let name = symbol.name()?.to_string();
700 let demangled_name = cwdemangle::demangle(&name, &cwdemangle::DemangleOptions::default());
701 Ok(ExtabSymbolRef { original_index: symbol.index().0 - 1, name, demangled_name })
702}
703
704#[derive(Debug)]
705struct PoolReference {
706 addr_src_gpr: powerpc::GPR,
707 addr_offset: i64,
708 addr_dst_gpr: Option<powerpc::GPR>,
709}
710
711fn get_pool_reference_for_inst(
715 ins: powerpc::Ins,
716 simplified: &powerpc::ParsedIns,
717) -> Option<PoolReference> {
718 use powerpc::{Argument, Opcode};
719 let args = &simplified.args;
720 if flow_analysis::guess_data_type_from_load_store_inst_op(ins.op).is_some() {
721 match (args[1], args[2]) {
722 (Argument::Offset(offset), Argument::GPR(addr_src_gpr)) => {
723 Some(PoolReference {
725 addr_src_gpr,
726 addr_offset: offset.0 as i64,
727 addr_dst_gpr: None,
728 })
729 }
730 (Argument::GPR(addr_src_gpr), Argument::GPR(_offset_gpr)) => {
731 Some(PoolReference { addr_src_gpr, addr_offset: 0, addr_dst_gpr: None })
737 }
738 _ => None,
739 }
740 } else {
741 match (ins.op, args[0], args[1], args[2]) {
749 (
750 Opcode::Addi,
752 Argument::GPR(addr_dst_gpr),
753 Argument::GPR(addr_src_gpr),
754 Argument::Simm(simm),
755 ) => {
756 let offset = if simplified.mnemonic == "addi" { simm.0 } else { -simm.0 };
757 Some(PoolReference {
758 addr_src_gpr,
759 addr_offset: offset as i64,
760 addr_dst_gpr: Some(addr_dst_gpr),
761 })
762 }
763 (
764 Opcode::Addis,
766 Argument::GPR(addr_dst_gpr),
767 Argument::GPR(addr_src_gpr),
768 Argument::Uimm(uimm), ) => {
770 assert_eq!(simplified.mnemonic, "addis");
771 let offset = (uimm.0 as i64) << 16;
772 Some(PoolReference {
773 addr_src_gpr,
774 addr_offset: offset,
775 addr_dst_gpr: Some(addr_dst_gpr),
776 })
777 }
778 (
779 Opcode::Addis,
781 Argument::GPR(addr_dst_gpr),
782 Argument::GPR(addr_src_gpr),
783 Argument::Simm(simm),
784 ) => {
785 assert_eq!(simplified.mnemonic, "subis");
786 let offset = (simm.0 as i64) << 16;
787 Some(PoolReference {
788 addr_src_gpr,
789 addr_offset: offset,
790 addr_dst_gpr: Some(addr_dst_gpr),
791 })
792 }
793 (
794 Opcode::Or,
796 Argument::GPR(addr_dst_gpr),
797 Argument::GPR(addr_src_gpr),
798 Argument::None,
799 ) => Some(PoolReference {
800 addr_src_gpr,
801 addr_offset: 0,
802 addr_dst_gpr: Some(addr_dst_gpr),
803 }),
804 (
805 Opcode::Add,
806 Argument::GPR(addr_dst_gpr),
807 Argument::GPR(addr_src_gpr),
808 Argument::GPR(_offset_gpr),
809 ) => Some(PoolReference {
810 addr_src_gpr,
811 addr_offset: 0,
812 addr_dst_gpr: Some(addr_dst_gpr),
813 }),
814 _ => None,
815 }
816 }
817}
818
819fn clear_overwritten_gprs(ins: powerpc::Ins, gpr_pool_relocs: &mut BTreeMap<u8, Relocation>) {
822 use powerpc::{Argument, Arguments, Opcode};
823 let mut def_args = Arguments::default();
824 ins.parse_defs(&mut def_args);
825 for arg in def_args {
826 if let Argument::GPR(gpr) = arg {
827 if ins.op == Opcode::Lmw {
828 for reg in gpr.0..31 {
831 gpr_pool_relocs.remove(®);
832 }
833 break;
834 }
835 gpr_pool_relocs.remove(&gpr.0);
836 }
837 }
838}
839
840fn make_fake_pool_reloc(
847 offset: i64,
848 cur_addr: u32,
849 pool_reloc: &Relocation,
850 symbols: &[Symbol],
851) -> Option<Relocation> {
852 let pool_reloc = resolve_relocation(symbols, pool_reloc);
853 let offset_from_pool = pool_reloc.relocation.addend + offset;
854 let target_address = pool_reloc.symbol.address.checked_add_signed(offset_from_pool)?;
855 let target_symbol;
856 let addend;
857 if let Some(section_index) = pool_reloc.symbol.section {
858 target_symbol = symbols.iter().position(|s| {
860 s.section == Some(section_index)
861 && s.size > 0
862 && !s.flags.contains(SymbolFlag::Hidden)
863 && !s.flags.contains(SymbolFlag::Ignored)
864 && s.kind != SymbolKind::Section
865 && (s.address..s.address + s.size).contains(&target_address)
866 })?;
867 addend = target_address.checked_sub(symbols[target_symbol].address)? as i64;
868 } else {
869 target_symbol = pool_reloc.relocation.target_symbol;
878 addend = offset_from_pool;
879 }
880 Some(Relocation {
881 flags: RelocationFlags::Elf(elf::R_PPC_NONE),
882 address: cur_addr as u64,
883 target_symbol,
884 addend,
885 })
886}
887
888fn generate_fake_pool_relocations_for_function(
908 func_address: u64,
909 code: &[u8],
910 relocations: &[Relocation],
911 symbols: &[Symbol],
912 extensions: powerpc::Extensions,
913) -> Vec<Relocation> {
914 use powerpc::{Argument, InsIter, Opcode};
915 let mut visited_ins_addrs = BTreeSet::new();
916 let mut pool_reloc_for_addr = BTreeMap::new();
917 let mut ins_iters_with_gpr_state =
918 vec![(InsIter::new(code, func_address as u32, extensions), BTreeMap::new())];
919 let mut gpr_state_at_bctr = BTreeMap::new();
920 while let Some((ins_iter, mut gpr_pool_relocs)) = ins_iters_with_gpr_state.pop() {
921 for (cur_addr, ins) in ins_iter {
922 if visited_ins_addrs.contains(&cur_addr) {
923 break;
925 }
926 visited_ins_addrs.insert(cur_addr);
927
928 let simplified = ins.simplified();
929
930 let mut branch_dest = None;
932 for arg in simplified.args_iter() {
933 if let Argument::BranchDest(dest) = arg {
934 let dest = cur_addr.wrapping_add_signed(dest.0);
935 branch_dest = Some(dest);
936 break;
937 }
938 }
939 if let Some(branch_dest) = branch_dest
940 && branch_dest >= func_address as u32
941 && (branch_dest - func_address as u32) < code.len() as u32
942 {
943 let dest_offset_into_func = branch_dest - func_address as u32;
944 let dest_code_slice = &code[dest_offset_into_func as usize..];
945 match ins.op {
946 Opcode::Bc => {
947 ins_iters_with_gpr_state.push((
950 InsIter::new(dest_code_slice, branch_dest, extensions),
951 gpr_pool_relocs.clone(),
952 ));
953 }
955 Opcode::B => {
956 if simplified.mnemonic != "bl" {
957 ins_iters_with_gpr_state.push((
960 InsIter::new(dest_code_slice, branch_dest, extensions),
961 gpr_pool_relocs.clone(),
962 ));
963 break;
965 }
966 }
967 _ => unreachable!(),
968 }
969 }
970 if let Opcode::Bcctr = ins.op
971 && simplified.mnemonic == "bctr"
972 {
973 gpr_state_at_bctr.insert(cur_addr, gpr_pool_relocs.clone());
976 }
977
978 let reloc = relocations.iter().find(|r| (r.address as u32 & !3) == cur_addr);
980 if let Some(reloc) = reloc {
981 let args = &simplified.args;
984 match (ins.op, args[0], args[1], args[2]) {
985 (
986 Opcode::Addi,
988 Argument::GPR(addr_dst_gpr),
989 Argument::GPR(_addr_src_gpr),
990 Argument::Simm(_simm),
991 ) => {
992 gpr_pool_relocs.insert(addr_dst_gpr.0, reloc.clone());
993 }
994 (
995 Opcode::Ori,
997 Argument::GPR(addr_dst_gpr),
998 Argument::GPR(_addr_src_gpr),
999 Argument::Uimm(_uimm),
1000 ) => {
1001 gpr_pool_relocs.insert(addr_dst_gpr.0, reloc.clone());
1002 }
1003 (Opcode::B, _, _, _) => {
1004 if simplified.mnemonic == "bl" {
1005 gpr_pool_relocs.remove(&0);
1008 for gpr in 3..12 {
1009 gpr_pool_relocs.remove(&gpr);
1010 }
1011 }
1012 }
1013 _ => {
1014 clear_overwritten_gprs(ins, &mut gpr_pool_relocs);
1015 }
1016 }
1017 } else if let Some(pool_ref) = get_pool_reference_for_inst(ins, &simplified) {
1018 if let Some(pool_reloc) = gpr_pool_relocs.get(&pool_ref.addr_src_gpr.0) {
1021 if let Some(fake_pool_reloc) =
1022 make_fake_pool_reloc(pool_ref.addr_offset, cur_addr, pool_reloc, symbols)
1023 {
1024 pool_reloc_for_addr.insert(cur_addr, fake_pool_reloc);
1025 }
1026 if let Some(addr_dst_gpr) = pool_ref.addr_dst_gpr {
1027 let mut new_reloc = pool_reloc.clone();
1036 new_reloc.addend += pool_ref.addr_offset;
1037 gpr_pool_relocs.insert(addr_dst_gpr.0, new_reloc);
1038 } else {
1039 clear_overwritten_gprs(ins, &mut gpr_pool_relocs);
1040 }
1041 } else {
1042 clear_overwritten_gprs(ins, &mut gpr_pool_relocs);
1043 }
1044 } else {
1045 clear_overwritten_gprs(ins, &mut gpr_pool_relocs);
1046 }
1047 }
1048
1049 if ins_iters_with_gpr_state.is_empty() {
1054 let unseen_addrs = (func_address as u32..func_address as u32 + code.len() as u32)
1055 .step_by(4)
1056 .filter(|addr| !visited_ins_addrs.contains(addr));
1057 for unseen_addr in unseen_addrs {
1058 let prev_bctr_gpr_state = gpr_state_at_bctr
1059 .iter()
1060 .filter(|&(&addr, _)| addr < unseen_addr)
1061 .min_by_key(|&(&addr, _)| addr)
1062 .map(|(_, gpr_state)| gpr_state);
1063 if let Some(gpr_pool_relocs) = prev_bctr_gpr_state {
1064 let dest_offset_into_func = unseen_addr - func_address as u32;
1065 let dest_code_slice = &code[dest_offset_into_func as usize..];
1066 ins_iters_with_gpr_state.push((
1067 InsIter::new(dest_code_slice, unseen_addr, extensions),
1068 gpr_pool_relocs.clone(),
1069 ));
1070 break;
1071 }
1072 }
1073 }
1074 }
1075
1076 pool_reloc_for_addr.values().cloned().collect()
1077}