Skip to content

Commit 160ad08

Browse files
committed
write/cfi: implement .eh_frame
1 parent 68ff099 commit 160ad08

File tree

2 files changed

+140
-41
lines changed

2 files changed

+140
-41
lines changed

src/write/cfi.rs

Lines changed: 111 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use indexmap::IndexSet;
44
use std::ops::{Deref, DerefMut};
55

66
use crate::collections::HashMap;
7-
use crate::common::{DebugFrameOffset, Encoding, Format, Register, SectionId};
7+
use crate::common::{DebugFrameOffset, EhFrameOffset, Encoding, Format, Register, SectionId};
88
use crate::constants;
99
use crate::write::{Address, BaseId, Error, Expression, Result, Section, Writer};
1010

@@ -14,6 +14,8 @@ define_section!(
1414
"A writable `.debug_frame` section."
1515
);
1616

17+
define_section!(EhFrame, EhFrameOffset, "A writable `.eh_frame` section.");
18+
1719
define_id!(CieId, "An identifier for a CIE in a `FrameTable`.");
1820

1921
/// A table of frame description entries.
@@ -36,6 +38,11 @@ impl FrameTable {
3638
CieId::new(self.base_id, index)
3739
}
3840

41+
/// The number of CIEs.
42+
pub fn cie_count(&self) -> usize {
43+
self.cies.len()
44+
}
45+
3946
/// Add a FDE.
4047
///
4148
/// Does not check for duplicates.
@@ -48,8 +55,22 @@ impl FrameTable {
4855
self.fdes.push((cie, fde));
4956
}
5057

