authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-10-21 20:22:27-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-10-23 16:27:39-07:00
log336466c9dfcc5547c90dfe364e02a79576862e7c
tree25f5fdec236b91e2b0b17d16151453b30bfc93cf
parentccac11196b377bf37bed85b03bd53abfc785dc99

glibc sometimes makes archives be ld scripts

it is incredible how many bad ideas glibc is bundled into one project.

3 files changed, 71 insertions(+), 47 deletions(-)

src/link.zig+55-23
......@@ -1009,11 +1009,25 @@ pub const File = struct {
10091009 }
10101010
10111011 /// Opens a path as a static library and parses it into the linker.
1012 fn openLoadArchive(base: *File, path: Path) anyerror!void {
1013 const diags = &base.comp.link_diags;
1014 const input = try openArchiveInput(diags, path, false, false);
1015 errdefer input.archive.file.close();
1016 try loadInput(base, input);
1012 /// If `query` is non-null, allows GNU ld scripts.
1013 fn openLoadArchive(base: *File, path: Path, opt_query: ?UnresolvedInput.Query) anyerror!void {
1014 if (opt_query) |query| {
1015 const archive = try openObject(path, query.must_link, query.hidden);
1016 errdefer archive.file.close();
1017 loadInput(base, .{ .archive = archive }) catch |err| switch (err) {
1018 error.BadMagic, error.UnexpectedEndOfFile => {
1019 if (base.tag != .elf) return err;
1020 try loadGnuLdScript(base, path, query, archive.file);
1021 archive.file.close();
1022 return;
1023 },
1024 else => return err,
1025 };
1026 } else {
1027 const archive = try openObject(path, false, false);
1028 errdefer archive.file.close();
1029 try loadInput(base, .{ .archive = archive });
1030 }
10171031 }
10181032
10191033 /// Opens a path as a shared library and parses it into the linker.
......@@ -1060,7 +1074,7 @@ pub const File = struct {
10601074 switch (Compilation.classifyFileExt(arg.path)) {
10611075 .shared_library => try openLoadDso(base, new_path, query),
10621076 .object => try openLoadObject(base, new_path),
1063 .static_library => try openLoadArchive(base, new_path),
1077 .static_library => try openLoadArchive(base, new_path, query),
10641078 else => diags.addParseError(path, "GNU ld script references file with unrecognized extension: {s}", .{arg.path}),
10651079 }
10661080 } else {
......@@ -1408,33 +1422,51 @@ pub const File = struct {
14081422 assert(mem.startsWith(u8, flag, "-l"));
14091423 const lib_name = flag["-l".len..];
14101424 switch (comp.config.link_mode) {
1411 .dynamic => d: {
1412 const path = Path.initCwd(
1425 .dynamic => {
1426 const dso_path = Path.initCwd(
14131427 std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{
14141428 crt_dir, target.libPrefix(), lib_name, target.dynamicLibSuffix(),
14151429 }) catch return diags.setAllocFailure(),
14161430 );
1417 base.openLoadDso(path, .{
1431 base.openLoadDso(dso_path, .{
14181432 .preferred_mode = .dynamic,
14191433 .search_strategy = .paths_first,
14201434 }) catch |err| switch (err) {
1421 error.FileNotFound => break :d, // also try static
1435 error.FileNotFound => {
1436 // Also try static.
1437 const archive_path = Path.initCwd(
1438 std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{
1439 crt_dir, target.libPrefix(), lib_name, target.staticLibSuffix(),
1440 }) catch return diags.setAllocFailure(),
1441 );
1442 base.openLoadArchive(archive_path, .{
1443 .preferred_mode = .dynamic,
1444 .search_strategy = .paths_first,
1445 }) catch |archive_err| switch (archive_err) {
1446 error.LinkFailure => return, // error reported via diags
1447 else => |e| diags.addParseError(dso_path, "failed to parse archive {}: {s}", .{ archive_path, @errorName(e) }),
1448 };
1449 },
1450 error.LinkFailure => return, // error reported via diags
1451 else => |e| diags.addParseError(dso_path, "failed to parse shared library: {s}", .{@errorName(e)}),
1452 };
1453 },
1454 .static => {
1455 const path = Path.initCwd(
1456 std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{
1457 crt_dir, target.libPrefix(), lib_name, target.staticLibSuffix(),
1458 }) catch return diags.setAllocFailure(),
1459 );
1460 // glibc sometimes makes even archive files GNU ld scripts.
1461 base.openLoadArchive(path, .{
1462 .preferred_mode = .static,
1463 .search_strategy = .no_fallback,
1464 }) catch |err| switch (err) {
14221465 error.LinkFailure => return, // error reported via diags
1423 else => |e| diags.addParseError(path, "failed to parse shared library: {s}", .{@errorName(e)}),
1466 else => |e| diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(e)}),
14241467 };
1425 continue;
14261468 },
1427 .static => {},
14281469 }
1429 const path = Path.initCwd(
1430 std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{
1431 crt_dir, target.libPrefix(), lib_name, target.staticLibSuffix(),
1432 }) catch return diags.setAllocFailure(),
1433 );
1434 base.openLoadArchive(path) catch |err| switch (err) {
1435 error.LinkFailure => return, // error reported via diags
1436 else => |e| diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(e)}),
1437 };
14381470 }
14391471 },
14401472 .load_object => |path| {
......@@ -1444,7 +1476,7 @@ pub const File = struct {
14441476 };
14451477 },
14461478 .load_archive => |path| {
1447 base.openLoadArchive(path) catch |err| switch (err) {
1479 base.openLoadArchive(path, null) catch |err| switch (err) {
14481480 error.LinkFailure => return, // error reported via link_diags
14491481 else => |e| comp.link_diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(e)}),
14501482 };
src/link/Elf.zig+2-16
......@@ -1098,20 +1098,6 @@ fn dumpArgvInit(self: *Elf, arena: Allocator) !void {
10981098 }
10991099}
11001100
1101pub const ParseError = error{
1102 /// Indicates the error is already reported on `Compilation.link_diags`.
1103 LinkFailure,
1104
1105 OutOfMemory,
1106 Overflow,
1107 InputOutput,
1108 EndOfStream,
1109 FileSystem,
1110 NotSupported,
1111 InvalidCharacter,
1112 UnknownFileType,
1113} || fs.Dir.AccessError || fs.File.SeekError || fs.File.OpenError || fs.File.ReadError;
1114
11151101pub fn openParseObjectReportingFailure(self: *Elf, path: Path) void {
11161102 const diags = &self.base.comp.link_diags;
11171103 const obj = link.openObject(path, false, false) catch |err| {
......@@ -1130,7 +1116,7 @@ fn parseObjectReportingFailure(self: *Elf, obj: link.Input.Object) void {
11301116 };
11311117}
11321118
1133fn parseObject(self: *Elf, obj: link.Input.Object) ParseError!void {
1119fn parseObject(self: *Elf, obj: link.Input.Object) !void {
11341120 const tracy = trace(@src());
11351121 defer tracy.end();
11361122
......@@ -1175,7 +1161,7 @@ fn parseArchive(
11751161 objects: *std.ArrayListUnmanaged(File.Index),
11761162 obj: link.Input.Object,
11771163 is_static_lib: bool,
1178) ParseError!void {
1164) !void {
11791165 const tracy = trace(@src());
11801166 defer tracy.end();
11811167
src/link/Elf/Archive.zig+14-8
......@@ -16,6 +16,15 @@ pub fn parse(
1616 handle_index: File.HandleIndex,
1717) !Archive {
1818 const handle = file_handles.items[handle_index];
19 var pos: usize = 0;
20 {
21 var magic_buffer: [elf.ARMAG.len]u8 = undefined;
22 const n = try handle.preadAll(&magic_buffer, pos);
23 if (n != magic_buffer.len) return error.BadMagic;
24 if (!mem.eql(u8, &magic_buffer, elf.ARMAG)) return error.BadMagic;
25 pos += magic_buffer.len;
26 }
27
1928 const size = (try handle.stat()).size;
2029
2130 var objects: std.ArrayListUnmanaged(Object) = .empty;
......@@ -24,17 +33,14 @@ pub fn parse(
2433 var strtab: std.ArrayListUnmanaged(u8) = .empty;
2534 defer strtab.deinit(gpa);
2635
27 var pos: usize = elf.ARMAG.len;
28 while (true) {
29 if (pos >= size) break;
30 if (!mem.isAligned(pos, 2)) pos += 1;
36 while (pos < size) {
37 pos = mem.alignForward(usize, pos, 2);
3138
32 var hdr_buffer: [@sizeOf(elf.ar_hdr)]u8 = undefined;
39 var hdr: elf.ar_hdr = undefined;
3340 {
34 const amt = try handle.preadAll(&hdr_buffer, pos);
35 if (amt != @sizeOf(elf.ar_hdr)) return error.InputOutput;
41 const n = try handle.preadAll(mem.asBytes(&hdr), pos);
42 if (n != @sizeOf(elf.ar_hdr)) return error.UnexpectedEndOfFile;
3643 }
37 const hdr = @as(*align(1) const elf.ar_hdr, @ptrCast(&hdr_buffer)).*;
3844 pos += @sizeOf(elf.ar_hdr);
3945
4046 if (!mem.eql(u8, &hdr.ar_fmag, elf.ARFMAG)) {