authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-04 04:54:30-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-01-04 04:54:30-05:00
log5c228765f1094d30e64d13c0077c67b2867ecd6a
tree3987430030804d36895f680f5a5573db6b7e66cb
parent3c87d4e14ec6b0c2442372cbcc60174d654edddc
parentb6d6152e6514dcb4e67750cbb22b90b38ebedf49
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10504 from ziglang/linker-plumbing

linker: fix build-obj and -fno-emit-bin

7 files changed, 138 insertions(+), 73 deletions(-)

src/codegen/llvm.zig+14-18
......@@ -181,8 +181,6 @@ pub const Object = struct {
181181 /// The backing memory for `type_map`. Periodically garbage collected after flush().
182182 /// The code for doing the periodical GC is not yet implemented.
183183 type_map_arena: std.heap.ArenaAllocator,
184 /// Where to put the output object file, relative to bin_file.options.emit directory.
185 sub_path: []const u8,
186184
187185 pub const TypeMap = std.HashMapUnmanaged(
188186 Type,
......@@ -191,14 +189,14 @@ pub const Object = struct {
191189 std.hash_map.default_max_load_percentage,
192190 );
193191
194 pub fn create(gpa: Allocator, sub_path: []const u8, options: link.Options) !*Object {
192 pub fn create(gpa: Allocator, options: link.Options) !*Object {
195193 const obj = try gpa.create(Object);
196194 errdefer gpa.destroy(obj);
197 obj.* = try Object.init(gpa, sub_path, options);
195 obj.* = try Object.init(gpa, options);
198196 return obj;
199197 }
200198
201 pub fn init(gpa: Allocator, sub_path: []const u8, options: link.Options) !Object {
199 pub fn init(gpa: Allocator, options: link.Options) !Object {
202200 const context = llvm.Context.create();
203201 errdefer context.dispose();
204202
......@@ -271,7 +269,6 @@ pub const Object = struct {
271269 .decl_map = .{},
272270 .type_map = .{},
273271 .type_map_arena = std.heap.ArenaAllocator.init(gpa),
274 .sub_path = sub_path,
275272 };
276273 }
277274
......@@ -324,19 +321,22 @@ pub const Object = struct {
324321 const mod = comp.bin_file.options.module.?;
325322 const cache_dir = mod.zig_cache_artifact_directory;
326323
327 const emit_bin_path: ?[*:0]const u8 = if (comp.bin_file.options.emit) |emit| blk: {
328 const full_out_path = try emit.directory.join(arena, &[_][]const u8{emit.sub_path});
329 break :blk try std.fs.path.joinZ(arena, &.{
330 std.fs.path.dirname(full_out_path).?, self.sub_path,
331 });
332 } else null;
324 const emit_bin_path: ?[*:0]const u8 = if (comp.bin_file.options.emit) |emit|
325 try emit.basenamePath(arena, try arena.dupeZ(u8, comp.bin_file.intermediary_basename.?))
326 else
327 null;
333328
334329 const emit_asm_path = try locPath(arena, comp.emit_asm, cache_dir);
335330 const emit_llvm_ir_path = try locPath(arena, comp.emit_llvm_ir, cache_dir);
336331 const emit_llvm_bc_path = try locPath(arena, comp.emit_llvm_bc, cache_dir);
337332
338 const debug_emit_path = emit_bin_path orelse "(none)";
339 log.debug("emit LLVM object to {s}", .{debug_emit_path});
333 const emit_asm_msg = emit_asm_path orelse "(none)";
334 const emit_bin_msg = emit_bin_path orelse "(none)";
335 const emit_llvm_ir_msg = emit_llvm_ir_path orelse "(none)";
336 const emit_llvm_bc_msg = emit_llvm_bc_path orelse "(none)";
337 log.debug("emit LLVM object asm={s} bin={s} ir={s} bc={s}", .{
338 emit_asm_msg, emit_bin_msg, emit_llvm_ir_msg, emit_llvm_bc_msg,
339 });
340340
341341 var error_message: [*:0]const u8 = undefined;
342342 if (self.target_machine.emitToFile(
......@@ -354,10 +354,6 @@ pub const Object = struct {
354354 )) {
355355 defer llvm.disposeMessage(error_message);
356356
357 const emit_asm_msg = emit_asm_path orelse "(none)";
358 const emit_bin_msg = emit_bin_path orelse "(none)";
359 const emit_llvm_ir_msg = emit_llvm_ir_path orelse "(none)";
360 const emit_llvm_bc_msg = emit_llvm_bc_path orelse "(none)";
361357 log.err("LLVM failed to emit asm={s} bin={s} ir={s} bc={s}: {s}", .{
362358 emit_asm_msg, emit_bin_msg, emit_llvm_ir_msg, emit_llvm_bc_msg,
363359 error_message,
src/glibc.zig+2-2
......@@ -225,7 +225,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
225225 });
226226 },
227227 .scrt1_o => {
228 const start_os: Compilation.CSourceFile = blk: {
228 const start_o: Compilation.CSourceFile = blk: {
229229 var args = std.ArrayList([]const u8).init(arena);
230230 try add_include_dirs(comp, arena, &args);
231231 try args.appendSlice(&[_][]const u8{
......@@ -266,7 +266,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
266266 .extra_flags = args.items,
267267 };
268268 };
269 return comp.build_crt_file("Scrt1", .Obj, &[_]Compilation.CSourceFile{ start_os, abi_note_o });
269 return comp.build_crt_file("Scrt1", .Obj, &[_]Compilation.CSourceFile{ start_o, abi_note_o });
270270 },
271271 .libc_nonshared_a => {
272272 const target = comp.getTarget();
src/link.zig+16-2
......@@ -43,6 +43,21 @@ pub const Emit = struct {
4343 directory: Compilation.Directory,
4444 /// Path to the output file, relative to `directory`.
4545 sub_path: []const u8,
46
47 /// Returns the full path to `basename` if it were in the same directory as the
48 /// `Emit` sub_path.
49 pub fn basenamePath(emit: Emit, arena: Allocator, basename: [:0]const u8) ![:0]const u8 {
50 const full_path = if (emit.directory.path) |p|
51 try fs.path.join(arena, &[_][]const u8{ p, emit.sub_path })
52 else
53 emit.sub_path;
54
55 if (fs.path.dirname(full_path)) |dirname| {
56 return try fs.path.joinZ(arena, &.{ dirname, basename });
57 } else {
58 return basename;
59 }
60 }
4661};
4762
4863pub const Options = struct {
......@@ -533,9 +548,8 @@ pub const File = struct {
533548 /// Commit pending changes and write headers. Takes into account final output mode
534549 /// and `use_lld`, not only `effectiveOutputMode`.
535550 pub fn flush(base: *File, comp: *Compilation) !void {
536 const emit = base.options.emit orelse return; // -fno-emit-bin
537
538551 if (comp.clang_preprocessor_mode == .yes) {
552 const emit = base.options.emit orelse return; // -fno-emit-bin
539553 // TODO: avoid extra link step when it's just 1 object file (the `zig cc -c` case)
540554 // Until then, we do `lld -r -o output.o input.o` even though the output is the same
541555 // as the input. For the preprocessing case (`zig cc -E -o foo`) we copy the file
src/link/Coff.zig+21-8
......@@ -129,11 +129,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
129129 assert(options.object_format == .coff);
130130
131131 if (build_options.have_llvm and options.use_llvm) {
132 const self = try createEmpty(allocator, options);
133 errdefer self.base.destroy();
134
135 self.llvm_object = try LlvmObject.create(allocator, sub_path, options);
136 return self;
132 return createEmpty(allocator, options);
137133 }
138134
139135 const file = try options.emit.?.directory.handle.createFile(sub_path, .{
......@@ -403,6 +399,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff {
403399 else => return error.UnsupportedCOFFArchitecture,
404400 };
405401 const self = try gpa.create(Coff);
402 errdefer gpa.destroy(self);
406403 self.* = .{
407404 .base = .{
408405 .tag = .coff,
......@@ -412,6 +409,12 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff {
412409 },
413410 .ptr_width = ptr_width,
414411 };
412
413 const use_llvm = build_options.have_llvm and options.use_llvm;
414 const use_stage1 = build_options.is_stage1 and options.use_stage1;
415 if (use_llvm and !use_stage1) {
416 self.llvm_object = try LlvmObject.create(gpa, options);
417 }
415418 return self;
416419}
417420
......@@ -817,6 +820,14 @@ pub fn updateDeclExports(
817820}
818821
819822pub fn flush(self: *Coff, comp: *Compilation) !void {
823 if (self.base.options.emit == null) {
824 if (build_options.have_llvm) {
825 if (self.llvm_object) |llvm_object| {
826 return try llvm_object.flushModule(comp);
827 }
828 }
829 return;
830 }
820831 if (build_options.have_llvm and self.base.options.use_lld) {
821832 return self.linkWithLLD(comp);
822833 } else {
......@@ -905,9 +916,11 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
905916
906917 try self.flushModule(comp);
907918
908 break :blk try fs.path.join(arena, &.{
909 fs.path.dirname(full_out_path).?, self.base.intermediary_basename.?,
910 });
919 if (fs.path.dirname(full_out_path)) |dirname| {
920 break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? });
921 } else {
922 break :blk self.base.intermediary_basename.?;
923 }
911924 } else null;
912925
913926 const is_lib = self.base.options.output_mode == .Lib;
src/link/Elf.zig+38-25
......@@ -241,11 +241,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
241241 assert(options.object_format == .elf);
242242
243243 if (build_options.have_llvm and options.use_llvm) {
244 const self = try createEmpty(allocator, options);
245 errdefer self.base.destroy();
246
247 self.llvm_object = try LlvmObject.create(allocator, sub_path, options);
248 return self;
244 return createEmpty(allocator, options);
249245 }
250246
251247 const file = try options.emit.?.directory.handle.createFile(sub_path, .{
......@@ -298,6 +294,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {
298294 };
299295 const self = try gpa.create(Elf);
300296 errdefer gpa.destroy(self);
297
301298 self.* = .{
302299 .base = .{
303300 .tag = .elf,
......@@ -307,9 +304,11 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {
307304 },
308305 .ptr_width = ptr_width,
309306 };
310 // TODO get rid of the sub_path parameter to LlvmObject.create
311 // and create the llvm_object here. Also openPath needs to
312 // not override this field or there will be a memory leak.
307 const use_llvm = build_options.have_llvm and options.use_llvm;
308 const use_stage1 = build_options.is_stage1 and options.use_stage1;
309 if (use_llvm and !use_stage1) {
310 self.llvm_object = try LlvmObject.create(gpa, options);
311 }
313312 return self;
314313}
315314
......@@ -788,14 +787,21 @@ pub const abbrev_pad1 = 5;
788787pub const abbrev_parameter = 6;
789788
790789pub fn flush(self: *Elf, comp: *Compilation) !void {
791 if (build_options.have_llvm and self.base.options.use_lld) {
792 return self.linkWithLLD(comp);
793 } else {
794 switch (self.base.options.effectiveOutputMode()) {
795 .Exe, .Obj => {},
796 .Lib => return error.TODOImplementWritingLibFiles,
790 if (self.base.options.emit == null) {
791 if (build_options.have_llvm) {
792 if (self.llvm_object) |llvm_object| {
793 return try llvm_object.flushModule(comp);
794 }
797795 }
798 return self.flushModule(comp);
796 return;
797 }
798 const use_lld = build_options.have_llvm and self.base.options.use_lld;
799 if (use_lld) {
800 return self.linkWithLLD(comp);
801 }
802 switch (self.base.options.output_mode) {
803 .Exe, .Obj => return self.flushModule(comp),
804 .Lib => return error.TODOImplementWritingLibFiles,
799805 }
800806}
801807
......@@ -803,8 +809,11 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
803809 const tracy = trace(@src());
804810 defer tracy.end();
805811
806 if (build_options.have_llvm)
807 if (self.llvm_object) |llvm_object| return try llvm_object.flushModule(comp);
812 if (build_options.have_llvm) {
813 if (self.llvm_object) |llvm_object| {
814 return try llvm_object.flushModule(comp);
815 }
816 }
808817
809818 // TODO This linker code currently assumes there is only 1 compilation unit and it
810819 // corresponds to the Zig source code.
......@@ -1327,9 +1336,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
13271336
13281337 try self.flushModule(comp);
13291338
1330 break :blk try fs.path.join(arena, &.{
1331 fs.path.dirname(full_out_path).?, self.base.intermediary_basename.?,
1332 });
1339 if (fs.path.dirname(full_out_path)) |dirname| {
1340 break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? });
1341 } else {
1342 break :blk self.base.intermediary_basename.?;
1343 }
13331344 } else null;
13341345
13351346 const is_obj = self.base.options.output_mode == .Obj;
......@@ -1446,10 +1457,13 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
14461457 };
14471458 }
14481459
1449 // Due to a deficiency in LLD, we need to special-case BPF to a simple file copy when generating
1450 // relocatables. Normally, we would expect `lld -r` to work. However, because LLD wants to resolve
1451 // BPF relocations which it shouldn't, it fails before even generating the relocatable.
1452 if (self.base.options.output_mode == .Obj and (self.base.options.lto or target.isBpfFreestanding())) {
1460 // Due to a deficiency in LLD, we need to special-case BPF to a simple file
1461 // copy when generating relocatables. Normally, we would expect `lld -r` to work.
1462 // However, because LLD wants to resolve BPF relocations which it shouldn't, it fails
1463 // before even generating the relocatable.
1464 if (self.base.options.output_mode == .Obj and
1465 (self.base.options.lto or target.isBpfFreestanding()))
1466 {
14531467 // In this case we must do a simple file copy
14541468 // here. TODO: think carefully about how we can avoid this redundant operation when doing
14551469 // build-obj. See also the corresponding TODO in linkAsArchive.
......@@ -1473,7 +1487,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
14731487 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
14741488 }
14751489 } else {
1476
14771490 // Create an LLD command line and invoke it.
14781491 var argv = std.ArrayList([]const u8).init(self.base.allocator);
14791492 defer argv.deinit();
src/link/MachO.zig+24-7
......@@ -313,11 +313,10 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
313313 // TODO this intermediary_basename isn't enough; in the case of `zig build-exe`,
314314 // we also want to put the intermediary object file in the cache while the
315315 // main emit directory is the cwd.
316 const sub_path = try std.fmt.allocPrint(allocator, "{s}{s}", .{
316 self.llvm_object = try LlvmObject.create(allocator, options);
317 self.base.intermediary_basename = try std.fmt.allocPrint(allocator, "{s}{s}", .{
317318 emit.sub_path, options.object_format.fileExt(options.target.cpu.arch),
318319 });
319 self.llvm_object = try LlvmObject.create(allocator, sub_path, options);
320 self.base.intermediary_basename = sub_path;
321320 }
322321
323322 if (options.output_mode == .Lib and
......@@ -373,7 +372,6 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
373372}
374373
375374pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
376 const self = try gpa.create(MachO);
377375 const cpu_arch = options.target.cpu.arch;
378376 const os_tag = options.target.os.tag;
379377 const abi = options.target.abi;
......@@ -383,6 +381,9 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
383381 const requires_adhoc_codesig = cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator);
384382 const needs_prealloc = !(build_options.is_stage1 and options.use_stage1);
385383
384 const self = try gpa.create(MachO);
385 errdefer gpa.destroy(self);
386
386387 self.* = .{
387388 .base = .{
388389 .tag = .macho,
......@@ -395,10 +396,24 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
395396 .needs_prealloc = needs_prealloc,
396397 };
397398
399 const use_llvm = build_options.have_llvm and options.use_llvm;
400 const use_stage1 = build_options.is_stage1 and options.use_stage1;
401 if (use_llvm and !use_stage1) {
402 self.llvm_object = try LlvmObject.create(gpa, options);
403 }
404
398405 return self;
399406}
400407
401408pub fn flush(self: *MachO, comp: *Compilation) !void {
409 if (self.base.options.emit == null) {
410 if (build_options.have_llvm) {
411 if (self.llvm_object) |llvm_object| {
412 return try llvm_object.flushModule(comp);
413 }
414 }
415 return;
416 }
402417 if (self.base.options.output_mode == .Lib and self.base.options.link_mode == .Static) {
403418 if (build_options.have_llvm) {
404419 return self.base.linkAsArchive(comp);
......@@ -449,9 +464,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
449464
450465 try self.flushObject(comp);
451466
452 break :blk try fs.path.join(arena, &.{
453 fs.path.dirname(full_out_path).?, obj_basename,
454 });
467 if (fs.path.dirname(full_out_path)) |dirname| {
468 break :blk try fs.path.join(arena, &.{ dirname, obj_basename });
469 } else {
470 break :blk obj_basename;
471 }
455472 } else null;
456473
457474 const is_lib = self.base.options.output_mode == .Lib;
src/link/Wasm.zig+23-11
......@@ -101,11 +101,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
101101 assert(options.object_format == .wasm);
102102
103103 if (build_options.have_llvm and options.use_llvm) {
104 const self = try createEmpty(allocator, options);
105 errdefer self.base.destroy();
106
107 self.llvm_object = try LlvmObject.create(allocator, sub_path, options);
108 return self;
104 return createEmpty(allocator, options);
109105 }
110106
111107 // TODO: read the file and keep valid parts instead of truncating
......@@ -139,8 +135,9 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
139135}
140136
141137pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm {
142 const wasm_bin = try gpa.create(Wasm);
143 wasm_bin.* = .{
138 const self = try gpa.create(Wasm);
139 errdefer gpa.destroy(self);
140 self.* = .{
144141 .base = .{
145142 .tag = .wasm,
146143 .options = options,
......@@ -148,7 +145,12 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm {
148145 .allocator = gpa,
149146 },
150147 };
151 return wasm_bin;
148 const use_llvm = build_options.have_llvm and options.use_llvm;
149 const use_stage1 = build_options.is_stage1 and options.use_stage1;
150 if (use_llvm and !use_stage1) {
151 self.llvm_object = try LlvmObject.create(gpa, options);
152 }
153 return self;
152154}
153155
154156pub fn deinit(self: *Wasm) void {
......@@ -576,6 +578,14 @@ fn resetState(self: *Wasm) void {
576578}
577579
578580pub fn flush(self: *Wasm, comp: *Compilation) !void {
581 if (self.base.options.emit == null) {
582 if (build_options.have_llvm) {
583 if (self.llvm_object) |llvm_object| {
584 return try llvm_object.flushModule(comp);
585 }
586 }
587 return;
588 }
579589 if (build_options.have_llvm and self.base.options.use_lld) {
580590 return self.linkWithLLD(comp);
581591 } else {
......@@ -1075,9 +1085,11 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
10751085
10761086 try self.flushModule(comp);
10771087
1078 break :blk try fs.path.join(arena, &.{
1079 fs.path.dirname(full_out_path).?, self.base.intermediary_basename.?,
1080 });
1088 if (fs.path.dirname(full_out_path)) |dirname| {
1089 break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? });
1090 } else {
1091 break :blk self.base.intermediary_basename.?;
1092 }
10811093 } else null;
10821094
10831095 const is_obj = self.base.options.output_mode == .Obj;