mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 12:59:04 +00:00
Merge pull request #15135 from ziglang/fix-15103
macho: handle cases where entrypoint is in an archive or dylib
This commit is contained in:
commit
ab44b454d0
@ -202,6 +202,11 @@ subsystem: ?std.Target.SubSystem = null,
|
||||
|
||||
entry_symbol_name: ?[]const u8 = null,
|
||||
|
||||
/// List of symbols forced as undefined in the symbol table
|
||||
/// thus forcing their resolution by the linker.
|
||||
/// Corresponds to `-u <symbol>` for ELF/MachO and `/include:<symbol>` for COFF/PE.
|
||||
force_undefined_symbols: std.StringHashMap(void),
|
||||
|
||||
/// Overrides the default stack size
|
||||
stack_size: ?u64 = null,
|
||||
|
||||
@ -386,6 +391,7 @@ pub fn create(owner: *std.Build, options: Options) *CompileStep {
|
||||
.override_dest_dir = null,
|
||||
.installed_path = null,
|
||||
.install_step = null,
|
||||
.force_undefined_symbols = StringHashMap(void).init(owner.allocator),
|
||||
|
||||
.output_path_source = GeneratedFile{ .step = &self.step },
|
||||
.output_lib_path_source = GeneratedFile{ .step = &self.step },
|
||||
@ -568,6 +574,11 @@ pub fn setLinkerScriptPath(self: *CompileStep, source: FileSource) void {
|
||||
source.addStepDependencies(&self.step);
|
||||
}
|
||||
|
||||
pub fn forceUndefinedSymbol(self: *CompileStep, symbol_name: []const u8) void {
|
||||
const b = self.step.owner;
|
||||
self.force_undefined_symbols.put(b.dupe(symbol_name), {}) catch @panic("OOM");
|
||||
}
|
||||
|
||||
pub fn linkFramework(self: *CompileStep, framework_name: []const u8) void {
|
||||
const b = self.step.owner;
|
||||
self.frameworks.put(b.dupe(framework_name), .{}) catch @panic("OOM");
|
||||
@ -1266,6 +1277,14 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
|
||||
try zig_args.append(entry);
|
||||
}
|
||||
|
||||
{
|
||||
var it = self.force_undefined_symbols.keyIterator();
|
||||
while (it.next()) |symbol_name| {
|
||||
try zig_args.append("--force_undefined");
|
||||
try zig_args.append(symbol_name.*);
|
||||
}
|
||||
}
|
||||
|
||||
if (self.stack_size) |stack_size| {
|
||||
try zig_args.append("--stack");
|
||||
try zig_args.append(try std.fmt.allocPrint(b.allocator, "{}", .{stack_size}));
|
||||
|
||||
@ -602,6 +602,7 @@ pub const InitOptions = struct {
|
||||
parent_compilation_link_libc: bool = false,
|
||||
hash_style: link.HashStyle = .both,
|
||||
entry: ?[]const u8 = null,
|
||||
force_undefined_symbols: std.StringArrayHashMapUnmanaged(void) = .{},
|
||||
stack_size_override: ?u64 = null,
|
||||
image_base_override: ?u64 = null,
|
||||
self_exe_path: ?[]const u8 = null,
|
||||
@ -1523,7 +1524,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
|
||||
.headerpad_size = options.headerpad_size,
|
||||
.headerpad_max_install_names = options.headerpad_max_install_names,
|
||||
.dead_strip_dylibs = options.dead_strip_dylibs,
|
||||
.force_undefined_symbols = .{},
|
||||
.force_undefined_symbols = options.force_undefined_symbols,
|
||||
.pdb_source_path = options.pdb_source_path,
|
||||
.pdb_out_path = options.pdb_out_path,
|
||||
});
|
||||
@ -2186,7 +2187,7 @@ fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemo
|
||||
/// to remind the programmer to update multiple related pieces of code that
|
||||
/// are in different locations. Bump this number when adding or deleting
|
||||
/// anything from the link cache manifest.
|
||||
pub const link_hash_implementation_version = 7;
|
||||
pub const link_hash_implementation_version = 8;
|
||||
|
||||
fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void {
|
||||
const gpa = comp.gpa;
|
||||
@ -2196,7 +2197,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
|
||||
defer arena_allocator.deinit();
|
||||
const arena = arena_allocator.allocator();
|
||||
|
||||
comptime assert(link_hash_implementation_version == 7);
|
||||
comptime assert(link_hash_implementation_version == 8);
|
||||
|
||||
if (comp.bin_file.options.module) |mod| {
|
||||
const main_zig_file = try mod.main_pkg.root_src_directory.join(arena, &[_][]const u8{
|
||||
|
||||
@ -1448,7 +1448,7 @@ flagpsl("MT"),
|
||||
.{
|
||||
.name = "u",
|
||||
.syntax = .flag,
|
||||
.zig_equivalent = .other,
|
||||
.zig_equivalent = .force_undefined_symbol,
|
||||
.pd1 = true,
|
||||
.pd2 = false,
|
||||
.psl = true,
|
||||
@ -7170,7 +7170,14 @@ joinpd1("d"),
|
||||
.pd2 = false,
|
||||
.psl = true,
|
||||
},
|
||||
jspd1("u"),
|
||||
.{
|
||||
.name = "u",
|
||||
.syntax = .joined_or_separate,
|
||||
.zig_equivalent = .force_undefined_symbol,
|
||||
.pd1 = true,
|
||||
.pd2 = false,
|
||||
.psl = false,
|
||||
},
|
||||
.{
|
||||
.name = "x",
|
||||
.syntax = .joined_or_separate,
|
||||
|
||||
@ -186,8 +186,7 @@ pub const Options = struct {
|
||||
|
||||
/// List of symbols forced as undefined in the symbol table
|
||||
/// thus forcing their resolution by the linker.
|
||||
/// Corresponds to `-u <symbol>` for ELF and `/include:<symbol>` for COFF/PE.
|
||||
/// TODO add handling for MachO.
|
||||
/// Corresponds to `-u <symbol>` for ELF/MachO and `/include:<symbol>` for COFF/PE.
|
||||
force_undefined_symbols: std.StringArrayHashMapUnmanaged(void),
|
||||
|
||||
version: ?std.builtin.Version,
|
||||
|
||||
@ -63,7 +63,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
|
||||
man = comp.cache_parent.obtain();
|
||||
self.base.releaseLock();
|
||||
|
||||
comptime assert(Compilation.link_hash_implementation_version == 7);
|
||||
comptime assert(Compilation.link_hash_implementation_version == 8);
|
||||
|
||||
for (self.base.options.objects) |obj| {
|
||||
_ = try man.addFile(obj.path, null);
|
||||
|
||||
@ -1305,7 +1305,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
|
||||
// We are about to obtain this lock, so here we give other processes a chance first.
|
||||
self.base.releaseLock();
|
||||
|
||||
comptime assert(Compilation.link_hash_implementation_version == 7);
|
||||
comptime assert(Compilation.link_hash_implementation_version == 8);
|
||||
|
||||
try man.addOptionalFile(self.base.options.linker_script);
|
||||
try man.addOptionalFile(self.base.options.version_script);
|
||||
|
||||
@ -3981,7 +3981,7 @@ pub fn getStubsAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?At
|
||||
/// Returns symbol location corresponding to the set entrypoint.
|
||||
/// Asserts output mode is executable.
|
||||
pub fn getEntryPoint(self: MachO) error{MissingMainEntrypoint}!SymbolWithLoc {
|
||||
const entry_name = self.base.options.entry orelse "_main";
|
||||
const entry_name = self.base.options.entry orelse load_commands.default_entry_point;
|
||||
const global = self.getGlobal(entry_name) orelse {
|
||||
log.err("entrypoint '{s}' not found", .{entry_name});
|
||||
return error.MissingMainEntrypoint;
|
||||
|
||||
@ -389,7 +389,7 @@ pub fn addGotEntry(zld: *Zld, target: SymbolWithLoc) !void {
|
||||
try zld.got_table.putNoClobber(gpa, target, got_index);
|
||||
}
|
||||
|
||||
fn addStub(zld: *Zld, target: SymbolWithLoc) !void {
|
||||
pub fn addStub(zld: *Zld, target: SymbolWithLoc) !void {
|
||||
const target_sym = zld.getSymbol(target);
|
||||
if (!target_sym.undf()) return;
|
||||
if (zld.stubs_table.contains(target)) return;
|
||||
|
||||
@ -12,6 +12,7 @@ const Allocator = mem.Allocator;
|
||||
const AtomIndex = @import("zld.zig").AtomIndex;
|
||||
const Atom = @import("ZldAtom.zig");
|
||||
const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
|
||||
const SymbolResolver = @import("zld.zig").SymbolResolver;
|
||||
const UnwindInfo = @import("UnwindInfo.zig");
|
||||
const Zld = @import("zld.zig").Zld;
|
||||
|
||||
@ -19,7 +20,7 @@ const N_DEAD = @import("zld.zig").N_DEAD;
|
||||
|
||||
const AtomTable = std.AutoHashMap(AtomIndex, void);
|
||||
|
||||
pub fn gcAtoms(zld: *Zld) !void {
|
||||
pub fn gcAtoms(zld: *Zld, resolver: *const SymbolResolver) !void {
|
||||
const gpa = zld.gpa;
|
||||
|
||||
var arena = std.heap.ArenaAllocator.init(gpa);
|
||||
@ -31,27 +32,36 @@ pub fn gcAtoms(zld: *Zld) !void {
|
||||
var alive = AtomTable.init(arena.allocator());
|
||||
try alive.ensureTotalCapacity(@intCast(u32, zld.atoms.items.len));
|
||||
|
||||
try collectRoots(zld, &roots);
|
||||
try collectRoots(zld, &roots, resolver);
|
||||
try mark(zld, roots, &alive);
|
||||
prune(zld, alive);
|
||||
}
|
||||
|
||||
fn collectRoots(zld: *Zld, roots: *AtomTable) !void {
|
||||
fn addRoot(zld: *Zld, roots: *AtomTable, file: u32, sym_loc: SymbolWithLoc) !void {
|
||||
const sym = zld.getSymbol(sym_loc);
|
||||
assert(!sym.undf());
|
||||
const object = &zld.objects.items[file];
|
||||
const atom_index = object.getAtomIndexForSymbol(sym_loc.sym_index).?; // panic here means fatal error
|
||||
log.debug("root(ATOM({d}, %{d}, {d}))", .{
|
||||
atom_index,
|
||||
zld.getAtom(atom_index).sym_index,
|
||||
file,
|
||||
});
|
||||
_ = try roots.getOrPut(atom_index);
|
||||
}
|
||||
|
||||
fn collectRoots(zld: *Zld, roots: *AtomTable, resolver: *const SymbolResolver) !void {
|
||||
log.debug("collecting roots", .{});
|
||||
|
||||
switch (zld.options.output_mode) {
|
||||
.Exe => {
|
||||
// Add entrypoint as GC root
|
||||
const global: SymbolWithLoc = zld.getEntryPoint();
|
||||
const object = zld.objects.items[global.getFile().?];
|
||||
const atom_index = object.getAtomIndexForSymbol(global.sym_index).?; // panic here means fatal error
|
||||
_ = try roots.getOrPut(atom_index);
|
||||
|
||||
log.debug("root(ATOM({d}, %{d}, {?d}))", .{
|
||||
atom_index,
|
||||
zld.getAtom(atom_index).sym_index,
|
||||
zld.getAtom(atom_index).getFile(),
|
||||
});
|
||||
if (global.getFile()) |file| {
|
||||
try addRoot(zld, roots, file, global);
|
||||
} else {
|
||||
assert(zld.getSymbol(global).undf()); // Stub as our entrypoint is in a dylib.
|
||||
}
|
||||
},
|
||||
else => |other| {
|
||||
assert(other == .Lib);
|
||||
@ -60,20 +70,22 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {
|
||||
const sym = zld.getSymbol(global);
|
||||
if (sym.undf()) continue;
|
||||
|
||||
const file = global.getFile() orelse continue; // synthetic globals are atomless
|
||||
const object = zld.objects.items[file];
|
||||
const atom_index = object.getAtomIndexForSymbol(global.sym_index).?; // panic here means fatal error
|
||||
_ = try roots.getOrPut(atom_index);
|
||||
|
||||
log.debug("root(ATOM({d}, %{d}, {?d}))", .{
|
||||
atom_index,
|
||||
zld.getAtom(atom_index).sym_index,
|
||||
zld.getAtom(atom_index).getFile(),
|
||||
});
|
||||
if (global.getFile()) |file| {
|
||||
try addRoot(zld, roots, file, global);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// Add all symbols force-defined by the user.
|
||||
for (zld.options.force_undefined_symbols.keys()) |sym_name| {
|
||||
const global_index = resolver.table.get(sym_name).?;
|
||||
const global = zld.globals.items[global_index];
|
||||
const sym = zld.getSymbol(global);
|
||||
assert(!sym.undf());
|
||||
try addRoot(zld, roots, global.getFile().?, global);
|
||||
}
|
||||
|
||||
for (zld.objects.items) |object| {
|
||||
const has_subsections = object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
|
||||
|
||||
|
||||
@ -8,6 +8,10 @@ const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
const Dylib = @import("Dylib.zig");
|
||||
|
||||
/// Default implicit entrypoint symbol name.
|
||||
pub const default_entry_point: []const u8 = "_main";
|
||||
|
||||
/// Default path to dyld.
|
||||
pub const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld";
|
||||
|
||||
fn calcInstallNameLen(cmd_size: u64, name: []const u8, assume_max_path_len: bool) u64 {
|
||||
|
||||
@ -932,6 +932,43 @@ pub const Zld = struct {
|
||||
}
|
||||
}
|
||||
|
||||
fn forceSymbolDefined(self: *Zld, name: []const u8, resolver: *SymbolResolver) !void {
|
||||
const sym_index = try self.allocateSymbol();
|
||||
const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
|
||||
const sym = self.getSymbolPtr(sym_loc);
|
||||
sym.n_strx = try self.strtab.insert(self.gpa, name);
|
||||
sym.n_type = macho.N_UNDF | macho.N_EXT;
|
||||
const global_index = try self.addGlobal(sym_loc);
|
||||
try resolver.table.putNoClobber(name, global_index);
|
||||
try resolver.unresolved.putNoClobber(global_index, {});
|
||||
}
|
||||
|
||||
fn resolveSymbols(self: *Zld, resolver: *SymbolResolver) !void {
|
||||
// We add the specified entrypoint as the first unresolved symbols so that
|
||||
// we search for it in libraries should there be no object files specified
|
||||
// on the linker line.
|
||||
if (self.options.output_mode == .Exe) {
|
||||
const entry_name = self.options.entry orelse load_commands.default_entry_point;
|
||||
try self.forceSymbolDefined(entry_name, resolver);
|
||||
}
|
||||
|
||||
// Force resolution of any symbols requested by the user.
|
||||
for (self.options.force_undefined_symbols.keys()) |sym_name| {
|
||||
try self.forceSymbolDefined(sym_name, resolver);
|
||||
}
|
||||
|
||||
for (self.objects.items, 0..) |_, object_id| {
|
||||
try self.resolveSymbolsInObject(@intCast(u32, object_id), resolver);
|
||||
}
|
||||
|
||||
try self.resolveSymbolsInArchives(resolver);
|
||||
try self.resolveDyldStubBinder(resolver);
|
||||
try self.resolveSymbolsInDylibs(resolver);
|
||||
try self.createMhExecuteHeaderSymbol(resolver);
|
||||
try self.createDsoHandleSymbol(resolver);
|
||||
try self.resolveSymbolsAtLoading(resolver);
|
||||
}
|
||||
|
||||
fn resolveSymbolsInObject(self: *Zld, object_id: u32, resolver: *SymbolResolver) !void {
|
||||
const object = &self.objects.items[object_id];
|
||||
const in_symtab = object.in_symtab orelse return;
|
||||
@ -975,9 +1012,7 @@ pub const Zld = struct {
|
||||
const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = object_id + 1 };
|
||||
|
||||
const global_index = resolver.table.get(sym_name) orelse {
|
||||
const gpa = self.gpa;
|
||||
const global_index = @intCast(u32, self.globals.items.len);
|
||||
try self.globals.append(gpa, sym_loc);
|
||||
const global_index = try self.addGlobal(sym_loc);
|
||||
try resolver.table.putNoClobber(sym_name, global_index);
|
||||
if (sym.undf() and !sym.tentative()) {
|
||||
try resolver.unresolved.putNoClobber(global_index, {});
|
||||
@ -1034,8 +1069,10 @@ pub const Zld = struct {
|
||||
};
|
||||
|
||||
if (update_global) {
|
||||
const global_object = &self.objects.items[global.getFile().?];
|
||||
global_object.globals_lookup[global.sym_index] = global_index;
|
||||
if (global.getFile()) |file| {
|
||||
const global_object = &self.objects.items[file];
|
||||
global_object.globals_lookup[global.sym_index] = global_index;
|
||||
}
|
||||
_ = resolver.unresolved.swapRemove(resolver.table.get(sym_name).?);
|
||||
global.* = sym_loc;
|
||||
} else {
|
||||
@ -1180,9 +1217,7 @@ pub const Zld = struct {
|
||||
global.* = sym_loc;
|
||||
self.mh_execute_header_index = global_index;
|
||||
} else {
|
||||
const global_index = @intCast(u32, self.globals.items.len);
|
||||
try self.globals.append(gpa, sym_loc);
|
||||
self.mh_execute_header_index = global_index;
|
||||
self.mh_execute_header_index = try self.addGlobal(sym_loc);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1358,6 +1393,12 @@ pub const Zld = struct {
|
||||
return index;
|
||||
}
|
||||
|
||||
fn addGlobal(self: *Zld, sym_loc: SymbolWithLoc) !u32 {
|
||||
const global_index = @intCast(u32, self.globals.items.len);
|
||||
try self.globals.append(self.gpa, sym_loc);
|
||||
return global_index;
|
||||
}
|
||||
|
||||
fn allocateSpecialSymbols(self: *Zld) !void {
|
||||
for (&[_]?u32{
|
||||
self.dso_handle_index,
|
||||
@ -3507,7 +3548,7 @@ pub const SymbolWithLoc = extern struct {
|
||||
}
|
||||
};
|
||||
|
||||
const SymbolResolver = struct {
|
||||
pub const SymbolResolver = struct {
|
||||
arena: Allocator,
|
||||
table: std.StringHashMap(u32),
|
||||
unresolved: std.AutoArrayHashMap(u32, void),
|
||||
@ -3568,7 +3609,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
|
||||
// We are about to obtain this lock, so here we give other processes a chance first.
|
||||
macho_file.base.releaseLock();
|
||||
|
||||
comptime assert(Compilation.link_hash_implementation_version == 7);
|
||||
comptime assert(Compilation.link_hash_implementation_version == 8);
|
||||
|
||||
for (options.objects) |obj| {
|
||||
_ = try man.addFile(obj.path, null);
|
||||
@ -3598,6 +3639,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
|
||||
}
|
||||
link.hashAddSystemLibs(&man.hash, options.system_libs);
|
||||
man.hash.addOptionalBytes(options.sysroot);
|
||||
man.hash.addListOfBytes(options.force_undefined_symbols.keys());
|
||||
try man.addOptionalFile(options.entitlements);
|
||||
|
||||
// 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
|
||||
.table = std.StringHashMap(u32).init(arena),
|
||||
.unresolved = std.AutoArrayHashMap(u32, void).init(arena),
|
||||
};
|
||||
|
||||
for (zld.objects.items, 0..) |_, object_id| {
|
||||
try zld.resolveSymbolsInObject(@intCast(u32, object_id), &resolver);
|
||||
}
|
||||
|
||||
try zld.resolveSymbolsInArchives(&resolver);
|
||||
try zld.resolveDyldStubBinder(&resolver);
|
||||
try zld.resolveSymbolsInDylibs(&resolver);
|
||||
try zld.createMhExecuteHeaderSymbol(&resolver);
|
||||
try zld.createDsoHandleSymbol(&resolver);
|
||||
try zld.resolveSymbolsAtLoading(&resolver);
|
||||
try zld.resolveSymbols(&resolver);
|
||||
|
||||
if (resolver.unresolved.count() > 0) {
|
||||
return error.UndefinedSymbolReference;
|
||||
@ -4003,11 +4035,8 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
|
||||
}
|
||||
|
||||
if (options.output_mode == .Exe) {
|
||||
const entry_name = options.entry orelse "_main";
|
||||
const global_index = resolver.table.get(entry_name) orelse {
|
||||
log.err("entrypoint '{s}' not found", .{entry_name});
|
||||
return error.MissingMainEntrypoint;
|
||||
};
|
||||
const entry_name = options.entry orelse load_commands.default_entry_point;
|
||||
const global_index = resolver.table.get(entry_name).?; // Error was flagged earlier
|
||||
zld.entry_index = global_index;
|
||||
}
|
||||
|
||||
@ -4016,13 +4045,23 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
|
||||
}
|
||||
|
||||
if (gc_sections) {
|
||||
try dead_strip.gcAtoms(&zld);
|
||||
try dead_strip.gcAtoms(&zld, &resolver);
|
||||
}
|
||||
|
||||
try zld.createDyldPrivateAtom();
|
||||
try zld.createTentativeDefAtoms();
|
||||
try zld.createStubHelperPreambleAtom();
|
||||
|
||||
if (zld.options.output_mode == .Exe) {
|
||||
const global = zld.getEntryPoint();
|
||||
if (zld.getSymbol(global).undf()) {
|
||||
// We do one additional check here in case the entry point was found in one of the dylibs.
|
||||
// (I actually have no idea what this would imply but it is a possible outcome and so we
|
||||
// support it.)
|
||||
try Atom.addStub(&zld, global);
|
||||
}
|
||||
}
|
||||
|
||||
for (zld.objects.items) |object| {
|
||||
for (object.atoms.items) |atom_index| {
|
||||
const atom = zld.getAtom(atom_index);
|
||||
@ -4134,8 +4173,18 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
|
||||
const seg = zld.segments.items[seg_id];
|
||||
const global = zld.getEntryPoint();
|
||||
const sym = zld.getSymbol(global);
|
||||
|
||||
const addr: u64 = if (sym.undf()) blk: {
|
||||
// In this case, the symbol has been resolved in one of dylibs and so we point
|
||||
// to the stub as its vmaddr value.
|
||||
const stub_atom_index = zld.getStubsAtomIndexForSymbol(global).?;
|
||||
const stub_atom = zld.getAtom(stub_atom_index);
|
||||
const stub_sym = zld.getSymbol(stub_atom.getSymbolWithLoc());
|
||||
break :blk stub_sym.n_value;
|
||||
} else sym.n_value;
|
||||
|
||||
try lc_writer.writeStruct(macho.entry_point_command{
|
||||
.entryoff = @intCast(u32, sym.n_value - seg.vmaddr),
|
||||
.entryoff = @intCast(u32, addr - seg.vmaddr),
|
||||
.stacksize = options.stack_size_override orelse 0,
|
||||
});
|
||||
} else {
|
||||
|
||||
@ -3059,7 +3059,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
|
||||
// We are about to obtain this lock, so here we give other processes a chance first.
|
||||
wasm.base.releaseLock();
|
||||
|
||||
comptime assert(Compilation.link_hash_implementation_version == 7);
|
||||
comptime assert(Compilation.link_hash_implementation_version == 8);
|
||||
|
||||
for (options.objects) |obj| {
|
||||
_ = try man.addFile(obj.path, null);
|
||||
@ -4086,7 +4086,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
|
||||
// We are about to obtain this lock, so here we give other processes a chance first.
|
||||
wasm.base.releaseLock();
|
||||
|
||||
comptime assert(Compilation.link_hash_implementation_version == 7);
|
||||
comptime assert(Compilation.link_hash_implementation_version == 8);
|
||||
|
||||
for (wasm.base.options.objects) |obj| {
|
||||
_ = try man.addFile(obj.path, null);
|
||||
|
||||
294
src/main.zig
294
src/main.zig
@ -478,6 +478,7 @@ const usage_build_generic =
|
||||
\\ --sysroot [path] Set the system root directory (usually /)
|
||||
\\ --version [ver] Dynamic library semver
|
||||
\\ --entry [name] Set the entrypoint symbol name
|
||||
\\ --force_undefined [name] Specify the symbol must be defined for the link to succeed
|
||||
\\ -fsoname[=name] Override the default SONAME value
|
||||
\\ -fno-soname Disable emitting a SONAME
|
||||
\\ -fLLD Force using LLD as the linker
|
||||
@ -680,6 +681,28 @@ const Listen = union(enum) {
|
||||
stdio,
|
||||
};
|
||||
|
||||
const ArgsIterator = struct {
|
||||
resp_file: ?ArgIteratorResponseFile = null,
|
||||
args: []const []const u8,
|
||||
i: usize = 0,
|
||||
fn next(it: *@This()) ?[]const u8 {
|
||||
if (it.i >= it.args.len) {
|
||||
if (it.resp_file) |*resp| return resp.next();
|
||||
return null;
|
||||
}
|
||||
defer it.i += 1;
|
||||
return it.args[it.i];
|
||||
}
|
||||
fn nextOrFatal(it: *@This()) []const u8 {
|
||||
if (it.i >= it.args.len) {
|
||||
if (it.resp_file) |*resp| if (resp.next()) |ret| return ret;
|
||||
fatal("expected parameter after {s}", .{it.args[it.i - 1]});
|
||||
}
|
||||
defer it.i += 1;
|
||||
return it.args[it.i];
|
||||
}
|
||||
};
|
||||
|
||||
fn buildOutputType(
|
||||
gpa: Allocator,
|
||||
arena: Allocator,
|
||||
@ -784,6 +807,7 @@ fn buildOutputType(
|
||||
var test_evented_io = false;
|
||||
var test_no_exec = false;
|
||||
var entry: ?[]const u8 = null;
|
||||
var force_undefined_symbols: std.StringArrayHashMapUnmanaged(void) = .{};
|
||||
var stack_size_override: ?u64 = null;
|
||||
var image_base_override: ?u64 = null;
|
||||
var use_llvm: ?bool = null;
|
||||
@ -917,28 +941,7 @@ fn buildOutputType(
|
||||
|
||||
soname = .yes_default_value;
|
||||
|
||||
const Iterator = struct {
|
||||
resp_file: ?ArgIteratorResponseFile = null,
|
||||
args: []const []const u8,
|
||||
i: usize = 0,
|
||||
fn next(it: *@This()) ?[]const u8 {
|
||||
if (it.i >= it.args.len) {
|
||||
if (it.resp_file) |*resp| return resp.next();
|
||||
return null;
|
||||
}
|
||||
defer it.i += 1;
|
||||
return it.args[it.i];
|
||||
}
|
||||
fn nextOrFatal(it: *@This()) []const u8 {
|
||||
if (it.i >= it.args.len) {
|
||||
if (it.resp_file) |*resp| if (resp.next()) |ret| return ret;
|
||||
fatal("expected parameter after {s}", .{it.args[it.i - 1]});
|
||||
}
|
||||
defer it.i += 1;
|
||||
return it.args[it.i];
|
||||
}
|
||||
};
|
||||
var args_iter = Iterator{
|
||||
var args_iter = ArgsIterator{
|
||||
.args = all_args[2..],
|
||||
};
|
||||
|
||||
@ -1029,6 +1032,8 @@ fn buildOutputType(
|
||||
optimize_mode_string = args_iter.nextOrFatal();
|
||||
} else if (mem.eql(u8, arg, "--entry")) {
|
||||
entry = args_iter.nextOrFatal();
|
||||
} else if (mem.eql(u8, arg, "--force_undefined")) {
|
||||
try force_undefined_symbols.put(gpa, args_iter.nextOrFatal(), {});
|
||||
} else if (mem.eql(u8, arg, "--stack")) {
|
||||
const next_arg = args_iter.nextOrFatal();
|
||||
stack_size_override = std.fmt.parseUnsigned(u64, next_arg, 0) catch |err| {
|
||||
@ -1816,6 +1821,9 @@ fn buildOutputType(
|
||||
.entry => {
|
||||
entry = it.only_arg;
|
||||
},
|
||||
.force_undefined_symbol => {
|
||||
try force_undefined_symbols.put(gpa, it.only_arg, {});
|
||||
},
|
||||
.weak_library => try system_libs.put(it.only_arg, .{ .weak = true }),
|
||||
.weak_framework => try frameworks.put(gpa, it.only_arg, .{ .weak = true }),
|
||||
.headerpad_max_install_names => headerpad_max_install_names = true,
|
||||
@ -1843,17 +1851,14 @@ fn buildOutputType(
|
||||
}
|
||||
}
|
||||
// Parse linker args.
|
||||
var i: usize = 0;
|
||||
while (i < linker_args.items.len) : (i += 1) {
|
||||
const arg = linker_args.items[i];
|
||||
var linker_args_it = ArgsIterator{
|
||||
.args = linker_args.items,
|
||||
};
|
||||
while (linker_args_it.next()) |arg| {
|
||||
if (mem.eql(u8, arg, "-soname") or
|
||||
mem.eql(u8, arg, "--soname"))
|
||||
{
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
const name = linker_args.items[i];
|
||||
const name = linker_args_it.nextOrFatal();
|
||||
soname = .{ .yes = name };
|
||||
// Use it as --name.
|
||||
// Example: libsoundio.so.2
|
||||
@ -1881,64 +1886,37 @@ fn buildOutputType(
|
||||
}
|
||||
provided_name = name[prefix..end];
|
||||
} else if (mem.eql(u8, arg, "-rpath")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
try rpath_list.append(linker_args.items[i]);
|
||||
try rpath_list.append(linker_args_it.nextOrFatal());
|
||||
} else if (mem.eql(u8, arg, "--subsystem")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
subsystem = try parseSubSystem(linker_args.items[i]);
|
||||
subsystem = try parseSubSystem(linker_args_it.nextOrFatal());
|
||||
} else if (mem.eql(u8, arg, "-I") or
|
||||
mem.eql(u8, arg, "--dynamic-linker") or
|
||||
mem.eql(u8, arg, "-dynamic-linker"))
|
||||
{
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
target_dynamic_linker = linker_args.items[i];
|
||||
target_dynamic_linker = linker_args_it.nextOrFatal();
|
||||
} else if (mem.eql(u8, arg, "-E") or
|
||||
mem.eql(u8, arg, "--export-dynamic") or
|
||||
mem.eql(u8, arg, "-export-dynamic"))
|
||||
{
|
||||
rdynamic = true;
|
||||
} else if (mem.eql(u8, arg, "--version-script")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
version_script = linker_args.items[i];
|
||||
version_script = linker_args_it.nextOrFatal();
|
||||
} else if (mem.eql(u8, arg, "-O")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
linker_optimization = std.fmt.parseUnsigned(u8, linker_args.items[i], 10) catch |err| {
|
||||
fatal("unable to parse optimization level '{s}': {s}", .{ linker_args.items[i], @errorName(err) });
|
||||
const opt = linker_args_it.nextOrFatal();
|
||||
linker_optimization = std.fmt.parseUnsigned(u8, opt, 10) catch |err| {
|
||||
fatal("unable to parse optimization level '{s}': {s}", .{ opt, @errorName(err) });
|
||||
};
|
||||
} else if (mem.startsWith(u8, arg, "-O")) {
|
||||
linker_optimization = std.fmt.parseUnsigned(u8, arg["-O".len..], 10) catch |err| {
|
||||
fatal("unable to parse optimization level '{s}': {s}", .{ arg, @errorName(err) });
|
||||
};
|
||||
} else if (mem.eql(u8, arg, "-pagezero_size")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
const next_arg = linker_args.items[i];
|
||||
const next_arg = linker_args_it.nextOrFatal();
|
||||
pagezero_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| {
|
||||
fatal("unable to parse pagezero size '{s}': {s}", .{ next_arg, @errorName(err) });
|
||||
};
|
||||
} else if (mem.eql(u8, arg, "-headerpad")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
const next_arg = linker_args.items[i];
|
||||
const next_arg = linker_args_it.nextOrFatal();
|
||||
headerpad_size = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| {
|
||||
fatal("unable to parse headerpad size '{s}': {s}", .{ next_arg, @errorName(err) });
|
||||
};
|
||||
@ -1961,11 +1939,7 @@ fn buildOutputType(
|
||||
} else if (mem.eql(u8, arg, "--print-map")) {
|
||||
linker_print_map = true;
|
||||
} else if (mem.eql(u8, arg, "--sort-section")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
const arg1 = linker_args.items[i];
|
||||
const arg1 = linker_args_it.nextOrFatal();
|
||||
linker_sort_section = std.meta.stringToEnum(link.SortSection, arg1) orelse {
|
||||
fatal("expected [name|alignment] after --sort-section, found '{s}'", .{arg1});
|
||||
};
|
||||
@ -1998,28 +1972,16 @@ fn buildOutputType(
|
||||
} else if (mem.startsWith(u8, arg, "--export=")) {
|
||||
try linker_export_symbol_names.append(arg["--export=".len..]);
|
||||
} else if (mem.eql(u8, arg, "--export")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
try linker_export_symbol_names.append(linker_args.items[i]);
|
||||
try linker_export_symbol_names.append(linker_args_it.nextOrFatal());
|
||||
} else if (mem.eql(u8, arg, "--compress-debug-sections")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
const arg1 = linker_args.items[i];
|
||||
const arg1 = linker_args_it.nextOrFatal();
|
||||
linker_compress_debug_sections = std.meta.stringToEnum(link.CompressDebugSections, arg1) orelse {
|
||||
fatal("expected [none|zlib] after --compress-debug-sections, found '{s}'", .{arg1});
|
||||
};
|
||||
} else if (mem.startsWith(u8, arg, "-z")) {
|
||||
var z_arg = arg[2..];
|
||||
if (z_arg.len == 0) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker extension flag after '{s}'", .{arg});
|
||||
}
|
||||
z_arg = linker_args.items[i];
|
||||
z_arg = linker_args_it.nextOrFatal();
|
||||
}
|
||||
if (mem.eql(u8, z_arg, "nodelete")) {
|
||||
linker_z_nodelete = true;
|
||||
@ -2056,51 +2018,33 @@ fn buildOutputType(
|
||||
fatal("unsupported linker extension flag: -z {s}", .{z_arg});
|
||||
}
|
||||
} else if (mem.eql(u8, arg, "--major-image-version")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
version.major = std.fmt.parseUnsigned(u32, linker_args.items[i], 10) catch |err| {
|
||||
fatal("unable to parse major image version '{s}': {s}", .{ linker_args.items[i], @errorName(err) });
|
||||
const major = linker_args_it.nextOrFatal();
|
||||
version.major = std.fmt.parseUnsigned(u32, major, 10) catch |err| {
|
||||
fatal("unable to parse major image version '{s}': {s}", .{ major, @errorName(err) });
|
||||
};
|
||||
have_version = true;
|
||||
} else if (mem.eql(u8, arg, "--minor-image-version")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
version.minor = std.fmt.parseUnsigned(u32, linker_args.items[i], 10) catch |err| {
|
||||
fatal("unable to parse minor image version '{s}': {s}", .{ linker_args.items[i], @errorName(err) });
|
||||
const minor = linker_args_it.nextOrFatal();
|
||||
version.minor = std.fmt.parseUnsigned(u32, minor, 10) catch |err| {
|
||||
fatal("unable to parse minor image version '{s}': {s}", .{ minor, @errorName(err) });
|
||||
};
|
||||
have_version = true;
|
||||
} else if (mem.eql(u8, arg, "-e") or mem.eql(u8, arg, "--entry")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
entry = linker_args.items[i];
|
||||
entry = linker_args_it.nextOrFatal();
|
||||
} else if (mem.eql(u8, arg, "-u")) {
|
||||
try force_undefined_symbols.put(gpa, linker_args_it.nextOrFatal(), {});
|
||||
} else if (mem.eql(u8, arg, "--stack") or mem.eql(u8, arg, "-stack_size")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
stack_size_override = std.fmt.parseUnsigned(u64, linker_args.items[i], 0) catch |err| {
|
||||
fatal("unable to parse stack size override '{s}': {s}", .{ linker_args.items[i], @errorName(err) });
|
||||
const stack_size = linker_args_it.nextOrFatal();
|
||||
stack_size_override = std.fmt.parseUnsigned(u64, stack_size, 0) catch |err| {
|
||||
fatal("unable to parse stack size override '{s}': {s}", .{ stack_size, @errorName(err) });
|
||||
};
|
||||
} else if (mem.eql(u8, arg, "--image-base")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
image_base_override = std.fmt.parseUnsigned(u64, linker_args.items[i], 0) catch |err| {
|
||||
fatal("unable to parse image base override '{s}': {s}", .{ linker_args.items[i], @errorName(err) });
|
||||
const image_base = linker_args_it.nextOrFatal();
|
||||
image_base_override = std.fmt.parseUnsigned(u64, image_base, 0) catch |err| {
|
||||
fatal("unable to parse image base override '{s}': {s}", .{ image_base, @errorName(err) });
|
||||
};
|
||||
} else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
linker_script = linker_args.items[i];
|
||||
linker_script = linker_args_it.nextOrFatal();
|
||||
} else if (mem.eql(u8, arg, "--eh-frame-hdr")) {
|
||||
link_eh_frame_hdr = true;
|
||||
} else if (mem.eql(u8, arg, "--no-eh-frame-hdr")) {
|
||||
@ -2138,130 +2082,74 @@ fn buildOutputType(
|
||||
} else if (mem.eql(u8, arg, "--major-os-version") or
|
||||
mem.eql(u8, arg, "--minor-os-version"))
|
||||
{
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
// This option does not do anything.
|
||||
_ = linker_args_it.nextOrFatal();
|
||||
} else if (mem.eql(u8, arg, "--major-subsystem-version")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
|
||||
const major = linker_args_it.nextOrFatal();
|
||||
major_subsystem_version = std.fmt.parseUnsigned(
|
||||
u32,
|
||||
linker_args.items[i],
|
||||
major,
|
||||
10,
|
||||
) catch |err| {
|
||||
fatal("unable to parse major subsystem version '{s}': {s}", .{ linker_args.items[i], @errorName(err) });
|
||||
fatal("unable to parse major subsystem version '{s}': {s}", .{ major, @errorName(err) });
|
||||
};
|
||||
} else if (mem.eql(u8, arg, "--minor-subsystem-version")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
|
||||
const minor = linker_args_it.nextOrFatal();
|
||||
minor_subsystem_version = std.fmt.parseUnsigned(
|
||||
u32,
|
||||
linker_args.items[i],
|
||||
minor,
|
||||
10,
|
||||
) catch |err| {
|
||||
fatal("unable to parse minor subsystem version '{s}': {s}", .{ linker_args.items[i], @errorName(err) });
|
||||
fatal("unable to parse minor subsystem version '{s}': {s}", .{ minor, @errorName(err) });
|
||||
};
|
||||
} else if (mem.eql(u8, arg, "-framework")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
try frameworks.put(gpa, linker_args.items[i], .{});
|
||||
try frameworks.put(gpa, linker_args_it.nextOrFatal(), .{});
|
||||
} else if (mem.eql(u8, arg, "-weak_framework")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
try frameworks.put(gpa, linker_args.items[i], .{ .weak = true });
|
||||
try frameworks.put(gpa, linker_args_it.nextOrFatal(), .{ .weak = true });
|
||||
} else if (mem.eql(u8, arg, "-needed_framework")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
try frameworks.put(gpa, linker_args.items[i], .{ .needed = true });
|
||||
try frameworks.put(gpa, linker_args_it.nextOrFatal(), .{ .needed = true });
|
||||
} else if (mem.eql(u8, arg, "-needed_library")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
try system_libs.put(linker_args.items[i], .{ .needed = true });
|
||||
try system_libs.put(linker_args_it.nextOrFatal(), .{ .needed = true });
|
||||
} else if (mem.startsWith(u8, arg, "-weak-l")) {
|
||||
try system_libs.put(arg["-weak-l".len..], .{ .weak = true });
|
||||
} else if (mem.eql(u8, arg, "-weak_library")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
try system_libs.put(linker_args.items[i], .{ .weak = true });
|
||||
try system_libs.put(linker_args_it.nextOrFatal(), .{ .weak = true });
|
||||
} else if (mem.eql(u8, arg, "-compatibility_version")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
compatibility_version = std.builtin.Version.parse(linker_args.items[i]) catch |err| {
|
||||
fatal("unable to parse -compatibility_version '{s}': {s}", .{ linker_args.items[i], @errorName(err) });
|
||||
const compat_version = linker_args_it.nextOrFatal();
|
||||
compatibility_version = std.builtin.Version.parse(compat_version) catch |err| {
|
||||
fatal("unable to parse -compatibility_version '{s}': {s}", .{ compat_version, @errorName(err) });
|
||||
};
|
||||
} else if (mem.eql(u8, arg, "-current_version")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
version = std.builtin.Version.parse(linker_args.items[i]) catch |err| {
|
||||
fatal("unable to parse -current_version '{s}': {s}", .{ linker_args.items[i], @errorName(err) });
|
||||
const curr_version = linker_args_it.nextOrFatal();
|
||||
version = std.builtin.Version.parse(curr_version) catch |err| {
|
||||
fatal("unable to parse -current_version '{s}': {s}", .{ curr_version, @errorName(err) });
|
||||
};
|
||||
have_version = true;
|
||||
} else if (mem.eql(u8, arg, "--out-implib") or
|
||||
mem.eql(u8, arg, "-implib"))
|
||||
{
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
emit_implib = .{ .yes = linker_args.items[i] };
|
||||
emit_implib = .{ .yes = linker_args_it.nextOrFatal() };
|
||||
emit_implib_arg_provided = true;
|
||||
} else if (mem.eql(u8, arg, "-undefined")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
if (mem.eql(u8, "dynamic_lookup", linker_args.items[i])) {
|
||||
const lookup_type = linker_args_it.nextOrFatal();
|
||||
if (mem.eql(u8, "dynamic_lookup", lookup_type)) {
|
||||
linker_allow_shlib_undefined = true;
|
||||
} else if (mem.eql(u8, "error", linker_args.items[i])) {
|
||||
} else if (mem.eql(u8, "error", lookup_type)) {
|
||||
linker_allow_shlib_undefined = false;
|
||||
} else {
|
||||
fatal("unsupported -undefined option '{s}'", .{linker_args.items[i]});
|
||||
fatal("unsupported -undefined option '{s}'", .{lookup_type});
|
||||
}
|
||||
} else if (mem.eql(u8, arg, "-install_name")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
install_name = linker_args.items[i];
|
||||
install_name = linker_args_it.nextOrFatal();
|
||||
} else if (mem.eql(u8, arg, "-force_load")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
try link_objects.append(.{
|
||||
.path = linker_args.items[i],
|
||||
.path = linker_args_it.nextOrFatal(),
|
||||
.must_link = true,
|
||||
});
|
||||
} else if (mem.eql(u8, arg, "-hash-style") or
|
||||
mem.eql(u8, arg, "--hash-style"))
|
||||
{
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
const next_arg = linker_args.items[i];
|
||||
const next_arg = linker_args_it.nextOrFatal();
|
||||
hash_style = std.meta.stringToEnum(link.HashStyle, next_arg) orelse {
|
||||
fatal("expected [sysv|gnu|both] after --hash-style, found '{s}'", .{
|
||||
next_arg,
|
||||
@ -3219,6 +3107,7 @@ fn buildOutputType(
|
||||
.link_eh_frame_hdr = link_eh_frame_hdr,
|
||||
.link_emit_relocs = link_emit_relocs,
|
||||
.entry = entry,
|
||||
.force_undefined_symbols = force_undefined_symbols,
|
||||
.stack_size_override = stack_size_override,
|
||||
.image_base_override = image_base_override,
|
||||
.strip = strip,
|
||||
@ -5295,6 +5184,7 @@ pub const ClangArgIterator = struct {
|
||||
emit_llvm,
|
||||
sysroot,
|
||||
entry,
|
||||
force_undefined_symbol,
|
||||
weak_library,
|
||||
weak_framework,
|
||||
headerpad_max_install_names,
|
||||
|
||||
@ -104,6 +104,14 @@ pub const cases = [_]Case{
|
||||
.build_root = "test/link/macho/entry",
|
||||
.import = @import("link/macho/entry/build.zig"),
|
||||
},
|
||||
.{
|
||||
.build_root = "test/link/macho/entry_in_archive",
|
||||
.import = @import("link/macho/entry_in_archive/build.zig"),
|
||||
},
|
||||
.{
|
||||
.build_root = "test/link/macho/entry_in_dylib",
|
||||
.import = @import("link/macho/entry_in_dylib/build.zig"),
|
||||
},
|
||||
.{
|
||||
.build_root = "test/link/macho/headerpad",
|
||||
.import = @import("link/macho/headerpad/build.zig"),
|
||||
|
||||
36
test/link/macho/entry_in_archive/build.zig
Normal file
36
test/link/macho/entry_in_archive/build.zig
Normal file
@ -0,0 +1,36 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub const requires_symlinks = true;
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const test_step = b.step("test", "Test it");
|
||||
b.default_step = test_step;
|
||||
|
||||
add(b, test_step, .Debug);
|
||||
add(b, test_step, .ReleaseFast);
|
||||
add(b, test_step, .ReleaseSmall);
|
||||
add(b, test_step, .ReleaseSafe);
|
||||
}
|
||||
|
||||
fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
|
||||
const lib = b.addStaticLibrary(.{
|
||||
.name = "main",
|
||||
.optimize = optimize,
|
||||
.target = .{ .os_tag = .macos },
|
||||
});
|
||||
lib.addCSourceFile("main.c", &.{});
|
||||
lib.linkLibC();
|
||||
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "main",
|
||||
.optimize = optimize,
|
||||
.target = .{ .os_tag = .macos },
|
||||
});
|
||||
exe.linkLibrary(lib);
|
||||
exe.linkLibC();
|
||||
|
||||
const run = exe.run();
|
||||
run.skip_foreign_checks = true;
|
||||
run.expectExitCode(0);
|
||||
test_step.dependOn(&run.step);
|
||||
}
|
||||
5
test/link/macho/entry_in_archive/main.c
Normal file
5
test/link/macho/entry_in_archive/main.c
Normal file
@ -0,0 +1,5 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
return 0;
|
||||
}
|
||||
5
test/link/macho/entry_in_dylib/bootstrap.c
Normal file
5
test/link/macho/entry_in_dylib/bootstrap.c
Normal file
@ -0,0 +1,5 @@
|
||||
extern int my_main();
|
||||
|
||||
int bootstrap() {
|
||||
return my_main();
|
||||
}
|
||||
54
test/link/macho/entry_in_dylib/build.zig
Normal file
54
test/link/macho/entry_in_dylib/build.zig
Normal file
@ -0,0 +1,54 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub const requires_symlinks = true;
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const test_step = b.step("test", "Test it");
|
||||
b.default_step = test_step;
|
||||
|
||||
add(b, test_step, .Debug);
|
||||
add(b, test_step, .ReleaseFast);
|
||||
add(b, test_step, .ReleaseSmall);
|
||||
add(b, test_step, .ReleaseSafe);
|
||||
}
|
||||
|
||||
fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
|
||||
const lib = b.addSharedLibrary(.{
|
||||
.name = "bootstrap",
|
||||
.optimize = optimize,
|
||||
.target = .{ .os_tag = .macos },
|
||||
});
|
||||
lib.addCSourceFile("bootstrap.c", &.{});
|
||||
lib.linkLibC();
|
||||
lib.linker_allow_shlib_undefined = true;
|
||||
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "main",
|
||||
.optimize = optimize,
|
||||
.target = .{ .os_tag = .macos },
|
||||
});
|
||||
exe.addCSourceFile("main.c", &.{});
|
||||
exe.linkLibrary(lib);
|
||||
exe.linkLibC();
|
||||
exe.entry_symbol_name = "_bootstrap";
|
||||
exe.forceUndefinedSymbol("_my_main");
|
||||
|
||||
const check_exe = exe.checkObject();
|
||||
check_exe.checkStart("segname __TEXT");
|
||||
check_exe.checkNext("vmaddr {text_vmaddr}");
|
||||
|
||||
check_exe.checkStart("sectname __stubs");
|
||||
check_exe.checkNext("addr {stubs_vmaddr}");
|
||||
|
||||
check_exe.checkStart("cmd MAIN");
|
||||
check_exe.checkNext("entryoff {entryoff}");
|
||||
|
||||
check_exe.checkComputeCompare("text_vmaddr entryoff +", .{
|
||||
.op = .eq,
|
||||
.value = .{ .variable = "stubs_vmaddr" }, // The entrypoint should be a synthetic stub
|
||||
});
|
||||
|
||||
const run = check_exe.runAndCompare();
|
||||
run.expectStdOutEqual("Hello!\n");
|
||||
test_step.dependOn(&run.step);
|
||||
}
|
||||
6
test/link/macho/entry_in_dylib/main.c
Normal file
6
test/link/macho/entry_in_dylib/main.c
Normal file
@ -0,0 +1,6 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int my_main() {
|
||||
fprintf(stdout, "Hello!\n");
|
||||
return 0;
|
||||
}
|
||||
@ -468,6 +468,10 @@ const known_options = [_]KnownOpt{
|
||||
.name = "e",
|
||||
.ident = "entry",
|
||||
},
|
||||
.{
|
||||
.name = "u",
|
||||
.ident = "force_undefined_symbol",
|
||||
},
|
||||
.{
|
||||
.name = "weak-l",
|
||||
.ident = "weak_library",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user