authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-03 10:48:39+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-15 18:49:47+02:00
log989639efba0a7098819c3eb85130cb50413cbf7c
tree0fb8b5194194efe8a36baa5cc29c2e3ead7a6a50
parent980f2915fa15ab35029e8f3cab21d309811f6e30

zld: coalesce symbols on creation


3 files changed, 388 insertions(+), 610 deletions(-)

src/link/MachO/Object.zig+25-73
...@@ -45,9 +45,12 @@ dwarf_debug_str_index: ?u16 = null,...@@ -45,9 +45,12 @@ dwarf_debug_str_index: ?u16 = null,
45dwarf_debug_line_index: ?u16 = null,45dwarf_debug_line_index: ?u16 = null,
46dwarf_debug_ranges_index: ?u16 = null,46dwarf_debug_ranges_index: ?u16 = null,
4747
48symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
49strtab: std.ArrayListUnmanaged(u8) = .{},
50
48symbols: std.ArrayListUnmanaged(*Symbol) = .{},51symbols: std.ArrayListUnmanaged(*Symbol) = .{},
49stabs: std.ArrayListUnmanaged(*Symbol) = .{},52stabs: std.ArrayListUnmanaged(*Symbol) = .{},
50initializers: std.ArrayListUnmanaged(*Symbol) = .{},53initializers: std.ArrayListUnmanaged(u32) = .{},
51data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},54data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
5255
53pub const Section = struct {56pub const Section = struct {
...@@ -216,20 +219,13 @@ pub fn deinit(self: *Object) void {...@@ -216,20 +219,13 @@ pub fn deinit(self: *Object) void {
216 }219 }
217 self.sections.deinit(self.allocator);220 self.sections.deinit(self.allocator);
218221
219 for (self.symbols.items) |sym| {
220 sym.deinit(self.allocator);
221 self.allocator.destroy(sym);
222 }
223 self.symbols.deinit(self.allocator);222 self.symbols.deinit(self.allocator);
224
225 for (self.stabs.items) |stab| {
226 stab.deinit(self.allocator);
227 self.allocator.destroy(stab);
228 }
229 self.stabs.deinit(self.allocator);223 self.stabs.deinit(self.allocator);
230224
231 self.data_in_code_entries.deinit(self.allocator);225 self.data_in_code_entries.deinit(self.allocator);
232 self.initializers.deinit(self.allocator);226 self.initializers.deinit(self.allocator);
227 self.symtab.deinit(self.allocator);
228 self.strtab.deinit(self.allocator);
233229
234 if (self.name) |n| {230 if (self.name) |n| {
235 self.allocator.free(n);231 self.allocator.free(n);
...@@ -271,11 +267,10 @@ pub fn parse(self: *Object) !void {...@@ -271,11 +267,10 @@ pub fn parse(self: *Object) !void {
271 self.header = header;267 self.header = header;
272268
273 try self.readLoadCommands(reader);269 try self.readLoadCommands(reader);
274 try self.parseSymbols();
275 try self.parseSections();270 try self.parseSections();
271 try self.parseSymtab();
276 try self.parseDataInCode();272 try self.parseDataInCode();
277 try self.parseInitializers();273 try self.parseInitializers();
278 try self.parseDebugInfo();
279}274}
280275
281pub fn readLoadCommands(self: *Object, reader: anytype) !void {276pub fn readLoadCommands(self: *Object, reader: anytype) !void {
...@@ -394,14 +389,13 @@ pub fn parseInitializers(self: *Object) !void {...@@ -394,14 +389,13 @@ pub fn parseInitializers(self: *Object) !void {
394 const relocs = section.relocs orelse unreachable;389 const relocs = section.relocs orelse unreachable;
395 try self.initializers.ensureCapacity(self.allocator, relocs.len);390 try self.initializers.ensureCapacity(self.allocator, relocs.len);
396 for (relocs) |rel| {391 for (relocs) |rel| {
397 const sym = self.symbols.items[rel.target.symbol];392 self.initializers.appendAssumeCapacity(rel.target.symbol);
398 self.initializers.appendAssumeCapacity(sym);
399 }393 }
400394
401 mem.reverse(*Symbol, self.initializers.items);395 mem.reverse(u32, self.initializers.items);
402}396}
403397
404pub fn parseSymbols(self: *Object) !void {398fn parseSymtab(self: *Object) !void {
405 const index = self.symtab_cmd_index orelse return;399 const index = self.symtab_cmd_index orelse return;
406 const symtab_cmd = self.load_commands.items[index].Symtab;400 const symtab_cmd = self.load_commands.items[index].Symtab;
407401
...@@ -409,59 +403,12 @@ pub fn parseSymbols(self: *Object) !void {...@@ -409,59 +403,12 @@ pub fn parseSymbols(self: *Object) !void {
409 defer self.allocator.free(symtab);403 defer self.allocator.free(symtab);
410 _ = try self.file.?.preadAll(symtab, symtab_cmd.symoff);404 _ = try self.file.?.preadAll(symtab, symtab_cmd.symoff);
411 const slice = @alignCast(@alignOf(macho.nlist_64), mem.bytesAsSlice(macho.nlist_64, symtab));405 const slice = @alignCast(@alignOf(macho.nlist_64), mem.bytesAsSlice(macho.nlist_64, symtab));
406 try self.symtab.appendSlice(self.allocator, slice);
412407
413 var strtab = try self.allocator.alloc(u8, symtab_cmd.strsize);408 var strtab = try self.allocator.alloc(u8, symtab_cmd.strsize);
414 defer self.allocator.free(strtab);409 defer self.allocator.free(strtab);
415 _ = try self.file.?.preadAll(strtab, symtab_cmd.stroff);410 _ = try self.file.?.preadAll(strtab, symtab_cmd.stroff);
416411 try self.strtab.appendSlice(self.allocator, strtab);
417 for (slice) |sym| {
418 const sym_name = mem.spanZ(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx));
419
420 if (Symbol.isStab(sym)) {
421 log.err("unhandled symbol type: stab {s} in {s}", .{ sym_name, self.name.? });
422 return error.UnhandledSymbolType;
423 }
424 if (Symbol.isIndr(sym)) {
425 log.err("unhandled symbol type: indirect {s} in {s}", .{ sym_name, self.name.? });
426 return error.UnhandledSymbolType;
427 }
428 if (Symbol.isAbs(sym)) {
429 log.err("unhandled symbol type: absolute {s} in {s}", .{ sym_name, self.name.? });
430 return error.UnhandledSymbolType;
431 }
432
433 const name = try self.allocator.dupe(u8, sym_name);
434 const symbol: *Symbol = symbol: {
435 if (Symbol.isSect(sym)) {
436 const linkage: Symbol.Regular.Linkage = linkage: {
437 if (!Symbol.isExt(sym)) break :linkage .translation_unit;
438 if (Symbol.isWeakDef(sym) or Symbol.isPext(sym)) break :linkage .linkage_unit;
439 break :linkage .global;
440 };
441 break :symbol try Symbol.Regular.new(self.allocator, name, .{
442 .linkage = linkage,
443 .address = sym.n_value,
444 .section = sym.n_sect - 1,
445 .weak_ref = Symbol.isWeakRef(sym),
446 .file = self,
447 });
448 }
449
450 if (sym.n_value != 0) {
451 break :symbol try Symbol.Tentative.new(self.allocator, name, .{
452 .size = sym.n_value,
453 .alignment = (sym.n_desc >> 8) & 0x0f,
454 .file = self,
455 });
456 }
457
458 break :symbol try Symbol.Unresolved.new(self.allocator, name, .{
459 .file = self,
460 });
461 };
462
463 try self.symbols.append(self.allocator, symbol);
464 }
465}412}
466413
467pub fn parseDebugInfo(self: *Object) !void {414pub fn parseDebugInfo(self: *Object) !void {
...@@ -555,14 +502,6 @@ pub fn parseDebugInfo(self: *Object) !void {...@@ -555,14 +502,6 @@ pub fn parseDebugInfo(self: *Object) !void {
555 self.stabs.appendAssumeCapacity(delim_stab);502 self.stabs.appendAssumeCapacity(delim_stab);
556}503}
557504
558fn readSection(self: Object, allocator: *Allocator, index: u16) ![]u8 {
559 const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;
560 const sect = seg.sections.items[index];
561 var buffer = try allocator.alloc(u8, @intCast(usize, sect.size));
562 _ = try self.file.?.preadAll(buffer, sect.offset);
563 return buffer;
564}
565
566pub fn parseDataInCode(self: *Object) !void {505pub fn parseDataInCode(self: *Object) !void {
567 const index = self.data_in_code_cmd_index orelse return;506 const index = self.data_in_code_cmd_index orelse return;
568 const data_in_code = self.load_commands.items[index].LinkeditData;507 const data_in_code = self.load_commands.items[index].LinkeditData;
...@@ -582,3 +521,16 @@ pub fn parseDataInCode(self: *Object) !void {...@@ -582,3 +521,16 @@ pub fn parseDataInCode(self: *Object) !void {
582 try self.data_in_code_entries.append(self.allocator, dice);521 try self.data_in_code_entries.append(self.allocator, dice);
583 }522 }
584}523}
524
525fn readSection(self: Object, allocator: *Allocator, index: u16) ![]u8 {
526 const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;
527 const sect = seg.sections.items[index];
528 var buffer = try allocator.alloc(u8, @intCast(usize, sect.size));
529 _ = try self.file.?.preadAll(buffer, sect.offset);
530 return buffer;
531}
532
533pub fn getString(self: Object, off: u32) []const u8 {
534 assert(off < self.strtab.items.len);
535 return mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr + off));
536}
src/link/MachO/Symbol.zig+137-340
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1const Symbol = @This();1const Symbol = @This();
22
3const std = @import("std");3const std = @import("std");
4const assert = std.debug.assert;
4const macho = std.macho;5const macho = std.macho;
5const mem = std.mem;6const mem = std.mem;
67
...@@ -9,177 +10,32 @@ const Dylib = @import("Dylib.zig");...@@ -9,177 +10,32 @@ const Dylib = @import("Dylib.zig");
9const Object = @import("Object.zig");10const Object = @import("Object.zig");
10const StringTable = @import("StringTable.zig");11const StringTable = @import("StringTable.zig");
1112
12pub const Type = enum {
13 stab,
14 regular,
15 proxy,
16 unresolved,
17 tentative,
18};
19
20/// Symbol type.
21@"type": Type,
22
23/// Symbol name. Owned slice.13/// Symbol name. Owned slice.
24name: []const u8,14name: []const u8,
2515
26/// Alias of.
27alias: ?*Symbol = null,
28
29/// Index in GOT table for indirection.16/// Index in GOT table for indirection.
30got_index: ?u32 = null,17got_index: ?u32 = null,
3118
32/// Index in stubs table for late binding.19/// Index in stubs table for late binding.
33stubs_index: ?u32 = null,20stubs_index: ?u32 = null,
3421
35pub const Stab = struct {22payload: union(enum) {
36 base: Symbol,23 regular: Regular,
3724 tentative: Tentative,
38 // Symbol kind: function, etc.25 proxy: Proxy,
39 kind: Kind,26 undef: Undefined,
4027
41 // Size of stab.28 pub fn format(self: @This(), comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
42 size: u64,29 return switch (self) {
4330 .regular => |p| p.format(fmt, options, writer),
44 // Base regular symbol for this stub if defined.31 .tentative => |p| p.format(fmt, options, writer),
45 symbol: ?*Symbol = null,32 .proxy => |p| p.format(fmt, options, writer),
4633 .undef => |p| p.format(fmt, options, writer),
47 // null means self-reference.
48 file: ?*Object = null,
49
50 pub const base_type: Symbol.Type = .stab;
51
52 pub const Kind = enum {
53 so,
54 oso,
55 function,
56 global,
57 static,
58 };
59
60 const Opts = struct {
61 kind: Kind = .so,
62 size: u64 = 0,
63 symbol: ?*Symbol = null,
64 file: ?*Object = null,
65 };
66
67 pub fn new(allocator: *Allocator, name: []const u8, opts: Opts) !*Symbol {
68 const stab = try allocator.create(Stab);
69 errdefer allocator.destroy(stab);
70
71 stab.* = .{
72 .base = .{
73 .@"type" = .stab,
74 .name = try allocator.dupe(u8, name),
75 },
76 .kind = opts.kind,
77 .size = opts.size,
78 .symbol = opts.symbol,
79 .file = opts.file,
80 };34 };
81
82 return &stab.base;
83 }35 }
8436},
85 pub fn asNlists(stab: *Stab, allocator: *Allocator, strtab: *StringTable) ![]macho.nlist_64 {
86 var out = std.ArrayList(macho.nlist_64).init(allocator);
87 defer out.deinit();
88 if (stab.kind == .so) {
89 try out.append(.{
90 .n_strx = try strtab.getOrPut(stab.base.name),
91 .n_type = macho.N_SO,
92 .n_sect = 0,
93 .n_desc = 0,
94 .n_value = 0,
95 });
96 } else if (stab.kind == .oso) {
97 const mtime = mtime: {
98 const object = stab.file orelse break :mtime 0;
99 break :mtime object.mtime orelse 0;
100 };
101 try out.append(.{
102 .n_strx = try strtab.getOrPut(stab.base.name),
103 .n_type = macho.N_OSO,
104 .n_sect = 0,
105 .n_desc = 1,
106 .n_value = mtime,
107 });
108 } else outer: {
109 const symbol = stab.symbol orelse unreachable;
110 const regular = symbol.getTopmostAlias().cast(Regular) orelse unreachable;
111 const is_match = blk: {
112 if (regular.file == null and stab.file == null) break :blk true;
113 if (regular.file) |f1| {
114 if (stab.file) |f2| {
115 if (f1 == f2) break :blk true;
116 }
117 }
118 break :blk false;
119 };
120 if (!is_match) break :outer;
121
122 switch (stab.kind) {
123 .function => {
124 try out.ensureUnusedCapacity(4);
125 out.appendAssumeCapacity(.{
126 .n_strx = 0,
127 .n_type = macho.N_BNSYM,
128 .n_sect = regular.section,
129 .n_desc = 0,
130 .n_value = regular.address,
131 });
132 out.appendAssumeCapacity(.{
133 .n_strx = try strtab.getOrPut(stab.base.name),
134 .n_type = macho.N_FUN,
135 .n_sect = regular.section,
136 .n_desc = 0,
137 .n_value = regular.address,
138 });
139 out.appendAssumeCapacity(.{
140 .n_strx = 0,
141 .n_type = macho.N_FUN,
142 .n_sect = 0,
143 .n_desc = 0,
144 .n_value = stab.size,
145 });
146 out.appendAssumeCapacity(.{
147 .n_strx = 0,
148 .n_type = macho.N_ENSYM,
149 .n_sect = regular.section,
150 .n_desc = 0,
151 .n_value = stab.size,
152 });
153 },
154 .global => {
155 try out.append(.{
156 .n_strx = try strtab.getOrPut(stab.base.name),
157 .n_type = macho.N_GSYM,
158 .n_sect = 0,
159 .n_desc = 0,
160 .n_value = 0,
161 });
162 },
163 .static => {
164 try out.append(.{
165 .n_strx = try strtab.getOrPut(stab.base.name),
166 .n_type = macho.N_STSYM,
167 .n_sect = regular.section,
168 .n_desc = 0,
169 .n_value = regular.address,
170 });
171 },
172 .so, .oso => unreachable,
173 }
174 }
175
176 return out.toOwnedSlice();
177 }
178};
17937
180pub const Regular = struct {38pub const Regular = struct {
181 base: Symbol,
182
183 /// Linkage type.39 /// Linkage type.
184 linkage: Linkage,40 linkage: Linkage,
18541
...@@ -196,77 +52,56 @@ pub const Regular = struct {...@@ -196,77 +52,56 @@ pub const Regular = struct {
196 /// null means self-reference.52 /// null means self-reference.
197 file: ?*Object = null,53 file: ?*Object = null,
19854
199 /// True if symbol was already committed into the final
200 /// symbol table.
201 visited: bool = false,
202
203 pub const base_type: Symbol.Type = .regular;
204
205 pub const Linkage = enum {55 pub const Linkage = enum {
206 translation_unit,56 translation_unit,
207 linkage_unit,57 linkage_unit,
208 global,58 global,
209 };59 };
21060
211 const Opts = struct {61 pub fn isTemp(regular: Regular) bool {
212 linkage: Linkage = .translation_unit,62 if (regular.linkage == .translation_unit) {
213 address: u64 = 0,63 return mem.startsWith(u8, regular.base.name, "l") or mem.startsWith(u8, regular.base.name, "L");
214 section: u8 = 0,64 }
215 weak_ref: bool = false,65 return false;
216 file: ?*Object = null,
217 };
218
219 pub fn new(allocator: *Allocator, name: []const u8, opts: Opts) !*Symbol {
220 const reg = try allocator.create(Regular);
221 errdefer allocator.destroy(reg);
222
223 reg.* = .{
224 .base = .{
225 .@"type" = .regular,
226 .name = try allocator.dupe(u8, name),
227 },
228 .linkage = opts.linkage,
229 .address = opts.address,
230 .section = opts.section,
231 .weak_ref = opts.weak_ref,
232 .file = opts.file,
233 };
234
235 return &reg.base;
236 }66 }
23767
238 pub fn asNlist(regular: *Regular, strtab: *StringTable) !macho.nlist_64 {68 pub fn format(self: Regular, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
239 const n_strx = try strtab.getOrPut(regular.base.name);69 try std.fmt.format(writer, "Regular {{ ", .{});
240 var nlist = macho.nlist_64{70 try std.fmt.format(writer, ".linkage = {s}, ", .{self.linkage});
241 .n_strx = n_strx,71 try std.fmt.format(writer, ".address = 0x{x}, ", .{self.address});
242 .n_type = macho.N_SECT,72 try std.fmt.format(writer, ".section = {}, ", .{self.section});
243 .n_sect = regular.section,73 if (self.weak_ref) {
244 .n_desc = 0,74 try std.fmt.format(writer, ".weak_ref, ", .{});
245 .n_value = regular.address,
246 };
247
248 if (regular.linkage != .translation_unit) {
249 nlist.n_type |= macho.N_EXT;
250 }75 }
251 if (regular.linkage == .linkage_unit) {76 if (self.file) |file| {
252 nlist.n_type |= macho.N_PEXT;77 try std.fmt.format(writer, ".file = {s}, ", .{file.name.?});
253 nlist.n_desc |= macho.N_WEAK_DEF;
254 }78 }
25579 try std.fmt.format(writer, "}}", .{});
256 return nlist;
257 }80 }
81};
25882
259 pub fn isTemp(regular: *Regular) bool {83pub const Tentative = struct {
260 if (regular.linkage == .translation_unit) {84 /// Symbol size.
261 return mem.startsWith(u8, regular.base.name, "l") or mem.startsWith(u8, regular.base.name, "L");85 size: u64,
86
87 /// Symbol alignment as power of two.
88 alignment: u16,
89
90 /// File where this symbol was referenced.
91 file: ?*Object = null,
92
93 pub fn format(self: Tentative, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
94 try std.fmt.format(writer, "Tentative {{ ", .{});
95 try std.fmt.format(writer, ".size = 0x{x}, ", .{self.size});
96 try std.fmt.format(writer, ".alignment = 0x{x}, ", .{self.alignment});
97 if (self.file) |file| {
98 try std.fmt.format(writer, ".file = {s}, ", .{file.name.?});
262 }99 }
263 return false;100 try std.fmt.format(writer, "}}", .{});
264 }101 }
265};102};
266103
267pub const Proxy = struct {104pub const Proxy = struct {
268 base: Symbol,
269
270 /// Dynamic binding info - spots within the final105 /// Dynamic binding info - spots within the final
271 /// executable where this proxy is referenced from.106 /// executable where this proxy is referenced from.
272 bind_info: std.ArrayListUnmanaged(struct {107 bind_info: std.ArrayListUnmanaged(struct {
...@@ -278,159 +113,121 @@ pub const Proxy = struct {...@@ -278,159 +113,121 @@ pub const Proxy = struct {
278 /// null means self-reference.113 /// null means self-reference.
279 file: ?*Dylib = null,114 file: ?*Dylib = null,
280115
281 pub const base_type: Symbol.Type = .proxy;
282
283 const Opts = struct {
284 file: ?*Dylib = null,
285 };
286
287 pub fn new(allocator: *Allocator, name: []const u8, opts: Opts) !*Symbol {
288 const proxy = try allocator.create(Proxy);
289 errdefer allocator.destroy(proxy);
290
291 proxy.* = .{
292 .base = .{
293 .@"type" = .proxy,
294 .name = try allocator.dupe(u8, name),
295 },
296 .file = opts.file,
297 };
298
299 return &proxy.base;
300 }
301
302 pub fn asNlist(proxy: *Proxy, strtab: *StringTable) !macho.nlist_64 {
303 const n_strx = try strtab.getOrPut(proxy.base.name);
304 return macho.nlist_64{
305 .n_strx = n_strx,
306 .n_type = macho.N_UNDF | macho.N_EXT,
307 .n_sect = 0,
308 .n_desc = (proxy.dylibOrdinal() * macho.N_SYMBOL_RESOLVER) | macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY,
309 .n_value = 0,
310 };
311 }
312
313 pub fn deinit(proxy: *Proxy, allocator: *Allocator) void {116 pub fn deinit(proxy: *Proxy, allocator: *Allocator) void {
314 proxy.bind_info.deinit(allocator);117 proxy.bind_info.deinit(allocator);
315 }118 }
316119
317 pub fn dylibOrdinal(proxy: *Proxy) u16 {120 pub fn dylibOrdinal(proxy: Proxy) u16 {
318 const dylib = proxy.file orelse return 0;121 const dylib = proxy.file orelse return 0;
319 return dylib.ordinal.?;122 return dylib.ordinal.?;
320 }123 }
321};
322124
323pub const Unresolved = struct {125 pub fn format(self: Proxy, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
324 base: Symbol,126 try std.fmt.format(writer, "Proxy {{ ", .{});
127 if (self.bind_info.items.len > 0) {
128 // TODO
129 try std.fmt.format(writer, ".bind_info = {}, ", .{self.bind_info.items.len});
130 }
131 if (self.file) |file| {
132 try std.fmt.format(writer, ".file = {s}, ", .{file.name.?});
133 }
134 try std.fmt.format(writer, "}}", .{});
135 }
136};
325137
138pub const Undefined = struct {
326 /// File where this symbol was referenced.139 /// File where this symbol was referenced.
327 /// null means synthetic, e.g., dyld_stub_binder.140 /// null means synthetic, e.g., dyld_stub_binder.
328 file: ?*Object = null,141 file: ?*Object = null,
329142
330 pub const base_type: Symbol.Type = .unresolved;143 pub fn format(self: Undefined, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
331144 try std.fmt.format(writer, "Undefined {{ ", .{});
332 const Opts = struct {145 if (self.file) |file| {
333 file: ?*Object = null,146 try std.fmt.format(writer, ".file = {s}, ", .{file.name.?});
334 };147 }
335148 try std.fmt.format(writer, "}}", .{});
336 pub fn new(allocator: *Allocator, name: []const u8, opts: Opts) !*Symbol {
337 const undef = try allocator.create(Unresolved);
338 errdefer allocator.destroy(undef);
339
340 undef.* = .{
341 .base = .{
342 .@"type" = .unresolved,
343 .name = try allocator.dupe(u8, name),
344 },
345 .file = opts.file,
346 };
347
348 return &undef.base;
349 }
350
351 pub fn asNlist(undef: *Unresolved, strtab: *StringTable) !macho.nlist_64 {
352 const n_strx = try strtab.getOrPut(undef.base.name);
353 return macho.nlist_64{
354 .n_strx = n_strx,
355 .n_type = macho.N_UNDF,
356 .n_sect = 0,
357 .n_desc = 0,
358 .n_value = 0,
359 };
360 }149 }
361};150};
362151
363pub const Tentative = struct {152/// Create new undefined symbol.
364 base: Symbol,153pub fn new(allocator: *Allocator, name: []const u8) !*Symbol {
154 const new_sym = try allocator.create(Symbol);
155 errdefer allocator.destroy(new_sym);
365156
366 /// Symbol size.157 new_sym.* = .{
367 size: u64,158 .name = try allocator.dupe(u8, name),
368159 .payload = .{
369 /// Symbol alignment as power of two.160 .undef = .{},
370 alignment: u16,161 },
371
372 /// File where this symbol was referenced.
373 file: ?*Object = null,
374
375 pub const base_type: Symbol.Type = .tentative;
376
377 const Opts = struct {
378 size: u64 = 0,
379 alignment: u16 = 0,
380 file: ?*Object = null,
381 };162 };
382163
383 pub fn new(allocator: *Allocator, name: []const u8, opts: Opts) !*Symbol {164 return new_sym;
384 const tent = try allocator.create(Tentative);165}
385 errdefer allocator.destroy(tent);
386
387 tent.* = .{
388 .base = .{
389 .@"type" = .tentative,
390 .name = try allocator.dupe(u8, name),
391 },
392 .size = opts.size,
393 .alignment = opts.alignment,
394 .file = opts.file,
395 };
396
397 return &tent.base;
398 }
399
400 pub fn asNlist(tent: *Tentative, strtab: *StringTable) !macho.nlist_64 {
401 // TODO
402 const n_strx = try strtab.getOrPut(tent.base.name);
403 return macho.nlist_64{
404 .n_strx = n_strx,
405 .n_type = macho.N_UNDF,
406 .n_sect = 0,
407 .n_desc = 0,
408 .n_value = 0,
409 };
410 }
411};
412166
413pub fn deinit(base: *Symbol, allocator: *Allocator) void {167pub fn asNlist(symbol: *Symbol, strtab: *StringTable) macho.nlist_64 {
414 allocator.free(base.name);168 const n_strx = try strtab.getOrPut(symbol.name);
169 const nlist = nlist: {
170 switch (symbol.payload) {
171 .regular => |regular| {
172 var nlist = macho.nlist_64{
173 .n_strx = n_strx,
174 .n_type = macho.N_SECT,
175 .n_sect = regular.section,
176 .n_desc = 0,
177 .n_value = regular.address,
178 };
179
180 if (regular.linkage != .translation_unit) {
181 nlist.n_type |= macho.N_EXT;
182 }
183 if (regular.linkage == .linkage_unit) {
184 nlist.n_type |= macho.N_PEXT;
185 nlist.n_desc |= macho.N_WEAK_DEF;
186 }
415187
416 switch (base.@"type") {188 break :nlist nlist;
417 .proxy => @fieldParentPtr(Proxy, "base", base).deinit(allocator),189 },
418 else => {},190 .tentative => |tentative| {
419 }191 // TODO
192 break :nlist macho.nlist_64{
193 .n_strx = n_strx,
194 .n_type = macho.N_UNDF,
195 .n_sect = 0,
196 .n_desc = 0,
197 .n_value = 0,
198 };
199 },
200 .proxy => |proxy| {
201 break :nlist macho.nlist_64{
202 .n_strx = n_strx,
203 .n_type = macho.N_UNDF | macho.N_EXT,
204 .n_sect = 0,
205 .n_desc = (proxy.dylibOrdinal() * macho.N_SYMBOL_RESOLVER) | macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY,
206 .n_value = 0,
207 };
208 },
209 .undef => |undef| {
210 // TODO
211 break :nlist macho.nlist_64{
212 .n_strx = n_strx,
213 .n_type = macho.N_UNDF,
214 .n_sect = 0,
215 .n_desc = 0,
216 .n_value = 0,
217 };
218 },
219 }
220 };
221 return nlist;
420}222}
421223
422pub fn cast(base: *Symbol, comptime T: type) ?*T {224pub fn deinit(symbol: *Symbol, allocator: *Allocator) void {
423 if (base.@"type" != T.base_type) {225 allocator.free(symbol.name);
424 return null;
425 }
426 return @fieldParentPtr(T, "base", base);
427}
428226
429pub fn getTopmostAlias(base: *Symbol) *Symbol {227 switch (symbol.payload) {
430 if (base.alias) |alias| {228 .proxy => |*proxy| proxy.deinit(allocator),
431 return alias.getTopmostAlias();229 else => {},
432 }230 }
433 return base;
434}231}
435232
436pub fn isStab(sym: macho.nlist_64) bool {233pub fn isStab(sym: macho.nlist_64) bool {
src/link/MachO/Zld.zig+226-197
...@@ -102,10 +102,9 @@ objc_selrefs_section_index: ?u16 = null,...@@ -102,10 +102,9 @@ objc_selrefs_section_index: ?u16 = null,
102objc_classrefs_section_index: ?u16 = null,102objc_classrefs_section_index: ?u16 = null,
103objc_data_section_index: ?u16 = null,103objc_data_section_index: ?u16 = null,
104104
105locals: std.ArrayListUnmanaged(*Symbol) = .{},
105globals: std.StringArrayHashMapUnmanaged(*Symbol) = .{},106globals: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
106imports: std.StringArrayHashMapUnmanaged(*Symbol) = .{},107imports: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
107unresolved: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
108tentatives: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
109108
110/// Offset into __DATA,__common section.109/// Offset into __DATA,__common section.
111/// Set if the linker found tentative definitions in any of the objects.110/// Set if the linker found tentative definitions in any of the objects.
...@@ -173,15 +172,24 @@ pub fn deinit(self: *Zld) void {...@@ -173,15 +172,24 @@ pub fn deinit(self: *Zld) void {
173 }172 }
174 self.dylibs.deinit(self.allocator);173 self.dylibs.deinit(self.allocator);
175174
176 for (self.imports.values()) |proxy| {175 for (self.imports.values()) |sym| {
177 proxy.deinit(self.allocator);176 sym.deinit(self.allocator);
178 self.allocator.destroy(proxy);177 self.allocator.destroy(sym);
179 }178 }
180 self.imports.deinit(self.allocator);179 self.imports.deinit(self.allocator);
181180
182 self.tentatives.deinit(self.allocator);181 for (self.globals.values()) |sym| {
182 sym.deinit(self.allocator);
183 self.allocator.destroy(sym);
184 }
183 self.globals.deinit(self.allocator);185 self.globals.deinit(self.allocator);
184 self.unresolved.deinit(self.allocator);186
187 for (self.locals.items) |sym| {
188 sym.deinit(self.allocator);
189 self.allocator.destroy(sym);
190 }
191 self.locals.deinit(self.allocator);
192
185 self.strtab.deinit();193 self.strtab.deinit();
186}194}
187195
...@@ -221,20 +229,21 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg...@@ -221,20 +229,21 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg
221 try self.parseInputFiles(files, args.syslibroot);229 try self.parseInputFiles(files, args.syslibroot);
222 try self.parseLibs(args.libs, args.syslibroot);230 try self.parseLibs(args.libs, args.syslibroot);
223 try self.resolveSymbols();231 try self.resolveSymbols();
224 try self.resolveStubsAndGotEntries();232 return error.TODO;
225 try self.updateMetadata();233 // try self.resolveStubsAndGotEntries();
226 try self.sortSections();234 // try self.updateMetadata();
227 try self.addRpaths(args.rpaths);235 // try self.sortSections();
228 try self.addDataInCodeLC();236 // try self.addRpaths(args.rpaths);
229 try self.addCodeSignatureLC();237 // try self.addDataInCodeLC();
230 try self.allocateTextSegment();238 // try self.addCodeSignatureLC();
231 try self.allocateDataConstSegment();239 // try self.allocateTextSegment();
232 try self.allocateDataSegment();240 // try self.allocateDataConstSegment();
233 self.allocateLinkeditSegment();241 // try self.allocateDataSegment();
234 try self.allocateSymbols();242 // self.allocateLinkeditSegment();
235 try self.allocateTentativeSymbols();243 // try self.allocateSymbols();
236 try self.allocateProxyBindAddresses();244 // try self.allocateTentativeSymbols();
237 try self.flush();245 // try self.allocateProxyBindAddresses();
246 // try self.flush();
238}247}
239248
240fn parseInputFiles(self: *Zld, files: []const []const u8, syslibroot: ?[]const u8) !void {249fn parseInputFiles(self: *Zld, files: []const []const u8, syslibroot: ?[]const u8) !void {
...@@ -1458,92 +1467,100 @@ fn writeStubInStubHelper(self: *Zld, index: u32) !void {...@@ -1458,92 +1467,100 @@ fn writeStubInStubHelper(self: *Zld, index: u32) !void {
1458fn resolveSymbolsInObject(self: *Zld, object: *Object) !void {1467fn resolveSymbolsInObject(self: *Zld, object: *Object) !void {
1459 log.debug("resolving symbols in '{s}'", .{object.name});1468 log.debug("resolving symbols in '{s}'", .{object.name});
14601469
1461 for (object.symbols.items) |sym| {1470 for (object.symtab.items) |sym| {
1462 if (sym.cast(Symbol.Regular)) |reg| {1471 const sym_name = object.getString(sym.n_strx);
1463 if (reg.linkage == .translation_unit) continue; // Symbol local to TU.
14641472
1465 if (self.tentatives.fetchSwapRemove(sym.name)) |kv| {1473 if (Symbol.isStab(sym)) {
1466 // Create link to the global.1474 log.err("unhandled symbol type: stab {s}", .{sym_name});
1467 kv.value.alias = sym;1475 log.err(" | first definition in {s}", .{object.name.?});
1468 }1476 return error.UnhandledSymbolType;
1469 if (self.unresolved.fetchSwapRemove(sym.name)) |kv| {1477 }
1470 // Create link to the global.
1471 kv.value.alias = sym;
1472 }
1473 const sym_ptr = self.globals.getPtr(sym.name) orelse {
1474 // Put new global symbol into the symbol table.
1475 try self.globals.putNoClobber(self.allocator, sym.name, sym);
1476 continue;
1477 };
1478 const g_sym = sym_ptr.*;
1479 const g_reg = g_sym.cast(Symbol.Regular) orelse unreachable;
1480
1481 switch (g_reg.linkage) {
1482 .translation_unit => unreachable,
1483 .linkage_unit => {
1484 if (reg.linkage == .linkage_unit) {
1485 // Create link to the first encountered linkage_unit symbol.
1486 sym.alias = g_sym;
1487 continue;
1488 }
1489 },
1490 .global => {
1491 if (reg.linkage == .global) {
1492 log.debug("symbol '{s}' defined multiple times", .{reg.base.name});
1493 return error.MultipleSymbolDefinitions;
1494 }
1495 sym.alias = g_sym;
1496 continue;
1497 },
1498 }
14991478
1500 g_sym.alias = sym;1479 if (Symbol.isIndr(sym)) {
1501 sym_ptr.* = sym;1480 log.err("unhandled symbol type: indirect {s}", .{sym_name});
1502 } else if (sym.cast(Symbol.Tentative)) |tent| {1481 log.err(" | first definition in {s}", .{object.name.?});
1503 if (self.globals.get(sym.name)) |g_sym| {1482 return error.UnhandledSymbolType;
1504 sym.alias = g_sym;1483 }
1505 continue;
1506 }
15071484
1508 if (self.unresolved.fetchSwapRemove(sym.name)) |kv| {1485 if (Symbol.isAbs(sym)) {
1509 kv.value.alias = sym;1486 log.err("unhandled symbol type: absolute {s}", .{sym_name});
1510 }1487 log.err(" | first definition in {s}", .{object.name.?});
1488 return error.UnhandledSymbolType;
1489 }
15111490
1512 const sym_ptr = self.tentatives.getPtr(sym.name) orelse {1491 if (Symbol.isSect(sym) and !Symbol.isExt(sym)) {
1513 // Put new tentative definition symbol into symbol table.1492 // Regular symbol local to translation unit
1514 try self.tentatives.putNoClobber(self.allocator, sym.name, sym);1493 const symbol = try Symbol.new(self.allocator, sym_name);
1515 continue;1494 symbol.payload = .{
1495 .regular = .{
1496 .linkage = .translation_unit,
1497 .address = sym.n_value,
1498 .section = sym.n_sect - 1,
1499 .weak_ref = Symbol.isWeakRef(sym),
1500 .file = object,
1501 },
1516 };1502 };
1503 try self.locals.append(self.allocator, symbol);
1504 try object.symbols.append(self.allocator, symbol);
1505 continue;
1506 }
15171507
1518 // Compare by size and pick the largest tentative definition.1508 const symbol = self.globals.get(sym_name) orelse symbol: {
1519 // We model this like a heap where the tentative definition with the1509 // Insert new global symbol.
1520 // largest size always washes up on top.1510 const symbol = try Symbol.new(self.allocator, sym_name);
1521 const t_sym = sym_ptr.*;1511 symbol.payload.undef.file = object;
1522 const t_tent = t_sym.cast(Symbol.Tentative) orelse unreachable;1512 try self.globals.putNoClobber(self.allocator, symbol.name, symbol);
1513 break :symbol symbol;
1514 };
15231515
1524 if (tent.size < t_tent.size) {1516 if (Symbol.isSect(sym)) {
1525 sym.alias = t_sym;1517 // Global symbol
1526 continue;1518 const linkage: Symbol.Regular.Linkage = if (Symbol.isWeakDef(sym) or Symbol.isPext(sym))
1519 .linkage_unit
1520 else
1521 .global;
1522
1523 const should_update = if (symbol.payload == .regular) blk: {
1524 if (symbol.payload.regular.linkage == .global and linkage == .global) {
1525 log.err("symbol '{s}' defined multiple times", .{sym_name});
1526 log.err(" | first definition in {s}", .{symbol.payload.regular.file.?.name.?});
1527 log.err(" | next definition in {s}", .{object.name.?});
1528 return error.MultipleSymbolDefinitions;
1529 }
1530 break :blk symbol.payload.regular.linkage != .global;
1531 } else true;
1532
1533 if (should_update) {
1534 symbol.payload = .{
1535 .regular = .{
1536 .linkage = linkage,
1537 .address = sym.n_value,
1538 .section = sym.n_sect - 1,
1539 .weak_ref = Symbol.isWeakRef(sym),
1540 .file = object,
1541 },
1542 };
1527 }1543 }
1544 } else if (sym.n_value != 0) {
1545 // Tentative definition
1546 const should_update = switch (symbol.payload) {
1547 .tentative => |tent| tent.size < sym.n_value,
1548 .undef => true,
1549 else => false,
1550 };
15281551
1529 t_sym.alias = sym;1552 if (should_update) {
1530 sym_ptr.* = sym;1553 symbol.payload = .{
1531 } else if (sym.cast(Symbol.Unresolved)) |_| {1554 .tentative = .{
1532 if (self.globals.get(sym.name)) |g_sym| {1555 .size = sym.n_value,
1533 sym.alias = g_sym;1556 .alignment = (sym.n_desc >> 8) & 0x0f,
1534 continue;1557 .file = object,
1535 }1558 },
1536 if (self.tentatives.get(sym.name)) |t_sym| {1559 };
1537 sym.alias = t_sym;
1538 continue;
1539 }
1540 if (self.unresolved.get(sym.name)) |u_sym| {
1541 sym.alias = u_sym;
1542 continue;
1543 }1560 }
1561 }
15441562
1545 try self.unresolved.putNoClobber(self.allocator, sym.name, sym);1563 try object.symbols.append(self.allocator, symbol);
1546 } else unreachable;
1547 }1564 }
1548}1565}
15491566
...@@ -1553,111 +1570,123 @@ fn resolveSymbols(self: *Zld) !void {...@@ -1553,111 +1570,123 @@ fn resolveSymbols(self: *Zld) !void {
1553 try self.resolveSymbolsInObject(object);1570 try self.resolveSymbolsInObject(object);
1554 }1571 }
15551572
1556 // Second pass, resolve symbols in static libraries.1573 log.warn("globals", .{});
1557 var next_sym: usize = 0;1574 for (self.globals.values()) |value| {
1558 while (true) {1575 log.warn(" | {s}: {}", .{ value.name, value.payload });
1559 if (next_sym == self.unresolved.count()) break;
1560
1561 const sym = self.unresolved.values()[next_sym];
1562
1563 var reset: bool = false;
1564 for (self.archives.items) |archive| {
1565 // Check if the entry exists in a static archive.
1566 const offsets = archive.toc.get(sym.name) orelse {
1567 // No hit.
1568 continue;
1569 };
1570 assert(offsets.items.len > 0);
1571
1572 const object = try archive.parseObject(offsets.items[0]);
1573 try self.objects.append(self.allocator, object);
1574 try self.resolveSymbolsInObject(object);
1575
1576 reset = true;
1577 break;
1578 }
1579
1580 if (reset) {
1581 next_sym = 0;
1582 } else {
1583 next_sym += 1;
1584 }
1585 }
1586
1587 // Third pass, resolve symbols in dynamic libraries.
1588 var unresolved = std.ArrayList(*Symbol).init(self.allocator);
1589 defer unresolved.deinit();
1590
1591 try unresolved.ensureCapacity(self.unresolved.count());
1592 for (self.unresolved.values()) |value| {
1593 unresolved.appendAssumeCapacity(value);
1594 }
1595 self.unresolved.clearRetainingCapacity();
1596
1597 // Put dyld_stub_binder as an unresolved special symbol.
1598 {
1599 const name = try self.allocator.dupe(u8, "dyld_stub_binder");
1600 errdefer self.allocator.free(name);
1601 const undef = try Symbol.Unresolved.new(self.allocator, name, .{});
1602 try unresolved.append(undef);
1603 }
1604
1605 var referenced = std.AutoHashMap(*Dylib, void).init(self.allocator);
1606 defer referenced.deinit();
1607
1608 loop: while (unresolved.popOrNull()) |undef| {
1609 const proxy = self.imports.get(undef.name) orelse outer: {
1610 const proxy = inner: {
1611 for (self.dylibs.items) |dylib| {
1612 const proxy = (try dylib.createProxy(undef.name)) orelse continue;
1613 try referenced.put(dylib, {});
1614 break :inner proxy;
1615 }
1616 if (mem.eql(u8, undef.name, "___dso_handle")) {
1617 // TODO this is just a temp patch until I work out what to actually
1618 // do with ___dso_handle and __mh_execute_header symbols which are
1619 // synthetically created by the linker on macOS.
1620 break :inner try Symbol.Proxy.new(self.allocator, undef.name, .{});
1621 }
1622
1623 self.unresolved.putAssumeCapacityNoClobber(undef.name, undef);
1624 continue :loop;
1625 };
1626
1627 try self.imports.putNoClobber(self.allocator, proxy.name, proxy);
1628 break :outer proxy;
1629 };
1630 undef.alias = proxy;
1631 }1576 }
16321577
1633 // Add LC_LOAD_DYLIB load command for each referenced dylib/stub.1578 for (self.objects.items) |object| {
1634 var it = referenced.iterator();1579 log.warn("object {s}", .{object.name.?});
1635 while (it.next()) |entry| {1580 for (object.symbols.items) |sym| {
1636 const dylib = entry.key_ptr.*;1581 log.warn(" | {s}: {}", .{ sym.name, sym.payload });
1637 dylib.ordinal = self.next_dylib_ordinal;
1638 const dylib_id = dylib.id orelse unreachable;
1639 var dylib_cmd = try createLoadDylibCommand(
1640 self.allocator,
1641 dylib_id.name,
1642 dylib_id.timestamp,
1643 dylib_id.current_version,
1644 dylib_id.compatibility_version,
1645 );
1646 errdefer dylib_cmd.deinit(self.allocator);
1647 try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
1648 self.next_dylib_ordinal += 1;
1649 }
1650
1651 if (self.unresolved.count() > 0) {
1652 for (self.unresolved.values()) |undef| {
1653 log.err("undefined reference to symbol '{s}'", .{undef.name});
1654 if (undef.cast(Symbol.Unresolved).?.file) |file| {
1655 log.err(" | referenced in {s}", .{file.name.?});
1656 }
1657 }1582 }
1658
1659 return error.UndefinedSymbolReference;
1660 }1583 }
1584
1585 // // Second pass, resolve symbols in static libraries.
1586 // var next_sym: usize = 0;
1587 // while (true) {
1588 // if (next_sym == self.unresolved.count()) break;
1589
1590 // const sym = self.unresolved.values()[next_sym];
1591
1592 // var reset: bool = false;
1593 // for (self.archives.items) |archive| {
1594 // // Check if the entry exists in a static archive.
1595 // const offsets = archive.toc.get(sym.name) orelse {
1596 // // No hit.
1597 // continue;
1598 // };
1599 // assert(offsets.items.len > 0);
1600
1601 // const object = try archive.parseObject(offsets.items[0]);
1602 // try self.objects.append(self.allocator, object);
1603 // try self.resolveSymbolsInObject(object);
1604
1605 // reset = true;
1606 // break;
1607 // }
1608
1609 // if (reset) {
1610 // next_sym = 0;
1611 // } else {
1612 // next_sym += 1;
1613 // }
1614 // }
1615
1616 // // Third pass, resolve symbols in dynamic libraries.
1617 // var unresolved = std.ArrayList(*Symbol).init(self.allocator);
1618 // defer unresolved.deinit();
1619
1620 // try unresolved.ensureCapacity(self.unresolved.count());
1621 // for (self.unresolved.values()) |value| {
1622 // unresolved.appendAssumeCapacity(value);
1623 // }
1624 // self.unresolved.clearRetainingCapacity();
1625
1626 // // Put dyld_stub_binder as an unresolved special symbol.
1627 // {
1628 // const name = try self.allocator.dupe(u8, "dyld_stub_binder");
1629 // errdefer self.allocator.free(name);
1630 // const undef = try Symbol.Unresolved.new(self.allocator, name, .{});
1631 // try unresolved.append(undef);
1632 // }
1633
1634 // var referenced = std.AutoHashMap(*Dylib, void).init(self.allocator);
1635 // defer referenced.deinit();
1636
1637 // loop: while (unresolved.popOrNull()) |undef| {
1638 // const proxy = self.imports.get(undef.name) orelse outer: {
1639 // const proxy = inner: {
1640 // for (self.dylibs.items) |dylib| {
1641 // const proxy = (try dylib.createProxy(undef.name)) orelse continue;
1642 // try referenced.put(dylib, {});
1643 // break :inner proxy;
1644 // }
1645 // if (mem.eql(u8, undef.name, "___dso_handle")) {
1646 // // TODO this is just a temp patch until I work out what to actually
1647 // // do with ___dso_handle and __mh_execute_header symbols which are
1648 // // synthetically created by the linker on macOS.
1649 // break :inner try Symbol.Proxy.new(self.allocator, undef.name, .{});
1650 // }
1651
1652 // self.unresolved.putAssumeCapacityNoClobber(undef.name, undef);
1653 // continue :loop;
1654 // };
1655
1656 // try self.imports.putNoClobber(self.allocator, proxy.name, proxy);
1657 // break :outer proxy;
1658 // };
1659 // undef.alias = proxy;
1660 // }
1661
1662 // // Add LC_LOAD_DYLIB load command for each referenced dylib/stub.
1663 // var it = referenced.iterator();
1664 // while (it.next()) |entry| {
1665 // const dylib = entry.key_ptr.*;
1666 // dylib.ordinal = self.next_dylib_ordinal;
1667 // const dylib_id = dylib.id orelse unreachable;
1668 // var dylib_cmd = try createLoadDylibCommand(
1669 // self.allocator,
1670 // dylib_id.name,
1671 // dylib_id.timestamp,
1672 // dylib_id.current_version,
1673 // dylib_id.compatibility_version,
1674 // );
1675 // errdefer dylib_cmd.deinit(self.allocator);
1676 // try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
1677 // self.next_dylib_ordinal += 1;
1678 // }
1679
1680 // if (self.unresolved.count() > 0) {
1681 // for (self.unresolved.values()) |undef| {
1682 // log.err("undefined reference to symbol '{s}'", .{undef.name});
1683 // if (undef.cast(Symbol.Unresolved).?.file) |file| {
1684 // log.err(" | referenced in {s}", .{file.name.?});
1685 // }
1686 // }
1687
1688 // return error.UndefinedSymbolReference;
1689 // }
1661}1690}
16621691
1663fn resolveStubsAndGotEntries(self: *Zld) !void {1692fn resolveStubsAndGotEntries(self: *Zld) !void {