| ... | @@ -559,7 +559,7 @@ pub const TTY = struct { | ... | @@ -559,7 +559,7 @@ pub const TTY = struct { |
| 559 | | 559 | |
| 560 | fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const MachoSymbol { | 560 | fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const MachoSymbol { |
| 561 | var min: usize = 0; | 561 | var min: usize = 0; |
| 562 | var max: usize = symbols.len - 1; // Exclude sentinel. | 562 | var max: usize = symbols.len; |
| 563 | while (min < max) { | 563 | while (min < max) { |
| 564 | const mid = min + (max - min) / 2; | 564 | const mid = min + (max - min) / 2; |
| 565 | const curr = &symbols[mid]; | 565 | const curr = &symbols[mid]; |
| ... | @@ -850,51 +850,91 @@ fn readMachODebugInfo(allocator: *mem.Allocator, macho_file: File) !ModuleDebugI | ... | @@ -850,51 +850,91 @@ fn readMachODebugInfo(allocator: *mem.Allocator, macho_file: File) !ModuleDebugI |
| 850 | } else { | 850 | } else { |
| 851 | return error.MissingDebugInfo; | 851 | return error.MissingDebugInfo; |
| 852 | }; | 852 | }; |
| 853 | const syms = @ptrCast([*]const macho.nlist_64, @alignCast(@alignOf(macho.nlist_64), hdr_base + symtab.symoff))[0..symtab.nsyms]; | 853 | const syms = @ptrCast( |
| | 854 | [*]const macho.nlist_64, |
| | 855 | @alignCast(@alignOf(macho.nlist_64), hdr_base + symtab.symoff), |
| | 856 | )[0..symtab.nsyms]; |
| 854 | const strings = @ptrCast([*]const u8, hdr_base + symtab.stroff)[0 .. symtab.strsize - 1 :0]; | 857 | const strings = @ptrCast([*]const u8, hdr_base + symtab.stroff)[0 .. symtab.strsize - 1 :0]; |
| 855 | | 858 | |
| 856 | const symbols_buf = try allocator.alloc(MachoSymbol, syms.len); | 859 | const symbols_buf = try allocator.alloc(MachoSymbol, syms.len); |
| 857 | | 860 | |
| 858 | var ofile: ?*const macho.nlist_64 = null; | 861 | var ofile: u32 = undefined; |
| 859 | var reloc: u64 = 0; | 862 | var last_sym: MachoSymbol = undefined; |
| 860 | var symbol_index: usize = 0; | 863 | var symbol_index: usize = 0; |
| 861 | var last_len: u64 = 0; | 864 | var state: enum { |
| | 865 | init, |
| | 866 | oso_open, |
| | 867 | oso_close, |
| | 868 | bnsym, |
| | 869 | fun_strx, |
| | 870 | fun_size, |
| | 871 | ensym, |
| | 872 | } = .init; |
| | 873 | |
| 862 | for (syms) |*sym| { | 874 | for (syms) |*sym| { |
| 863 | if (sym.n_type & std.macho.N_STAB != 0) { | 875 | if (!sym.stab()) continue; |
| 864 | switch (sym.n_type) { | 876 | |
| 865 | std.macho.N_OSO => { | 877 | // TODO handle globals N_GSYM, and statics N_STSYM |
| 866 | ofile = sym; | 878 | switch (sym.n_type) { |
| 867 | reloc = 0; | 879 | macho.N_OSO => { |
| 868 | }, | 880 | switch (state) { |
| 869 | std.macho.N_FUN => { | 881 | .init, .oso_close => { |
| 870 | if (sym.n_sect == 0) { | 882 | state = .oso_open; |
| 871 | last_len = sym.n_value; | 883 | ofile = sym.n_strx; |
| 872 | } else { | 884 | }, |
| 873 | symbols_buf[symbol_index] = MachoSymbol{ | 885 | else => return error.InvalidDebugInfo, |
| 874 | .nlist = sym, | 886 | } |
| | 887 | }, |
| | 888 | macho.N_BNSYM => { |
| | 889 | switch (state) { |
| | 890 | .oso_open, .ensym => { |
| | 891 | state = .bnsym; |
| | 892 | last_sym = .{ |
| | 893 | .strx = 0, |
| | 894 | .addr = sym.n_value, |
| | 895 | .size = 0, |
| 875 | .ofile = ofile, | 896 | .ofile = ofile, |
| 876 | .reloc = reloc, | | |
| 877 | }; | 897 | }; |
| | 898 | }, |
| | 899 | else => return error.InvalidDebugInfo, |
| | 900 | } |
| | 901 | }, |
| | 902 | macho.N_FUN => { |
| | 903 | switch (state) { |
| | 904 | .bnsym => { |
| | 905 | state = .fun_strx; |
| | 906 | last_sym.strx = sym.n_strx; |
| | 907 | }, |
| | 908 | .fun_strx => { |
| | 909 | state = .fun_size; |
| | 910 | last_sym.size = @intCast(u32, sym.n_value); |
| | 911 | }, |
| | 912 | else => return error.InvalidDebugInfo, |
| | 913 | } |
| | 914 | }, |
| | 915 | macho.N_ENSYM => { |
| | 916 | switch (state) { |
| | 917 | .fun_size => { |
| | 918 | state = .ensym; |
| | 919 | symbols_buf[symbol_index] = last_sym; |
| 878 | symbol_index += 1; | 920 | symbol_index += 1; |
| 879 | } | 921 | }, |
| 880 | }, | 922 | else => return error.InvalidDebugInfo, |
| 881 | std.macho.N_BNSYM => { | 923 | } |
| 882 | if (reloc == 0) { | 924 | }, |
| 883 | reloc = sym.n_value; | 925 | macho.N_SO => { |
| 884 | } | 926 | switch (state) { |
| 885 | }, | 927 | .init, .oso_close => {}, |
| 886 | else => continue, | 928 | .oso_open, .ensym => { |
| 887 | } | 929 | state = .oso_close; |
| | 930 | }, |
| | 931 | else => return error.InvalidDebugInfo, |
| | 932 | } |
| | 933 | }, |
| | 934 | else => {}, |
| 888 | } | 935 | } |
| 889 | } | 936 | } |
| 890 | const sentinel = try allocator.create(macho.nlist_64); | 937 | assert(state == .oso_close); |
| 891 | sentinel.* = macho.nlist_64{ | | |
| 892 | .n_strx = 0, | | |
| 893 | .n_type = 36, | | |
| 894 | .n_sect = 0, | | |
| 895 | .n_desc = 0, | | |
| 896 | .n_value = symbols_buf[symbol_index - 1].nlist.n_value + last_len, | | |
| 897 | }; | | |
| 898 | | 938 | |
| 899 | const symbols = allocator.shrink(symbols_buf, symbol_index); | 939 | const symbols = allocator.shrink(symbols_buf, symbol_index); |
| 900 | | 940 | |
| ... | @@ -946,18 +986,19 @@ fn printLineFromFileAnyOs(out_stream: anytype, line_info: LineInfo) !void { | ... | @@ -946,18 +986,19 @@ fn printLineFromFileAnyOs(out_stream: anytype, line_info: LineInfo) !void { |
| 946 | } | 986 | } |
| 947 | | 987 | |
| 948 | const MachoSymbol = struct { | 988 | const MachoSymbol = struct { |
| 949 | nlist: *const macho.nlist_64, | 989 | strx: u32, |
| 950 | ofile: ?*const macho.nlist_64, | 990 | addr: u64, |
| 951 | reloc: u64, | 991 | size: u32, |
| | 992 | ofile: u32, |
| 952 | | 993 | |
| 953 | /// Returns the address from the macho file | 994 | /// Returns the address from the macho file |
| 954 | fn address(self: MachoSymbol) u64 { | 995 | fn address(self: MachoSymbol) u64 { |
| 955 | return self.nlist.n_value; | 996 | return self.addr; |
| 956 | } | 997 | } |
| 957 | | 998 | |
| 958 | fn addressLessThan(context: void, lhs: MachoSymbol, rhs: MachoSymbol) bool { | 999 | fn addressLessThan(context: void, lhs: MachoSymbol, rhs: MachoSymbol) bool { |
| 959 | _ = context; | 1000 | _ = context; |
| 960 | return lhs.address() < rhs.address(); | 1001 | return lhs.addr < rhs.addr; |
| 961 | } | 1002 | } |
| 962 | }; | 1003 | }; |
| 963 | | 1004 | |
| ... | @@ -1231,13 +1272,17 @@ pub const ModuleDebugInfo = switch (native_os) { | ... | @@ -1231,13 +1272,17 @@ pub const ModuleDebugInfo = switch (native_os) { |
| 1231 | strings: [:0]const u8, | 1272 | strings: [:0]const u8, |
| 1232 | ofiles: OFileTable, | 1273 | ofiles: OFileTable, |
| 1233 | | 1274 | |
| 1234 | const OFileTable = std.StringHashMap(DW.DwarfInfo); | 1275 | const OFileTable = std.StringHashMap(OFileInfo); |
| | 1276 | const OFileInfo = struct { |
| | 1277 | di: DW.DwarfInfo, |
| | 1278 | addr_table: std.StringHashMap(u64), |
| | 1279 | }; |
| 1235 | | 1280 | |
| 1236 | pub fn allocator(self: @This()) *mem.Allocator { | 1281 | pub fn allocator(self: @This()) *mem.Allocator { |
| 1237 | return self.ofiles.allocator; | 1282 | return self.ofiles.allocator; |
| 1238 | } | 1283 | } |
| 1239 | | 1284 | |
| 1240 | fn loadOFile(self: *@This(), o_file_path: []const u8) !DW.DwarfInfo { | 1285 | fn loadOFile(self: *@This(), o_file_path: []const u8) !OFileInfo { |
| 1241 | const o_file = try fs.cwd().openFile(o_file_path, .{ .intended_io_mode = .blocking }); | 1286 | const o_file = try fs.cwd().openFile(o_file_path, .{ .intended_io_mode = .blocking }); |
| 1242 | const mapped_mem = try mapWholeFile(o_file); | 1287 | const mapped_mem = try mapWholeFile(o_file); |
| 1243 | | 1288 | |
| ... | @@ -1250,22 +1295,54 @@ pub const ModuleDebugInfo = switch (native_os) { | ... | @@ -1250,22 +1295,54 @@ pub const ModuleDebugInfo = switch (native_os) { |
| 1250 | | 1295 | |
| 1251 | const hdr_base = @ptrCast([*]const u8, hdr); | 1296 | const hdr_base = @ptrCast([*]const u8, hdr); |
| 1252 | var ptr = hdr_base + @sizeOf(macho.mach_header_64); | 1297 | var ptr = hdr_base + @sizeOf(macho.mach_header_64); |
| | 1298 | var segptr = ptr; |
| 1253 | var ncmd: u32 = hdr.ncmds; | 1299 | var ncmd: u32 = hdr.ncmds; |
| 1254 | const segcmd = while (ncmd != 0) : (ncmd -= 1) { | 1300 | var segcmd: ?*const macho.segment_command_64 = null; |
| | 1301 | var symtabcmd: ?*const macho.symtab_command = null; |
| | 1302 | |
| | 1303 | while (ncmd != 0) : (ncmd -= 1) { |
| 1255 | const lc = @ptrCast(*const std.macho.load_command, ptr); | 1304 | const lc = @ptrCast(*const std.macho.load_command, ptr); |
| 1256 | switch (lc.cmd) { | 1305 | switch (lc.cmd) { |
| 1257 | std.macho.LC_SEGMENT_64 => { | 1306 | std.macho.LC_SEGMENT_64 => { |
| 1258 | break @ptrCast( | 1307 | segcmd = @ptrCast( |
| 1259 | *const std.macho.segment_command_64, | 1308 | *const std.macho.segment_command_64, |
| 1260 | @alignCast(@alignOf(std.macho.segment_command_64), ptr), | 1309 | @alignCast(@alignOf(std.macho.segment_command_64), ptr), |
| 1261 | ); | 1310 | ); |
| | 1311 | segptr = ptr; |
| | 1312 | }, |
| | 1313 | std.macho.LC_SYMTAB => { |
| | 1314 | symtabcmd = @ptrCast( |
| | 1315 | *const std.macho.symtab_command, |
| | 1316 | @alignCast(@alignOf(std.macho.symtab_command), ptr), |
| | 1317 | ); |
| 1262 | }, | 1318 | }, |
| 1263 | else => {}, | 1319 | else => {}, |
| 1264 | } | 1320 | } |
| 1265 | ptr = @alignCast(@alignOf(std.macho.load_command), ptr + lc.cmdsize); | 1321 | ptr = @alignCast(@alignOf(std.macho.load_command), ptr + lc.cmdsize); |
| 1266 | } else { | 1322 | } |
| 1267 | return error.MissingDebugInfo; | 1323 | |
| 1268 | }; | 1324 | if (segcmd == null or symtabcmd == null) return error.MissingDebugInfo; |
| | 1325 | |
| | 1326 | // Parse symbols |
| | 1327 | const strtab = @ptrCast( |
| | 1328 | [*]const u8, |
| | 1329 | hdr_base + symtabcmd.?.stroff, |
| | 1330 | )[0 .. symtabcmd.?.strsize - 1 :0]; |
| | 1331 | const symtab = @ptrCast( |
| | 1332 | [*]const macho.nlist_64, |
| | 1333 | @alignCast(@alignOf(macho.nlist_64), hdr_base + symtabcmd.?.symoff), |
| | 1334 | )[0..symtabcmd.?.nsyms]; |
| | 1335 | |
| | 1336 | // TODO handle tentative (common) symbols |
| | 1337 | var addr_table = std.StringHashMap(u64).init(self.allocator()); |
| | 1338 | try addr_table.ensureTotalCapacity(@intCast(u32, symtab.len)); |
| | 1339 | for (symtab) |sym| { |
| | 1340 | if (sym.n_strx == 0) continue; |
| | 1341 | if (sym.undf() or sym.tentative() or sym.abs()) continue; |
| | 1342 | const sym_name = mem.sliceTo(strtab[sym.n_strx..], 0); |
| | 1343 | // TODO is it possible to have a symbol collision? |
| | 1344 | addr_table.putAssumeCapacityNoClobber(sym_name, sym.n_value); |
| | 1345 | } |
| 1269 | | 1346 | |
| 1270 | var opt_debug_line: ?*const macho.section_64 = null; | 1347 | var opt_debug_line: ?*const macho.section_64 = null; |
| 1271 | var opt_debug_info: ?*const macho.section_64 = null; | 1348 | var opt_debug_info: ?*const macho.section_64 = null; |
| ... | @@ -1275,8 +1352,8 @@ pub const ModuleDebugInfo = switch (native_os) { | ... | @@ -1275,8 +1352,8 @@ pub const ModuleDebugInfo = switch (native_os) { |
| 1275 | | 1352 | |
| 1276 | const sections = @ptrCast( | 1353 | const sections = @ptrCast( |
| 1277 | [*]const macho.section_64, | 1354 | [*]const macho.section_64, |
| 1278 | @alignCast(@alignOf(macho.section_64), ptr + @sizeOf(std.macho.segment_command_64)), | 1355 | @alignCast(@alignOf(macho.section_64), segptr + @sizeOf(std.macho.segment_command_64)), |
| 1279 | )[0..segcmd.nsects]; | 1356 | )[0..segcmd.?.nsects]; |
| 1280 | for (sections) |*sect| { | 1357 | for (sections) |*sect| { |
| 1281 | // The section name may not exceed 16 chars and a trailing null may | 1358 | // The section name may not exceed 16 chars and a trailing null may |
| 1282 | // not be present | 1359 | // not be present |
| ... | @@ -1320,11 +1397,15 @@ pub const ModuleDebugInfo = switch (native_os) { | ... | @@ -1320,11 +1397,15 @@ pub const ModuleDebugInfo = switch (native_os) { |
| 1320 | }; | 1397 | }; |
| 1321 | | 1398 | |
| 1322 | try DW.openDwarfDebugInfo(&di, self.allocator()); | 1399 | try DW.openDwarfDebugInfo(&di, self.allocator()); |
| | 1400 | var info = OFileInfo{ |
| | 1401 | .di = di, |
| | 1402 | .addr_table = addr_table, |
| | 1403 | }; |
| 1323 | | 1404 | |
| 1324 | // Add the debug info to the cache | 1405 | // Add the debug info to the cache |
| 1325 | try self.ofiles.putNoClobber(o_file_path, di); | 1406 | try self.ofiles.putNoClobber(o_file_path, info); |
| 1326 | | 1407 | |
| 1327 | return di; | 1408 | return info; |
| 1328 | } | 1409 | } |
| 1329 | | 1410 | |
| 1330 | pub fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo { | 1411 | pub fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo { |
| ... | @@ -1336,18 +1417,15 @@ pub const ModuleDebugInfo = switch (native_os) { | ... | @@ -1336,18 +1417,15 @@ pub const ModuleDebugInfo = switch (native_os) { |
| 1336 | // Find the .o file where this symbol is defined | 1417 | // Find the .o file where this symbol is defined |
| 1337 | const symbol = machoSearchSymbols(self.symbols, relocated_address) orelse | 1418 | const symbol = machoSearchSymbols(self.symbols, relocated_address) orelse |
| 1338 | return SymbolInfo{}; | 1419 | return SymbolInfo{}; |
| | 1420 | const addr_off = relocated_address - symbol.addr; |
| 1339 | | 1421 | |
| 1340 | // Take the symbol name from the N_FUN STAB entry, we're going to | 1422 | // Take the symbol name from the N_FUN STAB entry, we're going to |
| 1341 | // use it if we fail to find the DWARF infos | 1423 | // use it if we fail to find the DWARF infos |
| 1342 | const stab_symbol = mem.sliceTo(self.strings[symbol.nlist.n_strx..], 0); | 1424 | const stab_symbol = mem.sliceTo(self.strings[symbol.strx..], 0); |
| 1343 | | 1425 | const o_file_path = mem.sliceTo(self.strings[symbol.ofile..], 0); |
| 1344 | if (symbol.ofile == null) | | |
| 1345 | return SymbolInfo{ .symbol_name = stab_symbol }; | | |
| 1346 | | | |
| 1347 | const o_file_path = mem.sliceTo(self.strings[symbol.ofile.?.n_strx..], 0); | | |
| 1348 | | 1426 | |
| 1349 | // Check if its debug infos are already in the cache | 1427 | // Check if its debug infos are already in the cache |
| 1350 | var o_file_di = self.ofiles.get(o_file_path) orelse | 1428 | var o_file_info = self.ofiles.get(o_file_path) orelse |
| 1351 | (self.loadOFile(o_file_path) catch |err| switch (err) { | 1429 | (self.loadOFile(o_file_path) catch |err| switch (err) { |
| 1352 | error.FileNotFound, | 1430 | error.FileNotFound, |
| 1353 | error.MissingDebugInfo, | 1431 | error.MissingDebugInfo, |
| ... | @@ -1357,19 +1435,22 @@ pub const ModuleDebugInfo = switch (native_os) { | ... | @@ -1357,19 +1435,22 @@ pub const ModuleDebugInfo = switch (native_os) { |
| 1357 | }, | 1435 | }, |
| 1358 | else => return err, | 1436 | else => return err, |
| 1359 | }); | 1437 | }); |
| | 1438 | const o_file_di = &o_file_info.di; |
| 1360 | | 1439 | |
| 1361 | // Translate again the address, this time into an address inside the | 1440 | // Translate again the address, this time into an address inside the |
| 1362 | // .o file | 1441 | // .o file |
| 1363 | const relocated_address_o = relocated_address - symbol.reloc; | 1442 | const relocated_address_o = o_file_info.addr_table.get(stab_symbol) orelse return SymbolInfo{ |
| | 1443 | .symbol_name = "???", |
| | 1444 | }; |
| 1364 | | 1445 | |
| 1365 | if (o_file_di.findCompileUnit(relocated_address_o)) |compile_unit| { | 1446 | if (o_file_di.findCompileUnit(relocated_address_o)) |compile_unit| { |
| 1366 | return SymbolInfo{ | 1447 | return SymbolInfo{ |
| 1367 | .symbol_name = o_file_di.getSymbolName(relocated_address_o) orelse "???", | 1448 | .symbol_name = o_file_di.getSymbolName(relocated_address_o) orelse "???", |
| 1368 | .compile_unit_name = compile_unit.die.getAttrString(&o_file_di, DW.AT.name) catch |err| switch (err) { | 1449 | .compile_unit_name = compile_unit.die.getAttrString(o_file_di, DW.AT.name) catch |err| switch (err) { |
| 1369 | error.MissingDebugInfo, error.InvalidDebugInfo => "???", | 1450 | error.MissingDebugInfo, error.InvalidDebugInfo => "???", |
| 1370 | else => return err, | 1451 | else => return err, |
| 1371 | }, | 1452 | }, |
| 1372 | .line_info = o_file_di.getLineNumberInfo(compile_unit.*, relocated_address_o) catch |err| switch (err) { | 1453 | .line_info = o_file_di.getLineNumberInfo(compile_unit.*, relocated_address_o + addr_off) catch |err| switch (err) { |
| 1373 | error.MissingDebugInfo, error.InvalidDebugInfo => null, | 1454 | error.MissingDebugInfo, error.InvalidDebugInfo => null, |
| 1374 | else => return err, | 1455 | else => return err, |
| 1375 | }, | 1456 | }, |