aboutsummaryrefslogtreecommitdiff
path: root/src/link/Elf.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/link/Elf.zig')
-rw-r--r--src/link/Elf.zig828
1 files changed, 13 insertions, 815 deletions
diff --git a/src/link/Elf.zig b/src/link/Elf.zig
index 1516993c74..0beea0d9e7 100644
--- a/src/link/Elf.zig
+++ b/src/link/Elf.zig
@@ -4,7 +4,6 @@ base: link.File,
zig_object: ?*ZigObject,
rpath_table: std.StringArrayHashMapUnmanaged(void),
image_base: u64,
-emit_relocs: bool,
z_nodelete: bool,
z_notext: bool,
z_defs: bool,
@@ -16,25 +15,11 @@ z_relro: bool,
z_common_page_size: ?u64,
/// TODO make this non optional and resolve the default in open()
z_max_page_size: ?u64,
-hash_style: HashStyle,
-compress_debug_sections: CompressDebugSections,
-symbol_wrap_set: std.StringArrayHashMapUnmanaged(void),
-sort_section: ?SortSection,
soname: ?[]const u8,
-bind_global_refs_locally: bool,
-linker_script: ?[]const u8,
-version_script: ?[]const u8,
-allow_undefined_version: bool,
-enable_new_dtags: ?bool,
-print_icf_sections: bool,
-print_map: bool,
entry_name: ?[]const u8,
ptr_width: PtrWidth,
-/// If this is not null, an object file is created by LLVM and emitted to zcu_object_sub_path.
-llvm_object: ?LlvmObject.Ptr = null,
-
/// A list of all input files.
/// First index is a special "null file". Order is otherwise not observed.
files: std.MultiArrayList(File.Entry) = .{},
@@ -204,9 +189,6 @@ const minimum_atom_size = 64;
pub const min_text_capacity = padToIdeal(minimum_atom_size);
pub const PtrWidth = enum { p32, p64 };
-pub const HashStyle = enum { sysv, gnu, both };
-pub const CompressDebugSections = enum { none, zlib, zstd };
-pub const SortSection = enum { name, alignment };
pub fn createEmpty(
arena: Allocator,
@@ -217,7 +199,6 @@ pub fn createEmpty(
const target = comp.root_mod.resolved_target.result;
assert(target.ofmt == .elf);
- const use_lld = build_options.have_llvm and comp.config.use_lld;
const use_llvm = comp.config.use_llvm;
const opt_zcu = comp.zcu;
const output_mode = comp.config.output_mode;
@@ -268,16 +249,6 @@ pub fn createEmpty(
const is_dyn_lib = output_mode == .Lib and link_mode == .dynamic;
const default_sym_version: elf.Versym = if (is_dyn_lib or comp.config.rdynamic) .GLOBAL else .LOCAL;
- // If using LLD to link, this code should produce an object file so that it
- // can be passed to LLD.
- // If using LLVM to generate the object file for the zig compilation unit,
- // we need a place to put the object file so that it can be subsequently
- // handled.
- const zcu_object_sub_path = if (!use_lld and !use_llvm)
- null
- else
- try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path});
-
var rpath_table: std.StringArrayHashMapUnmanaged(void) = .empty;
try rpath_table.entries.resize(arena, options.rpath_list.len);
@memcpy(rpath_table.entries.items(.key), options.rpath_list);
@@ -289,13 +260,15 @@ pub fn createEmpty(
.tag = .elf,
.comp = comp,
.emit = emit,
- .zcu_object_sub_path = zcu_object_sub_path,
+ .zcu_object_basename = if (use_llvm)
+ try std.fmt.allocPrint(arena, "{s}_zcu.o", .{fs.path.stem(emit.sub_path)})
+ else
+ null,
.gc_sections = options.gc_sections orelse (optimize_mode != .Debug and output_mode != .Obj),
.print_gc_sections = options.print_gc_sections,
.stack_size = options.stack_size orelse 16777216,
.allow_shlib_undefined = options.allow_shlib_undefined orelse !is_native_os,
.file = null,
- .disable_lld_caching = options.disable_lld_caching,
.build_id = options.build_id,
},
.zig_object = null,
@@ -320,7 +293,6 @@ pub fn createEmpty(
};
},
- .emit_relocs = options.emit_relocs,
.z_nodelete = options.z_nodelete,
.z_notext = options.z_notext,
.z_defs = options.z_defs,
@@ -330,30 +302,11 @@ pub fn createEmpty(
.z_relro = options.z_relro,
.z_common_page_size = options.z_common_page_size,
.z_max_page_size = options.z_max_page_size,
- .hash_style = options.hash_style,
- .compress_debug_sections = options.compress_debug_sections,
- .symbol_wrap_set = options.symbol_wrap_set,
- .sort_section = options.sort_section,
.soname = options.soname,
- .bind_global_refs_locally = options.bind_global_refs_locally,
- .linker_script = options.linker_script,
- .version_script = options.version_script,
- .allow_undefined_version = options.allow_undefined_version,
- .enable_new_dtags = options.enable_new_dtags,
- .print_icf_sections = options.print_icf_sections,
- .print_map = options.print_map,
.dump_argv_list = .empty,
};
- if (use_llvm and comp.config.have_zcu) {
- self.llvm_object = try LlvmObject.create(arena, comp);
- }
errdefer self.base.destroy();
- if (use_lld and (use_llvm or !comp.config.have_zcu)) {
- // LLVM emits the object file (if any); LLD links it into the final product.
- return self;
- }
-
// --verbose-link
if (comp.verbose_link) try dumpArgvInit(self, arena);
@@ -361,13 +314,11 @@ pub fn createEmpty(
const is_obj_or_ar = is_obj or (output_mode == .Lib and link_mode == .static);
// What path should this ELF linker code output to?
- // If using LLD to link, this code should produce an object file so that it
- // can be passed to LLD.
- const sub_path = if (use_lld) zcu_object_sub_path.? else emit.sub_path;
+ const sub_path = emit.sub_path;
self.base.file = try emit.root_dir.handle.createFile(sub_path, .{
.truncate = true,
.read = true,
- .mode = link.File.determineMode(use_lld, output_mode, link_mode),
+ .mode = link.File.determineMode(output_mode, link_mode),
});
const gpa = comp.gpa;
@@ -457,8 +408,6 @@ pub fn open(
pub fn deinit(self: *Elf) void {
const gpa = self.base.comp.gpa;
- if (self.llvm_object) |llvm_object| llvm_object.deinit();
-
for (self.file_handles.items) |fh| {
fh.close();
}
@@ -515,7 +464,6 @@ pub fn deinit(self: *Elf) void {
}
pub fn getNavVAddr(self: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, reloc_info: link.File.RelocInfo) !u64 {
- assert(self.llvm_object == null);
return self.zigObjectPtr().?.getNavVAddr(self, pt, nav_index, reloc_info);
}
@@ -530,7 +478,6 @@ pub fn lowerUav(
}
pub fn getUavVAddr(self: *Elf, uav: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 {
- assert(self.llvm_object == null);
return self.zigObjectPtr().?.getUavVAddr(self, uav, reloc_info);
}
@@ -795,60 +742,36 @@ pub fn loadInput(self: *Elf, input: link.Input) !void {
}
pub fn flush(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
- const comp = self.base.comp;
- const use_lld = build_options.have_llvm and comp.config.use_lld;
- const diags = &comp.link_diags;
- if (use_lld) {
- return self.linkWithLLD(arena, tid, prog_node) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- error.LinkFailure => return error.LinkFailure,
- else => |e| return diags.fail("failed to link with LLD: {s}", .{@errorName(e)}),
- };
- }
- try self.flushModule(arena, tid, prog_node);
-}
-
-pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
const tracy = trace(@src());
defer tracy.end();
const comp = self.base.comp;
const diags = &comp.link_diags;
- if (self.llvm_object) |llvm_object| {
- try self.base.emitLlvmObject(arena, llvm_object, prog_node);
- const use_lld = build_options.have_llvm and comp.config.use_lld;
- if (use_lld) return;
- }
-
if (comp.verbose_link) Compilation.dump_argv(self.dump_argv_list.items);
const sub_prog_node = prog_node.start("ELF Flush", 0);
defer sub_prog_node.end();
- return flushModuleInner(self, arena, tid) catch |err| switch (err) {
+ return flushInner(self, arena, tid) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
error.LinkFailure => return error.LinkFailure,
else => |e| return diags.fail("ELF flush failed: {s}", .{@errorName(e)}),
};
}
-fn flushModuleInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void {
+fn flushInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void {
const comp = self.base.comp;
const gpa = comp.gpa;
const diags = &comp.link_diags;
- const module_obj_path: ?Path = if (self.base.zcu_object_sub_path) |path| .{
- .root_dir = self.base.emit.root_dir,
- .sub_path = if (fs.path.dirname(self.base.emit.sub_path)) |dirname|
- try fs.path.join(arena, &.{ dirname, path })
- else
- path,
+ const zcu_obj_path: ?Path = if (self.base.zcu_object_basename) |raw| p: {
+ break :p try comp.resolveEmitPathFlush(arena, .temp, raw);
} else null;
if (self.zigObjectPtr()) |zig_object| try zig_object.flush(self, tid);
- if (module_obj_path) |path| openParseObjectReportingFailure(self, path);
+ if (zcu_obj_path) |path| openParseObjectReportingFailure(self, path);
switch (comp.config.output_mode) {
.Obj => return relocatable.flushObject(self, comp),
@@ -1508,639 +1431,6 @@ pub fn initOutputSection(self: *Elf, args: struct {
return out_shndx;
}
-fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void {
- dev.check(.lld_linker);
-
- const tracy = trace(@src());
- defer tracy.end();
-
- const comp = self.base.comp;
- const gpa = comp.gpa;
- const diags = &comp.link_diags;
-
- const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type.
- const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path});
-
- // If there is no Zig code to compile, then we should skip flushing the output file because it
- // will not be part of the linker line anyway.
- const module_obj_path: ?[]const u8 = if (comp.zcu != null) blk: {
- try self.flushModule(arena, tid, prog_node);
-
- if (fs.path.dirname(full_out_path)) |dirname| {
- break :blk try fs.path.join(arena, &.{ dirname, self.base.zcu_object_sub_path.? });
- } else {
- break :blk self.base.zcu_object_sub_path.?;
- }
- } else null;
-
- const sub_prog_node = prog_node.start("LLD Link", 0);
- defer sub_prog_node.end();
-
- const output_mode = comp.config.output_mode;
- const is_obj = output_mode == .Obj;
- const is_lib = output_mode == .Lib;
- const link_mode = comp.config.link_mode;
- const is_dyn_lib = link_mode == .dynamic and is_lib;
- const is_exe_or_dyn_lib = is_dyn_lib or output_mode == .Exe;
- const have_dynamic_linker = link_mode == .dynamic and is_exe_or_dyn_lib;
- const target = self.getTarget();
- const compiler_rt_path: ?Path = blk: {
- if (comp.compiler_rt_lib) |x| break :blk x.full_object_path;
- if (comp.compiler_rt_obj) |x| break :blk x.full_object_path;
- break :blk null;
- };
- const ubsan_rt_path: ?Path = blk: {
- if (comp.ubsan_rt_lib) |x| break :blk x.full_object_path;
- if (comp.ubsan_rt_obj) |x| break :blk x.full_object_path;
- break :blk null;
- };
-
- // Here we want to determine whether we can save time by not invoking LLD when the
- // output is unchanged. None of the linker options or the object files that are being
- // linked are in the hash that namespaces the directory we are outputting to. Therefore,
- // we must hash those now, and the resulting digest will form the "id" of the linking
- // job we are about to perform.
- // After a successful link, we store the id in the metadata of a symlink named "lld.id" in
- // the artifact directory. So, now, we check if this symlink exists, and if it matches
- // our digest. If so, we can skip linking. Otherwise, we proceed with invoking LLD.
- const id_symlink_basename = "lld.id";
-
- var man: std.Build.Cache.Manifest = undefined;
- defer if (!self.base.disable_lld_caching) man.deinit();
-
- var digest: [std.Build.Cache.hex_digest_len]u8 = undefined;
-
- if (!self.base.disable_lld_caching) {
- man = comp.cache_parent.obtain();
-
- // We are about to obtain this lock, so here we give other processes a chance first.
- self.base.releaseLock();
-
- comptime assert(Compilation.link_hash_implementation_version == 14);
-
- try man.addOptionalFile(self.linker_script);
- try man.addOptionalFile(self.version_script);
- man.hash.add(self.allow_undefined_version);
- man.hash.addOptional(self.enable_new_dtags);
- try link.hashInputs(&man, comp.link_inputs);
- for (comp.c_object_table.keys()) |key| {
- _ = try man.addFilePath(key.status.success.object_path, null);
- }
- try man.addOptionalFile(module_obj_path);
- try man.addOptionalFilePath(compiler_rt_path);
- try man.addOptionalFilePath(ubsan_rt_path);
- try man.addOptionalFilePath(if (comp.tsan_lib) |l| l.full_object_path else null);
- try man.addOptionalFilePath(if (comp.fuzzer_lib) |l| l.full_object_path else null);
-
- // We can skip hashing libc and libc++ components that we are in charge of building from Zig
- // installation sources because they are always a product of the compiler version + target information.
- man.hash.addOptionalBytes(self.entry_name);
- man.hash.add(self.image_base);
- man.hash.add(self.base.gc_sections);
- man.hash.addOptional(self.sort_section);
- man.hash.add(comp.link_eh_frame_hdr);
- man.hash.add(self.emit_relocs);
- man.hash.add(comp.config.rdynamic);
- man.hash.addListOfBytes(self.rpath_table.keys());
- if (output_mode == .Exe) {
- man.hash.add(self.base.stack_size);
- }
- man.hash.add(self.base.build_id);
- man.hash.addListOfBytes(self.symbol_wrap_set.keys());
- man.hash.add(comp.skip_linker_dependencies);
- man.hash.add(self.z_nodelete);
- man.hash.add(self.z_notext);
- man.hash.add(self.z_defs);
- man.hash.add(self.z_origin);
- man.hash.add(self.z_nocopyreloc);
- man.hash.add(self.z_now);
- man.hash.add(self.z_relro);
- man.hash.add(self.z_common_page_size orelse 0);
- man.hash.add(self.z_max_page_size orelse 0);
- man.hash.add(self.hash_style);
- // strip does not need to go into the linker hash because it is part of the hash namespace
- if (comp.config.link_libc) {
- man.hash.add(comp.libc_installation != null);
- if (comp.libc_installation) |libc_installation| {
- man.hash.addBytes(libc_installation.crt_dir.?);
- }
- }
- if (have_dynamic_linker) {
- man.hash.addOptionalBytes(target.dynamic_linker.get());
- }
- man.hash.addOptionalBytes(self.soname);
- man.hash.addOptional(comp.version);
- man.hash.addListOfBytes(comp.force_undefined_symbols.keys());
- man.hash.add(self.base.allow_shlib_undefined);
- man.hash.add(self.bind_global_refs_locally);
- man.hash.add(self.compress_debug_sections);
- man.hash.add(comp.config.any_sanitize_thread);
- man.hash.add(comp.config.any_fuzz);
- man.hash.addOptionalBytes(comp.sysroot);
-
- // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
- _ = try man.hit();
- digest = man.final();
-
- var prev_digest_buf: [digest.len]u8 = undefined;
- const prev_digest: []u8 = std.Build.Cache.readSmallFile(
- directory.handle,
- id_symlink_basename,
- &prev_digest_buf,
- ) catch |err| blk: {
- log.debug("ELF LLD 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("ELF LLD 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("ELF LLD 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) {
- error.FileNotFound => {},
- else => |e| return e,
- };
- }
-
- // Due to a deficiency in LLD, we need to special-case BPF to a simple file
- // copy when generating relocatables. Normally, we would expect `lld -r` to work.
- // However, because LLD wants to resolve BPF relocations which it shouldn't, it fails
- // before even generating the relocatable.
- //
- // For m68k, we go through this path because LLD doesn't support it yet, but LLVM can
- // produce usable object files.
- if (output_mode == .Obj and
- (comp.config.lto != .none or
- target.cpu.arch.isBpf() or
- target.cpu.arch == .lanai or
- target.cpu.arch == .m68k or
- target.cpu.arch.isSPARC() or
- target.cpu.arch == .ve or
- target.cpu.arch == .xcore))
- {
- // 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 (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path;
-
- if (comp.c_object_table.count() != 0)
- break :blk comp.c_object_table.keys()[0].status.success.object_path;
-
- if (module_obj_path) |p|
- break :blk Path.initCwd(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 std.fs.Dir.copyFile(
- the_object_path.root_dir.handle,
- the_object_path.sub_path,
- directory.handle,
- self.base.emit.sub_path,
- .{},
- );
- } else {
- // Create an LLD command line and invoke it.
- var argv = std.ArrayList([]const u8).init(gpa);
- 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.
- const linker_command = "ld.lld";
- try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command });
- if (is_obj) {
- try argv.append("-r");
- }
-
- try argv.append("--error-limit=0");
-
- if (comp.sysroot) |sysroot| {
- try argv.append(try std.fmt.allocPrint(arena, "--sysroot={s}", .{sysroot}));
- }
-
- if (target_util.llvmMachineAbi(target)) |mabi| {
- try argv.appendSlice(&.{
- "-mllvm",
- try std.fmt.allocPrint(arena, "-target-abi={s}", .{mabi}),
- });
- }
-
- try argv.appendSlice(&.{
- "-mllvm",
- try std.fmt.allocPrint(arena, "-float-abi={s}", .{if (target.abi.float() == .hard) "hard" else "soft"}),
- });
-
- if (comp.config.lto != .none) {
- switch (comp.root_mod.optimize_mode) {
- .Debug => {},
- .ReleaseSmall => try argv.append("--lto-O2"),
- .ReleaseFast, .ReleaseSafe => try argv.append("--lto-O3"),
- }
- }
- switch (comp.root_mod.optimize_mode) {
- .Debug => {},
- .ReleaseSmall => try argv.append("-O2"),
- .ReleaseFast, .ReleaseSafe => try argv.append("-O3"),
- }
-
- if (self.entry_name) |name| {
- try argv.appendSlice(&.{ "--entry", name });
- }
-
- for (comp.force_undefined_symbols.keys()) |sym| {
- try argv.append("-u");
- try argv.append(sym);
- }
-
- switch (self.hash_style) {
- .gnu => try argv.append("--hash-style=gnu"),
- .sysv => try argv.append("--hash-style=sysv"),
- .both => {}, // this is the default
- }
-
- if (output_mode == .Exe) {
- try argv.appendSlice(&.{
- "-z",
- try std.fmt.allocPrint(arena, "stack-size={d}", .{self.base.stack_size}),
- });
- }
-
- switch (self.base.build_id) {
- .none => try argv.append("--build-id=none"),
- .fast, .uuid, .sha1, .md5 => try argv.append(try std.fmt.allocPrint(arena, "--build-id={s}", .{
- @tagName(self.base.build_id),
- })),
- .hexstring => |hs| try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{s}", .{
- std.fmt.fmtSliceHexLower(hs.toSlice()),
- })),
- }
-
- try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{self.image_base}));
-
- if (self.linker_script) |linker_script| {
- try argv.append("-T");
- try argv.append(linker_script);
- }
-
- if (self.sort_section) |how| {
- const arg = try std.fmt.allocPrint(arena, "--sort-section={s}", .{@tagName(how)});
- try argv.append(arg);
- }
-
- if (self.base.gc_sections) {
- try argv.append("--gc-sections");
- }
-
- if (self.base.print_gc_sections) {
- try argv.append("--print-gc-sections");
- }
-
- if (self.print_icf_sections) {
- try argv.append("--print-icf-sections");
- }
-
- if (self.print_map) {
- try argv.append("--print-map");
- }
-
- if (comp.link_eh_frame_hdr) {
- try argv.append("--eh-frame-hdr");
- }
-
- if (self.emit_relocs) {
- try argv.append("--emit-relocs");
- }
-
- if (comp.config.rdynamic) {
- try argv.append("--export-dynamic");
- }
-
- if (comp.config.debug_format == .strip) {
- try argv.append("-s");
- }
-
- if (self.z_nodelete) {
- try argv.append("-z");
- try argv.append("nodelete");
- }
- if (self.z_notext) {
- try argv.append("-z");
- try argv.append("notext");
- }
- if (self.z_defs) {
- try argv.append("-z");
- try argv.append("defs");
- }
- if (self.z_origin) {
- try argv.append("-z");
- try argv.append("origin");
- }
- if (self.z_nocopyreloc) {
- try argv.append("-z");
- try argv.append("nocopyreloc");
- }
- if (self.z_now) {
- // LLD defaults to -zlazy
- try argv.append("-znow");
- }
- if (!self.z_relro) {
- // LLD defaults to -zrelro
- try argv.append("-znorelro");
- }
- if (self.z_common_page_size) |size| {
- try argv.append("-z");
- try argv.append(try std.fmt.allocPrint(arena, "common-page-size={d}", .{size}));
- }
- if (self.z_max_page_size) |size| {
- try argv.append("-z");
- try argv.append(try std.fmt.allocPrint(arena, "max-page-size={d}", .{size}));
- }
-
- if (getLDMOption(target)) |ldm| {
- try argv.append("-m");
- try argv.append(ldm);
- }
-
- if (link_mode == .static) {
- if (target.cpu.arch.isArm()) {
- try argv.append("-Bstatic");
- } else {
- try argv.append("-static");
- }
- } else if (switch (target.os.tag) {
- else => is_dyn_lib,
- .haiku => is_exe_or_dyn_lib,
- }) {
- try argv.append("-shared");
- }
-
- if (comp.config.pie and output_mode == .Exe) {
- try argv.append("-pie");
- }
-
- if (is_exe_or_dyn_lib and target.os.tag == .netbsd) {
- // Add options to produce shared objects with only 2 PT_LOAD segments.
- // NetBSD expects 2 PT_LOAD segments in a shared object, otherwise
- // ld.elf_so fails loading dynamic libraries with "not found" error.
- // See https://github.com/ziglang/zig/issues/9109 .
- try argv.append("--no-rosegment");
- try argv.append("-znorelro");
- }
-
- try argv.append("-o");
- try argv.append(full_out_path);
-
- // csu prelude
- const csu = try comp.getCrtPaths(arena);
- if (csu.crt0) |p| try argv.append(try p.toString(arena));
- if (csu.crti) |p| try argv.append(try p.toString(arena));
- if (csu.crtbegin) |p| try argv.append(try p.toString(arena));
-
- for (self.rpath_table.keys()) |rpath| {
- try argv.appendSlice(&.{ "-rpath", rpath });
- }
-
- for (self.symbol_wrap_set.keys()) |symbol_name| {
- try argv.appendSlice(&.{ "-wrap", symbol_name });
- }
-
- if (comp.config.link_libc) {
- if (comp.libc_installation) |libc_installation| {
- try argv.append("-L");
- try argv.append(libc_installation.crt_dir.?);
- }
- }
-
- if (have_dynamic_linker and
- (comp.config.link_libc or comp.root_mod.resolved_target.is_explicit_dynamic_linker))
- {
- if (target.dynamic_linker.get()) |dynamic_linker| {
- try argv.append("-dynamic-linker");
- try argv.append(dynamic_linker);
- }
- }
-
- if (is_dyn_lib) {
- if (self.soname) |soname| {
- try argv.append("-soname");
- try argv.append(soname);
- }
- if (self.version_script) |version_script| {
- try argv.append("-version-script");
- try argv.append(version_script);
- }
- if (self.allow_undefined_version) {
- try argv.append("--undefined-version");
- } else {
- try argv.append("--no-undefined-version");
- }
- if (self.enable_new_dtags) |enable_new_dtags| {
- if (enable_new_dtags) {
- try argv.append("--enable-new-dtags");
- } else {
- try argv.append("--disable-new-dtags");
- }
- }
- }
-
- // Positional arguments to the linker such as object files.
- var whole_archive = false;
-
- for (self.base.comp.link_inputs) |link_input| switch (link_input) {
- .res => unreachable, // Windows-only
- .dso => continue,
- .object, .archive => |obj| {
- if (obj.must_link and !whole_archive) {
- try argv.append("-whole-archive");
- whole_archive = true;
- } else if (!obj.must_link and whole_archive) {
- try argv.append("-no-whole-archive");
- whole_archive = false;
- }
- try argv.append(try obj.path.toString(arena));
- },
- .dso_exact => |dso_exact| {
- assert(dso_exact.name[0] == ':');
- try argv.appendSlice(&.{ "-l", dso_exact.name });
- },
- };
-
- if (whole_archive) {
- try argv.append("-no-whole-archive");
- whole_archive = false;
- }
-
- for (comp.c_object_table.keys()) |key| {
- try argv.append(try key.status.success.object_path.toString(arena));
- }
-
- if (module_obj_path) |p| {
- try argv.append(p);
- }
-
- if (comp.tsan_lib) |lib| {
- assert(comp.config.any_sanitize_thread);
- try argv.append(try lib.full_object_path.toString(arena));
- }
-
- if (comp.fuzzer_lib) |lib| {
- assert(comp.config.any_fuzz);
- try argv.append(try lib.full_object_path.toString(arena));
- }
-
- if (ubsan_rt_path) |p| {
- try argv.append(try p.toString(arena));
- }
-
- // Shared libraries.
- if (is_exe_or_dyn_lib) {
- // Worst-case, we need an --as-needed argument for every lib, as well
- // as one before and one after.
- try argv.ensureUnusedCapacity(2 * self.base.comp.link_inputs.len + 2);
- argv.appendAssumeCapacity("--as-needed");
- var as_needed = true;
-
- for (self.base.comp.link_inputs) |link_input| switch (link_input) {
- .res => unreachable, // Windows-only
- .object, .archive, .dso_exact => continue,
- .dso => |dso| {
- const lib_as_needed = !dso.needed;
- switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) {
- 0b00, 0b11 => {},
- 0b01 => {
- argv.appendAssumeCapacity("--no-as-needed");
- as_needed = false;
- },
- 0b10 => {
- argv.appendAssumeCapacity("--as-needed");
- as_needed = true;
- },
- }
-
- // 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".
- argv.appendAssumeCapacity(try dso.path.toString(arena));
- },
- };
-
- if (!as_needed) {
- argv.appendAssumeCapacity("--as-needed");
- as_needed = true;
- }
-
- // libc++ dep
- if (comp.config.link_libcpp) {
- try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena));
- try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena));
- }
-
- // libunwind dep
- if (comp.config.link_libunwind) {
- try argv.append(try comp.libunwind_static_lib.?.full_object_path.toString(arena));
- }
-
- // libc dep
- diags.flags.missing_libc = false;
- if (comp.config.link_libc) {
- if (comp.libc_installation != null) {
- const needs_grouping = link_mode == .static;
- if (needs_grouping) try argv.append("--start-group");
- try argv.appendSlice(target_util.libcFullLinkFlags(target));
- if (needs_grouping) try argv.append("--end-group");
- } else if (target.isGnuLibC()) {
- for (glibc.libs) |lib| {
- if (lib.removed_in) |rem_in| {
- if (target.os.versionRange().gnuLibCVersion().?.order(rem_in) != .lt) continue;
- }
-
- const lib_path = try std.fmt.allocPrint(arena, "{}{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.crtFileAsString(arena, "libc_nonshared.a"));
- } else if (target.isMuslLibC()) {
- try argv.append(try comp.crtFileAsString(arena, switch (link_mode) {
- .static => "libc.a",
- .dynamic => "libc.so",
- }));
- } else if (target.isFreeBSDLibC()) {
- for (freebsd.libs) |lib| {
- const lib_path = try std.fmt.allocPrint(arena, "{}{c}lib{s}.so.{d}", .{
- comp.freebsd_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
- });
- try argv.append(lib_path);
- }
- } else if (target.isNetBSDLibC()) {
- for (netbsd.libs) |lib| {
- const lib_path = try std.fmt.allocPrint(arena, "{}{c}lib{s}.so.{d}", .{
- comp.netbsd_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
- });
- try argv.append(lib_path);
- }
- } else {
- diags.flags.missing_libc = true;
- }
-
- if (comp.zigc_static_lib) |zigc| {
- try argv.append(try zigc.full_object_path.toString(arena));
- }
- }
- }
-
- // compiler-rt. Since compiler_rt exports symbols like `memset`, it needs
- // to be after the shared libraries, so they are picked up from the shared
- // libraries, not libcompiler_rt.
- if (compiler_rt_path) |p| {
- try argv.append(try p.toString(arena));
- }
-
- // crt postlude
- if (csu.crtend) |p| try argv.append(try p.toString(arena));
- if (csu.crtn) |p| try argv.append(try p.toString(arena));
-
- if (self.base.allow_shlib_undefined) {
- try argv.append("--allow-shlib-undefined");
- }
-
- switch (self.compress_debug_sections) {
- .none => {},
- .zlib => try argv.append("--compress-debug-sections=zlib"),
- .zstd => try argv.append("--compress-debug-sections=zstd"),
- }
-
- if (self.bind_global_refs_locally) {
- try argv.append("-Bsymbolic");
- }
-
- try link.spawnLld(comp, arena, argv.items);
- }
-
- if (!self.base.disable_lld_caching) {
- // Update the file with the digest. If it fails we can continue; it only
- // means that the next invocation will have an unnecessary cache miss.
- std.Build.Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
- log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)});
- };
- // Again failure here only means an unnecessary cache miss.
- man.writeManifest() catch |err| {
- log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)});
- };
- // We hang on to this lock so that the output file path can be used without
- // other processes clobbering it.
- self.base.lock = man.toOwnedLock();
- }
-}
-
pub fn writeShdrTable(self: *Elf) !void {
const gpa = self.base.comp.gpa;
const target_endian = self.getTarget().cpu.arch.endian();
@@ -2385,7 +1675,6 @@ pub fn writeElfHeader(self: *Elf) !void {
}
pub fn freeNav(self: *Elf, nav: InternPool.Nav.Index) void {
- if (self.llvm_object) |llvm_object| return llvm_object.freeNav(nav);
return self.zigObjectPtr().?.freeNav(self, nav);
}
@@ -2393,14 +1682,12 @@ pub fn updateFunc(
self: *Elf,
pt: Zcu.PerThread,
func_index: InternPool.Index,
- air: Air,
- liveness: Air.Liveness,
+ mir: *const codegen.AnyMir,
) link.File.UpdateNavError!void {
if (build_options.skip_non_native and builtin.object_format != .elf) {
@panic("Attempted to compile for object format that was disabled by build configuration");
}
- if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(pt, func_index, air, liveness);
- return self.zigObjectPtr().?.updateFunc(self, pt, func_index, air, liveness);
+ return self.zigObjectPtr().?.updateFunc(self, pt, func_index, mir);
}
pub fn updateNav(
@@ -2411,7 +1698,6 @@ pub fn updateNav(
if (build_options.skip_non_native and builtin.object_format != .elf) {
@panic("Attempted to compile for object format that was disabled by build configuration");
}
- if (self.llvm_object) |llvm_object| return llvm_object.updateNav(pt, nav);
return self.zigObjectPtr().?.updateNav(self, pt, nav);
}
@@ -2423,7 +1709,6 @@ pub fn updateContainerType(
if (build_options.skip_non_native and builtin.object_format != .elf) {
@panic("Attempted to compile for object format that was disabled by build configuration");
}
- if (self.llvm_object) |_| return;
const zcu = pt.zcu;
const gpa = zcu.gpa;
return self.zigObjectPtr().?.updateContainerType(pt, ty) catch |err| switch (err) {
@@ -2449,12 +1734,10 @@ pub fn updateExports(
if (build_options.skip_non_native and builtin.object_format != .elf) {
@panic("Attempted to compile for object format that was disabled by build configuration");
}
- if (self.llvm_object) |llvm_object| return llvm_object.updateExports(pt, exported, export_indices);
return self.zigObjectPtr().?.updateExports(self, pt, exported, export_indices);
}
pub fn updateLineNumber(self: *Elf, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {
- if (self.llvm_object) |_| return;
return self.zigObjectPtr().?.updateLineNumber(pt, ti_id);
}
@@ -2463,7 +1746,6 @@ pub fn deleteExport(
exported: Zcu.Exported,
name: InternPool.NullTerminatedString,
) void {
- if (self.llvm_object) |_| return;
return self.zigObjectPtr().?.deleteExport(self, exported, name);
}
@@ -4140,85 +3422,6 @@ fn shdrTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr {
};
}
-fn getLDMOption(target: std.Target) ?[]const u8 {
- // This should only return emulations understood by LLD's parseEmulation().
- return switch (target.cpu.arch) {
- .aarch64 => switch (target.os.tag) {
- .linux => "aarch64linux",
- else => "aarch64elf",
- },
- .aarch64_be => switch (target.os.tag) {
- .linux => "aarch64linuxb",
- else => "aarch64elfb",
- },
- .amdgcn => "elf64_amdgpu",
- .arm, .thumb => switch (target.os.tag) {
- .linux => "armelf_linux_eabi",
- else => "armelf",
- },
- .armeb, .thumbeb => switch (target.os.tag) {
- .linux => "armelfb_linux_eabi",
- else => "armelfb",
- },
- .hexagon => "hexagonelf",
- .loongarch32 => "elf32loongarch",
- .loongarch64 => "elf64loongarch",
- .mips => switch (target.os.tag) {
- .freebsd => "elf32btsmip_fbsd",
- else => "elf32btsmip",
- },
- .mipsel => switch (target.os.tag) {
- .freebsd => "elf32ltsmip_fbsd",
- else => "elf32ltsmip",
- },
- .mips64 => switch (target.os.tag) {
- .freebsd => switch (target.abi) {
- .gnuabin32, .muslabin32 => "elf32btsmipn32_fbsd",
- else => "elf64btsmip_fbsd",
- },
- else => switch (target.abi) {
- .gnuabin32, .muslabin32 => "elf32btsmipn32",
- else => "elf64btsmip",
- },
- },
- .mips64el => switch (target.os.tag) {
- .freebsd => switch (target.abi) {
- .gnuabin32, .muslabin32 => "elf32ltsmipn32_fbsd",
- else => "elf64ltsmip_fbsd",
- },
- else => switch (target.abi) {
- .gnuabin32, .muslabin32 => "elf32ltsmipn32",
- else => "elf64ltsmip",
- },
- },
- .msp430 => "msp430elf",
- .powerpc => switch (target.os.tag) {
- .freebsd => "elf32ppc_fbsd",
- .linux => "elf32ppclinux",
- else => "elf32ppc",
- },
- .powerpcle => switch (target.os.tag) {
- .linux => "elf32lppclinux",
- else => "elf32lppc",
- },
- .powerpc64 => "elf64ppc",
- .powerpc64le => "elf64lppc",
- .riscv32 => "elf32lriscv",
- .riscv64 => "elf64lriscv",
- .s390x => "elf64_s390",
- .sparc64 => "elf64_sparc",
- .x86 => switch (target.os.tag) {
- .freebsd => "elf_i386_fbsd",
- else => "elf_i386",
- },
- .x86_64 => switch (target.abi) {
- .gnux32, .muslx32 => "elf32_x86_64",
- else => "elf_x86_64",
- },
- else => null,
- };
-}
-
pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
return actual_size +| (actual_size / ideal_factor);
}
@@ -5303,10 +4506,7 @@ const codegen = @import("../codegen.zig");
const dev = @import("../dev.zig");
const eh_frame = @import("Elf/eh_frame.zig");
const gc = @import("Elf/gc.zig");
-const glibc = @import("../libs/glibc.zig");
const musl = @import("../libs/musl.zig");
-const freebsd = @import("../libs/freebsd.zig");
-const netbsd = @import("../libs/netbsd.zig");
const link = @import("../link.zig");
const relocatable = @import("Elf/relocatable.zig");
const relocation = @import("Elf/relocation.zig");
@@ -5315,7 +4515,6 @@ const trace = @import("../tracy.zig").trace;
const synthetic_sections = @import("Elf/synthetic_sections.zig");
const Merge = @import("Elf/Merge.zig");
-const Air = @import("../Air.zig");
const Archive = @import("Elf/Archive.zig");
const AtomList = @import("Elf/AtomList.zig");
const Compilation = @import("../Compilation.zig");
@@ -5332,7 +4531,6 @@ const GotSection = synthetic_sections.GotSection;
const GotPltSection = synthetic_sections.GotPltSection;
const HashSection = synthetic_sections.HashSection;
const LinkerDefined = @import("Elf/LinkerDefined.zig");
-const LlvmObject = @import("../codegen/llvm.zig").Object;
const Zcu = @import("../Zcu.zig");
const Object = @import("Elf/Object.zig");
const InternPool = @import("../InternPool.zig");