Skip to content

Commit 4935708

Browse files
committed
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.
1 parent 456f4ee commit 4935708

File tree

2 files changed

+170
-59
lines changed

2 files changed

+170
-59
lines changed

src/cmd/rel.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ use crate::{
3636
file::{buf_reader, buf_writer, map_file, process_rsp, verify_hash, FileIterator},
3737
nested::NestedMap,
3838
rel::{
39-
process_rel, process_rel_header, process_rel_sections, write_rel, RelHeader, RelReloc,
40-
RelSectionHeader, RelWriteInfo, PERMITTED_SECTIONS,
39+
print_relocations, process_rel, process_rel_header, process_rel_sections, write_rel,
40+
RelHeader, RelReloc, RelSectionHeader, RelWriteInfo, PERMITTED_SECTIONS,
4141
},
4242
IntoCow, ToCow,
4343
},
@@ -66,6 +66,9 @@ pub struct InfoArgs {
6666
#[argp(positional)]
6767
/// REL file
6868
rel_file: PathBuf,
69+
#[argp(switch, short = 'r')]
70+
/// print relocations
71+
relocations: bool,
6972
}
7073

7174
#[derive(FromArgs, PartialEq, Eq, Debug)]
@@ -408,6 +411,12 @@ fn info(args: InfoArgs) -> Result<()> {
408411
section_str, symbol.address, size_str, symbol.name
409412
);
410413
}
414+
415+
if args.relocations {
416+
println!("\nRelocations:");
417+
println!(" [Source] section:address RelocType -> [Target] module:section:address");
418+
print_relocations(&mut file.as_reader(), &header)?;
419+
}
411420
Ok(())
412421
}
413422

src/util/rel.rs

+159-57
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,53 @@ where R: Read + Seek + ?Sized {
553553
Ok((header, obj))
554554
}
555555

