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