authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-12-25 00:01:52-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-01-01 17:51:21-07:00
logb36659c972fb7d75b1e34cb668770353de27db7d
tree2261958ed6ae60283c40afc31dd8533cfe882fd4
parent944b0ef5188a066c9fe11a297c743bfb3301a02a

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.

5 files changed, 75 insertions(+), 92 deletions(-)

src/Compilation/Config.zig+4-8
...@@ -178,6 +178,9 @@ pub fn resolve(options: Options) ResolveError!Config {...@@ -178,6 +178,9 @@ pub fn resolve(options: Options) ResolveError!Config {
178 // For example, Zig can emit .bc and .ll files directly, and this is still considered178 // For example, Zig can emit .bc and .ll files directly, and this is still considered
179 // using "the LLVM backend".179 // using "the LLVM backend".
180 const use_llvm = b: {180 const use_llvm = b: {
181 // If we have no zig code to compile, no need for LLVM.
182 if (!options.have_zcu) break :b false;
183
181 // If emitting to LLVM bitcode object format, must use LLVM backend.184 // If emitting to LLVM bitcode object format, must use LLVM backend.
182 if (options.emit_llvm_ir or options.emit_llvm_bc) {185 if (options.emit_llvm_ir or options.emit_llvm_bc) {
183 if (options.use_llvm == false)186 if (options.use_llvm == false)
...@@ -202,13 +205,10 @@ pub fn resolve(options: Options) ResolveError!Config {...@@ -202,13 +205,10 @@ pub fn resolve(options: Options) ResolveError!Config {
202205
203 if (options.use_llvm) |x| break :b x;206 if (options.use_llvm) |x| break :b x;
204207
205 // If we have no zig code to compile, no need for LLVM.
206 if (!options.have_zcu) break :b false;
207
208 // If we cannot use LLVM libraries, then our own backends will be a208 // If we cannot use LLVM libraries, then our own backends will be a
209 // better default since the LLVM backend can only produce bitcode209 // better default since the LLVM backend can only produce bitcode
210 // and not an object file or executable.210 // and not an object file or executable.
211 if (!use_lib_llvm) break :b false;211 if (!use_lib_llvm and options.emit_bin) break :b false;
212212
213 // Prefer LLVM for release builds.213 // Prefer LLVM for release builds.
214 if (root_optimize_mode != .Debug) break :b true;214 if (root_optimize_mode != .Debug) break :b true;
...@@ -339,10 +339,6 @@ pub fn resolve(options: Options) ResolveError!Config {...@@ -339,10 +339,6 @@ pub fn resolve(options: Options) ResolveError!Config {
339 .default => b: {339 .default => b: {
340 if (options.output_mode != .Exe) break :b null;340 if (options.output_mode != .Exe) break :b null;
341341
342 // When linking libc, the entry point is inside libc and not in the
343 // zig compilation unit.
344 if (link_libc) break :b null;
345
346 // When producing C source code, the decision of entry point is made342 // When producing C source code, the decision of entry point is made
347 // when compiling the C code, not when producing the C code.343 // when compiling the C code, not when producing the C code.
348 if (target.ofmt == .c) break :b null;344 if (target.ofmt == .c) break :b null;
src/link/Coff.zig+2-2
...@@ -320,8 +320,8 @@ pub fn createEmpty(...@@ -320,8 +320,8 @@ pub fn createEmpty(
320 }320 }
321 errdefer self.base.destroy();321 errdefer self.base.destroy();
322322
323 if (use_lld and use_llvm) {323 if (use_lld and (use_llvm or !comp.config.have_zcu)) {
324 // LLVM emits the object file; LLD links it into the final product.324 // LLVM emits the object file (if any); LLD links it into the final product.
325 return self;325 return self;
326 }326 }
327327
src/link/Elf.zig+2-2
...@@ -328,8 +328,8 @@ pub fn createEmpty(...@@ -328,8 +328,8 @@ pub fn createEmpty(
328 }328 }
329 errdefer self.base.destroy();329 errdefer self.base.destroy();
330330
331 if (use_lld and use_llvm) {331 if (use_lld and (use_llvm or !comp.config.have_zcu)) {
332 // LLVM emits the object file; LLD links it into the final product.332 // LLVM emits the object file (if any); LLD links it into the final product.
333 return self;333 return self;
334 }334 }
335335
src/link/MachO.zig+65-78
...@@ -174,72 +174,97 @@ pub const SdkLayout = enum {...@@ -174,72 +174,97 @@ pub const SdkLayout = enum {
174 vendored,174 vendored,
175};175};
176176
177pub fn open(177pub fn createEmpty(
178 arena: Allocator,178 arena: Allocator,
179 comp: *Compilation,179 comp: *Compilation,
180 emit: Compilation.Emit,180 emit: Compilation.Emit,
181 options: link.File.OpenOptions,181 options: link.File.OpenOptions,
182) !*MachO {182) !*MachO {
183 const target = comp.root_mod.resolved_target.result;183 const target = comp.root_mod.resolved_target.result;
184 const use_lld = build_options.have_llvm and comp.config.use_lld;
185 const use_llvm = comp.config.use_llvm;
186 assert(target.ofmt == .macho);184 assert(target.ofmt == .macho);
187185 const use_llvm = comp.config.use_llvm;
188 const gpa = comp.gpa;186 const gpa = comp.gpa;
189 const mode: Mode = mode: {187 const optimize_mode = comp.root_mod.optimize_mode;
190 if (use_llvm or comp.module == null or comp.cache_use == .whole)188 const output_mode = comp.config.output_mode;
191 break :mode .zld;189 const link_mode = comp.config.link_mode;
192 break :mode .incremental;
193 };
194 const sub_path = if (mode == .zld) blk: {
195 if (comp.module == null) {
196 // No point in opening a file, we would not write anything to it.
197 // Initialize with empty.
198 return createEmpty(arena, comp, emit, options);
199 }
200 // Open a temporary object file, not the final output file because we
201 // want to link with LLD.
202 break :blk try std.fmt.allocPrint(arena, "{s}{s}", .{
203 emit.sub_path, target.ofmt.fileExt(target.cpu.arch),
204 });
205 } else emit.sub_path;
206190
207 const self = try createEmpty(arena, comp, emit, options);191 // TODO: get rid of zld mode
192 const mode: Mode = if (use_llvm or !comp.config.have_zcu or comp.cache_use == .whole)
193 .zld
194 else
195 .incremental;
196
197 // If using "zld mode" to link, this code should produce an object file so that it
198 // can be passed to "zld mode". TODO: get rid of "zld mode".
199 // If using LLVM to generate the object file for the zig compilation unit,
200 // we need a place to put the object file so that it can be subsequently
201 // handled.
202 const zcu_object_sub_path = if (mode != .zld and !use_llvm)
203 null
204 else
205 try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path});
206
207 const self = try arena.create(MachO);
208 self.* = .{
209 .base = .{
210 .tag = .macho,
211 .comp = comp,
212 .emit = emit,
213 .zcu_object_sub_path = zcu_object_sub_path,
214 .gc_sections = options.gc_sections orelse (optimize_mode != .Debug),
215 .print_gc_sections = options.print_gc_sections,
216 .stack_size = options.stack_size orelse 16777216,
217 .allow_shlib_undefined = options.allow_shlib_undefined orelse false,
218 .file = null,
219 .disable_lld_caching = options.disable_lld_caching,
220 .build_id = options.build_id,
221 .rpath_list = options.rpath_list,
222 .force_undefined_symbols = options.force_undefined_symbols,
223 },
224 .mode = mode,
225 .pagezero_vmsize = options.pagezero_size orelse default_pagezero_vmsize,
226 .headerpad_size = options.headerpad_size orelse default_headerpad_size,
227 .headerpad_max_install_names = options.headerpad_max_install_names,
228 .dead_strip_dylibs = options.dead_strip_dylibs,
229 .sdk_layout = options.darwin_sdk_layout,
230 .frameworks = options.frameworks,
231 .install_name = options.install_name,
232 .entitlements = options.entitlements,
233 .compatibility_version = options.compatibility_version,
234 };
235 if (use_llvm and comp.config.have_zcu) {
236 self.llvm_object = try LlvmObject.create(arena, comp);
237 }
208 errdefer self.base.destroy();238 errdefer self.base.destroy();
209239
240 log.debug("selected linker mode '{s}'", .{@tagName(self.mode)});
241
210 if (mode == .zld) {242 if (mode == .zld) {
211 // TODO this zcu_object_sub_path isn't enough; in the case of `zig build-exe`,243 // TODO: get rid of zld mode
212 // we also want to put the intermediary object file in the cache while the
213 // main emit directory is the cwd.
214 self.base.zcu_object_sub_path = sub_path;
215 return self;244 return self;
216 }245 }
217246
218 const file = try emit.directory.handle.createFile(sub_path, .{247 const file = try emit.directory.handle.createFile(emit.sub_path, .{
219 .truncate = false,248 .truncate = true,
220 .read = true,249 .read = true,
221 .mode = link.File.determineMode(250 .mode = link.File.determineMode(false, output_mode, link_mode),
222 use_lld,
223 comp.config.output_mode,
224 comp.config.link_mode,
225 ),
226 });251 });
227 self.base.file = file;252 self.base.file = file;
228253
229 if (comp.config.debug_format != .strip and comp.module != null) {254 if (comp.config.debug_format != .strip and comp.module != null) {
230 // Create dSYM bundle.255 // Create dSYM bundle.
231 log.debug("creating {s}.dSYM bundle", .{sub_path});256 log.debug("creating {s}.dSYM bundle", .{emit.sub_path});
232257
233 const d_sym_path = try std.fmt.allocPrint(258 const d_sym_path = try std.fmt.allocPrint(
234 arena,259 arena,
235 "{s}.dSYM" ++ fs.path.sep_str ++ "Contents" ++ fs.path.sep_str ++ "Resources" ++ fs.path.sep_str ++ "DWARF",260 "{s}.dSYM" ++ fs.path.sep_str ++ "Contents" ++ fs.path.sep_str ++ "Resources" ++ fs.path.sep_str ++ "DWARF",
236 .{sub_path},261 .{emit.sub_path},
237 );262 );
238263
239 var d_sym_bundle = try emit.directory.handle.makeOpenPath(d_sym_path, .{});264 var d_sym_bundle = try emit.directory.handle.makeOpenPath(d_sym_path, .{});
240 defer d_sym_bundle.close();265 defer d_sym_bundle.close();
241266
242 const d_sym_file = try d_sym_bundle.createFile(sub_path, .{267 const d_sym_file = try d_sym_bundle.createFile(emit.sub_path, .{
243 .truncate = false,268 .truncate = false,
244 .read = true,269 .read = true,
245 });270 });
...@@ -273,53 +298,15 @@ pub fn open(...@@ -273,53 +298,15 @@ pub fn open(
273 return self;298 return self;
274}299}
275300
276pub fn createEmpty(301pub fn open(
277 arena: Allocator,302 arena: Allocator,
278 comp: *Compilation,303 comp: *Compilation,
279 emit: Compilation.Emit,304 emit: Compilation.Emit,
280 options: link.File.OpenOptions,305 options: link.File.OpenOptions,
281) !*MachO {306) !*MachO {
282 const optimize_mode = comp.root_mod.optimize_mode;307 // TODO: restore saved linker state, don't truncate the file, and
283 const use_llvm = comp.config.use_llvm;308 // participate in incremental compilation.
284309 return createEmpty(arena, comp, emit, options);
285 const self = try arena.create(MachO);
286 self.* = .{
287 .base = .{
288 .tag = .macho,
289 .comp = comp,
290 .emit = emit,
291 .gc_sections = options.gc_sections orelse (optimize_mode != .Debug),
292 .print_gc_sections = options.print_gc_sections,
293 .stack_size = options.stack_size orelse 16777216,
294 .allow_shlib_undefined = options.allow_shlib_undefined orelse false,
295 .file = null,
296 .disable_lld_caching = options.disable_lld_caching,
297 .build_id = options.build_id,
298 .rpath_list = options.rpath_list,
299 .force_undefined_symbols = options.force_undefined_symbols,
300 },
301 .mode = if (use_llvm or comp.module == null or comp.cache_use == .whole)
302 .zld
303 else
304 .incremental,
305 .pagezero_vmsize = options.pagezero_size orelse default_pagezero_vmsize,
306 .headerpad_size = options.headerpad_size orelse default_headerpad_size,
307 .headerpad_max_install_names = options.headerpad_max_install_names,
308 .dead_strip_dylibs = options.dead_strip_dylibs,
309 .sdk_layout = options.darwin_sdk_layout,
310 .frameworks = options.frameworks,
311 .install_name = options.install_name,
312 .entitlements = options.entitlements,
313 .compatibility_version = options.compatibility_version,
314 };
315
316 if (use_llvm and comp.module != null) {
317 self.llvm_object = try LlvmObject.create(arena, comp);
318 }
319
320 log.debug("selected linker mode '{s}'", .{@tagName(self.mode)});
321
322 return self;
323}310}
324311
325pub fn flush(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void {312pub fn flush(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void {
src/link/Wasm.zig+2-2
...@@ -440,8 +440,8 @@ pub fn createEmpty(...@@ -440,8 +440,8 @@ pub fn createEmpty(
440 }440 }
441 errdefer wasm.base.destroy();441 errdefer wasm.base.destroy();
442442
443 if (use_lld and use_llvm) {443 if (use_lld and (use_llvm or !comp.config.have_zcu)) {
444 // LLVM emits the object file; LLD links it into the final product.444 // LLVM emits the object file (if any); LLD links it into the final product.
445 return wasm;445 return wasm;
446 }446 }
447447