| ... | ... | @@ -271,10 +271,11 @@ fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tt |
| 271 | 271 | const ofile_path = mem.toSliceConst(u8, di.strings.ptr + ofile.n_strx); |
| 272 | 272 | break :blk os.path.basename(ofile_path); |
| 273 | 273 | } else "???"; |
| 274 | | if (getLineNumberInfoMacOs(di, symbol.*, address)) |line_info| { |
| 274 | if (getLineNumberInfoMacOs(di, symbol.*, adjusted_addr)) |line_info| { |
| 275 | defer line_info.deinit(); |
| 275 | 276 | try printLineInfo(di, out_stream, line_info, address, symbol_name, compile_unit_name, tty_color); |
| 276 | 277 | } else |err| switch (err) { |
| 277 | | error.MissingDebugInfo => { |
| 278 | error.MissingDebugInfo, error.InvalidDebugInfo => { |
| 278 | 279 | if (tty_color) { |
| 279 | 280 | try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in {} ({})" ++ RESET ++ "\n\n\n", address, symbol_name, compile_unit_name); |
| 280 | 281 | } else { |
| ... | ... | @@ -427,12 +428,16 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { |
| 427 | 428 | const symbols_buf = try allocator.alloc(MachoSymbol, syms.len); |
| 428 | 429 | |
| 429 | 430 | var ofile: ?*std.c.nlist_64 = null; |
| 431 | var reloc: u64 = 0; |
| 430 | 432 | var symbol_index: usize = 0; |
| 431 | 433 | var last_len: u64 = 0; |
| 432 | 434 | for (syms) |*sym| { |
| 433 | 435 | if (sym.n_type & std.c.N_STAB != 0) { |
| 434 | 436 | switch (sym.n_type) { |
| 435 | | std.c.N_OSO => ofile = sym, |
| 437 | std.c.N_OSO => { |
| 438 | ofile = sym; |
| 439 | reloc = 0; |
| 440 | }, |
| 436 | 441 | std.c.N_FUN => { |
| 437 | 442 | if (sym.n_sect == 0) { |
| 438 | 443 | last_len = sym.n_value; |
| ... | ... | @@ -440,10 +445,16 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { |
| 440 | 445 | symbols_buf[symbol_index] = MachoSymbol{ |
| 441 | 446 | .nlist = sym, |
| 442 | 447 | .ofile = ofile, |
| 448 | .reloc = reloc, |
| 443 | 449 | }; |
| 444 | 450 | symbol_index += 1; |
| 445 | 451 | } |
| 446 | 452 | }, |
| 453 | std.c.N_BNSYM => { |
| 454 | if (reloc == 0) { |
| 455 | reloc = sym.n_value; |
| 456 | } |
| 457 | }, |
| 447 | 458 | else => continue, |
| 448 | 459 | } |
| 449 | 460 | } |
| ... | ... | @@ -506,6 +517,7 @@ fn printLineFromFile(out_stream: var, line_info: *const LineInfo) !void { |
| 506 | 517 | const MachoSymbol = struct { |
| 507 | 518 | nlist: *std.c.nlist_64, |
| 508 | 519 | ofile: ?*std.c.nlist_64, |
| 520 | reloc: u64, |
| 509 | 521 | |
| 510 | 522 | /// Returns the address from the macho file |
| 511 | 523 | fn address(self: MachoSymbol) u64 { |
| ... | ... | @@ -535,6 +547,10 @@ pub const DebugInfo = switch (builtin.os) { |
| 535 | 547 | std.hash_map.getHashPtrAddrFn(*std.c.nlist_64), |
| 536 | 548 | std.hash_map.getTrivialEqlFn(*std.c.nlist_64), |
| 537 | 549 | ); |
| 550 | |
| 551 | pub fn allocator(self: DebugInfo) *mem.Allocator { |
| 552 | return self.ofiles.allocator; |
| 553 | } |
| 538 | 554 | }, |
| 539 | 555 | else => struct { |
| 540 | 556 | self_exe_file: os.File, |
| ... | ... | @@ -965,7 +981,6 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u |
| 965 | 981 | const mach_o_file = if (gop.found_existing) &gop.kv.value else blk: { |
| 966 | 982 | errdefer _ = di.ofiles.remove(ofile); |
| 967 | 983 | const ofile_path = mem.toSliceConst(u8, di.strings.ptr + ofile.n_strx); |
| 968 | | std.debug.warn("reading .o file: {}\n", ofile_path); |
| 969 | 984 | |
| 970 | 985 | gop.kv.value = MachOFile{ |
| 971 | 986 | .bytes = try std.io.readFileAllocAligned(di.ofiles.allocator, ofile_path, @alignOf(std.c.mach_header_64)), |
| ... | ... | @@ -973,7 +988,7 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u |
| 973 | 988 | .sect_debug_line = null, |
| 974 | 989 | }; |
| 975 | 990 | const hdr = @ptrCast(*const std.c.mach_header_64, gop.kv.value.bytes.ptr); |
| 976 | | assert(hdr.magic == std.c.MH_MAGIC_64); |
| 991 | if (hdr.magic != std.c.MH_MAGIC_64) return error.InvalidDebugInfo; |
| 977 | 992 | |
| 978 | 993 | const hdr_base = @ptrCast([*]const u8, hdr); |
| 979 | 994 | var ptr = hdr_base + @sizeOf(std.c.mach_header_64); |
| ... | ... | @@ -998,13 +1013,163 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u |
| 998 | 1013 | } else if (mem.eql(u8, sect_name, "__debug_info")) { |
| 999 | 1014 | gop.kv.value.sect_debug_info = sect; |
| 1000 | 1015 | } |
| 1001 | | std.debug.warn("sect: {}\n", sect_name); |
| 1002 | 1016 | } |
| 1003 | 1017 | } |
| 1004 | 1018 | |
| 1005 | 1019 | break :blk &gop.kv.value; |
| 1006 | 1020 | }; |
| 1007 | 1021 | |
| 1022 | const sect_debug_line = mach_o_file.sect_debug_line orelse return error.MissingDebugInfo; |
| 1023 | var ptr = mach_o_file.bytes.ptr + sect_debug_line.offset; |
| 1024 | |
| 1025 | var is_64: bool = undefined; |
| 1026 | const unit_length = try readInitialLengthMem(&ptr, &is_64); |
| 1027 | if (unit_length == 0) return error.MissingDebugInfo; |
| 1028 | |
| 1029 | const version = readIntMem(&ptr, u16, builtin.Endian.Little); |
| 1030 | // TODO support 3 and 5 |
| 1031 | if (version != 2 and version != 4) return error.InvalidDebugInfo; |
| 1032 | |
| 1033 | const prologue_length = if (is_64) |
| 1034 | readIntMem(&ptr, u64, builtin.Endian.Little) |
| 1035 | else |
| 1036 | readIntMem(&ptr, u32, builtin.Endian.Little); |
| 1037 | const prog_start = ptr + prologue_length; |
| 1038 | |
| 1039 | const minimum_instruction_length = readByteMem(&ptr); |
| 1040 | if (minimum_instruction_length == 0) return error.InvalidDebugInfo; |
| 1041 | |
| 1042 | if (version >= 4) { |
| 1043 | // maximum_operations_per_instruction |
| 1044 | ptr += 1; |
| 1045 | } |
| 1046 | |
| 1047 | const default_is_stmt = readByteMem(&ptr) != 0; |
| 1048 | const line_base = readByteSignedMem(&ptr); |
| 1049 | |
| 1050 | const line_range = readByteMem(&ptr); |
| 1051 | if (line_range == 0) return error.InvalidDebugInfo; |
| 1052 | |
| 1053 | const opcode_base = readByteMem(&ptr); |
| 1054 | |
| 1055 | const standard_opcode_lengths = ptr[0..opcode_base - 1]; |
| 1056 | ptr += opcode_base - 1; |
| 1057 | |
| 1058 | var include_directories = ArrayList([]const u8).init(di.allocator()); |
| 1059 | //try include_directories.append(compile_unit_cwd); |
| 1060 | try include_directories.append("./"); |
| 1061 | while (true) { |
| 1062 | const dir = readStringMem(&ptr); |
| 1063 | if (dir.len == 0) break; |
| 1064 | try include_directories.append(dir); |
| 1065 | } |
| 1066 | |
| 1067 | var file_entries = ArrayList(FileEntry).init(di.allocator()); |
| 1068 | var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address); |
| 1069 | |
| 1070 | while (true) { |
| 1071 | const file_name = readStringMem(&ptr); |
| 1072 | if (file_name.len == 0) break; |
| 1073 | const dir_index = try readULeb128Mem(&ptr); |
| 1074 | const mtime = try readULeb128Mem(&ptr); |
| 1075 | const len_bytes = try readULeb128Mem(&ptr); |
| 1076 | try file_entries.append(FileEntry{ |
| 1077 | .file_name = file_name, |
| 1078 | .dir_index = dir_index, |
| 1079 | .mtime = mtime, |
| 1080 | .len_bytes = len_bytes, |
| 1081 | }); |
| 1082 | } |
| 1083 | |
| 1084 | ptr = prog_start; |
| 1085 | while (true) { |
| 1086 | const opcode = readByteMem(&ptr); |
| 1087 | |
| 1088 | if (opcode == DW.LNS_extended_op) { |
| 1089 | const op_size = try readULeb128Mem(&ptr); |
| 1090 | if (op_size < 1) return error.InvalidDebugInfo; |
| 1091 | var sub_op = readByteMem(&ptr); |
| 1092 | switch (sub_op) { |
| 1093 | DW.LNE_end_sequence => { |
| 1094 | prog.end_sequence = true; |
| 1095 | if (try prog.checkLineMatch()) |info| return info; |
| 1096 | return error.MissingDebugInfo; |
| 1097 | }, |
| 1098 | DW.LNE_set_address => { |
| 1099 | const addr = readIntMem(&ptr, usize, builtin.Endian.Little); |
| 1100 | prog.address = symbol.reloc + addr; |
| 1101 | }, |
| 1102 | DW.LNE_define_file => { |
| 1103 | const file_name = readStringMem(&ptr); |
| 1104 | const dir_index = try readULeb128Mem(&ptr); |
| 1105 | const mtime = try readULeb128Mem(&ptr); |
| 1106 | const len_bytes = try readULeb128Mem(&ptr); |
| 1107 | try file_entries.append(FileEntry{ |
| 1108 | .file_name = file_name, |
| 1109 | .dir_index = dir_index, |
| 1110 | .mtime = mtime, |
| 1111 | .len_bytes = len_bytes, |
| 1112 | }); |
| 1113 | }, |
| 1114 | else => { |
| 1115 | ptr += op_size - 1; |
| 1116 | }, |
| 1117 | } |
| 1118 | } else if (opcode >= opcode_base) { |
| 1119 | // special opcodes |
| 1120 | const adjusted_opcode = opcode - opcode_base; |
| 1121 | const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range); |
| 1122 | const inc_line = i32(line_base) + i32(adjusted_opcode % line_range); |
| 1123 | prog.line += inc_line; |
| 1124 | prog.address += inc_addr; |
| 1125 | if (try prog.checkLineMatch()) |info| return info; |
| 1126 | prog.basic_block = false; |
| 1127 | } else { |
| 1128 | switch (opcode) { |
| 1129 | DW.LNS_copy => { |
| 1130 | if (try prog.checkLineMatch()) |info| return info; |
| 1131 | prog.basic_block = false; |
| 1132 | }, |
| 1133 | DW.LNS_advance_pc => { |
| 1134 | const arg = try readULeb128Mem(&ptr); |
| 1135 | prog.address += arg * minimum_instruction_length; |
| 1136 | }, |
| 1137 | DW.LNS_advance_line => { |
| 1138 | const arg = try readILeb128Mem(&ptr); |
| 1139 | prog.line += arg; |
| 1140 | }, |
| 1141 | DW.LNS_set_file => { |
| 1142 | const arg = try readULeb128Mem(&ptr); |
| 1143 | prog.file = arg; |
| 1144 | }, |
| 1145 | DW.LNS_set_column => { |
| 1146 | const arg = try readULeb128Mem(&ptr); |
| 1147 | prog.column = arg; |
| 1148 | }, |
| 1149 | DW.LNS_negate_stmt => { |
| 1150 | prog.is_stmt = !prog.is_stmt; |
| 1151 | }, |
| 1152 | DW.LNS_set_basic_block => { |
| 1153 | prog.basic_block = true; |
| 1154 | }, |
| 1155 | DW.LNS_const_add_pc => { |
| 1156 | const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range); |
| 1157 | prog.address += inc_addr; |
| 1158 | }, |
| 1159 | DW.LNS_fixed_advance_pc => { |
| 1160 | const arg = readIntMem(&ptr, u16, builtin.Endian.Little); |
| 1161 | prog.address += arg; |
| 1162 | }, |
| 1163 | DW.LNS_set_prologue_end => {}, |
| 1164 | else => { |
| 1165 | if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo; |
| 1166 | const len_bytes = standard_opcode_lengths[opcode - 1]; |
| 1167 | ptr += len_bytes; |
| 1168 | }, |
| 1169 | } |
| 1170 | } |
| 1171 | } |
| 1172 | |
| 1008 | 1173 | return error.MissingDebugInfo; |
| 1009 | 1174 | } |
| 1010 | 1175 | |
| ... | ... | @@ -1094,11 +1259,10 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ |
| 1094 | 1259 | while (true) { |
| 1095 | 1260 | const opcode = try in_stream.readByte(); |
| 1096 | 1261 | |
| 1097 | | var sub_op: u8 = undefined; // TODO move this to the correct scope and fix the compiler crash |
| 1098 | 1262 | if (opcode == DW.LNS_extended_op) { |
| 1099 | 1263 | const op_size = try readULeb128(in_stream); |
| 1100 | 1264 | if (op_size < 1) return error.InvalidDebugInfo; |
| 1101 | | sub_op = try in_stream.readByte(); |
| 1265 | var sub_op = try in_stream.readByte(); |
| 1102 | 1266 | switch (sub_op) { |
| 1103 | 1267 | DW.LNE_end_sequence => { |
| 1104 | 1268 | prog.end_sequence = true; |
| ... | ... | @@ -1291,6 +1455,89 @@ fn findCompileUnit(st: *DebugInfo, target_address: u64) !*const CompileUnit { |
| 1291 | 1455 | return error.MissingDebugInfo; |
| 1292 | 1456 | } |
| 1293 | 1457 | |
| 1458 | fn readIntMem(ptr: *[*]const u8, comptime T: type, endian: builtin.Endian) T { |
| 1459 | const result = mem.readInt(ptr.*[0..@sizeOf(T)], T, endian); |
| 1460 | ptr.* += @sizeOf(T); |
| 1461 | return result; |
| 1462 | } |
| 1463 | |
| 1464 | fn readByteMem(ptr: *[*]const u8) u8 { |
| 1465 | const result = ptr.*[0]; |
| 1466 | ptr.* += 1; |
| 1467 | return result; |
| 1468 | } |
| 1469 | |
| 1470 | fn readByteSignedMem(ptr: *[*]const u8) i8 { |
| 1471 | return @bitCast(i8, readByteMem(ptr)); |
| 1472 | } |
| 1473 | |
| 1474 | fn readInitialLengthMem(ptr: *[*]const u8, is_64: *bool) !u64 { |
| 1475 | const first_32_bits = mem.readIntLE(u32, ptr.*[0..4]); |
| 1476 | is_64.* = (first_32_bits == 0xffffffff); |
| 1477 | if (is_64.*) { |
| 1478 | ptr.* += 4; |
| 1479 | const result = mem.readIntLE(u64, ptr.*[0..8]); |
| 1480 | ptr.* += 8; |
| 1481 | return result; |
| 1482 | } else { |
| 1483 | if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo; |
| 1484 | ptr.* += 4; |
| 1485 | return u64(first_32_bits); |
| 1486 | } |
| 1487 | } |
| 1488 | |
| 1489 | fn readStringMem(ptr: *[*]const u8) []const u8 { |
| 1490 | const result = mem.toSliceConst(u8, ptr.*); |
| 1491 | ptr.* += result.len + 1; |
| 1492 | return result; |
| 1493 | } |
| 1494 | |
| 1495 | fn readULeb128Mem(ptr: *[*]const u8) !u64 { |
| 1496 | var result: u64 = 0; |
| 1497 | var shift: usize = 0; |
| 1498 | var i: usize = 0; |
| 1499 | |
| 1500 | while (true) { |
| 1501 | const byte = ptr.*[i]; |
| 1502 | i += 1; |
| 1503 | |
| 1504 | var operand: u64 = undefined; |
| 1505 | |
| 1506 | if (@shlWithOverflow(u64, byte & 0b01111111, @intCast(u6, shift), &operand)) return error.InvalidDebugInfo; |
| 1507 | |
| 1508 | result |= operand; |
| 1509 | |
| 1510 | if ((byte & 0b10000000) == 0) { |
| 1511 | ptr.* += i; |
| 1512 | return result; |
| 1513 | } |
| 1514 | |
| 1515 | shift += 7; |
| 1516 | } |
| 1517 | } |
| 1518 | fn readILeb128Mem(ptr: *[*]const u8) !i64 { |
| 1519 | var result: i64 = 0; |
| 1520 | var shift: usize = 0; |
| 1521 | var i: usize = 0; |
| 1522 | |
| 1523 | while (true) { |
| 1524 | const byte = ptr.*[i]; |
| 1525 | i += 1; |
| 1526 | |
| 1527 | var operand: i64 = undefined; |
| 1528 | if (@shlWithOverflow(i64, byte & 0b01111111, @intCast(u6, shift), &operand)) return error.InvalidDebugInfo; |
| 1529 | |
| 1530 | result |= operand; |
| 1531 | shift += 7; |
| 1532 | |
| 1533 | if ((byte & 0b10000000) == 0) { |
| 1534 | if (shift < @sizeOf(i64) * 8 and (byte & 0b01000000) != 0) result |= -(i64(1) << @intCast(u6, shift)); |
| 1535 | ptr.* += i; |
| 1536 | return result; |
| 1537 | } |
| 1538 | } |
| 1539 | } |
| 1540 | |
| 1294 | 1541 | fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 { |
| 1295 | 1542 | const first_32_bits = try in_stream.readIntLe(u32); |
| 1296 | 1543 | is_64.* = (first_32_bits == 0xffffffff); |