From b36659c972fb7d75b1e34cb668770353de27db7d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 25 Dec 2023 00:01:52 -0700 Subject: [PATCH] fix MachO linking * fix relationship between createEmpty/open (similar logic as 607111aa758002bc51914b7dc800b23927c931b8) * still resolve the start symbol when linking libc because when zig is the linker it still needs to know the entry symbol. * make use_llvm=false when there is no zig compilation unit. --- src/Compilation/Config.zig | 12 +-- src/link/Coff.zig | 4 +- src/link/Elf.zig | 4 +- src/link/MachO.zig | 199 +++++++++++++++++-------------------- src/link/Wasm.zig | 4 +- 5 files changed, 103 insertions(+), 120 deletions(-) diff --git a/src/Compilation/Config.zig b/src/Compilation/Config.zig index 811d37a2db9fa6a04cb38fee435ba800b1c2d0e8..367187b6588dc15916639d7746c5e63ce82f2cd3 100644 --- a/src/Compilation/Config.zig +++ b/src/Compilation/Config.zig @@ -178,6 +178,9 @@ pub fn resolve(options: Options) ResolveError!Config { // For example, Zig can emit .bc and .ll files directly, and this is still considered // using "the LLVM backend". const use_llvm = b: { + // If we have no zig code to compile, no need for LLVM. + if (!options.have_zcu) break :b false; + // If emitting to LLVM bitcode object format, must use LLVM backend. if (options.emit_llvm_ir or options.emit_llvm_bc) { if (options.use_llvm == false) @@ -202,13 +205,10 @@ pub fn resolve(options: Options) ResolveError!Config { if (options.use_llvm) |x| break :b x; - // If we have no zig code to compile, no need for LLVM. - if (!options.have_zcu) break :b false; - // If we cannot use LLVM libraries, then our own backends will be a // better default since the LLVM backend can only produce bitcode // and not an object file or executable. - if (!use_lib_llvm) break :b false; + if (!use_lib_llvm and options.emit_bin) break :b false; // Prefer LLVM for release builds. if (root_optimize_mode != .Debug) break :b true; @@ -339,10 +339,6 @@ pub fn resolve(options: Options) ResolveError!Config { .default => b: { if (options.output_mode != .Exe) break :b null; - // When linking libc, the entry point is inside libc and not in the - // zig compilation unit. - if (link_libc) break :b null; - // When producing C source code, the decision of entry point is made // when compiling the C code, not when producing the C code. if (target.ofmt == .c) break :b null; diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 6db49713fcefe3085be8ab79868422bd05163785..861ec030b36d73f1e553fdbf7275d2f2694b478d 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -320,8 +320,8 @@ pub fn createEmpty( } errdefer self.base.destroy(); - if (use_lld and use_llvm) { - // LLVM emits the object file; LLD links it into the final product. + if (use_lld and (use_llvm or !comp.config.have_zcu)) { + // LLVM emits the object file (if any); LLD links it into the final product. return self; } diff --git a/src/link/Elf.zig b/src/link/Elf.zig index eb08fe2855caeaaf162f3ab388cb088730183330..378c8cbd5a65c30ad5e5045712552363589f2d4c 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -328,8 +328,8 @@ pub fn createEmpty( } errdefer self.base.destroy(); - if (use_lld and use_llvm) { - // LLVM emits the object file; LLD links it into the final product. + if (use_lld and (use_llvm or !comp.config.have_zcu)) { + // LLVM emits the object file (if any); LLD links it into the final product. return self; } diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 0964859c284acdddd05de60ceb85a48d75eb07df..d8fc71c0f07bec8570dcedd62e04aed91fce70fd 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -174,113 +174,35 @@ pub const SdkLayout = enum { vendored, }; -pub fn open( - arena: Allocator, - comp: *Compilation, - emit: Compilation.Emit, - options: link.File.OpenOptions, -) !*MachO { - const target = comp.root_mod.resolved_target.result; - const use_lld = build_options.have_llvm and comp.config.use_lld; - const use_llvm = comp.config.use_llvm; - assert(target.ofmt == .macho); - - const gpa = comp.gpa; - const mode: Mode = mode: { - if (use_llvm or comp.module == null or comp.cache_use == .whole) - break :mode .zld; - break :mode .incremental; - }; - const sub_path = if (mode == .zld) blk: { - if (comp.module == null) { - // No point in opening a file, we would not write anything to it. - // Initialize with empty. - return createEmpty(arena, comp, emit, options); - } - // Open a temporary object file, not the final output file because we - // want to link with LLD. - break :blk try std.fmt.allocPrint(arena, "{s}{s}", .{ - emit.sub_path, target.ofmt.fileExt(target.cpu.arch), - }); - } else emit.sub_path; - - const self = try createEmpty(arena, comp, emit, options); - errdefer self.base.destroy(); - - if (mode == .zld) { - // TODO this zcu_object_sub_path isn't enough; in the case of `zig build-exe`, - // we also want to put the intermediary object file in the cache while the - // main emit directory is the cwd. - self.base.zcu_object_sub_path = sub_path; - return self; - } - - const file = try emit.directory.handle.createFile(sub_path, .{ - .truncate = false, - .read = true, - .mode = link.File.determineMode( - use_lld, - comp.config.output_mode, - comp.config.link_mode, - ), - }); - self.base.file = file; - - if (comp.config.debug_format != .strip and comp.module != null) { - // Create dSYM bundle. - log.debug("creating {s}.dSYM bundle", .{sub_path}); - - const d_sym_path = try std.fmt.allocPrint( - arena, - "{s}.dSYM" ++ fs.path.sep_str ++ "Contents" ++ fs.path.sep_str ++ "Resources" ++ fs.path.sep_str ++ "DWARF", - .{sub_path}, - ); - - var d_sym_bundle = try emit.directory.handle.makeOpenPath(d_sym_path, .{}); - defer d_sym_bundle.close(); - - const d_sym_file = try d_sym_bundle.createFile(sub_path, .{ - .truncate = false, - .read = true, - }); - - self.d_sym = .{ - .allocator = gpa, - .dwarf = link.File.Dwarf.init(&self.base, .dwarf32), - .file = d_sym_file, - }; - } - - // Index 0 is always a null symbol. - try self.locals.append(gpa, .{ - .n_strx = 0, - .n_type = 0, - .n_sect = 0, - .n_desc = 0, - .n_value = 0, - }); - try self.strtab.buffer.append(gpa, 0); - - try self.populateMissingMetadata(.{ - .symbol_count_hint = options.symbol_count_hint, - .program_code_size_hint = options.program_code_size_hint, - }); - - if (self.d_sym) |*d_sym| { - try d_sym.populateMissingMetadata(self); - } - - return self; -} - pub fn createEmpty( arena: Allocator, comp: *Compilation, emit: Compilation.Emit, options: link.File.OpenOptions, ) !*MachO { + const target = comp.root_mod.resolved_target.result; + assert(target.ofmt == .macho); + const use_llvm = comp.config.use_llvm; + const gpa = comp.gpa; const optimize_mode = comp.root_mod.optimize_mode; - const use_llvm = comp.config.use_llvm; + const output_mode = comp.config.output_mode; + const link_mode = comp.config.link_mode; + + // TODO: get rid of zld mode + const mode: Mode = if (use_llvm or !comp.config.have_zcu or comp.cache_use == .whole) + .zld + else + .incremental; + + // If using "zld mode" to link, this code should produce an object file so that it + // can be passed to "zld mode". TODO: get rid of "zld mode". + // If using LLVM to generate the object file for the zig compilation unit, + // we need a place to put the object file so that it can be subsequently + // handled. + const zcu_object_sub_path = if (mode != .zld and !use_llvm) + null + else + try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path}); const self = try arena.create(MachO); self.* = .{ @@ -288,6 +210,7 @@ pub fn createEmpty( .tag = .macho, .comp = comp, .emit = emit, + .zcu_object_sub_path = zcu_object_sub_path, .gc_sections = options.gc_sections orelse (optimize_mode != .Debug), .print_gc_sections = options.print_gc_sections, .stack_size = options.stack_size orelse 16777216, @@ -298,10 +221,7 @@ pub fn createEmpty( .rpath_list = options.rpath_list, .force_undefined_symbols = options.force_undefined_symbols, }, - .mode = if (use_llvm or comp.module == null or comp.cache_use == .whole) - .zld - else - .incremental, + .mode = mode, .pagezero_vmsize = options.pagezero_size orelse default_pagezero_vmsize, .headerpad_size = options.headerpad_size orelse default_headerpad_size, .headerpad_max_install_names = options.headerpad_max_install_names, @@ -312,16 +232,83 @@ pub fn createEmpty( .entitlements = options.entitlements, .compatibility_version = options.compatibility_version, }; - - if (use_llvm and comp.module != null) { + if (use_llvm and comp.config.have_zcu) { self.llvm_object = try LlvmObject.create(arena, comp); } + errdefer self.base.destroy(); log.debug("selected linker mode '{s}'", .{@tagName(self.mode)}); + if (mode == .zld) { + // TODO: get rid of zld mode + return self; + } + + const file = try emit.directory.handle.createFile(emit.sub_path, .{ + .truncate = true, + .read = true, + .mode = link.File.determineMode(false, output_mode, link_mode), + }); + self.base.file = file; + + if (comp.config.debug_format != .strip and comp.module != null) { + // Create dSYM bundle. + log.debug("creating {s}.dSYM bundle", .{emit.sub_path}); + + const d_sym_path = try std.fmt.allocPrint( + arena, + "{s}.dSYM" ++ fs.path.sep_str ++ "Contents" ++ fs.path.sep_str ++ "Resources" ++ fs.path.sep_str ++ "DWARF", + .{emit.sub_path}, + ); + + var d_sym_bundle = try emit.directory.handle.makeOpenPath(d_sym_path, .{}); + defer d_sym_bundle.close(); + + const d_sym_file = try d_sym_bundle.createFile(emit.sub_path, .{ + .truncate = false, + .read = true, + }); + + self.d_sym = .{ + .allocator = gpa, + .dwarf = link.File.Dwarf.init(&self.base, .dwarf32), + .file = d_sym_file, + }; + } + + // Index 0 is always a null symbol. + try self.locals.append(gpa, .{ + .n_strx = 0, + .n_type = 0, + .n_sect = 0, + .n_desc = 0, + .n_value = 0, + }); + try self.strtab.buffer.append(gpa, 0); + + try self.populateMissingMetadata(.{ + .symbol_count_hint = options.symbol_count_hint, + .program_code_size_hint = options.program_code_size_hint, + }); + + if (self.d_sym) |*d_sym| { + try d_sym.populateMissingMetadata(self); + } + return self; } +pub fn open( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*MachO { + // TODO: restore saved linker state, don't truncate the file, and + // participate in incremental compilation. + return createEmpty(arena, comp, emit, options); +} + pub fn flush(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { const gpa = self.base.comp.gpa; const output_mode = self.base.comp.config.output_mode; diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index edc4ada4052757374f5be626ec3c02af7df0ee74..b4b0f16d4a8d1efeb336f3994c27d5182c9a34a1 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -440,8 +440,8 @@ pub fn createEmpty( } errdefer wasm.base.destroy(); - if (use_lld and use_llvm) { - // LLVM emits the object file; LLD links it into the final product. + if (use_lld and (use_llvm or !comp.config.have_zcu)) { + // LLVM emits the object file (if any); LLD links it into the final product. return wasm; } -- 2.54.0