diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-07-21 03:18:39 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-07-21 03:18:39 -0400 |
| commit | 26984852bdfdbe3564b19f3ff7b3ecfd606c9902 (patch) | |
| tree | a64c806e3e9900c4eb0cd281a9d6946ce07421f5 /src/link/MachO.zig | |
| parent | bfe20051673e285d3b1788cd637fab9ca84d1cb1 (diff) | |
| parent | c39c46c0d12b15874b1586ff47cf473b31867918 (diff) | |
| download | zig-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.zig | 295 |
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); |
