WIP objdiff 3.0 refactor

This commit is contained in:
2025-02-20 17:48:00 -07:00
parent 6d3c63ccd8
commit f3c157ff06
79 changed files with 14886 additions and 6820 deletions

View File

@@ -1,23 +1,30 @@
pub mod read;
pub mod split_meta;
use alloc::{borrow::Cow, boxed::Box, collections::BTreeMap, string::String, vec::Vec};
use core::fmt;
use alloc::{borrow::Cow, boxed::Box, collections::BTreeMap, string::String, vec, vec::Vec};
use core::{fmt, num::NonZeroU32};
use flagset::{flags, FlagSet};
use object::RelocationFlags;
use split_meta::SplitMeta;
use crate::{arch::ObjArch, util::ReallySigned};
use crate::{
arch::{Arch, ArchDummy},
obj::split_meta::SplitMeta,
util::ReallySigned,
};
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum ObjSectionKind {
#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)]
pub enum SectionKind {
#[default]
Unknown = -1,
Code,
Data,
Bss,
Common,
}
flags! {
pub enum ObjSymbolFlags: u8 {
#[derive(Hash)]
pub enum SymbolFlag: u8 {
Global,
Local,
Weak,
@@ -26,105 +33,152 @@ flags! {
/// Has extra data associated with the symbol
/// (e.g. exception table entry)
HasExtra,
/// Symbol size was missing and was inferred
SizeInferred,
}
}
#[derive(Debug, Copy, Clone, Default, PartialEq)]
pub struct ObjSymbolFlagSet(pub FlagSet<ObjSymbolFlags>);
#[derive(Debug, Clone)]
pub struct ObjSection {
pub type SymbolFlagSet = FlagSet<SymbolFlag>;
flags! {
#[derive(Hash)]
pub enum SectionFlag: u8 {
/// Section combined from multiple input sections
Combined,
Hidden,
}
}
pub type SectionFlagSet = FlagSet<SectionFlag>;
#[derive(Debug, Clone, Default)]
pub struct Section {
/// Unique section ID
pub id: String,
pub name: String,
pub kind: ObjSectionKind,
pub address: u64,
pub size: u64,
pub data: Vec<u8>,
pub orig_index: usize,
pub symbols: Vec<ObjSymbol>,
pub relocations: Vec<ObjReloc>,
pub virtual_address: Option<u64>,
pub kind: SectionKind,
pub data: SectionData,
pub flags: SectionFlagSet,
pub relocations: Vec<Relocation>,
/// Line number info (.line or .debug_line section)
pub line_info: BTreeMap<u64, u32>,
/// Original virtual address (from .note.split section)
pub virtual_address: Option<u64>,
}
#[derive(Clone, Default)]
#[repr(transparent)]
pub struct SectionData(pub Vec<u8>);
impl core::ops::Deref for SectionData {
type Target = Vec<u8>;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl fmt::Debug for SectionData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("SectionData").field(&self.0.len()).finish()
}
}
impl Section {
pub fn data_range(&self, address: u64, size: usize) -> Option<&[u8]> {
let start = self.address;
let end = start + self.size;
if address >= start && address + size as u64 <= end {
let offset = (address - start) as usize;
Some(&self.data[offset..offset + size])
} else {
None
}
}
pub fn relocation_at<'obj>(
&'obj self,
address: u64,
obj: &'obj Object,
) -> Option<ResolvedRelocation<'obj>> {
self.relocations.binary_search_by_key(&address, |r| r.address).ok().and_then(|i| {
let relocation = self.relocations.get(i)?;
let symbol = obj.symbols.get(relocation.target_symbol)?;
Some(ResolvedRelocation { relocation, symbol })
})
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum ObjInsArgValue {
pub enum InstructionArgValue {
Signed(i64),
Unsigned(u64),
Opaque(Cow<'static, str>),
}
impl ObjInsArgValue {
pub fn loose_eq(&self, other: &ObjInsArgValue) -> bool {
impl InstructionArgValue {
pub fn loose_eq(&self, other: &InstructionArgValue) -> bool {
match (self, other) {
(ObjInsArgValue::Signed(a), ObjInsArgValue::Signed(b)) => a == b,
(ObjInsArgValue::Unsigned(a), ObjInsArgValue::Unsigned(b)) => a == b,
(ObjInsArgValue::Signed(a), ObjInsArgValue::Unsigned(b))
| (ObjInsArgValue::Unsigned(b), ObjInsArgValue::Signed(a)) => *a as u64 == *b,
(ObjInsArgValue::Opaque(a), ObjInsArgValue::Opaque(b)) => a == b,
(InstructionArgValue::Signed(a), InstructionArgValue::Signed(b)) => a == b,
(InstructionArgValue::Unsigned(a), InstructionArgValue::Unsigned(b)) => a == b,
(InstructionArgValue::Signed(a), InstructionArgValue::Unsigned(b))
| (InstructionArgValue::Unsigned(b), InstructionArgValue::Signed(a)) => *a as u64 == *b,
(InstructionArgValue::Opaque(a), InstructionArgValue::Opaque(b)) => a == b,
_ => false,
}
}
}
impl fmt::Display for ObjInsArgValue {
impl fmt::Display for InstructionArgValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ObjInsArgValue::Signed(v) => write!(f, "{:#x}", ReallySigned(*v)),
ObjInsArgValue::Unsigned(v) => write!(f, "{:#x}", v),
ObjInsArgValue::Opaque(v) => write!(f, "{}", v),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum ObjInsArg {
PlainText(Cow<'static, str>),
Arg(ObjInsArgValue),
Reloc,
BranchDest(u64),
}
impl ObjInsArg {
#[inline]
pub fn is_plain_text(&self) -> bool { matches!(self, ObjInsArg::PlainText(_)) }
pub fn loose_eq(&self, other: &ObjInsArg) -> bool {
match (self, other) {
(ObjInsArg::Arg(a), ObjInsArg::Arg(b)) => a.loose_eq(b),
(ObjInsArg::Reloc, ObjInsArg::Reloc) => true,
(ObjInsArg::BranchDest(a), ObjInsArg::BranchDest(b)) => a == b,
_ => false,
InstructionArgValue::Signed(v) => write!(f, "{:#x}", ReallySigned(*v)),
InstructionArgValue::Unsigned(v) => write!(f, "{:#x}", v),
InstructionArgValue::Opaque(v) => write!(f, "{}", v),
}
}
}
#[derive(Debug, Clone)]
pub struct ObjIns {
pub address: u64,
pub size: u8,
pub op: u16,
pub mnemonic: Cow<'static, str>,
pub args: Vec<ObjInsArg>,
pub reloc: Option<ObjReloc>,
pub branch_dest: Option<u64>,
/// Line number
pub line: Option<u32>,
/// Formatted instruction
pub formatted: String,
/// Original (unsimplified) instruction
pub orig: Option<String>,
pub enum InstructionArg {
Value(InstructionArgValue),
Reloc,
BranchDest(u64),
}
impl ObjIns {
/// Iterate over non-PlainText arguments.
#[inline]
pub fn iter_args(&self) -> impl DoubleEndedIterator<Item = &ObjInsArg> {
self.args.iter().filter(|a| !a.is_plain_text())
impl InstructionArg {
pub fn loose_eq(&self, other: &InstructionArg) -> bool {
match (self, other) {
(InstructionArg::Value(a), InstructionArg::Value(b)) => a.loose_eq(b),
(InstructionArg::Reloc, InstructionArg::Reloc) => true,
(InstructionArg::BranchDest(a), InstructionArg::BranchDest(b)) => a == b,
_ => false,
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct InstructionRef {
pub address: u64,
pub size: u8,
pub opcode: u16,
}
#[derive(Copy, Clone, Debug)]
pub struct ScannedInstruction {
pub ins_ref: InstructionRef,
pub branch_dest: Option<u64>,
}
#[derive(Debug, Clone)]
pub struct ParsedInstruction {
pub ins_ref: InstructionRef,
pub mnemonic: Cow<'static, str>,
pub args: Vec<InstructionArg>,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
pub enum ObjSymbolKind {
pub enum SymbolKind {
#[default]
Unknown,
Function,
@@ -132,60 +186,65 @@ pub enum ObjSymbolKind {
Section,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ObjSymbol {
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
pub struct Symbol {
pub name: String,
pub demangled_name: Option<String>,
pub address: u64,
pub section_address: u64,
pub size: u64,
pub size_known: bool,
pub kind: ObjSymbolKind,
pub flags: ObjSymbolFlagSet,
pub orig_section_index: Option<usize>,
pub kind: SymbolKind,
pub section: Option<usize>,
pub flags: SymbolFlagSet,
/// Alignment (from Metrowerks .comment section)
pub align: Option<NonZeroU32>,
/// Original virtual address (from .note.split section)
pub virtual_address: Option<u64>,
/// Original index in object symbol table
pub original_index: Option<usize>,
pub bytes: Vec<u8>,
}
pub struct ObjInfo {
pub arch: Box<dyn ObjArch>,
pub path: Option<String>,
#[cfg(feature = "std")]
pub timestamp: Option<filetime::FileTime>,
pub sections: Vec<ObjSection>,
/// Common BSS symbols
pub common: Vec<ObjSymbol>,
#[derive(Debug)]
pub struct Object {
pub arch: Box<dyn Arch>,
pub symbols: Vec<Symbol>,
pub sections: Vec<Section>,
/// Split object metadata (.note.split section)
pub split_meta: Option<SplitMeta>,
#[cfg(feature = "std")]
pub path: Option<std::path::PathBuf>,
#[cfg(feature = "std")]
pub timestamp: Option<filetime::FileTime>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ObjReloc {
impl Default for Object {
fn default() -> Self {
Self {
arch: ArchDummy::new(),
symbols: vec![],
sections: vec![],
split_meta: None,
#[cfg(feature = "std")]
path: None,
#[cfg(feature = "std")]
timestamp: None,
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Relocation {
pub flags: RelocationFlags,
pub address: u64,
pub target: ObjSymbol,
pub target_symbol: usize,
pub addend: i64,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct SymbolRef {
pub section_idx: usize,
pub symbol_idx: usize,
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum RelocationFlags {
Elf(u32),
Coff(u16),
}
pub const SECTION_COMMON: usize = usize::MAX - 1;
impl ObjInfo {
pub fn section_symbol(&self, symbol_ref: SymbolRef) -> (Option<&ObjSection>, &ObjSymbol) {
if symbol_ref.section_idx == SECTION_COMMON {
let symbol = &self.common[symbol_ref.symbol_idx];
return (None, symbol);
}
let section = &self.sections[symbol_ref.section_idx];
let symbol = &section.symbols[symbol_ref.symbol_idx];
(Some(section), symbol)
}
#[derive(Clone, Copy)]
pub struct ResolvedRelocation<'a> {
pub relocation: &'a Relocation,
pub symbol: &'a Symbol,
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,161 @@
---
source: objdiff-core/src/obj/read.rs
expression: "(sections, symbols)"
---
(
[
Section {
id: ".text-0",
name: ".text",
address: 0,
size: 8,
kind: Code,
data: SectionData(
8,
),
flags: FlagSet(),
relocations: [
Relocation {
flags: Elf(
0,
),
address: 0,
target_symbol: 0,
addend: 4,
},
Relocation {
flags: Elf(
0,
),
address: 2,
target_symbol: 1,
addend: 0,
},
Relocation {
flags: Elf(
0,
),
address: 4,
target_symbol: 0,
addend: 10,
},
],
line_info: {},
virtual_address: None,
},
Section {
id: ".data-0",
name: ".data",
address: 0,
size: 12,
kind: Data,
data: SectionData(
12,
),
flags: FlagSet(Combined),
relocations: [
Relocation {
flags: Elf(
0,
),
address: 0,
target_symbol: 2,
addend: 0,
},
Relocation {
flags: Elf(
0,
),
address: 4,
target_symbol: 2,
addend: 0,
},
],
line_info: {
0: 1,
8: 2,
},
virtual_address: None,
},
Section {
id: ".data-1",
name: ".data",
address: 0,
size: 0,
kind: Data,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
relocations: [],
line_info: {},
virtual_address: None,
},
Section {
id: ".data-2",
name: ".data",
address: 0,
size: 0,
kind: Data,
data: SectionData(
0,
),
flags: FlagSet(Hidden),
relocations: [],
line_info: {},
virtual_address: None,
},
],
[
Symbol {
name: ".data",
demangled_name: None,
address: 0,
size: 0,
kind: Section,
section: Some(
1,
),
flags: FlagSet(),
align: None,
virtual_address: None,
},
Symbol {
name: "symbol",
demangled_name: None,
address: 4,
size: 4,
kind: Object,
section: Some(
1,
),
flags: FlagSet(),
align: None,
virtual_address: None,
},
Symbol {
name: "function",
demangled_name: None,
address: 0,
size: 8,
kind: Function,
section: Some(
0,
),
flags: FlagSet(),
align: None,
virtual_address: None,
},
Symbol {
name: ".data",
demangled_name: None,
address: 0,
size: 0,
kind: Unknown,
section: None,
flags: FlagSet(),
align: None,
virtual_address: None,
},
],
)