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