x86: Support inline data for jumptables

This commit is contained in:
Luke Street
2025-03-19 18:54:34 -06:00
parent 6768df9d80
commit e735adbd3d
12 changed files with 995 additions and 8 deletions
+2 -1
View File
@@ -14,7 +14,7 @@ use crate::{
arch::Arch,
diff::{ArmArchVersion, ArmR9Usage, DiffObjConfig, display::InstructionPart},
obj::{
InstructionRef, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation,
InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation,
ScannedInstruction, SymbolFlag, SymbolFlagSet, SymbolKind,
},
};
@@ -183,6 +183,7 @@ impl Arch for ArchArm {
address: u64,
code: &[u8],
section_index: usize,
_relocations: &[Relocation],
diff_config: &DiffObjConfig,
) -> Result<Vec<ScannedInstruction>> {
let start_addr = address as u32;
+2 -1
View File
@@ -17,7 +17,7 @@ use crate::{
arch::Arch,
diff::{DiffObjConfig, display::InstructionPart},
obj::{
InstructionRef, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation,
InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef, ResolvedRelocation,
ScannedInstruction,
},
};
@@ -35,6 +35,7 @@ impl Arch for ArchArm64 {
address: u64,
code: &[u8],
_section_index: usize,
_relocations: &[Relocation],
_diff_config: &DiffObjConfig,
) -> Result<Vec<ScannedInstruction>> {
let start_address = address;
+1
View File
@@ -194,6 +194,7 @@ impl Arch for ArchMips {
address: u64,
code: &[u8],
_section_index: usize,
_relocations: &[Relocation],
diff_config: &DiffObjConfig,
) -> Result<Vec<ScannedInstruction>> {
let instruction_flags = self.instruction_flags(diff_config);
+2
View File
@@ -170,6 +170,7 @@ pub trait Arch: Send + Sync + Debug {
address: u64,
code: &[u8],
section_index: usize,
relocations: &[Relocation],
diff_config: &DiffObjConfig,
) -> Result<Vec<ScannedInstruction>>;
@@ -301,6 +302,7 @@ impl Arch for ArchDummy {
_address: u64,
_code: &[u8],
_section_index: usize,
_relocations: &[Relocation],
_diff_config: &DiffObjConfig,
) -> Result<Vec<ScannedInstruction>> {
Ok(Vec::new())
+1
View File
@@ -87,6 +87,7 @@ impl Arch for ArchPpc {
address: u64,
code: &[u8],
_section_index: usize,
_relocations: &[Relocation],
_diff_config: &DiffObjConfig,
) -> Result<Vec<ScannedInstruction>> {
ensure!(code.len() & 3 == 0, "Code length must be a multiple of 4");
+52 -5
View File
@@ -1,6 +1,6 @@
use alloc::{boxed::Box, string::String, vec::Vec};
use anyhow::{Result, anyhow, bail};
use anyhow::{Context, Result, anyhow, bail};
use iced_x86::{
Decoder, DecoderOptions, DecoratorKind, FormatterOutput, FormatterTextKind, GasFormatter,
Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind, Register,
@@ -10,7 +10,9 @@ use object::{Endian as _, Object as _, ObjectSection as _, pe};
use crate::{
arch::Arch,
diff::{DiffObjConfig, X86Formatter, display::InstructionPart},
obj::{InstructionRef, RelocationFlags, ResolvedInstructionRef, ScannedInstruction},
obj::{
InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef, ScannedInstruction,
},
};
#[derive(Debug)]
@@ -80,18 +82,48 @@ impl ArchX86 {
}
}
const DATA_OPCODE: u16 = u16::MAX - 1;
impl Arch for ArchX86 {
fn scan_instructions(
&self,
address: u64,
code: &[u8],
_section_index: usize,
relocations: &[Relocation],
_diff_config: &DiffObjConfig,
) -> Result<Vec<ScannedInstruction>> {
let mut out = Vec::with_capacity(code.len() / 2);
let mut decoder = self.decoder(code, address);
let mut instruction = Instruction::default();
while decoder.can_decode() {
let mut reloc_iter = relocations.iter().peekable();
'outer: while decoder.can_decode() {
let address = decoder.ip();
while let Some(reloc) = reloc_iter.peek() {
if reloc.address < address {
reloc_iter.next();
} else if reloc.address == address {
// If the instruction starts at a relocation, it's inline data
let size = self.reloc_size(reloc.flags).with_context(|| {
format!("Unsupported inline x86 relocation {:?}", reloc.flags)
})?;
if decoder.set_position(decoder.position() + size).is_ok() {
decoder.set_ip(address + size as u64);
out.push(ScannedInstruction {
ins_ref: InstructionRef {
address,
size: size as u8,
opcode: DATA_OPCODE,
},
branch_dest: None,
});
reloc_iter.next();
continue 'outer;
}
} else {
break;
}
}
decoder.decode_out(&mut instruction);
let branch_dest = match instruction.op0_kind() {
OpKind::NearBranch16 => Some(instruction.near_branch16() as u64),
@@ -101,7 +133,7 @@ impl Arch for ArchX86 {
};
out.push(ScannedInstruction {
ins_ref: InstructionRef {
address: instruction.ip(),
address,
size: instruction.len() as u8,
opcode: instruction.mnemonic() as u16,
},
@@ -117,6 +149,21 @@ impl Arch for ArchX86 {
diff_config: &DiffObjConfig,
cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
) -> Result<()> {
if resolved.ins_ref.opcode == DATA_OPCODE {
let (mnemonic, imm) = match resolved.ins_ref.size {
2 => (".word", self.endianness.read_u16_bytes(resolved.code.try_into()?) as u64),
4 => (".dword", self.endianness.read_u32_bytes(resolved.code.try_into()?) as u64),
_ => bail!("Unsupported x86 inline data size {}", resolved.ins_ref.size),
};
cb(InstructionPart::opcode(mnemonic, DATA_OPCODE))?;
if resolved.relocation.is_some() {
cb(InstructionPart::reloc())?;
} else {
cb(InstructionPart::unsigned(imm))?;
}
return Ok(());
}
let mut decoder = self.decoder(resolved.code, resolved.ins_ref.address);
let mut formatter = self.formatter(diff_config);
let mut instruction = Instruction::default();
@@ -406,7 +453,7 @@ mod test {
0xc7, 0x85, 0x68, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x04, 0x85, 0x00,
0x00, 0x00, 0x00,
];
let scanned = arch.scan_instructions(0, &code, 0, &DiffObjConfig::default()).unwrap();
let scanned = arch.scan_instructions(0, &code, 0, &[], &DiffObjConfig::default()).unwrap();
assert_eq!(scanned.len(), 2);
assert_eq!(scanned[0].ins_ref.address, 0);
assert_eq!(scanned[0].ins_ref.size, 10);
+9 -1
View File
@@ -32,7 +32,13 @@ pub fn no_diff_code(
symbol.address + symbol.size
)
})?;
let ops = obj.arch.scan_instructions(symbol.address, data, section_index, diff_config)?;
let ops = obj.arch.scan_instructions(
symbol.address,
data,
section_index,
&section.relocations,
diff_config,
)?;
let mut instruction_rows = Vec::<InstructionDiffRow>::new();
for i in &ops {
instruction_rows
@@ -89,12 +95,14 @@ pub fn diff_code(
left_symbol.address,
left_data,
left_section_idx,
&left_section.relocations,
diff_config,
)?;
let right_ops = right_obj.arch.scan_instructions(
right_symbol.address,
right_data,
right_section_idx,
&right_section.relocations,
diff_config,
)?;
let (mut left_rows, mut right_rows) = diff_instructions(&left_ops, &right_ops)?;