2023-01-20 21:03:56 -08:00
|
|
|
use std::{collections::BTreeMap, fs, io::Cursor, path::Path};
|
2022-09-08 14:19:20 -07:00
|
|
|
|
2023-01-21 09:41:41 -08:00
|
|
|
use anyhow::{anyhow, bail, Context, Result};
|
2023-01-20 21:03:56 -08:00
|
|
|
use byteorder::{BigEndian, ReadBytesExt};
|
2022-09-08 14:19:20 -07:00
|
|
|
use cwdemangle::demangle;
|
|
|
|
use flagset::Flags;
|
|
|
|
use object::{
|
2023-01-21 09:41:41 -08:00
|
|
|
elf, Architecture, File, Object, ObjectSection, ObjectSymbol, RelocationKind, RelocationTarget,
|
2022-12-10 07:34:03 -08:00
|
|
|
SectionIndex, SectionKind, Symbol, SymbolKind, SymbolSection,
|
2022-09-08 14:19:20 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
use crate::obj::{
|
2022-09-11 10:52:55 -07:00
|
|
|
ObjArchitecture, ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol,
|
|
|
|
ObjSymbolFlagSet, ObjSymbolFlags,
|
2022-09-08 14:19:20 -07:00
|
|
|
};
|
|
|
|
|
2023-01-20 22:27:37 -08:00
|
|
|
fn to_obj_section_kind(kind: SectionKind) -> Option<ObjSectionKind> {
|
2022-09-08 14:19:20 -07:00
|
|
|
match kind {
|
2023-01-20 22:27:37 -08:00
|
|
|
SectionKind::Text => Some(ObjSectionKind::Code),
|
|
|
|
SectionKind::Data | SectionKind::ReadOnlyData => Some(ObjSectionKind::Data),
|
|
|
|
SectionKind::UninitializedData => Some(ObjSectionKind::Bss),
|
|
|
|
_ => None,
|
2022-09-08 14:19:20 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-20 15:19:44 -07:00
|
|
|
fn to_obj_symbol(obj_file: &File<'_>, symbol: &Symbol<'_, '_>, addend: i64) -> Result<ObjSymbol> {
|
2022-09-08 14:19:20 -07:00
|
|
|
let mut name = symbol.name().context("Failed to process symbol name")?;
|
|
|
|
if name.is_empty() {
|
2022-12-06 15:09:19 -08:00
|
|
|
println!("Found empty sym: {symbol:?}");
|
2022-09-08 14:19:20 -07:00
|
|
|
name = "?";
|
|
|
|
}
|
|
|
|
let mut flags = ObjSymbolFlagSet(ObjSymbolFlags::none());
|
|
|
|
if symbol.is_global() {
|
|
|
|
flags = ObjSymbolFlagSet(flags.0 | ObjSymbolFlags::Global);
|
|
|
|
}
|
|
|
|
if symbol.is_local() {
|
|
|
|
flags = ObjSymbolFlagSet(flags.0 | ObjSymbolFlags::Local);
|
|
|
|
}
|
|
|
|
if symbol.is_common() {
|
|
|
|
flags = ObjSymbolFlagSet(flags.0 | ObjSymbolFlags::Common);
|
|
|
|
}
|
|
|
|
if symbol.is_weak() {
|
|
|
|
flags = ObjSymbolFlagSet(flags.0 | ObjSymbolFlags::Weak);
|
|
|
|
}
|
2022-09-11 10:52:55 -07:00
|
|
|
let section_address = if let Some(section) =
|
|
|
|
symbol.section_index().and_then(|idx| obj_file.section_by_index(idx).ok())
|
|
|
|
{
|
|
|
|
symbol.address() - section.address()
|
|
|
|
} else {
|
|
|
|
symbol.address()
|
|
|
|
};
|
2022-09-08 14:19:20 -07:00
|
|
|
Ok(ObjSymbol {
|
|
|
|
name: name.to_string(),
|
2022-11-05 10:16:44 -07:00
|
|
|
demangled_name: demangle(name, &Default::default()),
|
2022-09-08 14:19:20 -07:00
|
|
|
address: symbol.address(),
|
2022-09-11 10:52:55 -07:00
|
|
|
section_address,
|
2022-09-08 14:19:20 -07:00
|
|
|
size: symbol.size(),
|
|
|
|
size_known: symbol.size() != 0,
|
|
|
|
flags,
|
2022-09-20 15:19:44 -07:00
|
|
|
addend,
|
2022-09-08 14:19:20 -07:00
|
|
|
diff_symbol: None,
|
|
|
|
instructions: vec![],
|
2022-12-06 14:53:32 -08:00
|
|
|
match_percent: None,
|
2022-09-08 14:19:20 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-09-11 10:52:55 -07:00
|
|
|
fn filter_sections(obj_file: &File<'_>) -> Result<Vec<ObjSection>> {
|
2022-09-08 14:19:20 -07:00
|
|
|
let mut result = Vec::<ObjSection>::new();
|
|
|
|
for section in obj_file.sections() {
|
|
|
|
if section.size() == 0 {
|
|
|
|
continue;
|
|
|
|
}
|
2023-01-20 22:27:37 -08:00
|
|
|
let Some(kind) = to_obj_section_kind(section.kind()) else {
|
2022-09-08 14:19:20 -07:00
|
|
|
continue;
|
2023-01-20 22:27:37 -08:00
|
|
|
};
|
2022-09-08 14:19:20 -07:00
|
|
|
let name = section.name().context("Failed to process section name")?;
|
2022-12-06 14:53:32 -08:00
|
|
|
let data = section.uncompressed_data().context("Failed to read section data")?;
|
2022-09-08 14:19:20 -07:00
|
|
|
result.push(ObjSection {
|
|
|
|
name: name.to_string(),
|
2023-01-20 22:27:37 -08:00
|
|
|
kind,
|
2022-09-08 14:19:20 -07:00
|
|
|
address: section.address(),
|
|
|
|
size: section.size(),
|
|
|
|
data: data.to_vec(),
|
|
|
|
index: section.index().0,
|
|
|
|
symbols: Vec::new(),
|
|
|
|
relocations: Vec::new(),
|
2022-11-05 21:49:46 -07:00
|
|
|
data_diff: vec![],
|
|
|
|
match_percent: 0.0,
|
2022-09-08 14:19:20 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
result.sort_by(|a, b| a.name.cmp(&b.name));
|
|
|
|
Ok(result)
|
|
|
|
}
|
|
|
|
|
2022-09-11 10:52:55 -07:00
|
|
|
fn symbols_by_section(obj_file: &File<'_>, section: &ObjSection) -> Result<Vec<ObjSymbol>> {
|
2022-09-08 14:19:20 -07:00
|
|
|
let mut result = Vec::<ObjSymbol>::new();
|
|
|
|
for symbol in obj_file.symbols() {
|
|
|
|
if symbol.kind() == SymbolKind::Section {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if let Some(index) = symbol.section().index() {
|
|
|
|
if index.0 == section.index {
|
|
|
|
if symbol.is_local() && section.kind == ObjSectionKind::Code {
|
|
|
|
// TODO strip local syms in diff?
|
|
|
|
let name = symbol.name().context("Failed to process symbol name")?;
|
2022-09-11 10:52:55 -07:00
|
|
|
if symbol.size() == 0 || name.starts_with("lbl_") {
|
2022-09-08 14:19:20 -07:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2022-09-20 15:19:44 -07:00
|
|
|
result.push(to_obj_symbol(obj_file, &symbol, 0)?);
|
2022-09-08 14:19:20 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
result.sort_by_key(|v| v.address);
|
|
|
|
let mut iter = result.iter_mut().peekable();
|
|
|
|
while let Some(symbol) = iter.next() {
|
|
|
|
if symbol.size == 0 {
|
|
|
|
if let Some(next_symbol) = iter.peek() {
|
|
|
|
symbol.size = next_symbol.address - symbol.address;
|
|
|
|
} else {
|
|
|
|
symbol.size = (section.address + section.size) - symbol.address;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(result)
|
|
|
|
}
|
|
|
|
|
2022-09-11 10:52:55 -07:00
|
|
|
fn common_symbols(obj_file: &File<'_>) -> Result<Vec<ObjSymbol>> {
|
2023-01-25 21:19:20 -08:00
|
|
|
obj_file.symbols()
|
|
|
|
.filter(Symbol::is_common)
|
|
|
|
.map(|symbol| to_obj_symbol(obj_file, &symbol, 0))
|
|
|
|
.collect::<Result<Vec<ObjSymbol>>>()
|
2022-09-08 14:19:20 -07:00
|
|
|
}
|
|
|
|
|
2022-09-11 10:52:55 -07:00
|
|
|
fn find_section_symbol(
|
|
|
|
obj_file: &File<'_>,
|
|
|
|
target: &Symbol<'_, '_>,
|
2022-09-08 14:19:20 -07:00
|
|
|
address: u64,
|
|
|
|
) -> Result<ObjSymbol> {
|
|
|
|
let section_index =
|
|
|
|
target.section_index().ok_or_else(|| anyhow::Error::msg("Unknown section index"))?;
|
2022-09-11 10:52:55 -07:00
|
|
|
let section = obj_file.section_by_index(section_index)?;
|
|
|
|
let mut closest_symbol: Option<Symbol<'_, '_>> = None;
|
2022-09-08 14:19:20 -07:00
|
|
|
for symbol in obj_file.symbols() {
|
|
|
|
if !matches!(symbol.section_index(), Some(idx) if idx == section_index) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if symbol.kind() == SymbolKind::Section || symbol.address() != address {
|
2022-09-11 10:52:55 -07:00
|
|
|
if symbol.address() < address
|
|
|
|
&& symbol.size() != 0
|
|
|
|
&& (closest_symbol.is_none()
|
|
|
|
|| matches!(&closest_symbol, Some(s) if s.address() <= symbol.address()))
|
|
|
|
{
|
|
|
|
closest_symbol = Some(symbol);
|
|
|
|
}
|
2022-09-08 14:19:20 -07:00
|
|
|
continue;
|
|
|
|
}
|
2022-09-20 15:19:44 -07:00
|
|
|
return to_obj_symbol(obj_file, &symbol, 0);
|
2022-09-08 14:19:20 -07:00
|
|
|
}
|
2022-09-11 10:52:55 -07:00
|
|
|
let (name, offset) = closest_symbol
|
|
|
|
.and_then(|s| s.name().map(|n| (n, s.address())).ok())
|
|
|
|
.or_else(|| section.name().map(|n| (n, section.address())).ok())
|
|
|
|
.unwrap_or(("<unknown>", 0));
|
|
|
|
let offset_addr = address - offset;
|
|
|
|
Ok(ObjSymbol {
|
2022-09-20 15:19:44 -07:00
|
|
|
name: name.to_string(),
|
2022-09-11 10:52:55 -07:00
|
|
|
demangled_name: None,
|
2022-10-09 20:51:09 -07:00
|
|
|
address: offset,
|
2022-09-11 10:52:55 -07:00
|
|
|
section_address: address - section.address(),
|
|
|
|
size: 0,
|
|
|
|
size_known: false,
|
|
|
|
flags: Default::default(),
|
2022-09-20 15:19:44 -07:00
|
|
|
addend: offset_addr as i64,
|
2022-09-11 10:52:55 -07:00
|
|
|
diff_symbol: None,
|
|
|
|
instructions: vec![],
|
2022-12-06 14:53:32 -08:00
|
|
|
match_percent: None,
|
2022-09-11 10:52:55 -07:00
|
|
|
})
|
2022-09-08 14:19:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn relocations_by_section(
|
2022-09-11 10:52:55 -07:00
|
|
|
arch: ObjArchitecture,
|
|
|
|
obj_file: &File<'_>,
|
|
|
|
section: &mut ObjSection,
|
2022-09-08 14:19:20 -07:00
|
|
|
) -> Result<Vec<ObjReloc>> {
|
2022-12-10 07:34:03 -08:00
|
|
|
let obj_section = obj_file.section_by_index(SectionIndex(section.index))?;
|
2022-09-08 14:19:20 -07:00
|
|
|
let mut relocations = Vec::<ObjReloc>::new();
|
|
|
|
for (address, reloc) in obj_section.relocations() {
|
|
|
|
let symbol = match reloc.target() {
|
|
|
|
RelocationTarget::Symbol(idx) => obj_file
|
|
|
|
.symbol_by_index(idx)
|
|
|
|
.context("Failed to locate relocation target symbol")?,
|
|
|
|
_ => {
|
|
|
|
return Err(anyhow::Error::msg(format!(
|
|
|
|
"Unhandled relocation target: {:?}",
|
|
|
|
reloc.target()
|
|
|
|
)));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let kind = match reloc.kind() {
|
|
|
|
RelocationKind::Absolute => ObjRelocKind::Absolute,
|
2022-09-11 10:52:55 -07:00
|
|
|
RelocationKind::Elf(kind) => match arch {
|
|
|
|
ObjArchitecture::PowerPc => match kind {
|
2023-01-21 09:41:41 -08:00
|
|
|
elf::R_PPC_ADDR16_LO => ObjRelocKind::PpcAddr16Lo,
|
|
|
|
elf::R_PPC_ADDR16_HI => ObjRelocKind::PpcAddr16Hi,
|
|
|
|
elf::R_PPC_ADDR16_HA => ObjRelocKind::PpcAddr16Ha,
|
|
|
|
elf::R_PPC_REL24 => ObjRelocKind::PpcRel24,
|
|
|
|
elf::R_PPC_REL14 => ObjRelocKind::PpcRel14,
|
|
|
|
elf::R_PPC_EMB_SDA21 => ObjRelocKind::PpcEmbSda21,
|
2022-09-11 10:52:55 -07:00
|
|
|
_ => {
|
|
|
|
return Err(anyhow::Error::msg(format!(
|
2022-12-06 15:09:19 -08:00
|
|
|
"Unhandled PPC relocation type: {kind}"
|
2022-09-11 10:52:55 -07:00
|
|
|
)))
|
|
|
|
}
|
|
|
|
},
|
|
|
|
ObjArchitecture::Mips => match kind {
|
2023-01-21 09:41:41 -08:00
|
|
|
elf::R_MIPS_26 => ObjRelocKind::Mips26,
|
|
|
|
elf::R_MIPS_HI16 => ObjRelocKind::MipsHi16,
|
|
|
|
elf::R_MIPS_LO16 => ObjRelocKind::MipsLo16,
|
|
|
|
elf::R_MIPS_GOT16 => ObjRelocKind::MipsGot16,
|
|
|
|
elf::R_MIPS_CALL16 => ObjRelocKind::MipsCall16,
|
|
|
|
elf::R_MIPS_GPREL16 => ObjRelocKind::MipsGpRel16,
|
|
|
|
elf::R_MIPS_GPREL32 => ObjRelocKind::MipsGpRel32,
|
|
|
|
_ => bail!("Unhandled MIPS relocation type: {kind}"),
|
2022-09-11 10:52:55 -07:00
|
|
|
},
|
2022-09-08 14:19:20 -07:00
|
|
|
},
|
|
|
|
_ => {
|
|
|
|
return Err(anyhow::Error::msg(format!(
|
|
|
|
"Unhandled relocation type: {:?}",
|
|
|
|
reloc.kind()
|
|
|
|
)))
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let target_section = match symbol.section() {
|
|
|
|
SymbolSection::Common => Some(".comm".to_string()),
|
2022-09-11 10:52:55 -07:00
|
|
|
SymbolSection::Section(idx) => {
|
|
|
|
obj_file.section_by_index(idx).and_then(|s| s.name().map(|s| s.to_string())).ok()
|
|
|
|
}
|
2022-09-08 14:19:20 -07:00
|
|
|
_ => None,
|
|
|
|
};
|
2023-01-21 09:41:41 -08:00
|
|
|
let addend = if reloc.has_implicit_addend() {
|
|
|
|
let addend = u32::from_be_bytes(
|
|
|
|
section.data[address as usize..address as usize + 4].try_into()?,
|
|
|
|
);
|
|
|
|
match kind {
|
|
|
|
ObjRelocKind::Absolute => addend as i64,
|
2023-01-21 09:56:29 -08:00
|
|
|
ObjRelocKind::MipsHi16 => ((addend & 0x0000FFFF) << 16) as i32 as i64,
|
2023-01-21 09:41:41 -08:00
|
|
|
ObjRelocKind::MipsLo16
|
|
|
|
| ObjRelocKind::MipsGot16
|
|
|
|
| ObjRelocKind::MipsCall16
|
|
|
|
| ObjRelocKind::MipsGpRel16 => (addend & 0x0000FFFF) as i16 as i64,
|
|
|
|
ObjRelocKind::MipsGpRel32 => addend as i32 as i64,
|
|
|
|
ObjRelocKind::Mips26 => ((addend & 0x03FFFFFF) << 2) as i64,
|
|
|
|
_ => bail!("Unsupported implicit relocation {kind:?}"),
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
reloc.addend()
|
|
|
|
};
|
|
|
|
// println!("Reloc: {reloc:?}, symbol: {symbol:?}, addend: {addend:#X}");
|
2022-09-08 14:19:20 -07:00
|
|
|
let target = match symbol.kind() {
|
2023-01-20 22:11:40 -08:00
|
|
|
SymbolKind::Text | SymbolKind::Data | SymbolKind::Label | SymbolKind::Unknown => {
|
2023-01-21 09:41:41 -08:00
|
|
|
to_obj_symbol(obj_file, &symbol, addend)
|
2022-09-11 10:52:55 -07:00
|
|
|
}
|
2022-09-08 14:19:20 -07:00
|
|
|
SymbolKind::Section => {
|
2023-01-21 09:41:41 -08:00
|
|
|
if addend < 0 {
|
|
|
|
return Err(anyhow::Error::msg(format!("Negative addend in reloc: {addend}")));
|
|
|
|
}
|
2022-09-11 10:52:55 -07:00
|
|
|
find_section_symbol(obj_file, &symbol, addend as u64)
|
2022-09-08 14:19:20 -07:00
|
|
|
}
|
2023-01-21 09:41:41 -08:00
|
|
|
kind => Err(anyhow!("Unhandled relocation symbol type {kind:?}")),
|
2022-09-08 14:19:20 -07:00
|
|
|
}?;
|
|
|
|
relocations.push(ObjReloc { kind, address, target, target_section });
|
|
|
|
}
|
|
|
|
Ok(relocations)
|
|
|
|
}
|
|
|
|
|
2023-01-20 21:03:56 -08:00
|
|
|
fn line_info(obj_file: &File<'_>) -> Result<Option<BTreeMap<u32, u32>>> {
|
|
|
|
if let Some(section) = obj_file.section_by_name(".line") {
|
|
|
|
if section.size() == 0 {
|
|
|
|
return Ok(None);
|
|
|
|
}
|
|
|
|
let data = section.uncompressed_data()?;
|
|
|
|
let mut reader = Cursor::new(data.as_ref());
|
|
|
|
|
|
|
|
let mut map = BTreeMap::new();
|
|
|
|
let size = reader.read_u32::<BigEndian>()?;
|
|
|
|
let base_address = reader.read_u32::<BigEndian>()?;
|
|
|
|
while reader.position() < size as u64 {
|
|
|
|
let line_number = reader.read_u32::<BigEndian>()?;
|
|
|
|
let statement_pos = reader.read_u16::<BigEndian>()?;
|
|
|
|
if statement_pos != 0xFFFF {
|
|
|
|
log::warn!("Unhandled statement pos {}", statement_pos);
|
|
|
|
}
|
|
|
|
let address_delta = reader.read_u32::<BigEndian>()?;
|
|
|
|
map.insert(base_address + address_delta, line_number);
|
|
|
|
}
|
2023-01-20 22:11:40 -08:00
|
|
|
println!("Line info: {map:#X?}");
|
2023-01-20 21:03:56 -08:00
|
|
|
return Ok(Some(map));
|
|
|
|
}
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
|
2022-09-08 14:19:20 -07:00
|
|
|
pub fn read(obj_path: &Path) -> Result<ObjInfo> {
|
2022-12-06 14:53:32 -08:00
|
|
|
let data = {
|
|
|
|
let file = fs::File::open(obj_path)?;
|
|
|
|
unsafe { memmap2::Mmap::map(&file) }?
|
|
|
|
};
|
|
|
|
let obj_file = File::parse(&*data)?;
|
2022-09-11 10:52:55 -07:00
|
|
|
let architecture = match obj_file.architecture() {
|
|
|
|
Architecture::PowerPc => ObjArchitecture::PowerPc,
|
|
|
|
Architecture::Mips => ObjArchitecture::Mips,
|
|
|
|
_ => {
|
|
|
|
return Err(anyhow::Error::msg(format!(
|
|
|
|
"Unsupported architecture: {:?}",
|
|
|
|
obj_file.architecture()
|
|
|
|
)))
|
|
|
|
}
|
|
|
|
};
|
2022-09-08 14:19:20 -07:00
|
|
|
let mut result = ObjInfo {
|
2022-09-11 10:52:55 -07:00
|
|
|
architecture,
|
2022-09-08 14:19:20 -07:00
|
|
|
path: obj_path.to_owned(),
|
|
|
|
sections: filter_sections(&obj_file)?,
|
|
|
|
common: common_symbols(&obj_file)?,
|
2023-01-20 21:03:56 -08:00
|
|
|
line_info: line_info(&obj_file)?,
|
2022-09-08 14:19:20 -07:00
|
|
|
};
|
|
|
|
for section in &mut result.sections {
|
|
|
|
section.symbols = symbols_by_section(&obj_file, section)?;
|
2022-09-11 10:52:55 -07:00
|
|
|
section.relocations = relocations_by_section(architecture, &obj_file, section)?;
|
2022-09-08 14:19:20 -07:00
|
|
|
}
|
|
|
|
Ok(result)
|
|
|
|
}
|