| author | |
| committer | |
| log | 405075f7455492717ac6cba505603eb74e4f54b9 |
| tree | 73a0c76f92a0e16518e4b7737772d72e087753fd |
| parent | c895aa7a35b178576b89e600a20af9d16da36dea |
| signature |
2 files changed, 124 insertions(+), 37 deletions(-)
lib/std/debug/Dwarf/ElfModule.zig+69-19| ... | ... | @@ -3,6 +3,13 @@ |
| 3 | 3 | |
| 4 | 4 | dwarf: Dwarf, |
| 5 | 5 | |
| 6 | /// If we encounter a `.eh_frame` section while loading the ELF module, it is stored here and may be | |
| 7 | /// used with `Dwarf.Unwind` for call stack unwinding. | |
| 8 | eh_frame: ?UnwindSection, | |
| 9 | /// If we encounter a `.debug_frame` section while loading the ELF module, it is stored here and may | |
| 10 | /// be used with `Dwarf.Unwind` for call stack unwinding. | |
| 11 | debug_frame: ?UnwindSection, | |
| 12 | ||
| 6 | 13 | /// The memory-mapped ELF file, which is referenced by `dwarf`. This field is here only so that |
| 7 | 14 | /// this memory can be unmapped by `ElfModule.deinit`. |
| 8 | 15 | mapped_file: []align(std.heap.page_size_min) const u8, |
| ... | ... | @@ -11,10 +18,18 @@ mapped_file: []align(std.heap.page_size_min) const u8, |
| 11 | 18 | /// be unmapped by `ElfModule.deinit`. |
| 12 | 19 | mapped_debug_file: ?[]align(std.heap.page_size_min) const u8, |
| 13 | 20 | |
| 14 | pub fn deinit(em: *ElfModule, allocator: Allocator) void { | |
| 15 | em.dwarf.deinit(allocator); | |
| 21 | pub const UnwindSection = struct { | |
| 22 | vaddr: u64, | |
| 23 | bytes: []const u8, | |
| 24 | owned: bool, | |
| 25 | }; | |
| 26 | ||
| 27 | pub fn deinit(em: *ElfModule, gpa: Allocator) void { | |
| 28 | em.dwarf.deinit(gpa); | |
| 16 | 29 | std.posix.munmap(em.mapped_file); |
| 17 | 30 | if (em.mapped_debug_file) |m| std.posix.munmap(m); |
| 31 | if (em.eh_frame) |s| if (s.owned) gpa.free(s.bytes); | |
| 32 | if (em.debug_frame) |s| if (s.owned) gpa.free(s.bytes); | |
| 18 | 33 | } |
| 19 | 34 | |
| 20 | 35 | pub const LoadError = error{ |
| ... | ... | @@ -98,7 +113,6 @@ pub fn load( |
| 98 | 113 | )[0..hdr.e_shnum]; |
| 99 | 114 | |
| 100 | 115 | var sections: Dwarf.SectionArray = @splat(null); |
| 101 | ||
| 102 | 116 | // Combine section list. This takes ownership over any owned sections from the parent scope. |
| 103 | 117 | if (parent_sections) |ps| { |
| 104 | 118 | for (ps, &sections) |*parent, *section_elem| { |
| ... | ... | @@ -110,6 +124,12 @@ pub fn load( |
| 110 | 124 | } |
| 111 | 125 | errdefer for (sections) |opt_section| if (opt_section) |s| if (s.owned) gpa.free(s.data); |
| 112 | 126 | |
| 127 | var eh_frame_section: ?UnwindSection = null; | |
| 128 | errdefer if (eh_frame_section) |s| if (s.owned) gpa.free(s.bytes); | |
| 129 | ||
| 130 | var debug_frame_section: ?UnwindSection = null; | |
| 131 | errdefer if (debug_frame_section) |s| if (s.owned) gpa.free(s.bytes); | |
| 132 | ||
| 113 | 133 | var separate_debug_filename: ?[]const u8 = null; |
| 114 | 134 | var separate_debug_crc: ?u32 = null; |
| 115 | 135 | |
| ... | ... | @@ -128,17 +148,35 @@ pub fn load( |
| 128 | 148 | continue; |
| 129 | 149 | } |
| 130 | 150 | |
| 131 | var section_index: ?usize = null; | |
| 132 | inline for (@typeInfo(Dwarf.Section.Id).@"enum".fields, 0..) |sect, i| { | |
| 133 | if (mem.eql(u8, "." ++ sect.name, name)) section_index = i; | |
| 151 | const section_id: union(enum) { | |
| 152 | dwarf: Dwarf.Section.Id, | |
| 153 | eh_frame, | |
| 154 | debug_frame, | |
| 155 | } = s: { | |
| 156 | inline for (@typeInfo(Dwarf.Section.Id).@"enum".fields) |s| { | |
| 157 | if (mem.eql(u8, "." ++ s.name, name)) { | |
| 158 | break :s .{ .dwarf = @enumFromInt(s.value) }; | |
| 159 | } | |
| 160 | } | |
| 161 | if (mem.eql(u8, ".eh_frame", name)) break :s .eh_frame; | |
| 162 | if (mem.eql(u8, ".debug_frame", name)) break :s .debug_frame; | |
| 163 | continue; | |
| 164 | }; | |
| 165 | ||
| 166 | switch (section_id) { | |
| 167 | .dwarf => |i| if (sections[@intFromEnum(i)] != null) continue, | |
| 168 | .eh_frame => if (eh_frame_section != null) continue, | |
| 169 | .debug_frame => if (debug_frame_section != null) continue, | |
| 134 | 170 | } |
| 135 | if (section_index == null) continue; | |
| 136 | if (sections[section_index.?] != null) continue; | |
| 137 | 171 | |
| 138 | 172 | if (mapped_mem.len < shdr.sh_offset + shdr.sh_size) return error.InvalidDebugInfo; |
| 139 | const section_bytes = mapped_mem[@intCast(shdr.sh_offset)..][0..@intCast(shdr.sh_size)]; | |
| 140 | sections[section_index.?] = if ((shdr.sh_flags & elf.SHF_COMPRESSED) > 0) blk: { | |
| 141 | var section_reader: Reader = .fixed(section_bytes); | |
| 173 | const raw_section_bytes = mapped_mem[@intCast(shdr.sh_offset)..][0..@intCast(shdr.sh_size)]; | |
| 174 | ||
| 175 | const section_bytes: []const u8, const section_owned: bool = section: { | |
| 176 | if ((shdr.sh_flags & elf.SHF_COMPRESSED) == 0) { | |
| 177 | break :section .{ raw_section_bytes, false }; | |
| 178 | } | |
| 179 | var section_reader: Reader = .fixed(raw_section_bytes); | |
| 142 | 180 | const chdr = section_reader.takeStruct(elf.Chdr, endian) catch continue; |
| 143 | 181 | if (chdr.ch_type != .ZLIB) continue; |
| 144 | 182 | |
| ... | ... | @@ -153,14 +191,24 @@ pub fn load( |
| 153 | 191 | Dwarf.invalidDebugInfoDetected(); |
| 154 | 192 | continue; |
| 155 | 193 | } |
| 156 | break :blk .{ | |
| 157 | .data = try decompressed_section.toOwnedSlice(gpa), | |
| 158 | .owned = true, | |
| 159 | }; | |
| 160 | } else .{ | |
| 161 | .data = section_bytes, | |
| 162 | .owned = false, | |
| 194 | break :section .{ try decompressed_section.toOwnedSlice(gpa), true }; | |
| 163 | 195 | }; |
| 196 | switch (section_id) { | |
| 197 | .dwarf => |id| sections[@intFromEnum(id)] = .{ | |
| 198 | .data = section_bytes, | |
| 199 | .owned = section_owned, | |
| 200 | }, | |
| 201 | .eh_frame => eh_frame_section = .{ | |
| 202 | .vaddr = shdr.sh_addr, | |
| 203 | .bytes = section_bytes, | |
| 204 | .owned = section_owned, | |
| 205 | }, | |
| 206 | .debug_frame => debug_frame_section = .{ | |
| 207 | .vaddr = shdr.sh_addr, | |
| 208 | .bytes = section_bytes, | |
| 209 | .owned = section_owned, | |
| 210 | }, | |
| 211 | } | |
| 164 | 212 | } |
| 165 | 213 | |
| 166 | 214 | const missing_debug_info = |
| ... | ... | @@ -305,9 +353,11 @@ pub fn load( |
| 305 | 353 | var dwarf: Dwarf = .{ .sections = sections }; |
| 306 | 354 | try dwarf.open(gpa, endian); |
| 307 | 355 | return .{ |
| 356 | .dwarf = dwarf, | |
| 357 | .eh_frame = eh_frame_section, | |
| 358 | .debug_frame = debug_frame_section, | |
| 308 | 359 | .mapped_file = parent_mapped_mem orelse mapped_mem, |
| 309 | 360 | .mapped_debug_file = if (parent_mapped_mem != null) mapped_mem else null, |
| 310 | .dwarf = dwarf, | |
| 311 | 361 | }; |
| 312 | 362 | } |
| 313 | 363 |
lib/std/debug/SelfInfo/ElfModule.zig+55-18| ... | ... | @@ -8,10 +8,10 @@ pub const LookupCache = void; |
| 8 | 8 | |
| 9 | 9 | pub const DebugInfo = struct { |
| 10 | 10 | loaded_elf: ?Dwarf.ElfModule, |
| 11 | unwind: ?Dwarf.Unwind, | |
| 11 | unwind: [2]?Dwarf.Unwind, | |
| 12 | 12 | pub const init: DebugInfo = .{ |
| 13 | 13 | .loaded_elf = null, |
| 14 | .unwind = null, | |
| 14 | .unwind = @splat(null), | |
| 15 | 15 | }; |
| 16 | 16 | pub fn deinit(di: *DebugInfo, gpa: Allocator) void { |
| 17 | 17 | if (di.loaded_elf) |*loaded_elf| loaded_elf.deinit(gpa); |
| ... | ... | @@ -143,30 +143,67 @@ pub fn getSymbolAtAddress(module: *const ElfModule, gpa: Allocator, di: *DebugIn |
| 143 | 143 | => return error.InvalidDebugInfo, |
| 144 | 144 | }; |
| 145 | 145 | } |
| 146 | fn loadUnwindInfo(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) Error!void { | |
| 147 | const section_bytes = module.gnu_eh_frame orelse return error.MissingDebugInfo; // MLUGG TODO: load from file | |
| 148 | ||
| 149 | const section_vaddr: u64 = @intFromPtr(section_bytes.ptr) - module.load_offset; | |
| 150 | const header = Dwarf.Unwind.EhFrameHeader.parse(section_vaddr, section_bytes, @sizeOf(usize), native_endian) catch |err| switch (err) { | |
| 151 | error.ReadFailed => unreachable, // it's all fixed buffers | |
| 152 | error.InvalidDebugInfo => |e| return e, | |
| 153 | error.EndOfStream, error.Overflow => return error.InvalidDebugInfo, | |
| 154 | error.UnsupportedAddrSize => return error.UnsupportedDebugInfo, | |
| 155 | }; | |
| 156 | ||
| 157 | var unwind: Dwarf.Unwind = .initEhFrameHdr(header, section_vaddr, @ptrFromInt(module.load_offset + header.eh_frame_vaddr)); | |
| 146 | fn prepareUnwindLookup(unwind: *Dwarf.Unwind, gpa: Allocator) Error!void { | |
| 158 | 147 | unwind.prepareLookup(gpa, @sizeOf(usize), native_endian) catch |err| switch (err) { |
| 159 | 148 | error.ReadFailed => unreachable, // it's all fixed buffers |
| 160 | 149 | error.InvalidDebugInfo, error.MissingDebugInfo, error.OutOfMemory => |e| return e, |
| 161 | 150 | error.EndOfStream, error.Overflow, error.StreamTooLong => return error.InvalidDebugInfo, |
| 162 | 151 | error.UnsupportedAddrSize, error.UnsupportedDwarfVersion => return error.UnsupportedDebugInfo, |
| 163 | 152 | }; |
| 164 | ||
| 165 | di.unwind = unwind; | |
| 153 | } | |
| 154 | fn loadUnwindInfo(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) Error!void { | |
| 155 | var buf: [2]Dwarf.Unwind = undefined; | |
| 156 | const unwinds: []Dwarf.Unwind = if (module.gnu_eh_frame) |section_bytes| unwinds: { | |
| 157 | const section_vaddr: u64 = @intFromPtr(section_bytes.ptr) - module.load_offset; | |
| 158 | const header = Dwarf.Unwind.EhFrameHeader.parse(section_vaddr, section_bytes, @sizeOf(usize), native_endian) catch |err| switch (err) { | |
| 159 | error.ReadFailed => unreachable, // it's all fixed buffers | |
| 160 | error.InvalidDebugInfo => |e| return e, | |
| 161 | error.EndOfStream, error.Overflow => return error.InvalidDebugInfo, | |
| 162 | error.UnsupportedAddrSize => return error.UnsupportedDebugInfo, | |
| 163 | }; | |
| 164 | buf[0] = .initEhFrameHdr(header, section_vaddr, @ptrFromInt(module.load_offset + header.eh_frame_vaddr)); | |
| 165 | break :unwinds buf[0..1]; | |
| 166 | } else unwinds: { | |
| 167 | // There is no `.eh_frame_hdr` section. There may still be an `.eh_frame` or `.debug_frame` | |
| 168 | // section, but we'll have to load the binary to get at it. | |
| 169 | try module.loadDwarf(gpa, di); | |
| 170 | const opt_debug_frame = &di.loaded_elf.?.debug_frame; | |
| 171 | const opt_eh_frame = &di.loaded_elf.?.eh_frame; | |
| 172 | // If both are present, we can't just pick one -- the info could be split between them. | |
| 173 | // `.debug_frame` is likely to be the more complete section, so we'll prioritize that one. | |
| 174 | if (opt_debug_frame.*) |*debug_frame| { | |
| 175 | buf[0] = .initSection(.debug_frame, debug_frame.vaddr, debug_frame.bytes); | |
| 176 | if (opt_eh_frame.*) |*eh_frame| { | |
| 177 | buf[1] = .initSection(.eh_frame, eh_frame.vaddr, eh_frame.bytes); | |
| 178 | break :unwinds buf[0..2]; | |
| 179 | } | |
| 180 | break :unwinds buf[0..1]; | |
| 181 | } else if (opt_eh_frame.*) |eh_frame| { | |
| 182 | buf[0] = .initSection(.eh_frame, eh_frame.vaddr, eh_frame.bytes); | |
| 183 | break :unwinds buf[0..1]; | |
| 184 | } | |
| 185 | return error.MissingDebugInfo; | |
| 186 | }; | |
| 187 | errdefer for (unwinds) |*u| u.deinit(gpa); | |
| 188 | for (unwinds) |*u| try prepareUnwindLookup(u, gpa); | |
| 189 | switch (unwinds.len) { | |
| 190 | 0 => unreachable, | |
| 191 | 1 => di.unwind = .{ unwinds[0], null }, | |
| 192 | 2 => di.unwind = .{ unwinds[0], unwinds[1] }, | |
| 193 | else => unreachable, | |
| 194 | } | |
| 166 | 195 | } |
| 167 | 196 | pub fn unwindFrame(module: *const ElfModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) Error!usize { |
| 168 | if (di.unwind == null) try module.loadUnwindInfo(gpa, di); | |
| 169 | return context.unwindFrameDwarf(&di.unwind.?, module.load_offset, null); | |
| 197 | if (di.unwind[0] == null) try module.loadUnwindInfo(gpa, di); | |
| 198 | std.debug.assert(di.unwind[0] != null); | |
| 199 | for (&di.unwind) |*opt_unwind| { | |
| 200 | const unwind = &(opt_unwind.* orelse break); | |
| 201 | return context.unwindFrameDwarf(unwind, module.load_offset, null) catch |err| switch (err) { | |
| 202 | error.MissingDebugInfo => continue, // try the next one | |
| 203 | else => |e| return e, | |
| 204 | }; | |
| 205 | } | |
| 206 | return error.MissingDebugInfo; | |
| 170 | 207 | } |
| 171 | 208 | |
| 172 | 209 | const ElfModule = @This(); |