Commit 819ef52104
Changed files (1)
src
link
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();
+ 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);
+ }
}
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,