summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRusty Wagner <rusty.wagner@gmail.com>2024-01-03 17:03:16 -0700
committerRusty Wagner <rusty.wagner@gmail.com>2024-01-04 12:42:14 -0700
commit5a2d058c381a5fa0889464772b7399df2f39ebe8 (patch)
tree69e203f0ba2a7dd65fc842933027e341b28940d8
parent7e8de24c479672261b8a5c9911cac8800bad180b (diff)
Add RISC-V relocations required by Linux kernel modules
-rw-r--r--arch/riscv/src/lib.rs377
1 files changed, 377 insertions, 0 deletions
diff --git a/arch/riscv/src/lib.rs b/arch/riscv/src/lib.rs
index 20136114..d8f6cff5 100644
--- a/arch/riscv/src/lib.rs
+++ b/arch/riscv/src/lib.rs
@@ -8,6 +8,7 @@ use std::borrow::Cow;
use std::fmt;
use std::marker::PhantomData;
+use binaryninja::relocation::{Relocation, RelocationHandlerExt};
use binaryninja::{
add_optional_plugin_dependency, architecture,
architecture::{
@@ -2149,6 +2150,75 @@ impl<D: 'static + RiscVDisassembler + Send + Sync> RiscVELFRelocationHandler<D>
const R_RISCV_RELATIVE: u64 = 3;
const R_RISCV_COPY: u64 = 4;
const R_RISCV_JUMP_SLOT: u64 = 5;
+ const R_RISCV_BRANCH: u64 = 16;
+ const R_RISCV_JAL: u64 = 17;
+ const R_RISCV_CALL: u64 = 18;
+ const R_RISCV_CALL_PLT: u64 = 19;
+ const R_RISCV_PCREL_HI20: u64 = 23;
+ const R_RISCV_PCREL_LO12_I: u64 = 24;
+ const R_RISCV_PCREL_LO12_S: u64 = 25;
+ const R_RISCV_HI20: u64 = 26;
+ const R_RISCV_LO12_I: u64 = 27;
+ const R_RISCV_LO12_S: u64 = 28;
+ const R_RISCV_ADD8: u64 = 33;
+ const R_RISCV_ADD16: u64 = 34;
+ const R_RISCV_ADD32: u64 = 35;
+ const R_RISCV_ADD64: u64 = 36;
+ const R_RISCV_SUB8: u64 = 37;
+ const R_RISCV_SUB16: u64 = 38;
+ const R_RISCV_SUB32: u64 = 39;
+ const R_RISCV_SUB64: u64 = 40;
+ const R_RISCV_RVC_BRANCH: u64 = 44;
+ const R_RISCV_RVC_JUMP: u64 = 45;
+
+ fn replace_b_imm(opcode: u32, imm: u32) -> u32 {
+ (opcode & 0x01fff07f)
+ | (((imm >> 1) & 0xf) << 8)
+ | (((imm >> 5) & 0x3f) << 25)
+ | (((imm >> 11) & 1) << 7)
+ | (((imm >> 12) & 1) << 31)
+ }
+
+ fn replace_j_imm(opcode: u32, imm: u32) -> u32 {
+ (opcode & 0x00000fff)
+ | (((imm >> 1) & 0x3ff) << 21)
+ | (((imm >> 11) & 1) << 20)
+ | (((imm >> 12) & 0xff) << 12)
+ | (((imm >> 20) & 1) << 31)
+ }
+
+ fn replace_u_imm(opcode: u32, imm: u32) -> u32 {
+ (opcode & 0x00000fff) | (imm << 12)
+ }
+
+ fn replace_i_imm(opcode: u32, imm: u32) -> u32 {
+ (opcode & 0x000fffff) | (imm << 20)
+ }
+
+ fn replace_s_imm(opcode: u32, imm: u32) -> u32 {
+ (opcode & 0x01fff07f) | ((imm & 0x1f) << 7) | (((imm >> 5) & 0x7f) << 25)
+ }
+
+ fn replace_cb_imm(opcode: u16, imm: u16) -> u16 {
+ (opcode & 0xe383)
+ | (((imm >> 1) & 3) << 3)
+ | (((imm >> 3) & 3) << 10)
+ | (((imm >> 5) & 1) << 2)
+ | (((imm >> 6) & 3) << 5)
+ | (((imm >> 8) & 1) << 12)
+ }
+
+ fn replace_cj_imm(opcode: u16, imm: u16) -> u16 {
+ (opcode & 0xe003)
+ | (((imm >> 1) & 7) << 3)
+ | (((imm >> 4) & 1) << 11)
+ | (((imm >> 5) & 1) << 2)
+ | (((imm >> 6) & 1) << 7)
+ | (((imm >> 7) & 1) << 6)
+ | (((imm >> 8) & 3) << 9)
+ | (((imm >> 10) & 1) << 8)
+ | (((imm >> 11) & 1) << 12)
+ }
}
impl<D: 'static + RiscVDisassembler + Send + Sync> RelocationHandler
@@ -2195,6 +2265,62 @@ impl<D: 'static + RiscVDisassembler + Send + Sync> RelocationHandler
reloc.type_ = RelocationType::ELFJumpSlotRelocationType;
reloc.size = <D::RegFile as RegFile>::Int::width();
}
+ Self::R_RISCV_BRANCH
+ | Self::R_RISCV_JAL
+ | Self::R_RISCV_PCREL_HI20
+ | Self::R_RISCV_PCREL_LO12_I
+ | Self::R_RISCV_PCREL_LO12_S
+ | Self::R_RISCV_HI20
+ | Self::R_RISCV_LO12_I
+ | Self::R_RISCV_LO12_S => {
+ reloc.pc_relative = true;
+ reloc.base_relative = false;
+ reloc.has_sign = false;
+ reloc.size = 4;
+ reloc.truncate_size = 4;
+ }
+ Self::R_RISCV_CALL | Self::R_RISCV_CALL_PLT => {
+ reloc.pc_relative = true;
+ reloc.base_relative = false;
+ reloc.has_sign = false;
+ reloc.size = 8;
+ reloc.truncate_size = 8;
+ }
+ Self::R_RISCV_ADD8 | Self::R_RISCV_SUB8 => {
+ reloc.pc_relative = false;
+ reloc.base_relative = false;
+ reloc.has_sign = reloc.native_type == Self::R_RISCV_SUB8;
+ reloc.size = 1;
+ reloc.truncate_size = 1;
+ }
+ Self::R_RISCV_ADD16 | Self::R_RISCV_SUB16 => {
+ reloc.pc_relative = false;
+ reloc.base_relative = false;
+ reloc.has_sign = reloc.native_type == Self::R_RISCV_SUB16;
+ reloc.size = 2;
+ reloc.truncate_size = 2;
+ }
+ Self::R_RISCV_ADD32 | Self::R_RISCV_SUB32 => {
+ reloc.pc_relative = false;
+ reloc.base_relative = false;
+ reloc.has_sign = reloc.native_type == Self::R_RISCV_SUB32;
+ reloc.size = 4;
+ reloc.truncate_size = 4;
+ }
+ Self::R_RISCV_ADD64 | Self::R_RISCV_SUB64 => {
+ reloc.pc_relative = false;
+ reloc.base_relative = false;
+ reloc.has_sign = reloc.native_type == Self::R_RISCV_SUB64;
+ reloc.size = 8;
+ reloc.truncate_size = 8;
+ }
+ Self::R_RISCV_RVC_BRANCH | Self::R_RISCV_RVC_JUMP => {
+ reloc.pc_relative = true;
+ reloc.base_relative = false;
+ reloc.has_sign = false;
+ reloc.size = 2;
+ reloc.truncate_size = 2;
+ }
_ => {
reloc.type_ = RelocationType::UnhandledRelocation;
log::warn!(
@@ -2208,6 +2334,257 @@ impl<D: 'static + RiscVDisassembler + Send + Sync> RelocationHandler
true
}
+ fn apply_relocation(
+ &self,
+ bv: &BinaryView,
+ arch: &CoreArchitecture,
+ reloc: &Relocation,
+ dest: &mut [u8],
+ ) -> bool {
+ let info = reloc.info();
+ match info.native_type {
+ Self::R_RISCV_BRANCH => {
+ let opcode = u32::from_le_bytes(match dest[0..4].try_into() {
+ Ok(bytes) => bytes,
+ Err(_) => return false,
+ });
+ let offset = reloc
+ .target()
+ .wrapping_add(info.addend as u64)
+ .wrapping_sub(info.address) as u32;
+ let opcode = Self::replace_b_imm(opcode, offset);
+ dest[0..4].copy_from_slice(&opcode.to_le_bytes());
+ true
+ }
+ Self::R_RISCV_JAL => {
+ let opcode = u32::from_le_bytes(match dest[0..4].try_into() {
+ Ok(bytes) => bytes,
+ Err(_) => return false,
+ });
+ let offset = reloc
+ .target()
+ .wrapping_add(info.addend as u64)
+ .wrapping_sub(info.address) as u32;
+ let opcode = Self::replace_j_imm(opcode, offset);
+ dest[0..4].copy_from_slice(&opcode.to_le_bytes());
+ true
+ }
+ Self::R_RISCV_CALL | Self::R_RISCV_CALL_PLT => {
+ let u_opcode = u32::from_le_bytes(match dest[0..4].try_into() {
+ Ok(bytes) => bytes,
+ Err(_) => return false,
+ });
+ let i_opcode = u32::from_le_bytes(match dest[4..8].try_into() {
+ Ok(bytes) => bytes,
+ Err(_) => return false,
+ });
+ let offset = reloc
+ .target()
+ .wrapping_add(info.addend as u64)
+ .wrapping_sub(info.address) as u32;
+ let high_offset = (offset.wrapping_add(0x800)) >> 12;
+ let low_offset = offset & 0xfff;
+ let u_opcode = Self::replace_u_imm(u_opcode, high_offset);
+ let i_opcode = Self::replace_i_imm(i_opcode, low_offset);
+ dest[0..4].copy_from_slice(&u_opcode.to_le_bytes());
+ dest[4..8].copy_from_slice(&i_opcode.to_le_bytes());
+ true
+ }
+ Self::R_RISCV_PCREL_HI20 => {
+ let opcode = u32::from_le_bytes(match dest[0..4].try_into() {
+ Ok(bytes) => bytes,
+ Err(_) => return false,
+ });
+ let offset = reloc
+ .target()
+ .wrapping_add(info.addend as u64)
+ .wrapping_sub(info.address) as u32;
+ let high_offset = (offset.wrapping_add(0x800)) >> 12;
+ let opcode = Self::replace_u_imm(opcode, high_offset);
+ dest[0..4].copy_from_slice(&opcode.to_le_bytes());
+ true
+ }
+ Self::R_RISCV_PCREL_LO12_I | Self::R_RISCV_PCREL_LO12_S => {
+ let opcode = u32::from_le_bytes(match dest[0..4].try_into() {
+ Ok(bytes) => bytes,
+ Err(_) => return false,
+ });
+
+ // Actual target symbol is on the associated R_RISCV_PCREL_HI20 relocation, which
+ // is pointed to by `reloc.target()`.
+ let target = match bv
+ .get_relocations_at(reloc.target())
+ .iter()
+ .find(|r| r.info().native_type == Self::R_RISCV_PCREL_HI20)
+ {
+ Some(target) => target,
+ None => return false,
+ };
+ let target = target.target().wrapping_add(target.info().addend as u64);
+
+ let offset = target.wrapping_sub(reloc.target()) as u32;
+ let low_offset = offset & 0xfff;
+
+ let opcode = match info.native_type {
+ Self::R_RISCV_PCREL_LO12_I => Self::replace_i_imm(opcode, low_offset),
+ Self::R_RISCV_PCREL_LO12_S => Self::replace_s_imm(opcode, low_offset),
+ _ => return false,
+ };
+
+ dest[0..4].copy_from_slice(&opcode.to_le_bytes());
+ true
+ }
+ Self::R_RISCV_HI20 => {
+ let opcode = u32::from_le_bytes(match dest[0..4].try_into() {
+ Ok(bytes) => bytes,
+ Err(_) => return false,
+ });
+ let addr = reloc.target().wrapping_add(info.addend as u64) as u32;
+ let high_addr = (addr.wrapping_add(0x800)) >> 12;
+ let opcode = Self::replace_u_imm(opcode, high_addr);
+ dest[0..4].copy_from_slice(&opcode.to_le_bytes());
+ true
+ }
+ Self::R_RISCV_LO12_I => {
+ let opcode = u32::from_le_bytes(match dest[0..4].try_into() {
+ Ok(bytes) => bytes,
+ Err(_) => return false,
+ });
+ let addr = reloc.target().wrapping_add(info.addend as u64) as u32;
+ let low_addr = addr & 0xfff;
+ let opcode = Self::replace_i_imm(opcode, low_addr);
+ dest[0..4].copy_from_slice(&opcode.to_le_bytes());
+ true
+ }
+ Self::R_RISCV_LO12_S => {
+ let opcode = u32::from_le_bytes(match dest[0..4].try_into() {
+ Ok(bytes) => bytes,
+ Err(_) => return false,
+ });
+ let addr = reloc.target().wrapping_add(info.addend as u64) as u32;
+ let low_addr = addr & 0xfff;
+ let opcode = Self::replace_s_imm(opcode, low_addr);
+ dest[0..4].copy_from_slice(&opcode.to_le_bytes());
+ true
+ }
+ Self::R_RISCV_ADD8 => {
+ let value = u8::from_le_bytes(match dest[0..1].try_into() {
+ Ok(bytes) => bytes,
+ Err(_) => return false,
+ });
+ let value = value
+ .wrapping_add(reloc.target() as u8)
+ .wrapping_add(info.addend as u8);
+ dest[0..1].copy_from_slice(&value.to_le_bytes());
+ true
+ }
+ Self::R_RISCV_ADD16 => {
+ let value = u16::from_le_bytes(match dest[0..2].try_into() {
+ Ok(bytes) => bytes,
+ Err(_) => return false,
+ });
+ let value = value
+ .wrapping_add(reloc.target() as u16)
+ .wrapping_add(info.addend as u16);
+ dest[0..2].copy_from_slice(&value.to_le_bytes());
+ true
+ }
+ Self::R_RISCV_ADD32 => {
+ let value = u32::from_le_bytes(match dest[0..4].try_into() {
+ Ok(bytes) => bytes,
+ Err(_) => return false,
+ });
+ let value = value
+ .wrapping_add(reloc.target() as u32)
+ .wrapping_add(info.addend as u32);
+ dest[0..4].copy_from_slice(&value.to_le_bytes());
+ true
+ }
+ Self::R_RISCV_ADD64 => {
+ let value = u64::from_le_bytes(match dest[0..8].try_into() {
+ Ok(bytes) => bytes,
+ Err(_) => return false,
+ });
+ let value = value
+ .wrapping_add(reloc.target())
+ .wrapping_add(info.addend as u64);
+ dest[0..8].copy_from_slice(&value.to_le_bytes());
+ true
+ }
+ Self::R_RISCV_SUB8 => {
+ let value = u8::from_le_bytes(match dest[0..1].try_into() {
+ Ok(bytes) => bytes,
+ Err(_) => return false,
+ });
+ let value = value
+ .wrapping_sub(reloc.target() as u8)
+ .wrapping_sub(info.addend as u8);
+ dest[0..1].copy_from_slice(&value.to_le_bytes());
+ true
+ }
+ Self::R_RISCV_SUB16 => {
+ let value = u16::from_le_bytes(match dest[0..2].try_into() {
+ Ok(bytes) => bytes,
+ Err(_) => return false,
+ });
+ let value = value
+ .wrapping_sub(reloc.target() as u16)
+ .wrapping_sub(info.addend as u16);
+ dest[0..2].copy_from_slice(&value.to_le_bytes());
+ true
+ }
+ Self::R_RISCV_SUB32 => {
+ let value = u32::from_le_bytes(match dest[0..4].try_into() {
+ Ok(bytes) => bytes,
+ Err(_) => return false,
+ });
+ let value = value
+ .wrapping_sub(reloc.target() as u32)
+ .wrapping_sub(info.addend as u32);
+ dest[0..4].copy_from_slice(&value.to_le_bytes());
+ true
+ }
+ Self::R_RISCV_SUB64 => {
+ let value = u64::from_le_bytes(match dest[0..8].try_into() {
+ Ok(bytes) => bytes,
+ Err(_) => return false,
+ });
+ let value = value
+ .wrapping_sub(reloc.target())
+ .wrapping_sub(info.addend as u64);
+ dest[0..8].copy_from_slice(&value.to_le_bytes());
+ true
+ }
+ Self::R_RISCV_RVC_BRANCH => {
+ let opcode = u16::from_le_bytes(match dest[0..2].try_into() {
+ Ok(bytes) => bytes,
+ Err(_) => return false,
+ });
+ let offset = reloc
+ .target()
+ .wrapping_add(info.addend as u64)
+ .wrapping_sub(info.address) as u16;
+ let opcode = Self::replace_cb_imm(opcode, offset);
+ dest[0..2].copy_from_slice(&opcode.to_le_bytes());
+ true
+ }
+ Self::R_RISCV_RVC_JUMP => {
+ let opcode = u16::from_le_bytes(match dest[0..2].try_into() {
+ Ok(bytes) => bytes,
+ Err(_) => return false,
+ });
+ let offset = reloc
+ .target()
+ .wrapping_add(info.addend as u64)
+ .wrapping_sub(info.address) as u16;
+ let opcode = Self::replace_cj_imm(opcode, offset);
+ dest[0..2].copy_from_slice(&opcode.to_le_bytes());
+ true
+ }
+ _ => self.default_apply_relocation(bv, arch, reloc, dest),
+ }
+ }
+
fn handle(&self) -> Self::Handle {
self.custom_handle
}