zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

blob ccfdfd0d (131691B) - Raw


      1 allocator: Allocator,
      2 bin_file: *File,
      3 format: Format,
      4 ptr_width: PtrWidth,
      5 
      6 /// A list of `Atom`s whose Line Number Programs have surplus capacity.
      7 /// This is the same concept as `Section.free_list` in Elf; see those doc comments.
      8 src_fn_free_list: std.AutoHashMapUnmanaged(Atom.Index, void) = .{},
      9 src_fn_first_index: ?Atom.Index = null,
     10 src_fn_last_index: ?Atom.Index = null,
     11 src_fns: std.ArrayListUnmanaged(Atom) = .{},
     12 src_fn_decls: AtomTable = .{},
     13 
     14 /// A list of `Atom`s whose corresponding .debug_info tags have surplus capacity.
     15 /// This is the same concept as `text_block_free_list`; see those doc comments.
     16 di_atom_free_list: std.AutoHashMapUnmanaged(Atom.Index, void) = .{},
     17 di_atom_first_index: ?Atom.Index = null,
     18 di_atom_last_index: ?Atom.Index = null,
     19 di_atoms: std.ArrayListUnmanaged(Atom) = .{},
     20 di_atom_decls: AtomTable = .{},
     21 
     22 dbg_line_header: DbgLineHeader,
     23 
     24 abbrev_table_offset: ?u64 = null,
     25 
     26 /// TODO replace with InternPool
     27 /// Table of debug symbol names.
     28 strtab: StringTable = .{},
     29 
     30 /// Quick lookup array of all defined source files referenced by at least one Decl.
     31 /// They will end up in the DWARF debug_line header as two lists:
     32 /// * []include_directory
     33 /// * []file_names
     34 di_files: std.AutoArrayHashMapUnmanaged(*const Module.File, void) = .{},
     35 
     36 global_abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{},
     37 
     38 const AtomTable = std.AutoHashMapUnmanaged(InternPool.DeclIndex, Atom.Index);
     39 
     40 const Atom = struct {
     41     /// Offset into .debug_info pointing to the tag for this Decl, or
     42     /// offset from the beginning of the Debug Line Program header that contains this function.
     43     off: u32,
     44     /// Size of the .debug_info tag for this Decl, not including padding, or
     45     /// size of the line number program component belonging to this function, not
     46     /// including padding.
     47     len: u32,
     48 
     49     prev_index: ?Index,
     50     next_index: ?Index,
     51 
     52     pub const Index = u32;
     53 };
     54 
     55 const DbgLineHeader = struct {
     56     minimum_instruction_length: u8,
     57     maximum_operations_per_instruction: u8,
     58     default_is_stmt: bool,
     59     line_base: i8,
     60     line_range: u8,
     61     opcode_base: u8,
     62 };
     63 
     64 /// Represents state of the analysed Decl.
     65 /// Includes Decl's abbrev table of type Types, matching arena
     66 /// and a set of relocations that will be resolved once this
     67 /// Decl's inner Atom is assigned an offset within the DWARF section.
     68 pub const DeclState = struct {
     69     dwarf: *Dwarf,
     70     mod: *Module,
     71     di_atom_decls: *const AtomTable,
     72     dbg_line_func: InternPool.Index,
     73     dbg_line: std.ArrayList(u8),
     74     dbg_info: std.ArrayList(u8),
     75     abbrev_type_arena: std.heap.ArenaAllocator,
     76     abbrev_table: std.ArrayListUnmanaged(AbbrevEntry),
     77     abbrev_resolver: std.AutoHashMapUnmanaged(InternPool.Index, u32),
     78     abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation),
     79     exprloc_relocs: std.ArrayListUnmanaged(ExprlocRelocation),
     80 
     81     pub fn deinit(self: *DeclState) void {
     82         const gpa = self.dwarf.allocator;
     83         self.dbg_line.deinit();
     84         self.dbg_info.deinit();
     85         self.abbrev_type_arena.deinit();
     86         self.abbrev_table.deinit(gpa);
     87         self.abbrev_resolver.deinit(gpa);
     88         self.abbrev_relocs.deinit(gpa);
     89         self.exprloc_relocs.deinit(gpa);
     90     }
     91 
     92     /// Adds local type relocation of the form: @offset => @this + addend
     93     /// @this signifies the offset within the .debug_abbrev section of the containing atom.
     94     fn addTypeRelocLocal(self: *DeclState, atom_index: Atom.Index, offset: u32, addend: u32) !void {
     95         log.debug("{x}: @this + {x}", .{ offset, addend });
     96         try self.abbrev_relocs.append(self.dwarf.allocator, .{
     97             .target = null,
     98             .atom_index = atom_index,
     99             .offset = offset,
    100             .addend = addend,
    101         });
    102     }
    103 
    104     /// Adds global type relocation of the form: @offset => @symbol + 0
    105     /// @symbol signifies a type abbreviation posititioned somewhere in the .debug_abbrev section
    106     /// which we use as our target of the relocation.
    107     fn addTypeRelocGlobal(self: *DeclState, atom_index: Atom.Index, ty: Type, offset: u32) !void {
    108         const gpa = self.dwarf.allocator;
    109         const resolv = self.abbrev_resolver.get(ty.toIntern()) orelse blk: {
    110             const sym_index: u32 = @intCast(self.abbrev_table.items.len);
    111             try self.abbrev_table.append(gpa, .{
    112                 .atom_index = atom_index,
    113                 .type = ty,
    114                 .offset = undefined,
    115             });
    116             log.debug("%{d}: {}", .{ sym_index, ty.fmt(self.mod) });
    117             try self.abbrev_resolver.putNoClobber(gpa, ty.toIntern(), sym_index);
    118             break :blk sym_index;
    119         };
    120         log.debug("{x}: %{d} + 0", .{ offset, resolv });
    121         try self.abbrev_relocs.append(gpa, .{
    122             .target = resolv,
    123             .atom_index = atom_index,
    124             .offset = offset,
    125             .addend = 0,
    126         });
    127     }
    128 
    129     fn addDbgInfoType(
    130         self: *DeclState,
    131         mod: *Module,
    132         atom_index: Atom.Index,
    133         ty: Type,
    134     ) error{OutOfMemory}!void {
    135         const dbg_info_buffer = &self.dbg_info;
    136         const target = mod.getTarget();
    137         const target_endian = target.cpu.arch.endian();
    138         const ip = &mod.intern_pool;
    139 
    140         switch (ty.zigTypeTag(mod)) {
    141             .NoReturn => unreachable,
    142             .Void => {
    143                 try dbg_info_buffer.append(@intFromEnum(AbbrevCode.zero_bit_type));
    144             },
    145             .Bool => {
    146                 try dbg_info_buffer.ensureUnusedCapacity(12);
    147                 dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.base_type));
    148                 // DW.AT.encoding, DW.FORM.data1
    149                 dbg_info_buffer.appendAssumeCapacity(DW.ATE.boolean);
    150                 // DW.AT.byte_size, DW.FORM.udata
    151                 try leb128.writeUleb128(dbg_info_buffer.writer(), ty.abiSize(mod));
    152                 // DW.AT.name, DW.FORM.string
    153                 try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(mod)});
    154             },
    155             .Int => {
    156                 const info = ty.intInfo(mod);
    157                 try dbg_info_buffer.ensureUnusedCapacity(12);
    158                 dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.base_type));
    159                 // DW.AT.encoding, DW.FORM.data1
    160                 dbg_info_buffer.appendAssumeCapacity(switch (info.signedness) {
    161                     .signed => DW.ATE.signed,
    162                     .unsigned => DW.ATE.unsigned,
    163                 });
    164                 // DW.AT.byte_size, DW.FORM.udata
    165                 try leb128.writeUleb128(dbg_info_buffer.writer(), ty.abiSize(mod));
    166                 // DW.AT.name, DW.FORM.string
    167                 try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(mod)});
    168             },
    169             .Optional => {
    170                 if (ty.isPtrLikeOptional(mod)) {
    171                     try dbg_info_buffer.ensureUnusedCapacity(12);
    172                     dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.base_type));
    173                     // DW.AT.encoding, DW.FORM.data1
    174                     dbg_info_buffer.appendAssumeCapacity(DW.ATE.address);
    175                     // DW.AT.byte_size, DW.FORM.udata
    176                     try leb128.writeUleb128(dbg_info_buffer.writer(), ty.abiSize(mod));
    177                     // DW.AT.name, DW.FORM.string
    178                     try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(mod)});
    179                 } else {
    180                     // Non-pointer optionals are structs: struct { .maybe = *, .val = * }
    181                     const payload_ty = ty.optionalChild(mod);
    182                     // DW.AT.structure_type
    183                     try dbg_info_buffer.append(@intFromEnum(AbbrevCode.struct_type));
    184                     // DW.AT.byte_size, DW.FORM.udata
    185                     const abi_size = ty.abiSize(mod);
    186                     try leb128.writeUleb128(dbg_info_buffer.writer(), abi_size);
    187                     // DW.AT.name, DW.FORM.string
    188                     try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(mod)});
    189                     // DW.AT.member
    190                     try dbg_info_buffer.ensureUnusedCapacity(21);
    191                     dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.struct_member));
    192                     // DW.AT.name, DW.FORM.string
    193                     dbg_info_buffer.appendSliceAssumeCapacity("maybe");
    194                     dbg_info_buffer.appendAssumeCapacity(0);
    195                     // DW.AT.type, DW.FORM.ref4
    196                     var index = dbg_info_buffer.items.len;
    197                     dbg_info_buffer.appendNTimesAssumeCapacity(0, 4);
    198                     try self.addTypeRelocGlobal(atom_index, Type.bool, @intCast(index));
    199                     // DW.AT.data_member_location, DW.FORM.udata
    200                     dbg_info_buffer.appendAssumeCapacity(0);
    201                     // DW.AT.member
    202                     dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.struct_member));
    203                     // DW.AT.name, DW.FORM.string
    204                     dbg_info_buffer.appendSliceAssumeCapacity("val");
    205                     dbg_info_buffer.appendAssumeCapacity(0);
    206                     // DW.AT.type, DW.FORM.ref4
    207                     index = dbg_info_buffer.items.len;
    208                     dbg_info_buffer.appendNTimesAssumeCapacity(0, 4);
    209                     try self.addTypeRelocGlobal(atom_index, payload_ty, @intCast(index));
    210                     // DW.AT.data_member_location, DW.FORM.udata
    211                     const offset = abi_size - payload_ty.abiSize(mod);
    212                     try leb128.writeUleb128(dbg_info_buffer.writer(), offset);
    213                     // DW.AT.structure_type delimit children
    214                     try dbg_info_buffer.append(0);
    215                 }
    216             },
    217             .Pointer => {
    218                 if (ty.isSlice(mod)) {
    219                     // Slices are structs: struct { .ptr = *, .len = N }
    220                     const ptr_bits = target.ptrBitWidth();
    221                     const ptr_bytes: u8 = @intCast(@divExact(ptr_bits, 8));
    222                     // DW.AT.structure_type
    223                     try dbg_info_buffer.ensureUnusedCapacity(2);
    224                     dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.struct_type));
    225                     // DW.AT.byte_size, DW.FORM.udata
    226                     try leb128.writeUleb128(dbg_info_buffer.writer(), ty.abiSize(mod));
    227                     // DW.AT.name, DW.FORM.string
    228                     try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(mod)});
    229                     // DW.AT.member
    230                     try dbg_info_buffer.ensureUnusedCapacity(21);
    231                     dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.struct_member));
    232                     // DW.AT.name, DW.FORM.string
    233                     dbg_info_buffer.appendSliceAssumeCapacity("ptr");
    234                     dbg_info_buffer.appendAssumeCapacity(0);
    235                     // DW.AT.type, DW.FORM.ref4
    236                     var index = dbg_info_buffer.items.len;
    237                     dbg_info_buffer.appendNTimesAssumeCapacity(0, 4);
    238                     const ptr_ty = ty.slicePtrFieldType(mod);
    239                     try self.addTypeRelocGlobal(atom_index, ptr_ty, @intCast(index));
    240                     // DW.AT.data_member_location, DW.FORM.udata
    241                     dbg_info_buffer.appendAssumeCapacity(0);
    242                     // DW.AT.member
    243                     dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.struct_member));
    244                     // DW.AT.name, DW.FORM.string
    245                     dbg_info_buffer.appendSliceAssumeCapacity("len");
    246                     dbg_info_buffer.appendAssumeCapacity(0);
    247                     // DW.AT.type, DW.FORM.ref4
    248                     index = dbg_info_buffer.items.len;
    249                     dbg_info_buffer.appendNTimesAssumeCapacity(0, 4);
    250                     try self.addTypeRelocGlobal(atom_index, Type.usize, @intCast(index));
    251                     // DW.AT.data_member_location, DW.FORM.udata
    252                     dbg_info_buffer.appendAssumeCapacity(ptr_bytes);
    253                     // DW.AT.structure_type delimit children
    254                     dbg_info_buffer.appendAssumeCapacity(0);
    255                 } else {
    256                     try dbg_info_buffer.ensureUnusedCapacity(9);
    257                     dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.ptr_type));
    258                     // DW.AT.type, DW.FORM.ref4
    259                     const index = dbg_info_buffer.items.len;
    260                     dbg_info_buffer.appendNTimesAssumeCapacity(0, 4);
    261                     try self.addTypeRelocGlobal(atom_index, ty.childType(mod), @intCast(index));
    262                 }
    263             },
    264             .Array => {
    265                 // DW.AT.array_type
    266                 try dbg_info_buffer.append(@intFromEnum(AbbrevCode.array_type));
    267                 // DW.AT.name, DW.FORM.string
    268                 try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(mod)});
    269                 // DW.AT.type, DW.FORM.ref4
    270                 var index = dbg_info_buffer.items.len;
    271                 try dbg_info_buffer.ensureUnusedCapacity(9);
    272                 dbg_info_buffer.appendNTimesAssumeCapacity(0, 4);
    273                 try self.addTypeRelocGlobal(atom_index, ty.childType(mod), @intCast(index));
    274                 // DW.AT.subrange_type
    275                 dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.array_dim));
    276                 // DW.AT.type, DW.FORM.ref4
    277                 index = dbg_info_buffer.items.len;
    278                 dbg_info_buffer.appendNTimesAssumeCapacity(0, 4);
    279                 try self.addTypeRelocGlobal(atom_index, Type.usize, @intCast(index));
    280                 // DW.AT.count, DW.FORM.udata
    281                 const len = ty.arrayLenIncludingSentinel(mod);
    282                 try leb128.writeUleb128(dbg_info_buffer.writer(), len);
    283                 // DW.AT.array_type delimit children
    284                 try dbg_info_buffer.append(0);
    285             },
    286             .Struct => {
    287                 // DW.AT.structure_type
    288                 try dbg_info_buffer.append(@intFromEnum(AbbrevCode.struct_type));
    289                 // DW.AT.byte_size, DW.FORM.udata
    290                 try leb128.writeUleb128(dbg_info_buffer.writer(), ty.abiSize(mod));
    291 
    292                 blk: {
    293                     switch (ip.indexToKey(ty.ip_index)) {
    294                         .anon_struct_type => |fields| {
    295                             // DW.AT.name, DW.FORM.string
    296                             try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(mod)});
    297 
    298                             for (fields.types.get(ip), 0..) |field_ty, field_index| {
    299                                 // DW.AT.member
    300                                 try dbg_info_buffer.append(@intFromEnum(AbbrevCode.struct_member));
    301                                 // DW.AT.name, DW.FORM.string
    302                                 try dbg_info_buffer.writer().print("{d}\x00", .{field_index});
    303                                 // DW.AT.type, DW.FORM.ref4
    304                                 const index = dbg_info_buffer.items.len;
    305                                 try dbg_info_buffer.appendNTimes(0, 4);
    306                                 try self.addTypeRelocGlobal(atom_index, Type.fromInterned(field_ty), @intCast(index));
    307                                 // DW.AT.data_member_location, DW.FORM.udata
    308                                 const field_off = ty.structFieldOffset(field_index, mod);
    309                                 try leb128.writeUleb128(dbg_info_buffer.writer(), field_off);
    310                             }
    311                         },
    312                         .struct_type => {
    313                             const struct_type = ip.loadStructType(ty.toIntern());
    314                             // DW.AT.name, DW.FORM.string
    315                             try ty.print(dbg_info_buffer.writer(), mod);
    316                             try dbg_info_buffer.append(0);
    317 
    318                             if (struct_type.layout == .@"packed") {
    319                                 log.debug("TODO implement .debug_info for packed structs", .{});
    320                                 break :blk;
    321                             }
    322 
    323                             if (struct_type.isTuple(ip)) {
    324                                 for (struct_type.field_types.get(ip), struct_type.offsets.get(ip), 0..) |field_ty, field_off, field_index| {
    325                                     if (!Type.fromInterned(field_ty).hasRuntimeBits(mod)) continue;
    326                                     // DW.AT.member
    327                                     try dbg_info_buffer.append(@intFromEnum(AbbrevCode.struct_member));
    328                                     // DW.AT.name, DW.FORM.string
    329                                     try dbg_info_buffer.writer().print("{d}\x00", .{field_index});
    330                                     // DW.AT.type, DW.FORM.ref4
    331                                     const index = dbg_info_buffer.items.len;
    332                                     try dbg_info_buffer.appendNTimes(0, 4);
    333                                     try self.addTypeRelocGlobal(atom_index, Type.fromInterned(field_ty), @intCast(index));
    334                                     // DW.AT.data_member_location, DW.FORM.udata
    335                                     try leb128.writeUleb128(dbg_info_buffer.writer(), field_off);
    336                                 }
    337                             } else {
    338                                 for (
    339                                     struct_type.field_names.get(ip),
    340                                     struct_type.field_types.get(ip),
    341                                     struct_type.offsets.get(ip),
    342                                 ) |field_name, field_ty, field_off| {
    343                                     if (!Type.fromInterned(field_ty).hasRuntimeBits(mod)) continue;
    344                                     const field_name_slice = field_name.toSlice(ip);
    345                                     // DW.AT.member
    346                                     try dbg_info_buffer.ensureUnusedCapacity(field_name_slice.len + 2);
    347                                     dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.struct_member));
    348                                     // DW.AT.name, DW.FORM.string
    349                                     dbg_info_buffer.appendSliceAssumeCapacity(field_name_slice[0 .. field_name_slice.len + 1]);
    350                                     // DW.AT.type, DW.FORM.ref4
    351                                     const index = dbg_info_buffer.items.len;
    352                                     try dbg_info_buffer.appendNTimes(0, 4);
    353                                     try self.addTypeRelocGlobal(atom_index, Type.fromInterned(field_ty), @intCast(index));
    354                                     // DW.AT.data_member_location, DW.FORM.udata
    355                                     try leb128.writeUleb128(dbg_info_buffer.writer(), field_off);
    356                                 }
    357                             }
    358                         },
    359                         else => unreachable,
    360                     }
    361                 }
    362 
    363                 // DW.AT.structure_type delimit children
    364                 try dbg_info_buffer.append(0);
    365             },
    366             .Enum => {
    367                 // DW.AT.enumeration_type
    368                 try dbg_info_buffer.append(@intFromEnum(AbbrevCode.enum_type));
    369                 // DW.AT.byte_size, DW.FORM.udata
    370                 try leb128.writeUleb128(dbg_info_buffer.writer(), ty.abiSize(mod));
    371                 // DW.AT.name, DW.FORM.string
    372                 try ty.print(dbg_info_buffer.writer(), mod);
    373                 try dbg_info_buffer.append(0);
    374 
    375                 const enum_type = ip.loadEnumType(ty.ip_index);
    376                 for (enum_type.names.get(ip), 0..) |field_name, field_i| {
    377                     const field_name_slice = field_name.toSlice(ip);
    378                     // DW.AT.enumerator
    379                     try dbg_info_buffer.ensureUnusedCapacity(field_name_slice.len + 2 + @sizeOf(u64));
    380                     dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.enum_variant));
    381                     // DW.AT.name, DW.FORM.string
    382                     dbg_info_buffer.appendSliceAssumeCapacity(field_name_slice[0 .. field_name_slice.len + 1]);
    383                     // DW.AT.const_value, DW.FORM.data8
    384                     const value: u64 = value: {
    385                         if (enum_type.values.len == 0) break :value field_i; // auto-numbered
    386                         const value = enum_type.values.get(ip)[field_i];
    387                         // TODO do not assume a 64bit enum value - could be bigger.
    388                         // See https://github.com/ziglang/zig/issues/645
    389                         const field_int_val = try Value.fromInterned(value).intFromEnum(ty, mod);
    390                         break :value @bitCast(field_int_val.toSignedInt(mod));
    391                     };
    392                     mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), value, target_endian);
    393                 }
    394 
    395                 // DW.AT.enumeration_type delimit children
    396                 try dbg_info_buffer.append(0);
    397             },
    398             .Union => {
    399                 const union_obj = mod.typeToUnion(ty).?;
    400                 const layout = mod.getUnionLayout(union_obj);
    401                 const payload_offset = if (layout.tag_align.compare(.gte, layout.payload_align)) layout.tag_size else 0;
    402                 const tag_offset = if (layout.tag_align.compare(.gte, layout.payload_align)) 0 else layout.payload_size;
    403                 // TODO this is temporary to match current state of unions in Zig - we don't yet have
    404                 // safety checks implemented meaning the implicit tag is not yet stored and generated
    405                 // for untagged unions.
    406                 const is_tagged = layout.tag_size > 0;
    407                 if (is_tagged) {
    408                     // DW.AT.structure_type
    409                     try dbg_info_buffer.append(@intFromEnum(AbbrevCode.struct_type));
    410                     // DW.AT.byte_size, DW.FORM.udata
    411                     try leb128.writeUleb128(dbg_info_buffer.writer(), layout.abi_size);
    412                     // DW.AT.name, DW.FORM.string
    413                     try ty.print(dbg_info_buffer.writer(), mod);
    414                     try dbg_info_buffer.append(0);
    415 
    416                     // DW.AT.member
    417                     try dbg_info_buffer.ensureUnusedCapacity(13);
    418                     dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.struct_member));
    419                     // DW.AT.name, DW.FORM.string
    420                     dbg_info_buffer.appendSliceAssumeCapacity("payload");
    421                     dbg_info_buffer.appendAssumeCapacity(0);
    422                     // DW.AT.type, DW.FORM.ref4
    423                     const inner_union_index = dbg_info_buffer.items.len;
    424                     dbg_info_buffer.appendNTimesAssumeCapacity(0, 4);
    425                     try self.addTypeRelocLocal(atom_index, @intCast(inner_union_index), 5);
    426                     // DW.AT.data_member_location, DW.FORM.udata
    427                     try leb128.writeUleb128(dbg_info_buffer.writer(), payload_offset);
    428                 }
    429 
    430                 // DW.AT.union_type
    431                 try dbg_info_buffer.append(@intFromEnum(AbbrevCode.union_type));
    432                 // DW.AT.byte_size, DW.FORM.udata,
    433                 try leb128.writeUleb128(dbg_info_buffer.writer(), layout.payload_size);
    434                 // DW.AT.name, DW.FORM.string
    435                 if (is_tagged) {
    436                     try dbg_info_buffer.writer().print("AnonUnion\x00", .{});
    437                 } else {
    438                     try ty.print(dbg_info_buffer.writer(), mod);
    439                     try dbg_info_buffer.append(0);
    440                 }
    441 
    442                 for (union_obj.field_types.get(ip), union_obj.loadTagType(ip).names.get(ip)) |field_ty, field_name| {
    443                     if (!Type.fromInterned(field_ty).hasRuntimeBits(mod)) continue;
    444                     const field_name_slice = field_name.toSlice(ip);
    445                     // DW.AT.member
    446                     try dbg_info_buffer.append(@intFromEnum(AbbrevCode.struct_member));
    447                     // DW.AT.name, DW.FORM.string
    448                     try dbg_info_buffer.appendSlice(field_name_slice[0 .. field_name_slice.len + 1]);
    449                     // DW.AT.type, DW.FORM.ref4
    450                     const index = dbg_info_buffer.items.len;
    451                     try dbg_info_buffer.appendNTimes(0, 4);
    452                     try self.addTypeRelocGlobal(atom_index, Type.fromInterned(field_ty), @intCast(index));
    453                     // DW.AT.data_member_location, DW.FORM.udata
    454                     try dbg_info_buffer.append(0);
    455                 }
    456                 // DW.AT.union_type delimit children
    457                 try dbg_info_buffer.append(0);
    458 
    459                 if (is_tagged) {
    460                     // DW.AT.member
    461                     try dbg_info_buffer.ensureUnusedCapacity(9);
    462                     dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.struct_member));
    463                     // DW.AT.name, DW.FORM.string
    464                     dbg_info_buffer.appendSliceAssumeCapacity("tag");
    465                     dbg_info_buffer.appendAssumeCapacity(0);
    466                     // DW.AT.type, DW.FORM.ref4
    467                     const index = dbg_info_buffer.items.len;
    468                     dbg_info_buffer.appendNTimesAssumeCapacity(0, 4);
    469                     try self.addTypeRelocGlobal(atom_index, Type.fromInterned(union_obj.enum_tag_ty), @intCast(index));
    470                     // DW.AT.data_member_location, DW.FORM.udata
    471                     try leb128.writeUleb128(dbg_info_buffer.writer(), tag_offset);
    472 
    473                     // DW.AT.structure_type delimit children
    474                     try dbg_info_buffer.append(0);
    475                 }
    476             },
    477             .ErrorSet => try addDbgInfoErrorSet(mod, ty, target, &self.dbg_info),
    478             .ErrorUnion => {
    479                 const error_ty = ty.errorUnionSet(mod);
    480                 const payload_ty = ty.errorUnionPayload(mod);
    481                 const payload_align = if (payload_ty.isNoReturn(mod)) .none else payload_ty.abiAlignment(mod);
    482                 const error_align = Type.anyerror.abiAlignment(mod);
    483                 const abi_size = ty.abiSize(mod);
    484                 const payload_off = if (error_align.compare(.gte, payload_align)) Type.anyerror.abiSize(mod) else 0;
    485                 const error_off = if (error_align.compare(.gte, payload_align)) 0 else payload_ty.abiSize(mod);
    486 
    487                 // DW.AT.structure_type
    488                 try dbg_info_buffer.append(@intFromEnum(AbbrevCode.struct_type));
    489                 // DW.AT.byte_size, DW.FORM.udata
    490                 try leb128.writeUleb128(dbg_info_buffer.writer(), abi_size);
    491                 // DW.AT.name, DW.FORM.string
    492                 try ty.print(dbg_info_buffer.writer(), mod);
    493                 try dbg_info_buffer.append(0);
    494 
    495                 if (!payload_ty.isNoReturn(mod)) {
    496                     // DW.AT.member
    497                     try dbg_info_buffer.ensureUnusedCapacity(11);
    498                     dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.struct_member));
    499                     // DW.AT.name, DW.FORM.string
    500                     dbg_info_buffer.appendSliceAssumeCapacity("value");
    501                     dbg_info_buffer.appendAssumeCapacity(0);
    502                     // DW.AT.type, DW.FORM.ref4
    503                     const index = dbg_info_buffer.items.len;
    504                     dbg_info_buffer.appendNTimesAssumeCapacity(0, 4);
    505                     try self.addTypeRelocGlobal(atom_index, payload_ty, @intCast(index));
    506                     // DW.AT.data_member_location, DW.FORM.udata
    507                     try leb128.writeUleb128(dbg_info_buffer.writer(), payload_off);
    508                 }
    509 
    510                 {
    511                     // DW.AT.member
    512                     try dbg_info_buffer.ensureUnusedCapacity(9);
    513                     dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.struct_member));
    514                     // DW.AT.name, DW.FORM.string
    515                     dbg_info_buffer.appendSliceAssumeCapacity("err");
    516                     dbg_info_buffer.appendAssumeCapacity(0);
    517                     // DW.AT.type, DW.FORM.ref4
    518                     const index = dbg_info_buffer.items.len;
    519                     dbg_info_buffer.appendNTimesAssumeCapacity(0, 4);
    520                     try self.addTypeRelocGlobal(atom_index, error_ty, @intCast(index));
    521                     // DW.AT.data_member_location, DW.FORM.udata
    522                     try leb128.writeUleb128(dbg_info_buffer.writer(), error_off);
    523                 }
    524 
    525                 // DW.AT.structure_type delimit children
    526                 try dbg_info_buffer.append(0);
    527             },
    528             else => {
    529                 log.debug("TODO implement .debug_info for type '{}'", .{ty.fmt(self.mod)});
    530                 try dbg_info_buffer.append(@intFromEnum(AbbrevCode.zero_bit_type));
    531             },
    532         }
    533     }
    534 
    535     pub const DbgInfoLoc = union(enum) {
    536         register: u8,
    537         register_pair: [2]u8,
    538         stack: struct {
    539             fp_register: u8,
    540             offset: i32,
    541         },
    542         wasm_local: u32,
    543         memory: u64,
    544         linker_load: LinkerLoad,
    545         immediate: u64,
    546         undef,
    547         none,
    548         nop,
    549     };
    550 
    551     pub fn genArgDbgInfo(
    552         self: *DeclState,
    553         name: [:0]const u8,
    554         ty: Type,
    555         owner_decl: InternPool.DeclIndex,
    556         loc: DbgInfoLoc,
    557     ) error{OutOfMemory}!void {
    558         const dbg_info = &self.dbg_info;
    559         const atom_index = self.di_atom_decls.get(owner_decl).?;
    560         const name_with_null = name.ptr[0 .. name.len + 1];
    561 
    562         switch (loc) {
    563             .register => |reg| {
    564                 try dbg_info.ensureUnusedCapacity(4);
    565                 dbg_info.appendAssumeCapacity(@intFromEnum(AbbrevCode.parameter));
    566                 // DW.AT.location, DW.FORM.exprloc
    567                 var expr_len = std.io.countingWriter(std.io.null_writer);
    568                 if (reg < 32) {
    569                     expr_len.writer().writeByte(DW.OP.reg0 + reg) catch unreachable;
    570                 } else {
    571                     expr_len.writer().writeByte(DW.OP.regx) catch unreachable;
    572                     leb128.writeUleb128(expr_len.writer(), reg) catch unreachable;
    573                 }
    574                 leb128.writeUleb128(dbg_info.writer(), expr_len.bytes_written) catch unreachable;
    575                 if (reg < 32) {
    576                     dbg_info.appendAssumeCapacity(DW.OP.reg0 + reg);
    577                 } else {
    578                     dbg_info.appendAssumeCapacity(DW.OP.regx);
    579                     leb128.writeUleb128(dbg_info.writer(), reg) catch unreachable;
    580                 }
    581             },
    582             .register_pair => |regs| {
    583                 const reg_bits = self.mod.getTarget().ptrBitWidth();
    584                 const reg_bytes: u8 = @intCast(@divExact(reg_bits, 8));
    585                 const abi_size = ty.abiSize(self.mod);
    586                 try dbg_info.ensureUnusedCapacity(10);
    587                 dbg_info.appendAssumeCapacity(@intFromEnum(AbbrevCode.parameter));
    588                 // DW.AT.location, DW.FORM.exprloc
    589                 var expr_len = std.io.countingWriter(std.io.null_writer);
    590                 for (regs, 0..) |reg, reg_i| {
    591                     if (reg < 32) {
    592                         expr_len.writer().writeByte(DW.OP.reg0 + reg) catch unreachable;
    593                     } else {
    594                         expr_len.writer().writeByte(DW.OP.regx) catch unreachable;
    595                         leb128.writeUleb128(expr_len.writer(), reg) catch unreachable;
    596                     }
    597                     expr_len.writer().writeByte(DW.OP.piece) catch unreachable;
    598                     leb128.writeUleb128(
    599                         expr_len.writer(),
    600                         @min(abi_size - reg_i * reg_bytes, reg_bytes),
    601                     ) catch unreachable;
    602                 }
    603                 leb128.writeUleb128(dbg_info.writer(), expr_len.bytes_written) catch unreachable;
    604                 for (regs, 0..) |reg, reg_i| {
    605                     if (reg < 32) {
    606                         dbg_info.appendAssumeCapacity(DW.OP.reg0 + reg);
    607                     } else {
    608                         dbg_info.appendAssumeCapacity(DW.OP.regx);
    609                         leb128.writeUleb128(dbg_info.writer(), reg) catch unreachable;
    610                     }
    611                     dbg_info.appendAssumeCapacity(DW.OP.piece);
    612                     leb128.writeUleb128(
    613                         dbg_info.writer(),
    614                         @min(abi_size - reg_i * reg_bytes, reg_bytes),
    615                     ) catch unreachable;
    616                 }
    617             },
    618             .stack => |info| {
    619                 try dbg_info.ensureUnusedCapacity(9);
    620                 dbg_info.appendAssumeCapacity(@intFromEnum(AbbrevCode.parameter));
    621                 // DW.AT.location, DW.FORM.exprloc
    622                 var expr_len = std.io.countingWriter(std.io.null_writer);
    623                 if (info.fp_register < 32) {
    624                     expr_len.writer().writeByte(DW.OP.breg0 + info.fp_register) catch unreachable;
    625                 } else {
    626                     expr_len.writer().writeByte(DW.OP.bregx) catch unreachable;
    627                     leb128.writeUleb128(expr_len.writer(), info.fp_register) catch unreachable;
    628                 }
    629                 leb128.writeIleb128(expr_len.writer(), info.offset) catch unreachable;
    630                 leb128.writeUleb128(dbg_info.writer(), expr_len.bytes_written) catch unreachable;
    631                 if (info.fp_register < 32) {
    632                     dbg_info.appendAssumeCapacity(DW.OP.breg0 + info.fp_register);
    633                 } else {
    634                     dbg_info.appendAssumeCapacity(DW.OP.bregx);
    635                     leb128.writeUleb128(dbg_info.writer(), info.fp_register) catch unreachable;
    636                 }
    637                 leb128.writeIleb128(dbg_info.writer(), info.offset) catch unreachable;
    638             },
    639             .wasm_local => |value| {
    640                 const leb_size = link.File.Wasm.getUleb128Size(value);
    641                 try dbg_info.ensureUnusedCapacity(3 + leb_size);
    642                 // wasm locations are encoded as follow:
    643                 // DW_OP_WASM_location wasm-op
    644                 // where wasm-op is defined as
    645                 // wasm-op := wasm-local | wasm-global | wasm-operand_stack
    646                 // where each argument is encoded as
    647                 // <opcode> i:uleb128
    648                 dbg_info.appendSliceAssumeCapacity(&.{
    649                     @intFromEnum(AbbrevCode.parameter),
    650                     DW.OP.WASM_location,
    651                     DW.OP.WASM_local,
    652                 });
    653                 leb128.writeUleb128(dbg_info.writer(), value) catch unreachable;
    654             },
    655             else => unreachable,
    656         }
    657 
    658         try dbg_info.ensureUnusedCapacity(5 + name_with_null.len);
    659         const index = dbg_info.items.len;
    660         dbg_info.appendNTimesAssumeCapacity(0, 4);
    661         try self.addTypeRelocGlobal(atom_index, ty, @intCast(index)); // DW.AT.type, DW.FORM.ref4
    662         dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
    663     }
    664 
    665     pub fn genVarDbgInfo(
    666         self: *DeclState,
    667         name: [:0]const u8,
    668         ty: Type,
    669         owner_decl: InternPool.DeclIndex,
    670         is_ptr: bool,
    671         loc: DbgInfoLoc,
    672     ) error{OutOfMemory}!void {
    673         const dbg_info = &self.dbg_info;
    674         const atom_index = self.di_atom_decls.get(owner_decl).?;
    675         const name_with_null = name.ptr[0 .. name.len + 1];
    676         try dbg_info.append(@intFromEnum(AbbrevCode.variable));
    677         const gpa = self.dwarf.allocator;
    678         const mod = self.mod;
    679         const target = mod.getTarget();
    680         const endian = target.cpu.arch.endian();
    681         const child_ty = if (is_ptr) ty.childType(mod) else ty;
    682 
    683         switch (loc) {
    684             .register => |reg| {
    685                 try dbg_info.ensureUnusedCapacity(3);
    686                 // DW.AT.location, DW.FORM.exprloc
    687                 var expr_len = std.io.countingWriter(std.io.null_writer);
    688                 if (reg < 32) {
    689                     expr_len.writer().writeByte(DW.OP.reg0 + reg) catch unreachable;
    690                 } else {
    691                     expr_len.writer().writeByte(DW.OP.regx) catch unreachable;
    692                     leb128.writeUleb128(expr_len.writer(), reg) catch unreachable;
    693                 }
    694                 leb128.writeUleb128(dbg_info.writer(), expr_len.bytes_written) catch unreachable;
    695                 if (reg < 32) {
    696                     dbg_info.appendAssumeCapacity(DW.OP.reg0 + reg);
    697                 } else {
    698                     dbg_info.appendAssumeCapacity(DW.OP.regx);
    699                     leb128.writeUleb128(dbg_info.writer(), reg) catch unreachable;
    700                 }
    701             },
    702 
    703             .register_pair => |regs| {
    704                 const reg_bits = self.mod.getTarget().ptrBitWidth();
    705                 const reg_bytes: u8 = @intCast(@divExact(reg_bits, 8));
    706                 const abi_size = child_ty.abiSize(self.mod);
    707                 try dbg_info.ensureUnusedCapacity(9);
    708                 // DW.AT.location, DW.FORM.exprloc
    709                 var expr_len = std.io.countingWriter(std.io.null_writer);
    710                 for (regs, 0..) |reg, reg_i| {
    711                     if (reg < 32) {
    712                         expr_len.writer().writeByte(DW.OP.reg0 + reg) catch unreachable;
    713                     } else {
    714                         expr_len.writer().writeByte(DW.OP.regx) catch unreachable;
    715                         leb128.writeUleb128(expr_len.writer(), reg) catch unreachable;
    716                     }
    717                     expr_len.writer().writeByte(DW.OP.piece) catch unreachable;
    718                     leb128.writeUleb128(
    719                         expr_len.writer(),
    720                         @min(abi_size - reg_i * reg_bytes, reg_bytes),
    721                     ) catch unreachable;
    722                 }
    723                 leb128.writeUleb128(dbg_info.writer(), expr_len.bytes_written) catch unreachable;
    724                 for (regs, 0..) |reg, reg_i| {
    725                     if (reg < 32) {
    726                         dbg_info.appendAssumeCapacity(DW.OP.reg0 + reg);
    727                     } else {
    728                         dbg_info.appendAssumeCapacity(DW.OP.regx);
    729                         leb128.writeUleb128(dbg_info.writer(), reg) catch unreachable;
    730                     }
    731                     dbg_info.appendAssumeCapacity(DW.OP.piece);
    732                     leb128.writeUleb128(
    733                         dbg_info.writer(),
    734                         @min(abi_size - reg_i * reg_bytes, reg_bytes),
    735                     ) catch unreachable;
    736                 }
    737             },
    738 
    739             .stack => |info| {
    740                 try dbg_info.ensureUnusedCapacity(9);
    741                 // DW.AT.location, DW.FORM.exprloc
    742                 var expr_len = std.io.countingWriter(std.io.null_writer);
    743                 if (info.fp_register < 32) {
    744                     expr_len.writer().writeByte(DW.OP.breg0 + info.fp_register) catch unreachable;
    745                 } else {
    746                     expr_len.writer().writeByte(DW.OP.bregx) catch unreachable;
    747                     leb128.writeUleb128(expr_len.writer(), info.fp_register) catch unreachable;
    748                 }
    749                 leb128.writeIleb128(expr_len.writer(), info.offset) catch unreachable;
    750                 leb128.writeUleb128(dbg_info.writer(), expr_len.bytes_written) catch unreachable;
    751                 if (info.fp_register < 32) {
    752                     dbg_info.appendAssumeCapacity(DW.OP.breg0 + info.fp_register);
    753                 } else {
    754                     dbg_info.appendAssumeCapacity(DW.OP.bregx);
    755                     leb128.writeUleb128(dbg_info.writer(), info.fp_register) catch unreachable;
    756                 }
    757                 leb128.writeIleb128(dbg_info.writer(), info.offset) catch unreachable;
    758             },
    759 
    760             .wasm_local => |value| {
    761                 const leb_size = link.File.Wasm.getUleb128Size(value);
    762                 try dbg_info.ensureUnusedCapacity(2 + leb_size);
    763                 // wasm locals are encoded as follow:
    764                 // DW_OP_WASM_location wasm-op
    765                 // where wasm-op is defined as
    766                 // wasm-op := wasm-local | wasm-global | wasm-operand_stack
    767                 // where wasm-local is encoded as
    768                 // wasm-local := 0x00 i:uleb128
    769                 dbg_info.appendSliceAssumeCapacity(&.{
    770                     DW.OP.WASM_location,
    771                     DW.OP.WASM_local,
    772                 });
    773                 leb128.writeUleb128(dbg_info.writer(), value) catch unreachable;
    774             },
    775 
    776             .memory,
    777             .linker_load,
    778             => {
    779                 const ptr_width: u8 = @intCast(@divExact(target.ptrBitWidth(), 8));
    780                 try dbg_info.ensureUnusedCapacity(2 + ptr_width);
    781                 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
    782                     1 + ptr_width + @intFromBool(is_ptr),
    783                     DW.OP.addr, // literal address
    784                 });
    785                 const offset: u32 = @intCast(dbg_info.items.len);
    786                 const addr = switch (loc) {
    787                     .memory => |x| x,
    788                     else => 0,
    789                 };
    790                 switch (ptr_width) {
    791                     0...4 => {
    792                         try dbg_info.writer().writeInt(u32, @intCast(addr), endian);
    793                     },
    794                     5...8 => {
    795                         try dbg_info.writer().writeInt(u64, addr, endian);
    796                     },
    797                     else => unreachable,
    798                 }
    799                 if (is_ptr) {
    800                     // We need deref the address as we point to the value via GOT entry.
    801                     try dbg_info.append(DW.OP.deref);
    802                 }
    803                 switch (loc) {
    804                     .linker_load => |load_struct| switch (load_struct.type) {
    805                         .direct => {
    806                             log.debug("{x}: target sym %{d}", .{ offset, load_struct.sym_index });
    807                             try self.exprloc_relocs.append(gpa, .{
    808                                 .type = .direct_load,
    809                                 .target = load_struct.sym_index,
    810                                 .offset = offset,
    811                             });
    812                         },
    813                         .got => {
    814                             log.debug("{x}: target sym %{d} via GOT", .{ offset, load_struct.sym_index });
    815                             try self.exprloc_relocs.append(gpa, .{
    816                                 .type = .got_load,
    817                                 .target = load_struct.sym_index,
    818                                 .offset = offset,
    819                             });
    820                         },
    821                         else => {}, // TODO
    822                     },
    823                     else => {},
    824                 }
    825             },
    826 
    827             .immediate => |x| {
    828                 try dbg_info.ensureUnusedCapacity(2);
    829                 const fixup = dbg_info.items.len;
    830                 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
    831                     1,
    832                     if (child_ty.isSignedInt(mod)) DW.OP.consts else DW.OP.constu,
    833                 });
    834                 if (child_ty.isSignedInt(mod)) {
    835                     try leb128.writeIleb128(dbg_info.writer(), @as(i64, @bitCast(x)));
    836                 } else {
    837                     try leb128.writeUleb128(dbg_info.writer(), x);
    838                 }
    839                 try dbg_info.append(DW.OP.stack_value);
    840                 dbg_info.items[fixup] += @intCast(dbg_info.items.len - fixup - 2);
    841             },
    842 
    843             .undef => {
    844                 // DW.AT.location, DW.FORM.exprloc
    845                 // uleb128(exprloc_len)
    846                 // DW.OP.implicit_value uleb128(len_of_bytes) bytes
    847                 const abi_size: u32 = @intCast(child_ty.abiSize(mod));
    848                 var implicit_value_len = std.ArrayList(u8).init(gpa);
    849                 defer implicit_value_len.deinit();
    850                 try leb128.writeUleb128(implicit_value_len.writer(), abi_size);
    851                 const total_exprloc_len = 1 + implicit_value_len.items.len + abi_size;
    852                 try leb128.writeUleb128(dbg_info.writer(), total_exprloc_len);
    853                 try dbg_info.ensureUnusedCapacity(total_exprloc_len);
    854                 dbg_info.appendAssumeCapacity(DW.OP.implicit_value);
    855                 dbg_info.appendSliceAssumeCapacity(implicit_value_len.items);
    856                 dbg_info.appendNTimesAssumeCapacity(0xaa, abi_size);
    857             },
    858 
    859             .none => {
    860                 try dbg_info.ensureUnusedCapacity(3);
    861                 dbg_info.appendSliceAssumeCapacity(&[3]u8{ // DW.AT.location, DW.FORM.exprloc
    862                     2, DW.OP.lit0, DW.OP.stack_value,
    863                 });
    864             },
    865 
    866             .nop => {
    867                 try dbg_info.ensureUnusedCapacity(2);
    868                 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
    869                     1, DW.OP.nop,
    870                 });
    871             },
    872         }
    873 
    874         try dbg_info.ensureUnusedCapacity(5 + name_with_null.len);
    875         const index = dbg_info.items.len;
    876         dbg_info.appendNTimesAssumeCapacity(0, 4); // dw.at.type, dw.form.ref4
    877         try self.addTypeRelocGlobal(atom_index, child_ty, @intCast(index));
    878         dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
    879     }
    880 
    881     pub fn advancePCAndLine(
    882         self: *DeclState,
    883         delta_line: i33,
    884         delta_pc: u64,
    885     ) error{OutOfMemory}!void {
    886         const dbg_line = &self.dbg_line;
    887         try dbg_line.ensureUnusedCapacity(5 + 5 + 1);
    888 
    889         const header = self.dwarf.dbg_line_header;
    890         assert(header.maximum_operations_per_instruction == 1);
    891         const delta_op: u64 = 0;
    892 
    893         const remaining_delta_line: i9 = @intCast(if (delta_line < header.line_base or
    894             delta_line - header.line_base >= header.line_range)
    895         remaining: {
    896             assert(delta_line != 0);
    897             dbg_line.appendAssumeCapacity(DW.LNS.advance_line);
    898             leb128.writeIleb128(dbg_line.writer(), delta_line) catch unreachable;
    899             break :remaining 0;
    900         } else delta_line);
    901 
    902         const op_advance = @divExact(delta_pc, header.minimum_instruction_length) *
    903             header.maximum_operations_per_instruction + delta_op;
    904         const max_op_advance: u9 = (std.math.maxInt(u8) - header.opcode_base) / header.line_range;
    905         const remaining_op_advance: u8 = @intCast(if (op_advance >= 2 * max_op_advance) remaining: {
    906             dbg_line.appendAssumeCapacity(DW.LNS.advance_pc);
    907             leb128.writeUleb128(dbg_line.writer(), op_advance) catch unreachable;
    908             break :remaining 0;
    909         } else if (op_advance >= max_op_advance) remaining: {
    910             dbg_line.appendAssumeCapacity(DW.LNS.const_add_pc);
    911             break :remaining op_advance - max_op_advance;
    912         } else op_advance);
    913 
    914         if (remaining_delta_line == 0 and remaining_op_advance == 0) {
    915             dbg_line.appendAssumeCapacity(DW.LNS.copy);
    916         } else {
    917             dbg_line.appendAssumeCapacity(@intCast((remaining_delta_line - header.line_base) +
    918                 (header.line_range * remaining_op_advance) + header.opcode_base));
    919         }
    920     }
    921 
    922     pub fn setColumn(self: *DeclState, column: u32) error{OutOfMemory}!void {
    923         try self.dbg_line.ensureUnusedCapacity(1 + 5);
    924         self.dbg_line.appendAssumeCapacity(DW.LNS.set_column);
    925         leb128.writeUleb128(self.dbg_line.writer(), column + 1) catch unreachable;
    926     }
    927 
    928     pub fn setPrologueEnd(self: *DeclState) error{OutOfMemory}!void {
    929         try self.dbg_line.append(DW.LNS.set_prologue_end);
    930     }
    931 
    932     pub fn setEpilogueBegin(self: *DeclState) error{OutOfMemory}!void {
    933         try self.dbg_line.append(DW.LNS.set_epilogue_begin);
    934     }
    935 
    936     pub fn setInlineFunc(self: *DeclState, func: InternPool.Index) error{OutOfMemory}!void {
    937         if (self.dbg_line_func == func) return;
    938 
    939         try self.dbg_line.ensureUnusedCapacity((1 + 4) + (1 + 5));
    940 
    941         const old_func_info = self.mod.funcInfo(self.dbg_line_func);
    942         const new_func_info = self.mod.funcInfo(func);
    943 
    944         const old_file = try self.dwarf.addDIFile(self.mod, old_func_info.owner_decl);
    945         const new_file = try self.dwarf.addDIFile(self.mod, new_func_info.owner_decl);
    946         if (old_file != new_file) {
    947             self.dbg_line.appendAssumeCapacity(DW.LNS.set_file);
    948             leb128.writeUnsignedFixed(4, self.dbg_line.addManyAsArrayAssumeCapacity(4), new_file);
    949         }
    950 
    951         const old_src_line: i33 = self.mod.declPtr(old_func_info.owner_decl).src_line;
    952         const new_src_line: i33 = self.mod.declPtr(new_func_info.owner_decl).src_line;
    953         if (new_src_line != old_src_line) {
    954             self.dbg_line.appendAssumeCapacity(DW.LNS.advance_line);
    955             leb128.writeSignedFixed(5, self.dbg_line.addManyAsArrayAssumeCapacity(5), new_src_line - old_src_line);
    956         }
    957 
    958         self.dbg_line_func = func;
    959     }
    960 };
    961 
    962 pub const AbbrevEntry = struct {
    963     atom_index: Atom.Index,
    964     type: Type,
    965     offset: u32,
    966 };
    967 
    968 pub const AbbrevRelocation = struct {
    969     /// If target is null, we deal with a local relocation that is based on simple offset + addend
    970     /// only.
    971     target: ?u32,
    972     atom_index: Atom.Index,
    973     offset: u32,
    974     addend: u32,
    975 };
    976 
    977 pub const ExprlocRelocation = struct {
    978     /// Type of the relocation: direct load ref, or GOT load ref (via GOT table)
    979     type: enum {
    980         direct_load,
    981         got_load,
    982     },
    983     /// Index of the target in the linker's locals symbol table.
    984     target: u32,
    985     /// Offset within the debug info buffer where to patch up the address value.
    986     offset: u32,
    987 };
    988 
    989 pub const PtrWidth = enum { p32, p64 };
    990 
    991 pub const AbbrevCode = enum(u8) {
    992     null,
    993     padding,
    994     compile_unit,
    995     subprogram,
    996     subprogram_retvoid,
    997     base_type,
    998     ptr_type,
    999     struct_type,
   1000     struct_member,
   1001     enum_type,
   1002     enum_variant,
   1003     union_type,
   1004     zero_bit_type,
   1005     parameter,
   1006     variable,
   1007     array_type,
   1008     array_dim,
   1009 };
   1010 
   1011 /// The reloc offset for the virtual address of a function in its Line Number Program.
   1012 /// Size is a virtual address integer.
   1013 const dbg_line_vaddr_reloc_index = 3;
   1014 /// The reloc offset for the virtual address of a function in its .debug_info TAG.subprogram.
   1015 /// Size is a virtual address integer.
   1016 const dbg_info_low_pc_reloc_index = 1;
   1017 
   1018 const min_nop_size = 2;
   1019 
   1020 /// When allocating, the ideal_capacity is calculated by
   1021 /// actual_capacity + (actual_capacity / ideal_factor)
   1022 const ideal_factor = 3;
   1023 
   1024 pub fn init(lf: *File, format: Format) Dwarf {
   1025     const comp = lf.comp;
   1026     const gpa = comp.gpa;
   1027     const target = comp.root_mod.resolved_target.result;
   1028     const ptr_width: PtrWidth = switch (target.ptrBitWidth()) {
   1029         0...32 => .p32,
   1030         33...64 => .p64,
   1031         else => unreachable,
   1032     };
   1033     return .{
   1034         .allocator = gpa,
   1035         .bin_file = lf,
   1036         .format = format,
   1037         .ptr_width = ptr_width,
   1038         .dbg_line_header = switch (target.cpu.arch) {
   1039             .x86_64, .aarch64 => .{
   1040                 .minimum_instruction_length = 1,
   1041                 .maximum_operations_per_instruction = 1,
   1042                 .default_is_stmt = true,
   1043                 .line_base = -5,
   1044                 .line_range = 14,
   1045                 .opcode_base = DW.LNS.set_isa + 1,
   1046             },
   1047             else => .{
   1048                 .minimum_instruction_length = 1,
   1049                 .maximum_operations_per_instruction = 1,
   1050                 .default_is_stmt = true,
   1051                 .line_base = 1,
   1052                 .line_range = 1,
   1053                 .opcode_base = DW.LNS.set_isa + 1,
   1054             },
   1055         },
   1056     };
   1057 }
   1058 
   1059 pub fn deinit(self: *Dwarf) void {
   1060     const gpa = self.allocator;
   1061 
   1062     self.src_fn_free_list.deinit(gpa);
   1063     self.src_fns.deinit(gpa);
   1064     self.src_fn_decls.deinit(gpa);
   1065 
   1066     self.di_atom_free_list.deinit(gpa);
   1067     self.di_atoms.deinit(gpa);
   1068     self.di_atom_decls.deinit(gpa);
   1069 
   1070     self.strtab.deinit(gpa);
   1071     self.di_files.deinit(gpa);
   1072     self.global_abbrev_relocs.deinit(gpa);
   1073 }
   1074 
   1075 /// Initializes Decl's state and its matching output buffers.
   1076 /// Call this before `commitDeclState`.
   1077 pub fn initDeclState(self: *Dwarf, mod: *Module, decl_index: InternPool.DeclIndex) !DeclState {
   1078     const tracy = trace(@src());
   1079     defer tracy.end();
   1080 
   1081     const decl = mod.declPtr(decl_index);
   1082     const decl_linkage_name = try decl.fullyQualifiedName(mod);
   1083 
   1084     log.debug("initDeclState {}{*}", .{ decl_linkage_name.fmt(&mod.intern_pool), decl });
   1085 
   1086     const gpa = self.allocator;
   1087     var decl_state: DeclState = .{
   1088         .dwarf = self,
   1089         .mod = mod,
   1090         .di_atom_decls = &self.di_atom_decls,
   1091         .dbg_line_func = undefined,
   1092         .dbg_line = std.ArrayList(u8).init(gpa),
   1093         .dbg_info = std.ArrayList(u8).init(gpa),
   1094         .abbrev_type_arena = std.heap.ArenaAllocator.init(gpa),
   1095         .abbrev_table = .{},
   1096         .abbrev_resolver = .{},
   1097         .abbrev_relocs = .{},
   1098         .exprloc_relocs = .{},
   1099     };
   1100     errdefer decl_state.deinit();
   1101     const dbg_line_buffer = &decl_state.dbg_line;
   1102     const dbg_info_buffer = &decl_state.dbg_info;
   1103 
   1104     const di_atom_index = try self.getOrCreateAtomForDecl(.di_atom, decl_index);
   1105 
   1106     assert(decl.has_tv);
   1107 
   1108     switch (decl.typeOf(mod).zigTypeTag(mod)) {
   1109         .Fn => {
   1110             _ = try self.getOrCreateAtomForDecl(.src_fn, decl_index);
   1111 
   1112             // For functions we need to add a prologue to the debug line program.
   1113             const ptr_width_bytes = self.ptrWidthBytes();
   1114             try dbg_line_buffer.ensureTotalCapacity((3 + ptr_width_bytes) + (1 + 4) + (1 + 4) + (1 + 5) + 1);
   1115 
   1116             decl_state.dbg_line_func = decl.val.toIntern();
   1117             const func = decl.val.getFunction(mod).?;
   1118             log.debug("decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d}", .{
   1119                 decl.src_line,
   1120                 func.lbrace_line,
   1121                 func.rbrace_line,
   1122             });
   1123             const line: u28 = @intCast(decl.src_line + func.lbrace_line);
   1124 
   1125             dbg_line_buffer.appendSliceAssumeCapacity(&.{
   1126                 DW.LNS.extended_op,
   1127                 ptr_width_bytes + 1,
   1128                 DW.LNE.set_address,
   1129             });
   1130             // This is the "relocatable" vaddr, corresponding to `code_buffer` index `0`.
   1131             assert(dbg_line_vaddr_reloc_index == dbg_line_buffer.items.len);
   1132             dbg_line_buffer.appendNTimesAssumeCapacity(0, ptr_width_bytes);
   1133 
   1134             dbg_line_buffer.appendAssumeCapacity(DW.LNS.advance_line);
   1135             // This is the "relocatable" relative line offset from the previous function's end curly
   1136             // to this function's begin curly.
   1137             assert(self.getRelocDbgLineOff() == dbg_line_buffer.items.len);
   1138             // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later.
   1139             leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line);
   1140 
   1141             dbg_line_buffer.appendAssumeCapacity(DW.LNS.set_file);
   1142             assert(self.getRelocDbgFileIndex() == dbg_line_buffer.items.len);
   1143             // Once we support more than one source file, this will have the ability to be more
   1144             // than one possible value.
   1145             const file_index = try self.addDIFile(mod, decl_index);
   1146             leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), file_index);
   1147 
   1148             dbg_line_buffer.appendAssumeCapacity(DW.LNS.set_column);
   1149             leb128.writeUleb128(dbg_line_buffer.writer(), func.lbrace_column + 1) catch unreachable;
   1150 
   1151             // Emit a line for the begin curly with prologue_end=false. The codegen will
   1152             // do the work of setting prologue_end=true and epilogue_begin=true.
   1153             dbg_line_buffer.appendAssumeCapacity(DW.LNS.copy);
   1154 
   1155             // .debug_info subprogram
   1156             const decl_name_slice = decl.name.toSlice(&mod.intern_pool);
   1157             const decl_linkage_name_slice = decl_linkage_name.toSlice(&mod.intern_pool);
   1158             try dbg_info_buffer.ensureUnusedCapacity(1 + ptr_width_bytes + 4 + 4 +
   1159                 (decl_name_slice.len + 1) + (decl_linkage_name_slice.len + 1));
   1160 
   1161             const fn_ret_type = decl.typeOf(mod).fnReturnType(mod);
   1162             const fn_ret_has_bits = fn_ret_type.hasRuntimeBits(mod);
   1163             dbg_info_buffer.appendAssumeCapacity(@intFromEnum(
   1164                 @as(AbbrevCode, if (fn_ret_has_bits) .subprogram else .subprogram_retvoid),
   1165             ));
   1166             // These get overwritten after generating the machine code. These values are
   1167             // "relocations" and have to be in this fixed place so that functions can be
   1168             // moved in virtual address space.
   1169             assert(dbg_info_low_pc_reloc_index == dbg_info_buffer.items.len);
   1170             dbg_info_buffer.appendNTimesAssumeCapacity(0, ptr_width_bytes); // DW.AT.low_pc, DW.FORM.addr
   1171             assert(self.getRelocDbgInfoSubprogramHighPC() == dbg_info_buffer.items.len);
   1172             dbg_info_buffer.appendNTimesAssumeCapacity(0, 4); // DW.AT.high_pc, DW.FORM.data4
   1173             if (fn_ret_has_bits) {
   1174                 try decl_state.addTypeRelocGlobal(di_atom_index, fn_ret_type, @intCast(dbg_info_buffer.items.len));
   1175                 dbg_info_buffer.appendNTimesAssumeCapacity(0, 4); // DW.AT.type, DW.FORM.ref4
   1176             }
   1177             dbg_info_buffer.appendSliceAssumeCapacity(
   1178                 decl_name_slice[0 .. decl_name_slice.len + 1],
   1179             ); // DW.AT.name, DW.FORM.string
   1180             dbg_info_buffer.appendSliceAssumeCapacity(
   1181                 decl_linkage_name_slice[0 .. decl_linkage_name_slice.len + 1],
   1182             ); // DW.AT.linkage_name, DW.FORM.string
   1183         },
   1184         else => {
   1185             // TODO implement .debug_info for global variables
   1186         },
   1187     }
   1188 
   1189     return decl_state;
   1190 }
   1191 
   1192 pub fn commitDeclState(
   1193     self: *Dwarf,
   1194     zcu: *Module,
   1195     decl_index: InternPool.DeclIndex,
   1196     sym_addr: u64,
   1197     sym_size: u64,
   1198     decl_state: *DeclState,
   1199 ) !void {
   1200     const tracy = trace(@src());
   1201     defer tracy.end();
   1202 
   1203     const gpa = self.allocator;
   1204     const decl = zcu.declPtr(decl_index);
   1205     const ip = &zcu.intern_pool;
   1206     const namespace = zcu.namespacePtr(decl.src_namespace);
   1207     const target = namespace.file_scope.mod.resolved_target.result;
   1208     const target_endian = target.cpu.arch.endian();
   1209 
   1210     var dbg_line_buffer = &decl_state.dbg_line;
   1211     var dbg_info_buffer = &decl_state.dbg_info;
   1212 
   1213     assert(decl.has_tv);
   1214     switch (decl.typeOf(zcu).zigTypeTag(zcu)) {
   1215         .Fn => {
   1216             try decl_state.setInlineFunc(decl.val.toIntern());
   1217 
   1218             // Since the Decl is a function, we need to update the .debug_line program.
   1219             // Perform the relocations based on vaddr.
   1220             switch (self.ptr_width) {
   1221                 .p32 => {
   1222                     {
   1223                         const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..4];
   1224                         mem.writeInt(u32, ptr, @intCast(sym_addr), target_endian);
   1225                     }
   1226                     {
   1227                         const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..4];
   1228                         mem.writeInt(u32, ptr, @intCast(sym_addr), target_endian);
   1229                     }
   1230                 },
   1231                 .p64 => {
   1232                     {
   1233                         const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..8];
   1234                         mem.writeInt(u64, ptr, sym_addr, target_endian);
   1235                     }
   1236                     {
   1237                         const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..8];
   1238                         mem.writeInt(u64, ptr, sym_addr, target_endian);
   1239                     }
   1240                 },
   1241             }
   1242             {
   1243                 log.debug("relocating subprogram high PC value: {x} => {x}", .{
   1244                     self.getRelocDbgInfoSubprogramHighPC(),
   1245                     sym_size,
   1246                 });
   1247                 const ptr = dbg_info_buffer.items[self.getRelocDbgInfoSubprogramHighPC()..][0..4];
   1248                 mem.writeInt(u32, ptr, @intCast(sym_size), target_endian);
   1249             }
   1250 
   1251             try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS.extended_op, 1, DW.LNE.end_sequence });
   1252 
   1253             // Now we have the full contents and may allocate a region to store it.
   1254 
   1255             // This logic is nearly identical to the logic below in `updateDeclDebugInfo` for
   1256             // `TextBlock` and the .debug_info. If you are editing this logic, you
   1257             // probably need to edit that logic too.
   1258             const src_fn_index = self.src_fn_decls.get(decl_index).?;
   1259             const src_fn = self.getAtomPtr(.src_fn, src_fn_index);
   1260             src_fn.len = @intCast(dbg_line_buffer.items.len);
   1261 
   1262             if (self.src_fn_last_index) |last_index| blk: {
   1263                 if (src_fn_index == last_index) break :blk;
   1264                 if (src_fn.next_index) |next_index| {
   1265                     const next = self.getAtomPtr(.src_fn, next_index);
   1266                     // Update existing function - non-last item.
   1267                     if (src_fn.off + src_fn.len + min_nop_size > next.off) {
   1268                         // It grew too big, so we move it to a new location.
   1269                         if (src_fn.prev_index) |prev_index| {
   1270                             self.src_fn_free_list.put(gpa, prev_index, {}) catch {};
   1271                             self.getAtomPtr(.src_fn, prev_index).next_index = src_fn.next_index;
   1272                         }
   1273                         next.prev_index = src_fn.prev_index;
   1274                         src_fn.next_index = null;
   1275                         // Populate where it used to be with NOPs.
   1276                         switch (self.bin_file.tag) {
   1277                             .elf => {
   1278                                 const elf_file = self.bin_file.cast(File.Elf).?;
   1279                                 const debug_line_sect = &elf_file.shdrs.items[elf_file.debug_line_section_index.?];
   1280                                 const file_pos = debug_line_sect.sh_offset + src_fn.off;
   1281                                 try pwriteDbgLineNops(elf_file.base.file.?, file_pos, 0, &[0]u8{}, src_fn.len);
   1282                             },
   1283                             .macho => {
   1284                                 const macho_file = self.bin_file.cast(File.MachO).?;
   1285                                 if (macho_file.base.isRelocatable()) {
   1286                                     const debug_line_sect = &macho_file.sections.items(.header)[macho_file.debug_line_sect_index.?];
   1287                                     const file_pos = debug_line_sect.offset + src_fn.off;
   1288                                     try pwriteDbgLineNops(macho_file.base.file.?, file_pos, 0, &[0]u8{}, src_fn.len);
   1289                                 } else {
   1290                                     const d_sym = macho_file.getDebugSymbols().?;
   1291                                     const debug_line_sect = d_sym.getSectionPtr(d_sym.debug_line_section_index.?);
   1292                                     const file_pos = debug_line_sect.offset + src_fn.off;
   1293                                     try pwriteDbgLineNops(d_sym.file, file_pos, 0, &[0]u8{}, src_fn.len);
   1294                                 }
   1295                             },
   1296                             .wasm => {
   1297                                 // const wasm_file = self.bin_file.cast(File.Wasm).?;
   1298                                 // const debug_line = wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code;
   1299                                 // writeDbgLineNopsBuffered(debug_line.items, src_fn.off, 0, &.{}, src_fn.len);
   1300                             },
   1301                             else => unreachable,
   1302                         }
   1303                         // TODO Look at the free list before appending at the end.
   1304                         src_fn.prev_index = last_index;
   1305                         const last = self.getAtomPtr(.src_fn, last_index);
   1306                         last.next_index = src_fn_index;
   1307                         self.src_fn_last_index = src_fn_index;
   1308 
   1309                         src_fn.off = last.off + padToIdeal(last.len);
   1310                     }
   1311                 } else if (src_fn.prev_index == null) {
   1312                     // Append new function.
   1313                     // TODO Look at the free list before appending at the end.
   1314                     src_fn.prev_index = last_index;
   1315                     const last = self.getAtomPtr(.src_fn, last_index);
   1316                     last.next_index = src_fn_index;
   1317                     self.src_fn_last_index = src_fn_index;
   1318 
   1319                     src_fn.off = last.off + padToIdeal(last.len);
   1320                 }
   1321             } else {
   1322                 // This is the first function of the Line Number Program.
   1323                 self.src_fn_first_index = src_fn_index;
   1324                 self.src_fn_last_index = src_fn_index;
   1325 
   1326                 src_fn.off = padToIdeal(self.dbgLineNeededHeaderBytes(&[0][]u8{}, &[0][]u8{}));
   1327             }
   1328 
   1329             const last_src_fn_index = self.src_fn_last_index.?;
   1330             const last_src_fn = self.getAtom(.src_fn, last_src_fn_index);
   1331             const needed_size = last_src_fn.off + last_src_fn.len;
   1332             const prev_padding_size: u32 = if (src_fn.prev_index) |prev_index| blk: {
   1333                 const prev = self.getAtom(.src_fn, prev_index);
   1334                 break :blk src_fn.off - (prev.off + prev.len);
   1335             } else 0;
   1336             const next_padding_size: u32 = if (src_fn.next_index) |next_index| blk: {
   1337                 const next = self.getAtom(.src_fn, next_index);
   1338                 break :blk next.off - (src_fn.off + src_fn.len);
   1339             } else 0;
   1340 
   1341             // We only have support for one compilation unit so far, so the offsets are directly
   1342             // from the .debug_line section.
   1343             switch (self.bin_file.tag) {
   1344                 .elf => {
   1345                     const elf_file = self.bin_file.cast(File.Elf).?;
   1346                     const shdr_index = elf_file.debug_line_section_index.?;
   1347                     try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true);
   1348                     const debug_line_sect = elf_file.shdrs.items[shdr_index];
   1349                     const file_pos = debug_line_sect.sh_offset + src_fn.off;
   1350                     try pwriteDbgLineNops(
   1351                         elf_file.base.file.?,
   1352                         file_pos,
   1353                         prev_padding_size,
   1354                         dbg_line_buffer.items,
   1355                         next_padding_size,
   1356                     );
   1357                 },
   1358 
   1359                 .macho => {
   1360                     const macho_file = self.bin_file.cast(File.MachO).?;
   1361                     if (macho_file.base.isRelocatable()) {
   1362                         const sect_index = macho_file.debug_line_sect_index.?;
   1363                         try macho_file.growSection(sect_index, needed_size);
   1364                         const sect = macho_file.sections.items(.header)[sect_index];
   1365                         const file_pos = sect.offset + src_fn.off;
   1366                         try pwriteDbgLineNops(
   1367                             macho_file.base.file.?,
   1368                             file_pos,
   1369                             prev_padding_size,
   1370                             dbg_line_buffer.items,
   1371                             next_padding_size,
   1372                         );
   1373                     } else {
   1374                         const d_sym = macho_file.getDebugSymbols().?;
   1375                         const sect_index = d_sym.debug_line_section_index.?;
   1376                         try d_sym.growSection(sect_index, needed_size, true, macho_file);
   1377                         const sect = d_sym.getSection(sect_index);
   1378                         const file_pos = sect.offset + src_fn.off;
   1379                         try pwriteDbgLineNops(
   1380                             d_sym.file,
   1381                             file_pos,
   1382                             prev_padding_size,
   1383                             dbg_line_buffer.items,
   1384                             next_padding_size,
   1385                         );
   1386                     }
   1387                 },
   1388 
   1389                 .wasm => {
   1390                     // const wasm_file = self.bin_file.cast(File.Wasm).?;
   1391                     // const atom = wasm_file.getAtomPtr(wasm_file.debug_line_atom.?);
   1392                     // const debug_line = &atom.code;
   1393                     // const segment_size = debug_line.items.len;
   1394                     // if (needed_size != segment_size) {
   1395                     //     log.debug(" needed size does not equal allocated size: {d}", .{needed_size});
   1396                     //     if (needed_size > segment_size) {
   1397                     //         log.debug("  allocating {d} bytes for 'debug line' information", .{needed_size - segment_size});
   1398                     //         try debug_line.resize(self.allocator, needed_size);
   1399                     //         @memset(debug_line.items[segment_size..], 0);
   1400                     //     }
   1401                     //     debug_line.items.len = needed_size;
   1402                     // }
   1403                     // writeDbgLineNopsBuffered(
   1404                     //     debug_line.items,
   1405                     //     src_fn.off,
   1406                     //     prev_padding_size,
   1407                     //     dbg_line_buffer.items,
   1408                     //     next_padding_size,
   1409                     // );
   1410                 },
   1411                 else => unreachable,
   1412             }
   1413 
   1414             // .debug_info - End the TAG.subprogram children.
   1415             try dbg_info_buffer.append(0);
   1416         },
   1417         else => {},
   1418     }
   1419 
   1420     if (dbg_info_buffer.items.len == 0)
   1421         return;
   1422 
   1423     const di_atom_index = self.di_atom_decls.get(decl_index).?;
   1424     if (decl_state.abbrev_table.items.len > 0) {
   1425         // Now we emit the .debug_info types of the Decl. These will count towards the size of
   1426         // the buffer, so we have to do it before computing the offset, and we can't perform the actual
   1427         // relocations yet.
   1428         var sym_index: usize = 0;
   1429         while (sym_index < decl_state.abbrev_table.items.len) : (sym_index += 1) {
   1430             const symbol = &decl_state.abbrev_table.items[sym_index];
   1431             const ty = symbol.type;
   1432             if (ip.isErrorSetType(ty.toIntern())) continue;
   1433 
   1434             symbol.offset = @intCast(dbg_info_buffer.items.len);
   1435             try decl_state.addDbgInfoType(zcu, di_atom_index, ty);
   1436         }
   1437     }
   1438 
   1439     try self.updateDeclDebugInfoAllocation(di_atom_index, @intCast(dbg_info_buffer.items.len));
   1440 
   1441     while (decl_state.abbrev_relocs.popOrNull()) |reloc| {
   1442         if (reloc.target) |reloc_target| {
   1443             const symbol = decl_state.abbrev_table.items[reloc_target];
   1444             const ty = symbol.type;
   1445             if (ip.isErrorSetType(ty.toIntern())) {
   1446                 log.debug("resolving %{d} deferred until flush", .{reloc_target});
   1447                 try self.global_abbrev_relocs.append(gpa, .{
   1448                     .target = null,
   1449                     .offset = reloc.offset,
   1450                     .atom_index = reloc.atom_index,
   1451                     .addend = reloc.addend,
   1452                 });
   1453             } else {
   1454                 const atom = self.getAtom(.di_atom, symbol.atom_index);
   1455                 const value = atom.off + symbol.offset + reloc.addend;
   1456                 log.debug("{x}: [() => {x}] (%{d}, '{}')", .{
   1457                     reloc.offset,
   1458                     value,
   1459                     reloc_target,
   1460                     ty.fmt(zcu),
   1461                 });
   1462                 mem.writeInt(
   1463                     u32,
   1464                     dbg_info_buffer.items[reloc.offset..][0..@sizeOf(u32)],
   1465                     value,
   1466                     target_endian,
   1467                 );
   1468             }
   1469         } else {
   1470             const atom = self.getAtom(.di_atom, reloc.atom_index);
   1471             mem.writeInt(
   1472                 u32,
   1473                 dbg_info_buffer.items[reloc.offset..][0..@sizeOf(u32)],
   1474                 atom.off + reloc.offset + reloc.addend,
   1475                 target_endian,
   1476             );
   1477         }
   1478     }
   1479 
   1480     while (decl_state.exprloc_relocs.popOrNull()) |reloc| {
   1481         switch (self.bin_file.tag) {
   1482             .macho => {
   1483                 const macho_file = self.bin_file.cast(File.MachO).?;
   1484                 if (macho_file.base.isRelocatable()) {
   1485                     // TODO
   1486                 } else {
   1487                     const d_sym = macho_file.getDebugSymbols().?;
   1488                     try d_sym.relocs.append(d_sym.allocator, .{
   1489                         .type = switch (reloc.type) {
   1490                             .direct_load => .direct_load,
   1491                             .got_load => .got_load,
   1492                         },
   1493                         .target = reloc.target,
   1494                         .offset = reloc.offset + self.getAtom(.di_atom, di_atom_index).off,
   1495                         .addend = 0,
   1496                     });
   1497                 }
   1498             },
   1499             .elf => {}, // TODO
   1500             else => unreachable,
   1501         }
   1502     }
   1503 
   1504     try self.writeDeclDebugInfo(di_atom_index, dbg_info_buffer.items);
   1505 }
   1506 
   1507 fn updateDeclDebugInfoAllocation(self: *Dwarf, atom_index: Atom.Index, len: u32) !void {
   1508     const tracy = trace(@src());
   1509     defer tracy.end();
   1510 
   1511     // This logic is nearly identical to the logic above in `updateDecl` for
   1512     // `SrcFn` and the line number programs. If you are editing this logic, you
   1513     // probably need to edit that logic too.
   1514     const gpa = self.allocator;
   1515 
   1516     const atom = self.getAtomPtr(.di_atom, atom_index);
   1517     atom.len = len;
   1518     if (self.di_atom_last_index) |last_index| blk: {
   1519         if (atom_index == last_index) break :blk;
   1520         if (atom.next_index) |next_index| {
   1521             const next = self.getAtomPtr(.di_atom, next_index);
   1522             // Update existing Decl - non-last item.
   1523             if (atom.off + atom.len + min_nop_size > next.off) {
   1524                 // It grew too big, so we move it to a new location.
   1525                 if (atom.prev_index) |prev_index| {
   1526                     self.di_atom_free_list.put(gpa, prev_index, {}) catch {};
   1527                     self.getAtomPtr(.di_atom, prev_index).next_index = atom.next_index;
   1528                 }
   1529                 next.prev_index = atom.prev_index;
   1530                 atom.next_index = null;
   1531                 // Populate where it used to be with NOPs.
   1532                 switch (self.bin_file.tag) {
   1533                     .elf => {
   1534                         const elf_file = self.bin_file.cast(File.Elf).?;
   1535                         const debug_info_sect = &elf_file.shdrs.items[elf_file.debug_info_section_index.?];
   1536                         const file_pos = debug_info_sect.sh_offset + atom.off;
   1537                         try pwriteDbgInfoNops(elf_file.base.file.?, file_pos, 0, &[0]u8{}, atom.len, false);
   1538                     },
   1539                     .macho => {
   1540                         const macho_file = self.bin_file.cast(File.MachO).?;
   1541                         if (macho_file.base.isRelocatable()) {
   1542                             const debug_info_sect = macho_file.sections.items(.header)[macho_file.debug_info_sect_index.?];
   1543                             const file_pos = debug_info_sect.offset + atom.off;
   1544                             try pwriteDbgInfoNops(macho_file.base.file.?, file_pos, 0, &[0]u8{}, atom.len, false);
   1545                         } else {
   1546                             const d_sym = macho_file.getDebugSymbols().?;
   1547                             const debug_info_sect = d_sym.getSectionPtr(d_sym.debug_info_section_index.?);
   1548                             const file_pos = debug_info_sect.offset + atom.off;
   1549                             try pwriteDbgInfoNops(d_sym.file, file_pos, 0, &[0]u8{}, atom.len, false);
   1550                         }
   1551                     },
   1552                     .wasm => {
   1553                         // const wasm_file = self.bin_file.cast(File.Wasm).?;
   1554                         // const debug_info_index = wasm_file.debug_info_atom.?;
   1555                         // const debug_info = &wasm_file.getAtomPtr(debug_info_index).code;
   1556                         // try writeDbgInfoNopsToArrayList(gpa, debug_info, atom.off, 0, &.{0}, atom.len, false);
   1557                     },
   1558                     else => unreachable,
   1559                 }
   1560                 // TODO Look at the free list before appending at the end.
   1561                 atom.prev_index = last_index;
   1562                 const last = self.getAtomPtr(.di_atom, last_index);
   1563                 last.next_index = atom_index;
   1564                 self.di_atom_last_index = atom_index;
   1565 
   1566                 atom.off = last.off + padToIdeal(last.len);
   1567             }
   1568         } else if (atom.prev_index == null) {
   1569             // Append new Decl.
   1570             // TODO Look at the free list before appending at the end.
   1571             atom.prev_index = last_index;
   1572             const last = self.getAtomPtr(.di_atom, last_index);
   1573             last.next_index = atom_index;
   1574             self.di_atom_last_index = atom_index;
   1575 
   1576             atom.off = last.off + padToIdeal(last.len);
   1577         }
   1578     } else {
   1579         // This is the first Decl of the .debug_info
   1580         self.di_atom_first_index = atom_index;
   1581         self.di_atom_last_index = atom_index;
   1582 
   1583         atom.off = @intCast(padToIdeal(self.dbgInfoHeaderBytes()));
   1584     }
   1585 }
   1586 
   1587 fn writeDeclDebugInfo(self: *Dwarf, atom_index: Atom.Index, dbg_info_buf: []const u8) !void {
   1588     const tracy = trace(@src());
   1589     defer tracy.end();
   1590 
   1591     // This logic is nearly identical to the logic above in `updateDecl` for
   1592     // `SrcFn` and the line number programs. If you are editing this logic, you
   1593     // probably need to edit that logic too.
   1594 
   1595     const atom = self.getAtom(.di_atom, atom_index);
   1596     const last_decl_index = self.di_atom_last_index.?;
   1597     const last_decl = self.getAtom(.di_atom, last_decl_index);
   1598     // +1 for a trailing zero to end the children of the decl tag.
   1599     const needed_size = last_decl.off + last_decl.len + 1;
   1600     const prev_padding_size: u32 = if (atom.prev_index) |prev_index| blk: {
   1601         const prev = self.getAtom(.di_atom, prev_index);
   1602         break :blk atom.off - (prev.off + prev.len);
   1603     } else 0;
   1604     const next_padding_size: u32 = if (atom.next_index) |next_index| blk: {
   1605         const next = self.getAtom(.di_atom, next_index);
   1606         break :blk next.off - (atom.off + atom.len);
   1607     } else 0;
   1608 
   1609     // To end the children of the decl tag.
   1610     const trailing_zero = atom.next_index == null;
   1611 
   1612     // We only have support for one compilation unit so far, so the offsets are directly
   1613     // from the .debug_info section.
   1614     switch (self.bin_file.tag) {
   1615         .elf => {
   1616             const elf_file = self.bin_file.cast(File.Elf).?;
   1617             const shdr_index = elf_file.debug_info_section_index.?;
   1618             try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true);
   1619             const debug_info_sect = &elf_file.shdrs.items[shdr_index];
   1620             const file_pos = debug_info_sect.sh_offset + atom.off;
   1621             try pwriteDbgInfoNops(
   1622                 elf_file.base.file.?,
   1623                 file_pos,
   1624                 prev_padding_size,
   1625                 dbg_info_buf,
   1626                 next_padding_size,
   1627                 trailing_zero,
   1628             );
   1629         },
   1630 
   1631         .macho => {
   1632             const macho_file = self.bin_file.cast(File.MachO).?;
   1633             if (macho_file.base.isRelocatable()) {
   1634                 const sect_index = macho_file.debug_info_sect_index.?;
   1635                 try macho_file.growSection(sect_index, needed_size);
   1636                 const sect = macho_file.sections.items(.header)[sect_index];
   1637                 const file_pos = sect.offset + atom.off;
   1638                 try pwriteDbgInfoNops(
   1639                     macho_file.base.file.?,
   1640                     file_pos,
   1641                     prev_padding_size,
   1642                     dbg_info_buf,
   1643                     next_padding_size,
   1644                     trailing_zero,
   1645                 );
   1646             } else {
   1647                 const d_sym = macho_file.getDebugSymbols().?;
   1648                 const sect_index = d_sym.debug_info_section_index.?;
   1649                 try d_sym.growSection(sect_index, needed_size, true, macho_file);
   1650                 const sect = d_sym.getSection(sect_index);
   1651                 const file_pos = sect.offset + atom.off;
   1652                 try pwriteDbgInfoNops(
   1653                     d_sym.file,
   1654                     file_pos,
   1655                     prev_padding_size,
   1656                     dbg_info_buf,
   1657                     next_padding_size,
   1658                     trailing_zero,
   1659                 );
   1660             }
   1661         },
   1662 
   1663         .wasm => {
   1664             // const wasm_file = self.bin_file.cast(File.Wasm).?;
   1665             // const info_atom = wasm_file.debug_info_atom.?;
   1666             // const debug_info = &wasm_file.getAtomPtr(info_atom).code;
   1667             // const segment_size = debug_info.items.len;
   1668             // if (needed_size != segment_size) {
   1669             //     log.debug(" needed size does not equal allocated size: {d}", .{needed_size});
   1670             //     if (needed_size > segment_size) {
   1671             //         log.debug("  allocating {d} bytes for 'debug info' information", .{needed_size - segment_size});
   1672             //         try debug_info.resize(self.allocator, needed_size);
   1673             //         @memset(debug_info.items[segment_size..], 0);
   1674             //     }
   1675             //     debug_info.items.len = needed_size;
   1676             // }
   1677             // log.debug(" writeDbgInfoNopsToArrayList debug_info_len={d} offset={d} content_len={d} next_padding_size={d}", .{
   1678             //     debug_info.items.len, atom.off, dbg_info_buf.len, next_padding_size,
   1679             // });
   1680             // try writeDbgInfoNopsToArrayList(
   1681             //     gpa,
   1682             //     debug_info,
   1683             //     atom.off,
   1684             //     prev_padding_size,
   1685             //     dbg_info_buf,
   1686             //     next_padding_size,
   1687             //     trailing_zero,
   1688             // );
   1689         },
   1690         else => unreachable,
   1691     }
   1692 }
   1693 
   1694 pub fn updateDeclLineNumber(self: *Dwarf, mod: *Module, decl_index: InternPool.DeclIndex) !void {
   1695     const tracy = trace(@src());
   1696     defer tracy.end();
   1697 
   1698     const atom_index = try self.getOrCreateAtomForDecl(.src_fn, decl_index);
   1699     const atom = self.getAtom(.src_fn, atom_index);
   1700     if (atom.len == 0) return;
   1701 
   1702     const decl = mod.declPtr(decl_index);
   1703     const func = decl.val.getFunction(mod).?;
   1704     log.debug("decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d}", .{
   1705         decl.src_line,
   1706         func.lbrace_line,
   1707         func.rbrace_line,
   1708     });
   1709     const line: u28 = @intCast(decl.src_line + func.lbrace_line);
   1710     var data: [4]u8 = undefined;
   1711     leb128.writeUnsignedFixed(4, &data, line);
   1712 
   1713     switch (self.bin_file.tag) {
   1714         .elf => {
   1715             const elf_file = self.bin_file.cast(File.Elf).?;
   1716             const shdr = elf_file.shdrs.items[elf_file.debug_line_section_index.?];
   1717             const file_pos = shdr.sh_offset + atom.off + self.getRelocDbgLineOff();
   1718             try elf_file.base.file.?.pwriteAll(&data, file_pos);
   1719         },
   1720         .macho => {
   1721             const macho_file = self.bin_file.cast(File.MachO).?;
   1722             if (macho_file.base.isRelocatable()) {
   1723                 const sect = macho_file.sections.items(.header)[macho_file.debug_line_sect_index.?];
   1724                 const file_pos = sect.offset + atom.off + self.getRelocDbgLineOff();
   1725                 try macho_file.base.file.?.pwriteAll(&data, file_pos);
   1726             } else {
   1727                 const d_sym = macho_file.getDebugSymbols().?;
   1728                 const sect = d_sym.getSection(d_sym.debug_line_section_index.?);
   1729                 const file_pos = sect.offset + atom.off + self.getRelocDbgLineOff();
   1730                 try d_sym.file.pwriteAll(&data, file_pos);
   1731             }
   1732         },
   1733         .wasm => {
   1734             // const wasm_file = self.bin_file.cast(File.Wasm).?;
   1735             // const offset = atom.off + self.getRelocDbgLineOff();
   1736             // const line_atom_index = wasm_file.debug_line_atom.?;
   1737             // wasm_file.getAtomPtr(line_atom_index).code.items[offset..][0..data.len].* = data;
   1738         },
   1739         else => unreachable,
   1740     }
   1741 }
   1742 
   1743 pub fn freeDecl(self: *Dwarf, decl_index: InternPool.DeclIndex) void {
   1744     const gpa = self.allocator;
   1745 
   1746     // Free SrcFn atom
   1747     if (self.src_fn_decls.fetchRemove(decl_index)) |kv| {
   1748         const src_fn_index = kv.value;
   1749         const src_fn = self.getAtom(.src_fn, src_fn_index);
   1750         _ = self.src_fn_free_list.remove(src_fn_index);
   1751 
   1752         if (src_fn.prev_index) |prev_index| {
   1753             self.src_fn_free_list.put(gpa, prev_index, {}) catch {};
   1754             const prev = self.getAtomPtr(.src_fn, prev_index);
   1755             prev.next_index = src_fn.next_index;
   1756             if (src_fn.next_index) |next_index| {
   1757                 self.getAtomPtr(.src_fn, next_index).prev_index = prev_index;
   1758             } else {
   1759                 self.src_fn_last_index = prev_index;
   1760             }
   1761         } else if (src_fn.next_index) |next_index| {
   1762             self.src_fn_first_index = next_index;
   1763             self.getAtomPtr(.src_fn, next_index).prev_index = null;
   1764         }
   1765         if (self.src_fn_first_index == src_fn_index) {
   1766             self.src_fn_first_index = src_fn.next_index;
   1767         }
   1768         if (self.src_fn_last_index == src_fn_index) {
   1769             self.src_fn_last_index = src_fn.prev_index;
   1770         }
   1771     }
   1772 
   1773     // Free DI atom
   1774     if (self.di_atom_decls.fetchRemove(decl_index)) |kv| {
   1775         const di_atom_index = kv.value;
   1776         const di_atom = self.getAtomPtr(.di_atom, di_atom_index);
   1777 
   1778         if (self.di_atom_first_index == di_atom_index) {
   1779             self.di_atom_first_index = di_atom.next_index;
   1780         }
   1781         if (self.di_atom_last_index == di_atom_index) {
   1782             // TODO shrink the .debug_info section size here
   1783             self.di_atom_last_index = di_atom.prev_index;
   1784         }
   1785 
   1786         if (di_atom.prev_index) |prev_index| {
   1787             self.getAtomPtr(.di_atom, prev_index).next_index = di_atom.next_index;
   1788             // TODO the free list logic like we do for SrcFn above
   1789         } else {
   1790             di_atom.prev_index = null;
   1791         }
   1792 
   1793         if (di_atom.next_index) |next_index| {
   1794             self.getAtomPtr(.di_atom, next_index).prev_index = di_atom.prev_index;
   1795         } else {
   1796             di_atom.next_index = null;
   1797         }
   1798     }
   1799 }
   1800 
   1801 pub fn writeDbgAbbrev(self: *Dwarf) !void {
   1802     // These are LEB encoded but since the values are all less than 127
   1803     // we can simply append these bytes.
   1804     // zig fmt: off
   1805     const abbrev_buf = [_]u8{
   1806         @intFromEnum(AbbrevCode.padding),
   1807         @as(u8, 0x80) | @as(u7, @truncate(DW.TAG.ZIG_padding >> 0)),
   1808         @as(u8, 0x80) | @as(u7, @truncate(DW.TAG.ZIG_padding >> 7)),
   1809         @as(u8, 0x00) | @as(u7, @intCast(DW.TAG.ZIG_padding >> 14)),
   1810         DW.CHILDREN.no,
   1811         0, 0,
   1812 
   1813         @intFromEnum(AbbrevCode.compile_unit),
   1814         DW.TAG.compile_unit,
   1815         DW.CHILDREN.yes,
   1816         DW.AT.stmt_list, DW.FORM.sec_offset,
   1817         DW.AT.low_pc,    DW.FORM.addr,
   1818         DW.AT.high_pc,   DW.FORM.addr,
   1819         DW.AT.name,      DW.FORM.strp,
   1820         DW.AT.comp_dir,  DW.FORM.strp,
   1821         DW.AT.producer,  DW.FORM.strp,
   1822         DW.AT.language,  DW.FORM.data2,
   1823         0,               0,
   1824 
   1825         @intFromEnum(AbbrevCode.subprogram),
   1826         DW.TAG.subprogram,
   1827         DW.CHILDREN.yes,
   1828         DW.AT.low_pc,       DW.FORM.addr,
   1829         DW.AT.high_pc,      DW.FORM.data4,
   1830         DW.AT.type,         DW.FORM.ref4,
   1831         DW.AT.name,         DW.FORM.string,
   1832         DW.AT.linkage_name, DW.FORM.string,
   1833         0,                  0,
   1834 
   1835         @intFromEnum(AbbrevCode.subprogram_retvoid),
   1836         DW.TAG.subprogram,
   1837         DW.CHILDREN.yes,
   1838         DW.AT.low_pc,       DW.FORM.addr,
   1839         DW.AT.high_pc,      DW.FORM.data4,
   1840         DW.AT.name,         DW.FORM.string,
   1841         DW.AT.linkage_name, DW.FORM.string,
   1842         0,                  0,
   1843 
   1844         @intFromEnum(AbbrevCode.base_type),
   1845         DW.TAG.base_type, DW.CHILDREN.no,
   1846         DW.AT.encoding,   DW.FORM.data1,
   1847         DW.AT.byte_size,  DW.FORM.udata,
   1848         DW.AT.name,       DW.FORM.string,
   1849         0,                0,
   1850 
   1851         @intFromEnum(AbbrevCode.ptr_type),
   1852         DW.TAG.pointer_type, DW.CHILDREN.no,
   1853         DW.AT.type,          DW.FORM.ref4,
   1854         0,                   0,
   1855 
   1856         @intFromEnum(AbbrevCode.struct_type),
   1857         DW.TAG.structure_type, DW.CHILDREN.yes,
   1858         DW.AT.byte_size,       DW.FORM.udata,
   1859         DW.AT.name,            DW.FORM.string,
   1860         0,                     0,
   1861 
   1862         @intFromEnum(AbbrevCode.struct_member),
   1863         DW.TAG.member,
   1864         DW.CHILDREN.no,
   1865         DW.AT.name,                 DW.FORM.string,
   1866         DW.AT.type,                 DW.FORM.ref4,
   1867         DW.AT.data_member_location, DW.FORM.udata,
   1868         0,                          0,
   1869 
   1870         @intFromEnum(AbbrevCode.enum_type),
   1871         DW.TAG.enumeration_type,
   1872         DW.CHILDREN.yes,
   1873         DW.AT.byte_size, DW.FORM.udata,
   1874         DW.AT.name,      DW.FORM.string,
   1875         0,               0,
   1876 
   1877         @intFromEnum(AbbrevCode.enum_variant),
   1878         DW.TAG.enumerator, DW.CHILDREN.no,
   1879         DW.AT.name,        DW.FORM.string,
   1880         DW.AT.const_value, DW.FORM.data8,
   1881         0,                 0,
   1882 
   1883         @intFromEnum(AbbrevCode.union_type),
   1884         DW.TAG.union_type, DW.CHILDREN.yes,
   1885         DW.AT.byte_size,   DW.FORM.udata,
   1886         DW.AT.name,        DW.FORM.string,
   1887         0,                 0,
   1888 
   1889         @intFromEnum(AbbrevCode.zero_bit_type),
   1890         DW.TAG.unspecified_type,
   1891         DW.CHILDREN.no,
   1892         0, 0,
   1893 
   1894         @intFromEnum(AbbrevCode.parameter),
   1895         DW.TAG.formal_parameter,
   1896         DW.CHILDREN.no,
   1897         DW.AT.location, DW.FORM.exprloc,
   1898         DW.AT.type,     DW.FORM.ref4,
   1899         DW.AT.name,     DW.FORM.string,
   1900         0,              0,
   1901 
   1902         @intFromEnum(AbbrevCode.variable),
   1903         DW.TAG.variable,
   1904         DW.CHILDREN.no,
   1905         DW.AT.location, DW.FORM.exprloc,
   1906         DW.AT.type,     DW.FORM.ref4,
   1907         DW.AT.name,     DW.FORM.string,
   1908         0,              0,
   1909 
   1910         @intFromEnum(AbbrevCode.array_type),
   1911         DW.TAG.array_type,
   1912         DW.CHILDREN.yes,
   1913         DW.AT.name, DW.FORM.string,
   1914         DW.AT.type, DW.FORM.ref4,
   1915         0,          0,
   1916 
   1917         @intFromEnum(AbbrevCode.array_dim),
   1918         DW.TAG.subrange_type,
   1919         DW.CHILDREN.no,
   1920         DW.AT.type,  DW.FORM.ref4,
   1921         DW.AT.count, DW.FORM.udata,
   1922         0,           0,
   1923 
   1924         0,
   1925     };
   1926     // zig fmt: on
   1927     const abbrev_offset = 0;
   1928     self.abbrev_table_offset = abbrev_offset;
   1929 
   1930     const needed_size = abbrev_buf.len;
   1931     switch (self.bin_file.tag) {
   1932         .elf => {
   1933             const elf_file = self.bin_file.cast(File.Elf).?;
   1934             const shdr_index = elf_file.debug_abbrev_section_index.?;
   1935             try elf_file.growNonAllocSection(shdr_index, needed_size, 1, false);
   1936             const debug_abbrev_sect = &elf_file.shdrs.items[shdr_index];
   1937             const file_pos = debug_abbrev_sect.sh_offset + abbrev_offset;
   1938             try elf_file.base.file.?.pwriteAll(&abbrev_buf, file_pos);
   1939         },
   1940         .macho => {
   1941             const macho_file = self.bin_file.cast(File.MachO).?;
   1942             if (macho_file.base.isRelocatable()) {
   1943                 const sect_index = macho_file.debug_abbrev_sect_index.?;
   1944                 try macho_file.growSection(sect_index, needed_size);
   1945                 const sect = macho_file.sections.items(.header)[sect_index];
   1946                 const file_pos = sect.offset + abbrev_offset;
   1947                 try macho_file.base.file.?.pwriteAll(&abbrev_buf, file_pos);
   1948             } else {
   1949                 const d_sym = macho_file.getDebugSymbols().?;
   1950                 const sect_index = d_sym.debug_abbrev_section_index.?;
   1951                 try d_sym.growSection(sect_index, needed_size, false, macho_file);
   1952                 const sect = d_sym.getSection(sect_index);
   1953                 const file_pos = sect.offset + abbrev_offset;
   1954                 try d_sym.file.pwriteAll(&abbrev_buf, file_pos);
   1955             }
   1956         },
   1957         .wasm => {
   1958             // const wasm_file = self.bin_file.cast(File.Wasm).?;
   1959             // const debug_abbrev = &wasm_file.getAtomPtr(wasm_file.debug_abbrev_atom.?).code;
   1960             // try debug_abbrev.resize(gpa, needed_size);
   1961             // debug_abbrev.items[0..abbrev_buf.len].* = abbrev_buf;
   1962         },
   1963         else => unreachable,
   1964     }
   1965 }
   1966 
   1967 fn dbgInfoHeaderBytes(self: *Dwarf) usize {
   1968     _ = self;
   1969     return 120;
   1970 }
   1971 
   1972 pub fn writeDbgInfoHeader(self: *Dwarf, zcu: *Module, low_pc: u64, high_pc: u64) !void {
   1973     // If this value is null it means there is an error in the module;
   1974     // leave debug_info_header_dirty=true.
   1975     const first_dbg_info_off = self.getDebugInfoOff() orelse return;
   1976 
   1977     // We have a function to compute the upper bound size, because it's needed
   1978     // for determining where to put the offset of the first `LinkBlock`.
   1979     const needed_bytes = self.dbgInfoHeaderBytes();
   1980     var di_buf = try std.ArrayList(u8).initCapacity(self.allocator, needed_bytes);
   1981     defer di_buf.deinit();
   1982 
   1983     const comp = self.bin_file.comp;
   1984     const target = comp.root_mod.resolved_target.result;
   1985     const target_endian = target.cpu.arch.endian();
   1986     const init_len_size: usize = switch (self.format) {
   1987         .dwarf32 => 4,
   1988         .dwarf64 => 12,
   1989     };
   1990 
   1991     // initial length - length of the .debug_info contribution for this compilation unit,
   1992     // not including the initial length itself.
   1993     // We have to come back and write it later after we know the size.
   1994     const after_init_len = di_buf.items.len + init_len_size;
   1995     const dbg_info_end = self.getDebugInfoEnd().?;
   1996     const init_len = dbg_info_end - after_init_len + 1;
   1997 
   1998     if (self.format == .dwarf64) di_buf.appendNTimesAssumeCapacity(0xff, 4);
   1999     self.writeOffsetAssumeCapacity(&di_buf, init_len);
   2000 
   2001     mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // DWARF version
   2002     const abbrev_offset = self.abbrev_table_offset.?;
   2003 
   2004     self.writeOffsetAssumeCapacity(&di_buf, abbrev_offset);
   2005     di_buf.appendAssumeCapacity(self.ptrWidthBytes()); // address size
   2006 
   2007     // Write the form for the compile unit, which must match the abbrev table above.
   2008     const name_strp = try self.strtab.insert(self.allocator, zcu.root_mod.root_src_path);
   2009     var compile_unit_dir_buffer: [std.fs.max_path_bytes]u8 = undefined;
   2010     const compile_unit_dir = resolveCompilationDir(zcu, &compile_unit_dir_buffer);
   2011     const comp_dir_strp = try self.strtab.insert(self.allocator, compile_unit_dir);
   2012     const producer_strp = try self.strtab.insert(self.allocator, link.producer_string);
   2013 
   2014     di_buf.appendAssumeCapacity(@intFromEnum(AbbrevCode.compile_unit));
   2015     self.writeOffsetAssumeCapacity(&di_buf, 0); // DW.AT.stmt_list, DW.FORM.sec_offset
   2016     self.writeAddrAssumeCapacity(&di_buf, low_pc);
   2017     self.writeAddrAssumeCapacity(&di_buf, high_pc);
   2018     self.writeOffsetAssumeCapacity(&di_buf, name_strp);
   2019     self.writeOffsetAssumeCapacity(&di_buf, comp_dir_strp);
   2020     self.writeOffsetAssumeCapacity(&di_buf, producer_strp);
   2021 
   2022     // We are still waiting on dwarf-std.org to assign DW_LANG_Zig a number:
   2023     // http://dwarfstd.org/ShowIssue.php?issue=171115.1
   2024     // Until then we say it is C99.
   2025     mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG.C99, target_endian);
   2026 
   2027     if (di_buf.items.len > first_dbg_info_off) {
   2028         // Move the first N decls to the end to make more padding for the header.
   2029         @panic("TODO: handle .debug_info header exceeding its padding");
   2030     }
   2031     const jmp_amt = first_dbg_info_off - di_buf.items.len;
   2032     switch (self.bin_file.tag) {
   2033         .elf => {
   2034             const elf_file = self.bin_file.cast(File.Elf).?;
   2035             const debug_info_sect = &elf_file.shdrs.items[elf_file.debug_info_section_index.?];
   2036             const file_pos = debug_info_sect.sh_offset;
   2037             try pwriteDbgInfoNops(elf_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt, false);
   2038         },
   2039         .macho => {
   2040             const macho_file = self.bin_file.cast(File.MachO).?;
   2041             if (macho_file.base.isRelocatable()) {
   2042                 const debug_info_sect = macho_file.sections.items(.header)[macho_file.debug_info_sect_index.?];
   2043                 const file_pos = debug_info_sect.offset;
   2044                 try pwriteDbgInfoNops(macho_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt, false);
   2045             } else {
   2046                 const d_sym = macho_file.getDebugSymbols().?;
   2047                 const debug_info_sect = d_sym.getSection(d_sym.debug_info_section_index.?);
   2048                 const file_pos = debug_info_sect.offset;
   2049                 try pwriteDbgInfoNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt, false);
   2050             }
   2051         },
   2052         .wasm => {
   2053             // const wasm_file = self.bin_file.cast(File.Wasm).?;
   2054             // const debug_info = &wasm_file.getAtomPtr(wasm_file.debug_info_atom.?).code;
   2055             // try writeDbgInfoNopsToArrayList(self.allocator, debug_info, 0, 0, di_buf.items, jmp_amt, false);
   2056         },
   2057         else => unreachable,
   2058     }
   2059 }
   2060 
   2061 fn resolveCompilationDir(module: *Module, buffer: *[std.fs.max_path_bytes]u8) []const u8 {
   2062     // We fully resolve all paths at this point to avoid lack of source line info in stack
   2063     // traces or lack of debugging information which, if relative paths were used, would
   2064     // be very location dependent.
   2065     // TODO: the only concern I have with this is WASI as either host or target, should
   2066     // we leave the paths as relative then?
   2067     const root_dir_path = module.root_mod.root.root_dir.path orelse ".";
   2068     const sub_path = module.root_mod.root.sub_path;
   2069     const realpath = if (std.fs.path.isAbsolute(root_dir_path)) r: {
   2070         @memcpy(buffer[0..root_dir_path.len], root_dir_path);
   2071         break :r root_dir_path;
   2072     } else std.fs.realpath(root_dir_path, buffer) catch return root_dir_path;
   2073     const len = realpath.len + 1 + sub_path.len;
   2074     if (buffer.len < len) return root_dir_path;
   2075     buffer[realpath.len] = '/';
   2076     @memcpy(buffer[realpath.len + 1 ..][0..sub_path.len], sub_path);
   2077     return buffer[0..len];
   2078 }
   2079 
   2080 fn writeAddrAssumeCapacity(self: *Dwarf, buf: *std.ArrayList(u8), addr: u64) void {
   2081     const comp = self.bin_file.comp;
   2082     const target = comp.root_mod.resolved_target.result;
   2083     const target_endian = target.cpu.arch.endian();
   2084     switch (self.ptr_width) {
   2085         .p32 => mem.writeInt(u32, buf.addManyAsArrayAssumeCapacity(4), @intCast(addr), target_endian),
   2086         .p64 => mem.writeInt(u64, buf.addManyAsArrayAssumeCapacity(8), addr, target_endian),
   2087     }
   2088 }
   2089 
   2090 fn writeOffsetAssumeCapacity(self: *Dwarf, buf: *std.ArrayList(u8), off: u64) void {
   2091     const comp = self.bin_file.comp;
   2092     const target = comp.root_mod.resolved_target.result;
   2093     const target_endian = target.cpu.arch.endian();
   2094     switch (self.format) {
   2095         .dwarf32 => mem.writeInt(u32, buf.addManyAsArrayAssumeCapacity(4), @intCast(off), target_endian),
   2096         .dwarf64 => mem.writeInt(u64, buf.addManyAsArrayAssumeCapacity(8), off, target_endian),
   2097     }
   2098 }
   2099 
   2100 /// Writes to the file a buffer, prefixed and suffixed by the specified number of
   2101 /// bytes of NOPs. Asserts each padding size is at least `min_nop_size` and total padding bytes
   2102 /// are less than 1044480 bytes (if this limit is ever reached, this function can be
   2103 /// improved to make more than one pwritev call, or the limit can be raised by a fixed
   2104 /// amount by increasing the length of `vecs`).
   2105 fn pwriteDbgLineNops(
   2106     file: fs.File,
   2107     offset: u64,
   2108     prev_padding_size: usize,
   2109     buf: []const u8,
   2110     next_padding_size: usize,
   2111 ) !void {
   2112     const tracy = trace(@src());
   2113     defer tracy.end();
   2114 
   2115     const page_of_nops = [1]u8{DW.LNS.negate_stmt} ** 4096;
   2116     const three_byte_nop = [3]u8{ DW.LNS.advance_pc, 0b1000_0000, 0 };
   2117     var vecs: [512]std.posix.iovec_const = undefined;
   2118     var vec_index: usize = 0;
   2119     {
   2120         var padding_left = prev_padding_size;
   2121         if (padding_left % 2 != 0) {
   2122             vecs[vec_index] = .{
   2123                 .base = &three_byte_nop,
   2124                 .len = three_byte_nop.len,
   2125             };
   2126             vec_index += 1;
   2127             padding_left -= three_byte_nop.len;
   2128         }
   2129         while (padding_left > page_of_nops.len) {
   2130             vecs[vec_index] = .{
   2131                 .base = &page_of_nops,
   2132                 .len = page_of_nops.len,
   2133             };
   2134             vec_index += 1;
   2135             padding_left -= page_of_nops.len;
   2136         }
   2137         if (padding_left > 0) {
   2138             vecs[vec_index] = .{
   2139                 .base = &page_of_nops,
   2140                 .len = padding_left,
   2141             };
   2142             vec_index += 1;
   2143         }
   2144     }
   2145 
   2146     vecs[vec_index] = .{
   2147         .base = buf.ptr,
   2148         .len = buf.len,
   2149     };
   2150     if (buf.len > 0) vec_index += 1;
   2151 
   2152     {
   2153         var padding_left = next_padding_size;
   2154         if (padding_left % 2 != 0) {
   2155             vecs[vec_index] = .{
   2156                 .base = &three_byte_nop,
   2157                 .len = three_byte_nop.len,
   2158             };
   2159             vec_index += 1;
   2160             padding_left -= three_byte_nop.len;
   2161         }
   2162         while (padding_left > page_of_nops.len) {
   2163             vecs[vec_index] = .{
   2164                 .base = &page_of_nops,
   2165                 .len = page_of_nops.len,
   2166             };
   2167             vec_index += 1;
   2168             padding_left -= page_of_nops.len;
   2169         }
   2170         if (padding_left > 0) {
   2171             vecs[vec_index] = .{
   2172                 .base = &page_of_nops,
   2173                 .len = padding_left,
   2174             };
   2175             vec_index += 1;
   2176         }
   2177     }
   2178     try file.pwritevAll(vecs[0..vec_index], offset - prev_padding_size);
   2179 }
   2180 
   2181 fn writeDbgLineNopsBuffered(
   2182     buf: []u8,
   2183     offset: u32,
   2184     prev_padding_size: usize,
   2185     content: []const u8,
   2186     next_padding_size: usize,
   2187 ) void {
   2188     assert(buf.len >= content.len + prev_padding_size + next_padding_size);
   2189     const tracy = trace(@src());
   2190     defer tracy.end();
   2191 
   2192     const three_byte_nop = [3]u8{ DW.LNS.advance_pc, 0b1000_0000, 0 };
   2193     {
   2194         var padding_left = prev_padding_size;
   2195         if (padding_left % 2 != 0) {
   2196             buf[offset - padding_left ..][0..3].* = three_byte_nop;
   2197             padding_left -= 3;
   2198         }
   2199 
   2200         while (padding_left > 0) : (padding_left -= 1) {
   2201             buf[offset - padding_left] = DW.LNS.negate_stmt;
   2202         }
   2203     }
   2204 
   2205     @memcpy(buf[offset..][0..content.len], content);
   2206 
   2207     {
   2208         var padding_left = next_padding_size;
   2209         if (padding_left % 2 != 0) {
   2210             buf[offset + content.len + padding_left ..][0..3].* = three_byte_nop;
   2211             padding_left -= 3;
   2212         }
   2213 
   2214         while (padding_left > 0) : (padding_left -= 1) {
   2215             buf[offset + content.len + padding_left] = DW.LNS.negate_stmt;
   2216         }
   2217     }
   2218 }
   2219 
   2220 /// Writes to the file a buffer, prefixed and suffixed by the specified number of
   2221 /// bytes of padding.
   2222 fn pwriteDbgInfoNops(
   2223     file: fs.File,
   2224     offset: u64,
   2225     prev_padding_size: usize,
   2226     buf: []const u8,
   2227     next_padding_size: usize,
   2228     trailing_zero: bool,
   2229 ) !void {
   2230     const tracy = trace(@src());
   2231     defer tracy.end();
   2232 
   2233     const page_of_nops = [1]u8{@intFromEnum(AbbrevCode.padding)} ** 4096;
   2234     var vecs: [32]std.posix.iovec_const = undefined;
   2235     var vec_index: usize = 0;
   2236     {
   2237         var padding_left = prev_padding_size;
   2238         while (padding_left > page_of_nops.len) {
   2239             vecs[vec_index] = .{
   2240                 .base = &page_of_nops,
   2241                 .len = page_of_nops.len,
   2242             };
   2243             vec_index += 1;
   2244             padding_left -= page_of_nops.len;
   2245         }
   2246         if (padding_left > 0) {
   2247             vecs[vec_index] = .{
   2248                 .base = &page_of_nops,
   2249                 .len = padding_left,
   2250             };
   2251             vec_index += 1;
   2252         }
   2253     }
   2254 
   2255     vecs[vec_index] = .{
   2256         .base = buf.ptr,
   2257         .len = buf.len,
   2258     };
   2259     if (buf.len > 0) vec_index += 1;
   2260 
   2261     {
   2262         var padding_left = next_padding_size;
   2263         while (padding_left > page_of_nops.len) {
   2264             vecs[vec_index] = .{
   2265                 .base = &page_of_nops,
   2266                 .len = page_of_nops.len,
   2267             };
   2268             vec_index += 1;
   2269             padding_left -= page_of_nops.len;
   2270         }
   2271         if (padding_left > 0) {
   2272             vecs[vec_index] = .{
   2273                 .base = &page_of_nops,
   2274                 .len = padding_left,
   2275             };
   2276             vec_index += 1;
   2277         }
   2278     }
   2279 
   2280     if (trailing_zero) {
   2281         var zbuf = [1]u8{0};
   2282         vecs[vec_index] = .{
   2283             .base = &zbuf,
   2284             .len = zbuf.len,
   2285         };
   2286         vec_index += 1;
   2287     }
   2288 
   2289     try file.pwritevAll(vecs[0..vec_index], offset - prev_padding_size);
   2290 }
   2291 
   2292 fn writeDbgInfoNopsToArrayList(
   2293     gpa: Allocator,
   2294     buffer: *std.ArrayListUnmanaged(u8),
   2295     offset: u32,
   2296     prev_padding_size: usize,
   2297     content: []const u8,
   2298     next_padding_size: usize,
   2299     trailing_zero: bool,
   2300 ) Allocator.Error!void {
   2301     try buffer.resize(gpa, @max(
   2302         buffer.items.len,
   2303         offset + content.len + next_padding_size + 1,
   2304     ));
   2305     @memset(buffer.items[offset - prev_padding_size .. offset], @intFromEnum(AbbrevCode.padding));
   2306     @memcpy(buffer.items[offset..][0..content.len], content);
   2307     @memset(buffer.items[offset + content.len ..][0..next_padding_size], @intFromEnum(AbbrevCode.padding));
   2308 
   2309     if (trailing_zero) {
   2310         buffer.items[offset + content.len + next_padding_size] = 0;
   2311     }
   2312 }
   2313 
   2314 pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void {
   2315     const comp = self.bin_file.comp;
   2316     const target = comp.root_mod.resolved_target.result;
   2317     const target_endian = target.cpu.arch.endian();
   2318     const ptr_width_bytes = self.ptrWidthBytes();
   2319 
   2320     // Enough for all the data without resizing. When support for more compilation units
   2321     // is added, the size of this section will become more variable.
   2322     var di_buf = try std.ArrayList(u8).initCapacity(self.allocator, 100);
   2323     defer di_buf.deinit();
   2324 
   2325     // initial length - length of the .debug_aranges contribution for this compilation unit,
   2326     // not including the initial length itself.
   2327     // We have to come back and write it later after we know the size.
   2328     if (self.format == .dwarf64) di_buf.appendNTimesAssumeCapacity(0xff, 4);
   2329     const init_len_index = di_buf.items.len;
   2330     self.writeOffsetAssumeCapacity(&di_buf, 0);
   2331     const after_init_len = di_buf.items.len;
   2332     mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 2, target_endian); // version
   2333 
   2334     // When more than one compilation unit is supported, this will be the offset to it.
   2335     // For now it is always at offset 0 in .debug_info.
   2336     self.writeOffsetAssumeCapacity(&di_buf, 0); // .debug_info offset
   2337     di_buf.appendAssumeCapacity(ptr_width_bytes); // address_size
   2338     di_buf.appendAssumeCapacity(0); // segment_selector_size
   2339 
   2340     const end_header_offset = di_buf.items.len;
   2341     const begin_entries_offset = mem.alignForward(usize, end_header_offset, ptr_width_bytes * 2);
   2342     di_buf.appendNTimesAssumeCapacity(0, begin_entries_offset - end_header_offset);
   2343 
   2344     // Currently only one compilation unit is supported, so the address range is simply
   2345     // identical to the main program header virtual address and memory size.
   2346     self.writeAddrAssumeCapacity(&di_buf, addr);
   2347     self.writeAddrAssumeCapacity(&di_buf, size);
   2348 
   2349     // Sentinel.
   2350     self.writeAddrAssumeCapacity(&di_buf, 0);
   2351     self.writeAddrAssumeCapacity(&di_buf, 0);
   2352 
   2353     // Go back and populate the initial length.
   2354     const init_len = di_buf.items.len - after_init_len;
   2355     switch (self.format) {
   2356         .dwarf32 => mem.writeInt(u32, di_buf.items[init_len_index..][0..4], @intCast(init_len), target_endian),
   2357         .dwarf64 => mem.writeInt(u64, di_buf.items[init_len_index..][0..8], init_len, target_endian),
   2358     }
   2359 
   2360     const needed_size: u32 = @intCast(di_buf.items.len);
   2361     switch (self.bin_file.tag) {
   2362         .elf => {
   2363             const elf_file = self.bin_file.cast(File.Elf).?;
   2364             const shdr_index = elf_file.debug_aranges_section_index.?;
   2365             try elf_file.growNonAllocSection(shdr_index, needed_size, 16, false);
   2366             const debug_aranges_sect = &elf_file.shdrs.items[shdr_index];
   2367             const file_pos = debug_aranges_sect.sh_offset;
   2368             try elf_file.base.file.?.pwriteAll(di_buf.items, file_pos);
   2369         },
   2370         .macho => {
   2371             const macho_file = self.bin_file.cast(File.MachO).?;
   2372             if (macho_file.base.isRelocatable()) {
   2373                 const sect_index = macho_file.debug_aranges_sect_index.?;
   2374                 try macho_file.growSection(sect_index, needed_size);
   2375                 const sect = macho_file.sections.items(.header)[sect_index];
   2376                 const file_pos = sect.offset;
   2377                 try macho_file.base.file.?.pwriteAll(di_buf.items, file_pos);
   2378             } else {
   2379                 const d_sym = macho_file.getDebugSymbols().?;
   2380                 const sect_index = d_sym.debug_aranges_section_index.?;
   2381                 try d_sym.growSection(sect_index, needed_size, false, macho_file);
   2382                 const sect = d_sym.getSection(sect_index);
   2383                 const file_pos = sect.offset;
   2384                 try d_sym.file.pwriteAll(di_buf.items, file_pos);
   2385             }
   2386         },
   2387         .wasm => {
   2388             // const wasm_file = self.bin_file.cast(File.Wasm).?;
   2389             // const debug_ranges = &wasm_file.getAtomPtr(wasm_file.debug_ranges_atom.?).code;
   2390             // try debug_ranges.resize(gpa, needed_size);
   2391             // @memcpy(debug_ranges.items[0..di_buf.items.len], di_buf.items);
   2392         },
   2393         else => unreachable,
   2394     }
   2395 }
   2396 
   2397 pub fn writeDbgLineHeader(self: *Dwarf) !void {
   2398     const comp = self.bin_file.comp;
   2399     const gpa = self.allocator;
   2400     const target = comp.root_mod.resolved_target.result;
   2401     const target_endian = target.cpu.arch.endian();
   2402     const init_len_size: usize = switch (self.format) {
   2403         .dwarf32 => 4,
   2404         .dwarf64 => 12,
   2405     };
   2406 
   2407     const dbg_line_prg_off = self.getDebugLineProgramOff() orelse return;
   2408     assert(self.getDebugLineProgramEnd().? != 0);
   2409 
   2410     // Convert all input DI files into a set of include dirs and file names.
   2411     var arena = std.heap.ArenaAllocator.init(gpa);
   2412     defer arena.deinit();
   2413     const paths = try self.genIncludeDirsAndFileNames(arena.allocator());
   2414 
   2415     // The size of this header is variable, depending on the number of directories,
   2416     // files, and padding. We have a function to compute the upper bound size, however,
   2417     // because it's needed for determining where to put the offset of the first `SrcFn`.
   2418     const needed_bytes = self.dbgLineNeededHeaderBytes(paths.dirs, paths.files);
   2419     var di_buf = try std.ArrayList(u8).initCapacity(gpa, needed_bytes);
   2420     defer di_buf.deinit();
   2421 
   2422     if (self.format == .dwarf64) di_buf.appendNTimesAssumeCapacity(0xff, 4);
   2423     self.writeOffsetAssumeCapacity(&di_buf, 0);
   2424 
   2425     mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // version
   2426 
   2427     // Empirically, debug info consumers do not respect this field, or otherwise
   2428     // consider it to be an error when it does not point exactly to the end of the header.
   2429     // Therefore we rely on the NOP jump at the beginning of the Line Number Program for
   2430     // padding rather than this field.
   2431     const before_header_len = di_buf.items.len;
   2432     self.writeOffsetAssumeCapacity(&di_buf, 0); // We will come back and write this.
   2433     const after_header_len = di_buf.items.len;
   2434 
   2435     assert(self.dbg_line_header.opcode_base == DW.LNS.set_isa + 1);
   2436     di_buf.appendSliceAssumeCapacity(&[_]u8{
   2437         self.dbg_line_header.minimum_instruction_length,
   2438         self.dbg_line_header.maximum_operations_per_instruction,
   2439         @intFromBool(self.dbg_line_header.default_is_stmt),
   2440         @bitCast(self.dbg_line_header.line_base),
   2441         self.dbg_line_header.line_range,
   2442         self.dbg_line_header.opcode_base,
   2443 
   2444         // Standard opcode lengths. The number of items here is based on `opcode_base`.
   2445         // The value is the number of LEB128 operands the instruction takes.
   2446         0, // `DW.LNS.copy`
   2447         1, // `DW.LNS.advance_pc`
   2448         1, // `DW.LNS.advance_line`
   2449         1, // `DW.LNS.set_file`
   2450         1, // `DW.LNS.set_column`
   2451         0, // `DW.LNS.negate_stmt`
   2452         0, // `DW.LNS.set_basic_block`
   2453         0, // `DW.LNS.const_add_pc`
   2454         1, // `DW.LNS.fixed_advance_pc`
   2455         0, // `DW.LNS.set_prologue_end`
   2456         0, // `DW.LNS.set_epilogue_begin`
   2457         1, // `DW.LNS.set_isa`
   2458     });
   2459 
   2460     for (paths.dirs, 0..) |dir, i| {
   2461         log.debug("adding new include dir at {d} of '{s}'", .{ i + 1, dir });
   2462         di_buf.appendSliceAssumeCapacity(dir);
   2463         di_buf.appendAssumeCapacity(0);
   2464     }
   2465     di_buf.appendAssumeCapacity(0); // include directories sentinel
   2466 
   2467     for (paths.files, 0..) |file, i| {
   2468         const dir_index = paths.files_dirs_indexes[i];
   2469         log.debug("adding new file name at {d} of '{s}' referencing directory {d}", .{
   2470             i + 1,
   2471             file,
   2472             dir_index + 1,
   2473         });
   2474         di_buf.appendSliceAssumeCapacity(file);
   2475         di_buf.appendSliceAssumeCapacity(&[_]u8{
   2476             0, // null byte for the relative path name
   2477             @intCast(dir_index), // directory_index
   2478             0, // mtime (TODO supply this)
   2479             0, // file size bytes (TODO supply this)
   2480         });
   2481     }
   2482     di_buf.appendAssumeCapacity(0); // file names sentinel
   2483 
   2484     const header_len = di_buf.items.len - after_header_len;
   2485     switch (self.format) {
   2486         .dwarf32 => mem.writeInt(u32, di_buf.items[before_header_len..][0..4], @intCast(header_len), target_endian),
   2487         .dwarf64 => mem.writeInt(u64, di_buf.items[before_header_len..][0..8], header_len, target_endian),
   2488     }
   2489 
   2490     assert(needed_bytes == di_buf.items.len);
   2491 
   2492     if (di_buf.items.len > dbg_line_prg_off) {
   2493         const needed_with_padding = padToIdeal(needed_bytes);
   2494         const delta = needed_with_padding - dbg_line_prg_off;
   2495 
   2496         const first_fn_index = self.src_fn_first_index.?;
   2497         const first_fn = self.getAtom(.src_fn, first_fn_index);
   2498         const last_fn_index = self.src_fn_last_index.?;
   2499         const last_fn = self.getAtom(.src_fn, last_fn_index);
   2500 
   2501         var src_fn_index = first_fn_index;
   2502 
   2503         var buffer = try gpa.alloc(u8, last_fn.off + last_fn.len - first_fn.off);
   2504         defer gpa.free(buffer);
   2505 
   2506         switch (self.bin_file.tag) {
   2507             .elf => {
   2508                 const elf_file = self.bin_file.cast(File.Elf).?;
   2509                 const shdr_index = elf_file.debug_line_section_index.?;
   2510                 const needed_size = elf_file.shdrs.items[shdr_index].sh_size + delta;
   2511                 try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true);
   2512                 const file_pos = elf_file.shdrs.items[shdr_index].sh_offset + first_fn.off;
   2513 
   2514                 const amt = try elf_file.base.file.?.preadAll(buffer, file_pos);
   2515                 if (amt != buffer.len) return error.InputOutput;
   2516 
   2517                 try elf_file.base.file.?.pwriteAll(buffer, file_pos + delta);
   2518             },
   2519             .macho => {
   2520                 const macho_file = self.bin_file.cast(File.MachO).?;
   2521                 if (macho_file.base.isRelocatable()) {
   2522                     const sect_index = macho_file.debug_line_sect_index.?;
   2523                     const needed_size: u32 = @intCast(macho_file.sections.items(.header)[sect_index].size + delta);
   2524                     try macho_file.growSection(sect_index, needed_size);
   2525                     const file_pos = macho_file.sections.items(.header)[sect_index].offset + first_fn.off;
   2526 
   2527                     const amt = try macho_file.base.file.?.preadAll(buffer, file_pos);
   2528                     if (amt != buffer.len) return error.InputOutput;
   2529 
   2530                     try macho_file.base.file.?.pwriteAll(buffer, file_pos + delta);
   2531                 } else {
   2532                     const d_sym = macho_file.getDebugSymbols().?;
   2533                     const sect_index = d_sym.debug_line_section_index.?;
   2534                     const needed_size: u32 = @intCast(d_sym.getSection(sect_index).size + delta);
   2535                     try d_sym.growSection(sect_index, needed_size, true, macho_file);
   2536                     const file_pos = d_sym.getSection(sect_index).offset + first_fn.off;
   2537 
   2538                     const amt = try d_sym.file.preadAll(buffer, file_pos);
   2539                     if (amt != buffer.len) return error.InputOutput;
   2540 
   2541                     try d_sym.file.pwriteAll(buffer, file_pos + delta);
   2542                 }
   2543             },
   2544             .wasm => {
   2545                 _ = &buffer;
   2546                 // const wasm_file = self.bin_file.cast(File.Wasm).?;
   2547                 // const debug_line = &wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code;
   2548                 // {
   2549                 //     const src = debug_line.items[first_fn.off..];
   2550                 //     @memcpy(buffer[0..src.len], src);
   2551                 // }
   2552                 // try debug_line.resize(self.allocator, debug_line.items.len + delta);
   2553                 // @memcpy(debug_line.items[first_fn.off + delta ..][0..buffer.len], buffer);
   2554             },
   2555             else => unreachable,
   2556         }
   2557 
   2558         while (true) {
   2559             const src_fn = self.getAtomPtr(.src_fn, src_fn_index);
   2560             src_fn.off += delta;
   2561 
   2562             if (src_fn.next_index) |next_index| {
   2563                 src_fn_index = next_index;
   2564             } else break;
   2565         }
   2566     }
   2567 
   2568     // Backpatch actual length of the debug line program
   2569     const init_len = self.getDebugLineProgramEnd().? - init_len_size;
   2570     switch (self.format) {
   2571         .dwarf32 => {
   2572             mem.writeInt(u32, di_buf.items[0..4], @intCast(init_len), target_endian);
   2573         },
   2574         .dwarf64 => {
   2575             mem.writeInt(u64, di_buf.items[4..][0..8], init_len, target_endian);
   2576         },
   2577     }
   2578 
   2579     // We use NOPs because consumers empirically do not respect the header length field.
   2580     const jmp_amt = self.getDebugLineProgramOff().? - di_buf.items.len;
   2581     switch (self.bin_file.tag) {
   2582         .elf => {
   2583             const elf_file = self.bin_file.cast(File.Elf).?;
   2584             const debug_line_sect = &elf_file.shdrs.items[elf_file.debug_line_section_index.?];
   2585             const file_pos = debug_line_sect.sh_offset;
   2586             try pwriteDbgLineNops(elf_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt);
   2587         },
   2588         .macho => {
   2589             const macho_file = self.bin_file.cast(File.MachO).?;
   2590             if (macho_file.base.isRelocatable()) {
   2591                 const debug_line_sect = macho_file.sections.items(.header)[macho_file.debug_line_sect_index.?];
   2592                 const file_pos = debug_line_sect.offset;
   2593                 try pwriteDbgLineNops(macho_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt);
   2594             } else {
   2595                 const d_sym = macho_file.getDebugSymbols().?;
   2596                 const debug_line_sect = d_sym.getSection(d_sym.debug_line_section_index.?);
   2597                 const file_pos = debug_line_sect.offset;
   2598                 try pwriteDbgLineNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt);
   2599             }
   2600         },
   2601         .wasm => {
   2602             // const wasm_file = self.bin_file.cast(File.Wasm).?;
   2603             // const debug_line = &wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code;
   2604             // writeDbgLineNopsBuffered(debug_line.items, 0, 0, di_buf.items, jmp_amt);
   2605         },
   2606         else => unreachable,
   2607     }
   2608 }
   2609 
   2610 fn getDebugInfoOff(self: Dwarf) ?u32 {
   2611     const first_index = self.di_atom_first_index orelse return null;
   2612     const first = self.getAtom(.di_atom, first_index);
   2613     return first.off;
   2614 }
   2615 
   2616 fn getDebugInfoEnd(self: Dwarf) ?u32 {
   2617     const last_index = self.di_atom_last_index orelse return null;
   2618     const last = self.getAtom(.di_atom, last_index);
   2619     return last.off + last.len;
   2620 }
   2621 
   2622 fn getDebugLineProgramOff(self: Dwarf) ?u32 {
   2623     const first_index = self.src_fn_first_index orelse return null;
   2624     const first = self.getAtom(.src_fn, first_index);
   2625     return first.off;
   2626 }
   2627 
   2628 fn getDebugLineProgramEnd(self: Dwarf) ?u32 {
   2629     const last_index = self.src_fn_last_index orelse return null;
   2630     const last = self.getAtom(.src_fn, last_index);
   2631     return last.off + last.len;
   2632 }
   2633 
   2634 /// Always 4 or 8 depending on whether this is 32-bit or 64-bit format.
   2635 fn ptrWidthBytes(self: Dwarf) u8 {
   2636     return switch (self.ptr_width) {
   2637         .p32 => 4,
   2638         .p64 => 8,
   2639     };
   2640 }
   2641 
   2642 fn dbgLineNeededHeaderBytes(self: Dwarf, dirs: []const []const u8, files: []const []const u8) u32 {
   2643     var size: usize = switch (self.format) { // length field
   2644         .dwarf32 => 4,
   2645         .dwarf64 => 12,
   2646     };
   2647     size += @sizeOf(u16); // version field
   2648     size += switch (self.format) { // offset to end-of-header
   2649         .dwarf32 => 4,
   2650         .dwarf64 => 8,
   2651     };
   2652     size += 18; // opcodes
   2653 
   2654     for (dirs) |dir| { // include dirs
   2655         size += dir.len + 1;
   2656     }
   2657     size += 1; // include dirs sentinel
   2658 
   2659     for (files) |file| { // file names
   2660         size += file.len + 1 + 1 + 1 + 1;
   2661     }
   2662     size += 1; // file names sentinel
   2663 
   2664     return @intCast(size);
   2665 }
   2666 
   2667 /// The reloc offset for the line offset of a function from the previous function's line.
   2668 /// It's a fixed-size 4-byte ULEB128.
   2669 fn getRelocDbgLineOff(self: Dwarf) usize {
   2670     return dbg_line_vaddr_reloc_index + self.ptrWidthBytes() + 1;
   2671 }
   2672 
   2673 fn getRelocDbgFileIndex(self: Dwarf) usize {
   2674     return self.getRelocDbgLineOff() + 5;
   2675 }
   2676 
   2677 fn getRelocDbgInfoSubprogramHighPC(self: Dwarf) u32 {
   2678     return dbg_info_low_pc_reloc_index + self.ptrWidthBytes();
   2679 }
   2680 
   2681 fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
   2682     return actual_size +| (actual_size / ideal_factor);
   2683 }
   2684 
   2685 pub fn flushModule(self: *Dwarf, module: *Module) !void {
   2686     const comp = self.bin_file.comp;
   2687     const target = comp.root_mod.resolved_target.result;
   2688 
   2689     if (self.global_abbrev_relocs.items.len > 0) {
   2690         const gpa = self.allocator;
   2691         var arena_alloc = std.heap.ArenaAllocator.init(gpa);
   2692         defer arena_alloc.deinit();
   2693         const arena = arena_alloc.allocator();
   2694 
   2695         var dbg_info_buffer = std.ArrayList(u8).init(arena);
   2696         try addDbgInfoErrorSetNames(
   2697             module,
   2698             Type.anyerror,
   2699             module.global_error_set.keys(),
   2700             target,
   2701             &dbg_info_buffer,
   2702         );
   2703 
   2704         const di_atom_index = try self.createAtom(.di_atom);
   2705         log.debug("updateDeclDebugInfoAllocation in flushModule", .{});
   2706         try self.updateDeclDebugInfoAllocation(di_atom_index, @intCast(dbg_info_buffer.items.len));
   2707         log.debug("writeDeclDebugInfo in flushModule", .{});
   2708         try self.writeDeclDebugInfo(di_atom_index, dbg_info_buffer.items);
   2709 
   2710         const file_pos = switch (self.bin_file.tag) {
   2711             .elf => pos: {
   2712                 const elf_file = self.bin_file.cast(File.Elf).?;
   2713                 const debug_info_sect = &elf_file.shdrs.items[elf_file.debug_info_section_index.?];
   2714                 break :pos debug_info_sect.sh_offset;
   2715             },
   2716             .macho => pos: {
   2717                 const macho_file = self.bin_file.cast(File.MachO).?;
   2718                 if (macho_file.base.isRelocatable()) {
   2719                     const debug_info_sect = &macho_file.sections.items(.header)[macho_file.debug_info_sect_index.?];
   2720                     break :pos debug_info_sect.offset;
   2721                 } else {
   2722                     const d_sym = macho_file.getDebugSymbols().?;
   2723                     const debug_info_sect = d_sym.getSectionPtr(d_sym.debug_info_section_index.?);
   2724                     break :pos debug_info_sect.offset;
   2725                 }
   2726             },
   2727             // for wasm, the offset is always 0 as we write to memory first
   2728             .wasm => 0,
   2729             else => unreachable,
   2730         };
   2731 
   2732         var buf: [@sizeOf(u32)]u8 = undefined;
   2733         mem.writeInt(u32, &buf, self.getAtom(.di_atom, di_atom_index).off, target.cpu.arch.endian());
   2734 
   2735         while (self.global_abbrev_relocs.popOrNull()) |reloc| {
   2736             const atom = self.getAtom(.di_atom, reloc.atom_index);
   2737             switch (self.bin_file.tag) {
   2738                 .elf => {
   2739                     const elf_file = self.bin_file.cast(File.Elf).?;
   2740                     try elf_file.base.file.?.pwriteAll(&buf, file_pos + atom.off + reloc.offset);
   2741                 },
   2742                 .macho => {
   2743                     const macho_file = self.bin_file.cast(File.MachO).?;
   2744                     if (macho_file.base.isRelocatable()) {
   2745                         try macho_file.base.file.?.pwriteAll(&buf, file_pos + atom.off + reloc.offset);
   2746                     } else {
   2747                         const d_sym = macho_file.getDebugSymbols().?;
   2748                         try d_sym.file.pwriteAll(&buf, file_pos + atom.off + reloc.offset);
   2749                     }
   2750                 },
   2751                 .wasm => {
   2752                     // const wasm_file = self.bin_file.cast(File.Wasm).?;
   2753                     // const debug_info = wasm_file.getAtomPtr(wasm_file.debug_info_atom.?).code;
   2754                     // debug_info.items[atom.off + reloc.offset ..][0..buf.len].* = buf;
   2755                 },
   2756                 else => unreachable,
   2757             }
   2758         }
   2759     }
   2760 }
   2761 
   2762 fn addDIFile(self: *Dwarf, mod: *Module, decl_index: InternPool.DeclIndex) !u28 {
   2763     const decl = mod.declPtr(decl_index);
   2764     const file_scope = decl.getFileScope(mod);
   2765     const gop = try self.di_files.getOrPut(self.allocator, file_scope);
   2766     if (!gop.found_existing) {
   2767         switch (self.bin_file.tag) {
   2768             .elf => {
   2769                 const elf_file = self.bin_file.cast(File.Elf).?;
   2770                 elf_file.markDirty(elf_file.debug_line_section_index.?);
   2771             },
   2772             .macho => {
   2773                 const macho_file = self.bin_file.cast(File.MachO).?;
   2774                 if (macho_file.base.isRelocatable()) {
   2775                     macho_file.markDirty(macho_file.debug_line_sect_index.?);
   2776                 } else {
   2777                     const d_sym = macho_file.getDebugSymbols().?;
   2778                     d_sym.markDirty(d_sym.debug_line_section_index.?, macho_file);
   2779                 }
   2780             },
   2781             .wasm => {},
   2782             else => unreachable,
   2783         }
   2784     }
   2785     return @intCast(gop.index + 1);
   2786 }
   2787 
   2788 fn genIncludeDirsAndFileNames(self: *Dwarf, arena: Allocator) !struct {
   2789     dirs: []const []const u8,
   2790     files: []const []const u8,
   2791     files_dirs_indexes: []u28,
   2792 } {
   2793     var dirs = std.StringArrayHashMap(void).init(arena);
   2794     try dirs.ensureTotalCapacity(self.di_files.count());
   2795 
   2796     var files = std.ArrayList([]const u8).init(arena);
   2797     try files.ensureTotalCapacityPrecise(self.di_files.count());
   2798 
   2799     var files_dir_indexes = std.ArrayList(u28).init(arena);
   2800     try files_dir_indexes.ensureTotalCapacity(self.di_files.count());
   2801 
   2802     for (self.di_files.keys()) |dif| {
   2803         const full_path = try dif.mod.root.joinString(arena, dif.sub_file_path);
   2804         const dir_path = std.fs.path.dirname(full_path) orelse ".";
   2805         const sub_file_path = std.fs.path.basename(full_path);
   2806         // https://github.com/ziglang/zig/issues/19353
   2807         var buffer: [std.fs.max_path_bytes]u8 = undefined;
   2808         const resolved = if (!std.fs.path.isAbsolute(dir_path))
   2809             std.posix.realpath(dir_path, &buffer) catch dir_path
   2810         else
   2811             dir_path;
   2812 
   2813         const dir_index: u28 = index: {
   2814             const dirs_gop = dirs.getOrPutAssumeCapacity(try arena.dupe(u8, resolved));
   2815             break :index @intCast(dirs_gop.index + 1);
   2816         };
   2817 
   2818         files_dir_indexes.appendAssumeCapacity(dir_index);
   2819         files.appendAssumeCapacity(sub_file_path);
   2820     }
   2821 
   2822     return .{
   2823         .dirs = dirs.keys(),
   2824         .files = files.items,
   2825         .files_dirs_indexes = files_dir_indexes.items,
   2826     };
   2827 }
   2828 
   2829 fn addDbgInfoErrorSet(
   2830     mod: *Module,
   2831     ty: Type,
   2832     target: std.Target,
   2833     dbg_info_buffer: *std.ArrayList(u8),
   2834 ) !void {
   2835     return addDbgInfoErrorSetNames(mod, ty, ty.errorSetNames(mod).get(&mod.intern_pool), target, dbg_info_buffer);
   2836 }
   2837 
   2838 fn addDbgInfoErrorSetNames(
   2839     mod: *Module,
   2840     /// Used for printing the type name only.
   2841     ty: Type,
   2842     error_names: []const InternPool.NullTerminatedString,
   2843     target: std.Target,
   2844     dbg_info_buffer: *std.ArrayList(u8),
   2845 ) !void {
   2846     const target_endian = target.cpu.arch.endian();
   2847 
   2848     // DW.AT.enumeration_type
   2849     try dbg_info_buffer.append(@intFromEnum(AbbrevCode.enum_type));
   2850     // DW.AT.byte_size, DW.FORM.udata
   2851     const abi_size = Type.anyerror.abiSize(mod);
   2852     try leb128.writeUleb128(dbg_info_buffer.writer(), abi_size);
   2853     // DW.AT.name, DW.FORM.string
   2854     try ty.print(dbg_info_buffer.writer(), mod);
   2855     try dbg_info_buffer.append(0);
   2856 
   2857     // DW.AT.enumerator
   2858     const no_error = "(no error)";
   2859     try dbg_info_buffer.ensureUnusedCapacity(no_error.len + 2 + @sizeOf(u64));
   2860     dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.enum_variant));
   2861     // DW.AT.name, DW.FORM.string
   2862     dbg_info_buffer.appendSliceAssumeCapacity(no_error);
   2863     dbg_info_buffer.appendAssumeCapacity(0);
   2864     // DW.AT.const_value, DW.FORM.data8
   2865     mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), 0, target_endian);
   2866 
   2867     for (error_names) |error_name| {
   2868         const int = try mod.getErrorValue(error_name);
   2869         const error_name_slice = error_name.toSlice(&mod.intern_pool);
   2870         // DW.AT.enumerator
   2871         try dbg_info_buffer.ensureUnusedCapacity(error_name_slice.len + 2 + @sizeOf(u64));
   2872         dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.enum_variant));
   2873         // DW.AT.name, DW.FORM.string
   2874         dbg_info_buffer.appendSliceAssumeCapacity(error_name_slice[0 .. error_name_slice.len + 1]);
   2875         // DW.AT.const_value, DW.FORM.data8
   2876         mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), int, target_endian);
   2877     }
   2878 
   2879     // DW.AT.enumeration_type delimit children
   2880     try dbg_info_buffer.append(0);
   2881 }
   2882 
   2883 const Kind = enum { src_fn, di_atom };
   2884 
   2885 fn createAtom(self: *Dwarf, comptime kind: Kind) !Atom.Index {
   2886     const index = blk: {
   2887         switch (kind) {
   2888             .src_fn => {
   2889                 const index: Atom.Index = @intCast(self.src_fns.items.len);
   2890                 _ = try self.src_fns.addOne(self.allocator);
   2891                 break :blk index;
   2892             },
   2893             .di_atom => {
   2894                 const index: Atom.Index = @intCast(self.di_atoms.items.len);
   2895                 _ = try self.di_atoms.addOne(self.allocator);
   2896                 break :blk index;
   2897             },
   2898         }
   2899     };
   2900     const atom = self.getAtomPtr(kind, index);
   2901     atom.* = .{
   2902         .off = 0,
   2903         .len = 0,
   2904         .prev_index = null,
   2905         .next_index = null,
   2906     };
   2907     return index;
   2908 }
   2909 
   2910 fn getOrCreateAtomForDecl(self: *Dwarf, comptime kind: Kind, decl_index: InternPool.DeclIndex) !Atom.Index {
   2911     switch (kind) {
   2912         .src_fn => {
   2913             const gop = try self.src_fn_decls.getOrPut(self.allocator, decl_index);
   2914             if (!gop.found_existing) {
   2915                 gop.value_ptr.* = try self.createAtom(kind);
   2916             }
   2917             return gop.value_ptr.*;
   2918         },
   2919         .di_atom => {
   2920             const gop = try self.di_atom_decls.getOrPut(self.allocator, decl_index);
   2921             if (!gop.found_existing) {
   2922                 gop.value_ptr.* = try self.createAtom(kind);
   2923             }
   2924             return gop.value_ptr.*;
   2925         },
   2926     }
   2927 }
   2928 
   2929 fn getAtom(self: *const Dwarf, comptime kind: Kind, index: Atom.Index) Atom {
   2930     return switch (kind) {
   2931         .src_fn => self.src_fns.items[index],
   2932         .di_atom => self.di_atoms.items[index],
   2933     };
   2934 }
   2935 
   2936 fn getAtomPtr(self: *Dwarf, comptime kind: Kind, index: Atom.Index) *Atom {
   2937     return switch (kind) {
   2938         .src_fn => &self.src_fns.items[index],
   2939         .di_atom => &self.di_atoms.items[index],
   2940     };
   2941 }
   2942 
   2943 pub const Format = enum {
   2944     dwarf32,
   2945     dwarf64,
   2946 };
   2947 
   2948 const Dwarf = @This();
   2949 
   2950 const std = @import("std");
   2951 const builtin = @import("builtin");
   2952 const assert = std.debug.assert;
   2953 const fs = std.fs;
   2954 const leb128 = std.leb;
   2955 const log = std.log.scoped(.dwarf);
   2956 const mem = std.mem;
   2957 
   2958 const link = @import("../link.zig");
   2959 const trace = @import("../tracy.zig").trace;
   2960 
   2961 const Allocator = mem.Allocator;
   2962 const DW = std.dwarf;
   2963 const File = link.File;
   2964 const LinkBlock = File.LinkBlock;
   2965 const LinkFn = File.LinkFn;
   2966 const LinkerLoad = @import("../codegen.zig").LinkerLoad;
   2967 const Zcu = @import("../Zcu.zig");
   2968 /// Deprecated.
   2969 const Module = Zcu;
   2970 const InternPool = @import("../InternPool.zig");
   2971 const StringTable = @import("StringTable.zig");
   2972 const Type = @import("../type.zig").Type;
   2973 const Value = @import("../Value.zig");