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::{Address, Dwarf, EndianVec, FrameTable, Result, Sections, Writer};
9 use gimli::{RunTimeEndian, SectionId};
10 
11 pub struct DwarfSection {
12     pub name: &'static str,
13     pub body: Vec<u8>,
14     pub relocs: Vec<DwarfSectionReloc>,
15 }
16 
17 #[derive(Clone)]
18 pub struct DwarfSectionReloc {
19     pub target: DwarfSectionRelocTarget,
20     pub offset: u32,
21     pub addend: i32,
22     pub size: u8,
23 }
24 
25 #[derive(Clone)]
26 pub enum DwarfSectionRelocTarget {
27     Func(usize),
28     Section(&'static str),
29 }
30 
31 fn emit_dwarf_sections(
32     isa: &dyn TargetIsa,
33     mut dwarf: Dwarf,
34     frames: Option<FrameTable>,
35 ) -> wasmtime_environ::error::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| -> wasmtime_environ::error::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)]
67 pub struct WriterRelocate {
68     relocs: Vec<DwarfSectionReloc>,
69     writer: EndianVec<RunTimeEndian>,
70 }
71 
72 impl 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 
137 fn 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 
165 pub fn emit_dwarf(
166     isa: &dyn TargetIsa,
167     compilation: &mut Compilation<'_>,
168 ) -> wasmtime_environ::error::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 }
174