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:
parent
456f4eebd4
commit
4935708b61
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
162
src/util/rel.rs
162
src/util/rel.rs
|
@ -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(§ion) {
|
.sections()
|
||||||
continue;
|
.filter(is_permitted_section)
|
||||||
}
|
.enumerate()
|
||||||
|
.filter(|(_, s)| should_write_section(s))
|
||||||
|
{
|
||||||
let align = section_align(idx, §ion, info) - 1;
|
let align = section_align(idx, §ion, 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(§ion) {
|
.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(§ion_data)?;
|
w.write_all(§ion_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(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue