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