1use alloc::{
2 borrow::Cow,
3 boxed::Box,
4 format,
5 string::{String, ToString},
6 vec::Vec,
7};
8use core::{
9 any::Any,
10 fmt::{self, Debug},
11};
12
13use anyhow::{Result, bail};
14use object::Endian as _;
15
16use crate::{
17 diff::{
18 ConfigEnum, DiffObjConfig, DiffSide, PreferredStringEncoding,
19 display::{ContextItem, HoverItem, InstructionPart},
20 },
21 obj::{
22 FlowAnalysisResult, InstructionArg, InstructionRef, Object, ParsedInstruction, Relocation,
23 RelocationFlags, ResolvedInstructionRef, ResolvedRelocation, ResolvedSymbol, Section,
24 Symbol, SymbolFlagSet, SymbolKind,
25 },
26 util::ReallySigned,
27};
28
29#[cfg(feature = "arm")]
30pub mod arm;
31#[cfg(feature = "arm64")]
32pub mod arm64;
33#[cfg(feature = "mips")]
34pub mod mips;
35#[cfg(feature = "ppc")]
36pub mod ppc;
37#[cfg(feature = "superh")]
38pub mod superh;
39#[cfg(feature = "x86")]
40pub mod x86;
41
42pub const OPCODE_INVALID: u16 = u16::MAX;
43pub const OPCODE_DATA: u16 = u16::MAX - 1;
44
45const SUPPORTED_ENCODINGS_WITH_NULL_TERM: [(&encoding_rs::Encoding, &str); 5] = [
46 (encoding_rs::UTF_8, "UTF-8"),
47 (encoding_rs::SHIFT_JIS, "Shift JIS"),
48 (encoding_rs::WINDOWS_1252, "Windows-1252"),
49 (encoding_rs::EUC_JP, "EUC-JP"),
50 (encoding_rs::BIG5, "Big5"),
51];
52const SUPPORTED_ENCODINGS_NO_NULL_TERM: [(&encoding_rs::Encoding, &str); 2] =
53 [(encoding_rs::UTF_16BE, "UTF-16BE"), (encoding_rs::UTF_16LE, "UTF-16LE")];
54
55#[derive(Debug, Clone, Default, PartialEq)]
56pub struct LiteralInfo {
57 pub literal: String,
58 pub label_override: Option<String>,
59 pub copy_string: Option<String>,
60 pub hidden: bool, pub is_string: bool,
62}
63
64impl LiteralInfo {
65 pub fn hidden(&self, diff_config: Option<&DiffObjConfig>) -> bool {
66 let Some(diff_config) = diff_config else {
67 return self.hidden;
68 };
69 if !self.is_string {
70 return self.hidden;
71 }
72 if diff_config.preferred_string_encoding == PreferredStringEncoding::Auto {
73 return self.hidden;
74 }
75 let Some(ref label) = self.label_override else {
76 return self.hidden;
77 };
78 *label != diff_config.preferred_string_encoding.name()
79 }
80}
81
82#[derive(PartialEq)]
84pub enum DataType {
85 Int8,
86 Int16,
87 Int32,
88 Int64,
89 Float,
90 Double,
91 Bytes,
92 String,
93}
94
95impl fmt::Display for DataType {
96 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97 f.write_str(match self {
98 DataType::Int8 => "Int8",
99 DataType::Int16 => "Int16",
100 DataType::Int32 => "Int32",
101 DataType::Int64 => "Int64",
102 DataType::Float => "Float",
103 DataType::Double => "Double",
104 DataType::Bytes => "Bytes",
105 DataType::String => "String",
106 })
107 }
108}
109
110impl DataType {
111 pub fn display_labels(&self, endian: object::Endianness, bytes: &[u8]) -> Vec<String> {
112 let mut strs = Vec::new();
113 for lit_info in self.display_literals(endian, bytes) {
114 let label = lit_info.label_override.unwrap_or_else(|| self.to_string());
115 strs.push(format!("{}: {:?}", label, lit_info.literal))
116 }
117 strs
118 }
119
120 pub fn display_literals(&self, endian: object::Endianness, bytes: &[u8]) -> Vec<LiteralInfo> {
121 let mut strs = Vec::new();
122 if self.required_len().is_some_and(|l| bytes.len() < l) {
123 log::warn!(
124 "Failed to display a symbol value for a symbol whose size is too small for instruction referencing it."
125 );
126 return strs;
127 }
128 let mut bytes = bytes;
129 if self.required_len().is_some_and(|l| bytes.len() > l) {
130 bytes = &bytes[0..self.required_len().unwrap()];
133 }
140
141 match self {
142 DataType::Int8 => {
143 let i = i8::from_ne_bytes(bytes.try_into().unwrap());
144 strs.push(LiteralInfo { literal: format!("{i:#x}"), ..Default::default() });
145
146 if i < 0 {
147 strs.push(LiteralInfo {
148 literal: format!("{:#x}", ReallySigned(i)),
149 ..Default::default()
150 });
151 }
152 }
153 DataType::Int16 => {
154 let i = endian.read_i16(bytes.try_into().unwrap());
155 strs.push(LiteralInfo { literal: format!("{i:#x}"), ..Default::default() });
156
157 if i < 0 {
158 strs.push(LiteralInfo {
159 literal: format!("{:#x}", ReallySigned(i)),
160 ..Default::default()
161 });
162 }
163 }
164 DataType::Int32 => {
165 let i = endian.read_i32(bytes.try_into().unwrap());
166 strs.push(LiteralInfo { literal: format!("{i:#x}"), ..Default::default() });
167
168 if i < 0 {
169 strs.push(LiteralInfo {
170 literal: format!("{:#x}", ReallySigned(i)),
171 ..Default::default()
172 });
173 }
174 }
175 DataType::Int64 => {
176 let i = endian.read_i64(bytes.try_into().unwrap());
177 strs.push(LiteralInfo { literal: format!("{i:#x}"), ..Default::default() });
178
179 if i < 0 {
180 strs.push(LiteralInfo {
181 literal: format!("{:#x}", ReallySigned(i)),
182 ..Default::default()
183 });
184 }
185 }
186 DataType::Float => {
187 let bytes: [u8; 4] = bytes.try_into().unwrap();
188 strs.push(LiteralInfo {
189 literal: format!("{:?}f", match endian {
190 object::Endianness::Little => f32::from_le_bytes(bytes),
191 object::Endianness::Big => f32::from_be_bytes(bytes),
192 }),
193 ..Default::default()
194 });
195 }
196 DataType::Double => {
197 let bytes: [u8; 8] = bytes.try_into().unwrap();
198 strs.push(LiteralInfo {
199 literal: format!("{:?}", match endian {
200 object::Endianness::Little => f64::from_le_bytes(bytes),
201 object::Endianness::Big => f64::from_be_bytes(bytes),
202 }),
203 ..Default::default()
204 });
205 }
206 DataType::Bytes => {
207 strs.push(LiteralInfo { literal: format!("{bytes:#?}"), ..Default::default() });
208 }
209 DataType::String => {
210 if let Some(nul_idx) = bytes.iter().position(|&c| c == b'\0') {
211 let str_bytes = &bytes[..nul_idx];
212 let (cow, _, had_errors) = encoding_rs::UTF_8.decode(str_bytes);
214 if !had_errors && cow.is_ascii() {
215 let string = format!("{cow}");
216 let copy_string = escape_special_ascii_characters(&string);
217 strs.push(LiteralInfo {
218 literal: string,
219 label_override: Some("ASCII".into()),
220 copy_string: Some(copy_string),
221 hidden: false,
222 is_string: true,
223 });
224 }
225 for (encoding, encoding_name) in SUPPORTED_ENCODINGS_WITH_NULL_TERM {
226 let (cow, _, had_errors) = encoding.decode(str_bytes);
227 if !had_errors {
228 let string = format!("{cow}");
229 let copy_string = escape_special_ascii_characters(&string);
230 let hidden = encoding.is_ascii_compatible() && cow.is_ascii();
232 strs.push(LiteralInfo {
233 literal: string,
234 label_override: Some(encoding_name.into()),
235 copy_string: Some(copy_string),
236 hidden,
237 is_string: true,
238 });
239 }
240 }
241 }
242
243 for (encoding, encoding_name) in SUPPORTED_ENCODINGS_NO_NULL_TERM {
244 let (cow, _, had_errors) = encoding.decode(bytes);
245 if had_errors {
246 continue;
247 }
248 let trimmed = cow.trim_end_matches('\0');
249 if !trimmed.is_empty() {
250 let copy_string = escape_special_ascii_characters(trimmed);
251 strs.push(LiteralInfo {
252 literal: trimmed.to_string(),
253 label_override: Some(encoding_name.into()),
254 copy_string: Some(copy_string),
255 hidden: false,
256 is_string: true,
257 });
258 }
259 }
260 }
261 }
262
263 strs
264 }
265
266 fn required_len(&self) -> Option<usize> {
267 match self {
268 DataType::Int8 => Some(1),
269 DataType::Int16 => Some(2),
270 DataType::Int32 => Some(4),
271 DataType::Int64 => Some(8),
272 DataType::Float => Some(4),
273 DataType::Double => Some(8),
274 DataType::Bytes => None,
275 DataType::String => None,
276 }
277 }
278}
279
280impl dyn Arch {
281 pub fn scan_instructions(
285 &self,
286 resolved: ResolvedSymbol,
287 diff_config: &DiffObjConfig,
288 ) -> Result<Vec<InstructionRef>> {
289 let mut result = self.scan_instructions_internal(
290 resolved.symbol.address,
291 resolved.data,
292 resolved.section_index,
293 &resolved.section.relocations,
294 diff_config,
295 )?;
296
297 let function_start = resolved.symbol.address;
298 let function_end = function_start + resolved.symbol.size;
299
300 for ins in result.iter_mut() {
302 if let Some(branch_dest) = ins.branch_dest
303 && (branch_dest < function_start || branch_dest >= function_end)
304 {
305 ins.branch_dest = None;
306 }
307 }
308
309 let mut ins_iter = result.iter_mut().peekable();
311 'outer: for reloc in resolved
312 .section
313 .relocations
314 .iter()
315 .skip_while(|r| r.address < function_start)
316 .take_while(|r| r.address < function_end)
317 {
318 let ins = loop {
319 let Some(ins) = ins_iter.peek_mut() else {
320 break 'outer;
321 };
322 if reloc.address < ins.address {
323 continue 'outer;
324 }
325 let ins = ins_iter.next().unwrap();
326 if reloc.address >= ins.address && reloc.address < ins.address + ins.size as u64 {
327 break ins;
328 }
329 };
330 ins.branch_dest = None;
332 let Some(target) = resolved.obj.symbols.get(reloc.target_symbol) else {
333 continue;
334 };
335 if target.section != Some(resolved.section_index) {
336 continue;
337 }
338 let Some(target_address) = target.address.checked_add_signed(reloc.addend) else {
339 continue;
340 };
341 if target_address >= function_start && target_address < function_end {
343 ins.branch_dest = Some(target_address);
344 }
345 }
346
347 Ok(result)
348 }
349
350 pub fn process_instruction(
354 &self,
355 resolved: ResolvedInstructionRef,
356 diff_config: &DiffObjConfig,
357 ) -> Result<ParsedInstruction> {
358 let mut mnemonic = None;
359 let mut args = Vec::with_capacity(8);
360 let mut relocation_emitted = false;
361 self.display_instruction(resolved, diff_config, &mut |part| {
362 match part {
363 InstructionPart::Opcode(m, _) => mnemonic = Some(Cow::Owned(m.into_owned())),
364 InstructionPart::Arg(arg) => {
365 if arg == InstructionArg::Reloc {
366 relocation_emitted = true;
367 if let Some(dest) = resolved.ins_ref.branch_dest {
369 args.push(InstructionArg::BranchDest(dest));
370 return Ok(());
371 }
372 }
373 args.push(arg.into_static());
374 }
375 _ => {}
376 }
377 Ok(())
378 })?;
379 if resolved.relocation.is_some() && !relocation_emitted {
382 args.push(InstructionArg::Reloc);
383 }
384 Ok(ParsedInstruction {
385 ins_ref: resolved.ins_ref,
386 mnemonic: mnemonic.unwrap_or_default(),
387 args,
388 })
389 }
390}
391
392pub trait Arch: Any + Debug + Send + Sync {
393 fn post_init(&mut self, _sections: &[Section], _symbols: &[Symbol], _symbol_indices: &[usize]) {
395 }
396
397 fn scan_instructions_internal(
404 &self,
405 address: u64,
406 code: &[u8],
407 section_index: usize,
408 relocations: &[Relocation],
409 diff_config: &DiffObjConfig,
410 ) -> Result<Vec<InstructionRef>>;
411
412 fn display_instruction(
417 &self,
418 resolved: ResolvedInstructionRef,
419 diff_config: &DiffObjConfig,
420 cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
421 ) -> Result<()>;
422
423 fn generate_pooled_relocations(
425 &self,
426 _address: u64,
427 _code: &[u8],
428 _relocations: &[Relocation],
429 _symbols: &[Symbol],
430 ) -> Vec<Relocation> {
431 Vec::new()
432 }
433
434 fn data_flow_analysis(
436 &self,
437 _obj: &Object,
438 _symbol: &Symbol,
439 _code: &[u8],
440 _relocations: &[Relocation],
441 ) -> Option<Box<dyn FlowAnalysisResult>> {
442 None
443 }
444
445 fn relocation_override(
446 &self,
447 _file: &object::File<'_>,
448 _section: &object::Section,
449 _address: u64,
450 _relocation: &object::Relocation,
451 ) -> Result<Option<RelocationOverride>> {
452 Ok(None)
453 }
454
455 fn reloc_name(&self, _flags: RelocationFlags) -> Option<&'static str> { None }
456
457 fn data_reloc_size(&self, flags: RelocationFlags) -> usize;
458
459 fn symbol_address(&self, address: u64, _kind: SymbolKind) -> u64 { address }
460
461 fn extra_symbol_flags(&self, _symbol: &object::Symbol) -> SymbolFlagSet {
462 SymbolFlagSet::default()
463 }
464
465 fn guess_data_type(
466 &self,
467 _ins: Option<ResolvedInstructionRef>,
468 _reloc: Option<ResolvedRelocation>,
469 _bytes: &[u8],
470 ) -> Option<DataType> {
471 None
472 }
473
474 fn guess_ins_data_type(&self, ins: ResolvedInstructionRef, bytes: &[u8]) -> Option<DataType> {
475 self.guess_data_type(Some(ins), ins.relocation, bytes)
476 }
477
478 fn symbol_hover(&self, _obj: &Object, _symbol_index: usize) -> Vec<HoverItem> { Vec::new() }
479
480 fn symbol_context(&self, _obj: &Object, _symbol_index: usize) -> Vec<ContextItem> { Vec::new() }
481
482 fn instruction_hover(
483 &self,
484 _obj: &Object,
485 _resolved: ResolvedInstructionRef,
486 ) -> Vec<HoverItem> {
487 Vec::new()
488 }
489
490 fn instruction_context(
491 &self,
492 _obj: &Object,
493 _resolved: ResolvedInstructionRef,
494 ) -> Vec<ContextItem> {
495 Vec::new()
496 }
497
498 fn infer_function_size(
499 &self,
500 symbol: &Symbol,
501 _section: &Section,
502 next_address: u64,
503 ) -> Result<u64> {
504 Ok(next_address.saturating_sub(symbol.address))
505 }
506}
507
508pub fn new_arch(object: &object::File, diff_side: DiffSide) -> Result<Box<dyn Arch>> {
509 use object::Object as _;
510 let _ = diff_side;
512
513 Ok(match object.architecture() {
514 #[cfg(feature = "ppc")]
515 object::Architecture::PowerPc | object::Architecture::PowerPc64 => {
516 Box::new(ppc::ArchPpc::new(object)?)
517 }
518 #[cfg(feature = "mips")]
519 object::Architecture::Mips => Box::new(mips::ArchMips::new(object, diff_side)?),
520 #[cfg(feature = "x86")]
521 object::Architecture::I386 | object::Architecture::X86_64 => {
522 Box::new(x86::ArchX86::new(object)?)
523 }
524 #[cfg(feature = "arm")]
525 object::Architecture::Arm => Box::new(arm::ArchArm::new(object)?),
526 #[cfg(feature = "arm64")]
527 object::Architecture::Aarch64 => Box::new(arm64::ArchArm64::new(object)?),
528 #[cfg(feature = "superh")]
529 object::Architecture::SuperH => Box::new(superh::ArchSuperH::new(object)?),
530 arch => bail!("Unsupported architecture: {arch:?}"),
531 })
532}
533
534#[derive(Debug, Default)]
535pub struct ArchDummy {}
536
537impl ArchDummy {
538 pub fn new() -> Box<Self> { Box::new(Self {}) }
539}
540
541impl Arch for ArchDummy {
542 fn scan_instructions_internal(
543 &self,
544 _address: u64,
545 _code: &[u8],
546 _section_index: usize,
547 _relocations: &[Relocation],
548 _diff_config: &DiffObjConfig,
549 ) -> Result<Vec<InstructionRef>> {
550 Ok(Vec::new())
551 }
552
553 fn display_instruction(
554 &self,
555 _resolved: ResolvedInstructionRef,
556 _diff_config: &DiffObjConfig,
557 _cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
558 ) -> Result<()> {
559 Ok(())
560 }
561
562 fn data_reloc_size(&self, _flags: RelocationFlags) -> usize { 0 }
563}
564
565#[derive(Debug, Clone, Copy, PartialEq, Eq)]
566pub enum RelocationOverrideTarget {
567 Keep,
568 Skip,
569 Symbol(object::SymbolIndex),
570 Section(object::SectionIndex),
571}
572
573#[derive(Debug, Clone, Copy, PartialEq, Eq)]
574pub struct RelocationOverride {
575 pub target: RelocationOverrideTarget,
576 pub addend: i64,
577}
578
579fn escape_special_ascii_characters(value: &str) -> String {
582 let mut escaped = String::new();
583 escaped.push('"');
584 for c in value.chars() {
585 if c.is_ascii() {
586 for e in c.escape_default() {
587 escaped.push(e);
588 }
589 } else {
590 escaped.push(c);
591 }
592 }
593 escaped.push('"');
594 escaped
595}