glibc sometimes makes archives be ld scripts

it is incredible how many bad ideas glibc is bundled into one project.
This commit is contained in:
Andrew Kelley 2024-10-21 20:22:27 -07:00
parent ccac11196b
commit 336466c9df
3 changed files with 71 additions and 47 deletions

View File

@ -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)}),
};

View File

@ -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();

View File

@ -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)) {