wasmtime_cranelift/debug/
write_debuginfo.rs

1pub use crate::debug::transform::transform_dwarf;
2use crate::debug::Compilation;
3use cranelift_codegen::ir::Endianness;
4use cranelift_codegen::isa::{
5    unwind::{CfaUnwindInfo, UnwindInfo},
6    TargetIsa,
7};
8use gimli::write::{Address, Dwarf, EndianVec, FrameTable, Result, Sections, Writer};
9use gimli::{RunTimeEndian, SectionId};
10
11pub struct DwarfSection {
12    pub name: &'static str,
13    pub body: Vec<u8>,
14    pub relocs: Vec<DwarfSectionReloc>,
15}
16
17#[derive(Clone)]
18pub struct DwarfSectionReloc {
19    pub target: DwarfSectionRelocTarget,
20    pub offset: u32,
21    pub addend: i32,
22    pub size: u8,
23}
24
25#[derive(Clone)]
26pub enum DwarfSectionRelocTarget {
27    Func(usize),
28    Section(&'static str),
29}
30
31fn emit_dwarf_sections(
32    isa: &dyn TargetIsa,
33    mut dwarf: Dwarf,
34    frames: Option<FrameTable>,
35) -> anyhow::Result<Vec<DwarfSection>> {
36    let endian = match isa.endianness() {
37        Endianness::Little => RunTimeEndian::Little,
38        Endianness::Big => RunTimeEndian::Big,
39    };
40    let writer = WriterRelocate {
41        relocs: Vec::new(),
42        writer: EndianVec::new(endian),
43    };
44    let mut sections = Sections::new(writer);
45    dwarf.write(&mut sections)?;
46    if let Some(frames) = frames {
47        frames.write_debug_frame(&mut sections.debug_frame)?;
48    }
49
50    let mut result = Vec::new();
51    sections.for_each_mut(|id, s| -> anyhow::Result<()> {
52        let name = id.name();
53        let body = s.writer.take();
54        if body.is_empty() {
55            return Ok(());
56        }
57        let mut relocs = vec![];
58        ::std::mem::swap(&mut relocs, &mut s.relocs);
59        result.push(DwarfSection { name, body, relocs });
60        Ok(())
61    })?;
62
63    Ok(result)
64}
65
66#[derive(Clone)]
67pub struct WriterRelocate {
68    relocs: Vec<DwarfSectionReloc>,
69    writer: EndianVec<RunTimeEndian>,
70}
71
72impl Writer for WriterRelocate {
73    type Endian = RunTimeEndian;
74
75    fn endian(&self) -> Self::Endian {
76        self.writer.endian()
77    }
78
79    fn len(&self) -> usize {
80        self.writer.len()
81    }
82
83    fn write(&mut self, bytes: &[u8]) -> Result<()> {
84        self.writer.write(bytes)
85    }
86
87    fn write_at(&mut self, offset: usize, bytes: &[u8]) -> Result<()> {
88        self.writer.write_at(offset, bytes)
89    }
90
91    fn write_address(&mut self, address: Address, size: u8) -> Result<()> {
92        match address {
93            Address::Constant(val) => self.write_udata(val, size),
94            Address::Symbol { symbol, addend } => {
95                let offset = self.len() as u32;
96                self.relocs.push(DwarfSectionReloc {
97                    target: DwarfSectionRelocTarget::Func(symbol),
98                    offset,
99                    size,
100                    addend: addend as i32,
101                });
102                self.write_udata(addend as u64, size)
103            }
104        }
105    }
106
107    fn write_offset(&mut self, val: usize, section: SectionId, size: u8) -> Result<()> {
108        let offset = self.len() as u32;
109        let target = DwarfSectionRelocTarget::Section(section.name());
110        self.relocs.push(DwarfSectionReloc {
111            target,
112            offset,
113            size,
114            addend: val as i32,
115        });
116        self.write_udata(val as u64, size)
117    }
118
119    fn write_offset_at(
120        &mut self,
121        offset: usize,
122        val: usize,
123        section: SectionId,
124        size: u8,
125    ) -> Result<()> {
126        let target = DwarfSectionRelocTarget::Section(section.name());
127        self.relocs.push(DwarfSectionReloc {
128            target,
129            offset: offset as u32,
130            size,
131            addend: val as i32,
132        });
133        self.write_udata_at(offset, val as u64, size)
134    }
135}
136
137fn create_frame_table(
138    isa: &dyn TargetIsa,
139    compilation: &mut Compilation<'_>,
140) -> Option<FrameTable> {
141    let mut table = FrameTable::default();
142
143    let cie_id = table.add_cie(isa.create_systemv_cie()?);
144
145    for (_, symbol, metadata) in compilation.functions() {
146        // The CFA-based unwind info will either be natively present, or we
147        // have generated it and placed into the "cfa_unwind_info" auxiliary
148        // field. We shouldn't emit both, though, it'd be wasteful.
149        let mut unwind_info: Option<&CfaUnwindInfo> = None;
150        if let Some(UnwindInfo::SystemV(info)) = &metadata.unwind_info {
151            debug_assert!(metadata.cfa_unwind_info.is_none());
152            unwind_info = Some(info);
153        } else if let Some(info) = &metadata.cfa_unwind_info {
154            unwind_info = Some(info);
155        }
156
157        if let Some(info) = unwind_info {
158            table.add_fde(cie_id, info.to_fde(Address::Symbol { symbol, addend: 0 }));
159        }
160    }
161
162    Some(table)
163}
164
165pub fn emit_dwarf(
166    isa: &dyn TargetIsa,
167    compilation: &mut Compilation<'_>,
168) -> anyhow::Result<Vec<DwarfSection>> {
169    let dwarf = transform_dwarf(isa, compilation)?;
170    let frame_table = create_frame_table(isa, compilation);
171    let sections = emit_dwarf_sections(isa, dwarf, frame_table)?;
172    Ok(sections)
173}