| ... | ... | @@ -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 | 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 | 739 | fn printLineInfo( |
| ... | ... | @@ -1034,8 +992,8 @@ pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: *mem.Allocator) !void { |
| 1034 | 992 | di.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator); |
| 1035 | 993 | di.compile_unit_list = ArrayList(CompileUnit).init(allocator); |
| 1036 | 994 | di.func_list = ArrayList(Func).init(allocator); |
| 1037 | | try scanAllFunctions(di); |
| 1038 | | try scanAllCompileUnits(di); |
| 995 | try di.scanAllFunctions(); |
| 996 | try di.scanAllCompileUnits(); |
| 1039 | 997 | } |
| 1040 | 998 | |
| 1041 | 999 | pub fn openElfDebugInfo( |
| ... | ... | @@ -1253,6 +1211,492 @@ pub const DwarfInfo = struct { |
| 1253 | 1211 | pub fn readString(self: *DwarfInfo) ![]u8 { |
| 1254 | 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 | 1702 | pub const DebugInfo = switch (builtin.os) { |
| ... | ... | @@ -1391,7 +1835,7 @@ const Die = struct { |
| 1391 | 1835 | const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; |
| 1392 | 1836 | return switch (form_value.*) { |
| 1393 | 1837 | FormValue.String => |value| value, |
| 1394 | | FormValue.StrPtr => |offset| getString(di, offset), |
| 1838 | FormValue.StrPtr => |offset| di.getString(offset), |
| 1395 | 1839 | else => error.InvalidDebugInfo, |
| 1396 | 1840 | }; |
| 1397 | 1841 | } |
| ... | ... | @@ -1504,12 +1948,6 @@ fn readStringRaw(allocator: *mem.Allocator, in_stream: var) ![]u8 { |
| 1504 | 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 | 1951 | // TODO the noasyncs here are workarounds |
| 1514 | 1952 | fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8 { |
| 1515 | 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 | 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 | 2077 | fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*const AbbrevTableEntry { |
| 1681 | 2078 | for (abbrev_table.toSliceConst()) |*table_entry| { |
| 1682 | 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 | 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 | 2084 | fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: usize) !LineInfo { |
| 1708 | 2085 | const ofile = symbol.ofile orelse return error.MissingDebugInfo; |
| 1709 | 2086 | const gop = try di.ofiles.getOrPut(ofile); |
| ... | ... | @@ -1907,388 +2284,11 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u |
| 1907 | 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 | 2287 | const Func = struct { |
| 2077 | 2288 | pc_range: ?PcRange, |
| 2078 | 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 | 2292 | fn readIntMem(ptr: *[*]const u8, comptime T: type, endian: builtin.Endian) T { |
| 2293 | 2293 | // TODO https://github.com/ziglang/zig/issues/863 |
| 2294 | 2294 | const size = (T.bit_count + 7) / 8; |