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, } /// 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 Writer for T { type Endian = <::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, relocations: Vec, } impl RelocateWriter for Section { type Writer = EndianVec; 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); } }