51-
/// Write the frame table entries to the given section.
52-
pub fn write<W: Writer>(&self, w: &mut DebugFrame<W>) -> Result<()> {
58+
/// The number of FDEs.
59+
pub fn fde_count(&self) -> usize {
60+
self.fdes.len()
61+
}
62+
63+
/// Write the frame table entries to the given `.debug_frame` section.
64+
pub fn write_debug_frame<W: Writer>(&self, w: &mut DebugFrame<W>) -> Result<()> {
65+
self.write(&mut w.0, false)
66+
}
67+
68+
/// Write the frame table entries to the given `.eh_frame` section.
69+
pub fn write_eh_frame<W: Writer>(&self, w: &mut EhFrame<W>) -> Result<()> {
70+
self.write(&mut w.0, true)
71+
}
72+
73+
fn write<W: Writer>(&self, w: &mut W, eh_frame: bool) -> Result<()> {
5374
let mut cie_offsets = vec![None; self.cies.len()];
5475
for (cie_id, fde) in &self.fdes {
5576
let cie_index = cie_id.index;
@@ -58,13 +79,13 @@ impl FrameTable {
5879
Some(offset) => offset,
5980
None => {
6081
// Only write CIEs as they are referenced.
61-
let offset = cie.write(w)?;
82+
let offset = cie.write(w, eh_frame)?;
6283
cie_offsets[cie_index] = Some(offset);
6384
offset
6485
}
6586
};
6687

67-
fde.write(w, cie_offset, cie)?;
88+
fde.write(w, eh_frame, cie_offset, cie)?;
6889
}
6990
// TODO: write length 0 terminator for eh_frame?
7091
Ok(())
@@ -144,22 +165,33 @@ impl CommonInformationEntry {
144165
|| self.fde_address_encoding != constants::DW_EH_PE_absptr
145166
}
146167

147-
fn write<W: Writer>(&self, w: &mut DebugFrame<W>) -> Result<DebugFrameOffset> {
168+
/// Returns the section offset of the CIE.
169+
fn write<W: Writer>(&self, w: &mut W, eh_frame: bool) -> Result<usize> {
148170
let encoding = self.encoding;
149-
let offset = w.offset();
171+
let offset = w.len();
150172

151173
let length_offset = w.write_initial_length(encoding.format)?;
152174
let length_base = w.len();
153175

154-
match encoding.format {
155-
Format::Dwarf32 => w.write_u32(0xffff_ffff)?,
156-
Format::Dwarf64 => w.write_u64(0xffff_ffff_ffff_ffff)?,
176+
if eh_frame {
177+
w.write_u32(0)?;
178+
} else {
179+
match encoding.format {
180+
Format::Dwarf32 => w.write_u32(0xffff_ffff)?,
181+
Format::Dwarf64 => w.write_u64(0xffff_ffff_ffff_ffff)?,
182+
}
157183
}
158184

159-
match encoding.version {
160-
1 | 3 | 4 => {}
161-
_ => return Err(Error::UnsupportedVersion(encoding.version)),
162-
};
185+
if eh_frame {
186+
if encoding.version != 1 {
187+
return Err(Error::UnsupportedVersion(encoding.version));
188+
};
189+
} else {
190+
match encoding.version {
191+
1 | 3 | 4 => {}
192+
_ => return Err(Error::UnsupportedVersion(encoding.version)),
193+
};
194+
}
163195
w.write_u8(encoding.version as u8)?;
164196

165197
let augmentation = self.has_augmentation();
@@ -189,8 +221,7 @@ impl CommonInformationEntry {
189221
w.write_uleb128(self.code_alignment_factor.into())?;
190222
w.write_sleb128(self.data_alignment_factor.into())?;
191223

192-
// TODO: eh_frame encoding
193-
if encoding.version == 1 {
224+
if !eh_frame && encoding.version == 1 {
194225
let register = self.return_address_register.0 as u8;
195226
if u16::from(register) != self.return_address_register.0 {
196227
return Err(Error::ValueTooLarge);
@@ -275,20 +306,25 @@ impl FrameDescriptionEntry {
275306

276307
fn write<W: Writer>(
277308
&self,
278-
w: &mut DebugFrame<W>,
279-
cie_offset: DebugFrameOffset,
309+
w: &mut W,
310+
eh_frame: bool,
311+
cie_offset: usize,
280312
cie: &CommonInformationEntry,
281313
) -> Result<()> {
282314
let encoding = cie.encoding;
283315
let length_offset = w.write_initial_length(encoding.format)?;
284316
let length_base = w.len();
285317

286-
// TODO: eh_frame encoding
287-
w.write_offset(
288-
cie_offset.0,
289-
SectionId::DebugFrame,
290-
encoding.format.word_size(),
291-
)?;
318+
if eh_frame {
319+
// .eh_frame uses a relative offset which doesn't need relocation.
320+
w.write_word((w.len() - cie_offset) as u64, 4)?;
321+
} else {
322+
w.write_offset(
323+
cie_offset,
324+
SectionId::DebugFrame,
325+
encoding.format.word_size(),
326+
)?;
327+
}
292328

293329
if cie.fde_address_encoding != constants::DW_EH_PE_absptr {
294330
w.write_eh_pointer(
@@ -379,7 +415,7 @@ pub enum CallFrameInstruction {
379415
}
380416

381417
impl CallFrameInstruction {
382-
fn write<W: Writer>(&self, w: &mut DebugFrame<W>, cie: &CommonInformationEntry) -> Result<()> {
418+
fn write<W: Writer>(&self, w: &mut W, cie: &CommonInformationEntry) -> Result<()> {
383419
match *self {
384420
CallFrameInstruction::Cfa(register, offset) => {
385421
if offset < 0 {
@@ -490,7 +526,7 @@ impl CallFrameInstruction {
490526
}
491527

492528
fn write_advance_loc<W: Writer>(
493-
w: &mut DebugFrame<W>,
529+
w: &mut W,
494530
code_alignment_factor: u8,
495531
prev_offset: u32,
496532
offset: u32,
@@ -514,7 +550,7 @@ fn write_advance_loc<W: Writer>(
514550
Ok(())
515551
}
516552

517-
fn write_nop<W: Writer>(w: &mut DebugFrame<W>, len: usize, align: u8) -> Result<()> {
553+
fn write_nop<W: Writer>(w: &mut W, len: usize, align: u8) -> Result<()> {
518554
debug_assert_eq!(align & (align - 1), 0);
519555
let tail_len = (!len + 1) & (align as usize - 1);
520556
for _ in 0..tail_len {
@@ -548,7 +584,7 @@ fn factored_data_offset(offset: i32, factor: i8) -> Result<i32> {
548584
#[cfg(feature = "read")]
549585
pub(crate) mod convert {
550586
use super::*;
551-
use crate::read::{self, Reader, UnwindSection};
587+
use crate::read::{self, Reader};
552588
use crate::write::{ConvertError, ConvertResult};
553589

554590
impl FrameTable {
@@ -559,11 +595,16 @@ pub(crate) mod convert {
559595
/// `Address::Constant(address)`. For relocatable addresses, it is the caller's
560596
/// responsibility to determine the symbol and addend corresponding to the address
561597
/// and return `Address::Symbol { symbol, addend }`.
562-
pub fn from<R: Reader<Offset = usize>>(
563-
frame: &read::DebugFrame<R>,
598+
pub fn from<R, Section>(
599+
frame: &Section,
564600
convert_address: &dyn Fn(u64) -> Option<Address>,
565-
) -> ConvertResult<FrameTable> {
566-
let bases = read::BaseAddresses::default();
601+
) -> ConvertResult<FrameTable>
602+
where
603+
R: Reader<Offset = usize>,
604+
Section: read::UnwindSection<R>,
605+
Section::Offset: read::UnwindOffset<usize>,
606+
{
607+
let bases = read::BaseAddresses::default().set_eh_frame(0);
567608

568609
let mut frame_table = FrameTable::default();
569610

@@ -577,7 +618,7 @@ pub(crate) mod convert {
577618

578619
// TODO: is it worth caching the parsed CIEs? It would be better if FDEs only
579620
// stored a reference.
580-
let from_fde = partial.parse(read::DebugFrame::cie_from_offset)?;
621+
let from_fde = partial.parse(Section::cie_from_offset)?;
581622
let from_cie = from_fde.cie();
582623
let cie_id = match cie_ids.entry(from_cie.offset()) {
583624
hash_map::Entry::Occupied(o) => *o.get(),
@@ -598,12 +639,17 @@ pub(crate) mod convert {
598639
}
599640

600641
impl CommonInformationEntry {
601-
fn from<R: Reader<Offset = usize>>(
642+
fn from<R, Section>(
602643
from_cie: &read::CommonInformationEntry<R>,
603-
frame: &read::DebugFrame<R>,
644+
frame: &Section,
604645
bases: &read::BaseAddresses,
605646
convert_address: &dyn Fn(u64) -> Option<Address>,
606-
) -> ConvertResult<CommonInformationEntry> {
647+
) -> ConvertResult<CommonInformationEntry>
648+
where
649+
R: Reader<Offset = usize>,
650+
Section: read::UnwindSection<R>,
651+
Section::Offset: read::UnwindOffset<usize>,
652+
{
607653
let mut cie = CommonInformationEntry::new(
608654
from_cie.encoding(),
609655
from_cie.code_alignment_factor() as u8,
@@ -641,12 +687,17 @@ pub(crate) mod convert {
641687
}
642688

643689
impl FrameDescriptionEntry {
644-
fn from<R: Reader<Offset = usize>>(
690+
fn from<R, Section>(
645691
from_fde: &read::FrameDescriptionEntry<R>,
646-
frame: &read::DebugFrame<R>,
692+
frame: &Section,
647693
bases: &read::BaseAddresses,
648694
convert_address: &dyn Fn(u64) -> Option<Address>,
649-
) -> ConvertResult<FrameDescriptionEntry> {
695+
) -> ConvertResult<FrameDescriptionEntry>
696+
where
697+
R: Reader<Offset = usize>,
698+
Section: read::UnwindSection<R>,
699+
Section::Offset: read::UnwindOffset<usize>,
700+
{
650701
let address =
651702
convert_address(from_fde.initial_address()).ok_or(ConvertError::InvalidAddress)?;
652703
let length = from_fde.len() as u32;
@@ -830,8 +881,9 @@ mod tests {
830881
fde4.lsda = Some(Address::Constant(0x4400));
831882
frames.add_fde(cie2_id, fde4.clone());
832883

884+
// Test writing `.debug_frame`.
833885
let mut debug_frame = DebugFrame::from(EndianVec::new(LittleEndian));
834-
frames.write(&mut debug_frame).unwrap();
886+
frames.write_debug_frame(&mut debug_frame).unwrap();
835887

836888
let mut read_debug_frame =
837889
read::DebugFrame::new(debug_frame.slice(), LittleEndian);
@@ -845,6 +897,24 @@ mod tests {
845897
for (a, b) in frames.fdes.iter().zip(convert_frames.fdes.iter()) {
846898
assert_eq!(a.1, b.1);
847899
}
900+
901+
if version == 1 {
902+
// Test writing `.eh_frame`.
903+
let mut eh_frame = EhFrame::from(EndianVec::new(LittleEndian));
904+
frames.write_eh_frame(&mut eh_frame).unwrap();
905+
906+
let mut read_eh_frame = read::EhFrame::new(eh_frame.slice(), LittleEndian);
907+
read_eh_frame.set_address_size(address_size);
908+
let convert_frames = FrameTable::from(&read_eh_frame, &|address| {
909+
Some(Address::Constant(address))
910+
})
911+
.unwrap();
912+
assert_eq!(frames.cies, convert_frames.cies);
913+
assert_eq!(frames.fdes.len(), convert_frames.fdes.len());
914+
for (a, b) in frames.fdes.iter().zip(convert_frames.fdes.iter()) {
915+
assert_eq!(a.1, b.1);
916+
}
917+
}
848918
}
849919
}
850920
}
@@ -913,7 +983,7 @@ mod tests {
913983
frames.add_fde(cie_id, fde);
914984

915985
let mut debug_frame = DebugFrame::from(EndianVec::new(LittleEndian));
916-
frames.write(&mut debug_frame).unwrap();
986+
frames.write_debug_frame(&mut debug_frame).unwrap();
917987

918988
let mut read_debug_frame =
919989
read::DebugFrame::new(debug_frame.slice(), LittleEndian);

tests/convert_self.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,32 @@ fn test_convert_debug_info() {
111111
assert_eq!(entries, 29_560);
112112
assert_eq!(dwarf.strings.count(), 3921);
113113
}
114+
115+
#[test]
116+
fn test_convert_eh_frame() {
117+
// Convert existing section
118+
let eh_frame = read_section("eh_frame");
119+
let mut eh_frame = read::EhFrame::new(&eh_frame, LittleEndian);
120+
// The `.eh_frame` fixture data was created on a 64-bit machine.
121+
eh_frame.set_address_size(8);
122+
let frames = write::FrameTable::from(&eh_frame, &|address| Some(Address::Constant(address)))
123+
.expect("Should convert eh_frame information");
124+
assert_eq!(frames.cie_count(), 2);
125+
assert_eq!(frames.fde_count(), 3482);
126+
127+
// Write to new section
128+
let mut write_eh_frame = write::EhFrame(EndianVec::new(LittleEndian));
129+
frames
130+
.write_eh_frame(&mut write_eh_frame)
131+
.expect("Should write eh_frame information");
132+
let eh_frame = write_eh_frame.slice();
133+
assert_eq!(eh_frame.len(), 147152);
134+
135+
// Convert new section
136+
let mut eh_frame = read::EhFrame::new(&eh_frame, LittleEndian);
137+
eh_frame.set_address_size(8);
138+
let frames = write::FrameTable::from(&eh_frame, &|address| Some(Address::Constant(address)))
139+
.expect("Should convert eh_frame information");
140+
assert_eq!(frames.cie_count(), 2);
141+
assert_eq!(frames.fde_count(), 3482);
142+
}

0 commit comments

Comments
 (0)