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, ResolvedSymbol, Section, Symbol, SymbolFlagSet,
24 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 _resolved: ResolvedInstructionRef,
468 _bytes: &[u8],
469 ) -> Option<DataType> {
470 None
471 }
472
473 fn symbol_hover(&self, _obj: &Object, _symbol_index: usize) -> Vec<HoverItem> { Vec::new() }
474
475 fn symbol_context(&self, _obj: &Object, _symbol_index: usize) -> Vec<ContextItem> { Vec::new() }
476
477 fn instruction_hover(
478 &self,
479 _obj: &Object,
480 _resolved: ResolvedInstructionRef,
481 ) -> Vec<HoverItem> {
482 Vec::new()
483 }
484
485 fn instruction_context(
486 &self,
487 _obj: &Object,
488 _resolved: ResolvedInstructionRef,
489 ) -> Vec<ContextItem> {
490 Vec::new()
491 }
492
493 fn infer_function_size(
494 &self,
495 symbol: &Symbol,
496 _section: &Section,
497 next_address: u64,
498 ) -> Result<u64> {
499 Ok(next_address.saturating_sub(symbol.address))
500 }
501}
502
503pub fn new_arch(object: &object::File, diff_side: DiffSide) -> Result<Box<dyn Arch>> {
504 use object::Object as _;
505 let _ = diff_side;
507
508 Ok(match object.architecture() {
509 #[cfg(feature = "ppc")]
510 object::Architecture::PowerPc | object::Architecture::PowerPc64 => {
511 Box::new(ppc::ArchPpc::new(object)?)
512 }
513 #[cfg(feature = "mips")]
514 object::Architecture::Mips => Box::new(mips::ArchMips::new(object, diff_side)?),
515 #[cfg(feature = "x86")]
516 object::Architecture::I386 | object::Architecture::X86_64 => {
517 Box::new(x86::ArchX86::new(object)?)
518 }
519 #[cfg(feature = "arm")]
520 object::Architecture::Arm => Box::new(arm::ArchArm::new(object)?),
521 #[cfg(feature = "arm64")]
522 object::Architecture::Aarch64 => Box::new(arm64::ArchArm64::new(object)?),
523 #[cfg(feature = "superh")]
524 object::Architecture::SuperH => Box::new(superh::ArchSuperH::new(object)?),
525 arch => bail!("Unsupported architecture: {arch:?}"),
526 })
527}
528
529#[derive(Debug, Default)]
530pub struct ArchDummy {}
531
532impl ArchDummy {
533 pub fn new() -> Box<Self> { Box::new(Self {}) }
534}
535
536impl Arch for ArchDummy {
537 fn scan_instructions_internal(
538 &self,
539 _address: u64,
540 _code: &[u8],
541 _section_index: usize,
542 _relocations: &[Relocation],
543 _diff_config: &DiffObjConfig,
544 ) -> Result<Vec<InstructionRef>> {
545 Ok(Vec::new())
546 }
547
548 fn display_instruction(
549 &self,
550 _resolved: ResolvedInstructionRef,
551 _diff_config: &DiffObjConfig,
552 _cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
553 ) -> Result<()> {
554 Ok(())
555 }
556
557 fn data_reloc_size(&self, _flags: RelocationFlags) -> usize { 0 }
558}
559
560#[derive(Debug, Clone, Copy, PartialEq, Eq)]
561pub enum RelocationOverrideTarget {
562 Keep,
563 Skip,
564 Symbol(object::SymbolIndex),
565 Section(object::SectionIndex),
566}
567
568#[derive(Debug, Clone, Copy, PartialEq, Eq)]
569pub struct RelocationOverride {
570 pub target: RelocationOverrideTarget,
571 pub addend: i64,
572}
573
574fn escape_special_ascii_characters(value: &str) -> String {
577 let mut escaped = String::new();
578 escaped.push('"');
579 for c in value.chars() {
580 if c.is_ascii() {
581 for e in c.escape_default() {
582 escaped.push(e);
583 }
584 } else {
585 escaped.push(c);
586 }
587 }
588 escaped.push('"');
589 escaped
590}