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");
77
88extern "c" fn __error() *c_int;
99pub extern "c" fn _NSGetExecutablePath(buf: [*]u8, bufsize: *u32) c_int;
10pub extern "c" fn _dyld_image_count() u32;
1011pub 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
1215pub 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 {
963963
964964pub fn openElfDebugInfo(
965965 allocator: *mem.Allocator,
966 elf_seekable_stream: *DwarfSeekableStream,
967 elf_in_stream: *DwarfInStream,
966 data: []u8,
968967) !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");
971985
972986 var di = DwarfInfo{
973 .dwarf_seekable_stream = elf_seekable_stream,
974 .dwarf_in_stream = elf_in_stream,
975987 .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,
984996 };
997
998 efile.close();
999
9851000 try openDwarfDebugInfo(&di, allocator);
9861001 return di;
9871002}
9881003
9891004fn 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();
9971007
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(
10011011 null,
1002 self_exe_mmap_len,
1012 exe_len,
10031013 os.PROT_READ,
10041014 os.MAP_SHARED,
1005 S.self_exe_file.handle,
1015 exe_file.handle,
10061016 0,
10071017 );
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);
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);
10191021}
10201022
10211023fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo {
......@@ -1142,41 +1144,26 @@ const MachoSymbol = struct {
11421144 }
11431145};
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
11511147pub const DwarfSeekableStream = io.SeekableStream(anyerror, anyerror);
11521148pub const DwarfInStream = io.InStream(anyerror);
11531149
11541150pub const DwarfInfo = struct {
1155 dwarf_seekable_stream: *DwarfSeekableStream,
1156 dwarf_in_stream: *DwarfInStream,
11571151 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,
11711162
11721163 pub fn allocator(self: DwarfInfo) *mem.Allocator {
11731164 return self.abbrev_table_list.allocator;
11741165 }
11751166
1176 pub fn readString(self: *DwarfInfo) ![]u8 {
1177 return readStringRaw(self.allocator(), self.dwarf_in_stream);
1178 }
1179
11801167 /// This function works in freestanding mode.
11811168 /// fn printLineFromFile(out_stream: var, line_info: LineInfo) !void
11821169 pub fn printSourceAtAddress(
......@@ -1222,35 +1209,38 @@ pub const DwarfInfo = struct {
12221209 }
12231210
12241211 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;
12271214
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 };
12301220
12311221 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);
12331223 if (unit_length == 0) return;
12341224 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);
12371227 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();
12421232 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();
12451235 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
12491239 const next_unit_pos = this_unit_offset + next_offset;
12501240
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();
12541244
12551245 switch (die_obj.tag_id) {
12561246 DW.TAG_subprogram, DW.TAG_inlined_subroutine, DW.TAG_subroutine, DW.TAG_entry_point => {
......@@ -1266,14 +1256,14 @@ pub const DwarfInfo = struct {
12661256 // Follow the DIE it points to and repeat
12671257 const ref_offset = try this_die_obj.getAttrRef(DW.AT_abstract_origin);
12681258 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;
12711261 } else if (this_die_obj.getAttr(DW.AT_specification)) |ref| {
12721262 // Follow the DIE it points to and repeat
12731263 const ref_offset = try this_die_obj.getAttrRef(DW.AT_specification);
12741264 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;
12771267 } else {
12781268 break :x null;
12791269 }
......@@ -1311,12 +1301,10 @@ pub const DwarfInfo = struct {
13111301 .pc_range = pc_range,
13121302 });
13131303 },
1314 else => {
1315 continue;
1316 },
1304 else => {},
13171305 }
13181306
1319 try di.dwarf_seekable_stream.seekTo(after_die_offset);
1307 try s.seekable_stream.seekTo(after_die_offset);
13201308 }
13211309
13221310 this_unit_offset += next_offset;
......@@ -1324,32 +1312,35 @@ pub const DwarfInfo = struct {
13241312 }
13251313
13261314 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;
13291317
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 };
13321323
13331324 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);
13351326 if (unit_length == 0) return;
13361327 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);
13391330 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();
13441335 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();
13471338 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
13511342 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
13541345 if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo;
13551346
......@@ -1395,15 +1386,18 @@ pub const DwarfInfo = struct {
13951386 }
13961387 if (di.debug_ranges) |debug_ranges| {
13971388 if (compile_unit.die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| {
1389 var s = io.SliceSeekableInStream.init(debug_ranges);
1390
13981391 // All the addresses in the list are relative to the value
13991392 // specified by DW_AT_low_pc or to some other value encoded
14001393 // in the list itself
14011394 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
14041398 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);
14071401 if (begin_addr == 0 and end_addr == 0) {
14081402 break;
14091403 }
......@@ -1435,30 +1429,33 @@ pub const DwarfInfo = struct {
14351429 return &header.table;
14361430 }
14371431 }
1438 try di.dwarf_seekable_stream.seekTo(di.debug_abbrev.offset + abbrev_offset);
14391432 try di.abbrev_table_list.append(AbbrevTableHeader{
14401433 .offset = abbrev_offset,
1441 .table = try di.parseAbbrevTable(),
1434 .table = try di.parseAbbrevTable(abbrev_offset),
14421435 });
14431436 return &di.abbrev_table_list.items[di.abbrev_table_list.len - 1].table;
14441437 }
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);
14471443 var result = AbbrevTable.init(di.allocator());
1444 errdefer result.deinit();
14481445 while (true) {
1449 const abbrev_code = try leb.readULEB128(u64, di.dwarf_in_stream);
1446 const abbrev_code = try leb.readULEB128(u64, &s.stream);
14501447 if (abbrev_code == 0) return result;
14511448 try result.append(AbbrevTableEntry{
14521449 .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,
14551452 .attrs = ArrayList(AbbrevAttr).init(di.allocator()),
14561453 });
14571454 const attrs = &result.items[result.len - 1].attrs;
14581455
14591456 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);
14621459 if (attr_id == 0 and form_id == 0) break;
14631460 try attrs.append(AbbrevAttr{
14641461 .attr_id = attr_id,
......@@ -1468,8 +1465,8 @@ pub const DwarfInfo = struct {
14681465 }
14691466 }
14701467
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);
14731470 if (abbrev_code == 0) return null;
14741471 const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo;
14751472
......@@ -1482,64 +1479,63 @@ pub const DwarfInfo = struct {
14821479 for (table_entry.attrs.toSliceConst()) |attr, i| {
14831480 result.attrs.items[i] = Die.Attr{
14841481 .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),
14861483 };
14871484 }
14881485 return result;
14891486 }
14901487
14911488 fn getLineNumberInfo(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !LineInfo {
1489 var s = io.SliceSeekableInStream.init(di.debug_line);
1490
14921491 const compile_unit_cwd = try compile_unit.die.getAttrString(di, DW.AT_comp_dir);
14931492 const line_info_offset = try compile_unit.die.getAttrSecOffset(DW.AT_stmt_list);
14941493
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);
14991495
15001496 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);
15021498 if (unit_length == 0) {
15031499 return error.MissingDebugInfo;
15041500 }
15051501 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);
15081504 // TODO support 3 and 5
15091505 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);
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;
15131509
1514 const minimum_instruction_length = try di.dwarf_in_stream.readByte();
1510 const minimum_instruction_length = try s.stream.readByte();
15151511 if (minimum_instruction_length == 0) return error.InvalidDebugInfo;
15161512
15171513 if (version >= 4) {
15181514 // maximum_operations_per_instruction
1519 _ = try di.dwarf_in_stream.readByte();
1515 _ = try s.stream.readByte();
15201516 }
15211517
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();
15241520
1525 const line_range = try di.dwarf_in_stream.readByte();
1521 const line_range = try s.stream.readByte();
15261522 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
15301526 const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1);
15311527
15321528 {
15331529 var i: usize = 0;
15341530 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();
15361532 }
15371533 }
15381534
15391535 var include_directories = ArrayList([]u8).init(di.allocator());
15401536 try include_directories.append(compile_unit_cwd);
15411537 while (true) {
1542 const dir = try di.readString();
1538 const dir = try readStringRaw(di.allocator(), &s.stream);
15431539 if (dir.len == 0) break;
15441540 try include_directories.append(dir);
15451541 }
......@@ -1548,11 +1544,11 @@ pub const DwarfInfo = struct {
15481544 var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address);
15491545
15501546 while (true) {
1551 const file_name = try di.readString();
1547 const file_name = try readStringRaw(di.allocator(), &s.stream);
15521548 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);
15561552 try file_entries.append(FileEntry{
15571553 .file_name = file_name,
15581554 .dir_index = dir_index,
......@@ -1561,17 +1557,17 @@ pub const DwarfInfo = struct {
15611557 });
15621558 }
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) {
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();
15701566
15711567 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);
15731569 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();
15751571 switch (sub_op) {
15761572 DW.LNE_end_sequence => {
15771573 prog.end_sequence = true;
......@@ -1579,14 +1575,14 @@ pub const DwarfInfo = struct {
15791575 prog.reset();
15801576 },
15811577 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);
15831579 prog.address = addr;
15841580 },
15851581 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);
15901586 try file_entries.append(FileEntry{
15911587 .file_name = file_name,
15921588 .dir_index = dir_index,
......@@ -1596,7 +1592,7 @@ pub const DwarfInfo = struct {
15961592 },
15971593 else => {
15981594 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);
16001596 },
16011597 }
16021598 } else if (opcode >= opcode_base) {
......@@ -1615,19 +1611,19 @@ pub const DwarfInfo = struct {
16151611 prog.basic_block = false;
16161612 },
16171613 DW.LNS_advance_pc => {
1618 const arg = try leb.readULEB128(usize, di.dwarf_in_stream);
1614 const arg = try leb.readULEB128(usize, &s.stream);
16191615 prog.address += arg * minimum_instruction_length;
16201616 },
16211617 DW.LNS_advance_line => {
1622 const arg = try leb.readILEB128(i64, di.dwarf_in_stream);
1618 const arg = try leb.readILEB128(i64, &s.stream);
16231619 prog.line += arg;
16241620 },
16251621 DW.LNS_set_file => {
1626 const arg = try leb.readULEB128(usize, di.dwarf_in_stream);
1622 const arg = try leb.readULEB128(usize, &s.stream);
16271623 prog.file = arg;
16281624 },
16291625 DW.LNS_set_column => {
1630 const arg = try leb.readULEB128(u64, di.dwarf_in_stream);
1626 const arg = try leb.readULEB128(u64, &s.stream);
16311627 prog.column = arg;
16321628 },
16331629 DW.LNS_negate_stmt => {
......@@ -1641,14 +1637,14 @@ pub const DwarfInfo = struct {
16411637 prog.address += inc_addr;
16421638 },
16431639 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);
16451641 prog.address += arg;
16461642 },
16471643 DW.LNS_set_prologue_end => {},
16481644 else => {
16491645 if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo;
16501646 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);
16521648 },
16531649 }
16541650 }
......@@ -1658,9 +1654,17 @@ pub const DwarfInfo = struct {
16581654 }
16591655
16601656 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;
16641668 }
16651669};
16661670
......@@ -1672,7 +1676,7 @@ pub const DebugInfo = switch (builtin.os) {
16721676
16731677 const OFileTable = std.HashMap(
16741678 *macho.nlist_64,
1675 MachOFile,
1679 DwarfInfo,
16761680 std.hash_map.getHashPtrAddrFn(*macho.nlist_64),
16771681 std.hash_map.getTrivialEqlFn(*macho.nlist_64),
16781682 );
......@@ -2066,24 +2070,32 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con
20662070 return null;
20672071}
20682072
2069fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: usize) !LineInfo {
2073fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, address: usize) !LineInfo {
20702074 const ofile = symbol.ofile orelse return error.MissingDebugInfo;
20712075 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: {
20732077 errdefer _ = di.ofiles.remove(ofile);
20742078 const ofile_path = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + ofile.n_strx));
20752079
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 );
20872099 if (hdr.magic != std.macho.MH_MAGIC_64) return error.InvalidDebugInfo;
20882100
20892101 const hdr_base = @ptrCast([*]const u8, hdr);
......@@ -2092,181 +2104,75 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u
20922104 const segcmd = while (ncmd != 0) : (ncmd -= 1) {
20932105 const lc = @ptrCast(*const std.macho.load_command, ptr);
20942106 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 },
20962113 else => {},
20972114 }
20982115 ptr = @alignCast(@alignOf(std.macho.load_command), ptr + lc.cmdsize);
20992116 } else {
21002117 return error.MissingDebugInfo;
21012118 };
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
21022126 const sections = @ptrCast([*]const macho.section_64, @alignCast(@alignOf(macho.section_64), ptr + @sizeOf(std.macho.segment_command_64)))[0..segcmd.nsects];
21032127 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;
21132145 }
21142146 }
21152147
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;
21652156
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());
21792169
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 };
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);
22702176}
22712177
22722178const Func = struct {
......@@ -2274,47 +2180,6 @@ const Func = struct {
22742180 name: ?[]u8,
22752181};
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
23182183fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 {
23192184 const first_32_bits = try in_stream.readIntLittle(u32);
23202185 is_64.* = (first_32_bits == 0xffffffff);