diff --git a/src/link.zig b/src/link.zig index a935458f56..fdad8a6369 100644 --- a/src/link.zig +++ b/src/link.zig @@ -1009,11 +1009,25 @@ pub const File = struct { } /// Opens a path as a static library and parses it into the linker. - fn openLoadArchive(base: *File, path: Path) anyerror!void { - const diags = &base.comp.link_diags; - const input = try openArchiveInput(diags, path, false, false); - errdefer input.archive.file.close(); - try loadInput(base, input); + /// If `query` is non-null, allows GNU ld scripts. + fn openLoadArchive(base: *File, path: Path, opt_query: ?UnresolvedInput.Query) anyerror!void { + if (opt_query) |query| { + const archive = try openObject(path, query.must_link, query.hidden); + errdefer archive.file.close(); + loadInput(base, .{ .archive = archive }) catch |err| switch (err) { + error.BadMagic, error.UnexpectedEndOfFile => { + if (base.tag != .elf) return err; + try loadGnuLdScript(base, path, query, archive.file); + archive.file.close(); + return; + }, + else => return err, + }; + } else { + const archive = try openObject(path, false, false); + errdefer archive.file.close(); + try loadInput(base, .{ .archive = archive }); + } } /// Opens a path as a shared library and parses it into the linker. @@ -1060,7 +1074,7 @@ pub const File = struct { switch (Compilation.classifyFileExt(arg.path)) { .shared_library => try openLoadDso(base, new_path, query), .object => try openLoadObject(base, new_path), - .static_library => try openLoadArchive(base, new_path), + .static_library => try openLoadArchive(base, new_path, query), else => diags.addParseError(path, "GNU ld script references file with unrecognized extension: {s}", .{arg.path}), } } else { @@ -1408,33 +1422,51 @@ pub const File = struct { assert(mem.startsWith(u8, flag, "-l")); const lib_name = flag["-l".len..]; switch (comp.config.link_mode) { - .dynamic => d: { - const path = Path.initCwd( + .dynamic => { + const dso_path = Path.initCwd( std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{ crt_dir, target.libPrefix(), lib_name, target.dynamicLibSuffix(), }) catch return diags.setAllocFailure(), ); - base.openLoadDso(path, .{ + base.openLoadDso(dso_path, .{ .preferred_mode = .dynamic, .search_strategy = .paths_first, }) catch |err| switch (err) { - error.FileNotFound => break :d, // also try static + error.FileNotFound => { + // Also try static. + const archive_path = Path.initCwd( + std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{ + crt_dir, target.libPrefix(), lib_name, target.staticLibSuffix(), + }) catch return diags.setAllocFailure(), + ); + base.openLoadArchive(archive_path, .{ + .preferred_mode = .dynamic, + .search_strategy = .paths_first, + }) catch |archive_err| switch (archive_err) { + error.LinkFailure => return, // error reported via diags + else => |e| diags.addParseError(dso_path, "failed to parse archive {}: {s}", .{ archive_path, @errorName(e) }), + }; + }, error.LinkFailure => return, // error reported via diags - else => |e| diags.addParseError(path, "failed to parse shared library: {s}", .{@errorName(e)}), + else => |e| diags.addParseError(dso_path, "failed to parse shared library: {s}", .{@errorName(e)}), + }; + }, + .static => { + const path = Path.initCwd( + std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{ + crt_dir, target.libPrefix(), lib_name, target.staticLibSuffix(), + }) catch return diags.setAllocFailure(), + ); + // glibc sometimes makes even archive files GNU ld scripts. + base.openLoadArchive(path, .{ + .preferred_mode = .static, + .search_strategy = .no_fallback, + }) catch |err| switch (err) { + error.LinkFailure => return, // error reported via diags + else => |e| diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(e)}), }; - continue; }, - .static => {}, } - const path = Path.initCwd( - std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{ - crt_dir, target.libPrefix(), lib_name, target.staticLibSuffix(), - }) catch return diags.setAllocFailure(), - ); - base.openLoadArchive(path) catch |err| switch (err) { - error.LinkFailure => return, // error reported via diags - else => |e| diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(e)}), - }; } }, .load_object => |path| { @@ -1444,7 +1476,7 @@ pub const File = struct { }; }, .load_archive => |path| { - base.openLoadArchive(path) catch |err| switch (err) { + base.openLoadArchive(path, null) catch |err| switch (err) { error.LinkFailure => return, // error reported via link_diags else => |e| comp.link_diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(e)}), }; diff --git a/src/link/Elf.zig b/src/link/Elf.zig index c6d4f3317c..979798eeee 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1098,20 +1098,6 @@ fn dumpArgvInit(self: *Elf, arena: Allocator) !void { } } -pub const ParseError = error{ - /// Indicates the error is already reported on `Compilation.link_diags`. - LinkFailure, - - OutOfMemory, - Overflow, - InputOutput, - EndOfStream, - FileSystem, - NotSupported, - InvalidCharacter, - UnknownFileType, -} || fs.Dir.AccessError || fs.File.SeekError || fs.File.OpenError || fs.File.ReadError; - pub fn openParseObjectReportingFailure(self: *Elf, path: Path) void { const diags = &self.base.comp.link_diags; const obj = link.openObject(path, false, false) catch |err| { @@ -1130,7 +1116,7 @@ fn parseObjectReportingFailure(self: *Elf, obj: link.Input.Object) void { }; } -fn parseObject(self: *Elf, obj: link.Input.Object) ParseError!void { +fn parseObject(self: *Elf, obj: link.Input.Object) !void { const tracy = trace(@src()); defer tracy.end(); @@ -1175,7 +1161,7 @@ fn parseArchive( objects: *std.ArrayListUnmanaged(File.Index), obj: link.Input.Object, is_static_lib: bool, -) ParseError!void { +) !void { const tracy = trace(@src()); defer tracy.end(); diff --git a/src/link/Elf/Archive.zig b/src/link/Elf/Archive.zig index 5b9edc0c77..0d177bc21a 100644 --- a/src/link/Elf/Archive.zig +++ b/src/link/Elf/Archive.zig @@ -16,6 +16,15 @@ pub fn parse( handle_index: File.HandleIndex, ) !Archive { const handle = file_handles.items[handle_index]; + var pos: usize = 0; + { + var magic_buffer: [elf.ARMAG.len]u8 = undefined; + const n = try handle.preadAll(&magic_buffer, pos); + if (n != magic_buffer.len) return error.BadMagic; + if (!mem.eql(u8, &magic_buffer, elf.ARMAG)) return error.BadMagic; + pos += magic_buffer.len; + } + const size = (try handle.stat()).size; var objects: std.ArrayListUnmanaged(Object) = .empty; @@ -24,17 +33,14 @@ pub fn parse( var strtab: std.ArrayListUnmanaged(u8) = .empty; defer strtab.deinit(gpa); - var pos: usize = elf.ARMAG.len; - while (true) { - if (pos >= size) break; - if (!mem.isAligned(pos, 2)) pos += 1; + while (pos < size) { + pos = mem.alignForward(usize, pos, 2); - var hdr_buffer: [@sizeOf(elf.ar_hdr)]u8 = undefined; + var hdr: elf.ar_hdr = undefined; { - const amt = try handle.preadAll(&hdr_buffer, pos); - if (amt != @sizeOf(elf.ar_hdr)) return error.InputOutput; + const n = try handle.preadAll(mem.asBytes(&hdr), pos); + if (n != @sizeOf(elf.ar_hdr)) return error.UnexpectedEndOfFile; } - const hdr = @as(*align(1) const elf.ar_hdr, @ptrCast(&hdr_buffer)).*; pos += @sizeOf(elf.ar_hdr); if (!mem.eql(u8, &hdr.ar_fmag, elf.ARFMAG)) {