mirror of
https://github.com/ziglang/zig.git
synced 2026-02-09 19:10:48 +00:00
and rename std.io.BufferedWriter.writableSlice to writableSliceGreedy and make writableSlice and writableArray advance the buffer end position introduce std.io.BufferedWriter.writeSplatLimit but it's unimplemented
6159 lines
259 KiB
Zig
6159 lines
259 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{
|
|
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("../arch/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_br: std.io.BufferedReader = undefined;
|
|
abbrev_code_br.initFixed(&abbrev_code_buf);
|
|
return @enumFromInt(abbrev_code_br.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) !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!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!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!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 bw: std.io.BufferedWriter = undefined;
|
|
bw.initFixed(&buf);
|
|
bw.writeByte(DW.LNS.extended_op) catch unreachable;
|
|
const extended_op_bytes = bw.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 bw.writeLeb128(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((bw.writableSlice(op_len_bytes) catch unreachable), len - extended_op_bytes - op_len_bytes);
|
|
break;
|
|
},
|
|
.gt => op_len_bytes += 1,
|
|
};
|
|
assert(bw.end == extended_op_bytes + op_len_bytes);
|
|
bw.writeByte(DW.LNE.padding) catch unreachable;
|
|
assert(bw.end >= unit.trailer_len and bw.end <= len);
|
|
return dwarf.getFile().?.pwriteAll(bw.getWritten(), sec.off(dwarf) + start);
|
|
}
|
|
var trailer_bw: std.io.BufferedWriter = undefined;
|
|
trailer_bw.initFixed(try dwarf.gpa.alloc(u8, len));
|
|
defer dwarf.gpa.free(trailer_bw.buffer);
|
|
const fill_byte: u8 = if (sec == &dwarf.debug_abbrev.section) fill: {
|
|
assert(uleb128Bytes(@intFromEnum(AbbrevCode.null)) == 1);
|
|
trailer_bw.writeByte(@intFromEnum(AbbrevCode.null)) catch unreachable;
|
|
break :fill @intFromEnum(AbbrevCode.null);
|
|
} else if (sec == &dwarf.debug_aranges.section) fill: {
|
|
trailer_bw.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" => trailer_bw.writeInt(u32, @intCast(unit_len), dwarf.endian) catch unreachable,
|
|
.@"64" => {
|
|
trailer_bw.writeInt(u32, std.math.maxInt(u32), dwarf.endian) catch unreachable;
|
|
trailer_bw.writeInt(u64, unit_len, dwarf.endian) catch unreachable;
|
|
},
|
|
}
|
|
switch (format) {
|
|
.none => unreachable,
|
|
.debug_frame => {
|
|
switch (dwarf.format) {
|
|
.@"32" => trailer_bw.writeInt(u32, std.math.maxInt(u32), dwarf.endian) catch unreachable,
|
|
.@"64" => trailer_bw.writeInt(u64, std.math.maxInt(u64), dwarf.endian) catch unreachable,
|
|
}
|
|
trailer_bw.writeByte(4) catch unreachable;
|
|
trailer_bw.writeAll("\x00") catch unreachable;
|
|
trailer_bw.writeByte(@intFromEnum(dwarf.address_size)) catch unreachable;
|
|
trailer_bw.writeByte(0) catch unreachable;
|
|
},
|
|
.eh_frame => {
|
|
trailer_bw.writeInt(u32, 0, dwarf.endian) catch unreachable;
|
|
trailer_bw.writeByte(1) catch unreachable;
|
|
trailer_bw.writeAll("\x00") catch unreachable;
|
|
},
|
|
}
|
|
trailer_bw.writeUleb128(1) catch unreachable;
|
|
trailer_bw.writeSleb128(1) catch unreachable;
|
|
trailer_bw.writeUleb128(0) catch unreachable;
|
|
},
|
|
}
|
|
trailer_bw.splatByteAll(DW.CFA.nop, unit.trailer_len - trailer_bw.end) catch unreachable;
|
|
break :fill DW.CFA.nop;
|
|
} else if (sec == &dwarf.debug_info.section) fill: {
|
|
assert(uleb128Bytes(@intFromEnum(AbbrevCode.null)) == 1);
|
|
trailer_bw.splatByteAll(@intFromEnum(AbbrevCode.null), 2) catch unreachable;
|
|
break :fill @intFromEnum(AbbrevCode.null);
|
|
} else if (sec == &dwarf.debug_rnglists.section) fill: {
|
|
trailer_bw.writeByte(DW.RLE.end_of_list) catch unreachable;
|
|
break :fill DW.RLE.end_of_list;
|
|
} else unreachable;
|
|
assert(trailer_bw.end == unit.trailer_len);
|
|
trailer_bw.splatByteAll(fill_byte, len - unit.trailer_len) catch unreachable;
|
|
assert(trailer_bw.end == len);
|
|
try dwarf.getFile().?.pwriteAll(trailer_bw.buffer, 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!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: [8]u8 = undefined;
|
|
dwarf.writeInt(unit_len[0..dwarf.sectionOffsetBytes()], len - dwarf.unitLengthBytes());
|
|
try dwarf.getFile().?.pwriteAll(
|
|
unit_len[0..dwarf.sectionOffsetBytes()],
|
|
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 bw: std.io.BufferedWriter = undefined;
|
|
bw.initFixed(&buf);
|
|
if (sec == &dwarf.debug_info.section) switch (len) {
|
|
0 => {},
|
|
1 => bw.writeLeb128(try dwarf.refAbbrevCode(.pad_1)) catch unreachable,
|
|
else => {
|
|
bw.writeLeb128(try dwarf.refAbbrevCode(.pad_n)) catch unreachable;
|
|
const abbrev_code_bytes = bw.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 bw.writeLeb128(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((try bw.writableSlice(block_len_bytes)), len - abbrev_code_bytes - block_len_bytes);
|
|
break;
|
|
},
|
|
.gt => block_len_bytes += 1,
|
|
};
|
|
assert(bw.end == abbrev_code_bytes + block_len_bytes);
|
|
},
|
|
} else if (sec == &dwarf.debug_line.section) switch (len) {
|
|
0 => {},
|
|
1 => bw.writeByte(DW.LNS.const_add_pc) catch unreachable,
|
|
else => {
|
|
bw.writeByte(DW.LNS.extended_op) catch unreachable;
|
|
const extended_op_bytes = bw.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 bw.writeLeb128(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(
|
|
(bw.writableSlice(op_len_bytes) catch unreachable),
|
|
len - extended_op_bytes - op_len_bytes,
|
|
);
|
|
break;
|
|
},
|
|
.gt => op_len_bytes += 1,
|
|
};
|
|
assert(bw.end == extended_op_bytes + op_len_bytes);
|
|
if (len > 2) bw.writeByte(DW.LNE.padding) catch unreachable;
|
|
},
|
|
} else assert(!sec.pad_entries_to_ideal and len == 0);
|
|
assert(bw.end <= len);
|
|
try dwarf.getFile().?.pwriteAll(bw.getWritten(), sec.off(dwarf) + unit.off + unit.header_len + start);
|
|
}
|
|
|
|
fn resize(entry_ptr: *Entry, unit: *Unit, sec: *Section, dwarf: *Dwarf, len: u32) UpdateError!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(bw: *std.io.BufferedWriter, reg: u32, op0: u8, opx: u8) std.io.Writer.Error!void {
|
|
if (std.math.cast(u5, reg)) |small_reg| {
|
|
try bw.writeByte(op0 + small_reg);
|
|
} else {
|
|
try bw.writeByte(opx);
|
|
try bw.writeLeb128(reg);
|
|
}
|
|
}
|
|
|
|
fn write(loc: Loc, bw: *std.io.BufferedWriter, adapter: anytype) UpdateError!void {
|
|
switch (loc) {
|
|
.empty => {},
|
|
.addr_reloc => |sym_index| {
|
|
try bw.writeByte(DW.OP.addr);
|
|
try adapter.addrSym(sym_index);
|
|
},
|
|
.deref => |addr| {
|
|
try addr.write(adapter);
|
|
try bw.writeByte(DW.OP.deref);
|
|
},
|
|
.constu => |constu| if (std.math.cast(u5, constu)) |lit| {
|
|
try bw.writeByte(@as(u8, DW.OP.lit0) + lit);
|
|
} else if (std.math.cast(u8, constu)) |const1u| {
|
|
try bw.writeAll(&.{ DW.OP.const1u, const1u });
|
|
} else if (std.math.cast(u16, constu)) |const2u| {
|
|
try bw.writeByte(DW.OP.const2u);
|
|
try bw.writeInt(u16, const2u, adapter.endian());
|
|
} else if (std.math.cast(u21, constu)) |const3u| {
|
|
try bw.writeByte(DW.OP.constu);
|
|
try bw.writeLeb128(const3u);
|
|
} else if (std.math.cast(u32, constu)) |const4u| {
|
|
try bw.writeByte(DW.OP.const4u);
|
|
try bw.writeInt(u32, const4u, adapter.endian());
|
|
} else if (std.math.cast(u49, constu)) |const7u| {
|
|
try bw.writeByte(DW.OP.constu);
|
|
try bw.writeLeb128(const7u);
|
|
} else {
|
|
try bw.writeByte(DW.OP.const8u);
|
|
try bw.writeInt(u64, constu, adapter.endian());
|
|
},
|
|
.consts => |consts| if (std.math.cast(i8, consts)) |const1s| {
|
|
try bw.writeAll(&.{ DW.OP.const1s, @bitCast(const1s) });
|
|
} else if (std.math.cast(i16, consts)) |const2s| {
|
|
try bw.writeByte(DW.OP.const2s);
|
|
try bw.writeInt(i16, const2s, adapter.endian());
|
|
} else if (std.math.cast(i21, consts)) |const3s| {
|
|
try bw.writeByte(DW.OP.consts);
|
|
try bw.writeLeb128(const3s);
|
|
} else if (std.math.cast(i32, consts)) |const4s| {
|
|
try bw.writeByte(DW.OP.const4s);
|
|
try bw.writeInt(i32, const4s, adapter.endian());
|
|
} else if (std.math.cast(i49, consts)) |const7s| {
|
|
try bw.writeByte(DW.OP.consts);
|
|
try bw.writeLeb128(const7s);
|
|
} else {
|
|
try bw.writeByte(DW.OP.const8s);
|
|
try bw.writeInt(i64, consts, adapter.endian());
|
|
},
|
|
.plus => |plus| done: {
|
|
if (plus[0].getConst(u0)) |_| {
|
|
try plus[1].write(bw, adapter);
|
|
break :done;
|
|
}
|
|
if (plus[1].getConst(u0)) |_| {
|
|
try plus[0].write(bw, adapter);
|
|
break :done;
|
|
}
|
|
if (plus[0].getBaseReg()) |breg| {
|
|
if (plus[1].getConst(i65)) |offset| {
|
|
try writeReg(bw, breg, DW.OP.breg0, DW.OP.bregx);
|
|
try bw.writeLeb128(offset);
|
|
break :done;
|
|
}
|
|
}
|
|
if (plus[1].getBaseReg()) |breg| {
|
|
if (plus[0].getConst(i65)) |offset| {
|
|
try writeReg(bw, breg, DW.OP.breg0, DW.OP.bregx);
|
|
try bw.writeLeb128(offset);
|
|
break :done;
|
|
}
|
|
}
|
|
if (plus[0].getConst(u64)) |uconst| {
|
|
try plus[1].write(bw, adapter);
|
|
try bw.writeByte(DW.OP.plus_uconst);
|
|
try bw.writeLeb128(uconst);
|
|
break :done;
|
|
}
|
|
if (plus[1].getConst(u64)) |uconst| {
|
|
try plus[0].write(bw, adapter);
|
|
try bw.writeByte(DW.OP.plus_uconst);
|
|
try bw.writeLeb128(uconst);
|
|
break :done;
|
|
}
|
|
try plus[0].write(bw, adapter);
|
|
try plus[1].write(bw, adapter);
|
|
try bw.writeByte(DW.OP.plus);
|
|
},
|
|
.reg => |reg| try writeReg(bw, reg, DW.OP.reg0, DW.OP.regx),
|
|
.breg => |breg| {
|
|
try writeReg(bw, breg, DW.OP.breg0, DW.OP.bregx);
|
|
try bw.writeSleb128(0);
|
|
},
|
|
.push_object_address => try bw.writeByte(DW.OP.push_object_address),
|
|
.call => |call| {
|
|
for (call.args) |arg| try arg.write(adapter);
|
|
try bw.writeByte(DW.OP.call_ref);
|
|
try adapter.infoEntry(call.unit, call.entry);
|
|
},
|
|
.form_tls_address => |addr| {
|
|
try addr.write(bw, adapter);
|
|
try bw.writeByte(DW.OP.form_tls_address);
|
|
},
|
|
.implicit_value => |value| {
|
|
try bw.writeByte(DW.OP.implicit_value);
|
|
try bw.writeLeb128(value.len);
|
|
try bw.writeAll(value);
|
|
},
|
|
.stack_value => |value| {
|
|
try value.write(bw, adapter);
|
|
try bw.writeByte(DW.OP.stack_value);
|
|
},
|
|
.implicit_pointer => |implicit_pointer| {
|
|
try bw.writeByte(DW.OP.implicit_pointer);
|
|
try adapter.infoEntry(bw, implicit_pointer.unit, implicit_pointer.entry);
|
|
try bw.writeLeb128(implicit_pointer.offset);
|
|
},
|
|
.wasm_ext => |wasm_ext| {
|
|
try bw.writeByte(DW.OP.WASM_location);
|
|
switch (wasm_ext) {
|
|
.local => |local| {
|
|
try bw.writeByte(DW.OP.WASM_local);
|
|
try bw.writeLeb128(local);
|
|
},
|
|
.global => |global| if (std.math.cast(u21, global)) |global_u21| {
|
|
try bw.writeByte(DW.OP.WASM_global);
|
|
try bw.writeLeb128(global_u21);
|
|
} else {
|
|
try bw.writeByte(DW.OP.WASM_global_u32);
|
|
try bw.writeInt(u32, global, adapter.endian());
|
|
},
|
|
.operand_stack => |operand_stack| {
|
|
try bw.writeByte(DW.OP.WASM_operand_stack);
|
|
try bw.writeLeb128(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!void {
|
|
const bw = &wip_nav.debug_frame.buffered_writer;
|
|
switch (cfa) {
|
|
.nop => try bw.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 bw.writeByte(@as(u8, DW.CFA.advance_loc) + small_delta)
|
|
else if (std.math.cast(u8, delta)) |ubyte_delta|
|
|
try bw.writeAll(&.{ DW.CFA.advance_loc1, ubyte_delta })
|
|
else if (std.math.cast(u16, delta)) |uhalf_delta| {
|
|
try bw.writeByte(DW.CFA.advance_loc2);
|
|
try bw.writeInt(u16, uhalf_delta, wip_nav.dwarf.endian);
|
|
} else if (std.math.cast(u32, delta)) |uword_delta| {
|
|
try bw.writeByte(DW.CFA.advance_loc4);
|
|
try bw.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 bw.writeByte(@as(u8, DW.CFA.offset) + small_reg);
|
|
} else {
|
|
try bw.writeByte(DW.CFA.offset_extended);
|
|
try bw.writeLeb128(reg_off.reg);
|
|
}
|
|
try bw.writeLeb128(unsigned_off);
|
|
} else {
|
|
try bw.writeByte(DW.CFA.offset_extended_sf);
|
|
try bw.writeLeb128(reg_off.reg);
|
|
try bw.writeLeb128(factored_off);
|
|
}
|
|
},
|
|
.restore => |reg| if (std.math.cast(u6, reg)) |small_reg|
|
|
try bw.writeByte(@as(u8, DW.CFA.restore) + small_reg)
|
|
else {
|
|
try bw.writeByte(DW.CFA.restore_extended);
|
|
try bw.writeLeb128(reg);
|
|
},
|
|
.undefined => |reg| {
|
|
try bw.writeByte(DW.CFA.undefined);
|
|
try bw.writeLeb128(reg);
|
|
},
|
|
.same_value => |reg| {
|
|
try bw.writeByte(DW.CFA.same_value);
|
|
try bw.writeLeb128(reg);
|
|
},
|
|
.register => |regs| if (regs[0] != regs[1]) {
|
|
try bw.writeByte(DW.CFA.register);
|
|
for (regs) |reg| try bw.writeLeb128(reg);
|
|
} else {
|
|
try bw.writeByte(DW.CFA.same_value);
|
|
try bw.writeLeb128(regs[0]);
|
|
},
|
|
.remember_state => try bw.writeByte(DW.CFA.remember_state),
|
|
.restore_state => try bw.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 bw.writeByte(DW.CFA.def_cfa_register);
|
|
try bw.writeLeb128(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 bw.writeByte(if (changed_reg) DW.CFA.def_cfa else DW.CFA.def_cfa_offset);
|
|
if (changed_reg) try bw.writeLeb128(reg_off.reg);
|
|
try bw.writeLeb128(unsigned_off.?);
|
|
} else {
|
|
try bw.writeByte(if (changed_reg) DW.CFA.def_cfa_sf else DW.CFA.def_cfa_offset_sf);
|
|
if (changed_reg) try bw.writeLeb128(reg_off.reg);
|
|
try bw.writeLeb128(@divExact(reg_off.off, wip_nav.dwarf.debug_frame.header.data_alignment_factor));
|
|
}
|
|
wip_nav.cfi.cfa = reg_off;
|
|
},
|
|
.def_cfa_expression => |expr| {
|
|
try bw.writeByte(DW.CFA.def_cfa_expression);
|
|
try wip_nav.frameExprLoc(expr);
|
|
},
|
|
.expression => |reg_expr| {
|
|
try bw.writeByte(DW.CFA.expression);
|
|
try bw.writeLeb128(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 bw.writeByte(DW.CFA.val_offset);
|
|
try bw.writeLeb128(reg_off.reg);
|
|
try bw.writeLeb128(unsigned_off);
|
|
} else {
|
|
try bw.writeByte(DW.CFA.val_offset_sf);
|
|
try bw.writeLeb128(reg_off.reg);
|
|
try bw.writeLeb128(factored_off);
|
|
}
|
|
},
|
|
.val_expression => |reg_expr| {
|
|
try bw.writeByte(DW.CFA.val_expression);
|
|
try bw.writeLeb128(reg_expr.reg);
|
|
try wip_nav.frameExprLoc(reg_expr.expr);
|
|
},
|
|
.escape => |bytes| try bw.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: std.io.AllocatingWriter,
|
|
debug_info: std.io.AllocatingWriter,
|
|
debug_line: std.io.AllocatingWriter,
|
|
debug_loclists: std.io.AllocatingWriter,
|
|
pending_lazy: PendingLazy,
|
|
|
|
pub fn init(wip_nav: *WipNav) void {
|
|
const gpa = wip_nav.dwarf.gpa;
|
|
wip_nav.debug_frame.init(gpa);
|
|
wip_nav.debug_info.init(gpa);
|
|
wip_nav.debug_line.init(gpa);
|
|
wip_nav.debug_loclists.init(gpa);
|
|
}
|
|
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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,
|
|
) error{OutOfMemory}!void {
|
|
const dlbw = &wip_nav.debug_line.buffered_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 dlbw.writeByte(DW.LNS.advance_line);
|
|
try dlbw.writeLeb128(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 dlbw.writeByte(DW.LNS.advance_pc);
|
|
try dlbw.writeLeb128(op_advance);
|
|
break :remaining 0;
|
|
} else if (op_advance >= max_op_advance) remaining: {
|
|
try dlbw.writeByte(DW.LNS.const_add_pc);
|
|
break :remaining op_advance - max_op_advance;
|
|
} else op_advance);
|
|
|
|
try dlbw.writeByte(
|
|
if (remaining_delta_line == 0 and remaining_op_advance == 0)
|
|
DW.LNS.copy
|
|
else
|
|
@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 {
|
|
const dlbw = &wip_nav.debug_line.buffered_writer;
|
|
try dlbw.writeByte(DW.LNS.set_column);
|
|
try dlbw.writeLeb128(column + 1);
|
|
}
|
|
|
|
pub fn negateStmt(wip_nav: *WipNav) Allocator.Error!void {
|
|
return wip_nav.debug_line.buffered_writer.writeByte(DW.LNS.negate_stmt);
|
|
}
|
|
|
|
pub fn setPrologueEnd(wip_nav: *WipNav) Allocator.Error!void {
|
|
return wip_nav.debug_line.buffered_writer.writeByte(DW.LNS.set_prologue_end);
|
|
}
|
|
|
|
pub fn setEpilogueBegin(wip_nav: *WipNav) Allocator.Error!void {
|
|
return wip_nav.debug_line.buffered_writer.writeByte(DW.LNS.set_epilogue_begin);
|
|
}
|
|
|
|
pub fn enterBlock(wip_nav: *WipNav, code_off: u64) UpdateError!void {
|
|
const dwarf = wip_nav.dwarf;
|
|
const dibw = &wip_nav.debug_info.buffered_writer;
|
|
const block = try wip_nav.blocks.addOne(dwarf.gpa);
|
|
|
|
block.abbrev_code = @intCast(dibw.count);
|
|
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(dibw.count);
|
|
try dibw.writeInt(u32, 0, dwarf.endian);
|
|
wip_nav.any_children = false;
|
|
}
|
|
|
|
pub fn leaveBlock(wip_nav: *WipNav, code_off: u64) UpdateError!void {
|
|
const block_bytes = comptime uleb128Bytes(@intFromEnum(AbbrevCode.block));
|
|
const block = wip_nav.blocks.pop().?;
|
|
const dib = wip_nav.debug_info.getWritten();
|
|
if (wip_nav.any_children)
|
|
try wip_nav.debug_info.buffered_writer.writeLeb128(@intFromEnum(AbbrevCode.null))
|
|
else
|
|
std.leb.writeUnsignedFixed(
|
|
block_bytes,
|
|
dib[block.abbrev_code..][0..block_bytes],
|
|
try wip_nav.dwarf.refAbbrevCode(.empty_block),
|
|
);
|
|
std.mem.writeInt(u32, dib[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 {
|
|
const dwarf = wip_nav.dwarf;
|
|
const zcu = wip_nav.pt.zcu;
|
|
const dibw = &wip_nav.debug_info.buffered_writer;
|
|
const block = try wip_nav.blocks.addOne(dwarf.gpa);
|
|
|
|
block.abbrev_code = @intCast(dibw.count);
|
|
try wip_nav.abbrevCode(.inlined_func);
|
|
try wip_nav.refNav(zcu.funcInfo(func).owner_nav);
|
|
try dibw.writeLeb128(zcu.navSrcLine(zcu.funcInfo(wip_nav.func).owner_nav) + line + 1);
|
|
try dibw.writeLeb128(column + 1);
|
|
block.low_pc_off = code_off;
|
|
try wip_nav.infoAddrSym(wip_nav.func_sym_index, code_off);
|
|
block.high_pc = @intCast(dibw.count);
|
|
try dibw.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 {
|
|
const inlined_func_bytes = comptime uleb128Bytes(@intFromEnum(AbbrevCode.inlined_func));
|
|
const block = wip_nav.blocks.pop().?;
|
|
const dib = wip_nav.debug_info.getWritten();
|
|
if (wip_nav.any_children)
|
|
try wip_nav.debug_info.buffered_writer.writeLeb128(@intFromEnum(AbbrevCode.null))
|
|
else
|
|
std.leb.writeUnsignedFixed(
|
|
inlined_func_bytes,
|
|
dib[block.abbrev_code..][0..inlined_func_bytes],
|
|
try wip_nav.dwarf.refAbbrevCode(.empty_inlined_func),
|
|
);
|
|
std.mem.writeInt(u32, dib[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 {
|
|
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 dlbw = &wip_nav.debug_line.buffered_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 dlbw.writeByte(DW.LNS.extended_op);
|
|
try dlbw.writeLeb128(1 + dwarf.sectionOffsetBytes());
|
|
try dlbw.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(dlbw.count),
|
|
.target_sec = .debug_info,
|
|
.target_unit = new_unit,
|
|
.target_entry = new_nav_gop.value_ptr.toOptional(),
|
|
});
|
|
try dlbw.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 dlbw.writeByte(DW.LNS.set_file);
|
|
try dlbw.writeLeb128(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 dlbw.writeByte(DW.LNS.advance_line);
|
|
try dlbw.writeLeb128(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!void {
|
|
try wip_nav.debug_info.buffered_writer.writeLeb128(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!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 bw = &@field(wip_nav, @tagName(sec)).buffered_writer;
|
|
const source_off: u32 = @intCast(bw.count);
|
|
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 bw.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!void {
|
|
try wip_nav.sectionOffset(.debug_info, target_sec, target_unit, target_entry, target_off);
|
|
}
|
|
|
|
fn strp(wip_nav: *WipNav, str: []const u8) UpdateError!void {
|
|
try wip_nav.infoSectionOffset(.debug_str, StringSection.unit, try wip_nav.dwarf.debug_str.addString(wip_nav.dwarf, str), 0);
|
|
}
|
|
|
|
const ExprLocCounter = struct {
|
|
section_offset_bytes: u32,
|
|
address_size: AddressSize,
|
|
fn init(dwarf: *Dwarf) ExprLocCounter {
|
|
return .{
|
|
.section_offset_bytes = dwarf.sectionOffsetBytes(),
|
|
.address_size = dwarf.address_size,
|
|
};
|
|
}
|
|
fn endian(_: ExprLocCounter) std.builtin.Endian {
|
|
return @import("builtin").cpu.arch.endian();
|
|
}
|
|
fn addrSym(counter: ExprLocCounter, bw: *std.io.BufferedWriter, _: u32) error{}!void {
|
|
bw.count += @intFromEnum(counter.address_size);
|
|
}
|
|
fn infoEntry(counter: ExprLocCounter, bw: *std.io.BufferedWriter, _: Unit.Index, _: Entry.Index) error{}!void {
|
|
bw.count += counter.section_offset_bytes;
|
|
}
|
|
};
|
|
|
|
fn infoExprLoc(wip_nav: *WipNav, loc: Loc) UpdateError!void {
|
|
const bw = &wip_nav.debug_info.buffered_writer;
|
|
const counter: ExprLocCounter = .init(wip_nav.dwarf);
|
|
const start = bw.count;
|
|
try loc.write(bw, counter);
|
|
const len = bw.count - start;
|
|
bw.count = start;
|
|
wip_nav.debug_info.shrinkRetainingCapacity(start);
|
|
|
|
const adapter: struct {
|
|
wip_nav: *WipNav,
|
|
fn endian(ctx: @This()) std.builtin.Endian {
|
|
return ctx.wip_nav.dwarf.endian;
|
|
}
|
|
fn addrSym(ctx: @This(), _: *std.io.BufferedWriter, sym_index: u32) UpdateError!void {
|
|
try ctx.wip_nav.infoAddrSym(sym_index, 0);
|
|
}
|
|
fn infoEntry(ctx: @This(), _: *std.io.BufferedWriter, unit: Unit.Index, entry: Entry.Index) UpdateError!void {
|
|
try ctx.wip_nav.infoSectionOffset(.debug_info, unit, entry, 0);
|
|
}
|
|
} = .{ .wip_nav = wip_nav };
|
|
try bw.writeLeb128(len);
|
|
try loc.write(bw, adapter);
|
|
}
|
|
|
|
fn infoAddrSym(wip_nav: *WipNav, sym_index: u32, sym_off: u64) UpdateError!void {
|
|
const dibw = &wip_nav.debug_info.buffered_writer;
|
|
try wip_nav.infoExternalReloc(.{
|
|
.source_off = @intCast(dibw.count),
|
|
.target_sym = sym_index,
|
|
.target_off = sym_off,
|
|
});
|
|
try dibw.splatByteAll(0, @intFromEnum(wip_nav.dwarf.address_size));
|
|
}
|
|
|
|
fn frameExprLoc(wip_nav: *WipNav, loc: Loc) UpdateError!void {
|
|
const bw = &wip_nav.debug_frame.buffered_writer;
|
|
const counter: ExprLocCounter = .init(wip_nav.dwarf);
|
|
const start = bw.count;
|
|
try loc.write(bw, counter);
|
|
const len = bw.count - start;
|
|
bw.count = start;
|
|
wip_nav.debug_frame.shrinkRetainingCapacity(start);
|
|
|
|
const adapter: struct {
|
|
wip_nav: *WipNav,
|
|
fn endian(ctx: @This()) std.builtin.Endian {
|
|
return ctx.wip_nav.dwarf.endian;
|
|
}
|
|
fn addrSym(ctx: @This(), _: *std.io.BufferedWriter, sym_index: u32) UpdateError!void {
|
|
try ctx.wip_nav.frameAddrSym(sym_index, 0);
|
|
}
|
|
fn infoEntry(ctx: @This(), _: *std.io.BufferedWriter, unit: Unit.Index, entry: Entry.Index) UpdateError!void {
|
|
try ctx.wip_nav.sectionOffset(.debug_frame, .debug_info, unit, entry, 0);
|
|
}
|
|
} = .{ .wip_nav = wip_nav };
|
|
try bw.writeLeb128(len);
|
|
try loc.write(bw, adapter);
|
|
}
|
|
|
|
fn frameAddrSym(wip_nav: *WipNav, sym_index: u32, sym_off: u64) UpdateError!void {
|
|
const dfbw = &wip_nav.debug_frame.buffered_writer;
|
|
try wip_nav.frameExternalReloc(.{
|
|
.source_off = @intCast(dfbw.count),
|
|
.target_sym = sym_index,
|
|
.target_off = sym_off,
|
|
});
|
|
try dfbw.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!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!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(zcu.funcInfo(value.toIntern()).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!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!u32 {
|
|
const dwarf = wip_nav.dwarf;
|
|
const dibw = &wip_nav.debug_info.buffered_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(dibw.count),
|
|
.target_entry = undefined,
|
|
.target_off = undefined,
|
|
});
|
|
try dibw.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.buffered_writer.count);
|
|
}
|
|
|
|
fn blockValue(wip_nav: *WipNav, src_loc: Zcu.LazySrcLoc, val: Value) UpdateError!void {
|
|
const ty = val.typeOf(wip_nav.pt.zcu);
|
|
const dibw = &wip_nav.debug_info.buffered_writer;
|
|
const bytes = if (ty.hasRuntimeBits(wip_nav.pt.zcu)) ty.abiSize(wip_nav.pt.zcu) else 0;
|
|
try dibw.writeLeb128(bytes);
|
|
if (bytes == 0) return;
|
|
var dial = wip_nav.debug_info.toArrayList();
|
|
defer _ = wip_nav.debug_info.fromArrayList(wip_nav.dwarf.gpa, &dial);
|
|
const old_len = dial.items.len;
|
|
try codegen.generateSymbol(
|
|
wip_nav.dwarf.bin_file,
|
|
wip_nav.pt,
|
|
src_loc,
|
|
val,
|
|
&dial,
|
|
.{ .debug_output = .{ .dwarf = wip_nav } },
|
|
);
|
|
if (old_len + bytes != wip_nav.debug_info.items.len) {
|
|
std.debug.print("{f} [{}]: {} != {}\n", .{ ty.fmt(wip_nav.pt), ty.toIntern(), bytes, wip_nav.debug_info.items.len - old_len });
|
|
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!void {
|
|
const zcu = wip_nav.pt.zcu;
|
|
const dibw = &wip_nav.debug_info.buffered_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 dibw.writableSliceGreedy(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;
|
|
dibw.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 dibw.writeLeb128(bytes);
|
|
big_int.writeTwosComplement(
|
|
try dibw.writableSliceGreedy(@intCast(bytes)),
|
|
wip_nav.dwarf.endian,
|
|
);
|
|
dibw.advance(@intCast(bytes));
|
|
}
|
|
}
|
|
|
|
fn enumConstValue(
|
|
wip_nav: *WipNav,
|
|
loaded_enum: InternPool.LoadedEnumType,
|
|
abbrev_code: AbbrevCodeForForm,
|
|
field_index: usize,
|
|
) UpdateError!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!void {
|
|
const zcu = wip_nav.pt.zcu;
|
|
const ip = &zcu.intern_pool;
|
|
const dwarf = wip_nav.dwarf;
|
|
const dibw = &wip_nav.debug_info.buffered_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,
|
|
=> false,
|
|
.generic_decl_var,
|
|
.generic_decl_const,
|
|
.generic_decl_func,
|
|
=> true,
|
|
else => unreachable,
|
|
};
|
|
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(dibw.count == DebugInfo.declEntryLineOff(dwarf));
|
|
try dibw.writeInt(u32, decl.src_line + 1, dwarf.endian);
|
|
try dibw.writeLeb128(decl.src_column + 1);
|
|
try dibw.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.getWritten());
|
|
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!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("../arch/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 dwarf.initWipNavInner(pt, nav_index, sym_index) catch |err| switch (err) {
|
|
error.OutOfMemory => return error.OutOfMemory,
|
|
else => |e| return pt.zcu.codegenFail(nav_index, "failed to init dwarf nav: {s}", .{@errorName(e)}),
|
|
};
|
|
}
|
|
|
|
pub fn finishWipNavFunc(
|
|
dwarf: *Dwarf,
|
|
pt: Zcu.PerThread,
|
|
nav_index: InternPool.Nav.Index,
|
|
code_size: u64,
|
|
wip_nav: *WipNav,
|
|
) error{ OutOfMemory, CodegenFail }!void {
|
|
return dwarf.finishWipNavFuncInner(pt, nav_index, code_size, wip_nav) catch |err| switch (err) {
|
|
error.OutOfMemory => return error.OutOfMemory,
|
|
else => |e| return pt.zcu.codegenFail(nav_index, "failed to finish dwarf func nav: {s}", .{@errorName(e)}),
|
|
};
|
|
}
|
|
|
|
pub fn finishWipNav(
|
|
dwarf: *Dwarf,
|
|
pt: Zcu.PerThread,
|
|
nav_index: InternPool.Nav.Index,
|
|
wip_nav: *WipNav,
|
|
) error{ OutOfMemory, CodegenFail }!void {
|
|
return dwarf.finishWipNavInner(pt, nav_index, wip_nav) catch |err| switch (err) {
|
|
error.OutOfMemory => return error.OutOfMemory,
|
|
else => |e| return pt.zcu.codegenFail(nav_index, "failed to finish dwarf nav: {s}", .{@errorName(e)}),
|
|
};
|
|
}
|
|
|
|
pub fn updateComptimeNav(
|
|
dwarf: *Dwarf,
|
|
pt: Zcu.PerThread,
|
|
nav_index: InternPool.Nav.Index,
|
|
) error{ OutOfMemory, CodegenFail }!void {
|
|
return dwarf.updateComptimeNavInner(pt, nav_index) catch |err| switch (err) {
|
|
error.OutOfMemory => return error.OutOfMemory,
|
|
else => |e| return pt.zcu.codegenFail(nav_index, "failed to update dwarf comptime nav: {s}", .{@errorName(e)}),
|
|
};
|
|
}
|
|
|
|
pub fn updateContainerType(
|
|
dwarf: *Dwarf,
|
|
pt: Zcu.PerThread,
|
|
type_index: InternPool.Index,
|
|
) error{ OutOfMemory, CodegenFail }!void {
|
|
return dwarf.updateContainerType(pt, type_index) catch |err| switch (err) {
|
|
error.OutOfMemory => return error.OutOfMemory,
|
|
else => |e| return pt.zcu.codegenFailType(type_index, "failed to update dwarf comptime nav: {s}", .{@errorName(e)}),
|
|
};
|
|
}
|
|
|
|
pub fn flushModule(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void {
|
|
return dwarf.flushModuleInner(pt);
|
|
}
|
|
|
|
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 nav_val = zcu.navValue(nav_index);
|
|
const nav_key = ip.indexToKey(nav_val.toIntern());
|
|
switch (nav_key) {
|
|
// Ignore @extern
|
|
.@"extern" => |@"extern"| if (decl.linkage != .@"extern" or
|
|
!@"extern".name.eqlSlice(file.zir.?.nullTerminatedString(decl.name), ip)) return null,
|
|
else => {},
|
|
}
|
|
|
|
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 = undefined,
|
|
.debug_info = undefined,
|
|
.debug_line = undefined,
|
|
.debug_loclists = undefined,
|
|
.pending_lazy = .empty,
|
|
};
|
|
errdefer wip_nav.deinit();
|
|
|
|
switch (nav_key) {
|
|
else => {
|
|
const dibw = &wip_nav.debug_info.buffered_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(nav.fqn.toSlice(ip));
|
|
const ty: Type = nav_val.typeOf(zcu);
|
|
const addr: Loc = .{ .addr_reloc = sym_index };
|
|
const loc: Loc = if (decl.is_threadlocal) .{ .form_tls_address = &addr } else addr;
|
|
switch (decl.kind) {
|
|
.unnamed_test, .@"test", .decltest, .@"comptime", .@"usingnamespace" => unreachable,
|
|
.@"const" => {
|
|
const const_ty_reloc_index = try wip_nav.refForward();
|
|
try wip_nav.infoExprLoc(loc);
|
|
try dibw.writeLeb128(nav.status.fully_resolved.alignment.toByteUnits() orelse
|
|
ty.abiAlignment(zcu).toByteUnits().?);
|
|
try dibw.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 dibw.writeLeb128(dibw, nav.status.fully_resolved.alignment.toByteUnits() orelse
|
|
ty.abiAlignment(zcu).toByteUnits().?);
|
|
try dibw.writeByte(@intFromBool(decl.linkage != .normal));
|
|
},
|
|
}
|
|
},
|
|
.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 dfbw = &wip_nav.debug_frame.buffered_writer;
|
|
switch (dwarf.format) {
|
|
.@"32" => try dfbw.writeInt(u32, undefined, dwarf.endian),
|
|
.@"64" => {
|
|
try dfbw.writeInt(u32, std.math.maxInt(u32), dwarf.endian);
|
|
try dfbw.writeInt(u64, undefined, dwarf.endian);
|
|
},
|
|
}
|
|
switch (format) {
|
|
.none => unreachable,
|
|
.debug_frame => {
|
|
try entry.cross_entry_relocs.append(dwarf.gpa, .{
|
|
.source_off = @intCast(dfbw.count),
|
|
});
|
|
try dfbw.splatByteAll(0, dwarf.sectionOffsetBytes());
|
|
try wip_nav.frameAddrSym(sym_index, 0);
|
|
try dfbw.splatByteAll(undefined, @intFromEnum(dwarf.address_size));
|
|
},
|
|
.eh_frame => {
|
|
try dfbw.writeInt(u32, undefined, dwarf.endian);
|
|
try wip_nav.frameExternalReloc(.{
|
|
.source_off = @intCast(dfbw.count),
|
|
.target_sym = sym_index,
|
|
});
|
|
try dfbw.writeInt(u32, 0, dwarf.endian);
|
|
try dfbw.writeInt(u32, undefined, dwarf.endian);
|
|
try dfbw.writeUleb128(0);
|
|
},
|
|
}
|
|
},
|
|
}
|
|
|
|
const dibw = &wip_nav.debug_info.buffered_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(nav.fqn.toSlice(ip));
|
|
try wip_nav.refType(.fromInterned(func_type.return_type));
|
|
try wip_nav.infoAddrSym(sym_index, 0);
|
|
wip_nav.func_high_pc = @intCast(wip_nav.debug_info.items.len);
|
|
try dibw.writeInt(u32, 0, dwarf.endian);
|
|
const target = &mod.resolved_target.result;
|
|
try dibw.writeLeb128(switch (nav.status.fully_resolved.alignment) {
|
|
.none => target_info.defaultFunctionAlignment(target),
|
|
else => |a| a.maxStrict(target_info.minFunctionAlignment(target)),
|
|
}.toByteUnits().?);
|
|
try dibw.writeByte(@intFromBool(decl.linkage != .normal));
|
|
try dibw.writeByte(@intFromBool(func_type.return_type == .noreturn_type));
|
|
|
|
const dlbw = &wip_nav.debug_line.buffered_writer;
|
|
try dlbw.writeByte(DW.LNS.extended_op);
|
|
if (dwarf.incremental()) {
|
|
try dlbw.writeLeb128(1 + dwarf.sectionOffsetBytes());
|
|
try dlbw.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(dlbw.count),
|
|
.target_sec = .debug_info,
|
|
.target_unit = wip_nav.unit,
|
|
.target_entry = wip_nav.entry.toOptional(),
|
|
});
|
|
try dlbw.splatByteAll(0, dwarf.sectionOffsetBytes());
|
|
|
|
try dlbw.writeByte(DW.LNS.set_column);
|
|
try dlbw.writeLeb128(func.lbrace_column + 1);
|
|
|
|
try wip_nav.advancePCAndLine(func.lbrace_line, 0);
|
|
} else {
|
|
try dlbw.writeLeb128(1 + @intFromEnum(dwarf.address_size));
|
|
try dlbw.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(dlbw.count),
|
|
.target_sym = sym_index,
|
|
});
|
|
try dlbw.splatByteAll(0, @intFromEnum(dwarf.address_size));
|
|
|
|
const file_gop = try dwarf.getModInfo(unit).files.getOrPut(dwarf.gpa, inst_info.file);
|
|
try dlbw.writeByte(DW.LNS.set_file);
|
|
try dlbw.writeLeb128(file_gop.index);
|
|
|
|
try dlbw.writeByte(DW.LNS.set_column);
|
|
try dlbw.writeLeb128(func.lbrace_column + 1);
|
|
|
|
try wip_nav.advancePCAndLine(@intCast(decl.src_line + func.lbrace_line), 0);
|
|
}
|
|
},
|
|
}
|
|
return wip_nav;
|
|
}
|
|
|
|
fn finishWipNavFuncInner(
|
|
dwarf: *Dwarf,
|
|
pt: Zcu.PerThread,
|
|
nav_index: InternPool.Nav.Index,
|
|
code_size: u64,
|
|
wip_nav: *WipNav,
|
|
) UpdateError!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 dfbw = &wip_nav.debug_frame.buffered_writer;
|
|
try dfbw.splatByteAll(DW.CFA.nop, @intCast(dwarf.debug_frame.section.alignment.forward(dfbw.count) - dfbw.count));
|
|
const contents = wip_nav.debug_frame.getWritten();
|
|
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.getWritten()[wip_nav.func_high_pc..][0..4], @intCast(code_size), dwarf.endian);
|
|
if (wip_nav.any_children) {
|
|
const dibw = &wip_nav.debug_info.buffered_writer;
|
|
try dibw.writeLeb128(@intFromEnum(AbbrevCode.null));
|
|
} else {
|
|
const abbrev_code_buf = wip_nav.debug_info.getWritten()[0..AbbrevCode.decl_bytes];
|
|
var abbrev_code_br: std.io.BufferedReader = undefined;
|
|
abbrev_code_br.initFixed(abbrev_code_buf);
|
|
const abbrev_code: AbbrevCode = @enumFromInt(abbrev_code_br.takeLeb128(@typeInfo(AbbrevCode).@"enum".tag_type) catch unreachable);
|
|
std.leb.writeUnsignedFixed(
|
|
AbbrevCode.decl_bytes,
|
|
abbrev_code_buf,
|
|
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.finishWipNavInner(pt, nav_index, wip_nav);
|
|
}
|
|
|
|
fn finishWipNavInner(
|
|
dwarf: *Dwarf,
|
|
pt: Zcu.PerThread,
|
|
nav_index: InternPool.Nav.Index,
|
|
wip_nav: *WipNav,
|
|
) UpdateError!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.getWritten());
|
|
const debug_line = wip_nav.debug_line.getWritten();
|
|
if (debug_line.len > 0) {
|
|
const dlbw = &wip_nav.debug_line.buffered_writer;
|
|
try dlbw.writeByte(DW.LNS.extended_op);
|
|
try dlbw.writeUleb128(1);
|
|
try dlbw.writeByte(DW.LNE.end_sequence);
|
|
try dwarf.debug_line.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_line.getWritten());
|
|
}
|
|
try dwarf.debug_loclists.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_loclists.getWritten());
|
|
|
|
try wip_nav.updateLazy(zcu.navSrcLoc(nav_index));
|
|
}
|
|
|
|
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", .@"usingnamespace", .@"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 = undefined,
|
|
.debug_info = undefined,
|
|
.debug_line = undefined,
|
|
.debug_loclists = undefined,
|
|
.pending_lazy = .empty,
|
|
};
|
|
wip_nav.init();
|
|
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 dibw = &wip_nav.debug_info.buffered_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 dibw.writeByte(@intFromBool(false)) else {
|
|
try dibw.writeLeb128(nav_val.toType().abiSize(zcu));
|
|
try dibw.writeLeb128(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);
|
|
if (loaded_struct.fieldName(ip, field_index).unwrap()) |field_name| try wip_nav.strp(field_name.toSlice(ip)) else {
|
|
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 (!is_comptime) {
|
|
try dibw.writeLeb128(loaded_struct.offsets.get(ip)[field_index]);
|
|
try dibw.writeLeb128(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 dibw.writeLeb128(@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).unwrap().?.toSlice(ip));
|
|
const field_type: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]);
|
|
try wip_nav.refType(field_type);
|
|
try dibw.writeLeb128(field_bit_offset);
|
|
field_bit_offset += @intCast(field_type.bitSize(zcu));
|
|
}
|
|
try dibw.writeLeb128(@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 dibw = &wip_nav.debug_info.buffered_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 dibw.writeLeb128(@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 dibw = &wip_nav.debug_info.buffered_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 dibw.writeLeb128(union_layout.abi_size);
|
|
try dibw.writeLeb128(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(dibw.count + dwarf.sectionOffsetBytes()),
|
|
);
|
|
{
|
|
try wip_nav.abbrevCode(.generated_field);
|
|
try wip_nav.strp("tag");
|
|
try wip_nav.refType(.fromInterned(loaded_union.enum_tag_ty));
|
|
try dibw.writeLeb128(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 dibw.writeLeb128(union_layout.payloadOffset());
|
|
try dibw.writeLeb128(loaded_union.fieldAlign(ip, field_index).toByteUnits() orelse
|
|
if (field_type.isNoReturn(zcu)) 1 else field_type.abiAlignment(zcu).toByteUnits().?);
|
|
}
|
|
try dibw.writeLeb128(@intFromEnum(AbbrevCode.null));
|
|
}
|
|
}
|
|
try dibw.writeLeb128(@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 dibw.writeLeb128(loaded_union.fieldAlign(ip, field_index).toByteUnits() orelse
|
|
field_type.abiAlignment(zcu).toByteUnits().?);
|
|
}
|
|
try dibw.writeLeb128(@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 dibw = &wip_nav.debug_info.buffered_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 dibw.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 dibw = &wip_nav.debug_info.buffered_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 dibw.writeLeb128(@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 dibw = &wip_nav.debug_info.buffered_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(nav.fqn.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 dibw.writeLeb128(nav.status.fully_resolved.alignment.toByteUnits() orelse
|
|
nav_ty.abiAlignment(zcu).toByteUnits().?);
|
|
try dibw.writeByte(@intFromBool(decl.linkage != .normal));
|
|
},
|
|
.decl_const => {
|
|
const dibw = &wip_nav.debug_info.buffered_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(nav.fqn.toSlice(ip));
|
|
const nav_ty_reloc_index = try wip_nav.refForward();
|
|
try dibw.writeLeb128(nav.status.fully_resolved.alignment.toByteUnits() orelse
|
|
nav_ty.abiAlignment(zcu).toByteUnits().?);
|
|
try dibw.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.getWritten());
|
|
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!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 = undefined,
|
|
.debug_info = undefined,
|
|
.debug_line = undefined,
|
|
.debug_loclists = undefined,
|
|
.pending_lazy = pending_lazy.*,
|
|
};
|
|
wip_nav.init();
|
|
defer {
|
|
pending_lazy.* = wip_nav.pending_lazy;
|
|
wip_nav.pending_lazy = .empty;
|
|
wip_nav.deinit();
|
|
}
|
|
const dibw = &wip_nav.debug_info.buffered_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 dibw.writeByte(switch (int_type.signedness) {
|
|
inline .signed, .unsigned => |signedness| @field(DW.ATE, @tagName(signedness)),
|
|
});
|
|
try dibw.writeLeb128(int_type.bits);
|
|
try dibw.writeLeb128(ty.abiSize(zcu));
|
|
try dibw.writeLeb128(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 dibw.writeLeb128(ptr_type.flags.alignment.toByteUnits() orelse
|
|
ptr_child_type.abiAlignment(zcu).toByteUnits().?);
|
|
try dibw.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(dibw.count + 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(dibw.count + 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 dibw.writeLeb128(ty.abiSize(zcu));
|
|
try dibw.writeLeb128(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 dibw.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 dibw.writeLeb128(len_field_type.abiAlignment(zcu).forward(ptr_field_type.abiSize(zcu)));
|
|
try dibw.writeLeb128(@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 dibw.writeLeb128(array_type.len);
|
|
try dibw.writeLeb128(@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 dibw.writeLeb128(vector_type.len);
|
|
try dibw.writeLeb128(@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 dibw.writeLeb128(ty.abiSize(zcu));
|
|
try dibw.writeLeb128(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 dibw.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(dibw.count + 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 dibw.writeLeb128(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 dibw.writeUleb128(0);
|
|
},
|
|
.pointer => {
|
|
try wip_nav.refType(.usize);
|
|
try dibw.writeUleb128(0);
|
|
},
|
|
}
|
|
|
|
try wip_nav.abbrevCode(.unsigned_tagged_union_field);
|
|
try dibw.writeUleb128(0);
|
|
{
|
|
try wip_nav.abbrevCode(.generated_field);
|
|
try wip_nav.strp("null");
|
|
try wip_nav.refType(.null);
|
|
try dibw.writeUleb128(0);
|
|
}
|
|
try dibw.writeLeb128(@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 dibw.writeUleb128(0);
|
|
}
|
|
try dibw.writeLeb128(@intFromEnum(AbbrevCode.null));
|
|
}
|
|
try dibw.writeLeb128(@intFromEnum(AbbrevCode.null));
|
|
},
|
|
}
|
|
try dibw.writeLeb128(@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 dibw.writeLeb128(ty.abiSize(zcu));
|
|
try dibw.writeLeb128(ty.abiAlignment(zcu).toByteUnits().?);
|
|
} else {
|
|
try dibw.writeUleb128(0);
|
|
try dibw.writeUleb128(1);
|
|
}
|
|
{
|
|
try wip_nav.abbrevCode(.tagged_union);
|
|
try wip_nav.infoSectionOffset(
|
|
.debug_info,
|
|
wip_nav.unit,
|
|
wip_nav.entry,
|
|
@intCast(dibw.count + 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 dibw.writeLeb128(error_union_error_set_offset);
|
|
|
|
try wip_nav.abbrevCode(.unsigned_tagged_union_field);
|
|
try dibw.writeUleb128(0);
|
|
{
|
|
try wip_nav.abbrevCode(.generated_field);
|
|
try wip_nav.strp("value");
|
|
try wip_nav.refType(error_union_payload_type);
|
|
try dibw.writeLeb128(error_union_payload_offset);
|
|
}
|
|
try dibw.writeLeb128(@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 dibw.writeLeb128(error_union_error_set_offset);
|
|
}
|
|
try dibw.writeLeb128(@intFromEnum(AbbrevCode.null));
|
|
}
|
|
try dibw.writeLeb128(@intFromEnum(AbbrevCode.null));
|
|
}
|
|
try dibw.writeLeb128(@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 dibw.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 dibw.writeLeb128(ty.bitSize(zcu));
|
|
try dibw.writeLeb128(ty.abiSize(zcu));
|
|
try dibw.writeLeb128(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 dibw.writeByte(@intFromBool(false));
|
|
} else {
|
|
try wip_nav.abbrevCode(.generated_struct_type);
|
|
try wip_nav.strp(name);
|
|
try dibw.writeLeb128(ty.abiSize(zcu));
|
|
try dibw.writeLeb128(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 dibw.writeLeb128(field_byte_offset);
|
|
try dibw.writeLeb128(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 dibw.writeLeb128(@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 dibw.writeLeb128(@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,
|
|
|
|
.amdgcn_kernel => .LLVM_OpenCLKernel,
|
|
.nvptx_kernel,
|
|
.spirv_kernel,
|
|
=> .nocall,
|
|
|
|
.x86_64_interrupt,
|
|
.x86_interrupt,
|
|
.arm_interrupt,
|
|
.mips64_interrupt,
|
|
.mips_interrupt,
|
|
.riscv64_interrupt,
|
|
.riscv32_interrupt,
|
|
.avr_builtin,
|
|
.avr_signal,
|
|
.avr_interrupt,
|
|
.csky_interrupt,
|
|
.m68k_interrupt,
|
|
=> .normal,
|
|
|
|
else => .nocall,
|
|
};
|
|
};
|
|
try dibw.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 dibw.writeLeb128(@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 dibw.writeLeb128(ip.getErrorValueIfExists(field_name).?);
|
|
try wip_nav.strp(field_name.toSlice(ip));
|
|
}
|
|
if (error_set_type.names.len > 0) try dibw.writeLeb128(@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.getWritten());
|
|
}
|
|
|
|
fn updateLazyValue(
|
|
dwarf: *Dwarf,
|
|
pt: Zcu.PerThread,
|
|
src_loc: Zcu.LazySrcLoc,
|
|
value_index: InternPool.Index,
|
|
pending_lazy: *WipNav.PendingLazy,
|
|
) UpdateError!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 = undefined,
|
|
.debug_info = undefined,
|
|
.debug_line = undefined,
|
|
.debug_loclists = undefined,
|
|
.pending_lazy = pending_lazy.*,
|
|
};
|
|
wip_nav.init();
|
|
defer {
|
|
pending_lazy.* = wip_nav.pending_lazy;
|
|
wip_nav.pending_lazy = .empty;
|
|
wip_nav.deinit();
|
|
}
|
|
const dibw = &wip_nav.debug_info.buffered_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 dibw.writeLeb128(try pt.getErrorValue(err.name));
|
|
},
|
|
.error_union => |error_union| {
|
|
try wip_nav.abbrevCode(.aggregate_comptime_value);
|
|
const err_abi_size = std.math.divCeil(u17, zcu.errorSetBits(), 8) catch unreachable;
|
|
const err_value = 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 dibw.writeLeb128(err_abi_size);
|
|
try dwarf.writeIntTo(dibw, err_abi_size, err_value);
|
|
}
|
|
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 dibw.writeLeb128(err_abi_size);
|
|
try dwarf.writeIntTo(dibw, err_abi_size, err_value);
|
|
}
|
|
switch (error_union.val) {
|
|
.err_name => {},
|
|
.payload => |payload| {
|
|
_ = payload;
|
|
try wip_nav.abbrevCode(.aggregate_comptime_value);
|
|
},
|
|
}
|
|
try wip_nav.refType(.fromInterned(error_union.ty));
|
|
try dibw.writeLeb128(@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 dibw.writeInt(u16, @bitCast(f16_val), dwarf.endian);
|
|
},
|
|
.f32 => |f32_val| {
|
|
try wip_nav.abbrevCode(.data4_comptime_value);
|
|
try dibw.writeInt(u32, @bitCast(f32_val), dwarf.endian);
|
|
},
|
|
.f64 => |f64_val| {
|
|
try wip_nav.abbrevCode(.data8_comptime_value);
|
|
try dibw.writeInt(u64, @bitCast(f64_val), dwarf.endian);
|
|
},
|
|
.f80 => |f80_val| {
|
|
try wip_nav.abbrevCode(.block_comptime_value);
|
|
try dibw.writeUleb128(@divExact(80, 8));
|
|
try dibw.writeInt(u80, @bitCast(f80_val), dwarf.endian);
|
|
},
|
|
.f128 => |f128_val| {
|
|
try wip_nav.abbrevCode(.data16_comptime_value);
|
|
try dibw.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 dibw.writeLeb128(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 dibw.writeLeb128(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 dibw.writeLeb128(@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 dibw.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 dibw.writeLeb128(bytes.len);
|
|
try dibw.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 dibw.writeLeb128(@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);
|
|
if (loaded_struct_type.fieldName(ip, field_index).unwrap()) |field_name| try wip_nav.strp(field_name.toSlice(ip)) else {
|
|
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);
|
|
}
|
|
},
|
|
.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 dibw.writeLeb128(@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 dibw.writeLeb128(@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.getWritten());
|
|
}
|
|
|
|
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,
|
|
},
|
|
};
|
|
}
|
|
|
|
fn updateContainerTypeInner(dwarf: *Dwarf, pt: Zcu.PerThread, type_index: InternPool.Index) UpdateError!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 = .empty,
|
|
.debug_info = .empty,
|
|
.debug_line = .empty,
|
|
.debug_loclists = .empty,
|
|
.pending_lazy = .empty,
|
|
};
|
|
defer wip_nav.deinit();
|
|
|
|
const loaded_struct = ip.loadStructType(type_index);
|
|
|
|
const diw = wip_nav.debug_info.writer(dwarf.gpa);
|
|
try wip_nav.abbrevCode(if (loaded_struct.field_types.len == 0) .empty_file else .file);
|
|
try uleb128(diw, file_gop.index);
|
|
try wip_nav.strp(loaded_struct.name.toSlice(ip));
|
|
if (loaded_struct.field_types.len > 0) {
|
|
try uleb128(diw, ty.abiSize(zcu));
|
|
try uleb128(diw, 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);
|
|
if (loaded_struct.fieldName(ip, field_index).unwrap()) |field_name| try wip_nav.strp(field_name.toSlice(ip)) else {
|
|
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 (!is_comptime) {
|
|
try uleb128(diw, loaded_struct.offsets.get(ip)[field_index]);
|
|
try uleb128(diw, 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 uleb128(diw, @intFromEnum(AbbrevCode.null));
|
|
}
|
|
|
|
try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.items);
|
|
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 = .empty,
|
|
.debug_info = .empty,
|
|
.debug_line = .empty,
|
|
.debug_loclists = .empty,
|
|
.pending_lazy = .empty,
|
|
};
|
|
defer wip_nav.deinit();
|
|
const diw = wip_nav.debug_info.writer(dwarf.gpa);
|
|
const name = try std.fmt.allocPrint(dwarf.gpa, "{}", .{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 uleb128(diw, file_gop.index);
|
|
try wip_nav.strp(name);
|
|
if (loaded_struct.field_types.len == 0) try diw.writeByte(@intFromBool(false)) else {
|
|
try uleb128(diw, ty.abiSize(zcu));
|
|
try uleb128(diw, 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);
|
|
if (loaded_struct.fieldName(ip, field_index).unwrap()) |field_name| try wip_nav.strp(field_name.toSlice(ip)) else {
|
|
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 (!is_comptime) {
|
|
try uleb128(diw, loaded_struct.offsets.get(ip)[field_index]);
|
|
try uleb128(diw, 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 uleb128(diw, @intFromEnum(AbbrevCode.null));
|
|
}
|
|
},
|
|
.@"packed" => {
|
|
try wip_nav.abbrevCode(if (loaded_struct.field_types.len > 0) .packed_struct_type else .empty_packed_struct_type);
|
|
try uleb128(diw, 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).unwrap().?.toSlice(ip));
|
|
const field_type: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]);
|
|
try wip_nav.refType(field_type);
|
|
try uleb128(diw, field_bit_offset);
|
|
field_bit_offset += @intCast(field_type.bitSize(zcu));
|
|
}
|
|
if (loaded_struct.field_types.len > 0) try uleb128(diw, @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 uleb128(diw, 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 uleb128(diw, @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 uleb128(diw, file_gop.index);
|
|
try wip_nav.strp(name);
|
|
const union_layout = Type.getUnionLayout(loaded_union, zcu);
|
|
try uleb128(diw, union_layout.abi_size);
|
|
try uleb128(diw, 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(wip_nav.debug_info.items.len + dwarf.sectionOffsetBytes()),
|
|
);
|
|
{
|
|
try wip_nav.abbrevCode(.generated_field);
|
|
try wip_nav.strp("tag");
|
|
try wip_nav.refType(.fromInterned(loaded_union.enum_tag_ty));
|
|
try uleb128(diw, 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 uleb128(diw, union_layout.payloadOffset());
|
|
try uleb128(diw, loaded_union.fieldAlign(ip, field_index).toByteUnits() orelse
|
|
if (field_type.isNoReturn(zcu)) 1 else field_type.abiAlignment(zcu).toByteUnits().?);
|
|
}
|
|
try uleb128(diw, @intFromEnum(AbbrevCode.null));
|
|
}
|
|
}
|
|
try uleb128(diw, @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 uleb128(diw, loaded_union.fieldAlign(ip, field_index).toByteUnits() orelse
|
|
field_type.abiAlignment(zcu).toByteUnits().?);
|
|
}
|
|
if (loaded_union.field_types.len > 0) try uleb128(diw, @intFromEnum(AbbrevCode.null));
|
|
},
|
|
.opaque_type => {
|
|
try wip_nav.abbrevCode(.empty_struct_type);
|
|
try uleb128(diw, 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.items);
|
|
try dwarf.debug_loclists.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_loclists.items);
|
|
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!@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 daaw: std.io.AllocatingWriter = undefined;
|
|
daaw.init(dwarf.gpa);
|
|
defer daaw.deinit();
|
|
const dabw = &daaw.buffered_writer;
|
|
const abbrev = AbbrevCode.abbrevs.get(abbrev_code);
|
|
try dabw.writeLeb128(@intFromEnum(abbrev_code));
|
|
try dabw.writeLeb128(@intFromEnum(abbrev.tag));
|
|
try dabw.writeByte(if (abbrev.children) DW.CHILDREN.yes else DW.CHILDREN.no);
|
|
for (abbrev.attrs) |*attr| inline for (attr) |info| try dabw.writeLeb128(@intFromEnum(info));
|
|
for (0..2) |_| try dabw.writeUleb128(0);
|
|
try dwarf.debug_abbrev.section.replaceEntry(DebugAbbrev.unit, entry, dwarf, daaw.getWritten());
|
|
return @intFromEnum(abbrev_code);
|
|
}
|
|
|
|
pub fn flush(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void {
|
|
const gpa = dwarf.gpa;
|
|
const zcu = pt.zcu;
|
|
const ip = &zcu.intern_pool;
|
|
|
|
{
|
|
const type_gop = try dwarf.types.getOrPut(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 = undefined,
|
|
.debug_info = undefined,
|
|
.debug_line = undefined,
|
|
.debug_loclists = undefined,
|
|
.pending_lazy = .empty,
|
|
};
|
|
wip_nav.init();
|
|
defer wip_nav.deinit();
|
|
const dibw = &wip_nav.debug_info.buffered_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 dibw.writeLeb128(value);
|
|
try wip_nav.strp(name.toSlice(ip));
|
|
}
|
|
if (global_error_set_names.len > 0) try dibw.writeLeb128(@intFromEnum(AbbrevCode.null));
|
|
try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, dibw.getWritten());
|
|
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, gpa);
|
|
defer gpa.free(root_dir_path);
|
|
mod_info.root_dir_path = try dwarf.debug_line_str.addString(dwarf, root_dir_path);
|
|
}
|
|
|
|
var header: std.ArrayListUnmanaged(u8) = .empty;
|
|
defer header.deinit(gpa);
|
|
var header_bw: std.io.BufferedWriter = undefined;
|
|
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(gpa, 1);
|
|
try header.resize(gpa, unit_ptr.header_len);
|
|
header_bw.initFixed(header.items);
|
|
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" => header_bw.writeInt(u32, @intCast(unit_len), dwarf.endian) catch unreachable,
|
|
.@"64" => {
|
|
header_bw.writeInt(u32, std.math.maxInt(u32), dwarf.endian) catch unreachable;
|
|
header_bw.writeInt(u64, unit_len, dwarf.endian) catch unreachable;
|
|
},
|
|
}
|
|
header_bw.writeInt(u16, 2, dwarf.endian) catch unreachable;
|
|
unit_ptr.cross_section_relocs.appendAssumeCapacity(.{
|
|
.source_off = @intCast(header_bw.end),
|
|
.target_sec = .debug_info,
|
|
.target_unit = unit,
|
|
});
|
|
header_bw.splatByteAll(0, dwarf.sectionOffsetBytes()) catch unreachable;
|
|
header_bw.writeAll(&.{ @intFromEnum(dwarf.address_size), 0 }) catch unreachable;
|
|
header_bw.splatByteAll(0, unit_ptr.header_len - header_bw.end) catch unreachable;
|
|
assert(header_bw.end == header_bw.buffer.len);
|
|
try unit_ptr.replaceHeader(&dwarf.debug_aranges.section, dwarf, header_bw.buffer);
|
|
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("../arch/x86_64/bits.zig").Register;
|
|
for (dwarf.debug_frame.section.units.items) |*unit| {
|
|
try header.resize(gpa, unit.header_len);
|
|
header_bw.initFixed(header.items);
|
|
const unit_len = unit.header_len - dwarf.unitLengthBytes();
|
|
switch (dwarf.format) {
|
|
.@"32" => header_bw.writeInt(u32, @intCast(unit_len), dwarf.endian) catch unreachable,
|
|
.@"64" => {
|
|
header_bw.writeInt(u32, std.math.maxInt(u32), dwarf.endian) catch unreachable;
|
|
header_bw.writeInt(u64, unit_len, dwarf.endian) catch unreachable;
|
|
},
|
|
}
|
|
header_bw.splatByteAll(0, 4) catch unreachable;
|
|
header_bw.writeByte(1) catch unreachable;
|
|
header_bw.writeAll("zR\x00") catch unreachable;
|
|
header_bw.writeLeb128(dwarf.debug_frame.header.code_alignment_factor) catch unreachable;
|
|
header_bw.writeLeb128(dwarf.debug_frame.header.data_alignment_factor) catch unreachable;
|
|
header_bw.writeLeb128(dwarf.debug_frame.header.return_address_register) catch unreachable;
|
|
header_bw.writeUleb128(1) catch unreachable;
|
|
header_bw.writeByte(DW.EH.PE.pcrel | DW.EH.PE.sdata4) catch unreachable;
|
|
header_bw.writeByte(DW.CFA.def_cfa_sf) catch unreachable;
|
|
header_bw.writeUleb128(1) catch unreachable;
|
|
header_bw.writeLeb128(Register.rsp.dwarfNum()) catch unreachable;
|
|
header_bw.writeSleb128(-1) catch unreachable;
|
|
header_bw.writeByte(@as(u8, DW.CFA.offset) + Register.rip.dwarfNum()) catch unreachable;
|
|
header_bw.writeUleb128(1) catch unreachable;
|
|
header_bw.splatByteAll(DW.CFA.nop, unit.header_len - header_bw.end) catch unreachable;
|
|
assert(header_bw.end == header_bw.buffer.len);
|
|
try unit.replaceHeader(&dwarf.debug_frame.section, dwarf, header_bw.buffer);
|
|
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(gpa, 1);
|
|
try unit_ptr.cross_section_relocs.ensureTotalCapacity(gpa, 7);
|
|
try header.resize(gpa, unit_ptr.header_len);
|
|
header_bw.initFixed(header.items);
|
|
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" => header_bw.writeInt(u32, @intCast(unit_len), dwarf.endian) catch unreachable,
|
|
.@"64" => {
|
|
header_bw.writeInt(u32, std.math.maxInt(u32), dwarf.endian) catch unreachable;
|
|
header_bw.writeInt(u64, unit_len, dwarf.endian) catch unreachable;
|
|
},
|
|
}
|
|
header_bw.writeInt(u16, 5, dwarf.endian) catch unreachable;
|
|
header_bw.writeAll(&.{ DW.UT.compile, @intFromEnum(dwarf.address_size) }) catch unreachable;
|
|
unit_ptr.cross_section_relocs.appendAssumeCapacity(.{
|
|
.source_off = @intCast(header_bw.end),
|
|
.target_sec = .debug_abbrev,
|
|
.target_unit = DebugAbbrev.unit,
|
|
});
|
|
header_bw.splatByteAll(0, dwarf.sectionOffsetBytes()) catch unreachable;
|
|
const compile_unit_off: u32 = @intCast(header_bw.end);
|
|
header_bw.writeLeb128(try dwarf.refAbbrevCode(.compile_unit)) catch unreachable;
|
|
header_bw.writeByte(DW.LANG.Zig) catch unreachable;
|
|
unit_ptr.cross_section_relocs.appendAssumeCapacity(.{
|
|
.source_off = @intCast(header_bw.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(),
|
|
});
|
|
header_bw.splatByteAll(0, dwarf.sectionOffsetBytes()) catch unreachable;
|
|
unit_ptr.cross_section_relocs.appendAssumeCapacity(.{
|
|
.source_off = @intCast(header_bw.end),
|
|
.target_sec = .debug_line_str,
|
|
.target_unit = StringSection.unit,
|
|
.target_entry = mod_info.root_dir_path.toOptional(),
|
|
});
|
|
header_bw.splatByteAll(0, dwarf.sectionOffsetBytes()) catch unreachable;
|
|
unit_ptr.cross_section_relocs.appendAssumeCapacity(.{
|
|
.source_off = @intCast(header_bw.end),
|
|
.target_sec = .debug_line_str,
|
|
.target_unit = StringSection.unit,
|
|
.target_entry = (try dwarf.debug_line_str.addString(dwarf, mod.root_src_path)).toOptional(),
|
|
});
|
|
header_bw.splatByteAll(0, dwarf.sectionOffsetBytes()) catch unreachable;
|
|
unit_ptr.cross_unit_relocs.appendAssumeCapacity(.{
|
|
.source_off = @intCast(header_bw.end),
|
|
.target_unit = .main,
|
|
.target_off = compile_unit_off,
|
|
});
|
|
header_bw.splatByteAll(0, dwarf.sectionOffsetBytes()) catch unreachable;
|
|
unit_ptr.cross_section_relocs.appendAssumeCapacity(.{
|
|
.source_off = @intCast(header_bw.end),
|
|
.target_sec = .debug_line,
|
|
.target_unit = unit,
|
|
});
|
|
header_bw.splatByteAll(0, dwarf.sectionOffsetBytes()) catch unreachable;
|
|
unit_ptr.cross_section_relocs.appendAssumeCapacity(.{
|
|
.source_off = @intCast(header_bw.end),
|
|
.target_sec = .debug_rnglists,
|
|
.target_unit = unit,
|
|
.target_off = DebugRngLists.baseOffset(dwarf),
|
|
});
|
|
header_bw.splatByteAll(0, dwarf.sectionOffsetBytes()) catch unreachable;
|
|
header_bw.writeUleb128(0) catch unreachable;
|
|
header_bw.writeLeb128(try dwarf.refAbbrevCode(.module)) catch unreachable;
|
|
unit_ptr.cross_section_relocs.appendAssumeCapacity(.{
|
|
.source_off = @intCast(header_bw.end),
|
|
.target_sec = .debug_str,
|
|
.target_unit = StringSection.unit,
|
|
.target_entry = (try dwarf.debug_str.addString(dwarf, mod.fully_qualified_name)).toOptional(),
|
|
});
|
|
header_bw.splatByteAll(0, dwarf.sectionOffsetBytes()) catch unreachable;
|
|
header_bw.writeUleb128(0) catch unreachable;
|
|
assert(header_bw.end == header_bw.buffer.len);
|
|
try unit_ptr.replaceHeader(&dwarf.debug_info.section, dwarf, header_bw.buffer);
|
|
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(gpa, mod_info.dirs.count() + 2 * (mod_info.files.count()));
|
|
try header.resize(gpa, unit.header_len);
|
|
header_bw.initFixed(header.items);
|
|
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" => header_bw.writeInt(u32, @intCast(unit_len), dwarf.endian) catch unreachable,
|
|
.@"64" => {
|
|
header_bw.writeInt(u32, std.math.maxInt(u32), dwarf.endian) catch unreachable;
|
|
header_bw.writeInt(u64, unit_len, dwarf.endian) catch unreachable;
|
|
},
|
|
}
|
|
header_bw.writeInt(u16, 5, dwarf.endian) catch unreachable;
|
|
header_bw.writeAll(&.{ @intFromEnum(dwarf.address_size), 0 }) catch unreachable;
|
|
dwarf.writeIntTo(
|
|
&header_bw,
|
|
dwarf.sectionOffsetBytes(),
|
|
unit.header_len - header_bw.end,
|
|
) catch unreachable;
|
|
const StandardOpcode = DeclValEnum(DW.LNS);
|
|
header_bw.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;
|
|
header_bw.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;
|
|
header_bw.writeByte(1) catch unreachable;
|
|
header_bw.writeLeb128(@as(u14, DW.LNCT.path)) catch unreachable;
|
|
header_bw.writeLeb128(@as(u13, DW.FORM.line_strp)) catch unreachable;
|
|
header_bw.writeLeb128(mod_info.dirs.count()) catch unreachable;
|
|
for (mod_info.dirs.keys()) |dir_unit| {
|
|
unit.cross_section_relocs.appendAssumeCapacity(.{
|
|
.source_off = @intCast(header_bw.end),
|
|
.target_sec = .debug_line_str,
|
|
.target_unit = StringSection.unit,
|
|
.target_entry = dwarf.getModInfo(dir_unit).root_dir_path.toOptional(),
|
|
});
|
|
header_bw.splatByteAll(0, dwarf.sectionOffsetBytes()) catch unreachable;
|
|
}
|
|
const dir_index_info = DebugLine.dirIndexInfo(@intCast(mod_info.dirs.count()));
|
|
header_bw.writeByte(3) catch unreachable;
|
|
header_bw.writeLeb128(@as(u14, DW.LNCT.path)) catch unreachable;
|
|
header_bw.writeLeb128(@as(u13, DW.FORM.line_strp)) catch unreachable;
|
|
header_bw.writeLeb128(@as(u14, DW.LNCT.directory_index)) catch unreachable;
|
|
header_bw.writeLeb128(@intFromEnum(dir_index_info.form)) catch unreachable;
|
|
header_bw.writeLeb128(@as(u14, DW.LNCT.LLVM_source)) catch unreachable;
|
|
header_bw.writeLeb128(@as(u13, DW.FORM.line_strp)) catch unreachable;
|
|
header_bw.writeLeb128(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(header_bw.end),
|
|
.target_sec = .debug_line_str,
|
|
.target_unit = StringSection.unit,
|
|
.target_entry = (try dwarf.debug_line_str.addString(dwarf, file.sub_file_path)).toOptional(),
|
|
});
|
|
header.appendNTimesAssumeCapacity(0, dwarf.sectionOffsetBytes());
|
|
dwarf.writeIntTo(
|
|
&header_bw,
|
|
dir_index_info.bytes,
|
|
mod_info.dirs.getIndex(dwarf.getUnitIfExists(file.mod.?).?) orelse 0,
|
|
);
|
|
unit.cross_section_relocs.appendAssumeCapacity(.{
|
|
.source_off = @intCast(header_bw.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(),
|
|
});
|
|
header_bw.splatByteAll(0, dwarf.sectionOffsetBytes()) catch unreachable;
|
|
}
|
|
assert(header_bw.end == header_bw.buffer.len);
|
|
try unit.replaceHeader(&dwarf.debug_line.section, dwarf, header_bw.buffer);
|
|
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| {
|
|
try header.resize(gpa, unit.header_len);
|
|
header_bw.initFixed(header.items);
|
|
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" => header_bw.writeInt(u32, @intCast(unit_len), dwarf.endian) catch unreachable,
|
|
.@"64" => {
|
|
header_bw.writeInt(u32, std.math.maxInt(u32), dwarf.endian) catch unreachable;
|
|
header_bw.writeInt(u64, unit_len, dwarf.endian) catch unreachable;
|
|
},
|
|
}
|
|
header_bw.writeInt(u16, 5, dwarf.endian) catch unreachable;
|
|
header_bw.writeAll(&.{ @intFromEnum(dwarf.address_size), 0 }) catch unreachable;
|
|
header_bw.writeInt(u32, 1, dwarf.endian) catch unreachable;
|
|
dwarf.writeIntTo(&header_bw, dwarf.sectionOffsetBytes(), dwarf.sectionOffsetBytes() * 1) catch unreachable;
|
|
assert(header_bw.end == header_bw.buffer.len);
|
|
try unit.replaceHeader(&dwarf.debug_rnglists.section, dwarf, header_bw.buffer);
|
|
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);
|
|
}
|
|
|
|
const sleb128 = {};
|
|
const uleb128 = {};
|
|
|
|
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,
|
|
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,
|
|
// 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,
|
|
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,
|
|
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_func_generic));
|
|
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 },
|
|
},
|
|
},
|
|
.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 },
|
|
},
|
|
},
|
|
.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 },
|
|
},
|
|
},
|
|
.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 },
|
|
},
|
|
},
|
|
.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!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 writeIntTo(dwarf: *Dwarf, bw: *std.io.BufferedWriter, len: usize, int: u64) !void {
|
|
dwarf.writeInt(try bw.writableSlice(len), int);
|
|
}
|
|
|
|
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 {
|
|
return leb128Bytes(switch (@typeInfo(@TypeOf(value))) {
|
|
.comptime_int => @as(std.math.IntFittingRange(0, @abs(value)), value),
|
|
.int => |value_info| switch (value_info.signedness) {
|
|
.signed => @as(@Type(.{ .int = .{ .signedness = .unsigned, .bits = value_info.bits -| 1 } }), @intCast(value)),
|
|
.unsigned => value,
|
|
},
|
|
else => comptime unreachable,
|
|
});
|
|
}
|
|
fn sleb128Bytes(value: anytype) u32 {
|
|
return leb128Bytes(switch (@typeInfo(@TypeOf(value))) {
|
|
.comptime_int => @as(std.math.IntFittingRange(@min(value, -1), @max(0, value)), value),
|
|
.int => |value_info| switch (value_info.signedness) {
|
|
.signed => value,
|
|
.unsigned => @as(@Type(.{ .int = .{ .signedness = .signed, .bits = value_info.bits + 1 } }), value),
|
|
},
|
|
else => comptime unreachable,
|
|
});
|
|
}
|
|
fn leb128Bytes(value: anytype) u32 {
|
|
const value_info = @typeInfo(@TypeOf(value)).int;
|
|
var buffer: [
|
|
std.math.divCeil(u16, @intFromBool(value_info.signedness == .signed) + value_info.bits, 7) catch unreachable
|
|
]u8 = undefined;
|
|
var bw: std.io.BufferedWriter = undefined;
|
|
bw.initFixed(&buffer);
|
|
bw.writeLeb128(value) catch unreachable;
|
|
return @intCast(bw.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.incremental;
|
|
}
|
|
|
|
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 Allocator = std.mem.Allocator;
|