1 use super::address_transform::AddressTransform;
2 use super::attr::clone_attr_string;
3 use super::{Reader, TransformError};
4 use anyhow::{Context, Error};
5 use gimli::{
6     write, AttributeValue::DebugLineRef, DebugLine, DebugLineOffset, DebugStr,
7     DebuggingInformationEntry, LineEncoding, Unit,
8 };
9 use wasmtime_environ::DefinedFuncIndex;
10 
11 #[derive(Debug)]
12 enum SavedLineProgramRow {
13     Normal {
14         address: u64,
15         op_index: u64,
16         file_index: u64,
17         line: u64,
18         column: u64,
19         discriminator: u64,
20         is_stmt: bool,
21         basic_block: bool,
22         prologue_end: bool,
23         epilogue_begin: bool,
24         isa: u64,
25     },
26     EndOfSequence,
27 }
28 
29 #[derive(Debug)]
30 struct FuncRows {
31     index: DefinedFuncIndex,
32     sorted_rows: Vec<(u64, SavedLineProgramRow)>,
33 }
34 
35 #[derive(Debug, Eq, PartialEq)]
36 enum ReadLineProgramState {
37     SequenceEnded,
38     ReadSequence(DefinedFuncIndex),
39     IgnoreSequence,
40 }
41 
42 pub(crate) fn clone_line_program<R>(
43     dwarf: &gimli::Dwarf<R>,
44     skeleton_dwarf: &gimli::Dwarf<R>,
45     unit: &Unit<R, R::Offset>,
46     root: &DebuggingInformationEntry<R>,
47     skeleton_die: Option<&DebuggingInformationEntry<R>>,
48     addr_tr: &AddressTransform,
49     out_encoding: gimli::Encoding,
50     debug_str: &DebugStr<R>,
51     debug_line: &DebugLine<R>,
52     out_strings: &mut write::StringTable,
53 ) -> Result<(write::LineProgram, DebugLineOffset, Vec<write::FileId>, u64), Error>
54 where
55     R: Reader,
56 {
57     // Where are the "location" attributes
58     let (location_die, location_dwarf) = match skeleton_die {
59         Some(die) => (die, skeleton_dwarf),
60         _ => (root, dwarf),
61     };
62 
63     let offset = match location_die.attr_value(gimli::DW_AT_stmt_list)? {
64         Some(DebugLineRef(offset)) => offset,
65         Some(gimli::AttributeValue::SecOffset(offset)) => DebugLineOffset(offset),
66         _ => {
67             return Err(TransformError("Debug line offset is not found").into());
68         }
69     };
70     let comp_dir = location_die.attr_value(gimli::DW_AT_comp_dir)?;
71     let comp_name = root.attr_value(gimli::DW_AT_name)?;
72     let out_comp_dir = match &comp_dir {
73         Some(comp_dir) => Some(clone_attr_string(
74             comp_dir,
75             gimli::DW_FORM_strp,
76             unit,
77             location_dwarf,
78             out_strings,
79         )?),
80         None => None,
81     };
82     let out_comp_name = match comp_name {
83         Some(_) => clone_attr_string(
84             comp_name
85                 .as_ref()
86                 .context("failed to read DW_AT_name attribute")?,
87             gimli::DW_FORM_strp,
88             unit,
89             dwarf,
90             out_strings,
91         )?,
92         _ => gimli::write::LineString::String("missing DW_AT_name attribute".into()),
93     };
94 
95     let program = debug_line.program(
96         offset,
97         unit.header.address_size(),
98         comp_dir.and_then(|val| val.string_value(&debug_str)),
99         comp_name.and_then(|val| val.string_value(&debug_str)),
100     );
101     if let Ok(program) = program {
102         let header = program.header();
103         let file_index_base = if header.version() < 5 { 1 } else { 0 };
104         assert!(header.version() <= 5, "not supported 6");
105         let line_encoding = LineEncoding {
106             minimum_instruction_length: header.minimum_instruction_length(),
107             maximum_operations_per_instruction: header.maximum_operations_per_instruction(),
108             default_is_stmt: header.default_is_stmt(),
109             line_base: header.line_base(),
110             line_range: header.line_range(),
111         };
112         let mut out_program = write::LineProgram::new(
113             out_encoding,
114             line_encoding,
115             out_comp_dir.unwrap_or_else(|| write::LineString::String(Vec::new())),
116             out_comp_name,
117             None,
118         );
119         let mut dirs = Vec::new();
120         dirs.push(out_program.default_directory());
121         for dir_attr in header.include_directories() {
122             let dir_id = out_program.add_directory(clone_attr_string(
123                 dir_attr,
124                 gimli::DW_FORM_string,
125                 unit,
126                 location_dwarf,
127                 out_strings,
128             )?);
129             dirs.push(dir_id);
130         }
131         let mut files = Vec::new();
132         // Since we are outputting DWARF-4, perform base change.
133         let directory_index_correction = if header.version() >= 5 { 1 } else { 0 };
134         for file_entry in header.file_names() {
135             let dir_index = file_entry.directory_index() + directory_index_correction;
136             let dir_id = dirs[dir_index as usize];
137             let file_id = out_program.add_file(
138                 clone_attr_string(
139                     &file_entry.path_name(),
140                     gimli::DW_FORM_string,
141                     unit,
142                     location_dwarf,
143                     out_strings,
144                 )?,
145                 dir_id,
146                 None,
147             );
148             files.push(file_id);
149         }
150 
151         let mut rows = program.rows();
152         let mut func_rows = Vec::new();
153         let mut saved_rows: Vec<(u64, SavedLineProgramRow)> = Vec::new();
154         let mut state = ReadLineProgramState::SequenceEnded;
155         while let Some((_header, row)) = rows.next_row()? {
156             if state == ReadLineProgramState::IgnoreSequence {
157                 if row.end_sequence() {
158                     state = ReadLineProgramState::SequenceEnded;
159                 }
160                 continue;
161             }
162             let saved_row = if row.end_sequence() {
163                 let index = match state {
164                     ReadLineProgramState::ReadSequence(index) => index,
165                     _ => panic!(),
166                 };
167                 saved_rows.sort_by_key(|r| r.0);
168                 func_rows.push(FuncRows {
169                     index,
170                     sorted_rows: saved_rows,
171                 });
172 
173                 saved_rows = Vec::new();
174                 state = ReadLineProgramState::SequenceEnded;
175                 SavedLineProgramRow::EndOfSequence
176             } else {
177                 if state == ReadLineProgramState::SequenceEnded {
178                     // Discard sequences for non-existent code.
179                     if row.address() == 0 {
180                         state = ReadLineProgramState::IgnoreSequence;
181                         continue;
182                     }
183                     match addr_tr.find_func_index(row.address()) {
184                         Some(index) => {
185                             state = ReadLineProgramState::ReadSequence(index);
186                         }
187                         None => {
188                             // Some non-existent address found.
189                             state = ReadLineProgramState::IgnoreSequence;
190                             continue;
191                         }
192                     }
193                 }
194                 SavedLineProgramRow::Normal {
195                     address: row.address(),
196                     op_index: row.op_index(),
197                     file_index: row.file_index(),
198                     line: row.line().map(|nonzero| nonzero.get()).unwrap_or(0),
199                     column: match row.column() {
200                         gimli::ColumnType::LeftEdge => 0,
201                         gimli::ColumnType::Column(val) => val.get(),
202                     },
203                     discriminator: row.discriminator(),
204                     is_stmt: row.is_stmt(),
205                     basic_block: row.basic_block(),
206                     prologue_end: row.prologue_end(),
207                     epilogue_begin: row.epilogue_begin(),
208                     isa: row.isa(),
209                 }
210             };
211             saved_rows.push((row.address(), saved_row));
212         }
213 
214         for FuncRows {
215             index,
216             sorted_rows: saved_rows,
217         } in func_rows
218         {
219             let map = match addr_tr.map().get(index) {
220                 Some(map) if map.len > 0 => map,
221                 _ => {
222                     continue; // no code generated
223                 }
224             };
225             let symbol = map.symbol;
226             let base_addr = map.offset;
227             out_program.begin_sequence(Some(write::Address::Symbol { symbol, addend: 0 }));
228             // TODO track and place function declaration line here
229             let mut last_address = None;
230             for addr_map in map.addresses.iter() {
231                 let saved_row = match saved_rows.binary_search_by_key(&addr_map.wasm, |i| i.0) {
232                     Ok(i) => Some(&saved_rows[i].1),
233                     Err(i) => {
234                         if i > 0 {
235                             Some(&saved_rows[i - 1].1)
236                         } else {
237                             None
238                         }
239                     }
240                 };
241                 if let Some(SavedLineProgramRow::Normal {
242                     address,
243                     op_index,
244                     file_index,
245                     line,
246                     column,
247                     discriminator,
248                     is_stmt,
249                     basic_block,
250                     prologue_end,
251                     epilogue_begin,
252                     isa,
253                 }) = saved_row
254                 {
255                     // Ignore duplicates
256                     if Some(*address) != last_address {
257                         let address_offset = if last_address.is_none() {
258                             // Extend first entry to the function declaration
259                             // TODO use the function declaration line instead
260                             0
261                         } else {
262                             (addr_map.generated - base_addr) as u64
263                         };
264                         out_program.row().address_offset = address_offset;
265                         out_program.row().op_index = *op_index;
266                         out_program.row().file = files[(file_index - file_index_base) as usize];
267                         out_program.row().line = *line;
268                         out_program.row().column = *column;
269                         out_program.row().discriminator = *discriminator;
270                         out_program.row().is_statement = *is_stmt;
271                         out_program.row().basic_block = *basic_block;
272                         out_program.row().prologue_end = *prologue_end;
273                         out_program.row().epilogue_begin = *epilogue_begin;
274                         out_program.row().isa = *isa;
275                         out_program.generate_row();
276                         last_address = Some(*address);
277                     }
278                 }
279             }
280             let end_addr = (map.offset + map.len) as u64;
281             out_program.end_sequence(end_addr);
282         }
283         Ok((out_program, offset, files, file_index_base))
284     } else {
285         Err(TransformError("Valid line program not found").into())
286     }
287 }
288