| ... | @@ -7,6 +7,7 @@ const os = std.os; | ... | @@ -7,6 +7,7 @@ const os = std.os; |
| 7 | const mem = std.mem; | 7 | const mem = std.mem; |
| 8 | const math = std.math; | 8 | const math = std.math; |
| 9 | const leb = @import("leb128.zig"); | 9 | const leb = @import("leb128.zig"); |
| | 10 | const assert = std.debug.assert; |
| 10 | | 11 | |
| 11 | pub const TAG = @import("dwarf/TAG.zig"); | 12 | pub const TAG = @import("dwarf/TAG.zig"); |
| 12 | pub const AT = @import("dwarf/AT.zig"); | 13 | pub const AT = @import("dwarf/AT.zig"); |
| ... | @@ -1530,45 +1531,49 @@ pub const DwarfInfo = struct { | ... | @@ -1530,45 +1531,49 @@ pub const DwarfInfo = struct { |
| 1530 | return; | 1531 | return; |
| 1531 | } | 1532 | } |
| 1532 | | 1533 | |
| 1533 | if (di.section(.eh_frame)) |eh_frame| { | 1534 | const frame_sections = [2]DwarfSection{ .eh_frame, .debug_frame }; |
| 1534 | var stream = io.fixedBufferStream(eh_frame); | 1535 | for (frame_sections) |frame_section| { |
| 1535 | while (stream.pos < stream.buffer.len) { | 1536 | if (di.section(frame_section)) |eh_frame| { |
| 1536 | const entry_header = try EntryHeader.read(&stream, di.endian); | 1537 | var stream = io.fixedBufferStream(eh_frame); |
| 1537 | switch (entry_header.type) { | 1538 | while (stream.pos < stream.buffer.len) { |
| 1538 | .cie => { | 1539 | const entry_header = try EntryHeader.read(&stream, frame_section, di.endian); |
| 1539 | const cie = try CommonInformationEntry.parse( | 1540 | switch (entry_header.type) { |
| 1540 | entry_header.entry_bytes, | 1541 | .cie => { |
| 1541 | -@as(i64, @intCast(@intFromPtr(binary_mem.ptr))), | 1542 | const cie = try CommonInformationEntry.parse( |
| 1542 | true, | 1543 | entry_header.entry_bytes, |
| 1543 | entry_header.length_offset, | 1544 | -@as(i64, @intCast(@intFromPtr(binary_mem.ptr))), |
| 1544 | @sizeOf(usize), | 1545 | true, |
| 1545 | di.endian, | 1546 | frame_section, |
| 1546 | ); | 1547 | entry_header.length_offset, |
| 1547 | try di.cie_map.put(allocator, entry_header.length_offset, cie); | 1548 | @sizeOf(usize), |
| 1548 | }, | 1549 | di.endian, |
| 1549 | .fde => |cie_offset| { | 1550 | ); |
| 1550 | const cie = di.cie_map.get(cie_offset) orelse return badDwarf(); | 1551 | try di.cie_map.put(allocator, entry_header.length_offset, cie); |
| 1551 | const fde = try FrameDescriptionEntry.parse( | 1552 | }, |
| 1552 | entry_header.entry_bytes, | 1553 | .fde => |cie_offset| { |
| 1553 | -@as(i64, @intCast(@intFromPtr(binary_mem.ptr))), | 1554 | const cie = di.cie_map.get(cie_offset) orelse return badDwarf(); |
| 1554 | true, | 1555 | const fde = try FrameDescriptionEntry.parse( |
| 1555 | cie, | 1556 | entry_header.entry_bytes, |
| 1556 | @sizeOf(usize), | 1557 | -@as(i64, @intCast(@intFromPtr(binary_mem.ptr))), |
| 1557 | di.endian, | 1558 | true, |
| 1558 | ); | 1559 | cie, |
| 1559 | try di.fde_list.append(allocator, fde); | 1560 | @sizeOf(usize), |
| 1560 | }, | 1561 | di.endian, |
| 1561 | .terminator => break, | 1562 | ); |
| | 1563 | try di.fde_list.append(allocator, fde); |
| | 1564 | }, |
| | 1565 | .terminator => break, |
| | 1566 | } |
| 1562 | } | 1567 | } |
| 1563 | } | | |
| 1564 | | 1568 | |
| 1565 | // TODO: Avoiding sorting if has_eh_frame_hdr exists | 1569 | // TODO: Avoiding sorting if has_eh_frame_hdr exists |
| 1566 | std.mem.sort(FrameDescriptionEntry, di.fde_list.items, {}, struct { | 1570 | std.mem.sort(FrameDescriptionEntry, di.fde_list.items, {}, struct { |
| 1567 | fn lessThan(ctx: void, a: FrameDescriptionEntry, b: FrameDescriptionEntry) bool { | 1571 | fn lessThan(ctx: void, a: FrameDescriptionEntry, b: FrameDescriptionEntry) bool { |
| 1568 | _ = ctx; | 1572 | _ = ctx; |
| 1569 | return a.pc_begin < b.pc_begin; | 1573 | return a.pc_begin < b.pc_begin; |
| 1570 | } | 1574 | } |
| 1571 | }.lessThan); | 1575 | }.lessThan); |
| | 1576 | } |
| 1572 | } | 1577 | } |
| 1573 | } | 1578 | } |
| 1574 | | 1579 | |
| ... | @@ -1905,14 +1910,14 @@ pub const ExceptionFrameHeader = struct { | ... | @@ -1905,14 +1910,14 @@ pub const ExceptionFrameHeader = struct { |
| 1905 | var eh_frame_stream = io.fixedBufferStream(eh_frame); | 1910 | var eh_frame_stream = io.fixedBufferStream(eh_frame); |
| 1906 | try eh_frame_stream.seekTo(fde_offset); | 1911 | try eh_frame_stream.seekTo(fde_offset); |
| 1907 | | 1912 | |
| 1908 | const fde_entry_header = try EntryHeader.read(&eh_frame_stream, builtin.cpu.arch.endian()); | 1913 | const fde_entry_header = try EntryHeader.read(&eh_frame_stream, .eh_frame, builtin.cpu.arch.endian()); |
| 1909 | if (!self.isValidPtr(@intFromPtr(&fde_entry_header.entry_bytes[fde_entry_header.entry_bytes.len - 1]), isValidMemory, eh_frame_len)) return badDwarf(); | 1914 | if (!self.isValidPtr(@intFromPtr(&fde_entry_header.entry_bytes[fde_entry_header.entry_bytes.len - 1]), isValidMemory, eh_frame_len)) return badDwarf(); |
| 1910 | if (fde_entry_header.type != .fde) return badDwarf(); | 1915 | if (fde_entry_header.type != .fde) return badDwarf(); |
| 1911 | | 1916 | |
| 1912 | // CIEs always come before FDEs (the offset is a subtration), so we can assume this memory is readable | 1917 | // CIEs always come before FDEs (the offset is a subtration), so we can assume this memory is readable |
| 1913 | const cie_offset = fde_entry_header.type.fde; | 1918 | const cie_offset = fde_entry_header.type.fde; |
| 1914 | try eh_frame_stream.seekTo(cie_offset); | 1919 | try eh_frame_stream.seekTo(cie_offset); |
| 1915 | const cie_entry_header = try EntryHeader.read(&eh_frame_stream, builtin.cpu.arch.endian()); | 1920 | const cie_entry_header = try EntryHeader.read(&eh_frame_stream, .eh_frame, builtin.cpu.arch.endian()); |
| 1916 | if (!self.isValidPtr(@intFromPtr(&cie_entry_header.entry_bytes[cie_entry_header.entry_bytes.len - 1]), isValidMemory, eh_frame_len)) return badDwarf(); | 1921 | if (!self.isValidPtr(@intFromPtr(&cie_entry_header.entry_bytes[cie_entry_header.entry_bytes.len - 1]), isValidMemory, eh_frame_len)) return badDwarf(); |
| 1917 | if (cie_entry_header.type != .cie) return badDwarf(); | 1922 | if (cie_entry_header.type != .cie) return badDwarf(); |
| 1918 | | 1923 | |
| ... | @@ -1920,6 +1925,7 @@ pub const ExceptionFrameHeader = struct { | ... | @@ -1920,6 +1925,7 @@ pub const ExceptionFrameHeader = struct { |
| 1920 | cie_entry_header.entry_bytes, | 1925 | cie_entry_header.entry_bytes, |
| 1921 | 0, | 1926 | 0, |
| 1922 | true, | 1927 | true, |
| | 1928 | .eh_frame, |
| 1923 | cie_entry_header.length_offset, | 1929 | cie_entry_header.length_offset, |
| 1924 | @sizeOf(usize), | 1930 | @sizeOf(usize), |
| 1925 | builtin.cpu.arch.endian(), | 1931 | builtin.cpu.arch.endian(), |
| ... | @@ -1937,7 +1943,7 @@ pub const ExceptionFrameHeader = struct { | ... | @@ -1937,7 +1943,7 @@ pub const ExceptionFrameHeader = struct { |
| 1937 | }; | 1943 | }; |
| 1938 | | 1944 | |
| 1939 | pub const EntryHeader = struct { | 1945 | pub const EntryHeader = struct { |
| 1940 | /// Offset of the length in the backing buffer | 1946 | /// Offset of the length field in the backing buffer |
| 1941 | length_offset: usize, | 1947 | length_offset: usize, |
| 1942 | is_64: bool, | 1948 | is_64: bool, |
| 1943 | type: union(enum) { | 1949 | type: union(enum) { |
| ... | @@ -1950,8 +1956,10 @@ pub const EntryHeader = struct { | ... | @@ -1950,8 +1956,10 @@ pub const EntryHeader = struct { |
| 1950 | entry_bytes: []const u8, | 1956 | entry_bytes: []const u8, |
| 1951 | | 1957 | |
| 1952 | /// Reads a header for either an FDE or a CIE, then advances the stream to the position after the trailing structure. | 1958 | /// Reads a header for either an FDE or a CIE, then advances the stream to the position after the trailing structure. |
| 1953 | /// `stream` must be a stream backed by the .eh_frame section. | 1959 | /// `stream` must be a stream backed by either the .eh_frame or .debug_frame sections. |
| 1954 | pub fn read(stream: *std.io.FixedBufferStream([]const u8), endian: std.builtin.Endian) !EntryHeader { | 1960 | pub fn read(stream: *std.io.FixedBufferStream([]const u8), dwarf_section: DwarfSection, endian: std.builtin.Endian) !EntryHeader { |
| | 1961 | assert(dwarf_section == .eh_frame or dwarf_section == .debug_frame); |
| | 1962 | |
| 1955 | const reader = stream.reader(); | 1963 | const reader = stream.reader(); |
| 1956 | const length_offset = stream.pos; | 1964 | const length_offset = stream.pos; |
| 1957 | | 1965 | |
| ... | @@ -1967,14 +1975,21 @@ pub const EntryHeader = struct { | ... | @@ -1967,14 +1975,21 @@ pub const EntryHeader = struct { |
| 1967 | const id_len = @as(u8, if (is_64) 8 else 4); | 1975 | const id_len = @as(u8, if (is_64) 8 else 4); |
| 1968 | const id = if (is_64) try reader.readInt(u64, endian) else try reader.readInt(u32, endian); | 1976 | const id = if (is_64) try reader.readInt(u64, endian) else try reader.readInt(u32, endian); |
| 1969 | const entry_bytes = stream.buffer[stream.pos..][0 .. length - id_len]; | 1977 | const entry_bytes = stream.buffer[stream.pos..][0 .. length - id_len]; |
| | 1978 | const cie_id: u64 = switch (dwarf_section) { |
| | 1979 | .eh_frame => CommonInformationEntry.eh_id, |
| | 1980 | .debug_frame => if (is_64) CommonInformationEntry.dwarf64_id else CommonInformationEntry.dwarf32_id, |
| | 1981 | else => unreachable, |
| | 1982 | }; |
| 1970 | | 1983 | |
| 1971 | const result = EntryHeader{ | 1984 | const result = EntryHeader{ |
| 1972 | .length_offset = length_offset, | 1985 | .length_offset = length_offset, |
| 1973 | .is_64 = is_64, | 1986 | .is_64 = is_64, |
| 1974 | .type = switch (id) { | 1987 | .type = if (id == cie_id) .{ .cie = {} } else .{ |
| 1975 | 0 => .{ .cie = {} }, | 1988 | .fde = switch (dwarf_section) { |
| 1976 | // TODO: Support CommonInformationEntry.dwarf32_id, CommonInformationEntry.dwarf64_id | 1989 | .eh_frame => stream.pos - id_len - id, |
| 1977 | else => .{ .fde = stream.pos - id_len - id }, | 1990 | .debug_frame => id, |
| | 1991 | else => unreachable, |
| | 1992 | }, |
| 1978 | }, | 1993 | }, |
| 1979 | .entry_bytes = entry_bytes, | 1994 | .entry_bytes = entry_bytes, |
| 1980 | }; | 1995 | }; |
| ... | @@ -2004,6 +2019,11 @@ pub const CommonInformationEntry = struct { | ... | @@ -2004,6 +2019,11 @@ pub const CommonInformationEntry = struct { |
| 2004 | length_offset: u64, | 2019 | length_offset: u64, |
| 2005 | version: u8, | 2020 | version: u8, |
| 2006 | | 2021 | |
| | 2022 | address_size: u8, |
| | 2023 | |
| | 2024 | // Only present in version 4 |
| | 2025 | segment_selector_size: ?u8, |
| | 2026 | |
| 2007 | code_alignment_factor: u32, | 2027 | code_alignment_factor: u32, |
| 2008 | data_alignment_factor: i32, | 2028 | data_alignment_factor: i32, |
| 2009 | return_address_register: u8, | 2029 | return_address_register: u8, |
| ... | @@ -2038,11 +2058,12 @@ pub const CommonInformationEntry = struct { | ... | @@ -2038,11 +2058,12 @@ pub const CommonInformationEntry = struct { |
| 2038 | /// of `pc_rel_offset` and `is_runtime`. | 2058 | /// of `pc_rel_offset` and `is_runtime`. |
| 2039 | /// | 2059 | /// |
| 2040 | /// `length_offset` specifies the offset of this CIE's length field in the | 2060 | /// `length_offset` specifies the offset of this CIE's length field in the |
| 2041 | /// .eh_frame section. | 2061 | /// .eh_frame / .debug_framesection. |
| 2042 | pub fn parse( | 2062 | pub fn parse( |
| 2043 | cie_bytes: []const u8, | 2063 | cie_bytes: []const u8, |
| 2044 | pc_rel_offset: i64, | 2064 | pc_rel_offset: i64, |
| 2045 | is_runtime: bool, | 2065 | is_runtime: bool, |
| | 2066 | dwarf_section: DwarfSection, |
| 2046 | length_offset: u64, | 2067 | length_offset: u64, |
| 2047 | addr_size_bytes: u8, | 2068 | addr_size_bytes: u8, |
| 2048 | endian: std.builtin.Endian, | 2069 | endian: std.builtin.Endian, |
| ... | @@ -2053,7 +2074,11 @@ pub const CommonInformationEntry = struct { | ... | @@ -2053,7 +2074,11 @@ pub const CommonInformationEntry = struct { |
| 2053 | const reader = stream.reader(); | 2074 | const reader = stream.reader(); |
| 2054 | | 2075 | |
| 2055 | const version = try reader.readByte(); | 2076 | const version = try reader.readByte(); |
| 2056 | if (version != 1 and version != 3) return error.UnsupportedDwarfVersion; | 2077 | switch (dwarf_section) { |
| | 2078 | .eh_frame => if (version != 1 and version != 3) return error.UnsupportedDwarfVersion, |
| | 2079 | .debug_frame => if (version != 4) return error.UnsupportedDwarfVersion, |
| | 2080 | else => return error.UnsupportedDwarfSection, |
| | 2081 | } |
| 2057 | | 2082 | |
| 2058 | var has_eh_data = false; | 2083 | var has_eh_data = false; |
| 2059 | var has_aug_data = false; | 2084 | var has_aug_data = false; |
| ... | @@ -2083,6 +2108,9 @@ pub const CommonInformationEntry = struct { | ... | @@ -2083,6 +2108,9 @@ pub const CommonInformationEntry = struct { |
| 2083 | for (0..addr_size_bytes) |_| _ = try reader.readByte(); | 2108 | for (0..addr_size_bytes) |_| _ = try reader.readByte(); |
| 2084 | } | 2109 | } |
| 2085 | | 2110 | |
| | 2111 | const address_size = if (version == 4) try reader.readByte() else addr_size_bytes; |
| | 2112 | const segment_selector_size = if (version == 4) try reader.readByte() else null; |
| | 2113 | |
| 2086 | const code_alignment_factor = try leb.readULEB128(u32, reader); | 2114 | const code_alignment_factor = try leb.readULEB128(u32, reader); |
| 2087 | const data_alignment_factor = try leb.readILEB128(i32, reader); | 2115 | const data_alignment_factor = try leb.readILEB128(i32, reader); |
| 2088 | const return_address_register = if (version == 1) try reader.readByte() else try leb.readULEB128(u8, reader); | 2116 | const return_address_register = if (version == 1) try reader.readByte() else try leb.readULEB128(u8, reader); |
| ... | @@ -2134,6 +2162,8 @@ pub const CommonInformationEntry = struct { | ... | @@ -2134,6 +2162,8 @@ pub const CommonInformationEntry = struct { |
| 2134 | return .{ | 2162 | return .{ |
| 2135 | .length_offset = length_offset, | 2163 | .length_offset = length_offset, |
| 2136 | .version = version, | 2164 | .version = version, |
| | 2165 | .address_size = address_size, |
| | 2166 | .segment_selector_size = segment_selector_size, |
| 2137 | .code_alignment_factor = code_alignment_factor, | 2167 | .code_alignment_factor = code_alignment_factor, |
| 2138 | .data_alignment_factor = data_alignment_factor, | 2168 | .data_alignment_factor = data_alignment_factor, |
| 2139 | .return_address_register = return_address_register, | 2169 | .return_address_register = return_address_register, |