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,
4545dwarf_debug_line_index: ?u16 = null,
4646dwarf_debug_ranges_index: ?u16 = null,
4747
48symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
49strtab: std.ArrayListUnmanaged(u8) = .{},
50
4851symbols: std.ArrayListUnmanaged(*Symbol) = .{},
4952stabs: std.ArrayListUnmanaged(*Symbol) = .{},
50initializers: std.ArrayListUnmanaged(*Symbol) = .{},
53initializers: std.ArrayListUnmanaged(u32) = .{},
5154data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
5255
5356pub const Section = struct {
......@@ -216,20 +219,13 @@ pub fn deinit(self: *Object) void {
216219 }
217220 self.sections.deinit(self.allocator);
218221
219 for (self.symbols.items) |sym| {
220 sym.deinit(self.allocator);
221 self.allocator.destroy(sym);
222 }
223222 self.symbols.deinit(self.allocator);
224
225 for (self.stabs.items) |stab| {
226 stab.deinit(self.allocator);
227 self.allocator.destroy(stab);
228 }
229223 self.stabs.deinit(self.allocator);
230224
231225 self.data_in_code_entries.deinit(self.allocator);
232226 self.initializers.deinit(self.allocator);
227 self.symtab.deinit(self.allocator);
228 self.strtab.deinit(self.allocator);
233229
234230 if (self.name) |n| {
235231 self.allocator.free(n);
......@@ -271,11 +267,10 @@ pub fn parse(self: *Object) !void {
271267 self.header = header;
272268
273269 try self.readLoadCommands(reader);
274 try self.parseSymbols();
275270 try self.parseSections();
271 try self.parseSymtab();
276272 try self.parseDataInCode();
277273 try self.parseInitializers();
278 try self.parseDebugInfo();
279274}
280275
281276pub fn readLoadCommands(self: *Object, reader: anytype) !void {
......@@ -394,14 +389,13 @@ pub fn parseInitializers(self: *Object) !void {
394389 const relocs = section.relocs orelse unreachable;
395390 try self.initializers.ensureCapacity(self.allocator, relocs.len);
396391 for (relocs) |rel| {
397 const sym = self.symbols.items[rel.target.symbol];
398 self.initializers.appendAssumeCapacity(sym);
392 self.initializers.appendAssumeCapacity(rel.target.symbol);
399393 }
400394
401 mem.reverse(*Symbol, self.initializers.items);
395 mem.reverse(u32, self.initializers.items);
402396}
403397
404pub fn parseSymbols(self: *Object) !void {
398fn parseSymtab(self: *Object) !void {
405399 const index = self.symtab_cmd_index orelse return;
406400 const symtab_cmd = self.load_commands.items[index].Symtab;
407401
......@@ -409,59 +403,12 @@ pub fn parseSymbols(self: *Object) !void {
409403 defer self.allocator.free(symtab);
410404 _ = try self.file.?.preadAll(symtab, symtab_cmd.symoff);
411405 const slice = @alignCast(@alignOf(macho.nlist_64), mem.bytesAsSlice(macho.nlist_64, symtab));
406 try self.symtab.appendSlice(self.allocator, slice);
412407
413408 var strtab = try self.allocator.alloc(u8, symtab_cmd.strsize);
414409 defer self.allocator.free(strtab);
415410 _ = try self.file.?.preadAll(strtab, symtab_cmd.stroff);
416
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 }
411 try self.strtab.appendSlice(self.allocator, strtab);
465412}
466413
467414pub fn parseDebugInfo(self: *Object) !void {
......@@ -555,14 +502,6 @@ pub fn parseDebugInfo(self: *Object) !void {
555502 self.stabs.appendAssumeCapacity(delim_stab);
556503}
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
566505pub fn parseDataInCode(self: *Object) !void {
567506 const index = self.data_in_code_cmd_index orelse return;
568507 const data_in_code = self.load_commands.items[index].LinkeditData;
......@@ -582,3 +521,16 @@ pub fn parseDataInCode(self: *Object) !void {
582521 try self.data_in_code_entries.append(self.allocator, dice);
583522 }
584523}
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 @@
11const Symbol = @This();
22
33const std = @import("std");
4const assert = std.debug.assert;
45const macho = std.macho;
56const mem = std.mem;
67
......@@ -9,177 +10,32 @@ const Dylib = @import("Dylib.zig");
910const Object = @import("Object.zig");
1011const 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
2313/// Symbol name. Owned slice.
2414name: []const u8,
2515
26/// Alias of.
27alias: ?*Symbol = null,
28
2916/// Index in GOT table for indirection.
3017got_index: ?u32 = null,
3118
3219/// Index in stubs table for late binding.
3320stubs_index: ?u32 = null,
3421
35pub const Stab = struct {
36 base: Symbol,
37
38 // Symbol kind: function, etc.
39 kind: Kind,
40
41 // Size of stab.
42 size: u64,
43
44 // Base regular symbol for this stub if defined.
45 symbol: ?*Symbol = null,
46
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,
22payload: union(enum) {
23 regular: Regular,
24 tentative: Tentative,
25 proxy: Proxy,
26 undef: Undefined,
27
28 pub fn format(self: @This(), comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
29 return switch (self) {
30 .regular => |p| p.format(fmt, options, writer),
31 .tentative => |p| p.format(fmt, options, writer),
32 .proxy => |p| p.format(fmt, options, writer),
33 .undef => |p| p.format(fmt, options, writer),
8034 };
81
82 return &stab.base;
8335 }
84
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};
36},
17937
18038pub const Regular = struct {
181 base: Symbol,
182
18339 /// Linkage type.
18440 linkage: Linkage,
18541
......@@ -196,77 +52,56 @@ pub const Regular = struct {
19652 /// null means self-reference.
19753 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
20555 pub const Linkage = enum {
20656 translation_unit,
20757 linkage_unit,
20858 global,
20959 };
21060
211 const Opts = struct {
212 linkage: Linkage = .translation_unit,
213 address: u64 = 0,
214 section: u8 = 0,
215 weak_ref: bool = 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;
61 pub fn isTemp(regular: Regular) bool {
62 if (regular.linkage == .translation_unit) {
63 return mem.startsWith(u8, regular.base.name, "l") or mem.startsWith(u8, regular.base.name, "L");
64 }
65 return false;
23666 }
23767
238 pub fn asNlist(regular: *Regular, strtab: *StringTable) !macho.nlist_64 {
239 const n_strx = try strtab.getOrPut(regular.base.name);
240 var nlist = macho.nlist_64{
241 .n_strx = n_strx,
242 .n_type = macho.N_SECT,
243 .n_sect = regular.section,
244 .n_desc = 0,
245 .n_value = regular.address,
246 };
247
248 if (regular.linkage != .translation_unit) {
249 nlist.n_type |= macho.N_EXT;
68 pub fn format(self: Regular, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
69 try std.fmt.format(writer, "Regular {{ ", .{});
70 try std.fmt.format(writer, ".linkage = {s}, ", .{self.linkage});
71 try std.fmt.format(writer, ".address = 0x{x}, ", .{self.address});
72 try std.fmt.format(writer, ".section = {}, ", .{self.section});
73 if (self.weak_ref) {
74 try std.fmt.format(writer, ".weak_ref, ", .{});
25075 }
251 if (regular.linkage == .linkage_unit) {
252 nlist.n_type |= macho.N_PEXT;
253 nlist.n_desc |= macho.N_WEAK_DEF;
76 if (self.file) |file| {
77 try std.fmt.format(writer, ".file = {s}, ", .{file.name.?});
25478 }
255
256 return nlist;
79 try std.fmt.format(writer, "}}", .{});
25780 }
81};
25882
259 pub fn isTemp(regular: *Regular) bool {
260 if (regular.linkage == .translation_unit) {
261 return mem.startsWith(u8, regular.base.name, "l") or mem.startsWith(u8, regular.base.name, "L");
83pub const Tentative = struct {
84 /// Symbol size.
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.?});
26299 }
263 return false;
100 try std.fmt.format(writer, "}}", .{});
264101 }
265102};
266103
267104pub const Proxy = struct {
268 base: Symbol,
269
270105 /// Dynamic binding info - spots within the final
271106 /// executable where this proxy is referenced from.
272107 bind_info: std.ArrayListUnmanaged(struct {
......@@ -278,159 +113,121 @@ pub const Proxy = struct {
278113 /// null means self-reference.
279114 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
313116 pub fn deinit(proxy: *Proxy, allocator: *Allocator) void {
314117 proxy.bind_info.deinit(allocator);
315118 }
316119
317 pub fn dylibOrdinal(proxy: *Proxy) u16 {
120 pub fn dylibOrdinal(proxy: Proxy) u16 {
318121 const dylib = proxy.file orelse return 0;
319122 return dylib.ordinal.?;
320123 }
321};
322124
323pub const Unresolved = struct {
324 base: Symbol,
125 pub fn format(self: Proxy, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
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 {
326139 /// File where this symbol was referenced.
327140 /// null means synthetic, e.g., dyld_stub_binder.
328141 file: ?*Object = null,
329142
330 pub const base_type: Symbol.Type = .unresolved;
331
332 const Opts = struct {
333 file: ?*Object = null,
334 };
335
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 };
143 pub fn format(self: Undefined, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
144 try std.fmt.format(writer, "Undefined {{ ", .{});
145 if (self.file) |file| {
146 try std.fmt.format(writer, ".file = {s}, ", .{file.name.?});
147 }
148 try std.fmt.format(writer, "}}", .{});
360149 }
361150};
362151
363pub const Tentative = struct {
364 base: Symbol,
152/// Create new undefined 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.
367 size: u64,
368
369 /// Symbol alignment as power of two.
370 alignment: u16,
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,
157 new_sym.* = .{
158 .name = try allocator.dupe(u8, name),
159 .payload = .{
160 .undef = .{},
161 },
381162 };
382163
383 pub fn new(allocator: *Allocator, name: []const u8, opts: Opts) !*Symbol {
384 const tent = try allocator.create(Tentative);
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};
164 return new_sym;
165}
412166
413pub fn deinit(base: *Symbol, allocator: *Allocator) void {
414 allocator.free(base.name);
167pub fn asNlist(symbol: *Symbol, strtab: *StringTable) macho.nlist_64 {
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") {
417 .proxy => @fieldParentPtr(Proxy, "base", base).deinit(allocator),
418 else => {},
419 }
188 break :nlist nlist;
189 },
190 .tentative => |tentative| {
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;
420222}
421223
422pub fn cast(base: *Symbol, comptime T: type) ?*T {
423 if (base.@"type" != T.base_type) {
424 return null;
425 }
426 return @fieldParentPtr(T, "base", base);
427}
224pub fn deinit(symbol: *Symbol, allocator: *Allocator) void {
225 allocator.free(symbol.name);
428226
429pub fn getTopmostAlias(base: *Symbol) *Symbol {
430 if (base.alias) |alias| {
431 return alias.getTopmostAlias();
227 switch (symbol.payload) {
228 .proxy => |*proxy| proxy.deinit(allocator),
229 else => {},
432230 }
433 return base;
434231}
435232
436233pub fn isStab(sym: macho.nlist_64) bool {
src/link/MachO/Zld.zig+226-197
......@@ -102,10 +102,9 @@ objc_selrefs_section_index: ?u16 = null,
102102objc_classrefs_section_index: ?u16 = null,
103103objc_data_section_index: ?u16 = null,
104104
105locals: std.ArrayListUnmanaged(*Symbol) = .{},
105106globals: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
106107imports: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
107unresolved: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
108tentatives: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
109108
110109/// Offset into __DATA,__common section.
111110/// Set if the linker found tentative definitions in any of the objects.
......@@ -173,15 +172,24 @@ pub fn deinit(self: *Zld) void {
173172 }
174173 self.dylibs.deinit(self.allocator);
175174
176 for (self.imports.values()) |proxy| {
177 proxy.deinit(self.allocator);
178 self.allocator.destroy(proxy);
175 for (self.imports.values()) |sym| {
176 sym.deinit(self.allocator);
177 self.allocator.destroy(sym);
179178 }
180179 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 }
183185 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
185193 self.strtab.deinit();
186194}
187195
......@@ -221,20 +229,21 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg
221229 try self.parseInputFiles(files, args.syslibroot);
222230 try self.parseLibs(args.libs, args.syslibroot);
223231 try self.resolveSymbols();
224 try self.resolveStubsAndGotEntries();
225 try self.updateMetadata();
226 try self.sortSections();
227 try self.addRpaths(args.rpaths);
228 try self.addDataInCodeLC();
229 try self.addCodeSignatureLC();
230 try self.allocateTextSegment();
231 try self.allocateDataConstSegment();
232 try self.allocateDataSegment();
233 self.allocateLinkeditSegment();
234 try self.allocateSymbols();
235 try self.allocateTentativeSymbols();
236 try self.allocateProxyBindAddresses();
237 try self.flush();
232 return error.TODO;
233 // try self.resolveStubsAndGotEntries();
234 // try self.updateMetadata();
235 // try self.sortSections();
236 // try self.addRpaths(args.rpaths);
237 // try self.addDataInCodeLC();
238 // try self.addCodeSignatureLC();
239 // try self.allocateTextSegment();
240 // try self.allocateDataConstSegment();
241 // try self.allocateDataSegment();
242 // self.allocateLinkeditSegment();
243 // try self.allocateSymbols();
244 // try self.allocateTentativeSymbols();
245 // try self.allocateProxyBindAddresses();
246 // try self.flush();
238247}
239248
240249fn parseInputFiles(self: *Zld, files: []const []const u8, syslibroot: ?[]const u8) !void {
......@@ -1458,92 +1467,100 @@ fn writeStubInStubHelper(self: *Zld, index: u32) !void {
14581467fn resolveSymbolsInObject(self: *Zld, object: *Object) !void {
14591468 log.debug("resolving symbols in '{s}'", .{object.name});
14601469
1461 for (object.symbols.items) |sym| {
1462 if (sym.cast(Symbol.Regular)) |reg| {
1463 if (reg.linkage == .translation_unit) continue; // Symbol local to TU.
1470 for (object.symtab.items) |sym| {
1471 const sym_name = object.getString(sym.n_strx);
14641472
1465 if (self.tentatives.fetchSwapRemove(sym.name)) |kv| {
1466 // Create link to the global.
1467 kv.value.alias = sym;
1468 }
1469 if (self.unresolved.fetchSwapRemove(sym.name)) |kv| {
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 }
1473 if (Symbol.isStab(sym)) {
1474 log.err("unhandled symbol type: stab {s}", .{sym_name});
1475 log.err(" | first definition in {s}", .{object.name.?});
1476 return error.UnhandledSymbolType;
1477 }
14991478
1500 g_sym.alias = sym;
1501 sym_ptr.* = sym;
1502 } else if (sym.cast(Symbol.Tentative)) |tent| {
1503 if (self.globals.get(sym.name)) |g_sym| {
1504 sym.alias = g_sym;
1505 continue;
1506 }
1479 if (Symbol.isIndr(sym)) {
1480 log.err("unhandled symbol type: indirect {s}", .{sym_name});
1481 log.err(" | first definition in {s}", .{object.name.?});
1482 return error.UnhandledSymbolType;
1483 }
15071484
1508 if (self.unresolved.fetchSwapRemove(sym.name)) |kv| {
1509 kv.value.alias = sym;
1510 }
1485 if (Symbol.isAbs(sym)) {
1486 log.err("unhandled symbol type: absolute {s}", .{sym_name});
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 {
1513 // Put new tentative definition symbol into symbol table.
1514 try self.tentatives.putNoClobber(self.allocator, sym.name, sym);
1515 continue;
1491 if (Symbol.isSect(sym) and !Symbol.isExt(sym)) {
1492 // Regular symbol local to translation unit
1493 const symbol = try Symbol.new(self.allocator, sym_name);
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 },
15161502 };
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.
1519 // We model this like a heap where the tentative definition with the
1520 // largest size always washes up on top.
1521 const t_sym = sym_ptr.*;
1522 const t_tent = t_sym.cast(Symbol.Tentative) orelse unreachable;
1508 const symbol = self.globals.get(sym_name) orelse symbol: {
1509 // Insert new global symbol.
1510 const symbol = try Symbol.new(self.allocator, sym_name);
1511 symbol.payload.undef.file = object;
1512 try self.globals.putNoClobber(self.allocator, symbol.name, symbol);
1513 break :symbol symbol;
1514 };
15231515
1524 if (tent.size < t_tent.size) {
1525 sym.alias = t_sym;
1526 continue;
1516 if (Symbol.isSect(sym)) {
1517 // Global symbol
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 };
15271543 }
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;
1530 sym_ptr.* = sym;
1531 } else if (sym.cast(Symbol.Unresolved)) |_| {
1532 if (self.globals.get(sym.name)) |g_sym| {
1533 sym.alias = g_sym;
1534 continue;
1535 }
1536 if (self.tentatives.get(sym.name)) |t_sym| {
1537 sym.alias = t_sym;
1538 continue;
1539 }
1540 if (self.unresolved.get(sym.name)) |u_sym| {
1541 sym.alias = u_sym;
1542 continue;
1552 if (should_update) {
1553 symbol.payload = .{
1554 .tentative = .{
1555 .size = sym.n_value,
1556 .alignment = (sym.n_desc >> 8) & 0x0f,
1557 .file = object,
1558 },
1559 };
15431560 }
1561 }
15441562
1545 try self.unresolved.putNoClobber(self.allocator, sym.name, sym);
1546 } else unreachable;
1563 try object.symbols.append(self.allocator, symbol);
15471564 }
15481565}
15491566
......@@ -1553,111 +1570,123 @@ fn resolveSymbols(self: *Zld) !void {
15531570 try self.resolveSymbolsInObject(object);
15541571 }
15551572
1556 // Second pass, resolve symbols in static libraries.
1557 var next_sym: usize = 0;
1558 while (true) {
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;
1573 log.warn("globals", .{});
1574 for (self.globals.values()) |value| {
1575 log.warn(" | {s}: {}", .{ value.name, value.payload });
16311576 }
16321577
1633 // Add LC_LOAD_DYLIB load command for each referenced dylib/stub.
1634 var it = referenced.iterator();
1635 while (it.next()) |entry| {
1636 const dylib = entry.key_ptr.*;
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 }
1578 for (self.objects.items) |object| {
1579 log.warn("object {s}", .{object.name.?});
1580 for (object.symbols.items) |sym| {
1581 log.warn(" | {s}: {}", .{ sym.name, sym.payload });
16571582 }
1658
1659 return error.UndefinedSymbolReference;
16601583 }
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 // }
16611690}
16621691
16631692fn resolveStubsAndGotEntries(self: *Zld) !void {