diff --git a/CMakeLists.txt b/CMakeLists.txt index bf5af25e9a431de9e2462b353697fd70ad22030e..919f7b5fa0329edc62259bba6a5f4969f27da6fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -383,6 +383,7 @@ set(ZIG_STAGE2_SOURCES src/link/ConstPool.zig src/link/Coff.zig src/link/Dwarf.zig + src/link/Dwarf2.zig src/link/Elf.zig src/link/Elf/Archive.zig src/link/Elf/Atom.zig diff --git a/lib/std/debug/Dwarf/Unwind.zig b/lib/std/debug/Dwarf/Unwind.zig index d351c0421e5c4b164c2f42e27125b8670ccde649..d41c3f358885c0d3055fd1167bffa2ed80d6f877 100644 --- a/lib/std/debug/Dwarf/Unwind.zig +++ b/lib/std/debug/Dwarf/Unwind.zig @@ -362,8 +362,7 @@ pub const CommonInformationEntry = struct { if (aug_str.len == 0) break :aug .none; if (aug_str[0] == 'z') break :aug .lsb_z; if (std.mem.eql(u8, aug_str, "eh")) break :aug .gcc_eh; - // We can't finish parsing the CIE if we don't know what its augmentation means. - return bad(); + return error.UnsupportedAugmentation; }; switch (aug_kind) { @@ -396,7 +395,7 @@ pub const CommonInformationEntry = struct { 'R' => fde_pointer_enc = @bitCast(try aug_data.takeByte()), 'S' => is_signal_frame = true, 'B', 'G' => {}, - else => return bad(), + else => return error.UnsupportedAugmentation, }; break :aug .{ fde_pointer_enc, is_signal_frame }; }; @@ -502,7 +501,15 @@ pub fn prepare( const idx = unwind.cie_list.len; try unwind.cie_list.append(gpa, .{ .offset = entry_offset, - .cie = try .parse(cie_info.format, try r.take(bytes_len), section.id, addr_size_bytes), + .cie = CommonInformationEntry.parse(cie_info.format, try r.take(bytes_len), section.id, addr_size_bytes) catch |err| switch (err) { + error.UnsupportedDwarfVersion, + error.UnsupportedAugmentation, + => { + // These are recoverable by just skipping the CIE. + continue; + }, + else => |e| return e, + }, }); errdefer _ = unwind.cie_list.pop().?; try VirtualMachine.populateCieLastRow(gpa, &unwind.cie_list.items(.cie)[idx], addr_size_bytes, endian); @@ -514,8 +521,10 @@ pub fn prepare( try r.discardAll(bytes_len); continue; } - const cie = unwind.findCie(fde_info.cie_offset) orelse return error.InvalidDebugInfo; - const fde: FrameDescriptionEntry = try .parse(section.vaddr + r.seek, try r.take(bytes_len), cie, endian); + const fde_vaddr = section.vaddr + r.seek; + const fde_bytes = try r.take(bytes_len); + const cie = unwind.findCie(fde_info.cie_offset) orelse continue; + const fde: FrameDescriptionEntry = try .parse(fde_vaddr, fde_bytes, cie, endian); try fde_list.append(gpa, .{ .pc_begin = fde.pc_begin, .fde_offset = entry_offset, @@ -612,7 +621,7 @@ pub fn getFde(unwind: *const Unwind, fde_offset: u64, endian: Endian) !struct { .cie, .terminator => return bad(), // This is meant to be an FDE }; - const cie = unwind.findCie(fde_info.cie_offset) orelse return error.InvalidDebugInfo; + const cie = unwind.findCie(fde_info.cie_offset) orelse return bad(); const fde: FrameDescriptionEntry = try .parse( section.vaddr + fde_offset + fde_reader.seek, try fde_reader.take(cast(usize, fde_info.bytes_len) orelse return error.EndOfStream), diff --git a/lib/std/debug/SelfInfo/Elf.zig b/lib/std/debug/SelfInfo/Elf.zig index 398a3f95a8933d489f67e3c9958084f2ed4e5660..93cee2810793e22208c86dd63b09b420f7f7f089 100644 --- a/lib/std/debug/SelfInfo/Elf.zig +++ b/lib/std/debug/SelfInfo/Elf.zig @@ -340,7 +340,6 @@ const Module = struct { error.InvalidOperation, => return error.InvalidDebugInfo, error.UnsupportedAddrSize, - error.UnsupportedDwarfVersion, error.UnimplementedUserOpcode, => return error.UnsupportedDebugInfo, }; diff --git a/lib/std/debug/SelfInfo/MachO.zig b/lib/std/debug/SelfInfo/MachO.zig index e771d7a9f5e45b3b5c217053f3394fd7b713a029..22430159cfd00e1efa57933a70af214b4b3803c3 100644 --- a/lib/std/debug/SelfInfo/MachO.zig +++ b/lib/std/debug/SelfInfo/MachO.zig @@ -578,7 +578,6 @@ const Module = struct { error.InvalidOperation, => return error.InvalidDebugInfo, error.UnsupportedAddrSize, - error.UnsupportedDwarfVersion, error.UnimplementedUserOpcode, => return error.UnsupportedDebugInfo, }; diff --git a/src/codegen.zig b/src/codegen.zig index b4efad778181ae69f35265f5e5edfe29e565921a..2986419f9292d754a83e13048a008c862fa74e78 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -26,6 +26,7 @@ pub const aarch64 = @import("codegen/aarch64.zig"); pub const loongarch = @import("codegen/loongarch.zig"); pub const Error = link.Error; +pub const EmitError = Error || std.Io.Writer.Error || error{MappedFileIo}; fn devFeatureForBackend(backend: std.lang.CompilerBackend) dev.Feature { return switch (backend) { @@ -196,7 +197,7 @@ pub fn emitFunction( any_mir: *const AnyMir, w: *std.Io.Writer, debug_output: link.File.DebugInfoOutput, -) (Error || std.Io.Writer.Error)!void { +) EmitError!void { const zcu = pt.zcu; const func = zcu.funcInfo(func_index); const target = &zcu.navFileScope(func.owner_nav).mod.?.resolved_target.result; @@ -228,7 +229,7 @@ pub fn generateLazyFunction( atom_id: link.File.AtomId, w: *std.Io.Writer, debug_output: link.File.DebugInfoOutput, -) (Error || std.Io.Writer.Error)!void { +) EmitError!void { const zcu = pt.zcu; const target = if (Type.fromInterned(lazy_sym.ty).typeDeclInstAllowGeneratedTag(zcu)) |inst_index| &zcu.fileByIndex(inst_index.resolveFile(&zcu.intern_pool)).mod.?.resolved_target.result @@ -252,7 +253,7 @@ pub fn generateLazySymbol( w: *std.Io.Writer, debug_output: link.File.DebugInfoOutput, reloc_parent: link.File.RelocInfo.Parent, -) (Error || std.Io.Writer.Error)!void { +) EmitError!void { const tracy = trace(@src()); defer tracy.end(); tracy.addTextFmt("{t}, {f}", .{ lazy_sym.kind, Type.fromInterned(lazy_sym.ty).fmt(pt) }); diff --git a/src/codegen/riscv64/CodeGen.zig b/src/codegen/riscv64/CodeGen.zig index 0b40d2fd34576e135160ca4cebd8f32d5a9fb6c5..97921d321136f3070f9188f2d93cadebc8c4be92 100644 --- a/src/codegen/riscv64/CodeGen.zig +++ b/src/codegen/riscv64/CodeGen.zig @@ -856,7 +856,7 @@ pub fn generateLazy( atom_index: link.File.AtomId, w: *std.Io.Writer, debug_output: link.File.DebugInfoOutput, -) (codegen.Error || std.Io.Writer.Error)!void { +) codegen.EmitError!void { _ = atom_index; const comp = bin_file.comp; const gpa = comp.gpa; diff --git a/src/codegen/riscv64/Emit.zig b/src/codegen/riscv64/Emit.zig index f5a3f9584a5018fa88e0d2a7507d2fd73cbef645..fd145a3dce7fe5dd1db20a72d8c8549d0f038ed9 100644 --- a/src/codegen/riscv64/Emit.zig +++ b/src/codegen/riscv64/Emit.zig @@ -13,7 +13,7 @@ prev_di_pc: usize, code_offset_mapping: std.AutoHashMapUnmanaged(Mir.Inst.Index, usize) = .empty, relocs: std.ArrayList(Reloc) = .empty, -pub const Error = Lower.Error || std.Io.Writer.Error || error{ +pub const Error = Lower.Error || codegen.EmitError || error{ EmitFail, }; @@ -118,14 +118,14 @@ pub fn emitMir(emit: *Emit) Error!void { else => unreachable, .pseudo_dbg_prologue_end => { switch (emit.debug_output) { - .dwarf => |dw| { + inline .dwarf, .dwarf2 => |dw| { try dw.setPrologueEnd(); log.debug("mirDbgPrologueEnd (line={d}, col={d})", .{ emit.prev_di_line, emit.prev_di_column, }); try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); }, - .none => {}, + .eh_frame, .none => {}, } }, .pseudo_dbg_line_column => try emit.dbgAdvancePCAndLine( @@ -134,14 +134,14 @@ pub fn emitMir(emit: *Emit) Error!void { ), .pseudo_dbg_epilogue_begin => { switch (emit.debug_output) { - .dwarf => |dw| { + inline .dwarf, .dwarf2 => |dw| { try dw.setEpilogueBegin(); log.debug("mirDbgEpilogueBegin (line={d}, col={d})", .{ emit.prev_di_line, emit.prev_di_column, }); try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); }, - .none => {}, + .eh_frame, .none => {}, } }, .pseudo_dead => {}, @@ -190,15 +190,15 @@ fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) Error!void { const delta_pc: usize = emit.w.end - emit.prev_di_pc; log.debug(" (advance pc={d} and line={d})", .{ delta_pc, delta_line }); switch (emit.debug_output) { - .dwarf => |dw| { + inline .dwarf, .dwarf2 => |dw| { if (column != emit.prev_di_column) try dw.setColumn(column); if (delta_line == 0) return; // TODO: fix these edge cases. - try dw.advancePCAndLine(delta_line, delta_pc); + try dw.advancePcAndLine(delta_line, delta_pc); emit.prev_di_line = line; emit.prev_di_column = column; emit.prev_di_pc = emit.w.end; }, - .none => {}, + .eh_frame, .none => {}, } } @@ -209,6 +209,7 @@ fn fail(emit: *Emit, comptime format: []const u8, args: anytype) Error { }; } +const codegen = @import("../../codegen.zig"); const link = @import("../../link.zig"); const log = std.log.scoped(.emit); const mem = std.mem; diff --git a/src/codegen/riscv64/Mir.zig b/src/codegen/riscv64/Mir.zig index 52cadccd87bdcb7bb016dcf9a7f03ada3bd76b3a..3c2c6ac1d625d908bc25cdec6396863aede7b554 100644 --- a/src/codegen/riscv64/Mir.zig +++ b/src/codegen/riscv64/Mir.zig @@ -111,7 +111,7 @@ pub fn emit( atom_index: link.File.AtomId, w: *std.Io.Writer, debug_output: link.File.DebugInfoOutput, -) (codegen.Error || std.Io.Writer.Error)!void { +) codegen.EmitError!void { _ = atom_index; const zcu = pt.zcu; const comp = zcu.comp; diff --git a/src/codegen/sparc64/Emit.zig b/src/codegen/sparc64/Emit.zig index 1fd9b5c769e258c68dd3c50e994f032a159f5bcc..80c9f25832cc17240f53a3b8f9ad9b7397d6f7b1 100644 --- a/src/codegen/sparc64/Emit.zig +++ b/src/codegen/sparc64/Emit.zig @@ -4,6 +4,7 @@ const std = @import("std"); const Endian = std.lang.Endian; const assert = std.debug.assert; +const codegen = @import("../../codegen.zig"); const link = @import("../../link.zig"); const Zcu = @import("../../Zcu.zig"); const ErrorMsg = Zcu.ErrorMsg; @@ -40,8 +41,7 @@ branch_forward_origins: std.AutoHashMapUnmanaged(Mir.Inst.Index, std.ArrayList(M /// instruction code_offset_mapping: std.AutoHashMapUnmanaged(Mir.Inst.Index, usize) = .empty, -const InnerError = std.Io.Writer.Error || error{ - OutOfMemory, +const InnerError = codegen.EmitError || error{ EmitFail, }; @@ -175,21 +175,21 @@ fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void { fn mirDebugPrologueEnd(emit: *Emit) !void { switch (emit.debug_output) { - .dwarf => |dbg_out| { + inline .dwarf, .dwarf2 => |dbg_out| { try dbg_out.setPrologueEnd(); try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); }, - .none => {}, + .eh_frame, .none => {}, } } fn mirDebugEpilogueBegin(emit: *Emit) !void { switch (emit.debug_output) { - .dwarf => |dbg_out| { + inline .dwarf, .dwarf2 => |dbg_out| { try dbg_out.setEpilogueBegin(); try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); }, - .none => {}, + .eh_frame, .none => {}, } } @@ -496,13 +496,13 @@ fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) !void { const delta_line = @as(i32, @intCast(line)) - @as(i32, @intCast(emit.prev_di_line)); const delta_pc: usize = emit.w.end - emit.prev_di_pc; switch (emit.debug_output) { - .dwarf => |dbg_out| { - try dbg_out.advancePCAndLine(delta_line, delta_pc); + inline .dwarf, .dwarf2 => |dbg_out| { + try dbg_out.advancePcAndLine(delta_line, delta_pc); emit.prev_di_line = line; emit.prev_di_column = column; emit.prev_di_pc = emit.w.end; }, - else => {}, + .eh_frame, .none => {}, } } diff --git a/src/codegen/sparc64/Mir.zig b/src/codegen/sparc64/Mir.zig index 9b701e4e7654fd1fb36334e02f5bd3da7e3c9f14..aa5c204d96697c8afe9d9869c51faf67bfd96f99 100644 --- a/src/codegen/sparc64/Mir.zig +++ b/src/codegen/sparc64/Mir.zig @@ -382,7 +382,7 @@ pub fn emit( atom_index: link.File.AtomId, w: *std.Io.Writer, debug_output: link.File.DebugInfoOutput, -) (codegen.Error || std.Io.Writer.Error)!void { +) codegen.EmitError!void { _ = atom_index; const zcu = pt.zcu; const func = zcu.funcInfo(func_index); diff --git a/src/codegen/x86_64/CodeGen.zig b/src/codegen/x86_64/CodeGen.zig index 506a5a6a0ddfd66e30bec64676fef2ba266ec99e..f44fc72d83e9b4fabc55d20b80d4de0658d0c0ca 100644 --- a/src/codegen/x86_64/CodeGen.zig +++ b/src/codegen/x86_64/CodeGen.zig @@ -1120,7 +1120,7 @@ pub fn generateLazy( atom_id: link.File.AtomId, w: *std.Io.Writer, debug_output: link.File.DebugInfoOutput, -) codegen.Error!void { +) codegen.EmitError!void { const gpa = pt.zcu.gpa; // This function is for generating global code, so we use the root module. const mod = pt.zcu.comp.root_mod; diff --git a/src/codegen/x86_64/Emit.zig b/src/codegen/x86_64/Emit.zig index 0852a6d5ce77f5d40e20149e5cac7b15d64ea3b3..6523d7f638b40721d0247496b453acd35af1e37a 100644 --- a/src/codegen/x86_64/Emit.zig +++ b/src/codegen/x86_64/Emit.zig @@ -16,10 +16,8 @@ code_offset_mapping: std.ArrayList(u32), relocs: std.ArrayList(Reloc), table_relocs: std.ArrayList(TableReloc), -pub const Error = Lower.Error || error{ - AlreadyReported, +pub const Error = Lower.Error || codegen.EmitError || error{ EmitFail, - NotFile, } || std.posix.MMapError || std.posix.MRemapError || link.File.UpdateDebugInfoError; pub fn emitMir(emit: *Emit) Error!void { @@ -38,7 +36,7 @@ pub fn emitMir(emit: *Emit) Error!void { if (lowered_inst.prefix == .directive) { const start_offset: u32 = @intCast(emit.w.end); switch (emit.debug_output) { - .dwarf => |dwarf| switch (lowered_inst.encoding.mnemonic) { + inline .dwarf, .eh_frame, .dwarf2 => |dwarf| switch (lowered_inst.encoding.mnemonic) { .@".cfi_def_cfa" => try dwarf.genDebugFrame(start_offset, .{ .def_cfa = .{ .reg = lowered_inst.ops[0].reg.dwarfNum(), .off = lowered_inst.ops[1].imm.signed, @@ -460,204 +458,207 @@ pub fn emitMir(emit: *Emit) Error!void { if (lowered.insts.len == 0) { const mir_inst = emit.lower.mir.instructions.get(mir_index); - switch (mir_inst.tag) { + assert(mir_inst.tag == .pseudo); + switch (mir_inst.ops) { else => unreachable, - .pseudo => switch (mir_inst.ops) { - else => unreachable, - .pseudo_dbg_prologue_end_none => switch (emit.debug_output) { - .dwarf => |dwarf| try dwarf.setPrologueEnd(), - .none => {}, + .pseudo_dbg_prologue_end_none => switch (emit.debug_output) { + inline .dwarf, .dwarf2 => |dwarf| try dwarf.setPrologueEnd(), + .eh_frame, .none => {}, + }, + .pseudo_dbg_line_stmt_line_column => try emit.dbgAdvancePcAndLine(.{ + .line = mir_inst.data.line_column.line, + .column = mir_inst.data.line_column.column, + .is_stmt = true, + }), + .pseudo_dbg_line_line_column => try emit.dbgAdvancePcAndLine(.{ + .line = mir_inst.data.line_column.line, + .column = mir_inst.data.line_column.column, + .is_stmt = false, + }), + .pseudo_dbg_epilogue_begin_none => switch (emit.debug_output) { + inline .dwarf, .dwarf2 => |dwarf| { + try dwarf.setEpilogueBegin(); + log.debug("mirDbgEpilogueBegin (line={d}, col={d})", .{ + emit.prev_di_loc.line, emit.prev_di_loc.column, + }); + try emit.dbgAdvancePcAndLine(emit.prev_di_loc); }, - .pseudo_dbg_line_stmt_line_column => try emit.dbgAdvancePCAndLine(.{ - .line = mir_inst.data.line_column.line, - .column = mir_inst.data.line_column.column, - .is_stmt = true, - }), - .pseudo_dbg_line_line_column => try emit.dbgAdvancePCAndLine(.{ - .line = mir_inst.data.line_column.line, - .column = mir_inst.data.line_column.column, - .is_stmt = false, - }), - .pseudo_dbg_epilogue_begin_none => switch (emit.debug_output) { - .dwarf => |dwarf| { - try dwarf.setEpilogueBegin(); - log.debug("mirDbgEpilogueBegin (line={d}, col={d})", .{ - emit.prev_di_loc.line, emit.prev_di_loc.column, - }); - try emit.dbgAdvancePCAndLine(emit.prev_di_loc); - }, - .none => {}, + .eh_frame, .none => {}, + }, + .pseudo_dbg_enter_block_none => switch (emit.debug_output) { + inline .dwarf, .dwarf2 => |dwarf| { + log.debug("mirDbgEnterBlock (line={d}, col={d})", .{ + emit.prev_di_loc.line, emit.prev_di_loc.column, + }); + try dwarf.enterBlock(emit.w.end); }, - .pseudo_dbg_enter_block_none => switch (emit.debug_output) { - .dwarf => |dwarf| { - log.debug("mirDbgEnterBlock (line={d}, col={d})", .{ - emit.prev_di_loc.line, emit.prev_di_loc.column, - }); - try dwarf.enterBlock(emit.w.end); - }, - .none => {}, + .eh_frame, .none => {}, + }, + .pseudo_dbg_leave_block_none => switch (emit.debug_output) { + inline .dwarf, .dwarf2 => |dwarf| { + log.debug("mirDbgLeaveBlock (line={d}, col={d})", .{ + emit.prev_di_loc.line, emit.prev_di_loc.column, + }); + try dwarf.leaveBlock(emit.w.end); }, - .pseudo_dbg_leave_block_none => switch (emit.debug_output) { - .dwarf => |dwarf| { - log.debug("mirDbgLeaveBlock (line={d}, col={d})", .{ - emit.prev_di_loc.line, emit.prev_di_loc.column, - }); - try dwarf.leaveBlock(emit.w.end); - }, - .none => {}, + .eh_frame, .none => {}, + }, + .pseudo_dbg_enter_inline_func => switch (emit.debug_output) { + inline .dwarf, .dwarf2 => |dwarf| { + log.debug("mirDbgEnterInline (line={d}, col={d})", .{ + emit.prev_di_loc.line, emit.prev_di_loc.column, + }); + try dwarf.enterInlineFunc(mir_inst.data.ip_index, emit.w.end, emit.prev_di_loc.line, emit.prev_di_loc.column); }, - .pseudo_dbg_enter_inline_func => switch (emit.debug_output) { - .dwarf => |dwarf| { - log.debug("mirDbgEnterInline (line={d}, col={d})", .{ - emit.prev_di_loc.line, emit.prev_di_loc.column, - }); - try dwarf.enterInlineFunc(mir_inst.data.ip_index, emit.w.end, emit.prev_di_loc.line, emit.prev_di_loc.column); - }, - .none => {}, + .eh_frame, .none => {}, + }, + .pseudo_dbg_leave_inline_func => switch (emit.debug_output) { + inline .dwarf, .dwarf2 => |dwarf| { + log.debug("mirDbgLeaveInline (line={d}, col={d})", .{ + emit.prev_di_loc.line, emit.prev_di_loc.column, + }); + try dwarf.leaveInlineFunc(mir_inst.data.ip_index, emit.w.end); }, - .pseudo_dbg_leave_inline_func => switch (emit.debug_output) { - .dwarf => |dwarf| { - log.debug("mirDbgLeaveInline (line={d}, col={d})", .{ - emit.prev_di_loc.line, emit.prev_di_loc.column, - }); - try dwarf.leaveInlineFunc(mir_inst.data.ip_index, emit.w.end); - }, - .none => {}, - }, - .pseudo_dbg_arg_none, - .pseudo_dbg_arg_i_s, - .pseudo_dbg_arg_i_u, - .pseudo_dbg_arg_i_64, - .pseudo_dbg_arg_ro, - .pseudo_dbg_arg_fa, - .pseudo_dbg_arg_m, - .pseudo_dbg_var_none, - .pseudo_dbg_var_i_s, - .pseudo_dbg_var_i_u, - .pseudo_dbg_var_i_64, - .pseudo_dbg_var_ro, - .pseudo_dbg_var_fa, - .pseudo_dbg_var_m, - => switch (emit.debug_output) { - .dwarf => |dwarf| { - var loc_buf: [2]link.File.Dwarf.Loc = undefined; - const loc: link.File.Dwarf.Loc = loc: switch (mir_inst.ops) { + .eh_frame, .none => {}, + }, + .pseudo_dbg_arg_none, + .pseudo_dbg_arg_i_s, + .pseudo_dbg_arg_i_u, + .pseudo_dbg_arg_i_64, + .pseudo_dbg_arg_ro, + .pseudo_dbg_arg_fa, + .pseudo_dbg_arg_m, + .pseudo_dbg_var_none, + .pseudo_dbg_var_i_s, + .pseudo_dbg_var_i_u, + .pseudo_dbg_var_i_64, + .pseudo_dbg_var_ro, + .pseudo_dbg_var_fa, + .pseudo_dbg_var_m, + => switch (emit.debug_output) { + inline .dwarf, .dwarf2 => |dwarf, tag| { + const DwarfLoc = switch (tag) { + .dwarf => link.File.Dwarf.Loc, + .dwarf2 => link.File.Dwarf2.Loc, + .eh_frame, .none => comptime unreachable, + }; + var loc_buf: [2]DwarfLoc = undefined; + const loc: DwarfLoc = loc: switch (mir_inst.ops) { + else => unreachable, + .pseudo_dbg_arg_none, .pseudo_dbg_var_none => .empty, + .pseudo_dbg_arg_i_s, + .pseudo_dbg_arg_i_u, + .pseudo_dbg_var_i_s, + .pseudo_dbg_var_i_u, + => .{ .stack_value = stack_value: { + loc_buf[0] = switch (emit.lower.imm(mir_inst.ops, mir_inst.data.i.i)) { + .signed => |s| .{ .consts = s }, + .unsigned => |u| .{ .constu = u }, + }; + break :stack_value &loc_buf[0]; + } }, + .pseudo_dbg_arg_i_64, .pseudo_dbg_var_i_64 => .{ .stack_value = stack_value: { + loc_buf[0] = .{ .constu = mir_inst.data.i64 }; + break :stack_value &loc_buf[0]; + } }, + .pseudo_dbg_arg_fa, .pseudo_dbg_var_fa => { + const reg_off = emit.lower.mir.resolveFrameAddr(mir_inst.data.fa); + break :loc .{ .plus = .{ + reg: { + loc_buf[0] = .{ .breg = reg_off.reg.dwarfNum() }; + break :reg &loc_buf[0]; + }, + off: { + loc_buf[1] = .{ .consts = reg_off.off }; + break :off &loc_buf[1]; + }, + } }; + }, + .pseudo_dbg_arg_m, .pseudo_dbg_var_m => { + const mem = emit.lower.mir.resolveMemoryExtra(mir_inst.data.x.payload).decode(); + break :loc .{ .plus = .{ + base: { + loc_buf[0] = switch (mem.base()) { + .none => .{ .constu = 0 }, + .reg => |reg| .{ .breg = reg.dwarfNum() }, + .frame, .table, .rip_inst => unreachable, + .nav => |nav| .{ .addr_reloc = try codegen.genNavRef( + emit.bin_file, + emit.pt, + nav, + ) }, + .uav => |uav| .{ .addr_reloc = try emit.bin_file.lowerUav( + emit.pt, + uav.val, + Type.fromInterned(uav.orig_ty).ptrAlignment(emit.pt.zcu), + ) }, + .lazy_sym, .extern_func => unreachable, + }; + break :base &loc_buf[0]; + }, + disp: { + loc_buf[1] = switch (mem.disp()) { + .signed => |s| .{ .consts = s }, + .unsigned => |u| .{ .constu = u }, + }; + break :disp &loc_buf[1]; + }, + } }; + }, + }; + + const local = &emit.lower.mir.locals[local_index]; + local_index += 1; + try dwarf.genLocalVarDebugInfo( + switch (mir_inst.ops) { else => unreachable, - .pseudo_dbg_arg_none, .pseudo_dbg_var_none => .empty, + .pseudo_dbg_arg_none, .pseudo_dbg_arg_i_s, .pseudo_dbg_arg_i_u, + .pseudo_dbg_arg_i_64, + .pseudo_dbg_arg_ro, + .pseudo_dbg_arg_fa, + .pseudo_dbg_arg_m, + .pseudo_dbg_arg_val, + => .arg, + .pseudo_dbg_var_none, .pseudo_dbg_var_i_s, .pseudo_dbg_var_i_u, - => .{ .stack_value = stack_value: { - loc_buf[0] = switch (emit.lower.imm(mir_inst.ops, mir_inst.data.i.i)) { - .signed => |s| .{ .consts = s }, - .unsigned => |u| .{ .constu = u }, - }; - break :stack_value &loc_buf[0]; - } }, - .pseudo_dbg_arg_i_64, .pseudo_dbg_var_i_64 => .{ .stack_value = stack_value: { - loc_buf[0] = .{ .constu = mir_inst.data.i64 }; - break :stack_value &loc_buf[0]; - } }, - .pseudo_dbg_arg_fa, .pseudo_dbg_var_fa => { - const reg_off = emit.lower.mir.resolveFrameAddr(mir_inst.data.fa); - break :loc .{ .plus = .{ - reg: { - loc_buf[0] = .{ .breg = reg_off.reg.dwarfNum() }; - break :reg &loc_buf[0]; - }, - off: { - loc_buf[1] = .{ .consts = reg_off.off }; - break :off &loc_buf[1]; - }, - } }; - }, - .pseudo_dbg_arg_m, .pseudo_dbg_var_m => { - const mem = emit.lower.mir.resolveMemoryExtra(mir_inst.data.x.payload).decode(); - break :loc .{ .plus = .{ - base: { - loc_buf[0] = switch (mem.base()) { - .none => .{ .constu = 0 }, - .reg => |reg| .{ .breg = reg.dwarfNum() }, - .frame, .table, .rip_inst => unreachable, - .nav => |nav| .{ .addr_reloc = try codegen.genNavRef( - emit.bin_file, - emit.pt, - nav, - ) }, - .uav => |uav| .{ .addr_reloc = try emit.bin_file.lowerUav( - emit.pt, - uav.val, - Type.fromInterned(uav.orig_ty).ptrAlignment(emit.pt.zcu), - ) }, - .lazy_sym, .extern_func => unreachable, - }; - break :base &loc_buf[0]; - }, - disp: { - loc_buf[1] = switch (mem.disp()) { - .signed => |s| .{ .consts = s }, - .unsigned => |u| .{ .constu = u }, - }; - break :disp &loc_buf[1]; - }, - } }; - }, - }; - - const local = &emit.lower.mir.locals[local_index]; - local_index += 1; - try dwarf.genLocalVarDebugInfo( - switch (mir_inst.ops) { - else => unreachable, - .pseudo_dbg_arg_none, - .pseudo_dbg_arg_i_s, - .pseudo_dbg_arg_i_u, - .pseudo_dbg_arg_i_64, - .pseudo_dbg_arg_ro, - .pseudo_dbg_arg_fa, - .pseudo_dbg_arg_m, - .pseudo_dbg_arg_val, - => .arg, - .pseudo_dbg_var_none, - .pseudo_dbg_var_i_s, - .pseudo_dbg_var_i_u, - .pseudo_dbg_var_i_64, - .pseudo_dbg_var_ro, - .pseudo_dbg_var_fa, - .pseudo_dbg_var_m, - .pseudo_dbg_var_val, - => .local_var, - }, - local.name.toSlice(&emit.lower.mir), - .fromInterned(local.type), - loc, - ); - }, - .none => local_index += 1, + .pseudo_dbg_var_i_64, + .pseudo_dbg_var_ro, + .pseudo_dbg_var_fa, + .pseudo_dbg_var_m, + .pseudo_dbg_var_val, + => .local_var, + }, + local.name.toSlice(&emit.lower.mir), + .fromInterned(local.type), + loc, + ); }, - .pseudo_dbg_arg_val, .pseudo_dbg_var_val => switch (emit.debug_output) { - .dwarf => |dwarf| { - const local = &emit.lower.mir.locals[local_index]; - local_index += 1; - try dwarf.genLocalConstDebugInfo( - switch (mir_inst.ops) { - else => unreachable, - .pseudo_dbg_arg_val => .comptime_arg, - .pseudo_dbg_var_val => .local_const, - }, - local.name.toSlice(&emit.lower.mir), - .fromInterned(mir_inst.data.ip_index), - ); - }, - .none => local_index += 1, - }, - .pseudo_dbg_var_args_none => switch (emit.debug_output) { - .dwarf => |dwarf| try dwarf.genVarArgsDebugInfo(), - .none => {}, + .eh_frame, .none => local_index += 1, + }, + .pseudo_dbg_arg_val, .pseudo_dbg_var_val => switch (emit.debug_output) { + inline .dwarf, .dwarf2 => |dwarf| { + const local = &emit.lower.mir.locals[local_index]; + local_index += 1; + try dwarf.genLocalConstDebugInfo( + switch (mir_inst.ops) { + else => unreachable, + .pseudo_dbg_arg_val => .comptime_arg, + .pseudo_dbg_var_val => .local_const, + }, + local.name.toSlice(&emit.lower.mir), + .fromInterned(mir_inst.data.ip_index), + ); }, - .pseudo_dead_none => {}, + .eh_frame, .none => local_index += 1, + }, + .pseudo_dbg_var_args_none => switch (emit.debug_output) { + inline .dwarf, .dwarf2 => |dwarf| try dwarf.genVarArgsDebugInfo(), + .eh_frame, .none => {}, }, + .pseudo_dead_none => {}, } } } @@ -974,19 +975,19 @@ const Loc = struct { is_stmt: bool, }; -fn dbgAdvancePCAndLine(emit: *Emit, loc: Loc) Error!void { +fn dbgAdvancePcAndLine(emit: *Emit, loc: Loc) Error!void { const delta_line = @as(i33, loc.line) - @as(i33, emit.prev_di_loc.line); const delta_pc: usize = emit.w.end - emit.prev_di_pc; log.debug(" (advance pc={d} and line={d})", .{ delta_pc, delta_line }); switch (emit.debug_output) { - .dwarf => |dwarf| { + inline .dwarf, .dwarf2 => |dwarf| { if (loc.is_stmt != emit.prev_di_loc.is_stmt) try dwarf.negateStmt(); if (loc.column != emit.prev_di_loc.column) try dwarf.setColumn(loc.column); - try dwarf.advancePCAndLine(delta_line, delta_pc); + try dwarf.advancePcAndLine(delta_line, delta_pc); emit.prev_di_loc = loc; emit.prev_di_pc = emit.w.end; }, - .none => {}, + .eh_frame, .none => {}, } } diff --git a/src/codegen/x86_64/Mir.zig b/src/codegen/x86_64/Mir.zig index 274437d54ccf55ce4fb47470e7a82b607c4bf0d2..dd385854d441428dd1b657ae22ee1ce737b4dc86 100644 --- a/src/codegen/x86_64/Mir.zig +++ b/src/codegen/x86_64/Mir.zig @@ -1978,7 +1978,7 @@ pub fn emit( atom_id: link.File.AtomId, w: *std.Io.Writer, debug_output: link.File.DebugInfoOutput, -) codegen.Error!void { +) codegen.EmitError!void { const zcu = pt.zcu; const comp = zcu.comp; const gpa = comp.gpa; @@ -1986,7 +1986,7 @@ pub fn emit( const fn_info = zcu.typeToFunc(.fromInterned(func.ty)).?; const nav = func.owner_nav; const mod = zcu.navFileScope(nav).mod.?; - var e: Emit = .{ + var em: Emit = .{ .lower = .{ .target = &mod.resolved_target.result, .allocator = gpa, @@ -2006,7 +2006,8 @@ pub fn emit( .column = func.lbrace_column, .is_stmt = switch (debug_output) { .dwarf => |dwarf| dwarf.dwarf.debug_line.header.default_is_stmt, - .none => undefined, + .dwarf2 => |dwarf| dwarf.wip_nav.dwarf.debug_line.header.default_is_stmt, + .eh_frame, .none => undefined, }, }, .prev_di_pc = 0, @@ -2015,11 +2016,12 @@ pub fn emit( .relocs = .empty, .table_relocs = .empty, }; - defer e.deinit(); - e.emitMir() catch |err| switch (err) { - error.LowerFail, error.EmitFail => return zcu.codegenFailMsg(nav, e.lower.err_msg.?), + defer em.deinit(); + em.emitMir() catch |err| switch (err) { + error.LowerFail, error.EmitFail => return zcu.codegenFailMsg(nav, em.lower.err_msg.?), error.InvalidInstruction, error.CannotEncode => return zcu.codegenFail(nav, "emit MIR failed: {s} (Zig compiler bug)", .{@errorName(err)}), else => return zcu.codegenFail(nav, "emit MIR failed: {s}", .{@errorName(err)}), + error.AlreadyReported, error.Canceled, error.MappedFileIo, error.WriteFailed => |e| return e, }; } @@ -2031,12 +2033,12 @@ pub fn emitLazy( atom_id: link.File.AtomId, w: *std.Io.Writer, debug_output: link.File.DebugInfoOutput, -) codegen.Error!void { +) codegen.EmitError!void { const zcu = pt.zcu; const comp = zcu.comp; const gpa = comp.gpa; const mod = comp.root_mod; - var e: Emit = .{ + var em: Emit = .{ .lower = .{ .target = &mod.resolved_target.result, .allocator = gpa, @@ -2058,11 +2060,12 @@ pub fn emitLazy( .relocs = .empty, .table_relocs = .empty, }; - defer e.deinit(); - e.emitMir() catch |err| switch (err) { - error.LowerFail, error.EmitFail => return zcu.codegenFailTypeMsg(lazy_sym.ty, e.lower.err_msg.?), + defer em.deinit(); + em.emitMir() catch |err| switch (err) { + error.LowerFail, error.EmitFail => return zcu.codegenFailTypeMsg(lazy_sym.ty, em.lower.err_msg.?), error.InvalidInstruction, error.CannotEncode => return zcu.codegenFailType(lazy_sym.ty, "emit MIR failed: {s} (Zig compiler bug)", .{@errorName(err)}), else => return zcu.codegenFailType(lazy_sym.ty, "emit MIR failed: {s}", .{@errorName(err)}), + error.AlreadyReported, error.Canceled, error.MappedFileIo, error.WriteFailed => |e| return e, }; } diff --git a/src/link.zig b/src/link.zig index 9e200d1d1c850062ccfc5e95cfa10e74fcd98411..158570be706b99cbcf650d34b18fd963ec49070a 100644 --- a/src/link.zig +++ b/src/link.zig @@ -757,6 +757,8 @@ pub const File = struct { pub const DebugInfoOutput = union(enum) { dwarf: *Dwarf.WipNav, + eh_frame: *Dwarf2.WipNav, + dwarf2: *Dwarf2.WipNav.Debug, none, }; pub const UpdateDebugInfoError = Dwarf.UpdateError; @@ -1391,6 +1393,7 @@ pub const File = struct { pub const SpirV = @import("link/SpirV.zig"); pub const Wasm = @import("link/Wasm.zig"); pub const Dwarf = @import("link/Dwarf.zig"); + pub const Dwarf2 = @import("link/Dwarf2.zig"); }; pub const PrelinkTask = union(enum) { diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 78f3287d8e014e3fb37f51fd7fc2355b2ccbbeef..59c9952afba6642c665f529364fd0560af479c5c 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -1626,12 +1626,12 @@ pub const WipNav = struct { wip_nav.any_children = true; } - pub fn advancePCAndLine(wip_nav: *WipNav, delta_line: i33, delta_pc: u64) Allocator.Error!void { - return wip_nav.advancePCAndLineWriterError(delta_line, delta_pc) catch |err| switch (err) { + pub fn advancePcAndLine(wip_nav: *WipNav, delta_line: i33, delta_pc: u64) Allocator.Error!void { + return wip_nav.advancePcAndLineWriterError(delta_line, delta_pc) catch |err| switch (err) { error.WriteFailed => error.OutOfMemory, }; } - fn advancePCAndLineWriterError( + fn advancePcAndLineWriterError( wip_nav: *WipNav, delta_line: i33, delta_pc: u64, @@ -1990,7 +1990,7 @@ pub const WipNav = struct { try ctx.wip_nav.infoSectionOffset(.debug_info, unit, entry, 0); } } = .{ .wip_nav = wip_nav }; - try adapter.writer().writeUleb128(counter.dw.count + counter.dw.writer.end); + try adapter.writer().writeUleb128(counter.dw.fullCount()); try loc.write(adapter); } @@ -2032,7 +2032,7 @@ pub const WipNav = struct { try ctx.wip_nav.sectionOffset(.debug_frame, .debug_info, unit, entry, 0); } } = .{ .wip_nav = wip_nav }; - try adapter.writer().writeUleb128(counter.dw.count + counter.dw.writer.end); + try adapter.writer().writeUleb128(counter.dw.fullCount()); try loc.write(adapter); } @@ -2105,20 +2105,20 @@ pub const WipNav = struct { const size = ty.abiSize(wip_nav.pt.zcu); try diw.writeUleb128(size); if (size == 0) return; - const old_end = wip_nav.debug_info.writer.end; + const old_end = diw.end; try codegen.generateSymbol( wip_nav.dwarf.bin_file, wip_nav.pt, val, - &wip_nav.debug_info.writer, + diw, .{ .debug_output = .{ .dwarf = wip_nav } }, ); - if (old_end + size != wip_nav.debug_info.writer.end) { + if (old_end + size != diw.end) { std.debug.print("{f} [{}]: {} != {}\n", .{ ty.fmt(wip_nav.pt), ty.toIntern(), size, - wip_nav.debug_info.writer.end - old_end, + diw.end - old_end, }); unreachable; } @@ -2278,10 +2278,9 @@ fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { 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, + .gpa = comp.gpa, .bin_file = lf, .format = format, .address_size = switch (target.ptrBitWidth()) { @@ -2774,7 +2773,7 @@ fn initWipNavInner( try dlw.writeByte(DW.LNS.set_column); try dlw.writeUleb128(func.lbrace_column + 1); - try wip_nav.advancePCAndLine(func.lbrace_line, 0); + try wip_nav.advancePcAndLine(func.lbrace_line, 0); } else { try dlw.writeUleb128(1 + @backingInt(dwarf.address_size)); try dlw.writeByte(DW.LNE.set_address); @@ -2791,7 +2790,7 @@ fn initWipNavInner( try dlw.writeByte(DW.LNS.set_column); try dlw.writeUleb128(func.lbrace_column + 1); - try wip_nav.advancePCAndLine(@intCast(decl.src_line + func.lbrace_line), 0); + try wip_nav.advancePcAndLine(@intCast(decl.src_line + func.lbrace_line), 0); } }, else => { @@ -6362,14 +6361,14 @@ fn uleb128Bytes(value: anytype) u32 { var buf: [64]u8 = undefined; var dw: Writer.Discarding = .init(&buf); dw.writer.writeUleb128(value) catch unreachable; - return @intCast(dw.count + dw.writer.end); + return @intCast(dw.fullCount()); } fn sleb128Bytes(value: anytype) u32 { var buf: [64]u8 = undefined; var dw: Writer.Discarding = .init(&buf); dw.writer.writeSleb128(value) catch unreachable; - return @intCast(dw.count + dw.writer.end); + return @intCast(dw.fullCount()); } /// overrides `-fno-incremental` for testing incremental debug info until `-fincremental` is functional diff --git a/src/link/Dwarf2.zig b/src/link/Dwarf2.zig new file mode 100644 index 0000000000000000000000000000000000000000..2d49814581426bfcdce4022532620e1d1e3b9d87 --- /dev/null +++ b/src/link/Dwarf2.zig @@ -0,0 +1,2685 @@ +tag: link.File.Tag, +format: DW.Format, +endian: std.lang.Endian, +address_size: AddressSize, +const_pool: link.ConstPool, + +units: std.array_hash_map.Auto(*Module, Unit), +/// Indices are `link.ConstPool.Index`. +values: std.ArrayList(struct { + debug_info_ni: MappedFile.Node.Index, +}), +globals: std.array_hash_map.Auto(InternPool.Nav.Index, Global), +funcs: std.array_hash_map.Auto(InternPool.Nav.Index, Func), + +frame: Frame, +debug_info: DebugInfo, +debug_line: DebugLine, + +pub const UpdateError = link.Error || error{MappedFileIo}; + +pub const AddressSize = enum(u8) { @"32" = 4, @"64" = 8, _ }; + +pub const Unit = struct { + frame_ni: MappedFile.Node.Index.Optional, + cie_ni: MappedFile.Node.Index.Optional, + + pub const Index = enum(u32) { + _, + + pub fn mod(ui: Unit.Index, dwarf: *Dwarf) *Module { + return dwarf.units.keys()[@backingInt(ui)]; + } + + pub fn get(ui: Unit.Index, dwarf: *Dwarf) *Unit { + return &dwarf.units.values()[@backingInt(ui)]; + } + }; +}; + +pub const Global = struct { + debug_info_ni: MappedFile.Node.Index.Optional, + + pub const Index = enum(u32) { + _, + + pub fn nav(gi: Global.Index, dwarf: *Dwarf) InternPool.Nav.Index { + return dwarf.globals.keys()[@backingInt(gi)]; + } + + pub fn get(gi: Global.Index, dwarf: *Dwarf) *Global { + return &dwarf.globals.values()[@backingInt(gi)]; + } + }; +}; + +pub const Func = struct { + fde_ni: MappedFile.Node.Index.Optional, + debug_info_ni: MappedFile.Node.Index.Optional, + debug_line_ni: MappedFile.Node.Index.Optional, + + pub const Index = enum(u32) { + _, + + pub fn nav(fi: Func.Index, dwarf: *Dwarf) InternPool.Nav.Index { + return dwarf.funcs.keys()[@backingInt(fi)]; + } + + pub fn get(fi: Func.Index, dwarf: *Dwarf) *Func { + return &dwarf.funcs.values()[@backingInt(fi)]; + } + }; +}; + +pub const Frame = struct { + header: Header, + section_index: SectionIndex, + + pub const Header = struct { + code_alignment_factor: u32, + data_alignment_factor: i32, + return_address_register: u32, + initial_instructions: []const Cfa, + }; + + pub const Format = std.debug.Dwarf.Unwind.Section; +}; + +pub const DebugInfo = struct { + section_index: SectionIndex, +}; + +pub const DebugLine = struct { + header: Header, + section_index: SectionIndex, + + pub 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, + }; +}; + +pub const SectionIndex = enum(u32) { none = std.math.maxInt(u32), _ }; + +pub const Loc = union(enum) { + empty, + addr_reloc: link.File.SymbolId, + deref: *const Loc, + constu: u64, + consts: i64, + plus: Bin, + reg: u32, + breg: u32, + push_object_address, + call: struct { + args: []const Loc = &.{}, + node: MappedFile.Node.Index, + }, + form_tls_address: *const Loc, + implicit_value: []const u8, + stack_value: *const Loc, + implicit_pointer: struct { + node: MappedFile.Node.Index, + offset: i65, + }, + wasm_ext: union(enum) { + local: u32, + global: u32, + operand_stack: u32, + }, + + pub const Bin = struct { *const Loc, *const Loc }; + + fn getConst(loc: Loc, comptime Int: type) ?Int { + return switch (loc) { + .constu => |constu| std.math.cast(Int, constu), + .consts => |consts| std.math.cast(Int, consts), + else => null, + }; + } + + fn getBaseReg(loc: Loc) ?u32 { + return switch (loc) { + .breg => |breg| breg, + else => null, + }; + } + + fn writeReg(reg: u32, op0: u8, opx: u8, writer: *Writer) Writer.Error!void { + if (std.math.cast(u5, reg)) |small_reg| { + try writer.writeByte(op0 + small_reg); + } else { + try writer.writeByte(opx); + try writer.writeUleb128(reg); + } + } + + fn write(loc: Loc, adapter: anytype) (UpdateError || Writer.Error)!void { + const writer = adapter.writer(); + switch (loc) { + .empty => {}, + .addr_reloc => |si| { + try writer.writeByte(DW.OP.addr); + try adapter.addrSym(si); + }, + .deref => |addr| { + try addr.write(adapter); + try writer.writeByte(DW.OP.deref); + }, + .constu => |constu| if (std.math.cast(u5, constu)) |lit| { + try writer.writeByte(@as(u8, DW.OP.lit0) + lit); + } else if (std.math.cast(u8, constu)) |const1u| { + try writer.writeAll(&.{ DW.OP.const1u, const1u }); + } else if (std.math.cast(u16, constu)) |const2u| { + try writer.writeByte(DW.OP.const2u); + try writer.writeInt(u16, const2u, adapter.endian()); + } else if (std.math.cast(u21, constu)) |const3u| { + try writer.writeByte(DW.OP.constu); + try writer.writeUleb128(const3u); + } else if (std.math.cast(u32, constu)) |const4u| { + try writer.writeByte(DW.OP.const4u); + try writer.writeInt(u32, const4u, adapter.endian()); + } else if (std.math.cast(u49, constu)) |const7u| { + try writer.writeByte(DW.OP.constu); + try writer.writeUleb128(const7u); + } else { + try writer.writeByte(DW.OP.const8u); + try writer.writeInt(u64, constu, adapter.endian()); + }, + .consts => |consts| if (std.math.cast(i8, consts)) |const1s| { + try writer.writeAll(&.{ DW.OP.const1s, @bitCast(const1s) }); + } else if (std.math.cast(i16, consts)) |const2s| { + try writer.writeByte(DW.OP.const2s); + try writer.writeInt(i16, const2s, adapter.endian()); + } else if (std.math.cast(i21, consts)) |const3s| { + try writer.writeByte(DW.OP.consts); + try writer.writeSleb128(const3s); + } else if (std.math.cast(i32, consts)) |const4s| { + try writer.writeByte(DW.OP.const4s); + try writer.writeInt(i32, const4s, adapter.endian()); + } else if (std.math.cast(i49, consts)) |const7s| { + try writer.writeByte(DW.OP.consts); + try writer.writeSleb128(const7s); + } else { + try writer.writeByte(DW.OP.const8s); + try writer.writeInt(i64, consts, adapter.endian()); + }, + .plus => |plus| done: { + if (plus[0].getConst(u0)) |_| { + try plus[1].write(adapter); + break :done; + } + if (plus[1].getConst(u0)) |_| { + try plus[0].write(adapter); + break :done; + } + if (plus[0].getBaseReg()) |breg| { + if (plus[1].getConst(i65)) |offset| { + try writeReg(breg, DW.OP.breg0, DW.OP.bregx, writer); + try writer.writeSleb128(offset); + break :done; + } + } + if (plus[1].getBaseReg()) |breg| { + if (plus[0].getConst(i65)) |offset| { + try writeReg(breg, DW.OP.breg0, DW.OP.bregx, writer); + try writer.writeSleb128(offset); + break :done; + } + } + if (plus[0].getConst(u64)) |uconst| { + try plus[1].write(adapter); + try writer.writeByte(DW.OP.plus_uconst); + try writer.writeUleb128(uconst); + break :done; + } + if (plus[1].getConst(u64)) |uconst| { + try plus[0].write(adapter); + try writer.writeByte(DW.OP.plus_uconst); + try writer.writeUleb128(uconst); + break :done; + } + try plus[0].write(adapter); + try plus[1].write(adapter); + try writer.writeByte(DW.OP.plus); + }, + .reg => |reg| try writeReg(reg, DW.OP.reg0, DW.OP.regx, writer), + .breg => |breg| { + try writeReg(breg, DW.OP.breg0, DW.OP.bregx, writer); + try writer.writeSleb128(0); + }, + .push_object_address => try writer.writeByte(DW.OP.push_object_address), + .call => |call| { + for (call.args) |arg| try arg.write(adapter); + try writer.writeByte(DW.OP.call_ref); + try adapter.infoEntry(call.node); + }, + .form_tls_address => |addr| { + try addr.write(adapter); + try writer.writeByte(DW.OP.form_tls_address); + }, + .implicit_value => |value| { + try writer.writeByte(DW.OP.implicit_value); + try writer.writeUleb128(value.len); + try writer.writeAll(value); + }, + .stack_value => |value| { + try value.write(adapter); + try writer.writeByte(DW.OP.stack_value); + }, + .implicit_pointer => |implicit_pointer| { + try writer.writeByte(DW.OP.implicit_pointer); + try adapter.infoEntry(implicit_pointer.node); + try writer.writeSleb128(implicit_pointer.offset); + }, + .wasm_ext => |wasm_ext| { + try writer.writeByte(DW.OP.WASM_location); + switch (wasm_ext) { + .local => |local| { + try writer.writeByte(DW.OP.WASM_local); + try writer.writeUleb128(local); + }, + .global => |global| if (std.math.cast(u21, global)) |global_u21| { + try writer.writeByte(DW.OP.WASM_global); + try writer.writeUleb128(global_u21); + } else { + try writer.writeByte(DW.OP.WASM_global_u32); + try writer.writeInt(u32, global, adapter.endian()); + }, + .operand_stack => |operand_stack| { + try writer.writeByte(DW.OP.WASM_operand_stack); + try writer.writeUleb128(operand_stack); + }, + } + }, + } + } +}; + +pub const Cfa = union(enum) { + nop, + advance_loc: u32, + offset: RegOff, + rel_offset: RegOff, + restore: u32, + undefined: u32, + same_value: u32, + register: [2]u32, + remember_state, + restore_state, + def_cfa: RegOff, + def_cfa_register: u32, + def_cfa_offset: i64, + adjust_cfa_offset: i64, + def_cfa_expression: Loc, + expression: RegExpr, + val_offset: RegOff, + val_expression: RegExpr, + escape: []const u8, + + const RegOff = struct { reg: u32, off: i64 }; + const RegExpr = struct { reg: u32, expr: Loc }; + + fn write(cfa: Cfa, wip_nav: *WipNav) (UpdateError || Writer.Error)!void { + const dfw = &wip_nav.fde_writer.interface; + switch (cfa) { + .nop => try dfw.writeByte(DW.CFA.nop), + .advance_loc => |loc| { + const delta = + @divExact(loc - wip_nav.cfi.loc, wip_nav.dwarf.frame.header.code_alignment_factor); + if (delta == 0) {} else if (std.math.cast(u6, delta)) |small_delta| + try dfw.writeByte(@as(u8, DW.CFA.advance_loc) + small_delta) + else if (std.math.cast(u8, delta)) |ubyte_delta| + try dfw.writeAll(&.{ DW.CFA.advance_loc1, ubyte_delta }) + else if (std.math.cast(u16, delta)) |uhalf_delta| { + try dfw.writeByte(DW.CFA.advance_loc2); + try dfw.writeInt(u16, uhalf_delta, wip_nav.dwarf.endian); + } else if (std.math.cast(u32, delta)) |uword_delta| { + try dfw.writeByte(DW.CFA.advance_loc4); + try dfw.writeInt(u32, uword_delta, wip_nav.dwarf.endian); + } + wip_nav.cfi.loc = loc; + }, + .offset, .rel_offset => |reg_off| { + const factored_off = @divExact(reg_off.off - switch (cfa) { + else => unreachable, + .offset => 0, + .rel_offset => wip_nav.cfi.cfa.off, + }, wip_nav.dwarf.frame.header.data_alignment_factor); + if (std.math.cast(u63, factored_off)) |unsigned_off| { + if (std.math.cast(u6, reg_off.reg)) |small_reg| { + try dfw.writeByte(@as(u8, DW.CFA.offset) + small_reg); + } else { + try dfw.writeByte(DW.CFA.offset_extended); + try dfw.writeUleb128(reg_off.reg); + } + try dfw.writeUleb128(unsigned_off); + } else { + try dfw.writeByte(DW.CFA.offset_extended_sf); + try dfw.writeUleb128(reg_off.reg); + try dfw.writeSleb128(factored_off); + } + }, + .restore => |reg| if (std.math.cast(u6, reg)) |small_reg| + try dfw.writeByte(@as(u8, DW.CFA.restore) + small_reg) + else { + try dfw.writeByte(DW.CFA.restore_extended); + try dfw.writeUleb128(reg); + }, + .undefined => |reg| { + try dfw.writeByte(DW.CFA.undefined); + try dfw.writeUleb128(reg); + }, + .same_value => |reg| { + try dfw.writeByte(DW.CFA.same_value); + try dfw.writeUleb128(reg); + }, + .register => |regs| if (regs[0] != regs[1]) { + try dfw.writeByte(DW.CFA.register); + for (regs) |reg| try dfw.writeUleb128(reg); + } else { + try dfw.writeByte(DW.CFA.same_value); + try dfw.writeUleb128(regs[0]); + }, + .remember_state => try dfw.writeByte(DW.CFA.remember_state), + .restore_state => try dfw.writeByte(DW.CFA.restore_state), + .def_cfa, .def_cfa_register, .def_cfa_offset, .adjust_cfa_offset => { + const reg_off: RegOff = switch (cfa) { + else => unreachable, + .def_cfa => |reg_off| reg_off, + .def_cfa_register => |reg| .{ .reg = reg, .off = wip_nav.cfi.cfa.off }, + .def_cfa_offset => |off| .{ .reg = wip_nav.cfi.cfa.reg, .off = off }, + .adjust_cfa_offset => |off| .{ + .reg = wip_nav.cfi.cfa.reg, + .off = wip_nav.cfi.cfa.off + off, + }, + }; + const changed_reg = reg_off.reg != wip_nav.cfi.cfa.reg; + const unsigned_off = std.math.cast(u63, reg_off.off); + if (reg_off.off == wip_nav.cfi.cfa.off) { + if (changed_reg) { + try dfw.writeByte(DW.CFA.def_cfa_register); + try dfw.writeUleb128(reg_off.reg); + } + } else if (switch (wip_nav.dwarf.frame.header.data_alignment_factor) { + 0 => unreachable, + 1 => unsigned_off != null, + else => |data_alignment_factor| @rem(reg_off.off, data_alignment_factor) != 0, + }) { + try dfw.writeByte(if (changed_reg) DW.CFA.def_cfa else DW.CFA.def_cfa_offset); + if (changed_reg) try dfw.writeUleb128(reg_off.reg); + try dfw.writeUleb128(unsigned_off.?); + } else { + try dfw.writeByte(if (changed_reg) DW.CFA.def_cfa_sf else DW.CFA.def_cfa_offset_sf); + if (changed_reg) try dfw.writeUleb128(reg_off.reg); + try dfw.writeSleb128( + @divExact(reg_off.off, wip_nav.dwarf.frame.header.data_alignment_factor), + ); + } + wip_nav.cfi.cfa = reg_off; + }, + .def_cfa_expression => |expr| { + try dfw.writeByte(DW.CFA.def_cfa_expression); + try wip_nav.frameExprLoc(expr); + }, + .expression => |reg_expr| { + try dfw.writeByte(DW.CFA.expression); + try dfw.writeUleb128(reg_expr.reg); + try wip_nav.frameExprLoc(reg_expr.expr); + }, + .val_offset => |reg_off| { + const factored_off = + @divExact(reg_off.off, wip_nav.dwarf.frame.header.data_alignment_factor); + if (std.math.cast(u63, factored_off)) |unsigned_off| { + try dfw.writeByte(DW.CFA.val_offset); + try dfw.writeUleb128(reg_off.reg); + try dfw.writeUleb128(unsigned_off); + } else { + try dfw.writeByte(DW.CFA.val_offset_sf); + try dfw.writeUleb128(reg_off.reg); + try dfw.writeSleb128(factored_off); + } + }, + .val_expression => |reg_expr| { + try dfw.writeByte(DW.CFA.val_expression); + try dfw.writeUleb128(reg_expr.reg); + try wip_nav.frameExprLoc(reg_expr.expr); + }, + .escape => |bytes| try dfw.writeAll(bytes), + } + } +}; + +pub const WipNav = struct { + dwarf: *Dwarf, + unit: Unit.Index, + func: ?Func.Index, + func_si: link.File.SymbolId, + cfi: struct { + loc: u32, + cfa: Cfa.RegOff, + }, + frame_format: Frame.Format, + fde_writer: MappedFile.Node.Writer, + frame_func_length_offset: usize, + + pub const Debug = struct { + wip_nav: WipNav, + pt: Zcu.PerThread, + any_children: bool, + blocks: std.ArrayList(struct { + abbrev_code: u32, + low_pc_off: u64, + high_pc: u32, + }), + info_writer: MappedFile.Node.Writer, + line_writer: MappedFile.Node.Writer, + + pub fn deinit(debug: *Debug, gpa: Allocator) void { + debug.line_writer.deinit(); + debug.info_writer.deinit(); + debug.blocks.deinit(gpa); + debug.wip_nav.deinit(gpa); + debug.* = undefined; + } + + pub fn genFuncHeaders(debug: *Debug) UpdateError!void { + try debug.wip_nav.genFuncHeaders(); + } + + pub fn genDebugFrame(debug: *Debug, loc: u32, cfa: Cfa) UpdateError!void { + return debug.wip_nav.genDebugFrame(loc, cfa); + } + + pub const LocalVarTag = enum { arg, local_var }; + pub fn genLocalVarDebugInfo( + debug: *Debug, + tag: LocalVarTag, + opt_name: ?[]const u8, + ty: Type, + loc: Loc, + ) UpdateError!void { + if (true) return; + return debug.genLocalVarDebugInfoInner(tag, opt_name, ty, loc) catch |err| switch (err) { + error.WriteFailed => debug.info_writer.err.?, + else => |e| e, + }; + } + fn genLocalVarDebugInfoInner( + debug: *Debug, + tag: LocalVarTag, + opt_name: ?[]const u8, + ty: Type, + loc: Loc, + ) (UpdateError || Writer.Error)!void { + assert(debug.wip_nav.func != null); + try debug.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 debug.strp(name); + try debug.refType(ty); + try debug.infoExprLoc(loc); + debug.any_children = true; + } + + pub const LocalConstTag = enum { comptime_arg, local_const }; + pub fn genLocalConstDebugInfo( + debug: *Debug, + tag: LocalConstTag, + opt_name: ?[]const u8, + val: Value, + ) UpdateError!void { + if (true) return; + return debug.genLocalConstDebugInfoInner(tag, opt_name, val) catch |err| switch (err) { + error.WriteFailed => debug.info_writer.err.?, + else => |e| e, + }; + } + fn genLocalConstDebugInfoInner( + debug: *Debug, + tag: LocalConstTag, + opt_name: ?[]const u8, + val: Value, + ) (UpdateError || Writer.Error)!void { + assert(debug.wip_nav.func != null); + const zcu = debug.pt.zcu; + const ty = val.typeOf(zcu); + const has_runtime_bits = ty.hasRuntimeBits(zcu); + const has_comptime_state = ty.comptimeOnly(zcu); + try debug.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 debug.strp(name); + try debug.refType(ty); + if (has_runtime_bits) try debug.blockValue(val); + if (has_comptime_state) try debug.refValue(val); + debug.any_children = true; + } + + pub fn genVarArgsDebugInfo(debug: *Debug) UpdateError!void { + if (true) return; + return debug.genVarArgsDebugInfoInner() catch |err| switch (err) { + error.WriteFailed => debug.info_writer.err.?, + else => |e| e, + }; + } + fn genVarArgsDebugInfoInner(debug: *Debug) (UpdateError || Writer.Error)!void { + assert(debug.wip_nav.func != null); + try debug.abbrevCode(.is_var_args); + debug.any_children = true; + } + + pub fn advancePcAndLine(debug: *Debug, delta_line: i33, delta_pc: u64) UpdateError!void { + if (true) return; + return debug.advancePcAndLineInner(delta_line, delta_pc) catch |err| switch (err) { + error.WriteFailed => debug.line_writer.err.?, + }; + } + fn advancePcAndLineInner(debug: *Debug, delta_line: i33, delta_pc: u64) Writer.Error!void { + const dlw = &debug.line_writer.interface; + + const header = debug.wip_nav.dwarf.debug_line.header; + assert(header.maximum_operations_per_instruction == 1); + const delta_op: u64 = 0; + + const remaining_delta_line: i9 = @intCast(if (delta_line < header.line_base or + delta_line - header.line_base >= header.line_range) + remaining: { + assert(delta_line != 0); + try dlw.writeByte(DW.LNS.advance_line); + try dlw.writeSleb128(delta_line); + break :remaining 0; + } else delta_line); + + const op_advance = @divExact(delta_pc, header.minimum_instruction_length) * + header.maximum_operations_per_instruction + delta_op; + const max_op_advance: u9 = (std.math.maxInt(u8) - header.opcode_base) / header.line_range; + const remaining_op_advance: u8 = @intCast(if (op_advance >= 2 * max_op_advance) remaining: { + try dlw.writeByte(DW.LNS.advance_pc); + try dlw.writeUleb128(op_advance); + break :remaining 0; + } else if (op_advance >= max_op_advance) remaining: { + try dlw.writeByte(DW.LNS.const_add_pc); + break :remaining op_advance - max_op_advance; + } else op_advance); + + if (remaining_delta_line == 0 and remaining_op_advance == 0) + try dlw.writeByte(DW.LNS.copy) + else + try dlw.writeByte(@intCast((remaining_delta_line - header.line_base) + + (header.line_range * remaining_op_advance) + header.opcode_base)); + } + + pub fn setColumn(debug: *Debug, column: u32) UpdateError!void { + if (true) return; + return debug.setColumnInner(column) catch |err| switch (err) { + error.WriteFailed => debug.line_writer.err.?, + }; + } + fn setColumnInner(debug: *Debug, column: u32) Writer.Error!void { + const dlw = &debug.line_writer.interface; + try dlw.writeByte(DW.LNS.set_column); + try dlw.writeUleb128(column + 1); + } + + pub fn negateStmt(debug: *Debug) UpdateError!void { + if (true) return; + return debug.negateStmtInner() catch |err| switch (err) { + error.WriteFailed => debug.line_writer.err.?, + }; + } + fn negateStmtInner(debug: *Debug) Writer.Error!void { + try debug.line_writer.interface.writeByte(DW.LNS.negate_stmt); + } + + pub fn setPrologueEnd(debug: *Debug) UpdateError!void { + if (true) return; + return debug.setPrologueEndInner() catch |err| switch (err) { + error.WriteFailed => debug.line_writer.err.?, + }; + } + fn setPrologueEndInner(debug: *Debug) Writer.Error!void { + try debug.line_writer.interface.writeByte(DW.LNS.set_prologue_end); + } + + pub fn setEpilogueBegin(debug: *Debug) UpdateError!void { + if (true) return; + return debug.setEpilogueBeginInner() catch |err| switch (err) { + error.WriteFailed => debug.line_writer.err.?, + }; + } + fn setEpilogueBeginInner(debug: *Debug) Writer.Error!void { + try debug.line_writer.interface.writeByte(DW.LNS.set_epilogue_begin); + } + + pub fn enterBlock(debug: *Debug, code_off: u64) UpdateError!void { + if (true) return; + return debug.enterBlockInner(code_off) catch |err| switch (err) { + error.WriteFailed => debug.info_writer.err.?, + else => |e| e, + }; + } + fn enterBlockInner(debug: *Debug, code_off: u64) (UpdateError || Writer.Error)!void { + const dwarf = debug.wip_nav.dwarf; + const diw = &debug.info_writer.interface; + const block = try debug.blocks.addOne(dwarf.linkFile().comp.gpa); + + block.abbrev_code = @intCast(diw.end); + try debug.abbrevCode(.block); + block.low_pc_off = code_off; + try debug.infoAddrSym(debug.wip_nav.func_si, code_off); + block.high_pc = @intCast(diw.end); + try diw.writeInt(u32, 0, dwarf.endian); + debug.any_children = false; + } + + pub fn leaveBlock(debug: *Debug, code_off: u64) UpdateError!void { + if (true) return; + return debug.leaveBlockInner(code_off) catch |err| switch (err) { + error.WriteFailed => debug.info_writer.err.?, + else => |e| e, + }; + } + fn leaveBlockInner(debug: *Debug, code_off: u64) (UpdateError || Writer.Error)!void { + const dwarf = debug.wip_nav.dwarf; + const block_bytes = comptime uleb128Bytes(@backingInt(AbbrevCode.block)); + const block = debug.blocks.pop().?; + if (debug.any_children) + try debug.info_writer.interface.writeUleb128(@backingInt(AbbrevCode.null)) + else + std.leb.writeUnsignedFixed( + block_bytes, + debug.info_writer.interface.buffered()[block.abbrev_code..][0..block_bytes], + @intCast(try dwarf.refAbbrevCode(.empty_block)), + ); + std.mem.writeInt( + u32, + debug.info_writer.interface.buffered()[block.high_pc..][0..4], + @intCast(code_off - block.low_pc_off), + dwarf.endian, + ); + debug.any_children = true; + } + + pub fn enterInlineFunc( + debug: *Debug, + func: InternPool.Index, + code_off: u64, + line: u32, + column: u32, + ) UpdateError!void { + if (true) return; + return debug.enterInlineFuncInner(func, code_off, line, column) catch |err| switch (err) { + error.WriteFailed => debug.info_writer.err.?, + else => |e| e, + }; + } + fn enterInlineFuncInner( + debug: *Debug, + func: InternPool.Index, + code_off: u64, + line: u32, + column: u32, + ) (UpdateError || Writer.Error)!void { + const dwarf = debug.wip_nav.dwarf; + const zcu = debug.pt.zcu; + const diw = &debug.info_writer.interface; + const block = try debug.blocks.addOne(zcu.gpa); + + block.abbrev_code = @intCast(diw.end); + try debug.abbrevCode(.inlined_func); + try debug.refNav(zcu.funcInfo(func).owner_nav); + try diw.writeUleb128(zcu.navSrcLine(debug.wip_nav.func.?.nav(dwarf)) + line + 1); + try diw.writeUleb128(column + 1); + block.low_pc_off = code_off; + try debug.infoAddrSym(debug.wip_nav.func_si, code_off); + block.high_pc = @intCast(diw.end); + try diw.writeInt(u32, 0, dwarf.endian); + try debug.setInlineFunc(func); + debug.any_children = false; + } + + pub fn leaveInlineFunc(debug: *Debug, func: InternPool.Index, code_off: u64) UpdateError!void { + if (true) return; + return debug.leaveInlineFuncInner(func, code_off) catch |err| switch (err) { + error.WriteFailed => debug.info_writer.err.?, + else => |e| e, + }; + } + fn leaveInlineFuncInner( + debug: *Debug, + func: InternPool.Index, + code_off: u64, + ) (UpdateError || Writer.Error)!void { + const dwarf = debug.wip_nav.dwarf; + const inlined_func_bytes = comptime uleb128Bytes(@backingInt(AbbrevCode.inlined_func)); + const block = debug.blocks.pop().?; + if (debug.any_children) + try debug.info_writer.interface.writeUleb128(@backingInt(AbbrevCode.null)) + else + std.leb.writeUnsignedFixed( + inlined_func_bytes, + debug.info_writer.interface.buffered()[block.abbrev_code..][0..inlined_func_bytes], + @intCast(try dwarf.refAbbrevCode(.empty_inlined_func)), + ); + std.mem.writeInt( + u32, + debug.info_writer.interface.buffered()[block.high_pc..][0..4], + @intCast(code_off - block.low_pc_off), + dwarf.endian, + ); + try debug.setInlineFunc(func); + debug.any_children = true; + } + + pub fn setInlineFunc(debug: *Debug, func: InternPool.Index) UpdateError!void { + return debug.setInlineFuncInner(func) catch |err| switch (err) { + error.WriteFailed => debug.line_writer.err.?, + else => |e| e, + }; + } + fn setInlineFuncInner(debug: *Debug, func: InternPool.Index) (UpdateError || Writer.Error)!void { + const wip_nav = &debug.wip_nav; + const zcu = debug.pt.zcu; + const dwarf = wip_nav.dwarf; + + const func_index = try dwarf.getFunc(zcu.funcInfo(func).owner_nav); + if (wip_nav.func == func_index) return; + + if (true) @panic("TODO"); + const new_func_info = zcu.funcInfo(func); + const new_file = zcu.navFileScopeIndex(new_func_info.owner_nav); + const new_unit = try dwarf.getUnit(zcu.fileByIndex(new_file).mod.?); + + const dlw = &debug.line_writer.interface; + if (zcu.comp.config.incremental) { + const new_func_gop = try dwarf.funcs.getOrPut(dwarf.gpa, new_func_info.owner_nav); + errdefer _ = if (!new_func_gop.found_existing) dwarf.funcs.pop(); + if (!new_func_gop.found_existing) new_func_gop.value_ptr.* = .{ + .frame_node = .none, + .debug_info_node = .none, + .debug_line_node = .none, + }; + + const section_offset_size: u4 = switch (dwarf.format) { + .@"32" => 4, + .@"64" => 8, + }; + + try dlw.writeByte(DW.LNS.extended_op); + try dlw.writeUleb128(1 + section_offset_size); + try dlw.writeByte(DW.LNE.ZIG_set_decl); + try dwarf.debug_line.section.getUnit(wip_nav.unit).getEntry(wip_nav.entry).cross_section_relocs.append(dwarf.gpa, .{ + .source_off = @intCast(dlw.end), + .target_sec = .debug_info, + .target_unit = new_unit, + .target_entry = new_func_gop.value_ptr.toOptional(), + }); + try dlw.splatByteAll(0, section_offset_size); + return; + } + + const old_func_info = zcu.funcInfo(wip_nav.func); + const old_file = zcu.navFileScopeIndex(old_func_info.owner_nav); + if (old_file != new_file) { + const mod_info = dwarf.getModInfo(wip_nav.unit); + try mod_info.dirs.put(dwarf.gpa, new_unit, {}); + const file_gop = try mod_info.files.getOrPut(dwarf.gpa, new_file); + + try dlw.writeByte(DW.LNS.set_file); + try dlw.writeUleb128(file_gop.index); + } + + const old_src_line: i33 = zcu.navSrcLine(old_func_info.owner_nav); + const new_src_line: i33 = zcu.navSrcLine(new_func_info.owner_nav); + if (new_src_line != old_src_line) { + try dlw.writeByte(DW.LNS.advance_line); + try dlw.writeSleb128(new_src_line - old_src_line); + } + + wip_nav.func = func; + } + + fn abbrevCode(debug: *Debug, abbrev_code: AbbrevCode) (UpdateError || Writer.Error)!void { + try debug.info_writer.interface.writeUleb128(try debug.wip_nav.dwarf.refAbbrevCode(abbrev_code)); + } + + fn infoExternalReloc(debug: *Debug, reloc: struct { + source_off: u32 = 0, + target_si: link.File.SymbolId, + target_off: u64 = 0, + }) Allocator.Error!void { + if (true) @panic("TODO"); + try debug.wip_nav.externalReloc(&debug.wip_nav.dwarf.debug_frame.section, reloc); + } + + fn infoSectionOffset( + debug: *Debug, + target: MappedFile.Node.Index, + addend: i64, + ) (UpdateError || Writer.Error)!void { + const dwarf = debug.wip_nav.dwarf; + const diw = &debug.info_writer.interface; + const offset = diw.end; + switch (dwarf.format) { + .@"32" => try diw.writeInt(u32, 0, dwarf.endian), + .@"64" => try diw.writeInt(u64, 0, dwarf.endian), + } + try dwarf.linkFile().cast(.elf2).?.addNodeReloc( + debug.info_writer.ni, + offset, + target, + addend, + switch (dwarf.format) { + .@"32" => .abs32, + .@"64" => .abs64, + }, + ); + } + + fn strp(debug: *Debug, str: []const u8) (UpdateError || Writer.Error)!void { + if (true) @panic("TODO"); + const dwarf = debug.wip_nav.dwarf; + try debug.infoSectionOffset(.debug_str, try dwarf.debug_str.addString(dwarf, str), 0); + } + + fn strpFmt( + debug: *Debug, + comptime fmt: []const u8, + args: anytype, + ) (UpdateError || Writer.Error)!void { + const gpa = &debug.wip_nav.dwarf.gpa; + const str = try std.fmt.allocPrint(gpa, fmt, args); + defer gpa.free(str); + return debug.strp(str); + } + + fn infoExprLoc(debug: *Debug, loc: Loc) (UpdateError || Writer.Error)!void { + var buf: [64]u8 = undefined; + var counter: ExprLocCounter = .init(debug.wip_nav.dwarf, &buf); + try loc.write(&counter); + + const adapter: struct { + debug: *Debug, + fn writer(ctx: @This()) *Writer { + return &ctx.debug.info_writer.interface; + } + fn endian(ctx: @This()) std.lang.Endian { + return ctx.debug.wip_nav.dwarf.endian; + } + fn addrSym(ctx: @This(), si: link.File.SymbolId) (UpdateError || Writer.Error)!void { + try ctx.debug.infoAddrSym(si, 0); + } + fn infoEntry( + ctx: @This(), + node: MappedFile.Node.Index, + ) (UpdateError || Writer.Error)!void { + try ctx.debug.infoSectionOffset(node, 0); + } + } = .{ .debug = debug }; + try adapter.writer().writeUleb128(counter.dw.fullCount()); + try loc.write(adapter); + } + + fn infoAddrSym( + debug: *Debug, + si: link.File.SymbolId, + sym_off: u64, + ) (UpdateError || Writer.Error)!void { + const diw = &debug.info_writer.interface; + try debug.infoExternalReloc(.{ + .source_off = @intCast(diw.end), + .target_si = si, + .target_off = sym_off, + }); + try diw.splatByteAll(0, @backingInt(debug.wip_nav.dwarf.address_size)); + } + + fn refNav(debug: *Debug, nav_index: InternPool.Nav.Index) (UpdateError || Writer.Error)!void { + try debug.infoSectionOffset(try debug.wip_nav.dwarf.getNavNode(nav_index), 0); + } + + fn refType(debug: *Debug, ty: Type) (UpdateError || Writer.Error)!void { + return debug.refValue(ty.toValue()); + } + + fn refValue(debug: *Debug, value: Value) (UpdateError || Writer.Error)!void { + if (true) @panic("TODO"); + try debug.infoSectionOffset(.debug_info, try debug.getValueNode(value), 0); + } + + fn getValueNode(debug: *Debug, value: Value) UpdateError!MappedFile.Node.Index { + if (value.typeOf(debug.zcu).toIntern() != .type_type) { + assert(value.typeOf(debug.zcu).comptimeOnly(debug.zcu)); + } + const dwarf = debug.wip_nav.dwarf; + const index = try dwarf.const_pool.get(debug.pt, .{ .dwarf = dwarf }, value.toIntern()); + return dwarf.values.items[@backingInt(index)]; + } + + fn blockValue(debug: *Debug, val: Value) (UpdateError || Writer.Error)!void { + const ty = val.typeOf(debug.pt.zcu); + const diw = &debug.info_writer.interface; + const size = ty.abiSize(debug.pt.zcu); + try diw.writeUleb128(size); + if (size == 0) return; + const old_end = diw.end; + try codegen.generateSymbol( + debug.wip_nav.dwarf.linkFile(), + debug.pt, + val, + diw, + .{ .debug_output = .{ .dwarf2 = debug } }, + ); + if (old_end + size != diw.end) { + std.debug.print("{f} [{}]: {} != {}\n", .{ + ty.fmt(debug.pt), + ty.toIntern(), + size, + diw.end - old_end, + }); + unreachable; + } + } + }; + + pub fn deinit(wip_nav: *WipNav, gpa: Allocator) void { + _ = gpa; + wip_nav.fde_writer.deinit(); + wip_nav.* = undefined; + } + + pub fn genFuncHeaders(wip_nav: *WipNav) UpdateError!void { + wip_nav.genDebugFrameHeader() catch |err| switch (err) { + error.WriteFailed => return wip_nav.fde_writer.err.?, + else => |e| return e, + }; + } + fn genDebugFrameHeader(wip_nav: *WipNav) (UpdateError || Writer.Error)!void { + assert(wip_nav.func != null); + const dwarf = wip_nav.dwarf; + const dfw = &wip_nav.fde_writer.interface; + switch (dwarf.format) { + .@"32" => try dfw.writeInt(u32, undefined, dwarf.endian), + .@"64" => { + try dfw.writeInt(u32, std.math.maxInt(u32), dwarf.endian); + try dfw.writeInt(u64, undefined, dwarf.endian); + }, + } + const unit = wip_nav.unit.get(dwarf); + switch (wip_nav.frame_format) { + .eh_frame => { + try dfw.writeInt(u32, undefined, dwarf.endian); + { + const offset = dfw.end; + try dfw.writeInt(u32, 0, dwarf.endian); + const elf = dwarf.linkFile().cast(.elf2).?; + try elf.addReloc( + @bitCast(wip_nav.fde_writer.ni), + offset, + wip_nav.func_si, + 0, + .rel32(elf), + ); + } + wip_nav.frame_func_length_offset = dfw.end; + try dfw.writeInt(u32, undefined, dwarf.endian); + try dfw.writeUleb128(0); + }, + .debug_frame => { + try wip_nav.frameSectionOffset(unit.cie_ni.unwrap().?, 0); + try wip_nav.frameAddrSym(wip_nav.func_si, 0); + wip_nav.frame_func_length_offset = dfw.end; + try dfw.splatByteAll(undefined, @backingInt(dwarf.address_size)); + }, + } + } + + pub fn genDebugFrame(wip_nav: *WipNav, loc: u32, cfa: Cfa) UpdateError!void { + return wip_nav.genDebugFrameInner(loc, cfa) catch |err| switch (err) { + error.WriteFailed => wip_nav.fde_writer.err.?, + else => |e| e, + }; + } + fn genDebugFrameInner(wip_nav: *WipNav, loc: u32, cfa: Cfa) (UpdateError || Writer.Error)!void { + assert(wip_nav.func != null); + const loc_cfa: Cfa = .{ .advance_loc = loc }; + try loc_cfa.write(wip_nav); + try cfa.write(wip_nav); + } + + pub fn finishDebugFrameFde(wip_nav: *WipNav, func_length: u64) void { + const dwarf = wip_nav.dwarf; + const fde = wip_nav.fde_writer.interface.buffer; + switch (wip_nav.frame_format) { + .eh_frame => std.mem.writeInt( + u32, + fde[wip_nav.frame_func_length_offset..][0..4], + @intCast(func_length), + dwarf.endian, + ), + .debug_frame => switch (dwarf.address_size) { + _ => unreachable, + .@"32" => std.mem.writeInt( + u32, + fde[wip_nav.frame_func_length_offset..][0..4], + @intCast(func_length), + dwarf.endian, + ), + .@"64" => std.mem.writeInt( + u64, + fde[wip_nav.frame_func_length_offset..][0..8], + func_length, + dwarf.endian, + ), + }, + } + } + + const ExprLocCounter = struct { + dw: Writer.Discarding, + section_offset_bytes: u32, + address_size: AddressSize, + fn init(dwarf: *Dwarf, buf: []u8) ExprLocCounter { + return .{ + .dw = .init(buf), + .section_offset_bytes = switch (dwarf.format) { + .@"32" => 4, + .@"64" => 8, + }, + .address_size = dwarf.address_size, + }; + } + fn writer(counter: *ExprLocCounter) *Writer { + return &counter.dw.writer; + } + fn endian(_: ExprLocCounter) std.lang.Endian { + return .native; + } + fn addrSym(counter: *ExprLocCounter, _: link.File.SymbolId) Writer.Error!void { + try counter.dw.writer.splatByteAll(undefined, @backingInt(counter.address_size)); + } + fn infoEntry(counter: *ExprLocCounter, _: MappedFile.Node.Index) Writer.Error!void { + try counter.dw.writer.splatByteAll(undefined, counter.section_offset_bytes); + } + }; + + fn frameSectionOffset( + wip_nav: *WipNav, + target: MappedFile.Node.Index, + addend: i64, + ) (UpdateError || Writer.Error)!void { + const dwarf = wip_nav.dwarf; + const dfw = &wip_nav.fde_writer.interface; + const offset = dfw.end; + switch (dwarf.format) { + .@"32" => try dfw.writeInt(u32, 0, dwarf.endian), + .@"64" => try dfw.writeInt(u64, 0, dwarf.endian), + } + try dwarf.linkFile().cast(.elf2).?.addNodeReloc( + wip_nav.fde_writer.ni, + offset, + target, + addend, + switch (dwarf.format) { + .@"32" => .abs32, + .@"64" => .abs64, + }, + ); + } + + fn frameExprLoc(wip_nav: *WipNav, loc: Loc) (UpdateError || Writer.Error)!void { + var buf: [64]u8 = undefined; + var counter: ExprLocCounter = .init(wip_nav.dwarf, &buf); + try loc.write(&counter); + + const adapter: struct { + wip_nav: *WipNav, + fn writer(ctx: @This()) *Writer { + return &ctx.wip_nav.fde_writer.interface; + } + fn endian(ctx: @This()) std.lang.Endian { + return ctx.wip_nav.dwarf.endian; + } + fn addrSym(ctx: @This(), si: link.File.SymbolId) (UpdateError || Writer.Error)!void { + try ctx.wip_nav.frameAddrSym(si, 0); + } + fn infoEntry(ctx: @This(), node: MappedFile.Node.Index) (UpdateError || Writer.Error)!void { + try ctx.wip_nav.frameSectionOffset(node, 0); + } + } = .{ .wip_nav = wip_nav }; + try adapter.writer().writeUleb128(counter.dw.fullCount()); + try loc.write(adapter); + } + + fn frameAddrSym( + wip_nav: *WipNav, + si: link.File.SymbolId, + sym_off: u64, + ) (UpdateError || Writer.Error)!void { + const dwarf = wip_nav.dwarf; + const dfw = &wip_nav.fde_writer.interface; + const offset = dfw.end; + try dfw.splatByteAll(0, @backingInt(dwarf.address_size)); + const elf = dwarf.linkFile().cast(.elf2).?; + try elf.addReloc( + @bitCast(wip_nav.fde_writer.ni), + offset, + si, + @bitCast(sym_off), + switch (dwarf.format) { + .@"32" => .abs32(elf), + .@"64" => .abs64(elf), + }, + ); + } +}; + +pub fn init(lf: *link.File, format: DW.Format) Dwarf { + const comp = lf.comp; + const target = &comp.root_mod.resolved_target.result; + return .{ + .tag = lf.tag, + .format = format, + .address_size = switch (target.ptrBitWidth()) { + 0...32 => .@"32", + 33...64 => .@"64", + else => unreachable, + }, + .endian = target.cpu.arch.endian(), + .const_pool = .empty, + .units = .empty, + .values = .empty, + .globals = .empty, + .funcs = .empty, + + .debug_info = .{ + .section_index = .none, + }, + .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_index = .none, + }, + .frame = .{ + .header = if (target.cpu.arch == .x86_64 and target.ofmt == .elf) header: { + dev.check(.x86_64_backend); + const Register = @import("../codegen/x86_64/bits.zig").Register; + break :header comptime .{ + .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 .{ + .code_alignment_factor = undefined, + .data_alignment_factor = undefined, + .return_address_register = undefined, + .initial_instructions = &.{}, + }, + .section_index = .none, + }, + }; +} + +pub fn deinit(dwarf: *Dwarf, gpa: Allocator) void { + dwarf.const_pool.deinit(gpa); + dwarf.units.deinit(gpa); + dwarf.values.deinit(gpa); + dwarf.globals.deinit(gpa); + dwarf.funcs.deinit(gpa); + dwarf.* = undefined; +} + +fn linkFile(dwarf: *Dwarf) *link.File { + return switch (dwarf.tag) { + else => unreachable, + .elf2 => |tag| &@as(*tag.Type(), @alignCast(@fieldParentPtr("dwarf", dwarf))).base, + }; +} + +pub fn initUnits(dwarf: *Dwarf, zcu: *Zcu) Allocator.Error!void { + try dwarf.units.ensureTotalCapacity(zcu.gpa, zcu.module_roots.count()); + for (zcu.module_roots.keys(), zcu.module_roots.values()) |mod, root| switch (root) { + .none => {}, + else => dwarf.units.putAssumeCapacityNoClobber(mod, .{ + .frame_ni = .none, + .cie_ni = .none, + }), + }; +} + +fn getNavNode(dwarf: *Dwarf, nav_index: InternPool.Nav.Index) UpdateError!MappedFile.Node.Index { + if (true) @panic("TODO"); + const zcu = dwarf.linkFile().comp.zcu.?; + const ip = &zcu.intern_pool; + const nav = ip.getNav(nav_index); + const unit = try dwarf.getUnit(zcu.fileByIndex(nav.srcInst(ip).resolveFile(ip)).mod.?); + const gop = try dwarf.navs.getOrPut(dwarf.gpa, nav_index); + if (gop.found_existing) return .{ unit, gop.value_ptr.* }; + const entry = try dwarf.addCommonEntry(unit); + gop.value_ptr.* = entry; + return .{ unit, entry }; +} + +pub fn getUnit(dwarf: *Dwarf, mod: *Module) Unit.Index { + return @fromBackingInt(@intCast(dwarf.units.getIndex(mod).?)); +} + +pub fn getFunc(dwarf: *Dwarf, owner_nav: InternPool.Nav.Index) Allocator.Error!Func.Index { + const func_gop = try dwarf.funcs.getOrPut(dwarf.linkFile().comp.gpa, owner_nav); + if (!func_gop.found_existing) func_gop.value_ptr.* = .{ + .fde_ni = .none, + .debug_info_ni = .none, + .debug_line_ni = .none, + }; + return @fromBackingInt(@intCast(func_gop.index)); +} + +pub fn updateUnitLength(dwarf: *Dwarf, header: []u8, unit_length: u64) void { + switch (dwarf.format) { + .@"32" => std.mem.writeInt(u32, header[0..4], @intCast(unit_length - 4), dwarf.endian), + .@"64" => std.mem.writeInt(u64, header[4..12], unit_length - 12, dwarf.endian), + } +} + +pub const EhFrameHdr = extern struct { + version: u8, + eh_frame_ptr_enc: std.dwarf.EH.PE, + fde_count_enc: std.dwarf.EH.PE, + table_enc: std.dwarf.EH.PE, + eh_frame_ptr: u32, +}; +pub fn genEhFrameHdr( + dwarf: *Dwarf, + eh_frame_hdr_ai: link.File.AtomId, + eh_frame_hdr: *EhFrameHdr, + eh_frame_si: link.File.SymbolId, +) UpdateError!void { + eh_frame_hdr.* = .{ + .version = 1, + .eh_frame_ptr_enc = .{ .type = .sdata4, .rel = .pcrel }, + .fde_count_enc = .omit, + .table_enc = .omit, + .eh_frame_ptr = undefined, + }; + const elf = dwarf.linkFile().cast(.elf2).?; + try elf.addReloc( + eh_frame_hdr_ai, + @offsetOf(EhFrameHdr, "eh_frame_ptr"), + eh_frame_si, + 0, + .rel32(elf), + ); +} + +pub fn genDebugFrameCie( + dwarf: *Dwarf, + w: *Writer, + /// `null` means to generate an architecture-agnostic padding cie + arch: ?std.Target.Cpu.Arch, + format: Frame.Format, +) Writer.Error!void { + switch (dwarf.format) { + .@"32" => try w.writeInt(u32, undefined, dwarf.endian), + .@"64" => { + try w.writeInt(u32, std.math.maxInt(u32), dwarf.endian); + try w.writeInt(u64, undefined, dwarf.endian); + }, + } + switch (format) { + .eh_frame => try w.writeInt(u32, 0, dwarf.endian), + .debug_frame => switch (dwarf.format) { + .@"32" => try w.writeInt(u32, std.math.maxInt(u32), dwarf.endian), + .@"64" => try w.writeInt(u64, std.math.maxInt(u64), dwarf.endian), + }, + } + try w.writeByte(if (arch) |_| switch (format) { + .eh_frame => 1, + .debug_frame => 4, + } else 0); + switch (arch orelse return) { + else => unreachable, + .x86_64 => { + dev.check(.x86_64_backend); + const Register = @import("../codegen/x86_64/bits.zig").Register; + switch (format) { + .eh_frame => try w.writeAll("zR\x00"), + .debug_frame => { + try w.writeAll("\x00"); + try w.writeByte(@backingInt(dwarf.address_size)); + try w.writeByte(0); + }, + } + try w.writeUleb128(dwarf.frame.header.code_alignment_factor); + try w.writeSleb128(dwarf.frame.header.data_alignment_factor); + switch (format) { + .eh_frame => try w.writeByte(@intCast(dwarf.frame.header.return_address_register)), + .debug_frame => try w.writeUleb128(dwarf.frame.header.return_address_register), + } + switch (format) { + .eh_frame => { + try w.writeUleb128(1); + try w.writeByte(@bitCast(@as(DW.EH.PE, .{ .type = .sdata4, .rel = .pcrel }))); + }, + .debug_frame => {}, + } + try w.writeByte(DW.CFA.def_cfa_sf); + try w.writeUleb128(Register.rsp.dwarfNum()); + try w.writeSleb128(-1); + try w.writeByte(@as(u8, DW.CFA.offset) + Register.rip.dwarfNum()); + try w.writeUleb128(1); + }, + } +} + +pub fn updateEhFrameFde(dwarf: *Dwarf, fde: []u8, fde_offset: u64) void { + const cie_pointer_offset: usize = switch (dwarf.format) { + .@"32" => 4, + .@"64" => 12, + }; + std.mem.writeInt( + u32, + fde[cie_pointer_offset..][0..4], + @intCast(fde_offset + cie_pointer_offset), + dwarf.endian, + ); +} + +fn refAbbrevCode( + dwarf: *Dwarf, + abbrev_code: AbbrevCode, +) (UpdateError || Writer.Error)!@typeInfo(AbbrevCode).@"enum".tag_type { + if (true) @panic("TODO"); + const Entry = {}; + const DebugAbbrev = {}; + assert(abbrev_code != .null); + const entry: Entry.Index = @fromBackingInt(@intCast(@backingInt(abbrev_code))); + if (dwarf.debug_abbrev.section.getUnit(DebugAbbrev.unit).getEntry(entry).len > 0) return @backingInt(abbrev_code); + var debug_abbrev_aw: Writer.Allocating = .init(dwarf.gpa); + defer debug_abbrev_aw.deinit(); + const daw = &debug_abbrev_aw.writer; + const abbrev = AbbrevCode.abbrevs.get(abbrev_code); + try daw.writeUleb128(@backingInt(abbrev_code)); + try daw.writeUleb128(@backingInt(abbrev.tag)); + try daw.writeByte(if (abbrev.children) DW.CHILDREN.yes else DW.CHILDREN.no); + for (abbrev.attrs) |*attr| inline for (attr) |info| try daw.writeUleb128(@backingInt(info)); + for (0..2) |_| try daw.writeUleb128(0); + try dwarf.debug_abbrev.section.replaceEntry(DebugAbbrev.unit, entry, dwarf, debug_abbrev_aw.written()); + return @backingInt(abbrev_code); +} + +fn DeclValEnum(comptime T: type) type { + const decl_names = @typeInfo(T).@"struct".decl_names; + @setEvalBranchQuota(10 * decl_names.len); + var field_names: [decl_names.len][]const u8 = undefined; + var fields_len = 0; + var min_value: ?comptime_int = null; + var max_value: ?comptime_int = null; + for (decl_names) |decl_name| { + if (std.mem.startsWith(u8, decl_name, "HP_") or std.mem.endsWith(u8, decl_name, "_user")) continue; + const value = @field(T, decl_name); + field_names[fields_len] = decl_name; + 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; + } + if (fields_len == 0) return enum {}; + const TagInt = std.math.IntFittingRange(min_value orelse 0, max_value orelse 0); + var field_vals: [fields_len]TagInt = undefined; + for (field_names[0..fields_len], &field_vals) |name, *val| val.* = @field(T, name); + return @Enum(TagInt, .exhaustive, field_names[0..fields_len], &field_vals); +} + +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_packed_union, + decl_var, + decl_const, + decl_const_runtime_bits, + decl_const_comptime_state, + decl_const_runtime_bits_comptime_state, + decl_nullary_func, + decl_func, + decl_nullary_func_generic, + decl_func_generic, + decl_extern_nullary_func, + decl_extern_func, + generic_decl_var, + generic_decl_const, + generic_decl_func, + decl_instance_alias, + decl_instance_empty_enum, + decl_instance_enum, + decl_instance_namespace_struct, + decl_instance_struct, + decl_instance_packed_struct, + decl_instance_union, + decl_instance_packed_union, + decl_instance_var, + decl_instance_const, + decl_instance_const_runtime_bits, + decl_instance_const_comptime_state, + decl_instance_const_runtime_bits_comptime_state, + decl_instance_nullary_func, + decl_instance_func, + decl_instance_nullary_func_generic, + decl_instance_func_generic, + decl_instance_extern_nullary_func, + decl_instance_extern_func, + // the rest are unrestricted other than empty variants must not be longer + // than the non-empty variant, and so should appear first + compile_unit, + module, + empty_file, + file, + access, + enum_field, + generated_field, + field, + field_default_runtime_bits, + field_default_comptime_state, + field_comptime, + field_comptime_runtime_bits, + field_comptime_comptime_state, + packed_field, + tagged_union, + tagged_union_field, + tagged_union_default_field, + void_type, + numeric_type, + inferred_error_set_type, + ptr_type, + ptr_sentinel_type, + ptr_aligned_type, + ptr_aligned_sentinel_type, + is_const, + is_volatile, + array_type, + array_sentinel_type, + vector_type, + array_index, + array_len, + 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_packed_union_type, + packed_union_type, + builtin_extern_nullary_func, + builtin_extern_func, + builtin_extern_var, + empty_block, + block, + empty_inlined_func, + inlined_func, + arg, + unnamed_arg, + comptime_arg, + unnamed_comptime_arg, + comptime_arg_runtime_bits, + unnamed_comptime_arg_runtime_bits, + comptime_arg_comptime_state, + unnamed_comptime_arg_comptime_state, + comptime_arg_runtime_bits_comptime_state, + unnamed_comptime_arg_runtime_bits_comptime_state, + extern_param, + local_var, + local_const, + local_const_runtime_bits, + local_const_comptime_state, + local_const_runtime_bits_comptime_state, + undefined_comptime_value, + comptime_value, + location_comptime_value, + aggregate_undefined_comptime_value, + aggregate_comptime_value, + aggregate_location_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(@backingInt(AbbrevCode.decl_instance_extern_func)); + comptime { + assert(uleb128Bytes(@backingInt(AbbrevCode.pad_1)) == 1); + assert(uleb128Bytes(@backingInt(AbbrevCode.pad_n)) == 1); + assert(uleb128Bytes(@backingInt(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_packed_union = .{ + .tag = .union_type, + .children = true, + .attrs = decl_abbrev_common_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .decl_var = .{ + .tag = .variable, + .attrs = decl_abbrev_common_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .location, .exprloc }, + .{ .alignment, .udata }, + .{ .external, .flag }, + }, + }, + .decl_const = .{ + .tag = .constant, + .attrs = decl_abbrev_common_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .alignment, .udata }, + .{ .external, .flag }, + }, + }, + .decl_const_runtime_bits = .{ + .tag = .constant, + .attrs = decl_abbrev_common_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .alignment, .udata }, + .{ .external, .flag }, + .{ .const_value, .block }, + }, + }, + .decl_const_comptime_state = .{ + .tag = .constant, + .attrs = decl_abbrev_common_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .alignment, .udata }, + .{ .external, .flag }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .decl_const_runtime_bits_comptime_state = .{ + .tag = .constant, + .attrs = decl_abbrev_common_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .alignment, .udata }, + .{ .external, .flag }, + .{ .const_value, .block }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .decl_nullary_func = .{ + .tag = .subprogram, + .attrs = decl_abbrev_common_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .low_pc, .addr }, + .{ .high_pc, .data4 }, + .{ .alignment, .udata }, + .{ .external, .flag }, + .{ .noreturn, .flag }, + }, + }, + .decl_func = .{ + .tag = .subprogram, + .children = true, + .attrs = decl_abbrev_common_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .low_pc, .addr }, + .{ .high_pc, .data4 }, + .{ .alignment, .udata }, + .{ .external, .flag }, + .{ .noreturn, .flag }, + }, + }, + .decl_nullary_func_generic = .{ + .tag = .subprogram, + .attrs = decl_abbrev_common_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .decl_func_generic = .{ + .tag = .subprogram, + .children = true, + .attrs = decl_abbrev_common_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .decl_extern_nullary_func = .{ + .tag = .subprogram, + .attrs = decl_abbrev_common_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .low_pc, .addr }, + .{ .external, .flag_present }, + .{ .noreturn, .flag }, + }, + }, + .decl_extern_func = .{ + .tag = .subprogram, + .children = true, + .attrs = decl_abbrev_common_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .low_pc, .addr }, + .{ .external, .flag_present }, + .{ .noreturn, .flag }, + }, + }, + .generic_decl_var = .{ + .tag = .variable, + .attrs = generic_decl_abbrev_common_attrs, + }, + .generic_decl_const = .{ + .tag = .constant, + .attrs = generic_decl_abbrev_common_attrs, + }, + .generic_decl_func = .{ + .tag = .subprogram, + .attrs = generic_decl_abbrev_common_attrs, + }, + .decl_instance_alias = .{ + .tag = .imported_declaration, + .attrs = decl_instance_abbrev_common_attrs ++ .{ + .{ .import, .ref_addr }, + }, + }, + .decl_instance_empty_enum = .{ + .tag = .enumeration_type, + .attrs = decl_instance_abbrev_common_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .decl_instance_enum = .{ + .tag = .enumeration_type, + .children = true, + .attrs = decl_instance_abbrev_common_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .decl_instance_namespace_struct = .{ + .tag = .structure_type, + .attrs = decl_instance_abbrev_common_attrs ++ .{ + .{ .declaration, .flag }, + }, + }, + .decl_instance_struct = .{ + .tag = .structure_type, + .children = true, + .attrs = decl_instance_abbrev_common_attrs ++ .{ + .{ .byte_size, .udata }, + .{ .alignment, .udata }, + }, + }, + .decl_instance_packed_struct = .{ + .tag = .structure_type, + .children = true, + .attrs = decl_instance_abbrev_common_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .decl_instance_union = .{ + .tag = .union_type, + .children = true, + .attrs = decl_instance_abbrev_common_attrs ++ .{ + .{ .byte_size, .udata }, + .{ .alignment, .udata }, + }, + }, + .decl_instance_packed_union = .{ + .tag = .union_type, + .children = true, + .attrs = decl_instance_abbrev_common_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .decl_instance_var = .{ + .tag = .variable, + .attrs = decl_instance_abbrev_common_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .location, .exprloc }, + .{ .alignment, .udata }, + .{ .external, .flag }, + }, + }, + .decl_instance_const = .{ + .tag = .constant, + .attrs = decl_instance_abbrev_common_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .alignment, .udata }, + .{ .external, .flag }, + }, + }, + .decl_instance_const_runtime_bits = .{ + .tag = .constant, + .attrs = decl_instance_abbrev_common_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .alignment, .udata }, + .{ .external, .flag }, + .{ .const_value, .block }, + }, + }, + .decl_instance_const_comptime_state = .{ + .tag = .constant, + .attrs = decl_instance_abbrev_common_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .alignment, .udata }, + .{ .external, .flag }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .decl_instance_const_runtime_bits_comptime_state = .{ + .tag = .constant, + .attrs = decl_instance_abbrev_common_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .alignment, .udata }, + .{ .external, .flag }, + .{ .const_value, .block }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .decl_instance_nullary_func = .{ + .tag = .subprogram, + .attrs = decl_instance_abbrev_common_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .low_pc, .addr }, + .{ .high_pc, .data4 }, + .{ .alignment, .udata }, + .{ .external, .flag }, + .{ .noreturn, .flag }, + }, + }, + .decl_instance_func = .{ + .tag = .subprogram, + .children = true, + .attrs = decl_instance_abbrev_common_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .low_pc, .addr }, + .{ .high_pc, .data4 }, + .{ .alignment, .udata }, + .{ .external, .flag }, + .{ .noreturn, .flag }, + }, + }, + .decl_instance_nullary_func_generic = .{ + .tag = .subprogram, + .attrs = decl_instance_abbrev_common_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .decl_instance_func_generic = .{ + .tag = .subprogram, + .children = true, + .attrs = decl_instance_abbrev_common_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .decl_instance_extern_nullary_func = .{ + .tag = .subprogram, + .attrs = decl_instance_abbrev_common_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .low_pc, .addr }, + .{ .external, .flag_present }, + .{ .noreturn, .flag }, + }, + }, + .decl_instance_extern_func = .{ + .tag = .subprogram, + .children = true, + .attrs = decl_instance_abbrev_common_attrs ++ .{ + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .low_pc, .addr }, + .{ .external, .flag_present }, + .{ .noreturn, .flag }, + }, + }, + .compile_unit = .{ + .tag = .compile_unit, + .children = true, + .attrs = &.{ + .{ .language, .data1 }, + .{ .producer, .line_strp }, + .{ .comp_dir, .line_strp }, + .{ .name, .line_strp }, + .{ .base_types, .ref_addr }, + .{ .stmt_list, .sec_offset }, + .{ .rnglists_base, .sec_offset }, + .{ .ranges, .rnglistx }, + }, + }, + .module = .{ + .tag = .module, + .children = true, + .attrs = &.{ + .{ .name, .strp }, + .{ .ranges, .rnglistx }, + }, + }, + .empty_file = .{ + .tag = .structure_type, + .attrs = &.{ + .{ .decl_file, .udata }, + .{ .name, .strp }, + }, + }, + .file = .{ + .tag = .structure_type, + .children = true, + .attrs = &.{ + .{ .decl_file, .udata }, + .{ .name, .strp }, + .{ .byte_size, .udata }, + .{ .alignment, .udata }, + }, + }, + .access = .{ + .tag = .member, + .attrs = &.{ + .{ .name, .strp }, + }, + }, + .enum_field = .{ + .tag = .enumerator, + .attrs = &.{ + .{ .const_value, .indirect }, + .{ .name, .strp }, + }, + }, + .generated_field = .{ + .tag = .member, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .data_member_location, .udata }, + .{ .artificial, .flag_present }, + }, + }, + .field = .{ + .tag = .member, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .data_member_location, .udata }, + .{ .alignment, .udata }, + }, + }, + .field_default_runtime_bits = .{ + .tag = .member, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .data_member_location, .udata }, + .{ .alignment, .udata }, + .{ .default_value, .block }, + }, + }, + .field_default_comptime_state = .{ + .tag = .member, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .data_member_location, .udata }, + .{ .alignment, .udata }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .field_comptime = .{ + .tag = .member, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .name, .strp }, + .{ .type, .ref_addr }, + }, + }, + .field_comptime_runtime_bits = .{ + .tag = .member, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .const_value, .block }, + }, + }, + .field_comptime_comptime_state = .{ + .tag = .member, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .packed_field = .{ + .tag = .member, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .data_bit_offset, .udata }, + }, + }, + .tagged_union = .{ + .tag = .variant_part, + .children = true, + .attrs = &.{ + .{ .discr, .ref_addr }, + }, + }, + .tagged_union_field = .{ + .tag = .variant, + .children = true, + .attrs = &.{ + .{ .discr_value, .indirect }, + }, + }, + .tagged_union_default_field = .{ + .tag = .variant, + .children = true, + }, + .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 }, + .{ .address_class, .data1 }, + .{ .type, .ref_addr }, + }, + }, + .ptr_sentinel_type = .{ + .tag = .pointer_type, + .attrs = &.{ + .{ .name, .strp }, + .{ .ZIG_sentinel, .block }, + .{ .address_class, .data1 }, + .{ .type, .ref_addr }, + }, + }, + .ptr_aligned_type = .{ + .tag = .pointer_type, + .attrs = &.{ + .{ .name, .strp }, + .{ .alignment, .udata }, + .{ .address_class, .data1 }, + .{ .type, .ref_addr }, + }, + }, + .ptr_aligned_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 = &.{ + .{ .lower_bound, .udata }, + }, + }, + .array_len = .{ + .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_packed_union_type = .{ + .tag = .union_type, + .attrs = &.{ + .{ .decl_file, .udata }, + .{ .name, .strp }, + .{ .type, .ref_addr }, + }, + }, + .packed_union_type = .{ + .tag = .union_type, + .children = true, + .attrs = &.{ + .{ .decl_file, .udata }, + .{ .name, .strp }, + .{ .type, .ref_addr }, + }, + }, + .builtin_extern_nullary_func = .{ + .tag = .subprogram, + .attrs = &.{ + .{ .ZIG_parent, .ref_addr }, + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .low_pc, .addr }, + .{ .external, .flag_present }, + .{ .noreturn, .flag }, + }, + }, + .builtin_extern_func = .{ + .tag = .subprogram, + .children = true, + .attrs = &.{ + .{ .ZIG_parent, .ref_addr }, + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .low_pc, .addr }, + .{ .external, .flag_present }, + .{ .noreturn, .flag }, + }, + }, + .builtin_extern_var = .{ + .tag = .variable, + .attrs = &.{ + .{ .ZIG_parent, .ref_addr }, + .{ .linkage_name, .strp }, + .{ .type, .ref_addr }, + .{ .location, .exprloc }, + .{ .external, .flag_present }, + }, + }, + .empty_block = .{ + .tag = .lexical_block, + .attrs = &.{ + .{ .low_pc, .addr }, + .{ .high_pc, .data4 }, + }, + }, + .block = .{ + .tag = .lexical_block, + .children = true, + .attrs = &.{ + .{ .low_pc, .addr }, + .{ .high_pc, .data4 }, + }, + }, + .empty_inlined_func = .{ + .tag = .inlined_subroutine, + .attrs = &.{ + .{ .abstract_origin, .ref_addr }, + .{ .call_line, .udata }, + .{ .call_column, .udata }, + .{ .low_pc, .addr }, + .{ .high_pc, .data4 }, + }, + }, + .inlined_func = .{ + .tag = .inlined_subroutine, + .children = true, + .attrs = &.{ + .{ .abstract_origin, .ref_addr }, + .{ .call_line, .udata }, + .{ .call_column, .udata }, + .{ .low_pc, .addr }, + .{ .high_pc, .data4 }, + }, + }, + .arg = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .location, .exprloc }, + }, + }, + .unnamed_arg = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .type, .ref_addr }, + .{ .location, .exprloc }, + }, + }, + .comptime_arg = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .name, .strp }, + .{ .type, .ref_addr }, + }, + }, + .unnamed_comptime_arg = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .type, .ref_addr }, + }, + }, + .comptime_arg_runtime_bits = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .const_value, .block }, + }, + }, + .unnamed_comptime_arg_runtime_bits = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .type, .ref_addr }, + .{ .const_value, .block }, + }, + }, + .comptime_arg_comptime_state = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .unnamed_comptime_arg_comptime_state = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .type, .ref_addr }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .comptime_arg_runtime_bits_comptime_state = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .const_value, .block }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .unnamed_comptime_arg_runtime_bits_comptime_state = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .const_expr, .flag_present }, + .{ .type, .ref_addr }, + .{ .const_value, .block }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .extern_param = .{ + .tag = .formal_parameter, + .attrs = &.{ + .{ .type, .ref_addr }, + }, + }, + .local_var = .{ + .tag = .variable, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .location, .exprloc }, + }, + }, + .local_const = .{ + .tag = .constant, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + }, + }, + .local_const_runtime_bits = .{ + .tag = .constant, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .const_value, .block }, + }, + }, + .local_const_comptime_state = .{ + .tag = .constant, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .local_const_runtime_bits_comptime_state = .{ + .tag = .constant, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + .{ .const_value, .block }, + .{ .ZIG_comptime_value, .ref_addr }, + }, + }, + .undefined_comptime_value = .{ + .tag = .ZIG_comptime_value, + .attrs = &.{ + .{ .type, .ref_addr }, + }, + }, + .aggregate_undefined_comptime_value = .{ + .tag = .ZIG_comptime_value, + .children = true, + .attrs = &.{ + .{ .type, .ref_addr }, + }, + }, + .comptime_value = .{ + .tag = .ZIG_comptime_value, + .attrs = &.{ + .{ .type, .ref_addr }, + .{ .const_value, .indirect }, + }, + }, + .aggregate_comptime_value = .{ + .tag = .ZIG_comptime_value, + .children = true, + .attrs = &.{ + .{ .type, .ref_addr }, + .{ .const_value, .indirect }, + }, + }, + .location_comptime_value = .{ + .tag = .ZIG_comptime_value, + .attrs = &.{ + .{ .type, .ref_addr }, + .{ .location, .exprloc }, + }, + }, + .aggregate_location_comptime_value = .{ + .tag = .ZIG_comptime_value, + .children = true, + .attrs = &.{ + .{ .type, .ref_addr }, + .{ .location, .exprloc }, + }, + }, + .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 uleb128Bytes(value: anytype) u32 { + var buf: [64]u8 = undefined; + var dw: Writer.Discarding = .init(&buf); + dw.writer.writeUleb128(value) catch unreachable; + return @intCast(dw.fullCount()); +} + +fn sleb128Bytes(value: anytype) u32 { + var buf: [64]u8 = undefined; + var dw: Writer.Discarding = .init(&buf); + dw.writer.writeSleb128(value) catch unreachable; + return @intCast(dw.fullCount()); +} + +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; +const codegen = @import("../codegen.zig"); +const dev = @import("../dev.zig"); +const DW = std.dwarf; +const Dwarf = @This(); +const InternPool = @import("../InternPool.zig"); +const link = @import("../link.zig"); +const MappedFile = @import("MappedFile.zig"); +const Module = @import("../Module.zig"); +const std = @import("std"); +const Type = @import("../Type.zig"); +const Value = @import("../Value.zig"); +const Writer = std.Io.Writer; +const Zcu = @import("../Zcu.zig"); diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 2ba12bcef1972cd8295ddee3e95bd6a5998e9907..bf45dcc7353ff11676bb5bb8d50e75ac6bb82f34 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -946,14 +946,11 @@ pub fn getNavVAddr( .r_addend = reloc_info.addend, }, self); }, - .debug_output => |debug_output| switch (debug_output) { - .dwarf => |wip_nav| try wip_nav.infoExternalReloc(.{ - .source_off = @intCast(reloc_info.offset), - .target_sym = @fromBackingInt(@intCast(this_sym_index)), - .target_off = reloc_info.addend, - }), - .none => unreachable, - }, + .debug_output => |debug_output| try debug_output.dwarf.infoExternalReloc(.{ + .source_off = @intCast(reloc_info.offset), + .target_sym = @fromBackingInt(@intCast(this_sym_index)), + .target_off = reloc_info.addend, + }), } return @intCast(vaddr); } @@ -978,14 +975,11 @@ pub fn getUavVAddr( .r_addend = reloc_info.addend, }, self); }, - .debug_output => |debug_output| switch (debug_output) { - .dwarf => |wip_nav| try wip_nav.infoExternalReloc(.{ - .source_off = @intCast(reloc_info.offset), - .target_sym = @fromBackingInt(@intCast(sym_index)), - .target_off = reloc_info.addend, - }), - .none => unreachable, - }, + .debug_output => |debug_output| try debug_output.dwarf.infoExternalReloc(.{ + .source_off = @intCast(reloc_info.offset), + .target_sym = @fromBackingInt(@intCast(sym_index)), + .target_off = reloc_info.addend, + }), } return @intCast(vaddr); } @@ -1555,6 +1549,7 @@ pub fn updateFunc( if (debug_wip_nav) |*dn| .{ .dwarf = dn } else .none, ) catch |err| switch (err) { error.WriteFailed => return error.OutOfMemory, + error.MappedFileIo => unreachable, // MappedFile is not being used else => |e| return e, }; const code = aw.written(); diff --git a/src/link/Elf2.zig b/src/link/Elf2.zig index 61d9c81f9cd3da43219e24ebf9581540a920894b..5d9d719a455129a817391d7faa13ed456c3a800e 100644 --- a/src/link/Elf2.zig +++ b/src/link/Elf2.zig @@ -1,8 +1,5 @@ const Elf = @This(); -const builtin = @import("builtin"); -const native_endian = builtin.cpu.arch.endian(); - const std = @import("std"); const Io = std.Io; const assert = std.debug.assert; @@ -10,6 +7,8 @@ const log = std.log.scoped(.link); const codegen = @import("../codegen.zig"); const Compilation = @import("../Compilation.zig"); +const dev = @import("../dev.zig"); +const Dwarf = @import("Dwarf2.zig"); const InternPool = @import("../InternPool.zig"); const link = @import("../link.zig"); const MappedFile = @import("MappedFile.zig"); @@ -43,6 +42,11 @@ shndx: struct { tdata: Section.Index, rela_dyn: Section.Index, rela_plt: Section.Index, + eh_frame_hdr: Section.Index, + eh_frame: Section.Index, + debug_frame: Section.Index, + debug_info: Section.Index, + debug_line: Section.Index, // These sections are created only as needed, and are initially `.UNDEF`. init_array: Section.Index, fini_array: Section.Index, @@ -123,6 +127,8 @@ got: std.array_hash_map.Auto(GotKey, Section.RelaIndex.Optional), plt: std.array_hash_map.Auto(String(.strtab), void), /// The `.plt` section contains zero or more symbol relocations starting at this index. plt_first_symbol_reloc: SymbolReloc.Index, +/// The `.eh_frame_hdr` section contains zero or more symbol relocations starting at this index. +eh_frame_hdr_first_symbol_reloc: SymbolReloc.Index, needed: std.array_hash_map.Auto(String(.dynstr), void), inputs: std.ArrayList(struct { @@ -175,9 +181,10 @@ lazy: std.EnumArray(link.File.LazySymbol.Kind, struct { }), pending_uavs: std.ArrayList(Node.UavMapIndex), symbol_relocs: std.ArrayList(SymbolReloc), -got_relocs: std.ArrayList(GotReloc), /// Set of relocations which must be re-applied if the size of the TLS segment changes. tls_size_symbol_relocs: std.array_hash_map.Auto(SymbolReloc.Index, void), +node_relocs: std.ArrayList(NodeReloc), +got_relocs: std.ArrayList(GotReloc), /// Index matches the index into `shdrs`. Like `shdrs`, this map excludes `SHN_UNDEF`. section_by_name: std.array_hash_map.Auto(String(.shstrtab), void), /// Key is the name of a global symbol which has been moved to a new symtab index. Any relocation @@ -191,6 +198,17 @@ changed_symtab_index: std.array_hash_map.Auto(String(.strtab), void), /// section in `flush` only when it is actually necessary. See also `nodeWantsDsoRelocation`. textrel_count: u32, +dwarf: Dwarf, +dwarf_units: std.ArrayList(struct { + unit_frame_cie_first_target_reloc: NodeReloc.Index, +}), +dwarf_values: std.ArrayList(struct {}), +dwarf_globals: std.ArrayList(struct {}), +dwarf_funcs: std.ArrayList(struct { + func_frame_fde_first_symbol_reloc: SymbolReloc.Index, + func_frame_fde_first_node_reloc: NodeReloc.Index, +}), + overflowed_reloc_count: u32, misaligned_reloc_count: u32, @@ -232,8 +250,9 @@ const Node = union(enum) { ehdr, shdr, segment: u32, - /// The section '.plt' may contain relocations via `elf.plt_first_symbol_reloc`. section: Section.Index, + /// The section '.plt' may contain relocations via `elf.plt_first_symbol_reloc`. + section_manual_size: Section.Index, /// May contain relocations. input_section: InputSection.Index, /// Value is the name of a global which has an entry in `elf.copied_globals`, so, a global for @@ -253,6 +272,15 @@ const Node = union(enum) { /// May contain relocations. lazy_const_data: LazyMapRef.Index(.const_data), + value_debug_info: link.ConstPool.Index, + global_debug_info: Dwarf.Global.Index, + frame_padding, + unit_frame: Dwarf.Unit.Index, + unit_frame_cie: Dwarf.Unit.Index, + func_frame_fde: Dwarf.Func.Index, + func_debug_info: Dwarf.Func.Index, + func_debug_line: Dwarf.Func.Index, + pub const InputIndex = enum(u32) { _, @@ -373,6 +401,7 @@ const Node = union(enum) { data: MappedFile.Node.Index, data_rel_ro: MappedFile.Node.Index, tls: MappedFile.Node.Index.Optional, + gnu_eh_frame: MappedFile.Node.Index.Optional, }; comptime { @@ -649,7 +678,7 @@ const Section = struct { }, .addend = @intCast(old_free_len + 1), // list length }; - if (elf.targetEndian() != native_endian) { + if (elf.targetEndian() != std.lang.Endian.native) { std.mem.byteSwapAllFields(class.ElfN().Rela, &relas[@backingInt(index)]); } }, @@ -708,7 +737,7 @@ const Section = struct { }, .addend = @intCast(opts.addend), }; - if (elf.targetEndian() != native_endian) { + if (elf.targetEndian() != std.lang.Endian.native) { std.mem.byteSwapAllFields(class.ElfN().Rela, &relas[@backingInt(new_index)]); } return new_index; @@ -807,244 +836,13 @@ const Section = struct { }, } } - }; -}; - -/// Identifies a single entry in the GOT. -const GotKey = union(enum) { - /// The entry is a reserved word, initialized to zero. `initHeaders` will add as many of these - /// as the target machine ABI requires. - /// - /// This `u32` value exists to allow reserving multiple words with distinct keys. - reserved: u32, - - /// Value is the address of the given symbol. - symbol: Symbol.Id, - - /// Value is the signed offset of the given symbol from the TLS pointer. - tpoff: Symbol.Id, - - /// Value is the TLS module ID of the DSO we are creating. - /// - /// Used for the first of the two GOT entries generated by a TLSLD relocation. - tlsld0, - /// Value is always 0. - /// - /// Used for the second of the two GOT entries generated by a TLSLD relocation. - tlsld1, - - /// Value is the TLS module ID for the given STT_TLS symbol. - /// - /// Used for the first of the two GOT entries generated by a TLSGD relocation. - tlsgd0: Symbol.Id, - /// Value is the offset of the given STT_TLS symbol from the base of the per-module TLS area. - /// - /// Used for the second of the two GOT entries generated by a TLSGD relocation. - tlsgd1: Symbol.Id, -}; - -/// A relocation targeting a particular GOT entry. -const GotReloc = struct { - /// The node containing this relocation. Possible values are: - /// * An input section - /// * A section - /// * A NAV, UAV, or lazy code/data - /// * `.none`, if this relocation was deleted (in which case it should be ignored) - node: MappedFile.Node.Index.Optional, - /// The offset of the relocation inside of `node`. - offset: u64, - target: GotKey, - addend: i64, - type: GotReloc.Type, - result: enum(u8) { ok, overflowed, misaligned }, - - /// `GotReloc.Type` has the same structure as `SymbolReloc.Type`, just with different `Target` - /// and `Special` enums---consult doc comments on `SymbolReloc.Type` for an overview. - const Type = packed struct(u16) { - fn simple(target: Target, action: Simple) GotReloc.Type { - assert(target != .special); - return .{ .target = target, .action = .{ .simple = action } }; - } - - fn special(s: Special) GotReloc.Type { - return .{ .target = .special, .action = .{ .special = s } }; - } - - target: Target, - action: packed union { - simple: Simple, - special: Special, - }, - - /// Like `SymbolReloc.Target`, but for GOT relocations. There are fewer tags because there - /// are fewer different kinds of GOT relocation. - const Target = enum(u3) { - /// This is a "special" relocation whose specific type is in the `action.special` field. - special, - - /// Absolute address of the GOT entry. - abs, - /// Offset from the relocation itself to the GOT entry ("PC-relative"). - rel, - /// Offset from the base of the GOT to the GOT entry. - offset, - }; - const Simple = SymbolReloc.Type.Simple; - - /// Like `SymbolReloc.Special`, but for GOT relocations. - const Special = enum(u13) { - larch_pcala_hi20, - larch_pcala64_lo20, - larch_pcala64_hi12, - - sparc_op_lox10, - sparc_op_hix22, - - fn applyInner( - s: Special, - elf: *Elf, - got_vaddr: u64, - got_offset: u64, - addend: u64, - dest_vaddr: u64, - dest_slice: []u8, - ) error{ RelocationMisaligned, RelocationOverflow }!void { - switch (s) { - .larch_pcala_hi20 => { - const val = got_vaddr +% got_offset +% addend; - const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]); - elf.targetStore(inst, .{ - .b0_4 = elf.targetLoad(inst).b0_4, - .j20 = link.loongarch.pcalaHi20(val, dest_vaddr), - .b25_31 = elf.targetLoad(inst).b25_31, - }); - }, - .larch_pcala64_lo20 => { - const val = got_vaddr +% got_offset +% addend; - const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]); - elf.targetStore(inst, .{ - .b0_4 = elf.targetLoad(inst).b0_4, - .j20 = link.loongarch.pcala64Lo20(val, dest_vaddr), - .b25_31 = elf.targetLoad(inst).b25_31, - }); - }, - .larch_pcala64_hi12 => { - const val = got_vaddr +% got_offset +% addend; - const inst: *align(1) link.loongarch.K12 = @ptrCast(dest_slice[0..4]); - elf.targetStore(inst, .{ - .b0_9 = elf.targetLoad(inst).b0_9, - .k12 = link.loongarch.pcala64Hi12(val, dest_vaddr), - .b22_31 = elf.targetLoad(inst).b22_31, - }); - }, - .sparc_op_lox10 => { - const dest_ptr: *align(1) packed struct(u32) { - imm13: u13, - b13_31: u19, - } = @ptrCast(dest_slice); - elf.targetStore(dest_ptr, .{ - .imm13 = @as(u10, @truncate(got_offset)), - .b13_31 = elf.targetLoad(dest_ptr).b13_31, - }); - }, - .sparc_op_hix22 => { - const dest_ptr: *align(1) packed struct(u32) { - imm22: u22, - b22_31: u10, - } = @ptrCast(dest_slice); - elf.targetStore(dest_ptr, .{ - .imm22 = @truncate(got_offset >> 10), - .b22_31 = elf.targetLoad(dest_ptr).b22_31, - }); - }, - } - } - }; - }; - - const Index = enum(u32) { - none = std.math.maxInt(u32), - _, - - fn get(index: GotReloc.Index, elf: *Elf) *GotReloc { - return &elf.got_relocs.items[@backingInt(index)]; + fn debugFrameFormat(shndx: Index, elf: *Elf) ?Dwarf.Frame.Format { + if (shndx == elf.shndx.eh_frame) return .eh_frame; + if (shndx == elf.shndx.debug_frame) return .debug_frame; + return null; } }; - - fn apply(reloc: *GotReloc, elf: *Elf) void { - assert(elf.ehdrType() != .REL); - const node = reloc.node.unwrap() orelse { - return; // deleted - }; - if (node.hasMoved(&elf.mf) or elf.shndx.got.get(elf).ni.hasMoved(&elf.mf)) { - // There's no point applying the relocation now, because it will be re-applied by - // `flushMoved` at some point anyway. - return; - } - switch (reloc.result) { - .ok => {}, - .overflowed => elf.overflowed_reloc_count -= 1, - .misaligned => elf.misaligned_reloc_count -= 1, - } - if (reloc.applyInner(elf)) { - @branchHint(.likely); - reloc.result = .ok; - } else |err| switch (err) { - error.RelocationOverflow => { - reloc.result = .overflowed; - elf.overflowed_reloc_count += 1; - }, - error.RelocationMisaligned => { - reloc.result = .misaligned; - elf.misaligned_reloc_count += 1; - }, - } - } - fn applyInner(reloc: *const GotReloc, elf: *Elf) error{ RelocationOverflow, RelocationMisaligned }!void { - const node = reloc.node.unwrap().?; - const dest_vaddr = elf.getNodeVAddr(node) + reloc.offset; - const dest_slice = node.slice(&elf.mf)[@intCast(reloc.offset)..]; - - const got_vaddr = elf.shndx.got.vaddr(elf); - const got_index: u64 = elf.got.getIndex(reloc.target).?; - const got_offset: u64 = switch (elf.identClass()) { - .NONE, _ => unreachable, - inline else => |class| @sizeOf(class.ElfN().Addr) * got_index, - }; - const addend: u64 = @bitCast(reloc.addend); - - const target_val: u64 = switch (reloc.type.target) { - .abs => got_vaddr +% got_offset +% addend, - .rel => got_vaddr +% got_offset +% addend -% dest_vaddr, - .offset => got_offset +% addend, - .special => return reloc.type.action.special.applyInner( - elf, - got_vaddr, - got_offset, - addend, - dest_vaddr, - dest_slice, - ), - }; - try reloc.type.action.simple.write(target_val, dest_slice, elf.targetEndian()); - } - - fn delete(reloc: *GotReloc, elf: *Elf) void { - switch (reloc.result) { - .ok => {}, - .overflowed => elf.overflowed_reloc_count -= 1, - .misaligned => elf.misaligned_reloc_count -= 1, - } - reloc.* = .{ - .node = .none, - .offset = undefined, - .target = undefined, - .addend = undefined, - .type = undefined, - .result = undefined, - }; - } }; pub const MachineRelocType = union { @@ -1118,51 +916,144 @@ pub const MachineRelocType = union { pub fn globDat(elf: *const Elf) MachineRelocType { return switch (elf.ehdrMachine()) { .AARCH64 => .{ .AARCH64 = .GLOB_DAT }, - .LOONGARCH => .{ .LARCH = if (elf.identClass() == .@"64") .@"64" else .@"32" }, + .LOONGARCH => .{ .LARCH = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .@"32", + .@"64" => .@"64", + } }, .PPC64 => .{ .PPC64 = .GLOB_DAT }, - .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .@"64" else .@"32" }, + .RISCV => .{ .RISCV = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .@"32", + .@"64" => .@"64", + } }, .SPARCV9 => .{ .SPARC = .GLOB_DAT }, .X86_64 => .{ .X86_64 = .GLOB_DAT }, }; } pub fn dtpMod(elf: *const Elf) MachineRelocType { return switch (elf.ehdrMachine()) { - .AARCH64 => .{ .AARCH64 = if (elf.identClass() == .@"64") .TLS_DTPMOD else .P32_TLS_DTPMOD }, - .LOONGARCH => .{ .LARCH = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 }, + .AARCH64 => .{ .AARCH64 = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .P32_TLS_DTPMOD, + .@"64" => .TLS_DTPMOD, + } }, + .LOONGARCH => .{ .LARCH = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .TLS_DTPMOD32, + .@"64" => .TLS_DTPMOD64, + } }, .PPC64 => .{ .PPC64 = .DTPMOD64 }, - .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 }, - .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 }, + .RISCV => .{ .RISCV = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .TLS_DTPMOD32, + .@"64" => .TLS_DTPMOD64, + } }, + .SPARCV9 => .{ .SPARC = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .TLS_DTPMOD32, + .@"64" => .TLS_DTPMOD64, + } }, .X86_64 => .{ .X86_64 = .DTPMOD64 }, }; } pub fn dtpOff(elf: *const Elf) MachineRelocType { return switch (elf.ehdrMachine()) { - .AARCH64 => .{ .AARCH64 = if (elf.identClass() == .@"64") .TLS_DTPREL else .P32_TLS_DTPREL }, - .LOONGARCH => .{ .LARCH = if (elf.identClass() == .@"64") .TLS_DTPREL64 else .TLS_DTPREL32 }, + .AARCH64 => .{ .AARCH64 = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .P32_TLS_DTPREL, + .@"64" => .TLS_DTPREL, + } }, + .LOONGARCH => .{ .LARCH = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .TLS_DTPREL32, + .@"64" => .TLS_DTPREL64, + } }, .PPC64 => .{ .PPC64 = .DTPREL64 }, - .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .TLS_DTPREL64 else .TLS_DTPREL32 }, - .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .TLS_DTPOFF64 else .TLS_DTPOFF32 }, + .RISCV => .{ .RISCV = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .TLS_DTPREL32, + .@"64" => .TLS_DTPREL64, + } }, + .SPARCV9 => .{ .SPARC = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .TLS_DTPOFF32, + .@"64" => .TLS_DTPOFF64, + } }, .X86_64 => .{ .X86_64 = .DTPOFF64 }, }; } pub fn tpOff(elf: *const Elf) MachineRelocType { return switch (elf.ehdrMachine()) { - .AARCH64 => .{ .AARCH64 = if (elf.identClass() == .@"64") .TLS_TPREL else .P32_TLS_TPREL }, - .LOONGARCH => .{ .LARCH = if (elf.identClass() == .@"64") .TLS_TPREL64 else .TLS_TPREL32 }, + .AARCH64 => .{ .AARCH64 = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .P32_TLS_TPREL, + .@"64" => .TLS_TPREL, + } }, + .LOONGARCH => .{ .LARCH = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .TLS_TPREL32, + .@"64" => .TLS_TPREL64, + } }, .PPC64 => .{ .PPC64 = .TPREL64 }, - .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .TLS_TPREL64 else .TLS_TPREL32 }, - .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .TLS_TPOFF64 else .TLS_TPOFF32 }, + .RISCV => .{ .RISCV = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .TLS_TPREL32, + .@"64" => .TLS_TPREL64, + } }, + .SPARCV9 => .{ .SPARC = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .TLS_TPOFF32, + .@"64" => .TLS_TPOFF64, + } }, .X86_64 => .{ .X86_64 = .TPOFF64 }, }; } pub fn absAddr(elf: *const Elf) MachineRelocType { + return switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .abs32(elf), + .@"64" => .abs64(elf), + }; + } + pub fn abs32(elf: *const Elf) MachineRelocType { return switch (elf.ehdrMachine()) { - .AARCH64 => .{ .AARCH64 = if (elf.identClass() == .@"64") .ABS64 else .P32_ABS32 }, - .LOONGARCH => .{ .LARCH = if (elf.identClass() == .@"64") .@"64" else .@"32" }, + .AARCH64 => .{ .AARCH64 = .P32_ABS32 }, + .LOONGARCH => .{ .LARCH = .@"32" }, + .PPC64 => .{ .PPC64 = .ADDR32 }, + .RISCV => .{ .RISCV = .@"32" }, + .SPARCV9 => .{ .SPARC = .@"32" }, + .X86_64 => .{ .X86_64 = .@"32" }, + }; + } + pub fn abs64(elf: *const Elf) MachineRelocType { + return switch (elf.ehdrMachine()) { + .AARCH64 => .{ .AARCH64 = .ABS64 }, + .LOONGARCH => .{ .LARCH = .@"64" }, .PPC64 => .{ .PPC64 = .ADDR64 }, - .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .@"64" else .@"32" }, - .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .@"64" else .@"32" }, - .X86_64 => .{ .X86_64 = if (elf.identClass() == .@"64") .@"64" else .@"32" }, + .RISCV => .{ .RISCV = .@"64" }, + .SPARCV9 => .{ .SPARC = .@"64" }, + .X86_64 => .{ .X86_64 = .@"64" }, + }; + } + pub fn rel32(elf: *const Elf) MachineRelocType { + return switch (elf.ehdrMachine()) { + .AARCH64 => .{ .AARCH64 = .PREL32 }, + .LOONGARCH => .{ .LARCH = .@"32_PCREL" }, + .PPC64 => .{ .PPC64 = .REL32 }, + .RISCV => .{ .RISCV = .@"32_PCREL" }, + .SPARCV9 => .{ .SPARC = .DISP32 }, + .X86_64 => .{ .X86_64 = .PC32 }, + }; + } + pub fn rel64(elf: *const Elf) MachineRelocType { + return switch (elf.ehdrMachine()) { + .AARCH64 => .{ .AARCH64 = .PREL64 }, + .LOONGARCH => unreachable, + .PPC64 => .{ .PPC64 = .REL64 }, + .RISCV => unreachable, + .SPARCV9 => .{ .SPARC = .DISP64 }, + .X86_64 => .{ .X86_64 = .PC64 }, }; } pub fn size32(elf: *const Elf) ?MachineRelocType { @@ -1265,15 +1156,6 @@ const SymbolReloc = struct { return shndx; } - const Index = enum(u32) { - none = std.math.maxInt(u32), - _, - - fn get(index: SymbolReloc.Index, elf: *Elf) *SymbolReloc { - return &elf.symbol_relocs.items[@backingInt(index)]; - } - }; - /// Instead of using the ELF relocation enums, we have our own internal representation for /// relocation types. This representation is more compact (requiring only 16 bits), and allows /// sharing a lot of relocation handling between multiple relocs and target architectures. @@ -1679,6 +1561,15 @@ const SymbolReloc = struct { } }; + const Index = enum(u32) { + none = std.math.maxInt(u32), + _, + + fn get(index: SymbolReloc.Index, elf: *Elf) *SymbolReloc { + return &elf.symbol_relocs.items[@backingInt(index)]; + } + }; + fn apply(reloc: *SymbolReloc, elf: *Elf) void { assert(elf.ehdrType() != .REL); if (reloc.node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) { @@ -1782,9 +1673,9 @@ const SymbolReloc = struct { switch (reloc.prev) { .none => { - const target_ptr = reloc.target.index(elf).ptr(elf); - assert(target_ptr.first_target_reloc == index); - target_ptr.first_target_reloc = reloc.next; + const first_target_reloc = &reloc.target.index(elf).ptr(elf).first_target_reloc; + assert(first_target_reloc.* == index); + first_target_reloc.* = reloc.next; }, else => |prev| prev.get(elf).next = reloc.next, } @@ -1818,6 +1709,386 @@ const SymbolReloc = struct { } }; +/// A relocation targeting an arbitrary node (within a section) with a fixed addend. +/// This represents a symbol reloc against the section symbol containing the node +/// with a variable addend that changes when the target node moves. +const NodeReloc = struct { + node: MappedFile.Node.Index, + offset: u64, + target: MappedFile.Node.Index, + addend: i64, + type: NodeReloc.Type, + next: NodeReloc.Index, + prev: NodeReloc.Index, + rela_index: Section.RelaIndex.Optional, + result: enum(u8) { ok, overflowed, misaligned }, + + const Type = enum { abs32, abs64 }; + + const Index = enum(u32) { + none = std.math.maxInt(u32), + _, + + fn get(index: NodeReloc.Index, elf: *Elf) *NodeReloc { + return &elf.node_relocs.items[@backingInt(index)]; + } + }; + + fn apply(reloc: *NodeReloc, elf: *Elf) void { + assert(elf.ehdrType() != .REL); + if (reloc.node.hasMoved(&elf.mf) or reloc.target.hasMoved(&elf.mf)) { + // There's no point applying the relocation now, because it will be re-applied by + // `flushMoved` at some point anyway. + return; + } + switch (reloc.result) { + .ok => {}, + .overflowed => elf.overflowed_reloc_count -= 1, + .misaligned => elf.misaligned_reloc_count -= 1, + } + if (reloc.applyInner(elf)) { + @branchHint(.likely); + reloc.result = .ok; + } else |err| switch (err) { + error.RelocationOverflow => { + reloc.result = .overflowed; + elf.overflowed_reloc_count += 1; + }, + error.RelocationMisaligned => { + reloc.result = .misaligned; + elf.misaligned_reloc_count += 1; + }, + } + } + fn applyInner(reloc: *const NodeReloc, elf: *Elf) error{ RelocationOverflow, RelocationMisaligned }!void { + const simple: SymbolReloc.Type.Simple = .{ .dest = switch (reloc.type) { + .abs32 => .@"32", + .abs64 => .@"64", + }, .cast = .unsigned, .shift = .@"0" }; + const addend: u64 = @bitCast(reloc.addend); + const target_val = elf.getNodeVAddr(reloc.target) +% addend; + const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..]; + try simple.write(target_val, dest_slice, elf.targetEndian()); + } + + fn delete(reloc: *NodeReloc, elf: *Elf) void { + reloc.deleteOutputRel(elf); + + switch (reloc.prev) { + .none => { + const first_target_reloc = switch (elf.getNode(reloc.target)) { + else => unreachable, + .unit_frame_cie => |ui| &elf.dwarf_units.items[@backingInt(ui)].unit_frame_cie_first_target_reloc, + }; + first_target_reloc.* = reloc.next; + }, + else => |prev| prev.get(elf).next = reloc.next, + } + switch (reloc.next) { + .none => {}, + else => |next| next.get(elf).prev = reloc.prev, + } + switch (reloc.result) { + .ok => {}, + .overflowed => elf.overflowed_reloc_count -= 1, + .misaligned => elf.misaligned_reloc_count -= 1, + } + + reloc.* = undefined; + } + + /// If `reloc.rela_index` is populated, reset it to `.none` and delete the relocation. + fn deleteOutputRel(reloc: *NodeReloc, elf: *Elf) void { + const rela_index = reloc.rela_index.unwrap() orelse return; + assert(elf.ehdrType() == .REL); + elf.getNodeShndx(reloc.node).get(elf).rela.shndx.relaDeleteOne(elf, rela_index); + reloc.rela_index = .none; + } +}; + +/// Identifies a single entry in the GOT. +const GotKey = union(enum) { + /// The entry is a reserved word, initialized to zero. `initHeaders` will add as many of these + /// as the target machine ABI requires. + /// + /// This `u32` value exists to allow reserving multiple words with distinct keys. + reserved: u32, + + /// Value is the address of the given symbol. + symbol: Symbol.Id, + + /// Value is the signed offset of the given symbol from the TLS pointer. + tpoff: Symbol.Id, + + /// Value is the TLS module ID of the DSO we are creating. + /// + /// Used for the first of the two GOT entries generated by a TLSLD relocation. + tlsld0, + /// Value is always 0. + /// + /// Used for the second of the two GOT entries generated by a TLSLD relocation. + tlsld1, + + /// Value is the TLS module ID for the given STT_TLS symbol. + /// + /// Used for the first of the two GOT entries generated by a TLSGD relocation. + tlsgd0: Symbol.Id, + /// Value is the offset of the given STT_TLS symbol from the base of the per-module TLS area. + /// + /// Used for the second of the two GOT entries generated by a TLSGD relocation. + tlsgd1: Symbol.Id, +}; + +/// A relocation targeting a particular GOT entry. +const GotReloc = struct { + /// The node containing this relocation. Possible values are: + /// * An input section + /// * A section + /// * A NAV, UAV, or lazy code/data + /// * `.none`, if this relocation was deleted (in which case it should be ignored) + node: MappedFile.Node.Index.Optional, + /// The offset of the relocation inside of `node`. + offset: u64, + target: GotKey, + addend: i64, + type: GotReloc.Type, + result: enum(u8) { ok, overflowed, misaligned }, + + /// `GotReloc.Type` has the same structure as `SymbolReloc.Type`, just with different `Target` + /// and `Special` enums---consult doc comments on `SymbolReloc.Type` for an overview. + const Type = packed struct(u16) { + fn simple(target: Target, action: Simple) GotReloc.Type { + assert(target != .special); + return .{ .target = target, .action = .{ .simple = action } }; + } + + fn special(s: Special) GotReloc.Type { + return .{ .target = .special, .action = .{ .special = s } }; + } + + target: Target, + action: packed union { + simple: Simple, + special: Special, + }, + + /// Like `SymbolReloc.Target`, but for GOT relocations. There are fewer tags because there + /// are fewer different kinds of GOT relocation. + const Target = enum(u3) { + /// This is a "special" relocation whose specific type is in the `action.special` field. + special, + + /// Absolute address of the GOT entry. + abs, + /// Offset from the relocation itself to the GOT entry ("PC-relative"). + rel, + /// Offset from the base of the GOT to the GOT entry. + offset, + }; + + const Simple = SymbolReloc.Type.Simple; + + /// Like `SymbolReloc.Special`, but for GOT relocations. + const Special = enum(u13) { + larch_pcala_hi20, + larch_pcala64_lo20, + larch_pcala64_hi12, + + sparc_op_lox10, + sparc_op_hix22, + + fn applyInner( + s: Special, + elf: *Elf, + got_vaddr: u64, + got_offset: u64, + addend: u64, + dest_vaddr: u64, + dest_slice: []u8, + ) error{ RelocationMisaligned, RelocationOverflow }!void { + switch (s) { + .larch_pcala_hi20 => { + const val = got_vaddr +% got_offset +% addend; + const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]); + elf.targetStore(inst, .{ + .b0_4 = elf.targetLoad(inst).b0_4, + .j20 = link.loongarch.pcalaHi20(val, dest_vaddr), + .b25_31 = elf.targetLoad(inst).b25_31, + }); + }, + .larch_pcala64_lo20 => { + const val = got_vaddr +% got_offset +% addend; + const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]); + elf.targetStore(inst, .{ + .b0_4 = elf.targetLoad(inst).b0_4, + .j20 = link.loongarch.pcala64Lo20(val, dest_vaddr), + .b25_31 = elf.targetLoad(inst).b25_31, + }); + }, + .larch_pcala64_hi12 => { + const val = got_vaddr +% got_offset +% addend; + const inst: *align(1) link.loongarch.K12 = @ptrCast(dest_slice[0..4]); + elf.targetStore(inst, .{ + .b0_9 = elf.targetLoad(inst).b0_9, + .k12 = link.loongarch.pcala64Hi12(val, dest_vaddr), + .b22_31 = elf.targetLoad(inst).b22_31, + }); + }, + .sparc_op_lox10 => { + const dest_ptr: *align(1) packed struct(u32) { + imm13: u13, + b13_31: u19, + } = @ptrCast(dest_slice); + elf.targetStore(dest_ptr, .{ + .imm13 = @as(u10, @truncate(got_offset)), + .b13_31 = elf.targetLoad(dest_ptr).b13_31, + }); + }, + .sparc_op_hix22 => { + const dest_ptr: *align(1) packed struct(u32) { + imm22: u22, + b22_31: u10, + } = @ptrCast(dest_slice); + elf.targetStore(dest_ptr, .{ + .imm22 = @truncate(got_offset >> 10), + .b22_31 = elf.targetLoad(dest_ptr).b22_31, + }); + }, + } + } + }; + }; + + const Index = enum(u32) { + none = std.math.maxInt(u32), + _, + + fn get(index: GotReloc.Index, elf: *Elf) *GotReloc { + return &elf.got_relocs.items[@backingInt(index)]; + } + }; + + fn apply(reloc: *GotReloc, elf: *Elf) void { + assert(elf.ehdrType() != .REL); + const node = reloc.node.unwrap() orelse return; // deleted + if (node.hasMoved(&elf.mf) or elf.shndx.got.get(elf).ni.hasMoved(&elf.mf)) { + // There's no point applying the relocation now, because it will be re-applied by + // `flushMoved` at some point anyway. + return; + } + switch (reloc.result) { + .ok => {}, + .overflowed => elf.overflowed_reloc_count -= 1, + .misaligned => elf.misaligned_reloc_count -= 1, + } + if (reloc.applyInner(elf)) { + @branchHint(.likely); + reloc.result = .ok; + } else |err| switch (err) { + error.RelocationOverflow => { + reloc.result = .overflowed; + elf.overflowed_reloc_count += 1; + }, + error.RelocationMisaligned => { + reloc.result = .misaligned; + elf.misaligned_reloc_count += 1; + }, + } + } + fn applyInner(reloc: *const GotReloc, elf: *Elf) error{ RelocationOverflow, RelocationMisaligned }!void { + const node = reloc.node.unwrap().?; + const dest_vaddr = elf.getNodeVAddr(node) + reloc.offset; + const dest_slice = node.slice(&elf.mf)[@intCast(reloc.offset)..]; + + const got_vaddr = elf.shndx.got.vaddr(elf); + const got_index: u64 = elf.got.getIndex(reloc.target).?; + const got_offset: u64 = switch (elf.identClass()) { + .NONE, _ => unreachable, + inline else => |class| @sizeOf(class.ElfN().Addr) * got_index, + }; + const addend: u64 = @bitCast(reloc.addend); + + const target_val: u64 = switch (reloc.type.target) { + .abs => got_vaddr +% got_offset +% addend, + .rel => got_vaddr +% got_offset +% addend -% dest_vaddr, + .offset => got_offset +% addend, + .special => return reloc.type.action.special.applyInner( + elf, + got_vaddr, + got_offset, + addend, + dest_vaddr, + dest_slice, + ), + }; + try reloc.type.action.simple.write(target_val, dest_slice, elf.targetEndian()); + } + + fn delete(reloc: *GotReloc, elf: *Elf) void { + switch (reloc.result) { + .ok => {}, + .overflowed => elf.overflowed_reloc_count -= 1, + .misaligned => elf.misaligned_reloc_count -= 1, + } + reloc.* = .{ + .node = .none, + .offset = undefined, + .target = undefined, + .addend = undefined, + .type = undefined, + .result = undefined, + }; + } +}; + +fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe_global }) Error!void { + const gpa = elf.base.comp.gpa; + + try elf.symtab.ensureUnusedCapacity(gpa, len); + + // If adding locals, we may need to move one global out of the way for each local. If adding + // globals, they could all get demoted to STB_LOCAL, meaning we have to move N other globals + // around to keep `.dynsym` compact. Either way, the maximum is N. + try elf.changed_symtab_index.ensureUnusedCapacity(gpa, len); + + { + // Ensure the symtab section's node is big enough + const need_node_size: u64 = switch (elf.shdrPtr(.symtab)) { + inline else => |shdr, class| elf.targetLoad(&shdr.size) + len * @sizeOf(class.ElfN().Sym), + }; + try Section.Index.symtab.get(elf).ni.ensureMinimumSize(&elf.mf, gpa, need_node_size); + } + + switch (kind) { + .all_local => {}, + .maybe_global => { + try elf.globals.strong_def.ensureUnusedCapacity(gpa, len); + try elf.globals.weak_def.ensureUnusedCapacity(gpa, len); + try elf.globals.strong_undef.ensureUnusedCapacity(gpa, len); + try elf.globals.weak_undef.ensureUnusedCapacity(gpa, len); + + try elf.node_global_symbols.ensureUnusedCapacity(gpa, len); + + if (elf.shndx.dynsym != .UNDEF) { + const dynsym_cur_size: u64, const dynsym_ent_size: u32 = switch (elf.shdrPtr(elf.shndx.dynsym)) { + inline else => |shdr, class| .{ + elf.targetLoad(&shdr.size), + @sizeOf(class.ElfN().Sym), + }, + }; + const dynsym_cur_len: u32 = @intCast(@divExact(dynsym_cur_size, dynsym_ent_size)); + + const dynsym_need_size: u64 = (dynsym_cur_len + len) * dynsym_ent_size; + try elf.shndx.dynsym.get(elf).ni.ensureMinimumSize(&elf.mf, gpa, dynsym_need_size); + + try elf.ensureDynsymHashCapacity(dynsym_cur_len + len); + + try elf.ensureUnusedPltCapacity(len); + } + }, + } +} + fn ensureDynsymHashCapacity(elf: *Elf, max_dynsym_count: u32) Error!void { const gpa = elf.base.comp.gpa; @@ -1984,53 +2255,6 @@ fn clearDynsymHashEntry(elf: *Elf, dynsym_index: u32) void { } } -fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe_global }) Error!void { - const gpa = elf.base.comp.gpa; - - try elf.symtab.ensureUnusedCapacity(gpa, len); - - // If adding locals, we may need to move one global out of the way for each local. If adding - // globals, they could all get demoted to STB_LOCAL, meaning we have to move N other globals - // around to keep `.dynsym` compact. Either way, the maximum is N. - try elf.changed_symtab_index.ensureUnusedCapacity(gpa, len); - - { - // Ensure the symtab section's node is big enough - const need_node_size: u64 = switch (elf.shdrPtr(.symtab)) { - inline else => |shdr, class| elf.targetLoad(&shdr.size) + len * @sizeOf(class.ElfN().Sym), - }; - try Section.Index.symtab.get(elf).ni.ensureMinimumSize(&elf.mf, gpa, need_node_size); - } - - switch (kind) { - .all_local => {}, - .maybe_global => { - try elf.globals.strong_def.ensureUnusedCapacity(gpa, len); - try elf.globals.weak_def.ensureUnusedCapacity(gpa, len); - try elf.globals.strong_undef.ensureUnusedCapacity(gpa, len); - try elf.globals.weak_undef.ensureUnusedCapacity(gpa, len); - - try elf.node_global_symbols.ensureUnusedCapacity(gpa, len); - - if (elf.shndx.dynsym != .UNDEF) { - const dynsym_cur_size: u64, const dynsym_ent_size: u32 = switch (elf.shdrPtr(elf.shndx.dynsym)) { - inline else => |shdr, class| .{ - elf.targetLoad(&shdr.size), - @sizeOf(class.ElfN().Sym), - }, - }; - const dynsym_cur_len: u32 = @intCast(@divExact(dynsym_cur_size, dynsym_ent_size)); - - const dynsym_need_size: u64 = (dynsym_cur_len + len) * dynsym_ent_size; - try elf.shndx.dynsym.get(elf).ni.ensureMinimumSize(&elf.mf, gpa, dynsym_need_size); - - try elf.ensureDynsymHashCapacity(dynsym_cur_len + len); - - try elf.ensureUnusedPltCapacity(len); - } - }, - } -} fn ensureUnusedPltCapacity(elf: *Elf, len: u32) Error!void { const gpa = elf.base.comp.gpa; @@ -2138,7 +2362,7 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L .other = .{ .visibility = .DEFAULT }, .shndx = opts.shndx.toSection().?, }; - if (elf.targetEndian() != native_endian) { + if (elf.targetEndian() != std.lang.Endian.native) { std.mem.byteSwapAllFields(class.ElfN().Sym, target_sym); } @@ -2323,7 +2547,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ .other = .{ .visibility = opts.visibility }, .shndx = opts.shndx.toSection().?, }; - if (elf.targetEndian() != native_endian) { + if (elf.targetEndian() != std.lang.Endian.native) { std.mem.byteSwapAllFields(Sym, sym); } }, @@ -2359,7 +2583,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ .other = .{ .visibility = opts.visibility }, .shndx = opts.shndx.toSection().?, }; - if (elf.targetEndian() != native_endian) { + if (elf.targetEndian() != std.lang.Endian.native) { std.mem.byteSwapAllFields(Sym, sym); } elf.appendDynsymHashEntry(dynsym_index); @@ -2991,10 +3215,18 @@ pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId { .shdr, .segment, .section, + .section_manual_size, .input_section, .copied_global, + .value_debug_info, + .global_debug_info, + .frame_padding, + .unit_frame, + .unit_frame_cie, + .func_frame_fde, + .func_debug_info, + .func_debug_line, => unreachable, - inline .nav, .uav, .lazy_code, @@ -3025,7 +3257,7 @@ fn lazySymbolInner(elf: *Elf, lazy: link.File.LazySymbol) Error!link.File.Symbol .const_data => .{ .rodata, .OBJECT }, }; const node = try shndx.get(elf).ni.addFloatingChild(&elf.mf, gpa, .{}); - var name_buf: [64]u8 = undefined; + var name_buf: [std.fmt.count("__lazy_const_data_{d}", .{std.math.maxInt(u32)})]u8 = undefined; const name = std.mem.print( &name_buf, "__lazy_{t}_{d}", @@ -3114,6 +3346,24 @@ pub fn addReloc( else => |e| return e, }; } +pub fn addNodeReloc( + elf: *Elf, + node: MappedFile.Node.Index, + offset: u64, + target: MappedFile.Node.Index, + addend: i64, + @"type": NodeReloc.Type, +) link.Error!void { + const diags = &elf.base.comp.link_diags; + elf.ensureUnusedRelocCapacity(node, 1) catch |err| switch (err) { + error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), + else => |e| return e, + }; + elf.addNodeRelocAssumeCapacity(node, offset, target, addend, @"type") catch |err| switch (err) { + error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), + else => |e| return e, + }; +} pub fn navSymbol(elf: *Elf, nav_index: InternPool.Nav.Index) link.Error!link.File.SymbolId { const diags = &elf.base.comp.link_diags; const zcu = elf.base.comp.zcu.?; @@ -3393,6 +3643,7 @@ fn create( .data = undefined, .data_rel_ro = undefined, .tls = .none, + .gnu_eh_frame = .none, }, .archive = null, .nodes = .empty, @@ -3410,6 +3661,11 @@ fn create( .tdata = .UNDEF, .rela_dyn = .UNDEF, .rela_plt = .UNDEF, + .eh_frame_hdr = .UNDEF, + .eh_frame = .UNDEF, + .debug_frame = .UNDEF, + .debug_info = .UNDEF, + .debug_line = .UNDEF, .init_array = .UNDEF, .fini_array = .UNDEF, .preinit_array = .UNDEF, @@ -3437,6 +3693,7 @@ fn create( .got = .empty, .plt = .empty, .plt_first_symbol_reloc = .none, + .eh_frame_hdr_first_symbol_reloc = .none, .needed = .empty, .inputs = .empty, .input_pending_index = 0, @@ -3451,13 +3708,26 @@ fn create( }), .pending_uavs = .empty, .symbol_relocs = .empty, - .got_relocs = .empty, .tls_size_symbol_relocs = .empty, + .node_relocs = .empty, + .got_relocs = .empty, .section_by_name = .empty, .changed_symtab_index = .empty, .textrel_count = 0, + + .dwarf = .init(&elf.base, switch (comp.config.debug_format) { + .strip => .@"32", + .dwarf => |v| v, + .code_view => unreachable, + }), + .dwarf_units = .empty, + .dwarf_values = .empty, + .dwarf_globals = .empty, + .dwarf_funcs = .empty, + .overflowed_reloc_count = 0, .misaligned_reloc_count = 0, + .const_prog_node = .none, .synth_prog_node = .none, .input_prog_node = .none, @@ -3498,10 +3768,18 @@ pub fn deinit(elf: *Elf) void { for (&elf.lazy.values) |*lazy| lazy.map.deinit(gpa); elf.pending_uavs.deinit(gpa); elf.symbol_relocs.deinit(gpa); - elf.got_relocs.deinit(gpa); elf.tls_size_symbol_relocs.deinit(gpa); + elf.node_relocs.deinit(gpa); + elf.got_relocs.deinit(gpa); elf.section_by_name.deinit(gpa); elf.changed_symtab_index.deinit(gpa); + + elf.dwarf.deinit(gpa); + elf.dwarf_units.deinit(gpa); + elf.dwarf_values.deinit(gpa); + elf.dwarf_globals.deinit(gpa); + elf.dwarf_funcs.deinit(gpa); + elf.* = undefined; } @@ -3518,11 +3796,17 @@ fn initHeaders( const gpa = comp.gpa; const is_archive = comp.config.output_mode == .Lib and comp.config.link_mode == .static; - const have_dynamic_section = switch (@"type") { + const have_dynamic = switch (@"type") { .REL => false, .EXEC => comp.config.link_mode == .dynamic, .DYN => true, }; + const have_eh_frame = machine == .X86_64 and comp.config.any_unwind_tables; + const have_debug_frame = machine == .X86_64 and switch (comp.config.debug_format) { + .strip => false, + .dwarf => !comp.config.any_unwind_tables, + .code_view => unreachable, + }; const addr_align: Alignment = switch (class) { .NONE, _ => unreachable, .@"32" => .@"4", @@ -3537,7 +3821,7 @@ fn initHeaders( // // It can be handy to temporarily set this to `.@"1"` when working on the linker, because it // prevents alignment bugs from being hidden by your filesystem's block alignment. - const node_block_align: Alignment = elf.mf.flags.block_size; + const node_block_align = elf.mf.flags.block_size; const plt: PltInfo = .fromMachine(machine); @@ -3552,7 +3836,7 @@ fn initHeaders( shnum += 1; // .data shnum += @intFromBool(comp.config.any_non_single_threaded); // .tdata shnum += 1; // .data.rel.ro - if (have_dynamic_section) { + if (have_dynamic) { shnum += 1; // .dynamic shnum += 1; // .dynstr shnum += 1; // .dynsym @@ -3560,6 +3844,19 @@ fn initHeaders( shnum += 1; // .rela.dyn shnum += 1; // .rela.plt } + if (have_eh_frame) { + shnum += @intFromBool(@"type" != .REL); // .eh_frame_hdr + shnum += 1; // .eh_frame + } + switch (comp.config.debug_format) { + .strip => {}, + .dwarf => { + shnum += @intFromBool(have_debug_frame); // .debug_frame + shnum += 1; // .debug_info + shnum += 1; // .debug_line + }, + .code_view => unreachable, + } if (@"type" != .REL) { shnum += 1; // .got shnum += @intFromBool(plt.got_plt != null); // .got.plt @@ -3574,14 +3871,15 @@ fn initHeaders( interp: u32, rodata: u32, text: u32, - data: u32, /// On most targets this is `undefined`, but on machines where JUMP_SLOT relocations write /// directly to the PLT, we place the PLT in its own segment in order to avoid making the /// general data segment RWX. plt: u32, + data: u32, tls: u32, dynamic: u32, relro: u32, + gnu_eh_frame: u32, gnu_stack: u32, }, const phnum: u32 = ph: { switch (@"type") { @@ -3622,7 +3920,7 @@ fn initHeaders( defer phnum += 1; break :phndx phnum; } else undefined, - .dynamic = if (have_dynamic_section) phndx: { + .dynamic = if (have_dynamic) phndx: { defer phnum += 1; break :phndx phnum; } else undefined, @@ -3630,6 +3928,10 @@ fn initHeaders( defer phnum += 1; break :phndx phnum; }, + .gnu_eh_frame = if (have_eh_frame) phndx: { + defer phnum += 1; + break :phndx phnum; + } else undefined, .gnu_stack = phndx: { defer phnum += 1; break :phndx phnum; @@ -3872,7 +4174,7 @@ fn initHeaders( ehdr.shentsize = @sizeOf(ElfN.Shdr); ehdr.shnum = 1; // Only the SHN_UNDEF shdr initially---will be incremented by `addSection` ehdr.shstrndx = std.elf.SHN_UNDEF; - if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr); + if (elf.targetEndian() != std.lang.Endian.native) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr); }, } @@ -3925,8 +4227,7 @@ fn initHeaders( elf.ni.phdr.slice(&elf.mf)[0 .. phnum * @sizeOf(ElfN.Phdr)], )); - const ph_phdr = &phdr[phndx.phdr]; - ph_phdr.* = .{ + phdr[phndx.phdr] = .{ .type = .PHDR, .offset = 0, .vaddr = 0, @@ -3938,8 +4239,7 @@ fn initHeaders( }; if (maybe_interp) |_| { - const ph_interp = &phdr[phndx.interp]; - ph_interp.* = .{ + phdr[phndx.interp] = .{ .type = .INTERP, .offset = 0, .vaddr = 0, @@ -3951,8 +4251,7 @@ fn initHeaders( }; } - const ph_rodata = &phdr[phndx.rodata]; - ph_rodata.* = .{ + phdr[phndx.rodata] = .{ .type = .NULL, .offset = 0, .vaddr = @intCast(base_vaddr), @@ -3963,8 +4262,7 @@ fn initHeaders( .@"align" = @intCast(page_align.toByteUnits()), }; - const ph_text = &phdr[phndx.text]; - ph_text.* = .{ + phdr[phndx.text] = .{ .type = .NULL, .offset = 0, .vaddr = @intCast(base_vaddr), @@ -3975,8 +4273,7 @@ fn initHeaders( .@"align" = @intCast(page_align.toByteUnits()), }; - const ph_data = &phdr[phndx.data]; - ph_data.* = .{ + phdr[phndx.data] = .{ .type = .NULL, .offset = 0, .vaddr = @intCast(base_vaddr), @@ -3987,50 +4284,40 @@ fn initHeaders( .@"align" = @intCast(page_align.toByteUnits()), }; - if (plt.got_plt == null) { - const ph_plt = &phdr[phndx.plt]; - ph_plt.* = .{ - .type = .NULL, - .offset = 0, - .vaddr = @intCast(base_vaddr), - .paddr = @intCast(base_vaddr), - .filesz = 0, - .memsz = 0, - .flags = .{ .R = true, .W = true, .X = true }, - .@"align" = @intCast(page_align.toByteUnits()), - }; - } + if (plt.got_plt == null) phdr[phndx.plt] = .{ + .type = .NULL, + .offset = 0, + .vaddr = @intCast(base_vaddr), + .paddr = @intCast(base_vaddr), + .filesz = 0, + .memsz = 0, + .flags = .{ .R = true, .W = true, .X = true }, + .@"align" = @intCast(page_align.toByteUnits()), + }; - if (elf.ni.tls.unwrap()) |tls_segment_ni| { - const ph_tls = &phdr[phndx.tls]; - ph_tls.* = .{ - .type = .TLS, - .offset = 0, - .vaddr = 0, - .paddr = 0, - .filesz = 0, - .memsz = 0, - .flags = .{ .R = true }, - .@"align" = @intCast(tls_segment_ni.alignment(&elf.mf).toByteUnits()), - }; - } + if (elf.ni.tls.unwrap()) |tls_segment_ni| phdr[phndx.tls] = .{ + .type = .TLS, + .offset = 0, + .vaddr = 0, + .paddr = 0, + .filesz = 0, + .memsz = 0, + .flags = .{ .R = true }, + .@"align" = @intCast(tls_segment_ni.alignment(&elf.mf).toByteUnits()), + }; - if (have_dynamic_section) { - const ph_dynamic = &phdr[phndx.dynamic]; - ph_dynamic.* = .{ - .type = .DYNAMIC, - .offset = 0, - .vaddr = 0, - .paddr = 0, - .filesz = 0, - .memsz = 0, - .flags = .{ .R = true, .W = true }, - .@"align" = @intCast(addr_align.toByteUnits()), - }; - } + if (have_dynamic) phdr[phndx.dynamic] = .{ + .type = .DYNAMIC, + .offset = 0, + .vaddr = 0, + .paddr = 0, + .filesz = 0, + .memsz = 0, + .flags = .{ .R = true, .W = true }, + .@"align" = @intCast(addr_align.toByteUnits()), + }; - const ph_relro = &phdr[phndx.relro]; - ph_relro.* = .{ + phdr[phndx.relro] = .{ .type = .GNU_RELRO, .offset = 0, .vaddr = 0, @@ -4041,8 +4328,18 @@ fn initHeaders( .@"align" = @intCast(elf.ni.data_rel_ro.alignment(&elf.mf).toByteUnits()), }; - const ph_gnu_stack = &phdr[phndx.gnu_stack]; - ph_gnu_stack.* = .{ + if (have_eh_frame) phdr[phndx.gnu_eh_frame] = .{ + .type = .GNU_EH_FRAME, + .offset = 0, + .vaddr = 0, + .paddr = 0, + .filesz = @sizeOf(Dwarf.EhFrameHdr), + .memsz = @sizeOf(Dwarf.EhFrameHdr), + .flags = .{ .R = true }, + .@"align" = 4, + }; + + phdr[phndx.gnu_stack] = .{ .type = .GNU_STACK, .offset = 0, .vaddr = 0, @@ -4071,7 +4368,7 @@ fn initHeaders( .addralign = 0, .entsize = 0, }; - if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Shdr, sh_undef); + if (target_endian != std.lang.Endian.native) std.mem.byteSwapAllFields(ElfN.Shdr, sh_undef); elf.symtab.addOneAssumeCapacity().* = .{ .node = .none, @@ -4084,6 +4381,7 @@ fn initHeaders( .entsize = @sizeOf(ElfN.Sym), .node_align = node_block_align, .info = 1, // index of first non-local symbol + .manual_size = true, })); const symtab_null = @field(elf.symPtr(.null), @tagName(ct_class)); symtab_null.* = .{ @@ -4094,7 +4392,7 @@ fn initHeaders( .other = .{ .visibility = .DEFAULT }, .shndx = std.elf.SHN_UNDEF, }; - if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Sym, symtab_null); + if (target_endian != std.lang.Endian.native) std.mem.byteSwapAllFields(ElfN.Sym, symtab_null); const ehdr = @field(elf.ehdrPtr(), @tagName(ct_class)); ehdr.shstrndx = ehdr.shnum; @@ -4105,6 +4403,7 @@ fn initHeaders( .size = 1, .entsize = 1, .node_align = node_block_align, + .manual_size = true, })); Section.Index.get(.shstrtab, elf).ni.slice(&elf.mf)[0] = 0; @@ -4117,6 +4416,7 @@ fn initHeaders( .size = 1, .entsize = 1, .node_align = node_block_align, + .manual_size = true, })); Section.Index.get(.strtab, elf).ni.slice(&elf.mf)[0] = 0; switch (elf.shdrPtr(.symtab)) { @@ -4156,6 +4456,7 @@ fn initHeaders( .flags = .{ .WRITE = true, .ALLOC = true }, .addralign = addr_align, .entsize = @intCast(addr_align.toByteUnits()), + .manual_size = true, }); { const init_plt_size = plt.entry_size * plt.header_entries; @@ -4168,6 +4469,7 @@ fn initHeaders( .size = got_plt.header_entries * elf.targetPtrSize(), .addralign = addr_align, .entsize = @intCast(addr_align.toByteUnits()), + .manual_size = true, }); elf.shndx.plt = try elf.addSection(elf.ni.text, .{ .name = ".plt", @@ -4176,6 +4478,7 @@ fn initHeaders( .size = plt.@"align".forward(init_plt_size), .addralign = plt.@"align", .node_align = node_block_align, + .manual_size = true, }); } else { elf.shndx.plt = try elf.addSection(elf.phdrs.items[phndx.plt].unwrap().?, .{ @@ -4185,6 +4488,7 @@ fn initHeaders( .size = plt.@"align".forward(init_plt_size), .addralign = plt.@"align", .node_align = node_block_align, + .manual_size = true, }); } // And the award for most annoying PLT requirement goes to SPARC, which decided that the @@ -4222,7 +4526,7 @@ fn initHeaders( @memcpy(sec_interp[0..interp.len], interp); sec_interp[interp.len] = 0; } - if (have_dynamic_section) { + if (have_dynamic) { assert(elf.ni.data_rel_ro.alignment(&elf.mf).compare(.gte, addr_align)); const dynamic_ni = try elf.ni.data_rel_ro.addFloatingChild(&elf.mf, gpa, .{ .alignment = addr_align, @@ -4239,6 +4543,7 @@ fn initHeaders( .size = 1, .entsize = 1, .node_align = node_block_align, + .manual_size = true, }); dynstr_shndx.get(elf).ni.slice(&elf.mf)[0] = 0; elf.shndx.dynstr = dynstr_shndx; @@ -4257,6 +4562,7 @@ fn initHeaders( .addralign = addr_align, .entsize = @sizeOf(Sym), .node_align = node_block_align, + .manual_size = true, }); const dynsym_null = @field(elf.dynsymPtr(0), @tagName(ct_class)); dynsym_null.* = .{ @@ -4267,7 +4573,7 @@ fn initHeaders( .other = .{ .visibility = .DEFAULT }, .shndx = std.elf.SHN_UNDEF, }; - if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields( + if (elf.targetEndian() != std.lang.Endian.native) std.mem.byteSwapAllFields( Sym, dynsym_null, ); @@ -4285,6 +4591,7 @@ fn initHeaders( .addralign = addr_align, .entsize = rela_size, .node_align = node_block_align, + .manual_size = true, }); elf.shndx.rela_plt = try elf.addSection(elf.ni.rodata, .{ .name = ".rela.plt", @@ -4295,6 +4602,7 @@ fn initHeaders( .addralign = addr_align, .entsize = rela_size, .node_align = node_block_align, + .manual_size = true, }); elf.shndx.dynamic = try elf.addSection(dynamic_ni, .{ .name = ".dynamic", @@ -4303,6 +4611,7 @@ fn initHeaders( .link = dynstr_shndx.toSection().?, .entsize = @intCast(addr_align.toByteUnits() * 2), .addralign = addr_align, + .manual_size = true, }); switch (elf.targetDynsymHashInfo()) { inline else => |info| { @@ -4318,6 +4627,7 @@ fn initHeaders( .addralign = .fromByteUnits(@sizeOf(info.Int())), // initially: nbucket = 8 + nchain = 1 .size = @sizeOf(info.Header()) + @sizeOf(info.Int()) * (8 + 1), + .manual_size = true, }); const hash_slice: []align(@sizeOf(info.Int())) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf)); const header: *info.Header() = @ptrCast(hash_slice[0..@sizeOf(info.Header())]); @@ -4407,13 +4717,45 @@ fn initHeaders( .SPARCV9 => {}, } } + if (have_eh_frame) { + elf.ni.gnu_eh_frame = .wrap(try elf.ni.rodata.addFloatingChild(&elf.mf, gpa, .{ + .size = @sizeOf(Dwarf.EhFrameHdr), + .alignment = .@"4", + .moved = true, + .bubbles_moved = false, + })); + elf.nodes.appendAssumeCapacity(.{ .segment = phndx.gnu_eh_frame }); + elf.phdrs.items[phndx.gnu_eh_frame] = elf.ni.gnu_eh_frame; + + elf.shndx.eh_frame_hdr = try elf.addSection(elf.ni.gnu_eh_frame.unwrap().?, .{ + .name = ".eh_frame_hdr", + .type = .PROGBITS, + .flags = .{ .ALLOC = true }, + .size = @sizeOf(Dwarf.EhFrameHdr), + .addralign = .@"4", + }); + elf.shndx.eh_frame = try elf.addSection(elf.ni.rodata, .{ + .name = ".eh_frame", + .flags = .{ .ALLOC = true }, + .addralign = addr_align, + .node_align = elf.mf.flags.block_size, + }); + const eh_frame_hdr_ni = elf.shndx.eh_frame_hdr.get(elf).ni; + elf.eh_frame_hdr_first_symbol_reloc = + @fromBackingInt(@intCast(elf.symbol_relocs.items.len)); + try elf.dwarf.genEhFrameHdr( + Node.toAtom(eh_frame_hdr_ni), + @ptrCast(@alignCast(eh_frame_hdr_ni.slice(&elf.mf))), + Symbol.Id.local(elf.shndx.eh_frame.get(elf).lsi).toTypeErased(), + ); + } // Populate reserved GOT words. switch (machine) { .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)), .X86_64 => { try elf.got.ensureUnusedCapacity(gpa, 3); - elf.got.putAssumeCapacityNoClobber(switch (have_dynamic_section) { + elf.got.putAssumeCapacityNoClobber(switch (have_dynamic) { true => .{ .symbol = .local(elf.shndx.dynamic.get(elf).lsi) }, false => .{ .reserved = 0 }, }, .none); @@ -4422,7 +4764,7 @@ fn initHeaders( }, .LOONGARCH, .SPARCV9 => { try elf.got.ensureUnusedCapacity(gpa, 1); - elf.got.putAssumeCapacityNoClobber(switch (have_dynamic_section) { + elf.got.putAssumeCapacityNoClobber(switch (have_dynamic) { true => .{ .symbol = .local(elf.shndx.dynamic.get(elf).lsi) }, false => .{ .reserved = 0 }, }, .none); @@ -4567,7 +4909,7 @@ fn initHeaders( }) catch |err| switch (err) { error.MultipleDefinitions => unreachable, // no inputs are processed yet }; - if (have_dynamic_section) { + if (have_dynamic) { _ = elf.addGlobalSymbolAssumeCapacity(.{ .node = .wrap(elf.shndx.dynamic.get(elf).ni), .name = try .string(elf, "_DYNAMIC"), @@ -4583,13 +4925,39 @@ fn initHeaders( } } else { assert(maybe_interp == null); - assert(!have_dynamic_section); + assert(!have_dynamic); + if (have_eh_frame) elf.shndx.eh_frame = try elf.addSection(elf.ni.rodata, .{ + .name = ".eh_frame", + .type = if (machine == .X86_64) .X86_64_UNWIND else .NULL, + .flags = .{ .ALLOC = true }, + .addralign = addr_align, + .node_align = elf.mf.flags.block_size, + }); } if (elf.ni.tls.unwrap()) |tls_segment_ni| elf.shndx.tdata = try elf.addSection(tls_segment_ni, .{ .name = ".tdata", .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true }, .node_align = node_block_align, }); + switch (comp.config.debug_format) { + .strip => {}, + .dwarf => { + if (have_debug_frame) elf.shndx.debug_frame = try elf.addSection(elf.ni.elf, .{ + .name = ".debug_frame", + .addralign = addr_align, + .node_align = elf.mf.flags.block_size, + }); + elf.shndx.debug_info = try elf.addSection(elf.ni.elf, .{ + .name = ".debug_info", + .node_align = elf.mf.flags.block_size, + }); + elf.shndx.debug_line = try elf.addSection(elf.ni.elf, .{ + .name = ".debug_line", + .node_align = elf.mf.flags.block_size, + }); + }, + .code_view => unreachable, + } assert(elf.nodes.len == expected_nodes_len); assert(elf.shdrs.items.len == shnum - 1); // -1 to exclude SHN_UNDEF @@ -4599,7 +4967,7 @@ fn initHeaders( elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {}); } - if (have_dynamic_section) elf.dynamic = .{ + if (have_dynamic) elf.dynamic = .{ .flags = if (elf.options.z_now) std.elf.DF_BIND_NOW else 0, .flags_1 = f: { var f: u32 = 0; @@ -4677,15 +5045,26 @@ fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index { .ehdr, .shdr, .segment, + .value_debug_info, + .global_debug_info, + .frame_padding, + .func_debug_info, + .func_debug_line, => unreachable, - .section => |shndx| shndx, + .section, .section_manual_size => |shndx| shndx, .input_section, .copied_global, .nav, .uav, .lazy_code, .lazy_const_data, + .unit_frame, => elf.getNode(ni.parent(&elf.mf).unwrap().?).section, + .unit_frame_cie, .func_frame_fde => { + const unit_frame_ni = ni.parent(&elf.mf).unwrap().?; + assert(elf.getNode(unit_frame_ni) == .unit_frame); + return elf.getNode(unit_frame_ni.parent(&elf.mf).unwrap().?).section; + }, }; } fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { @@ -4699,31 +5078,52 @@ fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { .shdr, .segment, .copied_global, + .value_debug_info, + .global_debug_info, + .frame_padding, + .func_debug_info, + .func_debug_line, => unreachable, - .section => |shndx| shndx.vaddr(elf), + .section, .section_manual_size => |shndx| shndx.vaddr(elf), .input_section => |isi| isi.ptrConst(elf).vaddr, inline .nav, .uav, .lazy_code, .lazy_const_data, => |i| Symbol.Id.local(i.symbol(elf)).value(elf), + .unit_frame, .unit_frame_cie, .func_frame_fde => elf.computeNodeVAddr(ni), }; } fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { - const parent_vaddr = switch (elf.getNode(ni.parent(&elf.mf).unwrap().?)) { - .archive, - .archive_header, - .archive_input_member, - .archive_elf_member_header, - => unreachable, + const parent_ni = ni.parent(&elf.mf).unwrap().?; + const parent_vaddr = parent_vaddr: switch (elf.getNode(parent_ni)) { + .archive, .archive_header, .archive_input_member, .archive_elf_member_header => unreachable, .elf => return 0, .ehdr, .shdr => unreachable, .segment => |phndx| switch (elf.phdrSlice()) { inline else => |phdr| elf.targetLoad(&phdr[phndx].vaddr), }, - .section => |shndx| if (shndx == elf.shndx.tdata) 0 else shndx.vaddr(elf), + .section, .section_manual_size => |shndx| if (shndx == elf.shndx.tdata) 0 else shndx.vaddr(elf), .input_section, .copied_global => unreachable, - inline .nav, .uav, .lazy_code, .lazy_const_data => |i| Symbol.Id.local(i.symbol(elf)).value(elf), + inline .nav, + .uav, + .lazy_code, + .lazy_const_data, + => |i| Symbol.Id.local(i.symbol(elf)).value(elf), + .value_debug_info, + .global_debug_info, + .frame_padding, + => unreachable, + .unit_frame => { + const section_ni = parent_ni.parent(&elf.mf).unwrap().?; + const section_offset, _ = parent_ni.location(&elf.mf).resolve(&elf.mf); + break :parent_vaddr elf.getNode(section_ni).section.vaddr(elf) + section_offset; + }, + .unit_frame_cie, + .func_frame_fde, + .func_debug_info, + .func_debug_line, + => unreachable, }; const offset, _ = ni.location(&elf.mf).resolve(&elf.mf); return parent_vaddr + offset; @@ -4736,9 +5136,9 @@ fn getNodeElfOffset(elf: *Elf, ni: MappedFile.Node.Index) u64 { /// sequence of relocations, so that the caller may append the node's updated relocations. /// /// Asserts that `ni` must be a node which supports relocations (see `Elf.Node`). Does not support -/// the special-case sections '.plt' and '.dynamic'. +/// the special-case sections '.plt', '.dynamic', and '.eh_frame_hdr'. fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void { - const symbol_relocs: *SymbolReloc.Index, const got_relocs: ?*GotReloc.Index = switch (elf.getNode(ni)) { + const symbol_relocs: *SymbolReloc.Index, const node_relocs: ?*NodeReloc.Index, const got_relocs: ?*GotReloc.Index = switch (elf.getNode(ni)) { .archive, .archive_header, .archive_input_member, @@ -4748,24 +5148,42 @@ fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void { .shdr, .segment, .copied_global, + .value_debug_info, + .global_debug_info, + .frame_padding, + .unit_frame, + .unit_frame_cie, + .func_debug_info, + .func_debug_line, => unreachable, // cannot contain relocs - .section => unreachable, // cannot contain relocs (.plt and .dynamic unsupported) + .section, + .section_manual_size, + => unreachable, // cannot contain relocs (.plt, .dynamic, and .eh_frame_hdr unsupported) .input_section => |isi| .{ &elf.input_sections.items[@backingInt(isi)].first_symbol_reloc, + null, &elf.input_sections.items[@backingInt(isi)].first_got_reloc, }, .nav => |nmi| .{ &elf.navs.values()[@backingInt(nmi)].first_symbol_reloc, + null, &elf.navs.values()[@backingInt(nmi)].first_got_reloc, }, .uav => |umi| .{ &elf.uavs.values()[@backingInt(umi)].first_symbol_reloc, null, + null, }, inline .lazy_code, .lazy_const_data => |lmi| .{ &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_symbol_reloc, + null, &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_got_reloc, }, + .func_frame_fde => |fi| .{ + &elf.dwarf_funcs.items[@backingInt(fi)].func_frame_fde_first_symbol_reloc, + &elf.dwarf_funcs.items[@backingInt(fi)].func_frame_fde_first_node_reloc, + null, + }, }; if (symbol_relocs.* != .none) { @@ -4779,6 +5197,16 @@ fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void { } symbol_relocs.* = @fromBackingInt(@intCast(elf.symbol_relocs.items.len)); + if (node_relocs) |ptr| { + if (ptr.* != .none) { + for (elf.node_relocs.items[@backingInt(ptr.*)..]) |*reloc| { + if (reloc.node != ni) break; + reloc.delete(elf); + } + } + ptr.* = @fromBackingInt(@intCast(elf.node_relocs.items.len)); + } + if (got_relocs) |ptr| { if (ptr.* != .none) { for (elf.got_relocs.items[@backingInt(ptr.*)..]) |*reloc| { @@ -4797,6 +5225,7 @@ fn flushMovedNodeRelocs( node: MappedFile.Node.Index, node_vaddr: u64, first_symbol_reloc: SymbolReloc.Index, + first_node_reloc: NodeReloc.Index, first_got_reloc: GotReloc.Index, ) void { if (first_symbol_reloc != .none) { @@ -4816,6 +5245,21 @@ fn flushMovedNodeRelocs( } } + if (first_node_reloc != .none) { + for (elf.node_relocs.items[@backingInt(first_node_reloc)..]) |*reloc| { + if (reloc.node != node) break; + if (reloc.rela_index.unwrap()) |rela_index| { + assert(elf.ehdrType() == .REL); + // The node has moved, so the offset of the relocation within the section might have + // changed, so update the `offset` field of the `ElfN.Rela` entry. + elf.getNodeShndx(reloc.node).get(elf).rela.shndx.relaSetOffset(elf, rela_index, node_vaddr + reloc.offset); + } else { + assert(elf.ehdrType() != .REL); + reloc.apply(elf); + } + } + } + if (first_got_reloc != .none) { for (elf.got_relocs.items[@backingInt(first_got_reloc)..]) |*reloc| { if (reloc.node != node.toOptional()) break; @@ -5226,7 +5670,7 @@ fn mapInputSection(elf: *Elf, opts: struct { } switch (elf.targetLoad(&shdr.type)) { - .NULL, .PROGBITS => {}, + .NULL, .PROGBITS, .X86_64_UNWIND => {}, else => return error.SectionTypeConflict, } @@ -5360,7 +5804,7 @@ fn uavMapIndex( .moved = true, // see assert at end of `genUav` .alignment = resolved_align, }); - var name_buf: [32]u8 = undefined; + var name_buf: [std.fmt.count("__anon_{d}", .{std.math.maxInt(u32)})]u8 = undefined; const name = std.mem.print( &name_buf, "__anon_{d}", @@ -5520,7 +5964,7 @@ fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (Load } var strtab: std.Io.Writer.Allocating = .init(gpa); defer strtab.deinit(); - while (r.takeStruct(std.elf.ar_hdr, native_endian)) |header| { + while (r.takeStruct(std.elf.ar_hdr, .native)) |header| { if (!std.mem.eql(u8, &header.ar_fmag, std.elf.ARFMAG)) return diags.failParse(path, "bad file magic", .{}); const offset = fr.logicalPos(); @@ -5717,13 +6161,13 @@ fn loadObject( for (sections[1..]) |*section| { if (section.shdr.name >= shstrtab.len) continue; const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0); + if (!comp.config.any_unwind_tables and std.mem.eql(u8, name, ".eh_frame")) continue; const opts: struct { shndx: Section.Index, - has_file_bits: bool, node_fixed: bool, } = switch (section.shdr.type) { else => continue, - .PROGBITS, .NOBITS => opts: { + .PROGBITS, .NOBITS, .X86_64_UNWIND => opts: { const shndx = elf.mapInputSection(.{ .name = name, .flags = section.shdr.flags.shf, @@ -5776,7 +6220,6 @@ fn loadObject( } break :opts .{ .shndx = shndx, - .has_file_bits = section.shdr.type == .PROGBITS, // For well-known sections, we know that it's fine to have e.g. random // padding, so there's no need to make the sections fixed. For custom // sections, however, we do want fixed nodes to avoid padding. @@ -5819,7 +6262,6 @@ fn loadObject( } break :shndx shndx.*; }, - .has_file_bits = true, // This node must be fixed to prevent padding from being added between different // INIT_ARRAY/FINI_ARRAY/PREINIT_ARRAY input sections. .node_fixed = true, @@ -5855,7 +6297,7 @@ fn loadObject( .input = input_index, .file_location = .{ .offset = fl.offset + section.shdr.offset, - .size = if (opts.has_file_bits) section.shdr.size else 0, + .size = if (section.shdr.type == .NOBITS) 0 else section.shdr.size, }, // The section vaddr is initially 0, because the symbol addresses are // zero-based. This will eventually be updated by `flushMoved`. @@ -6407,6 +6849,7 @@ fn createInitFiniArraySection( .type = @"type", .flags = .{ .WRITE = true, .ALLOC = true }, .node_align = addr_align, + .manual_size = true, }); elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {}); try elf.ensureUnusedSymbolCapacity(2, .maybe_global); @@ -6455,7 +6898,9 @@ fn prelinkInner(elf: *Elf) Error!void { const comp = elf.base.comp; const gpa = comp.gpa; - if (comp.zcu != null and !comp.config.use_llvm and elf.ni.elf == .root) { + if (comp.zcu) |zcu| self_hosted_codegen: { + if (comp.config.use_llvm) break :self_hosted_codegen; + // We're using self-hosted codegen---add an input representing the Zig "object". try elf.ensureUnusedSymbolCapacity(1, .all_local); try elf.inputs.ensureUnusedCapacity(gpa, 1); @@ -6475,6 +6920,37 @@ fn prelinkInner(elf: *Elf) Error!void { .extra = .{ .file_symbol = zcu_file_symbol }, }; elf.input_pending_index += 1; + + try elf.dwarf.initUnits(zcu); + try elf.dwarf_units.appendNTimes(gpa, .{ + .unit_frame_cie_first_target_reloc = .none, + }, elf.dwarf.units.count()); + + try elf.nodes.ensureUnusedCapacity(gpa, 2); + for ( + [2]Dwarf.Frame.Format{ .eh_frame, .debug_frame }, + [2]Section.Index{ elf.shndx.eh_frame, elf.shndx.debug_frame }, + ) |format, frame_shndx| { + if (frame_shndx == .UNDEF) continue; + const frame_ni = frame_shndx.get(elf).ni; + _ = frame_ni.last(&elf.mf).unwrap() orelse continue; + const frame_padding_ni = try frame_ni.addFloatingChild(&elf.mf, gpa, .{ + .alignment = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .@"4", + .@"64" => .@"8", + }, + .next_moved = true, + .enable_next_moved = true, + }); + elf.nodes.appendAssumeCapacity(.frame_padding); + var cie_writer: MappedFile.Node.Writer = undefined; + frame_padding_ni.writer(&elf.mf, gpa, &cie_writer); + defer cie_writer.deinit(); + elf.dwarf.genDebugFrameCie(&cie_writer.interface, null, format) catch |err| switch (err) { + error.WriteFailed => return cie_writer.err.?, + }; + } } } @@ -6610,7 +7086,7 @@ fn flushDynamic(elf: *Elf) void { dynamic_index += 9; assert(dynamic_index == dynamic_entries.len); - if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry| + if (elf.targetEndian() != std.lang.Endian.native) for (dynamic_entries) |*dynamic_entry| std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry); }, } @@ -6626,6 +7102,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { addralign: Alignment = .@"1", entsize: std.elf.Word = 0, node_align: Alignment = .@"1", + manual_size: bool = false, }) Error!Section.Index { switch (opts.type) { .NULL => assert(opts.size == 0), @@ -6679,6 +7156,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { .size = opts.node_align.forward(opts.size), .alignment = opts.addralign.max(opts.node_align), .resized = opts.size > 0, + .bubbles_moved = opts.flags.ALLOC, }); const addr = elf.computeNodeVAddr(ni); const lsi: Symbol.LocalIndex = if (opts.flags.ALLOC) elf.addLocalSymbolAssumeCapacity(.{ @@ -6694,7 +7172,10 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { .RELA => .{ .free_head = .none }, else => .{ .shndx = .UNDEF }, } }); - elf.nodes.appendAssumeCapacity(.{ .section = shndx }); + elf.nodes.appendAssumeCapacity(switch (opts.manual_size) { + false => .{ .section = shndx }, + true => .{ .section_manual_size = shndx }, + }); switch (elf.shdrPtr(shndx)) { inline else => |shdr, class| { shdr.* = .{ @@ -6709,7 +7190,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { .addralign = @intCast(opts.addralign.toByteUnits()), .entsize = opts.entsize, }; - if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(class.ElfN().Shdr, shdr); + if (elf.targetEndian() != std.lang.Endian.native) std.mem.byteSwapAllFields(class.ElfN().Shdr, shdr); }, } return shndx; @@ -6719,6 +7200,7 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) if (len == 0) return; const gpa = elf.base.comp.gpa; try elf.symbol_relocs.ensureUnusedCapacity(gpa, len); + try elf.node_relocs.ensureUnusedCapacity(gpa, len); try elf.got_relocs.ensureUnusedCapacity(gpa, len); const class = elf.identClass(); switch (elf.ehdrType()) { @@ -6749,6 +7231,7 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) inline else => |ct_class| @sizeOf(ct_class.ElfN().Rela), }, .node_align = elf.mf.flags.block_size, + .manual_size = true, }); elf.section_by_name.putAssumeCapacityNoClobber(rela_shndx.name(elf), {}); shndx.get(elf).rela.shndx = rela_shndx; @@ -6795,15 +7278,10 @@ fn addRelocAssumeCapacity( .addend = addend, }); const ri: SymbolReloc.Index = @fromBackingInt(@intCast(elf.symbol_relocs.items.len)); - const next: SymbolReloc.Index = next: { - const target_ptr = target.index(elf).ptr(elf); - const next = target_ptr.first_target_reloc; - target_ptr.first_target_reloc = ri; - break :next next; - }; - if (next != .none) { - next.get(elf).prev = ri; - } + const first_target_reloc = &target.index(elf).ptr(elf).first_target_reloc; + const next = first_target_reloc.*; + first_target_reloc.* = ri; + if (next != .none) next.get(elf).prev = ri; elf.symbol_relocs.appendAssumeCapacity(.{ .node = node, .offset = offset, @@ -6816,7 +7294,6 @@ fn addRelocAssumeCapacity( .result = .ok, }); }, - .DYN, .EXEC => switch (elf.ehdrMachine()) { .AARCH64 => switch (@"type".AARCH64) { .NONE => {}, @@ -7239,12 +7716,10 @@ fn addSymbolRelocAssumeCapacity( }; const ri: SymbolReloc.Index = @fromBackingInt(@intCast(elf.symbol_relocs.items.len)); - const target_ptr = target.index(elf).ptr(elf); - const next = target_ptr.first_target_reloc; - target_ptr.first_target_reloc = ri; - if (next != .none) { - next.get(elf).prev = ri; - } + const first_target_reloc = &target.index(elf).ptr(elf).first_target_reloc; + const next = first_target_reloc.*; + first_target_reloc.* = ri; + if (next != .none) next.get(elf).prev = ri; elf.symbol_relocs.appendAssumeCapacity(.{ .node = node, .offset = offset, @@ -7263,6 +7738,92 @@ fn addSymbolRelocAssumeCapacity( // Actually apply the new relocation! ri.get(elf).apply(elf); } +fn addNodeRelocAssumeCapacity( + elf: *Elf, + node: MappedFile.Node.Index, + offset: u64, + target: MappedFile.Node.Index, + addend: i64, + @"type": NodeReloc.Type, +) Error!void { + const shndx = elf.getNodeShndx(target); + assert(!shndx.flags(elf).ALLOC); // not yet needed so not implemented + const first_target_reloc = switch (elf.getNode(target)) { + else => unreachable, + .unit_frame_cie => |ui| &elf.dwarf_units.items[@backingInt(ui)].unit_frame_cie_first_target_reloc, + }; + const next = first_target_reloc.*; + const ri: NodeReloc.Index = @fromBackingInt(@intCast(elf.node_relocs.items.len)); + first_target_reloc.* = ri; + if (next != .none) next.get(elf).prev = ri; + switch (elf.ehdrType()) { + .REL => { + const rela_shndx = elf.getNodeShndx(node).get(elf).rela.shndx; + const rela_index = rela_shndx.relaAddOneAssumeCapacity(elf, .{ + .type = switch (elf.ehdrMachine()) { + .AARCH64 => .{ .AARCH64 = switch (@"type") { + .abs32 => .ABS32, + .abs64 => .ABS64, + } }, + .LOONGARCH => .{ .LARCH = switch (@"type") { + .abs32 => .@"32", + .abs64 => .@"64", + } }, + .PPC64 => .{ .PPC64 = switch (@"type") { + .abs32 => .ADDR32, + .abs64 => .ADDR64, + } }, + .RISCV => .{ .RISCV = switch (@"type") { + .abs32 => .@"32", + .abs64 => .@"64", + } }, + .SPARCV9 => .{ .SPARC = switch (@"type") { + .abs32 => .UA32, + .abs64 => .UA64, + } }, + .X86_64 => .{ .SPARC = switch (@"type") { + .abs32 => .@"32", + .abs64 => .@"64", + } }, + }, + // This field needs to equal the offset into the section, which is *not* necessarily + // the same thing as our `offset`, which is the offset into `node`. We could compute + // the section offset now, but there's no point, because `flushMovedNodeRelocs` will + // eventually do it for us anyway, so just init to 0. + .offset = 0, + .raw_sym_index = @backingInt(shndx.get(elf).lsi.index()), + .addend = addend, + }); + elf.node_relocs.appendAssumeCapacity(.{ + .node = node, + .offset = offset, + .type = undefined, + .target = target, + .addend = addend, + .next = next, + .prev = .none, + .rela_index = rela_index.toOptional(), + .result = .ok, + }); + }, + .DYN, .EXEC => { + elf.node_relocs.appendAssumeCapacity(.{ + .node = node, + .offset = offset, + .target = target, + .addend = addend, + .type = @"type", + .next = next, + .prev = .none, + .rela_index = .none, + .result = .ok, + }); + + // Actually apply the new relocation! + ri.get(elf).apply(elf); + }, + } +} fn addGotRelocAssumeCapacity( elf: *Elf, node: MappedFile.Node.Index, @@ -7282,8 +7843,17 @@ fn addGotRelocAssumeCapacity( .shdr, .segment, .copied_global, + .value_debug_info, + .global_debug_info, + .frame_padding, + .unit_frame, + .unit_frame_cie, + .func_frame_fde, + .func_debug_info, + .func_debug_line, => unreachable, // cannot contain relocs, .section, + .section_manual_size, .uav, => unreachable, // cannot contain GOT relocs .input_section, @@ -7572,7 +8142,6 @@ fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) const nmi = try elf.navMapIndex(zcu, nav_index); const ni = nmi.symbol(elf).index().ptr(elf).node.unwrap().?; - elf.resetNodeRelocs(ni); // Ensure the NAV is marked as moved so that once we're done, `flushMoved` will eventually be // called to apply the NAV's new relocations. @@ -7582,6 +8151,7 @@ fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) var nw: MappedFile.Node.Writer = undefined; ni.writer(&elf.mf, gpa, &nw); defer nw.deinit(); + elf.resetNodeRelocs(ni); codegen.generateSymbol( &elf.base, pt, @@ -7628,7 +8198,6 @@ fn updateFuncInner( const nmi = try elf.navMapIndex(zcu, func.owner_nav); log.debug("updateFunc({f}) = {d}", .{ nav.fqn.fmt(ip), nmi.symbol(elf) }); const ni = nmi.symbol(elf).index().ptr(elf).node.unwrap().?; - elf.resetNodeRelocs(ni); // Ensure the NAV is marked as moved so that once we're done, `flushMoved` will eventually be // called to apply the NAV's new relocations. @@ -7638,6 +8207,132 @@ fn updateFuncInner( var nw: MappedFile.Node.Writer = undefined; ni.writer(&elf.mf, gpa, &nw); defer nw.deinit(); + var debug: Dwarf.WipNav.Debug = undefined; + const debug_output: link.File.DebugInfoOutput = debug_output: { + if (elf.ehdrMachine() != .X86_64) break :debug_output .none; + const dwarf = &elf.dwarf; + const mod = zcu.navFileScope(func.owner_nav).mod.?; + if (mod.strip and mod.unwind_tables == .none) break :debug_output .none; + + try elf.nodes.ensureUnusedCapacity(gpa, 5); + const dwarf_func_index = try dwarf.getFunc(func.owner_nav); + try elf.dwarf_funcs.appendNTimes(gpa, .{ + .func_frame_fde_first_symbol_reloc = .none, + .func_frame_fde_first_node_reloc = .none, + }, @backingInt(dwarf_func_index) + 1 -| elf.dwarf_funcs.items.len); + + debug.wip_nav = .{ + .dwarf = dwarf, + .unit = dwarf.getUnit(mod), + .func = dwarf_func_index, + .func_si = Symbol.Id.local(nmi.symbol(elf)).toTypeErased(), + .cfi = .{ + .loc = 0, + .cfa = dwarf.frame.header.initial_instructions[0].def_cfa, + }, + .frame_format = switch (mod.unwind_tables) { + .none => .debug_frame, + .sync, .async => .eh_frame, + }, + .fde_writer = undefined, + .frame_func_length_offset = std.math.maxInt(usize), + }; + const unit = debug.wip_nav.unit.get(dwarf); + const frame_align: Alignment = switch (elf.identClass()) { + .NONE, _ => unreachable, + .@"32" => .@"4", + .@"64" => .@"8", + }; + const frame_ni = unit.frame_ni.unwrap() orelse frame_ni: { + const frame_ni = try switch (debug.wip_nav.frame_format) { + .debug_frame => elf.shndx.debug_frame, + .eh_frame => elf.shndx.eh_frame, + }.get(elf).ni.addFloatingChild(&elf.mf, gpa, .{ + .alignment = frame_align.max(elf.mf.flags.block_size), + .next_moved = true, + .enable_next_moved = true, + }); + unit.frame_ni = .wrap(frame_ni); + elf.nodes.appendAssumeCapacity(.{ .unit_frame = debug.wip_nav.unit }); + break :frame_ni frame_ni; + }; + _ = unit.cie_ni.unwrap() orelse { + const cie_ni = try frame_ni.addOnlyHeaderChild(&elf.mf, gpa, .{ + .alignment = frame_align, + .next_moved = true, + .enable_next_moved = true, + }); + unit.cie_ni = .wrap(cie_ni); + elf.nodes.appendAssumeCapacity(.{ .unit_frame_cie = debug.wip_nav.unit }); + var cie_writer: MappedFile.Node.Writer = undefined; + cie_ni.writer(&elf.mf, gpa, &cie_writer); + defer cie_writer.deinit(); + dwarf.genDebugFrameCie(&cie_writer.interface, switch (elf.ehdrMachine()) { + else => unreachable, + .X86_64 => .x86_64, + }, debug.wip_nav.frame_format) catch |err| switch (err) { + error.WriteFailed => return cie_writer.err.?, + }; + @memset(cie_writer.interface.unusedCapacitySlice(), std.dwarf.CFA.nop); + }; + const dwarf_func = dwarf_func_index.get(dwarf); + const fde_ni = if (dwarf_func.fde_ni.unwrap()) |fde_ni| fde_ni: { + try fde_ni.moved(gpa, &elf.mf); + try fde_ni.nextMoved(gpa, &elf.mf); + break :fde_ni fde_ni; + } else fde_ni: { + const fde_ni = try frame_ni.addFloatingChild(&elf.mf, gpa, .{ + .alignment = frame_align, + .moved = true, + .next_moved = true, + .enable_next_moved = true, + }); + dwarf_func.fde_ni = .wrap(fde_ni); + break :fde_ni fde_ni; + }; + fde_ni.writer(&elf.mf, gpa, &debug.wip_nav.fde_writer); + if (mod.strip) break :debug_output .{ .eh_frame = &debug.wip_nav }; + + debug.pt = pt; + debug.any_children = false; + debug.blocks = .empty; + const debug_info_ni = dwarf_func.debug_info_ni.unwrap() orelse debug_info_ni: { + const debug_info_ni = + try elf.shndx.debug_info.get(elf).ni.addFloatingChild(&elf.mf, gpa, .{ + .next_moved = true, + .enable_next_moved = true, + }); + dwarf_func.debug_info_ni = .wrap(debug_info_ni); + elf.nodes.appendAssumeCapacity(.{ .func_debug_info = dwarf_func_index }); + break :debug_info_ni debug_info_ni; + }; + debug_info_ni.writer(&elf.mf, gpa, &debug.info_writer); + const debug_line_ni = dwarf_func.debug_line_ni.unwrap() orelse debug_line_ni: { + const debug_line_ni = + try elf.shndx.debug_line.get(elf).ni.addFloatingChild(&elf.mf, gpa, .{ + .next_moved = true, + .enable_next_moved = true, + }); + elf.nodes.appendAssumeCapacity(.{ .func_debug_line = dwarf_func_index }); + break :debug_line_ni debug_line_ni; + }; + debug_line_ni.writer(&elf.mf, gpa, &debug.line_writer); + break :debug_output .{ .dwarf2 = &debug }; + }; + defer switch (debug_output) { + .dwarf => unreachable, + inline .eh_frame, .dwarf2 => |dwarf| dwarf.deinit(gpa), + .none => {}, + }; + switch (debug_output) { + .dwarf => unreachable, + inline .eh_frame, .dwarf2 => |dwarf| { + elf.resetNodeRelocs(debug.wip_nav.func.?.get(&elf.dwarf).fde_ni.unwrap().?); + try dwarf.genFuncHeaders(); + }, + .none => {}, + } + elf.resetNodeRelocs(ni); codegen.emitFunction( &elf.base, pt, @@ -7645,13 +8340,35 @@ fn updateFuncInner( Node.toAtom(ni), mir, &nw.interface, - .none, + debug_output, ) catch |err| switch (err) { error.WriteFailed => return nw.err.?, else => |e| return e, }; + const func_length = nw.interface.end; + switch (debug_output) { + .dwarf => unreachable, + .eh_frame, .dwarf2 => { + debug.wip_nav.finishDebugFrameFde(func_length); + const frame_ni = switch (debug.wip_nav.frame_format) { + .debug_frame => elf.shndx.debug_frame, + .eh_frame => elf.shndx.eh_frame, + }.get(elf).ni; + try frame_ni.trimStart(&elf.mf, elf.base.comp.gpa); + switch (debug.wip_nav.frame_format) { + .debug_frame => {}, + .eh_frame => { + const last_offset, const last_size = + frame_ni.last(&elf.mf).unwrap().?.location(&elf.mf).resolve(&elf.mf); + const last_end = last_offset + last_size; + try frame_ni.ensureMinimumSize(&elf.mf, elf.base.comp.gpa, last_end + 4); + }, + } + }, + .none => {}, + } switch (elf.symPtr(nmi.symbol(elf).index())) { - inline else => |sym| elf.targetStore(&sym.size, @intCast(nw.interface.end)), + inline else => |sym| elf.targetStore(&sym.size, @intCast(func_length)), } } @@ -7915,11 +8632,11 @@ fn idleProgNode( var name: [std.Progress.Node.max_name_len]u8 = undefined; return prog_node.start(name: switch (node) { else => |tag| @tagName(tag), - .section => |shndx| shndx.name(elf).slice(elf), .archive_input_member => |ii| std.mem.print(&name, "{f}{f}", .{ ii.path(elf).fmtEscapeString(), fmtMemberString(ii.member(elf)), }) catch &name, + .section, .section_manual_size => |shndx| shndx.name(elf).slice(elf), .input_section => |isi| { const ii = isi.input(elf); break :name std.mem.print(&name, "{f}{f} {s}", .{ @@ -7935,6 +8652,31 @@ fn idleProgNode( .uav => |umi| std.mem.print(&name, "{f}", .{ Value.fromInterned(umi.uavValue(elf)).fmtValue(.{ .zcu = elf.base.comp.zcu.?, .tid = tid }), }) catch &name, + .value_debug_info => |cpi| std.mem.print(&name, "debug info for {f}", .{ + Value.fromInterned(cpi.val(&elf.dwarf.const_pool)) + .fmtValue(.{ .zcu = elf.base.comp.zcu.?, .tid = tid }), + }) catch &name, + .global_debug_info => |gi| { + const ip = &elf.base.comp.zcu.?.intern_pool; + break :name std.mem.print(&name, "debug info for {f}", .{ + ip.getNav(gi.nav(&elf.dwarf)).fqn.fmt(ip), + }) catch &name; + }, + .unit_frame, .unit_frame_cie => |ui| std.mem.print(&name, "unwind info for {s}", .{ + ui.mod(&elf.dwarf).fully_qualified_name, + }) catch &name, + .func_frame_fde, .func_debug_info, .func_debug_line => |fi, tag| { + const ip = &elf.base.comp.zcu.?.intern_pool; + break :name std.mem.print(&name, "{s} info for {f}", .{ + switch (tag) { + else => unreachable, + .func_frame_fde => "unwind", + .func_debug_info => "debug", + .func_debug_line => "line", + }, + ip.getNav(fi.nav(&elf.dwarf)).fqn.fmt(ip), + }) catch &name; + }, }, 0); } @@ -7984,11 +8726,11 @@ fn genUav( const uav_val = umi.uavValue(elf); const ni = umi.symbol(elf).index().ptr(elf).node.unwrap().?; - elf.resetNodeRelocs(ni); var nw: MappedFile.Node.Writer = undefined; ni.writer(&elf.mf, gpa, &nw); defer nw.deinit(); + elf.resetNodeRelocs(ni); codegen.generateSymbol( &elf.base, pt, @@ -8013,7 +8755,6 @@ fn genLazy(elf: *Elf, pt: Zcu.PerThread, lmr: Node.LazyMapRef) Error!void { const lazy = lmr.lazySymbol(elf); const ni = lmr.symbol(elf).index().ptr(elf).node.unwrap().?; - elf.resetNodeRelocs(ni); // Ensure the lazy node is marked as moved so that once we're done, `flushMoved` will eventually // be called to apply the lazy node's new relocations. @@ -8023,6 +8764,7 @@ fn genLazy(elf: *Elf, pt: Zcu.PerThread, lmr: Node.LazyMapRef) Error!void { var nw: MappedFile.Node.Writer = undefined; ni.writer(&elf.mf, gpa, &nw); defer nw.deinit(); + elf.resetNodeRelocs(ni); codegen.generateLazySymbol( &elf.base, pt, @@ -8155,7 +8897,7 @@ fn flushElfOffset(elf: *Elf, ni: MappedFile.Node.Index) void { elf.flushElfOffset(child_ni); } }, - .section => |shndx| switch (elf.shdrPtr(shndx)) { + .section, .section_manual_size => |shndx| switch (elf.shdrPtr(shndx)) { inline else => |shdr| elf.targetStore(&shdr.offset, @intCast(elf_offset)), }, } @@ -8194,6 +8936,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void .INTERP, .PHDR, .TLS, + .GNU_EH_FRAME, .GNU_RELRO, => { const new_vaddr = elf.computeNodeVAddr(ni); @@ -8204,14 +8947,11 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void }, } }, - .section => |shndx| { + .section, .section_manual_size => |shndx| { elf.flushElfOffset(ni); const addr = elf.computeNodeVAddr(ni); const old_addr: u64, const flags: std.elf.SHF = switch (elf.shdrPtr(shndx)) { - inline else => |shdr| .{ - elf.targetLoad(&shdr.addr), - elf.targetLoad(&shdr.flags).shf, - }, + inline else => |shdr| .{ elf.targetLoad(&shdr.addr), elf.targetLoad(&shdr.flags).shf }, }; if (flags.ALLOC) { @@ -8225,10 +8965,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void var name = first_name; while (name != .empty) { const old_sym_addr = Symbol.Id.global(name).value(elf); - Symbol.Id.global(name).flushMoved( - elf, - old_sym_addr - old_addr + addr, - ); + Symbol.Id.global(name).flushMoved(elf, old_sym_addr - old_addr + addr); name = elf.globalByName(name).?.next_in_node; } } @@ -8246,12 +8983,14 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void reloc.apply(elf); } } else if (shndx == elf.shndx.plt) { - elf.flushMovedNodeRelocs(ni, addr, elf.plt_first_symbol_reloc, .none); + elf.flushMovedNodeRelocs(ni, addr, elf.plt_first_symbol_reloc, .none, .none); elf.flushMovedPltSection(.plt, old_addr, addr); } else if (shndx == elf.shndx.got_plt) { elf.flushMovedPltSection(.got_plt, old_addr, addr); } else if (shndx == elf.shndx.plt_sec) { elf.flushMovedPltSection(.plt_sec, old_addr, addr); + } else if (shndx == elf.shndx.eh_frame_hdr) { + elf.flushMovedNodeRelocs(ni, addr, elf.eh_frame_hdr_first_symbol_reloc, .none, .none); } }, .input_section => |isi| { @@ -8302,6 +9041,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void ni, new_section_addr, isi.ptrConst(elf).first_symbol_reloc, + .none, isi.ptrConst(elf).first_got_reloc, ); }, @@ -8329,13 +9069,45 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void name = elf.globalByName(name).?.next_in_node; } } + elf.flushMovedNodeRelocs(ni, new_addr, mi.firstSymbolReloc(elf), .none, mi.firstGotReloc(elf)); + }, + .value_debug_info, + .global_debug_info, + .frame_padding, + .unit_frame, + => {}, + .unit_frame_cie => |ui| { + const dwarf_unit = &elf.dwarf_units.items[@backingInt(ui)]; + var target_ri = dwarf_unit.unit_frame_cie_first_target_reloc; + while (target_ri != .none) { + const target_reloc = target_ri.get(elf); + assert(target_reloc.target == ni); + target_reloc.apply(elf); + target_ri = target_reloc.next; + } + }, + .func_frame_fde => |fi| { + const new_addr = elf.computeNodeVAddr(ni); + const dwarf_func = &elf.dwarf_funcs.items[@backingInt(fi)]; + const mod = elf.base.comp.zcu.?.navFileScope(fi.nav(&elf.dwarf)).mod.?; + switch (mod.unwind_tables) { + .none => {}, + .sync, .async => { + const offset, _ = ni.location(&elf.mf).resolve(&elf.mf); + elf.dwarf.updateEhFrameFde(ni.slice(&elf.mf), offset); + }, + } elf.flushMovedNodeRelocs( ni, new_addr, - mi.firstSymbolReloc(elf), - mi.firstGotReloc(elf), + dwarf_func.func_frame_fde_first_symbol_reloc, + dwarf_func.func_frame_fde_first_node_reloc, + .none, ); }, + .func_debug_info, + .func_debug_line, + => {}, } try ni.childrenMoved(elf.base.comp.gpa, &elf.mf); } @@ -8523,7 +9295,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo elf.targetStore(&ph.type, if (size > 0) .LOAD else .NULL); try elf.allocateSegmentLoadAddress(phndx); }, - .DYNAMIC, .INTERP, .PHDR, std.elf.PT.GNU_RELRO => { + .DYNAMIC, .INTERP, .PHDR, .GNU_EH_FRAME, .GNU_RELRO => { elf.targetStore(&ph.memsz, @intCast(size)); }, .TLS => { @@ -8558,31 +9330,29 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo inline else => |shdr| { switch (elf.targetLoad(&shdr.type)) { else => unreachable, - .NULL => if (size > 0) elf.targetStore(&shdr.type, .PROGBITS), .PROGBITS => if (size == 0) elf.targetStore(&shdr.type, .NULL), - - .INIT_ARRAY, - .FINI_ARRAY, - .PREINIT_ARRAY, - .STRTAB, - .SYMTAB, - .DYNAMIC, - .REL, - .RELA, - .DYNSYM, - .HASH, - => return, - } - if (shndx != elf.shndx.plt and - shndx != elf.shndx.got and - shndx != elf.shndx.got_plt) - { - elf.targetStore(&shdr.size, @intCast(size)); + .X86_64_UNWIND => {}, } + elf.targetStore(&shdr.size, @intCast(size)); }, }, - .input_section, .copied_global, .nav, .uav, .lazy_code, .lazy_const_data => {}, + .section_manual_size, + .input_section, + .copied_global, + .nav, + .uav, + .lazy_code, + .lazy_const_data, + .value_debug_info, + .global_debug_info, + .frame_padding, + .unit_frame, + .unit_frame_cie, + .func_frame_fde, + .func_debug_info, + .func_debug_line, + => {}, } } @@ -8599,6 +9369,7 @@ fn flushNextMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error! .shdr, .segment, .section, + .section_manual_size, .input_section, .copied_global, .nav, @@ -8633,6 +9404,66 @@ fn flushNextMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error! error.NoSpaceLeft => archive.strtab_member_too_big = true, } }, + .value_debug_info, + .global_debug_info, + => {}, + .frame_padding, .unit_frame_cie, .func_frame_fde => |_, tag| { + const offset, const size = ni.location(&elf.mf).resolve(&elf.mf); + const slice = slice: { + const parent_ni = ni.parent(&elf.mf).unwrap().?; + if (ni.next(&elf.mf).unwrap()) |next_ni| { + const parent_slice = parent_ni.slicePadding(&elf.mf); + const next_offset, _ = next_ni.location(&elf.mf).resolve(&elf.mf); + break :slice parent_slice[@intCast(offset)..@intCast(next_offset)]; + } else switch (tag) { + else => unreachable, + .frame_padding => { + const frame_slice = parent_ni.slicePadding(&elf.mf); + switch (elf.getNode(parent_ni).section.debugFrameFormat(elf).?) { + .eh_frame => { + const end = frame_slice.len - 4; + std.mem.writeInt(u32, frame_slice[end..][0..4], 0, elf.dwarf.endian); + break :slice frame_slice[@intCast(offset)..end]; + }, + .debug_frame => break :slice frame_slice[@intCast(offset)..], + } + }, + .unit_frame_cie, .func_frame_fde => { + const parent_offset, _ = parent_ni.location(&elf.mf).resolve(&elf.mf); + const frame_ni = parent_ni.parent(&elf.mf).unwrap().?; + const frame_slice = frame_ni.slicePadding(&elf.mf); + const format = elf.getNode(frame_ni).section.debugFrameFormat(elf).?; + const slice = frame_slice[@intCast(parent_offset + offset)..if (parent_ni.next(&elf.mf).unwrap()) |parent_next_ni| frame_end: { + const parent_next_offset, _ = parent_next_ni.location(&elf.mf).resolve(&elf.mf); + break :frame_end @intCast(parent_next_offset); + } else frame_end: switch (format) { + .eh_frame => { + const frame_end = frame_slice.len - 4; + std.mem.writeInt(u32, frame_slice[frame_end..][0..4], 0, elf.dwarf.endian); + break :frame_end frame_end; + }, + .debug_frame => frame_slice.len, + }]; + var fw: std.Io.Writer = .fixed(slice[@intCast(size)..]); + elf.dwarf.genDebugFrameCie(&fw, null, format) catch |err| switch (err) { + error.WriteFailed => break :slice slice, + }; + elf.dwarf.updateUnitLength(fw.buffer, fw.buffer.len); + break :slice slice[0..@intCast(size)]; + }, + } + }; + elf.dwarf.updateUnitLength(slice, slice.len); + switch (tag) { + else => unreachable, + .frame_padding => {}, + .unit_frame_cie, .func_frame_fde => @memset(slice[@intCast(size)..], std.dwarf.CFA.nop), + } + }, + .unit_frame, + .func_debug_info, + .func_debug_line, + => {}, } } @@ -9118,7 +9949,7 @@ pub fn printNode( try w.writeByte(')'); }, }, - .section => |shndx| try w.print("({s})", .{shndx.name(elf).slice(elf)}), + .section, .section_manual_size => |shndx| try w.print("({s})", .{shndx.name(elf).slice(elf)}), .input_section => |isi| { const ii = isi.input(elf); try w.print("({f}{f}, {s})", .{ @@ -9151,6 +9982,31 @@ pub fn printNode( .tid = tid, }), }), + .value_debug_info => |cpi| try w.print("({f})", .{ + Value.fromInterned(cpi.val(&elf.dwarf.const_pool)) + .fmtValue(.{ .zcu = elf.base.comp.zcu.?, .tid = tid }), + }), + .global_debug_info => |gi| { + const zcu = elf.base.comp.zcu.?; + const ip = &zcu.intern_pool; + const nav = ip.getNav(gi.nav(&elf.dwarf)); + try w.print("({f}, {f})", .{ + Type.fromInterned(ip.typeOf(nav.resolved.?.value)).fmt(.{ .zcu = zcu, .tid = tid }), + nav.fqn.fmt(ip), + }); + }, + .unit_frame, .unit_frame_cie => |ui| try w.print("({s})", .{ + ui.mod(&elf.dwarf).fully_qualified_name, + }), + .func_frame_fde, .func_debug_info, .func_debug_line => |fi| { + const zcu = elf.base.comp.zcu.?; + const ip = &zcu.intern_pool; + const nav = ip.getNav(fi.nav(&elf.dwarf)); + try w.print("({f}, {f})", .{ + Type.fromInterned(ip.typeOf(nav.resolved.?.value)).fmt(.{ .zcu = zcu, .tid = tid }), + nav.fqn.fmt(ip), + }); + }, } { const mf_node = &elf.mf.nodes.items[@backingInt(ni)]; diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index 0ff8358077b1c7a7d636a3164bdfe4d677cdcd97..d6e87a551ac0a5c15fb94dea437db825d906fb26 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -647,14 +647,11 @@ pub fn getNavVAddr( }, }); }, - .debug_output => |debug_output| switch (debug_output) { - .dwarf => |wip_nav| try wip_nav.infoExternalReloc(.{ - .source_off = @intCast(reloc_info.offset), - .target_sym = @fromBackingInt(@intCast(sym_index)), - .target_off = reloc_info.addend, - }), - .none => unreachable, - }, + .debug_output => |debug_output| try debug_output.dwarf.infoExternalReloc(.{ + .source_off = @intCast(reloc_info.offset), + .target_sym = @fromBackingInt(@intCast(sym_index)), + .target_off = reloc_info.addend, + }), } return vaddr; } @@ -686,14 +683,11 @@ pub fn getUavVAddr( }, }); }, - .debug_output => |debug_output| switch (debug_output) { - .dwarf => |wip_nav| try wip_nav.infoExternalReloc(.{ - .source_off = @intCast(reloc_info.offset), - .target_sym = @fromBackingInt(@intCast(sym_index)), - .target_off = reloc_info.addend, - }), - .none => unreachable, - }, + .debug_output => |debug_output| try debug_output.dwarf.infoExternalReloc(.{ + .source_off = @intCast(reloc_info.offset), + .target_sym = @fromBackingInt(@intCast(sym_index)), + .target_off = reloc_info.addend, + }), } return vaddr; } @@ -796,6 +790,7 @@ pub fn updateFunc( if (debug_wip_nav) |*wip_nav| .{ .dwarf = wip_nav } else .none, ) catch |err| switch (err) { error.WriteFailed => return error.OutOfMemory, + error.MappedFileIo => unreachable, // MappedFile is not being used else => |e| return e, }; const code = aw.written(); diff --git a/src/link/MappedFile.zig b/src/link/MappedFile.zig index 937b0989a8d40dabcf74ea8c920ac47a2a5d1f67..4f7607632a9af7ab182aa785840f58c308292cac 100644 --- a/src/link/MappedFile.zig +++ b/src/link/MappedFile.zig @@ -636,11 +636,33 @@ pub const Node = extern struct { return mf.memory_map.memory[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)]; } + pub fn slicePadding(ni: Node.Index, mf: *const MappedFile) []u8 { + const file_loc = ni.fileLocation(mf, false); + return mf.memory_map.memory[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)]; + } + pub fn sliceConst(ni: Node.Index, mf: *const MappedFile) []const u8 { const file_loc = ni.fileLocation(mf, false); return mf.memory_map.memory[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)]; } + pub fn trimStart(ni: Node.Index, mf: *MappedFile, gpa: Allocator) Allocator.Error!void { + mf.nodes_lock.assertUnlocked(); + const node = ni.get(mf); + const first_ni = node.first.unwrap() orelse return; + const shift, _ = first_ni.location(mf).resolve(mf); + if (shift == 0) return; + const offset, const size = node.location().resolve(mf); + try ni.setLocation(mf, gpa, offset + shift, size - shift); + var child_oni = node.first; + while (child_oni.unwrap()) |child_ni| { + const child_node = child_ni.get(mf); + const child_offset, const child_size = child_node.location().resolve(mf); + try child_ni.setLocation(mf, gpa, child_offset - shift, child_size); + child_oni = child_node.next; + } + } + /// Ensures that the size of `ni` is at least `min_size`. Valid for any node. /// /// Applies `growth_factor` if necessary (so the caller should *not* apply `growth_factor`). diff --git a/tools/incr-check.zig b/tools/incr-check.zig index 49a2fae873d33b40a9eaf573fda416f6085840eb..21c24e9048203c80a5562f6c3b47fce31cb2e80d 100644 --- a/tools/incr-check.zig +++ b/tools/incr-check.zig @@ -17,6 +17,7 @@ const usage = \\Debug Options: \\ --preserve-tmp \\ --debug-log foo + \\ --debug-link-snapshot ; pub const std_options: std.Options = .{ @@ -60,6 +61,7 @@ pub fn main(init: std.process.Init) !void { var quiet: bool = false; var debug_log_args: std.ArrayList([]const u8) = .empty; + var debug_link_snapshot = false; var arg_it = try init.minimal.args.iterateAllocator(arena); _ = arg_it.skip(); @@ -77,6 +79,8 @@ pub fn main(init: std.process.Init) !void { arena, arg_it.next() orelse badUsage("expected arg after --debug-log", .{}), ); + } else if (std.mem.eql(u8, arg, "--debug-link-snapshot")) { + debug_link_snapshot = true; } else if (std.mem.eql(u8, arg, "--preserve-tmp")) { preserve_tmp = true; } else if (std.mem.eql(u8, arg, "-fqemu")) { @@ -173,6 +177,9 @@ pub fn main(init: std.process.Init) !void { for (debug_log_args.items) |arg| { try child_args.appendSlice(arena, &.{ "--debug-log", arg }); } + if (debug_link_snapshot) { + try child_args.append(arena, "--debug-link-snapshot"); + } for (case.modules) |mod| { try child_args.appendSlice(arena, &.{ "--dep", mod.name }); }