From 2e9f65efa462ec761bb7e3c007f83dd8346589db Mon Sep 17 00:00:00 2001 From: Dmitri Makarov Date: Mon, 19 Jul 2021 11:27:16 -0700 Subject: [PATCH] Implement handling of R_BPF_64_64 relocations used in ld_imm64 (#200) --- src/elf.rs | 66 ++++++++++++++++++++++++++++++++++++-------- tests/elfs/elfs.sh | 6 +++- tests/elfs/reloc.c | 11 ++++++++ tests/elfs/reloc.so | Bin 0 -> 1680 bytes 4 files changed, 70 insertions(+), 13 deletions(-) create mode 100644 tests/elfs/reloc.c create mode 100755 tests/elfs/reloc.so diff --git a/src/elf.rs b/src/elf.rs index 50259820..70af14ff 100644 --- a/src/elf.rs +++ b/src/elf.rs @@ -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, /// 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 @@ -182,6 +188,7 @@ impl BpfRelocationType { fn from_x86_relocation_type(from: u32) -> Option { 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, @@ -288,19 +295,20 @@ impl Executable for EBpfElf { + 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))?; + } } + _ => (), } } } @@ -568,6 +576,30 @@ impl<'a, E: UserDefinedError, I: InstructionMeter> EBpfElf { // 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, @@ -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"); + } } diff --git a/tests/elfs/elfs.sh b/tests/elfs/elfs.sh index 73e51d54..23c235ba 100755 --- a/tests/elfs/elfs.sh +++ b/tests/elfs/elfs.sh @@ -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 \ No newline at end of file +rm pass_stack_reference.o diff --git a/tests/elfs/reloc.c b/tests/elfs/reloc.c new file mode 100644 index 00000000..cb4a66fe --- /dev/null +++ b/tests/elfs/reloc.c @@ -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; + return (uint64_t) ptr; +} diff --git a/tests/elfs/reloc.so b/tests/elfs/reloc.so new file mode 100755 index 0000000000000000000000000000000000000000..94501f9c3111403c60215c2bf2ac4f8eaa3aba5a GIT binary patch literal 1680 zcmbVL!EVz)5S^w4O5sonst^*$M?hjpoV0d(3Q$l%fdk@#gaoebCZTa$Ic`Nxh(F-M zfA9&p^CKML0Nfg8yz?yALPF|F_M3S-Z)bLP_v7QOdKuJA?img17)6-7r12RDEOGq5P8*Ddpr^0>ucJdN>#7G(PiC-PP;rJlVww+#Y$7#8yXGA#1=s3A|n5FOIFgG0FM1I@dkKCZ`wS9ln?Sx+BHodqRcyZ9$ z+~`F69dE;LckWa(p0=f)CHgr(%@SP`qQMo{3ckqnKl@|y_(M)6nvR2H7#7B4npn&; zGX3$983dC7nJgYj7^%x!Ot`sZI6M#b7zdL)vzA3-MH-s0dIoQIUcY$o{OO~5-DmZ2 zPPmsiKVW$eG{JH$(FOKJTRwO6O>w62xR=oDvo3Crya8O-8FY%xie4z_QJ0|Ph92(* zhD%4`-C2S z1a7fb`hq^OS&Us>un$5}+gkSFA@j&x4gIR*J0<0eqz3FCbv;vF!%A4yg5Q*kHIN_l SD>kq-xWT@pG#H literal 0 HcmV?d00001