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