diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 61361b6dcc..1a8a70c39b 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -527,19 +527,11 @@ pub const Object = struct { if (self.llvm_module.getNamedGlobalAlias(exp_name_z.ptr, exp_name_z.len)) |alias| { alias.setAliasee(llvm_global); } else { - const alias = self.llvm_module.addAlias(llvm_global.typeOf(), llvm_global, exp_name_z); - switch (exp.options.linkage) { - .Internal => alias.setLinkage(.Internal), - .Strong => alias.setLinkage(.External), - .Weak => { - if (is_extern) { - alias.setLinkage(.ExternalWeak); - } else { - alias.setLinkage(.WeakODR); - } - }, - .LinkOnce => alias.setLinkage(.LinkOnceODR), - } + _ = self.llvm_module.addAlias( + llvm_global.typeOf(), + llvm_global, + exp_name_z, + ); } } } else { diff --git a/src/link.zig b/src/link.zig index 9af7fe929c..5874ed5703 100644 --- a/src/link.zig +++ b/src/link.zig @@ -192,12 +192,16 @@ pub const File = struct { /// rewriting it. A malicious file is detected as incremental link failure /// and does not cause Illegal Behavior. This operation is not atomic. pub fn openPath(allocator: *Allocator, options: Options) !*File { + if (options.object_format == .macho) { + return &(try MachO.openPath(allocator, options)).base; + } + const use_stage1 = build_options.is_stage1 and options.use_stage1; if (use_stage1 or options.emit == null) { return switch (options.object_format) { .coff => &(try Coff.createEmpty(allocator, options)).base, .elf => &(try Elf.createEmpty(allocator, options)).base, - .macho => &(try MachO.createEmpty(allocator, options)).base, + .macho => unreachable, .wasm => &(try Wasm.createEmpty(allocator, options)).base, .plan9 => return &(try Plan9.createEmpty(allocator, options)).base, .c => unreachable, // Reported error earlier. @@ -215,7 +219,7 @@ pub const File = struct { return switch (options.object_format) { .coff => &(try Coff.createEmpty(allocator, options)).base, .elf => &(try Elf.createEmpty(allocator, options)).base, - .macho => &(try MachO.createEmpty(allocator, options)).base, + .macho => unreachable, .plan9 => &(try Plan9.createEmpty(allocator, options)).base, .wasm => &(try Wasm.createEmpty(allocator, options)).base, .c => unreachable, // Reported error earlier. @@ -235,7 +239,7 @@ pub const File = struct { const file: *File = switch (options.object_format) { .coff => &(try Coff.openPath(allocator, sub_path, options)).base, .elf => &(try Elf.openPath(allocator, sub_path, options)).base, - .macho => &(try MachO.openPath(allocator, sub_path, options)).base, + .macho => unreachable, .plan9 => &(try Plan9.openPath(allocator, sub_path, options)).base, .wasm => &(try Wasm.openPath(allocator, sub_path, options)).base, .c => &(try C.openPath(allocator, sub_path, options)).base, @@ -576,7 +580,11 @@ pub const File = struct { const full_obj_path = try o_directory.join(arena, &[_][]const u8{obj_basename}); break :blk full_obj_path; } - try base.flushModule(comp); + if (base.options.object_format == .macho) { + try base.cast(MachO).?.flushObject(comp); + } else { + try base.flushModule(comp); + } const obj_basename = base.intermediary_basename.?; const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename}); break :blk full_obj_path; diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 0d8487ad79..8d951fe878 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -275,18 +275,15 @@ pub const SrcFn = struct { }; }; -pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*MachO { +pub fn openPath(allocator: *Allocator, options: link.Options) !*MachO { assert(options.object_format == .macho); - if (build_options.have_llvm and options.use_llvm) { - const self = try createEmpty(allocator, options); - errdefer self.base.destroy(); - - self.llvm_object = try LlvmObject.create(allocator, sub_path, options); - return self; + const use_stage1 = build_options.is_stage1 and options.use_stage1; + if (use_stage1 or options.emit == null) { + return createEmpty(allocator, options); } - - const file = try options.emit.?.directory.handle.createFile(sub_path, .{ + const emit = options.emit.?; + const file = try emit.directory.handle.createFile(emit.sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options), @@ -301,7 +298,20 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio self.base.file = file; - if (options.output_mode == .Lib and options.link_mode == .Static) { + if (build_options.have_llvm and options.use_llvm and options.module != null) { + // TODO this intermediary_basename 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. + const sub_path = try std.fmt.allocPrint(allocator, "{s}{s}", .{ + emit.sub_path, options.object_format.fileExt(options.target.cpu.arch), + }); + self.llvm_object = try LlvmObject.create(allocator, sub_path, options); + self.base.intermediary_basename = sub_path; + } + + if (options.output_mode == .Lib and + options.link_mode == .Static and self.base.intermediary_basename != null) + { return self; } @@ -384,16 +394,22 @@ pub fn flush(self: *MachO, comp: *Compilation) !void { return error.TODOImplementWritingStaticLibFiles; } } + try self.flushModule(comp); +} +pub fn flushModule(self: *MachO, comp: *Compilation) !void { const tracy = trace(@src()); defer tracy.end(); + const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1; + if (!use_stage1 and self.base.options.output_mode == .Obj) + return self.flushObject(comp); + var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator); defer arena_allocator.deinit(); const arena = &arena_allocator.allocator; const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type. - const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1; // If there is no Zig code to compile, then we should skip flushing the output file because it // will not be part of the linker line anyway. @@ -410,7 +426,7 @@ pub fn flush(self: *MachO, comp: *Compilation) !void { } const obj_basename = self.base.intermediary_basename orelse break :blk null; - try self.flushModule(comp); + try self.flushObject(comp); const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename}); break :blk full_obj_path; } else null; @@ -534,15 +550,16 @@ pub fn flush(self: *MachO, comp: *Compilation) !void { .read = true, .mode = link.determineMode(self.base.options), }); - try self.populateMissingMetadata(); + // Index 0 is always a null symbol. try self.locals.append(self.base.allocator, .{ .n_strx = 0, - .n_type = macho.N_UNDF, + .n_type = 0, .n_sect = 0, .n_desc = 0, .n_value = 0, }); try self.strtab.append(self.base.allocator, 0); + try self.populateMissingMetadata(); } if (needs_full_relink) { @@ -887,7 +904,40 @@ pub fn flush(self: *MachO, comp: *Compilation) !void { sect.offset = 0; } - try self.flushModule(comp); + try self.setEntryPoint(); + try self.updateSectionOrdinals(); + try self.writeLinkeditSegment(); + + if (self.d_sym) |*ds| { + // Flush debug symbols bundle. + try ds.flushModule(self.base.allocator, self.base.options); + } + + if (self.requires_adhoc_codesig) { + // Preallocate space for the code signature. + // We need to do this at this stage so that we have the load commands with proper values + // written out to the file. + // The most important here is to have the correct vm and filesize of the __LINKEDIT segment + // where the code signature goes into. + try self.writeCodeSignaturePadding(); + } + + try self.writeLoadCommands(); + try self.writeHeader(); + + if (self.entry_addr == null and self.base.options.output_mode == .Exe) { + log.debug("flushing. no_entry_point_found = true", .{}); + self.error_flags.no_entry_point_found = true; + } else { + log.debug("flushing. no_entry_point_found = false", .{}); + self.error_flags.no_entry_point_found = false; + } + + assert(!self.load_commands_dirty); + + if (self.requires_adhoc_codesig) { + try self.writeCodeSignature(); // code signing always comes last + } } cache: { @@ -909,46 +959,14 @@ pub fn flush(self: *MachO, comp: *Compilation) !void { self.cold_start = false; } -pub fn flushModule(self: *MachO, comp: *Compilation) !void { - _ = comp; - +pub fn flushObject(self: *MachO, comp: *Compilation) !void { const tracy = trace(@src()); defer tracy.end(); - try self.setEntryPoint(); - try self.updateSectionOrdinals(); - try self.writeLinkeditSegment(); + if (build_options.have_llvm) + if (self.llvm_object) |llvm_object| return llvm_object.flushModule(comp); - if (self.d_sym) |*ds| { - // Flush debug symbols bundle. - try ds.flushModule(self.base.allocator, self.base.options); - } - - if (self.requires_adhoc_codesig) { - // Preallocate space for the code signature. - // We need to do this at this stage so that we have the load commands with proper values - // written out to the file. - // The most important here is to have the correct vm and filesize of the __LINKEDIT segment - // where the code signature goes into. - try self.writeCodeSignaturePadding(); - } - - try self.writeLoadCommands(); - try self.writeHeader(); - - if (self.entry_addr == null and self.base.options.output_mode == .Exe) { - log.debug("flushing. no_entry_point_found = true", .{}); - self.error_flags.no_entry_point_found = true; - } else { - log.debug("flushing. no_entry_point_found = false", .{}); - self.error_flags.no_entry_point_found = false; - } - - assert(!self.load_commands_dirty); - - if (self.requires_adhoc_codesig) { - try self.writeCodeSignature(); // code signing always comes last - } + return error.TODOImplementWritingObjFiles; } fn resolveSearchDir( @@ -3035,6 +3053,7 @@ fn growAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, match } pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void { + if (self.llvm_object) |_| return; if (decl.link.macho.local_sym_index != 0) return; try self.locals.ensureUnusedCapacity(self.base.allocator, 1); @@ -3458,6 +3477,7 @@ pub fn updateDeclExports( } pub fn deleteExport(self: *MachO, exp: Export) void { + if (self.llvm_object) |_| return; const sym_index = exp.sym_index orelse return; self.globals_free_list.append(self.base.allocator, sym_index) catch {}; const global = &self.globals.items[sym_index];