WIP objdiff 3.0 refactor

This commit is contained in:
Luke Street
2025-02-26 22:09:39 -07:00
parent 6d3c63ccd8
commit f3c157ff06
79 changed files with 14886 additions and 6820 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+218 -180
View File
@@ -1,25 +1,27 @@
use alloc::{borrow::Cow, collections::BTreeMap, format, string::ToString, vec::Vec};
use alloc::{borrow::Cow, format, string::ToString, vec::Vec};
use core::ops::Range;
use anyhow::{bail, Result};
use object::{
elf, Endian, Endianness, File, FileFlags, Object, ObjectSection, ObjectSymbol, Relocation,
RelocationFlags, RelocationTarget,
};
use object::{elf, Endian as _, Object as _, ObjectSection as _, ObjectSymbol as _};
use rabbitizer::{
abi::Abi,
operands::{ValuedOperand, IU16},
registers_meta::Register,
Instruction, InstructionDisplayFlags, InstructionFlags, IsaExtension, IsaVersion, Vram,
IsaExtension, IsaVersion, Vram,
};
use crate::{
arch::{ObjArch, ProcessCodeResult},
diff::{DiffObjConfig, MipsAbi, MipsInstrCategory},
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
arch::Arch,
diff::{display::InstructionPart, DiffObjConfig, MipsAbi, MipsInstrCategory},
obj::{
InstructionArg, InstructionArgValue, InstructionRef, Relocation, RelocationFlags,
ResolvedRelocation, ScannedInstruction,
},
};
pub struct ObjArchMips {
pub endianness: Endianness,
#[derive(Debug)]
pub struct ArchMips {
pub endianness: object::Endianness,
pub abi: Abi,
pub isa_extension: Option<IsaExtension>,
pub ri_gp_value: i32,
@@ -33,13 +35,13 @@ const EF_MIPS_MACH_5900: u32 = 0x00920000;
const R_MIPS15_S3: u32 = 119;
impl ObjArchMips {
pub fn new(object: &File) -> Result<Self> {
impl ArchMips {
pub fn new(object: &object::File) -> Result<Self> {
let mut abi = Abi::O32;
let mut isa_extension = None;
match object.flags() {
FileFlags::None => {}
FileFlags::Elf { e_flags, .. } => {
object::FileFlags::None => {}
object::FileFlags::Elf { e_flags, .. } => {
abi = match e_flags & EF_MIPS_ABI {
elf::EF_MIPS_ABI_O32 | elf::EF_MIPS_ABI_O64 => Abi::O32,
elf::EF_MIPS_ABI_EABI32 | elf::EF_MIPS_ABI_EABI64 => Abi::N32,
@@ -73,19 +75,9 @@ impl ObjArchMips {
Ok(Self { endianness: object.endianness(), abi, isa_extension, ri_gp_value })
}
}
impl ObjArch for ObjArchMips {
fn process_code(
&self,
address: u64,
code: &[u8],
section_index: usize,
relocations: &[ObjReloc],
line_info: &BTreeMap<u64, u32>,
config: &DiffObjConfig,
) -> Result<ProcessCodeResult> {
let isa_extension = match config.mips_instr_category {
fn instruction_flags(&self, diff_config: &DiffObjConfig) -> rabbitizer::InstructionFlags {
let isa_extension = match diff_config.mips_instr_category {
MipsInstrCategory::Auto => self.isa_extension,
MipsInstrCategory::Cpu => None,
MipsInstrCategory::Rsp => Some(IsaExtension::RSP),
@@ -93,158 +85,105 @@ impl ObjArch for ObjArchMips {
MipsInstrCategory::R4000allegrex => Some(IsaExtension::R4000ALLEGREX),
MipsInstrCategory::R5900 => Some(IsaExtension::R5900),
};
let instruction_flags = match isa_extension {
Some(extension) => InstructionFlags::new_extension(extension),
None => InstructionFlags::new_isa(IsaVersion::MIPS_III, None),
match isa_extension {
Some(extension) => rabbitizer::InstructionFlags::new_extension(extension),
None => rabbitizer::InstructionFlags::new_isa(IsaVersion::MIPS_III, None),
}
.with_abi(match config.mips_abi {
.with_abi(match diff_config.mips_abi {
MipsAbi::Auto => self.abi,
MipsAbi::O32 => Abi::O32,
MipsAbi::N32 => Abi::N32,
MipsAbi::N64 => Abi::N64,
});
let display_flags = InstructionDisplayFlags::default().with_unknown_instr_comment(false);
})
}
fn instruction_display_flags(
&self,
_diff_config: &DiffObjConfig,
) -> rabbitizer::InstructionDisplayFlags {
rabbitizer::InstructionDisplayFlags::default().with_unknown_instr_comment(false)
}
fn parse_ins_ref(
&self,
ins_ref: InstructionRef,
code: &[u8],
diff_config: &DiffObjConfig,
) -> Result<rabbitizer::Instruction> {
Ok(rabbitizer::Instruction::new(
self.endianness.read_u32_bytes(code.try_into()?),
Vram::new(ins_ref.address as u32),
self.instruction_flags(diff_config),
))
}
}
impl Arch for ArchMips {
fn scan_instructions(
&self,
address: u64,
code: &[u8],
_section_index: usize,
diff_config: &DiffObjConfig,
) -> Result<Vec<ScannedInstruction>> {
let instruction_flags = self.instruction_flags(diff_config);
let start_address = address;
let end_address = address + code.len() as u64;
let ins_count = code.len() / 4;
let mut ops = Vec::<u16>::with_capacity(ins_count);
let mut insts = Vec::<ObjIns>::with_capacity(ins_count);
let mut ops = Vec::<ScannedInstruction>::with_capacity(code.len() / 4);
let mut cur_addr = start_address as u32;
for chunk in code.chunks_exact(4) {
let reloc = relocations.iter().find(|r| (r.address as u32 & !3) == cur_addr);
let code = self.endianness.read_u32_bytes(chunk.try_into()?);
let instruction = Instruction::new(code, Vram::new(cur_addr), instruction_flags);
let formatted = instruction.display(&display_flags, None::<&str>, 0).to_string();
let op = instruction.opcode() as u16;
ops.push(op);
let mnemonic = instruction.opcode().name();
let mut branch_dest = instruction.get_branch_offset_generic().map(|a| a.inner() as u64);
let operands = instruction.valued_operands_iter();
let mut args = Vec::with_capacity(6);
for (idx, op) in operands.enumerate() {
if idx > 0 {
args.push(ObjInsArg::PlainText(config.separator().into()));
}
match op {
ValuedOperand::core_immediate(imm) => {
if let Some(reloc) = reloc {
push_reloc(&mut args, reloc)?;
} else {
args.push(ObjInsArg::Arg(match imm {
IU16::Integer(s) => ObjInsArgValue::Signed(s as i64),
IU16::Unsigned(u) => ObjInsArgValue::Unsigned(u as u64),
}));
}
}
ValuedOperand::core_label(..) | ValuedOperand::core_branch_target_label(..) => {
if let Some(reloc) = reloc {
// If the relocation target is within the current function, we can
// convert it into a relative branch target. Note that we check
// target_address > start_address instead of >= so that recursive
// tail calls are not considered branch targets.
let target_address =
reloc.target.address.checked_add_signed(reloc.addend);
if reloc.target.orig_section_index == Some(section_index)
&& matches!(target_address, Some(addr) if addr > start_address && addr < end_address)
{
let target_address = target_address.unwrap();
args.push(ObjInsArg::BranchDest(target_address));
branch_dest = Some(target_address);
} else {
push_reloc(&mut args, reloc)?;
branch_dest = None;
}
} else if let Some(branch_dest) = branch_dest {
args.push(ObjInsArg::BranchDest(branch_dest));
} else {
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
op.display(&instruction, &display_flags, None::<&str>)
.to_string()
.into(),
)));
}
}
ValuedOperand::core_immediate_base(imm, base) => {
if let Some(reloc) = reloc {
push_reloc(&mut args, reloc)?;
} else {
args.push(ObjInsArg::Arg(match imm {
IU16::Integer(s) => ObjInsArgValue::Signed(s as i64),
IU16::Unsigned(u) => ObjInsArgValue::Unsigned(u as u64),
}));
}
args.push(ObjInsArg::PlainText("(".into()));
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
base.either_name(instruction.flags().abi(), display_flags.named_gpr())
.into(),
)));
args.push(ObjInsArg::PlainText(")".into()));
}
// ValuedOperand::r5900_immediate15(..) => match reloc {
// Some(reloc)
// if reloc.flags == RelocationFlags::Elf { r_type: R_MIPS15_S3 } =>
// {
// push_reloc(&mut args, reloc)?;
// }
// _ => {
// args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
// op.disassemble(&instruction, None).into(),
// )));
// }
// },
_ => {
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(
op.display(&instruction, &display_flags, None::<&str>)
.to_string()
.into(),
)));
}
}
}
let line = line_info.range(..=cur_addr as u64).last().map(|(_, &b)| b);
insts.push(ObjIns {
address: cur_addr as u64,
size: 4,
op,
mnemonic: Cow::Borrowed(mnemonic),
args,
reloc: reloc.cloned(),
let vram = Vram::new(cur_addr);
let instruction = rabbitizer::Instruction::new(code, vram, instruction_flags);
let opcode = instruction.opcode() as u16;
let branch_dest =
instruction.get_branch_offset_generic().map(|o| (vram + o).inner() as u64);
ops.push(ScannedInstruction {
ins_ref: InstructionRef { address, size: 4, opcode },
branch_dest,
line,
formatted,
orig: None,
});
cur_addr += 4;
}
Ok(ProcessCodeResult { ops, insts })
Ok(ops)
}
fn display_instruction(
&self,
ins_ref: InstructionRef,
code: &[u8],
relocation: Option<ResolvedRelocation>,
function_range: Range<u64>,
section_index: usize,
diff_config: &DiffObjConfig,
cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
) -> Result<()> {
let instruction = self.parse_ins_ref(ins_ref, code, diff_config)?;
let display_flags = self.instruction_display_flags(diff_config);
let opcode = instruction.opcode();
cb(InstructionPart::Opcode(Cow::Borrowed(opcode.name()), opcode as u16))?;
push_args(&instruction, relocation, function_range, section_index, &display_flags, cb)?;
Ok(())
}
fn implcit_addend(
&self,
file: &File<'_>,
section: &ObjSection,
file: &object::File<'_>,
section: &object::Section,
address: u64,
reloc: &Relocation,
reloc: &object::Relocation,
flags: RelocationFlags,
) -> Result<i64> {
let data = section.data[address as usize..address as usize + 4].try_into()?;
let addend = self.endianness.read_u32_bytes(data);
Ok(match reloc.flags() {
RelocationFlags::Elf { r_type: elf::R_MIPS_32 } => addend as i64,
RelocationFlags::Elf { r_type: elf::R_MIPS_26 } => ((addend & 0x03FFFFFF) << 2) as i64,
RelocationFlags::Elf { r_type: elf::R_MIPS_HI16 } => {
((addend & 0x0000FFFF) << 16) as i32 as i64
let data = section.data()?;
let code = data[address as usize..address as usize + 4].try_into()?;
let addend = self.endianness.read_u32_bytes(code);
Ok(match flags {
RelocationFlags::Elf(elf::R_MIPS_32) => addend as i64,
RelocationFlags::Elf(elf::R_MIPS_26) => ((addend & 0x03FFFFFF) << 2) as i64,
RelocationFlags::Elf(elf::R_MIPS_HI16) => ((addend & 0x0000FFFF) << 16) as i32 as i64,
RelocationFlags::Elf(elf::R_MIPS_LO16 | elf::R_MIPS_GOT16 | elf::R_MIPS_CALL16) => {
(addend & 0x0000FFFF) as i16 as i64
}
RelocationFlags::Elf {
r_type: elf::R_MIPS_LO16 | elf::R_MIPS_GOT16 | elf::R_MIPS_CALL16,
} => (addend & 0x0000FFFF) as i16 as i64,
RelocationFlags::Elf { r_type: elf::R_MIPS_GPREL16 | elf::R_MIPS_LITERAL } => {
let RelocationTarget::Symbol(idx) = reloc.target() else {
RelocationFlags::Elf(elf::R_MIPS_GPREL16 | elf::R_MIPS_LITERAL) => {
let object::RelocationTarget::Symbol(idx) = reloc.target() else {
bail!("Unsupported R_MIPS_GPREL16 relocation against a non-symbol");
};
let sym = file.symbol_by_index(idx)?;
@@ -257,15 +196,15 @@ impl ObjArch for ObjArchMips {
(addend & 0x0000FFFF) as i16 as i64
}
}
RelocationFlags::Elf { r_type: elf::R_MIPS_PC16 } => 0, // PC-relative relocation
RelocationFlags::Elf { r_type: R_MIPS15_S3 } => ((addend & 0x001FFFC0) >> 3) as i64,
RelocationFlags::Elf(elf::R_MIPS_PC16) => 0, // PC-relative relocation
RelocationFlags::Elf(R_MIPS15_S3) => ((addend & 0x001FFFC0) >> 3) as i64,
flags => bail!("Unsupported MIPS implicit relocation {flags:?}"),
})
}
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> {
match flags {
RelocationFlags::Elf { r_type } => match r_type {
RelocationFlags::Elf(r_type) => match r_type {
elf::R_MIPS_32 => Cow::Borrowed("R_MIPS_32"),
elf::R_MIPS_26 => Cow::Borrowed("R_MIPS_26"),
elf::R_MIPS_HI16 => Cow::Borrowed("R_MIPS_HI16"),
@@ -284,7 +223,7 @@ impl ObjArch for ObjArchMips {
fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize {
match flags {
RelocationFlags::Elf { r_type } => match r_type {
RelocationFlags::Elf(r_type) => match r_type {
elf::R_MIPS_16 => 2,
elf::R_MIPS_32 => 4,
_ => 1,
@@ -294,40 +233,139 @@ impl ObjArch for ObjArchMips {
}
}
fn push_reloc(args: &mut Vec<ObjInsArg>, reloc: &ObjReloc) -> Result<()> {
fn push_args(
instruction: &rabbitizer::Instruction,
relocation: Option<ResolvedRelocation>,
function_range: Range<u64>,
section_index: usize,
display_flags: &rabbitizer::InstructionDisplayFlags,
mut arg_cb: impl FnMut(InstructionPart) -> Result<()>,
) -> Result<()> {
let operands = instruction.valued_operands_iter();
for (idx, op) in operands.enumerate() {
if idx > 0 {
arg_cb(InstructionPart::Separator)?;
}
match op {
ValuedOperand::core_immediate(imm) => {
if let Some(resolved) = relocation {
push_reloc(resolved.relocation, &mut arg_cb)?;
} else {
arg_cb(InstructionPart::Arg(InstructionArg::Value(match imm {
IU16::Integer(s) => InstructionArgValue::Signed(s as i64),
IU16::Unsigned(u) => InstructionArgValue::Unsigned(u as u64),
})))?;
}
}
ValuedOperand::core_label(..) | ValuedOperand::core_branch_target_label(..) => {
if let Some(resolved) = relocation {
// If the relocation target is within the current function, we can
// convert it into a relative branch target. Note that we check
// target_address > start_address instead of >= so that recursive
// tail calls are not considered branch targets.
let target_address =
resolved.symbol.address.checked_add_signed(resolved.relocation.addend);
if resolved.symbol.section == Some(section_index)
&& target_address.is_some_and(|addr| {
addr > function_range.start && addr < function_range.end
})
{
// TODO move this logic up a level
let target_address = target_address.unwrap();
arg_cb(InstructionPart::Arg(InstructionArg::BranchDest(target_address)))?;
} else {
push_reloc(resolved.relocation, &mut arg_cb)?;
}
} else if let Some(branch_dest) = instruction
.get_branch_offset_generic()
.map(|o| (instruction.vram() + o).inner() as u64)
{
arg_cb(InstructionPart::Arg(InstructionArg::BranchDest(branch_dest)))?;
} else {
arg_cb(InstructionPart::Arg(InstructionArg::Value(
InstructionArgValue::Opaque(
op.display(instruction, display_flags, None::<&str>)
.to_string()
.into(),
),
)))?;
}
}
ValuedOperand::core_immediate_base(imm, base) => {
if let Some(resolved) = relocation {
push_reloc(resolved.relocation, &mut arg_cb)?;
} else {
arg_cb(InstructionPart::Arg(InstructionArg::Value(match imm {
IU16::Integer(s) => InstructionArgValue::Signed(s as i64),
IU16::Unsigned(u) => InstructionArgValue::Unsigned(u as u64),
})))?;
}
arg_cb(InstructionPart::Basic("("))?;
arg_cb(InstructionPart::Arg(InstructionArg::Value(InstructionArgValue::Opaque(
base.either_name(instruction.flags().abi(), display_flags.named_gpr()).into(),
))))?;
arg_cb(InstructionPart::Basic(")"))?;
}
// ValuedOperand::r5900_immediate15(..) => match relocation {
// Some(resolved)
// if resolved.relocation.flags == RelocationFlags::Elf(R_MIPS15_S3) =>
// {
// push_reloc(&resolved.relocation, &mut arg_cb, &mut plain_cb)?;
// }
// _ => {
// arg_cb(InstructionArg::Value(InstructionArgValue::Opaque(
// op.disassemble(&instruction, None).into(),
// )))?;
// }
// },
_ => {
arg_cb(InstructionPart::Arg(InstructionArg::Value(InstructionArgValue::Opaque(
op.display(instruction, display_flags, None::<&str>).to_string().into(),
))))?;
}
}
}
Ok(())
}
fn push_reloc(
reloc: &Relocation,
mut arg_cb: impl FnMut(InstructionPart) -> Result<()>,
) -> Result<()> {
match reloc.flags {
RelocationFlags::Elf { r_type } => match r_type {
RelocationFlags::Elf(r_type) => match r_type {
elf::R_MIPS_HI16 => {
args.push(ObjInsArg::PlainText("%hi(".into()));
args.push(ObjInsArg::Reloc);
args.push(ObjInsArg::PlainText(")".into()));
arg_cb(InstructionPart::Basic("%hi("))?;
arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?;
arg_cb(InstructionPart::Basic(")"))?;
}
elf::R_MIPS_LO16 => {
args.push(ObjInsArg::PlainText("%lo(".into()));
args.push(ObjInsArg::Reloc);
args.push(ObjInsArg::PlainText(")".into()));
arg_cb(InstructionPart::Basic("%lo("))?;
arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?;
arg_cb(InstructionPart::Basic(")"))?;
}
elf::R_MIPS_GOT16 => {
args.push(ObjInsArg::PlainText("%got(".into()));
args.push(ObjInsArg::Reloc);
args.push(ObjInsArg::PlainText(")".into()));
arg_cb(InstructionPart::Basic("%got("))?;
arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?;
arg_cb(InstructionPart::Basic(")"))?;
}
elf::R_MIPS_CALL16 => {
args.push(ObjInsArg::PlainText("%call16(".into()));
args.push(ObjInsArg::Reloc);
args.push(ObjInsArg::PlainText(")".into()));
arg_cb(InstructionPart::Basic("%call16("))?;
arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?;
arg_cb(InstructionPart::Basic(")"))?;
}
elf::R_MIPS_GPREL16 => {
args.push(ObjInsArg::PlainText("%gp_rel(".into()));
args.push(ObjInsArg::Reloc);
args.push(ObjInsArg::PlainText(")".into()));
arg_cb(InstructionPart::Basic("%gp_rel("))?;
arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?;
arg_cb(InstructionPart::Basic(")"))?;
}
elf::R_MIPS_32
| elf::R_MIPS_26
| elf::R_MIPS_LITERAL
| elf::R_MIPS_PC16
| R_MIPS15_S3 => {
args.push(ObjInsArg::Reloc);
arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?;
}
_ => bail!("Unsupported ELF MIPS relocation type {r_type}"),
},
+179 -57
View File
@@ -1,13 +1,16 @@
use alloc::{borrow::Cow, boxed::Box, collections::BTreeMap, format, string::String, vec::Vec};
use core::ffi::CStr;
use alloc::{borrow::Cow, boxed::Box, format, string::String, vec, vec::Vec};
use core::{ffi::CStr, fmt, fmt::Debug, ops::Range};
use anyhow::{bail, Result};
use byteorder::ByteOrder;
use object::{Architecture, File, Object, ObjectSymbol, Relocation, RelocationFlags, Symbol};
use object::{File, Relocation, Section};
use crate::{
diff::DiffObjConfig,
obj::{ObjIns, ObjReloc, ObjSection},
diff::{display::InstructionPart, DiffObjConfig},
obj::{
InstructionRef, ParsedInstruction, RelocationFlags, ResolvedRelocation, ScannedInstruction,
SymbolFlagSet, SymbolKind,
},
util::ReallySigned,
};
@@ -35,8 +38,8 @@ pub enum DataType {
String,
}
impl std::fmt::Display for DataType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl fmt::Display for DataType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DataType::Int8 => write!(f, "Int8"),
DataType::Int16 => write!(f, "Int16"),
@@ -154,23 +157,76 @@ impl DataType {
}
}
pub trait ObjArch: Send + Sync {
fn process_code(
pub trait Arch: Send + Sync + Debug {
/// Generate a list of instructions references (offset, size, opcode) from the given code.
///
/// The opcode IDs are used to generate the initial diff. Implementations should do as little
/// parsing as possible here: just enough to identify the base instruction opcode, size, and
/// possible branch destination (for visual representation). As needed, instructions are parsed
/// via `process_instruction` to compare their arguments.
fn scan_instructions(
&self,
address: u64,
code: &[u8],
section_index: usize,
relocations: &[ObjReloc],
line_info: &BTreeMap<u64, u32>,
config: &DiffObjConfig,
) -> Result<ProcessCodeResult>;
diff_config: &DiffObjConfig,
) -> Result<Vec<ScannedInstruction>>;
/// Parse an instruction to gather its mnemonic and arguments for more detailed comparison.
///
/// This is called only when we need to compare the arguments of an instruction.
fn process_instruction(
&self,
ins_ref: InstructionRef,
code: &[u8],
relocation: Option<ResolvedRelocation>,
function_range: Range<u64>,
section_index: usize,
diff_config: &DiffObjConfig,
) -> Result<ParsedInstruction> {
let mut mnemonic = None;
let mut args = Vec::with_capacity(8);
self.display_instruction(
ins_ref,
code,
relocation,
function_range,
section_index,
diff_config,
&mut |part| {
match part {
InstructionPart::Opcode(m, _) => mnemonic = Some(m),
InstructionPart::Arg(arg) => args.push(arg),
_ => {}
}
Ok(())
},
)?;
Ok(ParsedInstruction { ins_ref, mnemonic: mnemonic.unwrap_or_default(), args })
}
/// Format an instruction for display.
///
/// Implementations should call the callback for each part of the instruction: usually the
/// mnemonic and arguments, plus any separators and visual formatting.
fn display_instruction(
&self,
ins_ref: InstructionRef,
code: &[u8],
relocation: Option<ResolvedRelocation>,
function_range: Range<u64>,
section_index: usize,
diff_config: &DiffObjConfig,
cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
) -> Result<()>;
fn implcit_addend(
&self,
file: &File<'_>,
section: &ObjSection,
file: &object::File<'_>,
section: &object::Section,
address: u64,
reloc: &Relocation,
relocation: &object::Relocation,
flags: RelocationFlags,
) -> Result<i64>;
fn demangle(&self, _name: &str) -> Option<String> { None }
@@ -179,9 +235,20 @@ pub trait ObjArch: Send + Sync {
fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize;
fn symbol_address(&self, symbol: &Symbol) -> u64 { symbol.address() }
fn symbol_address(&self, address: u64, _kind: SymbolKind) -> u64 { address }
fn guess_data_type(&self, _instruction: &ObjIns) -> Option<DataType> { None }
fn extra_symbol_flags(&self, _symbol: &object::Symbol) -> SymbolFlagSet {
SymbolFlagSet::default()
}
fn guess_data_type(
&self,
_ins_ref: InstructionRef,
_code: &[u8],
_relocation: Option<ResolvedRelocation>,
) -> Option<DataType> {
None
}
fn display_data_labels(&self, _ty: DataType, bytes: &[u8]) -> Vec<String> {
vec![format!("Bytes: {:#x?}", bytes)]
@@ -191,58 +258,113 @@ pub trait ObjArch: Send + Sync {
vec![format!("{:#?}", bytes)]
}
fn display_ins_data_labels(&self, ins: &ObjIns) -> Vec<String> {
let Some(reloc) = ins.reloc.as_ref() else {
return Vec::new();
};
if reloc.addend >= 0 && reloc.target.bytes.len() > reloc.addend as usize {
return self
.guess_data_type(ins)
.map(|ty| {
self.display_data_labels(ty, &reloc.target.bytes[reloc.addend as usize..])
})
.unwrap_or_default();
}
fn display_ins_data_labels(
&self,
_ins_ref: InstructionRef,
_code: &[u8],
_relocation: Option<ResolvedRelocation>,
) -> Vec<String> {
// TODO
// let Some(reloc) = relocation else {
// return Vec::new();
// };
// if reloc.relocation.addend >= 0 && reloc.symbol.bytes.len() > reloc.relocation.addend as usize {
// return self
// .guess_data_type(ins)
// .map(|ty| {
// self.display_data_labels(ty, &reloc.target.bytes[reloc.addend as usize..])
// })
// .unwrap_or_default();
// }
Vec::new()
}
fn display_ins_data_literals(&self, ins: &ObjIns) -> Vec<String> {
let Some(reloc) = ins.reloc.as_ref() else {
return Vec::new();
};
if reloc.addend >= 0 && reloc.target.bytes.len() > reloc.addend as usize {
return self
.guess_data_type(ins)
.map(|ty| {
self.display_data_literals(ty, &reloc.target.bytes[reloc.addend as usize..])
})
.unwrap_or_default();
}
fn display_ins_data_literals(
&self,
_ins_ref: InstructionRef,
_code: &[u8],
_relocation: Option<ResolvedRelocation>,
) -> Vec<String> {
// TODO
// let Some(reloc) = ins.reloc.as_ref() else {
// return Vec::new();
// };
// if reloc.addend >= 0 && reloc.target.bytes.len() > reloc.addend as usize {
// return self
// .guess_data_type(ins)
// .map(|ty| {
// self.display_data_literals(ty, &reloc.target.bytes[reloc.addend as usize..])
// })
// .unwrap_or_default();
// }
Vec::new()
}
// Downcast methods
#[cfg(feature = "ppc")]
fn ppc(&self) -> Option<&ppc::ObjArchPpc> { None }
}
pub struct ProcessCodeResult {
pub ops: Vec<u16>,
pub insts: Vec<ObjIns>,
}
pub fn new_arch(object: &File) -> Result<Box<dyn ObjArch>> {
pub fn new_arch(object: &object::File) -> Result<Box<dyn Arch>> {
use object::Object as _;
Ok(match object.architecture() {
#[cfg(feature = "ppc")]
Architecture::PowerPc => Box::new(ppc::ObjArchPpc::new(object)?),
object::Architecture::PowerPc => Box::new(ppc::ArchPpc::new(object)?),
#[cfg(feature = "mips")]
Architecture::Mips => Box::new(mips::ObjArchMips::new(object)?),
object::Architecture::Mips => Box::new(mips::ArchMips::new(object)?),
#[cfg(feature = "x86")]
Architecture::I386 | Architecture::X86_64 => Box::new(x86::ObjArchX86::new(object)?),
object::Architecture::I386 | object::Architecture::X86_64 => {
Box::new(x86::ArchX86::new(object)?)
}
#[cfg(feature = "arm")]
Architecture::Arm => Box::new(arm::ObjArchArm::new(object)?),
object::Architecture::Arm => Box::new(arm::ArchArm::new(object)?),
#[cfg(feature = "arm64")]
Architecture::Aarch64 => Box::new(arm64::ObjArchArm64::new(object)?),
object::Architecture::Aarch64 => Box::new(arm64::ArchArm64::new(object)?),
arch => bail!("Unsupported architecture: {arch:?}"),
})
}
#[derive(Debug, Default)]
pub struct ArchDummy {}
impl ArchDummy {
pub fn new() -> Box<Self> { Box::new(Self {}) }
}
impl Arch for ArchDummy {
fn scan_instructions(
&self,
_address: u64,
_code: &[u8],
_section_index: usize,
_diff_config: &DiffObjConfig,
) -> Result<Vec<ScannedInstruction>> {
Ok(Vec::new())
}
fn display_instruction(
&self,
_ins_ref: InstructionRef,
_code: &[u8],
_relocation: Option<ResolvedRelocation>,
_function_range: Range<u64>,
_section_index: usize,
_diff_config: &DiffObjConfig,
_cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
) -> Result<()> {
Ok(())
}
fn implcit_addend(
&self,
_file: &File<'_>,
_section: &Section,
_address: u64,
_relocation: &Relocation,
_flags: RelocationFlags,
) -> Result<i64> {
Ok(0)
}
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> {
format!("{flags:?}").into()
}
fn get_reloc_byte_size(&self, _flags: RelocationFlags) -> usize { 0 }
}
File diff suppressed because it is too large Load Diff
+118 -53
View File
@@ -7,33 +7,93 @@ use alloc::{
vec,
vec::Vec,
};
use std::ops::Range;
use anyhow::{anyhow, bail, ensure, Result};
use iced_x86::{
Decoder, DecoderOptions, DecoratorKind, Formatter, FormatterOutput, FormatterTextKind,
GasFormatter, Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind,
PrefixKind, Register,
Decoder, DecoderOptions, DecoratorKind, FormatterOutput, FormatterTextKind, GasFormatter,
Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind, PrefixKind,
Register,
};
use object::{pe, Endian, Endianness, File, Object, Relocation, RelocationFlags};
use object::{pe, Endian as _, Object as _, ObjectSection as _};
use crate::{
arch::{ObjArch, ProcessCodeResult},
diff::{DiffObjConfig, X86Formatter},
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
arch::Arch,
diff::{display::InstructionPart, DiffObjConfig, X86Formatter},
obj::{
InstructionArg, InstructionArgValue, InstructionRef, ParsedInstruction, RelocationFlags,
ResolvedRelocation, ScannedInstruction,
},
};
pub struct ObjArchX86 {
#[derive(Debug)]
pub struct ArchX86 {
bits: u32,
endianness: Endianness,
endianness: object::Endianness,
}
impl ObjArchX86 {
pub fn new(object: &File) -> Result<Self> {
impl ArchX86 {
pub fn new(object: &object::File) -> Result<Self> {
Ok(Self { bits: if object.is_64() { 64 } else { 32 }, endianness: object.endianness() })
}
fn formatter(&self, diff_config: &DiffObjConfig) -> Box<dyn iced_x86::Formatter> {
let mut formatter: Box<dyn iced_x86::Formatter> = match diff_config.x86_formatter {
X86Formatter::Intel => Box::new(IntelFormatter::new()),
X86Formatter::Gas => Box::new(GasFormatter::new()),
X86Formatter::Nasm => Box::new(NasmFormatter::new()),
X86Formatter::Masm => Box::new(MasmFormatter::new()),
};
formatter.options_mut().set_space_after_operand_separator(diff_config.space_between_args);
formatter
}
}
impl ObjArch for ObjArchX86 {
impl Arch for ArchX86 {
fn scan_instructions(
&self,
address: u64,
code: &[u8],
_section_index: usize,
_diff_config: &DiffObjConfig,
) -> Result<Vec<ScannedInstruction>> {
let mut out = Vec::with_capacity(code.len() / 2);
let mut decoder = Decoder::with_ip(self.bits, code, address, DecoderOptions::NONE);
let mut instruction = Instruction::default();
while decoder.can_decode() {
decoder.decode_out(&mut instruction);
// TODO is this right?
let branch_dest = match instruction.op0_kind() {
OpKind::NearBranch16 => Some(instruction.near_branch16() as u64),
OpKind::NearBranch32 => Some(instruction.near_branch32() as u64),
OpKind::NearBranch64 => Some(instruction.near_branch64()),
_ => None,
};
out.push(ScannedInstruction {
ins_ref: InstructionRef {
address: instruction.ip(),
size: instruction.len() as u8,
opcode: instruction.mnemonic() as u16,
},
branch_dest,
});
}
Ok(out)
}
fn display_instruction(
&self,
ins_ref: InstructionRef,
code: &[u8],
relocation: Option<ResolvedRelocation>,
function_range: Range<u64>,
section_index: usize,
diff_config: &DiffObjConfig,
cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
) -> Result<()> {
todo!()
}
fn process_code(
&self,
address: u64,
@@ -45,13 +105,7 @@ impl ObjArch for ObjArchX86 {
) -> Result<ProcessCodeResult> {
let mut result = ProcessCodeResult { ops: Vec::new(), insts: Vec::new() };
let mut decoder = Decoder::with_ip(self.bits, code, address, DecoderOptions::NONE);
let mut formatter: Box<dyn Formatter> = match config.x86_formatter {
X86Formatter::Intel => Box::new(IntelFormatter::new()),
X86Formatter::Gas => Box::new(GasFormatter::new()),
X86Formatter::Nasm => Box::new(NasmFormatter::new()),
X86Formatter::Masm => Box::new(MasmFormatter::new()),
};
formatter.options_mut().set_space_after_operand_separator(config.space_between_args);
let mut formatter = self.formatter(config);
let mut output = InstructionFormatterOutput {
formatted: String::new(),
@@ -101,10 +155,12 @@ impl ObjArch for ObjArchX86 {
output.ins.formatted.clone_from(&output.formatted);
// 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, InstructionArg::Reloc))
{
let mut found = replace_arg(
OpKind::Memory,
ObjInsArg::Reloc,
InstructionArg::Reloc,
&mut output.ins.args,
&instruction,
&output.ins_operands,
@@ -112,7 +168,7 @@ impl ObjArch for ObjArchX86 {
if !found {
found = replace_arg(
OpKind::Immediate32,
ObjInsArg::Reloc,
InstructionArg::Reloc,
&mut output.ins.args,
&instruction,
&output.ins_operands,
@@ -120,7 +176,9 @@ impl ObjArch for ObjArchX86 {
}
ensure!(found, "x86: Failed to find operand for Absolute relocation");
}
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, InstructionArg::Reloc))
{
bail!("Failed to find relocation in instruction");
}
@@ -136,14 +194,15 @@ impl ObjArch for ObjArchX86 {
fn implcit_addend(
&self,
_file: &File<'_>,
section: &ObjSection,
_file: &object::File<'_>,
section: &object::Section,
address: u64,
reloc: &Relocation,
_relocation: &object::Relocation,
flags: RelocationFlags,
) -> Result<i64> {
match reloc.flags() {
RelocationFlags::Coff { typ: pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32 } => {
let data = section.data[address as usize..address as usize + 4].try_into()?;
match flags {
RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32) => {
let data = section.data()[address as usize..address as usize + 4].try_into()?;
Ok(self.endianness.read_i32_bytes(data) as i64)
}
flags => bail!("Unsupported x86 implicit relocation {flags:?}"),
@@ -162,7 +221,7 @@ impl ObjArch for ObjArchX86 {
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> {
match flags {
RelocationFlags::Coff { typ } => match typ {
RelocationFlags::Coff(typ) => match typ {
pe::IMAGE_REL_I386_DIR32 => Cow::Borrowed("IMAGE_REL_I386_DIR32"),
pe::IMAGE_REL_I386_REL32 => Cow::Borrowed("IMAGE_REL_I386_REL32"),
_ => Cow::Owned(format!("<{flags:?}>")),
@@ -173,7 +232,7 @@ impl ObjArch for ObjArchX86 {
fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize {
match flags {
RelocationFlags::Coff { typ } => match typ {
RelocationFlags::Coff(typ) => match typ {
pe::IMAGE_REL_I386_DIR16 => 2,
pe::IMAGE_REL_I386_REL16 => 2,
pe::IMAGE_REL_I386_DIR32 => 4,
@@ -187,8 +246,8 @@ impl ObjArch for ObjArchX86 {
fn replace_arg(
from: OpKind,
to: ObjInsArg,
args: &mut [ObjInsArg],
to: InstructionArg,
args: &mut [InstructionArg],
instruction: &Instruction,
ins_operands: &[Option<u32>],
) -> Result<bool> {
@@ -213,7 +272,7 @@ fn replace_arg(
struct InstructionFormatterOutput {
formatted: String,
ins: ObjIns,
ins: ParsedInstruction,
error: Option<anyhow::Error>,
ins_operands: Vec<Option<u32>>,
}
@@ -223,11 +282,13 @@ impl InstructionFormatterOutput {
// The formatter writes the '-' operator and then gives us a negative value,
// so convert it to a positive value to avoid double negatives
if value < 0
&& matches!(self.ins.args.last(), Some(ObjInsArg::Arg(ObjInsArgValue::Opaque(v))) if v == "-")
&& matches!(self.ins.args.last(), Some(InstructionArg::Value(InstructionArgValue::Opaque(v))) if v == "-")
{
self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(value.wrapping_abs())));
self.ins
.args
.push(InstructionArg::Value(InstructionArgValue::Signed(value.wrapping_abs())));
} else {
self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(value)));
self.ins.args.push(InstructionArg::Value(InstructionArgValue::Signed(value)));
}
}
}
@@ -242,10 +303,12 @@ impl FormatterOutput for InstructionFormatterOutput {
self.ins_operands.push(None);
match kind {
FormatterTextKind::Text | FormatterTextKind::Punctuation => {
self.ins.args.push(ObjInsArg::PlainText(text.to_string().into()));
self.ins.args.push(InstructionArg::PlainText(text.to_string().into()));
}
FormatterTextKind::Keyword | FormatterTextKind::Operator => {
self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(text.to_string().into())));
self.ins.args.push(InstructionArg::Value(InstructionArgValue::Opaque(
text.to_string().into(),
)));
}
_ => {
if self.error.is_none() {
@@ -258,12 +321,13 @@ impl FormatterOutput for InstructionFormatterOutput {
fn write_prefix(&mut self, _instruction: &Instruction, text: &str, _prefix: PrefixKind) {
self.formatted.push_str(text);
self.ins_operands.push(None);
self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(text.to_string().into())));
self.ins
.args
.push(InstructionArg::Value(InstructionArgValue::Opaque(text.to_string().into())));
}
fn write_mnemonic(&mut self, _instruction: &Instruction, text: &str) {
self.formatted.push_str(text);
// TODO: can iced-x86 guarantee 'static here?
self.ins.mnemonic = Cow::Owned(text.to_string());
}
@@ -284,10 +348,11 @@ impl FormatterOutput for InstructionFormatterOutput {
match kind {
FormatterTextKind::LabelAddress => {
if let Some(reloc) = self.ins.reloc.as_ref() {
if matches!(reloc.flags, RelocationFlags::Coff {
typ: pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32
}) {
self.ins.args.push(ObjInsArg::Reloc);
if matches!(
reloc.flags,
RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32)
) {
self.ins.args.push(InstructionArg::Reloc);
return;
} else if self.error.is_none() {
self.error = Some(anyhow!(
@@ -296,16 +361,14 @@ impl FormatterOutput for InstructionFormatterOutput {
));
}
}
self.ins.args.push(ObjInsArg::BranchDest(value));
self.ins.args.push(InstructionArg::BranchDest(value));
self.ins.branch_dest = Some(value);
return;
}
FormatterTextKind::FunctionAddress => {
if let Some(reloc) = self.ins.reloc.as_ref() {
if matches!(reloc.flags, RelocationFlags::Coff {
typ: pe::IMAGE_REL_I386_REL32
}) {
self.ins.args.push(ObjInsArg::Reloc);
if matches!(reloc.flags, RelocationFlags::Coff(pe::IMAGE_REL_I386_REL32)) {
self.ins.args.push(InstructionArg::Reloc);
return;
} else if self.error.is_none() {
self.error = Some(anyhow!(
@@ -332,7 +395,7 @@ impl FormatterOutput for InstructionFormatterOutput {
self.push_signed(value as i64);
}
NumberKind::UInt8 | NumberKind::UInt16 | NumberKind::UInt32 | NumberKind::UInt64 => {
self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(value)));
self.ins.args.push(InstructionArg::Value(InstructionArgValue::Unsigned(value)));
}
}
}
@@ -347,7 +410,7 @@ impl FormatterOutput for InstructionFormatterOutput {
) {
self.formatted.push_str(text);
self.ins_operands.push(instruction_operand);
self.ins.args.push(ObjInsArg::PlainText(text.to_string().into()));
self.ins.args.push(InstructionArg::PlainText(text.to_string().into()));
}
fn write_register(
@@ -360,6 +423,8 @@ impl FormatterOutput for InstructionFormatterOutput {
) {
self.formatted.push_str(text);
self.ins_operands.push(instruction_operand);
self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(text.to_string().into())));
self.ins
.args
.push(InstructionArg::Value(InstructionArgValue::Opaque(text.to_string().into())));
}
}
+228 -247
View File
@@ -1,18 +1,6 @@
#![allow(clippy::needless_lifetimes)] // Generated serde code
use alloc::string::ToString;
use crate::{
diff::{
ObjDataDiff, ObjDataDiffKind, ObjDiff, ObjInsArgDiff, ObjInsBranchFrom, ObjInsBranchTo,
ObjInsDiff, ObjInsDiffKind, ObjSectionDiff, ObjSymbolDiff,
},
obj,
obj::{
ObjInfo, ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSectionKind, ObjSymbol,
ObjSymbolFlagSet, ObjSymbolFlags,
},
};
use crate::{diff, obj};
// Protobuf diff types
include!(concat!(env!("OUT_DIR"), "/objdiff.diff.rs"));
@@ -20,242 +8,235 @@ include!(concat!(env!("OUT_DIR"), "/objdiff.diff.rs"));
include!(concat!(env!("OUT_DIR"), "/objdiff.diff.serde.rs"));
impl DiffResult {
pub fn new(left: Option<(&ObjInfo, &ObjDiff)>, right: Option<(&ObjInfo, &ObjDiff)>) -> Self {
pub fn new(
_left: Option<(&obj::Object, &diff::ObjectDiff)>,
_right: Option<(&obj::Object, &diff::ObjectDiff)>,
) -> Self {
Self {
left: left.map(|(obj, diff)| ObjectDiff::new(obj, diff)),
right: right.map(|(obj, diff)| ObjectDiff::new(obj, diff)),
// TODO
// left: left.map(|(obj, diff)| ObjectDiff::new(obj, diff)),
// right: right.map(|(obj, diff)| ObjectDiff::new(obj, diff)),
left: None,
right: None,
}
}
}
impl ObjectDiff {
pub fn new(obj: &ObjInfo, diff: &ObjDiff) -> Self {
Self {
sections: diff
.sections
.iter()
.enumerate()
.map(|(i, d)| SectionDiff::new(obj, i, d))
.collect(),
}
}
}
impl SectionDiff {
pub fn new(obj: &ObjInfo, section_index: usize, section_diff: &ObjSectionDiff) -> Self {
let section = &obj.sections[section_index];
let symbols = section_diff.symbols.iter().map(|d| SymbolDiff::new(obj, d)).collect();
let data = section_diff.data_diff.iter().map(|d| DataDiff::new(obj, d)).collect();
// TODO: section_diff.reloc_diff
Self {
name: section.name.to_string(),
kind: SectionKind::from(section.kind) as i32,
size: section.size,
address: section.address,
symbols,
data,
match_percent: section_diff.match_percent,
}
}
}
impl From<ObjSectionKind> for SectionKind {
fn from(value: ObjSectionKind) -> Self {
match value {
ObjSectionKind::Code => SectionKind::SectionText,
ObjSectionKind::Data => SectionKind::SectionData,
ObjSectionKind::Bss => SectionKind::SectionBss,
// TODO common
}
}
}
impl From<obj::SymbolRef> for SymbolRef {
fn from(value: obj::SymbolRef) -> Self {
Self {
section_index: if value.section_idx == obj::SECTION_COMMON {
None
} else {
Some(value.section_idx as u32)
},
symbol_index: value.symbol_idx as u32,
}
}
}
impl SymbolDiff {
pub fn new(object: &ObjInfo, symbol_diff: &ObjSymbolDiff) -> Self {
let (_section, symbol) = object.section_symbol(symbol_diff.symbol_ref);
let instructions = symbol_diff
.instructions
.iter()
.map(|ins_diff| InstructionDiff::new(object, ins_diff))
.collect();
Self {
symbol: Some(Symbol::new(symbol)),
instructions,
match_percent: symbol_diff.match_percent,
target: symbol_diff.target_symbol.map(SymbolRef::from),
}
}
}
impl DataDiff {
pub fn new(_object: &ObjInfo, data_diff: &ObjDataDiff) -> Self {
Self {
kind: DiffKind::from(data_diff.kind) as i32,
data: data_diff.data.clone(),
size: data_diff.len as u64,
}
}
}
impl Symbol {
pub fn new(value: &ObjSymbol) -> Self {
Self {
name: value.name.to_string(),
demangled_name: value.demangled_name.clone(),
address: value.address,
size: value.size,
flags: symbol_flags(value.flags),
}
}
}
fn symbol_flags(value: ObjSymbolFlagSet) -> u32 {
let mut flags = 0u32;
if value.0.contains(ObjSymbolFlags::Global) {
flags |= SymbolFlag::SymbolGlobal as u32;
}
if value.0.contains(ObjSymbolFlags::Local) {
flags |= SymbolFlag::SymbolLocal as u32;
}
if value.0.contains(ObjSymbolFlags::Weak) {
flags |= SymbolFlag::SymbolWeak as u32;
}
if value.0.contains(ObjSymbolFlags::Common) {
flags |= SymbolFlag::SymbolCommon as u32;
}
if value.0.contains(ObjSymbolFlags::Hidden) {
flags |= SymbolFlag::SymbolHidden as u32;
}
flags
}
impl Instruction {
pub fn new(object: &ObjInfo, instruction: &ObjIns) -> Self {
Self {
address: instruction.address,
size: instruction.size as u32,
opcode: instruction.op as u32,
mnemonic: instruction.mnemonic.to_string(),
formatted: instruction.formatted.clone(),
arguments: instruction.args.iter().map(Argument::new).collect(),
relocation: instruction.reloc.as_ref().map(|reloc| Relocation::new(object, reloc)),
branch_dest: instruction.branch_dest,
line_number: instruction.line,
original: instruction.orig.clone(),
}
}
}
impl Argument {
pub fn new(value: &ObjInsArg) -> Self {
Self {
value: Some(match value {
ObjInsArg::PlainText(s) => argument::Value::PlainText(s.to_string()),
ObjInsArg::Arg(v) => argument::Value::Argument(ArgumentValue::new(v)),
ObjInsArg::Reloc => argument::Value::Relocation(ArgumentRelocation {}),
ObjInsArg::BranchDest(dest) => argument::Value::BranchDest(*dest),
}),
}
}
}
impl ArgumentValue {
pub fn new(value: &ObjInsArgValue) -> Self {
Self {
value: Some(match value {
ObjInsArgValue::Signed(v) => argument_value::Value::Signed(*v),
ObjInsArgValue::Unsigned(v) => argument_value::Value::Unsigned(*v),
ObjInsArgValue::Opaque(v) => argument_value::Value::Opaque(v.to_string()),
}),
}
}
}
impl Relocation {
pub fn new(object: &ObjInfo, reloc: &ObjReloc) -> Self {
Self {
r#type: match reloc.flags {
object::RelocationFlags::Elf { r_type } => r_type,
object::RelocationFlags::MachO { r_type, .. } => r_type as u32,
object::RelocationFlags::Coff { typ } => typ as u32,
object::RelocationFlags::Xcoff { r_rtype, .. } => r_rtype as u32,
_ => unreachable!(),
},
type_name: object.arch.display_reloc(reloc.flags).into_owned(),
target: Some(RelocationTarget {
symbol: Some(Symbol::new(&reloc.target)),
addend: reloc.addend,
}),
}
}
}
impl InstructionDiff {
pub fn new(object: &ObjInfo, instruction_diff: &ObjInsDiff) -> Self {
Self {
instruction: instruction_diff.ins.as_ref().map(|ins| Instruction::new(object, ins)),
diff_kind: DiffKind::from(instruction_diff.kind) as i32,
branch_from: instruction_diff.branch_from.as_ref().map(InstructionBranchFrom::new),
branch_to: instruction_diff.branch_to.as_ref().map(InstructionBranchTo::new),
arg_diff: instruction_diff.arg_diff.iter().map(ArgumentDiff::new).collect(),
}
}
}
impl ArgumentDiff {
pub fn new(value: &Option<ObjInsArgDiff>) -> Self {
Self { diff_index: value.as_ref().map(|v| v.idx as u32) }
}
}
impl From<ObjInsDiffKind> for DiffKind {
fn from(value: ObjInsDiffKind) -> Self {
match value {
ObjInsDiffKind::None => DiffKind::DiffNone,
ObjInsDiffKind::OpMismatch => DiffKind::DiffOpMismatch,
ObjInsDiffKind::ArgMismatch => DiffKind::DiffArgMismatch,
ObjInsDiffKind::Replace => DiffKind::DiffReplace,
ObjInsDiffKind::Delete => DiffKind::DiffDelete,
ObjInsDiffKind::Insert => DiffKind::DiffInsert,
}
}
}
impl From<ObjDataDiffKind> for DiffKind {
fn from(value: ObjDataDiffKind) -> Self {
match value {
ObjDataDiffKind::None => DiffKind::DiffNone,
ObjDataDiffKind::Replace => DiffKind::DiffReplace,
ObjDataDiffKind::Delete => DiffKind::DiffDelete,
ObjDataDiffKind::Insert => DiffKind::DiffInsert,
}
}
}
impl InstructionBranchFrom {
pub fn new(value: &ObjInsBranchFrom) -> Self {
Self {
instruction_index: value.ins_idx.iter().map(|&x| x as u32).collect(),
branch_index: value.branch_idx as u32,
}
}
}
impl InstructionBranchTo {
pub fn new(value: &ObjInsBranchTo) -> Self {
Self { instruction_index: value.ins_idx as u32, branch_index: value.branch_idx as u32 }
}
}
// impl ObjectDiff {
// pub fn new(obj: &obj::Object, diff: &diff::ObjectDiff) -> Self {
// Self {
// sections: diff
// .sections
// .iter()
// .enumerate()
// .map(|(i, d)| SectionDiff::new(obj, i, d))
// .collect(),
// }
// }
// }
//
// impl SectionDiff {
// pub fn new(obj: &obj::Object, section_index: usize, section_diff: &diff::SectionDiff) -> Self {
// let section = &obj.sections[section_index];
// let symbols = section_diff.symbols.iter().map(|d| SymbolDiff::new(obj, d)).collect();
// let data = section_diff.data_diff.iter().map(|d| DataDiff::new(obj, d)).collect();
// // TODO: section_diff.reloc_diff
// Self {
// name: section.name.to_string(),
// kind: SectionKind::from(section.kind) as i32,
// size: section.size,
// address: section.address,
// symbols,
// data,
// match_percent: section_diff.match_percent,
// }
// }
// }
//
// impl From<obj::SectionKind> for SectionKind {
// fn from(value: obj::SectionKind) -> Self {
// match value {
// obj::SectionKind::Code => SectionKind::SectionText,
// obj::SectionKind::Data => SectionKind::SectionData,
// obj::SectionKind::Bss => SectionKind::SectionBss,
// // TODO common
// }
// }
// }
//
// impl SymbolDiff {
// pub fn new(object: &obj::Object, symbol_diff: &diff::SymbolDiff) -> Self {
// let symbol = object.symbols[symbol_diff.symbol_index];
// let instructions = symbol_diff
// .instruction_rows
// .iter()
// .map(|ins_diff| InstructionDiff::new(object, ins_diff))
// .collect();
// Self {
// symbol: Some(Symbol::new(symbol)),
// instructions,
// match_percent: symbol_diff.match_percent,
// target: symbol_diff.target_symbol.map(SymbolRef::from),
// }
// }
// }
//
// impl DataDiff {
// pub fn new(_object: &obj::Object, data_diff: &diff::DataDiff) -> Self {
// Self {
// kind: DiffKind::from(data_diff.kind) as i32,
// data: data_diff.data.clone(),
// size: data_diff.len as u64,
// }
// }
// }
//
// impl Symbol {
// pub fn new(value: &ObjSymbol) -> Self {
// Self {
// name: value.name.to_string(),
// demangled_name: value.demangled_name.clone(),
// address: value.address,
// size: value.size,
// flags: symbol_flags(value.flags),
// }
// }
// }
//
// fn symbol_flags(value: ObjSymbolFlagSet) -> u32 {
// let mut flags = 0u32;
// if value.0.contains(ObjSymbolFlags::Global) {
// flags |= SymbolFlag::SymbolGlobal as u32;
// }
// if value.0.contains(ObjSymbolFlags::Local) {
// flags |= SymbolFlag::SymbolLocal as u32;
// }
// if value.0.contains(ObjSymbolFlags::Weak) {
// flags |= SymbolFlag::SymbolWeak as u32;
// }
// if value.0.contains(ObjSymbolFlags::Common) {
// flags |= SymbolFlag::SymbolCommon as u32;
// }
// if value.0.contains(ObjSymbolFlags::Hidden) {
// flags |= SymbolFlag::SymbolHidden as u32;
// }
// flags
// }
//
// impl Instruction {
// pub fn new(object: &obj::Object, instruction: &ObjIns) -> Self {
// Self {
// address: instruction.address,
// size: instruction.size as u32,
// opcode: instruction.op as u32,
// mnemonic: instruction.mnemonic.to_string(),
// formatted: instruction.formatted.clone(),
// arguments: instruction.args.iter().map(Argument::new).collect(),
// relocation: instruction.reloc.as_ref().map(|reloc| Relocation::new(object, reloc)),
// branch_dest: instruction.branch_dest,
// line_number: instruction.line,
// original: instruction.orig.clone(),
// }
// }
// }
//
// impl Argument {
// pub fn new(value: &ObjInsArg) -> Self {
// Self {
// value: Some(match value {
// ObjInsArg::PlainText(s) => argument::Value::PlainText(s.to_string()),
// ObjInsArg::Arg(v) => argument::Value::Argument(ArgumentValue::new(v)),
// ObjInsArg::Reloc => argument::Value::Relocation(ArgumentRelocation {}),
// ObjInsArg::BranchDest(dest) => argument::Value::BranchDest(*dest),
// }),
// }
// }
// }
//
// impl ArgumentValue {
// pub fn new(value: &ObjInsArgValue) -> Self {
// Self {
// value: Some(match value {
// ObjInsArgValue::Signed(v) => argument_value::Value::Signed(*v),
// ObjInsArgValue::Unsigned(v) => argument_value::Value::Unsigned(*v),
// ObjInsArgValue::Opaque(v) => argument_value::Value::Opaque(v.to_string()),
// }),
// }
// }
// }
//
// impl Relocation {
// pub fn new(object: &obj::Object, reloc: &ObjReloc) -> Self {
// Self {
// r#type: match reloc.flags {
// object::RelocationFlags::Elf { r_type } => r_type,
// object::RelocationFlags::MachO { r_type, .. } => r_type as u32,
// object::RelocationFlags::Coff { typ } => typ as u32,
// object::RelocationFlags::Xcoff { r_rtype, .. } => r_rtype as u32,
// _ => unreachable!(),
// },
// type_name: object.arch.display_reloc(reloc.flags).into_owned(),
// target: Some(RelocationTarget {
// symbol: Some(Symbol::new(&reloc.target)),
// addend: reloc.addend,
// }),
// }
// }
// }
//
// impl InstructionDiff {
// pub fn new(object: &obj::Object, instruction_diff: &ObjInsDiff) -> Self {
// Self {
// instruction: instruction_diff.ins.as_ref().map(|ins| Instruction::new(object, ins)),
// diff_kind: DiffKind::from(instruction_diff.kind) as i32,
// branch_from: instruction_diff.branch_from.as_ref().map(InstructionBranchFrom::new),
// branch_to: instruction_diff.branch_to.as_ref().map(InstructionBranchTo::new),
// arg_diff: instruction_diff.arg_diff.iter().map(ArgumentDiff::new).collect(),
// }
// }
// }
//
// impl ArgumentDiff {
// pub fn new(value: &Option<ObjInsArgDiff>) -> Self {
// Self { diff_index: value.as_ref().map(|v| v.idx as u32) }
// }
// }
//
// impl From<ObjInsDiffKind> for DiffKind {
// fn from(value: ObjInsDiffKind) -> Self {
// match value {
// ObjInsDiffKind::None => DiffKind::DiffNone,
// ObjInsDiffKind::OpMismatch => DiffKind::DiffOpMismatch,
// ObjInsDiffKind::ArgMismatch => DiffKind::DiffArgMismatch,
// ObjInsDiffKind::Replace => DiffKind::DiffReplace,
// ObjInsDiffKind::Delete => DiffKind::DiffDelete,
// ObjInsDiffKind::Insert => DiffKind::DiffInsert,
// }
// }
// }
//
// impl From<ObjDataDiffKind> for DiffKind {
// fn from(value: ObjDataDiffKind) -> Self {
// match value {
// ObjDataDiffKind::None => DiffKind::DiffNone,
// ObjDataDiffKind::Replace => DiffKind::DiffReplace,
// ObjDataDiffKind::Delete => DiffKind::DiffDelete,
// ObjDataDiffKind::Insert => DiffKind::DiffInsert,
// }
// }
// }
//
// impl InstructionBranchFrom {
// pub fn new(value: &ObjInsBranchFrom) -> Self {
// Self {
// instruction_index: value.ins_idx.iter().map(|&x| x as u32).collect(),
// branch_index: value.branch_idx as u32,
// }
// }
// }
//
// impl InstructionBranchTo {
// pub fn new(value: &ObjInsBranchTo) -> Self {
// Self { instruction_index: value.ins_idx as u32, branch_index: value.branch_idx as u32 }
// }
// }
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+354 -77
View File
@@ -1,16 +1,28 @@
use alloc::string::{String, ToString};
use alloc::{
borrow::Cow,
collections::BTreeSet,
format,
string::{String, ToString},
vec::Vec,
};
use core::cmp::Ordering;
use anyhow::Result;
use itertools::Itertools;
use regex::Regex;
use crate::{
diff::{ObjInsArgDiff, ObjInsDiff},
obj::{ObjInsArg, ObjInsArgValue, ObjReloc, ObjSymbol},
diff::{DiffObjConfig, InstructionArgDiffIndex, InstructionDiffRow, ObjectDiff, SymbolDiff},
obj::{
InstructionArg, InstructionArgValue, Object, SectionFlag, SectionKind, Symbol, SymbolFlag,
SymbolKind,
},
};
#[derive(Debug, Copy, Clone)]
pub enum DiffText<'a> {
/// Basic text
Basic(&'a str),
/// Colored text
BasicColor(&'a str, usize),
/// Line number
Line(u32),
/// Instruction address
@@ -18,13 +30,13 @@ pub enum DiffText<'a> {
/// Instruction mnemonic
Opcode(&'a str, u16),
/// Instruction argument
Argument(&'a ObjInsArgValue, Option<&'a ObjInsArgDiff>),
Argument(&'a InstructionArgValue),
/// Branch destination
BranchDest(u64, Option<&'a ObjInsArgDiff>),
BranchDest(u64),
/// Symbol name
Symbol(&'a ObjSymbol, Option<&'a ObjInsArgDiff>),
Symbol(&'a Symbol),
/// Relocation addend
Addend(i64, Option<&'a ObjInsArgDiff>),
Addend(i64),
/// Number of spaces
Spacing(usize),
/// End of line
@@ -36,83 +48,113 @@ pub enum HighlightKind {
#[default]
None,
Opcode(u16),
Arg(ObjInsArgValue),
Argument(InstructionArgValue),
Symbol(String),
Address(u64),
}
pub fn display_diff<E>(
ins_diff: &ObjInsDiff,
base_addr: u64,
mut cb: impl FnMut(DiffText) -> Result<(), E>,
) -> Result<(), E> {
let Some(ins) = &ins_diff.ins else {
cb(DiffText::Eol)?;
return Ok(());
};
if let Some(line) = ins.line {
cb(DiffText::Line(line))?;
}
cb(DiffText::Address(ins.address - base_addr))?;
if let Some(branch) = &ins_diff.branch_from {
cb(DiffText::BasicColor(" ~> ", branch.branch_idx))?;
} else {
cb(DiffText::Spacing(4))?;
}
cb(DiffText::Opcode(&ins.mnemonic, ins.op))?;
let mut arg_diff_idx = 0; // non-PlainText index
for (i, arg) in ins.args.iter().enumerate() {
if i == 0 {
cb(DiffText::Spacing(1))?;
}
let diff = ins_diff.arg_diff.get(arg_diff_idx).and_then(|o| o.as_ref());
match arg {
ObjInsArg::PlainText(s) => {
cb(DiffText::Basic(s))?;
}
ObjInsArg::Arg(v) => {
cb(DiffText::Argument(v, diff))?;
arg_diff_idx += 1;
}
ObjInsArg::Reloc => {
display_reloc_name(ins.reloc.as_ref().unwrap(), &mut cb, diff)?;
arg_diff_idx += 1;
}
ObjInsArg::BranchDest(dest) => {
if let Some(dest) = dest.checked_sub(base_addr) {
cb(DiffText::BranchDest(dest, diff))?;
} else {
cb(DiffText::Basic("<unknown>"))?;
}
arg_diff_idx += 1;
}
}
}
if let Some(branch) = &ins_diff.branch_to {
cb(DiffText::BasicColor(" ~>", branch.branch_idx))?;
}
cb(DiffText::Eol)?;
Ok(())
pub enum InstructionPart {
Basic(&'static str),
Opcode(Cow<'static, str>, u16),
Arg(InstructionArg),
Separator,
}
fn display_reloc_name<E>(
reloc: &ObjReloc,
mut cb: impl FnMut(DiffText) -> Result<(), E>,
diff: Option<&ObjInsArgDiff>,
) -> Result<(), E> {
cb(DiffText::Symbol(&reloc.target, diff))?;
cb(DiffText::Addend(reloc.addend, diff))
pub fn display_row(
obj: &Object,
symbol_index: usize,
ins_row: &InstructionDiffRow,
diff_config: &DiffObjConfig,
mut cb: impl FnMut(DiffText, InstructionArgDiffIndex) -> Result<()>,
) -> Result<()> {
let Some(ins_ref) = ins_row.ins_ref else {
cb(DiffText::Eol, InstructionArgDiffIndex::NONE)?;
return Ok(());
};
let symbol = &obj.symbols[symbol_index];
let Some(section_index) = symbol.section else {
cb(DiffText::Eol, InstructionArgDiffIndex::NONE)?;
return Ok(());
};
let section = &obj.sections[section_index];
let Some(data) = section.data_range(ins_ref.address, ins_ref.size as usize) else {
cb(DiffText::Eol, InstructionArgDiffIndex::NONE)?;
return Ok(());
};
if let Some(line) = section.line_info.range(..=ins_ref.address).last().map(|(_, &b)| b) {
cb(DiffText::Line(line), InstructionArgDiffIndex::NONE)?;
}
cb(
DiffText::Address(ins_ref.address.saturating_sub(symbol.address)),
InstructionArgDiffIndex::NONE,
)?;
if let Some(branch) = &ins_row.branch_from {
cb(DiffText::Basic(" ~> "), InstructionArgDiffIndex::new(branch.branch_idx))?;
} else {
cb(DiffText::Spacing(4), InstructionArgDiffIndex::NONE)?;
}
let mut arg_idx = 0;
let relocation = section.relocation_at(ins_ref.address, obj);
obj.arch.display_instruction(
ins_ref,
data,
relocation,
symbol.address..symbol.address + symbol.size,
section_index,
diff_config,
&mut |part| match part {
InstructionPart::Basic(text) => {
cb(DiffText::Basic(text), InstructionArgDiffIndex::NONE)
}
InstructionPart::Opcode(mnemonic, opcode) => {
cb(DiffText::Opcode(mnemonic.as_ref(), opcode), InstructionArgDiffIndex::NONE)
}
InstructionPart::Arg(arg) => {
let diff_index = ins_row.arg_diff.get(arg_idx).copied().unwrap_or_default();
arg_idx += 1;
match arg {
InstructionArg::Value(ref value) => cb(DiffText::Argument(value), diff_index),
InstructionArg::Reloc => {
let resolved = relocation.unwrap();
cb(DiffText::Symbol(resolved.symbol), diff_index)?;
if resolved.relocation.addend != 0 {
cb(DiffText::Addend(resolved.relocation.addend), diff_index)?;
}
Ok(())
}
InstructionArg::BranchDest(dest) => {
if let Some(addr) = dest.checked_sub(symbol.address) {
cb(DiffText::BranchDest(addr), diff_index)
} else {
cb(
DiffText::Argument(&InstructionArgValue::Opaque(Cow::Borrowed(
"<invalid>",
))),
diff_index,
)
}
}
}
}
InstructionPart::Separator => {
cb(DiffText::Basic(diff_config.separator()), InstructionArgDiffIndex::NONE)
}
},
)?;
if let Some(branch) = &ins_row.branch_to {
cb(DiffText::Basic(" ~>"), InstructionArgDiffIndex::new(branch.branch_idx))?;
}
cb(DiffText::Eol, InstructionArgDiffIndex::NONE)?;
Ok(())
}
impl PartialEq<DiffText<'_>> for HighlightKind {
fn eq(&self, other: &DiffText) -> bool {
match (self, other) {
(HighlightKind::Opcode(a), DiffText::Opcode(_, b)) => a == b,
(HighlightKind::Arg(a), DiffText::Argument(b, _)) => a.loose_eq(b),
(HighlightKind::Symbol(a), DiffText::Symbol(b, _)) => a == &b.name,
(HighlightKind::Address(a), DiffText::Address(b) | DiffText::BranchDest(b, _)) => {
a == b
}
(HighlightKind::Argument(a), DiffText::Argument(b)) => a.loose_eq(b),
(HighlightKind::Symbol(a), DiffText::Symbol(b)) => a == &b.name,
(HighlightKind::Address(a), DiffText::Address(b) | DiffText::BranchDest(b)) => a == b,
_ => false,
}
}
@@ -126,10 +168,245 @@ impl From<DiffText<'_>> for HighlightKind {
fn from(value: DiffText<'_>) -> Self {
match value {
DiffText::Opcode(_, op) => HighlightKind::Opcode(op),
DiffText::Argument(arg, _) => HighlightKind::Arg(arg.clone()),
DiffText::Symbol(sym, _) => HighlightKind::Symbol(sym.name.to_string()),
DiffText::Address(addr) | DiffText::BranchDest(addr, _) => HighlightKind::Address(addr),
DiffText::Argument(arg) => HighlightKind::Argument(arg.clone()),
DiffText::Symbol(sym) => HighlightKind::Symbol(sym.name.to_string()),
DiffText::Address(addr) | DiffText::BranchDest(addr) => HighlightKind::Address(addr),
_ => HighlightKind::None,
}
}
}
pub enum ContextMenuItem {
Copy { value: String, label: Option<String> },
Navigate { label: String },
}
pub enum HoverItemColor {
Normal, // Gray
Emphasized, // White
Special, // Blue
}
pub struct HoverItem {
pub text: String,
pub color: HoverItemColor,
}
pub fn symbol_context(_obj: &Object, symbol: &Symbol) -> Vec<ContextMenuItem> {
let mut out = Vec::new();
if let Some(name) = &symbol.demangled_name {
out.push(ContextMenuItem::Copy { value: name.clone(), label: None });
}
out.push(ContextMenuItem::Copy { value: symbol.name.clone(), label: None });
if let Some(address) = symbol.virtual_address {
out.push(ContextMenuItem::Copy {
value: format!("{:#x}", address),
label: Some("virtual address".to_string()),
});
}
// if let Some(_extab) = obj.arch.ppc().and_then(|ppc| ppc.extab_for_symbol(symbol)) {
// out.push(ContextMenuItem::Navigate { label: "Decode exception table".to_string() });
// }
out
}
pub fn symbol_hover(_obj: &Object, symbol: &Symbol) -> Vec<HoverItem> {
let mut out = Vec::new();
out.push(HoverItem {
text: format!("Name: {}", symbol.name),
color: HoverItemColor::Emphasized,
});
out.push(HoverItem {
text: format!("Address: {:x}", symbol.address),
color: HoverItemColor::Emphasized,
});
if symbol.flags.contains(SymbolFlag::SizeInferred) {
out.push(HoverItem {
text: format!("Size: {:x} (inferred)", symbol.size),
color: HoverItemColor::Emphasized,
});
} else {
out.push(HoverItem {
text: format!("Size: {:x}", symbol.size),
color: HoverItemColor::Emphasized,
});
}
if let Some(address) = symbol.virtual_address {
out.push(HoverItem {
text: format!("Virtual address: {:#x}", address),
color: HoverItemColor::Special,
});
}
// if let Some(extab) = obj.arch.ppc().and_then(|ppc| ppc.extab_for_symbol(symbol)) {
// out.push(HoverItem {
// text: format!("extab symbol: {}", extab.etb_symbol.name),
// color: HoverItemColor::Special,
// });
// out.push(HoverItem {
// text: format!("extabindex symbol: {}", extab.eti_symbol.name),
// color: HoverItemColor::Special,
// });
// }
out
}
#[derive(Copy, Clone)]
pub enum SymbolFilter<'a> {
None,
Search(&'a Regex),
Mapping(usize, Option<&'a Regex>),
}
fn symbol_matches_filter(
symbol: &Symbol,
diff: &SymbolDiff,
filter: SymbolFilter<'_>,
show_hidden_symbols: bool,
) -> bool {
// Ignore absolute symbols
if symbol.section.is_none() && !symbol.flags.contains(SymbolFlag::Common) {
return false;
}
if !show_hidden_symbols && symbol.flags.contains(SymbolFlag::Hidden) {
return false;
}
match filter {
SymbolFilter::None => true,
SymbolFilter::Search(regex) => {
regex.is_match(&symbol.name)
|| symbol.demangled_name.as_deref().is_some_and(|s| regex.is_match(s))
}
SymbolFilter::Mapping(symbol_ref, regex) => {
diff.target_symbol == Some(symbol_ref)
&& regex.is_none_or(|r| {
r.is_match(&symbol.name)
|| symbol.demangled_name.as_deref().is_some_and(|s| r.is_match(s))
})
}
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct SectionDisplaySymbol {
pub symbol: usize,
pub is_mapping_symbol: bool,
}
#[derive(Debug, Clone)]
pub struct SectionDisplay {
pub id: String,
pub name: String,
pub size: u64,
pub match_percent: Option<f32>,
pub symbols: Vec<SectionDisplaySymbol>,
}
pub fn display_sections(
obj: &Object,
diff: &ObjectDiff,
filter: SymbolFilter<'_>,
show_hidden_symbols: bool,
show_mapped_symbols: bool,
reverse_fn_order: bool,
) -> Vec<SectionDisplay> {
let mut mapping = BTreeSet::new();
let is_mapping_symbol = if let SymbolFilter::Mapping(_, _) = filter {
for mapping_diff in &diff.mapping_symbols {
let symbol = &obj.symbols[mapping_diff.symbol_index];
if !symbol_matches_filter(
symbol,
&mapping_diff.symbol_diff,
filter,
show_hidden_symbols,
) {
continue;
}
if !show_mapped_symbols {
let symbol_diff = &diff.symbols[mapping_diff.symbol_index];
if symbol_diff.target_symbol.is_some() {
continue;
}
}
mapping.insert((symbol.section, mapping_diff.symbol_index));
}
true
} else {
for (symbol_idx, (symbol, symbol_diff)) in obj.symbols.iter().zip(&diff.symbols).enumerate()
{
if !symbol_matches_filter(symbol, symbol_diff, filter, show_hidden_symbols) {
continue;
}
mapping.insert((symbol.section, symbol_idx));
}
false
};
let num_sections = mapping.iter().map(|(section_idx, _)| *section_idx).dedup().count();
let mut sections = Vec::with_capacity(num_sections);
for (section_idx, group) in &mapping.iter().chunk_by(|(section_idx, _)| *section_idx) {
let mut symbols = group
.map(|&(_, symbol)| SectionDisplaySymbol { symbol, is_mapping_symbol })
.collect::<Vec<_>>();
if let Some(section_idx) = section_idx {
let section = &obj.sections[section_idx];
if section.kind == SectionKind::Unknown || section.flags.contains(SectionFlag::Hidden) {
// Skip unknown and hidden sections
continue;
}
let section_diff = &diff.sections[section_idx];
if section.kind == SectionKind::Code && reverse_fn_order {
symbols.sort_by(|a, b| {
let a_symbol = &obj.symbols[a.symbol];
let b_symbol = &obj.symbols[b.symbol];
symbol_sort_reverse(a_symbol, b_symbol)
});
} else {
symbols.sort_by(|a, b| {
let a_symbol = &obj.symbols[a.symbol];
let b_symbol = &obj.symbols[b.symbol];
symbol_sort(a_symbol, b_symbol)
});
}
sections.push(SectionDisplay {
id: section.id.clone(),
name: if section.flags.contains(SectionFlag::Combined) {
format!("{} [combined]", section.name)
} else {
section.name.clone()
},
size: section.size,
match_percent: section_diff.match_percent,
symbols,
});
} else {
// Don't sort, preserve order of absolute symbols
sections.push(SectionDisplay {
id: ".comm".to_string(),
name: ".comm".to_string(),
size: 0,
match_percent: None,
symbols,
});
}
}
sections.sort_by(|a, b| a.id.cmp(&b.id));
sections
}
fn section_symbol_sort(a: &Symbol, b: &Symbol) -> Ordering {
if a.kind == SymbolKind::Section {
if b.kind != SymbolKind::Section {
return Ordering::Less;
}
} else if b.kind == SymbolKind::Section {
return Ordering::Greater;
}
Ordering::Equal
}
fn symbol_sort(a: &Symbol, b: &Symbol) -> Ordering {
section_symbol_sort(a, b).then(a.address.cmp(&b.address)).then(a.size.cmp(&b.size))
}
fn symbol_sort_reverse(a: &Symbol, b: &Symbol) -> Ordering {
section_symbol_sort(a, b).then(b.address.cmp(&a.address)).then(b.size.cmp(&a.size))
}
File diff suppressed because it is too large Load Diff
+6 -6
View File
@@ -6,9 +6,9 @@ use typed_path::Utf8PlatformPathBuf;
use crate::{
build::{run_make, BuildConfig, BuildStatus},
diff::{diff_objs, DiffObjConfig, MappingConfig, ObjDiff},
diff::{diff_objs, DiffObjConfig, MappingConfig, ObjectDiff},
jobs::{start_job, update_status, Job, JobContext, JobResult, JobState},
obj::{read, ObjInfo},
obj::{read, Object},
};
pub struct ObjDiffConfig {
@@ -24,8 +24,8 @@ pub struct ObjDiffConfig {
pub struct ObjDiffResult {
pub first_status: BuildStatus,
pub second_status: BuildStatus,
pub first_obj: Option<(ObjInfo, ObjDiff)>,
pub second_obj: Option<(ObjInfo, ObjDiff)>,
pub first_obj: Option<(Object, ObjectDiff)>,
pub second_obj: Option<(Object, ObjectDiff)>,
pub time: OffsetDateTime,
}
@@ -170,11 +170,11 @@ fn run_build(
update_status(context, "Performing diff".to_string(), step_idx, total, &cancel)?;
step_idx += 1;
let result = diff_objs(
&config.diff_obj_config,
&config.mapping_config,
first_obj.as_ref(),
second_obj.as_ref(),
None,
&config.diff_obj_config,
&config.mapping_config,
)?;
update_status(context, "Complete".to_string(), step_idx, total, &cancel)?;
+1 -2
View File
@@ -1,3 +1,4 @@
#![allow(clippy::too_many_arguments)]
#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
@@ -17,5 +18,3 @@ pub mod jobs;
pub mod obj;
#[cfg(feature = "any-arch")]
pub mod util;
#[cfg(feature = "wasm")]
pub mod wasm;
+168 -109
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
@@ -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,
},
],
)
+5 -7
View File
@@ -1,7 +1,7 @@
use alloc::format;
use core::fmt;
use anyhow::Result;
use anyhow::{ensure, Result};
use num_traits::PrimInt;
use object::{Endian, Object};
@@ -27,17 +27,15 @@ impl<N: PrimInt> fmt::UpperHex for ReallySigned<N> {
}
pub fn read_u32(obj_file: &object::File, reader: &mut &[u8]) -> Result<u32> {
if reader.len() < 4 {
return Err(anyhow::anyhow!("Not enough bytes to read u32"));
}
ensure!(reader.len() >= 4, "Not enough bytes to read u32");
let value = u32::from_ne_bytes(reader[..4].try_into()?);
*reader = &reader[4..];
Ok(obj_file.endianness().read_u32(value))
}
pub fn read_u16(obj_file: &object::File, reader: &mut &[u8]) -> Result<u16> {
if reader.len() < 2 {
return Err(anyhow::anyhow!("Not enough bytes to read u16"));
}
ensure!(reader.len() >= 2, "Not enough bytes to read u16");
let value = u16::from_ne_bytes(reader[..2].try_into()?);
*reader = &reader[2..];
Ok(obj_file.endianness().read_u16(value))
}
-99
View File
@@ -1,99 +0,0 @@
use alloc::{
format,
str::FromStr,
string::{String, ToString},
vec::Vec,
};
use core::cell::RefCell;
use prost::Message;
use crate::{bindings::diff::DiffResult, diff, obj};
wit_bindgen::generate!({
world: "api",
});
use exports::objdiff::core::diff::{
DiffConfigBorrow, Guest as GuestTypes, GuestDiffConfig, GuestObject, Object, ObjectBorrow,
};
struct Component;
impl Guest for Component {
fn init() -> Result<(), String> {
// console_error_panic_hook::set_once();
// #[cfg(debug_assertions)]
// console_log::init_with_level(log::Level::Debug).map_err(|e| e.to_string())?;
// #[cfg(not(debug_assertions))]
// console_log::init_with_level(log::Level::Info).map_err(|e| e.to_string())?;
Ok(())
}
fn version() -> String { env!("CARGO_PKG_VERSION").to_string() }
}
#[repr(transparent)]
struct ResourceDiffConfig(RefCell<diff::DiffObjConfig>);
impl GuestTypes for Component {
type DiffConfig = ResourceDiffConfig;
type Object = obj::ObjInfo;
fn run_diff(
left: Option<ObjectBorrow>,
right: Option<ObjectBorrow>,
diff_config: DiffConfigBorrow,
) -> Result<Vec<u8>, String> {
let diff_config = diff_config.get::<ResourceDiffConfig>().0.borrow();
let result = run_diff_internal(
left.as_ref().map(|o| o.get()),
right.as_ref().map(|o| o.get()),
&diff_config,
&diff::MappingConfig::default(),
)
.map_err(|e| e.to_string())?;
Ok(result.encode_to_vec())
}
}
impl GuestDiffConfig for ResourceDiffConfig {
fn new() -> Self { Self(RefCell::new(diff::DiffObjConfig::default())) }
fn set_property(&self, key: String, value: String) -> Result<(), String> {
let id = diff::ConfigPropertyId::from_str(&key)
.map_err(|_| format!("Invalid property key {:?}", key))?;
self.0
.borrow_mut()
.set_property_value_str(id, &value)
.map_err(|_| format!("Invalid property value {:?}", value))
}
fn get_property(&self, key: String) -> Result<String, String> {
let id = diff::ConfigPropertyId::from_str(&key)
.map_err(|_| format!("Invalid property key {:?}", key))?;
Ok(self.0.borrow().get_property_value(id).to_string())
}
}
impl GuestObject for obj::ObjInfo {
fn parse(data: Vec<u8>, diff_config: DiffConfigBorrow) -> Result<Object, String> {
let diff_config = diff_config.get::<ResourceDiffConfig>().0.borrow();
obj::read::parse(&data, &diff_config).map(|o| Object::new(o)).map_err(|e| e.to_string())
}
}
fn run_diff_internal(
left: Option<&obj::ObjInfo>,
right: Option<&obj::ObjInfo>,
diff_config: &diff::DiffObjConfig,
mapping_config: &diff::MappingConfig,
) -> anyhow::Result<DiffResult> {
log::debug!("Running diff with config: {:?}", diff_config);
let result = diff::diff_objs(diff_config, mapping_config, left, right, None)?;
let left = left.and_then(|o| result.left.as_ref().map(|d| (o, d)));
let right = right.and_then(|o| result.right.as_ref().map(|d| (o, d)));
Ok(DiffResult::new(left, right))
}
export!(Component);
-64
View File
@@ -1,64 +0,0 @@
//! This module contains a canonical definition of the `cabi_realloc` function
//! for the component model.
//!
//! The component model's canonical ABI for representing datatypes in memory
//! makes use of this function when transferring lists and strings, for example.
//! This function behaves like C's `realloc` but also takes alignment into
//! account.
//!
//! Components are notably not required to export this function, but nearly
//! all components end up doing so currently. This definition in the standard
//! library removes the need for all compilations to define this themselves.
//!
//! More information about the canonical ABI can be found at
//! <https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md>
//!
//! Note that the name of this function is not standardized in the canonical ABI
//! at this time. Instead it's a convention of the "componentization process"
//! where a core wasm module is converted to a component to use this name.
//! Additionally this is not the only possible definition of this function, so
//! this is defined as a "weak" symbol. This means that other definitions are
//! allowed to overwrite it if they are present in a compilation.
use alloc::{alloc, Layout};
use core::ptr;
#[used]
static FORCE_CODEGEN_OF_CABI_REALLOC: unsafe extern "C" fn(
*mut u8,
usize,
usize,
usize,
) -> *mut u8 = cabi_realloc;
#[no_mangle]
pub unsafe extern "C" fn cabi_realloc(
old_ptr: *mut u8,
old_len: usize,
align: usize,
new_len: usize,
) -> *mut u8 {
let layout;
let ptr = if old_len == 0 {
if new_len == 0 {
return ptr::without_provenance_mut(align);
}
layout = Layout::from_size_align_unchecked(new_len, align);
alloc::alloc(layout)
} else {
debug_assert_ne!(new_len, 0, "non-zero old_len requires non-zero new_len!");
layout = Layout::from_size_align_unchecked(old_len, align);
alloc::realloc(old_ptr, layout, new_len)
};
if ptr.is_null() {
// Print a nice message in debug mode, but in release mode don't
// pull in so many dependencies related to printing so just emit an
// `unreachable` instruction.
if cfg!(debug_assertions) {
alloc::handle_alloc_error(layout);
} else {
core::unreachable!("allocation failed")
}
}
ptr
}
-18
View File
@@ -1,18 +0,0 @@
mod api;
#[cfg(not(feature = "std"))]
mod cabi_realloc;
#[cfg(not(feature = "std"))]
static mut ARENA: [u8; 10000] = [0; 10000];
#[cfg(not(feature = "std"))]
#[global_allocator]
static ALLOCATOR: talc::Talck<spin::Mutex<()>, talc::ClaimOnOom> = talc::Talc::new(unsafe {
talc::ClaimOnOom::new(talc::Span::from_array(core::ptr::addr_of!(ARENA) as *mut [u8; 10000]))
})
.lock();
#[cfg(not(feature = "std"))]
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! { loop {} }