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