authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-10-18 21:09:03+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-10-19 20:39:42+02:00
log372e9709ad2f4af24461f0fa601754a469091c2b
treed989814d9c7665d41f8afa973fc00a91ba61b387
parent2d7b55aa0ac30bd0e85cc43e22ef578c7a0d766c

macho: fix LLVM codepaths in self-hosted linker

* do not add linkage scope to aliased exported symbols - this is not respected on macOS * special-case `MachO.openPath` in `link.File.openPath` as on macOS we always link with zld * redirect to `MachO.flushObject` when linking relocatable objects in MachO linker whereas move the entire linking logic into `MachO.flushModule`

3 files changed, 88 insertions(+), 68 deletions(-)

src/codegen/llvm.zig+5-13
...@@ -527,19 +527,11 @@ pub const Object = struct {...@@ -527,19 +527,11 @@ pub const Object = struct {
527 if (self.llvm_module.getNamedGlobalAlias(exp_name_z.ptr, exp_name_z.len)) |alias| {527 if (self.llvm_module.getNamedGlobalAlias(exp_name_z.ptr, exp_name_z.len)) |alias| {
528 alias.setAliasee(llvm_global);528 alias.setAliasee(llvm_global);
529 } else {529 } else {
530 const alias = self.llvm_module.addAlias(llvm_global.typeOf(), llvm_global, exp_name_z);530 _ = self.llvm_module.addAlias(
531 switch (exp.options.linkage) {531 llvm_global.typeOf(),
532 .Internal => alias.setLinkage(.Internal),532 llvm_global,
533 .Strong => alias.setLinkage(.External),533 exp_name_z,
534 .Weak => {534 );
535 if (is_extern) {
536 alias.setLinkage(.ExternalWeak);
537 } else {
538 alias.setLinkage(.WeakODR);
539 }
540 },
541 .LinkOnce => alias.setLinkage(.LinkOnceODR),
542 }
543 }535 }
544 }536 }
545 } else {537 } else {
src/link.zig+12-4
...@@ -192,12 +192,16 @@ pub const File = struct {...@@ -192,12 +192,16 @@ pub const File = struct {
192 /// rewriting it. A malicious file is detected as incremental link failure192 /// rewriting it. A malicious file is detected as incremental link failure
193 /// and does not cause Illegal Behavior. This operation is not atomic.193 /// and does not cause Illegal Behavior. This operation is not atomic.
194 pub fn openPath(allocator: *Allocator, options: Options) !*File {194 pub fn openPath(allocator: *Allocator, options: Options) !*File {
195 if (options.object_format == .macho) {
196 return &(try MachO.openPath(allocator, options)).base;
197 }
198
195 const use_stage1 = build_options.is_stage1 and options.use_stage1;199 const use_stage1 = build_options.is_stage1 and options.use_stage1;
196 if (use_stage1 or options.emit == null) {200 if (use_stage1 or options.emit == null) {
197 return switch (options.object_format) {201 return switch (options.object_format) {
198 .coff => &(try Coff.createEmpty(allocator, options)).base,202 .coff => &(try Coff.createEmpty(allocator, options)).base,
199 .elf => &(try Elf.createEmpty(allocator, options)).base,203 .elf => &(try Elf.createEmpty(allocator, options)).base,
200 .macho => &(try MachO.createEmpty(allocator, options)).base,204 .macho => unreachable,
201 .wasm => &(try Wasm.createEmpty(allocator, options)).base,205 .wasm => &(try Wasm.createEmpty(allocator, options)).base,
202 .plan9 => return &(try Plan9.createEmpty(allocator, options)).base,206 .plan9 => return &(try Plan9.createEmpty(allocator, options)).base,
203 .c => unreachable, // Reported error earlier.207 .c => unreachable, // Reported error earlier.
...@@ -215,7 +219,7 @@ pub const File = struct {...@@ -215,7 +219,7 @@ pub const File = struct {
215 return switch (options.object_format) {219 return switch (options.object_format) {
216 .coff => &(try Coff.createEmpty(allocator, options)).base,220 .coff => &(try Coff.createEmpty(allocator, options)).base,
217 .elf => &(try Elf.createEmpty(allocator, options)).base,221 .elf => &(try Elf.createEmpty(allocator, options)).base,
218 .macho => &(try MachO.createEmpty(allocator, options)).base,222 .macho => unreachable,
219 .plan9 => &(try Plan9.createEmpty(allocator, options)).base,223 .plan9 => &(try Plan9.createEmpty(allocator, options)).base,
220 .wasm => &(try Wasm.createEmpty(allocator, options)).base,224 .wasm => &(try Wasm.createEmpty(allocator, options)).base,
221 .c => unreachable, // Reported error earlier.225 .c => unreachable, // Reported error earlier.
...@@ -235,7 +239,7 @@ pub const File = struct {...@@ -235,7 +239,7 @@ pub const File = struct {
235 const file: *File = switch (options.object_format) {239 const file: *File = switch (options.object_format) {
236 .coff => &(try Coff.openPath(allocator, sub_path, options)).base,240 .coff => &(try Coff.openPath(allocator, sub_path, options)).base,
237 .elf => &(try Elf.openPath(allocator, sub_path, options)).base,241 .elf => &(try Elf.openPath(allocator, sub_path, options)).base,
238 .macho => &(try MachO.openPath(allocator, sub_path, options)).base,242 .macho => unreachable,
239 .plan9 => &(try Plan9.openPath(allocator, sub_path, options)).base,243 .plan9 => &(try Plan9.openPath(allocator, sub_path, options)).base,
240 .wasm => &(try Wasm.openPath(allocator, sub_path, options)).base,244 .wasm => &(try Wasm.openPath(allocator, sub_path, options)).base,
241 .c => &(try C.openPath(allocator, sub_path, options)).base,245 .c => &(try C.openPath(allocator, sub_path, options)).base,
...@@ -576,7 +580,11 @@ pub const File = struct {...@@ -576,7 +580,11 @@ pub const File = struct {
576 const full_obj_path = try o_directory.join(arena, &[_][]const u8{obj_basename});580 const full_obj_path = try o_directory.join(arena, &[_][]const u8{obj_basename});
577 break :blk full_obj_path;581 break :blk full_obj_path;
578 }582 }
579 try base.flushModule(comp);583 if (base.options.object_format == .macho) {
584 try base.cast(MachO).?.flushObject(comp);
585 } else {
586 try base.flushModule(comp);
587 }
580 const obj_basename = base.intermediary_basename.?;588 const obj_basename = base.intermediary_basename.?;
581 const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename});589 const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename});
582 break :blk full_obj_path;590 break :blk full_obj_path;
src/link/MachO.zig+71-51
...@@ -275,18 +275,15 @@ pub const SrcFn = struct {...@@ -275,18 +275,15 @@ pub const SrcFn = struct {
275 };275 };
276};276};
277277
278pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*MachO {278pub fn openPath(allocator: *Allocator, options: link.Options) !*MachO {
279 assert(options.object_format == .macho);279 assert(options.object_format == .macho);
280280
281 if (build_options.have_llvm and options.use_llvm) {281 const use_stage1 = build_options.is_stage1 and options.use_stage1;
282 const self = try createEmpty(allocator, options);282 if (use_stage1 or options.emit == null) {
283 errdefer self.base.destroy();283 return createEmpty(allocator, options);
284
285 self.llvm_object = try LlvmObject.create(allocator, sub_path, options);
286 return self;
287 }284 }
288285 const emit = options.emit.?;
289 const file = try options.emit.?.directory.handle.createFile(sub_path, .{286 const file = try emit.directory.handle.createFile(emit.sub_path, .{
290 .truncate = false,287 .truncate = false,
291 .read = true,288 .read = true,
292 .mode = link.determineMode(options),289 .mode = link.determineMode(options),
...@@ -301,7 +298,20 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -301,7 +298,20 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
301298
302 self.base.file = file;299 self.base.file = file;
303300
304 if (options.output_mode == .Lib and options.link_mode == .Static) {301 if (build_options.have_llvm and options.use_llvm and options.module != null) {
302 // TODO this intermediary_basename isn't enough; in the case of `zig build-exe`,
303 // we also want to put the intermediary object file in the cache while the
304 // main emit directory is the cwd.
305 const sub_path = try std.fmt.allocPrint(allocator, "{s}{s}", .{
306 emit.sub_path, options.object_format.fileExt(options.target.cpu.arch),
307 });
308 self.llvm_object = try LlvmObject.create(allocator, sub_path, options);
309 self.base.intermediary_basename = sub_path;
310 }
311
312 if (options.output_mode == .Lib and
313 options.link_mode == .Static and self.base.intermediary_basename != null)
314 {
305 return self;315 return self;
306 }316 }
307317
...@@ -384,16 +394,22 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {...@@ -384,16 +394,22 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {
384 return error.TODOImplementWritingStaticLibFiles;394 return error.TODOImplementWritingStaticLibFiles;
385 }395 }
386 }396 }
397 try self.flushModule(comp);
398}
387399
400pub fn flushModule(self: *MachO, comp: *Compilation) !void {
388 const tracy = trace(@src());401 const tracy = trace(@src());
389 defer tracy.end();402 defer tracy.end();
390403
404 const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1;
405 if (!use_stage1 and self.base.options.output_mode == .Obj)
406 return self.flushObject(comp);
407
391 var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);408 var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
392 defer arena_allocator.deinit();409 defer arena_allocator.deinit();
393 const arena = &arena_allocator.allocator;410 const arena = &arena_allocator.allocator;
394411
395 const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type.412 const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type.
396 const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1;
397413
398 // If there is no Zig code to compile, then we should skip flushing the output file because it414 // If there is no Zig code to compile, then we should skip flushing the output file because it
399 // will not be part of the linker line anyway.415 // will not be part of the linker line anyway.
...@@ -410,7 +426,7 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {...@@ -410,7 +426,7 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {
410 }426 }
411427
412 const obj_basename = self.base.intermediary_basename orelse break :blk null;428 const obj_basename = self.base.intermediary_basename orelse break :blk null;
413 try self.flushModule(comp);429 try self.flushObject(comp);
414 const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename});430 const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename});
415 break :blk full_obj_path;431 break :blk full_obj_path;
416 } else null;432 } else null;
...@@ -534,15 +550,16 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {...@@ -534,15 +550,16 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {
534 .read = true,550 .read = true,
535 .mode = link.determineMode(self.base.options),551 .mode = link.determineMode(self.base.options),
536 });552 });
537 try self.populateMissingMetadata();553 // Index 0 is always a null symbol.
538 try self.locals.append(self.base.allocator, .{554 try self.locals.append(self.base.allocator, .{
539 .n_strx = 0,555 .n_strx = 0,
540 .n_type = macho.N_UNDF,556 .n_type = 0,
541 .n_sect = 0,557 .n_sect = 0,
542 .n_desc = 0,558 .n_desc = 0,
543 .n_value = 0,559 .n_value = 0,
544 });560 });
545 try self.strtab.append(self.base.allocator, 0);561 try self.strtab.append(self.base.allocator, 0);
562 try self.populateMissingMetadata();
546 }563 }
547564
548 if (needs_full_relink) {565 if (needs_full_relink) {
...@@ -887,7 +904,40 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {...@@ -887,7 +904,40 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {
887 sect.offset = 0;904 sect.offset = 0;
888 }905 }
889906
890 try self.flushModule(comp);907 try self.setEntryPoint();
908 try self.updateSectionOrdinals();
909 try self.writeLinkeditSegment();
910
911 if (self.d_sym) |*ds| {
912 // Flush debug symbols bundle.
913 try ds.flushModule(self.base.allocator, self.base.options);
914 }
915
916 if (self.requires_adhoc_codesig) {
917 // Preallocate space for the code signature.
918 // We need to do this at this stage so that we have the load commands with proper values
919 // written out to the file.
920 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
921 // where the code signature goes into.
922 try self.writeCodeSignaturePadding();
923 }
924
925 try self.writeLoadCommands();
926 try self.writeHeader();
927
928 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
929 log.debug("flushing. no_entry_point_found = true", .{});
930 self.error_flags.no_entry_point_found = true;
931 } else {
932 log.debug("flushing. no_entry_point_found = false", .{});
933 self.error_flags.no_entry_point_found = false;
934 }
935
936 assert(!self.load_commands_dirty);
937
938 if (self.requires_adhoc_codesig) {
939 try self.writeCodeSignature(); // code signing always comes last
940 }
891 }941 }
892942
893 cache: {943 cache: {
...@@ -909,46 +959,14 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {...@@ -909,46 +959,14 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {
909 self.cold_start = false;959 self.cold_start = false;
910}960}
911961
912pub fn flushModule(self: *MachO, comp: *Compilation) !void {962pub fn flushObject(self: *MachO, comp: *Compilation) !void {
913 _ = comp;
914
915 const tracy = trace(@src());963 const tracy = trace(@src());
916 defer tracy.end();964 defer tracy.end();
917965
918 try self.setEntryPoint();966 if (build_options.have_llvm)
919 try self.updateSectionOrdinals();967 if (self.llvm_object) |llvm_object| return llvm_object.flushModule(comp);
920 try self.writeLinkeditSegment();
921
922 if (self.d_sym) |*ds| {
923 // Flush debug symbols bundle.
924 try ds.flushModule(self.base.allocator, self.base.options);
925 }
926
927 if (self.requires_adhoc_codesig) {
928 // Preallocate space for the code signature.
929 // We need to do this at this stage so that we have the load commands with proper values
930 // written out to the file.
931 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
932 // where the code signature goes into.
933 try self.writeCodeSignaturePadding();
934 }
935
936 try self.writeLoadCommands();
937 try self.writeHeader();
938968
939 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {969 return error.TODOImplementWritingObjFiles;
940 log.debug("flushing. no_entry_point_found = true", .{});
941 self.error_flags.no_entry_point_found = true;
942 } else {
943 log.debug("flushing. no_entry_point_found = false", .{});
944 self.error_flags.no_entry_point_found = false;
945 }
946
947 assert(!self.load_commands_dirty);
948
949 if (self.requires_adhoc_codesig) {
950 try self.writeCodeSignature(); // code signing always comes last
951 }
952}970}
953971
954fn resolveSearchDir(972fn resolveSearchDir(
...@@ -3035,6 +3053,7 @@ fn growAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, match...@@ -3035,6 +3053,7 @@ fn growAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, match
3035}3053}
30363054
3037pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {3055pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
3056 if (self.llvm_object) |_| return;
3038 if (decl.link.macho.local_sym_index != 0) return;3057 if (decl.link.macho.local_sym_index != 0) return;
30393058
3040 try self.locals.ensureUnusedCapacity(self.base.allocator, 1);3059 try self.locals.ensureUnusedCapacity(self.base.allocator, 1);
...@@ -3458,6 +3477,7 @@ pub fn updateDeclExports(...@@ -3458,6 +3477,7 @@ pub fn updateDeclExports(
3458}3477}
34593478
3460pub fn deleteExport(self: *MachO, exp: Export) void {3479pub fn deleteExport(self: *MachO, exp: Export) void {
3480 if (self.llvm_object) |_| return;
3461 const sym_index = exp.sym_index orelse return;3481 const sym_index = exp.sym_index orelse return;
3462 self.globals_free_list.append(self.base.allocator, sym_index) catch {};3482 self.globals_free_list.append(self.base.allocator, sym_index) catch {};
3463 const global = &self.globals.items[sym_index];3483 const global = &self.globals.items[sym_index];