aboutsummaryrefslogtreecommitdiff
path: root/src/link/MachO.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-07-21 03:18:39 -0400
committerGitHub <noreply@github.com>2021-07-21 03:18:39 -0400
commit26984852bdfdbe3564b19f3ff7b3ecfd606c9902 (patch)
treea64c806e3e9900c4eb0cd281a9d6946ce07421f5 /src/link/MachO.zig
parentbfe20051673e285d3b1788cd637fab9ca84d1cb1 (diff)
parentc39c46c0d12b15874b1586ff47cf473b31867918 (diff)
downloadzig-26984852bdfdbe3564b19f3ff7b3ecfd606c9902.tar.gz
zig-26984852bdfdbe3564b19f3ff7b3ecfd606c9902.zip
Merge pull request #9353 from ziglang/stage2-air
stage2: rework AIR memory layout
Diffstat (limited to 'src/link/MachO.zig')
-rw-r--r--src/link/MachO.zig295
1 files changed, 195 insertions, 100 deletions
diff --git a/src/link/MachO.zig b/src/link/MachO.zig
index df2e0134e4..02ea5856f4 100644
--- a/src/link/MachO.zig
+++ b/src/link/MachO.zig
@@ -1,6 +1,7 @@
const MachO = @This();
const std = @import("std");
+const builtin = @import("builtin");
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
const fmt = std.fmt;
@@ -22,11 +23,14 @@ const link = @import("../link.zig");
const File = link.File;
const Cache = @import("../Cache.zig");
const target_util = @import("../target.zig");
+const Air = @import("../Air.zig");
+const Liveness = @import("../Liveness.zig");
const DebugSymbols = @import("MachO/DebugSymbols.zig");
const Trie = @import("MachO/Trie.zig");
const CodeSignature = @import("MachO/CodeSignature.zig");
const Zld = @import("MachO/Zld.zig");
+const llvm_backend = @import("../codegen/llvm.zig");
usingnamespace @import("MachO/commands.zig");
@@ -34,6 +38,9 @@ pub const base_tag: File.Tag = File.Tag.macho;
base: File,
+/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
+llvm_object: ?*llvm_backend.Object = null,
+
/// Debug symbols bundle (or dSym).
d_sym: ?DebugSymbols = null,
@@ -344,8 +351,13 @@ pub const SrcFn = struct {
pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*MachO {
assert(options.object_format == .macho);
- if (options.use_llvm) return error.LLVM_BackendIsTODO_ForMachO; // TODO
- if (options.use_lld) return error.LLD_LinkingIsTODO_ForMachO; // TODO
+ if (build_options.have_llvm and options.use_llvm) {
+ const self = try createEmpty(allocator, options);
+ errdefer self.base.destroy();
+
+ self.llvm_object = try llvm_backend.Object.create(allocator, sub_path, options);
+ return self;
+ }
const file = try options.emit.?.directory.handle.createFile(sub_path, .{
.truncate = false,
@@ -1132,20 +1144,28 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
};
}
-pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
+pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
+ if (build_options.skip_non_native and builtin.object_format != .macho) {
+ @panic("Attempted to compile for object format that was disabled by build configuration");
+ }
+ if (build_options.have_llvm) {
+ if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(module, func, air, liveness);
+ }
const tracy = trace(@src());
defer tracy.end();
- if (decl.val.tag() == .extern_fn) {
- return; // TODO Should we do more when front-end analyzed extern decl?
- }
+ const decl = func.owner_decl;
var code_buffer = std.ArrayList(u8).init(self.base.allocator);
defer code_buffer.deinit();
- var debug_buffers = if (self.d_sym) |*ds| try ds.initDeclDebugBuffers(self.base.allocator, module, decl) else null;
+ var debug_buffers_buf: DebugSymbols.DeclDebugBuffers = undefined;
+ const debug_buffers = if (self.d_sym) |*ds| blk: {
+ debug_buffers_buf = try ds.initDeclDebugBuffers(self.base.allocator, module, decl);
+ break :blk &debug_buffers_buf;
+ } else null;
defer {
- if (debug_buffers) |*dbg| {
+ if (debug_buffers) |dbg| {
dbg.dbg_line_buffer.deinit();
dbg.dbg_info_buffer.deinit();
var it = dbg.dbg_info_type_relocs.valueIterator();
@@ -1156,11 +1176,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
}
}
- const res = if (debug_buffers) |*dbg|
- try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
- .ty = decl.ty,
- .val = decl.val,
- }, &code_buffer, .{
+ const res = if (debug_buffers) |dbg|
+ try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{
.dwarf = .{
.dbg_line = &dbg.dbg_line_buffer,
.dbg_info = &dbg.dbg_info_buffer,
@@ -1168,14 +1185,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
},
})
else
- try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
- .ty = decl.ty,
- .val = decl.val,
- }, &code_buffer, .none);
-
- const code = switch (res) {
- .externally_managed => |x| x,
- .appended => code_buffer.items,
+ try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);
+ switch (res) {
+ .appended => {},
.fail => |em| {
// Clear any PIE fixups for this decl.
self.pie_fixups.shrinkRetainingCapacity(0);
@@ -1185,76 +1197,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
try module.failed_decls.put(module.gpa, decl, em);
return;
},
- };
-
- const required_alignment = decl.ty.abiAlignment(self.base.options.target);
- assert(decl.link.macho.local_sym_index != 0); // Caller forgot to call allocateDeclIndexes()
- const symbol = &self.locals.items[decl.link.macho.local_sym_index];
-
- if (decl.link.macho.size != 0) {
- const capacity = decl.link.macho.capacity(self.*);
- const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, symbol.n_value, required_alignment);
- if (need_realloc) {
- const vaddr = try self.growTextBlock(&decl.link.macho, code.len, required_alignment);
-
- log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr });
-
- if (vaddr != symbol.n_value) {
- log.debug(" (writing new offset table entry)", .{});
- self.offset_table.items[decl.link.macho.offset_table_index] = .{
- .kind = .Local,
- .symbol = decl.link.macho.local_sym_index,
- .index = decl.link.macho.offset_table_index,
- };
- try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
- }
-
- symbol.n_value = vaddr;
- } else if (code.len < decl.link.macho.size) {
- self.shrinkTextBlock(&decl.link.macho, code.len);
- }
- decl.link.macho.size = code.len;
-
- const new_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{mem.spanZ(decl.name)});
- defer self.base.allocator.free(new_name);
-
- symbol.n_strx = try self.updateString(symbol.n_strx, new_name);
- symbol.n_type = macho.N_SECT;
- symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1;
- symbol.n_desc = 0;
-
- try self.writeLocalSymbol(decl.link.macho.local_sym_index);
- if (self.d_sym) |*ds|
- try ds.writeLocalSymbol(decl.link.macho.local_sym_index);
- } else {
- const decl_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{mem.spanZ(decl.name)});
- defer self.base.allocator.free(decl_name);
-
- const name_str_index = try self.makeString(decl_name);
- const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment);
-
- log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, addr });
-
- errdefer self.freeTextBlock(&decl.link.macho);
-
- symbol.* = .{
- .n_strx = name_str_index,
- .n_type = macho.N_SECT,
- .n_sect = @intCast(u8, self.text_section_index.?) + 1,
- .n_desc = 0,
- .n_value = addr,
- };
- self.offset_table.items[decl.link.macho.offset_table_index] = .{
- .kind = .Local,
- .symbol = decl.link.macho.local_sym_index,
- .index = decl.link.macho.offset_table_index,
- };
-
- try self.writeLocalSymbol(decl.link.macho.local_sym_index);
- if (self.d_sym) |*ds|
- try ds.writeLocalSymbol(decl.link.macho.local_sym_index);
- try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
}
+ const symbol = try self.placeDecl(decl, code_buffer.items.len);
// Calculate displacements to target addr (if any).
while (self.pie_fixups.popOrNull()) |fixup| {
@@ -1271,7 +1215,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
// TODO optimize instruction based on jump length (use ldr(literal) + nop if possible).
{
const inst = code_buffer.items[fixup.offset..][0..4];
- var parsed = mem.bytesAsValue(meta.TagPayload(
+ const parsed = mem.bytesAsValue(meta.TagPayload(
aarch64.Instruction,
aarch64.Instruction.pc_relative_address,
), inst);
@@ -1283,7 +1227,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
}
{
const inst = code_buffer.items[fixup.offset + 4 ..][0..4];
- var parsed = mem.bytesAsValue(meta.TagPayload(
+ const parsed = mem.bytesAsValue(meta.TagPayload(
aarch64.Instruction,
aarch64.Instruction.load_store_register,
), inst);
@@ -1306,13 +1250,13 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
.x86_64 => {
assert(stub_addr >= text_addr + fixup.len);
const displacement = try math.cast(u32, stub_addr - text_addr - fixup.len);
- var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)];
+ const placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)];
mem.writeIntSliceLittle(u32, placeholder, displacement);
},
.aarch64 => {
assert(stub_addr >= text_addr);
const displacement = try math.cast(i28, stub_addr - text_addr);
- var placeholder = code_buffer.items[fixup.start..][0..fixup.len];
+ const placeholder = code_buffer.items[fixup.start..][0..fixup.len];
mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.bl(displacement).toU32());
},
else => unreachable, // unsupported target architecture
@@ -1328,12 +1272,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
}
self.stub_fixups.shrinkRetainingCapacity(0);
- const text_section = text_segment.sections.items[self.text_section_index.?];
- const section_offset = symbol.n_value - text_section.addr;
- const file_offset = text_section.offset + section_offset;
- try self.base.file.?.pwriteAll(code, file_offset);
+ try self.writeCode(symbol, code_buffer.items);
- if (debug_buffers) |*db| {
+ if (debug_buffers) |db| {
try self.d_sym.?.commitDeclDebugInfo(
self.base.allocator,
module,
@@ -1343,11 +1284,165 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
);
}
- // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
+ // Since we updated the vaddr and the size, each corresponding export symbol also
+ // needs to be updated.
const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
try self.updateDeclExports(module, decl, decl_exports);
}
+pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
+ if (build_options.skip_non_native and builtin.object_format != .macho) {
+ @panic("Attempted to compile for object format that was disabled by build configuration");
+ }
+ if (build_options.have_llvm) {
+ if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl);
+ }
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ if (decl.val.tag() == .extern_fn) {
+ return; // TODO Should we do more when front-end analyzed extern decl?
+ }
+
+ var code_buffer = std.ArrayList(u8).init(self.base.allocator);
+ defer code_buffer.deinit();
+
+ var debug_buffers_buf: DebugSymbols.DeclDebugBuffers = undefined;
+ const debug_buffers = if (self.d_sym) |*ds| blk: {
+ debug_buffers_buf = try ds.initDeclDebugBuffers(self.base.allocator, module, decl);
+ break :blk &debug_buffers_buf;
+ } else null;
+ defer {
+ if (debug_buffers) |dbg| {
+ dbg.dbg_line_buffer.deinit();
+ dbg.dbg_info_buffer.deinit();
+ var it = dbg.dbg_info_type_relocs.valueIterator();
+ while (it.next()) |value| {
+ value.relocs.deinit(self.base.allocator);
+ }
+ dbg.dbg_info_type_relocs.deinit(self.base.allocator);
+ }
+ }
+
+ const res = if (debug_buffers) |dbg|
+ try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
+ .ty = decl.ty,
+ .val = decl.val,
+ }, &code_buffer, .{
+ .dwarf = .{
+ .dbg_line = &dbg.dbg_line_buffer,
+ .dbg_info = &dbg.dbg_info_buffer,
+ .dbg_info_type_relocs = &dbg.dbg_info_type_relocs,
+ },
+ })
+ else
+ try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
+ .ty = decl.ty,
+ .val = decl.val,
+ }, &code_buffer, .none);
+
+ const code = switch (res) {
+ .externally_managed => |x| x,
+ .appended => code_buffer.items,
+ .fail => |em| {
+ decl.analysis = .codegen_failure;
+ try module.failed_decls.put(module.gpa, decl, em);
+ return;
+ },
+ };
+ const symbol = try self.placeDecl(decl, code.len);
+ assert(self.pie_fixups.items.len == 0);
+ assert(self.stub_fixups.items.len == 0);
+
+ try self.writeCode(symbol, code);
+
+ // Since we updated the vaddr and the size, each corresponding export symbol also
+ // needs to be updated.
+ const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
+ try self.updateDeclExports(module, decl, decl_exports);
+}
+
+fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64 {
+ const required_alignment = decl.ty.abiAlignment(self.base.options.target);
+ assert(decl.link.macho.local_sym_index != 0); // Caller forgot to call allocateDeclIndexes()
+ const symbol = &self.locals.items[decl.link.macho.local_sym_index];
+
+ if (decl.link.macho.size != 0) {
+ const capacity = decl.link.macho.capacity(self.*);
+ const need_realloc = code_len > capacity or !mem.isAlignedGeneric(u64, symbol.n_value, required_alignment);
+ if (need_realloc) {
+ const vaddr = try self.growTextBlock(&decl.link.macho, code_len, required_alignment);
+
+ log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr });
+
+ if (vaddr != symbol.n_value) {
+ log.debug(" (writing new offset table entry)", .{});
+ self.offset_table.items[decl.link.macho.offset_table_index] = .{
+ .kind = .Local,
+ .symbol = decl.link.macho.local_sym_index,
+ .index = decl.link.macho.offset_table_index,
+ };
+ try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
+ }
+
+ symbol.n_value = vaddr;
+ } else if (code_len < decl.link.macho.size) {
+ self.shrinkTextBlock(&decl.link.macho, code_len);
+ }
+ decl.link.macho.size = code_len;
+
+ const new_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{mem.spanZ(decl.name)});
+ defer self.base.allocator.free(new_name);
+
+ symbol.n_strx = try self.updateString(symbol.n_strx, new_name);
+ symbol.n_type = macho.N_SECT;
+ symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1;
+ symbol.n_desc = 0;
+
+ try self.writeLocalSymbol(decl.link.macho.local_sym_index);
+ if (self.d_sym) |*ds|
+ try ds.writeLocalSymbol(decl.link.macho.local_sym_index);
+ } else {
+ const decl_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{mem.spanZ(decl.name)});
+ defer self.base.allocator.free(decl_name);
+
+ const name_str_index = try self.makeString(decl_name);
+ const addr = try self.allocateTextBlock(&decl.link.macho, code_len, required_alignment);
+
+ log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, addr });
+
+ errdefer self.freeTextBlock(&decl.link.macho);
+
+ symbol.* = .{
+ .n_strx = name_str_index,
+ .n_type = macho.N_SECT,
+ .n_sect = @intCast(u8, self.text_section_index.?) + 1,
+ .n_desc = 0,
+ .n_value = addr,
+ };
+ self.offset_table.items[decl.link.macho.offset_table_index] = .{
+ .kind = .Local,
+ .symbol = decl.link.macho.local_sym_index,
+ .index = decl.link.macho.offset_table_index,
+ };
+
+ try self.writeLocalSymbol(decl.link.macho.local_sym_index);
+ if (self.d_sym) |*ds|
+ try ds.writeLocalSymbol(decl.link.macho.local_sym_index);
+ try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
+ }
+
+ return symbol;
+}
+
+fn writeCode(self: *MachO, symbol: *macho.nlist_64, code: []const u8) !void {
+ const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
+ const text_section = text_segment.sections.items[self.text_section_index.?];
+ const section_offset = symbol.n_value - text_section.addr;
+ const file_offset = text_section.offset + section_offset;
+ try self.base.file.?.pwriteAll(code, file_offset);
+}
+
pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {
if (self.d_sym) |*ds| {
try ds.updateDeclLineNumber(module, decl);