master
  1index: File.Index,
  2
  3symtab: std.ArrayList(elf.Elf64_Sym) = .empty,
  4strtab: std.ArrayList(u8) = .empty,
  5
  6symbols: std.ArrayList(Symbol) = .empty,
  7symbols_extra: std.ArrayList(u32) = .empty,
  8symbols_resolver: std.ArrayList(Elf.SymbolResolver.Index) = .empty,
  9
 10entry_index: ?Symbol.Index = null,
 11dynamic_index: ?Symbol.Index = null,
 12ehdr_start_index: ?Symbol.Index = null,
 13init_array_start_index: ?Symbol.Index = null,
 14init_array_end_index: ?Symbol.Index = null,
 15fini_array_start_index: ?Symbol.Index = null,
 16fini_array_end_index: ?Symbol.Index = null,
 17preinit_array_start_index: ?Symbol.Index = null,
 18preinit_array_end_index: ?Symbol.Index = null,
 19got_index: ?Symbol.Index = null,
 20plt_index: ?Symbol.Index = null,
 21end_index: ?Symbol.Index = null,
 22gnu_eh_frame_hdr_index: ?Symbol.Index = null,
 23dso_handle_index: ?Symbol.Index = null,
 24rela_iplt_start_index: ?Symbol.Index = null,
 25rela_iplt_end_index: ?Symbol.Index = null,
 26global_pointer_index: ?Symbol.Index = null,
 27start_stop_indexes: std.ArrayList(u32) = .empty,
 28
 29output_symtab_ctx: Elf.SymtabCtx = .{},
 30
 31pub fn deinit(self: *LinkerDefined, allocator: Allocator) void {
 32    self.symtab.deinit(allocator);
 33    self.strtab.deinit(allocator);
 34    self.symbols.deinit(allocator);
 35    self.symbols_extra.deinit(allocator);
 36    self.symbols_resolver.deinit(allocator);
 37    self.start_stop_indexes.deinit(allocator);
 38}
 39
 40pub fn init(self: *LinkerDefined, allocator: Allocator) !void {
 41    // Null byte in strtab
 42    try self.strtab.append(allocator, 0);
 43}
 44
 45fn newSymbolAssumeCapacity(self: *LinkerDefined, name_off: u32, elf_file: *Elf) Symbol.Index {
 46    const esym_index: u32 = @intCast(self.symtab.items.len);
 47    const esym = self.symtab.addOneAssumeCapacity();
 48    esym.* = .{
 49        .st_name = name_off,
 50        .st_info = @as(u8, elf.STB_WEAK) << 4,
 51        .st_other = @intFromEnum(elf.STV.HIDDEN),
 52        .st_shndx = elf.SHN_ABS,
 53        .st_value = 0,
 54        .st_size = 0,
 55    };
 56    const index = self.addSymbolAssumeCapacity();
 57    const symbol = &self.symbols.items[index];
 58    symbol.name_offset = name_off;
 59    symbol.extra_index = self.addSymbolExtraAssumeCapacity(.{});
 60    symbol.ref = .{ .index = 0, .file = 0 };
 61    symbol.esym_index = esym_index;
 62    symbol.version_index = elf_file.default_sym_version;
 63    return index;
 64}
 65
 66pub fn initSymbols(self: *LinkerDefined, elf_file: *Elf) !void {
 67    const gpa = elf_file.base.comp.gpa;
 68
 69    var nsyms: usize = 0;
 70    if (elf_file.entry_name) |_| {
 71        nsyms += 1; // entry
 72    }
 73    nsyms += 1; // _DYNAMIC
 74    nsyms += 1; // __ehdr_start
 75    nsyms += 1; // __init_array_start
 76    nsyms += 1; // __init_array_end
 77    nsyms += 1; // __fini_array_start
 78    nsyms += 1; // __fini_array_end
 79    nsyms += 1; // __preinit_array_start
 80    nsyms += 1; // __preinit_array_end
 81    nsyms += 1; // _GLOBAL_OFFSET_TABLE_
 82    nsyms += 1; // _PROCEDURE_LINKAGE_TABLE_
 83    nsyms += 1; // _end
 84    if (elf_file.base.comp.link_eh_frame_hdr) {
 85        nsyms += 1; // __GNU_EH_FRAME_HDR
 86    }
 87    nsyms += 1; // __dso_handle
 88    nsyms += 1; // __rela_iplt_start
 89    nsyms += 1; // __rela_iplt_end
 90    if (elf_file.getTarget().cpu.arch.isRISCV() and elf_file.isEffectivelyDynLib()) {
 91        nsyms += 1; // __global_pointer$
 92    }
 93
 94    try self.symtab.ensureTotalCapacityPrecise(gpa, nsyms);
 95    try self.symbols.ensureTotalCapacityPrecise(gpa, nsyms);
 96    try self.symbols_extra.ensureTotalCapacityPrecise(gpa, nsyms * @sizeOf(Symbol.Extra));
 97    try self.symbols_resolver.ensureTotalCapacityPrecise(gpa, nsyms);
 98    self.symbols_resolver.resize(gpa, nsyms) catch unreachable;
 99    @memset(self.symbols_resolver.items, 0);
100
101    if (elf_file.entry_name) |name| {
102        self.entry_index = self.newSymbolAssumeCapacity(try self.addString(gpa, name), elf_file);
103    }
104
105    self.dynamic_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "_DYNAMIC"), elf_file);
106    self.ehdr_start_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__ehdr_start"), elf_file);
107    self.init_array_start_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__init_array_start"), elf_file);
108    self.init_array_end_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__init_array_end"), elf_file);
109    self.fini_array_start_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__fini_array_start"), elf_file);
110    self.fini_array_end_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__fini_array_end"), elf_file);
111    self.preinit_array_start_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__preinit_array_start"), elf_file);
112    self.preinit_array_end_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__preinit_array_end"), elf_file);
113    self.got_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "_GLOBAL_OFFSET_TABLE_"), elf_file);
114    self.plt_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "_PROCEDURE_LINKAGE_TABLE_"), elf_file);
115    self.end_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "_end"), elf_file);
116
117    if (elf_file.base.comp.link_eh_frame_hdr) {
118        self.gnu_eh_frame_hdr_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__GNU_EH_FRAME_HDR"), elf_file);
119    }
120
121    self.dso_handle_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__dso_handle"), elf_file);
122    self.rela_iplt_start_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__rela_iplt_start"), elf_file);
123    self.rela_iplt_end_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__rela_iplt_end"), elf_file);
124
125    if (elf_file.getTarget().cpu.arch.isRISCV() and elf_file.isEffectivelyDynLib()) {
126        self.global_pointer_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__global_pointer$"), elf_file);
127    }
128}
129
130pub fn initStartStopSymbols(self: *LinkerDefined, elf_file: *Elf) !void {
131    const gpa = elf_file.base.comp.gpa;
132    const slice = elf_file.sections.slice();
133
134    var nsyms: usize = 0;
135    for (slice.items(.shdr)) |shdr| {
136        if (elf_file.getStartStopBasename(shdr)) |_| {
137            nsyms += 2; // __start_, __stop_
138        }
139    }
140
141    try self.start_stop_indexes.ensureTotalCapacityPrecise(gpa, nsyms);
142    try self.symtab.ensureUnusedCapacity(gpa, nsyms);
143    try self.symbols.ensureUnusedCapacity(gpa, nsyms);
144    try self.symbols_extra.ensureUnusedCapacity(gpa, nsyms * @sizeOf(Symbol.Extra));
145    try self.symbols_resolver.ensureUnusedCapacity(gpa, nsyms);
146
147    for (slice.items(.shdr)) |shdr| {
148        // TODO use getOrPut for incremental so that we don't create duplicates
149        if (elf_file.getStartStopBasename(shdr)) |name| {
150            const start_name = try std.fmt.allocPrintSentinel(gpa, "__start_{s}", .{name}, 0);
151            defer gpa.free(start_name);
152            const stop_name = try std.fmt.allocPrintSentinel(gpa, "__stop_{s}", .{name}, 0);
153            defer gpa.free(stop_name);
154
155            for (&[_][]const u8{ start_name, stop_name }) |nn| {
156                const index = self.newSymbolAssumeCapacity(try self.addString(gpa, nn), elf_file);
157                self.start_stop_indexes.appendAssumeCapacity(index);
158                const gop = try elf_file.resolver.getOrPut(gpa, .{
159                    .index = index,
160                    .file = self.index,
161                }, elf_file);
162                gop.ref.* = .{ .index = index, .file = self.index };
163                self.symbols_resolver.appendAssumeCapacity(gop.index);
164            }
165        }
166    }
167}
168
169pub fn resolveSymbols(self: *LinkerDefined, elf_file: *Elf) !void {
170    const gpa = elf_file.base.comp.gpa;
171
172    for (self.symtab.items, self.symbols_resolver.items, 0..) |esym, *resolv, i| {
173        const gop = try elf_file.resolver.getOrPut(gpa, .{
174            .index = @intCast(i),
175            .file = self.index,
176        }, elf_file);
177        if (!gop.found_existing) {
178            gop.ref.* = .{ .index = 0, .file = 0 };
179        }
180        resolv.* = gop.index;
181
182        if (esym.st_shndx == elf.SHN_UNDEF) continue;
183        if (elf_file.symbol(gop.ref.*) == null) {
184            gop.ref.* = .{ .index = @intCast(i), .file = self.index };
185            continue;
186        }
187
188        if (self.asFile().symbolRank(esym, false) < elf_file.symbol(gop.ref.*).?.symbolRank(elf_file)) {
189            gop.ref.* = .{ .index = @intCast(i), .file = self.index };
190        }
191    }
192}
193
194pub fn allocateSymbols(self: *LinkerDefined, elf_file: *Elf) void {
195    const comp = elf_file.base.comp;
196    const link_mode = comp.config.link_mode;
197    const shdrs = elf_file.sections.items(.shdr);
198
199    const allocSymbol = struct {
200        fn allocSymbol(ld: *LinkerDefined, index: Symbol.Index, value: u64, osec: u32, ef: *Elf) void {
201            const sym = ef.symbol(ld.resolveSymbol(index, ef)).?;
202            sym.value = @intCast(value);
203            sym.output_section_index = osec;
204        }
205    }.allocSymbol;
206
207    // _DYNAMIC
208    if (elf_file.section_indexes.dynamic) |shndx| {
209        const shdr = shdrs[shndx];
210        allocSymbol(self, self.dynamic_index.?, shdr.sh_addr, shndx, elf_file);
211    }
212
213    // __ehdr_start
214    allocSymbol(self, self.ehdr_start_index.?, elf_file.image_base, 1, elf_file);
215
216    // __init_array_start, __init_array_end
217    if (elf_file.sectionByName(".init_array")) |shndx| {
218        const shdr = shdrs[shndx];
219        allocSymbol(self, self.init_array_start_index.?, shdr.sh_addr, shndx, elf_file);
220        allocSymbol(self, self.init_array_end_index.?, shdr.sh_addr + shdr.sh_size, shndx, elf_file);
221    }
222
223    // __fini_array_start, __fini_array_end
224    if (elf_file.sectionByName(".fini_array")) |shndx| {
225        const shdr = shdrs[shndx];
226        allocSymbol(self, self.fini_array_start_index.?, shdr.sh_addr, shndx, elf_file);
227        allocSymbol(self, self.fini_array_end_index.?, shdr.sh_addr + shdr.sh_size, shndx, elf_file);
228    }
229
230    // __preinit_array_start, __preinit_array_end
231    if (elf_file.sectionByName(".preinit_array")) |shndx| {
232        const shdr = shdrs[shndx];
233        allocSymbol(self, self.preinit_array_start_index.?, shdr.sh_addr, shndx, elf_file);
234        allocSymbol(self, self.preinit_array_end_index.?, shdr.sh_addr + shdr.sh_size, shndx, elf_file);
235    }
236
237    // _GLOBAL_OFFSET_TABLE_
238    if (elf_file.getTarget().cpu.arch == .x86_64) {
239        if (elf_file.section_indexes.got_plt) |shndx| {
240            const shdr = shdrs[shndx];
241            allocSymbol(self, self.got_index.?, shdr.sh_addr, shndx, elf_file);
242        }
243    } else {
244        if (elf_file.section_indexes.got) |shndx| {
245            const shdr = shdrs[shndx];
246            allocSymbol(self, self.got_index.?, shdr.sh_addr, shndx, elf_file);
247        }
248    }
249
250    // _PROCEDURE_LINKAGE_TABLE_
251    if (elf_file.section_indexes.plt) |shndx| {
252        const shdr = shdrs[shndx];
253        allocSymbol(self, self.plt_index.?, shdr.sh_addr, shndx, elf_file);
254    }
255
256    // __dso_handle
257    if (self.dso_handle_index) |index| {
258        if (self.resolveSymbol(index, elf_file).file == self.index) {
259            const shdr = shdrs[1];
260            allocSymbol(self, index, shdr.sh_addr, 0, elf_file);
261        }
262    }
263
264    // __GNU_EH_FRAME_HDR
265    if (elf_file.section_indexes.eh_frame_hdr) |shndx| {
266        const shdr = shdrs[shndx];
267        allocSymbol(self, self.gnu_eh_frame_hdr_index.?, shdr.sh_addr, shndx, elf_file);
268    }
269
270    // __rela_iplt_start, __rela_iplt_end
271    if (elf_file.section_indexes.rela_dyn) |shndx| blk: {
272        if (link_mode != .static or comp.config.pie) break :blk;
273        const shdr = shdrs[shndx];
274        const end_addr = shdr.sh_addr + shdr.sh_size;
275        const start_addr = end_addr - elf_file.calcNumIRelativeRelocs() * @sizeOf(elf.Elf64_Rela);
276        allocSymbol(self, self.rela_iplt_start_index.?, start_addr, shndx, elf_file);
277        allocSymbol(self, self.rela_iplt_end_index.?, end_addr, shndx, elf_file);
278    }
279
280    // _end
281    {
282        var value: u64 = 0;
283        var osec: u32 = 0;
284        for (shdrs, 0..) |shdr, shndx| {
285            if (shdr.sh_flags & elf.SHF_ALLOC != 0) {
286                value = shdr.sh_addr + shdr.sh_size;
287                osec = @intCast(shndx);
288            }
289        }
290        allocSymbol(self, self.end_index.?, value, osec, elf_file);
291    }
292
293    // __global_pointer$
294    if (self.global_pointer_index) |index| {
295        const value, const osec = if (elf_file.sectionByName(".sdata")) |shndx| .{
296            shdrs[shndx].sh_addr + 0x800,
297            shndx,
298        } else .{ 0, 0 };
299        allocSymbol(self, index, value, osec, elf_file);
300    }
301
302    // __start_*, __stop_*
303    {
304        var index: usize = 0;
305        while (index < self.start_stop_indexes.items.len) : (index += 2) {
306            const start_ref = self.resolveSymbol(self.start_stop_indexes.items[index], elf_file);
307            const start = elf_file.symbol(start_ref).?;
308            const name = start.name(elf_file);
309            const stop_ref = self.resolveSymbol(self.start_stop_indexes.items[index + 1], elf_file);
310            const stop = elf_file.symbol(stop_ref).?;
311            const shndx = elf_file.sectionByName(name["__start_".len..]).?;
312            const shdr = shdrs[shndx];
313            start.value = @intCast(shdr.sh_addr);
314            start.output_section_index = shndx;
315            stop.value = @intCast(shdr.sh_addr + shdr.sh_size);
316            stop.output_section_index = shndx;
317        }
318    }
319}
320
321pub fn updateSymtabSize(self: *LinkerDefined, elf_file: *Elf) void {
322    for (self.symbols.items, self.symbols_resolver.items) |*global, resolv| {
323        const ref = elf_file.resolver.get(resolv).?;
324        const ref_sym = elf_file.symbol(ref) orelse continue;
325        if (ref_sym.file(elf_file).?.index() != self.index) continue;
326        global.flags.output_symtab = true;
327        if (global.isLocal(elf_file)) {
328            global.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file);
329            self.output_symtab_ctx.nlocals += 1;
330        } else {
331            global.addExtra(.{ .symtab = self.output_symtab_ctx.nglobals }, elf_file);
332            self.output_symtab_ctx.nglobals += 1;
333        }
334        self.output_symtab_ctx.strsize += @as(u32, @intCast(global.name(elf_file).len)) + 1;
335    }
336}
337
338pub fn writeSymtab(self: *LinkerDefined, elf_file: *Elf) void {
339    for (self.symbols.items, self.symbols_resolver.items) |global, resolv| {
340        const ref = elf_file.resolver.get(resolv).?;
341        const ref_sym = elf_file.symbol(ref) orelse continue;
342        if (ref_sym.file(elf_file).?.index() != self.index) continue;
343        const idx = global.outputSymtabIndex(elf_file) orelse continue;
344        const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
345        elf_file.strtab.appendSliceAssumeCapacity(global.name(elf_file));
346        elf_file.strtab.appendAssumeCapacity(0);
347        const out_sym = &elf_file.symtab.items[idx];
348        out_sym.st_name = st_name;
349        global.setOutputSym(elf_file, out_sym);
350    }
351}
352
353pub fn dynamicSymbol(self: LinkerDefined, elf_file: *Elf) ?*Symbol {
354    const index = self.dynamic_index orelse return null;
355    const resolv = self.resolveSymbol(index, elf_file);
356    return elf_file.symbol(resolv);
357}
358
359pub fn entrySymbol(self: LinkerDefined, elf_file: *Elf) ?*Symbol {
360    const index = self.entry_index orelse return null;
361    const resolv = self.resolveSymbol(index, elf_file);
362    return elf_file.symbol(resolv);
363}
364
365pub fn asFile(self: *LinkerDefined) File {
366    return .{ .linker_defined = self };
367}
368
369fn addString(self: *LinkerDefined, allocator: Allocator, str: []const u8) !u32 {
370    const off: u32 = @intCast(self.strtab.items.len);
371    try self.strtab.ensureUnusedCapacity(allocator, str.len + 1);
372    self.strtab.appendSliceAssumeCapacity(str);
373    self.strtab.appendAssumeCapacity(0);
374    return off;
375}
376
377pub fn getString(self: LinkerDefined, off: u32) [:0]const u8 {
378    assert(off < self.strtab.items.len);
379    return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);
380}
381
382pub fn resolveSymbol(self: LinkerDefined, index: Symbol.Index, elf_file: *Elf) Elf.Ref {
383    const resolv = self.symbols_resolver.items[index];
384    return elf_file.resolver.get(resolv).?;
385}
386
387fn addSymbol(self: *LinkerDefined, allocator: Allocator) !Symbol.Index {
388    try self.symbols.ensureUnusedCapacity(allocator, 1);
389    return self.addSymbolAssumeCapacity();
390}
391
392fn addSymbolAssumeCapacity(self: *LinkerDefined) Symbol.Index {
393    const index: Symbol.Index = @intCast(self.symbols.items.len);
394    self.symbols.appendAssumeCapacity(.{ .file_index = self.index });
395    return index;
396}
397
398pub fn addSymbolExtra(self: *LinkerDefined, allocator: Allocator, extra: Symbol.Extra) !u32 {
399    const fields = @typeInfo(Symbol.Extra).@"struct".fields;
400    try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len);
401    return self.addSymbolExtraAssumeCapacity(extra);
402}
403
404pub fn addSymbolExtraAssumeCapacity(self: *LinkerDefined, extra: Symbol.Extra) u32 {
405    const index = @as(u32, @intCast(self.symbols_extra.items.len));
406    const fields = @typeInfo(Symbol.Extra).@"struct".fields;
407    inline for (fields) |field| {
408        self.symbols_extra.appendAssumeCapacity(switch (field.type) {
409            u32 => @field(extra, field.name),
410            else => @compileError("bad field type"),
411        });
412    }
413    return index;
414}
415
416pub fn symbolExtra(self: *LinkerDefined, index: u32) Symbol.Extra {
417    const fields = @typeInfo(Symbol.Extra).@"struct".fields;
418    var i: usize = index;
419    var result: Symbol.Extra = undefined;
420    inline for (fields) |field| {
421        @field(result, field.name) = switch (field.type) {
422            u32 => self.symbols_extra.items[i],
423            else => @compileError("bad field type"),
424        };
425        i += 1;
426    }
427    return result;
428}
429
430pub fn setSymbolExtra(self: *LinkerDefined, index: u32, extra: Symbol.Extra) void {
431    const fields = @typeInfo(Symbol.Extra).@"struct".fields;
432    inline for (fields, 0..) |field, i| {
433        self.symbols_extra.items[index + i] = switch (field.type) {
434            u32 => @field(extra, field.name),
435            else => @compileError("bad field type"),
436        };
437    }
438}
439
440pub fn fmtSymtab(self: *LinkerDefined, elf_file: *Elf) std.fmt.Alt(Format, Format.symtab) {
441    return .{ .data = .{
442        .self = self,
443        .elf_file = elf_file,
444    } };
445}
446
447const Format = struct {
448    self: *LinkerDefined,
449    elf_file: *Elf,
450
451    fn symtab(ctx: Format, writer: *std.Io.Writer) std.Io.Writer.Error!void {
452        const self = ctx.self;
453        const elf_file = ctx.elf_file;
454        try writer.writeAll("  globals\n");
455        for (self.symbols.items, 0..) |sym, i| {
456            const ref = self.resolveSymbol(@intCast(i), elf_file);
457            if (elf_file.symbol(ref)) |ref_sym| {
458                try writer.print("    {f}\n", .{ref_sym.fmt(elf_file)});
459            } else {
460                try writer.print("    {s} : unclaimed\n", .{sym.name(elf_file)});
461            }
462        }
463    }
464};
465
466const assert = std.debug.assert;
467const elf = std.elf;
468const mem = std.mem;
469const std = @import("std");
470
471const Allocator = mem.Allocator;
472const Atom = @import("Atom.zig");
473const Elf = @import("../Elf.zig");
474const File = @import("file.zig").File;
475const LinkerDefined = @This();
476const Symbol = @import("Symbol.zig");