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