mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 04:48:20 +00:00
Merge pull request #9266 from ziglang/zld-dylibs
zig ld can create dylibs now; remove system linker hack and any mention of ld64.lld from the codebase
This commit is contained in:
commit
81bf05bf6c
@ -42,7 +42,6 @@ else()
|
||||
FIND_AND_ADD_LLD_LIB(lldMinGW)
|
||||
FIND_AND_ADD_LLD_LIB(lldELF)
|
||||
FIND_AND_ADD_LLD_LIB(lldCOFF)
|
||||
FIND_AND_ADD_LLD_LIB(lldMachO)
|
||||
FIND_AND_ADD_LLD_LIB(lldWasm)
|
||||
FIND_AND_ADD_LLD_LIB(lldReaderWriter)
|
||||
FIND_AND_ADD_LLD_LIB(lldCore)
|
||||
|
||||
@ -879,24 +879,16 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
|
||||
break :blk false;
|
||||
};
|
||||
|
||||
const darwin_can_use_system_linker_and_sdk =
|
||||
const darwin_can_use_system_sdk =
|
||||
// comptime conditions
|
||||
((build_options.have_llvm and comptime std.Target.current.isDarwin()) and
|
||||
// runtime conditions
|
||||
(use_lld and std.builtin.os.tag == .macos and options.target.isDarwin()));
|
||||
|
||||
const darwin_system_linker_hack = blk: {
|
||||
if (darwin_can_use_system_linker_and_sdk) {
|
||||
break :blk std.os.getenv("ZIG_SYSTEM_LINKER_HACK") != null;
|
||||
} else {
|
||||
break :blk false;
|
||||
}
|
||||
};
|
||||
|
||||
const sysroot = blk: {
|
||||
if (options.sysroot) |sysroot| {
|
||||
break :blk sysroot;
|
||||
} else if (darwin_can_use_system_linker_and_sdk) {
|
||||
} else if (darwin_can_use_system_sdk) {
|
||||
// TODO Revisit this targeting versions lower than macOS 11 when LLVM 12 is out.
|
||||
// See https://github.com/ziglang/zig/issues/6996
|
||||
const at_least_big_sur = options.target.os.getVersionRange().semver.min.major >= 11;
|
||||
@ -915,8 +907,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
|
||||
break :blk false;
|
||||
} else if (options.c_source_files.len == 0) {
|
||||
break :blk false;
|
||||
} else if (darwin_system_linker_hack) {
|
||||
break :blk false;
|
||||
} else switch (options.output_mode) {
|
||||
.Lib, .Obj => break :blk false,
|
||||
.Exe => switch (options.optimize_mode) {
|
||||
@ -1295,7 +1285,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
|
||||
.optimize_mode = options.optimize_mode,
|
||||
.use_lld = use_lld,
|
||||
.use_llvm = use_llvm,
|
||||
.system_linker_hack = darwin_system_linker_hack,
|
||||
.link_libc = link_libc,
|
||||
.link_libcpp = link_libcpp,
|
||||
.link_libunwind = link_libunwind,
|
||||
|
||||
@ -496,12 +496,10 @@ fn LLVMInitializeAllAsmParsers() callconv(.C) void {
|
||||
|
||||
extern fn ZigLLDLinkCOFF(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int;
|
||||
extern fn ZigLLDLinkELF(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int;
|
||||
extern fn ZigLLDLinkMachO(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int;
|
||||
extern fn ZigLLDLinkWasm(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int;
|
||||
|
||||
pub const LinkCOFF = ZigLLDLinkCOFF;
|
||||
pub const LinkELF = ZigLLDLinkELF;
|
||||
pub const LinkMachO = ZigLLDLinkMachO;
|
||||
pub const LinkWasm = ZigLLDLinkWasm;
|
||||
|
||||
pub const ObjectFormatType = enum(c_int) {
|
||||
|
||||
@ -61,9 +61,6 @@ pub const Options = struct {
|
||||
/// other objects.
|
||||
/// Otherwise (depending on `use_lld`) this link code directly outputs and updates the final binary.
|
||||
use_llvm: bool,
|
||||
/// Darwin-only. If this is true, `use_llvm` is true, and `is_native_os` is true, this link code will
|
||||
/// use system linker `ld` instead of the LLD.
|
||||
system_linker_hack: bool,
|
||||
link_libc: bool,
|
||||
link_libcpp: bool,
|
||||
link_libunwind: bool,
|
||||
|
||||
@ -430,7 +430,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO {
|
||||
|
||||
pub fn flush(self: *MachO, comp: *Compilation) !void {
|
||||
if (build_options.have_llvm and self.base.options.use_lld) {
|
||||
return self.linkWithLLD(comp);
|
||||
return self.linkWithZld(comp);
|
||||
} else {
|
||||
switch (self.base.options.effectiveOutputMode()) {
|
||||
.Exe, .Obj => {},
|
||||
@ -593,7 +593,7 @@ fn resolveFramework(
|
||||
return null;
|
||||
}
|
||||
|
||||
fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
|
||||
fn linkWithZld(self: *MachO, comp: *Compilation) !void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
@ -631,7 +631,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
|
||||
const stack_size = self.base.options.stack_size_override orelse 0;
|
||||
const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os;
|
||||
|
||||
const id_symlink_basename = "lld.id";
|
||||
const id_symlink_basename = "zld.id";
|
||||
|
||||
var man: Cache.Manifest = undefined;
|
||||
defer if (!self.base.options.disable_lld_caching) man.deinit();
|
||||
@ -669,7 +669,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
|
||||
man.hash.addStringSet(self.base.options.system_libs);
|
||||
man.hash.add(allow_shlib_undefined);
|
||||
man.hash.add(self.base.options.bind_global_refs_locally);
|
||||
man.hash.add(self.base.options.system_linker_hack);
|
||||
man.hash.addOptionalBytes(self.base.options.sysroot);
|
||||
|
||||
// We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
|
||||
@ -682,17 +681,17 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
|
||||
id_symlink_basename,
|
||||
&prev_digest_buf,
|
||||
) catch |err| blk: {
|
||||
log.debug("MachO LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
|
||||
log.debug("MachO Zld new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
|
||||
// Handle this as a cache miss.
|
||||
break :blk prev_digest_buf[0..0];
|
||||
};
|
||||
if (mem.eql(u8, prev_digest, &digest)) {
|
||||
log.debug("MachO LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
|
||||
log.debug("MachO Zld digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
|
||||
// Hot diggity dog! The output binary is already there.
|
||||
self.base.lock = man.toOwnedLock();
|
||||
return;
|
||||
}
|
||||
log.debug("MachO LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
|
||||
log.debug("MachO Zld prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
|
||||
|
||||
// We are about to change the output file to be different, so we invalidate the build hash now.
|
||||
directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
|
||||
@ -726,495 +725,227 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
|
||||
if (!mem.eql(u8, the_object_path, full_out_path)) {
|
||||
try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
|
||||
}
|
||||
} else outer: {
|
||||
const use_zld = blk: {
|
||||
if (self.base.options.is_native_os and self.base.options.system_linker_hack) {
|
||||
// If the user forces the use of ld64, make sure we are running native!
|
||||
break :blk false;
|
||||
}
|
||||
|
||||
if (self.base.options.target.cpu.arch == .aarch64) {
|
||||
// On aarch64, always use zld.
|
||||
break :blk true;
|
||||
}
|
||||
|
||||
if (self.base.options.output_mode == .Lib or
|
||||
self.base.options.linker_script != null)
|
||||
{
|
||||
// Fallback to LLD in this handful of cases on x86_64 only.
|
||||
break :blk false;
|
||||
}
|
||||
|
||||
break :blk true;
|
||||
};
|
||||
|
||||
if (use_zld) {
|
||||
var zld = Zld.init(self.base.allocator);
|
||||
defer {
|
||||
zld.closeFiles();
|
||||
zld.deinit();
|
||||
}
|
||||
zld.arch = target.cpu.arch;
|
||||
zld.stack_size = stack_size;
|
||||
|
||||
// Positional arguments to the linker such as object files and static archives.
|
||||
var positionals = std.ArrayList([]const u8).init(arena);
|
||||
|
||||
try positionals.appendSlice(self.base.options.objects);
|
||||
|
||||
for (comp.c_object_table.keys()) |key| {
|
||||
try positionals.append(key.status.success.object_path);
|
||||
}
|
||||
|
||||
if (module_obj_path) |p| {
|
||||
try positionals.append(p);
|
||||
}
|
||||
|
||||
try positionals.append(comp.compiler_rt_static_lib.?.full_object_path);
|
||||
|
||||
// libc++ dep
|
||||
if (self.base.options.link_libcpp) {
|
||||
try positionals.append(comp.libcxxabi_static_lib.?.full_object_path);
|
||||
try positionals.append(comp.libcxx_static_lib.?.full_object_path);
|
||||
}
|
||||
|
||||
// Shared and static libraries passed via `-l` flag.
|
||||
var search_lib_names = std.ArrayList([]const u8).init(arena);
|
||||
|
||||
const system_libs = self.base.options.system_libs.keys();
|
||||
for (system_libs) |link_lib| {
|
||||
// By this time, we depend on these libs being dynamically linked libraries and not static libraries
|
||||
// (the check for that needs to be earlier), but they could be full paths to .dylib files, in which
|
||||
// case we want to avoid prepending "-l".
|
||||
if (Compilation.classifyFileExt(link_lib) == .shared_library) {
|
||||
try positionals.append(link_lib);
|
||||
continue;
|
||||
}
|
||||
|
||||
try search_lib_names.append(link_lib);
|
||||
}
|
||||
|
||||
var lib_dirs = std.ArrayList([]const u8).init(arena);
|
||||
for (self.base.options.lib_dirs) |dir| {
|
||||
if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| {
|
||||
try lib_dirs.append(search_dir);
|
||||
} else {
|
||||
log.warn("directory not found for '-L{s}'", .{dir});
|
||||
}
|
||||
}
|
||||
|
||||
var libs = std.ArrayList([]const u8).init(arena);
|
||||
var lib_not_found = false;
|
||||
for (search_lib_names.items) |lib_name| {
|
||||
// Assume ld64 default: -search_paths_first
|
||||
// Look in each directory for a dylib (stub first), and then for archive
|
||||
// TODO implement alternative: -search_dylibs_first
|
||||
for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {
|
||||
if (try resolveLib(arena, lib_dirs.items, lib_name, ext)) |full_path| {
|
||||
try libs.append(full_path);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
log.warn("library not found for '-l{s}'", .{lib_name});
|
||||
lib_not_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (lib_not_found) {
|
||||
log.warn("Library search paths:", .{});
|
||||
for (lib_dirs.items) |dir| {
|
||||
log.warn(" {s}", .{dir});
|
||||
}
|
||||
}
|
||||
|
||||
// If we're compiling native and we can find libSystem.B.{dylib, tbd},
|
||||
// we link against that instead of embedded libSystem.B.tbd file.
|
||||
var native_libsystem_available = false;
|
||||
if (self.base.options.is_native_os) blk: {
|
||||
// Try stub file first. If we hit it, then we're done as the stub file
|
||||
// re-exports every single symbol definition.
|
||||
if (try resolveLib(arena, lib_dirs.items, "System", ".tbd")) |full_path| {
|
||||
try libs.append(full_path);
|
||||
native_libsystem_available = true;
|
||||
break :blk;
|
||||
}
|
||||
// If we didn't hit the stub file, try .dylib next. However, libSystem.dylib
|
||||
// doesn't export libc.dylib which we'll need to resolve subsequently also.
|
||||
if (try resolveLib(arena, lib_dirs.items, "System", ".dylib")) |libsystem_path| {
|
||||
if (try resolveLib(arena, lib_dirs.items, "c", ".dylib")) |libc_path| {
|
||||
try libs.append(libsystem_path);
|
||||
try libs.append(libc_path);
|
||||
native_libsystem_available = true;
|
||||
break :blk;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!native_libsystem_available) {
|
||||
const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
|
||||
"libc", "darwin", "libSystem.B.tbd",
|
||||
});
|
||||
try libs.append(full_path);
|
||||
}
|
||||
|
||||
// frameworks
|
||||
var framework_dirs = std.ArrayList([]const u8).init(arena);
|
||||
for (self.base.options.framework_dirs) |dir| {
|
||||
if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| {
|
||||
try framework_dirs.append(search_dir);
|
||||
} else {
|
||||
log.warn("directory not found for '-F{s}'", .{dir});
|
||||
}
|
||||
}
|
||||
|
||||
var framework_not_found = false;
|
||||
for (self.base.options.frameworks) |framework| {
|
||||
for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
|
||||
if (try resolveFramework(arena, framework_dirs.items, framework, ext)) |full_path| {
|
||||
try libs.append(full_path);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
log.warn("framework not found for '-f{s}'", .{framework});
|
||||
framework_not_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (framework_not_found) {
|
||||
log.warn("Framework search paths:", .{});
|
||||
for (framework_dirs.items) |dir| {
|
||||
log.warn(" {s}", .{dir});
|
||||
}
|
||||
}
|
||||
|
||||
// rpaths
|
||||
var rpath_table = std.StringArrayHashMap(void).init(arena);
|
||||
for (self.base.options.rpath_list) |rpath| {
|
||||
if (rpath_table.contains(rpath)) continue;
|
||||
try rpath_table.putNoClobber(rpath, {});
|
||||
}
|
||||
|
||||
var rpaths = std.ArrayList([]const u8).init(arena);
|
||||
try rpaths.ensureCapacity(rpath_table.count());
|
||||
for (rpath_table.keys()) |*key| {
|
||||
rpaths.appendAssumeCapacity(key.*);
|
||||
}
|
||||
|
||||
if (self.base.options.verbose_link) {
|
||||
var argv = std.ArrayList([]const u8).init(arena);
|
||||
|
||||
try argv.append("zig");
|
||||
try argv.append("ld");
|
||||
|
||||
if (self.base.options.sysroot) |syslibroot| {
|
||||
try argv.append("-syslibroot");
|
||||
try argv.append(syslibroot);
|
||||
}
|
||||
|
||||
for (rpaths.items) |rpath| {
|
||||
try argv.append("-rpath");
|
||||
try argv.append(rpath);
|
||||
}
|
||||
|
||||
try argv.appendSlice(positionals.items);
|
||||
|
||||
try argv.append("-o");
|
||||
try argv.append(full_out_path);
|
||||
|
||||
if (native_libsystem_available) {
|
||||
try argv.append("-lSystem");
|
||||
try argv.append("-lc");
|
||||
}
|
||||
|
||||
for (search_lib_names.items) |l_name| {
|
||||
try argv.append(try std.fmt.allocPrint(arena, "-l{s}", .{l_name}));
|
||||
}
|
||||
|
||||
for (self.base.options.lib_dirs) |lib_dir| {
|
||||
try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}));
|
||||
}
|
||||
|
||||
Compilation.dump_argv(argv.items);
|
||||
}
|
||||
|
||||
try zld.link(positionals.items, full_out_path, .{
|
||||
.syslibroot = self.base.options.sysroot,
|
||||
.libs = libs.items,
|
||||
.rpaths = rpaths.items,
|
||||
});
|
||||
|
||||
break :outer;
|
||||
} else {
|
||||
var zld = Zld.init(self.base.allocator);
|
||||
defer {
|
||||
zld.closeFiles();
|
||||
zld.deinit();
|
||||
}
|
||||
zld.target = target;
|
||||
zld.stack_size = stack_size;
|
||||
|
||||
// Create an LLD command line and invoke it.
|
||||
var argv = std.ArrayList([]const u8).init(self.base.allocator);
|
||||
defer argv.deinit();
|
||||
// Positional arguments to the linker such as object files and static archives.
|
||||
var positionals = std.ArrayList([]const u8).init(arena);
|
||||
|
||||
// TODO https://github.com/ziglang/zig/issues/6971
|
||||
// Note that there is no need to check if running natively since we do that already
|
||||
// when setting `system_linker_hack` in Compilation struct.
|
||||
if (self.base.options.system_linker_hack) {
|
||||
try argv.append("ld");
|
||||
} else {
|
||||
// We will invoke ourselves as a child process to gain access to LLD.
|
||||
// This is necessary because LLD does not behave properly as a library -
|
||||
// it calls exit() and does not reset all global data between invocations.
|
||||
try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "ld64.lld" });
|
||||
|
||||
try argv.append("-error-limit");
|
||||
try argv.append("0");
|
||||
}
|
||||
|
||||
if (self.base.options.lto) {
|
||||
switch (self.base.options.optimize_mode) {
|
||||
.Debug => {},
|
||||
.ReleaseSmall => try argv.append("-O2"),
|
||||
.ReleaseFast, .ReleaseSafe => try argv.append("-O3"),
|
||||
}
|
||||
}
|
||||
try argv.append("-demangle");
|
||||
|
||||
if (self.base.options.rdynamic and !self.base.options.system_linker_hack) {
|
||||
try argv.append("--export-dynamic");
|
||||
}
|
||||
|
||||
try argv.appendSlice(self.base.options.extra_lld_args);
|
||||
|
||||
if (self.base.options.z_nodelete) {
|
||||
try argv.append("-z");
|
||||
try argv.append("nodelete");
|
||||
}
|
||||
if (self.base.options.z_defs) {
|
||||
try argv.append("-z");
|
||||
try argv.append("defs");
|
||||
}
|
||||
|
||||
if (is_exe_or_dyn_lib) {
|
||||
try argv.append("-dynamic");
|
||||
}
|
||||
|
||||
if (is_dyn_lib) {
|
||||
try argv.append("-dylib");
|
||||
|
||||
if (self.base.options.version) |ver| {
|
||||
const compat_vers = try std.fmt.allocPrint(arena, "{d}.0.0", .{ver.major});
|
||||
try argv.append("-compatibility_version");
|
||||
try argv.append(compat_vers);
|
||||
|
||||
const cur_vers = try std.fmt.allocPrint(arena, "{d}.{d}.{d}", .{ ver.major, ver.minor, ver.patch });
|
||||
try argv.append("-current_version");
|
||||
try argv.append(cur_vers);
|
||||
}
|
||||
|
||||
const dylib_install_name = try std.fmt.allocPrint(arena, "@rpath/{s}", .{self.base.options.emit.?.sub_path});
|
||||
try argv.append("-install_name");
|
||||
try argv.append(dylib_install_name);
|
||||
}
|
||||
|
||||
try argv.append("-arch");
|
||||
try argv.append(darwinArchString(target.cpu.arch));
|
||||
|
||||
switch (target.os.tag) {
|
||||
.macos => {
|
||||
try argv.append("-macosx_version_min");
|
||||
},
|
||||
.ios, .tvos, .watchos => switch (target.cpu.arch) {
|
||||
.i386, .x86_64 => {
|
||||
try argv.append("-ios_simulator_version_min");
|
||||
},
|
||||
else => {
|
||||
try argv.append("-iphoneos_version_min");
|
||||
},
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
const ver = target.os.version_range.semver.min;
|
||||
const version_string = try std.fmt.allocPrint(arena, "{d}.{d}.{d}", .{ ver.major, ver.minor, ver.patch });
|
||||
try argv.append(version_string);
|
||||
|
||||
try argv.append("-sdk_version");
|
||||
try argv.append(version_string);
|
||||
|
||||
if (target_util.requiresPIE(target) and self.base.options.output_mode == .Exe) {
|
||||
try argv.append("-pie");
|
||||
}
|
||||
|
||||
try argv.append("-o");
|
||||
try argv.append(full_out_path);
|
||||
|
||||
// rpaths
|
||||
var rpath_table = std.StringHashMap(void).init(self.base.allocator);
|
||||
defer rpath_table.deinit();
|
||||
for (self.base.options.rpath_list) |rpath| {
|
||||
if ((try rpath_table.fetchPut(rpath, {})) == null) {
|
||||
try argv.append("-rpath");
|
||||
try argv.append(rpath);
|
||||
}
|
||||
}
|
||||
if (is_dyn_lib) {
|
||||
if ((try rpath_table.fetchPut(full_out_path, {})) == null) {
|
||||
try argv.append("-rpath");
|
||||
try argv.append(full_out_path);
|
||||
}
|
||||
}
|
||||
|
||||
if (self.base.options.sysroot) |dir| {
|
||||
try argv.append("-syslibroot");
|
||||
try argv.append(dir);
|
||||
}
|
||||
|
||||
for (self.base.options.lib_dirs) |lib_dir| {
|
||||
try argv.append("-L");
|
||||
try argv.append(lib_dir);
|
||||
}
|
||||
|
||||
// Positional arguments to the linker such as object files.
|
||||
try argv.appendSlice(self.base.options.objects);
|
||||
try positionals.appendSlice(self.base.options.objects);
|
||||
|
||||
for (comp.c_object_table.keys()) |key| {
|
||||
try argv.append(key.status.success.object_path);
|
||||
try positionals.append(key.status.success.object_path);
|
||||
}
|
||||
|
||||
if (module_obj_path) |p| {
|
||||
try argv.append(p);
|
||||
try positionals.append(p);
|
||||
}
|
||||
|
||||
// compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce
|
||||
if (is_exe_or_dyn_lib and !self.base.options.skip_linker_dependencies) {
|
||||
try argv.append(comp.compiler_rt_static_lib.?.full_object_path);
|
||||
try positionals.append(comp.compiler_rt_static_lib.?.full_object_path);
|
||||
|
||||
// libc++ dep
|
||||
if (self.base.options.link_libcpp) {
|
||||
try positionals.append(comp.libcxxabi_static_lib.?.full_object_path);
|
||||
try positionals.append(comp.libcxx_static_lib.?.full_object_path);
|
||||
}
|
||||
|
||||
// Shared libraries.
|
||||
// Shared and static libraries passed via `-l` flag.
|
||||
var search_lib_names = std.ArrayList([]const u8).init(arena);
|
||||
|
||||
const system_libs = self.base.options.system_libs.keys();
|
||||
try argv.ensureCapacity(argv.items.len + system_libs.len);
|
||||
for (system_libs) |link_lib| {
|
||||
// By this time, we depend on these libs being dynamically linked libraries and not static libraries
|
||||
// (the check for that needs to be earlier), but they could be full paths to .dylib files, in which
|
||||
// case we want to avoid prepending "-l".
|
||||
const ext = Compilation.classifyFileExt(link_lib);
|
||||
const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{s}", .{link_lib});
|
||||
argv.appendAssumeCapacity(arg);
|
||||
}
|
||||
|
||||
// libc++ dep
|
||||
if (self.base.options.link_libcpp) {
|
||||
try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
|
||||
try argv.append(comp.libcxx_static_lib.?.full_object_path);
|
||||
}
|
||||
|
||||
// On Darwin, libSystem has libc in it, but also you have to use it
|
||||
// to make syscalls because the syscall numbers are not documented
|
||||
// and change between versions. So we always link against libSystem.
|
||||
// LLD craps out if you do -lSystem cross compiling, so until that
|
||||
// codebase gets some love from the new maintainers we're left with
|
||||
// this dirty hack.
|
||||
if (self.base.options.is_native_os) {
|
||||
try argv.append("-lSystem");
|
||||
}
|
||||
|
||||
for (self.base.options.framework_dirs) |framework_dir| {
|
||||
try argv.append("-F");
|
||||
try argv.append(framework_dir);
|
||||
}
|
||||
for (self.base.options.frameworks) |framework| {
|
||||
try argv.append("-framework");
|
||||
try argv.append(framework);
|
||||
}
|
||||
|
||||
if (allow_shlib_undefined) {
|
||||
try argv.append("-undefined");
|
||||
try argv.append("dynamic_lookup");
|
||||
}
|
||||
if (self.base.options.bind_global_refs_locally) {
|
||||
try argv.append("-Bsymbolic");
|
||||
}
|
||||
|
||||
if (self.base.options.verbose_link) {
|
||||
// Potentially skip over our own name so that the LLD linker name is the first argv item.
|
||||
const adjusted_argv = if (self.base.options.system_linker_hack) argv.items else argv.items[1..];
|
||||
Compilation.dump_argv(adjusted_argv);
|
||||
}
|
||||
|
||||
// TODO https://github.com/ziglang/zig/issues/6971
|
||||
// Note that there is no need to check if running natively since we do that already
|
||||
// when setting `system_linker_hack` in Compilation struct.
|
||||
if (self.base.options.system_linker_hack) {
|
||||
const result = try std.ChildProcess.exec(.{ .allocator = self.base.allocator, .argv = argv.items });
|
||||
defer {
|
||||
self.base.allocator.free(result.stdout);
|
||||
self.base.allocator.free(result.stderr);
|
||||
if (Compilation.classifyFileExt(link_lib) == .shared_library) {
|
||||
try positionals.append(link_lib);
|
||||
continue;
|
||||
}
|
||||
if (result.stdout.len != 0) {
|
||||
log.warn("unexpected LD stdout: {s}", .{result.stdout});
|
||||
}
|
||||
if (result.stderr.len != 0) {
|
||||
log.warn("unexpected LD stderr: {s}", .{result.stderr});
|
||||
}
|
||||
if (result.term != .Exited or result.term.Exited != 0) {
|
||||
// TODO parse this output and surface with the Compilation API rather than
|
||||
// directly outputting to stderr here.
|
||||
log.err("{s}", .{result.stderr});
|
||||
return error.LDReportedFailure;
|
||||
}
|
||||
} else {
|
||||
// Sadly, we must run LLD as a child process because it does not behave
|
||||
// properly as a library.
|
||||
const child = try std.ChildProcess.init(argv.items, arena);
|
||||
defer child.deinit();
|
||||
|
||||
if (comp.clang_passthrough_mode) {
|
||||
child.stdin_behavior = .Inherit;
|
||||
child.stdout_behavior = .Inherit;
|
||||
child.stderr_behavior = .Inherit;
|
||||
try search_lib_names.append(link_lib);
|
||||
}
|
||||
|
||||
const term = child.spawnAndWait() catch |err| {
|
||||
log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
|
||||
return error.UnableToSpawnSelf;
|
||||
};
|
||||
switch (term) {
|
||||
.Exited => |code| {
|
||||
if (code != 0) {
|
||||
// TODO https://github.com/ziglang/zig/issues/6342
|
||||
std.process.exit(1);
|
||||
}
|
||||
},
|
||||
else => {
|
||||
log.err("{s} terminated", .{argv.items[0]});
|
||||
return error.LLDCrashed;
|
||||
},
|
||||
var lib_dirs = std.ArrayList([]const u8).init(arena);
|
||||
for (self.base.options.lib_dirs) |dir| {
|
||||
if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| {
|
||||
try lib_dirs.append(search_dir);
|
||||
} else {
|
||||
log.warn("directory not found for '-L{s}'", .{dir});
|
||||
}
|
||||
}
|
||||
|
||||
var libs = std.ArrayList([]const u8).init(arena);
|
||||
var lib_not_found = false;
|
||||
for (search_lib_names.items) |lib_name| {
|
||||
// Assume ld64 default: -search_paths_first
|
||||
// Look in each directory for a dylib (stub first), and then for archive
|
||||
// TODO implement alternative: -search_dylibs_first
|
||||
for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {
|
||||
if (try resolveLib(arena, lib_dirs.items, lib_name, ext)) |full_path| {
|
||||
try libs.append(full_path);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
child.stdin_behavior = .Ignore;
|
||||
child.stdout_behavior = .Ignore;
|
||||
child.stderr_behavior = .Pipe;
|
||||
log.warn("library not found for '-l{s}'", .{lib_name});
|
||||
lib_not_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
try child.spawn();
|
||||
if (lib_not_found) {
|
||||
log.warn("Library search paths:", .{});
|
||||
for (lib_dirs.items) |dir| {
|
||||
log.warn(" {s}", .{dir});
|
||||
}
|
||||
}
|
||||
|
||||
const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024);
|
||||
|
||||
const term = child.wait() catch |err| {
|
||||
log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
|
||||
return error.UnableToSpawnSelf;
|
||||
};
|
||||
|
||||
switch (term) {
|
||||
.Exited => |code| {
|
||||
if (code != 0) {
|
||||
// TODO parse this output and surface with the Compilation API rather than
|
||||
// directly outputting to stderr here.
|
||||
std.debug.print("{s}", .{stderr});
|
||||
return error.LLDReportedFailure;
|
||||
}
|
||||
},
|
||||
else => {
|
||||
log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
|
||||
return error.LLDCrashed;
|
||||
},
|
||||
}
|
||||
|
||||
if (stderr.len != 0) {
|
||||
log.warn("unexpected LLD stderr:\n{s}", .{stderr});
|
||||
// If we're compiling native and we can find libSystem.B.{dylib, tbd},
|
||||
// we link against that instead of embedded libSystem.B.tbd file.
|
||||
var native_libsystem_available = false;
|
||||
if (self.base.options.is_native_os) blk: {
|
||||
// Try stub file first. If we hit it, then we're done as the stub file
|
||||
// re-exports every single symbol definition.
|
||||
if (try resolveLib(arena, lib_dirs.items, "System", ".tbd")) |full_path| {
|
||||
try libs.append(full_path);
|
||||
native_libsystem_available = true;
|
||||
break :blk;
|
||||
}
|
||||
// If we didn't hit the stub file, try .dylib next. However, libSystem.dylib
|
||||
// doesn't export libc.dylib which we'll need to resolve subsequently also.
|
||||
if (try resolveLib(arena, lib_dirs.items, "System", ".dylib")) |libsystem_path| {
|
||||
if (try resolveLib(arena, lib_dirs.items, "c", ".dylib")) |libc_path| {
|
||||
try libs.append(libsystem_path);
|
||||
try libs.append(libc_path);
|
||||
native_libsystem_available = true;
|
||||
break :blk;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!native_libsystem_available) {
|
||||
const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
|
||||
"libc", "darwin", "libSystem.B.tbd",
|
||||
});
|
||||
try libs.append(full_path);
|
||||
}
|
||||
|
||||
// frameworks
|
||||
var framework_dirs = std.ArrayList([]const u8).init(arena);
|
||||
for (self.base.options.framework_dirs) |dir| {
|
||||
if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| {
|
||||
try framework_dirs.append(search_dir);
|
||||
} else {
|
||||
log.warn("directory not found for '-F{s}'", .{dir});
|
||||
}
|
||||
}
|
||||
|
||||
var framework_not_found = false;
|
||||
for (self.base.options.frameworks) |framework| {
|
||||
for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
|
||||
if (try resolveFramework(arena, framework_dirs.items, framework, ext)) |full_path| {
|
||||
try libs.append(full_path);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
log.warn("framework not found for '-f{s}'", .{framework});
|
||||
framework_not_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (framework_not_found) {
|
||||
log.warn("Framework search paths:", .{});
|
||||
for (framework_dirs.items) |dir| {
|
||||
log.warn(" {s}", .{dir});
|
||||
}
|
||||
}
|
||||
|
||||
// rpaths
|
||||
var rpath_table = std.StringArrayHashMap(void).init(arena);
|
||||
for (self.base.options.rpath_list) |rpath| {
|
||||
if (rpath_table.contains(rpath)) continue;
|
||||
try rpath_table.putNoClobber(rpath, {});
|
||||
}
|
||||
|
||||
var rpaths = std.ArrayList([]const u8).init(arena);
|
||||
try rpaths.ensureCapacity(rpath_table.count());
|
||||
for (rpath_table.keys()) |*key| {
|
||||
rpaths.appendAssumeCapacity(key.*);
|
||||
}
|
||||
|
||||
const output: Zld.Output = output: {
|
||||
if (is_dyn_lib) {
|
||||
const install_name = try std.fmt.allocPrint(arena, "@rpath/{s}", .{
|
||||
self.base.options.emit.?.sub_path,
|
||||
});
|
||||
break :output .{
|
||||
.tag = .dylib,
|
||||
.path = full_out_path,
|
||||
.install_name = install_name,
|
||||
};
|
||||
}
|
||||
break :output .{
|
||||
.tag = .exe,
|
||||
.path = full_out_path,
|
||||
};
|
||||
};
|
||||
|
||||
if (self.base.options.verbose_link) {
|
||||
var argv = std.ArrayList([]const u8).init(arena);
|
||||
|
||||
try argv.append("zig");
|
||||
try argv.append("ld");
|
||||
|
||||
if (is_exe_or_dyn_lib) {
|
||||
try argv.append("-dynamic");
|
||||
}
|
||||
|
||||
if (is_dyn_lib) {
|
||||
try argv.append("-dylib");
|
||||
|
||||
try argv.append("-install_name");
|
||||
try argv.append(output.install_name.?);
|
||||
}
|
||||
|
||||
if (self.base.options.sysroot) |syslibroot| {
|
||||
try argv.append("-syslibroot");
|
||||
try argv.append(syslibroot);
|
||||
}
|
||||
|
||||
for (rpaths.items) |rpath| {
|
||||
try argv.append("-rpath");
|
||||
try argv.append(rpath);
|
||||
}
|
||||
|
||||
try argv.appendSlice(positionals.items);
|
||||
|
||||
try argv.append("-o");
|
||||
try argv.append(output.path);
|
||||
|
||||
if (native_libsystem_available) {
|
||||
try argv.append("-lSystem");
|
||||
try argv.append("-lc");
|
||||
}
|
||||
|
||||
for (search_lib_names.items) |l_name| {
|
||||
try argv.append(try std.fmt.allocPrint(arena, "-l{s}", .{l_name}));
|
||||
}
|
||||
|
||||
for (self.base.options.lib_dirs) |lib_dir| {
|
||||
try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}));
|
||||
}
|
||||
|
||||
Compilation.dump_argv(argv.items);
|
||||
}
|
||||
|
||||
try zld.link(positionals.items, output, .{
|
||||
.syslibroot = self.base.options.sysroot,
|
||||
.libs = libs.items,
|
||||
.rpaths = rpaths.items,
|
||||
});
|
||||
}
|
||||
|
||||
if (!self.base.options.disable_lld_caching) {
|
||||
|
||||
@ -334,8 +334,9 @@ pub fn finalize(self: *Trie) !void {
|
||||
self.ordered_nodes.shrinkRetainingCapacity(0);
|
||||
try self.ordered_nodes.ensureCapacity(self.allocator, self.node_count);
|
||||
|
||||
const Fifo = std.fifo.LinearFifo(*Node, .{ .Static = std.math.maxInt(u8) });
|
||||
var fifo = Fifo.init();
|
||||
var fifo = std.fifo.LinearFifo(*Node, .Dynamic).init(self.allocator);
|
||||
defer fifo.deinit();
|
||||
|
||||
try fifo.writeItem(self.root.?);
|
||||
|
||||
while (fifo.readItem()) |next| {
|
||||
|
||||
@ -25,10 +25,10 @@ usingnamespace @import("bind.zig");
|
||||
|
||||
allocator: *Allocator,
|
||||
|
||||
arch: ?std.Target.Cpu.Arch = null,
|
||||
target: ?std.Target = null,
|
||||
page_size: ?u16 = null,
|
||||
file: ?fs.File = null,
|
||||
out_path: ?[]const u8 = null,
|
||||
output: ?Output = null,
|
||||
|
||||
// TODO these args will become obselete once Zld is coalesced with incremental
|
||||
// linker.
|
||||
@ -54,6 +54,7 @@ dylinker_cmd_index: ?u16 = null,
|
||||
data_in_code_cmd_index: ?u16 = null,
|
||||
function_starts_cmd_index: ?u16 = null,
|
||||
main_cmd_index: ?u16 = null,
|
||||
dylib_id_cmd_index: ?u16 = null,
|
||||
version_min_cmd_index: ?u16 = null,
|
||||
source_version_cmd_index: ?u16 = null,
|
||||
uuid_cmd_index: ?u16 = null,
|
||||
@ -118,6 +119,12 @@ got_entries: std.ArrayListUnmanaged(*Symbol) = .{},
|
||||
|
||||
stub_helper_stubs_start_off: ?u64 = null,
|
||||
|
||||
pub const Output = struct {
|
||||
tag: enum { exe, dylib },
|
||||
path: []const u8,
|
||||
install_name: ?[]const u8 = null,
|
||||
};
|
||||
|
||||
const TlvOffset = struct {
|
||||
source_addr: u64,
|
||||
offset: u64,
|
||||
@ -200,36 +207,17 @@ const LinkArgs = struct {
|
||||
rpaths: []const []const u8,
|
||||
};
|
||||
|
||||
pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: LinkArgs) !void {
|
||||
pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArgs) !void {
|
||||
if (files.len == 0) return error.NoInputFiles;
|
||||
if (out_path.len == 0) return error.EmptyOutputPath;
|
||||
if (output.path.len == 0) return error.EmptyOutputPath;
|
||||
|
||||
if (self.arch == null) {
|
||||
// Try inferring the arch from the object files.
|
||||
self.arch = blk: {
|
||||
const file = try fs.cwd().openFile(files[0], .{});
|
||||
defer file.close();
|
||||
var reader = file.reader();
|
||||
const header = try reader.readStruct(macho.mach_header_64);
|
||||
const arch: std.Target.Cpu.Arch = switch (header.cputype) {
|
||||
macho.CPU_TYPE_X86_64 => .x86_64,
|
||||
macho.CPU_TYPE_ARM64 => .aarch64,
|
||||
else => |value| {
|
||||
log.err("unsupported cpu architecture 0x{x}", .{value});
|
||||
return error.UnsupportedCpuArchitecture;
|
||||
},
|
||||
};
|
||||
break :blk arch;
|
||||
};
|
||||
}
|
||||
|
||||
self.page_size = switch (self.arch.?) {
|
||||
self.page_size = switch (self.target.?.cpu.arch) {
|
||||
.aarch64 => 0x4000,
|
||||
.x86_64 => 0x1000,
|
||||
else => unreachable,
|
||||
};
|
||||
self.out_path = out_path;
|
||||
self.file = try fs.cwd().createFile(out_path, .{
|
||||
self.output = output;
|
||||
self.file = try fs.cwd().createFile(self.output.?.path, .{
|
||||
.truncate = true,
|
||||
.read = true,
|
||||
.mode = if (std.Target.current.os.tag == .windows) 0 else 0o777,
|
||||
@ -263,19 +251,19 @@ fn parseInputFiles(self: *Zld, files: []const []const u8, syslibroot: ?[]const u
|
||||
break :full_path try self.allocator.dupe(u8, path);
|
||||
};
|
||||
|
||||
if (try Object.createAndParseFromPath(self.allocator, self.arch.?, full_path)) |object| {
|
||||
if (try Object.createAndParseFromPath(self.allocator, self.target.?.cpu.arch, full_path)) |object| {
|
||||
try self.objects.append(self.allocator, object);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (try Archive.createAndParseFromPath(self.allocator, self.arch.?, full_path)) |archive| {
|
||||
if (try Archive.createAndParseFromPath(self.allocator, self.target.?.cpu.arch, full_path)) |archive| {
|
||||
try self.archives.append(self.allocator, archive);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (try Dylib.createAndParseFromPath(
|
||||
self.allocator,
|
||||
self.arch.?,
|
||||
self.target.?.cpu.arch,
|
||||
full_path,
|
||||
.{ .syslibroot = syslibroot },
|
||||
)) |dylibs| {
|
||||
@ -292,7 +280,7 @@ fn parseLibs(self: *Zld, libs: []const []const u8, syslibroot: ?[]const u8) !voi
|
||||
for (libs) |lib| {
|
||||
if (try Dylib.createAndParseFromPath(
|
||||
self.allocator,
|
||||
self.arch.?,
|
||||
self.target.?.cpu.arch,
|
||||
lib,
|
||||
.{ .syslibroot = syslibroot },
|
||||
)) |dylibs| {
|
||||
@ -301,7 +289,7 @@ fn parseLibs(self: *Zld, libs: []const []const u8, syslibroot: ?[]const u8) !voi
|
||||
continue;
|
||||
}
|
||||
|
||||
if (try Archive.createAndParseFromPath(self.allocator, self.arch.?, lib)) |archive| {
|
||||
if (try Archive.createAndParseFromPath(self.allocator, self.target.?.cpu.arch, lib)) |archive| {
|
||||
try self.archives.append(self.allocator, archive);
|
||||
continue;
|
||||
}
|
||||
@ -989,7 +977,7 @@ fn allocateTextSegment(self: *Zld) !void {
|
||||
const stub_helper = &seg.sections.items[self.stub_helper_section_index.?];
|
||||
stubs.size += nstubs * stubs.reserved2;
|
||||
|
||||
const stub_size: u4 = switch (self.arch.?) {
|
||||
const stub_size: u4 = switch (self.target.?.cpu.arch) {
|
||||
.x86_64 => 10,
|
||||
.aarch64 => 3 * @sizeOf(u32),
|
||||
else => unreachable,
|
||||
@ -1226,7 +1214,7 @@ fn writeStubHelperCommon(self: *Zld) !void {
|
||||
const data = &data_segment.sections.items[self.data_section_index.?];
|
||||
|
||||
self.stub_helper_stubs_start_off = blk: {
|
||||
switch (self.arch.?) {
|
||||
switch (self.target.?.cpu.arch) {
|
||||
.x86_64 => {
|
||||
const code_size = 15;
|
||||
var code: [code_size]u8 = undefined;
|
||||
@ -1358,7 +1346,7 @@ fn writeLazySymbolPointer(self: *Zld, index: u32) !void {
|
||||
const data_segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
|
||||
const la_symbol_ptr = data_segment.sections.items[self.la_symbol_ptr_section_index.?];
|
||||
|
||||
const stub_size: u4 = switch (self.arch.?) {
|
||||
const stub_size: u4 = switch (self.target.?.cpu.arch) {
|
||||
.x86_64 => 10,
|
||||
.aarch64 => 3 * @sizeOf(u32),
|
||||
else => unreachable,
|
||||
@ -1384,7 +1372,7 @@ fn writeStub(self: *Zld, index: u32) !void {
|
||||
log.debug("writing stub at 0x{x}", .{stub_off});
|
||||
var code = try self.allocator.alloc(u8, stubs.reserved2);
|
||||
defer self.allocator.free(code);
|
||||
switch (self.arch.?) {
|
||||
switch (self.target.?.cpu.arch) {
|
||||
.x86_64 => {
|
||||
assert(la_ptr_addr >= stub_addr + stubs.reserved2);
|
||||
const displacement = try math.cast(u32, la_ptr_addr - stub_addr - stubs.reserved2);
|
||||
@ -1447,7 +1435,7 @@ fn writeStubInStubHelper(self: *Zld, index: u32) !void {
|
||||
const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
|
||||
const stub_helper = text_segment.sections.items[self.stub_helper_section_index.?];
|
||||
|
||||
const stub_size: u4 = switch (self.arch.?) {
|
||||
const stub_size: u4 = switch (self.target.?.cpu.arch) {
|
||||
.x86_64 => 10,
|
||||
.aarch64 => 3 * @sizeOf(u32),
|
||||
else => unreachable,
|
||||
@ -1455,7 +1443,7 @@ fn writeStubInStubHelper(self: *Zld, index: u32) !void {
|
||||
const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size;
|
||||
var code = try self.allocator.alloc(u8, stub_size);
|
||||
defer self.allocator.free(code);
|
||||
switch (self.arch.?) {
|
||||
switch (self.target.?.cpu.arch) {
|
||||
.x86_64 => {
|
||||
const displacement = try math.cast(
|
||||
i32,
|
||||
@ -1999,7 +1987,7 @@ fn populateMetadata(self: *Zld) !void {
|
||||
if (self.text_section_index == null) {
|
||||
const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
|
||||
self.text_section_index = @intCast(u16, text_seg.sections.items.len);
|
||||
const alignment: u2 = switch (self.arch.?) {
|
||||
const alignment: u2 = switch (self.target.?.cpu.arch) {
|
||||
.x86_64 => 0,
|
||||
.aarch64 => 2,
|
||||
else => unreachable, // unhandled architecture type
|
||||
@ -2013,12 +2001,12 @@ fn populateMetadata(self: *Zld) !void {
|
||||
if (self.stubs_section_index == null) {
|
||||
const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
|
||||
self.stubs_section_index = @intCast(u16, text_seg.sections.items.len);
|
||||
const alignment: u2 = switch (self.arch.?) {
|
||||
const alignment: u2 = switch (self.target.?.cpu.arch) {
|
||||
.x86_64 => 0,
|
||||
.aarch64 => 2,
|
||||
else => unreachable, // unhandled architecture type
|
||||
};
|
||||
const stub_size: u4 = switch (self.arch.?) {
|
||||
const stub_size: u4 = switch (self.target.?.cpu.arch) {
|
||||
.x86_64 => 6,
|
||||
.aarch64 => 3 * @sizeOf(u32),
|
||||
else => unreachable, // unhandled architecture type
|
||||
@ -2033,12 +2021,12 @@ fn populateMetadata(self: *Zld) !void {
|
||||
if (self.stub_helper_section_index == null) {
|
||||
const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
|
||||
self.stub_helper_section_index = @intCast(u16, text_seg.sections.items.len);
|
||||
const alignment: u2 = switch (self.arch.?) {
|
||||
const alignment: u2 = switch (self.target.?.cpu.arch) {
|
||||
.x86_64 => 0,
|
||||
.aarch64 => 2,
|
||||
else => unreachable, // unhandled architecture type
|
||||
};
|
||||
const stub_helper_size: u6 = switch (self.arch.?) {
|
||||
const stub_helper_size: u6 = switch (self.target.?.cpu.arch) {
|
||||
.x86_64 => 15,
|
||||
.aarch64 => 6 * @sizeOf(u32),
|
||||
else => unreachable,
|
||||
@ -2187,7 +2175,7 @@ fn populateMetadata(self: *Zld) !void {
|
||||
try self.load_commands.append(self.allocator, .{ .Dylinker = dylinker_cmd });
|
||||
}
|
||||
|
||||
if (self.main_cmd_index == null) {
|
||||
if (self.main_cmd_index == null and self.output.?.tag == .exe) {
|
||||
self.main_cmd_index = @intCast(u16, self.load_commands.items.len);
|
||||
try self.load_commands.append(self.allocator, .{
|
||||
.Main = .{
|
||||
@ -2199,6 +2187,41 @@ fn populateMetadata(self: *Zld) !void {
|
||||
});
|
||||
}
|
||||
|
||||
if (self.dylib_id_cmd_index == null and self.output.?.tag == .dylib) {
|
||||
self.dylib_id_cmd_index = @intCast(u16, self.load_commands.items.len);
|
||||
var dylib_cmd = try createLoadDylibCommand(
|
||||
self.allocator,
|
||||
self.output.?.install_name.?,
|
||||
2,
|
||||
0x10000, // TODO forward user-provided versions
|
||||
0x10000,
|
||||
);
|
||||
errdefer dylib_cmd.deinit(self.allocator);
|
||||
dylib_cmd.inner.cmd = macho.LC_ID_DYLIB;
|
||||
try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
|
||||
}
|
||||
|
||||
if (self.version_min_cmd_index == null) {
|
||||
self.version_min_cmd_index = @intCast(u16, self.load_commands.items.len);
|
||||
const cmd: u32 = switch (self.target.?.os.tag) {
|
||||
.macos => macho.LC_VERSION_MIN_MACOSX,
|
||||
.ios => macho.LC_VERSION_MIN_IPHONEOS,
|
||||
.tvos => macho.LC_VERSION_MIN_TVOS,
|
||||
.watchos => macho.LC_VERSION_MIN_WATCHOS,
|
||||
else => unreachable, // wrong OS
|
||||
};
|
||||
const ver = self.target.?.os.version_range.semver.min;
|
||||
const version = ver.major << 16 | ver.minor << 8 | ver.patch;
|
||||
try self.load_commands.append(self.allocator, .{
|
||||
.VersionMin = .{
|
||||
.cmd = cmd,
|
||||
.cmdsize = @sizeOf(macho.version_min_command),
|
||||
.version = version,
|
||||
.sdk = version,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (self.source_version_cmd_index == null) {
|
||||
self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len);
|
||||
try self.load_commands.append(self.allocator, .{
|
||||
@ -2237,7 +2260,7 @@ fn addDataInCodeLC(self: *Zld) !void {
|
||||
}
|
||||
|
||||
fn addCodeSignatureLC(self: *Zld) !void {
|
||||
if (self.code_signature_cmd_index == null and self.arch.? == .aarch64) {
|
||||
if (self.code_signature_cmd_index == null and self.target.?.cpu.arch == .aarch64) {
|
||||
self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);
|
||||
try self.load_commands.append(self.allocator, .{
|
||||
.LinkeditData = .{
|
||||
@ -2355,19 +2378,20 @@ fn flush(self: *Zld) !void {
|
||||
seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size.?);
|
||||
}
|
||||
|
||||
if (self.arch.? == .aarch64) {
|
||||
if (self.target.?.cpu.arch == .aarch64) {
|
||||
try self.writeCodeSignaturePadding();
|
||||
}
|
||||
|
||||
try self.writeLoadCommands();
|
||||
try self.writeHeader();
|
||||
|
||||
if (self.arch.? == .aarch64) {
|
||||
if (self.target.?.cpu.arch == .aarch64) {
|
||||
try self.writeCodeSignature();
|
||||
}
|
||||
|
||||
if (comptime std.Target.current.isDarwin() and std.Target.current.cpu.arch == .aarch64) {
|
||||
try fs.cwd().copyFile(self.out_path.?, fs.cwd(), self.out_path.?, .{});
|
||||
const out_path = self.output.?.path;
|
||||
try fs.cwd().copyFile(out_path, fs.cwd(), out_path, .{});
|
||||
}
|
||||
}
|
||||
|
||||
@ -2392,6 +2416,8 @@ fn writeGotEntries(self: *Zld) !void {
|
||||
}
|
||||
|
||||
fn setEntryPoint(self: *Zld) !void {
|
||||
if (self.output.?.tag != .exe) return;
|
||||
|
||||
// TODO we should respect the -entry flag passed in by the user to set a custom
|
||||
// entrypoint. For now, assume default of `_main`.
|
||||
const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
|
||||
@ -2636,12 +2662,12 @@ fn populateLazyBindOffsetsInStubHelper(self: *Zld, buffer: []const u8) !void {
|
||||
}
|
||||
assert(self.stubs.items.len <= offsets.items.len);
|
||||
|
||||
const stub_size: u4 = switch (self.arch.?) {
|
||||
const stub_size: u4 = switch (self.target.?.cpu.arch) {
|
||||
.x86_64 => 10,
|
||||
.aarch64 => 3 * @sizeOf(u32),
|
||||
else => unreachable,
|
||||
};
|
||||
const off: u4 = switch (self.arch.?) {
|
||||
const off: u4 = switch (self.target.?.cpu.arch) {
|
||||
.x86_64 => 1,
|
||||
.aarch64 => 2 * @sizeOf(u32),
|
||||
else => unreachable,
|
||||
@ -2660,17 +2686,40 @@ fn writeExportInfo(self: *Zld) !void {
|
||||
defer trie.deinit();
|
||||
|
||||
const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
|
||||
const base_address = text_segment.inner.vmaddr;
|
||||
|
||||
// TODO export items for dylibs
|
||||
const sym = self.globals.get("_main") orelse return error.MissingMainEntrypoint;
|
||||
const reg = sym.cast(Symbol.Regular) orelse unreachable;
|
||||
assert(reg.address >= text_segment.inner.vmaddr);
|
||||
// TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
|
||||
log.debug("writing export trie", .{});
|
||||
|
||||
try trie.put(.{
|
||||
.name = sym.name,
|
||||
.vmaddr_offset = reg.address - text_segment.inner.vmaddr,
|
||||
.export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
|
||||
});
|
||||
const Sorter = struct {
|
||||
fn lessThan(_: void, a: []const u8, b: []const u8) bool {
|
||||
return mem.lessThan(u8, a, b);
|
||||
}
|
||||
};
|
||||
|
||||
var sorted_globals = std.ArrayList([]const u8).init(self.allocator);
|
||||
defer sorted_globals.deinit();
|
||||
|
||||
for (self.globals.values()) |sym| {
|
||||
const reg = sym.cast(Symbol.Regular) orelse continue;
|
||||
if (reg.linkage != .global) continue;
|
||||
try sorted_globals.append(sym.name);
|
||||
}
|
||||
|
||||
std.sort.sort([]const u8, sorted_globals.items, {}, Sorter.lessThan);
|
||||
|
||||
for (sorted_globals.items) |sym_name| {
|
||||
const sym = self.globals.get(sym_name) orelse unreachable;
|
||||
const reg = sym.cast(Symbol.Regular) orelse unreachable;
|
||||
|
||||
log.debug(" | putting '{s}' defined at 0x{x}", .{ reg.base.name, reg.address });
|
||||
|
||||
try trie.put(.{
|
||||
.name = sym.name,
|
||||
.vmaddr_offset = reg.address - base_address,
|
||||
.export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
|
||||
});
|
||||
}
|
||||
|
||||
try trie.finalize();
|
||||
|
||||
@ -2975,7 +3024,7 @@ fn writeStringTable(self: *Zld) !void {
|
||||
|
||||
try self.file.?.pwriteAll(self.strtab.items, symtab.stroff);
|
||||
|
||||
if (symtab.strsize > self.strtab.items.len and self.arch.? == .x86_64) {
|
||||
if (symtab.strsize > self.strtab.items.len and self.target.?.cpu.arch == .x86_64) {
|
||||
// This is the last section, so we need to pad it out.
|
||||
try self.file.?.pwriteAll(&[_]u8{0}, seg.inner.fileoff + seg.inner.filesize - 1);
|
||||
}
|
||||
@ -3023,7 +3072,7 @@ fn writeCodeSignaturePadding(self: *Zld) !void {
|
||||
const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData;
|
||||
const fileoff = seg.inner.fileoff + seg.inner.filesize;
|
||||
const needed_size = CodeSignature.calcCodeSignaturePaddingSize(
|
||||
self.out_path.?,
|
||||
self.output.?.path,
|
||||
fileoff,
|
||||
self.page_size.?,
|
||||
);
|
||||
@ -3049,7 +3098,7 @@ fn writeCodeSignature(self: *Zld) !void {
|
||||
defer code_sig.deinit();
|
||||
try code_sig.calcAdhocSignature(
|
||||
self.file.?,
|
||||
self.out_path.?,
|
||||
self.output.?.path,
|
||||
text_seg.inner,
|
||||
code_sig_cmd,
|
||||
.Exe,
|
||||
@ -3091,7 +3140,7 @@ fn writeHeader(self: *Zld) !void {
|
||||
cpu_subtype: macho.cpu_subtype_t,
|
||||
};
|
||||
|
||||
const cpu_info: CpuInfo = switch (self.arch.?) {
|
||||
const cpu_info: CpuInfo = switch (self.target.?.cpu.arch) {
|
||||
.aarch64 => .{
|
||||
.cpu_type = macho.CPU_TYPE_ARM64,
|
||||
.cpu_subtype = macho.CPU_SUBTYPE_ARM_ALL,
|
||||
@ -3104,8 +3153,22 @@ fn writeHeader(self: *Zld) !void {
|
||||
};
|
||||
header.cputype = cpu_info.cpu_type;
|
||||
header.cpusubtype = cpu_info.cpu_subtype;
|
||||
header.filetype = macho.MH_EXECUTE;
|
||||
header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;
|
||||
|
||||
switch (self.output.?.tag) {
|
||||
.exe => {
|
||||
header.filetype = macho.MH_EXECUTE;
|
||||
header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;
|
||||
},
|
||||
.dylib => {
|
||||
header.filetype = macho.MH_DYLIB;
|
||||
header.flags = macho.MH_NOUNDEFS |
|
||||
macho.MH_DYLDLINK |
|
||||
macho.MH_PIE |
|
||||
macho.MH_TWOLEVEL |
|
||||
macho.MH_NO_REEXPORTED_DYLIBS;
|
||||
},
|
||||
}
|
||||
|
||||
header.reserved = 0;
|
||||
|
||||
if (self.tlv_section_index) |_|
|
||||
|
||||
@ -226,7 +226,6 @@ pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
|
||||
{
|
||||
return punt_to_clang(arena, args);
|
||||
} else if (mem.eql(u8, cmd, "ld.lld") or
|
||||
mem.eql(u8, cmd, "ld64.lld") or
|
||||
mem.eql(u8, cmd, "lld-link") or
|
||||
mem.eql(u8, cmd, "wasm-ld"))
|
||||
{
|
||||
@ -3384,7 +3383,6 @@ fn punt_to_llvm_ar(arena: *Allocator, args: []const []const u8) error{OutOfMemor
|
||||
|
||||
/// The first argument determines which backend is invoked. The options are:
|
||||
/// * `ld.lld` - ELF
|
||||
/// * `ld64.lld` - Mach-O
|
||||
/// * `lld-link` - COFF
|
||||
/// * `wasm-ld` - WebAssembly
|
||||
/// TODO https://github.com/ziglang/zig/issues/3257
|
||||
@ -3402,8 +3400,6 @@ pub fn punt_to_lld(arena: *Allocator, args: []const []const u8) error{OutOfMemor
|
||||
const argc = @intCast(c_int, argv.len);
|
||||
if (mem.eql(u8, args[1], "ld.lld")) {
|
||||
break :rc llvm.LinkELF(argc, argv.ptr, true);
|
||||
} else if (mem.eql(u8, args[1], "ld64.lld")) {
|
||||
break :rc llvm.LinkMachO(argc, argv.ptr, true);
|
||||
} else if (mem.eql(u8, args[1], "lld-link")) {
|
||||
break :rc llvm.LinkCOFF(argc, argv.ptr, true);
|
||||
} else if (mem.eql(u8, args[1], "wasm-ld")) {
|
||||
|
||||
@ -1187,11 +1187,6 @@ int ZigLLDLinkELF(int argc, const char **argv, bool can_exit_early) {
|
||||
return lld::elf::link(args, can_exit_early, llvm::outs(), llvm::errs());
|
||||
}
|
||||
|
||||
int ZigLLDLinkMachO(int argc, const char **argv, bool can_exit_early) {
|
||||
std::vector<const char *> args(argv, argv + argc);
|
||||
return lld::mach_o::link(args, can_exit_early, llvm::outs(), llvm::errs());
|
||||
}
|
||||
|
||||
int ZigLLDLinkWasm(int argc, const char **argv, bool can_exit_early) {
|
||||
std::vector<const char *> args(argv, argv + argc);
|
||||
return lld::wasm::link(args, can_exit_early, llvm::outs(), llvm::errs());
|
||||
|
||||
@ -514,7 +514,6 @@ ZIG_EXTERN_C const char *ZigLLVMGetEnvironmentTypeName(enum ZigLLVM_EnvironmentT
|
||||
|
||||
ZIG_EXTERN_C int ZigLLDLinkCOFF(int argc, const char **argv, bool can_exit_early);
|
||||
ZIG_EXTERN_C int ZigLLDLinkELF(int argc, const char **argv, bool can_exit_early);
|
||||
ZIG_EXTERN_C int ZigLLDLinkMachO(int argc, const char **argv, bool can_exit_early);
|
||||
ZIG_EXTERN_C int ZigLLDLinkWasm(int argc, const char **argv, bool can_exit_early);
|
||||
|
||||
ZIG_EXTERN_C bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size_t file_name_count,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user