master
1//! Represents a defined symbol.
2
3/// Allocated address value of this symbol.
4value: u64 = 0,
5
6/// Offset into the linker's intern table.
7name: MachO.String = .{},
8
9/// File where this symbol is defined.
10file: File.Index = 0,
11
12/// Reference to Atom containing this symbol if any.
13/// Use `getAtom` to get the pointer to the atom.
14atom_ref: MachO.Ref = .{ .index = 0, .file = 0 },
15
16/// Assigned output section index for this symbol.
17out_n_sect: u8 = 0,
18
19/// Index of the source nlist this symbol references.
20/// Use `getNlist` to pull the nlist from the relevant file.
21nlist_idx: u32 = 0,
22
23/// Misc flags for the symbol packaged as packed struct for compression.
24flags: Flags = .{},
25
26sect_flags: std.atomic.Value(u8) = std.atomic.Value(u8).init(0),
27
28visibility: Visibility = .local,
29
30extra: u32 = 0,
31
32pub fn isLocal(symbol: Symbol) bool {
33 return !(symbol.flags.import or symbol.flags.@"export");
34}
35
36pub fn isSymbolStab(symbol: Symbol, macho_file: *MachO) bool {
37 const file = symbol.getFile(macho_file) orelse return false;
38 return switch (file) {
39 .object => symbol.getNlist(macho_file).n_type.bits.is_stab != 0,
40 else => false,
41 };
42}
43
44pub fn isTlvInit(symbol: Symbol, macho_file: *MachO) bool {
45 const name = symbol.getName(macho_file);
46 return std.mem.indexOf(u8, name, "$tlv$init") != null;
47}
48
49pub fn weakRef(symbol: Symbol, macho_file: *MachO) bool {
50 const file = symbol.getFile(macho_file).?;
51 const is_dylib_weak = switch (file) {
52 .dylib => |x| x.weak,
53 else => false,
54 };
55 return is_dylib_weak or symbol.flags.weak_ref;
56}
57
58pub fn getName(symbol: Symbol, macho_file: *MachO) [:0]const u8 {
59 return switch (symbol.getFile(macho_file).?) {
60 inline else => |x| x.getString(symbol.name),
61 };
62}
63
64pub fn getAtom(symbol: Symbol, macho_file: *MachO) ?*Atom {
65 return symbol.atom_ref.getAtom(macho_file);
66}
67
68pub fn getOutputSectionIndex(symbol: Symbol, macho_file: *MachO) u8 {
69 if (symbol.getAtom(macho_file)) |atom| return atom.out_n_sect;
70 return symbol.out_n_sect;
71}
72
73pub fn getSectionFlags(symbol: Symbol) SectionFlags {
74 return @bitCast(symbol.sect_flags.load(.seq_cst));
75}
76
77pub fn setSectionFlags(symbol: *Symbol, flags: SectionFlags) void {
78 _ = symbol.sect_flags.fetchOr(@bitCast(flags), .seq_cst);
79}
80
81pub fn getFile(symbol: Symbol, macho_file: *MachO) ?File {
82 return macho_file.getFile(symbol.file);
83}
84
85/// Asserts file is an object.
86pub fn getNlist(symbol: Symbol, macho_file: *MachO) macho.nlist_64 {
87 const file = symbol.getFile(macho_file).?;
88 return switch (file) {
89 .dylib => unreachable,
90 .zig_object => unreachable,
91 .object => |x| x.symtab.items(.nlist)[symbol.nlist_idx],
92 .internal => |x| x.symtab.items[symbol.nlist_idx],
93 };
94}
95
96pub fn getSize(symbol: Symbol, macho_file: *MachO) u64 {
97 const file = symbol.getFile(macho_file).?;
98 assert(file == .object);
99 return file.object.symtab.items(.size)[symbol.nlist_idx];
100}
101
102pub fn getDylibOrdinal(symbol: Symbol, macho_file: *MachO) ?u16 {
103 assert(symbol.flags.import);
104 const file = symbol.getFile(macho_file) orelse return null;
105 return switch (file) {
106 .dylib => |x| x.ordinal,
107 else => null,
108 };
109}
110
111pub fn getSymbolRank(symbol: Symbol, macho_file: *MachO) u32 {
112 const file = symbol.getFile(macho_file) orelse return std.math.maxInt(u32);
113 const in_archive = switch (file) {
114 .object => |x| !x.alive,
115 else => false,
116 };
117 return file.getSymbolRank(.{
118 .archive = in_archive,
119 .weak = symbol.flags.weak,
120 .tentative = symbol.flags.tentative,
121 });
122}
123
124pub fn getAddress(symbol: Symbol, opts: struct {
125 stubs: bool = true,
126 trampoline: bool = true,
127}, macho_file: *MachO) u64 {
128 if (opts.stubs) {
129 if (symbol.getSectionFlags().stubs) {
130 return symbol.getStubsAddress(macho_file);
131 } else if (symbol.getSectionFlags().objc_stubs) {
132 return symbol.getObjcStubsAddress(macho_file);
133 }
134 }
135 if (symbol.flags.trampoline and opts.trampoline) {
136 return symbol.getTrampolineAddress(macho_file);
137 }
138 if (symbol.getAtom(macho_file)) |atom| return atom.getAddress(macho_file) + symbol.value;
139 return symbol.value;
140}
141
142pub fn getGotAddress(symbol: Symbol, macho_file: *MachO) u64 {
143 if (!symbol.getSectionFlags().has_got) return 0;
144 const extra = symbol.getExtra(macho_file);
145 return macho_file.got.getAddress(extra.got, macho_file);
146}
147
148pub fn getStubsAddress(symbol: Symbol, macho_file: *MachO) u64 {
149 if (!symbol.getSectionFlags().stubs) return 0;
150 const extra = symbol.getExtra(macho_file);
151 return macho_file.stubs.getAddress(extra.stubs, macho_file);
152}
153
154pub fn getObjcStubsAddress(symbol: Symbol, macho_file: *MachO) u64 {
155 if (!symbol.getSectionFlags().objc_stubs) return 0;
156 const extra = symbol.getExtra(macho_file);
157 return macho_file.objc_stubs.getAddress(extra.objc_stubs, macho_file);
158}
159
160pub fn getObjcSelrefsAddress(symbol: Symbol, macho_file: *MachO) u64 {
161 if (!symbol.getSectionFlags().objc_stubs) return 0;
162 const extra = symbol.getExtra(macho_file);
163 const file = symbol.getFile(macho_file).?;
164 return switch (file) {
165 .dylib, .zig_object => unreachable,
166 inline else => |x| x.symbols.items[extra.objc_selrefs].getAddress(.{}, macho_file),
167 };
168}
169
170pub fn getTlvPtrAddress(symbol: Symbol, macho_file: *MachO) u64 {
171 if (!symbol.getSectionFlags().tlv_ptr) return 0;
172 const extra = symbol.getExtra(macho_file);
173 return macho_file.tlv_ptr.getAddress(extra.tlv_ptr, macho_file);
174}
175
176pub fn getTrampolineAddress(symbol: Symbol, macho_file: *MachO) u64 {
177 if (!symbol.flags.trampoline) return 0;
178 const zo = macho_file.getZigObject().?;
179 const index = symbol.getExtra(macho_file).trampoline;
180 return zo.symbols.items[index].getAddress(.{}, macho_file);
181}
182
183pub fn getOutputSymtabIndex(symbol: Symbol, macho_file: *MachO) ?u32 {
184 if (!symbol.flags.output_symtab) return null;
185 assert(!symbol.isSymbolStab(macho_file));
186 const file = symbol.getFile(macho_file).?;
187 const symtab_ctx = switch (file) {
188 inline else => |x| x.output_symtab_ctx,
189 };
190 var idx = symbol.getExtra(macho_file).symtab;
191 if (symbol.isLocal()) {
192 idx += symtab_ctx.ilocal;
193 } else if (symbol.flags.@"export") {
194 idx += symtab_ctx.iexport;
195 } else {
196 assert(symbol.flags.import);
197 idx += symtab_ctx.iimport;
198 }
199 return idx;
200}
201
202const AddExtraOpts = struct {
203 got: ?u32 = null,
204 stubs: ?u32 = null,
205 objc_stubs: ?u32 = null,
206 objc_selrefs: ?u32 = null,
207 tlv_ptr: ?u32 = null,
208 symtab: ?u32 = null,
209 trampoline: ?u32 = null,
210};
211
212pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, macho_file: *MachO) void {
213 var extra = symbol.getExtra(macho_file);
214 inline for (@typeInfo(@TypeOf(opts)).@"struct".fields) |field| {
215 if (@field(opts, field.name)) |x| {
216 @field(extra, field.name) = x;
217 }
218 }
219 symbol.setExtra(extra, macho_file);
220}
221
222pub inline fn getExtra(symbol: Symbol, macho_file: *MachO) Extra {
223 return switch (symbol.getFile(macho_file).?) {
224 inline else => |x| x.getSymbolExtra(symbol.extra),
225 };
226}
227
228pub inline fn setExtra(symbol: Symbol, extra: Extra, macho_file: *MachO) void {
229 return switch (symbol.getFile(macho_file).?) {
230 inline else => |x| x.setSymbolExtra(symbol.extra, extra),
231 };
232}
233
234pub fn setOutputSym(symbol: Symbol, macho_file: *MachO, out: *macho.nlist_64) void {
235 if (symbol.isLocal()) {
236 out.n_type = .{ .bits = .{
237 .ext = false,
238 .type = if (symbol.flags.abs) .abs else .sect,
239 .pext = false,
240 .is_stab = 0,
241 } };
242 out.n_sect = if (symbol.flags.abs) 0 else @intCast(symbol.getOutputSectionIndex(macho_file) + 1);
243 out.n_desc = @bitCast(@as(u16, 0));
244 out.n_value = symbol.getAddress(.{ .stubs = false }, macho_file);
245
246 switch (symbol.visibility) {
247 .hidden => out.n_type.bits.pext = true,
248 else => {},
249 }
250 } else if (symbol.flags.@"export") {
251 assert(symbol.visibility == .global);
252 out.n_type = .{ .bits = .{
253 .ext = true,
254 .type = if (symbol.flags.abs) .abs else .sect,
255 .pext = false,
256 .is_stab = 0,
257 } };
258 out.n_sect = if (symbol.flags.abs) 0 else @intCast(symbol.getOutputSectionIndex(macho_file) + 1);
259 out.n_value = symbol.getAddress(.{ .stubs = false }, macho_file);
260 out.n_desc = @bitCast(@as(u16, 0));
261
262 if (symbol.flags.weak) {
263 out.n_desc.weak_def_or_ref_to_weak = true;
264 }
265 if (symbol.flags.dyn_ref) {
266 out.n_desc.referenced_dynamically = true;
267 }
268 } else {
269 assert(symbol.visibility == .global);
270 out.n_type = .{ .bits = .{
271 .ext = true,
272 .type = .undf,
273 .pext = false,
274 .is_stab = 0,
275 } };
276 out.n_sect = 0;
277 out.n_value = 0;
278 out.n_desc = @bitCast(@as(u16, 0));
279
280 // TODO:
281 // const ord: u16 = if (macho_file.options.namespace == .flat)
282 // @as(u8, @bitCast(macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP))
283 // else if (symbol.getDylibOrdinal(macho_file)) |ord|
284 // ord
285 // else
286 // macho.BIND_SPECIAL_DYLIB_SELF;
287 const ord: u16 = if (symbol.getDylibOrdinal(macho_file)) |ord|
288 ord
289 else
290 macho.BIND_SPECIAL_DYLIB_SELF;
291 out.n_desc = @bitCast(macho.N_SYMBOL_RESOLVER * ord);
292
293 if (symbol.flags.weak) {
294 out.n_desc.weak_def_or_ref_to_weak = true;
295 }
296
297 if (symbol.weakRef(macho_file)) {
298 out.n_desc.weak_ref = true;
299 }
300 }
301}
302
303pub fn fmt(symbol: Symbol, macho_file: *MachO) std.fmt.Alt(Format, Format.default) {
304 return .{ .data = .{
305 .symbol = symbol,
306 .macho_file = macho_file,
307 } };
308}
309
310const Format = struct {
311 symbol: Symbol,
312 macho_file: *MachO,
313
314 fn default(f: Format, w: *Writer) Writer.Error!void {
315 const symbol = f.symbol;
316 try w.print("%{d} : {s} : @{x}", .{
317 symbol.nlist_idx,
318 symbol.getName(f.macho_file),
319 symbol.getAddress(.{}, f.macho_file),
320 });
321 if (symbol.getFile(f.macho_file)) |file| {
322 if (symbol.getOutputSectionIndex(f.macho_file) != 0) {
323 try w.print(" : sect({d})", .{symbol.getOutputSectionIndex(f.macho_file)});
324 }
325 if (symbol.getAtom(f.macho_file)) |atom| {
326 try w.print(" : atom({d})", .{atom.atom_index});
327 }
328 var buf: [3]u8 = .{'_'} ** 3;
329 if (symbol.flags.@"export") buf[0] = 'E';
330 if (symbol.flags.import) buf[1] = 'I';
331 switch (symbol.visibility) {
332 .local => buf[2] = 'L',
333 .hidden => buf[2] = 'H',
334 .global => buf[2] = 'G',
335 }
336 try w.print(" : {s}", .{&buf});
337 if (symbol.flags.weak) try w.writeAll(" : weak");
338 if (symbol.isSymbolStab(f.macho_file)) try w.writeAll(" : stab");
339 switch (file) {
340 .zig_object => |x| try w.print(" : zig_object({d})", .{x.index}),
341 .internal => |x| try w.print(" : internal({d})", .{x.index}),
342 .object => |x| try w.print(" : object({d})", .{x.index}),
343 .dylib => |x| try w.print(" : dylib({d})", .{x.index}),
344 }
345 } else try w.writeAll(" : unresolved");
346 }
347};
348
349pub const Flags = packed struct {
350 /// Whether the symbol is imported at runtime.
351 import: bool = false,
352
353 /// Whether the symbol is exported at runtime.
354 @"export": bool = false,
355
356 /// Whether this symbol is weak.
357 weak: bool = false,
358
359 /// Whether this symbol is weakly referenced.
360 weak_ref: bool = false,
361
362 /// Whether this symbol is dynamically referenced.
363 dyn_ref: bool = false,
364
365 /// Whether this symbol was marked as N_NO_DEAD_STRIP.
366 no_dead_strip: bool = false,
367
368 /// Whether this symbol can be interposed at runtime.
369 interposable: bool = false,
370
371 /// Whether this symbol is absolute.
372 abs: bool = false,
373
374 /// Whether this symbol is a tentative definition.
375 tentative: bool = false,
376
377 /// Whether this symbol is a thread-local variable.
378 tlv: bool = false,
379
380 /// Whether the symbol makes into the output symtab or not.
381 output_symtab: bool = false,
382
383 /// ZigObject specific flags
384 /// Whether the symbol has a trampoline
385 trampoline: bool = false,
386};
387
388pub const SectionFlags = packed struct(u8) {
389 /// Whether the symbol contains __got indirection.
390 needs_got: bool = false,
391 has_got: bool = false,
392
393 /// Whether the symbols contains __stubs indirection.
394 stubs: bool = false,
395
396 /// Whether the symbol has a TLV pointer.
397 tlv_ptr: bool = false,
398
399 /// Whether the symbol contains __objc_stubs indirection.
400 objc_stubs: bool = false,
401
402 _: u3 = 0,
403};
404
405pub const Visibility = enum {
406 global,
407 hidden,
408 local,
409
410 pub fn rank(vis: Visibility) u2 {
411 return switch (vis) {
412 .local => 2,
413 .hidden => 1,
414 .global => 0,
415 };
416 }
417};
418
419pub const Extra = struct {
420 got: u32 = 0,
421 stubs: u32 = 0,
422 objc_stubs: u32 = 0,
423 objc_selrefs: u32 = 0,
424 tlv_ptr: u32 = 0,
425 symtab: u32 = 0,
426 trampoline: u32 = 0,
427};
428
429pub const Index = u32;
430
431const assert = std.debug.assert;
432const macho = std.macho;
433const std = @import("std");
434const Writer = std.Io.Writer;
435
436const Atom = @import("Atom.zig");
437const File = @import("file.zig").File;
438const MachO = @import("../MachO.zig");
439const Nlist = Object.Nlist;
440const Object = @import("Object.zig");
441const Symbol = @This();
442const ZigGotSection = @import("synthetic.zig").ZigGotSection;