authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-04-01 11:51:05+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-04-01 14:22:44+02:00
loga88c0b4d089d286e36351cf476c8d982fb730541
treea4d985e8a6bd7f7b36b66f24e21e1ff53ff5303a
parent2dd178443abcbd91a923c64a4fd066caef974a82

link: handle -u flag in all linkers

Also clean up parsing of linker args - reuse `ArgsIterator`. In MachO, ensure we add every symbol marked with `-u` as undefined before proceeding with symbol resolution. Additionally, ensure those symbols are never garbage collected. MachO entry_in_dylib test: pass `-u _my_main` when linking executable so that it is not incorrectly garbage collected by the linker.

12 files changed, 185 insertions(+), 246 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/dead_strip.zig+30-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,12 +32,25 @@ pub fn gcAtoms(zld: *Zld) !void {...@@ -31,12 +32,25 @@ 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) {
...@@ -44,15 +58,7 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {...@@ -44,15 +58,7 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {
44 // Add entrypoint as GC root58 // Add entrypoint as GC root
45 const global: SymbolWithLoc = zld.getEntryPoint();59 const global: SymbolWithLoc = zld.getEntryPoint();
46 if (global.getFile()) |file| {60 if (global.getFile()) |file| {
47 const object = zld.objects.items[file];61 try addRoot(zld, roots, file, global);
48 const atom_index = object.getAtomIndexForSymbol(global.sym_index).?; // panic here means fatal error
49 _ = try roots.getOrPut(atom_index);
50
51 log.debug("root(ATOM({d}, %{d}, {?d}))", .{
52 atom_index,
53 zld.getAtom(atom_index).sym_index,
54 zld.getAtom(atom_index).getFile(),
55 });
56 } else {62 } else {
57 assert(zld.getSymbol(global).undf()); // Stub as our entrypoint is in a dylib.63 assert(zld.getSymbol(global).undf()); // Stub as our entrypoint is in a dylib.
58 }64 }
...@@ -64,20 +70,22 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {...@@ -64,20 +70,22 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {
64 const sym = zld.getSymbol(global);70 const sym = zld.getSymbol(global);
65 if (sym.undf()) continue;71 if (sym.undf()) continue;
6672
67 const file = global.getFile() orelse continue; // synthetic globals are atomless73 if (global.getFile()) |file| {
68 const object = zld.objects.items[file];74 try addRoot(zld, roots, file, global);
69 const atom_index = object.getAtomIndexForSymbol(global.sym_index).?; // panic here means fatal error75 }
70 _ = try roots.getOrPut(atom_index);
71
72 log.debug("root(ATOM({d}, %{d}, {?d}))", .{
73 atom_index,
74 zld.getAtom(atom_index).sym_index,
75 zld.getAtom(atom_index).getFile(),
76 });
77 }76 }
78 },77 },
79 }78 }
8079
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
81 for (zld.objects.items) |object| {89 for (zld.objects.items) |object| {
82 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;
8391
src/link/MachO/zld.zig+21-11
...@@ -932,20 +932,29 @@ pub const Zld = struct {...@@ -932,20 +932,29 @@ 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
935 fn resolveSymbols(self: *Zld, resolver: *SymbolResolver) !void {946 fn resolveSymbols(self: *Zld, resolver: *SymbolResolver) !void {
936 // We add the specified entrypoint as the first unresolved symbols so that947 // We add the specified entrypoint as the first unresolved symbols so that
937 // we search for it in libraries should there be no object files specified948 // we search for it in libraries should there be no object files specified
938 // on the linker line.949 // on the linker line.
939 if (self.options.output_mode == .Exe) {950 if (self.options.output_mode == .Exe) {
940 const entry_name = self.options.entry orelse load_commands.default_entry_point;951 const entry_name = self.options.entry orelse load_commands.default_entry_point;
941 const sym_index = try self.allocateSymbol();952 try self.forceSymbolDefined(entry_name, resolver);
942 const sym_loc = SymbolWithLoc{ .sym_index = sym_index };953 }
943 const sym = self.getSymbolPtr(sym_loc);954
944 sym.n_strx = try self.strtab.insert(self.gpa, entry_name);955 // Force resolution of any symbols requested by the user.
945 sym.n_type = macho.N_UNDF | macho.N_EXT;956 for (self.options.force_undefined_symbols.keys()) |sym_name| {
946 const global_index = try self.addGlobal(sym_loc);957 try self.forceSymbolDefined(sym_name, resolver);
947 try resolver.table.putNoClobber(entry_name, global_index);
948 try resolver.unresolved.putNoClobber(global_index, {});
949 }958 }
950959
951 for (self.objects.items, 0..) |_, object_id| {960 for (self.objects.items, 0..) |_, object_id| {
...@@ -3539,7 +3548,7 @@ pub const SymbolWithLoc = extern struct {...@@ -3539,7 +3548,7 @@ pub const SymbolWithLoc = extern struct {
3539 }3548 }
3540};3549};
35413550
3542const SymbolResolver = struct {3551pub const SymbolResolver = struct {
3543 arena: Allocator,3552 arena: Allocator,
3544 table: std.StringHashMap(u32),3553 table: std.StringHashMap(u32),
3545 unresolved: std.AutoArrayHashMap(u32, void),3554 unresolved: std.AutoArrayHashMap(u32, void),
...@@ -3600,7 +3609,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -3600,7 +3609,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
3600 // 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.
3601 macho_file.base.releaseLock();3610 macho_file.base.releaseLock();
36023611
3603 comptime assert(Compilation.link_hash_implementation_version == 7);3612 comptime assert(Compilation.link_hash_implementation_version == 8);
36043613
3605 for (options.objects) |obj| {3614 for (options.objects) |obj| {
3606 _ = try man.addFile(obj.path, null);3615 _ = try man.addFile(obj.path, null);
...@@ -3630,6 +3639,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -3630,6 +3639,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
3630 }3639 }
3631 link.hashAddSystemLibs(&man.hash, options.system_libs);3640 link.hashAddSystemLibs(&man.hash, options.system_libs);
3632 man.hash.addOptionalBytes(options.sysroot);3641 man.hash.addOptionalBytes(options.sysroot);
3642 man.hash.addListOfBytes(options.force_undefined_symbols.keys());
3633 try man.addOptionalFile(options.entitlements);3643 try man.addOptionalFile(options.entitlements);
36343644
3635 // 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
...@@ -4035,7 +4045,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -4035,7 +4045,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
4035 }4045 }
40364046
4037 if (gc_sections) {4047 if (gc_sections) {
4038 try dead_strip.gcAtoms(&zld);4048 try dead_strip.gcAtoms(&zld, &resolver);
4039 }4049 }
40404050
4041 try zld.createDyldPrivateAtom();4051 try zld.createDyldPrivateAtom();
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/macho/entry_in_dylib/build.zig+1
...@@ -31,6 +31,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize...@@ -31,6 +31,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
31 exe.linkLibrary(lib);31 exe.linkLibrary(lib);
32 exe.linkLibC();32 exe.linkLibC();
33 exe.entry_symbol_name = "_bootstrap";33 exe.entry_symbol_name = "_bootstrap";
34 exe.forceUndefinedSymbol("_my_main");
3435
35 const check_exe = exe.checkObject();36 const check_exe = exe.checkObject();
36 check_exe.checkStart("segname __TEXT");37 check_exe.checkStart("segname __TEXT");
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",