sway_core/debug_generation/
dwarf.rs1use std::fs::File;
2use std::os::unix::ffi::OsStringExt;
3use std::path::Path;
4
5use gimli::write::{
6 self, DebugLine, DebugLineStrOffsets, DebugStrOffsets, DwarfUnit, EndianVec, LineProgram,
7 LineString,
8};
9use gimli::{BigEndian, Encoding, LineEncoding};
10use sway_error::error::CompileError;
11use sway_types::Span;
12
13use crate::source_map::SourceMap;
14
15use object::write::Object;
16
17pub fn write_dwarf(
18 source_map: &SourceMap,
19 primary_dir: &Path,
20 primary_src: &Path,
21 out_file: &Path,
22) -> Result<(), CompileError> {
23 let encoding = gimli::Encoding {
24 format: gimli::Format::Dwarf64,
25 version: 5,
26 address_size: 8,
27 };
28
29 let program = build_line_number_program(encoding, primary_dir, primary_src, source_map)?;
30
31 program
32 .write(
33 &mut DebugLine::from(EndianVec::new(BigEndian)),
34 encoding,
35 &DebugLineStrOffsets::none(),
36 &DebugStrOffsets::none(),
37 )
38 .map_err(|err| {
39 sway_error::error::CompileError::InternalOwned(err.to_string(), Span::dummy())
40 })?;
41
42 let mut dwarf = DwarfUnit::new(encoding);
43 dwarf.unit.line_program = program;
44 let mut debug_sections = write::Sections::new(EndianVec::new(BigEndian));
46 dwarf.write(&mut debug_sections).map_err(|err| {
47 sway_error::error::CompileError::InternalOwned(err.to_string(), Span::dummy())
48 })?;
49
50 if let Some(parent) = out_file.parent() {
52 std::fs::create_dir_all(parent).map_err(|err| {
53 sway_error::error::CompileError::InternalOwned(err.to_string(), Span::dummy())
54 })?;
55 }
56 let file = File::create(out_file).map_err(|err| {
57 sway_error::error::CompileError::InternalOwned(err.to_string(), Span::dummy())
58 })?;
59 let mut obj = Object::new(
60 object::BinaryFormat::Elf,
61 object::Architecture::X86_64,
62 object::Endianness::Big,
63 );
64 debug_sections
65 .for_each(|section_id, data| {
66 let sec = obj.add_section(
67 [].into(),
68 section_id.name().into(),
69 object::SectionKind::Other,
70 );
71 obj.set_section_data(sec, data.clone().into_vec(), 8);
72 Ok::<(), ()>(())
73 })
74 .unwrap();
75
76 obj.write_stream(file).map_err(|err| {
77 sway_error::error::CompileError::InternalOwned(err.to_string(), Span::dummy())
78 })?;
79
80 Ok(())
81}
82
83fn build_line_number_program(
84 encoding: Encoding,
85 primary_dir: &Path,
86 primary_src: &Path,
87 source_map: &SourceMap,
88) -> Result<LineProgram, CompileError> {
89 let primary_src = primary_src.strip_prefix(primary_dir).map_err(|err| {
90 sway_error::error::CompileError::InternalOwned(err.to_string(), Span::dummy())
91 })?;
92 let mut program = LineProgram::new(
93 encoding,
94 LineEncoding::default(),
95 LineString::String(primary_dir.to_path_buf().into_os_string().into_vec()),
96 LineString::String(primary_src.to_path_buf().into_os_string().into_vec()),
97 None,
98 );
99
100 program.begin_sequence(Some(write::Address::Constant(0)));
101
102 for (ix, span) in &source_map.map {
103 let (path, span) = span.to_span(&source_map.paths, &source_map.dependency_paths);
104
105 let dir = path
106 .parent()
107 .ok_or(sway_error::error::CompileError::InternalOwned(
108 "Path doesn't have a proper prefix".to_string(),
109 Span::dummy(),
110 ))?;
111 let file = path
112 .file_name()
113 .ok_or(sway_error::error::CompileError::InternalOwned(
114 "Path doesn't have proper filename".to_string(),
115 Span::dummy(),
116 ))?;
117
118 let dir_id = program.add_directory(LineString::String(
119 dir.as_os_str().as_encoded_bytes().into(),
120 ));
121 let file_id = program.add_file(
122 LineString::String(file.as_encoded_bytes().into()),
123 dir_id,
124 None,
125 );
126
127 program.generate_row();
128
129 let current_row = program.row();
130 current_row.line = span.start.line as u64;
131 current_row.column = span.start.col as u64;
132 current_row.address_offset = *ix as u64;
133 current_row.file = file_id;
134 }
135
136 program.end_sequence(
137 source_map
138 .map
139 .last_key_value()
140 .map(|(key, _)| *key)
141 .unwrap_or_default() as u64
142 + 1,
143 );
144
145 Ok(program)
146}