Skip to main content

objdiff_core/bindings/
diff.rs

1#![allow(clippy::useless_borrows_in_formatting)] // Generated serde code
2
3use alloc::{
4    string::{String, ToString},
5    vec,
6    vec::Vec,
7};
8use core::fmt::Write;
9
10use anyhow::Result;
11
12use crate::{
13    diff::{self, DiffObjConfig, display::InstructionPart},
14    obj::{self, Object, SymbolFlag},
15};
16
17// Protobuf diff types
18include!(concat!(env!("OUT_DIR"), "/objdiff.diff.rs"));
19#[cfg(feature = "serde")]
20include!(concat!(env!("OUT_DIR"), "/objdiff.diff.serde.rs"));
21
22impl DiffResult {
23    pub fn new(
24        left: Option<(&Object, &diff::ObjectDiff)>,
25        right: Option<(&Object, &diff::ObjectDiff)>,
26        diff_config: &DiffObjConfig,
27    ) -> Result<Self> {
28        Ok(Self {
29            left: left.map(|(obj, diff)| DiffObject::new(obj, diff, diff_config)).transpose()?,
30            right: right.map(|(obj, diff)| DiffObject::new(obj, diff, diff_config)).transpose()?,
31        })
32    }
33}
34
35impl DiffObject {
36    pub fn new(obj: &Object, diff: &diff::ObjectDiff, diff_config: &DiffObjConfig) -> Result<Self> {
37        let mut sections = Vec::with_capacity(obj.sections.len());
38        for (section_idx, section) in obj.sections.iter().enumerate() {
39            let section_diff = &diff.sections[section_idx];
40            sections.push(DiffSection::new(obj, section, section_diff));
41        }
42
43        let mut symbols = Vec::with_capacity(obj.symbols.len());
44        for (symbol_idx, symbol) in obj.symbols.iter().enumerate() {
45            let symbol_diff = &diff.symbols[symbol_idx];
46            symbols.push(DiffSymbol::new(obj, symbol_idx, symbol, symbol_diff, diff_config)?);
47        }
48
49        Ok(Self { sections, symbols })
50    }
51}
52
53impl DiffSection {
54    pub fn new(obj: &Object, section: &obj::Section, section_diff: &diff::SectionDiff) -> Self {
55        Self {
56            name: section.name.clone(),
57            kind: DiffSectionKind::from(section.kind) as i32,
58            size: section.size,
59            address: section.address,
60            match_percent: section_diff.match_percent,
61            data_diff: section_diff.data_diff.iter().map(DiffDataSegment::from).collect(),
62            reloc_diff: section_diff
63                .reloc_diff
64                .iter()
65                .map(|r| DiffDataRelocation::new(obj, r))
66                .collect(),
67        }
68    }
69}
70
71impl From<obj::SectionKind> for DiffSectionKind {
72    fn from(value: obj::SectionKind) -> Self {
73        match value {
74            obj::SectionKind::Unknown => DiffSectionKind::SectionUnknown,
75            obj::SectionKind::Code => DiffSectionKind::SectionCode,
76            obj::SectionKind::Data => DiffSectionKind::SectionData,
77            obj::SectionKind::Bss => DiffSectionKind::SectionBss,
78            obj::SectionKind::Common => DiffSectionKind::SectionCommon,
79        }
80    }
81}
82
83impl DiffSymbol {
84    pub fn new(
85        obj: &Object,
86        symbol_idx: usize,
87        symbol: &obj::Symbol,
88        symbol_diff: &diff::SymbolDiff,
89        diff_config: &DiffObjConfig,
90    ) -> Result<Self> {
91        // Convert instruction rows
92        let instructions = symbol_diff
93            .instruction_rows
94            .iter()
95            .map(|row| DiffInstructionRow::new(obj, symbol_idx, row, diff_config))
96            .collect::<Result<Vec<_>>>()?;
97
98        // Convert data diff - flatten DataDiffRow segments into a single list
99        let data_diff: Vec<DiffDataSegment> = symbol_diff
100            .data_rows
101            .iter()
102            .flat_map(|row| row.segments.iter().map(DiffDataSegment::from))
103            .collect();
104
105        Ok(Self {
106            // Symbol metadata
107            name: symbol.name.clone(),
108            demangled_name: symbol.demangled_name.clone(),
109            address: symbol.address,
110            size: symbol.size,
111            flags: symbol_flags(&symbol.flags),
112            kind: DiffSymbolKind::from(symbol.kind) as i32,
113            // Diff information
114            target_symbol: symbol_diff.target_symbol.map(|i| i as u32),
115            match_percent: symbol_diff.match_percent,
116            instructions,
117            data_diff,
118        })
119    }
120}
121
122impl From<obj::SymbolKind> for DiffSymbolKind {
123    fn from(value: obj::SymbolKind) -> Self {
124        match value {
125            obj::SymbolKind::Unknown => DiffSymbolKind::SymbolUnknown,
126            obj::SymbolKind::Function => DiffSymbolKind::SymbolFunction,
127            obj::SymbolKind::Object => DiffSymbolKind::SymbolObject,
128            obj::SymbolKind::Section => DiffSymbolKind::SymbolSection,
129        }
130    }
131}
132
133fn symbol_flags(flags: &obj::SymbolFlagSet) -> Option<DiffSymbolFlags> {
134    Some(DiffSymbolFlags {
135        global: flags.contains(SymbolFlag::Global),
136        local: flags.contains(SymbolFlag::Local),
137        weak: flags.contains(SymbolFlag::Weak),
138        common: flags.contains(SymbolFlag::Common),
139        hidden: flags.contains(SymbolFlag::Hidden),
140        ignored: flags.contains(SymbolFlag::Ignored),
141        size_inferred: flags.contains(SymbolFlag::SizeInferred),
142    })
143}
144
145impl DiffInstructionRow {
146    pub fn new(
147        obj: &Object,
148        symbol_idx: usize,
149        row: &diff::InstructionDiffRow,
150        diff_config: &DiffObjConfig,
151    ) -> Result<Self> {
152        let instruction = if let Some(ins_ref) = row.ins_ref {
153            let resolved = obj.resolve_instruction_ref(symbol_idx, ins_ref);
154            resolved.map(|r| DiffInstruction::new(obj, r, diff_config)).transpose()?
155        } else {
156            None
157        };
158
159        let arg_diff =
160            row.arg_diff.iter().map(|d| DiffInstructionArgDiff { diff_index: d.get() }).collect();
161
162        Ok(Self { diff_kind: DiffKind::from(row.kind) as i32, instruction, arg_diff })
163    }
164}
165
166impl DiffInstruction {
167    pub fn new(
168        obj: &Object,
169        resolved: obj::ResolvedInstructionRef,
170        diff_config: &DiffObjConfig,
171    ) -> Result<Self> {
172        let mut formatted = String::new();
173        let mut parts = vec![];
174        let separator = diff_config.separator();
175
176        // Use the arch's display_instruction to get formatted parts
177        obj.arch.display_instruction(resolved, diff_config, &mut |part| {
178            write_instruction_part(&mut formatted, &part, separator, resolved.relocation);
179            parts
180                .push(DiffInstructionPart { part: Some(diff_instruction_part::Part::from(&part)) });
181            Ok(())
182        })?;
183
184        let relocation = resolved.relocation.map(|r| DiffRelocation::new(obj, r));
185
186        let line_number = resolved
187            .section
188            .line_info
189            .range(..=resolved.ins_ref.address)
190            .last()
191            .map(|(_, &line)| line);
192
193        Ok(Self {
194            address: resolved.ins_ref.address,
195            size: resolved.ins_ref.size as u32,
196            formatted,
197            parts,
198            relocation,
199            branch_dest: resolved.ins_ref.branch_dest,
200            line_number,
201        })
202    }
203}
204
205fn write_instruction_part(
206    out: &mut String,
207    part: &InstructionPart,
208    separator: &str,
209    reloc: Option<obj::ResolvedRelocation>,
210) {
211    match part {
212        InstructionPart::Basic(s) => out.push_str(s),
213        InstructionPart::Opcode(s, _) => {
214            out.push_str(s);
215            out.push(' ');
216        }
217        InstructionPart::Arg(arg) => match arg {
218            obj::InstructionArg::Value(v) => {
219                let _ = write!(out, "{}", v);
220            }
221            obj::InstructionArg::Reloc => {
222                if let Some(resolved) = reloc {
223                    out.push_str(&resolved.symbol.name);
224                    if resolved.relocation.addend != 0 {
225                        if resolved.relocation.addend < 0 {
226                            let _ = write!(out, "-{:#x}", -resolved.relocation.addend);
227                        } else {
228                            let _ = write!(out, "+{:#x}", resolved.relocation.addend);
229                        }
230                    }
231                }
232            }
233            obj::InstructionArg::BranchDest(dest) => {
234                let _ = write!(out, "{:#x}", dest);
235            }
236        },
237        InstructionPart::Separator => out.push_str(separator),
238    }
239}
240
241impl diff_instruction_part::Part {
242    fn from(part: &InstructionPart) -> Self {
243        match part {
244            InstructionPart::Basic(s) => diff_instruction_part::Part::Basic(s.to_string()),
245            InstructionPart::Opcode(mnemonic, opcode) => {
246                diff_instruction_part::Part::Opcode(DiffOpcode {
247                    mnemonic: mnemonic.to_string(),
248                    opcode: *opcode as u32,
249                })
250            }
251            InstructionPart::Arg(arg) => {
252                diff_instruction_part::Part::Arg(DiffInstructionArg::from(arg))
253            }
254            InstructionPart::Separator => diff_instruction_part::Part::Separator(true),
255        }
256    }
257}
258
259impl From<&obj::InstructionArg<'_>> for DiffInstructionArg {
260    fn from(arg: &obj::InstructionArg) -> Self {
261        let arg = match arg {
262            obj::InstructionArg::Value(v) => match v {
263                obj::InstructionArgValue::Signed(v) => diff_instruction_arg::Arg::Signed(*v),
264                obj::InstructionArgValue::Unsigned(v) => diff_instruction_arg::Arg::Unsigned(*v),
265                obj::InstructionArgValue::Opaque(v) => {
266                    diff_instruction_arg::Arg::Opaque(v.to_string())
267                }
268            },
269            obj::InstructionArg::Reloc => diff_instruction_arg::Arg::Reloc(true),
270            obj::InstructionArg::BranchDest(dest) => diff_instruction_arg::Arg::BranchDest(*dest),
271        };
272        DiffInstructionArg { arg: Some(arg) }
273    }
274}
275
276impl DiffRelocation {
277    pub fn new(obj: &Object, resolved: obj::ResolvedRelocation) -> Self {
278        let type_val = relocation_type(resolved.relocation.flags);
279        let type_name = obj
280            .arch
281            .reloc_name(resolved.relocation.flags)
282            .map(|s| s.to_string())
283            .unwrap_or_default();
284        Self {
285            r#type: type_val,
286            type_name,
287            target_symbol: resolved.relocation.target_symbol as u32,
288            addend: resolved.relocation.addend,
289        }
290    }
291}
292
293fn relocation_type(flags: obj::RelocationFlags) -> u32 {
294    match flags {
295        obj::RelocationFlags::Elf(r_type) => r_type,
296        obj::RelocationFlags::Coff(typ) => typ as u32,
297    }
298}
299
300impl From<diff::InstructionDiffKind> for DiffKind {
301    fn from(value: diff::InstructionDiffKind) -> Self {
302        match value {
303            diff::InstructionDiffKind::None => DiffKind::DiffNone,
304            diff::InstructionDiffKind::OpMismatch => DiffKind::DiffOpMismatch,
305            diff::InstructionDiffKind::ArgMismatch => DiffKind::DiffArgMismatch,
306            diff::InstructionDiffKind::Replace => DiffKind::DiffReplace,
307            diff::InstructionDiffKind::Delete => DiffKind::DiffDelete,
308            diff::InstructionDiffKind::Insert => DiffKind::DiffInsert,
309        }
310    }
311}
312
313impl From<diff::DataDiffKind> for DiffKind {
314    fn from(value: diff::DataDiffKind) -> Self {
315        match value {
316            diff::DataDiffKind::None => DiffKind::DiffNone,
317            diff::DataDiffKind::Replace => DiffKind::DiffReplace,
318            diff::DataDiffKind::Delete => DiffKind::DiffDelete,
319            diff::DataDiffKind::Insert => DiffKind::DiffInsert,
320        }
321    }
322}
323
324impl From<&diff::DataDiff> for DiffDataSegment {
325    fn from(value: &diff::DataDiff) -> Self {
326        Self {
327            kind: DiffKind::from(value.kind) as i32,
328            data: value.data.clone(),
329            size: value.size as u64,
330        }
331    }
332}
333
334impl DiffDataRelocation {
335    pub fn new(obj: &Object, value: &diff::DataRelocationDiff) -> Self {
336        let type_val = relocation_type(value.reloc.flags);
337        let type_name =
338            obj.arch.reloc_name(value.reloc.flags).map(|s| s.to_string()).unwrap_or_default();
339        Self {
340            relocation: Some(DiffRelocation {
341                r#type: type_val,
342                type_name,
343                target_symbol: value.reloc.target_symbol as u32,
344                addend: value.reloc.addend,
345            }),
346            kind: DiffKind::from(value.kind) as i32,
347            start: value.range.start,
348            end: value.range.end,
349        }
350    }
351}