sway_core/debug_generation/
dwarf.rs

1use 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    // Write to new sections
45    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    // Create parent directories if they don't exist
51    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}