mirror of
https://github.com/ziglang/zig.git
synced 2026-01-03 03:53:20 +00:00
commit
15278b7f4b
@ -444,6 +444,7 @@ pub const InitOptions = struct {
|
||||
want_valgrind: ?bool = null,
|
||||
want_tsan: ?bool = null,
|
||||
want_compiler_rt: ?bool = null,
|
||||
want_lto: ?bool = null,
|
||||
use_llvm: ?bool = null,
|
||||
use_lld: ?bool = null,
|
||||
use_clang: ?bool = null,
|
||||
@ -602,6 +603,12 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
|
||||
if (ofmt == .c)
|
||||
break :blk false;
|
||||
|
||||
if (options.want_lto) |lto| {
|
||||
if (lto) {
|
||||
break :blk true;
|
||||
}
|
||||
}
|
||||
|
||||
// Our linker can't handle objects or most advanced options yet.
|
||||
if (options.link_objects.len != 0 or
|
||||
options.c_source_files.len != 0 or
|
||||
@ -647,6 +654,26 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
|
||||
break :outer opts;
|
||||
} else .{};
|
||||
|
||||
const lto = blk: {
|
||||
if (options.want_lto) |explicit| {
|
||||
if (!use_lld)
|
||||
return error.LtoUnavailableWithoutLld;
|
||||
break :blk explicit;
|
||||
} else if (!use_lld) {
|
||||
break :blk false;
|
||||
} else if (options.c_source_files.len == 0) {
|
||||
break :blk false;
|
||||
} else if (darwin_options.system_linker_hack) {
|
||||
break :blk false;
|
||||
} else switch (options.output_mode) {
|
||||
.Lib, .Obj => break :blk false,
|
||||
.Exe => switch (options.optimize_mode) {
|
||||
.Debug => break :blk false,
|
||||
.ReleaseSafe, .ReleaseFast, .ReleaseSmall => break :blk true,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
const tsan = options.want_tsan orelse false;
|
||||
|
||||
const link_libc = options.link_libc or target_util.osRequiresLibC(options.target) or tsan;
|
||||
@ -821,6 +848,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
|
||||
cache.hash.add(ofmt);
|
||||
cache.hash.add(pic);
|
||||
cache.hash.add(pie);
|
||||
cache.hash.add(lto);
|
||||
cache.hash.add(tsan);
|
||||
cache.hash.add(stack_check);
|
||||
cache.hash.add(red_zone);
|
||||
@ -1022,6 +1050,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
|
||||
.libc_installation = libc_dirs.libc_installation,
|
||||
.pic = pic,
|
||||
.pie = pie,
|
||||
.lto = lto,
|
||||
.valgrind = valgrind,
|
||||
.tsan = tsan,
|
||||
.stack_check = stack_check,
|
||||
@ -2233,6 +2262,9 @@ pub fn addCCArgs(
|
||||
"-nostdinc",
|
||||
"-fno-spell-checking",
|
||||
});
|
||||
if (comp.bin_file.options.lto) {
|
||||
try argv.append("-flto");
|
||||
}
|
||||
|
||||
// According to Rich Felker libc headers are supposed to go before C language headers.
|
||||
// However as noted by @dimenus, appending libc headers before c_headers breaks intrinsics
|
||||
@ -3255,6 +3287,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
|
||||
.err_color = @enumToInt(comp.color),
|
||||
.pic = comp.bin_file.options.pic,
|
||||
.pie = comp.bin_file.options.pie,
|
||||
.lto = comp.bin_file.options.lto,
|
||||
.link_libc = comp.bin_file.options.link_libc,
|
||||
.link_libcpp = comp.bin_file.options.link_libcpp,
|
||||
.strip = comp.bin_file.options.strip,
|
||||
@ -3415,6 +3448,10 @@ pub fn build_crt_file(
|
||||
.want_tsan = false,
|
||||
.want_pic = comp.bin_file.options.pic,
|
||||
.want_pie = comp.bin_file.options.pie,
|
||||
.want_lto = switch (output_mode) {
|
||||
.Lib => comp.bin_file.options.lto,
|
||||
.Obj, .Exe => false,
|
||||
},
|
||||
.emit_h = null,
|
||||
.strip = comp.compilerRtStrip(),
|
||||
.is_native_os = comp.bin_file.options.is_native_os,
|
||||
|
||||
@ -2732,7 +2732,14 @@ flagpd1("fkeep-static-consts"),
|
||||
flagpd1("flat_namespace"),
|
||||
flagpd1("flax-vector-conversions"),
|
||||
flagpd1("flimit-debug-info"),
|
||||
flagpd1("flto"),
|
||||
.{
|
||||
.name = "flto",
|
||||
.syntax = .flag,
|
||||
.zig_equivalent = .lto,
|
||||
.pd1 = true,
|
||||
.pd2 = false,
|
||||
.psl = false,
|
||||
},
|
||||
flagpd1("flto-unit"),
|
||||
flagpd1("flto-visibility-public-std"),
|
||||
sepd1("fmacro-backtrace-limit"),
|
||||
@ -2942,7 +2949,14 @@ flagpd1("fno-jump-tables"),
|
||||
flagpd1("fno-keep-static-consts"),
|
||||
flagpd1("fno-lax-vector-conversions"),
|
||||
flagpd1("fno-limit-debug-info"),
|
||||
flagpd1("fno-lto"),
|
||||
.{
|
||||
.name = "fno-lto",
|
||||
.syntax = .flag,
|
||||
.zig_equivalent = .no_lto,
|
||||
.pd1 = true,
|
||||
.pd2 = false,
|
||||
.psl = false,
|
||||
},
|
||||
flagpd1("fno-lto-unit"),
|
||||
flagpd1("fno-math-builtin"),
|
||||
flagpd1("fno-math-errno"),
|
||||
@ -5638,7 +5652,14 @@ jspd1("Ttext"),
|
||||
.pd2 = true,
|
||||
.psl = false,
|
||||
},
|
||||
joinpd1("flto="),
|
||||
.{
|
||||
.name = "flto=",
|
||||
.syntax = .joined,
|
||||
.zig_equivalent = .lto,
|
||||
.pd1 = true,
|
||||
.pd2 = false,
|
||||
.psl = false,
|
||||
},
|
||||
joinpd1("gcoff"),
|
||||
joinpd1("mabi="),
|
||||
joinpd1("mabs="),
|
||||
|
||||
@ -243,7 +243,13 @@ pub const TargetMachine = opaque {
|
||||
extern fn LLVMDisposeTargetMachine(T: *const TargetMachine) void;
|
||||
|
||||
pub const emitToFile = LLVMTargetMachineEmitToFile;
|
||||
extern fn LLVMTargetMachineEmitToFile(*const TargetMachine, M: *const Module, Filename: [*:0]const u8, codegen: CodeGenFileType, ErrorMessage: *[*:0]const u8) LLVMBool;
|
||||
extern fn LLVMTargetMachineEmitToFile(
|
||||
*const TargetMachine,
|
||||
M: *const Module,
|
||||
Filename: [*:0]const u8,
|
||||
codegen: CodeGenFileType,
|
||||
ErrorMessage: *[*:0]const u8,
|
||||
) LLVMBool;
|
||||
};
|
||||
|
||||
pub const CodeMode = extern enum {
|
||||
|
||||
@ -74,6 +74,7 @@ pub const Options = struct {
|
||||
is_native_abi: bool,
|
||||
pic: bool,
|
||||
pie: bool,
|
||||
lto: bool,
|
||||
valgrind: bool,
|
||||
tsan: bool,
|
||||
stack_check: bool,
|
||||
|
||||
@ -945,6 +945,13 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
|
||||
if (!self.base.options.strip) {
|
||||
try argv.append("-DEBUG");
|
||||
}
|
||||
if (self.base.options.lto) {
|
||||
switch (self.base.options.optimize_mode) {
|
||||
.Debug => {},
|
||||
.ReleaseSmall => try argv.append("-OPT:lldlto=2"),
|
||||
.ReleaseFast, .ReleaseSafe => try argv.append("-OPT:lldlto=3"),
|
||||
}
|
||||
}
|
||||
if (self.base.options.output_mode == .Exe) {
|
||||
const stack_size = self.base.options.stack_size_override orelse 16777216;
|
||||
try argv.append(try allocPrint(arena, "-STACK:{d}", .{stack_size}));
|
||||
|
||||
708
src/link/Elf.zig
708
src/link/Elf.zig
@ -1384,351 +1384,385 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
|
||||
};
|
||||
}
|
||||
|
||||
// Create an LLD command line and invoke it.
|
||||
var argv = std.ArrayList([]const u8).init(self.base.allocator);
|
||||
defer argv.deinit();
|
||||
// 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.?, "ld.lld" });
|
||||
if (is_obj) {
|
||||
try argv.append("-r");
|
||||
}
|
||||
|
||||
try argv.append("-error-limit=0");
|
||||
|
||||
if (self.base.options.output_mode == .Exe) {
|
||||
try argv.append("-z");
|
||||
try argv.append(try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size}));
|
||||
}
|
||||
|
||||
if (self.base.options.image_base_override) |image_base| {
|
||||
try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{image_base}));
|
||||
}
|
||||
|
||||
if (self.base.options.linker_script) |linker_script| {
|
||||
try argv.append("-T");
|
||||
try argv.append(linker_script);
|
||||
}
|
||||
|
||||
if (gc_sections) {
|
||||
try argv.append("--gc-sections");
|
||||
}
|
||||
|
||||
if (self.base.options.eh_frame_hdr) {
|
||||
try argv.append("--eh-frame-hdr");
|
||||
}
|
||||
|
||||
if (self.base.options.emit_relocs) {
|
||||
try argv.append("--emit-relocs");
|
||||
}
|
||||
|
||||
if (self.base.options.rdynamic) {
|
||||
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 (getLDMOption(target)) |ldm| {
|
||||
// Any target ELF will use the freebsd osabi if suffixed with "_fbsd".
|
||||
const arg = if (target.os.tag == .freebsd)
|
||||
try std.fmt.allocPrint(arena, "{s}_fbsd", .{ldm})
|
||||
else
|
||||
ldm;
|
||||
try argv.append("-m");
|
||||
try argv.append(arg);
|
||||
}
|
||||
|
||||
if (self.base.options.link_mode == .Static) {
|
||||
if (target.cpu.arch.isARM() or target.cpu.arch.isThumb()) {
|
||||
try argv.append("-Bstatic");
|
||||
} else {
|
||||
try argv.append("-static");
|
||||
}
|
||||
} else if (is_dyn_lib) {
|
||||
try argv.append("-shared");
|
||||
}
|
||||
|
||||
if (self.base.options.pie and self.base.options.output_mode == .Exe) {
|
||||
try argv.append("-pie");
|
||||
}
|
||||
|
||||
const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
|
||||
try argv.append("-o");
|
||||
try argv.append(full_out_path);
|
||||
if (self.base.options.output_mode == .Obj and self.base.options.lto) {
|
||||
// In this case we must do a simple file copy
|
||||
// here. TODO: think carefully about how we can avoid this redundant operation when doing
|
||||
// build-obj. See also the corresponding TODO in linkAsArchive.
|
||||
const the_object_path = blk: {
|
||||
if (self.base.options.objects.len != 0)
|
||||
break :blk self.base.options.objects[0];
|
||||
|
||||
if (link_in_crt) {
|
||||
const crt1o: []const u8 = o: {
|
||||
if (target.os.tag == .netbsd) {
|
||||
break :o "crt0.o";
|
||||
} else if (target.os.tag == .openbsd) {
|
||||
if (self.base.options.link_mode == .Static) {
|
||||
break :o "rcrt0.o";
|
||||
} else {
|
||||
break :o "crt0.o";
|
||||
}
|
||||
} else if (target.isAndroid()) {
|
||||
if (self.base.options.link_mode == .Dynamic) {
|
||||
break :o "crtbegin_dynamic.o";
|
||||
} else {
|
||||
break :o "crtbegin_static.o";
|
||||
}
|
||||
} else if (self.base.options.link_mode == .Static) {
|
||||
if (self.base.options.pie) {
|
||||
break :o "rcrt1.o";
|
||||
} else {
|
||||
break :o "crt1.o";
|
||||
}
|
||||
} else {
|
||||
break :o "Scrt1.o";
|
||||
}
|
||||
if (comp.c_object_table.count() != 0)
|
||||
break :blk comp.c_object_table.items()[0].key.status.success.object_path;
|
||||
|
||||
if (module_obj_path) |p|
|
||||
break :blk p;
|
||||
|
||||
// TODO I think this is unreachable. Audit this situation when solving the above TODO
|
||||
// regarding eliding redundant object -> object transformations.
|
||||
return error.NoObjectsToLink;
|
||||
};
|
||||
try argv.append(try comp.get_libc_crt_file(arena, crt1o));
|
||||
if (target_util.libc_needs_crti_crtn(target)) {
|
||||
try argv.append(try comp.get_libc_crt_file(arena, "crti.o"));
|
||||
}
|
||||
if (target.os.tag == .openbsd) {
|
||||
try argv.append(try comp.get_libc_crt_file(arena, "crtbegin.o"));
|
||||
}
|
||||
}
|
||||
|
||||
// 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 (self.base.options.each_lib_rpath) {
|
||||
var test_path = std.ArrayList(u8).init(self.base.allocator);
|
||||
defer test_path.deinit();
|
||||
for (self.base.options.lib_dirs) |lib_dir_path| {
|
||||
for (self.base.options.system_libs.items()) |entry| {
|
||||
const link_lib = entry.key;
|
||||
test_path.shrinkRetainingCapacity(0);
|
||||
const sep = fs.path.sep_str;
|
||||
try test_path.writer().print("{s}" ++ sep ++ "lib{s}.so", .{ lib_dir_path, link_lib });
|
||||
fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
|
||||
error.FileNotFound => continue,
|
||||
else => |e| return e,
|
||||
};
|
||||
if ((try rpath_table.fetchPut(lib_dir_path, {})) == null) {
|
||||
try argv.append("-rpath");
|
||||
try argv.append(lib_dir_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (self.base.options.lib_dirs) |lib_dir| {
|
||||
try argv.append("-L");
|
||||
try argv.append(lib_dir);
|
||||
}
|
||||
|
||||
if (self.base.options.link_libc) {
|
||||
if (self.base.options.libc_installation) |libc_installation| {
|
||||
try argv.append("-L");
|
||||
try argv.append(libc_installation.crt_dir.?);
|
||||
}
|
||||
|
||||
if (have_dynamic_linker) {
|
||||
if (self.base.options.dynamic_linker) |dynamic_linker| {
|
||||
try argv.append("-dynamic-linker");
|
||||
try argv.append(dynamic_linker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_dyn_lib) {
|
||||
if (self.base.options.soname) |soname| {
|
||||
try argv.append("-soname");
|
||||
try argv.append(soname);
|
||||
}
|
||||
if (self.base.options.version_script) |version_script| {
|
||||
try argv.append("-version-script");
|
||||
try argv.append(version_script);
|
||||
}
|
||||
}
|
||||
|
||||
// Positional arguments to the linker such as object files.
|
||||
try argv.appendSlice(self.base.options.objects);
|
||||
|
||||
for (comp.c_object_table.items()) |entry| {
|
||||
try argv.append(entry.key.status.success.object_path);
|
||||
}
|
||||
|
||||
if (module_obj_path) |p| {
|
||||
try argv.append(p);
|
||||
}
|
||||
|
||||
// TSAN
|
||||
if (self.base.options.tsan) {
|
||||
try argv.append(comp.tsan_static_lib.?.full_object_path);
|
||||
}
|
||||
|
||||
// libc
|
||||
// TODO: enable when stage2 can build c.zig
|
||||
if (is_exe_or_dyn_lib and
|
||||
!self.base.options.skip_linker_dependencies and
|
||||
!self.base.options.link_libc and
|
||||
build_options.is_stage1)
|
||||
{
|
||||
try argv.append(comp.libc_static_lib.?.full_object_path);
|
||||
}
|
||||
|
||||
// compiler-rt
|
||||
if (compiler_rt_path) |p| {
|
||||
try argv.append(p);
|
||||
}
|
||||
|
||||
// Shared libraries.
|
||||
if (is_exe_or_dyn_lib) {
|
||||
const system_libs = self.base.options.system_libs.items();
|
||||
try argv.ensureCapacity(argv.items.len + system_libs.len);
|
||||
for (system_libs) |entry| {
|
||||
const link_lib = entry.key;
|
||||
// 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 .so 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);
|
||||
}
|
||||
|
||||
// libc dep
|
||||
if (self.base.options.link_libc) {
|
||||
if (self.base.options.libc_installation != null) {
|
||||
if (self.base.options.link_mode == .Static) {
|
||||
try argv.append("--start-group");
|
||||
try argv.append("-lc");
|
||||
try argv.append("-lm");
|
||||
try argv.append("--end-group");
|
||||
} else {
|
||||
try argv.append("-lc");
|
||||
try argv.append("-lm");
|
||||
}
|
||||
|
||||
if (target.os.tag == .freebsd or target.os.tag == .netbsd or target.os.tag == .openbsd) {
|
||||
try argv.append("-lpthread");
|
||||
}
|
||||
} else if (target.isGnuLibC()) {
|
||||
try argv.append(comp.libunwind_static_lib.?.full_object_path);
|
||||
for (glibc.libs) |lib| {
|
||||
const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{
|
||||
comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
|
||||
});
|
||||
try argv.append(lib_path);
|
||||
}
|
||||
try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a"));
|
||||
} else if (target.isMusl()) {
|
||||
try argv.append(comp.libunwind_static_lib.?.full_object_path);
|
||||
try argv.append(try comp.get_libc_crt_file(arena, switch (self.base.options.link_mode) {
|
||||
.Static => "libc.a",
|
||||
.Dynamic => "libc.so",
|
||||
}));
|
||||
} else if (self.base.options.link_libcpp) {
|
||||
try argv.append(comp.libunwind_static_lib.?.full_object_path);
|
||||
} else {
|
||||
unreachable; // Compiler was supposed to emit an error for not being able to provide libc.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// crt end
|
||||
if (link_in_crt) {
|
||||
if (target.isAndroid()) {
|
||||
try argv.append(try comp.get_libc_crt_file(arena, "crtend_android.o"));
|
||||
} else if (target.os.tag == .openbsd) {
|
||||
try argv.append(try comp.get_libc_crt_file(arena, "crtend.o"));
|
||||
} else if (target_util.libc_needs_crti_crtn(target)) {
|
||||
try argv.append(try comp.get_libc_crt_file(arena, "crtn.o"));
|
||||
}
|
||||
}
|
||||
|
||||
if (allow_shlib_undefined) {
|
||||
try argv.append("--allow-shlib-undefined");
|
||||
}
|
||||
|
||||
if (self.base.options.bind_global_refs_locally) {
|
||||
try argv.append("-Bsymbolic");
|
||||
}
|
||||
|
||||
if (self.base.options.verbose_link) {
|
||||
// Skip over our own name so that the LLD linker name is the first argv item.
|
||||
Compilation.dump_argv(argv.items[1..]);
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
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 => std.process.abort(),
|
||||
// This can happen when using --enable-cache and using the stage1 backend. In this case
|
||||
// we can skip the file copy.
|
||||
if (!mem.eql(u8, the_object_path, full_out_path)) {
|
||||
try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
|
||||
}
|
||||
} else {
|
||||
child.stdin_behavior = .Ignore;
|
||||
child.stdout_behavior = .Ignore;
|
||||
child.stderr_behavior = .Pipe;
|
||||
|
||||
try child.spawn();
|
||||
|
||||
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;
|
||||
},
|
||||
// Create an LLD command line and invoke it.
|
||||
var argv = std.ArrayList([]const u8).init(self.base.allocator);
|
||||
defer argv.deinit();
|
||||
// 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.?, "ld.lld" });
|
||||
if (is_obj) {
|
||||
try argv.append("-r");
|
||||
}
|
||||
|
||||
if (stderr.len != 0) {
|
||||
log.warn("unexpected LLD stderr:\n{s}", .{stderr});
|
||||
try argv.append("-error-limit=0");
|
||||
|
||||
if (self.base.options.lto) {
|
||||
switch (self.base.options.optimize_mode) {
|
||||
.Debug => {},
|
||||
.ReleaseSmall => try argv.append("-O2"),
|
||||
.ReleaseFast, .ReleaseSafe => try argv.append("-O3"),
|
||||
}
|
||||
}
|
||||
|
||||
if (self.base.options.output_mode == .Exe) {
|
||||
try argv.append("-z");
|
||||
try argv.append(try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size}));
|
||||
}
|
||||
|
||||
if (self.base.options.image_base_override) |image_base| {
|
||||
try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{image_base}));
|
||||
}
|
||||
|
||||
if (self.base.options.linker_script) |linker_script| {
|
||||
try argv.append("-T");
|
||||
try argv.append(linker_script);
|
||||
}
|
||||
|
||||
if (gc_sections) {
|
||||
try argv.append("--gc-sections");
|
||||
}
|
||||
|
||||
if (self.base.options.eh_frame_hdr) {
|
||||
try argv.append("--eh-frame-hdr");
|
||||
}
|
||||
|
||||
if (self.base.options.emit_relocs) {
|
||||
try argv.append("--emit-relocs");
|
||||
}
|
||||
|
||||
if (self.base.options.rdynamic) {
|
||||
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 (getLDMOption(target)) |ldm| {
|
||||
// Any target ELF will use the freebsd osabi if suffixed with "_fbsd".
|
||||
const arg = if (target.os.tag == .freebsd)
|
||||
try std.fmt.allocPrint(arena, "{s}_fbsd", .{ldm})
|
||||
else
|
||||
ldm;
|
||||
try argv.append("-m");
|
||||
try argv.append(arg);
|
||||
}
|
||||
|
||||
if (self.base.options.link_mode == .Static) {
|
||||
if (target.cpu.arch.isARM() or target.cpu.arch.isThumb()) {
|
||||
try argv.append("-Bstatic");
|
||||
} else {
|
||||
try argv.append("-static");
|
||||
}
|
||||
} else if (is_dyn_lib) {
|
||||
try argv.append("-shared");
|
||||
}
|
||||
|
||||
if (self.base.options.pie and self.base.options.output_mode == .Exe) {
|
||||
try argv.append("-pie");
|
||||
}
|
||||
|
||||
try argv.append("-o");
|
||||
try argv.append(full_out_path);
|
||||
|
||||
if (link_in_crt) {
|
||||
const crt1o: []const u8 = o: {
|
||||
if (target.os.tag == .netbsd) {
|
||||
break :o "crt0.o";
|
||||
} else if (target.os.tag == .openbsd) {
|
||||
if (self.base.options.link_mode == .Static) {
|
||||
break :o "rcrt0.o";
|
||||
} else {
|
||||
break :o "crt0.o";
|
||||
}
|
||||
} else if (target.isAndroid()) {
|
||||
if (self.base.options.link_mode == .Dynamic) {
|
||||
break :o "crtbegin_dynamic.o";
|
||||
} else {
|
||||
break :o "crtbegin_static.o";
|
||||
}
|
||||
} else if (self.base.options.link_mode == .Static) {
|
||||
if (self.base.options.pie) {
|
||||
break :o "rcrt1.o";
|
||||
} else {
|
||||
break :o "crt1.o";
|
||||
}
|
||||
} else {
|
||||
break :o "Scrt1.o";
|
||||
}
|
||||
};
|
||||
try argv.append(try comp.get_libc_crt_file(arena, crt1o));
|
||||
if (target_util.libc_needs_crti_crtn(target)) {
|
||||
try argv.append(try comp.get_libc_crt_file(arena, "crti.o"));
|
||||
}
|
||||
if (target.os.tag == .openbsd) {
|
||||
try argv.append(try comp.get_libc_crt_file(arena, "crtbegin.o"));
|
||||
}
|
||||
}
|
||||
|
||||
// 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 (self.base.options.each_lib_rpath) {
|
||||
var test_path = std.ArrayList(u8).init(self.base.allocator);
|
||||
defer test_path.deinit();
|
||||
for (self.base.options.lib_dirs) |lib_dir_path| {
|
||||
for (self.base.options.system_libs.items()) |entry| {
|
||||
const link_lib = entry.key;
|
||||
test_path.shrinkRetainingCapacity(0);
|
||||
const sep = fs.path.sep_str;
|
||||
try test_path.writer().print("{s}" ++ sep ++ "lib{s}.so", .{ lib_dir_path, link_lib });
|
||||
fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
|
||||
error.FileNotFound => continue,
|
||||
else => |e| return e,
|
||||
};
|
||||
if ((try rpath_table.fetchPut(lib_dir_path, {})) == null) {
|
||||
try argv.append("-rpath");
|
||||
try argv.append(lib_dir_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (self.base.options.lib_dirs) |lib_dir| {
|
||||
try argv.append("-L");
|
||||
try argv.append(lib_dir);
|
||||
}
|
||||
|
||||
if (self.base.options.link_libc) {
|
||||
if (self.base.options.libc_installation) |libc_installation| {
|
||||
try argv.append("-L");
|
||||
try argv.append(libc_installation.crt_dir.?);
|
||||
}
|
||||
|
||||
if (have_dynamic_linker) {
|
||||
if (self.base.options.dynamic_linker) |dynamic_linker| {
|
||||
try argv.append("-dynamic-linker");
|
||||
try argv.append(dynamic_linker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_dyn_lib) {
|
||||
if (self.base.options.soname) |soname| {
|
||||
try argv.append("-soname");
|
||||
try argv.append(soname);
|
||||
}
|
||||
if (self.base.options.version_script) |version_script| {
|
||||
try argv.append("-version-script");
|
||||
try argv.append(version_script);
|
||||
}
|
||||
}
|
||||
|
||||
// Positional arguments to the linker such as object files.
|
||||
try argv.appendSlice(self.base.options.objects);
|
||||
|
||||
for (comp.c_object_table.items()) |entry| {
|
||||
try argv.append(entry.key.status.success.object_path);
|
||||
}
|
||||
|
||||
if (module_obj_path) |p| {
|
||||
try argv.append(p);
|
||||
}
|
||||
|
||||
// TSAN
|
||||
if (self.base.options.tsan) {
|
||||
try argv.append(comp.tsan_static_lib.?.full_object_path);
|
||||
}
|
||||
|
||||
// libc
|
||||
// TODO: enable when stage2 can build c.zig
|
||||
if (is_exe_or_dyn_lib and
|
||||
!self.base.options.skip_linker_dependencies and
|
||||
!self.base.options.link_libc and
|
||||
build_options.is_stage1)
|
||||
{
|
||||
try argv.append(comp.libc_static_lib.?.full_object_path);
|
||||
}
|
||||
|
||||
// compiler-rt
|
||||
if (compiler_rt_path) |p| {
|
||||
try argv.append(p);
|
||||
}
|
||||
|
||||
// Shared libraries.
|
||||
if (is_exe_or_dyn_lib) {
|
||||
const system_libs = self.base.options.system_libs.items();
|
||||
try argv.ensureCapacity(argv.items.len + system_libs.len);
|
||||
for (system_libs) |entry| {
|
||||
const link_lib = entry.key;
|
||||
// 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 .so 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);
|
||||
}
|
||||
|
||||
// libc dep
|
||||
if (self.base.options.link_libc) {
|
||||
if (self.base.options.libc_installation != null) {
|
||||
if (self.base.options.link_mode == .Static) {
|
||||
try argv.append("--start-group");
|
||||
try argv.append("-lc");
|
||||
try argv.append("-lm");
|
||||
try argv.append("--end-group");
|
||||
} else {
|
||||
try argv.append("-lc");
|
||||
try argv.append("-lm");
|
||||
}
|
||||
|
||||
if (target.os.tag == .freebsd or target.os.tag == .netbsd or target.os.tag == .openbsd) {
|
||||
try argv.append("-lpthread");
|
||||
}
|
||||
} else if (target.isGnuLibC()) {
|
||||
try argv.append(comp.libunwind_static_lib.?.full_object_path);
|
||||
for (glibc.libs) |lib| {
|
||||
const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{
|
||||
comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
|
||||
});
|
||||
try argv.append(lib_path);
|
||||
}
|
||||
try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a"));
|
||||
} else if (target.isMusl()) {
|
||||
try argv.append(comp.libunwind_static_lib.?.full_object_path);
|
||||
try argv.append(try comp.get_libc_crt_file(arena, switch (self.base.options.link_mode) {
|
||||
.Static => "libc.a",
|
||||
.Dynamic => "libc.so",
|
||||
}));
|
||||
} else if (self.base.options.link_libcpp) {
|
||||
try argv.append(comp.libunwind_static_lib.?.full_object_path);
|
||||
} else {
|
||||
unreachable; // Compiler was supposed to emit an error for not being able to provide libc.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// crt end
|
||||
if (link_in_crt) {
|
||||
if (target.isAndroid()) {
|
||||
try argv.append(try comp.get_libc_crt_file(arena, "crtend_android.o"));
|
||||
} else if (target.os.tag == .openbsd) {
|
||||
try argv.append(try comp.get_libc_crt_file(arena, "crtend.o"));
|
||||
} else if (target_util.libc_needs_crti_crtn(target)) {
|
||||
try argv.append(try comp.get_libc_crt_file(arena, "crtn.o"));
|
||||
}
|
||||
}
|
||||
|
||||
if (allow_shlib_undefined) {
|
||||
try argv.append("--allow-shlib-undefined");
|
||||
}
|
||||
|
||||
if (self.base.options.bind_global_refs_locally) {
|
||||
try argv.append("-Bsymbolic");
|
||||
}
|
||||
|
||||
if (self.base.options.verbose_link) {
|
||||
// Skip over our own name so that the LLD linker name is the first argv item.
|
||||
Compilation.dump_argv(argv.items[1..]);
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
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 => std.process.abort(),
|
||||
}
|
||||
} else {
|
||||
child.stdin_behavior = .Ignore;
|
||||
child.stdout_behavior = .Ignore;
|
||||
child.stderr_behavior = .Pipe;
|
||||
|
||||
try child.spawn();
|
||||
|
||||
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});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -620,6 +620,13 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
|
||||
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) {
|
||||
|
||||
@ -362,122 +362,157 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
|
||||
};
|
||||
}
|
||||
|
||||
const is_obj = self.base.options.output_mode == .Obj;
|
||||
const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
|
||||
|
||||
// Create an LLD command line and invoke it.
|
||||
var argv = std.ArrayList([]const u8).init(self.base.allocator);
|
||||
defer argv.deinit();
|
||||
// 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.?, "wasm-ld" });
|
||||
if (is_obj) {
|
||||
try argv.append("-r");
|
||||
}
|
||||
if (self.base.options.output_mode == .Obj) {
|
||||
// LLD's WASM driver does not support the equvialent of `-r` so we do a simple file copy
|
||||
// here. TODO: think carefully about how we can avoid this redundant operation when doing
|
||||
// build-obj. See also the corresponding TODO in linkAsArchive.
|
||||
const the_object_path = blk: {
|
||||
if (self.base.options.objects.len != 0)
|
||||
break :blk self.base.options.objects[0];
|
||||
|
||||
try argv.append("-error-limit=0");
|
||||
if (comp.c_object_table.count() != 0)
|
||||
break :blk comp.c_object_table.items()[0].key.status.success.object_path;
|
||||
|
||||
if (self.base.options.output_mode == .Exe) {
|
||||
// Increase the default stack size to a more reasonable value of 1MB instead of
|
||||
// the default of 1 Wasm page being 64KB, unless overriden by the user.
|
||||
try argv.append("-z");
|
||||
const stack_size = self.base.options.stack_size_override orelse 1048576;
|
||||
const arg = try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size});
|
||||
try argv.append(arg);
|
||||
if (module_obj_path) |p|
|
||||
break :blk p;
|
||||
|
||||
// Put stack before globals so that stack overflow results in segfault immediately
|
||||
// before corrupting globals. See https://github.com/ziglang/zig/issues/4496
|
||||
try argv.append("--stack-first");
|
||||
} else {
|
||||
try argv.append("--no-entry"); // So lld doesn't look for _start.
|
||||
try argv.append("--export-all");
|
||||
}
|
||||
try argv.appendSlice(&[_][]const u8{
|
||||
"--allow-undefined",
|
||||
"-o",
|
||||
try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}),
|
||||
});
|
||||
|
||||
// Positional arguments to the linker such as object files.
|
||||
try argv.appendSlice(self.base.options.objects);
|
||||
|
||||
for (comp.c_object_table.items()) |entry| {
|
||||
try argv.append(entry.key.status.success.object_path);
|
||||
}
|
||||
if (module_obj_path) |p| {
|
||||
try argv.append(p);
|
||||
}
|
||||
|
||||
if (self.base.options.output_mode != .Obj and
|
||||
!self.base.options.skip_linker_dependencies and
|
||||
!self.base.options.link_libc)
|
||||
{
|
||||
try argv.append(comp.libc_static_lib.?.full_object_path);
|
||||
}
|
||||
|
||||
if (compiler_rt_path) |p| {
|
||||
try argv.append(p);
|
||||
}
|
||||
|
||||
if (self.base.options.verbose_link) {
|
||||
// Skip over our own name so that the LLD linker name is the first argv item.
|
||||
Compilation.dump_argv(argv.items[1..]);
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
const term = child.spawnAndWait() catch |err| {
|
||||
log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
|
||||
return error.UnableToSpawnSelf;
|
||||
// TODO I think this is unreachable. Audit this situation when solving the above TODO
|
||||
// regarding eliding redundant object -> object transformations.
|
||||
return error.NoObjectsToLink;
|
||||
};
|
||||
switch (term) {
|
||||
.Exited => |code| {
|
||||
if (code != 0) {
|
||||
// TODO https://github.com/ziglang/zig/issues/6342
|
||||
std.process.exit(1);
|
||||
}
|
||||
},
|
||||
else => std.process.abort(),
|
||||
// This can happen when using --enable-cache and using the stage1 backend. In this case
|
||||
// we can skip the file copy.
|
||||
if (!mem.eql(u8, the_object_path, full_out_path)) {
|
||||
try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
|
||||
}
|
||||
} else {
|
||||
child.stdin_behavior = .Ignore;
|
||||
child.stdout_behavior = .Ignore;
|
||||
child.stderr_behavior = .Pipe;
|
||||
const is_obj = self.base.options.output_mode == .Obj;
|
||||
|
||||
try child.spawn();
|
||||
|
||||
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;
|
||||
},
|
||||
// Create an LLD command line and invoke it.
|
||||
var argv = std.ArrayList([]const u8).init(self.base.allocator);
|
||||
defer argv.deinit();
|
||||
// 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.?, "wasm-ld" });
|
||||
if (is_obj) {
|
||||
try argv.append("-r");
|
||||
}
|
||||
|
||||
if (stderr.len != 0) {
|
||||
log.warn("unexpected LLD stderr:\n{s}", .{stderr});
|
||||
try argv.append("-error-limit=0");
|
||||
|
||||
if (self.base.options.lto) {
|
||||
switch (self.base.options.optimize_mode) {
|
||||
.Debug => {},
|
||||
.ReleaseSmall => try argv.append("-O2"),
|
||||
.ReleaseFast, .ReleaseSafe => try argv.append("-O3"),
|
||||
}
|
||||
}
|
||||
|
||||
if (self.base.options.output_mode == .Exe) {
|
||||
// Increase the default stack size to a more reasonable value of 1MB instead of
|
||||
// the default of 1 Wasm page being 64KB, unless overriden by the user.
|
||||
try argv.append("-z");
|
||||
const stack_size = self.base.options.stack_size_override orelse 1048576;
|
||||
const arg = try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size});
|
||||
try argv.append(arg);
|
||||
|
||||
// Put stack before globals so that stack overflow results in segfault immediately
|
||||
// before corrupting globals. See https://github.com/ziglang/zig/issues/4496
|
||||
try argv.append("--stack-first");
|
||||
} else {
|
||||
try argv.append("--no-entry"); // So lld doesn't look for _start.
|
||||
try argv.append("--export-all");
|
||||
}
|
||||
try argv.appendSlice(&[_][]const u8{
|
||||
"--allow-undefined",
|
||||
"-o",
|
||||
full_out_path,
|
||||
});
|
||||
|
||||
// Positional arguments to the linker such as object files.
|
||||
try argv.appendSlice(self.base.options.objects);
|
||||
|
||||
for (comp.c_object_table.items()) |entry| {
|
||||
try argv.append(entry.key.status.success.object_path);
|
||||
}
|
||||
if (module_obj_path) |p| {
|
||||
try argv.append(p);
|
||||
}
|
||||
|
||||
if (self.base.options.output_mode != .Obj and
|
||||
!self.base.options.skip_linker_dependencies and
|
||||
!self.base.options.link_libc)
|
||||
{
|
||||
try argv.append(comp.libc_static_lib.?.full_object_path);
|
||||
}
|
||||
|
||||
if (compiler_rt_path) |p| {
|
||||
try argv.append(p);
|
||||
}
|
||||
|
||||
if (self.base.options.verbose_link) {
|
||||
// Skip over our own name so that the LLD linker name is the first argv item.
|
||||
Compilation.dump_argv(argv.items[1..]);
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
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 => std.process.abort(),
|
||||
}
|
||||
} else {
|
||||
child.stdin_behavior = .Ignore;
|
||||
child.stdout_behavior = .Ignore;
|
||||
child.stderr_behavior = .Pipe;
|
||||
|
||||
try child.spawn();
|
||||
|
||||
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});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
12
src/main.zig
12
src/main.zig
@ -287,6 +287,8 @@ const usage_build_generic =
|
||||
\\ -fno-PIC Force-disable Position Independent Code
|
||||
\\ -fPIE Force-enable Position Independent Executable
|
||||
\\ -fno-PIE Force-disable Position Independent Executable
|
||||
\\ -flto Force-enable Link Time Optimization (requires LLVM extensions)
|
||||
\\ -fno-lto Force-disable Link Time Optimization
|
||||
\\ -fstack-check Enable stack probing in unsafe builds
|
||||
\\ -fno-stack-check Disable stack probing in safe builds
|
||||
\\ -fsanitize-c Enable C undefined behavior detection in unsafe builds
|
||||
@ -511,6 +513,7 @@ fn buildOutputType(
|
||||
var enable_cache: ?bool = null;
|
||||
var want_pic: ?bool = null;
|
||||
var want_pie: ?bool = null;
|
||||
var want_lto: ?bool = null;
|
||||
var want_sanitize_c: ?bool = null;
|
||||
var want_stack_check: ?bool = null;
|
||||
var want_red_zone: ?bool = null;
|
||||
@ -852,6 +855,10 @@ fn buildOutputType(
|
||||
want_pie = true;
|
||||
} else if (mem.eql(u8, arg, "-fno-PIE")) {
|
||||
want_pie = false;
|
||||
} else if (mem.eql(u8, arg, "-flto")) {
|
||||
want_lto = true;
|
||||
} else if (mem.eql(u8, arg, "-fno-lto")) {
|
||||
want_lto = false;
|
||||
} else if (mem.eql(u8, arg, "-fstack-check")) {
|
||||
want_stack_check = true;
|
||||
} else if (mem.eql(u8, arg, "-fno-stack-check")) {
|
||||
@ -1085,6 +1092,8 @@ fn buildOutputType(
|
||||
.no_pic => want_pic = false,
|
||||
.pie => want_pie = true,
|
||||
.no_pie => want_pie = false,
|
||||
.lto => want_lto = true,
|
||||
.no_lto => want_lto = false,
|
||||
.red_zone => want_red_zone = true,
|
||||
.no_red_zone => want_red_zone = false,
|
||||
.nostdlib => ensure_libc_on_non_freestanding = false,
|
||||
@ -1771,6 +1780,7 @@ fn buildOutputType(
|
||||
.link_libcpp = link_libcpp,
|
||||
.want_pic = want_pic,
|
||||
.want_pie = want_pie,
|
||||
.want_lto = want_lto,
|
||||
.want_sanitize_c = want_sanitize_c,
|
||||
.want_stack_check = want_stack_check,
|
||||
.want_red_zone = want_red_zone,
|
||||
@ -2952,6 +2962,8 @@ pub const ClangArgIterator = struct {
|
||||
no_pic,
|
||||
pie,
|
||||
no_pie,
|
||||
lto,
|
||||
no_lto,
|
||||
nostdlib,
|
||||
nostdlib_cpp,
|
||||
shared,
|
||||
|
||||
@ -707,6 +707,7 @@ const mingwex_generic_src = [_][]const u8{
|
||||
"math" ++ path.sep_str ++ "fpclassifyf.c",
|
||||
"math" ++ path.sep_str ++ "fpclassifyl.c",
|
||||
"math" ++ path.sep_str ++ "frexpf.c",
|
||||
"math" ++ path.sep_str ++ "frexpl.c",
|
||||
"math" ++ path.sep_str ++ "hypot.c",
|
||||
"math" ++ path.sep_str ++ "hypotf.c",
|
||||
"math" ++ path.sep_str ++ "hypotl.c",
|
||||
|
||||
@ -109,6 +109,7 @@ pub const Module = extern struct {
|
||||
err_color: ErrColor,
|
||||
pic: bool,
|
||||
pie: bool,
|
||||
lto: bool,
|
||||
link_libc: bool,
|
||||
link_libcpp: bool,
|
||||
strip: bool,
|
||||
|
||||
@ -2192,6 +2192,7 @@ struct CodeGen {
|
||||
bool is_single_threaded;
|
||||
bool have_pic;
|
||||
bool have_pie;
|
||||
bool have_lto;
|
||||
bool link_mode_dynamic;
|
||||
bool dll_export_fns;
|
||||
bool have_stack_probing;
|
||||
|
||||
@ -8449,8 +8449,9 @@ static void zig_llvm_emit_output(CodeGen *g) {
|
||||
// Unfortunately, LLVM shits the bed when we ask for both binary and assembly. So we call the entire
|
||||
// pipeline multiple times if this is requested.
|
||||
if (asm_filename != nullptr && bin_filename != nullptr) {
|
||||
if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg, g->build_mode == BuildModeDebug,
|
||||
is_small, g->enable_time_report, g->tsan_enabled, nullptr, bin_filename, llvm_ir_filename))
|
||||
if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg,
|
||||
g->build_mode == BuildModeDebug, is_small, g->enable_time_report, g->tsan_enabled,
|
||||
g->have_lto, nullptr, bin_filename, llvm_ir_filename))
|
||||
{
|
||||
fprintf(stderr, "LLVM failed to emit file: %s\n", err_msg);
|
||||
exit(1);
|
||||
@ -8459,8 +8460,9 @@ static void zig_llvm_emit_output(CodeGen *g) {
|
||||
llvm_ir_filename = nullptr;
|
||||
}
|
||||
|
||||
if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg, g->build_mode == BuildModeDebug,
|
||||
is_small, g->enable_time_report, g->tsan_enabled, asm_filename, bin_filename, llvm_ir_filename))
|
||||
if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg,
|
||||
g->build_mode == BuildModeDebug, is_small, g->enable_time_report, g->tsan_enabled,
|
||||
g->have_lto, asm_filename, bin_filename, llvm_ir_filename))
|
||||
{
|
||||
fprintf(stderr, "LLVM failed to emit file: %s\n", err_msg);
|
||||
exit(1);
|
||||
|
||||
@ -90,6 +90,7 @@ void zig_stage1_build_object(struct ZigStage1 *stage1) {
|
||||
g->dll_export_fns = stage1->dll_export_fns;
|
||||
g->have_pic = stage1->pic;
|
||||
g->have_pie = stage1->pie;
|
||||
g->have_lto = stage1->lto;
|
||||
g->have_stack_probing = stage1->enable_stack_probing;
|
||||
g->red_zone = stage1->red_zone;
|
||||
g->is_single_threaded = stage1->is_single_threaded;
|
||||
|
||||
@ -178,6 +178,7 @@ struct ZigStage1 {
|
||||
|
||||
bool pic;
|
||||
bool pie;
|
||||
bool lto;
|
||||
bool link_libc;
|
||||
bool link_libcpp;
|
||||
bool strip;
|
||||
|
||||
@ -354,8 +354,6 @@ pub fn hasRedZone(target: std.Target) bool {
|
||||
return switch (target.cpu.arch) {
|
||||
.x86_64,
|
||||
.i386,
|
||||
.wasm32,
|
||||
.wasm64,
|
||||
.powerpc,
|
||||
.powerpc64,
|
||||
.powerpc64le,
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
|
||||
#include <llvm/Analysis/TargetLibraryInfo.h>
|
||||
#include <llvm/Analysis/TargetTransformInfo.h>
|
||||
#include <llvm/Bitcode/BitcodeWriter.h>
|
||||
#include <llvm/IR/DIBuilder.h>
|
||||
#include <llvm/IR/DiagnosticInfo.h>
|
||||
#include <llvm/IR/IRBuilder.h>
|
||||
@ -184,7 +185,7 @@ unsigned ZigLLVMDataLayoutGetProgramAddressSpace(LLVMTargetDataRef TD) {
|
||||
|
||||
bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
|
||||
char **error_message, bool is_debug,
|
||||
bool is_small, bool time_report, bool tsan,
|
||||
bool is_small, bool time_report, bool tsan, bool lto,
|
||||
const char *asm_filename, const char *bin_filename, const char *llvm_ir_filename)
|
||||
{
|
||||
TimePassesIsEnabled = time_report;
|
||||
@ -234,7 +235,7 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
|
||||
PMBuilder->VerifyInput = assertions_on;
|
||||
PMBuilder->VerifyOutput = assertions_on;
|
||||
PMBuilder->MergeFunctions = !is_debug;
|
||||
PMBuilder->PrepareForLTO = false;
|
||||
PMBuilder->PrepareForLTO = lto;
|
||||
PMBuilder->PrepareForThinLTO = false;
|
||||
PMBuilder->PerformThinLTO = false;
|
||||
|
||||
@ -272,7 +273,7 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
|
||||
PMBuilder->populateModulePassManager(MPM);
|
||||
|
||||
// Set output passes.
|
||||
if (dest_bin) {
|
||||
if (dest_bin && !lto) {
|
||||
if (target_machine->addPassesToEmitFile(MPM, *dest_bin, nullptr, CGFT_ObjectFile)) {
|
||||
*error_message = strdup("TargetMachine can't emit an object file");
|
||||
return true;
|
||||
@ -299,6 +300,9 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (dest_bin && lto) {
|
||||
WriteBitcodeToFile(*module, *dest_bin);
|
||||
}
|
||||
|
||||
if (time_report) {
|
||||
TimerGroup::printAll(errs());
|
||||
|
||||
@ -48,7 +48,7 @@ ZIG_EXTERN_C char *ZigLLVMGetNativeFeatures(void);
|
||||
|
||||
ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
|
||||
char **error_message, bool is_debug,
|
||||
bool is_small, bool time_report, bool tsan,
|
||||
bool is_small, bool time_report, bool tsan, bool lto,
|
||||
const char *asm_filename, const char *bin_filename, const char *llvm_ir_filename);
|
||||
|
||||
|
||||
|
||||
@ -62,6 +62,14 @@ const known_options = [_]KnownOpt{
|
||||
.name = "fno-PIE",
|
||||
.ident = "no_pie",
|
||||
},
|
||||
.{
|
||||
.name = "flto",
|
||||
.ident = "lto",
|
||||
},
|
||||
.{
|
||||
.name = "fno-lto",
|
||||
.ident = "no_lto",
|
||||
},
|
||||
.{
|
||||
.name = "nolibc",
|
||||
.ident = "nostdlib",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user