use crate::common::{BaseAddresses, Encoding, Format, Unwinder};
use crate::constants::*;
use crate::write::writer::{field_size, write_constant_pointer};
use crate::write::{Address, Error, Result, Writer};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Relocation {
pub offset: usize,
pub size: u8,
pub kind: RelocationKind,
pub symbol: usize,
pub addend: i64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RelocationKind {
Absolute,
Relative,
Target2,
}
pub trait RelocateWriter {
type Writer: Writer;
fn writer(&self) -> &Self::Writer;
fn writer_mut(&mut self) -> &mut Self::Writer;
fn relocate(&mut self, relocation: Relocation);
}
impl<T: RelocateWriter> Writer for T {
type Endian = <<T as RelocateWriter>::Writer as Writer>::Endian;
fn endian(&self) -> Self::Endian {
self.writer().endian()
}
fn len(&self) -> usize {
self.writer().len()
}
fn write(&mut self, bytes: &[u8]) -> Result<()> {
self.writer_mut().write(bytes)
}
fn write_pointer(
&mut self,
encoding: Encoding,
address: Address,
format: Format,
bases: &BaseAddresses,
) -> Result<()> {
let (symbol, addend) = match address {
Address::Constant(value) => {
return write_constant_pointer(self.writer_mut(), encoding, value, format, bases);
}
Address::Symbol { symbol, addend } => (symbol, addend),
};
let kind = match encoding.application() {
DW_EH_PE_ABSPTR | DW_EH_PE_ALIGNED => RelocationKind::Absolute,
DW_EH_PE_PCREL => RelocationKind::Relative,
_ => return Err(Error::UnsupportedRelocationEncoding(encoding)),
};
write_relocation(self, kind, symbol, addend, encoding, format)
}
fn write_type_info(
&mut self,
encoding: Encoding,
address: Address,
format: Format,
bases: &BaseAddresses,
) -> Result<()> {
match address {
Address::Symbol { symbol, addend } if format.unwinder == Unwinder::ArmEhabi => {
write_relocation(
self,
RelocationKind::Target2,
symbol,
addend,
encoding,
format,
)
}
_ => self.write_pointer(encoding, address, format, bases),
}
}
}
fn write_relocation<T: RelocateWriter + ?Sized>(
w: &mut T,
kind: RelocationKind,
symbol: usize,
addend: i64,
encoding: Encoding,
format: Format,
) -> Result<()> {
let size =
field_size(encoding, format)?.ok_or(Error::UnsupportedRelocationEncoding(encoding))?;
if encoding.application() == DW_EH_PE_ALIGNED {
w.writer_mut().align(u32::from(format.address_size))?;
}
let offset = w.writer().len();
w.writer_mut().write_sized(addend.cast_unsigned(), size)?;
w.relocate(Relocation {
offset,
size,
kind,
symbol,
addend,
});
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::endianity::LittleEndian;
use crate::write::EndianVec;
use alloc::vec::Vec;
struct Section<W> {
writer: W,
relocations: Vec<Relocation>,
}
impl<W: Writer> RelocateWriter for Section<W> {
type Writer = W;
fn writer(&self) -> &Self::Writer {
&self.writer
}
fn writer_mut(&mut self) -> &mut Self::Writer {
&mut self.writer
}
fn relocate(&mut self, relocation: Relocation) {
self.relocations.push(relocation);
}
}
#[test]
fn test_relocate_writer() {
let mut w = Section {
writer: EndianVec::new(LittleEndian),
relocations: Vec::new(),
};
let bases = BaseAddresses::default().set_section(0x100);
let symbol = |symbol| Address::Symbol { symbol, addend: -4 };
let f8 = Format::new(8);
w.write_pointer(Encoding::INDIRECT_PCREL_SDATA4, symbol(1), f8, &bases)
.unwrap();
w.write_pointer(Encoding::ABSPTR, symbol(2), f8, &bases)
.unwrap();
w.write_pointer(Encoding::PCREL_SDATA4, Address::Constant(0x200), f8, &bases)
.unwrap();
w.write_type_info(Encoding::ABSPTR, symbol(3), Format::arm_ehabi(), &bases)
.unwrap();
assert_eq!(
w.write_pointer(Encoding(0x21), symbol(4), f8, &bases),
Err(Error::UnsupportedRelocationEncoding(Encoding(0x21)))
);
assert_eq!(
w.write_pointer(Encoding(0x11), symbol(4), f8, &bases),
Err(Error::UnsupportedRelocationEncoding(Encoding(0x11)))
);
assert_eq!(w.writer.len(), 20);
assert_eq!(&w.writer.slice()[..4], [0xfc, 0xff, 0xff, 0xff]);
assert_eq!(&w.writer.slice()[12..16], (0x200u32 - 0x10c).to_le_bytes());
assert_eq!(
w.relocations,
[
Relocation {
offset: 0,
size: 4,
kind: RelocationKind::Relative,
symbol: 1,
addend: -4,
},
Relocation {
offset: 4,
size: 8,
kind: RelocationKind::Absolute,
symbol: 2,
addend: -4,
},
Relocation {
offset: 16,
size: 4,
kind: RelocationKind::Target2,
symbol: 3,
addend: -4,
},
]
);
}
}