1#![allow(clippy::useless_borrows_in_formatting)] use 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
17include!(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 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 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 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 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 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}