mod support;
use elf_loader::{Loader, arch::NativeArch, input::ElfBinary, relocation::RelocationArch};
const REL_COPY: u32 = <NativeArch as RelocationArch>::COPY.raw();
const REL_IRELATIVE: u32 = <NativeArch as RelocationArch>::IRELATIVE.raw();
const REL_RELATIVE: u32 = <NativeArch as RelocationArch>::RELATIVE.raw();
use gen_elf::{Arch, ElfWriterConfig, RelocEntry, SectionKind, SymbolDesc};
use support::{
dylib_relocation_checks::{
anonymous_relocations, relocation_for_symbol, slot_address, slot_word,
},
test_dylib::{load_relocated_dylib, write_test_dylib, write_test_dylib_with_config},
};
const COPY_SOURCE_NAME: &str = "copy_source";
const COPY_SOURCE_NAME2: &str = "copy_source_two";
#[test]
fn copy_relocation_copies_bytes() {
let mut loader = Loader::new();
let helper_output = write_test_dylib(
&[],
&[SymbolDesc::global_object(
COPY_SOURCE_NAME,
&[0x10, 0x20, 0x30, 0x40, 0x50, 0x60],
)],
);
let helper = load_relocated_dylib(&mut loader, "libcopy_source.so", &helper_output);
let consumer_output = write_test_dylib(
&[RelocEntry::copy(COPY_SOURCE_NAME, Arch::current())],
&[SymbolDesc::undefined_object(COPY_SOURCE_NAME).with_size(6)],
);
let relocated = loader
.load_dylib(ElfBinary::new("copy_consumer.so", &consumer_output.data))
.expect("failed to load copy consumer")
.relocator()
.scope(&[helper.clone()])
.relocate()
.expect("failed to relocate copy consumer");
let copy = relocation_for_symbol(&consumer_output, REL_COPY, COPY_SOURCE_NAME);
assert_eq!(copy.section, SectionKind::Data);
let deps = relocated.deps().collect::<Vec<_>>();
assert_eq!(deps.len(), 1, "expected one retained dependency");
assert_eq!(deps[0].name(), helper.name());
unsafe {
let src = helper
.get::<u8>(COPY_SOURCE_NAME)
.expect("missing copy source")
.into_raw();
let dst = slot_address(&relocated, copy) as *const u8;
assert_eq!(
std::slice::from_raw_parts(dst, copy.sym_size as usize),
std::slice::from_raw_parts(src as *const u8, copy.sym_size as usize)
);
}
}
#[test]
fn relative_relocation_uses_recorded_addend() {
let output = write_test_dylib(&[RelocEntry::relative(Arch::current())], &[]);
let relocated = Loader::new()
.load_dylib(ElfBinary::new("relative.so", &output.data))
.expect("failed to load relative test dylib")
.relocator()
.relocate()
.expect("failed to relocate relative test dylib");
let relative = anonymous_relocations(&output, REL_RELATIVE)[0];
assert_eq!(relative.section, SectionKind::Got);
assert_eq!(
slot_word(&relocated, relative),
(relocated.base() as i64 + relative.addend) as u64
);
assert!(
relocated.deps().is_empty(),
"relative relocations should not retain dependencies"
);
}
#[test]
fn irelative_relocation_uses_ifunc_resolver() {
let resolver_offset = 0x88;
let output = write_test_dylib_with_config(
ElfWriterConfig::default().with_ifunc_resolver_val(resolver_offset),
&[RelocEntry::irelative(Arch::current())],
&[],
);
let relocated = Loader::new()
.load_dylib(ElfBinary::new("irelative.so", &output.data))
.expect("failed to load irelative test dylib")
.relocator()
.relocate()
.expect("failed to relocate irelative test dylib");
let irelative = anonymous_relocations(&output, REL_IRELATIVE)[0];
assert_eq!(irelative.section, SectionKind::Got);
assert_eq!(
slot_word(&relocated, irelative),
relocated.base() as u64 + resolver_offset
);
assert!(
relocated.deps().is_empty(),
"irelative relocations should not retain dependencies"
);
}
#[test]
fn copy_relocations_keep_symbols_separate() {
let mut loader = Loader::new();
let copy_sources = [
(COPY_SOURCE_NAME, &[0x10, 0x20, 0x30, 0x40][..]),
(COPY_SOURCE_NAME2, &[0x55, 0x66, 0x77, 0x88, 0x99][..]),
];
let helper_symbols: Vec<_> = copy_sources
.iter()
.map(|(name, bytes)| SymbolDesc::global_object(*name, bytes))
.collect();
let helper_output = write_test_dylib(&[], &helper_symbols);
let helper = load_relocated_dylib(&mut loader, "libcopy_sources.so", &helper_output);
let consumer_relocations: Vec<_> = copy_sources
.iter()
.map(|(name, _)| RelocEntry::copy(*name, Arch::current()))
.collect();
let consumer_symbols: Vec<_> = copy_sources
.iter()
.map(|(name, bytes)| SymbolDesc::undefined_object(*name).with_size(bytes.len() as u64))
.collect();
let consumer_output = write_test_dylib(&consumer_relocations, &consumer_symbols);
let relocated = loader
.load_dylib(ElfBinary::new(
"copy_consumer_many.so",
&consumer_output.data,
))
.expect("failed to load copy consumer")
.relocator()
.scope(&[helper.clone()])
.relocate()
.expect("failed to relocate copy consumer");
let deps = relocated.deps().collect::<Vec<_>>();
assert_eq!(deps.len(), 1, "expected one retained dependency");
assert_eq!(deps[0].name(), helper.name());
for (name, expected_bytes) in copy_sources {
let relocation = relocation_for_symbol(&consumer_output, REL_COPY, name);
assert_eq!(relocation.section, SectionKind::Data);
unsafe {
let copied = std::slice::from_raw_parts(
slot_address(&relocated, relocation) as *const u8,
relocation.sym_size as usize,
);
assert_eq!(copied, expected_bytes, "REL_COPY mismatch for {name}");
}
}
}
#[test]
fn relative_relocations_apply_to_all_slots() {
let output = write_test_dylib(
&[
RelocEntry::relative(Arch::current()),
RelocEntry::relative(Arch::current()),
RelocEntry::relative(Arch::current()),
],
&[],
);
let relocated = Loader::new()
.load_dylib(ElfBinary::new("relative_many.so", &output.data))
.expect("failed to load relative test dylib")
.relocator()
.relocate()
.expect("failed to relocate relative test dylib");
let relatives = anonymous_relocations(&output, REL_RELATIVE);
assert_eq!(relatives.len(), 3, "expected three relative relocations");
for relative in relatives {
assert_eq!(relative.section, SectionKind::Got);
assert_eq!(
slot_word(&relocated, relative),
(relocated.base() as i64 + relative.addend) as u64
);
}
assert!(
relocated.deps().is_empty(),
"relative relocations should not retain dependencies"
);
}
#[test]
fn irelative_relocations_apply_to_all_slots() {
let resolver_offset = 0x88;
let output = write_test_dylib_with_config(
ElfWriterConfig::default().with_ifunc_resolver_val(resolver_offset),
&[
RelocEntry::irelative(Arch::current()),
RelocEntry::irelative(Arch::current()),
],
&[],
);
let relocated = Loader::new()
.load_dylib(ElfBinary::new("irelative_many.so", &output.data))
.expect("failed to load irelative test dylib")
.relocator()
.relocate()
.expect("failed to relocate irelative test dylib");
let irelatives = anonymous_relocations(&output, REL_IRELATIVE);
assert_eq!(irelatives.len(), 2, "expected two irelative relocations");
for irelative in irelatives {
assert_eq!(irelative.section, SectionKind::Got);
assert_eq!(
slot_word(&relocated, irelative),
relocated.base() as u64 + resolver_offset
);
}
assert!(
relocated.deps().is_empty(),
"irelative relocations should not retain dependencies"
);
}