mirror of
https://github.com/ziglang/zig.git
synced 2026-01-20 14:25:16 +00:00
change one million things
This commit is contained in:
parent
b706949736
commit
b750e7cf9e
@ -1083,26 +1083,27 @@ pub const Coff = struct {
|
||||
age: u32 = undefined,
|
||||
|
||||
// The lifetime of `data` must be longer than the lifetime of the returned Coff
|
||||
pub fn init(data: []const u8, is_loaded: bool) !Coff {
|
||||
pub fn init(data: []const u8, is_loaded: bool) error{ EndOfStream, MissingPEHeader }!Coff {
|
||||
const pe_pointer_offset = 0x3C;
|
||||
const pe_magic = "PE\x00\x00";
|
||||
|
||||
var reader: std.Io.Reader = .fixed(data);
|
||||
reader.seek = pe_pointer_offset;
|
||||
const coff_header_offset = try reader.takeInt(u32, .little);
|
||||
reader.seek = coff_header_offset;
|
||||
const is_image = mem.eql(u8, pe_magic, try reader.takeArray(4));
|
||||
if (data.len < pe_pointer_offset + 4) return error.EndOfStream;
|
||||
const header_offset = mem.readInt(u32, data[pe_pointer_offset..][0..4], .little);
|
||||
if (data.len < header_offset + 4) return error.EndOfStream;
|
||||
const is_image = mem.eql(u8, data[header_offset..][0..4], pe_magic);
|
||||
|
||||
var coff = @This(){
|
||||
const coff: Coff = .{
|
||||
.data = data,
|
||||
.is_image = is_image,
|
||||
.is_loaded = is_loaded,
|
||||
.coff_header_offset = coff_header_offset,
|
||||
.coff_header_offset = o: {
|
||||
if (is_image) break :o header_offset + 4;
|
||||
break :o header_offset;
|
||||
},
|
||||
};
|
||||
|
||||
// Do some basic validation upfront
|
||||
if (is_image) {
|
||||
coff.coff_header_offset = coff.coff_header_offset + 4;
|
||||
const coff_header = coff.getCoffHeader();
|
||||
if (coff_header.size_of_optional_header == 0) return error.MissingPEHeader;
|
||||
}
|
||||
|
||||
@ -153,6 +153,7 @@ pub const SourceLocation = struct {
|
||||
};
|
||||
|
||||
pub const Symbol = struct {
|
||||
// MLUGG TODO: remove the defaults and audit everywhere. also grep for '???' across std
|
||||
name: []const u8 = "???",
|
||||
compile_unit_name: []const u8 = "???",
|
||||
source_location: ?SourceLocation = null,
|
||||
@ -232,15 +233,14 @@ pub fn print(comptime fmt: []const u8, args: anytype) void {
|
||||
}
|
||||
|
||||
/// TODO multithreaded awareness
|
||||
var self_debug_info: ?SelfInfo = null;
|
||||
|
||||
pub fn getSelfDebugInfo() !*SelfInfo {
|
||||
if (self_debug_info) |*info| {
|
||||
return info;
|
||||
} else {
|
||||
self_debug_info = try SelfInfo.open(getDebugInfoAllocator());
|
||||
return &self_debug_info.?;
|
||||
}
|
||||
/// Marked `inline` to propagate a comptime-known error to callers.
|
||||
pub inline fn getSelfDebugInfo() !*SelfInfo {
|
||||
if (builtin.strip_debug_info) return error.MissingDebugInfo;
|
||||
if (!SelfInfo.target_supported) return error.UnsupportedOperatingSystem;
|
||||
const S = struct {
|
||||
var self_info: SelfInfo = .init;
|
||||
};
|
||||
return &S.self_info;
|
||||
}
|
||||
|
||||
/// Tries to print a hexadecimal view of the bytes, unbuffered, and ignores any error returned.
|
||||
@ -446,10 +446,7 @@ pub fn dumpStackTraceFromBase(context: *ThreadContext, stderr: *Writer) void {
|
||||
defer it.deinit();
|
||||
|
||||
// DWARF unwinding on aarch64-macos is not complete so we need to get pc address from mcontext
|
||||
const pc_addr = if (builtin.target.os.tag.isDarwin() and native_arch == .aarch64)
|
||||
context.mcontext.ss.pc
|
||||
else
|
||||
it.unwind_state.?.dwarf_context.pc;
|
||||
const pc_addr = it.unwind_state.?.dwarf_context.pc;
|
||||
printSourceAtAddress(debug_info, stderr, pc_addr, tty_config) catch return;
|
||||
|
||||
while (it.next()) |return_address| {
|
||||
@ -460,7 +457,7 @@ pub fn dumpStackTraceFromBase(context: *ThreadContext, stderr: *Writer) void {
|
||||
// an overflow. We do not need to signal `StackIterator` as it will correctly detect this
|
||||
// condition on the subsequent iteration and return `null` thus terminating the loop.
|
||||
// same behaviour for x86-windows-msvc
|
||||
const address = if (return_address == 0) return_address else return_address - 1;
|
||||
const address = return_address -| 1;
|
||||
printSourceAtAddress(debug_info, stderr, address, tty_config) catch return;
|
||||
} else printLastUnwindError(&it, debug_info, stderr, tty_config);
|
||||
}
|
||||
@ -758,7 +755,7 @@ pub fn writeStackTrace(
|
||||
frame_index = (frame_index + 1) % stack_trace.instruction_addresses.len;
|
||||
}) {
|
||||
const return_address = stack_trace.instruction_addresses[frame_index];
|
||||
try printSourceAtAddress(debug_info, writer, return_address - 1, tty_config);
|
||||
try printSourceAtAddress(debug_info, writer, return_address -| 1, tty_config);
|
||||
}
|
||||
|
||||
if (stack_trace.index > stack_trace.instruction_addresses.len) {
|
||||
@ -808,16 +805,11 @@ pub const StackIterator = struct {
|
||||
}
|
||||
|
||||
pub fn initWithContext(first_address: ?usize, debug_info: *SelfInfo, context: *posix.ucontext_t, fp: usize) !StackIterator {
|
||||
// The implementation of DWARF unwinding on aarch64-macos is not complete. However, Apple mandates that
|
||||
// the frame pointer register is always used, so on this platform we can safely use the FP-based unwinder.
|
||||
if (builtin.target.os.tag.isDarwin() and native_arch == .aarch64)
|
||||
return init(first_address, @truncate(context.mcontext.ss.fp));
|
||||
|
||||
if (SelfInfo.supports_unwinding) {
|
||||
var iterator = init(first_address, fp);
|
||||
iterator.unwind_state = .{
|
||||
.debug_info = debug_info,
|
||||
.dwarf_context = try SelfInfo.UnwindContext.init(debug_info.allocator, context),
|
||||
.dwarf_context = try SelfInfo.UnwindContext.init(getDebugInfoAllocator(), context),
|
||||
};
|
||||
return iterator;
|
||||
}
|
||||
@ -890,7 +882,7 @@ pub const StackIterator = struct {
|
||||
if (!unwind_state.failed) {
|
||||
if (unwind_state.dwarf_context.pc == 0) return null;
|
||||
defer it.fp = unwind_state.dwarf_context.getFp() catch 0;
|
||||
if (unwind_state.debug_info.unwindFrame(&unwind_state.dwarf_context)) |return_address| {
|
||||
if (unwind_state.debug_info.unwindFrame(getDebugInfoAllocator(), &unwind_state.dwarf_context)) |return_address| {
|
||||
return return_address;
|
||||
} else |err| {
|
||||
unwind_state.last_error = err;
|
||||
@ -1039,19 +1031,6 @@ pub fn writeStackTraceWindows(
|
||||
}
|
||||
}
|
||||
|
||||
fn printUnknownSource(debug_info: *SelfInfo, writer: *Writer, address: usize, tty_config: tty.Config) !void {
|
||||
const module_name = debug_info.getModuleNameForAddress(address);
|
||||
return printLineInfo(
|
||||
writer,
|
||||
null,
|
||||
address,
|
||||
"???",
|
||||
module_name orelse "???",
|
||||
tty_config,
|
||||
printLineFromFileAnyOs,
|
||||
);
|
||||
}
|
||||
|
||||
fn printLastUnwindError(it: *StackIterator, debug_info: *SelfInfo, writer: *Writer, tty_config: tty.Config) void {
|
||||
if (!have_ucontext) return;
|
||||
if (it.getLastError()) |unwind_error| {
|
||||
@ -1059,32 +1038,48 @@ fn printLastUnwindError(it: *StackIterator, debug_info: *SelfInfo, writer: *Writ
|
||||
}
|
||||
}
|
||||
|
||||
fn printUnwindError(debug_info: *SelfInfo, writer: *Writer, address: usize, err: UnwindError, tty_config: tty.Config) !void {
|
||||
const module_name = debug_info.getModuleNameForAddress(address) orelse "???";
|
||||
fn printUnwindError(debug_info: *SelfInfo, writer: *Writer, address: usize, unwind_err: UnwindError, tty_config: tty.Config) !void {
|
||||
const module_name = debug_info.getModuleNameForAddress(getDebugInfoAllocator(), address) catch |err| switch (err) {
|
||||
error.Unexpected, error.OutOfMemory => |e| return e,
|
||||
error.MissingDebugInfo => "???",
|
||||
};
|
||||
try tty_config.setColor(writer, .dim);
|
||||
if (err == error.MissingDebugInfo) {
|
||||
if (unwind_err == error.MissingDebugInfo) {
|
||||
try writer.print("Unwind information for `{s}:0x{x}` was not available, trace may be incomplete\n\n", .{ module_name, address });
|
||||
} else {
|
||||
try writer.print("Unwind error at address `{s}:0x{x}` ({}), trace may be incomplete\n\n", .{ module_name, address, err });
|
||||
try writer.print("Unwind error at address `{s}:0x{x}` ({}), trace may be incomplete\n\n", .{ module_name, address, unwind_err });
|
||||
}
|
||||
try tty_config.setColor(writer, .reset);
|
||||
}
|
||||
|
||||
pub fn printSourceAtAddress(debug_info: *SelfInfo, writer: *Writer, address: usize, tty_config: tty.Config) !void {
|
||||
const symbol_info = debug_info.getSymbolAtAddress(address) catch |err| switch (err) {
|
||||
error.MissingDebugInfo, error.InvalidDebugInfo => return printUnknownSource(debug_info, writer, address, tty_config),
|
||||
else => return err,
|
||||
const gpa = getDebugInfoAllocator();
|
||||
if (debug_info.getSymbolAtAddress(gpa, address)) |symbol_info| {
|
||||
defer if (symbol_info.source_location) |sl| gpa.free(sl.file_name);
|
||||
return printLineInfo(
|
||||
writer,
|
||||
symbol_info.source_location,
|
||||
address,
|
||||
symbol_info.name,
|
||||
symbol_info.compile_unit_name,
|
||||
tty_config,
|
||||
);
|
||||
} else |err| switch (err) {
|
||||
error.MissingDebugInfo, error.InvalidDebugInfo => {},
|
||||
else => |e| return e,
|
||||
}
|
||||
// Unknown source location, but perhaps we can at least get a module name
|
||||
const compile_unit_name = debug_info.getModuleNameForAddress(getDebugInfoAllocator(), address) catch |err| switch (err) {
|
||||
error.MissingDebugInfo => "???",
|
||||
error.Unexpected, error.OutOfMemory => |e| return e,
|
||||
};
|
||||
defer if (symbol_info.source_location) |sl| debug_info.allocator.free(sl.file_name);
|
||||
|
||||
return printLineInfo(
|
||||
writer,
|
||||
symbol_info.source_location,
|
||||
null,
|
||||
address,
|
||||
symbol_info.name,
|
||||
symbol_info.compile_unit_name,
|
||||
"???",
|
||||
compile_unit_name,
|
||||
tty_config,
|
||||
printLineFromFileAnyOs,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1095,7 +1090,6 @@ fn printLineInfo(
|
||||
symbol_name: []const u8,
|
||||
compile_unit_name: []const u8,
|
||||
tty_config: tty.Config,
|
||||
comptime printLineFromFile: anytype,
|
||||
) !void {
|
||||
nosuspend {
|
||||
try tty_config.setColor(writer, .bold);
|
||||
@ -1136,7 +1130,7 @@ fn printLineInfo(
|
||||
}
|
||||
}
|
||||
|
||||
fn printLineFromFileAnyOs(writer: *Writer, source_location: SourceLocation) !void {
|
||||
fn printLineFromFile(writer: *Writer, source_location: SourceLocation) !void {
|
||||
// Need this to always block even in async I/O mode, because this could potentially
|
||||
// be called from e.g. the event loop code crashing.
|
||||
var f = try fs.cwd().openFile(source_location.file_name, .{});
|
||||
@ -1190,7 +1184,7 @@ fn printLineFromFileAnyOs(writer: *Writer, source_location: SourceLocation) !voi
|
||||
}
|
||||
}
|
||||
|
||||
test printLineFromFileAnyOs {
|
||||
test printLineFromFile {
|
||||
var aw: Writer.Allocating = .init(std.testing.allocator);
|
||||
defer aw.deinit();
|
||||
const output_stream = &aw.writer;
|
||||
@ -1212,9 +1206,9 @@ test printLineFromFileAnyOs {
|
||||
defer allocator.free(path);
|
||||
try test_dir.dir.writeFile(.{ .sub_path = "one_line.zig", .data = "no new lines in this file, but one is printed anyway" });
|
||||
|
||||
try expectError(error.EndOfFile, printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 }));
|
||||
try expectError(error.EndOfFile, printLineFromFile(output_stream, .{ .file_name = path, .line = 2, .column = 0 }));
|
||||
|
||||
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
|
||||
try printLineFromFile(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
|
||||
try expectEqualStrings("no new lines in this file, but one is printed anyway\n", aw.written());
|
||||
aw.clearRetainingCapacity();
|
||||
}
|
||||
@ -1230,11 +1224,11 @@ test printLineFromFileAnyOs {
|
||||
,
|
||||
});
|
||||
|
||||
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
|
||||
try printLineFromFile(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
|
||||
try expectEqualStrings("1\n", aw.written());
|
||||
aw.clearRetainingCapacity();
|
||||
|
||||
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 3, .column = 0 });
|
||||
try printLineFromFile(output_stream, .{ .file_name = path, .line = 3, .column = 0 });
|
||||
try expectEqualStrings("3\n", aw.written());
|
||||
aw.clearRetainingCapacity();
|
||||
}
|
||||
@ -1253,7 +1247,7 @@ test printLineFromFileAnyOs {
|
||||
try writer.splatByteAll('a', overlap);
|
||||
try writer.flush();
|
||||
|
||||
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 });
|
||||
try printLineFromFile(output_stream, .{ .file_name = path, .line = 2, .column = 0 });
|
||||
try expectEqualStrings(("a" ** overlap) ++ "\n", aw.written());
|
||||
aw.clearRetainingCapacity();
|
||||
}
|
||||
@ -1267,7 +1261,7 @@ test printLineFromFileAnyOs {
|
||||
const writer = &file_writer.interface;
|
||||
try writer.splatByteAll('a', std.heap.page_size_max);
|
||||
|
||||
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
|
||||
try printLineFromFile(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
|
||||
try expectEqualStrings(("a" ** std.heap.page_size_max) ++ "\n", aw.written());
|
||||
aw.clearRetainingCapacity();
|
||||
}
|
||||
@ -1281,19 +1275,19 @@ test printLineFromFileAnyOs {
|
||||
const writer = &file_writer.interface;
|
||||
try writer.splatByteAll('a', 3 * std.heap.page_size_max);
|
||||
|
||||
try expectError(error.EndOfFile, printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 }));
|
||||
try expectError(error.EndOfFile, printLineFromFile(output_stream, .{ .file_name = path, .line = 2, .column = 0 }));
|
||||
|
||||
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
|
||||
try printLineFromFile(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
|
||||
try expectEqualStrings(("a" ** (3 * std.heap.page_size_max)) ++ "\n", aw.written());
|
||||
aw.clearRetainingCapacity();
|
||||
|
||||
try writer.writeAll("a\na");
|
||||
|
||||
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
|
||||
try printLineFromFile(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
|
||||
try expectEqualStrings(("a" ** (3 * std.heap.page_size_max)) ++ "a\n", aw.written());
|
||||
aw.clearRetainingCapacity();
|
||||
|
||||
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 });
|
||||
try printLineFromFile(output_stream, .{ .file_name = path, .line = 2, .column = 0 });
|
||||
try expectEqualStrings("a\n", aw.written());
|
||||
aw.clearRetainingCapacity();
|
||||
}
|
||||
@ -1309,26 +1303,23 @@ test printLineFromFileAnyOs {
|
||||
try writer.splatByteAll('\n', real_file_start);
|
||||
try writer.writeAll("abc\ndef");
|
||||
|
||||
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = real_file_start + 1, .column = 0 });
|
||||
try printLineFromFile(output_stream, .{ .file_name = path, .line = real_file_start + 1, .column = 0 });
|
||||
try expectEqualStrings("abc\n", aw.written());
|
||||
aw.clearRetainingCapacity();
|
||||
|
||||
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = real_file_start + 2, .column = 0 });
|
||||
try printLineFromFile(output_stream, .{ .file_name = path, .line = real_file_start + 2, .column = 0 });
|
||||
try expectEqualStrings("def\n", aw.written());
|
||||
aw.clearRetainingCapacity();
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO multithreaded awareness
|
||||
var debug_info_allocator: ?mem.Allocator = null;
|
||||
var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined;
|
||||
var debug_info_arena: ?std.heap.ArenaAllocator = null;
|
||||
fn getDebugInfoAllocator() mem.Allocator {
|
||||
if (debug_info_allocator) |a| return a;
|
||||
|
||||
debug_info_arena_allocator = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||
const allocator = debug_info_arena_allocator.allocator();
|
||||
debug_info_allocator = allocator;
|
||||
return allocator;
|
||||
if (debug_info_arena == null) {
|
||||
debug_info_arena = .init(std.heap.page_allocator);
|
||||
}
|
||||
return debug_info_arena.?.allocator();
|
||||
}
|
||||
|
||||
/// Whether or not the current target can print useful debug information when a segfault occurs.
|
||||
|
||||
@ -78,17 +78,6 @@ pub const Section = struct {
|
||||
debug_addr,
|
||||
debug_names,
|
||||
};
|
||||
|
||||
// For sections that are not memory mapped by the loader, this is an offset
|
||||
// from `data.ptr` to where the section would have been mapped. Otherwise,
|
||||
// `data` is directly backed by the section and the offset is zero.
|
||||
pub fn virtualOffset(self: Section, base_address: usize) i64 {
|
||||
return if (self.virtual_address) |va|
|
||||
@as(i64, @intCast(base_address + va)) -
|
||||
@as(i64, @intCast(@intFromPtr(self.data.ptr)))
|
||||
else
|
||||
0;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Abbrev = struct {
|
||||
@ -342,10 +331,6 @@ pub fn section(di: Dwarf, dwarf_section: Section.Id) ?[]const u8 {
|
||||
return if (di.sections[@intFromEnum(dwarf_section)]) |s| s.data else null;
|
||||
}
|
||||
|
||||
pub fn sectionVirtualOffset(di: Dwarf, dwarf_section: Section.Id, base_address: usize) ?i64 {
|
||||
return if (di.sections[@intFromEnum(dwarf_section)]) |s| s.virtualOffset(base_address) else null;
|
||||
}
|
||||
|
||||
pub fn deinit(di: *Dwarf, gpa: Allocator) void {
|
||||
for (di.sections) |opt_section| {
|
||||
if (opt_section) |s| if (s.owned) gpa.free(s.data);
|
||||
@ -364,8 +349,6 @@ pub fn deinit(di: *Dwarf, gpa: Allocator) void {
|
||||
}
|
||||
di.compile_unit_list.deinit(gpa);
|
||||
di.func_list.deinit(gpa);
|
||||
di.cie_map.deinit(gpa);
|
||||
di.fde_list.deinit(gpa);
|
||||
di.ranges.deinit(gpa);
|
||||
di.* = undefined;
|
||||
}
|
||||
@ -983,8 +966,8 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, endian: Endian, compile_unit:
|
||||
},
|
||||
0,
|
||||
};
|
||||
_ = addr_size;
|
||||
_ = seg_size;
|
||||
if (seg_size != 0) return bad(); // unsupported
|
||||
_ = addr_size; // TODO: ignoring this is incorrect, we should use it to decide address lengths
|
||||
|
||||
const prologue_length = try readAddress(&fr, unit_header.format, endian);
|
||||
const prog_start_offset = fr.seek + prologue_length;
|
||||
@ -1472,44 +1455,27 @@ pub const ElfModule = struct {
|
||||
mapped_memory: ?[]align(std.heap.page_size_min) const u8,
|
||||
external_mapped_memory: ?[]align(std.heap.page_size_min) const u8,
|
||||
|
||||
pub const Lookup = struct {
|
||||
base_address: usize,
|
||||
name: []const u8,
|
||||
build_id: ?[]const u8,
|
||||
gnu_eh_frame: ?[]const u8,
|
||||
pub const init: ElfModule = .{
|
||||
.unwind = .{
|
||||
.debug_frame = null,
|
||||
.eh_frame = null,
|
||||
},
|
||||
.dwarf = .{},
|
||||
.mapped_memory = null,
|
||||
.external_mapped_memory = null,
|
||||
};
|
||||
|
||||
pub fn init(lookup: *const Lookup) ElfModule {
|
||||
var em: ElfModule = .{
|
||||
.unwind = .{
|
||||
.sections = @splat(null),
|
||||
},
|
||||
.dwarf = .{},
|
||||
.mapped_memory = null,
|
||||
.external_mapped_memory = null,
|
||||
};
|
||||
if (lookup.gnu_eh_frame) |eh_frame_hdr| {
|
||||
// This is a special case - pointer offsets inside .eh_frame_hdr
|
||||
// are encoded relative to its base address, so we must use the
|
||||
// version that is already memory mapped, and not the one that
|
||||
// will be mapped separately from the ELF file.
|
||||
em.unwind.sections[@intFromEnum(Dwarf.Unwind.Section.Id.eh_frame_hdr)] = .{
|
||||
.data = eh_frame_hdr,
|
||||
};
|
||||
}
|
||||
return em;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *@This(), allocator: Allocator) void {
|
||||
self.dwarf.deinit(allocator);
|
||||
std.posix.munmap(self.mapped_memory);
|
||||
if (self.external_mapped_memory) |m| std.posix.munmap(m);
|
||||
}
|
||||
|
||||
pub fn getSymbolAtAddress(self: *@This(), allocator: Allocator, endian: Endian, base_address: usize, address: usize) !std.debug.Symbol {
|
||||
// Translate the VA into an address into this object
|
||||
const relocated_address = address - base_address;
|
||||
return self.dwarf.getSymbol(allocator, endian, relocated_address);
|
||||
pub fn getSymbolAtAddress(self: *@This(), allocator: Allocator, endian: Endian, load_offset: usize, address: usize) !std.debug.Symbol {
|
||||
// Translate the runtime address into a virtual address into the module
|
||||
// MLUGG TODO: this clearly tells us that the logic should live near SelfInfo...
|
||||
const vaddr = address - load_offset;
|
||||
return self.dwarf.getSymbol(allocator, endian, vaddr);
|
||||
}
|
||||
|
||||
pub fn getDwarfUnwindForAddress(self: *@This(), allocator: Allocator, address: usize) !?*Dwarf.Unwind {
|
||||
@ -1548,7 +1514,7 @@ pub const ElfModule = struct {
|
||||
mapped_mem: []align(std.heap.page_size_min) const u8,
|
||||
build_id: ?[]const u8,
|
||||
expected_crc: ?u32,
|
||||
parent_sections: *Dwarf.SectionArray,
|
||||
parent_sections: ?*Dwarf.SectionArray,
|
||||
parent_mapped_mem: ?[]align(std.heap.page_size_min) const u8,
|
||||
elf_filename: ?[]const u8,
|
||||
) LoadError!void {
|
||||
@ -1577,10 +1543,12 @@ pub const ElfModule = struct {
|
||||
var sections: Dwarf.SectionArray = @splat(null);
|
||||
|
||||
// Combine section list. This takes ownership over any owned sections from the parent scope.
|
||||
for (parent_sections, §ions) |*parent, *section_elem| {
|
||||
if (parent.*) |*p| {
|
||||
section_elem.* = p.*;
|
||||
p.owned = false;
|
||||
if (parent_sections) |ps| {
|
||||
for (ps, §ions) |*parent, *section_elem| {
|
||||
if (parent.*) |*p| {
|
||||
section_elem.* = p.*;
|
||||
p.owned = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
errdefer for (sections) |opt_section| if (opt_section) |s| if (s.owned) gpa.free(s.data);
|
||||
@ -1647,7 +1615,6 @@ pub const ElfModule = struct {
|
||||
// Attempt to load debug info from an external file
|
||||
// See: https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
|
||||
if (missing_debug_info) {
|
||||
|
||||
// Only allow one level of debug info nesting
|
||||
if (parent_mapped_mem) |_| {
|
||||
return error.MissingDebugInfo;
|
||||
@ -1775,6 +1742,7 @@ pub const ElfModule = struct {
|
||||
|
||||
em.mapped_memory = parent_mapped_mem orelse mapped_mem;
|
||||
em.external_mapped_memory = if (parent_mapped_mem != null) mapped_mem else null;
|
||||
em.dwarf.sections = sections;
|
||||
try em.dwarf.open(gpa, endian);
|
||||
}
|
||||
|
||||
@ -1844,7 +1812,8 @@ pub fn chopSlice(ptr: []const u8, offset: u64, size: u64) error{Overflow}![]cons
|
||||
return ptr[start..end];
|
||||
}
|
||||
|
||||
pub fn readAddress(r: *Reader, format: std.dwarf.Format, endian: Endian) !u64 {
|
||||
fn readAddress(r: *Reader, format: std.dwarf.Format, endian: Endian) !u64 {
|
||||
// MLUGG TODO FIX BEFORE MERGE: this function is slightly bogus. addresses have a byte width which is independent of the `dwarf.Format`!
|
||||
return switch (format) {
|
||||
.@"32" => try r.takeInt(u32, endian),
|
||||
.@"64" => try r.takeInt(u64, endian),
|
||||
@ -1852,6 +1821,8 @@ pub fn readAddress(r: *Reader, format: std.dwarf.Format, endian: Endian) !u64 {
|
||||
}
|
||||
|
||||
fn nativeFormat() std.dwarf.Format {
|
||||
// MLUGG TODO FIX BEFORE MERGE: this is nonsensical. this is neither what `dwarf.Format` is for, nor does it make sense to check the NATIVE FUCKING FORMAT
|
||||
// when parsing ARBITRARY DWARF.
|
||||
return switch (@sizeOf(usize)) {
|
||||
4 => .@"32",
|
||||
8 => .@"64",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
298
lib/std/debug/Dwarf/Unwind/VirtualMachine.zig
Normal file
298
lib/std/debug/Dwarf/Unwind/VirtualMachine.zig
Normal file
@ -0,0 +1,298 @@
|
||||
//! Virtual machine that evaluates DWARF call frame instructions
|
||||
|
||||
/// See section 6.4.1 of the DWARF5 specification for details on each
|
||||
pub const RegisterRule = union(enum) {
|
||||
/// The spec says that the default rule for each column is the undefined rule.
|
||||
/// However, it also allows ABI / compiler authors to specify alternate defaults, so
|
||||
/// there is a distinction made here.
|
||||
default: void,
|
||||
undefined: void,
|
||||
same_value: void,
|
||||
/// offset(N)
|
||||
offset: i64,
|
||||
/// val_offset(N)
|
||||
val_offset: i64,
|
||||
/// register(R)
|
||||
register: u8,
|
||||
/// expression(E)
|
||||
expression: []const u8,
|
||||
/// val_expression(E)
|
||||
val_expression: []const u8,
|
||||
/// Augmenter-defined rule
|
||||
architectural: void,
|
||||
};
|
||||
|
||||
/// Each row contains unwinding rules for a set of registers.
|
||||
pub const Row = struct {
|
||||
/// Offset from `FrameDescriptionEntry.pc_begin`
|
||||
offset: u64 = 0,
|
||||
/// Special-case column that defines the CFA (Canonical Frame Address) rule.
|
||||
/// The register field of this column defines the register that CFA is derived from.
|
||||
cfa: Column = .{},
|
||||
/// The register fields in these columns define the register the rule applies to.
|
||||
columns: ColumnRange = .{},
|
||||
/// Indicates that the next write to any column in this row needs to copy
|
||||
/// the backing column storage first, as it may be referenced by previous rows.
|
||||
copy_on_write: bool = false,
|
||||
};
|
||||
|
||||
pub const Column = struct {
|
||||
register: ?u8 = null,
|
||||
rule: RegisterRule = .{ .default = {} },
|
||||
};
|
||||
|
||||
const ColumnRange = struct {
|
||||
/// Index into `columns` of the first column in this row.
|
||||
start: usize = undefined,
|
||||
len: u8 = 0,
|
||||
};
|
||||
|
||||
columns: std.ArrayList(Column) = .empty,
|
||||
stack: std.ArrayList(ColumnRange) = .empty,
|
||||
current_row: Row = .{},
|
||||
|
||||
/// The result of executing the CIE's initial_instructions
|
||||
cie_row: ?Row = null,
|
||||
|
||||
pub fn deinit(self: *VirtualMachine, gpa: Allocator) void {
|
||||
self.stack.deinit(gpa);
|
||||
self.columns.deinit(gpa);
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
pub fn reset(self: *VirtualMachine) void {
|
||||
self.stack.clearRetainingCapacity();
|
||||
self.columns.clearRetainingCapacity();
|
||||
self.current_row = .{};
|
||||
self.cie_row = null;
|
||||
}
|
||||
|
||||
/// Return a slice backed by the row's non-CFA columns
|
||||
pub fn rowColumns(self: VirtualMachine, row: Row) []Column {
|
||||
if (row.columns.len == 0) return &.{};
|
||||
return self.columns.items[row.columns.start..][0..row.columns.len];
|
||||
}
|
||||
|
||||
/// Either retrieves or adds a column for `register` (non-CFA) in the current row.
|
||||
fn getOrAddColumn(self: *VirtualMachine, gpa: Allocator, register: u8) !*Column {
|
||||
for (self.rowColumns(self.current_row)) |*c| {
|
||||
if (c.register == register) return c;
|
||||
}
|
||||
|
||||
if (self.current_row.columns.len == 0) {
|
||||
self.current_row.columns.start = self.columns.items.len;
|
||||
}
|
||||
self.current_row.columns.len += 1;
|
||||
|
||||
const column = try self.columns.addOne(gpa);
|
||||
column.* = .{
|
||||
.register = register,
|
||||
};
|
||||
|
||||
return column;
|
||||
}
|
||||
|
||||
/// Runs the CIE instructions, then the FDE instructions. Execution halts
|
||||
/// once the row that corresponds to `pc` is known, and the row is returned.
|
||||
pub fn runTo(
|
||||
self: *VirtualMachine,
|
||||
gpa: Allocator,
|
||||
pc: u64,
|
||||
cie: Dwarf.Unwind.CommonInformationEntry,
|
||||
fde: Dwarf.Unwind.FrameDescriptionEntry,
|
||||
addr_size_bytes: u8,
|
||||
endian: std.builtin.Endian,
|
||||
) !Row {
|
||||
assert(self.cie_row == null);
|
||||
assert(pc >= fde.pc_begin);
|
||||
assert(pc < fde.pc_begin + fde.pc_range);
|
||||
|
||||
var prev_row: Row = self.current_row;
|
||||
|
||||
const instruction_slices: [2][]const u8 = .{
|
||||
cie.initial_instructions,
|
||||
fde.instructions,
|
||||
};
|
||||
for (instruction_slices, [2]bool{ true, false }) |slice, is_cie_stream| {
|
||||
var stream: std.Io.Reader = .fixed(slice);
|
||||
while (stream.seek < slice.len) {
|
||||
const instruction: Dwarf.call_frame.Instruction = try .read(&stream, addr_size_bytes, endian);
|
||||
prev_row = try self.step(gpa, cie, is_cie_stream, instruction);
|
||||
if (pc < fde.pc_begin + self.current_row.offset) return prev_row;
|
||||
}
|
||||
}
|
||||
|
||||
return self.current_row;
|
||||
}
|
||||
|
||||
fn resolveCopyOnWrite(self: *VirtualMachine, gpa: Allocator) !void {
|
||||
if (!self.current_row.copy_on_write) return;
|
||||
|
||||
const new_start = self.columns.items.len;
|
||||
if (self.current_row.columns.len > 0) {
|
||||
try self.columns.ensureUnusedCapacity(gpa, self.current_row.columns.len);
|
||||
self.columns.appendSliceAssumeCapacity(self.rowColumns(self.current_row));
|
||||
self.current_row.columns.start = new_start;
|
||||
}
|
||||
}
|
||||
|
||||
/// Executes a single instruction.
|
||||
/// If this instruction is from the CIE, `is_initial` should be set.
|
||||
/// Returns the value of `current_row` before executing this instruction.
|
||||
pub fn step(
|
||||
self: *VirtualMachine,
|
||||
gpa: Allocator,
|
||||
cie: Dwarf.Unwind.CommonInformationEntry,
|
||||
is_initial: bool,
|
||||
instruction: Dwarf.call_frame.Instruction,
|
||||
) !Row {
|
||||
// CIE instructions must be run before FDE instructions
|
||||
assert(!is_initial or self.cie_row == null);
|
||||
if (!is_initial and self.cie_row == null) {
|
||||
self.cie_row = self.current_row;
|
||||
self.current_row.copy_on_write = true;
|
||||
}
|
||||
|
||||
const prev_row = self.current_row;
|
||||
switch (instruction) {
|
||||
.set_loc => |i| {
|
||||
if (i.address <= self.current_row.offset) return error.InvalidOperation;
|
||||
if (cie.segment_selector_size != 0) return error.InvalidOperation; // unsupported
|
||||
// TODO: Check cie.segment_selector_size != 0 for DWARFV4
|
||||
self.current_row.offset = i.address;
|
||||
},
|
||||
inline .advance_loc,
|
||||
.advance_loc1,
|
||||
.advance_loc2,
|
||||
.advance_loc4,
|
||||
=> |i| {
|
||||
self.current_row.offset += i.delta * cie.code_alignment_factor;
|
||||
self.current_row.copy_on_write = true;
|
||||
},
|
||||
inline .offset,
|
||||
.offset_extended,
|
||||
.offset_extended_sf,
|
||||
=> |i| {
|
||||
try self.resolveCopyOnWrite(gpa);
|
||||
const column = try self.getOrAddColumn(gpa, i.register);
|
||||
column.rule = .{ .offset = @as(i64, @intCast(i.offset)) * cie.data_alignment_factor };
|
||||
},
|
||||
inline .restore,
|
||||
.restore_extended,
|
||||
=> |i| {
|
||||
try self.resolveCopyOnWrite(gpa);
|
||||
if (self.cie_row) |cie_row| {
|
||||
const column = try self.getOrAddColumn(gpa, i.register);
|
||||
column.rule = for (self.rowColumns(cie_row)) |cie_column| {
|
||||
if (cie_column.register == i.register) break cie_column.rule;
|
||||
} else .{ .default = {} };
|
||||
} else return error.InvalidOperation;
|
||||
},
|
||||
.nop => {},
|
||||
.undefined => |i| {
|
||||
try self.resolveCopyOnWrite(gpa);
|
||||
const column = try self.getOrAddColumn(gpa, i.register);
|
||||
column.rule = .{ .undefined = {} };
|
||||
},
|
||||
.same_value => |i| {
|
||||
try self.resolveCopyOnWrite(gpa);
|
||||
const column = try self.getOrAddColumn(gpa, i.register);
|
||||
column.rule = .{ .same_value = {} };
|
||||
},
|
||||
.register => |i| {
|
||||
try self.resolveCopyOnWrite(gpa);
|
||||
const column = try self.getOrAddColumn(gpa, i.register);
|
||||
column.rule = .{ .register = i.target_register };
|
||||
},
|
||||
.remember_state => {
|
||||
try self.stack.append(gpa, self.current_row.columns);
|
||||
self.current_row.copy_on_write = true;
|
||||
},
|
||||
.restore_state => {
|
||||
const restored_columns = self.stack.pop() orelse return error.InvalidOperation;
|
||||
self.columns.shrinkRetainingCapacity(self.columns.items.len - self.current_row.columns.len);
|
||||
try self.columns.ensureUnusedCapacity(gpa, restored_columns.len);
|
||||
|
||||
self.current_row.columns.start = self.columns.items.len;
|
||||
self.current_row.columns.len = restored_columns.len;
|
||||
self.columns.appendSliceAssumeCapacity(self.columns.items[restored_columns.start..][0..restored_columns.len]);
|
||||
},
|
||||
.def_cfa => |i| {
|
||||
try self.resolveCopyOnWrite(gpa);
|
||||
self.current_row.cfa = .{
|
||||
.register = i.register,
|
||||
.rule = .{ .val_offset = @intCast(i.offset) },
|
||||
};
|
||||
},
|
||||
.def_cfa_sf => |i| {
|
||||
try self.resolveCopyOnWrite(gpa);
|
||||
self.current_row.cfa = .{
|
||||
.register = i.register,
|
||||
.rule = .{ .val_offset = i.offset * cie.data_alignment_factor },
|
||||
};
|
||||
},
|
||||
.def_cfa_register => |i| {
|
||||
try self.resolveCopyOnWrite(gpa);
|
||||
if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .val_offset) return error.InvalidOperation;
|
||||
self.current_row.cfa.register = i.register;
|
||||
},
|
||||
.def_cfa_offset => |i| {
|
||||
try self.resolveCopyOnWrite(gpa);
|
||||
if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .val_offset) return error.InvalidOperation;
|
||||
self.current_row.cfa.rule = .{
|
||||
.val_offset = @intCast(i.offset),
|
||||
};
|
||||
},
|
||||
.def_cfa_offset_sf => |i| {
|
||||
try self.resolveCopyOnWrite(gpa);
|
||||
if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .val_offset) return error.InvalidOperation;
|
||||
self.current_row.cfa.rule = .{
|
||||
.val_offset = i.offset * cie.data_alignment_factor,
|
||||
};
|
||||
},
|
||||
.def_cfa_expression => |i| {
|
||||
try self.resolveCopyOnWrite(gpa);
|
||||
self.current_row.cfa.register = undefined;
|
||||
self.current_row.cfa.rule = .{
|
||||
.expression = i.block,
|
||||
};
|
||||
},
|
||||
.expression => |i| {
|
||||
try self.resolveCopyOnWrite(gpa);
|
||||
const column = try self.getOrAddColumn(gpa, i.register);
|
||||
column.rule = .{
|
||||
.expression = i.block,
|
||||
};
|
||||
},
|
||||
.val_offset => |i| {
|
||||
try self.resolveCopyOnWrite(gpa);
|
||||
const column = try self.getOrAddColumn(gpa, i.register);
|
||||
column.rule = .{
|
||||
.val_offset = @as(i64, @intCast(i.offset)) * cie.data_alignment_factor,
|
||||
};
|
||||
},
|
||||
.val_offset_sf => |i| {
|
||||
try self.resolveCopyOnWrite(gpa);
|
||||
const column = try self.getOrAddColumn(gpa, i.register);
|
||||
column.rule = .{
|
||||
.val_offset = i.offset * cie.data_alignment_factor,
|
||||
};
|
||||
},
|
||||
.val_expression => |i| {
|
||||
try self.resolveCopyOnWrite(gpa);
|
||||
const column = try self.getOrAddColumn(gpa, i.register);
|
||||
column.rule = .{
|
||||
.val_expression = i.block,
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
return prev_row;
|
||||
}
|
||||
|
||||
const std = @import("../../../std.zig");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const Dwarf = std.debug.Dwarf;
|
||||
|
||||
const VirtualMachine = @This();
|
||||
@ -1,12 +1,5 @@
|
||||
const builtin = @import("builtin");
|
||||
const std = @import("../../std.zig");
|
||||
const mem = std.mem;
|
||||
const debug = std.debug;
|
||||
const leb = std.leb;
|
||||
const DW = std.dwarf;
|
||||
const abi = std.debug.Dwarf.abi;
|
||||
const assert = std.debug.assert;
|
||||
const native_endian = builtin.cpu.arch.endian();
|
||||
const Reader = std.Io.Reader;
|
||||
|
||||
/// TODO merge with std.dwarf.CFA
|
||||
const Opcode = enum(u8) {
|
||||
@ -51,9 +44,13 @@ const Opcode = enum(u8) {
|
||||
pub const hi_user = 0x3f;
|
||||
};
|
||||
|
||||
fn readBlock(reader: *std.Io.Reader) ![]const u8 {
|
||||
/// The returned slice points into `reader.buffer`.
|
||||
fn readBlock(reader: *Reader) ![]const u8 {
|
||||
const block_len = try reader.takeLeb128(usize);
|
||||
return reader.take(block_len);
|
||||
return reader.take(block_len) catch |err| switch (err) {
|
||||
error.EndOfStream => return error.InvalidOperand,
|
||||
error.ReadFailed => |e| return e,
|
||||
};
|
||||
}
|
||||
|
||||
pub const Instruction = union(Opcode) {
|
||||
@ -140,8 +137,9 @@ pub const Instruction = union(Opcode) {
|
||||
block: []const u8,
|
||||
},
|
||||
|
||||
/// `reader` must be a `Reader.fixed` so that regions of its buffer are never invalidated.
|
||||
pub fn read(
|
||||
reader: *std.Io.Reader,
|
||||
reader: *Reader,
|
||||
addr_size_bytes: u8,
|
||||
endian: std.builtin.Endian,
|
||||
) !Instruction {
|
||||
@ -173,16 +171,14 @@ pub const Instruction = union(Opcode) {
|
||||
.restore,
|
||||
=> unreachable,
|
||||
.nop => .{ .nop = {} },
|
||||
.set_loc => .{
|
||||
.set_loc = .{
|
||||
.address = switch (addr_size_bytes) {
|
||||
2 => try reader.takeInt(u16, endian),
|
||||
4 => try reader.takeInt(u32, endian),
|
||||
8 => try reader.takeInt(u64, endian),
|
||||
else => return error.InvalidAddrSize,
|
||||
},
|
||||
.set_loc => .{ .set_loc = .{
|
||||
.address = switch (addr_size_bytes) {
|
||||
2 => try reader.takeInt(u16, endian),
|
||||
4 => try reader.takeInt(u32, endian),
|
||||
8 => try reader.takeInt(u64, endian),
|
||||
else => return error.UnsupportedAddrSize,
|
||||
},
|
||||
},
|
||||
} },
|
||||
.advance_loc1 => .{
|
||||
.advance_loc1 = .{ .delta = try reader.takeByte() },
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,27 +1,32 @@
|
||||
pub const PE = struct {
|
||||
pub const absptr = 0x00;
|
||||
pub const PE = packed struct(u8) {
|
||||
type: Type,
|
||||
rel: Rel,
|
||||
|
||||
pub const size_mask = 0x7;
|
||||
pub const sign_mask = 0x8;
|
||||
pub const type_mask = size_mask | sign_mask;
|
||||
/// This is a special encoding which does not correspond to named `type`/`rel` values.
|
||||
pub const omit: PE = @bitCast(@as(u8, 0xFF));
|
||||
|
||||
pub const uleb128 = 0x01;
|
||||
pub const udata2 = 0x02;
|
||||
pub const udata4 = 0x03;
|
||||
pub const udata8 = 0x04;
|
||||
pub const sleb128 = 0x09;
|
||||
pub const sdata2 = 0x0A;
|
||||
pub const sdata4 = 0x0B;
|
||||
pub const sdata8 = 0x0C;
|
||||
pub const Type = enum(u4) {
|
||||
absptr = 0x0,
|
||||
uleb128 = 0x1,
|
||||
udata2 = 0x2,
|
||||
udata4 = 0x3,
|
||||
udata8 = 0x4,
|
||||
sleb128 = 0x9,
|
||||
sdata2 = 0xA,
|
||||
sdata4 = 0xB,
|
||||
sdata8 = 0xC,
|
||||
_,
|
||||
};
|
||||
|
||||
pub const rel_mask = 0x70;
|
||||
pub const pcrel = 0x10;
|
||||
pub const textrel = 0x20;
|
||||
pub const datarel = 0x30;
|
||||
pub const funcrel = 0x40;
|
||||
pub const aligned = 0x50;
|
||||
|
||||
pub const indirect = 0x80;
|
||||
|
||||
pub const omit = 0xff;
|
||||
pub const Rel = enum(u4) {
|
||||
abs = 0x0,
|
||||
pcrel = 0x1,
|
||||
textrel = 0x2,
|
||||
datarel = 0x3,
|
||||
funcrel = 0x4,
|
||||
aligned = 0x5,
|
||||
/// Undocumented GCC extension
|
||||
indirect = 0x8,
|
||||
_,
|
||||
};
|
||||
};
|
||||
|
||||
@ -839,62 +839,112 @@ pub const nlist = extern struct {
|
||||
|
||||
pub const nlist_64 = extern struct {
|
||||
n_strx: u32,
|
||||
n_type: u8,
|
||||
n_type: packed union {
|
||||
bits: packed struct(u8) {
|
||||
ext: bool,
|
||||
type: enum(u3) {
|
||||
undf = 0,
|
||||
abs = 1,
|
||||
sect = 7,
|
||||
pbud = 6,
|
||||
indr = 5,
|
||||
_,
|
||||
},
|
||||
pext: bool,
|
||||
/// Any non-zero value indicates this is an stab, so the `stab` field should be used.
|
||||
is_stab: u3,
|
||||
},
|
||||
stab: enum(u8) {
|
||||
gsym = N_GSYM,
|
||||
fname = N_FNAME,
|
||||
fun = N_FUN,
|
||||
stsym = N_STSYM,
|
||||
lcsym = N_LCSYM,
|
||||
bnsym = N_BNSYM,
|
||||
ast = N_AST,
|
||||
opt = N_OPT,
|
||||
rsym = N_RSYM,
|
||||
sline = N_SLINE,
|
||||
ensym = N_ENSYM,
|
||||
ssym = N_SSYM,
|
||||
so = N_SO,
|
||||
oso = N_OSO,
|
||||
lsym = N_LSYM,
|
||||
bincl = N_BINCL,
|
||||
sol = N_SOL,
|
||||
params = N_PARAMS,
|
||||
version = N_VERSION,
|
||||
olevel = N_OLEVEL,
|
||||
psym = N_PSYM,
|
||||
eincl = N_EINCL,
|
||||
entry = N_ENTRY,
|
||||
lbrac = N_LBRAC,
|
||||
excl = N_EXCL,
|
||||
rbrac = N_RBRAC,
|
||||
bcomm = N_BCOMM,
|
||||
ecomm = N_ECOMM,
|
||||
ecoml = N_ECOML,
|
||||
leng = N_LENG,
|
||||
_,
|
||||
},
|
||||
},
|
||||
n_sect: u8,
|
||||
n_desc: u16,
|
||||
n_desc: packed struct(u16) {
|
||||
_pad0: u3 = 0,
|
||||
arm_thumb_def: bool,
|
||||
_pad1: u1 = 0,
|
||||
/// The meaning of this bit is contextual.
|
||||
/// See `N_DESC_DISCARDED` and `N_NO_DEAD_STRIP`.
|
||||
discarded_or_no_dead_strip: bool,
|
||||
weak_ref: bool,
|
||||
/// The meaning of this bit is contextual.
|
||||
/// See `N_WEAK_DEF` and `N_REF_TO_WEAK`.
|
||||
weak_def_or_ref_to_weak: bool,
|
||||
symbol_resolver: bool,
|
||||
alt_entry: bool,
|
||||
_pad2: u6 = 0,
|
||||
},
|
||||
n_value: u64,
|
||||
|
||||
// MLUGG TODO DELETE
|
||||
pub fn stab(sym: nlist_64) bool {
|
||||
return N_STAB & sym.n_type != 0;
|
||||
return sym.n_type.bits.is_stab != 0;
|
||||
}
|
||||
|
||||
pub fn pext(sym: nlist_64) bool {
|
||||
return N_PEXT & sym.n_type != 0;
|
||||
}
|
||||
|
||||
pub fn ext(sym: nlist_64) bool {
|
||||
return N_EXT & sym.n_type != 0;
|
||||
}
|
||||
|
||||
// MLUGG TODO DELETE
|
||||
pub fn sect(sym: nlist_64) bool {
|
||||
const type_ = N_TYPE & sym.n_type;
|
||||
return type_ == N_SECT;
|
||||
return sym.n_type.type == .sect;
|
||||
}
|
||||
|
||||
// MLUGG TODO DELETE
|
||||
pub fn undf(sym: nlist_64) bool {
|
||||
const type_ = N_TYPE & sym.n_type;
|
||||
return type_ == N_UNDF;
|
||||
return sym.n_type.type == .undf;
|
||||
}
|
||||
|
||||
// MLUGG TODO DELETE
|
||||
pub fn indr(sym: nlist_64) bool {
|
||||
const type_ = N_TYPE & sym.n_type;
|
||||
return type_ == N_INDR;
|
||||
return sym.n_type.type == .indr;
|
||||
}
|
||||
|
||||
// MLUGG TODO DELETE
|
||||
pub fn abs(sym: nlist_64) bool {
|
||||
const type_ = N_TYPE & sym.n_type;
|
||||
return type_ == N_ABS;
|
||||
return sym.n_type.type == .abs;
|
||||
}
|
||||
|
||||
// MLUGG TODO DELETE
|
||||
pub fn weakDef(sym: nlist_64) bool {
|
||||
return sym.n_desc & N_WEAK_DEF != 0;
|
||||
return sym.n_desc.weak_def_or_ref_to_weak;
|
||||
}
|
||||
|
||||
// MLUGG TODO DELETE
|
||||
pub fn weakRef(sym: nlist_64) bool {
|
||||
return sym.n_desc & N_WEAK_REF != 0;
|
||||
return sym.n_desc.weak_ref;
|
||||
}
|
||||
|
||||
// MLUGG TODO DELETE
|
||||
pub fn discarded(sym: nlist_64) bool {
|
||||
return sym.n_desc & N_DESC_DISCARDED != 0;
|
||||
return sym.n_desc.discarded_or_no_dead_strip;
|
||||
}
|
||||
|
||||
// MLUGG TODO DELETE
|
||||
pub fn noDeadStrip(sym: nlist_64) bool {
|
||||
return sym.n_desc & N_NO_DEAD_STRIP != 0;
|
||||
return sym.n_desc.discarded_or_no_dead_strip;
|
||||
}
|
||||
|
||||
pub fn tentative(sym: nlist_64) bool {
|
||||
if (!sym.undf()) return false;
|
||||
return sym.n_value != 0;
|
||||
return sym.n_type.type == .undf and sym.n_value != 0;
|
||||
}
|
||||
};
|
||||
|
||||
@ -2046,7 +2096,7 @@ pub const unwind_info_compressed_second_level_page_header = extern struct {
|
||||
// encodings array
|
||||
};
|
||||
|
||||
pub const UnwindInfoCompressedEntry = packed struct {
|
||||
pub const UnwindInfoCompressedEntry = packed struct(u32) {
|
||||
funcOffset: u24,
|
||||
encodingIndex: u8,
|
||||
};
|
||||
|
||||
@ -455,72 +455,23 @@ pub fn writeEhFrameRelocs(elf_file: *Elf, relocs: *std.array_list.Managed(elf.El
|
||||
}
|
||||
|
||||
pub fn writeEhFrameHdr(elf_file: *Elf, writer: anytype) !void {
|
||||
const comp = elf_file.base.comp;
|
||||
const gpa = comp.gpa;
|
||||
|
||||
try writer.writeByte(1); // version
|
||||
try writer.writeByte(DW_EH_PE.pcrel | DW_EH_PE.sdata4);
|
||||
try writer.writeByte(DW_EH_PE.udata4);
|
||||
try writer.writeByte(DW_EH_PE.datarel | DW_EH_PE.sdata4);
|
||||
try writer.writeByte(DW_EH_PE.pcrel | DW_EH_PE.sdata4); // eh_frame_ptr_enc
|
||||
// Building the lookup table would be expensive work on every `flush` -- omit it.
|
||||
try writer.writeByte(DW_EH_PE.omit); // fde_count_enc
|
||||
try writer.writeByte(DW_EH_PE.omit); // table_enc
|
||||
|
||||
const shdrs = elf_file.sections.items(.shdr);
|
||||
const eh_frame_shdr = shdrs[elf_file.section_indexes.eh_frame.?];
|
||||
const eh_frame_hdr_shdr = shdrs[elf_file.section_indexes.eh_frame_hdr.?];
|
||||
const num_fdes = @as(u32, @intCast(@divExact(eh_frame_hdr_shdr.sh_size - eh_frame_hdr_header_size, 8)));
|
||||
const existing_size = existing_size: {
|
||||
const zo = elf_file.zigObjectPtr() orelse break :existing_size 0;
|
||||
const sym = zo.symbol(zo.eh_frame_index orelse break :existing_size 0);
|
||||
break :existing_size sym.atom(elf_file).?.size;
|
||||
};
|
||||
try writer.writeInt(
|
||||
u32,
|
||||
@as(u32, @bitCast(@as(
|
||||
i32,
|
||||
@truncate(@as(i64, @intCast(eh_frame_shdr.sh_addr + existing_size)) - @as(i64, @intCast(eh_frame_hdr_shdr.sh_addr)) - 4),
|
||||
@truncate(@as(i64, @intCast(eh_frame_shdr.sh_addr)) - @as(i64, @intCast(eh_frame_hdr_shdr.sh_addr)) - 4),
|
||||
))),
|
||||
.little,
|
||||
);
|
||||
try writer.writeInt(u32, num_fdes, .little);
|
||||
|
||||
const Entry = extern struct {
|
||||
init_addr: u32,
|
||||
fde_addr: u32,
|
||||
|
||||
pub fn lessThan(ctx: void, lhs: @This(), rhs: @This()) bool {
|
||||
_ = ctx;
|
||||
return lhs.init_addr < rhs.init_addr;
|
||||
}
|
||||
};
|
||||
|
||||
var entries = std.array_list.Managed(Entry).init(gpa);
|
||||
defer entries.deinit();
|
||||
try entries.ensureTotalCapacityPrecise(num_fdes);
|
||||
|
||||
for (elf_file.objects.items) |index| {
|
||||
const object = elf_file.file(index).?.object;
|
||||
for (object.fdes.items) |fde| {
|
||||
if (!fde.alive) continue;
|
||||
|
||||
const relocs = fde.relocs(object);
|
||||
assert(relocs.len > 0); // Should this be an error? Things are completely broken anyhow if this trips...
|
||||
const rel = relocs[0];
|
||||
const ref = object.resolveSymbol(rel.r_sym(), elf_file);
|
||||
const sym = elf_file.symbol(ref).?;
|
||||
const P = @as(i64, @intCast(fde.address(elf_file)));
|
||||
const S = @as(i64, @intCast(sym.address(.{}, elf_file)));
|
||||
const A = rel.r_addend;
|
||||
entries.appendAssumeCapacity(.{
|
||||
.init_addr = @bitCast(@as(i32, @truncate(S + A - @as(i64, @intCast(eh_frame_hdr_shdr.sh_addr))))),
|
||||
.fde_addr = @as(
|
||||
u32,
|
||||
@bitCast(@as(i32, @truncate(P - @as(i64, @intCast(eh_frame_hdr_shdr.sh_addr))))),
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
std.mem.sort(Entry, entries.items, {}, Entry.lessThan);
|
||||
try writer.writeSliceEndian(Entry, entries.items, .little);
|
||||
}
|
||||
|
||||
const eh_frame_hdr_header_size: usize = 12;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user