master
   1archive: ?InArchive = null,
   2/// Archive files cannot contain subdirectories, so only the basename is needed
   3/// for output. However, the full path is kept for error reporting.
   4path: Path,
   5file_handle: File.HandleIndex,
   6index: File.Index,
   7
   8header: ?elf.Elf64_Ehdr = null,
   9shdrs: std.ArrayList(elf.Elf64_Shdr) = .empty,
  10
  11symtab: std.ArrayList(elf.Elf64_Sym) = .empty,
  12strtab: std.ArrayList(u8) = .empty,
  13first_global: ?Symbol.Index = null,
  14symbols: std.ArrayList(Symbol) = .empty,
  15symbols_extra: std.ArrayList(u32) = .empty,
  16symbols_resolver: std.ArrayList(Elf.SymbolResolver.Index) = .empty,
  17relocs: std.ArrayList(elf.Elf64_Rela) = .empty,
  18
  19atoms: std.ArrayList(Atom) = .empty,
  20atoms_indexes: std.ArrayList(Atom.Index) = .empty,
  21atoms_extra: std.ArrayList(u32) = .empty,
  22
  23groups: std.ArrayList(Elf.Group) = .empty,
  24group_data: std.ArrayList(u32) = .empty,
  25
  26input_merge_sections: std.ArrayList(Merge.InputSection) = .empty,
  27input_merge_sections_indexes: std.ArrayList(Merge.InputSection.Index) = .empty,
  28
  29fdes: std.ArrayList(Fde) = .empty,
  30cies: std.ArrayList(Cie) = .empty,
  31eh_frame_data: std.ArrayList(u8) = .empty,
  32
  33alive: bool = true,
  34dirty: bool = true,
  35num_dynrelocs: u32 = 0,
  36
  37output_symtab_ctx: Elf.SymtabCtx = .{},
  38output_ar_state: Archive.ArState = .{},
  39
  40pub fn deinit(self: *Object, gpa: Allocator) void {
  41    if (self.archive) |*ar| gpa.free(ar.path.sub_path);
  42    gpa.free(self.path.sub_path);
  43    self.shdrs.deinit(gpa);
  44    self.symtab.deinit(gpa);
  45    self.strtab.deinit(gpa);
  46    self.symbols.deinit(gpa);
  47    self.symbols_extra.deinit(gpa);
  48    self.symbols_resolver.deinit(gpa);
  49    self.atoms.deinit(gpa);
  50    self.atoms_indexes.deinit(gpa);
  51    self.atoms_extra.deinit(gpa);
  52    self.groups.deinit(gpa);
  53    self.group_data.deinit(gpa);
  54    self.relocs.deinit(gpa);
  55    self.fdes.deinit(gpa);
  56    self.cies.deinit(gpa);
  57    self.eh_frame_data.deinit(gpa);
  58    for (self.input_merge_sections.items) |*isec| {
  59        isec.deinit(gpa);
  60    }
  61    self.input_merge_sections.deinit(gpa);
  62    self.input_merge_sections_indexes.deinit(gpa);
  63}
  64
  65pub fn parse(
  66    self: *Object,
  67    gpa: Allocator,
  68    diags: *Diags,
  69    /// For error reporting purposes only.
  70    path: Path,
  71    handle: fs.File,
  72    target: *const std.Target,
  73    debug_fmt_strip: bool,
  74    default_sym_version: elf.Versym,
  75) !void {
  76    // Append null input merge section
  77    try self.input_merge_sections.append(gpa, .{});
  78    // Allocate atom index 0 to null atom
  79    try self.atoms.append(gpa, .{ .extra_index = try self.addAtomExtra(gpa, .{}) });
  80
  81    try self.initAtoms(gpa, diags, path, handle, debug_fmt_strip, target);
  82    try self.initSymbols(gpa, default_sym_version);
  83
  84    for (self.shdrs.items, 0..) |shdr, i| {
  85        const atom_ptr = self.atom(self.atoms_indexes.items[i]) orelse continue;
  86        if (!atom_ptr.alive) continue;
  87        if ((target.cpu.arch == .x86_64 and shdr.sh_type == elf.SHT_X86_64_UNWIND) or
  88            mem.eql(u8, self.getString(atom_ptr.name_offset), ".eh_frame"))
  89        {
  90            try self.parseEhFrame(gpa, handle, @intCast(i), target);
  91        }
  92    }
  93}
  94
  95pub fn parseCommon(
  96    self: *Object,
  97    gpa: Allocator,
  98    diags: *Diags,
  99    path: Path,
 100    handle: fs.File,
 101    target: *const std.Target,
 102) !void {
 103    const offset = if (self.archive) |ar| ar.offset else 0;
 104    const file_size = (try handle.stat()).size;
 105
 106    const header_buffer = try Elf.preadAllAlloc(gpa, handle, offset, @sizeOf(elf.Elf64_Ehdr));
 107    defer gpa.free(header_buffer);
 108    self.header = @as(*align(1) const elf.Elf64_Ehdr, @ptrCast(header_buffer)).*;
 109    if (!mem.eql(u8, self.header.?.e_ident[0..4], elf.MAGIC)) {
 110        return diags.failParse(path, "not an ELF file", .{});
 111    }
 112
 113    const em = target.toElfMachine();
 114    if (em != self.header.?.e_machine) {
 115        return diags.failParse(path, "invalid ELF machine type: {s}", .{
 116            @tagName(self.header.?.e_machine),
 117        });
 118    }
 119    try validateEFlags(diags, path, target, self.header.?.e_flags);
 120
 121    if (self.header.?.e_shnum == 0) return;
 122
 123    const shoff = math.cast(usize, self.header.?.e_shoff) orelse return error.Overflow;
 124    const shnum = math.cast(usize, self.header.?.e_shnum) orelse return error.Overflow;
 125    const shsize = shnum * @sizeOf(elf.Elf64_Shdr);
 126    if (file_size < offset + shoff or file_size < offset + shoff + shsize) {
 127        return diags.failParse(path, "corrupt header: section header table extends past the end of file", .{});
 128    }
 129
 130    const shdrs_buffer = try Elf.preadAllAlloc(gpa, handle, offset + shoff, shsize);
 131    defer gpa.free(shdrs_buffer);
 132    const shdrs = @as([*]align(1) const elf.Elf64_Shdr, @ptrCast(shdrs_buffer.ptr))[0..shnum];
 133    try self.shdrs.appendUnalignedSlice(gpa, shdrs);
 134
 135    for (self.shdrs.items) |shdr| {
 136        if (shdr.sh_type != elf.SHT_NOBITS) {
 137            if (file_size < offset + shdr.sh_offset or file_size < offset + shdr.sh_offset + shdr.sh_size) {
 138                return diags.failParse(path, "corrupt section: extends past the end of file", .{});
 139            }
 140        }
 141    }
 142
 143    const shstrtab = try self.preadShdrContentsAlloc(gpa, handle, self.header.?.e_shstrndx);
 144    defer gpa.free(shstrtab);
 145    for (self.shdrs.items) |shdr| {
 146        if (shdr.sh_name >= shstrtab.len) {
 147            return diags.failParse(path, "corrupt section name offset", .{});
 148        }
 149    }
 150    try self.strtab.appendSlice(gpa, shstrtab);
 151
 152    const symtab_index = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {
 153        elf.SHT_SYMTAB => break @as(u32, @intCast(i)),
 154        else => {},
 155    } else null;
 156
 157    if (symtab_index) |index| {
 158        const shdr = self.shdrs.items[index];
 159        self.first_global = shdr.sh_info;
 160
 161        const raw_symtab = try self.preadShdrContentsAlloc(gpa, handle, index);
 162        defer gpa.free(raw_symtab);
 163        const nsyms = math.divExact(usize, raw_symtab.len, @sizeOf(elf.Elf64_Sym)) catch {
 164            return diags.failParse(path, "symbol table not evenly divisible", .{});
 165        };
 166        const symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw_symtab.ptr))[0..nsyms];
 167
 168        const strtab_bias = @as(u32, @intCast(self.strtab.items.len));
 169        const strtab = try self.preadShdrContentsAlloc(gpa, handle, shdr.sh_link);
 170        defer gpa.free(strtab);
 171        try self.strtab.appendSlice(gpa, strtab);
 172
 173        try self.symtab.ensureUnusedCapacity(gpa, symtab.len);
 174        for (symtab) |sym| {
 175            const out_sym = self.symtab.addOneAssumeCapacity();
 176            out_sym.* = sym;
 177            out_sym.st_name = if (sym.st_name == 0 and sym.st_type() == elf.STT_SECTION)
 178                shdrs[sym.st_shndx].sh_name
 179            else
 180                sym.st_name + strtab_bias;
 181        }
 182    }
 183}
 184
 185pub fn validateEFlags(
 186    diags: *Diags,
 187    path: Path,
 188    target: *const std.Target,
 189    e_flags: elf.Word,
 190) !void {
 191    switch (target.cpu.arch) {
 192        .riscv64, .riscv64be => {
 193            const flags: riscv.Eflags = @bitCast(e_flags);
 194            var any_errors: bool = false;
 195
 196            // For an input object to target an ABI that the target CPU doesn't have enabled
 197            // is invalid, and will throw an error.
 198
 199            // Invalid when
 200            // 1. The input uses C and we do not.
 201            if (flags.rvc and !target.cpu.has(.riscv, .c)) {
 202                any_errors = true;
 203                diags.addParseError(
 204                    path,
 205                    "cannot link object file targeting the C feature without having the C feature enabled",
 206                    .{},
 207                );
 208            }
 209
 210            // Invalid when
 211            // 1. We use E and the input does not.
 212            // 2. The input uses E and we do not.
 213            if (target.cpu.has(.riscv, .e) != flags.rve) {
 214                any_errors = true;
 215                diags.addParseError(
 216                    path,
 217                    "{s}",
 218                    .{
 219                        if (flags.rve)
 220                            "cannot link object file targeting the E feature without having the E feature enabled"
 221                        else
 222                            "cannot link object file not targeting the E feature while having the E feature enabled",
 223                    },
 224                );
 225            }
 226
 227            // Invalid when
 228            // 1. We use total store order and the input does not.
 229            // 2. The input uses total store order and we do not.
 230            if (flags.tso != target.cpu.has(.riscv, .ztso)) {
 231                any_errors = true;
 232                diags.addParseError(
 233                    path,
 234                    "cannot link object file targeting the TSO memory model without having the ztso feature enabled",
 235                    .{},
 236                );
 237            }
 238
 239            const fabi: riscv.Eflags.FloatAbi =
 240                if (target.cpu.has(.riscv, .d))
 241                    .double
 242                else if (target.cpu.has(.riscv, .f))
 243                    .single
 244                else
 245                    .soft;
 246
 247            if (flags.fabi != fabi) {
 248                any_errors = true;
 249                diags.addParseError(
 250                    path,
 251                    "cannot link object file targeting a different floating-point ABI. targeting {s}, found {s}",
 252                    .{ @tagName(fabi), @tagName(flags.fabi) },
 253                );
 254            }
 255
 256            if (any_errors) return error.LinkFailure;
 257        },
 258        else => {},
 259    }
 260}
 261
 262fn initAtoms(
 263    self: *Object,
 264    gpa: Allocator,
 265    diags: *Diags,
 266    path: Path,
 267    handle: fs.File,
 268    debug_fmt_strip: bool,
 269    target: *const std.Target,
 270) !void {
 271    const shdrs = self.shdrs.items;
 272    try self.atoms.ensureTotalCapacityPrecise(gpa, shdrs.len);
 273    try self.atoms_extra.ensureTotalCapacityPrecise(gpa, shdrs.len * @sizeOf(Atom.Extra));
 274    try self.atoms_indexes.ensureTotalCapacityPrecise(gpa, shdrs.len);
 275    try self.atoms_indexes.resize(gpa, shdrs.len);
 276    @memset(self.atoms_indexes.items, 0);
 277
 278    for (shdrs, 0..) |shdr, i| {
 279        if (shdr.sh_flags & elf.SHF_EXCLUDE != 0 and
 280            shdr.sh_flags & elf.SHF_ALLOC == 0 and
 281            shdr.sh_type != elf.SHT_LLVM_ADDRSIG) continue;
 282
 283        switch (shdr.sh_type) {
 284            elf.SHT_GROUP => {
 285                if (shdr.sh_info >= self.symtab.items.len) {
 286                    // TODO convert into an error
 287                    log.debug("{f}: invalid symbol index in sh_info", .{self.fmtPath()});
 288                    continue;
 289                }
 290                const group_info_sym = self.symtab.items[shdr.sh_info];
 291                const group_signature = blk: {
 292                    if (group_info_sym.st_name == 0 and group_info_sym.st_type() == elf.STT_SECTION) {
 293                        const sym_shdr = shdrs[group_info_sym.st_shndx];
 294                        break :blk sym_shdr.sh_name;
 295                    }
 296                    break :blk group_info_sym.st_name;
 297                };
 298
 299                const shndx: u32 = @intCast(i);
 300                const group_raw_data = try self.preadShdrContentsAlloc(gpa, handle, shndx);
 301                defer gpa.free(group_raw_data);
 302                const group_nmembers = math.divExact(usize, group_raw_data.len, @sizeOf(u32)) catch {
 303                    return diags.failParse(path, "corrupt section group: not evenly divisible ", .{});
 304                };
 305                if (group_nmembers == 0) {
 306                    return diags.failParse(path, "corrupt section group: empty section", .{});
 307                }
 308                const group_members = @as([*]align(1) const u32, @ptrCast(group_raw_data.ptr))[0..group_nmembers];
 309
 310                switch (group_members[0]) {
 311                    0, elf.GRP_COMDAT => {
 312                        const group_start: u32 = @intCast(self.group_data.items.len);
 313                        try self.group_data.appendUnalignedSlice(gpa, group_members[1..]);
 314
 315                        self.group(try self.addGroup(gpa)).* = .{
 316                            .signature_off = group_signature,
 317                            .file_index = self.index,
 318                            .shndx = shndx,
 319                            .members_start = group_start,
 320                            .members_len = @intCast(group_nmembers - 1),
 321                            .is_comdat = group_members[0] == elf.GRP_COMDAT,
 322                        };
 323                    },
 324                    else => return diags.failParse(path, "corrupt section group: unknown SHT_GROUP format", .{}),
 325                }
 326            },
 327
 328            elf.SHT_SYMTAB_SHNDX => @panic("TODO SHT_SYMTAB_SHNDX"),
 329
 330            elf.SHT_NULL,
 331            elf.SHT_REL,
 332            elf.SHT_RELA,
 333            elf.SHT_SYMTAB,
 334            elf.SHT_STRTAB,
 335            => {},
 336
 337            else => {
 338                const shndx: u32 = @intCast(i);
 339                if (self.skipShdr(shndx, debug_fmt_strip)) continue;
 340                const size, const alignment = if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) blk: {
 341                    const data = try self.preadShdrContentsAlloc(gpa, handle, shndx);
 342                    defer gpa.free(data);
 343                    const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*;
 344                    break :blk .{ chdr.ch_size, Alignment.fromNonzeroByteUnits(chdr.ch_addralign) };
 345                } else .{ shdr.sh_size, Alignment.fromNonzeroByteUnits(shdr.sh_addralign) };
 346                const atom_index = self.addAtomAssumeCapacity(.{
 347                    .name = shdr.sh_name,
 348                    .shndx = shndx,
 349                    .size = size,
 350                    .alignment = alignment,
 351                });
 352                self.atoms_indexes.items[shndx] = atom_index;
 353            },
 354        }
 355    }
 356
 357    // Parse relocs sections if any.
 358    for (shdrs, 0..) |shdr, i| switch (shdr.sh_type) {
 359        elf.SHT_REL, elf.SHT_RELA => {
 360            const atom_index = self.atoms_indexes.items[shdr.sh_info];
 361            if (self.atom(atom_index)) |atom_ptr| {
 362                const relocs = try self.preadRelocsAlloc(gpa, handle, @intCast(i));
 363                defer gpa.free(relocs);
 364                atom_ptr.relocs_section_index = @intCast(i);
 365                const rel_index: u32 = @intCast(self.relocs.items.len);
 366                const rel_count: u32 = @intCast(relocs.len);
 367                self.setAtomFields(atom_ptr, .{ .rel_index = rel_index, .rel_count = rel_count });
 368                try self.relocs.appendUnalignedSlice(gpa, relocs);
 369                if (target.cpu.arch.isRiscv64()) {
 370                    sortRelocs(self.relocs.items[rel_index..][0..rel_count]);
 371                }
 372            }
 373        },
 374        else => {},
 375    };
 376}
 377
 378fn skipShdr(self: *Object, index: u32, debug_fmt_strip: bool) bool {
 379    const shdr = self.shdrs.items[index];
 380    const name = self.getString(shdr.sh_name);
 381    const ignore = blk: {
 382        if (mem.startsWith(u8, name, ".note")) break :blk true;
 383        if (mem.startsWith(u8, name, ".llvm_addrsig")) break :blk true;
 384        if (mem.startsWith(u8, name, ".riscv.attributes")) break :blk true; // TODO: riscv attributes
 385        if (debug_fmt_strip and shdr.sh_flags & elf.SHF_ALLOC == 0 and
 386            mem.startsWith(u8, name, ".debug")) break :blk true;
 387        break :blk false;
 388    };
 389    return ignore;
 390}
 391
 392fn initSymbols(
 393    self: *Object,
 394    gpa: Allocator,
 395    default_sym_version: elf.Versym,
 396) !void {
 397    const first_global = self.first_global orelse self.symtab.items.len;
 398    const nglobals = self.symtab.items.len - first_global;
 399
 400    try self.symbols.ensureTotalCapacityPrecise(gpa, self.symtab.items.len);
 401    try self.symbols_extra.ensureTotalCapacityPrecise(gpa, self.symtab.items.len * @sizeOf(Symbol.Extra));
 402    try self.symbols_resolver.ensureTotalCapacityPrecise(gpa, nglobals);
 403    self.symbols_resolver.resize(gpa, nglobals) catch unreachable;
 404    @memset(self.symbols_resolver.items, 0);
 405
 406    for (self.symtab.items, 0..) |sym, i| {
 407        const index = self.addSymbolAssumeCapacity();
 408        const sym_ptr = &self.symbols.items[index];
 409        sym_ptr.value = @intCast(sym.st_value);
 410        sym_ptr.name_offset = sym.st_name;
 411        sym_ptr.esym_index = @intCast(i);
 412        sym_ptr.extra_index = self.addSymbolExtraAssumeCapacity(.{});
 413        sym_ptr.version_index = if (i >= first_global) default_sym_version else .LOCAL;
 414        sym_ptr.flags.weak = sym.st_bind() == elf.STB_WEAK;
 415        if (sym.st_shndx != elf.SHN_ABS and sym.st_shndx != elf.SHN_COMMON) {
 416            sym_ptr.ref = .{ .index = self.atoms_indexes.items[sym.st_shndx], .file = self.index };
 417        }
 418    }
 419}
 420
 421fn parseEhFrame(
 422    self: *Object,
 423    gpa: Allocator,
 424    handle: fs.File,
 425    shndx: u32,
 426    target: *const std.Target,
 427) !void {
 428    const relocs_shndx = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {
 429        elf.SHT_RELA => if (shdr.sh_info == shndx) break @as(u32, @intCast(i)),
 430        else => {},
 431    } else null;
 432
 433    const raw = try self.preadShdrContentsAlloc(gpa, handle, shndx);
 434    defer gpa.free(raw);
 435    const data_start: u32 = @intCast(self.eh_frame_data.items.len);
 436    try self.eh_frame_data.appendSlice(gpa, raw);
 437    const relocs = if (relocs_shndx) |index|
 438        try self.preadRelocsAlloc(gpa, handle, index)
 439    else
 440        &[0]elf.Elf64_Rela{};
 441    defer gpa.free(relocs);
 442    const rel_start: u32 = @intCast(self.relocs.items.len);
 443    try self.relocs.appendUnalignedSlice(gpa, relocs);
 444
 445    // We expect relocations to be sorted by r_offset as per this comment in mold linker:
 446    // https://github.com/rui314/mold/blob/8e4f7b53832d8af4f48a633a8385cbc932d1944e/src/input-files.cc#L653
 447    // Except for RISCV and Loongarch which do not seem to be uphold this convention.
 448    if (target.cpu.arch.isRiscv64()) {
 449        sortRelocs(self.relocs.items[rel_start..][0..relocs.len]);
 450    }
 451    const fdes_start = self.fdes.items.len;
 452    const cies_start = self.cies.items.len;
 453
 454    var it = eh_frame.Iterator{ .data = raw };
 455    while (try it.next()) |rec| {
 456        const rel_range = filterRelocs(self.relocs.items[rel_start..][0..relocs.len], rec.offset, rec.size + 4);
 457        switch (rec.tag) {
 458            .cie => try self.cies.append(gpa, .{
 459                .offset = data_start + rec.offset,
 460                .size = rec.size,
 461                .rel_index = rel_start + @as(u32, @intCast(rel_range.start)),
 462                .rel_num = @intCast(rel_range.len),
 463                .input_section_index = shndx,
 464                .file_index = self.index,
 465            }),
 466            .fde => {
 467                if (rel_range.len == 0) {
 468                    // No relocs for an FDE means we cannot associate this FDE to an Atom
 469                    // so we skip it. According to mold source code
 470                    // (https://github.com/rui314/mold/blob/a3e69502b0eaf1126d6093e8ea5e6fdb95219811/src/input-files.cc#L525-L528)
 471                    // this can happen for object files built with -r flag by the linker.
 472                    continue;
 473                }
 474                try self.fdes.append(gpa, .{
 475                    .offset = data_start + rec.offset,
 476                    .size = rec.size,
 477                    .cie_index = undefined,
 478                    .rel_index = rel_start + @as(u32, @intCast(rel_range.start)),
 479                    .rel_num = @intCast(rel_range.len),
 480                    .input_section_index = shndx,
 481                    .file_index = self.index,
 482                });
 483            },
 484        }
 485    }
 486
 487    // Tie each FDE to its CIE
 488    for (self.fdes.items[fdes_start..]) |*fde| {
 489        const cie_ptr = fde.offset + 4 - fde.ciePointer(self);
 490        const cie_index = for (self.cies.items[cies_start..], cies_start..) |cie, cie_index| {
 491            if (cie.offset == cie_ptr) break @as(u32, @intCast(cie_index));
 492        } else {
 493            // TODO convert into an error
 494            log.debug("{f}: no matching CIE found for FDE at offset {x}", .{ self.fmtPath(), fde.offset });
 495            continue;
 496        };
 497        fde.cie_index = cie_index;
 498    }
 499
 500    // Tie each FDE record to its matching atom
 501    const SortFdes = struct {
 502        pub fn lessThan(ctx: *Object, lhs: Fde, rhs: Fde) bool {
 503            const lhs_atom = lhs.atom(ctx);
 504            const rhs_atom = rhs.atom(ctx);
 505            return Atom.priorityLookup(ctx.index, lhs_atom.input_section_index) < Atom.priorityLookup(ctx.index, rhs_atom.input_section_index);
 506        }
 507    };
 508    mem.sort(Fde, self.fdes.items[fdes_start..], self, SortFdes.lessThan);
 509
 510    // Create a back-link from atom to FDEs
 511    var i: u32 = @intCast(fdes_start);
 512    while (i < self.fdes.items.len) {
 513        const fde = self.fdes.items[i];
 514        const atom_ptr = fde.atom(self);
 515        const start = i;
 516        i += 1;
 517        while (i < self.fdes.items.len) : (i += 1) {
 518            const next_fde = self.fdes.items[i];
 519            if (atom_ptr.atom_index != next_fde.atom(self).atom_index) break;
 520        }
 521        self.setAtomFields(atom_ptr, .{ .fde_start = start, .fde_count = i - start });
 522    }
 523}
 524
 525fn sortRelocs(relocs: []elf.Elf64_Rela) void {
 526    const sortFn = struct {
 527        fn lessThan(c: void, lhs: elf.Elf64_Rela, rhs: elf.Elf64_Rela) bool {
 528            _ = c;
 529            return lhs.r_offset < rhs.r_offset;
 530        }
 531    }.lessThan;
 532    mem.sort(elf.Elf64_Rela, relocs, {}, sortFn);
 533}
 534
 535fn filterRelocs(
 536    relocs: []const elf.Elf64_Rela,
 537    start: u64,
 538    len: u64,
 539) struct { start: u64, len: u64 } {
 540    const Predicate = struct {
 541        value: u64,
 542
 543        pub fn predicate(self: @This(), rel: elf.Elf64_Rela) bool {
 544            return rel.r_offset < self.value;
 545        }
 546    };
 547    const LPredicate = struct {
 548        value: u64,
 549
 550        pub fn predicate(self: @This(), rel: elf.Elf64_Rela) bool {
 551            return rel.r_offset >= self.value;
 552        }
 553    };
 554
 555    const f_start = Elf.bsearch(elf.Elf64_Rela, relocs, Predicate{ .value = start });
 556    const f_len = Elf.lsearch(elf.Elf64_Rela, relocs[f_start..], LPredicate{ .value = start + len });
 557
 558    return .{ .start = f_start, .len = f_len };
 559}
 560
 561pub fn scanRelocs(self: *Object, elf_file: *Elf, undefs: anytype) !void {
 562    const comp = elf_file.base.comp;
 563    const gpa = comp.gpa;
 564    for (self.atoms_indexes.items) |atom_index| {
 565        const atom_ptr = self.atom(atom_index) orelse continue;
 566        if (!atom_ptr.alive) continue;
 567        const shdr = atom_ptr.inputShdr(elf_file);
 568        if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;
 569        if (shdr.sh_type == elf.SHT_NOBITS) continue;
 570        if (atom_ptr.scanRelocsRequiresCode(elf_file)) {
 571            // TODO ideally, we don't have to decompress at this stage (should already be done)
 572            // and we just fetch the code slice.
 573            const code = try self.codeDecompressAlloc(elf_file, atom_index);
 574            defer gpa.free(code);
 575            try atom_ptr.scanRelocs(elf_file, code, undefs);
 576        } else try atom_ptr.scanRelocs(elf_file, null, undefs);
 577    }
 578
 579    for (self.cies.items) |cie| {
 580        for (cie.relocs(elf_file)) |rel| {
 581            const sym = elf_file.symbol(self.resolveSymbol(rel.r_sym(), elf_file)).?;
 582            if (sym.flags.import) {
 583                if (sym.type(elf_file) != elf.STT_FUNC)
 584                    // TODO convert into an error
 585                    log.debug("{f}: {s}: CIE referencing external data reference", .{
 586                        self.fmtPath(), sym.name(elf_file),
 587                    });
 588                sym.flags.needs_plt = true;
 589            }
 590        }
 591    }
 592}
 593
 594pub fn resolveSymbols(self: *Object, elf_file: *Elf) !void {
 595    const gpa = elf_file.base.comp.gpa;
 596
 597    const first_global = self.first_global orelse return;
 598    for (self.globals(), first_global..) |_, i| {
 599        const esym = self.symtab.items[i];
 600        const resolv = &self.symbols_resolver.items[i - first_global];
 601        const gop = try elf_file.resolver.getOrPut(gpa, .{
 602            .index = @intCast(i),
 603            .file = self.index,
 604        }, elf_file);
 605        if (!gop.found_existing) {
 606            gop.ref.* = .{ .index = 0, .file = 0 };
 607        }
 608        resolv.* = gop.index;
 609
 610        if (esym.st_shndx == elf.SHN_UNDEF) continue;
 611        if (esym.st_shndx != elf.SHN_ABS and esym.st_shndx != elf.SHN_COMMON) {
 612            const atom_index = self.atoms_indexes.items[esym.st_shndx];
 613            const atom_ptr = self.atom(atom_index) orelse continue;
 614            if (!atom_ptr.alive) continue;
 615        }
 616        if (elf_file.symbol(gop.ref.*) == null) {
 617            gop.ref.* = .{ .index = @intCast(i), .file = self.index };
 618            continue;
 619        }
 620
 621        if (self.asFile().symbolRank(esym, !self.alive) < elf_file.symbol(gop.ref.*).?.symbolRank(elf_file)) {
 622            gop.ref.* = .{ .index = @intCast(i), .file = self.index };
 623        }
 624    }
 625}
 626
 627pub fn claimUnresolved(self: *Object, elf_file: *Elf) void {
 628    const first_global = self.first_global orelse return;
 629    for (self.globals(), 0..) |*sym, i| {
 630        const esym_index = @as(u32, @intCast(first_global + i));
 631        const esym = self.symtab.items[esym_index];
 632        if (esym.st_shndx != elf.SHN_UNDEF) continue;
 633        if (elf_file.symbol(self.resolveSymbol(esym_index, elf_file)) != null) continue;
 634
 635        const is_import = blk: {
 636            if (!elf_file.isEffectivelyDynLib()) break :blk false;
 637            const vis: elf.STV = @enumFromInt(@as(u3, @truncate(esym.st_other)));
 638            if (vis == .HIDDEN) break :blk false;
 639            break :blk true;
 640        };
 641
 642        sym.value = 0;
 643        sym.ref = .{ .index = 0, .file = 0 };
 644        sym.esym_index = esym_index;
 645        sym.file_index = self.index;
 646        sym.version_index = if (is_import) .LOCAL else elf_file.default_sym_version;
 647        sym.flags.import = is_import;
 648
 649        const idx = self.symbols_resolver.items[i];
 650        elf_file.resolver.values.items[idx - 1] = .{ .index = esym_index, .file = self.index };
 651    }
 652}
 653
 654pub fn claimUnresolvedRelocatable(self: *Object, elf_file: *Elf) void {
 655    const first_global = self.first_global orelse return;
 656    for (self.globals(), 0..) |*sym, i| {
 657        const esym_index = @as(u32, @intCast(first_global + i));
 658        const esym = self.symtab.items[esym_index];
 659        if (esym.st_shndx != elf.SHN_UNDEF) continue;
 660        if (elf_file.symbol(self.resolveSymbol(esym_index, elf_file)) != null) continue;
 661
 662        sym.value = 0;
 663        sym.ref = .{ .index = 0, .file = 0 };
 664        sym.esym_index = esym_index;
 665        sym.file_index = self.index;
 666
 667        const idx = self.symbols_resolver.items[i];
 668        elf_file.resolver.values.items[idx - 1] = .{ .index = esym_index, .file = self.index };
 669    }
 670}
 671
 672pub fn markLive(self: *Object, elf_file: *Elf) void {
 673    const first_global = self.first_global orelse return;
 674    for (0..self.globals().len) |i| {
 675        const esym_idx = first_global + i;
 676        const esym = self.symtab.items[esym_idx];
 677        if (esym.st_bind() == elf.STB_WEAK) continue;
 678
 679        const ref = self.resolveSymbol(@intCast(esym_idx), elf_file);
 680        const sym = elf_file.symbol(ref) orelse continue;
 681        const file = sym.file(elf_file).?;
 682        const should_keep = esym.st_shndx == elf.SHN_UNDEF or
 683            (esym.st_shndx == elf.SHN_COMMON and sym.elfSym(elf_file).st_shndx != elf.SHN_COMMON);
 684        if (should_keep and !file.isAlive()) {
 685            file.setAlive();
 686            file.markLive(elf_file);
 687        }
 688    }
 689}
 690
 691pub fn markEhFrameAtomsDead(self: *Object, elf_file: *Elf) void {
 692    const cpu_arch = elf_file.getTarget().cpu.arch;
 693    for (self.atoms_indexes.items) |atom_index| {
 694        const atom_ptr = self.atom(atom_index) orelse continue;
 695        const is_eh_frame = (cpu_arch == .x86_64 and atom_ptr.inputShdr(elf_file).sh_type == elf.SHT_X86_64_UNWIND) or
 696            mem.eql(u8, atom_ptr.name(elf_file), ".eh_frame");
 697        if (atom_ptr.alive and is_eh_frame) atom_ptr.alive = false;
 698    }
 699}
 700
 701pub fn markImportsExports(self: *Object, elf_file: *Elf) void {
 702    const first_global = self.first_global orelse return;
 703    for (0..self.globals().len) |i| {
 704        const idx = first_global + i;
 705        const ref = self.resolveSymbol(@intCast(idx), elf_file);
 706        const sym = elf_file.symbol(ref) orelse continue;
 707        const file = sym.file(elf_file).?;
 708        // https://github.com/ziglang/zig/issues/21678
 709        if (@as(u16, @bitCast(sym.version_index)) == @as(u16, @bitCast(elf.Versym.LOCAL))) continue;
 710        const vis: elf.STV = @enumFromInt(@as(u3, @truncate(sym.elfSym(elf_file).st_other)));
 711        if (vis == .HIDDEN) continue;
 712        if (file == .shared_object and !sym.isAbs(elf_file)) {
 713            sym.flags.import = true;
 714            continue;
 715        }
 716        if (file.index() == self.index) {
 717            sym.flags.@"export" = true;
 718            if (elf_file.isEffectivelyDynLib() and vis != .PROTECTED) {
 719                sym.flags.import = true;
 720            }
 721        }
 722    }
 723}
 724
 725pub fn checkDuplicates(self: *Object, dupes: anytype, elf_file: *Elf) error{OutOfMemory}!void {
 726    const first_global = self.first_global orelse return;
 727    for (0..self.globals().len) |i| {
 728        const esym_idx = first_global + i;
 729        const esym = self.symtab.items[esym_idx];
 730        const ref = self.resolveSymbol(@intCast(esym_idx), elf_file);
 731        const ref_sym = elf_file.symbol(ref) orelse continue;
 732        const ref_file = ref_sym.file(elf_file).?;
 733
 734        if (self.index == ref_file.index() or
 735            esym.st_shndx == elf.SHN_UNDEF or
 736            esym.st_bind() == elf.STB_WEAK or
 737            esym.st_shndx == elf.SHN_COMMON) continue;
 738
 739        if (esym.st_shndx != elf.SHN_ABS) {
 740            const atom_index = self.atoms_indexes.items[esym.st_shndx];
 741            const atom_ptr = self.atom(atom_index) orelse continue;
 742            if (!atom_ptr.alive) continue;
 743        }
 744
 745        const gop = try dupes.getOrPut(self.symbols_resolver.items[i]);
 746        if (!gop.found_existing) {
 747            gop.value_ptr.* = .{};
 748        }
 749        try gop.value_ptr.append(elf_file.base.comp.gpa, self.index);
 750    }
 751}
 752
 753pub fn initInputMergeSections(self: *Object, elf_file: *Elf) !void {
 754    const gpa = elf_file.base.comp.gpa;
 755    const diags = &elf_file.base.comp.link_diags;
 756
 757    try self.input_merge_sections.ensureUnusedCapacity(gpa, self.shdrs.items.len);
 758    try self.input_merge_sections_indexes.resize(gpa, self.shdrs.items.len);
 759    @memset(self.input_merge_sections_indexes.items, 0);
 760
 761    for (self.shdrs.items, 0..) |shdr, shndx| {
 762        if (shdr.sh_flags & elf.SHF_MERGE == 0) continue;
 763
 764        const atom_index = self.atoms_indexes.items[shndx];
 765        const atom_ptr = self.atom(atom_index) orelse continue;
 766        if (!atom_ptr.alive) continue;
 767        if (atom_ptr.relocs(elf_file).len > 0) continue;
 768
 769        const imsec_idx = try self.addInputMergeSection(gpa);
 770        const imsec = self.inputMergeSection(imsec_idx).?;
 771        self.input_merge_sections_indexes.items[shndx] = imsec_idx;
 772        imsec.atom_index = atom_index;
 773
 774        const data = try self.codeDecompressAlloc(elf_file, atom_index);
 775        defer gpa.free(data);
 776
 777        if (shdr.sh_flags & elf.SHF_STRINGS != 0) {
 778            const sh_entsize: u32 = switch (shdr.sh_entsize) {
 779                // According to mold's source code, GHC emits MS sections with sh_entsize = 0.
 780                // This actually can also happen for output created with `-r` mode.
 781                0 => 1,
 782                else => |x| @intCast(x),
 783            };
 784
 785            const isNull = struct {
 786                fn isNull(slice: []u8) bool {
 787                    for (slice) |x| if (x != 0) return false;
 788                    return true;
 789                }
 790            }.isNull;
 791
 792            var start: u32 = 0;
 793            while (start < data.len) {
 794                var end = start;
 795                while (end < data.len - sh_entsize and !isNull(data[end .. end + sh_entsize])) : (end += sh_entsize) {}
 796                if (!isNull(data[end .. end + sh_entsize])) {
 797                    var err = try diags.addErrorWithNotes(1);
 798                    try err.addMsg("string not null terminated", .{});
 799                    err.addNote("in {f}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
 800                    return error.LinkFailure;
 801                }
 802                end += sh_entsize;
 803                const string = data[start..end];
 804                try imsec.insert(gpa, string);
 805                try imsec.offsets.append(gpa, start);
 806                start = end;
 807            }
 808        } else {
 809            const sh_entsize: u32 = @intCast(shdr.sh_entsize);
 810            if (sh_entsize == 0) continue; // Malformed, don't split but don't error out
 811            if (shdr.sh_size % sh_entsize != 0) {
 812                var err = try diags.addErrorWithNotes(1);
 813                try err.addMsg("size not a multiple of sh_entsize", .{});
 814                err.addNote("in {f}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
 815                return error.LinkFailure;
 816            }
 817
 818            var pos: u32 = 0;
 819            while (pos < data.len) : (pos += sh_entsize) {
 820                const string = data.ptr[pos..][0..sh_entsize];
 821                try imsec.insert(gpa, string);
 822                try imsec.offsets.append(gpa, pos);
 823            }
 824        }
 825
 826        atom_ptr.alive = false;
 827    }
 828}
 829
 830pub fn initOutputMergeSections(self: *Object, elf_file: *Elf) !void {
 831    for (self.input_merge_sections_indexes.items) |index| {
 832        const imsec = self.inputMergeSection(index) orelse continue;
 833        const atom_ptr = self.atom(imsec.atom_index).?;
 834        const shdr = atom_ptr.inputShdr(elf_file);
 835        imsec.merge_section_index = try elf_file.getOrCreateMergeSection(
 836            atom_ptr.name(elf_file),
 837            shdr.sh_flags,
 838            shdr.sh_type,
 839        );
 840    }
 841}
 842
 843pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) error{
 844    LinkFailure,
 845    OutOfMemory,
 846    /// TODO report the error and remove this
 847    Overflow,
 848}!void {
 849    const gpa = elf_file.base.comp.gpa;
 850    const diags = &elf_file.base.comp.link_diags;
 851
 852    for (self.input_merge_sections_indexes.items) |index| {
 853        const imsec = self.inputMergeSection(index) orelse continue;
 854        if (imsec.offsets.items.len == 0) continue;
 855        const msec = elf_file.mergeSection(imsec.merge_section_index);
 856        const atom_ptr = self.atom(imsec.atom_index).?;
 857        const isec = atom_ptr.inputShdr(elf_file);
 858
 859        try imsec.subsections.resize(gpa, imsec.strings.items.len);
 860
 861        for (imsec.strings.items, imsec.subsections.items) |str, *imsec_msub| {
 862            const string = imsec.bytes.items[str.pos..][0..str.len];
 863            const res = try msec.insert(gpa, string);
 864            if (res.found_existing) {
 865                const msub = msec.mergeSubsection(res.sub.*);
 866                msub.alignment = msub.alignment.maxStrict(atom_ptr.alignment);
 867            } else {
 868                const msub_index = try msec.addMergeSubsection(gpa);
 869                const msub = msec.mergeSubsection(msub_index);
 870                msub.merge_section_index = imsec.merge_section_index;
 871                msub.string_index = res.key.pos;
 872                msub.alignment = atom_ptr.alignment;
 873                msub.size = res.key.len;
 874                msub.entsize = math.cast(u32, isec.sh_entsize) orelse return error.Overflow;
 875                msub.alive = !elf_file.base.gc_sections or isec.sh_flags & elf.SHF_ALLOC == 0;
 876                res.sub.* = msub_index;
 877            }
 878            imsec_msub.* = res.sub.*;
 879        }
 880
 881        imsec.clearAndFree(gpa);
 882    }
 883
 884    for (self.symtab.items, 0..) |*esym, idx| {
 885        const sym = &self.symbols.items[idx];
 886        if (esym.st_shndx == elf.SHN_COMMON or esym.st_shndx == elf.SHN_UNDEF or esym.st_shndx == elf.SHN_ABS) continue;
 887
 888        const imsec_index = self.input_merge_sections_indexes.items[esym.st_shndx];
 889        const imsec = self.inputMergeSection(imsec_index) orelse continue;
 890        if (imsec.offsets.items.len == 0) continue;
 891        const res = imsec.findSubsection(@intCast(esym.st_value)) orelse {
 892            var err = try diags.addErrorWithNotes(2);
 893            try err.addMsg("invalid symbol value: {x}", .{esym.st_value});
 894            err.addNote("for symbol {s}", .{sym.name(elf_file)});
 895            err.addNote("in {f}", .{self.fmtPath()});
 896            return error.LinkFailure;
 897        };
 898
 899        sym.ref = .{ .index = res.msub_index, .file = imsec.merge_section_index };
 900        sym.flags.merge_subsection = true;
 901        sym.value = res.offset;
 902    }
 903
 904    for (self.atoms_indexes.items) |atom_index| {
 905        const atom_ptr = self.atom(atom_index) orelse continue;
 906        if (!atom_ptr.alive) continue;
 907        const extras = atom_ptr.extra(elf_file);
 908        const relocs = self.relocs.items[extras.rel_index..][0..extras.rel_count];
 909        for (relocs) |*rel| {
 910            const esym = self.symtab.items[rel.r_sym()];
 911            if (esym.st_type() != elf.STT_SECTION) continue;
 912
 913            const imsec_index = self.input_merge_sections_indexes.items[esym.st_shndx];
 914            const imsec = self.inputMergeSection(imsec_index) orelse continue;
 915            if (imsec.offsets.items.len == 0) continue;
 916            const msec = elf_file.mergeSection(imsec.merge_section_index);
 917            const res = imsec.findSubsection(@intCast(@as(i64, @intCast(esym.st_value)) + rel.r_addend)) orelse {
 918                var err = try diags.addErrorWithNotes(1);
 919                try err.addMsg("invalid relocation at offset 0x{x}", .{rel.r_offset});
 920                err.addNote("in {f}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
 921                return error.LinkFailure;
 922            };
 923
 924            const sym_index = try self.addSymbol(gpa);
 925            const sym = &self.symbols.items[sym_index];
 926            const name = try std.fmt.allocPrint(gpa, "{s}$subsection{d}", .{ msec.name(elf_file), res.msub_index });
 927            defer gpa.free(name);
 928            sym.* = .{
 929                .value = @bitCast(@as(i64, @intCast(res.offset)) - rel.r_addend),
 930                .name_offset = try self.addString(gpa, name),
 931                .esym_index = rel.r_sym(),
 932                .file_index = self.index,
 933                .extra_index = try self.addSymbolExtra(gpa, .{}),
 934            };
 935            sym.ref = .{ .index = res.msub_index, .file = imsec.merge_section_index };
 936            sym.flags.merge_subsection = true;
 937            rel.r_info = (@as(u64, @intCast(sym_index)) << 32) | rel.r_type();
 938        }
 939    }
 940}
 941
 942/// We will create dummy shdrs per each resolved common symbols to make it
 943/// play nicely with the rest of the system.
 944pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void {
 945    const first_global = self.first_global orelse return;
 946    for (self.globals(), self.symbols_resolver.items, 0..) |*sym, resolv, i| {
 947        const esym_idx = @as(u32, @intCast(first_global + i));
 948        const esym = self.symtab.items[esym_idx];
 949        if (esym.st_shndx != elf.SHN_COMMON) continue;
 950        if (elf_file.resolver.get(resolv).?.file != self.index) continue;
 951
 952        const comp = elf_file.base.comp;
 953        const gpa = comp.gpa;
 954
 955        const is_tls = sym.type(elf_file) == elf.STT_TLS;
 956        const name = if (is_tls) ".tls_common" else ".common";
 957        const name_offset = @as(u32, @intCast(self.strtab.items.len));
 958        try self.strtab.print(gpa, "{s}\x00", .{name});
 959
 960        var sh_flags: u32 = elf.SHF_ALLOC | elf.SHF_WRITE;
 961        if (is_tls) sh_flags |= elf.SHF_TLS;
 962        const shndx = @as(u32, @intCast(self.shdrs.items.len));
 963        const shdr = try self.shdrs.addOne(gpa);
 964        const sh_size = math.cast(usize, esym.st_size) orelse return error.Overflow;
 965        shdr.* = .{
 966            .sh_name = name_offset,
 967            .sh_type = elf.SHT_NOBITS,
 968            .sh_flags = sh_flags,
 969            .sh_addr = 0,
 970            .sh_offset = 0,
 971            .sh_size = sh_size,
 972            .sh_link = 0,
 973            .sh_info = 0,
 974            .sh_addralign = esym.st_value,
 975            .sh_entsize = 0,
 976        };
 977
 978        const atom_index = try self.addAtom(gpa, .{
 979            .name = name_offset,
 980            .shndx = shndx,
 981            .size = esym.st_size,
 982            .alignment = Alignment.fromNonzeroByteUnits(esym.st_value),
 983        });
 984        try self.atoms_indexes.append(gpa, atom_index);
 985
 986        sym.value = 0;
 987        sym.ref = .{ .index = atom_index, .file = self.index };
 988        sym.flags.weak = false;
 989    }
 990}
 991
 992pub fn resolveGroups(self: *Object, elf_file: *Elf, table: anytype) !void {
 993    for (self.groups.items, 0..) |*g, gi| {
 994        const signature = g.signature(elf_file);
 995        const gop = try table.getOrPut(signature);
 996        if (!gop.found_existing) {
 997            gop.value_ptr.* = .{ .index = @intCast(gi), .file = self.index };
 998            continue;
 999        }
1000        const current = elf_file.group(gop.value_ptr.*);
1001        g.alive = false;
1002        if (self.index < current.file_index) {
1003            current.alive = false;
1004            g.alive = true;
1005            gop.value_ptr.* = .{ .index = @intCast(gi), .file = self.index };
1006        }
1007    }
1008}
1009
1010pub fn markGroupsDead(self: *Object, elf_file: *Elf) void {
1011    for (self.groups.items) |g| {
1012        if (g.alive) continue;
1013        for (g.members(elf_file)) |shndx| {
1014            const atom_index = self.atoms_indexes.items[shndx];
1015            if (self.atom(atom_index)) |atom_ptr| {
1016                atom_ptr.alive = false;
1017                atom_ptr.markFdesDead(self);
1018            }
1019        }
1020    }
1021}
1022
1023pub fn initOutputSections(self: *Object, elf_file: *Elf) !void {
1024    for (self.atoms_indexes.items) |atom_index| {
1025        const atom_ptr = self.atom(atom_index) orelse continue;
1026        if (!atom_ptr.alive) continue;
1027        const shdr = atom_ptr.inputShdr(elf_file);
1028        const osec = try elf_file.initOutputSection(.{
1029            .name = self.getString(shdr.sh_name),
1030            .flags = shdr.sh_flags,
1031            .type = shdr.sh_type,
1032        });
1033        const atom_list = &elf_file.sections.items(.atom_list_2)[osec];
1034        atom_list.output_section_index = osec;
1035        _ = try atom_list.atoms.getOrPut(elf_file.base.comp.gpa, atom_ptr.ref());
1036    }
1037}
1038
1039pub fn initRelaSections(self: *Object, elf_file: *Elf) !void {
1040    for (self.atoms_indexes.items) |atom_index| {
1041        const atom_ptr = self.atom(atom_index) orelse continue;
1042        if (!atom_ptr.alive) continue;
1043        if (atom_ptr.output_section_index == elf_file.section_indexes.eh_frame) continue;
1044        const shndx = atom_ptr.relocsShndx() orelse continue;
1045        const shdr = self.shdrs.items[shndx];
1046        const out_shndx = try elf_file.initOutputSection(.{
1047            .name = self.getString(shdr.sh_name),
1048            .flags = shdr.sh_flags,
1049            .type = shdr.sh_type,
1050        });
1051        const out_shdr = &elf_file.sections.items(.shdr)[out_shndx];
1052        out_shdr.sh_type = elf.SHT_RELA;
1053        out_shdr.sh_addralign = @alignOf(elf.Elf64_Rela);
1054        out_shdr.sh_entsize = @sizeOf(elf.Elf64_Rela);
1055        out_shdr.sh_flags |= elf.SHF_INFO_LINK;
1056    }
1057}
1058
1059pub fn addAtomsToRelaSections(self: *Object, elf_file: *Elf) !void {
1060    for (self.atoms_indexes.items) |atom_index| {
1061        const atom_ptr = self.atom(atom_index) orelse continue;
1062        if (!atom_ptr.alive) continue;
1063        if (atom_ptr.output_section_index == elf_file.section_indexes.eh_frame) continue;
1064        const shndx = blk: {
1065            const shndx = atom_ptr.relocsShndx() orelse continue;
1066            const shdr = self.shdrs.items[shndx];
1067            break :blk elf_file.initOutputSection(.{
1068                .name = self.getString(shdr.sh_name),
1069                .flags = shdr.sh_flags,
1070                .type = shdr.sh_type,
1071            }) catch unreachable;
1072        };
1073        const slice = elf_file.sections.slice();
1074        const shdr = &slice.items(.shdr)[shndx];
1075        shdr.sh_info = atom_ptr.output_section_index;
1076        shdr.sh_link = elf_file.section_indexes.symtab.?;
1077        const gpa = elf_file.base.comp.gpa;
1078        const atom_list = &elf_file.sections.items(.atom_list)[shndx];
1079        try atom_list.append(gpa, .{ .index = atom_index, .file = self.index });
1080    }
1081}
1082
1083pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) !void {
1084    const comp = elf_file.base.comp;
1085    const gpa = comp.gpa;
1086    const start = self.first_global orelse self.symtab.items.len;
1087
1088    try ar_symtab.symtab.ensureUnusedCapacity(gpa, self.symtab.items.len - start);
1089
1090    for (self.symtab.items[start..]) |sym| {
1091        if (sym.st_shndx == elf.SHN_UNDEF) continue;
1092        const off = try ar_symtab.strtab.insert(gpa, self.getString(sym.st_name));
1093        ar_symtab.symtab.appendAssumeCapacity(.{ .off = off, .file_index = self.index });
1094    }
1095}
1096
1097pub fn updateArSize(self: *Object, elf_file: *Elf) !void {
1098    self.output_ar_state.size = if (self.archive) |ar| ar.size else size: {
1099        const handle = elf_file.fileHandle(self.file_handle);
1100        break :size (try handle.stat()).size;
1101    };
1102}
1103
1104pub fn writeAr(self: Object, elf_file: *Elf, writer: anytype) !void {
1105    const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow;
1106    const offset: u64 = if (self.archive) |ar| ar.offset else 0;
1107    const name = fs.path.basename(self.path.sub_path);
1108    const hdr = Archive.setArHdr(.{
1109        .name = if (name.len <= Archive.max_member_name_len)
1110            .{ .name = name }
1111        else
1112            .{ .name_off = self.output_ar_state.name_off },
1113        .size = size,
1114    });
1115    try writer.writeAll(mem.asBytes(&hdr));
1116    const handle = elf_file.fileHandle(self.file_handle);
1117    const gpa = elf_file.base.comp.gpa;
1118    const data = try gpa.alloc(u8, size);
1119    defer gpa.free(data);
1120    const amt = try handle.preadAll(data, offset);
1121    if (amt != size) return error.InputOutput;
1122    try writer.writeAll(data);
1123}
1124
1125pub fn updateSymtabSize(self: *Object, elf_file: *Elf) void {
1126    const isAlive = struct {
1127        fn isAlive(sym: *const Symbol, ctx: *Elf) bool {
1128            if (sym.mergeSubsection(ctx)) |msub| return msub.alive;
1129            if (sym.atom(ctx)) |atom_ptr| return atom_ptr.alive;
1130            return true;
1131        }
1132    }.isAlive;
1133
1134    for (self.locals()) |*local| {
1135        if (!isAlive(local, elf_file)) continue;
1136        const esym = local.elfSym(elf_file);
1137        switch (esym.st_type()) {
1138            elf.STT_SECTION => continue,
1139            elf.STT_NOTYPE => if (esym.st_shndx == elf.SHN_UNDEF) continue,
1140            else => {},
1141        }
1142        local.flags.output_symtab = true;
1143        local.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file);
1144        self.output_symtab_ctx.nlocals += 1;
1145        self.output_symtab_ctx.strsize += @as(u32, @intCast(local.name(elf_file).len)) + 1;
1146    }
1147
1148    for (self.globals(), self.symbols_resolver.items) |*global, resolv| {
1149        const ref = elf_file.resolver.values.items[resolv - 1];
1150        const ref_sym = elf_file.symbol(ref) orelse continue;
1151        if (ref_sym.file(elf_file).?.index() != self.index) continue;
1152        if (!isAlive(global, elf_file)) continue;
1153        global.flags.output_symtab = true;
1154        if (global.isLocal(elf_file)) {
1155            global.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file);
1156            self.output_symtab_ctx.nlocals += 1;
1157        } else {
1158            global.addExtra(.{ .symtab = self.output_symtab_ctx.nglobals }, elf_file);
1159            self.output_symtab_ctx.nglobals += 1;
1160        }
1161        self.output_symtab_ctx.strsize += @as(u32, @intCast(global.name(elf_file).len)) + 1;
1162    }
1163}
1164
1165pub fn writeSymtab(self: *Object, elf_file: *Elf) void {
1166    for (self.locals()) |local| {
1167        const idx = local.outputSymtabIndex(elf_file) orelse continue;
1168        const out_sym = &elf_file.symtab.items[idx];
1169        out_sym.st_name = @intCast(elf_file.strtab.items.len);
1170        elf_file.strtab.appendSliceAssumeCapacity(local.name(elf_file));
1171        elf_file.strtab.appendAssumeCapacity(0);
1172        local.setOutputSym(elf_file, out_sym);
1173    }
1174
1175    for (self.globals(), self.symbols_resolver.items) |global, resolv| {
1176        const ref = elf_file.resolver.values.items[resolv - 1];
1177        const ref_sym = elf_file.symbol(ref) orelse continue;
1178        if (ref_sym.file(elf_file).?.index() != self.index) continue;
1179        const idx = global.outputSymtabIndex(elf_file) orelse continue;
1180        const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
1181        elf_file.strtab.appendSliceAssumeCapacity(global.name(elf_file));
1182        elf_file.strtab.appendAssumeCapacity(0);
1183        const out_sym = &elf_file.symtab.items[idx];
1184        out_sym.st_name = st_name;
1185        global.setOutputSym(elf_file, out_sym);
1186    }
1187}
1188
1189/// Returns atom's code and optionally uncompresses data if required (for compressed sections).
1190/// Caller owns the memory.
1191pub fn codeDecompressAlloc(self: *Object, elf_file: *Elf, atom_index: Atom.Index) ![]u8 {
1192    const comp = elf_file.base.comp;
1193    const gpa = comp.gpa;
1194    const atom_ptr = self.atom(atom_index).?;
1195    const shdr = atom_ptr.inputShdr(elf_file);
1196    const handle = elf_file.fileHandle(self.file_handle);
1197    const data = try self.preadShdrContentsAlloc(gpa, handle, atom_ptr.input_section_index);
1198    defer if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) gpa.free(data);
1199
1200    if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) {
1201        const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*;
1202        switch (chdr.ch_type) {
1203            .ZLIB => {
1204                var stream: std.Io.Reader = .fixed(data[@sizeOf(elf.Elf64_Chdr)..]);
1205                var zlib_stream: std.compress.flate.Decompress = .init(&stream, .zlib, &.{});
1206                const size = std.math.cast(usize, chdr.ch_size) orelse return error.Overflow;
1207                var aw: std.Io.Writer.Allocating = .init(gpa);
1208                try aw.ensureUnusedCapacity(size);
1209                defer aw.deinit();
1210                _ = try zlib_stream.reader.streamRemaining(&aw.writer);
1211                return aw.toOwnedSlice();
1212            },
1213            else => @panic("TODO unhandled compression scheme"),
1214        }
1215    }
1216
1217    return data;
1218}
1219
1220fn locals(self: *Object) []Symbol {
1221    if (self.symbols.items.len == 0) return &[0]Symbol{};
1222    assert(self.symbols.items.len >= self.symtab.items.len);
1223    const end = self.first_global orelse self.symtab.items.len;
1224    return self.symbols.items[0..end];
1225}
1226
1227pub fn globals(self: *Object) []Symbol {
1228    if (self.symbols.items.len == 0) return &[0]Symbol{};
1229    assert(self.symbols.items.len >= self.symtab.items.len);
1230    const start = self.first_global orelse self.symtab.items.len;
1231    return self.symbols.items[start..self.symtab.items.len];
1232}
1233
1234pub fn resolveSymbol(self: Object, index: Symbol.Index, elf_file: *Elf) Elf.Ref {
1235    const start = self.first_global orelse self.symtab.items.len;
1236    const end = self.symtab.items.len;
1237    if (index < start or index >= end) return .{ .index = index, .file = self.index };
1238    const resolv = self.symbols_resolver.items[index - start];
1239    return elf_file.resolver.get(resolv).?;
1240}
1241
1242fn addSymbol(self: *Object, gpa: Allocator) !Symbol.Index {
1243    try self.symbols.ensureUnusedCapacity(gpa, 1);
1244    return self.addSymbolAssumeCapacity();
1245}
1246
1247fn addSymbolAssumeCapacity(self: *Object) Symbol.Index {
1248    const index: Symbol.Index = @intCast(self.symbols.items.len);
1249    self.symbols.appendAssumeCapacity(.{ .file_index = self.index });
1250    return index;
1251}
1252
1253pub fn addSymbolExtra(self: *Object, gpa: Allocator, extra: Symbol.Extra) !u32 {
1254    const fields = @typeInfo(Symbol.Extra).@"struct".fields;
1255    try self.symbols_extra.ensureUnusedCapacity(gpa, fields.len);
1256    return self.addSymbolExtraAssumeCapacity(extra);
1257}
1258
1259pub fn addSymbolExtraAssumeCapacity(self: *Object, extra: Symbol.Extra) u32 {
1260    const index = @as(u32, @intCast(self.symbols_extra.items.len));
1261    const fields = @typeInfo(Symbol.Extra).@"struct".fields;
1262    inline for (fields) |field| {
1263        self.symbols_extra.appendAssumeCapacity(switch (field.type) {
1264            u32 => @field(extra, field.name),
1265            else => @compileError("bad field type"),
1266        });
1267    }
1268    return index;
1269}
1270
1271pub fn symbolExtra(self: *Object, index: u32) Symbol.Extra {
1272    const fields = @typeInfo(Symbol.Extra).@"struct".fields;
1273    var i: usize = index;
1274    var result: Symbol.Extra = undefined;
1275    inline for (fields) |field| {
1276        @field(result, field.name) = switch (field.type) {
1277            u32 => self.symbols_extra.items[i],
1278            else => @compileError("bad field type"),
1279        };
1280        i += 1;
1281    }
1282    return result;
1283}
1284
1285pub fn setSymbolExtra(self: *Object, index: u32, extra: Symbol.Extra) void {
1286    const fields = @typeInfo(Symbol.Extra).@"struct".fields;
1287    inline for (fields, 0..) |field, i| {
1288        self.symbols_extra.items[index + i] = switch (field.type) {
1289            u32 => @field(extra, field.name),
1290            else => @compileError("bad field type"),
1291        };
1292    }
1293}
1294
1295pub fn asFile(self: *Object) File {
1296    return .{ .object = self };
1297}
1298
1299pub fn getString(self: Object, off: u32) [:0]const u8 {
1300    assert(off < self.strtab.items.len);
1301    return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);
1302}
1303
1304fn addString(self: *Object, gpa: Allocator, str: []const u8) !u32 {
1305    const off: u32 = @intCast(self.strtab.items.len);
1306    try self.strtab.ensureUnusedCapacity(gpa, str.len + 1);
1307    self.strtab.appendSliceAssumeCapacity(str);
1308    self.strtab.appendAssumeCapacity(0);
1309    return off;
1310}
1311
1312/// Caller owns the memory.
1313fn preadShdrContentsAlloc(self: Object, gpa: Allocator, handle: fs.File, index: u32) ![]u8 {
1314    assert(index < self.shdrs.items.len);
1315    const offset = if (self.archive) |ar| ar.offset else 0;
1316    const shdr = self.shdrs.items[index];
1317    const sh_offset = math.cast(u64, shdr.sh_offset) orelse return error.Overflow;
1318    const sh_size = math.cast(u64, shdr.sh_size) orelse return error.Overflow;
1319    return Elf.preadAllAlloc(gpa, handle, offset + sh_offset, sh_size);
1320}
1321
1322/// Caller owns the memory.
1323fn preadRelocsAlloc(self: Object, gpa: Allocator, handle: fs.File, shndx: u32) ![]align(1) const elf.Elf64_Rela {
1324    const raw = try self.preadShdrContentsAlloc(gpa, handle, shndx);
1325    const num = @divExact(raw.len, @sizeOf(elf.Elf64_Rela));
1326    return @as([*]align(1) const elf.Elf64_Rela, @ptrCast(raw.ptr))[0..num];
1327}
1328
1329const AddAtomArgs = struct {
1330    name: u32,
1331    shndx: u32,
1332    size: u64,
1333    alignment: Alignment,
1334};
1335
1336fn addAtom(self: *Object, gpa: Allocator, args: AddAtomArgs) !Atom.Index {
1337    try self.atoms.ensureUnusedCapacity(gpa, 1);
1338    try self.atoms_extra.ensureUnusedCapacity(gpa, @sizeOf(Atom.Extra));
1339    return self.addAtomAssumeCapacity(args);
1340}
1341
1342fn addAtomAssumeCapacity(self: *Object, args: AddAtomArgs) Atom.Index {
1343    const atom_index: Atom.Index = @intCast(self.atoms.items.len);
1344    const atom_ptr = self.atoms.addOneAssumeCapacity();
1345    atom_ptr.* = .{
1346        .atom_index = atom_index,
1347        .name_offset = args.name,
1348        .file_index = self.index,
1349        .input_section_index = args.shndx,
1350        .extra_index = self.addAtomExtraAssumeCapacity(.{}),
1351        .size = args.size,
1352        .alignment = args.alignment,
1353    };
1354    return atom_index;
1355}
1356
1357pub fn atom(self: *Object, atom_index: Atom.Index) ?*Atom {
1358    if (atom_index == 0) return null;
1359    assert(atom_index < self.atoms.items.len);
1360    return &self.atoms.items[atom_index];
1361}
1362
1363pub fn addAtomExtra(self: *Object, gpa: Allocator, extra: Atom.Extra) !u32 {
1364    const fields = @typeInfo(Atom.Extra).@"struct".fields;
1365    try self.atoms_extra.ensureUnusedCapacity(gpa, fields.len);
1366    return self.addAtomExtraAssumeCapacity(extra);
1367}
1368
1369pub fn addAtomExtraAssumeCapacity(self: *Object, extra: Atom.Extra) u32 {
1370    const index: u32 = @intCast(self.atoms_extra.items.len);
1371    const fields = @typeInfo(Atom.Extra).@"struct".fields;
1372    inline for (fields) |field| {
1373        self.atoms_extra.appendAssumeCapacity(switch (field.type) {
1374            u32 => @field(extra, field.name),
1375            else => @compileError("bad field type"),
1376        });
1377    }
1378    return index;
1379}
1380
1381pub fn atomExtra(self: *Object, index: u32) Atom.Extra {
1382    const fields = @typeInfo(Atom.Extra).@"struct".fields;
1383    var i: usize = index;
1384    var result: Atom.Extra = undefined;
1385    inline for (fields) |field| {
1386        @field(result, field.name) = switch (field.type) {
1387            u32 => self.atoms_extra.items[i],
1388            else => @compileError("bad field type"),
1389        };
1390        i += 1;
1391    }
1392    return result;
1393}
1394
1395pub fn setAtomExtra(self: *Object, index: u32, extra: Atom.Extra) void {
1396    const fields = @typeInfo(Atom.Extra).@"struct".fields;
1397    inline for (fields, 0..) |field, i| {
1398        self.atoms_extra.items[index + i] = switch (field.type) {
1399            u32 => @field(extra, field.name),
1400            else => @compileError("bad field type"),
1401        };
1402    }
1403}
1404
1405fn setAtomFields(o: *Object, atom_ptr: *Atom, opts: Atom.Extra.AsOptionals) void {
1406    assert(o.index == atom_ptr.file_index);
1407    var extras = o.atomExtra(atom_ptr.extra_index);
1408    inline for (@typeInfo(@TypeOf(opts)).@"struct".fields) |field| {
1409        if (@field(opts, field.name)) |x| @field(extras, field.name) = x;
1410    }
1411    o.setAtomExtra(atom_ptr.extra_index, extras);
1412}
1413
1414fn addInputMergeSection(self: *Object, gpa: Allocator) !Merge.InputSection.Index {
1415    const index: Merge.InputSection.Index = @intCast(self.input_merge_sections.items.len);
1416    const msec = try self.input_merge_sections.addOne(gpa);
1417    msec.* = .{};
1418    return index;
1419}
1420
1421fn inputMergeSection(self: *Object, index: Merge.InputSection.Index) ?*Merge.InputSection {
1422    if (index == 0) return null;
1423    return &self.input_merge_sections.items[index];
1424}
1425
1426fn addGroup(self: *Object, gpa: Allocator) !Elf.Group.Index {
1427    const index: Elf.Group.Index = @intCast(self.groups.items.len);
1428    _ = try self.groups.addOne(gpa);
1429    return index;
1430}
1431
1432pub fn group(self: *Object, index: Elf.Group.Index) *Elf.Group {
1433    assert(index < self.groups.items.len);
1434    return &self.groups.items[index];
1435}
1436
1437pub fn fmtSymtab(self: *Object, elf_file: *Elf) std.fmt.Alt(Format, Format.symtab) {
1438    return .{ .data = .{
1439        .object = self,
1440        .elf_file = elf_file,
1441    } };
1442}
1443
1444const Format = struct {
1445    object: *Object,
1446    elf_file: *Elf,
1447
1448    fn symtab(f: Format, writer: *std.Io.Writer) std.Io.Writer.Error!void {
1449        const object = f.object;
1450        const elf_file = f.elf_file;
1451        try writer.writeAll("  locals\n");
1452        for (object.locals()) |sym| {
1453            try writer.print("    {f}\n", .{sym.fmt(elf_file)});
1454        }
1455        try writer.writeAll("  globals\n");
1456        for (object.globals(), 0..) |sym, i| {
1457            const first_global = object.first_global.?;
1458            const ref = object.resolveSymbol(@intCast(i + first_global), elf_file);
1459            if (elf_file.symbol(ref)) |ref_sym| {
1460                try writer.print("    {f}\n", .{ref_sym.fmt(elf_file)});
1461            } else {
1462                try writer.print("    {s} : unclaimed\n", .{sym.name(elf_file)});
1463            }
1464        }
1465    }
1466
1467    fn atoms(f: Format, writer: *std.Io.Writer) std.Io.Writer.Error!void {
1468        const object = f.object;
1469        try writer.writeAll("  atoms\n");
1470        for (object.atoms_indexes.items) |atom_index| {
1471            const atom_ptr = object.atom(atom_index) orelse continue;
1472            try writer.print("    {f}\n", .{atom_ptr.fmt(f.elf_file)});
1473        }
1474    }
1475
1476    fn cies(f: Format, writer: *std.Io.Writer) std.Io.Writer.Error!void {
1477        const object = f.object;
1478        try writer.writeAll("  cies\n");
1479        for (object.cies.items, 0..) |cie, i| {
1480            try writer.print("    cie({d}) : {f}\n", .{ i, cie.fmt(f.elf_file) });
1481        }
1482    }
1483
1484    fn fdes(f: Format, writer: *std.Io.Writer) std.Io.Writer.Error!void {
1485        const object = f.object;
1486        try writer.writeAll("  fdes\n");
1487        for (object.fdes.items, 0..) |fde, i| {
1488            try writer.print("    fde({d}) : {f}\n", .{ i, fde.fmt(f.elf_file) });
1489        }
1490    }
1491
1492    fn groups(f: Format, writer: *std.Io.Writer) std.Io.Writer.Error!void {
1493        const object = f.object;
1494        const elf_file = f.elf_file;
1495        try writer.writeAll("  groups\n");
1496        for (object.groups.items, 0..) |g, g_index| {
1497            try writer.print("    {s}({d})", .{ if (g.is_comdat) "COMDAT" else "GROUP", g_index });
1498            if (!g.alive) try writer.writeAll(" : [*]");
1499            try writer.writeByte('\n');
1500            const g_members = g.members(elf_file);
1501            for (g_members) |shndx| {
1502                const atom_index = object.atoms_indexes.items[shndx];
1503                const atom_ptr = object.atom(atom_index) orelse continue;
1504                try writer.print("      atom({d}) : {s}\n", .{ atom_index, atom_ptr.name(elf_file) });
1505            }
1506        }
1507    }
1508};
1509
1510pub fn fmtAtoms(self: *Object, elf_file: *Elf) std.fmt.Alt(Format, Format.atoms) {
1511    return .{ .data = .{
1512        .object = self,
1513        .elf_file = elf_file,
1514    } };
1515}
1516
1517pub fn fmtCies(self: *Object, elf_file: *Elf) std.fmt.Alt(Format, Format.cies) {
1518    return .{ .data = .{
1519        .object = self,
1520        .elf_file = elf_file,
1521    } };
1522}
1523
1524pub fn fmtFdes(self: *Object, elf_file: *Elf) std.fmt.Alt(Format, Format.fdes) {
1525    return .{ .data = .{
1526        .object = self,
1527        .elf_file = elf_file,
1528    } };
1529}
1530
1531pub fn fmtGroups(self: *Object, elf_file: *Elf) std.fmt.Alt(Format, Format.groups) {
1532    return .{ .data = .{
1533        .object = self,
1534        .elf_file = elf_file,
1535    } };
1536}
1537
1538pub fn fmtPath(self: Object) std.fmt.Alt(Object, formatPath) {
1539    return .{ .data = self };
1540}
1541
1542fn formatPath(object: Object, writer: *std.Io.Writer) std.Io.Writer.Error!void {
1543    if (object.archive) |ar| {
1544        try writer.print("{f}({f})", .{ ar.path, object.path });
1545    } else {
1546        try writer.print("{f}", .{object.path});
1547    }
1548}
1549
1550const InArchive = struct {
1551    path: Path,
1552    offset: u64,
1553    size: u32,
1554};
1555
1556const Object = @This();
1557
1558const std = @import("std");
1559const assert = std.debug.assert;
1560const eh_frame = @import("eh_frame.zig");
1561const elf = std.elf;
1562const fs = std.fs;
1563const log = std.log.scoped(.link);
1564const math = std.math;
1565const mem = std.mem;
1566const Path = std.Build.Cache.Path;
1567const Allocator = std.mem.Allocator;
1568
1569const Diags = @import("../../link.zig").Diags;
1570const Archive = @import("Archive.zig");
1571const Atom = @import("Atom.zig");
1572const AtomList = @import("AtomList.zig");
1573const Cie = eh_frame.Cie;
1574const Elf = @import("../Elf.zig");
1575const Fde = eh_frame.Fde;
1576const File = @import("file.zig").File;
1577const Merge = @import("Merge.zig");
1578const Symbol = @import("Symbol.zig");
1579const Alignment = Atom.Alignment;
1580const riscv = @import("../riscv.zig");