Commit 5e617e4b0c

Jakub Konka <kubkon@jakubkonka.com>
2023-09-25 17:35:48
elf: put libc on the linker line if requested
1 parent e7c6dfd
Changed files (2)
src
link
test
link
src/link/Elf.zig
@@ -965,6 +965,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
     defer arena_allocator.deinit();
     const arena = arena_allocator.allocator();
 
+    const target = self.base.options.target;
     const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type.
     const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
 
@@ -993,22 +994,63 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
         try positionals.append(.{ .path = key.status.success.object_path });
     }
 
+    for (positionals.items) |obj| {
+        const in_file = try std.fs.cwd().openFile(obj.path, .{});
+        defer in_file.close();
+        var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };
+        self.parsePositional(in_file, obj.path, obj.must_link, &parse_ctx) catch |err|
+            try self.handleAndReportParseError(obj.path, err, &parse_ctx);
+    }
+
+    var system_libs = std.ArrayList(SystemLib).init(arena);
+
+    // 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");
+        } else if (target.isGnuLibC()) {
+            try system_libs.ensureUnusedCapacity(glibc.libs.len + 1);
+            for (glibc.libs) |lib| {
+                const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{
+                    comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
+                });
+                system_libs.appendAssumeCapacity(.{ .path = lib_path });
+            }
+            system_libs.appendAssumeCapacity(.{
+                .path = try comp.get_libc_crt_file(arena, "libc_nonshared.a"),
+            });
+        } else if (target.isMusl()) {
+            const path = try comp.get_libc_crt_file(arena, switch (self.base.options.link_mode) {
+                .Static => "libc.a",
+                .Dynamic => "libc.so",
+            });
+            try system_libs.append(.{ .path = path });
+        } else {
+            self.error_flags.missing_libc = true;
+        }
+    }
+
+    for (system_libs.items) |lib| {
+        const in_file = try std.fs.cwd().openFile(lib.path, .{});
+        defer in_file.close();
+        var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };
+        self.parseLibrary(in_file, lib, false, &parse_ctx) catch |err|
+            try self.handleAndReportParseError(lib.path, err, &parse_ctx);
+    }
+
+    // Finally, as the last input object add compiler_rt if any.
     const compiler_rt_path: ?[]const u8 = blk: {
         if (comp.compiler_rt_lib) |x| break :blk x.full_object_path;
         if (comp.compiler_rt_obj) |x| break :blk x.full_object_path;
         break :blk null;
     };
     if (compiler_rt_path) |path| {
-        try positionals.append(.{ .path = path });
-    }
-
-    for (positionals.items) |obj| {
-        const in_file = try std.fs.cwd().openFile(obj.path, .{});
+        const in_file = try std.fs.cwd().openFile(path, .{});
         defer in_file.close();
-
         var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };
-        self.parsePositional(in_file, obj.path, obj.must_link, &parse_ctx) catch |err|
-            try self.handleAndReportParseError(obj.path, err, &parse_ctx);
+        self.parsePositional(in_file, path, false, &parse_ctx) catch |err|
+            try self.handleAndReportParseError(path, err, &parse_ctx);
     }
 
     // Handle any lazy symbols that were emitted by incremental compilation.
@@ -1347,28 +1389,22 @@ fn parsePositional(
     if (Object.isObject(in_file)) {
         try self.parseObject(in_file, path, ctx);
     } else {
-        try self.parseLibrary(in_file, path, .{
-            .path = null,
-            .needed = false,
-            .weak = false,
-        }, must_link, ctx);
+        try self.parseLibrary(in_file, .{ .path = path }, must_link, ctx);
     }
 }
 
 fn parseLibrary(
     self: *Elf,
     in_file: std.fs.File,
-    path: []const u8,
-    lib: link.SystemLib,
+    lib: SystemLib,
     must_link: bool,
     ctx: *ParseErrorCtx,
 ) ParseError!void {
     const tracy = trace(@src());
     defer tracy.end();
-    _ = lib;
 
     if (Archive.isArchive(in_file)) {
-        try self.parseArchive(in_file, path, must_link, ctx);
+        try self.parseArchive(in_file, lib.path, must_link, ctx);
     } else return error.UnknownFileType;
 }
 
@@ -4149,6 +4185,11 @@ pub const null_sym = elf.Elf64_Sym{
     .st_size = 0,
 };
 
+const SystemLib = struct {
+    needed: bool = false,
+    path: []const u8,
+};
+
 const std = @import("std");
 const build_options = @import("build_options");
 const builtin = @import("builtin");
test/link/elf.zig
@@ -6,18 +6,19 @@ pub fn build(b: *Build) void {
     const elf_step = b.step("test-elf", "Run ELF tests");
     b.default_step = elf_step;
 
-    const target: CrossTarget = .{
+    const musl_target = CrossTarget{
         .cpu_arch = .x86_64, // TODO relax this once ELF linker is able to handle other archs
         .os_tag = .linux,
+        .abi = .musl,
     };
 
-    elf_step.dependOn(testLinkingZigStatic(b, .{ .target = target, .use_llvm = true }));
-    elf_step.dependOn(testLinkingZigStatic(b, .{ .target = target, .use_llvm = false }));
-    elf_step.dependOn(testLinkingCStatic(b, .{ .target = target, .use_llvm = true }));
-    // elf_step.dependOn(testLinkingCStatic(b, .{ .target = target, .use_llvm = false })); // TODO arocc
+    elf_step.dependOn(testLinkingZig(b, .{ .use_llvm = true }));
+    elf_step.dependOn(testLinkingZig(b, .{ .use_llvm = false }));
+    elf_step.dependOn(testLinkingC(b, .{ .target = musl_target, .use_llvm = true }));
+    // elf_step.dependOn(testLinkingC(b, .{ .target = musl_target, .use_llvm = false })); // TODO arocc
 }
 
-fn testLinkingZigStatic(b: *Build, opts: Options) *Step {
+fn testLinkingZig(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "linking-zig-static", opts);
 
     const exe = addExecutable(b, opts);
@@ -26,7 +27,6 @@ fn testLinkingZigStatic(b: *Build, opts: Options) *Step {
         \\    @import("std").debug.print("Hello World!\n", .{});
         \\}
     );
-    exe.linkage = .static;
 
     const run = b.addRunArtifact(exe);
     run.expectStdErrEqual("Hello World!\n");
@@ -44,7 +44,7 @@ fn testLinkingZigStatic(b: *Build, opts: Options) *Step {
     return test_step;
 }
 
-fn testLinkingCStatic(b: *Build, opts: Options) *Step {
+fn testLinkingC(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "linking-c-static", opts);
 
     const exe = addExecutable(b, opts);
@@ -56,7 +56,6 @@ fn testLinkingCStatic(b: *Build, opts: Options) *Step {
         \\}
     );
     exe.is_linking_libc = true;
-    exe.linkage = .static;
 
     const run = b.addRunArtifact(exe);
     run.expectStdOutEqual("Hello World!\n");
@@ -75,7 +74,7 @@ fn testLinkingCStatic(b: *Build, opts: Options) *Step {
 }
 
 const Options = struct {
-    target: CrossTarget = .{ .os_tag = .linux },
+    target: CrossTarget = .{ .cpu_arch = .x86_64, .os_tag = .linux },
     optimize: std.builtin.OptimizeMode = .Debug,
     use_llvm: bool = true,
 };