Commit dd0addab1f
Changed files (17)
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).?;
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) {
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)});
}
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,
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 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 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");
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;
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");
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();
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;
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");
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;
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");
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");
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");
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;
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});
+ // Index 0 is always a null symbol.
+ // try self.locals.append(gpa, null_sym);
+ try self.strtab.buffer.append(gpa, 0);
- 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},
- );
+ // TODO: init
- var d_sym_bundle = try emit.directory.handle.makeOpenPath(d_sym_path, .{});
- defer d_sym_bundle.close();
+ if (opt_zcu) |zcu| {
+ if (!use_llvm) {
+ _ = zcu;
+ // TODO: create .zig_object
- const d_sym_file = try d_sym_bundle.createFile(emit.sub_path, .{
- .truncate = false,
- .read = true,
- });
+ if (comp.config.debug_format != .strip) {
+ // Create dSYM bundle.
+ log.debug("creating {s}.dSYM bundle", .{emit.sub_path});
- self.d_sym = .{
- .allocator = gpa,
- .dwarf = link.File.Dwarf.init(&self.base, .dwarf32),
- .file = d_sym_file,
- };
- }
+ 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},
+ );
- // 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.strtab.buffer.append(gpa, 0);
+ var d_sym_bundle = try emit.directory.handle.makeOpenPath(d_sym_path, .{});
+ defer d_sym_bundle.close();
- try self.populateMissingMetadata(.{
- .symbol_count_hint = options.symbol_count_hint,
- .program_code_size_hint = options.program_code_size_hint,
- });
+ const d_sym_file = try d_sym_bundle.createFile(emit.sub_path, .{
+ .truncate = false,
+ .read = true,
+ });
- if (self.d_sym) |*d_sym| {
- try d_sym.populateMissingMetadata(self);
+ 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);
- }
- }
-
- 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);
+ _ = 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.writeCodeSignaturePadding(&codesig);
- break :blk codesig;
} else null;
- defer if (codesig) |*csig| csig.deinit(gpa);
-
- // Write load commands
- var lc_buffer = std.ArrayList(u8).init(arena);
- const lc_writer = lc_buffer.writer();
-
- 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);
-
- 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 => {},
- }
+ _ = module_obj_path;
- 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);
- }
+ // --verbose-link
+ if (comp.verbose_link) try self.dumpArgv(comp);
- 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);
- }
+ @panic("TODO");
+}
- 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,4441 +379,338 @@ 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);
- }
+ _ = self;
+ _ = file;
+ _ = path;
+ _ = must_link;
+ _ = 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();
-
+pub fn deinit(self: *MachO) void {
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 (self.llvm_object) |llvm_object| llvm_object.deinit();
- 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;
+ if (self.d_sym) |*d_sym| {
+ d_sym.deinit();
}
- 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();
+ self.strtab.deinit(gpa);
- const target = self.base.comp.root_mod.resolved_target.result;
+ self.segments.deinit(gpa);
- 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,
- };
+ for (self.sections.items(.free_list)) |*list| {
+ list.deinit(gpa);
}
+ self.sections.deinit(gpa);
}
-pub fn parseFatLibrary(
- self: *MachO,
- file: std.fs.File,
- cpu_arch: std.Target.Cpu.Arch,
- ctx: *ParseErrorCtx,
-) ParseError!u64 {
+fn freeAtom(self: *MachO, atom_index: Atom.Index) void {
const gpa = self.base.comp.gpa;
+ log.debug("freeAtom {d}", .{atom_index});
- const fat_archs = try fat.parseArchs(gpa, file);
- defer gpa.free(fat_archs);
+ // Remove any relocs and base relocs associated with this Atom
+ Atom.freeRelocations(self, atom_index);
- 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)));
+ const atom = self.getAtom(atom_index);
+ const sect_id = atom.getSymbol(self).n_sect - 1;
+ const free_list = &self.sections.items(.free_list)[sect_id];
+ var already_have_free_list_node = false;
+ {
+ var i: usize = 0;
+ // TODO turn free_list into a hash map
+ while (i < free_list.items.len) {
+ if (free_list.items[i] == atom_index) {
+ _ = free_list.swapRemove(i);
+ continue;
+ }
+ if (free_list.items[i] == atom.prev_index) {
+ already_have_free_list_node = true;
+ }
+ i += 1;
}
- 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());
+ const maybe_last_atom_index = &self.sections.items(.last_atom_index)[sect_id];
+ if (maybe_last_atom_index.*) |last_atom_index| {
+ if (last_atom_index == atom_index) {
+ if (atom.prev_index) |prev_index| {
+ // TODO shrink the section size here
+ maybe_last_atom_index.* = prev_index;
+ } else {
+ maybe_last_atom_index.* = null;
+ }
+ }
+ }
- // 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);
+ if (atom.prev_index) |prev_index| {
+ const prev = self.getAtomPtr(prev_index);
+ prev.next_index = atom.next_index;
- 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 (!already_have_free_list_node and prev.*.freeListEligible(self)) {
+ // The free list is heuristics, it doesn't have to be perfect, so we can ignore
+ // the OOM here.
+ free_list.append(gpa, prev_index) catch {};
}
+ } else {
+ self.getAtomPtr(atom_index).prev_index = null;
}
- 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);
- }
+ if (atom.next_index) |next_index| {
+ self.getAtomPtr(next_index).prev_index = atom.prev_index;
} else {
- try self.archives.append(gpa, archive);
+ self.getAtomPtr(atom_index).next_index = null;
}
-}
-pub const DylibReExportInfo = struct {
- id: Dylib.Id,
- parent: u16,
-};
+ // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
+ const sym_index = atom.getSymbolIndex().?;
-const DylibOpts = struct {
- reexport_info: ?DylibReExportInfo = null,
- dependent: bool = false,
- needed: bool = false,
- weak: bool = false,
-};
+ self.locals_free_list.append(gpa, sym_index) catch {};
-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);
+ // Try freeing GOT atom if this decl had one
+ self.got_table.freeEntry(gpa, .{ .sym_index = sym_index });
- 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;
+ if (self.d_sym) |*d_sym| {
+ d_sym.swapRemoveRelocs(sym_index);
}
- try self.addDylib(dylib, dylib_options, ctx);
+ self.locals.items[sym_index].n_type = 0;
+ _ = self.atom_by_index_table.remove(sym_index);
+ log.debug(" adding local symbol index {d} to free list", .{sym_index});
+ self.getAtomPtr(atom_index).sym_index = 0;
}
-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;
- }
-}
-
-pub fn deinit(self: *MachO) void {
- const gpa = self.base.comp.gpa;
-
- if (self.llvm_object) |llvm_object| llvm_object.deinit();
-
- if (self.d_sym) |*d_sym| {
- 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);
-
- for (self.sections.items(.free_list)) |*list| {
- 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 {
- const gpa = self.base.comp.gpa;
- log.debug("freeAtom {d}", .{atom_index});
-
- // Remove any relocs and base relocs associated with this Atom
- Atom.freeRelocations(self, atom_index);
-
- const atom = self.getAtom(atom_index);
- const sect_id = atom.getSymbol(self).n_sect - 1;
- const free_list = &self.sections.items(.free_list)[sect_id];
- var already_have_free_list_node = false;
- {
- var i: usize = 0;
- // TODO turn free_list into a hash map
- while (i < free_list.items.len) {
- if (free_list.items[i] == atom_index) {
- _ = free_list.swapRemove(i);
- continue;
- }
- if (free_list.items[i] == atom.prev_index) {
- already_have_free_list_node = true;
- }
- i += 1;
- }
- }
-
- const maybe_last_atom_index = &self.sections.items(.last_atom_index)[sect_id];
- if (maybe_last_atom_index.*) |last_atom_index| {
- if (last_atom_index == atom_index) {
- if (atom.prev_index) |prev_index| {
- // TODO shrink the section size here
- maybe_last_atom_index.* = prev_index;
- } else {
- maybe_last_atom_index.* = null;
- }
- }
- }
-
- if (atom.prev_index) |prev_index| {
- const prev = self.getAtomPtr(prev_index);
- prev.next_index = atom.next_index;
-
- if (!already_have_free_list_node and prev.*.freeListEligible(self)) {
- // The free list is heuristics, it doesn't have to be perfect, so we can ignore
- // the OOM here.
- free_list.append(gpa, prev_index) catch {};
- }
- } else {
- self.getAtomPtr(atom_index).prev_index = null;
- }
-
- if (atom.next_index) |next_index| {
- self.getAtomPtr(next_index).prev_index = atom.prev_index;
- } else {
- self.getAtomPtr(atom_index).next_index = null;
- }
-
- // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
- const sym_index = atom.getSymbolIndex().?;
-
- self.locals_free_list.append(gpa, sym_index) catch {};
-
- // Try freeing GOT atom if this decl had one
- self.got_table.freeEntry(gpa, .{ .sym_index = sym_index });
-
- if (self.d_sym) |*d_sym| {
- d_sym.swapRemoveRelocs(sym_index);
- }
-
- self.locals.items[sym_index].n_type = 0;
- _ = self.atom_by_index_table.remove(sym_index);
- log.debug(" adding local symbol index {d} to free list", .{sym_index});
- self.getAtomPtr(atom_index).sym_index = 0;
-}
-
-fn shrinkAtom(self: *MachO, atom_index: Atom.Index, new_block_size: u64) void {
- _ = self;
- _ = atom_index;
- _ = new_block_size;
- // TODO check the new capacity, and if it crosses the size threshold into a big enough
- // capacity, insert a free list node for it.
-}
-
-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,
- });
- }
-}
-
-pub fn updateFunc(self: *MachO, mod: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {
- if (build_options.skip_non_native and builtin.object_format != .macho) {
- @panic("Attempted to compile for object format that was disabled by build configuration");
- }
- if (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));
-}
-
-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().?;
-}
-
-const LowerConstResult = union(enum) {
- ok: Atom.Index,
- fail: *Module.ErrorMsg,
-};
-
-fn lowerConst(
- self: *MachO,
- name: []const u8,
- tv: TypedValue,
- required_alignment: InternPool.Alignment,
- sect_id: u8,
- src_loc: Module.SrcLoc,
-) !LowerConstResult {
- const gpa = self.base.comp.gpa;
-
- 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 };
-}
-
-pub fn updateDecl(self: *MachO, mod: *Module, decl_index: InternPool.DeclIndex) !void {
- if (build_options.skip_non_native and builtin.object_format != .macho) {
- @panic("Attempted to compile for object format that was disabled by build configuration");
- }
- if (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));
-}
-
-fn updateLazySymbolAtom(
- self: *MachO,
- sym: File.LazySymbol,
- 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);
-}
-
-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);
-}
-
-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;
-}
-
-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;
-}
-
-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;
-}
-
-pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl_index: InternPool.DeclIndex) !void {
- if (self.d_sym) |*d_sym| {
- try d_sym.dwarf.updateDeclLineNumber(module, decl_index);
- }
-}
-
-pub fn updateExports(
- self: *MachO,
- mod: *Module,
- exported: Module.Exported,
- exports: []const *Module.Export,
-) File.UpdateExportsError!void {
- if (build_options.skip_non_native and builtin.object_format != .macho) {
- @panic("Attempted to compile for object format that was disabled by build configuration");
- }
- if (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,
- };
- }
-}
-
-pub fn deleteDeclExport(
- self: *MachO,
- decl_index: InternPool.DeclIndex,
- 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;
-}
-
-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);
-}
-
-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);
- }
-}
-
-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;
-}
-
-pub fn lowerAnonDecl(
- self: *MachO,
- decl_val: InternPool.Index,
- 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;
-}
-
-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;
-}
-
-pub fn getGlobalSymbol(self: *MachO, name: []const u8, lib_name: ?[]const u8) !u32 {
- _ = 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,
- };
+fn shrinkAtom(self: *MachO, atom_index: Atom.Index, new_block_size: u64) void {
+ _ = self;
+ _ = atom_index;
+ _ = new_block_size;
+ // TODO check the new capacity, and if it crosses the size threshold into a big enough
+ // capacity, insert a free list node for it.
}
-// 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 growAtom(self: *MachO, atom_index: Atom.Index, new_atom_size: u64, alignment: Alignment) !u64 {
+ _ = self;
+ _ = atom_index;
+ _ = new_atom_size;
+ _ = alignment;
+ @panic("TODO growAtom");
}
-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];
+pub fn updateFunc(self: *MachO, mod: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {
+ if (build_options.skip_non_native and builtin.object_format != .macho) {
+ @panic("Attempted to compile for object format that was disabled by build configuration");
}
+ if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(mod, func_index, air, liveness);
- 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;
+ @panic("TODO updateFunc");
}
-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);
+pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: InternPool.DeclIndex) !u32 {
+ _ = self;
+ _ = typed_value;
+ _ = decl_index;
- self.symtab_cmd.stroff = @as(u32, @intCast(offset));
- self.symtab_cmd.strsize = @as(u32, @intCast(needed_size_aligned));
+ @panic("TODO lowerUnnamedConst");
}
-const SymtabCtx = struct {
- nlocalsym: u32,
- nextdefsym: u32,
- nundefsym: u32,
- imports_table: std.AutoHashMap(SymbolWithLoc, u32),
+const LowerConstResult = union(enum) {
+ ok: Atom.Index,
+ fail: *Module.ErrorMsg,
};
-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);
-}
-
-pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
- return actual_size +| (actual_size / ideal_factor);
-}
-
-fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
- // TODO: header and load commands have to be part of the __TEXT segment
- const header_size = self.segments.items[self.header_segment_cmd_index.?].filesize;
- if (start < header_size)
- return header_size;
-
- const end = start + padToIdeal(size);
-
- for (self.sections.items(.header)) |header| {
- const tight_size = header.size;
- const increased_size = padToIdeal(tight_size);
- const test_end = header.offset + increased_size;
- if (end > header.offset and start < test_end) {
- return test_end;
- }
- }
-
- return null;
-}
-
-fn allocatedSize(self: *MachO, start: u64) u64 {
- if (start == 0)
- return 0;
- var min_pos: u64 = std.math.maxInt(u64);
- for (self.sections.items(.header)) |header| {
- if (header.offset <= start) continue;
- if (header.offset < min_pos) min_pos = header.offset;
- }
- return min_pos - start;
-}
-
-fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 {
- var start: u64 = 0;
- while (self.detectAllocCollision(start, object_size)) |item_end| {
- start = mem.alignForward(u64, item_end, min_alignment);
- }
- 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];
- }
-}
+fn lowerConst(
+ self: *MachO,
+ name: []const u8,
+ tv: TypedValue,
+ required_alignment: InternPool.Alignment,
+ sect_id: u8,
+ src_loc: Module.SrcLoc,
+) !LowerConstResult {
+ _ = self;
+ _ = name;
+ _ = tv;
+ _ = required_alignment;
+ _ = sect_id;
+ _ = src_loc;
-/// 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).?;
- }
+ @panic("TODO lowerConst");
}
-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 };
+pub fn updateDecl(self: *MachO, mod: *Module, decl_index: InternPool.DeclIndex) !void {
+ if (build_options.skip_non_native and builtin.object_format != .macho) {
+ @panic("Attempted to compile for object format that was disabled by build configuration");
}
- return null;
-}
+ if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(mod, decl_index);
-const SegmentBoundarySymbol = struct {
- kind: BoundarySymbolKind,
- segname: []const u8,
-};
+ const tracy = trace(@src());
+ defer tracy.end();
-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;
+ @panic("TODO updateDecl");
}
-/// 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];
+fn updateLazySymbolAtom(
+ self: *MachO,
+ sym: File.LazySymbol,
+ atom_index: Atom.Index,
+ section_index: u8,
+) !void {
+ _ = self;
+ _ = sym;
+ _ = atom_index;
+ _ = section_index;
+ @panic("TODO updateLazySymbolAtom");
}
-/// 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];
+pub fn getOrCreateAtomForLazySymbol(self: *MachO, sym: File.LazySymbol) !Atom.Index {
+ _ = self;
+ _ = sym;
+ @panic("TODO getOrCreateAtomForLazySymbol");
}
-/// 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);
+pub fn getOrCreateAtomForDecl(self: *MachO, decl_index: InternPool.DeclIndex) !Atom.Index {
+ _ = self;
+ _ = decl_index;
+ @panic("TODO getOrCreateAtomForDecl");
}
-/// 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];
+fn getDeclOutputSection(self: *MachO, decl_index: InternPool.DeclIndex) u8 {
+ _ = self;
+ _ = decl_index;
+ @panic("TODO getDeclOutputSection");
}
-const GetOrPutGlobalPtrResult = struct {
- found_existing: bool,
- value_ptr: *SymbolWithLoc,
-};
+fn updateDeclCode(self: *MachO, decl_index: InternPool.DeclIndex, code: []u8) !u64 {
+ _ = self;
+ _ = decl_index;
+ _ = code;
+ @panic("TODO updateDeclCode");
+}
-/// 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 };
+pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl_index: InternPool.DeclIndex) !void {
+ if (self.d_sym) |*d_sym| {
+ try d_sym.dwarf.updateDeclLineNumber(module, decl_index);
}
- 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 updateExports(
+ self: *MachO,
+ mod: *Module,
+ exported: Module.Exported,
+ exports: []const *Module.Export,
+) File.UpdateExportsError!void {
+ if (build_options.skip_non_native and builtin.object_format != .macho) {
+ @panic("Attempted to compile for object format that was disabled by build configuration");
+ }
+ if (self.llvm_object) |llvm_object|
+ return llvm_object.updateExports(mod, exported, exports);
+
+ @panic("TODO updateExports");
}
-pub fn getAtomPtr(self: *MachO, atom_index: Atom.Index) *Atom {
- assert(atom_index < self.atoms.items.len);
- return &self.atoms.items[atom_index];
+pub fn deleteDeclExport(
+ self: *MachO,
+ decl_index: InternPool.DeclIndex,
+ name: InternPool.NullTerminatedString,
+) Allocator.Error!void {
+ if (self.llvm_object) |_| return;
+ _ = decl_index;
+ _ = name;
+ @panic("TODO deleteDeclExport");
}
-/// 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);
+fn freeUnnamedConsts(self: *MachO, decl_index: InternPool.DeclIndex) void {
+ _ = self;
+ _ = decl_index;
+ @panic("TODO freeUnnamedConst");
}
-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 freeDecl(self: *MachO, decl_index: InternPool.DeclIndex) void {
+ if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index);
+ @panic("TODO freeDecl");
}
-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 getDeclVAddr(self: *MachO, decl_index: InternPool.DeclIndex, reloc_info: File.RelocInfo) !u64 {
+ assert(self.llvm_object == null);
+ _ = decl_index;
+ _ = reloc_info;
+ @panic("TODO getDeclVAddr");
}
-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;
+pub fn lowerAnonDecl(
+ self: *MachO,
+ decl_val: InternPool.Index,
+ explicit_alignment: InternPool.Alignment,
+ src_loc: Module.SrcLoc,
+) !codegen.Result {
+ _ = self;
+ _ = decl_val;
+ _ = explicit_alignment;
+ _ = src_loc;
+ @panic("TODO lowerAnonDecl");
}
-/// 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 getAnonDeclVAddr(self: *MachO, decl_val: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 {
+ assert(self.llvm_object == null);
+ _ = decl_val;
+ _ = reloc_info;
+ @panic("TODO getAnonDeclVAddr");
}
-pub fn getDebugSymbols(self: *MachO) ?*DebugSymbols {
- if (self.d_sym == null) return null;
- return &self.d_sym.?;
+pub fn getGlobalSymbol(self: *MachO, name: []const u8, lib_name: ?[]const u8) !u32 {
+ _ = self;
+ _ = name;
+ _ = lib_name;
+ @panic("TODO getGlobalSymbol");
}
-pub inline fn getPageSize(cpu_arch: std.Target.Cpu.Arch) u16 {
- return switch (cpu_arch) {
- .aarch64 => 0x4000,
- .x86_64 => 0x1000,
- else => unreachable,
- };
+pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
+ return actual_size +| (actual_size / ideal_factor);
}
-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;
+fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
+ // TODO: header and load commands have to be part of the __TEXT segment
+ const header_size = self.segments.items[self.header_segment_cmd_index.?].filesize;
+ if (start < header_size)
+ return header_size;
+
+ const end = start + padToIdeal(size);
+
+ for (self.sections.items(.header)) |header| {
+ const tight_size = header.size;
+ const increased_size = padToIdeal(tight_size);
+ const test_end = header.offset + increased_size;
+ if (end > header.offset and start < test_end) {
+ return test_end;
+ }
+ }
+
+ return null;
}
-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;
+fn allocatedSize(self: *MachO, start: u64) u64 {
+ if (start == 0)
+ return 0;
+ var min_pos: u64 = std.math.maxInt(u64);
+ for (self.sections.items(.header)) |header| {
+ if (header.offset <= start) continue;
+ if (header.offset < min_pos) min_pos = header.offset;
+ }
+ return min_pos - start;
}
-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;
+fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 {
+ var start: u64 = 0;
+ while (self.detectAllocCollision(start, object_size)) |item_end| {
+ start = mem.alignForward(u64, item_end, min_alignment);
+ }
+ return start;
}
-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 fn makeStaticString(bytes: []const u8) [16]u8 {
+ var buf = [_]u8{0} ** 16;
+ @memcpy(buf[0..bytes.len], bytes);
+ return buf;
}
pub const ParseErrorCtx = 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);
-
- 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 });
- }
-
- 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);
-}
-
-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}) };
+// 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();
+
+// 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();
+
+// 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,
+// });
+// }
- 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,
- });
-}
-
-/// 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");
-
- 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;
+pub fn getDebugSymbols(self: *MachO) ?*DebugSymbols {
+ if (self.d_sym) |*ds| {
+ return ds;
+ } else return 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 ptraceAttach(self: *MachO, pid: std.os.pid_t) !void {
+ if (!is_hot_update_compatible) return;
-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,
- });
- }
-}
+ 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;
-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,
- });
- }
-}
+ // TODO start exception handler in another thread
-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..];
+ // TODO enable ones we register for exceptions
+ // try std.os.ptrace(std.os.darwin.PT.ATTACHEXC, pid, 0, 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 ptraceDetach(self: *MachO, pid: std.os.pid_t) !void {
+ if (!is_hot_update_compatible) return;
-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];
+ _ = pid;
- log.debug("{s},{s}", .{ header.segName(), header.sectName() });
+ // TODO stop exception handler
- while (true) {
- const atom = self.getAtom(atom_index);
- self.logAtom(atom_index, log);
+ // TODO see comment in ptraceAttach
+ // try std.os.ptrace(std.os.darwin.PT.DETACH, pid, 0, 0);
- if (atom.next_index) |next_index| {
- atom_index = next_index;
- } else break;
- }
- }
+ self.hot_state.mach_task = null;
}
-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,38 +1066,143 @@ 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,
+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 SymbolWithLoc = extern struct {
- // Index into the respective symbol table.
- sym_index: u32,
+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));
+ }
- // 0 means it's a synthetic global.
- file: u32 = 0,
+ 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 getFile(self: SymbolWithLoc) ?u32 {
- if (self.file == 0) return null;
- return self.file - 1;
+ 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 eql(self: SymbolWithLoc, other: SymbolWithLoc) bool {
- return self.file == other.file and self.sym_index == other.sym_index;
+ 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 HotUpdateState = struct {
- mach_task: ?std.os.darwin.MachTask = null,
+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
@@ -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");
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 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 });
+ // 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 });
}
- 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);