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");