Revision control
Copy as Markdown
Other Tools
use crate::constants;
use crate::write::{Address, Error, Result, Writer};
use crate::SectionId;
/// A relocation to be applied to a section.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Relocation {
/// The offset within the section where the relocation should be applied.
pub offset: usize,
/// The size of the value to be relocated.
pub size: u8,
/// The target of the relocation.
pub target: RelocationTarget,
/// The addend to be applied to the relocated value.
pub addend: i64,
/// The pointer encoding for relocations in unwind information.
pub eh_pe: Option<constants::DwEhPe>,
}
/// The target of a relocation.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RelocationTarget {
/// The relocation target is a symbol.
///
/// The meaning of this value is decided by the writer, but
/// will typically be an index into a symbol table.
Symbol(usize),
/// The relocation target is a section.
Section(SectionId),
}
/// A `Writer` which also records relocations.
pub trait RelocateWriter {
/// The type of the writer being used to write the section data.
type Writer: Writer;
/// Get the writer being used to write the section data.
fn writer(&self) -> &Self::Writer;
/// Get the writer being used to write the section data.
fn writer_mut(&mut self) -> &mut Self::Writer;
/// Record a relocation.
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_at(&mut self, offset: usize, bytes: &[u8]) -> Result<()> {
self.writer_mut().write_at(offset, bytes)
}
fn write_address(&mut self, address: Address, size: u8) -> Result<()> {
match address {
Address::Constant(val) => self.writer_mut().write_udata(val, size),
Address::Symbol { symbol, addend } => {
self.relocate(Relocation {
offset: self.len(),
size,
target: RelocationTarget::Symbol(symbol),
addend,
eh_pe: None,
});
self.writer_mut().write_udata(0, size)
}
}
}
fn write_offset(&mut self, val: usize, section: SectionId, size: u8) -> Result<()> {
self.relocate(Relocation {
offset: self.len(),
size,
target: RelocationTarget::Section(section),
addend: val as i64,
eh_pe: None,
});
self.writer_mut().write_udata(0, size)
}
fn write_offset_at(
&mut self,
offset: usize,
val: usize,
section: SectionId,
size: u8,
) -> Result<()> {
self.relocate(Relocation {
offset,
size,
target: RelocationTarget::Section(section),
addend: val as i64,
eh_pe: None,
});
self.writer_mut().write_udata_at(offset, 0, size)
}
fn write_eh_pointer(
&mut self,
address: Address,
eh_pe: constants::DwEhPe,
size: u8,
) -> Result<()> {
match address {
Address::Constant(_) => self.writer_mut().write_eh_pointer(address, eh_pe, size),
Address::Symbol { symbol, addend } => {
let size = match eh_pe.format() {
constants::DW_EH_PE_absptr => size,
constants::DW_EH_PE_udata2 => 2,
constants::DW_EH_PE_udata4 => 4,
constants::DW_EH_PE_udata8 => 8,
constants::DW_EH_PE_sdata2 => 2,
constants::DW_EH_PE_sdata4 => 4,
constants::DW_EH_PE_sdata8 => 8,
_ => return Err(Error::UnsupportedPointerEncoding(eh_pe)),
};
self.relocate(Relocation {
offset: self.len(),
size,
target: RelocationTarget::Symbol(symbol),
addend,
eh_pe: Some(eh_pe),
});
self.writer_mut().write_udata(0, size)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::write::EndianVec;
use crate::{LittleEndian, SectionId};
use alloc::vec::Vec;
struct Section {
writer: EndianVec<LittleEndian>,
relocations: Vec<Relocation>,
}
impl RelocateWriter for Section {
type Writer = EndianVec<LittleEndian>;
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 expected_data = Vec::new();
let mut expected_relocations = Vec::new();
let mut section = Section {
writer: EndianVec::new(LittleEndian),
relocations: Vec::new(),
};
// No relocation for plain data.
section.write_udata(0x12345678, 4).unwrap();
expected_data.extend_from_slice(&0x12345678u32.to_le_bytes());
// No relocation for a constant address.
section
.write_address(Address::Constant(0x87654321), 4)
.unwrap();
expected_data.extend_from_slice(&0x87654321u32.to_le_bytes());
// Relocation for a symbol address.
let offset = section.len();
section
.write_address(
Address::Symbol {
symbol: 1,
addend: 0x12345678,
},
4,
)
.unwrap();
expected_data.extend_from_slice(&[0; 4]);
expected_relocations.push(Relocation {
offset,
size: 4,
target: RelocationTarget::Symbol(1),
addend: 0x12345678,
eh_pe: None,
});
// Relocation for a section offset.
let offset = section.len();
section
.write_offset(0x12345678, SectionId::DebugAbbrev, 4)
.unwrap();
expected_data.extend_from_slice(&[0; 4]);
expected_relocations.push(Relocation {
offset,
size: 4,
target: RelocationTarget::Section(SectionId::DebugAbbrev),
addend: 0x12345678,
eh_pe: None,
});
// Relocation for a section offset at a specific offset.
let offset = section.len();
section.write_udata(0x12345678, 4).unwrap();
section
.write_offset_at(offset, 0x12345678, SectionId::DebugStr, 4)
.unwrap();
expected_data.extend_from_slice(&[0; 4]);
expected_relocations.push(Relocation {
offset,
size: 4,
target: RelocationTarget::Section(SectionId::DebugStr),
addend: 0x12345678,
eh_pe: None,
});
// No relocation for a constant in unwind information.
section
.write_eh_pointer(Address::Constant(0x87654321), constants::DW_EH_PE_absptr, 8)
.unwrap();
expected_data.extend_from_slice(&0x87654321u64.to_le_bytes());
// No relocation for a relative constant in unwind information.
let offset = section.len();
section
.write_eh_pointer(
Address::Constant(offset as u64 - 8),
constants::DW_EH_PE_pcrel | constants::DW_EH_PE_sdata4,
8,
)
.unwrap();
expected_data.extend_from_slice(&(-8i32).to_le_bytes());
// Relocation for a symbol in unwind information.
let offset = section.len();
section
.write_eh_pointer(
Address::Symbol {
symbol: 2,
addend: 0x12345678,
},
constants::DW_EH_PE_pcrel | constants::DW_EH_PE_sdata4,
8,
)
.unwrap();
expected_data.extend_from_slice(&[0; 4]);
expected_relocations.push(Relocation {
offset,
size: 4,
target: RelocationTarget::Symbol(2),
addend: 0x12345678,
eh_pe: Some(constants::DW_EH_PE_pcrel | constants::DW_EH_PE_sdata4),
});
assert_eq!(section.writer.into_vec(), expected_data);
assert_eq!(section.relocations, expected_relocations);
}
}