elf: add incomplete handling of build-obj -fllvm -fno-lld

This commit is contained in:
Jakub Konka
2023-10-04 13:06:26 +02:00
parent 976d4f51cc
commit f1b9c365f2
2 changed files with 135 additions and 13 deletions

View File

@@ -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);

View File

@@ -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);