| ... | @@ -732,50 +732,8 @@ fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tt | ... | @@ -732,50 +732,8 @@ fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tt |
| 732 | } | 732 | } |
| 733 | } | 733 | } |
| 734 | | 734 | |
| 735 | /// This function works in freestanding mode. | | |
| 736 | /// fn printLineFromFile(out_stream: var, line_info: LineInfo) !void | | |
| 737 | pub fn printSourceAtAddressDwarf( | | |
| 738 | debug_info: *DwarfInfo, | | |
| 739 | out_stream: var, | | |
| 740 | address: usize, | | |
| 741 | tty_color: bool, | | |
| 742 | comptime printLineFromFile: var, | | |
| 743 | ) !void { | | |
| 744 | const compile_unit = findCompileUnit(debug_info, address) catch { | | |
| 745 | if (tty_color) { | | |
| 746 | try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n\n\n", address); | | |
| 747 | } else { | | |
| 748 | try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", address); | | |
| 749 | } | | |
| 750 | return; | | |
| 751 | }; | | |
| 752 | const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name); | | |
| 753 | if (getLineNumberInfoDwarf(debug_info, compile_unit.*, address)) |line_info| { | | |
| 754 | defer line_info.deinit(); | | |
| 755 | const symbol_name = getSymbolNameDwarf(debug_info, address) orelse "???"; | | |
| 756 | try printLineInfo( | | |
| 757 | out_stream, | | |
| 758 | line_info, | | |
| 759 | address, | | |
| 760 | symbol_name, | | |
| 761 | compile_unit_name, | | |
| 762 | tty_color, | | |
| 763 | printLineFromFile, | | |
| 764 | ); | | |
| 765 | } else |err| switch (err) { | | |
| 766 | error.MissingDebugInfo, error.InvalidDebugInfo => { | | |
| 767 | if (tty_color) { | | |
| 768 | try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? ({})" ++ RESET ++ "\n\n\n", address, compile_unit_name); | | |
| 769 | } else { | | |
| 770 | try out_stream.print("???:?:?: 0x{x} in ??? ({})\n\n\n", address, compile_unit_name); | | |
| 771 | } | | |
| 772 | }, | | |
| 773 | else => return err, | | |
| 774 | } | | |
| 775 | } | | |
| 776 | | | |
| 777 | pub fn printSourceAtAddressPosix(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { | 735 | pub fn printSourceAtAddressPosix(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { |
| 778 | return printSourceAtAddressDwarf(debug_info, out_stream, address, tty_color, printLineFromFileAnyOs); | 736 | return debug_info.printSourceAtAddress(out_stream, address, tty_color, printLineFromFileAnyOs); |
| 779 | } | 737 | } |
| 780 | | 738 | |
| 781 | fn printLineInfo( | 739 | fn printLineInfo( |
| ... | @@ -1034,8 +992,8 @@ pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: *mem.Allocator) !void { | ... | @@ -1034,8 +992,8 @@ pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: *mem.Allocator) !void { |
| 1034 | di.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator); | 992 | di.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator); |
| 1035 | di.compile_unit_list = ArrayList(CompileUnit).init(allocator); | 993 | di.compile_unit_list = ArrayList(CompileUnit).init(allocator); |
| 1036 | di.func_list = ArrayList(Func).init(allocator); | 994 | di.func_list = ArrayList(Func).init(allocator); |
| 1037 | try scanAllFunctions(di); | 995 | try di.scanAllFunctions(); |
| 1038 | try scanAllCompileUnits(di); | 996 | try di.scanAllCompileUnits(); |
| 1039 | } | 997 | } |
| 1040 | | 998 | |
| 1041 | pub fn openElfDebugInfo( | 999 | pub fn openElfDebugInfo( |
| ... | @@ -1253,6 +1211,492 @@ pub const DwarfInfo = struct { | ... | @@ -1253,6 +1211,492 @@ pub const DwarfInfo = struct { |
| 1253 | pub fn readString(self: *DwarfInfo) ![]u8 { | 1211 | pub fn readString(self: *DwarfInfo) ![]u8 { |
| 1254 | return readStringRaw(self.allocator(), self.dwarf_in_stream); | 1212 | return readStringRaw(self.allocator(), self.dwarf_in_stream); |
| 1255 | } | 1213 | } |
| | 1214 | |
| | 1215 | /// This function works in freestanding mode. |
| | 1216 | /// fn printLineFromFile(out_stream: var, line_info: LineInfo) !void |
| | 1217 | pub fn printSourceAtAddress( |
| | 1218 | self: *DwarfInfo, |
| | 1219 | out_stream: var, |
| | 1220 | address: usize, |
| | 1221 | tty_color: bool, |
| | 1222 | comptime printLineFromFile: var, |
| | 1223 | ) !void { |
| | 1224 | const compile_unit = self.findCompileUnit(address) catch { |
| | 1225 | if (tty_color) { |
| | 1226 | try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n\n\n", address); |
| | 1227 | } else { |
| | 1228 | try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", address); |
| | 1229 | } |
| | 1230 | return; |
| | 1231 | }; |
| | 1232 | const compile_unit_name = try compile_unit.die.getAttrString(self, DW.AT_name); |
| | 1233 | if (self.getLineNumberInfo(compile_unit.*, address)) |line_info| { |
| | 1234 | defer line_info.deinit(); |
| | 1235 | const symbol_name = self.getSymbolName(address) orelse "???"; |
| | 1236 | try printLineInfo( |
| | 1237 | out_stream, |
| | 1238 | line_info, |
| | 1239 | address, |
| | 1240 | symbol_name, |
| | 1241 | compile_unit_name, |
| | 1242 | tty_color, |
| | 1243 | printLineFromFile, |
| | 1244 | ); |
| | 1245 | } else |err| switch (err) { |
| | 1246 | error.MissingDebugInfo, error.InvalidDebugInfo => { |
| | 1247 | if (tty_color) { |
| | 1248 | try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? ({})" ++ RESET ++ "\n\n\n", address, compile_unit_name); |
| | 1249 | } else { |
| | 1250 | try out_stream.print("???:?:?: 0x{x} in ??? ({})\n\n\n", address, compile_unit_name); |
| | 1251 | } |
| | 1252 | }, |
| | 1253 | else => return err, |
| | 1254 | } |
| | 1255 | } |
| | 1256 | |
| | 1257 | fn getSymbolName(di: *DwarfInfo, address: u64) ?[]const u8 { |
| | 1258 | for (di.func_list.toSliceConst()) |*func| { |
| | 1259 | if (func.pc_range) |range| { |
| | 1260 | if (address >= range.start and address < range.end) { |
| | 1261 | return func.name; |
| | 1262 | } |
| | 1263 | } |
| | 1264 | } |
| | 1265 | |
| | 1266 | return null; |
| | 1267 | } |
| | 1268 | |
| | 1269 | fn scanAllFunctions(di: *DwarfInfo) !void { |
| | 1270 | const debug_info_end = di.debug_info.offset + di.debug_info.size; |
| | 1271 | var this_unit_offset = di.debug_info.offset; |
| | 1272 | |
| | 1273 | while (this_unit_offset < debug_info_end) { |
| | 1274 | try di.dwarf_seekable_stream.seekTo(this_unit_offset); |
| | 1275 | |
| | 1276 | var is_64: bool = undefined; |
| | 1277 | const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64); |
| | 1278 | if (unit_length == 0) return; |
| | 1279 | const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4)); |
| | 1280 | |
| | 1281 | const version = try di.dwarf_in_stream.readInt(u16, di.endian); |
| | 1282 | if (version < 2 or version > 5) return error.InvalidDebugInfo; |
| | 1283 | |
| | 1284 | const debug_abbrev_offset = if (is_64) try di.dwarf_in_stream.readInt(u64, di.endian) else try di.dwarf_in_stream.readInt(u32, di.endian); |
| | 1285 | |
| | 1286 | const address_size = try di.dwarf_in_stream.readByte(); |
| | 1287 | if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; |
| | 1288 | |
| | 1289 | const compile_unit_pos = try di.dwarf_seekable_stream.getPos(); |
| | 1290 | const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset); |
| | 1291 | |
| | 1292 | try di.dwarf_seekable_stream.seekTo(compile_unit_pos); |
| | 1293 | |
| | 1294 | const next_unit_pos = this_unit_offset + next_offset; |
| | 1295 | |
| | 1296 | while ((try di.dwarf_seekable_stream.getPos()) < next_unit_pos) { |
| | 1297 | const die_obj = (try di.parseDie(abbrev_table, is_64)) orelse continue; |
| | 1298 | const after_die_offset = try di.dwarf_seekable_stream.getPos(); |
| | 1299 | |
| | 1300 | switch (die_obj.tag_id) { |
| | 1301 | DW.TAG_subprogram, DW.TAG_inlined_subroutine, DW.TAG_subroutine, DW.TAG_entry_point => { |
| | 1302 | const fn_name = x: { |
| | 1303 | var depth: i32 = 3; |
| | 1304 | var this_die_obj = die_obj; |
| | 1305 | // Prenvent endless loops |
| | 1306 | while (depth > 0) : (depth -= 1) { |
| | 1307 | if (this_die_obj.getAttr(DW.AT_name)) |_| { |
| | 1308 | const name = try this_die_obj.getAttrString(di, DW.AT_name); |
| | 1309 | break :x name; |
| | 1310 | } else if (this_die_obj.getAttr(DW.AT_abstract_origin)) |ref| { |
| | 1311 | // Follow the DIE it points to and repeat |
| | 1312 | const ref_offset = try this_die_obj.getAttrRef(DW.AT_abstract_origin); |
| | 1313 | if (ref_offset > next_offset) return error.InvalidDebugInfo; |
| | 1314 | try di.dwarf_seekable_stream.seekTo(this_unit_offset + ref_offset); |
| | 1315 | this_die_obj = (try di.parseDie(abbrev_table, is_64)) orelse return error.InvalidDebugInfo; |
| | 1316 | } else if (this_die_obj.getAttr(DW.AT_specification)) |ref| { |
| | 1317 | // Follow the DIE it points to and repeat |
| | 1318 | const ref_offset = try this_die_obj.getAttrRef(DW.AT_specification); |
| | 1319 | if (ref_offset > next_offset) return error.InvalidDebugInfo; |
| | 1320 | try di.dwarf_seekable_stream.seekTo(this_unit_offset + ref_offset); |
| | 1321 | this_die_obj = (try di.parseDie(abbrev_table, is_64)) orelse return error.InvalidDebugInfo; |
| | 1322 | } else { |
| | 1323 | break :x null; |
| | 1324 | } |
| | 1325 | } |
| | 1326 | |
| | 1327 | break :x null; |
| | 1328 | }; |
| | 1329 | |
| | 1330 | const pc_range = x: { |
| | 1331 | if (die_obj.getAttrAddr(DW.AT_low_pc)) |low_pc| { |
| | 1332 | if (die_obj.getAttr(DW.AT_high_pc)) |high_pc_value| { |
| | 1333 | const pc_end = switch (high_pc_value.*) { |
| | 1334 | FormValue.Address => |value| value, |
| | 1335 | FormValue.Const => |value| b: { |
| | 1336 | const offset = try value.asUnsignedLe(); |
| | 1337 | break :b (low_pc + offset); |
| | 1338 | }, |
| | 1339 | else => return error.InvalidDebugInfo, |
| | 1340 | }; |
| | 1341 | break :x PcRange{ |
| | 1342 | .start = low_pc, |
| | 1343 | .end = pc_end, |
| | 1344 | }; |
| | 1345 | } else { |
| | 1346 | break :x null; |
| | 1347 | } |
| | 1348 | } else |err| { |
| | 1349 | if (err != error.MissingDebugInfo) return err; |
| | 1350 | break :x null; |
| | 1351 | } |
| | 1352 | }; |
| | 1353 | |
| | 1354 | try di.func_list.append(Func{ |
| | 1355 | .name = fn_name, |
| | 1356 | .pc_range = pc_range, |
| | 1357 | }); |
| | 1358 | }, |
| | 1359 | else => { |
| | 1360 | continue; |
| | 1361 | }, |
| | 1362 | } |
| | 1363 | |
| | 1364 | try di.dwarf_seekable_stream.seekTo(after_die_offset); |
| | 1365 | } |
| | 1366 | |
| | 1367 | this_unit_offset += next_offset; |
| | 1368 | } |
| | 1369 | } |
| | 1370 | |
| | 1371 | fn scanAllCompileUnits(di: *DwarfInfo) !void { |
| | 1372 | const debug_info_end = di.debug_info.offset + di.debug_info.size; |
| | 1373 | var this_unit_offset = di.debug_info.offset; |
| | 1374 | |
| | 1375 | while (this_unit_offset < debug_info_end) { |
| | 1376 | try di.dwarf_seekable_stream.seekTo(this_unit_offset); |
| | 1377 | |
| | 1378 | var is_64: bool = undefined; |
| | 1379 | const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64); |
| | 1380 | if (unit_length == 0) return; |
| | 1381 | const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4)); |
| | 1382 | |
| | 1383 | const version = try di.dwarf_in_stream.readInt(u16, di.endian); |
| | 1384 | if (version < 2 or version > 5) return error.InvalidDebugInfo; |
| | 1385 | |
| | 1386 | const debug_abbrev_offset = if (is_64) try di.dwarf_in_stream.readInt(u64, di.endian) else try di.dwarf_in_stream.readInt(u32, di.endian); |
| | 1387 | |
| | 1388 | const address_size = try di.dwarf_in_stream.readByte(); |
| | 1389 | if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; |
| | 1390 | |
| | 1391 | const compile_unit_pos = try di.dwarf_seekable_stream.getPos(); |
| | 1392 | const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset); |
| | 1393 | |
| | 1394 | try di.dwarf_seekable_stream.seekTo(compile_unit_pos); |
| | 1395 | |
| | 1396 | const compile_unit_die = try di.allocator().create(Die); |
| | 1397 | compile_unit_die.* = (try di.parseDie(abbrev_table, is_64)) orelse return error.InvalidDebugInfo; |
| | 1398 | |
| | 1399 | if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo; |
| | 1400 | |
| | 1401 | const pc_range = x: { |
| | 1402 | if (compile_unit_die.getAttrAddr(DW.AT_low_pc)) |low_pc| { |
| | 1403 | if (compile_unit_die.getAttr(DW.AT_high_pc)) |high_pc_value| { |
| | 1404 | const pc_end = switch (high_pc_value.*) { |
| | 1405 | FormValue.Address => |value| value, |
| | 1406 | FormValue.Const => |value| b: { |
| | 1407 | const offset = try value.asUnsignedLe(); |
| | 1408 | break :b (low_pc + offset); |
| | 1409 | }, |
| | 1410 | else => return error.InvalidDebugInfo, |
| | 1411 | }; |
| | 1412 | break :x PcRange{ |
| | 1413 | .start = low_pc, |
| | 1414 | .end = pc_end, |
| | 1415 | }; |
| | 1416 | } else { |
| | 1417 | break :x null; |
| | 1418 | } |
| | 1419 | } else |err| { |
| | 1420 | if (err != error.MissingDebugInfo) return err; |
| | 1421 | break :x null; |
| | 1422 | } |
| | 1423 | }; |
| | 1424 | |
| | 1425 | try di.compile_unit_list.append(CompileUnit{ |
| | 1426 | .version = version, |
| | 1427 | .is_64 = is_64, |
| | 1428 | .pc_range = pc_range, |
| | 1429 | .die = compile_unit_die, |
| | 1430 | }); |
| | 1431 | |
| | 1432 | this_unit_offset += next_offset; |
| | 1433 | } |
| | 1434 | } |
| | 1435 | |
| | 1436 | fn findCompileUnit(di: *DwarfInfo, target_address: u64) !*const CompileUnit { |
| | 1437 | for (di.compile_unit_list.toSlice()) |*compile_unit| { |
| | 1438 | if (compile_unit.pc_range) |range| { |
| | 1439 | if (target_address >= range.start and target_address < range.end) return compile_unit; |
| | 1440 | } |
| | 1441 | if (compile_unit.die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| { |
| | 1442 | var base_address: usize = 0; |
| | 1443 | if (di.debug_ranges) |debug_ranges| { |
| | 1444 | try di.dwarf_seekable_stream.seekTo(debug_ranges.offset + ranges_offset); |
| | 1445 | while (true) { |
| | 1446 | const begin_addr = try di.dwarf_in_stream.readIntLittle(usize); |
| | 1447 | const end_addr = try di.dwarf_in_stream.readIntLittle(usize); |
| | 1448 | if (begin_addr == 0 and end_addr == 0) { |
| | 1449 | break; |
| | 1450 | } |
| | 1451 | if (begin_addr == maxInt(usize)) { |
| | 1452 | base_address = begin_addr; |
| | 1453 | continue; |
| | 1454 | } |
| | 1455 | if (target_address >= begin_addr and target_address < end_addr) { |
| | 1456 | return compile_unit; |
| | 1457 | } |
| | 1458 | } |
| | 1459 | } |
| | 1460 | } else |err| { |
| | 1461 | if (err != error.MissingDebugInfo) return err; |
| | 1462 | continue; |
| | 1463 | } |
| | 1464 | } |
| | 1465 | return error.MissingDebugInfo; |
| | 1466 | } |
| | 1467 | |
| | 1468 | /// Gets an already existing AbbrevTable given the abbrev_offset, or if not found, |
| | 1469 | /// seeks in the stream and parses it. |
| | 1470 | fn getAbbrevTable(di: *DwarfInfo, abbrev_offset: u64) !*const AbbrevTable { |
| | 1471 | for (di.abbrev_table_list.toSlice()) |*header| { |
| | 1472 | if (header.offset == abbrev_offset) { |
| | 1473 | return &header.table; |
| | 1474 | } |
| | 1475 | } |
| | 1476 | try di.dwarf_seekable_stream.seekTo(di.debug_abbrev.offset + abbrev_offset); |
| | 1477 | try di.abbrev_table_list.append(AbbrevTableHeader{ |
| | 1478 | .offset = abbrev_offset, |
| | 1479 | .table = try di.parseAbbrevTable(), |
| | 1480 | }); |
| | 1481 | return &di.abbrev_table_list.items[di.abbrev_table_list.len - 1].table; |
| | 1482 | } |
| | 1483 | |
| | 1484 | fn parseAbbrevTable(di: *DwarfInfo) !AbbrevTable { |
| | 1485 | var result = AbbrevTable.init(di.allocator()); |
| | 1486 | while (true) { |
| | 1487 | const abbrev_code = try leb.readULEB128(u64, di.dwarf_in_stream); |
| | 1488 | if (abbrev_code == 0) return result; |
| | 1489 | try result.append(AbbrevTableEntry{ |
| | 1490 | .abbrev_code = abbrev_code, |
| | 1491 | .tag_id = try leb.readULEB128(u64, di.dwarf_in_stream), |
| | 1492 | .has_children = (try di.dwarf_in_stream.readByte()) == DW.CHILDREN_yes, |
| | 1493 | .attrs = ArrayList(AbbrevAttr).init(di.allocator()), |
| | 1494 | }); |
| | 1495 | const attrs = &result.items[result.len - 1].attrs; |
| | 1496 | |
| | 1497 | while (true) { |
| | 1498 | const attr_id = try leb.readULEB128(u64, di.dwarf_in_stream); |
| | 1499 | const form_id = try leb.readULEB128(u64, di.dwarf_in_stream); |
| | 1500 | if (attr_id == 0 and form_id == 0) break; |
| | 1501 | try attrs.append(AbbrevAttr{ |
| | 1502 | .attr_id = attr_id, |
| | 1503 | .form_id = form_id, |
| | 1504 | }); |
| | 1505 | } |
| | 1506 | } |
| | 1507 | } |
| | 1508 | |
| | 1509 | fn parseDie(di: *DwarfInfo, abbrev_table: *const AbbrevTable, is_64: bool) !?Die { |
| | 1510 | const abbrev_code = try leb.readULEB128(u64, di.dwarf_in_stream); |
| | 1511 | if (abbrev_code == 0) return null; |
| | 1512 | const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo; |
| | 1513 | |
| | 1514 | var result = Die{ |
| | 1515 | .tag_id = table_entry.tag_id, |
| | 1516 | .has_children = table_entry.has_children, |
| | 1517 | .attrs = ArrayList(Die.Attr).init(di.allocator()), |
| | 1518 | }; |
| | 1519 | try result.attrs.resize(table_entry.attrs.len); |
| | 1520 | for (table_entry.attrs.toSliceConst()) |attr, i| { |
| | 1521 | result.attrs.items[i] = Die.Attr{ |
| | 1522 | .id = attr.attr_id, |
| | 1523 | .value = try parseFormValue(di.allocator(), di.dwarf_in_stream, attr.form_id, is_64), |
| | 1524 | }; |
| | 1525 | } |
| | 1526 | return result; |
| | 1527 | } |
| | 1528 | |
| | 1529 | fn getLineNumberInfo(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !LineInfo { |
| | 1530 | const compile_unit_cwd = try compile_unit.die.getAttrString(di, DW.AT_comp_dir); |
| | 1531 | const line_info_offset = try compile_unit.die.getAttrSecOffset(DW.AT_stmt_list); |
| | 1532 | |
| | 1533 | assert(line_info_offset < di.debug_line.size); |
| | 1534 | |
| | 1535 | try di.dwarf_seekable_stream.seekTo(di.debug_line.offset + line_info_offset); |
| | 1536 | |
| | 1537 | var is_64: bool = undefined; |
| | 1538 | const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64); |
| | 1539 | if (unit_length == 0) { |
| | 1540 | return error.MissingDebugInfo; |
| | 1541 | } |
| | 1542 | const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4)); |
| | 1543 | |
| | 1544 | const version = try di.dwarf_in_stream.readInt(u16, di.endian); |
| | 1545 | // TODO support 3 and 5 |
| | 1546 | if (version != 2 and version != 4) return error.InvalidDebugInfo; |
| | 1547 | |
| | 1548 | const prologue_length = if (is_64) try di.dwarf_in_stream.readInt(u64, di.endian) else try di.dwarf_in_stream.readInt(u32, di.endian); |
| | 1549 | const prog_start_offset = (try di.dwarf_seekable_stream.getPos()) + prologue_length; |
| | 1550 | |
| | 1551 | const minimum_instruction_length = try di.dwarf_in_stream.readByte(); |
| | 1552 | if (minimum_instruction_length == 0) return error.InvalidDebugInfo; |
| | 1553 | |
| | 1554 | if (version >= 4) { |
| | 1555 | // maximum_operations_per_instruction |
| | 1556 | _ = try di.dwarf_in_stream.readByte(); |
| | 1557 | } |
| | 1558 | |
| | 1559 | const default_is_stmt = (try di.dwarf_in_stream.readByte()) != 0; |
| | 1560 | const line_base = try di.dwarf_in_stream.readByteSigned(); |
| | 1561 | |
| | 1562 | const line_range = try di.dwarf_in_stream.readByte(); |
| | 1563 | if (line_range == 0) return error.InvalidDebugInfo; |
| | 1564 | |
| | 1565 | const opcode_base = try di.dwarf_in_stream.readByte(); |
| | 1566 | |
| | 1567 | const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1); |
| | 1568 | |
| | 1569 | { |
| | 1570 | var i: usize = 0; |
| | 1571 | while (i < opcode_base - 1) : (i += 1) { |
| | 1572 | standard_opcode_lengths[i] = try di.dwarf_in_stream.readByte(); |
| | 1573 | } |
| | 1574 | } |
| | 1575 | |
| | 1576 | var include_directories = ArrayList([]u8).init(di.allocator()); |
| | 1577 | try include_directories.append(compile_unit_cwd); |
| | 1578 | while (true) { |
| | 1579 | const dir = try di.readString(); |
| | 1580 | if (dir.len == 0) break; |
| | 1581 | try include_directories.append(dir); |
| | 1582 | } |
| | 1583 | |
| | 1584 | var file_entries = ArrayList(FileEntry).init(di.allocator()); |
| | 1585 | var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address); |
| | 1586 | |
| | 1587 | while (true) { |
| | 1588 | const file_name = try di.readString(); |
| | 1589 | if (file_name.len == 0) break; |
| | 1590 | const dir_index = try leb.readULEB128(usize, di.dwarf_in_stream); |
| | 1591 | const mtime = try leb.readULEB128(usize, di.dwarf_in_stream); |
| | 1592 | const len_bytes = try leb.readULEB128(usize, di.dwarf_in_stream); |
| | 1593 | try file_entries.append(FileEntry{ |
| | 1594 | .file_name = file_name, |
| | 1595 | .dir_index = dir_index, |
| | 1596 | .mtime = mtime, |
| | 1597 | .len_bytes = len_bytes, |
| | 1598 | }); |
| | 1599 | } |
| | 1600 | |
| | 1601 | try di.dwarf_seekable_stream.seekTo(prog_start_offset); |
| | 1602 | |
| | 1603 | while (true) { |
| | 1604 | const opcode = try di.dwarf_in_stream.readByte(); |
| | 1605 | |
| | 1606 | if (opcode == DW.LNS_extended_op) { |
| | 1607 | const op_size = try leb.readULEB128(u64, di.dwarf_in_stream); |
| | 1608 | if (op_size < 1) return error.InvalidDebugInfo; |
| | 1609 | var sub_op = try di.dwarf_in_stream.readByte(); |
| | 1610 | switch (sub_op) { |
| | 1611 | DW.LNE_end_sequence => { |
| | 1612 | prog.end_sequence = true; |
| | 1613 | if (try prog.checkLineMatch()) |info| return info; |
| | 1614 | return error.MissingDebugInfo; |
| | 1615 | }, |
| | 1616 | DW.LNE_set_address => { |
| | 1617 | const addr = try di.dwarf_in_stream.readInt(usize, di.endian); |
| | 1618 | prog.address = addr; |
| | 1619 | }, |
| | 1620 | DW.LNE_define_file => { |
| | 1621 | const file_name = try di.readString(); |
| | 1622 | const dir_index = try leb.readULEB128(usize, di.dwarf_in_stream); |
| | 1623 | const mtime = try leb.readULEB128(usize, di.dwarf_in_stream); |
| | 1624 | const len_bytes = try leb.readULEB128(usize, di.dwarf_in_stream); |
| | 1625 | try file_entries.append(FileEntry{ |
| | 1626 | .file_name = file_name, |
| | 1627 | .dir_index = dir_index, |
| | 1628 | .mtime = mtime, |
| | 1629 | .len_bytes = len_bytes, |
| | 1630 | }); |
| | 1631 | }, |
| | 1632 | else => { |
| | 1633 | const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo; |
| | 1634 | try di.dwarf_seekable_stream.seekBy(fwd_amt); |
| | 1635 | }, |
| | 1636 | } |
| | 1637 | } else if (opcode >= opcode_base) { |
| | 1638 | // special opcodes |
| | 1639 | const adjusted_opcode = opcode - opcode_base; |
| | 1640 | const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range); |
| | 1641 | const inc_line = @as(i32, line_base) + @as(i32, adjusted_opcode % line_range); |
| | 1642 | prog.line += inc_line; |
| | 1643 | prog.address += inc_addr; |
| | 1644 | if (try prog.checkLineMatch()) |info| return info; |
| | 1645 | prog.basic_block = false; |
| | 1646 | } else { |
| | 1647 | switch (opcode) { |
| | 1648 | DW.LNS_copy => { |
| | 1649 | if (try prog.checkLineMatch()) |info| return info; |
| | 1650 | prog.basic_block = false; |
| | 1651 | }, |
| | 1652 | DW.LNS_advance_pc => { |
| | 1653 | const arg = try leb.readULEB128(usize, di.dwarf_in_stream); |
| | 1654 | prog.address += arg * minimum_instruction_length; |
| | 1655 | }, |
| | 1656 | DW.LNS_advance_line => { |
| | 1657 | const arg = try leb.readILEB128(i64, di.dwarf_in_stream); |
| | 1658 | prog.line += arg; |
| | 1659 | }, |
| | 1660 | DW.LNS_set_file => { |
| | 1661 | const arg = try leb.readULEB128(usize, di.dwarf_in_stream); |
| | 1662 | prog.file = arg; |
| | 1663 | }, |
| | 1664 | DW.LNS_set_column => { |
| | 1665 | const arg = try leb.readULEB128(u64, di.dwarf_in_stream); |
| | 1666 | prog.column = arg; |
| | 1667 | }, |
| | 1668 | DW.LNS_negate_stmt => { |
| | 1669 | prog.is_stmt = !prog.is_stmt; |
| | 1670 | }, |
| | 1671 | DW.LNS_set_basic_block => { |
| | 1672 | prog.basic_block = true; |
| | 1673 | }, |
| | 1674 | DW.LNS_const_add_pc => { |
| | 1675 | const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range); |
| | 1676 | prog.address += inc_addr; |
| | 1677 | }, |
| | 1678 | DW.LNS_fixed_advance_pc => { |
| | 1679 | const arg = try di.dwarf_in_stream.readInt(u16, di.endian); |
| | 1680 | prog.address += arg; |
| | 1681 | }, |
| | 1682 | DW.LNS_set_prologue_end => {}, |
| | 1683 | else => { |
| | 1684 | if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo; |
| | 1685 | const len_bytes = standard_opcode_lengths[opcode - 1]; |
| | 1686 | try di.dwarf_seekable_stream.seekBy(len_bytes); |
| | 1687 | }, |
| | 1688 | } |
| | 1689 | } |
| | 1690 | } |
| | 1691 | |
| | 1692 | return error.MissingDebugInfo; |
| | 1693 | } |
| | 1694 | |
| | 1695 | fn getString(di: *DwarfInfo, offset: u64) ![]u8 { |
| | 1696 | const pos = di.debug_str.offset + offset; |
| | 1697 | try di.dwarf_seekable_stream.seekTo(pos); |
| | 1698 | return di.readString(); |
| | 1699 | } |
| 1256 | }; | 1700 | }; |
| 1257 | | 1701 | |
| 1258 | pub const DebugInfo = switch (builtin.os) { | 1702 | pub const DebugInfo = switch (builtin.os) { |
| ... | @@ -1391,7 +1835,7 @@ const Die = struct { | ... | @@ -1391,7 +1835,7 @@ const Die = struct { |
| 1391 | const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; | 1835 | const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; |
| 1392 | return switch (form_value.*) { | 1836 | return switch (form_value.*) { |
| 1393 | FormValue.String => |value| value, | 1837 | FormValue.String => |value| value, |
| 1394 | FormValue.StrPtr => |offset| getString(di, offset), | 1838 | FormValue.StrPtr => |offset| di.getString(offset), |
| 1395 | else => error.InvalidDebugInfo, | 1839 | else => error.InvalidDebugInfo, |
| 1396 | }; | 1840 | }; |
| 1397 | } | 1841 | } |
| ... | @@ -1504,12 +1948,6 @@ fn readStringRaw(allocator: *mem.Allocator, in_stream: var) ![]u8 { | ... | @@ -1504,12 +1948,6 @@ fn readStringRaw(allocator: *mem.Allocator, in_stream: var) ![]u8 { |
| 1504 | return buf.toSlice(); | 1948 | return buf.toSlice(); |
| 1505 | } | 1949 | } |
| 1506 | | 1950 | |
| 1507 | fn getString(di: *DwarfInfo, offset: u64) ![]u8 { | | |
| 1508 | const pos = di.debug_str.offset + offset; | | |
| 1509 | try di.dwarf_seekable_stream.seekTo(pos); | | |
| 1510 | return di.readString(); | | |
| 1511 | } | | |
| 1512 | | | |
| 1513 | // TODO the noasyncs here are workarounds | 1951 | // TODO the noasyncs here are workarounds |
| 1514 | fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8 { | 1952 | fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8 { |
| 1515 | const buf = try allocator.alloc(u8, size); | 1953 | const buf = try allocator.alloc(u8, size); |
| ... | @@ -1636,47 +2074,6 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64 | ... | @@ -1636,47 +2074,6 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64 |
| 1636 | }; | 2074 | }; |
| 1637 | } | 2075 | } |
| 1638 | | 2076 | |
| 1639 | fn parseAbbrevTable(di: *DwarfInfo) !AbbrevTable { | | |
| 1640 | var result = AbbrevTable.init(di.allocator()); | | |
| 1641 | while (true) { | | |
| 1642 | const abbrev_code = try leb.readULEB128(u64, di.dwarf_in_stream); | | |
| 1643 | if (abbrev_code == 0) return result; | | |
| 1644 | try result.append(AbbrevTableEntry{ | | |
| 1645 | .abbrev_code = abbrev_code, | | |
| 1646 | .tag_id = try leb.readULEB128(u64, di.dwarf_in_stream), | | |
| 1647 | .has_children = (try di.dwarf_in_stream.readByte()) == DW.CHILDREN_yes, | | |
| 1648 | .attrs = ArrayList(AbbrevAttr).init(di.allocator()), | | |
| 1649 | }); | | |
| 1650 | const attrs = &result.items[result.len - 1].attrs; | | |
| 1651 | | | |
| 1652 | while (true) { | | |
| 1653 | const attr_id = try leb.readULEB128(u64, di.dwarf_in_stream); | | |
| 1654 | const form_id = try leb.readULEB128(u64, di.dwarf_in_stream); | | |
| 1655 | if (attr_id == 0 and form_id == 0) break; | | |
| 1656 | try attrs.append(AbbrevAttr{ | | |
| 1657 | .attr_id = attr_id, | | |
| 1658 | .form_id = form_id, | | |
| 1659 | }); | | |
| 1660 | } | | |
| 1661 | } | | |
| 1662 | } | | |
| 1663 | | | |
| 1664 | /// Gets an already existing AbbrevTable given the abbrev_offset, or if not found, | | |
| 1665 | /// seeks in the stream and parses it. | | |
| 1666 | fn getAbbrevTable(di: *DwarfInfo, abbrev_offset: u64) !*const AbbrevTable { | | |
| 1667 | for (di.abbrev_table_list.toSlice()) |*header| { | | |
| 1668 | if (header.offset == abbrev_offset) { | | |
| 1669 | return &header.table; | | |
| 1670 | } | | |
| 1671 | } | | |
| 1672 | try di.dwarf_seekable_stream.seekTo(di.debug_abbrev.offset + abbrev_offset); | | |
| 1673 | try di.abbrev_table_list.append(AbbrevTableHeader{ | | |
| 1674 | .offset = abbrev_offset, | | |
| 1675 | .table = try parseAbbrevTable(di), | | |
| 1676 | }); | | |
| 1677 | return &di.abbrev_table_list.items[di.abbrev_table_list.len - 1].table; | | |
| 1678 | } | | |
| 1679 | | | |
| 1680 | fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*const AbbrevTableEntry { | 2077 | fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*const AbbrevTableEntry { |
| 1681 | for (abbrev_table.toSliceConst()) |*table_entry| { | 2078 | for (abbrev_table.toSliceConst()) |*table_entry| { |
| 1682 | if (table_entry.abbrev_code == abbrev_code) return table_entry; | 2079 | if (table_entry.abbrev_code == abbrev_code) return table_entry; |
| ... | @@ -1684,26 +2081,6 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con | ... | @@ -1684,26 +2081,6 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con |
| 1684 | return null; | 2081 | return null; |
| 1685 | } | 2082 | } |
| 1686 | | 2083 | |
| 1687 | fn parseDie(di: *DwarfInfo, abbrev_table: *const AbbrevTable, is_64: bool) !?Die { | | |
| 1688 | const abbrev_code = try leb.readULEB128(u64, di.dwarf_in_stream); | | |
| 1689 | if (abbrev_code == 0) return null; | | |
| 1690 | const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo; | | |
| 1691 | | | |
| 1692 | var result = Die{ | | |
| 1693 | .tag_id = table_entry.tag_id, | | |
| 1694 | .has_children = table_entry.has_children, | | |
| 1695 | .attrs = ArrayList(Die.Attr).init(di.allocator()), | | |
| 1696 | }; | | |
| 1697 | try result.attrs.resize(table_entry.attrs.len); | | |
| 1698 | for (table_entry.attrs.toSliceConst()) |attr, i| { | | |
| 1699 | result.attrs.items[i] = Die.Attr{ | | |
| 1700 | .id = attr.attr_id, | | |
| 1701 | .value = try parseFormValue(di.allocator(), di.dwarf_in_stream, attr.form_id, is_64), | | |
| 1702 | }; | | |
| 1703 | } | | |
| 1704 | return result; | | |
| 1705 | } | | |
| 1706 | | | |
| 1707 | fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: usize) !LineInfo { | 2084 | fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: usize) !LineInfo { |
| 1708 | const ofile = symbol.ofile orelse return error.MissingDebugInfo; | 2085 | const ofile = symbol.ofile orelse return error.MissingDebugInfo; |
| 1709 | const gop = try di.ofiles.getOrPut(ofile); | 2086 | const gop = try di.ofiles.getOrPut(ofile); |
| ... | @@ -1907,388 +2284,11 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u | ... | @@ -1907,388 +2284,11 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u |
| 1907 | return error.MissingDebugInfo; | 2284 | return error.MissingDebugInfo; |
| 1908 | } | 2285 | } |
| 1909 | | 2286 | |
| 1910 | fn getLineNumberInfoDwarf(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !LineInfo { | | |
| 1911 | const compile_unit_cwd = try compile_unit.die.getAttrString(di, DW.AT_comp_dir); | | |
| 1912 | const line_info_offset = try compile_unit.die.getAttrSecOffset(DW.AT_stmt_list); | | |
| 1913 | | | |
| 1914 | assert(line_info_offset < di.debug_line.size); | | |
| 1915 | | | |
| 1916 | try di.dwarf_seekable_stream.seekTo(di.debug_line.offset + line_info_offset); | | |
| 1917 | | | |
| 1918 | var is_64: bool = undefined; | | |
| 1919 | const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64); | | |
| 1920 | if (unit_length == 0) { | | |
| 1921 | return error.MissingDebugInfo; | | |
| 1922 | } | | |
| 1923 | const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4)); | | |
| 1924 | | | |
| 1925 | const version = try di.dwarf_in_stream.readInt(u16, di.endian); | | |
| 1926 | // TODO support 3 and 5 | | |
| 1927 | if (version != 2 and version != 4) return error.InvalidDebugInfo; | | |
| 1928 | | | |
| 1929 | const prologue_length = if (is_64) try di.dwarf_in_stream.readInt(u64, di.endian) else try di.dwarf_in_stream.readInt(u32, di.endian); | | |
| 1930 | const prog_start_offset = (try di.dwarf_seekable_stream.getPos()) + prologue_length; | | |
| 1931 | | | |
| 1932 | const minimum_instruction_length = try di.dwarf_in_stream.readByte(); | | |
| 1933 | if (minimum_instruction_length == 0) return error.InvalidDebugInfo; | | |
| 1934 | | | |
| 1935 | if (version >= 4) { | | |
| 1936 | // maximum_operations_per_instruction | | |
| 1937 | _ = try di.dwarf_in_stream.readByte(); | | |
| 1938 | } | | |
| 1939 | | | |
| 1940 | const default_is_stmt = (try di.dwarf_in_stream.readByte()) != 0; | | |
| 1941 | const line_base = try di.dwarf_in_stream.readByteSigned(); | | |
| 1942 | | | |
| 1943 | const line_range = try di.dwarf_in_stream.readByte(); | | |
| 1944 | if (line_range == 0) return error.InvalidDebugInfo; | | |
| 1945 | | | |
| 1946 | const opcode_base = try di.dwarf_in_stream.readByte(); | | |
| 1947 | | | |
| 1948 | const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1); | | |
| 1949 | | | |
| 1950 | { | | |
| 1951 | var i: usize = 0; | | |
| 1952 | while (i < opcode_base - 1) : (i += 1) { | | |
| 1953 | standard_opcode_lengths[i] = try di.dwarf_in_stream.readByte(); | | |
| 1954 | } | | |
| 1955 | } | | |
| 1956 | | | |
| 1957 | var include_directories = ArrayList([]u8).init(di.allocator()); | | |
| 1958 | try include_directories.append(compile_unit_cwd); | | |
| 1959 | while (true) { | | |
| 1960 | const dir = try di.readString(); | | |
| 1961 | if (dir.len == 0) break; | | |
| 1962 | try include_directories.append(dir); | | |
| 1963 | } | | |
| 1964 | | | |
| 1965 | var file_entries = ArrayList(FileEntry).init(di.allocator()); | | |
| 1966 | var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address); | | |
| 1967 | | | |
| 1968 | while (true) { | | |
| 1969 | const file_name = try di.readString(); | | |
| 1970 | if (file_name.len == 0) break; | | |
| 1971 | const dir_index = try leb.readULEB128(usize, di.dwarf_in_stream); | | |
| 1972 | const mtime = try leb.readULEB128(usize, di.dwarf_in_stream); | | |
| 1973 | const len_bytes = try leb.readULEB128(usize, di.dwarf_in_stream); | | |
| 1974 | try file_entries.append(FileEntry{ | | |
| 1975 | .file_name = file_name, | | |
| 1976 | .dir_index = dir_index, | | |
| 1977 | .mtime = mtime, | | |
| 1978 | .len_bytes = len_bytes, | | |
| 1979 | }); | | |
| 1980 | } | | |
| 1981 | | | |
| 1982 | try di.dwarf_seekable_stream.seekTo(prog_start_offset); | | |
| 1983 | | | |
| 1984 | while (true) { | | |
| 1985 | const opcode = try di.dwarf_in_stream.readByte(); | | |
| 1986 | | | |
| 1987 | if (opcode == DW.LNS_extended_op) { | | |
| 1988 | const op_size = try leb.readULEB128(u64, di.dwarf_in_stream); | | |
| 1989 | if (op_size < 1) return error.InvalidDebugInfo; | | |
| 1990 | var sub_op = try di.dwarf_in_stream.readByte(); | | |
| 1991 | switch (sub_op) { | | |
| 1992 | DW.LNE_end_sequence => { | | |
| 1993 | prog.end_sequence = true; | | |
| 1994 | if (try prog.checkLineMatch()) |info| return info; | | |
| 1995 | return error.MissingDebugInfo; | | |
| 1996 | }, | | |
| 1997 | DW.LNE_set_address => { | | |
| 1998 | const addr = try di.dwarf_in_stream.readInt(usize, di.endian); | | |
| 1999 | prog.address = addr; | | |
| 2000 | }, | | |
| 2001 | DW.LNE_define_file => { | | |
| 2002 | const file_name = try di.readString(); | | |
| 2003 | const dir_index = try leb.readULEB128(usize, di.dwarf_in_stream); | | |
| 2004 | const mtime = try leb.readULEB128(usize, di.dwarf_in_stream); | | |
| 2005 | const len_bytes = try leb.readULEB128(usize, di.dwarf_in_stream); | | |
| 2006 | try file_entries.append(FileEntry{ | | |
| 2007 | .file_name = file_name, | | |
| 2008 | .dir_index = dir_index, | | |
| 2009 | .mtime = mtime, | | |
| 2010 | .len_bytes = len_bytes, | | |
| 2011 | }); | | |
| 2012 | }, | | |
| 2013 | else => { | | |
| 2014 | const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo; | | |
| 2015 | try di.dwarf_seekable_stream.seekBy(fwd_amt); | | |
| 2016 | }, | | |
| 2017 | } | | |
| 2018 | } else if (opcode >= opcode_base) { | | |
| 2019 | // special opcodes | | |
| 2020 | const adjusted_opcode = opcode - opcode_base; | | |
| 2021 | const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range); | | |
| 2022 | const inc_line = @as(i32, line_base) + @as(i32, adjusted_opcode % line_range); | | |
| 2023 | prog.line += inc_line; | | |
| 2024 | prog.address += inc_addr; | | |
| 2025 | if (try prog.checkLineMatch()) |info| return info; | | |
| 2026 | prog.basic_block = false; | | |
| 2027 | } else { | | |
| 2028 | switch (opcode) { | | |
| 2029 | DW.LNS_copy => { | | |
| 2030 | if (try prog.checkLineMatch()) |info| return info; | | |
| 2031 | prog.basic_block = false; | | |
| 2032 | }, | | |
| 2033 | DW.LNS_advance_pc => { | | |
| 2034 | const arg = try leb.readULEB128(usize, di.dwarf_in_stream); | | |
| 2035 | prog.address += arg * minimum_instruction_length; | | |
| 2036 | }, | | |
| 2037 | DW.LNS_advance_line => { | | |
| 2038 | const arg = try leb.readILEB128(i64, di.dwarf_in_stream); | | |
| 2039 | prog.line += arg; | | |
| 2040 | }, | | |
| 2041 | DW.LNS_set_file => { | | |
| 2042 | const arg = try leb.readULEB128(usize, di.dwarf_in_stream); | | |
| 2043 | prog.file = arg; | | |
| 2044 | }, | | |
| 2045 | DW.LNS_set_column => { | | |
| 2046 | const arg = try leb.readULEB128(u64, di.dwarf_in_stream); | | |
| 2047 | prog.column = arg; | | |
| 2048 | }, | | |
| 2049 | DW.LNS_negate_stmt => { | | |
| 2050 | prog.is_stmt = !prog.is_stmt; | | |
| 2051 | }, | | |
| 2052 | DW.LNS_set_basic_block => { | | |
| 2053 | prog.basic_block = true; | | |
| 2054 | }, | | |
| 2055 | DW.LNS_const_add_pc => { | | |
| 2056 | const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range); | | |
| 2057 | prog.address += inc_addr; | | |
| 2058 | }, | | |
| 2059 | DW.LNS_fixed_advance_pc => { | | |
| 2060 | const arg = try di.dwarf_in_stream.readInt(u16, di.endian); | | |
| 2061 | prog.address += arg; | | |
| 2062 | }, | | |
| 2063 | DW.LNS_set_prologue_end => {}, | | |
| 2064 | else => { | | |
| 2065 | if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo; | | |
| 2066 | const len_bytes = standard_opcode_lengths[opcode - 1]; | | |
| 2067 | try di.dwarf_seekable_stream.seekBy(len_bytes); | | |
| 2068 | }, | | |
| 2069 | } | | |
| 2070 | } | | |
| 2071 | } | | |
| 2072 | | | |
| 2073 | return error.MissingDebugInfo; | | |
| 2074 | } | | |
| 2075 | | | |
| 2076 | const Func = struct { | 2287 | const Func = struct { |
| 2077 | pc_range: ?PcRange, | 2288 | pc_range: ?PcRange, |
| 2078 | name: ?[]u8, | 2289 | name: ?[]u8, |
| 2079 | }; | 2290 | }; |
| 2080 | | 2291 | |
| 2081 | fn getSymbolNameDwarf(di: *DwarfInfo, address: u64) ?[]const u8 { | | |
| 2082 | for (di.func_list.toSliceConst()) |*func| { | | |
| 2083 | if (func.pc_range) |range| { | | |
| 2084 | if (address >= range.start and address < range.end) { | | |
| 2085 | return func.name; | | |
| 2086 | } | | |
| 2087 | } | | |
| 2088 | } | | |
| 2089 | | | |
| 2090 | return null; | | |
| 2091 | } | | |
| 2092 | | | |
| 2093 | fn scanAllFunctions(di: *DwarfInfo) !void { | | |
| 2094 | const debug_info_end = di.debug_info.offset + di.debug_info.size; | | |
| 2095 | var this_unit_offset = di.debug_info.offset; | | |
| 2096 | | | |
| 2097 | while (this_unit_offset < debug_info_end) { | | |
| 2098 | try di.dwarf_seekable_stream.seekTo(this_unit_offset); | | |
| 2099 | | | |
| 2100 | var is_64: bool = undefined; | | |
| 2101 | const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64); | | |
| 2102 | if (unit_length == 0) return; | | |
| 2103 | const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4)); | | |
| 2104 | | | |
| 2105 | const version = try di.dwarf_in_stream.readInt(u16, di.endian); | | |
| 2106 | if (version < 2 or version > 5) return error.InvalidDebugInfo; | | |
| 2107 | | | |
| 2108 | const debug_abbrev_offset = if (is_64) try di.dwarf_in_stream.readInt(u64, di.endian) else try di.dwarf_in_stream.readInt(u32, di.endian); | | |
| 2109 | | | |
| 2110 | const address_size = try di.dwarf_in_stream.readByte(); | | |
| 2111 | if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; | | |
| 2112 | | | |
| 2113 | const compile_unit_pos = try di.dwarf_seekable_stream.getPos(); | | |
| 2114 | const abbrev_table = try getAbbrevTable(di, debug_abbrev_offset); | | |
| 2115 | | | |
| 2116 | try di.dwarf_seekable_stream.seekTo(compile_unit_pos); | | |
| 2117 | | | |
| 2118 | const next_unit_pos = this_unit_offset + next_offset; | | |
| 2119 | | | |
| 2120 | while ((try di.dwarf_seekable_stream.getPos()) < next_unit_pos) { | | |
| 2121 | const die_obj = (try parseDie(di, abbrev_table, is_64)) orelse continue; | | |
| 2122 | const after_die_offset = try di.dwarf_seekable_stream.getPos(); | | |
| 2123 | | | |
| 2124 | switch (die_obj.tag_id) { | | |
| 2125 | DW.TAG_subprogram, DW.TAG_inlined_subroutine, DW.TAG_subroutine, DW.TAG_entry_point => { | | |
| 2126 | const fn_name = x: { | | |
| 2127 | var depth: i32 = 3; | | |
| 2128 | var this_die_obj = die_obj; | | |
| 2129 | // Prenvent endless loops | | |
| 2130 | while (depth > 0) : (depth -= 1) { | | |
| 2131 | if (this_die_obj.getAttr(DW.AT_name)) |_| { | | |
| 2132 | const name = try this_die_obj.getAttrString(di, DW.AT_name); | | |
| 2133 | break :x name; | | |
| 2134 | } else if (this_die_obj.getAttr(DW.AT_abstract_origin)) |ref| { | | |
| 2135 | // Follow the DIE it points to and repeat | | |
| 2136 | const ref_offset = try this_die_obj.getAttrRef(DW.AT_abstract_origin); | | |
| 2137 | if (ref_offset > next_offset) return error.InvalidDebugInfo; | | |
| 2138 | try di.dwarf_seekable_stream.seekTo(this_unit_offset + ref_offset); | | |
| 2139 | this_die_obj = (try parseDie(di, abbrev_table, is_64)) orelse return error.InvalidDebugInfo; | | |
| 2140 | } else if (this_die_obj.getAttr(DW.AT_specification)) |ref| { | | |
| 2141 | // Follow the DIE it points to and repeat | | |
| 2142 | const ref_offset = try this_die_obj.getAttrRef(DW.AT_specification); | | |
| 2143 | if (ref_offset > next_offset) return error.InvalidDebugInfo; | | |
| 2144 | try di.dwarf_seekable_stream.seekTo(this_unit_offset + ref_offset); | | |
| 2145 | this_die_obj = (try parseDie(di, abbrev_table, is_64)) orelse return error.InvalidDebugInfo; | | |
| 2146 | } else { | | |
| 2147 | break :x null; | | |
| 2148 | } | | |
| 2149 | } | | |
| 2150 | | | |
| 2151 | break :x null; | | |
| 2152 | }; | | |
| 2153 | | | |
| 2154 | const pc_range = x: { | | |
| 2155 | if (die_obj.getAttrAddr(DW.AT_low_pc)) |low_pc| { | | |
| 2156 | if (die_obj.getAttr(DW.AT_high_pc)) |high_pc_value| { | | |
| 2157 | const pc_end = switch (high_pc_value.*) { | | |
| 2158 | FormValue.Address => |value| value, | | |
| 2159 | FormValue.Const => |value| b: { | | |
| 2160 | const offset = try value.asUnsignedLe(); | | |
| 2161 | break :b (low_pc + offset); | | |
| 2162 | }, | | |
| 2163 | else => return error.InvalidDebugInfo, | | |
| 2164 | }; | | |
| 2165 | break :x PcRange{ | | |
| 2166 | .start = low_pc, | | |
| 2167 | .end = pc_end, | | |
| 2168 | }; | | |
| 2169 | } else { | | |
| 2170 | break :x null; | | |
| 2171 | } | | |
| 2172 | } else |err| { | | |
| 2173 | if (err != error.MissingDebugInfo) return err; | | |
| 2174 | break :x null; | | |
| 2175 | } | | |
| 2176 | }; | | |
| 2177 | | | |
| 2178 | try di.func_list.append(Func{ | | |
| 2179 | .name = fn_name, | | |
| 2180 | .pc_range = pc_range, | | |
| 2181 | }); | | |
| 2182 | }, | | |
| 2183 | else => { | | |
| 2184 | continue; | | |
| 2185 | }, | | |
| 2186 | } | | |
| 2187 | | | |
| 2188 | try di.dwarf_seekable_stream.seekTo(after_die_offset); | | |
| 2189 | } | | |
| 2190 | | | |
| 2191 | this_unit_offset += next_offset; | | |
| 2192 | } | | |
| 2193 | } | | |
| 2194 | | | |
| 2195 | fn scanAllCompileUnits(di: *DwarfInfo) !void { | | |
| 2196 | const debug_info_end = di.debug_info.offset + di.debug_info.size; | | |
| 2197 | var this_unit_offset = di.debug_info.offset; | | |
| 2198 | | | |
| 2199 | while (this_unit_offset < debug_info_end) { | | |
| 2200 | try di.dwarf_seekable_stream.seekTo(this_unit_offset); | | |
| 2201 | | | |
| 2202 | var is_64: bool = undefined; | | |
| 2203 | const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64); | | |
| 2204 | if (unit_length == 0) return; | | |
| 2205 | const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4)); | | |
| 2206 | | | |
| 2207 | const version = try di.dwarf_in_stream.readInt(u16, di.endian); | | |
| 2208 | if (version < 2 or version > 5) return error.InvalidDebugInfo; | | |
| 2209 | | | |
| 2210 | const debug_abbrev_offset = if (is_64) try di.dwarf_in_stream.readInt(u64, di.endian) else try di.dwarf_in_stream.readInt(u32, di.endian); | | |
| 2211 | | | |
| 2212 | const address_size = try di.dwarf_in_stream.readByte(); | | |
| 2213 | if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; | | |
| 2214 | | | |
| 2215 | const compile_unit_pos = try di.dwarf_seekable_stream.getPos(); | | |
| 2216 | const abbrev_table = try getAbbrevTable(di, debug_abbrev_offset); | | |
| 2217 | | | |
| 2218 | try di.dwarf_seekable_stream.seekTo(compile_unit_pos); | | |
| 2219 | | | |
| 2220 | const compile_unit_die = try di.allocator().create(Die); | | |
| 2221 | compile_unit_die.* = (try parseDie(di, abbrev_table, is_64)) orelse return error.InvalidDebugInfo; | | |
| 2222 | | | |
| 2223 | if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo; | | |
| 2224 | | | |
| 2225 | const pc_range = x: { | | |
| 2226 | if (compile_unit_die.getAttrAddr(DW.AT_low_pc)) |low_pc| { | | |
| 2227 | if (compile_unit_die.getAttr(DW.AT_high_pc)) |high_pc_value| { | | |
| 2228 | const pc_end = switch (high_pc_value.*) { | | |
| 2229 | FormValue.Address => |value| value, | | |
| 2230 | FormValue.Const => |value| b: { | | |
| 2231 | const offset = try value.asUnsignedLe(); | | |
| 2232 | break :b (low_pc + offset); | | |
| 2233 | }, | | |
| 2234 | else => return error.InvalidDebugInfo, | | |
| 2235 | }; | | |
| 2236 | break :x PcRange{ | | |
| 2237 | .start = low_pc, | | |
| 2238 | .end = pc_end, | | |
| 2239 | }; | | |
| 2240 | } else { | | |
| 2241 | break :x null; | | |
| 2242 | } | | |
| 2243 | } else |err| { | | |
| 2244 | if (err != error.MissingDebugInfo) return err; | | |
| 2245 | break :x null; | | |
| 2246 | } | | |
| 2247 | }; | | |
| 2248 | | | |
| 2249 | try di.compile_unit_list.append(CompileUnit{ | | |
| 2250 | .version = version, | | |
| 2251 | .is_64 = is_64, | | |
| 2252 | .pc_range = pc_range, | | |
| 2253 | .die = compile_unit_die, | | |
| 2254 | }); | | |
| 2255 | | | |
| 2256 | this_unit_offset += next_offset; | | |
| 2257 | } | | |
| 2258 | } | | |
| 2259 | | | |
| 2260 | fn findCompileUnit(di: *DwarfInfo, target_address: u64) !*const CompileUnit { | | |
| 2261 | for (di.compile_unit_list.toSlice()) |*compile_unit| { | | |
| 2262 | if (compile_unit.pc_range) |range| { | | |
| 2263 | if (target_address >= range.start and target_address < range.end) return compile_unit; | | |
| 2264 | } | | |
| 2265 | if (compile_unit.die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| { | | |
| 2266 | var base_address: usize = 0; | | |
| 2267 | if (di.debug_ranges) |debug_ranges| { | | |
| 2268 | try di.dwarf_seekable_stream.seekTo(debug_ranges.offset + ranges_offset); | | |
| 2269 | while (true) { | | |
| 2270 | const begin_addr = try di.dwarf_in_stream.readIntLittle(usize); | | |
| 2271 | const end_addr = try di.dwarf_in_stream.readIntLittle(usize); | | |
| 2272 | if (begin_addr == 0 and end_addr == 0) { | | |
| 2273 | break; | | |
| 2274 | } | | |
| 2275 | if (begin_addr == maxInt(usize)) { | | |
| 2276 | base_address = begin_addr; | | |
| 2277 | continue; | | |
| 2278 | } | | |
| 2279 | if (target_address >= begin_addr and target_address < end_addr) { | | |
| 2280 | return compile_unit; | | |
| 2281 | } | | |
| 2282 | } | | |
| 2283 | } | | |
| 2284 | } else |err| { | | |
| 2285 | if (err != error.MissingDebugInfo) return err; | | |
| 2286 | continue; | | |
| 2287 | } | | |
| 2288 | } | | |
| 2289 | return error.MissingDebugInfo; | | |
| 2290 | } | | |
| 2291 | | | |
| 2292 | fn readIntMem(ptr: *[*]const u8, comptime T: type, endian: builtin.Endian) T { | 2292 | fn readIntMem(ptr: *[*]const u8, comptime T: type, endian: builtin.Endian) T { |
| 2293 | // TODO https://github.com/ziglang/zig/issues/863 | 2293 | // TODO https://github.com/ziglang/zig/issues/863 |
| 2294 | const size = (T.bit_count + 7) / 8; | 2294 | const size = (T.bit_count + 7) / 8; |