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,
202202
203203entry_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
205210/// Overrides the default stack size
206211stack_size: ?u64 = null,
207212
......@@ -386,6 +391,7 @@ pub fn create(owner: *std.Build, options: Options) *CompileStep {
386391 .override_dest_dir = null,
387392 .installed_path = null,
388393 .install_step = null,
394 .force_undefined_symbols = StringHashMap(void).init(owner.allocator),
389395
390396 .output_path_source = GeneratedFile{ .step = &self.step },
391397 .output_lib_path_source = GeneratedFile{ .step = &self.step },
......@@ -568,6 +574,11 @@ pub fn setLinkerScriptPath(self: *CompileStep, source: FileSource) void {
568574 source.addStepDependencies(&self.step);
569575}
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
571582pub fn linkFramework(self: *CompileStep, framework_name: []const u8) void {
572583 const b = self.step.owner;
573584 self.frameworks.put(b.dupe(framework_name), .{}) catch @panic("OOM");
......@@ -1266,6 +1277,14 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
12661277 try zig_args.append(entry);
12671278 }
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
12691288 if (self.stack_size) |stack_size| {
12701289 try zig_args.append("--stack");
12711290 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 {
602602 parent_compilation_link_libc: bool = false,
603603 hash_style: link.HashStyle = .both,
604604 entry: ?[]const u8 = null,
605 force_undefined_symbols: std.StringArrayHashMapUnmanaged(void) = .{},
605606 stack_size_override: ?u64 = null,
606607 image_base_override: ?u64 = null,
607608 self_exe_path: ?[]const u8 = null,
......@@ -1523,7 +1524,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
15231524 .headerpad_size = options.headerpad_size,
15241525 .headerpad_max_install_names = options.headerpad_max_install_names,
15251526 .dead_strip_dylibs = options.dead_strip_dylibs,
1526 .force_undefined_symbols = .{},
1527 .force_undefined_symbols = options.force_undefined_symbols,
15271528 .pdb_source_path = options.pdb_source_path,
15281529 .pdb_out_path = options.pdb_out_path,
15291530 });
......@@ -2186,7 +2187,7 @@ fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemo
21862187/// to remind the programmer to update multiple related pieces of code that
21872188/// are in different locations. Bump this number when adding or deleting
21882189/// anything from the link cache manifest.
2189pub const link_hash_implementation_version = 7;
2190pub const link_hash_implementation_version = 8;
21902191
21912192fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void {
21922193 const gpa = comp.gpa;
......@@ -2196,7 +2197,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
21962197 defer arena_allocator.deinit();
21972198 const arena = arena_allocator.allocator();
21982199
2199 comptime assert(link_hash_implementation_version == 7);
2200 comptime assert(link_hash_implementation_version == 8);
22002201
22012202 if (comp.bin_file.options.module) |mod| {
22022203 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"),
14481448.{
14491449 .name = "u",
14501450 .syntax = .flag,
1451 .zig_equivalent = .other,
1451 .zig_equivalent = .force_undefined_symbol,
14521452 .pd1 = true,
14531453 .pd2 = false,
14541454 .psl = true,
......@@ -7170,7 +7170,14 @@ joinpd1("d"),
71707170 .pd2 = false,
71717171 .psl = true,
71727172},
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},
71747181.{
71757182 .name = "x",
71767183 .syntax = .joined_or_separate,
src/link.zig+1-2
......@@ -186,8 +186,7 @@ pub const Options = struct {
186186
187187 /// List of symbols forced as undefined in the symbol table
188188 /// thus forcing their resolution by the linker.
189 /// Corresponds to `-u <symbol>` for ELF and `/include:<symbol>` for COFF/PE.
190 /// TODO add handling for MachO.
189 /// Corresponds to `-u <symbol>` for ELF/MachO and `/include:<symbol>` for COFF/PE.
191190 force_undefined_symbols: std.StringArrayHashMapUnmanaged(void),
192191
193192 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
6363 man = comp.cache_parent.obtain();
6464 self.base.releaseLock();
6565
66 comptime assert(Compilation.link_hash_implementation_version == 7);
66 comptime assert(Compilation.link_hash_implementation_version == 8);
6767
6868 for (self.base.options.objects) |obj| {
6969 _ = 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
13051305 // We are about to obtain this lock, so here we give other processes a chance first.
13061306 self.base.releaseLock();
13071307
1308 comptime assert(Compilation.link_hash_implementation_version == 7);
1308 comptime assert(Compilation.link_hash_implementation_version == 8);
13091309
13101310 try man.addOptionalFile(self.base.options.linker_script);
13111311 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
39813981/// Returns symbol location corresponding to the set entrypoint.
39823982/// Asserts output mode is executable.
39833983pub 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;
39853985 const global = self.getGlobal(entry_name) orelse {
39863986 log.err("entrypoint '{s}' not found", .{entry_name});
39873987 return error.MissingMainEntrypoint;
src/link/MachO/ZldAtom.zig+1-1
......@@ -389,7 +389,7 @@ pub fn addGotEntry(zld: *Zld, target: SymbolWithLoc) !void {
389389 try zld.got_table.putNoClobber(gpa, target, got_index);
390390}
391391
392fn addStub(zld: *Zld, target: SymbolWithLoc) !void {
392pub fn addStub(zld: *Zld, target: SymbolWithLoc) !void {
393393 const target_sym = zld.getSymbol(target);
394394 if (!target_sym.undf()) return;
395395 if (zld.stubs_table.contains(target)) return;
src/link/MachO/dead_strip.zig+34-22
......@@ -12,6 +12,7 @@ const Allocator = mem.Allocator;
1212const AtomIndex = @import("zld.zig").AtomIndex;
1313const Atom = @import("ZldAtom.zig");
1414const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
15const SymbolResolver = @import("zld.zig").SymbolResolver;
1516const UnwindInfo = @import("UnwindInfo.zig");
1617const Zld = @import("zld.zig").Zld;
1718
......@@ -19,7 +20,7 @@ const N_DEAD = @import("zld.zig").N_DEAD;
1920
2021const AtomTable = std.AutoHashMap(AtomIndex, void);
2122
22pub fn gcAtoms(zld: *Zld) !void {
23pub fn gcAtoms(zld: *Zld, resolver: *const SymbolResolver) !void {
2324 const gpa = zld.gpa;
2425
2526 var arena = std.heap.ArenaAllocator.init(gpa);
......@@ -31,27 +32,36 @@ pub fn gcAtoms(zld: *Zld) !void {
3132 var alive = AtomTable.init(arena.allocator());
3233 try alive.ensureTotalCapacity(@intCast(u32, zld.atoms.items.len));
3334
34 try collectRoots(zld, &roots);
35 try collectRoots(zld, &roots, resolver);
3536 try mark(zld, roots, &alive);
3637 prune(zld, alive);
3738}
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 {
4054 log.debug("collecting roots", .{});
4155
4256 switch (zld.options.output_mode) {
4357 .Exe => {
4458 // Add entrypoint as GC root
4559 const global: SymbolWithLoc = zld.getEntryPoint();
46 const object = zld.objects.items[global.getFile().?];
47 const atom_index = object.getAtomIndexForSymbol(global.sym_index).?; // panic here means fatal error
48 _ = try roots.getOrPut(atom_index);
49
50 log.debug("root(ATOM({d}, %{d}, {?d}))", .{
51 atom_index,
52 zld.getAtom(atom_index).sym_index,
53 zld.getAtom(atom_index).getFile(),
54 });
60 if (global.getFile()) |file| {
61 try addRoot(zld, roots, file, global);
62 } else {
63 assert(zld.getSymbol(global).undf()); // Stub as our entrypoint is in a dylib.
64 }
5565 },
5666 else => |other| {
5767 assert(other == .Lib);
......@@ -60,20 +70,22 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {
6070 const sym = zld.getSymbol(global);
6171 if (sym.undf()) continue;
6272
63 const file = global.getFile() orelse continue; // synthetic globals are atomless
64 const object = zld.objects.items[file];
65 const atom_index = object.getAtomIndexForSymbol(global.sym_index).?; // panic here means fatal error
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 if (global.getFile()) |file| {
74 try addRoot(zld, roots, file, global);
75 }
7376 }
7477 },
7578 }
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
7789 for (zld.objects.items) |object| {
7890 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;
88const Allocator = mem.Allocator;
99const 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.
1115pub const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld";
1216
1317fn 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 {
932932 }
933933 }
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
935972 fn resolveSymbolsInObject(self: *Zld, object_id: u32, resolver: *SymbolResolver) !void {
936973 const object = &self.objects.items[object_id];
937974 const in_symtab = object.in_symtab orelse return;
......@@ -975,9 +1012,7 @@ pub const Zld = struct {
9751012 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = object_id + 1 };
9761013
9771014 const global_index = resolver.table.get(sym_name) orelse {
978 const gpa = self.gpa;
979 const global_index = @intCast(u32, self.globals.items.len);
980 try self.globals.append(gpa, sym_loc);
1015 const global_index = try self.addGlobal(sym_loc);
9811016 try resolver.table.putNoClobber(sym_name, global_index);
9821017 if (sym.undf() and !sym.tentative()) {
9831018 try resolver.unresolved.putNoClobber(global_index, {});
......@@ -1034,8 +1069,10 @@ pub const Zld = struct {
10341069 };
10351070
10361071 if (update_global) {
1037 const global_object = &self.objects.items[global.getFile().?];
1038 global_object.globals_lookup[global.sym_index] = global_index;
1072 if (global.getFile()) |file| {
1073 const global_object = &self.objects.items[file];
1074 global_object.globals_lookup[global.sym_index] = global_index;
1075 }
10391076 _ = resolver.unresolved.swapRemove(resolver.table.get(sym_name).?);
10401077 global.* = sym_loc;
10411078 } else {
......@@ -1180,9 +1217,7 @@ pub const Zld = struct {
11801217 global.* = sym_loc;
11811218 self.mh_execute_header_index = global_index;
11821219 } else {
1183 const global_index = @intCast(u32, self.globals.items.len);
1184 try self.globals.append(gpa, sym_loc);
1185 self.mh_execute_header_index = global_index;
1220 self.mh_execute_header_index = try self.addGlobal(sym_loc);
11861221 }
11871222 }
11881223
......@@ -1358,6 +1393,12 @@ pub const Zld = struct {
13581393 return index;
13591394 }
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
13611402 fn allocateSpecialSymbols(self: *Zld) !void {
13621403 for (&[_]?u32{
13631404 self.dso_handle_index,
......@@ -3507,7 +3548,7 @@ pub const SymbolWithLoc = extern struct {
35073548 }
35083549};
35093550
3510const SymbolResolver = struct {
3551pub const SymbolResolver = struct {
35113552 arena: Allocator,
35123553 table: std.StringHashMap(u32),
35133554 unresolved: std.AutoArrayHashMap(u32, void),
......@@ -3568,7 +3609,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
35683609 // We are about to obtain this lock, so here we give other processes a chance first.
35693610 macho_file.base.releaseLock();
35703611
3571 comptime assert(Compilation.link_hash_implementation_version == 7);
3612 comptime assert(Compilation.link_hash_implementation_version == 8);
35723613
35733614 for (options.objects) |obj| {
35743615 _ = try man.addFile(obj.path, null);
......@@ -3598,6 +3639,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
35983639 }
35993640 link.hashAddSystemLibs(&man.hash, options.system_libs);
36003641 man.hash.addOptionalBytes(options.sysroot);
3642 man.hash.addListOfBytes(options.force_undefined_symbols.keys());
36013643 try man.addOptionalFile(options.entitlements);
36023644
36033645 // 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
39804022 .table = std.StringHashMap(u32).init(arena),
39814023 .unresolved = std.AutoArrayHashMap(u32, void).init(arena),
39824024 };
3983
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);
4025 try zld.resolveSymbols(&resolver);
39944026
39954027 if (resolver.unresolved.count() > 0) {
39964028 return error.UndefinedSymbolReference;
......@@ -4003,11 +4035,8 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
40034035 }
40044036
40054037 if (options.output_mode == .Exe) {
4006 const entry_name = options.entry orelse "_main";
4007 const global_index = resolver.table.get(entry_name) orelse {
4008 log.err("entrypoint '{s}' not found", .{entry_name});
4009 return error.MissingMainEntrypoint;
4010 };
4038 const entry_name = options.entry orelse load_commands.default_entry_point;
4039 const global_index = resolver.table.get(entry_name).?; // Error was flagged earlier
40114040 zld.entry_index = global_index;
40124041 }
40134042
......@@ -4016,13 +4045,23 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
40164045 }
40174046
40184047 if (gc_sections) {
4019 try dead_strip.gcAtoms(&zld);
4048 try dead_strip.gcAtoms(&zld, &resolver);
40204049 }
40214050
40224051 try zld.createDyldPrivateAtom();
40234052 try zld.createTentativeDefAtoms();
40244053 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
40264065 for (zld.objects.items) |object| {
40274066 for (object.atoms.items) |atom_index| {
40284067 const atom = zld.getAtom(atom_index);
......@@ -4134,8 +4173,18 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
41344173 const seg = zld.segments.items[seg_id];
41354174 const global = zld.getEntryPoint();
41364175 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
41374186 try lc_writer.writeStruct(macho.entry_point_command{
4138 .entryoff = @intCast(u32, sym.n_value - seg.vmaddr),
4187 .entryoff = @intCast(u32, addr - seg.vmaddr),
41394188 .stacksize = options.stack_size_override orelse 0,
41404189 });
41414190 } else {
src/link/Wasm.zig+2-2
......@@ -3059,7 +3059,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
30593059 // We are about to obtain this lock, so here we give other processes a chance first.
30603060 wasm.base.releaseLock();
30613061
3062 comptime assert(Compilation.link_hash_implementation_version == 7);
3062 comptime assert(Compilation.link_hash_implementation_version == 8);
30633063
30643064 for (options.objects) |obj| {
30653065 _ = try man.addFile(obj.path, null);
......@@ -4086,7 +4086,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
40864086 // We are about to obtain this lock, so here we give other processes a chance first.
40874087 wasm.base.releaseLock();
40884088
4089 comptime assert(Compilation.link_hash_implementation_version == 7);
4089 comptime assert(Compilation.link_hash_implementation_version == 8);
40904090
40914091 for (wasm.base.options.objects) |obj| {
40924092 _ = try man.addFile(obj.path, null);
src/main.zig+92-202
......@@ -478,6 +478,7 @@ const usage_build_generic =
478478 \\ --sysroot [path] Set the system root directory (usually /)
479479 \\ --version [ver] Dynamic library semver
480480 \\ --entry [name] Set the entrypoint symbol name
481 \\ --force_undefined [name] Specify the symbol must be defined for the link to succeed
481482 \\ -fsoname[=name] Override the default SONAME value
482483 \\ -fno-soname Disable emitting a SONAME
483484 \\ -fLLD Force using LLD as the linker
......@@ -680,6 +681,28 @@ const Listen = union(enum) {
680681 stdio,
681682};
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
683706fn buildOutputType(
684707 gpa: Allocator,
685708 arena: Allocator,
......@@ -784,6 +807,7 @@ fn buildOutputType(
784807 var test_evented_io = false;
785808 var test_no_exec = false;
786809 var entry: ?[]const u8 = null;
810 var force_undefined_symbols: std.StringArrayHashMapUnmanaged(void) = .{};
787811 var stack_size_override: ?u64 = null;
788812 var image_base_override: ?u64 = null;
789813 var use_llvm: ?bool = null;
......@@ -917,28 +941,7 @@ fn buildOutputType(
917941
918942 soname = .yes_default_value;
919943
920 const Iterator = struct {
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{
944 var args_iter = ArgsIterator{
942945 .args = all_args[2..],
943946 };
944947
......@@ -1029,6 +1032,8 @@ fn buildOutputType(
10291032 optimize_mode_string = args_iter.nextOrFatal();
10301033 } else if (mem.eql(u8, arg, "--entry")) {
10311034 entry = args_iter.nextOrFatal();
1035 } else if (mem.eql(u8, arg, "--force_undefined")) {
1036 try force_undefined_symbols.put(gpa, args_iter.nextOrFatal(), {});
10321037 } else if (mem.eql(u8, arg, "--stack")) {
10331038 const next_arg = args_iter.nextOrFatal();
10341039 stack_size_override = std.fmt.parseUnsigned(u64, next_arg, 0) catch |err| {
......@@ -1816,6 +1821,9 @@ fn buildOutputType(
18161821 .entry => {
18171822 entry = it.only_arg;
18181823 },
1824 .force_undefined_symbol => {
1825 try force_undefined_symbols.put(gpa, it.only_arg, {});
1826 },
18191827 .weak_library => try system_libs.put(it.only_arg, .{ .weak = true }),
18201828 .weak_framework => try frameworks.put(gpa, it.only_arg, .{ .weak = true }),
18211829 .headerpad_max_install_names => headerpad_max_install_names = true,
......@@ -1843,17 +1851,14 @@ fn buildOutputType(
18431851 }
18441852 }
18451853 // Parse linker args.
1846 var i: usize = 0;
1847 while (i < linker_args.items.len) : (i += 1) {
1848 const arg = linker_args.items[i];
1854 var linker_args_it = ArgsIterator{
1855 .args = linker_args.items,
1856 };
1857 while (linker_args_it.next()) |arg| {
18491858 if (mem.eql(u8, arg, "-soname") or
18501859 mem.eql(u8, arg, "--soname"))
18511860 {
1852 i += 1;
1853 if (i >= linker_args.items.len) {
1854 fatal("expected linker arg after '{s}'", .{arg});
1855 }
1856 const name = linker_args.items[i];
1861 const name = linker_args_it.nextOrFatal();
18571862 soname = .{ .yes = name };
18581863 // Use it as --name.
18591864 // Example: libsoundio.so.2
......@@ -1881,64 +1886,37 @@ fn buildOutputType(
18811886 }
18821887 provided_name = name[prefix..end];
18831888 } else if (mem.eql(u8, arg, "-rpath")) {
1884 i += 1;
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 try rpath_list.append(linker_args_it.nextOrFatal());
18891890 } else if (mem.eql(u8, arg, "--subsystem")) {
1890 i += 1;
1891 if (i >= linker_args.items.len) {
1892 fatal("expected linker arg after '{s}'", .{arg});
1893 }
1894 subsystem = try parseSubSystem(linker_args.items[i]);
1891 subsystem = try parseSubSystem(linker_args_it.nextOrFatal());
18951892 } else if (mem.eql(u8, arg, "-I") or
18961893 mem.eql(u8, arg, "--dynamic-linker") or
18971894 mem.eql(u8, arg, "-dynamic-linker"))
18981895 {
1899 i += 1;
1900 if (i >= linker_args.items.len) {
1901 fatal("expected linker arg after '{s}'", .{arg});
1902 }
1903 target_dynamic_linker = linker_args.items[i];
1896 target_dynamic_linker = linker_args_it.nextOrFatal();
19041897 } else if (mem.eql(u8, arg, "-E") or
19051898 mem.eql(u8, arg, "--export-dynamic") or
19061899 mem.eql(u8, arg, "-export-dynamic"))
19071900 {
19081901 rdynamic = true;
19091902 } else if (mem.eql(u8, arg, "--version-script")) {
1910 i += 1;
1911 if (i >= linker_args.items.len) {
1912 fatal("expected linker arg after '{s}'", .{arg});
1913 }
1914 version_script = linker_args.items[i];
1903 version_script = linker_args_it.nextOrFatal();
19151904 } else if (mem.eql(u8, arg, "-O")) {
1916 i += 1;
1917 if (i >= linker_args.items.len) {
1918 fatal("expected linker arg after '{s}'", .{arg});
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) });
1905 const opt = linker_args_it.nextOrFatal();
1906 linker_optimization = std.fmt.parseUnsigned(u8, opt, 10) catch |err| {
1907 fatal("unable to parse optimization level '{s}': {s}", .{ opt, @errorName(err) });
19221908 };
19231909 } else if (mem.startsWith(u8, arg, "-O")) {
19241910 linker_optimization = std.fmt.parseUnsigned(u8, arg["-O".len..], 10) catch |err| {
19251911 fatal("unable to parse optimization level '{s}': {s}", .{ arg, @errorName(err) });
19261912 };
19271913 } else if (mem.eql(u8, arg, "-pagezero_size")) {
1928 i += 1;
1929 if (i >= linker_args.items.len) {
1930 fatal("expected linker arg after '{s}'", .{arg});
1931 }
1932 const next_arg = linker_args.items[i];
1914 const next_arg = linker_args_it.nextOrFatal();
19331915 pagezero_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| {
19341916 fatal("unable to parse pagezero size '{s}': {s}", .{ next_arg, @errorName(err) });
19351917 };
19361918 } else if (mem.eql(u8, arg, "-headerpad")) {
1937 i += 1;
1938 if (i >= linker_args.items.len) {
1939 fatal("expected linker arg after '{s}'", .{arg});
1940 }
1941 const next_arg = linker_args.items[i];
1919 const next_arg = linker_args_it.nextOrFatal();
19421920 headerpad_size = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| {
19431921 fatal("unable to parse headerpad size '{s}': {s}", .{ next_arg, @errorName(err) });
19441922 };
......@@ -1961,11 +1939,7 @@ fn buildOutputType(
19611939 } else if (mem.eql(u8, arg, "--print-map")) {
19621940 linker_print_map = true;
19631941 } else if (mem.eql(u8, arg, "--sort-section")) {
1964 i += 1;
1965 if (i >= linker_args.items.len) {
1966 fatal("expected linker arg after '{s}'", .{arg});
1967 }
1968 const arg1 = linker_args.items[i];
1942 const arg1 = linker_args_it.nextOrFatal();
19691943 linker_sort_section = std.meta.stringToEnum(link.SortSection, arg1) orelse {
19701944 fatal("expected [name|alignment] after --sort-section, found '{s}'", .{arg1});
19711945 };
......@@ -1998,28 +1972,16 @@ fn buildOutputType(
19981972 } else if (mem.startsWith(u8, arg, "--export=")) {
19991973 try linker_export_symbol_names.append(arg["--export=".len..]);
20001974 } else if (mem.eql(u8, arg, "--export")) {
2001 i += 1;
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]);
1975 try linker_export_symbol_names.append(linker_args_it.nextOrFatal());
20061976 } else if (mem.eql(u8, arg, "--compress-debug-sections")) {
2007 i += 1;
2008 if (i >= linker_args.items.len) {
2009 fatal("expected linker arg after '{s}'", .{arg});
2010 }
2011 const arg1 = linker_args.items[i];
1977 const arg1 = linker_args_it.nextOrFatal();
20121978 linker_compress_debug_sections = std.meta.stringToEnum(link.CompressDebugSections, arg1) orelse {
20131979 fatal("expected [none|zlib] after --compress-debug-sections, found '{s}'", .{arg1});
20141980 };
20151981 } else if (mem.startsWith(u8, arg, "-z")) {
20161982 var z_arg = arg[2..];
20171983 if (z_arg.len == 0) {
2018 i += 1;
2019 if (i >= linker_args.items.len) {
2020 fatal("expected linker extension flag after '{s}'", .{arg});
2021 }
2022 z_arg = linker_args.items[i];
1984 z_arg = linker_args_it.nextOrFatal();
20231985 }
20241986 if (mem.eql(u8, z_arg, "nodelete")) {
20251987 linker_z_nodelete = true;
......@@ -2056,51 +2018,33 @@ fn buildOutputType(
20562018 fatal("unsupported linker extension flag: -z {s}", .{z_arg});
20572019 }
20582020 } else if (mem.eql(u8, arg, "--major-image-version")) {
2059 i += 1;
2060 if (i >= linker_args.items.len) {
2061 fatal("expected linker arg after '{s}'", .{arg});
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) });
2021 const major = linker_args_it.nextOrFatal();
2022 version.major = std.fmt.parseUnsigned(u32, major, 10) catch |err| {
2023 fatal("unable to parse major image version '{s}': {s}", .{ major, @errorName(err) });
20652024 };
20662025 have_version = true;
20672026 } else if (mem.eql(u8, arg, "--minor-image-version")) {
2068 i += 1;
2069 if (i >= linker_args.items.len) {
2070 fatal("expected linker arg after '{s}'", .{arg});
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) });
2027 const minor = linker_args_it.nextOrFatal();
2028 version.minor = std.fmt.parseUnsigned(u32, minor, 10) catch |err| {
2029 fatal("unable to parse minor image version '{s}': {s}", .{ minor, @errorName(err) });
20742030 };
20752031 have_version = true;
20762032 } else if (mem.eql(u8, arg, "-e") or mem.eql(u8, arg, "--entry")) {
2077 i += 1;
2078 if (i >= linker_args.items.len) {
2079 fatal("expected linker arg after '{s}'", .{arg});
2080 }
2081 entry = linker_args.items[i];
2033 entry = linker_args_it.nextOrFatal();
2034 } else if (mem.eql(u8, arg, "-u")) {
2035 try force_undefined_symbols.put(gpa, linker_args_it.nextOrFatal(), {});
20822036 } else if (mem.eql(u8, arg, "--stack") or mem.eql(u8, arg, "-stack_size")) {
2083 i += 1;
2084 if (i >= linker_args.items.len) {
2085 fatal("expected linker arg after '{s}'", .{arg});
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) });
2037 const stack_size = linker_args_it.nextOrFatal();
2038 stack_size_override = std.fmt.parseUnsigned(u64, stack_size, 0) catch |err| {
2039 fatal("unable to parse stack size override '{s}': {s}", .{ stack_size, @errorName(err) });
20892040 };
20902041 } else if (mem.eql(u8, arg, "--image-base")) {
2091 i += 1;
2092 if (i >= linker_args.items.len) {
2093 fatal("expected linker arg after '{s}'", .{arg});
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) });
2042 const image_base = linker_args_it.nextOrFatal();
2043 image_base_override = std.fmt.parseUnsigned(u64, image_base, 0) catch |err| {
2044 fatal("unable to parse image base override '{s}': {s}", .{ image_base, @errorName(err) });
20972045 };
20982046 } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) {
2099 i += 1;
2100 if (i >= linker_args.items.len) {
2101 fatal("expected linker arg after '{s}'", .{arg});
2102 }
2103 linker_script = linker_args.items[i];
2047 linker_script = linker_args_it.nextOrFatal();
21042048 } else if (mem.eql(u8, arg, "--eh-frame-hdr")) {
21052049 link_eh_frame_hdr = true;
21062050 } else if (mem.eql(u8, arg, "--no-eh-frame-hdr")) {
......@@ -2138,130 +2082,74 @@ fn buildOutputType(
21382082 } else if (mem.eql(u8, arg, "--major-os-version") or
21392083 mem.eql(u8, arg, "--minor-os-version"))
21402084 {
2141 i += 1;
2142 if (i >= linker_args.items.len) {
2143 fatal("expected linker arg after '{s}'", .{arg});
2144 }
21452085 // This option does not do anything.
2086 _ = linker_args_it.nextOrFatal();
21462087 } else if (mem.eql(u8, arg, "--major-subsystem-version")) {
2147 i += 1;
2148 if (i >= linker_args.items.len) {
2149 fatal("expected linker arg after '{s}'", .{arg});
2150 }
2151
2088 const major = linker_args_it.nextOrFatal();
21522089 major_subsystem_version = std.fmt.parseUnsigned(
21532090 u32,
2154 linker_args.items[i],
2091 major,
21552092 10,
21562093 ) 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) });
21582095 };
21592096 } else if (mem.eql(u8, arg, "--minor-subsystem-version")) {
2160 i += 1;
2161 if (i >= linker_args.items.len) {
2162 fatal("expected linker arg after '{s}'", .{arg});
2163 }
2164
2097 const minor = linker_args_it.nextOrFatal();
21652098 minor_subsystem_version = std.fmt.parseUnsigned(
21662099 u32,
2167 linker_args.items[i],
2100 minor,
21682101 10,
21692102 ) 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) });
21712104 };
21722105 } else if (mem.eql(u8, arg, "-framework")) {
2173 i += 1;
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], .{});
2106 try frameworks.put(gpa, linker_args_it.nextOrFatal(), .{});
21782107 } else if (mem.eql(u8, arg, "-weak_framework")) {
2179 i += 1;
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 });
2108 try frameworks.put(gpa, linker_args_it.nextOrFatal(), .{ .weak = true });
21842109 } else if (mem.eql(u8, arg, "-needed_framework")) {
2185 i += 1;
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 });
2110 try frameworks.put(gpa, linker_args_it.nextOrFatal(), .{ .needed = true });
21902111 } else if (mem.eql(u8, arg, "-needed_library")) {
2191 i += 1;
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 });
2112 try system_libs.put(linker_args_it.nextOrFatal(), .{ .needed = true });
21962113 } else if (mem.startsWith(u8, arg, "-weak-l")) {
21972114 try system_libs.put(arg["-weak-l".len..], .{ .weak = true });
21982115 } else if (mem.eql(u8, arg, "-weak_library")) {
2199 i += 1;
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 });
2116 try system_libs.put(linker_args_it.nextOrFatal(), .{ .weak = true });
22042117 } else if (mem.eql(u8, arg, "-compatibility_version")) {
2205 i += 1;
2206 if (i >= linker_args.items.len) {
2207 fatal("expected linker arg after '{s}'", .{arg});
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) });
2118 const compat_version = linker_args_it.nextOrFatal();
2119 compatibility_version = std.builtin.Version.parse(compat_version) catch |err| {
2120 fatal("unable to parse -compatibility_version '{s}': {s}", .{ compat_version, @errorName(err) });
22112121 };
22122122 } else if (mem.eql(u8, arg, "-current_version")) {
2213 i += 1;
2214 if (i >= linker_args.items.len) {
2215 fatal("expected linker arg after '{s}'", .{arg});
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) });
2123 const curr_version = linker_args_it.nextOrFatal();
2124 version = std.builtin.Version.parse(curr_version) catch |err| {
2125 fatal("unable to parse -current_version '{s}': {s}", .{ curr_version, @errorName(err) });
22192126 };
22202127 have_version = true;
22212128 } else if (mem.eql(u8, arg, "--out-implib") or
22222129 mem.eql(u8, arg, "-implib"))
22232130 {
2224 i += 1;
2225 if (i >= linker_args.items.len) {
2226 fatal("expected linker arg after '{s}'", .{arg});
2227 }
2228 emit_implib = .{ .yes = linker_args.items[i] };
2131 emit_implib = .{ .yes = linker_args_it.nextOrFatal() };
22292132 emit_implib_arg_provided = true;
22302133 } else if (mem.eql(u8, arg, "-undefined")) {
2231 i += 1;
2232 if (i >= linker_args.items.len) {
2233 fatal("expected linker arg after '{s}'", .{arg});
2234 }
2235 if (mem.eql(u8, "dynamic_lookup", linker_args.items[i])) {
2134 const lookup_type = linker_args_it.nextOrFatal();
2135 if (mem.eql(u8, "dynamic_lookup", lookup_type)) {
22362136 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)) {
22382138 linker_allow_shlib_undefined = false;
22392139 } else {
2240 fatal("unsupported -undefined option '{s}'", .{linker_args.items[i]});
2140 fatal("unsupported -undefined option '{s}'", .{lookup_type});
22412141 }
22422142 } else if (mem.eql(u8, arg, "-install_name")) {
2243 i += 1;
2244 if (i >= linker_args.items.len) {
2245 fatal("expected linker arg after '{s}'", .{arg});
2246 }
2247 install_name = linker_args.items[i];
2143 install_name = linker_args_it.nextOrFatal();
22482144 } 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 }
22532145 try link_objects.append(.{
2254 .path = linker_args.items[i],
2146 .path = linker_args_it.nextOrFatal(),
22552147 .must_link = true,
22562148 });
22572149 } else if (mem.eql(u8, arg, "-hash-style") or
22582150 mem.eql(u8, arg, "--hash-style"))
22592151 {
2260 i += 1;
2261 if (i >= linker_args.items.len) {
2262 fatal("expected linker arg after '{s}'", .{arg});
2263 }
2264 const next_arg = linker_args.items[i];
2152 const next_arg = linker_args_it.nextOrFatal();
22652153 hash_style = std.meta.stringToEnum(link.HashStyle, next_arg) orelse {
22662154 fatal("expected [sysv|gnu|both] after --hash-style, found '{s}'", .{
22672155 next_arg,
......@@ -3219,6 +3107,7 @@ fn buildOutputType(
32193107 .link_eh_frame_hdr = link_eh_frame_hdr,
32203108 .link_emit_relocs = link_emit_relocs,
32213109 .entry = entry,
3110 .force_undefined_symbols = force_undefined_symbols,
32223111 .stack_size_override = stack_size_override,
32233112 .image_base_override = image_base_override,
32243113 .strip = strip,
......@@ -5295,6 +5184,7 @@ pub const ClangArgIterator = struct {
52955184 emit_llvm,
52965185 sysroot,
52975186 entry,
5187 force_undefined_symbol,
52985188 weak_library,
52995189 weak_framework,
53005190 headerpad_max_install_names,
test/link.zig+8
......@@ -104,6 +104,14 @@ pub const cases = [_]Case{
104104 .build_root = "test/link/macho/entry",
105105 .import = @import("link/macho/entry/build.zig"),
106106 },
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 },
107115 .{
108116 .build_root = "test/link/macho/headerpad",
109117 .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{
468468 .name = "e",
469469 .ident = "entry",
470470 },
471 .{
472 .name = "u",
473 .ident = "force_undefined_symbol",
474 },
471475 .{
472476 .name = "weak-l",
473477 .ident = "weak_library",