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