master
  1pub const File = union(enum) {
  2    zig_object: *ZigObject,
  3    linker_defined: *LinkerDefined,
  4    object: *Object,
  5    shared_object: *SharedObject,
  6
  7    pub fn index(file: File) Index {
  8        return switch (file) {
  9            inline else => |x| x.index,
 10        };
 11    }
 12
 13    pub fn fmtPath(file: File) std.fmt.Alt(File, formatPath) {
 14        return .{ .data = file };
 15    }
 16
 17    fn formatPath(file: File, writer: *std.Io.Writer) std.Io.Writer.Error!void {
 18        switch (file) {
 19            .zig_object => |zo| try writer.writeAll(zo.basename),
 20            .linker_defined => try writer.writeAll("(linker defined)"),
 21            .object => |x| try writer.print("{f}", .{x.fmtPath()}),
 22            .shared_object => |x| try writer.print("{f}", .{@as(Path, x.path)}),
 23        }
 24    }
 25
 26    pub fn isAlive(file: File) bool {
 27        return switch (file) {
 28            .zig_object => true,
 29            .linker_defined => true,
 30            inline else => |x| x.alive,
 31        };
 32    }
 33
 34    /// Encodes symbol rank so that the following ordering applies:
 35    /// * strong defined
 36    /// * weak defined
 37    /// * strong in lib (dso/archive)
 38    /// * weak in lib (dso/archive)
 39    /// * common
 40    /// * common in lib (archive)
 41    /// * unclaimed
 42    pub fn symbolRank(file: File, sym: elf.Elf64_Sym, in_archive: bool) u32 {
 43        const base: u3 = blk: {
 44            if (sym.st_shndx == elf.SHN_COMMON) break :blk if (in_archive) 6 else 5;
 45            if (file == .shared_object or in_archive) break :blk switch (sym.st_bind()) {
 46                elf.STB_GLOBAL => 3,
 47                else => 4,
 48            };
 49            break :blk switch (sym.st_bind()) {
 50                elf.STB_GLOBAL => 1,
 51                else => 2,
 52            };
 53        };
 54        return (@as(u32, base) << 24) + file.index();
 55    }
 56
 57    pub fn resolveSymbols(file: File, elf_file: *Elf) !void {
 58        return switch (file) {
 59            inline else => |x| x.resolveSymbols(elf_file),
 60        };
 61    }
 62
 63    pub fn setAlive(file: File) void {
 64        switch (file) {
 65            .zig_object, .linker_defined => {},
 66            inline else => |x| x.alive = true,
 67        }
 68    }
 69
 70    pub fn markLive(file: File, elf_file: *Elf) void {
 71        switch (file) {
 72            .linker_defined => {},
 73            inline else => |x| x.markLive(elf_file),
 74        }
 75    }
 76
 77    pub fn scanRelocs(file: File, elf_file: *Elf, undefs: anytype) !void {
 78        switch (file) {
 79            .linker_defined, .shared_object => unreachable,
 80            inline else => |x| try x.scanRelocs(elf_file, undefs),
 81        }
 82    }
 83
 84    pub fn createSymbolIndirection(file: File, elf_file: *Elf) !void {
 85        const impl = struct {
 86            fn impl(sym: *Symbol, ref: Elf.Ref, ef: *Elf) !void {
 87                if (!sym.isLocal(ef) and !sym.flags.has_dynamic) {
 88                    log.debug("'{s}' is non-local", .{sym.name(ef)});
 89                    try ef.dynsym.addSymbol(ref, ef);
 90                }
 91                if (sym.flags.needs_got and !sym.flags.has_got) {
 92                    log.debug("'{s}' needs GOT", .{sym.name(ef)});
 93                    _ = try ef.got.addGotSymbol(ref, ef);
 94                }
 95                if (sym.flags.needs_plt) {
 96                    if (sym.flags.is_canonical and !sym.flags.has_plt) {
 97                        log.debug("'{s}' needs CPLT", .{sym.name(ef)});
 98                        sym.flags.@"export" = true;
 99                        try ef.plt.addSymbol(ref, ef);
100                    } else if (sym.flags.needs_got and !sym.flags.has_pltgot) {
101                        log.debug("'{s}' needs PLTGOT", .{sym.name(ef)});
102                        try ef.plt_got.addSymbol(ref, ef);
103                    } else if (!sym.flags.has_plt) {
104                        log.debug("'{s}' needs PLT", .{sym.name(ef)});
105                        try ef.plt.addSymbol(ref, ef);
106                    }
107                }
108                if (sym.flags.needs_copy_rel and !sym.flags.has_copy_rel) {
109                    log.debug("'{s}' needs COPYREL", .{sym.name(ef)});
110                    try ef.copy_rel.addSymbol(ref, ef);
111                }
112                if (sym.flags.needs_tlsgd and !sym.flags.has_tlsgd) {
113                    log.debug("'{s}' needs TLSGD", .{sym.name(ef)});
114                    try ef.got.addTlsGdSymbol(ref, ef);
115                }
116                if (sym.flags.needs_gottp and !sym.flags.has_gottp) {
117                    log.debug("'{s}' needs GOTTP", .{sym.name(ef)});
118                    try ef.got.addGotTpSymbol(ref, ef);
119                }
120                if (sym.flags.needs_tlsdesc and !sym.flags.has_tlsdesc) {
121                    log.debug("'{s}' needs TLSDESC", .{sym.name(ef)});
122                    try ef.got.addTlsDescSymbol(ref, ef);
123                }
124            }
125        }.impl;
126
127        switch (file) {
128            .zig_object => |x| {
129                for (x.local_symbols.items, 0..) |idx, i| {
130                    const sym = &x.symbols.items[idx];
131                    const ref = x.resolveSymbol(@intCast(i), elf_file);
132                    const ref_sym = elf_file.symbol(ref) orelse continue;
133                    if (ref_sym.file(elf_file).?.index() != x.index) continue;
134                    try impl(sym, ref, elf_file);
135                }
136                for (x.global_symbols.items, 0..) |idx, i| {
137                    const sym = &x.symbols.items[idx];
138                    const ref = x.resolveSymbol(@intCast(i | ZigObject.global_symbol_bit), elf_file);
139                    const ref_sym = elf_file.symbol(ref) orelse continue;
140                    if (ref_sym.file(elf_file).?.index() != x.index) continue;
141                    try impl(sym, ref, elf_file);
142                }
143            },
144            inline else => |x| {
145                for (x.symbols.items, 0..) |*sym, i| {
146                    const ref = x.resolveSymbol(@intCast(i), elf_file);
147                    const ref_sym = elf_file.symbol(ref) orelse continue;
148                    if (ref_sym.file(elf_file).?.index() != x.index) continue;
149                    try impl(sym, ref, elf_file);
150                }
151            },
152        }
153    }
154
155    pub fn atom(file: File, atom_index: Atom.Index) ?*Atom {
156        return switch (file) {
157            .shared_object => unreachable,
158            .linker_defined => null,
159            inline else => |x| x.atom(atom_index),
160        };
161    }
162
163    pub fn atoms(file: File) []const Atom.Index {
164        return switch (file) {
165            .shared_object => unreachable,
166            .linker_defined => &[0]Atom.Index{},
167            .zig_object => |x| x.atoms_indexes.items,
168            .object => |x| x.atoms_indexes.items,
169        };
170    }
171
172    pub fn atomExtra(file: File, extra_index: u32) Atom.Extra {
173        return switch (file) {
174            .shared_object, .linker_defined => unreachable,
175            inline else => |x| x.atomExtra(extra_index),
176        };
177    }
178
179    pub fn setAtomExtra(file: File, extra_index: u32, extra: Atom.Extra) void {
180        return switch (file) {
181            .shared_object, .linker_defined => unreachable,
182            inline else => |x| x.setAtomExtra(extra_index, extra),
183        };
184    }
185
186    pub fn cies(file: File) []const Cie {
187        return switch (file) {
188            .zig_object => &[0]Cie{},
189            .object => |x| x.cies.items,
190            inline else => unreachable,
191        };
192    }
193
194    pub fn group(file: File, ind: Elf.Group.Index) *Elf.Group {
195        return switch (file) {
196            .linker_defined, .shared_object, .zig_object => unreachable,
197            .object => |x| x.group(ind),
198        };
199    }
200
201    pub fn resolveSymbol(file: File, ind: Symbol.Index, elf_file: *Elf) Elf.Ref {
202        return switch (file) {
203            inline else => |x| x.resolveSymbol(ind, elf_file),
204        };
205    }
206
207    pub fn symbol(file: File, ind: Symbol.Index) *Symbol {
208        return switch (file) {
209            .zig_object => |x| x.symbol(ind),
210            inline else => |x| &x.symbols.items[ind],
211        };
212    }
213
214    pub fn getString(file: File, off: u32) [:0]const u8 {
215        return switch (file) {
216            inline else => |x| x.getString(off),
217        };
218    }
219
220    pub fn updateSymtabSize(file: File, elf_file: *Elf) !void {
221        return switch (file) {
222            inline else => |x| x.updateSymtabSize(elf_file),
223        };
224    }
225
226    pub fn writeSymtab(file: File, elf_file: *Elf) void {
227        return switch (file) {
228            inline else => |x| x.writeSymtab(elf_file),
229        };
230    }
231
232    pub fn updateArSymtab(file: File, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) !void {
233        return switch (file) {
234            .zig_object => |x| x.updateArSymtab(ar_symtab, elf_file),
235            .object => |x| x.updateArSymtab(ar_symtab, elf_file),
236            else => unreachable,
237        };
238    }
239
240    pub fn updateArStrtab(file: File, allocator: Allocator, ar_strtab: *Archive.ArStrtab) !void {
241        switch (file) {
242            .zig_object => |zo| {
243                const basename = zo.basename;
244                if (basename.len <= Archive.max_member_name_len) return;
245                zo.output_ar_state.name_off = try ar_strtab.insert(allocator, basename);
246            },
247            .object => |o| {
248                const basename = std.fs.path.basename(o.path.sub_path);
249                if (basename.len <= Archive.max_member_name_len) return;
250                o.output_ar_state.name_off = try ar_strtab.insert(allocator, basename);
251            },
252            else => unreachable,
253        }
254    }
255
256    pub fn updateArSize(file: File, elf_file: *Elf) !void {
257        return switch (file) {
258            .zig_object => |x| x.updateArSize(),
259            .object => |x| x.updateArSize(elf_file),
260            else => unreachable,
261        };
262    }
263
264    pub fn writeAr(file: File, elf_file: *Elf, writer: anytype) !void {
265        return switch (file) {
266            .zig_object => |x| x.writeAr(writer),
267            .object => |x| x.writeAr(elf_file, writer),
268            else => unreachable,
269        };
270    }
271
272    pub const Index = u32;
273
274    pub const Entry = union(enum) {
275        null,
276        zig_object,
277        linker_defined: LinkerDefined,
278        object: Object,
279        shared_object: SharedObject,
280    };
281
282    pub const Handle = std.fs.File;
283    pub const HandleIndex = Index;
284};
285
286const std = @import("std");
287const elf = std.elf;
288const log = std.log.scoped(.link);
289const Path = std.Build.Cache.Path;
290const Allocator = std.mem.Allocator;
291
292const Archive = @import("Archive.zig");
293const Atom = @import("Atom.zig");
294const Cie = @import("eh_frame.zig").Cie;
295const Elf = @import("../Elf.zig");
296const LinkerDefined = @import("LinkerDefined.zig");
297const Object = @import("Object.zig");
298const SharedObject = @import("SharedObject.zig");
299const Symbol = @import("Symbol.zig");
300const ZigObject = @import("ZigObject.zig");