| ... | ... | @@ -299,30 +299,7 @@ const Die = struct { |
| 299 | 299 | const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; |
| 300 | 300 | return switch (form_value.*) { |
| 301 | 301 | FormValue.Address => |value| value, |
| 302 | | FormValue.AddrOffset => |index| { |
| 303 | | const debug_addr = di.debug_addr orelse return badDwarf(); |
| 304 | | // addr_base points to the first item after the header, however we |
| 305 | | // need to read the header to know the size of each item. Empirically, |
| 306 | | // it may disagree with is_64 on the compile unit. |
| 307 | | // The header is 8 or 12 bytes depending on is_64. |
| 308 | | if (compile_unit.addr_base < 8) return badDwarf(); |
| 309 | | |
| 310 | | const version = mem.readInt(u16, debug_addr[compile_unit.addr_base - 4 ..][0..2], di.endian); |
| 311 | | if (version != 5) return badDwarf(); |
| 312 | | |
| 313 | | const addr_size = debug_addr[compile_unit.addr_base - 2]; |
| 314 | | const seg_size = debug_addr[compile_unit.addr_base - 1]; |
| 315 | | |
| 316 | | const byte_offset = compile_unit.addr_base + (addr_size + seg_size) * index; |
| 317 | | if (byte_offset + addr_size > debug_addr.len) return badDwarf(); |
| 318 | | switch (addr_size) { |
| 319 | | 1 => return debug_addr[byte_offset], |
| 320 | | 2 => return mem.readInt(u16, debug_addr[byte_offset..][0..2], di.endian), |
| 321 | | 4 => return mem.readInt(u32, debug_addr[byte_offset..][0..4], di.endian), |
| 322 | | 8 => return mem.readInt(u64, debug_addr[byte_offset..][0..8], di.endian), |
| 323 | | else => return badDwarf(), |
| 324 | | } |
| 325 | | }, |
| 302 | FormValue.AddrOffset => |index| di.readDebugAddr(compile_unit, index), |
| 326 | 303 | else => error.InvalidDebugInfo, |
| 327 | 304 | }; |
| 328 | 305 | } |
| ... | ... | @@ -952,41 +929,119 @@ pub const DwarfInfo = struct { |
| 952 | 929 | if (compile_unit.pc_range) |range| { |
| 953 | 930 | if (target_address >= range.start and target_address < range.end) return compile_unit; |
| 954 | 931 | } |
| 955 | | if (di.debug_ranges) |debug_ranges| { |
| 956 | | if (compile_unit.die.getAttrSecOffset(AT.ranges)) |ranges_offset| { |
| 957 | | var stream = io.fixedBufferStream(debug_ranges); |
| 958 | | const in = &stream.reader(); |
| 959 | | const seekable = &stream.seekableStream(); |
| 960 | | |
| 961 | | // All the addresses in the list are relative to the value |
| 962 | | // specified by DW_AT.low_pc or to some other value encoded |
| 963 | | // in the list itself. |
| 964 | | // If no starting value is specified use zero. |
| 965 | | var base_address = compile_unit.die.getAttrAddr(di, AT.low_pc, compile_unit.*) catch |err| switch (err) { |
| 966 | | error.MissingDebugInfo => @as(u64, 0), // TODO https://github.com/ziglang/zig/issues/11135 |
| 967 | | else => return err, |
| 968 | | }; |
| 969 | 932 | |
| 970 | | try seekable.seekTo(ranges_offset); |
| 933 | const opt_debug_ranges = if (compile_unit.version >= 5) di.debug_rnglists else di.debug_ranges; |
| 934 | const debug_ranges = opt_debug_ranges orelse continue; |
| 935 | |
| 936 | const ranges_val = compile_unit.die.getAttr(AT.ranges) orelse continue; |
| 937 | const ranges_offset = switch (ranges_val.*) { |
| 938 | .SecOffset => |off| off, |
| 939 | .RangeListOffset => |idx| off: { |
| 940 | if (compile_unit.is_64) { |
| 941 | const offset_loc = @intCast(usize, compile_unit.rnglists_base + 8 * idx); |
| 942 | if (offset_loc + 8 > debug_ranges.len) return badDwarf(); |
| 943 | const offset = mem.readInt(u64, debug_ranges[offset_loc..][0..8], di.endian); |
| 944 | break :off compile_unit.rnglists_base + offset; |
| 945 | } else { |
| 946 | const offset_loc = @intCast(usize, compile_unit.rnglists_base + 4 * idx); |
| 947 | if (offset_loc + 4 > debug_ranges.len) return badDwarf(); |
| 948 | const offset = mem.readInt(u32, debug_ranges[offset_loc..][0..4], di.endian); |
| 949 | break :off compile_unit.rnglists_base + offset; |
| 950 | } |
| 951 | }, |
| 952 | else => return badDwarf(), |
| 953 | }; |
| 971 | 954 | |
| 972 | | while (true) { |
| 973 | | const begin_addr = try in.readInt(usize, di.endian); |
| 974 | | const end_addr = try in.readInt(usize, di.endian); |
| 975 | | if (begin_addr == 0 and end_addr == 0) { |
| 976 | | break; |
| 977 | | } |
| 978 | | // This entry selects a new value for the base address |
| 979 | | if (begin_addr == math.maxInt(usize)) { |
| 980 | | base_address = end_addr; |
| 981 | | continue; |
| 982 | | } |
| 983 | | if (target_address >= base_address + begin_addr and target_address < base_address + end_addr) { |
| 984 | | return compile_unit; |
| 985 | | } |
| 955 | var stream = io.fixedBufferStream(debug_ranges); |
| 956 | const in = &stream.reader(); |
| 957 | const seekable = &stream.seekableStream(); |
| 958 | |
| 959 | // All the addresses in the list are relative to the value |
| 960 | // specified by DW_AT.low_pc or to some other value encoded |
| 961 | // in the list itself. |
| 962 | // If no starting value is specified use zero. |
| 963 | var base_address = compile_unit.die.getAttrAddr(di, AT.low_pc, compile_unit.*) catch |err| switch (err) { |
| 964 | error.MissingDebugInfo => @as(u64, 0), // TODO https://github.com/ziglang/zig/issues/11135 |
| 965 | else => return err, |
| 966 | }; |
| 967 | |
| 968 | try seekable.seekTo(ranges_offset); |
| 969 | |
| 970 | if (compile_unit.version >= 5) { |
| 971 | while (true) { |
| 972 | const kind = try in.readByte(); |
| 973 | switch (kind) { |
| 974 | RLE.end_of_list => break, |
| 975 | RLE.base_addressx => { |
| 976 | const index = try leb.readULEB128(usize, in); |
| 977 | base_address = try di.readDebugAddr(compile_unit.*, index); |
| 978 | }, |
| 979 | RLE.startx_endx => { |
| 980 | const start_index = try leb.readULEB128(usize, in); |
| 981 | const start_addr = try di.readDebugAddr(compile_unit.*, start_index); |
| 982 | |
| 983 | const end_index = try leb.readULEB128(usize, in); |
| 984 | const end_addr = try di.readDebugAddr(compile_unit.*, end_index); |
| 985 | |
| 986 | if (target_address >= start_addr and target_address < end_addr) { |
| 987 | return compile_unit; |
| 988 | } |
| 989 | }, |
| 990 | RLE.startx_length => { |
| 991 | const start_index = try leb.readULEB128(usize, in); |
| 992 | const start_addr = try di.readDebugAddr(compile_unit.*, start_index); |
| 993 | |
| 994 | const len = try leb.readULEB128(usize, in); |
| 995 | const end_addr = start_addr + len; |
| 996 | |
| 997 | if (target_address >= start_addr and target_address < end_addr) { |
| 998 | return compile_unit; |
| 999 | } |
| 1000 | }, |
| 1001 | RLE.offset_pair => { |
| 1002 | const start_addr = try leb.readULEB128(usize, in); |
| 1003 | const end_addr = try leb.readULEB128(usize, in); |
| 1004 | // This is the only kind that uses the base address |
| 1005 | if (target_address >= base_address + start_addr and target_address < base_address + end_addr) { |
| 1006 | return compile_unit; |
| 1007 | } |
| 1008 | }, |
| 1009 | RLE.base_address => { |
| 1010 | base_address = try in.readInt(usize, di.endian); |
| 1011 | }, |
| 1012 | RLE.start_end => { |
| 1013 | const start_addr = try in.readInt(usize, di.endian); |
| 1014 | const end_addr = try in.readInt(usize, di.endian); |
| 1015 | if (target_address >= start_addr and target_address < end_addr) { |
| 1016 | return compile_unit; |
| 1017 | } |
| 1018 | }, |
| 1019 | RLE.start_length => { |
| 1020 | const start_addr = try in.readInt(usize, di.endian); |
| 1021 | const len = try leb.readULEB128(usize, in); |
| 1022 | const end_addr = start_addr + len; |
| 1023 | if (target_address >= start_addr and target_address < end_addr) { |
| 1024 | return compile_unit; |
| 1025 | } |
| 1026 | }, |
| 1027 | else => return badDwarf(), |
| 1028 | } |
| 1029 | } |
| 1030 | } else { |
| 1031 | while (true) { |
| 1032 | const begin_addr = try in.readInt(usize, di.endian); |
| 1033 | const end_addr = try in.readInt(usize, di.endian); |
| 1034 | if (begin_addr == 0 and end_addr == 0) { |
| 1035 | break; |
| 1036 | } |
| 1037 | // This entry selects a new value for the base address |
| 1038 | if (begin_addr == math.maxInt(usize)) { |
| 1039 | base_address = end_addr; |
| 1040 | continue; |
| 1041 | } |
| 1042 | if (target_address >= base_address + begin_addr and target_address < base_address + end_addr) { |
| 1043 | return compile_unit; |
| 986 | 1044 | } |
| 987 | | } else |err| { |
| 988 | | if (err != error.MissingDebugInfo) return err; |
| 989 | | continue; |
| 990 | 1045 | } |
| 991 | 1046 | } |
| 992 | 1047 | } |
| ... | ... | @@ -1366,6 +1421,32 @@ pub const DwarfInfo = struct { |
| 1366 | 1421 | fn getLineString(di: DwarfInfo, offset: u64) ![]const u8 { |
| 1367 | 1422 | return getStringGeneric(di.debug_line_str, offset); |
| 1368 | 1423 | } |
| 1424 | |
| 1425 | fn readDebugAddr(di: DwarfInfo, compile_unit: CompileUnit, index: u64) !u64 { |
| 1426 | const debug_addr = di.debug_addr orelse return badDwarf(); |
| 1427 | |
| 1428 | // addr_base points to the first item after the header, however we |
| 1429 | // need to read the header to know the size of each item. Empirically, |
| 1430 | // it may disagree with is_64 on the compile unit. |
| 1431 | // The header is 8 or 12 bytes depending on is_64. |
| 1432 | if (compile_unit.addr_base < 8) return badDwarf(); |
| 1433 | |
| 1434 | const version = mem.readInt(u16, debug_addr[compile_unit.addr_base - 4 ..][0..2], di.endian); |
| 1435 | if (version != 5) return badDwarf(); |
| 1436 | |
| 1437 | const addr_size = debug_addr[compile_unit.addr_base - 2]; |
| 1438 | const seg_size = debug_addr[compile_unit.addr_base - 1]; |
| 1439 | |
| 1440 | const byte_offset = @intCast(usize, compile_unit.addr_base + (addr_size + seg_size) * index); |
| 1441 | if (byte_offset + addr_size > debug_addr.len) return badDwarf(); |
| 1442 | return switch (addr_size) { |
| 1443 | 1 => debug_addr[byte_offset], |
| 1444 | 2 => mem.readInt(u16, debug_addr[byte_offset..][0..2], di.endian), |
| 1445 | 4 => mem.readInt(u32, debug_addr[byte_offset..][0..4], di.endian), |
| 1446 | 8 => mem.readInt(u64, debug_addr[byte_offset..][0..8], di.endian), |
| 1447 | else => badDwarf(), |
| 1448 | }; |
| 1449 | } |
| 1369 | 1450 | }; |
| 1370 | 1451 | |
| 1371 | 1452 | /// Initialize DWARF info. The caller has the responsibility to initialize most |