| ... | @@ -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 { |
| 963 | | 963 | |
| 964 | pub fn openElfDebugInfo( | 964 | pub 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"); |
| 971 | | 985 | |
| 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 | } |
| 988 | | 1003 | |
| 989 | fn openSelfDebugInfoPosix(allocator: *mem.Allocator) !DwarfInfo { | 1004 | fn 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(); | | |
| 997 | | 1007 | |
| 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); |
| 1009 | | 1019 | |
| 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 | } |
| 1020 | | 1022 | |
| 1021 | fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { | 1023 | fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { |
| ... | @@ -1142,41 +1144,26 @@ const MachoSymbol = struct { | ... | @@ -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 | pub const DwarfSeekableStream = io.SeekableStream(anyerror, anyerror); | 1147 | pub const DwarfSeekableStream = io.SeekableStream(anyerror, anyerror); |
| 1152 | pub const DwarfInStream = io.InStream(anyerror); | 1148 | pub const DwarfInStream = io.InStream(anyerror); |
| 1153 | | 1149 | |
| 1154 | pub const DwarfInfo = struct { | 1150 | pub 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, |
| 1166 | | 1160 | compile_unit_list: ArrayList(CompileUnit) = undefined, |
| 1167 | pub const Section = struct { | 1161 | func_list: ArrayList(Func) = undefined, |
| 1168 | offset: u64, | | |
| 1169 | size: u64, | | |
| 1170 | }; | | |
| 1171 | | 1162 | |
| 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 | } |
| 1175 | | 1166 | |
| 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) !void | 1168 | /// 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 | } |
| 1223 | | 1210 | |
| 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; |
| 1227 | | 1214 | |
| 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 | }; |
| 1230 | | 1220 | |
| 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)); |
| 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 | if (version < 2 or version > 5) return error.InvalidDebugInfo; | 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 | if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; | 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 | const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset); | 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 | const next_unit_pos = this_unit_offset + next_offset; | 1239 | const next_unit_pos = this_unit_offset + next_offset; |
| 1250 | | 1240 | |
| 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(); |
| 1254 | | 1244 | |
| 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 repeat | 1256 | // 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 repeat | 1262 | // 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 | } |
| 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 | 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 | } |
| 1325 | | 1313 | |
| 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; |
| 1329 | | 1317 | |
| 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 | }; |
| 1332 | | 1323 | |
| 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)); |
| 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 | if (version < 2 or version > 5) return error.InvalidDebugInfo; | 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 | if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; | 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 | const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset); | 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 | 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; |
| 1353 | | 1344 | |
| 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; |
| 1355 | | 1346 | |
| ... | @@ -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 value | 1391 | // All the addresses in the list are relative to the value |
| 1399 | // specified by DW_AT_low_pc or to some other value encoded | 1392 | // specified by DW_AT_low_pc or to some other value encoded |
| 1400 | // in the list itself | 1393 | // 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); |
| 1402 | | 1395 | |
| 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 | } |
| 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 | 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; |
| 1458 | | 1455 | |
| 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 | } |
| 1470 | | 1467 | |
| 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; |
| 1475 | | 1472 | |
| ... | @@ -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 | } |
| 1490 | | 1487 | |
| 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); |
| 1494 | | 1493 | |
| 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); | | |
| 1499 | | 1495 | |
| 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)); |
| 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 | // TODO support 3 and 5 | 1504 | // 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; |
| 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); | 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; |
| 1513 | | 1509 | |
| 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; |
| 1516 | | 1512 | |
| 1517 | if (version >= 4) { | 1513 | if (version >= 4) { |
| 1518 | // maximum_operations_per_instruction | 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; | 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(); |
| 1524 | | 1520 | |
| 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; |
| 1527 | | 1523 | |
| 1528 | const opcode_base = try di.dwarf_in_stream.readByte(); | 1524 | const opcode_base = try s.stream.readByte(); |
| 1529 | | 1525 | |
| 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); |
| 1531 | | 1527 | |
| 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 | } |
| 1538 | | 1534 | |
| 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); |
| 1549 | | 1545 | |
| 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 | } |
| 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) { | 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(); |
| 1570 | | 1566 | |
| 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 | } |
| 1659 | | 1655 | |
| 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 | }; |
| 1666 | | 1670 | |
| ... | @@ -1672,7 +1676,7 @@ pub const DebugInfo = switch (builtin.os) { | ... | @@ -1672,7 +1676,7 @@ pub const DebugInfo = switch (builtin.os) { |
| 1672 | | 1676 | |
| 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 | } |
| 2068 | | 2072 | |
| 2069 | fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: usize) !LineInfo { | 2073 | fn 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)); |
| 2075 | | 2079 | |
| 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; |
| 2088 | | 2100 | |
| 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 and | 2128 | // 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 | } |
| 2115 | | 2147 | |
| 2116 | break :blk &gop.kv.value; | 2148 | var debug_line = opt_debug_line orelse |
| 2117 | }; | 2149 | return error.MissingDebugInfo; |
| 2118 | | 2150 | 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 |
| 2121 | | 2153 | 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); | | |
| 2165 | | 2156 | |
| 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 | } | | |
| 2179 | | 2169 | |
| 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 | } | | |
| 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 | const Func = struct { | 2178 | const Func = struct { |
| ... | @@ -2274,47 +2180,6 @@ const Func = struct { | ... | @@ -2274,47 +2180,6 @@ const Func = struct { |
| 2274 | name: ?[]u8, | 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 | fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 { | 2183 | fn 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); |