mirror of
https://github.com/encounter/objdiff.git
synced 2025-07-03 11:45:57 +00:00
Merge branch 'line-info' into arm
This commit is contained in:
commit
8862d95b26
@ -476,7 +476,7 @@ fn read_report(path: &Path) -> Result<Report> {
|
|||||||
fn serialize_hex<S>(x: &Option<u64>, s: S) -> Result<S::Ok, S::Error>
|
fn serialize_hex<S>(x: &Option<u64>, s: S) -> Result<S::Ok, S::Error>
|
||||||
where S: serde::Serializer {
|
where S: serde::Serializer {
|
||||||
if let Some(x) = x {
|
if let Some(x) = x {
|
||||||
s.serialize_str(&format!("{:#X}", x))
|
s.serialize_str(&format!("{:#x}", x))
|
||||||
} else {
|
} else {
|
||||||
s.serialize_none()
|
s.serialize_none()
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use object::{elf, Endian, Endianness, File, Object, Relocation, RelocationFlags, SectionIndex};
|
use object::{elf, Endian, Endianness, File, Object, Relocation, RelocationFlags};
|
||||||
use rabbitizer::{config, Abi, InstrCategory, Instruction, OperandType};
|
use rabbitizer::{config, Abi, InstrCategory, Instruction, OperandType};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -38,9 +38,6 @@ impl ObjArch for ObjArchMips {
|
|||||||
let code = §ion.data
|
let code = §ion.data
|
||||||
[symbol.section_address as usize..(symbol.section_address + symbol.size) as usize];
|
[symbol.section_address as usize..(symbol.section_address + symbol.size) as usize];
|
||||||
|
|
||||||
let line_info =
|
|
||||||
obj.line_info.as_ref().and_then(|map| map.get(&SectionIndex(section.orig_index)));
|
|
||||||
|
|
||||||
let start_address = symbol.address;
|
let start_address = symbol.address;
|
||||||
let end_address = symbol.address + symbol.size;
|
let end_address = symbol.address + symbol.size;
|
||||||
let ins_count = code.len() / 4;
|
let ins_count = code.len() / 4;
|
||||||
@ -52,6 +49,7 @@ impl ObjArch for ObjArchMips {
|
|||||||
let code = self.endianness.read_u32_bytes(chunk.try_into()?);
|
let code = self.endianness.read_u32_bytes(chunk.try_into()?);
|
||||||
let instruction = Instruction::new(code, cur_addr, InstrCategory::CPU);
|
let instruction = Instruction::new(code, cur_addr, InstrCategory::CPU);
|
||||||
|
|
||||||
|
let formatted = instruction.disassemble(None, 0);
|
||||||
let op = instruction.unique_id as u16;
|
let op = instruction.unique_id as u16;
|
||||||
ops.push(op);
|
ops.push(op);
|
||||||
|
|
||||||
@ -113,8 +111,7 @@ impl ObjArch for ObjArchMips {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let line =
|
let line = section.line_info.range(..=cur_addr as u64).last().map(|(_, &b)| b);
|
||||||
line_info.and_then(|map| map.range(..=cur_addr as u64).last().map(|(_, &b)| b));
|
|
||||||
insts.push(ObjIns {
|
insts.push(ObjIns {
|
||||||
address: cur_addr as u64,
|
address: cur_addr as u64,
|
||||||
size: 4,
|
size: 4,
|
||||||
@ -124,6 +121,7 @@ impl ObjArch for ObjArchMips {
|
|||||||
reloc: reloc.cloned(),
|
reloc: reloc.cloned(),
|
||||||
branch_dest,
|
branch_dest,
|
||||||
line,
|
line,
|
||||||
|
formatted,
|
||||||
orig: None,
|
orig: None,
|
||||||
});
|
});
|
||||||
cur_addr += 4;
|
cur_addr += 4;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use object::{elf, File, Relocation, RelocationFlags, SectionIndex};
|
use object::{elf, File, Relocation, RelocationFlags};
|
||||||
use ppc750cl::{Argument, InsIter, GPR};
|
use ppc750cl::{Argument, InsIter, GPR};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -39,9 +39,6 @@ impl ObjArch for ObjArchPpc {
|
|||||||
let code = §ion.data
|
let code = §ion.data
|
||||||
[symbol.section_address as usize..(symbol.section_address + symbol.size) as usize];
|
[symbol.section_address as usize..(symbol.section_address + symbol.size) as usize];
|
||||||
|
|
||||||
let line_info =
|
|
||||||
obj.line_info.as_ref().and_then(|map| map.get(&SectionIndex(section.orig_index)));
|
|
||||||
|
|
||||||
let ins_count = code.len() / 4;
|
let ins_count = code.len() / 4;
|
||||||
let mut ops = Vec::<u16>::with_capacity(ins_count);
|
let mut ops = Vec::<u16>::with_capacity(ins_count);
|
||||||
let mut insts = Vec::<ObjIns>::with_capacity(ins_count);
|
let mut insts = Vec::<ObjIns>::with_capacity(ins_count);
|
||||||
@ -62,6 +59,7 @@ impl ObjArch for ObjArchPpc {
|
|||||||
|
|
||||||
let orig = ins.basic().to_string();
|
let orig = ins.basic().to_string();
|
||||||
let simplified = ins.simplified();
|
let simplified = ins.simplified();
|
||||||
|
let formatted = simplified.to_string();
|
||||||
|
|
||||||
let mut reloc_arg = None;
|
let mut reloc_arg = None;
|
||||||
if let Some(reloc) = reloc {
|
if let Some(reloc) = reloc {
|
||||||
@ -134,8 +132,7 @@ impl ObjArch for ObjArchPpc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ops.push(ins.op as u16);
|
ops.push(ins.op as u16);
|
||||||
let line =
|
let line = section.line_info.range(..=cur_addr as u64).last().map(|(_, &b)| b);
|
||||||
line_info.and_then(|map| map.range(..=cur_addr as u64).last().map(|(_, &b)| b));
|
|
||||||
insts.push(ObjIns {
|
insts.push(ObjIns {
|
||||||
address: cur_addr as u64,
|
address: cur_addr as u64,
|
||||||
size: 4,
|
size: 4,
|
||||||
@ -145,6 +142,7 @@ impl ObjArch for ObjArchPpc {
|
|||||||
op: ins.op as u16,
|
op: ins.op as u16,
|
||||||
branch_dest,
|
branch_dest,
|
||||||
line,
|
line,
|
||||||
|
formatted,
|
||||||
orig: Some(orig),
|
orig: Some(orig),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ use iced_x86::{
|
|||||||
GasFormatter, Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind,
|
GasFormatter, Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind,
|
||||||
PrefixKind, Register,
|
PrefixKind, Register,
|
||||||
};
|
};
|
||||||
use object::{pe, Endian, Endianness, File, Object, Relocation, RelocationFlags, SectionIndex};
|
use object::{pe, Endian, Endianness, File, Object, Relocation, RelocationFlags};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arch::{ObjArch, ProcessCodeResult},
|
arch::{ObjArch, ProcessCodeResult},
|
||||||
@ -36,9 +36,6 @@ impl ObjArch for ObjArchX86 {
|
|||||||
let code = §ion.data
|
let code = §ion.data
|
||||||
[symbol.section_address as usize..(symbol.section_address + symbol.size) as usize];
|
[symbol.section_address as usize..(symbol.section_address + symbol.size) as usize];
|
||||||
|
|
||||||
let line_info =
|
|
||||||
obj.line_info.as_ref().and_then(|map| map.get(&SectionIndex(section.orig_index)));
|
|
||||||
|
|
||||||
let mut result = ProcessCodeResult { ops: Vec::new(), insts: Vec::new() };
|
let mut result = ProcessCodeResult { ops: Vec::new(), insts: Vec::new() };
|
||||||
let mut decoder = Decoder::with_ip(self.bits, code, symbol.address, DecoderOptions::NONE);
|
let mut decoder = Decoder::with_ip(self.bits, code, symbol.address, DecoderOptions::NONE);
|
||||||
let mut formatter: Box<dyn Formatter> = match config.x86_formatter {
|
let mut formatter: Box<dyn Formatter> = match config.x86_formatter {
|
||||||
@ -60,6 +57,7 @@ impl ObjArch for ObjArchX86 {
|
|||||||
reloc: None,
|
reloc: None,
|
||||||
branch_dest: None,
|
branch_dest: None,
|
||||||
line: None,
|
line: None,
|
||||||
|
formatted: String::new(),
|
||||||
orig: None,
|
orig: None,
|
||||||
},
|
},
|
||||||
error: None,
|
error: None,
|
||||||
@ -75,6 +73,7 @@ impl ObjArch for ObjArchX86 {
|
|||||||
.relocations
|
.relocations
|
||||||
.iter()
|
.iter()
|
||||||
.find(|r| r.address >= address && r.address < address + instruction.len() as u64);
|
.find(|r| r.address >= address && r.address < address + instruction.len() as u64);
|
||||||
|
let line = section.line_info.range(..=address).last().map(|(_, &b)| b);
|
||||||
output.ins = ObjIns {
|
output.ins = ObjIns {
|
||||||
address,
|
address,
|
||||||
size: instruction.len() as u8,
|
size: instruction.len() as u8,
|
||||||
@ -83,7 +82,8 @@ impl ObjArch for ObjArchX86 {
|
|||||||
args: vec![],
|
args: vec![],
|
||||||
reloc: reloc.cloned(),
|
reloc: reloc.cloned(),
|
||||||
branch_dest: None,
|
branch_dest: None,
|
||||||
line: line_info.and_then(|m| m.get(&address).cloned()),
|
line,
|
||||||
|
formatted: String::new(),
|
||||||
orig: None,
|
orig: None,
|
||||||
};
|
};
|
||||||
// Run the formatter, which will populate output.ins
|
// Run the formatter, which will populate output.ins
|
||||||
@ -92,7 +92,7 @@ impl ObjArch for ObjArchX86 {
|
|||||||
return Err(error);
|
return Err(error);
|
||||||
}
|
}
|
||||||
ensure!(output.ins_operands.len() == output.ins.args.len());
|
ensure!(output.ins_operands.len() == output.ins.args.len());
|
||||||
output.ins.orig = Some(output.formatted.clone());
|
output.ins.formatted.clone_from(&output.formatted);
|
||||||
|
|
||||||
// Make sure we've put the relocation somewhere in the instruction
|
// Make sure we've put the relocation somewhere in the instruction
|
||||||
if reloc.is_some() && !output.ins.args.iter().any(|a| matches!(a, ObjInsArg::Reloc)) {
|
if reloc.is_some() && !output.ins.args.iter().any(|a| matches!(a, ObjInsArg::Reloc)) {
|
||||||
|
@ -39,7 +39,7 @@ pub fn diff_code(
|
|||||||
config: &DiffObjConfig,
|
config: &DiffObjConfig,
|
||||||
) -> Result<(ObjSymbolDiff, ObjSymbolDiff)> {
|
) -> Result<(ObjSymbolDiff, ObjSymbolDiff)> {
|
||||||
let left_out = left_obj.arch.process_code(left_obj, left_symbol_ref, config)?;
|
let left_out = left_obj.arch.process_code(left_obj, left_symbol_ref, config)?;
|
||||||
let right_out = left_obj.arch.process_code(right_obj, right_symbol_ref, config)?;
|
let right_out = right_obj.arch.process_code(right_obj, right_symbol_ref, config)?;
|
||||||
|
|
||||||
let mut left_diff = Vec::<ObjInsDiff>::new();
|
let mut left_diff = Vec::<ObjInsDiff>::new();
|
||||||
let mut right_diff = Vec::<ObjInsDiff>::new();
|
let mut right_diff = Vec::<ObjInsDiff>::new();
|
||||||
|
@ -5,7 +5,7 @@ use crate::{
|
|||||||
obj::{ObjInsArg, ObjInsArgValue, ObjReloc, ObjSymbol},
|
obj::{ObjInsArg, ObjInsArgValue, ObjReloc, ObjSymbol},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub enum DiffText<'a> {
|
pub enum DiffText<'a> {
|
||||||
/// Basic text
|
/// Basic text
|
||||||
Basic(&'a str),
|
Basic(&'a str),
|
||||||
@ -95,8 +95,8 @@ fn display_reloc_name<E>(
|
|||||||
) -> Result<(), E> {
|
) -> Result<(), E> {
|
||||||
cb(DiffText::Symbol(&reloc.target))?;
|
cb(DiffText::Symbol(&reloc.target))?;
|
||||||
match reloc.target.addend.cmp(&0i64) {
|
match reloc.target.addend.cmp(&0i64) {
|
||||||
Ordering::Greater => cb(DiffText::Basic(&format!("+{:#X}", reloc.target.addend))),
|
Ordering::Greater => cb(DiffText::Basic(&format!("+{:#x}", reloc.target.addend))),
|
||||||
Ordering::Less => cb(DiffText::Basic(&format!("-{:#X}", -reloc.target.addend))),
|
Ordering::Less => cb(DiffText::Basic(&format!("-{:#x}", -reloc.target.addend))),
|
||||||
_ => Ok(()),
|
_ => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
pub mod read;
|
pub mod read;
|
||||||
pub mod split_meta;
|
pub mod split_meta;
|
||||||
|
|
||||||
use std::{
|
use std::{borrow::Cow, collections::BTreeMap, fmt, path::PathBuf};
|
||||||
borrow::Cow,
|
|
||||||
collections::{BTreeMap, HashMap},
|
|
||||||
fmt,
|
|
||||||
path::PathBuf,
|
|
||||||
};
|
|
||||||
|
|
||||||
use filetime::FileTime;
|
use filetime::FileTime;
|
||||||
use flagset::{flags, FlagSet};
|
use flagset::{flags, FlagSet};
|
||||||
use object::{RelocationFlags, SectionIndex};
|
use object::RelocationFlags;
|
||||||
use split_meta::SplitMeta;
|
use split_meta::SplitMeta;
|
||||||
|
|
||||||
use crate::{arch::ObjArch, util::ReallySigned};
|
use crate::{arch::ObjArch, util::ReallySigned};
|
||||||
@ -44,6 +39,8 @@ pub struct ObjSection {
|
|||||||
pub symbols: Vec<ObjSymbol>,
|
pub symbols: Vec<ObjSymbol>,
|
||||||
pub relocations: Vec<ObjReloc>,
|
pub relocations: Vec<ObjReloc>,
|
||||||
pub virtual_address: Option<u64>,
|
pub virtual_address: Option<u64>,
|
||||||
|
/// Line number info (.line or .debug_line section)
|
||||||
|
pub line_info: BTreeMap<u64, u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
@ -106,6 +103,8 @@ pub struct ObjIns {
|
|||||||
pub branch_dest: Option<u64>,
|
pub branch_dest: Option<u64>,
|
||||||
/// Line number
|
/// Line number
|
||||||
pub line: Option<u64>,
|
pub line: Option<u64>,
|
||||||
|
/// Formatted instruction
|
||||||
|
pub formatted: String,
|
||||||
/// Original (unsimplified) instruction
|
/// Original (unsimplified) instruction
|
||||||
pub orig: Option<String>,
|
pub orig: Option<String>,
|
||||||
}
|
}
|
||||||
@ -131,8 +130,6 @@ pub struct ObjInfo {
|
|||||||
pub sections: Vec<ObjSection>,
|
pub sections: Vec<ObjSection>,
|
||||||
/// Common BSS symbols
|
/// Common BSS symbols
|
||||||
pub common: Vec<ObjSymbol>,
|
pub common: Vec<ObjSymbol>,
|
||||||
/// Line number info (.line or .debug_line section)
|
|
||||||
pub line_info: Option<HashMap<SectionIndex, BTreeMap<u64, u64>>>,
|
|
||||||
/// Split object metadata (.note.split section)
|
/// Split object metadata (.note.split section)
|
||||||
pub split_meta: Option<SplitMeta>,
|
pub split_meta: Option<SplitMeta>,
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,4 @@
|
|||||||
use std::{
|
use std::{fs, io::Cursor, path::Path};
|
||||||
collections::{BTreeMap, HashMap},
|
|
||||||
fs,
|
|
||||||
io::Cursor,
|
|
||||||
path::Path,
|
|
||||||
};
|
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, ensure, Context, Result};
|
use anyhow::{anyhow, bail, ensure, Context, Result};
|
||||||
use byteorder::{BigEndian, ReadBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt};
|
||||||
@ -116,6 +111,7 @@ fn filter_sections(obj_file: &File<'_>, split_meta: Option<&SplitMeta>) -> Resul
|
|||||||
symbols: Vec::new(),
|
symbols: Vec::new(),
|
||||||
relocations: Vec::new(),
|
relocations: Vec::new(),
|
||||||
virtual_address,
|
virtual_address,
|
||||||
|
line_info: Default::default(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
result.sort_by(|a, b| a.name.cmp(&b.name));
|
result.sort_by(|a, b| a.name.cmp(&b.name));
|
||||||
@ -257,7 +253,7 @@ fn relocations_by_section(
|
|||||||
} else {
|
} else {
|
||||||
reloc.addend()
|
reloc.addend()
|
||||||
};
|
};
|
||||||
// println!("Reloc: {reloc:?}, symbol: {symbol:?}, addend: {addend:#X}");
|
// println!("Reloc: {reloc:?}, symbol: {symbol:?}, addend: {addend:#x}");
|
||||||
let target = match symbol.kind() {
|
let target = match symbol.kind() {
|
||||||
SymbolKind::Text | SymbolKind::Data | SymbolKind::Label | SymbolKind::Unknown => {
|
SymbolKind::Text | SymbolKind::Data | SymbolKind::Label | SymbolKind::Unknown => {
|
||||||
to_obj_symbol(arch, obj_file, &symbol, addend, split_meta)
|
to_obj_symbol(arch, obj_file, &symbol, addend, split_meta)
|
||||||
@ -273,46 +269,46 @@ fn relocations_by_section(
|
|||||||
Ok(relocations)
|
Ok(relocations)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn line_info(obj_file: &File<'_>) -> Result<Option<HashMap<SectionIndex, BTreeMap<u64, u64>>>> {
|
fn line_info(obj_file: &File<'_>, sections: &mut [ObjSection]) -> Result<()> {
|
||||||
let mut map = HashMap::new();
|
|
||||||
|
|
||||||
// DWARF 1.1
|
// DWARF 1.1
|
||||||
if let Some(section) = obj_file.section_by_name(".line") {
|
if let Some(section) = obj_file.section_by_name(".line") {
|
||||||
if section.size() == 0 {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
let text_section = obj_file
|
|
||||||
.sections()
|
|
||||||
.find(|s| s.kind() == SectionKind::Text)
|
|
||||||
.context("No text section found for line info")?;
|
|
||||||
let mut lines = BTreeMap::new();
|
|
||||||
|
|
||||||
let data = section.uncompressed_data()?;
|
let data = section.uncompressed_data()?;
|
||||||
let mut reader = Cursor::new(data.as_ref());
|
let mut reader = Cursor::new(data.as_ref());
|
||||||
|
|
||||||
let size = reader.read_u32::<BigEndian>()?;
|
let mut text_sections = obj_file.sections().filter(|s| s.kind() == SectionKind::Text);
|
||||||
let base_address = reader.read_u32::<BigEndian>()? as u64;
|
while reader.position() < data.len() as u64 {
|
||||||
while reader.position() < size as u64 {
|
let text_section_index = text_sections
|
||||||
let line_number = reader.read_u32::<BigEndian>()? as u64;
|
.next()
|
||||||
let statement_pos = reader.read_u16::<BigEndian>()?;
|
.ok_or_else(|| anyhow!("Next text section not found for line info"))?
|
||||||
if statement_pos != 0xFFFF {
|
.index()
|
||||||
log::warn!("Unhandled statement pos {}", statement_pos);
|
.0;
|
||||||
|
let start = reader.position();
|
||||||
|
let size = reader.read_u32::<BigEndian>()?;
|
||||||
|
let base_address = reader.read_u32::<BigEndian>()? as u64;
|
||||||
|
let Some(out_section) =
|
||||||
|
sections.iter_mut().find(|s| s.orig_index == text_section_index)
|
||||||
|
else {
|
||||||
|
// Skip line info for sections we filtered out
|
||||||
|
reader.set_position(start + size as u64);
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let end = start + size as u64;
|
||||||
|
while reader.position() < end {
|
||||||
|
let line_number = reader.read_u32::<BigEndian>()? as u64;
|
||||||
|
let statement_pos = reader.read_u16::<BigEndian>()?;
|
||||||
|
if statement_pos != 0xFFFF {
|
||||||
|
log::warn!("Unhandled statement pos {}", statement_pos);
|
||||||
|
}
|
||||||
|
let address_delta = reader.read_u32::<BigEndian>()? as u64;
|
||||||
|
out_section.line_info.insert(base_address + address_delta, line_number);
|
||||||
|
log::debug!("Line: {:#x} -> {}", base_address + address_delta, line_number);
|
||||||
}
|
}
|
||||||
let address_delta = reader.read_u32::<BigEndian>()? as u64;
|
|
||||||
lines.insert(base_address + address_delta, line_number);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
map.insert(text_section.index(), lines);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DWARF 2+
|
// DWARF 2+
|
||||||
#[cfg(feature = "dwarf")]
|
#[cfg(feature = "dwarf")]
|
||||||
{
|
{
|
||||||
let mut text_sections = obj_file.sections().filter(|s| s.kind() == SectionKind::Text);
|
|
||||||
let first_section = text_sections.next().context("No text section found for line info")?;
|
|
||||||
map.insert(first_section.index(), BTreeMap::new());
|
|
||||||
let mut lines = map.get_mut(&first_section.index()).unwrap();
|
|
||||||
|
|
||||||
let dwarf_cow = gimli::DwarfSections::load(|id| {
|
let dwarf_cow = gimli::DwarfSections::load(|id| {
|
||||||
Ok::<_, gimli::Error>(
|
Ok::<_, gimli::Error>(
|
||||||
obj_file
|
obj_file
|
||||||
@ -327,32 +323,48 @@ fn line_info(obj_file: &File<'_>) -> Result<Option<HashMap<SectionIndex, BTreeMa
|
|||||||
};
|
};
|
||||||
let dwarf = dwarf_cow.borrow(|section| gimli::EndianSlice::new(section, endian));
|
let dwarf = dwarf_cow.borrow(|section| gimli::EndianSlice::new(section, endian));
|
||||||
let mut iter = dwarf.units();
|
let mut iter = dwarf.units();
|
||||||
'outer: while let Some(header) = iter.next()? {
|
if let Some(header) = iter.next()? {
|
||||||
let unit = dwarf.unit(header)?;
|
let unit = dwarf.unit(header)?;
|
||||||
if let Some(program) = unit.line_program.clone() {
|
if let Some(program) = unit.line_program.clone() {
|
||||||
|
let mut text_sections =
|
||||||
|
obj_file.sections().filter(|s| s.kind() == SectionKind::Text);
|
||||||
|
let section_index = text_sections
|
||||||
|
.next()
|
||||||
|
.ok_or_else(|| anyhow!("Next text section not found for line info"))?
|
||||||
|
.index()
|
||||||
|
.0;
|
||||||
|
let mut lines = sections
|
||||||
|
.iter_mut()
|
||||||
|
.find(|s| s.orig_index == section_index)
|
||||||
|
.map(|s| &mut s.line_info);
|
||||||
|
|
||||||
let mut rows = program.rows();
|
let mut rows = program.rows();
|
||||||
while let Some((_header, row)) = rows.next_row()? {
|
while let Some((_header, row)) = rows.next_row()? {
|
||||||
if let Some(line) = row.line() {
|
if let (Some(line), Some(lines)) = (row.line(), &mut lines) {
|
||||||
lines.insert(row.address(), line.get());
|
lines.insert(row.address(), line.get());
|
||||||
}
|
}
|
||||||
if row.end_sequence() {
|
if row.end_sequence() {
|
||||||
// The next row is the start of a new sequence, which means we must
|
// The next row is the start of a new sequence, which means we must
|
||||||
// advance to the next .text section.
|
// advance to the next .text section.
|
||||||
if let Some(next_section) = text_sections.next() {
|
let section_index = text_sections
|
||||||
map.insert(next_section.index(), BTreeMap::new());
|
.next()
|
||||||
lines = map.get_mut(&next_section.index()).unwrap();
|
.ok_or_else(|| anyhow!("Next text section not found for line info"))?
|
||||||
} else {
|
.index()
|
||||||
break 'outer;
|
.0;
|
||||||
}
|
lines = sections
|
||||||
|
.iter_mut()
|
||||||
|
.find(|s| s.orig_index == section_index)
|
||||||
|
.map(|s| &mut s.line_info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if iter.next()?.is_some() {
|
||||||
|
log::warn!("Multiple units found in DWARF data, only processing the first");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if map.is_empty() {
|
|
||||||
return Ok(None);
|
Ok(())
|
||||||
}
|
|
||||||
Ok(Some(map))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(obj_path: &Path) -> Result<ObjInfo> {
|
pub fn read(obj_path: &Path) -> Result<ObjInfo> {
|
||||||
@ -371,16 +383,9 @@ pub fn read(obj_path: &Path) -> Result<ObjInfo> {
|
|||||||
section.relocations =
|
section.relocations =
|
||||||
relocations_by_section(arch.as_ref(), &obj_file, section, split_meta.as_ref())?;
|
relocations_by_section(arch.as_ref(), &obj_file, section, split_meta.as_ref())?;
|
||||||
}
|
}
|
||||||
|
line_info(&obj_file, &mut sections)?;
|
||||||
let common = common_symbols(arch.as_ref(), &obj_file, split_meta.as_ref())?;
|
let common = common_symbols(arch.as_ref(), &obj_file, split_meta.as_ref())?;
|
||||||
Ok(ObjInfo {
|
Ok(ObjInfo { arch, path: obj_path.to_owned(), timestamp, sections, common, split_meta })
|
||||||
arch,
|
|
||||||
path: obj_path.to_owned(),
|
|
||||||
timestamp,
|
|
||||||
sections,
|
|
||||||
common,
|
|
||||||
line_info: line_info(&obj_file)?,
|
|
||||||
split_meta,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_function(obj_path: &Path, symbol_name: &str) -> Result<bool> {
|
pub fn has_function(obj_path: &Path, symbol_name: &str) -> Result<bool> {
|
||||||
|
@ -26,7 +26,7 @@ fn data_row_ui(ui: &mut egui::Ui, address: usize, diffs: &[ObjDataDiff], appeara
|
|||||||
}
|
}
|
||||||
let mut job = LayoutJob::default();
|
let mut job = LayoutJob::default();
|
||||||
write_text(
|
write_text(
|
||||||
format!("{address:08X}: ").as_str(),
|
format!("{address:08x}: ").as_str(),
|
||||||
appearance.text_color,
|
appearance.text_color,
|
||||||
&mut job,
|
&mut job,
|
||||||
appearance.code_font.clone(),
|
appearance.code_font.clone(),
|
||||||
@ -47,7 +47,7 @@ fn data_row_ui(ui: &mut egui::Ui, address: usize, diffs: &[ObjDataDiff], appeara
|
|||||||
} else {
|
} else {
|
||||||
let mut text = String::new();
|
let mut text = String::new();
|
||||||
for byte in &diff.data {
|
for byte in &diff.data {
|
||||||
text.push_str(format!("{byte:02X} ").as_str());
|
text.push_str(format!("{byte:02x} ").as_str());
|
||||||
cur_addr += 1;
|
cur_addr += 1;
|
||||||
if cur_addr % 8 == 0 {
|
if cur_addr % 8 == 0 {
|
||||||
text.push(' ');
|
text.push(' ');
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
|
|
||||||
use egui::{text::LayoutJob, Align, Label, Layout, Sense, Vec2, Widget};
|
use egui::{text::LayoutJob, Align, Label, Layout, Response, Sense, Vec2, Widget};
|
||||||
use egui_extras::{Column, TableBuilder, TableRow};
|
use egui_extras::{Column, TableBuilder, TableRow};
|
||||||
use objdiff_core::{
|
use objdiff_core::{
|
||||||
arch::ObjArch,
|
arch::ObjArch,
|
||||||
@ -27,6 +27,7 @@ fn ins_hover_ui(
|
|||||||
arch: &dyn ObjArch,
|
arch: &dyn ObjArch,
|
||||||
section: &ObjSection,
|
section: &ObjSection,
|
||||||
ins: &ObjIns,
|
ins: &ObjIns,
|
||||||
|
symbol: &ObjSymbol,
|
||||||
appearance: &Appearance,
|
appearance: &Appearance,
|
||||||
) {
|
) {
|
||||||
ui.scope(|ui| {
|
ui.scope(|ui| {
|
||||||
@ -35,10 +36,18 @@ fn ins_hover_ui(
|
|||||||
|
|
||||||
let offset = ins.address - section.address;
|
let offset = ins.address - section.address;
|
||||||
ui.label(format!(
|
ui.label(format!(
|
||||||
"{:02X?}",
|
"{:02x?}",
|
||||||
§ion.data[offset as usize..(offset + ins.size as u64) as usize]
|
§ion.data[offset as usize..(offset + ins.size as u64) as usize]
|
||||||
));
|
));
|
||||||
|
|
||||||
|
if let Some(virtual_address) = symbol.virtual_address {
|
||||||
|
let offset = ins.address - symbol.address;
|
||||||
|
ui.colored_label(
|
||||||
|
appearance.replace_color,
|
||||||
|
format!("Virtual address: {:#x}", virtual_address + offset),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(orig) = &ins.orig {
|
if let Some(orig) = &ins.orig {
|
||||||
ui.label(format!("Original: {}", orig));
|
ui.label(format!("Original: {}", orig));
|
||||||
}
|
}
|
||||||
@ -77,12 +86,33 @@ fn ins_hover_ui(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ins_context_menu(ui: &mut egui::Ui, ins: &ObjIns) {
|
fn ins_context_menu(ui: &mut egui::Ui, section: &ObjSection, ins: &ObjIns, symbol: &ObjSymbol) {
|
||||||
ui.scope(|ui| {
|
ui.scope(|ui| {
|
||||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
||||||
ui.style_mut().wrap = Some(false);
|
ui.style_mut().wrap = Some(false);
|
||||||
|
|
||||||
// if ui.button("Copy hex").clicked() {}
|
if ui.button(format!("Copy \"{}\"", ins.formatted)).clicked() {
|
||||||
|
ui.output_mut(|output| output.copied_text.clone_from(&ins.formatted));
|
||||||
|
ui.close_menu();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut hex_string = "0x".to_string();
|
||||||
|
for byte in §ion.data[ins.address as usize..(ins.address + ins.size as u64) as usize] {
|
||||||
|
hex_string.push_str(&format!("{:02x}", byte));
|
||||||
|
}
|
||||||
|
if ui.button(format!("Copy \"{hex_string}\" (instruction bytes)")).clicked() {
|
||||||
|
ui.output_mut(|output| output.copied_text = hex_string);
|
||||||
|
ui.close_menu();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(virtual_address) = symbol.virtual_address {
|
||||||
|
let offset = ins.address - symbol.address;
|
||||||
|
let offset_string = format!("{:#x}", virtual_address + offset);
|
||||||
|
if ui.button(format!("Copy \"{offset_string}\" (virtual address)")).clicked() {
|
||||||
|
ui.output_mut(|output| output.copied_text = offset_string);
|
||||||
|
ui.close_menu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for arg in &ins.args {
|
for arg in &ins.args {
|
||||||
if let ObjInsArg::Arg(arg) = arg {
|
if let ObjInsArg::Arg(arg) = arg {
|
||||||
@ -144,6 +174,7 @@ fn diff_text_ui(
|
|||||||
appearance: &Appearance,
|
appearance: &Appearance,
|
||||||
ins_view_state: &mut FunctionViewState,
|
ins_view_state: &mut FunctionViewState,
|
||||||
space_width: f32,
|
space_width: f32,
|
||||||
|
response_cb: impl Fn(Response) -> Response,
|
||||||
) {
|
) {
|
||||||
let label_text;
|
let label_text;
|
||||||
let mut base_color = match ins_diff.kind {
|
let mut base_color = match ins_diff.kind {
|
||||||
@ -204,13 +235,13 @@ fn diff_text_ui(
|
|||||||
|
|
||||||
let len = label_text.len();
|
let len = label_text.len();
|
||||||
let highlight = ins_view_state.highlight == text;
|
let highlight = ins_view_state.highlight == text;
|
||||||
let response = Label::new(LayoutJob::single_section(
|
let mut response = Label::new(LayoutJob::single_section(
|
||||||
label_text,
|
label_text,
|
||||||
appearance.code_text_format(base_color, highlight),
|
appearance.code_text_format(base_color, highlight),
|
||||||
))
|
))
|
||||||
.sense(Sense::click())
|
.sense(Sense::click())
|
||||||
.ui(ui);
|
.ui(ui);
|
||||||
response.context_menu(|ui| ins_context_menu(ui, ins_diff.ins.as_ref().unwrap()));
|
response = response_cb(response);
|
||||||
if response.clicked() {
|
if response.clicked() {
|
||||||
if highlight {
|
if highlight {
|
||||||
ins_view_state.highlight = HighlightKind::None;
|
ins_view_state.highlight = HighlightKind::None;
|
||||||
@ -229,6 +260,7 @@ fn asm_row_ui(
|
|||||||
symbol: &ObjSymbol,
|
symbol: &ObjSymbol,
|
||||||
appearance: &Appearance,
|
appearance: &Appearance,
|
||||||
ins_view_state: &mut FunctionViewState,
|
ins_view_state: &mut FunctionViewState,
|
||||||
|
response_cb: impl Fn(Response) -> Response,
|
||||||
) {
|
) {
|
||||||
ui.spacing_mut().item_spacing.x = 0.0;
|
ui.spacing_mut().item_spacing.x = 0.0;
|
||||||
if ins_diff.kind != ObjInsDiffKind::None {
|
if ins_diff.kind != ObjInsDiffKind::None {
|
||||||
@ -236,7 +268,7 @@ fn asm_row_ui(
|
|||||||
}
|
}
|
||||||
let space_width = ui.fonts(|f| f.glyph_width(&appearance.code_font, ' '));
|
let space_width = ui.fonts(|f| f.glyph_width(&appearance.code_font, ' '));
|
||||||
display_diff(ins_diff, symbol.address, |text| {
|
display_diff(ins_diff, symbol.address, |text| {
|
||||||
diff_text_ui(ui, text, ins_diff, appearance, ins_view_state, space_width);
|
diff_text_ui(ui, text, ins_diff, appearance, ins_view_state, space_width, &response_cb);
|
||||||
Ok::<_, ()>(())
|
Ok::<_, ()>(())
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -251,14 +283,20 @@ fn asm_col_ui(
|
|||||||
) {
|
) {
|
||||||
let (section, symbol) = obj.0.section_symbol(symbol_ref);
|
let (section, symbol) = obj.0.section_symbol(symbol_ref);
|
||||||
let ins_diff = &obj.1.symbol_diff(symbol_ref).instructions[row.index()];
|
let ins_diff = &obj.1.symbol_diff(symbol_ref).instructions[row.index()];
|
||||||
|
let response_cb = |response: Response| {
|
||||||
|
if let Some(ins) = &ins_diff.ins {
|
||||||
|
response.context_menu(|ui| ins_context_menu(ui, section, ins, symbol));
|
||||||
|
response.on_hover_ui_at_pointer(|ui| {
|
||||||
|
ins_hover_ui(ui, obj.0.arch.as_ref(), section, ins, symbol, appearance)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
response
|
||||||
|
}
|
||||||
|
};
|
||||||
let (_, response) = row.col(|ui| {
|
let (_, response) = row.col(|ui| {
|
||||||
asm_row_ui(ui, ins_diff, symbol, appearance, ins_view_state);
|
asm_row_ui(ui, ins_diff, symbol, appearance, ins_view_state, response_cb);
|
||||||
});
|
});
|
||||||
if let Some(ins) = &ins_diff.ins {
|
response_cb(response);
|
||||||
response.on_hover_ui_at_pointer(|ui| {
|
|
||||||
ins_hover_ui(ui, obj.0.arch.as_ref(), section, ins, appearance)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn empty_col_ui(row: &mut TableRow<'_, '_>) {
|
fn empty_col_ui(row: &mut TableRow<'_, '_>) {
|
||||||
|
@ -171,10 +171,7 @@ fn symbol_hover_ui(ui: &mut Ui, symbol: &ObjSymbol, appearance: &Appearance) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
if let Some(address) = symbol.virtual_address {
|
if let Some(address) = symbol.virtual_address {
|
||||||
ui.colored_label(
|
ui.colored_label(appearance.replace_color, format!("Virtual address: {:#x}", address));
|
||||||
appearance.highlight_color,
|
|
||||||
format!("Virtual address: {:#x}", address),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user