authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-10-04 13:06:26+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-10-16 19:33:04+02:00
logf1b9c365f2bcf4e2e00e221d34f43bb343367c4d
tree5c5706e7d80f7dfc88c38e0df6fd11a2d9e33708
parent976d4f51ccae088044ac90a89c3840db94f4ceb2

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


2 files changed, 135 insertions(+), 13 deletions(-)

src/link/Elf.zig+37-13
...@@ -1069,10 +1069,11 @@ pub fn flush(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) link...@@ -1069,10 +1069,11 @@ pub fn flush(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) link
1069 if (use_lld) {1069 if (use_lld) {
1070 return self.linkWithLLD(comp, prog_node);1070 return self.linkWithLLD(comp, prog_node);
1071 }1071 }
1072 switch (self.base.options.output_mode) {1072 if (self.base.options.output_mode == .Lib and self.isStatic()) {
1073 .Exe, .Obj => return self.flushModule(comp, prog_node),1073 // TODO writing static library files
1074 .Lib => return error.TODOImplementWritingLibFiles,1074 return error.TODOImplementWritingLibFiles;
1075 }1075 }
1076 try self.flushModule(comp, prog_node);
1076}1077}
10771078
1078pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void {1079pub 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...@@ -1098,21 +1099,44 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1098 const target = self.base.options.target;1099 const target = self.base.options.target;
1099 const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type.1100 const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type.
1100 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});1101 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
1102 const module_obj_path: ?[]const u8 = if (self.base.intermediary_basename) |path| blk: {
1103 if (fs.path.dirname(full_out_path)) |dirname| {
1104 break :blk try fs.path.join(arena, &.{ dirname, path });
1105 } else {
1106 break :blk path;
1107 }
1108 } else null;
1109
1110 if (self.base.options.output_mode == .Obj and self.zig_module_index == null) {
1111 // TODO this will become -r route I guess. For now, just copy the object file.
1112 const the_object_path = blk: {
1113 if (self.base.options.objects.len != 0) {
1114 break :blk self.base.options.objects[0].path;
1115 }
1116
1117 if (comp.c_object_table.count() != 0)
1118 break :blk comp.c_object_table.keys()[0].status.success.object_path;
1119
1120 if (module_obj_path) |p|
1121 break :blk p;
1122
1123 // TODO I think this is unreachable. Audit this situation when solving the above TODO
1124 // regarding eliding redundant object -> object transformations.
1125 return error.NoObjectsToLink;
1126 };
1127 // This can happen when using --enable-cache and using the stage1 backend. In this case
1128 // we can skip the file copy.
1129 if (!mem.eql(u8, the_object_path, full_out_path)) {
1130 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
1131 }
1132 return;
1133 }
11011134
1102 // Here we will parse input positional and library files (if referenced).1135 // Here we will parse input positional and library files (if referenced).
1103 // This will roughly match in any linker backend we support.1136 // This will roughly match in any linker backend we support.
1104 var positionals = std.ArrayList(Compilation.LinkObject).init(arena);1137 var positionals = std.ArrayList(Compilation.LinkObject).init(arena);
11051138
1106 if (self.base.intermediary_basename) |path| {1139 if (module_obj_path) |path| try positionals.append(.{ .path = path });
1107 const full_path = blk: {
1108 if (fs.path.dirname(full_out_path)) |dirname| {
1109 break :blk try fs.path.join(arena, &.{ dirname, path });
1110 } else {
1111 break :blk path;
1112 }
1113 };
1114 try positionals.append(.{ .path = full_path });
1115 }
11161140
1117 try positionals.ensureUnusedCapacity(self.base.options.objects.len);1141 try positionals.ensureUnusedCapacity(self.base.options.objects.len);
1118 positionals.appendSliceAssumeCapacity(self.base.options.objects);1142 positionals.appendSliceAssumeCapacity(self.base.options.objects);
test/link/elf.zig+98
...@@ -17,6 +17,7 @@ pub fn build(b: *Build) void {...@@ -17,6 +17,7 @@ pub fn build(b: *Build) void {
1717
18 // Exercise linker with LLVM backend18 // Exercise linker with LLVM backend
19 elf_step.dependOn(testEmptyObject(b, .{ .target = musl_target }));19 elf_step.dependOn(testEmptyObject(b, .{ .target = musl_target }));
20 elf_step.dependOn(testGcSections(b, .{ .target = musl_target }));
20 elf_step.dependOn(testLinkingC(b, .{ .target = musl_target }));21 elf_step.dependOn(testLinkingC(b, .{ .target = musl_target }));
21 elf_step.dependOn(testLinkingCpp(b, .{ .target = musl_target }));22 elf_step.dependOn(testLinkingCpp(b, .{ .target = musl_target }));
22 elf_step.dependOn(testLinkingZig(b, .{ .target = musl_target }));23 elf_step.dependOn(testLinkingZig(b, .{ .target = musl_target }));
...@@ -38,6 +39,93 @@ fn testEmptyObject(b: *Build, opts: Options) *Step {...@@ -38,6 +39,93 @@ fn testEmptyObject(b: *Build, opts: Options) *Step {
38 return test_step;39 return test_step;
39}40}
4041
42fn testGcSections(b: *Build, opts: Options) *Step {
43 const test_step = addTestStep(b, "gc-sections", opts);
44
45 const obj = addObject(b, opts);
46 addCppSourceBytes(obj,
47 \\#include <stdio.h>
48 \\int two() { return 2; }
49 \\int live_var1 = 1;
50 \\int live_var2 = two();
51 \\int dead_var1 = 3;
52 \\int dead_var2 = 4;
53 \\void live_fn1() {}
54 \\void live_fn2() { live_fn1(); }
55 \\void dead_fn1() {}
56 \\void dead_fn2() { dead_fn1(); }
57 \\int main() {
58 \\ printf("%d %d\n", live_var1, live_var2);
59 \\ live_fn2();
60 \\}
61 );
62 obj.link_function_sections = true;
63 obj.is_linking_libc = true;
64 obj.is_linking_libcpp = true;
65
66 {
67 const exe = addExecutable(b, opts);
68 exe.addObject(obj);
69 exe.link_gc_sections = false;
70 exe.is_linking_libc = true;
71 exe.is_linking_libcpp = true;
72
73 const run = addRunArtifact(exe);
74 run.expectStdOutEqual("1 2\n");
75 test_step.dependOn(&run.step);
76
77 const check = exe.checkObject();
78 check.checkInSymtab();
79 check.checkContains("live_var1");
80 check.checkInSymtab();
81 check.checkContains("live_var2");
82 check.checkInSymtab();
83 check.checkContains("dead_var1");
84 check.checkInSymtab();
85 check.checkContains("dead_var2");
86 check.checkInSymtab();
87 check.checkContains("live_fn1");
88 check.checkInSymtab();
89 check.checkContains("live_fn2");
90 check.checkInSymtab();
91 check.checkContains("dead_fn1");
92 check.checkInSymtab();
93 check.checkContains("dead_fn2");
94 test_step.dependOn(&check.step);
95 }
96
97 // {
98 // const exe = cc(b, opts);
99 // exe.addFileSource(obj_out.file);
100 // exe.addArg("-Wl,-gc-sections");
101
102 // const run = exe.run();
103 // run.expectStdOutEqual("1 2\n");
104 // test_step.dependOn(run.step());
105
106 // const check = exe.check();
107 // check.checkInSymtab();
108 // check.checkContains("live_var1");
109 // check.checkInSymtab();
110 // check.checkContains("live_var2");
111 // check.checkInSymtab();
112 // check.checkNotPresent("dead_var1");
113 // check.checkInSymtab();
114 // check.checkNotPresent("dead_var2");
115 // check.checkInSymtab();
116 // check.checkContains("live_fn1");
117 // check.checkInSymtab();
118 // check.checkContains("live_fn2");
119 // check.checkInSymtab();
120 // check.checkNotPresent("dead_fn1");
121 // check.checkInSymtab();
122 // check.checkNotPresent("dead_fn2");
123 // test_step.dependOn(&check.step);
124 // }
125
126 return test_step;
127}
128
41fn testLinkingC(b: *Build, opts: Options) *Step {129fn testLinkingC(b: *Build, opts: Options) *Step {
42 const test_step = addTestStep(b, "linking-c", opts);130 const test_step = addTestStep(b, "linking-c", opts);
43131
...@@ -182,6 +270,16 @@ fn addExecutable(b: *Build, opts: Options) *Compile {...@@ -182,6 +270,16 @@ fn addExecutable(b: *Build, opts: Options) *Compile {
182 });270 });
183}271}
184272
273fn addObject(b: *Build, opts: Options) *Compile {
274 return b.addObject(.{
275 .name = "a.o",
276 .target = opts.target,
277 .optimize = opts.optimize,
278 .use_llvm = opts.use_llvm,
279 .use_lld = false,
280 });
281}
282
185fn addRunArtifact(comp: *Compile) *Run {283fn addRunArtifact(comp: *Compile) *Run {
186 const b = comp.step.owner;284 const b = comp.step.owner;
187 const run = b.addRunArtifact(comp);285 const run = b.addRunArtifact(comp);