| ... | ... | @@ -700,7 +700,8 @@ fn printSourceAtAddressMacOs(di1: *DebugInfo, out_stream: var, address: usize, t |
| 700 | 700 | const di = try di1.lookupByAddress(address); |
| 701 | 701 | |
| 702 | 702 | const base_addr = di.base_address; |
| 703 | | const adjusted_addr = 0x100000000 + (address - base_addr); |
| 703 | const adjusted_addr = address - base_addr; |
| 704 | assert(adjusted_addr >= 0x100000000); |
| 704 | 705 | |
| 705 | 706 | const symbol = machoSearchSymbols(di.symbols, adjusted_addr) orelse { |
| 706 | 707 | return printLineInfo(out_stream, null, address, "???", "???", tty_config, printLineFromFileAnyOs); |
| ... | ... | @@ -734,7 +735,6 @@ pub fn printSourceAtAddressPosix(debug_info: *DebugInfo, out_stream: var, addres |
| 734 | 735 | const module = try debug_info.lookupByAddress(address); |
| 735 | 736 | |
| 736 | 737 | const reloc_address = address - module.base_address; |
| 737 | | warn("reloc {x} => {x}\n", .{ address, reloc_address }); |
| 738 | 738 | |
| 739 | 739 | if (module.dwarf.findCompileUnit(reloc_address) catch null) |compile_unit| { |
| 740 | 740 | const compile_unit_name = try compile_unit.die.getAttrString(&module.dwarf, DW.AT_name); |
| ... | ... | @@ -1078,29 +1078,33 @@ fn openSelfDebugInfoPosix(allocator: *mem.Allocator) !DW.DwarfInfo { |
| 1078 | 1078 | } |
| 1079 | 1079 | |
| 1080 | 1080 | /// TODO resources https://github.com/ziglang/zig/issues/4353 |
| 1081 | | fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { |
| 1082 | | const hdr = &std.c._mh_execute_header; |
| 1081 | fn openMachODebugInfo(allocator: *mem.Allocator, memmo: []const u8) !ObjectDebugInfo { |
| 1082 | // const hdr = &std.c._mh_execute_header; |
| 1083 | const hdr = @ptrCast( |
| 1084 | *const macho.mach_header_64, |
| 1085 | @alignCast(@alignOf(macho.mach_header_64), memmo.ptr), |
| 1086 | ); |
| 1083 | 1087 | assert(hdr.magic == std.macho.MH_MAGIC_64); |
| 1084 | 1088 | |
| 1085 | | const hdr_base = @ptrCast([*]u8, hdr); |
| 1089 | const hdr_base = @ptrCast([*]const u8, hdr); |
| 1086 | 1090 | var ptr = hdr_base + @sizeOf(macho.mach_header_64); |
| 1087 | 1091 | var ncmd: u32 = hdr.ncmds; |
| 1088 | 1092 | const symtab = while (ncmd != 0) : (ncmd -= 1) { |
| 1089 | | const lc = @ptrCast(*std.macho.load_command, ptr); |
| 1093 | const lc = @ptrCast(*const std.macho.load_command, ptr); |
| 1090 | 1094 | switch (lc.cmd) { |
| 1091 | | std.macho.LC_SYMTAB => break @ptrCast(*std.macho.symtab_command, ptr), |
| 1095 | std.macho.LC_SYMTAB => break @ptrCast(*const std.macho.symtab_command, ptr), |
| 1092 | 1096 | else => {}, |
| 1093 | 1097 | } |
| 1094 | 1098 | ptr = @alignCast(@alignOf(std.macho.load_command), ptr + lc.cmdsize); |
| 1095 | 1099 | } else { |
| 1096 | 1100 | return error.MissingDebugInfo; |
| 1097 | 1101 | }; |
| 1098 | | const syms = @ptrCast([*]macho.nlist_64, @alignCast(@alignOf(macho.nlist_64), hdr_base + symtab.symoff))[0..symtab.nsyms]; |
| 1099 | | const strings = @ptrCast([*]u8, hdr_base + symtab.stroff)[0..symtab.strsize]; |
| 1102 | const syms = @ptrCast([*]const macho.nlist_64, @alignCast(@alignOf(macho.nlist_64), hdr_base + symtab.symoff))[0..symtab.nsyms]; |
| 1103 | const strings = @ptrCast([*]const u8, hdr_base + symtab.stroff)[0..symtab.strsize]; |
| 1100 | 1104 | |
| 1101 | 1105 | const symbols_buf = try allocator.alloc(MachoSymbol, syms.len); |
| 1102 | 1106 | |
| 1103 | | var ofile: ?*macho.nlist_64 = null; |
| 1107 | var ofile: ?*const macho.nlist_64 = null; |
| 1104 | 1108 | var reloc: u64 = 0; |
| 1105 | 1109 | var symbol_index: usize = 0; |
| 1106 | 1110 | var last_len: u64 = 0; |
| ... | ... | @@ -1148,8 +1152,10 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { |
| 1148 | 1152 | // This sort is so that we can binary search later. |
| 1149 | 1153 | std.sort.sort(MachoSymbol, symbols, MachoSymbol.addressLessThan); |
| 1150 | 1154 | |
| 1151 | | return DebugInfo{ |
| 1152 | | .ofiles = DebugInfo.OFileTable.init(allocator), |
| 1155 | return ObjectDebugInfo{ |
| 1156 | .base_address = undefined, |
| 1157 | .mapped_memory = undefined, |
| 1158 | .ofiles = ObjectDebugInfo.OFileTable.init(allocator), |
| 1153 | 1159 | .symbols = symbols, |
| 1154 | 1160 | .strings = strings, |
| 1155 | 1161 | }; |
| ... | ... | @@ -1188,8 +1194,8 @@ fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void { |
| 1188 | 1194 | } |
| 1189 | 1195 | |
| 1190 | 1196 | const MachoSymbol = struct { |
| 1191 | | nlist: *macho.nlist_64, |
| 1192 | | ofile: ?*macho.nlist_64, |
| 1197 | nlist: *const macho.nlist_64, |
| 1198 | ofile: ?*const macho.nlist_64, |
| 1193 | 1199 | reloc: u64, |
| 1194 | 1200 | |
| 1195 | 1201 | /// Returns the address from the macho file |
| ... | ... | @@ -1233,18 +1239,68 @@ pub const DebugInfo = struct { |
| 1233 | 1239 | |
| 1234 | 1240 | var i: u32 = 0; |
| 1235 | 1241 | while (i < image_count) : (i += 1) { |
| 1242 | const base_address = std.c._dyld_get_image_vmaddr_slide(i); |
| 1243 | |
| 1244 | if (address < base_address) continue; |
| 1245 | |
| 1236 | 1246 | const header = std.c._dyld_get_image_header(i) orelse continue; |
| 1237 | 1247 | // The array of load commands is right after the header |
| 1238 | 1248 | var cmd_ptr = @intToPtr([*]u8, @ptrToInt(header) + @sizeOf(macho.mach_header_64)); |
| 1239 | | const cmd_ptr_end = cmd_ptr + header.sizeofcmds; |
| 1240 | 1249 | |
| 1241 | | var cmd = |
| 1242 | | for (cmds) |*lc| { |
| 1250 | var cmds = header.ncmds; |
| 1251 | while (cmds != 0) : (cmds -= 1) { |
| 1252 | const lc = @ptrCast( |
| 1253 | *macho.load_command, |
| 1254 | @alignCast(@alignOf(macho.load_command), cmd_ptr), |
| 1255 | ); |
| 1256 | cmd_ptr += lc.cmdsize; |
| 1243 | 1257 | if (lc.cmd != macho.LC_SEGMENT_64) continue; |
| 1244 | 1258 | |
| 1245 | | const segment_cmd = @ptrCast(*macho.segment_command_64, lc); |
| 1259 | const segment_cmd = @ptrCast( |
| 1260 | *const std.macho.segment_command_64, |
| 1261 | @alignCast(@alignOf(std.macho.segment_command_64), lc), |
| 1262 | ); |
| 1263 | |
| 1264 | const rebased_address = address - base_address; |
| 1265 | const seg_start = segment_cmd.vmaddr; |
| 1266 | const seg_end = seg_start + segment_cmd.vmsize; |
| 1267 | |
| 1268 | if (rebased_address >= seg_start and rebased_address < seg_end) { |
| 1269 | if (self.address_map.getValue(base_address)) |obj_di| { |
| 1270 | return obj_di; |
| 1271 | } |
| 1272 | |
| 1273 | const image_name = std.c._dyld_get_image_name(i); |
| 1274 | const exe_file = try fs.openFileAbsoluteC(image_name, .{}); |
| 1275 | errdefer exe_file.close(); |
| 1276 | |
| 1277 | const exe_len = math.cast(usize, try exe_file.getEndPos()) catch |
| 1278 | return error.DebugInfoTooLarge; |
| 1279 | const exe_mmap = try os.mmap( |
| 1280 | null, |
| 1281 | exe_len, |
| 1282 | os.PROT_READ, |
| 1283 | os.MAP_SHARED, |
| 1284 | exe_file.handle, |
| 1285 | 0, |
| 1286 | ); |
| 1287 | errdefer os.munmap(exe_mmap); |
| 1288 | |
| 1289 | const obj_di = try self.allocator.create(ObjectDebugInfo); |
| 1290 | errdefer self.allocator.destroy(obj_di); |
| 1291 | |
| 1292 | try self.address_map.putNoClobber(base_address, obj_di); |
| 1293 | |
| 1294 | obj_di.* = try openMachODebugInfo(self.allocator, exe_mmap); |
| 1295 | obj_di.base_address = base_address; |
| 1296 | obj_di.mapped_memory = exe_mmap; |
| 1297 | |
| 1298 | return obj_di; |
| 1299 | } |
| 1246 | 1300 | } |
| 1247 | 1301 | } |
| 1302 | |
| 1303 | return error.DebugInfoNotFound; |
| 1248 | 1304 | } |
| 1249 | 1305 | |
| 1250 | 1306 | fn lookupModuleWin32(self: *DebugInfo, address: usize) !*ObjectDebugInfo { |
| ... | ... | @@ -1252,7 +1308,8 @@ pub const DebugInfo = struct { |
| 1252 | 1308 | |
| 1253 | 1309 | var modules: [32]windows.HMODULE = undefined; |
| 1254 | 1310 | var modules_needed: windows.DWORD = undefined; |
| 1255 | | // XXX Ask for the number of modules by passing size zero |
| 1311 | // TODO: Ask for the number of modules by passing size zero, 32 ought to |
| 1312 | // be enough for everyone in the meanwhile |
| 1256 | 1313 | if (windows.kernel32.K32EnumProcessModules( |
| 1257 | 1314 | process_handle, |
| 1258 | 1315 | @ptrCast([*]windows.HMODULE, &modules), |
| ... | ... | @@ -1275,6 +1332,10 @@ pub const DebugInfo = struct { |
| 1275 | 1332 | const seg_end = seg_start + info.SizeOfImage; |
| 1276 | 1333 | |
| 1277 | 1334 | if (address >= seg_start and address < seg_end) { |
| 1335 | if (self.address_map.getValue(seg_start)) |obj_di| { |
| 1336 | return obj_di; |
| 1337 | } |
| 1338 | |
| 1278 | 1339 | var name_buffer: [windows.PATH_MAX_WIDE + 4:0]u16 = undefined; |
| 1279 | 1340 | // openFileAbsoluteW requires the prefix to be present |
| 1280 | 1341 | mem.copy(u16, name_buffer[0..4], &[_]u16{ '\\', '?', '?', '\\' }); |
| ... | ... | @@ -1286,12 +1347,8 @@ pub const DebugInfo = struct { |
| 1286 | 1347 | ); |
| 1287 | 1348 | assert(len > 0); |
| 1288 | 1349 | |
| 1289 | | if (self.address_map.getValue(seg_start)) |obj_di| { |
| 1290 | | return obj_di; |
| 1291 | | } |
| 1292 | | |
| 1293 | | // XXX: The compiler segfaults if the slicing is done as a |
| 1294 | | // parameter (#4423) |
| 1350 | // The compiler segfaults if the slicing is done as a parameter |
| 1351 | // (#4423) |
| 1295 | 1352 | const tmp = name_buffer[0..:0]; |
| 1296 | 1353 | const file_obj = try fs.openFileAbsoluteW(tmp, .{}); |
| 1297 | 1354 | errdefer file_obj.close(); |
| ... | ... | @@ -1312,11 +1369,49 @@ pub const DebugInfo = struct { |
| 1312 | 1369 | } |
| 1313 | 1370 | |
| 1314 | 1371 | fn lookupModuleDl(self: *DebugInfo, address: usize) !*ObjectDebugInfo { |
| 1315 | | var ctx = DIPContext{ .address = address }; |
| 1372 | var ctx: struct { |
| 1373 | // Input |
| 1374 | address: usize, |
| 1375 | // Output |
| 1376 | base_address: usize = undefined, |
| 1377 | name: []const u8 = undefined, |
| 1378 | } = .{ .address = address }; |
| 1379 | const CtxTy = @TypeOf(ctx); |
| 1380 | |
| 1381 | if (os.dl_iterate_phdr(&ctx, anyerror, struct { |
| 1382 | fn callback(info: *os.dl_phdr_info, size: usize, context: *CtxTy) !void { |
| 1383 | // The base address is too high |
| 1384 | if (context.address < info.dlpi_addr) |
| 1385 | return; |
| 1316 | 1386 | |
| 1317 | | // XXX Locking? |
| 1318 | | if (os.dl_iterate_phdr(DIPContext, dl_iterate_phdr_callback, &ctx) == 0) |
| 1387 | const phdrs = info.dlpi_phdr[0..info.dlpi_phnum]; |
| 1388 | for (phdrs) |*phdr| { |
| 1389 | if (phdr.p_type != elf.PT_LOAD) continue; |
| 1390 | |
| 1391 | const seg_start = info.dlpi_addr + phdr.p_vaddr; |
| 1392 | const seg_end = seg_start + phdr.p_memsz; |
| 1393 | |
| 1394 | if (context.address >= seg_start and context.address < seg_end) { |
| 1395 | // Android libc uses NULL instead of an empty string to mark the |
| 1396 | // main program |
| 1397 | context.name = if (info.dlpi_name) |dlpi_name| |
| 1398 | mem.toSliceConst(u8, dlpi_name) |
| 1399 | else |
| 1400 | ""; |
| 1401 | context.base_address = info.dlpi_addr; |
| 1402 | // Stop the iteration |
| 1403 | return error.Found; |
| 1404 | } |
| 1405 | } |
| 1406 | } |
| 1407 | }.callback)) { |
| 1319 | 1408 | return error.DebugInfoNotFound; |
| 1409 | } else |err| { |
| 1410 | switch (err) { |
| 1411 | error.Found => {}, |
| 1412 | else => return error.DebugInfoNotFound, |
| 1413 | } |
| 1414 | } |
| 1320 | 1415 | |
| 1321 | 1416 | if (self.address_map.getValue(ctx.base_address)) |obj_di| { |
| 1322 | 1417 | return obj_di; |
| ... | ... | @@ -1355,58 +1450,24 @@ pub const DebugInfo = struct { |
| 1355 | 1450 | } |
| 1356 | 1451 | }; |
| 1357 | 1452 | |
| 1358 | | const DIPContext = struct { |
| 1359 | | address: usize, |
| 1360 | | base_address: usize = undefined, |
| 1361 | | name: []const u8 = undefined, |
| 1362 | | }; |
| 1363 | | |
| 1364 | | fn dl_iterate_phdr_callback(info: *os.dl_phdr_info, size: usize, context: ?*DIPContext) callconv(.C) i32 { |
| 1365 | | const address = context.?.address; |
| 1366 | | |
| 1367 | | // The base address is too high |
| 1368 | | if (address < info.dlpi_addr) |
| 1369 | | return 0; |
| 1370 | | |
| 1371 | | const phdrs = info.dlpi_phdr[0..info.dlpi_phnum]; |
| 1372 | | for (phdrs) |*phdr| { |
| 1373 | | if (phdr.p_type != elf.PT_LOAD) continue; |
| 1374 | | |
| 1375 | | const seg_start = info.dlpi_addr + phdr.p_vaddr; |
| 1376 | | const seg_end = seg_start + phdr.p_memsz; |
| 1377 | | |
| 1378 | | if (address >= seg_start and address < seg_end) { |
| 1379 | | // Android libc uses NULL instead of an empty string to mark the |
| 1380 | | // main program |
| 1381 | | context.?.name = if (info.dlpi_name) |dlpi_name| |
| 1382 | | mem.toSliceConst(u8, dlpi_name) |
| 1383 | | else |
| 1384 | | ""; |
| 1385 | | context.?.base_address = info.dlpi_addr; |
| 1386 | | // Stop the iteration |
| 1387 | | return 1; |
| 1388 | | } |
| 1389 | | } |
| 1390 | | |
| 1391 | | // Continue the iteration |
| 1392 | | return 0; |
| 1393 | | } |
| 1453 | const DIPContext = struct {}; |
| 1394 | 1454 | |
| 1395 | 1455 | pub const ObjectDebugInfo = switch (builtin.os) { |
| 1396 | 1456 | .macosx, .ios, .watchos, .tvos => struct { |
| 1397 | 1457 | base_address: usize, |
| 1458 | mapped_memory: []u8, |
| 1398 | 1459 | symbols: []const MachoSymbol, |
| 1399 | 1460 | strings: []const u8, |
| 1400 | 1461 | ofiles: OFileTable, |
| 1401 | 1462 | |
| 1402 | 1463 | const OFileTable = std.HashMap( |
| 1403 | | *macho.nlist_64, |
| 1464 | *const macho.nlist_64, |
| 1404 | 1465 | DW.DwarfInfo, |
| 1405 | | std.hash_map.getHashPtrAddrFn(*macho.nlist_64), |
| 1406 | | std.hash_map.getTrivialEqlFn(*macho.nlist_64), |
| 1466 | std.hash_map.getHashPtrAddrFn(*const macho.nlist_64), |
| 1467 | std.hash_map.getTrivialEqlFn(*const macho.nlist_64), |
| 1407 | 1468 | ); |
| 1408 | 1469 | |
| 1409 | | pub fn allocator(self: DebugInfo) *mem.Allocator { |
| 1470 | pub fn allocator(self: @This()) *mem.Allocator { |
| 1410 | 1471 | return self.ofiles.allocator; |
| 1411 | 1472 | } |
| 1412 | 1473 | }, |
| ... | ... | @@ -1426,7 +1487,7 @@ pub const ObjectDebugInfo = switch (builtin.os) { |
| 1426 | 1487 | }; |
| 1427 | 1488 | |
| 1428 | 1489 | /// TODO resources https://github.com/ziglang/zig/issues/4353 |
| 1429 | | fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, address: usize) !LineInfo { |
| 1490 | fn getLineNumberInfoMacOs(di: *ObjectDebugInfo, symbol: MachoSymbol, address: usize) !LineInfo { |
| 1430 | 1491 | const ofile = symbol.ofile orelse return error.MissingDebugInfo; |
| 1431 | 1492 | const gop = try di.ofiles.getOrPut(ofile); |
| 1432 | 1493 | const dwarf_info = if (gop.found_existing) &gop.kv.value else blk: { |