1use gimli::{
10 Encoding, Format, LineEncoding, RunTimeEndian, SectionId, constants,
11 write::{
12 Address, AttributeValue, DwarfUnit, EndianVec, LineProgram, LineString,
13 Result as GimliResult, Sections, Writer,
14 },
15};
16use object::{
17 RelocationEncoding, RelocationFlags, RelocationKind, SectionKind,
18 write::{Object, Relocation, StandardSegment, SymbolId},
19};
20use wasmer_types::{CompileError, SourceLoc, target::Endianness};
21
22#[derive(Clone, Copy, Debug)]
25pub enum EhTarget {
26 Function,
28 Personality,
30 Lsda,
32}
33
34#[derive(Clone, Debug)]
36pub struct EhRelocation {
37 pub offset: u64,
39 pub kind: RelocationKind,
41 pub size: u8,
43 pub target: EhTarget,
45 pub addend: i64,
47}
48
49#[derive(Clone, Debug)]
52pub struct WriterRelocate {
53 pub relocs: Vec<EhRelocation>,
55 writer: EndianVec<RunTimeEndian>,
56}
57
58impl Default for WriterRelocate {
59 fn default() -> Self {
60 Self::new()
61 }
62}
63
64impl WriterRelocate {
65 pub const FUNCTION_SYMBOL: usize = 0;
67 pub const PERSONALITY_SYMBOL: usize = 1;
69 pub const LSDA_SYMBOL: usize = 2;
71
72 pub fn new() -> Self {
74 Self {
75 relocs: Vec::new(),
76 writer: EndianVec::new(RunTimeEndian::Little),
77 }
78 }
79
80 pub fn into_bytes(self) -> Vec<u8> {
82 self.writer.into_vec()
83 }
84
85 fn target_for(symbol: usize) -> GimliResult<EhTarget> {
86 match symbol {
87 Self::FUNCTION_SYMBOL => Ok(EhTarget::Function),
88 Self::PERSONALITY_SYMBOL => Ok(EhTarget::Personality),
89 Self::LSDA_SYMBOL => Ok(EhTarget::Lsda),
90 _ => Err(gimli::write::Error::InvalidAddress),
91 }
92 }
93}
94
95impl Writer for WriterRelocate {
96 type Endian = RunTimeEndian;
97
98 fn endian(&self) -> Self::Endian {
99 self.writer.endian()
100 }
101
102 fn len(&self) -> usize {
103 self.writer.len()
104 }
105
106 fn write(&mut self, bytes: &[u8]) -> GimliResult<()> {
107 self.writer.write(bytes)
108 }
109
110 fn write_at(&mut self, offset: usize, bytes: &[u8]) -> GimliResult<()> {
111 self.writer.write_at(offset, bytes)
112 }
113
114 fn write_address(&mut self, address: Address, size: u8) -> GimliResult<()> {
115 match address {
116 Address::Constant(val) => self.write_udata(val, size),
117 Address::Symbol { symbol, addend } => {
118 let target = Self::target_for(symbol)?;
119 let offset = self.len() as u64;
120 self.relocs.push(EhRelocation {
121 offset,
122 kind: RelocationKind::Absolute,
123 size,
124 target,
125 addend,
126 });
127 self.write_udata(0, size)
128 }
129 }
130 }
131
132 fn write_eh_pointer(
133 &mut self,
134 address: Address,
135 eh_pe: constants::DwEhPe,
136 size: u8,
137 ) -> GimliResult<()> {
138 if eh_pe == constants::DW_EH_PE_absptr {
139 return self.write_address(address, size);
140 }
141
142 match address {
143 Address::Constant(_) => self.writer.write_eh_pointer(address, eh_pe, size),
144 Address::Symbol { symbol, addend }
145 if eh_pe == (constants::DW_EH_PE_pcrel | constants::DW_EH_PE_sdata4)
146 && size == 8 =>
147 {
148 let target = Self::target_for(symbol)?;
149 let offset = self.len() as u64;
150 self.relocs.push(EhRelocation {
151 offset,
152 kind: RelocationKind::Relative,
153 size: 4,
154 target,
155 addend,
156 });
157 self.write_udata(0, 4)
158 }
159 Address::Symbol { symbol, addend }
165 if eh_pe
166 == (constants::DW_EH_PE_indirect
167 | constants::DW_EH_PE_pcrel
168 | constants::DW_EH_PE_sdata4)
169 && size == 8 =>
170 {
171 let target = Self::target_for(symbol)?;
172 let offset = self.len() as u64;
173 self.relocs.push(EhRelocation {
174 offset,
175 kind: RelocationKind::GotRelative,
176 size: 4,
177 target,
178 addend,
179 });
180 self.write_udata(0, 4)
181 }
182 Address::Symbol { .. } => Err(gimli::write::Error::InvalidAddress),
183 }
184 }
185
186 fn write_offset(&mut self, _val: usize, _section: SectionId, _size: u8) -> GimliResult<()> {
187 Err(gimli::write::Error::OffsetOutOfBounds)
188 }
189
190 fn write_offset_at(
191 &mut self,
192 _offset: usize,
193 _val: usize,
194 _section: SectionId,
195 _size: u8,
196 ) -> GimliResult<()> {
197 Err(gimli::write::Error::OffsetOutOfBounds)
198 }
199}
200
201#[derive(Clone, Debug)]
202struct DebugRelocation {
203 offset: u64,
204 size: u8,
205 target: DebugRelocationTarget,
206 addend: i64,
207}
208
209#[derive(Clone, Debug)]
210enum DebugRelocationTarget {
211 Function,
212 Section(SectionId),
213}
214
215#[derive(Clone, Debug)]
216struct DebugWriter {
217 relocs: Vec<DebugRelocation>,
218 writer: EndianVec<RunTimeEndian>,
219}
220
221impl DebugWriter {
222 fn new(_endianness: Option<Endianness>) -> Self {
223 Self {
224 relocs: Vec::new(),
225 writer: EndianVec::new(RunTimeEndian::Little),
226 }
227 }
228
229 fn into_parts(self) -> (Vec<u8>, Vec<DebugRelocation>) {
230 (self.writer.into_vec(), self.relocs)
231 }
232}
233
234impl Writer for DebugWriter {
235 type Endian = RunTimeEndian;
236
237 fn endian(&self) -> Self::Endian {
238 self.writer.endian()
239 }
240
241 fn len(&self) -> usize {
242 self.writer.len()
243 }
244
245 fn write(&mut self, bytes: &[u8]) -> GimliResult<()> {
246 self.writer.write(bytes)
247 }
248
249 fn write_at(&mut self, offset: usize, bytes: &[u8]) -> GimliResult<()> {
250 self.writer.write_at(offset, bytes)
251 }
252
253 fn write_address(&mut self, address: Address, size: u8) -> GimliResult<()> {
254 match address {
255 Address::Constant(val) => self.write_udata(val, size),
256 Address::Symbol { addend, .. } => {
257 let offset = self.len() as u64;
258 self.relocs.push(DebugRelocation {
259 offset,
260 size,
261 target: DebugRelocationTarget::Function,
262 addend,
263 });
264 self.write_udata(0, size)
265 }
266 }
267 }
268
269 fn write_offset(&mut self, val: usize, section: SectionId, size: u8) -> GimliResult<()> {
270 let offset = self.len() as u64;
271 self.relocs.push(DebugRelocation {
272 offset,
273 size,
274 target: DebugRelocationTarget::Section(section),
275 addend: val as i64,
276 });
277 self.write_udata(0, size)
278 }
279
280 fn write_offset_at(
281 &mut self,
282 offset: usize,
283 val: usize,
284 section: SectionId,
285 size: u8,
286 ) -> GimliResult<()> {
287 self.relocs.push(DebugRelocation {
288 offset: offset as u64,
289 size,
290 target: DebugRelocationTarget::Section(section),
291 addend: val as i64,
292 });
293 self.write_udata_at(offset, 0, size)
294 }
295}
296
297pub struct DwarfState {
299 dwarf: DwarfUnit,
300 file_id: gimli::write::FileId,
301 subprogram: gimli::write::UnitEntryId,
302}
303
304pub fn init_dwarf_unit(
306 function_name: &str,
307 module_name: Option<&str>,
308 producer: &str,
309) -> Result<DwarfState, CompileError> {
310 let encoding = Encoding {
311 address_size: 8,
312 format: Format::Dwarf32,
313 version: 4,
314 };
315 let mut dwarf = DwarfUnit::new(encoding);
316 let comp_dir = dwarf.strings.add(".");
317 let file_name_str = module_name.unwrap_or("<module>");
318 let file_name = dwarf.strings.add(file_name_str);
319 dwarf.unit.line_program = LineProgram::new(
320 encoding,
321 LineEncoding::default(),
322 LineString::String(b".".to_vec()),
323 None,
324 LineString::String(file_name_str.as_bytes().to_vec()),
325 None,
326 );
327 let dir_id = dwarf.unit.line_program.default_directory();
328 let file_id = dwarf.unit.line_program.add_file(
329 LineString::String(file_name_str.as_bytes().to_vec()),
330 dir_id,
331 None,
332 );
333
334 let function_address = Address::Symbol {
335 symbol: 0,
336 addend: 0,
337 };
338 dwarf
339 .unit
340 .line_program
341 .begin_sequence(Some(function_address));
342
343 let root = dwarf.unit.root();
344 let cu = dwarf.unit.get_mut(root);
345 cu.set(
346 gimli::DW_AT_producer,
347 AttributeValue::String(producer.as_bytes().to_vec()),
348 );
349 cu.set(
350 gimli::DW_AT_language,
351 AttributeValue::Language(gimli::DW_LANG_C),
352 );
353 cu.set(gimli::DW_AT_name, AttributeValue::StringRef(file_name));
354 cu.set(gimli::DW_AT_comp_dir, AttributeValue::StringRef(comp_dir));
355 cu.set(
356 gimli::DW_AT_low_pc,
357 AttributeValue::Address(function_address),
358 );
359
360 let subprogram = dwarf.unit.add(root, gimli::DW_TAG_subprogram);
361 let entry = dwarf.unit.get_mut(subprogram);
362 entry.set(
363 gimli::DW_AT_name,
364 AttributeValue::String(function_name.as_bytes().to_vec()),
365 );
366 entry.set(
367 gimli::DW_AT_decl_file,
368 AttributeValue::FileIndex(Some(file_id)),
369 );
370 entry.set(
371 gimli::DW_AT_low_pc,
372 AttributeValue::Address(function_address),
373 );
374
375 Ok(DwarfState {
376 dwarf,
377 file_id,
378 subprogram,
379 })
380}
381
382impl DwarfState {
383 pub fn add_row(&mut self, code_offset: u64, srcloc: SourceLoc) {
385 if srcloc.is_default() {
386 return;
387 }
388 let row = self.dwarf.unit.line_program.row();
389 row.address_offset = code_offset;
390 row.file = self.file_id;
391 row.line = (srcloc.bits() as u64).saturating_add(1);
392 row.column = 0;
393 self.dwarf.unit.line_program.generate_row();
394 }
395
396 pub fn write_sections(
398 &mut self,
399 object: &mut Object<'static>,
400 function_symbol: SymbolId,
401 body_len: u64,
402 endianness: Option<Endianness>,
403 ) -> Result<(), CompileError> {
404 self.dwarf.unit.line_program.end_sequence(body_len);
406
407 let root = self.dwarf.unit.root();
409 let cu = self.dwarf.unit.get_mut(root);
410 cu.set(gimli::DW_AT_high_pc, AttributeValue::Data8(body_len));
411 let entry = self.dwarf.unit.get_mut(self.subprogram);
412 entry.set(gimli::DW_AT_decl_line, AttributeValue::Udata(1));
413 entry.set(gimli::DW_AT_high_pc, AttributeValue::Data8(body_len));
414
415 let mut sections = Sections::new(DebugWriter::new(endianness));
416 self.dwarf
417 .write(&mut sections)
418 .map_err(|e| CompileError::Codegen(format!("failed to write DWARF debug info: {e}")))?;
419
420 let mut object_sections = Vec::new();
421 sections
422 .for_each(|id, writer| {
423 let (bytes, relocs) = writer.clone().into_parts();
424 if bytes.is_empty() {
425 object_sections.push((id, None, relocs));
426 } else {
427 let section = object.add_section(
428 object.segment_name(StandardSegment::Debug).to_vec(),
429 id.name().as_bytes().to_vec(),
430 SectionKind::Debug,
431 );
432 object.append_section_data(section, &bytes, 1);
433 object_sections.push((id, Some(section), relocs));
434 }
435 Ok::<_, gimli::write::Error>(())
436 })
437 .map_err(|e| CompileError::Codegen(format!("failed to collect DWARF sections: {e}")))?;
438
439 for (_, section, relocs) in object_sections.clone() {
440 let Some(section) = section else { continue };
441 for reloc in relocs {
442 let symbol = match reloc.target {
443 DebugRelocationTarget::Function => function_symbol,
444 DebugRelocationTarget::Section(target) => {
445 let Some((_, Some(target_section), _)) =
446 object_sections.iter().find(|(id, _, _)| *id == target)
447 else {
448 continue;
449 };
450 object.section_symbol(*target_section)
451 }
452 };
453 object
454 .add_relocation(
455 section,
456 Relocation {
457 offset: reloc.offset,
458 symbol,
459 addend: reloc.addend,
460 flags: RelocationFlags::Generic {
461 kind: RelocationKind::Absolute,
462 encoding: RelocationEncoding::Generic,
463 size: u8::checked_mul(reloc.size, 8).ok_or_else(|| {
464 CompileError::Codegen("unexpected relocation size".to_string())
465 })?,
466 },
467 },
468 )
469 .map_err(|e| {
470 CompileError::Codegen(format!("failed to add DWARF relocation: {e}"))
471 })?;
472 }
473 }
474
475 Ok(())
476 }
477}