mirror of
https://github.com/ziglang/zig.git
synced 2026-02-11 20:11:23 +00:00
I started this diff trying to remove a little dead code from the C backend, but ended up finding a bunch of dead code sprinkled all over the place: * `packed` handling in the C backend which was made dead by `Legalize` * Representation of pointers to runtime-known vector indices * Handling for the `vector_store_elem` AIR instruction (now removed) * Old tuple handling from when they used the InternPool repr of structs * Straightforward unused functions * TODOs in the LLVM backend for features which Zig just does not support
6445 lines
268 KiB
Zig
6445 lines
268 KiB
Zig
gpa: Allocator,
|
|
bin_file: *link.File,
|
|
format: DW.Format,
|
|
endian: std.builtin.Endian,
|
|
address_size: AddressSize,
|
|
|
|
mods: std.AutoArrayHashMapUnmanaged(*Module, ModInfo),
|
|
types: std.AutoArrayHashMapUnmanaged(InternPool.Index, Entry.Index),
|
|
values: std.AutoArrayHashMapUnmanaged(InternPool.Index, Entry.Index),
|
|
navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, Entry.Index),
|
|
decls: std.AutoArrayHashMapUnmanaged(InternPool.TrackedInst.Index, Entry.Index),
|
|
|
|
debug_abbrev: DebugAbbrev,
|
|
debug_aranges: DebugAranges,
|
|
debug_frame: DebugFrame,
|
|
debug_info: DebugInfo,
|
|
debug_line: DebugLine,
|
|
debug_line_str: StringSection,
|
|
debug_loclists: DebugLocLists,
|
|
debug_rnglists: DebugRngLists,
|
|
debug_str: StringSection,
|
|
|
|
pub const UpdateError = error{
|
|
WriteFailed,
|
|
ReinterpretDeclRef,
|
|
Unimplemented,
|
|
EndOfStream,
|
|
Underflow,
|
|
UnexpectedEndOfFile,
|
|
} ||
|
|
codegen.GenerateSymbolError ||
|
|
std.fs.File.OpenError ||
|
|
std.fs.File.SetEndPosError ||
|
|
std.fs.File.CopyRangeError ||
|
|
std.fs.File.PReadError ||
|
|
std.fs.File.PWriteError;
|
|
|
|
pub const FlushError = UpdateError;
|
|
|
|
pub const RelocError =
|
|
std.fs.File.PWriteError;
|
|
|
|
pub const AddressSize = enum(u8) {
|
|
@"32" = 4,
|
|
@"64" = 8,
|
|
_,
|
|
};
|
|
|
|
const ModInfo = struct {
|
|
root_dir_path: Entry.Index,
|
|
dirs: std.AutoArrayHashMapUnmanaged(Unit.Index, void),
|
|
files: std.AutoArrayHashMapUnmanaged(Zcu.File.Index, void),
|
|
|
|
fn deinit(mod_info: *ModInfo, gpa: Allocator) void {
|
|
mod_info.dirs.deinit(gpa);
|
|
mod_info.files.deinit(gpa);
|
|
mod_info.* = undefined;
|
|
}
|
|
};
|
|
|
|
const DebugAbbrev = struct {
|
|
section: Section,
|
|
const unit: Unit.Index = @enumFromInt(0);
|
|
|
|
const header_bytes = 0;
|
|
|
|
const trailer_bytes = uleb128Bytes(@intFromEnum(AbbrevCode.null));
|
|
};
|
|
|
|
const DebugAranges = struct {
|
|
section: Section,
|
|
|
|
fn headerBytes(dwarf: *Dwarf) u32 {
|
|
return dwarf.unitLengthBytes() + 2 + dwarf.sectionOffsetBytes() + 1 + 1;
|
|
}
|
|
|
|
fn trailerBytes(dwarf: *Dwarf) u32 {
|
|
return @intFromEnum(dwarf.address_size) * 2;
|
|
}
|
|
};
|
|
|
|
const DebugFrame = struct {
|
|
header: Header,
|
|
section: Section,
|
|
|
|
const Format = enum { none, debug_frame, eh_frame };
|
|
const Header = struct {
|
|
format: Format,
|
|
code_alignment_factor: u32,
|
|
data_alignment_factor: i32,
|
|
return_address_register: u32,
|
|
initial_instructions: []const Cfa,
|
|
};
|
|
|
|
fn headerBytes(dwarf: *Dwarf) u32 {
|
|
const target = &dwarf.bin_file.comp.root_mod.resolved_target.result;
|
|
return @intCast(switch (dwarf.debug_frame.header.format) {
|
|
.none => return 0,
|
|
.debug_frame => dwarf.unitLengthBytes() + dwarf.sectionOffsetBytes() + 1 + "\x00".len + 1 + 1,
|
|
.eh_frame => dwarf.unitLengthBytes() + 4 + 1 + "zR\x00".len +
|
|
uleb128Bytes(1) + 1,
|
|
} + switch (target.cpu.arch) {
|
|
.x86_64 => len: {
|
|
dev.check(.x86_64_backend);
|
|
const Register = @import("../codegen/x86_64/bits.zig").Register;
|
|
break :len uleb128Bytes(1) + sleb128Bytes(-8) + uleb128Bytes(Register.rip.dwarfNum()) +
|
|
1 + uleb128Bytes(Register.rsp.dwarfNum()) + sleb128Bytes(-1) +
|
|
1 + uleb128Bytes(1);
|
|
},
|
|
else => unreachable,
|
|
});
|
|
}
|
|
|
|
fn trailerBytes(dwarf: *Dwarf) u32 {
|
|
return @intCast(switch (dwarf.debug_frame.header.format) {
|
|
.none => 0,
|
|
.debug_frame => dwarf.unitLengthBytes() + dwarf.sectionOffsetBytes() + 1 + "\x00".len + 1 + 1 + uleb128Bytes(1) + sleb128Bytes(1) + uleb128Bytes(0),
|
|
.eh_frame => dwarf.unitLengthBytes() + 4 + 1 + "\x00".len + uleb128Bytes(1) + sleb128Bytes(1) + uleb128Bytes(0),
|
|
});
|
|
}
|
|
};
|
|
|
|
const DebugInfo = struct {
|
|
section: Section,
|
|
|
|
fn headerBytes(dwarf: *Dwarf) u32 {
|
|
return dwarf.unitLengthBytes() + 2 + 1 + 1 + dwarf.sectionOffsetBytes() +
|
|
uleb128Bytes(@intFromEnum(AbbrevCode.compile_unit)) + 1 + dwarf.sectionOffsetBytes() * 6 + uleb128Bytes(0) +
|
|
uleb128Bytes(@intFromEnum(AbbrevCode.module)) + dwarf.sectionOffsetBytes() + uleb128Bytes(0);
|
|
}
|
|
|
|
fn declEntryLineOff(dwarf: *Dwarf) u32 {
|
|
return AbbrevCode.decl_bytes + dwarf.sectionOffsetBytes();
|
|
}
|
|
|
|
fn declAbbrevCode(debug_info: *DebugInfo, unit: Unit.Index, entry: Entry.Index) !AbbrevCode {
|
|
const dwarf: *Dwarf = @fieldParentPtr("debug_info", debug_info);
|
|
const unit_ptr = debug_info.section.getUnit(unit);
|
|
const entry_ptr = unit_ptr.getEntry(entry);
|
|
if (entry_ptr.len < AbbrevCode.decl_bytes) return .null;
|
|
var abbrev_code_buf: [AbbrevCode.decl_bytes]u8 = undefined;
|
|
if (try dwarf.getFile().?.preadAll(
|
|
&abbrev_code_buf,
|
|
debug_info.section.off(dwarf) + unit_ptr.off + unit_ptr.header_len + entry_ptr.off,
|
|
) != abbrev_code_buf.len) return error.InputOutput;
|
|
var abbrev_code_reader: std.Io.Reader = .fixed(&abbrev_code_buf);
|
|
return @enumFromInt(
|
|
abbrev_code_reader.takeLeb128(@typeInfo(AbbrevCode).@"enum".tag_type) catch unreachable,
|
|
);
|
|
}
|
|
|
|
const trailer_bytes = 1 + 1;
|
|
};
|
|
|
|
const DebugLine = struct {
|
|
header: Header,
|
|
section: Section,
|
|
|
|
const Header = struct {
|
|
minimum_instruction_length: u8,
|
|
maximum_operations_per_instruction: u8,
|
|
default_is_stmt: bool,
|
|
line_base: i8,
|
|
line_range: u8,
|
|
opcode_base: u8,
|
|
};
|
|
|
|
fn dirIndexInfo(dir_count: u32) struct { bytes: u8, form: DeclValEnum(DW.FORM) } {
|
|
return if (dir_count <= 1 << 8)
|
|
.{ .bytes = 1, .form = .data1 }
|
|
else if (dir_count <= 1 << 16)
|
|
.{ .bytes = 2, .form = .data2 }
|
|
else
|
|
unreachable;
|
|
}
|
|
|
|
fn headerBytes(dwarf: *Dwarf, dir_count: u32, file_count: u32) u32 {
|
|
const dir_index_info = dirIndexInfo(dir_count);
|
|
return dwarf.unitLengthBytes() + 2 + 1 + 1 + dwarf.sectionOffsetBytes() + 1 + 1 + 1 + 1 + 1 + 1 + 1 * (dwarf.debug_line.header.opcode_base - 1) +
|
|
1 + uleb128Bytes(DW.LNCT.path) + uleb128Bytes(DW.FORM.line_strp) + uleb128Bytes(dir_count) + (dwarf.sectionOffsetBytes()) * dir_count +
|
|
1 + uleb128Bytes(DW.LNCT.path) + uleb128Bytes(DW.FORM.line_strp) + uleb128Bytes(DW.LNCT.directory_index) + uleb128Bytes(@intFromEnum(dir_index_info.form)) + uleb128Bytes(DW.LNCT.LLVM_source) + uleb128Bytes(DW.FORM.line_strp) + uleb128Bytes(file_count) + (dwarf.sectionOffsetBytes() + dir_index_info.bytes + dwarf.sectionOffsetBytes()) * file_count;
|
|
}
|
|
|
|
const trailer_bytes = 1 + uleb128Bytes(1) + 1;
|
|
};
|
|
|
|
const DebugLocLists = struct {
|
|
section: Section,
|
|
|
|
fn baseOffset(dwarf: *Dwarf) u32 {
|
|
return dwarf.unitLengthBytes() + 2 + 1 + 1 + 4;
|
|
}
|
|
|
|
fn headerBytes(dwarf: *Dwarf) u32 {
|
|
return baseOffset(dwarf);
|
|
}
|
|
|
|
const trailer_bytes = 0;
|
|
};
|
|
|
|
const DebugRngLists = struct {
|
|
section: Section,
|
|
|
|
const baseOffset = DebugLocLists.baseOffset;
|
|
|
|
fn headerBytes(dwarf: *Dwarf) u32 {
|
|
return baseOffset(dwarf) + dwarf.sectionOffsetBytes() * 1;
|
|
}
|
|
|
|
const trailer_bytes = 1;
|
|
};
|
|
|
|
const StringSection = struct {
|
|
contents: std.ArrayListUnmanaged(u8),
|
|
map: std.AutoArrayHashMapUnmanaged(void, void),
|
|
section: Section,
|
|
|
|
const unit: Unit.Index = @enumFromInt(0);
|
|
|
|
const init: StringSection = .{
|
|
.contents = .empty,
|
|
.map = .empty,
|
|
.section = Section.init,
|
|
};
|
|
|
|
fn deinit(str_sec: *StringSection, gpa: Allocator) void {
|
|
str_sec.contents.deinit(gpa);
|
|
str_sec.map.deinit(gpa);
|
|
str_sec.section.deinit(gpa);
|
|
}
|
|
|
|
fn addString(str_sec: *StringSection, dwarf: *Dwarf, str: []const u8) UpdateError!Entry.Index {
|
|
const gop = try str_sec.map.getOrPutAdapted(dwarf.gpa, str, Adapter{ .str_sec = str_sec });
|
|
const entry: Entry.Index = @enumFromInt(gop.index);
|
|
if (!gop.found_existing) {
|
|
errdefer _ = str_sec.map.pop();
|
|
const unit_ptr = str_sec.section.getUnit(unit);
|
|
assert(try str_sec.section.getUnit(unit).addEntry(dwarf.gpa) == entry);
|
|
errdefer _ = unit_ptr.entries.pop();
|
|
const entry_ptr = unit_ptr.getEntry(entry);
|
|
if (unit_ptr.last.unwrap()) |last_entry|
|
|
unit_ptr.getEntry(last_entry).next = entry.toOptional();
|
|
entry_ptr.prev = unit_ptr.last;
|
|
unit_ptr.last = entry.toOptional();
|
|
entry_ptr.off = @intCast(str_sec.contents.items.len);
|
|
entry_ptr.len = @intCast(str.len + 1);
|
|
try str_sec.contents.ensureUnusedCapacity(dwarf.gpa, str.len + 1);
|
|
str_sec.contents.appendSliceAssumeCapacity(str);
|
|
str_sec.contents.appendAssumeCapacity(0);
|
|
str_sec.section.dirty = true;
|
|
}
|
|
return entry;
|
|
}
|
|
|
|
const Adapter = struct {
|
|
str_sec: *StringSection,
|
|
|
|
pub fn hash(_: Adapter, key: []const u8) u32 {
|
|
return @truncate(std.hash.Wyhash.hash(0, key));
|
|
}
|
|
|
|
pub fn eql(adapter: Adapter, key: []const u8, _: void, rhs_index: usize) bool {
|
|
const entry = adapter.str_sec.section.getUnit(unit).getEntry(@enumFromInt(rhs_index));
|
|
return std.mem.eql(u8, key, adapter.str_sec.contents.items[entry.off..][0 .. entry.len - 1 :0]);
|
|
}
|
|
};
|
|
};
|
|
|
|
/// A linker section containing a sequence of `Unit`s.
|
|
pub const Section = struct {
|
|
dirty: bool,
|
|
pad_entries_to_ideal: bool,
|
|
alignment: InternPool.Alignment,
|
|
index: u32,
|
|
first: Unit.Index.Optional,
|
|
last: Unit.Index.Optional,
|
|
len: u64,
|
|
units: std.ArrayListUnmanaged(Unit),
|
|
|
|
pub const Index = enum {
|
|
debug_abbrev,
|
|
debug_aranges,
|
|
debug_frame,
|
|
debug_info,
|
|
debug_line,
|
|
debug_line_str,
|
|
debug_loclists,
|
|
debug_rnglists,
|
|
debug_str,
|
|
};
|
|
|
|
const init: Section = .{
|
|
.dirty = true,
|
|
.pad_entries_to_ideal = true,
|
|
.alignment = .@"1",
|
|
.index = std.math.maxInt(u32),
|
|
.first = .none,
|
|
.last = .none,
|
|
.units = .empty,
|
|
.len = 0,
|
|
};
|
|
|
|
fn deinit(sec: *Section, gpa: Allocator) void {
|
|
for (sec.units.items) |*unit| unit.deinit(gpa);
|
|
sec.units.deinit(gpa);
|
|
sec.* = undefined;
|
|
}
|
|
|
|
fn off(sec: Section, dwarf: *Dwarf) u64 {
|
|
if (dwarf.bin_file.cast(.elf)) |elf_file| {
|
|
const zo = elf_file.zigObjectPtr().?;
|
|
const atom = zo.symbol(sec.index).atom(elf_file).?;
|
|
return atom.offset(elf_file);
|
|
} else if (dwarf.bin_file.cast(.macho)) |macho_file| {
|
|
const header = if (macho_file.d_sym) |d_sym|
|
|
d_sym.sections.items[sec.index]
|
|
else
|
|
macho_file.sections.items(.header)[sec.index];
|
|
return header.offset;
|
|
} else unreachable;
|
|
}
|
|
|
|
fn addUnit(sec: *Section, header_len: u32, trailer_len: u32, dwarf: *Dwarf) UpdateError!Unit.Index {
|
|
const unit: Unit.Index = @enumFromInt(sec.units.items.len);
|
|
const unit_ptr = try sec.units.addOne(dwarf.gpa);
|
|
errdefer sec.popUnit(dwarf.gpa);
|
|
const aligned_header_len: u32 = @intCast(sec.alignment.forward(header_len));
|
|
const aligned_trailer_len: u32 = @intCast(sec.alignment.forward(trailer_len));
|
|
unit_ptr.* = .{
|
|
.prev = sec.last,
|
|
.next = .none,
|
|
.first = .none,
|
|
.last = .none,
|
|
.free = .none,
|
|
.header_len = aligned_header_len,
|
|
.trailer_len = aligned_trailer_len,
|
|
.off = 0,
|
|
.len = aligned_header_len + aligned_trailer_len,
|
|
.entries = .empty,
|
|
.cross_unit_relocs = .empty,
|
|
.cross_section_relocs = .empty,
|
|
};
|
|
if (sec.last.unwrap()) |last_unit| {
|
|
const last_unit_ptr = sec.getUnit(last_unit);
|
|
last_unit_ptr.next = unit.toOptional();
|
|
unit_ptr.off = last_unit_ptr.off + sec.padUnitToIdeal(last_unit_ptr.len);
|
|
}
|
|
if (sec.first == .none)
|
|
sec.first = unit.toOptional();
|
|
sec.last = unit.toOptional();
|
|
try sec.resize(dwarf, unit_ptr.off + sec.padUnitToIdeal(unit_ptr.len));
|
|
return unit;
|
|
}
|
|
|
|
fn unlinkUnit(sec: *Section, unit: Unit.Index) void {
|
|
const unit_ptr = sec.getUnit(unit);
|
|
if (unit_ptr.prev.unwrap()) |prev_unit| sec.getUnit(prev_unit).next = unit_ptr.next;
|
|
if (unit_ptr.next.unwrap()) |next_unit| sec.getUnit(next_unit).prev = unit_ptr.prev;
|
|
if (sec.first == unit.toOptional()) sec.first = unit_ptr.next;
|
|
if (sec.last == unit.toOptional()) sec.last = unit_ptr.prev;
|
|
}
|
|
|
|
fn popUnit(sec: *Section, gpa: Allocator) void {
|
|
const unit_index: Unit.Index = @enumFromInt(sec.units.items.len - 1);
|
|
sec.unlinkUnit(unit_index);
|
|
var unit = sec.units.pop().?;
|
|
unit.deinit(gpa);
|
|
}
|
|
|
|
pub fn getUnit(sec: *Section, unit: Unit.Index) *Unit {
|
|
return &sec.units.items[@intFromEnum(unit)];
|
|
}
|
|
|
|
fn resizeEntry(
|
|
sec: *Section,
|
|
unit: Unit.Index,
|
|
entry: Entry.Index,
|
|
dwarf: *Dwarf,
|
|
len: u32,
|
|
) (UpdateError || Writer.Error)!void {
|
|
const unit_ptr = sec.getUnit(unit);
|
|
const entry_ptr = unit_ptr.getEntry(entry);
|
|
if (len > 0) {
|
|
if (entry_ptr.len == 0) {
|
|
assert(entry_ptr.prev == .none and entry_ptr.next == .none);
|
|
entry_ptr.off = if (unit_ptr.last.unwrap()) |last_entry| off: {
|
|
const last_entry_ptr = unit_ptr.getEntry(last_entry);
|
|
last_entry_ptr.next = entry.toOptional();
|
|
break :off last_entry_ptr.off + sec.padEntryToIdeal(last_entry_ptr.len);
|
|
} else 0;
|
|
entry_ptr.prev = unit_ptr.last;
|
|
unit_ptr.last = entry.toOptional();
|
|
if (unit_ptr.first == .none) unit_ptr.first = unit_ptr.last;
|
|
if (entry_ptr.prev.unwrap()) |prev_entry| try unit_ptr.getEntry(prev_entry).pad(unit_ptr, sec, dwarf);
|
|
}
|
|
try entry_ptr.resize(unit_ptr, sec, dwarf, len);
|
|
}
|
|
assert(entry_ptr.len == len);
|
|
}
|
|
|
|
fn replaceEntry(
|
|
sec: *Section,
|
|
unit: Unit.Index,
|
|
entry: Entry.Index,
|
|
dwarf: *Dwarf,
|
|
contents: []const u8,
|
|
) (UpdateError || Writer.Error)!void {
|
|
try sec.resizeEntry(unit, entry, dwarf, @intCast(contents.len));
|
|
const unit_ptr = sec.getUnit(unit);
|
|
try unit_ptr.getEntry(entry).replace(unit_ptr, sec, dwarf, contents);
|
|
}
|
|
|
|
fn freeEntry(
|
|
sec: *Section,
|
|
unit: Unit.Index,
|
|
entry: Entry.Index,
|
|
dwarf: *Dwarf,
|
|
) (UpdateError || Writer.Error)!void {
|
|
const unit_ptr = sec.getUnit(unit);
|
|
const entry_ptr = unit_ptr.getEntry(entry);
|
|
if (entry_ptr.len > 0) {
|
|
if (entry_ptr.next.unwrap()) |next_entry| unit_ptr.getEntry(next_entry).prev = entry_ptr.prev;
|
|
if (entry_ptr.prev.unwrap()) |prev_entry| {
|
|
const prev_entry_ptr = unit_ptr.getEntry(prev_entry);
|
|
prev_entry_ptr.next = entry_ptr.next;
|
|
try prev_entry_ptr.pad(unit_ptr, sec, dwarf);
|
|
} else {
|
|
unit_ptr.trim();
|
|
sec.trim(dwarf);
|
|
}
|
|
} else assert(entry_ptr.prev == .none and entry_ptr.next == .none);
|
|
entry_ptr.prev = .none;
|
|
entry_ptr.next = unit_ptr.free;
|
|
entry_ptr.off = 0;
|
|
entry_ptr.len = 0;
|
|
entry_ptr.clear();
|
|
unit_ptr.free = entry.toOptional();
|
|
}
|
|
|
|
fn resize(sec: *Section, dwarf: *Dwarf, len: u64) UpdateError!void {
|
|
if (len <= sec.len) return;
|
|
if (dwarf.bin_file.cast(.elf)) |elf_file| {
|
|
const zo = elf_file.zigObjectPtr().?;
|
|
const atom = zo.symbol(sec.index).atom(elf_file).?;
|
|
atom.size = len;
|
|
atom.alignment = sec.alignment;
|
|
sec.len = len;
|
|
try zo.allocateAtom(atom, false, elf_file);
|
|
} else if (dwarf.bin_file.cast(.macho)) |macho_file| {
|
|
const header = if (macho_file.d_sym) |*d_sym| header: {
|
|
try d_sym.growSection(@intCast(sec.index), len, true, macho_file);
|
|
break :header &d_sym.sections.items[sec.index];
|
|
} else header: {
|
|
try macho_file.growSection(@intCast(sec.index), len);
|
|
break :header &macho_file.sections.items(.header)[sec.index];
|
|
};
|
|
sec.len = header.size;
|
|
}
|
|
}
|
|
|
|
fn trim(sec: *Section, dwarf: *Dwarf) void {
|
|
const len = sec.getUnit(sec.first.unwrap() orelse return).off;
|
|
if (len == 0) return;
|
|
for (sec.units.items) |*unit| unit.off -= len;
|
|
sec.len -= len;
|
|
if (dwarf.bin_file.cast(.elf)) |elf_file| {
|
|
const zo = elf_file.zigObjectPtr().?;
|
|
const atom = zo.symbol(sec.index).atom(elf_file).?;
|
|
if (atom.prevAtom(elf_file)) |_| {
|
|
atom.value += len;
|
|
} else {
|
|
const shdr = &elf_file.sections.items(.shdr)[atom.output_section_index];
|
|
shdr.sh_offset += len;
|
|
shdr.sh_size -= len;
|
|
atom.value = 0;
|
|
}
|
|
atom.size -= len;
|
|
} else if (dwarf.bin_file.cast(.macho)) |macho_file| {
|
|
const header = if (macho_file.d_sym) |*d_sym|
|
|
&d_sym.sections.items[sec.index]
|
|
else
|
|
&macho_file.sections.items(.header)[sec.index];
|
|
header.offset += @intCast(len);
|
|
header.size -= len;
|
|
}
|
|
}
|
|
|
|
fn resolveRelocs(sec: *Section, dwarf: *Dwarf) RelocError!void {
|
|
for (sec.units.items) |*unit| try unit.resolveRelocs(sec, dwarf);
|
|
}
|
|
|
|
fn padUnitToIdeal(sec: *Section, actual_size: anytype) @TypeOf(actual_size) {
|
|
return @intCast(sec.alignment.forward(Dwarf.padToIdeal(actual_size)));
|
|
}
|
|
|
|
fn padEntryToIdeal(sec: *Section, actual_size: anytype) @TypeOf(actual_size) {
|
|
return @intCast(sec.alignment.forward(if (sec.pad_entries_to_ideal) Dwarf.padToIdeal(actual_size) else actual_size));
|
|
}
|
|
};
|
|
|
|
/// A unit within a `Section` containing a sequence of `Entry`s.
|
|
const Unit = struct {
|
|
prev: Index.Optional,
|
|
next: Index.Optional,
|
|
first: Entry.Index.Optional,
|
|
last: Entry.Index.Optional,
|
|
free: Entry.Index.Optional,
|
|
/// offset within containing section
|
|
off: u32,
|
|
header_len: u32,
|
|
trailer_len: u32,
|
|
/// data length in bytes
|
|
len: u32,
|
|
entries: std.ArrayListUnmanaged(Entry),
|
|
cross_unit_relocs: std.ArrayListUnmanaged(CrossUnitReloc),
|
|
cross_section_relocs: std.ArrayListUnmanaged(CrossSectionReloc),
|
|
|
|
const Index = enum(u32) {
|
|
main,
|
|
_,
|
|
|
|
const Optional = enum(u32) {
|
|
none = std.math.maxInt(u32),
|
|
_,
|
|
|
|
pub fn unwrap(uio: Optional) ?Index {
|
|
return if (uio != .none) @enumFromInt(@intFromEnum(uio)) else null;
|
|
}
|
|
};
|
|
|
|
fn toOptional(ui: Index) Optional {
|
|
return @enumFromInt(@intFromEnum(ui));
|
|
}
|
|
};
|
|
|
|
fn clear(unit: *Unit) void {
|
|
unit.cross_unit_relocs.clearRetainingCapacity();
|
|
unit.cross_section_relocs.clearRetainingCapacity();
|
|
}
|
|
|
|
fn deinit(unit: *Unit, gpa: Allocator) void {
|
|
for (unit.entries.items) |*entry| entry.deinit(gpa);
|
|
unit.entries.deinit(gpa);
|
|
unit.cross_unit_relocs.deinit(gpa);
|
|
unit.cross_section_relocs.deinit(gpa);
|
|
unit.* = undefined;
|
|
}
|
|
|
|
fn addEntry(unit: *Unit, gpa: Allocator) Allocator.Error!Entry.Index {
|
|
if (unit.free.unwrap()) |entry| {
|
|
const entry_ptr = unit.getEntry(entry);
|
|
unit.free = entry_ptr.next;
|
|
entry_ptr.next = .none;
|
|
return entry;
|
|
}
|
|
const entry: Entry.Index = @enumFromInt(unit.entries.items.len);
|
|
const entry_ptr = try unit.entries.addOne(gpa);
|
|
entry_ptr.* = .{
|
|
.prev = .none,
|
|
.next = .none,
|
|
.off = 0,
|
|
.len = 0,
|
|
.cross_entry_relocs = .empty,
|
|
.cross_unit_relocs = .empty,
|
|
.cross_section_relocs = .empty,
|
|
.external_relocs = .empty,
|
|
};
|
|
return entry;
|
|
}
|
|
|
|
pub fn getEntry(unit: *Unit, entry: Entry.Index) *Entry {
|
|
return &unit.entries.items[@intFromEnum(entry)];
|
|
}
|
|
|
|
fn resize(unit_ptr: *Unit, sec: *Section, dwarf: *Dwarf, extra_header_len: u32, len: u32) UpdateError!void {
|
|
const end = if (unit_ptr.next.unwrap()) |next_unit|
|
|
sec.getUnit(next_unit).off
|
|
else
|
|
sec.len;
|
|
if (extra_header_len > 0 or unit_ptr.off + len > end) {
|
|
unit_ptr.len = @min(unit_ptr.len, len);
|
|
var new_off = unit_ptr.off;
|
|
if (unit_ptr.next.unwrap()) |next_unit| {
|
|
const next_unit_ptr = sec.getUnit(next_unit);
|
|
if (unit_ptr.prev.unwrap()) |prev_unit|
|
|
sec.getUnit(prev_unit).next = unit_ptr.next
|
|
else
|
|
sec.first = unit_ptr.next;
|
|
const unit = next_unit_ptr.prev;
|
|
next_unit_ptr.prev = unit_ptr.prev;
|
|
const last_unit_ptr = sec.getUnit(sec.last.unwrap().?);
|
|
last_unit_ptr.next = unit;
|
|
unit_ptr.prev = sec.last;
|
|
unit_ptr.next = .none;
|
|
new_off = last_unit_ptr.off + sec.padUnitToIdeal(last_unit_ptr.len);
|
|
sec.last = unit;
|
|
sec.dirty = true;
|
|
} else if (extra_header_len > 0) {
|
|
// `copyRangeAll` in `move` does not support overlapping ranges
|
|
// so make sure new location is disjoint from current location.
|
|
new_off += unit_ptr.len -| extra_header_len;
|
|
}
|
|
try sec.resize(dwarf, new_off + len);
|
|
try unit_ptr.move(sec, dwarf, new_off + extra_header_len);
|
|
unit_ptr.off -= extra_header_len;
|
|
unit_ptr.header_len += extra_header_len;
|
|
sec.trim(dwarf);
|
|
}
|
|
unit_ptr.len = len;
|
|
}
|
|
|
|
fn trim(unit: *Unit) void {
|
|
const len = unit.getEntry(unit.first.unwrap() orelse return).off;
|
|
if (len == 0) return;
|
|
for (unit.entries.items) |*entry| entry.off -= len;
|
|
unit.off += len;
|
|
unit.len -= len;
|
|
}
|
|
|
|
fn move(unit: *Unit, sec: *Section, dwarf: *Dwarf, new_off: u32) UpdateError!void {
|
|
if (unit.off == new_off) return;
|
|
const n = try dwarf.getFile().?.copyRangeAll(
|
|
sec.off(dwarf) + unit.off,
|
|
dwarf.getFile().?,
|
|
sec.off(dwarf) + new_off,
|
|
unit.len,
|
|
);
|
|
if (n != unit.len) return error.InputOutput;
|
|
unit.off = new_off;
|
|
}
|
|
|
|
fn resizeHeader(unit: *Unit, sec: *Section, dwarf: *Dwarf, len: u32) UpdateError!void {
|
|
unit.trim();
|
|
if (unit.header_len == len) return;
|
|
const available_len = if (unit.prev.unwrap()) |prev_unit| prev_excess: {
|
|
const prev_unit_ptr = sec.getUnit(prev_unit);
|
|
break :prev_excess unit.off - prev_unit_ptr.off - prev_unit_ptr.len;
|
|
} else 0;
|
|
if (available_len + unit.header_len < len)
|
|
try unit.resize(sec, dwarf, len - unit.header_len, unit.len - unit.header_len + len);
|
|
if (unit.header_len > len) {
|
|
const excess_header_len = unit.header_len - len;
|
|
unit.off += excess_header_len;
|
|
unit.header_len -= excess_header_len;
|
|
unit.len -= excess_header_len;
|
|
} else if (unit.header_len < len) {
|
|
const needed_header_len = len - unit.header_len;
|
|
unit.off -= needed_header_len;
|
|
unit.header_len += needed_header_len;
|
|
unit.len += needed_header_len;
|
|
}
|
|
assert(unit.header_len == len);
|
|
sec.trim(dwarf);
|
|
}
|
|
|
|
fn replaceHeader(unit: *Unit, sec: *Section, dwarf: *Dwarf, contents: []const u8) UpdateError!void {
|
|
assert(contents.len == unit.header_len);
|
|
try dwarf.getFile().?.pwriteAll(contents, sec.off(dwarf) + unit.off);
|
|
}
|
|
|
|
fn writeTrailer(unit: *Unit, sec: *Section, dwarf: *Dwarf) UpdateError!void {
|
|
const start = unit.off + unit.header_len + if (unit.last.unwrap()) |last_entry| end: {
|
|
const last_entry_ptr = unit.getEntry(last_entry);
|
|
break :end last_entry_ptr.off + last_entry_ptr.len;
|
|
} else 0;
|
|
const end = if (unit.next.unwrap()) |next_unit| sec.getUnit(next_unit).off else sec.len;
|
|
const len: usize = @intCast(end - start);
|
|
assert(len >= unit.trailer_len);
|
|
if (sec == &dwarf.debug_line.section) {
|
|
var buf: [1 + uleb128Bytes(std.math.maxInt(u32)) + 1]u8 = undefined;
|
|
var fw: Writer = .fixed(&buf);
|
|
fw.writeByte(DW.LNS.extended_op) catch unreachable;
|
|
const extended_op_bytes = fw.end;
|
|
var op_len_bytes: u5 = 1;
|
|
while (true) switch (std.math.order(len - extended_op_bytes - op_len_bytes, @as(u32, 1) << 7 * op_len_bytes)) {
|
|
.lt => break fw.writeUleb128(len - extended_op_bytes - op_len_bytes) catch unreachable,
|
|
.eq => {
|
|
// no length will ever work, so undercount and futz with the leb encoding to make up the missing byte
|
|
op_len_bytes += 1;
|
|
std.leb.writeUnsignedExtended(
|
|
fw.writableSlice(op_len_bytes) catch unreachable,
|
|
len - extended_op_bytes - op_len_bytes,
|
|
);
|
|
break;
|
|
},
|
|
.gt => op_len_bytes += 1,
|
|
};
|
|
assert(fw.end == extended_op_bytes + op_len_bytes);
|
|
fw.writeByte(DW.LNE.padding) catch unreachable;
|
|
assert(fw.end >= unit.trailer_len and fw.end <= len);
|
|
return dwarf.getFile().?.pwriteAll(fw.buffered(), sec.off(dwarf) + start);
|
|
}
|
|
var trailer_aw: Writer.Allocating = try .initCapacity(dwarf.gpa, len);
|
|
defer trailer_aw.deinit();
|
|
const tw = &trailer_aw.writer;
|
|
const fill_byte: u8 = if (sec == &dwarf.debug_abbrev.section) fill: {
|
|
tw.writeUleb128(@intFromEnum(AbbrevCode.null)) catch unreachable;
|
|
assert(uleb128Bytes(@intFromEnum(AbbrevCode.null)) == 1);
|
|
break :fill @intFromEnum(AbbrevCode.null);
|
|
} else if (sec == &dwarf.debug_aranges.section) fill: {
|
|
tw.splatByteAll(0, @intFromEnum(dwarf.address_size) * 2) catch unreachable;
|
|
break :fill 0;
|
|
} else if (sec == &dwarf.debug_frame.section) fill: {
|
|
switch (dwarf.debug_frame.header.format) {
|
|
.none => {},
|
|
.debug_frame, .eh_frame => |format| {
|
|
const unit_len = len - dwarf.unitLengthBytes();
|
|
switch (dwarf.format) {
|
|
.@"32" => tw.writeInt(u32, @intCast(unit_len), dwarf.endian) catch unreachable,
|
|
.@"64" => {
|
|
tw.writeInt(u32, std.math.maxInt(u32), dwarf.endian) catch unreachable;
|
|
tw.writeInt(u64, unit_len, dwarf.endian) catch unreachable;
|
|
},
|
|
}
|
|
switch (format) {
|
|
.none => unreachable,
|
|
.debug_frame => {
|
|
switch (dwarf.format) {
|
|
.@"32" => tw.writeInt(u32, std.math.maxInt(u32), dwarf.endian) catch unreachable,
|
|
.@"64" => tw.writeInt(u64, std.math.maxInt(u64), dwarf.endian) catch unreachable,
|
|
}
|
|
tw.writeByte(4) catch unreachable;
|
|
tw.writeAll("\x00") catch unreachable;
|
|
tw.writeByte(@intFromEnum(dwarf.address_size)) catch unreachable;
|
|
tw.writeByte(0) catch unreachable;
|
|
},
|
|
.eh_frame => {
|
|
tw.writeInt(u32, 0, dwarf.endian) catch unreachable;
|
|
tw.writeByte(1) catch unreachable;
|
|
tw.writeAll("\x00") catch unreachable;
|
|
},
|
|
}
|
|
tw.writeUleb128(1) catch unreachable;
|
|
tw.writeSleb128(1) catch unreachable;
|
|
tw.writeUleb128(0) catch unreachable;
|
|
},
|
|
}
|
|
tw.splatByteAll(DW.CFA.nop, unit.trailer_len - tw.end) catch unreachable;
|
|
break :fill DW.CFA.nop;
|
|
} else if (sec == &dwarf.debug_info.section) fill: {
|
|
for (0..2) |_| tw.writeUleb128(@intFromEnum(AbbrevCode.null)) catch unreachable;
|
|
assert(uleb128Bytes(@intFromEnum(AbbrevCode.null)) == 1);
|
|
break :fill @intFromEnum(AbbrevCode.null);
|
|
} else if (sec == &dwarf.debug_rnglists.section) fill: {
|
|
tw.writeByte(DW.RLE.end_of_list) catch unreachable;
|
|
break :fill DW.RLE.end_of_list;
|
|
} else unreachable;
|
|
assert(tw.end == unit.trailer_len);
|
|
tw.splatByteAll(fill_byte, len - unit.trailer_len) catch unreachable;
|
|
assert(tw.end == len);
|
|
try dwarf.getFile().?.pwriteAll(trailer_aw.written(), sec.off(dwarf) + start);
|
|
}
|
|
|
|
fn resolveRelocs(unit: *Unit, sec: *Section, dwarf: *Dwarf) RelocError!void {
|
|
const unit_off = sec.off(dwarf) + unit.off;
|
|
for (unit.cross_unit_relocs.items) |reloc| {
|
|
const target_unit = sec.getUnit(reloc.target_unit);
|
|
try dwarf.resolveReloc(
|
|
unit_off + reloc.source_off,
|
|
target_unit.off + (if (reloc.target_entry.unwrap()) |target_entry|
|
|
target_unit.header_len + target_unit.getEntry(target_entry).assertNonEmpty(target_unit, sec, dwarf).off
|
|
else
|
|
0) + reloc.target_off,
|
|
dwarf.sectionOffsetBytes(),
|
|
);
|
|
}
|
|
for (unit.cross_section_relocs.items) |reloc| {
|
|
const target_sec = switch (reloc.target_sec) {
|
|
inline else => |target_sec| &@field(dwarf, @tagName(target_sec)).section,
|
|
};
|
|
const target_unit = target_sec.getUnit(reloc.target_unit);
|
|
try dwarf.resolveReloc(
|
|
unit_off + reloc.source_off,
|
|
target_unit.off + (if (reloc.target_entry.unwrap()) |target_entry|
|
|
target_unit.header_len + target_unit.getEntry(target_entry).assertNonEmpty(target_unit, sec, dwarf).off
|
|
else
|
|
0) + reloc.target_off,
|
|
dwarf.sectionOffsetBytes(),
|
|
);
|
|
}
|
|
for (unit.entries.items) |*entry| try entry.resolveRelocs(unit, sec, dwarf);
|
|
}
|
|
};
|
|
|
|
/// An indivisible entry within a `Unit` containing section-specific data.
|
|
const Entry = struct {
|
|
prev: Index.Optional,
|
|
next: Index.Optional,
|
|
/// offset from end of containing unit header
|
|
off: u32,
|
|
/// data length in bytes
|
|
len: u32,
|
|
cross_entry_relocs: std.ArrayListUnmanaged(CrossEntryReloc),
|
|
cross_unit_relocs: std.ArrayListUnmanaged(CrossUnitReloc),
|
|
cross_section_relocs: std.ArrayListUnmanaged(CrossSectionReloc),
|
|
external_relocs: std.ArrayListUnmanaged(ExternalReloc),
|
|
|
|
fn clear(entry: *Entry) void {
|
|
entry.cross_entry_relocs.clearRetainingCapacity();
|
|
entry.cross_unit_relocs.clearRetainingCapacity();
|
|
entry.cross_section_relocs.clearRetainingCapacity();
|
|
entry.external_relocs.clearRetainingCapacity();
|
|
}
|
|
|
|
fn deinit(entry: *Entry, gpa: Allocator) void {
|
|
entry.cross_entry_relocs.deinit(gpa);
|
|
entry.cross_unit_relocs.deinit(gpa);
|
|
entry.cross_section_relocs.deinit(gpa);
|
|
entry.external_relocs.deinit(gpa);
|
|
entry.* = undefined;
|
|
}
|
|
|
|
const Index = enum(u32) {
|
|
_,
|
|
|
|
const Optional = enum(u32) {
|
|
none = std.math.maxInt(u32),
|
|
_,
|
|
|
|
pub fn unwrap(eio: Optional) ?Index {
|
|
return if (eio != .none) @enumFromInt(@intFromEnum(eio)) else null;
|
|
}
|
|
};
|
|
|
|
fn toOptional(ei: Index) Optional {
|
|
return @enumFromInt(@intFromEnum(ei));
|
|
}
|
|
};
|
|
|
|
fn pad(
|
|
entry: *Entry,
|
|
unit: *Unit,
|
|
sec: *Section,
|
|
dwarf: *Dwarf,
|
|
) (UpdateError || Writer.Error)!void {
|
|
assert(entry.len > 0);
|
|
const start = entry.off + entry.len;
|
|
if (sec == &dwarf.debug_frame.section) {
|
|
const len = if (entry.next.unwrap()) |next_entry|
|
|
unit.getEntry(next_entry).off - entry.off
|
|
else
|
|
entry.len;
|
|
var unit_len_buf: [8]u8 = undefined;
|
|
const unit_len_bytes = unit_len_buf[0..dwarf.sectionOffsetBytes()];
|
|
dwarf.writeInt(unit_len_bytes, len - dwarf.unitLengthBytes());
|
|
try dwarf.getFile().?.pwriteAll(unit_len_bytes, sec.off(dwarf) + unit.off + unit.header_len + entry.off);
|
|
const buf = try dwarf.gpa.alloc(u8, len - entry.len);
|
|
defer dwarf.gpa.free(buf);
|
|
@memset(buf, DW.CFA.nop);
|
|
try dwarf.getFile().?.pwriteAll(buf, sec.off(dwarf) + unit.off + unit.header_len + start);
|
|
return;
|
|
}
|
|
const len = unit.getEntry(entry.next.unwrap() orelse return).off - start;
|
|
var buf: [
|
|
@max(
|
|
uleb128Bytes(@intFromEnum(AbbrevCode.pad_1)),
|
|
uleb128Bytes(@intFromEnum(AbbrevCode.pad_n)) + uleb128Bytes(std.math.maxInt(u32)),
|
|
1 + uleb128Bytes(std.math.maxInt(u32)) + 1,
|
|
)
|
|
]u8 = undefined;
|
|
var fw: Writer = .fixed(&buf);
|
|
if (sec == &dwarf.debug_info.section) switch (len) {
|
|
0 => {},
|
|
1 => fw.writeUleb128(try dwarf.refAbbrevCode(.pad_1)) catch unreachable,
|
|
else => {
|
|
fw.writeUleb128(try dwarf.refAbbrevCode(.pad_n)) catch unreachable;
|
|
const abbrev_code_bytes = fw.end;
|
|
var block_len_bytes: u5 = 1;
|
|
while (true) switch (std.math.order(len - abbrev_code_bytes - block_len_bytes, @as(u32, 1) << 7 * block_len_bytes)) {
|
|
.lt => break fw.writeUleb128(len - abbrev_code_bytes - block_len_bytes) catch unreachable,
|
|
.eq => {
|
|
// no length will ever work, so undercount and futz with the leb encoding to make up the missing byte
|
|
block_len_bytes += 1;
|
|
std.leb.writeUnsignedExtended(
|
|
fw.writableSlice(block_len_bytes) catch unreachable,
|
|
len - abbrev_code_bytes - block_len_bytes,
|
|
);
|
|
break;
|
|
},
|
|
.gt => block_len_bytes += 1,
|
|
};
|
|
assert(fw.end == abbrev_code_bytes + block_len_bytes);
|
|
},
|
|
} else if (sec == &dwarf.debug_line.section) switch (len) {
|
|
0 => {},
|
|
1 => fw.writeByte(DW.LNS.const_add_pc) catch unreachable,
|
|
else => {
|
|
fw.writeByte(DW.LNS.extended_op) catch unreachable;
|
|
const extended_op_bytes = fw.end;
|
|
var op_len_bytes: u5 = 1;
|
|
while (true) switch (std.math.order(len - extended_op_bytes - op_len_bytes, @as(u32, 1) << 7 * op_len_bytes)) {
|
|
.lt => break fw.writeUleb128(len - extended_op_bytes - op_len_bytes) catch unreachable,
|
|
.eq => {
|
|
// no length will ever work, so undercount and futz with the leb encoding to make up the missing byte
|
|
op_len_bytes += 1;
|
|
std.leb.writeUnsignedExtended(
|
|
fw.writableSlice(op_len_bytes) catch unreachable,
|
|
len - extended_op_bytes - op_len_bytes,
|
|
);
|
|
break;
|
|
},
|
|
.gt => op_len_bytes += 1,
|
|
};
|
|
assert(fw.end == extended_op_bytes + op_len_bytes);
|
|
if (len > 2) fw.writeByte(DW.LNE.padding) catch unreachable;
|
|
},
|
|
} else assert(!sec.pad_entries_to_ideal and len == 0);
|
|
assert(fw.end <= len);
|
|
try dwarf.getFile().?.pwriteAll(fw.buffered(), sec.off(dwarf) + unit.off + unit.header_len + start);
|
|
}
|
|
|
|
fn resize(
|
|
entry_ptr: *Entry,
|
|
unit: *Unit,
|
|
sec: *Section,
|
|
dwarf: *Dwarf,
|
|
len: u32,
|
|
) (UpdateError || Writer.Error)!void {
|
|
assert(len > 0);
|
|
assert(sec.alignment.check(len));
|
|
if (entry_ptr.len == len) return;
|
|
const end = if (entry_ptr.next.unwrap()) |next_entry|
|
|
unit.getEntry(next_entry).off
|
|
else
|
|
unit.len -| (unit.header_len + unit.trailer_len);
|
|
if (entry_ptr.off + len > end) {
|
|
if (entry_ptr.next.unwrap()) |next_entry| {
|
|
if (entry_ptr.prev.unwrap()) |prev_entry| {
|
|
const prev_entry_ptr = unit.getEntry(prev_entry);
|
|
prev_entry_ptr.next = entry_ptr.next;
|
|
try prev_entry_ptr.pad(unit, sec, dwarf);
|
|
} else unit.first = entry_ptr.next;
|
|
const next_entry_ptr = unit.getEntry(next_entry);
|
|
const entry = next_entry_ptr.prev;
|
|
next_entry_ptr.prev = entry_ptr.prev;
|
|
const last_entry_ptr = unit.getEntry(unit.last.unwrap().?);
|
|
last_entry_ptr.next = entry;
|
|
entry_ptr.prev = unit.last;
|
|
entry_ptr.next = .none;
|
|
entry_ptr.off = last_entry_ptr.off + sec.padEntryToIdeal(last_entry_ptr.len);
|
|
unit.last = entry;
|
|
try last_entry_ptr.pad(unit, sec, dwarf);
|
|
}
|
|
try unit.resize(sec, dwarf, 0, @intCast(unit.header_len + entry_ptr.off + sec.padEntryToIdeal(len) + unit.trailer_len));
|
|
}
|
|
entry_ptr.len = len;
|
|
try entry_ptr.pad(unit, sec, dwarf);
|
|
}
|
|
|
|
fn replace(entry_ptr: *Entry, unit: *Unit, sec: *Section, dwarf: *Dwarf, contents: []const u8) UpdateError!void {
|
|
assert(contents.len == entry_ptr.len);
|
|
try dwarf.getFile().?.pwriteAll(contents, sec.off(dwarf) + unit.off + unit.header_len + entry_ptr.off);
|
|
if (false) {
|
|
const buf = try dwarf.gpa.alloc(u8, sec.len);
|
|
defer dwarf.gpa.free(buf);
|
|
_ = try dwarf.getFile().?.preadAll(buf, sec.off(dwarf));
|
|
log.info("Section{{ .first = {}, .last = {}, .off = 0x{x}, .len = 0x{x} }}", .{
|
|
@intFromEnum(sec.first),
|
|
@intFromEnum(sec.last),
|
|
sec.off(dwarf),
|
|
sec.len,
|
|
});
|
|
for (sec.units.items) |*unit_ptr| {
|
|
log.info(" Unit{{ .prev = {}, .next = {}, .first = {}, .last = {}, .off = 0x{x}, .header_len = 0x{x}, .trailer_len = 0x{x}, .len = 0x{x} }}", .{
|
|
@intFromEnum(unit_ptr.prev),
|
|
@intFromEnum(unit_ptr.next),
|
|
@intFromEnum(unit_ptr.first),
|
|
@intFromEnum(unit_ptr.last),
|
|
unit_ptr.off,
|
|
unit_ptr.header_len,
|
|
unit_ptr.trailer_len,
|
|
unit_ptr.len,
|
|
});
|
|
for (unit_ptr.entries.items) |*entry| {
|
|
log.info(" Entry{{ .prev = {}, .next = {}, .off = 0x{x}, .len = 0x{x} }}", .{
|
|
@intFromEnum(entry.prev),
|
|
@intFromEnum(entry.next),
|
|
entry.off,
|
|
entry.len,
|
|
});
|
|
}
|
|
}
|
|
std.debug.dumpHex(buf);
|
|
}
|
|
}
|
|
|
|
pub fn assertNonEmpty(entry: *Entry, unit: *Unit, sec: *Section, dwarf: *Dwarf) *Entry {
|
|
if (entry.len > 0) return entry;
|
|
if (std.debug.runtime_safety) {
|
|
log.err("missing {} from {s}", .{
|
|
@as(Entry.Index, @enumFromInt(entry - unit.entries.items.ptr)),
|
|
std.mem.sliceTo(if (dwarf.bin_file.cast(.elf)) |elf_file|
|
|
elf_file.zigObjectPtr().?.symbol(sec.index).name(elf_file)
|
|
else if (dwarf.bin_file.cast(.macho)) |macho_file|
|
|
if (macho_file.d_sym) |*d_sym|
|
|
&d_sym.sections.items[sec.index].segname
|
|
else
|
|
&macho_file.sections.items(.header)[sec.index].segname
|
|
else
|
|
"?", 0),
|
|
});
|
|
const zcu = dwarf.bin_file.comp.zcu.?;
|
|
const ip = &zcu.intern_pool;
|
|
for (dwarf.types.keys(), dwarf.types.values()) |ty, other_entry| {
|
|
const ty_unit: Unit.Index = if (Type.fromInterned(ty).typeDeclInst(zcu)) |inst_index|
|
|
dwarf.getUnit(zcu.fileByIndex(inst_index.resolveFile(ip)).mod.?) catch unreachable
|
|
else
|
|
.main;
|
|
if (sec.getUnit(ty_unit) == unit and unit.getEntry(other_entry) == entry)
|
|
log.err("missing Type({f}({d}))", .{
|
|
Type.fromInterned(ty).fmt(.{ .tid = .main, .zcu = zcu }),
|
|
@intFromEnum(ty),
|
|
});
|
|
}
|
|
for (dwarf.navs.keys(), dwarf.navs.values()) |nav, other_entry| {
|
|
const nav_unit = dwarf.getUnit(zcu.fileByIndex(ip.getNav(nav).srcInst(ip).resolveFile(ip)).mod.?) catch unreachable;
|
|
if (sec.getUnit(nav_unit) == unit and unit.getEntry(other_entry) == entry)
|
|
log.err("missing Nav({f}({d}))", .{ ip.getNav(nav).fqn.fmt(ip), @intFromEnum(nav) });
|
|
}
|
|
}
|
|
@panic("missing dwarf relocation target");
|
|
}
|
|
|
|
fn resolveRelocs(entry: *Entry, unit: *Unit, sec: *Section, dwarf: *Dwarf) RelocError!void {
|
|
const entry_off = sec.off(dwarf) + unit.off + unit.header_len + entry.off;
|
|
for (entry.cross_entry_relocs.items) |reloc| {
|
|
try dwarf.resolveReloc(
|
|
entry_off + reloc.source_off,
|
|
unit.off + unit.header_len + unit.getEntry(reloc.target_entry).assertNonEmpty(unit, sec, dwarf).off + reloc.target_off,
|
|
dwarf.sectionOffsetBytes(),
|
|
);
|
|
}
|
|
for (entry.cross_unit_relocs.items) |reloc| {
|
|
const target_unit = sec.getUnit(reloc.target_unit);
|
|
try dwarf.resolveReloc(
|
|
entry_off + reloc.source_off,
|
|
target_unit.off + (if (reloc.target_entry.unwrap()) |target_entry|
|
|
target_unit.header_len + target_unit.getEntry(target_entry).assertNonEmpty(target_unit, sec, dwarf).off
|
|
else
|
|
0) + reloc.target_off,
|
|
dwarf.sectionOffsetBytes(),
|
|
);
|
|
}
|
|
for (entry.cross_section_relocs.items) |reloc| {
|
|
const target_sec = switch (reloc.target_sec) {
|
|
inline else => |target_sec| &@field(dwarf, @tagName(target_sec)).section,
|
|
};
|
|
const target_unit = target_sec.getUnit(reloc.target_unit);
|
|
try dwarf.resolveReloc(
|
|
entry_off + reloc.source_off,
|
|
target_unit.off + (if (reloc.target_entry.unwrap()) |target_entry|
|
|
target_unit.header_len + target_unit.getEntry(target_entry).assertNonEmpty(target_unit, sec, dwarf).off
|
|
else
|
|
0) + reloc.target_off,
|
|
dwarf.sectionOffsetBytes(),
|
|
);
|
|
}
|
|
if (sec == &dwarf.debug_frame.section) switch (DebugFrame.format(dwarf)) {
|
|
.none, .debug_frame => {},
|
|
.eh_frame => return if (dwarf.bin_file.cast(.elf)) |elf_file| {
|
|
const zo = elf_file.zigObjectPtr().?;
|
|
const shndx = zo.symbol(sec.index).atom(elf_file).?.output_section_index;
|
|
const entry_addr: i64 = @intCast(entry_off - sec.off(dwarf) + elf_file.shdrs.items[shndx].sh_addr);
|
|
for (entry.external_relocs.items) |reloc| {
|
|
const symbol = zo.symbol(reloc.target_sym);
|
|
try dwarf.resolveReloc(
|
|
entry_off + reloc.source_off,
|
|
@bitCast((symbol.address(.{}, elf_file) + @as(i64, @intCast(reloc.target_off))) -
|
|
(entry_addr + reloc.source_off + 4)),
|
|
4,
|
|
);
|
|
}
|
|
} else unreachable,
|
|
};
|
|
if (dwarf.bin_file.cast(.elf)) |elf_file| {
|
|
const zo = elf_file.zigObjectPtr().?;
|
|
for (entry.external_relocs.items) |reloc| {
|
|
const symbol = zo.symbol(reloc.target_sym);
|
|
try dwarf.resolveReloc(
|
|
entry_off + reloc.source_off,
|
|
@bitCast(symbol.address(.{}, elf_file) + @as(i64, @intCast(reloc.target_off)) -
|
|
if (symbol.flags.is_tls) elf_file.dtpAddress() else 0),
|
|
@intFromEnum(dwarf.address_size),
|
|
);
|
|
}
|
|
} else if (dwarf.bin_file.cast(.macho)) |macho_file| {
|
|
const zo = macho_file.getZigObject().?;
|
|
for (entry.external_relocs.items) |reloc| {
|
|
const ref = zo.getSymbolRef(reloc.target_sym, macho_file);
|
|
try dwarf.resolveReloc(
|
|
entry_off + reloc.source_off,
|
|
ref.getSymbol(macho_file).?.getAddress(.{}, macho_file) + @as(i64, @intCast(reloc.target_off)),
|
|
@intFromEnum(dwarf.address_size),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
const CrossEntryReloc = struct {
|
|
source_off: u32 = 0,
|
|
target_entry: Entry.Index.Optional = .none,
|
|
target_off: u32 = 0,
|
|
};
|
|
const CrossUnitReloc = struct {
|
|
source_off: u32 = 0,
|
|
target_unit: Unit.Index,
|
|
target_entry: Entry.Index.Optional = .none,
|
|
target_off: u32 = 0,
|
|
};
|
|
const CrossSectionReloc = struct {
|
|
source_off: u32 = 0,
|
|
target_sec: Section.Index,
|
|
target_unit: Unit.Index,
|
|
target_entry: Entry.Index.Optional = .none,
|
|
target_off: u32 = 0,
|
|
};
|
|
const ExternalReloc = struct {
|
|
source_off: u32 = 0,
|
|
target_sym: u32,
|
|
target_off: u64 = 0,
|
|
};
|
|
|
|
pub const Loc = union(enum) {
|
|
empty,
|
|
addr_reloc: u32,
|
|
deref: *const Loc,
|
|
constu: u64,
|
|
consts: i64,
|
|
plus: Bin,
|
|
reg: u32,
|
|
breg: u32,
|
|
push_object_address,
|
|
call: struct {
|
|
args: []const Loc = &.{},
|
|
unit: Unit.Index,
|
|
entry: Entry.Index,
|
|
},
|
|
form_tls_address: *const Loc,
|
|
implicit_value: []const u8,
|
|
stack_value: *const Loc,
|
|
implicit_pointer: struct {
|
|
unit: Unit.Index,
|
|
entry: Entry.Index,
|
|
offset: i65,
|
|
},
|
|
wasm_ext: union(enum) {
|
|
local: u32,
|
|
global: u32,
|
|
operand_stack: u32,
|
|
},
|
|
|
|
pub const Bin = struct { *const Loc, *const Loc };
|
|
|
|
fn getConst(loc: Loc, comptime Int: type) ?Int {
|
|
return switch (loc) {
|
|
.constu => |constu| std.math.cast(Int, constu),
|
|
.consts => |consts| std.math.cast(Int, consts),
|
|
else => null,
|
|
};
|
|
}
|
|
|
|
fn getBaseReg(loc: Loc) ?u32 {
|
|
return switch (loc) {
|
|
.breg => |breg| breg,
|
|
else => null,
|
|
};
|
|
}
|
|
|
|
fn writeReg(reg: u32, op0: u8, opx: u8, writer: *Writer) Writer.Error!void {
|
|
if (std.math.cast(u5, reg)) |small_reg| {
|
|
try writer.writeByte(op0 + small_reg);
|
|
} else {
|
|
try writer.writeByte(opx);
|
|
try writer.writeUleb128(reg);
|
|
}
|
|
}
|
|
|
|
fn write(loc: Loc, adapter: anytype) (UpdateError || Writer.Error)!void {
|
|
const writer = adapter.writer();
|
|
switch (loc) {
|
|
.empty => {},
|
|
.addr_reloc => |sym_index| {
|
|
try writer.writeByte(DW.OP.addr);
|
|
try adapter.addrSym(sym_index);
|
|
},
|
|
.deref => |addr| {
|
|
try addr.write(adapter);
|
|
try writer.writeByte(DW.OP.deref);
|
|
},
|
|
.constu => |constu| if (std.math.cast(u5, constu)) |lit| {
|
|
try writer.writeByte(@as(u8, DW.OP.lit0) + lit);
|
|
} else if (std.math.cast(u8, constu)) |const1u| {
|
|
try writer.writeAll(&.{ DW.OP.const1u, const1u });
|
|
} else if (std.math.cast(u16, constu)) |const2u| {
|
|
try writer.writeByte(DW.OP.const2u);
|
|
try writer.writeInt(u16, const2u, adapter.endian());
|
|
} else if (std.math.cast(u21, constu)) |const3u| {
|
|
try writer.writeByte(DW.OP.constu);
|
|
try writer.writeUleb128(const3u);
|
|
} else if (std.math.cast(u32, constu)) |const4u| {
|
|
try writer.writeByte(DW.OP.const4u);
|
|
try writer.writeInt(u32, const4u, adapter.endian());
|
|
} else if (std.math.cast(u49, constu)) |const7u| {
|
|
try writer.writeByte(DW.OP.constu);
|
|
try writer.writeUleb128(const7u);
|
|
} else {
|
|
try writer.writeByte(DW.OP.const8u);
|
|
try writer.writeInt(u64, constu, adapter.endian());
|
|
},
|
|
.consts => |consts| if (std.math.cast(i8, consts)) |const1s| {
|
|
try writer.writeAll(&.{ DW.OP.const1s, @bitCast(const1s) });
|
|
} else if (std.math.cast(i16, consts)) |const2s| {
|
|
try writer.writeByte(DW.OP.const2s);
|
|
try writer.writeInt(i16, const2s, adapter.endian());
|
|
} else if (std.math.cast(i21, consts)) |const3s| {
|
|
try writer.writeByte(DW.OP.consts);
|
|
try writer.writeSleb128(const3s);
|
|
} else if (std.math.cast(i32, consts)) |const4s| {
|
|
try writer.writeByte(DW.OP.const4s);
|
|
try writer.writeInt(i32, const4s, adapter.endian());
|
|
} else if (std.math.cast(i49, consts)) |const7s| {
|
|
try writer.writeByte(DW.OP.consts);
|
|
try writer.writeSleb128(const7s);
|
|
} else {
|
|
try writer.writeByte(DW.OP.const8s);
|
|
try writer.writeInt(i64, consts, adapter.endian());
|
|
},
|
|
.plus => |plus| done: {
|
|
if (plus[0].getConst(u0)) |_| {
|
|
try plus[1].write(adapter);
|
|
break :done;
|
|
}
|
|
if (plus[1].getConst(u0)) |_| {
|
|
try plus[0].write(adapter);
|
|
break :done;
|
|
}
|
|
if (plus[0].getBaseReg()) |breg| {
|
|
if (plus[1].getConst(i65)) |offset| {
|
|
try writeReg(breg, DW.OP.breg0, DW.OP.bregx, writer);
|
|
try writer.writeSleb128(offset);
|
|
break :done;
|
|
}
|
|
}
|
|
if (plus[1].getBaseReg()) |breg| {
|
|
if (plus[0].getConst(i65)) |offset| {
|
|
try writeReg(breg, DW.OP.breg0, DW.OP.bregx, writer);
|
|
try writer.writeSleb128(offset);
|
|
break :done;
|
|
}
|
|
}
|
|
if (plus[0].getConst(u64)) |uconst| {
|
|
try plus[1].write(adapter);
|
|
try writer.writeByte(DW.OP.plus_uconst);
|
|
try writer.writeUleb128(uconst);
|
|
break :done;
|
|
}
|
|
if (plus[1].getConst(u64)) |uconst| {
|
|
try plus[0].write(adapter);
|
|
try writer.writeByte(DW.OP.plus_uconst);
|
|
try writer.writeUleb128(uconst);
|
|
break :done;
|
|
}
|
|
try plus[0].write(adapter);
|
|
try plus[1].write(adapter);
|
|
try writer.writeByte(DW.OP.plus);
|
|
},
|
|
.reg => |reg| try writeReg(reg, DW.OP.reg0, DW.OP.regx, writer),
|
|
.breg => |breg| {
|
|
try writeReg(breg, DW.OP.breg0, DW.OP.bregx, writer);
|
|
try writer.writeSleb128(0);
|
|
},
|
|
.push_object_address => try writer.writeByte(DW.OP.push_object_address),
|
|
.call => |call| {
|
|
for (call.args) |arg| try arg.write(adapter);
|
|
try writer.writeByte(DW.OP.call_ref);
|
|
try adapter.infoEntry(call.unit, call.entry);
|
|
},
|
|
.form_tls_address => |addr| {
|
|
try addr.write(adapter);
|
|
try writer.writeByte(DW.OP.form_tls_address);
|
|
},
|
|
.implicit_value => |value| {
|
|
try writer.writeByte(DW.OP.implicit_value);
|
|
try writer.writeUleb128(value.len);
|
|
try writer.writeAll(value);
|
|
},
|
|
.stack_value => |value| {
|
|
try value.write(adapter);
|
|
try writer.writeByte(DW.OP.stack_value);
|
|
},
|
|
.implicit_pointer => |implicit_pointer| {
|
|
try writer.writeByte(DW.OP.implicit_pointer);
|
|
try adapter.infoEntry(implicit_pointer.unit, implicit_pointer.entry);
|
|
try writer.writeSleb128(implicit_pointer.offset);
|
|
},
|
|
.wasm_ext => |wasm_ext| {
|
|
try writer.writeByte(DW.OP.WASM_location);
|
|
switch (wasm_ext) {
|
|
.local => |local| {
|
|
try writer.writeByte(DW.OP.WASM_local);
|
|
try writer.writeUleb128(local);
|
|
},
|
|
.global => |global| if (std.math.cast(u21, global)) |global_u21| {
|
|
try writer.writeByte(DW.OP.WASM_global);
|
|
try writer.writeUleb128(global_u21);
|
|
} else {
|
|
try writer.writeByte(DW.OP.WASM_global_u32);
|
|
try writer.writeInt(u32, global, adapter.endian());
|
|
},
|
|
.operand_stack => |operand_stack| {
|
|
try writer.writeByte(DW.OP.WASM_operand_stack);
|
|
try writer.writeUleb128(operand_stack);
|
|
},
|
|
}
|
|
},
|
|
}
|
|
}
|
|
};
|
|
|
|
pub const Cfa = union(enum) {
|
|
nop,
|
|
advance_loc: u32,
|
|
offset: RegOff,
|
|
rel_offset: RegOff,
|
|
restore: u32,
|
|
undefined: u32,
|
|
same_value: u32,
|
|
register: [2]u32,
|
|
remember_state,
|
|
restore_state,
|
|
def_cfa: RegOff,
|
|
def_cfa_register: u32,
|
|
def_cfa_offset: i64,
|
|
adjust_cfa_offset: i64,
|
|
def_cfa_expression: Loc,
|
|
expression: RegExpr,
|
|
val_offset: RegOff,
|
|
val_expression: RegExpr,
|
|
escape: []const u8,
|
|
|
|
const RegOff = struct { reg: u32, off: i64 };
|
|
const RegExpr = struct { reg: u32, expr: Loc };
|
|
|
|
fn write(cfa: Cfa, wip_nav: *WipNav) (UpdateError || Writer.Error)!void {
|
|
const dfw = &wip_nav.debug_frame.writer;
|
|
switch (cfa) {
|
|
.nop => try dfw.writeByte(DW.CFA.nop),
|
|
.advance_loc => |loc| {
|
|
const delta = @divExact(loc - wip_nav.cfi.loc, wip_nav.dwarf.debug_frame.header.code_alignment_factor);
|
|
if (delta == 0) {} else if (std.math.cast(u6, delta)) |small_delta|
|
|
try dfw.writeByte(@as(u8, DW.CFA.advance_loc) + small_delta)
|
|
else if (std.math.cast(u8, delta)) |ubyte_delta|
|
|
try dfw.writeAll(&.{ DW.CFA.advance_loc1, ubyte_delta })
|
|
else if (std.math.cast(u16, delta)) |uhalf_delta| {
|
|
try dfw.writeByte(DW.CFA.advance_loc2);
|
|
try dfw.writeInt(u16, uhalf_delta, wip_nav.dwarf.endian);
|
|
} else if (std.math.cast(u32, delta)) |uword_delta| {
|
|
try dfw.writeByte(DW.CFA.advance_loc4);
|
|
try dfw.writeInt(u32, uword_delta, wip_nav.dwarf.endian);
|
|
}
|
|
wip_nav.cfi.loc = loc;
|
|
},
|
|
.offset, .rel_offset => |reg_off| {
|
|
const factored_off = @divExact(reg_off.off - switch (cfa) {
|
|
else => unreachable,
|
|
.offset => 0,
|
|
.rel_offset => wip_nav.cfi.cfa.off,
|
|
}, wip_nav.dwarf.debug_frame.header.data_alignment_factor);
|
|
if (std.math.cast(u63, factored_off)) |unsigned_off| {
|
|
if (std.math.cast(u6, reg_off.reg)) |small_reg| {
|
|
try dfw.writeByte(@as(u8, DW.CFA.offset) + small_reg);
|
|
} else {
|
|
try dfw.writeByte(DW.CFA.offset_extended);
|
|
try dfw.writeUleb128(reg_off.reg);
|
|
}
|
|
try dfw.writeUleb128(unsigned_off);
|
|
} else {
|
|
try dfw.writeByte(DW.CFA.offset_extended_sf);
|
|
try dfw.writeUleb128(reg_off.reg);
|
|
try dfw.writeSleb128(factored_off);
|
|
}
|
|
},
|
|
.restore => |reg| if (std.math.cast(u6, reg)) |small_reg|
|
|
try dfw.writeByte(@as(u8, DW.CFA.restore) + small_reg)
|
|
else {
|
|
try dfw.writeByte(DW.CFA.restore_extended);
|
|
try dfw.writeUleb128(reg);
|
|
},
|
|
.undefined => |reg| {
|
|
try dfw.writeByte(DW.CFA.undefined);
|
|
try dfw.writeUleb128(reg);
|
|
},
|
|
.same_value => |reg| {
|
|
try dfw.writeByte(DW.CFA.same_value);
|
|
try dfw.writeUleb128(reg);
|
|
},
|
|
.register => |regs| if (regs[0] != regs[1]) {
|
|
try dfw.writeByte(DW.CFA.register);
|
|
for (regs) |reg| try dfw.writeUleb128(reg);
|
|
} else {
|
|
try dfw.writeByte(DW.CFA.same_value);
|
|
try dfw.writeUleb128(regs[0]);
|
|
},
|
|
.remember_state => try dfw.writeByte(DW.CFA.remember_state),
|
|
.restore_state => try dfw.writeByte(DW.CFA.restore_state),
|
|
.def_cfa, .def_cfa_register, .def_cfa_offset, .adjust_cfa_offset => {
|
|
const reg_off: RegOff = switch (cfa) {
|
|
else => unreachable,
|
|
.def_cfa => |reg_off| reg_off,
|
|
.def_cfa_register => |reg| .{ .reg = reg, .off = wip_nav.cfi.cfa.off },
|
|
.def_cfa_offset => |off| .{ .reg = wip_nav.cfi.cfa.reg, .off = off },
|
|
.adjust_cfa_offset => |off| .{ .reg = wip_nav.cfi.cfa.reg, .off = wip_nav.cfi.cfa.off + off },
|
|
};
|
|
const changed_reg = reg_off.reg != wip_nav.cfi.cfa.reg;
|
|
const unsigned_off = std.math.cast(u63, reg_off.off);
|
|
if (reg_off.off == wip_nav.cfi.cfa.off) {
|
|
if (changed_reg) {
|
|
try dfw.writeByte(DW.CFA.def_cfa_register);
|
|
try dfw.writeUleb128(reg_off.reg);
|
|
}
|
|
} else if (switch (wip_nav.dwarf.debug_frame.header.data_alignment_factor) {
|
|
0 => unreachable,
|
|
1 => unsigned_off != null,
|
|
else => |data_alignment_factor| @rem(reg_off.off, data_alignment_factor) != 0,
|
|
}) {
|
|
try dfw.writeByte(if (changed_reg) DW.CFA.def_cfa else DW.CFA.def_cfa_offset);
|
|
if (changed_reg) try dfw.writeUleb128(reg_off.reg);
|
|
try dfw.writeUleb128(unsigned_off.?);
|
|
} else {
|
|
try dfw.writeByte(if (changed_reg) DW.CFA.def_cfa_sf else DW.CFA.def_cfa_offset_sf);
|
|
if (changed_reg) try dfw.writeUleb128(reg_off.reg);
|
|
try dfw.writeSleb128(@divExact(reg_off.off, wip_nav.dwarf.debug_frame.header.data_alignment_factor));
|
|
}
|
|
wip_nav.cfi.cfa = reg_off;
|
|
},
|
|
.def_cfa_expression => |expr| {
|
|
try dfw.writeByte(DW.CFA.def_cfa_expression);
|
|
try wip_nav.frameExprLoc(expr);
|
|
},
|
|
.expression => |reg_expr| {
|
|
try dfw.writeByte(DW.CFA.expression);
|
|
try dfw.writeUleb128(reg_expr.reg);
|
|
try wip_nav.frameExprLoc(reg_expr.expr);
|
|
},
|
|
.val_offset => |reg_off| {
|
|
const factored_off = @divExact(reg_off.off, wip_nav.dwarf.debug_frame.header.data_alignment_factor);
|
|
if (std.math.cast(u63, factored_off)) |unsigned_off| {
|
|
try dfw.writeByte(DW.CFA.val_offset);
|
|
try dfw.writeUleb128(reg_off.reg);
|
|
try dfw.writeUleb128(unsigned_off);
|
|
} else {
|
|
try dfw.writeByte(DW.CFA.val_offset_sf);
|
|
try dfw.writeUleb128(reg_off.reg);
|
|
try dfw.writeSleb128(factored_off);
|
|
}
|
|
},
|
|
.val_expression => |reg_expr| {
|
|
try dfw.writeByte(DW.CFA.val_expression);
|
|
try dfw.writeUleb128(reg_expr.reg);
|
|
try wip_nav.frameExprLoc(reg_expr.expr);
|
|
},
|
|
.escape => |bytes| try dfw.writeAll(bytes),
|
|
}
|
|
}
|
|
};
|
|
|
|
pub const WipNav = struct {
|
|
dwarf: *Dwarf,
|
|
pt: Zcu.PerThread,
|
|
unit: Unit.Index,
|
|
entry: Entry.Index,
|
|
any_children: bool,
|
|
func: InternPool.Index,
|
|
func_sym_index: u32,
|
|
func_high_pc: u32,
|
|
blocks: std.ArrayListUnmanaged(struct {
|
|
abbrev_code: u32,
|
|
low_pc_off: u64,
|
|
high_pc: u32,
|
|
}),
|
|
cfi: struct {
|
|
loc: u32,
|
|
cfa: Cfa.RegOff,
|
|
},
|
|
debug_frame: Writer.Allocating,
|
|
debug_info: Writer.Allocating,
|
|
debug_line: Writer.Allocating,
|
|
debug_loclists: Writer.Allocating,
|
|
pending_lazy: PendingLazy,
|
|
|
|
pub fn deinit(wip_nav: *WipNav) void {
|
|
const gpa = wip_nav.dwarf.gpa;
|
|
if (wip_nav.func != .none) wip_nav.blocks.deinit(gpa);
|
|
wip_nav.debug_frame.deinit();
|
|
wip_nav.debug_info.deinit();
|
|
wip_nav.debug_line.deinit();
|
|
wip_nav.debug_loclists.deinit();
|
|
wip_nav.pending_lazy.types.deinit(gpa);
|
|
wip_nav.pending_lazy.values.deinit(gpa);
|
|
}
|
|
|
|
pub fn genDebugFrame(wip_nav: *WipNav, loc: u32, cfa: Cfa) UpdateError!void {
|
|
return wip_nav.genDebugFrameWriterError(loc, cfa) catch |err| switch (err) {
|
|
error.WriteFailed => error.OutOfMemory,
|
|
else => |e| e,
|
|
};
|
|
}
|
|
fn genDebugFrameWriterError(wip_nav: *WipNav, loc: u32, cfa: Cfa) (UpdateError || Writer.Error)!void {
|
|
assert(wip_nav.func != .none);
|
|
if (wip_nav.dwarf.debug_frame.header.format == .none) return;
|
|
const loc_cfa: Cfa = .{ .advance_loc = loc };
|
|
try loc_cfa.write(wip_nav);
|
|
try cfa.write(wip_nav);
|
|
}
|
|
|
|
pub const LocalVarTag = enum { arg, local_var };
|
|
pub fn genLocalVarDebugInfo(
|
|
wip_nav: *WipNav,
|
|
tag: LocalVarTag,
|
|
opt_name: ?[]const u8,
|
|
ty: Type,
|
|
loc: Loc,
|
|
) UpdateError!void {
|
|
return wip_nav.genLocalVarDebugInfoWriterError(tag, opt_name, ty, loc) catch |err| switch (err) {
|
|
error.WriteFailed => error.OutOfMemory,
|
|
else => |e| e,
|
|
};
|
|
}
|
|
fn genLocalVarDebugInfoWriterError(
|
|
wip_nav: *WipNav,
|
|
tag: LocalVarTag,
|
|
opt_name: ?[]const u8,
|
|
ty: Type,
|
|
loc: Loc,
|
|
) (UpdateError || Writer.Error)!void {
|
|
assert(wip_nav.func != .none);
|
|
try wip_nav.abbrevCode(switch (tag) {
|
|
.arg => if (opt_name) |_| .arg else .unnamed_arg,
|
|
.local_var => if (opt_name) |_| .local_var else unreachable,
|
|
});
|
|
if (opt_name) |name| try wip_nav.strp(name);
|
|
try wip_nav.refType(ty);
|
|
try wip_nav.infoExprLoc(loc);
|
|
wip_nav.any_children = true;
|
|
}
|
|
|
|
pub const LocalConstTag = enum { comptime_arg, local_const };
|
|
pub fn genLocalConstDebugInfo(
|
|
wip_nav: *WipNav,
|
|
src_loc: Zcu.LazySrcLoc,
|
|
tag: LocalConstTag,
|
|
opt_name: ?[]const u8,
|
|
val: Value,
|
|
) UpdateError!void {
|
|
return wip_nav.genLocalConstDebugInfoWriterError(src_loc, tag, opt_name, val) catch |err| switch (err) {
|
|
error.WriteFailed => error.OutOfMemory,
|
|
else => |e| e,
|
|
};
|
|
}
|
|
fn genLocalConstDebugInfoWriterError(
|
|
wip_nav: *WipNav,
|
|
src_loc: Zcu.LazySrcLoc,
|
|
tag: LocalConstTag,
|
|
opt_name: ?[]const u8,
|
|
val: Value,
|
|
) (UpdateError || Writer.Error)!void {
|
|
assert(wip_nav.func != .none);
|
|
const pt = wip_nav.pt;
|
|
const zcu = pt.zcu;
|
|
const ty = val.typeOf(zcu);
|
|
const has_runtime_bits = ty.hasRuntimeBits(zcu);
|
|
const has_comptime_state = ty.comptimeOnly(zcu) and try ty.onePossibleValue(pt) == null;
|
|
try wip_nav.abbrevCode(if (has_runtime_bits and has_comptime_state) switch (tag) {
|
|
.comptime_arg => if (opt_name) |_| .comptime_arg_runtime_bits_comptime_state else .unnamed_comptime_arg_runtime_bits_comptime_state,
|
|
.local_const => if (opt_name) |_| .local_const_runtime_bits_comptime_state else unreachable,
|
|
} else if (has_comptime_state) switch (tag) {
|
|
.comptime_arg => if (opt_name) |_| .comptime_arg_comptime_state else .unnamed_comptime_arg_comptime_state,
|
|
.local_const => if (opt_name) |_| .local_const_comptime_state else unreachable,
|
|
} else if (has_runtime_bits) switch (tag) {
|
|
.comptime_arg => if (opt_name) |_| .comptime_arg_runtime_bits else .unnamed_comptime_arg_runtime_bits,
|
|
.local_const => if (opt_name) |_| .local_const_runtime_bits else unreachable,
|
|
} else switch (tag) {
|
|
.comptime_arg => if (opt_name) |_| .comptime_arg else .unnamed_comptime_arg,
|
|
.local_const => if (opt_name) |_| .local_const else unreachable,
|
|
});
|
|
if (opt_name) |name| try wip_nav.strp(name);
|
|
try wip_nav.refType(ty);
|
|
if (has_runtime_bits) try wip_nav.blockValue(src_loc, val);
|
|
if (has_comptime_state) try wip_nav.refValue(val);
|
|
wip_nav.any_children = true;
|
|
}
|
|
|
|
pub fn genVarArgsDebugInfo(wip_nav: *WipNav) UpdateError!void {
|
|
return wip_nav.genVarArgsDebugInfoWriterError() catch |err| switch (err) {
|
|
error.WriteFailed => error.OutOfMemory,
|
|
else => |e| e,
|
|
};
|
|
}
|
|
fn genVarArgsDebugInfoWriterError(wip_nav: *WipNav) (UpdateError || Writer.Error)!void {
|
|
assert(wip_nav.func != .none);
|
|
try wip_nav.abbrevCode(.is_var_args);
|
|
wip_nav.any_children = true;
|
|
}
|
|
|
|
pub fn advancePCAndLine(wip_nav: *WipNav, delta_line: i33, delta_pc: u64) Allocator.Error!void {
|
|
return wip_nav.advancePCAndLineWriterError(delta_line, delta_pc) catch |err| switch (err) {
|
|
error.WriteFailed => error.OutOfMemory,
|
|
};
|
|
}
|
|
fn advancePCAndLineWriterError(
|
|
wip_nav: *WipNav,
|
|
delta_line: i33,
|
|
delta_pc: u64,
|
|
) Writer.Error!void {
|
|
const dlw = &wip_nav.debug_line.writer;
|
|
|
|
const header = wip_nav.dwarf.debug_line.header;
|
|
assert(header.maximum_operations_per_instruction == 1);
|
|
const delta_op: u64 = 0;
|
|
|
|
const remaining_delta_line: i9 = @intCast(if (delta_line < header.line_base or
|
|
delta_line - header.line_base >= header.line_range)
|
|
remaining: {
|
|
assert(delta_line != 0);
|
|
try dlw.writeByte(DW.LNS.advance_line);
|
|
try dlw.writeSleb128(delta_line);
|
|
break :remaining 0;
|
|
} else delta_line);
|
|
|
|
const op_advance = @divExact(delta_pc, header.minimum_instruction_length) *
|
|
header.maximum_operations_per_instruction + delta_op;
|
|
const max_op_advance: u9 = (std.math.maxInt(u8) - header.opcode_base) / header.line_range;
|
|
const remaining_op_advance: u8 = @intCast(if (op_advance >= 2 * max_op_advance) remaining: {
|
|
try dlw.writeByte(DW.LNS.advance_pc);
|
|
try dlw.writeUleb128(op_advance);
|
|
break :remaining 0;
|
|
} else if (op_advance >= max_op_advance) remaining: {
|
|
try dlw.writeByte(DW.LNS.const_add_pc);
|
|
break :remaining op_advance - max_op_advance;
|
|
} else op_advance);
|
|
|
|
if (remaining_delta_line == 0 and remaining_op_advance == 0)
|
|
try dlw.writeByte(DW.LNS.copy)
|
|
else
|
|
try dlw.writeByte(@intCast((remaining_delta_line - header.line_base) +
|
|
(header.line_range * remaining_op_advance) + header.opcode_base));
|
|
}
|
|
|
|
pub fn setColumn(wip_nav: *WipNav, column: u32) Allocator.Error!void {
|
|
return wip_nav.setColumnWriterError(column) catch |err| switch (err) {
|
|
error.WriteFailed => error.OutOfMemory,
|
|
};
|
|
}
|
|
fn setColumnWriterError(wip_nav: *WipNav, column: u32) Writer.Error!void {
|
|
const dlw = &wip_nav.debug_line.writer;
|
|
try dlw.writeByte(DW.LNS.set_column);
|
|
try dlw.writeUleb128(column + 1);
|
|
}
|
|
|
|
pub fn negateStmt(wip_nav: *WipNav) Allocator.Error!void {
|
|
return wip_nav.negateStmtWriterError() catch |err| switch (err) {
|
|
error.WriteFailed => error.OutOfMemory,
|
|
};
|
|
}
|
|
fn negateStmtWriterError(wip_nav: *WipNav) Writer.Error!void {
|
|
try wip_nav.debug_line.writer.writeByte(DW.LNS.negate_stmt);
|
|
}
|
|
|
|
pub fn setPrologueEnd(wip_nav: *WipNav) Allocator.Error!void {
|
|
return wip_nav.setPrologueEndWriterError() catch |err| switch (err) {
|
|
error.WriteFailed => error.OutOfMemory,
|
|
};
|
|
}
|
|
fn setPrologueEndWriterError(wip_nav: *WipNav) Writer.Error!void {
|
|
try wip_nav.debug_line.writer.writeByte(DW.LNS.set_prologue_end);
|
|
}
|
|
|
|
pub fn setEpilogueBegin(wip_nav: *WipNav) Allocator.Error!void {
|
|
return wip_nav.setEpilogueBeginWriterError() catch |err| switch (err) {
|
|
error.WriteFailed => error.OutOfMemory,
|
|
};
|
|
}
|
|
fn setEpilogueBeginWriterError(wip_nav: *WipNav) Writer.Error!void {
|
|
try wip_nav.debug_line.writer.writeByte(DW.LNS.set_epilogue_begin);
|
|
}
|
|
|
|
pub fn enterBlock(wip_nav: *WipNav, code_off: u64) UpdateError!void {
|
|
return wip_nav.enterBlockWriterError(code_off) catch |err| switch (err) {
|
|
error.WriteFailed => error.OutOfMemory,
|
|
else => |e| e,
|
|
};
|
|
}
|
|
fn enterBlockWriterError(wip_nav: *WipNav, code_off: u64) (UpdateError || Writer.Error)!void {
|
|
const dwarf = wip_nav.dwarf;
|
|
const diw = &wip_nav.debug_info.writer;
|
|
const block = try wip_nav.blocks.addOne(dwarf.gpa);
|
|
|
|
block.abbrev_code = @intCast(diw.end);
|
|
try wip_nav.abbrevCode(.block);
|
|
block.low_pc_off = code_off;
|
|
try wip_nav.infoAddrSym(wip_nav.func_sym_index, code_off);
|
|
block.high_pc = @intCast(diw.end);
|
|
try diw.writeInt(u32, 0, dwarf.endian);
|
|
wip_nav.any_children = false;
|
|
}
|
|
|
|
pub fn leaveBlock(wip_nav: *WipNav, code_off: u64) UpdateError!void {
|
|
return wip_nav.leaveBlockWriterError(code_off) catch |err| switch (err) {
|
|
error.WriteFailed => error.OutOfMemory,
|
|
else => |e| e,
|
|
};
|
|
}
|
|
fn leaveBlockWriterError(wip_nav: *WipNav, code_off: u64) (UpdateError || Writer.Error)!void {
|
|
const block_bytes = comptime uleb128Bytes(@intFromEnum(AbbrevCode.block));
|
|
const block = wip_nav.blocks.pop().?;
|
|
if (wip_nav.any_children)
|
|
try wip_nav.debug_info.writer.writeUleb128(@intFromEnum(AbbrevCode.null))
|
|
else
|
|
std.leb.writeUnsignedFixed(
|
|
block_bytes,
|
|
wip_nav.debug_info.written()[block.abbrev_code..][0..block_bytes],
|
|
@intCast(try wip_nav.dwarf.refAbbrevCode(.empty_block)),
|
|
);
|
|
std.mem.writeInt(u32, wip_nav.debug_info.written()[block.high_pc..][0..4], @intCast(code_off - block.low_pc_off), wip_nav.dwarf.endian);
|
|
wip_nav.any_children = true;
|
|
}
|
|
|
|
pub fn enterInlineFunc(
|
|
wip_nav: *WipNav,
|
|
func: InternPool.Index,
|
|
code_off: u64,
|
|
line: u32,
|
|
column: u32,
|
|
) UpdateError!void {
|
|
return wip_nav.enterInlineFuncWriterError(func, code_off, line, column) catch |err| switch (err) {
|
|
error.WriteFailed => error.OutOfMemory,
|
|
else => |e| e,
|
|
};
|
|
}
|
|
fn enterInlineFuncWriterError(
|
|
wip_nav: *WipNav,
|
|
func: InternPool.Index,
|
|
code_off: u64,
|
|
line: u32,
|
|
column: u32,
|
|
) (UpdateError || Writer.Error)!void {
|
|
const dwarf = wip_nav.dwarf;
|
|
const zcu = wip_nav.pt.zcu;
|
|
const diw = &wip_nav.debug_info.writer;
|
|
const block = try wip_nav.blocks.addOne(dwarf.gpa);
|
|
|
|
block.abbrev_code = @intCast(diw.end);
|
|
try wip_nav.abbrevCode(.inlined_func);
|
|
try wip_nav.refNav(zcu.funcInfo(func).owner_nav);
|
|
try diw.writeUleb128(zcu.navSrcLine(zcu.funcInfo(wip_nav.func).owner_nav) + line + 1);
|
|
try diw.writeUleb128(column + 1);
|
|
block.low_pc_off = code_off;
|
|
try wip_nav.infoAddrSym(wip_nav.func_sym_index, code_off);
|
|
block.high_pc = @intCast(diw.end);
|
|
try diw.writeInt(u32, 0, dwarf.endian);
|
|
try wip_nav.setInlineFunc(func);
|
|
wip_nav.any_children = false;
|
|
}
|
|
|
|
pub fn leaveInlineFunc(wip_nav: *WipNav, func: InternPool.Index, code_off: u64) UpdateError!void {
|
|
return wip_nav.leaveInlineFuncWriterError(func, code_off) catch |err| switch (err) {
|
|
error.WriteFailed => error.OutOfMemory,
|
|
else => |e| e,
|
|
};
|
|
}
|
|
fn leaveInlineFuncWriterError(
|
|
wip_nav: *WipNav,
|
|
func: InternPool.Index,
|
|
code_off: u64,
|
|
) (UpdateError || Writer.Error)!void {
|
|
const inlined_func_bytes = comptime uleb128Bytes(@intFromEnum(AbbrevCode.inlined_func));
|
|
const block = wip_nav.blocks.pop().?;
|
|
if (wip_nav.any_children)
|
|
try wip_nav.debug_info.writer.writeUleb128(@intFromEnum(AbbrevCode.null))
|
|
else
|
|
std.leb.writeUnsignedFixed(
|
|
inlined_func_bytes,
|
|
wip_nav.debug_info.written()[block.abbrev_code..][0..inlined_func_bytes],
|
|
@intCast(try wip_nav.dwarf.refAbbrevCode(.empty_inlined_func)),
|
|
);
|
|
std.mem.writeInt(u32, wip_nav.debug_info.written()[block.high_pc..][0..4], @intCast(code_off - block.low_pc_off), wip_nav.dwarf.endian);
|
|
try wip_nav.setInlineFunc(func);
|
|
wip_nav.any_children = true;
|
|
}
|
|
|
|
pub fn setInlineFunc(wip_nav: *WipNav, func: InternPool.Index) UpdateError!void {
|
|
return wip_nav.setInlineFuncWriterError(func) catch |err| switch (err) {
|
|
error.WriteFailed => error.OutOfMemory,
|
|
else => |e| e,
|
|
};
|
|
}
|
|
fn setInlineFuncWriterError(wip_nav: *WipNav, func: InternPool.Index) (UpdateError || Writer.Error)!void {
|
|
const zcu = wip_nav.pt.zcu;
|
|
const dwarf = wip_nav.dwarf;
|
|
if (wip_nav.func == func) return;
|
|
|
|
const new_func_info = zcu.funcInfo(func);
|
|
const new_file = zcu.navFileScopeIndex(new_func_info.owner_nav);
|
|
const new_unit = try dwarf.getUnit(zcu.fileByIndex(new_file).mod.?);
|
|
|
|
const dlw = &wip_nav.debug_line.writer;
|
|
if (dwarf.incremental()) {
|
|
const new_nav_gop = try dwarf.navs.getOrPut(dwarf.gpa, new_func_info.owner_nav);
|
|
errdefer _ = if (!new_nav_gop.found_existing) dwarf.navs.pop();
|
|
if (!new_nav_gop.found_existing) new_nav_gop.value_ptr.* = try dwarf.addCommonEntry(new_unit);
|
|
|
|
try dlw.writeByte(DW.LNS.extended_op);
|
|
try dlw.writeUleb128(1 + dwarf.sectionOffsetBytes());
|
|
try dlw.writeByte(DW.LNE.ZIG_set_decl);
|
|
try dwarf.debug_line.section.getUnit(wip_nav.unit).getEntry(wip_nav.entry).cross_section_relocs.append(dwarf.gpa, .{
|
|
.source_off = @intCast(dlw.end),
|
|
.target_sec = .debug_info,
|
|
.target_unit = new_unit,
|
|
.target_entry = new_nav_gop.value_ptr.toOptional(),
|
|
});
|
|
try dlw.splatByteAll(0, dwarf.sectionOffsetBytes());
|
|
return;
|
|
}
|
|
|
|
const old_func_info = zcu.funcInfo(wip_nav.func);
|
|
const old_file = zcu.navFileScopeIndex(old_func_info.owner_nav);
|
|
if (old_file != new_file) {
|
|
const mod_info = dwarf.getModInfo(wip_nav.unit);
|
|
try mod_info.dirs.put(dwarf.gpa, new_unit, {});
|
|
const file_gop = try mod_info.files.getOrPut(dwarf.gpa, new_file);
|
|
|
|
try dlw.writeByte(DW.LNS.set_file);
|
|
try dlw.writeUleb128(file_gop.index);
|
|
}
|
|
|
|
const old_src_line: i33 = zcu.navSrcLine(old_func_info.owner_nav);
|
|
const new_src_line: i33 = zcu.navSrcLine(new_func_info.owner_nav);
|
|
if (new_src_line != old_src_line) {
|
|
try dlw.writeByte(DW.LNS.advance_line);
|
|
try dlw.writeSleb128(new_src_line - old_src_line);
|
|
}
|
|
|
|
wip_nav.func = func;
|
|
}
|
|
|
|
fn externalReloc(wip_nav: *WipNav, sec: *Section, reloc: ExternalReloc) Allocator.Error!void {
|
|
try sec.getUnit(wip_nav.unit).getEntry(wip_nav.entry).external_relocs.append(wip_nav.dwarf.gpa, reloc);
|
|
}
|
|
|
|
pub fn infoExternalReloc(wip_nav: *WipNav, reloc: ExternalReloc) Allocator.Error!void {
|
|
try wip_nav.externalReloc(&wip_nav.dwarf.debug_info.section, reloc);
|
|
}
|
|
|
|
fn frameExternalReloc(wip_nav: *WipNav, reloc: ExternalReloc) Allocator.Error!void {
|
|
try wip_nav.externalReloc(&wip_nav.dwarf.debug_frame.section, reloc);
|
|
}
|
|
|
|
fn abbrevCode(wip_nav: *WipNav, abbrev_code: AbbrevCode) (UpdateError || Writer.Error)!void {
|
|
try wip_nav.debug_info.writer.writeUleb128(try wip_nav.dwarf.refAbbrevCode(abbrev_code));
|
|
}
|
|
|
|
fn sectionOffset(
|
|
wip_nav: *WipNav,
|
|
comptime sec: Section.Index,
|
|
target_sec: Section.Index,
|
|
target_unit: Unit.Index,
|
|
target_entry: Entry.Index,
|
|
target_off: u32,
|
|
) (UpdateError || Writer.Error)!void {
|
|
const dwarf = wip_nav.dwarf;
|
|
const gpa = dwarf.gpa;
|
|
const entry_ptr = @field(dwarf, @tagName(sec)).section.getUnit(wip_nav.unit).getEntry(wip_nav.entry);
|
|
const sw = &@field(wip_nav, @tagName(sec)).writer;
|
|
const source_off: u32 = @intCast(sw.end);
|
|
if (target_sec != sec) {
|
|
try entry_ptr.cross_section_relocs.append(gpa, .{
|
|
.source_off = source_off,
|
|
.target_sec = target_sec,
|
|
.target_unit = target_unit,
|
|
.target_entry = target_entry.toOptional(),
|
|
.target_off = target_off,
|
|
});
|
|
} else if (target_unit != wip_nav.unit) {
|
|
try entry_ptr.cross_unit_relocs.append(gpa, .{
|
|
.source_off = source_off,
|
|
.target_unit = target_unit,
|
|
.target_entry = target_entry.toOptional(),
|
|
.target_off = target_off,
|
|
});
|
|
} else {
|
|
try entry_ptr.cross_entry_relocs.append(gpa, .{
|
|
.source_off = source_off,
|
|
.target_entry = target_entry.toOptional(),
|
|
.target_off = target_off,
|
|
});
|
|
}
|
|
try sw.splatByteAll(0, dwarf.sectionOffsetBytes());
|
|
}
|
|
|
|
fn infoSectionOffset(
|
|
wip_nav: *WipNav,
|
|
target_sec: Section.Index,
|
|
target_unit: Unit.Index,
|
|
target_entry: Entry.Index,
|
|
target_off: u32,
|
|
) (UpdateError || Writer.Error)!void {
|
|
try wip_nav.sectionOffset(.debug_info, target_sec, target_unit, target_entry, target_off);
|
|
}
|
|
|
|
fn strp(wip_nav: *WipNav, str: []const u8) (UpdateError || Writer.Error)!void {
|
|
try wip_nav.infoSectionOffset(.debug_str, StringSection.unit, try wip_nav.dwarf.debug_str.addString(wip_nav.dwarf, str), 0);
|
|
}
|
|
|
|
const ExprLocCounter = struct {
|
|
dw: Writer.Discarding,
|
|
section_offset_bytes: u32,
|
|
address_size: AddressSize,
|
|
fn init(dwarf: *Dwarf, buf: []u8) ExprLocCounter {
|
|
return .{
|
|
.dw = .init(buf),
|
|
.section_offset_bytes = dwarf.sectionOffsetBytes(),
|
|
.address_size = dwarf.address_size,
|
|
};
|
|
}
|
|
fn writer(counter: *ExprLocCounter) *Writer {
|
|
return &counter.dw.writer;
|
|
}
|
|
fn endian(_: ExprLocCounter) std.builtin.Endian {
|
|
return @import("builtin").cpu.arch.endian();
|
|
}
|
|
fn addrSym(counter: *ExprLocCounter, _: u32) Writer.Error!void {
|
|
try counter.dw.writer.splatByteAll(undefined, @intFromEnum(counter.address_size));
|
|
}
|
|
fn infoEntry(counter: *ExprLocCounter, _: Unit.Index, _: Entry.Index) Writer.Error!void {
|
|
try counter.dw.writer.splatByteAll(undefined, counter.section_offset_bytes);
|
|
}
|
|
};
|
|
|
|
fn infoExprLoc(wip_nav: *WipNav, loc: Loc) (UpdateError || Writer.Error)!void {
|
|
var buf: [64]u8 = undefined;
|
|
var counter: ExprLocCounter = .init(wip_nav.dwarf, &buf);
|
|
try loc.write(&counter);
|
|
|
|
const adapter: struct {
|
|
wip_nav: *WipNav,
|
|
fn writer(ctx: @This()) *Writer {
|
|
return &ctx.wip_nav.debug_info.writer;
|
|
}
|
|
fn endian(ctx: @This()) std.builtin.Endian {
|
|
return ctx.wip_nav.dwarf.endian;
|
|
}
|
|
fn addrSym(ctx: @This(), sym_index: u32) (UpdateError || Writer.Error)!void {
|
|
try ctx.wip_nav.infoAddrSym(sym_index, 0);
|
|
}
|
|
fn infoEntry(
|
|
ctx: @This(),
|
|
unit: Unit.Index,
|
|
entry: Entry.Index,
|
|
) (UpdateError || Writer.Error)!void {
|
|
try ctx.wip_nav.infoSectionOffset(.debug_info, unit, entry, 0);
|
|
}
|
|
} = .{ .wip_nav = wip_nav };
|
|
try adapter.writer().writeUleb128(counter.dw.count + counter.dw.writer.end);
|
|
try loc.write(adapter);
|
|
}
|
|
|
|
fn infoAddrSym(
|
|
wip_nav: *WipNav,
|
|
sym_index: u32,
|
|
sym_off: u64,
|
|
) (UpdateError || Writer.Error)!void {
|
|
const diw = &wip_nav.debug_info.writer;
|
|
try wip_nav.infoExternalReloc(.{
|
|
.source_off = @intCast(diw.end),
|
|
.target_sym = sym_index,
|
|
.target_off = sym_off,
|
|
});
|
|
try diw.splatByteAll(0, @intFromEnum(wip_nav.dwarf.address_size));
|
|
}
|
|
|
|
fn frameExprLoc(wip_nav: *WipNav, loc: Loc) (UpdateError || Writer.Error)!void {
|
|
var buf: [64]u8 = undefined;
|
|
var counter: ExprLocCounter = .init(wip_nav.dwarf, &buf);
|
|
try loc.write(&counter);
|
|
|
|
const adapter: struct {
|
|
wip_nav: *WipNav,
|
|
fn writer(ctx: @This()) *Writer {
|
|
return &ctx.wip_nav.debug_frame.writer;
|
|
}
|
|
fn endian(ctx: @This()) std.builtin.Endian {
|
|
return ctx.wip_nav.dwarf.endian;
|
|
}
|
|
fn addrSym(ctx: @This(), sym_index: u32) (UpdateError || Writer.Error)!void {
|
|
try ctx.wip_nav.frameAddrSym(sym_index, 0);
|
|
}
|
|
fn infoEntry(
|
|
ctx: @This(),
|
|
unit: Unit.Index,
|
|
entry: Entry.Index,
|
|
) (UpdateError || Writer.Error)!void {
|
|
try ctx.wip_nav.sectionOffset(.debug_frame, .debug_info, unit, entry, 0);
|
|
}
|
|
} = .{ .wip_nav = wip_nav };
|
|
try adapter.writer().writeUleb128(counter.dw.count + counter.dw.writer.end);
|
|
try loc.write(adapter);
|
|
}
|
|
|
|
fn frameAddrSym(
|
|
wip_nav: *WipNav,
|
|
sym_index: u32,
|
|
sym_off: u64,
|
|
) (UpdateError || Writer.Error)!void {
|
|
const dfw = &wip_nav.debug_frame.writer;
|
|
try wip_nav.frameExternalReloc(.{
|
|
.source_off = @intCast(dfw.end),
|
|
.target_sym = sym_index,
|
|
.target_off = sym_off,
|
|
});
|
|
try dfw.splatByteAll(0, @intFromEnum(wip_nav.dwarf.address_size));
|
|
}
|
|
|
|
fn getNavEntry(
|
|
wip_nav: *WipNav,
|
|
nav_index: InternPool.Nav.Index,
|
|
) UpdateError!struct { Unit.Index, Entry.Index } {
|
|
const zcu = wip_nav.pt.zcu;
|
|
const ip = &zcu.intern_pool;
|
|
const nav = ip.getNav(nav_index);
|
|
const unit = try wip_nav.dwarf.getUnit(zcu.fileByIndex(nav.srcInst(ip).resolveFile(ip)).mod.?);
|
|
const gop = try wip_nav.dwarf.navs.getOrPut(wip_nav.dwarf.gpa, nav_index);
|
|
if (gop.found_existing) return .{ unit, gop.value_ptr.* };
|
|
const entry = try wip_nav.dwarf.addCommonEntry(unit);
|
|
gop.value_ptr.* = entry;
|
|
return .{ unit, entry };
|
|
}
|
|
|
|
fn refNav(
|
|
wip_nav: *WipNav,
|
|
nav_index: InternPool.Nav.Index,
|
|
) (UpdateError || Writer.Error)!void {
|
|
const unit, const entry = try wip_nav.getNavEntry(nav_index);
|
|
try wip_nav.infoSectionOffset(.debug_info, unit, entry, 0);
|
|
}
|
|
|
|
fn getTypeEntry(wip_nav: *WipNav, ty: Type) UpdateError!struct { Unit.Index, Entry.Index } {
|
|
const zcu = wip_nav.pt.zcu;
|
|
const ip = &zcu.intern_pool;
|
|
const maybe_inst_index = ty.typeDeclInst(zcu);
|
|
const unit = if (maybe_inst_index) |inst_index| switch (switch (ip.indexToKey(ty.toIntern())) {
|
|
else => unreachable,
|
|
.struct_type => ip.loadStructType(ty.toIntern()).name_nav,
|
|
.union_type => ip.loadUnionType(ty.toIntern()).name_nav,
|
|
.enum_type => ip.loadEnumType(ty.toIntern()).name_nav,
|
|
.opaque_type => ip.loadOpaqueType(ty.toIntern()).name_nav,
|
|
}) {
|
|
.none => try wip_nav.dwarf.getUnit(zcu.fileByIndex(inst_index.resolveFile(ip)).mod.?),
|
|
else => |name_nav| return wip_nav.getNavEntry(name_nav.unwrap().?),
|
|
} else .main;
|
|
const gop = try wip_nav.dwarf.types.getOrPut(wip_nav.dwarf.gpa, ty.toIntern());
|
|
if (gop.found_existing) return .{ unit, gop.value_ptr.* };
|
|
const entry = try wip_nav.dwarf.addCommonEntry(unit);
|
|
gop.value_ptr.* = entry;
|
|
if (maybe_inst_index == null) try wip_nav.pending_lazy.types.append(wip_nav.dwarf.gpa, ty.toIntern());
|
|
return .{ unit, entry };
|
|
}
|
|
|
|
fn refType(wip_nav: *WipNav, ty: Type) (UpdateError || Writer.Error)!void {
|
|
const unit, const entry = try wip_nav.getTypeEntry(ty);
|
|
try wip_nav.infoSectionOffset(.debug_info, unit, entry, 0);
|
|
}
|
|
|
|
fn getValueEntry(wip_nav: *WipNav, value: Value) UpdateError!struct { Unit.Index, Entry.Index } {
|
|
const zcu = wip_nav.pt.zcu;
|
|
const ip = &zcu.intern_pool;
|
|
const ty = value.typeOf(zcu);
|
|
if (std.debug.runtime_safety) assert(ty.comptimeOnly(zcu) and try ty.onePossibleValue(wip_nav.pt) == null);
|
|
if (ty.toIntern() == .type_type) return wip_nav.getTypeEntry(value.toType());
|
|
if (ip.isFunctionType(ty.toIntern()) and !value.isUndef(zcu)) return wip_nav.getNavEntry(switch (ip.indexToKey(value.toIntern())) {
|
|
else => unreachable,
|
|
.func => |func| func.owner_nav,
|
|
.@"extern" => |@"extern"| @"extern".owner_nav,
|
|
});
|
|
const gop = try wip_nav.dwarf.values.getOrPut(wip_nav.dwarf.gpa, value.toIntern());
|
|
const unit: Unit.Index = .main;
|
|
if (gop.found_existing) return .{ unit, gop.value_ptr.* };
|
|
const entry = try wip_nav.dwarf.addCommonEntry(unit);
|
|
gop.value_ptr.* = entry;
|
|
try wip_nav.pending_lazy.values.append(wip_nav.dwarf.gpa, value.toIntern());
|
|
return .{ unit, entry };
|
|
}
|
|
|
|
fn refValue(wip_nav: *WipNav, value: Value) (UpdateError || Writer.Error)!void {
|
|
const unit, const entry = try wip_nav.getValueEntry(value);
|
|
try wip_nav.infoSectionOffset(.debug_info, unit, entry, 0);
|
|
}
|
|
|
|
fn refForward(wip_nav: *WipNav) (Allocator.Error || Writer.Error)!u32 {
|
|
const dwarf = wip_nav.dwarf;
|
|
const diw = &wip_nav.debug_info.writer;
|
|
const cross_entry_relocs = &dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(wip_nav.entry).cross_entry_relocs;
|
|
const reloc_index: u32 = @intCast(cross_entry_relocs.items.len);
|
|
try cross_entry_relocs.append(dwarf.gpa, .{
|
|
.source_off = @intCast(diw.end),
|
|
.target_entry = undefined,
|
|
.target_off = undefined,
|
|
});
|
|
try diw.splatByteAll(0, dwarf.sectionOffsetBytes());
|
|
return reloc_index;
|
|
}
|
|
|
|
fn finishForward(wip_nav: *WipNav, reloc_index: u32) void {
|
|
const reloc = &wip_nav.dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(wip_nav.entry).cross_entry_relocs.items[reloc_index];
|
|
reloc.target_entry = wip_nav.entry.toOptional();
|
|
reloc.target_off = @intCast(wip_nav.debug_info.writer.end);
|
|
}
|
|
|
|
fn blockValue(
|
|
wip_nav: *WipNav,
|
|
src_loc: Zcu.LazySrcLoc,
|
|
val: Value,
|
|
) (UpdateError || Writer.Error)!void {
|
|
const ty = val.typeOf(wip_nav.pt.zcu);
|
|
const diw = &wip_nav.debug_info.writer;
|
|
const size = if (ty.hasRuntimeBits(wip_nav.pt.zcu)) ty.abiSize(wip_nav.pt.zcu) else 0;
|
|
try diw.writeUleb128(size);
|
|
if (size == 0) return;
|
|
const old_end = wip_nav.debug_info.writer.end;
|
|
try codegen.generateSymbol(
|
|
wip_nav.dwarf.bin_file,
|
|
wip_nav.pt,
|
|
src_loc,
|
|
val,
|
|
&wip_nav.debug_info.writer,
|
|
.{ .debug_output = .{ .dwarf = wip_nav } },
|
|
);
|
|
if (old_end + size != wip_nav.debug_info.writer.end) {
|
|
std.debug.print("{f} [{}]: {} != {}\n", .{
|
|
ty.fmt(wip_nav.pt),
|
|
ty.toIntern(),
|
|
size,
|
|
wip_nav.debug_info.writer.end - old_end,
|
|
});
|
|
unreachable;
|
|
}
|
|
}
|
|
|
|
const AbbrevCodeForForm = struct {
|
|
sdata: AbbrevCode,
|
|
udata: AbbrevCode,
|
|
block: AbbrevCode,
|
|
};
|
|
|
|
fn bigIntConstValue(
|
|
wip_nav: *WipNav,
|
|
abbrev_code: AbbrevCodeForForm,
|
|
ty: Type,
|
|
big_int: std.math.big.int.Const,
|
|
) (UpdateError || Writer.Error)!void {
|
|
const zcu = wip_nav.pt.zcu;
|
|
const diw = &wip_nav.debug_info.writer;
|
|
const signedness = switch (ty.toIntern()) {
|
|
.comptime_int_type, .comptime_float_type => .signed,
|
|
else => ty.intInfo(zcu).signedness,
|
|
};
|
|
const bits = @max(1, big_int.bitCountTwosCompForSignedness(signedness));
|
|
if (bits <= 64) {
|
|
try wip_nav.abbrevCode(switch (signedness) {
|
|
.signed => abbrev_code.sdata,
|
|
.unsigned => abbrev_code.udata,
|
|
});
|
|
try wip_nav.debug_info.ensureUnusedCapacity(std.math.divCeil(usize, bits, 7) catch unreachable);
|
|
var bit: usize = 0;
|
|
var carry: u1 = 1;
|
|
while (bit < bits) {
|
|
const limb_bits = @typeInfo(std.math.big.Limb).int.bits;
|
|
const limb_index = bit / limb_bits;
|
|
const limb_shift: std.math.Log2Int(std.math.big.Limb) = @intCast(bit % limb_bits);
|
|
const low_abs_part: u7 = @truncate(big_int.limbs[limb_index] >> limb_shift);
|
|
const abs_part = if (limb_shift > limb_bits - 7 and limb_index + 1 < big_int.limbs.len) abs_part: {
|
|
const high_abs_part: u7 = @truncate(big_int.limbs[limb_index + 1] << -%limb_shift);
|
|
break :abs_part high_abs_part | low_abs_part;
|
|
} else low_abs_part;
|
|
const twos_comp_part = if (big_int.positive) abs_part else twos_comp_part: {
|
|
const twos_comp_part, carry = @addWithOverflow(~abs_part, carry);
|
|
break :twos_comp_part twos_comp_part;
|
|
};
|
|
bit += 7;
|
|
diw.writeByte(@as(u8, if (bit < bits) 0x80 else 0x00) | twos_comp_part) catch unreachable;
|
|
}
|
|
} else {
|
|
try wip_nav.abbrevCode(abbrev_code.block);
|
|
const bytes = @max(ty.abiSize(zcu), std.math.divCeil(usize, bits, 8) catch unreachable);
|
|
try diw.writeUleb128(bytes);
|
|
try wip_nav.debug_info.ensureUnusedCapacity(@intCast(bytes));
|
|
big_int.writeTwosComplement(
|
|
try diw.writableSlice(@intCast(bytes)),
|
|
wip_nav.dwarf.endian,
|
|
);
|
|
}
|
|
}
|
|
|
|
fn enumConstValue(
|
|
wip_nav: *WipNav,
|
|
loaded_enum: InternPool.LoadedEnumType,
|
|
abbrev_code: AbbrevCodeForForm,
|
|
field_index: usize,
|
|
) (UpdateError || Writer.Error)!void {
|
|
const zcu = wip_nav.pt.zcu;
|
|
const ip = &zcu.intern_pool;
|
|
var big_int_space: Value.BigIntSpace = undefined;
|
|
try wip_nav.bigIntConstValue(abbrev_code, .fromInterned(loaded_enum.tag_ty), if (loaded_enum.values.len > 0)
|
|
Value.fromInterned(loaded_enum.values.get(ip)[field_index]).toBigInt(&big_int_space, zcu)
|
|
else
|
|
std.math.big.int.Mutable.init(&big_int_space.limbs, field_index).toConst());
|
|
}
|
|
|
|
fn declCommon(
|
|
wip_nav: *WipNav,
|
|
abbrev_code: struct {
|
|
decl: AbbrevCode,
|
|
generic_decl: AbbrevCode,
|
|
decl_instance: AbbrevCode,
|
|
},
|
|
nav: *const InternPool.Nav,
|
|
file: Zcu.File.Index,
|
|
decl: *const std.zig.Zir.Inst.Declaration.Unwrapped,
|
|
) (UpdateError || Writer.Error)!void {
|
|
const zcu = wip_nav.pt.zcu;
|
|
const ip = &zcu.intern_pool;
|
|
const dwarf = wip_nav.dwarf;
|
|
const diw = &wip_nav.debug_info.writer;
|
|
|
|
const orig_entry = wip_nav.entry;
|
|
defer wip_nav.entry = orig_entry;
|
|
const parent_type, const is_generic_decl = if (nav.analysis) |analysis| parent_info: {
|
|
const parent_type: Type = .fromInterned(zcu.namespacePtr(analysis.namespace).owner_type);
|
|
const decl_gop = try dwarf.decls.getOrPut(dwarf.gpa, analysis.zir_index);
|
|
errdefer _ = if (!decl_gop.found_existing) dwarf.decls.pop();
|
|
const was_generic_decl = decl_gop.found_existing and
|
|
switch (try dwarf.debug_info.declAbbrevCode(wip_nav.unit, decl_gop.value_ptr.*)) {
|
|
.null,
|
|
.decl_alias,
|
|
.decl_empty_enum,
|
|
.decl_enum,
|
|
.decl_namespace_struct,
|
|
.decl_struct,
|
|
.decl_packed_struct,
|
|
.decl_union,
|
|
.decl_var,
|
|
.decl_const,
|
|
.decl_const_runtime_bits,
|
|
.decl_const_comptime_state,
|
|
.decl_const_runtime_bits_comptime_state,
|
|
.decl_nullary_func,
|
|
.decl_func,
|
|
.decl_nullary_func_generic,
|
|
.decl_func_generic,
|
|
.decl_extern_nullary_func,
|
|
.decl_extern_func,
|
|
=> false,
|
|
.generic_decl_var,
|
|
.generic_decl_const,
|
|
.generic_decl_func,
|
|
=> true,
|
|
else => |t| std.debug.panic("bad decl abbrev code: {t}", .{t}),
|
|
};
|
|
if (parent_type.getCaptures(zcu).len == 0) {
|
|
if (was_generic_decl) try dwarf.freeCommonEntry(wip_nav.unit, decl_gop.value_ptr.*);
|
|
decl_gop.value_ptr.* = orig_entry;
|
|
break :parent_info .{ parent_type, false };
|
|
} else {
|
|
if (was_generic_decl)
|
|
dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(decl_gop.value_ptr.*).clear()
|
|
else
|
|
decl_gop.value_ptr.* = try dwarf.addCommonEntry(wip_nav.unit);
|
|
wip_nav.entry = decl_gop.value_ptr.*;
|
|
break :parent_info .{ parent_type, true };
|
|
}
|
|
} else .{ null, false };
|
|
|
|
try wip_nav.abbrevCode(if (is_generic_decl) abbrev_code.generic_decl else abbrev_code.decl);
|
|
try wip_nav.refType((if (is_generic_decl) null else parent_type) orelse
|
|
.fromInterned(zcu.fileRootType(file)));
|
|
assert(diw.end == DebugInfo.declEntryLineOff(dwarf));
|
|
try diw.writeInt(u32, decl.src_line + 1, dwarf.endian);
|
|
try diw.writeUleb128(decl.src_column + 1);
|
|
try diw.writeByte(if (decl.is_pub) DW.ACCESS.public else DW.ACCESS.private);
|
|
try wip_nav.strp(nav.name.toSlice(ip));
|
|
|
|
if (!is_generic_decl) return;
|
|
const generic_decl_entry = wip_nav.entry;
|
|
try dwarf.debug_info.section.replaceEntry(wip_nav.unit, generic_decl_entry, dwarf, wip_nav.debug_info.written());
|
|
wip_nav.debug_info.clearRetainingCapacity();
|
|
wip_nav.entry = orig_entry;
|
|
try wip_nav.abbrevCode(abbrev_code.decl_instance);
|
|
try wip_nav.refType(parent_type.?);
|
|
try wip_nav.infoSectionOffset(.debug_info, wip_nav.unit, generic_decl_entry, 0);
|
|
}
|
|
|
|
const PendingLazy = struct {
|
|
types: std.ArrayListUnmanaged(InternPool.Index),
|
|
values: std.ArrayListUnmanaged(InternPool.Index),
|
|
|
|
const empty: PendingLazy = .{ .types = .empty, .values = .empty };
|
|
};
|
|
|
|
fn updateLazy(wip_nav: *WipNav, src_loc: Zcu.LazySrcLoc) (UpdateError || Writer.Error)!void {
|
|
while (true) if (wip_nav.pending_lazy.types.pop()) |pending_ty|
|
|
try wip_nav.dwarf.updateLazyType(wip_nav.pt, src_loc, pending_ty, &wip_nav.pending_lazy)
|
|
else if (wip_nav.pending_lazy.values.pop()) |pending_val|
|
|
try wip_nav.dwarf.updateLazyValue(wip_nav.pt, src_loc, pending_val, &wip_nav.pending_lazy)
|
|
else
|
|
break;
|
|
}
|
|
};
|
|
|
|
/// When allocating, the ideal_capacity is calculated by
|
|
/// actual_capacity + (actual_capacity / ideal_factor)
|
|
const ideal_factor = 3;
|
|
|
|
fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
|
|
return actual_size +| (actual_size / ideal_factor);
|
|
}
|
|
|
|
pub fn init(lf: *link.File, format: DW.Format) Dwarf {
|
|
const comp = lf.comp;
|
|
const gpa = comp.gpa;
|
|
const target = &comp.root_mod.resolved_target.result;
|
|
return .{
|
|
.gpa = gpa,
|
|
.bin_file = lf,
|
|
.format = format,
|
|
.address_size = switch (target.ptrBitWidth()) {
|
|
0...32 => .@"32",
|
|
33...64 => .@"64",
|
|
else => unreachable,
|
|
},
|
|
.endian = target.cpu.arch.endian(),
|
|
|
|
.mods = .empty,
|
|
.types = .empty,
|
|
.values = .empty,
|
|
.navs = .empty,
|
|
.decls = .empty,
|
|
|
|
.debug_abbrev = .{ .section = Section.init },
|
|
.debug_aranges = .{ .section = Section.init },
|
|
.debug_frame = .{
|
|
.header = if (target.cpu.arch == .x86_64 and target.ofmt == .elf) header: {
|
|
const Register = @import("../codegen/x86_64/bits.zig").Register;
|
|
break :header comptime .{
|
|
.format = .eh_frame,
|
|
.code_alignment_factor = 1,
|
|
.data_alignment_factor = -8,
|
|
.return_address_register = Register.rip.dwarfNum(),
|
|
.initial_instructions = &.{
|
|
.{ .def_cfa = .{ .reg = Register.rsp.dwarfNum(), .off = 8 } },
|
|
.{ .offset = .{ .reg = Register.rip.dwarfNum(), .off = -8 } },
|
|
},
|
|
};
|
|
} else .{
|
|
.format = .none,
|
|
.code_alignment_factor = undefined,
|
|
.data_alignment_factor = undefined,
|
|
.return_address_register = undefined,
|
|
.initial_instructions = &.{},
|
|
},
|
|
.section = Section.init,
|
|
},
|
|
.debug_info = .{ .section = Section.init },
|
|
.debug_line = .{
|
|
.header = switch (target.cpu.arch) {
|
|
.x86_64, .aarch64 => .{
|
|
.minimum_instruction_length = 1,
|
|
.maximum_operations_per_instruction = 1,
|
|
.default_is_stmt = true,
|
|
.line_base = -5,
|
|
.line_range = 14,
|
|
.opcode_base = DW.LNS.set_isa + 1,
|
|
},
|
|
else => .{
|
|
.minimum_instruction_length = 1,
|
|
.maximum_operations_per_instruction = 1,
|
|
.default_is_stmt = true,
|
|
.line_base = 0,
|
|
.line_range = 1,
|
|
.opcode_base = DW.LNS.set_isa + 1,
|
|
},
|
|
},
|
|
.section = Section.init,
|
|
},
|
|
.debug_line_str = StringSection.init,
|
|
.debug_loclists = .{ .section = Section.init },
|
|
.debug_rnglists = .{ .section = Section.init },
|
|
.debug_str = StringSection.init,
|
|
};
|
|
}
|
|
|
|
pub fn reloadSectionMetadata(dwarf: *Dwarf) void {
|
|
if (dwarf.bin_file.cast(.macho)) |macho_file| {
|
|
if (macho_file.d_sym) |*d_sym| {
|
|
for ([_]*Section{
|
|
&dwarf.debug_abbrev.section,
|
|
&dwarf.debug_aranges.section,
|
|
&dwarf.debug_info.section,
|
|
&dwarf.debug_line.section,
|
|
&dwarf.debug_line_str.section,
|
|
&dwarf.debug_loclists.section,
|
|
&dwarf.debug_rnglists.section,
|
|
&dwarf.debug_str.section,
|
|
}, [_]u8{
|
|
d_sym.debug_abbrev_section_index.?,
|
|
d_sym.debug_aranges_section_index.?,
|
|
d_sym.debug_info_section_index.?,
|
|
d_sym.debug_line_section_index.?,
|
|
d_sym.debug_line_str_section_index.?,
|
|
d_sym.debug_loclists_section_index.?,
|
|
d_sym.debug_rnglists_section_index.?,
|
|
d_sym.debug_str_section_index.?,
|
|
}) |sec, sect_index| {
|
|
const header = &d_sym.sections.items[sect_index];
|
|
sec.index = sect_index;
|
|
sec.len = header.size;
|
|
}
|
|
} else {
|
|
for ([_]*Section{
|
|
&dwarf.debug_abbrev.section,
|
|
&dwarf.debug_aranges.section,
|
|
&dwarf.debug_info.section,
|
|
&dwarf.debug_line.section,
|
|
&dwarf.debug_line_str.section,
|
|
&dwarf.debug_loclists.section,
|
|
&dwarf.debug_rnglists.section,
|
|
&dwarf.debug_str.section,
|
|
}, [_]u8{
|
|
macho_file.debug_abbrev_sect_index.?,
|
|
macho_file.debug_aranges_sect_index.?,
|
|
macho_file.debug_info_sect_index.?,
|
|
macho_file.debug_line_sect_index.?,
|
|
macho_file.debug_line_str_sect_index.?,
|
|
macho_file.debug_loclists_sect_index.?,
|
|
macho_file.debug_rnglists_sect_index.?,
|
|
macho_file.debug_str_sect_index.?,
|
|
}) |sec, sect_index| {
|
|
const header = &macho_file.sections.items(.header)[sect_index];
|
|
sec.index = sect_index;
|
|
sec.len = header.size;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn initMetadata(dwarf: *Dwarf) UpdateError!void {
|
|
if (dwarf.bin_file.cast(.elf)) |elf_file| {
|
|
const zo = elf_file.zigObjectPtr().?;
|
|
for ([_]*Section{
|
|
&dwarf.debug_abbrev.section,
|
|
&dwarf.debug_aranges.section,
|
|
&dwarf.debug_frame.section,
|
|
&dwarf.debug_info.section,
|
|
&dwarf.debug_line.section,
|
|
&dwarf.debug_line_str.section,
|
|
&dwarf.debug_loclists.section,
|
|
&dwarf.debug_rnglists.section,
|
|
&dwarf.debug_str.section,
|
|
}, [_]u32{
|
|
zo.debug_abbrev_index.?,
|
|
zo.debug_aranges_index.?,
|
|
zo.eh_frame_index.?,
|
|
zo.debug_info_index.?,
|
|
zo.debug_line_index.?,
|
|
zo.debug_line_str_index.?,
|
|
zo.debug_loclists_index.?,
|
|
zo.debug_rnglists_index.?,
|
|
zo.debug_str_index.?,
|
|
}) |sec, sym_index| {
|
|
sec.index = sym_index;
|
|
}
|
|
}
|
|
dwarf.reloadSectionMetadata();
|
|
|
|
dwarf.debug_abbrev.section.pad_entries_to_ideal = false;
|
|
assert(try dwarf.debug_abbrev.section.addUnit(DebugAbbrev.header_bytes, DebugAbbrev.trailer_bytes, dwarf) == DebugAbbrev.unit);
|
|
errdefer dwarf.debug_abbrev.section.popUnit(dwarf.gpa);
|
|
for (std.enums.values(AbbrevCode)) |abbrev_code|
|
|
assert(@intFromEnum(try dwarf.debug_abbrev.section.getUnit(DebugAbbrev.unit).addEntry(dwarf.gpa)) == @intFromEnum(abbrev_code));
|
|
|
|
dwarf.debug_aranges.section.pad_entries_to_ideal = false;
|
|
dwarf.debug_aranges.section.alignment = InternPool.Alignment.fromNonzeroByteUnits(@intFromEnum(dwarf.address_size) * 2);
|
|
|
|
dwarf.debug_frame.section.alignment = switch (dwarf.debug_frame.header.format) {
|
|
.none => .@"1",
|
|
.debug_frame => InternPool.Alignment.fromNonzeroByteUnits(@intFromEnum(dwarf.address_size)),
|
|
.eh_frame => .@"4",
|
|
};
|
|
|
|
dwarf.debug_line_str.section.pad_entries_to_ideal = false;
|
|
assert(try dwarf.debug_line_str.section.addUnit(0, 0, dwarf) == StringSection.unit);
|
|
errdefer dwarf.debug_line_str.section.popUnit(dwarf.gpa);
|
|
|
|
dwarf.debug_str.section.pad_entries_to_ideal = false;
|
|
assert(try dwarf.debug_str.section.addUnit(0, 0, dwarf) == StringSection.unit);
|
|
errdefer dwarf.debug_str.section.popUnit(dwarf.gpa);
|
|
|
|
dwarf.debug_loclists.section.pad_entries_to_ideal = false;
|
|
|
|
dwarf.debug_rnglists.section.pad_entries_to_ideal = false;
|
|
}
|
|
|
|
pub fn deinit(dwarf: *Dwarf) void {
|
|
const gpa = dwarf.gpa;
|
|
for (dwarf.mods.values()) |*mod_info| mod_info.deinit(gpa);
|
|
dwarf.mods.deinit(gpa);
|
|
dwarf.types.deinit(gpa);
|
|
dwarf.values.deinit(gpa);
|
|
dwarf.navs.deinit(gpa);
|
|
dwarf.decls.deinit(gpa);
|
|
dwarf.debug_abbrev.section.deinit(gpa);
|
|
dwarf.debug_aranges.section.deinit(gpa);
|
|
dwarf.debug_frame.section.deinit(gpa);
|
|
dwarf.debug_info.section.deinit(gpa);
|
|
dwarf.debug_line.section.deinit(gpa);
|
|
dwarf.debug_line_str.deinit(gpa);
|
|
dwarf.debug_loclists.section.deinit(gpa);
|
|
dwarf.debug_rnglists.section.deinit(gpa);
|
|
dwarf.debug_str.deinit(gpa);
|
|
dwarf.* = undefined;
|
|
}
|
|
|
|
fn getUnit(dwarf: *Dwarf, mod: *Module) !Unit.Index {
|
|
const mod_gop = try dwarf.mods.getOrPut(dwarf.gpa, mod);
|
|
const unit: Unit.Index = @enumFromInt(mod_gop.index);
|
|
if (!mod_gop.found_existing) {
|
|
errdefer _ = dwarf.mods.pop();
|
|
mod_gop.value_ptr.* = .{
|
|
.root_dir_path = undefined,
|
|
.dirs = .empty,
|
|
.files = .empty,
|
|
};
|
|
errdefer mod_gop.value_ptr.dirs.deinit(dwarf.gpa);
|
|
try mod_gop.value_ptr.dirs.putNoClobber(dwarf.gpa, unit, {});
|
|
assert(try dwarf.debug_aranges.section.addUnit(
|
|
DebugAranges.headerBytes(dwarf),
|
|
DebugAranges.trailerBytes(dwarf),
|
|
dwarf,
|
|
) == unit);
|
|
errdefer dwarf.debug_aranges.section.popUnit(dwarf.gpa);
|
|
assert(try dwarf.debug_frame.section.addUnit(
|
|
DebugFrame.headerBytes(dwarf),
|
|
DebugFrame.trailerBytes(dwarf),
|
|
dwarf,
|
|
) == unit);
|
|
errdefer dwarf.debug_frame.section.popUnit(dwarf.gpa);
|
|
assert(try dwarf.debug_info.section.addUnit(
|
|
DebugInfo.headerBytes(dwarf),
|
|
DebugInfo.trailer_bytes,
|
|
dwarf,
|
|
) == unit);
|
|
errdefer dwarf.debug_info.section.popUnit(dwarf.gpa);
|
|
assert(try dwarf.debug_line.section.addUnit(
|
|
DebugLine.headerBytes(dwarf, 5, 25),
|
|
DebugLine.trailer_bytes,
|
|
dwarf,
|
|
) == unit);
|
|
errdefer dwarf.debug_line.section.popUnit(dwarf.gpa);
|
|
assert(try dwarf.debug_loclists.section.addUnit(
|
|
DebugLocLists.headerBytes(dwarf),
|
|
DebugLocLists.trailer_bytes,
|
|
dwarf,
|
|
) == unit);
|
|
errdefer dwarf.debug_loclists.section.popUnit(dwarf.gpa);
|
|
assert(try dwarf.debug_rnglists.section.addUnit(
|
|
DebugRngLists.headerBytes(dwarf),
|
|
DebugRngLists.trailer_bytes,
|
|
dwarf,
|
|
) == unit);
|
|
errdefer dwarf.debug_rnglists.section.popUnit(dwarf.gpa);
|
|
}
|
|
return unit;
|
|
}
|
|
|
|
fn getUnitIfExists(dwarf: *const Dwarf, mod: *Module) ?Unit.Index {
|
|
return @enumFromInt(dwarf.mods.getIndex(mod) orelse return null);
|
|
}
|
|
|
|
fn getModInfo(dwarf: *Dwarf, unit: Unit.Index) *ModInfo {
|
|
return &dwarf.mods.values()[@intFromEnum(unit)];
|
|
}
|
|
|
|
pub fn initWipNav(
|
|
dwarf: *Dwarf,
|
|
pt: Zcu.PerThread,
|
|
nav_index: InternPool.Nav.Index,
|
|
sym_index: u32,
|
|
) error{ OutOfMemory, CodegenFail }!WipNav {
|
|
return initWipNavInner(dwarf, pt, nav_index, sym_index) catch |err| switch (err) {
|
|
error.OutOfMemory => error.OutOfMemory,
|
|
else => |e| pt.zcu.codegenFail(nav_index, "failed to init dwarf: {s}", .{@errorName(e)}),
|
|
};
|
|
}
|
|
|
|
fn initWipNavInner(
|
|
dwarf: *Dwarf,
|
|
pt: Zcu.PerThread,
|
|
nav_index: InternPool.Nav.Index,
|
|
sym_index: u32,
|
|
) !WipNav {
|
|
const zcu = pt.zcu;
|
|
const ip = &zcu.intern_pool;
|
|
|
|
const nav = ip.getNav(nav_index);
|
|
const inst_info = nav.srcInst(ip).resolveFull(ip).?;
|
|
const file = zcu.fileByIndex(inst_info.file);
|
|
const decl = file.zir.?.getDeclaration(inst_info.inst);
|
|
log.debug("initWipNav({s}:{d}:{d} %{d} = {f})", .{
|
|
file.sub_file_path,
|
|
decl.src_line + 1,
|
|
decl.src_column + 1,
|
|
@intFromEnum(inst_info.inst),
|
|
nav.fqn.fmt(ip),
|
|
});
|
|
|
|
const mod = file.mod.?;
|
|
const unit = try dwarf.getUnit(mod);
|
|
const nav_gop = try dwarf.navs.getOrPut(dwarf.gpa, nav_index);
|
|
errdefer _ = if (!nav_gop.found_existing) dwarf.navs.pop();
|
|
if (nav_gop.found_existing) {
|
|
for ([_]*Section{
|
|
&dwarf.debug_aranges.section,
|
|
&dwarf.debug_info.section,
|
|
&dwarf.debug_line.section,
|
|
&dwarf.debug_loclists.section,
|
|
&dwarf.debug_rnglists.section,
|
|
}) |sec| sec.getUnit(unit).getEntry(nav_gop.value_ptr.*).clear();
|
|
} else nav_gop.value_ptr.* = try dwarf.addCommonEntry(unit);
|
|
var wip_nav: WipNav = .{
|
|
.dwarf = dwarf,
|
|
.pt = pt,
|
|
.unit = unit,
|
|
.entry = nav_gop.value_ptr.*,
|
|
.any_children = false,
|
|
.func = .none,
|
|
.func_sym_index = undefined,
|
|
.func_high_pc = undefined,
|
|
.blocks = undefined,
|
|
.cfi = undefined,
|
|
.debug_frame = .init(dwarf.gpa),
|
|
.debug_info = .init(dwarf.gpa),
|
|
.debug_line = .init(dwarf.gpa),
|
|
.debug_loclists = .init(dwarf.gpa),
|
|
.pending_lazy = .empty,
|
|
};
|
|
errdefer wip_nav.deinit();
|
|
|
|
const nav_val = zcu.navValue(nav_index);
|
|
nav_val: switch (ip.indexToKey(nav_val.toIntern())) {
|
|
.@"extern" => |@"extern"| switch (@"extern".source) {
|
|
.builtin => {
|
|
const maybe_func_type = switch (ip.indexToKey(@"extern".ty)) {
|
|
.func_type => |func_type| func_type,
|
|
else => null,
|
|
};
|
|
const diw = &wip_nav.debug_info.writer;
|
|
try wip_nav.abbrevCode(if (maybe_func_type) |func_type|
|
|
if (func_type.param_types.len > 0 or func_type.is_var_args) .builtin_extern_func else .builtin_extern_nullary_func
|
|
else
|
|
.builtin_extern_var);
|
|
try wip_nav.refType(.fromInterned(zcu.fileRootType(inst_info.file)));
|
|
try wip_nav.strp(@"extern".name.toSlice(ip));
|
|
try wip_nav.refType(.fromInterned(if (maybe_func_type) |func_type| func_type.return_type else @"extern".ty));
|
|
if (maybe_func_type) |func_type| {
|
|
try wip_nav.infoAddrSym(sym_index, 0);
|
|
try diw.writeByte(@intFromBool(ip.isNoReturn(func_type.return_type)));
|
|
if (func_type.param_types.len > 0 or func_type.is_var_args) {
|
|
for (func_type.param_types.get(ip)) |param_type| {
|
|
try wip_nav.abbrevCode(.extern_param);
|
|
try wip_nav.refType(.fromInterned(param_type));
|
|
}
|
|
if (func_type.is_var_args) try wip_nav.abbrevCode(.is_var_args);
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
}
|
|
} else try wip_nav.infoExprLoc(.{ .addr_reloc = sym_index });
|
|
},
|
|
.syntax => switch (ip.isFunctionType(@"extern".ty)) {
|
|
false => continue :nav_val .{ .variable = undefined },
|
|
true => {
|
|
const func_type = ip.indexToKey(@"extern".ty).func_type;
|
|
const diw = &wip_nav.debug_info.writer;
|
|
try wip_nav.declCommon(if (func_type.param_types.len > 0 or func_type.is_var_args) .{
|
|
.decl = .decl_extern_func,
|
|
.generic_decl = .generic_decl_func,
|
|
.decl_instance = .decl_instance_extern_func,
|
|
} else .{
|
|
.decl = .decl_extern_nullary_func,
|
|
.generic_decl = .generic_decl_func,
|
|
.decl_instance = .decl_instance_extern_nullary_func,
|
|
}, &nav, inst_info.file, &decl);
|
|
try wip_nav.strp(@"extern".name.toSlice(ip));
|
|
try wip_nav.refType(.fromInterned(func_type.return_type));
|
|
try wip_nav.infoAddrSym(sym_index, 0);
|
|
try diw.writeByte(@intFromBool(ip.isNoReturn(func_type.return_type)));
|
|
if (func_type.param_types.len > 0 or func_type.is_var_args) {
|
|
for (func_type.param_types.get(ip)) |param_type| {
|
|
try wip_nav.abbrevCode(.extern_param);
|
|
try wip_nav.refType(.fromInterned(param_type));
|
|
}
|
|
if (func_type.is_var_args) try wip_nav.abbrevCode(.is_var_args);
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
}
|
|
},
|
|
},
|
|
},
|
|
.func => |func| if (func.owner_nav != nav_index) {
|
|
try wip_nav.declCommon(.{
|
|
.decl = .decl_alias,
|
|
.generic_decl = .generic_decl_const,
|
|
.decl_instance = .decl_instance_alias,
|
|
}, &nav, inst_info.file, &decl);
|
|
try wip_nav.refNav(func.owner_nav);
|
|
} else {
|
|
const func_type = ip.indexToKey(func.ty).func_type;
|
|
wip_nav.func = nav_val.toIntern();
|
|
wip_nav.func_sym_index = sym_index;
|
|
wip_nav.blocks = .empty;
|
|
if (dwarf.debug_frame.header.format != .none) wip_nav.cfi = .{
|
|
.loc = 0,
|
|
.cfa = dwarf.debug_frame.header.initial_instructions[0].def_cfa,
|
|
};
|
|
|
|
switch (dwarf.debug_frame.header.format) {
|
|
.none => {},
|
|
.debug_frame, .eh_frame => |format| {
|
|
const entry = dwarf.debug_frame.section.getUnit(wip_nav.unit).getEntry(wip_nav.entry);
|
|
const dfw = &wip_nav.debug_frame.writer;
|
|
switch (dwarf.format) {
|
|
.@"32" => try dfw.writeInt(u32, undefined, dwarf.endian),
|
|
.@"64" => {
|
|
try dfw.writeInt(u32, std.math.maxInt(u32), dwarf.endian);
|
|
try dfw.writeInt(u64, undefined, dwarf.endian);
|
|
},
|
|
}
|
|
switch (format) {
|
|
.none => unreachable,
|
|
.debug_frame => {
|
|
try entry.cross_entry_relocs.append(dwarf.gpa, .{
|
|
.source_off = @intCast(dfw.end),
|
|
});
|
|
try dfw.splatByteAll(0, dwarf.sectionOffsetBytes());
|
|
try wip_nav.frameAddrSym(sym_index, 0);
|
|
try dfw.splatByteAll(undefined, @intFromEnum(dwarf.address_size));
|
|
},
|
|
.eh_frame => {
|
|
try dfw.writeInt(u32, undefined, dwarf.endian);
|
|
try wip_nav.frameExternalReloc(.{
|
|
.source_off = @intCast(dfw.end),
|
|
.target_sym = sym_index,
|
|
});
|
|
try dfw.writeInt(u32, 0, dwarf.endian);
|
|
try dfw.writeInt(u32, undefined, dwarf.endian);
|
|
try dfw.writeUleb128(0);
|
|
},
|
|
}
|
|
},
|
|
}
|
|
|
|
const diw = &wip_nav.debug_info.writer;
|
|
try wip_nav.declCommon(.{
|
|
.decl = .decl_func,
|
|
.generic_decl = .generic_decl_func,
|
|
.decl_instance = .decl_instance_func,
|
|
}, &nav, inst_info.file, &decl);
|
|
try wip_nav.strp(switch (decl.linkage) {
|
|
.normal => nav.fqn,
|
|
.@"extern", .@"export" => nav.name,
|
|
}.toSlice(ip));
|
|
try wip_nav.refType(.fromInterned(func_type.return_type));
|
|
try wip_nav.infoAddrSym(sym_index, 0);
|
|
wip_nav.func_high_pc = @intCast(diw.end);
|
|
try diw.writeInt(u32, 0, dwarf.endian);
|
|
const target = &mod.resolved_target.result;
|
|
try diw.writeUleb128(switch (nav.status.fully_resolved.alignment) {
|
|
.none => target_info.defaultFunctionAlignment(target),
|
|
else => |a| a.maxStrict(target_info.minFunctionAlignment(target)),
|
|
}.toByteUnits().?);
|
|
try diw.writeByte(@intFromBool(decl.linkage != .normal));
|
|
try diw.writeByte(@intFromBool(ip.isNoReturn(func_type.return_type)));
|
|
|
|
const dlw = &wip_nav.debug_line.writer;
|
|
try dlw.writeByte(DW.LNS.extended_op);
|
|
if (dwarf.incremental()) {
|
|
try dlw.writeUleb128(1 + dwarf.sectionOffsetBytes());
|
|
try dlw.writeByte(DW.LNE.ZIG_set_decl);
|
|
try dwarf.debug_line.section.getUnit(wip_nav.unit).getEntry(wip_nav.entry).cross_section_relocs.append(dwarf.gpa, .{
|
|
.source_off = @intCast(dlw.end),
|
|
.target_sec = .debug_info,
|
|
.target_unit = wip_nav.unit,
|
|
.target_entry = wip_nav.entry.toOptional(),
|
|
});
|
|
try dlw.splatByteAll(0, dwarf.sectionOffsetBytes());
|
|
|
|
try dlw.writeByte(DW.LNS.set_column);
|
|
try dlw.writeUleb128(func.lbrace_column + 1);
|
|
|
|
try wip_nav.advancePCAndLine(func.lbrace_line, 0);
|
|
} else {
|
|
try dlw.writeUleb128(1 + @intFromEnum(dwarf.address_size));
|
|
try dlw.writeByte(DW.LNE.set_address);
|
|
try dwarf.debug_line.section.getUnit(wip_nav.unit).getEntry(wip_nav.entry).external_relocs.append(dwarf.gpa, .{
|
|
.source_off = @intCast(dlw.end),
|
|
.target_sym = sym_index,
|
|
});
|
|
try dlw.splatByteAll(0, @intFromEnum(dwarf.address_size));
|
|
|
|
const file_gop = try dwarf.getModInfo(unit).files.getOrPut(dwarf.gpa, inst_info.file);
|
|
try dlw.writeByte(DW.LNS.set_file);
|
|
try dlw.writeUleb128(file_gop.index);
|
|
|
|
try dlw.writeByte(DW.LNS.set_column);
|
|
try dlw.writeUleb128(func.lbrace_column + 1);
|
|
|
|
try wip_nav.advancePCAndLine(@intCast(decl.src_line + func.lbrace_line), 0);
|
|
}
|
|
},
|
|
else => {
|
|
const diw = &wip_nav.debug_info.writer;
|
|
try wip_nav.declCommon(.{
|
|
.decl = .decl_var,
|
|
.generic_decl = .generic_decl_var,
|
|
.decl_instance = .decl_instance_var,
|
|
}, &nav, inst_info.file, &decl);
|
|
try wip_nav.strp(switch (decl.linkage) {
|
|
.normal => nav.fqn,
|
|
.@"extern", .@"export" => nav.name,
|
|
}.toSlice(ip));
|
|
const ty: Type = nav_val.typeOf(zcu);
|
|
const addr: Loc = .{ .addr_reloc = sym_index };
|
|
const loc: Loc = if (decl.is_threadlocal) loc: {
|
|
const target = zcu.comp.root_mod.resolved_target.result;
|
|
break :loc switch (target.cpu.arch) {
|
|
.x86_64 => .{ .form_tls_address = &addr },
|
|
else => .empty,
|
|
};
|
|
} else addr;
|
|
switch (decl.kind) {
|
|
.unnamed_test, .@"test", .decltest, .@"comptime" => unreachable,
|
|
.@"const" => {
|
|
const const_ty_reloc_index = try wip_nav.refForward();
|
|
try wip_nav.infoExprLoc(loc);
|
|
try diw.writeUleb128(nav.status.fully_resolved.alignment.toByteUnits() orelse
|
|
ty.abiAlignment(zcu).toByteUnits().?);
|
|
try diw.writeByte(@intFromBool(decl.linkage != .normal));
|
|
wip_nav.finishForward(const_ty_reloc_index);
|
|
try wip_nav.abbrevCode(.is_const);
|
|
try wip_nav.refType(ty);
|
|
},
|
|
.@"var" => {
|
|
try wip_nav.refType(ty);
|
|
try wip_nav.infoExprLoc(loc);
|
|
try diw.writeUleb128(nav.status.fully_resolved.alignment.toByteUnits() orelse
|
|
ty.abiAlignment(zcu).toByteUnits().?);
|
|
try diw.writeByte(@intFromBool(decl.linkage != .normal));
|
|
},
|
|
}
|
|
},
|
|
}
|
|
return wip_nav;
|
|
}
|
|
|
|
pub fn finishWipNavFunc(
|
|
dwarf: *Dwarf,
|
|
pt: Zcu.PerThread,
|
|
nav_index: InternPool.Nav.Index,
|
|
code_size: u64,
|
|
wip_nav: *WipNav,
|
|
) UpdateError!void {
|
|
return dwarf.finishWipNavFuncWriterError(pt, nav_index, code_size, wip_nav) catch |err| switch (err) {
|
|
error.WriteFailed => error.OutOfMemory,
|
|
else => |e| e,
|
|
};
|
|
}
|
|
fn finishWipNavFuncWriterError(
|
|
dwarf: *Dwarf,
|
|
pt: Zcu.PerThread,
|
|
nav_index: InternPool.Nav.Index,
|
|
code_size: u64,
|
|
wip_nav: *WipNav,
|
|
) (UpdateError || Writer.Error)!void {
|
|
const zcu = pt.zcu;
|
|
const ip = &zcu.intern_pool;
|
|
const nav = ip.getNav(nav_index);
|
|
assert(wip_nav.func != .none);
|
|
log.debug("finishWipNavFunc({f})", .{nav.fqn.fmt(ip)});
|
|
|
|
{
|
|
const external_relocs = &dwarf.debug_aranges.section.getUnit(wip_nav.unit).getEntry(wip_nav.entry).external_relocs;
|
|
try external_relocs.append(dwarf.gpa, .{ .target_sym = wip_nav.func_sym_index });
|
|
var entry: [8 + 8]u8 = undefined;
|
|
@memset(entry[0..@intFromEnum(dwarf.address_size)], 0);
|
|
dwarf.writeInt(entry[@intFromEnum(dwarf.address_size)..][0..@intFromEnum(dwarf.address_size)], code_size);
|
|
try dwarf.debug_aranges.section.replaceEntry(
|
|
wip_nav.unit,
|
|
wip_nav.entry,
|
|
dwarf,
|
|
entry[0 .. @intFromEnum(dwarf.address_size) * 2],
|
|
);
|
|
}
|
|
switch (dwarf.debug_frame.header.format) {
|
|
.none => {},
|
|
.debug_frame, .eh_frame => |format| {
|
|
const dfw = &wip_nav.debug_frame.writer;
|
|
try dfw.splatByteAll(
|
|
DW.CFA.nop,
|
|
@intCast(dwarf.debug_frame.section.alignment.forward(dfw.end) - dfw.end),
|
|
);
|
|
const contents = wip_nav.debug_frame.written();
|
|
try dwarf.debug_frame.section.resizeEntry(wip_nav.unit, wip_nav.entry, dwarf, @intCast(contents.len));
|
|
const unit = dwarf.debug_frame.section.getUnit(wip_nav.unit);
|
|
const entry = unit.getEntry(wip_nav.entry);
|
|
const unit_len = (if (entry.next.unwrap()) |next_entry|
|
|
unit.getEntry(next_entry).off - entry.off
|
|
else
|
|
entry.len) - dwarf.unitLengthBytes();
|
|
dwarf.writeInt(contents[dwarf.unitLengthBytes() - dwarf.sectionOffsetBytes() ..][0..dwarf.sectionOffsetBytes()], unit_len);
|
|
switch (format) {
|
|
.none => unreachable,
|
|
.debug_frame => dwarf.writeInt(contents[dwarf.unitLengthBytes() + dwarf.sectionOffsetBytes() +
|
|
@intFromEnum(dwarf.address_size) ..][0..@intFromEnum(dwarf.address_size)], code_size),
|
|
.eh_frame => {
|
|
std.mem.writeInt(
|
|
u32,
|
|
contents[dwarf.unitLengthBytes()..][0..4],
|
|
unit.header_len + entry.off + dwarf.unitLengthBytes(),
|
|
dwarf.endian,
|
|
);
|
|
std.mem.writeInt(u32, contents[dwarf.unitLengthBytes() + 4 + 4 ..][0..4], @intCast(code_size), dwarf.endian);
|
|
},
|
|
}
|
|
try entry.replace(unit, &dwarf.debug_frame.section, dwarf, contents);
|
|
},
|
|
}
|
|
{
|
|
std.mem.writeInt(u32, wip_nav.debug_info.written()[wip_nav.func_high_pc..][0..4], @intCast(code_size), dwarf.endian);
|
|
if (wip_nav.any_children) {
|
|
const diw = &wip_nav.debug_info.writer;
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
} else {
|
|
const abbrev_code_buf = wip_nav.debug_info.written()[0..AbbrevCode.decl_bytes];
|
|
var abbrev_code_fr: std.Io.Reader = .fixed(abbrev_code_buf);
|
|
const abbrev_code: AbbrevCode = @enumFromInt(
|
|
abbrev_code_fr.takeLeb128(@typeInfo(AbbrevCode).@"enum".tag_type) catch unreachable,
|
|
);
|
|
std.leb.writeUnsignedFixed(
|
|
AbbrevCode.decl_bytes,
|
|
abbrev_code_buf,
|
|
@intCast(try dwarf.refAbbrevCode(switch (abbrev_code) {
|
|
else => unreachable,
|
|
.decl_func => .decl_nullary_func,
|
|
.decl_instance_func => .decl_instance_nullary_func,
|
|
})),
|
|
);
|
|
}
|
|
}
|
|
{
|
|
try dwarf.debug_rnglists.section.getUnit(wip_nav.unit).getEntry(wip_nav.entry).external_relocs.appendSlice(dwarf.gpa, &.{
|
|
.{
|
|
.source_off = 1,
|
|
.target_sym = wip_nav.func_sym_index,
|
|
},
|
|
.{
|
|
.source_off = 1 + @intFromEnum(dwarf.address_size),
|
|
.target_sym = wip_nav.func_sym_index,
|
|
.target_off = code_size,
|
|
},
|
|
});
|
|
try dwarf.debug_rnglists.section.replaceEntry(
|
|
wip_nav.unit,
|
|
wip_nav.entry,
|
|
dwarf,
|
|
([1]u8{DW.RLE.start_end} ++ [1]u8{0} ** (8 + 8))[0 .. 1 + @intFromEnum(dwarf.address_size) + @intFromEnum(dwarf.address_size)],
|
|
);
|
|
}
|
|
|
|
try dwarf.finishWipNav(pt, nav_index, wip_nav);
|
|
}
|
|
|
|
pub fn finishWipNav(
|
|
dwarf: *Dwarf,
|
|
pt: Zcu.PerThread,
|
|
nav_index: InternPool.Nav.Index,
|
|
wip_nav: *WipNav,
|
|
) UpdateError!void {
|
|
return dwarf.finishWipNavWriterError(pt, nav_index, wip_nav) catch |err| switch (err) {
|
|
error.WriteFailed => error.OutOfMemory,
|
|
else => |e| e,
|
|
};
|
|
}
|
|
fn finishWipNavWriterError(
|
|
dwarf: *Dwarf,
|
|
pt: Zcu.PerThread,
|
|
nav_index: InternPool.Nav.Index,
|
|
wip_nav: *WipNav,
|
|
) (UpdateError || Writer.Error)!void {
|
|
const zcu = pt.zcu;
|
|
const ip = &zcu.intern_pool;
|
|
const nav = ip.getNav(nav_index);
|
|
log.debug("finishWipNav({f})", .{nav.fqn.fmt(ip)});
|
|
|
|
try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.written());
|
|
const dlw = &wip_nav.debug_line.writer;
|
|
if (dlw.end > 0) {
|
|
try dlw.writeByte(DW.LNS.extended_op);
|
|
try dlw.writeUleb128(1);
|
|
try dlw.writeByte(DW.LNE.end_sequence);
|
|
try dwarf.debug_line.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_line.written());
|
|
}
|
|
try dwarf.debug_loclists.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_loclists.written());
|
|
|
|
try wip_nav.updateLazy(zcu.navSrcLoc(nav_index));
|
|
}
|
|
|
|
pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error{ OutOfMemory, CodegenFail }!void {
|
|
return updateComptimeNavInner(dwarf, pt, nav_index) catch |err| switch (err) {
|
|
error.OutOfMemory => error.OutOfMemory,
|
|
else => |e| pt.zcu.codegenFail(nav_index, "failed to update dwarf: {s}", .{@errorName(e)}),
|
|
};
|
|
}
|
|
|
|
fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
|
|
const zcu = pt.zcu;
|
|
const ip = &zcu.intern_pool;
|
|
const nav_src_loc = zcu.navSrcLoc(nav_index);
|
|
const nav_val = zcu.navValue(nav_index);
|
|
|
|
const nav = ip.getNav(nav_index);
|
|
const inst_info = nav.srcInst(ip).resolveFull(ip).?;
|
|
const file = zcu.fileByIndex(inst_info.file);
|
|
const decl = file.zir.?.getDeclaration(inst_info.inst);
|
|
log.debug("updateComptimeNav({s}:{d}:{d} %{d} = {f})", .{
|
|
file.sub_file_path,
|
|
decl.src_line + 1,
|
|
decl.src_column + 1,
|
|
@intFromEnum(inst_info.inst),
|
|
nav.fqn.fmt(ip),
|
|
});
|
|
|
|
const is_test = switch (decl.kind) {
|
|
.unnamed_test, .@"test", .decltest => true,
|
|
.@"comptime", .@"const", .@"var" => false,
|
|
};
|
|
if (is_test) {
|
|
// This isn't actually a comptime Nav! It's a test, so it'll definitely never be referenced at comptime.
|
|
return;
|
|
}
|
|
|
|
var wip_nav: WipNav = .{
|
|
.dwarf = dwarf,
|
|
.pt = pt,
|
|
.unit = try dwarf.getUnit(file.mod.?),
|
|
.entry = undefined,
|
|
.any_children = false,
|
|
.func = .none,
|
|
.func_sym_index = undefined,
|
|
.func_high_pc = undefined,
|
|
.blocks = undefined,
|
|
.cfi = undefined,
|
|
.debug_frame = .init(dwarf.gpa),
|
|
.debug_info = .init(dwarf.gpa),
|
|
.debug_line = .init(dwarf.gpa),
|
|
.debug_loclists = .init(dwarf.gpa),
|
|
.pending_lazy = .empty,
|
|
};
|
|
defer wip_nav.deinit();
|
|
|
|
const nav_gop = try dwarf.navs.getOrPut(dwarf.gpa, nav_index);
|
|
errdefer _ = if (!nav_gop.found_existing) dwarf.navs.pop();
|
|
|
|
const tag: union(enum) {
|
|
done,
|
|
decl_alias,
|
|
decl_var,
|
|
decl_const,
|
|
decl_func_alias: InternPool.Nav.Index,
|
|
} = switch (ip.indexToKey(nav_val.toIntern())) {
|
|
.int_type,
|
|
.ptr_type,
|
|
.array_type,
|
|
.vector_type,
|
|
.opt_type,
|
|
.error_union_type,
|
|
.anyframe_type,
|
|
.simple_type,
|
|
.tuple_type,
|
|
.func_type,
|
|
.error_set_type,
|
|
.inferred_error_set_type,
|
|
=> .decl_alias,
|
|
.struct_type => tag: {
|
|
const loaded_struct = ip.loadStructType(nav_val.toIntern());
|
|
if (loaded_struct.zir_index.resolveFile(ip) != inst_info.file) break :tag .decl_alias;
|
|
|
|
const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern());
|
|
if (type_gop.found_existing) {
|
|
if (dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(type_gop.value_ptr.*).len > 0) break :tag .decl_alias;
|
|
assert(!nav_gop.found_existing);
|
|
nav_gop.value_ptr.* = type_gop.value_ptr.*;
|
|
} else {
|
|
if (nav_gop.found_existing)
|
|
dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(nav_gop.value_ptr.*).clear()
|
|
else
|
|
nav_gop.value_ptr.* = try dwarf.addCommonEntry(wip_nav.unit);
|
|
type_gop.value_ptr.* = nav_gop.value_ptr.*;
|
|
}
|
|
wip_nav.entry = nav_gop.value_ptr.*;
|
|
|
|
const diw = &wip_nav.debug_info.writer;
|
|
|
|
switch (loaded_struct.layout) {
|
|
.auto, .@"extern" => {
|
|
try wip_nav.declCommon(if (loaded_struct.field_types.len == 0) .{
|
|
.decl = .decl_namespace_struct,
|
|
.generic_decl = .generic_decl_const,
|
|
.decl_instance = .decl_instance_namespace_struct,
|
|
} else .{
|
|
.decl = .decl_struct,
|
|
.generic_decl = .generic_decl_const,
|
|
.decl_instance = .decl_instance_struct,
|
|
}, &nav, inst_info.file, &decl);
|
|
if (loaded_struct.field_types.len == 0) try diw.writeByte(@intFromBool(false)) else {
|
|
try diw.writeUleb128(nav_val.toType().abiSize(zcu));
|
|
try diw.writeUleb128(nav_val.toType().abiAlignment(zcu).toByteUnits().?);
|
|
for (0..loaded_struct.field_types.len) |field_index| {
|
|
const is_comptime = loaded_struct.fieldIsComptime(ip, field_index);
|
|
const field_init = loaded_struct.fieldInit(ip, field_index);
|
|
assert(!(is_comptime and field_init == .none));
|
|
const field_type: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]);
|
|
const has_runtime_bits, const has_comptime_state = switch (field_init) {
|
|
.none => .{ false, false },
|
|
else => .{
|
|
field_type.hasRuntimeBits(zcu),
|
|
field_type.comptimeOnly(zcu) and try field_type.onePossibleValue(pt) == null,
|
|
},
|
|
};
|
|
try wip_nav.abbrevCode(if (is_comptime)
|
|
if (has_comptime_state)
|
|
.struct_field_comptime_comptime_state
|
|
else if (has_runtime_bits)
|
|
.struct_field_comptime_runtime_bits
|
|
else
|
|
.struct_field_comptime
|
|
else if (field_init != .none)
|
|
if (has_comptime_state)
|
|
.struct_field_default_comptime_state
|
|
else if (has_runtime_bits)
|
|
.struct_field_default_runtime_bits
|
|
else
|
|
.struct_field
|
|
else
|
|
.struct_field);
|
|
try wip_nav.strp(loaded_struct.fieldName(ip, field_index).toSlice(ip));
|
|
try wip_nav.refType(field_type);
|
|
if (!is_comptime) {
|
|
try diw.writeUleb128(loaded_struct.offsets.get(ip)[field_index]);
|
|
try diw.writeUleb128(loaded_struct.fieldAlign(ip, field_index).toByteUnits() orelse
|
|
field_type.abiAlignment(zcu).toByteUnits().?);
|
|
}
|
|
if (has_comptime_state)
|
|
try wip_nav.refValue(.fromInterned(field_init))
|
|
else if (has_runtime_bits)
|
|
try wip_nav.blockValue(nav_src_loc, .fromInterned(field_init));
|
|
}
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
}
|
|
},
|
|
.@"packed" => {
|
|
try wip_nav.declCommon(.{
|
|
.decl = .decl_packed_struct,
|
|
.generic_decl = .generic_decl_const,
|
|
.decl_instance = .decl_instance_packed_struct,
|
|
}, &nav, inst_info.file, &decl);
|
|
try wip_nav.refType(.fromInterned(loaded_struct.backingIntTypeUnordered(ip)));
|
|
var field_bit_offset: u16 = 0;
|
|
for (0..loaded_struct.field_types.len) |field_index| {
|
|
try wip_nav.abbrevCode(.packed_struct_field);
|
|
try wip_nav.strp(loaded_struct.fieldName(ip, field_index).toSlice(ip));
|
|
const field_type: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]);
|
|
try wip_nav.refType(field_type);
|
|
try diw.writeUleb128(field_bit_offset);
|
|
field_bit_offset += @intCast(field_type.bitSize(zcu));
|
|
}
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
},
|
|
}
|
|
break :tag .done;
|
|
},
|
|
.enum_type => tag: {
|
|
const loaded_enum = ip.loadEnumType(nav_val.toIntern());
|
|
const type_zir_index = loaded_enum.zir_index.unwrap() orelse break :tag .decl_alias;
|
|
if (type_zir_index.resolveFile(ip) != inst_info.file) break :tag .decl_alias;
|
|
|
|
const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern());
|
|
if (type_gop.found_existing) {
|
|
if (dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(type_gop.value_ptr.*).len > 0) break :tag .decl_alias;
|
|
assert(!nav_gop.found_existing);
|
|
nav_gop.value_ptr.* = type_gop.value_ptr.*;
|
|
} else {
|
|
if (nav_gop.found_existing)
|
|
dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(nav_gop.value_ptr.*).clear()
|
|
else
|
|
nav_gop.value_ptr.* = try dwarf.addCommonEntry(wip_nav.unit);
|
|
type_gop.value_ptr.* = nav_gop.value_ptr.*;
|
|
}
|
|
wip_nav.entry = nav_gop.value_ptr.*;
|
|
const diw = &wip_nav.debug_info.writer;
|
|
try wip_nav.declCommon(if (loaded_enum.names.len > 0) .{
|
|
.decl = .decl_enum,
|
|
.generic_decl = .generic_decl_const,
|
|
.decl_instance = .decl_instance_enum,
|
|
} else .{
|
|
.decl = .decl_empty_enum,
|
|
.generic_decl = .generic_decl_const,
|
|
.decl_instance = .decl_instance_empty_enum,
|
|
}, &nav, inst_info.file, &decl);
|
|
try wip_nav.refType(.fromInterned(loaded_enum.tag_ty));
|
|
for (0..loaded_enum.names.len) |field_index| {
|
|
try wip_nav.enumConstValue(loaded_enum, .{
|
|
.sdata = .signed_enum_field,
|
|
.udata = .unsigned_enum_field,
|
|
.block = .big_enum_field,
|
|
}, field_index);
|
|
try wip_nav.strp(loaded_enum.names.get(ip)[field_index].toSlice(ip));
|
|
}
|
|
if (loaded_enum.names.len > 0) try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
break :tag .done;
|
|
},
|
|
.union_type => tag: {
|
|
const loaded_union = ip.loadUnionType(nav_val.toIntern());
|
|
if (loaded_union.zir_index.resolveFile(ip) != inst_info.file) break :tag .decl_alias;
|
|
|
|
const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern());
|
|
if (type_gop.found_existing) {
|
|
if (dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(type_gop.value_ptr.*).len > 0) break :tag .decl_alias;
|
|
assert(!nav_gop.found_existing);
|
|
nav_gop.value_ptr.* = type_gop.value_ptr.*;
|
|
} else {
|
|
if (nav_gop.found_existing)
|
|
dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(nav_gop.value_ptr.*).clear()
|
|
else
|
|
nav_gop.value_ptr.* = try dwarf.addCommonEntry(wip_nav.unit);
|
|
type_gop.value_ptr.* = nav_gop.value_ptr.*;
|
|
}
|
|
wip_nav.entry = nav_gop.value_ptr.*;
|
|
const diw = &wip_nav.debug_info.writer;
|
|
try wip_nav.declCommon(.{
|
|
.decl = .decl_union,
|
|
.generic_decl = .generic_decl_const,
|
|
.decl_instance = .decl_instance_union,
|
|
}, &nav, inst_info.file, &decl);
|
|
const union_layout = Type.getUnionLayout(loaded_union, zcu);
|
|
try diw.writeUleb128(union_layout.abi_size);
|
|
try diw.writeUleb128(union_layout.abi_align.toByteUnits().?);
|
|
const loaded_tag = loaded_union.loadTagType(ip);
|
|
if (loaded_union.hasTag(ip)) {
|
|
try wip_nav.abbrevCode(.tagged_union);
|
|
try wip_nav.infoSectionOffset(
|
|
.debug_info,
|
|
wip_nav.unit,
|
|
wip_nav.entry,
|
|
@intCast(diw.end + dwarf.sectionOffsetBytes()),
|
|
);
|
|
{
|
|
try wip_nav.abbrevCode(.generated_field);
|
|
try wip_nav.strp("tag");
|
|
try wip_nav.refType(.fromInterned(loaded_union.enum_tag_ty));
|
|
try diw.writeUleb128(union_layout.tagOffset());
|
|
|
|
for (0..loaded_union.field_types.len) |field_index| {
|
|
try wip_nav.enumConstValue(loaded_tag, .{
|
|
.sdata = .signed_tagged_union_field,
|
|
.udata = .unsigned_tagged_union_field,
|
|
.block = .big_tagged_union_field,
|
|
}, field_index);
|
|
{
|
|
try wip_nav.abbrevCode(.struct_field);
|
|
try wip_nav.strp(loaded_tag.names.get(ip)[field_index].toSlice(ip));
|
|
const field_type: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]);
|
|
try wip_nav.refType(field_type);
|
|
try diw.writeUleb128(union_layout.payloadOffset());
|
|
try diw.writeUleb128(loaded_union.fieldAlign(ip, field_index).toByteUnits() orelse
|
|
if (field_type.isNoReturn(zcu)) 1 else field_type.abiAlignment(zcu).toByteUnits().?);
|
|
}
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
}
|
|
}
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
} else for (0..loaded_union.field_types.len) |field_index| {
|
|
try wip_nav.abbrevCode(.untagged_union_field);
|
|
try wip_nav.strp(loaded_tag.names.get(ip)[field_index].toSlice(ip));
|
|
const field_type: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]);
|
|
try wip_nav.refType(field_type);
|
|
try diw.writeUleb128(loaded_union.fieldAlign(ip, field_index).toByteUnits() orelse
|
|
if (field_type.isNoReturn(zcu)) 1 else field_type.abiAlignment(zcu).toByteUnits().?);
|
|
}
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
break :tag .done;
|
|
},
|
|
.opaque_type => tag: {
|
|
const loaded_opaque = ip.loadOpaqueType(nav_val.toIntern());
|
|
if (loaded_opaque.zir_index.resolveFile(ip) != inst_info.file) break :tag .decl_alias;
|
|
|
|
const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern());
|
|
if (type_gop.found_existing) {
|
|
if (dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(type_gop.value_ptr.*).len > 0) break :tag .decl_alias;
|
|
assert(!nav_gop.found_existing);
|
|
nav_gop.value_ptr.* = type_gop.value_ptr.*;
|
|
} else {
|
|
if (nav_gop.found_existing)
|
|
dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(nav_gop.value_ptr.*).clear()
|
|
else
|
|
nav_gop.value_ptr.* = try dwarf.addCommonEntry(wip_nav.unit);
|
|
type_gop.value_ptr.* = nav_gop.value_ptr.*;
|
|
}
|
|
wip_nav.entry = nav_gop.value_ptr.*;
|
|
const diw = &wip_nav.debug_info.writer;
|
|
try wip_nav.declCommon(.{
|
|
.decl = .decl_namespace_struct,
|
|
.generic_decl = .generic_decl_const,
|
|
.decl_instance = .decl_instance_namespace_struct,
|
|
}, &nav, inst_info.file, &decl);
|
|
try diw.writeByte(@intFromBool(true));
|
|
break :tag .done;
|
|
},
|
|
.undef,
|
|
.simple_value,
|
|
.int,
|
|
.err,
|
|
.error_union,
|
|
.enum_literal,
|
|
.enum_tag,
|
|
.empty_enum_value,
|
|
.float,
|
|
.ptr,
|
|
.slice,
|
|
.opt,
|
|
.aggregate,
|
|
.un,
|
|
=> .decl_const,
|
|
.variable => .decl_var,
|
|
.@"extern" => unreachable,
|
|
.func => |func| tag: {
|
|
if (func.owner_nav != nav_index) break :tag .{ .decl_func_alias = func.owner_nav };
|
|
if (nav_gop.found_existing) switch (try dwarf.debug_info.declAbbrevCode(wip_nav.unit, nav_gop.value_ptr.*)) {
|
|
.null => {},
|
|
else => unreachable,
|
|
.decl_nullary_func, .decl_func, .decl_instance_nullary_func, .decl_instance_func => return,
|
|
.decl_nullary_func_generic,
|
|
.decl_func_generic,
|
|
.decl_instance_nullary_func_generic,
|
|
.decl_instance_func_generic,
|
|
=> dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(nav_gop.value_ptr.*).clear(),
|
|
} else nav_gop.value_ptr.* = try dwarf.addCommonEntry(wip_nav.unit);
|
|
wip_nav.entry = nav_gop.value_ptr.*;
|
|
|
|
const func_type = ip.indexToKey(func.ty).func_type;
|
|
const is_nullary = !func_type.is_var_args and for (0..func_type.param_types.len) |param_index| {
|
|
if (!func_type.paramIsComptime(std.math.cast(u5, param_index) orelse break false)) break false;
|
|
} else true;
|
|
const diw = &wip_nav.debug_info.writer;
|
|
try wip_nav.declCommon(if (is_nullary) .{
|
|
.decl = .decl_nullary_func_generic,
|
|
.generic_decl = .generic_decl_func,
|
|
.decl_instance = .decl_instance_nullary_func_generic,
|
|
} else .{
|
|
.decl = .decl_func_generic,
|
|
.generic_decl = .generic_decl_func,
|
|
.decl_instance = .decl_instance_func_generic,
|
|
}, &nav, inst_info.file, &decl);
|
|
try wip_nav.refType(.fromInterned(func_type.return_type));
|
|
if (!is_nullary) {
|
|
for (0..func_type.param_types.len) |param_index| {
|
|
if (std.math.cast(u5, param_index)) |small_param_index|
|
|
if (func_type.paramIsComptime(small_param_index)) continue;
|
|
try wip_nav.abbrevCode(.func_type_param);
|
|
try wip_nav.refType(.fromInterned(func_type.param_types.get(ip)[param_index]));
|
|
}
|
|
if (func_type.is_var_args) try wip_nav.abbrevCode(.is_var_args);
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
}
|
|
break :tag .done;
|
|
},
|
|
// memoization, not types
|
|
.memoized_call => unreachable,
|
|
};
|
|
if (tag != .done) {
|
|
if (nav_gop.found_existing)
|
|
dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(nav_gop.value_ptr.*).clear()
|
|
else
|
|
nav_gop.value_ptr.* = try dwarf.addCommonEntry(wip_nav.unit);
|
|
wip_nav.entry = nav_gop.value_ptr.*;
|
|
}
|
|
switch (tag) {
|
|
.done => {},
|
|
.decl_alias => {
|
|
try wip_nav.declCommon(.{
|
|
.decl = .decl_alias,
|
|
.generic_decl = .generic_decl_const,
|
|
.decl_instance = .decl_instance_alias,
|
|
}, &nav, inst_info.file, &decl);
|
|
try wip_nav.refType(nav_val.toType());
|
|
},
|
|
.decl_var => {
|
|
const diw = &wip_nav.debug_info.writer;
|
|
try wip_nav.declCommon(.{
|
|
.decl = .decl_var,
|
|
.generic_decl = .generic_decl_var,
|
|
.decl_instance = .decl_instance_var,
|
|
}, &nav, inst_info.file, &decl);
|
|
try wip_nav.strp(switch (decl.linkage) {
|
|
.normal => nav.fqn,
|
|
.@"extern", .@"export" => nav.name,
|
|
}.toSlice(ip));
|
|
const nav_ty = nav_val.typeOf(zcu);
|
|
try wip_nav.refType(nav_ty);
|
|
try wip_nav.blockValue(nav_src_loc, nav_val);
|
|
try diw.writeUleb128(nav.status.fully_resolved.alignment.toByteUnits() orelse
|
|
nav_ty.abiAlignment(zcu).toByteUnits().?);
|
|
try diw.writeByte(@intFromBool(decl.linkage != .normal));
|
|
},
|
|
.decl_const => {
|
|
const diw = &wip_nav.debug_info.writer;
|
|
const nav_ty = nav_val.typeOf(zcu);
|
|
const has_runtime_bits = nav_ty.hasRuntimeBits(zcu);
|
|
const has_comptime_state = nav_ty.comptimeOnly(zcu) and try nav_ty.onePossibleValue(pt) == null;
|
|
try wip_nav.declCommon(if (has_runtime_bits and has_comptime_state) .{
|
|
.decl = .decl_const_runtime_bits_comptime_state,
|
|
.generic_decl = .generic_decl_const,
|
|
.decl_instance = .decl_instance_const_runtime_bits_comptime_state,
|
|
} else if (has_comptime_state) .{
|
|
.decl = .decl_const_comptime_state,
|
|
.generic_decl = .generic_decl_const,
|
|
.decl_instance = .decl_instance_const_comptime_state,
|
|
} else if (has_runtime_bits) .{
|
|
.decl = .decl_const_runtime_bits,
|
|
.generic_decl = .generic_decl_const,
|
|
.decl_instance = .decl_instance_const_runtime_bits,
|
|
} else .{
|
|
.decl = .decl_const,
|
|
.generic_decl = .generic_decl_const,
|
|
.decl_instance = .decl_instance_const,
|
|
}, &nav, inst_info.file, &decl);
|
|
try wip_nav.strp(switch (decl.linkage) {
|
|
.normal => nav.fqn,
|
|
.@"extern", .@"export" => nav.name,
|
|
}.toSlice(ip));
|
|
const nav_ty_reloc_index = try wip_nav.refForward();
|
|
try diw.writeUleb128(nav.status.fully_resolved.alignment.toByteUnits() orelse
|
|
nav_ty.abiAlignment(zcu).toByteUnits().?);
|
|
try diw.writeByte(@intFromBool(decl.linkage != .normal));
|
|
if (has_runtime_bits) try wip_nav.blockValue(nav_src_loc, nav_val);
|
|
if (has_comptime_state) try wip_nav.refValue(nav_val);
|
|
wip_nav.finishForward(nav_ty_reloc_index);
|
|
try wip_nav.abbrevCode(.is_const);
|
|
try wip_nav.refType(nav_ty);
|
|
},
|
|
.decl_func_alias => |owner_nav| {
|
|
try wip_nav.declCommon(.{
|
|
.decl = .decl_alias,
|
|
.generic_decl = .generic_decl_const,
|
|
.decl_instance = .decl_instance_alias,
|
|
}, &nav, inst_info.file, &decl);
|
|
try wip_nav.refNav(owner_nav);
|
|
},
|
|
}
|
|
try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.written());
|
|
try wip_nav.updateLazy(nav_src_loc);
|
|
}
|
|
|
|
fn updateLazyType(
|
|
dwarf: *Dwarf,
|
|
pt: Zcu.PerThread,
|
|
src_loc: Zcu.LazySrcLoc,
|
|
type_index: InternPool.Index,
|
|
pending_lazy: *WipNav.PendingLazy,
|
|
) (UpdateError || Writer.Error)!void {
|
|
const zcu = pt.zcu;
|
|
const ip = &zcu.intern_pool;
|
|
assert(ip.typeOf(type_index) == .type_type);
|
|
const ty: Type = .fromInterned(type_index);
|
|
switch (type_index) {
|
|
.generic_poison_type => log.debug("updateLazyType({s})", .{"anytype"}),
|
|
else => log.debug("updateLazyType({f})", .{ty.fmt(pt)}),
|
|
}
|
|
|
|
var wip_nav: WipNav = .{
|
|
.dwarf = dwarf,
|
|
.pt = pt,
|
|
.unit = .main,
|
|
.entry = dwarf.types.get(type_index).?,
|
|
.any_children = false,
|
|
.func = .none,
|
|
.func_sym_index = undefined,
|
|
.func_high_pc = undefined,
|
|
.blocks = undefined,
|
|
.cfi = undefined,
|
|
.debug_frame = .init(dwarf.gpa),
|
|
.debug_info = .init(dwarf.gpa),
|
|
.debug_line = .init(dwarf.gpa),
|
|
.debug_loclists = .init(dwarf.gpa),
|
|
.pending_lazy = pending_lazy.*,
|
|
};
|
|
defer {
|
|
pending_lazy.* = wip_nav.pending_lazy;
|
|
wip_nav.pending_lazy = .empty;
|
|
wip_nav.deinit();
|
|
}
|
|
const diw = &wip_nav.debug_info.writer;
|
|
const name = switch (type_index) {
|
|
.generic_poison_type => "",
|
|
else => try std.fmt.allocPrint(dwarf.gpa, "{f}", .{ty.fmt(pt)}),
|
|
};
|
|
defer dwarf.gpa.free(name);
|
|
|
|
switch (ip.indexToKey(type_index)) {
|
|
.undef => {
|
|
try wip_nav.abbrevCode(.undefined_comptime_value);
|
|
try wip_nav.refType(.type);
|
|
},
|
|
.int_type => |int_type| {
|
|
try wip_nav.abbrevCode(.numeric_type);
|
|
try wip_nav.strp(name);
|
|
try diw.writeByte(switch (int_type.signedness) {
|
|
inline .signed, .unsigned => |signedness| @field(DW.ATE, @tagName(signedness)),
|
|
});
|
|
try diw.writeUleb128(int_type.bits);
|
|
try diw.writeUleb128(ty.abiSize(zcu));
|
|
try diw.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?);
|
|
},
|
|
.ptr_type => |ptr_type| switch (ptr_type.flags.size) {
|
|
.one, .many, .c => {
|
|
const ptr_child_type: Type = .fromInterned(ptr_type.child);
|
|
try wip_nav.abbrevCode(if (ptr_type.sentinel == .none) .ptr_type else .ptr_sentinel_type);
|
|
try wip_nav.strp(name);
|
|
if (ptr_type.sentinel != .none) try wip_nav.blockValue(src_loc, .fromInterned(ptr_type.sentinel));
|
|
try diw.writeUleb128(ptr_type.flags.alignment.toByteUnits() orelse
|
|
ptr_child_type.abiAlignment(zcu).toByteUnits().?);
|
|
try diw.writeByte(@intFromEnum(ptr_type.flags.address_space));
|
|
if (ptr_type.flags.is_const or ptr_type.flags.is_volatile) try wip_nav.infoSectionOffset(
|
|
.debug_info,
|
|
wip_nav.unit,
|
|
wip_nav.entry,
|
|
@intCast(diw.end + dwarf.sectionOffsetBytes()),
|
|
) else try wip_nav.refType(ptr_child_type);
|
|
if (ptr_type.flags.is_const) {
|
|
try wip_nav.abbrevCode(.is_const);
|
|
if (ptr_type.flags.is_volatile) try wip_nav.infoSectionOffset(
|
|
.debug_info,
|
|
wip_nav.unit,
|
|
wip_nav.entry,
|
|
@intCast(diw.end + dwarf.sectionOffsetBytes()),
|
|
) else try wip_nav.refType(ptr_child_type);
|
|
}
|
|
if (ptr_type.flags.is_volatile) {
|
|
try wip_nav.abbrevCode(.is_volatile);
|
|
try wip_nav.refType(ptr_child_type);
|
|
}
|
|
},
|
|
.slice => {
|
|
try wip_nav.abbrevCode(.generated_struct_type);
|
|
try wip_nav.strp(name);
|
|
try diw.writeUleb128(ty.abiSize(zcu));
|
|
try diw.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?);
|
|
try wip_nav.abbrevCode(.generated_field);
|
|
try wip_nav.strp("ptr");
|
|
const ptr_field_type = ty.slicePtrFieldType(zcu);
|
|
try wip_nav.refType(ptr_field_type);
|
|
try diw.writeUleb128(0);
|
|
try wip_nav.abbrevCode(.generated_field);
|
|
try wip_nav.strp("len");
|
|
const len_field_type: Type = .usize;
|
|
try wip_nav.refType(len_field_type);
|
|
try diw.writeUleb128(len_field_type.abiAlignment(zcu).forward(ptr_field_type.abiSize(zcu)));
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
},
|
|
},
|
|
.array_type => |array_type| {
|
|
const array_child_type: Type = .fromInterned(array_type.child);
|
|
try wip_nav.abbrevCode(if (array_type.sentinel == .none) .array_type else .array_sentinel_type);
|
|
try wip_nav.strp(name);
|
|
if (array_type.sentinel != .none) try wip_nav.blockValue(src_loc, .fromInterned(array_type.sentinel));
|
|
try wip_nav.refType(array_child_type);
|
|
try wip_nav.abbrevCode(.array_index);
|
|
try wip_nav.refType(.usize);
|
|
try diw.writeUleb128(array_type.len);
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
},
|
|
.vector_type => |vector_type| {
|
|
try wip_nav.abbrevCode(.vector_type);
|
|
try wip_nav.strp(name);
|
|
try wip_nav.refType(.fromInterned(vector_type.child));
|
|
try wip_nav.abbrevCode(.array_index);
|
|
try wip_nav.refType(.usize);
|
|
try diw.writeUleb128(vector_type.len);
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
},
|
|
.opt_type => |opt_child_type_index| {
|
|
const opt_child_type: Type = .fromInterned(opt_child_type_index);
|
|
const opt_repr = optRepr(opt_child_type, zcu);
|
|
try wip_nav.abbrevCode(.generated_union_type);
|
|
try wip_nav.strp(name);
|
|
try diw.writeUleb128(ty.abiSize(zcu));
|
|
try diw.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?);
|
|
switch (opt_repr) {
|
|
.opv_null => {
|
|
try wip_nav.abbrevCode(.generated_field);
|
|
try wip_nav.strp("null");
|
|
try wip_nav.refType(.null);
|
|
try diw.writeUleb128(0);
|
|
},
|
|
.unpacked, .error_set, .pointer => {
|
|
try wip_nav.abbrevCode(.tagged_union);
|
|
try wip_nav.infoSectionOffset(
|
|
.debug_info,
|
|
wip_nav.unit,
|
|
wip_nav.entry,
|
|
@intCast(diw.end + dwarf.sectionOffsetBytes()),
|
|
);
|
|
{
|
|
try wip_nav.abbrevCode(.generated_field);
|
|
try wip_nav.strp("has_value");
|
|
switch (opt_repr) {
|
|
.opv_null => unreachable,
|
|
.unpacked => {
|
|
try wip_nav.refType(.bool);
|
|
try diw.writeUleb128(if (opt_child_type.hasRuntimeBits(zcu))
|
|
opt_child_type.abiSize(zcu)
|
|
else
|
|
0);
|
|
},
|
|
.error_set => {
|
|
try wip_nav.refType(.fromInterned(try pt.intern(.{ .int_type = .{
|
|
.signedness = .unsigned,
|
|
.bits = zcu.errorSetBits(),
|
|
} })));
|
|
try diw.writeUleb128(0);
|
|
},
|
|
.pointer => {
|
|
try wip_nav.refType(.usize);
|
|
try diw.writeUleb128(0);
|
|
},
|
|
}
|
|
|
|
try wip_nav.abbrevCode(.unsigned_tagged_union_field);
|
|
try diw.writeUleb128(0);
|
|
{
|
|
try wip_nav.abbrevCode(.generated_field);
|
|
try wip_nav.strp("null");
|
|
try wip_nav.refType(.null);
|
|
try diw.writeUleb128(0);
|
|
}
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
|
|
try wip_nav.abbrevCode(.tagged_union_default_field);
|
|
{
|
|
try wip_nav.abbrevCode(.generated_field);
|
|
try wip_nav.strp("?");
|
|
try wip_nav.refType(opt_child_type);
|
|
try diw.writeUleb128(0);
|
|
}
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
}
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
},
|
|
}
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
},
|
|
.anyframe_type => unreachable,
|
|
.error_union_type => |error_union_type| {
|
|
const error_union_error_set_type: Type = .fromInterned(error_union_type.error_set_type);
|
|
const error_union_payload_type: Type = .fromInterned(error_union_type.payload_type);
|
|
const error_union_error_set_offset, const error_union_payload_offset = switch (error_union_type.payload_type) {
|
|
.generic_poison_type => .{ 0, 0 },
|
|
else => .{
|
|
codegen.errUnionErrorOffset(error_union_payload_type, zcu),
|
|
codegen.errUnionPayloadOffset(error_union_payload_type, zcu),
|
|
},
|
|
};
|
|
|
|
try wip_nav.abbrevCode(.generated_union_type);
|
|
try wip_nav.strp(name);
|
|
if (error_union_type.error_set_type != .generic_poison_type and
|
|
error_union_type.payload_type != .generic_poison_type)
|
|
{
|
|
try diw.writeUleb128(ty.abiSize(zcu));
|
|
try diw.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?);
|
|
} else {
|
|
try diw.writeUleb128(0);
|
|
try diw.writeUleb128(1);
|
|
}
|
|
{
|
|
try wip_nav.abbrevCode(.tagged_union);
|
|
try wip_nav.infoSectionOffset(
|
|
.debug_info,
|
|
wip_nav.unit,
|
|
wip_nav.entry,
|
|
@intCast(diw.end + dwarf.sectionOffsetBytes()),
|
|
);
|
|
{
|
|
try wip_nav.abbrevCode(.generated_field);
|
|
try wip_nav.strp("is_error");
|
|
try wip_nav.refType(.fromInterned(try pt.intern(.{ .int_type = .{
|
|
.signedness = .unsigned,
|
|
.bits = zcu.errorSetBits(),
|
|
} })));
|
|
try diw.writeUleb128(error_union_error_set_offset);
|
|
|
|
try wip_nav.abbrevCode(.unsigned_tagged_union_field);
|
|
try diw.writeUleb128(0);
|
|
{
|
|
try wip_nav.abbrevCode(.generated_field);
|
|
try wip_nav.strp("value");
|
|
try wip_nav.refType(error_union_payload_type);
|
|
try diw.writeUleb128(error_union_payload_offset);
|
|
}
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
|
|
try wip_nav.abbrevCode(.tagged_union_default_field);
|
|
{
|
|
try wip_nav.abbrevCode(.generated_field);
|
|
try wip_nav.strp("error");
|
|
try wip_nav.refType(error_union_error_set_type);
|
|
try diw.writeUleb128(error_union_error_set_offset);
|
|
}
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
}
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
}
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
},
|
|
.simple_type => |simple_type| switch (simple_type) {
|
|
.f16,
|
|
.f32,
|
|
.f64,
|
|
.f80,
|
|
.f128,
|
|
.usize,
|
|
.isize,
|
|
.c_char,
|
|
.c_short,
|
|
.c_ushort,
|
|
.c_int,
|
|
.c_uint,
|
|
.c_long,
|
|
.c_ulong,
|
|
.c_longlong,
|
|
.c_ulonglong,
|
|
.c_longdouble,
|
|
.bool,
|
|
=> {
|
|
try wip_nav.abbrevCode(.numeric_type);
|
|
try wip_nav.strp(name);
|
|
try diw.writeByte(if (type_index == .bool_type)
|
|
DW.ATE.boolean
|
|
else if (ty.isRuntimeFloat())
|
|
DW.ATE.float
|
|
else if (ty.isSignedInt(zcu))
|
|
DW.ATE.signed
|
|
else if (ty.isUnsignedInt(zcu))
|
|
DW.ATE.unsigned
|
|
else
|
|
unreachable);
|
|
try diw.writeUleb128(ty.bitSize(zcu));
|
|
try diw.writeUleb128(ty.abiSize(zcu));
|
|
try diw.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?);
|
|
},
|
|
.anyopaque,
|
|
.void,
|
|
.type,
|
|
.comptime_int,
|
|
.comptime_float,
|
|
.noreturn,
|
|
.null,
|
|
.undefined,
|
|
.enum_literal,
|
|
.generic_poison,
|
|
=> {
|
|
try wip_nav.abbrevCode(.void_type);
|
|
try wip_nav.strp(if (type_index == .generic_poison_type) "anytype" else name);
|
|
},
|
|
.anyerror => return, // delay until flush
|
|
.adhoc_inferred_error_set => unreachable,
|
|
},
|
|
.struct_type,
|
|
.union_type,
|
|
.opaque_type,
|
|
=> unreachable,
|
|
.tuple_type => |tuple_type| if (tuple_type.types.len == 0) {
|
|
try wip_nav.abbrevCode(.generated_empty_struct_type);
|
|
try wip_nav.strp(name);
|
|
try diw.writeByte(@intFromBool(false));
|
|
} else {
|
|
try wip_nav.abbrevCode(.generated_struct_type);
|
|
try wip_nav.strp(name);
|
|
try diw.writeUleb128(ty.abiSize(zcu));
|
|
try diw.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?);
|
|
var field_byte_offset: u64 = 0;
|
|
for (0..tuple_type.types.len) |field_index| {
|
|
const comptime_value = tuple_type.values.get(ip)[field_index];
|
|
const field_type: Type = .fromInterned(tuple_type.types.get(ip)[field_index]);
|
|
const has_runtime_bits, const has_comptime_state = switch (comptime_value) {
|
|
.none => .{ false, false },
|
|
else => .{
|
|
field_type.hasRuntimeBits(zcu),
|
|
field_type.comptimeOnly(zcu) and try field_type.onePossibleValue(pt) == null,
|
|
},
|
|
};
|
|
try wip_nav.abbrevCode(if (has_comptime_state)
|
|
.struct_field_comptime_comptime_state
|
|
else if (has_runtime_bits)
|
|
.struct_field_comptime_runtime_bits
|
|
else if (comptime_value != .none)
|
|
.struct_field_comptime
|
|
else
|
|
.struct_field);
|
|
{
|
|
var field_name_buf: [std.fmt.count("{d}", .{std.math.maxInt(u32)})]u8 = undefined;
|
|
const field_name = std.fmt.bufPrint(&field_name_buf, "{d}", .{field_index}) catch unreachable;
|
|
try wip_nav.strp(field_name);
|
|
}
|
|
try wip_nav.refType(field_type);
|
|
if (comptime_value == .none) {
|
|
const field_align = field_type.abiAlignment(zcu);
|
|
field_byte_offset = field_align.forward(field_byte_offset);
|
|
try diw.writeUleb128(field_byte_offset);
|
|
try diw.writeUleb128(field_type.abiAlignment(zcu).toByteUnits().?);
|
|
field_byte_offset += field_type.abiSize(zcu);
|
|
}
|
|
if (has_comptime_state)
|
|
try wip_nav.refValue(.fromInterned(comptime_value))
|
|
else if (has_runtime_bits)
|
|
try wip_nav.blockValue(src_loc, .fromInterned(comptime_value));
|
|
}
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
},
|
|
.enum_type => {
|
|
const loaded_enum = ip.loadEnumType(type_index);
|
|
try wip_nav.abbrevCode(if (loaded_enum.names.len == 0) .generated_empty_enum_type else .generated_enum_type);
|
|
try wip_nav.strp(name);
|
|
try wip_nav.refType(.fromInterned(loaded_enum.tag_ty));
|
|
for (0..loaded_enum.names.len) |field_index| {
|
|
try wip_nav.enumConstValue(loaded_enum, .{
|
|
.sdata = .signed_enum_field,
|
|
.udata = .unsigned_enum_field,
|
|
.block = .big_enum_field,
|
|
}, field_index);
|
|
try wip_nav.strp(loaded_enum.names.get(ip)[field_index].toSlice(ip));
|
|
}
|
|
if (loaded_enum.names.len > 0) try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
},
|
|
.func_type => |func_type| {
|
|
const is_nullary = func_type.param_types.len == 0 and !func_type.is_var_args;
|
|
try wip_nav.abbrevCode(if (is_nullary) .nullary_func_type else .func_type);
|
|
try wip_nav.strp(name);
|
|
const cc: DW.CC = cc: {
|
|
if (zcu.getTarget().cCallingConvention()) |cc| {
|
|
if (@as(std.builtin.CallingConvention.Tag, cc) == func_type.cc) {
|
|
break :cc .normal;
|
|
}
|
|
}
|
|
// For better or worse, we try to match what Clang emits.
|
|
break :cc switch (func_type.cc) {
|
|
.@"inline" => .nocall,
|
|
.async, .auto, .naked => .normal,
|
|
.x86_64_sysv => .LLVM_X86_64SysV,
|
|
.x86_64_win => .LLVM_Win64,
|
|
.x86_64_regcall_v3_sysv => .LLVM_X86RegCall,
|
|
.x86_64_regcall_v4_win => .LLVM_X86RegCall,
|
|
.x86_64_vectorcall => .LLVM_vectorcall,
|
|
.x86_sysv => .normal,
|
|
.x86_win => .normal,
|
|
.x86_stdcall => .BORLAND_stdcall,
|
|
.x86_fastcall => .BORLAND_msfastcall,
|
|
.x86_thiscall => .BORLAND_thiscall,
|
|
.x86_thiscall_mingw => .BORLAND_thiscall,
|
|
.x86_regcall_v3 => .LLVM_X86RegCall,
|
|
.x86_regcall_v4_win => .LLVM_X86RegCall,
|
|
.x86_vectorcall => .LLVM_vectorcall,
|
|
|
|
.aarch64_aapcs => .normal,
|
|
.aarch64_aapcs_darwin => .normal,
|
|
.aarch64_aapcs_win => .normal,
|
|
.aarch64_vfabi => .LLVM_AAPCS,
|
|
.aarch64_vfabi_sve => .LLVM_AAPCS,
|
|
|
|
.arm_aapcs => .LLVM_AAPCS,
|
|
.arm_aapcs_vfp => .LLVM_AAPCS_VFP,
|
|
|
|
.riscv64_lp64_v,
|
|
.riscv32_ilp32_v,
|
|
=> .LLVM_RISCVVectorCall,
|
|
|
|
.m68k_rtd => .LLVM_M68kRTD,
|
|
|
|
.sh_renesas => .GNU_renesas_sh,
|
|
|
|
.amdgcn_kernel => .LLVM_OpenCLKernel,
|
|
.nvptx_kernel,
|
|
.spirv_kernel,
|
|
=> .nocall,
|
|
|
|
.x86_64_interrupt,
|
|
.x86_interrupt,
|
|
.arm_interrupt,
|
|
.mips64_interrupt,
|
|
.mips_interrupt,
|
|
.riscv64_interrupt,
|
|
.riscv32_interrupt,
|
|
.sh_interrupt,
|
|
.arc_interrupt,
|
|
.avr_builtin,
|
|
.avr_signal,
|
|
.avr_interrupt,
|
|
.csky_interrupt,
|
|
.m68k_interrupt,
|
|
.microblaze_interrupt,
|
|
.msp430_interrupt,
|
|
=> .normal,
|
|
|
|
else => .nocall,
|
|
};
|
|
};
|
|
try diw.writeByte(@intFromEnum(cc));
|
|
try wip_nav.refType(.fromInterned(func_type.return_type));
|
|
if (!is_nullary) {
|
|
for (0..func_type.param_types.len) |param_index| {
|
|
try wip_nav.abbrevCode(.func_type_param);
|
|
try wip_nav.refType(.fromInterned(func_type.param_types.get(ip)[param_index]));
|
|
}
|
|
if (func_type.is_var_args) try wip_nav.abbrevCode(.is_var_args);
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
}
|
|
},
|
|
.error_set_type => |error_set_type| {
|
|
try wip_nav.abbrevCode(if (error_set_type.names.len == 0) .generated_empty_enum_type else .generated_enum_type);
|
|
try wip_nav.strp(name);
|
|
try wip_nav.refType(.fromInterned(try pt.intern(.{ .int_type = .{
|
|
.signedness = .unsigned,
|
|
.bits = zcu.errorSetBits(),
|
|
} })));
|
|
for (0..error_set_type.names.len) |field_index| {
|
|
const field_name = error_set_type.names.get(ip)[field_index];
|
|
try wip_nav.abbrevCode(.unsigned_enum_field);
|
|
try diw.writeUleb128(ip.getErrorValueIfExists(field_name).?);
|
|
try wip_nav.strp(field_name.toSlice(ip));
|
|
}
|
|
if (error_set_type.names.len > 0) try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
},
|
|
.inferred_error_set_type => |func| {
|
|
try wip_nav.abbrevCode(.inferred_error_set_type);
|
|
try wip_nav.strp(name);
|
|
try wip_nav.refType(.fromInterned(switch (ip.funcIesResolvedUnordered(func)) {
|
|
.none => .anyerror_type,
|
|
else => |ies| ies,
|
|
}));
|
|
},
|
|
|
|
// values, not types
|
|
.simple_value,
|
|
.variable,
|
|
.@"extern",
|
|
.func,
|
|
.int,
|
|
.err,
|
|
.error_union,
|
|
.enum_literal,
|
|
.enum_tag,
|
|
.empty_enum_value,
|
|
.float,
|
|
.ptr,
|
|
.slice,
|
|
.opt,
|
|
.aggregate,
|
|
.un,
|
|
// memoization, not types
|
|
.memoized_call,
|
|
=> unreachable,
|
|
}
|
|
try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.written());
|
|
}
|
|
|
|
fn updateLazyValue(
|
|
dwarf: *Dwarf,
|
|
pt: Zcu.PerThread,
|
|
src_loc: Zcu.LazySrcLoc,
|
|
value_index: InternPool.Index,
|
|
pending_lazy: *WipNav.PendingLazy,
|
|
) (UpdateError || Writer.Error)!void {
|
|
const zcu = pt.zcu;
|
|
const ip = &zcu.intern_pool;
|
|
assert(ip.typeOf(value_index) != .type_type);
|
|
log.debug("updateLazyValue(@as({f}, {f}))", .{
|
|
Value.fromInterned(value_index).typeOf(zcu).fmt(pt),
|
|
Value.fromInterned(value_index).fmtValue(pt),
|
|
});
|
|
var wip_nav: WipNav = .{
|
|
.dwarf = dwarf,
|
|
.pt = pt,
|
|
.unit = .main,
|
|
.entry = dwarf.values.get(value_index).?,
|
|
.any_children = false,
|
|
.func = .none,
|
|
.func_sym_index = undefined,
|
|
.func_high_pc = undefined,
|
|
.blocks = undefined,
|
|
.cfi = undefined,
|
|
.debug_frame = .init(dwarf.gpa),
|
|
.debug_info = .init(dwarf.gpa),
|
|
.debug_line = .init(dwarf.gpa),
|
|
.debug_loclists = .init(dwarf.gpa),
|
|
.pending_lazy = pending_lazy.*,
|
|
};
|
|
defer {
|
|
pending_lazy.* = wip_nav.pending_lazy;
|
|
wip_nav.pending_lazy = .empty;
|
|
wip_nav.deinit();
|
|
}
|
|
const diw = &wip_nav.debug_info.writer;
|
|
var big_int_space: Value.BigIntSpace = undefined;
|
|
switch (ip.indexToKey(value_index)) {
|
|
.int_type,
|
|
.ptr_type,
|
|
.array_type,
|
|
.vector_type,
|
|
.opt_type,
|
|
.anyframe_type,
|
|
.error_union_type,
|
|
.simple_type,
|
|
.struct_type,
|
|
.tuple_type,
|
|
.union_type,
|
|
.opaque_type,
|
|
.enum_type,
|
|
.func_type,
|
|
.error_set_type,
|
|
.inferred_error_set_type,
|
|
=> unreachable, // already handled
|
|
.undef => |ty| {
|
|
try wip_nav.abbrevCode(.undefined_comptime_value);
|
|
try wip_nav.refType(.fromInterned(ty));
|
|
},
|
|
.simple_value => unreachable, // opv state
|
|
.variable, .@"extern" => unreachable, // not a value
|
|
.func => unreachable, // already handled
|
|
.int => |int| {
|
|
try wip_nav.bigIntConstValue(.{
|
|
.sdata = .sdata_comptime_value,
|
|
.udata = .udata_comptime_value,
|
|
.block = .block_comptime_value,
|
|
}, .fromInterned(int.ty), Value.fromInterned(value_index).toBigInt(&big_int_space, zcu));
|
|
try wip_nav.refType(.fromInterned(int.ty));
|
|
},
|
|
.err => |err| {
|
|
try wip_nav.abbrevCode(.udata_comptime_value);
|
|
try wip_nav.refType(.fromInterned(err.ty));
|
|
try diw.writeUleb128(try pt.getErrorValue(err.name));
|
|
},
|
|
.error_union => |error_union| {
|
|
try wip_nav.abbrevCode(.aggregate_comptime_value);
|
|
try wip_nav.refType(.fromInterned(error_union.ty));
|
|
var err_buf: [4]u8 = undefined;
|
|
const err_bytes = err_buf[0 .. std.math.divCeil(u17, zcu.errorSetBits(), 8) catch unreachable];
|
|
dwarf.writeInt(err_bytes, switch (error_union.val) {
|
|
.err_name => |err_name| try pt.getErrorValue(err_name),
|
|
.payload => 0,
|
|
});
|
|
{
|
|
try wip_nav.abbrevCode(.comptime_value_field_runtime_bits);
|
|
try wip_nav.strp("is_error");
|
|
try diw.writeUleb128(err_bytes.len);
|
|
try diw.writeAll(err_bytes);
|
|
}
|
|
payload_field: switch (error_union.val) {
|
|
.err_name => {},
|
|
.payload => |payload_val| {
|
|
const payload_type: Type = .fromInterned(ip.typeOf(payload_val));
|
|
const has_runtime_bits = payload_type.hasRuntimeBits(zcu);
|
|
const has_comptime_state = payload_type.comptimeOnly(zcu) and try payload_type.onePossibleValue(pt) == null;
|
|
try wip_nav.abbrevCode(if (has_comptime_state)
|
|
.comptime_value_field_comptime_state
|
|
else if (has_runtime_bits)
|
|
.comptime_value_field_runtime_bits
|
|
else
|
|
break :payload_field);
|
|
try wip_nav.strp("value");
|
|
if (has_comptime_state)
|
|
try wip_nav.refValue(.fromInterned(payload_val))
|
|
else
|
|
try wip_nav.blockValue(src_loc, .fromInterned(payload_val));
|
|
},
|
|
}
|
|
{
|
|
try wip_nav.abbrevCode(.comptime_value_field_runtime_bits);
|
|
try wip_nav.strp("error");
|
|
try diw.writeUleb128(err_bytes.len);
|
|
try diw.writeAll(err_bytes);
|
|
}
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
},
|
|
.enum_literal => |enum_literal| {
|
|
try wip_nav.abbrevCode(.string_comptime_value);
|
|
try wip_nav.strp(enum_literal.toSlice(ip));
|
|
try wip_nav.refType(.enum_literal);
|
|
},
|
|
.enum_tag => |enum_tag| {
|
|
const int = ip.indexToKey(enum_tag.int).int;
|
|
try wip_nav.bigIntConstValue(.{
|
|
.sdata = .sdata_comptime_value,
|
|
.udata = .udata_comptime_value,
|
|
.block = .block_comptime_value,
|
|
}, .fromInterned(int.ty), Value.fromInterned(value_index).toBigInt(&big_int_space, zcu));
|
|
try wip_nav.refType(.fromInterned(enum_tag.ty));
|
|
},
|
|
.empty_enum_value => unreachable,
|
|
.float => |float| {
|
|
switch (float.storage) {
|
|
.f16 => |f16_val| {
|
|
try wip_nav.abbrevCode(.data2_comptime_value);
|
|
try diw.writeInt(u16, @bitCast(f16_val), dwarf.endian);
|
|
},
|
|
.f32 => |f32_val| {
|
|
try wip_nav.abbrevCode(.data4_comptime_value);
|
|
try diw.writeInt(u32, @bitCast(f32_val), dwarf.endian);
|
|
},
|
|
.f64 => |f64_val| {
|
|
try wip_nav.abbrevCode(.data8_comptime_value);
|
|
try diw.writeInt(u64, @bitCast(f64_val), dwarf.endian);
|
|
},
|
|
.f80 => |f80_val| {
|
|
try wip_nav.abbrevCode(.block_comptime_value);
|
|
try diw.writeUleb128(@divExact(80, 8));
|
|
try diw.writeInt(u80, @bitCast(f80_val), dwarf.endian);
|
|
},
|
|
.f128 => |f128_val| {
|
|
try wip_nav.abbrevCode(.data16_comptime_value);
|
|
try diw.writeInt(u128, @bitCast(f128_val), dwarf.endian);
|
|
},
|
|
}
|
|
try wip_nav.refType(.fromInterned(float.ty));
|
|
},
|
|
.ptr => |ptr| {
|
|
location: {
|
|
var base_addr = ptr.base_addr;
|
|
var byte_offset = ptr.byte_offset;
|
|
const base_unit, const base_entry = while (true) {
|
|
const base_ptr = base_ptr: switch (base_addr) {
|
|
.nav => |nav_index| break try wip_nav.getNavEntry(nav_index),
|
|
.comptime_alloc, .comptime_field => unreachable,
|
|
.uav => |uav| {
|
|
const uav_ty: Type = .fromInterned(ip.typeOf(uav.val));
|
|
if (try uav_ty.onePossibleValue(pt)) |_| {
|
|
try wip_nav.abbrevCode(.udata_comptime_value);
|
|
try diw.writeUleb128(ip.indexToKey(uav.orig_ty).ptr_type.flags.alignment.toByteUnits() orelse
|
|
uav_ty.abiAlignment(zcu).toByteUnits().?);
|
|
break :location;
|
|
} else break try wip_nav.getValueEntry(.fromInterned(uav.val));
|
|
},
|
|
.int => {
|
|
try wip_nav.abbrevCode(.udata_comptime_value);
|
|
try diw.writeUleb128(byte_offset);
|
|
break :location;
|
|
},
|
|
.eu_payload => |eu_ptr| {
|
|
const base_ptr = ip.indexToKey(eu_ptr).ptr;
|
|
byte_offset += codegen.errUnionPayloadOffset(.fromInterned(ip.indexToKey(
|
|
ip.indexToKey(base_ptr.ty).ptr_type.child,
|
|
).error_union_type.payload_type), zcu);
|
|
break :base_ptr base_ptr;
|
|
},
|
|
.opt_payload => |opt_ptr| ip.indexToKey(opt_ptr).ptr,
|
|
.field => unreachable,
|
|
.arr_elem => unreachable,
|
|
};
|
|
base_addr = base_ptr.base_addr;
|
|
byte_offset += base_ptr.byte_offset;
|
|
};
|
|
try wip_nav.abbrevCode(.location_comptime_value);
|
|
try wip_nav.infoExprLoc(.{ .implicit_pointer = .{
|
|
.unit = base_unit,
|
|
.entry = base_entry,
|
|
.offset = byte_offset,
|
|
} });
|
|
}
|
|
try wip_nav.refType(.fromInterned(ptr.ty));
|
|
},
|
|
.slice => |slice| {
|
|
try wip_nav.abbrevCode(.aggregate_comptime_value);
|
|
try wip_nav.refType(.fromInterned(slice.ty));
|
|
{
|
|
try wip_nav.abbrevCode(.comptime_value_field_comptime_state);
|
|
try wip_nav.strp("ptr");
|
|
try wip_nav.refValue(.fromInterned(slice.ptr));
|
|
}
|
|
{
|
|
try wip_nav.abbrevCode(.comptime_value_field_runtime_bits);
|
|
try wip_nav.strp("len");
|
|
try wip_nav.blockValue(src_loc, .fromInterned(slice.len));
|
|
}
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
},
|
|
.opt => |opt| {
|
|
const opt_child_type: Type = .fromInterned(ip.indexToKey(opt.ty).opt_type);
|
|
try wip_nav.abbrevCode(.aggregate_comptime_value);
|
|
try wip_nav.refType(.fromInterned(opt.ty));
|
|
{
|
|
try wip_nav.abbrevCode(.comptime_value_field_runtime_bits);
|
|
try wip_nav.strp("has_value");
|
|
switch (optRepr(opt_child_type, zcu)) {
|
|
.opv_null => try diw.writeUleb128(0),
|
|
.unpacked => try wip_nav.blockValue(src_loc, .makeBool(opt.val != .none)),
|
|
.error_set => try wip_nav.blockValue(src_loc, .fromInterned(value_index)),
|
|
.pointer => if (opt_child_type.comptimeOnly(zcu)) {
|
|
var buf: [8]u8 = undefined;
|
|
const bytes = buf[0..@divExact(zcu.getTarget().ptrBitWidth(), 8)];
|
|
dwarf.writeInt(bytes, switch (opt.val) {
|
|
.none => 0,
|
|
else => opt_child_type.ptrAlignment(zcu).toByteUnits().?,
|
|
});
|
|
try diw.writeUleb128(bytes.len);
|
|
try diw.writeAll(bytes);
|
|
} else try wip_nav.blockValue(src_loc, .fromInterned(value_index)),
|
|
}
|
|
}
|
|
if (opt.val != .none) child_field: {
|
|
const has_runtime_bits = opt_child_type.hasRuntimeBits(zcu);
|
|
const has_comptime_state = opt_child_type.comptimeOnly(zcu) and try opt_child_type.onePossibleValue(pt) == null;
|
|
try wip_nav.abbrevCode(if (has_comptime_state)
|
|
.comptime_value_field_comptime_state
|
|
else if (has_runtime_bits)
|
|
.comptime_value_field_runtime_bits
|
|
else
|
|
break :child_field);
|
|
try wip_nav.strp("?");
|
|
if (has_comptime_state)
|
|
try wip_nav.refValue(.fromInterned(opt.val))
|
|
else
|
|
try wip_nav.blockValue(src_loc, .fromInterned(opt.val));
|
|
}
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
},
|
|
.aggregate => |aggregate| {
|
|
try wip_nav.abbrevCode(.aggregate_comptime_value);
|
|
try wip_nav.refType(.fromInterned(aggregate.ty));
|
|
switch (ip.indexToKey(aggregate.ty)) {
|
|
.struct_type => {
|
|
const loaded_struct_type = ip.loadStructType(aggregate.ty);
|
|
assert(loaded_struct_type.layout == .auto);
|
|
for (0..loaded_struct_type.field_types.len) |field_index| {
|
|
if (loaded_struct_type.fieldIsComptime(ip, field_index)) continue;
|
|
const field_type: Type = .fromInterned(loaded_struct_type.field_types.get(ip)[field_index]);
|
|
const has_runtime_bits = field_type.hasRuntimeBits(zcu);
|
|
const has_comptime_state = field_type.comptimeOnly(zcu) and try field_type.onePossibleValue(pt) == null;
|
|
try wip_nav.abbrevCode(if (has_comptime_state)
|
|
.comptime_value_field_comptime_state
|
|
else if (has_runtime_bits)
|
|
.comptime_value_field_runtime_bits
|
|
else
|
|
continue);
|
|
try wip_nav.strp(loaded_struct_type.fieldName(ip, field_index).toSlice(ip));
|
|
const field_value: Value = .fromInterned(switch (aggregate.storage) {
|
|
.bytes => unreachable,
|
|
.elems => |elems| elems[field_index],
|
|
.repeated_elem => |repeated_elem| repeated_elem,
|
|
});
|
|
if (has_comptime_state)
|
|
try wip_nav.refValue(field_value)
|
|
else
|
|
try wip_nav.blockValue(src_loc, field_value);
|
|
}
|
|
},
|
|
.tuple_type => |tuple_type| for (0..tuple_type.types.len) |field_index| {
|
|
if (tuple_type.values.get(ip)[field_index] != .none) continue;
|
|
const field_type: Type = .fromInterned(tuple_type.types.get(ip)[field_index]);
|
|
const has_runtime_bits = field_type.hasRuntimeBits(zcu);
|
|
const has_comptime_state = field_type.comptimeOnly(zcu) and try field_type.onePossibleValue(pt) == null;
|
|
try wip_nav.abbrevCode(if (has_comptime_state)
|
|
.comptime_value_field_comptime_state
|
|
else if (has_runtime_bits)
|
|
.comptime_value_field_runtime_bits
|
|
else
|
|
continue);
|
|
{
|
|
var field_name_buf: [std.fmt.count("{d}", .{std.math.maxInt(u32)})]u8 = undefined;
|
|
const field_name = std.fmt.bufPrint(&field_name_buf, "{d}", .{field_index}) catch unreachable;
|
|
try wip_nav.strp(field_name);
|
|
}
|
|
const field_value: Value = .fromInterned(switch (aggregate.storage) {
|
|
.bytes => unreachable,
|
|
.elems => |elems| elems[field_index],
|
|
.repeated_elem => |repeated_elem| repeated_elem,
|
|
});
|
|
if (has_comptime_state)
|
|
try wip_nav.refValue(field_value)
|
|
else
|
|
try wip_nav.blockValue(src_loc, field_value);
|
|
},
|
|
inline .array_type, .vector_type => |sequence_type| {
|
|
const child_type: Type = .fromInterned(sequence_type.child);
|
|
const has_runtime_bits = child_type.hasRuntimeBits(zcu);
|
|
const has_comptime_state = child_type.comptimeOnly(zcu) and try child_type.onePossibleValue(pt) == null;
|
|
for (switch (aggregate.storage) {
|
|
.bytes => unreachable,
|
|
.elems => |elems| elems,
|
|
.repeated_elem => |*repeated_elem| repeated_elem[0..1],
|
|
}) |elem| {
|
|
try wip_nav.abbrevCode(if (has_comptime_state)
|
|
.comptime_value_elem_comptime_state
|
|
else if (has_runtime_bits)
|
|
.comptime_value_elem_runtime_bits
|
|
else
|
|
break);
|
|
if (has_comptime_state)
|
|
try wip_nav.refValue(.fromInterned(elem))
|
|
else
|
|
try wip_nav.blockValue(src_loc, .fromInterned(elem));
|
|
}
|
|
},
|
|
else => unreachable,
|
|
}
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
},
|
|
.un => |un| {
|
|
try wip_nav.abbrevCode(.aggregate_comptime_value);
|
|
try wip_nav.refType(.fromInterned(un.ty));
|
|
field: {
|
|
const loaded_union_type = ip.loadUnionType(un.ty);
|
|
assert(loaded_union_type.flagsUnordered(ip).layout == .auto);
|
|
const field_index = zcu.unionTagFieldIndex(loaded_union_type, Value.fromInterned(un.tag)).?;
|
|
const field_ty: Type = .fromInterned(loaded_union_type.field_types.get(ip)[field_index]);
|
|
const field_name = loaded_union_type.loadTagType(ip).names.get(ip)[field_index];
|
|
const has_runtime_bits = field_ty.hasRuntimeBits(zcu);
|
|
const has_comptime_state = field_ty.comptimeOnly(zcu) and try field_ty.onePossibleValue(pt) == null;
|
|
try wip_nav.abbrevCode(if (has_comptime_state)
|
|
.comptime_value_field_comptime_state
|
|
else if (has_runtime_bits)
|
|
.comptime_value_field_runtime_bits
|
|
else
|
|
break :field);
|
|
try wip_nav.strp(field_name.toSlice(ip));
|
|
if (has_comptime_state)
|
|
try wip_nav.refValue(.fromInterned(un.val))
|
|
else
|
|
try wip_nav.blockValue(src_loc, .fromInterned(un.val));
|
|
}
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
},
|
|
.memoized_call => unreachable, // not a value
|
|
}
|
|
try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.written());
|
|
}
|
|
|
|
fn optRepr(opt_child_type: Type, zcu: *const Zcu) enum {
|
|
unpacked,
|
|
opv_null,
|
|
error_set,
|
|
pointer,
|
|
} {
|
|
if (opt_child_type.isNoReturn(zcu)) return .opv_null;
|
|
return switch (opt_child_type.toIntern()) {
|
|
.anyerror_type => .error_set,
|
|
else => switch (zcu.intern_pool.indexToKey(opt_child_type.toIntern())) {
|
|
else => .unpacked,
|
|
.error_set_type, .inferred_error_set_type => .error_set,
|
|
.ptr_type => |ptr_type| if (ptr_type.flags.is_allowzero) .unpacked else .pointer,
|
|
},
|
|
};
|
|
}
|
|
|
|
pub fn updateContainerType(
|
|
dwarf: *Dwarf,
|
|
pt: Zcu.PerThread,
|
|
type_index: InternPool.Index,
|
|
) UpdateError!void {
|
|
return dwarf.updateContainerTypeWriterError(pt, type_index) catch |err| switch (err) {
|
|
error.WriteFailed => error.OutOfMemory,
|
|
else => |e| e,
|
|
};
|
|
}
|
|
fn updateContainerTypeWriterError(
|
|
dwarf: *Dwarf,
|
|
pt: Zcu.PerThread,
|
|
type_index: InternPool.Index,
|
|
) (UpdateError || Writer.Error)!void {
|
|
const zcu = pt.zcu;
|
|
const ip = &zcu.intern_pool;
|
|
const ty: Type = .fromInterned(type_index);
|
|
const ty_src_loc = ty.srcLoc(zcu);
|
|
log.debug("updateContainerType({f})", .{ty.fmt(pt)});
|
|
|
|
const inst_info = ty.typeDeclInst(zcu).?.resolveFull(ip).?;
|
|
const file = zcu.fileByIndex(inst_info.file);
|
|
const unit = try dwarf.getUnit(file.mod.?);
|
|
const file_gop = try dwarf.getModInfo(unit).files.getOrPut(dwarf.gpa, inst_info.file);
|
|
if (inst_info.inst == .main_struct_inst) {
|
|
const type_gop = try dwarf.types.getOrPut(dwarf.gpa, type_index);
|
|
if (!type_gop.found_existing) type_gop.value_ptr.* = try dwarf.addCommonEntry(unit);
|
|
var wip_nav: WipNav = .{
|
|
.dwarf = dwarf,
|
|
.pt = pt,
|
|
.unit = unit,
|
|
.entry = type_gop.value_ptr.*,
|
|
.any_children = false,
|
|
.func = .none,
|
|
.func_sym_index = undefined,
|
|
.func_high_pc = undefined,
|
|
.blocks = undefined,
|
|
.cfi = undefined,
|
|
.debug_frame = .init(dwarf.gpa),
|
|
.debug_info = .init(dwarf.gpa),
|
|
.debug_line = .init(dwarf.gpa),
|
|
.debug_loclists = .init(dwarf.gpa),
|
|
.pending_lazy = .empty,
|
|
};
|
|
defer wip_nav.deinit();
|
|
|
|
const loaded_struct = ip.loadStructType(type_index);
|
|
|
|
const diw = &wip_nav.debug_info.writer;
|
|
try wip_nav.abbrevCode(if (loaded_struct.field_types.len == 0) .empty_file else .file);
|
|
try diw.writeUleb128(file_gop.index);
|
|
try wip_nav.strp(loaded_struct.name.toSlice(ip));
|
|
if (loaded_struct.field_types.len > 0) {
|
|
try diw.writeUleb128(ty.abiSize(zcu));
|
|
try diw.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?);
|
|
for (0..loaded_struct.field_types.len) |field_index| {
|
|
const is_comptime = loaded_struct.fieldIsComptime(ip, field_index);
|
|
const field_init = loaded_struct.fieldInit(ip, field_index);
|
|
assert(!(is_comptime and field_init == .none));
|
|
const field_type: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]);
|
|
const has_runtime_bits, const has_comptime_state = switch (field_init) {
|
|
.none => .{ false, false },
|
|
else => .{
|
|
field_type.hasRuntimeBits(zcu),
|
|
field_type.comptimeOnly(zcu) and try field_type.onePossibleValue(pt) == null,
|
|
},
|
|
};
|
|
try wip_nav.abbrevCode(if (is_comptime)
|
|
if (has_comptime_state)
|
|
.struct_field_comptime_comptime_state
|
|
else if (has_runtime_bits)
|
|
.struct_field_comptime_runtime_bits
|
|
else
|
|
.struct_field_comptime
|
|
else if (field_init != .none)
|
|
if (has_comptime_state)
|
|
.struct_field_default_comptime_state
|
|
else if (has_runtime_bits)
|
|
.struct_field_default_runtime_bits
|
|
else
|
|
.struct_field
|
|
else
|
|
.struct_field);
|
|
try wip_nav.strp(loaded_struct.fieldName(ip, field_index).toSlice(ip));
|
|
try wip_nav.refType(field_type);
|
|
if (!is_comptime) {
|
|
try diw.writeUleb128(loaded_struct.offsets.get(ip)[field_index]);
|
|
try diw.writeUleb128(loaded_struct.fieldAlign(ip, field_index).toByteUnits() orelse
|
|
field_type.abiAlignment(zcu).toByteUnits().?);
|
|
}
|
|
if (has_comptime_state)
|
|
try wip_nav.refValue(.fromInterned(field_init))
|
|
else if (has_runtime_bits)
|
|
try wip_nav.blockValue(ty_src_loc, .fromInterned(field_init));
|
|
}
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
}
|
|
|
|
try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.written());
|
|
try wip_nav.updateLazy(ty_src_loc);
|
|
} else {
|
|
{
|
|
// Note that changes to ZIR instruction tracking only need to update this code
|
|
// if a newly-tracked instruction can be a type's owner `zir_index`.
|
|
comptime assert(Zir.inst_tracking_version == 0);
|
|
|
|
const decl_inst = file.zir.?.instructions.get(@intFromEnum(inst_info.inst));
|
|
const name_strat: Zir.Inst.NameStrategy = switch (decl_inst.tag) {
|
|
.struct_init, .struct_init_ref, .struct_init_anon => .anon,
|
|
.extended => switch (decl_inst.data.extended.opcode) {
|
|
.struct_decl => @as(Zir.Inst.StructDecl.Small, @bitCast(decl_inst.data.extended.small)).name_strategy,
|
|
.enum_decl => @as(Zir.Inst.EnumDecl.Small, @bitCast(decl_inst.data.extended.small)).name_strategy,
|
|
.union_decl => @as(Zir.Inst.UnionDecl.Small, @bitCast(decl_inst.data.extended.small)).name_strategy,
|
|
.opaque_decl => @as(Zir.Inst.OpaqueDecl.Small, @bitCast(decl_inst.data.extended.small)).name_strategy,
|
|
.reify => @as(Zir.Inst.NameStrategy, @enumFromInt(decl_inst.data.extended.small)),
|
|
else => unreachable,
|
|
},
|
|
else => unreachable,
|
|
};
|
|
if (name_strat == .parent) return;
|
|
}
|
|
|
|
const type_gop = try dwarf.types.getOrPut(dwarf.gpa, type_index);
|
|
if (!type_gop.found_existing) type_gop.value_ptr.* = try dwarf.addCommonEntry(unit);
|
|
var wip_nav: WipNav = .{
|
|
.dwarf = dwarf,
|
|
.pt = pt,
|
|
.unit = unit,
|
|
.entry = type_gop.value_ptr.*,
|
|
.any_children = false,
|
|
.func = .none,
|
|
.func_sym_index = undefined,
|
|
.func_high_pc = undefined,
|
|
.blocks = undefined,
|
|
.cfi = undefined,
|
|
.debug_frame = .init(dwarf.gpa),
|
|
.debug_info = .init(dwarf.gpa),
|
|
.debug_line = .init(dwarf.gpa),
|
|
.debug_loclists = .init(dwarf.gpa),
|
|
.pending_lazy = .empty,
|
|
};
|
|
defer wip_nav.deinit();
|
|
const diw = &wip_nav.debug_info.writer;
|
|
const name = try std.fmt.allocPrint(dwarf.gpa, "{f}", .{ty.fmt(pt)});
|
|
defer dwarf.gpa.free(name);
|
|
|
|
switch (ip.indexToKey(type_index)) {
|
|
.struct_type => {
|
|
const loaded_struct = ip.loadStructType(type_index);
|
|
switch (loaded_struct.layout) {
|
|
.auto, .@"extern" => {
|
|
try wip_nav.abbrevCode(if (loaded_struct.field_types.len == 0) .empty_struct_type else .struct_type);
|
|
try diw.writeUleb128(file_gop.index);
|
|
try wip_nav.strp(name);
|
|
if (loaded_struct.field_types.len == 0) try diw.writeByte(@intFromBool(false)) else {
|
|
try diw.writeUleb128(ty.abiSize(zcu));
|
|
try diw.writeUleb128(ty.abiAlignment(zcu).toByteUnits().?);
|
|
for (0..loaded_struct.field_types.len) |field_index| {
|
|
const is_comptime = loaded_struct.fieldIsComptime(ip, field_index);
|
|
const field_init = loaded_struct.fieldInit(ip, field_index);
|
|
assert(!(is_comptime and field_init == .none));
|
|
const field_type: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]);
|
|
const has_runtime_bits, const has_comptime_state = switch (field_init) {
|
|
.none => .{ false, false },
|
|
else => .{
|
|
field_type.hasRuntimeBits(zcu),
|
|
field_type.comptimeOnly(zcu) and try field_type.onePossibleValue(pt) == null,
|
|
},
|
|
};
|
|
try wip_nav.abbrevCode(if (is_comptime)
|
|
if (has_comptime_state)
|
|
.struct_field_comptime_comptime_state
|
|
else if (has_runtime_bits)
|
|
.struct_field_comptime_runtime_bits
|
|
else
|
|
.struct_field_comptime
|
|
else if (field_init != .none)
|
|
if (has_comptime_state)
|
|
.struct_field_default_comptime_state
|
|
else if (has_runtime_bits)
|
|
.struct_field_default_runtime_bits
|
|
else
|
|
.struct_field
|
|
else
|
|
.struct_field);
|
|
try wip_nav.strp(loaded_struct.fieldName(ip, field_index).toSlice(ip));
|
|
try wip_nav.refType(field_type);
|
|
if (!is_comptime) {
|
|
try diw.writeUleb128(loaded_struct.offsets.get(ip)[field_index]);
|
|
try diw.writeUleb128(loaded_struct.fieldAlign(ip, field_index).toByteUnits() orelse
|
|
field_type.abiAlignment(zcu).toByteUnits().?);
|
|
}
|
|
if (has_comptime_state)
|
|
try wip_nav.refValue(.fromInterned(field_init))
|
|
else if (has_runtime_bits)
|
|
try wip_nav.blockValue(ty_src_loc, .fromInterned(field_init));
|
|
}
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
}
|
|
},
|
|
.@"packed" => {
|
|
try wip_nav.abbrevCode(if (loaded_struct.field_types.len > 0) .packed_struct_type else .empty_packed_struct_type);
|
|
try diw.writeUleb128(file_gop.index);
|
|
try wip_nav.strp(name);
|
|
try wip_nav.refType(.fromInterned(loaded_struct.backingIntTypeUnordered(ip)));
|
|
var field_bit_offset: u16 = 0;
|
|
for (0..loaded_struct.field_types.len) |field_index| {
|
|
try wip_nav.abbrevCode(.packed_struct_field);
|
|
try wip_nav.strp(loaded_struct.fieldName(ip, field_index).toSlice(ip));
|
|
const field_type: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]);
|
|
try wip_nav.refType(field_type);
|
|
try diw.writeUleb128(field_bit_offset);
|
|
field_bit_offset += @intCast(field_type.bitSize(zcu));
|
|
}
|
|
if (loaded_struct.field_types.len > 0) try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
},
|
|
}
|
|
},
|
|
.enum_type => {
|
|
const loaded_enum = ip.loadEnumType(type_index);
|
|
try wip_nav.abbrevCode(if (loaded_enum.names.len > 0) .enum_type else .empty_enum_type);
|
|
try diw.writeUleb128(file_gop.index);
|
|
try wip_nav.strp(name);
|
|
try wip_nav.refType(.fromInterned(loaded_enum.tag_ty));
|
|
for (0..loaded_enum.names.len) |field_index| {
|
|
try wip_nav.enumConstValue(loaded_enum, .{
|
|
.sdata = .signed_enum_field,
|
|
.udata = .unsigned_enum_field,
|
|
.block = .big_enum_field,
|
|
}, field_index);
|
|
try wip_nav.strp(loaded_enum.names.get(ip)[field_index].toSlice(ip));
|
|
}
|
|
if (loaded_enum.names.len > 0) try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
},
|
|
.union_type => {
|
|
const loaded_union = ip.loadUnionType(type_index);
|
|
try wip_nav.abbrevCode(if (loaded_union.field_types.len > 0) .union_type else .empty_union_type);
|
|
try diw.writeUleb128(file_gop.index);
|
|
try wip_nav.strp(name);
|
|
const union_layout = Type.getUnionLayout(loaded_union, zcu);
|
|
try diw.writeUleb128(union_layout.abi_size);
|
|
try diw.writeUleb128(union_layout.abi_align.toByteUnits().?);
|
|
const loaded_tag = loaded_union.loadTagType(ip);
|
|
if (loaded_union.hasTag(ip)) {
|
|
try wip_nav.abbrevCode(.tagged_union);
|
|
try wip_nav.infoSectionOffset(
|
|
.debug_info,
|
|
wip_nav.unit,
|
|
wip_nav.entry,
|
|
@intCast(diw.end + dwarf.sectionOffsetBytes()),
|
|
);
|
|
{
|
|
try wip_nav.abbrevCode(.generated_field);
|
|
try wip_nav.strp("tag");
|
|
try wip_nav.refType(.fromInterned(loaded_union.enum_tag_ty));
|
|
try diw.writeUleb128(union_layout.tagOffset());
|
|
|
|
for (0..loaded_union.field_types.len) |field_index| {
|
|
try wip_nav.enumConstValue(loaded_tag, .{
|
|
.sdata = .signed_tagged_union_field,
|
|
.udata = .unsigned_tagged_union_field,
|
|
.block = .big_tagged_union_field,
|
|
}, field_index);
|
|
{
|
|
try wip_nav.abbrevCode(.struct_field);
|
|
try wip_nav.strp(loaded_tag.names.get(ip)[field_index].toSlice(ip));
|
|
const field_type: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]);
|
|
try wip_nav.refType(field_type);
|
|
try diw.writeUleb128(union_layout.payloadOffset());
|
|
try diw.writeUleb128(loaded_union.fieldAlign(ip, field_index).toByteUnits() orelse
|
|
if (field_type.isNoReturn(zcu)) 1 else field_type.abiAlignment(zcu).toByteUnits().?);
|
|
}
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
}
|
|
}
|
|
try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
} else for (0..loaded_union.field_types.len) |field_index| {
|
|
try wip_nav.abbrevCode(.untagged_union_field);
|
|
try wip_nav.strp(loaded_tag.names.get(ip)[field_index].toSlice(ip));
|
|
const field_type: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]);
|
|
try wip_nav.refType(field_type);
|
|
try diw.writeUleb128(loaded_union.fieldAlign(ip, field_index).toByteUnits() orelse
|
|
if (field_type.isNoReturn(zcu)) 1 else field_type.abiAlignment(zcu).toByteUnits().?);
|
|
}
|
|
if (loaded_union.field_types.len > 0) try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
},
|
|
.opaque_type => {
|
|
try wip_nav.abbrevCode(.empty_struct_type);
|
|
try diw.writeUleb128(file_gop.index);
|
|
try wip_nav.strp(name);
|
|
try diw.writeByte(@intFromBool(true));
|
|
},
|
|
else => unreachable,
|
|
}
|
|
try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.written());
|
|
try dwarf.debug_loclists.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_loclists.written());
|
|
try wip_nav.updateLazy(ty_src_loc);
|
|
}
|
|
}
|
|
|
|
pub fn updateLineNumber(dwarf: *Dwarf, zcu: *Zcu, zir_index: InternPool.TrackedInst.Index) UpdateError!void {
|
|
const ip = &zcu.intern_pool;
|
|
|
|
const inst_info = zir_index.resolveFull(ip).?;
|
|
assert(inst_info.inst != .main_struct_inst);
|
|
const file = zcu.fileByIndex(inst_info.file);
|
|
const decl = file.zir.?.getDeclaration(inst_info.inst);
|
|
log.debug("updateLineNumber({s}:{d}:{d} %{d} = {s})", .{
|
|
file.sub_file_path,
|
|
decl.src_line + 1,
|
|
decl.src_column + 1,
|
|
@intFromEnum(inst_info.inst),
|
|
file.zir.?.nullTerminatedString(decl.name),
|
|
});
|
|
|
|
var line_buf: [4]u8 = undefined;
|
|
std.mem.writeInt(u32, &line_buf, decl.src_line + 1, dwarf.endian);
|
|
|
|
const unit = dwarf.debug_info.section.getUnit(dwarf.getUnitIfExists(file.mod.?) orelse return);
|
|
const entry = unit.getEntry(dwarf.decls.get(zir_index) orelse return);
|
|
try dwarf.getFile().?.pwriteAll(&line_buf, dwarf.debug_info.section.off(dwarf) + unit.off + unit.header_len + entry.off + DebugInfo.declEntryLineOff(dwarf));
|
|
}
|
|
|
|
pub fn freeNav(dwarf: *Dwarf, nav_index: InternPool.Nav.Index) void {
|
|
_ = dwarf;
|
|
_ = nav_index;
|
|
}
|
|
|
|
fn refAbbrevCode(
|
|
dwarf: *Dwarf,
|
|
abbrev_code: AbbrevCode,
|
|
) (UpdateError || Writer.Error)!@typeInfo(AbbrevCode).@"enum".tag_type {
|
|
assert(abbrev_code != .null);
|
|
const entry: Entry.Index = @enumFromInt(@intFromEnum(abbrev_code));
|
|
if (dwarf.debug_abbrev.section.getUnit(DebugAbbrev.unit).getEntry(entry).len > 0) return @intFromEnum(abbrev_code);
|
|
var debug_abbrev_aw: Writer.Allocating = .init(dwarf.gpa);
|
|
defer debug_abbrev_aw.deinit();
|
|
const daw = &debug_abbrev_aw.writer;
|
|
const abbrev = AbbrevCode.abbrevs.get(abbrev_code);
|
|
try daw.writeUleb128(@intFromEnum(abbrev_code));
|
|
try daw.writeUleb128(@intFromEnum(abbrev.tag));
|
|
try daw.writeByte(if (abbrev.children) DW.CHILDREN.yes else DW.CHILDREN.no);
|
|
for (abbrev.attrs) |*attr| inline for (attr) |info| try daw.writeUleb128(@intFromEnum(info));
|
|
for (0..2) |_| try daw.writeUleb128(0);
|
|
try dwarf.debug_abbrev.section.replaceEntry(DebugAbbrev.unit, entry, dwarf, debug_abbrev_aw.written());
|
|
return @intFromEnum(abbrev_code);
|
|
}
|
|
|
|
pub fn flush(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void {
|
|
return dwarf.flushWriterError(pt) catch |err| switch (err) {
|
|
error.WriteFailed => error.OutOfMemory,
|
|
else => |e| e,
|
|
};
|
|
}
|
|
fn flushWriterError(dwarf: *Dwarf, pt: Zcu.PerThread) (FlushError || Writer.Error)!void {
|
|
const zcu = pt.zcu;
|
|
const ip = &zcu.intern_pool;
|
|
|
|
{
|
|
const type_gop = try dwarf.types.getOrPut(dwarf.gpa, .anyerror_type);
|
|
if (!type_gop.found_existing) type_gop.value_ptr.* = try dwarf.addCommonEntry(.main);
|
|
var wip_nav: WipNav = .{
|
|
.dwarf = dwarf,
|
|
.pt = pt,
|
|
.unit = .main,
|
|
.entry = type_gop.value_ptr.*,
|
|
.any_children = false,
|
|
.func = .none,
|
|
.func_sym_index = undefined,
|
|
.func_high_pc = undefined,
|
|
.blocks = undefined,
|
|
.cfi = undefined,
|
|
.debug_frame = .init(dwarf.gpa),
|
|
.debug_info = .init(dwarf.gpa),
|
|
.debug_line = .init(dwarf.gpa),
|
|
.debug_loclists = .init(dwarf.gpa),
|
|
.pending_lazy = .empty,
|
|
};
|
|
defer wip_nav.deinit();
|
|
const diw = &wip_nav.debug_info.writer;
|
|
const global_error_set_names = ip.global_error_set.getNamesFromMainThread();
|
|
try wip_nav.abbrevCode(if (global_error_set_names.len == 0) .generated_empty_enum_type else .generated_enum_type);
|
|
try wip_nav.strp("anyerror");
|
|
try wip_nav.refType(.fromInterned(try pt.intern(.{ .int_type = .{
|
|
.signedness = .unsigned,
|
|
.bits = zcu.errorSetBits(),
|
|
} })));
|
|
for (global_error_set_names, 1..) |name, value| {
|
|
try wip_nav.abbrevCode(.unsigned_enum_field);
|
|
try diw.writeUleb128(value);
|
|
try wip_nav.strp(name.toSlice(ip));
|
|
}
|
|
if (global_error_set_names.len > 0) try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
|
|
try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.written());
|
|
try wip_nav.updateLazy(.unneeded);
|
|
}
|
|
|
|
for (dwarf.mods.keys(), dwarf.mods.values()) |mod, *mod_info| {
|
|
const root_dir_path = try mod.root.toAbsolute(zcu.comp.dirs, dwarf.gpa);
|
|
defer dwarf.gpa.free(root_dir_path);
|
|
mod_info.root_dir_path = try dwarf.debug_line_str.addString(dwarf, root_dir_path);
|
|
}
|
|
|
|
var header_aw: Writer.Allocating = .init(dwarf.gpa);
|
|
defer header_aw.deinit();
|
|
const hw = &header_aw.writer;
|
|
if (dwarf.debug_aranges.section.dirty) {
|
|
for (dwarf.debug_aranges.section.units.items, 0..) |*unit_ptr, unit_index| {
|
|
const unit: Unit.Index = @enumFromInt(unit_index);
|
|
unit_ptr.clear();
|
|
try unit_ptr.cross_section_relocs.ensureTotalCapacity(dwarf.gpa, 1);
|
|
header_aw.clearRetainingCapacity();
|
|
try header_aw.ensureTotalCapacity(unit_ptr.header_len);
|
|
const unit_len = (if (unit_ptr.next.unwrap()) |next_unit|
|
|
dwarf.debug_aranges.section.getUnit(next_unit).off
|
|
else
|
|
dwarf.debug_aranges.section.len) - unit_ptr.off - dwarf.unitLengthBytes();
|
|
switch (dwarf.format) {
|
|
.@"32" => hw.writeInt(u32, @intCast(unit_len), dwarf.endian) catch unreachable,
|
|
.@"64" => {
|
|
hw.writeInt(u32, std.math.maxInt(u32), dwarf.endian) catch unreachable;
|
|
hw.writeInt(u64, unit_len, dwarf.endian) catch unreachable;
|
|
},
|
|
}
|
|
hw.writeInt(u16, 2, dwarf.endian) catch unreachable;
|
|
unit_ptr.cross_section_relocs.appendAssumeCapacity(.{
|
|
.source_off = @intCast(hw.end),
|
|
.target_sec = .debug_info,
|
|
.target_unit = unit,
|
|
});
|
|
hw.splatByteAll(0, dwarf.sectionOffsetBytes()) catch unreachable;
|
|
hw.writeByte(@intFromEnum(dwarf.address_size)) catch unreachable;
|
|
hw.writeByte(0) catch unreachable;
|
|
hw.splatByteAll(0, unit_ptr.header_len - hw.end) catch unreachable;
|
|
try unit_ptr.replaceHeader(&dwarf.debug_aranges.section, dwarf, header_aw.written());
|
|
try unit_ptr.writeTrailer(&dwarf.debug_aranges.section, dwarf);
|
|
}
|
|
dwarf.debug_aranges.section.dirty = false;
|
|
}
|
|
if (dwarf.debug_frame.section.dirty) {
|
|
const target = &dwarf.bin_file.comp.root_mod.resolved_target.result;
|
|
switch (dwarf.debug_frame.header.format) {
|
|
.none => {},
|
|
.debug_frame => unreachable,
|
|
.eh_frame => switch (target.cpu.arch) {
|
|
.x86_64 => {
|
|
dev.check(.x86_64_backend);
|
|
const Register = @import("../codegen/x86_64/bits.zig").Register;
|
|
for (dwarf.debug_frame.section.units.items) |*unit| {
|
|
header_aw.clearRetainingCapacity();
|
|
try header_aw.ensureTotalCapacity(unit.header_len);
|
|
const unit_len = unit.header_len - dwarf.unitLengthBytes();
|
|
switch (dwarf.format) {
|
|
.@"32" => hw.writeInt(u32, @intCast(unit_len), dwarf.endian) catch unreachable,
|
|
.@"64" => {
|
|
hw.writeInt(u32, std.math.maxInt(u32), dwarf.endian) catch unreachable;
|
|
hw.writeInt(u64, unit_len, dwarf.endian) catch unreachable;
|
|
},
|
|
}
|
|
hw.splatByteAll(0, 4) catch unreachable;
|
|
hw.writeByte(1) catch unreachable;
|
|
hw.writeAll("zR\x00") catch unreachable;
|
|
hw.writeUleb128(dwarf.debug_frame.header.code_alignment_factor) catch unreachable;
|
|
hw.writeSleb128(dwarf.debug_frame.header.data_alignment_factor) catch unreachable;
|
|
hw.writeUleb128(dwarf.debug_frame.header.return_address_register) catch unreachable;
|
|
hw.writeUleb128(1) catch unreachable;
|
|
hw.writeByte(@bitCast(@as(DW.EH.PE, .{ .type = .sdata4, .rel = .pcrel }))) catch unreachable;
|
|
hw.writeByte(DW.CFA.def_cfa_sf) catch unreachable;
|
|
hw.writeUleb128(Register.rsp.dwarfNum()) catch unreachable;
|
|
hw.writeSleb128(-1) catch unreachable;
|
|
hw.writeByte(@as(u8, DW.CFA.offset) + Register.rip.dwarfNum()) catch unreachable;
|
|
hw.writeUleb128(1) catch unreachable;
|
|
hw.splatByteAll(DW.CFA.nop, unit.header_len - hw.end) catch unreachable;
|
|
try unit.replaceHeader(&dwarf.debug_frame.section, dwarf, header_aw.written());
|
|
try unit.writeTrailer(&dwarf.debug_frame.section, dwarf);
|
|
}
|
|
},
|
|
else => unreachable,
|
|
},
|
|
}
|
|
dwarf.debug_frame.section.dirty = false;
|
|
}
|
|
if (dwarf.debug_info.section.dirty) {
|
|
for (dwarf.mods.keys(), dwarf.mods.values(), dwarf.debug_info.section.units.items, 0..) |mod, mod_info, *unit_ptr, unit_index| {
|
|
const unit: Unit.Index = @enumFromInt(unit_index);
|
|
unit_ptr.clear();
|
|
try unit_ptr.cross_unit_relocs.ensureTotalCapacity(dwarf.gpa, 1);
|
|
try unit_ptr.cross_section_relocs.ensureTotalCapacity(dwarf.gpa, 7);
|
|
header_aw.clearRetainingCapacity();
|
|
try header_aw.ensureTotalCapacity(unit_ptr.header_len);
|
|
const unit_len = (if (unit_ptr.next.unwrap()) |next_unit|
|
|
dwarf.debug_info.section.getUnit(next_unit).off
|
|
else
|
|
dwarf.debug_info.section.len) - unit_ptr.off - dwarf.unitLengthBytes();
|
|
switch (dwarf.format) {
|
|
.@"32" => hw.writeInt(u32, @intCast(unit_len), dwarf.endian) catch unreachable,
|
|
.@"64" => {
|
|
hw.writeInt(u32, std.math.maxInt(u32), dwarf.endian) catch unreachable;
|
|
hw.writeInt(u64, unit_len, dwarf.endian) catch unreachable;
|
|
},
|
|
}
|
|
hw.writeInt(u16, 5, dwarf.endian) catch unreachable;
|
|
hw.writeByte(DW.UT.compile) catch unreachable;
|
|
hw.writeByte(@intFromEnum(dwarf.address_size)) catch unreachable;
|
|
unit_ptr.cross_section_relocs.appendAssumeCapacity(.{
|
|
.source_off = @intCast(hw.end),
|
|
.target_sec = .debug_abbrev,
|
|
.target_unit = DebugAbbrev.unit,
|
|
});
|
|
hw.splatByteAll(0, dwarf.sectionOffsetBytes()) catch unreachable;
|
|
const compile_unit_off: u32 = @intCast(hw.end);
|
|
hw.writeUleb128(try dwarf.refAbbrevCode(.compile_unit)) catch unreachable;
|
|
hw.writeByte(DW.LANG.Zig) catch unreachable;
|
|
unit_ptr.cross_section_relocs.appendAssumeCapacity(.{
|
|
.source_off = @intCast(hw.end),
|
|
.target_sec = .debug_line_str,
|
|
.target_unit = StringSection.unit,
|
|
.target_entry = (try dwarf.debug_line_str.addString(dwarf, "zig " ++ @import("build_options").version)).toOptional(),
|
|
});
|
|
hw.splatByteAll(0, dwarf.sectionOffsetBytes()) catch unreachable;
|
|
unit_ptr.cross_section_relocs.appendAssumeCapacity(.{
|
|
.source_off = @intCast(hw.end),
|
|
.target_sec = .debug_line_str,
|
|
.target_unit = StringSection.unit,
|
|
.target_entry = mod_info.root_dir_path.toOptional(),
|
|
});
|
|
hw.splatByteAll(0, dwarf.sectionOffsetBytes()) catch unreachable;
|
|
unit_ptr.cross_section_relocs.appendAssumeCapacity(.{
|
|
.source_off = @intCast(hw.end),
|
|
.target_sec = .debug_line_str,
|
|
.target_unit = StringSection.unit,
|
|
.target_entry = (try dwarf.debug_line_str.addString(dwarf, mod.root_src_path)).toOptional(),
|
|
});
|
|
hw.splatByteAll(0, dwarf.sectionOffsetBytes()) catch unreachable;
|
|
unit_ptr.cross_unit_relocs.appendAssumeCapacity(.{
|
|
.source_off = @intCast(hw.end),
|
|
.target_unit = .main,
|
|
.target_off = compile_unit_off,
|
|
});
|
|
hw.splatByteAll(0, dwarf.sectionOffsetBytes()) catch unreachable;
|
|
unit_ptr.cross_section_relocs.appendAssumeCapacity(.{
|
|
.source_off = @intCast(hw.end),
|
|
.target_sec = .debug_line,
|
|
.target_unit = unit,
|
|
});
|
|
hw.splatByteAll(0, dwarf.sectionOffsetBytes()) catch unreachable;
|
|
unit_ptr.cross_section_relocs.appendAssumeCapacity(.{
|
|
.source_off = @intCast(hw.end),
|
|
.target_sec = .debug_rnglists,
|
|
.target_unit = unit,
|
|
.target_off = DebugRngLists.baseOffset(dwarf),
|
|
});
|
|
hw.splatByteAll(0, dwarf.sectionOffsetBytes()) catch unreachable;
|
|
hw.writeUleb128(0) catch unreachable;
|
|
hw.writeUleb128(try dwarf.refAbbrevCode(.module)) catch unreachable;
|
|
unit_ptr.cross_section_relocs.appendAssumeCapacity(.{
|
|
.source_off = @intCast(hw.end),
|
|
.target_sec = .debug_str,
|
|
.target_unit = StringSection.unit,
|
|
.target_entry = (try dwarf.debug_str.addString(dwarf, mod.fully_qualified_name)).toOptional(),
|
|
});
|
|
hw.splatByteAll(0, dwarf.sectionOffsetBytes()) catch unreachable;
|
|
hw.writeUleb128(0) catch unreachable;
|
|
try unit_ptr.replaceHeader(&dwarf.debug_info.section, dwarf, header_aw.written());
|
|
try unit_ptr.writeTrailer(&dwarf.debug_info.section, dwarf);
|
|
}
|
|
dwarf.debug_info.section.dirty = false;
|
|
}
|
|
if (dwarf.debug_abbrev.section.dirty) {
|
|
assert(!dwarf.debug_info.section.dirty);
|
|
try dwarf.debug_abbrev.section.getUnit(DebugAbbrev.unit).writeTrailer(&dwarf.debug_abbrev.section, dwarf);
|
|
dwarf.debug_abbrev.section.dirty = false;
|
|
}
|
|
if (dwarf.debug_str.section.dirty) {
|
|
const contents = dwarf.debug_str.contents.items;
|
|
try dwarf.debug_str.section.resize(dwarf, contents.len);
|
|
try dwarf.getFile().?.pwriteAll(contents, dwarf.debug_str.section.off(dwarf));
|
|
dwarf.debug_str.section.dirty = false;
|
|
}
|
|
if (dwarf.debug_line.section.dirty) {
|
|
for (dwarf.mods.values(), dwarf.debug_line.section.units.items) |mod_info, *unit| try unit.resizeHeader(
|
|
&dwarf.debug_line.section,
|
|
dwarf,
|
|
DebugLine.headerBytes(dwarf, @intCast(mod_info.dirs.count()), @intCast(mod_info.files.count())),
|
|
);
|
|
for (dwarf.mods.values(), dwarf.debug_line.section.units.items) |mod_info, *unit| {
|
|
unit.clear();
|
|
try unit.cross_section_relocs.ensureTotalCapacity(dwarf.gpa, mod_info.dirs.count() + 2 * (mod_info.files.count()));
|
|
header_aw.clearRetainingCapacity();
|
|
try header_aw.ensureTotalCapacity(unit.header_len);
|
|
const unit_len = (if (unit.next.unwrap()) |next_unit|
|
|
dwarf.debug_line.section.getUnit(next_unit).off
|
|
else
|
|
dwarf.debug_line.section.len) - unit.off - dwarf.unitLengthBytes();
|
|
switch (dwarf.format) {
|
|
.@"32" => hw.writeInt(u32, @intCast(unit_len), dwarf.endian) catch unreachable,
|
|
.@"64" => {
|
|
hw.writeInt(u32, std.math.maxInt(u32), dwarf.endian) catch unreachable;
|
|
hw.writeInt(u64, unit_len, dwarf.endian) catch unreachable;
|
|
},
|
|
}
|
|
hw.writeInt(u16, 5, dwarf.endian) catch unreachable;
|
|
hw.writeByte(@intFromEnum(dwarf.address_size)) catch unreachable;
|
|
hw.writeByte(0) catch unreachable;
|
|
switch (dwarf.format) {
|
|
.@"32" => hw.writeInt(u32, @intCast(unit.header_len - hw.end - 4), dwarf.endian) catch unreachable,
|
|
.@"64" => hw.writeInt(u64, @intCast(unit.header_len - hw.end - 8), dwarf.endian) catch unreachable,
|
|
}
|
|
const StandardOpcode = DeclValEnum(DW.LNS);
|
|
hw.writeAll(&.{
|
|
dwarf.debug_line.header.minimum_instruction_length,
|
|
dwarf.debug_line.header.maximum_operations_per_instruction,
|
|
@intFromBool(dwarf.debug_line.header.default_is_stmt),
|
|
@bitCast(dwarf.debug_line.header.line_base),
|
|
dwarf.debug_line.header.line_range,
|
|
dwarf.debug_line.header.opcode_base,
|
|
}) catch unreachable;
|
|
hw.writeAll(std.enums.EnumArray(StandardOpcode, u8).init(.{
|
|
.extended_op = undefined,
|
|
.copy = 0,
|
|
.advance_pc = 1,
|
|
.advance_line = 1,
|
|
.set_file = 1,
|
|
.set_column = 1,
|
|
.negate_stmt = 0,
|
|
.set_basic_block = 0,
|
|
.const_add_pc = 0,
|
|
.fixed_advance_pc = 1,
|
|
.set_prologue_end = 0,
|
|
.set_epilogue_begin = 0,
|
|
.set_isa = 1,
|
|
}).values[1..dwarf.debug_line.header.opcode_base]) catch unreachable;
|
|
hw.writeByte(1) catch unreachable;
|
|
hw.writeUleb128(DW.LNCT.path) catch unreachable;
|
|
hw.writeUleb128(DW.FORM.line_strp) catch unreachable;
|
|
hw.writeUleb128(mod_info.dirs.count()) catch unreachable;
|
|
for (mod_info.dirs.keys()) |dir_unit| {
|
|
unit.cross_section_relocs.appendAssumeCapacity(.{
|
|
.source_off = @intCast(hw.end),
|
|
.target_sec = .debug_line_str,
|
|
.target_unit = StringSection.unit,
|
|
.target_entry = dwarf.getModInfo(dir_unit).root_dir_path.toOptional(),
|
|
});
|
|
hw.splatByteAll(0, dwarf.sectionOffsetBytes()) catch unreachable;
|
|
}
|
|
const dir_index_info = DebugLine.dirIndexInfo(@intCast(mod_info.dirs.count()));
|
|
hw.writeByte(3) catch unreachable;
|
|
hw.writeUleb128(DW.LNCT.path) catch unreachable;
|
|
hw.writeUleb128(DW.FORM.line_strp) catch unreachable;
|
|
hw.writeUleb128(DW.LNCT.directory_index) catch unreachable;
|
|
hw.writeUleb128(@intFromEnum(dir_index_info.form)) catch unreachable;
|
|
hw.writeUleb128(DW.LNCT.LLVM_source) catch unreachable;
|
|
hw.writeUleb128(DW.FORM.line_strp) catch unreachable;
|
|
hw.writeUleb128(mod_info.files.count()) catch unreachable;
|
|
for (mod_info.files.keys()) |file_index| {
|
|
const file = zcu.fileByIndex(file_index);
|
|
unit.cross_section_relocs.appendAssumeCapacity(.{
|
|
.source_off = @intCast(hw.end),
|
|
.target_sec = .debug_line_str,
|
|
.target_unit = StringSection.unit,
|
|
.target_entry = (try dwarf.debug_line_str.addString(dwarf, file.sub_file_path)).toOptional(),
|
|
});
|
|
hw.splatByteAll(0, dwarf.sectionOffsetBytes()) catch unreachable;
|
|
const dir_index = mod_info.dirs.getIndex(dwarf.getUnitIfExists(file.mod.?).?) orelse 0;
|
|
switch (dir_index_info.bytes) {
|
|
else => unreachable,
|
|
1 => hw.writeByte(@intCast(dir_index)) catch unreachable,
|
|
2 => hw.writeInt(u16, @intCast(dir_index), dwarf.endian) catch unreachable,
|
|
}
|
|
unit.cross_section_relocs.appendAssumeCapacity(.{
|
|
.source_off = @intCast(hw.end),
|
|
.target_sec = .debug_line_str,
|
|
.target_unit = StringSection.unit,
|
|
.target_entry = (try dwarf.debug_line_str.addString(
|
|
dwarf,
|
|
if (file.is_builtin) file.source.? else "",
|
|
)).toOptional(),
|
|
});
|
|
hw.splatByteAll(0, dwarf.sectionOffsetBytes()) catch unreachable;
|
|
}
|
|
try unit.replaceHeader(&dwarf.debug_line.section, dwarf, header_aw.written());
|
|
try unit.writeTrailer(&dwarf.debug_line.section, dwarf);
|
|
}
|
|
dwarf.debug_line.section.dirty = false;
|
|
}
|
|
if (dwarf.debug_line_str.section.dirty) {
|
|
const contents = dwarf.debug_line_str.contents.items;
|
|
try dwarf.debug_line_str.section.resize(dwarf, contents.len);
|
|
try dwarf.getFile().?.pwriteAll(contents, dwarf.debug_line_str.section.off(dwarf));
|
|
dwarf.debug_line_str.section.dirty = false;
|
|
}
|
|
if (dwarf.debug_loclists.section.dirty) {
|
|
dwarf.debug_loclists.section.dirty = false;
|
|
}
|
|
if (dwarf.debug_rnglists.section.dirty) {
|
|
for (dwarf.debug_rnglists.section.units.items) |*unit| {
|
|
header_aw.clearRetainingCapacity();
|
|
try header_aw.ensureTotalCapacity(unit.header_len);
|
|
const unit_len = (if (unit.next.unwrap()) |next_unit|
|
|
dwarf.debug_rnglists.section.getUnit(next_unit).off
|
|
else
|
|
dwarf.debug_rnglists.section.len) - unit.off - dwarf.unitLengthBytes();
|
|
switch (dwarf.format) {
|
|
.@"32" => hw.writeInt(u32, @intCast(unit_len), dwarf.endian) catch unreachable,
|
|
.@"64" => {
|
|
hw.writeInt(u32, std.math.maxInt(u32), dwarf.endian) catch unreachable;
|
|
hw.writeInt(u64, unit_len, dwarf.endian) catch unreachable;
|
|
},
|
|
}
|
|
hw.writeInt(u16, 5, dwarf.endian) catch unreachable;
|
|
hw.writeByte(@intFromEnum(dwarf.address_size)) catch unreachable;
|
|
hw.writeByte(0) catch unreachable;
|
|
hw.writeInt(u32, 1, dwarf.endian) catch unreachable;
|
|
switch (dwarf.format) {
|
|
.@"32" => hw.writeInt(u32, dwarf.sectionOffsetBytes() * 1, dwarf.endian) catch unreachable,
|
|
.@"64" => hw.writeInt(u64, dwarf.sectionOffsetBytes() * 1, dwarf.endian) catch unreachable,
|
|
}
|
|
try unit.replaceHeader(&dwarf.debug_rnglists.section, dwarf, header_aw.written());
|
|
try unit.writeTrailer(&dwarf.debug_rnglists.section, dwarf);
|
|
}
|
|
dwarf.debug_rnglists.section.dirty = false;
|
|
}
|
|
assert(!dwarf.debug_abbrev.section.dirty);
|
|
assert(!dwarf.debug_aranges.section.dirty);
|
|
assert(!dwarf.debug_frame.section.dirty);
|
|
assert(!dwarf.debug_info.section.dirty);
|
|
assert(!dwarf.debug_line.section.dirty);
|
|
assert(!dwarf.debug_line_str.section.dirty);
|
|
assert(!dwarf.debug_loclists.section.dirty);
|
|
assert(!dwarf.debug_rnglists.section.dirty);
|
|
assert(!dwarf.debug_str.section.dirty);
|
|
}
|
|
|
|
pub fn resolveRelocs(dwarf: *Dwarf) RelocError!void {
|
|
for ([_]*Section{
|
|
&dwarf.debug_abbrev.section,
|
|
&dwarf.debug_aranges.section,
|
|
&dwarf.debug_frame.section,
|
|
&dwarf.debug_info.section,
|
|
&dwarf.debug_line.section,
|
|
&dwarf.debug_line_str.section,
|
|
&dwarf.debug_loclists.section,
|
|
&dwarf.debug_rnglists.section,
|
|
&dwarf.debug_str.section,
|
|
}) |sec| try sec.resolveRelocs(dwarf);
|
|
}
|
|
|
|
fn DeclValEnum(comptime T: type) type {
|
|
const decls = @typeInfo(T).@"struct".decls;
|
|
@setEvalBranchQuota(7 * decls.len);
|
|
var fields: [decls.len]std.builtin.Type.EnumField = undefined;
|
|
var fields_len = 0;
|
|
var min_value: ?comptime_int = null;
|
|
var max_value: ?comptime_int = null;
|
|
for (decls) |decl| {
|
|
if (std.mem.startsWith(u8, decl.name, "HP_") or std.mem.endsWith(u8, decl.name, "_user")) continue;
|
|
const value = @field(T, decl.name);
|
|
fields[fields_len] = .{ .name = decl.name, .value = value };
|
|
fields_len += 1;
|
|
if (min_value == null or min_value.? > value) min_value = value;
|
|
if (max_value == null or max_value.? < value) max_value = value;
|
|
}
|
|
return @Type(.{ .@"enum" = .{
|
|
.tag_type = std.math.IntFittingRange(min_value orelse 0, max_value orelse 0),
|
|
.fields = fields[0..fields_len],
|
|
.decls = &.{},
|
|
.is_exhaustive = true,
|
|
} });
|
|
}
|
|
|
|
const AbbrevCode = enum {
|
|
null,
|
|
// padding codes must be one byte uleb128 values to function
|
|
pad_1,
|
|
pad_n,
|
|
// decl, generic decl, and instance codes are assumed to all have the same uleb128 length
|
|
decl_alias,
|
|
decl_empty_enum,
|
|
decl_enum,
|
|
decl_namespace_struct,
|
|
decl_struct,
|
|
decl_packed_struct,
|
|
decl_union,
|
|
decl_var,
|
|
decl_const,
|
|
decl_const_runtime_bits,
|
|
decl_const_comptime_state,
|
|
decl_const_runtime_bits_comptime_state,
|
|
decl_nullary_func,
|
|
decl_func,
|
|
decl_nullary_func_generic,
|
|
decl_func_generic,
|
|
decl_extern_nullary_func,
|
|
decl_extern_func,
|
|
generic_decl_var,
|
|
generic_decl_const,
|
|
generic_decl_func,
|
|
decl_instance_alias,
|
|
decl_instance_empty_enum,
|
|
decl_instance_enum,
|
|
decl_instance_namespace_struct,
|
|
decl_instance_struct,
|
|
decl_instance_packed_struct,
|
|
decl_instance_union,
|
|
decl_instance_var,
|
|
decl_instance_const,
|
|
decl_instance_const_runtime_bits,
|
|
decl_instance_const_comptime_state,
|
|
decl_instance_const_runtime_bits_comptime_state,
|
|
decl_instance_nullary_func,
|
|
decl_instance_func,
|
|
decl_instance_nullary_func_generic,
|
|
decl_instance_func_generic,
|
|
decl_instance_extern_nullary_func,
|
|
decl_instance_extern_func,
|
|
// the rest are unrestricted other than empty variants must not be longer
|
|
// than the non-empty variant, and so should appear first
|
|
compile_unit,
|
|
module,
|
|
empty_file,
|
|
file,
|
|
signed_enum_field,
|
|
unsigned_enum_field,
|
|
big_enum_field,
|
|
generated_field,
|
|
struct_field,
|
|
struct_field_default_runtime_bits,
|
|
struct_field_default_comptime_state,
|
|
struct_field_comptime,
|
|
struct_field_comptime_runtime_bits,
|
|
struct_field_comptime_comptime_state,
|
|
packed_struct_field,
|
|
untagged_union_field,
|
|
tagged_union,
|
|
signed_tagged_union_field,
|
|
unsigned_tagged_union_field,
|
|
big_tagged_union_field,
|
|
tagged_union_default_field,
|
|
void_type,
|
|
numeric_type,
|
|
inferred_error_set_type,
|
|
ptr_type,
|
|
ptr_sentinel_type,
|
|
is_const,
|
|
is_volatile,
|
|
array_type,
|
|
array_sentinel_type,
|
|
vector_type,
|
|
array_index,
|
|
nullary_func_type,
|
|
func_type,
|
|
func_type_param,
|
|
is_var_args,
|
|
generated_empty_enum_type,
|
|
generated_enum_type,
|
|
generated_empty_struct_type,
|
|
generated_struct_type,
|
|
generated_union_type,
|
|
empty_enum_type,
|
|
enum_type,
|
|
empty_struct_type,
|
|
struct_type,
|
|
empty_packed_struct_type,
|
|
packed_struct_type,
|
|
empty_union_type,
|
|
union_type,
|
|
builtin_extern_nullary_func,
|
|
builtin_extern_func,
|
|
builtin_extern_var,
|
|
empty_block,
|
|
block,
|
|
empty_inlined_func,
|
|
inlined_func,
|
|
arg,
|
|
unnamed_arg,
|
|
comptime_arg,
|
|
unnamed_comptime_arg,
|
|
comptime_arg_runtime_bits,
|
|
unnamed_comptime_arg_runtime_bits,
|
|
comptime_arg_comptime_state,
|
|
unnamed_comptime_arg_comptime_state,
|
|
comptime_arg_runtime_bits_comptime_state,
|
|
unnamed_comptime_arg_runtime_bits_comptime_state,
|
|
extern_param,
|
|
local_var,
|
|
local_const,
|
|
local_const_runtime_bits,
|
|
local_const_comptime_state,
|
|
local_const_runtime_bits_comptime_state,
|
|
undefined_comptime_value,
|
|
data2_comptime_value,
|
|
data4_comptime_value,
|
|
data8_comptime_value,
|
|
data16_comptime_value,
|
|
sdata_comptime_value,
|
|
udata_comptime_value,
|
|
block_comptime_value,
|
|
string_comptime_value,
|
|
location_comptime_value,
|
|
aggregate_comptime_value,
|
|
comptime_value_field_runtime_bits,
|
|
comptime_value_field_comptime_state,
|
|
comptime_value_elem_runtime_bits,
|
|
comptime_value_elem_comptime_state,
|
|
|
|
const decl_bytes = uleb128Bytes(@intFromEnum(AbbrevCode.decl_instance_extern_func));
|
|
comptime {
|
|
assert(uleb128Bytes(@intFromEnum(AbbrevCode.pad_1)) == 1);
|
|
assert(uleb128Bytes(@intFromEnum(AbbrevCode.pad_n)) == 1);
|
|
assert(uleb128Bytes(@intFromEnum(AbbrevCode.decl_alias)) == decl_bytes);
|
|
}
|
|
|
|
const Attr = struct {
|
|
DeclValEnum(DW.AT),
|
|
DeclValEnum(DW.FORM),
|
|
};
|
|
const decl_abbrev_common_attrs = &[_]Attr{
|
|
.{ .ZIG_parent, .ref_addr },
|
|
.{ .decl_line, .data4 },
|
|
.{ .decl_column, .udata },
|
|
.{ .accessibility, .data1 },
|
|
.{ .name, .strp },
|
|
};
|
|
const generic_decl_abbrev_common_attrs = decl_abbrev_common_attrs ++ &[_]Attr{
|
|
.{ .declaration, .flag_present },
|
|
};
|
|
const decl_instance_abbrev_common_attrs = &[_]Attr{
|
|
.{ .ZIG_parent, .ref_addr },
|
|
.{ .abstract_origin, .ref_addr },
|
|
};
|
|
const abbrevs = std.EnumArray(AbbrevCode, struct {
|
|
tag: DeclValEnum(DW.TAG),
|
|
children: bool = false,
|
|
attrs: []const Attr = &.{},
|
|
}).init(.{
|
|
.pad_1 = .{
|
|
.tag = .ZIG_padding,
|
|
},
|
|
.pad_n = .{
|
|
.tag = .ZIG_padding,
|
|
.attrs = &.{
|
|
.{ .ZIG_padding, .block },
|
|
},
|
|
},
|
|
.decl_alias = .{
|
|
.tag = .imported_declaration,
|
|
.attrs = decl_abbrev_common_attrs ++ .{
|
|
.{ .import, .ref_addr },
|
|
},
|
|
},
|
|
.decl_empty_enum = .{
|
|
.tag = .enumeration_type,
|
|
.attrs = decl_abbrev_common_attrs ++ .{
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.decl_enum = .{
|
|
.tag = .enumeration_type,
|
|
.children = true,
|
|
.attrs = decl_abbrev_common_attrs ++ .{
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.decl_namespace_struct = .{
|
|
.tag = .structure_type,
|
|
.attrs = decl_abbrev_common_attrs ++ .{
|
|
.{ .declaration, .flag },
|
|
},
|
|
},
|
|
.decl_struct = .{
|
|
.tag = .structure_type,
|
|
.children = true,
|
|
.attrs = decl_abbrev_common_attrs ++ .{
|
|
.{ .byte_size, .udata },
|
|
.{ .alignment, .udata },
|
|
},
|
|
},
|
|
.decl_packed_struct = .{
|
|
.tag = .structure_type,
|
|
.children = true,
|
|
.attrs = decl_abbrev_common_attrs ++ .{
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.decl_union = .{
|
|
.tag = .union_type,
|
|
.children = true,
|
|
.attrs = decl_abbrev_common_attrs ++ .{
|
|
.{ .byte_size, .udata },
|
|
.{ .alignment, .udata },
|
|
},
|
|
},
|
|
.decl_var = .{
|
|
.tag = .variable,
|
|
.attrs = decl_abbrev_common_attrs ++ .{
|
|
.{ .linkage_name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .location, .exprloc },
|
|
.{ .alignment, .udata },
|
|
.{ .external, .flag },
|
|
},
|
|
},
|
|
.decl_const = .{
|
|
.tag = .constant,
|
|
.attrs = decl_abbrev_common_attrs ++ .{
|
|
.{ .linkage_name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .alignment, .udata },
|
|
.{ .external, .flag },
|
|
},
|
|
},
|
|
.decl_const_runtime_bits = .{
|
|
.tag = .constant,
|
|
.attrs = decl_abbrev_common_attrs ++ .{
|
|
.{ .linkage_name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .alignment, .udata },
|
|
.{ .external, .flag },
|
|
.{ .const_value, .block },
|
|
},
|
|
},
|
|
.decl_const_comptime_state = .{
|
|
.tag = .constant,
|
|
.attrs = decl_abbrev_common_attrs ++ .{
|
|
.{ .linkage_name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .alignment, .udata },
|
|
.{ .external, .flag },
|
|
.{ .ZIG_comptime_value, .ref_addr },
|
|
},
|
|
},
|
|
.decl_const_runtime_bits_comptime_state = .{
|
|
.tag = .constant,
|
|
.attrs = decl_abbrev_common_attrs ++ .{
|
|
.{ .linkage_name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .alignment, .udata },
|
|
.{ .external, .flag },
|
|
.{ .const_value, .block },
|
|
.{ .ZIG_comptime_value, .ref_addr },
|
|
},
|
|
},
|
|
.decl_nullary_func = .{
|
|
.tag = .subprogram,
|
|
.attrs = decl_abbrev_common_attrs ++ .{
|
|
.{ .linkage_name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .low_pc, .addr },
|
|
.{ .high_pc, .data4 },
|
|
.{ .alignment, .udata },
|
|
.{ .external, .flag },
|
|
.{ .noreturn, .flag },
|
|
},
|
|
},
|
|
.decl_func = .{
|
|
.tag = .subprogram,
|
|
.children = true,
|
|
.attrs = decl_abbrev_common_attrs ++ .{
|
|
.{ .linkage_name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .low_pc, .addr },
|
|
.{ .high_pc, .data4 },
|
|
.{ .alignment, .udata },
|
|
.{ .external, .flag },
|
|
.{ .noreturn, .flag },
|
|
},
|
|
},
|
|
.decl_nullary_func_generic = .{
|
|
.tag = .subprogram,
|
|
.attrs = decl_abbrev_common_attrs ++ .{
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.decl_func_generic = .{
|
|
.tag = .subprogram,
|
|
.children = true,
|
|
.attrs = decl_abbrev_common_attrs ++ .{
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.decl_extern_nullary_func = .{
|
|
.tag = .subprogram,
|
|
.attrs = decl_abbrev_common_attrs ++ .{
|
|
.{ .linkage_name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .low_pc, .addr },
|
|
.{ .external, .flag_present },
|
|
.{ .noreturn, .flag },
|
|
},
|
|
},
|
|
.decl_extern_func = .{
|
|
.tag = .subprogram,
|
|
.children = true,
|
|
.attrs = decl_abbrev_common_attrs ++ .{
|
|
.{ .linkage_name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .low_pc, .addr },
|
|
.{ .external, .flag_present },
|
|
.{ .noreturn, .flag },
|
|
},
|
|
},
|
|
.generic_decl_var = .{
|
|
.tag = .variable,
|
|
.attrs = generic_decl_abbrev_common_attrs,
|
|
},
|
|
.generic_decl_const = .{
|
|
.tag = .constant,
|
|
.attrs = generic_decl_abbrev_common_attrs,
|
|
},
|
|
.generic_decl_func = .{
|
|
.tag = .subprogram,
|
|
.attrs = generic_decl_abbrev_common_attrs,
|
|
},
|
|
.decl_instance_alias = .{
|
|
.tag = .imported_declaration,
|
|
.attrs = decl_instance_abbrev_common_attrs ++ .{
|
|
.{ .import, .ref_addr },
|
|
},
|
|
},
|
|
.decl_instance_empty_enum = .{
|
|
.tag = .enumeration_type,
|
|
.attrs = decl_instance_abbrev_common_attrs ++ .{
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.decl_instance_enum = .{
|
|
.tag = .enumeration_type,
|
|
.children = true,
|
|
.attrs = decl_instance_abbrev_common_attrs ++ .{
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.decl_instance_namespace_struct = .{
|
|
.tag = .structure_type,
|
|
.attrs = decl_instance_abbrev_common_attrs ++ .{
|
|
.{ .declaration, .flag },
|
|
},
|
|
},
|
|
.decl_instance_struct = .{
|
|
.tag = .structure_type,
|
|
.children = true,
|
|
.attrs = decl_instance_abbrev_common_attrs ++ .{
|
|
.{ .byte_size, .udata },
|
|
.{ .alignment, .udata },
|
|
},
|
|
},
|
|
.decl_instance_packed_struct = .{
|
|
.tag = .structure_type,
|
|
.children = true,
|
|
.attrs = decl_instance_abbrev_common_attrs ++ .{
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.decl_instance_union = .{
|
|
.tag = .union_type,
|
|
.children = true,
|
|
.attrs = decl_instance_abbrev_common_attrs ++ .{
|
|
.{ .byte_size, .udata },
|
|
.{ .alignment, .udata },
|
|
},
|
|
},
|
|
.decl_instance_var = .{
|
|
.tag = .variable,
|
|
.attrs = decl_instance_abbrev_common_attrs ++ .{
|
|
.{ .linkage_name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .location, .exprloc },
|
|
.{ .alignment, .udata },
|
|
.{ .external, .flag },
|
|
},
|
|
},
|
|
.decl_instance_const = .{
|
|
.tag = .constant,
|
|
.attrs = decl_instance_abbrev_common_attrs ++ .{
|
|
.{ .linkage_name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .alignment, .udata },
|
|
.{ .external, .flag },
|
|
},
|
|
},
|
|
.decl_instance_const_runtime_bits = .{
|
|
.tag = .constant,
|
|
.attrs = decl_instance_abbrev_common_attrs ++ .{
|
|
.{ .linkage_name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .alignment, .udata },
|
|
.{ .external, .flag },
|
|
.{ .const_value, .block },
|
|
},
|
|
},
|
|
.decl_instance_const_comptime_state = .{
|
|
.tag = .constant,
|
|
.attrs = decl_instance_abbrev_common_attrs ++ .{
|
|
.{ .linkage_name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .alignment, .udata },
|
|
.{ .external, .flag },
|
|
.{ .ZIG_comptime_value, .ref_addr },
|
|
},
|
|
},
|
|
.decl_instance_const_runtime_bits_comptime_state = .{
|
|
.tag = .constant,
|
|
.attrs = decl_instance_abbrev_common_attrs ++ .{
|
|
.{ .linkage_name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .alignment, .udata },
|
|
.{ .external, .flag },
|
|
.{ .const_value, .block },
|
|
.{ .ZIG_comptime_value, .ref_addr },
|
|
},
|
|
},
|
|
.decl_instance_nullary_func = .{
|
|
.tag = .subprogram,
|
|
.attrs = decl_instance_abbrev_common_attrs ++ .{
|
|
.{ .linkage_name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .low_pc, .addr },
|
|
.{ .high_pc, .data4 },
|
|
.{ .alignment, .udata },
|
|
.{ .external, .flag },
|
|
.{ .noreturn, .flag },
|
|
},
|
|
},
|
|
.decl_instance_func = .{
|
|
.tag = .subprogram,
|
|
.children = true,
|
|
.attrs = decl_instance_abbrev_common_attrs ++ .{
|
|
.{ .linkage_name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .low_pc, .addr },
|
|
.{ .high_pc, .data4 },
|
|
.{ .alignment, .udata },
|
|
.{ .external, .flag },
|
|
.{ .noreturn, .flag },
|
|
},
|
|
},
|
|
.decl_instance_nullary_func_generic = .{
|
|
.tag = .subprogram,
|
|
.attrs = decl_instance_abbrev_common_attrs ++ .{
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.decl_instance_func_generic = .{
|
|
.tag = .subprogram,
|
|
.children = true,
|
|
.attrs = decl_instance_abbrev_common_attrs ++ .{
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.decl_instance_extern_nullary_func = .{
|
|
.tag = .subprogram,
|
|
.attrs = decl_instance_abbrev_common_attrs ++ .{
|
|
.{ .linkage_name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .low_pc, .addr },
|
|
.{ .external, .flag_present },
|
|
.{ .noreturn, .flag },
|
|
},
|
|
},
|
|
.decl_instance_extern_func = .{
|
|
.tag = .subprogram,
|
|
.children = true,
|
|
.attrs = decl_instance_abbrev_common_attrs ++ .{
|
|
.{ .linkage_name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .low_pc, .addr },
|
|
.{ .external, .flag_present },
|
|
.{ .noreturn, .flag },
|
|
},
|
|
},
|
|
.compile_unit = .{
|
|
.tag = .compile_unit,
|
|
.children = true,
|
|
.attrs = &.{
|
|
.{ .language, .data1 },
|
|
.{ .producer, .line_strp },
|
|
.{ .comp_dir, .line_strp },
|
|
.{ .name, .line_strp },
|
|
.{ .base_types, .ref_addr },
|
|
.{ .stmt_list, .sec_offset },
|
|
.{ .rnglists_base, .sec_offset },
|
|
.{ .ranges, .rnglistx },
|
|
},
|
|
},
|
|
.module = .{
|
|
.tag = .module,
|
|
.children = true,
|
|
.attrs = &.{
|
|
.{ .name, .strp },
|
|
.{ .ranges, .rnglistx },
|
|
},
|
|
},
|
|
.empty_file = .{
|
|
.tag = .structure_type,
|
|
.attrs = &.{
|
|
.{ .decl_file, .udata },
|
|
.{ .name, .strp },
|
|
},
|
|
},
|
|
.file = .{
|
|
.tag = .structure_type,
|
|
.children = true,
|
|
.attrs = &.{
|
|
.{ .decl_file, .udata },
|
|
.{ .name, .strp },
|
|
.{ .byte_size, .udata },
|
|
.{ .alignment, .udata },
|
|
},
|
|
},
|
|
.signed_enum_field = .{
|
|
.tag = .enumerator,
|
|
.attrs = &.{
|
|
.{ .const_value, .sdata },
|
|
.{ .name, .strp },
|
|
},
|
|
},
|
|
.unsigned_enum_field = .{
|
|
.tag = .enumerator,
|
|
.attrs = &.{
|
|
.{ .const_value, .udata },
|
|
.{ .name, .strp },
|
|
},
|
|
},
|
|
.big_enum_field = .{
|
|
.tag = .enumerator,
|
|
.attrs = &.{
|
|
.{ .const_value, .block },
|
|
.{ .name, .strp },
|
|
},
|
|
},
|
|
.generated_field = .{
|
|
.tag = .member,
|
|
.attrs = &.{
|
|
.{ .name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .data_member_location, .udata },
|
|
.{ .artificial, .flag_present },
|
|
},
|
|
},
|
|
.struct_field = .{
|
|
.tag = .member,
|
|
.attrs = &.{
|
|
.{ .name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .data_member_location, .udata },
|
|
.{ .alignment, .udata },
|
|
},
|
|
},
|
|
.struct_field_default_runtime_bits = .{
|
|
.tag = .member,
|
|
.attrs = &.{
|
|
.{ .name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .data_member_location, .udata },
|
|
.{ .alignment, .udata },
|
|
.{ .default_value, .block },
|
|
},
|
|
},
|
|
.struct_field_default_comptime_state = .{
|
|
.tag = .member,
|
|
.attrs = &.{
|
|
.{ .name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .data_member_location, .udata },
|
|
.{ .alignment, .udata },
|
|
.{ .ZIG_comptime_value, .ref_addr },
|
|
},
|
|
},
|
|
.struct_field_comptime = .{
|
|
.tag = .member,
|
|
.attrs = &.{
|
|
.{ .const_expr, .flag_present },
|
|
.{ .name, .strp },
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.struct_field_comptime_runtime_bits = .{
|
|
.tag = .member,
|
|
.attrs = &.{
|
|
.{ .const_expr, .flag_present },
|
|
.{ .name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .const_value, .block },
|
|
},
|
|
},
|
|
.struct_field_comptime_comptime_state = .{
|
|
.tag = .member,
|
|
.attrs = &.{
|
|
.{ .const_expr, .flag_present },
|
|
.{ .name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .ZIG_comptime_value, .ref_addr },
|
|
},
|
|
},
|
|
.packed_struct_field = .{
|
|
.tag = .member,
|
|
.attrs = &.{
|
|
.{ .name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .data_bit_offset, .udata },
|
|
},
|
|
},
|
|
.untagged_union_field = .{
|
|
.tag = .member,
|
|
.attrs = &.{
|
|
.{ .name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .alignment, .udata },
|
|
},
|
|
},
|
|
.tagged_union = .{
|
|
.tag = .variant_part,
|
|
.children = true,
|
|
.attrs = &.{
|
|
.{ .discr, .ref_addr },
|
|
},
|
|
},
|
|
.signed_tagged_union_field = .{
|
|
.tag = .variant,
|
|
.children = true,
|
|
.attrs = &.{
|
|
.{ .discr_value, .sdata },
|
|
},
|
|
},
|
|
.unsigned_tagged_union_field = .{
|
|
.tag = .variant,
|
|
.children = true,
|
|
.attrs = &.{
|
|
.{ .discr_value, .udata },
|
|
},
|
|
},
|
|
.big_tagged_union_field = .{
|
|
.tag = .variant,
|
|
.children = true,
|
|
.attrs = &.{
|
|
.{ .discr_value, .block },
|
|
},
|
|
},
|
|
.tagged_union_default_field = .{
|
|
.tag = .variant,
|
|
.children = true,
|
|
.attrs = &.{},
|
|
},
|
|
.void_type = .{
|
|
.tag = .unspecified_type,
|
|
.attrs = &.{
|
|
.{ .name, .strp },
|
|
},
|
|
},
|
|
.numeric_type = .{
|
|
.tag = .base_type,
|
|
.attrs = &.{
|
|
.{ .name, .strp },
|
|
.{ .encoding, .data1 },
|
|
.{ .bit_size, .udata },
|
|
.{ .byte_size, .udata },
|
|
.{ .alignment, .udata },
|
|
},
|
|
},
|
|
.inferred_error_set_type = .{
|
|
.tag = .typedef,
|
|
.attrs = &.{
|
|
.{ .name, .strp },
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.ptr_type = .{
|
|
.tag = .pointer_type,
|
|
.attrs = &.{
|
|
.{ .name, .strp },
|
|
.{ .alignment, .udata },
|
|
.{ .address_class, .data1 },
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.ptr_sentinel_type = .{
|
|
.tag = .pointer_type,
|
|
.attrs = &.{
|
|
.{ .name, .strp },
|
|
.{ .ZIG_sentinel, .block },
|
|
.{ .alignment, .udata },
|
|
.{ .address_class, .data1 },
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.is_const = .{
|
|
.tag = .const_type,
|
|
.attrs = &.{
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.is_volatile = .{
|
|
.tag = .volatile_type,
|
|
.attrs = &.{
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.array_type = .{
|
|
.tag = .array_type,
|
|
.children = true,
|
|
.attrs = &.{
|
|
.{ .name, .strp },
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.array_sentinel_type = .{
|
|
.tag = .array_type,
|
|
.children = true,
|
|
.attrs = &.{
|
|
.{ .name, .strp },
|
|
.{ .ZIG_sentinel, .block },
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.vector_type = .{
|
|
.tag = .array_type,
|
|
.children = true,
|
|
.attrs = &.{
|
|
.{ .name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .GNU_vector, .flag_present },
|
|
},
|
|
},
|
|
.array_index = .{
|
|
.tag = .subrange_type,
|
|
.attrs = &.{
|
|
.{ .type, .ref_addr },
|
|
.{ .count, .udata },
|
|
},
|
|
},
|
|
.nullary_func_type = .{
|
|
.tag = .subroutine_type,
|
|
.attrs = &.{
|
|
.{ .name, .strp },
|
|
.{ .calling_convention, .data1 },
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.func_type = .{
|
|
.tag = .subroutine_type,
|
|
.children = true,
|
|
.attrs = &.{
|
|
.{ .name, .strp },
|
|
.{ .calling_convention, .data1 },
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.func_type_param = .{
|
|
.tag = .formal_parameter,
|
|
.attrs = &.{
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.is_var_args = .{
|
|
.tag = .unspecified_parameters,
|
|
},
|
|
.generated_empty_enum_type = .{
|
|
.tag = .enumeration_type,
|
|
.attrs = &.{
|
|
.{ .name, .strp },
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.generated_enum_type = .{
|
|
.tag = .enumeration_type,
|
|
.children = true,
|
|
.attrs = &.{
|
|
.{ .name, .strp },
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.generated_empty_struct_type = .{
|
|
.tag = .structure_type,
|
|
.attrs = &.{
|
|
.{ .name, .strp },
|
|
.{ .declaration, .flag },
|
|
},
|
|
},
|
|
.generated_struct_type = .{
|
|
.tag = .structure_type,
|
|
.children = true,
|
|
.attrs = &.{
|
|
.{ .name, .strp },
|
|
.{ .byte_size, .udata },
|
|
.{ .alignment, .udata },
|
|
},
|
|
},
|
|
.generated_union_type = .{
|
|
.tag = .union_type,
|
|
.children = true,
|
|
.attrs = &.{
|
|
.{ .name, .strp },
|
|
.{ .byte_size, .udata },
|
|
.{ .alignment, .udata },
|
|
},
|
|
},
|
|
.empty_enum_type = .{
|
|
.tag = .enumeration_type,
|
|
.attrs = &.{
|
|
.{ .decl_file, .udata },
|
|
.{ .name, .strp },
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.enum_type = .{
|
|
.tag = .enumeration_type,
|
|
.children = true,
|
|
.attrs = &.{
|
|
.{ .decl_file, .udata },
|
|
.{ .name, .strp },
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.empty_struct_type = .{
|
|
.tag = .structure_type,
|
|
.attrs = &.{
|
|
.{ .decl_file, .udata },
|
|
.{ .name, .strp },
|
|
.{ .declaration, .flag },
|
|
},
|
|
},
|
|
.struct_type = .{
|
|
.tag = .structure_type,
|
|
.children = true,
|
|
.attrs = &.{
|
|
.{ .decl_file, .udata },
|
|
.{ .name, .strp },
|
|
.{ .byte_size, .udata },
|
|
.{ .alignment, .udata },
|
|
},
|
|
},
|
|
.empty_packed_struct_type = .{
|
|
.tag = .structure_type,
|
|
.attrs = &.{
|
|
.{ .decl_file, .udata },
|
|
.{ .name, .strp },
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.packed_struct_type = .{
|
|
.tag = .structure_type,
|
|
.children = true,
|
|
.attrs = &.{
|
|
.{ .decl_file, .udata },
|
|
.{ .name, .strp },
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.empty_union_type = .{
|
|
.tag = .union_type,
|
|
.attrs = &.{
|
|
.{ .decl_file, .udata },
|
|
.{ .name, .strp },
|
|
.{ .byte_size, .udata },
|
|
.{ .alignment, .udata },
|
|
},
|
|
},
|
|
.union_type = .{
|
|
.tag = .union_type,
|
|
.children = true,
|
|
.attrs = &.{
|
|
.{ .decl_file, .udata },
|
|
.{ .name, .strp },
|
|
.{ .byte_size, .udata },
|
|
.{ .alignment, .udata },
|
|
},
|
|
},
|
|
.builtin_extern_nullary_func = .{
|
|
.tag = .subprogram,
|
|
.attrs = &.{
|
|
.{ .ZIG_parent, .ref_addr },
|
|
.{ .linkage_name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .low_pc, .addr },
|
|
.{ .external, .flag_present },
|
|
.{ .noreturn, .flag },
|
|
},
|
|
},
|
|
.builtin_extern_func = .{
|
|
.tag = .subprogram,
|
|
.children = true,
|
|
.attrs = &.{
|
|
.{ .ZIG_parent, .ref_addr },
|
|
.{ .linkage_name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .low_pc, .addr },
|
|
.{ .external, .flag_present },
|
|
.{ .noreturn, .flag },
|
|
},
|
|
},
|
|
.builtin_extern_var = .{
|
|
.tag = .variable,
|
|
.attrs = &.{
|
|
.{ .ZIG_parent, .ref_addr },
|
|
.{ .linkage_name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .location, .exprloc },
|
|
.{ .external, .flag_present },
|
|
},
|
|
},
|
|
.empty_block = .{
|
|
.tag = .lexical_block,
|
|
.attrs = &.{
|
|
.{ .low_pc, .addr },
|
|
.{ .high_pc, .data4 },
|
|
},
|
|
},
|
|
.block = .{
|
|
.tag = .lexical_block,
|
|
.children = true,
|
|
.attrs = &.{
|
|
.{ .low_pc, .addr },
|
|
.{ .high_pc, .data4 },
|
|
},
|
|
},
|
|
.empty_inlined_func = .{
|
|
.tag = .inlined_subroutine,
|
|
.attrs = &.{
|
|
.{ .abstract_origin, .ref_addr },
|
|
.{ .call_line, .udata },
|
|
.{ .call_column, .udata },
|
|
.{ .low_pc, .addr },
|
|
.{ .high_pc, .data4 },
|
|
},
|
|
},
|
|
.inlined_func = .{
|
|
.tag = .inlined_subroutine,
|
|
.children = true,
|
|
.attrs = &.{
|
|
.{ .abstract_origin, .ref_addr },
|
|
.{ .call_line, .udata },
|
|
.{ .call_column, .udata },
|
|
.{ .low_pc, .addr },
|
|
.{ .high_pc, .data4 },
|
|
},
|
|
},
|
|
.arg = .{
|
|
.tag = .formal_parameter,
|
|
.attrs = &.{
|
|
.{ .name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .location, .exprloc },
|
|
},
|
|
},
|
|
.unnamed_arg = .{
|
|
.tag = .formal_parameter,
|
|
.attrs = &.{
|
|
.{ .type, .ref_addr },
|
|
.{ .location, .exprloc },
|
|
},
|
|
},
|
|
.comptime_arg = .{
|
|
.tag = .formal_parameter,
|
|
.attrs = &.{
|
|
.{ .const_expr, .flag_present },
|
|
.{ .name, .strp },
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.unnamed_comptime_arg = .{
|
|
.tag = .formal_parameter,
|
|
.attrs = &.{
|
|
.{ .const_expr, .flag_present },
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.comptime_arg_runtime_bits = .{
|
|
.tag = .formal_parameter,
|
|
.attrs = &.{
|
|
.{ .const_expr, .flag_present },
|
|
.{ .name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .const_value, .block },
|
|
},
|
|
},
|
|
.unnamed_comptime_arg_runtime_bits = .{
|
|
.tag = .formal_parameter,
|
|
.attrs = &.{
|
|
.{ .const_expr, .flag_present },
|
|
.{ .type, .ref_addr },
|
|
.{ .const_value, .block },
|
|
},
|
|
},
|
|
.comptime_arg_comptime_state = .{
|
|
.tag = .formal_parameter,
|
|
.attrs = &.{
|
|
.{ .const_expr, .flag_present },
|
|
.{ .name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .ZIG_comptime_value, .ref_addr },
|
|
},
|
|
},
|
|
.unnamed_comptime_arg_comptime_state = .{
|
|
.tag = .formal_parameter,
|
|
.attrs = &.{
|
|
.{ .const_expr, .flag_present },
|
|
.{ .type, .ref_addr },
|
|
.{ .ZIG_comptime_value, .ref_addr },
|
|
},
|
|
},
|
|
.comptime_arg_runtime_bits_comptime_state = .{
|
|
.tag = .formal_parameter,
|
|
.attrs = &.{
|
|
.{ .const_expr, .flag_present },
|
|
.{ .name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .const_value, .block },
|
|
.{ .ZIG_comptime_value, .ref_addr },
|
|
},
|
|
},
|
|
.unnamed_comptime_arg_runtime_bits_comptime_state = .{
|
|
.tag = .formal_parameter,
|
|
.attrs = &.{
|
|
.{ .const_expr, .flag_present },
|
|
.{ .type, .ref_addr },
|
|
.{ .const_value, .block },
|
|
.{ .ZIG_comptime_value, .ref_addr },
|
|
},
|
|
},
|
|
.extern_param = .{
|
|
.tag = .formal_parameter,
|
|
.attrs = &.{
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.local_var = .{
|
|
.tag = .variable,
|
|
.attrs = &.{
|
|
.{ .name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .location, .exprloc },
|
|
},
|
|
},
|
|
.local_const = .{
|
|
.tag = .constant,
|
|
.attrs = &.{
|
|
.{ .name, .strp },
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.local_const_runtime_bits = .{
|
|
.tag = .constant,
|
|
.attrs = &.{
|
|
.{ .name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .const_value, .block },
|
|
},
|
|
},
|
|
.local_const_comptime_state = .{
|
|
.tag = .constant,
|
|
.attrs = &.{
|
|
.{ .name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .ZIG_comptime_value, .ref_addr },
|
|
},
|
|
},
|
|
.local_const_runtime_bits_comptime_state = .{
|
|
.tag = .constant,
|
|
.attrs = &.{
|
|
.{ .name, .strp },
|
|
.{ .type, .ref_addr },
|
|
.{ .const_value, .block },
|
|
.{ .ZIG_comptime_value, .ref_addr },
|
|
},
|
|
},
|
|
.undefined_comptime_value = .{
|
|
.tag = .ZIG_comptime_value,
|
|
.attrs = &.{
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.data2_comptime_value = .{
|
|
.tag = .ZIG_comptime_value,
|
|
.attrs = &.{
|
|
.{ .const_value, .data2 },
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.data4_comptime_value = .{
|
|
.tag = .ZIG_comptime_value,
|
|
.attrs = &.{
|
|
.{ .const_value, .data4 },
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.data8_comptime_value = .{
|
|
.tag = .ZIG_comptime_value,
|
|
.attrs = &.{
|
|
.{ .const_value, .data8 },
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.data16_comptime_value = .{
|
|
.tag = .ZIG_comptime_value,
|
|
.attrs = &.{
|
|
.{ .const_value, .data16 },
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.sdata_comptime_value = .{
|
|
.tag = .ZIG_comptime_value,
|
|
.attrs = &.{
|
|
.{ .const_value, .sdata },
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.udata_comptime_value = .{
|
|
.tag = .ZIG_comptime_value,
|
|
.attrs = &.{
|
|
.{ .const_value, .udata },
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.block_comptime_value = .{
|
|
.tag = .ZIG_comptime_value,
|
|
.attrs = &.{
|
|
.{ .const_value, .block },
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.string_comptime_value = .{
|
|
.tag = .ZIG_comptime_value,
|
|
.attrs = &.{
|
|
.{ .const_value, .strp },
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.location_comptime_value = .{
|
|
.tag = .ZIG_comptime_value,
|
|
.attrs = &.{
|
|
.{ .location, .exprloc },
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.aggregate_comptime_value = .{
|
|
.tag = .ZIG_comptime_value,
|
|
.children = true,
|
|
.attrs = &.{
|
|
.{ .type, .ref_addr },
|
|
},
|
|
},
|
|
.comptime_value_field_runtime_bits = .{
|
|
.tag = .member,
|
|
.attrs = &.{
|
|
.{ .name, .strp },
|
|
.{ .const_value, .block },
|
|
},
|
|
},
|
|
.comptime_value_field_comptime_state = .{
|
|
.tag = .member,
|
|
.attrs = &.{
|
|
.{ .name, .strp },
|
|
.{ .ZIG_comptime_value, .ref_addr },
|
|
},
|
|
},
|
|
.comptime_value_elem_runtime_bits = .{
|
|
.tag = .member,
|
|
.attrs = &.{
|
|
.{ .const_value, .block },
|
|
},
|
|
},
|
|
.comptime_value_elem_comptime_state = .{
|
|
.tag = .member,
|
|
.attrs = &.{
|
|
.{ .ZIG_comptime_value, .ref_addr },
|
|
},
|
|
},
|
|
.null = undefined,
|
|
});
|
|
};
|
|
|
|
fn getFile(dwarf: *Dwarf) ?std.fs.File {
|
|
if (dwarf.bin_file.cast(.macho)) |macho_file| if (macho_file.d_sym) |*d_sym| return d_sym.file;
|
|
return dwarf.bin_file.file;
|
|
}
|
|
|
|
fn addCommonEntry(dwarf: *Dwarf, unit: Unit.Index) UpdateError!Entry.Index {
|
|
const entry = try dwarf.debug_aranges.section.getUnit(unit).addEntry(dwarf.gpa);
|
|
assert(try dwarf.debug_frame.section.getUnit(unit).addEntry(dwarf.gpa) == entry);
|
|
assert(try dwarf.debug_info.section.getUnit(unit).addEntry(dwarf.gpa) == entry);
|
|
assert(try dwarf.debug_line.section.getUnit(unit).addEntry(dwarf.gpa) == entry);
|
|
assert(try dwarf.debug_loclists.section.getUnit(unit).addEntry(dwarf.gpa) == entry);
|
|
assert(try dwarf.debug_rnglists.section.getUnit(unit).addEntry(dwarf.gpa) == entry);
|
|
return entry;
|
|
}
|
|
|
|
fn freeCommonEntry(
|
|
dwarf: *Dwarf,
|
|
unit: Unit.Index,
|
|
entry: Entry.Index,
|
|
) (UpdateError || Writer.Error)!void {
|
|
try dwarf.debug_aranges.section.freeEntry(unit, entry, dwarf);
|
|
try dwarf.debug_frame.section.freeEntry(unit, entry, dwarf);
|
|
try dwarf.debug_info.section.freeEntry(unit, entry, dwarf);
|
|
try dwarf.debug_line.section.freeEntry(unit, entry, dwarf);
|
|
try dwarf.debug_loclists.section.freeEntry(unit, entry, dwarf);
|
|
try dwarf.debug_rnglists.section.freeEntry(unit, entry, dwarf);
|
|
}
|
|
|
|
fn writeInt(dwarf: *Dwarf, buf: []u8, int: u64) void {
|
|
switch (buf.len) {
|
|
inline 0...8 => |len| std.mem.writeInt(@Type(.{ .int = .{
|
|
.signedness = .unsigned,
|
|
.bits = len * 8,
|
|
} }), buf[0..len], @intCast(int), dwarf.endian),
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
fn resolveReloc(dwarf: *Dwarf, source: u64, target: u64, size: u32) RelocError!void {
|
|
var buf: [8]u8 = undefined;
|
|
dwarf.writeInt(buf[0..size], target);
|
|
try dwarf.getFile().?.pwriteAll(buf[0..size], source);
|
|
}
|
|
|
|
fn unitLengthBytes(dwarf: *Dwarf) u32 {
|
|
return switch (dwarf.format) {
|
|
.@"32" => 4,
|
|
.@"64" => 4 + 8,
|
|
};
|
|
}
|
|
|
|
fn sectionOffsetBytes(dwarf: *Dwarf) u32 {
|
|
return switch (dwarf.format) {
|
|
.@"32" => 4,
|
|
.@"64" => 8,
|
|
};
|
|
}
|
|
|
|
fn uleb128Bytes(value: anytype) u32 {
|
|
var buf: [64]u8 = undefined;
|
|
var dw: Writer.Discarding = .init(&buf);
|
|
dw.writer.writeUleb128(value) catch unreachable;
|
|
return @intCast(dw.count + dw.writer.end);
|
|
}
|
|
|
|
fn sleb128Bytes(value: anytype) u32 {
|
|
var buf: [64]u8 = undefined;
|
|
var dw: Writer.Discarding = .init(&buf);
|
|
dw.writer.writeSleb128(value) catch unreachable;
|
|
return @intCast(dw.count + dw.writer.end);
|
|
}
|
|
|
|
/// overrides `-fno-incremental` for testing incremental debug info until `-fincremental` is functional
|
|
const force_incremental = false;
|
|
inline fn incremental(dwarf: Dwarf) bool {
|
|
return force_incremental or dwarf.bin_file.comp.config.incremental;
|
|
}
|
|
|
|
const Allocator = std.mem.Allocator;
|
|
const DW = std.dwarf;
|
|
const Dwarf = @This();
|
|
const InternPool = @import("../InternPool.zig");
|
|
const Module = @import("../Package.zig").Module;
|
|
const Type = @import("../Type.zig");
|
|
const Value = @import("../Value.zig");
|
|
const Zcu = @import("../Zcu.zig");
|
|
const Zir = std.zig.Zir;
|
|
const assert = std.debug.assert;
|
|
const codegen = @import("../codegen.zig");
|
|
const dev = @import("../dev.zig");
|
|
const link = @import("../link.zig");
|
|
const log = std.log.scoped(.dwarf);
|
|
const std = @import("std");
|
|
const target_info = @import("../target.zig");
|
|
const Writer = std.Io.Writer;
|