Write .splitmeta section in split objects

This enables showing the original address of
symbols in objdiff, as well as `elf disasm`
on split objects retaining the original
addresses.
This commit is contained in:
2024-02-28 22:27:38 -07:00
parent a2cae4f82a
commit 4f8a9e6fab
14 changed files with 365 additions and 84 deletions

View File

@@ -836,7 +836,8 @@ fn split_write_obj(
}
debug!("Splitting {} objects", module.obj.link_order.len());
let split_objs = split_obj(&module.obj)?;
let module_name = module.config.name().to_string();
let split_objs = split_obj(&module.obj, Some(module_name.as_str()))?;
debug!("Writing object files");
DirBuilder::new()
@@ -855,7 +856,7 @@ fn split_write_obj(
module.obj.symbols.by_name("_prolog")?.map(|(_, s)| s.name.clone())
};
let mut out_config = OutputModule {
name: module.config.name().to_string(),
name: module_name,
module_id,
ldscript: out_dir.join("ldscript.lcf"),
units: Vec::with_capacity(split_objs.len()),

View File

@@ -8,6 +8,7 @@ use std::{
use anyhow::{anyhow, bail, ensure, Context, Result};
use argp::FromArgs;
use objdiff_core::obj::split_meta::{SplitMeta, SPLITMETA_SECTION};
use object::{
elf,
write::{Mangling, SectionId, SymbolId},
@@ -148,7 +149,7 @@ fn disasm(args: DisasmArgs) -> Result<()> {
match obj.kind {
ObjKind::Executable => {
log::info!("Splitting {} objects", obj.link_order.len());
let split_objs = split_obj(&obj)?;
let split_objs = split_obj(&obj, None)?;
let asm_dir = args.out.join("asm");
let include_dir = args.out.join("include");
@@ -183,7 +184,7 @@ fn split(args: SplitArgs) -> Result<()> {
let mut file_map = HashMap::<String, Vec<u8>>::new();
let split_objs = split_obj(&obj)?;
let split_objs = split_obj(&obj, None)?;
for (unit, split_obj) in obj.link_order.iter().zip(&split_objs) {
let out_obj = write_elf(split_obj, false)?;
match file_map.entry(unit.name.clone()) {
@@ -596,5 +597,27 @@ fn info(args: InfoArgs) -> Result<()> {
}
}
if let Some(split_meta_section) = in_file.section_by_name(SPLITMETA_SECTION) {
let data = split_meta_section.uncompressed_data()?;
if !data.is_empty() {
let meta =
SplitMeta::from_reader(&mut data.as_ref(), in_file.endianness(), in_file.is_64())
.context("While reading .splitmeta section")?;
println!("\nSplit metadata (.splitmeta):");
if let Some(generator) = &meta.generator {
println!("\tGenerator: {}", generator);
}
if let Some(virtual_addresses) = &meta.virtual_addresses {
println!("\tVirtual addresses:");
println!("\t{: >10} | {: <10}", "Addr", "Symbol");
for (symbol, addr) in in_file.symbols().zip(virtual_addresses) {
if symbol.is_definition() {
println!("\t{: >10} | {: <10}", format!("{:#X}", addr), symbol.name()?);
}
}
}
}
}
Ok(())
}

View File

@@ -485,7 +485,7 @@ fn merge(args: MergeArgs) -> Result<()> {
align: mod_section.align,
elf_index: mod_section.elf_index,
relocations: Default::default(),
original_address: mod_section.original_address,
virtual_address: mod_section.virtual_address,
file_offset: mod_section.file_offset,
section_known: mod_section.section_known,
splits: mod_section.splits.clone(),

View File

@@ -100,7 +100,7 @@ enum SubCommand {
// Duplicated from supports-color so we can check early.
fn env_no_color() -> bool {
match env::var("NO_COLOR").as_deref() {
Ok("0") | Err(_) => false,
Ok("") | Ok("0") | Err(_) => false,
Ok(_) => true,
}
}

View File

@@ -10,6 +10,7 @@ use std::{
};
use anyhow::{anyhow, bail, ensure, Result};
use objdiff_core::obj::split_meta::SplitMeta;
pub use relocations::{ObjReloc, ObjRelocKind, ObjRelocations};
pub use sections::{ObjSection, ObjSectionKind, ObjSections};
pub use splits::{ObjSplit, ObjSplits};
@@ -55,6 +56,7 @@ pub struct ObjInfo {
pub sections: ObjSections,
pub entry: Option<u64>,
pub mw_comment: Option<MWComment>,
pub split_meta: Option<SplitMeta>,
// Linker generated
pub sda2_base: Option<u32>,
@@ -94,6 +96,7 @@ impl ObjInfo {
sections: ObjSections::new(kind, sections),
entry: None,
mw_comment: Default::default(),
split_meta: None,
sda2_base: None,
sda_base: None,
stack_address: None,

View File

@@ -28,7 +28,7 @@ pub struct ObjSection {
/// REL files reference the original ELF section indices
pub elf_index: usize,
pub relocations: ObjRelocations,
pub original_address: u64,
pub virtual_address: Option<u64>,
pub file_offset: u64,
pub section_known: bool,
pub splits: ObjSplits,

View File

@@ -89,7 +89,7 @@ where W: Write + ?Sized {
.or_else(|| vec.iter().find(|e| e.kind == SymbolEntryKind::Start))
.map(|e| e.index);
if target_symbol_idx.is_none() {
let display_address = address as u64 + section.original_address;
let display_address = address as u64 + section.virtual_address.unwrap_or(0);
let symbol_idx = symbols.len();
symbols.push(ObjSymbol {
name: format!(".L_{display_address:08X}"),
@@ -148,7 +148,7 @@ where W: Write + ?Sized {
.iter()
.any(|e| e.kind == SymbolEntryKind::Label || e.kind == SymbolEntryKind::Start)
{
let display_address = address + target_section.original_address;
let display_address = address + target_section.virtual_address.unwrap_or(0);
let symbol_idx = symbols.len();
symbols.push(ObjSymbol {
name: format!(".L_{display_address:08X}"),
@@ -246,7 +246,7 @@ where
for ins in disasm_iter(data, address) {
let reloc = relocations.get(&ins.addr);
let file_offset = section.file_offset + (ins.addr as u64 - section.address);
write_ins(w, symbols, ins, reloc, file_offset, section.original_address)?;
write_ins(w, symbols, ins, reloc, file_offset, section.virtual_address)?;
}
Ok(())
}
@@ -257,7 +257,7 @@ fn write_ins<W>(
mut ins: Ins,
reloc: Option<&ObjReloc>,
file_offset: u64,
section_address: u64,
section_vaddr: Option<u64>,
) -> Result<()>
where
W: Write + ?Sized,
@@ -265,7 +265,7 @@ where
write!(
w,
"/* {:08X} {:08X} {:02X} {:02X} {:02X} {:02X} */\t",
ins.addr as u64 + section_address,
ins.addr as u64 + section_vaddr.unwrap_or(0),
file_offset,
(ins.code >> 24) & 0xFF,
(ins.code >> 16) & 0xFF,
@@ -466,7 +466,7 @@ where
let dbg_symbols = vec.iter().map(|e| &symbols[e.index]).collect_vec();
bail!(
"Unaligned symbol entry @ {:#010X}:\n\t{:?}",
section.original_address as u32 + sym_addr,
section.virtual_address.unwrap_or(0) as u32 + sym_addr,
dbg_symbols
);
}
@@ -838,11 +838,12 @@ fn write_section_header<W>(
where
W: Write + ?Sized,
{
let section_virtual_address = section.virtual_address.unwrap_or(0);
writeln!(
w,
"\n# {:#010X} - {:#010X}",
start as u64 + section.original_address,
end as u64 + section.original_address
start as u64 + section_virtual_address,
end as u64 + section_virtual_address
)?;
match section.name.as_str() {
".text" if subsection == 0 => {

View File

@@ -430,7 +430,7 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result<ObjInfo> {
align: 0,
elf_index: 0,
relocations: Default::default(),
original_address: 0,
virtual_address: Some(dol_section.address as u64),
file_offset: dol_section.file_offset as u64,
section_known: known,
splits: Default::default(),
@@ -460,7 +460,7 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result<ObjInfo> {
align: 0,
elf_index: 0,
relocations: Default::default(),
original_address: 0,
virtual_address: Some(addr as u64),
file_offset: 0,
section_known: false,
splits: Default::default(),
@@ -480,7 +480,7 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result<ObjInfo> {
align: 0,
elf_index: 0,
relocations: Default::default(),
original_address: 0,
virtual_address: Some(bss_section.address as u64),
file_offset: 0,
section_known: false,
splits: Default::default(),
@@ -507,7 +507,7 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result<ObjInfo> {
align: 0,
elf_index: 0,
relocations: Default::default(),
original_address: 0,
virtual_address: Some(bss_sections[0].0 as u64),
file_offset: 0,
section_known: false,
splits: Default::default(),
@@ -521,7 +521,7 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result<ObjInfo> {
align: 0,
elf_index: 0,
relocations: Default::default(),
original_address: 0,
virtual_address: Some(bss_sections[1].0 as u64),
file_offset: 0,
section_known: false,
splits: Default::default(),

View File

@@ -9,6 +9,7 @@ use anyhow::{anyhow, bail, ensure, Context, Result};
use cwdemangle::demangle;
use flagset::Flags;
use indexmap::IndexMap;
use objdiff_core::obj::split_meta::{SplitMeta, SHT_SPLITMETA, SPLITMETA_SECTION};
use object::{
elf,
elf::{SHF_ALLOC, SHF_EXECINSTR, SHF_WRITE, SHT_NOBITS, SHT_PROGBITS},
@@ -95,7 +96,7 @@ where P: AsRef<Path> {
align: section.align(),
elf_index: section.index().0,
relocations: Default::default(),
original_address: 0, // TODO load from abs symbol
virtual_address: None, // Loaded from section symbol
file_offset: section.file_range().map(|(v, _)| v).unwrap_or_default(),
section_known: true,
splits: Default::default(),
@@ -127,6 +128,26 @@ where P: AsRef<Path> {
None
};
let split_meta = if let Some(split_meta_section) = obj_file.section_by_name(SPLITMETA_SECTION) {
let data = split_meta_section.uncompressed_data()?;
if data.is_empty() {
None
} else {
let mut reader = Cursor::new(&*data);
let metadata =
SplitMeta::from_reader(&mut reader, obj_file.endianness(), obj_file.is_64())
.context("While reading .splitmeta section")?;
log::debug!("Loaded .splitmeta section");
ensure!(
data.len() - reader.position() as usize == 0,
".splitmeta section data not fully read"
);
Some(metadata)
}
} else {
None
};
let mut symbols: Vec<ObjSymbol> = vec![];
let mut symbol_indexes: Vec<Option<usize>> = vec![];
let mut section_starts = IndexMap::<String, Vec<(u64, String)>>::new();
@@ -209,6 +230,16 @@ where P: AsRef<Path> {
let section_index = symbol
.section_index()
.ok_or_else(|| anyhow!("Section symbol without section"))?;
// Resolve original address from split metadata
if let Some(addr) = split_meta
.as_ref()
.and_then(|m| m.virtual_addresses.as_ref())
.and_then(|v| v.get(symbol.index().0).cloned())
{
sections[section_index.0].virtual_address = Some(addr);
}
let section = obj_file.section_by_index(section_index)?;
let section_name = section.name()?.to_string();
match &mut boundary_state {
@@ -335,6 +366,7 @@ where P: AsRef<Path> {
let mut obj = ObjInfo::new(kind, architecture, obj_name, symbols, sections);
obj.entry = NonZeroU64::new(obj_file.entry()).map(|n| n.get());
obj.mw_comment = mw_comment.map(|(header, _)| header);
obj.split_meta = split_meta;
obj.sda2_base = sda2_base;
obj.sda_base = sda_base;
obj.stack_address = stack_address;
@@ -348,7 +380,7 @@ where P: AsRef<Path> {
pub fn write_elf(obj: &ObjInfo, export_all: bool) -> Result<Vec<u8>> {
let mut out_data = Vec::new();
let mut writer = object::write::elf::Writer::new(Endianness::Big, false, &mut out_data);
let mut writer = Writer::new(Endianness::Big, false, &mut out_data);
struct OutSection {
index: SectionIndex,
@@ -357,6 +389,7 @@ pub fn write_elf(obj: &ObjInfo, export_all: bool) -> Result<Vec<u8>> {
rela_offset: usize,
name: StringId,
rela_name: Option<StringId>,
virtual_address: Option<u64>,
}
struct OutSymbol {
#[allow(dead_code)]
@@ -376,6 +409,7 @@ pub fn write_elf(obj: &ObjInfo, export_all: bool) -> Result<Vec<u8>> {
rela_offset: 0,
name,
rela_name: None,
virtual_address: section.virtual_address,
});
}
@@ -395,11 +429,12 @@ pub fn write_elf(obj: &ObjInfo, export_all: bool) -> Result<Vec<u8>> {
writer.reserve_strtab_section_index();
writer.reserve_shstrtab_section_index();
// Generate comment section
// Generate .comment section
let mut comment_data = if let Some(mw_comment) = &obj.mw_comment {
let mut comment_data = Vec::<u8>::with_capacity(0x2C + obj.symbols.count() * 8);
// Reserve section
let name = writer.add_section_name(".comment".as_bytes());
let index = writer.reserve_section_index();
let out_section_idx = out_sections.len();
out_sections.push(OutSection {
index,
rela_index: None,
@@ -407,12 +442,42 @@ pub fn write_elf(obj: &ObjInfo, export_all: bool) -> Result<Vec<u8>> {
rela_offset: 0,
name,
rela_name: None,
virtual_address: None,
});
// Generate .comment data
let mut comment_data = Vec::<u8>::with_capacity(0x2C + obj.symbols.count() * 8);
mw_comment.to_writer_static(&mut comment_data, Endian::Big)?;
// Null symbol
CommentSym { align: 0, vis_flags: 0, active_flags: 0 }
.to_writer_static(&mut comment_data, Endian::Big)?;
Some(comment_data)
Some((comment_data, out_section_idx))
} else {
None
};
// Generate .splitmeta section
let mut split_meta = if let Some(metadata) = &obj.split_meta {
// Reserve section
let name = writer.add_section_name(SPLITMETA_SECTION.as_bytes());
let index = writer.reserve_section_index();
let out_section_idx = out_sections.len();
out_sections.push(OutSection {
index,
rela_index: None,
offset: 0,
rela_offset: 0,
name,
rela_name: None,
virtual_address: None,
});
// Generate .splitmeta data
let mut out = metadata.clone();
out.virtual_addresses = Some(vec![
0, // Null symbol
]);
Some((out, out_section_idx))
} else {
None
};
@@ -449,10 +514,15 @@ pub fn write_elf(obj: &ObjInfo, export_all: bool) -> Result<Vec<u8>> {
st_size: 0,
},
});
if let Some(comment_data) = &mut comment_data {
if let Some((comment_data, _)) = &mut comment_data {
CommentSym { align: 1, vis_flags: 0, active_flags: 0 }
.to_writer_static(comment_data, Endian::Big)?;
}
if let Some(virtual_addresses) =
split_meta.as_mut().and_then(|(m, _)| m.virtual_addresses.as_mut())
{
virtual_addresses.push(0);
}
section_symbol_offset += 1;
}
@@ -472,10 +542,15 @@ pub fn write_elf(obj: &ObjInfo, export_all: bool) -> Result<Vec<u8>> {
};
num_local = writer.symbol_count();
out_symbols.push(OutSymbol { index, sym });
if let Some(comment_data) = &mut comment_data {
if let Some((comment_data, _)) = &mut comment_data {
CommentSym { align: section.align as u32, vis_flags: 0, active_flags: 0 }
.to_writer_static(comment_data, Endian::Big)?;
}
if let Some(virtual_addresses) =
split_meta.as_mut().and_then(|(m, _)| m.virtual_addresses.as_mut())
{
virtual_addresses.push(section.virtual_address.unwrap_or(0));
}
}
}
@@ -495,7 +570,8 @@ pub fn write_elf(obj: &ObjInfo, export_all: bool) -> Result<Vec<u8>> {
continue;
}
let section_index = symbol.section.and_then(|idx| out_sections.get(idx)).map(|s| s.index);
let section = symbol.section.and_then(|idx| out_sections.get(idx));
let section_index = section.map(|s| s.index);
let index = writer.reserve_symbol_index(section_index);
let name_index = if symbol.name.is_empty() {
None
@@ -539,9 +615,18 @@ pub fn write_elf(obj: &ObjInfo, export_all: bool) -> Result<Vec<u8>> {
}
out_symbols.push(OutSymbol { index, sym });
symbol_map[symbol_index] = Some(index.0);
if let Some(comment_data) = &mut comment_data {
if let Some((comment_data, _)) = &mut comment_data {
CommentSym::from(symbol, export_all).to_writer_static(comment_data, Endian::Big)?;
}
if let Some(virtual_addresses) =
split_meta.as_mut().and_then(|(m, _)| m.virtual_addresses.as_mut())
{
if let Some(section_vaddr) = section.and_then(|s| s.virtual_address) {
virtual_addresses.push(section_vaddr + symbol.address);
} else {
virtual_addresses.push(0);
}
}
}
writer.reserve_file_header();
@@ -576,12 +661,18 @@ pub fn write_elf(obj: &ObjInfo, export_all: bool) -> Result<Vec<u8>> {
writer.reserve_strtab();
writer.reserve_shstrtab();
// Reserve comment section
if let Some(comment_data) = &comment_data {
let out_section = out_sections.last_mut().unwrap();
// Reserve .comment section
if let Some((comment_data, idx)) = &comment_data {
let out_section = &mut out_sections[*idx];
out_section.offset = writer.reserve(comment_data.len(), 32);
}
// Reserve .splitmeta section
if let Some((metadata, idx)) = &split_meta {
let out_section = &mut out_sections[*idx];
out_section.offset = writer.reserve(metadata.write_size(false), 32);
}
writer.reserve_section_headers();
writer.write_file_header(&object::write::elf::FileHeader {
@@ -688,13 +779,24 @@ pub fn write_elf(obj: &ObjInfo, export_all: bool) -> Result<Vec<u8>> {
writer.write_shstrtab();
// Write comment section
if let Some(comment_data) = &comment_data {
let out_section = out_sections.last().unwrap();
if let Some((comment_data, idx)) = &comment_data {
let out_section = &out_sections[*idx];
writer.write_align(32);
ensure!(writer.len() == out_section.offset);
writer.write(comment_data);
}
// Write .splitmeta section
if let Some((metadata, idx)) = &split_meta {
let out_section = &out_sections[*idx];
writer.write_align(32);
ensure!(writer.len() == out_section.offset);
// object::write::elf::Writer doesn't implement std::io::Write...
let mut data = Vec::with_capacity(metadata.write_size(false));
metadata.to_writer(&mut data, object::BigEndian, false)?;
writer.write(&data);
}
writer.write_null_section_header();
for ((_, section), out_section) in obj.sections.iter().zip(&out_sections) {
writer.write_section_header(&SectionHeader {
@@ -737,9 +839,9 @@ pub fn write_elf(obj: &ObjInfo, export_all: bool) -> Result<Vec<u8>> {
writer.write_strtab_section_header();
writer.write_shstrtab_section_header();
// Write comment section header
if let Some(comment_data) = &comment_data {
let out_section = out_sections.last().unwrap();
// Write .comment section header
if let Some((comment_data, idx)) = &comment_data {
let out_section = &out_sections[*idx];
writer.write_section_header(&SectionHeader {
name: Some(out_section.name),
sh_type: SHT_PROGBITS,
@@ -754,6 +856,23 @@ pub fn write_elf(obj: &ObjInfo, export_all: bool) -> Result<Vec<u8>> {
});
}
// Write .splitmeta section header
if let Some((metadata, idx)) = &split_meta {
let out_section = &out_sections[*idx];
writer.write_section_header(&SectionHeader {
name: Some(out_section.name),
sh_type: SHT_SPLITMETA,
sh_flags: 0,
sh_addr: 0,
sh_offset: out_section.offset as u64,
sh_size: metadata.write_size(false) as u64,
sh_link: 0,
sh_info: 0,
sh_addralign: 1,
sh_entsize: 1,
});
}
ensure!(writer.reserved_len() == writer.len());
Ok(out_data)
}

View File

@@ -420,7 +420,7 @@ where R: Read + Seek + ?Sized {
.unwrap_or_default() as u64,
elf_index: idx,
relocations: Default::default(),
original_address: 0,
virtual_address: None, // TODO option to set?
file_offset: offset as u64,
section_known,
splits: Default::default(),

View File

@@ -410,7 +410,7 @@ where R: Read + Seek + ?Sized {
align: 0,
elf_index: idx as usize,
relocations: Default::default(),
original_address: 0,
virtual_address: None, // TODO option to set?
file_offset: offset as u64,
section_known: false,
splits: Default::default(),

View File

@@ -5,6 +5,7 @@ use std::{
use anyhow::{anyhow, bail, ensure, Context, Result};
use itertools::Itertools;
use objdiff_core::obj::split_meta::SplitMeta;
use petgraph::{graph::NodeIndex, Graph};
use sanitise_file_name::sanitize_with_options;
use tracing_attributes::instrument;
@@ -882,7 +883,7 @@ fn resolve_link_order(obj: &ObjInfo) -> Result<Vec<ObjUnit>> {
/// Split an object into multiple relocatable objects.
#[instrument(level = "debug", skip(obj))]
pub fn split_obj(obj: &ObjInfo) -> Result<Vec<ObjInfo>> {
pub fn split_obj(obj: &ObjInfo, module_name: Option<&str>) -> Result<Vec<ObjInfo>> {
let mut objects: Vec<ObjInfo> = vec![];
let mut object_symbols: Vec<Vec<Option<usize>>> = vec![];
let mut name_to_obj: HashMap<String, usize> = HashMap::new();
@@ -903,6 +904,12 @@ pub fn split_obj(obj: &ObjInfo) -> Result<Vec<ObjInfo>> {
} else {
split_obj.mw_comment = obj.mw_comment.clone();
}
split_obj.split_meta = Some(SplitMeta {
generator: Some(format!("{} {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"))),
module_name: module_name.map(str::to_string),
module_id: Some(obj.module_id),
virtual_addresses: None,
});
objects.push(split_obj);
}
@@ -1083,7 +1090,7 @@ pub fn split_obj(obj: &ObjInfo) -> Result<Vec<ObjInfo>> {
align,
elf_index: out_section_idx + 1,
relocations: ObjRelocations::new(out_relocations)?,
original_address: current_address.address as u64,
virtual_address: Some(current_address.address as u64),
file_offset: section.file_offset
+ (current_address.address as u64 - section.address),
section_known: true,
@@ -1148,7 +1155,7 @@ pub fn split_obj(obj: &ObjInfo) -> Result<Vec<ObjInfo>> {
else {
bail!(
"Bad extabindex relocation @ {:#010X}",
reloc_address as u64 + section.original_address
reloc_address as u64 + section.virtual_address.unwrap_or(0)
);
};
let target_section = &obj.sections.at_address(target_addr)?.1.name;
@@ -1158,9 +1165,9 @@ pub fn split_obj(obj: &ObjInfo) -> Result<Vec<ObjInfo>> {
\tTarget object: {}:{:#010X} ({})\n\
\tTarget symbol: {:#010X} ({})\n\
This will cause the linker to crash.\n",
reloc_address as u64 + section.original_address,
reloc_address as u64 + section.virtual_address.unwrap_or(0),
section.name,
section.original_address,
section.virtual_address.unwrap_or(0),
out_obj.name,
target_section,
target_addr,