diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2023-10-04 13:06:26 +0200 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2023-10-16 19:33:04 +0200 |
| commit | f1b9c365f2bcf4e2e00e221d34f43bb343367c4d (patch) | |
| tree | 5c5706e7d80f7dfc88c38e0df6fd11a2d9e33708 | |
| parent | 976d4f51ccae088044ac90a89c3840db94f4ceb2 (diff) | |
| download | zig-f1b9c365f2bcf4e2e00e221d34f43bb343367c4d.tar.gz zig-f1b9c365f2bcf4e2e00e221d34f43bb343367c4d.zip | |
elf: add incomplete handling of build-obj -fllvm -fno-lld
| -rw-r--r-- | src/link/Elf.zig | 50 | ||||
| -rw-r--r-- | test/link/elf.zig | 98 |
2 files changed, 135 insertions, 13 deletions
diff --git a/src/link/Elf.zig b/src/link/Elf.zig index b4b473e17c..f98465bd52 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1069,10 +1069,11 @@ pub fn flush(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) link if (use_lld) { return self.linkWithLLD(comp, prog_node); } - switch (self.base.options.output_mode) { - .Exe, .Obj => return self.flushModule(comp, prog_node), - .Lib => return error.TODOImplementWritingLibFiles, + if (self.base.options.output_mode == .Lib and self.isStatic()) { + // TODO writing static library files + return error.TODOImplementWritingLibFiles; } + try self.flushModule(comp, prog_node); } pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { @@ -1098,21 +1099,44 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node 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}); + const module_obj_path: ?[]const u8 = if (self.base.intermediary_basename) |path| blk: { + if (fs.path.dirname(full_out_path)) |dirname| { + break :blk try fs.path.join(arena, &.{ dirname, path }); + } else { + break :blk path; + } + } else null; + + if (self.base.options.output_mode == .Obj and self.zig_module_index == null) { + // TODO this will become -r route I guess. For now, just copy the object file. + const the_object_path = blk: { + if (self.base.options.objects.len != 0) { + break :blk self.base.options.objects[0].path; + } + + if (comp.c_object_table.count() != 0) + break :blk comp.c_object_table.keys()[0].status.success.object_path; + + if (module_obj_path) |p| + break :blk p; + + // TODO I think this is unreachable. Audit this situation when solving the above TODO + // regarding eliding redundant object -> object transformations. + return error.NoObjectsToLink; + }; + // This can happen when using --enable-cache and using the stage1 backend. In this case + // we can skip the file copy. + if (!mem.eql(u8, the_object_path, full_out_path)) { + try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{}); + } + return; + } // Here we will parse input positional and library files (if referenced). // This will roughly match in any linker backend we support. var positionals = std.ArrayList(Compilation.LinkObject).init(arena); - if (self.base.intermediary_basename) |path| { - const full_path = blk: { - if (fs.path.dirname(full_out_path)) |dirname| { - break :blk try fs.path.join(arena, &.{ dirname, path }); - } else { - break :blk path; - } - }; - try positionals.append(.{ .path = full_path }); - } + if (module_obj_path) |path| try positionals.append(.{ .path = path }); try positionals.ensureUnusedCapacity(self.base.options.objects.len); positionals.appendSliceAssumeCapacity(self.base.options.objects); diff --git a/test/link/elf.zig b/test/link/elf.zig index 8b70249ad8..6c8e03a21c 100644 --- a/test/link/elf.zig +++ b/test/link/elf.zig @@ -17,6 +17,7 @@ pub fn build(b: *Build) void { // Exercise linker with LLVM backend elf_step.dependOn(testEmptyObject(b, .{ .target = musl_target })); + elf_step.dependOn(testGcSections(b, .{ .target = musl_target })); elf_step.dependOn(testLinkingC(b, .{ .target = musl_target })); elf_step.dependOn(testLinkingCpp(b, .{ .target = musl_target })); elf_step.dependOn(testLinkingZig(b, .{ .target = musl_target })); @@ -38,6 +39,93 @@ fn testEmptyObject(b: *Build, opts: Options) *Step { return test_step; } +fn testGcSections(b: *Build, opts: Options) *Step { + const test_step = addTestStep(b, "gc-sections", opts); + + const obj = addObject(b, opts); + addCppSourceBytes(obj, + \\#include <stdio.h> + \\int two() { return 2; } + \\int live_var1 = 1; + \\int live_var2 = two(); + \\int dead_var1 = 3; + \\int dead_var2 = 4; + \\void live_fn1() {} + \\void live_fn2() { live_fn1(); } + \\void dead_fn1() {} + \\void dead_fn2() { dead_fn1(); } + \\int main() { + \\ printf("%d %d\n", live_var1, live_var2); + \\ live_fn2(); + \\} + ); + obj.link_function_sections = true; + obj.is_linking_libc = true; + obj.is_linking_libcpp = true; + + { + const exe = addExecutable(b, opts); + exe.addObject(obj); + exe.link_gc_sections = false; + exe.is_linking_libc = true; + exe.is_linking_libcpp = true; + + const run = addRunArtifact(exe); + run.expectStdOutEqual("1 2\n"); + test_step.dependOn(&run.step); + + const check = exe.checkObject(); + check.checkInSymtab(); + check.checkContains("live_var1"); + check.checkInSymtab(); + check.checkContains("live_var2"); + check.checkInSymtab(); + check.checkContains("dead_var1"); + check.checkInSymtab(); + check.checkContains("dead_var2"); + check.checkInSymtab(); + check.checkContains("live_fn1"); + check.checkInSymtab(); + check.checkContains("live_fn2"); + check.checkInSymtab(); + check.checkContains("dead_fn1"); + check.checkInSymtab(); + check.checkContains("dead_fn2"); + test_step.dependOn(&check.step); + } + + // { + // const exe = cc(b, opts); + // exe.addFileSource(obj_out.file); + // exe.addArg("-Wl,-gc-sections"); + + // const run = exe.run(); + // run.expectStdOutEqual("1 2\n"); + // test_step.dependOn(run.step()); + + // const check = exe.check(); + // check.checkInSymtab(); + // check.checkContains("live_var1"); + // check.checkInSymtab(); + // check.checkContains("live_var2"); + // check.checkInSymtab(); + // check.checkNotPresent("dead_var1"); + // check.checkInSymtab(); + // check.checkNotPresent("dead_var2"); + // check.checkInSymtab(); + // check.checkContains("live_fn1"); + // check.checkInSymtab(); + // check.checkContains("live_fn2"); + // check.checkInSymtab(); + // check.checkNotPresent("dead_fn1"); + // check.checkInSymtab(); + // check.checkNotPresent("dead_fn2"); + // test_step.dependOn(&check.step); + // } + + return test_step; +} + fn testLinkingC(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "linking-c", opts); @@ -182,6 +270,16 @@ fn addExecutable(b: *Build, opts: Options) *Compile { }); } +fn addObject(b: *Build, opts: Options) *Compile { + return b.addObject(.{ + .name = "a.o", + .target = opts.target, + .optimize = opts.optimize, + .use_llvm = opts.use_llvm, + .use_lld = false, + }); +} + fn addRunArtifact(comp: *Compile) *Run { const b = comp.step.owner; const run = b.addRunArtifact(comp); |
