authorgravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2020-01-23 18:07:05+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-01-27 09:46:20-05:00
logd5c2a20d8e2a8c694127fffbe0d1a19f5eaaf92f
treee9d4dd167d2badcf2a508abd32ec2de3b085fe0c
parent518dbd30cb985cd9a5d89a8201becf1bd398330e

Unify the two DWARF interpreters

* Let's consolidate the special-cased DWARF interpreter for OSX with the general purpose one * Drop the assumption that all the debug data is contained in a single contiguous slice of memory. This is a good news for freestanding targets and paves the way for supporting compressed debug sections.

2 files changed, 232 insertions(+), 364 deletions(-)

lib/std/c/darwin.zig+3
...@@ -7,7 +7,10 @@ usingnamespace @import("../os/bits.zig");...@@ -7,7 +7,10 @@ usingnamespace @import("../os/bits.zig");
77
8extern "c" fn __error() *c_int;8extern "c" fn __error() *c_int;
9pub extern "c" fn _NSGetExecutablePath(buf: [*]u8, bufsize: *u32) c_int;9pub extern "c" fn _NSGetExecutablePath(buf: [*]u8, bufsize: *u32) c_int;
10pub extern "c" fn _dyld_image_count() u32;
10pub extern "c" fn _dyld_get_image_header(image_index: u32) ?*mach_header;11pub extern "c" fn _dyld_get_image_header(image_index: u32) ?*mach_header;
12pub extern "c" fn _dyld_get_image_vmaddr_slide(image_index: u32) usize;
13pub extern "c" fn _dyld_get_image_name(image_index: u32) [*:0]const u8;
1114
12pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: [*]u8, buf_len: usize, basep: *i64) isize;15pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: [*]u8, buf_len: usize, basep: *i64) isize;
1316
lib/std/debug.zig+229-364
...@@ -963,59 +963,61 @@ pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: *mem.Allocator) !void {...@@ -963,59 +963,61 @@ pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: *mem.Allocator) !void {
963963
964pub fn openElfDebugInfo(964pub fn openElfDebugInfo(
965 allocator: *mem.Allocator,965 allocator: *mem.Allocator,
966 elf_seekable_stream: *DwarfSeekableStream,966 data: []u8,
967 elf_in_stream: *DwarfInStream,
968) !DwarfInfo {967) !DwarfInfo {
969 var efile = try elf.Elf.openStream(allocator, elf_seekable_stream, elf_in_stream);968 var seekable_stream = io.SliceSeekableInStream.init(data);
970 errdefer efile.close();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");
971985
972 var di = DwarfInfo{986 var di = DwarfInfo{
973 .dwarf_seekable_stream = elf_seekable_stream,
974 .dwarf_in_stream = elf_in_stream,
975 .endian = efile.endian,987 .endian = efile.endian,
976 .debug_info = (try findDwarfSectionFromElf(&efile, ".debug_info")) orelse return error.MissingDebugInfo,988 .debug_info = (data[@intCast(usize, debug_info.offset)..@intCast(usize, debug_info.offset + debug_info.size)]),
977 .debug_abbrev = (try findDwarfSectionFromElf(&efile, ".debug_abbrev")) orelse return error.MissingDebugInfo,989 .debug_abbrev = (data[@intCast(usize, debug_abbrev.offset)..@intCast(usize, debug_abbrev.offset + debug_abbrev.size)]),
978 .debug_str = (try findDwarfSectionFromElf(&efile, ".debug_str")) orelse return error.MissingDebugInfo,990 .debug_str = (data[@intCast(usize, debug_str.offset)..@intCast(usize, debug_str.offset + debug_str.size)]),
979 .debug_line = (try findDwarfSectionFromElf(&efile, ".debug_line")) orelse return error.MissingDebugInfo,991 .debug_line = (data[@intCast(usize, debug_line.offset)..@intCast(usize, debug_line.offset + debug_line.size)]),
980 .debug_ranges = (try findDwarfSectionFromElf(&efile, ".debug_ranges")),992 .debug_ranges = if (opt_debug_ranges) |debug_ranges|
981 .abbrev_table_list = undefined,993 data[@intCast(usize, debug_ranges.offset)..@intCast(usize, debug_ranges.offset + debug_ranges.size)]
982 .compile_unit_list = undefined,994 else
983 .func_list = undefined,995 null,
984 };996 };
997
998 efile.close();
999
985 try openDwarfDebugInfo(&di, allocator);1000 try openDwarfDebugInfo(&di, allocator);
986 return di;1001 return di;
987}1002}
9881003
989fn openSelfDebugInfoPosix(allocator: *mem.Allocator) !DwarfInfo {1004fn openSelfDebugInfoPosix(allocator: *mem.Allocator) !DwarfInfo {
990 const S = struct {1005 var exe_file = try fs.openSelfExe();
991 var self_exe_file: File = undefined;1006 errdefer exe_file.close();
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();
9971007
998 const self_exe_len = math.cast(usize, try S.self_exe_file.getEndPos()) catch return error.DebugInfoTooLarge;1008 const exe_len = math.cast(usize, try exe_file.getEndPos()) catch
999 const self_exe_mmap_len = mem.alignForward(self_exe_len, mem.page_size);1009 return error.DebugInfoTooLarge;
1000 const self_exe_mmap = try os.mmap(1010 const exe_mmap = try os.mmap(
1001 null,1011 null,
1002 self_exe_mmap_len,1012 exe_len,
1003 os.PROT_READ,1013 os.PROT_READ,
1004 os.MAP_SHARED,1014 os.MAP_SHARED,
1005 S.self_exe_file.handle,1015 exe_file.handle,
1006 0,1016 0,
1007 );1017 );
1008 errdefer os.munmap(self_exe_mmap);1018 errdefer os.munmap(exe_mmap);
10091019
1010 S.self_exe_mmap_seekable = io.SliceSeekableInStream.init(self_exe_mmap);1020 return openElfDebugInfo(allocator, 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 );
1019}1021}
10201022
1021fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo {1023fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo {
...@@ -1142,41 +1144,26 @@ const MachoSymbol = struct {...@@ -1142,41 +1144,26 @@ const MachoSymbol = struct {
1142 }1144 }
1143};1145};
11441146
1145const 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
1151pub const DwarfSeekableStream = io.SeekableStream(anyerror, anyerror);1147pub const DwarfSeekableStream = io.SeekableStream(anyerror, anyerror);
1152pub const DwarfInStream = io.InStream(anyerror);1148pub const DwarfInStream = io.InStream(anyerror);
11531149
1154pub const DwarfInfo = struct {1150pub const DwarfInfo = struct {
1155 dwarf_seekable_stream: *DwarfSeekableStream,
1156 dwarf_in_stream: *DwarfInStream,
1157 endian: builtin.Endian,1151 endian: builtin.Endian,
1158 debug_info: Section,1152 // No memory is owned by the DwarfInfo
1159 debug_abbrev: Section,1153 debug_info: []u8,
1160 debug_str: Section,1154 debug_abbrev: []u8,
1161 debug_line: Section,1155 debug_str: []u8,
1162 debug_ranges: ?Section,1156 debug_line: []u8,
1163 abbrev_table_list: ArrayList(AbbrevTableHeader),1157 debug_ranges: ?[]u8,
1164 compile_unit_list: ArrayList(CompileUnit),1158 // Filled later by the initializer
1165 func_list: ArrayList(Func),1159 abbrev_table_list: ArrayList(AbbrevTableHeader) = undefined,
11661160 compile_unit_list: ArrayList(CompileUnit) = undefined,
1167 pub const Section = struct {1161 func_list: ArrayList(Func) = undefined,
1168 offset: u64,
1169 size: u64,
1170 };
11711162
1172 pub fn allocator(self: DwarfInfo) *mem.Allocator {1163 pub fn allocator(self: DwarfInfo) *mem.Allocator {
1173 return self.abbrev_table_list.allocator;1164 return self.abbrev_table_list.allocator;
1174 }1165 }
11751166
1176 pub fn readString(self: *DwarfInfo) ![]u8 {
1177 return readStringRaw(self.allocator(), self.dwarf_in_stream);
1178 }
1179
1180 /// This function works in freestanding mode.1167 /// This function works in freestanding mode.
1181 /// fn printLineFromFile(out_stream: var, line_info: LineInfo) !void1168 /// fn printLineFromFile(out_stream: var, line_info: LineInfo) !void
1182 pub fn printSourceAtAddress(1169 pub fn printSourceAtAddress(
...@@ -1222,35 +1209,38 @@ pub const DwarfInfo = struct {...@@ -1222,35 +1209,38 @@ pub const DwarfInfo = struct {
1222 }1209 }
12231210
1224 fn scanAllFunctions(di: *DwarfInfo) !void {1211 fn scanAllFunctions(di: *DwarfInfo) !void {
1225 const debug_info_end = di.debug_info.offset + di.debug_info.size;1212 var s = io.SliceSeekableInStream.init(di.debug_info);
1226 var this_unit_offset = di.debug_info.offset;1213 var this_unit_offset: u64 = 0;
12271214
1228 while (this_unit_offset < debug_info_end) {1215 while (true) {
1229 try di.dwarf_seekable_stream.seekTo(this_unit_offset);1216 s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) {
1217 error.EndOfStream => return,
1218 else => return err,
1219 };
12301220
1231 var is_64: bool = undefined;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 if (unit_length == 0) return;1223 if (unit_length == 0) return;
1234 const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));1224 const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
12351225
1236 const version = try di.dwarf_in_stream.readInt(u16, di.endian);1226 const version = try s.stream.readInt(u16, di.endian);
1237 if (version < 2 or version > 5) return error.InvalidDebugInfo;1227 if (version < 2 or version > 5) return error.InvalidDebugInfo;
12381228
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);
12401230
1241 const address_size = try di.dwarf_in_stream.readByte();1231 const address_size = try s.stream.readByte();
1242 if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;1232 if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
12431233
1244 const compile_unit_pos = try di.dwarf_seekable_stream.getPos();1234 const compile_unit_pos = try s.seekable_stream.getPos();
1245 const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset);1235 const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset);
12461236
1247 try di.dwarf_seekable_stream.seekTo(compile_unit_pos);1237 try s.seekable_stream.seekTo(compile_unit_pos);
12481238
1249 const next_unit_pos = this_unit_offset + next_offset;1239 const next_unit_pos = this_unit_offset + next_offset;
12501240
1251 while ((try di.dwarf_seekable_stream.getPos()) < next_unit_pos) {1241 while ((try s.seekable_stream.getPos()) < next_unit_pos) {
1252 const die_obj = (try di.parseDie(abbrev_table, is_64)) orelse continue;1242 const die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse continue;
1253 const after_die_offset = try di.dwarf_seekable_stream.getPos();1243 const after_die_offset = try s.seekable_stream.getPos();
12541244
1255 switch (die_obj.tag_id) {1245 switch (die_obj.tag_id) {
1256 DW.TAG_subprogram, DW.TAG_inlined_subroutine, DW.TAG_subroutine, DW.TAG_entry_point => {1246 DW.TAG_subprogram, DW.TAG_inlined_subroutine, DW.TAG_subroutine, DW.TAG_entry_point => {
...@@ -1266,14 +1256,14 @@ pub const DwarfInfo = struct {...@@ -1266,14 +1256,14 @@ pub const DwarfInfo = struct {
1266 // Follow the DIE it points to and repeat1256 // Follow the DIE it points to and repeat
1267 const ref_offset = try this_die_obj.getAttrRef(DW.AT_abstract_origin);1257 const ref_offset = try this_die_obj.getAttrRef(DW.AT_abstract_origin);
1268 if (ref_offset > next_offset) return error.InvalidDebugInfo;1258 if (ref_offset > next_offset) return error.InvalidDebugInfo;
1269 try di.dwarf_seekable_stream.seekTo(this_unit_offset + ref_offset);1259 try s.seekable_stream.seekTo(this_unit_offset + ref_offset);
1270 this_die_obj = (try di.parseDie(abbrev_table, is_64)) orelse return error.InvalidDebugInfo;1260 this_die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
1271 } else if (this_die_obj.getAttr(DW.AT_specification)) |ref| {1261 } else if (this_die_obj.getAttr(DW.AT_specification)) |ref| {
1272 // Follow the DIE it points to and repeat1262 // Follow the DIE it points to and repeat
1273 const ref_offset = try this_die_obj.getAttrRef(DW.AT_specification);1263 const ref_offset = try this_die_obj.getAttrRef(DW.AT_specification);
1274 if (ref_offset > next_offset) return error.InvalidDebugInfo;1264 if (ref_offset > next_offset) return error.InvalidDebugInfo;
1275 try di.dwarf_seekable_stream.seekTo(this_unit_offset + ref_offset);1265 try s.seekable_stream.seekTo(this_unit_offset + ref_offset);
1276 this_die_obj = (try di.parseDie(abbrev_table, is_64)) orelse return error.InvalidDebugInfo;1266 this_die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
1277 } else {1267 } else {
1278 break :x null;1268 break :x null;
1279 }1269 }
...@@ -1311,12 +1301,10 @@ pub const DwarfInfo = struct {...@@ -1311,12 +1301,10 @@ pub const DwarfInfo = struct {
1311 .pc_range = pc_range,1301 .pc_range = pc_range,
1312 });1302 });
1313 },1303 },
1314 else => {1304 else => {},
1315 continue;
1316 },
1317 }1305 }
13181306
1319 try di.dwarf_seekable_stream.seekTo(after_die_offset);1307 try s.seekable_stream.seekTo(after_die_offset);
1320 }1308 }
13211309
1322 this_unit_offset += next_offset;1310 this_unit_offset += next_offset;
...@@ -1324,32 +1312,35 @@ pub const DwarfInfo = struct {...@@ -1324,32 +1312,35 @@ pub const DwarfInfo = struct {
1324 }1312 }
13251313
1326 fn scanAllCompileUnits(di: *DwarfInfo) !void {1314 fn scanAllCompileUnits(di: *DwarfInfo) !void {
1327 const debug_info_end = di.debug_info.offset + di.debug_info.size;1315 var s = io.SliceSeekableInStream.init(di.debug_info);
1328 var this_unit_offset = di.debug_info.offset;1316 var this_unit_offset: u64 = 0;
13291317
1330 while (this_unit_offset < debug_info_end) {1318 while (true) {
1331 try di.dwarf_seekable_stream.seekTo(this_unit_offset);1319 s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) {
1320 error.EndOfStream => return,
1321 else => return err,
1322 };
13321323
1333 var is_64: bool = undefined;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 if (unit_length == 0) return;1326 if (unit_length == 0) return;
1336 const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));1327 const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
13371328
1338 const version = try di.dwarf_in_stream.readInt(u16, di.endian);1329 const version = try s.stream.readInt(u16, di.endian);
1339 if (version < 2 or version > 5) return error.InvalidDebugInfo;1330 if (version < 2 or version > 5) return error.InvalidDebugInfo;
13401331
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);
13421333
1343 const address_size = try di.dwarf_in_stream.readByte();1334 const address_size = try s.stream.readByte();
1344 if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;1335 if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
13451336
1346 const compile_unit_pos = try di.dwarf_seekable_stream.getPos();1337 const compile_unit_pos = try s.seekable_stream.getPos();
1347 const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset);1338 const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset);
13481339
1349 try di.dwarf_seekable_stream.seekTo(compile_unit_pos);1340 try s.seekable_stream.seekTo(compile_unit_pos);
13501341
1351 const compile_unit_die = try di.allocator().create(Die);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;
13531344
1354 if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo;1345 if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo;
13551346
...@@ -1395,15 +1386,18 @@ pub const DwarfInfo = struct {...@@ -1395,15 +1386,18 @@ pub const DwarfInfo = struct {
1395 }1386 }
1396 if (di.debug_ranges) |debug_ranges| {1387 if (di.debug_ranges) |debug_ranges| {
1397 if (compile_unit.die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| {1388 if (compile_unit.die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| {
1389 var s = io.SliceSeekableInStream.init(debug_ranges);
1390
1398 // All the addresses in the list are relative to the value1391 // All the addresses in the list are relative to the value
1399 // specified by DW_AT_low_pc or to some other value encoded1392 // specified by DW_AT_low_pc or to some other value encoded
1400 // in the list itself1393 // in the list itself
1401 var base_address = try compile_unit.die.getAttrAddr(DW.AT_low_pc);1394 var base_address = try compile_unit.die.getAttrAddr(DW.AT_low_pc);
14021395
1403 try di.dwarf_seekable_stream.seekTo(debug_ranges.offset + ranges_offset);1396 try s.seekable_stream.seekTo(ranges_offset);
1397
1404 while (true) {1398 while (true) {
1405 const begin_addr = try di.dwarf_in_stream.readIntLittle(usize);1399 const begin_addr = try s.stream.readIntLittle(usize);
1406 const end_addr = try di.dwarf_in_stream.readIntLittle(usize);1400 const end_addr = try s.stream.readIntLittle(usize);
1407 if (begin_addr == 0 and end_addr == 0) {1401 if (begin_addr == 0 and end_addr == 0) {
1408 break;1402 break;
1409 }1403 }
...@@ -1435,30 +1429,33 @@ pub const DwarfInfo = struct {...@@ -1435,30 +1429,33 @@ pub const DwarfInfo = struct {
1435 return &header.table;1429 return &header.table;
1436 }1430 }
1437 }1431 }
1438 try di.dwarf_seekable_stream.seekTo(di.debug_abbrev.offset + abbrev_offset);
1439 try di.abbrev_table_list.append(AbbrevTableHeader{1432 try di.abbrev_table_list.append(AbbrevTableHeader{
1440 .offset = abbrev_offset,1433 .offset = abbrev_offset,
1441 .table = try di.parseAbbrevTable(),1434 .table = try di.parseAbbrevTable(abbrev_offset),
1442 });1435 });
1443 return &di.abbrev_table_list.items[di.abbrev_table_list.len - 1].table;1436 return &di.abbrev_table_list.items[di.abbrev_table_list.len - 1].table;
1444 }1437 }
14451438
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 var result = AbbrevTable.init(di.allocator());1443 var result = AbbrevTable.init(di.allocator());
1444 errdefer result.deinit();
1448 while (true) {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 if (abbrev_code == 0) return result;1447 if (abbrev_code == 0) return result;
1451 try result.append(AbbrevTableEntry{1448 try result.append(AbbrevTableEntry{
1452 .abbrev_code = abbrev_code,1449 .abbrev_code = abbrev_code,
1453 .tag_id = try leb.readULEB128(u64, di.dwarf_in_stream),1450 .tag_id = try leb.readULEB128(u64, &s.stream),
1454 .has_children = (try di.dwarf_in_stream.readByte()) == DW.CHILDREN_yes,1451 .has_children = (try s.stream.readByte()) == DW.CHILDREN_yes,
1455 .attrs = ArrayList(AbbrevAttr).init(di.allocator()),1452 .attrs = ArrayList(AbbrevAttr).init(di.allocator()),
1456 });1453 });
1457 const attrs = &result.items[result.len - 1].attrs;1454 const attrs = &result.items[result.len - 1].attrs;
14581455
1459 while (true) {1456 while (true) {
1460 const attr_id = try leb.readULEB128(u64, di.dwarf_in_stream);1457 const attr_id = try leb.readULEB128(u64, &s.stream);
1461 const form_id = try leb.readULEB128(u64, di.dwarf_in_stream);1458 const form_id = try leb.readULEB128(u64, &s.stream);
1462 if (attr_id == 0 and form_id == 0) break;1459 if (attr_id == 0 and form_id == 0) break;
1463 try attrs.append(AbbrevAttr{1460 try attrs.append(AbbrevAttr{
1464 .attr_id = attr_id,1461 .attr_id = attr_id,
...@@ -1468,8 +1465,8 @@ pub const DwarfInfo = struct {...@@ -1468,8 +1465,8 @@ pub const DwarfInfo = struct {
1468 }1465 }
1469 }1466 }
14701467
1471 fn parseDie(di: *DwarfInfo, abbrev_table: *const AbbrevTable, is_64: bool) !?Die {1468 fn parseDie(di: *DwarfInfo, in_stream: var, abbrev_table: *const AbbrevTable, is_64: bool) !?Die {
1472 const abbrev_code = try leb.readULEB128(u64, di.dwarf_in_stream);1469 const abbrev_code = try leb.readULEB128(u64, in_stream);
1473 if (abbrev_code == 0) return null;1470 if (abbrev_code == 0) return null;
1474 const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo;1471 const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo;
14751472
...@@ -1482,64 +1479,63 @@ pub const DwarfInfo = struct {...@@ -1482,64 +1479,63 @@ pub const DwarfInfo = struct {
1482 for (table_entry.attrs.toSliceConst()) |attr, i| {1479 for (table_entry.attrs.toSliceConst()) |attr, i| {
1483 result.attrs.items[i] = Die.Attr{1480 result.attrs.items[i] = Die.Attr{
1484 .id = attr.attr_id,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 return result;1485 return result;
1489 }1486 }
14901487
1491 fn getLineNumberInfo(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !LineInfo {1488 fn getLineNumberInfo(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !LineInfo {
1489 var s = io.SliceSeekableInStream.init(di.debug_line);
1490
1492 const compile_unit_cwd = try compile_unit.die.getAttrString(di, DW.AT_comp_dir);1491 const compile_unit_cwd = try compile_unit.die.getAttrString(di, DW.AT_comp_dir);
1493 const line_info_offset = try compile_unit.die.getAttrSecOffset(DW.AT_stmt_list);1492 const line_info_offset = try compile_unit.die.getAttrSecOffset(DW.AT_stmt_list);
14941493
1495 assert(line_info_offset < di.debug_line.size);1494 try s.seekable_stream.seekTo(line_info_offset);
1496
1497 const this_unit_offset = di.debug_line.offset + line_info_offset;
1498 try di.dwarf_seekable_stream.seekTo(this_unit_offset);
14991495
1500 var is_64: bool = undefined;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 if (unit_length == 0) {1498 if (unit_length == 0) {
1503 return error.MissingDebugInfo;1499 return error.MissingDebugInfo;
1504 }1500 }
1505 const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));1501 const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
15061502
1507 const version = try di.dwarf_in_stream.readInt(u16, di.endian);1503 const version = try s.stream.readInt(u16, di.endian);
1508 // TODO support 3 and 51504 // TODO support 3 and 5
1509 if (version != 2 and version != 4) return error.InvalidDebugInfo;1505 if (version != 2 and version != 4) return error.InvalidDebugInfo;
15101506
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);1507 const prologue_length = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian);
1512 const prog_start_offset = (try di.dwarf_seekable_stream.getPos()) + prologue_length;1508 const prog_start_offset = (try s.seekable_stream.getPos()) + prologue_length;
15131509
1514 const minimum_instruction_length = try di.dwarf_in_stream.readByte();1510 const minimum_instruction_length = try s.stream.readByte();
1515 if (minimum_instruction_length == 0) return error.InvalidDebugInfo;1511 if (minimum_instruction_length == 0) return error.InvalidDebugInfo;
15161512
1517 if (version >= 4) {1513 if (version >= 4) {
1518 // maximum_operations_per_instruction1514 // maximum_operations_per_instruction
1519 _ = try di.dwarf_in_stream.readByte();1515 _ = try s.stream.readByte();
1520 }1516 }
15211517
1522 const default_is_stmt = (try di.dwarf_in_stream.readByte()) != 0;1518 const default_is_stmt = (try s.stream.readByte()) != 0;
1523 const line_base = try di.dwarf_in_stream.readByteSigned();1519 const line_base = try s.stream.readByteSigned();
15241520
1525 const line_range = try di.dwarf_in_stream.readByte();1521 const line_range = try s.stream.readByte();
1526 if (line_range == 0) return error.InvalidDebugInfo;1522 if (line_range == 0) return error.InvalidDebugInfo;
15271523
1528 const opcode_base = try di.dwarf_in_stream.readByte();1524 const opcode_base = try s.stream.readByte();
15291525
1530 const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1);1526 const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1);
15311527
1532 {1528 {
1533 var i: usize = 0;1529 var i: usize = 0;
1534 while (i < opcode_base - 1) : (i += 1) {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 }
15381534
1539 var include_directories = ArrayList([]u8).init(di.allocator());1535 var include_directories = ArrayList([]u8).init(di.allocator());
1540 try include_directories.append(compile_unit_cwd);1536 try include_directories.append(compile_unit_cwd);
1541 while (true) {1537 while (true) {
1542 const dir = try di.readString();1538 const dir = try readStringRaw(di.allocator(), &s.stream);
1543 if (dir.len == 0) break;1539 if (dir.len == 0) break;
1544 try include_directories.append(dir);1540 try include_directories.append(dir);
1545 }1541 }
...@@ -1548,11 +1544,11 @@ pub const DwarfInfo = struct {...@@ -1548,11 +1544,11 @@ pub const DwarfInfo = struct {
1548 var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address);1544 var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address);
15491545
1550 while (true) {1546 while (true) {
1551 const file_name = try di.readString();1547 const file_name = try readStringRaw(di.allocator(), &s.stream);
1552 if (file_name.len == 0) break;1548 if (file_name.len == 0) break;
1553 const dir_index = try leb.readULEB128(usize, di.dwarf_in_stream);1549 const dir_index = try leb.readULEB128(usize, &s.stream);
1554 const mtime = try leb.readULEB128(usize, di.dwarf_in_stream);1550 const mtime = try leb.readULEB128(usize, &s.stream);
1555 const len_bytes = try leb.readULEB128(usize, di.dwarf_in_stream);1551 const len_bytes = try leb.readULEB128(usize, &s.stream);
1556 try file_entries.append(FileEntry{1552 try file_entries.append(FileEntry{
1557 .file_name = file_name,1553 .file_name = file_name,
1558 .dir_index = dir_index,1554 .dir_index = dir_index,
...@@ -1561,17 +1557,17 @@ pub const DwarfInfo = struct {...@@ -1561,17 +1557,17 @@ pub const DwarfInfo = struct {
1561 });1557 });
1562 }1558 }
15631559
1564 try di.dwarf_seekable_stream.seekTo(prog_start_offset);1560 try s.seekable_stream.seekTo(prog_start_offset);
15651561
1566 const next_unit_pos = this_unit_offset + next_offset;1562 const next_unit_pos = line_info_offset + next_offset;
15671563
1568 while ((try di.dwarf_seekable_stream.getPos()) < next_unit_pos) {1564 while ((try s.seekable_stream.getPos()) < next_unit_pos) {
1569 const opcode = try di.dwarf_in_stream.readByte();1565 const opcode = try s.stream.readByte();
15701566
1571 if (opcode == DW.LNS_extended_op) {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 if (op_size < 1) return error.InvalidDebugInfo;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 switch (sub_op) {1571 switch (sub_op) {
1576 DW.LNE_end_sequence => {1572 DW.LNE_end_sequence => {
1577 prog.end_sequence = true;1573 prog.end_sequence = true;
...@@ -1579,14 +1575,14 @@ pub const DwarfInfo = struct {...@@ -1579,14 +1575,14 @@ pub const DwarfInfo = struct {
1579 prog.reset();1575 prog.reset();
1580 },1576 },
1581 DW.LNE_set_address => {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 prog.address = addr;1579 prog.address = addr;
1584 },1580 },
1585 DW.LNE_define_file => {1581 DW.LNE_define_file => {
1586 const file_name = try di.readString();1582 const file_name = try readStringRaw(di.allocator(), &s.stream);
1587 const dir_index = try leb.readULEB128(usize, di.dwarf_in_stream);1583 const dir_index = try leb.readULEB128(usize, &s.stream);
1588 const mtime = try leb.readULEB128(usize, di.dwarf_in_stream);1584 const mtime = try leb.readULEB128(usize, &s.stream);
1589 const len_bytes = try leb.readULEB128(usize, di.dwarf_in_stream);1585 const len_bytes = try leb.readULEB128(usize, &s.stream);
1590 try file_entries.append(FileEntry{1586 try file_entries.append(FileEntry{
1591 .file_name = file_name,1587 .file_name = file_name,
1592 .dir_index = dir_index,1588 .dir_index = dir_index,
...@@ -1596,7 +1592,7 @@ pub const DwarfInfo = struct {...@@ -1596,7 +1592,7 @@ pub const DwarfInfo = struct {
1596 },1592 },
1597 else => {1593 else => {
1598 const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo;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 } else if (opcode >= opcode_base) {1598 } else if (opcode >= opcode_base) {
...@@ -1615,19 +1611,19 @@ pub const DwarfInfo = struct {...@@ -1615,19 +1611,19 @@ pub const DwarfInfo = struct {
1615 prog.basic_block = false;1611 prog.basic_block = false;
1616 },1612 },
1617 DW.LNS_advance_pc => {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 prog.address += arg * minimum_instruction_length;1615 prog.address += arg * minimum_instruction_length;
1620 },1616 },
1621 DW.LNS_advance_line => {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 prog.line += arg;1619 prog.line += arg;
1624 },1620 },
1625 DW.LNS_set_file => {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 prog.file = arg;1623 prog.file = arg;
1628 },1624 },
1629 DW.LNS_set_column => {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 prog.column = arg;1627 prog.column = arg;
1632 },1628 },
1633 DW.LNS_negate_stmt => {1629 DW.LNS_negate_stmt => {
...@@ -1641,14 +1637,14 @@ pub const DwarfInfo = struct {...@@ -1641,14 +1637,14 @@ pub const DwarfInfo = struct {
1641 prog.address += inc_addr;1637 prog.address += inc_addr;
1642 },1638 },
1643 DW.LNS_fixed_advance_pc => {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 prog.address += arg;1641 prog.address += arg;
1646 },1642 },
1647 DW.LNS_set_prologue_end => {},1643 DW.LNS_set_prologue_end => {},
1648 else => {1644 else => {
1649 if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo;1645 if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo;
1650 const len_bytes = standard_opcode_lengths[opcode - 1];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,9 +1654,17 @@ pub const DwarfInfo = struct {
1658 }1654 }
16591655
1660 fn getString(di: *DwarfInfo, offset: u64) ![]u8 {1656 fn getString(di: *DwarfInfo, offset: u64) ![]u8 {
1661 const pos = di.debug_str.offset + offset;1657 if (offset > di.debug_str.len)
1662 try di.dwarf_seekable_stream.seekTo(pos);1658 return error.InvalidDebugInfo;
1663 return di.readString();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};
16661670
...@@ -1672,7 +1676,7 @@ pub const DebugInfo = switch (builtin.os) {...@@ -1672,7 +1676,7 @@ pub const DebugInfo = switch (builtin.os) {
16721676
1673 const OFileTable = std.HashMap(1677 const OFileTable = std.HashMap(
1674 *macho.nlist_64,1678 *macho.nlist_64,
1675 MachOFile,1679 DwarfInfo,
1676 std.hash_map.getHashPtrAddrFn(*macho.nlist_64),1680 std.hash_map.getHashPtrAddrFn(*macho.nlist_64),
1677 std.hash_map.getTrivialEqlFn(*macho.nlist_64),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,24 +2070,32 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con
2066 return null;2070 return null;
2067}2071}
20682072
2069fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: usize) !LineInfo {2073fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, address: usize) !LineInfo {
2070 const ofile = symbol.ofile orelse return error.MissingDebugInfo;2074 const ofile = symbol.ofile orelse return error.MissingDebugInfo;
2071 const gop = try di.ofiles.getOrPut(ofile);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 errdefer _ = di.ofiles.remove(ofile);2077 errdefer _ = di.ofiles.remove(ofile);
2074 const ofile_path = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + ofile.n_strx));2078 const ofile_path = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + ofile.n_strx));
20752079
2076 gop.kv.value = MachOFile{2080 var exe_file = try std.fs.openFileAbsoluteC(ofile_path, .{});
2077 .bytes = try std.fs.cwd().readFileAllocAligned(2081 errdefer exe_file.close();
2078 di.ofiles.allocator,2082
2079 ofile_path,2083 const exe_len = math.cast(usize, try exe_file.getEndPos()) catch
2080 maxInt(usize),2084 return error.DebugInfoTooLarge;
2081 @alignOf(macho.mach_header_64),2085 const exe_mmap = try os.mmap(
2082 ),2086 null,
2083 .sect_debug_info = null,2087 exe_len,
2084 .sect_debug_line = null,2088 os.PROT_READ,
2085 };2089 os.MAP_SHARED,
2086 const hdr = @ptrCast(*const macho.mach_header_64, gop.kv.value.bytes.ptr);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 if (hdr.magic != std.macho.MH_MAGIC_64) return error.InvalidDebugInfo;2099 if (hdr.magic != std.macho.MH_MAGIC_64) return error.InvalidDebugInfo;
20882100
2089 const hdr_base = @ptrCast([*]const u8, hdr);2101 const hdr_base = @ptrCast([*]const u8, hdr);
...@@ -2092,181 +2104,75 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u...@@ -2092,181 +2104,75 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u
2092 const segcmd = while (ncmd != 0) : (ncmd -= 1) {2104 const segcmd = while (ncmd != 0) : (ncmd -= 1) {
2093 const lc = @ptrCast(*const std.macho.load_command, ptr);2105 const lc = @ptrCast(*const std.macho.load_command, ptr);
2094 switch (lc.cmd) {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 else => {},2113 else => {},
2097 }2114 }
2098 ptr = @alignCast(@alignOf(std.macho.load_command), ptr + lc.cmdsize);2115 ptr = @alignCast(@alignOf(std.macho.load_command), ptr + lc.cmdsize);
2099 } else {2116 } else {
2100 return error.MissingDebugInfo;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 const sections = @ptrCast([*]const macho.section_64, @alignCast(@alignOf(macho.section_64), ptr + @sizeOf(std.macho.segment_command_64)))[0..segcmd.nsects];2126 const sections = @ptrCast([*]const macho.section_64, @alignCast(@alignOf(macho.section_64), ptr + @sizeOf(std.macho.segment_command_64)))[0..segcmd.nsects];
2103 for (sections) |*sect| {2127 for (sections) |*sect| {
2104 if (sect.flags & macho.SECTION_TYPE == macho.S_REGULAR and2128 // The section name may not exceed 16 chars and a trailing null may
2105 (sect.flags & macho.SECTION_ATTRIBUTES) & macho.S_ATTR_DEBUG == macho.S_ATTR_DEBUG)2129 // not be present
2106 {2130 const name = if (mem.indexOfScalar(u8, sect.sectname[0..], 0)) |last|
2107 const sect_name = mem.toSliceConst(u8, @ptrCast([*:0]const u8, &sect.sectname));2131 sect.sectname[0..last]
2108 if (mem.eql(u8, sect_name, "__debug_line")) {2132 else
2109 gop.kv.value.sect_debug_line = sect;2133 sect.sectname[0..];
2110 } else if (mem.eql(u8, sect_name, "__debug_info")) {2134
2111 gop.kv.value.sect_debug_info = sect;2135 if (mem.eql(u8, name, "__debug_line")) {
2112 }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 }
21152147
2116 break :blk &gop.kv.value;2148 var debug_line = opt_debug_line orelse
2117 };2149 return error.MissingDebugInfo;
21182150 var debug_info = opt_debug_info orelse
2119 const sect_debug_line = mach_o_file.sect_debug_line orelse return error.MissingDebugInfo;2151 return error.MissingDebugInfo;
2120 var ptr = mach_o_file.bytes.ptr + sect_debug_line.offset;2152 var debug_str = opt_debug_str orelse
21212153 return error.MissingDebugInfo;
2122 var is_64: bool = undefined;2154 var debug_abbrev = opt_debug_abbrev orelse
2123 const unit_length = try readInitialLengthMem(&ptr, &is_64);2155 return error.MissingDebugInfo;
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);
21652156
2166 while (true) {2157 gop.kv.value = DwarfInfo{
2167 const file_name = readStringMem(&ptr);2158 .endian = .Little,
2168 if (file_name.len == 0) break;2159 .debug_info = exe_mmap[@intCast(usize, debug_info.offset)..@intCast(usize, debug_info.offset + debug_info.size)],
2169 const dir_index = try leb.readULEB128Mem(usize, &ptr);2160 .debug_abbrev = exe_mmap[@intCast(usize, debug_abbrev.offset)..@intCast(usize, debug_abbrev.offset + debug_abbrev.size)],
2170 const mtime = try leb.readULEB128Mem(usize, &ptr);2161 .debug_str = exe_mmap[@intCast(usize, debug_str.offset)..@intCast(usize, debug_str.offset + debug_str.size)],
2171 const len_bytes = try leb.readULEB128Mem(usize, &ptr);2162 .debug_line = exe_mmap[@intCast(usize, debug_line.offset)..@intCast(usize, debug_line.offset + debug_line.size)],
2172 try file_entries.append(FileEntry{2163 .debug_ranges = if (opt_debug_ranges) |debug_ranges|
2173 .file_name = file_name,2164 exe_mmap[@intCast(usize, debug_ranges.offset)..@intCast(usize, debug_ranges.offset + debug_ranges.size)]
2174 .dir_index = dir_index,2165 else
2175 .mtime = mtime,2166 null,
2176 .len_bytes = len_bytes,2167 };
2177 });2168 try openDwarfDebugInfo(&gop.kv.value, di.allocator());
2178 }
21792169
2180 ptr = prog_start;2170 break :blk &gop.kv.value;
2181 while (true) {2171 };
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 }
22682172
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}
22712177
2272const Func = struct {2178const Func = struct {
...@@ -2274,47 +2180,6 @@ const Func = struct {...@@ -2274,47 +2180,6 @@ const Func = struct {
2274 name: ?[]u8,2180 name: ?[]u8,
2275};2181};
22762182
2277fn 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
2285fn readByteMem(ptr: *[*]const u8) u8 {
2286 const result = ptr.*[0];
2287 ptr.* += 1;
2288 return result;
2289}
2290
2291fn readByteSignedMem(ptr: *[*]const u8) i8 {
2292 return @bitCast(i8, readByteMem(ptr));
2293}
2294
2295fn 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
2312fn 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
2318fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 {2183fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 {
2319 const first_32_bits = try in_stream.readIntLittle(u32);2184 const first_32_bits = try in_stream.readIntLittle(u32);
2320 is_64.* = (first_32_bits == 0xffffffff);2185 is_64.* = (first_32_bits == 0xffffffff);