authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-16 02:00:17-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-20 22:58:16-04:00
log618b0eb3d3ac5ecb84acb4296c591f53ba9c4298
treea2e012a08c04a5f83f61ce895dcb0819e336d97a
parent5e399d97d7b8ed3fc4c5b3664acfa7c79e72136c

dwarf: fixup integer overflow in readEhPointer

debug: handle the possibility of eh_frame / debug_frame being mapped in memory or loaded from disk

3 files changed, 66 insertions(+), 37 deletions(-)

lib/std/coff.zig+3-5
...@@ -1253,14 +1253,12 @@ pub const Coff = struct {...@@ -1253,14 +1253,12 @@ pub const Coff = struct {
1253 return null;1253 return null;
1254 }1254 }
12551255
1256 pub fn getSectionData(self: *const Coff, comptime name: []const u8) ![]const u8 {1256 pub fn getSectionData(self: *const Coff, sec: *align(1) const SectionHeader) []const u8 {
1257 const sec = self.getSectionByName(name) orelse return error.MissingCoffSection;
1258 return self.data[sec.pointer_to_raw_data..][0..sec.virtual_size];1257 return self.data[sec.pointer_to_raw_data..][0..sec.virtual_size];
1259 }1258 }
12601259
1261 // Return an owned slice full of the section data1260 pub fn getSectionDataAlloc(self: *const Coff, sec: *align(1) const SectionHeader, allocator: mem.Allocator) ![]u8 {
1262 pub fn getSectionDataAlloc(self: *const Coff, comptime name: []const u8, allocator: mem.Allocator) ![]u8 {1261 const section_data = self.getSectionData(sec);
1263 const section_data = try self.getSectionData(name);
1264 return allocator.dupe(u8, section_data);1262 return allocator.dupe(u8, section_data);
1265 }1263 }
1266};1264};
lib/std/debug.zig+23-14
...@@ -987,23 +987,19 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_obj: *coff.Coff) !ModuleDebu...@@ -987,23 +987,19 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_obj: *coff.Coff) !ModuleDebu
987 .debug_data = undefined,987 .debug_data = undefined,
988 };988 };
989989
990 if (coff_obj.getSectionByName(".debug_info")) |sec| {990 if (coff_obj.getSectionByName(".debug_info")) |_| {
991 // This coff file has embedded DWARF debug info991 // This coff file has embedded DWARF debug info
992 _ = sec;
993
994 var sections: DW.DwarfInfo.SectionArray = DW.DwarfInfo.null_section_array;992 var sections: DW.DwarfInfo.SectionArray = DW.DwarfInfo.null_section_array;
995 errdefer for (sections) |section| if (section) |s| if (s.owned) allocator.free(s.data);993 errdefer for (sections) |section| if (section) |s| if (s.owned) allocator.free(s.data);
996994
997 inline for (@typeInfo(DW.DwarfSection).Enum.fields, 0..) |section, i| {995 inline for (@typeInfo(DW.DwarfSection).Enum.fields, 0..) |section, i| {
998 sections[i] = if (coff_obj.getSectionDataAlloc("." ++ section.name, allocator)) |data| blk: {996 sections[i] = if (coff_obj.getSectionByName("." ++ section.name)) |section_header| blk: {
999 break :blk .{997 break :blk .{
1000 .data = data,998 .data = try coff_obj.getSectionDataAlloc(section_header, allocator),
999 .virtual_address = section_header.virtual_address,
1001 .owned = true,1000 .owned = true,
1002 };1001 };
1003 } else |err| blk: {1002 } else null;
1004 if (err == error.MissingCoffSection) break :blk null;
1005 return err;
1006 };
1007 }1003 }
10081004
1009 var dwarf = DW.DwarfInfo{1005 var dwarf = DW.DwarfInfo{
...@@ -1012,7 +1008,7 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_obj: *coff.Coff) !ModuleDebu...@@ -1012,7 +1008,7 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_obj: *coff.Coff) !ModuleDebu
1012 .is_macho = false,1008 .is_macho = false,
1013 };1009 };
10141010
1015 try DW.openDwarfDebugInfo(&dwarf, allocator, coff_obj.data);1011 try DW.openDwarfDebugInfo(&dwarf, allocator);
1016 di.debug_data = PdbOrDwarf{ .dwarf = dwarf };1012 di.debug_data = PdbOrDwarf{ .dwarf = dwarf };
1017 return di;1013 return di;
1018 }1014 }
...@@ -1049,6 +1045,10 @@ fn chopSlice(ptr: []const u8, offset: u64, size: u64) error{Overflow}![]const u8...@@ -1049,6 +1045,10 @@ fn chopSlice(ptr: []const u8, offset: u64, size: u64) error{Overflow}![]const u8
1049 return ptr[start..end];1045 return ptr[start..end];
1050}1046}
10511047
1048/// Reads debug info from an ELF file, or the current binary if none in specified.
1049/// If the required sections aren't present but a reference to external debug info is,
1050/// then this this function will recurse to attempt to load the debug sections from
1051/// an external file.
1052pub fn readElfDebugInfo(1052pub fn readElfDebugInfo(
1053 allocator: mem.Allocator,1053 allocator: mem.Allocator,
1054 elf_filename: ?[]const u8,1054 elf_filename: ?[]const u8,
...@@ -1146,10 +1146,12 @@ pub fn readElfDebugInfo(...@@ -1146,10 +1146,12 @@ pub fn readElfDebugInfo(
11461146
1147 break :blk .{1147 break :blk .{
1148 .data = decompressed_section,1148 .data = decompressed_section,
1149 .virtual_address = shdr.sh_addr,
1149 .owned = true,1150 .owned = true,
1150 };1151 };
1151 } else .{1152 } else .{
1152 .data = section_bytes,1153 .data = section_bytes,
1154 .virtual_address = shdr.sh_addr,
1153 .owned = false,1155 .owned = false,
1154 };1156 };
1155 }1157 }
...@@ -1232,7 +1234,7 @@ pub fn readElfDebugInfo(...@@ -1232,7 +1234,7 @@ pub fn readElfDebugInfo(
1232 .is_macho = false,1234 .is_macho = false,
1233 };1235 };
12341236
1235 try DW.openDwarfDebugInfo(&di, allocator, parent_mapped_mem orelse mapped_mem);1237 try DW.openDwarfDebugInfo(&di, allocator);
12361238
1237 return ModuleDebugInfo{1239 return ModuleDebugInfo{
1238 .base_address = undefined,1240 .base_address = undefined,
...@@ -1900,6 +1902,10 @@ pub const DebugInfo = struct {...@@ -1900,6 +1902,10 @@ pub const DebugInfo = struct {
1900 obj_di.* = try readElfDebugInfo(self.allocator, if (ctx.name.len > 0) ctx.name else null, ctx.build_id, null, &sections, null);1902 obj_di.* = try readElfDebugInfo(self.allocator, if (ctx.name.len > 0) ctx.name else null, ctx.build_id, null, &sections, null);
1901 obj_di.base_address = ctx.base_address;1903 obj_di.base_address = ctx.base_address;
19021904
1905 // TODO: Don't actually scan everything, search on demand
1906 // Missing unwind info isn't treated as a failure, as the unwinder will fall back to FP-based unwinding
1907 obj_di.dwarf.scanAllUnwindInfo(self.allocator, ctx.base_address) catch {};
1908
1903 try self.address_map.putNoClobber(ctx.base_address, obj_di);1909 try self.address_map.putNoClobber(ctx.base_address, obj_di);
19041910
1905 return obj_di;1911 return obj_di;
...@@ -2004,11 +2010,12 @@ pub const ModuleDebugInfo = switch (native_os) {...@@ -2004,11 +2010,12 @@ pub const ModuleDebugInfo = switch (native_os) {
2004 inline for (@typeInfo(DW.DwarfSection).Enum.fields, 0..) |section, i| {2010 inline for (@typeInfo(DW.DwarfSection).Enum.fields, 0..) |section, i| {
2005 if (mem.eql(u8, "__" ++ section.name, sect.sectName())) section_index = i;2011 if (mem.eql(u8, "__" ++ section.name, sect.sectName())) section_index = i;
2006 }2012 }
2007 if (section_index == null or sections[section_index.?] != null) continue;2013 if (section_index == null) continue;
20082014
2009 const section_bytes = try chopSlice(mapped_mem, sect.offset, sect.size);2015 const section_bytes = try chopSlice(mapped_mem, sect.offset, sect.size);
2010 sections[section_index.?] = .{2016 sections[section_index.?] = .{
2011 .data = section_bytes,2017 .data = section_bytes,
2018 .virtual_address = sect.addr,
2012 .owned = false,2019 .owned = false,
2013 };2020 };
2014 }2021 }
...@@ -2026,9 +2033,11 @@ pub const ModuleDebugInfo = switch (native_os) {...@@ -2026,9 +2033,11 @@ pub const ModuleDebugInfo = switch (native_os) {
2026 .is_macho = true,2033 .is_macho = true,
2027 };2034 };
20282035
2029 // TODO: Don't actually need to scan unwind info in this case, since __unwind_info points us to the entries2036 try DW.openDwarfDebugInfo(&di, allocator);
2037
2038 // TODO: Don't actually scan everything, search on demand
2039 di.scanAllUnwindInfo(allocator, self.base_address) catch {};
20302040
2031 try DW.openDwarfDebugInfo(&di, allocator, mapped_mem);
2032 var info = OFileInfo{2041 var info = OFileInfo{
2033 .di = di,2042 .di = di,
2034 .addr_table = addr_table,2043 .addr_table = addr_table,
lib/std/dwarf.zig+40-18
...@@ -663,7 +663,22 @@ pub const DwarfSection = enum {...@@ -663,7 +663,22 @@ pub const DwarfSection = enum {
663pub const DwarfInfo = struct {663pub const DwarfInfo = struct {
664 pub const Section = struct {664 pub const Section = struct {
665 data: []const u8,665 data: []const u8,
666 // Module-relative virtual address.
667 // Only set if the section data was loaded from disk.
668 virtual_address: ?usize = null,
669 // If `data` is owned by this DwarfInfo.
666 owned: bool,670 owned: bool,
671
672 // For sections that are not memory mapped by the loader, this is an offset
673 // from `data.ptr` to where the section would have been mapped. Otherwise,
674 // `data` is directly backed by the section and the offset is zero.
675 pub fn virtualOffset(self: Section, base_address: usize) i64 {
676 return if (self.virtual_address) |va|
677 @as(i64, @intCast(base_address + va)) -
678 @as(i64, @intCast(@intFromPtr(self.data.ptr)))
679 else
680 0;
681 }
667 };682 };
668683
669 const num_sections = std.enums.directEnumArrayLen(DwarfSection, 0);684 const num_sections = std.enums.directEnumArrayLen(DwarfSection, 0);
...@@ -690,6 +705,10 @@ pub const DwarfInfo = struct {...@@ -690,6 +705,10 @@ pub const DwarfInfo = struct {
690 return if (di.sections[@intFromEnum(dwarf_section)]) |s| s.data else null;705 return if (di.sections[@intFromEnum(dwarf_section)]) |s| s.data else null;
691 }706 }
692707
708 pub fn sectionVirtualOffset(di: DwarfInfo, dwarf_section: DwarfSection, base_address: usize) ?i64 {
709 return if (di.sections[@intFromEnum(dwarf_section)]) |s| s.virtualOffset(base_address) else null;
710 }
711
693 pub fn deinit(di: *DwarfInfo, allocator: mem.Allocator) void {712 pub fn deinit(di: *DwarfInfo, allocator: mem.Allocator) void {
694 for (di.sections) |opt_section| {713 for (di.sections) |opt_section| {
695 if (opt_section) |s| if (s.owned) allocator.free(s.data);714 if (opt_section) |s| if (s.owned) allocator.free(s.data);
...@@ -1540,7 +1559,12 @@ pub const DwarfInfo = struct {...@@ -1540,7 +1559,12 @@ pub const DwarfInfo = struct {
1540 };1559 };
1541 }1560 }
15421561
1543 pub fn scanAllUnwindInfo(di: *DwarfInfo, allocator: mem.Allocator, binary_mem: []const u8) !void {1562 /// If .eh_frame_hdr is present, then only the header needs to be parsed.
1563 ///
1564 /// Otherwise, .eh_frame and .debug_frame are scanned and a sorted list
1565 /// of FDEs is built. In this case, the decoded PC ranges in the FDEs
1566 /// are all normalized to be relative to the module's base.
1567 pub fn scanAllUnwindInfo(di: *DwarfInfo, allocator: mem.Allocator, base_address: usize) !void {
1544 if (di.section(.eh_frame_hdr)) |eh_frame_hdr| blk: {1568 if (di.section(.eh_frame_hdr)) |eh_frame_hdr| blk: {
1545 var stream = io.fixedBufferStream(eh_frame_hdr);1569 var stream = io.fixedBufferStream(eh_frame_hdr);
1546 const reader = stream.reader();1570 const reader = stream.reader();
...@@ -1582,15 +1606,15 @@ pub const DwarfInfo = struct {...@@ -1582,15 +1606,15 @@ pub const DwarfInfo = struct {
15821606
1583 const frame_sections = [2]DwarfSection{ .eh_frame, .debug_frame };1607 const frame_sections = [2]DwarfSection{ .eh_frame, .debug_frame };
1584 for (frame_sections) |frame_section| {1608 for (frame_sections) |frame_section| {
1585 if (di.section(frame_section)) |eh_frame| {1609 if (di.section(frame_section)) |section_data| {
1586 var stream = io.fixedBufferStream(eh_frame);1610 var stream = io.fixedBufferStream(section_data);
1587 while (stream.pos < stream.buffer.len) {1611 while (stream.pos < stream.buffer.len) {
1588 const entry_header = try EntryHeader.read(&stream, frame_section, di.endian);1612 const entry_header = try EntryHeader.read(&stream, frame_section, di.endian);
1589 switch (entry_header.type) {1613 switch (entry_header.type) {
1590 .cie => {1614 .cie => {
1591 const cie = try CommonInformationEntry.parse(1615 const cie = try CommonInformationEntry.parse(
1592 entry_header.entry_bytes,1616 entry_header.entry_bytes,
1593 -@as(i64, @intCast(@intFromPtr(binary_mem.ptr))),1617 di.sectionVirtualOffset(frame_section, base_address).?,
1594 true,1618 true,
1595 entry_header.is_64,1619 entry_header.is_64,
1596 frame_section,1620 frame_section,
...@@ -1604,7 +1628,7 @@ pub const DwarfInfo = struct {...@@ -1604,7 +1628,7 @@ pub const DwarfInfo = struct {
1604 const cie = di.cie_map.get(cie_offset) orelse return badDwarf();1628 const cie = di.cie_map.get(cie_offset) orelse return badDwarf();
1605 const fde = try FrameDescriptionEntry.parse(1629 const fde = try FrameDescriptionEntry.parse(
1606 entry_header.entry_bytes,1630 entry_header.entry_bytes,
1607 -@as(i64, @intCast(@intFromPtr(binary_mem.ptr))),1631 di.sectionVirtualOffset(frame_section, base_address).?,
1608 true,1632 true,
1609 cie,1633 cie,
1610 @sizeOf(usize),1634 @sizeOf(usize),
...@@ -1637,7 +1661,7 @@ pub const DwarfInfo = struct {...@@ -1637,7 +1661,7 @@ pub const DwarfInfo = struct {
1637 var fde: FrameDescriptionEntry = undefined;1661 var fde: FrameDescriptionEntry = undefined;
16381662
1639 // In order to support reading .eh_frame from the ELF file (vs using the already-mapped section),1663 // In order to support reading .eh_frame from the ELF file (vs using the already-mapped section),
1640 // scanAllUnwindInfo has already mapped any pc-relative offsets such that they we be relative to zero1664 // scanAllUnwindInfo has already mapped any pc-relative offsets such that they will be relative to zero
1641 // instead of the actual base address of the module. When using .eh_frame_hdr, PC can be used directly1665 // instead of the actual base address of the module. When using .eh_frame_hdr, PC can be used directly
1642 // as pointers will be decoded relative to the already-mapped .eh_frame.1666 // as pointers will be decoded relative to the already-mapped .eh_frame.
1643 var mapped_pc: usize = undefined;1667 var mapped_pc: usize = undefined;
...@@ -1653,7 +1677,8 @@ pub const DwarfInfo = struct {...@@ -1653,7 +1677,8 @@ pub const DwarfInfo = struct {
1653 &fde,1677 &fde,
1654 );1678 );
1655 } else {1679 } else {
1656 mapped_pc = context.pc - module_base_address;1680 //mapped_pc = context.pc - module_base_address;
1681 mapped_pc = context.pc;
1657 const index = std.sort.binarySearch(FrameDescriptionEntry, mapped_pc, di.fde_list.items, {}, struct {1682 const index = std.sort.binarySearch(FrameDescriptionEntry, mapped_pc, di.fde_list.items, {}, struct {
1658 pub fn compareFn(_: void, pc: usize, mid_item: FrameDescriptionEntry) std.math.Order {1683 pub fn compareFn(_: void, pc: usize, mid_item: FrameDescriptionEntry) std.math.Order {
1659 if (pc < mid_item.pc_begin) return .lt;1684 if (pc < mid_item.pc_begin) return .lt;
...@@ -1819,12 +1844,9 @@ pub const UnwindContext = struct {...@@ -1819,12 +1844,9 @@ pub const UnwindContext = struct {
1819/// Initialize DWARF info. The caller has the responsibility to initialize most1844/// Initialize DWARF info. The caller has the responsibility to initialize most
1820/// the DwarfInfo fields before calling. `binary_mem` is the raw bytes of the1845/// the DwarfInfo fields before calling. `binary_mem` is the raw bytes of the
1821/// main binary file (not the secondary debug info file).1846/// main binary file (not the secondary debug info file).
1822pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: mem.Allocator, binary_mem: []const u8) !void {1847pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: mem.Allocator) !void {
1823 try di.scanAllFunctions(allocator);1848 try di.scanAllFunctions(allocator);
1824 try di.scanAllCompileUnits(allocator);1849 try di.scanAllCompileUnits(allocator);
1825
1826 // Unwind info is not required
1827 di.scanAllUnwindInfo(allocator, binary_mem) catch {};
1828}1850}
18291851
1830/// This function is to make it handy to comment out the return and make it1852/// This function is to make it handy to comment out the return and make it
...@@ -1898,9 +1920,10 @@ fn readEhPointer(reader: anytype, enc: u8, addr_size_bytes: u8, ctx: EhPointerCo...@@ -1898,9 +1920,10 @@ fn readEhPointer(reader: anytype, enc: u8, addr_size_bytes: u8, ctx: EhPointerCo
1898 else => null,1920 else => null,
1899 };1921 };
19001922
1901 const ptr = if (base) |b| switch (value) {1923 const ptr: u64 = if (base) |b| switch (value) {
1902 .signed => |s| @as(u64, @intCast(s + @as(i64, @intCast(b)))),1924 .signed => |s| @intCast(try math.add(i64, s, @as(i64, @intCast(b)))),
1903 .unsigned => |u| u + b,1925 // absptr can actually contain signed values in some cases (aarch64 MachO)
1926 .unsigned => |u| u +% b,
1904 } else switch (value) {1927 } else switch (value) {
1905 .signed => |s| @as(u64, @intCast(s)),1928 .signed => |s| @as(u64, @intCast(s)),
1906 .unsigned => |u| u,1929 .unsigned => |u| u,
...@@ -2311,15 +2334,14 @@ pub const FrameDescriptionEntry = struct {...@@ -2311,15 +2334,14 @@ pub const FrameDescriptionEntry = struct {
2311 instructions: []const u8,2334 instructions: []const u8,
23122335
2313 /// This function expects to read the FDE starting at the PC Begin field.2336 /// This function expects to read the FDE starting at the PC Begin field.
2314 /// The returned struct references memory backed by fde_bytes.2337 /// The returned struct references memory backed by `fde_bytes`.
2315 ///2338 ///
2316 /// `pc_rel_offset` specifies an offset to be applied to pc_rel_base values2339 /// `pc_rel_offset` specifies an offset to be applied to pc_rel_base values
2317 /// used when decoding pointers. This should be set to zero if fde_bytes is2340 /// used when decoding pointers. This should be set to zero if fde_bytes is
2318 /// backed by the memory of the .eh_frame section in the running executable.2341 /// backed by the memory of a .eh_frame / .debug_frame section in the running executable.
2319 ///
2320 /// Otherwise, it should be the relative offset to translate addresses from2342 /// Otherwise, it should be the relative offset to translate addresses from
2321 /// where the section is currently stored in memory, to where it *would* be2343 /// where the section is currently stored in memory, to where it *would* be
2322 /// stored at runtime: section runtime offset - backing section data base ptr.2344 /// stored at runtime: section base addr - backing data base ptr.
2323 ///2345 ///
2324 /// Similarly, `is_runtime` specifies this function is being called on a runtime2346 /// Similarly, `is_runtime` specifies this function is being called on a runtime
2325 /// section, and so indirect pointers can be followed.2347 /// section, and so indirect pointers can be followed.