authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-04-01 22:30:22+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-04-01 22:30:22+02:00
logab44b454d0408a7968354f8889a2e57c02396153
tree23258a2a83fc450c430e8c29f301cdd7cb2060de
parent381dc2d9509ffeaf60a1775ee0983c7dd1b9e346
parente0bf7b6424a4b3d21e10e9371e0ebed7ba9730a5
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #15135 from ziglang/fix-15103

macho: handle cases where entrypoint is in an archive or dylib

20 files changed, 364 insertions(+), 265 deletions(-)

lib/std/Build/CompileStep.zig+19
...@@ -202,6 +202,11 @@ subsystem: ?std.Target.SubSystem = null,...@@ -202,6 +202,11 @@ subsystem: ?std.Target.SubSystem = null,
202202
203entry_symbol_name: ?[]const u8 = null,203entry_symbol_name: ?[]const u8 = null,
204204
205/// List of symbols forced as undefined in the symbol table
206/// thus forcing their resolution by the linker.
207/// Corresponds to `-u <symbol>` for ELF/MachO and `/include:<symbol>` for COFF/PE.
208force_undefined_symbols: std.StringHashMap(void),
209
205/// Overrides the default stack size210/// Overrides the default stack size
206stack_size: ?u64 = null,211stack_size: ?u64 = null,
207212
...@@ -386,6 +391,7 @@ pub fn create(owner: *std.Build, options: Options) *CompileStep {...@@ -386,6 +391,7 @@ pub fn create(owner: *std.Build, options: Options) *CompileStep {
386 .override_dest_dir = null,391 .override_dest_dir = null,
387 .installed_path = null,392 .installed_path = null,
388 .install_step = null,393 .install_step = null,
394 .force_undefined_symbols = StringHashMap(void).init(owner.allocator),
389395
390 .output_path_source = GeneratedFile{ .step = &self.step },396 .output_path_source = GeneratedFile{ .step = &self.step },
391 .output_lib_path_source = GeneratedFile{ .step = &self.step },397 .output_lib_path_source = GeneratedFile{ .step = &self.step },
...@@ -568,6 +574,11 @@ pub fn setLinkerScriptPath(self: *CompileStep, source: FileSource) void {...@@ -568,6 +574,11 @@ pub fn setLinkerScriptPath(self: *CompileStep, source: FileSource) void {
568 source.addStepDependencies(&self.step);574 source.addStepDependencies(&self.step);
569}575}
570576
577pub fn forceUndefinedSymbol(self: *CompileStep, symbol_name: []const u8) void {
578 const b = self.step.owner;
579 self.force_undefined_symbols.put(b.dupe(symbol_name), {}) catch @panic("OOM");
580}
581
571pub fn linkFramework(self: *CompileStep, framework_name: []const u8) void {582pub fn linkFramework(self: *CompileStep, framework_name: []const u8) void {
572 const b = self.step.owner;583 const b = self.step.owner;
573 self.frameworks.put(b.dupe(framework_name), .{}) catch @panic("OOM");584 self.frameworks.put(b.dupe(framework_name), .{}) catch @panic("OOM");
...@@ -1266,6 +1277,14 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1266,6 +1277,14 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1266 try zig_args.append(entry);1277 try zig_args.append(entry);
1267 }1278 }
12681279
1280 {
1281 var it = self.force_undefined_symbols.keyIterator();
1282 while (it.next()) |symbol_name| {
1283 try zig_args.append("--force_undefined");
1284 try zig_args.append(symbol_name.*);
1285 }
1286 }
1287
1269 if (self.stack_size) |stack_size| {1288 if (self.stack_size) |stack_size| {
1270 try zig_args.append("--stack");1289 try zig_args.append("--stack");
1271 try zig_args.append(try std.fmt.allocPrint(b.allocator, "{}", .{stack_size}));1290 try zig_args.append(try std.fmt.allocPrint(b.allocator, "{}", .{stack_size}));
src/Compilation.zig+4-3
...@@ -602,6 +602,7 @@ pub const InitOptions = struct {...@@ -602,6 +602,7 @@ pub const InitOptions = struct {
602 parent_compilation_link_libc: bool = false,602 parent_compilation_link_libc: bool = false,
603 hash_style: link.HashStyle = .both,603 hash_style: link.HashStyle = .both,
604 entry: ?[]const u8 = null,604 entry: ?[]const u8 = null,
605 force_undefined_symbols: std.StringArrayHashMapUnmanaged(void) = .{},
605 stack_size_override: ?u64 = null,606 stack_size_override: ?u64 = null,
606 image_base_override: ?u64 = null,607 image_base_override: ?u64 = null,
607 self_exe_path: ?[]const u8 = null,608 self_exe_path: ?[]const u8 = null,
...@@ -1523,7 +1524,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1523,7 +1524,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1523 .headerpad_size = options.headerpad_size,1524 .headerpad_size = options.headerpad_size,
1524 .headerpad_max_install_names = options.headerpad_max_install_names,1525 .headerpad_max_install_names = options.headerpad_max_install_names,
1525 .dead_strip_dylibs = options.dead_strip_dylibs,1526 .dead_strip_dylibs = options.dead_strip_dylibs,
1526 .force_undefined_symbols = .{},1527 .force_undefined_symbols = options.force_undefined_symbols,
1527 .pdb_source_path = options.pdb_source_path,1528 .pdb_source_path = options.pdb_source_path,
1528 .pdb_out_path = options.pdb_out_path,1529 .pdb_out_path = options.pdb_out_path,
1529 });1530 });
...@@ -2186,7 +2187,7 @@ fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemo...@@ -2186,7 +2187,7 @@ fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemo
2186/// to remind the programmer to update multiple related pieces of code that2187/// to remind the programmer to update multiple related pieces of code that
2187/// are in different locations. Bump this number when adding or deleting2188/// are in different locations. Bump this number when adding or deleting
2188/// anything from the link cache manifest.2189/// anything from the link cache manifest.
2189pub const link_hash_implementation_version = 7;2190pub const link_hash_implementation_version = 8;
21902191
2191fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void {2192fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void {
2192 const gpa = comp.gpa;2193 const gpa = comp.gpa;
...@@ -2196,7 +2197,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes...@@ -2196,7 +2197,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
2196 defer arena_allocator.deinit();2197 defer arena_allocator.deinit();
2197 const arena = arena_allocator.allocator();2198 const arena = arena_allocator.allocator();
21982199
2199 comptime assert(link_hash_implementation_version == 7);2200 comptime assert(link_hash_implementation_version == 8);
22002201
2201 if (comp.bin_file.options.module) |mod| {2202 if (comp.bin_file.options.module) |mod| {
2202 const main_zig_file = try mod.main_pkg.root_src_directory.join(arena, &[_][]const u8{2203 const main_zig_file = try mod.main_pkg.root_src_directory.join(arena, &[_][]const u8{
src/clang_options_data.zig+9-2
...@@ -1448,7 +1448,7 @@ flagpsl("MT"),...@@ -1448,7 +1448,7 @@ flagpsl("MT"),
1448.{1448.{
1449 .name = "u",1449 .name = "u",
1450 .syntax = .flag,1450 .syntax = .flag,
1451 .zig_equivalent = .other,1451 .zig_equivalent = .force_undefined_symbol,
1452 .pd1 = true,1452 .pd1 = true,
1453 .pd2 = false,1453 .pd2 = false,
1454 .psl = true,1454 .psl = true,
...@@ -7170,7 +7170,14 @@ joinpd1("d"),...@@ -7170,7 +7170,14 @@ joinpd1("d"),
7170 .pd2 = false,7170 .pd2 = false,
7171 .psl = true,7171 .psl = true,
7172},7172},
7173jspd1("u"),7173.{
7174 .name = "u",
7175 .syntax = .joined_or_separate,
7176 .zig_equivalent = .force_undefined_symbol,
7177 .pd1 = true,
7178 .pd2 = false,
7179 .psl = false,
7180},
7174.{7181.{
7175 .name = "x",7182 .name = "x",
7176 .syntax = .joined_or_separate,7183 .syntax = .joined_or_separate,
src/link.zig+1-2
...@@ -186,8 +186,7 @@ pub const Options = struct {...@@ -186,8 +186,7 @@ pub const Options = struct {
186186
187 /// List of symbols forced as undefined in the symbol table187 /// List of symbols forced as undefined in the symbol table
188 /// thus forcing their resolution by the linker.188 /// thus forcing their resolution by the linker.
189 /// Corresponds to `-u <symbol>` for ELF and `/include:<symbol>` for COFF/PE.189 /// Corresponds to `-u <symbol>` for ELF/MachO and `/include:<symbol>` for COFF/PE.
190 /// TODO add handling for MachO.
191 force_undefined_symbols: std.StringArrayHashMapUnmanaged(void),190 force_undefined_symbols: std.StringArrayHashMapUnmanaged(void),
192191
193 version: ?std.builtin.Version,192 version: ?std.builtin.Version,
src/link/Coff/lld.zig+1-1
...@@ -63,7 +63,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -63,7 +63,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
63 man = comp.cache_parent.obtain();63 man = comp.cache_parent.obtain();
64 self.base.releaseLock();64 self.base.releaseLock();
6565
66 comptime assert(Compilation.link_hash_implementation_version == 7);66 comptime assert(Compilation.link_hash_implementation_version == 8);
6767
68 for (self.base.options.objects) |obj| {68 for (self.base.options.objects) |obj| {
69 _ = try man.addFile(obj.path, null);69 _ = try man.addFile(obj.path, null);
src/link/Elf.zig+1-1
...@@ -1305,7 +1305,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v...@@ -1305,7 +1305,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
1305 // We are about to obtain this lock, so here we give other processes a chance first.1305 // We are about to obtain this lock, so here we give other processes a chance first.
1306 self.base.releaseLock();1306 self.base.releaseLock();
13071307
1308 comptime assert(Compilation.link_hash_implementation_version == 7);1308 comptime assert(Compilation.link_hash_implementation_version == 8);
13091309
1310 try man.addOptionalFile(self.base.options.linker_script);1310 try man.addOptionalFile(self.base.options.linker_script);
1311 try man.addOptionalFile(self.base.options.version_script);1311 try man.addOptionalFile(self.base.options.version_script);
src/link/MachO.zig+1-1
...@@ -3981,7 +3981,7 @@ pub fn getStubsAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?At...@@ -3981,7 +3981,7 @@ pub fn getStubsAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?At
3981/// Returns symbol location corresponding to the set entrypoint.3981/// Returns symbol location corresponding to the set entrypoint.
3982/// Asserts output mode is executable.3982/// Asserts output mode is executable.
3983pub fn getEntryPoint(self: MachO) error{MissingMainEntrypoint}!SymbolWithLoc {3983pub fn getEntryPoint(self: MachO) error{MissingMainEntrypoint}!SymbolWithLoc {
3984 const entry_name = self.base.options.entry orelse "_main";3984 const entry_name = self.base.options.entry orelse load_commands.default_entry_point;
3985 const global = self.getGlobal(entry_name) orelse {3985 const global = self.getGlobal(entry_name) orelse {
3986 log.err("entrypoint '{s}' not found", .{entry_name});3986 log.err("entrypoint '{s}' not found", .{entry_name});
3987 return error.MissingMainEntrypoint;3987 return error.MissingMainEntrypoint;
src/link/MachO/ZldAtom.zig+1-1
...@@ -389,7 +389,7 @@ pub fn addGotEntry(zld: *Zld, target: SymbolWithLoc) !void {...@@ -389,7 +389,7 @@ pub fn addGotEntry(zld: *Zld, target: SymbolWithLoc) !void {
389 try zld.got_table.putNoClobber(gpa, target, got_index);389 try zld.got_table.putNoClobber(gpa, target, got_index);
390}390}
391391
392fn addStub(zld: *Zld, target: SymbolWithLoc) !void {392pub fn addStub(zld: *Zld, target: SymbolWithLoc) !void {
393 const target_sym = zld.getSymbol(target);393 const target_sym = zld.getSymbol(target);
394 if (!target_sym.undf()) return;394 if (!target_sym.undf()) return;
395 if (zld.stubs_table.contains(target)) return;395 if (zld.stubs_table.contains(target)) return;
src/link/MachO/dead_strip.zig+34-22
...@@ -12,6 +12,7 @@ const Allocator = mem.Allocator;...@@ -12,6 +12,7 @@ const Allocator = mem.Allocator;
12const AtomIndex = @import("zld.zig").AtomIndex;12const AtomIndex = @import("zld.zig").AtomIndex;
13const Atom = @import("ZldAtom.zig");13const Atom = @import("ZldAtom.zig");
14const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;14const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
15const SymbolResolver = @import("zld.zig").SymbolResolver;
15const UnwindInfo = @import("UnwindInfo.zig");16const UnwindInfo = @import("UnwindInfo.zig");
16const Zld = @import("zld.zig").Zld;17const Zld = @import("zld.zig").Zld;
1718
...@@ -19,7 +20,7 @@ const N_DEAD = @import("zld.zig").N_DEAD;...@@ -19,7 +20,7 @@ const N_DEAD = @import("zld.zig").N_DEAD;
1920
20const AtomTable = std.AutoHashMap(AtomIndex, void);21const AtomTable = std.AutoHashMap(AtomIndex, void);
2122
22pub fn gcAtoms(zld: *Zld) !void {23pub fn gcAtoms(zld: *Zld, resolver: *const SymbolResolver) !void {
23 const gpa = zld.gpa;24 const gpa = zld.gpa;
2425
25 var arena = std.heap.ArenaAllocator.init(gpa);26 var arena = std.heap.ArenaAllocator.init(gpa);
...@@ -31,27 +32,36 @@ pub fn gcAtoms(zld: *Zld) !void {...@@ -31,27 +32,36 @@ pub fn gcAtoms(zld: *Zld) !void {
31 var alive = AtomTable.init(arena.allocator());32 var alive = AtomTable.init(arena.allocator());
32 try alive.ensureTotalCapacity(@intCast(u32, zld.atoms.items.len));33 try alive.ensureTotalCapacity(@intCast(u32, zld.atoms.items.len));
3334
34 try collectRoots(zld, &roots);35 try collectRoots(zld, &roots, resolver);
35 try mark(zld, roots, &alive);36 try mark(zld, roots, &alive);
36 prune(zld, alive);37 prune(zld, alive);
37}38}
3839
39fn collectRoots(zld: *Zld, roots: *AtomTable) !void {40fn addRoot(zld: *Zld, roots: *AtomTable, file: u32, sym_loc: SymbolWithLoc) !void {
41 const sym = zld.getSymbol(sym_loc);
42 assert(!sym.undf());
43 const object = &zld.objects.items[file];
44 const atom_index = object.getAtomIndexForSymbol(sym_loc.sym_index).?; // panic here means fatal error
45 log.debug("root(ATOM({d}, %{d}, {d}))", .{
46 atom_index,
47 zld.getAtom(atom_index).sym_index,
48 file,
49 });
50 _ = try roots.getOrPut(atom_index);
51}
52
53fn collectRoots(zld: *Zld, roots: *AtomTable, resolver: *const SymbolResolver) !void {
40 log.debug("collecting roots", .{});54 log.debug("collecting roots", .{});
4155
42 switch (zld.options.output_mode) {56 switch (zld.options.output_mode) {
43 .Exe => {57 .Exe => {
44 // Add entrypoint as GC root58 // Add entrypoint as GC root
45 const global: SymbolWithLoc = zld.getEntryPoint();59 const global: SymbolWithLoc = zld.getEntryPoint();
46 const object = zld.objects.items[global.getFile().?];60 if (global.getFile()) |file| {
47 const atom_index = object.getAtomIndexForSymbol(global.sym_index).?; // panic here means fatal error61 try addRoot(zld, roots, file, global);
48 _ = try roots.getOrPut(atom_index);62 } else {
4963 assert(zld.getSymbol(global).undf()); // Stub as our entrypoint is in a dylib.
50 log.debug("root(ATOM({d}, %{d}, {?d}))", .{64 }
51 atom_index,
52 zld.getAtom(atom_index).sym_index,
53 zld.getAtom(atom_index).getFile(),
54 });
55 },65 },
56 else => |other| {66 else => |other| {
57 assert(other == .Lib);67 assert(other == .Lib);
...@@ -60,20 +70,22 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {...@@ -60,20 +70,22 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {
60 const sym = zld.getSymbol(global);70 const sym = zld.getSymbol(global);
61 if (sym.undf()) continue;71 if (sym.undf()) continue;
6272
63 const file = global.getFile() orelse continue; // synthetic globals are atomless73 if (global.getFile()) |file| {
64 const object = zld.objects.items[file];74 try addRoot(zld, roots, file, global);
65 const atom_index = object.getAtomIndexForSymbol(global.sym_index).?; // panic here means fatal error75 }
66 _ = try roots.getOrPut(atom_index);
67
68 log.debug("root(ATOM({d}, %{d}, {?d}))", .{
69 atom_index,
70 zld.getAtom(atom_index).sym_index,
71 zld.getAtom(atom_index).getFile(),
72 });
73 }76 }
74 },77 },
75 }78 }
7679
80 // Add all symbols force-defined by the user.
81 for (zld.options.force_undefined_symbols.keys()) |sym_name| {
82 const global_index = resolver.table.get(sym_name).?;
83 const global = zld.globals.items[global_index];
84 const sym = zld.getSymbol(global);
85 assert(!sym.undf());
86 try addRoot(zld, roots, global.getFile().?, global);
87 }
88
77 for (zld.objects.items) |object| {89 for (zld.objects.items) |object| {
78 const has_subsections = object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;90 const has_subsections = object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
7991
src/link/MachO/load_commands.zig+4
...@@ -8,6 +8,10 @@ const mem = std.mem;...@@ -8,6 +8,10 @@ const mem = std.mem;
8const Allocator = mem.Allocator;8const Allocator = mem.Allocator;
9const Dylib = @import("Dylib.zig");9const Dylib = @import("Dylib.zig");
1010
11/// Default implicit entrypoint symbol name.
12pub const default_entry_point: []const u8 = "_main";
13
14/// Default path to dyld.
11pub const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld";15pub const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld";
1216
13fn calcInstallNameLen(cmd_size: u64, name: []const u8, assume_max_path_len: bool) u64 {17fn calcInstallNameLen(cmd_size: u64, name: []const u8, assume_max_path_len: bool) u64 {
src/link/MachO/zld.zig+77-28
...@@ -932,6 +932,43 @@ pub const Zld = struct {...@@ -932,6 +932,43 @@ pub const Zld = struct {
932 }932 }
933 }933 }
934934
935 fn forceSymbolDefined(self: *Zld, name: []const u8, resolver: *SymbolResolver) !void {
936 const sym_index = try self.allocateSymbol();
937 const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
938 const sym = self.getSymbolPtr(sym_loc);
939 sym.n_strx = try self.strtab.insert(self.gpa, name);
940 sym.n_type = macho.N_UNDF | macho.N_EXT;
941 const global_index = try self.addGlobal(sym_loc);
942 try resolver.table.putNoClobber(name, global_index);
943 try resolver.unresolved.putNoClobber(global_index, {});
944 }
945
946 fn resolveSymbols(self: *Zld, resolver: *SymbolResolver) !void {
947 // We add the specified entrypoint as the first unresolved symbols so that
948 // we search for it in libraries should there be no object files specified
949 // on the linker line.
950 if (self.options.output_mode == .Exe) {
951 const entry_name = self.options.entry orelse load_commands.default_entry_point;
952 try self.forceSymbolDefined(entry_name, resolver);
953 }
954
955 // Force resolution of any symbols requested by the user.
956 for (self.options.force_undefined_symbols.keys()) |sym_name| {
957 try self.forceSymbolDefined(sym_name, resolver);
958 }
959
960 for (self.objects.items, 0..) |_, object_id| {
961 try self.resolveSymbolsInObject(@intCast(u32, object_id), resolver);
962 }
963
964 try self.resolveSymbolsInArchives(resolver);
965 try self.resolveDyldStubBinder(resolver);
966 try self.resolveSymbolsInDylibs(resolver);
967 try self.createMhExecuteHeaderSymbol(resolver);
968 try self.createDsoHandleSymbol(resolver);
969 try self.resolveSymbolsAtLoading(resolver);
970 }
971
935 fn resolveSymbolsInObject(self: *Zld, object_id: u32, resolver: *SymbolResolver) !void {972 fn resolveSymbolsInObject(self: *Zld, object_id: u32, resolver: *SymbolResolver) !void {
936 const object = &self.objects.items[object_id];973 const object = &self.objects.items[object_id];
937 const in_symtab = object.in_symtab orelse return;974 const in_symtab = object.in_symtab orelse return;
...@@ -975,9 +1012,7 @@ pub const Zld = struct {...@@ -975,9 +1012,7 @@ pub const Zld = struct {
975 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = object_id + 1 };1012 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = object_id + 1 };
9761013
977 const global_index = resolver.table.get(sym_name) orelse {1014 const global_index = resolver.table.get(sym_name) orelse {
978 const gpa = self.gpa;1015 const global_index = try self.addGlobal(sym_loc);
979 const global_index = @intCast(u32, self.globals.items.len);
980 try self.globals.append(gpa, sym_loc);
981 try resolver.table.putNoClobber(sym_name, global_index);1016 try resolver.table.putNoClobber(sym_name, global_index);
982 if (sym.undf() and !sym.tentative()) {1017 if (sym.undf() and !sym.tentative()) {
983 try resolver.unresolved.putNoClobber(global_index, {});1018 try resolver.unresolved.putNoClobber(global_index, {});
...@@ -1034,8 +1069,10 @@ pub const Zld = struct {...@@ -1034,8 +1069,10 @@ pub const Zld = struct {
1034 };1069 };
10351070
1036 if (update_global) {1071 if (update_global) {
1037 const global_object = &self.objects.items[global.getFile().?];1072 if (global.getFile()) |file| {
1038 global_object.globals_lookup[global.sym_index] = global_index;1073 const global_object = &self.objects.items[file];
1074 global_object.globals_lookup[global.sym_index] = global_index;
1075 }
1039 _ = resolver.unresolved.swapRemove(resolver.table.get(sym_name).?);1076 _ = resolver.unresolved.swapRemove(resolver.table.get(sym_name).?);
1040 global.* = sym_loc;1077 global.* = sym_loc;
1041 } else {1078 } else {
...@@ -1180,9 +1217,7 @@ pub const Zld = struct {...@@ -1180,9 +1217,7 @@ pub const Zld = struct {
1180 global.* = sym_loc;1217 global.* = sym_loc;
1181 self.mh_execute_header_index = global_index;1218 self.mh_execute_header_index = global_index;
1182 } else {1219 } else {
1183 const global_index = @intCast(u32, self.globals.items.len);1220 self.mh_execute_header_index = try self.addGlobal(sym_loc);
1184 try self.globals.append(gpa, sym_loc);
1185 self.mh_execute_header_index = global_index;
1186 }1221 }
1187 }1222 }
11881223
...@@ -1358,6 +1393,12 @@ pub const Zld = struct {...@@ -1358,6 +1393,12 @@ pub const Zld = struct {
1358 return index;1393 return index;
1359 }1394 }
13601395
1396 fn addGlobal(self: *Zld, sym_loc: SymbolWithLoc) !u32 {
1397 const global_index = @intCast(u32, self.globals.items.len);
1398 try self.globals.append(self.gpa, sym_loc);
1399 return global_index;
1400 }
1401
1361 fn allocateSpecialSymbols(self: *Zld) !void {1402 fn allocateSpecialSymbols(self: *Zld) !void {
1362 for (&[_]?u32{1403 for (&[_]?u32{
1363 self.dso_handle_index,1404 self.dso_handle_index,
...@@ -3507,7 +3548,7 @@ pub const SymbolWithLoc = extern struct {...@@ -3507,7 +3548,7 @@ pub const SymbolWithLoc = extern struct {
3507 }3548 }
3508};3549};
35093550
3510const SymbolResolver = struct {3551pub const SymbolResolver = struct {
3511 arena: Allocator,3552 arena: Allocator,
3512 table: std.StringHashMap(u32),3553 table: std.StringHashMap(u32),
3513 unresolved: std.AutoArrayHashMap(u32, void),3554 unresolved: std.AutoArrayHashMap(u32, void),
...@@ -3568,7 +3609,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -3568,7 +3609,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
3568 // We are about to obtain this lock, so here we give other processes a chance first.3609 // We are about to obtain this lock, so here we give other processes a chance first.
3569 macho_file.base.releaseLock();3610 macho_file.base.releaseLock();
35703611
3571 comptime assert(Compilation.link_hash_implementation_version == 7);3612 comptime assert(Compilation.link_hash_implementation_version == 8);
35723613
3573 for (options.objects) |obj| {3614 for (options.objects) |obj| {
3574 _ = try man.addFile(obj.path, null);3615 _ = try man.addFile(obj.path, null);
...@@ -3598,6 +3639,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -3598,6 +3639,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
3598 }3639 }
3599 link.hashAddSystemLibs(&man.hash, options.system_libs);3640 link.hashAddSystemLibs(&man.hash, options.system_libs);
3600 man.hash.addOptionalBytes(options.sysroot);3641 man.hash.addOptionalBytes(options.sysroot);
3642 man.hash.addListOfBytes(options.force_undefined_symbols.keys());
3601 try man.addOptionalFile(options.entitlements);3643 try man.addOptionalFile(options.entitlements);
36023644
3603 // We don't actually care whether it's a cache hit or miss; we just3645 // We don't actually care whether it's a cache hit or miss; we just
...@@ -3980,17 +4022,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -3980,17 +4022,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
3980 .table = std.StringHashMap(u32).init(arena),4022 .table = std.StringHashMap(u32).init(arena),
3981 .unresolved = std.AutoArrayHashMap(u32, void).init(arena),4023 .unresolved = std.AutoArrayHashMap(u32, void).init(arena),
3982 };4024 };
39834025 try zld.resolveSymbols(&resolver);
3984 for (zld.objects.items, 0..) |_, object_id| {
3985 try zld.resolveSymbolsInObject(@intCast(u32, object_id), &resolver);
3986 }
3987
3988 try zld.resolveSymbolsInArchives(&resolver);
3989 try zld.resolveDyldStubBinder(&resolver);
3990 try zld.resolveSymbolsInDylibs(&resolver);
3991 try zld.createMhExecuteHeaderSymbol(&resolver);
3992 try zld.createDsoHandleSymbol(&resolver);
3993 try zld.resolveSymbolsAtLoading(&resolver);
39944026
3995 if (resolver.unresolved.count() > 0) {4027 if (resolver.unresolved.count() > 0) {
3996 return error.UndefinedSymbolReference;4028 return error.UndefinedSymbolReference;
...@@ -4003,11 +4035,8 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -4003,11 +4035,8 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
4003 }4035 }
40044036
4005 if (options.output_mode == .Exe) {4037 if (options.output_mode == .Exe) {
4006 const entry_name = options.entry orelse "_main";4038 const entry_name = options.entry orelse load_commands.default_entry_point;
4007 const global_index = resolver.table.get(entry_name) orelse {4039 const global_index = resolver.table.get(entry_name).?; // Error was flagged earlier
4008 log.err("entrypoint '{s}' not found", .{entry_name});
4009 return error.MissingMainEntrypoint;
4010 };
4011 zld.entry_index = global_index;4040 zld.entry_index = global_index;
4012 }4041 }
40134042
...@@ -4016,13 +4045,23 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -4016,13 +4045,23 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
4016 }4045 }
40174046
4018 if (gc_sections) {4047 if (gc_sections) {
4019 try dead_strip.gcAtoms(&zld);4048 try dead_strip.gcAtoms(&zld, &resolver);
4020 }4049 }
40214050
4022 try zld.createDyldPrivateAtom();4051 try zld.createDyldPrivateAtom();
4023 try zld.createTentativeDefAtoms();4052 try zld.createTentativeDefAtoms();
4024 try zld.createStubHelperPreambleAtom();4053 try zld.createStubHelperPreambleAtom();
40254054
4055 if (zld.options.output_mode == .Exe) {
4056 const global = zld.getEntryPoint();
4057 if (zld.getSymbol(global).undf()) {
4058 // We do one additional check here in case the entry point was found in one of the dylibs.
4059 // (I actually have no idea what this would imply but it is a possible outcome and so we
4060 // support it.)
4061 try Atom.addStub(&zld, global);
4062 }
4063 }
4064
4026 for (zld.objects.items) |object| {4065 for (zld.objects.items) |object| {
4027 for (object.atoms.items) |atom_index| {4066 for (object.atoms.items) |atom_index| {
4028 const atom = zld.getAtom(atom_index);4067 const atom = zld.getAtom(atom_index);
...@@ -4134,8 +4173,18 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -4134,8 +4173,18 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
4134 const seg = zld.segments.items[seg_id];4173 const seg = zld.segments.items[seg_id];
4135 const global = zld.getEntryPoint();4174 const global = zld.getEntryPoint();
4136 const sym = zld.getSymbol(global);4175 const sym = zld.getSymbol(global);
4176
4177 const addr: u64 = if (sym.undf()) blk: {
4178 // In this case, the symbol has been resolved in one of dylibs and so we point
4179 // to the stub as its vmaddr value.
4180 const stub_atom_index = zld.getStubsAtomIndexForSymbol(global).?;
4181 const stub_atom = zld.getAtom(stub_atom_index);
4182 const stub_sym = zld.getSymbol(stub_atom.getSymbolWithLoc());
4183 break :blk stub_sym.n_value;
4184 } else sym.n_value;
4185
4137 try lc_writer.writeStruct(macho.entry_point_command{4186 try lc_writer.writeStruct(macho.entry_point_command{
4138 .entryoff = @intCast(u32, sym.n_value - seg.vmaddr),4187 .entryoff = @intCast(u32, addr - seg.vmaddr),
4139 .stacksize = options.stack_size_override orelse 0,4188 .stacksize = options.stack_size_override orelse 0,
4140 });4189 });
4141 } else {4190 } else {
src/link/Wasm.zig+2-2
...@@ -3059,7 +3059,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l...@@ -3059,7 +3059,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
3059 // We are about to obtain this lock, so here we give other processes a chance first.3059 // We are about to obtain this lock, so here we give other processes a chance first.
3060 wasm.base.releaseLock();3060 wasm.base.releaseLock();
30613061
3062 comptime assert(Compilation.link_hash_implementation_version == 7);3062 comptime assert(Compilation.link_hash_implementation_version == 8);
30633063
3064 for (options.objects) |obj| {3064 for (options.objects) |obj| {
3065 _ = try man.addFile(obj.path, null);3065 _ = try man.addFile(obj.path, null);
...@@ -4086,7 +4086,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !...@@ -4086,7 +4086,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
4086 // We are about to obtain this lock, so here we give other processes a chance first.4086 // We are about to obtain this lock, so here we give other processes a chance first.
4087 wasm.base.releaseLock();4087 wasm.base.releaseLock();
40884088
4089 comptime assert(Compilation.link_hash_implementation_version == 7);4089 comptime assert(Compilation.link_hash_implementation_version == 8);
40904090
4091 for (wasm.base.options.objects) |obj| {4091 for (wasm.base.options.objects) |obj| {
4092 _ = try man.addFile(obj.path, null);4092 _ = try man.addFile(obj.path, null);
src/main.zig+92-202
...@@ -478,6 +478,7 @@ const usage_build_generic =...@@ -478,6 +478,7 @@ const usage_build_generic =
478 \\ --sysroot [path] Set the system root directory (usually /)478 \\ --sysroot [path] Set the system root directory (usually /)
479 \\ --version [ver] Dynamic library semver479 \\ --version [ver] Dynamic library semver
480 \\ --entry [name] Set the entrypoint symbol name480 \\ --entry [name] Set the entrypoint symbol name
481 \\ --force_undefined [name] Specify the symbol must be defined for the link to succeed
481 \\ -fsoname[=name] Override the default SONAME value482 \\ -fsoname[=name] Override the default SONAME value
482 \\ -fno-soname Disable emitting a SONAME483 \\ -fno-soname Disable emitting a SONAME
483 \\ -fLLD Force using LLD as the linker484 \\ -fLLD Force using LLD as the linker
...@@ -680,6 +681,28 @@ const Listen = union(enum) {...@@ -680,6 +681,28 @@ const Listen = union(enum) {
680 stdio,681 stdio,
681};682};
682683
684const ArgsIterator = struct {
685 resp_file: ?ArgIteratorResponseFile = null,
686 args: []const []const u8,
687 i: usize = 0,
688 fn next(it: *@This()) ?[]const u8 {
689 if (it.i >= it.args.len) {
690 if (it.resp_file) |*resp| return resp.next();
691 return null;
692 }
693 defer it.i += 1;
694 return it.args[it.i];
695 }
696 fn nextOrFatal(it: *@This()) []const u8 {
697 if (it.i >= it.args.len) {
698 if (it.resp_file) |*resp| if (resp.next()) |ret| return ret;
699 fatal("expected parameter after {s}", .{it.args[it.i - 1]});
700 }
701 defer it.i += 1;
702 return it.args[it.i];
703 }
704};
705
683fn buildOutputType(706fn buildOutputType(
684 gpa: Allocator,707 gpa: Allocator,
685 arena: Allocator,708 arena: Allocator,
...@@ -784,6 +807,7 @@ fn buildOutputType(...@@ -784,6 +807,7 @@ fn buildOutputType(
784 var test_evented_io = false;807 var test_evented_io = false;
785 var test_no_exec = false;808 var test_no_exec = false;
786 var entry: ?[]const u8 = null;809 var entry: ?[]const u8 = null;
810 var force_undefined_symbols: std.StringArrayHashMapUnmanaged(void) = .{};
787 var stack_size_override: ?u64 = null;811 var stack_size_override: ?u64 = null;
788 var image_base_override: ?u64 = null;812 var image_base_override: ?u64 = null;
789 var use_llvm: ?bool = null;813 var use_llvm: ?bool = null;
...@@ -917,28 +941,7 @@ fn buildOutputType(...@@ -917,28 +941,7 @@ fn buildOutputType(
917941
918 soname = .yes_default_value;942 soname = .yes_default_value;
919943
920 const Iterator = struct {944 var args_iter = ArgsIterator{
921 resp_file: ?ArgIteratorResponseFile = null,
922 args: []const []const u8,
923 i: usize = 0,
924 fn next(it: *@This()) ?[]const u8 {
925 if (it.i >= it.args.len) {
926 if (it.resp_file) |*resp| return resp.next();
927 return null;
928 }
929 defer it.i += 1;
930 return it.args[it.i];
931 }
932 fn nextOrFatal(it: *@This()) []const u8 {
933 if (it.i >= it.args.len) {
934 if (it.resp_file) |*resp| if (resp.next()) |ret| return ret;
935 fatal("expected parameter after {s}", .{it.args[it.i - 1]});
936 }
937 defer it.i += 1;
938 return it.args[it.i];
939 }
940 };
941 var args_iter = Iterator{
942 .args = all_args[2..],945 .args = all_args[2..],
943 };946 };
944947
...@@ -1029,6 +1032,8 @@ fn buildOutputType(...@@ -1029,6 +1032,8 @@ fn buildOutputType(
1029 optimize_mode_string = args_iter.nextOrFatal();1032 optimize_mode_string = args_iter.nextOrFatal();
1030 } else if (mem.eql(u8, arg, "--entry")) {1033 } else if (mem.eql(u8, arg, "--entry")) {
1031 entry = args_iter.nextOrFatal();1034 entry = args_iter.nextOrFatal();
1035 } else if (mem.eql(u8, arg, "--force_undefined")) {
1036 try force_undefined_symbols.put(gpa, args_iter.nextOrFatal(), {});
1032 } else if (mem.eql(u8, arg, "--stack")) {1037 } else if (mem.eql(u8, arg, "--stack")) {
1033 const next_arg = args_iter.nextOrFatal();1038 const next_arg = args_iter.nextOrFatal();
1034 stack_size_override = std.fmt.parseUnsigned(u64, next_arg, 0) catch |err| {1039 stack_size_override = std.fmt.parseUnsigned(u64, next_arg, 0) catch |err| {
...@@ -1816,6 +1821,9 @@ fn buildOutputType(...@@ -1816,6 +1821,9 @@ fn buildOutputType(
1816 .entry => {1821 .entry => {
1817 entry = it.only_arg;1822 entry = it.only_arg;
1818 },1823 },
1824 .force_undefined_symbol => {
1825 try force_undefined_symbols.put(gpa, it.only_arg, {});
1826 },
1819 .weak_library => try system_libs.put(it.only_arg, .{ .weak = true }),1827 .weak_library => try system_libs.put(it.only_arg, .{ .weak = true }),
1820 .weak_framework => try frameworks.put(gpa, it.only_arg, .{ .weak = true }),1828 .weak_framework => try frameworks.put(gpa, it.only_arg, .{ .weak = true }),
1821 .headerpad_max_install_names => headerpad_max_install_names = true,1829 .headerpad_max_install_names => headerpad_max_install_names = true,
...@@ -1843,17 +1851,14 @@ fn buildOutputType(...@@ -1843,17 +1851,14 @@ fn buildOutputType(
1843 }1851 }
1844 }1852 }
1845 // Parse linker args.1853 // Parse linker args.
1846 var i: usize = 0;1854 var linker_args_it = ArgsIterator{
1847 while (i < linker_args.items.len) : (i += 1) {1855 .args = linker_args.items,
1848 const arg = linker_args.items[i];1856 };
1857 while (linker_args_it.next()) |arg| {
1849 if (mem.eql(u8, arg, "-soname") or1858 if (mem.eql(u8, arg, "-soname") or
1850 mem.eql(u8, arg, "--soname"))1859 mem.eql(u8, arg, "--soname"))
1851 {1860 {
1852 i += 1;1861 const name = linker_args_it.nextOrFatal();
1853 if (i >= linker_args.items.len) {
1854 fatal("expected linker arg after '{s}'", .{arg});
1855 }
1856 const name = linker_args.items[i];
1857 soname = .{ .yes = name };1862 soname = .{ .yes = name };
1858 // Use it as --name.1863 // Use it as --name.
1859 // Example: libsoundio.so.21864 // Example: libsoundio.so.2
...@@ -1881,64 +1886,37 @@ fn buildOutputType(...@@ -1881,64 +1886,37 @@ fn buildOutputType(
1881 }1886 }
1882 provided_name = name[prefix..end];1887 provided_name = name[prefix..end];
1883 } else if (mem.eql(u8, arg, "-rpath")) {1888 } else if (mem.eql(u8, arg, "-rpath")) {
1884 i += 1;1889 try rpath_list.append(linker_args_it.nextOrFatal());
1885 if (i >= linker_args.items.len) {
1886 fatal("expected linker arg after '{s}'", .{arg});
1887 }
1888 try rpath_list.append(linker_args.items[i]);
1889 } else if (mem.eql(u8, arg, "--subsystem")) {1890 } else if (mem.eql(u8, arg, "--subsystem")) {
1890 i += 1;1891 subsystem = try parseSubSystem(linker_args_it.nextOrFatal());
1891 if (i >= linker_args.items.len) {
1892 fatal("expected linker arg after '{s}'", .{arg});
1893 }
1894 subsystem = try parseSubSystem(linker_args.items[i]);
1895 } else if (mem.eql(u8, arg, "-I") or1892 } else if (mem.eql(u8, arg, "-I") or
1896 mem.eql(u8, arg, "--dynamic-linker") or1893 mem.eql(u8, arg, "--dynamic-linker") or
1897 mem.eql(u8, arg, "-dynamic-linker"))1894 mem.eql(u8, arg, "-dynamic-linker"))
1898 {1895 {
1899 i += 1;1896 target_dynamic_linker = linker_args_it.nextOrFatal();
1900 if (i >= linker_args.items.len) {
1901 fatal("expected linker arg after '{s}'", .{arg});
1902 }
1903 target_dynamic_linker = linker_args.items[i];
1904 } else if (mem.eql(u8, arg, "-E") or1897 } else if (mem.eql(u8, arg, "-E") or
1905 mem.eql(u8, arg, "--export-dynamic") or1898 mem.eql(u8, arg, "--export-dynamic") or
1906 mem.eql(u8, arg, "-export-dynamic"))1899 mem.eql(u8, arg, "-export-dynamic"))
1907 {1900 {
1908 rdynamic = true;1901 rdynamic = true;
1909 } else if (mem.eql(u8, arg, "--version-script")) {1902 } else if (mem.eql(u8, arg, "--version-script")) {
1910 i += 1;1903 version_script = linker_args_it.nextOrFatal();
1911 if (i >= linker_args.items.len) {
1912 fatal("expected linker arg after '{s}'", .{arg});
1913 }
1914 version_script = linker_args.items[i];
1915 } else if (mem.eql(u8, arg, "-O")) {1904 } else if (mem.eql(u8, arg, "-O")) {
1916 i += 1;1905 const opt = linker_args_it.nextOrFatal();
1917 if (i >= linker_args.items.len) {1906 linker_optimization = std.fmt.parseUnsigned(u8, opt, 10) catch |err| {
1918 fatal("expected linker arg after '{s}'", .{arg});1907 fatal("unable to parse optimization level '{s}': {s}", .{ opt, @errorName(err) });
1919 }
1920 linker_optimization = std.fmt.parseUnsigned(u8, linker_args.items[i], 10) catch |err| {
1921 fatal("unable to parse optimization level '{s}': {s}", .{ linker_args.items[i], @errorName(err) });
1922 };1908 };
1923 } else if (mem.startsWith(u8, arg, "-O")) {1909 } else if (mem.startsWith(u8, arg, "-O")) {
1924 linker_optimization = std.fmt.parseUnsigned(u8, arg["-O".len..], 10) catch |err| {1910 linker_optimization = std.fmt.parseUnsigned(u8, arg["-O".len..], 10) catch |err| {
1925 fatal("unable to parse optimization level '{s}': {s}", .{ arg, @errorName(err) });1911 fatal("unable to parse optimization level '{s}': {s}", .{ arg, @errorName(err) });
1926 };1912 };
1927 } else if (mem.eql(u8, arg, "-pagezero_size")) {1913 } else if (mem.eql(u8, arg, "-pagezero_size")) {
1928 i += 1;1914 const next_arg = linker_args_it.nextOrFatal();
1929 if (i >= linker_args.items.len) {
1930 fatal("expected linker arg after '{s}'", .{arg});
1931 }
1932 const next_arg = linker_args.items[i];
1933 pagezero_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| {1915 pagezero_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| {
1934 fatal("unable to parse pagezero size '{s}': {s}", .{ next_arg, @errorName(err) });1916 fatal("unable to parse pagezero size '{s}': {s}", .{ next_arg, @errorName(err) });
1935 };1917 };
1936 } else if (mem.eql(u8, arg, "-headerpad")) {1918 } else if (mem.eql(u8, arg, "-headerpad")) {
1937 i += 1;1919 const next_arg = linker_args_it.nextOrFatal();
1938 if (i >= linker_args.items.len) {
1939 fatal("expected linker arg after '{s}'", .{arg});
1940 }
1941 const next_arg = linker_args.items[i];
1942 headerpad_size = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| {1920 headerpad_size = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| {
1943 fatal("unable to parse headerpad size '{s}': {s}", .{ next_arg, @errorName(err) });1921 fatal("unable to parse headerpad size '{s}': {s}", .{ next_arg, @errorName(err) });
1944 };1922 };
...@@ -1961,11 +1939,7 @@ fn buildOutputType(...@@ -1961,11 +1939,7 @@ fn buildOutputType(
1961 } else if (mem.eql(u8, arg, "--print-map")) {1939 } else if (mem.eql(u8, arg, "--print-map")) {
1962 linker_print_map = true;1940 linker_print_map = true;
1963 } else if (mem.eql(u8, arg, "--sort-section")) {1941 } else if (mem.eql(u8, arg, "--sort-section")) {
1964 i += 1;1942 const arg1 = linker_args_it.nextOrFatal();
1965 if (i >= linker_args.items.len) {
1966 fatal("expected linker arg after '{s}'", .{arg});
1967 }
1968 const arg1 = linker_args.items[i];
1969 linker_sort_section = std.meta.stringToEnum(link.SortSection, arg1) orelse {1943 linker_sort_section = std.meta.stringToEnum(link.SortSection, arg1) orelse {
1970 fatal("expected [name|alignment] after --sort-section, found '{s}'", .{arg1});1944 fatal("expected [name|alignment] after --sort-section, found '{s}'", .{arg1});
1971 };1945 };
...@@ -1998,28 +1972,16 @@ fn buildOutputType(...@@ -1998,28 +1972,16 @@ fn buildOutputType(
1998 } else if (mem.startsWith(u8, arg, "--export=")) {1972 } else if (mem.startsWith(u8, arg, "--export=")) {
1999 try linker_export_symbol_names.append(arg["--export=".len..]);1973 try linker_export_symbol_names.append(arg["--export=".len..]);
2000 } else if (mem.eql(u8, arg, "--export")) {1974 } else if (mem.eql(u8, arg, "--export")) {
2001 i += 1;1975 try linker_export_symbol_names.append(linker_args_it.nextOrFatal());
2002 if (i >= linker_args.items.len) {
2003 fatal("expected linker arg after '{s}'", .{arg});
2004 }
2005 try linker_export_symbol_names.append(linker_args.items[i]);
2006 } else if (mem.eql(u8, arg, "--compress-debug-sections")) {1976 } else if (mem.eql(u8, arg, "--compress-debug-sections")) {
2007 i += 1;1977 const arg1 = linker_args_it.nextOrFatal();
2008 if (i >= linker_args.items.len) {
2009 fatal("expected linker arg after '{s}'", .{arg});
2010 }
2011 const arg1 = linker_args.items[i];
2012 linker_compress_debug_sections = std.meta.stringToEnum(link.CompressDebugSections, arg1) orelse {1978 linker_compress_debug_sections = std.meta.stringToEnum(link.CompressDebugSections, arg1) orelse {
2013 fatal("expected [none|zlib] after --compress-debug-sections, found '{s}'", .{arg1});1979 fatal("expected [none|zlib] after --compress-debug-sections, found '{s}'", .{arg1});
2014 };1980 };
2015 } else if (mem.startsWith(u8, arg, "-z")) {1981 } else if (mem.startsWith(u8, arg, "-z")) {
2016 var z_arg = arg[2..];1982 var z_arg = arg[2..];
2017 if (z_arg.len == 0) {1983 if (z_arg.len == 0) {
2018 i += 1;1984 z_arg = linker_args_it.nextOrFatal();
2019 if (i >= linker_args.items.len) {
2020 fatal("expected linker extension flag after '{s}'", .{arg});
2021 }
2022 z_arg = linker_args.items[i];
2023 }1985 }
2024 if (mem.eql(u8, z_arg, "nodelete")) {1986 if (mem.eql(u8, z_arg, "nodelete")) {
2025 linker_z_nodelete = true;1987 linker_z_nodelete = true;
...@@ -2056,51 +2018,33 @@ fn buildOutputType(...@@ -2056,51 +2018,33 @@ fn buildOutputType(
2056 fatal("unsupported linker extension flag: -z {s}", .{z_arg});2018 fatal("unsupported linker extension flag: -z {s}", .{z_arg});
2057 }2019 }
2058 } else if (mem.eql(u8, arg, "--major-image-version")) {2020 } else if (mem.eql(u8, arg, "--major-image-version")) {
2059 i += 1;2021 const major = linker_args_it.nextOrFatal();
2060 if (i >= linker_args.items.len) {2022 version.major = std.fmt.parseUnsigned(u32, major, 10) catch |err| {
2061 fatal("expected linker arg after '{s}'", .{arg});2023 fatal("unable to parse major image version '{s}': {s}", .{ major, @errorName(err) });
2062 }
2063 version.major = std.fmt.parseUnsigned(u32, linker_args.items[i], 10) catch |err| {
2064 fatal("unable to parse major image version '{s}': {s}", .{ linker_args.items[i], @errorName(err) });
2065 };2024 };
2066 have_version = true;2025 have_version = true;
2067 } else if (mem.eql(u8, arg, "--minor-image-version")) {2026 } else if (mem.eql(u8, arg, "--minor-image-version")) {
2068 i += 1;2027 const minor = linker_args_it.nextOrFatal();
2069 if (i >= linker_args.items.len) {2028 version.minor = std.fmt.parseUnsigned(u32, minor, 10) catch |err| {
2070 fatal("expected linker arg after '{s}'", .{arg});2029 fatal("unable to parse minor image version '{s}': {s}", .{ minor, @errorName(err) });
2071 }
2072 version.minor = std.fmt.parseUnsigned(u32, linker_args.items[i], 10) catch |err| {
2073 fatal("unable to parse minor image version '{s}': {s}", .{ linker_args.items[i], @errorName(err) });
2074 };2030 };
2075 have_version = true;2031 have_version = true;
2076 } else if (mem.eql(u8, arg, "-e") or mem.eql(u8, arg, "--entry")) {2032 } else if (mem.eql(u8, arg, "-e") or mem.eql(u8, arg, "--entry")) {
2077 i += 1;2033 entry = linker_args_it.nextOrFatal();
2078 if (i >= linker_args.items.len) {2034 } else if (mem.eql(u8, arg, "-u")) {
2079 fatal("expected linker arg after '{s}'", .{arg});2035 try force_undefined_symbols.put(gpa, linker_args_it.nextOrFatal(), {});
2080 }
2081 entry = linker_args.items[i];
2082 } else if (mem.eql(u8, arg, "--stack") or mem.eql(u8, arg, "-stack_size")) {2036 } else if (mem.eql(u8, arg, "--stack") or mem.eql(u8, arg, "-stack_size")) {
2083 i += 1;2037 const stack_size = linker_args_it.nextOrFatal();
2084 if (i >= linker_args.items.len) {2038 stack_size_override = std.fmt.parseUnsigned(u64, stack_size, 0) catch |err| {
2085 fatal("expected linker arg after '{s}'", .{arg});2039 fatal("unable to parse stack size override '{s}': {s}", .{ stack_size, @errorName(err) });
2086 }
2087 stack_size_override = std.fmt.parseUnsigned(u64, linker_args.items[i], 0) catch |err| {
2088 fatal("unable to parse stack size override '{s}': {s}", .{ linker_args.items[i], @errorName(err) });
2089 };2040 };
2090 } else if (mem.eql(u8, arg, "--image-base")) {2041 } else if (mem.eql(u8, arg, "--image-base")) {
2091 i += 1;2042 const image_base = linker_args_it.nextOrFatal();
2092 if (i >= linker_args.items.len) {2043 image_base_override = std.fmt.parseUnsigned(u64, image_base, 0) catch |err| {
2093 fatal("expected linker arg after '{s}'", .{arg});2044 fatal("unable to parse image base override '{s}': {s}", .{ image_base, @errorName(err) });
2094 }
2095 image_base_override = std.fmt.parseUnsigned(u64, linker_args.items[i], 0) catch |err| {
2096 fatal("unable to parse image base override '{s}': {s}", .{ linker_args.items[i], @errorName(err) });
2097 };2045 };
2098 } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) {2046 } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) {
2099 i += 1;2047 linker_script = linker_args_it.nextOrFatal();
2100 if (i >= linker_args.items.len) {
2101 fatal("expected linker arg after '{s}'", .{arg});
2102 }
2103 linker_script = linker_args.items[i];
2104 } else if (mem.eql(u8, arg, "--eh-frame-hdr")) {2048 } else if (mem.eql(u8, arg, "--eh-frame-hdr")) {
2105 link_eh_frame_hdr = true;2049 link_eh_frame_hdr = true;
2106 } else if (mem.eql(u8, arg, "--no-eh-frame-hdr")) {2050 } else if (mem.eql(u8, arg, "--no-eh-frame-hdr")) {
...@@ -2138,130 +2082,74 @@ fn buildOutputType(...@@ -2138,130 +2082,74 @@ fn buildOutputType(
2138 } else if (mem.eql(u8, arg, "--major-os-version") or2082 } else if (mem.eql(u8, arg, "--major-os-version") or
2139 mem.eql(u8, arg, "--minor-os-version"))2083 mem.eql(u8, arg, "--minor-os-version"))
2140 {2084 {
2141 i += 1;
2142 if (i >= linker_args.items.len) {
2143 fatal("expected linker arg after '{s}'", .{arg});
2144 }
2145 // This option does not do anything.2085 // This option does not do anything.
2086 _ = linker_args_it.nextOrFatal();
2146 } else if (mem.eql(u8, arg, "--major-subsystem-version")) {2087 } else if (mem.eql(u8, arg, "--major-subsystem-version")) {
2147 i += 1;2088 const major = linker_args_it.nextOrFatal();
2148 if (i >= linker_args.items.len) {
2149 fatal("expected linker arg after '{s}'", .{arg});
2150 }
2151
2152 major_subsystem_version = std.fmt.parseUnsigned(2089 major_subsystem_version = std.fmt.parseUnsigned(
2153 u32,2090 u32,
2154 linker_args.items[i],2091 major,
2155 10,2092 10,
2156 ) catch |err| {2093 ) catch |err| {
2157 fatal("unable to parse major subsystem version '{s}': {s}", .{ linker_args.items[i], @errorName(err) });2094 fatal("unable to parse major subsystem version '{s}': {s}", .{ major, @errorName(err) });
2158 };2095 };
2159 } else if (mem.eql(u8, arg, "--minor-subsystem-version")) {2096 } else if (mem.eql(u8, arg, "--minor-subsystem-version")) {
2160 i += 1;2097 const minor = linker_args_it.nextOrFatal();
2161 if (i >= linker_args.items.len) {
2162 fatal("expected linker arg after '{s}'", .{arg});
2163 }
2164
2165 minor_subsystem_version = std.fmt.parseUnsigned(2098 minor_subsystem_version = std.fmt.parseUnsigned(
2166 u32,2099 u32,
2167 linker_args.items[i],2100 minor,
2168 10,2101 10,
2169 ) catch |err| {2102 ) catch |err| {
2170 fatal("unable to parse minor subsystem version '{s}': {s}", .{ linker_args.items[i], @errorName(err) });2103 fatal("unable to parse minor subsystem version '{s}': {s}", .{ minor, @errorName(err) });
2171 };2104 };
2172 } else if (mem.eql(u8, arg, "-framework")) {2105 } else if (mem.eql(u8, arg, "-framework")) {
2173 i += 1;2106 try frameworks.put(gpa, linker_args_it.nextOrFatal(), .{});
2174 if (i >= linker_args.items.len) {
2175 fatal("expected linker arg after '{s}'", .{arg});
2176 }
2177 try frameworks.put(gpa, linker_args.items[i], .{});
2178 } else if (mem.eql(u8, arg, "-weak_framework")) {2107 } else if (mem.eql(u8, arg, "-weak_framework")) {
2179 i += 1;2108 try frameworks.put(gpa, linker_args_it.nextOrFatal(), .{ .weak = true });
2180 if (i >= linker_args.items.len) {
2181 fatal("expected linker arg after '{s}'", .{arg});
2182 }
2183 try frameworks.put(gpa, linker_args.items[i], .{ .weak = true });
2184 } else if (mem.eql(u8, arg, "-needed_framework")) {2109 } else if (mem.eql(u8, arg, "-needed_framework")) {
2185 i += 1;2110 try frameworks.put(gpa, linker_args_it.nextOrFatal(), .{ .needed = true });
2186 if (i >= linker_args.items.len) {
2187 fatal("expected linker arg after '{s}'", .{arg});
2188 }
2189 try frameworks.put(gpa, linker_args.items[i], .{ .needed = true });
2190 } else if (mem.eql(u8, arg, "-needed_library")) {2111 } else if (mem.eql(u8, arg, "-needed_library")) {
2191 i += 1;2112 try system_libs.put(linker_args_it.nextOrFatal(), .{ .needed = true });
2192 if (i >= linker_args.items.len) {
2193 fatal("expected linker arg after '{s}'", .{arg});
2194 }
2195 try system_libs.put(linker_args.items[i], .{ .needed = true });
2196 } else if (mem.startsWith(u8, arg, "-weak-l")) {2113 } else if (mem.startsWith(u8, arg, "-weak-l")) {
2197 try system_libs.put(arg["-weak-l".len..], .{ .weak = true });2114 try system_libs.put(arg["-weak-l".len..], .{ .weak = true });
2198 } else if (mem.eql(u8, arg, "-weak_library")) {2115 } else if (mem.eql(u8, arg, "-weak_library")) {
2199 i += 1;2116 try system_libs.put(linker_args_it.nextOrFatal(), .{ .weak = true });
2200 if (i >= linker_args.items.len) {
2201 fatal("expected linker arg after '{s}'", .{arg});
2202 }
2203 try system_libs.put(linker_args.items[i], .{ .weak = true });
2204 } else if (mem.eql(u8, arg, "-compatibility_version")) {2117 } else if (mem.eql(u8, arg, "-compatibility_version")) {
2205 i += 1;2118 const compat_version = linker_args_it.nextOrFatal();
2206 if (i >= linker_args.items.len) {2119 compatibility_version = std.builtin.Version.parse(compat_version) catch |err| {
2207 fatal("expected linker arg after '{s}'", .{arg});2120 fatal("unable to parse -compatibility_version '{s}': {s}", .{ compat_version, @errorName(err) });
2208 }
2209 compatibility_version = std.builtin.Version.parse(linker_args.items[i]) catch |err| {
2210 fatal("unable to parse -compatibility_version '{s}': {s}", .{ linker_args.items[i], @errorName(err) });
2211 };2121 };
2212 } else if (mem.eql(u8, arg, "-current_version")) {2122 } else if (mem.eql(u8, arg, "-current_version")) {
2213 i += 1;2123 const curr_version = linker_args_it.nextOrFatal();
2214 if (i >= linker_args.items.len) {2124 version = std.builtin.Version.parse(curr_version) catch |err| {
2215 fatal("expected linker arg after '{s}'", .{arg});2125 fatal("unable to parse -current_version '{s}': {s}", .{ curr_version, @errorName(err) });
2216 }
2217 version = std.builtin.Version.parse(linker_args.items[i]) catch |err| {
2218 fatal("unable to parse -current_version '{s}': {s}", .{ linker_args.items[i], @errorName(err) });
2219 };2126 };
2220 have_version = true;2127 have_version = true;
2221 } else if (mem.eql(u8, arg, "--out-implib") or2128 } else if (mem.eql(u8, arg, "--out-implib") or
2222 mem.eql(u8, arg, "-implib"))2129 mem.eql(u8, arg, "-implib"))
2223 {2130 {
2224 i += 1;2131 emit_implib = .{ .yes = linker_args_it.nextOrFatal() };
2225 if (i >= linker_args.items.len) {
2226 fatal("expected linker arg after '{s}'", .{arg});
2227 }
2228 emit_implib = .{ .yes = linker_args.items[i] };
2229 emit_implib_arg_provided = true;2132 emit_implib_arg_provided = true;
2230 } else if (mem.eql(u8, arg, "-undefined")) {2133 } else if (mem.eql(u8, arg, "-undefined")) {
2231 i += 1;2134 const lookup_type = linker_args_it.nextOrFatal();
2232 if (i >= linker_args.items.len) {2135 if (mem.eql(u8, "dynamic_lookup", lookup_type)) {
2233 fatal("expected linker arg after '{s}'", .{arg});
2234 }
2235 if (mem.eql(u8, "dynamic_lookup", linker_args.items[i])) {
2236 linker_allow_shlib_undefined = true;2136 linker_allow_shlib_undefined = true;
2237 } else if (mem.eql(u8, "error", linker_args.items[i])) {2137 } else if (mem.eql(u8, "error", lookup_type)) {
2238 linker_allow_shlib_undefined = false;2138 linker_allow_shlib_undefined = false;
2239 } else {2139 } else {
2240 fatal("unsupported -undefined option '{s}'", .{linker_args.items[i]});2140 fatal("unsupported -undefined option '{s}'", .{lookup_type});
2241 }2141 }
2242 } else if (mem.eql(u8, arg, "-install_name")) {2142 } else if (mem.eql(u8, arg, "-install_name")) {
2243 i += 1;2143 install_name = linker_args_it.nextOrFatal();
2244 if (i >= linker_args.items.len) {
2245 fatal("expected linker arg after '{s}'", .{arg});
2246 }
2247 install_name = linker_args.items[i];
2248 } else if (mem.eql(u8, arg, "-force_load")) {2144 } else if (mem.eql(u8, arg, "-force_load")) {
2249 i += 1;
2250 if (i >= linker_args.items.len) {
2251 fatal("expected linker arg after '{s}'", .{arg});
2252 }
2253 try link_objects.append(.{2145 try link_objects.append(.{
2254 .path = linker_args.items[i],2146 .path = linker_args_it.nextOrFatal(),
2255 .must_link = true,2147 .must_link = true,
2256 });2148 });
2257 } else if (mem.eql(u8, arg, "-hash-style") or2149 } else if (mem.eql(u8, arg, "-hash-style") or
2258 mem.eql(u8, arg, "--hash-style"))2150 mem.eql(u8, arg, "--hash-style"))
2259 {2151 {
2260 i += 1;2152 const next_arg = linker_args_it.nextOrFatal();
2261 if (i >= linker_args.items.len) {
2262 fatal("expected linker arg after '{s}'", .{arg});
2263 }
2264 const next_arg = linker_args.items[i];
2265 hash_style = std.meta.stringToEnum(link.HashStyle, next_arg) orelse {2153 hash_style = std.meta.stringToEnum(link.HashStyle, next_arg) orelse {
2266 fatal("expected [sysv|gnu|both] after --hash-style, found '{s}'", .{2154 fatal("expected [sysv|gnu|both] after --hash-style, found '{s}'", .{
2267 next_arg,2155 next_arg,
...@@ -3219,6 +3107,7 @@ fn buildOutputType(...@@ -3219,6 +3107,7 @@ fn buildOutputType(
3219 .link_eh_frame_hdr = link_eh_frame_hdr,3107 .link_eh_frame_hdr = link_eh_frame_hdr,
3220 .link_emit_relocs = link_emit_relocs,3108 .link_emit_relocs = link_emit_relocs,
3221 .entry = entry,3109 .entry = entry,
3110 .force_undefined_symbols = force_undefined_symbols,
3222 .stack_size_override = stack_size_override,3111 .stack_size_override = stack_size_override,
3223 .image_base_override = image_base_override,3112 .image_base_override = image_base_override,
3224 .strip = strip,3113 .strip = strip,
...@@ -5295,6 +5184,7 @@ pub const ClangArgIterator = struct {...@@ -5295,6 +5184,7 @@ pub const ClangArgIterator = struct {
5295 emit_llvm,5184 emit_llvm,
5296 sysroot,5185 sysroot,
5297 entry,5186 entry,
5187 force_undefined_symbol,
5298 weak_library,5188 weak_library,
5299 weak_framework,5189 weak_framework,
5300 headerpad_max_install_names,5190 headerpad_max_install_names,
test/link.zig+8
...@@ -104,6 +104,14 @@ pub const cases = [_]Case{...@@ -104,6 +104,14 @@ pub const cases = [_]Case{
104 .build_root = "test/link/macho/entry",104 .build_root = "test/link/macho/entry",
105 .import = @import("link/macho/entry/build.zig"),105 .import = @import("link/macho/entry/build.zig"),
106 },106 },
107 .{
108 .build_root = "test/link/macho/entry_in_archive",
109 .import = @import("link/macho/entry_in_archive/build.zig"),
110 },
111 .{
112 .build_root = "test/link/macho/entry_in_dylib",
113 .import = @import("link/macho/entry_in_dylib/build.zig"),
114 },
107 .{115 .{
108 .build_root = "test/link/macho/headerpad",116 .build_root = "test/link/macho/headerpad",
109 .import = @import("link/macho/headerpad/build.zig"),117 .import = @import("link/macho/headerpad/build.zig"),
test/link/macho/entry_in_archive/build.zig created+36
...@@ -0,0 +1,36 @@
1const std = @import("std");
2
3pub const requires_symlinks = true;
4
5pub fn build(b: *std.Build) void {
6 const test_step = b.step("test", "Test it");
7 b.default_step = test_step;
8
9 add(b, test_step, .Debug);
10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
13}
14
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
16 const lib = b.addStaticLibrary(.{
17 .name = "main",
18 .optimize = optimize,
19 .target = .{ .os_tag = .macos },
20 });
21 lib.addCSourceFile("main.c", &.{});
22 lib.linkLibC();
23
24 const exe = b.addExecutable(.{
25 .name = "main",
26 .optimize = optimize,
27 .target = .{ .os_tag = .macos },
28 });
29 exe.linkLibrary(lib);
30 exe.linkLibC();
31
32 const run = exe.run();
33 run.skip_foreign_checks = true;
34 run.expectExitCode(0);
35 test_step.dependOn(&run.step);
36}
test/link/macho/entry_in_archive/main.c created+5
...@@ -0,0 +1,5 @@
1#include <stdio.h>
2
3int main(int argc, char* argv[]) {
4 return 0;
5}
test/link/macho/entry_in_dylib/bootstrap.c created+5
...@@ -0,0 +1,5 @@
1extern int my_main();
2
3int bootstrap() {
4 return my_main();
5}
test/link/macho/entry_in_dylib/build.zig created+54
...@@ -0,0 +1,54 @@
1const std = @import("std");
2
3pub const requires_symlinks = true;
4
5pub fn build(b: *std.Build) void {
6 const test_step = b.step("test", "Test it");
7 b.default_step = test_step;
8
9 add(b, test_step, .Debug);
10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
13}
14
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
16 const lib = b.addSharedLibrary(.{
17 .name = "bootstrap",
18 .optimize = optimize,
19 .target = .{ .os_tag = .macos },
20 });
21 lib.addCSourceFile("bootstrap.c", &.{});
22 lib.linkLibC();
23 lib.linker_allow_shlib_undefined = true;
24
25 const exe = b.addExecutable(.{
26 .name = "main",
27 .optimize = optimize,
28 .target = .{ .os_tag = .macos },
29 });
30 exe.addCSourceFile("main.c", &.{});
31 exe.linkLibrary(lib);
32 exe.linkLibC();
33 exe.entry_symbol_name = "_bootstrap";
34 exe.forceUndefinedSymbol("_my_main");
35
36 const check_exe = exe.checkObject();
37 check_exe.checkStart("segname __TEXT");
38 check_exe.checkNext("vmaddr {text_vmaddr}");
39
40 check_exe.checkStart("sectname __stubs");
41 check_exe.checkNext("addr {stubs_vmaddr}");
42
43 check_exe.checkStart("cmd MAIN");
44 check_exe.checkNext("entryoff {entryoff}");
45
46 check_exe.checkComputeCompare("text_vmaddr entryoff +", .{
47 .op = .eq,
48 .value = .{ .variable = "stubs_vmaddr" }, // The entrypoint should be a synthetic stub
49 });
50
51 const run = check_exe.runAndCompare();
52 run.expectStdOutEqual("Hello!\n");
53 test_step.dependOn(&run.step);
54}
test/link/macho/entry_in_dylib/main.c created+6
...@@ -0,0 +1,6 @@
1#include <stdio.h>
2
3int my_main() {
4 fprintf(stdout, "Hello!\n");
5 return 0;
6}
tools/update_clang_options.zig+4
...@@ -468,6 +468,10 @@ const known_options = [_]KnownOpt{...@@ -468,6 +468,10 @@ const known_options = [_]KnownOpt{
468 .name = "e",468 .name = "e",
469 .ident = "entry",469 .ident = "entry",
470 },470 },
471 .{
472 .name = "u",
473 .ident = "force_undefined_symbol",
474 },
471 .{475 .{
472 .name = "weak-l",476 .name = "weak-l",
473 .ident = "weak_library",477 .ident = "weak_library",