| ... | ... | @@ -26,7 +26,6 @@ const cast = std.math.cast; |
| 26 | 26 | const maxInt = std.math.maxInt; |
| 27 | 27 | const MemoryAccessor = std.debug.MemoryAccessor; |
| 28 | 28 | const Path = std.Build.Cache.Path; |
| 29 | | |
| 30 | 29 | const FixedBufferReader = std.debug.FixedBufferReader; |
| 31 | 30 | |
| 32 | 31 | const Dwarf = @This(); |
| ... | ... | @@ -35,22 +34,36 @@ pub const expression = @import("Dwarf/expression.zig"); |
| 35 | 34 | pub const abi = @import("Dwarf/abi.zig"); |
| 36 | 35 | pub const call_frame = @import("Dwarf/call_frame.zig"); |
| 37 | 36 | |
| 37 | /// Useful to temporarily enable while working on this file. |
| 38 | const debug_debug_mode = false; |
| 39 | |
| 38 | 40 | endian: std.builtin.Endian, |
| 39 | 41 | sections: SectionArray = null_section_array, |
| 40 | 42 | is_macho: bool, |
| 41 | | compile_units_sorted: bool, |
| 42 | 43 | |
| 43 | | // Filled later by the initializer |
| 44 | /// Filled later by the initializer |
| 44 | 45 | abbrev_table_list: std.ArrayListUnmanaged(Abbrev.Table) = .{}, |
| 46 | /// Filled later by the initializer |
| 45 | 47 | compile_unit_list: std.ArrayListUnmanaged(CompileUnit) = .{}, |
| 48 | /// Filled later by the initializer |
| 46 | 49 | func_list: std.ArrayListUnmanaged(Func) = .{}, |
| 47 | 50 | |
| 48 | 51 | eh_frame_hdr: ?ExceptionFrameHeader = null, |
| 49 | | // These lookup tables are only used if `eh_frame_hdr` is null |
| 52 | /// These lookup tables are only used if `eh_frame_hdr` is null |
| 50 | 53 | cie_map: std.AutoArrayHashMapUnmanaged(u64, CommonInformationEntry) = .{}, |
| 51 | | // Sorted by start_pc |
| 54 | /// Sorted by start_pc |
| 52 | 55 | fde_list: std.ArrayListUnmanaged(FrameDescriptionEntry) = .{}, |
| 53 | 56 | |
| 57 | /// Populated by `populateRanges`. |
| 58 | ranges: std.ArrayListUnmanaged(Range) = .{}, |
| 59 | |
| 60 | pub const Range = struct { |
| 61 | start: u64, |
| 62 | end: u64, |
| 63 | /// Index into `compile_unit_list`. |
| 64 | compile_unit_index: usize, |
| 65 | }; |
| 66 | |
| 54 | 67 | pub const Section = struct { |
| 55 | 68 | data: []const u8, |
| 56 | 69 | // Module-relative virtual address. |
| ... | ... | @@ -154,6 +167,16 @@ pub const CompileUnit = struct { |
| 154 | 167 | column: u32, |
| 155 | 168 | /// Offset by 1 depending on whether Dwarf version is >= 5. |
| 156 | 169 | file: u32, |
| 170 | |
| 171 | pub const invalid: LineEntry = .{ |
| 172 | .line = undefined, |
| 173 | .column = undefined, |
| 174 | .file = std.math.maxInt(u32), |
| 175 | }; |
| 176 | |
| 177 | pub fn isInvalid(le: LineEntry) bool { |
| 178 | return le.file == invalid.file; |
| 179 | } |
| 157 | 180 | }; |
| 158 | 181 | |
| 159 | 182 | pub fn findSource(slc: *const SrcLocCache, address: u64) !LineEntry { |
| ... | ... | @@ -799,6 +822,7 @@ pub fn deinit(di: *Dwarf, gpa: Allocator) void { |
| 799 | 822 | di.func_list.deinit(gpa); |
| 800 | 823 | di.cie_map.deinit(gpa); |
| 801 | 824 | di.fde_list.deinit(gpa); |
| 825 | di.ranges.deinit(gpa); |
| 802 | 826 | di.* = undefined; |
| 803 | 827 | } |
| 804 | 828 | |
| ... | ... | @@ -985,8 +1009,8 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) ScanError!void { |
| 985 | 1009 | try di.func_list.append(allocator, .{ |
| 986 | 1010 | .name = fn_name, |
| 987 | 1011 | .pc_range = .{ |
| 988 | | .start = range.start_addr, |
| 989 | | .end = range.end_addr, |
| 1012 | .start = range.start, |
| 1013 | .end = range.end, |
| 990 | 1014 | }, |
| 991 | 1015 | }); |
| 992 | 1016 | } |
| ... | ... | @@ -1096,37 +1120,38 @@ fn scanAllCompileUnits(di: *Dwarf, allocator: Allocator) ScanError!void { |
| 1096 | 1120 | } |
| 1097 | 1121 | } |
| 1098 | 1122 | |
| 1099 | | /// Populate missing PC ranges in compilation units, and then sort them by start address. |
| 1100 | | /// Does not guarantee pc_range to be non-null because there could be missing debug info. |
| 1101 | | pub fn sortCompileUnits(d: *Dwarf) ScanError!void { |
| 1102 | | assert(!d.compile_units_sorted); |
| 1123 | pub fn populateRanges(d: *Dwarf, gpa: Allocator) ScanError!void { |
| 1124 | assert(d.ranges.items.len == 0); |
| 1103 | 1125 | |
| 1104 | | for (d.compile_unit_list.items) |*cu| { |
| 1105 | | if (cu.pc_range != null) continue; |
| 1126 | for (d.compile_unit_list.items, 0..) |*cu, cu_index| { |
| 1127 | if (cu.pc_range) |range| { |
| 1128 | try d.ranges.append(gpa, .{ |
| 1129 | .start = range.start, |
| 1130 | .end = range.end, |
| 1131 | .compile_unit_index = cu_index, |
| 1132 | }); |
| 1133 | continue; |
| 1134 | } |
| 1106 | 1135 | const ranges_value = cu.die.getAttr(AT.ranges) orelse continue; |
| 1107 | 1136 | var iter = DebugRangeIterator.init(ranges_value, d, cu) catch continue; |
| 1108 | | var start: u64 = maxInt(u64); |
| 1109 | | var end: u64 = 0; |
| 1110 | 1137 | while (try iter.next()) |range| { |
| 1111 | | start = @min(start, range.start_addr); |
| 1112 | | end = @max(end, range.end_addr); |
| 1138 | // Not sure why LLVM thinks it's OK to emit these... |
| 1139 | if (range.start == range.end) continue; |
| 1140 | |
| 1141 | try d.ranges.append(gpa, .{ |
| 1142 | .start = range.start, |
| 1143 | .end = range.end, |
| 1144 | .compile_unit_index = cu_index, |
| 1145 | }); |
| 1113 | 1146 | } |
| 1114 | | if (end != 0) cu.pc_range = .{ |
| 1115 | | .start = start, |
| 1116 | | .end = end, |
| 1117 | | }; |
| 1118 | 1147 | } |
| 1119 | 1148 | |
| 1120 | | std.mem.sortUnstable(CompileUnit, d.compile_unit_list.items, {}, struct { |
| 1121 | | pub fn lessThan(ctx: void, a: CompileUnit, b: CompileUnit) bool { |
| 1149 | std.mem.sortUnstable(Range, d.ranges.items, {}, struct { |
| 1150 | pub fn lessThan(ctx: void, a: Range, b: Range) bool { |
| 1122 | 1151 | _ = ctx; |
| 1123 | | const a_range = a.pc_range orelse return false; |
| 1124 | | const b_range = b.pc_range orelse return true; |
| 1125 | | return a_range.start < b_range.start; |
| 1152 | return a.start < b.start; |
| 1126 | 1153 | } |
| 1127 | 1154 | }.lessThan); |
| 1128 | | |
| 1129 | | d.compile_units_sorted = true; |
| 1130 | 1155 | } |
| 1131 | 1156 | |
| 1132 | 1157 | const DebugRangeIterator = struct { |
| ... | ... | @@ -1184,7 +1209,7 @@ const DebugRangeIterator = struct { |
| 1184 | 1209 | } |
| 1185 | 1210 | |
| 1186 | 1211 | // Returns the next range in the list, or null if the end was reached. |
| 1187 | | pub fn next(self: *@This()) !?struct { start_addr: u64, end_addr: u64 } { |
| 1212 | pub fn next(self: *@This()) !?PcRange { |
| 1188 | 1213 | switch (self.section_type) { |
| 1189 | 1214 | .debug_rnglists => { |
| 1190 | 1215 | const kind = try self.fbr.readByte(); |
| ... | ... | @@ -1203,8 +1228,8 @@ const DebugRangeIterator = struct { |
| 1203 | 1228 | const end_addr = try self.di.readDebugAddr(self.compile_unit.*, end_index); |
| 1204 | 1229 | |
| 1205 | 1230 | return .{ |
| 1206 | | .start_addr = start_addr, |
| 1207 | | .end_addr = end_addr, |
| 1231 | .start = start_addr, |
| 1232 | .end = end_addr, |
| 1208 | 1233 | }; |
| 1209 | 1234 | }, |
| 1210 | 1235 | RLE.startx_length => { |
| ... | ... | @@ -1215,8 +1240,8 @@ const DebugRangeIterator = struct { |
| 1215 | 1240 | const end_addr = start_addr + len; |
| 1216 | 1241 | |
| 1217 | 1242 | return .{ |
| 1218 | | .start_addr = start_addr, |
| 1219 | | .end_addr = end_addr, |
| 1243 | .start = start_addr, |
| 1244 | .end = end_addr, |
| 1220 | 1245 | }; |
| 1221 | 1246 | }, |
| 1222 | 1247 | RLE.offset_pair => { |
| ... | ... | @@ -1225,8 +1250,8 @@ const DebugRangeIterator = struct { |
| 1225 | 1250 | |
| 1226 | 1251 | // This is the only kind that uses the base address |
| 1227 | 1252 | return .{ |
| 1228 | | .start_addr = self.base_address + start_addr, |
| 1229 | | .end_addr = self.base_address + end_addr, |
| 1253 | .start = self.base_address + start_addr, |
| 1254 | .end = self.base_address + end_addr, |
| 1230 | 1255 | }; |
| 1231 | 1256 | }, |
| 1232 | 1257 | RLE.base_address => { |
| ... | ... | @@ -1238,8 +1263,8 @@ const DebugRangeIterator = struct { |
| 1238 | 1263 | const end_addr = try self.fbr.readInt(usize); |
| 1239 | 1264 | |
| 1240 | 1265 | return .{ |
| 1241 | | .start_addr = start_addr, |
| 1242 | | .end_addr = end_addr, |
| 1266 | .start = start_addr, |
| 1267 | .end = end_addr, |
| 1243 | 1268 | }; |
| 1244 | 1269 | }, |
| 1245 | 1270 | RLE.start_length => { |
| ... | ... | @@ -1248,8 +1273,8 @@ const DebugRangeIterator = struct { |
| 1248 | 1273 | const end_addr = start_addr + len; |
| 1249 | 1274 | |
| 1250 | 1275 | return .{ |
| 1251 | | .start_addr = start_addr, |
| 1252 | | .end_addr = end_addr, |
| 1276 | .start = start_addr, |
| 1277 | .end = end_addr, |
| 1253 | 1278 | }; |
| 1254 | 1279 | }, |
| 1255 | 1280 | else => return bad(), |
| ... | ... | @@ -1267,8 +1292,8 @@ const DebugRangeIterator = struct { |
| 1267 | 1292 | } |
| 1268 | 1293 | |
| 1269 | 1294 | return .{ |
| 1270 | | .start_addr = self.base_address + start_addr, |
| 1271 | | .end_addr = self.base_address + end_addr, |
| 1295 | .start = self.base_address + start_addr, |
| 1296 | .end = self.base_address + end_addr, |
| 1272 | 1297 | }; |
| 1273 | 1298 | }, |
| 1274 | 1299 | else => unreachable, |
| ... | ... | @@ -1286,7 +1311,7 @@ pub fn findCompileUnit(di: *const Dwarf, target_address: u64) !*CompileUnit { |
| 1286 | 1311 | const ranges_value = compile_unit.die.getAttr(AT.ranges) orelse continue; |
| 1287 | 1312 | var iter = DebugRangeIterator.init(ranges_value, di, compile_unit) catch continue; |
| 1288 | 1313 | while (try iter.next()) |range| { |
| 1289 | | if (target_address >= range.start_addr and target_address < range.end_addr) return compile_unit; |
| 1314 | if (target_address >= range.start and target_address < range.end) return compile_unit; |
| 1290 | 1315 | } |
| 1291 | 1316 | } |
| 1292 | 1317 | |
| ... | ... | @@ -1387,6 +1412,7 @@ fn parseDie( |
| 1387 | 1412 | }; |
| 1388 | 1413 | } |
| 1389 | 1414 | |
| 1415 | /// Ensures that addresses in the returned LineTable are monotonically increasing. |
| 1390 | 1416 | fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !CompileUnit.SrcLocCache { |
| 1391 | 1417 | const compile_unit_cwd = try compile_unit.die.getAttrString(d, AT.comp_dir, d.section(.debug_line_str), compile_unit.*); |
| 1392 | 1418 | const line_info_offset = try compile_unit.die.getAttrSecOffset(AT.stmt_list); |
| ... | ... | @@ -1562,8 +1588,19 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) ! |
| 1562 | 1588 | const sub_op = try fbr.readByte(); |
| 1563 | 1589 | switch (sub_op) { |
| 1564 | 1590 | DW.LNE.end_sequence => { |
| 1565 | | prog.end_sequence = true; |
| 1566 | | try prog.addRow(gpa, &line_table); |
| 1591 | // The row being added here is an "end" address, meaning |
| 1592 | // that it does not map to the source location here - |
| 1593 | // rather it marks the previous address as the last address |
| 1594 | // that maps to this source location. |
| 1595 | |
| 1596 | // In this implementation we don't mark end of addresses. |
| 1597 | // This is a performance optimization based on the fact |
| 1598 | // that we don't need to know if an address is missing |
| 1599 | // source location info; we are only interested in being |
| 1600 | // able to look up source location info for addresses that |
| 1601 | // are known to have debug info. |
| 1602 | //if (debug_debug_mode) assert(!line_table.contains(prog.address)); |
| 1603 | //try line_table.put(gpa, prog.address, CompileUnit.SrcLocCache.LineEntry.invalid); |
| 1567 | 1604 | prog.reset(); |
| 1568 | 1605 | }, |
| 1569 | 1606 | DW.LNE.set_address => { |
| ... | ... | @@ -1638,6 +1675,17 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) ! |
| 1638 | 1675 | } |
| 1639 | 1676 | } |
| 1640 | 1677 | |
| 1678 | // Dwarf standard v5, 6.2.5 says |
| 1679 | // > Within a sequence, addresses and operation pointers may only increase. |
| 1680 | // However, this is empirically not the case in reality, so we sort here. |
| 1681 | line_table.sortUnstable(struct { |
| 1682 | keys: []const u64, |
| 1683 | |
| 1684 | pub fn lessThan(ctx: @This(), a_index: usize, b_index: usize) bool { |
| 1685 | return ctx.keys[a_index] < ctx.keys[b_index]; |
| 1686 | } |
| 1687 | }{ .keys = line_table.keys() }); |
| 1688 | |
| 1641 | 1689 | return .{ |
| 1642 | 1690 | .line_table = line_table, |
| 1643 | 1691 | .directories = try directories.toOwnedSlice(gpa), |
| ... | ... | @@ -1882,7 +1930,6 @@ const LineNumberProgram = struct { |
| 1882 | 1930 | version: u16, |
| 1883 | 1931 | is_stmt: bool, |
| 1884 | 1932 | basic_block: bool, |
| 1885 | | end_sequence: bool, |
| 1886 | 1933 | |
| 1887 | 1934 | default_is_stmt: bool, |
| 1888 | 1935 | |
| ... | ... | @@ -1894,7 +1941,6 @@ const LineNumberProgram = struct { |
| 1894 | 1941 | self.column = 0; |
| 1895 | 1942 | self.is_stmt = self.default_is_stmt; |
| 1896 | 1943 | self.basic_block = false; |
| 1897 | | self.end_sequence = false; |
| 1898 | 1944 | } |
| 1899 | 1945 | |
| 1900 | 1946 | pub fn init(is_stmt: bool, version: u16) LineNumberProgram { |
| ... | ... | @@ -1906,13 +1952,16 @@ const LineNumberProgram = struct { |
| 1906 | 1952 | .version = version, |
| 1907 | 1953 | .is_stmt = is_stmt, |
| 1908 | 1954 | .basic_block = false, |
| 1909 | | .end_sequence = false, |
| 1910 | 1955 | .default_is_stmt = is_stmt, |
| 1911 | 1956 | }; |
| 1912 | 1957 | } |
| 1913 | 1958 | |
| 1914 | 1959 | pub fn addRow(prog: *LineNumberProgram, gpa: Allocator, table: *CompileUnit.SrcLocCache.LineTable) !void { |
| 1915 | | if (prog.line == 0) return; // garbage data |
| 1960 | if (prog.line == 0) { |
| 1961 | //if (debug_debug_mode) @panic("garbage line data"); |
| 1962 | return; |
| 1963 | } |
| 1964 | if (debug_debug_mode) assert(!table.contains(prog.address)); |
| 1916 | 1965 | try table.put(gpa, prog.address, .{ |
| 1917 | 1966 | .line = cast(u32, prog.line) orelse maxInt(u32), |
| 1918 | 1967 | .column = cast(u32, prog.column) orelse maxInt(u32), |
| ... | ... | @@ -1959,12 +2008,12 @@ pub fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u8 { |
| 1959 | 2008 | /// This function is to make it handy to comment out the return and make it |
| 1960 | 2009 | /// into a crash when working on this file. |
| 1961 | 2010 | pub fn bad() error{InvalidDebugInfo} { |
| 1962 | | //if (true) @panic("bad dwarf"); // can be handy to uncomment when working on this file |
| 2011 | if (debug_debug_mode) @panic("bad dwarf"); |
| 1963 | 2012 | return error.InvalidDebugInfo; |
| 1964 | 2013 | } |
| 1965 | 2014 | |
| 1966 | 2015 | fn missing() error{MissingDebugInfo} { |
| 1967 | | //if (true) @panic("missing dwarf"); // can be handy to uncomment when working on this file |
| 2016 | if (debug_debug_mode) @panic("missing dwarf"); |
| 1968 | 2017 | return error.MissingDebugInfo; |
| 1969 | 2018 | } |
| 1970 | 2019 | |
| ... | ... | @@ -2345,7 +2394,6 @@ pub const ElfModule = struct { |
| 2345 | 2394 | .endian = endian, |
| 2346 | 2395 | .sections = sections, |
| 2347 | 2396 | .is_macho = false, |
| 2348 | | .compile_units_sorted = false, |
| 2349 | 2397 | }; |
| 2350 | 2398 | |
| 2351 | 2399 | try Dwarf.open(&di, gpa); |