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