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