wasm_debug/
read_debuginfo.rs

1use gimli::{
2    DebugAbbrev, DebugAddr, DebugInfo, DebugLine, DebugLineStr, DebugLoc, DebugLocLists,
3    DebugRanges, DebugRngLists, DebugStr, DebugStrOffsets, DebugTypes, EndianSlice, LittleEndian,
4    LocationLists, RangeLists,
5};
6use std::collections::HashMap;
7use std::path::PathBuf;
8use wasmparser::{self, ModuleReader, SectionCode};
9
10trait Reader: gimli::Reader<Offset = usize, Endian = LittleEndian> {}
11
12impl<'input> Reader for gimli::EndianSlice<'input, LittleEndian> {}
13
14pub use wasmparser::Type as WasmType;
15
16pub type Dwarf<'input> = gimli::Dwarf<gimli::EndianSlice<'input, LittleEndian>>;
17
18#[derive(Debug)]
19pub struct FunctionMetadata {
20    pub params: Box<[WasmType]>,
21    pub locals: Box<[(u32, WasmType)]>,
22}
23
24#[derive(Debug)]
25pub struct WasmFileInfo {
26    pub path: Option<PathBuf>,
27    pub code_section_offset: u64,
28    pub funcs: Box<[FunctionMetadata]>,
29}
30
31#[derive(Debug)]
32pub struct NameSection {
33    pub module_name: Option<String>,
34    pub func_names: HashMap<u32, String>,
35    pub locals_names: HashMap<u32, HashMap<u32, String>>,
36}
37
38#[derive(Debug)]
39pub struct DebugInfoData<'a> {
40    pub dwarf: Dwarf<'a>,
41    pub name_section: Option<NameSection>,
42    pub wasm_file: WasmFileInfo,
43}
44
45fn convert_sections<'a>(sections: HashMap<&str, &'a [u8]>) -> Dwarf<'a> {
46    const EMPTY_SECTION: &[u8] = &[];
47
48    let endian = LittleEndian;
49    let debug_str = DebugStr::new(sections.get(".debug_str").unwrap_or(&EMPTY_SECTION), endian);
50    let debug_abbrev = DebugAbbrev::new(
51        sections.get(".debug_abbrev").unwrap_or(&EMPTY_SECTION),
52        endian,
53    );
54    let debug_info = DebugInfo::new(
55        sections.get(".debug_info").unwrap_or(&EMPTY_SECTION),
56        endian,
57    );
58    let debug_line = DebugLine::new(
59        sections.get(".debug_line").unwrap_or(&EMPTY_SECTION),
60        endian,
61    );
62
63    if sections.contains_key(".debug_addr") {
64        panic!("Unexpected .debug_addr");
65    }
66
67    let debug_addr = DebugAddr::from(EndianSlice::new(EMPTY_SECTION, endian));
68
69    if sections.contains_key(".debug_line_str") {
70        panic!("Unexpected .debug_line_str");
71    }
72
73    let debug_line_str = DebugLineStr::from(EndianSlice::new(EMPTY_SECTION, endian));
74    let debug_str_sup = DebugStr::from(EndianSlice::new(EMPTY_SECTION, endian));
75
76    if sections.contains_key(".debug_rnglists") {
77        panic!("Unexpected .debug_rnglists");
78    }
79
80    let debug_ranges = match sections.get(".debug_ranges") {
81        Some(section) => DebugRanges::new(section, endian),
82        None => DebugRanges::new(EMPTY_SECTION, endian),
83    };
84    let debug_rnglists = DebugRngLists::new(EMPTY_SECTION, endian);
85    let ranges = RangeLists::new(debug_ranges, debug_rnglists);
86
87    if sections.contains_key(".debug_loclists") {
88        panic!("Unexpected .debug_loclists");
89    }
90
91    let debug_loc = match sections.get(".debug_loc") {
92        Some(section) => DebugLoc::new(section, endian),
93        None => DebugLoc::new(EMPTY_SECTION, endian),
94    };
95    let debug_loclists = DebugLocLists::new(EMPTY_SECTION, endian);
96    let locations = LocationLists::new(debug_loc, debug_loclists);
97
98    if sections.contains_key(".debug_str_offsets") {
99        panic!("Unexpected .debug_str_offsets");
100    }
101
102    let debug_str_offsets = DebugStrOffsets::from(EndianSlice::new(EMPTY_SECTION, endian));
103
104    if sections.contains_key(".debug_types") {
105        panic!("Unexpected .debug_types");
106    }
107
108    let debug_types = DebugTypes::from(EndianSlice::new(EMPTY_SECTION, endian));
109
110    Dwarf {
111        debug_abbrev,
112        debug_addr,
113        debug_info,
114        debug_line,
115        debug_line_str,
116        debug_str,
117        debug_str_offsets,
118        debug_str_sup,
119        debug_types,
120        locations,
121        ranges,
122    }
123}
124
125fn read_name_section(reader: wasmparser::NameSectionReader) -> wasmparser::Result<NameSection> {
126    let mut module_name = None;
127    let mut func_names = HashMap::new();
128    let mut locals_names = HashMap::new();
129    for i in reader.into_iter() {
130        match i? {
131            wasmparser::Name::Module(m) => {
132                module_name = Some(String::from(m.get_name()?));
133            }
134            wasmparser::Name::Function(f) => {
135                let mut reader = f.get_map()?;
136                while let Ok(naming) = reader.read() {
137                    func_names.insert(naming.index, String::from(naming.name));
138                }
139            }
140            wasmparser::Name::Local(l) => {
141                let mut reader = l.get_function_local_reader()?;
142                while let Ok(f) = reader.read() {
143                    let mut names = HashMap::new();
144                    let mut reader = f.get_map()?;
145                    while let Ok(naming) = reader.read() {
146                        names.insert(naming.index, String::from(naming.name));
147                    }
148                    locals_names.insert(f.func_index, names);
149                }
150            }
151        }
152    }
153    let result = NameSection {
154        module_name,
155        func_names,
156        locals_names,
157    };
158    Ok(result)
159}
160
161pub fn read_debuginfo(data: &[u8]) -> DebugInfoData {
162    let mut reader = ModuleReader::new(data).expect("reader");
163    let mut sections = HashMap::new();
164    let mut name_section = None;
165    let mut code_section_offset = 0;
166
167    let mut signatures_params: Vec<Box<[WasmType]>> = Vec::new();
168    let mut func_params_refs: Vec<usize> = Vec::new();
169    let mut func_locals: Vec<Box<[(u32, WasmType)]>> = Vec::new();
170
171    while !reader.eof() {
172        let section = reader.read().expect("section");
173        match section.code {
174            SectionCode::Custom { name, .. } => {
175                if name.starts_with(".debug_") {
176                    let mut reader = section.get_binary_reader();
177                    let len = reader.bytes_remaining();
178                    sections.insert(name, reader.read_bytes(len).expect("bytes"));
179                }
180                if name == "name" {
181                    if let Ok(reader) = section.get_name_section_reader() {
182                        if let Ok(section) = read_name_section(reader) {
183                            name_section = Some(section);
184                        }
185                    }
186                }
187            }
188            SectionCode::Type => {
189                signatures_params = section
190                    .get_type_section_reader()
191                    .expect("type section")
192                    .into_iter()
193                    .map(|ft| ft.expect("type").params)
194                    .collect::<Vec<_>>();
195            }
196            SectionCode::Function => {
197                func_params_refs = section
198                    .get_function_section_reader()
199                    .expect("function section")
200                    .into_iter()
201                    .map(|index| index.expect("func index") as usize)
202                    .collect::<Vec<_>>();
203            }
204            SectionCode::Code => {
205                code_section_offset = section.range().start as u64;
206                func_locals = section
207                    .get_code_section_reader()
208                    .expect("code section")
209                    .into_iter()
210                    .map(|body| {
211                        let locals = body
212                            .expect("body")
213                            .get_locals_reader()
214                            .expect("locals reader");
215                        locals
216                            .into_iter()
217                            .collect::<Result<Vec<_>, _>>()
218                            .expect("locals data")
219                            .into_boxed_slice()
220                    })
221                    .collect::<Vec<_>>();
222            }
223            _ => (),
224        }
225    }
226
227    let func_meta = func_params_refs
228        .into_iter()
229        .zip(func_locals.into_iter())
230        .map(|(params_index, locals)| FunctionMetadata {
231            params: signatures_params[params_index].clone(),
232            locals,
233        })
234        .collect::<Vec<_>>();
235
236    DebugInfoData {
237        dwarf: convert_sections(sections),
238        name_section,
239        wasm_file: WasmFileInfo {
240            path: None,
241            code_section_offset,
242            funcs: func_meta.into_boxed_slice(),
243        },
244    }
245}