authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-09-25 17:35:48+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-09-26 21:07:47+02:00
log5e617e4b0c35c75e8079f3c0918392327a55d413
treefd1925e67793570b54aa0f42c2eae6416d73d4ff
parente7c6dfde3d2e52ac8180fc3c147064528124301d

elf: put libc on the linker line if requested


2 files changed, 67 insertions(+), 27 deletions(-)

src/link/Elf.zig+58-17
......@@ -965,6 +965,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
965965 defer arena_allocator.deinit();
966966 const arena = arena_allocator.allocator();
967967
968 const target = self.base.options.target;
968969 const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type.
969970 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
970971
......@@ -993,22 +994,63 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
993994 try positionals.append(.{ .path = key.status.success.object_path });
994995 }
995996
997 for (positionals.items) |obj| {
998 const in_file = try std.fs.cwd().openFile(obj.path, .{});
999 defer in_file.close();
1000 var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };
1001 self.parsePositional(in_file, obj.path, obj.must_link, &parse_ctx) catch |err|
1002 try self.handleAndReportParseError(obj.path, err, &parse_ctx);
1003 }
1004
1005 var system_libs = std.ArrayList(SystemLib).init(arena);
1006
1007 // libc dep
1008 self.error_flags.missing_libc = false;
1009 if (self.base.options.link_libc) {
1010 if (self.base.options.libc_installation != null) {
1011 @panic("TODO explicit libc_installation");
1012 } else if (target.isGnuLibC()) {
1013 try system_libs.ensureUnusedCapacity(glibc.libs.len + 1);
1014 for (glibc.libs) |lib| {
1015 const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{
1016 comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
1017 });
1018 system_libs.appendAssumeCapacity(.{ .path = lib_path });
1019 }
1020 system_libs.appendAssumeCapacity(.{
1021 .path = try comp.get_libc_crt_file(arena, "libc_nonshared.a"),
1022 });
1023 } else if (target.isMusl()) {
1024 const path = try comp.get_libc_crt_file(arena, switch (self.base.options.link_mode) {
1025 .Static => "libc.a",
1026 .Dynamic => "libc.so",
1027 });
1028 try system_libs.append(.{ .path = path });
1029 } else {
1030 self.error_flags.missing_libc = true;
1031 }
1032 }
1033
1034 for (system_libs.items) |lib| {
1035 const in_file = try std.fs.cwd().openFile(lib.path, .{});
1036 defer in_file.close();
1037 var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };
1038 self.parseLibrary(in_file, lib, false, &parse_ctx) catch |err|
1039 try self.handleAndReportParseError(lib.path, err, &parse_ctx);
1040 }
1041
1042 // Finally, as the last input object add compiler_rt if any.
9961043 const compiler_rt_path: ?[]const u8 = blk: {
9971044 if (comp.compiler_rt_lib) |x| break :blk x.full_object_path;
9981045 if (comp.compiler_rt_obj) |x| break :blk x.full_object_path;
9991046 break :blk null;
10001047 };
10011048 if (compiler_rt_path) |path| {
1002 try positionals.append(.{ .path = path });
1003 }
1004
1005 for (positionals.items) |obj| {
1006 const in_file = try std.fs.cwd().openFile(obj.path, .{});
1049 const in_file = try std.fs.cwd().openFile(path, .{});
10071050 defer in_file.close();
1008
10091051 var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };
1010 self.parsePositional(in_file, obj.path, obj.must_link, &parse_ctx) catch |err|
1011 try self.handleAndReportParseError(obj.path, err, &parse_ctx);
1052 self.parsePositional(in_file, path, false, &parse_ctx) catch |err|
1053 try self.handleAndReportParseError(path, err, &parse_ctx);
10121054 }
10131055
10141056 // Handle any lazy symbols that were emitted by incremental compilation.
......@@ -1347,28 +1389,22 @@ fn parsePositional(
13471389 if (Object.isObject(in_file)) {
13481390 try self.parseObject(in_file, path, ctx);
13491391 } else {
1350 try self.parseLibrary(in_file, path, .{
1351 .path = null,
1352 .needed = false,
1353 .weak = false,
1354 }, must_link, ctx);
1392 try self.parseLibrary(in_file, .{ .path = path }, must_link, ctx);
13551393 }
13561394}
13571395
13581396fn parseLibrary(
13591397 self: *Elf,
13601398 in_file: std.fs.File,
1361 path: []const u8,
1362 lib: link.SystemLib,
1399 lib: SystemLib,
13631400 must_link: bool,
13641401 ctx: *ParseErrorCtx,
13651402) ParseError!void {
13661403 const tracy = trace(@src());
13671404 defer tracy.end();
1368 _ = lib;
13691405
13701406 if (Archive.isArchive(in_file)) {
1371 try self.parseArchive(in_file, path, must_link, ctx);
1407 try self.parseArchive(in_file, lib.path, must_link, ctx);
13721408 } else return error.UnknownFileType;
13731409}
13741410
......@@ -4149,6 +4185,11 @@ pub const null_sym = elf.Elf64_Sym{
41494185 .st_size = 0,
41504186};
41514187
4188const SystemLib = struct {
4189 needed: bool = false,
4190 path: []const u8,
4191};
4192
41524193const std = @import("std");
41534194const build_options = @import("build_options");
41544195const builtin = @import("builtin");
test/link/elf.zig+9-10
......@@ -6,18 +6,19 @@ pub fn build(b: *Build) void {
66 const elf_step = b.step("test-elf", "Run ELF tests");
77 b.default_step = elf_step;
88
9 const target: CrossTarget = .{
9 const musl_target = CrossTarget{
1010 .cpu_arch = .x86_64, // TODO relax this once ELF linker is able to handle other archs
1111 .os_tag = .linux,
12 .abi = .musl,
1213 };
1314
14 elf_step.dependOn(testLinkingZigStatic(b, .{ .target = target, .use_llvm = true }));
15 elf_step.dependOn(testLinkingZigStatic(b, .{ .target = target, .use_llvm = false }));
16 elf_step.dependOn(testLinkingCStatic(b, .{ .target = target, .use_llvm = true }));
17 // elf_step.dependOn(testLinkingCStatic(b, .{ .target = target, .use_llvm = false })); // TODO arocc
15 elf_step.dependOn(testLinkingZig(b, .{ .use_llvm = true }));
16 elf_step.dependOn(testLinkingZig(b, .{ .use_llvm = false }));
17 elf_step.dependOn(testLinkingC(b, .{ .target = musl_target, .use_llvm = true }));
18 // elf_step.dependOn(testLinkingC(b, .{ .target = musl_target, .use_llvm = false })); // TODO arocc
1819}
1920
20fn testLinkingZigStatic(b: *Build, opts: Options) *Step {
21fn testLinkingZig(b: *Build, opts: Options) *Step {
2122 const test_step = addTestStep(b, "linking-zig-static", opts);
2223
2324 const exe = addExecutable(b, opts);
......@@ -26,7 +27,6 @@ fn testLinkingZigStatic(b: *Build, opts: Options) *Step {
2627 \\ @import("std").debug.print("Hello World!\n", .{});
2728 \\}
2829 );
29 exe.linkage = .static;
3030
3131 const run = b.addRunArtifact(exe);
3232 run.expectStdErrEqual("Hello World!\n");
......@@ -44,7 +44,7 @@ fn testLinkingZigStatic(b: *Build, opts: Options) *Step {
4444 return test_step;
4545}
4646
47fn testLinkingCStatic(b: *Build, opts: Options) *Step {
47fn testLinkingC(b: *Build, opts: Options) *Step {
4848 const test_step = addTestStep(b, "linking-c-static", opts);
4949
5050 const exe = addExecutable(b, opts);
......@@ -56,7 +56,6 @@ fn testLinkingCStatic(b: *Build, opts: Options) *Step {
5656 \\}
5757 );
5858 exe.is_linking_libc = true;
59 exe.linkage = .static;
6059
6160 const run = b.addRunArtifact(exe);
6261 run.expectStdOutEqual("Hello World!\n");
......@@ -75,7 +74,7 @@ fn testLinkingCStatic(b: *Build, opts: Options) *Step {
7574}
7675
7776const Options = struct {
78 target: CrossTarget = .{ .os_tag = .linux },
77 target: CrossTarget = .{ .cpu_arch = .x86_64, .os_tag = .linux },
7978 optimize: std.builtin.OptimizeMode = .Debug,
8079 use_llvm: bool = true,
8180};