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