| ... | ... | @@ -963,59 +963,61 @@ pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: *mem.Allocator) !void { |
| 963 | 963 | |
| 964 | 964 | pub fn openElfDebugInfo( |
| 965 | 965 | allocator: *mem.Allocator, |
| 966 | | elf_seekable_stream: *DwarfSeekableStream, |
| 967 | | elf_in_stream: *DwarfInStream, |
| 966 | data: []u8, |
| 968 | 967 | ) !DwarfInfo { |
| 969 | | var efile = try elf.Elf.openStream(allocator, elf_seekable_stream, elf_in_stream); |
| 970 | | errdefer efile.close(); |
| 968 | var seekable_stream = io.SliceSeekableInStream.init(data); |
| 969 | var efile = try elf.Elf.openStream( |
| 970 | allocator, |
| 971 | @ptrCast(*DwarfSeekableStream, &seekable_stream.seekable_stream), |
| 972 | @ptrCast(*DwarfInStream, &seekable_stream.stream), |
| 973 | ); |
| 974 | defer efile.close(); |
| 975 | |
| 976 | const debug_info = (try efile.findSection(".debug_info")) orelse |
| 977 | return error.MissingDebugInfo; |
| 978 | const debug_abbrev = (try efile.findSection(".debug_abbrev")) orelse |
| 979 | return error.MissingDebugInfo; |
| 980 | const debug_str = (try efile.findSection(".debug_str")) orelse |
| 981 | return error.MissingDebugInfo; |
| 982 | const debug_line = (try efile.findSection(".debug_line")) orelse |
| 983 | return error.MissingDebugInfo; |
| 984 | const opt_debug_ranges = try efile.findSection(".debug_ranges"); |
| 971 | 985 | |
| 972 | 986 | var di = DwarfInfo{ |
| 973 | | .dwarf_seekable_stream = elf_seekable_stream, |
| 974 | | .dwarf_in_stream = elf_in_stream, |
| 975 | 987 | .endian = efile.endian, |
| 976 | | .debug_info = (try findDwarfSectionFromElf(&efile, ".debug_info")) orelse return error.MissingDebugInfo, |
| 977 | | .debug_abbrev = (try findDwarfSectionFromElf(&efile, ".debug_abbrev")) orelse return error.MissingDebugInfo, |
| 978 | | .debug_str = (try findDwarfSectionFromElf(&efile, ".debug_str")) orelse return error.MissingDebugInfo, |
| 979 | | .debug_line = (try findDwarfSectionFromElf(&efile, ".debug_line")) orelse return error.MissingDebugInfo, |
| 980 | | .debug_ranges = (try findDwarfSectionFromElf(&efile, ".debug_ranges")), |
| 981 | | .abbrev_table_list = undefined, |
| 982 | | .compile_unit_list = undefined, |
| 983 | | .func_list = undefined, |
| 988 | .debug_info = (data[@intCast(usize, debug_info.offset)..@intCast(usize, debug_info.offset + debug_info.size)]), |
| 989 | .debug_abbrev = (data[@intCast(usize, debug_abbrev.offset)..@intCast(usize, debug_abbrev.offset + debug_abbrev.size)]), |
| 990 | .debug_str = (data[@intCast(usize, debug_str.offset)..@intCast(usize, debug_str.offset + debug_str.size)]), |
| 991 | .debug_line = (data[@intCast(usize, debug_line.offset)..@intCast(usize, debug_line.offset + debug_line.size)]), |
| 992 | .debug_ranges = if (opt_debug_ranges) |debug_ranges| |
| 993 | data[@intCast(usize, debug_ranges.offset)..@intCast(usize, debug_ranges.offset + debug_ranges.size)] |
| 994 | else |
| 995 | null, |
| 984 | 996 | }; |
| 997 | |
| 998 | efile.close(); |
| 999 | |
| 985 | 1000 | try openDwarfDebugInfo(&di, allocator); |
| 986 | 1001 | return di; |
| 987 | 1002 | } |
| 988 | 1003 | |
| 989 | 1004 | fn openSelfDebugInfoPosix(allocator: *mem.Allocator) !DwarfInfo { |
| 990 | | const S = struct { |
| 991 | | var self_exe_file: File = undefined; |
| 992 | | var self_exe_mmap_seekable: io.SliceSeekableInStream = undefined; |
| 993 | | }; |
| 994 | | |
| 995 | | S.self_exe_file = try fs.openSelfExe(); |
| 996 | | errdefer S.self_exe_file.close(); |
| 1005 | var exe_file = try fs.openSelfExe(); |
| 1006 | errdefer exe_file.close(); |
| 997 | 1007 | |
| 998 | | const self_exe_len = math.cast(usize, try S.self_exe_file.getEndPos()) catch return error.DebugInfoTooLarge; |
| 999 | | const self_exe_mmap_len = mem.alignForward(self_exe_len, mem.page_size); |
| 1000 | | const self_exe_mmap = try os.mmap( |
| 1008 | const exe_len = math.cast(usize, try exe_file.getEndPos()) catch |
| 1009 | return error.DebugInfoTooLarge; |
| 1010 | const exe_mmap = try os.mmap( |
| 1001 | 1011 | null, |
| 1002 | | self_exe_mmap_len, |
| 1012 | exe_len, |
| 1003 | 1013 | os.PROT_READ, |
| 1004 | 1014 | os.MAP_SHARED, |
| 1005 | | S.self_exe_file.handle, |
| 1015 | exe_file.handle, |
| 1006 | 1016 | 0, |
| 1007 | 1017 | ); |
| 1008 | | errdefer os.munmap(self_exe_mmap); |
| 1018 | errdefer os.munmap(exe_mmap); |
| 1009 | 1019 | |
| 1010 | | S.self_exe_mmap_seekable = io.SliceSeekableInStream.init(self_exe_mmap); |
| 1011 | | |
| 1012 | | return openElfDebugInfo( |
| 1013 | | allocator, |
| 1014 | | // TODO https://github.com/ziglang/zig/issues/764 |
| 1015 | | @ptrCast(*DwarfSeekableStream, &S.self_exe_mmap_seekable.seekable_stream), |
| 1016 | | // TODO https://github.com/ziglang/zig/issues/764 |
| 1017 | | @ptrCast(*DwarfInStream, &S.self_exe_mmap_seekable.stream), |
| 1018 | | ); |
| 1020 | return openElfDebugInfo(allocator, exe_mmap); |
| 1019 | 1021 | } |
| 1020 | 1022 | |
| 1021 | 1023 | fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { |
| ... | ... | @@ -1142,41 +1144,26 @@ const MachoSymbol = struct { |
| 1142 | 1144 | } |
| 1143 | 1145 | }; |
| 1144 | 1146 | |
| 1145 | | const MachOFile = struct { |
| 1146 | | bytes: []align(@alignOf(macho.mach_header_64)) const u8, |
| 1147 | | sect_debug_info: ?*const macho.section_64, |
| 1148 | | sect_debug_line: ?*const macho.section_64, |
| 1149 | | }; |
| 1150 | | |
| 1151 | 1147 | pub const DwarfSeekableStream = io.SeekableStream(anyerror, anyerror); |
| 1152 | 1148 | pub const DwarfInStream = io.InStream(anyerror); |
| 1153 | 1149 | |
| 1154 | 1150 | pub const DwarfInfo = struct { |
| 1155 | | dwarf_seekable_stream: *DwarfSeekableStream, |
| 1156 | | dwarf_in_stream: *DwarfInStream, |
| 1157 | 1151 | endian: builtin.Endian, |
| 1158 | | debug_info: Section, |
| 1159 | | debug_abbrev: Section, |
| 1160 | | debug_str: Section, |
| 1161 | | debug_line: Section, |
| 1162 | | debug_ranges: ?Section, |
| 1163 | | abbrev_table_list: ArrayList(AbbrevTableHeader), |
| 1164 | | compile_unit_list: ArrayList(CompileUnit), |
| 1165 | | func_list: ArrayList(Func), |
| 1166 | | |
| 1167 | | pub const Section = struct { |
| 1168 | | offset: u64, |
| 1169 | | size: u64, |
| 1170 | | }; |
| 1152 | // No memory is owned by the DwarfInfo |
| 1153 | debug_info: []u8, |
| 1154 | debug_abbrev: []u8, |
| 1155 | debug_str: []u8, |
| 1156 | debug_line: []u8, |
| 1157 | debug_ranges: ?[]u8, |
| 1158 | // Filled later by the initializer |
| 1159 | abbrev_table_list: ArrayList(AbbrevTableHeader) = undefined, |
| 1160 | compile_unit_list: ArrayList(CompileUnit) = undefined, |
| 1161 | func_list: ArrayList(Func) = undefined, |
| 1171 | 1162 | |
| 1172 | 1163 | pub fn allocator(self: DwarfInfo) *mem.Allocator { |
| 1173 | 1164 | return self.abbrev_table_list.allocator; |
| 1174 | 1165 | } |
| 1175 | 1166 | |
| 1176 | | pub fn readString(self: *DwarfInfo) ![]u8 { |
| 1177 | | return readStringRaw(self.allocator(), self.dwarf_in_stream); |
| 1178 | | } |
| 1179 | | |
| 1180 | 1167 | /// This function works in freestanding mode. |
| 1181 | 1168 | /// fn printLineFromFile(out_stream: var, line_info: LineInfo) !void |
| 1182 | 1169 | pub fn printSourceAtAddress( |
| ... | ... | @@ -1222,35 +1209,38 @@ pub const DwarfInfo = struct { |
| 1222 | 1209 | } |
| 1223 | 1210 | |
| 1224 | 1211 | fn scanAllFunctions(di: *DwarfInfo) !void { |
| 1225 | | const debug_info_end = di.debug_info.offset + di.debug_info.size; |
| 1226 | | var this_unit_offset = di.debug_info.offset; |
| 1212 | var s = io.SliceSeekableInStream.init(di.debug_info); |
| 1213 | var this_unit_offset: u64 = 0; |
| 1227 | 1214 | |
| 1228 | | while (this_unit_offset < debug_info_end) { |
| 1229 | | try di.dwarf_seekable_stream.seekTo(this_unit_offset); |
| 1215 | while (true) { |
| 1216 | s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) { |
| 1217 | error.EndOfStream => return, |
| 1218 | else => return err, |
| 1219 | }; |
| 1230 | 1220 | |
| 1231 | 1221 | var is_64: bool = undefined; |
| 1232 | | const unit_length = try readInitialLength(@TypeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64); |
| 1222 | const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64); |
| 1233 | 1223 | if (unit_length == 0) return; |
| 1234 | 1224 | const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4)); |
| 1235 | 1225 | |
| 1236 | | const version = try di.dwarf_in_stream.readInt(u16, di.endian); |
| 1226 | const version = try s.stream.readInt(u16, di.endian); |
| 1237 | 1227 | if (version < 2 or version > 5) return error.InvalidDebugInfo; |
| 1238 | 1228 | |
| 1239 | | 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); |
| 1229 | const debug_abbrev_offset = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian); |
| 1240 | 1230 | |
| 1241 | | const address_size = try di.dwarf_in_stream.readByte(); |
| 1231 | const address_size = try s.stream.readByte(); |
| 1242 | 1232 | if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; |
| 1243 | 1233 | |
| 1244 | | const compile_unit_pos = try di.dwarf_seekable_stream.getPos(); |
| 1234 | const compile_unit_pos = try s.seekable_stream.getPos(); |
| 1245 | 1235 | const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset); |
| 1246 | 1236 | |
| 1247 | | try di.dwarf_seekable_stream.seekTo(compile_unit_pos); |
| 1237 | try s.seekable_stream.seekTo(compile_unit_pos); |
| 1248 | 1238 | |
| 1249 | 1239 | const next_unit_pos = this_unit_offset + next_offset; |
| 1250 | 1240 | |
| 1251 | | while ((try di.dwarf_seekable_stream.getPos()) < next_unit_pos) { |
| 1252 | | const die_obj = (try di.parseDie(abbrev_table, is_64)) orelse continue; |
| 1253 | | const after_die_offset = try di.dwarf_seekable_stream.getPos(); |
| 1241 | while ((try s.seekable_stream.getPos()) < next_unit_pos) { |
| 1242 | const die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse continue; |
| 1243 | const after_die_offset = try s.seekable_stream.getPos(); |
| 1254 | 1244 | |
| 1255 | 1245 | switch (die_obj.tag_id) { |
| 1256 | 1246 | DW.TAG_subprogram, DW.TAG_inlined_subroutine, DW.TAG_subroutine, DW.TAG_entry_point => { |
| ... | ... | @@ -1266,14 +1256,14 @@ pub const DwarfInfo = struct { |
| 1266 | 1256 | // Follow the DIE it points to and repeat |
| 1267 | 1257 | const ref_offset = try this_die_obj.getAttrRef(DW.AT_abstract_origin); |
| 1268 | 1258 | if (ref_offset > next_offset) return error.InvalidDebugInfo; |
| 1269 | | try di.dwarf_seekable_stream.seekTo(this_unit_offset + ref_offset); |
| 1270 | | this_die_obj = (try di.parseDie(abbrev_table, is_64)) orelse return error.InvalidDebugInfo; |
| 1259 | try s.seekable_stream.seekTo(this_unit_offset + ref_offset); |
| 1260 | this_die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo; |
| 1271 | 1261 | } else if (this_die_obj.getAttr(DW.AT_specification)) |ref| { |
| 1272 | 1262 | // Follow the DIE it points to and repeat |
| 1273 | 1263 | const ref_offset = try this_die_obj.getAttrRef(DW.AT_specification); |
| 1274 | 1264 | if (ref_offset > next_offset) return error.InvalidDebugInfo; |
| 1275 | | try di.dwarf_seekable_stream.seekTo(this_unit_offset + ref_offset); |
| 1276 | | this_die_obj = (try di.parseDie(abbrev_table, is_64)) orelse return error.InvalidDebugInfo; |
| 1265 | try s.seekable_stream.seekTo(this_unit_offset + ref_offset); |
| 1266 | this_die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo; |
| 1277 | 1267 | } else { |
| 1278 | 1268 | break :x null; |
| 1279 | 1269 | } |
| ... | ... | @@ -1311,12 +1301,10 @@ pub const DwarfInfo = struct { |
| 1311 | 1301 | .pc_range = pc_range, |
| 1312 | 1302 | }); |
| 1313 | 1303 | }, |
| 1314 | | else => { |
| 1315 | | continue; |
| 1316 | | }, |
| 1304 | else => {}, |
| 1317 | 1305 | } |
| 1318 | 1306 | |
| 1319 | | try di.dwarf_seekable_stream.seekTo(after_die_offset); |
| 1307 | try s.seekable_stream.seekTo(after_die_offset); |
| 1320 | 1308 | } |
| 1321 | 1309 | |
| 1322 | 1310 | this_unit_offset += next_offset; |
| ... | ... | @@ -1324,32 +1312,35 @@ pub const DwarfInfo = struct { |
| 1324 | 1312 | } |
| 1325 | 1313 | |
| 1326 | 1314 | fn scanAllCompileUnits(di: *DwarfInfo) !void { |
| 1327 | | const debug_info_end = di.debug_info.offset + di.debug_info.size; |
| 1328 | | var this_unit_offset = di.debug_info.offset; |
| 1315 | var s = io.SliceSeekableInStream.init(di.debug_info); |
| 1316 | var this_unit_offset: u64 = 0; |
| 1329 | 1317 | |
| 1330 | | while (this_unit_offset < debug_info_end) { |
| 1331 | | try di.dwarf_seekable_stream.seekTo(this_unit_offset); |
| 1318 | while (true) { |
| 1319 | s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) { |
| 1320 | error.EndOfStream => return, |
| 1321 | else => return err, |
| 1322 | }; |
| 1332 | 1323 | |
| 1333 | 1324 | var is_64: bool = undefined; |
| 1334 | | const unit_length = try readInitialLength(@TypeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64); |
| 1325 | const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64); |
| 1335 | 1326 | if (unit_length == 0) return; |
| 1336 | 1327 | const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4)); |
| 1337 | 1328 | |
| 1338 | | const version = try di.dwarf_in_stream.readInt(u16, di.endian); |
| 1329 | const version = try s.stream.readInt(u16, di.endian); |
| 1339 | 1330 | if (version < 2 or version > 5) return error.InvalidDebugInfo; |
| 1340 | 1331 | |
| 1341 | | 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); |
| 1332 | const debug_abbrev_offset = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian); |
| 1342 | 1333 | |
| 1343 | | const address_size = try di.dwarf_in_stream.readByte(); |
| 1334 | const address_size = try s.stream.readByte(); |
| 1344 | 1335 | if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; |
| 1345 | 1336 | |
| 1346 | | const compile_unit_pos = try di.dwarf_seekable_stream.getPos(); |
| 1337 | const compile_unit_pos = try s.seekable_stream.getPos(); |
| 1347 | 1338 | const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset); |
| 1348 | 1339 | |
| 1349 | | try di.dwarf_seekable_stream.seekTo(compile_unit_pos); |
| 1340 | try s.seekable_stream.seekTo(compile_unit_pos); |
| 1350 | 1341 | |
| 1351 | 1342 | const compile_unit_die = try di.allocator().create(Die); |
| 1352 | | compile_unit_die.* = (try di.parseDie(abbrev_table, is_64)) orelse return error.InvalidDebugInfo; |
| 1343 | compile_unit_die.* = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo; |
| 1353 | 1344 | |
| 1354 | 1345 | if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo; |
| 1355 | 1346 | |
| ... | ... | @@ -1395,15 +1386,18 @@ pub const DwarfInfo = struct { |
| 1395 | 1386 | } |
| 1396 | 1387 | if (di.debug_ranges) |debug_ranges| { |
| 1397 | 1388 | if (compile_unit.die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| { |
| 1389 | var s = io.SliceSeekableInStream.init(debug_ranges); |
| 1390 | |
| 1398 | 1391 | // All the addresses in the list are relative to the value |
| 1399 | 1392 | // specified by DW_AT_low_pc or to some other value encoded |
| 1400 | 1393 | // in the list itself |
| 1401 | 1394 | var base_address = try compile_unit.die.getAttrAddr(DW.AT_low_pc); |
| 1402 | 1395 | |
| 1403 | | try di.dwarf_seekable_stream.seekTo(debug_ranges.offset + ranges_offset); |
| 1396 | try s.seekable_stream.seekTo(ranges_offset); |
| 1397 | |
| 1404 | 1398 | while (true) { |
| 1405 | | const begin_addr = try di.dwarf_in_stream.readIntLittle(usize); |
| 1406 | | const end_addr = try di.dwarf_in_stream.readIntLittle(usize); |
| 1399 | const begin_addr = try s.stream.readIntLittle(usize); |
| 1400 | const end_addr = try s.stream.readIntLittle(usize); |
| 1407 | 1401 | if (begin_addr == 0 and end_addr == 0) { |
| 1408 | 1402 | break; |
| 1409 | 1403 | } |
| ... | ... | @@ -1435,30 +1429,33 @@ pub const DwarfInfo = struct { |
| 1435 | 1429 | return &header.table; |
| 1436 | 1430 | } |
| 1437 | 1431 | } |
| 1438 | | try di.dwarf_seekable_stream.seekTo(di.debug_abbrev.offset + abbrev_offset); |
| 1439 | 1432 | try di.abbrev_table_list.append(AbbrevTableHeader{ |
| 1440 | 1433 | .offset = abbrev_offset, |
| 1441 | | .table = try di.parseAbbrevTable(), |
| 1434 | .table = try di.parseAbbrevTable(abbrev_offset), |
| 1442 | 1435 | }); |
| 1443 | 1436 | return &di.abbrev_table_list.items[di.abbrev_table_list.len - 1].table; |
| 1444 | 1437 | } |
| 1445 | 1438 | |
| 1446 | | fn parseAbbrevTable(di: *DwarfInfo) !AbbrevTable { |
| 1439 | fn parseAbbrevTable(di: *DwarfInfo, offset: u64) !AbbrevTable { |
| 1440 | var s = io.SliceSeekableInStream.init(di.debug_abbrev); |
| 1441 | |
| 1442 | try s.seekable_stream.seekTo(offset); |
| 1447 | 1443 | var result = AbbrevTable.init(di.allocator()); |
| 1444 | errdefer result.deinit(); |
| 1448 | 1445 | while (true) { |
| 1449 | | const abbrev_code = try leb.readULEB128(u64, di.dwarf_in_stream); |
| 1446 | const abbrev_code = try leb.readULEB128(u64, &s.stream); |
| 1450 | 1447 | if (abbrev_code == 0) return result; |
| 1451 | 1448 | try result.append(AbbrevTableEntry{ |
| 1452 | 1449 | .abbrev_code = abbrev_code, |
| 1453 | | .tag_id = try leb.readULEB128(u64, di.dwarf_in_stream), |
| 1454 | | .has_children = (try di.dwarf_in_stream.readByte()) == DW.CHILDREN_yes, |
| 1450 | .tag_id = try leb.readULEB128(u64, &s.stream), |
| 1451 | .has_children = (try s.stream.readByte()) == DW.CHILDREN_yes, |
| 1455 | 1452 | .attrs = ArrayList(AbbrevAttr).init(di.allocator()), |
| 1456 | 1453 | }); |
| 1457 | 1454 | const attrs = &result.items[result.len - 1].attrs; |
| 1458 | 1455 | |
| 1459 | 1456 | while (true) { |
| 1460 | | const attr_id = try leb.readULEB128(u64, di.dwarf_in_stream); |
| 1461 | | const form_id = try leb.readULEB128(u64, di.dwarf_in_stream); |
| 1457 | const attr_id = try leb.readULEB128(u64, &s.stream); |
| 1458 | const form_id = try leb.readULEB128(u64, &s.stream); |
| 1462 | 1459 | if (attr_id == 0 and form_id == 0) break; |
| 1463 | 1460 | try attrs.append(AbbrevAttr{ |
| 1464 | 1461 | .attr_id = attr_id, |
| ... | ... | @@ -1468,8 +1465,8 @@ pub const DwarfInfo = struct { |
| 1468 | 1465 | } |
| 1469 | 1466 | } |
| 1470 | 1467 | |
| 1471 | | fn parseDie(di: *DwarfInfo, abbrev_table: *const AbbrevTable, is_64: bool) !?Die { |
| 1472 | | const abbrev_code = try leb.readULEB128(u64, di.dwarf_in_stream); |
| 1468 | fn parseDie(di: *DwarfInfo, in_stream: var, abbrev_table: *const AbbrevTable, is_64: bool) !?Die { |
| 1469 | const abbrev_code = try leb.readULEB128(u64, in_stream); |
| 1473 | 1470 | if (abbrev_code == 0) return null; |
| 1474 | 1471 | const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo; |
| 1475 | 1472 | |
| ... | ... | @@ -1482,64 +1479,63 @@ pub const DwarfInfo = struct { |
| 1482 | 1479 | for (table_entry.attrs.toSliceConst()) |attr, i| { |
| 1483 | 1480 | result.attrs.items[i] = Die.Attr{ |
| 1484 | 1481 | .id = attr.attr_id, |
| 1485 | | .value = try parseFormValue(di.allocator(), di.dwarf_in_stream, attr.form_id, is_64), |
| 1482 | .value = try parseFormValue(di.allocator(), in_stream, attr.form_id, is_64), |
| 1486 | 1483 | }; |
| 1487 | 1484 | } |
| 1488 | 1485 | return result; |
| 1489 | 1486 | } |
| 1490 | 1487 | |
| 1491 | 1488 | fn getLineNumberInfo(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !LineInfo { |
| 1489 | var s = io.SliceSeekableInStream.init(di.debug_line); |
| 1490 | |
| 1492 | 1491 | const compile_unit_cwd = try compile_unit.die.getAttrString(di, DW.AT_comp_dir); |
| 1493 | 1492 | const line_info_offset = try compile_unit.die.getAttrSecOffset(DW.AT_stmt_list); |
| 1494 | 1493 | |
| 1495 | | assert(line_info_offset < di.debug_line.size); |
| 1496 | | |
| 1497 | | const this_unit_offset = di.debug_line.offset + line_info_offset; |
| 1498 | | try di.dwarf_seekable_stream.seekTo(this_unit_offset); |
| 1494 | try s.seekable_stream.seekTo(line_info_offset); |
| 1499 | 1495 | |
| 1500 | 1496 | var is_64: bool = undefined; |
| 1501 | | const unit_length = try readInitialLength(@TypeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64); |
| 1497 | const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64); |
| 1502 | 1498 | if (unit_length == 0) { |
| 1503 | 1499 | return error.MissingDebugInfo; |
| 1504 | 1500 | } |
| 1505 | 1501 | const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4)); |
| 1506 | 1502 | |
| 1507 | | const version = try di.dwarf_in_stream.readInt(u16, di.endian); |
| 1503 | const version = try s.stream.readInt(u16, di.endian); |
| 1508 | 1504 | // TODO support 3 and 5 |
| 1509 | 1505 | if (version != 2 and version != 4) return error.InvalidDebugInfo; |
| 1510 | 1506 | |
| 1511 | | 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); |
| 1512 | | const prog_start_offset = (try di.dwarf_seekable_stream.getPos()) + prologue_length; |
| 1507 | const prologue_length = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian); |
| 1508 | const prog_start_offset = (try s.seekable_stream.getPos()) + prologue_length; |
| 1513 | 1509 | |
| 1514 | | const minimum_instruction_length = try di.dwarf_in_stream.readByte(); |
| 1510 | const minimum_instruction_length = try s.stream.readByte(); |
| 1515 | 1511 | if (minimum_instruction_length == 0) return error.InvalidDebugInfo; |
| 1516 | 1512 | |
| 1517 | 1513 | if (version >= 4) { |
| 1518 | 1514 | // maximum_operations_per_instruction |
| 1519 | | _ = try di.dwarf_in_stream.readByte(); |
| 1515 | _ = try s.stream.readByte(); |
| 1520 | 1516 | } |
| 1521 | 1517 | |
| 1522 | | const default_is_stmt = (try di.dwarf_in_stream.readByte()) != 0; |
| 1523 | | const line_base = try di.dwarf_in_stream.readByteSigned(); |
| 1518 | const default_is_stmt = (try s.stream.readByte()) != 0; |
| 1519 | const line_base = try s.stream.readByteSigned(); |
| 1524 | 1520 | |
| 1525 | | const line_range = try di.dwarf_in_stream.readByte(); |
| 1521 | const line_range = try s.stream.readByte(); |
| 1526 | 1522 | if (line_range == 0) return error.InvalidDebugInfo; |
| 1527 | 1523 | |
| 1528 | | const opcode_base = try di.dwarf_in_stream.readByte(); |
| 1524 | const opcode_base = try s.stream.readByte(); |
| 1529 | 1525 | |
| 1530 | 1526 | const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1); |
| 1531 | 1527 | |
| 1532 | 1528 | { |
| 1533 | 1529 | var i: usize = 0; |
| 1534 | 1530 | while (i < opcode_base - 1) : (i += 1) { |
| 1535 | | standard_opcode_lengths[i] = try di.dwarf_in_stream.readByte(); |
| 1531 | standard_opcode_lengths[i] = try s.stream.readByte(); |
| 1536 | 1532 | } |
| 1537 | 1533 | } |
| 1538 | 1534 | |
| 1539 | 1535 | var include_directories = ArrayList([]u8).init(di.allocator()); |
| 1540 | 1536 | try include_directories.append(compile_unit_cwd); |
| 1541 | 1537 | while (true) { |
| 1542 | | const dir = try di.readString(); |
| 1538 | const dir = try readStringRaw(di.allocator(), &s.stream); |
| 1543 | 1539 | if (dir.len == 0) break; |
| 1544 | 1540 | try include_directories.append(dir); |
| 1545 | 1541 | } |
| ... | ... | @@ -1548,11 +1544,11 @@ pub const DwarfInfo = struct { |
| 1548 | 1544 | var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address); |
| 1549 | 1545 | |
| 1550 | 1546 | while (true) { |
| 1551 | | const file_name = try di.readString(); |
| 1547 | const file_name = try readStringRaw(di.allocator(), &s.stream); |
| 1552 | 1548 | if (file_name.len == 0) break; |
| 1553 | | const dir_index = try leb.readULEB128(usize, di.dwarf_in_stream); |
| 1554 | | const mtime = try leb.readULEB128(usize, di.dwarf_in_stream); |
| 1555 | | const len_bytes = try leb.readULEB128(usize, di.dwarf_in_stream); |
| 1549 | const dir_index = try leb.readULEB128(usize, &s.stream); |
| 1550 | const mtime = try leb.readULEB128(usize, &s.stream); |
| 1551 | const len_bytes = try leb.readULEB128(usize, &s.stream); |
| 1556 | 1552 | try file_entries.append(FileEntry{ |
| 1557 | 1553 | .file_name = file_name, |
| 1558 | 1554 | .dir_index = dir_index, |
| ... | ... | @@ -1561,17 +1557,17 @@ pub const DwarfInfo = struct { |
| 1561 | 1557 | }); |
| 1562 | 1558 | } |
| 1563 | 1559 | |
| 1564 | | try di.dwarf_seekable_stream.seekTo(prog_start_offset); |
| 1560 | try s.seekable_stream.seekTo(prog_start_offset); |
| 1565 | 1561 | |
| 1566 | | const next_unit_pos = this_unit_offset + next_offset; |
| 1562 | const next_unit_pos = line_info_offset + next_offset; |
| 1567 | 1563 | |
| 1568 | | while ((try di.dwarf_seekable_stream.getPos()) < next_unit_pos) { |
| 1569 | | const opcode = try di.dwarf_in_stream.readByte(); |
| 1564 | while ((try s.seekable_stream.getPos()) < next_unit_pos) { |
| 1565 | const opcode = try s.stream.readByte(); |
| 1570 | 1566 | |
| 1571 | 1567 | if (opcode == DW.LNS_extended_op) { |
| 1572 | | const op_size = try leb.readULEB128(u64, di.dwarf_in_stream); |
| 1568 | const op_size = try leb.readULEB128(u64, &s.stream); |
| 1573 | 1569 | if (op_size < 1) return error.InvalidDebugInfo; |
| 1574 | | var sub_op = try di.dwarf_in_stream.readByte(); |
| 1570 | var sub_op = try s.stream.readByte(); |
| 1575 | 1571 | switch (sub_op) { |
| 1576 | 1572 | DW.LNE_end_sequence => { |
| 1577 | 1573 | prog.end_sequence = true; |
| ... | ... | @@ -1579,14 +1575,14 @@ pub const DwarfInfo = struct { |
| 1579 | 1575 | prog.reset(); |
| 1580 | 1576 | }, |
| 1581 | 1577 | DW.LNE_set_address => { |
| 1582 | | const addr = try di.dwarf_in_stream.readInt(usize, di.endian); |
| 1578 | const addr = try s.stream.readInt(usize, di.endian); |
| 1583 | 1579 | prog.address = addr; |
| 1584 | 1580 | }, |
| 1585 | 1581 | DW.LNE_define_file => { |
| 1586 | | const file_name = try di.readString(); |
| 1587 | | const dir_index = try leb.readULEB128(usize, di.dwarf_in_stream); |
| 1588 | | const mtime = try leb.readULEB128(usize, di.dwarf_in_stream); |
| 1589 | | const len_bytes = try leb.readULEB128(usize, di.dwarf_in_stream); |
| 1582 | const file_name = try readStringRaw(di.allocator(), &s.stream); |
| 1583 | const dir_index = try leb.readULEB128(usize, &s.stream); |
| 1584 | const mtime = try leb.readULEB128(usize, &s.stream); |
| 1585 | const len_bytes = try leb.readULEB128(usize, &s.stream); |
| 1590 | 1586 | try file_entries.append(FileEntry{ |
| 1591 | 1587 | .file_name = file_name, |
| 1592 | 1588 | .dir_index = dir_index, |
| ... | ... | @@ -1596,7 +1592,7 @@ pub const DwarfInfo = struct { |
| 1596 | 1592 | }, |
| 1597 | 1593 | else => { |
| 1598 | 1594 | const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo; |
| 1599 | | try di.dwarf_seekable_stream.seekBy(fwd_amt); |
| 1595 | try s.seekable_stream.seekBy(fwd_amt); |
| 1600 | 1596 | }, |
| 1601 | 1597 | } |
| 1602 | 1598 | } else if (opcode >= opcode_base) { |
| ... | ... | @@ -1615,19 +1611,19 @@ pub const DwarfInfo = struct { |
| 1615 | 1611 | prog.basic_block = false; |
| 1616 | 1612 | }, |
| 1617 | 1613 | DW.LNS_advance_pc => { |
| 1618 | | const arg = try leb.readULEB128(usize, di.dwarf_in_stream); |
| 1614 | const arg = try leb.readULEB128(usize, &s.stream); |
| 1619 | 1615 | prog.address += arg * minimum_instruction_length; |
| 1620 | 1616 | }, |
| 1621 | 1617 | DW.LNS_advance_line => { |
| 1622 | | const arg = try leb.readILEB128(i64, di.dwarf_in_stream); |
| 1618 | const arg = try leb.readILEB128(i64, &s.stream); |
| 1623 | 1619 | prog.line += arg; |
| 1624 | 1620 | }, |
| 1625 | 1621 | DW.LNS_set_file => { |
| 1626 | | const arg = try leb.readULEB128(usize, di.dwarf_in_stream); |
| 1622 | const arg = try leb.readULEB128(usize, &s.stream); |
| 1627 | 1623 | prog.file = arg; |
| 1628 | 1624 | }, |
| 1629 | 1625 | DW.LNS_set_column => { |
| 1630 | | const arg = try leb.readULEB128(u64, di.dwarf_in_stream); |
| 1626 | const arg = try leb.readULEB128(u64, &s.stream); |
| 1631 | 1627 | prog.column = arg; |
| 1632 | 1628 | }, |
| 1633 | 1629 | DW.LNS_negate_stmt => { |
| ... | ... | @@ -1641,14 +1637,14 @@ pub const DwarfInfo = struct { |
| 1641 | 1637 | prog.address += inc_addr; |
| 1642 | 1638 | }, |
| 1643 | 1639 | DW.LNS_fixed_advance_pc => { |
| 1644 | | const arg = try di.dwarf_in_stream.readInt(u16, di.endian); |
| 1640 | const arg = try s.stream.readInt(u16, di.endian); |
| 1645 | 1641 | prog.address += arg; |
| 1646 | 1642 | }, |
| 1647 | 1643 | DW.LNS_set_prologue_end => {}, |
| 1648 | 1644 | else => { |
| 1649 | 1645 | if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo; |
| 1650 | 1646 | const len_bytes = standard_opcode_lengths[opcode - 1]; |
| 1651 | | try di.dwarf_seekable_stream.seekBy(len_bytes); |
| 1647 | try s.seekable_stream.seekBy(len_bytes); |
| 1652 | 1648 | }, |
| 1653 | 1649 | } |
| 1654 | 1650 | } |
| ... | ... | @@ -1658,9 +1654,17 @@ pub const DwarfInfo = struct { |
| 1658 | 1654 | } |
| 1659 | 1655 | |
| 1660 | 1656 | fn getString(di: *DwarfInfo, offset: u64) ![]u8 { |
| 1661 | | const pos = di.debug_str.offset + offset; |
| 1662 | | try di.dwarf_seekable_stream.seekTo(pos); |
| 1663 | | return di.readString(); |
| 1657 | if (offset > di.debug_str.len) |
| 1658 | return error.InvalidDebugInfo; |
| 1659 | const casted_offset = math.cast(usize, offset) catch |
| 1660 | return error.InvalidDebugInfo; |
| 1661 | |
| 1662 | // Valid strings always have a terminating zero byte |
| 1663 | if (mem.indexOfScalarPos(u8, di.debug_str, casted_offset, 0)) |last| { |
| 1664 | return di.debug_str[casted_offset..last]; |
| 1665 | } |
| 1666 | |
| 1667 | return error.InvalidDebugInfo; |
| 1664 | 1668 | } |
| 1665 | 1669 | }; |
| 1666 | 1670 | |
| ... | ... | @@ -1672,7 +1676,7 @@ pub const DebugInfo = switch (builtin.os) { |
| 1672 | 1676 | |
| 1673 | 1677 | const OFileTable = std.HashMap( |
| 1674 | 1678 | *macho.nlist_64, |
| 1675 | | MachOFile, |
| 1679 | DwarfInfo, |
| 1676 | 1680 | std.hash_map.getHashPtrAddrFn(*macho.nlist_64), |
| 1677 | 1681 | std.hash_map.getTrivialEqlFn(*macho.nlist_64), |
| 1678 | 1682 | ); |
| ... | ... | @@ -2066,24 +2070,32 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con |
| 2066 | 2070 | return null; |
| 2067 | 2071 | } |
| 2068 | 2072 | |
| 2069 | | fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: usize) !LineInfo { |
| 2073 | fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, address: usize) !LineInfo { |
| 2070 | 2074 | const ofile = symbol.ofile orelse return error.MissingDebugInfo; |
| 2071 | 2075 | const gop = try di.ofiles.getOrPut(ofile); |
| 2072 | | const mach_o_file = if (gop.found_existing) &gop.kv.value else blk: { |
| 2076 | const dwarf_info = if (gop.found_existing) &gop.kv.value else blk: { |
| 2073 | 2077 | errdefer _ = di.ofiles.remove(ofile); |
| 2074 | 2078 | const ofile_path = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + ofile.n_strx)); |
| 2075 | 2079 | |
| 2076 | | gop.kv.value = MachOFile{ |
| 2077 | | .bytes = try std.fs.cwd().readFileAllocAligned( |
| 2078 | | di.ofiles.allocator, |
| 2079 | | ofile_path, |
| 2080 | | maxInt(usize), |
| 2081 | | @alignOf(macho.mach_header_64), |
| 2082 | | ), |
| 2083 | | .sect_debug_info = null, |
| 2084 | | .sect_debug_line = null, |
| 2085 | | }; |
| 2086 | | const hdr = @ptrCast(*const macho.mach_header_64, gop.kv.value.bytes.ptr); |
| 2080 | var exe_file = try std.fs.openFileAbsoluteC(ofile_path, .{}); |
| 2081 | errdefer exe_file.close(); |
| 2082 | |
| 2083 | const exe_len = math.cast(usize, try exe_file.getEndPos()) catch |
| 2084 | return error.DebugInfoTooLarge; |
| 2085 | const exe_mmap = try os.mmap( |
| 2086 | null, |
| 2087 | exe_len, |
| 2088 | os.PROT_READ, |
| 2089 | os.MAP_SHARED, |
| 2090 | exe_file.handle, |
| 2091 | 0, |
| 2092 | ); |
| 2093 | errdefer os.munmap(exe_mmap); |
| 2094 | |
| 2095 | const hdr = @ptrCast( |
| 2096 | *const macho.mach_header_64, |
| 2097 | @alignCast(@alignOf(macho.mach_header_64), exe_mmap.ptr), |
| 2098 | ); |
| 2087 | 2099 | if (hdr.magic != std.macho.MH_MAGIC_64) return error.InvalidDebugInfo; |
| 2088 | 2100 | |
| 2089 | 2101 | const hdr_base = @ptrCast([*]const u8, hdr); |
| ... | ... | @@ -2092,181 +2104,75 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u |
| 2092 | 2104 | const segcmd = while (ncmd != 0) : (ncmd -= 1) { |
| 2093 | 2105 | const lc = @ptrCast(*const std.macho.load_command, ptr); |
| 2094 | 2106 | switch (lc.cmd) { |
| 2095 | | std.macho.LC_SEGMENT_64 => break @ptrCast(*const std.macho.segment_command_64, @alignCast(@alignOf(std.macho.segment_command_64), ptr)), |
| 2107 | std.macho.LC_SEGMENT_64 => { |
| 2108 | break @ptrCast( |
| 2109 | *const std.macho.segment_command_64, |
| 2110 | @alignCast(@alignOf(std.macho.segment_command_64), ptr), |
| 2111 | ); |
| 2112 | }, |
| 2096 | 2113 | else => {}, |
| 2097 | 2114 | } |
| 2098 | 2115 | ptr = @alignCast(@alignOf(std.macho.load_command), ptr + lc.cmdsize); |
| 2099 | 2116 | } else { |
| 2100 | 2117 | return error.MissingDebugInfo; |
| 2101 | 2118 | }; |
| 2119 | |
| 2120 | var opt_debug_line: ?*const macho.section_64 = null; |
| 2121 | var opt_debug_info: ?*const macho.section_64 = null; |
| 2122 | var opt_debug_abbrev: ?*const macho.section_64 = null; |
| 2123 | var opt_debug_str: ?*const macho.section_64 = null; |
| 2124 | var opt_debug_ranges: ?*const macho.section_64 = null; |
| 2125 | |
| 2102 | 2126 | const sections = @ptrCast([*]const macho.section_64, @alignCast(@alignOf(macho.section_64), ptr + @sizeOf(std.macho.segment_command_64)))[0..segcmd.nsects]; |
| 2103 | 2127 | for (sections) |*sect| { |
| 2104 | | if (sect.flags & macho.SECTION_TYPE == macho.S_REGULAR and |
| 2105 | | (sect.flags & macho.SECTION_ATTRIBUTES) & macho.S_ATTR_DEBUG == macho.S_ATTR_DEBUG) |
| 2106 | | { |
| 2107 | | const sect_name = mem.toSliceConst(u8, @ptrCast([*:0]const u8, &sect.sectname)); |
| 2108 | | if (mem.eql(u8, sect_name, "__debug_line")) { |
| 2109 | | gop.kv.value.sect_debug_line = sect; |
| 2110 | | } else if (mem.eql(u8, sect_name, "__debug_info")) { |
| 2111 | | gop.kv.value.sect_debug_info = sect; |
| 2112 | | } |
| 2128 | // The section name may not exceed 16 chars and a trailing null may |
| 2129 | // not be present |
| 2130 | const name = if (mem.indexOfScalar(u8, sect.sectname[0..], 0)) |last| |
| 2131 | sect.sectname[0..last] |
| 2132 | else |
| 2133 | sect.sectname[0..]; |
| 2134 | |
| 2135 | if (mem.eql(u8, name, "__debug_line")) { |
| 2136 | opt_debug_line = sect; |
| 2137 | } else if (mem.eql(u8, name, "__debug_info")) { |
| 2138 | opt_debug_info = sect; |
| 2139 | } else if (mem.eql(u8, name, "__debug_abbrev")) { |
| 2140 | opt_debug_abbrev = sect; |
| 2141 | } else if (mem.eql(u8, name, "__debug_str")) { |
| 2142 | opt_debug_str = sect; |
| 2143 | } else if (mem.eql(u8, name, "__debug_ranges")) { |
| 2144 | opt_debug_ranges = sect; |
| 2113 | 2145 | } |
| 2114 | 2146 | } |
| 2115 | 2147 | |
| 2116 | | break :blk &gop.kv.value; |
| 2117 | | }; |
| 2118 | | |
| 2119 | | const sect_debug_line = mach_o_file.sect_debug_line orelse return error.MissingDebugInfo; |
| 2120 | | var ptr = mach_o_file.bytes.ptr + sect_debug_line.offset; |
| 2121 | | |
| 2122 | | var is_64: bool = undefined; |
| 2123 | | const unit_length = try readInitialLengthMem(&ptr, &is_64); |
| 2124 | | if (unit_length == 0) return error.MissingDebugInfo; |
| 2125 | | |
| 2126 | | const version = readIntMem(&ptr, u16, builtin.Endian.Little); |
| 2127 | | // TODO support 3 and 5 |
| 2128 | | if (version != 2 and version != 4) return error.InvalidDebugInfo; |
| 2129 | | |
| 2130 | | const prologue_length = if (is_64) |
| 2131 | | readIntMem(&ptr, u64, builtin.Endian.Little) |
| 2132 | | else |
| 2133 | | readIntMem(&ptr, u32, builtin.Endian.Little); |
| 2134 | | const prog_start = ptr + prologue_length; |
| 2135 | | |
| 2136 | | const minimum_instruction_length = readByteMem(&ptr); |
| 2137 | | if (minimum_instruction_length == 0) return error.InvalidDebugInfo; |
| 2138 | | |
| 2139 | | if (version >= 4) { |
| 2140 | | // maximum_operations_per_instruction |
| 2141 | | ptr += 1; |
| 2142 | | } |
| 2143 | | |
| 2144 | | const default_is_stmt = readByteMem(&ptr) != 0; |
| 2145 | | const line_base = readByteSignedMem(&ptr); |
| 2146 | | |
| 2147 | | const line_range = readByteMem(&ptr); |
| 2148 | | if (line_range == 0) return error.InvalidDebugInfo; |
| 2149 | | |
| 2150 | | const opcode_base = readByteMem(&ptr); |
| 2151 | | |
| 2152 | | const standard_opcode_lengths = ptr[0 .. opcode_base - 1]; |
| 2153 | | ptr += opcode_base - 1; |
| 2154 | | |
| 2155 | | var include_directories = ArrayList([]const u8).init(di.allocator()); |
| 2156 | | try include_directories.append(""); |
| 2157 | | while (true) { |
| 2158 | | const dir = readStringMem(&ptr); |
| 2159 | | if (dir.len == 0) break; |
| 2160 | | try include_directories.append(dir); |
| 2161 | | } |
| 2162 | | |
| 2163 | | var file_entries = ArrayList(FileEntry).init(di.allocator()); |
| 2164 | | var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address); |
| 2148 | var debug_line = opt_debug_line orelse |
| 2149 | return error.MissingDebugInfo; |
| 2150 | var debug_info = opt_debug_info orelse |
| 2151 | return error.MissingDebugInfo; |
| 2152 | var debug_str = opt_debug_str orelse |
| 2153 | return error.MissingDebugInfo; |
| 2154 | var debug_abbrev = opt_debug_abbrev orelse |
| 2155 | return error.MissingDebugInfo; |
| 2165 | 2156 | |
| 2166 | | while (true) { |
| 2167 | | const file_name = readStringMem(&ptr); |
| 2168 | | if (file_name.len == 0) break; |
| 2169 | | const dir_index = try leb.readULEB128Mem(usize, &ptr); |
| 2170 | | const mtime = try leb.readULEB128Mem(usize, &ptr); |
| 2171 | | const len_bytes = try leb.readULEB128Mem(usize, &ptr); |
| 2172 | | try file_entries.append(FileEntry{ |
| 2173 | | .file_name = file_name, |
| 2174 | | .dir_index = dir_index, |
| 2175 | | .mtime = mtime, |
| 2176 | | .len_bytes = len_bytes, |
| 2177 | | }); |
| 2178 | | } |
| 2157 | gop.kv.value = DwarfInfo{ |
| 2158 | .endian = .Little, |
| 2159 | .debug_info = exe_mmap[@intCast(usize, debug_info.offset)..@intCast(usize, debug_info.offset + debug_info.size)], |
| 2160 | .debug_abbrev = exe_mmap[@intCast(usize, debug_abbrev.offset)..@intCast(usize, debug_abbrev.offset + debug_abbrev.size)], |
| 2161 | .debug_str = exe_mmap[@intCast(usize, debug_str.offset)..@intCast(usize, debug_str.offset + debug_str.size)], |
| 2162 | .debug_line = exe_mmap[@intCast(usize, debug_line.offset)..@intCast(usize, debug_line.offset + debug_line.size)], |
| 2163 | .debug_ranges = if (opt_debug_ranges) |debug_ranges| |
| 2164 | exe_mmap[@intCast(usize, debug_ranges.offset)..@intCast(usize, debug_ranges.offset + debug_ranges.size)] |
| 2165 | else |
| 2166 | null, |
| 2167 | }; |
| 2168 | try openDwarfDebugInfo(&gop.kv.value, di.allocator()); |
| 2179 | 2169 | |
| 2180 | | ptr = prog_start; |
| 2181 | | while (true) { |
| 2182 | | const opcode = readByteMem(&ptr); |
| 2183 | | |
| 2184 | | if (opcode == DW.LNS_extended_op) { |
| 2185 | | const op_size = try leb.readULEB128Mem(u64, &ptr); |
| 2186 | | if (op_size < 1) return error.InvalidDebugInfo; |
| 2187 | | var sub_op = readByteMem(&ptr); |
| 2188 | | switch (sub_op) { |
| 2189 | | DW.LNE_end_sequence => { |
| 2190 | | prog.end_sequence = true; |
| 2191 | | if (try prog.checkLineMatch()) |info| return info; |
| 2192 | | return error.MissingDebugInfo; |
| 2193 | | }, |
| 2194 | | DW.LNE_set_address => { |
| 2195 | | const addr = readIntMem(&ptr, usize, builtin.Endian.Little); |
| 2196 | | prog.address = symbol.reloc + addr; |
| 2197 | | }, |
| 2198 | | DW.LNE_define_file => { |
| 2199 | | const file_name = readStringMem(&ptr); |
| 2200 | | const dir_index = try leb.readULEB128Mem(usize, &ptr); |
| 2201 | | const mtime = try leb.readULEB128Mem(usize, &ptr); |
| 2202 | | const len_bytes = try leb.readULEB128Mem(usize, &ptr); |
| 2203 | | try file_entries.append(FileEntry{ |
| 2204 | | .file_name = file_name, |
| 2205 | | .dir_index = dir_index, |
| 2206 | | .mtime = mtime, |
| 2207 | | .len_bytes = len_bytes, |
| 2208 | | }); |
| 2209 | | }, |
| 2210 | | else => { |
| 2211 | | ptr += op_size - 1; |
| 2212 | | }, |
| 2213 | | } |
| 2214 | | } else if (opcode >= opcode_base) { |
| 2215 | | // special opcodes |
| 2216 | | const adjusted_opcode = opcode - opcode_base; |
| 2217 | | const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range); |
| 2218 | | const inc_line = @as(i32, line_base) + @as(i32, adjusted_opcode % line_range); |
| 2219 | | prog.line += inc_line; |
| 2220 | | prog.address += inc_addr; |
| 2221 | | if (try prog.checkLineMatch()) |info| return info; |
| 2222 | | prog.basic_block = false; |
| 2223 | | } else { |
| 2224 | | switch (opcode) { |
| 2225 | | DW.LNS_copy => { |
| 2226 | | if (try prog.checkLineMatch()) |info| return info; |
| 2227 | | prog.basic_block = false; |
| 2228 | | }, |
| 2229 | | DW.LNS_advance_pc => { |
| 2230 | | const arg = try leb.readULEB128Mem(usize, &ptr); |
| 2231 | | prog.address += arg * minimum_instruction_length; |
| 2232 | | }, |
| 2233 | | DW.LNS_advance_line => { |
| 2234 | | const arg = try leb.readILEB128Mem(i64, &ptr); |
| 2235 | | prog.line += arg; |
| 2236 | | }, |
| 2237 | | DW.LNS_set_file => { |
| 2238 | | const arg = try leb.readULEB128Mem(usize, &ptr); |
| 2239 | | prog.file = arg; |
| 2240 | | }, |
| 2241 | | DW.LNS_set_column => { |
| 2242 | | const arg = try leb.readULEB128Mem(u64, &ptr); |
| 2243 | | prog.column = arg; |
| 2244 | | }, |
| 2245 | | DW.LNS_negate_stmt => { |
| 2246 | | prog.is_stmt = !prog.is_stmt; |
| 2247 | | }, |
| 2248 | | DW.LNS_set_basic_block => { |
| 2249 | | prog.basic_block = true; |
| 2250 | | }, |
| 2251 | | DW.LNS_const_add_pc => { |
| 2252 | | const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range); |
| 2253 | | prog.address += inc_addr; |
| 2254 | | }, |
| 2255 | | DW.LNS_fixed_advance_pc => { |
| 2256 | | const arg = readIntMem(&ptr, u16, builtin.Endian.Little); |
| 2257 | | prog.address += arg; |
| 2258 | | }, |
| 2259 | | DW.LNS_set_prologue_end => {}, |
| 2260 | | else => { |
| 2261 | | if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo; |
| 2262 | | const len_bytes = standard_opcode_lengths[opcode - 1]; |
| 2263 | | ptr += len_bytes; |
| 2264 | | }, |
| 2265 | | } |
| 2266 | | } |
| 2267 | | } |
| 2170 | break :blk &gop.kv.value; |
| 2171 | }; |
| 2268 | 2172 | |
| 2269 | | return error.MissingDebugInfo; |
| 2173 | const o_file_address = address - symbol.reloc; |
| 2174 | const compile_unit = try dwarf_info.findCompileUnit(o_file_address); |
| 2175 | return dwarf_info.getLineNumberInfo(compile_unit.*, o_file_address); |
| 2270 | 2176 | } |
| 2271 | 2177 | |
| 2272 | 2178 | const Func = struct { |
| ... | ... | @@ -2274,47 +2180,6 @@ const Func = struct { |
| 2274 | 2180 | name: ?[]u8, |
| 2275 | 2181 | }; |
| 2276 | 2182 | |
| 2277 | | fn readIntMem(ptr: *[*]const u8, comptime T: type, endian: builtin.Endian) T { |
| 2278 | | // TODO https://github.com/ziglang/zig/issues/863 |
| 2279 | | const size = (T.bit_count + 7) / 8; |
| 2280 | | const result = mem.readIntSlice(T, ptr.*[0..size], endian); |
| 2281 | | ptr.* += size; |
| 2282 | | return result; |
| 2283 | | } |
| 2284 | | |
| 2285 | | fn readByteMem(ptr: *[*]const u8) u8 { |
| 2286 | | const result = ptr.*[0]; |
| 2287 | | ptr.* += 1; |
| 2288 | | return result; |
| 2289 | | } |
| 2290 | | |
| 2291 | | fn readByteSignedMem(ptr: *[*]const u8) i8 { |
| 2292 | | return @bitCast(i8, readByteMem(ptr)); |
| 2293 | | } |
| 2294 | | |
| 2295 | | fn readInitialLengthMem(ptr: *[*]const u8, is_64: *bool) !u64 { |
| 2296 | | // TODO this code can be improved with https://github.com/ziglang/zig/issues/863 |
| 2297 | | const first_32_bits = mem.readIntSliceLittle(u32, ptr.*[0..4]); |
| 2298 | | is_64.* = (first_32_bits == 0xffffffff); |
| 2299 | | if (is_64.*) { |
| 2300 | | ptr.* += 4; |
| 2301 | | const result = mem.readIntSliceLittle(u64, ptr.*[0..8]); |
| 2302 | | ptr.* += 8; |
| 2303 | | return result; |
| 2304 | | } else { |
| 2305 | | if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo; |
| 2306 | | ptr.* += 4; |
| 2307 | | // TODO this cast should not be needed |
| 2308 | | return @as(u64, first_32_bits); |
| 2309 | | } |
| 2310 | | } |
| 2311 | | |
| 2312 | | fn readStringMem(ptr: *[*]const u8) [:0]const u8 { |
| 2313 | | const result = mem.toSliceConst(u8, @ptrCast([*:0]const u8, ptr.*)); |
| 2314 | | ptr.* += result.len + 1; |
| 2315 | | return result; |
| 2316 | | } |
| 2317 | | |
| 2318 | 2183 | fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 { |
| 2319 | 2184 | const first_32_bits = try in_stream.readIntLittle(u32); |
| 2320 | 2185 | is_64.* = (first_32_bits == 0xffffffff); |