diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index ee5e58ae05..88f211cc29 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -4013,10 +4013,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .import => unreachable, }; const atom_index = switch (self.bin_file.tag) { - .macho => blk: { - const macho_file = self.bin_file.cast(link.File.MachO).?; - const atom = try macho_file.getOrCreateAtomForDecl(self.owner_decl); - break :blk macho_file.getAtom(atom).getSymbolIndex().?; + .macho => { + // const macho_file = self.bin_file.cast(link.File.MachO).?; + // const atom = try macho_file.getOrCreateAtomForDecl(self.owner_decl); + // break :blk macho_file.getAtom(atom).getSymbolIndex().?; + @panic("TODO store"); }, .coff => blk: { const coff_file = self.bin_file.cast(link.File.Coff).?; @@ -4321,14 +4322,16 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier const got_addr = @as(u32, @intCast(sym.zigGotAddress(elf_file))); try self.genSetReg(Type.usize, .x30, .{ .memory = got_addr }); } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { - const atom = try macho_file.getOrCreateAtomForDecl(func.owner_decl); - const sym_index = macho_file.getAtom(atom).getSymbolIndex().?; - try self.genSetReg(Type.u64, .x30, .{ - .linker_load = .{ - .type = .got, - .sym_index = sym_index, - }, - }); + _ = macho_file; + @panic("TODO airCall"); + // const atom = try macho_file.getOrCreateAtomForDecl(func.owner_decl); + // const sym_index = macho_file.getAtom(atom).getSymbolIndex().?; + // try self.genSetReg(Type.u64, .x30, .{ + // .linker_load = .{ + // .type = .got, + // .sym_index = sym_index, + // }, + // }); } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { const atom = try coff_file.getOrCreateAtomForDecl(func.owner_decl); const sym_index = coff_file.getAtom(atom).getSymbolIndex().?; @@ -4352,18 +4355,20 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier const decl_name = mod.intern_pool.stringToSlice(mod.declPtr(extern_func.decl).name); const lib_name = mod.intern_pool.stringToSliceUnwrap(extern_func.lib_name); if (self.bin_file.cast(link.File.MachO)) |macho_file| { - const sym_index = try macho_file.getGlobalSymbol(decl_name, lib_name); - const atom = try macho_file.getOrCreateAtomForDecl(self.owner_decl); - const atom_index = macho_file.getAtom(atom).getSymbolIndex().?; - _ = try self.addInst(.{ - .tag = .call_extern, - .data = .{ - .relocation = .{ - .atom_index = atom_index, - .sym_index = sym_index, - }, - }, - }); + _ = macho_file; + @panic("TODO airCall"); + // const sym_index = try macho_file.getGlobalSymbol(decl_name, lib_name); + // const atom = try macho_file.getOrCreateAtomForDecl(self.owner_decl); + // const atom_index = macho_file.getAtom(atom).getSymbolIndex().?; + // _ = try self.addInst(.{ + // .tag = .call_extern, + // .data = .{ + // .relocation = .{ + // .atom_index = atom_index, + // .sym_index = sym_index, + // }, + // }, + // }); } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { const sym_index = try coff_file.getGlobalSymbol(decl_name, lib_name); try self.genSetReg(Type.u64, .x30, .{ @@ -5532,10 +5537,11 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro .import => unreachable, }; const atom_index = switch (self.bin_file.tag) { - .macho => blk: { - const macho_file = self.bin_file.cast(link.File.MachO).?; - const atom = try macho_file.getOrCreateAtomForDecl(self.owner_decl); - break :blk macho_file.getAtom(atom).getSymbolIndex().?; + .macho => { + // const macho_file = self.bin_file.cast(link.File.MachO).?; + // const atom = try macho_file.getOrCreateAtomForDecl(self.owner_decl); + // break :blk macho_file.getAtom(atom).getSymbolIndex().?; + @panic("TODO genSetStack"); }, .coff => blk: { const coff_file = self.bin_file.cast(link.File.Coff).?; @@ -5653,10 +5659,11 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .import => .load_memory_import, }; const atom_index = switch (self.bin_file.tag) { - .macho => blk: { - const macho_file = self.bin_file.cast(link.File.MachO).?; - const atom = try macho_file.getOrCreateAtomForDecl(self.owner_decl); - break :blk macho_file.getAtom(atom).getSymbolIndex().?; + .macho => { + @panic("TODO genSetReg"); + // const macho_file = self.bin_file.cast(link.File.MachO).?; + // const atom = try macho_file.getOrCreateAtomForDecl(self.owner_decl); + // break :blk macho_file.getAtom(atom).getSymbolIndex().?; }, .coff => blk: { const coff_file = self.bin_file.cast(link.File.Coff).?; @@ -5850,10 +5857,11 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I .import => unreachable, }; const atom_index = switch (self.bin_file.tag) { - .macho => blk: { - const macho_file = self.bin_file.cast(link.File.MachO).?; - const atom = try macho_file.getOrCreateAtomForDecl(self.owner_decl); - break :blk macho_file.getAtom(atom).getSymbolIndex().?; + .macho => { + @panic("TODO genSetStackArgument"); + // const macho_file = self.bin_file.cast(link.File.MachO).?; + // const atom = try macho_file.getOrCreateAtomForDecl(self.owner_decl); + // break :blk macho_file.getAtom(atom).getSymbolIndex().?; }, .coff => blk: { const coff_file = self.bin_file.cast(link.File.Coff).?; diff --git a/src/arch/aarch64/Emit.zig b/src/arch/aarch64/Emit.zig index 96eb5b8b30..d14c0c8aad 100644 --- a/src/arch/aarch64/Emit.zig +++ b/src/arch/aarch64/Emit.zig @@ -677,6 +677,7 @@ fn mirDebugEpilogueBegin(emit: *Emit) !void { fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void { assert(emit.mir.instructions.items(.tag)[inst] == .call_extern); const relocation = emit.mir.instructions.items(.data)[inst].relocation; + _ = relocation; const offset = blk: { const offset = @as(u32, @intCast(emit.code.items.len)); @@ -684,19 +685,22 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void { try emit.writeInstruction(Instruction.bl(0)); break :blk offset; }; + _ = offset; if (emit.bin_file.cast(link.File.MachO)) |macho_file| { - // Add relocation to the decl. - const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index }).?; - const target = macho_file.getGlobalByIndex(relocation.sym_index); - try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ - .type = .branch, - .target = target, - .offset = offset, - .addend = 0, - .pcrel = true, - .length = 2, - }); + _ = macho_file; + @panic("TODO mirCallExtern"); + // // Add relocation to the decl. + // const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index }).?; + // const target = macho_file.getGlobalByIndex(relocation.sym_index); + // try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ + // .type = .branch, + // .target = target, + // .offset = offset, + // .addend = 0, + // .pcrel = true, + // .length = 2, + // }); } else if (emit.bin_file.cast(link.File.Coff)) |_| { unreachable; // Calling imports is handled via `.load_memory_import` } else { @@ -900,32 +904,34 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void { } if (emit.bin_file.cast(link.File.MachO)) |macho_file| { - const Atom = link.File.MachO.Atom; - const Relocation = Atom.Relocation; - const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = data.atom_index }).?; - try Atom.addRelocations(macho_file, atom_index, &[_]Relocation{ .{ - .target = .{ .sym_index = data.sym_index }, - .offset = offset, - .addend = 0, - .pcrel = true, - .length = 2, - .type = switch (tag) { - .load_memory_got, .load_memory_ptr_got => Relocation.Type.got_page, - .load_memory_direct, .load_memory_ptr_direct => Relocation.Type.page, - else => unreachable, - }, - }, .{ - .target = .{ .sym_index = data.sym_index }, - .offset = offset + 4, - .addend = 0, - .pcrel = false, - .length = 2, - .type = switch (tag) { - .load_memory_got, .load_memory_ptr_got => Relocation.Type.got_pageoff, - .load_memory_direct, .load_memory_ptr_direct => Relocation.Type.pageoff, - else => unreachable, - }, - } }); + _ = macho_file; + @panic("TODO mirLoadMemoryPie"); + // const Atom = link.File.MachO.Atom; + // const Relocation = Atom.Relocation; + // const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = data.atom_index }).?; + // try Atom.addRelocations(macho_file, atom_index, &[_]Relocation{ .{ + // .target = .{ .sym_index = data.sym_index }, + // .offset = offset, + // .addend = 0, + // .pcrel = true, + // .length = 2, + // .type = switch (tag) { + // .load_memory_got, .load_memory_ptr_got => Relocation.Type.got_page, + // .load_memory_direct, .load_memory_ptr_direct => Relocation.Type.page, + // else => unreachable, + // }, + // }, .{ + // .target = .{ .sym_index = data.sym_index }, + // .offset = offset + 4, + // .addend = 0, + // .pcrel = false, + // .length = 2, + // .type = switch (tag) { + // .load_memory_got, .load_memory_ptr_got => Relocation.Type.got_pageoff, + // .load_memory_direct, .load_memory_ptr_direct => Relocation.Type.pageoff, + // else => unreachable, + // }, + // } }); } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| { const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = data.atom_index, .file = null }).?; const target = switch (tag) { diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 55e241cbd4..870b6a2472 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -139,8 +139,10 @@ const Owner = union(enum) { if (ctx.bin_file.cast(link.File.Elf)) |elf_file| { return elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index); } else if (ctx.bin_file.cast(link.File.MachO)) |macho_file| { - const atom = try macho_file.getOrCreateAtomForDecl(decl_index); - return macho_file.getAtom(atom).getSymbolIndex().?; + _ = macho_file; + // const atom = try macho_file.getOrCreateAtomForDecl(decl_index); + // return macho_file.getAtom(atom).getSymbolIndex().?; + @panic("TODO getSymbolIndex"); } else if (ctx.bin_file.cast(link.File.Coff)) |coff_file| { const atom = try coff_file.getOrCreateAtomForDecl(decl_index); return coff_file.getAtom(atom).getSymbolIndex().?; @@ -153,9 +155,11 @@ const Owner = union(enum) { return elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, lazy_sym) catch |err| ctx.fail("{s} creating lazy symbol", .{@errorName(err)}); } else if (ctx.bin_file.cast(link.File.MachO)) |macho_file| { - const atom = macho_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| - return ctx.fail("{s} creating lazy symbol", .{@errorName(err)}); - return macho_file.getAtom(atom).getSymbolIndex().?; + _ = macho_file; + // const atom = macho_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| + // return ctx.fail("{s} creating lazy symbol", .{@errorName(err)}); + // return macho_file.getAtom(atom).getSymbolIndex().?; + @panic("TODO getSymbolIndex"); } else if (ctx.bin_file.cast(link.File.Coff)) |coff_file| { const atom = coff_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| return ctx.fail("{s} creating lazy symbol", .{@errorName(err)}); @@ -10951,10 +10955,12 @@ fn genCall(self: *Self, info: union(enum) { try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym_index }); try self.asmRegister(.{ ._, .call }, .rax); } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { - const atom = try macho_file.getOrCreateAtomForDecl(func.owner_decl); - const sym_index = macho_file.getAtom(atom).getSymbolIndex().?; - try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym_index }); - try self.asmRegister(.{ ._, .call }, .rax); + _ = macho_file; + @panic("TODO genCall"); + // const atom = try macho_file.getOrCreateAtomForDecl(func.owner_decl); + // const sym_index = macho_file.getAtom(atom).getSymbolIndex().?; + // try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym_index }); + // try self.asmRegister(.{ ._, .call }, .rax); } else if (self.bin_file.cast(link.File.Plan9)) |p9| { const atom_index = try p9.seeDecl(func.owner_decl); const atom = p9.getAtom(atom_index); @@ -13814,11 +13820,15 @@ fn genExternSymbolRef( _ = try self.addInst(.{ .tag = .call, .ops = .extern_fn_reloc, - .data = .{ .reloc = .{ - .atom_index = atom_index, - .sym_index = link.File.MachO.global_symbol_bit | global_index, - } }, + .data = .{ + .reloc = .{ + .atom_index = atom_index, + // .sym_index = link.File.MachO.global_symbol_bit | global_index, + .sym_index = global_index, + }, + }, }); + @panic("TODO genExternSymbolRef"); } else return self.fail("TODO implement calling extern functions", .{}); } @@ -13906,19 +13916,21 @@ fn genLazySymbolRef( else => unreachable, } } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { - const atom_index = macho_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| - return self.fail("{s} creating lazy symbol", .{@errorName(err)}); - const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; - switch (tag) { - .lea, .call => try self.genSetReg(reg, Type.usize, .{ .lea_got = sym_index }), - .mov => try self.genSetReg(reg, Type.usize, .{ .load_got = sym_index }), - else => unreachable, - } - switch (tag) { - .lea, .mov => {}, - .call => try self.asmRegister(.{ ._, .call }, reg), - else => unreachable, - } + _ = macho_file; + @panic("TODO genLazySymbolRef"); + // const atom_index = macho_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| + // return self.fail("{s} creating lazy symbol", .{@errorName(err)}); + // const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; + // switch (tag) { + // .lea, .call => try self.genSetReg(reg, Type.usize, .{ .lea_got = sym_index }), + // .mov => try self.genSetReg(reg, Type.usize, .{ .load_got = sym_index }), + // else => unreachable, + // } + // switch (tag) { + // .lea, .mov => {}, + // .call => try self.asmRegister(.{ ._, .call }, reg), + // else => unreachable, + // } } else { return self.fail("TODO implement genLazySymbol for x86_64 {s}", .{@tagName(self.bin_file.tag)}); } diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 2c976bd00d..97c6cdfc1b 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -49,21 +49,23 @@ pub fn emitMir(emit: *Emit) Error!void { .r_addend = -4, }); } else if (emit.lower.bin_file.cast(link.File.MachO)) |macho_file| { - // Add relocation to the decl. - const atom_index = - macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?; - const target = if (link.File.MachO.global_symbol_bit & symbol.sym_index != 0) - macho_file.getGlobalByIndex(link.File.MachO.global_symbol_mask & symbol.sym_index) - else - link.File.MachO.SymbolWithLoc{ .sym_index = symbol.sym_index }; - try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ - .type = .branch, - .target = target, - .offset = end_offset - 4, - .addend = 0, - .pcrel = true, - .length = 2, - }); + _ = macho_file; + @panic("TODO emitMir"); + // // Add relocation to the decl. + // const atom_index = + // macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?; + // const target = if (link.File.MachO.global_symbol_bit & symbol.sym_index != 0) + // macho_file.getGlobalByIndex(link.File.MachO.global_symbol_mask & symbol.sym_index) + // else + // link.File.MachO.SymbolWithLoc{ .sym_index = symbol.sym_index }; + // try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ + // .type = .branch, + // .target = target, + // .offset = end_offset - 4, + // .addend = 0, + // .pcrel = true, + // .length = 2, + // }); } else if (emit.lower.bin_file.cast(link.File.Coff)) |coff_file| { // Add relocation to the decl. const atom_index = coff_file.getAtomIndexForSymbol( @@ -157,25 +159,27 @@ pub fn emitMir(emit: *Emit) Error!void { => |symbol| if (emit.lower.bin_file.cast(link.File.Elf)) |_| { unreachable; } else if (emit.lower.bin_file.cast(link.File.MachO)) |macho_file| { - const atom_index = - macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?; - const target = if (link.File.MachO.global_symbol_bit & symbol.sym_index != 0) - macho_file.getGlobalByIndex(link.File.MachO.global_symbol_mask & symbol.sym_index) - else - link.File.MachO.SymbolWithLoc{ .sym_index = symbol.sym_index }; - try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ - .type = switch (lowered_relocs[0].target) { - .linker_got => .got, - .linker_direct => .signed, - .linker_tlv => .tlv, - else => unreachable, - }, - .target = target, - .offset = @intCast(end_offset - 4), - .addend = 0, - .pcrel = true, - .length = 2, - }); + _ = macho_file; + @panic("TODO emitMir"); + // const atom_index = + // macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?; + // const target = if (link.File.MachO.global_symbol_bit & symbol.sym_index != 0) + // macho_file.getGlobalByIndex(link.File.MachO.global_symbol_mask & symbol.sym_index) + // else + // link.File.MachO.SymbolWithLoc{ .sym_index = symbol.sym_index }; + // try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ + // .type = switch (lowered_relocs[0].target) { + // .linker_got => .got, + // .linker_direct => .signed, + // .linker_tlv => .tlv, + // else => unreachable, + // }, + // .target = target, + // .offset = @intCast(end_offset - 4), + // .addend = 0, + // .pcrel = true, + // .length = 2, + // }); } else if (emit.lower.bin_file.cast(link.File.Coff)) |coff_file| { const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index, diff --git a/src/codegen.zig b/src/codegen.zig index 1ac8626a79..f9263c2a69 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -984,20 +984,22 @@ fn genDeclRef( } return GenResult.mcv(.{ .load_symbol = sym.esym_index }); } else if (lf.cast(link.File.MachO)) |macho_file| { + _ = macho_file; if (is_extern) { // TODO make this part of getGlobalSymbol - const name = zcu.intern_pool.stringToSlice(decl.name); - const sym_name = try std.fmt.allocPrint(gpa, "_{s}", .{name}); - defer gpa.free(sym_name); - const global_index = try macho_file.addUndefined(sym_name, .{ .add_got = true }); - return GenResult.mcv(.{ .load_got = link.File.MachO.global_symbol_bit | global_index }); + // const name = zcu.intern_pool.stringToSlice(decl.name); + // const sym_name = try std.fmt.allocPrint(gpa, "_{s}", .{name}); + // defer gpa.free(sym_name); + // const global_index = try macho_file.addUndefined(sym_name, .{ .add_got = true }); + // return GenResult.mcv(.{ .load_got = link.File.MachO.global_symbol_bit | global_index }); } - const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index); - const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; - if (is_threadlocal) { - return GenResult.mcv(.{ .load_tlv = sym_index }); - } - return GenResult.mcv(.{ .load_got = sym_index }); + // const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index); + // const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; + // if (is_threadlocal) { + // return GenResult.mcv(.{ .load_tlv = sym_index }); + // } + // return GenResult.mcv(.{ .load_got = sym_index }); + @panic("TODO genDeclRef"); } else if (lf.cast(link.File.Coff)) |coff_file| { if (is_extern) { const name = zcu.intern_pool.stringToSlice(decl.name); diff --git a/src/link/MachO.zig b/src/link/MachO.zig index cb26aa0ca3..895ec1cc2e 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -7,8 +7,6 @@ llvm_object: ?*LlvmObject = null, /// Debug symbols bundle (or dSym). d_sym: ?DebugSymbols = null, -mode: Mode, - dyld_info_cmd: macho.dyld_info_command = .{}, symtab_cmd: macho.symtab_command = .{}, dysymtab_cmd: macho.dysymtab_command = .{}, @@ -17,12 +15,6 @@ data_in_code_cmd: macho.linkedit_data_command = .{ .cmd = .DATA_IN_CODE }, uuid_cmd: macho.uuid_command = .{ .uuid = [_]u8{0} ** 16 }, codesig_cmd: macho.linkedit_data_command = .{ .cmd = .CODE_SIGNATURE }, -objects: std.ArrayListUnmanaged(Object) = .{}, -archives: std.ArrayListUnmanaged(Archive) = .{}, -dylibs: std.ArrayListUnmanaged(Dylib) = .{}, -dylibs_map: std.StringHashMapUnmanaged(u16) = .{}, -referenced_dylibs: std.AutoArrayHashMapUnmanaged(u16, void) = .{}, - segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{}, sections: std.MultiArrayList(Section) = .{}, @@ -48,93 +40,11 @@ got_section_index: ?u8 = null, la_symbol_ptr_section_index: ?u8 = null, tlv_ptr_section_index: ?u8 = null, -locals: std.ArrayListUnmanaged(macho.nlist_64) = .{}, -globals: std.ArrayListUnmanaged(SymbolWithLoc) = .{}, -resolver: std.StringHashMapUnmanaged(u32) = .{}, -unresolved: std.AutoArrayHashMapUnmanaged(u32, void) = .{}, - -locals_free_list: std.ArrayListUnmanaged(u32) = .{}, -globals_free_list: std.ArrayListUnmanaged(u32) = .{}, - -dyld_stub_binder_index: ?u32 = null, -dyld_private_atom_index: ?Atom.Index = null, - strtab: StringTable = .{}, -got_table: TableSection(SymbolWithLoc) = .{}, -stub_table: TableSection(SymbolWithLoc) = .{}, -tlv_ptr_table: TableSection(SymbolWithLoc) = .{}, - -thunk_table: std.AutoHashMapUnmanaged(Atom.Index, thunks.Thunk.Index) = .{}, -thunks: std.ArrayListUnmanaged(thunks.Thunk) = .{}, - -segment_table_dirty: bool = false, -got_table_count_dirty: bool = false, -got_table_contents_dirty: bool = false, -stub_table_count_dirty: bool = false, -stub_table_contents_dirty: bool = false, -stub_helper_preamble_allocated: bool = false, - /// List of atoms that are either synthetic or map directly to the Zig source program. atoms: std.ArrayListUnmanaged(Atom) = .{}, -/// Table of atoms indexed by the symbol index. -atom_by_index_table: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{}, - -/// Table of unnamed constants associated with a parent `Decl`. -/// We store them here so that we can free the constants whenever the `Decl` -/// needs updating or is freed. -/// -/// For example, -/// -/// ```zig -/// const Foo = struct{ -/// a: u8, -/// }; -/// -/// pub fn main() void { -/// var foo = Foo{ .a = 1 }; -/// _ = foo; -/// } -/// ``` -/// -/// value assigned to label `foo` is an unnamed constant belonging/associated -/// with `Decl` `main`, and lives as long as that `Decl`. -unnamed_const_atoms: UnnamedConstTable = .{}, -anon_decls: AnonDeclTable = .{}, - -/// A table of relocations indexed by the owning them `Atom`. -/// Note that once we refactor `Atom`'s lifetime and ownership rules, -/// this will be a table indexed by index into the list of Atoms. -relocs: RelocationTable = .{}, -/// TODO I do not have time to make this right but this will go once -/// MachO linker is rewritten more-or-less to feature the same resolution -/// mechanism as the ELF linker. -actions: ActionTable = .{}, - -/// A table of rebases indexed by the owning them `Atom`. -/// Note that once we refactor `Atom`'s lifetime and ownership rules, -/// this will be a table indexed by index into the list of Atoms. -rebases: RebaseTable = .{}, - -/// A table of bindings indexed by the owning them `Atom`. -/// Note that once we refactor `Atom`'s lifetime and ownership rules, -/// this will be a table indexed by index into the list of Atoms. -bindings: BindingTable = .{}, - -/// Table of tracked LazySymbols. -lazy_syms: LazySymbolTable = .{}, - -/// Table of tracked Decls. -decls: DeclTable = .{}, - -/// Table of threadlocal variables descriptors. -/// They are emitted in the `__thread_vars` section. -tlv_table: TlvSymbolTable = .{}, - -/// Hot-code swapping state. -hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{}, - sdk_layout: ?SdkLayout, /// Size of the __PAGEZERO segment. pagezero_vmsize: u64, @@ -152,6 +62,9 @@ install_name: ?[]const u8, entitlements: ?[]const u8, compatibility_version: ?std.SemanticVersion, +/// Hot-code swapping state. +hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{}, + /// When adding a new field, remember to update `hashAddFrameworks`. pub const Framework = struct { needed: bool = false, @@ -183,24 +96,18 @@ pub fn createEmpty( ) !*MachO { const target = comp.root_mod.resolved_target.result; assert(target.ofmt == .macho); - const use_llvm = comp.config.use_llvm; + const gpa = comp.gpa; + const use_llvm = comp.config.use_llvm; + const opt_zcu = comp.module; const optimize_mode = comp.root_mod.optimize_mode; const output_mode = comp.config.output_mode; const link_mode = comp.config.link_mode; - // TODO: get rid of zld mode - const mode: Mode = if (use_llvm or !comp.config.have_zcu or comp.cache_use == .whole) - .zld - else - .incremental; - - // If using "zld mode" to link, this code should produce an object file so that it - // can be passed to "zld mode". TODO: get rid of "zld mode". // If using LLVM to generate the object file for the zig compilation unit, // we need a place to put the object file so that it can be subsequently // handled. - const zcu_object_sub_path = if (mode != .zld and !use_llvm) + const zcu_object_sub_path = if (!use_llvm) null else try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path}); @@ -221,7 +128,6 @@ pub fn createEmpty( .build_id = options.build_id, .rpath_list = options.rpath_list, }, - .mode = mode, .pagezero_vmsize = options.pagezero_size orelse default_pagezero_vmsize, .headerpad_size = options.headerpad_size orelse default_headerpad_size, .headerpad_max_install_names = options.headerpad_max_install_names, @@ -243,62 +149,48 @@ pub fn createEmpty( } errdefer self.base.destroy(); - log.debug("selected linker mode '{s}'", .{@tagName(self.mode)}); - - if (mode == .zld) { - // TODO: get rid of zld mode - return self; - } - - const file = try emit.directory.handle.createFile(emit.sub_path, .{ + self.base.file = try emit.directory.handle.createFile(emit.sub_path, .{ .truncate = true, .read = true, .mode = link.File.determineMode(false, output_mode, link_mode), }); - self.base.file = file; - - if (comp.config.debug_format != .strip and comp.module != null) { - // Create dSYM bundle. - log.debug("creating {s}.dSYM bundle", .{emit.sub_path}); - - const d_sym_path = try std.fmt.allocPrint( - arena, - "{s}.dSYM" ++ fs.path.sep_str ++ "Contents" ++ fs.path.sep_str ++ "Resources" ++ fs.path.sep_str ++ "DWARF", - .{emit.sub_path}, - ); - - var d_sym_bundle = try emit.directory.handle.makeOpenPath(d_sym_path, .{}); - defer d_sym_bundle.close(); - - const d_sym_file = try d_sym_bundle.createFile(emit.sub_path, .{ - .truncate = false, - .read = true, - }); - - self.d_sym = .{ - .allocator = gpa, - .dwarf = link.File.Dwarf.init(&self.base, .dwarf32), - .file = d_sym_file, - }; - } // Index 0 is always a null symbol. - try self.locals.append(gpa, .{ - .n_strx = 0, - .n_type = 0, - .n_sect = 0, - .n_desc = 0, - .n_value = 0, - }); + // try self.locals.append(gpa, null_sym); try self.strtab.buffer.append(gpa, 0); - try self.populateMissingMetadata(.{ - .symbol_count_hint = options.symbol_count_hint, - .program_code_size_hint = options.program_code_size_hint, - }); + // TODO: init - if (self.d_sym) |*d_sym| { - try d_sym.populateMissingMetadata(self); + if (opt_zcu) |zcu| { + if (!use_llvm) { + _ = zcu; + // TODO: create .zig_object + + if (comp.config.debug_format != .strip) { + // Create dSYM bundle. + log.debug("creating {s}.dSYM bundle", .{emit.sub_path}); + + const d_sym_path = try std.fmt.allocPrint( + arena, + "{s}.dSYM" ++ fs.path.sep_str ++ "Contents" ++ fs.path.sep_str ++ "Resources" ++ fs.path.sep_str ++ "DWARF", + .{emit.sub_path}, + ); + + var d_sym_bundle = try emit.directory.handle.makeOpenPath(d_sym_path, .{}); + defer d_sym_bundle.close(); + + const d_sym_file = try d_sym_bundle.createFile(emit.sub_path, .{ + .truncate = false, + .read = true, + }); + + self.d_sym = .{ + .allocator = gpa, + .dwarf = link.File.Dwarf.init(&self.base, .dwarf32), + .file = d_sym_file, + }; + } + } } return self; @@ -316,26 +208,8 @@ pub fn open( } pub fn flush(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void { - const comp = self.base.comp; - const gpa = comp.gpa; - const output_mode = comp.config.output_mode; - - if (output_mode == .Lib and comp.config.link_mode == .Static) { - if (build_options.have_llvm) { - return self.base.linkAsArchive(arena, prog_node); - } else { - try comp.link_errors.ensureUnusedCapacity(gpa, 1); - comp.link_errors.appendAssumeCapacity(.{ - .msg = try gpa.dupe(u8, "TODO: non-LLVM archiver for MachO object files"), - }); - return error.FlushFailure; - } - } - - switch (self.mode) { - .zld => return zld.linkWithZld(self, arena, prog_node), - .incremental => return self.flushModule(arena, prog_node), - } + // TODO: what else should we do in flush? Is it actually needed at all? + try self.flushModule(arena, prog_node); } pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void { @@ -344,261 +218,40 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node const comp = self.base.comp; const gpa = comp.gpa; + _ = gpa; if (self.llvm_object) |llvm_object| { try self.base.emitLlvmObject(arena, llvm_object, prog_node); - return; } var sub_prog_node = prog_node.start("MachO Flush", 0); sub_prog_node.activate(); defer sub_prog_node.end(); - const output_mode = comp.config.output_mode; - const module = comp.module orelse return error.LinkingWithoutZigSourceUnimplemented; const target = comp.root_mod.resolved_target.result; - - if (self.lazy_syms.getPtr(.none)) |metadata| { - // Most lazy symbols can be updated on first use, but - // anyerror needs to wait for everything to be flushed. - if (metadata.text_state != .unused) self.updateLazySymbolAtom( - File.LazySymbol.initDecl(.code, null, module), - metadata.text_atom, - self.text_section_index.?, - ) catch |err| return switch (err) { - error.CodegenFail => error.FlushFailure, - else => |e| e, - }; - if (metadata.data_const_state != .unused) self.updateLazySymbolAtom( - File.LazySymbol.initDecl(.const_data, null, module), - metadata.data_const_atom, - self.data_const_section_index.?, - ) catch |err| return switch (err) { - error.CodegenFail => error.FlushFailure, - else => |e| e, - }; - } - for (self.lazy_syms.values()) |*metadata| { - if (metadata.text_state != .unused) metadata.text_state = .flushed; - if (metadata.data_const_state != .unused) metadata.data_const_state = .flushed; - } - - if (self.d_sym) |*d_sym| { - try d_sym.dwarf.flushModule(module); - } - - var libs = std.StringArrayHashMap(link.SystemLib).init(arena); - try self.resolveLibSystem(arena, comp, &libs); - - self.base.releaseLock(); - - for (self.dylibs.items) |*dylib| { - dylib.deinit(gpa); - } - self.dylibs.clearRetainingCapacity(); - self.dylibs_map.clearRetainingCapacity(); - self.referenced_dylibs.clearRetainingCapacity(); - - var dependent_libs = std.fifo.LinearFifo(DylibReExportInfo, .Dynamic).init(arena); - - for (libs.keys(), libs.values()) |path, lib| { - const in_file = try std.fs.cwd().openFile(path, .{}); - defer in_file.close(); - - var parse_ctx = ParseErrorCtx.init(gpa); - defer parse_ctx.deinit(); - - self.parseLibrary( - in_file, - path, - lib, - false, - false, - null, - &dependent_libs, - &parse_ctx, - ) catch |err| try self.handleAndReportParseError(path, err, &parse_ctx); - } - - try self.parseDependentLibs(&dependent_libs); - - try self.resolveSymbols(); - - if (self.getEntryPoint() == null) { - comp.link_error_flags.no_entry_point_found = true; - } - if (self.unresolved.count() > 0) { - try self.reportUndefined(); - return error.FlushFailure; - } - - { - var it = self.actions.iterator(); - while (it.next()) |entry| { - const global_index = entry.key_ptr.*; - const global = self.globals.items[global_index]; - const flags = entry.value_ptr.*; - if (flags.add_got) try self.addGotEntry(global); - if (flags.add_stub) try self.addStubEntry(global); + _ = target; + const directory = self.base.emit.directory; + const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); + const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: { + if (fs.path.dirname(full_out_path)) |dirname| { + break :blk try fs.path.join(arena, &.{ dirname, path }); + } else { + break :blk path; } - } - - try self.createDyldPrivateAtom(); - try self.writeStubHelperPreamble(); - - if (output_mode == .Exe and self.getEntryPoint() != null) { - const global = self.getEntryPoint().?; - if (self.getSymbol(global).undf()) { - // We do one additional check here in case the entry point was found in one of the dylibs. - // (I actually have no idea what this would imply but it is a possible outcome and so we - // support it.) - try self.addStubEntry(global); - } - } - - try self.allocateSpecialSymbols(); - - for (self.relocs.keys()) |atom_index| { - const relocs = self.relocs.get(atom_index).?; - const needs_update = for (relocs.items) |reloc| { - if (reloc.dirty) break true; - } else false; - - if (!needs_update) continue; - - const atom = self.getAtom(atom_index); - const sym = atom.getSymbol(self); - const section = self.sections.get(sym.n_sect - 1).header; - const file_offset = section.offset + sym.n_value - section.addr; - - var code = std.ArrayList(u8).init(gpa); - defer code.deinit(); - try code.resize(math.cast(usize, atom.size) orelse return error.Overflow); - - const amt = try self.base.file.?.preadAll(code.items, file_offset); - if (amt != code.items.len) return error.InputOutput; - - try self.writeAtom(atom_index, code.items); - } - - // Update GOT if it got moved in memory. - if (self.got_table_contents_dirty) { - for (self.got_table.entries.items, 0..) |entry, i| { - if (!self.got_table.lookup.contains(entry)) continue; - // TODO: write all in one go rather than incrementally. - try self.writeOffsetTableEntry(i); - } - self.got_table_contents_dirty = false; - } - - // Update stubs if we moved any section in memory. - // TODO: we probably don't need to update all sections if only one got moved. - if (self.stub_table_contents_dirty) { - for (self.stub_table.entries.items, 0..) |entry, i| { - if (!self.stub_table.lookup.contains(entry)) continue; - // TODO: write all in one go rather than incrementally. - try self.writeStubTableEntry(i); - } - self.stub_table_contents_dirty = false; - } - - if (build_options.enable_logging) { - self.logSymtab(); - self.logSections(); - self.logAtoms(); - } - - try self.writeLinkeditSegmentData(); - - var codesig: ?CodeSignature = if (self.requiresCodeSignature()) blk: { - // 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. - var codesig = CodeSignature.init(getPageSize(target.cpu.arch)); - codesig.code_directory.ident = self.base.emit.sub_path; - if (self.entitlements) |path| { - try codesig.addEntitlements(gpa, path); - } - try self.writeCodeSignaturePadding(&codesig); - break :blk codesig; } else null; - defer if (codesig) |*csig| csig.deinit(gpa); + _ = module_obj_path; - // Write load commands - var lc_buffer = std.ArrayList(u8).init(arena); - const lc_writer = lc_buffer.writer(); + // --verbose-link + if (comp.verbose_link) try self.dumpArgv(comp); - try self.writeSegmentHeaders(lc_writer); - try lc_writer.writeStruct(self.dyld_info_cmd); - try lc_writer.writeStruct(self.symtab_cmd); - try lc_writer.writeStruct(self.dysymtab_cmd); - try load_commands.writeDylinkerLC(lc_writer); + @panic("TODO"); +} - switch (output_mode) { - .Exe => blk: { - const seg_id = self.header_segment_cmd_index.?; - const seg = self.segments.items[seg_id]; - const global = self.getEntryPoint() orelse break :blk; - const sym = self.getSymbol(global); - - const addr: u64 = if (sym.undf()) - // In this case, the symbol has been resolved in one of dylibs and so we point - // to the stub as its vmaddr value. - self.getStubsEntryAddress(global).? - else - sym.n_value; - - try lc_writer.writeStruct(macho.entry_point_command{ - .entryoff = @as(u32, @intCast(addr - seg.vmaddr)), - .stacksize = self.base.stack_size, - }); - }, - .Lib => if (comp.config.link_mode == .Dynamic) { - try load_commands.writeDylibIdLC(self, lc_writer); - }, - else => {}, - } - - try load_commands.writeRpathLCs(self, lc_writer); - try lc_writer.writeStruct(macho.source_version_command{ - .version = 0, - }); - { - const platform = Platform.fromTarget(target); - const sdk_version: ?std.SemanticVersion = load_commands.inferSdkVersion(self); - if (platform.isBuildVersionCompatible()) { - try load_commands.writeBuildVersionLC(platform, sdk_version, lc_writer); - } else if (platform.isVersionMinCompatible()) { - try load_commands.writeVersionMinLC(platform, sdk_version, lc_writer); - } - } - - const uuid_cmd_offset = @sizeOf(macho.mach_header_64) + @as(u32, @intCast(lc_buffer.items.len)); - try lc_writer.writeStruct(self.uuid_cmd); - - try load_commands.writeLoadDylibLCs(self.dylibs.items, self.referenced_dylibs.keys(), lc_writer); - - if (codesig != null) { - try lc_writer.writeStruct(self.codesig_cmd); - } - - const ncmds = load_commands.calcNumOfLCs(lc_buffer.items); - try self.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64)); - try self.writeHeader(ncmds, @as(u32, @intCast(lc_buffer.items.len))); - try self.writeUuid(comp, uuid_cmd_offset, codesig != null); - - if (codesig) |*csig| { - try self.writeCodeSignature(comp, csig); // code signing always comes last - const emit = self.base.emit; - try invalidateKernelCache(emit.directory.handle, emit.sub_path); - } - - if (self.d_sym) |*d_sym| { - // Flush debug symbols bundle. - try d_sym.flushModule(self); - } +/// --verbose-link output +fn dumpArgv(self: *MachO, comp: *Compilation) !void { + _ = self; + _ = comp; + @panic("TODO dumpArgv"); } /// XNU starting with Big Sur running on arm64 is caching inodes of running binaries. @@ -726,1196 +379,12 @@ pub fn parsePositional( const tracy = trace(@src()); defer tracy.end(); - if (Object.isObject(file)) { - try self.parseObject(file, path, ctx); - } else { - try self.parseLibrary(file, path, .{ - .path = null, - .needed = false, - .weak = false, - }, must_link, false, null, dependent_libs, ctx); - } -} - -fn parseObject( - self: *MachO, - file: std.fs.File, - path: []const u8, - ctx: *ParseErrorCtx, -) ParseError!void { - const tracy = trace(@src()); - defer tracy.end(); - - const gpa = self.base.comp.gpa; - const target = self.base.comp.root_mod.resolved_target.result; - const mtime: u64 = mtime: { - const stat = file.stat() catch break :mtime 0; - break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000))); - }; - const file_stat = try file.stat(); - const file_size = math.cast(usize, file_stat.size) orelse return error.Overflow; - const contents = try file.readToEndAllocOptions(gpa, file_size, file_size, @alignOf(u64), null); - - var object = Object{ - .name = try gpa.dupe(u8, path), - .mtime = mtime, - .contents = contents, - }; - errdefer object.deinit(gpa); - try object.parse(gpa); - - const detected_cpu_arch: std.Target.Cpu.Arch = switch (object.header.cputype) { - macho.CPU_TYPE_ARM64 => .aarch64, - macho.CPU_TYPE_X86_64 => .x86_64, - else => unreachable, - }; - const detected_platform = object.getPlatform(); - const this_cpu_arch = target.cpu.arch; - const this_platform = Platform.fromTarget(target); - - if (this_cpu_arch != detected_cpu_arch or - (detected_platform != null and !detected_platform.?.eqlTarget(this_platform))) - { - const platform = detected_platform orelse this_platform; - try ctx.detected_targets.append(try platform.allocPrintTarget(ctx.arena(), detected_cpu_arch)); - return error.InvalidTarget; - } - - try self.objects.append(gpa, object); -} - -pub fn parseLibrary( - self: *MachO, - file: std.fs.File, - path: []const u8, - lib: link.SystemLib, - must_link: bool, - is_dependent: bool, - reexport_info: ?DylibReExportInfo, - dependent_libs: anytype, - ctx: *ParseErrorCtx, -) ParseError!void { - const tracy = trace(@src()); - defer tracy.end(); - - const target = self.base.comp.root_mod.resolved_target.result; - - if (fat.isFatLibrary(file)) { - const offset = try self.parseFatLibrary(file, target.cpu.arch, ctx); - try file.seekTo(offset); - - if (Archive.isArchive(file, offset)) { - try self.parseArchive(path, offset, must_link, ctx); - } else if (Dylib.isDylib(file, offset)) { - try self.parseDylib(file, path, offset, dependent_libs, .{ - .needed = lib.needed, - .weak = lib.weak, - .dependent = is_dependent, - .reexport_info = reexport_info, - }, ctx); - } else return error.UnknownFileType; - } else if (Archive.isArchive(file, 0)) { - try self.parseArchive(path, 0, must_link, ctx); - } else if (Dylib.isDylib(file, 0)) { - try self.parseDylib(file, path, 0, dependent_libs, .{ - .needed = lib.needed, - .weak = lib.weak, - .dependent = is_dependent, - .reexport_info = reexport_info, - }, ctx); - } else { - self.parseLibStub(file, path, dependent_libs, .{ - .needed = lib.needed, - .weak = lib.weak, - .dependent = is_dependent, - .reexport_info = reexport_info, - }, ctx) catch |err| switch (err) { - error.NotLibStub, error.UnexpectedToken => return error.UnknownFileType, - else => |e| return e, - }; - } -} - -pub fn parseFatLibrary( - self: *MachO, - file: std.fs.File, - cpu_arch: std.Target.Cpu.Arch, - ctx: *ParseErrorCtx, -) ParseError!u64 { - const gpa = self.base.comp.gpa; - - const fat_archs = try fat.parseArchs(gpa, file); - defer gpa.free(fat_archs); - - const offset = for (fat_archs) |arch| { - if (arch.tag == cpu_arch) break arch.offset; - } else { - try ctx.detected_targets.ensureUnusedCapacity(fat_archs.len); - for (fat_archs) |arch| { - ctx.detected_targets.appendAssumeCapacity(try ctx.arena().dupe(u8, @tagName(arch.tag))); - } - return error.InvalidTargetFatLibrary; - }; - return offset; -} - -fn parseArchive( - self: *MachO, - path: []const u8, - fat_offset: u64, - must_link: bool, - ctx: *ParseErrorCtx, -) ParseError!void { - const gpa = self.base.comp.gpa; - const target = self.base.comp.root_mod.resolved_target.result; - - // We take ownership of the file so that we can store it for the duration of symbol resolution. - // TODO we shouldn't need to do that and could pre-parse the archive like we do for zld/ELF? - const file = try std.fs.cwd().openFile(path, .{}); - try file.seekTo(fat_offset); - - var archive = Archive{ - .file = file, - .fat_offset = fat_offset, - .name = try gpa.dupe(u8, path), - }; - errdefer archive.deinit(gpa); - - try archive.parse(gpa, file.reader()); - - // Verify arch and platform - if (archive.toc.values().len > 0) { - const offsets = archive.toc.values()[0].items; - assert(offsets.len > 0); - const off = offsets[0]; - var object = try archive.parseObject(gpa, off); // TODO we are doing all this work to pull the header only! - defer object.deinit(gpa); - - const detected_cpu_arch: std.Target.Cpu.Arch = switch (object.header.cputype) { - macho.CPU_TYPE_ARM64 => .aarch64, - macho.CPU_TYPE_X86_64 => .x86_64, - else => unreachable, - }; - const detected_platform = object.getPlatform(); - const this_cpu_arch = target.cpu.arch; - const this_platform = Platform.fromTarget(target); - - if (this_cpu_arch != detected_cpu_arch or - (detected_platform != null and !detected_platform.?.eqlTarget(this_platform))) - { - const platform = detected_platform orelse this_platform; - try ctx.detected_targets.append(try platform.allocPrintTarget(gpa, detected_cpu_arch)); - return error.InvalidTarget; - } - } - - if (must_link) { - // Get all offsets from the ToC - var offsets = std.AutoArrayHashMap(u32, void).init(gpa); - defer offsets.deinit(); - for (archive.toc.values()) |offs| { - for (offs.items) |off| { - _ = try offsets.getOrPut(off); - } - } - for (offsets.keys()) |off| { - const object = try archive.parseObject(gpa, off); - try self.objects.append(gpa, object); - } - } else { - try self.archives.append(gpa, archive); - } -} - -pub const DylibReExportInfo = struct { - id: Dylib.Id, - parent: u16, -}; - -const DylibOpts = struct { - reexport_info: ?DylibReExportInfo = null, - dependent: bool = false, - needed: bool = false, - weak: bool = false, -}; - -fn parseDylib( - self: *MachO, - file: std.fs.File, - path: []const u8, - offset: u64, - dependent_libs: anytype, - dylib_options: DylibOpts, - ctx: *ParseErrorCtx, -) ParseError!void { - const gpa = self.base.comp.gpa; - const target = self.base.comp.root_mod.resolved_target.result; - const file_stat = try file.stat(); - const file_size = math.cast(usize, file_stat.size - offset) orelse return error.Overflow; - - const contents = try file.readToEndAllocOptions(gpa, file_size, file_size, @alignOf(u64), null); - defer gpa.free(contents); - - var dylib = Dylib{ .path = try gpa.dupe(u8, path), .weak = dylib_options.weak }; - errdefer dylib.deinit(gpa); - - try dylib.parseFromBinary( - gpa, - @intCast(self.dylibs.items.len), // TODO defer it till later - dependent_libs, - path, - contents, - ); - - const detected_cpu_arch: std.Target.Cpu.Arch = switch (dylib.header.?.cputype) { - macho.CPU_TYPE_ARM64 => .aarch64, - macho.CPU_TYPE_X86_64 => .x86_64, - else => unreachable, - }; - const detected_platform = dylib.getPlatform(contents); - const this_cpu_arch = target.cpu.arch; - const this_platform = Platform.fromTarget(target); - - if (this_cpu_arch != detected_cpu_arch or - (detected_platform != null and !detected_platform.?.eqlTarget(this_platform))) - { - const platform = detected_platform orelse this_platform; - try ctx.detected_targets.append(try platform.allocPrintTarget(ctx.arena(), detected_cpu_arch)); - return error.InvalidTarget; - } - - try self.addDylib(dylib, dylib_options, ctx); -} - -fn parseLibStub( - self: *MachO, - file: std.fs.File, - path: []const u8, - dependent_libs: anytype, - dylib_options: DylibOpts, - ctx: *ParseErrorCtx, -) ParseError!void { - const gpa = self.base.comp.gpa; - const target = self.base.comp.root_mod.resolved_target.result; - - var lib_stub = try LibStub.loadFromFile(gpa, file); - defer lib_stub.deinit(); - - if (lib_stub.inner.len == 0) return error.NotLibStub; - - // Verify target - { - var matcher = try Dylib.TargetMatcher.init(gpa, target); - defer matcher.deinit(); - - const first_tbd = lib_stub.inner[0]; - const targets = try first_tbd.targets(gpa); - defer { - for (targets) |t| gpa.free(t); - gpa.free(targets); - } - if (!matcher.matchesTarget(targets)) { - try ctx.detected_targets.ensureUnusedCapacity(targets.len); - for (targets) |t| { - ctx.detected_targets.appendAssumeCapacity(try ctx.arena().dupe(u8, t)); - } - return error.InvalidTarget; - } - } - - var dylib = Dylib{ .path = try gpa.dupe(u8, path), .weak = dylib_options.weak }; - errdefer dylib.deinit(gpa); - - try dylib.parseFromStub( - gpa, - target, - lib_stub, - @intCast(self.dylibs.items.len), // TODO defer it till later - dependent_libs, - path, - ); - - try self.addDylib(dylib, dylib_options, ctx); -} - -fn addDylib(self: *MachO, dylib: Dylib, dylib_options: DylibOpts, ctx: *ParseErrorCtx) ParseError!void { - if (dylib_options.reexport_info) |reexport_info| { - if (dylib.id.?.current_version < reexport_info.id.compatibility_version) { - ctx.detected_dylib_id = .{ - .parent = reexport_info.parent, - .required_version = reexport_info.id.compatibility_version, - .found_version = dylib.id.?.current_version, - }; - return error.IncompatibleDylibVersion; - } - } - - const gpa = self.base.comp.gpa; - const gop = try self.dylibs_map.getOrPut(gpa, dylib.id.?.name); - if (gop.found_existing) return error.DylibAlreadyExists; - - gop.value_ptr.* = @as(u16, @intCast(self.dylibs.items.len)); - try self.dylibs.append(gpa, dylib); - - const should_link_dylib_even_if_unreachable = blk: { - if (self.dead_strip_dylibs and !dylib_options.needed) break :blk false; - break :blk !(dylib_options.dependent or self.referenced_dylibs.contains(gop.value_ptr.*)); - }; - - if (should_link_dylib_even_if_unreachable) { - try self.referenced_dylibs.putNoClobber(gpa, gop.value_ptr.*, {}); - } -} - -pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype) !void { - const tracy = trace(@src()); - defer tracy.end(); - - // At this point, we can now parse dependents of dylibs preserving the inclusion order of: - // 1) anything on the linker line is parsed first - // 2) afterwards, we parse dependents of the included dylibs - // TODO this should not be performed if the user specifies `-flat_namespace` flag. - // See ld64 manpages. - const comp = self.base.comp; - const gpa = comp.gpa; - - while (dependent_libs.readItem()) |dep_id| { - defer dep_id.id.deinit(gpa); - - if (self.dylibs_map.contains(dep_id.id.name)) continue; - - const parent = &self.dylibs.items[dep_id.parent]; - const weak = parent.weak; - const dirname = fs.path.dirname(dep_id.id.name) orelse ""; - const stem = fs.path.stem(dep_id.id.name); - - var arena_allocator = std.heap.ArenaAllocator.init(gpa); - defer arena_allocator.deinit(); - const arena = arena_allocator.allocator(); - - var test_path = std.ArrayList(u8).init(arena); - var checked_paths = std.ArrayList([]const u8).init(arena); - - success: { - if (comp.sysroot) |root| { - const dir = try fs.path.join(arena, &[_][]const u8{ root, dirname }); - if (try accessLibPath(gpa, &test_path, &checked_paths, dir, stem)) break :success; - } - - if (try accessLibPath(gpa, &test_path, &checked_paths, dirname, stem)) break :success; - - try self.reportMissingLibraryError( - checked_paths.items, - "missing dynamic library dependency: '{s}'", - .{dep_id.id.name}, - ); - continue; - } - - const full_path = test_path.items; - const file = try std.fs.cwd().openFile(full_path, .{}); - defer file.close(); - - log.debug("parsing dependency {s} at fully resolved path {s}", .{ dep_id.id.name, full_path }); - - var parse_ctx = ParseErrorCtx.init(gpa); - defer parse_ctx.deinit(); - - self.parseLibrary(file, full_path, .{ - .path = null, - .needed = false, - .weak = weak, - }, false, true, dep_id, dependent_libs, &parse_ctx) catch |err| - try self.handleAndReportParseError(full_path, err, &parse_ctx); - - // TODO I think that it would be nice to rewrite this error to include metadata for failed dependency - // in addition to parsing error - } -} - -pub fn writeAtom(self: *MachO, atom_index: Atom.Index, code: []u8) !void { - const atom = self.getAtom(atom_index); - const sym = atom.getSymbol(self); - const section = self.sections.get(sym.n_sect - 1); - const file_offset = section.header.offset + sym.n_value - section.header.addr; - log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset }); - - // Gather relocs which can be resolved. - const gpa = self.base.comp.gpa; - var relocs = std.ArrayList(*Relocation).init(gpa); - defer relocs.deinit(); - - if (self.relocs.getPtr(atom_index)) |rels| { - try relocs.ensureTotalCapacityPrecise(rels.items.len); - for (rels.items) |*reloc| { - if (reloc.isResolvable(self) and reloc.dirty) { - relocs.appendAssumeCapacity(reloc); - } - } - } - - Atom.resolveRelocations(self, atom_index, relocs.items, code); - - if (is_hot_update_compatible) { - if (self.hot_state.mach_task) |task| { - self.writeToMemory(task, section.segment_index, sym.n_value, code) catch |err| { - log.warn("cannot hot swap: writing to memory failed: {s}", .{@errorName(err)}); - }; - } - } - - try self.base.file.?.pwriteAll(code, file_offset); - - // Now we can mark the relocs as resolved. - while (relocs.popOrNull()) |reloc| { - reloc.dirty = false; - } -} - -fn writeToMemory(self: *MachO, task: std.os.darwin.MachTask, segment_index: u8, addr: u64, code: []const u8) !void { - const segment = self.segments.items[segment_index]; - const target = self.base.comp.root_mod.resolved_target.result; - const cpu_arch = target.cpu.arch; - const nwritten = if (!segment.isWriteable()) - try task.writeMemProtected(addr, code, cpu_arch) - else - try task.writeMem(addr, code, cpu_arch); - if (nwritten != code.len) return error.InputOutput; -} - -fn writeOffsetTableEntry(self: *MachO, index: usize) !void { - const sect_id = self.got_section_index.?; - - if (self.got_table_count_dirty) { - const needed_size = self.got_table.entries.items.len * @sizeOf(u64); - try self.growSection(sect_id, needed_size); - self.got_table_count_dirty = false; - } - - const header = &self.sections.items(.header)[sect_id]; - const segment_index = self.sections.items(.segment_index)[sect_id]; - const entry = self.got_table.entries.items[index]; - const entry_value = self.getSymbol(entry).n_value; - const entry_offset = index * @sizeOf(u64); - const file_offset = header.offset + entry_offset; - const vmaddr = header.addr + entry_offset; - - log.debug("writing GOT entry {d}: @{x} => {x}", .{ index, vmaddr, entry_value }); - - var buf: [@sizeOf(u64)]u8 = undefined; - mem.writeInt(u64, &buf, entry_value, .little); - try self.base.file.?.pwriteAll(&buf, file_offset); - - if (is_hot_update_compatible) { - if (self.hot_state.mach_task) |task| { - self.writeToMemory(task, segment_index, vmaddr, &buf) catch |err| { - log.warn("cannot hot swap: writing to memory failed: {s}", .{@errorName(err)}); - }; - } - } -} - -fn writeStubHelperPreamble(self: *MachO) !void { - if (self.stub_helper_preamble_allocated) return; - - const gpa = self.base.comp.gpa; - const target = self.base.comp.root_mod.resolved_target.result; - const cpu_arch = target.cpu.arch; - const size = stubs.stubHelperPreambleSize(cpu_arch); - - var buf = try std.ArrayList(u8).initCapacity(gpa, size); - defer buf.deinit(); - - const dyld_private_addr = self.getAtom(self.dyld_private_atom_index.?).getSymbol(self).n_value; - const dyld_stub_binder_got_addr = blk: { - const index = self.got_table.lookup.get(self.getGlobalByIndex(self.dyld_stub_binder_index.?)).?; - const header = self.sections.items(.header)[self.got_section_index.?]; - break :blk header.addr + @sizeOf(u64) * index; - }; - const header = self.sections.items(.header)[self.stub_helper_section_index.?]; - - try stubs.writeStubHelperPreambleCode(.{ - .cpu_arch = cpu_arch, - .source_addr = header.addr, - .dyld_private_addr = dyld_private_addr, - .dyld_stub_binder_got_addr = dyld_stub_binder_got_addr, - }, buf.writer()); - try self.base.file.?.pwriteAll(buf.items, header.offset); - - self.stub_helper_preamble_allocated = true; -} - -fn writeStubTableEntry(self: *MachO, index: usize) !void { - const target = self.base.comp.root_mod.resolved_target.result; - const stubs_sect_id = self.stubs_section_index.?; - const stub_helper_sect_id = self.stub_helper_section_index.?; - const laptr_sect_id = self.la_symbol_ptr_section_index.?; - - const cpu_arch = target.cpu.arch; - const stub_entry_size = stubs.stubSize(cpu_arch); - const stub_helper_entry_size = stubs.stubHelperSize(cpu_arch); - const stub_helper_preamble_size = stubs.stubHelperPreambleSize(cpu_arch); - - if (self.stub_table_count_dirty) { - // We grow all 3 sections one by one. - { - const needed_size = stub_entry_size * self.stub_table.entries.items.len; - try self.growSection(stubs_sect_id, needed_size); - } - { - const needed_size = stub_helper_preamble_size + stub_helper_entry_size * self.stub_table.entries.items.len; - try self.growSection(stub_helper_sect_id, needed_size); - } - { - const needed_size = @sizeOf(u64) * self.stub_table.entries.items.len; - try self.growSection(laptr_sect_id, needed_size); - } - self.stub_table_count_dirty = false; - } - - const gpa = self.base.comp.gpa; - - const stubs_header = self.sections.items(.header)[stubs_sect_id]; - const stub_helper_header = self.sections.items(.header)[stub_helper_sect_id]; - const laptr_header = self.sections.items(.header)[laptr_sect_id]; - - const entry = self.stub_table.entries.items[index]; - const stub_addr: u64 = stubs_header.addr + stub_entry_size * index; - const stub_helper_addr: u64 = stub_helper_header.addr + stub_helper_preamble_size + stub_helper_entry_size * index; - const laptr_addr: u64 = laptr_header.addr + @sizeOf(u64) * index; - - log.debug("writing stub entry {d}: @{x} => '{s}'", .{ index, stub_addr, self.getSymbolName(entry) }); - - { - var buf = try std.ArrayList(u8).initCapacity(gpa, stub_entry_size); - defer buf.deinit(); - try stubs.writeStubCode(.{ - .cpu_arch = cpu_arch, - .source_addr = stub_addr, - .target_addr = laptr_addr, - }, buf.writer()); - const off = stubs_header.offset + stub_entry_size * index; - try self.base.file.?.pwriteAll(buf.items, off); - } - - { - var buf = try std.ArrayList(u8).initCapacity(gpa, stub_helper_entry_size); - defer buf.deinit(); - try stubs.writeStubHelperCode(.{ - .cpu_arch = cpu_arch, - .source_addr = stub_helper_addr, - .target_addr = stub_helper_header.addr, - }, buf.writer()); - const off = stub_helper_header.offset + stub_helper_preamble_size + stub_helper_entry_size * index; - try self.base.file.?.pwriteAll(buf.items, off); - } - - { - var buf: [@sizeOf(u64)]u8 = undefined; - mem.writeInt(u64, &buf, stub_helper_addr, .little); - const off = laptr_header.offset + @sizeOf(u64) * index; - try self.base.file.?.pwriteAll(&buf, off); - } - - // TODO: generating new stub entry will require pulling the address of the symbol from the - // target dylib when updating directly in memory. - if (is_hot_update_compatible) { - if (self.hot_state.mach_task) |_| { - @panic("TODO: update a stub entry in memory"); - } - } -} - -fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void { - log.debug("marking relocs dirty by target: {}", .{target}); - // TODO: reverse-lookup might come in handy here - for (self.relocs.values()) |*relocs| { - for (relocs.items) |*reloc| { - if (!reloc.target.eql(target)) continue; - reloc.dirty = true; - } - } -} - -fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void { - log.debug("marking relocs dirty by address: {x}", .{addr}); - - const got_moved = blk: { - const sect_id = self.got_section_index orelse break :blk false; - break :blk self.sections.items(.header)[sect_id].addr > addr; - }; - const stubs_moved = blk: { - const sect_id = self.stubs_section_index orelse break :blk false; - break :blk self.sections.items(.header)[sect_id].addr > addr; - }; - - for (self.relocs.values()) |*relocs| { - for (relocs.items) |*reloc| { - if (reloc.isGotIndirection()) { - reloc.dirty = reloc.dirty or got_moved; - } else if (reloc.isStubTrampoline(self)) { - reloc.dirty = reloc.dirty or stubs_moved; - } else { - const target_addr = reloc.getTargetBaseAddress(self) orelse continue; - if (target_addr > addr) reloc.dirty = true; - } - } - } - - // TODO: dirty only really affected GOT cells - for (self.got_table.entries.items) |entry| { - const target_addr = self.getSymbol(entry).n_value; - if (target_addr > addr) { - self.got_table_contents_dirty = true; - break; - } - } - - { - const stubs_addr = self.getSegment(self.stubs_section_index.?).vmaddr; - const stub_helper_addr = self.getSegment(self.stub_helper_section_index.?).vmaddr; - const laptr_addr = self.getSegment(self.la_symbol_ptr_section_index.?).vmaddr; - if (stubs_addr > addr or stub_helper_addr > addr or laptr_addr > addr) - self.stub_table_contents_dirty = true; - } -} - -pub fn allocateSpecialSymbols(self: *MachO) !void { - for (&[_][]const u8{ - "___dso_handle", - "__mh_execute_header", - }) |name| { - const global = self.getGlobal(name) orelse continue; - if (global.getFile() != null) continue; - const sym = self.getSymbolPtr(global); - const seg = self.getSegment(self.text_section_index.?); - sym.n_sect = self.text_section_index.? + 1; - sym.n_value = seg.vmaddr; - - log.debug("allocating {s}(@0x{x},sect({d})) at the start of {s}", .{ - name, - sym.n_value, - sym.n_sect, - seg.segName(), - }); - } - - for (self.globals.items) |global| { - const sym = self.getSymbolPtr(global); - if (sym.n_desc != N_BOUNDARY) continue; - if (self.getSectionBoundarySymbol(global)) |bsym| { - const sect_id = self.getSectionByName(bsym.segname, bsym.sectname) orelse { - try self.reportUnresolvedBoundarySymbol(self.getSymbolName(global), "section not found: {s},{s}", .{ - bsym.segname, bsym.sectname, - }); - continue; - }; - const sect = self.sections.items(.header)[sect_id]; - sym.n_sect = sect_id + 1; - sym.n_value = switch (bsym.kind) { - .start => sect.addr, - .stop => sect.addr + sect.size, - }; - - log.debug("allocating {s} at @0x{x} sect({d})", .{ - self.getSymbolName(global), - sym.n_value, - sym.n_sect, - }); - - continue; - } - if (self.getSegmentBoundarySymbol(global)) |bsym| { - const seg_id = self.getSegmentByName(bsym.segname) orelse { - try self.reportUnresolvedBoundarySymbol(self.getSymbolName(global), "segment not found: {s}", .{ - bsym.segname, - }); - - continue; - }; - const seg = self.segments.items[seg_id]; - sym.n_value = switch (bsym.kind) { - .start => seg.vmaddr, - .stop => seg.vmaddr + seg.vmsize, - }; - - log.debug("allocating {s} at @0x{x} ", .{ self.getSymbolName(global), sym.n_value }); - - continue; - } - } -} - -const CreateAtomOpts = struct { - size: u64 = 0, - alignment: Alignment = .@"1", -}; - -pub fn createAtom(self: *MachO, sym_index: u32, opts: CreateAtomOpts) !Atom.Index { - const gpa = self.base.comp.gpa; - const index = @as(Atom.Index, @intCast(self.atoms.items.len)); - const atom = try self.atoms.addOne(gpa); - atom.* = .{}; - atom.sym_index = sym_index; - atom.size = opts.size; - atom.alignment = opts.alignment; - log.debug("creating ATOM(%{d}) at index {d}", .{ sym_index, index }); - return index; -} - -pub fn createTentativeDefAtoms(self: *MachO) !void { - const gpa = self.base.comp.gpa; - - for (self.globals.items) |global| { - const sym = self.getSymbolPtr(global); - if (!sym.tentative()) continue; - if (sym.n_desc == N_DEAD) continue; - if (sym.n_desc == N_BOUNDARY) continue; - - log.debug("creating tentative definition for ATOM(%{d}, '{s}') in object({?})", .{ - global.sym_index, self.getSymbolName(global), global.file, - }); - - // Convert any tentative definition into a regular symbol and allocate - // text blocks for each tentative definition. - const size = sym.n_value; - const alignment = (sym.n_desc >> 8) & 0x0f; - - if (self.bss_section_index == null) { - self.bss_section_index = try self.initSection("__DATA", "__bss", .{ - .flags = macho.S_ZEROFILL, - }); - } - - sym.* = .{ - .n_strx = sym.n_strx, - .n_type = macho.N_SECT | macho.N_EXT, - .n_sect = self.bss_section_index.? + 1, - .n_desc = 0, - .n_value = 0, - }; - - const atom_index = try self.createAtom(global.sym_index, .{ - .size = size, - .alignment = @enumFromInt(alignment), - }); - const atom = self.getAtomPtr(atom_index); - atom.file = global.file; - - self.addAtomToSection(atom_index); - - assert(global.getFile() != null); - const object = &self.objects.items[global.getFile().?]; - try object.atoms.append(gpa, atom_index); - object.atom_by_index_table[global.sym_index] = atom_index; - } -} - -pub fn createDyldPrivateAtom(self: *MachO) !void { - if (self.dyld_private_atom_index != null) return; - - const sym_index = try self.allocateSymbol(); - const atom_index = try self.createAtom(sym_index, .{ - .size = @sizeOf(u64), - .alignment = .@"8", - }); - const gpa = self.base.comp.gpa; - try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index); - - if (self.data_section_index == null) { - self.data_section_index = try self.initSection("__DATA", "__data", .{}); - } - - const atom = self.getAtom(atom_index); - const sym = atom.getSymbolPtr(self); - sym.n_type = macho.N_SECT; - sym.n_sect = self.data_section_index.? + 1; - self.dyld_private_atom_index = atom_index; - - switch (self.mode) { - .zld => self.addAtomToSection(atom_index), - .incremental => { - sym.n_value = try self.allocateAtom(atom_index, atom.size, .@"8"); - log.debug("allocated dyld_private atom at 0x{x}", .{sym.n_value}); - var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64); - try self.writeAtom(atom_index, &buffer); - }, - } -} - -fn createThreadLocalDescriptorAtom(self: *MachO, sym_name: []const u8, target: SymbolWithLoc) !Atom.Index { - const gpa = self.base.comp.gpa; - const size = 3 * @sizeOf(u64); - const required_alignment: Alignment = .@"1"; - const sym_index = try self.allocateSymbol(); - const atom_index = try self.createAtom(sym_index, .{}); - try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index); - self.getAtomPtr(atom_index).size = size; - - const sym = self.getAtom(atom_index).getSymbolPtr(self); - sym.n_type = macho.N_SECT; - sym.n_sect = self.thread_vars_section_index.? + 1; - sym.n_strx = try self.strtab.insert(gpa, sym_name); - sym.n_value = try self.allocateAtom(atom_index, size, required_alignment); - - log.debug("allocated threadlocal descriptor atom '{s}' at 0x{x}", .{ sym_name, sym.n_value }); - - try Atom.addRelocation(self, atom_index, .{ - .type = .tlv_initializer, - .target = target, - .offset = 0x10, - .addend = 0, - .pcrel = false, - .length = 3, - }); - - var code: [size]u8 = undefined; - @memset(&code, 0); - try self.writeAtom(atom_index, &code); - - return atom_index; -} - -pub fn createMhExecuteHeaderSymbol(self: *MachO) !void { - const output_mode = self.base.comp.config.output_mode; - if (output_mode != .Exe) return; - - const gpa = self.base.comp.gpa; - const sym_index = try self.allocateSymbol(); - const sym_loc = SymbolWithLoc{ .sym_index = sym_index }; - const sym = self.getSymbolPtr(sym_loc); - sym.* = .{ - .n_strx = try self.strtab.insert(gpa, "__mh_execute_header"), - .n_type = macho.N_SECT | macho.N_EXT, - .n_sect = 0, - .n_desc = macho.REFERENCED_DYNAMICALLY, - .n_value = 0, - }; - - const gop = try self.getOrPutGlobalPtr("__mh_execute_header"); - if (gop.found_existing) { - const global = gop.value_ptr.*; - if (global.getFile()) |file| { - const global_object = &self.objects.items[file]; - global_object.globals_lookup[global.sym_index] = self.getGlobalIndex("__mh_execute_header").?; - } - } - gop.value_ptr.* = sym_loc; -} - -pub fn createDsoHandleSymbol(self: *MachO) !void { - const global = self.getGlobalPtr("___dso_handle") orelse return; - if (!self.getSymbol(global.*).undf()) return; - - const gpa = self.base.comp.gpa; - const sym_index = try self.allocateSymbol(); - const sym_loc = SymbolWithLoc{ .sym_index = sym_index }; - const sym = self.getSymbolPtr(sym_loc); - sym.* = .{ - .n_strx = try self.strtab.insert(gpa, "___dso_handle"), - .n_type = macho.N_SECT | macho.N_EXT, - .n_sect = 0, - .n_desc = macho.N_WEAK_DEF, - .n_value = 0, - }; - const global_index = self.getGlobalIndex("___dso_handle").?; - if (global.getFile()) |file| { - const global_object = &self.objects.items[file]; - global_object.globals_lookup[global.sym_index] = global_index; - } - global.* = sym_loc; - _ = self.unresolved.swapRemove(self.getGlobalIndex("___dso_handle").?); -} - -pub fn resolveSymbols(self: *MachO) !void { - const comp = self.base.comp; - const output_mode = comp.config.output_mode; - // We add the specified entrypoint as the first unresolved symbols so that - // we search for it in libraries should there be no object files specified - // on the linker line. - if (output_mode == .Exe) { - if (self.entry_name) |entry_name| { - _ = try self.addUndefined(entry_name, .{}); - } - } - - // Force resolution of any symbols requested by the user. - for (comp.force_undefined_symbols.keys()) |sym_name| { - _ = try self.addUndefined(sym_name, .{}); - } - - for (self.objects.items, 0..) |_, object_id| { - try self.resolveSymbolsInObject(@as(u32, @intCast(object_id))); - } - - try self.resolveSymbolsInArchives(); - - // Finally, force resolution of dyld_stub_binder if there are imports - // requested. - if (self.unresolved.count() > 0 and self.dyld_stub_binder_index == null) { - self.dyld_stub_binder_index = try self.addUndefined("dyld_stub_binder", .{ .add_got = true }); - } - if (comp.config.any_non_single_threaded and self.mode == .incremental) { - _ = try self.addUndefined("__tlv_bootstrap", .{}); - } - - try self.resolveSymbolsInDylibs(); - - try self.createMhExecuteHeaderSymbol(); - try self.createDsoHandleSymbol(); - try self.resolveSymbolsAtLoading(); - - // Final stop, check if unresolved contain any of the special magic boundary symbols - // * section$start$ - // * section$stop$ - // * segment$start$ - // * segment$stop$ - try self.resolveBoundarySymbols(); -} - -fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void { - const gpa = self.base.comp.gpa; - const sym = self.getSymbol(current); - const sym_name = self.getSymbolName(current); - - const gop = try self.getOrPutGlobalPtr(sym_name); - if (!gop.found_existing) { - gop.value_ptr.* = current; - if (sym.undf() and !sym.tentative()) { - try self.unresolved.putNoClobber(gpa, self.getGlobalIndex(sym_name).?, {}); - } - return; - } - const global_index = self.getGlobalIndex(sym_name).?; - const global = gop.value_ptr.*; - const global_sym = self.getSymbol(global); - - // Cases to consider: sym vs global_sym - // 1. strong(sym) and strong(global_sym) => error - // 2. strong(sym) and weak(global_sym) => sym - // 3. strong(sym) and tentative(global_sym) => sym - // 4. strong(sym) and undf(global_sym) => sym - // 5. weak(sym) and strong(global_sym) => global_sym - // 6. weak(sym) and tentative(global_sym) => sym - // 7. weak(sym) and undf(global_sym) => sym - // 8. tentative(sym) and strong(global_sym) => global_sym - // 9. tentative(sym) and weak(global_sym) => global_sym - // 10. tentative(sym) and tentative(global_sym) => pick larger - // 11. tentative(sym) and undf(global_sym) => sym - // 12. undf(sym) and * => global_sym - // - // Reduces to: - // 1. strong(sym) and strong(global_sym) => error - // 2. * and strong(global_sym) => global_sym - // 3. weak(sym) and weak(global_sym) => global_sym - // 4. tentative(sym) and tentative(global_sym) => pick larger - // 5. undf(sym) and * => global_sym - // 6. else => sym - - const sym_is_strong = sym.sect() and !(sym.weakDef() or sym.pext()); - const global_is_strong = global_sym.sect() and !(global_sym.weakDef() or global_sym.pext()); - const sym_is_weak = sym.sect() and (sym.weakDef() or sym.pext()); - const global_is_weak = global_sym.sect() and (global_sym.weakDef() or global_sym.pext()); - - if (sym_is_strong and global_is_strong) { - // TODO redo this logic with corresponding logic in updateExports to avoid this - // ugly check. - if (self.mode == .zld) { - try self.reportSymbolCollision(global, current); - } - return error.MultipleSymbolDefinitions; - } - - if (current.getFile()) |file| { - const object = &self.objects.items[file]; - object.globals_lookup[current.sym_index] = global_index; - } - - if (global_is_strong) return; - if (sym_is_weak and global_is_weak) return; - if (sym.tentative() and global_sym.tentative()) { - if (global_sym.n_value >= sym.n_value) return; - } - if (sym.undf() and !sym.tentative()) return; - - if (global.getFile()) |file| { - const global_object = &self.objects.items[file]; - global_object.globals_lookup[global.sym_index] = global_index; - } - _ = self.unresolved.swapRemove(global_index); - - gop.value_ptr.* = current; -} - -fn resolveSymbolsInObject(self: *MachO, object_id: u32) !void { - const object = &self.objects.items[object_id]; - const in_symtab = object.in_symtab orelse return; - - log.debug("resolving symbols in '{s}'", .{object.name}); - - var sym_index: u32 = 0; - while (sym_index < in_symtab.len) : (sym_index += 1) { - const sym = &object.symtab[sym_index]; - const sym_name = object.getSymbolName(sym_index); - const sym_with_loc = SymbolWithLoc{ - .sym_index = sym_index, - .file = object_id + 1, - }; - - if (sym.stab() or sym.indr() or sym.abs()) { - try self.reportUnhandledSymbolType(sym_with_loc); - continue; - } - - if (sym.sect() and !sym.ext()) { - log.debug("symbol '{s}' local to object {s}; skipping...", .{ - sym_name, - object.name, - }); - continue; - } - - self.resolveGlobalSymbol(.{ - .sym_index = sym_index, - .file = object_id + 1, - }) catch |err| switch (err) { - error.MultipleSymbolDefinitions => return error.FlushFailure, - else => |e| return e, - }; - } -} - -fn resolveSymbolsInArchives(self: *MachO) !void { - if (self.archives.items.len == 0) return; - - const gpa = self.base.comp.gpa; - var next_sym: usize = 0; - loop: while (next_sym < self.unresolved.count()) { - const global = self.globals.items[self.unresolved.keys()[next_sym]]; - const sym_name = self.getSymbolName(global); - - for (self.archives.items) |archive| { - // Check if the entry exists in a static archive. - const offsets = archive.toc.get(sym_name) orelse { - // No hit. - continue; - }; - assert(offsets.items.len > 0); - - const object_id = @as(u16, @intCast(self.objects.items.len)); - const object = try archive.parseObject(gpa, offsets.items[0]); - try self.objects.append(gpa, object); - try self.resolveSymbolsInObject(object_id); - - continue :loop; - } - - next_sym += 1; - } -} - -fn resolveSymbolsInDylibs(self: *MachO) !void { - if (self.dylibs.items.len == 0) return; - - const gpa = self.base.comp.gpa; - var next_sym: usize = 0; - loop: while (next_sym < self.unresolved.count()) { - const global_index = self.unresolved.keys()[next_sym]; - const global = self.globals.items[global_index]; - const sym = self.getSymbolPtr(global); - const sym_name = self.getSymbolName(global); - - for (self.dylibs.items, 0..) |dylib, id| { - if (!dylib.symbols.contains(sym_name)) continue; - - const dylib_id = @as(u16, @intCast(id)); - if (!self.referenced_dylibs.contains(dylib_id)) { - try self.referenced_dylibs.putNoClobber(gpa, dylib_id, {}); - } - - const ordinal = self.referenced_dylibs.getIndex(dylib_id) orelse unreachable; - sym.n_type |= macho.N_EXT; - sym.n_desc = @as(u16, @intCast(ordinal + 1)) * macho.N_SYMBOL_RESOLVER; - - if (dylib.weak) { - sym.n_desc |= macho.N_WEAK_REF; - } - - _ = self.unresolved.swapRemove(global_index); - - continue :loop; - } - - next_sym += 1; - } -} - -fn resolveSymbolsAtLoading(self: *MachO) !void { - const output_mode = self.base.comp.config.output_mode; - const is_lib = output_mode == .Lib; - const is_dyn_lib = self.base.comp.config.link_mode == .Dynamic and is_lib; - const allow_undef = is_dyn_lib and self.base.allow_shlib_undefined; - - var next_sym: usize = 0; - while (next_sym < self.unresolved.count()) { - const global_index = self.unresolved.keys()[next_sym]; - const global = self.globals.items[global_index]; - const sym = self.getSymbolPtr(global); - - if (sym.discarded()) { - sym.* = .{ - .n_strx = 0, - .n_type = macho.N_UNDF, - .n_sect = 0, - .n_desc = 0, - .n_value = 0, - }; - _ = self.unresolved.swapRemove(global_index); - continue; - } else if (allow_undef) { - const n_desc = @as( - u16, - @bitCast(macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP * @as(i16, @intCast(macho.N_SYMBOL_RESOLVER))), - ); - sym.n_type = macho.N_EXT; - sym.n_desc = n_desc; - _ = self.unresolved.swapRemove(global_index); - continue; - } - - next_sym += 1; - } -} - -fn resolveBoundarySymbols(self: *MachO) !void { - const gpa = self.base.comp.gpa; - var next_sym: usize = 0; - while (next_sym < self.unresolved.count()) { - const global_index = self.unresolved.keys()[next_sym]; - const global = &self.globals.items[global_index]; - - if (self.getSectionBoundarySymbol(global.*) != null or self.getSegmentBoundarySymbol(global.*) != null) { - const sym_index = try self.allocateSymbol(); - const sym_loc = SymbolWithLoc{ .sym_index = sym_index }; - const sym = self.getSymbolPtr(sym_loc); - sym.* = .{ - .n_strx = try self.strtab.insert(gpa, self.getSymbolName(global.*)), - .n_type = macho.N_SECT | macho.N_EXT, - .n_sect = 0, - .n_desc = N_BOUNDARY, - .n_value = 0, - }; - if (global.getFile()) |file| { - const global_object = &self.objects.items[file]; - global_object.globals_lookup[global.sym_index] = global_index; - } - global.* = sym_loc; - _ = self.unresolved.swapRemove(global_index); - continue; - } - - next_sym += 1; - } + _ = self; + _ = file; + _ = path; + _ = must_link; + _ = dependent_libs; + _ = ctx; } pub fn deinit(self: *MachO) void { @@ -1927,45 +396,7 @@ pub fn deinit(self: *MachO) void { d_sym.deinit(); } - self.got_table.deinit(gpa); - self.stub_table.deinit(gpa); - self.tlv_ptr_table.deinit(gpa); - self.thunk_table.deinit(gpa); - - for (self.thunks.items) |*thunk| { - thunk.deinit(gpa); - } - self.thunks.deinit(gpa); - self.strtab.deinit(gpa); - self.locals.deinit(gpa); - self.globals.deinit(gpa); - self.locals_free_list.deinit(gpa); - self.globals_free_list.deinit(gpa); - self.unresolved.deinit(gpa); - - { - var it = self.resolver.keyIterator(); - while (it.next()) |key_ptr| { - gpa.free(key_ptr.*); - } - self.resolver.deinit(gpa); - } - - for (self.objects.items) |*object| { - object.deinit(gpa); - } - self.objects.deinit(gpa); - for (self.archives.items) |*archive| { - archive.deinit(gpa); - } - self.archives.deinit(gpa); - for (self.dylibs.items) |*dylib| { - dylib.deinit(gpa); - } - self.dylibs.deinit(gpa); - self.dylibs_map.deinit(gpa); - self.referenced_dylibs.deinit(gpa); self.segments.deinit(gpa); @@ -1973,47 +404,6 @@ pub fn deinit(self: *MachO) void { list.deinit(gpa); } self.sections.deinit(gpa); - - self.atoms.deinit(gpa); - - for (self.decls.values()) |*m| { - m.exports.deinit(gpa); - } - self.decls.deinit(gpa); - - self.lazy_syms.deinit(gpa); - self.tlv_table.deinit(gpa); - - for (self.unnamed_const_atoms.values()) |*atoms| { - atoms.deinit(gpa); - } - self.unnamed_const_atoms.deinit(gpa); - - { - var it = self.anon_decls.iterator(); - while (it.next()) |entry| { - entry.value_ptr.exports.deinit(gpa); - } - self.anon_decls.deinit(gpa); - } - - self.atom_by_index_table.deinit(gpa); - - for (self.relocs.values()) |*relocs| { - relocs.deinit(gpa); - } - self.relocs.deinit(gpa); - self.actions.deinit(gpa); - - for (self.rebases.values()) |*rebases| { - rebases.deinit(gpa); - } - self.rebases.deinit(gpa); - - for (self.bindings.values()) |*bindings| { - bindings.deinit(gpa); - } - self.bindings.deinit(gpa); } fn freeAtom(self: *MachO, atom_index: Atom.Index) void { @@ -2100,116 +490,11 @@ fn shrinkAtom(self: *MachO, atom_index: Atom.Index, new_block_size: u64) void { } fn growAtom(self: *MachO, atom_index: Atom.Index, new_atom_size: u64, alignment: Alignment) !u64 { - const atom = self.getAtom(atom_index); - const sym = atom.getSymbol(self); - const align_ok = alignment.check(sym.n_value); - const need_realloc = !align_ok or new_atom_size > atom.capacity(self); - if (!need_realloc) return sym.n_value; - return self.allocateAtom(atom_index, new_atom_size, alignment); -} - -pub fn allocateSymbol(self: *MachO) !u32 { - const gpa = self.base.comp.gpa; - try self.locals.ensureUnusedCapacity(gpa, 1); - - const index = blk: { - if (self.locals_free_list.popOrNull()) |index| { - log.debug(" (reusing symbol index {d})", .{index}); - break :blk index; - } else { - log.debug(" (allocating symbol index {d})", .{self.locals.items.len}); - const index = @as(u32, @intCast(self.locals.items.len)); - _ = self.locals.addOneAssumeCapacity(); - break :blk index; - } - }; - - self.locals.items[index] = .{ - .n_strx = 0, - .n_type = 0, - .n_sect = 0, - .n_desc = 0, - .n_value = 0, - }; - - return index; -} - -fn allocateGlobal(self: *MachO) !u32 { - const gpa = self.base.comp.gpa; - try self.globals.ensureUnusedCapacity(gpa, 1); - - const index = blk: { - if (self.globals_free_list.popOrNull()) |index| { - log.debug(" (reusing global index {d})", .{index}); - break :blk index; - } else { - log.debug(" (allocating symbol index {d})", .{self.globals.items.len}); - const index = @as(u32, @intCast(self.globals.items.len)); - _ = self.globals.addOneAssumeCapacity(); - break :blk index; - } - }; - - self.globals.items[index] = .{ .sym_index = 0 }; - - return index; -} - -pub fn addGotEntry(self: *MachO, reloc_target: SymbolWithLoc) !void { - if (self.got_table.lookup.contains(reloc_target)) return; - const gpa = self.base.comp.gpa; - const got_index = try self.got_table.allocateEntry(gpa, reloc_target); - if (self.got_section_index == null) { - self.got_section_index = try self.initSection("__DATA_CONST", "__got", .{ - .flags = macho.S_NON_LAZY_SYMBOL_POINTERS, - }); - } - if (self.mode == .incremental) { - try self.writeOffsetTableEntry(got_index); - self.got_table_count_dirty = true; - self.markRelocsDirtyByTarget(reloc_target); - } -} - -pub fn addStubEntry(self: *MachO, reloc_target: SymbolWithLoc) !void { - if (self.stub_table.lookup.contains(reloc_target)) return; - const comp = self.base.comp; - const gpa = comp.gpa; - const cpu_arch = comp.root_mod.resolved_target.result.cpu.arch; - const stub_index = try self.stub_table.allocateEntry(gpa, reloc_target); - if (self.stubs_section_index == null) { - self.stubs_section_index = try self.initSection("__TEXT", "__stubs", .{ - .flags = macho.S_SYMBOL_STUBS | - macho.S_ATTR_PURE_INSTRUCTIONS | - macho.S_ATTR_SOME_INSTRUCTIONS, - .reserved2 = stubs.stubSize(cpu_arch), - }); - self.stub_helper_section_index = try self.initSection("__TEXT", "__stub_helper", .{ - .flags = macho.S_REGULAR | - macho.S_ATTR_PURE_INSTRUCTIONS | - macho.S_ATTR_SOME_INSTRUCTIONS, - }); - self.la_symbol_ptr_section_index = try self.initSection("__DATA", "__la_symbol_ptr", .{ - .flags = macho.S_LAZY_SYMBOL_POINTERS, - }); - } - if (self.mode == .incremental) { - try self.writeStubTableEntry(stub_index); - self.stub_table_count_dirty = true; - self.markRelocsDirtyByTarget(reloc_target); - } -} - -pub fn addTlvPtrEntry(self: *MachO, reloc_target: SymbolWithLoc) !void { - if (self.tlv_ptr_table.lookup.contains(reloc_target)) return; - const gpa = self.base.comp.gpa; - _ = try self.tlv_ptr_table.allocateEntry(gpa, reloc_target); - if (self.tlv_ptr_section_index == null) { - self.tlv_ptr_section_index = try self.initSection("__DATA", "__thread_ptrs", .{ - .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS, - }); - } + _ = self; + _ = atom_index; + _ = new_atom_size; + _ = alignment; + @panic("TODO growAtom"); } pub fn updateFunc(self: *MachO, mod: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) !void { @@ -2217,85 +502,16 @@ pub fn updateFunc(self: *MachO, mod: *Module, func_index: InternPool.Index, air: @panic("Attempted to compile for object format that was disabled by build configuration"); } if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(mod, func_index, air, liveness); - const tracy = trace(@src()); - defer tracy.end(); - const func = mod.funcInfo(func_index); - const decl_index = func.owner_decl; - const decl = mod.declPtr(decl_index); - - const atom_index = try self.getOrCreateAtomForDecl(decl_index); - self.freeUnnamedConsts(decl_index); - Atom.freeRelocations(self, atom_index); - - const gpa = self.base.comp.gpa; - var code_buffer = std.ArrayList(u8).init(gpa); - defer code_buffer.deinit(); - - var decl_state = if (self.d_sym) |*d_sym| - try d_sym.dwarf.initDeclState(mod, decl_index) - else - null; - defer if (decl_state) |*ds| ds.deinit(); - - const res = if (decl_state) |*ds| - try codegen.generateFunction(&self.base, decl.srcLoc(mod), func_index, air, liveness, &code_buffer, .{ - .dwarf = ds, - }) - else - try codegen.generateFunction(&self.base, decl.srcLoc(mod), func_index, air, liveness, &code_buffer, .none); - - const code = switch (res) { - .ok => code_buffer.items, - .fail => |em| { - decl.analysis = .codegen_failure; - try mod.failed_decls.put(mod.gpa, decl_index, em); - return; - }, - }; - - const addr = try self.updateDeclCode(decl_index, code); - - if (decl_state) |*ds| { - try self.d_sym.?.dwarf.commitDeclState( - mod, - decl_index, - addr, - self.getAtom(atom_index).size, - ds, - ); - } - - // Since we updated the vaddr and the size, each corresponding export symbol also - // needs to be updated. - try self.updateExports(mod, .{ .decl_index = decl_index }, mod.getDeclExports(decl_index)); + @panic("TODO updateFunc"); } pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: InternPool.DeclIndex) !u32 { - const gpa = self.base.comp.gpa; - const mod = self.base.comp.module.?; - const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index); - if (!gop.found_existing) { - gop.value_ptr.* = .{}; - } - const unnamed_consts = gop.value_ptr; - const decl = mod.declPtr(decl_index); - const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod)); - const index = unnamed_consts.items.len; - const name = try std.fmt.allocPrint(gpa, "___unnamed_{s}_{d}", .{ decl_name, index }); - defer gpa.free(name); - const atom_index = switch (try self.lowerConst(name, typed_value, typed_value.ty.abiAlignment(mod), self.data_const_section_index.?, decl.srcLoc(mod))) { - .ok => |atom_index| atom_index, - .fail => |em| { - decl.analysis = .codegen_failure; - try mod.failed_decls.put(mod.gpa, decl_index, em); - log.debug("{s}", .{em.msg}); - return error.CodegenFail; - }, - }; - try unnamed_consts.append(gpa, atom_index); - const atom = self.getAtomPtr(atom_index); - return atom.getSymbolIndex().?; + _ = self; + _ = typed_value; + _ = decl_index; + + @panic("TODO lowerUnnamedConst"); } const LowerConstResult = union(enum) { @@ -2311,44 +527,14 @@ fn lowerConst( sect_id: u8, src_loc: Module.SrcLoc, ) !LowerConstResult { - const gpa = self.base.comp.gpa; + _ = self; + _ = name; + _ = tv; + _ = required_alignment; + _ = sect_id; + _ = src_loc; - var code_buffer = std.ArrayList(u8).init(gpa); - defer code_buffer.deinit(); - - log.debug("allocating symbol indexes for {s}", .{name}); - - const sym_index = try self.allocateSymbol(); - const atom_index = try self.createAtom(sym_index, .{}); - try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index); - - const res = try codegen.generateSymbol(&self.base, src_loc, tv, &code_buffer, .none, .{ - .parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?, - }); - const code = switch (res) { - .ok => code_buffer.items, - .fail => |em| return .{ .fail = em }, - }; - - const atom = self.getAtomPtr(atom_index); - atom.size = code.len; - // TODO: work out logic for disambiguating functions from function pointers - // const sect_id = self.getDeclOutputSection(decl_index); - const symbol = atom.getSymbolPtr(self); - const name_str_index = try self.strtab.insert(gpa, name); - symbol.n_strx = name_str_index; - symbol.n_type = macho.N_SECT; - symbol.n_sect = sect_id + 1; - symbol.n_value = try self.allocateAtom(atom_index, code.len, required_alignment); - errdefer self.freeAtom(atom_index); - - log.debug("allocated atom for {s} at 0x{x}", .{ name, symbol.n_value }); - log.debug(" (required alignment 0x{x})", .{required_alignment}); - - try self.writeAtom(atom_index, code); - self.markRelocsDirtyByTarget(atom.getSymbolWithLoc()); - - return .{ .ok = atom_index }; + @panic("TODO lowerConst"); } pub fn updateDecl(self: *MachO, mod: *Module, decl_index: InternPool.DeclIndex) !void { @@ -2356,86 +542,11 @@ pub fn updateDecl(self: *MachO, mod: *Module, decl_index: InternPool.DeclIndex) @panic("Attempted to compile for object format that was disabled by build configuration"); } if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(mod, decl_index); + const tracy = trace(@src()); defer tracy.end(); - const comp = self.base.comp; - const gpa = comp.gpa; - const decl = mod.declPtr(decl_index); - - if (decl.val.getExternFunc(mod)) |_| { - return; - } - - if (decl.isExtern(mod)) { - // TODO make this part of getGlobalSymbol - const name = mod.intern_pool.stringToSlice(decl.name); - const sym_name = try std.fmt.allocPrint(gpa, "_{s}", .{name}); - defer gpa.free(sym_name); - _ = try self.addUndefined(sym_name, .{ .add_got = true }); - return; - } - - const is_threadlocal = if (decl.val.getVariable(mod)) |variable| - variable.is_threadlocal and comp.config.any_non_single_threaded - else - false; - if (is_threadlocal) return self.updateThreadlocalVariable(mod, decl_index); - - const atom_index = try self.getOrCreateAtomForDecl(decl_index); - const sym_index = self.getAtom(atom_index).getSymbolIndex().?; - Atom.freeRelocations(self, atom_index); - - var code_buffer = std.ArrayList(u8).init(gpa); - defer code_buffer.deinit(); - - var decl_state: ?Dwarf.DeclState = if (self.d_sym) |*d_sym| - try d_sym.dwarf.initDeclState(mod, decl_index) - else - null; - defer if (decl_state) |*ds| ds.deinit(); - - const decl_val = if (decl.val.getVariable(mod)) |variable| Value.fromInterned(variable.init) else decl.val; - const res = if (decl_state) |*ds| - try codegen.generateSymbol(&self.base, decl.srcLoc(mod), .{ - .ty = decl.ty, - .val = decl_val, - }, &code_buffer, .{ - .dwarf = ds, - }, .{ - .parent_atom_index = sym_index, - }) - else - try codegen.generateSymbol(&self.base, decl.srcLoc(mod), .{ - .ty = decl.ty, - .val = decl_val, - }, &code_buffer, .none, .{ - .parent_atom_index = sym_index, - }); - - const code = switch (res) { - .ok => code_buffer.items, - .fail => |em| { - decl.analysis = .codegen_failure; - try mod.failed_decls.put(mod.gpa, decl_index, em); - return; - }, - }; - const addr = try self.updateDeclCode(decl_index, code); - - if (decl_state) |*ds| { - try self.d_sym.?.dwarf.commitDeclState( - mod, - decl_index, - addr, - self.getAtom(atom_index).size, - ds, - ); - } - - // Since we updated the vaddr and the size, each corresponding export symbol also - // needs to be updated. - try self.updateExports(mod, .{ .decl_index = decl_index }, mod.getDeclExports(decl_index)); + @panic("TODO updateDecl"); } fn updateLazySymbolAtom( @@ -2444,321 +555,36 @@ fn updateLazySymbolAtom( atom_index: Atom.Index, section_index: u8, ) !void { - const gpa = self.base.comp.gpa; - const mod = self.base.comp.module.?; - - var required_alignment: Alignment = .none; - var code_buffer = std.ArrayList(u8).init(gpa); - defer code_buffer.deinit(); - - const name_str_index = blk: { - const name = try std.fmt.allocPrint(gpa, "___lazy_{s}_{}", .{ - @tagName(sym.kind), - sym.ty.fmt(mod), - }); - defer gpa.free(name); - break :blk try self.strtab.insert(gpa, name); - }; - const name = self.strtab.get(name_str_index).?; - - const atom = self.getAtomPtr(atom_index); - const local_sym_index = atom.getSymbolIndex().?; - - const src = if (sym.ty.getOwnerDeclOrNull(mod)) |owner_decl| - mod.declPtr(owner_decl).srcLoc(mod) - else - Module.SrcLoc{ - .file_scope = undefined, - .parent_decl_node = undefined, - .lazy = .unneeded, - }; - const res = try codegen.generateLazySymbol( - &self.base, - src, - sym, - &required_alignment, - &code_buffer, - .none, - .{ .parent_atom_index = local_sym_index }, - ); - const code = switch (res) { - .ok => code_buffer.items, - .fail => |em| { - log.debug("{s}", .{em.msg}); - return error.CodegenFail; - }, - }; - - const symbol = atom.getSymbolPtr(self); - symbol.n_strx = name_str_index; - symbol.n_type = macho.N_SECT; - symbol.n_sect = section_index + 1; - symbol.n_desc = 0; - - const vaddr = try self.allocateAtom(atom_index, code.len, required_alignment); - errdefer self.freeAtom(atom_index); - - log.debug("allocated atom for {s} at 0x{x}", .{ name, vaddr }); - log.debug(" (required alignment 0x{x})", .{required_alignment}); - - atom.size = code.len; - symbol.n_value = vaddr; - - try self.addGotEntry(.{ .sym_index = local_sym_index }); - try self.writeAtom(atom_index, code); + _ = self; + _ = sym; + _ = atom_index; + _ = section_index; + @panic("TODO updateLazySymbolAtom"); } pub fn getOrCreateAtomForLazySymbol(self: *MachO, sym: File.LazySymbol) !Atom.Index { - const mod = self.base.comp.module.?; - const gpa = self.base.comp.gpa; - const gop = try self.lazy_syms.getOrPut(gpa, sym.getDecl(mod)); - errdefer _ = if (!gop.found_existing) self.lazy_syms.pop(); - if (!gop.found_existing) gop.value_ptr.* = .{}; - const metadata: struct { atom: *Atom.Index, state: *LazySymbolMetadata.State } = switch (sym.kind) { - .code => .{ .atom = &gop.value_ptr.text_atom, .state = &gop.value_ptr.text_state }, - .const_data => .{ - .atom = &gop.value_ptr.data_const_atom, - .state = &gop.value_ptr.data_const_state, - }, - }; - switch (metadata.state.*) { - .unused => { - const sym_index = try self.allocateSymbol(); - metadata.atom.* = try self.createAtom(sym_index, .{}); - try self.atom_by_index_table.putNoClobber(gpa, sym_index, metadata.atom.*); - }, - .pending_flush => return metadata.atom.*, - .flushed => {}, - } - metadata.state.* = .pending_flush; - const atom = metadata.atom.*; - // anyerror needs to be deferred until flushModule - if (sym.getDecl(mod) != .none) try self.updateLazySymbolAtom(sym, atom, switch (sym.kind) { - .code => self.text_section_index.?, - .const_data => self.data_const_section_index.?, - }); - return atom; -} - -fn updateThreadlocalVariable(self: *MachO, module: *Module, decl_index: InternPool.DeclIndex) !void { - const mod = self.base.comp.module.?; - // Lowering a TLV on macOS involves two stages: - // 1. first we lower the initializer into appopriate section (__thread_data or __thread_bss) - // 2. next, we create a corresponding threadlocal variable descriptor in __thread_vars - - // 1. Lower the initializer value. - const init_atom_index = try self.getOrCreateAtomForDecl(decl_index); - const init_atom = self.getAtomPtr(init_atom_index); - const init_sym_index = init_atom.getSymbolIndex().?; - Atom.freeRelocations(self, init_atom_index); - - const gpa = self.base.comp.gpa; - - var code_buffer = std.ArrayList(u8).init(gpa); - defer code_buffer.deinit(); - - var decl_state: ?Dwarf.DeclState = if (self.d_sym) |*d_sym| - try d_sym.dwarf.initDeclState(module, decl_index) - else - null; - defer if (decl_state) |*ds| ds.deinit(); - - const decl = module.declPtr(decl_index); - const decl_metadata = self.decls.get(decl_index).?; - const decl_val = Value.fromInterned(decl.val.getVariable(mod).?.init); - const res = if (decl_state) |*ds| - try codegen.generateSymbol(&self.base, decl.srcLoc(mod), .{ - .ty = decl.ty, - .val = decl_val, - }, &code_buffer, .{ - .dwarf = ds, - }, .{ - .parent_atom_index = init_sym_index, - }) - else - try codegen.generateSymbol(&self.base, decl.srcLoc(mod), .{ - .ty = decl.ty, - .val = decl_val, - }, &code_buffer, .none, .{ - .parent_atom_index = init_sym_index, - }); - - const code = switch (res) { - .ok => code_buffer.items, - .fail => |em| { - decl.analysis = .codegen_failure; - try module.failed_decls.put(module.gpa, decl_index, em); - return; - }, - }; - - const required_alignment = decl.getAlignment(mod); - - const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(module)); - - const init_sym_name = try std.fmt.allocPrint(gpa, "{s}$tlv$init", .{decl_name}); - defer gpa.free(init_sym_name); - - const sect_id = decl_metadata.section; - const init_sym = init_atom.getSymbolPtr(self); - init_sym.n_strx = try self.strtab.insert(gpa, init_sym_name); - init_sym.n_type = macho.N_SECT; - init_sym.n_sect = sect_id + 1; - init_sym.n_desc = 0; - init_atom.size = code.len; - - init_sym.n_value = try self.allocateAtom(init_atom_index, code.len, required_alignment); - errdefer self.freeAtom(init_atom_index); - - log.debug("allocated atom for {s} at 0x{x}", .{ init_sym_name, init_sym.n_value }); - log.debug(" (required alignment 0x{x})", .{required_alignment}); - - try self.writeAtom(init_atom_index, code); - - if (decl_state) |*ds| { - try self.d_sym.?.dwarf.commitDeclState( - module, - decl_index, - init_sym.n_value, - self.getAtom(init_atom_index).size, - ds, - ); - } - - try self.updateExports(module, .{ .decl_index = decl_index }, module.getDeclExports(decl_index)); - - // 2. Create a TLV descriptor. - const init_atom_sym_loc = init_atom.getSymbolWithLoc(); - const gop = try self.tlv_table.getOrPut(gpa, init_atom_sym_loc); - assert(!gop.found_existing); - gop.value_ptr.* = try self.createThreadLocalDescriptorAtom(decl_name, init_atom_sym_loc); - self.markRelocsDirtyByTarget(init_atom_sym_loc); + _ = self; + _ = sym; + @panic("TODO getOrCreateAtomForLazySymbol"); } pub fn getOrCreateAtomForDecl(self: *MachO, decl_index: InternPool.DeclIndex) !Atom.Index { - const gpa = self.base.comp.gpa; - const gop = try self.decls.getOrPut(gpa, decl_index); - if (!gop.found_existing) { - const sym_index = try self.allocateSymbol(); - const atom_index = try self.createAtom(sym_index, .{}); - try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index); - gop.value_ptr.* = .{ - .atom = atom_index, - .section = self.getDeclOutputSection(decl_index), - .exports = .{}, - }; - } - return gop.value_ptr.atom; + _ = self; + _ = decl_index; + @panic("TODO getOrCreateAtomForDecl"); } fn getDeclOutputSection(self: *MachO, decl_index: InternPool.DeclIndex) u8 { - const decl = self.base.comp.module.?.declPtr(decl_index); - const ty = decl.ty; - const val = decl.val; - const mod = self.base.comp.module.?; - const zig_ty = ty.zigTypeTag(mod); - const any_non_single_threaded = self.base.comp.config.any_non_single_threaded; - const optimize_mode = self.base.comp.root_mod.optimize_mode; - const sect_id: u8 = blk: { - // TODO finish and audit this function - if (val.isUndefDeep(mod)) { - if (optimize_mode == .ReleaseFast or optimize_mode == .ReleaseSmall) { - @panic("TODO __DATA,__bss"); - } else { - break :blk self.data_section_index.?; - } - } - - if (val.getVariable(mod)) |variable| { - if (variable.is_threadlocal and any_non_single_threaded) { - break :blk self.thread_data_section_index.?; - } - break :blk self.data_section_index.?; - } - - switch (zig_ty) { - // TODO: what if this is a function pointer? - .Fn => break :blk self.text_section_index.?, - else => { - if (val.getVariable(mod)) |_| { - break :blk self.data_section_index.?; - } - break :blk self.data_const_section_index.?; - }, - } - }; - return sect_id; + _ = self; + _ = decl_index; + @panic("TODO getDeclOutputSection"); } fn updateDeclCode(self: *MachO, decl_index: InternPool.DeclIndex, code: []u8) !u64 { - const gpa = self.base.comp.gpa; - const mod = self.base.comp.module.?; - const decl = mod.declPtr(decl_index); - - const required_alignment = decl.getAlignment(mod); - - const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod)); - - const decl_metadata = self.decls.get(decl_index).?; - const atom_index = decl_metadata.atom; - const atom = self.getAtom(atom_index); - const sym_index = atom.getSymbolIndex().?; - const sect_id = decl_metadata.section; - const header = &self.sections.items(.header)[sect_id]; - const segment = self.getSegment(sect_id); - const code_len = code.len; - - if (atom.size != 0) { - const sym = atom.getSymbolPtr(self); - sym.n_strx = try self.strtab.insert(gpa, decl_name); - sym.n_type = macho.N_SECT; - sym.n_sect = sect_id + 1; - sym.n_desc = 0; - - const capacity = atom.capacity(self); - const need_realloc = code_len > capacity or !required_alignment.check(sym.n_value); - - if (need_realloc) { - const vaddr = try self.growAtom(atom_index, code_len, required_alignment); - log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ decl_name, sym.n_value, vaddr }); - log.debug(" (required alignment 0x{x})", .{required_alignment}); - - if (vaddr != sym.n_value) { - sym.n_value = vaddr; - log.debug(" (updating GOT entry)", .{}); - const got_atom_index = self.got_table.lookup.get(.{ .sym_index = sym_index }).?; - try self.writeOffsetTableEntry(got_atom_index); - self.markRelocsDirtyByTarget(.{ .sym_index = sym_index }); - } - } else if (code_len < atom.size) { - self.shrinkAtom(atom_index, code_len); - } else if (atom.next_index == null) { - const needed_size = (sym.n_value + code_len) - segment.vmaddr; - header.size = needed_size; - } - self.getAtomPtr(atom_index).size = code_len; - } else { - const sym = atom.getSymbolPtr(self); - sym.n_strx = try self.strtab.insert(gpa, decl_name); - sym.n_type = macho.N_SECT; - sym.n_sect = sect_id + 1; - sym.n_desc = 0; - - const vaddr = try self.allocateAtom(atom_index, code_len, required_alignment); - errdefer self.freeAtom(atom_index); - - log.debug("allocated atom for {s} at 0x{x}", .{ decl_name, vaddr }); - log.debug(" (required alignment 0x{x})", .{required_alignment}); - - self.getAtomPtr(atom_index).size = code_len; - sym.n_value = vaddr; - - try self.addGotEntry(.{ .sym_index = sym_index }); - } - - try self.writeAtom(atom_index, code); - - return atom.getSymbol(self).n_value; + _ = self; + _ = decl_index; + _ = code; + @panic("TODO updateDeclCode"); } pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl_index: InternPool.DeclIndex) !void { @@ -2779,124 +605,7 @@ pub fn updateExports( if (self.llvm_object) |llvm_object| return llvm_object.updateExports(mod, exported, exports); - const tracy = trace(@src()); - defer tracy.end(); - - const gpa = self.base.comp.gpa; - - const metadata = switch (exported) { - .decl_index => |decl_index| blk: { - _ = try self.getOrCreateAtomForDecl(decl_index); - break :blk self.decls.getPtr(decl_index).?; - }, - .value => |value| self.anon_decls.getPtr(value) orelse blk: { - const first_exp = exports[0]; - const res = try self.lowerAnonDecl(value, .none, first_exp.getSrcLoc(mod)); - switch (res) { - .ok => {}, - .fail => |em| { - // TODO maybe it's enough to return an error here and let Module.processExportsInner - // handle the error? - try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1); - mod.failed_exports.putAssumeCapacityNoClobber(first_exp, em); - return; - }, - } - break :blk self.anon_decls.getPtr(value).?; - }, - }; - const atom_index = metadata.atom; - const atom = self.getAtom(atom_index); - const sym = atom.getSymbol(self); - - for (exports) |exp| { - const exp_name = try std.fmt.allocPrint(gpa, "_{}", .{ - exp.opts.name.fmt(&mod.intern_pool), - }); - defer gpa.free(exp_name); - - log.debug("adding new export '{s}'", .{exp_name}); - - if (exp.opts.section.unwrap()) |section_name| { - if (!mod.intern_pool.stringEqlSlice(section_name, "__text")) { - try mod.failed_exports.putNoClobber(mod.gpa, exp, try Module.ErrorMsg.create( - gpa, - exp.getSrcLoc(mod), - "Unimplemented: ExportOptions.section", - .{}, - )); - continue; - } - } - - if (exp.opts.linkage == .LinkOnce) { - try mod.failed_exports.putNoClobber(mod.gpa, exp, try Module.ErrorMsg.create( - gpa, - exp.getSrcLoc(mod), - "Unimplemented: GlobalLinkage.LinkOnce", - .{}, - )); - continue; - } - - const global_sym_index = metadata.getExport(self, exp_name) orelse blk: { - const global_sym_index = if (self.getGlobalIndex(exp_name)) |global_index| ind: { - const global = self.globals.items[global_index]; - // TODO this is just plain wrong as it all should happen in a single `resolveSymbols` - // pass. This will go away once we abstact away Zig's incremental compilation into - // its own module. - if (global.getFile() == null and self.getSymbol(global).undf()) { - _ = self.unresolved.swapRemove(global_index); - break :ind global.sym_index; - } - break :ind try self.allocateSymbol(); - } else try self.allocateSymbol(); - try metadata.exports.append(gpa, global_sym_index); - break :blk global_sym_index; - }; - const global_sym_loc = SymbolWithLoc{ .sym_index = global_sym_index }; - const global_sym = self.getSymbolPtr(global_sym_loc); - global_sym.* = .{ - .n_strx = try self.strtab.insert(gpa, exp_name), - .n_type = macho.N_SECT | macho.N_EXT, - .n_sect = metadata.section + 1, - .n_desc = 0, - .n_value = sym.n_value, - }; - - switch (exp.opts.linkage) { - .Internal => { - // Symbol should be hidden, or in MachO lingo, private extern. - // We should also mark the symbol as Weak: n_desc == N_WEAK_DEF. - global_sym.n_type |= macho.N_PEXT; - global_sym.n_desc |= macho.N_WEAK_DEF; - }, - .Strong => {}, - .Weak => { - // Weak linkage is specified as part of n_desc field. - // Symbol's n_type is like for a symbol with strong linkage. - global_sym.n_desc |= macho.N_WEAK_DEF; - }, - else => unreachable, - } - - self.resolveGlobalSymbol(global_sym_loc) catch |err| switch (err) { - error.MultipleSymbolDefinitions => { - // TODO: this needs rethinking - const global = self.getGlobal(exp_name).?; - if (global_sym_loc.sym_index != global.sym_index and global.getFile() != null) { - _ = try mod.failed_exports.put(mod.gpa, exp, try Module.ErrorMsg.create( - gpa, - exp.getSrcLoc(mod), - \\LinkError: symbol '{s}' defined multiple times - , - .{exp_name}, - )); - } - }, - else => |e| return e, - }; - } + @panic("TODO updateExports"); } pub fn deleteDeclExport( @@ -2905,82 +614,27 @@ pub fn deleteDeclExport( name: InternPool.NullTerminatedString, ) Allocator.Error!void { if (self.llvm_object) |_| return; - const metadata = self.decls.getPtr(decl_index) orelse return; - - const gpa = self.base.comp.gpa; - const mod = self.base.comp.module.?; - const exp_name = try std.fmt.allocPrint(gpa, "_{s}", .{mod.intern_pool.stringToSlice(name)}); - defer gpa.free(exp_name); - const sym_index = metadata.getExportPtr(self, exp_name) orelse return; - - const sym_loc = SymbolWithLoc{ .sym_index = sym_index.* }; - const sym = self.getSymbolPtr(sym_loc); - log.debug("deleting export '{s}'", .{exp_name}); - assert(sym.sect() and sym.ext()); - sym.* = .{ - .n_strx = 0, - .n_type = 0, - .n_sect = 0, - .n_desc = 0, - .n_value = 0, - }; - self.locals_free_list.append(gpa, sym_index.*) catch {}; - - if (self.resolver.fetchRemove(exp_name)) |entry| { - defer gpa.free(entry.key); - self.globals_free_list.append(gpa, entry.value) catch {}; - self.globals.items[entry.value] = .{ .sym_index = 0 }; - } - - sym_index.* = 0; + _ = decl_index; + _ = name; + @panic("TODO deleteDeclExport"); } fn freeUnnamedConsts(self: *MachO, decl_index: InternPool.DeclIndex) void { - const gpa = self.base.comp.gpa; - const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return; - for (unnamed_consts.items) |atom| { - self.freeAtom(atom); - } - unnamed_consts.clearAndFree(gpa); + _ = self; + _ = decl_index; + @panic("TODO freeUnnamedConst"); } pub fn freeDecl(self: *MachO, decl_index: InternPool.DeclIndex) void { if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index); - const gpa = self.base.comp.gpa; - const mod = self.base.comp.module.?; - const decl = mod.declPtr(decl_index); - - log.debug("freeDecl {*}", .{decl}); - - if (self.decls.fetchSwapRemove(decl_index)) |const_kv| { - var kv = const_kv; - self.freeAtom(kv.value.atom); - self.freeUnnamedConsts(decl_index); - kv.value.exports.deinit(gpa); - } - - if (self.d_sym) |*d_sym| { - d_sym.dwarf.freeDecl(decl_index); - } + @panic("TODO freeDecl"); } pub fn getDeclVAddr(self: *MachO, decl_index: InternPool.DeclIndex, reloc_info: File.RelocInfo) !u64 { assert(self.llvm_object == null); - - const this_atom_index = try self.getOrCreateAtomForDecl(decl_index); - const sym_index = self.getAtom(this_atom_index).getSymbolIndex().?; - const atom_index = self.getAtomIndexForSymbol(.{ .sym_index = reloc_info.parent_atom_index }).?; - try Atom.addRelocation(self, atom_index, .{ - .type = .unsigned, - .target = .{ .sym_index = sym_index }, - .offset = @as(u32, @intCast(reloc_info.offset)), - .addend = reloc_info.addend, - .pcrel = false, - .length = 3, - }); - try Atom.addRebase(self, atom_index, @as(u32, @intCast(reloc_info.offset))); - - return 0; + _ = decl_index; + _ = reloc_info; + @panic("TODO getDeclVAddr"); } pub fn lowerAnonDecl( @@ -2989,1771 +643,25 @@ pub fn lowerAnonDecl( explicit_alignment: InternPool.Alignment, src_loc: Module.SrcLoc, ) !codegen.Result { - const gpa = self.base.comp.gpa; - const mod = self.base.comp.module.?; - const ty = Type.fromInterned(mod.intern_pool.typeOf(decl_val)); - const decl_alignment = switch (explicit_alignment) { - .none => ty.abiAlignment(mod), - else => explicit_alignment, - }; - if (self.anon_decls.get(decl_val)) |metadata| { - const existing_addr = self.getAtom(metadata.atom).getSymbol(self).n_value; - if (decl_alignment.check(existing_addr)) - return .ok; - } - - const val = Value.fromInterned(decl_val); - const tv = TypedValue{ .ty = ty, .val = val }; - var name_buf: [32]u8 = undefined; - const name = std.fmt.bufPrint(&name_buf, "__anon_{d}", .{ - @intFromEnum(decl_val), - }) catch unreachable; - const res = self.lowerConst( - name, - tv, - decl_alignment, - self.data_const_section_index.?, - src_loc, - ) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - else => |e| return .{ .fail = try Module.ErrorMsg.create( - gpa, - src_loc, - "unable to lower constant value: {s}", - .{@errorName(e)}, - ) }, - }; - const atom_index = switch (res) { - .ok => |atom_index| atom_index, - .fail => |em| return .{ .fail = em }, - }; - try self.anon_decls.put(gpa, decl_val, .{ - .atom = atom_index, - .section = self.data_const_section_index.?, - }); - return .ok; + _ = self; + _ = decl_val; + _ = explicit_alignment; + _ = src_loc; + @panic("TODO lowerAnonDecl"); } pub fn getAnonDeclVAddr(self: *MachO, decl_val: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 { assert(self.llvm_object == null); - - const this_atom_index = self.anon_decls.get(decl_val).?.atom; - const sym_index = self.getAtom(this_atom_index).getSymbolIndex().?; - const atom_index = self.getAtomIndexForSymbol(.{ .sym_index = reloc_info.parent_atom_index }).?; - try Atom.addRelocation(self, atom_index, .{ - .type = .unsigned, - .target = .{ .sym_index = sym_index }, - .offset = @as(u32, @intCast(reloc_info.offset)), - .addend = reloc_info.addend, - .pcrel = false, - .length = 3, - }); - try Atom.addRebase(self, atom_index, @as(u32, @intCast(reloc_info.offset))); - - return 0; -} - -const PopulateMissingMetadataOptions = struct { - symbol_count_hint: u64, - program_code_size_hint: u64, -}; - -fn populateMissingMetadata(self: *MachO, options: PopulateMissingMetadataOptions) !void { - assert(self.mode == .incremental); - - const comp = self.base.comp; - const gpa = comp.gpa; - const target = comp.root_mod.resolved_target.result; - const cpu_arch = target.cpu.arch; - const pagezero_vmsize = self.calcPagezeroSize(); - - if (self.pagezero_segment_cmd_index == null) { - if (pagezero_vmsize > 0) { - self.pagezero_segment_cmd_index = @as(u8, @intCast(self.segments.items.len)); - try self.segments.append(gpa, .{ - .segname = makeStaticString("__PAGEZERO"), - .vmsize = pagezero_vmsize, - .cmdsize = @sizeOf(macho.segment_command_64), - }); - } - } - - if (self.header_segment_cmd_index == null) { - // The first __TEXT segment is immovable and covers MachO header and load commands. - self.header_segment_cmd_index = @as(u8, @intCast(self.segments.items.len)); - const ideal_size = self.headerpad_size; - const needed_size = mem.alignForward(u64, padToIdeal(ideal_size), getPageSize(cpu_arch)); - - log.debug("found __TEXT segment (header-only) free space 0x{x} to 0x{x}", .{ 0, needed_size }); - - try self.segments.append(gpa, .{ - .segname = makeStaticString("__TEXT"), - .vmaddr = pagezero_vmsize, - .vmsize = needed_size, - .filesize = needed_size, - .maxprot = macho.PROT.READ | macho.PROT.EXEC, - .initprot = macho.PROT.READ | macho.PROT.EXEC, - .cmdsize = @sizeOf(macho.segment_command_64), - }); - self.segment_table_dirty = true; - } - - if (self.text_section_index == null) { - // Sadly, segments need unique string identfiers for some reason. - self.text_section_index = try self.allocateSection("__TEXT1", "__text", .{ - .size = options.program_code_size_hint, - .alignment = switch (cpu_arch) { - .x86_64 => 1, - .aarch64 => @sizeOf(u32), - else => unreachable, // unhandled architecture type - }, - .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, - .prot = macho.PROT.READ | macho.PROT.EXEC, - }); - self.segment_table_dirty = true; - } - - if (self.stubs_section_index == null) { - const stub_size = stubs.stubSize(cpu_arch); - self.stubs_section_index = try self.allocateSection("__TEXT2", "__stubs", .{ - .size = stub_size, - .alignment = stubs.stubAlignment(cpu_arch), - .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, - .reserved2 = stub_size, - .prot = macho.PROT.READ | macho.PROT.EXEC, - }); - self.segment_table_dirty = true; - } - - if (self.stub_helper_section_index == null) { - self.stub_helper_section_index = try self.allocateSection("__TEXT3", "__stub_helper", .{ - .size = @sizeOf(u32), - .alignment = stubs.stubAlignment(cpu_arch), - .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, - .prot = macho.PROT.READ | macho.PROT.EXEC, - }); - self.segment_table_dirty = true; - } - - if (self.got_section_index == null) { - self.got_section_index = try self.allocateSection("__DATA_CONST", "__got", .{ - .size = @sizeOf(u64) * options.symbol_count_hint, - .alignment = @alignOf(u64), - .flags = macho.S_NON_LAZY_SYMBOL_POINTERS, - .prot = macho.PROT.READ | macho.PROT.WRITE, - }); - self.segment_table_dirty = true; - } - - if (self.data_const_section_index == null) { - self.data_const_section_index = try self.allocateSection("__DATA_CONST1", "__const", .{ - .size = @sizeOf(u64), - .alignment = @alignOf(u64), - .flags = macho.S_REGULAR, - .prot = macho.PROT.READ | macho.PROT.WRITE, - }); - self.segment_table_dirty = true; - } - - if (self.la_symbol_ptr_section_index == null) { - self.la_symbol_ptr_section_index = try self.allocateSection("__DATA", "__la_symbol_ptr", .{ - .size = @sizeOf(u64), - .alignment = @alignOf(u64), - .flags = macho.S_LAZY_SYMBOL_POINTERS, - .prot = macho.PROT.READ | macho.PROT.WRITE, - }); - self.segment_table_dirty = true; - } - - if (self.data_section_index == null) { - self.data_section_index = try self.allocateSection("__DATA1", "__data", .{ - .size = @sizeOf(u64), - .alignment = @alignOf(u64), - .flags = macho.S_REGULAR, - .prot = macho.PROT.READ | macho.PROT.WRITE, - }); - self.segment_table_dirty = true; - } - - if (comp.config.any_non_single_threaded) { - if (self.thread_vars_section_index == null) { - self.thread_vars_section_index = try self.allocateSection("__DATA2", "__thread_vars", .{ - .size = @sizeOf(u64) * 3, - .alignment = @sizeOf(u64), - .flags = macho.S_THREAD_LOCAL_VARIABLES, - .prot = macho.PROT.READ | macho.PROT.WRITE, - }); - self.segment_table_dirty = true; - } - - if (self.thread_data_section_index == null) { - self.thread_data_section_index = try self.allocateSection("__DATA3", "__thread_data", .{ - .size = @sizeOf(u64), - .alignment = @alignOf(u64), - .flags = macho.S_THREAD_LOCAL_REGULAR, - .prot = macho.PROT.READ | macho.PROT.WRITE, - }); - self.segment_table_dirty = true; - } - } - - if (self.linkedit_segment_cmd_index == null) { - self.linkedit_segment_cmd_index = @as(u8, @intCast(self.segments.items.len)); - - try self.segments.append(gpa, .{ - .segname = makeStaticString("__LINKEDIT"), - .maxprot = macho.PROT.READ, - .initprot = macho.PROT.READ, - .cmdsize = @sizeOf(macho.segment_command_64), - }); - } -} - -fn calcPagezeroSize(self: *MachO) u64 { - const output_mode = self.base.comp.config.output_mode; - const target = self.base.comp.root_mod.resolved_target.result; - const page_size = getPageSize(target.cpu.arch); - const aligned_pagezero_vmsize = mem.alignBackward(u64, self.pagezero_vmsize, page_size); - if (output_mode == .Lib) return 0; - if (aligned_pagezero_vmsize == 0) return 0; - if (aligned_pagezero_vmsize != self.pagezero_vmsize) { - log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{self.pagezero_vmsize}); - log.warn(" rounding down to 0x{x}", .{aligned_pagezero_vmsize}); - } - return aligned_pagezero_vmsize; -} - -const InitSectionOpts = struct { - flags: u32 = macho.S_REGULAR, - reserved1: u32 = 0, - reserved2: u32 = 0, -}; - -pub fn initSection(self: *MachO, segname: []const u8, sectname: []const u8, opts: InitSectionOpts) !u8 { - log.debug("creating section '{s},{s}'", .{ segname, sectname }); - const index = @as(u8, @intCast(self.sections.slice().len)); - const gpa = self.base.comp.gpa; - try self.sections.append(gpa, .{ - .segment_index = undefined, // Segments will be created automatically later down the pipeline - .header = .{ - .sectname = makeStaticString(sectname), - .segname = makeStaticString(segname), - .flags = opts.flags, - .reserved1 = opts.reserved1, - .reserved2 = opts.reserved2, - }, - }); - return index; -} - -fn allocateSection(self: *MachO, segname: []const u8, sectname: []const u8, opts: struct { - size: u64 = 0, - alignment: u32 = 0, - prot: macho.vm_prot_t = macho.PROT.NONE, - flags: u32 = macho.S_REGULAR, - reserved2: u32 = 0, -}) !u8 { - const gpa = self.base.comp.gpa; - const target = self.base.comp.root_mod.resolved_target.result; - const page_size = getPageSize(target.cpu.arch); - // In incremental context, we create one section per segment pairing. This way, - // we can move the segment in raw file as we please. - const segment_id = @as(u8, @intCast(self.segments.items.len)); - const vmaddr = blk: { - const prev_segment = self.segments.items[segment_id - 1]; - break :blk mem.alignForward(u64, prev_segment.vmaddr + prev_segment.vmsize, page_size); - }; - // We commit more memory than needed upfront so that we don't have to reallocate too soon. - const vmsize = mem.alignForward(u64, opts.size, page_size); - const off = self.findFreeSpace(opts.size, page_size); - - log.debug("found {s},{s} free space 0x{x} to 0x{x} (0x{x} - 0x{x})", .{ - segname, - sectname, - off, - off + opts.size, - vmaddr, - vmaddr + vmsize, - }); - - const seg = try self.segments.addOne(gpa); - seg.* = .{ - .segname = makeStaticString(segname), - .vmaddr = vmaddr, - .vmsize = vmsize, - .fileoff = off, - .filesize = vmsize, - .maxprot = opts.prot, - .initprot = opts.prot, - .nsects = 1, - .cmdsize = @sizeOf(macho.segment_command_64) + @sizeOf(macho.section_64), - }; - - const sect_id = try self.initSection(segname, sectname, .{ - .flags = opts.flags, - .reserved2 = opts.reserved2, - }); - const section = &self.sections.items(.header)[sect_id]; - section.addr = mem.alignForward(u64, vmaddr, opts.alignment); - section.offset = mem.alignForward(u32, @as(u32, @intCast(off)), opts.alignment); - section.size = opts.size; - section.@"align" = math.log2(opts.alignment); - self.sections.items(.segment_index)[sect_id] = segment_id; - assert(!section.isZerofill()); // TODO zerofill sections - - return sect_id; -} - -fn growSection(self: *MachO, sect_id: u8, needed_size: u64) !void { - const header = &self.sections.items(.header)[sect_id]; - const segment_index = self.sections.items(.segment_index)[sect_id]; - const segment = &self.segments.items[segment_index]; - const maybe_last_atom_index = self.sections.items(.last_atom_index)[sect_id]; - const sect_capacity = self.allocatedSize(header.offset); - const target = self.base.comp.root_mod.resolved_target.result; - const page_size = getPageSize(target.cpu.arch); - - if (needed_size > sect_capacity) { - const new_offset = self.findFreeSpace(needed_size, page_size); - const current_size = if (maybe_last_atom_index) |last_atom_index| blk: { - const last_atom = self.getAtom(last_atom_index); - const sym = last_atom.getSymbol(self); - break :blk (sym.n_value + last_atom.size) - segment.vmaddr; - } else header.size; - - log.debug("moving {s},{s} from 0x{x} to 0x{x}", .{ - header.segName(), - header.sectName(), - header.offset, - new_offset, - }); - - const amt = try self.base.file.?.copyRangeAll( - header.offset, - self.base.file.?, - new_offset, - current_size, - ); - if (amt != current_size) return error.InputOutput; - header.offset = @as(u32, @intCast(new_offset)); - segment.fileoff = new_offset; - } - - const sect_vm_capacity = self.allocatedVirtualSize(segment.vmaddr); - if (needed_size > sect_vm_capacity) { - self.markRelocsDirtyByAddress(segment.vmaddr + segment.vmsize); - try self.growSectionVirtualMemory(sect_id, needed_size); - } - - header.size = needed_size; - segment.filesize = mem.alignForward(u64, needed_size, page_size); - segment.vmsize = mem.alignForward(u64, needed_size, page_size); -} - -fn growSectionVirtualMemory(self: *MachO, sect_id: u8, needed_size: u64) !void { - const target = self.base.comp.root_mod.resolved_target.result; - const page_size = getPageSize(target.cpu.arch); - const header = &self.sections.items(.header)[sect_id]; - const segment = self.getSegmentPtr(sect_id); - const increased_size = padToIdeal(needed_size); - const old_aligned_end = segment.vmaddr + segment.vmsize; - const new_aligned_end = segment.vmaddr + mem.alignForward(u64, increased_size, page_size); - const diff = new_aligned_end - old_aligned_end; - log.debug("shifting every segment after {s},{s} in virtual memory by {x}", .{ - header.segName(), - header.sectName(), - diff, - }); - - // TODO: enforce order by increasing VM addresses in self.sections container. - for (self.sections.items(.header)[sect_id + 1 ..], 0..) |*next_header, next_sect_id| { - const index = @as(u8, @intCast(sect_id + 1 + next_sect_id)); - const next_segment = self.getSegmentPtr(index); - next_header.addr += diff; - next_segment.vmaddr += diff; - - const maybe_last_atom_index = &self.sections.items(.last_atom_index)[index]; - if (maybe_last_atom_index.*) |last_atom_index| { - var atom_index = last_atom_index; - while (true) { - const atom = self.getAtom(atom_index); - const sym = atom.getSymbolPtr(self); - sym.n_value += diff; - - if (atom.prev_index) |prev_index| { - atom_index = prev_index; - } else break; - } - } - } -} - -pub fn addAtomToSection(self: *MachO, atom_index: Atom.Index) void { - assert(self.mode == .zld); - const atom = self.getAtomPtr(atom_index); - const sym = self.getSymbol(atom.getSymbolWithLoc()); - var section = self.sections.get(sym.n_sect - 1); - if (section.header.size > 0) { - const last_atom = self.getAtomPtr(section.last_atom_index.?); - last_atom.next_index = atom_index; - atom.prev_index = section.last_atom_index; - } else { - section.first_atom_index = atom_index; - } - section.last_atom_index = atom_index; - section.header.size += atom.size; - self.sections.set(sym.n_sect - 1, section); -} - -fn allocateAtom(self: *MachO, atom_index: Atom.Index, new_atom_size: u64, alignment: Alignment) !u64 { - const tracy = trace(@src()); - defer tracy.end(); - - assert(self.mode == .incremental); - - const atom = self.getAtom(atom_index); - const sect_id = atom.getSymbol(self).n_sect - 1; - const segment = self.getSegmentPtr(sect_id); - const header = &self.sections.items(.header)[sect_id]; - const free_list = &self.sections.items(.free_list)[sect_id]; - const maybe_last_atom_index = &self.sections.items(.last_atom_index)[sect_id]; - const requires_padding = blk: { - if (!header.isCode()) break :blk false; - if (header.isSymbolStubs()) break :blk false; - if (mem.eql(u8, "__stub_helper", header.sectName())) break :blk false; - break :blk true; - }; - const new_atom_ideal_capacity = if (requires_padding) padToIdeal(new_atom_size) else new_atom_size; - - // We use these to indicate our intention to update metadata, placing the new atom, - // and possibly removing a free list node. - // It would be simpler to do it inside the for loop below, but that would cause a - // problem if an error was returned later in the function. So this action - // is actually carried out at the end of the function, when errors are no longer possible. - var atom_placement: ?Atom.Index = null; - var free_list_removal: ?usize = null; - - // First we look for an appropriately sized free list node. - // The list is unordered. We'll just take the first thing that works. - const vaddr = blk: { - var i: usize = 0; - while (i < free_list.items.len) { - const big_atom_index = free_list.items[i]; - const big_atom = self.getAtom(big_atom_index); - // We now have a pointer to a live atom that has too much capacity. - // Is it enough that we could fit this new atom? - const sym = big_atom.getSymbol(self); - const capacity = big_atom.capacity(self); - const ideal_capacity = if (requires_padding) padToIdeal(capacity) else capacity; - const ideal_capacity_end_vaddr = math.add(u64, sym.n_value, ideal_capacity) catch ideal_capacity; - const capacity_end_vaddr = sym.n_value + capacity; - const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity; - const new_start_vaddr = alignment.backward(new_start_vaddr_unaligned); - if (new_start_vaddr < ideal_capacity_end_vaddr) { - // Additional bookkeeping here to notice if this free list node - // should be deleted because the atom that it points to has grown to take up - // more of the extra capacity. - if (!big_atom.freeListEligible(self)) { - _ = free_list.swapRemove(i); - } else { - i += 1; - } - continue; - } - // At this point we know that we will place the new atom here. But the - // remaining question is whether there is still yet enough capacity left - // over for there to still be a free list node. - const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr; - const keep_free_list_node = remaining_capacity >= min_text_capacity; - - // Set up the metadata to be updated, after errors are no longer possible. - atom_placement = big_atom_index; - if (!keep_free_list_node) { - free_list_removal = i; - } - break :blk new_start_vaddr; - } else if (maybe_last_atom_index.*) |last_index| { - const last = self.getAtom(last_index); - const last_symbol = last.getSymbol(self); - const ideal_capacity = if (requires_padding) padToIdeal(last.size) else last.size; - const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity; - const new_start_vaddr = alignment.forward(ideal_capacity_end_vaddr); - atom_placement = last_index; - break :blk new_start_vaddr; - } else { - break :blk alignment.forward(segment.vmaddr); - } - }; - - const expand_section = if (atom_placement) |placement_index| - self.getAtom(placement_index).next_index == null - else - true; - if (expand_section) { - const needed_size = (vaddr + new_atom_size) - segment.vmaddr; - try self.growSection(sect_id, needed_size); - maybe_last_atom_index.* = atom_index; - self.segment_table_dirty = true; - } - - assert(alignment != .none); - header.@"align" = @min(header.@"align", @intFromEnum(alignment)); - self.getAtomPtr(atom_index).size = new_atom_size; - - if (atom.prev_index) |prev_index| { - const prev = self.getAtomPtr(prev_index); - prev.next_index = atom.next_index; - } - if (atom.next_index) |next_index| { - const next = self.getAtomPtr(next_index); - next.prev_index = atom.prev_index; - } - - if (atom_placement) |big_atom_index| { - const big_atom = self.getAtomPtr(big_atom_index); - const atom_ptr = self.getAtomPtr(atom_index); - atom_ptr.prev_index = big_atom_index; - atom_ptr.next_index = big_atom.next_index; - big_atom.next_index = atom_index; - } else { - const atom_ptr = self.getAtomPtr(atom_index); - atom_ptr.prev_index = null; - atom_ptr.next_index = null; - } - if (free_list_removal) |i| { - _ = free_list.swapRemove(i); - } - - return vaddr; + _ = decl_val; + _ = reloc_info; + @panic("TODO getAnonDeclVAddr"); } pub fn getGlobalSymbol(self: *MachO, name: []const u8, lib_name: ?[]const u8) !u32 { + _ = self; + _ = name; _ = lib_name; - const gpa = self.base.comp.gpa; - const sym_name = try std.fmt.allocPrint(gpa, "_{s}", .{name}); - defer gpa.free(sym_name); - return self.addUndefined(sym_name, .{ .add_stub = true }); -} - -pub fn writeSegmentHeaders(self: *MachO, writer: anytype) !void { - for (self.segments.items, 0..) |seg, i| { - const indexes = self.getSectionIndexes(@intCast(i)); - var out_seg = seg; - out_seg.cmdsize = @sizeOf(macho.segment_command_64); - out_seg.nsects = 0; - - // Update section headers count; any section with size of 0 is excluded - // since it doesn't have any data in the final binary file. - for (self.sections.items(.header)[indexes.start..indexes.end]) |header| { - if (header.size == 0) continue; - out_seg.cmdsize += @sizeOf(macho.section_64); - out_seg.nsects += 1; - } - - if (out_seg.nsects == 0 and - (mem.eql(u8, out_seg.segName(), "__DATA_CONST") or - mem.eql(u8, out_seg.segName(), "__DATA"))) continue; - - try writer.writeStruct(out_seg); - for (self.sections.items(.header)[indexes.start..indexes.end]) |header| { - if (header.size == 0) continue; - try writer.writeStruct(header); - } - } -} - -pub fn writeLinkeditSegmentData(self: *MachO) !void { - const target = self.base.comp.root_mod.resolved_target.result; - const page_size = getPageSize(target.cpu.arch); - const seg = self.getLinkeditSegmentPtr(); - seg.filesize = 0; - seg.vmsize = 0; - - for (self.segments.items, 0..) |segment, id| { - if (self.linkedit_segment_cmd_index.? == @as(u8, @intCast(id))) continue; - if (seg.vmaddr < segment.vmaddr + segment.vmsize) { - seg.vmaddr = mem.alignForward(u64, segment.vmaddr + segment.vmsize, page_size); - } - if (seg.fileoff < segment.fileoff + segment.filesize) { - seg.fileoff = mem.alignForward(u64, segment.fileoff + segment.filesize, page_size); - } - } - - try self.writeDyldInfoData(); - // TODO handle this better - if (self.mode == .zld) { - try self.writeFunctionStarts(); - try self.writeDataInCode(); - } - try self.writeSymtabs(); - - seg.vmsize = mem.alignForward(u64, seg.filesize, page_size); -} - -fn collectRebaseDataFromTableSection(self: *MachO, sect_id: u8, rebase: *Rebase, table: anytype) !void { - const gpa = self.base.comp.gpa; - const header = self.sections.items(.header)[sect_id]; - const segment_index = self.sections.items(.segment_index)[sect_id]; - const segment = self.segments.items[segment_index]; - const base_offset = header.addr - segment.vmaddr; - const is_got = if (self.got_section_index) |index| index == sect_id else false; - - try rebase.entries.ensureUnusedCapacity(gpa, table.entries.items.len); - - for (table.entries.items, 0..) |entry, i| { - if (!table.lookup.contains(entry)) continue; - const sym = self.getSymbol(entry); - if (is_got and sym.undf()) continue; - const offset = i * @sizeOf(u64); - log.debug(" | rebase at {x}", .{base_offset + offset}); - rebase.entries.appendAssumeCapacity(.{ - .offset = base_offset + offset, - .segment_id = segment_index, - }); - } -} - -fn collectRebaseData(self: *MachO, rebase: *Rebase) !void { - const gpa = self.base.comp.gpa; - const slice = self.sections.slice(); - - for (self.rebases.keys(), 0..) |atom_index, i| { - const atom = self.getAtom(atom_index); - log.debug(" ATOM(%{?d}, '{s}')", .{ atom.getSymbolIndex(), atom.getName(self) }); - - const sym = atom.getSymbol(self); - const segment_index = slice.items(.segment_index)[sym.n_sect - 1]; - const seg = self.getSegment(sym.n_sect - 1); - - const base_offset = sym.n_value - seg.vmaddr; - - const rebases = self.rebases.values()[i]; - try rebase.entries.ensureUnusedCapacity(gpa, rebases.items.len); - - for (rebases.items) |offset| { - log.debug(" | rebase at {x}", .{base_offset + offset}); - - rebase.entries.appendAssumeCapacity(.{ - .offset = base_offset + offset, - .segment_id = segment_index, - }); - } - } - - // Unpack GOT entries - if (self.got_section_index) |sect_id| { - try self.collectRebaseDataFromTableSection(sect_id, rebase, self.got_table); - } - - // Next, unpack __la_symbol_ptr entries - if (self.la_symbol_ptr_section_index) |sect_id| { - try self.collectRebaseDataFromTableSection(sect_id, rebase, self.stub_table); - } - - // Finally, unpack the rest. - const target = self.base.comp.root_mod.resolved_target.result; - const cpu_arch = target.cpu.arch; - for (self.objects.items) |*object| { - for (object.atoms.items) |atom_index| { - const atom = self.getAtom(atom_index); - const sym = self.getSymbol(atom.getSymbolWithLoc()); - if (sym.n_desc == N_DEAD) continue; - if (sym.n_desc == N_BOUNDARY) continue; - - const sect_id = sym.n_sect - 1; - const section = self.sections.items(.header)[sect_id]; - const segment_id = self.sections.items(.segment_index)[sect_id]; - const segment = self.segments.items[segment_id]; - if (segment.maxprot & macho.PROT.WRITE == 0) continue; - switch (section.type()) { - macho.S_LITERAL_POINTERS, - macho.S_REGULAR, - macho.S_MOD_INIT_FUNC_POINTERS, - macho.S_MOD_TERM_FUNC_POINTERS, - => {}, - else => continue, - } - - log.debug(" ATOM({d}, %{d}, '{s}')", .{ - atom_index, - atom.sym_index, - self.getSymbolName(atom.getSymbolWithLoc()), - }); - - const code = Atom.getAtomCode(self, atom_index); - const relocs = Atom.getAtomRelocs(self, atom_index); - const ctx = Atom.getRelocContext(self, atom_index); - - for (relocs) |rel| { - switch (cpu_arch) { - .aarch64 => { - const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)); - if (rel_type != .ARM64_RELOC_UNSIGNED) continue; - if (rel.r_length != 3) continue; - }, - .x86_64 => { - const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type)); - if (rel_type != .X86_64_RELOC_UNSIGNED) continue; - if (rel.r_length != 3) continue; - }, - else => unreachable, - } - const reloc_target = Atom.parseRelocTarget(self, .{ - .object_id = atom.getFile().?, - .rel = rel, - .code = code, - .base_offset = ctx.base_offset, - .base_addr = ctx.base_addr, - }); - const target_sym = self.getSymbol(reloc_target); - if (target_sym.undf()) continue; - - const base_offset = @as(i32, @intCast(sym.n_value - segment.vmaddr)); - const rel_offset = rel.r_address - ctx.base_offset; - const offset = @as(u64, @intCast(base_offset + rel_offset)); - log.debug(" | rebase at {x}", .{offset}); - - try rebase.entries.append(gpa, .{ - .offset = offset, - .segment_id = segment_id, - }); - } - } - } - - try rebase.finalize(gpa); -} - -fn collectBindDataFromTableSection(self: *MachO, sect_id: u8, bind: anytype, table: anytype) !void { - const gpa = self.base.comp.gpa; - const header = self.sections.items(.header)[sect_id]; - const segment_index = self.sections.items(.segment_index)[sect_id]; - const segment = self.segments.items[segment_index]; - const base_offset = header.addr - segment.vmaddr; - - try bind.entries.ensureUnusedCapacity(gpa, table.entries.items.len); - - for (table.entries.items, 0..) |entry, i| { - if (!table.lookup.contains(entry)) continue; - const bind_sym = self.getSymbol(entry); - if (!bind_sym.undf()) continue; - const offset = i * @sizeOf(u64); - log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{ - base_offset + offset, - self.getSymbolName(entry), - @divTrunc(@as(i16, @bitCast(bind_sym.n_desc)), macho.N_SYMBOL_RESOLVER), - }); - if (bind_sym.weakRef()) { - log.debug(" | marking as weak ref ", .{}); - } - bind.entries.appendAssumeCapacity(.{ - .target = entry, - .offset = base_offset + offset, - .segment_id = segment_index, - .addend = 0, - }); - } -} - -fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void { - const gpa = self.base.comp.gpa; - const slice = self.sections.slice(); - - for (raw_bindings.keys(), 0..) |atom_index, i| { - const atom = self.getAtom(atom_index); - log.debug(" ATOM(%{?d}, '{s}')", .{ atom.getSymbolIndex(), atom.getName(self) }); - - const sym = atom.getSymbol(self); - const segment_index = slice.items(.segment_index)[sym.n_sect - 1]; - const seg = self.getSegment(sym.n_sect - 1); - - const base_offset = sym.n_value - seg.vmaddr; - - const bindings = raw_bindings.values()[i]; - try bind.entries.ensureUnusedCapacity(gpa, bindings.items.len); - - for (bindings.items) |binding| { - const bind_sym = self.getSymbol(binding.target); - const bind_sym_name = self.getSymbolName(binding.target); - const dylib_ordinal = @divTrunc( - @as(i16, @bitCast(bind_sym.n_desc)), - macho.N_SYMBOL_RESOLVER, - ); - log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{ - binding.offset + base_offset, - bind_sym_name, - dylib_ordinal, - }); - if (bind_sym.weakRef()) { - log.debug(" | marking as weak ref ", .{}); - } - bind.entries.appendAssumeCapacity(.{ - .target = binding.target, - .offset = binding.offset + base_offset, - .segment_id = segment_index, - .addend = 0, - }); - } - } - - // Unpack GOT pointers - if (self.got_section_index) |sect_id| { - try self.collectBindDataFromTableSection(sect_id, bind, self.got_table); - } - - // Next, unpack TLV pointers section - if (self.tlv_ptr_section_index) |sect_id| { - try self.collectBindDataFromTableSection(sect_id, bind, self.tlv_ptr_table); - } - - // Finally, unpack the rest. - const target = self.base.comp.root_mod.resolved_target.result; - const cpu_arch = target.cpu.arch; - for (self.objects.items) |*object| { - for (object.atoms.items) |atom_index| { - const atom = self.getAtom(atom_index); - const sym = self.getSymbol(atom.getSymbolWithLoc()); - if (sym.n_desc == N_DEAD) continue; - if (sym.n_desc == N_BOUNDARY) continue; - - const sect_id = sym.n_sect - 1; - const section = self.sections.items(.header)[sect_id]; - const segment_id = self.sections.items(.segment_index)[sect_id]; - const segment = self.segments.items[segment_id]; - if (segment.maxprot & macho.PROT.WRITE == 0) continue; - switch (section.type()) { - macho.S_LITERAL_POINTERS, - macho.S_REGULAR, - macho.S_MOD_INIT_FUNC_POINTERS, - macho.S_MOD_TERM_FUNC_POINTERS, - => {}, - else => continue, - } - - log.debug(" ATOM({d}, %{d}, '{s}')", .{ - atom_index, - atom.sym_index, - self.getSymbolName(atom.getSymbolWithLoc()), - }); - - const code = Atom.getAtomCode(self, atom_index); - const relocs = Atom.getAtomRelocs(self, atom_index); - const ctx = Atom.getRelocContext(self, atom_index); - - for (relocs) |rel| { - switch (cpu_arch) { - .aarch64 => { - const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)); - if (rel_type != .ARM64_RELOC_UNSIGNED) continue; - if (rel.r_length != 3) continue; - }, - .x86_64 => { - const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type)); - if (rel_type != .X86_64_RELOC_UNSIGNED) continue; - if (rel.r_length != 3) continue; - }, - else => unreachable, - } - - const global = Atom.parseRelocTarget(self, .{ - .object_id = atom.getFile().?, - .rel = rel, - .code = code, - .base_offset = ctx.base_offset, - .base_addr = ctx.base_addr, - }); - const bind_sym_name = self.getSymbolName(global); - const bind_sym = self.getSymbol(global); - if (!bind_sym.undf()) continue; - - const base_offset = sym.n_value - segment.vmaddr; - const rel_offset = @as(u32, @intCast(rel.r_address - ctx.base_offset)); - const offset = @as(u64, @intCast(base_offset + rel_offset)); - const addend = mem.readInt(i64, code[rel_offset..][0..8], .little); - - const dylib_ordinal = @divTrunc(@as(i16, @bitCast(bind_sym.n_desc)), macho.N_SYMBOL_RESOLVER); - log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{ - base_offset, - bind_sym_name, - dylib_ordinal, - }); - log.debug(" | with addend {x}", .{addend}); - if (bind_sym.weakRef()) { - log.debug(" | marking as weak ref ", .{}); - } - try bind.entries.append(gpa, .{ - .target = global, - .offset = offset, - .segment_id = segment_id, - .addend = addend, - }); - } - } - } - - try bind.finalize(gpa, self); -} - -fn collectLazyBindData(self: *MachO, bind: anytype) !void { - const sect_id = self.la_symbol_ptr_section_index orelse return; - const gpa = self.base.comp.gpa; - try self.collectBindDataFromTableSection(sect_id, bind, self.stub_table); - try bind.finalize(gpa, self); -} - -fn collectExportData(self: *MachO, trie: *Trie) !void { - const gpa = self.base.comp.gpa; - - // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER. - log.debug("generating export trie", .{}); - - const exec_segment = self.segments.items[self.header_segment_cmd_index.?]; - const base_address = exec_segment.vmaddr; - - for (self.globals.items) |global| { - const sym = self.getSymbol(global); - - if (sym.undf()) continue; - assert(sym.ext()); - if (sym.n_desc == N_DEAD) continue; - if (sym.n_desc == N_BOUNDARY) continue; - - const sym_name = self.getSymbolName(global); - log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value }); - try trie.put(gpa, .{ - .name = sym_name, - .vmaddr_offset = sym.n_value - base_address, - .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR, - }); - } - - try trie.finalize(gpa); -} - -fn writeDyldInfoData(self: *MachO) !void { - const tracy = trace(@src()); - defer tracy.end(); - - const gpa = self.base.comp.gpa; - - var rebase = Rebase{}; - defer rebase.deinit(gpa); - try self.collectRebaseData(&rebase); - - var bind = Bind{}; - defer bind.deinit(gpa); - try self.collectBindData(&bind, self.bindings); - - var lazy_bind = LazyBind{}; - defer lazy_bind.deinit(gpa); - try self.collectLazyBindData(&lazy_bind); - - var trie: Trie = .{}; - defer trie.deinit(gpa); - try trie.init(gpa); - try self.collectExportData(&trie); - - const link_seg = self.getLinkeditSegmentPtr(); - assert(mem.isAlignedGeneric(u64, link_seg.fileoff, @alignOf(u64))); - const rebase_off = link_seg.fileoff; - const rebase_size = rebase.size(); - const rebase_size_aligned = mem.alignForward(u64, rebase_size, @alignOf(u64)); - log.debug("writing rebase info from 0x{x} to 0x{x}", .{ rebase_off, rebase_off + rebase_size_aligned }); - - const bind_off = rebase_off + rebase_size_aligned; - const bind_size = bind.size(); - const bind_size_aligned = mem.alignForward(u64, bind_size, @alignOf(u64)); - log.debug("writing bind info from 0x{x} to 0x{x}", .{ bind_off, bind_off + bind_size_aligned }); - - const lazy_bind_off = bind_off + bind_size_aligned; - const lazy_bind_size = lazy_bind.size(); - const lazy_bind_size_aligned = mem.alignForward(u64, lazy_bind_size, @alignOf(u64)); - log.debug("writing lazy bind info from 0x{x} to 0x{x}", .{ - lazy_bind_off, - lazy_bind_off + lazy_bind_size_aligned, - }); - - const export_off = lazy_bind_off + lazy_bind_size_aligned; - const export_size = trie.size; - const export_size_aligned = mem.alignForward(u64, export_size, @alignOf(u64)); - log.debug("writing export trie from 0x{x} to 0x{x}", .{ export_off, export_off + export_size_aligned }); - - const needed_size = math.cast(usize, export_off + export_size_aligned - rebase_off) orelse - return error.Overflow; - link_seg.filesize = needed_size; - assert(mem.isAlignedGeneric(u64, link_seg.fileoff + link_seg.filesize, @alignOf(u64))); - - const buffer = try gpa.alloc(u8, needed_size); - defer gpa.free(buffer); - @memset(buffer, 0); - - var stream = std.io.fixedBufferStream(buffer); - const writer = stream.writer(); - - try rebase.write(writer); - try stream.seekTo(bind_off - rebase_off); - - try bind.write(writer); - try stream.seekTo(lazy_bind_off - rebase_off); - - try lazy_bind.write(writer); - try stream.seekTo(export_off - rebase_off); - - _ = try trie.write(writer); - - log.debug("writing dyld info from 0x{x} to 0x{x}", .{ - rebase_off, - rebase_off + needed_size, - }); - - try self.base.file.?.pwriteAll(buffer, rebase_off); - try self.populateLazyBindOffsetsInStubHelper(lazy_bind); - - self.dyld_info_cmd.rebase_off = @as(u32, @intCast(rebase_off)); - self.dyld_info_cmd.rebase_size = @as(u32, @intCast(rebase_size_aligned)); - self.dyld_info_cmd.bind_off = @as(u32, @intCast(bind_off)); - self.dyld_info_cmd.bind_size = @as(u32, @intCast(bind_size_aligned)); - self.dyld_info_cmd.lazy_bind_off = @as(u32, @intCast(lazy_bind_off)); - self.dyld_info_cmd.lazy_bind_size = @as(u32, @intCast(lazy_bind_size_aligned)); - self.dyld_info_cmd.export_off = @as(u32, @intCast(export_off)); - self.dyld_info_cmd.export_size = @as(u32, @intCast(export_size_aligned)); -} - -fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: anytype) !void { - if (lazy_bind.size() == 0) return; - - const stub_helper_section_index = self.stub_helper_section_index.?; - // assert(ctx.stub_helper_preamble_allocated); - - const header = self.sections.items(.header)[stub_helper_section_index]; - - const target = self.base.comp.root_mod.resolved_target.result; - const cpu_arch = target.cpu.arch; - const preamble_size = stubs.stubHelperPreambleSize(cpu_arch); - const stub_size = stubs.stubHelperSize(cpu_arch); - const stub_offset = stubs.stubOffsetInStubHelper(cpu_arch); - const base_offset = header.offset + preamble_size; - - for (lazy_bind.offsets.items, 0..) |bind_offset, index| { - const file_offset = base_offset + index * stub_size + stub_offset; - - log.debug("writing lazy bind offset 0x{x} ({s}) in stub helper at 0x{x}", .{ - bind_offset, - self.getSymbolName(lazy_bind.entries.items[index].target), - file_offset, - }); - - try self.base.file.?.pwriteAll(mem.asBytes(&bind_offset), file_offset); - } -} - -const asc_u64 = std.sort.asc(u64); - -fn addSymbolToFunctionStarts(self: *MachO, sym_loc: SymbolWithLoc, addresses: *std.ArrayList(u64)) !void { - const sym = self.getSymbol(sym_loc); - if (sym.n_strx == 0) return; - if (sym.n_desc == N_DEAD) return; - if (sym.n_desc == N_BOUNDARY) return; - if (self.symbolIsTemp(sym_loc)) return; - try addresses.append(sym.n_value); -} - -fn writeFunctionStarts(self: *MachO) !void { - const gpa = self.base.comp.gpa; - const seg = self.segments.items[self.header_segment_cmd_index.?]; - - // We need to sort by address first - var addresses = std.ArrayList(u64).init(gpa); - defer addresses.deinit(); - - for (self.objects.items) |object| { - for (object.exec_atoms.items) |atom_index| { - const atom = self.getAtom(atom_index); - const sym_loc = atom.getSymbolWithLoc(); - try self.addSymbolToFunctionStarts(sym_loc, &addresses); - - var it = Atom.getInnerSymbolsIterator(self, atom_index); - while (it.next()) |inner_sym_loc| { - try self.addSymbolToFunctionStarts(inner_sym_loc, &addresses); - } - } - } - - mem.sort(u64, addresses.items, {}, asc_u64); - - var offsets = std.ArrayList(u32).init(gpa); - defer offsets.deinit(); - try offsets.ensureTotalCapacityPrecise(addresses.items.len); - - var last_off: u32 = 0; - for (addresses.items) |addr| { - const offset = @as(u32, @intCast(addr - seg.vmaddr)); - const diff = offset - last_off; - - if (diff == 0) continue; - - offsets.appendAssumeCapacity(diff); - last_off = offset; - } - - var buffer = std.ArrayList(u8).init(gpa); - defer buffer.deinit(); - - const max_size = @as(usize, @intCast(offsets.items.len * @sizeOf(u64))); - try buffer.ensureTotalCapacity(max_size); - - for (offsets.items) |offset| { - try std.leb.writeULEB128(buffer.writer(), offset); - } - - const link_seg = self.getLinkeditSegmentPtr(); - const offset = link_seg.fileoff + link_seg.filesize; - assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64))); - const needed_size = buffer.items.len; - const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64)); - const padding = math.cast(usize, needed_size_aligned - needed_size) orelse return error.Overflow; - if (padding > 0) { - try buffer.ensureUnusedCapacity(padding); - buffer.appendNTimesAssumeCapacity(0, padding); - } - link_seg.filesize = offset + needed_size_aligned - link_seg.fileoff; - - log.debug("writing function starts info from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned }); - - try self.base.file.?.pwriteAll(buffer.items, offset); - - self.function_starts_cmd.dataoff = @as(u32, @intCast(offset)); - self.function_starts_cmd.datasize = @as(u32, @intCast(needed_size_aligned)); -} - -fn filterDataInCode( - dices: []const macho.data_in_code_entry, - start_addr: u64, - end_addr: u64, -) []const macho.data_in_code_entry { - const Predicate = struct { - addr: u64, - - pub fn predicate(self: @This(), dice: macho.data_in_code_entry) bool { - return dice.offset >= self.addr; - } - }; - - const start = MachO.lsearch(macho.data_in_code_entry, dices, Predicate{ .addr = start_addr }); - const end = MachO.lsearch(macho.data_in_code_entry, dices[start..], Predicate{ .addr = end_addr }) + start; - - return dices[start..end]; -} - -pub fn writeDataInCode(self: *MachO) !void { - const gpa = self.base.comp.gpa; - var out_dice = std.ArrayList(macho.data_in_code_entry).init(gpa); - defer out_dice.deinit(); - - const text_sect_id = self.text_section_index orelse return; - const text_sect_header = self.sections.items(.header)[text_sect_id]; - - for (self.objects.items) |object| { - if (!object.hasDataInCode()) continue; - const dice = object.data_in_code.items; - try out_dice.ensureUnusedCapacity(dice.len); - - for (object.exec_atoms.items) |atom_index| { - const atom = self.getAtom(atom_index); - const sym = self.getSymbol(atom.getSymbolWithLoc()); - if (sym.n_desc == N_DEAD) continue; - if (sym.n_desc == N_BOUNDARY) return; - - const source_addr = if (object.getSourceSymbol(atom.sym_index)) |source_sym| - source_sym.n_value - else blk: { - const nbase = @as(u32, @intCast(object.in_symtab.?.len)); - const source_sect_id = @as(u8, @intCast(atom.sym_index - nbase)); - break :blk object.getSourceSection(source_sect_id).addr; - }; - const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size); - const base = math.cast(u32, sym.n_value - text_sect_header.addr + text_sect_header.offset) orelse - return error.Overflow; - - for (filtered_dice) |single| { - const offset = math.cast(u32, single.offset - source_addr + base) orelse - return error.Overflow; - out_dice.appendAssumeCapacity(.{ - .offset = offset, - .length = single.length, - .kind = single.kind, - }); - } - } - } - - const seg = self.getLinkeditSegmentPtr(); - const offset = seg.fileoff + seg.filesize; - assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64))); - const needed_size = out_dice.items.len * @sizeOf(macho.data_in_code_entry); - const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64)); - seg.filesize = offset + needed_size_aligned - seg.fileoff; - - const buffer = try gpa.alloc(u8, math.cast(usize, needed_size_aligned) orelse return error.Overflow); - defer gpa.free(buffer); - { - const src = mem.sliceAsBytes(out_dice.items); - @memcpy(buffer[0..src.len], src); - @memset(buffer[src.len..], 0); - } - - log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned }); - - try self.base.file.?.pwriteAll(buffer, offset); - - self.data_in_code_cmd.dataoff = @as(u32, @intCast(offset)); - self.data_in_code_cmd.datasize = @as(u32, @intCast(needed_size_aligned)); -} - -fn writeSymtabs(self: *MachO) !void { - var ctx = try self.writeSymtab(); - defer ctx.imports_table.deinit(); - try self.writeDysymtab(ctx); - try self.writeStrtab(); -} - -fn addLocalToSymtab(self: *MachO, sym_loc: SymbolWithLoc, locals: *std.ArrayList(macho.nlist_64)) !void { - const sym = self.getSymbol(sym_loc); - if (sym.n_strx == 0) return; // no name, skip - if (sym.n_desc == N_DEAD) return; // garbage-collected, skip - if (sym.n_desc == N_BOUNDARY) return; // boundary symbol, skip - if (sym.ext()) return; // an export lands in its own symtab section, skip - if (self.symbolIsTemp(sym_loc)) return; // local temp symbol, skip - const gpa = self.base.comp.gpa; - var out_sym = sym; - out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(sym_loc)); - try locals.append(out_sym); -} - -fn writeSymtab(self: *MachO) !SymtabCtx { - const comp = self.base.comp; - const gpa = comp.gpa; - - var locals = std.ArrayList(macho.nlist_64).init(gpa); - defer locals.deinit(); - - for (0..self.locals.items.len) |sym_id| { - try self.addLocalToSymtab(.{ .sym_index = @intCast(sym_id) }, &locals); - } - - for (self.objects.items) |object| { - for (object.atoms.items) |atom_index| { - const atom = self.getAtom(atom_index); - const sym_loc = atom.getSymbolWithLoc(); - try self.addLocalToSymtab(sym_loc, &locals); - - var it = Atom.getInnerSymbolsIterator(self, atom_index); - while (it.next()) |inner_sym_loc| { - try self.addLocalToSymtab(inner_sym_loc, &locals); - } - } - } - - var exports = std.ArrayList(macho.nlist_64).init(gpa); - defer exports.deinit(); - - for (self.globals.items) |global| { - const sym = self.getSymbol(global); - if (sym.undf()) continue; // import, skip - if (sym.n_desc == N_DEAD) continue; - if (sym.n_desc == N_BOUNDARY) continue; - var out_sym = sym; - out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global)); - try exports.append(out_sym); - } - - var imports = std.ArrayList(macho.nlist_64).init(gpa); - defer imports.deinit(); - - var imports_table = std.AutoHashMap(SymbolWithLoc, u32).init(gpa); - - for (self.globals.items) |global| { - const sym = self.getSymbol(global); - if (sym.n_strx == 0) continue; // no name, skip - if (!sym.undf()) continue; // not an import, skip - if (sym.n_desc == N_DEAD) continue; - if (sym.n_desc == N_BOUNDARY) continue; - const new_index = @as(u32, @intCast(imports.items.len)); - var out_sym = sym; - out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global)); - try imports.append(out_sym); - try imports_table.putNoClobber(global, new_index); - } - - // We generate stabs last in order to ensure that the strtab always has debug info - // strings trailing - if (comp.config.debug_format != .strip) { - for (self.objects.items) |object| { - assert(self.d_sym == null); // TODO - try self.generateSymbolStabs(object, &locals); - } - } - - const nlocals = @as(u32, @intCast(locals.items.len)); - const nexports = @as(u32, @intCast(exports.items.len)); - const nimports = @as(u32, @intCast(imports.items.len)); - const nsyms = nlocals + nexports + nimports; - - const seg = self.getLinkeditSegmentPtr(); - const offset = seg.fileoff + seg.filesize; - assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64))); - const needed_size = nsyms * @sizeOf(macho.nlist_64); - seg.filesize = offset + needed_size - seg.fileoff; - assert(mem.isAlignedGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64))); - - var buffer = std.ArrayList(u8).init(gpa); - defer buffer.deinit(); - try buffer.ensureTotalCapacityPrecise(needed_size); - buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(locals.items)); - buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(exports.items)); - buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(imports.items)); - - log.debug("writing symtab from 0x{x} to 0x{x}", .{ offset, offset + needed_size }); - try self.base.file.?.pwriteAll(buffer.items, offset); - - self.symtab_cmd.symoff = @as(u32, @intCast(offset)); - self.symtab_cmd.nsyms = nsyms; - - return SymtabCtx{ - .nlocalsym = nlocals, - .nextdefsym = nexports, - .nundefsym = nimports, - .imports_table = imports_table, - }; -} - -// TODO this function currently skips generating symbol stabs in case errors are encountered in DWARF data. -// I think we should actually report those errors to the user and let them decide if they want to strip debug info -// in that case or not. -fn generateSymbolStabs( - self: *MachO, - object: Object, - locals: *std.ArrayList(macho.nlist_64), -) !void { - log.debug("generating stabs for '{s}'", .{object.name}); - - const gpa = self.base.comp.gpa; - var debug_info = object.parseDwarfInfo(); - - var lookup = DwarfInfo.AbbrevLookupTable.init(gpa); - defer lookup.deinit(); - try lookup.ensureUnusedCapacity(std.math.maxInt(u8)); - - // We assume there is only one CU. - var cu_it = debug_info.getCompileUnitIterator(); - const compile_unit = while (try cu_it.next()) |cu| { - const offset = math.cast(usize, cu.cuh.debug_abbrev_offset) orelse return error.Overflow; - try debug_info.genAbbrevLookupByKind(offset, &lookup); - break cu; - } else { - log.debug("no compile unit found in debug info in {s}; skipping", .{object.name}); - return; - }; - - var abbrev_it = compile_unit.getAbbrevEntryIterator(debug_info); - const maybe_cu_entry: ?DwarfInfo.AbbrevEntry = blk: { - while (abbrev_it.next(lookup) catch break :blk null) |entry| switch (entry.tag) { - dwarf.TAG.compile_unit => break :blk entry, - else => continue, - } else break :blk null; - }; - - const cu_entry = maybe_cu_entry orelse { - log.debug("missing DWARF_TAG_compile_unit tag in {s}; skipping", .{object.name}); - return; - }; - - var maybe_tu_name: ?[]const u8 = null; - var maybe_tu_comp_dir: ?[]const u8 = null; - var attr_it = cu_entry.getAttributeIterator(debug_info, compile_unit.cuh); - - blk: { - while (attr_it.next() catch break :blk) |attr| switch (attr.name) { - dwarf.AT.comp_dir => maybe_tu_comp_dir = attr.getString(debug_info, compile_unit.cuh) orelse continue, - dwarf.AT.name => maybe_tu_name = attr.getString(debug_info, compile_unit.cuh) orelse continue, - else => continue, - }; - } - - if (maybe_tu_name == null or maybe_tu_comp_dir == null) { - log.debug("missing DWARF_AT_comp_dir and DWARF_AT_name attributes {s}; skipping", .{object.name}); - return; - } - - const tu_name = maybe_tu_name.?; - const tu_comp_dir = maybe_tu_comp_dir.?; - - // Open scope - try locals.ensureUnusedCapacity(3); - locals.appendAssumeCapacity(.{ - .n_strx = try self.strtab.insert(gpa, tu_comp_dir), - .n_type = macho.N_SO, - .n_sect = 0, - .n_desc = 0, - .n_value = 0, - }); - locals.appendAssumeCapacity(.{ - .n_strx = try self.strtab.insert(gpa, tu_name), - .n_type = macho.N_SO, - .n_sect = 0, - .n_desc = 0, - .n_value = 0, - }); - locals.appendAssumeCapacity(.{ - .n_strx = try self.strtab.insert(gpa, object.name), - .n_type = macho.N_OSO, - .n_sect = 0, - .n_desc = 1, - .n_value = object.mtime, - }); - - var stabs_buf: [4]macho.nlist_64 = undefined; - - var name_lookup: ?DwarfInfo.SubprogramLookupByName = if (object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS == 0) blk: { - var name_lookup = DwarfInfo.SubprogramLookupByName.init(gpa); - errdefer name_lookup.deinit(); - try name_lookup.ensureUnusedCapacity(@as(u32, @intCast(object.atoms.items.len))); - debug_info.genSubprogramLookupByName(compile_unit, lookup, &name_lookup) catch |err| switch (err) { - error.UnhandledDwFormValue => {}, // TODO I don't like the fact we constantly re-iterate and hit this; we should validate once a priori - else => |e| return e, - }; - break :blk name_lookup; - } else null; - defer if (name_lookup) |*nl| nl.deinit(); - - for (object.atoms.items) |atom_index| { - const atom = self.getAtom(atom_index); - const stabs = try self.generateSymbolStabsForSymbol( - atom_index, - atom.getSymbolWithLoc(), - name_lookup, - &stabs_buf, - ); - try locals.appendSlice(stabs); - - var it = Atom.getInnerSymbolsIterator(self, atom_index); - while (it.next()) |sym_loc| { - const contained_stabs = try self.generateSymbolStabsForSymbol( - atom_index, - sym_loc, - name_lookup, - &stabs_buf, - ); - try locals.appendSlice(contained_stabs); - } - } - - // Close scope - try locals.append(.{ - .n_strx = 0, - .n_type = macho.N_SO, - .n_sect = 0, - .n_desc = 0, - .n_value = 0, - }); -} - -fn generateSymbolStabsForSymbol( - self: *MachO, - atom_index: Atom.Index, - sym_loc: SymbolWithLoc, - lookup: ?DwarfInfo.SubprogramLookupByName, - buf: *[4]macho.nlist_64, -) ![]const macho.nlist_64 { - const gpa = self.base.comp.gpa; - const object = self.objects.items[sym_loc.getFile().?]; - const sym = self.getSymbol(sym_loc); - const sym_name = self.getSymbolName(sym_loc); - const header = self.sections.items(.header)[sym.n_sect - 1]; - - if (sym.n_strx == 0) return buf[0..0]; - if (self.symbolIsTemp(sym_loc)) return buf[0..0]; - - if (!header.isCode()) { - // Since we are not dealing with machine code, it's either a global or a static depending - // on the linkage scope. - if (sym.sect() and sym.ext()) { - // Global gets an N_GSYM stab type. - buf[0] = .{ - .n_strx = try self.strtab.insert(gpa, sym_name), - .n_type = macho.N_GSYM, - .n_sect = sym.n_sect, - .n_desc = 0, - .n_value = 0, - }; - } else { - // Local static gets an N_STSYM stab type. - buf[0] = .{ - .n_strx = try self.strtab.insert(gpa, sym_name), - .n_type = macho.N_STSYM, - .n_sect = sym.n_sect, - .n_desc = 0, - .n_value = sym.n_value, - }; - } - return buf[0..1]; - } - - const size: u64 = size: { - if (object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0) { - break :size self.getAtom(atom_index).size; - } - - // Since we don't have subsections to work with, we need to infer the size of each function - // the slow way by scanning the debug info for matching symbol names and extracting - // the symbol's DWARF_AT_low_pc and DWARF_AT_high_pc values. - const source_sym = object.getSourceSymbol(sym_loc.sym_index) orelse return buf[0..0]; - const subprogram = lookup.?.get(sym_name[1..]) orelse return buf[0..0]; - - if (subprogram.addr <= source_sym.n_value and source_sym.n_value < subprogram.addr + subprogram.size) { - break :size subprogram.size; - } else { - log.debug("no stab found for {s}", .{sym_name}); - return buf[0..0]; - } - }; - - buf[0] = .{ - .n_strx = 0, - .n_type = macho.N_BNSYM, - .n_sect = sym.n_sect, - .n_desc = 0, - .n_value = sym.n_value, - }; - buf[1] = .{ - .n_strx = try self.strtab.insert(gpa, sym_name), - .n_type = macho.N_FUN, - .n_sect = sym.n_sect, - .n_desc = 0, - .n_value = sym.n_value, - }; - buf[2] = .{ - .n_strx = 0, - .n_type = macho.N_FUN, - .n_sect = 0, - .n_desc = 0, - .n_value = size, - }; - buf[3] = .{ - .n_strx = 0, - .n_type = macho.N_ENSYM, - .n_sect = sym.n_sect, - .n_desc = 0, - .n_value = size, - }; - - return buf; -} - -pub fn writeStrtab(self: *MachO) !void { - const gpa = self.base.comp.gpa; - const seg = self.getLinkeditSegmentPtr(); - const offset = seg.fileoff + seg.filesize; - assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64))); - const needed_size = self.strtab.buffer.items.len; - const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64)); - seg.filesize = offset + needed_size_aligned - seg.fileoff; - - log.debug("writing string table from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned }); - - const buffer = try gpa.alloc(u8, math.cast(usize, needed_size_aligned) orelse return error.Overflow); - defer gpa.free(buffer); - @memcpy(buffer[0..self.strtab.buffer.items.len], self.strtab.buffer.items); - @memset(buffer[self.strtab.buffer.items.len..], 0); - - try self.base.file.?.pwriteAll(buffer, offset); - - self.symtab_cmd.stroff = @as(u32, @intCast(offset)); - self.symtab_cmd.strsize = @as(u32, @intCast(needed_size_aligned)); -} - -const SymtabCtx = struct { - nlocalsym: u32, - nextdefsym: u32, - nundefsym: u32, - imports_table: std.AutoHashMap(SymbolWithLoc, u32), -}; - -pub fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void { - const gpa = self.base.comp.gpa; - const nstubs = @as(u32, @intCast(self.stub_table.lookup.count())); - const ngot_entries = @as(u32, @intCast(self.got_table.lookup.count())); - const nindirectsyms = nstubs * 2 + ngot_entries; - const iextdefsym = ctx.nlocalsym; - const iundefsym = iextdefsym + ctx.nextdefsym; - - const seg = self.getLinkeditSegmentPtr(); - const offset = seg.fileoff + seg.filesize; - assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64))); - const needed_size = nindirectsyms * @sizeOf(u32); - const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64)); - seg.filesize = offset + needed_size_aligned - seg.fileoff; - - log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned }); - - var buf = std.ArrayList(u8).init(gpa); - defer buf.deinit(); - try buf.ensureTotalCapacity(math.cast(usize, needed_size_aligned) orelse return error.Overflow); - const writer = buf.writer(); - - if (self.stubs_section_index) |sect_id| { - const stubs_header = &self.sections.items(.header)[sect_id]; - stubs_header.reserved1 = 0; - for (self.stub_table.entries.items) |entry| { - if (!self.stub_table.lookup.contains(entry)) continue; - const target_sym = self.getSymbol(entry); - assert(target_sym.undf()); - try writer.writeInt(u32, iundefsym + ctx.imports_table.get(entry).?, .little); - } - } - - if (self.got_section_index) |sect_id| { - const got = &self.sections.items(.header)[sect_id]; - got.reserved1 = nstubs; - for (self.got_table.entries.items) |entry| { - if (!self.got_table.lookup.contains(entry)) continue; - const target_sym = self.getSymbol(entry); - if (target_sym.undf()) { - try writer.writeInt(u32, iundefsym + ctx.imports_table.get(entry).?, .little); - } else { - try writer.writeInt(u32, macho.INDIRECT_SYMBOL_LOCAL, .little); - } - } - } - - if (self.la_symbol_ptr_section_index) |sect_id| { - const la_symbol_ptr = &self.sections.items(.header)[sect_id]; - la_symbol_ptr.reserved1 = nstubs + ngot_entries; - for (self.stub_table.entries.items) |entry| { - if (!self.stub_table.lookup.contains(entry)) continue; - const target_sym = self.getSymbol(entry); - assert(target_sym.undf()); - try writer.writeInt(u32, iundefsym + ctx.imports_table.get(entry).?, .little); - } - } - - const padding = math.cast(usize, needed_size_aligned - needed_size) orelse return error.Overflow; - if (padding > 0) { - buf.appendNTimesAssumeCapacity(0, padding); - } - - assert(buf.items.len == needed_size_aligned); - try self.base.file.?.pwriteAll(buf.items, offset); - - self.dysymtab_cmd.nlocalsym = ctx.nlocalsym; - self.dysymtab_cmd.iextdefsym = iextdefsym; - self.dysymtab_cmd.nextdefsym = ctx.nextdefsym; - self.dysymtab_cmd.iundefsym = iundefsym; - self.dysymtab_cmd.nundefsym = ctx.nundefsym; - self.dysymtab_cmd.indirectsymoff = @as(u32, @intCast(offset)); - self.dysymtab_cmd.nindirectsyms = nindirectsyms; -} - -pub fn writeUuid(self: *MachO, comp: *const Compilation, uuid_cmd_offset: u32, has_codesig: bool) !void { - const file_size = if (!has_codesig) blk: { - const seg = self.getLinkeditSegmentPtr(); - break :blk seg.fileoff + seg.filesize; - } else self.codesig_cmd.dataoff; - try calcUuid(comp, self.base.file.?, file_size, &self.uuid_cmd.uuid); - const offset = uuid_cmd_offset + @sizeOf(macho.load_command); - try self.base.file.?.pwriteAll(&self.uuid_cmd.uuid, offset); -} - -pub fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void { - const target = self.base.comp.root_mod.resolved_target.result; - const seg = self.getLinkeditSegmentPtr(); - // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file - // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271 - const offset = mem.alignForward(u64, seg.fileoff + seg.filesize, 16); - const needed_size = code_sig.estimateSize(offset); - seg.filesize = offset + needed_size - seg.fileoff; - seg.vmsize = mem.alignForward(u64, seg.filesize, getPageSize(target.cpu.arch)); - log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ offset, offset + needed_size }); - // Pad out the space. We need to do this to calculate valid hashes for everything in the file - // except for code signature data. - try self.base.file.?.pwriteAll(&[_]u8{0}, offset + needed_size - 1); - - self.codesig_cmd.dataoff = @as(u32, @intCast(offset)); - self.codesig_cmd.datasize = @as(u32, @intCast(needed_size)); -} - -pub fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *CodeSignature) !void { - const output_mode = self.base.comp.config.output_mode; - const seg_id = self.header_segment_cmd_index.?; - const seg = self.segments.items[seg_id]; - const offset = self.codesig_cmd.dataoff; - - const gpa = self.base.comp.gpa; - var buffer = std.ArrayList(u8).init(gpa); - defer buffer.deinit(); - try buffer.ensureTotalCapacityPrecise(code_sig.size()); - try code_sig.writeAdhocSignature(comp, .{ - .file = self.base.file.?, - .exec_seg_base = seg.fileoff, - .exec_seg_limit = seg.filesize, - .file_size = offset, - .output_mode = output_mode, - }, buffer.writer()); - assert(buffer.items.len == code_sig.size()); - - log.debug("writing code signature from 0x{x} to 0x{x}", .{ - offset, - offset + buffer.items.len, - }); - - try self.base.file.?.pwriteAll(buffer.items, offset); -} - -/// Writes Mach-O file header. -pub fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void { - const output_mode = self.base.comp.config.output_mode; - - var header: macho.mach_header_64 = .{}; - header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL; - - const target = self.base.comp.root_mod.resolved_target.result; - switch (target.cpu.arch) { - .aarch64 => { - header.cputype = macho.CPU_TYPE_ARM64; - header.cpusubtype = macho.CPU_SUBTYPE_ARM_ALL; - }, - .x86_64 => { - header.cputype = macho.CPU_TYPE_X86_64; - header.cpusubtype = macho.CPU_SUBTYPE_X86_64_ALL; - }, - else => unreachable, - } - - switch (output_mode) { - .Exe => { - header.filetype = macho.MH_EXECUTE; - }, - .Lib => { - // By this point, it can only be a dylib. - header.filetype = macho.MH_DYLIB; - header.flags |= macho.MH_NO_REEXPORTED_DYLIBS; - }, - else => unreachable, - } - - if (self.thread_vars_section_index) |sect_id| { - header.flags |= macho.MH_HAS_TLV_DESCRIPTORS; - if (self.sections.items(.header)[sect_id].size > 0) { - header.flags |= macho.MH_HAS_TLV_DESCRIPTORS; - } - } - - header.ncmds = ncmds; - header.sizeofcmds = sizeofcmds; - - log.debug("writing Mach-O header {}", .{header}); - - try self.base.file.?.pwriteAll(mem.asBytes(&header), 0); + @panic("TODO getGlobalSymbol"); } pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { @@ -4799,370 +707,12 @@ fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 { return start; } -pub fn allocatedVirtualSize(self: *MachO, start: u64) u64 { - if (start == 0) - return 0; - var min_pos: u64 = std.math.maxInt(u64); - for (self.sections.items(.segment_index)) |seg_id| { - const segment = self.segments.items[seg_id]; - if (segment.vmaddr <= start) continue; - if (segment.vmaddr < min_pos) min_pos = segment.vmaddr; - } - return min_pos - start; -} - -pub fn ptraceAttach(self: *MachO, pid: std.os.pid_t) !void { - if (!is_hot_update_compatible) return; - - const mach_task = try std.os.darwin.machTaskForPid(pid); - log.debug("Mach task for pid {d}: {any}", .{ pid, mach_task }); - self.hot_state.mach_task = mach_task; - - // TODO start exception handler in another thread - - // TODO enable ones we register for exceptions - // try std.os.ptrace(std.os.darwin.PT.ATTACHEXC, pid, 0, 0); -} - -pub fn ptraceDetach(self: *MachO, pid: std.os.pid_t) !void { - if (!is_hot_update_compatible) return; - - _ = pid; - - // TODO stop exception handler - - // TODO see comment in ptraceAttach - // try std.os.ptrace(std.os.darwin.PT.DETACH, pid, 0, 0); - - self.hot_state.mach_task = null; -} - -pub fn addUndefined(self: *MachO, name: []const u8, flags: RelocFlags) !u32 { - const gpa = self.base.comp.gpa; - - const gop = try self.getOrPutGlobalPtr(name); - const global_index = self.getGlobalIndex(name).?; - - if (gop.found_existing) { - try self.updateRelocActions(global_index, flags); - return global_index; - } - - const sym_index = try self.allocateSymbol(); - const sym_loc = SymbolWithLoc{ .sym_index = sym_index }; - gop.value_ptr.* = sym_loc; - - const sym = self.getSymbolPtr(sym_loc); - sym.n_strx = try self.strtab.insert(gpa, name); - sym.n_type = macho.N_EXT | macho.N_UNDF; - - try self.unresolved.putNoClobber(gpa, global_index, {}); - try self.updateRelocActions(global_index, flags); - - return global_index; -} - -fn updateRelocActions(self: *MachO, global_index: u32, flags: RelocFlags) !void { - const gpa = self.base.comp.gpa; - const act_gop = try self.actions.getOrPut(gpa, global_index); - if (!act_gop.found_existing) { - act_gop.value_ptr.* = .{}; - } - act_gop.value_ptr.add_got = act_gop.value_ptr.add_got or flags.add_got; - act_gop.value_ptr.add_stub = act_gop.value_ptr.add_stub or flags.add_stub; -} - pub fn makeStaticString(bytes: []const u8) [16]u8 { var buf = [_]u8{0} ** 16; @memcpy(buf[0..bytes.len], bytes); return buf; } -pub fn getSegmentByName(self: MachO, segname: []const u8) ?u8 { - for (self.segments.items, 0..) |seg, i| { - if (mem.eql(u8, segname, seg.segName())) return @as(u8, @intCast(i)); - } else return null; -} - -pub fn getSegment(self: MachO, sect_id: u8) macho.segment_command_64 { - const index = self.sections.items(.segment_index)[sect_id]; - return self.segments.items[index]; -} - -pub fn getSegmentPtr(self: *MachO, sect_id: u8) *macho.segment_command_64 { - const index = self.sections.items(.segment_index)[sect_id]; - return &self.segments.items[index]; -} - -pub fn getLinkeditSegmentPtr(self: *MachO) *macho.segment_command_64 { - const index = self.linkedit_segment_cmd_index.?; - return &self.segments.items[index]; -} - -pub fn getSectionByName(self: MachO, segname: []const u8, sectname: []const u8) ?u8 { - // TODO investigate caching with a hashmap - for (self.sections.items(.header), 0..) |header, i| { - if (mem.eql(u8, header.segName(), segname) and mem.eql(u8, header.sectName(), sectname)) - return @as(u8, @intCast(i)); - } else return null; -} - -pub fn getSectionIndexes(self: MachO, segment_index: u8) struct { start: u8, end: u8 } { - var start: u8 = 0; - const nsects = for (self.segments.items, 0..) |seg, i| { - if (i == segment_index) break @as(u8, @intCast(seg.nsects)); - start += @as(u8, @intCast(seg.nsects)); - } else 0; - return .{ .start = start, .end = start + nsects }; -} - -pub fn symbolIsTemp(self: *MachO, sym_with_loc: SymbolWithLoc) bool { - const sym = self.getSymbol(sym_with_loc); - if (!sym.sect()) return false; - if (sym.ext()) return false; - const sym_name = self.getSymbolName(sym_with_loc); - return mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L"); -} - -/// Returns pointer-to-symbol described by `sym_with_loc` descriptor. -pub fn getSymbolPtr(self: *MachO, sym_with_loc: SymbolWithLoc) *macho.nlist_64 { - if (sym_with_loc.getFile()) |file| { - const object = &self.objects.items[file]; - return &object.symtab[sym_with_loc.sym_index]; - } else { - return &self.locals.items[sym_with_loc.sym_index]; - } -} - -/// Returns symbol described by `sym_with_loc` descriptor. -pub fn getSymbol(self: *const MachO, sym_with_loc: SymbolWithLoc) macho.nlist_64 { - if (sym_with_loc.getFile()) |file| { - const object = &self.objects.items[file]; - return object.symtab[sym_with_loc.sym_index]; - } else { - return self.locals.items[sym_with_loc.sym_index]; - } -} - -/// Returns name of the symbol described by `sym_with_loc` descriptor. -pub fn getSymbolName(self: *const MachO, sym_with_loc: SymbolWithLoc) []const u8 { - if (sym_with_loc.getFile()) |file| { - const object = self.objects.items[file]; - return object.getSymbolName(sym_with_loc.sym_index); - } else { - const sym = self.locals.items[sym_with_loc.sym_index]; - return self.strtab.get(sym.n_strx).?; - } -} - -const BoundarySymbolKind = enum { - start, - stop, -}; - -const SectionBoundarySymbol = struct { - kind: BoundarySymbolKind, - segname: []const u8, - sectname: []const u8, -}; - -pub fn getSectionBoundarySymbol(self: *const MachO, sym_with_loc: SymbolWithLoc) ?SectionBoundarySymbol { - const sym_name = self.getSymbolName(sym_with_loc); - if (mem.startsWith(u8, sym_name, "section$")) { - const trailing = sym_name["section$".len..]; - const kind: BoundarySymbolKind = kind: { - if (mem.startsWith(u8, trailing, "start$")) break :kind .start; - if (mem.startsWith(u8, trailing, "stop$")) break :kind .stop; - return null; - }; - const names = trailing[@tagName(kind).len + 1 ..]; - const sep_idx = mem.indexOf(u8, names, "$") orelse return null; - const segname = names[0..sep_idx]; - const sectname = names[sep_idx + 1 ..]; - return .{ .kind = kind, .segname = segname, .sectname = sectname }; - } - return null; -} - -const SegmentBoundarySymbol = struct { - kind: BoundarySymbolKind, - segname: []const u8, -}; - -pub fn getSegmentBoundarySymbol(self: *const MachO, sym_with_loc: SymbolWithLoc) ?SegmentBoundarySymbol { - const sym_name = self.getSymbolName(sym_with_loc); - if (mem.startsWith(u8, sym_name, "segment$")) { - const trailing = sym_name["segment$".len..]; - const kind: BoundarySymbolKind = kind: { - if (mem.startsWith(u8, trailing, "start$")) break :kind .start; - if (mem.startsWith(u8, trailing, "stop$")) break :kind .stop; - return null; - }; - const segname = trailing[@tagName(kind).len + 1 ..]; - return .{ .kind = kind, .segname = segname }; - } - return null; -} - -/// Returns pointer to the global entry for `name` if one exists. -pub fn getGlobalPtr(self: *MachO, name: []const u8) ?*SymbolWithLoc { - const global_index = self.resolver.get(name) orelse return null; - return &self.globals.items[global_index]; -} - -/// Returns the global entry for `name` if one exists. -pub fn getGlobal(self: *const MachO, name: []const u8) ?SymbolWithLoc { - const global_index = self.resolver.get(name) orelse return null; - return self.globals.items[global_index]; -} - -/// Returns the index of the global entry for `name` if one exists. -pub fn getGlobalIndex(self: *const MachO, name: []const u8) ?u32 { - return self.resolver.get(name); -} - -/// Returns global entry at `index`. -pub fn getGlobalByIndex(self: *const MachO, index: u32) SymbolWithLoc { - assert(index < self.globals.items.len); - return self.globals.items[index]; -} - -const GetOrPutGlobalPtrResult = struct { - found_existing: bool, - value_ptr: *SymbolWithLoc, -}; - -/// Used only for disambiguating local from global at relocation level. -/// TODO this must go away. -pub const global_symbol_bit: u32 = 0x80000000; -pub const global_symbol_mask: u32 = 0x7fffffff; - -/// Return pointer to the global entry for `name` if one exists. -/// Puts a new global entry for `name` if one doesn't exist, and -/// returns a pointer to it. -pub fn getOrPutGlobalPtr(self: *MachO, name: []const u8) !GetOrPutGlobalPtrResult { - if (self.getGlobalPtr(name)) |ptr| { - return GetOrPutGlobalPtrResult{ .found_existing = true, .value_ptr = ptr }; - } - const gpa = self.base.comp.gpa; - const global_index = try self.allocateGlobal(); - const global_name = try gpa.dupe(u8, name); - _ = try self.resolver.put(gpa, global_name, global_index); - const ptr = &self.globals.items[global_index]; - return GetOrPutGlobalPtrResult{ .found_existing = false, .value_ptr = ptr }; -} - -pub fn getAtom(self: *MachO, atom_index: Atom.Index) Atom { - assert(atom_index < self.atoms.items.len); - return self.atoms.items[atom_index]; -} - -pub fn getAtomPtr(self: *MachO, atom_index: Atom.Index) *Atom { - assert(atom_index < self.atoms.items.len); - return &self.atoms.items[atom_index]; -} - -/// Returns atom if there is an atom referenced by the symbol described by `sym_with_loc` descriptor. -/// Returns null on failure. -pub fn getAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?Atom.Index { - assert(sym_with_loc.getFile() == null); - return self.atom_by_index_table.get(sym_with_loc.sym_index); -} - -pub fn getGotEntryAddress(self: *MachO, sym_with_loc: SymbolWithLoc) ?u64 { - const index = self.got_table.lookup.get(sym_with_loc) orelse return null; - const header = self.sections.items(.header)[self.got_section_index.?]; - return header.addr + @sizeOf(u64) * index; -} - -pub fn getTlvPtrEntryAddress(self: *MachO, sym_with_loc: SymbolWithLoc) ?u64 { - const index = self.tlv_ptr_table.lookup.get(sym_with_loc) orelse return null; - const header = self.sections.items(.header)[self.tlv_ptr_section_index.?]; - return header.addr + @sizeOf(u64) * index; -} - -pub fn getStubsEntryAddress(self: *MachO, sym_with_loc: SymbolWithLoc) ?u64 { - const target = self.base.comp.root_mod.resolved_target.result; - const index = self.stub_table.lookup.get(sym_with_loc) orelse return null; - const header = self.sections.items(.header)[self.stubs_section_index.?]; - return header.addr + stubs.stubSize(target.cpu.arch) * index; -} - -/// Returns symbol location corresponding to the set entrypoint if any. -/// Asserts output mode is executable. -pub fn getEntryPoint(self: MachO) ?SymbolWithLoc { - const entry_name = self.entry_name orelse return null; - const global = self.getGlobal(entry_name) orelse return null; - return global; -} - -pub fn getDebugSymbols(self: *MachO) ?*DebugSymbols { - if (self.d_sym == null) return null; - return &self.d_sym.?; -} - -pub inline fn getPageSize(cpu_arch: std.Target.Cpu.Arch) u16 { - return switch (cpu_arch) { - .aarch64 => 0x4000, - .x86_64 => 0x1000, - else => unreachable, - }; -} - -pub fn requiresCodeSignature(m: *MachO) bool { - if (m.entitlements) |_| return true; - const comp = m.base.comp; - const target = comp.root_mod.resolved_target.result; - const cpu_arch = target.cpu.arch; - const os_tag = target.os.tag; - const abi = target.abi; - if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) return true; - return false; -} - -pub fn getSegmentPrecedence(segname: []const u8) u4 { - if (mem.eql(u8, segname, "__PAGEZERO")) return 0x0; - if (mem.eql(u8, segname, "__TEXT")) return 0x1; - if (mem.eql(u8, segname, "__DATA_CONST")) return 0x2; - if (mem.eql(u8, segname, "__DATA")) return 0x3; - if (mem.eql(u8, segname, "__LINKEDIT")) return 0x5; - return 0x4; -} - -pub fn getSegmentMemoryProtection(segname: []const u8) macho.vm_prot_t { - if (mem.eql(u8, segname, "__PAGEZERO")) return macho.PROT.NONE; - if (mem.eql(u8, segname, "__TEXT")) return macho.PROT.READ | macho.PROT.EXEC; - if (mem.eql(u8, segname, "__LINKEDIT")) return macho.PROT.READ; - return macho.PROT.READ | macho.PROT.WRITE; -} - -pub fn getSectionPrecedence(header: macho.section_64) u8 { - const segment_precedence: u4 = getSegmentPrecedence(header.segName()); - const section_precedence: u4 = blk: { - if (header.isCode()) { - if (mem.eql(u8, "__text", header.sectName())) break :blk 0x0; - if (header.type() == macho.S_SYMBOL_STUBS) break :blk 0x1; - break :blk 0x2; - } - switch (header.type()) { - macho.S_NON_LAZY_SYMBOL_POINTERS, - macho.S_LAZY_SYMBOL_POINTERS, - => break :blk 0x0, - macho.S_MOD_INIT_FUNC_POINTERS => break :blk 0x1, - macho.S_MOD_TERM_FUNC_POINTERS => break :blk 0x2, - macho.S_ZEROFILL => break :blk 0xf, - macho.S_THREAD_LOCAL_REGULAR => break :blk 0xd, - macho.S_THREAD_LOCAL_ZEROFILL => break :blk 0xe, - else => { - if (mem.eql(u8, "__unwind_info", header.sectName())) break :blk 0xe; - if (mem.eql(u8, "__eh_frame", header.sectName())) break :blk 0xf; - break :blk 0x3; - }, - } - }; - return (@as(u8, @intCast(segment_precedence)) << 4) + section_precedence; -} - pub const ParseErrorCtx = struct { arena_allocator: std.heap.ArenaAllocator, detected_dylib_id: struct { @@ -5354,308 +904,105 @@ pub fn reportUndefined(self: *MachO) error{OutOfMemory}!void { } } -fn reportSymbolCollision( - self: *MachO, - first: SymbolWithLoc, - other: SymbolWithLoc, -) error{OutOfMemory}!void { - const comp = self.base.comp; - const gpa = comp.gpa; - try comp.link_errors.ensureUnusedCapacity(gpa, 1); +// fn reportSymbolCollision( +// self: *MachO, +// first: SymbolWithLoc, +// other: SymbolWithLoc, +// ) error{OutOfMemory}!void { +// const comp = self.base.comp; +// const gpa = comp.gpa; +// try comp.link_errors.ensureUnusedCapacity(gpa, 1); - var notes = try std.ArrayList(File.ErrorMsg).initCapacity(gpa, 2); - defer notes.deinit(); +// var notes = try std.ArrayList(File.ErrorMsg).initCapacity(gpa, 2); +// defer notes.deinit(); - if (first.getFile()) |file| { - const note = try std.fmt.allocPrint(gpa, "first definition in {s}", .{ - self.objects.items[file].name, - }); - notes.appendAssumeCapacity(.{ .msg = note }); - } - if (other.getFile()) |file| { - const note = try std.fmt.allocPrint(gpa, "next definition in {s}", .{ - self.objects.items[file].name, - }); - notes.appendAssumeCapacity(.{ .msg = note }); - } +// if (first.getFile()) |file| { +// const note = try std.fmt.allocPrint(gpa, "first definition in {s}", .{ +// self.objects.items[file].name, +// }); +// notes.appendAssumeCapacity(.{ .msg = note }); +// } +// if (other.getFile()) |file| { +// const note = try std.fmt.allocPrint(gpa, "next definition in {s}", .{ +// self.objects.items[file].name, +// }); +// notes.appendAssumeCapacity(.{ .msg = note }); +// } - var err_msg = File.ErrorMsg{ .msg = try std.fmt.allocPrint(gpa, "symbol {s} defined multiple times", .{ - self.getSymbolName(first), - }) }; - err_msg.notes = try notes.toOwnedSlice(); +// var err_msg = File.ErrorMsg{ .msg = try std.fmt.allocPrint(gpa, "symbol {s} defined multiple times", .{ +// self.getSymbolName(first), +// }) }; +// err_msg.notes = try notes.toOwnedSlice(); - comp.link_errors.appendAssumeCapacity(err_msg); +// comp.link_errors.appendAssumeCapacity(err_msg); +// } + +// fn reportUnhandledSymbolType(self: *MachO, sym_with_loc: SymbolWithLoc) error{OutOfMemory}!void { +// const comp = self.base.comp; +// const gpa = comp.gpa; +// try comp.link_errors.ensureUnusedCapacity(gpa, 1); + +// const notes = try gpa.alloc(File.ErrorMsg, 1); +// errdefer gpa.free(notes); + +// const file = sym_with_loc.getFile().?; +// notes[0] = .{ .msg = try std.fmt.allocPrint(gpa, "defined in {s}", .{self.objects.items[file].name}) }; + +// const sym = self.getSymbol(sym_with_loc); +// const sym_type = if (sym.stab()) +// "stab" +// else if (sym.indr()) +// "indirect" +// else if (sym.abs()) +// "absolute" +// else +// unreachable; + +// comp.link_errors.appendAssumeCapacity(.{ +// .msg = try std.fmt.allocPrint(gpa, "unhandled symbol type: '{s}' has type {s}", .{ +// self.getSymbolName(sym_with_loc), +// sym_type, +// }), +// .notes = notes, +// }); +// } + +pub fn getDebugSymbols(self: *MachO) ?*DebugSymbols { + if (self.d_sym) |*ds| { + return ds; + } else return null; } -fn reportUnhandledSymbolType(self: *MachO, sym_with_loc: SymbolWithLoc) error{OutOfMemory}!void { - const comp = self.base.comp; - const gpa = comp.gpa; - try comp.link_errors.ensureUnusedCapacity(gpa, 1); +pub fn ptraceAttach(self: *MachO, pid: std.os.pid_t) !void { + if (!is_hot_update_compatible) return; - const notes = try gpa.alloc(File.ErrorMsg, 1); - errdefer gpa.free(notes); + const mach_task = try std.os.darwin.machTaskForPid(pid); + log.debug("Mach task for pid {d}: {any}", .{ pid, mach_task }); + self.hot_state.mach_task = mach_task; - const file = sym_with_loc.getFile().?; - notes[0] = .{ .msg = try std.fmt.allocPrint(gpa, "defined in {s}", .{self.objects.items[file].name}) }; + // TODO start exception handler in another thread - const sym = self.getSymbol(sym_with_loc); - const sym_type = if (sym.stab()) - "stab" - else if (sym.indr()) - "indirect" - else if (sym.abs()) - "absolute" - else - unreachable; - - comp.link_errors.appendAssumeCapacity(.{ - .msg = try std.fmt.allocPrint(gpa, "unhandled symbol type: '{s}' has type {s}", .{ - self.getSymbolName(sym_with_loc), - sym_type, - }), - .notes = notes, - }); + // TODO enable ones we register for exceptions + // try std.os.ptrace(std.os.darwin.PT.ATTACHEXC, pid, 0, 0); } -/// Binary search -pub fn bsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize { - if (!@hasDecl(@TypeOf(predicate), "predicate")) - @compileError("Predicate is required to define fn predicate(@This(), T) bool"); +pub fn ptraceDetach(self: *MachO, pid: std.os.pid_t) !void { + if (!is_hot_update_compatible) return; - var min: usize = 0; - var max: usize = haystack.len; - while (min < max) { - const index = (min + max) / 2; - const curr = haystack[index]; - if (predicate.predicate(curr)) { - min = index + 1; - } else { - max = index; - } - } - return min; + _ = pid; + + // TODO stop exception handler + + // TODO see comment in ptraceAttach + // try std.os.ptrace(std.os.darwin.PT.DETACH, pid, 0, 0); + + self.hot_state.mach_task = null; } -/// Linear search -pub fn lsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize { - if (!@hasDecl(@TypeOf(predicate), "predicate")) - @compileError("Predicate is required to define fn predicate(@This(), T) bool"); - - var i: usize = 0; - while (i < haystack.len) : (i += 1) { - if (predicate.predicate(haystack[i])) break; - } - return i; -} - -pub fn logSegments(self: *MachO) void { - log.debug("segments:", .{}); - for (self.segments.items, 0..) |segment, i| { - log.debug(" segment({d}): {s} @{x} ({x}), sizeof({x})", .{ - i, - segment.segName(), - segment.fileoff, - segment.vmaddr, - segment.vmsize, - }); - } -} - -pub fn logSections(self: *MachO) void { - log.debug("sections:", .{}); - for (self.sections.items(.header), 0..) |header, i| { - log.debug(" sect({d}): {s},{s} @{x} ({x}), sizeof({x})", .{ - i + 1, - header.segName(), - header.sectName(), - header.offset, - header.addr, - header.size, - }); - } -} - -fn logSymAttributes(sym: macho.nlist_64, buf: []u8) []const u8 { - if (sym.sect()) { - buf[0] = 's'; - } - if (sym.ext()) { - if (sym.weakDef() or sym.pext()) { - buf[1] = 'w'; - } else { - buf[1] = 'e'; - } - } - if (sym.tentative()) { - buf[2] = 't'; - } - if (sym.undf()) { - buf[3] = 'u'; - } - return buf[0..]; -} - -pub fn logSymtab(self: *MachO) void { - var buf: [4]u8 = undefined; - - const scoped_log = std.log.scoped(.symtab); - - scoped_log.debug("locals:", .{}); - for (self.objects.items, 0..) |object, id| { - scoped_log.debug(" object({d}): {s}", .{ id, object.name }); - if (object.in_symtab == null) continue; - for (object.symtab, 0..) |sym, sym_id| { - @memset(&buf, '_'); - scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s}", .{ - sym_id, - object.getSymbolName(@as(u32, @intCast(sym_id))), - sym.n_value, - sym.n_sect, - logSymAttributes(sym, &buf), - }); - } - } - scoped_log.debug(" object(-1)", .{}); - for (self.locals.items, 0..) |sym, sym_id| { - if (sym.undf()) continue; - scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s}", .{ - sym_id, - self.strtab.get(sym.n_strx).?, - sym.n_value, - sym.n_sect, - logSymAttributes(sym, &buf), - }); - } - - scoped_log.debug("exports:", .{}); - for (self.globals.items, 0..) |global, i| { - const sym = self.getSymbol(global); - if (sym.undf()) continue; - if (sym.n_desc == N_DEAD) continue; - if (sym.n_desc == N_BOUNDARY) continue; - scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s} (def in object({?}))", .{ - i, - self.getSymbolName(global), - sym.n_value, - sym.n_sect, - logSymAttributes(sym, &buf), - global.file, - }); - } - - scoped_log.debug("imports:", .{}); - for (self.globals.items, 0..) |global, i| { - const sym = self.getSymbol(global); - if (!sym.undf()) continue; - if (sym.n_desc == N_DEAD) continue; - if (sym.n_desc == N_BOUNDARY) continue; - const ord = @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER); - scoped_log.debug(" %{d}: {s} @{x} in ord({d}), {s}", .{ - i, - self.getSymbolName(global), - sym.n_value, - ord, - logSymAttributes(sym, &buf), - }); - } - - scoped_log.debug("GOT entries:", .{}); - scoped_log.debug("{}", .{self.got_table}); - - scoped_log.debug("TLV pointers:", .{}); - scoped_log.debug("{}", .{self.tlv_ptr_table}); - - scoped_log.debug("stubs entries:", .{}); - scoped_log.debug("{}", .{self.stub_table}); - - scoped_log.debug("thunks:", .{}); - for (self.thunks.items, 0..) |thunk, i| { - scoped_log.debug(" thunk({d})", .{i}); - const slice = thunk.targets.slice(); - for (slice.items(.tag), slice.items(.target), 0..) |tag, target, j| { - const atom_index = @as(u32, @intCast(thunk.getStartAtomIndex() + j)); - const atom = self.getAtom(atom_index); - const atom_sym = self.getSymbol(atom.getSymbolWithLoc()); - const target_addr = switch (tag) { - .stub => self.getStubsEntryAddress(target).?, - .atom => self.getSymbol(target).n_value, - }; - scoped_log.debug(" {d}@{x} => {s}({s}@{x})", .{ - j, - atom_sym.n_value, - @tagName(tag), - self.getSymbolName(target), - target_addr, - }); - } - } -} - -pub fn logAtoms(self: *MachO) void { - log.debug("atoms:", .{}); - const slice = self.sections.slice(); - for (slice.items(.first_atom_index), 0..) |first_atom_index, sect_id| { - var atom_index = first_atom_index orelse continue; - const header = slice.items(.header)[sect_id]; - - log.debug("{s},{s}", .{ header.segName(), header.sectName() }); - - while (true) { - const atom = self.getAtom(atom_index); - self.logAtom(atom_index, log); - - if (atom.next_index) |next_index| { - atom_index = next_index; - } else break; - } - } -} - -pub fn logAtom(self: *MachO, atom_index: Atom.Index, logger: anytype) void { - if (!build_options.enable_logging) return; - - const atom = self.getAtom(atom_index); - const sym = self.getSymbol(atom.getSymbolWithLoc()); - const sym_name = self.getSymbolName(atom.getSymbolWithLoc()); - logger.debug(" ATOM({d}, %{d}, '{s}') @ {x} (sizeof({x}), alignof({x})) in object({?}) in sect({d})", .{ - atom_index, - atom.sym_index, - sym_name, - sym.n_value, - atom.size, - atom.alignment, - atom.getFile(), - sym.n_sect, - }); - - if (atom.getFile() != null) { - var it = Atom.getInnerSymbolsIterator(self, atom_index); - while (it.next()) |sym_loc| { - const inner = self.getSymbol(sym_loc); - const inner_name = self.getSymbolName(sym_loc); - const offset = Atom.calcInnerSymbolOffset(self, atom_index, sym_loc.sym_index); - - logger.debug(" (%{d}, '{s}') @ {x} ({x})", .{ - sym_loc.sym_index, - inner_name, - inner.n_value, - offset, - }); - } - - if (Atom.getSectionAlias(self, atom_index)) |sym_loc| { - const alias = self.getSymbol(sym_loc); - const alias_name = self.getSymbolName(sym_loc); - - logger.debug(" (%{d}, '{s}') @ {x} ({x})", .{ - sym_loc.sym_index, - alias_name, - alias.n_value, - 0, - }); - } - } -} +const is_hot_update_compatible = switch (builtin.target.os.tag) { + .macos => true, + else => false, +}; const default_entry_symbol_name = "_main"; @@ -5663,17 +1010,6 @@ pub const base_tag: File.Tag = File.Tag.macho; pub const N_DEAD: u16 = @as(u16, @bitCast(@as(i16, -1))); pub const N_BOUNDARY: u16 = @as(u16, @bitCast(@as(i16, -2))); -/// Mode of operation of the linker. -pub const Mode = enum { - /// Incremental mode will preallocate segments/sections and is compatible with - /// watch and HCS modes of operation. - incremental, - /// Zld mode will link relocatables in a traditional, one-shot - /// fashion (default for LLVM backend). It acts as a drop-in replacement for - /// LLD. - zld, -}; - pub const Section = struct { header: macho.section_64, segment_index: u8, @@ -5698,11 +1034,6 @@ pub const Section = struct { free_list: std.ArrayListUnmanaged(Atom.Index) = .{}, }; -const is_hot_update_compatible = switch (builtin.target.os.tag) { - .macos => true, - else => false, -}; - const LazySymbolTable = std.AutoArrayHashMapUnmanaged(InternPool.OptionalDeclIndex, LazySymbolMetadata); const LazySymbolMetadata = struct { @@ -5713,8 +1044,6 @@ const LazySymbolMetadata = struct { data_const_state: State = .unused, }; -const TlvSymbolTable = std.AutoArrayHashMapUnmanaged(SymbolWithLoc, Atom.Index); - const DeclMetadata = struct { atom: Atom.Index, section: u8, @@ -5737,40 +1066,145 @@ const DeclMetadata = struct { } }; -const DeclTable = std.AutoArrayHashMapUnmanaged(InternPool.DeclIndex, DeclMetadata); -const AnonDeclTable = std.AutoHashMapUnmanaged(InternPool.Index, DeclMetadata); -const BindingTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Atom.Binding)); -const UnnamedConstTable = std.AutoArrayHashMapUnmanaged(InternPool.DeclIndex, std.ArrayListUnmanaged(Atom.Index)); -const RebaseTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(u32)); -const RelocationTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Relocation)); -const ActionTable = std.AutoHashMapUnmanaged(u32, RelocFlags); - -pub const RelocFlags = packed struct { - add_got: bool = false, - add_stub: bool = false, -}; - -pub const SymbolWithLoc = extern struct { - // Index into the respective symbol table. - sym_index: u32, - - // 0 means it's a synthetic global. - file: u32 = 0, - - pub fn getFile(self: SymbolWithLoc) ?u32 { - if (self.file == 0) return null; - return self.file - 1; - } - - pub fn eql(self: SymbolWithLoc, other: SymbolWithLoc) bool { - return self.file == other.file and self.sym_index == other.sym_index; - } -}; - const HotUpdateState = struct { mach_task: ?std.os.darwin.MachTask = null, }; +pub const SymtabCtx = struct { + ilocal: u32 = 0, + istab: u32 = 0, + iexport: u32 = 0, + iimport: u32 = 0, + nlocals: u32 = 0, + nstabs: u32 = 0, + nexports: u32 = 0, + nimports: u32 = 0, + strsize: u32 = 0, +}; + +pub const null_sym = macho.nlist_64{ + .n_strx = 0, + .n_type = 0, + .n_sect = 0, + .n_desc = 0, + .n_value = 0, +}; + +pub const Platform = struct { + platform: macho.PLATFORM, + version: Version, + + /// Using Apple's ld64 as our blueprint, `min_version` as well as `sdk_version` are set to + /// the extracted minimum platform version. + pub fn fromLoadCommand(lc: macho.LoadCommandIterator.LoadCommand) Platform { + switch (lc.cmd()) { + .BUILD_VERSION => { + const lc_cmd = lc.cast(macho.build_version_command).?; + return .{ + .platform = lc_cmd.platform, + .version = .{ .value = lc_cmd.minos }, + }; + }, + .VERSION_MIN_MACOSX, + .VERSION_MIN_IPHONEOS, + .VERSION_MIN_TVOS, + .VERSION_MIN_WATCHOS, + => { + const lc_cmd = lc.cast(macho.version_min_command).?; + return .{ + .platform = switch (lc.cmd()) { + .VERSION_MIN_MACOSX => .MACOS, + .VERSION_MIN_IPHONEOS => .IOS, + .VERSION_MIN_TVOS => .TVOS, + .VERSION_MIN_WATCHOS => .WATCHOS, + else => unreachable, + }, + .version = .{ .value = lc_cmd.version }, + }; + }, + else => unreachable, + } + } + + pub fn isBuildVersionCompatible(plat: Platform) bool { + inline for (supported_platforms) |sup_plat| { + if (sup_plat[0] == plat.platform) { + return sup_plat[1] <= plat.version.value; + } + } + return false; + } +}; + +pub const Version = struct { + value: u32, + + pub fn major(v: Version) u16 { + return @as(u16, @truncate(v.value >> 16)); + } + + pub fn minor(v: Version) u8 { + return @as(u8, @truncate(v.value >> 8)); + } + + pub fn patch(v: Version) u8 { + return @as(u8, @truncate(v.value)); + } + + pub fn parse(raw: []const u8) ?Version { + var parsed: [3]u16 = [_]u16{0} ** 3; + var count: usize = 0; + var it = std.mem.splitAny(u8, raw, "."); + while (it.next()) |comp| { + if (count >= 3) return null; + parsed[count] = std.fmt.parseInt(u16, comp, 10) catch return null; + count += 1; + } + if (count == 0) return null; + const maj = parsed[0]; + const min = std.math.cast(u8, parsed[1]) orelse return null; + const pat = std.math.cast(u8, parsed[2]) orelse return null; + return Version.new(maj, min, pat); + } + + pub fn new(maj: u16, min: u8, pat: u8) Version { + return .{ .value = (@as(u32, @intCast(maj)) << 16) | (@as(u32, @intCast(min)) << 8) | pat }; + } + + pub fn format( + v: Version, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = unused_fmt_string; + _ = options; + try writer.print("{d}.{d}.{d}", .{ + v.major(), + v.minor(), + v.patch(), + }); + } +}; + +const SupportedPlatforms = struct { + macho.PLATFORM, // Platform identifier + u32, // Min platform version for which to emit LC_BUILD_VERSION + u32, // Min supported platform version + ?[]const u8, // Env var to look for +}; + +// Source: https://github.com/apple-oss-distributions/ld64/blob/59a99ab60399c5e6c49e6945a9e1049c42b71135/src/ld/PlatformSupport.cpp#L52 +const supported_platforms = [_]SupportedPlatforms{ + .{ .MACOS, 0xA0E00, 0xA0800, "MACOSX_DEPLOYMENT_TARGET" }, + .{ .IOS, 0xC0000, 0x70000, "IPHONEOS_DEPLOYMENT_TARGET" }, + .{ .TVOS, 0xC0000, 0x70000, "TVOS_DEPLOYMENT_TARGET" }, + .{ .WATCHOS, 0x50000, 0x20000, "WATCHOS_DEPLOYMENT_TARGET" }, + .{ .IOSSIMULATOR, 0xD0000, 0x80000, null }, + .{ .TVOSSIMULATOR, 0xD0000, 0x80000, null }, + .{ .WATCHOSSIMULATOR, 0x60000, 0x20000, null }, +}; + /// When allocating, the ideal_capacity is calculated by /// actual_capacity + (actual_capacity / ideal_factor) const ideal_factor = 3; @@ -5812,20 +1246,20 @@ const fat = @import("MachO/fat.zig"); const link = @import("../link.zig"); const llvm_backend = @import("../codegen/llvm.zig"); const load_commands = @import("MachO/load_commands.zig"); -const stubs = @import("MachO/stubs.zig"); const tapi = @import("tapi.zig"); const target_util = @import("../target.zig"); const thunks = @import("MachO/thunks.zig"); const trace = @import("../tracy.zig").trace; -const zld = @import("MachO/zld.zig"); const Air = @import("../Air.zig"); +const Alignment = Atom.Alignment; const Allocator = mem.Allocator; const Archive = @import("MachO/Archive.zig"); pub const Atom = @import("MachO/Atom.zig"); const Cache = std.Build.Cache; const CodeSignature = @import("MachO/CodeSignature.zig"); const Compilation = @import("../Compilation.zig"); +pub const DebugSymbols = @import("MachO/DebugSymbols.zig"); const Dwarf = File.Dwarf; const DwarfInfo = @import("MachO/DwarfInfo.zig"); const Dylib = @import("MachO/Dylib.zig"); @@ -5837,17 +1271,9 @@ const LlvmObject = @import("../codegen/llvm.zig").Object; const Md5 = std.crypto.hash.Md5; const Module = @import("../Module.zig"); const InternPool = @import("../InternPool.zig"); -const Platform = load_commands.Platform; const Relocation = @import("MachO/Relocation.zig"); const StringTable = @import("StringTable.zig"); const TableSection = @import("table_section.zig").TableSection; -const Trie = @import("MachO/Trie.zig"); const Type = @import("../type.zig").Type; const TypedValue = @import("../TypedValue.zig"); const Value = @import("../value.zig").Value; -const Alignment = Atom.Alignment; - -pub const DebugSymbols = @import("MachO/DebugSymbols.zig"); -pub const Bind = @import("MachO/dyld_info/bind.zig").Bind(*const MachO, SymbolWithLoc); -pub const LazyBind = @import("MachO/dyld_info/bind.zig").LazyBind(*const MachO, SymbolWithLoc); -pub const Rebase = @import("MachO/dyld_info/Rebase.zig"); diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index d734faa487..64e7b70a0d 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -449,7 +449,7 @@ fn resolveRelocInner( if (rel.getTargetSymbol(macho_file).flags.got) { try writer.writeInt(i32, @intCast(G + A - P), .little); } else { - try relaxGotLoad(code[rel_offset - 3 ..]); + try x86_64.relaxGotLoad(code[rel_offset - 3 ..]); try writer.writeInt(i32, @intCast(S + A - P), .little); } }, @@ -463,7 +463,7 @@ fn resolveRelocInner( const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file)); try writer.writeInt(i32, @intCast(S_ + A - P), .little); } else { - try relaxTlv(code[rel_offset - 3 ..]); + try x86_64.relaxTlv(code[rel_offset - 3 ..]); try writer.writeInt(i32, @intCast(S + A - P), .little); } }, @@ -631,43 +631,51 @@ fn resolveRelocInner( } } -fn relaxGotLoad(code: []u8) error{RelaxFail}!void { - const old_inst = disassemble(code) orelse return error.RelaxFail; - switch (old_inst.encoding.mnemonic) { - .mov => { - const inst = Instruction.new(old_inst.prefix, .lea, &old_inst.ops) catch return error.RelaxFail; - relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding }); - encode(&.{inst}, code) catch return error.RelaxFail; - }, - else => return error.RelaxFail, +const x86_64 = struct { + fn relaxGotLoad(code: []u8) error{RelaxFail}!void { + const old_inst = disassemble(code) orelse return error.RelaxFail; + switch (old_inst.encoding.mnemonic) { + .mov => { + const inst = Instruction.new(old_inst.prefix, .lea, &old_inst.ops) catch return error.RelaxFail; + relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding }); + encode(&.{inst}, code) catch return error.RelaxFail; + }, + else => return error.RelaxFail, + } } -} -fn relaxTlv(code: []u8) error{RelaxFail}!void { - const old_inst = disassemble(code) orelse return error.RelaxFail; - switch (old_inst.encoding.mnemonic) { - .mov => { - const inst = Instruction.new(old_inst.prefix, .lea, &old_inst.ops) catch return error.RelaxFail; - relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding }); - encode(&.{inst}, code) catch return error.RelaxFail; - }, - else => return error.RelaxFail, + fn relaxTlv(code: []u8) error{RelaxFail}!void { + const old_inst = disassemble(code) orelse return error.RelaxFail; + switch (old_inst.encoding.mnemonic) { + .mov => { + const inst = Instruction.new(old_inst.prefix, .lea, &old_inst.ops) catch return error.RelaxFail; + relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding }); + encode(&.{inst}, code) catch return error.RelaxFail; + }, + else => return error.RelaxFail, + } } -} -fn disassemble(code: []const u8) ?Instruction { - var disas = Disassembler.init(code); - const inst = disas.next() catch return null; - return inst; -} - -fn encode(insts: []const Instruction, code: []u8) !void { - var stream = std.io.fixedBufferStream(code); - const writer = stream.writer(); - for (insts) |inst| { - try inst.encode(writer, .{}); + fn disassemble(code: []const u8) ?Instruction { + var disas = Disassembler.init(code); + const inst = disas.next() catch return null; + return inst; } -} + + fn encode(insts: []const Instruction, code: []u8) !void { + var stream = std.io.fixedBufferStream(code); + const writer = stream.writer(); + for (insts) |inst| { + try inst.encode(writer, .{}); + } + } + + const bits = @import("../../arch/x86_64/bits.zig"); + const encoder = @import("../../arch/x86_64/encoder.zig"); + const Disassembler = @import("../../arch/x86_64/Disassembler.zig"); + const Immediate = bits.Immediate; + const Instruction = encoder.Instruction; +}; pub fn calcNumRelocs(self: Atom, macho_file: *MachO) u32 { switch (macho_file.options.cpu_arch.?) { @@ -879,24 +887,22 @@ pub const Loc = struct { len: usize = 0, }; -const aarch64 = @import("../aarch64.zig"); +pub const Alignment = @import("../../InternPool.zig").Alignment; + +const aarch64 = @import("../../arch/aarch64/bits.zig"); const assert = std.debug.assert; const bind = @import("dyld_info/bind.zig"); -const dis_x86_64 = @import("dis_x86_64"); const macho = std.macho; const math = std.math; const mem = std.mem; const log = std.log.scoped(.link); const relocs_log = std.log.scoped(.relocs); const std = @import("std"); -const trace = @import("../tracy.zig").trace; +const trace = @import("../../tracy.zig").trace; const Allocator = mem.Allocator; const Atom = @This(); -const Disassembler = dis_x86_64.Disassembler; const File = @import("file.zig").File; -const Instruction = dis_x86_64.Instruction; -const Immediate = dis_x86_64.Immediate; const MachO = @import("../MachO.zig"); const Object = @import("Object.zig"); const Relocation = @import("Relocation.zig"); diff --git a/src/link/MachO/CodeSignature.zig b/src/link/MachO/CodeSignature.zig index ce142b4376..54d83d4530 100644 --- a/src/link/MachO/CodeSignature.zig +++ b/src/link/MachO/CodeSignature.zig @@ -11,7 +11,6 @@ const Allocator = mem.Allocator; const Hasher = @import("hasher.zig").ParallelHasher; const MachO = @import("../MachO.zig"); const Sha256 = std.crypto.hash.sha2.Sha256; -const Zld = @import("../Zld.zig"); const hash_size = Sha256.digest_length; diff --git a/src/link/MachO/DwarfInfo.zig b/src/link/MachO/DwarfInfo.zig index c3f8d235ce..8b32faa567 100644 --- a/src/link/MachO/DwarfInfo.zig +++ b/src/link/MachO/DwarfInfo.zig @@ -461,7 +461,7 @@ const leb = std.leb; const log = std.log.scoped(.link); const mem = std.mem; const std = @import("std"); -const trace = @import("../tracy.zig").trace; +const trace = @import("../../tracy.zig").trace; const Allocator = mem.Allocator; const DwarfInfo = @This(); diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index 4944c4d5ef..f28e4eb08d 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -12,7 +12,7 @@ symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, dependents: std.ArrayListUnmanaged(Id) = .{}, rpaths: std.StringArrayHashMapUnmanaged(void) = .{}, umbrella: File.Index = 0, -platform: ?MachO.Options.Platform = null, +platform: ?MachO.Platform = null, needed: bool, weak: bool, @@ -815,7 +815,7 @@ const macho = std.macho; const math = std.math; const mem = std.mem; const tapi = @import("../tapi.zig"); -const trace = @import("../tracy.zig").trace; +const trace = @import("../../tracy.zig").trace; const std = @import("std"); const Allocator = mem.Allocator; diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index deb17ba80b..0ecf3b4d45 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -12,7 +12,7 @@ strtab: []const u8 = &[0]u8{}, symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, -platform: ?MachO.Options.Platform = null, +platform: ?MachO.Platform = null, dwarf_info: ?DwarfInfo = null, stab_files: std.ArrayListUnmanaged(StabFile) = .{}, @@ -2075,7 +2075,7 @@ const log = std.log.scoped(.link); const macho = std.macho; const math = std.math; const mem = std.mem; -const trace = @import("../tracy.zig").trace; +const trace = @import("../../tracy.zig").trace; const std = @import("std"); const Allocator = mem.Allocator; @@ -2088,6 +2088,5 @@ const LoadCommandIterator = macho.LoadCommandIterator; const MachO = @import("../MachO.zig"); const Object = @This(); const Relocation = @import("Relocation.zig"); -const StringTable = @import("../strtab.zig").StringTable; const Symbol = @import("Symbol.zig"); const UnwindInfo = @import("UnwindInfo.zig"); diff --git a/src/link/MachO/UnwindInfo.zig b/src/link/MachO/UnwindInfo.zig index 1d0bfc1ff9..33c07915dc 100644 --- a/src/link/MachO/UnwindInfo.zig +++ b/src/link/MachO/UnwindInfo.zig @@ -670,7 +670,7 @@ const log = std.log.scoped(.link); const macho = std.macho; const math = std.math; const mem = std.mem; -const trace = @import("../tracy.zig").trace; +const trace = @import("../../tracy.zig").trace; const Allocator = mem.Allocator; const Atom = @import("Atom.zig"); diff --git a/src/link/MachO/dead_strip.zig b/src/link/MachO/dead_strip.zig index 8d2dba53c6..8097d5f710 100644 --- a/src/link/MachO/dead_strip.zig +++ b/src/link/MachO/dead_strip.zig @@ -193,7 +193,7 @@ const log = std.log.scoped(.dead_strip); const macho = std.macho; const math = std.math; const mem = std.mem; -const trace = @import("../tracy.zig").trace; +const trace = @import("../../tracy.zig").trace; const track_live_log = std.log.scoped(.dead_strip_track_live); const std = @import("std"); diff --git a/src/link/MachO/eh_frame.zig b/src/link/MachO/eh_frame.zig index 6ca7a5cd2a..716a66d4fa 100644 --- a/src/link/MachO/eh_frame.zig +++ b/src/link/MachO/eh_frame.zig @@ -559,7 +559,7 @@ const macho = std.macho; const math = std.math; const mem = std.mem; const std = @import("std"); -const trace = @import("../tracy.zig").trace; +const trace = @import("../../tracy.zig").trace; const Allocator = std.mem.Allocator; const Atom = @import("Atom.zig"); diff --git a/src/link/MachO/hasher.zig b/src/link/MachO/hasher.zig index 95faaf3a92..57c8acd35e 100644 --- a/src/link/MachO/hasher.zig +++ b/src/link/MachO/hasher.zig @@ -67,7 +67,7 @@ const assert = std.debug.assert; const fs = std.fs; const mem = std.mem; const std = @import("std"); -const trace = @import("../tracy.zig").trace; +const trace = @import("../../tracy.zig").trace; const Allocator = mem.Allocator; const ThreadPool = std.Thread.Pool; diff --git a/src/link/MachO/thunks.zig b/src/link/MachO/thunks.zig index 6593fb6a1b..7f0dc56685 100644 --- a/src/link/MachO/thunks.zig +++ b/src/link/MachO/thunks.zig @@ -160,14 +160,14 @@ const max_distance = (1 << (jump_bits - 1)); /// and assume margin to be 5MiB. const max_allowed_distance = max_distance - 0x500_000; -const aarch64 = @import("../aarch64.zig"); +const aarch64 = @import("../../arch/aarch64/bits.zig"); const assert = std.debug.assert; const log = std.log.scoped(.link); const macho = std.macho; const math = std.math; const mem = std.mem; const std = @import("std"); -const trace = @import("../tracy.zig").trace; +const trace = @import("../../tracy.zig").trace; const Allocator = mem.Allocator; const Atom = @import("Atom.zig"); diff --git a/src/link/MachO/uuid.zig b/src/link/MachO/uuid.zig index 0248acc091..ca66e129d2 100644 --- a/src/link/MachO/uuid.zig +++ b/src/link/MachO/uuid.zig @@ -47,7 +47,7 @@ inline fn conform(out: *[Md5.digest_length]u8) void { const fs = std.fs; const mem = std.mem; const std = @import("std"); -const trace = @import("../tracy.zig").trace; +const trace = @import("../../tracy.zig").trace; const Allocator = mem.Allocator; const Md5 = std.crypto.hash.Md5;