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