| author | |
| committer | |
| log | ab44b454d0408a7968354f8889a2e57c02396153 |
| tree | 23258a2a83fc450c430e8c29f301cdd7cb2060de |
| parent | 381dc2d9509ffeaf60a1775ee0983c7dd1b9e346 |
| parent | e0bf7b6424a4b3d21e10e9371e0ebed7ba9730a5 |
| signature |
macho: handle cases where entrypoint is in an archive or dylib20 files changed, 364 insertions(+), 265 deletions(-)
lib/std/Build/CompileStep.zig+19| ... | @@ -202,6 +202,11 @@ subsystem: ?std.Target.SubSystem = null, | ... | @@ -202,6 +202,11 @@ subsystem: ?std.Target.SubSystem = null, |
| 202 | 202 | ||
| 203 | entry_symbol_name: ?[]const u8 = null, | 203 | entry_symbol_name: ?[]const u8 = null, |
| 204 | 204 | ||
| 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. | ||
| 208 | force_undefined_symbols: std.StringHashMap(void), | ||
| 209 | |||
| 205 | /// Overrides the default stack size | 210 | /// Overrides the default stack size |
| 206 | stack_size: ?u64 = null, | 211 | stack_size: ?u64 = null, |
| 207 | 212 | ||
| ... | @@ -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), | ||
| 389 | 395 | ||
| 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 | } |
| 570 | 576 | ||
| 577 | pub 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 | |||
| 571 | pub fn linkFramework(self: *CompileStep, framework_name: []const u8) void { | 582 | pub 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 | } |
| 1268 | 1279 | ||
| 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 that | 2187 | /// to remind the programmer to update multiple related pieces of code that |
| 2187 | /// are in different locations. Bump this number when adding or deleting | 2188 | /// 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. |
| 2189 | pub const link_hash_implementation_version = 7; | 2190 | pub const link_hash_implementation_version = 8; |
| 2190 | 2191 | ||
| 2191 | fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void { | 2192 | fn 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(); |
| 2198 | 2199 | ||
| 2199 | comptime assert(link_hash_implementation_version == 7); | 2200 | comptime assert(link_hash_implementation_version == 8); |
| 2200 | 2201 | ||
| 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 | }, |
| 7173 | jspd1("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 { |
| 186 | 186 | ||
| 187 | /// List of symbols forced as undefined in the symbol table | 187 | /// 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), |
| 192 | 191 | ||
| 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(); |
| 65 | 65 | ||
| 66 | comptime assert(Compilation.link_hash_implementation_version == 7); | 66 | comptime assert(Compilation.link_hash_implementation_version == 8); |
| 67 | 67 | ||
| 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(); |
| 1307 | 1307 | ||
| 1308 | comptime assert(Compilation.link_hash_implementation_version == 7); | 1308 | comptime assert(Compilation.link_hash_implementation_version == 8); |
| 1309 | 1309 | ||
| 1310 | try man.addOptionalFile(self.base.options.linker_script); | 1310 | try man.addOptionalFile(self.base.options.linker_script); |
| 1311 | try man.addOptionalFile(self.base.options.version_script); | 1311 | try man.addOptionalFile(self.base.options.version_script); |
src/link/MachO.zig+1-1| ... | @@ -3981,7 +3981,7 @@ pub fn getStubsAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?At | ... | @@ -3981,7 +3981,7 @@ pub fn getStubsAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?At |
| 3981 | /// Returns symbol location corresponding to the set entrypoint. | 3981 | /// Returns symbol location corresponding to the set entrypoint. |
| 3982 | /// Asserts output mode is executable. | 3982 | /// Asserts output mode is executable. |
| 3983 | pub fn getEntryPoint(self: MachO) error{MissingMainEntrypoint}!SymbolWithLoc { | 3983 | pub fn getEntryPoint(self: MachO) error{MissingMainEntrypoint}!SymbolWithLoc { |
| 3984 | const entry_name = self.base.options.entry orelse "_main"; | 3984 | const entry_name = self.base.options.entry orelse load_commands.default_entry_point; |
| 3985 | const global = self.getGlobal(entry_name) orelse { | 3985 | const global = self.getGlobal(entry_name) orelse { |
| 3986 | log.err("entrypoint '{s}' not found", .{entry_name}); | 3986 | log.err("entrypoint '{s}' not found", .{entry_name}); |
| 3987 | return error.MissingMainEntrypoint; | 3987 | return error.MissingMainEntrypoint; |
src/link/MachO/ZldAtom.zig+1-1| ... | @@ -389,7 +389,7 @@ pub fn addGotEntry(zld: *Zld, target: SymbolWithLoc) !void { | ... | @@ -389,7 +389,7 @@ pub fn addGotEntry(zld: *Zld, target: SymbolWithLoc) !void { |
| 389 | try zld.got_table.putNoClobber(gpa, target, got_index); | 389 | try zld.got_table.putNoClobber(gpa, target, got_index); |
| 390 | } | 390 | } |
| 391 | 391 | ||
| 392 | fn addStub(zld: *Zld, target: SymbolWithLoc) !void { | 392 | pub fn addStub(zld: *Zld, target: SymbolWithLoc) !void { |
| 393 | const target_sym = zld.getSymbol(target); | 393 | const target_sym = zld.getSymbol(target); |
| 394 | if (!target_sym.undf()) return; | 394 | if (!target_sym.undf()) return; |
| 395 | if (zld.stubs_table.contains(target)) return; | 395 | if (zld.stubs_table.contains(target)) return; |
src/link/MachO/dead_strip.zig+34-22| ... | @@ -12,6 +12,7 @@ const Allocator = mem.Allocator; | ... | @@ -12,6 +12,7 @@ const Allocator = mem.Allocator; |
| 12 | const AtomIndex = @import("zld.zig").AtomIndex; | 12 | const AtomIndex = @import("zld.zig").AtomIndex; |
| 13 | const Atom = @import("ZldAtom.zig"); | 13 | const Atom = @import("ZldAtom.zig"); |
| 14 | const SymbolWithLoc = @import("zld.zig").SymbolWithLoc; | 14 | const SymbolWithLoc = @import("zld.zig").SymbolWithLoc; |
| 15 | const SymbolResolver = @import("zld.zig").SymbolResolver; | ||
| 15 | const UnwindInfo = @import("UnwindInfo.zig"); | 16 | const UnwindInfo = @import("UnwindInfo.zig"); |
| 16 | const Zld = @import("zld.zig").Zld; | 17 | const Zld = @import("zld.zig").Zld; |
| 17 | 18 | ||
| ... | @@ -19,7 +20,7 @@ const N_DEAD = @import("zld.zig").N_DEAD; | ... | @@ -19,7 +20,7 @@ const N_DEAD = @import("zld.zig").N_DEAD; |
| 19 | 20 | ||
| 20 | const AtomTable = std.AutoHashMap(AtomIndex, void); | 21 | const AtomTable = std.AutoHashMap(AtomIndex, void); |
| 21 | 22 | ||
| 22 | pub fn gcAtoms(zld: *Zld) !void { | 23 | pub fn gcAtoms(zld: *Zld, resolver: *const SymbolResolver) !void { |
| 23 | const gpa = zld.gpa; | 24 | const gpa = zld.gpa; |
| 24 | 25 | ||
| 25 | var arena = std.heap.ArenaAllocator.init(gpa); | 26 | var arena = std.heap.ArenaAllocator.init(gpa); |
| ... | @@ -31,27 +32,36 @@ pub fn gcAtoms(zld: *Zld) !void { | ... | @@ -31,27 +32,36 @@ pub fn gcAtoms(zld: *Zld) !void { |
| 31 | var alive = AtomTable.init(arena.allocator()); | 32 | var alive = AtomTable.init(arena.allocator()); |
| 32 | try alive.ensureTotalCapacity(@intCast(u32, zld.atoms.items.len)); | 33 | try alive.ensureTotalCapacity(@intCast(u32, zld.atoms.items.len)); |
| 33 | 34 | ||
| 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 | } |
| 38 | 39 | ||
| 39 | fn collectRoots(zld: *Zld, roots: *AtomTable) !void { | 40 | fn 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 | |||
| 53 | fn collectRoots(zld: *Zld, roots: *AtomTable, resolver: *const SymbolResolver) !void { | ||
| 40 | log.debug("collecting roots", .{}); | 54 | log.debug("collecting roots", .{}); |
| 41 | 55 | ||
| 42 | switch (zld.options.output_mode) { | 56 | switch (zld.options.output_mode) { |
| 43 | .Exe => { | 57 | .Exe => { |
| 44 | // Add entrypoint as GC root | 58 | // Add entrypoint as GC root |
| 45 | const global: SymbolWithLoc = zld.getEntryPoint(); | 59 | const global: SymbolWithLoc = zld.getEntryPoint(); |
| 46 | const object = zld.objects.items[global.getFile().?]; | 60 | if (global.getFile()) |file| { |
| 47 | const atom_index = object.getAtomIndexForSymbol(global.sym_index).?; // panic here means fatal error | 61 | try addRoot(zld, roots, file, global); |
| 48 | _ = try roots.getOrPut(atom_index); | 62 | } else { |
| 49 | 63 | assert(zld.getSymbol(global).undf()); // Stub as our entrypoint is in a dylib. | |
| 50 | log.debug("root(ATOM({d}, %{d}, {?d}))", .{ | 64 | } |
| 51 | atom_index, | ||
| 52 | zld.getAtom(atom_index).sym_index, | ||
| 53 | zld.getAtom(atom_index).getFile(), | ||
| 54 | }); | ||
| 55 | }, | 65 | }, |
| 56 | else => |other| { | 66 | else => |other| { |
| 57 | assert(other == .Lib); | 67 | assert(other == .Lib); |
| ... | @@ -60,20 +70,22 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void { | ... | @@ -60,20 +70,22 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void { |
| 60 | const sym = zld.getSymbol(global); | 70 | const sym = zld.getSymbol(global); |
| 61 | if (sym.undf()) continue; | 71 | if (sym.undf()) continue; |
| 62 | 72 | ||
| 63 | const file = global.getFile() orelse continue; // synthetic globals are atomless | 73 | if (global.getFile()) |file| { |
| 64 | const object = zld.objects.items[file]; | 74 | try addRoot(zld, roots, file, global); |
| 65 | const atom_index = object.getAtomIndexForSymbol(global.sym_index).?; // panic here means fatal error | 75 | } |
| 66 | _ = try roots.getOrPut(atom_index); | ||
| 67 | |||
| 68 | log.debug("root(ATOM({d}, %{d}, {?d}))", .{ | ||
| 69 | atom_index, | ||
| 70 | zld.getAtom(atom_index).sym_index, | ||
| 71 | zld.getAtom(atom_index).getFile(), | ||
| 72 | }); | ||
| 73 | } | 76 | } |
| 74 | }, | 77 | }, |
| 75 | } | 78 | } |
| 76 | 79 | ||
| 80 | // Add all symbols force-defined by the user. | ||
| 81 | for (zld.options.force_undefined_symbols.keys()) |sym_name| { | ||
| 82 | const global_index = resolver.table.get(sym_name).?; | ||
| 83 | const global = zld.globals.items[global_index]; | ||
| 84 | const sym = zld.getSymbol(global); | ||
| 85 | assert(!sym.undf()); | ||
| 86 | try addRoot(zld, roots, global.getFile().?, global); | ||
| 87 | } | ||
| 88 | |||
| 77 | for (zld.objects.items) |object| { | 89 | for (zld.objects.items) |object| { |
| 78 | const has_subsections = object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0; | 90 | const has_subsections = object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0; |
| 79 | 91 |
src/link/MachO/load_commands.zig+4| ... | @@ -8,6 +8,10 @@ const mem = std.mem; | ... | @@ -8,6 +8,10 @@ const mem = std.mem; |
| 8 | const Allocator = mem.Allocator; | 8 | const Allocator = mem.Allocator; |
| 9 | const Dylib = @import("Dylib.zig"); | 9 | const Dylib = @import("Dylib.zig"); |
| 10 | 10 | ||
| 11 | /// Default implicit entrypoint symbol name. | ||
| 12 | pub const default_entry_point: []const u8 = "_main"; | ||
| 13 | |||
| 14 | /// Default path to dyld. | ||
| 11 | pub const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld"; | 15 | pub const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld"; |
| 12 | 16 | ||
| 13 | fn calcInstallNameLen(cmd_size: u64, name: []const u8, assume_max_path_len: bool) u64 { | 17 | fn calcInstallNameLen(cmd_size: u64, name: []const u8, assume_max_path_len: bool) u64 { |
src/link/MachO/zld.zig+77-28| ... | @@ -932,6 +932,43 @@ pub const Zld = struct { | ... | @@ -932,6 +932,43 @@ pub const Zld = struct { |
| 932 | } | 932 | } |
| 933 | } | 933 | } |
| 934 | 934 | ||
| 935 | fn forceSymbolDefined(self: *Zld, name: []const u8, resolver: *SymbolResolver) !void { | ||
| 936 | const sym_index = try self.allocateSymbol(); | ||
| 937 | const sym_loc = SymbolWithLoc{ .sym_index = sym_index }; | ||
| 938 | const sym = self.getSymbolPtr(sym_loc); | ||
| 939 | sym.n_strx = try self.strtab.insert(self.gpa, name); | ||
| 940 | sym.n_type = macho.N_UNDF | macho.N_EXT; | ||
| 941 | const global_index = try self.addGlobal(sym_loc); | ||
| 942 | try resolver.table.putNoClobber(name, global_index); | ||
| 943 | try resolver.unresolved.putNoClobber(global_index, {}); | ||
| 944 | } | ||
| 945 | |||
| 946 | fn resolveSymbols(self: *Zld, resolver: *SymbolResolver) !void { | ||
| 947 | // We add the specified entrypoint as the first unresolved symbols so that | ||
| 948 | // we search for it in libraries should there be no object files specified | ||
| 949 | // on the linker line. | ||
| 950 | if (self.options.output_mode == .Exe) { | ||
| 951 | const entry_name = self.options.entry orelse load_commands.default_entry_point; | ||
| 952 | try self.forceSymbolDefined(entry_name, resolver); | ||
| 953 | } | ||
| 954 | |||
| 955 | // Force resolution of any symbols requested by the user. | ||
| 956 | for (self.options.force_undefined_symbols.keys()) |sym_name| { | ||
| 957 | try self.forceSymbolDefined(sym_name, resolver); | ||
| 958 | } | ||
| 959 | |||
| 960 | for (self.objects.items, 0..) |_, object_id| { | ||
| 961 | try self.resolveSymbolsInObject(@intCast(u32, object_id), resolver); | ||
| 962 | } | ||
| 963 | |||
| 964 | try self.resolveSymbolsInArchives(resolver); | ||
| 965 | try self.resolveDyldStubBinder(resolver); | ||
| 966 | try self.resolveSymbolsInDylibs(resolver); | ||
| 967 | try self.createMhExecuteHeaderSymbol(resolver); | ||
| 968 | try self.createDsoHandleSymbol(resolver); | ||
| 969 | try self.resolveSymbolsAtLoading(resolver); | ||
| 970 | } | ||
| 971 | |||
| 935 | fn resolveSymbolsInObject(self: *Zld, object_id: u32, resolver: *SymbolResolver) !void { | 972 | fn resolveSymbolsInObject(self: *Zld, object_id: u32, resolver: *SymbolResolver) !void { |
| 936 | const object = &self.objects.items[object_id]; | 973 | const object = &self.objects.items[object_id]; |
| 937 | const in_symtab = object.in_symtab orelse return; | 974 | const in_symtab = object.in_symtab orelse return; |
| ... | @@ -975,9 +1012,7 @@ pub const Zld = struct { | ... | @@ -975,9 +1012,7 @@ pub const Zld = struct { |
| 975 | const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = object_id + 1 }; | 1012 | const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = object_id + 1 }; |
| 976 | 1013 | ||
| 977 | const global_index = resolver.table.get(sym_name) orelse { | 1014 | const global_index = resolver.table.get(sym_name) orelse { |
| 978 | const gpa = self.gpa; | 1015 | const global_index = try self.addGlobal(sym_loc); |
| 979 | const global_index = @intCast(u32, self.globals.items.len); | ||
| 980 | try self.globals.append(gpa, sym_loc); | ||
| 981 | try resolver.table.putNoClobber(sym_name, global_index); | 1016 | try resolver.table.putNoClobber(sym_name, global_index); |
| 982 | if (sym.undf() and !sym.tentative()) { | 1017 | if (sym.undf() and !sym.tentative()) { |
| 983 | try resolver.unresolved.putNoClobber(global_index, {}); | 1018 | try resolver.unresolved.putNoClobber(global_index, {}); |
| ... | @@ -1034,8 +1069,10 @@ pub const Zld = struct { | ... | @@ -1034,8 +1069,10 @@ pub const Zld = struct { |
| 1034 | }; | 1069 | }; |
| 1035 | 1070 | ||
| 1036 | if (update_global) { | 1071 | if (update_global) { |
| 1037 | const global_object = &self.objects.items[global.getFile().?]; | 1072 | if (global.getFile()) |file| { |
| 1038 | global_object.globals_lookup[global.sym_index] = global_index; | 1073 | const global_object = &self.objects.items[file]; |
| 1074 | global_object.globals_lookup[global.sym_index] = global_index; | ||
| 1075 | } | ||
| 1039 | _ = resolver.unresolved.swapRemove(resolver.table.get(sym_name).?); | 1076 | _ = resolver.unresolved.swapRemove(resolver.table.get(sym_name).?); |
| 1040 | global.* = sym_loc; | 1077 | global.* = sym_loc; |
| 1041 | } else { | 1078 | } else { |
| ... | @@ -1180,9 +1217,7 @@ pub const Zld = struct { | ... | @@ -1180,9 +1217,7 @@ pub const Zld = struct { |
| 1180 | global.* = sym_loc; | 1217 | global.* = sym_loc; |
| 1181 | self.mh_execute_header_index = global_index; | 1218 | self.mh_execute_header_index = global_index; |
| 1182 | } else { | 1219 | } else { |
| 1183 | const global_index = @intCast(u32, self.globals.items.len); | 1220 | self.mh_execute_header_index = try self.addGlobal(sym_loc); |
| 1184 | try self.globals.append(gpa, sym_loc); | ||
| 1185 | self.mh_execute_header_index = global_index; | ||
| 1186 | } | 1221 | } |
| 1187 | } | 1222 | } |
| 1188 | 1223 | ||
| ... | @@ -1358,6 +1393,12 @@ pub const Zld = struct { | ... | @@ -1358,6 +1393,12 @@ pub const Zld = struct { |
| 1358 | return index; | 1393 | return index; |
| 1359 | } | 1394 | } |
| 1360 | 1395 | ||
| 1396 | fn addGlobal(self: *Zld, sym_loc: SymbolWithLoc) !u32 { | ||
| 1397 | const global_index = @intCast(u32, self.globals.items.len); | ||
| 1398 | try self.globals.append(self.gpa, sym_loc); | ||
| 1399 | return global_index; | ||
| 1400 | } | ||
| 1401 | |||
| 1361 | fn allocateSpecialSymbols(self: *Zld) !void { | 1402 | fn allocateSpecialSymbols(self: *Zld) !void { |
| 1362 | for (&[_]?u32{ | 1403 | for (&[_]?u32{ |
| 1363 | self.dso_handle_index, | 1404 | self.dso_handle_index, |
| ... | @@ -3507,7 +3548,7 @@ pub const SymbolWithLoc = extern struct { | ... | @@ -3507,7 +3548,7 @@ pub const SymbolWithLoc = extern struct { |
| 3507 | } | 3548 | } |
| 3508 | }; | 3549 | }; |
| 3509 | 3550 | ||
| 3510 | const SymbolResolver = struct { | 3551 | pub const SymbolResolver = struct { |
| 3511 | arena: Allocator, | 3552 | arena: Allocator, |
| 3512 | table: std.StringHashMap(u32), | 3553 | table: std.StringHashMap(u32), |
| 3513 | unresolved: std.AutoArrayHashMap(u32, void), | 3554 | unresolved: std.AutoArrayHashMap(u32, void), |
| ... | @@ -3568,7 +3609,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr | ... | @@ -3568,7 +3609,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr |
| 3568 | // We are about to obtain this lock, so here we give other processes a chance first. | 3609 | // We are about to obtain this lock, so here we give other processes a chance first. |
| 3569 | macho_file.base.releaseLock(); | 3610 | macho_file.base.releaseLock(); |
| 3570 | 3611 | ||
| 3571 | comptime assert(Compilation.link_hash_implementation_version == 7); | 3612 | comptime assert(Compilation.link_hash_implementation_version == 8); |
| 3572 | 3613 | ||
| 3573 | for (options.objects) |obj| { | 3614 | for (options.objects) |obj| { |
| 3574 | _ = try man.addFile(obj.path, null); | 3615 | _ = try man.addFile(obj.path, null); |
| ... | @@ -3598,6 +3639,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr | ... | @@ -3598,6 +3639,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr |
| 3598 | } | 3639 | } |
| 3599 | link.hashAddSystemLibs(&man.hash, options.system_libs); | 3640 | link.hashAddSystemLibs(&man.hash, options.system_libs); |
| 3600 | man.hash.addOptionalBytes(options.sysroot); | 3641 | man.hash.addOptionalBytes(options.sysroot); |
| 3642 | man.hash.addListOfBytes(options.force_undefined_symbols.keys()); | ||
| 3601 | try man.addOptionalFile(options.entitlements); | 3643 | try man.addOptionalFile(options.entitlements); |
| 3602 | 3644 | ||
| 3603 | // We don't actually care whether it's a cache hit or miss; we just | 3645 | // We don't actually care whether it's a cache hit or miss; we just |
| ... | @@ -3980,17 +4022,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr | ... | @@ -3980,17 +4022,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr |
| 3980 | .table = std.StringHashMap(u32).init(arena), | 4022 | .table = std.StringHashMap(u32).init(arena), |
| 3981 | .unresolved = std.AutoArrayHashMap(u32, void).init(arena), | 4023 | .unresolved = std.AutoArrayHashMap(u32, void).init(arena), |
| 3982 | }; | 4024 | }; |
| 3983 | 4025 | try zld.resolveSymbols(&resolver); | |
| 3984 | for (zld.objects.items, 0..) |_, object_id| { | ||
| 3985 | try zld.resolveSymbolsInObject(@intCast(u32, object_id), &resolver); | ||
| 3986 | } | ||
| 3987 | |||
| 3988 | try zld.resolveSymbolsInArchives(&resolver); | ||
| 3989 | try zld.resolveDyldStubBinder(&resolver); | ||
| 3990 | try zld.resolveSymbolsInDylibs(&resolver); | ||
| 3991 | try zld.createMhExecuteHeaderSymbol(&resolver); | ||
| 3992 | try zld.createDsoHandleSymbol(&resolver); | ||
| 3993 | try zld.resolveSymbolsAtLoading(&resolver); | ||
| 3994 | 4026 | ||
| 3995 | if (resolver.unresolved.count() > 0) { | 4027 | if (resolver.unresolved.count() > 0) { |
| 3996 | return error.UndefinedSymbolReference; | 4028 | return error.UndefinedSymbolReference; |
| ... | @@ -4003,11 +4035,8 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr | ... | @@ -4003,11 +4035,8 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr |
| 4003 | } | 4035 | } |
| 4004 | 4036 | ||
| 4005 | if (options.output_mode == .Exe) { | 4037 | if (options.output_mode == .Exe) { |
| 4006 | const entry_name = options.entry orelse "_main"; | 4038 | const entry_name = options.entry orelse load_commands.default_entry_point; |
| 4007 | const global_index = resolver.table.get(entry_name) orelse { | 4039 | const global_index = resolver.table.get(entry_name).?; // Error was flagged earlier |
| 4008 | log.err("entrypoint '{s}' not found", .{entry_name}); | ||
| 4009 | return error.MissingMainEntrypoint; | ||
| 4010 | }; | ||
| 4011 | zld.entry_index = global_index; | 4040 | zld.entry_index = global_index; |
| 4012 | } | 4041 | } |
| 4013 | 4042 | ||
| ... | @@ -4016,13 +4045,23 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr | ... | @@ -4016,13 +4045,23 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr |
| 4016 | } | 4045 | } |
| 4017 | 4046 | ||
| 4018 | if (gc_sections) { | 4047 | if (gc_sections) { |
| 4019 | try dead_strip.gcAtoms(&zld); | 4048 | try dead_strip.gcAtoms(&zld, &resolver); |
| 4020 | } | 4049 | } |
| 4021 | 4050 | ||
| 4022 | try zld.createDyldPrivateAtom(); | 4051 | try zld.createDyldPrivateAtom(); |
| 4023 | try zld.createTentativeDefAtoms(); | 4052 | try zld.createTentativeDefAtoms(); |
| 4024 | try zld.createStubHelperPreambleAtom(); | 4053 | try zld.createStubHelperPreambleAtom(); |
| 4025 | 4054 | ||
| 4055 | if (zld.options.output_mode == .Exe) { | ||
| 4056 | const global = zld.getEntryPoint(); | ||
| 4057 | if (zld.getSymbol(global).undf()) { | ||
| 4058 | // We do one additional check here in case the entry point was found in one of the dylibs. | ||
| 4059 | // (I actually have no idea what this would imply but it is a possible outcome and so we | ||
| 4060 | // support it.) | ||
| 4061 | try Atom.addStub(&zld, global); | ||
| 4062 | } | ||
| 4063 | } | ||
| 4064 | |||
| 4026 | for (zld.objects.items) |object| { | 4065 | for (zld.objects.items) |object| { |
| 4027 | for (object.atoms.items) |atom_index| { | 4066 | for (object.atoms.items) |atom_index| { |
| 4028 | const atom = zld.getAtom(atom_index); | 4067 | const atom = zld.getAtom(atom_index); |
| ... | @@ -4134,8 +4173,18 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr | ... | @@ -4134,8 +4173,18 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr |
| 4134 | const seg = zld.segments.items[seg_id]; | 4173 | const seg = zld.segments.items[seg_id]; |
| 4135 | const global = zld.getEntryPoint(); | 4174 | const global = zld.getEntryPoint(); |
| 4136 | const sym = zld.getSymbol(global); | 4175 | const sym = zld.getSymbol(global); |
| 4176 | |||
| 4177 | const addr: u64 = if (sym.undf()) blk: { | ||
| 4178 | // In this case, the symbol has been resolved in one of dylibs and so we point | ||
| 4179 | // to the stub as its vmaddr value. | ||
| 4180 | const stub_atom_index = zld.getStubsAtomIndexForSymbol(global).?; | ||
| 4181 | const stub_atom = zld.getAtom(stub_atom_index); | ||
| 4182 | const stub_sym = zld.getSymbol(stub_atom.getSymbolWithLoc()); | ||
| 4183 | break :blk stub_sym.n_value; | ||
| 4184 | } else sym.n_value; | ||
| 4185 | |||
| 4137 | try lc_writer.writeStruct(macho.entry_point_command{ | 4186 | try lc_writer.writeStruct(macho.entry_point_command{ |
| 4138 | .entryoff = @intCast(u32, sym.n_value - seg.vmaddr), | 4187 | .entryoff = @intCast(u32, addr - seg.vmaddr), |
| 4139 | .stacksize = options.stack_size_override orelse 0, | 4188 | .stacksize = options.stack_size_override orelse 0, |
| 4140 | }); | 4189 | }); |
| 4141 | } else { | 4190 | } else { |
src/link/Wasm.zig+2-2| ... | @@ -3059,7 +3059,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l | ... | @@ -3059,7 +3059,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l |
| 3059 | // We are about to obtain this lock, so here we give other processes a chance first. | 3059 | // We are about to obtain this lock, so here we give other processes a chance first. |
| 3060 | wasm.base.releaseLock(); | 3060 | wasm.base.releaseLock(); |
| 3061 | 3061 | ||
| 3062 | comptime assert(Compilation.link_hash_implementation_version == 7); | 3062 | comptime assert(Compilation.link_hash_implementation_version == 8); |
| 3063 | 3063 | ||
| 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(); |
| 4088 | 4088 | ||
| 4089 | comptime assert(Compilation.link_hash_implementation_version == 7); | 4089 | comptime assert(Compilation.link_hash_implementation_version == 8); |
| 4090 | 4090 | ||
| 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 semver | 479 | \\ --version [ver] Dynamic library semver |
| 480 | \\ --entry [name] Set the entrypoint symbol name | 480 | \\ --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 value | 482 | \\ -fsoname[=name] Override the default SONAME value |
| 482 | \\ -fno-soname Disable emitting a SONAME | 483 | \\ -fno-soname Disable emitting a SONAME |
| 483 | \\ -fLLD Force using LLD as the linker | 484 | \\ -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 | }; |
| 682 | 683 | ||
| 684 | const 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 | |||
| 683 | fn buildOutputType( | 706 | fn 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( |
| 917 | 941 | ||
| 918 | soname = .yes_default_value; | 942 | soname = .yes_default_value; |
| 919 | 943 | ||
| 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 | }; |
| 944 | 947 | ||
| ... | @@ -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") or | 1858 | 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.2 | 1864 | // 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") or | 1892 | } else if (mem.eql(u8, arg, "-I") or |
| 1896 | mem.eql(u8, arg, "--dynamic-linker") or | 1893 | 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") or | 1897 | } else if (mem.eql(u8, arg, "-E") or |
| 1905 | mem.eql(u8, arg, "--export-dynamic") or | 1898 | 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") or | 2082 | } 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") or | 2128 | } 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") or | 2149 | } else if (mem.eql(u8, arg, "-hash-style") or |
| 2258 | mem.eql(u8, arg, "--hash-style")) | 2150 | mem.eql(u8, arg, "--hash-style")) |
| 2259 | { | 2151 | { |
| 2260 | i += 1; | 2152 | const next_arg = linker_args_it.nextOrFatal(); |
| 2261 | if (i >= linker_args.items.len) { | ||
| 2262 | fatal("expected linker arg after '{s}'", .{arg}); | ||
| 2263 | } | ||
| 2264 | const next_arg = linker_args.items[i]; | ||
| 2265 | hash_style = std.meta.stringToEnum(link.HashStyle, next_arg) orelse { | 2153 | hash_style = std.meta.stringToEnum(link.HashStyle, next_arg) orelse { |
| 2266 | fatal("expected [sysv|gnu|both] after --hash-style, found '{s}'", .{ | 2154 | fatal("expected [sysv|gnu|both] after --hash-style, found '{s}'", .{ |
| 2267 | next_arg, | 2155 | next_arg, |
| ... | @@ -3219,6 +3107,7 @@ fn buildOutputType( | ... | @@ -3219,6 +3107,7 @@ fn buildOutputType( |
| 3219 | .link_eh_frame_hdr = link_eh_frame_hdr, | 3107 | .link_eh_frame_hdr = link_eh_frame_hdr, |
| 3220 | .link_emit_relocs = link_emit_relocs, | 3108 | .link_emit_relocs = link_emit_relocs, |
| 3221 | .entry = entry, | 3109 | .entry = entry, |
| 3110 | .force_undefined_symbols = force_undefined_symbols, | ||
| 3222 | .stack_size_override = stack_size_override, | 3111 | .stack_size_override = stack_size_override, |
| 3223 | .image_base_override = image_base_override, | 3112 | .image_base_override = image_base_override, |
| 3224 | .strip = strip, | 3113 | .strip = strip, |
| ... | @@ -5295,6 +5184,7 @@ pub const ClangArgIterator = struct { | ... | @@ -5295,6 +5184,7 @@ pub const ClangArgIterator = struct { |
| 5295 | emit_llvm, | 5184 | emit_llvm, |
| 5296 | sysroot, | 5185 | sysroot, |
| 5297 | entry, | 5186 | entry, |
| 5187 | force_undefined_symbol, | ||
| 5298 | weak_library, | 5188 | weak_library, |
| 5299 | weak_framework, | 5189 | weak_framework, |
| 5300 | headerpad_max_install_names, | 5190 | headerpad_max_install_names, |
test/link.zig+8| ... | @@ -104,6 +104,14 @@ pub const cases = [_]Case{ | ... | @@ -104,6 +104,14 @@ pub const cases = [_]Case{ |
| 104 | .build_root = "test/link/macho/entry", | 104 | .build_root = "test/link/macho/entry", |
| 105 | .import = @import("link/macho/entry/build.zig"), | 105 | .import = @import("link/macho/entry/build.zig"), |
| 106 | }, | 106 | }, |
| 107 | .{ | ||
| 108 | .build_root = "test/link/macho/entry_in_archive", | ||
| 109 | .import = @import("link/macho/entry_in_archive/build.zig"), | ||
| 110 | }, | ||
| 111 | .{ | ||
| 112 | .build_root = "test/link/macho/entry_in_dylib", | ||
| 113 | .import = @import("link/macho/entry_in_dylib/build.zig"), | ||
| 114 | }, | ||
| 107 | .{ | 115 | .{ |
| 108 | .build_root = "test/link/macho/headerpad", | 116 | .build_root = "test/link/macho/headerpad", |
| 109 | .import = @import("link/macho/headerpad/build.zig"), | 117 | .import = @import("link/macho/headerpad/build.zig"), |
test/link/macho/entry_in_archive/build.zig created+36| ... | @@ -0,0 +1,36 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub const requires_symlinks = true; | ||
| 4 | |||
| 5 | pub 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 | |||
| 15 | fn 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 | |||
| 3 | int main(int argc, char* argv[]) { | ||
| 4 | return 0; | ||
| 5 | } | ||
test/link/macho/entry_in_dylib/bootstrap.c created+5| ... | @@ -0,0 +1,5 @@ | ||
| 1 | extern int my_main(); | ||
| 2 | |||
| 3 | int bootstrap() { | ||
| 4 | return my_main(); | ||
| 5 | } | ||
test/link/macho/entry_in_dylib/build.zig created+54| ... | @@ -0,0 +1,54 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub const requires_symlinks = true; | ||
| 4 | |||
| 5 | pub 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 | |||
| 15 | fn 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 | |||
| 3 | int my_main() { | ||
| 4 | fprintf(stdout, "Hello!\n"); | ||
| 5 | return 0; | ||
| 6 | } | ||
tools/update_clang_options.zig+4| ... | @@ -468,6 +468,10 @@ const known_options = [_]KnownOpt{ | ... | @@ -468,6 +468,10 @@ const known_options = [_]KnownOpt{ |
| 468 | .name = "e", | 468 | .name = "e", |
| 469 | .ident = "entry", | 469 | .ident = "entry", |
| 470 | }, | 470 | }, |
| 471 | .{ | ||
| 472 | .name = "u", | ||
| 473 | .ident = "force_undefined_symbol", | ||
| 474 | }, | ||
| 471 | .{ | 475 | .{ |
| 472 | .name = "weak-l", | 476 | .name = "weak-l", |
| 473 | .ident = "weak_library", | 477 | .ident = "weak_library", |