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