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