1use alloc::{boxed::Box, format, vec::Vec};
2use core::cmp::Ordering;
3
4use anyhow::{Context, Result, anyhow, bail};
5use iced_x86::{
6 Decoder, DecoderOptions, DecoratorKind, FormatterOutput, FormatterTextKind, GasFormatter,
7 Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind, Register,
8};
9use object::{Endian as _, Object as _, ObjectSection as _, elf, pe};
10
11use crate::{
12 arch::{Arch, OPCODE_DATA, RelocationOverride, RelocationOverrideTarget},
13 diff::{DiffObjConfig, X86Formatter, display::InstructionPart},
14 obj::{InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef, Section, Symbol},
15};
16
17#[derive(Debug)]
18pub struct ArchX86 {
19 arch: Architecture,
20 endianness: object::Endianness,
21}
22
23#[derive(Debug)]
24enum Architecture {
25 X86,
26 X86_64,
27}
28
29impl ArchX86 {
30 pub fn new(object: &object::File) -> Result<Self> {
31 let arch = match object.architecture() {
32 object::Architecture::I386 => Architecture::X86,
33 object::Architecture::X86_64 => Architecture::X86_64,
34 _ => bail!("Unsupported architecture for ArchX86: {:?}", object.architecture()),
35 };
36 Ok(Self { arch, endianness: object.endianness() })
37 }
38
39 fn decoder<'a>(&self, code: &'a [u8], address: u64) -> Decoder<'a> {
40 Decoder::with_ip(
41 match self.arch {
42 Architecture::X86 => 32,
43 Architecture::X86_64 => 64,
44 },
45 code,
46 address,
47 DecoderOptions::NONE,
48 )
49 }
50
51 fn formatter(&self, diff_config: &DiffObjConfig) -> Box<dyn iced_x86::Formatter> {
52 let mut formatter: Box<dyn iced_x86::Formatter> = match diff_config.x86_formatter {
53 X86Formatter::Intel => Box::new(IntelFormatter::new()),
54 X86Formatter::Gas => Box::new(GasFormatter::new()),
55 X86Formatter::Nasm => Box::new(NasmFormatter::new()),
56 X86Formatter::Masm => Box::new(MasmFormatter::new()),
57 };
58 formatter.options_mut().set_space_after_operand_separator(diff_config.space_between_args);
59 formatter
60 }
61
62 fn reloc_size(&self, flags: RelocationFlags) -> Option<usize> {
63 match self.arch {
64 Architecture::X86 => match flags {
65 RelocationFlags::Coff(typ) => match typ {
66 pe::IMAGE_REL_I386_DIR16 | pe::IMAGE_REL_I386_REL16 => Some(2),
67 pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32 => Some(4),
68 _ => None,
69 },
70 RelocationFlags::Elf(typ) => match typ {
71 elf::R_386_32
72 | elf::R_386_PC32
73 | elf::R_386_GOT32
74 | elf::R_386_PLT32
75 | elf::R_386_GOTOFF
76 | elf::R_386_GOTPC => Some(4),
77 elf::R_386_16 => Some(2),
78 _ => None,
79 },
80 },
81 Architecture::X86_64 => match flags {
82 RelocationFlags::Coff(typ) => match typ {
83 pe::IMAGE_REL_AMD64_ADDR32NB
84 | pe::IMAGE_REL_AMD64_REL32
85 | pe::IMAGE_REL_AMD64_REL32_1
86 | pe::IMAGE_REL_AMD64_REL32_2
87 | pe::IMAGE_REL_AMD64_REL32_3
88 | pe::IMAGE_REL_AMD64_REL32_4
89 | pe::IMAGE_REL_AMD64_REL32_5
90 | pe::IMAGE_REL_AMD64_SECREL => Some(4),
91 pe::IMAGE_REL_AMD64_ADDR64 => Some(8),
92 _ => None,
93 },
94 RelocationFlags::Elf(typ) => match typ {
95 elf::R_X86_64_PC32 => Some(4),
96 elf::R_X86_64_64 => Some(8),
97 _ => None,
98 },
99 },
100 }
101 }
102}
103
104impl Arch for ArchX86 {
105 fn scan_instructions_internal(
106 &self,
107 address: u64,
108 code: &[u8],
109 _section_index: usize,
110 relocations: &[Relocation],
111 _diff_config: &DiffObjConfig,
112 ) -> Result<Vec<InstructionRef>> {
113 let mut out = Vec::with_capacity(code.len() / 2);
114 let mut decoder = self.decoder(code, address);
115 let mut instruction = Instruction::default();
116 let mut reloc_iter = relocations.iter().peekable();
117 'outer: while decoder.can_decode() {
118 let address = decoder.ip();
119 while let Some(reloc) = reloc_iter.peek() {
120 match reloc.address.cmp(&address) {
121 Ordering::Less => {
122 reloc_iter.next();
123 }
124 Ordering::Equal => {
125 let size = self.reloc_size(reloc.flags).with_context(|| {
127 format!("Unsupported inline x86 relocation {:?}", reloc.flags)
128 })?;
129 if decoder.set_position(decoder.position() + size).is_ok() {
130 decoder.set_ip(address + size as u64);
131 out.push(InstructionRef {
132 address,
133 size: size as u8,
134 opcode: OPCODE_DATA,
135 branch_dest: None,
136 });
137
138 reloc_iter.next();
139
140 let indirect_array_address = address.wrapping_add(size as u64);
143 let indirect_array_pos = decoder.position();
144
145 let max_size = code.len().saturating_sub(indirect_array_pos);
146
147 let indirect_array_size = reloc_iter
148 .peek()
149 .map(|next_reloc| {
150 next_reloc.address.saturating_sub(indirect_array_address)
151 as usize
152 })
153 .unwrap_or(max_size)
154 .min(max_size);
155
156 if indirect_array_size > 0 {
157 for i in 0..indirect_array_size {
158 out.push(InstructionRef {
159 address: indirect_array_address + i as u64,
160 size: 1,
161 opcode: OPCODE_DATA,
162 branch_dest: None,
163 });
164 }
165 let _ =
167 decoder.set_position(indirect_array_pos + indirect_array_size);
168 decoder.set_ip(indirect_array_address + indirect_array_size as u64);
169 }
170
171 continue 'outer;
172 }
173 }
174 Ordering::Greater => break,
175 }
176 }
177 decoder.decode_out(&mut instruction);
178 let branch_dest = match instruction.op0_kind() {
179 OpKind::NearBranch16 => Some(instruction.near_branch16() as u64),
180 OpKind::NearBranch32 => Some(instruction.near_branch32() as u64),
181 OpKind::NearBranch64 => Some(instruction.near_branch64()),
182 _ => None,
183 };
184 out.push(InstructionRef {
185 address,
186 size: instruction.len() as u8,
187 opcode: instruction.mnemonic() as u16,
188 branch_dest,
189 });
190 }
191 if matches!(self.arch, Architecture::X86_64) {
193 while out.last().is_some_and(|i| i.opcode == iced_x86::Mnemonic::Int3 as u16) {
194 out.pop();
195 }
196 }
197 Ok(out)
198 }
199
200 fn display_instruction(
201 &self,
202 resolved: ResolvedInstructionRef,
203 diff_config: &DiffObjConfig,
204 cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
205 ) -> Result<()> {
206 if resolved.ins_ref.opcode == OPCODE_DATA {
207 let (mnemonic, imm) = match resolved.ins_ref.size {
208 1 => (".byte", resolved.code[0] as u64),
209 2 => (".word", self.endianness.read_u16(resolved.code.try_into()?) as u64),
210 4 => (".dword", self.endianness.read_u32(resolved.code.try_into()?) as u64),
211 _ => bail!("Unsupported x86 inline data size {}", resolved.ins_ref.size),
212 };
213 cb(InstructionPart::opcode(mnemonic, OPCODE_DATA))?;
214 if resolved.relocation.is_some() {
215 cb(InstructionPart::reloc())?;
216 } else {
217 cb(InstructionPart::unsigned(imm))?;
218 }
219 return Ok(());
220 }
221
222 let mut decoder = self.decoder(resolved.code, resolved.ins_ref.address);
223 let mut formatter = self.formatter(diff_config);
224 let mut instruction = Instruction::default();
225 decoder.decode_out(&mut instruction);
226
227 let mut reloc_replace = None;
232 if let Some(reloc) = resolved.relocation {
233 const PLACEHOLDER: u64 = 0x7BDE3E7D; let reloc_offset = reloc.relocation.address - resolved.ins_ref.address;
235 let reloc_size = self.reloc_size(reloc.relocation.flags).unwrap_or(usize::MAX);
236 let offsets = decoder.get_constant_offsets(&instruction);
237 if reloc_offset == offsets.displacement_offset() as u64
238 && reloc_size == offsets.displacement_size()
239 {
240 instruction.set_memory_displacement64(PLACEHOLDER);
241 reloc_replace = Some((OpKind::Memory, 4, PLACEHOLDER));
243 } else if reloc_offset == offsets.immediate_offset() as u64
244 && reloc_size == offsets.immediate_size()
245 {
246 let is_branch = matches!(
247 instruction.op0_kind(),
248 OpKind::NearBranch16 | OpKind::NearBranch32 | OpKind::NearBranch64
249 );
250 let op_kind = if is_branch {
251 instruction.op0_kind()
252 } else {
253 match reloc_size {
254 2 => OpKind::Immediate16,
255 4 => OpKind::Immediate32,
256 8 => OpKind::Immediate64,
257 _ => OpKind::default(),
258 }
259 };
260 if is_branch {
261 instruction.set_near_branch64(PLACEHOLDER);
262 } else {
263 instruction.set_immediate32(PLACEHOLDER as u32);
264 }
265 reloc_replace = Some((op_kind, reloc_size, PLACEHOLDER));
266 }
267 }
268
269 let mut output =
270 InstructionFormatterOutput { cb, reloc_replace, error: None, skip_next: false };
271 formatter.format(&instruction, &mut output);
272 if let Some(error) = output.error.take() {
273 return Err(error);
274 }
275 Ok(())
276 }
277
278 fn relocation_override(
279 &self,
280 _file: &object::File<'_>,
281 section: &object::Section,
282 address: u64,
283 relocation: &object::Relocation,
284 ) -> Result<Option<RelocationOverride>> {
285 if !relocation.has_implicit_addend() {
286 return Ok(None);
287 }
288 let addend = match self.arch {
289 Architecture::X86 => match relocation.flags() {
290 object::RelocationFlags::Coff {
291 typ: pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32,
292 }
293 | object::RelocationFlags::Elf {
294 r_type:
295 elf::R_386_32
296 | elf::R_386_PC32
297 | elf::R_386_GOT32
298 | elf::R_386_PLT32
299 | elf::R_386_GOTOFF
300 | elf::R_386_GOTPC,
301 } => {
302 let data =
303 section.data()?[address as usize..address as usize + 4].try_into()?;
304 self.endianness.read_i32(data) as i64
305 }
306 flags => bail!("Unsupported x86 implicit relocation {flags:?}"),
307 },
308 Architecture::X86_64 => match relocation.flags() {
309 object::RelocationFlags::Coff {
310 typ:
311 pe::IMAGE_REL_AMD64_ADDR32NB
312 | pe::IMAGE_REL_AMD64_REL32
313 | pe::IMAGE_REL_AMD64_REL32_1
314 | pe::IMAGE_REL_AMD64_REL32_2
315 | pe::IMAGE_REL_AMD64_REL32_3
316 | pe::IMAGE_REL_AMD64_REL32_4
317 | pe::IMAGE_REL_AMD64_REL32_5
318 | pe::IMAGE_REL_AMD64_SECREL,
319 }
320 | object::RelocationFlags::Elf { r_type: elf::R_X86_64_32 | elf::R_X86_64_PC32 } => {
321 let data =
322 section.data()?[address as usize..address as usize + 4].try_into()?;
323 self.endianness.read_i32(data) as i64
324 }
325 object::RelocationFlags::Coff { typ: pe::IMAGE_REL_AMD64_ADDR64 }
326 | object::RelocationFlags::Elf { r_type: elf::R_X86_64_64 } => {
327 let data =
328 section.data()?[address as usize..address as usize + 8].try_into()?;
329 self.endianness.read_i64(data)
330 }
331 flags => bail!("Unsupported x86-64 implicit relocation {flags:?}"),
332 },
333 };
334 Ok(Some(RelocationOverride { target: RelocationOverrideTarget::Keep, addend }))
335 }
336
337 fn reloc_name(&self, flags: RelocationFlags) -> Option<&'static str> {
338 match self.arch {
339 Architecture::X86 => match flags {
340 RelocationFlags::Coff(typ) => match typ {
341 pe::IMAGE_REL_I386_DIR32 => Some("IMAGE_REL_I386_DIR32"),
342 pe::IMAGE_REL_I386_REL32 => Some("IMAGE_REL_I386_REL32"),
343 _ => None,
344 },
345 RelocationFlags::Elf(typ) => match typ {
346 elf::R_386_32 => Some("R_386_32"),
347 elf::R_386_PC32 => Some("R_386_PC32"),
348 elf::R_386_GOT32 => Some("R_386_GOT32"),
349 elf::R_386_PLT32 => Some("R_386_PLT32"),
350 elf::R_386_GOTOFF => Some("R_386_GOTOFF"),
351 elf::R_386_GOTPC => Some("R_386_GOTPC"),
352 elf::R_386_16 => Some("R_386_16"),
353 _ => None,
354 },
355 },
356 Architecture::X86_64 => match flags {
357 RelocationFlags::Coff(typ) => match typ {
358 pe::IMAGE_REL_AMD64_ADDR64 => Some("IMAGE_REL_AMD64_ADDR64"),
359 pe::IMAGE_REL_AMD64_ADDR32NB => Some("IMAGE_REL_AMD64_ADDR32NB"),
360 pe::IMAGE_REL_AMD64_REL32 => Some("IMAGE_REL_AMD64_REL32"),
361 pe::IMAGE_REL_AMD64_REL32_1 => Some("IMAGE_REL_AMD64_REL32_1"),
362 pe::IMAGE_REL_AMD64_REL32_2 => Some("IMAGE_REL_AMD64_REL32_2"),
363 pe::IMAGE_REL_AMD64_REL32_3 => Some("IMAGE_REL_AMD64_REL32_3"),
364 pe::IMAGE_REL_AMD64_REL32_4 => Some("IMAGE_REL_AMD64_REL32_4"),
365 pe::IMAGE_REL_AMD64_REL32_5 => Some("IMAGE_REL_AMD64_REL32_5"),
366 pe::IMAGE_REL_AMD64_SECREL => Some("IMAGE_REL_AMD64_SECREL"),
367 _ => None,
368 },
369 _ => None,
370 },
371 }
372 }
373
374 fn data_reloc_size(&self, flags: RelocationFlags) -> usize {
375 self.reloc_size(flags).unwrap_or(1)
376 }
377
378 fn infer_function_size(
379 &self,
380 symbol: &Symbol,
381 section: &Section,
382 next_address: u64,
383 ) -> Result<u64> {
384 let Ok(size) = (next_address - symbol.address).try_into() else {
385 return Ok(next_address.saturating_sub(symbol.address));
386 };
387 let Some(code) = section.data_range(symbol.address, size) else {
388 return Ok(0);
389 };
390 let mut decoder = self.decoder(code, symbol.address);
392 let mut instruction = Instruction::default();
393 let mut new_address = 0;
394 let mut reloc_iter = section.relocations.iter().peekable();
395 'outer: while decoder.can_decode() {
396 let address = decoder.ip();
397 while let Some(reloc) = reloc_iter.peek() {
398 match reloc.address.cmp(&address) {
399 Ordering::Less => {
400 reloc_iter.next();
401 }
402 Ordering::Equal => {
403 let reloc_size = self.reloc_size(reloc.flags).with_context(|| {
405 format!("Unsupported inline x86 relocation {:?}", reloc.flags)
406 })?;
407 if decoder.set_position(decoder.position() + reloc_size).is_ok() {
408 new_address = address + reloc_size as u64;
409 decoder.set_ip(new_address);
410 continue 'outer;
411 }
412 }
413 Ordering::Greater => break,
414 }
415 }
416 decoder.decode_out(&mut instruction);
417 if instruction.mnemonic() != iced_x86::Mnemonic::Nop {
418 new_address = instruction.next_ip();
419 }
420 }
421 Ok(new_address.saturating_sub(symbol.address))
422 }
423}
424
425struct InstructionFormatterOutput<'a> {
426 cb: &'a mut dyn FnMut(InstructionPart<'_>) -> Result<()>,
427 reloc_replace: Option<(OpKind, usize, u64)>,
428 error: Option<anyhow::Error>,
429 skip_next: bool,
430}
431
432impl InstructionFormatterOutput<'_> {
433 fn push_signed(&mut self, mut value: i64) {
434 if self.error.is_some() {
435 return;
436 }
437 if value < 0 {
440 value = value.wrapping_abs();
441 }
442 if let Err(e) = (self.cb)(InstructionPart::signed(value)) {
443 self.error = Some(e);
444 }
445 }
446}
447
448impl FormatterOutput for InstructionFormatterOutput<'_> {
449 fn write(&mut self, text: &str, kind: FormatterTextKind) {
450 if self.error.is_some() {
451 return;
452 }
453 if self.skip_next {
455 self.skip_next = false;
456 if kind == FormatterTextKind::Text && text == " " {
457 return;
458 }
459 }
460 match kind {
461 FormatterTextKind::Text | FormatterTextKind::Punctuation => {
462 if let Err(e) = (self.cb)(InstructionPart::basic(text)) {
463 self.error = Some(e);
464 }
465 }
466 FormatterTextKind::Prefix
467 | FormatterTextKind::Keyword
468 | FormatterTextKind::Operator => {
469 if let Err(e) = (self.cb)(InstructionPart::opaque(text)) {
470 self.error = Some(e);
471 }
472 }
473 _ => self.error = Some(anyhow!("x86: Unsupported FormatterTextKind {:?}", kind)),
474 }
475 }
476
477 fn write_mnemonic(&mut self, instruction: &Instruction, text: &str) {
478 if self.error.is_some() {
479 return;
480 }
481 if let Err(e) = (self.cb)(InstructionPart::opcode(text, instruction.mnemonic() as u16)) {
482 self.error = Some(e);
483 }
484 self.skip_next = true;
486 }
487
488 fn write_number(
489 &mut self,
490 instruction: &Instruction,
491 _operand: u32,
492 instruction_operand: Option<u32>,
493 _text: &str,
494 value: u64,
495 number_kind: NumberKind,
496 kind: FormatterTextKind,
497 ) {
498 if self.error.is_some() {
499 return;
500 }
501
502 if let (Some(operand), Some((target_op_kind, reloc_size, target_value))) =
503 (instruction_operand, self.reloc_replace)
504 {
505 #[allow(clippy::match_like_matches_macro)]
506 if instruction.op_kind(operand) == target_op_kind
507 && match (number_kind, reloc_size) {
508 (NumberKind::Int8 | NumberKind::UInt8, 1)
509 | (NumberKind::Int16 | NumberKind::UInt16, 2)
510 | (NumberKind::Int32 | NumberKind::UInt32, 4)
511 | (NumberKind::Int64 | NumberKind::UInt64, 4) | (NumberKind::Int64 | NumberKind::UInt64, 8) => true,
513 _ => false,
514 }
515 && value == target_value
516 {
517 if let Err(e) = (self.cb)(InstructionPart::reloc()) {
518 self.error = Some(e);
519 }
520 return;
521 }
522 }
523
524 if let FormatterTextKind::LabelAddress | FormatterTextKind::FunctionAddress = kind {
525 if let Err(e) = (self.cb)(InstructionPart::branch_dest(value)) {
526 self.error = Some(e);
527 }
528 return;
529 }
530
531 match number_kind {
532 NumberKind::Int8 => self.push_signed(value as i8 as i64),
533 NumberKind::Int16 => self.push_signed(value as i16 as i64),
534 NumberKind::Int32 => self.push_signed(value as i32 as i64),
535 NumberKind::Int64 => self.push_signed(value as i64),
536 NumberKind::UInt8 | NumberKind::UInt16 | NumberKind::UInt32 | NumberKind::UInt64 => {
537 if let Err(e) = (self.cb)(InstructionPart::unsigned(value)) {
538 self.error = Some(e);
539 }
540 }
541 }
542 }
543
544 fn write_decorator(
545 &mut self,
546 _instruction: &Instruction,
547 _operand: u32,
548 _instruction_operand: Option<u32>,
549 text: &str,
550 _decorator: DecoratorKind,
551 ) {
552 if self.error.is_some() {
553 return;
554 }
555 if let Err(e) = (self.cb)(InstructionPart::basic(text)) {
556 self.error = Some(e);
557 }
558 }
559
560 fn write_register(
561 &mut self,
562 _instruction: &Instruction,
563 _operand: u32,
564 _instruction_operand: Option<u32>,
565 text: &str,
566 _register: Register,
567 ) {
568 if self.error.is_some() {
569 return;
570 }
571 if let Err(e) = (self.cb)(InstructionPart::opaque(text)) {
572 self.error = Some(e);
573 }
574 }
575}
576
577#[cfg(test)]
578mod test {
579 use super::*;
580 use crate::obj::{Relocation, ResolvedRelocation};
581
582 #[test]
583 fn test_scan_instructions() {
584 let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little };
585 let code = [
586 0xc7, 0x85, 0x68, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x04, 0x85, 0x00,
587 0x00, 0x00, 0x00,
588 ];
589 let scanned =
590 arch.scan_instructions_internal(0, &code, 0, &[], &DiffObjConfig::default()).unwrap();
591 assert_eq!(scanned.len(), 2);
592 assert_eq!(scanned[0].address, 0);
593 assert_eq!(scanned[0].size, 10);
594 assert_eq!(scanned[0].opcode, iced_x86::Mnemonic::Mov as u16);
595 assert_eq!(scanned[0].branch_dest, None);
596 assert_eq!(scanned[1].address, 10);
597 assert_eq!(scanned[1].size, 7);
598 assert_eq!(scanned[1].opcode, iced_x86::Mnemonic::Mov as u16);
599 assert_eq!(scanned[1].branch_dest, None);
600 }
601
602 #[test]
603 fn test_process_instruction() {
604 let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little };
605 let code = [0xc7, 0x85, 0x68, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00];
606 let opcode = iced_x86::Mnemonic::Mov as u16;
607 let mut parts = Vec::new();
608 arch.display_instruction(
609 ResolvedInstructionRef {
610 ins_ref: InstructionRef { address: 0x1234, size: 10, opcode, branch_dest: None },
611 code: &code,
612 ..Default::default()
613 },
614 &DiffObjConfig::default(),
615 &mut |part| {
616 parts.push(part.into_static());
617 Ok(())
618 },
619 )
620 .unwrap();
621 assert_eq!(parts, &[
622 InstructionPart::opcode("mov", opcode),
623 InstructionPart::opaque("dword"),
624 InstructionPart::basic(" "),
625 InstructionPart::opaque("ptr"),
626 InstructionPart::basic(" "),
627 InstructionPart::basic("["),
628 InstructionPart::opaque("ebp"),
629 InstructionPart::opaque("-"),
630 InstructionPart::signed(152i64),
631 InstructionPart::basic("]"),
632 InstructionPart::basic(","),
633 InstructionPart::basic(" "),
634 InstructionPart::unsigned(0u64),
635 ]);
636 }
637
638 #[test]
639 fn test_process_instruction_with_reloc_1() {
640 let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little };
641 let code = [0xc7, 0x85, 0x68, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00];
642 let opcode = iced_x86::Mnemonic::Mov as u16;
643 let mut parts = Vec::new();
644 arch.display_instruction(
645 ResolvedInstructionRef {
646 ins_ref: InstructionRef { address: 0x1234, size: 10, opcode, branch_dest: None },
647 code: &code,
648 relocation: Some(ResolvedRelocation {
649 relocation: &Relocation {
650 flags: RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32),
651 address: 0x1234 + 6,
652 target_symbol: 0,
653 addend: 0,
654 },
655 symbol: &Default::default(),
656 }),
657 ..Default::default()
658 },
659 &DiffObjConfig::default(),
660 &mut |part| {
661 parts.push(part.into_static());
662 Ok(())
663 },
664 )
665 .unwrap();
666 assert_eq!(parts, &[
667 InstructionPart::opcode("mov", opcode),
668 InstructionPart::opaque("dword"),
669 InstructionPart::basic(" "),
670 InstructionPart::opaque("ptr"),
671 InstructionPart::basic(" "),
672 InstructionPart::basic("["),
673 InstructionPart::opaque("ebp"),
674 InstructionPart::opaque("-"),
675 InstructionPart::signed(152i64),
676 InstructionPart::basic("]"),
677 InstructionPart::basic(","),
678 InstructionPart::basic(" "),
679 InstructionPart::reloc(),
680 ]);
681 }
682
683 #[test]
684 fn test_process_instruction_with_reloc_2() {
685 let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little };
686 let code = [0x8b, 0x04, 0x85, 0x00, 0x00, 0x00, 0x00];
687 let opcode = iced_x86::Mnemonic::Mov as u16;
688 let mut parts = Vec::new();
689 arch.display_instruction(
690 ResolvedInstructionRef {
691 ins_ref: InstructionRef { address: 0x1234, size: 7, opcode, branch_dest: None },
692 code: &code,
693 relocation: Some(ResolvedRelocation {
694 relocation: &Relocation {
695 flags: RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32),
696 address: 0x1234 + 3,
697 target_symbol: 0,
698 addend: 0,
699 },
700 symbol: &Default::default(),
701 }),
702 ..Default::default()
703 },
704 &DiffObjConfig::default(),
705 &mut |part| {
706 parts.push(part.into_static());
707 Ok(())
708 },
709 )
710 .unwrap();
711 assert_eq!(parts, &[
712 InstructionPart::opcode("mov", opcode),
713 InstructionPart::opaque("eax"),
714 InstructionPart::basic(","),
715 InstructionPart::basic(" "),
716 InstructionPart::basic("["),
717 InstructionPart::opaque("eax"),
718 InstructionPart::opaque("*"),
719 InstructionPart::signed(4),
720 InstructionPart::opaque("+"),
721 InstructionPart::reloc(),
722 InstructionPart::basic("]"),
723 ]);
724 }
725
726 #[test]
727 fn test_process_instruction_with_reloc_3() {
728 let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little };
729 let code = [0xe8, 0x00, 0x00, 0x00, 0x00];
730 let opcode = iced_x86::Mnemonic::Call as u16;
731 let mut parts = Vec::new();
732 arch.display_instruction(
733 ResolvedInstructionRef {
734 ins_ref: InstructionRef { address: 0x1234, size: 5, opcode, branch_dest: None },
735 code: &code,
736 relocation: Some(ResolvedRelocation {
737 relocation: &Relocation {
738 flags: RelocationFlags::Coff(pe::IMAGE_REL_I386_REL32),
739 address: 0x1234 + 1,
740 target_symbol: 0,
741 addend: 0,
742 },
743 symbol: &Default::default(),
744 }),
745 ..Default::default()
746 },
747 &DiffObjConfig::default(),
748 &mut |part| {
749 parts.push(part.into_static());
750 Ok(())
751 },
752 )
753 .unwrap();
754 assert_eq!(parts, &[InstructionPart::opcode("call", opcode), InstructionPart::reloc()]);
755 }
756
757 #[test]
758 fn test_process_instruction_with_reloc_4() {
759 let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little };
760 let code = [0x8b, 0x15, 0xa4, 0x21, 0x7e, 0x00];
761 let opcode = iced_x86::Mnemonic::Mov as u16;
762 let mut parts = Vec::new();
763 arch.display_instruction(
764 ResolvedInstructionRef {
765 ins_ref: InstructionRef { address: 0x1234, size: 6, opcode, branch_dest: None },
766 code: &code,
767 relocation: Some(ResolvedRelocation {
768 relocation: &Relocation {
769 flags: RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32),
770 address: 0x1234 + 2,
771 target_symbol: 0,
772 addend: 0,
773 },
774 symbol: &Default::default(),
775 }),
776 ..Default::default()
777 },
778 &DiffObjConfig::default(),
779 &mut |part| {
780 parts.push(part.into_static());
781 Ok(())
782 },
783 )
784 .unwrap();
785 assert_eq!(parts, &[
786 InstructionPart::opcode("mov", opcode),
787 InstructionPart::opaque("edx"),
788 InstructionPart::basic(","),
789 InstructionPart::basic(" "),
790 InstructionPart::basic("["),
791 InstructionPart::reloc(),
792 InstructionPart::basic("]"),
793 ]);
794 }
795
796 #[test]
797 fn test_process_x86_64_instruction_with_reloc_1() {
798 let arch = ArchX86 { arch: Architecture::X86_64, endianness: object::Endianness::Little };
799 let code = [0x48, 0x8b, 0x05, 0x00, 0x00, 0x00, 0x00];
800 let opcode = iced_x86::Mnemonic::Mov as u16;
801 let mut parts = Vec::new();
802 arch.display_instruction(
803 ResolvedInstructionRef {
804 ins_ref: InstructionRef { address: 0x1234, size: 7, opcode, branch_dest: None },
805 code: &code,
806 relocation: Some(ResolvedRelocation {
807 relocation: &Relocation {
808 flags: RelocationFlags::Coff(pe::IMAGE_REL_AMD64_REL32),
809 address: 0x1234 + 3,
810 target_symbol: 0,
811 addend: 0,
812 },
813 symbol: &Default::default(),
814 }),
815 ..Default::default()
816 },
817 &DiffObjConfig::default(),
818 &mut |part| {
819 parts.push(part.into_static());
820 Ok(())
821 },
822 )
823 .unwrap();
824 assert_eq!(parts, &[
825 InstructionPart::opcode("mov", opcode),
826 InstructionPart::opaque("rax"),
827 InstructionPart::basic(","),
828 InstructionPart::basic(" "),
829 InstructionPart::basic("["),
830 InstructionPart::reloc(),
831 InstructionPart::basic("]"),
832 ]);
833 }
834
835 #[test]
836 fn test_process_x86_64_instruction_with_reloc_2() {
837 let arch = ArchX86 { arch: Architecture::X86_64, endianness: object::Endianness::Little };
838 let code = [0xe8, 0x00, 0x00, 0x00, 0x00];
839 let opcode = iced_x86::Mnemonic::Call as u16;
840 let mut parts = Vec::new();
841 arch.display_instruction(
842 ResolvedInstructionRef {
843 ins_ref: InstructionRef { address: 0x1234, size: 5, opcode, branch_dest: None },
844 code: &code,
845 relocation: Some(ResolvedRelocation {
846 relocation: &Relocation {
847 flags: RelocationFlags::Coff(pe::IMAGE_REL_AMD64_REL32),
848 address: 0x1234 + 1,
849 target_symbol: 0,
850 addend: 0,
851 },
852 symbol: &Default::default(),
853 }),
854 ..Default::default()
855 },
856 &DiffObjConfig::default(),
857 &mut |part| {
858 parts.push(part.into_static());
859 Ok(())
860 },
861 )
862 .unwrap();
863 assert_eq!(parts, &[InstructionPart::opcode("call", opcode), InstructionPart::reloc()]);
864 }
865
866 #[test]
867 fn test_display_1_byte_inline_data() {
868 let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little };
869 let code = [0xAB];
870 let mut parts = Vec::new();
871 arch.display_instruction(
872 ResolvedInstructionRef {
873 ins_ref: InstructionRef {
874 address: 0x1234,
875 size: 1,
876 opcode: OPCODE_DATA,
877 branch_dest: None,
878 },
879 code: &code,
880 ..Default::default()
881 },
882 &DiffObjConfig::default(),
883 &mut |part| {
884 parts.push(part.into_static());
885 Ok(())
886 },
887 )
888 .unwrap();
889 assert_eq!(parts, &[
890 InstructionPart::opcode(".byte", OPCODE_DATA),
891 InstructionPart::unsigned(0xABu64),
892 ]);
893 }
894}