Fix REL v2 creation (and v1, hopefully)

- Adjusts `write_rel` to use the proper ordering for relocations and imports based on the REL version.
- Adds `-r`/`--relocations` switch to `rel info` that prints (very) verbose relocation information.
This commit is contained in:
Luke Street 2023-11-09 01:16:37 -05:00
parent 456f4eebd4
commit 4935708b61
2 changed files with 170 additions and 59 deletions

View File

@ -36,8 +36,8 @@ use crate::{
file::{buf_reader, buf_writer, map_file, process_rsp, verify_hash, FileIterator}, file::{buf_reader, buf_writer, map_file, process_rsp, verify_hash, FileIterator},
nested::NestedMap, nested::NestedMap,
rel::{ rel::{
process_rel, process_rel_header, process_rel_sections, write_rel, RelHeader, RelReloc, print_relocations, process_rel, process_rel_header, process_rel_sections, write_rel,
RelSectionHeader, RelWriteInfo, PERMITTED_SECTIONS, RelHeader, RelReloc, RelSectionHeader, RelWriteInfo, PERMITTED_SECTIONS,
}, },
IntoCow, ToCow, IntoCow, ToCow,
}, },
@ -66,6 +66,9 @@ pub struct InfoArgs {
#[argp(positional)] #[argp(positional)]
/// REL file /// REL file
rel_file: PathBuf, rel_file: PathBuf,
#[argp(switch, short = 'r')]
/// print relocations
relocations: bool,
} }
#[derive(FromArgs, PartialEq, Eq, Debug)] #[derive(FromArgs, PartialEq, Eq, Debug)]
@ -408,6 +411,12 @@ fn info(args: InfoArgs) -> Result<()> {
section_str, symbol.address, size_str, symbol.name section_str, symbol.address, size_str, symbol.name
); );
} }
if args.relocations {
println!("\nRelocations:");
println!(" [Source] section:address RelocType -> [Target] module:section:address");
print_relocations(&mut file.as_reader(), &header)?;
}
Ok(()) Ok(())
} }

View File

