Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement handling of R_BPF_64_64 relocations used in ld_imm64 #200

Merged
merged 1 commit into from
Jul 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 54 additions & 12 deletions src/elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,12 @@ const BYTE_LENGTH_IMMEIDATE: usize = 4;
enum BpfRelocationType {
/// No relocation, placeholder
R_Bpf_None = 0,
/// R_BPF_64_64 relocation type is used for ld_imm64 instruction.
/// The actual to-be-relocated data (0 or section offset) is
/// stored at r_offset + 4 and the read/write data bitsize is 32
/// (4 bytes). The relocation can be resolved with the symbol
/// value plus implicit addend.
R_Bpf_64_64 = 1,
Lichtso marked this conversation as resolved.
Show resolved Hide resolved
/// 64 bit relocation of a ldxdw instruction.
/// The ldxdw instruction occupies two instruction slots. The 64-bit address
/// to load from is split into the 32-bit imm field of each slot. The first
Expand All @@ -182,6 +188,7 @@ impl BpfRelocationType {
fn from_x86_relocation_type(from: u32) -> Option<BpfRelocationType> {
match from {
R_X86_64_NONE => Some(BpfRelocationType::R_Bpf_None),
R_X86_64_64 => Some(BpfRelocationType::R_Bpf_64_64),
R_X86_64_RELATIVE => Some(BpfRelocationType::R_Bpf_64_Relative),
R_X86_64_32 => Some(BpfRelocationType::R_Bpf_64_32),
_ => None,
Expand Down Expand Up @@ -288,19 +295,20 @@ impl<E: UserDefinedError, I: InstructionMeter> Executable<E, I> for EBpfElf<E, I
let mut name = "Unknown";
if let Ok(elf) = Elf::parse(self.elf_bytes.as_slice()) {
for relocation in &elf.dynrels {
if let Some(BpfRelocationType::R_Bpf_64_32) =
BpfRelocationType::from_x86_relocation_type(relocation.r_type)
{
if relocation.r_offset as usize == file_offset {
let sym = elf
.dynsyms
.get(relocation.r_sym)
.ok_or(ElfError::UnknownSymbol(relocation.r_sym))?;
name = elf
.dynstrtab
.get_at(sym.st_name)
.ok_or(ElfError::UnknownSymbol(sym.st_name))?;
match BpfRelocationType::from_x86_relocation_type(relocation.r_type) {
Some(BpfRelocationType::R_Bpf_64_32) | Some(BpfRelocationType::R_Bpf_64_64) => {
if relocation.r_offset as usize == file_offset {
let sym = elf
.dynsyms
.get(relocation.r_sym)
.ok_or(ElfError::UnknownSymbol(relocation.r_sym))?;
name = elf
.dynstrtab
.get_at(sym.st_name)
.ok_or(ElfError::UnknownSymbol(sym.st_name))?;
}
}
_ => (),
}
}
}
Expand Down Expand Up @@ -568,6 +576,30 @@ impl<'a, E: UserDefinedError, I: InstructionMeter> EBpfElf<E, I> {
// Offset of the immediate field
let imm_offset = r_offset.saturating_add(BYTE_OFFSET_IMMEDIATE);
match BpfRelocationType::from_x86_relocation_type(relocation.r_type) {
Some(BpfRelocationType::R_Bpf_64_64) => {
// Read the instruction's immediate field which contains virtual
// address to convert to physical
let checked_slice = elf_bytes
.get(imm_offset..imm_offset.saturating_add(BYTE_LENGTH_IMMEIDATE))
.ok_or(ElfError::OutOfBounds)?;
let refd_va = LittleEndian::read_u32(checked_slice) as u64;
// final "physical address" from the VM's perspetive is rooted at `MM_PROGRAM_START`
let refd_pa = ebpf::MM_PROGRAM_START.saturating_add(refd_va);

// The .text section has an unresolved load symbol instruction.
let sym = elf
.dynsyms
.get(relocation.r_sym)
.ok_or(ElfError::UnknownSymbol(relocation.r_sym))?;
if !text_section.vm_range().contains(&(sym.st_value as usize)) {
return Err(ElfError::OutOfBounds);
}
let addr = (sym.st_value + refd_pa) as u32;
let mut checked_slice = elf_bytes
.get_mut(imm_offset..imm_offset.saturating_add(BYTE_LENGTH_IMMEIDATE))
.ok_or(ElfError::OutOfBounds)?;
LittleEndian::write_u32(&mut checked_slice, addr);
}
Some(BpfRelocationType::R_Bpf_64_Relative) => {
// Raw relocation between sections. The instruction being relocated contains
// the virtual address that it needs turned into a physical address. Read it,
Expand Down Expand Up @@ -1048,4 +1080,14 @@ mod test {
},
);
}

#[test]
fn test_relocs() {
let mut file = File::open("tests/elfs/reloc.so").expect("file open failed");
let mut elf_bytes = Vec::new();
file.read_to_end(&mut elf_bytes)
.expect("failed to read elf file");
ElfExecutable::load(Config::default(), &elf_bytes, syscall_registry())
.expect("validation failed");
}
}
6 changes: 5 additions & 1 deletion tests/elfs/elfs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,14 @@ rm syscall.o
"$LLVM_DIR"ld.lld -z notext -shared --Bdynamic -entry entrypoint -o relative_call.so relative_call.o
rm relative_call.o

"$LLVM_DIR"clang -Werror -target bpf -O2 -fno-builtin -fPIC -o reloc.o -c reloc.c
"$LLVM_DIR"ld.lld -script elf.ld -z notext -shared --Bdynamic -entry entrypoint -o reloc.so reloc.o
rm reloc.o

"$LLVM_DIR"clang -Werror -target bpf -O2 -fno-builtin -fPIC -o scratch_registers.o -c scratch_registers.c
"$LLVM_DIR"ld.lld -z notext -shared --Bdynamic -entry entrypoint -o scratch_registers.so scratch_registers.o
rm scratch_registers.o

"$LLVM_DIR"clang -Werror -target bpf -O2 -fno-builtin -fPIC -o pass_stack_reference.o -c pass_stack_reference.c
"$LLVM_DIR"ld.lld -z notext -shared --Bdynamic -entry entrypoint -o pass_stack_reference.so pass_stack_reference.o
rm pass_stack_reference.o
rm pass_stack_reference.o
11 changes: 11 additions & 0 deletions tests/elfs/reloc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* @brief a program to test R_BPF_64_64 relocation handling
*/

typedef unsigned long int uint64_t;
typedef unsigned char uint8_t;

extern uint64_t entrypoint(const uint8_t *input) {
uint64_t (*ptr)(const uint8_t *) = entrypoint;
Lichtso marked this conversation as resolved.
Show resolved Hide resolved
return (uint64_t) ptr;
}
Binary file added tests/elfs/reloc.so
Binary file not shown.