aboutsummaryrefslogtreecommitdiff
path: root/src/link
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2024-10-18 00:23:35 -0700
committerAndrew Kelley <andrew@ziglang.org>2024-10-23 16:27:38 -0700
commit5ca54036ca0bc292ead681c03c8ac57e27a127db (patch)
tree17b151d68b2cb40c49d82af85286074be929db7d /src/link
parent2dcfa723767d284cef5eb180be7c080583ddbe25 (diff)
downloadzig-5ca54036ca0bc292ead681c03c8ac57e27a127db.tar.gz
zig-5ca54036ca0bc292ead681c03c8ac57e27a127db.zip
move linker input file parsing to the compilation pipeline
Diffstat (limited to 'src/link')
-rw-r--r--src/link/Elf.zig243
-rw-r--r--src/link/Elf/relocatable.zig56
2 files changed, 37 insertions, 262 deletions
diff --git a/src/link/Elf.zig b/src/link/Elf.zig
index 293573b112..28a161da0f 100644
--- a/src/link/Elf.zig
+++ b/src/link/Elf.zig
@@ -35,8 +35,7 @@ ptr_width: PtrWidth,
llvm_object: ?LlvmObject.Ptr = null,
/// A list of all input files.
-/// Index of each input file also encodes the priority or precedence of one input file
-/// over another.
+/// First index is a special "null file". Order is otherwise not observed.
files: std.MultiArrayList(File.Entry) = .{},
/// Long-lived list of all file descriptors.
/// We store them globally rather than per actual File so that we can re-use
@@ -350,6 +349,9 @@ pub fn createEmpty(
return self;
}
+ // --verbose-link
+ if (comp.verbose_link) try self.dumpArgv(comp);
+
const is_obj = output_mode == .Obj;
const is_obj_or_ar = is_obj or (output_mode == .Lib and link_mode == .static);
@@ -750,6 +752,22 @@ pub fn allocateChunk(self: *Elf, args: struct {
return res;
}
+pub fn loadInput(self: *Elf, input: link.Input) !void {
+ const gpa = self.base.comp.gpa;
+ const diags = &self.base.comp.link_diags;
+ const target = self.getTarget();
+ const debug_fmt_strip = self.base.comp.config.debug_format == .strip;
+ const default_sym_version = self.default_sym_version;
+
+ switch (input) {
+ .res => unreachable,
+ .dso_exact => @panic("TODO"),
+ .object => |obj| try parseObject(self, obj),
+ .archive => |obj| try parseArchive(gpa, diags, &self.file_handles, &self.files, &self.first_eflags, target, debug_fmt_strip, default_sym_version, &self.objects, obj),
+ .dso => |dso| try parseDso(gpa, diags, dso, &self.shared_objects, &self.files, target),
+ }
+}
+
pub fn flush(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
const use_lld = build_options.have_llvm and self.base.comp.config.use_lld;
if (use_lld) {
@@ -775,8 +793,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
const sub_prog_node = prog_node.start("ELF Flush", 0);
defer sub_prog_node.end();
- const target = self.getTarget();
- const link_mode = comp.config.link_mode;
const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type.
const module_obj_path: ?Path = if (self.base.zcu_object_sub_path) |path| .{
.root_dir = directory,
@@ -786,9 +802,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
path,
} else null;
- // --verbose-link
- if (comp.verbose_link) try self.dumpArgv(comp);
-
if (self.zigObjectPtr()) |zig_object| try zig_object.flush(self, tid);
switch (comp.config.output_mode) {
@@ -800,124 +813,8 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
.Exe => {},
}
- const csu = try comp.getCrtPaths(arena);
-
- // csu prelude
- if (csu.crt0) |path| openParseObjectReportingFailure(self, path);
- if (csu.crti) |path| openParseObjectReportingFailure(self, path);
- if (csu.crtbegin) |path| openParseObjectReportingFailure(self, path);
-
- // objects and archives
- for (comp.link_inputs) |link_input| switch (link_input) {
- .object, .archive => parseInputReportingFailure(self, link_input),
- .dso_exact => @panic("TODO"),
- .dso => continue, // handled below
- .res => unreachable,
- };
-
- // This is a set of object files emitted by clang in a single `build-exe` invocation.
- // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
- // in this set.
- for (comp.c_object_table.keys()) |key| {
- openParseObjectReportingFailure(self, key.status.success.object_path);
- }
-
if (module_obj_path) |path| openParseObjectReportingFailure(self, path);
- if (comp.config.any_sanitize_thread)
- openParseArchiveReportingFailure(self, comp.tsan_lib.?.full_object_path);
-
- if (comp.config.any_fuzz)
- openParseArchiveReportingFailure(self, comp.fuzzer_lib.?.full_object_path);
-
- // libc
- if (!comp.skip_linker_dependencies and !comp.config.link_libc) {
- if (comp.libc_static_lib) |lib|
- openParseArchiveReportingFailure(self, lib.full_object_path);
- }
-
- // dynamic libraries
- for (comp.link_inputs) |link_input| switch (link_input) {
- .object, .archive, .dso_exact => continue, // handled above
- .dso => parseInputReportingFailure(self, link_input),
- .res => unreachable,
- };
-
- // libc++ dep
- if (comp.config.link_libcpp) {
- openParseArchiveReportingFailure(self, comp.libcxxabi_static_lib.?.full_object_path);
- openParseArchiveReportingFailure(self, comp.libcxx_static_lib.?.full_object_path);
- }
-
- // libunwind dep
- if (comp.config.link_libunwind) {
- openParseArchiveReportingFailure(self, comp.libunwind_static_lib.?.full_object_path);
- }
-
- // libc dep
- diags.flags.missing_libc = false;
- if (comp.config.link_libc) {
- if (comp.libc_installation) |lc| {
- const flags = target_util.libcFullLinkFlags(target);
-
- for (flags) |flag| {
- assert(mem.startsWith(u8, flag, "-l"));
- const lib_name = flag["-l".len..];
- const suffix = switch (comp.config.link_mode) {
- .static => target.staticLibSuffix(),
- .dynamic => target.dynamicLibSuffix(),
- };
- const lib_path = try std.fmt.allocPrint(arena, "{s}/lib{s}{s}", .{
- lc.crt_dir.?, lib_name, suffix,
- });
- const resolved_path = Path.initCwd(lib_path);
- switch (comp.config.link_mode) {
- .static => openParseArchiveReportingFailure(self, resolved_path),
- .dynamic => openParseDsoReportingFailure(self, resolved_path),
- }
- }
- } else if (target.isGnuLibC()) {
- for (glibc.libs) |lib| {
- if (lib.removed_in) |rem_in| {
- if (target.os.version_range.linux.glibc.order(rem_in) != .lt) continue;
- }
-
- const lib_path = Path.initCwd(try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{
- comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
- }));
- openParseDsoReportingFailure(self, lib_path);
- }
- const crt_file_path = try comp.get_libc_crt_file(arena, "libc_nonshared.a");
- openParseArchiveReportingFailure(self, crt_file_path);
- } else if (target.isMusl()) {
- const path = try comp.get_libc_crt_file(arena, switch (link_mode) {
- .static => "libc.a",
- .dynamic => "libc.so",
- });
- switch (link_mode) {
- .static => openParseArchiveReportingFailure(self, path),
- .dynamic => openParseDsoReportingFailure(self, path),
- }
- } else {
- diags.flags.missing_libc = true;
- }
- }
-
- // Finally, as the last input objects we add compiler_rt and CSU postlude (if any).
-
- // 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 (comp.compiler_rt_lib) |crt_file| {
- openParseArchiveReportingFailure(self, crt_file.full_object_path);
- } else if (comp.compiler_rt_obj) |crt_file| {
- openParseObjectReportingFailure(self, crt_file.full_object_path);
- }
-
- // csu postlude
- if (csu.crtend) |path| openParseObjectReportingFailure(self, path);
- if (csu.crtn) |path| openParseObjectReportingFailure(self, path);
-
if (diags.hasErrors()) return error.FlushFailure;
// If we haven't already, create a linker-generated input file comprising of
@@ -1087,7 +984,17 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
}
} else null;
- const csu = try comp.getCrtPaths(arena);
+ const crt_basenames = std.zig.LibCInstallation.CrtBasenames.get(.{
+ .target = target,
+ .link_libc = comp.config.link_libc,
+ .output_mode = comp.config.output_mode,
+ .link_mode = link_mode,
+ .pie = comp.config.pie,
+ });
+ const crt_paths: std.zig.LibCInstallation.CrtPaths = if (comp.libc_installation) |lci|
+ try lci.resolveCrtPaths(arena, crt_basenames, target)
+ else
+ .{};
const compiler_rt_path: ?[]const u8 = blk: {
if (comp.compiler_rt_lib) |x| break :blk try x.full_object_path.toString(arena);
if (comp.compiler_rt_obj) |x| break :blk try x.full_object_path.toString(arena);
@@ -1204,10 +1111,9 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
try argv.append("-s");
}
- // csu prelude
- if (csu.crt0) |path| try argv.append(try path.toString(arena));
- if (csu.crti) |path| try argv.append(try path.toString(arena));
- if (csu.crtbegin) |path| try argv.append(try path.toString(arena));
+ if (crt_paths.crt0) |path| try argv.append(try path.toString(arena));
+ if (crt_paths.crti) |path| try argv.append(try path.toString(arena));
+ if (crt_paths.crtbegin) |path| try argv.append(try path.toString(arena));
if (comp.config.link_libc) {
if (self.base.comp.libc_installation) |libc_installation| {
@@ -1339,9 +1245,8 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
try argv.append(p);
}
- // crt postlude
- if (csu.crtend) |path| try argv.append(try path.toString(arena));
- if (csu.crtn) |path| try argv.append(try path.toString(arena));
+ if (crt_paths.crtend) |path| try argv.append(try path.toString(arena));
+ if (crt_paths.crtn) |path| try argv.append(try path.toString(arena));
}
Compilation.dump_argv(argv.items);
@@ -1361,20 +1266,6 @@ pub const ParseError = error{
UnknownFileType,
} || fs.Dir.AccessError || fs.File.SeekError || fs.File.OpenError || fs.File.ReadError;
-pub fn parseInputReportingFailure(self: *Elf, input: link.Input) void {
- const gpa = self.base.comp.gpa;
- const diags = &self.base.comp.link_diags;
- const target = self.getTarget();
-
- switch (input) {
- .res => unreachable,
- .dso_exact => unreachable,
- .object => |obj| parseObjectReportingFailure(self, obj),
- .archive => |obj| parseArchiveReportingFailure(self, obj),
- .dso => |dso| parseDsoReportingFailure(gpa, diags, dso, &self.shared_objects, &self.files, target),
- }
-}
-
pub fn openParseObjectReportingFailure(self: *Elf, path: Path) void {
const diags = &self.base.comp.link_diags;
const obj = link.openObject(path, false, false) catch |err| {
@@ -1385,7 +1276,7 @@ pub fn openParseObjectReportingFailure(self: *Elf, path: Path) void {
self.parseObjectReportingFailure(obj);
}
-pub fn parseObjectReportingFailure(self: *Elf, obj: link.Input.Object) void {
+fn parseObjectReportingFailure(self: *Elf, obj: link.Input.Object) void {
const diags = &self.base.comp.link_diags;
self.parseObject(obj) catch |err| switch (err) {
error.LinkFailure => return, // already reported
@@ -1423,33 +1314,6 @@ fn parseObject(self: *Elf, obj: link.Input.Object) ParseError!void {
try object.parse(gpa, diags, obj.path, handle, first_eflags, target, debug_fmt_strip, default_sym_version);
}
-pub fn openParseArchiveReportingFailure(self: *Elf, path: Path) void {
- const diags = &self.base.comp.link_diags;
- const obj = link.openObject(path, false, false) catch |err| {
- switch (diags.failParse(path, "failed to open archive {}: {s}", .{ path, @errorName(err) })) {
- error.LinkFailure => return,
- }
- };
- parseArchiveReportingFailure(self, obj);
-}
-
-pub fn parseArchiveReportingFailure(self: *Elf, obj: link.Input.Object) void {
- const gpa = self.base.comp.gpa;
- const diags = &self.base.comp.link_diags;
- const first_eflags = &self.first_eflags;
- const target = self.base.comp.root_mod.resolved_target.result;
- const debug_fmt_strip = self.base.comp.config.debug_format == .strip;
- const default_sym_version = self.default_sym_version;
- const file_handles = &self.file_handles;
- const files = &self.files;
- const objects = &self.objects;
-
- parseArchive(gpa, diags, file_handles, files, first_eflags, target, debug_fmt_strip, default_sym_version, objects, obj) catch |err| switch (err) {
- error.LinkFailure => return, // already reported
- else => |e| diags.addParseError(obj.path, "failed to parse archive: {s}", .{@errorName(e)}),
- };
-}
-
fn parseArchive(
gpa: Allocator,
diags: *Diags,
@@ -1480,38 +1344,6 @@ fn parseArchive(
}
}
-fn openParseDsoReportingFailure(self: *Elf, path: Path) void {
- const diags = &self.base.comp.link_diags;
- const target = self.getTarget();
- const dso = link.openDso(path, false, false, false) catch |err| {
- switch (diags.failParse(path, "failed to open shared object {}: {s}", .{ path, @errorName(err) })) {
- error.LinkFailure => return,
- }
- };
- const gpa = self.base.comp.gpa;
- parseDsoReportingFailure(gpa, diags, dso, &self.shared_objects, &self.files, target);
-}
-
-fn parseDsoReportingFailure(
- gpa: Allocator,
- diags: *Diags,
- dso: link.Input.Dso,
- shared_objects: *std.StringArrayHashMapUnmanaged(File.Index),
- files: *std.MultiArrayList(File.Entry),
- target: std.Target,
-) void {
- parseDso(gpa, diags, dso, shared_objects, files, target) catch |err| switch (err) {
- error.LinkFailure => return, // already reported
- error.BadMagic, error.UnexpectedEndOfFile => {
- var notes = diags.addErrorWithNotes(2) catch return diags.setAllocFailure();
- notes.addMsg("failed to parse shared object: {s}", .{@errorName(err)}) catch return diags.setAllocFailure();
- notes.addNote("while parsing {}", .{dso.path}) catch return diags.setAllocFailure();
- notes.addNote("{s}", .{@as([]const u8, "the file may be a GNU ld script, in which case it is not an ELF file but a text file referencing other libraries to link. In this case, avoid depending on the library, convince your system administrators to refrain from using this kind of file, or pass -fallow-so-scripts to force the compiler to check every shared library in case it is an ld script.")}) catch return diags.setAllocFailure();
- },
- else => |e| diags.addParseError(dso.path, "failed to parse shared object: {s}", .{@errorName(e)}),
- };
-}
-
fn parseDso(
gpa: Allocator,
diags: *Diags,
@@ -1524,7 +1356,6 @@ fn parseDso(
defer tracy.end();
const handle = dso.file;
- defer handle.close();
const stat = Stat.fromFs(try handle.stat());
var header = try SharedObject.parseHeader(gpa, diags, dso.path, handle, stat, target);
diff --git a/src/link/Elf/relocatable.zig b/src/link/Elf/relocatable.zig
index 5e4d4aa7b7..46622fdf69 100644
--- a/src/link/Elf/relocatable.zig
+++ b/src/link/Elf/relocatable.zig
@@ -2,26 +2,10 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path
const gpa = comp.gpa;
const diags = &comp.link_diags;
- for (comp.link_inputs) |link_input| switch (link_input) {
- .object => |obj| parseObjectStaticLibReportingFailure(elf_file, obj.path),
- .archive => |obj| parseArchiveStaticLibReportingFailure(elf_file, obj.path),
- .dso_exact => unreachable,
- .res => unreachable,
- .dso => unreachable,
- };
-
- for (comp.c_object_table.keys()) |key| {
- parseObjectStaticLibReportingFailure(elf_file, key.status.success.object_path);
- }
-
if (module_obj_path) |path| {
parseObjectStaticLibReportingFailure(elf_file, path);
}
- if (comp.include_compiler_rt) {
- parseObjectStaticLibReportingFailure(elf_file, comp.compiler_rt_obj.?.full_object_path);
- }
-
if (diags.hasErrors()) return error.FlushFailure;
// First, we flush relocatable object file generated with our backends.
@@ -153,17 +137,6 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path
pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path) link.File.FlushError!void {
const diags = &comp.link_diags;
- for (comp.link_inputs) |link_input| {
- elf_file.parseInputReportingFailure(link_input);
- }
-
- // This is a set of object files emitted by clang in a single `build-exe` invocation.
- // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
- // in this set.
- for (comp.c_object_table.keys()) |key| {
- elf_file.openParseObjectReportingFailure(key.status.success.object_path);
- }
-
if (module_obj_path) |path| elf_file.openParseObjectReportingFailure(path);
if (diags.hasErrors()) return error.FlushFailure;
@@ -223,14 +196,6 @@ fn parseObjectStaticLibReportingFailure(elf_file: *Elf, path: Path) void {
};
}
-fn parseArchiveStaticLibReportingFailure(elf_file: *Elf, path: Path) void {
- const diags = &elf_file.base.comp.link_diags;
- parseArchiveStaticLib(elf_file, path) catch |err| switch (err) {
- error.LinkFailure => return,
- else => |e| diags.addParseError(path, "parsing static library failed: {s}", .{@errorName(e)}),
- };
-}
-
fn parseObjectStaticLib(elf_file: *Elf, path: Path) Elf.ParseError!void {
const gpa = elf_file.base.comp.gpa;
const file_handles = &elf_file.file_handles;
@@ -253,27 +218,6 @@ fn parseObjectStaticLib(elf_file: *Elf, path: Path) Elf.ParseError!void {
try object.parseAr(path, elf_file);
}
-fn parseArchiveStaticLib(elf_file: *Elf, path: Path) Elf.ParseError!void {
- const gpa = elf_file.base.comp.gpa;
- const diags = &elf_file.base.comp.link_diags;
- const file_handles = &elf_file.file_handles;
-
- const handle = try path.root_dir.handle.openFile(path.sub_path, .{});
- const fh = try Elf.addFileHandle(gpa, file_handles, handle);
-
- var archive = try Archive.parse(gpa, diags, file_handles, path, fh);
- defer archive.deinit(gpa);
-
- for (archive.objects) |extracted| {
- const index: File.Index = @intCast(try elf_file.files.addOne(gpa));
- elf_file.files.set(index, .{ .object = extracted });
- const object = &elf_file.files.items(.data)[index].object;
- object.index = index;
- try object.parseAr(path, elf_file);
- try elf_file.objects.append(gpa, index);
- }
-}
-
fn claimUnresolved(elf_file: *Elf) void {
if (elf_file.zigObjectPtr()) |zig_object| {
zig_object.claimUnresolvedRelocatable(elf_file);