Files
objdiff/objdiff-core/src/obj/mod.rs
T

400 lines
11 KiB
Rust
Raw Normal View History

2024-03-16 23:30:27 -06:00
pub mod read;
pub mod split_meta;
2022-09-11 13:52:55 -04:00
use alloc::{
borrow::Cow,
boxed::Box,
collections::BTreeMap,
string::{String, ToString},
vec,
vec::Vec,
};
use core::{
fmt,
num::{NonZeroU32, NonZeroU64},
};
2022-09-08 17:19:20 -04:00
2025-03-04 22:31:38 -07:00
use flagset::{FlagSet, flags};
2022-09-08 17:19:20 -04:00
2025-02-20 17:48:00 -07:00
use crate::{
arch::{Arch, ArchDummy},
obj::split_meta::SplitMeta,
util::ReallySigned,
};
2025-02-20 17:48:00 -07:00
#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)]
pub enum SectionKind {
#[default]
Unknown = -1,
2022-09-08 17:19:20 -04:00
Code,
Data,
Bss,
2025-02-20 17:48:00 -07:00
Common,
2022-09-08 17:19:20 -04:00
}
2025-02-20 17:48:00 -07:00
2022-09-08 17:19:20 -04:00
flags! {
2025-02-20 17:48:00 -07:00
#[derive(Hash)]
pub enum SymbolFlag: u8 {
2022-09-08 17:19:20 -04:00
Global,
Local,
Weak,
Common,
Hidden,
2024-09-09 19:43:10 -06:00
/// Has extra data associated with the symbol
/// (e.g. exception table entry)
HasExtra,
2025-02-20 17:48:00 -07:00
/// Symbol size was missing and was inferred
SizeInferred,
/// Symbol should be ignored by any diffing
Ignored,
2022-09-08 17:19:20 -04:00
}
}
2025-02-20 17:48:00 -07:00
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,
2022-09-08 17:19:20 -04:00
pub name: String,
pub address: u64,
pub size: u64,
2025-02-20 17:48:00 -07:00
pub kind: SectionKind,
pub data: SectionData,
pub flags: SectionFlagSet,
pub align: Option<NonZeroU64>,
2025-02-20 17:48:00 -07:00
pub relocations: Vec<Relocation>,
/// Line number info (.line or .debug_line section)
2024-08-20 21:40:32 -06:00
pub line_info: BTreeMap<u64, u32>,
2025-02-20 17:48:00 -07:00
/// 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
}
}
2025-05-06 21:30:53 -07:00
pub fn symbol_data(&self, symbol: &Symbol) -> Option<&[u8]> {
let offset = symbol.address.checked_sub(self.address)?;
self.data.get(offset as usize..offset as usize + symbol.size as usize)
}
// The alignment to use when "Combine data/text sections" is enabled.
pub fn combined_alignment(&self) -> u64 {
const MIN_ALIGNMENT: u64 = 4;
self.align.map(|align| align.get().max(MIN_ALIGNMENT)).unwrap_or(MIN_ALIGNMENT)
}
2025-02-20 17:48:00 -07:00
pub fn relocation_at<'obj>(
&'obj self,
obj: &'obj Object,
ins_ref: InstructionRef,
2025-02-20 17:48:00 -07:00
) -> Option<ResolvedRelocation<'obj>> {
match self.relocations.binary_search_by_key(&ins_ref.address, |r| r.address) {
Ok(i) => self.relocations.get(i),
Err(i) => self
.relocations
.get(i)
.take_if(|r| r.address < ins_ref.address + ins_ref.size as u64),
}
.and_then(|relocation| {
2025-02-20 17:48:00 -07:00
let symbol = obj.symbols.get(relocation.target_symbol)?;
Some(ResolvedRelocation { relocation, symbol })
})
}
2022-09-08 17:19:20 -04:00
}
2023-10-05 23:40:45 -04:00
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum InstructionArgValue<'a> {
2024-03-16 23:30:27 -06:00
Signed(i64),
Unsigned(u64),
Opaque(Cow<'a, str>),
}
impl InstructionArgValue<'_> {
2025-02-20 17:48:00 -07:00
pub fn loose_eq(&self, other: &InstructionArgValue) -> bool {
match (self, other) {
2025-02-20 17:48:00 -07:00
(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,
}
}
pub fn to_static(&self) -> InstructionArgValue<'static> {
match self {
InstructionArgValue::Signed(v) => InstructionArgValue::Signed(*v),
InstructionArgValue::Unsigned(v) => InstructionArgValue::Unsigned(*v),
InstructionArgValue::Opaque(v) => InstructionArgValue::Opaque(v.to_string().into()),
}
}
pub fn into_static(self) -> InstructionArgValue<'static> {
match self {
InstructionArgValue::Signed(v) => InstructionArgValue::Signed(v),
InstructionArgValue::Unsigned(v) => InstructionArgValue::Unsigned(v),
InstructionArgValue::Opaque(v) => {
InstructionArgValue::Opaque(Cow::Owned(v.into_owned()))
}
}
}
}
impl fmt::Display for InstructionArgValue<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
2025-02-20 17:48:00 -07:00
InstructionArgValue::Signed(v) => write!(f, "{:#x}", ReallySigned(*v)),
InstructionArgValue::Unsigned(v) => write!(f, "{:#x}", v),
InstructionArgValue::Opaque(v) => write!(f, "{}", v),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum InstructionArg<'a> {
Value(InstructionArgValue<'a>),
2025-02-20 17:48:00 -07:00
Reloc,
BranchDest(u64),
2022-09-08 17:19:20 -04:00
}
impl InstructionArg<'_> {
2025-02-20 17:48:00 -07:00
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,
}
2024-10-31 17:27:27 -06:00
}
pub fn to_static(&self) -> InstructionArg<'static> {
match self {
InstructionArg::Value(v) => InstructionArg::Value(v.to_static()),
InstructionArg::Reloc => InstructionArg::Reloc,
InstructionArg::BranchDest(v) => InstructionArg::BranchDest(*v),
}
}
pub fn into_static(self) -> InstructionArg<'static> {
match self {
InstructionArg::Value(v) => InstructionArg::Value(v.into_static()),
InstructionArg::Reloc => InstructionArg::Reloc,
InstructionArg::BranchDest(v) => InstructionArg::BranchDest(v),
}
}
2024-10-31 17:27:27 -06:00
}
#[derive(Copy, Clone, Debug, Default)]
2025-02-20 17:48:00 -07:00
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<'static>>,
2025-02-20 17:48:00 -07:00
}
2024-10-09 21:44:18 -06:00
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
2025-02-20 17:48:00 -07:00
pub enum SymbolKind {
2024-10-09 21:44:18 -06:00
#[default]
Unknown,
Function,
Object,
Section,
}
2025-02-20 17:48:00 -07:00
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
pub struct Symbol {
2022-09-08 17:19:20 -04:00
pub name: String,
pub demangled_name: Option<String>,
pub address: u64,
pub size: u64,
2025-02-20 17:48:00 -07:00
pub kind: SymbolKind,
pub section: Option<usize>,
pub flags: SymbolFlagSet,
/// Alignment (from Metrowerks .comment section)
pub align: Option<NonZeroU32>,
2024-03-04 18:06:21 -07:00
/// Original virtual address (from .note.split section)
pub virtual_address: Option<u64>,
2024-07-22 00:25:54 -04:00
}
2025-02-20 17:48:00 -07:00
#[derive(Debug)]
pub struct Object {
pub arch: Box<dyn Arch>,
pub endianness: object::Endianness,
2025-02-20 17:48:00 -07:00
pub symbols: Vec<Symbol>,
pub sections: Vec<Section>,
2024-03-04 18:06:21 -07:00
/// Split object metadata (.note.split section)
pub split_meta: Option<SplitMeta>,
2025-02-20 17:48:00 -07:00
#[cfg(feature = "std")]
pub path: Option<std::path::PathBuf>,
#[cfg(feature = "std")]
pub timestamp: Option<filetime::FileTime>,
2022-09-08 17:19:20 -04:00
}
2025-02-20 17:48:00 -07:00
impl Default for Object {
fn default() -> Self {
Self {
arch: ArchDummy::new(),
endianness: object::Endianness::Little,
2025-02-20 17:48:00 -07:00
symbols: vec![],
sections: vec![],
split_meta: None,
#[cfg(feature = "std")]
path: None,
#[cfg(feature = "std")]
timestamp: None,
}
}
}
impl Object {
pub fn resolve_instruction_ref(
&self,
symbol_index: usize,
ins_ref: InstructionRef,
) -> Option<ResolvedInstructionRef> {
let symbol = self.symbols.get(symbol_index)?;
let section_index = symbol.section?;
let section = self.sections.get(section_index)?;
let offset = ins_ref.address.checked_sub(section.address)?;
let code = section.data.get(offset as usize..offset as usize + ins_ref.size as usize)?;
let relocation = section.relocation_at(self, ins_ref);
Some(ResolvedInstructionRef {
ins_ref,
symbol_index,
symbol,
section,
section_index,
code,
relocation,
})
}
pub fn symbol_data(&self, symbol_index: usize) -> Option<&[u8]> {
let symbol = self.symbols.get(symbol_index)?;
let section_index = symbol.section?;
let section = self.sections.get(section_index)?;
let offset = symbol.address.checked_sub(section.address)?;
section.data.get(offset as usize..offset as usize + symbol.size as usize)
}
}
2025-02-20 17:48:00 -07:00
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Relocation {
pub flags: RelocationFlags,
2022-09-08 17:19:20 -04:00
pub address: u64,
2025-02-20 17:48:00 -07:00
pub target_symbol: usize,
pub addend: i64,
2022-09-08 17:19:20 -04:00
}
2024-03-18 22:56:13 -06:00
2025-02-20 17:48:00 -07:00
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum RelocationFlags {
Elf(u32),
Coff(u16),
2024-03-18 22:56:13 -06:00
}
#[derive(Debug, Copy, Clone)]
2025-02-20 17:48:00 -07:00
pub struct ResolvedRelocation<'a> {
pub relocation: &'a Relocation,
pub symbol: &'a Symbol,
2024-03-18 22:56:13 -06:00
}
#[derive(Debug, Copy, Clone)]
pub struct ResolvedInstructionRef<'obj> {
pub ins_ref: InstructionRef,
pub symbol_index: usize,
pub symbol: &'obj Symbol,
pub section_index: usize,
pub section: &'obj Section,
pub code: &'obj [u8],
pub relocation: Option<ResolvedRelocation<'obj>>,
}
static DUMMY_SYMBOL: Symbol = Symbol {
name: String::new(),
demangled_name: None,
address: 0,
size: 0,
kind: SymbolKind::Unknown,
section: None,
flags: SymbolFlagSet::empty(),
align: None,
virtual_address: None,
};
static DUMMY_SECTION: Section = Section {
id: String::new(),
name: String::new(),
address: 0,
size: 0,
kind: SectionKind::Unknown,
data: SectionData(Vec::new()),
flags: SectionFlagSet::empty(),
align: None,
relocations: Vec::new(),
line_info: BTreeMap::new(),
virtual_address: None,
};
impl Default for ResolvedInstructionRef<'_> {
fn default() -> Self {
Self {
ins_ref: InstructionRef::default(),
symbol_index: 0,
symbol: &DUMMY_SYMBOL,
section_index: 0,
section: &DUMMY_SECTION,
code: &[],
relocation: None,
}
}
}