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