@ -553,6 +553,53 @@ where R: Read + Seek + ?Sized {
Ok((header, obj)) Ok((header, obj))
} }
pub fn print_relocations<R>(reader: &mut R, header: &RelHeader) -> Result<()>
where R: Read + Seek + ?Sized {
let imp_end = (header.imp_offset + header.imp_size) as u64;
reader.seek(SeekFrom::Start(header.imp_offset as u64))?;
while reader.stream_position()? < imp_end {
let import = RelImport::from_reader(reader, Endian::Big)?;
println!("Module {} (file offset {:#X}):", import.module_id, import.offset);
let position = reader.stream_position()?;
reader.seek(SeekFrom::Start(import.offset as u64))?;
let mut address = 0u32;
let mut section = u8::MAX;
loop {
let reloc = RelRelocRaw::from_reader(reader, Endian::Big)?;
let kind = match reloc.kind as u32 {
elf::R_PPC_NONE => continue,
elf::R_PPC_ADDR32 | elf::R_PPC_UADDR32 => ObjRelocKind::Absolute,
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,
R_DOLPHIN_NOP => {
address += reloc.offset as u32;
continue;
}
R_DOLPHIN_SECTION => {
address = 0;
section = reloc.section;
continue;
}
R_DOLPHIN_END => break,
// R_DOLPHIN_MRKREF => ?
reloc_type => bail!("Unhandled REL relocation type {reloc_type}"),
};
address += reloc.offset as u32;
println!(
" {}:{:#X} {:?} -> {}:{}:{:#X}",
reloc.section, address, kind, import.module_id, section, reloc.addend
);
}
reader.seek(SeekFrom::Start(position))?;
}
Ok(())
}
/// REL relocation. /// REL relocation.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct RelReloc { pub struct RelReloc {
@ -663,6 +710,9 @@ pub fn write_rel<W>(
where where
W: Write + Seek + ?Sized, W: Write + Seek + ?Sized,
{ {
if info.version >= 3 {
// Version 3 RELs put module ID 0 and self-relocations last,
// so that the space can be reclaimed via OSLinkFixed. (See fix_size)
relocations.sort_by(|a, b| { relocations.sort_by(|a, b| {
if a.module_id == 0 { if a.module_id == 0 {
if b.module_id == 0 { if b.module_id == 0 {
@ -686,6 +736,15 @@ where
.then(a.section.cmp(&b.section)) .then(a.section.cmp(&b.section))
.then(a.address.cmp(&b.address)) .then(a.address.cmp(&b.address))
}); });
} else {
// Version 1 and 2 RELs use simple ascending order.
relocations.sort_by(|a, b| {
a.module_id
.cmp(&b.module_id)
.then(a.section.cmp(&b.section))
.then(a.address.cmp(&b.address))
});
}
let mut apply_relocations = vec![]; let mut apply_relocations = vec![];
relocations.retain(|r| { relocations.retain(|r| {
@ -779,27 +838,28 @@ where
header.section_info_offset = offset; header.section_info_offset = offset;
offset += num_sections * RelSectionHeader::STATIC_SIZE as u32; offset += num_sections * RelSectionHeader::STATIC_SIZE as u32;
let section_data_offset = offset; let section_data_offset = offset;
for (idx, section) in file.sections().filter(is_permitted_section).enumerate() { for (idx, section) in file
if !should_write_section(&section) { .sections()
continue; .filter(is_permitted_section)
} .enumerate()
.filter(|(_, s)| should_write_section(s))
{
let align = section_align(idx, &section, info) - 1; let align = section_align(idx, &section, info) - 1;
offset = (offset + align) & !align; offset = (offset + align) & !align;
offset += section.size() as u32; offset += section.size() as u32;
} }
header.imp_offset = offset;
let imp_count = relocations.iter().map(|r| r.module_id).dedup().count();
header.imp_size = imp_count as u32 * RelImport::STATIC_SIZE as u32;
offset += header.imp_size;
header.rel_offset = offset;
let mut imp_entries = Vec::<RelImport>::with_capacity(imp_count); fn do_relocation_layout(
let mut raw_relocations = vec![]; relocations: &[RelReloc],
{ header: &mut RelHeader,
imp_entries: &mut Vec<RelImport>,
raw_relocations: &mut Vec<RelRelocRaw>,
offset: &mut u32,
) -> Result<()> {
let mut address = 0u32; let mut address = 0u32;
let mut section = u8::MAX; let mut section = u8::MAX;
let mut last_module_id = u32::MAX; let mut last_module_id = u32::MAX;
for reloc in &relocations { for reloc in relocations {
if reloc.module_id != last_module_id { if reloc.module_id != last_module_id {
if last_module_id != u32::MAX { if last_module_id != u32::MAX {
raw_relocations.push(RelRelocRaw { raw_relocations.push(RelRelocRaw {
@ -808,17 +868,17 @@ where
section: 0, section: 0,
addend: 0, addend: 0,
}); });
offset += 8; *offset += 8;
} }
imp_entries.push(RelImport { module_id: reloc.module_id, offset }); imp_entries.push(RelImport { module_id: reloc.module_id, offset: *offset });
section = u8::MAX; section = u8::MAX;
last_module_id = reloc.module_id; last_module_id = reloc.module_id;
} }
if info.version >= 3 if header.version >= 3
&& header.fix_size.is_none() && header.fix_size.is_none()
&& (reloc.module_id == 0 || reloc.module_id == info.module_id) && (reloc.module_id == 0 || reloc.module_id == header.module_id)
{ {
header.fix_size = Some(offset); header.fix_size = Some(*offset);
} }
if reloc.section != section { if reloc.section != section {
raw_relocations.push(RelRelocRaw { raw_relocations.push(RelRelocRaw {
@ -827,7 +887,7 @@ where
section: reloc.section, section: reloc.section,
addend: 0, addend: 0,
}); });
offset += 8; *offset += 8;
address = 0; address = 0;
section = reloc.section; section = reloc.section;
} }
@ -839,7 +899,7 @@ where
section: 0, section: 0,
addend: 0, addend: 0,
}); });
offset += 8; *offset += 8;
reloc_offset -= 0xffff; reloc_offset -= 0xffff;
} }
raw_relocations.push(RelRelocRaw { raw_relocations.push(RelRelocRaw {
@ -857,8 +917,7 @@ where
addend: reloc.addend, addend: reloc.addend,
}); });
address = reloc.address; address = reloc.address;
offset += 8; *offset += 8;
}
} }
raw_relocations.push(RelRelocRaw { raw_relocations.push(RelRelocRaw {
offset: 0, offset: 0,
@ -866,7 +925,39 @@ where
section: 0, section: 0,
addend: 0, addend: 0,
}); });
offset += 8; *offset += 8;
Ok(())
}
let imp_count = relocations.iter().map(|r| r.module_id).dedup().count();
let mut imp_entries = Vec::<RelImport>::with_capacity(imp_count);
let mut raw_relocations = vec![];
if info.version < 3 {
// Version 1 and 2 RELs write relocations before the import table.
header.rel_offset = offset;
do_relocation_layout(
&relocations,
&mut header,
&mut imp_entries,
&mut raw_relocations,
&mut offset,
)?;
}
header.imp_offset = offset;
header.imp_size = imp_count as u32 * RelImport::STATIC_SIZE as u32;
offset += header.imp_size;
if info.version >= 3 {
// Version 3 RELs write relocations after the import table,
// so that the import table isn't clobbered by OSLinkFixed.
header.rel_offset = offset;
do_relocation_layout(
&relocations,
&mut header,
&mut imp_entries,
&mut raw_relocations,
&mut offset,
)?;
}
for symbol in file.symbols().filter(|s| s.is_definition()) { for symbol in file.symbols().filter(|s| s.is_definition()) {
let Some(symbol_section) = symbol.section_index() else { let Some(symbol_section) = symbol.section_index() else {
@ -919,11 +1010,12 @@ where
} }
} }
ensure!(w.stream_position()? as u32 == section_data_offset); ensure!(w.stream_position()? as u32 == section_data_offset);
for (idx, section) in file.sections().filter(is_permitted_section).enumerate() { for (idx, section) in file
if !should_write_section(&section) { .sections()
continue; .filter(is_permitted_section)
} .enumerate()
.filter(|(_, s)| should_write_section(s))
{
fn calculate_padding(position: u64, align: u64) -> u64 { fn calculate_padding(position: u64, align: u64) -> u64 {
let align = align - 1; let align = align - 1;
((position + align) & !align) - position ((position + align) & !align) - position
@ -943,14 +1035,24 @@ where
} }
w.write_all(&section_data)?; w.write_all(&section_data)?;
} }
if info.version < 3 {
// Version 1 and 2 RELs write relocations before the import table.
ensure!(w.stream_position()? as u32 == header.rel_offset);
for reloc in &raw_relocations {
reloc.to_writer(w, Endian::Big)?;
}
}
ensure!(w.stream_position()? as u32 == header.imp_offset); ensure!(w.stream_position()? as u32 == header.imp_offset);
for entry in imp_entries { for entry in &imp_entries {
entry.to_writer(w, Endian::Big)?; entry.to_writer(w, Endian::Big)?;
} }
if info.version >= 3 {
// Version 3 RELs write relocations after the import table. See above.
ensure!(w.stream_position()? as u32 == header.rel_offset); ensure!(w.stream_position()? as u32 == header.rel_offset);
for reloc in raw_relocations { for reloc in &raw_relocations {
reloc.to_writer(w, Endian::Big)?; reloc.to_writer(w, Endian::Big)?;
} }
}
ensure!(w.stream_position()? as u32 == offset); ensure!(w.stream_position()? as u32 == offset);
Ok(()) Ok(())
} }