From 819ef521042e7e21db4ab5dd7a0dbe180bd31c57 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 13 Aug 2021 15:12:28 +0200 Subject: [PATCH] macho: merge linkWithZld with flush --- src/link/MachO.zig | 421 ++++++++++++++++++++++----------------------- 1 file changed, 207 insertions(+), 214 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 10e079d4f1..04f32ae6c4 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -415,171 +415,6 @@ pub fn flush(self: *MachO, comp: *Compilation) !void { } } - const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1; - if (use_stage1) { - return self.linkWithZld(comp); - } else { - switch (self.base.options.effectiveOutputMode()) { - .Exe, .Obj => {}, - .Lib => return error.TODOImplementWritingLibFiles, - } - return self.flushModule(comp); - } -} - -pub fn flushModule(self: *MachO, comp: *Compilation) !void { - _ = comp; - const tracy = trace(@src()); - defer tracy.end(); - - const output_mode = self.base.options.output_mode; - const target = self.base.options.target; - - switch (output_mode) { - .Exe => { - if (self.entry_addr) |addr| { - // Update LC_MAIN with entry offset. - const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; - const main_cmd = &self.load_commands.items[self.main_cmd_index.?].Main; - main_cmd.entryoff = addr - text_segment.inner.vmaddr; - main_cmd.stacksize = self.base.options.stack_size_override orelse 0; - self.load_commands_dirty = true; - } - try self.writeRebaseInfoTable(); - try self.writeBindInfoTable(); - try self.writeLazyBindInfoTable(); - try self.writeExportInfo(); - try self.writeAllGlobalAndUndefSymbols(); - try self.writeIndirectSymbolTable(); - try self.writeStringTable(); - try self.updateLinkeditSegmentSizes(); - - if (self.d_sym) |*ds| { - // Flush debug symbols bundle. - try ds.flushModule(self.base.allocator, self.base.options); - } - - if (target.cpu.arch == .aarch64) { - // 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(); - } - }, - .Obj => {}, - .Lib => return error.TODOImplementWritingLibFiles, - } - - 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.got_entries_count_dirty); - assert(!self.load_commands_dirty); - assert(!self.rebase_info_dirty); - assert(!self.binding_info_dirty); - assert(!self.lazy_binding_info_dirty); - assert(!self.export_info_dirty); - assert(!self.strtab_dirty); - assert(!self.strtab_needs_relocation); - - if (target.cpu.arch == .aarch64) { - switch (output_mode) { - .Exe, .Lib => try self.writeCodeSignature(), // code signing always comes last - else => {}, - } - } -} - -fn resolveSearchDir( - arena: *Allocator, - dir: []const u8, - syslibroot: ?[]const u8, -) !?[]const u8 { - var candidates = std.ArrayList([]const u8).init(arena); - - if (fs.path.isAbsolute(dir)) { - if (syslibroot) |root| { - const full_path = try fs.path.join(arena, &[_][]const u8{ root, dir }); - try candidates.append(full_path); - } - } - - try candidates.append(dir); - - for (candidates.items) |candidate| { - // Verify that search path actually exists - var tmp = fs.cwd().openDir(candidate, .{}) catch |err| switch (err) { - error.FileNotFound => continue, - else => |e| return e, - }; - defer tmp.close(); - - return candidate; - } - - return null; -} - -fn resolveLib( - arena: *Allocator, - search_dirs: []const []const u8, - name: []const u8, - ext: []const u8, -) !?[]const u8 { - const search_name = try std.fmt.allocPrint(arena, "lib{s}{s}", .{ name, ext }); - - for (search_dirs) |dir| { - const full_path = try fs.path.join(arena, &[_][]const u8{ dir, search_name }); - - // Check if the file exists. - const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) { - error.FileNotFound => continue, - else => |e| return e, - }; - defer tmp.close(); - - return full_path; - } - - return null; -} - -fn resolveFramework( - arena: *Allocator, - search_dirs: []const []const u8, - name: []const u8, - ext: []const u8, -) !?[]const u8 { - const search_name = try std.fmt.allocPrint(arena, "{s}{s}", .{ name, ext }); - const prefix_path = try std.fmt.allocPrint(arena, "{s}.framework", .{name}); - - for (search_dirs) |dir| { - const full_path = try fs.path.join(arena, &[_][]const u8{ dir, prefix_path, search_name }); - - // Check if the file exists. - const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) { - error.FileNotFound => continue, - else => |e| return e, - }; - defer tmp.close(); - - return full_path; - } - - return null; -} - -fn linkWithZld(self: *MachO, comp: *Compilation) !void { const tracy = trace(@src()); defer tracy.end(); @@ -588,11 +423,11 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void { 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. const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: { - const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1; if (use_stage1) { const obj_basename = try std.zig.binNameAlloc(arena, .{ .root_name = self.base.options.root_name, @@ -604,8 +439,8 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void { break :blk full_obj_path; } + const obj_basename = self.base.intermediary_basename orelse break :blk null; try self.flushModule(comp); - const obj_basename = self.base.intermediary_basename.?; const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename}); break :blk full_obj_path; } else null; @@ -714,7 +549,9 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void { try positionals.append(p); } - try positionals.append(comp.compiler_rt_static_lib.?.full_object_path); + if (comp.compiler_rt_static_lib) |lib| { + try positionals.append(lib.full_object_path); + } // libc++ dep if (self.base.options.link_libcpp) { @@ -899,56 +736,60 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void { Compilation.dump_argv(argv.items); } - const sub_path = self.base.options.emit.?.sub_path; - self.base.file = try directory.handle.createFile(sub_path, .{ - .truncate = true, - .read = true, - .mode = link.determineMode(self.base.options), - }); + if (use_stage1) { + const sub_path = self.base.options.emit.?.sub_path; + self.base.file = try directory.handle.createFile(sub_path, .{ + .truncate = true, + .read = true, + .mode = link.determineMode(self.base.options), + }); - // TODO mimicking insertion of null symbol from incremental linker. - // This will need to moved. - try self.locals.append(self.base.allocator, .{ - .n_strx = 0, - .n_type = macho.N_UNDF, - .n_sect = 0, - .n_desc = 0, - .n_value = 0, - }); - try self.strtab.append(self.base.allocator, 0); + // TODO mimicking insertion of null symbol from incremental linker. + // This will need to moved. + try self.locals.append(self.base.allocator, .{ + .n_strx = 0, + .n_type = macho.N_UNDF, + .n_sect = 0, + .n_desc = 0, + .n_value = 0, + }); + try self.strtab.append(self.base.allocator, 0); - try self.populateMetadata(); - try self.addRpathLCs(rpaths.items); - try self.parseInputFiles(positionals.items, self.base.options.sysroot); - try self.parseLibs(libs.items, self.base.options.sysroot); - try self.resolveSymbols(); - try self.parseTextBlocks(); - try self.addLoadDylibLCs(); - try self.addDataInCodeLC(); - try self.addCodeSignatureLC(); + try self.populateMetadata(); + try self.addRpathLCs(rpaths.items); + try self.parseInputFiles(positionals.items, self.base.options.sysroot); + try self.parseLibs(libs.items, self.base.options.sysroot); + try self.resolveSymbols(); + try self.parseTextBlocks(); + try self.addLoadDylibLCs(); + try self.addDataInCodeLC(); + try self.addCodeSignatureLC(); - { - // Add dyld_stub_binder as the final GOT entry. - const n_strx = self.strtab_dir.getAdapted(@as([]const u8, "dyld_stub_binder"), StringSliceAdapter{ - .strtab = &self.strtab, - }) orelse unreachable; - const resolv = self.symbol_resolver.get(n_strx) orelse unreachable; - const got_index = @intCast(u32, self.got_entries.items.len); - const got_entry = GotIndirectionKey{ - .where = .undef, - .where_index = resolv.where_index, - }; - try self.got_entries.append(self.base.allocator, got_entry); - try self.got_entries_map.putNoClobber(self.base.allocator, got_entry, got_index); + { + // Add dyld_stub_binder as the final GOT entry. + const n_strx = self.strtab_dir.getAdapted(@as([]const u8, "dyld_stub_binder"), StringSliceAdapter{ + .strtab = &self.strtab, + }) orelse unreachable; + const resolv = self.symbol_resolver.get(n_strx) orelse unreachable; + const got_index = @intCast(u32, self.got_entries.items.len); + const got_entry = GotIndirectionKey{ + .where = .undef, + .where_index = resolv.where_index, + }; + try self.got_entries.append(self.base.allocator, got_entry); + try self.got_entries_map.putNoClobber(self.base.allocator, got_entry, got_index); + } + + try self.sortSections(); + try self.allocateTextSegment(); + try self.allocateDataConstSegment(); + try self.allocateDataSegment(); + self.allocateLinkeditSegment(); + try self.allocateTextBlocks(); + try self.flushZld(); + } else { + try self.flushModule(comp); } - - try self.sortSections(); - try self.allocateTextSegment(); - try self.allocateDataConstSegment(); - try self.allocateDataSegment(); - self.allocateLinkeditSegment(); - try self.allocateTextBlocks(); - try self.flushZld(); } if (!self.base.options.disable_lld_caching) { @@ -967,6 +808,158 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void { } } +pub fn flushModule(self: *MachO, comp: *Compilation) !void { + _ = comp; + const tracy = trace(@src()); + defer tracy.end(); + + const output_mode = self.base.options.output_mode; + const target = self.base.options.target; + + switch (output_mode) { + .Exe => { + if (self.entry_addr) |addr| { + // Update LC_MAIN with entry offset. + const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; + const main_cmd = &self.load_commands.items[self.main_cmd_index.?].Main; + main_cmd.entryoff = addr - text_segment.inner.vmaddr; + main_cmd.stacksize = self.base.options.stack_size_override orelse 0; + self.load_commands_dirty = true; + } + try self.writeRebaseInfoTable(); + try self.writeBindInfoTable(); + try self.writeLazyBindInfoTable(); + try self.writeExportInfo(); + try self.writeAllGlobalAndUndefSymbols(); + try self.writeIndirectSymbolTable(); + try self.writeStringTable(); + try self.updateLinkeditSegmentSizes(); + + if (self.d_sym) |*ds| { + // Flush debug symbols bundle. + try ds.flushModule(self.base.allocator, self.base.options); + } + + if (target.cpu.arch == .aarch64) { + // 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(); + } + }, + .Obj => {}, + .Lib => return error.TODOImplementWritingLibFiles, + } + + 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.got_entries_count_dirty); + assert(!self.load_commands_dirty); + assert(!self.rebase_info_dirty); + assert(!self.binding_info_dirty); + assert(!self.lazy_binding_info_dirty); + assert(!self.export_info_dirty); + assert(!self.strtab_dirty); + assert(!self.strtab_needs_relocation); + + if (target.cpu.arch == .aarch64) { + switch (output_mode) { + .Exe, .Lib => try self.writeCodeSignature(), // code signing always comes last + else => {}, + } + } +} + +fn resolveSearchDir( + arena: *Allocator, + dir: []const u8, + syslibroot: ?[]const u8, +) !?[]const u8 { + var candidates = std.ArrayList([]const u8).init(arena); + + if (fs.path.isAbsolute(dir)) { + if (syslibroot) |root| { + const full_path = try fs.path.join(arena, &[_][]const u8{ root, dir }); + try candidates.append(full_path); + } + } + + try candidates.append(dir); + + for (candidates.items) |candidate| { + // Verify that search path actually exists + var tmp = fs.cwd().openDir(candidate, .{}) catch |err| switch (err) { + error.FileNotFound => continue, + else => |e| return e, + }; + defer tmp.close(); + + return candidate; + } + + return null; +} + +fn resolveLib( + arena: *Allocator, + search_dirs: []const []const u8, + name: []const u8, + ext: []const u8, +) !?[]const u8 { + const search_name = try std.fmt.allocPrint(arena, "lib{s}{s}", .{ name, ext }); + + for (search_dirs) |dir| { + const full_path = try fs.path.join(arena, &[_][]const u8{ dir, search_name }); + + // Check if the file exists. + const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) { + error.FileNotFound => continue, + else => |e| return e, + }; + defer tmp.close(); + + return full_path; + } + + return null; +} + +fn resolveFramework( + arena: *Allocator, + search_dirs: []const []const u8, + name: []const u8, + ext: []const u8, +) !?[]const u8 { + const search_name = try std.fmt.allocPrint(arena, "{s}{s}", .{ name, ext }); + const prefix_path = try std.fmt.allocPrint(arena, "{s}.framework", .{name}); + + for (search_dirs) |dir| { + const full_path = try fs.path.join(arena, &[_][]const u8{ dir, prefix_path, search_name }); + + // Check if the file exists. + const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) { + error.FileNotFound => continue, + else => |e| return e, + }; + defer tmp.close(); + + return full_path; + } + + return null; +} + fn parseObject(self: *MachO, path: []const u8) !bool { const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) { error.FileNotFound => return false,