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