commit 16604a93b9159fafec3528457366ca146bf29ce5 (tree)
parent e4f5dada615c103bfe656c9fcd8f28df5f5ef9d1
Author: Jakub Konka <kubkon@jakubkonka.com>
Date: Thu, 18 Jul 2024 22:23:30 +0200
Merge pull request #20650 from ziglang/parallel-macho
The tale of parallel MachO: part 1
Diffstat:
28 files changed, 4696 insertions(+), 3356 deletions(-)
diff --git a/build.zig b/build.zig
@@ -620,7 +620,7 @@ fn addCompilerStep(b: *std.Build, options: AddCompilerStepOptions) *std.Build.St
.root_source_file = b.path("src/main.zig"),
.target = options.target,
.optimize = options.optimize,
- .max_rss = 7_000_000_000,
+ .max_rss = 7_100_000_000,
.strip = options.strip,
.sanitize_thread = options.sanitize_thread,
.single_threaded = options.single_threaded,
diff --git a/src/Sema.zig b/src/Sema.zig
@@ -17787,8 +17787,7 @@ fn zirBuiltinSrc(
};
const file_name_val = v: {
- // The compiler must not call realpath anywhere.
- const file_name = try fn_owner_decl.getFileScope(mod).fullPath(sema.arena);
+ const file_name = fn_owner_decl.getFileScope(mod).sub_file_path;
const array_ty = try pt.intern(.{ .array_type = .{
.len = file_name.len,
.sentinel = .zero_u8,
diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig
@@ -12362,8 +12362,9 @@ fn genCall(self: *Self, info: union(enum) {
try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym_index }, .{});
try self.asmRegister(.{ ._, .call }, .rax);
} else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
- const sym_index = try macho_file.getZigObject().?.getOrCreateMetadataForDecl(macho_file, func.owner_decl);
- const sym = macho_file.getSymbol(sym_index);
+ const zo = macho_file.getZigObject().?;
+ const sym_index = try zo.getOrCreateMetadataForDecl(macho_file, func.owner_decl);
+ const sym = zo.symbols.items[sym_index];
try self.genSetReg(
.rax,
Type.usize,
@@ -15396,9 +15397,10 @@ fn genLazySymbolRef(
else => unreachable,
}
} else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
- const sym_index = macho_file.getZigObject().?.getOrCreateMetadataForLazySymbol(macho_file, pt, lazy_sym) catch |err|
+ const zo = macho_file.getZigObject().?;
+ const sym_index = zo.getOrCreateMetadataForLazySymbol(macho_file, pt, lazy_sym) catch |err|
return self.fail("{s} creating lazy symbol", .{@errorName(err)});
- const sym = macho_file.getSymbol(sym_index);
+ const sym = zo.symbols.items[sym_index];
switch (tag) {
.lea, .call => try self.genSetReg(
reg,
diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig
@@ -51,12 +51,12 @@ pub fn emitMir(emit: *Emit) Error!void {
});
} else if (emit.lower.bin_file.cast(link.File.MachO)) |macho_file| {
// Add relocation to the decl.
- const atom = macho_file.getSymbol(symbol.atom_index).getAtom(macho_file).?;
- const sym_index = macho_file.getZigObject().?.symbols.items[symbol.sym_index];
+ const zo = macho_file.getZigObject().?;
+ const atom = zo.symbols.items[symbol.atom_index].getAtom(macho_file).?;
try atom.addReloc(macho_file, .{
.tag = .@"extern",
.offset = end_offset - 4,
- .target = sym_index,
+ .target = symbol.sym_index,
.addend = 0,
.type = .branch,
.meta = .{
@@ -160,11 +160,11 @@ pub fn emitMir(emit: *Emit) Error!void {
.Obj => true,
.Lib => emit.lower.link_mode == .static,
};
- const atom = macho_file.getSymbol(data.atom_index).getAtom(macho_file).?;
- const sym_index = macho_file.getZigObject().?.symbols.items[data.sym_index];
- const sym = macho_file.getSymbol(sym_index);
+ const zo = macho_file.getZigObject().?;
+ const atom = zo.symbols.items[data.atom_index].getAtom(macho_file).?;
+ const sym = &zo.symbols.items[data.sym_index];
if (sym.flags.needs_zig_got and !is_obj_or_static_lib) {
- _ = try sym.getOrCreateZigGotEntry(sym_index, macho_file);
+ _ = try sym.getOrCreateZigGotEntry(data.sym_index, macho_file);
}
const @"type": link.File.MachO.Relocation.Type = if (sym.flags.needs_zig_got and !is_obj_or_static_lib)
.zig_got_load
@@ -179,7 +179,7 @@ pub fn emitMir(emit: *Emit) Error!void {
try atom.addReloc(macho_file, .{
.tag = .@"extern",
.offset = @intCast(end_offset - 4),
- .target = sym_index,
+ .target = data.sym_index,
.addend = 0,
.type = @"type",
.meta = .{
diff --git a/src/arch/x86_64/Lower.zig b/src/arch/x86_64/Lower.zig
@@ -425,8 +425,8 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
else => unreachable,
};
} else if (lower.bin_file.cast(link.File.MachO)) |macho_file| {
- const sym_index = macho_file.getZigObject().?.symbols.items[sym.sym_index];
- const macho_sym = macho_file.getSymbol(sym_index);
+ const zo = macho_file.getZigObject().?;
+ const macho_sym = zo.symbols.items[sym.sym_index];
if (macho_sym.flags.tlv) {
_ = lower.reloc(.{ .linker_reloc = sym });
diff --git a/src/codegen.zig b/src/codegen.zig
@@ -901,15 +901,16 @@ fn genDeclRef(
}
return GenResult.mcv(.{ .load_symbol = sym.esym_index });
} else if (lf.cast(link.File.MachO)) |macho_file| {
+ const zo = macho_file.getZigObject().?;
if (is_extern) {
const name = decl.name.toSlice(ip);
const lib_name = if (decl.getOwnedVariable(zcu)) |ov| ov.lib_name.toSlice(ip) else null;
const sym_index = try macho_file.getGlobalSymbol(name, lib_name);
- macho_file.getSymbol(macho_file.getZigObject().?.symbols.items[sym_index]).flags.needs_got = true;
+ zo.symbols.items[sym_index].flags.needs_got = true;
return GenResult.mcv(.{ .load_symbol = sym_index });
}
- const sym_index = try macho_file.getZigObject().?.getOrCreateMetadataForDecl(macho_file, decl_index);
- const sym = macho_file.getSymbol(sym_index);
+ const sym_index = try zo.getOrCreateMetadataForDecl(macho_file, decl_index);
+ const sym = zo.symbols.items[sym_index];
if (is_threadlocal) {
return GenResult.mcv(.{ .load_tlv = sym.nlist_idx });
}
@@ -956,7 +957,7 @@ fn genUnnamedConst(
},
.macho => {
const macho_file = lf.cast(link.File.MachO).?;
- const local = macho_file.getSymbol(local_sym_index);
+ const local = macho_file.getZigObject().?.symbols.items[local_sym_index];
return GenResult.mcv(.{ .load_symbol = local.nlist_idx });
},
.coff => {
diff --git a/src/link/MachO.zig b/src/link/MachO.zig
@@ -22,16 +22,11 @@ dylibs: std.ArrayListUnmanaged(File.Index) = .{},
segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
sections: std.MultiArrayList(Section) = .{},
-symbols: std.ArrayListUnmanaged(Symbol) = .{},
-symbols_extra: std.ArrayListUnmanaged(u32) = .{},
-symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{},
-globals: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{},
+resolver: SymbolResolver = .{},
/// This table will be populated after `scanRelocs` has run.
/// Key is symbol index.
-undefs: std.AutoHashMapUnmanaged(Symbol.Index, std.ArrayListUnmanaged(Atom.Index)) = .{},
-/// Global symbols we need to resolve for the link to succeed.
-undefined_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
-boundary_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
+undefs: std.AutoHashMapUnmanaged(SymbolResolver.Index, std.ArrayListUnmanaged(Ref)) = .{},
+dupes: std.AutoHashMapUnmanaged(SymbolResolver.Index, std.ArrayListUnmanaged(File.Index)) = .{},
dyld_info_cmd: macho.dyld_info_command = .{},
symtab_cmd: macho.symtab_command = .{},
@@ -55,22 +50,7 @@ eh_frame_sect_index: ?u8 = null,
unwind_info_sect_index: ?u8 = null,
objc_stubs_sect_index: ?u8 = null,
-mh_execute_header_index: ?Symbol.Index = null,
-mh_dylib_header_index: ?Symbol.Index = null,
-dyld_private_index: ?Symbol.Index = null,
-dyld_stub_binder_index: ?Symbol.Index = null,
-dso_handle_index: ?Symbol.Index = null,
-objc_msg_send_index: ?Symbol.Index = null,
-entry_index: ?Symbol.Index = null,
-
-/// List of atoms that are either synthetic or map directly to the Zig source program.
-atoms: std.ArrayListUnmanaged(Atom) = .{},
-atoms_extra: std.ArrayListUnmanaged(u32) = .{},
thunks: std.ArrayListUnmanaged(Thunk) = .{},
-unwind_records: std.ArrayListUnmanaged(UnwindInfo.Record) = .{},
-
-/// String interning table
-strings: StringTable = .{},
/// Output synthetic sections
symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
@@ -83,12 +63,13 @@ stubs_helper: StubsHelperSection = .{},
objc_stubs: ObjcStubsSection = .{},
la_symbol_ptr: LaSymbolPtrSection = .{},
tlv_ptr: TlvPtrSection = .{},
-rebase: RebaseSection = .{},
-bind: BindSection = .{},
-weak_bind: WeakBindSection = .{},
-lazy_bind: LazyBindSection = .{},
-export_trie: ExportTrieSection = .{},
+rebase: Rebase = .{},
+bind: Bind = .{},
+weak_bind: WeakBind = .{},
+lazy_bind: LazyBind = .{},
+export_trie: ExportTrie = .{},
unwind_info: UnwindInfo = .{},
+data_in_code: DataInCode = .{},
/// Tracked loadable segments during incremental linking.
zig_text_seg_index: ?u8 = null,
@@ -245,15 +226,8 @@ pub fn createEmpty(
// Append null file
try self.files.append(gpa, .null);
- // Atom at index 0 is reserved as null atom
- try self.atoms.append(gpa, .{});
- try self.atoms_extra.append(gpa, 0);
// Append empty string to string tables
- try self.strings.buffer.append(gpa, 0);
try self.strtab.append(gpa, 0);
- // Append null symbols
- try self.symbols.append(gpa, .{});
- try self.symbols_extra.append(gpa, 0);
if (opt_zcu) |zcu| {
if (!use_llvm) {
@@ -317,15 +291,20 @@ pub fn deinit(self: *MachO) void {
self.dylibs.deinit(gpa);
self.segments.deinit(gpa);
- for (self.sections.items(.atoms)) |*list| {
- list.deinit(gpa);
+ for (
+ self.sections.items(.atoms),
+ self.sections.items(.out),
+ self.sections.items(.thunks),
+ self.sections.items(.relocs),
+ ) |*atoms, *out, *thnks, *relocs| {
+ atoms.deinit(gpa);
+ out.deinit(gpa);
+ thnks.deinit(gpa);
+ relocs.deinit(gpa);
}
self.sections.deinit(gpa);
- self.symbols.deinit(gpa);
- self.symbols_extra.deinit(gpa);
- self.symbols_free_list.deinit(gpa);
- self.globals.deinit(gpa);
+ self.resolver.deinit(gpa);
{
var it = self.undefs.iterator();
while (it.next()) |entry| {
@@ -333,10 +312,14 @@ pub fn deinit(self: *MachO) void {
}
self.undefs.deinit(gpa);
}
- self.undefined_symbols.deinit(gpa);
- self.boundary_symbols.deinit(gpa);
+ {
+ var it = self.dupes.iterator();
+ while (it.next()) |entry| {
+ entry.value_ptr.deinit(gpa);
+ }
+ self.dupes.deinit(gpa);
+ }
- self.strings.deinit(gpa);
self.symtab.deinit(gpa);
self.strtab.deinit(gpa);
self.got.deinit(gpa);
@@ -350,14 +333,9 @@ pub fn deinit(self: *MachO) void {
self.lazy_bind.deinit(gpa);
self.export_trie.deinit(gpa);
self.unwind_info.deinit(gpa);
+ self.data_in_code.deinit(gpa);
- self.atoms.deinit(gpa);
- self.atoms_extra.deinit(gpa);
- for (self.thunks.items) |*thunk| {
- thunk.deinit(gpa);
- }
self.thunks.deinit(gpa);
- self.unwind_records.deinit(gpa);
}
pub fn flush(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
@@ -535,17 +513,14 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
self.files.set(index, .{ .internal = .{ .index = index } });
self.internal_object = index;
+ const object = self.getInternalObject().?;
+ try object.init(gpa);
+ try object.initSymbols(self);
}
- try self.addUndefinedGlobals();
try self.resolveSymbols();
- try self.parseDebugInfo();
- try self.resolveSyntheticSymbols();
-
- try self.convertTentativeDefinitions();
- try self.createObjcSections();
+ try self.convertTentativeDefsAndResolveSpecialSymbols();
try self.dedupLiterals();
- try self.claimUnresolved();
if (self.base.gc_sections) {
try dead_strip.gcAtoms(self);
@@ -567,6 +542,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
dylib.ordinal = @intCast(ord);
}
+ self.claimUnresolved();
+
self.scanRelocs() catch |err| switch (err) {
error.HasUndefinedSymbols => return error.FlushFailure,
else => |e| {
@@ -580,92 +557,38 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
try self.sortSections();
try self.addAtomsToSections();
try self.calcSectionSizes();
+
try self.generateUnwindInfo();
- try self.initSegments();
+ try self.initSegments();
try self.allocateSections();
self.allocateSegments();
self.allocateSyntheticSymbols();
- try self.allocateLinkeditSegment();
if (build_options.enable_logging) {
state_log.debug("{}", .{self.dumpState()});
}
- try self.initDyldInfoSections();
-
// Beyond this point, everything has been allocated a virtual address and we can resolve
// the relocations, and commit objects to file.
- if (self.getZigObject()) |zo| {
- var has_resolve_error = false;
-
- for (zo.atoms.items) |atom_index| {
- const atom = self.getAtom(atom_index) orelse continue;
- if (!atom.flags.alive) continue;
- const sect = &self.sections.items(.header)[atom.out_n_sect];
- if (sect.isZerofill()) continue;
- if (!self.isZigSection(atom.out_n_sect)) continue; // Non-Zig sections are handled separately
- if (atom.getRelocs(self).len == 0) continue;
- // TODO: we will resolve and write ZigObject's TLS data twice:
- // once here, and once in writeAtoms
- const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
- const code = try gpa.alloc(u8, atom_size);
- defer gpa.free(code);
- atom.getData(self, code) catch |err| switch (err) {
- error.InputOutput => {
- try self.reportUnexpectedError("fetching code for '{s}' failed", .{
- atom.getName(self),
- });
- return error.FlushFailure;
- },
- else => |e| {
- try self.reportUnexpectedError("unexpected error while fetching code for '{s}': {s}", .{
- atom.getName(self),
- @errorName(e),
- });
- return error.FlushFailure;
- },
- };
- const file_offset = sect.offset + atom.value;
- atom.resolveRelocs(self, code) catch |err| switch (err) {
- error.ResolveFailed => has_resolve_error = true,
- else => |e| {
- try self.reportUnexpectedError("unexpected error while resolving relocations", .{});
- return e;
- },
- };
- try self.base.file.?.pwriteAll(code, file_offset);
- }
+ try self.resizeSections();
- if (has_resolve_error) return error.FlushFailure;
+ if (self.getZigObject()) |zo| {
+ zo.resolveRelocs(self) catch |err| switch (err) {
+ error.ResolveFailed => return error.FlushFailure,
+ else => |e| return e,
+ };
}
-
- self.writeAtoms() catch |err| switch (err) {
- error.ResolveFailed => return error.FlushFailure,
- else => |e| {
- try self.reportUnexpectedError("unexpected error while resolving relocations", .{});
- return e;
- },
+ self.writeSectionsAndUpdateLinkeditSizes() catch |err| {
+ switch (err) {
+ error.ResolveFailed => return error.FlushFailure,
+ else => |e| return e,
+ }
};
- try self.writeUnwindInfo();
- try self.finalizeDyldInfoSections();
- try self.writeSyntheticSections();
-
- var off = math.cast(u32, self.getLinkeditSegment().fileoff) orelse return error.Overflow;
- off = try self.writeDyldInfoSections(off);
- off = mem.alignForward(u32, off, @alignOf(u64));
- off = try self.writeFunctionStarts(off);
- off = mem.alignForward(u32, off, @alignOf(u64));
- off = try self.writeDataInCode(self.getTextSegment().vmaddr, off);
- try self.calcSymtabSize();
- off = mem.alignForward(u32, off, @alignOf(u64));
- off = try self.writeSymtab(off);
- off = mem.alignForward(u32, off, @alignOf(u32));
- off = try self.writeIndsymtab(off);
- off = mem.alignForward(u32, off, @alignOf(u64));
- off = try self.writeStrtab(off);
- self.getLinkeditSegment().filesize = off - self.getLinkeditSegment().fileoff;
+ try self.writeSectionsToFile();
+ try self.allocateLinkeditSegment();
+ try self.writeLinkeditSectionsToFile();
var codesig: ?CodeSignature = if (self.requiresCodeSig()) blk: {
// Preallocate space for the code signature.
@@ -982,12 +905,15 @@ fn parseObject(self: *MachO, path: []const u8) ParseError!void {
break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));
};
const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
- self.files.set(index, .{ .object = .{
- .path = try gpa.dupe(u8, path),
- .file_handle = handle,
- .mtime = mtime,
- .index = index,
- } });
+ self.files.set(index, .{
+ .object = .{
+ .offset = 0, // TODO FAT objects
+ .path = try gpa.dupe(u8, path),
+ .file_handle = handle,
+ .mtime = mtime,
+ .index = index,
+ },
+ });
try self.objects.append(gpa, index);
const object = self.getFile(index).?.object;
@@ -1327,35 +1253,6 @@ fn parseDependentDylibs(self: *MachO) !void {
if (has_errors) return error.MissingLibraryDependencies;
}
-pub fn addUndefinedGlobals(self: *MachO) !void {
- const gpa = self.base.comp.gpa;
-
- try self.undefined_symbols.ensureUnusedCapacity(gpa, self.base.comp.force_undefined_symbols.keys().len);
- for (self.base.comp.force_undefined_symbols.keys()) |name| {
- const off = try self.strings.insert(gpa, name);
- const gop = try self.getOrCreateGlobal(off);
- self.undefined_symbols.appendAssumeCapacity(gop.index);
- }
-
- if (!self.base.isDynLib() and self.entry_name != null) {
- const off = try self.strings.insert(gpa, self.entry_name.?);
- const gop = try self.getOrCreateGlobal(off);
- self.entry_index = gop.index;
- }
-
- {
- const off = try self.strings.insert(gpa, "dyld_stub_binder");
- const gop = try self.getOrCreateGlobal(off);
- self.dyld_stub_binder_index = gop.index;
- }
-
- {
- const off = try self.strings.insert(gpa, "_objc_msgSend");
- const gop = try self.getOrCreateGlobal(off);
- self.objc_msg_send_index = gop.index;
- }
-}
-
/// When resolving symbols, we approach the problem similarly to `mold`.
/// 1. Resolve symbols across all objects (including those preemptively extracted archives).
/// 2. Resolve symbols across all shared objects.
@@ -1368,18 +1265,17 @@ pub fn resolveSymbols(self: *MachO) !void {
defer tracy.end();
// Resolve symbols in the ZigObject. For now, we assume that it's always live.
- if (self.getZigObject()) |zo| zo.asFile().resolveSymbols(self);
+ if (self.getZigObject()) |zo| try zo.asFile().resolveSymbols(self);
// Resolve symbols on the set of all objects and shared objects (even if some are unneeded).
- for (self.objects.items) |index| self.getFile(index).?.resolveSymbols(self);
- for (self.dylibs.items) |index| self.getFile(index).?.resolveSymbols(self);
+ for (self.objects.items) |index| try self.getFile(index).?.resolveSymbols(self);
+ for (self.dylibs.items) |index| try self.getFile(index).?.resolveSymbols(self);
+ if (self.getInternalObject()) |obj| try obj.resolveSymbols(self);
// Mark live objects.
self.markLive();
// Reset state of all globals after marking live objects.
- if (self.getZigObject()) |zo| zo.asFile().resetGlobals(self);
- for (self.objects.items) |index| self.getFile(index).?.resetGlobals(self);
- for (self.dylibs.items) |index| self.getFile(index).?.resetGlobals(self);
+ self.resolver.reset();
// Prune dead objects.
var i: usize = 0;
@@ -1393,37 +1289,26 @@ pub fn resolveSymbols(self: *MachO) !void {
}
// Re-resolve the symbols.
- if (self.getZigObject()) |zo| zo.resolveSymbols(self);
- for (self.objects.items) |index| self.getFile(index).?.resolveSymbols(self);
- for (self.dylibs.items) |index| self.getFile(index).?.resolveSymbols(self);
+ if (self.getZigObject()) |zo| try zo.resolveSymbols(self);
+ for (self.objects.items) |index| try self.getFile(index).?.resolveSymbols(self);
+ for (self.dylibs.items) |index| try self.getFile(index).?.resolveSymbols(self);
+ if (self.getInternalObject()) |obj| try obj.resolveSymbols(self);
+
+ // Merge symbol visibility
+ if (self.getZigObject()) |zo| zo.mergeSymbolVisibility(self);
+ for (self.objects.items) |index| self.getFile(index).?.object.mergeSymbolVisibility(self);
}
fn markLive(self: *MachO) void {
const tracy = trace(@src());
defer tracy.end();
- for (self.undefined_symbols.items) |index| {
- if (self.getSymbol(index).getFile(self)) |file| {
- if (file == .object) file.object.alive = true;
- }
- }
- if (self.entry_index) |index| {
- const sym = self.getSymbol(index);
- if (sym.getFile(self)) |file| {
- if (file == .object) file.object.alive = true;
- }
- }
if (self.getZigObject()) |zo| zo.markLive(self);
for (self.objects.items) |index| {
const object = self.getFile(index).?.object;
if (object.alive) object.markLive(self);
}
-}
-
-pub fn parseDebugInfo(self: *MachO) !void {
- for (self.objects.items) |index| {
- try self.getFile(index).?.object.parseDebugInfo(self);
- }
+ if (self.getInternalObject()) |obj| obj.markLive(self);
}
fn resolveSyntheticSymbols(self: *MachO) !void {
@@ -1473,10 +1358,14 @@ fn resolveSyntheticSymbols(self: *MachO) !void {
}
}
-fn convertTentativeDefinitions(self: *MachO) !void {
+fn convertTentativeDefsAndResolveSpecialSymbols(self: *MachO) !void {
for (self.objects.items) |index| {
try self.getFile(index).?.object.convertTentativeDefinitions(self);
}
+ if (self.getInternalObject()) |obj| {
+ try obj.resolveBoundarySymbols(self);
+ try obj.resolveObjcMsgSendSymbols(self);
+ }
}
fn createObjcSections(self: *MachO) !void {
@@ -1514,6 +1403,9 @@ fn createObjcSections(self: *MachO) !void {
}
pub fn dedupLiterals(self: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
const gpa = self.base.comp.gpa;
var lp: LiteralPool = .{};
defer lp.deinit(gpa);
@@ -1539,80 +1431,46 @@ pub fn dedupLiterals(self: *MachO) !void {
}
}
-fn claimUnresolved(self: *MachO) error{OutOfMemory}!void {
+fn claimUnresolved(self: *MachO) void {
if (self.getZigObject()) |zo| {
- try zo.asFile().claimUnresolved(self);
+ zo.asFile().claimUnresolved(self);
}
for (self.objects.items) |index| {
- try self.getFile(index).?.claimUnresolved(self);
+ self.getFile(index).?.claimUnresolved(self);
}
}
fn checkDuplicates(self: *MachO) !void {
- const gpa = self.base.comp.gpa;
-
- var dupes = std.AutoArrayHashMap(Symbol.Index, std.ArrayListUnmanaged(File.Index)).init(gpa);
- defer {
- for (dupes.values()) |*list| {
- list.deinit(gpa);
- }
- dupes.deinit();
- }
-
if (self.getZigObject()) |zo| {
- try zo.checkDuplicates(&dupes, self);
+ try zo.asFile().checkDuplicates(self);
}
-
for (self.objects.items) |index| {
- try self.getFile(index).?.object.checkDuplicates(&dupes, self);
+ try self.getFile(index).?.checkDuplicates(self);
}
-
- try self.reportDuplicates(dupes);
+ if (self.getInternalObject()) |obj| {
+ try obj.asFile().checkDuplicates(self);
+ }
+ try self.reportDuplicates();
}
fn markImportsAndExports(self: *MachO) void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
if (self.getZigObject()) |zo| {
zo.asFile().markImportsExports(self);
}
for (self.objects.items) |index| {
self.getFile(index).?.markImportsExports(self);
}
-
- for (self.undefined_symbols.items) |index| {
- const sym = self.getSymbol(index);
- if (sym.getFile(self)) |file| {
- if (sym.visibility != .global) continue;
- if (file == .dylib and !sym.flags.abs) sym.flags.import = true;
- }
- }
-
- for (&[_]?Symbol.Index{
- self.entry_index,
- self.dyld_stub_binder_index,
- self.objc_msg_send_index,
- }) |index| {
- if (index) |idx| {
- const sym = self.getSymbol(idx);
- if (sym.getFile(self)) |file| {
- if (file == .dylib) sym.flags.import = true;
- }
- }
+ if (self.getInternalObject()) |obj| {
+ obj.asFile().markImportsExports(self);
}
}
fn deadStripDylibs(self: *MachO) void {
- for (&[_]?Symbol.Index{
- self.entry_index,
- self.dyld_stub_binder_index,
- self.objc_msg_send_index,
- }) |index| {
- if (index) |idx| {
- const sym = self.getSymbol(idx);
- if (sym.getFile(self)) |file| {
- if (file == .dylib) file.dylib.referenced = true;
- }
- }
- }
+ const tracy = trace(@src());
+ defer tracy.end();
for (self.dylibs.items) |index| {
self.getFile(index).?.dylib.markReferenced(self);
@@ -1633,50 +1491,29 @@ fn scanRelocs(self: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
- if (self.getZigObject()) |zo| try zo.scanRelocs(self);
-
+ if (self.getZigObject()) |zo| {
+ try zo.scanRelocs(self);
+ }
for (self.objects.items) |index| {
try self.getFile(index).?.object.scanRelocs(self);
}
+ if (self.getInternalObject()) |obj| {
+ obj.scanRelocs(self);
+ }
try self.reportUndefs();
- if (self.entry_index) |index| {
- const sym = self.getSymbol(index);
- if (sym.getFile(self) != null) {
- if (sym.flags.import) sym.flags.stubs = true;
- }
+ if (self.getZigObject()) |zo| {
+ try zo.asFile().createSymbolIndirection(self);
}
-
- if (self.dyld_stub_binder_index) |index| {
- const sym = self.getSymbol(index);
- if (sym.getFile(self) != null) sym.flags.needs_got = true;
+ for (self.objects.items) |index| {
+ try self.getFile(index).?.createSymbolIndirection(self);
}
-
- if (self.objc_msg_send_index) |index| {
- const sym = self.getSymbol(index);
- if (sym.getFile(self) != null)
- sym.flags.needs_got = true; // TODO is it always needed, or only if we are synthesising fast stubs?
+ for (self.dylibs.items) |index| {
+ try self.getFile(index).?.createSymbolIndirection(self);
}
-
- for (self.symbols.items, 0..) |*symbol, i| {
- const index = @as(Symbol.Index, @intCast(i));
- if (symbol.flags.needs_got) {
- log.debug("'{s}' needs GOT", .{symbol.getName(self)});
- try self.got.addSymbol(index, self);
- }
- if (symbol.flags.stubs) {
- log.debug("'{s}' needs STUBS", .{symbol.getName(self)});
- try self.stubs.addSymbol(index, self);
- }
- if (symbol.flags.tlv_ptr) {
- log.debug("'{s}' needs TLV pointer", .{symbol.getName(self)});
- try self.tlv_ptr.addSymbol(index, self);
- }
- if (symbol.flags.objc_stubs) {
- log.debug("'{s}' needs OBJC STUBS", .{symbol.getName(self)});
- try self.objc_stubs.addSymbol(index, self);
- }
+ if (self.getInternalObject()) |obj| {
+ try obj.asFile().createSymbolIndirection(self);
}
}
@@ -1684,17 +1521,15 @@ fn reportUndefs(self: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
- switch (self.undefined_treatment) {
- .dynamic_lookup, .suppress => return,
- .@"error", .warn => {},
- }
+ if (self.undefined_treatment == .suppress or
+ self.undefined_treatment == .dynamic_lookup) return;
const max_notes = 4;
var has_undefs = false;
var it = self.undefs.iterator();
while (it.next()) |entry| {
- const undef_sym = self.getSymbol(entry.key_ptr.*);
+ const undef_sym = self.resolver.keys.items[entry.key_ptr.* - 1];
const notes = entry.value_ptr.*;
const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
@@ -1704,8 +1539,9 @@ fn reportUndefs(self: *MachO) !void {
var inote: usize = 0;
while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
- const atom = self.getAtom(notes.items[inote]).?;
- const file = atom.getFile(self);
+ const note = notes.items[inote];
+ const file = self.getFile(note.file).?;
+ const atom = note.getAtom(self).?;
try err.addNote(self, "referenced by {}:{s}", .{ file.fmtPath(), atom.getName(self) });
}
@@ -1714,64 +1550,18 @@ fn reportUndefs(self: *MachO) !void {
try err.addNote(self, "referenced {d} more times", .{remaining});
}
}
-
- for (self.undefined_symbols.items) |index| {
- const sym = self.getSymbol(index);
- if (sym.getFile(self) != null) continue; // If undefined in an object file, will be reported above
- has_undefs = true;
- var err = try self.addErrorWithNotes(1);
- try err.addMsg(self, "undefined symbol: {s}", .{sym.getName(self)});
- try err.addNote(self, "-u command line option", .{});
- }
-
- if (self.entry_index) |index| {
- const sym = self.getSymbol(index);
- if (sym.getFile(self) == null) {
- has_undefs = true;
- var err = try self.addErrorWithNotes(1);
- try err.addMsg(self, "undefined symbol: {s}", .{sym.getName(self)});
- try err.addNote(self, "implicit entry/start for main executable", .{});
- }
- }
-
- if (self.dyld_stub_binder_index) |index| {
- const sym = self.getSymbol(index);
- if (sym.getFile(self) == null and self.stubs_sect_index != null) {
- has_undefs = true;
- var err = try self.addErrorWithNotes(1);
- try err.addMsg(self, "undefined symbol: {s}", .{sym.getName(self)});
- try err.addNote(self, "implicit -u command line option", .{});
- }
- }
-
- if (self.objc_msg_send_index) |index| {
- const sym = self.getSymbol(index);
- if (sym.getFile(self) == null and self.objc_stubs_sect_index != null) {
- has_undefs = true;
- var err = try self.addErrorWithNotes(1);
- try err.addMsg(self, "undefined symbol: {s}", .{sym.getName(self)});
- try err.addNote(self, "implicit -u command line option", .{});
- }
- }
-
if (has_undefs) return error.HasUndefinedSymbols;
}
fn initOutputSections(self: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
for (self.objects.items) |index| {
- const object = self.getFile(index).?.object;
- for (object.atoms.items) |atom_index| {
- const atom = self.getAtom(atom_index) orelse continue;
- if (!atom.flags.alive) continue;
- atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(self), self);
- }
+ try self.getFile(index).?.initOutputSections(self);
}
- if (self.getInternalObject()) |object| {
- for (object.atoms.items) |atom_index| {
- const atom = self.getAtom(atom_index) orelse continue;
- if (!atom.flags.alive) continue;
- atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(self), self);
- }
+ if (self.getInternalObject()) |obj| {
+ try obj.asFile().initOutputSections(self);
}
self.text_sect_index = self.getSectionByName("__TEXT", "__text") orelse
try self.addSection("__TEXT", "__text", .{
@@ -1844,46 +1634,50 @@ fn initSyntheticSections(self: *MachO) !void {
self.eh_frame_sect_index = try self.addSection("__TEXT", "__eh_frame", .{});
}
- for (self.boundary_symbols.items) |sym_index| {
+ if (self.getInternalObject()) |obj| {
const gpa = self.base.comp.gpa;
- const sym = self.getSymbol(sym_index);
- const name = sym.getName(self);
-
- if (eatPrefix(name, "segment$start$")) |segname| {
- if (self.getSegmentByName(segname) == null) { // TODO check segname is valid
- const prot = getSegmentProt(segname);
- _ = try self.segments.append(gpa, .{
- .cmdsize = @sizeOf(macho.segment_command_64),
- .segname = makeStaticString(segname),
- .initprot = prot,
- .maxprot = prot,
- });
- }
- } else if (eatPrefix(name, "segment$stop$")) |segname| {
- if (self.getSegmentByName(segname) == null) { // TODO check segname is valid
- const prot = getSegmentProt(segname);
- _ = try self.segments.append(gpa, .{
- .cmdsize = @sizeOf(macho.segment_command_64),
- .segname = makeStaticString(segname),
- .initprot = prot,
- .maxprot = prot,
- });
- }
- } else if (eatPrefix(name, "section$start$")) |actual_name| {
- const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
- const segname = actual_name[0..sep]; // TODO check segname is valid
- const sectname = actual_name[sep + 1 ..]; // TODO check sectname is valid
- if (self.getSectionByName(segname, sectname) == null) {
- _ = try self.addSection(segname, sectname, .{});
- }
- } else if (eatPrefix(name, "section$stop$")) |actual_name| {
- const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
- const segname = actual_name[0..sep]; // TODO check segname is valid
- const sectname = actual_name[sep + 1 ..]; // TODO check sectname is valid
- if (self.getSectionByName(segname, sectname) == null) {
- _ = try self.addSection(segname, sectname, .{});
- }
- } else unreachable;
+
+ for (obj.boundary_symbols.items) |sym_index| {
+ const ref = obj.getSymbolRef(sym_index, self);
+ const sym = ref.getSymbol(self).?;
+ const name = sym.getName(self);
+
+ if (eatPrefix(name, "segment$start$")) |segname| {
+ if (self.getSegmentByName(segname) == null) { // TODO check segname is valid
+ const prot = getSegmentProt(segname);
+ _ = try self.segments.append(gpa, .{
+ .cmdsize = @sizeOf(macho.segment_command_64),
+ .segname = makeStaticString(segname),
+ .initprot = prot,
+ .maxprot = prot,
+ });
+ }
+ } else if (eatPrefix(name, "segment$stop$")) |segname| {
+ if (self.getSegmentByName(segname) == null) { // TODO check segname is valid
+ const prot = getSegmentProt(segname);
+ _ = try self.segments.append(gpa, .{
+ .cmdsize = @sizeOf(macho.segment_command_64),
+ .segname = makeStaticString(segname),
+ .initprot = prot,
+ .maxprot = prot,
+ });
+ }
+ } else if (eatPrefix(name, "section$start$")) |actual_name| {
+ const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
+ const segname = actual_name[0..sep]; // TODO check segname is valid
+ const sectname = actual_name[sep + 1 ..]; // TODO check sectname is valid
+ if (self.getSectionByName(segname, sectname) == null) {
+ _ = try self.addSection(segname, sectname, .{});
+ }
+ } else if (eatPrefix(name, "section$stop$")) |actual_name| {
+ const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
+ const segname = actual_name[0..sep]; // TODO check segname is valid
+ const sectname = actual_name[sep + 1 ..]; // TODO check sectname is valid
+ if (self.getSectionByName(segname, sectname) == null) {
+ _ = try self.addSection(segname, sectname, .{});
+ }
+ } else unreachable;
+ }
}
}
@@ -1990,38 +1784,25 @@ pub fn sortSections(self: *MachO) !void {
}
if (self.getZigObject()) |zo| {
- for (zo.atoms.items) |atom_index| {
- const atom = self.getAtom(atom_index) orelse continue;
+ for (zo.getAtoms()) |atom_index| {
+ const atom = zo.getAtom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
atom.out_n_sect = backlinks[atom.out_n_sect];
}
-
- for (zo.symtab.items(.nlist)) |*sym| {
- if (sym.sect()) {
- sym.n_sect = backlinks[sym.n_sect - 1] + 1;
- }
- }
-
- for (zo.symbols.items) |sym_index| {
- const sym = self.getSymbol(sym_index);
- const atom = sym.getAtom(self) orelse continue;
- if (!atom.flags.alive) continue;
- if (sym.getFile(self).?.getIndex() != zo.index) continue;
- sym.out_n_sect = backlinks[sym.out_n_sect];
- }
}
for (self.objects.items) |index| {
- for (self.getFile(index).?.object.atoms.items) |atom_index| {
- const atom = self.getAtom(atom_index) orelse continue;
+ const file = self.getFile(index).?;
+ for (file.getAtoms()) |atom_index| {
+ const atom = file.getAtom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
atom.out_n_sect = backlinks[atom.out_n_sect];
}
}
if (self.getInternalObject()) |object| {
- for (object.atoms.items) |atom_index| {
- const atom = self.getAtom(atom_index) orelse continue;
+ for (object.getAtoms()) |atom_index| {
+ const atom = object.getAtom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
atom.out_n_sect = backlinks[atom.out_n_sect];
}
@@ -2058,35 +1839,32 @@ pub fn addAtomsToSections(self: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
- for (self.objects.items) |index| {
- const object = self.getFile(index).?.object;
- for (object.atoms.items) |atom_index| {
- const atom = self.getAtom(atom_index) orelse continue;
+ const gpa = self.base.comp.gpa;
+
+ if (self.getZigObject()) |zo| {
+ for (zo.getAtoms()) |atom_index| {
+ const atom = zo.getAtom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
+ if (self.isZigSection(atom.out_n_sect)) continue;
const atoms = &self.sections.items(.atoms)[atom.out_n_sect];
- try atoms.append(self.base.comp.gpa, atom_index);
+ try atoms.append(gpa, .{ .index = atom_index, .file = zo.index });
}
- for (object.symbols.items) |sym_index| {
- const sym = self.getSymbol(sym_index);
- const atom = sym.getAtom(self) orelse continue;
+ }
+ for (self.objects.items) |index| {
+ const file = self.getFile(index).?;
+ for (file.getAtoms()) |atom_index| {
+ const atom = file.getAtom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
- if (sym.getFile(self).?.getIndex() != index) continue;
- sym.out_n_sect = atom.out_n_sect;
+ const atoms = &self.sections.items(.atoms)[atom.out_n_sect];
+ try atoms.append(gpa, .{ .index = atom_index, .file = index });
}
}
if (self.getInternalObject()) |object| {
- for (object.atoms.items) |atom_index| {
- const atom = self.getAtom(atom_index) orelse continue;
+ for (object.getAtoms()) |atom_index| {
+ const atom = object.getAtom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
const atoms = &self.sections.items(.atoms)[atom.out_n_sect];
- try atoms.append(self.base.comp.gpa, atom_index);
- }
- for (object.symbols.items) |sym_index| {
- const sym = self.getSymbol(sym_index);
- const atom = sym.getAtom(self) orelse continue;
- if (!atom.flags.alive) continue;
- if (sym.getFile(self).?.getIndex() != object.index) continue;
- sym.out_n_sect = atom.out_n_sect;
+ try atoms.append(gpa, .{ .index = atom_index, .file = object.index });
}
}
}
@@ -2108,8 +1886,8 @@ fn calcSectionSizes(self: *MachO) !void {
if (atoms.items.len == 0) continue;
if (self.requiresThunks() and header.isCode()) continue;
- for (atoms.items) |atom_index| {
- const atom = self.getAtom(atom_index).?;
+ for (atoms.items) |ref| {
+ const atom = ref.getAtom(self).?;
const atom_alignment = atom.alignment.toByteUnits() orelse 1;
const offset = mem.alignForward(u64, header.size, atom_alignment);
const padding = offset - header.size;
@@ -2129,6 +1907,22 @@ fn calcSectionSizes(self: *MachO) !void {
}
}
+ // At this point, we can also calculate symtab and data-in-code linkedit section sizes
+ if (self.getZigObject()) |zo| {
+ zo.asFile().calcSymtabSize(self);
+ }
+ for (self.objects.items) |index| {
+ self.getFile(index).?.calcSymtabSize(self);
+ }
+ for (self.dylibs.items) |index| {
+ self.getFile(index).?.calcSymtabSize(self);
+ }
+ if (self.getInternalObject()) |obj| {
+ obj.asFile().calcSymtabSize(self);
+ }
+
+ try self.calcSymtabSize();
+
if (self.got_sect_index) |idx| {
const header = &self.sections.items(.header)[idx];
header.size = self.got.size();
@@ -2409,77 +2203,61 @@ fn allocateSegments(self: *MachO) void {
}
fn allocateSyntheticSymbols(self: *MachO) void {
- const text_seg = self.getTextSegment();
-
- if (self.mh_execute_header_index) |index| {
- const global = self.getSymbol(index);
- global.value = text_seg.vmaddr;
- }
+ if (self.getInternalObject()) |obj| {
+ obj.allocateSyntheticSymbols(self);
- if (self.data_sect_index) |idx| {
- const sect = self.sections.items(.header)[idx];
- for (&[_]?Symbol.Index{
- self.dso_handle_index,
- self.mh_dylib_header_index,
- self.dyld_private_index,
- }) |maybe_index| {
- if (maybe_index) |index| {
- const global = self.getSymbol(index);
- global.value = sect.addr;
- global.out_n_sect = idx;
- }
- }
- }
+ const text_seg = self.getTextSegment();
- for (self.boundary_symbols.items) |sym_index| {
- const sym = self.getSymbol(sym_index);
- const name = sym.getName(self);
+ for (obj.boundary_symbols.items) |sym_index| {
+ const ref = obj.getSymbolRef(sym_index, self);
+ const sym = ref.getSymbol(self).?;
+ const name = sym.getName(self);
- sym.flags.@"export" = false;
- sym.value = text_seg.vmaddr;
+ sym.value = text_seg.vmaddr;
- if (mem.startsWith(u8, name, "segment$start$")) {
- const segname = name["segment$start$".len..];
- if (self.getSegmentByName(segname)) |seg_id| {
- const seg = self.segments.items[seg_id];
- sym.value = seg.vmaddr;
- }
- } else if (mem.startsWith(u8, name, "segment$stop$")) {
- const segname = name["segment$stop$".len..];
- if (self.getSegmentByName(segname)) |seg_id| {
- const seg = self.segments.items[seg_id];
- sym.value = seg.vmaddr + seg.vmsize;
- }
- } else if (mem.startsWith(u8, name, "section$start$")) {
- const actual_name = name["section$start$".len..];
- const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
- const segname = actual_name[0..sep];
- const sectname = actual_name[sep + 1 ..];
- if (self.getSectionByName(segname, sectname)) |sect_id| {
- const sect = self.sections.items(.header)[sect_id];
- sym.value = sect.addr;
- sym.out_n_sect = sect_id;
- }
- } else if (mem.startsWith(u8, name, "section$stop$")) {
- const actual_name = name["section$stop$".len..];
- const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
- const segname = actual_name[0..sep];
- const sectname = actual_name[sep + 1 ..];
- if (self.getSectionByName(segname, sectname)) |sect_id| {
- const sect = self.sections.items(.header)[sect_id];
- sym.value = sect.addr + sect.size;
- sym.out_n_sect = sect_id;
- }
- } else unreachable;
- }
+ if (mem.startsWith(u8, name, "segment$start$")) {
+ const segname = name["segment$start$".len..];
+ if (self.getSegmentByName(segname)) |seg_id| {
+ const seg = self.segments.items[seg_id];
+ sym.value = seg.vmaddr;
+ }
+ } else if (mem.startsWith(u8, name, "segment$stop$")) {
+ const segname = name["segment$stop$".len..];
+ if (self.getSegmentByName(segname)) |seg_id| {
+ const seg = self.segments.items[seg_id];
+ sym.value = seg.vmaddr + seg.vmsize;
+ }
+ } else if (mem.startsWith(u8, name, "section$start$")) {
+ const actual_name = name["section$start$".len..];
+ const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
+ const segname = actual_name[0..sep];
+ const sectname = actual_name[sep + 1 ..];
+ if (self.getSectionByName(segname, sectname)) |sect_id| {
+ const sect = self.sections.items(.header)[sect_id];
+ sym.value = sect.addr;
+ sym.out_n_sect = sect_id;
+ }
+ } else if (mem.startsWith(u8, name, "section$stop$")) {
+ const actual_name = name["section$stop$".len..];
+ const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
+ const segname = actual_name[0..sep];
+ const sectname = actual_name[sep + 1 ..];
+ if (self.getSectionByName(segname, sectname)) |sect_id| {
+ const sect = self.sections.items(.header)[sect_id];
+ sym.value = sect.addr + sect.size;
+ sym.out_n_sect = sect_id;
+ }
+ } else unreachable;
+ }
- if (self.objc_stubs.symbols.items.len > 0) {
- const addr = self.sections.items(.header)[self.objc_stubs_sect_index.?].addr;
+ if (self.objc_stubs.symbols.items.len > 0) {
+ const addr = self.sections.items(.header)[self.objc_stubs_sect_index.?].addr;
- for (self.objc_stubs.symbols.items, 0..) |sym_index, idx| {
- const sym = self.getSymbol(sym_index);
- sym.value = addr + idx * ObjcStubsSection.entrySize(self.getTarget().cpu.arch);
- sym.out_n_sect = self.objc_stubs_sect_index.?;
+ for (self.objc_stubs.symbols.items, 0..) |ref, idx| {
+ const sym = ref.getSymbol(self).?;
+ sym.value = addr + idx * ObjcStubsSection.entrySize(self.getTarget().cpu.arch);
+ sym.out_n_sect = self.objc_stubs_sect_index.?;
+ }
}
}
}
@@ -2497,269 +2275,228 @@ fn allocateLinkeditSegment(self: *MachO) !void {
const seg = self.getLinkeditSegment();
seg.vmaddr = mem.alignForward(u64, vmaddr, page_size);
seg.fileoff = mem.alignForward(u64, fileoff, page_size);
-}
-
-fn initDyldInfoSections(self: *MachO) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- const gpa = self.base.comp.gpa;
- if (self.zig_got_sect_index != null) try self.zig_got.addDyldRelocs(self);
- if (self.got_sect_index != null) try self.got.addDyldRelocs(self);
- if (self.tlv_ptr_sect_index != null) try self.tlv_ptr.addDyldRelocs(self);
- if (self.la_symbol_ptr_sect_index != null) try self.la_symbol_ptr.addDyldRelocs(self);
- try self.initExportTrie();
-
- var objects = try std.ArrayList(File.Index).initCapacity(gpa, self.objects.items.len + 1);
- defer objects.deinit();
- if (self.getZigObject()) |zo| objects.appendAssumeCapacity(zo.index);
- objects.appendSliceAssumeCapacity(self.objects.items);
-
- var nrebases: usize = 0;
- var nbinds: usize = 0;
- var nweak_binds: usize = 0;
- for (objects.items) |index| {
- const ctx = switch (self.getFile(index).?) {
- .zig_object => |x| x.dynamic_relocs,
- .object => |x| x.dynamic_relocs,
- else => unreachable,
- };
- nrebases += ctx.rebase_relocs;
- nbinds += ctx.bind_relocs;
- nweak_binds += ctx.weak_bind_relocs;
- }
- if (self.getInternalObject()) |int| {
- nrebases += int.num_rebase_relocs;
+ var off = math.cast(u32, seg.fileoff) orelse return error.Overflow;
+ // DYLD_INFO_ONLY
+ {
+ const cmd = &self.dyld_info_cmd;
+ cmd.rebase_off = off;
+ off += cmd.rebase_size;
+ cmd.bind_off = off;
+ off += cmd.bind_size;
+ cmd.weak_bind_off = off;
+ off += cmd.weak_bind_size;
+ cmd.lazy_bind_off = off;
+ off += cmd.lazy_bind_size;
+ cmd.export_off = off;
+ off += cmd.export_size;
+ off = mem.alignForward(u32, off, @alignOf(u64));
+ }
+
+ // FUNCTION_STARTS
+ {
+ const cmd = &self.function_starts_cmd;
+ cmd.dataoff = off;
+ off += cmd.datasize;
+ off = mem.alignForward(u32, off, @alignOf(u64));
}
- try self.rebase.entries.ensureUnusedCapacity(gpa, nrebases);
- try self.bind.entries.ensureUnusedCapacity(gpa, nbinds);
- try self.weak_bind.entries.ensureUnusedCapacity(gpa, nweak_binds);
-}
-
-fn initExportTrie(self: *MachO) !void {
- const tracy = trace(@src());
- defer tracy.end();
- const gpa = self.base.comp.gpa;
- try self.export_trie.init(gpa);
+ // DATA_IN_CODE
+ {
+ const cmd = &self.data_in_code_cmd;
+ cmd.dataoff = off;
+ off += cmd.datasize;
+ off = mem.alignForward(u32, off, @alignOf(u64));
+ }
- const seg = self.getTextSegment();
- for (self.objects.items) |index| {
- for (self.getFile(index).?.getSymbols()) |sym_index| {
- const sym = self.getSymbol(sym_index);
- if (!sym.flags.@"export") continue;
- if (sym.getAtom(self)) |atom| if (!atom.flags.alive) continue;
- if (sym.getFile(self).?.getIndex() != index) continue;
- var flags: u64 = if (sym.flags.abs)
- macho.EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
- else if (sym.flags.tlv)
- macho.EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL
- else
- macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR;
- if (sym.flags.weak) {
- flags |= macho.EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION;
- self.weak_defines = true;
- self.binds_to_weak = true;
- }
- try self.export_trie.put(gpa, .{
- .name = sym.getName(self),
- .vmaddr_offset = sym.getAddress(.{ .stubs = false }, self) - seg.vmaddr,
- .export_flags = flags,
- });
- }
+ // SYMTAB (symtab)
+ {
+ const cmd = &self.symtab_cmd;
+ cmd.symoff = off;
+ off += cmd.nsyms * @sizeOf(macho.nlist_64);
+ off = mem.alignForward(u32, off, @alignOf(u32));
}
- if (self.mh_execute_header_index) |index| {
- const sym = self.getSymbol(index);
- try self.export_trie.put(gpa, .{
- .name = sym.getName(self),
- .vmaddr_offset = sym.getAddress(.{}, self) - seg.vmaddr,
- .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
- });
+ // DYSYMTAB
+ {
+ const cmd = &self.dysymtab_cmd;
+ cmd.indirectsymoff = off;
+ off += cmd.nindirectsyms * @sizeOf(u32);
+ off = mem.alignForward(u32, off, @alignOf(u64));
}
-}
-fn writeAtoms(self: *MachO) !void {
- const tracy = trace(@src());
- defer tracy.end();
+ // SYMTAB (strtab)
+ {
+ const cmd = &self.symtab_cmd;
+ cmd.stroff = off;
+ off += cmd.strsize;
+ }
- const gpa = self.base.comp.gpa;
- var arena = std.heap.ArenaAllocator.init(gpa);
- defer arena.deinit();
+ seg.filesize = off - seg.fileoff;
+}
- const cpu_arch = self.getTarget().cpu.arch;
+fn resizeSections(self: *MachO) !void {
const slice = self.sections.slice();
-
- var has_resolve_error = false;
- for (slice.items(.header), slice.items(.atoms)) |header, atoms| {
- if (atoms.items.len == 0) continue;
+ for (slice.items(.header), slice.items(.out), 0..) |header, *out, n_sect| {
if (header.isZerofill()) continue;
-
+ if (self.isZigSection(@intCast(n_sect))) continue; // TODO this is horrible
+ const cpu_arch = self.getTarget().cpu.arch;
const size = math.cast(usize, header.size) orelse return error.Overflow;
- const buffer = try gpa.alloc(u8, size);
- defer gpa.free(buffer);
+ try out.resize(self.base.comp.gpa, size);
const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0;
- @memset(buffer, padding_byte);
-
- for (atoms.items) |atom_index| {
- const atom = self.getAtom(atom_index).?;
- assert(atom.flags.alive);
- const off = math.cast(usize, atom.value) orelse return error.Overflow;
- const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
- try atom.getData(self, buffer[off..][0..atom_size]);
- atom.resolveRelocs(self, buffer[off..][0..atom_size]) catch |err| switch (err) {
- error.ResolveFailed => has_resolve_error = true,
- else => |e| return e,
- };
- }
-
- try self.base.file.?.pwriteAll(buffer, header.offset);
+ @memset(out.items, padding_byte);
}
+}
+
+fn writeSectionsAndUpdateLinkeditSizes(self: *MachO) !void {
+ const gpa = self.base.comp.gpa;
+ const cmd = self.symtab_cmd;
+ try self.symtab.resize(gpa, cmd.nsyms);
+ try self.strtab.resize(gpa, cmd.strsize);
+ self.strtab.items[0] = 0;
+
+ for (self.objects.items) |index| {
+ try self.getFile(index).?.writeAtoms(self);
+ }
+ if (self.getZigObject()) |zo| {
+ try zo.writeAtoms(self);
+ }
+ if (self.getInternalObject()) |obj| {
+ try obj.asFile().writeAtoms(self);
+ }
for (self.thunks.items) |thunk| {
- const header = slice.items(.header)[thunk.out_n_sect];
- const offset = thunk.value + header.offset;
- const buffer = try gpa.alloc(u8, thunk.size());
- defer gpa.free(buffer);
- var stream = std.io.fixedBufferStream(buffer);
+ const out = self.sections.items(.out)[thunk.out_n_sect].items;
+ const off = math.cast(usize, thunk.value) orelse return error.Overflow;
+ const size = thunk.size();
+ var stream = std.io.fixedBufferStream(out[off..][0..size]);
try thunk.write(self, stream.writer());
- try self.base.file.?.pwriteAll(buffer, offset);
}
- if (has_resolve_error) return error.ResolveFailed;
-}
+ const slice = self.sections.slice();
+ for (&[_]?u8{
+ self.eh_frame_sect_index,
+ self.unwind_info_sect_index,
+ self.got_sect_index,
+ self.stubs_sect_index,
+ self.la_symbol_ptr_sect_index,
+ self.tlv_ptr_sect_index,
+ self.objc_stubs_sect_index,
+ }) |maybe_sect_id| {
+ if (maybe_sect_id) |sect_id| {
+ const out = slice.items(.out)[sect_id].items;
+ try self.writeSyntheticSection(sect_id, out);
+ }
+ }
-fn writeUnwindInfo(self: *MachO) !void {
- const tracy = trace(@src());
- defer tracy.end();
+ if (self.la_symbol_ptr_sect_index) |_| {
+ try self.updateLazyBindSize();
+ }
- const gpa = self.base.comp.gpa;
+ try self.rebase.updateSize(self);
+ try self.bind.updateSize(self);
+ try self.weak_bind.updateSize(self);
+ try self.export_trie.updateSize(self);
+ try self.data_in_code.updateSize(self);
- if (self.eh_frame_sect_index) |index| {
- const header = self.sections.items(.header)[index];
- const size = math.cast(usize, header.size) orelse return error.Overflow;
- const buffer = try gpa.alloc(u8, size);
- defer gpa.free(buffer);
- eh_frame.write(self, buffer);
- try self.base.file.?.pwriteAll(buffer, header.offset);
+ if (self.getZigObject()) |zo| {
+ zo.asFile().writeSymtab(self, self);
}
-
- if (self.unwind_info_sect_index) |index| {
- const header = self.sections.items(.header)[index];
- const size = math.cast(usize, header.size) orelse return error.Overflow;
- const buffer = try gpa.alloc(u8, size);
- defer gpa.free(buffer);
- try self.unwind_info.write(self, buffer);
- try self.base.file.?.pwriteAll(buffer, header.offset);
+ for (self.objects.items) |index| {
+ self.getFile(index).?.writeSymtab(self, self);
+ }
+ for (self.dylibs.items) |index| {
+ self.getFile(index).?.writeSymtab(self, self);
+ }
+ if (self.getInternalObject()) |obj| {
+ obj.asFile().writeSymtab(self, self);
}
}
-fn finalizeDyldInfoSections(self: *MachO) !void {
+fn writeSyntheticSection(self: *MachO, sect_id: u8, out: []u8) !void {
const tracy = trace(@src());
defer tracy.end();
- const gpa = self.base.comp.gpa;
- try self.rebase.finalize(gpa);
- try self.bind.finalize(gpa, self);
- try self.weak_bind.finalize(gpa, self);
- try self.lazy_bind.finalize(gpa, self);
- try self.export_trie.finalize(gpa);
+ const Tag = enum {
+ eh_frame,
+ unwind_info,
+ got,
+ stubs,
+ la_symbol_ptr,
+ tlv_ptr,
+ objc_stubs,
+ };
+
+ const tag: Tag = tag: {
+ if (self.eh_frame_sect_index != null and
+ self.eh_frame_sect_index.? == sect_id) break :tag .eh_frame;
+ if (self.unwind_info_sect_index != null and
+ self.unwind_info_sect_index.? == sect_id) break :tag .unwind_info;
+ if (self.got_sect_index != null and
+ self.got_sect_index.? == sect_id) break :tag .got;
+ if (self.stubs_sect_index != null and
+ self.stubs_sect_index.? == sect_id) break :tag .stubs;
+ if (self.la_symbol_ptr_sect_index != null and
+ self.la_symbol_ptr_sect_index.? == sect_id) break :tag .la_symbol_ptr;
+ if (self.tlv_ptr_sect_index != null and
+ self.tlv_ptr_sect_index.? == sect_id) break :tag .tlv_ptr;
+ if (self.objc_stubs_sect_index != null and
+ self.objc_stubs_sect_index.? == sect_id) break :tag .objc_stubs;
+ unreachable;
+ };
+ var stream = std.io.fixedBufferStream(out);
+ switch (tag) {
+ .eh_frame => eh_frame.write(self, out),
+ .unwind_info => try self.unwind_info.write(self, out),
+ .got => try self.got.write(self, stream.writer()),
+ .stubs => try self.stubs.write(self, stream.writer()),
+ .la_symbol_ptr => try self.la_symbol_ptr.write(self, stream.writer()),
+ .tlv_ptr => try self.tlv_ptr.write(self, stream.writer()),
+ .objc_stubs => try self.objc_stubs.write(self, stream.writer()),
+ }
}
-fn writeSyntheticSections(self: *MachO) !void {
+fn updateLazyBindSize(self: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
+ try self.lazy_bind.updateSize(self);
+ const sect_id = self.stubs_helper_sect_index.?;
+ const out = &self.sections.items(.out)[sect_id];
+ var stream = std.io.fixedBufferStream(out.items);
+ try self.stubs_helper.write(self, stream.writer());
+}
- const gpa = self.base.comp.gpa;
-
- if (self.got_sect_index) |sect_id| {
- const header = self.sections.items(.header)[sect_id];
- const size = math.cast(usize, header.size) orelse return error.Overflow;
- var buffer = try std.ArrayList(u8).initCapacity(gpa, size);
- defer buffer.deinit();
- try self.got.write(self, buffer.writer());
- assert(buffer.items.len == header.size);
- try self.base.file.?.pwriteAll(buffer.items, header.offset);
- }
-
- if (self.stubs_sect_index) |sect_id| {
- const header = self.sections.items(.header)[sect_id];
- const size = math.cast(usize, header.size) orelse return error.Overflow;
- var buffer = try std.ArrayList(u8).initCapacity(gpa, size);
- defer buffer.deinit();
- try self.stubs.write(self, buffer.writer());
- assert(buffer.items.len == header.size);
- try self.base.file.?.pwriteAll(buffer.items, header.offset);
- }
-
- if (self.stubs_helper_sect_index) |sect_id| {
- const header = self.sections.items(.header)[sect_id];
- const size = math.cast(usize, header.size) orelse return error.Overflow;
- var buffer = try std.ArrayList(u8).initCapacity(gpa, size);
- defer buffer.deinit();
- try self.stubs_helper.write(self, buffer.writer());
- assert(buffer.items.len == header.size);
- try self.base.file.?.pwriteAll(buffer.items, header.offset);
- }
-
- if (self.la_symbol_ptr_sect_index) |sect_id| {
- const header = self.sections.items(.header)[sect_id];
- const size = math.cast(usize, header.size) orelse return error.Overflow;
- var buffer = try std.ArrayList(u8).initCapacity(gpa, size);
- defer buffer.deinit();
- try self.la_symbol_ptr.write(self, buffer.writer());
- assert(buffer.items.len == header.size);
- try self.base.file.?.pwriteAll(buffer.items, header.offset);
- }
+fn writeSectionsToFile(self: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
- if (self.tlv_ptr_sect_index) |sect_id| {
- const header = self.sections.items(.header)[sect_id];
- const size = math.cast(usize, header.size) orelse return error.Overflow;
- var buffer = try std.ArrayList(u8).initCapacity(gpa, size);
- defer buffer.deinit();
- try self.tlv_ptr.write(self, buffer.writer());
- assert(buffer.items.len == header.size);
- try self.base.file.?.pwriteAll(buffer.items, header.offset);
+ const slice = self.sections.slice();
+ for (slice.items(.header), slice.items(.out)) |header, out| {
+ try self.base.file.?.pwriteAll(out.items, header.offset);
}
+}
- if (self.objc_stubs_sect_index) |sect_id| {
- const header = self.sections.items(.header)[sect_id];
- const size = math.cast(usize, header.size) orelse return error.Overflow;
- var buffer = try std.ArrayList(u8).initCapacity(gpa, size);
- defer buffer.deinit();
- try self.objc_stubs.write(self, buffer.writer());
- assert(buffer.items.len == header.size);
- try self.base.file.?.pwriteAll(buffer.items, header.offset);
- }
+fn writeLinkeditSectionsToFile(self: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+ try self.writeDyldInfo();
+ try self.writeDataInCode();
+ try self.writeSymtabToFile();
+ try self.writeIndsymtab();
}
-fn writeDyldInfoSections(self: *MachO, off: u32) !u32 {
+fn writeDyldInfo(self: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
const gpa = self.base.comp.gpa;
- const cmd = &self.dyld_info_cmd;
+ const base_off = self.getLinkeditSegment().fileoff;
+ const cmd = self.dyld_info_cmd;
var needed_size: u32 = 0;
-
- cmd.rebase_off = needed_size;
- cmd.rebase_size = mem.alignForward(u32, @intCast(self.rebase.size()), @alignOf(u64));
needed_size += cmd.rebase_size;
-
- cmd.bind_off = needed_size;
- cmd.bind_size = mem.alignForward(u32, @intCast(self.bind.size()), @alignOf(u64));
needed_size += cmd.bind_size;
-
- cmd.weak_bind_off = needed_size;
- cmd.weak_bind_size = mem.alignForward(u32, @intCast(self.weak_bind.size()), @alignOf(u64));
needed_size += cmd.weak_bind_size;
-
- cmd.lazy_bind_off = needed_size;
- cmd.lazy_bind_size = mem.alignForward(u32, @intCast(self.lazy_bind.size()), @alignOf(u64));
needed_size += cmd.lazy_bind_size;
-
- cmd.export_off = needed_size;
- cmd.export_size = mem.alignForward(u32, @intCast(self.export_trie.size), @alignOf(u64));
needed_size += cmd.export_size;
const buffer = try gpa.alloc(u8, needed_size);
@@ -2770,89 +2507,78 @@ fn writeDyldInfoSections(self: *MachO, off: u32) !u32 {
const writer = stream.writer();
try self.rebase.write(writer);
- try stream.seekTo(cmd.bind_off);
+ try stream.seekTo(cmd.bind_off - base_off);
try self.bind.write(writer);
- try stream.seekTo(cmd.weak_bind_off);
+ try stream.seekTo(cmd.weak_bind_off - base_off);
try self.weak_bind.write(writer);
- try stream.seekTo(cmd.lazy_bind_off);
+ try stream.seekTo(cmd.lazy_bind_off - base_off);
try self.lazy_bind.write(writer);
- try stream.seekTo(cmd.export_off);
+ try stream.seekTo(cmd.export_off - base_off);
try self.export_trie.write(writer);
+ try self.base.file.?.pwriteAll(buffer, cmd.rebase_off);
+}
- cmd.rebase_off += off;
- cmd.bind_off += off;
- cmd.weak_bind_off += off;
- cmd.lazy_bind_off += off;
- cmd.export_off += off;
-
- try self.base.file.?.pwriteAll(buffer, off);
+pub fn writeDataInCode(self: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+ const gpa = self.base.comp.gpa;
+ const cmd = self.data_in_code_cmd;
+ var buffer = try std.ArrayList(u8).initCapacity(gpa, self.data_in_code.size());
+ defer buffer.deinit();
+ try self.data_in_code.write(self, buffer.writer());
+ try self.base.file.?.pwriteAll(buffer.items, cmd.dataoff);
+}
- return off + needed_size;
+fn writeIndsymtab(self: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+ const gpa = self.base.comp.gpa;
+ const cmd = self.dysymtab_cmd;
+ const needed_size = cmd.nindirectsyms * @sizeOf(u32);
+ var buffer = try std.ArrayList(u8).initCapacity(gpa, needed_size);
+ defer buffer.deinit();
+ try self.indsymtab.write(self, buffer.writer());
+ try self.base.file.?.pwriteAll(buffer.items, cmd.indirectsymoff);
}
-fn writeFunctionStarts(self: *MachO, off: u32) !u32 {
- // TODO actually write it out
- const cmd = &self.function_starts_cmd;
- cmd.dataoff = off;
- return off;
+pub fn writeSymtabToFile(self: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+ const cmd = self.symtab_cmd;
+ try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.symtab.items), cmd.symoff);
+ try self.base.file.?.pwriteAll(self.strtab.items, cmd.stroff);
}
-pub fn writeDataInCode(self: *MachO, base_address: u64, off: u32) !u32 {
- const cmd = &self.data_in_code_cmd;
- cmd.dataoff = off;
+fn writeUnwindInfo(self: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
const gpa = self.base.comp.gpa;
- var dices = std.ArrayList(macho.data_in_code_entry).init(gpa);
- defer dices.deinit();
- for (self.objects.items) |index| {
- const object = self.getFile(index).?.object;
- const in_dices = object.getDataInCode();
-
- try dices.ensureUnusedCapacity(in_dices.len);
-
- var next_dice: usize = 0;
- for (object.atoms.items) |atom_index| {
- if (next_dice >= in_dices.len) break;
- const atom = self.getAtom(atom_index) orelse continue;
- const start_off = atom.getInputAddress(self);
- const end_off = start_off + atom.size;
- const start_dice = next_dice;
-
- if (end_off < in_dices[next_dice].offset) continue;
-
- while (next_dice < in_dices.len and
- in_dices[next_dice].offset < end_off) : (next_dice += 1)
- {}
-
- if (atom.flags.alive) for (in_dices[start_dice..next_dice]) |dice| {
- dices.appendAssumeCapacity(.{
- .offset = @intCast(atom.getAddress(self) + dice.offset - start_off - base_address),
- .length = dice.length,
- .kind = dice.kind,
- });
- };
- }
+ if (self.eh_frame_sect_index) |index| {
+ const header = self.sections.items(.header)[index];
+ const size = math.cast(usize, header.size) orelse return error.Overflow;
+ const buffer = try gpa.alloc(u8, size);
+ defer gpa.free(buffer);
+ eh_frame.write(self, buffer);
+ try self.base.file.?.pwriteAll(buffer, header.offset);
}
- const needed_size = math.cast(u32, dices.items.len * @sizeOf(macho.data_in_code_entry)) orelse return error.Overflow;
- cmd.datasize = needed_size;
-
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(dices.items), cmd.dataoff);
-
- return off + needed_size;
+ if (self.unwind_info_sect_index) |index| {
+ const header = self.sections.items(.header)[index];
+ const size = math.cast(usize, header.size) orelse return error.Overflow;
+ const buffer = try gpa.alloc(u8, size);
+ defer gpa.free(buffer);
+ try self.unwind_info.write(self, buffer);
+ try self.base.file.?.pwriteAll(buffer, header.offset);
+ }
}
-pub fn calcSymtabSize(self: *MachO) !void {
+fn calcSymtabSize(self: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
- const gpa = self.base.comp.gpa;
- var nlocals: u32 = 0;
- var nstabs: u32 = 0;
- var nexports: u32 = 0;
- var nimports: u32 = 0;
- var strsize: u32 = 0;
+ const gpa = self.base.comp.gpa;
var files = std.ArrayList(File.Index).init(gpa);
defer files.deinit();
@@ -2862,6 +2588,12 @@ pub fn calcSymtabSize(self: *MachO) !void {
for (self.dylibs.items) |index| files.appendAssumeCapacity(index);
if (self.internal_object) |index| files.appendAssumeCapacity(index);
+ var nlocals: u32 = 0;
+ var nstabs: u32 = 0;
+ var nexports: u32 = 0;
+ var nimports: u32 = 0;
+ var strsize: u32 = 1;
+
for (files.items) |index| {
const file = self.getFile(index).?;
const ctx = switch (file) {
@@ -2871,7 +2603,7 @@ pub fn calcSymtabSize(self: *MachO) !void {
ctx.istab = nstabs;
ctx.iexport = nexports;
ctx.iimport = nimports;
- try file.calcSymtabSize(self);
+ ctx.stroff = strsize;
nlocals += ctx.nlocals;
nstabs += ctx.nstabs;
nexports += ctx.nexports;
@@ -2889,10 +2621,12 @@ pub fn calcSymtabSize(self: *MachO) !void {
ctx.iimport += nlocals + nstabs + nexports;
}
+ try self.indsymtab.updateSize(self);
+
{
const cmd = &self.symtab_cmd;
cmd.nsyms = nlocals + nstabs + nexports + nimports;
- cmd.strsize = strsize + 1;
+ cmd.strsize = strsize;
}
{
@@ -2906,60 +2640,6 @@ pub fn calcSymtabSize(self: *MachO) !void {
}
}
-pub fn writeSymtab(self: *MachO, off: u32) !u32 {
- const tracy = trace(@src());
- defer tracy.end();
- const gpa = self.base.comp.gpa;
- const cmd = &self.symtab_cmd;
- cmd.symoff = off;
-
- try self.symtab.resize(gpa, cmd.nsyms);
- try self.strtab.ensureUnusedCapacity(gpa, cmd.strsize - 1);
-
- if (self.getZigObject()) |zo| {
- zo.writeSymtab(self, self);
- }
- for (self.objects.items) |index| {
- try self.getFile(index).?.writeSymtab(self, self);
- }
- for (self.dylibs.items) |index| {
- try self.getFile(index).?.writeSymtab(self, self);
- }
- if (self.getInternalObject()) |internal| {
- internal.writeSymtab(self, self);
- }
-
- assert(self.strtab.items.len == cmd.strsize);
-
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.symtab.items), cmd.symoff);
-
- return off + cmd.nsyms * @sizeOf(macho.nlist_64);
-}
-
-fn writeIndsymtab(self: *MachO, off: u32) !u32 {
- const gpa = self.base.comp.gpa;
- const cmd = &self.dysymtab_cmd;
- cmd.indirectsymoff = off;
- cmd.nindirectsyms = self.indsymtab.nsyms(self);
-
- const needed_size = cmd.nindirectsyms * @sizeOf(u32);
- var buffer = try std.ArrayList(u8).initCapacity(gpa, needed_size);
- defer buffer.deinit();
- try self.indsymtab.write(self, buffer.writer());
-
- try self.base.file.?.pwriteAll(buffer.items, cmd.indirectsymoff);
- assert(buffer.items.len == needed_size);
-
- return off + needed_size;
-}
-
-pub fn writeStrtab(self: *MachO, off: u32) !u32 {
- const cmd = &self.symtab_cmd;
- cmd.stroff = off;
- try self.base.file.?.pwriteAll(self.strtab.items, cmd.stroff);
- return off + cmd.strsize;
-}
-
fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } {
const comp = self.base.comp;
const gpa = comp.gpa;
@@ -2999,18 +2679,20 @@ fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } {
try load_commands.writeDylinkerLC(writer);
ncmds += 1;
- if (self.entry_index) |global_index| {
- const sym = self.getSymbol(global_index);
- const seg = self.getTextSegment();
- const entryoff: u32 = if (sym.getFile(self) == null)
- 0
- else
- @as(u32, @intCast(sym.getAddress(.{ .stubs = true }, self) - seg.vmaddr));
- try writer.writeStruct(macho.entry_point_command{
- .entryoff = entryoff,
- .stacksize = self.base.stack_size,
- });
- ncmds += 1;
+ if (self.getInternalObject()) |obj| {
+ if (obj.getEntryRef(self)) |ref| {
+ const sym = ref.getSymbol(self).?;
+ const seg = self.getTextSegment();
+ const entryoff: u32 = if (sym.getFile(self) == null)
+ 0
+ else
+ @as(u32, @intCast(sym.getAddress(.{ .stubs = true }, self) - seg.vmaddr));
+ try writer.writeStruct(macho.entry_point_command{
+ .entryoff = entryoff,
+ .stacksize = self.base.stack_size,
+ });
+ ncmds += 1;
+ }
}
if (self.base.isDynLib()) {
@@ -3919,157 +3601,6 @@ pub fn getFileHandle(self: MachO, index: File.HandleIndex) File.Handle {
return self.file_handles.items[index];
}
-pub fn addAtom(self: *MachO) error{OutOfMemory}!Atom.Index {
- const index = @as(Atom.Index, @intCast(self.atoms.items.len));
- const atom = try self.atoms.addOne(self.base.comp.gpa);
- atom.* = .{};
- return index;
-}
-
-pub fn getAtom(self: *MachO, index: Atom.Index) ?*Atom {
- if (index == 0) return null;
- assert(index < self.atoms.items.len);
- return &self.atoms.items[index];
-}
-
-pub fn addAtomExtra(self: *MachO, extra: Atom.Extra) !u32 {
- const fields = @typeInfo(Atom.Extra).Struct.fields;
- try self.atoms_extra.ensureUnusedCapacity(self.base.comp.gpa, fields.len);
- return self.addAtomExtraAssumeCapacity(extra);
-}
-
-pub fn addAtomExtraAssumeCapacity(self: *MachO, extra: Atom.Extra) u32 {
- const index = @as(u32, @intCast(self.atoms_extra.items.len));
- const fields = @typeInfo(Atom.Extra).Struct.fields;
- inline for (fields) |field| {
- self.atoms_extra.appendAssumeCapacity(switch (field.type) {
- u32 => @field(extra, field.name),
- else => @compileError("bad field type"),
- });
- }
- return index;
-}
-
-pub fn getAtomExtra(self: *MachO, index: u32) ?Atom.Extra {
- if (index == 0) return null;
- const fields = @typeInfo(Atom.Extra).Struct.fields;
- var i: usize = index;
- var result: Atom.Extra = undefined;
- inline for (fields) |field| {
- @field(result, field.name) = switch (field.type) {
- u32 => self.atoms_extra.items[i],
- else => @compileError("bad field type"),
- };
- i += 1;
- }
- return result;
-}
-
-pub fn setAtomExtra(self: *MachO, index: u32, extra: Atom.Extra) void {
- assert(index > 0);
- const fields = @typeInfo(Atom.Extra).Struct.fields;
- inline for (fields, 0..) |field, i| {
- self.atoms_extra.items[index + i] = switch (field.type) {
- u32 => @field(extra, field.name),
- else => @compileError("bad field type"),
- };
- }
-}
-
-pub fn addSymbol(self: *MachO) !Symbol.Index {
- const index = @as(Symbol.Index, @intCast(self.symbols.items.len));
- const symbol = try self.symbols.addOne(self.base.comp.gpa);
- symbol.* = .{};
- return index;
-}
-
-pub fn getSymbol(self: *MachO, index: Symbol.Index) *Symbol {
- assert(index < self.symbols.items.len);
- return &self.symbols.items[index];
-}
-
-pub fn addSymbolExtra(self: *MachO, extra: Symbol.Extra) !u32 {
- const fields = @typeInfo(Symbol.Extra).Struct.fields;
- try self.symbols_extra.ensureUnusedCapacity(self.base.comp.gpa, fields.len);
- return self.addSymbolExtraAssumeCapacity(extra);
-}
-
-pub fn addSymbolExtraAssumeCapacity(self: *MachO, extra: Symbol.Extra) u32 {
- const index = @as(u32, @intCast(self.symbols_extra.items.len));
- const fields = @typeInfo(Symbol.Extra).Struct.fields;
- inline for (fields) |field| {
- self.symbols_extra.appendAssumeCapacity(switch (field.type) {
- u32 => @field(extra, field.name),
- else => @compileError("bad field type"),
- });
- }
- return index;
-}
-
-pub fn getSymbolExtra(self: MachO, index: u32) ?Symbol.Extra {
- if (index == 0) return null;
- const fields = @typeInfo(Symbol.Extra).Struct.fields;
- var i: usize = index;
- var result: Symbol.Extra = undefined;
- inline for (fields) |field| {
- @field(result, field.name) = switch (field.type) {
- u32 => self.symbols_extra.items[i],
- else => @compileError("bad field type"),
- };
- i += 1;
- }
- return result;
-}
-
-pub fn setSymbolExtra(self: *MachO, index: u32, extra: Symbol.Extra) void {
- assert(index > 0);
- const fields = @typeInfo(Symbol.Extra).Struct.fields;
- inline for (fields, 0..) |field, i| {
- self.symbols_extra.items[index + i] = switch (field.type) {
- u32 => @field(extra, field.name),
- else => @compileError("bad field type"),
- };
- }
-}
-
-const GetOrCreateGlobalResult = struct {
- found_existing: bool,
- index: Symbol.Index,
-};
-
-pub fn getOrCreateGlobal(self: *MachO, off: u32) !GetOrCreateGlobalResult {
- const gpa = self.base.comp.gpa;
- const gop = try self.globals.getOrPut(gpa, off);
- if (!gop.found_existing) {
- const index = try self.addSymbol();
- const global = self.getSymbol(index);
- global.name = off;
- global.flags.global = true;
- gop.value_ptr.* = index;
- }
- return .{
- .found_existing = gop.found_existing,
- .index = gop.value_ptr.*,
- };
-}
-
-pub fn getGlobalByName(self: *MachO, name: []const u8) ?Symbol.Index {
- const off = self.strings.getOffset(name) orelse return null;
- return self.globals.get(off);
-}
-
-pub fn addUnwindRecord(self: *MachO) !UnwindInfo.Record.Index {
- const index = @as(UnwindInfo.Record.Index, @intCast(self.unwind_records.items.len));
- const rec = try self.unwind_records.addOne(self.base.comp.gpa);
- rec.* = .{};
- return index;
-}
-
-pub fn getUnwindRecord(self: *MachO, index: UnwindInfo.Record.Index) *UnwindInfo.Record {
- assert(index < self.unwind_records.items.len);
- return &self.unwind_records.items[index];
-}
-
pub fn addThunk(self: *MachO) !Thunk.Index {
const index = @as(Thunk.Index, @intCast(self.thunks.items.len));
const thunk = try self.thunks.addOne(self.base.comp.gpa);
@@ -4208,22 +3739,23 @@ pub fn reportUnexpectedError(self: *MachO, comptime format: []const u8, args: an
try err.addNote(self, "please report this as a linker bug on https://github.com/ziglang/zig/issues/new/choose", .{});
}
-fn reportDuplicates(self: *MachO, dupes: anytype) error{ HasDuplicates, OutOfMemory }!void {
+fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void {
const tracy = trace(@src());
defer tracy.end();
const max_notes = 3;
var has_dupes = false;
- var it = dupes.iterator();
+ var it = self.dupes.iterator();
while (it.next()) |entry| {
- const sym = self.getSymbol(entry.key_ptr.*);
+ const sym = self.resolver.keys.items[entry.key_ptr.* - 1];
const notes = entry.value_ptr.*;
const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
var err = try self.addErrorWithNotes(nnotes + 1);
try err.addMsg(self, "duplicate symbol definition: {s}", .{sym.getName(self)});
try err.addNote(self, "defined by {}", .{sym.getFile(self).?.fmtPath()});
+ has_dupes = true;
var inote: usize = 0;
while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
@@ -4235,10 +3767,7 @@ fn reportDuplicates(self: *MachO, dupes: anytype) error{ HasDuplicates, OutOfMem
const remaining = notes.items.len - max_notes;
try err.addNote(self, "defined {d} more times", .{remaining});
}
-
- has_dupes = true;
}
-
if (has_dupes) return error.HasDuplicates;
}
@@ -4435,15 +3964,18 @@ pub const base_tag: link.File.Tag = link.File.Tag.macho;
const Section = struct {
header: macho.section_64,
segment_id: u8,
- atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
+ atoms: std.ArrayListUnmanaged(Ref) = .{},
free_list: std.ArrayListUnmanaged(Atom.Index) = .{},
last_atom_index: Atom.Index = 0,
+ thunks: std.ArrayListUnmanaged(Thunk.Index) = .{},
+ out: std.ArrayListUnmanaged(u8) = .{},
+ relocs: std.ArrayListUnmanaged(macho.relocation_info) = .{},
};
pub const LiteralPool = struct {
table: std.AutoArrayHashMapUnmanaged(void, void) = .{},
keys: std.ArrayListUnmanaged(Key) = .{},
- values: std.ArrayListUnmanaged(Atom.Index) = .{},
+ values: std.ArrayListUnmanaged(MachO.Ref) = .{},
data: std.ArrayListUnmanaged(u8) = .{},
pub fn deinit(lp: *LiteralPool, allocator: Allocator) void {
@@ -4453,17 +3985,21 @@ pub const LiteralPool = struct {
lp.data.deinit(allocator);
}
- pub fn getAtom(lp: LiteralPool, index: Index, macho_file: *MachO) *Atom {
- assert(index < lp.values.items.len);
- return macho_file.getAtom(lp.values.items[index]).?;
- }
-
const InsertResult = struct {
found_existing: bool,
index: Index,
- atom: *Atom.Index,
+ ref: *MachO.Ref,
};
+ pub fn getSymbolRef(lp: LiteralPool, index: Index) MachO.Ref {
+ assert(index < lp.values.items.len);
+ return lp.values.items[index];
+ }
+
+ pub fn getSymbol(lp: LiteralPool, index: Index, macho_file: *MachO) *Symbol {
+ return lp.getSymbolRef(index).getSymbol(macho_file).?;
+ }
+
pub fn insert(lp: *LiteralPool, allocator: Allocator, @"type": u8, string: []const u8) !InsertResult {
const size: u32 = @intCast(string.len);
try lp.data.ensureUnusedCapacity(allocator, size);
@@ -4479,7 +4015,7 @@ pub const LiteralPool = struct {
return .{
.found_existing = gop.found_existing,
.index = @intCast(gop.index),
- .atom = &lp.values.items[gop.index],
+ .ref = &lp.values.items[gop.index],
};
}
@@ -4525,12 +4061,6 @@ const HotUpdateState = struct {
mach_task: ?std.c.MachTask = null,
};
-pub const DynamicRelocs = struct {
- rebase_relocs: u32 = 0,
- bind_relocs: u32 = 0,
- weak_bind_relocs: u32 = 0,
-};
-
pub const SymtabCtx = struct {
ilocal: u32 = 0,
istab: u32 = 0,
@@ -4540,6 +4070,7 @@ pub const SymtabCtx = struct {
nstabs: u32 = 0,
nexports: u32 = 0,
nimports: u32 = 0,
+ stroff: u32 = 0,
strsize: u32 = 0,
};
@@ -4826,6 +4357,136 @@ const UndefinedTreatment = enum {
dynamic_lookup,
};
+/// A reference to atom or symbol in an input file.
+/// If file == 0, symbol is an undefined global.
+pub const Ref = struct {
+ index: u32,
+ file: File.Index,
+
+ pub fn eql(ref: Ref, other: Ref) bool {
+ return ref.index == other.index and ref.file == other.file;
+ }
+
+ pub fn getFile(ref: Ref, macho_file: *MachO) ?File {
+ return macho_file.getFile(ref.file);
+ }
+
+ pub fn getAtom(ref: Ref, macho_file: *MachO) ?*Atom {
+ const file = ref.getFile(macho_file) orelse return null;
+ return file.getAtom(ref.index);
+ }
+
+ pub fn getSymbol(ref: Ref, macho_file: *MachO) ?*Symbol {
+ const file = ref.getFile(macho_file) orelse return null;
+ return switch (file) {
+ inline else => |x| &x.symbols.items[ref.index],
+ };
+ }
+
+ pub fn format(
+ ref: Ref,
+ comptime unused_fmt_string: []const u8,
+ options: std.fmt.FormatOptions,
+ writer: anytype,
+ ) !void {
+ _ = unused_fmt_string;
+ _ = options;
+ try writer.print("%{d} in file({d})", .{ ref.index, ref.file });
+ }
+};
+
+pub const SymbolResolver = struct {
+ keys: std.ArrayListUnmanaged(Key) = .{},
+ values: std.ArrayListUnmanaged(Ref) = .{},
+ table: std.AutoArrayHashMapUnmanaged(void, void) = .{},
+
+ const Result = struct {
+ found_existing: bool,
+ index: Index,
+ ref: *Ref,
+ };
+
+ pub fn deinit(resolver: *SymbolResolver, allocator: Allocator) void {
+ resolver.keys.deinit(allocator);
+ resolver.values.deinit(allocator);
+ resolver.table.deinit(allocator);
+ }
+
+ pub fn getOrPut(
+ resolver: *SymbolResolver,
+ allocator: Allocator,
+ ref: Ref,
+ macho_file: *MachO,
+ ) !Result {
+ const adapter = Adapter{ .keys = resolver.keys.items, .macho_file = macho_file };
+ const key = Key{ .index = ref.index, .file = ref.file };
+ const gop = try resolver.table.getOrPutAdapted(allocator, key, adapter);
+ if (!gop.found_existing) {
+ try resolver.keys.append(allocator, key);
+ _ = try resolver.values.addOne(allocator);
+ }
+ return .{
+ .found_existing = gop.found_existing,
+ .index = @intCast(gop.index + 1),
+ .ref = &resolver.values.items[gop.index],
+ };
+ }
+
+ pub fn get(resolver: SymbolResolver, index: Index) ?Ref {
+ if (index == 0) return null;
+ return resolver.values.items[index - 1];
+ }
+
+ pub fn reset(resolver: *SymbolResolver) void {
+ resolver.keys.clearRetainingCapacity();
+ resolver.values.clearRetainingCapacity();
+ resolver.table.clearRetainingCapacity();
+ }
+
+ const Key = struct {
+ index: Symbol.Index,
+ file: File.Index,
+
+ fn getName(key: Key, macho_file: *MachO) [:0]const u8 {
+ const ref = Ref{ .index = key.index, .file = key.file };
+ return ref.getSymbol(macho_file).?.getName(macho_file);
+ }
+
+ pub fn getFile(key: Key, macho_file: *MachO) ?File {
+ const ref = Ref{ .index = key.index, .file = key.file };
+ return ref.getFile(macho_file);
+ }
+
+ fn eql(key: Key, other: Key, macho_file: *MachO) bool {
+ const key_name = key.getName(macho_file);
+ const other_name = other.getName(macho_file);
+ return mem.eql(u8, key_name, other_name);
+ }
+
+ fn hash(key: Key, macho_file: *MachO) u32 {
+ const name = key.getName(macho_file);
+ return @truncate(Hash.hash(0, name));
+ }
+ };
+
+ const Adapter = struct {
+ keys: []const Key,
+ macho_file: *MachO,
+
+ pub fn eql(ctx: @This(), key: Key, b_void: void, b_map_index: usize) bool {
+ _ = b_void;
+ const other = ctx.keys[b_map_index];
+ return key.eql(other, ctx.macho_file);
+ }
+
+ pub fn hash(ctx: @This(), key: Key) u32 {
+ return key.hash(ctx.macho_file);
+ }
+ };
+
+ pub const Index = u32;
+};
+
const MachO = @This();
const std = @import("std");
@@ -4842,6 +4503,7 @@ const mem = std.mem;
const meta = std.meta;
const aarch64 = @import("../arch/aarch64/bits.zig");
+const bind = @import("MachO/dyld_info/bind.zig");
const calcUuid = @import("MachO/uuid.zig").calcUuid;
const codegen = @import("../codegen.zig");
const dead_strip = @import("MachO/dead_strip.zig");
@@ -4862,13 +4524,14 @@ const Alignment = Atom.Alignment;
const Allocator = mem.Allocator;
const Archive = @import("MachO/Archive.zig");
pub const Atom = @import("MachO/Atom.zig");
-const BindSection = synthetic.BindSection;
+const Bind = bind.Bind;
const Cache = std.Build.Cache;
const CodeSignature = @import("MachO/CodeSignature.zig");
const Compilation = @import("../Compilation.zig");
+const DataInCode = synthetic.DataInCode;
pub const DebugSymbols = @import("MachO/DebugSymbols.zig");
const Dylib = @import("MachO/Dylib.zig");
-const ExportTrieSection = synthetic.ExportTrieSection;
+const ExportTrie = @import("MachO/dyld_info/Trie.zig");
const File = @import("MachO/file.zig").File;
const GotSection = synthetic.GotSection;
const Hash = std.hash.Wyhash;
@@ -4876,7 +4539,7 @@ const Indsymtab = synthetic.Indsymtab;
const InternalObject = @import("MachO/InternalObject.zig");
const ObjcStubsSection = synthetic.ObjcStubsSection;
const Object = @import("MachO/Object.zig");
-const LazyBindSection = synthetic.LazyBindSection;
+const LazyBind = bind.LazyBind;
const LaSymbolPtrSection = synthetic.LaSymbolPtrSection;
const LibStub = tapi.LibStub;
const Liveness = @import("../Liveness.zig");
@@ -4886,7 +4549,7 @@ const Zcu = @import("../Zcu.zig");
/// Deprecated.
const Module = Zcu;
const InternPool = @import("../InternPool.zig");
-const RebaseSection = synthetic.RebaseSection;
+const Rebase = @import("MachO/dyld_info/Rebase.zig");
pub const Relocation = @import("MachO/Relocation.zig");
const StringTable = @import("StringTable.zig");
const StubsSection = synthetic.StubsSection;
@@ -4896,6 +4559,6 @@ const Thunk = thunks.Thunk;
const TlvPtrSection = synthetic.TlvPtrSection;
const Value = @import("../Value.zig");
const UnwindInfo = @import("MachO/UnwindInfo.zig");
-const WeakBindSection = synthetic.WeakBindSection;
+const WeakBind = bind.WeakBind;
const ZigGotSection = synthetic.ZigGotSection;
const ZigObject = @import("MachO/ZigObject.zig");
diff --git a/src/link/MachO/Archive.zig b/src/link/MachO/Archive.zig
@@ -67,9 +67,9 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index:
mem.eql(u8, name, SYMDEF64_SORTED)) continue;
const object = Object{
- .archive = .{
+ .offset = pos,
+ .in_archive = .{
.path = try gpa.dupe(u8, path),
- .offset = pos,
.size = hdr_size,
},
.path = try gpa.dupe(u8, name),
diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig
@@ -47,16 +47,6 @@ pub fn getFile(self: Atom, macho_file: *MachO) File {
return macho_file.getFile(self.file).?;
}
-pub fn getData(self: Atom, macho_file: *MachO, buffer: []u8) !void {
- assert(buffer.len == self.size);
- switch (self.getFile(macho_file)) {
- .internal => |x| try x.getAtomData(self, buffer),
- .object => |x| try x.getAtomData(macho_file, self, buffer),
- .zig_object => |x| try x.getAtomData(macho_file, self, buffer),
- else => unreachable,
- }
-}
-
pub fn getRelocs(self: Atom, macho_file: *MachO) []const Relocation {
return switch (self.getFile(macho_file)) {
.dylib => unreachable,
@@ -88,17 +78,18 @@ pub fn getPriority(self: Atom, macho_file: *MachO) u64 {
}
pub fn getUnwindRecords(self: Atom, macho_file: *MachO) []const UnwindInfo.Record.Index {
- if (!self.flags.unwind) return &[0]UnwindInfo.Record.Index{};
- const extra = self.getExtra(macho_file).?;
+ const extra = self.getExtra(macho_file);
return switch (self.getFile(macho_file)) {
- .dylib, .zig_object, .internal => unreachable,
- .object => |x| x.unwind_records.items[extra.unwind_index..][0..extra.unwind_count],
+ .dylib => unreachable,
+ .zig_object, .internal => &[0]UnwindInfo.Record.Index{},
+ .object => |x| x.unwind_records_indexes.items[extra.unwind_index..][0..extra.unwind_count],
};
}
pub fn markUnwindRecordsDead(self: Atom, macho_file: *MachO) void {
+ const object = self.getFile(macho_file).object;
for (self.getUnwindRecords(macho_file)) |cu_index| {
- const cu = macho_file.getUnwindRecord(cu_index);
+ const cu = object.getUnwindRecord(cu_index);
cu.alive = false;
if (cu.getFdePtr(macho_file)) |fde| {
@@ -108,44 +99,39 @@ pub fn markUnwindRecordsDead(self: Atom, macho_file: *MachO) void {
}
pub fn getThunk(self: Atom, macho_file: *MachO) *Thunk {
- assert(self.flags.thunk);
- const extra = self.getExtra(macho_file).?;
+ const extra = self.getExtra(macho_file);
return macho_file.getThunk(extra.thunk);
}
-pub fn getLiteralPoolIndex(self: Atom, macho_file: *MachO) ?MachO.LiteralPool.Index {
- if (!self.flags.literal_pool) return null;
- return self.getExtra(macho_file).?.literal_index;
-}
-
const AddExtraOpts = struct {
thunk: ?u32 = null,
rel_index: ?u32 = null,
rel_count: ?u32 = null,
+ rel_out_index: ?u32 = null,
+ rel_out_count: ?u32 = null,
unwind_index: ?u32 = null,
unwind_count: ?u32 = null,
- literal_index: ?u32 = null,
+ literal_pool_index: ?u32 = null,
+ literal_symbol_index: ?u32 = null,
};
-pub fn addExtra(atom: *Atom, opts: AddExtraOpts, macho_file: *MachO) !void {
- if (atom.getExtra(macho_file) == null) {
- atom.extra = try macho_file.addAtomExtra(.{});
- }
- var extra = atom.getExtra(macho_file).?;
+pub fn addExtra(atom: *Atom, opts: AddExtraOpts, macho_file: *MachO) void {
+ const file = atom.getFile(macho_file);
+ var extra = file.getAtomExtra(atom.extra);
inline for (@typeInfo(@TypeOf(opts)).Struct.fields) |field| {
if (@field(opts, field.name)) |x| {
@field(extra, field.name) = x;
}
}
- atom.setExtra(extra, macho_file);
+ file.setAtomExtra(atom.extra, extra);
}
-pub inline fn getExtra(atom: Atom, macho_file: *MachO) ?Extra {
- return macho_file.getAtomExtra(atom.extra);
+pub inline fn getExtra(atom: Atom, macho_file: *MachO) Extra {
+ return atom.getFile(macho_file).getAtomExtra(atom.extra);
}
pub inline fn setExtra(atom: Atom, extra: Extra, macho_file: *MachO) void {
- macho_file.setAtomExtra(atom.extra, extra);
+ atom.getFile(macho_file).setAtomExtra(atom.extra, extra);
}
pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 {
@@ -227,7 +213,8 @@ pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 {
/// File offset relocation happens transparently, so it is not included in
/// this calculation.
pub fn capacity(self: Atom, macho_file: *MachO) u64 {
- const next_addr = if (macho_file.getAtom(self.next_index)) |next|
+ const zo = macho_file.getZigObject().?;
+ const next_addr = if (zo.getAtom(self.next_index)) |next|
next.getAddress(macho_file)
else
std.math.maxInt(u32);
@@ -236,7 +223,8 @@ pub fn capacity(self: Atom, macho_file: *MachO) u64 {
pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {
// No need to keep a free list node for the last block.
- const next = macho_file.getAtom(self.next_index) orelse return false;
+ const zo = macho_file.getZigObject().?;
+ const next = zo.getAtom(self.next_index) orelse return false;
const cap = next.getAddress(macho_file) - self.getAddress(macho_file);
const ideal_cap = MachO.padToIdeal(self.size);
if (cap <= ideal_cap) return false;
@@ -245,6 +233,7 @@ pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {
}
pub fn allocate(self: *Atom, macho_file: *MachO) !void {
+ const zo = macho_file.getZigObject().?;
const sect = &macho_file.sections.items(.header)[self.out_n_sect];
const free_list = &macho_file.sections.items(.free_list)[self.out_n_sect];
const last_atom_index = &macho_file.sections.items(.last_atom_index)[self.out_n_sect];
@@ -264,7 +253,7 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void {
var i: usize = free_list.items.len;
while (i < free_list.items.len) {
const big_atom_index = free_list.items[i];
- const big_atom = macho_file.getAtom(big_atom_index).?;
+ const big_atom = zo.getAtom(big_atom_index).?;
// We now have a pointer to a live atom that has too much capacity.
// Is it enough that we could fit this new atom?
const cap = big_atom.capacity(macho_file);
@@ -296,7 +285,7 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void {
free_list_removal = i;
}
break :blk new_start_vaddr;
- } else if (macho_file.getAtom(last_atom_index.*)) |last| {
+ } else if (zo.getAtom(last_atom_index.*)) |last| {
const ideal_capacity = MachO.padToIdeal(last.size);
const ideal_capacity_end_vaddr = last.value + ideal_capacity;
const new_start_vaddr = self.alignment.forward(ideal_capacity_end_vaddr);
@@ -316,7 +305,7 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void {
});
const expand_section = if (atom_placement) |placement_index|
- macho_file.getAtom(placement_index).?.next_index == 0
+ zo.getAtom(placement_index).?.next_index == 0
else
true;
if (expand_section) {
@@ -341,15 +330,15 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void {
// This function can also reallocate an atom.
// In this case we need to "unplug" it from its previous location before
// plugging it in to its new location.
- if (macho_file.getAtom(self.prev_index)) |prev| {
+ if (zo.getAtom(self.prev_index)) |prev| {
prev.next_index = self.next_index;
}
- if (macho_file.getAtom(self.next_index)) |next| {
+ if (zo.getAtom(self.next_index)) |next| {
next.prev_index = self.prev_index;
}
if (atom_placement) |big_atom_index| {
- const big_atom = macho_file.getAtom(big_atom_index).?;
+ const big_atom = zo.getAtom(big_atom_index).?;
self.prev_index = big_atom_index;
self.next_index = big_atom.next_index;
big_atom.next_index = self.atom_index;
@@ -379,6 +368,7 @@ pub fn free(self: *Atom, macho_file: *MachO) void {
const comp = macho_file.base.comp;
const gpa = comp.gpa;
+ const zo = macho_file.getZigObject().?;
const free_list = &macho_file.sections.items(.free_list)[self.out_n_sect];
const last_atom_index = &macho_file.sections.items(.last_atom_index)[self.out_n_sect];
var already_have_free_list_node = false;
@@ -397,9 +387,9 @@ pub fn free(self: *Atom, macho_file: *MachO) void {
}
}
- if (macho_file.getAtom(last_atom_index.*)) |last_atom| {
+ if (zo.getAtom(last_atom_index.*)) |last_atom| {
if (last_atom.atom_index == self.atom_index) {
- if (macho_file.getAtom(self.prev_index)) |_| {
+ if (zo.getAtom(self.prev_index)) |_| {
// TODO shrink the section size here
last_atom_index.* = self.prev_index;
} else {
@@ -408,7 +398,7 @@ pub fn free(self: *Atom, macho_file: *MachO) void {
}
}
- if (macho_file.getAtom(self.prev_index)) |prev| {
+ if (zo.getAtom(self.prev_index)) |prev| {
prev.next_index = self.next_index;
if (!already_have_free_list_node and prev.*.freeListEligible(macho_file)) {
// The free list is heuristics, it doesn't have to be perfect, so we can
@@ -419,7 +409,7 @@ pub fn free(self: *Atom, macho_file: *MachO) void {
self.prev_index = 0;
}
- if (macho_file.getAtom(self.next_index)) |next| {
+ if (zo.getAtom(self.next_index)) |next| {
next.prev_index = self.prev_index;
} else {
self.next_index = 0;
@@ -437,8 +427,7 @@ pub fn addReloc(self: *Atom, macho_file: *MachO, reloc: Relocation) !void {
const gpa = macho_file.base.comp.gpa;
const file = self.getFile(macho_file);
assert(file == .zig_object);
- assert(self.flags.relocs);
- var extra = self.getExtra(macho_file).?;
+ var extra = self.getExtra(macho_file);
const rels = &file.zig_object.relocs.items[extra.rel_index];
try rels.append(gpa, reloc);
extra.rel_count += 1;
@@ -446,9 +435,8 @@ pub fn addReloc(self: *Atom, macho_file: *MachO, reloc: Relocation) !void {
}
pub fn freeRelocs(self: *Atom, macho_file: *MachO) void {
- if (!self.flags.relocs) return;
self.getFile(macho_file).zig_object.freeAtomRelocs(self.*, macho_file);
- var extra = self.getExtra(macho_file).?;
+ var extra = self.getExtra(macho_file);
extra.rel_count = 0;
self.setExtra(extra, macho_file);
}
@@ -458,11 +446,6 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
defer tracy.end();
assert(self.flags.alive);
- const dynrel_ctx = switch (self.getFile(macho_file)) {
- .zig_object => |x| &x.dynamic_relocs,
- .object => |x| &x.dynamic_relocs,
- else => unreachable,
- };
const relocs = self.getRelocs(macho_file);
for (relocs) |rel| {
@@ -470,7 +453,7 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
switch (rel.type) {
.branch => {
- const symbol = rel.getTargetSymbol(macho_file);
+ const symbol = rel.getTargetSymbol(self, macho_file);
if (symbol.flags.import or (symbol.flags.@"export" and symbol.flags.weak) or symbol.flags.interposable) {
symbol.flags.stubs = true;
if (symbol.flags.weak) {
@@ -485,7 +468,7 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
.got_load_page,
.got_load_pageoff,
=> {
- const symbol = rel.getTargetSymbol(macho_file);
+ const symbol = rel.getTargetSymbol(self, macho_file);
if (symbol.flags.import or
(symbol.flags.@"export" and symbol.flags.weak) or
symbol.flags.interposable or
@@ -499,18 +482,18 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
},
.zig_got_load => {
- assert(rel.getTargetSymbol(macho_file).flags.has_zig_got);
+ assert(rel.getTargetSymbol(self, macho_file).flags.has_zig_got);
},
.got => {
- rel.getTargetSymbol(macho_file).flags.needs_got = true;
+ rel.getTargetSymbol(self, macho_file).flags.needs_got = true;
},
.tlv,
.tlvp_page,
.tlvp_pageoff,
=> {
- const symbol = rel.getTargetSymbol(macho_file);
+ const symbol = rel.getTargetSymbol(self, macho_file);
if (!symbol.flags.tlv) {
try macho_file.reportParseError2(
self.getFile(macho_file).getIndex(),
@@ -529,27 +512,21 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
.unsigned => {
if (rel.meta.length == 3) { // TODO this really should check if this is pointer width
if (rel.tag == .@"extern") {
- const symbol = rel.getTargetSymbol(macho_file);
+ const symbol = rel.getTargetSymbol(self, macho_file);
if (symbol.isTlvInit(macho_file)) {
macho_file.has_tlv = true;
continue;
}
if (symbol.flags.import) {
- dynrel_ctx.bind_relocs += 1;
if (symbol.flags.weak) {
- dynrel_ctx.weak_bind_relocs += 1;
macho_file.binds_to_weak = true;
}
continue;
}
if (symbol.flags.@"export" and symbol.flags.weak) {
- dynrel_ctx.weak_bind_relocs += 1;
macho_file.binds_to_weak = true;
- } else if (symbol.flags.interposable) {
- dynrel_ctx.bind_relocs += 1;
}
}
- dynrel_ctx.rebase_relocs += 1;
}
},
@@ -568,14 +545,15 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
fn reportUndefSymbol(self: Atom, rel: Relocation, macho_file: *MachO) !bool {
if (rel.tag == .local) return false;
- const sym = rel.getTargetSymbol(macho_file);
- if (sym.getFile(macho_file) == null) {
+ const file = self.getFile(macho_file);
+ const ref = file.getSymbolRef(rel.target, macho_file);
+ if (ref.getFile(macho_file) == null) {
const gpa = macho_file.base.comp.gpa;
- const gop = try macho_file.undefs.getOrPut(gpa, rel.target);
+ const gop = try macho_file.undefs.getOrPut(gpa, file.getGlobals()[rel.target]);
if (!gop.found_existing) {
gop.value_ptr.* = .{};
}
- try gop.value_ptr.append(gpa, self.atom_index);
+ try gop.value_ptr.append(gpa, .{ .index = self.atom_index, .file = self.file });
return true;
}
@@ -591,7 +569,7 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void {
const name = self.getName(macho_file);
const relocs = self.getRelocs(macho_file);
- relocs_log.debug("{x}: {s}", .{ self.getAddress(macho_file), name });
+ relocs_log.debug("{x}: {s}", .{ self.value, name });
var has_error = false;
var stream = std.io.fixedBufferStream(buffer);
@@ -602,7 +580,7 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void {
const subtractor = if (rel.meta.has_subtractor) relocs[i - 1] else null;
if (rel.tag == .@"extern") {
- if (rel.getTargetSymbol(macho_file).getFile(macho_file) == null) continue;
+ if (rel.getTargetSymbol(self, macho_file).getFile(macho_file) == null) continue;
}
try stream.seekTo(rel_offset);
@@ -610,13 +588,19 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void {
switch (err) {
error.RelaxFail => {
const target = switch (rel.tag) {
- .@"extern" => rel.getTargetSymbol(macho_file).getName(macho_file),
- .local => rel.getTargetAtom(macho_file).getName(macho_file),
+ .@"extern" => rel.getTargetSymbol(self, macho_file).getName(macho_file),
+ .local => rel.getTargetAtom(self, macho_file).getName(macho_file),
};
try macho_file.reportParseError2(
file.getIndex(),
- "{s}: 0x{x}: 0x{x}: failed to relax relocation: type {s}, target {s}",
- .{ name, self.getAddress(macho_file), rel.offset, @tagName(rel.type), target },
+ "{s}: 0x{x}: 0x{x}: failed to relax relocation: type {}, target {s}",
+ .{
+ name,
+ self.getAddress(macho_file),
+ rel.offset,
+ rel.fmtPretty(macho_file.getTarget().cpu.arch),
+ target,
+ },
);
has_error = true;
},
@@ -649,14 +633,12 @@ fn resolveRelocInner(
) ResolveError!void {
const cpu_arch = macho_file.getTarget().cpu.arch;
const rel_offset = math.cast(usize, rel.offset - self.off) orelse return error.Overflow;
- const seg_id = macho_file.sections.items(.segment_id)[self.out_n_sect];
- const seg = macho_file.segments.items[seg_id];
const P = @as(i64, @intCast(self.getAddress(macho_file))) + @as(i64, @intCast(rel_offset));
const A = rel.addend + rel.getRelocAddend(cpu_arch);
- const S: i64 = @intCast(rel.getTargetAddress(macho_file));
- const G: i64 = @intCast(rel.getGotTargetAddress(macho_file));
+ const S: i64 = @intCast(rel.getTargetAddress(self, macho_file));
+ const G: i64 = @intCast(rel.getGotTargetAddress(self, macho_file));
const TLS = @as(i64, @intCast(macho_file.getTlsAddress()));
- const SUB = if (subtractor) |sub| @as(i64, @intCast(sub.getTargetAddress(macho_file))) else 0;
+ const SUB = if (subtractor) |sub| @as(i64, @intCast(sub.getTargetAddress(self, macho_file))) else 0;
// Address of the __got_zig table entry if any.
const ZIG_GOT = @as(i64, @intCast(rel.getZigGotTargetAddress(macho_file)));
@@ -674,21 +656,21 @@ fn resolveRelocInner(
}.divExact;
switch (rel.tag) {
- .local => relocs_log.debug(" {x}<+{d}>: {s}: [=> {x}] atom({d})", .{
+ .local => relocs_log.debug(" {x}<+{d}>: {}: [=> {x}] atom({d})", .{
P,
rel_offset,
- @tagName(rel.type),
+ rel.fmtPretty(cpu_arch),
S + A - SUB,
- rel.getTargetAtom(macho_file).atom_index,
+ rel.getTargetAtom(self, macho_file).atom_index,
}),
- .@"extern" => relocs_log.debug(" {x}<+{d}>: {s}: [=> {x}] G({x}) ZG({x}) ({s})", .{
+ .@"extern" => relocs_log.debug(" {x}<+{d}>: {}: [=> {x}] G({x}) ZG({x}) ({s})", .{
P,
rel_offset,
- @tagName(rel.type),
+ rel.fmtPretty(cpu_arch),
S + A - SUB,
G + A,
ZIG_GOT + A,
- rel.getTargetSymbol(macho_file).getName(macho_file),
+ rel.getTargetSymbol(self, macho_file).getName(macho_file),
}),
}
@@ -699,34 +681,13 @@ fn resolveRelocInner(
assert(!rel.meta.pcrel);
if (rel.meta.length == 3) {
if (rel.tag == .@"extern") {
- const sym = rel.getTargetSymbol(macho_file);
+ const sym = rel.getTargetSymbol(self, macho_file);
if (sym.isTlvInit(macho_file)) {
try writer.writeInt(u64, @intCast(S - TLS), .little);
return;
}
- const entry = bind.Entry{
- .target = rel.target,
- .offset = @as(u64, @intCast(P)) - seg.vmaddr,
- .segment_id = seg_id,
- .addend = A,
- };
- if (sym.flags.import) {
- macho_file.bind.entries.appendAssumeCapacity(entry);
- if (sym.flags.weak) {
- macho_file.weak_bind.entries.appendAssumeCapacity(entry);
- }
- return;
- }
- if (sym.flags.@"export" and sym.flags.weak) {
- macho_file.weak_bind.entries.appendAssumeCapacity(entry);
- } else if (sym.flags.interposable) {
- macho_file.bind.entries.appendAssumeCapacity(entry);
- }
+ if (sym.flags.import) return;
}
- macho_file.rebase.entries.appendAssumeCapacity(.{
- .offset = @as(u64, @intCast(P)) - seg.vmaddr,
- .segment_id = seg_id,
- });
try writer.writeInt(u64, @bitCast(S + A - SUB), .little);
} else if (rel.meta.length == 2) {
try writer.writeInt(u32, @bitCast(@as(i32, @truncate(S + A - SUB))), .little);
@@ -750,7 +711,7 @@ fn resolveRelocInner(
.aarch64 => {
const disp: i28 = math.cast(i28, S + A - P) orelse blk: {
const thunk = self.getThunk(macho_file);
- const S_: i64 = @intCast(thunk.getTargetAddress(rel.target, macho_file));
+ const S_: i64 = @intCast(thunk.getTargetAddress(rel.getTargetSymbolRef(self, macho_file), macho_file));
break :blk math.cast(i28, S_ + A - P) orelse return error.Overflow;
};
aarch64.writeBranchImm(disp, code[rel_offset..][0..4]);
@@ -763,7 +724,7 @@ fn resolveRelocInner(
assert(rel.tag == .@"extern");
assert(rel.meta.length == 2);
assert(rel.meta.pcrel);
- if (rel.getTargetSymbol(macho_file).flags.has_got) {
+ if (rel.getTargetSymbol(self, macho_file).flags.has_got) {
try writer.writeInt(i32, @intCast(G + A - P), .little);
} else {
try x86_64.relaxGotLoad(self, code[rel_offset - 3 ..], rel, macho_file);
@@ -786,7 +747,7 @@ fn resolveRelocInner(
assert(rel.tag == .@"extern");
assert(rel.meta.length == 2);
assert(rel.meta.pcrel);
- const sym = rel.getTargetSymbol(macho_file);
+ const sym = rel.getTargetSymbol(self, macho_file);
if (sym.flags.tlv_ptr) {
const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file));
try writer.writeInt(i32, @intCast(S_ + A - P), .little);
@@ -809,7 +770,7 @@ fn resolveRelocInner(
assert(rel.tag == .@"extern");
assert(rel.meta.length == 2);
assert(rel.meta.pcrel);
- const sym = rel.getTargetSymbol(macho_file);
+ const sym = rel.getTargetSymbol(self, macho_file);
const source = math.cast(u64, P) orelse return error.Overflow;
const target = target: {
const target = switch (rel.type) {
@@ -868,7 +829,7 @@ fn resolveRelocInner(
assert(rel.meta.length == 2);
assert(!rel.meta.pcrel);
- const sym = rel.getTargetSymbol(macho_file);
+ const sym = rel.getTargetSymbol(self, macho_file);
const target = target: {
const target = if (sym.flags.tlv_ptr) blk: {
const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file));
@@ -945,11 +906,11 @@ const x86_64 = struct {
},
else => |x| {
var err = try macho_file.addErrorWithNotes(2);
- try err.addMsg(macho_file, "{s}: 0x{x}: 0x{x}: failed to relax relocation of type {s}", .{
+ try err.addMsg(macho_file, "{s}: 0x{x}: 0x{x}: failed to relax relocation of type {}", .{
self.getName(macho_file),
self.getAddress(macho_file),
rel.offset,
- @tagName(rel.type),
+ rel.fmtPretty(.x86_64),
});
try err.addNote(macho_file, "expected .mov instruction but found .{s}", .{@tagName(x)});
try err.addNote(macho_file, "while parsing {}", .{self.getFile(macho_file).fmtPath()});
@@ -1012,48 +973,49 @@ pub fn calcNumRelocs(self: Atom, macho_file: *MachO) u32 {
}
}
-pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.ArrayList(macho.relocation_info)) !void {
+pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.relocation_info) !void {
const tracy = trace(@src());
defer tracy.end();
const cpu_arch = macho_file.getTarget().cpu.arch;
const relocs = self.getRelocs(macho_file);
- var stream = std.io.fixedBufferStream(code);
+ var i: usize = 0;
for (relocs) |rel| {
- const rel_offset = rel.offset - self.off;
+ defer i += 1;
+ const rel_offset = math.cast(usize, rel.offset - self.off) orelse return error.Overflow;
const r_address: i32 = math.cast(i32, self.value + rel_offset) orelse return error.Overflow;
+ assert(r_address >= 0);
const r_symbolnum = r_symbolnum: {
const r_symbolnum: u32 = switch (rel.tag) {
- .local => rel.getTargetAtom(macho_file).out_n_sect + 1,
- .@"extern" => rel.getTargetSymbol(macho_file).getOutputSymtabIndex(macho_file).?,
+ .local => rel.getTargetAtom(self, macho_file).out_n_sect + 1,
+ .@"extern" => rel.getTargetSymbol(self, macho_file).getOutputSymtabIndex(macho_file).?,
};
break :r_symbolnum math.cast(u24, r_symbolnum) orelse return error.Overflow;
};
const r_extern = rel.tag == .@"extern";
var addend = rel.addend + rel.getRelocAddend(cpu_arch);
if (rel.tag == .local) {
- const target: i64 = @intCast(rel.getTargetAddress(macho_file));
+ const target: i64 = @intCast(rel.getTargetAddress(self, macho_file));
addend += target;
}
- try stream.seekTo(rel_offset);
-
switch (cpu_arch) {
.aarch64 => {
if (rel.type == .unsigned) switch (rel.meta.length) {
0, 1 => unreachable,
- 2 => try stream.writer().writeInt(i32, @truncate(addend), .little),
- 3 => try stream.writer().writeInt(i64, addend, .little),
+ 2 => mem.writeInt(i32, code[rel_offset..][0..4], @truncate(addend), .little),
+ 3 => mem.writeInt(i64, code[rel_offset..][0..8], addend, .little),
} else if (addend > 0) {
- buffer.appendAssumeCapacity(.{
+ buffer[i] = .{
.r_address = r_address,
.r_symbolnum = @bitCast(math.cast(i24, addend) orelse return error.Overflow),
.r_pcrel = 0,
.r_length = 2,
.r_extern = 0,
.r_type = @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_ADDEND),
- });
+ };
+ i += 1;
}
const r_type: macho.reloc_type_arm64 = switch (rel.type) {
@@ -1077,14 +1039,14 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.Arra
.tlv,
=> unreachable,
};
- buffer.appendAssumeCapacity(.{
+ buffer[i] = .{
.r_address = r_address,
.r_symbolnum = r_symbolnum,
.r_pcrel = @intFromBool(rel.meta.pcrel),
.r_extern = @intFromBool(r_extern),
.r_length = rel.meta.length,
.r_type = @intFromEnum(r_type),
- });
+ };
},
.x86_64 => {
if (rel.meta.pcrel) {
@@ -1096,8 +1058,8 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.Arra
}
switch (rel.meta.length) {
0, 1 => unreachable,
- 2 => try stream.writer().writeInt(i32, @truncate(addend), .little),
- 3 => try stream.writer().writeInt(i64, addend, .little),
+ 2 => mem.writeInt(i32, code[rel_offset..][0..4], @truncate(addend), .little),
+ 3 => mem.writeInt(i64, code[rel_offset..][0..8], addend, .little),
}
const r_type: macho.reloc_type_x86_64 = switch (rel.type) {
@@ -1121,18 +1083,20 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.Arra
.tlvp_pageoff,
=> unreachable,
};
- buffer.appendAssumeCapacity(.{
+ buffer[i] = .{
.r_address = r_address,
.r_symbolnum = r_symbolnum,
.r_pcrel = @intFromBool(rel.meta.pcrel),
.r_extern = @intFromBool(r_extern),
.r_length = rel.meta.length,
.r_type = @intFromEnum(r_type),
- });
+ };
},
else => unreachable,
}
}
+
+ assert(i == buffer.len);
}
pub fn format(
@@ -1170,18 +1134,18 @@ fn format2(
_ = unused_fmt_string;
const atom = ctx.atom;
const macho_file = ctx.macho_file;
- try writer.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : nreloc({d})", .{
- atom.atom_index, atom.getName(macho_file), atom.getAddress(macho_file),
- atom.out_n_sect, atom.alignment, atom.size,
- atom.getRelocs(macho_file).len,
+ const file = atom.getFile(macho_file);
+ try writer.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : nreloc({d}) : thunk({d})", .{
+ atom.atom_index, atom.getName(macho_file), atom.getAddress(macho_file),
+ atom.out_n_sect, atom.alignment, atom.size,
+ atom.getRelocs(macho_file).len, atom.getExtra(macho_file).thunk,
});
- if (atom.flags.thunk) try writer.print(" : thunk({d})", .{atom.getExtra(macho_file).?.thunk});
if (!atom.flags.alive) try writer.writeAll(" : [*]");
- if (atom.flags.unwind) {
+ if (atom.getUnwindRecords(macho_file).len > 0) {
try writer.writeAll(" : unwind{ ");
- const extra = atom.getExtra(macho_file).?;
+ const extra = atom.getExtra(macho_file);
for (atom.getUnwindRecords(macho_file), extra.unwind_index..) |index, i| {
- const rec = macho_file.getUnwindRecord(index);
+ const rec = file.object.getUnwindRecord(index);
try writer.print("{d}", .{index});
if (!rec.alive) try writer.writeAll("([*])");
if (i < extra.unwind_index + extra.unwind_count - 1) try writer.writeAll(", ");
@@ -1198,18 +1162,6 @@ pub const Flags = packed struct {
/// Specifies if this atom has been visited during garbage collection.
visited: bool = false,
-
- /// Whether this atom has a range extension thunk.
- thunk: bool = false,
-
- /// Whether this atom has any relocations.
- relocs: bool = false,
-
- /// Whether this atom has any unwind records.
- unwind: bool = false,
-
- /// Whether this atom has LiteralPool entry.
- literal_pool: bool = false,
};
pub const Extra = struct {
@@ -1222,6 +1174,12 @@ pub const Extra = struct {
/// Count of relocations belonging to this atom.
rel_count: u32 = 0,
+ /// Start index of relocations being written out to file for this atom.
+ rel_out_index: u32 = 0,
+
+ /// Count of relocations written out to file for this atom.
+ rel_out_count: u32 = 0,
+
/// Start index of relocations belonging to this atom.
unwind_index: u32 = 0,
@@ -1229,14 +1187,16 @@ pub const Extra = struct {
unwind_count: u32 = 0,
/// Index into LiteralPool entry for this atom.
- literal_index: u32 = 0,
+ literal_pool_index: u32 = 0,
+
+ /// Index into the File's symbol table for local symbol representing this literal atom.
+ literal_symbol_index: u32 = 0,
};
pub const Alignment = @import("../../InternPool.zig").Alignment;
const aarch64 = @import("../aarch64.zig");
const assert = std.debug.assert;
-const bind = @import("dyld_info/bind.zig");
const macho = std.macho;
const math = std.math;
const mem = std.mem;
diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig
@@ -175,8 +175,9 @@ fn findFreeSpace(self: *DebugSymbols, object_size: u64, min_alignment: u64) u64
}
pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void {
+ const zo = macho_file.getZigObject().?;
for (self.relocs.items) |*reloc| {
- const sym = macho_file.getSymbol(reloc.target);
+ const sym = zo.symbols.items[reloc.target];
const sym_name = sym.getName(macho_file);
const addr = switch (reloc.type) {
.direct_load => sym.getAddress(.{}, macho_file),
@@ -382,23 +383,22 @@ pub fn writeSymtab(self: *DebugSymbols, off: u32, macho_file: *MachO) !u32 {
cmd.symoff = off;
try self.symtab.resize(gpa, cmd.nsyms);
- try self.strtab.ensureUnusedCapacity(gpa, cmd.strsize - 1);
+ try self.strtab.resize(gpa, cmd.strsize);
+ self.strtab.items[0] = 0;
if (macho_file.getZigObject()) |zo| {
zo.writeSymtab(macho_file, self);
}
for (macho_file.objects.items) |index| {
- try macho_file.getFile(index).?.writeSymtab(macho_file, self);
+ macho_file.getFile(index).?.writeSymtab(macho_file, self);
}
for (macho_file.dylibs.items) |index| {
- try macho_file.getFile(index).?.writeSymtab(macho_file, self);
+ macho_file.getFile(index).?.writeSymtab(macho_file, self);
}
if (macho_file.getInternalObject()) |internal| {
internal.writeSymtab(macho_file, self);
}
- assert(self.strtab.items.len == cmd.strsize);
-
try self.file.pwriteAll(mem.sliceAsBytes(self.symtab.items), cmd.symoff);
return off + cmd.nsyms * @sizeOf(macho.nlist_64);
diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig
@@ -6,7 +6,9 @@ strtab: std.ArrayListUnmanaged(u8) = .{},
id: ?Id = null,
ordinal: u16 = 0,
-symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
+symbols: std.ArrayListUnmanaged(Symbol) = .{},
+symbols_extra: std.ArrayListUnmanaged(u32) = .{},
+globals: std.ArrayListUnmanaged(MachO.SymbolResolver.Index) = .{},
dependents: std.ArrayListUnmanaged(Id) = .{},
rpaths: std.StringArrayHashMapUnmanaged(void) = .{},
umbrella: File.Index = 0,
@@ -37,6 +39,8 @@ pub fn deinit(self: *Dylib, allocator: Allocator) void {
self.strtab.deinit(allocator);
if (self.id) |*id| id.deinit(allocator);
self.symbols.deinit(allocator);
+ self.symbols_extra.deinit(allocator);
+ self.globals.deinit(allocator);
for (self.dependents.items) |*id| {
id.deinit(allocator);
}
@@ -494,53 +498,55 @@ fn addObjCExport(
pub fn initSymbols(self: *Dylib, macho_file: *MachO) !void {
const gpa = macho_file.base.comp.gpa;
- try self.symbols.ensureTotalCapacityPrecise(gpa, self.exports.items(.name).len);
-
- for (self.exports.items(.name)) |noff| {
- const name = self.getString(noff);
- const off = try macho_file.strings.insert(gpa, name);
- const gop = try macho_file.getOrCreateGlobal(off);
- self.symbols.addOneAssumeCapacity().* = gop.index;
+ const nsyms = self.exports.items(.name).len;
+ try self.symbols.ensureTotalCapacityPrecise(gpa, nsyms);
+ try self.symbols_extra.ensureTotalCapacityPrecise(gpa, nsyms * @sizeOf(Symbol.Extra));
+ try self.globals.ensureTotalCapacityPrecise(gpa, nsyms);
+ self.globals.resize(gpa, nsyms) catch unreachable;
+ @memset(self.globals.items, 0);
+
+ for (self.exports.items(.name), self.exports.items(.flags)) |noff, flags| {
+ const index = self.addSymbolAssumeCapacity();
+ const symbol = &self.symbols.items[index];
+ symbol.name = noff;
+ symbol.extra = self.addSymbolExtraAssumeCapacity(.{});
+ symbol.flags.weak = flags.weak;
+ symbol.flags.tlv = flags.tlv;
+ symbol.visibility = .global;
}
}
-pub fn resolveSymbols(self: *Dylib, macho_file: *MachO) void {
+pub fn resolveSymbols(self: *Dylib, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
if (!self.explicit and !self.hoisted) return;
- for (self.symbols.items, self.exports.items(.flags)) |index, flags| {
- const global = macho_file.getSymbol(index);
+ const gpa = macho_file.base.comp.gpa;
+
+ for (self.exports.items(.flags), self.globals.items, 0..) |flags, *global, i| {
+ const gop = try macho_file.resolver.getOrPut(gpa, .{
+ .index = @intCast(i),
+ .file = self.index,
+ }, macho_file);
+ if (!gop.found_existing) {
+ gop.ref.* = .{ .index = 0, .file = 0 };
+ }
+ global.* = gop.index;
+
+ if (gop.ref.getFile(macho_file) == null) {
+ gop.ref.* = .{ .index = @intCast(i), .file = self.index };
+ continue;
+ }
+
if (self.asFile().getSymbolRank(.{
.weak = flags.weak,
- }) < global.getSymbolRank(macho_file)) {
- global.value = 0;
- global.atom = 0;
- global.nlist_idx = 0;
- global.file = self.index;
- global.flags.weak = flags.weak;
- global.flags.tlv = flags.tlv;
- global.flags.dyn_ref = false;
- global.flags.tentative = false;
- global.visibility = .global;
+ }) < gop.ref.getSymbol(macho_file).?.getSymbolRank(macho_file)) {
+ gop.ref.* = .{ .index = @intCast(i), .file = self.index };
}
}
}
-pub fn resetGlobals(self: *Dylib, macho_file: *MachO) void {
- for (self.symbols.items) |sym_index| {
- const sym = macho_file.getSymbol(sym_index);
- const name = sym.name;
- const global = sym.flags.global;
- const weak_ref = sym.flags.weak_ref;
- sym.* = .{};
- sym.name = name;
- sym.flags.global = global;
- sym.flags.weak_ref = weak_ref;
- }
-}
-
pub fn isAlive(self: Dylib, macho_file: *MachO) bool {
if (!macho_file.dead_strip_dylibs) return self.explicit or self.referenced or self.needed;
return self.referenced or self.needed;
@@ -550,30 +556,31 @@ pub fn markReferenced(self: *Dylib, macho_file: *MachO) void {
const tracy = trace(@src());
defer tracy.end();
- for (self.symbols.items) |global_index| {
- const global = macho_file.getSymbol(global_index);
- const file_ptr = global.getFile(macho_file) orelse continue;
- if (file_ptr.getIndex() != self.index) continue;
+ for (0..self.symbols.items.len) |i| {
+ const ref = self.getSymbolRef(@intCast(i), macho_file);
+ const file = ref.getFile(macho_file) orelse continue;
+ if (file.getIndex() != self.index) continue;
+ const global = ref.getSymbol(macho_file).?;
if (global.isLocal()) continue;
self.referenced = true;
break;
}
}
-pub fn calcSymtabSize(self: *Dylib, macho_file: *MachO) !void {
+pub fn calcSymtabSize(self: *Dylib, macho_file: *MachO) void {
const tracy = trace(@src());
defer tracy.end();
- for (self.symbols.items) |global_index| {
- const global = macho_file.getSymbol(global_index);
- const file_ptr = global.getFile(macho_file) orelse continue;
- if (file_ptr.getIndex() != self.index) continue;
- if (global.isLocal()) continue;
- assert(global.flags.import);
- global.flags.output_symtab = true;
- try global.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file);
+ for (self.symbols.items, 0..) |*sym, i| {
+ const ref = self.getSymbolRef(@intCast(i), macho_file);
+ const file = ref.getFile(macho_file) orelse continue;
+ if (file.getIndex() != self.index) continue;
+ if (sym.isLocal()) continue;
+ assert(sym.flags.import);
+ sym.flags.output_symtab = true;
+ sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file);
self.output_symtab_ctx.nimports += 1;
- self.output_symtab_ctx.strsize += @as(u32, @intCast(global.getName(macho_file).len + 1));
+ self.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(macho_file).len + 1));
}
}
@@ -581,17 +588,20 @@ pub fn writeSymtab(self: Dylib, macho_file: *MachO, ctx: anytype) void {
const tracy = trace(@src());
defer tracy.end();
- for (self.symbols.items) |global_index| {
- const global = macho_file.getSymbol(global_index);
- const file = global.getFile(macho_file) orelse continue;
+ var n_strx = self.output_symtab_ctx.stroff;
+ for (self.symbols.items, 0..) |sym, i| {
+ const ref = self.getSymbolRef(@intCast(i), macho_file);
+ const file = ref.getFile(macho_file) orelse continue;
if (file.getIndex() != self.index) continue;
- const idx = global.getOutputSymtabIndex(macho_file) orelse continue;
- const n_strx = @as(u32, @intCast(ctx.strtab.items.len));
- ctx.strtab.appendSliceAssumeCapacity(global.getName(macho_file));
- ctx.strtab.appendAssumeCapacity(0);
+ const idx = sym.getOutputSymtabIndex(macho_file) orelse continue;
const out_sym = &ctx.symtab.items[idx];
out_sym.n_strx = n_strx;
- global.setOutputSym(macho_file, out_sym);
+ sym.setOutputSym(macho_file, out_sym);
+ const name = sym.getName(macho_file);
+ @memcpy(ctx.strtab.items[n_strx..][0..name.len], name);
+ n_strx += @intCast(name.len);
+ ctx.strtab.items[n_strx] = 0;
+ n_strx += 1;
}
}
@@ -605,7 +615,7 @@ fn addString(self: *Dylib, allocator: Allocator, name: []const u8) !u32 {
return off;
}
-pub inline fn getString(self: Dylib, off: u32) [:0]const u8 {
+pub fn getString(self: Dylib, off: u32) [:0]const u8 {
assert(off < self.strtab.items.len);
return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);
}
@@ -614,6 +624,66 @@ pub fn asFile(self: *Dylib) File {
return .{ .dylib = self };
}
+fn addSymbol(self: *Dylib, allocator: Allocator) !Symbol.Index {
+ try self.symbols.ensureUnusedCapacity(allocator, 1);
+ return self.addSymbolAssumeCapacity();
+}
+
+fn addSymbolAssumeCapacity(self: *Dylib) Symbol.Index {
+ const index: Symbol.Index = @intCast(self.symbols.items.len);
+ const symbol = self.symbols.addOneAssumeCapacity();
+ symbol.* = .{ .file = self.index };
+ return index;
+}
+
+pub fn getSymbolRef(self: Dylib, index: Symbol.Index, macho_file: *MachO) MachO.Ref {
+ const global_index = self.globals.items[index];
+ if (macho_file.resolver.get(global_index)) |ref| return ref;
+ return .{ .index = index, .file = self.index };
+}
+
+pub fn addSymbolExtra(self: *Dylib, allocator: Allocator, extra: Symbol.Extra) !u32 {
+ const fields = @typeInfo(Symbol.Extra).Struct.fields;
+ try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len);
+ return self.addSymbolExtraAssumeCapacity(extra);
+}
+
+fn addSymbolExtraAssumeCapacity(self: *Dylib, extra: Symbol.Extra) u32 {
+ const index = @as(u32, @intCast(self.symbols_extra.items.len));
+ const fields = @typeInfo(Symbol.Extra).Struct.fields;
+ inline for (fields) |field| {
+ self.symbols_extra.appendAssumeCapacity(switch (field.type) {
+ u32 => @field(extra, field.name),
+ else => @compileError("bad field type"),
+ });
+ }
+ return index;
+}
+
+pub fn getSymbolExtra(self: Dylib, index: u32) Symbol.Extra {
+ const fields = @typeInfo(Symbol.Extra).Struct.fields;
+ var i: usize = index;
+ var result: Symbol.Extra = undefined;
+ inline for (fields) |field| {
+ @field(result, field.name) = switch (field.type) {
+ u32 => self.symbols_extra.items[i],
+ else => @compileError("bad field type"),
+ };
+ i += 1;
+ }
+ return result;
+}
+
+pub fn setSymbolExtra(self: *Dylib, index: u32, extra: Symbol.Extra) void {
+ const fields = @typeInfo(Symbol.Extra).Struct.fields;
+ inline for (fields, 0..) |field, i| {
+ self.symbols_extra.items[index + i] = switch (field.type) {
+ u32 => @field(extra, field.name),
+ else => @compileError("bad field type"),
+ };
+ }
+}
+
pub fn format(
self: *Dylib,
comptime unused_fmt_string: []const u8,
@@ -648,10 +718,16 @@ fn formatSymtab(
_ = unused_fmt_string;
_ = options;
const dylib = ctx.dylib;
+ const macho_file = ctx.macho_file;
try writer.writeAll(" globals\n");
- for (dylib.symbols.items) |index| {
- const global = ctx.macho_file.getSymbol(index);
- try writer.print(" {}\n", .{global.fmt(ctx.macho_file)});
+ for (dylib.symbols.items, 0..) |sym, i| {
+ const ref = dylib.getSymbolRef(@intCast(i), macho_file);
+ if (ref.getFile(macho_file) == null) {
+ // TODO any better way of handling this?
+ try writer.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});
+ } else {
+ try writer.print(" {}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
+ }
}
}
diff --git a/src/link/MachO/InternalObject.zig b/src/link/MachO/InternalObject.zig
@@ -1,13 +1,28 @@
index: File.Index,
sections: std.MultiArrayList(Section) = .{},
-atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
-symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
+atoms: std.ArrayListUnmanaged(Atom) = .{},
+atoms_indexes: std.ArrayListUnmanaged(Atom.Index) = .{},
+atoms_extra: std.ArrayListUnmanaged(u32) = .{},
+symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
+strtab: std.ArrayListUnmanaged(u8) = .{},
+symbols: std.ArrayListUnmanaged(Symbol) = .{},
+symbols_extra: std.ArrayListUnmanaged(u32) = .{},
+globals: std.ArrayListUnmanaged(MachO.SymbolResolver.Index) = .{},
objc_methnames: std.ArrayListUnmanaged(u8) = .{},
objc_selrefs: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64),
-num_rebase_relocs: u32 = 0,
+force_undefined: std.ArrayListUnmanaged(Symbol.Index) = .{},
+entry_index: ?Symbol.Index = null,
+dyld_stub_binder_index: ?Symbol.Index = null,
+dyld_private_index: ?Symbol.Index = null,
+objc_msg_send_index: ?Symbol.Index = null,
+mh_execute_header_index: ?Symbol.Index = null,
+mh_dylib_header_index: ?Symbol.Index = null,
+dso_handle_index: ?Symbol.Index = null,
+boundary_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
+
output_symtab_ctx: MachO.SymtabCtx = .{},
pub fn deinit(self: *InternalObject, allocator: Allocator) void {
@@ -16,39 +31,224 @@ pub fn deinit(self: *InternalObject, allocator: Allocator) void {
}
self.sections.deinit(allocator);
self.atoms.deinit(allocator);
+ self.atoms_indexes.deinit(allocator);
+ self.atoms_extra.deinit(allocator);
+ self.symtab.deinit(allocator);
+ self.strtab.deinit(allocator);
self.symbols.deinit(allocator);
+ self.symbols_extra.deinit(allocator);
+ self.globals.deinit(allocator);
self.objc_methnames.deinit(allocator);
+ self.force_undefined.deinit(allocator);
+ self.boundary_symbols.deinit(allocator);
+}
+
+pub fn init(self: *InternalObject, allocator: Allocator) !void {
+ // Atom at index 0 is reserved as null atom.
+ try self.atoms.append(allocator, .{});
+ try self.atoms_extra.append(allocator, 0);
+ // Null byte in strtab
+ try self.strtab.append(allocator, 0);
}
-pub fn addSymbol(self: *InternalObject, name: [:0]const u8, macho_file: *MachO) !Symbol.Index {
+pub fn initSymbols(self: *InternalObject, macho_file: *MachO) !void {
+ const newSymbolAssumeCapacity = struct {
+ fn newSymbolAssumeCapacity(obj: *InternalObject, name: u32, args: struct {
+ type: u8 = macho.N_UNDF | macho.N_EXT,
+ desc: u16 = 0,
+ }) Symbol.Index {
+ const index = obj.addSymbolAssumeCapacity();
+ const symbol = &obj.symbols.items[index];
+ symbol.name = name;
+ symbol.extra = obj.addSymbolExtraAssumeCapacity(.{});
+ symbol.flags.dyn_ref = args.desc & macho.REFERENCED_DYNAMICALLY != 0;
+ symbol.visibility = if (args.type & macho.N_EXT != 0) blk: {
+ break :blk if (args.type & macho.N_PEXT != 0) .hidden else .global;
+ } else .local;
+
+ const nlist_idx: u32 = @intCast(obj.symtab.items.len);
+ const nlist = obj.symtab.addOneAssumeCapacity();
+ nlist.* = .{
+ .n_strx = name,
+ .n_type = args.type,
+ .n_sect = 0,
+ .n_desc = args.desc,
+ .n_value = 0,
+ };
+ symbol.nlist_idx = nlist_idx;
+ return index;
+ }
+ }.newSymbolAssumeCapacity;
+
const gpa = macho_file.base.comp.gpa;
- try self.symbols.ensureUnusedCapacity(gpa, 1);
- const off = try macho_file.strings.insert(gpa, name);
- const gop = try macho_file.getOrCreateGlobal(off);
- self.symbols.addOneAssumeCapacity().* = gop.index;
- const sym = macho_file.getSymbol(gop.index);
- sym.file = self.index;
- sym.value = 0;
- sym.atom = 0;
- sym.nlist_idx = 0;
- sym.flags = .{ .global = true };
- return gop.index;
+ var nsyms = macho_file.base.comp.force_undefined_symbols.keys().len;
+ nsyms += 1; // dyld_stub_binder
+ nsyms += 1; // _objc_msgSend
+ if (!macho_file.base.isDynLib()) {
+ nsyms += 1; // entry
+ nsyms += 1; // __mh_execute_header
+ } else {
+ nsyms += 1; // __mh_dylib_header
+ }
+ nsyms += 1; // ___dso_handle
+ nsyms += 1; // dyld_private
+
+ try self.symbols.ensureTotalCapacityPrecise(gpa, nsyms);
+ try self.symbols_extra.ensureTotalCapacityPrecise(gpa, nsyms * @sizeOf(Symbol.Extra));
+ try self.symtab.ensureTotalCapacityPrecise(gpa, nsyms);
+ try self.globals.ensureTotalCapacityPrecise(gpa, nsyms);
+ self.globals.resize(gpa, nsyms) catch unreachable;
+ @memset(self.globals.items, 0);
+
+ try self.force_undefined.ensureTotalCapacityPrecise(gpa, macho_file.base.comp.force_undefined_symbols.keys().len);
+ for (macho_file.base.comp.force_undefined_symbols.keys()) |name| {
+ self.force_undefined.addOneAssumeCapacity().* = newSymbolAssumeCapacity(self, try self.addString(gpa, name), .{});
+ }
+
+ self.dyld_stub_binder_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "dyld_stub_binder"), .{});
+ self.objc_msg_send_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "_objc_msgSend"), .{});
+
+ if (!macho_file.base.isDynLib()) {
+ self.entry_index = newSymbolAssumeCapacity(self, try self.addString(gpa, macho_file.entry_name orelse "_main"), .{});
+ self.mh_execute_header_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__mh_execute_header"), .{
+ .type = macho.N_SECT | macho.N_EXT,
+ .desc = macho.REFERENCED_DYNAMICALLY,
+ });
+ } else {
+ self.mh_dylib_header_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__mh_dylib_header"), .{
+ .type = macho.N_SECT | macho.N_EXT,
+ });
+ }
+
+ self.dso_handle_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "___dso_handle"), .{
+ .type = macho.N_SECT | macho.N_EXT,
+ });
+ self.dyld_private_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "dyld_private"), .{
+ .type = macho.N_SECT,
+ });
}
-/// Creates a fake input sections __TEXT,__objc_methname and __DATA,__objc_selrefs.
-pub fn addObjcMsgsendSections(self: *InternalObject, sym_name: []const u8, macho_file: *MachO) !Atom.Index {
- const methname_atom_index = try self.addObjcMethnameSection(sym_name, macho_file);
- return try self.addObjcSelrefsSection(methname_atom_index, macho_file);
+pub fn resolveSymbols(self: *InternalObject, macho_file: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const gpa = macho_file.base.comp.gpa;
+
+ for (self.symtab.items, self.globals.items, 0..) |nlist, *global, i| {
+ const gop = try macho_file.resolver.getOrPut(gpa, .{
+ .index = @intCast(i),
+ .file = self.index,
+ }, macho_file);
+ if (!gop.found_existing) {
+ gop.ref.* = .{ .index = 0, .file = 0 };
+ }
+ global.* = gop.index;
+
+ if (nlist.undf()) continue;
+ if (gop.ref.getFile(macho_file) == null) {
+ gop.ref.* = .{ .index = @intCast(i), .file = self.index };
+ continue;
+ }
+
+ if (self.asFile().getSymbolRank(.{
+ .archive = false,
+ .weak = false,
+ .tentative = false,
+ }) < gop.ref.getSymbol(macho_file).?.getSymbolRank(macho_file)) {
+ gop.ref.* = .{ .index = @intCast(i), .file = self.index };
+ }
+ }
}
-fn addObjcMethnameSection(self: *InternalObject, methname: []const u8, macho_file: *MachO) !Atom.Index {
+pub fn resolveBoundarySymbols(self: *InternalObject, macho_file: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
const gpa = macho_file.base.comp.gpa;
- const atom_index = try macho_file.addAtom();
- try self.atoms.append(gpa, atom_index);
+ var boundary_symbols = std.StringArrayHashMap(MachO.Ref).init(gpa);
+ defer boundary_symbols.deinit();
+
+ for (macho_file.objects.items) |index| {
+ const object = macho_file.getFile(index).?.object;
+ for (object.symbols.items, 0..) |sym, i| {
+ const nlist = object.symtab.items(.nlist)[i];
+ if (!nlist.undf() or !nlist.ext()) continue;
+ const ref = object.getSymbolRef(@intCast(i), macho_file);
+ if (ref.getFile(macho_file) != null) continue;
+ const name = sym.getName(macho_file);
+ if (mem.startsWith(u8, name, "segment$start$") or
+ mem.startsWith(u8, name, "segment$stop$") or
+ mem.startsWith(u8, name, "section$start$") or
+ mem.startsWith(u8, name, "section$stop$"))
+ {
+ const gop = try boundary_symbols.getOrPut(name);
+ if (!gop.found_existing) {
+ gop.value_ptr.* = .{ .index = @intCast(i), .file = index };
+ }
+ }
+ }
+ }
+
+ const nsyms = boundary_symbols.values().len;
+ try self.boundary_symbols.ensureTotalCapacityPrecise(gpa, nsyms);
+ try self.symbols.ensureUnusedCapacity(gpa, nsyms);
+ try self.symtab.ensureUnusedCapacity(gpa, nsyms);
+ try self.symbols_extra.ensureUnusedCapacity(gpa, nsyms * @sizeOf(Symbol.Extra));
+ try self.globals.ensureUnusedCapacity(gpa, nsyms);
+
+ for (boundary_symbols.keys(), boundary_symbols.values()) |name, ref| {
+ const name_off = try self.addString(gpa, name);
+ const sym_index = self.addSymbolAssumeCapacity();
+ self.boundary_symbols.appendAssumeCapacity(sym_index);
+ const sym = &self.symbols.items[sym_index];
+ sym.name = name_off;
+ sym.visibility = .local;
+ const nlist_idx: u32 = @intCast(self.symtab.items.len);
+ const nlist = self.symtab.addOneAssumeCapacity();
+ nlist.* = .{
+ .n_strx = name_off,
+ .n_type = macho.N_SECT,
+ .n_sect = 0,
+ .n_desc = 0,
+ .n_value = 0,
+ };
+ sym.nlist_idx = nlist_idx;
+ sym.extra = self.addSymbolExtraAssumeCapacity(.{});
+
+ const idx = ref.getFile(macho_file).?.object.globals.items[ref.index];
+ self.globals.addOneAssumeCapacity().* = idx;
+ macho_file.resolver.values.items[idx - 1] = .{ .index = sym_index, .file = self.index };
+ }
+}
+
+pub fn markLive(self: *InternalObject, macho_file: *MachO) void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ for (0..self.symbols.items.len) |i| {
+ const nlist = self.symtab.items[i];
+ if (!nlist.ext()) continue;
+
+ const ref = self.getSymbolRef(@intCast(i), macho_file);
+ const file = ref.getFile(macho_file) orelse continue;
+ if (file == .object and !file.object.alive) {
+ file.object.alive = true;
+ file.object.markLive(macho_file);
+ }
+ }
+}
- const atom = macho_file.getAtom(atom_index).?;
- atom.atom_index = atom_index;
- atom.file = self.index;
+/// Creates a fake input sections __TEXT,__objc_methname and __DATA,__objc_selrefs.
+pub fn addObjcMsgsendSections(self: *InternalObject, sym_name: []const u8, macho_file: *MachO) !Symbol.Index {
+ const methname_sym_index = try self.addObjcMethnameSection(sym_name, macho_file);
+ return try self.addObjcSelrefsSection(methname_sym_index, macho_file);
+}
+
+fn addObjcMethnameSection(self: *InternalObject, methname: []const u8, macho_file: *MachO) !Symbol.Index {
+ const gpa = macho_file.base.comp.gpa;
+ const atom_index = try self.addAtom(gpa);
+ try self.atoms_indexes.append(gpa, atom_index);
+ const atom = self.getAtom(atom_index).?;
atom.size = methname.len + 1;
atom.alignment = .@"1";
@@ -64,17 +264,32 @@ fn addObjcMethnameSection(self: *InternalObject, methname: []const u8, macho_fil
try self.objc_methnames.ensureUnusedCapacity(gpa, methname.len + 1);
self.objc_methnames.writer(gpa).print("{s}\x00", .{methname}) catch unreachable;
+ const name_str = try self.addString(gpa, "ltmp");
+ const sym_index = try self.addSymbol(gpa);
+ const sym = &self.symbols.items[sym_index];
+ sym.name = name_str;
+ sym.atom_ref = .{ .index = atom_index, .file = self.index };
+ sym.extra = try self.addSymbolExtra(gpa, .{});
+ const nlist_idx: u32 = @intCast(self.symtab.items.len);
+ const nlist = try self.symtab.addOne(gpa);
+ nlist.* = .{
+ .n_strx = name_str,
+ .n_type = macho.N_SECT,
+ .n_sect = @intCast(n_sect + 1),
+ .n_desc = 0,
+ .n_value = 0,
+ };
+ sym.nlist_idx = nlist_idx;
+ try self.globals.append(gpa, 0);
+
return atom_index;
}
-fn addObjcSelrefsSection(self: *InternalObject, methname_atom_index: Atom.Index, macho_file: *MachO) !Atom.Index {
+fn addObjcSelrefsSection(self: *InternalObject, methname_sym_index: Symbol.Index, macho_file: *MachO) !Symbol.Index {
const gpa = macho_file.base.comp.gpa;
- const atom_index = try macho_file.addAtom();
- try self.atoms.append(gpa, atom_index);
-
- const atom = macho_file.getAtom(atom_index).?;
- atom.atom_index = atom_index;
- atom.file = self.index;
+ const atom_index = try self.addAtom(gpa);
+ try self.atoms_indexes.append(gpa, atom_index);
+ const atom = self.getAtom(atom_index).?;
atom.size = @sizeOf(u64);
atom.alignment = .@"8";
@@ -90,9 +305,9 @@ fn addObjcSelrefsSection(self: *InternalObject, methname_atom_index: Atom.Index,
const relocs = &self.sections.items(.relocs)[n_sect];
try relocs.ensureUnusedCapacity(gpa, 1);
relocs.appendAssumeCapacity(.{
- .tag = .local,
+ .tag = .@"extern",
.offset = 0,
- .target = methname_atom_index,
+ .target = methname_sym_index,
.addend = 0,
.type = .unsigned,
.meta = .{
@@ -102,140 +317,285 @@ fn addObjcSelrefsSection(self: *InternalObject, methname_atom_index: Atom.Index,
.has_subtractor = false,
},
});
- try atom.addExtra(.{ .rel_index = 0, .rel_count = 1 }, macho_file);
- atom.flags.relocs = true;
- self.num_rebase_relocs += 1;
+ atom.addExtra(.{ .rel_index = 0, .rel_count = 1 }, macho_file);
+
+ const sym_index = try self.addSymbol(gpa);
+ const sym = &self.symbols.items[sym_index];
+ sym.atom_ref = .{ .index = atom_index, .file = self.index };
+ sym.extra = try self.addSymbolExtra(gpa, .{});
+ const nlist_idx: u32 = @intCast(self.symtab.items.len);
+ const nlist = try self.symtab.addOne(gpa);
+ nlist.* = .{
+ .n_strx = 0,
+ .n_type = macho.N_SECT,
+ .n_sect = @intCast(n_sect + 1),
+ .n_desc = 0,
+ .n_value = 0,
+ };
+ sym.nlist_idx = nlist_idx;
+ try self.globals.append(gpa, 0);
+ atom.addExtra(.{ .literal_symbol_index = sym_index }, macho_file);
- return atom_index;
+ return sym_index;
}
-pub fn resolveLiterals(self: InternalObject, lp: *MachO.LiteralPool, macho_file: *MachO) !void {
+pub fn resolveObjcMsgSendSymbols(self: *InternalObject, macho_file: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const gpa = macho_file.base.comp.gpa;
+
+ var objc_msgsend_syms = std.StringArrayHashMap(MachO.Ref).init(gpa);
+ defer objc_msgsend_syms.deinit();
+
+ for (macho_file.objects.items) |index| {
+ const object = macho_file.getFile(index).?.object;
+
+ for (object.symbols.items, 0..) |sym, i| {
+ const nlist = object.symtab.items(.nlist)[i];
+ if (!nlist.ext()) continue;
+ if (!nlist.undf()) continue;
+
+ const ref = object.getSymbolRef(@intCast(i), macho_file);
+ if (ref.getFile(macho_file) != null) continue;
+
+ const name = sym.getName(macho_file);
+ if (mem.startsWith(u8, name, "_objc_msgSend$")) {
+ const gop = try objc_msgsend_syms.getOrPut(name);
+ if (!gop.found_existing) {
+ gop.value_ptr.* = .{ .index = @intCast(i), .file = index };
+ }
+ }
+ }
+ }
+
+ for (objc_msgsend_syms.keys(), objc_msgsend_syms.values()) |sym_name, ref| {
+ const name = MachO.eatPrefix(sym_name, "_objc_msgSend$").?;
+ const selrefs_index = try self.addObjcMsgsendSections(name, macho_file);
+
+ const name_off = try self.addString(gpa, sym_name);
+ const sym_index = try self.addSymbol(gpa);
+ const sym = &self.symbols.items[sym_index];
+ sym.name = name_off;
+ sym.visibility = .hidden;
+ const nlist_idx: u32 = @intCast(self.symtab.items.len);
+ const nlist = try self.symtab.addOne(gpa);
+ nlist.* = .{
+ .n_strx = name_off,
+ .n_type = macho.N_SECT | macho.N_EXT | macho.N_PEXT,
+ .n_sect = 0,
+ .n_desc = 0,
+ .n_value = 0,
+ };
+ sym.nlist_idx = nlist_idx;
+ sym.extra = try self.addSymbolExtra(gpa, .{ .objc_selrefs = selrefs_index });
+ sym.flags.objc_stubs = true;
+
+ const idx = ref.getFile(macho_file).?.object.globals.items[ref.index];
+ try self.globals.append(gpa, idx);
+ macho_file.resolver.values.items[idx - 1] = .{ .index = sym_index, .file = self.index };
+ }
+}
+
+pub fn resolveLiterals(self: *InternalObject, lp: *MachO.LiteralPool, macho_file: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
const gpa = macho_file.base.comp.gpa;
var buffer = std.ArrayList(u8).init(gpa);
defer buffer.deinit();
const slice = self.sections.slice();
- for (slice.items(.header), self.atoms.items, 0..) |header, atom_index, n_sect| {
- if (Object.isCstringLiteral(header) or Object.isFixedSizeLiteral(header)) {
- const data = try self.getSectionData(@intCast(n_sect));
- const atom = macho_file.getAtom(atom_index).?;
- const res = try lp.insert(gpa, header.type(), data);
- if (!res.found_existing) {
- res.atom.* = atom_index;
- }
- atom.flags.literal_pool = true;
- try atom.addExtra(.{ .literal_index = res.index }, macho_file);
- } else if (Object.isPtrLiteral(header)) {
- const atom = macho_file.getAtom(atom_index).?;
- const relocs = atom.getRelocs(macho_file);
- assert(relocs.len == 1);
- const rel = relocs[0];
- assert(rel.tag == .local);
- const target = macho_file.getAtom(rel.target).?;
- const addend = std.math.cast(u32, rel.addend) orelse return error.Overflow;
- const target_size = std.math.cast(usize, target.size) orelse return error.Overflow;
- try buffer.ensureUnusedCapacity(target_size);
- buffer.resize(target_size) catch unreachable;
- try target.getData(macho_file, buffer.items);
- const res = try lp.insert(gpa, header.type(), buffer.items[addend..]);
- buffer.clearRetainingCapacity();
- if (!res.found_existing) {
- res.atom.* = atom_index;
- }
- atom.flags.literal_pool = true;
- try atom.addExtra(.{ .literal_index = res.index }, macho_file);
+ for (slice.items(.header), self.getAtoms()) |header, atom_index| {
+ if (!Object.isPtrLiteral(header)) continue;
+ const atom = self.getAtom(atom_index).?;
+ const relocs = atom.getRelocs(macho_file);
+ assert(relocs.len == 1);
+ const rel = relocs[0];
+ assert(rel.tag == .@"extern");
+ const target = rel.getTargetSymbol(atom.*, macho_file).getAtom(macho_file).?;
+ const target_size = std.math.cast(usize, target.size) orelse return error.Overflow;
+ try buffer.ensureUnusedCapacity(target_size);
+ buffer.resize(target_size) catch unreachable;
+ @memcpy(buffer.items, try self.getSectionData(target.n_sect));
+ const res = try lp.insert(gpa, header.type(), buffer.items);
+ buffer.clearRetainingCapacity();
+ if (!res.found_existing) {
+ res.ref.* = .{ .index = atom.getExtra(macho_file).literal_symbol_index, .file = self.index };
+ } else {
+ const lp_sym = lp.getSymbol(res.index, macho_file);
+ const lp_atom = lp_sym.getAtom(macho_file).?;
+ lp_atom.alignment = lp_atom.alignment.max(atom.alignment);
+ atom.flags.alive = false;
}
+ atom.addExtra(.{ .literal_pool_index = res.index }, macho_file);
}
}
-pub fn dedupLiterals(self: InternalObject, lp: MachO.LiteralPool, macho_file: *MachO) void {
- for (self.atoms.items) |atom_index| {
- const atom = macho_file.getAtom(atom_index) orelse continue;
+pub fn dedupLiterals(self: *InternalObject, lp: MachO.LiteralPool, macho_file: *MachO) void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ for (self.getAtoms()) |atom_index| {
+ const atom = self.getAtom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
- if (!atom.flags.relocs) continue;
const relocs = blk: {
- const extra = atom.getExtra(macho_file).?;
+ const extra = atom.getExtra(macho_file);
const relocs = self.sections.items(.relocs)[atom.n_sect].items;
break :blk relocs[extra.rel_index..][0..extra.rel_count];
};
- for (relocs) |*rel| switch (rel.tag) {
- .local => {
- const target = macho_file.getAtom(rel.target).?;
- if (target.getLiteralPoolIndex(macho_file)) |lp_index| {
- const lp_atom = lp.getAtom(lp_index, macho_file);
- if (target.atom_index != lp_atom.atom_index) {
- lp_atom.alignment = lp_atom.alignment.max(target.alignment);
- target.flags.alive = false;
- rel.target = lp_atom.atom_index;
- }
- }
- },
- .@"extern" => {
- const target_sym = rel.getTargetSymbol(macho_file);
- if (target_sym.getAtom(macho_file)) |target_atom| {
- if (target_atom.getLiteralPoolIndex(macho_file)) |lp_index| {
- const lp_atom = lp.getAtom(lp_index, macho_file);
- if (target_atom.atom_index != lp_atom.atom_index) {
- lp_atom.alignment = lp_atom.alignment.max(target_atom.alignment);
- target_atom.flags.alive = false;
- target_sym.atom = lp_atom.atom_index;
- }
- }
- }
- },
- };
+ for (relocs) |*rel| {
+ if (rel.tag != .@"extern") continue;
+ const target_sym_ref = rel.getTargetSymbolRef(atom.*, macho_file);
+ const file = target_sym_ref.getFile(macho_file) orelse continue;
+ if (file.getIndex() != self.index) continue;
+ const target_sym = target_sym_ref.getSymbol(macho_file).?;
+ const target_atom = target_sym.getAtom(macho_file) orelse continue;
+ if (!Object.isPtrLiteral(target_atom.getInputSection(macho_file))) continue;
+ const lp_index = target_atom.getExtra(macho_file).literal_pool_index;
+ const lp_sym = lp.getSymbol(lp_index, macho_file);
+ const lp_atom_ref = lp_sym.atom_ref;
+ if (target_atom.atom_index != lp_atom_ref.index or target_atom.file != lp_atom_ref.file) {
+ target_sym.atom_ref = lp_atom_ref;
+ }
+ }
}
- for (self.symbols.items) |sym_index| {
- const sym = macho_file.getSymbol(sym_index);
+ for (self.symbols.items) |*sym| {
if (!sym.flags.objc_stubs) continue;
- var extra = sym.getExtra(macho_file).?;
- const atom = macho_file.getAtom(extra.objc_selrefs).?;
- if (atom.getLiteralPoolIndex(macho_file)) |lp_index| {
- const lp_atom = lp.getAtom(lp_index, macho_file);
- if (atom.atom_index != lp_atom.atom_index) {
- lp_atom.alignment = lp_atom.alignment.max(atom.alignment);
- atom.flags.alive = false;
- extra.objc_selrefs = lp_atom.atom_index;
- sym.setExtra(extra, macho_file);
+ const extra = sym.getExtra(macho_file);
+ const file = sym.getFile(macho_file).?;
+ if (file.getIndex() != self.index) continue;
+ const tsym = switch (file) {
+ .dylib => unreachable,
+ inline else => |x| &x.symbols.items[extra.objc_selrefs],
+ };
+ const atom = tsym.getAtom(macho_file) orelse continue;
+ if (!Object.isPtrLiteral(atom.getInputSection(macho_file))) continue;
+ const lp_index = atom.getExtra(macho_file).literal_pool_index;
+ const lp_sym = lp.getSymbol(lp_index, macho_file);
+ const lp_atom_ref = lp_sym.atom_ref;
+ if (atom.atom_index != lp_atom_ref.index or atom.file != lp_atom_ref.file) {
+ tsym.atom_ref = lp_atom_ref;
+ }
+ }
+}
+
+pub fn scanRelocs(self: *InternalObject, macho_file: *MachO) void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ if (self.getEntryRef(macho_file)) |ref| {
+ if (ref.getFile(macho_file) != null) {
+ const sym = ref.getSymbol(macho_file).?;
+ if (sym.flags.import) sym.flags.stubs = true;
+ }
+ }
+ if (self.getDyldStubBinderRef(macho_file)) |ref| {
+ if (ref.getFile(macho_file) != null) {
+ const sym = ref.getSymbol(macho_file).?;
+ sym.flags.needs_got = true;
+ }
+ }
+ if (self.getObjcMsgSendRef(macho_file)) |ref| {
+ if (ref.getFile(macho_file) != null) {
+ const sym = ref.getSymbol(macho_file).?;
+ // TODO is it always needed, or only if we are synthesising fast stubs
+ sym.flags.needs_got = true;
+ }
+ }
+}
+
+pub fn allocateSyntheticSymbols(self: *InternalObject, macho_file: *MachO) void {
+ const text_seg = macho_file.getTextSegment();
+
+ if (self.mh_execute_header_index) |index| {
+ const ref = self.getSymbolRef(index, macho_file);
+ if (ref.getFile(macho_file)) |file| {
+ if (file.getIndex() == self.index) {
+ const sym = &self.symbols.items[index];
+ sym.value = text_seg.vmaddr;
+ }
+ }
+ }
+
+ if (macho_file.data_sect_index) |idx| {
+ const sect = macho_file.sections.items(.header)[idx];
+ for (&[_]?Symbol.Index{
+ self.dso_handle_index,
+ self.mh_dylib_header_index,
+ self.dyld_private_index,
+ }) |maybe_index| {
+ if (maybe_index) |index| {
+ const ref = self.getSymbolRef(index, macho_file);
+ if (ref.getFile(macho_file)) |file| {
+ if (file.getIndex() == self.index) {
+ const sym = &self.symbols.items[index];
+ sym.value = sect.addr;
+ sym.out_n_sect = idx;
+ }
+ }
}
}
}
}
-pub fn calcSymtabSize(self: *InternalObject, macho_file: *MachO) !void {
- for (self.symbols.items) |sym_index| {
- const sym = macho_file.getSymbol(sym_index);
- if (sym.getFile(macho_file)) |file| if (file.getIndex() != self.index) continue;
+pub fn calcSymtabSize(self: *InternalObject, macho_file: *MachO) void {
+ for (self.symbols.items, 0..) |*sym, i| {
+ const ref = self.getSymbolRef(@intCast(i), macho_file);
+ const file = ref.getFile(macho_file) orelse continue;
+ if (file.getIndex() != self.index) continue;
+ if (sym.getName(macho_file).len == 0) continue;
sym.flags.output_symtab = true;
if (sym.isLocal()) {
- try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file);
+ sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file);
self.output_symtab_ctx.nlocals += 1;
} else if (sym.flags.@"export") {
- try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nexports }, macho_file);
+ sym.addExtra(.{ .symtab = self.output_symtab_ctx.nexports }, macho_file);
self.output_symtab_ctx.nexports += 1;
} else {
assert(sym.flags.import);
- try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file);
+ sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file);
self.output_symtab_ctx.nimports += 1;
}
self.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(macho_file).len + 1));
}
}
+pub fn writeAtoms(self: *InternalObject, macho_file: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ for (self.getAtoms()) |atom_index| {
+ const atom = self.getAtom(atom_index) orelse continue;
+ if (!atom.flags.alive) continue;
+ const sect = atom.getInputSection(macho_file);
+ if (sect.isZerofill()) continue;
+ const off = std.math.cast(usize, atom.value) orelse return error.Overflow;
+ const size = std.math.cast(usize, atom.size) orelse return error.Overflow;
+ const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items[off..][0..size];
+ @memcpy(buffer, try self.getSectionData(atom.n_sect));
+ try atom.resolveRelocs(macho_file, buffer);
+ }
+}
+
pub fn writeSymtab(self: InternalObject, macho_file: *MachO, ctx: anytype) void {
- for (self.symbols.items) |sym_index| {
- const sym = macho_file.getSymbol(sym_index);
- if (sym.getFile(macho_file)) |file| if (file.getIndex() != self.index) continue;
+ var n_strx = self.output_symtab_ctx.stroff;
+ for (self.symbols.items, 0..) |sym, i| {
+ const ref = self.getSymbolRef(@intCast(i), macho_file);
+ const file = ref.getFile(macho_file) orelse continue;
+ if (file.getIndex() != self.index) continue;
const idx = sym.getOutputSymtabIndex(macho_file) orelse continue;
- const n_strx = @as(u32, @intCast(ctx.strtab.items.len));
- ctx.strtab.appendSliceAssumeCapacity(sym.getName(macho_file));
- ctx.strtab.appendAssumeCapacity(0);
const out_sym = &ctx.symtab.items[idx];
out_sym.n_strx = n_strx;
sym.setOutputSym(macho_file, out_sym);
+ const name = sym.getName(macho_file);
+ @memcpy(ctx.strtab.items[n_strx..][0..name.len], name);
+ n_strx += @intCast(name.len);
+ ctx.strtab.items[n_strx] = 0;
+ n_strx += 1;
}
}
@@ -264,30 +624,171 @@ fn getSectionData(self: *const InternalObject, index: u32) error{Overflow}![]con
@panic("ref to non-existent section");
}
-pub fn getAtomData(self: *const InternalObject, atom: Atom, buffer: []u8) error{Overflow}!void {
- assert(buffer.len == atom.size);
- const data = try self.getSectionData(atom.n_sect);
- const off = std.math.cast(usize, atom.off) orelse return error.Overflow;
- const size = std.math.cast(usize, atom.size) orelse return error.Overflow;
- @memcpy(buffer, data[off..][0..size]);
+pub fn addString(self: *InternalObject, allocator: Allocator, name: []const u8) !u32 {
+ const off: u32 = @intCast(self.strtab.items.len);
+ try self.strtab.ensureUnusedCapacity(allocator, name.len + 1);
+ self.strtab.appendSliceAssumeCapacity(name);
+ self.strtab.appendAssumeCapacity(0);
+ return off;
+}
+
+pub fn getString(self: InternalObject, off: u32) [:0]const u8 {
+ assert(off < self.strtab.items.len);
+ return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);
+}
+
+pub fn asFile(self: *InternalObject) File {
+ return .{ .internal = self };
}
pub fn getAtomRelocs(self: *const InternalObject, atom: Atom, macho_file: *MachO) []const Relocation {
- if (!atom.flags.relocs) return &[0]Relocation{};
- const extra = atom.getExtra(macho_file).?;
+ const extra = atom.getExtra(macho_file);
const relocs = self.sections.items(.relocs)[atom.n_sect];
return relocs.items[extra.rel_index..][0..extra.rel_count];
}
-pub fn getString(self: InternalObject, off: u32) [:0]const u8 {
- _ = self;
- _ = off;
- // We don't have any local strings for synthetic atoms.
- return "";
+fn addAtom(self: *InternalObject, allocator: Allocator) !Atom.Index {
+ const atom_index: Atom.Index = @intCast(self.atoms.items.len);
+ const atom = try self.atoms.addOne(allocator);
+ atom.* = .{
+ .file = self.index,
+ .atom_index = atom_index,
+ .extra = try self.addAtomExtra(allocator, .{}),
+ };
+ return atom_index;
}
-pub fn asFile(self: *InternalObject) File {
- return .{ .internal = self };
+pub fn getAtom(self: *InternalObject, atom_index: Atom.Index) ?*Atom {
+ if (atom_index == 0) return null;
+ assert(atom_index < self.atoms.items.len);
+ return &self.atoms.items[atom_index];
+}
+
+pub fn getAtoms(self: InternalObject) []const Atom.Index {
+ return self.atoms_indexes.items;
+}
+
+fn addAtomExtra(self: *InternalObject, allocator: Allocator, extra: Atom.Extra) !u32 {
+ const fields = @typeInfo(Atom.Extra).Struct.fields;
+ try self.atoms_extra.ensureUnusedCapacity(allocator, fields.len);
+ return self.addAtomExtraAssumeCapacity(extra);
+}
+
+fn addAtomExtraAssumeCapacity(self: *InternalObject, extra: Atom.Extra) u32 {
+ const index = @as(u32, @intCast(self.atoms_extra.items.len));
+ const fields = @typeInfo(Atom.Extra).Struct.fields;
+ inline for (fields) |field| {
+ self.atoms_extra.appendAssumeCapacity(switch (field.type) {
+ u32 => @field(extra, field.name),
+ else => @compileError("bad field type"),
+ });
+ }
+ return index;
+}
+
+pub fn getAtomExtra(self: InternalObject, index: u32) Atom.Extra {
+ const fields = @typeInfo(Atom.Extra).Struct.fields;
+ var i: usize = index;
+ var result: Atom.Extra = undefined;
+ inline for (fields) |field| {
+ @field(result, field.name) = switch (field.type) {
+ u32 => self.atoms_extra.items[i],
+ else => @compileError("bad field type"),
+ };
+ i += 1;
+ }
+ return result;
+}
+
+pub fn setAtomExtra(self: *InternalObject, index: u32, extra: Atom.Extra) void {
+ assert(index > 0);
+ const fields = @typeInfo(Atom.Extra).Struct.fields;
+ inline for (fields, 0..) |field, i| {
+ self.atoms_extra.items[index + i] = switch (field.type) {
+ u32 => @field(extra, field.name),
+ else => @compileError("bad field type"),
+ };
+ }
+}
+
+pub fn getEntryRef(self: InternalObject, macho_file: *MachO) ?MachO.Ref {
+ const index = self.entry_index orelse return null;
+ return self.getSymbolRef(index, macho_file);
+}
+
+pub fn getDyldStubBinderRef(self: InternalObject, macho_file: *MachO) ?MachO.Ref {
+ const index = self.dyld_stub_binder_index orelse return null;
+ return self.getSymbolRef(index, macho_file);
+}
+
+pub fn getDyldPrivateRef(self: InternalObject, macho_file: *MachO) ?MachO.Ref {
+ const index = self.dyld_private_index orelse return null;
+ return self.getSymbolRef(index, macho_file);
+}
+
+pub fn getObjcMsgSendRef(self: InternalObject, macho_file: *MachO) ?MachO.Ref {
+ const index = self.objc_msg_send_index orelse return null;
+ return self.getSymbolRef(index, macho_file);
+}
+
+pub fn addSymbol(self: *InternalObject, allocator: Allocator) !Symbol.Index {
+ try self.symbols.ensureUnusedCapacity(allocator, 1);
+ return self.addSymbolAssumeCapacity();
+}
+
+pub fn addSymbolAssumeCapacity(self: *InternalObject) Symbol.Index {
+ const index: Symbol.Index = @intCast(self.symbols.items.len);
+ const symbol = self.symbols.addOneAssumeCapacity();
+ symbol.* = .{ .file = self.index };
+ return index;
+}
+
+pub fn getSymbolRef(self: InternalObject, index: Symbol.Index, macho_file: *MachO) MachO.Ref {
+ const global_index = self.globals.items[index];
+ if (macho_file.resolver.get(global_index)) |ref| return ref;
+ return .{ .index = index, .file = self.index };
+}
+
+pub fn addSymbolExtra(self: *InternalObject, allocator: Allocator, extra: Symbol.Extra) !u32 {
+ const fields = @typeInfo(Symbol.Extra).Struct.fields;
+ try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len);
+ return self.addSymbolExtraAssumeCapacity(extra);
+}
+
+fn addSymbolExtraAssumeCapacity(self: *InternalObject, extra: Symbol.Extra) u32 {
+ const index = @as(u32, @intCast(self.symbols_extra.items.len));
+ const fields = @typeInfo(Symbol.Extra).Struct.fields;
+ inline for (fields) |field| {
+ self.symbols_extra.appendAssumeCapacity(switch (field.type) {
+ u32 => @field(extra, field.name),
+ else => @compileError("bad field type"),
+ });
+ }
+ return index;
+}
+
+pub fn getSymbolExtra(self: InternalObject, index: u32) Symbol.Extra {
+ const fields = @typeInfo(Symbol.Extra).Struct.fields;
+ var i: usize = index;
+ var result: Symbol.Extra = undefined;
+ inline for (fields) |field| {
+ @field(result, field.name) = switch (field.type) {
+ u32 => self.symbols_extra.items[i],
+ else => @compileError("bad field type"),
+ };
+ i += 1;
+ }
+ return result;
+}
+
+pub fn setSymbolExtra(self: *InternalObject, index: u32, extra: Symbol.Extra) void {
+ const fields = @typeInfo(Symbol.Extra).Struct.fields;
+ inline for (fields, 0..) |field, i| {
+ self.symbols_extra.items[index + i] = switch (field.type) {
+ u32 => @field(extra, field.name),
+ else => @compileError("bad field type"),
+ };
+ }
}
const FormatContext = struct {
@@ -311,8 +812,8 @@ fn formatAtoms(
_ = unused_fmt_string;
_ = options;
try writer.writeAll(" atoms\n");
- for (ctx.self.atoms.items) |atom_index| {
- const atom = ctx.macho_file.getAtom(atom_index).?;
+ for (ctx.self.getAtoms()) |atom_index| {
+ const atom = ctx.self.getAtom(atom_index) orelse continue;
try writer.print(" {}\n", .{atom.fmt(ctx.macho_file)});
}
}
@@ -332,10 +833,17 @@ fn formatSymtab(
) !void {
_ = unused_fmt_string;
_ = options;
+ const macho_file = ctx.macho_file;
+ const self = ctx.self;
try writer.writeAll(" symbols\n");
- for (ctx.self.symbols.items) |index| {
- const global = ctx.macho_file.getSymbol(index);
- try writer.print(" {}\n", .{global.fmt(ctx.macho_file)});
+ for (self.symbols.items, 0..) |sym, i| {
+ const ref = self.getSymbolRef(@intCast(i), macho_file);
+ if (ref.getFile(macho_file) == null) {
+ // TODO any better way of handling this?
+ try writer.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});
+ } else {
+ try writer.print(" {}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
+ }
}
}
@@ -354,6 +862,7 @@ const assert = std.debug.assert;
const macho = std.macho;
const mem = std.mem;
const std = @import("std");
+const trace = @import("../../tracy.zig").trace;
const Allocator = std.mem.Allocator;
const Atom = @import("Atom.zig");
diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig
@@ -1,16 +1,22 @@
-archive: ?InArchive = null,
+/// Non-zero for fat object files or archives
+offset: u64,
path: []const u8,
file_handle: File.HandleIndex,
mtime: u64,
index: File.Index,
+in_archive: ?InArchive = null,
header: ?macho.mach_header_64 = null,
sections: std.MultiArrayList(Section) = .{},
symtab: std.MultiArrayList(Nlist) = .{},
strtab: std.ArrayListUnmanaged(u8) = .{},
-symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
-atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
+symbols: std.ArrayListUnmanaged(Symbol) = .{},
+symbols_extra: std.ArrayListUnmanaged(u32) = .{},
+globals: std.ArrayListUnmanaged(MachO.SymbolResolver.Index) = .{},
+atoms: std.ArrayListUnmanaged(Atom) = .{},
+atoms_indexes: std.ArrayListUnmanaged(Atom.Index) = .{},
+atoms_extra: std.ArrayListUnmanaged(u32) = .{},
platform: ?MachO.Platform = null,
compile_unit: ?CompileUnit = null,
@@ -21,13 +27,14 @@ compact_unwind_sect_index: ?u8 = null,
cies: std.ArrayListUnmanaged(Cie) = .{},
fdes: std.ArrayListUnmanaged(Fde) = .{},
eh_frame_data: std.ArrayListUnmanaged(u8) = .{},
-unwind_records: std.ArrayListUnmanaged(UnwindInfo.Record.Index) = .{},
+unwind_records: std.ArrayListUnmanaged(UnwindInfo.Record) = .{},
+unwind_records_indexes: std.ArrayListUnmanaged(UnwindInfo.Record.Index) = .{},
data_in_code: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
alive: bool = true,
hidden: bool = false,
-dynamic_relocs: MachO.DynamicRelocs = .{},
+compact_unwind_ctx: CompactUnwindCtx = .{},
output_symtab_ctx: MachO.SymtabCtx = .{},
output_ar_state: Archive.ArState = .{},
@@ -39,7 +46,7 @@ pub fn isObject(path: []const u8) !bool {
}
pub fn deinit(self: *Object, allocator: Allocator) void {
- if (self.archive) |*ar| allocator.free(ar.path);
+ if (self.in_archive) |*ar| allocator.free(ar.path);
allocator.free(self.path);
for (self.sections.items(.relocs), self.sections.items(.subsections)) |*relocs, *sub| {
relocs.deinit(allocator);
@@ -49,11 +56,16 @@ pub fn deinit(self: *Object, allocator: Allocator) void {
self.symtab.deinit(allocator);
self.strtab.deinit(allocator);
self.symbols.deinit(allocator);
+ self.symbols_extra.deinit(allocator);
+ self.globals.deinit(allocator);
self.atoms.deinit(allocator);
+ self.atoms_indexes.deinit(allocator);
+ self.atoms_extra.deinit(allocator);
self.cies.deinit(allocator);
self.fdes.deinit(allocator);
self.eh_frame_data.deinit(allocator);
self.unwind_records.deinit(allocator);
+ self.unwind_records_indexes.deinit(allocator);
for (self.stab_files.items) |*sf| {
sf.stabs.deinit(allocator);
}
@@ -65,13 +77,18 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
+ log.debug("parsing {}", .{self.fmtPath()});
+
const gpa = macho_file.base.comp.gpa;
- const offset = if (self.archive) |ar| ar.offset else 0;
const handle = macho_file.getFileHandle(self.file_handle);
+ const cpu_arch = macho_file.getTarget().cpu.arch;
+
+ // Atom at index 0 is reserved as null atom
+ try self.atoms.append(gpa, .{ .extra = try self.addAtomExtra(gpa, .{}) });
var header_buffer: [@sizeOf(macho.mach_header_64)]u8 = undefined;
{
- const amt = try handle.preadAll(&header_buffer, offset);
+ const amt = try handle.preadAll(&header_buffer, self.offset);
if (amt != @sizeOf(macho.mach_header_64)) return error.InputOutput;
}
self.header = @as(*align(1) const macho.mach_header_64, @ptrCast(&header_buffer)).*;
@@ -84,7 +101,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
return error.InvalidCpuArch;
},
};
- if (macho_file.getTarget().cpu.arch != this_cpu_arch) {
+ if (cpu_arch != this_cpu_arch) {
try macho_file.reportParseError2(self.index, "invalid cpu architecture: {s}", .{@tagName(this_cpu_arch)});
return error.InvalidCpuArch;
}
@@ -92,7 +109,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
const lc_buffer = try gpa.alloc(u8, self.header.?.sizeofcmds);
defer gpa.free(lc_buffer);
{
- const amt = try handle.preadAll(lc_buffer, offset + @sizeOf(macho.mach_header_64));
+ const amt = try handle.preadAll(lc_buffer, self.offset + @sizeOf(macho.mach_header_64));
if (amt != self.header.?.sizeofcmds) return error.InputOutput;
}
@@ -119,14 +136,14 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
const cmd = lc.cast(macho.symtab_command).?;
try self.strtab.resize(gpa, cmd.strsize);
{
- const amt = try handle.preadAll(self.strtab.items, cmd.stroff + offset);
+ const amt = try handle.preadAll(self.strtab.items, cmd.stroff + self.offset);
if (amt != self.strtab.items.len) return error.InputOutput;
}
const symtab_buffer = try gpa.alloc(u8, cmd.nsyms * @sizeOf(macho.nlist_64));
defer gpa.free(symtab_buffer);
{
- const amt = try handle.preadAll(symtab_buffer, cmd.symoff + offset);
+ const amt = try handle.preadAll(symtab_buffer, cmd.symoff + self.offset);
if (amt != symtab_buffer.len) return error.InputOutput;
}
const symtab = @as([*]align(1) const macho.nlist_64, @ptrCast(symtab_buffer.ptr))[0..cmd.nsyms];
@@ -144,7 +161,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
const buffer = try gpa.alloc(u8, cmd.datasize);
defer gpa.free(buffer);
{
- const amt = try handle.preadAll(buffer, offset + cmd.dataoff);
+ const amt = try handle.preadAll(buffer, self.offset + cmd.dataoff);
if (amt != buffer.len) return error.InputOutput;
}
const ndice = @divExact(cmd.datasize, @sizeOf(macho.data_in_code_entry));
@@ -196,39 +213,39 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
mem.sort(NlistIdx, nlists.items, self, NlistIdx.lessThan);
if (self.hasSubsections()) {
- try self.initSubsections(nlists.items, macho_file);
+ try self.initSubsections(gpa, nlists.items);
} else {
- try self.initSections(nlists.items, macho_file);
+ try self.initSections(gpa, nlists.items);
}
- try self.initCstringLiterals(macho_file);
- try self.initFixedSizeLiterals(macho_file);
- try self.initPointerLiterals(macho_file);
+ try self.initCstringLiterals(gpa, handle, macho_file);
+ try self.initFixedSizeLiterals(gpa, macho_file);
+ try self.initPointerLiterals(gpa, macho_file);
try self.linkNlistToAtom(macho_file);
try self.sortAtoms(macho_file);
- try self.initSymbols(macho_file);
- try self.initSymbolStabs(nlists.items, macho_file);
- try self.initRelocs(macho_file);
+ try self.initSymbols(gpa, macho_file);
+ try self.initSymbolStabs(gpa, nlists.items, macho_file);
+ try self.initRelocs(handle, cpu_arch, macho_file);
// Parse DWARF __TEXT,__eh_frame section
if (self.eh_frame_sect_index) |index| {
- try self.initEhFrameRecords(index, macho_file);
+ try self.initEhFrameRecords(gpa, index, handle, macho_file);
}
// Parse Apple's __LD,__compact_unwind section
if (self.compact_unwind_sect_index) |index| {
- try self.initUnwindRecords(index, macho_file);
+ try self.initUnwindRecords(gpa, index, handle, macho_file);
}
if (self.hasUnwindRecords() or self.hasEhFrameRecords()) {
- try self.parseUnwindRecords(macho_file);
+ try self.parseUnwindRecords(gpa, cpu_arch, macho_file);
}
if (self.platform) |platform| {
if (!macho_file.platform.eqlTarget(platform)) {
try macho_file.reportParseError2(self.index, "invalid platform: {}", .{
- platform.fmtTarget(macho_file.getTarget().cpu.arch),
+ platform.fmtTarget(cpu_arch),
});
return error.InvalidTarget;
}
@@ -244,8 +261,10 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
// }
}
- for (self.atoms.items) |atom_index| {
- const atom = macho_file.getAtom(atom_index).?;
+ try self.parseDebugInfo(macho_file);
+
+ for (self.getAtoms()) |atom_index| {
+ const atom = self.getAtom(atom_index) orelse continue;
const isec = atom.getInputSection(macho_file);
if (mem.eql(u8, isec.sectName(), "__eh_frame") or
mem.eql(u8, isec.sectName(), "__compact_unwind") or
@@ -274,10 +293,9 @@ pub fn isPtrLiteral(sect: macho.section_64) bool {
return sect.type() == macho.S_LITERAL_POINTERS;
}
-fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
+fn initSubsections(self: *Object, allocator: Allocator, nlists: anytype) !void {
const tracy = trace(@src());
defer tracy.end();
- const gpa = macho_file.base.comp.gpa;
const slice = self.sections.slice();
for (slice.items(.header), slice.items(.subsections), 0..) |sect, *subsections, n_sect| {
if (isCstringLiteral(sect)) continue;
@@ -292,17 +310,18 @@ fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
} else nlists.len;
if (nlist_start == nlist_end or nlists[nlist_start].nlist.n_value > sect.addr) {
- const name = try std.fmt.allocPrintZ(gpa, "{s}${s}", .{ sect.segName(), sect.sectName() });
- defer gpa.free(name);
+ const name = try std.fmt.allocPrintZ(allocator, "{s}${s}", .{ sect.segName(), sect.sectName() });
+ defer allocator.free(name);
const size = if (nlist_start == nlist_end) sect.size else nlists[nlist_start].nlist.n_value - sect.addr;
- const atom_index = try self.addAtom(.{
- .name = try self.addString(gpa, name),
+ const atom_index = try self.addAtom(allocator, .{
+ .name = try self.addString(allocator, name),
.n_sect = @intCast(n_sect),
.off = 0,
.size = size,
.alignment = sect.@"align",
- }, macho_file);
- try subsections.append(gpa, .{
+ });
+ try self.atoms_indexes.append(allocator, atom_index);
+ try subsections.append(allocator, .{
.atom = atom_index,
.off = 0,
});
@@ -325,14 +344,15 @@ fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
@min(@ctz(nlist.nlist.n_value), sect.@"align")
else
sect.@"align";
- const atom_index = try self.addAtom(.{
+ const atom_index = try self.addAtom(allocator, .{
.name = nlist.nlist.n_strx,
.n_sect = @intCast(n_sect),
.off = nlist.nlist.n_value - sect.addr,
.size = size,
.alignment = alignment,
- }, macho_file);
- try subsections.append(gpa, .{
+ });
+ try self.atoms_indexes.append(allocator, atom_index);
+ try subsections.append(allocator, .{
.atom = atom_index,
.off = nlist.nlist.n_value - sect.addr,
});
@@ -344,30 +364,31 @@ fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
}
}
-fn initSections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
+fn initSections(self: *Object, allocator: Allocator, nlists: anytype) !void {
const tracy = trace(@src());
defer tracy.end();
- const gpa = macho_file.base.comp.gpa;
const slice = self.sections.slice();
- try self.atoms.ensureUnusedCapacity(gpa, self.sections.items(.header).len);
+ try self.atoms.ensureUnusedCapacity(allocator, self.sections.items(.header).len);
+ try self.atoms_indexes.ensureUnusedCapacity(allocator, self.sections.items(.header).len);
for (slice.items(.header), 0..) |sect, n_sect| {
if (isCstringLiteral(sect)) continue;
if (isFixedSizeLiteral(sect)) continue;
if (isPtrLiteral(sect)) continue;
- const name = try std.fmt.allocPrintZ(gpa, "{s}${s}", .{ sect.segName(), sect.sectName() });
- defer gpa.free(name);
+ const name = try std.fmt.allocPrintZ(allocator, "{s}${s}", .{ sect.segName(), sect.sectName() });
+ defer allocator.free(name);
- const atom_index = try self.addAtom(.{
- .name = try self.addString(gpa, name),
+ const atom_index = try self.addAtom(allocator, .{
+ .name = try self.addString(allocator, name),
.n_sect = @intCast(n_sect),
.off = 0,
.size = sect.size,
.alignment = sect.@"align",
- }, macho_file);
- try slice.items(.subsections)[n_sect].append(gpa, .{ .atom = atom_index, .off = 0 });
+ });
+ try self.atoms_indexes.append(allocator, atom_index);
+ try slice.items(.subsections)[n_sect].append(allocator, .{ .atom = atom_index, .off = 0 });
const nlist_start = for (nlists, 0..) |nlist, i| {
if (nlist.nlist.n_sect - 1 == n_sect) break i;
@@ -396,21 +417,25 @@ fn initSections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
}
}
-fn initCstringLiterals(self: *Object, macho_file: *MachO) !void {
+fn initCstringLiterals(self: *Object, allocator: Allocator, file: File.Handle, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
- const gpa = macho_file.base.comp.gpa;
const slice = self.sections.slice();
for (slice.items(.header), 0..) |sect, n_sect| {
if (!isCstringLiteral(sect)) continue;
- const data = try self.getSectionData(@intCast(n_sect), macho_file);
- defer gpa.free(data);
+ const sect_size = math.cast(usize, sect.size) orelse return error.Overflow;
+ const data = try allocator.alloc(u8, sect_size);
+ defer allocator.free(data);
+ const amt = try file.preadAll(data, sect.offset + self.offset);
+ if (amt != data.len) return error.InputOutput;
+ var count: u32 = 0;
var start: u32 = 0;
while (start < data.len) {
+ defer count += 1;
var end = start;
while (end < data.len - 1 and data[end] != 0) : (end += 1) {}
if (data[end] != 0) {
@@ -423,32 +448,52 @@ fn initCstringLiterals(self: *Object, macho_file: *MachO) !void {
}
end += 1;
- const atom_index = try self.addAtom(.{
- .name = 0,
+ const name = try std.fmt.allocPrintZ(allocator, "l._str{d}", .{count});
+ defer allocator.free(name);
+ const name_str = try self.addString(allocator, name);
+
+ const atom_index = try self.addAtom(allocator, .{
+ .name = name_str,
.n_sect = @intCast(n_sect),
.off = start,
.size = end - start,
.alignment = sect.@"align",
- }, macho_file);
- try slice.items(.subsections)[n_sect].append(gpa, .{
+ });
+ try self.atoms_indexes.append(allocator, atom_index);
+ try slice.items(.subsections)[n_sect].append(allocator, .{
.atom = atom_index,
.off = start,
});
+ const atom = self.getAtom(atom_index).?;
+ const nlist_index: u32 = @intCast(try self.symtab.addOne(allocator));
+ self.symtab.set(nlist_index, .{
+ .nlist = .{
+ .n_strx = name_str,
+ .n_type = macho.N_SECT,
+ .n_sect = @intCast(atom.n_sect + 1),
+ .n_desc = 0,
+ .n_value = atom.getInputAddress(macho_file),
+ },
+ .size = atom.size,
+ .atom = atom_index,
+ });
+ atom.addExtra(.{ .literal_symbol_index = nlist_index }, macho_file);
+
start = end;
}
}
}
-fn initFixedSizeLiterals(self: *Object, macho_file: *MachO) !void {
+fn initFixedSizeLiterals(self: *Object, allocator: Allocator, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
- const gpa = macho_file.base.comp.gpa;
const slice = self.sections.slice();
for (slice.items(.header), 0..) |sect, n_sect| {
if (!isFixedSizeLiteral(sect)) continue;
+
const rec_size: u8 = switch (sect.type()) {
macho.S_4BYTE_LITERALS => 4,
macho.S_8BYTE_LITERALS => 8,
@@ -463,28 +508,52 @@ fn initFixedSizeLiterals(self: *Object, macho_file: *MachO) !void {
);
return error.MalformedObject;
}
+
var pos: u32 = 0;
- while (pos < sect.size) : (pos += rec_size) {
- const atom_index = try self.addAtom(.{
- .name = 0,
+ var count: u32 = 0;
+ while (pos < sect.size) : ({
+ pos += rec_size;
+ count += 1;
+ }) {
+ const name = try std.fmt.allocPrintZ(allocator, "l._literal{d}", .{count});
+ defer allocator.free(name);
+ const name_str = try self.addString(allocator, name);
+
+ const atom_index = try self.addAtom(allocator, .{
+ .name = name_str,
.n_sect = @intCast(n_sect),
.off = pos,
.size = rec_size,
.alignment = sect.@"align",
- }, macho_file);
- try slice.items(.subsections)[n_sect].append(gpa, .{
+ });
+ try self.atoms_indexes.append(allocator, atom_index);
+ try slice.items(.subsections)[n_sect].append(allocator, .{
.atom = atom_index,
.off = pos,
});
+
+ const atom = self.getAtom(atom_index).?;
+ const nlist_index: u32 = @intCast(try self.symtab.addOne(allocator));
+ self.symtab.set(nlist_index, .{
+ .nlist = .{
+ .n_strx = name_str,
+ .n_type = macho.N_SECT,
+ .n_sect = @intCast(atom.n_sect + 1),
+ .n_desc = 0,
+ .n_value = atom.getInputAddress(macho_file),
+ },
+ .size = atom.size,
+ .atom = atom_index,
+ });
+ atom.addExtra(.{ .literal_symbol_index = nlist_index }, macho_file);
}
}
}
-fn initPointerLiterals(self: *Object, macho_file: *MachO) !void {
+fn initPointerLiterals(self: *Object, allocator: Allocator, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
- const gpa = macho_file.base.comp.gpa;
const slice = self.sections.slice();
for (slice.items(.header), 0..) |sect, n_sect| {
@@ -503,134 +572,171 @@ fn initPointerLiterals(self: *Object, macho_file: *MachO) !void {
for (0..num_ptrs) |i| {
const pos: u32 = @as(u32, @intCast(i)) * rec_size;
- const atom_index = try self.addAtom(.{
- .name = 0,
+
+ const name = try std.fmt.allocPrintZ(allocator, "l._ptr{d}", .{i});
+ defer allocator.free(name);
+ const name_str = try self.addString(allocator, name);
+
+ const atom_index = try self.addAtom(allocator, .{
+ .name = name_str,
.n_sect = @intCast(n_sect),
.off = pos,
.size = rec_size,
.alignment = sect.@"align",
- }, macho_file);
- try slice.items(.subsections)[n_sect].append(gpa, .{
+ });
+ try self.atoms_indexes.append(allocator, atom_index);
+ try slice.items(.subsections)[n_sect].append(allocator, .{
.atom = atom_index,
.off = pos,
});
+
+ const atom = self.getAtom(atom_index).?;
+ const nlist_index: u32 = @intCast(try self.symtab.addOne(allocator));
+ self.symtab.set(nlist_index, .{
+ .nlist = .{
+ .n_strx = name_str,
+ .n_type = macho.N_SECT,
+ .n_sect = @intCast(atom.n_sect + 1),
+ .n_desc = 0,
+ .n_value = atom.getInputAddress(macho_file),
+ },
+ .size = atom.size,
+ .atom = atom_index,
+ });
+ atom.addExtra(.{ .literal_symbol_index = nlist_index }, macho_file);
}
}
}
-pub fn resolveLiterals(self: Object, lp: *MachO.LiteralPool, macho_file: *MachO) !void {
+pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
const gpa = macho_file.base.comp.gpa;
+ const file = macho_file.getFileHandle(self.file_handle);
var buffer = std.ArrayList(u8).init(gpa);
defer buffer.deinit();
+ var sections_data = std.AutoHashMap(u32, []const u8).init(gpa);
+ try sections_data.ensureTotalCapacity(@intCast(self.sections.items(.header).len));
+ defer {
+ var it = sections_data.iterator();
+ while (it.next()) |entry| {
+ gpa.free(entry.value_ptr.*);
+ }
+ sections_data.deinit();
+ }
+
const slice = self.sections.slice();
- for (slice.items(.header), slice.items(.subsections), 0..) |header, subs, n_sect| {
+ for (slice.items(.header), slice.items(.subsections)) |header, subs| {
if (isCstringLiteral(header) or isFixedSizeLiteral(header)) {
- const data = try self.getSectionData(@intCast(n_sect), macho_file);
+ const sect_size = math.cast(usize, header.size) orelse return error.Overflow;
+ const data = try gpa.alloc(u8, sect_size);
defer gpa.free(data);
+ const amt = try file.preadAll(data, header.offset + self.offset);
+ if (amt != data.len) return error.InputOutput;
for (subs.items) |sub| {
- const atom = macho_file.getAtom(sub.atom).?;
+ const atom = self.getAtom(sub.atom).?;
const atom_off = math.cast(usize, atom.off) orelse return error.Overflow;
const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
const atom_data = data[atom_off..][0..atom_size];
const res = try lp.insert(gpa, header.type(), atom_data);
if (!res.found_existing) {
- res.atom.* = sub.atom;
+ res.ref.* = .{ .index = atom.getExtra(macho_file).literal_symbol_index, .file = self.index };
+ } else {
+ const lp_sym = lp.getSymbol(res.index, macho_file);
+ const lp_atom = lp_sym.getAtom(macho_file).?;
+ lp_atom.alignment = lp_atom.alignment.max(atom.alignment);
+ atom.flags.alive = false;
}
- atom.flags.literal_pool = true;
- try atom.addExtra(.{ .literal_index = res.index }, macho_file);
+ atom.addExtra(.{ .literal_pool_index = res.index }, macho_file);
}
} else if (isPtrLiteral(header)) {
for (subs.items) |sub| {
- const atom = macho_file.getAtom(sub.atom).?;
+ const atom = self.getAtom(sub.atom).?;
const relocs = atom.getRelocs(macho_file);
assert(relocs.len == 1);
const rel = relocs[0];
const target = switch (rel.tag) {
- .local => rel.target,
- .@"extern" => rel.getTargetSymbol(macho_file).atom,
+ .local => rel.getTargetAtom(atom.*, macho_file),
+ .@"extern" => rel.getTargetSymbol(atom.*, macho_file).getAtom(macho_file).?,
};
const addend = math.cast(u32, rel.addend) orelse return error.Overflow;
- const target_atom = macho_file.getAtom(target).?;
- const target_atom_size = math.cast(usize, target_atom.size) orelse return error.Overflow;
- try buffer.ensureUnusedCapacity(target_atom_size);
- buffer.resize(target_atom_size) catch unreachable;
- try target_atom.getData(macho_file, buffer.items);
+ const target_size = math.cast(usize, target.size) orelse return error.Overflow;
+ try buffer.ensureUnusedCapacity(target_size);
+ buffer.resize(target_size) catch unreachable;
+ const gop = try sections_data.getOrPut(target.n_sect);
+ if (!gop.found_existing) {
+ const target_sect = slice.items(.header)[target.n_sect];
+ const target_sect_size = math.cast(usize, target_sect.size) orelse return error.Overflow;
+ const data = try gpa.alloc(u8, target_sect_size);
+ const amt = try file.preadAll(data, target_sect.offset + self.offset);
+ if (amt != data.len) return error.InputOutput;
+ gop.value_ptr.* = data;
+ }
+ const data = gop.value_ptr.*;
+ const target_off = math.cast(usize, target.off) orelse return error.Overflow;
+ @memcpy(buffer.items, data[target_off..][0..target_size]);
const res = try lp.insert(gpa, header.type(), buffer.items[addend..]);
buffer.clearRetainingCapacity();
if (!res.found_existing) {
- res.atom.* = sub.atom;
+ res.ref.* = .{ .index = atom.getExtra(macho_file).literal_symbol_index, .file = self.index };
+ } else {
+ const lp_sym = lp.getSymbol(res.index, macho_file);
+ const lp_atom = lp_sym.getAtom(macho_file).?;
+ lp_atom.alignment = lp_atom.alignment.max(atom.alignment);
+ atom.flags.alive = false;
}
- atom.flags.literal_pool = true;
- try atom.addExtra(.{ .literal_index = res.index }, macho_file);
+ atom.addExtra(.{ .literal_pool_index = res.index }, macho_file);
}
}
}
}
-pub fn dedupLiterals(self: Object, lp: MachO.LiteralPool, macho_file: *MachO) void {
- for (self.atoms.items) |atom_index| {
- const atom = macho_file.getAtom(atom_index) orelse continue;
+pub fn dedupLiterals(self: *Object, lp: MachO.LiteralPool, macho_file: *MachO) void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ for (self.getAtoms()) |atom_index| {
+ const atom = self.getAtom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
- if (!atom.flags.relocs) continue;
const relocs = blk: {
- const extra = atom.getExtra(macho_file).?;
+ const extra = atom.getExtra(macho_file);
const relocs = self.sections.items(.relocs)[atom.n_sect].items;
break :blk relocs[extra.rel_index..][0..extra.rel_count];
};
- for (relocs) |*rel| switch (rel.tag) {
- .local => {
- const target = macho_file.getAtom(rel.target).?;
- if (target.getLiteralPoolIndex(macho_file)) |lp_index| {
- const lp_atom = lp.getAtom(lp_index, macho_file);
- if (target.atom_index != lp_atom.atom_index) {
- lp_atom.alignment = lp_atom.alignment.max(target.alignment);
- target.flags.alive = false;
- rel.target = lp_atom.atom_index;
- }
- }
- },
- .@"extern" => {
- const target_sym = rel.getTargetSymbol(macho_file);
- if (target_sym.getAtom(macho_file)) |target_atom| {
- if (target_atom.getLiteralPoolIndex(macho_file)) |lp_index| {
- const lp_atom = lp.getAtom(lp_index, macho_file);
- if (target_atom.atom_index != lp_atom.atom_index) {
- lp_atom.alignment = lp_atom.alignment.max(target_atom.alignment);
- target_atom.flags.alive = false;
- target_sym.atom = lp_atom.atom_index;
- }
- }
- }
- },
- };
+ for (relocs) |*rel| {
+ if (rel.tag != .@"extern") continue;
+ const target_sym_ref = rel.getTargetSymbolRef(atom.*, macho_file);
+ const file = target_sym_ref.getFile(macho_file) orelse continue;
+ if (file.getIndex() != self.index) continue;
+ const target_sym = target_sym_ref.getSymbol(macho_file).?;
+ const target_atom = target_sym.getAtom(macho_file) orelse continue;
+ const isec = target_atom.getInputSection(macho_file);
+ if (!Object.isCstringLiteral(isec) and !Object.isFixedSizeLiteral(isec) and !Object.isPtrLiteral(isec)) continue;
+ const lp_index = target_atom.getExtra(macho_file).literal_pool_index;
+ const lp_sym = lp.getSymbol(lp_index, macho_file);
+ const lp_atom_ref = lp_sym.atom_ref;
+ if (target_atom.atom_index != lp_atom_ref.index or target_atom.file != lp_atom_ref.file) {
+ target_sym.atom_ref = lp_atom_ref;
+ }
+ }
}
-}
-const AddAtomArgs = struct {
- name: u32,
- n_sect: u8,
- off: u64,
- size: u64,
- alignment: u32,
-};
-
-fn addAtom(self: *Object, args: AddAtomArgs, macho_file: *MachO) !Atom.Index {
- const gpa = macho_file.base.comp.gpa;
- const atom_index = try macho_file.addAtom();
- const atom = macho_file.getAtom(atom_index).?;
- atom.file = self.index;
- atom.atom_index = atom_index;
- atom.name = args.name;
- atom.n_sect = args.n_sect;
- atom.size = args.size;
- atom.alignment = Atom.Alignment.fromLog2Units(args.alignment);
- atom.off = args.off;
- try self.atoms.append(gpa, atom_index);
- return atom_index;
+ for (self.symbols.items) |*sym| {
+ const atom = sym.getAtom(macho_file) orelse continue;
+ const isec = atom.getInputSection(macho_file);
+ if (!Object.isCstringLiteral(isec) and !Object.isFixedSizeLiteral(isec) and !Object.isPtrLiteral(isec)) continue;
+ const lp_index = atom.getExtra(macho_file).literal_pool_index;
+ const lp_sym = lp.getSymbol(lp_index, macho_file);
+ const lp_atom_ref = lp_sym.atom_ref;
+ if (atom.atom_index != lp_atom_ref.index or self.index != lp_atom_ref.file) {
+ sym.atom_ref = lp_atom_ref;
+ }
+ }
}
pub fn findAtom(self: Object, addr: u64) ?Atom.Index {
@@ -702,55 +808,61 @@ fn linkNlistToAtom(self: *Object, macho_file: *MachO) !void {
}
}
-fn initSymbols(self: *Object, macho_file: *MachO) !void {
+fn initSymbols(self: *Object, allocator: Allocator, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
- const gpa = macho_file.base.comp.gpa;
+
const slice = self.symtab.slice();
+ const nsyms = slice.items(.nlist).len;
- try self.symbols.ensureUnusedCapacity(gpa, slice.items(.nlist).len);
+ try self.symbols.ensureTotalCapacityPrecise(allocator, nsyms);
+ try self.symbols_extra.ensureTotalCapacityPrecise(allocator, nsyms * @sizeOf(Symbol.Extra));
+ try self.globals.ensureTotalCapacityPrecise(allocator, nsyms);
+ self.globals.resize(allocator, nsyms) catch unreachable;
+ @memset(self.globals.items, 0);
for (slice.items(.nlist), slice.items(.atom), 0..) |nlist, atom_index, i| {
- if (nlist.ext()) {
- const name = self.getString(nlist.n_strx);
- const off = try macho_file.strings.insert(gpa, name);
- const gop = try macho_file.getOrCreateGlobal(off);
- self.symbols.addOneAssumeCapacity().* = gop.index;
- if (nlist.undf() and nlist.weakRef()) {
- macho_file.getSymbol(gop.index).flags.weak_ref = true;
- }
- continue;
- }
-
- const index = try macho_file.addSymbol();
- self.symbols.appendAssumeCapacity(index);
- const symbol = macho_file.getSymbol(index);
- symbol.* = .{
- .value = nlist.n_value,
- .name = nlist.n_strx,
- .nlist_idx = @intCast(i),
- .atom = 0,
- .file = self.index,
- };
-
- if (macho_file.getAtom(atom_index)) |atom| {
+ const index = self.addSymbolAssumeCapacity();
+ const symbol = &self.symbols.items[index];
+ symbol.value = nlist.n_value;
+ symbol.name = nlist.n_strx;
+ symbol.nlist_idx = @intCast(i);
+ symbol.extra = self.addSymbolExtraAssumeCapacity(.{});
+
+ if (self.getAtom(atom_index)) |atom| {
assert(!nlist.abs());
symbol.value -= atom.getInputAddress(macho_file);
- symbol.atom = atom_index;
+ symbol.atom_ref = .{ .index = atom_index, .file = self.index };
}
+ symbol.flags.weak = nlist.weakDef();
symbol.flags.abs = nlist.abs();
+ symbol.flags.tentative = nlist.tentative();
symbol.flags.no_dead_strip = symbol.flags.no_dead_strip or nlist.noDeadStrip();
+ symbol.flags.dyn_ref = nlist.n_desc & macho.REFERENCED_DYNAMICALLY != 0;
+ symbol.flags.interposable = false;
+ // TODO
+ // symbol.flags.interposable = nlist.ext() and (nlist.sect() or nlist.abs()) and macho_file.base.isDynLib() and macho_file.options.namespace == .flat and !nlist.pext();
if (nlist.sect() and
self.sections.items(.header)[nlist.n_sect - 1].type() == macho.S_THREAD_LOCAL_VARIABLES)
{
symbol.flags.tlv = true;
}
+
+ if (nlist.ext()) {
+ if (nlist.undf()) {
+ symbol.flags.weak_ref = nlist.weakRef();
+ } else if (nlist.pext() or (nlist.weakDef() and nlist.weakRef()) or self.hidden) {
+ symbol.visibility = .hidden;
+ } else {
+ symbol.visibility = .global;
+ }
+ }
}
}
-fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void {
+fn initSymbolStabs(self: *Object, allocator: Allocator, nlists: anytype, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
@@ -761,7 +873,7 @@ fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void {
fn find(fs: @This(), addr: u64) ?Symbol.Index {
// TODO binary search since we have the list sorted
for (fs.entries) |nlist| {
- if (nlist.nlist.n_value == addr) return fs.ctx.symbols.items[nlist.idx];
+ if (nlist.nlist.n_value == addr) return @intCast(nlist.idx);
}
return null;
}
@@ -776,14 +888,13 @@ fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void {
if (start == end) return;
- const gpa = macho_file.base.comp.gpa;
const syms = self.symtab.items(.nlist);
const sym_lookup = SymbolLookup{ .ctx = self, .entries = nlists };
// We need to cache nlists by name so that we can properly resolve local N_GSYM stabs.
// What happens is `ld -r` will emit an N_GSYM stab for a symbol that may be either an
// external or private external.
- var addr_lookup = std.StringHashMap(u64).init(gpa);
+ var addr_lookup = std.StringHashMap(u64).init(allocator);
defer addr_lookup.deinit();
for (syms) |sym| {
if (sym.sect() and (sym.ext() or sym.pext())) {
@@ -813,17 +924,17 @@ fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void {
switch (nlist.n_type) {
macho.N_BNSYM => {
stab.is_func = true;
- stab.symbol = sym_lookup.find(nlist.n_value);
+ stab.index = sym_lookup.find(nlist.n_value);
// TODO validate
i += 3;
},
macho.N_GSYM => {
stab.is_func = false;
- stab.symbol = sym_lookup.find(addr_lookup.get(self.getString(nlist.n_strx)).?);
+ stab.index = sym_lookup.find(addr_lookup.get(self.getString(nlist.n_strx)).?);
},
macho.N_STSYM => {
stab.is_func = false;
- stab.symbol = sym_lookup.find(nlist.n_value);
+ stab.index = sym_lookup.find(nlist.n_value);
},
else => {
try macho_file.reportParseError2(self.index, "unhandled symbol stab type 0x{x}", .{
@@ -832,29 +943,35 @@ fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void {
return error.MalformedObject;
},
}
- try sf.stabs.append(gpa, stab);
+ try sf.stabs.append(allocator, stab);
}
- try self.stab_files.append(gpa, sf);
+ try self.stab_files.append(allocator, sf);
}
}
fn sortAtoms(self: *Object, macho_file: *MachO) !void {
- const lessThanAtom = struct {
- fn lessThanAtom(ctx: *MachO, lhs: Atom.Index, rhs: Atom.Index) bool {
- return ctx.getAtom(lhs).?.getInputAddress(ctx) < ctx.getAtom(rhs).?.getInputAddress(ctx);
+ const Ctx = struct {
+ object: *Object,
+ mfile: *MachO,
+
+ fn lessThanAtom(ctx: @This(), lhs: Atom.Index, rhs: Atom.Index) bool {
+ return ctx.object.getAtom(lhs).?.getInputAddress(ctx.mfile) <
+ ctx.object.getAtom(rhs).?.getInputAddress(ctx.mfile);
}
- }.lessThanAtom;
- mem.sort(Atom.Index, self.atoms.items, macho_file, lessThanAtom);
+ };
+ mem.sort(Atom.Index, self.atoms_indexes.items, Ctx{
+ .object = self,
+ .mfile = macho_file,
+ }, Ctx.lessThanAtom);
}
-fn initRelocs(self: *Object, macho_file: *MachO) !void {
+fn initRelocs(self: *Object, file: File.Handle, cpu_arch: std.Target.Cpu.Arch, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
- const cpu_arch = macho_file.getTarget().cpu.arch;
const slice = self.sections.slice();
- for (slice.items(.header), slice.items(.relocs), 0..) |sect, *out, n_sect| {
+ for (slice.items(.header), slice.items(.relocs)) |sect, *out| {
if (sect.nreloc == 0) continue;
// We skip relocs for __DWARF since even in -r mode, the linker is expected to emit
// debug symbol stabs in the relocatable. This made me curious why that is. For now,
@@ -863,8 +980,8 @@ fn initRelocs(self: *Object, macho_file: *MachO) !void {
!mem.eql(u8, sect.sectName(), "__compact_unwind")) continue;
switch (cpu_arch) {
- .x86_64 => try x86_64.parseRelocs(self, @intCast(n_sect), sect, out, macho_file),
- .aarch64 => try aarch64.parseRelocs(self, @intCast(n_sect), sect, out, macho_file),
+ .x86_64 => try x86_64.parseRelocs(self, sect, out, file, macho_file),
+ .aarch64 => try aarch64.parseRelocs(self, sect, out, file, macho_file),
else => unreachable,
}
@@ -876,7 +993,7 @@ fn initRelocs(self: *Object, macho_file: *MachO) !void {
var next_reloc: u32 = 0;
for (subsections.items) |subsection| {
- const atom = macho_file.getAtom(subsection.atom).?;
+ const atom = self.getAtom(subsection.atom).?;
if (!atom.flags.alive) continue;
if (next_reloc >= relocs.items.len) break;
const end_addr = atom.off + atom.size;
@@ -885,27 +1002,23 @@ fn initRelocs(self: *Object, macho_file: *MachO) !void {
while (next_reloc < relocs.items.len and relocs.items[next_reloc].offset < end_addr) : (next_reloc += 1) {}
const rel_count = next_reloc - rel_index;
- try atom.addExtra(.{ .rel_index = rel_index, .rel_count = rel_count }, macho_file);
- atom.flags.relocs = true;
+ atom.addExtra(.{ .rel_index = @intCast(rel_index), .rel_count = @intCast(rel_count) }, macho_file);
}
}
}
-fn initEhFrameRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
+fn initEhFrameRecords(self: *Object, allocator: Allocator, sect_id: u8, file: File.Handle, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
- const gpa = macho_file.base.comp.gpa;
const nlists = self.symtab.items(.nlist);
const slice = self.sections.slice();
const sect = slice.items(.header)[sect_id];
const relocs = slice.items(.relocs)[sect_id];
- // TODO: read into buffer directly
- const data = try self.getSectionData(sect_id, macho_file);
- defer gpa.free(data);
-
- try self.eh_frame_data.ensureTotalCapacityPrecise(gpa, data.len);
- self.eh_frame_data.appendSliceAssumeCapacity(data);
+ const size = math.cast(usize, sect.size) orelse return error.Overflow;
+ try self.eh_frame_data.resize(allocator, size);
+ const amt = try file.preadAll(self.eh_frame_data.items, sect.offset + self.offset);
+ if (amt != self.eh_frame_data.items.len) return error.InputOutput;
// Check for non-personality relocs in FDEs and apply them
for (relocs.items, 0..) |rel, i| {
@@ -937,12 +1050,12 @@ fn initEhFrameRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
var it = eh_frame.Iterator{ .data = self.eh_frame_data.items };
while (try it.next()) |rec| {
switch (rec.tag) {
- .cie => try self.cies.append(gpa, .{
+ .cie => try self.cies.append(allocator, .{
.offset = rec.offset,
.size = rec.size,
.file = self.index,
}),
- .fde => try self.fdes.append(gpa, .{
+ .fde => try self.fdes.append(allocator, .{
.offset = rec.offset,
.size = rec.size,
.cie = undefined,
@@ -987,7 +1100,7 @@ fn initEhFrameRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
}
}
-fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
+fn initUnwindRecords(self: *Object, allocator: Allocator, sect_id: u8, file: File.Handle, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
@@ -995,27 +1108,31 @@ fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
ctx: *const Object,
fn find(fs: @This(), addr: u64) ?Symbol.Index {
- for (fs.ctx.symbols.items, 0..) |sym_index, i| {
+ for (0..fs.ctx.symbols.items.len) |i| {
const nlist = fs.ctx.symtab.items(.nlist)[i];
- if (nlist.ext() and nlist.n_value == addr) return sym_index;
+ if (nlist.ext() and nlist.n_value == addr) return @intCast(i);
}
return null;
}
};
- const gpa = macho_file.base.comp.gpa;
- const data = try self.getSectionData(sect_id, macho_file);
- defer gpa.free(data);
+ const header = self.sections.items(.header)[sect_id];
+ const size = math.cast(usize, header.size) orelse return error.Overflow;
+ const data = try allocator.alloc(u8, size);
+ defer allocator.free(data);
+ const amt = try file.preadAll(data, header.offset + self.offset);
+ if (amt != data.len) return error.InputOutput;
+
const nrecs = @divExact(data.len, @sizeOf(macho.compact_unwind_entry));
const recs = @as([*]align(1) const macho.compact_unwind_entry, @ptrCast(data.ptr))[0..nrecs];
const sym_lookup = SymbolLookup{ .ctx = self };
- try self.unwind_records.resize(gpa, nrecs);
+ try self.unwind_records.ensureTotalCapacityPrecise(allocator, nrecs);
+ try self.unwind_records_indexes.ensureTotalCapacityPrecise(allocator, nrecs);
- const header = self.sections.items(.header)[sect_id];
const relocs = self.sections.items(.relocs)[sect_id].items;
var reloc_idx: usize = 0;
- for (recs, self.unwind_records.items, 0..) |rec, *out_index, rec_idx| {
+ for (recs, 0..) |rec, rec_idx| {
const rec_start = rec_idx * @sizeOf(macho.compact_unwind_entry);
const rec_end = rec_start + @sizeOf(macho.compact_unwind_entry);
const reloc_start = reloc_idx;
@@ -1023,11 +1140,11 @@ fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
relocs[reloc_idx].offset < rec_end) : (reloc_idx += 1)
{}
- out_index.* = try macho_file.addUnwindRecord();
- const out = macho_file.getUnwindRecord(out_index.*);
+ const out_index = self.addUnwindRecordAssumeCapacity();
+ self.unwind_records_indexes.appendAssumeCapacity(out_index);
+ const out = self.getUnwindRecord(out_index);
out.length = rec.rangeLength;
out.enc = .{ .enc = rec.compactUnwindEncoding };
- out.file = self.index;
for (relocs[reloc_start..reloc_idx]) |rel| {
if (rel.type != .unsigned or rel.meta.length != 3) {
@@ -1090,7 +1207,7 @@ fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
}
}
-fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {
+fn parseUnwindRecords(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch, macho_file: *MachO) !void {
// Synthesise missing unwind records.
// The logic here is as follows:
// 1. if an atom has unwind info record that is not DWARF, FDE is marked dead
@@ -1100,8 +1217,7 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {
const Superposition = struct { atom: Atom.Index, size: u64, cu: ?UnwindInfo.Record.Index = null, fde: ?Fde.Index = null };
- const gpa = macho_file.base.comp.gpa;
- var superposition = std.AutoArrayHashMap(u64, Superposition).init(gpa);
+ var superposition = std.AutoArrayHashMap(u64, Superposition).init(allocator);
defer superposition.deinit();
const slice = self.symtab.slice();
@@ -1119,8 +1235,8 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {
}
}
- for (self.unwind_records.items) |rec_index| {
- const rec = macho_file.getUnwindRecord(rec_index);
+ for (self.unwind_records_indexes.items) |rec_index| {
+ const rec = self.getUnwindRecord(rec_index);
const atom = rec.getAtom(macho_file);
const addr = atom.getInputAddress(macho_file) + rec.atom_offset;
superposition.getPtr(addr).?.cu = rec_index;
@@ -1137,7 +1253,7 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {
const fde = &self.fdes.items[fde_index];
if (meta.cu) |rec_index| {
- const rec = macho_file.getUnwindRecord(rec_index);
+ const rec = self.getUnwindRecord(rec_index);
if (!rec.enc.isDwarf(macho_file)) {
// Mark FDE dead
fde.alive = false;
@@ -1147,15 +1263,14 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {
}
} else {
// Synthesise new unwind info record
- const rec_index = try macho_file.addUnwindRecord();
- const rec = macho_file.getUnwindRecord(rec_index);
- try self.unwind_records.append(gpa, rec_index);
+ const rec_index = try self.addUnwindRecord(allocator);
+ const rec = self.getUnwindRecord(rec_index);
+ try self.unwind_records_indexes.append(allocator, rec_index);
rec.length = @intCast(meta.size);
rec.atom = fde.atom;
rec.atom_offset = fde.atom_offset;
rec.fde = fde_index;
- rec.file = fde.file;
- switch (macho_file.getTarget().cpu.arch) {
+ switch (cpu_arch) {
.x86_64 => rec.enc.setMode(macho.UNWIND_X86_64_MODE.DWARF),
.aarch64 => rec.enc.setMode(macho.UNWIND_ARM64_MODE.DWARF),
else => unreachable,
@@ -1163,10 +1278,10 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {
}
} else if (meta.cu == null and meta.fde == null) {
// Create a null record
- const rec_index = try macho_file.addUnwindRecord();
- const rec = macho_file.getUnwindRecord(rec_index);
- const atom = macho_file.getAtom(meta.atom).?;
- try self.unwind_records.append(gpa, rec_index);
+ const rec_index = try self.addUnwindRecord(allocator);
+ const rec = self.getUnwindRecord(rec_index);
+ const atom = self.getAtom(meta.atom).?;
+ try self.unwind_records_indexes.append(allocator, rec_index);
rec.length = @intCast(meta.size);
rec.atom = meta.atom;
rec.atom_offset = @intCast(addr - atom.getInputAddress(macho_file));
@@ -1174,30 +1289,35 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {
}
}
- const sortFn = struct {
- fn sortFn(ctx: *MachO, lhs_index: UnwindInfo.Record.Index, rhs_index: UnwindInfo.Record.Index) bool {
- const lhs = ctx.getUnwindRecord(lhs_index);
- const rhs = ctx.getUnwindRecord(rhs_index);
- const lhsa = lhs.getAtom(ctx);
- const rhsa = rhs.getAtom(ctx);
- return lhsa.getInputAddress(ctx) + lhs.atom_offset < rhsa.getInputAddress(ctx) + rhs.atom_offset;
+ const SortCtx = struct {
+ object: *Object,
+ mfile: *MachO,
+
+ fn sort(ctx: @This(), lhs_index: UnwindInfo.Record.Index, rhs_index: UnwindInfo.Record.Index) bool {
+ const lhs = ctx.object.getUnwindRecord(lhs_index);
+ const rhs = ctx.object.getUnwindRecord(rhs_index);
+ const lhsa = lhs.getAtom(ctx.mfile);
+ const rhsa = rhs.getAtom(ctx.mfile);
+ return lhsa.getInputAddress(ctx.mfile) + lhs.atom_offset < rhsa.getInputAddress(ctx.mfile) + rhs.atom_offset;
}
- }.sortFn;
- mem.sort(UnwindInfo.Record.Index, self.unwind_records.items, macho_file, sortFn);
+ };
+ mem.sort(UnwindInfo.Record.Index, self.unwind_records_indexes.items, SortCtx{
+ .object = self,
+ .mfile = macho_file,
+ }, SortCtx.sort);
// Associate unwind records to atoms
var next_cu: u32 = 0;
- while (next_cu < self.unwind_records.items.len) {
+ while (next_cu < self.unwind_records_indexes.items.len) {
const start = next_cu;
- const rec_index = self.unwind_records.items[start];
- const rec = macho_file.getUnwindRecord(rec_index);
- while (next_cu < self.unwind_records.items.len and
- macho_file.getUnwindRecord(self.unwind_records.items[next_cu]).atom == rec.atom) : (next_cu += 1)
+ const rec_index = self.unwind_records_indexes.items[start];
+ const rec = self.getUnwindRecord(rec_index);
+ while (next_cu < self.unwind_records_indexes.items.len and
+ self.getUnwindRecord(self.unwind_records_indexes.items[next_cu]).atom == rec.atom) : (next_cu += 1)
{}
const atom = rec.getAtom(macho_file);
- try atom.addExtra(.{ .unwind_index = start, .unwind_count = next_cu - start }, macho_file);
- atom.flags.unwind = true;
+ atom.addExtra(.{ .unwind_index = start, .unwind_count = next_cu - start }, macho_file);
}
}
@@ -1205,7 +1325,7 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {
/// and record that so that we can emit symbol stabs.
/// TODO in the future, we want parse debug info and debug line sections so that
/// we can provide nice error locations to the user.
-pub fn parseDebugInfo(self: *Object, macho_file: *MachO) !void {
+fn parseDebugInfo(self: *Object, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
@@ -1224,11 +1344,34 @@ pub fn parseDebugInfo(self: *Object, macho_file: *MachO) !void {
if (debug_info_index == null or debug_abbrev_index == null) return;
- const debug_info = try self.getSectionData(@intCast(debug_info_index.?), macho_file);
+ const slice = self.sections.slice();
+ const file = macho_file.getFileHandle(self.file_handle);
+ const debug_info = blk: {
+ const sect = slice.items(.header)[debug_info_index.?];
+ const size = math.cast(usize, sect.size) orelse return error.Overflow;
+ const data = try gpa.alloc(u8, size);
+ const amt = try file.preadAll(data, sect.offset + self.offset);
+ if (amt != data.len) return error.InputOutput;
+ break :blk data;
+ };
defer gpa.free(debug_info);
- const debug_abbrev = try self.getSectionData(@intCast(debug_abbrev_index.?), macho_file);
+ const debug_abbrev = blk: {
+ const sect = slice.items(.header)[debug_abbrev_index.?];
+ const size = math.cast(usize, sect.size) orelse return error.Overflow;
+ const data = try gpa.alloc(u8, size);
+ const amt = try file.preadAll(data, sect.offset + self.offset);
+ if (amt != data.len) return error.InputOutput;
+ break :blk data;
+ };
defer gpa.free(debug_abbrev);
- const debug_str = if (debug_str_index) |index| try self.getSectionData(@intCast(index), macho_file) else &[0]u8{};
+ const debug_str = if (debug_str_index) |sid| blk: {
+ const sect = slice.items(.header)[sid];
+ const size = math.cast(usize, sect.size) orelse return error.Overflow;
+ const data = try gpa.alloc(u8, size);
+ const amt = try file.preadAll(data, sect.offset + self.offset);
+ if (amt != data.len) return error.InputOutput;
+ break :blk data;
+ } else &[0]u8{};
defer gpa.free(debug_str);
self.compile_unit = self.findCompileUnit(.{
@@ -1334,87 +1477,55 @@ fn findCompileUnit(self: *Object, args: struct {
};
}
-pub fn resolveSymbols(self: *Object, macho_file: *MachO) void {
+pub fn resolveSymbols(self: *Object, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
- for (self.symbols.items, 0..) |index, i| {
- const nlist_idx = @as(Symbol.Index, @intCast(i));
- const nlist = self.symtab.items(.nlist)[nlist_idx];
- const atom_index = self.symtab.items(.atom)[nlist_idx];
+ const gpa = macho_file.base.comp.gpa;
+ for (self.symtab.items(.nlist), self.symtab.items(.atom), self.globals.items, 0..) |nlist, atom_index, *global, i| {
if (!nlist.ext()) continue;
- if (nlist.undf() and !nlist.tentative()) continue;
if (nlist.sect()) {
- const atom = macho_file.getAtom(atom_index).?;
+ const atom = self.getAtom(atom_index).?;
if (!atom.flags.alive) continue;
}
- const symbol = macho_file.getSymbol(index);
+ const gop = try macho_file.resolver.getOrPut(gpa, .{
+ .index = @intCast(i),
+ .file = self.index,
+ }, macho_file);
+ if (!gop.found_existing) {
+ gop.ref.* = .{ .index = 0, .file = 0 };
+ }
+ global.* = gop.index;
+
+ if (nlist.undf() and !nlist.tentative()) continue;
+ if (gop.ref.getFile(macho_file) == null) {
+ gop.ref.* = .{ .index = @intCast(i), .file = self.index };
+ continue;
+ }
+
if (self.asFile().getSymbolRank(.{
.archive = !self.alive,
.weak = nlist.weakDef(),
.tentative = nlist.tentative(),
- }) < symbol.getSymbolRank(macho_file)) {
- const value = if (nlist.sect()) blk: {
- const atom = macho_file.getAtom(atom_index).?;
- break :blk nlist.n_value - atom.getInputAddress(macho_file);
- } else nlist.n_value;
- symbol.value = value;
- symbol.atom = atom_index;
- symbol.nlist_idx = nlist_idx;
- symbol.file = self.index;
- symbol.flags.weak = nlist.weakDef();
- symbol.flags.abs = nlist.abs();
- symbol.flags.tentative = nlist.tentative();
- symbol.flags.weak_ref = false;
- symbol.flags.dyn_ref = nlist.n_desc & macho.REFERENCED_DYNAMICALLY != 0;
- symbol.flags.no_dead_strip = symbol.flags.no_dead_strip or nlist.noDeadStrip();
- // TODO: symbol.flags.interposable = macho_file.base.isDynLib() and macho_file.options.namespace == .flat and !nlist.pext();
- symbol.flags.interposable = false;
-
- if (nlist.sect() and
- self.sections.items(.header)[nlist.n_sect - 1].type() == macho.S_THREAD_LOCAL_VARIABLES)
- {
- symbol.flags.tlv = true;
- }
- }
-
- // Regardless of who the winner is, we still merge symbol visibility here.
- if (nlist.pext() or (nlist.weakDef() and nlist.weakRef()) or self.hidden) {
- if (symbol.visibility != .global) {
- symbol.visibility = .hidden;
- }
- } else {
- symbol.visibility = .global;
+ }) < gop.ref.getSymbol(macho_file).?.getSymbolRank(macho_file)) {
+ gop.ref.* = .{ .index = @intCast(i), .file = self.index };
}
}
}
-pub fn resetGlobals(self: *Object, macho_file: *MachO) void {
- for (self.symbols.items, 0..) |sym_index, nlist_idx| {
- if (!self.symtab.items(.nlist)[nlist_idx].ext()) continue;
- const sym = macho_file.getSymbol(sym_index);
- const name = sym.name;
- const global = sym.flags.global;
- const weak_ref = sym.flags.weak_ref;
- sym.* = .{};
- sym.name = name;
- sym.flags.global = global;
- sym.flags.weak_ref = weak_ref;
- }
-}
-
pub fn markLive(self: *Object, macho_file: *MachO) void {
const tracy = trace(@src());
defer tracy.end();
- for (self.symbols.items, 0..) |index, nlist_idx| {
- const nlist = self.symtab.items(.nlist)[nlist_idx];
+ for (0..self.symbols.items.len) |i| {
+ const nlist = self.symtab.items(.nlist)[i];
if (!nlist.ext()) continue;
- const sym = macho_file.getSymbol(index);
- const file = sym.getFile(macho_file) orelse continue;
+ const ref = self.getSymbolRef(@intCast(i), macho_file);
+ const file = ref.getFile(macho_file) orelse continue;
+ const sym = ref.getSymbol(macho_file).?;
const should_keep = nlist.undf() or (nlist.tentative() and !sym.flags.tentative);
if (should_keep and file == .object and !file.object.alive) {
file.object.alive = true;
@@ -1423,38 +1534,36 @@ pub fn markLive(self: *Object, macho_file: *MachO) void {
}
}
-pub fn checkDuplicates(self: *Object, dupes: anytype, macho_file: *MachO) error{OutOfMemory}!void {
- for (self.symbols.items, 0..) |index, nlist_idx| {
- const sym = macho_file.getSymbol(index);
- if (sym.visibility != .global) continue;
- const file = sym.getFile(macho_file) orelse continue;
- if (file.getIndex() == self.index) continue;
+pub fn mergeSymbolVisibility(self: *Object, macho_file: *MachO) void {
+ const tracy = trace(@src());
+ defer tracy.end();
- const nlist = self.symtab.items(.nlist)[nlist_idx];
- if (!nlist.undf() and !nlist.tentative() and !(nlist.weakDef() or nlist.pext())) {
- const gop = try dupes.getOrPut(index);
- if (!gop.found_existing) {
- gop.value_ptr.* = .{};
- }
- try gop.value_ptr.append(macho_file.base.comp.gpa, self.index);
+ for (self.symbols.items, 0..) |sym, i| {
+ const ref = self.getSymbolRef(@intCast(i), macho_file);
+ const global = ref.getSymbol(macho_file) orelse continue;
+ if (sym.visibility.rank() < global.visibility.rank()) {
+ global.visibility = sym.visibility;
+ }
+ if (sym.flags.weak_ref) {
+ global.flags.weak_ref = true;
}
}
}
-pub fn scanRelocs(self: Object, macho_file: *MachO) !void {
+pub fn scanRelocs(self: *Object, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
- for (self.atoms.items) |atom_index| {
- const atom = macho_file.getAtom(atom_index).?;
+ for (self.getAtoms()) |atom_index| {
+ const atom = self.getAtom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
const sect = atom.getInputSection(macho_file);
if (sect.isZerofill()) continue;
try atom.scanRelocs(macho_file);
}
- for (self.unwind_records.items) |rec_index| {
- const rec = macho_file.getUnwindRecord(rec_index);
+ for (self.unwind_records_indexes.items) |rec_index| {
+ const rec = self.getUnwindRecord(rec_index);
if (!rec.alive) continue;
if (rec.getFde(macho_file)) |fde| {
if (fde.getCie(macho_file).getPersonality(macho_file)) |sym| {
@@ -1471,38 +1580,35 @@ pub fn convertTentativeDefinitions(self: *Object, macho_file: *MachO) !void {
defer tracy.end();
const gpa = macho_file.base.comp.gpa;
- for (self.symbols.items, 0..) |index, i| {
- const sym = macho_file.getSymbol(index);
+ for (self.symbols.items, self.globals.items, 0..) |*sym, off, i| {
if (!sym.flags.tentative) continue;
- const sym_file = sym.getFile(macho_file).?;
- if (sym_file.getIndex() != self.index) continue;
+ if (macho_file.resolver.get(off).?.file != self.index) continue;
const nlist_idx = @as(Symbol.Index, @intCast(i));
const nlist = &self.symtab.items(.nlist)[nlist_idx];
const nlist_atom = &self.symtab.items(.atom)[nlist_idx];
- const atom_index = try macho_file.addAtom();
- try self.atoms.append(gpa, atom_index);
-
const name = try std.fmt.allocPrintZ(gpa, "__DATA$__common${s}", .{sym.getName(macho_file)});
defer gpa.free(name);
- const atom = macho_file.getAtom(atom_index).?;
- atom.atom_index = atom_index;
- atom.name = try self.addString(gpa, name);
- atom.file = self.index;
- atom.size = nlist.n_value;
- atom.alignment = Atom.Alignment.fromLog2Units((nlist.n_desc >> 8) & 0x0f);
+ const alignment = (nlist.n_desc >> 8) & 0x0f;
const n_sect = try self.addSection(gpa, "__DATA", "__common");
+ const atom_index = try self.addAtom(gpa, .{
+ .name = try self.addString(gpa, name),
+ .n_sect = n_sect,
+ .off = 0,
+ .size = nlist.n_value,
+ .alignment = alignment,
+ });
+ try self.atoms_indexes.append(gpa, atom_index);
+
const sect = &self.sections.items(.header)[n_sect];
sect.flags = macho.S_ZEROFILL;
- sect.size = atom.size;
- sect.@"align" = atom.alignment.toLog2Units();
- atom.n_sect = n_sect;
+ sect.size = nlist.n_value;
+ sect.@"align" = alignment;
sym.value = 0;
- sym.atom = atom_index;
- sym.flags.global = true;
+ sym.atom_ref = .{ .index = atom_index, .file = self.index };
sym.flags.weak = false;
sym.flags.weak_ref = false;
sym.flags.tentative = false;
@@ -1516,8 +1622,8 @@ pub fn convertTentativeDefinitions(self: *Object, macho_file: *MachO) !void {
}
}
-fn addSection(self: *Object, allocator: Allocator, segname: []const u8, sectname: []const u8) !u32 {
- const n_sect = @as(u32, @intCast(try self.sections.addOne(allocator)));
+fn addSection(self: *Object, allocator: Allocator, segname: []const u8, sectname: []const u8) !u8 {
+ const n_sect = @as(u8, @intCast(try self.sections.addOne(allocator)));
self.sections.set(n_sect, .{
.header = .{
.sectname = MachO.makeStaticString(sectname),
@@ -1532,12 +1638,11 @@ pub fn parseAr(self: *Object, macho_file: *MachO) !void {
defer tracy.end();
const gpa = macho_file.base.comp.gpa;
- const offset = if (self.archive) |ar| ar.offset else 0;
const handle = macho_file.getFileHandle(self.file_handle);
var header_buffer: [@sizeOf(macho.mach_header_64)]u8 = undefined;
{
- const amt = try handle.preadAll(&header_buffer, offset);
+ const amt = try handle.preadAll(&header_buffer, self.offset);
if (amt != @sizeOf(macho.mach_header_64)) return error.InputOutput;
}
self.header = @as(*align(1) const macho.mach_header_64, @ptrCast(&header_buffer)).*;
@@ -1558,7 +1663,7 @@ pub fn parseAr(self: *Object, macho_file: *MachO) !void {
const lc_buffer = try gpa.alloc(u8, self.header.?.sizeofcmds);
defer gpa.free(lc_buffer);
{
- const amt = try handle.preadAll(lc_buffer, offset + @sizeOf(macho.mach_header_64));
+ const amt = try handle.preadAll(lc_buffer, self.offset + @sizeOf(macho.mach_header_64));
if (amt != self.header.?.sizeofcmds) return error.InputOutput;
}
@@ -1571,14 +1676,14 @@ pub fn parseAr(self: *Object, macho_file: *MachO) !void {
const cmd = lc.cast(macho.symtab_command).?;
try self.strtab.resize(gpa, cmd.strsize);
{
- const amt = try handle.preadAll(self.strtab.items, cmd.stroff + offset);
+ const amt = try handle.preadAll(self.strtab.items, cmd.stroff + self.offset);
if (amt != self.strtab.items.len) return error.InputOutput;
}
const symtab_buffer = try gpa.alloc(u8, cmd.nsyms * @sizeOf(macho.nlist_64));
defer gpa.free(symtab_buffer);
{
- const amt = try handle.preadAll(symtab_buffer, cmd.symoff + offset);
+ const amt = try handle.preadAll(symtab_buffer, cmd.symoff + self.offset);
if (amt != symtab_buffer.len) return error.InputOutput;
}
const symtab = @as([*]align(1) const macho.nlist_64, @ptrCast(symtab_buffer.ptr))[0..cmd.nsyms];
@@ -1613,7 +1718,7 @@ pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, macho_file: *M
}
pub fn updateArSize(self: *Object, macho_file: *MachO) !void {
- self.output_ar_state.size = if (self.archive) |ar| ar.size else size: {
+ self.output_ar_state.size = if (self.in_archive) |ar| ar.size else size: {
const file = macho_file.getFileHandle(self.file_handle);
break :size (try file.stat()).size;
};
@@ -1622,7 +1727,6 @@ pub fn updateArSize(self: *Object, macho_file: *MachO) !void {
pub fn writeAr(self: Object, ar_format: Archive.Format, macho_file: *MachO, writer: anytype) !void {
// Header
const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow;
- const offset: u64 = if (self.archive) |ar| ar.offset else 0;
try Archive.writeHeader(self.path, size, ar_format, writer);
// Data
const file = macho_file.getFileHandle(self.file_handle);
@@ -1630,72 +1734,75 @@ pub fn writeAr(self: Object, ar_format: Archive.Format, macho_file: *MachO, writ
const gpa = macho_file.base.comp.gpa;
const data = try gpa.alloc(u8, size);
defer gpa.free(data);
- const amt = try file.preadAll(data, offset);
+ const amt = try file.preadAll(data, self.offset);
if (amt != size) return error.InputOutput;
try writer.writeAll(data);
}
-pub fn calcSymtabSize(self: *Object, macho_file: *MachO) !void {
+pub fn calcSymtabSize(self: *Object, macho_file: *MachO) void {
const tracy = trace(@src());
defer tracy.end();
- for (self.symbols.items) |sym_index| {
- const sym = macho_file.getSymbol(sym_index);
- const file = sym.getFile(macho_file) orelse continue;
+ const is_obj = macho_file.base.isObject();
+
+ for (self.symbols.items, 0..) |*sym, i| {
+ const ref = self.getSymbolRef(@intCast(i), macho_file);
+ const file = ref.getFile(macho_file) orelse continue;
if (file.getIndex() != self.index) continue;
if (sym.getAtom(macho_file)) |atom| if (!atom.flags.alive) continue;
if (sym.isSymbolStab(macho_file)) continue;
const name = sym.getName(macho_file);
+ if (name.len == 0) continue;
// TODO in -r mode, we actually want to merge symbol names and emit only one
// work it out when emitting relocs
- if (name.len > 0 and
- (name[0] == 'L' or name[0] == 'l' or
+ if ((name[0] == 'L' or name[0] == 'l' or
mem.startsWith(u8, name, "_OBJC_SELECTOR_REFERENCES_")) and
- !macho_file.base.isObject()) continue;
+ !is_obj)
+ continue;
sym.flags.output_symtab = true;
if (sym.isLocal()) {
- try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file);
+ sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file);
self.output_symtab_ctx.nlocals += 1;
} else if (sym.flags.@"export") {
- try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nexports }, macho_file);
+ sym.addExtra(.{ .symtab = self.output_symtab_ctx.nexports }, macho_file);
self.output_symtab_ctx.nexports += 1;
} else {
assert(sym.flags.import);
- try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file);
+ sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file);
self.output_symtab_ctx.nimports += 1;
}
self.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(macho_file).len + 1));
}
if (macho_file.base.comp.config.debug_format != .strip and self.hasDebugInfo())
- try self.calcStabsSize(macho_file);
+ self.calcStabsSize(macho_file);
}
-pub fn calcStabsSize(self: *Object, macho_file: *MachO) error{Overflow}!void {
+pub fn calcStabsSize(self: *Object, macho_file: *MachO) void {
if (self.compile_unit) |cu| {
- const comp_dir = cu.getCompDir(self);
- const tu_name = cu.getTuName(self);
+ const comp_dir = cu.getCompDir(self.*);
+ const tu_name = cu.getTuName(self.*);
self.output_symtab_ctx.nstabs += 4; // N_SO, N_SO, N_OSO, N_SO
self.output_symtab_ctx.strsize += @as(u32, @intCast(comp_dir.len + 1)); // comp_dir
self.output_symtab_ctx.strsize += @as(u32, @intCast(tu_name.len + 1)); // tu_name
- if (self.archive) |ar| {
+ if (self.in_archive) |ar| {
self.output_symtab_ctx.strsize += @as(u32, @intCast(ar.path.len + 1 + self.path.len + 1 + 1));
} else {
self.output_symtab_ctx.strsize += @as(u32, @intCast(self.path.len + 1));
}
- for (self.symbols.items) |sym_index| {
- const sym = macho_file.getSymbol(sym_index);
- const file = sym.getFile(macho_file) orelse continue;
+ for (self.symbols.items, 0..) |sym, i| {
+ const ref = self.getSymbolRef(@intCast(i), macho_file);
+ const file = ref.getFile(macho_file) orelse continue;
if (file.getIndex() != self.index) continue;
if (!sym.flags.output_symtab) continue;
if (macho_file.base.isObject()) {
const name = sym.getName(macho_file);
if (name.len > 0 and (name[0] == 'L' or name[0] == 'l')) continue;
}
- const sect = macho_file.sections.items(.header)[sym.out_n_sect];
+ const sect = macho_file.sections.items(.header)[sym.getOutputSectionIndex(macho_file)];
if (sect.isCode()) {
self.output_symtab_ctx.nstabs += 4; // N_BNSYM, N_FUN, N_FUN, N_ENSYM
} else if (sym.visibility == .global) {
@@ -1709,12 +1816,12 @@ pub fn calcStabsSize(self: *Object, macho_file: *MachO) error{Overflow}!void {
for (self.stab_files.items) |sf| {
self.output_symtab_ctx.nstabs += 4; // N_SO, N_SO, N_OSO, N_SO
- self.output_symtab_ctx.strsize += @as(u32, @intCast(sf.getCompDir(self).len + 1)); // comp_dir
- self.output_symtab_ctx.strsize += @as(u32, @intCast(sf.getTuName(self).len + 1)); // tu_name
- self.output_symtab_ctx.strsize += @as(u32, @intCast(sf.getOsoPath(self).len + 1)); // path
+ self.output_symtab_ctx.strsize += @as(u32, @intCast(sf.getCompDir(self.*).len + 1)); // comp_dir
+ self.output_symtab_ctx.strsize += @as(u32, @intCast(sf.getTuName(self.*).len + 1)); // tu_name
+ self.output_symtab_ctx.strsize += @as(u32, @intCast(sf.getOsoPath(self.*).len + 1)); // path
for (sf.stabs.items) |stab| {
- const sym = stab.getSymbol(macho_file) orelse continue;
+ const sym = stab.getSymbol(self.*) orelse continue;
const file = sym.getFile(macho_file).?;
if (file.getIndex() != self.index) continue;
if (!sym.flags.output_symtab) continue;
@@ -1725,28 +1832,212 @@ pub fn calcStabsSize(self: *Object, macho_file: *MachO) error{Overflow}!void {
}
}
-pub fn writeSymtab(self: Object, macho_file: *MachO, ctx: anytype) error{Overflow}!void {
+pub fn writeAtoms(self: *Object, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
- for (self.symbols.items) |sym_index| {
- const sym = macho_file.getSymbol(sym_index);
- const file = sym.getFile(macho_file) orelse continue;
+ const gpa = macho_file.base.comp.gpa;
+ const headers = self.sections.items(.header);
+ const sections_data = try gpa.alloc([]const u8, headers.len);
+ defer {
+ for (sections_data) |data| {
+ gpa.free(data);
+ }
+ gpa.free(sections_data);
+ }
+ @memset(sections_data, &[0]u8{});
+ const file = macho_file.getFileHandle(self.file_handle);
+
+ for (headers, 0..) |header, n_sect| {
+ if (header.isZerofill()) continue;
+ const size = math.cast(usize, header.size) orelse return error.Overflow;
+ const data = try gpa.alloc(u8, size);
+ const amt = try file.preadAll(data, header.offset + self.offset);
+ if (amt != data.len) return error.InputOutput;
+ sections_data[n_sect] = data;
+ }
+ for (self.getAtoms()) |atom_index| {
+ const atom = self.getAtom(atom_index) orelse continue;
+ if (!atom.flags.alive) continue;
+ const sect = atom.getInputSection(macho_file);
+ if (sect.isZerofill()) continue;
+ const value = math.cast(usize, atom.value) orelse return error.Overflow;
+ const off = math.cast(usize, atom.off) orelse return error.Overflow;
+ const size = math.cast(usize, atom.size) orelse return error.Overflow;
+ const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;
+ const data = sections_data[atom.n_sect];
+ @memcpy(buffer[value..][0..size], data[off..][0..size]);
+ try atom.resolveRelocs(macho_file, buffer[value..][0..size]);
+ }
+}
+
+pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const gpa = macho_file.base.comp.gpa;
+ const headers = self.sections.items(.header);
+ const sections_data = try gpa.alloc([]const u8, headers.len);
+ defer {
+ for (sections_data) |data| {
+ gpa.free(data);
+ }
+ gpa.free(sections_data);
+ }
+ @memset(sections_data, &[0]u8{});
+ const file = macho_file.getFileHandle(self.file_handle);
+
+ for (headers, 0..) |header, n_sect| {
+ if (header.isZerofill()) continue;
+ const size = math.cast(usize, header.size) orelse return error.Overflow;
+ const data = try gpa.alloc(u8, size);
+ const amt = try file.preadAll(data, header.offset + self.offset);
+ if (amt != data.len) return error.InputOutput;
+ sections_data[n_sect] = data;
+ }
+ for (self.getAtoms()) |atom_index| {
+ const atom = self.getAtom(atom_index) orelse continue;
+ if (!atom.flags.alive) continue;
+ const sect = atom.getInputSection(macho_file);
+ if (sect.isZerofill()) continue;
+ const value = math.cast(usize, atom.value) orelse return error.Overflow;
+ const off = math.cast(usize, atom.off) orelse return error.Overflow;
+ const size = math.cast(usize, atom.size) orelse return error.Overflow;
+ const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;
+ const data = sections_data[atom.n_sect];
+ @memcpy(buffer[value..][0..size], data[off..][0..size]);
+ const relocs = macho_file.sections.items(.relocs)[atom.out_n_sect].items;
+ const extra = atom.getExtra(macho_file);
+ try atom.writeRelocs(macho_file, buffer[value..][0..size], relocs[extra.rel_out_index..][0..extra.rel_out_count]);
+ }
+}
+
+pub fn calcCompactUnwindSizeRelocatable(self: *Object, macho_file: *MachO) void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const ctx = &self.compact_unwind_ctx;
+
+ for (self.unwind_records_indexes.items) |irec| {
+ const rec = self.getUnwindRecord(irec);
+ if (!rec.alive) continue;
+
+ ctx.rec_count += 1;
+ ctx.reloc_count += 1;
+ if (rec.getPersonality(macho_file)) |_| {
+ ctx.reloc_count += 1;
+ }
+ if (rec.getLsdaAtom(macho_file)) |_| {
+ ctx.reloc_count += 1;
+ }
+ }
+}
+
+pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const cpu_arch = macho_file.getTarget().cpu.arch;
+
+ const addReloc = struct {
+ fn addReloc(offset: u32, arch: std.Target.Cpu.Arch) !macho.relocation_info {
+ return .{
+ .r_address = math.cast(i32, offset) orelse return error.Overflow,
+ .r_symbolnum = 0,
+ .r_pcrel = 0,
+ .r_length = 3,
+ .r_extern = 0,
+ .r_type = switch (arch) {
+ .aarch64 => @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
+ .x86_64 => @intFromEnum(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
+ else => unreachable,
+ },
+ };
+ }
+ }.addReloc;
+
+ const nsect = macho_file.unwind_info_sect_index.?;
+ const buffer = macho_file.sections.items(.out)[nsect].items;
+ const relocs = macho_file.sections.items(.relocs)[nsect].items;
+
+ var rec_index: u32 = self.compact_unwind_ctx.rec_index;
+ var reloc_index: u32 = self.compact_unwind_ctx.reloc_index;
+
+ for (self.unwind_records_indexes.items) |irec| {
+ const rec = self.getUnwindRecord(irec);
+ if (!rec.alive) continue;
+
+ var out: macho.compact_unwind_entry = .{
+ .rangeStart = 0,
+ .rangeLength = rec.length,
+ .compactUnwindEncoding = rec.enc.enc,
+ .personalityFunction = 0,
+ .lsda = 0,
+ };
+ defer rec_index += 1;
+
+ const offset = rec_index * @sizeOf(macho.compact_unwind_entry);
+
+ {
+ // Function address
+ const atom = rec.getAtom(macho_file);
+ const addr = rec.getAtomAddress(macho_file);
+ out.rangeStart = addr;
+ var reloc = try addReloc(offset, cpu_arch);
+ reloc.r_symbolnum = atom.out_n_sect + 1;
+ relocs[reloc_index] = reloc;
+ reloc_index += 1;
+ }
+
+ // Personality function
+ if (rec.getPersonality(macho_file)) |sym| {
+ const r_symbolnum = math.cast(u24, sym.getOutputSymtabIndex(macho_file).?) orelse return error.Overflow;
+ var reloc = try addReloc(offset + 16, cpu_arch);
+ reloc.r_symbolnum = r_symbolnum;
+ reloc.r_extern = 1;
+ relocs[reloc_index] = reloc;
+ reloc_index += 1;
+ }
+
+ // LSDA address
+ if (rec.getLsdaAtom(macho_file)) |atom| {
+ const addr = rec.getLsdaAddress(macho_file);
+ out.lsda = addr;
+ var reloc = try addReloc(offset + 24, cpu_arch);
+ reloc.r_symbolnum = atom.out_n_sect + 1;
+ relocs[reloc_index] = reloc;
+ reloc_index += 1;
+ }
+
+ @memcpy(buffer[offset..][0..@sizeOf(macho.compact_unwind_entry)], mem.asBytes(&out));
+ }
+}
+
+pub fn writeSymtab(self: Object, macho_file: *MachO, ctx: anytype) void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ var n_strx = self.output_symtab_ctx.stroff;
+ for (self.symbols.items, 0..) |sym, i| {
+ const ref = self.getSymbolRef(@intCast(i), macho_file);
+ const file = ref.getFile(macho_file) orelse continue;
if (file.getIndex() != self.index) continue;
const idx = sym.getOutputSymtabIndex(macho_file) orelse continue;
- const n_strx = @as(u32, @intCast(ctx.strtab.items.len));
- ctx.strtab.appendSliceAssumeCapacity(sym.getName(macho_file));
- ctx.strtab.appendAssumeCapacity(0);
const out_sym = &ctx.symtab.items[idx];
out_sym.n_strx = n_strx;
sym.setOutputSym(macho_file, out_sym);
+ const name = sym.getName(macho_file);
+ @memcpy(ctx.strtab.items[n_strx..][0..name.len], name);
+ n_strx += @intCast(name.len);
+ ctx.strtab.items[n_strx] = 0;
+ n_strx += 1;
}
if (macho_file.base.comp.config.debug_format != .strip and self.hasDebugInfo())
- try self.writeStabs(macho_file, ctx);
+ self.writeStabs(n_strx, macho_file, ctx);
}
-pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{Overflow}!void {
+pub fn writeStabs(self: Object, stroff: u32, macho_file: *MachO, ctx: anytype) void {
const writeFuncStab = struct {
inline fn writeFuncStab(
n_strx: u32,
@@ -1788,6 +2079,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
}.writeFuncStab;
var index = self.output_symtab_ctx.istab;
+ var n_strx = stroff;
if (self.compile_unit) |cu| {
const comp_dir = cu.getCompDir(self);
@@ -1795,9 +2087,6 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
// Open scope
// N_SO comp_dir
- var n_strx = @as(u32, @intCast(ctx.strtab.items.len));
- ctx.strtab.appendSliceAssumeCapacity(comp_dir);
- ctx.strtab.appendAssumeCapacity(0);
ctx.symtab.items[index] = .{
.n_strx = n_strx,
.n_type = macho.N_SO,
@@ -1806,11 +2095,12 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
.n_value = 0,
};
index += 1;
+ @memcpy(ctx.strtab.items[n_strx..][0..comp_dir.len], comp_dir);
+ n_strx += @intCast(comp_dir.len);
+ ctx.strtab.items[n_strx] = 0;
+ n_strx += 1;
// N_SO tu_name
- n_strx = @as(u32, @intCast(ctx.strtab.items.len));
- ctx.strtab.appendSliceAssumeCapacity(tu_name);
- ctx.strtab.appendAssumeCapacity(0);
- ctx.symtab.items[index] = .{
+ macho_file.symtab.items[index] = .{
.n_strx = n_strx,
.n_type = macho.N_SO,
.n_sect = 0,
@@ -1818,18 +2108,11 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
.n_value = 0,
};
index += 1;
+ @memcpy(ctx.strtab.items[n_strx..][0..tu_name.len], tu_name);
+ n_strx += @intCast(tu_name.len);
+ ctx.strtab.items[n_strx] = 0;
+ n_strx += 1;
// N_OSO path
- n_strx = @as(u32, @intCast(ctx.strtab.items.len));
- if (self.archive) |ar| {
- ctx.strtab.appendSliceAssumeCapacity(ar.path);
- ctx.strtab.appendAssumeCapacity('(');
- ctx.strtab.appendSliceAssumeCapacity(self.path);
- ctx.strtab.appendAssumeCapacity(')');
- ctx.strtab.appendAssumeCapacity(0);
- } else {
- ctx.strtab.appendSliceAssumeCapacity(self.path);
- ctx.strtab.appendAssumeCapacity(0);
- }
ctx.symtab.items[index] = .{
.n_strx = n_strx,
.n_type = macho.N_OSO,
@@ -1838,23 +2121,40 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
.n_value = self.mtime,
};
index += 1;
+ if (self.in_archive) |ar| {
+ @memcpy(ctx.strtab.items[n_strx..][0..ar.path.len], ar.path);
+ n_strx += @intCast(ar.path.len);
+ ctx.strtab.items[n_strx] = '(';
+ n_strx += 1;
+ @memcpy(ctx.strtab.items[n_strx..][0..self.path.len], self.path);
+ n_strx += @intCast(self.path.len);
+ ctx.strtab.items[n_strx] = ')';
+ n_strx += 1;
+ ctx.strtab.items[n_strx] = 0;
+ n_strx += 1;
+ } else {
+ @memcpy(ctx.strtab.items[n_strx..][0..self.path.len], self.path);
+ n_strx += @intCast(self.path.len);
+ ctx.strtab.items[n_strx] = 0;
+ n_strx += 1;
+ }
- for (self.symbols.items) |sym_index| {
- const sym = macho_file.getSymbol(sym_index);
- const file = sym.getFile(macho_file) orelse continue;
+ for (self.symbols.items, 0..) |sym, i| {
+ const ref = self.getSymbolRef(@intCast(i), macho_file);
+ const file = ref.getFile(macho_file) orelse continue;
if (file.getIndex() != self.index) continue;
if (!sym.flags.output_symtab) continue;
if (macho_file.base.isObject()) {
const name = sym.getName(macho_file);
if (name.len > 0 and (name[0] == 'L' or name[0] == 'l')) continue;
}
- const sect = macho_file.sections.items(.header)[sym.out_n_sect];
+ const sect = macho_file.sections.items(.header)[sym.getOutputSectionIndex(macho_file)];
const sym_n_strx = n_strx: {
const symtab_index = sym.getOutputSymtabIndex(macho_file).?;
const osym = ctx.symtab.items[symtab_index];
break :n_strx osym.n_strx;
};
- const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.out_n_sect + 1) else 0;
+ const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.getOutputSectionIndex(macho_file) + 1) else 0;
const sym_n_value = sym.getAddress(.{}, macho_file);
const sym_size = sym.getSize(macho_file);
if (sect.isCode()) {
@@ -1894,11 +2194,12 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
assert(self.hasSymbolStabs());
for (self.stab_files.items) |sf| {
+ const comp_dir = sf.getCompDir(self);
+ const tu_name = sf.getTuName(self);
+ const oso_path = sf.getOsoPath(self);
+
// Open scope
// N_SO comp_dir
- var n_strx = @as(u32, @intCast(ctx.strtab.items.len));
- ctx.strtab.appendSliceAssumeCapacity(sf.getCompDir(self));
- ctx.strtab.appendAssumeCapacity(0);
ctx.symtab.items[index] = .{
.n_strx = n_strx,
.n_type = macho.N_SO,
@@ -1907,10 +2208,11 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
.n_value = 0,
};
index += 1;
+ @memcpy(ctx.strtab.items[n_strx..][0..comp_dir.len], comp_dir);
+ n_strx += @intCast(comp_dir.len);
+ ctx.strtab.items[n_strx] = 0;
+ n_strx += 1;
// N_SO tu_name
- n_strx = @as(u32, @intCast(ctx.strtab.items.len));
- ctx.strtab.appendSliceAssumeCapacity(sf.getTuName(self));
- ctx.strtab.appendAssumeCapacity(0);
ctx.symtab.items[index] = .{
.n_strx = n_strx,
.n_type = macho.N_SO,
@@ -1919,10 +2221,11 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
.n_value = 0,
};
index += 1;
+ @memcpy(ctx.strtab.items[n_strx..][0..tu_name.len], tu_name);
+ n_strx += @intCast(tu_name.len);
+ ctx.strtab.items[n_strx] = 0;
+ n_strx += 1;
// N_OSO path
- n_strx = @as(u32, @intCast(ctx.strtab.items.len));
- ctx.strtab.appendSliceAssumeCapacity(sf.getOsoPath(self));
- ctx.strtab.appendAssumeCapacity(0);
ctx.symtab.items[index] = .{
.n_strx = n_strx,
.n_type = macho.N_OSO,
@@ -1931,9 +2234,13 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
.n_value = sf.getOsoModTime(self),
};
index += 1;
+ @memcpy(ctx.strtab.items[n_strx..][0..oso_path.len], oso_path);
+ n_strx += @intCast(oso_path.len);
+ ctx.strtab.items[n_strx] = 0;
+ n_strx += 1;
for (sf.stabs.items) |stab| {
- const sym = stab.getSymbol(macho_file) orelse continue;
+ const sym = stab.getSymbol(self) orelse continue;
const file = sym.getFile(macho_file).?;
if (file.getIndex() != self.index) continue;
if (!sym.flags.output_symtab) continue;
@@ -1942,7 +2249,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
const osym = ctx.symtab.items[symtab_index];
break :n_strx osym.n_strx;
};
- const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.out_n_sect + 1) else 0;
+ const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.getOutputSectionIndex(macho_file) + 1) else 0;
const sym_n_value = sym.getAddress(.{}, macho_file);
const sym_size = sym.getSize(macho_file);
if (stab.is_func) {
@@ -1983,34 +2290,8 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
}
}
-fn getSectionData(self: *const Object, index: u32, macho_file: *MachO) ![]u8 {
- const gpa = macho_file.base.comp.gpa;
- const slice = self.sections.slice();
- assert(index < slice.items(.header).len);
- const sect = slice.items(.header)[index];
- const handle = macho_file.getFileHandle(self.file_handle);
- const offset = if (self.archive) |ar| ar.offset else 0;
- const size = math.cast(usize, sect.size) orelse return error.Overflow;
- const buffer = try gpa.alloc(u8, size);
- errdefer gpa.free(buffer);
- const amt = try handle.preadAll(buffer, sect.offset + offset);
- if (amt != buffer.len) return error.InputOutput;
- return buffer;
-}
-
-pub fn getAtomData(self: *const Object, macho_file: *MachO, atom: Atom, buffer: []u8) !void {
- assert(buffer.len == atom.size);
- const slice = self.sections.slice();
- const handle = macho_file.getFileHandle(self.file_handle);
- const offset = if (self.archive) |ar| ar.offset else 0;
- const sect = slice.items(.header)[atom.n_sect];
- const amt = try handle.preadAll(buffer, sect.offset + offset + atom.off);
- if (amt != buffer.len) return error.InputOutput;
-}
-
pub fn getAtomRelocs(self: *const Object, atom: Atom, macho_file: *MachO) []const Relocation {
- if (!atom.flags.relocs) return &[0]Relocation{};
- const extra = atom.getExtra(macho_file).?;
+ const extra = atom.getExtra(macho_file);
const relocs = self.sections.items(.relocs)[atom.n_sect];
return relocs.items[extra.rel_index..][0..extra.rel_count];
}
@@ -2068,6 +2349,160 @@ pub fn asFile(self: *Object) File {
return .{ .object = self };
}
+const AddAtomArgs = struct {
+ name: u32,
+ n_sect: u8,
+ off: u64,
+ size: u64,
+ alignment: u32,
+};
+
+fn addAtom(self: *Object, allocator: Allocator, args: AddAtomArgs) !Atom.Index {
+ const atom_index: Atom.Index = @intCast(self.atoms.items.len);
+ const atom = try self.atoms.addOne(allocator);
+ atom.* = .{
+ .file = self.index,
+ .atom_index = atom_index,
+ .name = args.name,
+ .n_sect = args.n_sect,
+ .size = args.size,
+ .off = args.off,
+ .extra = try self.addAtomExtra(allocator, .{}),
+ .alignment = Atom.Alignment.fromLog2Units(args.alignment),
+ };
+ return atom_index;
+}
+
+pub fn getAtom(self: *Object, atom_index: Atom.Index) ?*Atom {
+ if (atom_index == 0) return null;
+ assert(atom_index < self.atoms.items.len);
+ return &self.atoms.items[atom_index];
+}
+
+pub fn getAtoms(self: *Object) []const Atom.Index {
+ return self.atoms_indexes.items;
+}
+
+fn addAtomExtra(self: *Object, allocator: Allocator, extra: Atom.Extra) !u32 {
+ const fields = @typeInfo(Atom.Extra).Struct.fields;
+ try self.atoms_extra.ensureUnusedCapacity(allocator, fields.len);
+ return self.addAtomExtraAssumeCapacity(extra);
+}
+
+fn addAtomExtraAssumeCapacity(self: *Object, extra: Atom.Extra) u32 {
+ const index = @as(u32, @intCast(self.atoms_extra.items.len));
+ const fields = @typeInfo(Atom.Extra).Struct.fields;
+ inline for (fields) |field| {
+ self.atoms_extra.appendAssumeCapacity(switch (field.type) {
+ u32 => @field(extra, field.name),
+ else => @compileError("bad field type"),
+ });
+ }
+ return index;
+}
+
+pub fn getAtomExtra(self: Object, index: u32) Atom.Extra {
+ const fields = @typeInfo(Atom.Extra).Struct.fields;
+ var i: usize = index;
+ var result: Atom.Extra = undefined;
+ inline for (fields) |field| {
+ @field(result, field.name) = switch (field.type) {
+ u32 => self.atoms_extra.items[i],
+ else => @compileError("bad field type"),
+ };
+ i += 1;
+ }
+ return result;
+}
+
+pub fn setAtomExtra(self: *Object, index: u32, extra: Atom.Extra) void {
+ assert(index > 0);
+ const fields = @typeInfo(Atom.Extra).Struct.fields;
+ inline for (fields, 0..) |field, i| {
+ self.atoms_extra.items[index + i] = switch (field.type) {
+ u32 => @field(extra, field.name),
+ else => @compileError("bad field type"),
+ };
+ }
+}
+
+fn addSymbol(self: *Object, allocator: Allocator) !Symbol.Index {
+ try self.symbols.ensureUnusedCapacity(allocator, 1);
+ return self.addSymbolAssumeCapacity();
+}
+
+fn addSymbolAssumeCapacity(self: *Object) Symbol.Index {
+ const index: Symbol.Index = @intCast(self.symbols.items.len);
+ const symbol = self.symbols.addOneAssumeCapacity();
+ symbol.* = .{ .file = self.index };
+ return index;
+}
+
+pub fn getSymbolRef(self: Object, index: Symbol.Index, macho_file: *MachO) MachO.Ref {
+ const global_index = self.globals.items[index];
+ if (macho_file.resolver.get(global_index)) |ref| return ref;
+ return .{ .index = index, .file = self.index };
+}
+
+pub fn addSymbolExtra(self: *Object, allocator: Allocator, extra: Symbol.Extra) !u32 {
+ const fields = @typeInfo(Symbol.Extra).Struct.fields;
+ try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len);
+ return self.addSymbolExtraAssumeCapacity(extra);
+}
+
+fn addSymbolExtraAssumeCapacity(self: *Object, extra: Symbol.Extra) u32 {
+ const index = @as(u32, @intCast(self.symbols_extra.items.len));
+ const fields = @typeInfo(Symbol.Extra).Struct.fields;
+ inline for (fields) |field| {
+ self.symbols_extra.appendAssumeCapacity(switch (field.type) {
+ u32 => @field(extra, field.name),
+ else => @compileError("bad field type"),
+ });
+ }
+ return index;
+}
+
+pub fn getSymbolExtra(self: Object, index: u32) Symbol.Extra {
+ const fields = @typeInfo(Symbol.Extra).Struct.fields;
+ var i: usize = index;
+ var result: Symbol.Extra = undefined;
+ inline for (fields) |field| {
+ @field(result, field.name) = switch (field.type) {
+ u32 => self.symbols_extra.items[i],
+ else => @compileError("bad field type"),
+ };
+ i += 1;
+ }
+ return result;
+}
+
+pub fn setSymbolExtra(self: *Object, index: u32, extra: Symbol.Extra) void {
+ const fields = @typeInfo(Symbol.Extra).Struct.fields;
+ inline for (fields, 0..) |field, i| {
+ self.symbols_extra.items[index + i] = switch (field.type) {
+ u32 => @field(extra, field.name),
+ else => @compileError("bad field type"),
+ };
+ }
+}
+
+fn addUnwindRecord(self: *Object, allocator: Allocator) !UnwindInfo.Record.Index {
+ try self.unwind_records.ensureUnusedCapacity(allocator, 1);
+ return self.addUnwindRecordAssumeCapacity();
+}
+
+fn addUnwindRecordAssumeCapacity(self: *Object) UnwindInfo.Record.Index {
+ const index = @as(UnwindInfo.Record.Index, @intCast(self.unwind_records.items.len));
+ const rec = self.unwind_records.addOneAssumeCapacity();
+ rec.* = .{ .file = self.index };
+ return index;
+}
+
+pub fn getUnwindRecord(self: *Object, index: UnwindInfo.Record.Index) *UnwindInfo.Record {
+ assert(index < self.unwind_records.items.len);
+ return &self.unwind_records.items[index];
+}
+
pub fn format(
self: *Object,
comptime unused_fmt_string: []const u8,
@@ -2102,10 +2537,11 @@ fn formatAtoms(
_ = unused_fmt_string;
_ = options;
const object = ctx.object;
+ const macho_file = ctx.macho_file;
try writer.writeAll(" atoms\n");
- for (object.atoms.items) |atom_index| {
- const atom = ctx.macho_file.getAtom(atom_index).?;
- try writer.print(" {}\n", .{atom.fmt(ctx.macho_file)});
+ for (object.getAtoms()) |atom_index| {
+ const atom = object.getAtom(atom_index) orelse continue;
+ try writer.print(" {}\n", .{atom.fmt(macho_file)});
}
}
@@ -2171,8 +2607,8 @@ fn formatUnwindRecords(
const object = ctx.object;
const macho_file = ctx.macho_file;
try writer.writeAll(" unwind records\n");
- for (object.unwind_records.items) |rec| {
- try writer.print(" rec({d}) : {}\n", .{ rec, macho_file.getUnwindRecord(rec).fmt(macho_file) });
+ for (object.unwind_records_indexes.items) |rec| {
+ try writer.print(" rec({d}) : {}\n", .{ rec, object.getUnwindRecord(rec).fmt(macho_file) });
}
}
@@ -2192,10 +2628,26 @@ fn formatSymtab(
_ = unused_fmt_string;
_ = options;
const object = ctx.object;
+ const macho_file = ctx.macho_file;
try writer.writeAll(" symbols\n");
- for (object.symbols.items) |index| {
- const sym = ctx.macho_file.getSymbol(index);
- try writer.print(" {}\n", .{sym.fmt(ctx.macho_file)});
+ for (object.symbols.items, 0..) |sym, i| {
+ const ref = object.getSymbolRef(@intCast(i), macho_file);
+ if (ref.getFile(macho_file) == null) {
+ // TODO any better way of handling this?
+ try writer.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});
+ } else {
+ try writer.print(" {}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
+ }
+ }
+ for (object.stab_files.items) |sf| {
+ try writer.print(" stabs({s},{s},{s})\n", .{
+ sf.getCompDir(object.*),
+ sf.getTuName(object.*),
+ sf.getOsoPath(object.*),
+ });
+ for (sf.stabs.items) |stab| {
+ try writer.print(" {}", .{stab.fmt(object.*)});
+ }
}
}
@@ -2211,7 +2663,7 @@ fn formatPath(
) !void {
_ = unused_fmt_string;
_ = options;
- if (object.archive) |ar| {
+ if (object.in_archive) |ar| {
try writer.writeAll(ar.path);
try writer.writeByte('(');
try writer.writeAll(object.path);
@@ -2240,32 +2692,71 @@ const StabFile = struct {
comp_dir: u32,
stabs: std.ArrayListUnmanaged(Stab) = .{},
- fn getCompDir(sf: StabFile, object: *const Object) [:0]const u8 {
+ fn getCompDir(sf: StabFile, object: Object) [:0]const u8 {
const nlist = object.symtab.items(.nlist)[sf.comp_dir];
return object.getString(nlist.n_strx);
}
- fn getTuName(sf: StabFile, object: *const Object) [:0]const u8 {
+ fn getTuName(sf: StabFile, object: Object) [:0]const u8 {
const nlist = object.symtab.items(.nlist)[sf.comp_dir + 1];
return object.getString(nlist.n_strx);
}
- fn getOsoPath(sf: StabFile, object: *const Object) [:0]const u8 {
+ fn getOsoPath(sf: StabFile, object: Object) [:0]const u8 {
const nlist = object.symtab.items(.nlist)[sf.comp_dir + 2];
return object.getString(nlist.n_strx);
}
- fn getOsoModTime(sf: StabFile, object: *const Object) u64 {
+ fn getOsoModTime(sf: StabFile, object: Object) u64 {
const nlist = object.symtab.items(.nlist)[sf.comp_dir + 2];
return nlist.n_value;
}
const Stab = struct {
is_func: bool = true,
- symbol: ?Symbol.Index = null,
+ index: ?Symbol.Index = null,
+
+ fn getSymbol(stab: Stab, object: Object) ?Symbol {
+ const index = stab.index orelse return null;
+ return object.symbols.items[index];
+ }
+
+ pub fn format(
+ stab: Stab,
+ comptime unused_fmt_string: []const u8,
+ options: std.fmt.FormatOptions,
+ writer: anytype,
+ ) !void {
+ _ = stab;
+ _ = unused_fmt_string;
+ _ = options;
+ _ = writer;
+ @compileError("do not format stabs directly");
+ }
- fn getSymbol(stab: Stab, macho_file: *MachO) ?*Symbol {
- return if (stab.symbol) |s| macho_file.getSymbol(s) else null;
+ const StabFormatContext = struct { Stab, Object };
+
+ pub fn fmt(stab: Stab, object: Object) std.fmt.Formatter(format2) {
+ return .{ .data = .{ stab, object } };
+ }
+
+ fn format2(
+ ctx: StabFormatContext,
+ comptime unused_fmt_string: []const u8,
+ options: std.fmt.FormatOptions,
+ writer: anytype,
+ ) !void {
+ _ = unused_fmt_string;
+ _ = options;
+ const stab, const object = ctx;
+ const sym = stab.getSymbol(object).?;
+ if (stab.is_func) {
+ try writer.print("func({d})", .{stab.index.?});
+ } else if (sym.visibility == .global) {
+ try writer.print("gsym({d})", .{stab.index.?});
+ } else {
+ try writer.print("stsym({d})", .{stab.index.?});
+ }
}
};
};
@@ -2274,43 +2765,52 @@ const CompileUnit = struct {
comp_dir: u32,
tu_name: u32,
- fn getCompDir(cu: CompileUnit, object: *const Object) [:0]const u8 {
+ fn getCompDir(cu: CompileUnit, object: Object) [:0]const u8 {
return object.getString(cu.comp_dir);
}
- fn getTuName(cu: CompileUnit, object: *const Object) [:0]const u8 {
+ fn getTuName(cu: CompileUnit, object: Object) [:0]const u8 {
return object.getString(cu.tu_name);
}
};
const InArchive = struct {
path: []const u8,
- offset: u64,
size: u32,
};
+const CompactUnwindCtx = struct {
+ rec_index: u32 = 0,
+ rec_count: u32 = 0,
+ reloc_index: u32 = 0,
+ reloc_count: u32 = 0,
+};
+
const x86_64 = struct {
fn parseRelocs(
- self: *const Object,
- n_sect: u8,
+ self: *Object,
sect: macho.section_64,
out: *std.ArrayListUnmanaged(Relocation),
+ handle: File.Handle,
macho_file: *MachO,
) !void {
const gpa = macho_file.base.comp.gpa;
- const handle = macho_file.getFileHandle(self.file_handle);
- const offset = if (self.archive) |ar| ar.offset else 0;
const relocs_buffer = try gpa.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info));
defer gpa.free(relocs_buffer);
{
- const amt = try handle.preadAll(relocs_buffer, sect.reloff + offset);
+ const amt = try handle.preadAll(relocs_buffer, sect.reloff + self.offset);
if (amt != relocs_buffer.len) return error.InputOutput;
}
const relocs = @as([*]align(1) const macho.relocation_info, @ptrCast(relocs_buffer.ptr))[0..sect.nreloc];
- const code = try self.getSectionData(@intCast(n_sect), macho_file);
+ const sect_size = math.cast(usize, sect.size) orelse return error.Overflow;
+ const code = try gpa.alloc(u8, sect_size);
defer gpa.free(code);
+ {
+ const amt = try handle.preadAll(code, sect.offset + self.offset);
+ if (amt != code.len) return error.InputOutput;
+ }
try out.ensureTotalCapacityPrecise(gpa, relocs.len);
@@ -2332,8 +2832,9 @@ const x86_64 = struct {
.X86_64_RELOC_SIGNED_4 => 4,
else => 0,
};
+ var is_extern = rel.r_extern == 1;
- const target = if (rel.r_extern == 0) blk: {
+ const target = if (!is_extern) blk: {
const nsect = rel.r_symbolnum - 1;
const taddr: i64 = if (rel.r_pcrel == 1)
@as(i64, @intCast(sect.addr)) + rel.r_address + addend + 4
@@ -2345,9 +2846,15 @@ const x86_64 = struct {
});
return error.MalformedObject;
};
- addend = taddr - @as(i64, @intCast(macho_file.getAtom(target).?.getInputAddress(macho_file)));
+ const target_atom = self.getAtom(target).?;
+ addend = taddr - @as(i64, @intCast(target_atom.getInputAddress(macho_file)));
+ const isec = target_atom.getInputSection(macho_file);
+ if (isCstringLiteral(isec) or isFixedSizeLiteral(isec) or isPtrLiteral(isec)) {
+ is_extern = true;
+ break :blk target_atom.getExtra(macho_file).literal_symbol_index;
+ }
break :blk target;
- } else self.symbols.items[rel.r_symbolnum];
+ } else rel.r_symbolnum;
const has_subtractor = if (i > 0 and
@as(macho.reloc_type_x86_64, @enumFromInt(relocs[i - 1].r_type)) == .X86_64_RELOC_SUBTRACTOR)
@@ -2361,7 +2868,7 @@ const x86_64 = struct {
break :blk true;
} else false;
- const @"type": Relocation.Type = validateRelocType(rel, rel_type) catch |err| {
+ const @"type": Relocation.Type = validateRelocType(rel, rel_type, is_extern) catch |err| {
switch (err) {
error.Pcrel => try macho_file.reportParseError2(
self.index,
@@ -2388,7 +2895,7 @@ const x86_64 = struct {
};
out.appendAssumeCapacity(.{
- .tag = if (rel.r_extern == 1) .@"extern" else .local,
+ .tag = if (is_extern) .@"extern" else .local,
.offset = @as(u32, @intCast(rel.r_address)),
.target = target,
.addend = addend,
@@ -2403,7 +2910,7 @@ const x86_64 = struct {
}
}
- fn validateRelocType(rel: macho.relocation_info, rel_type: macho.reloc_type_x86_64) !Relocation.Type {
+ fn validateRelocType(rel: macho.relocation_info, rel_type: macho.reloc_type_x86_64, is_extern: bool) !Relocation.Type {
switch (rel_type) {
.X86_64_RELOC_UNSIGNED => {
if (rel.r_pcrel == 1) return error.Pcrel;
@@ -2423,7 +2930,7 @@ const x86_64 = struct {
=> {
if (rel.r_pcrel == 0) return error.NonPcrel;
if (rel.r_length != 2) return error.InvalidLength;
- if (rel.r_extern == 0) return error.NonExtern;
+ if (!is_extern) return error.NonExtern;
return switch (rel_type) {
.X86_64_RELOC_BRANCH => .branch,
.X86_64_RELOC_GOT_LOAD => .got_load,
@@ -2454,26 +2961,29 @@ const x86_64 = struct {
const aarch64 = struct {
fn parseRelocs(
- self: *const Object,
- n_sect: u8,
+ self: *Object,
sect: macho.section_64,
out: *std.ArrayListUnmanaged(Relocation),
+ handle: File.Handle,
macho_file: *MachO,
) !void {
const gpa = macho_file.base.comp.gpa;
- const handle = macho_file.getFileHandle(self.file_handle);
- const offset = if (self.archive) |ar| ar.offset else 0;
const relocs_buffer = try gpa.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info));
defer gpa.free(relocs_buffer);
{
- const amt = try handle.preadAll(relocs_buffer, sect.reloff + offset);
+ const amt = try handle.preadAll(relocs_buffer, sect.reloff + self.offset);
if (amt != relocs_buffer.len) return error.InputOutput;
}
const relocs = @as([*]align(1) const macho.relocation_info, @ptrCast(relocs_buffer.ptr))[0..sect.nreloc];
- const code = try self.getSectionData(@intCast(n_sect), macho_file);
+ const sect_size = math.cast(usize, sect.size) orelse return error.Overflow;
+ const code = try gpa.alloc(u8, sect_size);
defer gpa.free(code);
+ {
+ const amt = try handle.preadAll(code, sect.offset + self.offset);
+ if (amt != code.len) return error.InputOutput;
+ }
try out.ensureTotalCapacityPrecise(gpa, relocs.len);
@@ -2519,8 +3029,9 @@ const aarch64 = struct {
}
const rel_type: macho.reloc_type_arm64 = @enumFromInt(rel.r_type);
+ var is_extern = rel.r_extern == 1;
- const target = if (rel.r_extern == 0) blk: {
+ const target = if (!is_extern) blk: {
const nsect = rel.r_symbolnum - 1;
const taddr: i64 = if (rel.r_pcrel == 1)
@as(i64, @intCast(sect.addr)) + rel.r_address + addend
@@ -2532,9 +3043,15 @@ const aarch64 = struct {
});
return error.MalformedObject;
};
- addend = taddr - @as(i64, @intCast(macho_file.getAtom(target).?.getInputAddress(macho_file)));
+ const target_atom = self.getAtom(target).?;
+ addend = taddr - @as(i64, @intCast(target_atom.getInputAddress(macho_file)));
+ const isec = target_atom.getInputSection(macho_file);
+ if (isCstringLiteral(isec) or isFixedSizeLiteral(isec) or isPtrLiteral(isec)) {
+ is_extern = true;
+ break :blk target_atom.getExtra(macho_file).literal_symbol_index;
+ }
break :blk target;
- } else self.symbols.items[rel.r_symbolnum];
+ } else rel.r_symbolnum;
const has_subtractor = if (i > 0 and
@as(macho.reloc_type_arm64, @enumFromInt(relocs[i - 1].r_type)) == .ARM64_RELOC_SUBTRACTOR)
@@ -2548,7 +3065,7 @@ const aarch64 = struct {
break :blk true;
} else false;
- const @"type": Relocation.Type = validateRelocType(rel, rel_type) catch |err| {
+ const @"type": Relocation.Type = validateRelocType(rel, rel_type, is_extern) catch |err| {
switch (err) {
error.Pcrel => try macho_file.reportParseError2(
self.index,
@@ -2575,7 +3092,7 @@ const aarch64 = struct {
};
out.appendAssumeCapacity(.{
- .tag = if (rel.r_extern == 1) .@"extern" else .local,
+ .tag = if (is_extern) .@"extern" else .local,
.offset = @as(u32, @intCast(rel.r_address)),
.target = target,
.addend = addend,
@@ -2590,7 +3107,7 @@ const aarch64 = struct {
}
}
- fn validateRelocType(rel: macho.relocation_info, rel_type: macho.reloc_type_arm64) !Relocation.Type {
+ fn validateRelocType(rel: macho.relocation_info, rel_type: macho.reloc_type_arm64, is_extern: bool) !Relocation.Type {
switch (rel_type) {
.ARM64_RELOC_UNSIGNED => {
if (rel.r_pcrel == 1) return error.Pcrel;
@@ -2611,7 +3128,7 @@ const aarch64 = struct {
=> {
if (rel.r_pcrel == 0) return error.NonPcrel;
if (rel.r_length != 2) return error.InvalidLength;
- if (rel.r_extern == 0) return error.NonExtern;
+ if (!is_extern) return error.NonExtern;
return switch (rel_type) {
.ARM64_RELOC_BRANCH26 => .branch,
.ARM64_RELOC_PAGE21 => .page,
@@ -2628,7 +3145,7 @@ const aarch64 = struct {
=> {
if (rel.r_pcrel == 1) return error.Pcrel;
if (rel.r_length != 2) return error.InvalidLength;
- if (rel.r_extern == 0) return error.NonExtern;
+ if (!is_extern) return error.NonExtern;
return switch (rel_type) {
.ARM64_RELOC_PAGEOFF12 => .pageoff,
.ARM64_RELOC_GOT_LOAD_PAGEOFF12 => .got_load_pageoff,
diff --git a/src/link/MachO/Relocation.zig b/src/link/MachO/Relocation.zig
@@ -1,4 +1,4 @@
-tag: enum { @"extern", local },
+tag: Tag,
offset: u32,
target: u32,
addend: i64,
@@ -10,34 +10,44 @@ meta: packed struct {
symbolnum: u24,
},
-pub fn getTargetSymbol(rel: Relocation, macho_file: *MachO) *Symbol {
+pub fn getTargetSymbolRef(rel: Relocation, atom: Atom, macho_file: *MachO) MachO.Ref {
assert(rel.tag == .@"extern");
- return macho_file.getSymbol(rel.target);
+ return atom.getFile(macho_file).getSymbolRef(rel.target, macho_file);
}
-pub fn getTargetAtom(rel: Relocation, macho_file: *MachO) *Atom {
+pub fn getTargetSymbol(rel: Relocation, atom: Atom, macho_file: *MachO) *Symbol {
+ assert(rel.tag == .@"extern");
+ const ref = atom.getFile(macho_file).getSymbolRef(rel.target, macho_file);
+ return ref.getSymbol(macho_file).?;
+}
+
+pub fn getTargetAtom(rel: Relocation, atom: Atom, macho_file: *MachO) *Atom {
assert(rel.tag == .local);
- return macho_file.getAtom(rel.target).?;
+ return atom.getFile(macho_file).getAtom(rel.target).?;
}
-pub fn getTargetAddress(rel: Relocation, macho_file: *MachO) u64 {
+pub fn getTargetAddress(rel: Relocation, atom: Atom, macho_file: *MachO) u64 {
return switch (rel.tag) {
- .local => rel.getTargetAtom(macho_file).getAddress(macho_file),
- .@"extern" => rel.getTargetSymbol(macho_file).getAddress(.{}, macho_file),
+ .local => rel.getTargetAtom(atom, macho_file).getAddress(macho_file),
+ .@"extern" => rel.getTargetSymbol(atom, macho_file).getAddress(.{}, macho_file),
};
}
-pub fn getGotTargetAddress(rel: Relocation, macho_file: *MachO) u64 {
+pub fn getGotTargetAddress(rel: Relocation, atom: Atom, macho_file: *MachO) u64 {
return switch (rel.tag) {
.local => 0,
- .@"extern" => rel.getTargetSymbol(macho_file).getGotAddress(macho_file),
+ .@"extern" => rel.getTargetSymbol(atom, macho_file).getGotAddress(macho_file),
};
}
pub fn getZigGotTargetAddress(rel: Relocation, macho_file: *MachO) u64 {
+ const zo = macho_file.getZigObject() orelse return 0;
return switch (rel.tag) {
.local => 0,
- .@"extern" => rel.getTargetSymbol(macho_file).getZigGotAddress(macho_file),
+ .@"extern" => {
+ const ref = zo.getSymbolRef(rel.target, macho_file);
+ return ref.getSymbol(macho_file).?.getZigGotAddress(macho_file);
+ },
};
}
@@ -155,6 +165,8 @@ pub const Type = enum {
unsigned,
};
+const Tag = enum { local, @"extern" };
+
const assert = std.debug.assert;
const macho = std.macho;
const math = std.math;
diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig
@@ -9,17 +9,16 @@ name: u32 = 0,
/// File where this symbol is defined.
file: File.Index = 0,
-/// Atom containing this symbol if any.
-/// Index of 0 means there is no associated atom with this symbol.
+/// Reference to Atom containing this symbol if any.
/// Use `getAtom` to get the pointer to the atom.
-atom: Atom.Index = 0,
+atom_ref: MachO.Ref = .{ .index = 0, .file = 0 },
/// Assigned output section index for this symbol.
out_n_sect: u8 = 0,
/// Index of the source nlist this symbol references.
/// Use `getNlist` to pull the nlist from the relevant file.
-nlist_idx: Index = 0,
+nlist_idx: u32 = 0,
/// Misc flags for the symbol packaged as packed struct for compression.
flags: Flags = .{},
@@ -55,16 +54,19 @@ pub fn weakRef(symbol: Symbol, macho_file: *MachO) bool {
}
pub fn getName(symbol: Symbol, macho_file: *MachO) [:0]const u8 {
- if (symbol.flags.global) return macho_file.strings.getAssumeExists(symbol.name);
return switch (symbol.getFile(macho_file).?) {
- .dylib => unreachable, // There are no local symbols for dylibs
.zig_object => |x| x.strtab.getAssumeExists(symbol.name),
inline else => |x| x.getString(symbol.name),
};
}
pub fn getAtom(symbol: Symbol, macho_file: *MachO) ?*Atom {
- return macho_file.getAtom(symbol.atom);
+ return symbol.atom_ref.getAtom(macho_file);
+}
+
+pub fn getOutputSectionIndex(symbol: Symbol, macho_file: *MachO) u8 {
+ if (symbol.getAtom(macho_file)) |atom| return atom.out_n_sect;
+ return symbol.out_n_sect;
}
pub fn getFile(symbol: Symbol, macho_file: *MachO) ?File {
@@ -75,8 +77,10 @@ pub fn getFile(symbol: Symbol, macho_file: *MachO) ?File {
pub fn getNlist(symbol: Symbol, macho_file: *MachO) macho.nlist_64 {
const file = symbol.getFile(macho_file).?;
return switch (file) {
+ .dylib => unreachable,
+ .zig_object => unreachable,
.object => |x| x.symtab.items(.nlist)[symbol.nlist_idx],
- else => unreachable,
+ .internal => |x| x.symtab.items[symbol.nlist_idx],
};
}
@@ -124,33 +128,35 @@ pub fn getAddress(symbol: Symbol, opts: struct {
pub fn getGotAddress(symbol: Symbol, macho_file: *MachO) u64 {
if (!symbol.flags.has_got) return 0;
- const extra = symbol.getExtra(macho_file).?;
+ const extra = symbol.getExtra(macho_file);
return macho_file.got.getAddress(extra.got, macho_file);
}
pub fn getStubsAddress(symbol: Symbol, macho_file: *MachO) u64 {
if (!symbol.flags.stubs) return 0;
- const extra = symbol.getExtra(macho_file).?;
+ const extra = symbol.getExtra(macho_file);
return macho_file.stubs.getAddress(extra.stubs, macho_file);
}
pub fn getObjcStubsAddress(symbol: Symbol, macho_file: *MachO) u64 {
if (!symbol.flags.objc_stubs) return 0;
- const extra = symbol.getExtra(macho_file).?;
+ const extra = symbol.getExtra(macho_file);
return macho_file.objc_stubs.getAddress(extra.objc_stubs, macho_file);
}
pub fn getObjcSelrefsAddress(symbol: Symbol, macho_file: *MachO) u64 {
if (!symbol.flags.objc_stubs) return 0;
- const extra = symbol.getExtra(macho_file).?;
- const atom = macho_file.getAtom(extra.objc_selrefs).?;
- assert(atom.flags.alive);
- return atom.getAddress(macho_file);
+ const extra = symbol.getExtra(macho_file);
+ const file = symbol.getFile(macho_file).?;
+ return switch (file) {
+ .dylib, .zig_object => unreachable,
+ inline else => |x| x.symbols.items[extra.objc_selrefs].getAddress(.{}, macho_file),
+ };
}
pub fn getTlvPtrAddress(symbol: Symbol, macho_file: *MachO) u64 {
if (!symbol.flags.tlv_ptr) return 0;
- const extra = symbol.getExtra(macho_file).?;
+ const extra = symbol.getExtra(macho_file);
return macho_file.tlv_ptr.getAddress(extra.tlv_ptr, macho_file);
}
@@ -162,14 +168,14 @@ const GetOrCreateZigGotEntryResult = struct {
pub fn getOrCreateZigGotEntry(symbol: *Symbol, symbol_index: Index, macho_file: *MachO) !GetOrCreateZigGotEntryResult {
assert(!macho_file.base.isRelocatable());
assert(symbol.flags.needs_zig_got);
- if (symbol.flags.has_zig_got) return .{ .found_existing = true, .index = symbol.getExtra(macho_file).?.zig_got };
+ if (symbol.flags.has_zig_got) return .{ .found_existing = true, .index = symbol.getExtra(macho_file).zig_got };
const index = try macho_file.zig_got.addSymbol(symbol_index, macho_file);
return .{ .found_existing = false, .index = index };
}
pub fn getZigGotAddress(symbol: Symbol, macho_file: *MachO) u64 {
if (!symbol.flags.has_zig_got) return 0;
- const extras = symbol.getExtra(macho_file).?;
+ const extras = symbol.getExtra(macho_file);
return macho_file.zig_got.entryAddress(extras.zig_got, macho_file);
}
@@ -180,7 +186,7 @@ pub fn getOutputSymtabIndex(symbol: Symbol, macho_file: *MachO) ?u32 {
const symtab_ctx = switch (file) {
inline else => |x| x.output_symtab_ctx,
};
- var idx = symbol.getExtra(macho_file).?.symtab;
+ var idx = symbol.getExtra(macho_file).symtab;
if (symbol.isLocal()) {
idx += symtab_ctx.ilocal;
} else if (symbol.flags.@"export") {
@@ -202,11 +208,8 @@ const AddExtraOpts = struct {
symtab: ?u32 = null,
};
-pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, macho_file: *MachO) !void {
- if (symbol.getExtra(macho_file) == null) {
- symbol.extra = try macho_file.addSymbolExtra(.{});
- }
- var extra = symbol.getExtra(macho_file).?;
+pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, macho_file: *MachO) void {
+ var extra = symbol.getExtra(macho_file);
inline for (@typeInfo(@TypeOf(opts)).Struct.fields) |field| {
if (@field(opts, field.name)) |x| {
@field(extra, field.name) = x;
@@ -215,18 +218,22 @@ pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, macho_file: *MachO) !void {
symbol.setExtra(extra, macho_file);
}
-pub inline fn getExtra(symbol: Symbol, macho_file: *MachO) ?Extra {
- return macho_file.getSymbolExtra(symbol.extra);
+pub inline fn getExtra(symbol: Symbol, macho_file: *MachO) Extra {
+ return switch (symbol.getFile(macho_file).?) {
+ inline else => |x| x.getSymbolExtra(symbol.extra),
+ };
}
pub inline fn setExtra(symbol: Symbol, extra: Extra, macho_file: *MachO) void {
- macho_file.setSymbolExtra(symbol.extra, extra);
+ return switch (symbol.getFile(macho_file).?) {
+ inline else => |x| x.setSymbolExtra(symbol.extra, extra),
+ };
}
pub fn setOutputSym(symbol: Symbol, macho_file: *MachO, out: *macho.nlist_64) void {
if (symbol.isLocal()) {
out.n_type = if (symbol.flags.abs) macho.N_ABS else macho.N_SECT;
- out.n_sect = if (symbol.flags.abs) 0 else @intCast(symbol.out_n_sect + 1);
+ out.n_sect = if (symbol.flags.abs) 0 else @intCast(symbol.getOutputSectionIndex(macho_file) + 1);
out.n_desc = 0;
out.n_value = symbol.getAddress(.{ .stubs = false }, macho_file);
@@ -238,7 +245,7 @@ pub fn setOutputSym(symbol: Symbol, macho_file: *MachO, out: *macho.nlist_64) vo
assert(symbol.visibility == .global);
out.n_type = macho.N_EXT;
out.n_type |= if (symbol.flags.abs) macho.N_ABS else macho.N_SECT;
- out.n_sect = if (symbol.flags.abs) 0 else @intCast(symbol.out_n_sect + 1);
+ out.n_sect = if (symbol.flags.abs) 0 else @intCast(symbol.getOutputSectionIndex(macho_file) + 1);
out.n_value = symbol.getAddress(.{ .stubs = false }, macho_file);
out.n_desc = 0;
@@ -318,15 +325,20 @@ fn format2(
symbol.getAddress(.{}, ctx.macho_file),
});
if (symbol.getFile(ctx.macho_file)) |file| {
- if (symbol.out_n_sect != 0) {
- try writer.print(" : sect({d})", .{symbol.out_n_sect});
+ if (symbol.getOutputSectionIndex(ctx.macho_file) != 0) {
+ try writer.print(" : sect({d})", .{symbol.getOutputSectionIndex(ctx.macho_file)});
}
if (symbol.getAtom(ctx.macho_file)) |atom| {
try writer.print(" : atom({d})", .{atom.atom_index});
}
- var buf: [2]u8 = .{'_'} ** 2;
+ var buf: [3]u8 = .{'_'} ** 3;
if (symbol.flags.@"export") buf[0] = 'E';
if (symbol.flags.import) buf[1] = 'I';
+ switch (symbol.visibility) {
+ .local => buf[2] = 'L',
+ .hidden => buf[2] = 'H',
+ .global => buf[2] = 'G',
+ }
try writer.print(" : {s}", .{&buf});
if (symbol.flags.weak) try writer.writeAll(" : weak");
if (symbol.isSymbolStab(ctx.macho_file)) try writer.writeAll(" : stab");
@@ -346,11 +358,6 @@ pub const Flags = packed struct {
/// Whether the symbol is exported at runtime.
@"export": bool = false,
- /// Whether the symbol is effectively an extern and takes part in global
- /// symbol resolution. Then, its name will be saved in global string interning
- /// table.
- global: bool = false,
-
/// Whether this symbol is weak.
weak: bool = false,
@@ -400,6 +407,14 @@ pub const Visibility = enum {
global,
hidden,
local,
+
+ pub fn rank(vis: Visibility) u2 {
+ return switch (vis) {
+ .local => 2,
+ .hidden => 1,
+ .global => 0,
+ };
+ }
};
pub const Extra = struct {
diff --git a/src/link/MachO/UnwindInfo.zig b/src/link/MachO/UnwindInfo.zig
@@ -1,10 +1,10 @@
/// List of all unwind records gathered from all objects and sorted
/// by allocated relative function address within the section.
-records: std.ArrayListUnmanaged(Record.Index) = .{},
+records: std.ArrayListUnmanaged(Record.Ref) = .{},
/// List of all personalities referenced by either unwind info entries
/// or __eh_frame entries.
-personalities: [max_personalities]Symbol.Index = undefined,
+personalities: [max_personalities]MachO.Ref = undefined,
personalities_count: u2 = 0,
/// List of common encodings sorted in descending order with the most common first.
@@ -25,10 +25,10 @@ pub fn deinit(info: *UnwindInfo, allocator: Allocator) void {
info.lsdas_lookup.deinit(allocator);
}
-fn canFold(macho_file: *MachO, lhs_index: Record.Index, rhs_index: Record.Index) bool {
+fn canFold(macho_file: *MachO, lhs_ref: Record.Ref, rhs_ref: Record.Ref) bool {
const cpu_arch = macho_file.getTarget().cpu.arch;
- const lhs = macho_file.getUnwindRecord(lhs_index);
- const rhs = macho_file.getUnwindRecord(rhs_index);
+ const lhs = lhs_ref.getUnwindRecord(macho_file);
+ const rhs = rhs_ref.getUnwindRecord(macho_file);
if (cpu_arch == .x86_64) {
if (lhs.enc.getMode() == @intFromEnum(macho.UNWIND_X86_64_MODE.STACK_IND) or
rhs.enc.getMode() == @intFromEnum(macho.UNWIND_X86_64_MODE.STACK_IND)) return false;
@@ -42,27 +42,31 @@ fn canFold(macho_file: *MachO, lhs_index: Record.Index, rhs_index: Record.Index)
}
pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
const gpa = macho_file.base.comp.gpa;
log.debug("generating unwind info", .{});
// Collect all unwind records
for (macho_file.sections.items(.atoms)) |atoms| {
- for (atoms.items) |atom_index| {
- const atom = macho_file.getAtom(atom_index) orelse continue;
+ for (atoms.items) |ref| {
+ const atom = ref.getAtom(macho_file) orelse continue;
if (!atom.flags.alive) continue;
const recs = atom.getUnwindRecords(macho_file);
+ const file = atom.getFile(macho_file);
try info.records.ensureUnusedCapacity(gpa, recs.len);
for (recs) |rec| {
- if (!macho_file.getUnwindRecord(rec).alive) continue;
- info.records.appendAssumeCapacity(rec);
+ if (!file.object.getUnwindRecord(rec).alive) continue;
+ info.records.appendAssumeCapacity(.{ .record = rec, .file = file.getIndex() });
}
}
}
// Encode records
- for (info.records.items) |index| {
- const rec = macho_file.getUnwindRecord(index);
+ for (info.records.items) |ref| {
+ const rec = ref.getUnwindRecord(macho_file);
if (rec.getFde(macho_file)) |fde| {
rec.enc.setDwarfSectionOffset(@intCast(fde.out_offset));
if (fde.getLsdaAtom(macho_file)) |lsda| {
@@ -72,27 +76,31 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
}
const cie = fde.getCie(macho_file);
if (cie.getPersonality(macho_file)) |_| {
- const personality_index = try info.getOrPutPersonalityFunction(cie.personality.?.index); // TODO handle error
+ const object = cie.getObject(macho_file);
+ const sym_ref = object.getSymbolRef(cie.personality.?.index, macho_file);
+ const personality_index = try info.getOrPutPersonalityFunction(sym_ref); // TODO handle error
rec.enc.setPersonalityIndex(personality_index + 1);
}
} else if (rec.getPersonality(macho_file)) |_| {
- const personality_index = try info.getOrPutPersonalityFunction(rec.personality.?); // TODO handle error
+ const object = rec.getObject(macho_file);
+ const sym_ref = object.getSymbolRef(rec.personality.?, macho_file);
+ const personality_index = try info.getOrPutPersonalityFunction(sym_ref); // TODO handle error
rec.enc.setPersonalityIndex(personality_index + 1);
}
}
// Sort by assigned relative address within each output section
const sortFn = struct {
- fn sortFn(ctx: *MachO, lhs_index: Record.Index, rhs_index: Record.Index) bool {
- const lhs = ctx.getUnwindRecord(lhs_index);
- const rhs = ctx.getUnwindRecord(rhs_index);
+ fn sortFn(ctx: *MachO, lhs_ref: Record.Ref, rhs_ref: Record.Ref) bool {
+ const lhs = lhs_ref.getUnwindRecord(ctx);
+ const rhs = rhs_ref.getUnwindRecord(ctx);
const lhsa = lhs.getAtom(ctx);
const rhsa = rhs.getAtom(ctx);
if (lhsa.out_n_sect == rhsa.out_n_sect) return lhs.getAtomAddress(ctx) < rhs.getAtomAddress(ctx);
return lhsa.out_n_sect < rhsa.out_n_sect;
}
}.sortFn;
- mem.sort(Record.Index, info.records.items, macho_file, sortFn);
+ mem.sort(Record.Ref, info.records.items, macho_file, sortFn);
// Fold the records
// Any adjacent two records that share encoding can be folded into one.
@@ -101,8 +109,8 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
var j: usize = 1;
while (j < info.records.items.len) : (j += 1) {
if (canFold(macho_file, info.records.items[i], info.records.items[j])) {
- const rec = macho_file.getUnwindRecord(info.records.items[i]);
- rec.length += macho_file.getUnwindRecord(info.records.items[j]).length + 1;
+ const rec = info.records.items[i].getUnwindRecord(macho_file);
+ rec.length += info.records.items[j].getUnwindRecord(macho_file).length + 1;
} else {
i += 1;
info.records.items[i] = info.records.items[j];
@@ -111,14 +119,15 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
info.records.shrinkAndFree(gpa, i + 1);
}
- for (info.records.items) |rec_index| {
- const rec = macho_file.getUnwindRecord(rec_index);
+ for (info.records.items) |ref| {
+ const rec = ref.getUnwindRecord(macho_file);
const atom = rec.getAtom(macho_file);
- log.debug("@{x}-{x} : {s} : rec({d}) : {}", .{
+ log.debug("@{x}-{x} : {s} : rec({d}) : object({d}) : {}", .{
rec.getAtomAddress(macho_file),
rec.getAtomAddress(macho_file) + rec.length,
atom.getName(macho_file),
- rec_index,
+ ref.record,
+ ref.file,
rec.enc,
});
}
@@ -161,8 +170,8 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
).init(gpa);
defer common_encodings_counts.deinit();
- for (info.records.items) |rec_index| {
- const rec = macho_file.getUnwindRecord(rec_index);
+ for (info.records.items) |ref| {
+ const rec = ref.getUnwindRecord(macho_file);
if (rec.enc.isDwarf(macho_file)) continue;
const gop = try common_encodings_counts.getOrPut(rec.enc);
if (!gop.found_existing) {
@@ -190,7 +199,7 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
{
var i: u32 = 0;
while (i < info.records.items.len) {
- const rec = macho_file.getUnwindRecord(info.records.items[i]);
+ const rec = info.records.items[i].getUnwindRecord(macho_file);
const range_start_max: u64 = rec.getAtomAddress(macho_file) + compressed_entry_func_offset_mask;
var encoding_count: u9 = info.common_encodings_count;
var space_left: u32 = second_level_page_words -
@@ -202,7 +211,7 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
};
while (space_left >= 1 and i < info.records.items.len) {
- const next = macho_file.getUnwindRecord(info.records.items[i]);
+ const next = info.records.items[i].getUnwindRecord(macho_file);
const is_dwarf = next.enc.isDwarf(macho_file);
if (next.getAtomAddress(macho_file) >= range_start_max) {
@@ -244,8 +253,8 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
// Save records having an LSDA pointer
log.debug("LSDA pointers:", .{});
try info.lsdas_lookup.ensureTotalCapacityPrecise(gpa, info.records.items.len);
- for (info.records.items, 0..) |index, i| {
- const rec = macho_file.getUnwindRecord(index);
+ for (info.records.items, 0..) |ref, i| {
+ const rec = ref.getUnwindRecord(macho_file);
info.lsdas_lookup.appendAssumeCapacity(@intCast(info.lsdas.items.len));
if (rec.getLsdaAtom(macho_file)) |lsda| {
log.debug(" @{x} => lsda({d})", .{ rec.getAtomAddress(macho_file), lsda.atom_index });
@@ -255,6 +264,9 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
}
pub fn calcSize(info: UnwindInfo) usize {
+ const tracy = trace(@src());
+ defer tracy.end();
+
var total_size: usize = 0;
total_size += @sizeOf(macho.unwind_info_section_header);
total_size +=
@@ -291,8 +303,8 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void {
try writer.writeAll(mem.sliceAsBytes(info.common_encodings[0..info.common_encodings_count]));
- for (info.personalities[0..info.personalities_count]) |sym_index| {
- const sym = macho_file.getSymbol(sym_index);
+ for (info.personalities[0..info.personalities_count]) |ref| {
+ const sym = ref.getSymbol(macho_file).?;
try writer.writeInt(u32, @intCast(sym.getGotAddress(macho_file) - seg.vmaddr), .little);
}
@@ -301,7 +313,7 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void {
(info.lsdas.items.len * @sizeOf(macho.unwind_info_section_header_lsda_index_entry))));
for (info.pages.items, 0..) |page, i| {
assert(page.count > 0);
- const rec = macho_file.getUnwindRecord(info.records.items[page.start]);
+ const rec = info.records.items[page.start].getUnwindRecord(macho_file);
try writer.writeStruct(macho.unwind_info_section_header_index_entry{
.functionOffset = @as(u32, @intCast(rec.getAtomAddress(macho_file) - seg.vmaddr)),
.secondLevelPagesSectionOffset = @as(u32, @intCast(pages_base_offset + i * second_level_page_bytes)),
@@ -310,7 +322,7 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void {
});
}
- const last_rec = macho_file.getUnwindRecord(info.records.items[info.records.items.len - 1]);
+ const last_rec = info.records.items[info.records.items.len - 1].getUnwindRecord(macho_file);
const sentinel_address = @as(u32, @intCast(last_rec.getAtomAddress(macho_file) + last_rec.length - seg.vmaddr));
try writer.writeStruct(macho.unwind_info_section_header_index_entry{
.functionOffset = sentinel_address,
@@ -320,7 +332,7 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void {
});
for (info.lsdas.items) |index| {
- const rec = macho_file.getUnwindRecord(info.records.items[index]);
+ const rec = info.records.items[index].getUnwindRecord(macho_file);
try writer.writeStruct(macho.unwind_info_section_header_lsda_index_entry{
.functionOffset = @as(u32, @intCast(rec.getAtomAddress(macho_file) - seg.vmaddr)),
.lsdaOffset = @as(u32, @intCast(rec.getLsdaAddress(macho_file) - seg.vmaddr)),
@@ -340,13 +352,13 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void {
@memset(buffer[stream.pos..], 0);
}
-fn getOrPutPersonalityFunction(info: *UnwindInfo, sym_index: Symbol.Index) error{TooManyPersonalities}!u2 {
+fn getOrPutPersonalityFunction(info: *UnwindInfo, ref: MachO.Ref) error{TooManyPersonalities}!u2 {
comptime var index: u2 = 0;
inline while (index < max_personalities) : (index += 1) {
- if (info.personalities[index] == sym_index) {
+ if (info.personalities[index].eql(ref)) {
return index;
} else if (index == info.personalities_count) {
- info.personalities[index] = sym_index;
+ info.personalities[index] = ref;
info.personalities_count += 1;
return index;
}
@@ -461,16 +473,17 @@ pub const Record = struct {
}
pub fn getAtom(rec: Record, macho_file: *MachO) *Atom {
- return macho_file.getAtom(rec.atom).?;
+ return rec.getObject(macho_file).getAtom(rec.atom).?;
}
pub fn getLsdaAtom(rec: Record, macho_file: *MachO) ?*Atom {
- return macho_file.getAtom(rec.lsda);
+ return rec.getObject(macho_file).getAtom(rec.lsda);
}
pub fn getPersonality(rec: Record, macho_file: *MachO) ?*Symbol {
const personality = rec.personality orelse return null;
- return macho_file.getSymbol(personality);
+ const object = rec.getObject(macho_file);
+ return object.getSymbolRef(personality, macho_file).getSymbol(macho_file);
}
pub fn getFde(rec: Record, macho_file: *MachO) ?Fde {
@@ -537,6 +550,15 @@ pub const Record = struct {
}
pub const Index = u32;
+
+ const Ref = struct {
+ record: Index,
+ file: File.Index,
+
+ pub fn getUnwindRecord(ref: Ref, macho_file: *MachO) *Record {
+ return macho_file.getFile(ref.file).?.object.getUnwindRecord(ref.record);
+ }
+ };
};
const max_personalities = 3;
@@ -635,8 +657,8 @@ const Page = struct {
.entryCount = page.count,
});
- for (info.records.items[page.start..][0..page.count]) |index| {
- const rec = macho_file.getUnwindRecord(index);
+ for (info.records.items[page.start..][0..page.count]) |ref| {
+ const rec = ref.getUnwindRecord(macho_file);
try writer.writeStruct(macho.unwind_info_regular_second_level_entry{
.functionOffset = @as(u32, @intCast(rec.getAtomAddress(macho_file) - seg.vmaddr)),
.encoding = rec.enc.enc,
@@ -658,9 +680,9 @@ const Page = struct {
}
assert(page.count > 0);
- const first_rec = macho_file.getUnwindRecord(info.records.items[page.start]);
- for (info.records.items[page.start..][0..page.count]) |index| {
- const rec = macho_file.getUnwindRecord(index);
+ const first_rec = info.records.items[page.start].getUnwindRecord(macho_file);
+ for (info.records.items[page.start..][0..page.count]) |ref| {
+ const rec = ref.getUnwindRecord(macho_file);
const enc_index = blk: {
if (info.getCommonEncoding(rec.enc)) |id| break :blk id;
const ncommon = info.common_encodings_count;
diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig
@@ -6,9 +6,15 @@ index: File.Index,
symtab: std.MultiArrayList(Nlist) = .{},
strtab: StringTable = .{},
-symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
-atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
-globals_lookup: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .{},
+symbols: std.ArrayListUnmanaged(Symbol) = .{},
+symbols_extra: std.ArrayListUnmanaged(u32) = .{},
+globals: std.ArrayListUnmanaged(MachO.SymbolResolver.Index) = .{},
+/// Maps string index (so name) into nlist index for the global symbol defined within this
+/// module.
+globals_lookup: std.AutoHashMapUnmanaged(u32, u32) = .{},
+atoms: std.ArrayListUnmanaged(Atom) = .{},
+atoms_indexes: std.ArrayListUnmanaged(Atom.Index) = .{},
+atoms_extra: std.ArrayListUnmanaged(u32) = .{},
/// Table of tracked LazySymbols.
lazy_syms: LazySymbolTable = .{},
@@ -48,7 +54,6 @@ relocs: RelocationTable = .{},
dwarf: ?Dwarf = null,
-dynamic_relocs: MachO.DynamicRelocs = .{},
output_symtab_ctx: MachO.SymtabCtx = .{},
output_ar_state: Archive.ArState = .{},
@@ -59,10 +64,13 @@ debug_info_header_dirty: bool = false,
debug_line_header_dirty: bool = false,
pub fn init(self: *ZigObject, macho_file: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
const comp = macho_file.base.comp;
const gpa = comp.gpa;
- try self.atoms.append(gpa, 0); // null input section
+ try self.atoms.append(gpa, .{ .extra = try self.addAtomExtra(gpa, .{}) }); // null input section
try self.strtab.buffer.append(gpa, 0);
switch (comp.config.debug_format) {
@@ -85,8 +93,12 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void {
self.symtab.deinit(allocator);
self.strtab.deinit(allocator);
self.symbols.deinit(allocator);
- self.atoms.deinit(allocator);
+ self.symbols_extra.deinit(allocator);
+ self.globals.deinit(allocator);
self.globals_lookup.deinit(allocator);
+ self.atoms.deinit(allocator);
+ self.atoms_indexes.deinit(allocator);
+ self.atoms_extra.deinit(allocator);
{
var it = self.decls.iterator();
@@ -129,53 +141,73 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void {
}
}
-fn addNlist(self: *ZigObject, allocator: Allocator) !Symbol.Index {
+fn newSymbol(self: *ZigObject, allocator: Allocator, name: u32, args: struct {
+ type: u8 = macho.N_UNDF | macho.N_EXT,
+ desc: u16 = 0,
+}) !Symbol.Index {
try self.symtab.ensureUnusedCapacity(allocator, 1);
- const index = @as(Symbol.Index, @intCast(self.symtab.addOneAssumeCapacity()));
- self.symtab.set(index, .{
- .nlist = MachO.null_sym,
+ try self.symbols.ensureUnusedCapacity(allocator, 1);
+ try self.symbols_extra.ensureUnusedCapacity(allocator, @sizeOf(Symbol.Extra));
+ try self.globals.ensureUnusedCapacity(allocator, 1);
+
+ const index = self.addSymbolAssumeCapacity();
+ const symbol = &self.symbols.items[index];
+ symbol.name = name;
+ symbol.extra = self.addSymbolExtraAssumeCapacity(.{});
+
+ const nlist_idx: u32 = @intCast(self.symtab.addOneAssumeCapacity());
+ self.symtab.set(nlist_idx, .{
+ .nlist = .{
+ .n_strx = name,
+ .n_type = args.type,
+ .n_sect = 0,
+ .n_desc = args.desc,
+ .n_value = 0,
+ },
.size = 0,
.atom = 0,
});
- return index;
-}
-
-pub fn addAtom(self: *ZigObject, macho_file: *MachO) !Symbol.Index {
- const gpa = macho_file.base.comp.gpa;
- const atom_index = try macho_file.addAtom();
- const symbol_index = try macho_file.addSymbol();
- const nlist_index = try self.addNlist(gpa);
+ symbol.nlist_idx = nlist_idx;
- try self.atoms.append(gpa, atom_index);
- try self.symbols.append(gpa, symbol_index);
+ self.globals.appendAssumeCapacity(0);
- const atom = macho_file.getAtom(atom_index).?;
- atom.file = self.index;
- atom.atom_index = atom_index;
+ return index;
+}
- const symbol = macho_file.getSymbol(symbol_index);
- symbol.file = self.index;
- symbol.atom = atom_index;
+fn newAtom(self: *ZigObject, allocator: Allocator, name: u32, macho_file: *MachO) !Atom.Index {
+ try self.atoms.ensureUnusedCapacity(allocator, 1);
+ try self.atoms_extra.ensureUnusedCapacity(allocator, @sizeOf(Atom.Extra));
+ try self.atoms_indexes.ensureUnusedCapacity(allocator, 1);
+ try self.relocs.ensureUnusedCapacity(allocator, 1);
- self.symtab.items(.atom)[nlist_index] = atom_index;
- symbol.nlist_idx = nlist_index;
+ const index = self.addAtomAssumeCapacity();
+ self.atoms_indexes.appendAssumeCapacity(index);
+ const atom = self.getAtom(index).?;
+ atom.name = name;
const relocs_index = @as(u32, @intCast(self.relocs.items.len));
- const relocs = try self.relocs.addOne(gpa);
- relocs.* = .{};
- try atom.addExtra(.{ .rel_index = relocs_index, .rel_count = 0 }, macho_file);
- atom.flags.relocs = true;
+ self.relocs.addOneAssumeCapacity().* = .{};
+ atom.addExtra(.{ .rel_index = relocs_index, .rel_count = 0 }, macho_file);
- return symbol_index;
+ return index;
+}
+
+fn newSymbolWithAtom(self: *ZigObject, allocator: Allocator, name: u32, macho_file: *MachO) !Symbol.Index {
+ const atom_index = try self.newAtom(allocator, name, macho_file);
+ const sym_index = try self.newSymbol(allocator, name, .{ .type = macho.N_SECT });
+ const sym = &self.symbols.items[sym_index];
+ sym.atom_ref = .{ .index = atom_index, .file = self.index };
+ self.symtab.items(.atom)[sym.nlist_idx] = atom_index;
+ return sym_index;
}
pub fn getAtomData(self: ZigObject, macho_file: *MachO, atom: Atom, buffer: []u8) !void {
assert(atom.file == self.index);
assert(atom.size == buffer.len);
- const sect = macho_file.sections.items(.header)[atom.out_n_sect];
- assert(!sect.isZerofill());
+ const isec = atom.getInputSection(macho_file);
+ assert(!isec.isZerofill());
- switch (sect.type()) {
+ switch (isec.type()) {
macho.S_THREAD_LOCAL_REGULAR => {
const tlv = self.tlv_initializers.get(atom.atom_index).?;
@memcpy(buffer, tlv.data);
@@ -184,6 +216,7 @@ pub fn getAtomData(self: ZigObject, macho_file: *MachO, atom: Atom, buffer: []u8
@memset(buffer, 0);
},
else => {
+ const sect = macho_file.sections.items(.header)[atom.out_n_sect];
const file_offset = sect.offset + atom.value;
const amt = try macho_file.base.file.?.preadAll(buffer, file_offset);
if (amt != buffer.len) return error.InputOutput;
@@ -192,102 +225,65 @@ pub fn getAtomData(self: ZigObject, macho_file: *MachO, atom: Atom, buffer: []u8
}
pub fn getAtomRelocs(self: *ZigObject, atom: Atom, macho_file: *MachO) []const Relocation {
- if (!atom.flags.relocs) return &[0]Relocation{};
- const extra = atom.getExtra(macho_file).?;
+ const extra = atom.getExtra(macho_file);
const relocs = self.relocs.items[extra.rel_index];
return relocs.items[0..extra.rel_count];
}
pub fn freeAtomRelocs(self: *ZigObject, atom: Atom, macho_file: *MachO) void {
- if (atom.flags.relocs) {
- const extra = atom.getExtra(macho_file).?;
- self.relocs.items[extra.rel_index].clearRetainingCapacity();
- }
+ const extra = atom.getExtra(macho_file);
+ self.relocs.items[extra.rel_index].clearRetainingCapacity();
}
-pub fn resolveSymbols(self: *ZigObject, macho_file: *MachO) void {
+pub fn resolveSymbols(self: *ZigObject, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
- for (self.symbols.items, 0..) |index, i| {
- const nlist_idx = @as(Symbol.Index, @intCast(i));
- const nlist = self.symtab.items(.nlist)[nlist_idx];
- const atom_index = self.symtab.items(.atom)[nlist_idx];
+ const gpa = macho_file.base.comp.gpa;
+ for (self.symtab.items(.nlist), self.symtab.items(.atom), self.globals.items, 0..) |nlist, atom_index, *global, i| {
if (!nlist.ext()) continue;
- if (nlist.undf() and !nlist.tentative()) continue;
if (nlist.sect()) {
- const atom = macho_file.getAtom(atom_index).?;
+ const atom = self.getAtom(atom_index).?;
if (!atom.flags.alive) continue;
}
- const symbol = macho_file.getSymbol(index);
+ const gop = try macho_file.resolver.getOrPut(gpa, .{
+ .index = @intCast(i),
+ .file = self.index,
+ }, macho_file);
+ if (!gop.found_existing) {
+ gop.ref.* = .{ .index = 0, .file = 0 };
+ }
+ global.* = gop.index;
+
+ if (nlist.undf() and !nlist.tentative()) continue;
+ if (gop.ref.getFile(macho_file) == null) {
+ gop.ref.* = .{ .index = @intCast(i), .file = self.index };
+ continue;
+ }
+
if (self.asFile().getSymbolRank(.{
.archive = false,
.weak = nlist.weakDef(),
.tentative = nlist.tentative(),
- }) < symbol.getSymbolRank(macho_file)) {
- const value = if (nlist.sect()) blk: {
- const atom = macho_file.getAtom(atom_index).?;
- break :blk nlist.n_value - atom.getInputAddress(macho_file);
- } else nlist.n_value;
- const out_n_sect = if (nlist.sect()) macho_file.getAtom(atom_index).?.out_n_sect else 0;
- symbol.value = value;
- symbol.atom = atom_index;
- symbol.out_n_sect = out_n_sect;
- symbol.nlist_idx = nlist_idx;
- symbol.file = self.index;
- symbol.flags.weak = nlist.weakDef();
- symbol.flags.abs = nlist.abs();
- symbol.flags.tentative = nlist.tentative();
- symbol.flags.weak_ref = false;
- symbol.flags.dyn_ref = nlist.n_desc & macho.REFERENCED_DYNAMICALLY != 0;
- symbol.flags.no_dead_strip = symbol.flags.no_dead_strip or nlist.noDeadStrip();
- // TODO: symbol.flags.interposable = macho_file.base.isDynLib() and macho_file.options.namespace == .flat and !nlist.pext();
- symbol.flags.interposable = false;
-
- if (nlist.sect() and
- macho_file.sections.items(.header)[nlist.n_sect - 1].type() == macho.S_THREAD_LOCAL_VARIABLES)
- {
- symbol.flags.tlv = true;
- }
- }
-
- // Regardless of who the winner is, we still merge symbol visibility here.
- if (nlist.pext() or (nlist.weakDef() and nlist.weakRef())) {
- if (symbol.visibility != .global) {
- symbol.visibility = .hidden;
- }
- } else {
- symbol.visibility = .global;
+ }) < gop.ref.getSymbol(macho_file).?.getSymbolRank(macho_file)) {
+ gop.ref.* = .{ .index = @intCast(i), .file = self.index };
}
}
}
-pub fn resetGlobals(self: *ZigObject, macho_file: *MachO) void {
- for (self.symbols.items, 0..) |sym_index, nlist_idx| {
- if (!self.symtab.items(.nlist)[nlist_idx].ext()) continue;
- const sym = macho_file.getSymbol(sym_index);
- const name = sym.name;
- const global = sym.flags.global;
- const weak_ref = sym.flags.weak_ref;
- sym.* = .{};
- sym.name = name;
- sym.flags.global = global;
- sym.flags.weak_ref = weak_ref;
- }
-}
-
pub fn markLive(self: *ZigObject, macho_file: *MachO) void {
const tracy = trace(@src());
defer tracy.end();
- for (self.symbols.items, 0..) |index, nlist_idx| {
- const nlist = self.symtab.items(.nlist)[nlist_idx];
+ for (0..self.symbols.items.len) |i| {
+ const nlist = self.symtab.items(.nlist)[i];
if (!nlist.ext()) continue;
- const sym = macho_file.getSymbol(index);
- const file = sym.getFile(macho_file) orelse continue;
+ const ref = self.getSymbolRef(@intCast(i), macho_file);
+ const file = ref.getFile(macho_file) orelse continue;
+ const sym = ref.getSymbol(macho_file).?;
const should_keep = nlist.undf() or (nlist.tentative() and !sym.flags.tentative);
if (should_keep and file == .object and !file.object.alive) {
file.object.alive = true;
@@ -296,20 +292,18 @@ pub fn markLive(self: *ZigObject, macho_file: *MachO) void {
}
}
-pub fn checkDuplicates(self: *ZigObject, dupes: anytype, macho_file: *MachO) !void {
- for (self.symbols.items, 0..) |index, nlist_idx| {
- const sym = macho_file.getSymbol(index);
- if (sym.visibility != .global) continue;
- const file = sym.getFile(macho_file) orelse continue;
- if (file.getIndex() == self.index) continue;
+pub fn mergeSymbolVisibility(self: *ZigObject, macho_file: *MachO) void {
+ const tracy = trace(@src());
+ defer tracy.end();
- const nlist = self.symtab.items(.nlist)[nlist_idx];
- if (!nlist.undf() and !nlist.tentative() and !(nlist.weakDef() or nlist.pext())) {
- const gop = try dupes.getOrPut(index);
- if (!gop.found_existing) {
- gop.value_ptr.* = .{};
- }
- try gop.value_ptr.append(macho_file.base.comp.gpa, self.index);
+ for (self.symbols.items, 0..) |sym, i| {
+ const ref = self.getSymbolRef(@intCast(i), macho_file);
+ const global = ref.getSymbol(macho_file) orelse continue;
+ if (sym.visibility.rank() < global.visibility.rank()) {
+ global.visibility = sym.visibility;
+ }
+ if (sym.flags.weak_ref) {
+ global.flags.weak_ref = true;
}
}
}
@@ -331,7 +325,9 @@ pub fn dedupLiterals(self: *ZigObject, lp: MachO.LiteralPool, macho_file: *MachO
/// This is just a temporary helper function that allows us to re-read what we wrote to file into a buffer.
/// We need this so that we can write to an archive.
/// TODO implement writing ZigObject data directly to a buffer instead.
-pub fn readFileContents(self: *ZigObject, size: usize, macho_file: *MachO) !void {
+pub fn readFileContents(self: *ZigObject, macho_file: *MachO) !void {
+ // Size of the output object file is always the offset + size of the strtab
+ const size = macho_file.symtab_cmd.stroff + macho_file.symtab_cmd.strsize;
const gpa = macho_file.base.comp.gpa;
try self.data.resize(gpa, size);
const amt = try macho_file.base.file.?.preadAll(self.data.items, 0);
@@ -340,9 +336,9 @@ pub fn readFileContents(self: *ZigObject, size: usize, macho_file: *MachO) !void
pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, macho_file: *MachO) error{OutOfMemory}!void {
const gpa = macho_file.base.comp.gpa;
- for (self.symbols.items) |sym_index| {
- const sym = macho_file.getSymbol(sym_index);
- const file = sym.getFile(macho_file).?;
+ for (self.symbols.items, 0..) |sym, i| {
+ const ref = self.getSymbolRef(@intCast(i), macho_file);
+ const file = ref.getFile(macho_file).?;
assert(file.getIndex() == self.index);
if (!sym.flags.@"export") continue;
const off = try ar_symtab.strtab.insert(gpa, sym.getName(macho_file));
@@ -362,9 +358,39 @@ pub fn writeAr(self: ZigObject, ar_format: Archive.Format, writer: anytype) !voi
try writer.writeAll(self.data.items);
}
+pub fn claimUnresolved(self: *ZigObject, macho_file: *MachO) void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ for (self.symbols.items, 0..) |*sym, i| {
+ const nlist = self.symtab.items(.nlist)[i];
+ if (!nlist.ext()) continue;
+ if (!nlist.undf()) continue;
+
+ if (self.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue;
+
+ const is_import = switch (macho_file.undefined_treatment) {
+ .@"error" => false,
+ .warn, .suppress => nlist.weakRef(),
+ .dynamic_lookup => true,
+ };
+ if (is_import) {
+ sym.value = 0;
+ sym.atom_ref = .{ .index = 0, .file = 0 };
+ sym.flags.weak = false;
+ sym.flags.weak_ref = nlist.weakRef();
+ sym.flags.import = is_import;
+ sym.visibility = .global;
+
+ const idx = self.globals.items[i];
+ macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = self.index };
+ }
+ }
+}
+
pub fn scanRelocs(self: *ZigObject, macho_file: *MachO) !void {
- for (self.atoms.items) |atom_index| {
- const atom = macho_file.getAtom(atom_index) orelse continue;
+ for (self.getAtoms()) |atom_index| {
+ const atom = self.getAtom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
const sect = atom.getInputSection(macho_file);
if (sect.isZerofill()) continue;
@@ -372,25 +398,168 @@ pub fn scanRelocs(self: *ZigObject, macho_file: *MachO) !void {
}
}
-pub fn calcSymtabSize(self: *ZigObject, macho_file: *MachO) !void {
+pub fn resolveRelocs(self: *ZigObject, macho_file: *MachO) !void {
+ const gpa = macho_file.base.comp.gpa;
+ var has_error = false;
+ for (self.getAtoms()) |atom_index| {
+ const atom = self.getAtom(atom_index) orelse continue;
+ if (!atom.flags.alive) continue;
+ const sect = &macho_file.sections.items(.header)[atom.out_n_sect];
+ if (sect.isZerofill()) continue;
+ if (!macho_file.isZigSection(atom.out_n_sect)) continue; // Non-Zig sections are handled separately
+ if (atom.getRelocs(macho_file).len == 0) continue;
+ // TODO: we will resolve and write ZigObject's TLS data twice:
+ // once here, and once in writeAtoms
+ const atom_size = std.math.cast(usize, atom.size) orelse return error.Overflow;
+ const code = try gpa.alloc(u8, atom_size);
+ defer gpa.free(code);
+ self.getAtomData(macho_file, atom.*, code) catch |err| {
+ switch (err) {
+ error.InputOutput => {
+ try macho_file.reportUnexpectedError("fetching code for '{s}' failed", .{
+ atom.getName(macho_file),
+ });
+ },
+ else => |e| {
+ try macho_file.reportUnexpectedError("unexpected error while fetching code for '{s}': {s}", .{
+ atom.getName(macho_file),
+ @errorName(e),
+ });
+ },
+ }
+ has_error = true;
+ continue;
+ };
+ const file_offset = sect.offset + atom.value;
+ atom.resolveRelocs(macho_file, code) catch |err| {
+ switch (err) {
+ error.ResolveFailed => {},
+ else => |e| {
+ try macho_file.reportUnexpectedError("unexpected error while resolving relocations: {s}", .{@errorName(e)});
+ },
+ }
+ has_error = true;
+ continue;
+ };
+ try macho_file.base.file.?.pwriteAll(code, file_offset);
+ }
+
+ if (has_error) return error.ResolveFailed;
+}
+
+pub fn calcNumRelocs(self: *ZigObject, macho_file: *MachO) void {
+ for (self.getAtoms()) |atom_index| {
+ const atom = self.getAtom(atom_index) orelse continue;
+ if (!atom.flags.alive) continue;
+ const header = &macho_file.sections.items(.header)[atom.out_n_sect];
+ if (header.isZerofill()) continue;
+ if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;
+ const nreloc = atom.calcNumRelocs(macho_file);
+ atom.addExtra(.{ .rel_out_index = header.nreloc, .rel_out_count = nreloc }, macho_file);
+ header.nreloc += nreloc;
+ }
+}
+
+pub fn writeRelocs(self: *ZigObject, macho_file: *MachO) !void {
+ const gpa = macho_file.base.comp.gpa;
+
+ for (self.getAtoms()) |atom_index| {
+ const atom = self.getAtom(atom_index) orelse continue;
+ if (!atom.flags.alive) continue;
+ const header = macho_file.sections.items(.header)[atom.out_n_sect];
+ const relocs = macho_file.sections.items(.relocs)[atom.out_n_sect].items;
+ if (header.isZerofill()) continue;
+ if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;
+ if (atom.getRelocs(macho_file).len == 0) continue;
+ const extra = atom.getExtra(macho_file);
+ const atom_size = std.math.cast(usize, atom.size) orelse return error.Overflow;
+ const code = try gpa.alloc(u8, atom_size);
+ defer gpa.free(code);
+ self.getAtomData(macho_file, atom.*, code) catch |err| switch (err) {
+ error.InputOutput => {
+ try macho_file.reportUnexpectedError("fetching code for '{s}' failed", .{
+ atom.getName(macho_file),
+ });
+ return error.FlushFailure;
+ },
+ else => |e| {
+ try macho_file.reportUnexpectedError("unexpected error while fetching code for '{s}': {s}", .{
+ atom.getName(macho_file),
+ @errorName(e),
+ });
+ return error.FlushFailure;
+ },
+ };
+ const file_offset = header.offset + atom.value;
+ try atom.writeRelocs(macho_file, code, relocs[extra.rel_out_index..][0..extra.rel_out_count]);
+ try macho_file.base.file.?.pwriteAll(code, file_offset);
+ }
+}
+
+// TODO we need this because not everything gets written out incrementally.
+// For example, TLS data gets written out via traditional route.
+// Is there any better way of handling this?
+pub fn writeAtomsRelocatable(self: *ZigObject, macho_file: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ for (self.getAtoms()) |atom_index| {
+ const atom = self.getAtom(atom_index) orelse continue;
+ if (!atom.flags.alive) continue;
+ const sect = atom.getInputSection(macho_file);
+ if (sect.isZerofill()) continue;
+ if (macho_file.isZigSection(atom.out_n_sect)) continue;
+ if (atom.getRelocs(macho_file).len == 0) continue;
+ const off = std.math.cast(usize, atom.value) orelse return error.Overflow;
+ const size = std.math.cast(usize, atom.size) orelse return error.Overflow;
+ const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;
+ try self.getAtomData(macho_file, atom.*, buffer[off..][0..size]);
+ const relocs = macho_file.sections.items(.relocs)[atom.out_n_sect].items;
+ const extra = atom.getExtra(macho_file);
+ try atom.writeRelocs(macho_file, buffer[off..][0..size], relocs[extra.rel_out_index..][0..extra.rel_out_count]);
+ }
+}
+
+// TODO we need this because not everything gets written out incrementally.
+// For example, TLS data gets written out via traditional route.
+// Is there any better way of handling this?
+pub fn writeAtoms(self: *ZigObject, macho_file: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ for (self.getAtoms()) |atom_index| {
+ const atom = self.getAtom(atom_index) orelse continue;
+ if (!atom.flags.alive) continue;
+ const sect = atom.getInputSection(macho_file);
+ if (sect.isZerofill()) continue;
+ if (macho_file.isZigSection(atom.out_n_sect)) continue;
+ const off = std.math.cast(usize, atom.value) orelse return error.Overflow;
+ const size = std.math.cast(usize, atom.size) orelse return error.Overflow;
+ const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;
+ try self.getAtomData(macho_file, atom.*, buffer[off..][0..size]);
+ try atom.resolveRelocs(macho_file, buffer[off..][0..size]);
+ }
+}
+
+pub fn calcSymtabSize(self: *ZigObject, macho_file: *MachO) void {
const tracy = trace(@src());
defer tracy.end();
- for (self.symbols.items) |sym_index| {
- const sym = macho_file.getSymbol(sym_index);
- const file = sym.getFile(macho_file) orelse continue;
+ for (self.symbols.items, 0..) |*sym, i| {
+ const ref = self.getSymbolRef(@intCast(i), macho_file);
+ const file = ref.getFile(macho_file) orelse continue;
if (file.getIndex() != self.index) continue;
if (sym.getAtom(macho_file)) |atom| if (!atom.flags.alive) continue;
sym.flags.output_symtab = true;
if (sym.isLocal()) {
- try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file);
+ sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file);
self.output_symtab_ctx.nlocals += 1;
} else if (sym.flags.@"export") {
- try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nexports }, macho_file);
+ sym.addExtra(.{ .symtab = self.output_symtab_ctx.nexports }, macho_file);
self.output_symtab_ctx.nexports += 1;
} else {
assert(sym.flags.import);
- try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file);
+ sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file);
self.output_symtab_ctx.nimports += 1;
}
self.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(macho_file).len + 1));
@@ -401,17 +570,20 @@ pub fn writeSymtab(self: ZigObject, macho_file: *MachO, ctx: anytype) void {
const tracy = trace(@src());
defer tracy.end();
- for (self.symbols.items) |sym_index| {
- const sym = macho_file.getSymbol(sym_index);
- const file = sym.getFile(macho_file) orelse continue;
+ var n_strx = self.output_symtab_ctx.stroff;
+ for (self.symbols.items, 0..) |sym, i| {
+ const ref = self.getSymbolRef(@intCast(i), macho_file);
+ const file = ref.getFile(macho_file) orelse continue;
if (file.getIndex() != self.index) continue;
const idx = sym.getOutputSymtabIndex(macho_file) orelse continue;
- const n_strx = @as(u32, @intCast(ctx.strtab.items.len));
- ctx.strtab.appendSliceAssumeCapacity(sym.getName(macho_file));
- ctx.strtab.appendAssumeCapacity(0);
const out_sym = &ctx.symtab.items[idx];
out_sym.n_strx = n_strx;
sym.setOutputSym(macho_file, out_sym);
+ const name = sym.getName(macho_file);
+ @memcpy(ctx.strtab.items[n_strx..][0..name.len], name);
+ n_strx += @intCast(name.len);
+ ctx.strtab.items[n_strx] = 0;
+ n_strx += 1;
}
}
@@ -524,9 +696,9 @@ pub fn getDeclVAddr(
reloc_info: link.File.RelocInfo,
) !u64 {
const sym_index = try self.getOrCreateMetadataForDecl(macho_file, decl_index);
- const sym = macho_file.getSymbol(sym_index);
+ const sym = self.symbols.items[sym_index];
const vaddr = sym.getAddress(.{}, macho_file);
- const parent_atom = macho_file.getSymbol(reloc_info.parent_atom_index).getAtom(macho_file).?;
+ const parent_atom = self.symbols.items[reloc_info.parent_atom_index].getAtom(macho_file).?;
try parent_atom.addReloc(macho_file, .{
.tag = .@"extern",
.offset = @intCast(reloc_info.offset),
@@ -550,9 +722,9 @@ pub fn getAnonDeclVAddr(
reloc_info: link.File.RelocInfo,
) !u64 {
const sym_index = self.anon_decls.get(decl_val).?.symbol_index;
- const sym = macho_file.getSymbol(sym_index);
+ const sym = self.symbols.items[sym_index];
const vaddr = sym.getAddress(.{}, macho_file);
- const parent_atom = macho_file.getSymbol(reloc_info.parent_atom_index).getAtom(macho_file).?;
+ const parent_atom = self.symbols.items[reloc_info.parent_atom_index].getAtom(macho_file).?;
try parent_atom.addReloc(macho_file, .{
.tag = .@"extern",
.offset = @intCast(reloc_info.offset),
@@ -585,7 +757,7 @@ pub fn lowerAnonDecl(
else => explicit_alignment,
};
if (self.anon_decls.get(decl_val)) |metadata| {
- const existing_alignment = macho_file.getSymbol(metadata.symbol_index).getAtom(macho_file).?.alignment;
+ const existing_alignment = self.symbols.items[metadata.symbol_index].getAtom(macho_file).?.alignment;
if (decl_alignment.order(existing_alignment).compare(.lte))
return .ok;
}
@@ -629,13 +801,10 @@ fn freeUnnamedConsts(self: *ZigObject, macho_file: *MachO, decl_index: InternPoo
}
fn freeDeclMetadata(self: *ZigObject, macho_file: *MachO, sym_index: Symbol.Index) void {
- _ = self;
- const gpa = macho_file.base.comp.gpa;
- const sym = macho_file.getSymbol(sym_index);
+ const sym = self.symbols.items[sym_index];
sym.getAtom(macho_file).?.free(macho_file);
log.debug("adding %{d} to local symbols free list", .{sym_index});
- macho_file.symbols_free_list.append(gpa, sym_index) catch {};
- macho_file.symbols.items[sym_index] = .{};
+ // TODO redo this
// TODO free GOT entry here
}
@@ -676,7 +845,7 @@ pub fn updateFunc(
const sym_index = try self.getOrCreateMetadataForDecl(macho_file, decl_index);
self.freeUnnamedConsts(macho_file, decl_index);
- macho_file.getSymbol(sym_index).getAtom(macho_file).?.freeRelocs(macho_file);
+ self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file);
var code_buffer = std.ArrayList(u8).init(gpa);
defer code_buffer.deinit();
@@ -709,7 +878,7 @@ pub fn updateFunc(
try self.updateDeclCode(macho_file, pt, decl_index, sym_index, sect_index, code);
if (decl_state) |*ds| {
- const sym = macho_file.getSymbol(sym_index);
+ const sym = self.symbols.items[sym_index];
try self.dwarf.?.commitDeclState(
pt,
decl_index,
@@ -744,13 +913,13 @@ pub fn updateDecl(
const name = decl.name.toSlice(&mod.intern_pool);
const lib_name = variable.lib_name.toSlice(&mod.intern_pool);
const index = try self.getGlobalSymbol(macho_file, name, lib_name);
- const actual_index = self.symbols.items[index];
- macho_file.getSymbol(actual_index).flags.needs_got = true;
+ const sym = &self.symbols.items[index];
+ sym.flags.needs_got = true;
return;
}
const sym_index = try self.getOrCreateMetadataForDecl(macho_file, decl_index);
- macho_file.getSymbol(sym_index).getAtom(macho_file).?.freeRelocs(macho_file);
+ self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file);
const gpa = macho_file.base.comp.gpa;
var code_buffer = std.ArrayList(u8).init(gpa);
@@ -773,19 +942,16 @@ pub fn updateDecl(
return;
},
};
- const sect_index = try self.getDeclOutputSection(macho_file, decl, code);
- const is_threadlocal = switch (macho_file.sections.items(.header)[sect_index].type()) {
- macho.S_THREAD_LOCAL_ZEROFILL, macho.S_THREAD_LOCAL_REGULAR => true,
- else => false,
- };
- if (is_threadlocal) {
+ if (isThreadlocal(macho_file, decl_index)) {
+ const sect_index = try self.getDeclOutputSection(macho_file, decl, code);
try self.updateTlv(macho_file, pt, decl_index, sym_index, sect_index, code);
} else {
+ const sect_index = try self.getDeclOutputSection(macho_file, decl, code);
try self.updateDeclCode(macho_file, pt, decl_index, sym_index, sect_index, code);
}
if (decl_state) |*ds| {
- const sym = macho_file.getSymbol(sym_index);
+ const sym = self.symbols.items[sym_index];
try self.dwarf.?.commitDeclState(
pt,
decl_index,
@@ -817,14 +983,16 @@ fn updateDeclCode(
const required_alignment = decl.getAlignment(pt);
const sect = &macho_file.sections.items(.header)[sect_index];
- const sym = macho_file.getSymbol(sym_index);
+ const sym = &self.symbols.items[sym_index];
const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
const atom = sym.getAtom(macho_file).?;
sym.out_n_sect = sect_index;
atom.out_n_sect = sect_index;
- sym.name = try self.strtab.insert(gpa, decl.fqn.toSlice(ip));
+ const sym_name = try std.fmt.allocPrintZ(gpa, "_{s}", .{decl.fqn.toSlice(ip)});
+ defer gpa.free(sym_name);
+ sym.name = try self.strtab.insert(gpa, sym_name);
atom.flags.alive = true;
atom.name = sym.name;
nlist.n_strx = sym.name;
@@ -851,13 +1019,13 @@ fn updateDeclCode(
if (!macho_file.base.isRelocatable()) {
log.debug(" (updating offset table entry)", .{});
assert(sym.flags.has_zig_got);
- const extra = sym.getExtra(macho_file).?;
+ const extra = sym.getExtra(macho_file);
try macho_file.zig_got.writeOne(macho_file, extra.zig_got);
}
}
} else if (code.len < old_size) {
atom.shrink(macho_file);
- } else if (macho_file.getAtom(atom.next_index) == null) {
+ } else if (self.getAtom(atom.next_index) == null) {
const needed_size = atom.value + code.len;
sect.size = needed_size;
}
@@ -922,31 +1090,22 @@ fn createTlvInitializer(
const gpa = macho_file.base.comp.gpa;
const sym_name = try std.fmt.allocPrint(gpa, "{s}$tlv$init", .{name});
defer gpa.free(sym_name);
+ const off = try self.strtab.insert(gpa, sym_name);
- const sym_index = try self.addAtom(macho_file);
- const sym = macho_file.getSymbol(sym_index);
+ const sym_index = try self.newSymbolWithAtom(gpa, off, macho_file);
+ const sym = &self.symbols.items[sym_index];
const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
const atom = sym.getAtom(macho_file).?;
-
sym.out_n_sect = sect_index;
atom.out_n_sect = sect_index;
-
- sym.value = 0;
- sym.name = try self.strtab.insert(gpa, sym_name);
atom.flags.alive = true;
- atom.name = sym.name;
- nlist.n_strx = sym.name;
- nlist.n_sect = sect_index + 1;
- nlist.n_type = macho.N_SECT;
- nlist.n_value = 0;
- self.symtab.items(.size)[sym.nlist_idx] = code.len;
-
atom.alignment = alignment;
atom.size = code.len;
+ nlist.n_sect = sect_index + 1;
+ self.symtab.items(.size)[sym.nlist_idx] = code.len;
const slice = macho_file.sections.slice();
const header = slice.items(.header)[sect_index];
- const atoms = &slice.items(.atoms)[sect_index];
const gop = try self.tlv_initializers.getOrPut(gpa, atom.atom_index);
assert(!gop.found_existing); // TODO incremental updates
@@ -957,8 +1116,6 @@ fn createTlvInitializer(
gop.value_ptr.data = try gpa.dupe(u8, code);
}
- try atoms.append(gpa, atom.atom_index);
-
return sym_index;
}
@@ -971,7 +1128,7 @@ fn createTlvDescriptor(
) !void {
const gpa = macho_file.base.comp.gpa;
- const sym = macho_file.getSymbol(sym_index);
+ const sym = &self.symbols.items[sym_index];
const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
const atom = sym.getAtom(macho_file).?;
const alignment = Atom.Alignment.fromNonzeroByteUnits(@alignOf(u64));
@@ -1001,7 +1158,7 @@ fn createTlvDescriptor(
try atom.addReloc(macho_file, .{
.tag = .@"extern",
.offset = 0,
- .target = self.symbols.items[tlv_bootstrap_index],
+ .target = tlv_bootstrap_index,
.addend = 0,
.type = .unsigned,
.meta = .{
@@ -1021,11 +1178,9 @@ fn createTlvDescriptor(
.pcrel = false,
.has_subtractor = false,
.length = 3,
- .symbolnum = @intCast(macho_file.getSymbol(init_sym_index).nlist_idx),
+ .symbolnum = @intCast(init_sym_index),
},
});
-
- try macho_file.sections.items(.atoms)[sect_index].append(gpa, atom.atom_index);
}
fn getDeclOutputSection(
@@ -1116,8 +1271,8 @@ pub fn lowerUnnamedConst(
return error.CodegenFail;
},
};
- const sym = macho_file.getSymbol(sym_index);
- try unnamed_consts.append(gpa, sym.atom);
+ const sym = self.symbols.items[sym_index];
+ try unnamed_consts.append(gpa, sym.atom_ref.index);
return sym_index;
}
@@ -1141,7 +1296,8 @@ fn lowerConst(
var code_buffer = std.ArrayList(u8).init(gpa);
defer code_buffer.deinit();
- const sym_index = try self.addAtom(macho_file);
+ const name_str_index = try self.strtab.insert(gpa, name);
+ const sym_index = try self.newSymbolWithAtom(gpa, name_str_index, macho_file);
const res = try codegen.generateSymbol(&macho_file.base, pt, src_loc, val, &code_buffer, .{
.none = {},
@@ -1153,20 +1309,15 @@ fn lowerConst(
.fail => |em| return .{ .fail = em },
};
- const sym = macho_file.getSymbol(sym_index);
- const name_str_index = try self.strtab.insert(gpa, name);
- sym.name = name_str_index;
+ const sym = &self.symbols.items[sym_index];
sym.out_n_sect = output_section_index;
const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
- nlist.n_strx = name_str_index;
- nlist.n_type = macho.N_SECT;
nlist.n_sect = output_section_index + 1;
self.symtab.items(.size)[sym.nlist_idx] = code.len;
const atom = sym.getAtom(macho_file).?;
atom.flags.alive = true;
- atom.name = name_str_index;
atom.alignment = required_alignment;
atom.size = code.len;
atom.out_n_sect = output_section_index;
@@ -1175,9 +1326,6 @@ fn lowerConst(
// TODO rename and re-audit this method
errdefer self.freeDeclMetadata(macho_file, sym_index);
- sym.value = 0;
- nlist.n_value = 0;
-
const sect = macho_file.sections.items(.header)[output_section_index];
const file_offset = sect.offset + atom.value;
try macho_file.base.file.?.pwriteAll(code, file_offset);
@@ -1219,7 +1367,7 @@ pub fn updateExports(
},
};
const sym_index = metadata.symbol_index;
- const nlist_idx = macho_file.getSymbol(sym_index).nlist_idx;
+ const nlist_idx = self.symbols.items[sym_index].nlist_idx;
const nlist = self.symtab.items(.nlist)[nlist_idx];
for (export_indices) |export_idx| {
@@ -1255,22 +1403,30 @@ pub fn updateExports(
break :blk global_nlist_index;
};
const global_nlist = &self.symtab.items(.nlist)[global_nlist_index];
+ const atom_index = self.symtab.items(.atom)[nlist_idx];
+ const global_sym = &self.symbols.items[global_nlist_index];
global_nlist.n_value = nlist.n_value;
global_nlist.n_sect = nlist.n_sect;
global_nlist.n_type = macho.N_EXT | macho.N_SECT;
self.symtab.items(.size)[global_nlist_index] = self.symtab.items(.size)[nlist_idx];
- self.symtab.items(.atom)[global_nlist_index] = self.symtab.items(.atom)[nlist_idx];
+ self.symtab.items(.atom)[global_nlist_index] = atom_index;
+ global_sym.atom_ref = .{ .index = atom_index, .file = self.index };
switch (exp.opts.linkage) {
.internal => {
// Symbol should be hidden, or in MachO lingo, private extern.
global_nlist.n_type |= macho.N_PEXT;
+ global_sym.visibility = .hidden;
+ },
+ .strong => {
+ global_sym.visibility = .global;
},
- .strong => {},
.weak => {
// Weak linkage is specified as part of n_desc field.
// Symbol's n_type is like for a symbol with strong linkage.
global_nlist.n_desc |= macho.N_WEAK_DEF;
+ global_sym.visibility = .global;
+ global_sym.flags.weak = true;
},
else => unreachable,
}
@@ -1323,7 +1479,7 @@ fn updateLazySymbol(
.code => macho_file.zig_text_sect_index.?,
.const_data => macho_file.zig_const_sect_index.?,
};
- const sym = macho_file.getSymbol(symbol_index);
+ const sym = &self.symbols.items[symbol_index];
sym.name = name_str_index;
sym.out_n_sect = output_section_index;
@@ -1383,12 +1539,12 @@ pub fn deleteExport(
const nlist = &self.symtab.items(.nlist)[nlist_index.*];
self.symtab.items(.size)[nlist_index.*] = 0;
_ = self.globals_lookup.remove(nlist.n_strx);
- const sym_index = macho_file.globals.get(nlist.n_strx).?;
- const sym = macho_file.getSymbol(sym_index);
- if (sym.file == self.index) {
- _ = macho_file.globals.swapRemove(nlist.n_strx);
- sym.* = .{};
- }
+ // TODO actually remove the export
+ // const sym_index = macho_file.globals.get(nlist.n_strx).?;
+ // const sym = &self.symbols.items[sym_index];
+ // if (sym.file == self.index) {
+ // sym.* = .{};
+ // }
nlist.* = MachO.null_sym;
}
@@ -1400,14 +1556,9 @@ pub fn getGlobalSymbol(self: *ZigObject, macho_file: *MachO, name: []const u8, l
const off = try self.strtab.insert(gpa, sym_name);
const lookup_gop = try self.globals_lookup.getOrPut(gpa, off);
if (!lookup_gop.found_existing) {
- const nlist_index = try self.addNlist(gpa);
- const nlist = &self.symtab.items(.nlist)[nlist_index];
- nlist.n_strx = off;
- nlist.n_type = macho.N_EXT;
- lookup_gop.value_ptr.* = nlist_index;
- const global_name_off = try macho_file.strings.insert(gpa, sym_name);
- const gop = try macho_file.getOrCreateGlobal(global_name_off);
- try self.symbols.append(gpa, gop.index);
+ const sym_index = try self.newSymbol(gpa, off, .{});
+ const sym = &self.symbols.items[sym_index];
+ lookup_gop.value_ptr.* = sym.nlist_idx;
}
return lookup_gop.value_ptr.*;
}
@@ -1420,17 +1571,11 @@ pub fn getOrCreateMetadataForDecl(
const gpa = macho_file.base.comp.gpa;
const gop = try self.decls.getOrPut(gpa, decl_index);
if (!gop.found_existing) {
- const any_non_single_threaded = macho_file.base.comp.config.any_non_single_threaded;
- const sym_index = try self.addAtom(macho_file);
- const mod = macho_file.base.comp.module.?;
- const decl = mod.declPtr(decl_index);
- const sym = macho_file.getSymbol(sym_index);
- if (decl.getOwnedVariable(mod)) |variable| {
- if (variable.is_threadlocal and any_non_single_threaded) {
- sym.flags.tlv = true;
- }
- }
- if (!sym.flags.tlv) {
+ const sym_index = try self.newSymbolWithAtom(gpa, 0, macho_file);
+ const sym = &self.symbols.items[sym_index];
+ if (isThreadlocal(macho_file, decl_index)) {
+ sym.flags.tlv = true;
+ } else {
sym.flags.needs_zig_got = true;
}
gop.value_ptr.* = .{ .symbol_index = sym_index };
@@ -1464,8 +1609,8 @@ pub fn getOrCreateMetadataForLazySymbol(
};
switch (metadata.state.*) {
.unused => {
- const symbol_index = try self.addAtom(macho_file);
- const sym = macho_file.getSymbol(symbol_index);
+ const symbol_index = try self.newSymbolWithAtom(gpa, 0, macho_file);
+ const sym = &self.symbols.items[symbol_index];
sym.flags.needs_zig_got = true;
metadata.symbol_index.* = symbol_index;
},
@@ -1479,6 +1624,144 @@ pub fn getOrCreateMetadataForLazySymbol(
return symbol_index;
}
+fn isThreadlocal(macho_file: *MachO, decl_index: InternPool.DeclIndex) bool {
+ const any_non_single_threaded = macho_file.base.comp.config.any_non_single_threaded;
+ const zcu = macho_file.base.comp.module.?;
+ const decl = zcu.declPtr(decl_index);
+ const variable = decl.getOwnedVariable(zcu) orelse return false;
+ return variable.is_threadlocal and any_non_single_threaded;
+}
+
+fn addAtom(self: *ZigObject, allocator: Allocator) !Atom.Index {
+ try self.atoms.ensureUnusedCapacity(allocator, 1);
+ try self.atoms_extra.ensureUnusedCapacity(allocator, 1);
+ return self.addAtomAssumeCapacity();
+}
+
+fn addAtomAssumeCapacity(self: *ZigObject) Atom.Index {
+ const atom_index: Atom.Index = @intCast(self.atoms.items.len);
+ const atom = self.atoms.addOneAssumeCapacity();
+ atom.* = .{
+ .file = self.index,
+ .atom_index = atom_index,
+ .extra = self.addAtomExtraAssumeCapacity(.{}),
+ };
+ return atom_index;
+}
+
+pub fn getAtom(self: *ZigObject, atom_index: Atom.Index) ?*Atom {
+ if (atom_index == 0) return null;
+ assert(atom_index < self.atoms.items.len);
+ return &self.atoms.items[atom_index];
+}
+
+pub fn getAtoms(self: *ZigObject) []const Atom.Index {
+ return self.atoms_indexes.items;
+}
+
+fn addAtomExtra(self: *ZigObject, allocator: Allocator, extra: Atom.Extra) !u32 {
+ const fields = @typeInfo(Atom.Extra).Struct.fields;
+ try self.atoms_extra.ensureUnusedCapacity(allocator, fields.len);
+ return self.addAtomExtraAssumeCapacity(extra);
+}
+
+fn addAtomExtraAssumeCapacity(self: *ZigObject, extra: Atom.Extra) u32 {
+ const index = @as(u32, @intCast(self.atoms_extra.items.len));
+ const fields = @typeInfo(Atom.Extra).Struct.fields;
+ inline for (fields) |field| {
+ self.atoms_extra.appendAssumeCapacity(switch (field.type) {
+ u32 => @field(extra, field.name),
+ else => @compileError("bad field type"),
+ });
+ }
+ return index;
+}
+
+pub fn getAtomExtra(self: ZigObject, index: u32) Atom.Extra {
+ const fields = @typeInfo(Atom.Extra).Struct.fields;
+ var i: usize = index;
+ var result: Atom.Extra = undefined;
+ inline for (fields) |field| {
+ @field(result, field.name) = switch (field.type) {
+ u32 => self.atoms_extra.items[i],
+ else => @compileError("bad field type"),
+ };
+ i += 1;
+ }
+ return result;
+}
+
+pub fn setAtomExtra(self: *ZigObject, index: u32, extra: Atom.Extra) void {
+ assert(index > 0);
+ const fields = @typeInfo(Atom.Extra).Struct.fields;
+ inline for (fields, 0..) |field, i| {
+ self.atoms_extra.items[index + i] = switch (field.type) {
+ u32 => @field(extra, field.name),
+ else => @compileError("bad field type"),
+ };
+ }
+}
+
+fn addSymbol(self: *ZigObject, allocator: Allocator) !Symbol.Index {
+ try self.symbols.ensureUnusedCapacity(allocator, 1);
+ return self.addSymbolAssumeCapacity();
+}
+
+fn addSymbolAssumeCapacity(self: *ZigObject) Symbol.Index {
+ const index: Symbol.Index = @intCast(self.symbols.items.len);
+ const symbol = self.symbols.addOneAssumeCapacity();
+ symbol.* = .{ .file = self.index };
+ return index;
+}
+
+pub fn getSymbolRef(self: ZigObject, index: Symbol.Index, macho_file: *MachO) MachO.Ref {
+ const global_index = self.globals.items[index];
+ if (macho_file.resolver.get(global_index)) |ref| return ref;
+ return .{ .index = index, .file = self.index };
+}
+
+pub fn addSymbolExtra(self: *ZigObject, allocator: Allocator, extra: Symbol.Extra) !u32 {
+ const fields = @typeInfo(Symbol.Extra).Struct.fields;
+ try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len);
+ return self.addSymbolExtraAssumeCapacity(extra);
+}
+
+fn addSymbolExtraAssumeCapacity(self: *ZigObject, extra: Symbol.Extra) u32 {
+ const index = @as(u32, @intCast(self.symbols_extra.items.len));
+ const fields = @typeInfo(Symbol.Extra).Struct.fields;
+ inline for (fields) |field| {
+ self.symbols_extra.appendAssumeCapacity(switch (field.type) {
+ u32 => @field(extra, field.name),
+ else => @compileError("bad field type"),
+ });
+ }
+ return index;
+}
+
+pub fn getSymbolExtra(self: ZigObject, index: u32) Symbol.Extra {
+ const fields = @typeInfo(Symbol.Extra).Struct.fields;
+ var i: usize = index;
+ var result: Symbol.Extra = undefined;
+ inline for (fields) |field| {
+ @field(result, field.name) = switch (field.type) {
+ u32 => self.symbols_extra.items[i],
+ else => @compileError("bad field type"),
+ };
+ i += 1;
+ }
+ return result;
+}
+
+pub fn setSymbolExtra(self: *ZigObject, index: u32, extra: Symbol.Extra) void {
+ const fields = @typeInfo(Symbol.Extra).Struct.fields;
+ inline for (fields, 0..) |field, i| {
+ self.symbols_extra.items[index + i] = switch (field.type) {
+ u32 => @field(extra, field.name),
+ else => @compileError("bad field type"),
+ };
+ }
+}
+
pub fn asFile(self: *ZigObject) File {
return .{ .zig_object = self };
}
@@ -1504,9 +1787,16 @@ fn formatSymtab(
_ = unused_fmt_string;
_ = options;
try writer.writeAll(" symbols\n");
- for (ctx.self.symbols.items) |index| {
- const sym = ctx.macho_file.getSymbol(index);
- try writer.print(" {}\n", .{sym.fmt(ctx.macho_file)});
+ const self = ctx.self;
+ const macho_file = ctx.macho_file;
+ for (self.symbols.items, 0..) |sym, i| {
+ const ref = self.getSymbolRef(@intCast(i), macho_file);
+ if (ref.getFile(macho_file) == null) {
+ // TODO any better way of handling this?
+ try writer.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});
+ } else {
+ try writer.print(" {}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
+ }
}
}
@@ -1525,10 +1815,12 @@ fn formatAtoms(
) !void {
_ = unused_fmt_string;
_ = options;
+ const self = ctx.self;
+ const macho_file = ctx.macho_file;
try writer.writeAll(" atoms\n");
- for (ctx.self.atoms.items) |atom_index| {
- const atom = ctx.macho_file.getAtom(atom_index) orelse continue;
- try writer.print(" {}\n", .{atom.fmt(ctx.macho_file)});
+ for (self.getAtoms()) |atom_index| {
+ const atom = self.getAtom(atom_index) orelse continue;
+ try writer.print(" {}\n", .{atom.fmt(macho_file)});
}
}
diff --git a/src/link/MachO/dead_strip.zig b/src/link/MachO/dead_strip.zig
@@ -17,16 +17,16 @@ pub fn gcAtoms(macho_file: *MachO) !void {
fn collectRoots(roots: *std.ArrayList(*Atom), objects: []const File.Index, macho_file: *MachO) !void {
for (objects) |index| {
const object = macho_file.getFile(index).?;
- for (object.getSymbols()) |sym_index| {
- const sym = macho_file.getSymbol(sym_index);
- const file = sym.getFile(macho_file) orelse continue;
+ for (object.getSymbols(), 0..) |*sym, i| {
+ const ref = object.getSymbolRef(@intCast(i), macho_file);
+ const file = ref.getFile(macho_file) orelse continue;
if (file.getIndex() != index) continue;
if (sym.flags.no_dead_strip or (macho_file.base.isDynLib() and sym.visibility == .global))
try markSymbol(sym, roots, macho_file);
}
for (object.getAtoms()) |atom_index| {
- const atom = macho_file.getAtom(atom_index).?;
+ const atom = object.getAtom(atom_index) orelse continue;
const isec = atom.getInputSection(macho_file);
switch (isec.type()) {
macho.S_MOD_INIT_FUNC_POINTERS,
@@ -41,8 +41,9 @@ fn collectRoots(roots: *std.ArrayList(*Atom), objects: []const File.Index, macho
}
for (macho_file.objects.items) |index| {
- for (macho_file.getFile(index).?.object.unwind_records.items) |cu_index| {
- const cu = macho_file.getUnwindRecord(cu_index);
+ const object = macho_file.getFile(index).?.object;
+ for (object.unwind_records_indexes.items) |cu_index| {
+ const cu = object.getUnwindRecord(cu_index);
if (!cu.alive) continue;
if (cu.getFde(macho_file)) |fde| {
if (fde.getCie(macho_file).getPersonality(macho_file)) |sym| try markSymbol(sym, roots, macho_file);
@@ -50,19 +51,27 @@ fn collectRoots(roots: *std.ArrayList(*Atom), objects: []const File.Index, macho
}
}
- for (macho_file.undefined_symbols.items) |sym_index| {
- const sym = macho_file.getSymbol(sym_index);
- try markSymbol(sym, roots, macho_file);
- }
+ if (macho_file.getInternalObject()) |obj| {
+ for (obj.force_undefined.items) |sym_index| {
+ const ref = obj.getSymbolRef(sym_index, macho_file);
+ if (ref.getFile(macho_file) != null) {
+ const sym = ref.getSymbol(macho_file).?;
+ try markSymbol(sym, roots, macho_file);
+ }
+ }
- for (&[_]?Symbol.Index{
- macho_file.entry_index,
- macho_file.dyld_stub_binder_index,
- macho_file.objc_msg_send_index,
- }) |index| {
- if (index) |idx| {
- const sym = macho_file.getSymbol(idx);
- try markSymbol(sym, roots, macho_file);
+ for (&[_]?Symbol.Index{
+ obj.entry_index,
+ obj.dyld_stub_binder_index,
+ obj.objc_msg_send_index,
+ }) |index| {
+ if (index) |idx| {
+ const ref = obj.getSymbolRef(idx, macho_file);
+ if (ref.getFile(macho_file) != null) {
+ const sym = ref.getSymbol(macho_file).?;
+ try markSymbol(sym, roots, macho_file);
+ }
+ }
}
}
}
@@ -88,8 +97,9 @@ fn mark(roots: []*Atom, objects: []const File.Index, macho_file: *MachO) void {
loop = false;
for (objects) |index| {
- for (macho_file.getFile(index).?.getAtoms()) |atom_index| {
- const atom = macho_file.getAtom(atom_index).?;
+ const file = macho_file.getFile(index).?;
+ for (file.getAtoms()) |atom_index| {
+ const atom = file.getAtom(atom_index) orelse continue;
const isec = atom.getInputSection(macho_file);
if (isec.isDontDeadStripIfReferencesLive() and
!(mem.eql(u8, isec.sectName(), "__eh_frame") or
@@ -119,16 +129,20 @@ fn markLive(atom: *Atom, macho_file: *MachO) void {
for (atom.getRelocs(macho_file)) |rel| {
const target_atom = switch (rel.tag) {
- .local => rel.getTargetAtom(macho_file),
- .@"extern" => rel.getTargetSymbol(macho_file).getAtom(macho_file),
+ .local => rel.getTargetAtom(atom.*, macho_file),
+ .@"extern" => blk: {
+ const ref = rel.getTargetSymbolRef(atom.*, macho_file);
+ break :blk if (ref.getSymbol(macho_file)) |sym| sym.getAtom(macho_file) else null;
+ },
};
if (target_atom) |ta| {
if (markAtom(ta)) markLive(ta, macho_file);
}
}
+ const file = atom.getFile(macho_file);
for (atom.getUnwindRecords(macho_file)) |cu_index| {
- const cu = macho_file.getUnwindRecord(cu_index);
+ const cu = file.object.getUnwindRecord(cu_index);
const cu_atom = cu.getAtom(macho_file);
if (markAtom(cu_atom)) markLive(cu_atom, macho_file);
@@ -149,8 +163,11 @@ fn markLive(atom: *Atom, macho_file: *MachO) void {
fn refersLive(atom: *Atom, macho_file: *MachO) bool {
for (atom.getRelocs(macho_file)) |rel| {
const target_atom = switch (rel.tag) {
- .local => rel.getTargetAtom(macho_file),
- .@"extern" => rel.getTargetSymbol(macho_file).getAtom(macho_file),
+ .local => rel.getTargetAtom(atom.*, macho_file),
+ .@"extern" => blk: {
+ const ref = rel.getTargetSymbolRef(atom.*, macho_file);
+ break :blk if (ref.getSymbol(macho_file)) |sym| sym.getAtom(macho_file) else null;
+ },
};
if (target_atom) |ta| {
if (ta.flags.alive) return true;
@@ -161,8 +178,9 @@ fn refersLive(atom: *Atom, macho_file: *MachO) bool {
fn prune(objects: []const File.Index, macho_file: *MachO) void {
for (objects) |index| {
- for (macho_file.getFile(index).?.getAtoms()) |atom_index| {
- const atom = macho_file.getAtom(atom_index).?;
+ const file = macho_file.getFile(index).?;
+ for (file.getAtoms()) |atom_index| {
+ const atom = file.getAtom(atom_index) orelse continue;
if (atom.flags.alive and !atom.flags.visited) {
atom.flags.alive = false;
atom.markUnwindRecordsDead(macho_file);
diff --git a/src/link/MachO/dyld_info/Rebase.zig b/src/link/MachO/dyld_info/Rebase.zig
@@ -1,14 +1,3 @@
-const Rebase = @This();
-
-const std = @import("std");
-const assert = std.debug.assert;
-const leb = std.leb;
-const log = std.log.scoped(.link_dyld_info);
-const macho = std.macho;
-const testing = std.testing;
-
-const Allocator = std.mem.Allocator;
-
entries: std.ArrayListUnmanaged(Entry) = .{},
buffer: std.ArrayListUnmanaged(u8) = .{},
@@ -30,11 +19,107 @@ pub fn deinit(rebase: *Rebase, gpa: Allocator) void {
rebase.buffer.deinit(gpa);
}
-pub fn size(rebase: Rebase) u64 {
- return @as(u64, @intCast(rebase.buffer.items.len));
+pub fn updateSize(rebase: *Rebase, macho_file: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const gpa = macho_file.base.comp.gpa;
+
+ var objects = try std.ArrayList(File.Index).initCapacity(gpa, macho_file.objects.items.len + 2);
+ defer objects.deinit();
+ objects.appendSliceAssumeCapacity(macho_file.objects.items);
+ if (macho_file.getZigObject()) |obj| objects.appendAssumeCapacity(obj.index);
+ if (macho_file.getInternalObject()) |obj| objects.appendAssumeCapacity(obj.index);
+
+ for (objects.items) |index| {
+ const file = macho_file.getFile(index).?;
+ for (file.getAtoms()) |atom_index| {
+ const atom = file.getAtom(atom_index) orelse continue;
+ if (!atom.flags.alive) continue;
+ if (atom.getInputSection(macho_file).isZerofill()) continue;
+ const atom_addr = atom.getAddress(macho_file);
+ const seg_id = macho_file.sections.items(.segment_id)[atom.out_n_sect];
+ const seg = macho_file.segments.items[seg_id];
+ for (atom.getRelocs(macho_file)) |rel| {
+ if (rel.type != .unsigned or rel.meta.length != 3) continue;
+ if (rel.tag == .@"extern") {
+ const sym = rel.getTargetSymbol(atom.*, macho_file);
+ if (sym.isTlvInit(macho_file)) continue;
+ if (sym.flags.import) continue;
+ }
+ const rel_offset = rel.offset - atom.off;
+ try rebase.entries.append(gpa, .{
+ .offset = atom_addr + rel_offset - seg.vmaddr,
+ .segment_id = seg_id,
+ });
+ }
+ }
+ }
+
+ if (macho_file.zig_got_sect_index) |sid| {
+ const seg_id = macho_file.sections.items(.segment_id)[sid];
+ const seg = macho_file.segments.items[seg_id];
+ for (0..macho_file.zig_got.entries.items.len) |idx| {
+ const addr = macho_file.zig_got.entryAddress(@intCast(idx), macho_file);
+ try rebase.entries.append(gpa, .{
+ .offset = addr - seg.vmaddr,
+ .segment_id = seg_id,
+ });
+ }
+ }
+
+ if (macho_file.got_sect_index) |sid| {
+ const seg_id = macho_file.sections.items(.segment_id)[sid];
+ const seg = macho_file.segments.items[seg_id];
+ for (macho_file.got.symbols.items, 0..) |ref, idx| {
+ const sym = ref.getSymbol(macho_file).?;
+ const addr = macho_file.got.getAddress(@intCast(idx), macho_file);
+ if (!sym.flags.import) {
+ try rebase.entries.append(gpa, .{
+ .offset = addr - seg.vmaddr,
+ .segment_id = seg_id,
+ });
+ }
+ }
+ }
+
+ if (macho_file.la_symbol_ptr_sect_index) |sid| {
+ const sect = macho_file.sections.items(.header)[sid];
+ const seg_id = macho_file.sections.items(.segment_id)[sid];
+ const seg = macho_file.segments.items[seg_id];
+ for (macho_file.stubs.symbols.items, 0..) |ref, idx| {
+ const sym = ref.getSymbol(macho_file).?;
+ const addr = sect.addr + idx * @sizeOf(u64);
+ const rebase_entry = Rebase.Entry{
+ .offset = addr - seg.vmaddr,
+ .segment_id = seg_id,
+ };
+ if ((sym.flags.import and !sym.flags.weak) or !sym.flags.import) {
+ try rebase.entries.append(gpa, rebase_entry);
+ }
+ }
+ }
+
+ if (macho_file.tlv_ptr_sect_index) |sid| {
+ const seg_id = macho_file.sections.items(.segment_id)[sid];
+ const seg = macho_file.segments.items[seg_id];
+ for (macho_file.tlv_ptr.symbols.items, 0..) |ref, idx| {
+ const sym = ref.getSymbol(macho_file).?;
+ const addr = macho_file.tlv_ptr.getAddress(@intCast(idx), macho_file);
+ if (!sym.flags.import) {
+ try rebase.entries.append(gpa, .{
+ .offset = addr - seg.vmaddr,
+ .segment_id = seg_id,
+ });
+ }
+ }
+ }
+
+ try rebase.finalize(gpa);
+ macho_file.dyld_info_cmd.rebase_size = mem.alignForward(u32, @intCast(rebase.buffer.items.len), @alignOf(u64));
}
-pub fn finalize(rebase: *Rebase, gpa: Allocator) !void {
+fn finalize(rebase: *Rebase, gpa: Allocator) !void {
if (rebase.entries.items.len == 0) return;
const writer = rebase.buffer.writer(gpa);
@@ -198,7 +283,6 @@ fn done(writer: anytype) !void {
}
pub fn write(rebase: Rebase, writer: anytype) !void {
- if (rebase.size() == 0) return;
try writer.writeAll(rebase.buffer.items);
}
@@ -574,3 +658,17 @@ test "rebase - composite" {
macho.REBASE_OPCODE_DONE,
}, rebase.buffer.items);
}
+
+const std = @import("std");
+const assert = std.debug.assert;
+const leb = std.leb;
+const log = std.log.scoped(.link_dyld_info);
+const macho = std.macho;
+const mem = std.mem;
+const testing = std.testing;
+const trace = @import("../../../tracy.zig").trace;
+
+const Allocator = mem.Allocator;
+const File = @import("../file.zig").File;
+const MachO = @import("../../MachO.zig");
+const Rebase = @This();
diff --git a/src/link/MachO/dyld_info/Trie.zig b/src/link/MachO/dyld_info/Trie.zig
@@ -28,463 +28,310 @@
//! After the optional exported symbol information is a byte of how many edges (0-255) that
//! this node has leaving it, followed by each edge. Each edge is a zero terminated UTF8 of
//! the addition chars in the symbol, followed by a uleb128 offset for the node that edge points to.
-const Trie = @This();
-
-const std = @import("std");
-const mem = std.mem;
-const leb = std.leb;
-const log = std.log.scoped(.macho);
-const macho = std.macho;
-const testing = std.testing;
-const assert = std.debug.assert;
-const Allocator = mem.Allocator;
-
-pub const Node = struct {
- base: *Trie,
-
- /// Terminal info associated with this node.
- /// If this node is not a terminal node, info is null.
- terminal_info: ?struct {
- /// Export flags associated with this exported symbol.
- export_flags: u64,
- /// VM address offset wrt to the section this symbol is defined against.
- vmaddr_offset: u64,
- } = null,
-
- /// Offset of this node in the trie output byte stream.
- trie_offset: ?u64 = null,
-
- /// List of all edges originating from this node.
- edges: std.ArrayListUnmanaged(Edge) = .{},
-
- node_dirty: bool = true,
-
- /// Edge connecting to nodes in the trie.
- pub const Edge = struct {
- from: *Node,
- to: *Node,
- label: []u8,
-
- fn deinit(self: *Edge, allocator: Allocator) void {
- self.to.deinit(allocator);
- allocator.destroy(self.to);
- allocator.free(self.label);
- self.from = undefined;
- self.to = undefined;
- self.label = undefined;
- }
- };
-
- fn deinit(self: *Node, allocator: Allocator) void {
- for (self.edges.items) |*edge| {
- edge.deinit(allocator);
- }
- self.edges.deinit(allocator);
- }
-
- /// Inserts a new node starting from `self`.
- fn put(self: *Node, allocator: Allocator, label: []const u8) !*Node {
- // Check for match with edges from this node.
- for (self.edges.items) |*edge| {
- const match = mem.indexOfDiff(u8, edge.label, label) orelse return edge.to;
- if (match == 0) continue;
- if (match == edge.label.len) return edge.to.put(allocator, label[match..]);
-
- // Found a match, need to splice up nodes.
- // From: A -> B
- // To: A -> C -> B
- const mid = try allocator.create(Node);
- mid.* = .{ .base = self.base };
- const to_label = try allocator.dupe(u8, edge.label[match..]);
- allocator.free(edge.label);
- const to_node = edge.to;
- edge.to = mid;
- edge.label = try allocator.dupe(u8, label[0..match]);
- self.base.node_count += 1;
-
- try mid.edges.append(allocator, .{
- .from = mid,
- .to = to_node,
- .label = to_label,
- });
-
- return if (match == label.len) mid else mid.put(allocator, label[match..]);
- }
-
- // Add a new node.
- const node = try allocator.create(Node);
- node.* = .{ .base = self.base };
- self.base.node_count += 1;
- try self.edges.append(allocator, .{
- .from = self,
- .to = node,
- .label = try allocator.dupe(u8, label),
- });
-
- return node;
- }
-
- /// Recursively parses the node from the input byte stream.
- fn read(self: *Node, allocator: Allocator, reader: anytype) Trie.ReadError!usize {
- self.node_dirty = true;
- const trie_offset = try reader.context.getPos();
- self.trie_offset = trie_offset;
-
- var nread: usize = 0;
-
- const node_size = try leb.readUleb128(u64, reader);
- if (node_size > 0) {
- const export_flags = try leb.readUleb128(u64, reader);
- // TODO Parse special flags.
- assert(export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and
- export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);
-
- const vmaddr_offset = try leb.readUleb128(u64, reader);
-
- self.terminal_info = .{
- .export_flags = export_flags,
- .vmaddr_offset = vmaddr_offset,
- };
- }
-
- const nedges = try reader.readByte();
- self.base.node_count += nedges;
-
- nread += (try reader.context.getPos()) - trie_offset;
-
- var i: usize = 0;
- while (i < nedges) : (i += 1) {
- const edge_start_pos = try reader.context.getPos();
-
- const label = blk: {
- var label_buf = std.ArrayList(u8).init(allocator);
- while (true) {
- const next = try reader.readByte();
- if (next == @as(u8, 0))
- break;
- try label_buf.append(next);
- }
- break :blk try label_buf.toOwnedSlice();
- };
-
- const seek_to = try leb.readUleb128(u64, reader);
- const return_pos = try reader.context.getPos();
-
- nread += return_pos - edge_start_pos;
- try reader.context.seekTo(seek_to);
-
- const node = try allocator.create(Node);
- node.* = .{ .base = self.base };
-
- nread += try node.read(allocator, reader);
- try self.edges.append(allocator, .{
- .from = self,
- .to = node,
- .label = label,
- });
- try reader.context.seekTo(return_pos);
- }
+/// The root node of the trie.
+root: ?Node.Index = null,
+buffer: std.ArrayListUnmanaged(u8) = .{},
+nodes: std.MultiArrayList(Node) = .{},
+edges: std.ArrayListUnmanaged(Edge) = .{},
- return nread;
- }
+/// Insert a symbol into the trie, updating the prefixes in the process.
+/// This operation may change the layout of the trie by splicing edges in
+/// certain circumstances.
+fn put(self: *Trie, allocator: Allocator, symbol: ExportSymbol) !void {
+ // const tracy = trace(@src());
+ // defer tracy.end();
+
+ const node_index = try self.putNode(self.root.?, allocator, symbol.name);
+ const slice = self.nodes.slice();
+ slice.items(.is_terminal)[node_index] = true;
+ slice.items(.vmaddr_offset)[node_index] = symbol.vmaddr_offset;
+ slice.items(.export_flags)[node_index] = symbol.export_flags;
+}
- /// Writes this node to a byte stream.
- /// The children of this node *are* not written to the byte stream
- /// recursively. To write all nodes to a byte stream in sequence,
- /// iterate over `Trie.ordered_nodes` and call this method on each node.
- /// This is one of the requirements of the MachO.
- /// Panics if `finalize` was not called before calling this method.
- fn write(self: Node, writer: anytype) !void {
- assert(!self.node_dirty);
- if (self.terminal_info) |info| {
- // Terminal node info: encode export flags and vmaddr offset of this symbol.
- var info_buf: [@sizeOf(u64) * 2]u8 = undefined;
- var info_stream = std.io.fixedBufferStream(&info_buf);
- // TODO Implement for special flags.
- assert(info.export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and
- info.export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);
- try leb.writeUleb128(info_stream.writer(), info.export_flags);
- try leb.writeUleb128(info_stream.writer(), info.vmaddr_offset);
-
- // Encode the size of the terminal node info.
- var size_buf: [@sizeOf(u64)]u8 = undefined;
- var size_stream = std.io.fixedBufferStream(&size_buf);
- try leb.writeUleb128(size_stream.writer(), info_stream.pos);
-
- // Now, write them to the output stream.
- try writer.writeAll(size_buf[0..size_stream.pos]);
- try writer.writeAll(info_buf[0..info_stream.pos]);
- } else {
- // Non-terminal node is delimited by 0 byte.
- try writer.writeByte(0);
- }
- // Write number of edges (max legal number of edges is 256).
- try writer.writeByte(@as(u8, @intCast(self.edges.items.len)));
-
- for (self.edges.items) |edge| {
- // Write edge label and offset to next node in trie.
- try writer.writeAll(edge.label);
- try writer.writeByte(0);
- try leb.writeUleb128(writer, edge.to.trie_offset.?);
- }
+/// Inserts a new node starting at `node_index`.
+fn putNode(self: *Trie, node_index: Node.Index, allocator: Allocator, label: []const u8) !Node.Index {
+ // Check for match with edges from this node.
+ for (self.nodes.items(.edges)[node_index].items) |edge_index| {
+ const edge = &self.edges.items[edge_index];
+ const match = mem.indexOfDiff(u8, edge.label, label) orelse return edge.node;
+ if (match == 0) continue;
+ if (match == edge.label.len) return self.putNode(edge.node, allocator, label[match..]);
+
+ // Found a match, need to splice up nodes.
+ // From: A -> B
+ // To: A -> C -> B
+ const mid_index = try self.addNode(allocator);
+ const to_label = edge.label[match..];
+ const to_node = edge.node;
+ edge.node = mid_index;
+ edge.label = label[0..match];
+
+ const new_edge_index = try self.addEdge(allocator);
+ const new_edge = &self.edges.items[new_edge_index];
+ new_edge.node = to_node;
+ new_edge.label = to_label;
+ try self.nodes.items(.edges)[mid_index].append(allocator, new_edge_index);
+
+ return if (match == label.len) mid_index else self.putNode(mid_index, allocator, label[match..]);
}
- const FinalizeResult = struct {
- /// Current size of this node in bytes.
- node_size: u64,
-
- /// True if the trie offset of this node in the output byte stream
- /// would need updating; false otherwise.
- updated: bool,
- };
+ // Add a new node.
+ const new_node_index = try self.addNode(allocator);
+ const new_edge_index = try self.addEdge(allocator);
+ const new_edge = &self.edges.items[new_edge_index];
+ new_edge.node = new_node_index;
+ new_edge.label = label;
+ try self.nodes.items(.edges)[node_index].append(allocator, new_edge_index);
- /// Updates offset of this node in the output byte stream.
- fn finalize(self: *Node, offset_in_trie: u64) !FinalizeResult {
- var stream = std.io.countingWriter(std.io.null_writer);
- const writer = stream.writer();
-
- var node_size: u64 = 0;
- if (self.terminal_info) |info| {
- try leb.writeUleb128(writer, info.export_flags);
- try leb.writeUleb128(writer, info.vmaddr_offset);
- try leb.writeUleb128(writer, stream.bytes_written);
- } else {
- node_size += 1; // 0x0 for non-terminal nodes
- }
- node_size += 1; // 1 byte for edge count
+ return new_node_index;
+}
- for (self.edges.items) |edge| {
- const next_node_offset = edge.to.trie_offset orelse 0;
- node_size += edge.label.len + 1;
- try leb.writeUleb128(writer, next_node_offset);
+pub fn updateSize(self: *Trie, macho_file: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const gpa = macho_file.base.comp.gpa;
+
+ try self.init(gpa);
+ try self.nodes.ensureUnusedCapacity(gpa, macho_file.resolver.values.items.len * 2);
+ try self.edges.ensureUnusedCapacity(gpa, macho_file.resolver.values.items.len * 2);
+
+ const seg = macho_file.getTextSegment();
+ for (macho_file.resolver.values.items) |ref| {
+ if (ref.getFile(macho_file) == null) continue;
+ const sym = ref.getSymbol(macho_file).?;
+ if (!sym.flags.@"export") continue;
+ if (sym.getAtom(macho_file)) |atom| if (!atom.flags.alive) continue;
+ var flags: u64 = if (sym.flags.abs)
+ macho.EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
+ else if (sym.flags.tlv)
+ macho.EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL
+ else
+ macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR;
+ if (sym.flags.weak) {
+ flags |= macho.EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION;
+ macho_file.weak_defines = true;
+ macho_file.binds_to_weak = true;
}
-
- const trie_offset = self.trie_offset orelse 0;
- const updated = offset_in_trie != trie_offset;
- self.trie_offset = offset_in_trie;
- self.node_dirty = false;
- node_size += stream.bytes_written;
-
- return FinalizeResult{ .node_size = node_size, .updated = updated };
+ try self.put(gpa, .{
+ .name = sym.getName(macho_file),
+ .vmaddr_offset = sym.getAddress(.{ .stubs = false }, macho_file) - seg.vmaddr,
+ .export_flags = flags,
+ });
}
-};
-
-/// The root node of the trie.
-root: ?*Node = null,
-/// If you want to access nodes ordered in DFS fashion,
-/// you should call `finalize` first since the nodes
-/// in this container are not guaranteed to not be stale
-/// if more insertions took place after the last `finalize`
-/// call.
-ordered_nodes: std.ArrayListUnmanaged(*Node) = .{},
+ try self.finalize(gpa);
-/// The size of the trie in bytes.
-/// This value may be outdated if there were additional
-/// insertions performed after `finalize` was called.
-/// Call `finalize` before accessing this value to ensure
-/// it is up-to-date.
-size: u64 = 0,
-
-/// Number of nodes currently in the trie.
-node_count: usize = 0,
-
-trie_dirty: bool = true,
-
-/// Export symbol that is to be placed in the trie.
-pub const ExportSymbol = struct {
- /// Name of the symbol.
- name: []const u8,
-
- /// Offset of this symbol's virtual memory address from the beginning
- /// of the __TEXT segment.
- vmaddr_offset: u64,
-
- /// Export flags of this exported symbol.
- export_flags: u64,
-};
-
-/// Insert a symbol into the trie, updating the prefixes in the process.
-/// This operation may change the layout of the trie by splicing edges in
-/// certain circumstances.
-pub fn put(self: *Trie, allocator: Allocator, symbol: ExportSymbol) !void {
- const node = try self.root.?.put(allocator, symbol.name);
- node.terminal_info = .{
- .vmaddr_offset = symbol.vmaddr_offset,
- .export_flags = symbol.export_flags,
- };
- self.trie_dirty = true;
+ macho_file.dyld_info_cmd.export_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64));
}
/// Finalizes this trie for writing to a byte stream.
/// This step performs multiple passes through the trie ensuring
/// there are no gaps after every `Node` is ULEB128 encoded.
/// Call this method before trying to `write` the trie to a byte stream.
-pub fn finalize(self: *Trie, allocator: Allocator) !void {
- if (!self.trie_dirty) return;
+fn finalize(self: *Trie, allocator: Allocator) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
- self.ordered_nodes.shrinkRetainingCapacity(0);
- try self.ordered_nodes.ensureTotalCapacity(allocator, self.node_count);
+ var ordered_nodes = std.ArrayList(Node.Index).init(allocator);
+ defer ordered_nodes.deinit();
+ try ordered_nodes.ensureTotalCapacityPrecise(self.nodes.items(.is_terminal).len);
- var fifo = std.fifo.LinearFifo(*Node, .Dynamic).init(allocator);
+ var fifo = std.fifo.LinearFifo(Node.Index, .Dynamic).init(allocator);
defer fifo.deinit();
try fifo.writeItem(self.root.?);
- while (fifo.readItem()) |next| {
- for (next.edges.items) |*edge| {
- try fifo.writeItem(edge.to);
+ while (fifo.readItem()) |next_index| {
+ const edges = &self.nodes.items(.edges)[next_index];
+ for (edges.items) |edge_index| {
+ const edge = self.edges.items[edge_index];
+ try fifo.writeItem(edge.node);
}
- self.ordered_nodes.appendAssumeCapacity(next);
+ ordered_nodes.appendAssumeCapacity(next_index);
}
var more: bool = true;
+ var size: u32 = 0;
while (more) {
- self.size = 0;
+ size = 0;
more = false;
- for (self.ordered_nodes.items) |node| {
- const res = try node.finalize(self.size);
- self.size += res.node_size;
+ for (ordered_nodes.items) |node_index| {
+ const res = try self.finalizeNode(node_index, size);
+ size += res.node_size;
if (res.updated) more = true;
}
}
- self.trie_dirty = false;
+ try self.buffer.ensureTotalCapacityPrecise(allocator, size);
+ for (ordered_nodes.items) |node_index| {
+ try self.writeNode(node_index, self.buffer.writer(allocator));
+ }
}
-const ReadError = error{
- OutOfMemory,
- EndOfStream,
- Overflow,
+const FinalizeNodeResult = struct {
+ /// Current size of this node in bytes.
+ node_size: u32,
+
+ /// True if the trie offset of this node in the output byte stream
+ /// would need updating; false otherwise.
+ updated: bool,
};
-/// Parse the trie from a byte stream.
-pub fn read(self: *Trie, allocator: Allocator, reader: anytype) ReadError!usize {
- return self.root.?.read(allocator, reader);
-}
+/// Updates offset of this node in the output byte stream.
+fn finalizeNode(self: *Trie, node_index: Node.Index, offset_in_trie: u32) !FinalizeNodeResult {
+ var stream = std.io.countingWriter(std.io.null_writer);
+ const writer = stream.writer();
+ const slice = self.nodes.slice();
+
+ var node_size: u32 = 0;
+ if (slice.items(.is_terminal)[node_index]) {
+ const export_flags = slice.items(.export_flags)[node_index];
+ const vmaddr_offset = slice.items(.vmaddr_offset)[node_index];
+ try leb.writeULEB128(writer, export_flags);
+ try leb.writeULEB128(writer, vmaddr_offset);
+ try leb.writeULEB128(writer, stream.bytes_written);
+ } else {
+ node_size += 1; // 0x0 for non-terminal nodes
+ }
+ node_size += 1; // 1 byte for edge count
-/// Write the trie to a byte stream.
-/// Panics if the trie was not finalized using `finalize` before calling this method.
-pub fn write(self: Trie, writer: anytype) !void {
- assert(!self.trie_dirty);
- for (self.ordered_nodes.items) |node| {
- try node.write(writer);
+ for (slice.items(.edges)[node_index].items) |edge_index| {
+ const edge = &self.edges.items[edge_index];
+ const next_node_offset = slice.items(.trie_offset)[edge.node];
+ node_size += @intCast(edge.label.len + 1);
+ try leb.writeULEB128(writer, next_node_offset);
}
+
+ const trie_offset = slice.items(.trie_offset)[node_index];
+ const updated = offset_in_trie != trie_offset;
+ slice.items(.trie_offset)[node_index] = offset_in_trie;
+ node_size += @intCast(stream.bytes_written);
+
+ return .{ .node_size = node_size, .updated = updated };
}
-pub fn init(self: *Trie, allocator: Allocator) !void {
+fn init(self: *Trie, allocator: Allocator) !void {
assert(self.root == null);
- const root = try allocator.create(Node);
- root.* = .{ .base = self };
- self.root = root;
- self.node_count += 1;
+ self.root = try self.addNode(allocator);
}
pub fn deinit(self: *Trie, allocator: Allocator) void {
- if (self.root) |root| {
- root.deinit(allocator);
- allocator.destroy(root);
+ for (self.nodes.items(.edges)) |*edges| {
+ edges.deinit(allocator);
}
- self.ordered_nodes.deinit(allocator);
+ self.nodes.deinit(allocator);
+ self.edges.deinit(allocator);
+ self.buffer.deinit(allocator);
}
-test "Trie node count" {
- const gpa = testing.allocator;
- var trie: Trie = .{};
- defer trie.deinit(gpa);
- try trie.init(gpa);
+pub fn write(self: Trie, writer: anytype) !void {
+ if (self.buffer.items.len == 0) return;
+ try writer.writeAll(self.buffer.items);
+}
- try testing.expectEqual(@as(usize, 1), trie.node_count);
- try testing.expect(trie.root != null);
+/// Writes this node to a byte stream.
+/// The children of this node *are* not written to the byte stream
+/// recursively. To write all nodes to a byte stream in sequence,
+/// iterate over `Trie.ordered_nodes` and call this method on each node.
+/// This is one of the requirements of the MachO.
+/// Panics if `finalize` was not called before calling this method.
+fn writeNode(self: *Trie, node_index: Node.Index, writer: anytype) !void {
+ const slice = self.nodes.slice();
+ const edges = slice.items(.edges)[node_index];
+ const is_terminal = slice.items(.is_terminal)[node_index];
+ const export_flags = slice.items(.export_flags)[node_index];
+ const vmaddr_offset = slice.items(.vmaddr_offset)[node_index];
+
+ if (is_terminal) {
+ // Terminal node info: encode export flags and vmaddr offset of this symbol.
+ var info_buf: [@sizeOf(u64) * 2]u8 = undefined;
+ var info_stream = std.io.fixedBufferStream(&info_buf);
+ // TODO Implement for special flags.
+ assert(export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and
+ export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);
+ try leb.writeULEB128(info_stream.writer(), export_flags);
+ try leb.writeULEB128(info_stream.writer(), vmaddr_offset);
+
+ // Encode the size of the terminal node info.
+ var size_buf: [@sizeOf(u64)]u8 = undefined;
+ var size_stream = std.io.fixedBufferStream(&size_buf);
+ try leb.writeULEB128(size_stream.writer(), info_stream.pos);
+
+ // Now, write them to the output stream.
+ try writer.writeAll(size_buf[0..size_stream.pos]);
+ try writer.writeAll(info_buf[0..info_stream.pos]);
+ } else {
+ // Non-terminal node is delimited by 0 byte.
+ try writer.writeByte(0);
+ }
+ // Write number of edges (max legal number of edges is 256).
+ try writer.writeByte(@as(u8, @intCast(edges.items.len)));
+
+ for (edges.items) |edge_index| {
+ const edge = self.edges.items[edge_index];
+ // Write edge label and offset to next node in trie.
+ try writer.writeAll(edge.label);
+ try writer.writeByte(0);
+ try leb.writeULEB128(writer, slice.items(.trie_offset)[edge.node]);
+ }
+}
- try trie.put(gpa, .{
- .name = "_main",
- .vmaddr_offset = 0,
- .export_flags = 0,
- });
- try testing.expectEqual(@as(usize, 2), trie.node_count);
+fn addNode(self: *Trie, allocator: Allocator) !Node.Index {
+ const index: Node.Index = @intCast(try self.nodes.addOne(allocator));
+ self.nodes.set(index, .{});
+ return index;
+}
- // Inserting the same node shouldn't update the trie.
- try trie.put(gpa, .{
- .name = "_main",
- .vmaddr_offset = 0,
- .export_flags = 0,
- });
- try testing.expectEqual(@as(usize, 2), trie.node_count);
+fn addEdge(self: *Trie, allocator: Allocator) !Edge.Index {
+ const index: Edge.Index = @intCast(self.edges.items.len);
+ const edge = try self.edges.addOne(allocator);
+ edge.* = .{};
+ return index;
+}
- try trie.put(gpa, .{
- .name = "__mh_execute_header",
- .vmaddr_offset = 0x1000,
- .export_flags = 0,
- });
- try testing.expectEqual(@as(usize, 4), trie.node_count);
+/// Export symbol that is to be placed in the trie.
+pub const ExportSymbol = struct {
+ /// Name of the symbol.
+ name: []const u8,
- // Inserting the same node shouldn't update the trie.
- try trie.put(gpa, .{
- .name = "__mh_execute_header",
- .vmaddr_offset = 0x1000,
- .export_flags = 0,
- });
- try testing.expectEqual(@as(usize, 4), trie.node_count);
- try trie.put(gpa, .{
- .name = "_main",
- .vmaddr_offset = 0,
- .export_flags = 0,
- });
- try testing.expectEqual(@as(usize, 4), trie.node_count);
-}
+ /// Offset of this symbol's virtual memory address from the beginning
+ /// of the __TEXT segment.
+ vmaddr_offset: u64,
-test "Trie basic" {
- const gpa = testing.allocator;
- var trie: Trie = .{};
- defer trie.deinit(gpa);
- try trie.init(gpa);
+ /// Export flags of this exported symbol.
+ export_flags: u64,
+};
- // root --- _st ---> node
- try trie.put(gpa, .{
- .name = "_st",
- .vmaddr_offset = 0,
- .export_flags = 0,
- });
- try testing.expect(trie.root.?.edges.items.len == 1);
- try testing.expect(mem.eql(u8, trie.root.?.edges.items[0].label, "_st"));
-
- {
- // root --- _st ---> node --- art ---> node
- try trie.put(gpa, .{
- .name = "_start",
- .vmaddr_offset = 0,
- .export_flags = 0,
- });
- try testing.expect(trie.root.?.edges.items.len == 1);
+const Node = struct {
+ is_terminal: bool = false,
- const nextEdge = &trie.root.?.edges.items[0];
- try testing.expect(mem.eql(u8, nextEdge.label, "_st"));
- try testing.expect(nextEdge.to.edges.items.len == 1);
- try testing.expect(mem.eql(u8, nextEdge.to.edges.items[0].label, "art"));
- }
- {
- // root --- _ ---> node --- st ---> node --- art ---> node
- // |
- // | --- main ---> node
- try trie.put(gpa, .{
- .name = "_main",
- .vmaddr_offset = 0,
- .export_flags = 0,
- });
- try testing.expect(trie.root.?.edges.items.len == 1);
+ /// Export flags associated with this exported symbol.
+ export_flags: u64 = 0,
- const nextEdge = &trie.root.?.edges.items[0];
- try testing.expect(mem.eql(u8, nextEdge.label, "_"));
- try testing.expect(nextEdge.to.edges.items.len == 2);
- try testing.expect(mem.eql(u8, nextEdge.to.edges.items[0].label, "st"));
- try testing.expect(mem.eql(u8, nextEdge.to.edges.items[1].label, "main"));
+ /// VM address offset wrt to the section this symbol is defined against.
+ vmaddr_offset: u64 = 0,
- const nextNextEdge = &nextEdge.to.edges.items[0];
- try testing.expect(mem.eql(u8, nextNextEdge.to.edges.items[0].label, "art"));
- }
-}
+ /// Offset of this node in the trie output byte stream.
+ trie_offset: u32 = 0,
+
+ /// List of all edges originating from this node.
+ edges: std.ArrayListUnmanaged(Edge.Index) = .{},
+
+ const Index = u32;
+};
+
+/// Edge connecting nodes in the trie.
+const Edge = struct {
+ /// Target node in the trie.
+ node: Node.Index = 0,
+
+ /// Matching prefix.
+ label: []const u8 = "",
+
+ const Index = u32;
+};
fn expectEqualHexStrings(expected: []const u8, given: []const u8) !void {
assert(expected.len > 0);
@@ -502,7 +349,7 @@ fn expectEqualHexStrings(expected: []const u8, given: []const u8) !void {
}
test "write Trie to a byte stream" {
- var gpa = testing.allocator;
+ const gpa = testing.allocator;
var trie: Trie = .{};
defer trie.deinit(gpa);
try trie.init(gpa);
@@ -519,7 +366,6 @@ test "write Trie to a byte stream" {
});
try trie.finalize(gpa);
- try trie.finalize(gpa); // Finalizing mulitple times is a nop subsequently unless we add new nodes.
const exp_buffer = [_]u8{
0x0, 0x1, // node root
@@ -531,51 +377,7 @@ test "write Trie to a byte stream" {
0x2, 0x0, 0x0, 0x0, // terminal node
0x3, 0x0, 0x80, 0x20, 0x0, // terminal node
};
-
- const buffer = try gpa.alloc(u8, trie.size);
- defer gpa.free(buffer);
- var stream = std.io.fixedBufferStream(buffer);
- {
- _ = try trie.write(stream.writer());
- try expectEqualHexStrings(&exp_buffer, buffer);
- }
- {
- // Writing finalized trie again should yield the same result.
- try stream.seekTo(0);
- _ = try trie.write(stream.writer());
- try expectEqualHexStrings(&exp_buffer, buffer);
- }
-}
-
-test "parse Trie from byte stream" {
- const gpa = testing.allocator;
-
- const in_buffer = [_]u8{
- 0x0, 0x1, // node root
- 0x5f, 0x0, 0x5, // edge '_'
- 0x0, 0x2, // non-terminal node
- 0x5f, 0x6d, 0x68, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, // edge '_mh_execute_header'
- 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x0, 0x21, // edge '_mh_execute_header'
- 0x6d, 0x61, 0x69, 0x6e, 0x0, 0x25, // edge 'main'
- 0x2, 0x0, 0x0, 0x0, // terminal node
- 0x3, 0x0, 0x80, 0x20, 0x0, // terminal node
- };
-
- var in_stream = std.io.fixedBufferStream(&in_buffer);
- var trie: Trie = .{};
- defer trie.deinit(gpa);
- try trie.init(gpa);
- const nread = try trie.read(gpa, in_stream.reader());
-
- try testing.expect(nread == in_buffer.len);
-
- try trie.finalize(gpa);
-
- const out_buffer = try gpa.alloc(u8, trie.size);
- defer gpa.free(out_buffer);
- var out_stream = std.io.fixedBufferStream(out_buffer);
- _ = try trie.write(out_stream.writer());
- try expectEqualHexStrings(&in_buffer, out_buffer);
+ try expectEqualHexStrings(&exp_buffer, trie.buffer.items);
}
test "ordering bug" {
@@ -602,11 +404,18 @@ test "ordering bug" {
0x88, 0x80, 0x02, 0x01, 0x73, 0x53, 0x74, 0x72,
0x00, 0x12, 0x03, 0x00, 0xD8, 0x0A, 0x00,
};
-
- const buffer = try gpa.alloc(u8, trie.size);
- defer gpa.free(buffer);
- var stream = std.io.fixedBufferStream(buffer);
- // Writing finalized trie again should yield the same result.
- _ = try trie.write(stream.writer());
- try expectEqualHexStrings(&exp_buffer, buffer);
+ try expectEqualHexStrings(&exp_buffer, trie.buffer.items);
}
+
+const assert = std.debug.assert;
+const leb = std.leb;
+const log = std.log.scoped(.macho);
+const macho = std.macho;
+const mem = std.mem;
+const std = @import("std");
+const testing = std.testing;
+const trace = @import("../../../tracy.zig").trace;
+
+const Allocator = mem.Allocator;
+const MachO = @import("../../MachO.zig");
+const Trie = @This();
diff --git a/src/link/MachO/dyld_info/bind.zig b/src/link/MachO/dyld_info/bind.zig
@@ -1,28 +1,19 @@
-const std = @import("std");
-const assert = std.debug.assert;
-const leb = std.leb;
-const log = std.log.scoped(.link_dyld_info);
-const macho = std.macho;
-const testing = std.testing;
-
-const Allocator = std.mem.Allocator;
-const MachO = @import("../../MachO.zig");
-const Symbol = @import("../Symbol.zig");
-
pub const Entry = struct {
- target: Symbol.Index,
+ target: MachO.Ref,
offset: u64,
segment_id: u8,
addend: i64,
pub fn lessThan(ctx: *MachO, entry: Entry, other: Entry) bool {
+ _ = ctx;
if (entry.segment_id == other.segment_id) {
- if (entry.target == other.target) {
+ if (entry.target.eql(other.target)) {
return entry.offset < other.offset;
}
- const entry_name = ctx.getSymbol(entry.target).getName(ctx);
- const other_name = ctx.getSymbol(other.target).getName(ctx);
- return std.mem.lessThan(u8, entry_name, other_name);
+ if (entry.target.file == other.target.file) {
+ return entry.target.index < other.target.index;
+ }
+ return entry.target.file < other.target.file;
}
return entry.segment_id < other.segment_id;
}
@@ -39,11 +30,109 @@ pub const Bind = struct {
self.buffer.deinit(gpa);
}
- pub fn size(self: Self) u64 {
- return @intCast(self.buffer.items.len);
+ pub fn updateSize(self: *Self, macho_file: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const gpa = macho_file.base.comp.gpa;
+ const cpu_arch = macho_file.getTarget().cpu.arch;
+
+ var objects = try std.ArrayList(File.Index).initCapacity(gpa, macho_file.objects.items.len + 2);
+ defer objects.deinit();
+ objects.appendSliceAssumeCapacity(macho_file.objects.items);
+ if (macho_file.getZigObject()) |obj| objects.appendAssumeCapacity(obj.index);
+ if (macho_file.getInternalObject()) |obj| objects.appendAssumeCapacity(obj.index);
+
+ for (objects.items) |index| {
+ const file = macho_file.getFile(index).?;
+ for (file.getAtoms()) |atom_index| {
+ const atom = file.getAtom(atom_index) orelse continue;
+ if (!atom.flags.alive) continue;
+ if (atom.getInputSection(macho_file).isZerofill()) continue;
+ const atom_addr = atom.getAddress(macho_file);
+ const relocs = atom.getRelocs(macho_file);
+ const seg_id = macho_file.sections.items(.segment_id)[atom.out_n_sect];
+ const seg = macho_file.segments.items[seg_id];
+ for (relocs) |rel| {
+ if (rel.type != .unsigned or rel.meta.length != 3 or rel.tag != .@"extern") continue;
+ const rel_offset = rel.offset - atom.off;
+ const addend = rel.addend + rel.getRelocAddend(cpu_arch);
+ const sym = rel.getTargetSymbol(atom.*, macho_file);
+ if (sym.isTlvInit(macho_file)) continue;
+ const entry = Entry{
+ .target = rel.getTargetSymbolRef(atom.*, macho_file),
+ .offset = atom_addr + rel_offset - seg.vmaddr,
+ .segment_id = seg_id,
+ .addend = addend,
+ };
+ if (sym.flags.import or (!(sym.flags.@"export" and sym.flags.weak) and sym.flags.interposable)) {
+ try self.entries.append(gpa, entry);
+ }
+ }
+ }
+ }
+
+ if (macho_file.got_sect_index) |sid| {
+ const seg_id = macho_file.sections.items(.segment_id)[sid];
+ const seg = macho_file.segments.items[seg_id];
+ for (macho_file.got.symbols.items, 0..) |ref, idx| {
+ const sym = ref.getSymbol(macho_file).?;
+ const addr = macho_file.got.getAddress(@intCast(idx), macho_file);
+ const entry = Entry{
+ .target = ref,
+ .offset = addr - seg.vmaddr,
+ .segment_id = seg_id,
+ .addend = 0,
+ };
+ if (sym.flags.import or (sym.flags.@"export" and sym.flags.interposable and !sym.flags.weak)) {
+ try self.entries.append(gpa, entry);
+ }
+ }
+ }
+
+ if (macho_file.la_symbol_ptr_sect_index) |sid| {
+ const sect = macho_file.sections.items(.header)[sid];
+ const seg_id = macho_file.sections.items(.segment_id)[sid];
+ const seg = macho_file.segments.items[seg_id];
+ for (macho_file.stubs.symbols.items, 0..) |ref, idx| {
+ const sym = ref.getSymbol(macho_file).?;
+ const addr = sect.addr + idx * @sizeOf(u64);
+ const bind_entry = Entry{
+ .target = ref,
+ .offset = addr - seg.vmaddr,
+ .segment_id = seg_id,
+ .addend = 0,
+ };
+ if (sym.flags.import and sym.flags.weak) {
+ try self.entries.append(gpa, bind_entry);
+ }
+ }
+ }
+
+ if (macho_file.tlv_ptr_sect_index) |sid| {
+ const seg_id = macho_file.sections.items(.segment_id)[sid];
+ const seg = macho_file.segments.items[seg_id];
+
+ for (macho_file.tlv_ptr.symbols.items, 0..) |ref, idx| {
+ const sym = ref.getSymbol(macho_file).?;
+ const addr = macho_file.tlv_ptr.getAddress(@intCast(idx), macho_file);
+ const entry = Entry{
+ .target = ref,
+ .offset = addr - seg.vmaddr,
+ .segment_id = seg_id,
+ .addend = 0,
+ };
+ if (sym.flags.import or (sym.flags.@"export" and sym.flags.interposable and !sym.flags.weak)) {
+ try self.entries.append(gpa, entry);
+ }
+ }
+ }
+
+ try self.finalize(gpa, macho_file);
+ macho_file.dyld_info_cmd.bind_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64));
}
- pub fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void {
+ fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void {
if (self.entries.items.len == 0) return;
const writer = self.buffer.writer(gpa);
@@ -75,7 +164,7 @@ pub const Bind = struct {
var addend: i64 = 0;
var count: usize = 0;
var skip: u64 = 0;
- var target: ?Symbol.Index = null;
+ var target: ?MachO.Ref = null;
var state: enum {
start,
@@ -86,7 +175,7 @@ pub const Bind = struct {
var i: usize = 0;
while (i < entries.len) : (i += 1) {
const current = entries[i];
- if (target == null or target.? != current.target) {
+ if (target == null or !target.?.eql(current.target)) {
switch (state) {
.start => {},
.bind_single => try doBind(writer),
@@ -95,7 +184,7 @@ pub const Bind = struct {
state = .start;
target = current.target;
- const sym = ctx.getSymbol(current.target);
+ const sym = current.target.getSymbol(ctx).?;
const name = sym.getName(ctx);
const flags: u8 = if (sym.weakRef(ctx)) macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT else 0;
const ordinal: i16 = ord: {
@@ -178,7 +267,6 @@ pub const Bind = struct {
}
pub fn write(self: Self, writer: anytype) !void {
- if (self.size() == 0) return;
try writer.writeAll(self.buffer.items);
}
};
@@ -194,11 +282,110 @@ pub const WeakBind = struct {
self.buffer.deinit(gpa);
}
- pub fn size(self: Self) u64 {
- return @intCast(self.buffer.items.len);
+ pub fn updateSize(self: *Self, macho_file: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const gpa = macho_file.base.comp.gpa;
+ const cpu_arch = macho_file.getTarget().cpu.arch;
+
+ var objects = try std.ArrayList(File.Index).initCapacity(gpa, macho_file.objects.items.len + 2);
+ defer objects.deinit();
+ objects.appendSliceAssumeCapacity(macho_file.objects.items);
+ if (macho_file.getZigObject()) |obj| objects.appendAssumeCapacity(obj.index);
+ if (macho_file.getInternalObject()) |obj| objects.appendAssumeCapacity(obj.index);
+
+ for (objects.items) |index| {
+ const file = macho_file.getFile(index).?;
+ for (file.getAtoms()) |atom_index| {
+ const atom = file.getAtom(atom_index) orelse continue;
+ if (!atom.flags.alive) continue;
+ if (atom.getInputSection(macho_file).isZerofill()) continue;
+ const atom_addr = atom.getAddress(macho_file);
+ const relocs = atom.getRelocs(macho_file);
+ const seg_id = macho_file.sections.items(.segment_id)[atom.out_n_sect];
+ const seg = macho_file.segments.items[seg_id];
+ for (relocs) |rel| {
+ if (rel.type != .unsigned or rel.meta.length != 3 or rel.tag != .@"extern") continue;
+ const rel_offset = rel.offset - atom.off;
+ const addend = rel.addend + rel.getRelocAddend(cpu_arch);
+ const sym = rel.getTargetSymbol(atom.*, macho_file);
+ if (sym.isTlvInit(macho_file)) continue;
+ const entry = Entry{
+ .target = rel.getTargetSymbolRef(atom.*, macho_file),
+ .offset = atom_addr + rel_offset - seg.vmaddr,
+ .segment_id = seg_id,
+ .addend = addend,
+ };
+ if (!sym.isLocal() and sym.flags.weak) {
+ try self.entries.append(gpa, entry);
+ }
+ }
+ }
+ }
+
+ if (macho_file.got_sect_index) |sid| {
+ const seg_id = macho_file.sections.items(.segment_id)[sid];
+ const seg = macho_file.segments.items[seg_id];
+ for (macho_file.got.symbols.items, 0..) |ref, idx| {
+ const sym = ref.getSymbol(macho_file).?;
+ const addr = macho_file.got.getAddress(@intCast(idx), macho_file);
+ const entry = Entry{
+ .target = ref,
+ .offset = addr - seg.vmaddr,
+ .segment_id = seg_id,
+ .addend = 0,
+ };
+ if (sym.flags.weak) {
+ try self.entries.append(gpa, entry);
+ }
+ }
+ }
+
+ if (macho_file.la_symbol_ptr_sect_index) |sid| {
+ const sect = macho_file.sections.items(.header)[sid];
+ const seg_id = macho_file.sections.items(.segment_id)[sid];
+ const seg = macho_file.segments.items[seg_id];
+
+ for (macho_file.stubs.symbols.items, 0..) |ref, idx| {
+ const sym = ref.getSymbol(macho_file).?;
+ const addr = sect.addr + idx * @sizeOf(u64);
+ const bind_entry = Entry{
+ .target = ref,
+ .offset = addr - seg.vmaddr,
+ .segment_id = seg_id,
+ .addend = 0,
+ };
+ if (sym.flags.weak) {
+ try self.entries.append(gpa, bind_entry);
+ }
+ }
+ }
+
+ if (macho_file.tlv_ptr_sect_index) |sid| {
+ const seg_id = macho_file.sections.items(.segment_id)[sid];
+ const seg = macho_file.segments.items[seg_id];
+
+ for (macho_file.tlv_ptr.symbols.items, 0..) |ref, idx| {
+ const sym = ref.getSymbol(macho_file).?;
+ const addr = macho_file.tlv_ptr.getAddress(@intCast(idx), macho_file);
+ const entry = Entry{
+ .target = ref,
+ .offset = addr - seg.vmaddr,
+ .segment_id = seg_id,
+ .addend = 0,
+ };
+ if (sym.flags.weak) {
+ try self.entries.append(gpa, entry);
+ }
+ }
+ }
+
+ try self.finalize(gpa, macho_file);
+ macho_file.dyld_info_cmd.weak_bind_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64));
}
- pub fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void {
+ fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void {
if (self.entries.items.len == 0) return;
const writer = self.buffer.writer(gpa);
@@ -230,7 +417,7 @@ pub const WeakBind = struct {
var addend: i64 = 0;
var count: usize = 0;
var skip: u64 = 0;
- var target: ?Symbol.Index = null;
+ var target: ?MachO.Ref = null;
var state: enum {
start,
@@ -241,7 +428,7 @@ pub const WeakBind = struct {
var i: usize = 0;
while (i < entries.len) : (i += 1) {
const current = entries[i];
- if (target == null or target.? != current.target) {
+ if (target == null or !target.?.eql(current.target)) {
switch (state) {
.start => {},
.bind_single => try doBind(writer),
@@ -250,7 +437,7 @@ pub const WeakBind = struct {
state = .start;
target = current.target;
- const sym = ctx.getSymbol(current.target);
+ const sym = current.target.getSymbol(ctx).?;
const name = sym.getName(ctx);
const flags: u8 = 0; // TODO NON_WEAK_DEFINITION
@@ -322,7 +509,6 @@ pub const WeakBind = struct {
}
pub fn write(self: Self, writer: anytype) !void {
- if (self.size() == 0) return;
try writer.writeAll(self.buffer.items);
}
};
@@ -340,11 +526,36 @@ pub const LazyBind = struct {
self.offsets.deinit(gpa);
}
- pub fn size(self: Self) u64 {
- return @intCast(self.buffer.items.len);
+ pub fn updateSize(self: *Self, macho_file: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const gpa = macho_file.base.comp.gpa;
+
+ const sid = macho_file.la_symbol_ptr_sect_index.?;
+ const sect = macho_file.sections.items(.header)[sid];
+ const seg_id = macho_file.sections.items(.segment_id)[sid];
+ const seg = macho_file.segments.items[seg_id];
+
+ for (macho_file.stubs.symbols.items, 0..) |ref, idx| {
+ const sym = ref.getSymbol(macho_file).?;
+ const addr = sect.addr + idx * @sizeOf(u64);
+ const bind_entry = Entry{
+ .target = ref,
+ .offset = addr - seg.vmaddr,
+ .segment_id = seg_id,
+ .addend = 0,
+ };
+ if ((sym.flags.import and !sym.flags.weak) or (sym.flags.interposable and !sym.flags.weak)) {
+ try self.entries.append(gpa, bind_entry);
+ }
+ }
+
+ try self.finalize(gpa, macho_file);
+ macho_file.dyld_info_cmd.lazy_bind_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64));
}
- pub fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void {
+ fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void {
try self.offsets.ensureTotalCapacityPrecise(gpa, self.entries.items.len);
const writer = self.buffer.writer(gpa);
@@ -356,7 +567,7 @@ pub const LazyBind = struct {
for (self.entries.items) |entry| {
self.offsets.appendAssumeCapacity(@intCast(self.buffer.items.len));
- const sym = ctx.getSymbol(entry.target);
+ const sym = entry.target.getSymbol(ctx).?;
const name = sym.getName(ctx);
const flags: u8 = if (sym.weakRef(ctx)) macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT else 0;
const ordinal: i16 = ord: {
@@ -474,3 +685,17 @@ fn done(writer: anytype) !void {
log.debug(">>> done", .{});
try writer.writeByte(macho.BIND_OPCODE_DONE);
}
+
+const assert = std.debug.assert;
+const leb = std.leb;
+const log = std.log.scoped(.link_dyld_info);
+const macho = std.macho;
+const mem = std.mem;
+const testing = std.testing;
+const trace = @import("../../../tracy.zig").trace;
+const std = @import("std");
+
+const Allocator = mem.Allocator;
+const File = @import("../file.zig").File;
+const MachO = @import("../../MachO.zig");
+const Symbol = @import("../Symbol.zig");
diff --git a/src/link/MachO/eh_frame.zig b/src/link/MachO/eh_frame.zig
@@ -68,7 +68,8 @@ pub const Cie = struct {
pub fn getPersonality(cie: Cie, macho_file: *MachO) ?*Symbol {
const personality = cie.personality orelse return null;
- return macho_file.getSymbol(personality.index);
+ const object = cie.getObject(macho_file);
+ return object.getSymbolRef(personality.index, macho_file).getSymbol(macho_file);
}
pub fn eql(cie: Cie, other: Cie, macho_file: *MachO) bool {
@@ -223,11 +224,11 @@ pub const Fde = struct {
}
pub fn getAtom(fde: Fde, macho_file: *MachO) *Atom {
- return macho_file.getAtom(fde.atom).?;
+ return fde.getObject(macho_file).getAtom(fde.atom).?;
}
pub fn getLsdaAtom(fde: Fde, macho_file: *MachO) ?*Atom {
- return macho_file.getAtom(fde.lsda);
+ return fde.getObject(macho_file).getAtom(fde.lsda);
}
pub fn format(
@@ -448,7 +449,7 @@ pub fn write(macho_file: *MachO, buffer: []u8) void {
}
}
-pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho.relocation_info)) error{Overflow}!void {
+pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: []macho.relocation_info) error{Overflow}!void {
const tracy = trace(@src());
defer tracy.end();
@@ -459,6 +460,7 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho.
else => 0,
};
+ var i: usize = 0;
for (macho_file.objects.items) |index| {
const object = macho_file.getFile(index).?.object;
for (object.cies.items) |cie| {
@@ -469,7 +471,7 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho.
if (cie.getPersonality(macho_file)) |sym| {
const r_address = math.cast(i32, cie.out_offset + cie.personality.?.offset) orelse return error.Overflow;
const r_symbolnum = math.cast(u24, sym.getOutputSymtabIndex(macho_file).?) orelse return error.Overflow;
- relocs.appendAssumeCapacity(.{
+ relocs[i] = .{
.r_address = r_address,
.r_symbolnum = r_symbolnum,
.r_length = 2,
@@ -480,7 +482,8 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho.
.x86_64 => @intFromEnum(macho.reloc_type_x86_64.X86_64_RELOC_GOT),
else => unreachable,
},
- });
+ };
+ i += 1;
}
}
}
@@ -531,6 +534,8 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho.
}
}
}
+
+ assert(relocs.len == i);
}
pub const EH_PE = struct {
diff --git a/src/link/MachO/file.zig b/src/link/MachO/file.zig
@@ -24,41 +24,194 @@ pub const File = union(enum) {
_ = options;
switch (file) {
.zig_object => |x| try writer.writeAll(x.path),
- .internal => try writer.writeAll(""),
+ .internal => try writer.writeAll("internal"),
.object => |x| try writer.print("{}", .{x.fmtPath()}),
.dylib => |x| try writer.writeAll(x.path),
}
}
- pub fn resolveSymbols(file: File, macho_file: *MachO) void {
- switch (file) {
- .internal => unreachable,
+ pub fn resolveSymbols(file: File, macho_file: *MachO) !void {
+ return switch (file) {
inline else => |x| x.resolveSymbols(macho_file),
- }
+ };
}
- pub fn resetGlobals(file: File, macho_file: *MachO) void {
+ pub fn scanRelocs(file: File, macho_file: *MachO) !void {
switch (file) {
- .internal => unreachable,
- inline else => |x| x.resetGlobals(macho_file),
+ .dylib => unreachable,
+ .internal => |x| x.scanRelocs(macho_file),
+ inline else => |x| x.scanRelocs(macho_file),
+ }
+ }
+
+ /// Encodes symbol rank so that the following ordering applies:
+ /// * strong in object
+ /// * weak in object
+ /// * tentative in object
+ /// * strong in archive/dylib
+ /// * weak in archive/dylib
+ /// * tentative in archive
+ /// * unclaimed
+ pub fn getSymbolRank(file: File, args: struct {
+ archive: bool = false,
+ weak: bool = false,
+ tentative: bool = false,
+ }) u32 {
+ if (file != .dylib and !args.archive) {
+ const base: u32 = blk: {
+ if (args.tentative) break :blk 3;
+ break :blk if (args.weak) 2 else 1;
+ };
+ return (base << 16) + file.getIndex();
+ }
+ const base: u32 = blk: {
+ if (args.tentative) break :blk 3;
+ break :blk if (args.weak) 2 else 1;
+ };
+ return base + (file.getIndex() << 24);
+ }
+
+ pub fn getAtom(file: File, atom_index: Atom.Index) ?*Atom {
+ return switch (file) {
+ .dylib => unreachable,
+ inline else => |x| x.getAtom(atom_index),
+ };
+ }
+
+ pub fn getAtoms(file: File) []const Atom.Index {
+ return switch (file) {
+ .dylib => unreachable,
+ inline else => |x| x.getAtoms(),
+ };
+ }
+
+ pub fn addAtomExtra(file: File, allocator: Allocator, extra: Atom.Extra) !u32 {
+ return switch (file) {
+ .dylib => unreachable,
+ inline else => |x| x.addAtomExtra(allocator, extra),
+ };
+ }
+
+ pub fn getAtomExtra(file: File, index: u32) Atom.Extra {
+ return switch (file) {
+ .dylib => unreachable,
+ inline else => |x| x.getAtomExtra(index),
+ };
+ }
+
+ pub fn setAtomExtra(file: File, index: u32, extra: Atom.Extra) void {
+ return switch (file) {
+ .dylib => unreachable,
+ inline else => |x| x.setAtomExtra(index, extra),
+ };
+ }
+
+ pub fn getSymbols(file: File) []Symbol {
+ return switch (file) {
+ inline else => |x| x.symbols.items,
+ };
+ }
+
+ pub fn getSymbolRef(file: File, sym_index: Symbol.Index, macho_file: *MachO) MachO.Ref {
+ return switch (file) {
+ inline else => |x| x.getSymbolRef(sym_index, macho_file),
+ };
+ }
+
+ pub fn getNlists(file: File) []macho.nlist_64 {
+ return switch (file) {
+ .dylib => unreachable,
+ .internal => |x| x.symtab.items,
+ inline else => |x| x.symtab.items(.nlist),
+ };
+ }
+
+ pub fn getGlobals(file: File) []MachO.SymbolResolver.Index {
+ return switch (file) {
+ inline else => |x| x.globals.items,
+ };
+ }
+
+ pub fn markImportsExports(file: File, macho_file: *MachO) void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const nsyms = switch (file) {
+ .dylib => unreachable,
+ inline else => |x| x.symbols.items.len,
+ };
+ for (0..nsyms) |i| {
+ const ref = file.getSymbolRef(@intCast(i), macho_file);
+ if (ref.getFile(macho_file) == null) continue;
+ const sym = ref.getSymbol(macho_file).?;
+ if (sym.visibility != .global) continue;
+ if (sym.getFile(macho_file).? == .dylib and !sym.flags.abs) {
+ sym.flags.import = true;
+ continue;
+ }
+ if (file.getIndex() == ref.file) {
+ sym.flags.@"export" = true;
+ }
}
}
- pub fn claimUnresolved(file: File, macho_file: *MachO) error{OutOfMemory}!void {
+ pub fn markExportsRelocatable(file: File, macho_file: *MachO) void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
assert(file == .object or file == .zig_object);
- for (file.getSymbols(), 0..) |sym_index, i| {
- const nlist_idx = @as(Symbol.Index, @intCast(i));
- const nlist = switch (file) {
- .object => |x| x.symtab.items(.nlist)[nlist_idx],
- .zig_object => |x| x.symtab.items(.nlist)[nlist_idx],
- else => unreachable,
- };
+ for (file.getSymbols(), 0..) |*sym, i| {
+ const ref = file.getSymbolRef(@intCast(i), macho_file);
+ const other_file = ref.getFile(macho_file) orelse continue;
+ if (other_file.getIndex() != file.getIndex()) continue;
+ if (sym.visibility != .global) continue;
+ sym.flags.@"export" = true;
+ }
+ }
+
+ pub fn createSymbolIndirection(file: File, macho_file: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const nsyms = switch (file) {
+ inline else => |x| x.symbols.items.len,
+ };
+ for (0..nsyms) |i| {
+ const ref = file.getSymbolRef(@intCast(i), macho_file);
+ if (ref.getFile(macho_file) == null) continue;
+ if (ref.file != file.getIndex()) continue;
+ const sym = ref.getSymbol(macho_file).?;
+ if (sym.flags.needs_got) {
+ log.debug("'{s}' needs GOT", .{sym.getName(macho_file)});
+ try macho_file.got.addSymbol(ref, macho_file);
+ }
+ if (sym.flags.stubs) {
+ log.debug("'{s}' needs STUBS", .{sym.getName(macho_file)});
+ try macho_file.stubs.addSymbol(ref, macho_file);
+ }
+ if (sym.flags.tlv_ptr) {
+ log.debug("'{s}' needs TLV pointer", .{sym.getName(macho_file)});
+ try macho_file.tlv_ptr.addSymbol(ref, macho_file);
+ }
+ if (sym.flags.objc_stubs) {
+ log.debug("'{s}' needs OBJC STUBS", .{sym.getName(macho_file)});
+ try macho_file.objc_stubs.addSymbol(ref, macho_file);
+ }
+ }
+ }
+
+ pub fn claimUnresolved(file: File, macho_file: *MachO) void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ assert(file == .object or file == .zig_object);
+
+ for (file.getSymbols(), file.getNlists(), 0..) |*sym, nlist, i| {
if (!nlist.ext()) continue;
if (!nlist.undf()) continue;
- const sym = macho_file.getSymbol(sym_index);
- if (sym.getFile(macho_file) != null) continue;
+ if (file.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue;
const is_import = switch (macho_file.undefined_treatment) {
.@"error" => false,
@@ -67,111 +220,95 @@ pub const File = union(enum) {
};
if (is_import) {
sym.value = 0;
- sym.atom = 0;
- sym.nlist_idx = 0;
- sym.file = macho_file.internal_object.?;
+ sym.atom_ref = .{ .index = 0, .file = 0 };
sym.flags.weak = false;
sym.flags.weak_ref = nlist.weakRef();
sym.flags.import = is_import;
sym.visibility = .global;
- try macho_file.getInternalObject().?.symbols.append(macho_file.base.comp.gpa, sym_index);
+
+ const idx = file.getGlobals()[i];
+ macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = file.getIndex() };
}
}
}
pub fn claimUnresolvedRelocatable(file: File, macho_file: *MachO) void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
assert(file == .object or file == .zig_object);
- for (file.getSymbols(), 0..) |sym_index, i| {
- const nlist_idx = @as(Symbol.Index, @intCast(i));
- const nlist = switch (file) {
- .object => |x| x.symtab.items(.nlist)[nlist_idx],
- .zig_object => |x| x.symtab.items(.nlist)[nlist_idx],
- else => unreachable,
- };
+ for (file.getSymbols(), file.getNlists(), 0..) |*sym, nlist, i| {
if (!nlist.ext()) continue;
if (!nlist.undf()) continue;
-
- const sym = macho_file.getSymbol(sym_index);
- if (sym.getFile(macho_file) != null) continue;
+ if (file.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue;
sym.value = 0;
- sym.atom = 0;
- sym.nlist_idx = nlist_idx;
- sym.file = file.getIndex();
+ sym.atom_ref = .{ .index = 0, .file = 0 };
sym.flags.weak_ref = nlist.weakRef();
sym.flags.import = true;
sym.visibility = .global;
+
+ const idx = file.getGlobals()[i];
+ macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = file.getIndex() };
}
}
- pub fn markImportsExports(file: File, macho_file: *MachO) void {
- assert(file == .object or file == .zig_object);
+ pub fn checkDuplicates(file: File, macho_file: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const gpa = macho_file.base.comp.gpa;
- for (file.getSymbols()) |sym_index| {
- const sym = macho_file.getSymbol(sym_index);
- const other_file = sym.getFile(macho_file) orelse continue;
+ for (file.getSymbols(), file.getNlists(), 0..) |sym, nlist, i| {
if (sym.visibility != .global) continue;
- if (other_file == .dylib and !sym.flags.abs) {
- sym.flags.import = true;
- continue;
- }
- if (other_file.getIndex() == file.getIndex()) {
- sym.flags.@"export" = true;
+ if (sym.flags.weak) continue;
+ if (nlist.undf()) continue;
+ const ref = file.getSymbolRef(@intCast(i), macho_file);
+ const ref_file = ref.getFile(macho_file) orelse continue;
+ if (ref_file.getIndex() == file.getIndex()) continue;
+
+ const gop = try macho_file.dupes.getOrPut(gpa, file.getGlobals()[i]);
+ if (!gop.found_existing) {
+ gop.value_ptr.* = .{};
}
+ try gop.value_ptr.append(gpa, file.getIndex());
}
}
- pub fn markExportsRelocatable(file: File, macho_file: *MachO) void {
- assert(file == .object or file == .zig_object);
-
- for (file.getSymbols()) |sym_index| {
- const sym = macho_file.getSymbol(sym_index);
- const other_file = sym.getFile(macho_file) orelse continue;
- if (sym.visibility != .global) continue;
- if (other_file.getIndex() == file.getIndex()) {
- sym.flags.@"export" = true;
- }
+ pub fn initOutputSections(file: File, macho_file: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+ for (file.getAtoms()) |atom_index| {
+ const atom = file.getAtom(atom_index) orelse continue;
+ if (!atom.flags.alive) continue;
+ atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(macho_file), macho_file);
}
}
- /// Encodes symbol rank so that the following ordering applies:
- /// * strong in object
- /// * weak in object
- /// * tentative in object
- /// * strong in archive/dylib
- /// * weak in archive/dylib
- /// * tentative in archive
- /// * unclaimed
- pub fn getSymbolRank(file: File, args: struct {
- archive: bool = false,
- weak: bool = false,
- tentative: bool = false,
- }) u32 {
- if (file == .object and !args.archive) {
- const base: u32 = blk: {
- if (args.tentative) break :blk 3;
- break :blk if (args.weak) 2 else 1;
- };
- return (base << 16) + file.getIndex();
- }
- const base: u32 = blk: {
- if (args.tentative) break :blk 3;
- break :blk if (args.weak) 2 else 1;
+ pub fn dedupLiterals(file: File, lp: MachO.LiteralPool, macho_file: *MachO) void {
+ return switch (file) {
+ .dylib => unreachable,
+ inline else => |x| x.dedupLiterals(lp, macho_file),
};
- return base + (file.getIndex() << 24);
}
- pub fn getSymbols(file: File) []const Symbol.Index {
+ pub fn writeAtoms(file: File, macho_file: *MachO) !void {
return switch (file) {
- inline else => |x| x.symbols.items,
+ .dylib, .zig_object => unreachable,
+ inline else => |x| x.writeAtoms(macho_file),
};
}
- pub fn getAtoms(file: File) []const Atom.Index {
+ pub fn calcSymtabSize(file: File, macho_file: *MachO) void {
return switch (file) {
- .dylib => unreachable,
- inline else => |x| x.atoms.items,
+ inline else => |x| x.calcSymtabSize(macho_file),
+ };
+ }
+
+ pub fn writeSymtab(file: File, macho_file: *MachO, ctx: anytype) void {
+ return switch (file) {
+ inline else => |x| x.writeSymtab(macho_file, ctx),
};
}
@@ -198,18 +335,6 @@ pub const File = union(enum) {
};
}
- pub fn calcSymtabSize(file: File, macho_file: *MachO) !void {
- return switch (file) {
- inline else => |x| x.calcSymtabSize(macho_file),
- };
- }
-
- pub fn writeSymtab(file: File, macho_file: *MachO, ctx: anytype) !void {
- return switch (file) {
- inline else => |x| x.writeSymtab(macho_file, ctx),
- };
- }
-
pub const Index = u32;
pub const Entry = union(enum) {
@@ -225,8 +350,10 @@ pub const File = union(enum) {
};
const assert = std.debug.assert;
+const log = std.log.scoped(.link);
const macho = std.macho;
const std = @import("std");
+const trace = @import("../../tracy.zig").trace;
const Allocator = std.mem.Allocator;
const Archive = @import("Archive.zig");
diff --git a/src/link/MachO/relocatable.zig b/src/link/MachO/relocatable.zig
@@ -44,9 +44,7 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]c
if (comp.link_errors.items.len > 0) return error.FlushFailure;
- try macho_file.addUndefinedGlobals();
try macho_file.resolveSymbols();
- try macho_file.parseDebugInfo();
try macho_file.dedupLiterals();
markExports(macho_file);
claimUnresolved(macho_file);
@@ -59,28 +57,13 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]c
try allocateSections(macho_file);
allocateSegment(macho_file);
- var off = off: {
- const seg = macho_file.segments.items[0];
- const off = math.cast(u32, seg.fileoff + seg.filesize) orelse return error.Overflow;
- break :off mem.alignForward(u32, off, @alignOf(macho.relocation_info));
- };
- off = allocateSectionsRelocs(macho_file, off);
-
if (build_options.enable_logging) {
state_log.debug("{}", .{macho_file.dumpState()});
}
- try macho_file.calcSymtabSize();
- try writeAtoms(macho_file);
- try writeCompactUnwind(macho_file);
- try writeEhFrame(macho_file);
-
- off = mem.alignForward(u32, off, @alignOf(u64));
- off = try macho_file.writeDataInCode(0, off);
- off = mem.alignForward(u32, off, @alignOf(u64));
- off = try macho_file.writeSymtab(off);
- off = mem.alignForward(u32, off, @alignOf(u64));
- off = try macho_file.writeStrtab(off);
+ try writeSections(macho_file);
+ sortRelocs(macho_file);
+ try writeSectionsToFile(macho_file);
// In order to please Apple ld (and possibly other MachO linkers in the wild),
// we will now sanitize segment names of Zig-specific segments.
@@ -129,7 +112,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
// First, we flush relocatable object file generated with our backends.
if (macho_file.getZigObject()) |zo| {
- zo.resolveSymbols(macho_file);
+ try zo.resolveSymbols(macho_file);
zo.asFile().markExportsRelocatable(macho_file);
zo.asFile().claimUnresolvedRelocatable(macho_file);
try macho_file.sortSections();
@@ -139,26 +122,13 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
try allocateSections(macho_file);
allocateSegment(macho_file);
- var off = off: {
- const seg = macho_file.segments.items[0];
- const off = math.cast(u32, seg.fileoff + seg.filesize) orelse return error.Overflow;
- break :off mem.alignForward(u32, off, @alignOf(macho.relocation_info));
- };
- off = allocateSectionsRelocs(macho_file, off);
-
if (build_options.enable_logging) {
state_log.debug("{}", .{macho_file.dumpState()});
}
- try macho_file.calcSymtabSize();
- try writeAtoms(macho_file);
-
- off = mem.alignForward(u32, off, @alignOf(u64));
- off = try macho_file.writeDataInCode(0, off);
- off = mem.alignForward(u32, off, @alignOf(u64));
- off = try macho_file.writeSymtab(off);
- off = mem.alignForward(u32, off, @alignOf(u64));
- off = try macho_file.writeStrtab(off);
+ try writeSections(macho_file);
+ sortRelocs(macho_file);
+ try writeSectionsToFile(macho_file);
// In order to please Apple ld (and possibly other MachO linkers in the wild),
// we will now sanitize segment names of Zig-specific segments.
@@ -169,7 +139,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
// TODO we can avoid reading in the file contents we just wrote if we give the linker
// ability to write directly to a buffer.
- try zo.readFileContents(off, macho_file);
+ try zo.readFileContents(macho_file);
}
var files = std.ArrayList(File.Index).init(gpa);
@@ -286,12 +256,15 @@ fn parseObject(macho_file: *MachO, path: []const u8) MachO.ParseError!void {
break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));
};
const index = @as(File.Index, @intCast(try macho_file.files.addOne(gpa)));
- macho_file.files.set(index, .{ .object = .{
- .path = try gpa.dupe(u8, path),
- .file_handle = handle,
- .mtime = mtime,
- .index = index,
- } });
+ macho_file.files.set(index, .{
+ .object = .{
+ .offset = 0, // TODO FAT objects
+ .path = try gpa.dupe(u8, path),
+ .file_handle = handle,
+ .mtime = mtime,
+ .index = index,
+ },
+ });
try macho_file.objects.append(gpa, index);
const object = macho_file.getFile(index).?.object;
@@ -347,9 +320,9 @@ pub fn claimUnresolved(macho_file: *MachO) void {
fn initOutputSections(macho_file: *MachO) !void {
for (macho_file.objects.items) |index| {
- const object = macho_file.getFile(index).?.object;
- for (object.atoms.items) |atom_index| {
- const atom = macho_file.getAtom(atom_index) orelse continue;
+ const file = macho_file.getFile(index).?;
+ for (file.getAtoms()) |atom_index| {
+ const atom = file.getAtom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(macho_file), macho_file);
}
@@ -377,69 +350,147 @@ fn calcSectionSizes(macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
- const slice = macho_file.sections.slice();
- for (slice.items(.header), slice.items(.atoms)) |*header, atoms| {
+ for (macho_file.sections.items(.atoms), 0..) |atoms, i| {
if (atoms.items.len == 0) continue;
- for (atoms.items) |atom_index| {
- const atom = macho_file.getAtom(atom_index).?;
- const atom_alignment = atom.alignment.toByteUnits() orelse 1;
- const offset = mem.alignForward(u64, header.size, atom_alignment);
- const padding = offset - header.size;
- atom.value = offset;
- header.size += padding + atom.size;
- header.@"align" = @max(header.@"align", atom.alignment.toLog2Units());
- header.nreloc += atom.calcNumRelocs(macho_file);
- }
+ calcSectionSize(macho_file, @intCast(i));
}
- if (macho_file.unwind_info_sect_index) |index| {
- calcCompactUnwindSize(macho_file, index);
+ if (macho_file.getZigObject()) |zo| {
+ // TODO this will create a race
+ zo.calcNumRelocs(macho_file);
+ zo.calcSymtabSize(macho_file);
}
- if (macho_file.eh_frame_sect_index) |index| {
- const sect = &macho_file.sections.items(.header)[index];
- sect.size = try eh_frame.calcSize(macho_file);
- sect.@"align" = 3;
- sect.nreloc = eh_frame.calcNumRelocs(macho_file);
+ if (macho_file.eh_frame_sect_index) |_| {
+ try calcEhFrameSize(macho_file);
}
- if (macho_file.getZigObject()) |zo| {
- for (zo.atoms.items) |atom_index| {
- const atom = macho_file.getAtom(atom_index) orelse continue;
- if (!atom.flags.alive) continue;
- const header = &macho_file.sections.items(.header)[atom.out_n_sect];
- if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;
- header.nreloc += atom.calcNumRelocs(macho_file);
+ for (macho_file.objects.items) |index| {
+ if (macho_file.unwind_info_sect_index) |_| {
+ macho_file.getFile(index).?.object.calcCompactUnwindSizeRelocatable(macho_file);
}
+ macho_file.getFile(index).?.calcSymtabSize(macho_file);
}
+
+ try macho_file.data_in_code.updateSize(macho_file);
+
+ if (macho_file.unwind_info_sect_index) |_| {
+ calcCompactUnwindSize(macho_file);
+ }
+ try calcSymtabSize(macho_file);
+}
+
+fn calcSectionSize(macho_file: *MachO, sect_id: u8) void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const slice = macho_file.sections.slice();
+ const header = &slice.items(.header)[sect_id];
+ const atoms = slice.items(.atoms)[sect_id].items;
+ for (atoms) |ref| {
+ const atom = ref.getAtom(macho_file).?;
+ const atom_alignment = atom.alignment.toByteUnits() orelse 1;
+ const offset = mem.alignForward(u64, header.size, atom_alignment);
+ const padding = offset - header.size;
+ atom.value = offset;
+ header.size += padding + atom.size;
+ header.@"align" = @max(header.@"align", atom.alignment.toLog2Units());
+ const nreloc = atom.calcNumRelocs(macho_file);
+ atom.addExtra(.{ .rel_out_index = header.nreloc, .rel_out_count = nreloc }, macho_file);
+ header.nreloc += nreloc;
+ }
+}
+
+fn calcEhFrameSize(macho_file: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const header = &macho_file.sections.items(.header)[macho_file.eh_frame_sect_index.?];
+ header.size = try eh_frame.calcSize(macho_file);
+ header.@"align" = 3;
+ header.nreloc = eh_frame.calcNumRelocs(macho_file);
}
-fn calcCompactUnwindSize(macho_file: *MachO, sect_index: u8) void {
- var size: u32 = 0;
+fn calcCompactUnwindSize(macho_file: *MachO) void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ var nrec: u32 = 0;
var nreloc: u32 = 0;
for (macho_file.objects.items) |index| {
- const object = macho_file.getFile(index).?.object;
- for (object.unwind_records.items) |irec| {
- const rec = macho_file.getUnwindRecord(irec);
- if (!rec.alive) continue;
- size += @sizeOf(macho.compact_unwind_entry);
- nreloc += 1;
- if (rec.getPersonality(macho_file)) |_| {
- nreloc += 1;
- }
- if (rec.getLsdaAtom(macho_file)) |_| {
- nreloc += 1;
- }
- }
+ const ctx = &macho_file.getFile(index).?.object.compact_unwind_ctx;
+ ctx.rec_index = nrec;
+ ctx.reloc_index = nreloc;
+ nrec += ctx.rec_count;
+ nreloc += ctx.reloc_count;
}
- const sect = &macho_file.sections.items(.header)[sect_index];
- sect.size = size;
+ const sect = &macho_file.sections.items(.header)[macho_file.unwind_info_sect_index.?];
+ sect.size = nrec * @sizeOf(macho.compact_unwind_entry);
sect.nreloc = nreloc;
sect.@"align" = 3;
}
+fn calcSymtabSize(macho_file: *MachO) error{OutOfMemory}!void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const gpa = macho_file.base.comp.gpa;
+
+ var nlocals: u32 = 0;
+ var nstabs: u32 = 0;
+ var nexports: u32 = 0;
+ var nimports: u32 = 0;
+ var strsize: u32 = 1;
+
+ var objects = try std.ArrayList(File.Index).initCapacity(gpa, macho_file.objects.items.len + 1);
+ defer objects.deinit();
+ if (macho_file.getZigObject()) |zo| objects.appendAssumeCapacity(zo.index);
+ objects.appendSliceAssumeCapacity(macho_file.objects.items);
+
+ for (objects.items) |index| {
+ const ctx = switch (macho_file.getFile(index).?) {
+ inline else => |x| &x.output_symtab_ctx,
+ };
+ ctx.ilocal = nlocals;
+ ctx.istab = nstabs;
+ ctx.iexport = nexports;
+ ctx.iimport = nimports;
+ ctx.stroff = strsize;
+ nlocals += ctx.nlocals;
+ nstabs += ctx.nstabs;
+ nexports += ctx.nexports;
+ nimports += ctx.nimports;
+ strsize += ctx.strsize;
+ }
+
+ for (objects.items) |index| {
+ const ctx = switch (macho_file.getFile(index).?) {
+ inline else => |x| &x.output_symtab_ctx,
+ };
+ ctx.istab += nlocals;
+ ctx.iexport += nlocals + nstabs;
+ ctx.iimport += nlocals + nstabs + nexports;
+ }
+
+ {
+ const cmd = &macho_file.symtab_cmd;
+ cmd.nsyms = nlocals + nstabs + nexports + nimports;
+ cmd.strsize = strsize;
+ }
+
+ {
+ const cmd = &macho_file.dysymtab_cmd;
+ cmd.ilocalsym = 0;
+ cmd.nlocalsym = nlocals + nstabs;
+ cmd.iextdefsym = nlocals + nstabs;
+ cmd.nextdefsym = nexports;
+ cmd.iundefsym = nlocals + nstabs + nexports;
+ cmd.nundefsym = nimports;
+ }
+}
+
fn allocateSections(macho_file: *MachO) !void {
const slice = macho_file.sections.slice();
for (slice.items(.header)) |*header| {
@@ -457,6 +508,37 @@ fn allocateSections(macho_file: *MachO) !void {
}
header.size = needed_size;
}
+
+ var fileoff: u32 = 0;
+ for (slice.items(.header)) |header| {
+ fileoff = @max(fileoff, header.offset + @as(u32, @intCast(header.size)));
+ }
+
+ for (slice.items(.header)) |*header| {
+ if (header.nreloc == 0) continue;
+ header.reloff = mem.alignForward(u32, fileoff, @alignOf(macho.relocation_info));
+ fileoff = header.reloff + header.nreloc * @sizeOf(macho.relocation_info);
+ }
+
+ // In -r mode, there is no LINKEDIT segment and so we allocate required LINKEDIT commands
+ // as if they were detached or part of the single segment.
+
+ // DATA_IN_CODE
+ {
+ const cmd = &macho_file.data_in_code_cmd;
+ cmd.dataoff = fileoff;
+ fileoff += cmd.datasize;
+ fileoff = mem.alignForward(u32, fileoff, @alignOf(u64));
+ }
+
+ // SYMTAB
+ {
+ const cmd = &macho_file.symtab_cmd;
+ cmd.symoff = fileoff;
+ fileoff += cmd.nsyms * @sizeOf(macho.nlist_64);
+ fileoff = mem.alignForward(u32, fileoff, @alignOf(u32));
+ cmd.stroff = fileoff;
+ }
}
/// Renames segment names in Zig sections to standard MachO segment names such as
@@ -519,232 +601,86 @@ fn allocateSegment(macho_file: *MachO) void {
seg.filesize = fileoff - seg.fileoff;
}
-fn allocateSectionsRelocs(macho_file: *MachO, off: u32) u32 {
- var fileoff = off;
- const slice = macho_file.sections.slice();
- for (slice.items(.header)) |*header| {
- if (header.nreloc == 0) continue;
- header.reloff = mem.alignForward(u32, fileoff, @alignOf(macho.relocation_info));
- fileoff = header.reloff + header.nreloc * @sizeOf(macho.relocation_info);
- }
- return fileoff;
-}
-
// We need to sort relocations in descending order to be compatible with Apple's linker.
fn sortReloc(ctx: void, lhs: macho.relocation_info, rhs: macho.relocation_info) bool {
_ = ctx;
return lhs.r_address > rhs.r_address;
}
-fn writeAtoms(macho_file: *MachO) !void {
+fn sortRelocs(macho_file: *MachO) void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ for (macho_file.sections.items(.relocs)) |*relocs| {
+ mem.sort(macho.relocation_info, relocs.items, {}, sortReloc);
+ }
+}
+
+fn writeSections(macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
const gpa = macho_file.base.comp.gpa;
const cpu_arch = macho_file.getTarget().cpu.arch;
const slice = macho_file.sections.slice();
-
- var relocs = std.ArrayList(macho.relocation_info).init(gpa);
- defer relocs.deinit();
-
- for (slice.items(.header), slice.items(.atoms), 0..) |header, atoms, i| {
- if (atoms.items.len == 0) continue;
+ for (slice.items(.header), slice.items(.out), slice.items(.relocs), 0..) |header, *out, *relocs, n_sect| {
if (header.isZerofill()) continue;
- if (macho_file.isZigSection(@intCast(i)) or macho_file.isDebugSection(@intCast(i))) continue;
-
- const size = math.cast(usize, header.size) orelse return error.Overflow;
- const code = try gpa.alloc(u8, size);
- defer gpa.free(code);
- const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0;
- @memset(code, padding_byte);
-
- try relocs.ensureTotalCapacity(header.nreloc);
-
- for (atoms.items) |atom_index| {
- const atom = macho_file.getAtom(atom_index).?;
- assert(atom.flags.alive);
- const off = math.cast(usize, atom.value) orelse return error.Overflow;
- const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
- try atom.getData(macho_file, code[off..][0..atom_size]);
- try atom.writeRelocs(macho_file, code[off..][0..atom_size], &relocs);
+ if (!macho_file.isZigSection(@intCast(n_sect))) { // TODO this is wrong; what about debug sections?
+ const size = math.cast(usize, header.size) orelse return error.Overflow;
+ try out.resize(gpa, size);
+ const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0;
+ @memset(out.items, padding_byte);
}
+ try relocs.resize(gpa, header.nreloc);
+ }
- assert(relocs.items.len == header.nreloc);
-
- mem.sort(macho.relocation_info, relocs.items, {}, sortReloc);
-
- // TODO scattered writes?
- try macho_file.base.file.?.pwriteAll(code, header.offset);
- try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff);
+ const cmd = macho_file.symtab_cmd;
+ try macho_file.symtab.resize(gpa, cmd.nsyms);
+ try macho_file.strtab.resize(gpa, cmd.strsize);
+ macho_file.strtab.items[0] = 0;
- relocs.clearRetainingCapacity();
+ for (macho_file.objects.items) |index| {
+ try macho_file.getFile(index).?.object.writeAtomsRelocatable(macho_file);
+ macho_file.getFile(index).?.writeSymtab(macho_file, macho_file);
}
if (macho_file.getZigObject()) |zo| {
- // TODO: this is ugly; perhaps we should aggregrate before?
- var zo_relocs = std.AutoArrayHashMap(u8, std.ArrayList(macho.relocation_info)).init(gpa);
- defer {
- for (zo_relocs.values()) |*list| {
- list.deinit();
- }
- zo_relocs.deinit();
- }
-
- for (macho_file.sections.items(.header), 0..) |header, n_sect| {
- if (header.isZerofill()) continue;
- if (!macho_file.isZigSection(@intCast(n_sect)) and !macho_file.isDebugSection(@intCast(n_sect))) continue;
- const gop = try zo_relocs.getOrPut(@intCast(n_sect));
- if (gop.found_existing) continue;
- gop.value_ptr.* = try std.ArrayList(macho.relocation_info).initCapacity(gpa, header.nreloc);
- }
-
- for (zo.atoms.items) |atom_index| {
- const atom = macho_file.getAtom(atom_index) orelse continue;
- if (!atom.flags.alive) continue;
- const header = macho_file.sections.items(.header)[atom.out_n_sect];
- if (header.isZerofill()) continue;
- if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;
- if (atom.getRelocs(macho_file).len == 0) continue;
- const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
- const code = try gpa.alloc(u8, atom_size);
- defer gpa.free(code);
- atom.getData(macho_file, code) catch |err| switch (err) {
- error.InputOutput => {
- try macho_file.reportUnexpectedError("fetching code for '{s}' failed", .{
- atom.getName(macho_file),
- });
- return error.FlushFailure;
- },
- else => |e| {
- try macho_file.reportUnexpectedError("unexpected error while fetching code for '{s}': {s}", .{
- atom.getName(macho_file),
- @errorName(e),
- });
- return error.FlushFailure;
- },
- };
- const file_offset = header.offset + atom.value;
- const rels = zo_relocs.getPtr(atom.out_n_sect).?;
- try atom.writeRelocs(macho_file, code, rels);
- try macho_file.base.file.?.pwriteAll(code, file_offset);
- }
-
- for (zo_relocs.keys(), zo_relocs.values()) |sect_id, rels| {
- const header = macho_file.sections.items(.header)[sect_id];
- assert(rels.items.len == header.nreloc);
- mem.sort(macho.relocation_info, rels.items, {}, sortReloc);
- try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(rels.items), header.reloff);
- }
+ try zo.writeRelocs(macho_file);
+ try zo.writeAtomsRelocatable(macho_file);
+ zo.writeSymtab(macho_file, macho_file);
}
-}
-
-fn writeCompactUnwind(macho_file: *MachO) !void {
- const sect_index = macho_file.unwind_info_sect_index orelse return;
- const gpa = macho_file.base.comp.gpa;
- const header = macho_file.sections.items(.header)[sect_index];
-
- const nrecs = math.cast(usize, @divExact(header.size, @sizeOf(macho.compact_unwind_entry))) orelse return error.Overflow;
- var entries = try std.ArrayList(macho.compact_unwind_entry).initCapacity(gpa, nrecs);
- defer entries.deinit();
-
- var relocs = try std.ArrayList(macho.relocation_info).initCapacity(gpa, header.nreloc);
- defer relocs.deinit();
-
- const addReloc = struct {
- fn addReloc(offset: i32, cpu_arch: std.Target.Cpu.Arch) macho.relocation_info {
- return .{
- .r_address = offset,
- .r_symbolnum = 0,
- .r_pcrel = 0,
- .r_length = 3,
- .r_extern = 0,
- .r_type = switch (cpu_arch) {
- .aarch64 => @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
- .x86_64 => @intFromEnum(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
- else => unreachable,
- },
- };
- }
- }.addReloc;
-
- var offset: i32 = 0;
- for (macho_file.objects.items) |index| {
- const object = macho_file.getFile(index).?.object;
- for (object.unwind_records.items) |irec| {
- const rec = macho_file.getUnwindRecord(irec);
- if (!rec.alive) continue;
-
- var out: macho.compact_unwind_entry = .{
- .rangeStart = 0,
- .rangeLength = rec.length,
- .compactUnwindEncoding = rec.enc.enc,
- .personalityFunction = 0,
- .lsda = 0,
- };
-
- {
- // Function address
- const atom = rec.getAtom(macho_file);
- const addr = rec.getAtomAddress(macho_file);
- out.rangeStart = addr;
- var reloc = addReloc(offset, macho_file.getTarget().cpu.arch);
- reloc.r_symbolnum = atom.out_n_sect + 1;
- relocs.appendAssumeCapacity(reloc);
- }
- // Personality function
- if (rec.getPersonality(macho_file)) |sym| {
- const r_symbolnum = math.cast(u24, sym.getOutputSymtabIndex(macho_file).?) orelse return error.Overflow;
- var reloc = addReloc(offset + 16, macho_file.getTarget().cpu.arch);
- reloc.r_symbolnum = r_symbolnum;
- reloc.r_extern = 1;
- relocs.appendAssumeCapacity(reloc);
- }
-
- // LSDA address
- if (rec.getLsdaAtom(macho_file)) |atom| {
- const addr = rec.getLsdaAddress(macho_file);
- out.lsda = addr;
- var reloc = addReloc(offset + 24, macho_file.getTarget().cpu.arch);
- reloc.r_symbolnum = atom.out_n_sect + 1;
- relocs.appendAssumeCapacity(reloc);
- }
+ if (macho_file.eh_frame_sect_index) |_| {
+ try writeEhFrame(macho_file);
+ }
- entries.appendAssumeCapacity(out);
- offset += @sizeOf(macho.compact_unwind_entry);
+ if (macho_file.unwind_info_sect_index) |_| {
+ for (macho_file.objects.items) |index| {
+ try macho_file.getFile(index).?.object.writeCompactUnwindRelocatable(macho_file);
}
}
-
- assert(entries.items.len == nrecs);
- assert(relocs.items.len == header.nreloc);
-
- mem.sort(macho.relocation_info, relocs.items, {}, sortReloc);
-
- // TODO scattered writes?
- try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(entries.items), header.offset);
- try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff);
}
fn writeEhFrame(macho_file: *MachO) !void {
- const sect_index = macho_file.eh_frame_sect_index orelse return;
- const gpa = macho_file.base.comp.gpa;
- const header = macho_file.sections.items(.header)[sect_index];
- const size = math.cast(usize, header.size) orelse return error.Overflow;
-
- const code = try gpa.alloc(u8, size);
- defer gpa.free(code);
-
- var relocs = try std.ArrayList(macho.relocation_info).initCapacity(gpa, header.nreloc);
- defer relocs.deinit();
+ const sect_index = macho_file.eh_frame_sect_index.?;
+ const buffer = macho_file.sections.items(.out)[sect_index];
+ const relocs = macho_file.sections.items(.relocs)[sect_index];
+ try eh_frame.writeRelocs(macho_file, buffer.items, relocs.items);
+}
- try eh_frame.writeRelocs(macho_file, code, &relocs);
- assert(relocs.items.len == header.nreloc);
+fn writeSectionsToFile(macho_file: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
- mem.sort(macho.relocation_info, relocs.items, {}, sortReloc);
+ const slice = macho_file.sections.slice();
+ for (slice.items(.header), slice.items(.out), slice.items(.relocs)) |header, out, relocs| {
+ try macho_file.base.file.?.pwriteAll(out.items, header.offset);
+ try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff);
+ }
- // TODO scattered writes?
- try macho_file.base.file.?.pwriteAll(code, header.offset);
- try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff);
+ try macho_file.writeDataInCode();
+ try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(macho_file.symtab.items), macho_file.symtab_cmd.symoff);
+ try macho_file.base.file.?.pwriteAll(macho_file.strtab.items, macho_file.symtab_cmd.stroff);
}
fn writeLoadCommands(macho_file: *MachO) !struct { usize, usize } {
diff --git a/src/link/MachO/synthetic.zig b/src/link/MachO/synthetic.zig
@@ -18,15 +18,15 @@ pub const ZigGotSection = struct {
}
pub fn addSymbol(zig_got: *ZigGotSection, sym_index: Symbol.Index, macho_file: *MachO) !Index {
- const comp = macho_file.base.comp;
- const gpa = comp.gpa;
+ const gpa = macho_file.base.comp.gpa;
+ const zo = macho_file.getZigObject().?;
const index = try zig_got.allocateEntry(gpa);
const entry = &zig_got.entries.items[index];
entry.* = sym_index;
- const symbol = macho_file.getSymbol(sym_index);
+ const symbol = &zo.symbols.items[sym_index];
assert(symbol.flags.needs_zig_got);
symbol.flags.has_zig_got = true;
- try symbol.addExtra(.{ .zig_got = index }, macho_file);
+ symbol.addExtra(.{ .zig_got = index }, macho_file);
return index;
}
@@ -53,9 +53,10 @@ pub const ZigGotSection = struct {
try macho_file.growSection(macho_file.zig_got_sect_index.?, needed_size);
zig_got.dirty = false;
}
+ const zo = macho_file.getZigObject().?;
const off = zig_got.entryOffset(index, macho_file);
const entry = zig_got.entries.items[index];
- const value = macho_file.getSymbol(entry).getAddress(.{ .stubs = false }, macho_file);
+ const value = zo.symbols.items[entry].getAddress(.{ .stubs = false }, macho_file);
var buf: [8]u8 = undefined;
std.mem.writeInt(u64, &buf, value, .little);
@@ -63,29 +64,14 @@ pub const ZigGotSection = struct {
}
pub fn writeAll(zig_got: ZigGotSection, macho_file: *MachO, writer: anytype) !void {
+ const zo = macho_file.getZigObject().?;
for (zig_got.entries.items) |entry| {
- const symbol = macho_file.getSymbol(entry);
+ const symbol = zo.symbols.items[entry];
const value = symbol.address(.{ .stubs = false }, macho_file);
try writer.writeInt(u64, value, .little);
}
}
- pub fn addDyldRelocs(zig_got: ZigGotSection, macho_file: *MachO) !void {
- const tracy = trace(@src());
- defer tracy.end();
- const gpa = macho_file.base.comp.gpa;
- const seg_id = macho_file.sections.items(.segment_id)[macho_file.zig_got_sect_index.?];
- const seg = macho_file.segments.items[seg_id];
-
- for (0..zig_got.entries.items.len) |idx| {
- const addr = zig_got.entryAddress(@intCast(idx), macho_file);
- try macho_file.rebase.entries.append(gpa, .{
- .offset = addr - seg.vmaddr,
- .segment_id = seg_id,
- });
- }
- }
-
const FormatCtx = struct {
zig_got: ZigGotSection,
macho_file: *MachO,
@@ -103,22 +89,25 @@ pub const ZigGotSection = struct {
) !void {
_ = options;
_ = unused_fmt_string;
+ const zig_got = ctx.zig_got;
+ const macho_file = ctx.macho_file;
try writer.writeAll("__zig_got\n");
- for (ctx.zig_got.entries.items, 0..) |entry, index| {
- const symbol = ctx.macho_file.getSymbol(entry);
+ for (zig_got.entries.items, 0..) |entry, index| {
+ const zo = macho_file.getZigObject().?;
+ const symbol = zo.symbols.items[entry];
try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{
index,
- ctx.zig_got.entryAddress(@intCast(index), ctx.macho_file),
+ zig_got.entryAddress(@intCast(index), macho_file),
entry,
- symbol.getAddress(.{}, ctx.macho_file),
- symbol.getName(ctx.macho_file),
+ symbol.getAddress(.{}, macho_file),
+ symbol.getName(macho_file),
});
}
}
};
pub const GotSection = struct {
- symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
+ symbols: std.ArrayListUnmanaged(MachO.Ref) = .{},
pub const Index = u32;
@@ -126,14 +115,14 @@ pub const GotSection = struct {
got.symbols.deinit(allocator);
}
- pub fn addSymbol(got: *GotSection, sym_index: Symbol.Index, macho_file: *MachO) !void {
+ pub fn addSymbol(got: *GotSection, ref: MachO.Ref, macho_file: *MachO) !void {
const gpa = macho_file.base.comp.gpa;
const index = @as(Index, @intCast(got.symbols.items.len));
const entry = try got.symbols.addOne(gpa);
- entry.* = sym_index;
- const symbol = macho_file.getSymbol(sym_index);
+ entry.* = ref;
+ const symbol = ref.getSymbol(macho_file).?;
symbol.flags.has_got = true;
- try symbol.addExtra(.{ .got = index }, macho_file);
+ symbol.addExtra(.{ .got = index }, macho_file);
}
pub fn getAddress(got: GotSection, index: Index, macho_file: *MachO) u64 {
@@ -146,46 +135,11 @@ pub const GotSection = struct {
return got.symbols.items.len * @sizeOf(u64);
}
- pub fn addDyldRelocs(got: GotSection, macho_file: *MachO) !void {
- const tracy = trace(@src());
- defer tracy.end();
- const gpa = macho_file.base.comp.gpa;
- const seg_id = macho_file.sections.items(.segment_id)[macho_file.got_sect_index.?];
- const seg = macho_file.segments.items[seg_id];
-
- for (got.symbols.items, 0..) |sym_index, idx| {
- const sym = macho_file.getSymbol(sym_index);
- const addr = got.getAddress(@intCast(idx), macho_file);
- const entry = bind.Entry{
- .target = sym_index,
- .offset = addr - seg.vmaddr,
- .segment_id = seg_id,
- .addend = 0,
- };
- if (sym.flags.import) {
- try macho_file.bind.entries.append(gpa, entry);
- if (sym.flags.weak) {
- try macho_file.weak_bind.entries.append(gpa, entry);
- }
- } else {
- try macho_file.rebase.entries.append(gpa, .{
- .offset = addr - seg.vmaddr,
- .segment_id = seg_id,
- });
- if (sym.flags.weak) {
- try macho_file.weak_bind.entries.append(gpa, entry);
- } else if (sym.flags.interposable) {
- try macho_file.bind.entries.append(gpa, entry);
- }
- }
- }
- }
-
pub fn write(got: GotSection, macho_file: *MachO, writer: anytype) !void {
const tracy = trace(@src());
defer tracy.end();
- for (got.symbols.items) |sym_index| {
- const sym = macho_file.getSymbol(sym_index);
+ for (got.symbols.items) |ref| {
+ const sym = ref.getSymbol(macho_file).?;
const value = if (sym.flags.import) @as(u64, 0) else sym.getAddress(.{}, macho_file);
try writer.writeInt(u64, value, .little);
}
@@ -208,12 +162,12 @@ pub const GotSection = struct {
) !void {
_ = options;
_ = unused_fmt_string;
- for (ctx.got.symbols.items, 0..) |entry, i| {
- const symbol = ctx.macho_file.getSymbol(entry);
+ for (ctx.got.symbols.items, 0..) |ref, i| {
+ const symbol = ref.getSymbol(ctx.macho_file).?;
try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{
i,
symbol.getGotAddress(ctx.macho_file),
- entry,
+ ref,
symbol.getAddress(.{}, ctx.macho_file),
symbol.getName(ctx.macho_file),
});
@@ -222,7 +176,7 @@ pub const GotSection = struct {
};
pub const StubsSection = struct {
- symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
+ symbols: std.ArrayListUnmanaged(MachO.Ref) = .{},
pub const Index = u32;
@@ -230,13 +184,13 @@ pub const StubsSection = struct {
stubs.symbols.deinit(allocator);
}
- pub fn addSymbol(stubs: *StubsSection, sym_index: Symbol.Index, macho_file: *MachO) !void {
+ pub fn addSymbol(stubs: *StubsSection, ref: MachO.Ref, macho_file: *MachO) !void {
const gpa = macho_file.base.comp.gpa;
const index = @as(Index, @intCast(stubs.symbols.items.len));
const entry = try stubs.symbols.addOne(gpa);
- entry.* = sym_index;
- const symbol = macho_file.getSymbol(sym_index);
- try symbol.addExtra(.{ .stubs = index }, macho_file);
+ entry.* = ref;
+ const symbol = ref.getSymbol(macho_file).?;
+ symbol.addExtra(.{ .stubs = index }, macho_file);
}
pub fn getAddress(stubs: StubsSection, index: Index, macho_file: *MachO) u64 {
@@ -256,8 +210,8 @@ pub const StubsSection = struct {
const cpu_arch = macho_file.getTarget().cpu.arch;
const laptr_sect = macho_file.sections.items(.header)[macho_file.la_symbol_ptr_sect_index.?];
- for (stubs.symbols.items, 0..) |sym_index, idx| {
- const sym = macho_file.getSymbol(sym_index);
+ for (stubs.symbols.items, 0..) |ref, idx| {
+ const sym = ref.getSymbol(macho_file).?;
const source = sym.getAddress(.{ .stubs = true }, macho_file);
const target = laptr_sect.addr + idx * @sizeOf(u64);
switch (cpu_arch) {
@@ -299,12 +253,12 @@ pub const StubsSection = struct {
) !void {
_ = options;
_ = unused_fmt_string;
- for (ctx.stubs.symbols.items, 0..) |entry, i| {
- const symbol = ctx.macho_file.getSymbol(entry);
+ for (ctx.stubs.symbols.items, 0..) |ref, i| {
+ const symbol = ref.getSymbol(ctx.macho_file).?;
try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{
i,
symbol.getStubsAddress(ctx.macho_file),
- entry,
+ ref,
symbol.getAddress(.{}, ctx.macho_file),
symbol.getName(ctx.macho_file),
});
@@ -335,8 +289,8 @@ pub const StubsHelperSection = struct {
_ = stubs_helper;
const cpu_arch = macho_file.getTarget().cpu.arch;
var s: usize = preambleSize(cpu_arch);
- for (macho_file.stubs.symbols.items) |sym_index| {
- const sym = macho_file.getSymbol(sym_index);
+ for (macho_file.stubs.symbols.items) |ref| {
+ const sym = ref.getSymbol(macho_file).?;
if (sym.flags.weak) continue;
s += entrySize(cpu_arch);
}
@@ -355,8 +309,8 @@ pub const StubsHelperSection = struct {
const entry_size = entrySize(cpu_arch);
var idx: usize = 0;
- for (macho_file.stubs.symbols.items) |sym_index| {
- const sym = macho_file.getSymbol(sym_index);
+ for (macho_file.stubs.symbols.items) |ref| {
+ const sym = ref.getSymbol(macho_file).?;
if (sym.flags.weak) continue;
const offset = macho_file.lazy_bind.offsets.items[idx];
const source: i64 = @intCast(sect.addr + preamble_size + entry_size * idx);
@@ -390,14 +344,15 @@ pub const StubsHelperSection = struct {
fn writePreamble(stubs_helper: StubsHelperSection, macho_file: *MachO, writer: anytype) !void {
_ = stubs_helper;
+ const obj = macho_file.getInternalObject().?;
const cpu_arch = macho_file.getTarget().cpu.arch;
const sect = macho_file.sections.items(.header)[macho_file.stubs_helper_sect_index.?];
const dyld_private_addr = target: {
- const sym = macho_file.getSymbol(macho_file.dyld_private_index.?);
+ const sym = obj.getDyldPrivateRef(macho_file).?.getSymbol(macho_file).?;
break :target sym.getAddress(.{}, macho_file);
};
const dyld_stub_binder_addr = target: {
- const sym = macho_file.getSymbol(macho_file.dyld_stub_binder_index.?);
+ const sym = obj.getDyldStubBinderRef(macho_file).?.getSymbol(macho_file).?;
break :target sym.getGotAddress(macho_file);
};
switch (cpu_arch) {
@@ -446,49 +401,6 @@ pub const LaSymbolPtrSection = struct {
return macho_file.stubs.symbols.items.len * @sizeOf(u64);
}
- pub fn addDyldRelocs(laptr: LaSymbolPtrSection, macho_file: *MachO) !void {
- const tracy = trace(@src());
- defer tracy.end();
- _ = laptr;
- const gpa = macho_file.base.comp.gpa;
-
- const sect = macho_file.sections.items(.header)[macho_file.la_symbol_ptr_sect_index.?];
- const seg_id = macho_file.sections.items(.segment_id)[macho_file.la_symbol_ptr_sect_index.?];
- const seg = macho_file.segments.items[seg_id];
-
- for (macho_file.stubs.symbols.items, 0..) |sym_index, idx| {
- const sym = macho_file.getSymbol(sym_index);
- const addr = sect.addr + idx * @sizeOf(u64);
- const rebase_entry = Rebase.Entry{
- .offset = addr - seg.vmaddr,
- .segment_id = seg_id,
- };
- const bind_entry = bind.Entry{
- .target = sym_index,
- .offset = addr - seg.vmaddr,
- .segment_id = seg_id,
- .addend = 0,
- };
- if (sym.flags.import) {
- if (sym.flags.weak) {
- try macho_file.bind.entries.append(gpa, bind_entry);
- try macho_file.weak_bind.entries.append(gpa, bind_entry);
- } else {
- try macho_file.lazy_bind.entries.append(gpa, bind_entry);
- try macho_file.rebase.entries.append(gpa, rebase_entry);
- }
- } else {
- if (sym.flags.weak) {
- try macho_file.rebase.entries.append(gpa, rebase_entry);
- try macho_file.weak_bind.entries.append(gpa, bind_entry);
- } else if (sym.flags.interposable) {
- try macho_file.lazy_bind.entries.append(gpa, bind_entry);
- try macho_file.rebase.entries.append(gpa, rebase_entry);
- }
- }
- }
- }
-
pub fn write(laptr: LaSymbolPtrSection, macho_file: *MachO, writer: anytype) !void {
const tracy = trace(@src());
defer tracy.end();
@@ -496,8 +408,8 @@ pub const LaSymbolPtrSection = struct {
const cpu_arch = macho_file.getTarget().cpu.arch;
const sect = macho_file.sections.items(.header)[macho_file.stubs_helper_sect_index.?];
var stub_helper_idx: u32 = 0;
- for (macho_file.stubs.symbols.items) |sym_index| {
- const sym = macho_file.getSymbol(sym_index);
+ for (macho_file.stubs.symbols.items) |ref| {
+ const sym = ref.getSymbol(macho_file).?;
if (sym.flags.weak) {
const value = sym.getAddress(.{ .stubs = false }, macho_file);
try writer.writeInt(u64, @intCast(value), .little);
@@ -512,7 +424,7 @@ pub const LaSymbolPtrSection = struct {
};
pub const TlvPtrSection = struct {
- symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
+ symbols: std.ArrayListUnmanaged(MachO.Ref) = .{},
pub const Index = u32;
@@ -520,13 +432,13 @@ pub const TlvPtrSection = struct {
tlv.symbols.deinit(allocator);
}
- pub fn addSymbol(tlv: *TlvPtrSection, sym_index: Symbol.Index, macho_file: *MachO) !void {
+ pub fn addSymbol(tlv: *TlvPtrSection, ref: MachO.Ref, macho_file: *MachO) !void {
const gpa = macho_file.base.comp.gpa;
const index = @as(Index, @intCast(tlv.symbols.items.len));
const entry = try tlv.symbols.addOne(gpa);
- entry.* = sym_index;
- const symbol = macho_file.getSymbol(sym_index);
- try symbol.addExtra(.{ .tlv_ptr = index }, macho_file);
+ entry.* = ref;
+ const symbol = ref.getSymbol(macho_file).?;
+ symbol.addExtra(.{ .tlv_ptr = index }, macho_file);
}
pub fn getAddress(tlv: TlvPtrSection, index: Index, macho_file: *MachO) u64 {
@@ -539,47 +451,12 @@ pub const TlvPtrSection = struct {
return tlv.symbols.items.len * @sizeOf(u64);
}
- pub fn addDyldRelocs(tlv: TlvPtrSection, macho_file: *MachO) !void {
- const tracy = trace(@src());
- defer tracy.end();
- const gpa = macho_file.base.comp.gpa;
- const seg_id = macho_file.sections.items(.segment_id)[macho_file.tlv_ptr_sect_index.?];
- const seg = macho_file.segments.items[seg_id];
-
- for (tlv.symbols.items, 0..) |sym_index, idx| {
- const sym = macho_file.getSymbol(sym_index);
- const addr = tlv.getAddress(@intCast(idx), macho_file);
- const entry = bind.Entry{
- .target = sym_index,
- .offset = addr - seg.vmaddr,
- .segment_id = seg_id,
- .addend = 0,
- };
- if (sym.flags.import) {
- try macho_file.bind.entries.append(gpa, entry);
- if (sym.flags.weak) {
- try macho_file.weak_bind.entries.append(gpa, entry);
- }
- } else {
- try macho_file.rebase.entries.append(gpa, .{
- .offset = addr - seg.vmaddr,
- .segment_id = seg_id,
- });
- if (sym.flags.weak) {
- try macho_file.weak_bind.entries.append(gpa, entry);
- } else if (sym.flags.interposable) {
- try macho_file.bind.entries.append(gpa, entry);
- }
- }
- }
- }
-
pub fn write(tlv: TlvPtrSection, macho_file: *MachO, writer: anytype) !void {
const tracy = trace(@src());
defer tracy.end();
- for (tlv.symbols.items) |sym_index| {
- const sym = macho_file.getSymbol(sym_index);
+ for (tlv.symbols.items) |ref| {
+ const sym = ref.getSymbol(macho_file).?;
if (sym.flags.import) {
try writer.writeInt(u64, 0, .little);
} else {
@@ -605,12 +482,12 @@ pub const TlvPtrSection = struct {
) !void {
_ = options;
_ = unused_fmt_string;
- for (ctx.tlv.symbols.items, 0..) |entry, i| {
- const symbol = ctx.macho_file.getSymbol(entry);
+ for (ctx.tlv.symbols.items, 0..) |ref, i| {
+ const symbol = ref.getSymbol(ctx.macho_file).?;
try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{
i,
symbol.getTlvPtrAddress(ctx.macho_file),
- entry,
+ ref,
symbol.getAddress(.{}, ctx.macho_file),
symbol.getName(ctx.macho_file),
});
@@ -619,7 +496,7 @@ pub const TlvPtrSection = struct {
};
pub const ObjcStubsSection = struct {
- symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
+ symbols: std.ArrayListUnmanaged(MachO.Ref) = .{},
pub fn deinit(objc: *ObjcStubsSection, allocator: Allocator) void {
objc.symbols.deinit(allocator);
@@ -633,13 +510,13 @@ pub const ObjcStubsSection = struct {
};
}
- pub fn addSymbol(objc: *ObjcStubsSection, sym_index: Symbol.Index, macho_file: *MachO) !void {
+ pub fn addSymbol(objc: *ObjcStubsSection, ref: MachO.Ref, macho_file: *MachO) !void {
const gpa = macho_file.base.comp.gpa;
const index = @as(Index, @intCast(objc.symbols.items.len));
const entry = try objc.symbols.addOne(gpa);
- entry.* = sym_index;
- const symbol = macho_file.getSymbol(sym_index);
- try symbol.addExtra(.{ .objc_stubs = index }, macho_file);
+ entry.* = ref;
+ const symbol = ref.getSymbol(macho_file).?;
+ symbol.addExtra(.{ .objc_stubs = index }, macho_file);
}
pub fn getAddress(objc: ObjcStubsSection, index: Index, macho_file: *MachO) u64 {
@@ -656,8 +533,10 @@ pub const ObjcStubsSection = struct {
const tracy = trace(@src());
defer tracy.end();
- for (objc.symbols.items, 0..) |sym_index, idx| {
- const sym = macho_file.getSymbol(sym_index);
+ const obj = macho_file.getInternalObject().?;
+
+ for (objc.symbols.items, 0..) |ref, idx| {
+ const sym = ref.getSymbol(macho_file).?;
const addr = objc.getAddress(@intCast(idx), macho_file);
switch (macho_file.getTarget().cpu.arch) {
.x86_64 => {
@@ -669,7 +548,7 @@ pub const ObjcStubsSection = struct {
}
try writer.writeAll(&.{ 0xff, 0x25 });
{
- const target_sym = macho_file.getSymbol(macho_file.objc_msg_send_index.?);
+ const target_sym = obj.getObjcMsgSendRef(macho_file).?.getSymbol(macho_file).?;
const target = target_sym.getGotAddress(macho_file);
const source = addr + 7;
try writer.writeInt(i32, @intCast(target - source - 2 - 4), .little);
@@ -689,7 +568,7 @@ pub const ObjcStubsSection = struct {
);
}
{
- const target_sym = macho_file.getSymbol(macho_file.objc_msg_send_index.?);
+ const target_sym = obj.getObjcMsgSendRef(macho_file).?.getSymbol(macho_file).?;
const target = target_sym.getGotAddress(macho_file);
const source = addr + 2 * @sizeOf(u32);
const pages = try aarch64.calcNumberOfPages(@intCast(source), @intCast(target));
@@ -728,12 +607,12 @@ pub const ObjcStubsSection = struct {
) !void {
_ = options;
_ = unused_fmt_string;
- for (ctx.objc.symbols.items, 0..) |entry, i| {
- const symbol = ctx.macho_file.getSymbol(entry);
+ for (ctx.objc.symbols.items, 0..) |ref, i| {
+ const symbol = ref.getSymbol(ctx.macho_file).?;
try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{
i,
symbol.getObjcStubsAddress(ctx.macho_file),
- entry,
+ ref,
symbol.getAddress(.{}, ctx.macho_file),
symbol.getName(ctx.macho_file),
});
@@ -749,44 +628,112 @@ pub const Indsymtab = struct {
return @intCast(macho_file.stubs.symbols.items.len * 2 + macho_file.got.symbols.items.len);
}
+ pub fn updateSize(ind: *Indsymtab, macho_file: *MachO) !void {
+ macho_file.dysymtab_cmd.nindirectsyms = ind.nsyms(macho_file);
+ }
+
pub fn write(ind: Indsymtab, macho_file: *MachO, writer: anytype) !void {
const tracy = trace(@src());
defer tracy.end();
_ = ind;
- for (macho_file.stubs.symbols.items) |sym_index| {
- const sym = macho_file.getSymbol(sym_index);
+ for (macho_file.stubs.symbols.items) |ref| {
+ const sym = ref.getSymbol(macho_file).?;
try writer.writeInt(u32, sym.getOutputSymtabIndex(macho_file).?, .little);
}
- for (macho_file.got.symbols.items) |sym_index| {
- const sym = macho_file.getSymbol(sym_index);
+ for (macho_file.got.symbols.items) |ref| {
+ const sym = ref.getSymbol(macho_file).?;
try writer.writeInt(u32, sym.getOutputSymtabIndex(macho_file).?, .little);
}
- for (macho_file.stubs.symbols.items) |sym_index| {
- const sym = macho_file.getSymbol(sym_index);
+ for (macho_file.stubs.symbols.items) |ref| {
+ const sym = ref.getSymbol(macho_file).?;
try writer.writeInt(u32, sym.getOutputSymtabIndex(macho_file).?, .little);
}
}
};
-pub const RebaseSection = Rebase;
-pub const BindSection = bind.Bind;
-pub const WeakBindSection = bind.WeakBind;
-pub const LazyBindSection = bind.LazyBind;
-pub const ExportTrieSection = Trie;
+pub const DataInCode = struct {
+ entries: std.ArrayListUnmanaged(Entry) = .{},
+
+ pub fn deinit(dice: *DataInCode, allocator: Allocator) void {
+ dice.entries.deinit(allocator);
+ }
+
+ pub fn size(dice: DataInCode) usize {
+ return dice.entries.items.len * @sizeOf(macho.data_in_code_entry);
+ }
+
+ pub fn updateSize(dice: *DataInCode, macho_file: *MachO) !void {
+ const gpa = macho_file.base.comp.gpa;
+
+ for (macho_file.objects.items) |index| {
+ const object = macho_file.getFile(index).?.object;
+ const dices = object.getDataInCode();
+
+ try dice.entries.ensureUnusedCapacity(gpa, dices.len);
+
+ var next_dice: usize = 0;
+ for (object.getAtoms()) |atom_index| {
+ if (next_dice >= dices.len) break;
+ const atom = object.getAtom(atom_index) orelse continue;
+ const start_off = atom.getInputAddress(macho_file);
+ const end_off = start_off + atom.size;
+ const start_dice = next_dice;
+
+ if (end_off < dices[next_dice].offset) continue;
+
+ while (next_dice < dices.len and
+ dices[next_dice].offset < end_off) : (next_dice += 1)
+ {}
+
+ if (atom.flags.alive) for (dices[start_dice..next_dice]) |d| {
+ dice.entries.appendAssumeCapacity(.{
+ .atom_ref = .{ .index = atom_index, .file = index },
+ .offset = @intCast(d.offset - start_off),
+ .length = d.length,
+ .kind = d.kind,
+ });
+ };
+ }
+ }
+
+ macho_file.data_in_code_cmd.datasize = math.cast(u32, dice.size()) orelse return error.Overflow;
+ }
+
+ pub fn write(dice: DataInCode, macho_file: *MachO, writer: anytype) !void {
+ const base_address = if (!macho_file.base.isRelocatable())
+ macho_file.getTextSegment().vmaddr
+ else
+ 0;
+ for (dice.entries.items) |entry| {
+ const atom_address = entry.atom_ref.getAtom(macho_file).?.getAddress(macho_file);
+ const offset = atom_address + entry.offset - base_address;
+ try writer.writeStruct(macho.data_in_code_entry{
+ .offset = @intCast(offset),
+ .length = entry.length,
+ .kind = entry.kind,
+ });
+ }
+ }
+
+ const Entry = struct {
+ atom_ref: MachO.Ref,
+ offset: u32,
+ length: u16,
+ kind: u16,
+ };
+};
const aarch64 = @import("../aarch64.zig");
const assert = std.debug.assert;
-const bind = @import("dyld_info/bind.zig");
+const macho = std.macho;
const math = std.math;
const std = @import("std");
const trace = @import("../../tracy.zig").trace;
const Allocator = std.mem.Allocator;
const MachO = @import("../MachO.zig");
-const Rebase = @import("dyld_info/Rebase.zig");
const Symbol = @import("Symbol.zig");
-const Trie = @import("dyld_info/Trie.zig");
diff --git a/src/link/MachO/thunks.zig b/src/link/MachO/thunks.zig
@@ -5,55 +5,45 @@ pub fn createThunks(sect_id: u8, macho_file: *MachO) !void {
const gpa = macho_file.base.comp.gpa;
const slice = macho_file.sections.slice();
const header = &slice.items(.header)[sect_id];
+ const thnks = &slice.items(.thunks)[sect_id];
const atoms = slice.items(.atoms)[sect_id].items;
assert(atoms.len > 0);
- for (atoms) |atom_index| {
- macho_file.getAtom(atom_index).?.value = @bitCast(@as(i64, -1));
+ for (atoms) |ref| {
+ ref.getAtom(macho_file).?.value = @bitCast(@as(i64, -1));
}
var i: usize = 0;
while (i < atoms.len) {
const start = i;
- const start_atom = macho_file.getAtom(atoms[start]).?;
+ const start_atom = atoms[start].getAtom(macho_file).?;
assert(start_atom.flags.alive);
- start_atom.value = try advance(header, start_atom.size, start_atom.alignment);
+ start_atom.value = advance(header, start_atom.size, start_atom.alignment);
i += 1;
while (i < atoms.len and
header.size - start_atom.value < max_allowed_distance) : (i += 1)
{
- const atom_index = atoms[i];
- const atom = macho_file.getAtom(atom_index).?;
+ const atom = atoms[i].getAtom(macho_file).?;
assert(atom.flags.alive);
- atom.value = try advance(header, atom.size, atom.alignment);
+ atom.value = advance(header, atom.size, atom.alignment);
}
// Insert a thunk at the group end
const thunk_index = try macho_file.addThunk();
const thunk = macho_file.getThunk(thunk_index);
thunk.out_n_sect = sect_id;
+ try thnks.append(gpa, thunk_index);
// Scan relocs in the group and create trampolines for any unreachable callsite
- for (atoms[start..i]) |atom_index| {
- const atom = macho_file.getAtom(atom_index).?;
- log.debug("atom({d}) {s}", .{ atom_index, atom.getName(macho_file) });
- for (atom.getRelocs(macho_file)) |rel| {
- if (rel.type != .branch) continue;
- if (isReachable(atom, rel, macho_file)) continue;
- try thunk.symbols.put(gpa, rel.target, {});
- }
- try atom.addExtra(.{ .thunk = thunk_index }, macho_file);
- atom.flags.thunk = true;
- }
-
- thunk.value = try advance(header, thunk.size(), .@"4");
+ try scanRelocs(thunk_index, gpa, atoms[start..i], macho_file);
+ thunk.value = advance(header, thunk.size(), .@"4");
log.debug("thunk({d}) : {}", .{ thunk_index, thunk.fmt(macho_file) });
}
}
-fn advance(sect: *macho.section_64, size: u64, alignment: Atom.Alignment) !u64 {
+fn advance(sect: *macho.section_64, size: u64, alignment: Atom.Alignment) u64 {
const offset = alignment.forward(sect.size);
const padding = offset - sect.size;
sect.size += padding + size;
@@ -61,14 +51,32 @@ fn advance(sect: *macho.section_64, size: u64, alignment: Atom.Alignment) !u64 {
return offset;
}
+fn scanRelocs(thunk_index: Thunk.Index, gpa: Allocator, atoms: []const MachO.Ref, macho_file: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const thunk = macho_file.getThunk(thunk_index);
+
+ for (atoms) |ref| {
+ const atom = ref.getAtom(macho_file).?;
+ log.debug("atom({d}) {s}", .{ atom.atom_index, atom.getName(macho_file) });
+ for (atom.getRelocs(macho_file)) |rel| {
+ if (rel.type != .branch) continue;
+ if (isReachable(atom, rel, macho_file)) continue;
+ try thunk.symbols.put(gpa, rel.getTargetSymbolRef(atom.*, macho_file), {});
+ }
+ atom.addExtra(.{ .thunk = thunk_index }, macho_file);
+ }
+}
+
fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool {
- const target = rel.getTargetSymbol(macho_file);
+ const target = rel.getTargetSymbol(atom.*, macho_file);
if (target.flags.stubs or target.flags.objc_stubs) return false;
- if (atom.out_n_sect != target.out_n_sect) return false;
+ if (atom.out_n_sect != target.getOutputSectionIndex(macho_file)) return false;
const target_atom = target.getAtom(macho_file).?;
if (target_atom.value == @as(u64, @bitCast(@as(i64, -1)))) return false;
const saddr = @as(i64, @intCast(atom.getAddress(macho_file))) + @as(i64, @intCast(rel.offset - atom.off));
- const taddr: i64 = @intCast(rel.getTargetAddress(macho_file));
+ const taddr: i64 = @intCast(rel.getTargetAddress(atom.*, macho_file));
_ = math.cast(i28, taddr + rel.addend - saddr) orelse return false;
return true;
}
@@ -76,7 +84,7 @@ fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool {
pub const Thunk = struct {
value: u64 = 0,
out_n_sect: u8 = 0,
- symbols: std.AutoArrayHashMapUnmanaged(Symbol.Index, void) = .{},
+ symbols: std.AutoArrayHashMapUnmanaged(MachO.Ref, void) = .{},
pub fn deinit(thunk: *Thunk, allocator: Allocator) void {
thunk.symbols.deinit(allocator);
@@ -91,13 +99,13 @@ pub const Thunk = struct {
return header.addr + thunk.value;
}
- pub fn getTargetAddress(thunk: Thunk, sym_index: Symbol.Index, macho_file: *MachO) u64 {
- return thunk.getAddress(macho_file) + thunk.symbols.getIndex(sym_index).? * trampoline_size;
+ pub fn getTargetAddress(thunk: Thunk, ref: MachO.Ref, macho_file: *MachO) u64 {
+ return thunk.getAddress(macho_file) + thunk.symbols.getIndex(ref).? * trampoline_size;
}
pub fn write(thunk: Thunk, macho_file: *MachO, writer: anytype) !void {
- for (thunk.symbols.keys(), 0..) |sym_index, i| {
- const sym = macho_file.getSymbol(sym_index);
+ for (thunk.symbols.keys(), 0..) |ref, i| {
+ const sym = ref.getSymbol(macho_file).?;
const saddr = thunk.getAddress(macho_file) + i * trampoline_size;
const taddr = sym.getAddress(.{}, macho_file);
const pages = try aarch64.calcNumberOfPages(@intCast(saddr), @intCast(taddr));
@@ -144,9 +152,9 @@ pub const Thunk = struct {
const thunk = ctx.thunk;
const macho_file = ctx.macho_file;
try writer.print("@{x} : size({x})\n", .{ thunk.value, thunk.size() });
- for (thunk.symbols.keys()) |index| {
- const sym = macho_file.getSymbol(index);
- try writer.print(" %{d} : {s} : @{x}\n", .{ index, sym.getName(macho_file), sym.value });
+ for (thunk.symbols.keys()) |ref| {
+ const sym = ref.getSymbol(macho_file).?;
+ try writer.print(" {} : {s} : @{x}\n", .{ ref, sym.getName(macho_file), sym.value });
}
}
diff --git a/test/link/link.zig b/test/link/link.zig
@@ -74,8 +74,9 @@ fn addCompileStep(
.target = base.target,
.optimize = base.optimize,
.root_source_file = rsf: {
+ const name = b.fmt("{s}.zig", .{overlay.name});
const bytes = overlay.zig_source_bytes orelse break :rsf null;
- break :rsf b.addWriteFiles().add("a.zig", bytes);
+ break :rsf b.addWriteFiles().add(name, bytes);
},
.pic = overlay.pic,
.strip = if (base.strip) |s| s else overlay.strip,
diff --git a/test/link/macho.zig b/test/link/macho.zig
@@ -25,9 +25,12 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {
macho_step.dependOn(testLinkingStaticLib(b, .{ .use_llvm = false, .target = x86_64_target }));
macho_step.dependOn(testReexportsZig(b, .{ .use_llvm = false, .target = x86_64_target }));
macho_step.dependOn(testRelocatableZig(b, .{ .use_llvm = false, .target = x86_64_target }));
+ macho_step.dependOn(testTlsZig(b, .{ .use_llvm = false, .target = x86_64_target }));
+ macho_step.dependOn(testUnresolvedError(b, .{ .use_llvm = false, .target = x86_64_target }));
// Exercise linker with LLVM backend
macho_step.dependOn(testDeadStrip(b, .{ .target = default_target }));
+ macho_step.dependOn(testDuplicateDefinitions(b, .{ .target = default_target }));
macho_step.dependOn(testEmptyObject(b, .{ .target = default_target }));
macho_step.dependOn(testEmptyZig(b, .{ .target = default_target }));
macho_step.dependOn(testEntryPoint(b, .{ .target = default_target }));
@@ -56,7 +59,9 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {
macho_step.dependOn(testTentative(b, .{ .target = default_target }));
macho_step.dependOn(testThunks(b, .{ .target = aarch64_target }));
macho_step.dependOn(testTlsLargeTbss(b, .{ .target = default_target }));
+ macho_step.dependOn(testTlsZig(b, .{ .target = default_target }));
macho_step.dependOn(testUndefinedFlag(b, .{ .target = default_target }));
+ macho_step.dependOn(testUnresolvedError(b, .{ .target = default_target }));
macho_step.dependOn(testUnwindInfo(b, .{ .target = default_target }));
macho_step.dependOn(testUnwindInfoNoSubsectionsX64(b, .{ .target = x86_64_target }));
macho_step.dependOn(testUnwindInfoNoSubsectionsArm64(b, .{ .target = aarch64_target }));
@@ -178,6 +183,37 @@ fn testDeadStrip(b: *Build, opts: Options) *Step {
return test_step;
}
+fn testDuplicateDefinitions(b: *Build, opts: Options) *Step {
+ const test_step = addTestStep(b, "duplicate-definitions", opts);
+
+ const obj = addObject(b, opts, .{ .name = "a", .zig_source_bytes =
+ \\var x: usize = 1;
+ \\export fn strong() void { x += 1; }
+ \\export fn weak() void { x += 1; }
+ });
+
+ const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes =
+ \\var x: usize = 1;
+ \\export fn strong() void { x += 1; }
+ \\comptime { @export(weakImpl, .{ .name = "weak", .linkage = .weak }); }
+ \\fn weakImpl() callconv(.C) void { x += 1; }
+ \\extern fn weak() void;
+ \\pub fn main() void {
+ \\ weak();
+ \\ strong();
+ \\}
+ });
+ exe.addObject(obj);
+
+ expectLinkErrors(exe, test_step, .{ .exact = &.{
+ "error: duplicate symbol definition: _strong",
+ "note: defined by /?/a.o",
+ "note: defined by /?/main.o",
+ } });
+
+ return test_step;
+}
+
fn testDeadStripDylibs(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "dead-strip-dylibs", opts);
@@ -912,7 +948,7 @@ fn testLinksection(b: *Build, opts: Options) *Step {
if (opts.optimize == .Debug) {
check.checkInSymtab();
- check.checkContains("(__TEXT,__TestGenFnA) _a.testGenericFn__anon_");
+ check.checkContains("(__TEXT,__TestGenFnA) _main.testGenericFn__anon_");
}
test_step.dependOn(&check.step);
@@ -2274,6 +2310,32 @@ fn testTlsLargeTbss(b: *Build, opts: Options) *Step {
return test_step;
}
+fn testTlsZig(b: *Build, opts: Options) *Step {
+ const test_step = addTestStep(b, "tls-zig", opts);
+
+ const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes =
+ \\const std = @import("std");
+ \\threadlocal var x: i32 = 0;
+ \\threadlocal var y: i32 = -1;
+ \\pub fn main() void {
+ \\ std.io.getStdOut().writer().print("{d} {d}\n", .{x, y}) catch unreachable;
+ \\ x -= 1;
+ \\ y += 1;
+ \\ std.io.getStdOut().writer().print("{d} {d}\n", .{x, y}) catch unreachable;
+ \\}
+ });
+
+ const run = addRunArtifact(exe);
+ run.expectStdOutEqual(
+ \\0 -1
+ \\-1 0
+ \\
+ );
+ test_step.dependOn(&run.step);
+
+ return test_step;
+}
+
fn testTwoLevelNamespace(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "two-level-namespace", opts);
@@ -2471,6 +2533,42 @@ fn testUndefinedFlag(b: *Build, opts: Options) *Step {
return test_step;
}
+fn testUnresolvedError(b: *Build, opts: Options) *Step {
+ const test_step = addTestStep(b, "unresolved-error", opts);
+
+ const obj = addObject(b, opts, .{ .name = "a", .zig_source_bytes =
+ \\extern fn foo() i32;
+ \\export fn bar() i32 { return foo() + 1; }
+ });
+
+ const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes =
+ \\const std = @import("std");
+ \\extern fn foo() i32;
+ \\extern fn bar() i32;
+ \\pub fn main() void {
+ \\ std.debug.print("foo() + bar() = {d}", .{foo() + bar()});
+ \\}
+ });
+ exe.addObject(obj);
+
+ // TODO order should match across backends if possible
+ if (opts.use_llvm) {
+ expectLinkErrors(exe, test_step, .{ .exact = &.{
+ "error: undefined symbol: _foo",
+ "note: referenced by /?/a.o:_bar",
+ "note: referenced by /?/main.o:_main.main",
+ } });
+ } else {
+ expectLinkErrors(exe, test_step, .{ .exact = &.{
+ "error: undefined symbol: _foo",
+ "note: referenced by /?/main.o:_main.main",
+ "note: referenced by /?/a.o:__TEXT$__text_zig",
+ } });
+ }
+
+ return test_step;
+}
+
fn testUnwindInfo(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "unwind-info", opts);