556+
pub fn print_relocations<R>(reader: &mut R, header: &RelHeader) -> Result<()>
557+
where R: Read + Seek + ?Sized {
558+
let imp_end = (header.imp_offset + header.imp_size) as u64;
559+
reader.seek(SeekFrom::Start(header.imp_offset as u64))?;
560+
while reader.stream_position()? < imp_end {
561+
let import = RelImport::from_reader(reader, Endian::Big)?;
562+
println!("Module {} (file offset {:#X}):", import.module_id, import.offset);
563+
564+
let position = reader.stream_position()?;
565+
reader.seek(SeekFrom::Start(import.offset as u64))?;
566+
let mut address = 0u32;
567+
let mut section = u8::MAX;
568+
loop {
569+
let reloc = RelRelocRaw::from_reader(reader, Endian::Big)?;
570+
let kind = match reloc.kind as u32 {
571+
elf::R_PPC_NONE => continue,
572+
elf::R_PPC_ADDR32 | elf::R_PPC_UADDR32 => ObjRelocKind::Absolute,
573+
elf::R_PPC_ADDR16_LO => ObjRelocKind::PpcAddr16Lo,
574+
elf::R_PPC_ADDR16_HI => ObjRelocKind::PpcAddr16Hi,
575+
elf::R_PPC_ADDR16_HA => ObjRelocKind::PpcAddr16Ha,
576+
elf::R_PPC_REL24 => ObjRelocKind::PpcRel24,
577+
elf::R_PPC_REL14 => ObjRelocKind::PpcRel14,
578+
R_DOLPHIN_NOP => {
579+
address += reloc.offset as u32;
580+
continue;
581+
}
582+
R_DOLPHIN_SECTION => {
583+
address = 0;
584+
section = reloc.section;
585+
continue;
586+
}
587+
R_DOLPHIN_END => break,
588+
// R_DOLPHIN_MRKREF => ?
589+
reloc_type => bail!("Unhandled REL relocation type {reloc_type}"),
590+
};
591+
address += reloc.offset as u32;
592+
println!(
593+
" {}:{:#X} {:?} -> {}:{}:{:#X}",
594+
reloc.section, address, kind, import.module_id, section, reloc.addend
595+
);
596+
}
597+
reader.seek(SeekFrom::Start(position))?;
598+
}
599+
600+
Ok(())
601+
}
602+
556603
/// REL relocation.
557604
#[derive(Debug, Clone)]
558605
pub struct RelReloc {
@@ -663,29 +710,41 @@ pub fn write_rel<W>(
663710
where
664711
W: Write + Seek + ?Sized,
665712
{
666-
relocations.sort_by(|a, b| {
667-
if a.module_id == 0 {
668-
if b.module_id == 0 {
669-
Ordering::Equal
670-
} else {
671-
Ordering::Greater
672-
}
673-
} else if a.module_id == info.module_id {
674-
if b.module_id == 0 {
713+
if info.version >= 3 {
714+
// Version 3 RELs put module ID 0 and self-relocations last,
715+
// so that the space can be reclaimed via OSLinkFixed. (See fix_size)
716+
relocations.sort_by(|a, b| {
717+
if a.module_id == 0 {
718+
if b.module_id == 0 {
719+
Ordering::Equal
720+
} else {
721+
Ordering::Greater
722+
}
723+
} else if a.module_id == info.module_id {
724+
if b.module_id == 0 {
725+
Ordering::Less
726+
} else if b.module_id == info.module_id {
727+
Ordering::Equal
728+
} else {
729+
Ordering::Greater
730+
}
731+
} else if b.module_id == 0 || b.module_id == info.module_id {
675732
Ordering::Less
676-
} else if b.module_id == info.module_id {
677-
Ordering::Equal
678733
} else {
679-
Ordering::Greater
734+
a.module_id.cmp(&b.module_id)
680735
}
681-
} else if b.module_id == 0 || b.module_id == info.module_id {
682-
Ordering::Less
683-
} else {
684-
a.module_id.cmp(&b.module_id)
685-
}
686-
.then(a.section.cmp(&b.section))
687-
.then(a.address.cmp(&b.address))
688-
});
736+
.then(a.section.cmp(&b.section))
737+
.then(a.address.cmp(&b.address))
738+
});
739+
} else {
740+
// Version 1 and 2 RELs use simple ascending order.
741+
relocations.sort_by(|a, b| {
742+
a.module_id
743+
.cmp(&b.module_id)
744+
.then(a.section.cmp(&b.section))
745+
.then(a.address.cmp(&b.address))
746+
});
747+
}
689748

690749
let mut apply_relocations = vec![];
691750
relocations.retain(|r| {
@@ -779,27 +838,28 @@ where
779838
header.section_info_offset = offset;
780839
offset += num_sections * RelSectionHeader::STATIC_SIZE as u32;
781840
let section_data_offset = offset;
782-
for (idx, section) in file.sections().filter(is_permitted_section).enumerate() {
783-
if !should_write_section(&section) {
784-
continue;
785-
}
841+
for (idx, section) in file
842+
.sections()
843+
.filter(is_permitted_section)
844+
.enumerate()
845+
.filter(|(_, s)| should_write_section(s))
846+
{
786847
let align = section_align(idx, &section, info) - 1;
787848
offset = (offset + align) & !align;
788849
offset += section.size() as u32;
789850
}
790-
header.imp_offset = offset;
791-
let imp_count = relocations.iter().map(|r| r.module_id).dedup().count();
792-
header.imp_size = imp_count as u32 * RelImport::STATIC_SIZE as u32;
793-
offset += header.imp_size;
794-
header.rel_offset = offset;
795851

796-
let mut imp_entries = Vec::<RelImport>::with_capacity(imp_count);
797-
let mut raw_relocations = vec![];
798-
{
852+
fn do_relocation_layout(
853+
relocations: &[RelReloc],
854+
header: &mut RelHeader,
855+
imp_entries: &mut Vec<RelImport>,
856+
raw_relocations: &mut Vec<RelRelocRaw>,
857+
offset: &mut u32,
858+
) -> Result<()> {
799859
let mut address = 0u32;
800860
let mut section = u8::MAX;
801861
let mut last_module_id = u32::MAX;
802-
for reloc in &relocations {
862+
for reloc in relocations {
803863
if reloc.module_id != last_module_id {
804864
if last_module_id != u32::MAX {
805865
raw_relocations.push(RelRelocRaw {
@@ -808,17 +868,17 @@ where
808868
section: 0,
809869
addend: 0,
810870
});
811-
offset += 8;
871+
*offset += 8;
812872
}
813-
imp_entries.push(RelImport { module_id: reloc.module_id, offset });
873+
imp_entries.push(RelImport { module_id: reloc.module_id, offset: *offset });
814874
section = u8::MAX;
815875
last_module_id = reloc.module_id;
816876
}
817-
if info.version >= 3
877+
if header.version >= 3
818878
&& header.fix_size.is_none()
819-
&& (reloc.module_id == 0 || reloc.module_id == info.module_id)
879+
&& (reloc.module_id == 0 || reloc.module_id == header.module_id)
820880
{
821-
header.fix_size = Some(offset);
881+
header.fix_size = Some(*offset);
822882
}
823883
if reloc.section != section {
824884
raw_relocations.push(RelRelocRaw {
@@ -827,7 +887,7 @@ where
827887
section: reloc.section,
828888
addend: 0,
829889
});
830-
offset += 8;
890+
*offset += 8;
831891
address = 0;
832892
section = reloc.section;
833893
}
@@ -839,7 +899,7 @@ where
839899
section: 0,
840900
addend: 0,
841901
});
842-
offset += 8;
902+
*offset += 8;
843903
reloc_offset -= 0xffff;
844904
}
845905
raw_relocations.push(RelRelocRaw {
@@ -857,16 +917,47 @@ where
857917
addend: reloc.addend,
858918
});
859919
address = reloc.address;
860-
offset += 8;
920+
*offset += 8;
861921
}
922+
raw_relocations.push(RelRelocRaw {
923+
offset: 0,
924+
kind: R_DOLPHIN_END as u8,
925+
section: 0,
926+
addend: 0,
927+
});
928+
*offset += 8;
929+
Ok(())
930+
}
931+
932+
let imp_count = relocations.iter().map(|r| r.module_id).dedup().count();
933+
let mut imp_entries = Vec::<RelImport>::with_capacity(imp_count);
934+
let mut raw_relocations = vec![];
935+
if info.version < 3 {
936+
// Version 1 and 2 RELs write relocations before the import table.
937+
header.rel_offset = offset;
938+
do_relocation_layout(
939+
&relocations,
940+
&mut header,
941+
&mut imp_entries,
942+
&mut raw_relocations,
943+
&mut offset,
944+
)?;
945+
}
946+
header.imp_offset = offset;
947+
header.imp_size = imp_count as u32 * RelImport::STATIC_SIZE as u32;
948+
offset += header.imp_size;
949+
if info.version >= 3 {
950+
// Version 3 RELs write relocations after the import table,
951+
// so that the import table isn't clobbered by OSLinkFixed.
952+
header.rel_offset = offset;
953+
do_relocation_layout(
954+
&relocations,
955+
&mut header,
956+
&mut imp_entries,
957+
&mut raw_relocations,
958+
&mut offset,
959+
)?;
862960
}
863-
raw_relocations.push(RelRelocRaw {
864-
offset: 0,
865-
kind: R_DOLPHIN_END as u8,
866-
section: 0,
867-
addend: 0,
868-
});
869-
offset += 8;
870961

871962
for symbol in file.symbols().filter(|s| s.is_definition()) {
872963
let Some(symbol_section) = symbol.section_index() else {
@@ -919,11 +1010,12 @@ where
9191010
}
9201011
}
9211012
ensure!(w.stream_position()? as u32 == section_data_offset);
922-
for (idx, section) in file.sections().filter(is_permitted_section).enumerate() {
923-
if !should_write_section(&section) {
924-
continue;
925-
}
926-
1013+
for (idx, section) in file
1014+
.sections()
1015+
.filter(is_permitted_section)
1016+
.enumerate()
1017+
.filter(|(_, s)| should_write_section(s))
1018+
{
9271019
fn calculate_padding(position: u64, align: u64) -> u64 {
9281020
let align = align - 1;
9291021
((position + align) & !align) - position
@@ -943,13 +1035,23 @@ where
9431035
}
9441036
w.write_all(&section_data)?;
9451037
}
1038+
if info.version < 3 {
1039+
// Version 1 and 2 RELs write relocations before the import table.
1040+
ensure!(w.stream_position()? as u32 == header.rel_offset);
1041+
for reloc in &raw_relocations {
1042+
reloc.to_writer(w, Endian::Big)?;
1043+
}
1044+
}
9461045
ensure!(w.stream_position()? as u32 == header.imp_offset);
947-
for entry in imp_entries {
1046+
for entry in &imp_entries {
9481047
entry.to_writer(w, Endian::Big)?;
9491048
}
950-
ensure!(w.stream_position()? as u32 == header.rel_offset);
951-
for reloc in raw_relocations {
952-
reloc.to_writer(w, Endian::Big)?;
1049+
if info.version >= 3 {
1050+
// Version 3 RELs write relocations after the import table. See above.
1051+
ensure!(w.stream_position()? as u32 == header.rel_offset);
1052+
for reloc in &raw_relocations {
1053+
reloc.to_writer(w, Endian::Big)?;
1054+
}
9531055
}
9541056
ensure!(w.stream_position()? as u32 == offset);
9551057
Ok(())

0 commit comments

Comments
 (0)