Skip to content

Commit 7cc6ed2

Browse files
authored
Add support for x86 indirect tables (#251)
* Add support for x86 indirect tables Closes #239 * Fix formatting issues in x86.rs * Add x86 indirect table test
1 parent 532b684 commit 7cc6ed2

File tree

6 files changed

+16469
-0
lines changed

6 files changed

+16469
-0
lines changed

objdiff-core/src/arch/x86.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,40 @@ impl Arch for ArchX86 {
124124
opcode: DATA_OPCODE,
125125
branch_dest: None,
126126
});
127+
127128
reloc_iter.next();
129+
130+
// support .byte arrays after jump tables (they're typically known as indirect tables)
131+
132+
let indirect_array_address = address.wrapping_add(size as u64);
133+
let indirect_array_pos = decoder.position();
134+
135+
let max_size = code.len().saturating_sub(indirect_array_pos);
136+
137+
let indirect_array_size = reloc_iter
138+
.peek()
139+
.map(|next_reloc| {
140+
next_reloc.address.saturating_sub(indirect_array_address)
141+
as usize
142+
})
143+
.unwrap_or(max_size)
144+
.min(max_size);
145+
146+
if indirect_array_size > 0 {
147+
for i in 0..indirect_array_size {
148+
out.push(InstructionRef {
149+
address: indirect_array_address + i as u64,
150+
size: 1,
151+
opcode: DATA_OPCODE,
152+
branch_dest: None,
153+
});
154+
}
155+
// move decoder to after the array (there can be multiple jump+indirect tables in one function)
156+
let _ =
157+
decoder.set_position(indirect_array_pos + indirect_array_size);
158+
decoder.set_ip(indirect_array_address + indirect_array_size as u64);
159+
}
160+
128161
continue 'outer;
129162
}
130163
}
@@ -156,6 +189,7 @@ impl Arch for ArchX86 {
156189
) -> Result<()> {
157190
if resolved.ins_ref.opcode == DATA_OPCODE {
158191
let (mnemonic, imm) = match resolved.ins_ref.size {
192+
1 => (".byte", resolved.code[0] as u64),
159193
2 => (".word", self.endianness.read_u16_bytes(resolved.code.try_into()?) as u64),
160194
4 => (".dword", self.endianness.read_u32_bytes(resolved.code.try_into()?) as u64),
161195
_ => bail!("Unsupported x86 inline data size {}", resolved.ins_ref.size),
@@ -791,4 +825,33 @@ mod test {
791825
.unwrap();
792826
assert_eq!(parts, &[InstructionPart::opcode("call", opcode), InstructionPart::reloc()]);
793827
}
828+
829+
#[test]
830+
fn test_display_1_byte_inline_data() {
831+
let arch = ArchX86 { arch: Architecture::X86, endianness: object::Endianness::Little };
832+
let code = [0xAB];
833+
let mut parts = Vec::new();
834+
arch.display_instruction(
835+
ResolvedInstructionRef {
836+
ins_ref: InstructionRef {
837+
address: 0x1234,
838+
size: 1,
839+
opcode: DATA_OPCODE,
840+
branch_dest: None,
841+
},
842+
code: &code,
843+
..Default::default()
844+
},
845+
&DiffObjConfig::default(),
846+
&mut |part| {
847+
parts.push(part.into_static());
848+
Ok(())
849+
},
850+
)
851+
.unwrap();
852+
assert_eq!(parts, &[
853+
InstructionPart::opcode(".byte", DATA_OPCODE),
854+
InstructionPart::unsigned(0xABu64),
855+
]);
856+
}
794857
}

objdiff-core/tests/arch_x86.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,21 @@ fn read_x86_local_labels() {
104104
.unwrap();
105105
insta::assert_debug_snapshot!(obj);
106106
}
107+
108+
#[test]
109+
#[cfg(feature = "x86")]
110+
fn read_x86_indirect_table() {
111+
let diff_config = diff::DiffObjConfig::default();
112+
let obj = obj::read::parse(
113+
include_object!("data/x86/indirect_table.obj"),
114+
&diff_config,
115+
diff::DiffSide::Base,
116+
)
117+
.unwrap();
118+
insta::assert_debug_snapshot!(obj);
119+
let symbol_idx = obj.symbols.iter().position(|s| s.name == "?process@@YAHHHH@Z").unwrap();
120+
let diff = diff::code::no_diff_code(&obj, symbol_idx, &diff_config).unwrap();
121+
insta::assert_debug_snapshot!(diff.instruction_rows);
122+
let output = common::display_diff(&obj, &diff, symbol_idx, &diff_config);
123+
insta::assert_snapshot!(output);
124+
}
4.8 KB
Binary file not shown.

0 commit comments

Comments
 (0)