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