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