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}