| ... | @@ -271,10 +271,11 @@ fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tt | ... | @@ -271,10 +271,11 @@ fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tt |
| 271 | const ofile_path = mem.toSliceConst(u8, di.strings.ptr + ofile.n_strx); | 271 | const ofile_path = mem.toSliceConst(u8, di.strings.ptr + ofile.n_strx); |
| 272 | break :blk os.path.basename(ofile_path); | 272 | break :blk os.path.basename(ofile_path); |
| 273 | } else "???"; | 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 | try printLineInfo(di, out_stream, line_info, address, symbol_name, compile_unit_name, tty_color); | 276 | try printLineInfo(di, out_stream, line_info, address, symbol_name, compile_unit_name, tty_color); |
| 276 | } else |err| switch (err) { | 277 | } else |err| switch (err) { |
| 277 | error.MissingDebugInfo => { | 278 | error.MissingDebugInfo, error.InvalidDebugInfo => { |
| 278 | if (tty_color) { | 279 | if (tty_color) { |
| 279 | try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in {} ({})" ++ RESET ++ "\n\n\n", address, symbol_name, compile_unit_name); | 280 | try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in {} ({})" ++ RESET ++ "\n\n\n", address, symbol_name, compile_unit_name); |
| 280 | } else { | 281 | } else { |
| ... | @@ -427,12 +428,16 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { | ... | @@ -427,12 +428,16 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { |
| 427 | const symbols_buf = try allocator.alloc(MachoSymbol, syms.len); | 428 | const symbols_buf = try allocator.alloc(MachoSymbol, syms.len); |
| 428 | | 429 | |
| 429 | var ofile: ?*std.c.nlist_64 = null; | 430 | var ofile: ?*std.c.nlist_64 = null; |
| | 431 | var reloc: u64 = 0; |
| 430 | var symbol_index: usize = 0; | 432 | var symbol_index: usize = 0; |
| 431 | var last_len: u64 = 0; | 433 | var last_len: u64 = 0; |
| 432 | for (syms) |*sym| { | 434 | for (syms) |*sym| { |
| 433 | if (sym.n_type & std.c.N_STAB != 0) { | 435 | if (sym.n_type & std.c.N_STAB != 0) { |
| 434 | switch (sym.n_type) { | 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 | std.c.N_FUN => { | 441 | std.c.N_FUN => { |
| 437 | if (sym.n_sect == 0) { | 442 | if (sym.n_sect == 0) { |
| 438 | last_len = sym.n_value; | 443 | last_len = sym.n_value; |
| ... | @@ -440,10 +445,16 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { | ... | @@ -440,10 +445,16 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { |
| 440 | symbols_buf[symbol_index] = MachoSymbol{ | 445 | symbols_buf[symbol_index] = MachoSymbol{ |
| 441 | .nlist = sym, | 446 | .nlist = sym, |
| 442 | .ofile = ofile, | 447 | .ofile = ofile, |
| | 448 | .reloc = reloc, |
| 443 | }; | 449 | }; |
| 444 | symbol_index += 1; | 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 | else => continue, | 458 | else => continue, |
| 448 | } | 459 | } |
| 449 | } | 460 | } |
| ... | @@ -506,6 +517,7 @@ fn printLineFromFile(out_stream: var, line_info: *const LineInfo) !void { | ... | @@ -506,6 +517,7 @@ fn printLineFromFile(out_stream: var, line_info: *const LineInfo) !void { |
| 506 | const MachoSymbol = struct { | 517 | const MachoSymbol = struct { |
| 507 | nlist: *std.c.nlist_64, | 518 | nlist: *std.c.nlist_64, |
| 508 | ofile: ?*std.c.nlist_64, | 519 | ofile: ?*std.c.nlist_64, |
| | 520 | reloc: u64, |
| 509 | | 521 | |
| 510 | /// Returns the address from the macho file | 522 | /// Returns the address from the macho file |
| 511 | fn address(self: MachoSymbol) u64 { | 523 | fn address(self: MachoSymbol) u64 { |
| ... | @@ -535,6 +547,10 @@ pub const DebugInfo = switch (builtin.os) { | ... | @@ -535,6 +547,10 @@ pub const DebugInfo = switch (builtin.os) { |
| 535 | std.hash_map.getHashPtrAddrFn(*std.c.nlist_64), | 547 | std.hash_map.getHashPtrAddrFn(*std.c.nlist_64), |
| 536 | std.hash_map.getTrivialEqlFn(*std.c.nlist_64), | 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 | else => struct { | 555 | else => struct { |
| 540 | self_exe_file: os.File, | 556 | self_exe_file: os.File, |
| ... | @@ -965,7 +981,6 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u | ... | @@ -965,7 +981,6 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u |
| 965 | const mach_o_file = if (gop.found_existing) &gop.kv.value else blk: { | 981 | const mach_o_file = if (gop.found_existing) &gop.kv.value else blk: { |
| 966 | errdefer _ = di.ofiles.remove(ofile); | 982 | errdefer _ = di.ofiles.remove(ofile); |
| 967 | const ofile_path = mem.toSliceConst(u8, di.strings.ptr + ofile.n_strx); | 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 | gop.kv.value = MachOFile{ | 985 | gop.kv.value = MachOFile{ |
| 971 | .bytes = try std.io.readFileAllocAligned(di.ofiles.allocator, ofile_path, @alignOf(std.c.mach_header_64)), | 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,7 +988,7 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u |
| 973 | .sect_debug_line = null, | 988 | .sect_debug_line = null, |
| 974 | }; | 989 | }; |
| 975 | const hdr = @ptrCast(*const std.c.mach_header_64, gop.kv.value.bytes.ptr); | 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 | const hdr_base = @ptrCast([*]const u8, hdr); | 993 | const hdr_base = @ptrCast([*]const u8, hdr); |
| 979 | var ptr = hdr_base + @sizeOf(std.c.mach_header_64); | 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,13 +1013,163 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u |
| 998 | } else if (mem.eql(u8, sect_name, "__debug_info")) { | 1013 | } else if (mem.eql(u8, sect_name, "__debug_info")) { |
| 999 | gop.kv.value.sect_debug_info = sect; | 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 | break :blk &gop.kv.value; | 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 | return error.MissingDebugInfo; | 1173 | return error.MissingDebugInfo; |
| 1009 | } | 1174 | } |
| 1010 | | 1175 | |
| ... | @@ -1094,11 +1259,10 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ | ... | @@ -1094,11 +1259,10 @@ fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, targ |
| 1094 | while (true) { | 1259 | while (true) { |
| 1095 | const opcode = try in_stream.readByte(); | 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 | if (opcode == DW.LNS_extended_op) { | 1262 | if (opcode == DW.LNS_extended_op) { |
| 1099 | const op_size = try readULeb128(in_stream); | 1263 | const op_size = try readULeb128(in_stream); |
| 1100 | if (op_size < 1) return error.InvalidDebugInfo; | 1264 | if (op_size < 1) return error.InvalidDebugInfo; |
| 1101 | sub_op = try in_stream.readByte(); | 1265 | var sub_op = try in_stream.readByte(); |
| 1102 | switch (sub_op) { | 1266 | switch (sub_op) { |
| 1103 | DW.LNE_end_sequence => { | 1267 | DW.LNE_end_sequence => { |
| 1104 | prog.end_sequence = true; | 1268 | prog.end_sequence = true; |
| ... | @@ -1291,6 +1455,89 @@ fn findCompileUnit(st: *DebugInfo, target_address: u64) !*const CompileUnit { | ... | @@ -1291,6 +1455,89 @@ fn findCompileUnit(st: *DebugInfo, target_address: u64) !*const CompileUnit { |
| 1291 | return error.MissingDebugInfo; | 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 | fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 { | 1541 | fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 { |
| 1295 | const first_32_bits = try in_stream.readIntLe(u32); | 1542 | const first_32_bits = try in_stream.readIntLe(u32); |
| 1296 | is_64.* = (first_32_bits == 0xffffffff); | 1543 | is_64.* = (first_32_bits == 0xffffffff); |