include!("constants_relocation.rs");
macro_rules! elf_reloc {
($size:ident, $isize:ty) => {
use core::fmt;
#[cfg(feature = "alloc")]
use scroll::{Pread, Pwrite, SizeWith};
#[repr(C)]
#[derive(Clone, Copy, PartialEq, Default)]
#[cfg_attr(feature = "alloc", derive(Pread, Pwrite, SizeWith))]
pub struct Rela {
pub r_offset: $size,
pub r_info: $size,
pub r_addend: $isize,
}
#[repr(C)]
#[derive(Clone, PartialEq, Default)]
#[cfg_attr(feature = "alloc", derive(Pread, Pwrite, SizeWith))]
pub struct Rel {
pub r_offset: $size,
pub r_info: $size,
}
use plain;
unsafe impl plain::Plain for Rela {}
unsafe impl plain::Plain for Rel {}
impl fmt::Debug for Rela {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let sym = r_sym(self.r_info);
let typ = r_type(self.r_info);
f.debug_struct("Rela")
.field("r_offset", &format_args!("{:x}", self.r_offset))
.field("r_info", &format_args!("{:x}", self.r_info))
.field("r_addend", &format_args!("{:x}", self.r_addend))
.field("r_typ", &typ)
.field("r_sym", &sym)
.finish()
}
}
impl fmt::Debug for Rel {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let sym = r_sym(self.r_info);
let typ = r_type(self.r_info);
f.debug_struct("Rel")
.field("r_offset", &format_args!("{:x}", self.r_offset))
.field("r_info", &format_args!("{:x}", self.r_info))
.field("r_typ", &typ)
.field("r_sym", &sym)
.finish()
}
}
};
}
macro_rules! elf_rela_std_impl {
($size:ident, $isize:ty) => {
if_alloc! {
use core::slice;
if_std! {
use crate::error::Result;
use std::fs::File;
use std::io::{Read, Seek};
use std::io::SeekFrom::Start;
}
impl From<Rela> for Reloc {
fn from(rela: Rela) -> Self {
Reloc {
r_offset: u64::from(rela.r_offset),
r_addend: Some(i64::from(rela.r_addend)),
r_sym: r_sym(rela.r_info) as usize,
r_type: r_type(rela.r_info),
}
}
}
impl From<Rel> for Reloc {
fn from(rel: Rel) -> Self {
Reloc {
r_offset: u64::from(rel.r_offset),
r_addend: None,
r_sym: r_sym(rel.r_info) as usize,
r_type: r_type(rel.r_info),
}
}
}
impl From<Reloc> for Rela {
fn from(rela: Reloc) -> Self {
let r_info = r_info(rela.r_sym as $size, $size::from(rela.r_type));
Rela {
r_offset: rela.r_offset as $size,
r_info: r_info,
r_addend: rela.r_addend.unwrap_or(0) as $isize,
}
}
}
impl From<Reloc> for Rel {
fn from(rel: Reloc) -> Self {
let r_info = r_info(rel.r_sym as $size, $size::from(rel.r_type));
Rel {
r_offset: rel.r_offset as $size,
r_info: r_info,
}
}
}
pub unsafe fn from_raw_rela<'a>(ptr: *const Rela, size: usize) -> &'a [Rela] { unsafe {
slice::from_raw_parts(ptr, size / SIZEOF_RELA)
}}
pub unsafe fn from_raw_rel<'a>(ptr: *const Rel, size: usize) -> &'a [Rel] { unsafe {
slice::from_raw_parts(ptr, size / SIZEOF_REL)
}}
#[cfg(feature = "std")]
pub fn from_fd(fd: &mut File, offset: usize, size: usize) -> Result<Vec<Rela>> {
let count = size / SIZEOF_RELA;
let mut relocs = vec![Rela::default(); count];
fd.seek(Start(offset as u64))?;
unsafe {
fd.read_exact(plain::as_mut_bytes(&mut *relocs))?;
}
Ok(relocs)
}
} };
}
pub mod reloc32 {
pub use crate::elf::reloc::*;
elf_reloc!(u32, i32);
pub const SIZEOF_RELA: usize = 4 + 4 + 4;
pub const SIZEOF_REL: usize = 4 + 4;
#[inline(always)]
pub fn r_sym(info: u32) -> u32 {
info >> 8
}
#[inline(always)]
pub fn r_type(info: u32) -> u32 {
info & 0xff
}
#[inline(always)]
pub fn r_info(sym: u32, typ: u32) -> u32 {
(sym << 8) + (typ & 0xff)
}
elf_rela_std_impl!(u32, i32);
}
pub mod reloc64 {
pub use crate::elf::reloc::*;
elf_reloc!(u64, i64);
pub const SIZEOF_RELA: usize = 8 + 8 + 8;
pub const SIZEOF_REL: usize = 8 + 8;
#[inline(always)]
pub fn r_sym(info: u64) -> u32 {
(info >> 32) as u32
}
#[inline(always)]
pub fn r_type(info: u64) -> u32 {
(info & 0xffff_ffff) as u32
}
#[inline(always)]
pub fn r_info(sym: u64, typ: u64) -> u64 {
(sym << 32) + typ
}
#[inline(always)]
pub fn mips64el_r_info(info: u64) -> u64 {
(info << 32)
| ((info >> 8) & 0xff000000)
| ((info >> 24) & 0x00ff0000)
| ((info >> 40) & 0x0000ff00)
| ((info >> 56) & 0x000000ff)
}
elf_rela_std_impl!(u64, i64);
}
if_alloc! {
use scroll::{ctx, Pread};
use scroll::ctx::SizeWith;
use core::fmt;
use core::result;
use crate::container::{Ctx, Container};
use alloc::vec::Vec;
#[derive(Clone, Copy, PartialEq, Default)]
pub struct Reloc {
pub r_offset: u64,
pub r_addend: Option<i64>,
pub r_sym: usize,
pub r_type: u32,
}
impl Reloc {
pub fn size(is_rela: bool, ctx: Ctx) -> usize {
use scroll::ctx::SizeWith;
Reloc::size_with(&(is_rela, ctx ))
}
fn fixup_mips64el(&mut self) {
let r_info = ((self.r_sym as u64) << 32) | (self.r_type as u64);
let fixed = reloc64::mips64el_r_info(r_info);
self.r_sym = reloc64::r_sym(fixed) as usize;
self.r_type = reloc64::r_type(fixed);
}
}
type RelocCtx = (bool, Ctx);
impl ctx::SizeWith<RelocCtx> for Reloc {
fn size_with(rctx: &RelocCtx) -> usize {
match rctx.1.container {
Container::Little => {
if rctx.0 { reloc32::SIZEOF_RELA } else { reloc32::SIZEOF_REL }
},
Container::Big => {
if rctx.0 { reloc64::SIZEOF_RELA } else { reloc64::SIZEOF_REL }
}
}
}
}
impl<'a> ctx::TryFromCtx<'a, RelocCtx> for Reloc {
type Error = crate::error::Error;
fn try_from_ctx(bytes: &'a [u8], rctx: RelocCtx) -> result::Result<(Self, usize), Self::Error> {
use scroll::Pread;
let Ctx { container, le } = rctx.1;
let (reloc, size): (Reloc, usize) = match container {
Container::Little => {
if rctx.0 {
(bytes.pread_with::<reloc32::Rela>(0, le)?.into(), reloc32::SIZEOF_RELA)
} else {
(bytes.pread_with::<reloc32::Rel>(0, le)?.into(), reloc32::SIZEOF_REL)
}
},
Container::Big => {
if rctx.0 {
(bytes.pread_with::<reloc64::Rela>(0, le)?.into(), reloc64::SIZEOF_RELA)
} else {
(bytes.pread_with::<reloc64::Rel>(0, le)?.into(), reloc64::SIZEOF_REL)
}
}
};
Ok((reloc, size))
}
}
impl ctx::TryIntoCtx<RelocCtx> for Reloc {
type Error = crate::error::Error;
fn try_into_ctx(self, bytes: &mut [u8], rctx: RelocCtx) -> result::Result<usize, Self::Error> {
use scroll::Pwrite;
let Ctx { container, le } = rctx.1;
match container {
Container::Little => {
if rctx.0 {
let rela: reloc32::Rela = self.into();
Ok(bytes.pwrite_with(rela, 0, le)?)
} else {
let rel: reloc32::Rel = self.into();
Ok(bytes.pwrite_with(rel, 0, le)?)
}
},
Container::Big => {
if rctx.0 {
let rela: reloc64::Rela = self.into();
Ok(bytes.pwrite_with(rela, 0, le)?)
} else {
let rel: reloc64::Rel = self.into();
Ok(bytes.pwrite_with(rel, 0, le)?)
}
},
}
}
}
impl ctx::IntoCtx<RelocCtx> for Reloc {
fn into_ctx(self, bytes: &mut [u8], rctx: RelocCtx) {
use scroll::Pwrite;
bytes.pwrite_with(self, 0, rctx).unwrap();
}
}
impl fmt::Debug for Reloc {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Reloc")
.field("r_offset", &format_args!("{:x}", self.r_offset))
.field("r_addend", &format_args!("{:x}", self.r_addend.unwrap_or(0)))
.field("r_sym", &self.r_sym)
.field("r_type", &self.r_type)
.finish()
}
}
#[derive(Default)]
pub struct RelocSection<'a> {
bytes: &'a [u8],
count: usize,
ctx: RelocCtx,
is_mips64el: bool,
start: usize,
end: usize,
}
impl<'a> fmt::Debug for RelocSection<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let len = self.bytes.len();
fmt.debug_struct("RelocSection")
.field("bytes", &len)
.field("range", &format!("{:#x}..{:#x}", self.start, self.end))
.field("count", &self.count)
.field("Relocations", &self.to_vec())
.finish()
}
}
impl<'a> RelocSection<'a> {
#[cfg(feature = "endian_fd")]
pub fn parse(bytes: &'a [u8], offset: usize, filesz: usize, is_rela: bool, ctx: Ctx) -> crate::error::Result<RelocSection<'a>> {
Self::parse_inner(bytes, offset, filesz, is_rela, ctx, false)
}
#[cfg(feature = "endian_fd")]
pub(crate) fn parse_inner(bytes: &'a [u8], offset: usize, filesz: usize, is_rela: bool, ctx: Ctx, is_mips64el: bool) -> crate::error::Result<RelocSection<'a>> {
let bytes = if filesz != 0 {
bytes.pread_with::<&'a [u8]>(offset, filesz)?
} else {
&[]
};
Ok(RelocSection {
bytes,
count: filesz / Reloc::size(is_rela, ctx),
ctx: (is_rela, ctx),
is_mips64el,
start: offset,
end: offset + filesz,
})
}
#[inline]
pub fn get(&self, index: usize) -> Option<Reloc> {
if index >= self.count {
None
} else {
let mut reloc: Reloc = self.bytes.pread_with(index * Reloc::size_with(&self.ctx), self.ctx).unwrap();
if self.is_mips64el {
reloc.fixup_mips64el();
}
Some(reloc)
}
}
#[inline]
pub fn len(&self) -> usize {
self.count
}
#[inline]
pub fn is_empty(&self) -> bool {
self.count == 0
}
pub fn iter(&self) -> RelocIterator<'a> {
self.into_iter()
}
pub fn to_vec(&self) -> Vec<Reloc> {
self.iter().collect()
}
}
impl<'a, 'b> IntoIterator for &'b RelocSection<'a> {
type Item = <RelocIterator<'a> as Iterator>::Item;
type IntoIter = RelocIterator<'a>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
RelocIterator {
bytes: self.bytes,
offset: 0,
index: 0,
count: self.count,
ctx: self.ctx,
is_mips64el: self.is_mips64el,
}
}
}
pub struct RelocIterator<'a> {
bytes: &'a [u8],
offset: usize,
index: usize,
count: usize,
ctx: RelocCtx,
is_mips64el: bool,
}
impl<'a> fmt::Debug for RelocIterator<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("RelocIterator")
.field("bytes", &"<... redacted ...>")
.field("offset", &self.offset)
.field("index", &self.index)
.field("count", &self.count)
.field("ctx", &self.ctx)
.finish()
}
}
impl<'a> Iterator for RelocIterator<'a> {
type Item = Reloc;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.index >= self.count {
None
} else {
self.index += 1;
let mut reloc: Reloc = self.bytes.gread_with(&mut self.offset, self.ctx).unwrap();
if self.is_mips64el {
reloc.fixup_mips64el();
}
Some(reloc)
}
}
}
impl<'a> ExactSizeIterator for RelocIterator<'a> {
#[inline]
fn len(&self) -> usize {
self.count - self.index
}
}
}
#[cfg(test)]
mod tests {
use super::reloc64;
#[test]
fn test_mips64el_r_info() {
let info: u64 = 0x0312000000000000;
let fixed = reloc64::mips64el_r_info(info);
assert_eq!(reloc64::r_sym(fixed), 0, "r_sym should be 0");
assert_eq!(
reloc64::r_type(fixed),
0x00001203,
"r_type should contain composite MIPS64 type"
);
assert_eq!(
reloc64::r_type(fixed) & 0xFF,
3,
"primary r_type should be R_MIPS_REL32 (3)"
);
assert_eq!(
(reloc64::r_type(fixed) >> 8) & 0xFF,
0x12,
"r_type2 should be R_MIPS_64 (18)"
);
}
#[test]
fn test_mips64el_r_info_with_sym() {
let info: u64 = 0x0312000000000027;
let fixed = reloc64::mips64el_r_info(info);
assert_eq!(reloc64::r_sym(fixed), 0x27, "r_sym should be 0x27 (39)");
assert_eq!(
reloc64::r_type(fixed) & 0xFF,
3,
"primary r_type should be R_MIPS_REL32 (3)"
);
}
#[test]
fn test_mips64el_r_info_zero() {
let info: u64 = 0;
let fixed = reloc64::mips64el_r_info(info);
assert_eq!(reloc64::r_sym(fixed), 0);
assert_eq!(reloc64::r_type(fixed), 0);
}
#[test]
fn test_standard_r_sym_r_type_unchanged() {
let info: u64 = (42u64 << 32) | 7u64;
assert_eq!(reloc64::r_sym(info), 42);
assert_eq!(reloc64::r_type(info), 7);
}
#[test]
#[cfg(feature = "endian_fd")]
fn test_mips64el_reloc_section_parse() {
use super::RelocSection;
use crate::container::{Container, Ctx};
let ctx = Ctx::new(Container::Big, scroll::Endian::Little);
let rel_bytes: Vec<u8> = vec![
0xf0, 0x50, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x03,
];
let mut bytes = vec![0u8; 64];
let offset = bytes.len();
bytes.extend_from_slice(&rel_bytes);
let section_no_fix =
RelocSection::parse(&bytes, offset, rel_bytes.len(), false, ctx).unwrap();
let reloc_no_fix = section_no_fix.get(0).unwrap();
assert_eq!(reloc_no_fix.r_offset, 0x150f0);
assert_eq!(
reloc_no_fix.r_sym, 51511296,
"Without fixup, r_sym should be 51511296 (wrong)"
);
assert_eq!(
reloc_no_fix.r_type, 0,
"Without fixup, r_type should be 0 (wrong)"
);
let section_fixed =
RelocSection::parse_inner(&bytes, offset, rel_bytes.len(), false, ctx, true).unwrap();
let reloc_fixed = section_fixed.get(0).unwrap();
assert_eq!(reloc_fixed.r_offset, 0x150f0);
assert_eq!(reloc_fixed.r_sym, 0, "With fixup, r_sym should be 0");
assert_eq!(
reloc_fixed.r_type & 0xFF,
3,
"With fixup, primary r_type should be R_MIPS_REL32 (3)"
);
let relocs: Vec<_> = section_fixed.iter().collect();
assert_eq!(relocs.len(), 1);
assert_eq!(relocs[0].r_sym, 0);
assert_eq!(relocs[0].r_type & 0xFF, 3);
}
}