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.zig165
1 files changed, 156 insertions, 9 deletions
diff --git a/src/link/Elf.zig b/src/link/Elf.zig
index 49b95da7ea..f31df0dcba 100644
--- a/src/link/Elf.zig
+++ b/src/link/Elf.zig
@@ -1383,8 +1383,15 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
// libc dep
self.error_flags.missing_libc = false;
if (self.base.options.link_libc) {
- if (self.base.options.libc_installation != null) {
- @panic("TODO explicit libc_installation");
+ if (self.base.options.libc_installation) |lc| {
+ const flags = target_util.libcFullLinkFlags(target);
+ try system_libs.ensureUnusedCapacity(flags.len);
+ for (flags) |flag| {
+ const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so", .{
+ lc.crt_dir.?, fs.path.sep, flag["-l".len..],
+ });
+ system_libs.appendAssumeCapacity(.{ .path = lib_path });
+ }
} else if (target.isGnuLibC()) {
try system_libs.ensureUnusedCapacity(glibc.libs.len + 1);
for (glibc.libs) |lib| {
@@ -1649,7 +1656,7 @@ const ParseError = error{
FileSystem,
NotSupported,
InvalidCharacter,
-} || std.os.SeekError || std.fs.File.OpenError || std.fs.File.ReadError;
+} || LdScript.Error || std.os.AccessError || std.os.SeekError || std.fs.File.OpenError || std.fs.File.ReadError;
fn parsePositional(
self: *Elf,
@@ -1682,7 +1689,13 @@ fn parseLibrary(
try self.parseArchive(in_file, lib.path, must_link, ctx);
} else if (SharedObject.isSharedObject(in_file)) {
try self.parseSharedObject(in_file, lib, ctx);
- } else return error.UnknownFileType;
+ } else {
+ // TODO if the script has a top-level comment identifying it as GNU ld script,
+ // then report parse errors. Otherwise return UnknownFileType.
+ self.parseLdScript(in_file, lib, ctx) catch |err| switch (err) {
+ else => return error.UnknownFileType,
+ };
+ }
}
fn parseObject(self: *Elf, in_file: std.fs.File, path: []const u8, ctx: *ParseErrorCtx) ParseError!void {
@@ -1693,7 +1706,7 @@ fn parseObject(self: *Elf, in_file: std.fs.File, path: []const u8, ctx: *ParseEr
const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));
const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
self.files.set(index, .{ .object = .{
- .path = path,
+ .path = try gpa.dupe(u8, path),
.data = data,
.index = index,
} });
@@ -1718,11 +1731,14 @@ fn parseArchive(
const gpa = self.base.allocator;
const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));
- var archive = Archive{ .path = path, .data = data };
+ var archive = Archive{ .path = try gpa.dupe(u8, path), .data = data };
defer archive.deinit(gpa);
try archive.parse(self);
- for (archive.objects.items) |extracted| {
+ const objects = try archive.objects.toOwnedSlice(gpa);
+ defer gpa.free(objects);
+
+ for (objects) |extracted| {
const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
self.files.set(index, .{ .object = extracted });
const object = &self.files.items(.data)[index].object;
@@ -1749,7 +1765,7 @@ fn parseSharedObject(
const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));
const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
self.files.set(index, .{ .shared_object = .{
- .path = lib.path,
+ .path = try gpa.dupe(u8, lib.path),
.data = data,
.index = index,
.needed = lib.needed,
@@ -1764,6 +1780,123 @@ fn parseSharedObject(
if (ctx.detected_cpu_arch != self.base.options.target.cpu.arch) return error.InvalidCpuArch;
}
+fn parseLdScript(self: *Elf, in_file: std.fs.File, lib: SystemLib, ctx: *ParseErrorCtx) ParseError!void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const gpa = self.base.allocator;
+ const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));
+ defer gpa.free(data);
+
+ var script = LdScript{};
+ defer script.deinit(gpa);
+ try script.parse(data, self);
+
+ if (script.cpu_arch) |cpu_arch| {
+ ctx.detected_cpu_arch = cpu_arch;
+ if (ctx.detected_cpu_arch != self.base.options.target.cpu.arch) return error.InvalidCpuArch;
+ }
+
+ const lib_dirs = self.base.options.lib_dirs;
+
+ var arena_allocator = std.heap.ArenaAllocator.init(gpa);
+ defer arena_allocator.deinit();
+ const arena = arena_allocator.allocator();
+
+ var test_path = std.ArrayList(u8).init(arena);
+ var checked_paths = std.ArrayList([]const u8).init(arena);
+
+ for (script.args.items) |scr_obj| {
+ checked_paths.clearRetainingCapacity();
+
+ success: {
+ if (mem.startsWith(u8, scr_obj.path, "-l")) {
+ const lib_name = scr_obj.path["-l".len..];
+
+ // TODO I think technically we should re-use the mechanism used by the frontend here.
+ // Maybe we should hoist search-strategy all the way here?
+ for (lib_dirs) |lib_dir| {
+ if (!self.isStatic()) {
+ if (try self.accessLibPath(&test_path, &checked_paths, lib_dir, lib_name, .Dynamic))
+ break :success;
+ }
+ if (try self.accessLibPath(&test_path, &checked_paths, lib_dir, lib_name, .Static))
+ break :success;
+ }
+
+ try self.reportMissingLibraryError(
+ checked_paths.items,
+ "missing library dependency: GNU ld script '{s}' requires '{s}', but file not found",
+ .{
+ lib.path,
+ scr_obj.path,
+ },
+ );
+ } else {
+ var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
+ if (fs.realpath(scr_obj.path, &buffer)) |path| {
+ test_path.clearRetainingCapacity();
+ try test_path.writer().writeAll(path);
+ break :success;
+ } else |_| {}
+
+ try checked_paths.append(try gpa.dupe(u8, scr_obj.path));
+ for (lib_dirs) |lib_dir| {
+ if (try self.accessLibPath(&test_path, &checked_paths, lib_dir, scr_obj.path, null))
+ break :success;
+ }
+
+ try self.reportMissingLibraryError(
+ checked_paths.items,
+ "missing library dependency: GNU ld script '{s}' requires '{s}', but file not found",
+ .{
+ lib.path,
+ scr_obj.path,
+ },
+ );
+ }
+ }
+
+ const full_path = test_path.items;
+ const scr_file = try std.fs.cwd().openFile(full_path, .{});
+ defer scr_file.close();
+
+ var scr_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };
+ self.parseLibrary(scr_file, .{
+ .needed = scr_obj.needed,
+ .path = full_path,
+ }, false, &scr_ctx) catch |err| try self.handleAndReportParseError(full_path, err, &scr_ctx);
+ }
+}
+
+fn accessLibPath(
+ self: *Elf,
+ test_path: *std.ArrayList(u8),
+ checked_paths: *std.ArrayList([]const u8),
+ lib_dir_path: []const u8,
+ lib_name: []const u8,
+ link_mode: ?std.builtin.LinkMode,
+) !bool {
+ const sep = fs.path.sep_str;
+ const target = self.base.options.target;
+ test_path.clearRetainingCapacity();
+ try test_path.writer().print("{s}" ++ sep ++ "{s}{s}{s}", .{
+ lib_dir_path,
+ target.libPrefix(),
+ lib_name,
+ if (link_mode) |mode| switch (mode) {
+ .Static => target.staticLibSuffix(),
+ .Dynamic => target.dynamicLibSuffix(),
+ } else "",
+ });
+ try checked_paths.append(try self.base.allocator.dupe(u8, test_path.items));
+ fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
+ error.FileNotFound => return false,
+ else => |e| return e,
+ };
+ return true;
+}
+
/// When resolving symbols, we approach the problem similarly to `mold`.
/// 1. Resolve symbols across all objects (including those preemptively extracted archives).
/// 2. Resolve symbols across all shared objects.
@@ -5886,6 +6019,19 @@ fn reportUndefined(self: *Elf, undefs: anytype) !void {
}
}
+fn reportMissingLibraryError(
+ self: *Elf,
+ checked_paths: []const []const u8,
+ comptime format: []const u8,
+ args: anytype,
+) error{OutOfMemory}!void {
+ var err = try self.addErrorWithNotes(checked_paths.len);
+ try err.addMsg(self, format, args);
+ for (checked_paths) |path| {
+ try err.addNote(self, "tried {s}", .{path});
+ }
+}
+
const ParseErrorCtx = struct {
detected_cpu_arch: std.Target.Cpu.Arch,
};
@@ -6182,7 +6328,7 @@ pub const null_shdr = elf.Elf64_Shdr{
.sh_entsize = 0,
};
-const SystemLib = struct {
+pub const SystemLib = struct {
needed: bool = false,
path: []const u8,
};
@@ -6228,6 +6374,7 @@ const GnuHashSection = synthetic_sections.GnuHashSection;
const GotSection = synthetic_sections.GotSection;
const GotPltSection = synthetic_sections.GotPltSection;
const HashSection = synthetic_sections.HashSection;
+const LdScript = @import("Elf/LdScript.zig");
const LinkerDefined = @import("Elf/LinkerDefined.zig");
const Liveness = @import("../Liveness.zig");
const LlvmObject = @import("../codegen/llvm.zig").Object;