Option to combine data sections (#76)

Co-authored-by: Luke Street <luke.street@encounterpc.com>
This commit is contained in:
Aetias 2024-06-19 06:05:24 +02:00 committed by GitHub
parent 759d55994a
commit 1fd901a863
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 140 additions and 17 deletions

View File

@ -809,23 +809,28 @@ impl FunctionDiffUi {
fn reload(&mut self) -> Result<()> { fn reload(&mut self) -> Result<()> {
let prev = self.right_obj.take(); let prev = self.right_obj.take();
let target = self
.target_path
.as_deref()
.map(|p| obj::read::read(p).with_context(|| format!("Loading {}", p.display())))
.transpose()?;
let base = self
.base_path
.as_deref()
.map(|p| obj::read::read(p).with_context(|| format!("Loading {}", p.display())))
.transpose()?;
let config = diff::DiffObjConfig { let config = diff::DiffObjConfig {
relax_reloc_diffs: self.relax_reloc_diffs, relax_reloc_diffs: self.relax_reloc_diffs,
space_between_args: true, // TODO space_between_args: true, // TODO
combine_data_sections: false, // TODO
x86_formatter: Default::default(), // TODO x86_formatter: Default::default(), // TODO
mips_abi: Default::default(), // TODO mips_abi: Default::default(), // TODO
mips_instr_category: Default::default(), // TODO mips_instr_category: Default::default(), // TODO
}; };
let target = self
.target_path
.as_deref()
.map(|p| {
obj::read::read(p, &config).with_context(|| format!("Loading {}", p.display()))
})
.transpose()?;
let base = self
.base_path
.as_deref()
.map(|p| {
obj::read::read(p, &config).with_context(|| format!("Loading {}", p.display()))
})
.transpose()?;
let result = diff::diff_objs(&config, target.as_ref(), base.as_ref(), prev.as_ref())?; let result = diff::diff_objs(&config, target.as_ref(), base.as_ref(), prev.as_ref())?;
let left_sym = target.as_ref().and_then(|o| find_function(o, &self.symbol_name)); let left_sym = target.as_ref().and_then(|o| find_function(o, &self.symbol_name));

View File

@ -230,17 +230,21 @@ fn report_object(
} }
_ => {} _ => {}
} }
let config = diff::DiffObjConfig { relax_reloc_diffs: true, ..Default::default() };
let target = object let target = object
.target_path .target_path
.as_ref() .as_ref()
.map(|p| obj::read::read(p).with_context(|| format!("Failed to open {}", p.display()))) .map(|p| {
obj::read::read(p, &config).with_context(|| format!("Failed to open {}", p.display()))
})
.transpose()?; .transpose()?;
let base = object let base = object
.base_path .base_path
.as_ref() .as_ref()
.map(|p| obj::read::read(p).with_context(|| format!("Failed to open {}", p.display()))) .map(|p| {
obj::read::read(p, &config).with_context(|| format!("Failed to open {}", p.display()))
})
.transpose()?; .transpose()?;
let config = diff::DiffObjConfig { relax_reloc_diffs: true, ..Default::default() };
let result = diff::diff_objs(&config, target.as_ref(), base.as_ref(), None)?; let result = diff::diff_objs(&config, target.as_ref(), base.as_ref(), None)?;
let mut unit = ReportUnit { let mut unit = ReportUnit {
name: object.name().to_string(), name: object.name().to_string(),

View File

@ -102,6 +102,7 @@ pub struct DiffObjConfig {
pub relax_reloc_diffs: bool, pub relax_reloc_diffs: bool,
#[serde(default = "default_true")] #[serde(default = "default_true")]
pub space_between_args: bool, pub space_between_args: bool,
pub combine_data_sections: bool,
// x86 // x86
pub x86_formatter: X86Formatter, pub x86_formatter: X86Formatter,
// MIPS // MIPS
@ -114,6 +115,7 @@ impl Default for DiffObjConfig {
Self { Self {
relax_reloc_diffs: false, relax_reloc_diffs: false,
space_between_args: true, space_between_args: true,
combine_data_sections: false,
x86_formatter: Default::default(), x86_formatter: Default::default(),
mips_abi: Default::default(), mips_abi: Default::default(),
mips_instr_category: Default::default(), mips_instr_category: Default::default(),

View File

@ -1,4 +1,4 @@
use std::{fs, io::Cursor, path::Path}; use std::{collections::HashSet, 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};
@ -11,6 +11,7 @@ use object::{
use crate::{ use crate::{
arch::{new_arch, ObjArch}, arch::{new_arch, ObjArch},
diff::DiffObjConfig,
obj::{ obj::{
split_meta::{SplitMeta, SPLITMETA_SECTION}, split_meta::{SplitMeta, SPLITMETA_SECTION},
ObjInfo, ObjReloc, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjInfo, ObjReloc, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags,
@ -361,7 +362,105 @@ fn line_info(obj_file: &File<'_>, sections: &mut [ObjSection]) -> Result<()> {
Ok(()) Ok(())
} }
pub fn read(obj_path: &Path) -> Result<ObjInfo> { fn update_combined_symbol(symbol: ObjSymbol, address_change: i64) -> Result<ObjSymbol> {
Ok(ObjSymbol {
name: symbol.name,
demangled_name: symbol.demangled_name,
address: (symbol.address as i64 + address_change).try_into()?,
section_address: (symbol.section_address as i64 + address_change).try_into()?,
size: symbol.size,
size_known: symbol.size_known,
flags: symbol.flags,
addend: symbol.addend,
virtual_address: if let Some(virtual_address) = symbol.virtual_address {
Some((virtual_address as i64 + address_change).try_into()?)
} else {
None
},
})
}
fn combine_sections(section: ObjSection, combine: ObjSection) -> Result<ObjSection> {
let mut data = section.data;
data.extend(combine.data);
let address_change: i64 = (section.address + section.size) as i64 - combine.address as i64;
let mut symbols = section.symbols;
for symbol in combine.symbols {
symbols.push(update_combined_symbol(symbol, address_change)?);
}
let mut relocations = section.relocations;
for reloc in combine.relocations {
relocations.push(ObjReloc {
flags: reloc.flags,
address: (reloc.address as i64 + address_change).try_into()?,
target: reloc.target, // TODO: Should be updated?
target_section: reloc.target_section, // TODO: Same as above
});
}
let mut line_info = section.line_info;
for (addr, line) in combine.line_info {
let key = (addr as i64 + address_change).try_into()?;
line_info.insert(key, line);
}
Ok(ObjSection {
name: section.name,
kind: section.kind,
address: section.address,
size: section.size + combine.size,
data,
orig_index: section.orig_index,
symbols,
relocations,
virtual_address: section.virtual_address,
line_info,
})
}
fn combine_data_sections(sections: &mut Vec<ObjSection>) -> Result<()> {
let names_to_combine: HashSet<_> = sections
.iter()
.filter(|s| s.kind == ObjSectionKind::Data)
.map(|s| s.name.clone())
.collect();
for name in names_to_combine {
// Take section with lowest index
let (mut section_index, _) = sections
.iter()
.enumerate()
.filter(|(_, s)| s.name == name)
.min_by_key(|(_, s)| s.orig_index)
// Should not happen
.context("No combine section found with name")?;
let mut section = sections.remove(section_index);
// Remove equally named sections
let mut combines = vec![];
for i in (0..sections.len()).rev() {
if sections[i].name != name || sections[i].orig_index == section.orig_index {
continue;
}
combines.push(sections.remove(i));
if i < section_index {
section_index -= 1;
}
}
// Combine sections ordered by index
combines.sort_unstable_by_key(|c| c.orig_index);
for combine in combines {
section = combine_sections(section, combine)?;
}
sections.insert(section_index, section);
}
Ok(())
}
pub fn read(obj_path: &Path, config: &DiffObjConfig) -> Result<ObjInfo> {
let (data, timestamp) = { let (data, timestamp) = {
let file = fs::File::open(obj_path)?; let file = fs::File::open(obj_path)?;
let timestamp = FileTime::from_last_modification_time(&file.metadata()?); let timestamp = FileTime::from_last_modification_time(&file.metadata()?);
@ -377,6 +476,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())?;
} }
if config.combine_data_sections {
combine_data_sections(&mut sections)?;
}
line_info(&obj_file, &mut sections)?; 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 { arch, path: obj_path.to_owned(), timestamp, sections, common, split_meta }) Ok(ObjInfo { arch, path: obj_path.to_owned(), timestamp, sections, common, split_meta })

View File

@ -559,6 +559,16 @@ impl eframe::App for App {
{ {
config.queue_reload = true; config.queue_reload = true;
} }
if ui
.checkbox(
&mut config.diff_obj_config.combine_data_sections,
"Combine data sections",
)
.on_hover_text("Combines data sections with equal names.")
.changed()
{
config.queue_reload = true;
}
}); });
}); });
}); });

View File

@ -236,7 +236,7 @@ fn run_build(
total, total,
&cancel, &cancel,
)?; )?;
Some(read::read(target_path).with_context(|| { Some(read::read(target_path, &config.diff_obj_config).with_context(|| {
format!("Failed to read object '{}'", target_path.display()) format!("Failed to read object '{}'", target_path.display())
})?) })?)
} }
@ -253,7 +253,7 @@ fn run_build(
&cancel, &cancel,
)?; )?;
Some( Some(
read::read(base_path) read::read(base_path, &config.diff_obj_config)
.with_context(|| format!("Failed to read object '{}'", base_path.display()))?, .with_context(|| format!("Failed to read object '{}'", base_path.display()))?,
) )
} }