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