Commit f1b9c365f2
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);
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);