1051 lines
41 KiB
Rust
1051 lines
41 KiB
Rust
|
|
use alloc::vec::Vec;
|
||
|
|
use indexmap::IndexSet;
|
||
|
|
use std::ops::{Deref, DerefMut};
|
||
|
|
|
||
|
|
use crate::common::{DebugFrameOffset, EhFrameOffset, Encoding, Format, Register, SectionId};
|
||
|
|
use crate::constants;
|
||
|
|
use crate::write::{Address, BaseId, Error, Expression, Result, Section, Writer};
|
||
|
|
|
||
|
|
define_section!(
|
||
|
|
DebugFrame,
|
||
|
|
DebugFrameOffset,
|
||
|
|
"A writable `.debug_frame` section."
|
||
|
|
);
|
||
|
|
|
||
|
|
define_section!(EhFrame, EhFrameOffset, "A writable `.eh_frame` section.");
|
||
|
|
|
||
|
|
define_id!(CieId, "An identifier for a CIE in a `FrameTable`.");
|
||
|
|
|
||
|
|
/// A table of frame description entries.
|
||
|
|
#[derive(Debug, Default)]
|
||
|
|
pub struct FrameTable {
|
||
|
|
/// Base id for CIEs.
|
||
|
|
base_id: BaseId,
|
||
|
|
/// The common information entries.
|
||
|
|
cies: IndexSet<CommonInformationEntry>,
|
||
|
|
/// The frame description entries.
|
||
|
|
fdes: Vec<(CieId, FrameDescriptionEntry)>,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl FrameTable {
|
||
|
|
/// Add a CIE and return its id.
|
||
|
|
///
|
||
|
|
/// If the CIE already exists, then return the id of the existing CIE.
|
||
|
|
pub fn add_cie(&mut self, cie: CommonInformationEntry) -> CieId {
|
||
|
|
let (index, _) = self.cies.insert_full(cie);
|
||
|
|
CieId::new(self.base_id, index)
|
||
|
|
}
|
||
|
|
|
||
|
|
/// The number of CIEs.
|
||
|
|
pub fn cie_count(&self) -> usize {
|
||
|
|
self.cies.len()
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Add a FDE.
|
||
|
|
///
|
||
|
|
/// Does not check for duplicates.
|
||
|
|
///
|
||
|
|
/// # Panics
|
||
|
|
///
|
||
|
|
/// Panics if the CIE id is invalid.
|
||
|
|
pub fn add_fde(&mut self, cie: CieId, fde: FrameDescriptionEntry) {
|
||
|
|
debug_assert_eq!(self.base_id, cie.base_id);
|
||
|
|
self.fdes.push((cie, fde));
|
||
|
|
}
|
||
|
|
|
||
|
|
/// The number of FDEs.
|
||
|
|
pub fn fde_count(&self) -> usize {
|
||
|
|
self.fdes.len()
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Write the frame table entries to the given `.debug_frame` section.
|
||
|
|
pub fn write_debug_frame<W: Writer>(&self, w: &mut DebugFrame<W>) -> Result<()> {
|
||
|
|
self.write(&mut w.0, false)
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Write the frame table entries to the given `.eh_frame` section.
|
||
|
|
pub fn write_eh_frame<W: Writer>(&self, w: &mut EhFrame<W>) -> Result<()> {
|
||
|
|
self.write(&mut w.0, true)
|
||
|
|
}
|
||
|
|
|
||
|
|
fn write<W: Writer>(&self, w: &mut W, eh_frame: bool) -> Result<()> {
|
||
|
|
let mut cie_offsets = vec![None; self.cies.len()];
|
||
|
|
for (cie_id, fde) in &self.fdes {
|
||
|
|
let cie_index = cie_id.index;
|
||
|
|
let cie = self.cies.get_index(cie_index).unwrap();
|
||
|
|
let cie_offset = match cie_offsets[cie_index] {
|
||
|
|
Some(offset) => offset,
|
||
|
|
None => {
|
||
|
|
// Only write CIEs as they are referenced.
|
||
|
|
let offset = cie.write(w, eh_frame)?;
|
||
|
|
cie_offsets[cie_index] = Some(offset);
|
||
|
|
offset
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
fde.write(w, eh_frame, cie_offset, cie)?;
|
||
|
|
}
|
||
|
|
// TODO: write length 0 terminator for eh_frame?
|
||
|
|
Ok(())
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// A common information entry. This contains information that is shared between FDEs.
|
||
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||
|
|
pub struct CommonInformationEntry {
|
||
|
|
encoding: Encoding,
|
||
|
|
|
||
|
|
/// A constant that is factored out of code offsets.
|
||
|
|
///
|
||
|
|
/// This should be set to the minimum instruction length.
|
||
|
|
/// Writing a code offset that is not a multiple of this factor will generate an error.
|
||
|
|
code_alignment_factor: u8,
|
||
|
|
|
||
|
|
/// A constant that is factored out of data offsets.
|
||
|
|
///
|
||
|
|
/// This should be set to the minimum data alignment for the frame.
|
||
|
|
/// Writing a data offset that is not a multiple of this factor will generate an error.
|
||
|
|
data_alignment_factor: i8,
|
||
|
|
|
||
|
|
/// The return address register. This might not correspond to an actual machine register.
|
||
|
|
return_address_register: Register,
|
||
|
|
|
||
|
|
/// The address of the personality function and its encoding.
|
||
|
|
pub personality: Option<(constants::DwEhPe, Address)>,
|
||
|
|
|
||
|
|
/// The encoding to use for the LSDA address in FDEs.
|
||
|
|
///
|
||
|
|
/// If set then all FDEs which use this CIE must have a LSDA address.
|
||
|
|
pub lsda_encoding: Option<constants::DwEhPe>,
|
||
|
|
|
||
|
|
/// The encoding to use for addresses in FDEs.
|
||
|
|
pub fde_address_encoding: constants::DwEhPe,
|
||
|
|
|
||
|
|
/// True for signal trampolines.
|
||
|
|
pub signal_trampoline: bool,
|
||
|
|
|
||
|
|
/// The initial instructions upon entry to this function.
|
||
|
|
instructions: Vec<CallFrameInstruction>,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl CommonInformationEntry {
|
||
|
|
/// Create a new common information entry.
|
||
|
|
///
|
||
|
|
/// The encoding version must be a CFI version, not a DWARF version.
|
||
|
|
pub fn new(
|
||
|
|
encoding: Encoding,
|
||
|
|
code_alignment_factor: u8,
|
||
|
|
data_alignment_factor: i8,
|
||
|
|
return_address_register: Register,
|
||
|
|
) -> Self {
|
||
|
|
CommonInformationEntry {
|
||
|
|
encoding,
|
||
|
|
code_alignment_factor,
|
||
|
|
data_alignment_factor,
|
||
|
|
return_address_register,
|
||
|
|
personality: None,
|
||
|
|
lsda_encoding: None,
|
||
|
|
fde_address_encoding: constants::DW_EH_PE_absptr,
|
||
|
|
signal_trampoline: false,
|
||
|
|
instructions: Vec::new(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Add an initial instruction.
|
||
|
|
pub fn add_instruction(&mut self, instruction: CallFrameInstruction) {
|
||
|
|
self.instructions.push(instruction);
|
||
|
|
}
|
||
|
|
|
||
|
|
fn has_augmentation(&self) -> bool {
|
||
|
|
self.personality.is_some()
|
||
|
|
|| self.lsda_encoding.is_some()
|
||
|
|
|| self.signal_trampoline
|
||
|
|
|| self.fde_address_encoding != constants::DW_EH_PE_absptr
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Returns the section offset of the CIE.
|
||
|
|
fn write<W: Writer>(&self, w: &mut W, eh_frame: bool) -> Result<usize> {
|
||
|
|
let encoding = self.encoding;
|
||
|
|
let offset = w.len();
|
||
|
|
|
||
|
|
let length_offset = w.write_initial_length(encoding.format)?;
|
||
|
|
let length_base = w.len();
|
||
|
|
|
||
|
|
if eh_frame {
|
||
|
|
w.write_u32(0)?;
|
||
|
|
} else {
|
||
|
|
match encoding.format {
|
||
|
|
Format::Dwarf32 => w.write_u32(0xffff_ffff)?,
|
||
|
|
Format::Dwarf64 => w.write_u64(0xffff_ffff_ffff_ffff)?,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if eh_frame {
|
||
|
|
if encoding.version != 1 {
|
||
|
|
return Err(Error::UnsupportedVersion(encoding.version));
|
||
|
|
};
|
||
|
|
} else {
|
||
|
|
match encoding.version {
|
||
|
|
1 | 3 | 4 => {}
|
||
|
|
_ => return Err(Error::UnsupportedVersion(encoding.version)),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
w.write_u8(encoding.version as u8)?;
|
||
|
|
|
||
|
|
let augmentation = self.has_augmentation();
|
||
|
|
if augmentation {
|
||
|
|
w.write_u8(b'z')?;
|
||
|
|
if self.lsda_encoding.is_some() {
|
||
|
|
w.write_u8(b'L')?;
|
||
|
|
}
|
||
|
|
if self.personality.is_some() {
|
||
|
|
w.write_u8(b'P')?;
|
||
|
|
}
|
||
|
|
if self.fde_address_encoding != constants::DW_EH_PE_absptr {
|
||
|
|
w.write_u8(b'R')?;
|
||
|
|
}
|
||
|
|
if self.signal_trampoline {
|
||
|
|
w.write_u8(b'S')?;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
w.write_u8(0)?;
|
||
|
|
|
||
|
|
if encoding.version >= 4 {
|
||
|
|
w.write_u8(encoding.address_size)?;
|
||
|
|
// TODO: segment_selector_size
|
||
|
|
w.write_u8(0)?;
|
||
|
|
}
|
||
|
|
|
||
|
|
w.write_uleb128(self.code_alignment_factor.into())?;
|
||
|
|
w.write_sleb128(self.data_alignment_factor.into())?;
|
||
|
|
|
||
|
|
if !eh_frame && encoding.version == 1 {
|
||
|
|
let register = self.return_address_register.0 as u8;
|
||
|
|
if u16::from(register) != self.return_address_register.0 {
|
||
|
|
return Err(Error::ValueTooLarge);
|
||
|
|
}
|
||
|
|
w.write_u8(register)?;
|
||
|
|
} else {
|
||
|
|
w.write_uleb128(self.return_address_register.0.into())?;
|
||
|
|
}
|
||
|
|
|
||
|
|
if augmentation {
|
||
|
|
let augmentation_length_offset = w.len();
|
||
|
|
w.write_u8(0)?;
|
||
|
|
let augmentation_length_base = w.len();
|
||
|
|
|
||
|
|
if let Some(eh_pe) = self.lsda_encoding {
|
||
|
|
w.write_u8(eh_pe.0)?;
|
||
|
|
}
|
||
|
|
if let Some((eh_pe, address)) = self.personality {
|
||
|
|
w.write_u8(eh_pe.0)?;
|
||
|
|
w.write_eh_pointer(address, eh_pe, encoding.address_size)?;
|
||
|
|
}
|
||
|
|
if self.fde_address_encoding != constants::DW_EH_PE_absptr {
|
||
|
|
w.write_u8(self.fde_address_encoding.0)?;
|
||
|
|
}
|
||
|
|
|
||
|
|
let augmentation_length = (w.len() - augmentation_length_base) as u64;
|
||
|
|
debug_assert!(augmentation_length < 0x80);
|
||
|
|
w.write_udata_at(augmentation_length_offset, augmentation_length, 1)?;
|
||
|
|
}
|
||
|
|
|
||
|
|
for instruction in &self.instructions {
|
||
|
|
instruction.write(w, encoding, self)?;
|
||
|
|
}
|
||
|
|
|
||
|
|
write_nop(
|
||
|
|
w,
|
||
|
|
encoding.format.word_size() as usize + w.len() - length_base,
|
||
|
|
encoding.address_size,
|
||
|
|
)?;
|
||
|
|
|
||
|
|
let length = (w.len() - length_base) as u64;
|
||
|
|
w.write_initial_length_at(length_offset, length, encoding.format)?;
|
||
|
|
|
||
|
|
Ok(offset)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// A frame description entry. There should be one FDE per function.
|
||
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||
|
|
pub struct FrameDescriptionEntry {
|
||
|
|
/// The initial address of the function.
|
||
|
|
address: Address,
|
||
|
|
|
||
|
|
/// The length in bytes of the function.
|
||
|
|
length: u32,
|
||
|
|
|
||
|
|
/// The address of the LSDA.
|
||
|
|
pub lsda: Option<Address>,
|
||
|
|
|
||
|
|
/// The instructions for this function, ordered by offset.
|
||
|
|
instructions: Vec<(u32, CallFrameInstruction)>,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl FrameDescriptionEntry {
|
||
|
|
/// Create a new frame description entry for a function.
|
||
|
|
pub fn new(address: Address, length: u32) -> Self {
|
||
|
|
FrameDescriptionEntry {
|
||
|
|
address,
|
||
|
|
length,
|
||
|
|
lsda: None,
|
||
|
|
instructions: Vec::new(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Add an instruction.
|
||
|
|
///
|
||
|
|
/// Instructions must be added in increasing order of offset, or writing will fail.
|
||
|
|
pub fn add_instruction(&mut self, offset: u32, instruction: CallFrameInstruction) {
|
||
|
|
debug_assert!(self.instructions.last().map(|x| x.0).unwrap_or(0) <= offset);
|
||
|
|
self.instructions.push((offset, instruction));
|
||
|
|
}
|
||
|
|
|
||
|
|
fn write<W: Writer>(
|
||
|
|
&self,
|
||
|
|
w: &mut W,
|
||
|
|
eh_frame: bool,
|
||
|
|
cie_offset: usize,
|
||
|
|
cie: &CommonInformationEntry,
|
||
|
|
) -> Result<()> {
|
||
|
|
let encoding = cie.encoding;
|
||
|
|
let length_offset = w.write_initial_length(encoding.format)?;
|
||
|
|
let length_base = w.len();
|
||
|
|
|
||
|
|
if eh_frame {
|
||
|
|
// .eh_frame uses a relative offset which doesn't need relocation.
|
||
|
|
w.write_udata((w.len() - cie_offset) as u64, 4)?;
|
||
|
|
} else {
|
||
|
|
w.write_offset(
|
||
|
|
cie_offset,
|
||
|
|
SectionId::DebugFrame,
|
||
|
|
encoding.format.word_size(),
|
||
|
|
)?;
|
||
|
|
}
|
||
|
|
|
||
|
|
if cie.fde_address_encoding != constants::DW_EH_PE_absptr {
|
||
|
|
w.write_eh_pointer(
|
||
|
|
self.address,
|
||
|
|
cie.fde_address_encoding,
|
||
|
|
encoding.address_size,
|
||
|
|
)?;
|
||
|
|
w.write_eh_pointer_data(
|
||
|
|
self.length.into(),
|
||
|
|
cie.fde_address_encoding.format(),
|
||
|
|
encoding.address_size,
|
||
|
|
)?;
|
||
|
|
} else {
|
||
|
|
w.write_address(self.address, encoding.address_size)?;
|
||
|
|
w.write_udata(self.length.into(), encoding.address_size)?;
|
||
|
|
}
|
||
|
|
|
||
|
|
if cie.has_augmentation() {
|
||
|
|
let mut augmentation_length = 0u64;
|
||
|
|
if self.lsda.is_some() {
|
||
|
|
augmentation_length += u64::from(encoding.address_size);
|
||
|
|
}
|
||
|
|
w.write_uleb128(augmentation_length)?;
|
||
|
|
|
||
|
|
debug_assert_eq!(self.lsda.is_some(), cie.lsda_encoding.is_some());
|
||
|
|
if let (Some(lsda), Some(lsda_encoding)) = (self.lsda, cie.lsda_encoding) {
|
||
|
|
w.write_eh_pointer(lsda, lsda_encoding, encoding.address_size)?;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
let mut prev_offset = 0;
|
||
|
|
for (offset, instruction) in &self.instructions {
|
||
|
|
write_advance_loc(w, cie.code_alignment_factor, prev_offset, *offset)?;
|
||
|
|
prev_offset = *offset;
|
||
|
|
instruction.write(w, encoding, cie)?;
|
||
|
|
}
|
||
|
|
|
||
|
|
write_nop(
|
||
|
|
w,
|
||
|
|
encoding.format.word_size() as usize + w.len() - length_base,
|
||
|
|
encoding.address_size,
|
||
|
|
)?;
|
||
|
|
|
||
|
|
let length = (w.len() - length_base) as u64;
|
||
|
|
w.write_initial_length_at(length_offset, length, encoding.format)?;
|
||
|
|
|
||
|
|
Ok(())
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// An instruction in a frame description entry.
|
||
|
|
///
|
||
|
|
/// This may be a CFA definition, a register rule, or some other directive.
|
||
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||
|
|
#[non_exhaustive]
|
||
|
|
pub enum CallFrameInstruction {
|
||
|
|
/// Define the CFA rule to use the provided register and offset.
|
||
|
|
Cfa(Register, i32),
|
||
|
|
/// Update the CFA rule to use the provided register. The offset is unchanged.
|
||
|
|
CfaRegister(Register),
|
||
|
|
/// Update the CFA rule to use the provided offset. The register is unchanged.
|
||
|
|
CfaOffset(i32),
|
||
|
|
/// Define the CFA rule to use the provided expression.
|
||
|
|
CfaExpression(Expression),
|
||
|
|
|
||
|
|
/// Restore the initial rule for the register.
|
||
|
|
Restore(Register),
|
||
|
|
/// The previous value of the register is not recoverable.
|
||
|
|
Undefined(Register),
|
||
|
|
/// The register has not been modified.
|
||
|
|
SameValue(Register),
|
||
|
|
/// The previous value of the register is saved at address CFA + offset.
|
||
|
|
Offset(Register, i32),
|
||
|
|
/// The previous value of the register is CFA + offset.
|
||
|
|
ValOffset(Register, i32),
|
||
|
|
/// The previous value of the register is stored in another register.
|
||
|
|
Register(Register, Register),
|
||
|
|
/// The previous value of the register is saved at address given by the expression.
|
||
|
|
Expression(Register, Expression),
|
||
|
|
/// The previous value of the register is given by the expression.
|
||
|
|
ValExpression(Register, Expression),
|
||
|
|
|
||
|
|
/// Push all register rules onto a stack.
|
||
|
|
RememberState,
|
||
|
|
/// Pop all register rules off the stack.
|
||
|
|
RestoreState,
|
||
|
|
/// The size of the arguments that have been pushed onto the stack.
|
||
|
|
ArgsSize(u32),
|
||
|
|
|
||
|
|
/// AAarch64 extension: negate the `RA_SIGN_STATE` pseudo-register.
|
||
|
|
NegateRaState,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl CallFrameInstruction {
|
||
|
|
fn write<W: Writer>(
|
||
|
|
&self,
|
||
|
|
w: &mut W,
|
||
|
|
encoding: Encoding,
|
||
|
|
cie: &CommonInformationEntry,
|
||
|
|
) -> Result<()> {
|
||
|
|
match *self {
|
||
|
|
CallFrameInstruction::Cfa(register, offset) => {
|
||
|
|
if offset < 0 {
|
||
|
|
let offset = factored_data_offset(offset, cie.data_alignment_factor)?;
|
||
|
|
w.write_u8(constants::DW_CFA_def_cfa_sf.0)?;
|
||
|
|
w.write_uleb128(register.0.into())?;
|
||
|
|
w.write_sleb128(offset.into())?;
|
||
|
|
} else {
|
||
|
|
// Unfactored offset.
|
||
|
|
w.write_u8(constants::DW_CFA_def_cfa.0)?;
|
||
|
|
w.write_uleb128(register.0.into())?;
|
||
|
|
w.write_uleb128(offset as u64)?;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
CallFrameInstruction::CfaRegister(register) => {
|
||
|
|
w.write_u8(constants::DW_CFA_def_cfa_register.0)?;
|
||
|
|
w.write_uleb128(register.0.into())?;
|
||
|
|
}
|
||
|
|
CallFrameInstruction::CfaOffset(offset) => {
|
||
|
|
if offset < 0 {
|
||
|
|
let offset = factored_data_offset(offset, cie.data_alignment_factor)?;
|
||
|
|
w.write_u8(constants::DW_CFA_def_cfa_offset_sf.0)?;
|
||
|
|
w.write_sleb128(offset.into())?;
|
||
|
|
} else {
|
||
|
|
// Unfactored offset.
|
||
|
|
w.write_u8(constants::DW_CFA_def_cfa_offset.0)?;
|
||
|
|
w.write_uleb128(offset as u64)?;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
CallFrameInstruction::CfaExpression(ref expression) => {
|
||
|
|
w.write_u8(constants::DW_CFA_def_cfa_expression.0)?;
|
||
|
|
w.write_uleb128(expression.size(encoding, None) as u64)?;
|
||
|
|
expression.write(w, None, encoding, None)?;
|
||
|
|
}
|
||
|
|
CallFrameInstruction::Restore(register) => {
|
||
|
|
if register.0 < 0x40 {
|
||
|
|
w.write_u8(constants::DW_CFA_restore.0 | register.0 as u8)?;
|
||
|
|
} else {
|
||
|
|
w.write_u8(constants::DW_CFA_restore_extended.0)?;
|
||
|
|
w.write_uleb128(register.0.into())?;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
CallFrameInstruction::Undefined(register) => {
|
||
|
|
w.write_u8(constants::DW_CFA_undefined.0)?;
|
||
|
|
w.write_uleb128(register.0.into())?;
|
||
|
|
}
|
||
|
|
CallFrameInstruction::SameValue(register) => {
|
||
|
|
w.write_u8(constants::DW_CFA_same_value.0)?;
|
||
|
|
w.write_uleb128(register.0.into())?;
|
||
|
|
}
|
||
|
|
CallFrameInstruction::Offset(register, offset) => {
|
||
|
|
let offset = factored_data_offset(offset, cie.data_alignment_factor)?;
|
||
|
|
if offset < 0 {
|
||
|
|
w.write_u8(constants::DW_CFA_offset_extended_sf.0)?;
|
||
|
|
w.write_uleb128(register.0.into())?;
|
||
|
|
w.write_sleb128(offset.into())?;
|
||
|
|
} else if register.0 < 0x40 {
|
||
|
|
w.write_u8(constants::DW_CFA_offset.0 | register.0 as u8)?;
|
||
|
|
w.write_uleb128(offset as u64)?;
|
||
|
|
} else {
|
||
|
|
w.write_u8(constants::DW_CFA_offset_extended.0)?;
|
||
|
|
w.write_uleb128(register.0.into())?;
|
||
|
|
w.write_uleb128(offset as u64)?;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
CallFrameInstruction::ValOffset(register, offset) => {
|
||
|
|
let offset = factored_data_offset(offset, cie.data_alignment_factor)?;
|
||
|
|
if offset < 0 {
|
||
|
|
w.write_u8(constants::DW_CFA_val_offset_sf.0)?;
|
||
|
|
w.write_uleb128(register.0.into())?;
|
||
|
|
w.write_sleb128(offset.into())?;
|
||
|
|
} else {
|
||
|
|
w.write_u8(constants::DW_CFA_val_offset.0)?;
|
||
|
|
w.write_uleb128(register.0.into())?;
|
||
|
|
w.write_uleb128(offset as u64)?;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
CallFrameInstruction::Register(register1, register2) => {
|
||
|
|
w.write_u8(constants::DW_CFA_register.0)?;
|
||
|
|
w.write_uleb128(register1.0.into())?;
|
||
|
|
w.write_uleb128(register2.0.into())?;
|
||
|
|
}
|
||
|
|
CallFrameInstruction::Expression(register, ref expression) => {
|
||
|
|
w.write_u8(constants::DW_CFA_expression.0)?;
|
||
|
|
w.write_uleb128(register.0.into())?;
|
||
|
|
w.write_uleb128(expression.size(encoding, None) as u64)?;
|
||
|
|
expression.write(w, None, encoding, None)?;
|
||
|
|
}
|
||
|
|
CallFrameInstruction::ValExpression(register, ref expression) => {
|
||
|
|
w.write_u8(constants::DW_CFA_val_expression.0)?;
|
||
|
|
w.write_uleb128(register.0.into())?;
|
||
|
|
w.write_uleb128(expression.size(encoding, None) as u64)?;
|
||
|
|
expression.write(w, None, encoding, None)?;
|
||
|
|
}
|
||
|
|
CallFrameInstruction::RememberState => {
|
||
|
|
w.write_u8(constants::DW_CFA_remember_state.0)?;
|
||
|
|
}
|
||
|
|
CallFrameInstruction::RestoreState => {
|
||
|
|
w.write_u8(constants::DW_CFA_restore_state.0)?;
|
||
|
|
}
|
||
|
|
CallFrameInstruction::ArgsSize(size) => {
|
||
|
|
w.write_u8(constants::DW_CFA_GNU_args_size.0)?;
|
||
|
|
w.write_uleb128(size.into())?;
|
||
|
|
}
|
||
|
|
CallFrameInstruction::NegateRaState => {
|
||
|
|
w.write_u8(constants::DW_CFA_AARCH64_negate_ra_state.0)?;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
Ok(())
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn write_advance_loc<W: Writer>(
|
||
|
|
w: &mut W,
|
||
|
|
code_alignment_factor: u8,
|
||
|
|
prev_offset: u32,
|
||
|
|
offset: u32,
|
||
|
|
) -> Result<()> {
|
||
|
|
if offset == prev_offset {
|
||
|
|
return Ok(());
|
||
|
|
}
|
||
|
|
let delta = factored_code_delta(prev_offset, offset, code_alignment_factor)?;
|
||
|
|
if delta < 0x40 {
|
||
|
|
w.write_u8(constants::DW_CFA_advance_loc.0 | delta as u8)?;
|
||
|
|
} else if delta < 0x100 {
|
||
|
|
w.write_u8(constants::DW_CFA_advance_loc1.0)?;
|
||
|
|
w.write_u8(delta as u8)?;
|
||
|
|
} else if delta < 0x10000 {
|
||
|
|
w.write_u8(constants::DW_CFA_advance_loc2.0)?;
|
||
|
|
w.write_u16(delta as u16)?;
|
||
|
|
} else {
|
||
|
|
w.write_u8(constants::DW_CFA_advance_loc4.0)?;
|
||
|
|
w.write_u32(delta)?;
|
||
|
|
}
|
||
|
|
Ok(())
|
||
|
|
}
|
||
|
|
|
||
|
|
fn write_nop<W: Writer>(w: &mut W, len: usize, align: u8) -> Result<()> {
|
||
|
|
debug_assert_eq!(align & (align - 1), 0);
|
||
|
|
let tail_len = (!len + 1) & (align as usize - 1);
|
||
|
|
for _ in 0..tail_len {
|
||
|
|
w.write_u8(constants::DW_CFA_nop.0)?;
|
||
|
|
}
|
||
|
|
Ok(())
|
||
|
|
}
|
||
|
|
|
||
|
|
fn factored_code_delta(prev_offset: u32, offset: u32, factor: u8) -> Result<u32> {
|
||
|
|
if offset < prev_offset {
|
||
|
|
return Err(Error::InvalidFrameCodeOffset(offset));
|
||
|
|
}
|
||
|
|
let delta = offset - prev_offset;
|
||
|
|
let factor = u32::from(factor);
|
||
|
|
let factored_delta = delta / factor;
|
||
|
|
if delta != factored_delta * factor {
|
||
|
|
return Err(Error::InvalidFrameCodeOffset(offset));
|
||
|
|
}
|
||
|
|
Ok(factored_delta)
|
||
|
|
}
|
||
|
|
|
||
|
|
fn factored_data_offset(offset: i32, factor: i8) -> Result<i32> {
|
||
|
|
let factor = i32::from(factor);
|
||
|
|
let factored_offset = offset / factor;
|
||
|
|
if offset != factored_offset * factor {
|
||
|
|
return Err(Error::InvalidFrameDataOffset(offset));
|
||
|
|
}
|
||
|
|
Ok(factored_offset)
|
||
|
|
}
|
||
|
|
|
||
|
|
#[cfg(feature = "read")]
|
||
|
|
pub(crate) mod convert {
|
||
|
|
use super::*;
|
||
|
|
use crate::read::{self, Reader};
|
||
|
|
use crate::write::{ConvertError, ConvertResult};
|
||
|
|
use std::collections::{hash_map, HashMap};
|
||
|
|
|
||
|
|
impl FrameTable {
|
||
|
|
/// Create a frame table by reading the data in the given section.
|
||
|
|
///
|
||
|
|
/// `convert_address` is a function to convert read addresses into the `Address`
|
||
|
|
/// type. For non-relocatable addresses, this function may simply return
|
||
|
|
/// `Address::Constant(address)`. For relocatable addresses, it is the caller's
|
||
|
|
/// responsibility to determine the symbol and addend corresponding to the address
|
||
|
|
/// and return `Address::Symbol { symbol, addend }`.
|
||
|
|
pub fn from<R, Section>(
|
||
|
|
frame: &Section,
|
||
|
|
convert_address: &dyn Fn(u64) -> Option<Address>,
|
||
|
|
) -> ConvertResult<FrameTable>
|
||
|
|
where
|
||
|
|
R: Reader<Offset = usize>,
|
||
|
|
Section: read::UnwindSection<R>,
|
||
|
|
Section::Offset: read::UnwindOffset<usize>,
|
||
|
|
{
|
||
|
|
let bases = read::BaseAddresses::default().set_eh_frame(0);
|
||
|
|
|
||
|
|
let mut frame_table = FrameTable::default();
|
||
|
|
|
||
|
|
let mut cie_ids = HashMap::new();
|
||
|
|
let mut entries = frame.entries(&bases);
|
||
|
|
while let Some(entry) = entries.next()? {
|
||
|
|
let partial = match entry {
|
||
|
|
read::CieOrFde::Cie(_) => continue,
|
||
|
|
read::CieOrFde::Fde(partial) => partial,
|
||
|
|
};
|
||
|
|
|
||
|
|
// TODO: is it worth caching the parsed CIEs? It would be better if FDEs only
|
||
|
|
// stored a reference.
|
||
|
|
let from_fde = partial.parse(Section::cie_from_offset)?;
|
||
|
|
let from_cie = from_fde.cie();
|
||
|
|
let cie_id = match cie_ids.entry(from_cie.offset()) {
|
||
|
|
hash_map::Entry::Occupied(o) => *o.get(),
|
||
|
|
hash_map::Entry::Vacant(e) => {
|
||
|
|
let cie =
|
||
|
|
CommonInformationEntry::from(from_cie, frame, &bases, convert_address)?;
|
||
|
|
let cie_id = frame_table.add_cie(cie);
|
||
|
|
e.insert(cie_id);
|
||
|
|
cie_id
|
||
|
|
}
|
||
|
|
};
|
||
|
|
let fde = FrameDescriptionEntry::from(&from_fde, frame, &bases, convert_address)?;
|
||
|
|
frame_table.add_fde(cie_id, fde);
|
||
|
|
}
|
||
|
|
|
||
|
|
Ok(frame_table)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl CommonInformationEntry {
|
||
|
|
fn from<R, Section>(
|
||
|
|
from_cie: &read::CommonInformationEntry<R>,
|
||
|
|
frame: &Section,
|
||
|
|
bases: &read::BaseAddresses,
|
||
|
|
convert_address: &dyn Fn(u64) -> Option<Address>,
|
||
|
|
) -> ConvertResult<CommonInformationEntry>
|
||
|
|
where
|
||
|
|
R: Reader<Offset = usize>,
|
||
|
|
Section: read::UnwindSection<R>,
|
||
|
|
Section::Offset: read::UnwindOffset<usize>,
|
||
|
|
{
|
||
|
|
let mut cie = CommonInformationEntry::new(
|
||
|
|
from_cie.encoding(),
|
||
|
|
from_cie.code_alignment_factor() as u8,
|
||
|
|
from_cie.data_alignment_factor() as i8,
|
||
|
|
from_cie.return_address_register(),
|
||
|
|
);
|
||
|
|
|
||
|
|
cie.personality = match from_cie.personality_with_encoding() {
|
||
|
|
// We treat these the same because the encoding already determines
|
||
|
|
// whether it is indirect.
|
||
|
|
Some((eh_pe, read::Pointer::Direct(p)))
|
||
|
|
| Some((eh_pe, read::Pointer::Indirect(p))) => {
|
||
|
|
let address = convert_address(p).ok_or(ConvertError::InvalidAddress)?;
|
||
|
|
Some((eh_pe, address))
|
||
|
|
}
|
||
|
|
_ => None,
|
||
|
|
};
|
||
|
|
cie.lsda_encoding = from_cie.lsda_encoding();
|
||
|
|
cie.fde_address_encoding = from_cie
|
||
|
|
.fde_address_encoding()
|
||
|
|
.unwrap_or(constants::DW_EH_PE_absptr);
|
||
|
|
cie.signal_trampoline = from_cie.is_signal_trampoline();
|
||
|
|
|
||
|
|
let mut offset = 0;
|
||
|
|
let mut from_instructions = from_cie.instructions(frame, bases);
|
||
|
|
while let Some(from_instruction) = from_instructions.next()? {
|
||
|
|
if let Some(instruction) = CallFrameInstruction::from(
|
||
|
|
from_instruction,
|
||
|
|
from_cie,
|
||
|
|
convert_address,
|
||
|
|
&mut offset,
|
||
|
|
)? {
|
||
|
|
cie.instructions.push(instruction);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
Ok(cie)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl FrameDescriptionEntry {
|
||
|
|
fn from<R, Section>(
|
||
|
|
from_fde: &read::FrameDescriptionEntry<R>,
|
||
|
|
frame: &Section,
|
||
|
|
bases: &read::BaseAddresses,
|
||
|
|
convert_address: &dyn Fn(u64) -> Option<Address>,
|
||
|
|
) -> ConvertResult<FrameDescriptionEntry>
|
||
|
|
where
|
||
|
|
R: Reader<Offset = usize>,
|
||
|
|
Section: read::UnwindSection<R>,
|
||
|
|
Section::Offset: read::UnwindOffset<usize>,
|
||
|
|
{
|
||
|
|
let address =
|
||
|
|
convert_address(from_fde.initial_address()).ok_or(ConvertError::InvalidAddress)?;
|
||
|
|
let length = from_fde.len() as u32;
|
||
|
|
let mut fde = FrameDescriptionEntry::new(address, length);
|
||
|
|
|
||
|
|
match from_fde.lsda() {
|
||
|
|
// We treat these the same because the encoding already determines
|
||
|
|
// whether it is indirect.
|
||
|
|
Some(read::Pointer::Direct(p)) | Some(read::Pointer::Indirect(p)) => {
|
||
|
|
let address = convert_address(p).ok_or(ConvertError::InvalidAddress)?;
|
||
|
|
fde.lsda = Some(address);
|
||
|
|
}
|
||
|
|
None => {}
|
||
|
|
}
|
||
|
|
|
||
|
|
let from_cie = from_fde.cie();
|
||
|
|
let mut offset = 0;
|
||
|
|
let mut from_instructions = from_fde.instructions(frame, bases);
|
||
|
|
while let Some(from_instruction) = from_instructions.next()? {
|
||
|
|
if let Some(instruction) = CallFrameInstruction::from(
|
||
|
|
from_instruction,
|
||
|
|
from_cie,
|
||
|
|
convert_address,
|
||
|
|
&mut offset,
|
||
|
|
)? {
|
||
|
|
fde.instructions.push((offset, instruction));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Ok(fde)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl CallFrameInstruction {
|
||
|
|
fn from<R: Reader<Offset = usize>>(
|
||
|
|
from_instruction: read::CallFrameInstruction<R>,
|
||
|
|
from_cie: &read::CommonInformationEntry<R>,
|
||
|
|
convert_address: &dyn Fn(u64) -> Option<Address>,
|
||
|
|
offset: &mut u32,
|
||
|
|
) -> ConvertResult<Option<CallFrameInstruction>> {
|
||
|
|
let convert_expression =
|
||
|
|
|x| Expression::from(x, from_cie.encoding(), None, None, None, convert_address);
|
||
|
|
// TODO: validate integer type conversions
|
||
|
|
Ok(Some(match from_instruction {
|
||
|
|
read::CallFrameInstruction::SetLoc { .. } => {
|
||
|
|
return Err(ConvertError::UnsupportedCfiInstruction);
|
||
|
|
}
|
||
|
|
read::CallFrameInstruction::AdvanceLoc { delta } => {
|
||
|
|
*offset += delta * from_cie.code_alignment_factor() as u32;
|
||
|
|
return Ok(None);
|
||
|
|
}
|
||
|
|
read::CallFrameInstruction::DefCfa { register, offset } => {
|
||
|
|
CallFrameInstruction::Cfa(register, offset as i32)
|
||
|
|
}
|
||
|
|
read::CallFrameInstruction::DefCfaSf {
|
||
|
|
register,
|
||
|
|
factored_offset,
|
||
|
|
} => {
|
||
|
|
let offset = factored_offset * from_cie.data_alignment_factor();
|
||
|
|
CallFrameInstruction::Cfa(register, offset as i32)
|
||
|
|
}
|
||
|
|
read::CallFrameInstruction::DefCfaRegister { register } => {
|
||
|
|
CallFrameInstruction::CfaRegister(register)
|
||
|
|
}
|
||
|
|
|
||
|
|
read::CallFrameInstruction::DefCfaOffset { offset } => {
|
||
|
|
CallFrameInstruction::CfaOffset(offset as i32)
|
||
|
|
}
|
||
|
|
read::CallFrameInstruction::DefCfaOffsetSf { factored_offset } => {
|
||
|
|
let offset = factored_offset * from_cie.data_alignment_factor();
|
||
|
|
CallFrameInstruction::CfaOffset(offset as i32)
|
||
|
|
}
|
||
|
|
read::CallFrameInstruction::DefCfaExpression { expression } => {
|
||
|
|
CallFrameInstruction::CfaExpression(convert_expression(expression)?)
|
||
|
|
}
|
||
|
|
read::CallFrameInstruction::Undefined { register } => {
|
||
|
|
CallFrameInstruction::Undefined(register)
|
||
|
|
}
|
||
|
|
read::CallFrameInstruction::SameValue { register } => {
|
||
|
|
CallFrameInstruction::SameValue(register)
|
||
|
|
}
|
||
|
|
read::CallFrameInstruction::Offset {
|
||
|
|
register,
|
||
|
|
factored_offset,
|
||
|
|
} => {
|
||
|
|
let offset = factored_offset as i64 * from_cie.data_alignment_factor();
|
||
|
|
CallFrameInstruction::Offset(register, offset as i32)
|
||
|
|
}
|
||
|
|
read::CallFrameInstruction::OffsetExtendedSf {
|
||
|
|
register,
|
||
|
|
factored_offset,
|
||
|
|
} => {
|
||
|
|
let offset = factored_offset * from_cie.data_alignment_factor();
|
||
|
|
CallFrameInstruction::Offset(register, offset as i32)
|
||
|
|
}
|
||
|
|
read::CallFrameInstruction::ValOffset {
|
||
|
|
register,
|
||
|
|
factored_offset,
|
||
|
|
} => {
|
||
|
|
let offset = factored_offset as i64 * from_cie.data_alignment_factor();
|
||
|
|
CallFrameInstruction::ValOffset(register, offset as i32)
|
||
|
|
}
|
||
|
|
read::CallFrameInstruction::ValOffsetSf {
|
||
|
|
register,
|
||
|
|
factored_offset,
|
||
|
|
} => {
|
||
|
|
let offset = factored_offset * from_cie.data_alignment_factor();
|
||
|
|
CallFrameInstruction::ValOffset(register, offset as i32)
|
||
|
|
}
|
||
|
|
read::CallFrameInstruction::Register {
|
||
|
|
dest_register,
|
||
|
|
src_register,
|
||
|
|
} => CallFrameInstruction::Register(dest_register, src_register),
|
||
|
|
read::CallFrameInstruction::Expression {
|
||
|
|
register,
|
||
|
|
expression,
|
||
|
|
} => CallFrameInstruction::Expression(register, convert_expression(expression)?),
|
||
|
|
read::CallFrameInstruction::ValExpression {
|
||
|
|
register,
|
||
|
|
expression,
|
||
|
|
} => CallFrameInstruction::ValExpression(register, convert_expression(expression)?),
|
||
|
|
read::CallFrameInstruction::Restore { register } => {
|
||
|
|
CallFrameInstruction::Restore(register)
|
||
|
|
}
|
||
|
|
read::CallFrameInstruction::RememberState => CallFrameInstruction::RememberState,
|
||
|
|
read::CallFrameInstruction::RestoreState => CallFrameInstruction::RestoreState,
|
||
|
|
read::CallFrameInstruction::ArgsSize { size } => {
|
||
|
|
CallFrameInstruction::ArgsSize(size as u32)
|
||
|
|
}
|
||
|
|
read::CallFrameInstruction::NegateRaState => CallFrameInstruction::NegateRaState,
|
||
|
|
read::CallFrameInstruction::Nop => return Ok(None),
|
||
|
|
}))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[cfg(test)]
|
||
|
|
#[cfg(feature = "read")]
|
||
|
|
mod tests {
|
||
|
|
use super::*;
|
||
|
|
use crate::arch::X86_64;
|
||
|
|
use crate::read;
|
||
|
|
use crate::write::EndianVec;
|
||
|
|
use crate::{LittleEndian, Vendor};
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_frame_table() {
|
||
|
|
for &version in &[1, 3, 4] {
|
||
|
|
for &address_size in &[4, 8] {
|
||
|
|
for &format in &[Format::Dwarf32, Format::Dwarf64] {
|
||
|
|
let encoding = Encoding {
|
||
|
|
format,
|
||
|
|
version,
|
||
|
|
address_size,
|
||
|
|
};
|
||
|
|
let mut frames = FrameTable::default();
|
||
|
|
|
||
|
|
let cie1 = CommonInformationEntry::new(encoding, 1, 8, X86_64::RA);
|
||
|
|
let cie1_id = frames.add_cie(cie1.clone());
|
||
|
|
assert_eq!(cie1_id, frames.add_cie(cie1.clone()));
|
||
|
|
|
||
|
|
let mut cie2 = CommonInformationEntry::new(encoding, 1, 8, X86_64::RA);
|
||
|
|
cie2.lsda_encoding = Some(constants::DW_EH_PE_absptr);
|
||
|
|
cie2.personality =
|
||
|
|
Some((constants::DW_EH_PE_absptr, Address::Constant(0x1234)));
|
||
|
|
cie2.signal_trampoline = true;
|
||
|
|
let cie2_id = frames.add_cie(cie2.clone());
|
||
|
|
assert_ne!(cie1_id, cie2_id);
|
||
|
|
assert_eq!(cie2_id, frames.add_cie(cie2.clone()));
|
||
|
|
|
||
|
|
let fde1 = FrameDescriptionEntry::new(Address::Constant(0x1000), 0x10);
|
||
|
|
frames.add_fde(cie1_id, fde1.clone());
|
||
|
|
|
||
|
|
let fde2 = FrameDescriptionEntry::new(Address::Constant(0x2000), 0x20);
|
||
|
|
frames.add_fde(cie1_id, fde2.clone());
|
||
|
|
|
||
|
|
let mut fde3 = FrameDescriptionEntry::new(Address::Constant(0x3000), 0x30);
|
||
|
|
fde3.lsda = Some(Address::Constant(0x3300));
|
||
|
|
frames.add_fde(cie2_id, fde3.clone());
|
||
|
|
|
||
|
|
let mut fde4 = FrameDescriptionEntry::new(Address::Constant(0x4000), 0x40);
|
||
|
|
fde4.lsda = Some(Address::Constant(0x4400));
|
||
|
|
frames.add_fde(cie2_id, fde4.clone());
|
||
|
|
|
||
|
|
let mut cie3 = CommonInformationEntry::new(encoding, 1, 8, X86_64::RA);
|
||
|
|
cie3.fde_address_encoding = constants::DW_EH_PE_pcrel;
|
||
|
|
cie3.lsda_encoding = Some(constants::DW_EH_PE_pcrel);
|
||
|
|
cie3.personality = Some((constants::DW_EH_PE_pcrel, Address::Constant(0x1235)));
|
||
|
|
cie3.signal_trampoline = true;
|
||
|
|
let cie3_id = frames.add_cie(cie3.clone());
|
||
|
|
assert_ne!(cie2_id, cie3_id);
|
||
|
|
assert_eq!(cie3_id, frames.add_cie(cie3.clone()));
|
||
|
|
|
||
|
|
let mut fde5 = FrameDescriptionEntry::new(Address::Constant(0x5000), 0x50);
|
||
|
|
fde5.lsda = Some(Address::Constant(0x5500));
|
||
|
|
frames.add_fde(cie3_id, fde5.clone());
|
||
|
|
|
||
|
|
// Test writing `.debug_frame`.
|
||
|
|
let mut debug_frame = DebugFrame::from(EndianVec::new(LittleEndian));
|
||
|
|
frames.write_debug_frame(&mut debug_frame).unwrap();
|
||
|
|
|
||
|
|
let mut read_debug_frame =
|
||
|
|
read::DebugFrame::new(debug_frame.slice(), LittleEndian);
|
||
|
|
read_debug_frame.set_address_size(address_size);
|
||
|
|
let convert_frames = FrameTable::from(&read_debug_frame, &|address| {
|
||
|
|
Some(Address::Constant(address))
|
||
|
|
})
|
||
|
|
.unwrap();
|
||
|
|
assert_eq!(frames.cies, convert_frames.cies);
|
||
|
|
assert_eq!(frames.fdes.len(), convert_frames.fdes.len());
|
||
|
|
for (a, b) in frames.fdes.iter().zip(convert_frames.fdes.iter()) {
|
||
|
|
assert_eq!(a.1, b.1);
|
||
|
|
}
|
||
|
|
|
||
|
|
if version == 1 {
|
||
|
|
// Test writing `.eh_frame`.
|
||
|
|
let mut eh_frame = EhFrame::from(EndianVec::new(LittleEndian));
|
||
|
|
frames.write_eh_frame(&mut eh_frame).unwrap();
|
||
|
|
|
||
|
|
let mut read_eh_frame = read::EhFrame::new(eh_frame.slice(), LittleEndian);
|
||
|
|
read_eh_frame.set_address_size(address_size);
|
||
|
|
let convert_frames = FrameTable::from(&read_eh_frame, &|address| {
|
||
|
|
Some(Address::Constant(address))
|
||
|
|
})
|
||
|
|
.unwrap();
|
||
|
|
assert_eq!(frames.cies, convert_frames.cies);
|
||
|
|
assert_eq!(frames.fdes.len(), convert_frames.fdes.len());
|
||
|
|
for (a, b) in frames.fdes.iter().zip(convert_frames.fdes.iter()) {
|
||
|
|
assert_eq!(a.1, b.1);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_frame_instruction() {
|
||
|
|
let mut expression = Expression::new();
|
||
|
|
expression.op_constu(0);
|
||
|
|
|
||
|
|
let cie_instructions = [
|
||
|
|
CallFrameInstruction::Cfa(X86_64::RSP, 8),
|
||
|
|
CallFrameInstruction::Offset(X86_64::RA, -8),
|
||
|
|
];
|
||
|
|
|
||
|
|
let fde_instructions = [
|
||
|
|
(0, CallFrameInstruction::Cfa(X86_64::RSP, 0)),
|
||
|
|
(0, CallFrameInstruction::Cfa(X86_64::RSP, -8)),
|
||
|
|
(2, CallFrameInstruction::CfaRegister(X86_64::RBP)),
|
||
|
|
(4, CallFrameInstruction::CfaOffset(8)),
|
||
|
|
(4, CallFrameInstruction::CfaOffset(0)),
|
||
|
|
(4, CallFrameInstruction::CfaOffset(-8)),
|
||
|
|
(6, CallFrameInstruction::CfaExpression(expression.clone())),
|
||
|
|
(8, CallFrameInstruction::Restore(Register(1))),
|
||
|
|
(8, CallFrameInstruction::Restore(Register(101))),
|
||
|
|
(10, CallFrameInstruction::Undefined(Register(2))),
|
||
|
|
(12, CallFrameInstruction::SameValue(Register(3))),
|
||
|
|
(14, CallFrameInstruction::Offset(Register(4), 16)),
|
||
|
|
(14, CallFrameInstruction::Offset(Register(104), 16)),
|
||
|
|
(16, CallFrameInstruction::ValOffset(Register(5), -24)),
|
||
|
|
(16, CallFrameInstruction::ValOffset(Register(5), 24)),
|
||
|
|
(18, CallFrameInstruction::Register(Register(6), Register(7))),
|
||
|
|
(
|
||
|
|
20,
|
||
|
|
CallFrameInstruction::Expression(Register(8), expression.clone()),
|
||
|
|
),
|
||
|
|
(
|
||
|
|
22,
|
||
|
|
CallFrameInstruction::ValExpression(Register(9), expression.clone()),
|
||
|
|
),
|
||
|
|
(24 + 0x80, CallFrameInstruction::RememberState),
|
||
|
|
(26 + 0x280, CallFrameInstruction::RestoreState),
|
||
|
|
(28 + 0x20280, CallFrameInstruction::ArgsSize(23)),
|
||
|
|
];
|
||
|
|
|
||
|
|
let fde_instructions_aarch64 = [(0, CallFrameInstruction::NegateRaState)];
|
||
|
|
|
||
|
|
for &version in &[1, 3, 4] {
|
||
|
|
for &address_size in &[4, 8] {
|
||
|
|
for &vendor in &[Vendor::Default, Vendor::AArch64] {
|
||
|
|
for &format in &[Format::Dwarf32, Format::Dwarf64] {
|
||
|
|
let encoding = Encoding {
|
||
|
|
format,
|
||
|
|
version,
|
||
|
|
address_size,
|
||
|
|
};
|
||
|
|
let mut frames = FrameTable::default();
|
||
|
|
|
||
|
|
let mut cie = CommonInformationEntry::new(encoding, 2, 8, X86_64::RA);
|
||
|
|
for i in &cie_instructions {
|
||
|
|
cie.add_instruction(i.clone());
|
||
|
|
}
|
||
|
|
let cie_id = frames.add_cie(cie);
|
||
|
|
|
||
|
|
let mut fde = FrameDescriptionEntry::new(Address::Constant(0x1000), 0x10);
|
||
|
|
for (o, i) in &fde_instructions {
|
||
|
|
fde.add_instruction(*o, i.clone());
|
||
|
|
}
|
||
|
|
frames.add_fde(cie_id, fde);
|
||
|
|
|
||
|
|
if vendor == Vendor::AArch64 {
|
||
|
|
let mut fde =
|
||
|
|
FrameDescriptionEntry::new(Address::Constant(0x2000), 0x10);
|
||
|
|
for (o, i) in &fde_instructions_aarch64 {
|
||
|
|
fde.add_instruction(*o, i.clone());
|
||
|
|
}
|
||
|
|
frames.add_fde(cie_id, fde);
|
||
|
|
}
|
||
|
|
|
||
|
|
let mut debug_frame = DebugFrame::from(EndianVec::new(LittleEndian));
|
||
|
|
frames.write_debug_frame(&mut debug_frame).unwrap();
|
||
|
|
|
||
|
|
let mut read_debug_frame =
|
||
|
|
read::DebugFrame::new(debug_frame.slice(), LittleEndian);
|
||
|
|
read_debug_frame.set_address_size(address_size);
|
||
|
|
read_debug_frame.set_vendor(vendor);
|
||
|
|
let frames = FrameTable::from(&read_debug_frame, &|address| {
|
||
|
|
Some(Address::Constant(address))
|
||
|
|
})
|
||
|
|
.unwrap();
|
||
|
|
|
||
|
|
assert_eq!(
|
||
|
|
&frames.cies.get_index(0).unwrap().instructions,
|
||
|
|
&cie_instructions
|
||
|
|
);
|
||
|
|
assert_eq!(&frames.fdes[0].1.instructions, &fde_instructions);
|
||
|
|
if vendor == Vendor::AArch64 {
|
||
|
|
assert_eq!(&frames.fdes[1].1.instructions, &fde_instructions_aarch64);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|