authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-01-09 19:51:01+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-01-24 12:34:38+01:00
logdd0addab1fe11b019e83c0050eedfa0ec67eb408
tree852e59e811627cb8fb13f4091c5e1532fccda706
parent2f94dc939e8acf0dab4cf1f3cf01b132ac6fdd3c

macho: get the ball rolling!


17 files changed, 749 insertions(+), 5287 deletions(-)

src/arch/aarch64/CodeGen.zig+44-36
...@@ -4013,10 +4013,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -4013,10 +4013,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
4013 .import => unreachable,4013 .import => unreachable,
4014 };4014 };
4015 const atom_index = switch (self.bin_file.tag) {4015 const atom_index = switch (self.bin_file.tag) {
4016 .macho => blk: {4016 .macho => {
4017 const macho_file = self.bin_file.cast(link.File.MachO).?;4017 // const macho_file = self.bin_file.cast(link.File.MachO).?;
4018 const atom = try macho_file.getOrCreateAtomForDecl(self.owner_decl);4018 // const atom = try macho_file.getOrCreateAtomForDecl(self.owner_decl);
4019 break :blk macho_file.getAtom(atom).getSymbolIndex().?;4019 // break :blk macho_file.getAtom(atom).getSymbolIndex().?;
4020 @panic("TODO store");
4020 },4021 },
4021 .coff => blk: {4022 .coff => blk: {
4022 const coff_file = self.bin_file.cast(link.File.Coff).?;4023 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...@@ -4321,14 +4322,16 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
4321 const got_addr = @as(u32, @intCast(sym.zigGotAddress(elf_file)));4322 const got_addr = @as(u32, @intCast(sym.zigGotAddress(elf_file)));
4322 try self.genSetReg(Type.usize, .x30, .{ .memory = got_addr });4323 try self.genSetReg(Type.usize, .x30, .{ .memory = got_addr });
4323 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {4324 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4324 const atom = try macho_file.getOrCreateAtomForDecl(func.owner_decl);4325 _ = macho_file;
4325 const sym_index = macho_file.getAtom(atom).getSymbolIndex().?;4326 @panic("TODO airCall");
4326 try self.genSetReg(Type.u64, .x30, .{4327 // const atom = try macho_file.getOrCreateAtomForDecl(func.owner_decl);
4327 .linker_load = .{4328 // const sym_index = macho_file.getAtom(atom).getSymbolIndex().?;
4328 .type = .got,4329 // try self.genSetReg(Type.u64, .x30, .{
4329 .sym_index = sym_index,4330 // .linker_load = .{
4330 },4331 // .type = .got,
4331 });4332 // .sym_index = sym_index,
4333 // },
4334 // });
4332 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {4335 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
4333 const atom = try coff_file.getOrCreateAtomForDecl(func.owner_decl);4336 const atom = try coff_file.getOrCreateAtomForDecl(func.owner_decl);
4334 const sym_index = coff_file.getAtom(atom).getSymbolIndex().?;4337 const sym_index = coff_file.getAtom(atom).getSymbolIndex().?;
...@@ -4352,18 +4355,20 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -4352,18 +4355,20 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
4352 const decl_name = mod.intern_pool.stringToSlice(mod.declPtr(extern_func.decl).name);4355 const decl_name = mod.intern_pool.stringToSlice(mod.declPtr(extern_func.decl).name);
4353 const lib_name = mod.intern_pool.stringToSliceUnwrap(extern_func.lib_name);4356 const lib_name = mod.intern_pool.stringToSliceUnwrap(extern_func.lib_name);
4354 if (self.bin_file.cast(link.File.MachO)) |macho_file| {4357 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4355 const sym_index = try macho_file.getGlobalSymbol(decl_name, lib_name);4358 _ = macho_file;
4356 const atom = try macho_file.getOrCreateAtomForDecl(self.owner_decl);4359 @panic("TODO airCall");
4357 const atom_index = macho_file.getAtom(atom).getSymbolIndex().?;4360 // const sym_index = try macho_file.getGlobalSymbol(decl_name, lib_name);
4358 _ = try self.addInst(.{4361 // const atom = try macho_file.getOrCreateAtomForDecl(self.owner_decl);
4359 .tag = .call_extern,4362 // const atom_index = macho_file.getAtom(atom).getSymbolIndex().?;
4360 .data = .{4363 // _ = try self.addInst(.{
4361 .relocation = .{4364 // .tag = .call_extern,
4362 .atom_index = atom_index,4365 // .data = .{
4363 .sym_index = sym_index,4366 // .relocation = .{
4364 },4367 // .atom_index = atom_index,
4365 },4368 // .sym_index = sym_index,
4366 });4369 // },
4370 // },
4371 // });
4367 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {4372 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
4368 const sym_index = try coff_file.getGlobalSymbol(decl_name, lib_name);4373 const sym_index = try coff_file.getGlobalSymbol(decl_name, lib_name);
4369 try self.genSetReg(Type.u64, .x30, .{4374 try self.genSetReg(Type.u64, .x30, .{
...@@ -5532,10 +5537,11 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro...@@ -5532,10 +5537,11 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
5532 .import => unreachable,5537 .import => unreachable,
5533 };5538 };
5534 const atom_index = switch (self.bin_file.tag) {5539 const atom_index = switch (self.bin_file.tag) {
5535 .macho => blk: {5540 .macho => {
5536 const macho_file = self.bin_file.cast(link.File.MachO).?;5541 // const macho_file = self.bin_file.cast(link.File.MachO).?;
5537 const atom = try macho_file.getOrCreateAtomForDecl(self.owner_decl);5542 // const atom = try macho_file.getOrCreateAtomForDecl(self.owner_decl);
5538 break :blk macho_file.getAtom(atom).getSymbolIndex().?;5543 // break :blk macho_file.getAtom(atom).getSymbolIndex().?;
5544 @panic("TODO genSetStack");
5539 },5545 },
5540 .coff => blk: {5546 .coff => blk: {
5541 const coff_file = self.bin_file.cast(link.File.Coff).?;5547 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...@@ -5653,10 +5659,11 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
5653 .import => .load_memory_import,5659 .import => .load_memory_import,
5654 };5660 };
5655 const atom_index = switch (self.bin_file.tag) {5661 const atom_index = switch (self.bin_file.tag) {
5656 .macho => blk: {5662 .macho => {
5657 const macho_file = self.bin_file.cast(link.File.MachO).?;5663 @panic("TODO genSetReg");
5658 const atom = try macho_file.getOrCreateAtomForDecl(self.owner_decl);5664 // const macho_file = self.bin_file.cast(link.File.MachO).?;
5659 break :blk macho_file.getAtom(atom).getSymbolIndex().?;5665 // const atom = try macho_file.getOrCreateAtomForDecl(self.owner_decl);
5666 // break :blk macho_file.getAtom(atom).getSymbolIndex().?;
5660 },5667 },
5661 .coff => blk: {5668 .coff => blk: {
5662 const coff_file = self.bin_file.cast(link.File.Coff).?;5669 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...@@ -5850,10 +5857,11 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I
5850 .import => unreachable,5857 .import => unreachable,
5851 };5858 };
5852 const atom_index = switch (self.bin_file.tag) {5859 const atom_index = switch (self.bin_file.tag) {
5853 .macho => blk: {5860 .macho => {
5854 const macho_file = self.bin_file.cast(link.File.MachO).?;5861 @panic("TODO genSetStackArgument");
5855 const atom = try macho_file.getOrCreateAtomForDecl(self.owner_decl);5862 // const macho_file = self.bin_file.cast(link.File.MachO).?;
5856 break :blk macho_file.getAtom(atom).getSymbolIndex().?;5863 // const atom = try macho_file.getOrCreateAtomForDecl(self.owner_decl);
5864 // break :blk macho_file.getAtom(atom).getSymbolIndex().?;
5857 },5865 },
5858 .coff => blk: {5866 .coff => blk: {
5859 const coff_file = self.bin_file.cast(link.File.Coff).?;5867 const coff_file = self.bin_file.cast(link.File.Coff).?;
src/arch/aarch64/Emit.zig+43-37
...@@ -677,6 +677,7 @@ fn mirDebugEpilogueBegin(emit: *Emit) !void {...@@ -677,6 +677,7 @@ fn mirDebugEpilogueBegin(emit: *Emit) !void {
677fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void {677fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void {
678 assert(emit.mir.instructions.items(.tag)[inst] == .call_extern);678 assert(emit.mir.instructions.items(.tag)[inst] == .call_extern);
679 const relocation = emit.mir.instructions.items(.data)[inst].relocation;679 const relocation = emit.mir.instructions.items(.data)[inst].relocation;
680 _ = relocation;
680681
681 const offset = blk: {682 const offset = blk: {
682 const offset = @as(u32, @intCast(emit.code.items.len));683 const offset = @as(u32, @intCast(emit.code.items.len));
...@@ -684,19 +685,22 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -684,19 +685,22 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void {
684 try emit.writeInstruction(Instruction.bl(0));685 try emit.writeInstruction(Instruction.bl(0));
685 break :blk offset;686 break :blk offset;
686 };687 };
688 _ = offset;
687689
688 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {690 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
689 // Add relocation to the decl.691 _ = macho_file;
690 const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index }).?;692 @panic("TODO mirCallExtern");
691 const target = macho_file.getGlobalByIndex(relocation.sym_index);693 // // Add relocation to the decl.
692 try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{694 // const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index }).?;
693 .type = .branch,695 // const target = macho_file.getGlobalByIndex(relocation.sym_index);
694 .target = target,696 // try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
695 .offset = offset,697 // .type = .branch,
696 .addend = 0,698 // .target = target,
697 .pcrel = true,699 // .offset = offset,
698 .length = 2,700 // .addend = 0,
699 });701 // .pcrel = true,
702 // .length = 2,
703 // });
700 } else if (emit.bin_file.cast(link.File.Coff)) |_| {704 } else if (emit.bin_file.cast(link.File.Coff)) |_| {
701 unreachable; // Calling imports is handled via `.load_memory_import`705 unreachable; // Calling imports is handled via `.load_memory_import`
702 } else {706 } else {
...@@ -900,32 +904,34 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -900,32 +904,34 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
900 }904 }
901905
902 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {906 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
903 const Atom = link.File.MachO.Atom;907 _ = macho_file;
904 const Relocation = Atom.Relocation;908 @panic("TODO mirLoadMemoryPie");
905 const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = data.atom_index }).?;909 // const Atom = link.File.MachO.Atom;
906 try Atom.addRelocations(macho_file, atom_index, &[_]Relocation{ .{910 // const Relocation = Atom.Relocation;
907 .target = .{ .sym_index = data.sym_index },911 // const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = data.atom_index }).?;
908 .offset = offset,912 // try Atom.addRelocations(macho_file, atom_index, &[_]Relocation{ .{
909 .addend = 0,913 // .target = .{ .sym_index = data.sym_index },
910 .pcrel = true,914 // .offset = offset,
911 .length = 2,915 // .addend = 0,
912 .type = switch (tag) {916 // .pcrel = true,
913 .load_memory_got, .load_memory_ptr_got => Relocation.Type.got_page,917 // .length = 2,
914 .load_memory_direct, .load_memory_ptr_direct => Relocation.Type.page,918 // .type = switch (tag) {
915 else => unreachable,919 // .load_memory_got, .load_memory_ptr_got => Relocation.Type.got_page,
916 },920 // .load_memory_direct, .load_memory_ptr_direct => Relocation.Type.page,
917 }, .{921 // else => unreachable,
918 .target = .{ .sym_index = data.sym_index },922 // },
919 .offset = offset + 4,923 // }, .{
920 .addend = 0,924 // .target = .{ .sym_index = data.sym_index },
921 .pcrel = false,925 // .offset = offset + 4,
922 .length = 2,926 // .addend = 0,
923 .type = switch (tag) {927 // .pcrel = false,
924 .load_memory_got, .load_memory_ptr_got => Relocation.Type.got_pageoff,928 // .length = 2,
925 .load_memory_direct, .load_memory_ptr_direct => Relocation.Type.pageoff,929 // .type = switch (tag) {
926 else => unreachable,930 // .load_memory_got, .load_memory_ptr_got => Relocation.Type.got_pageoff,
927 },931 // .load_memory_direct, .load_memory_ptr_direct => Relocation.Type.pageoff,
928 } });932 // else => unreachable,
933 // },
934 // } });
929 } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {935 } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
930 const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = data.atom_index, .file = null }).?;936 const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = data.atom_index, .file = null }).?;
931 const target = switch (tag) {937 const target = switch (tag) {
src/arch/x86_64/CodeGen.zig+38-26
...@@ -139,8 +139,10 @@ const Owner = union(enum) {...@@ -139,8 +139,10 @@ const Owner = union(enum) {
139 if (ctx.bin_file.cast(link.File.Elf)) |elf_file| {139 if (ctx.bin_file.cast(link.File.Elf)) |elf_file| {
140 return elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index);140 return elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index);
141 } else if (ctx.bin_file.cast(link.File.MachO)) |macho_file| {141 } else if (ctx.bin_file.cast(link.File.MachO)) |macho_file| {
142 const atom = try macho_file.getOrCreateAtomForDecl(decl_index);142 _ = macho_file;
143 return macho_file.getAtom(atom).getSymbolIndex().?;143 // const atom = try macho_file.getOrCreateAtomForDecl(decl_index);
144 // return macho_file.getAtom(atom).getSymbolIndex().?;
145 @panic("TODO getSymbolIndex");
144 } else if (ctx.bin_file.cast(link.File.Coff)) |coff_file| {146 } else if (ctx.bin_file.cast(link.File.Coff)) |coff_file| {
145 const atom = try coff_file.getOrCreateAtomForDecl(decl_index);147 const atom = try coff_file.getOrCreateAtomForDecl(decl_index);
146 return coff_file.getAtom(atom).getSymbolIndex().?;148 return coff_file.getAtom(atom).getSymbolIndex().?;
...@@ -153,9 +155,11 @@ const Owner = union(enum) {...@@ -153,9 +155,11 @@ const Owner = union(enum) {
153 return elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, lazy_sym) catch |err|155 return elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, lazy_sym) catch |err|
154 ctx.fail("{s} creating lazy symbol", .{@errorName(err)});156 ctx.fail("{s} creating lazy symbol", .{@errorName(err)});
155 } else if (ctx.bin_file.cast(link.File.MachO)) |macho_file| {157 } else if (ctx.bin_file.cast(link.File.MachO)) |macho_file| {
156 const atom = macho_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err|158 _ = macho_file;
157 return ctx.fail("{s} creating lazy symbol", .{@errorName(err)});159 // const atom = macho_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err|
158 return macho_file.getAtom(atom).getSymbolIndex().?;160 // return ctx.fail("{s} creating lazy symbol", .{@errorName(err)});
161 // return macho_file.getAtom(atom).getSymbolIndex().?;
162 @panic("TODO getSymbolIndex");
159 } else if (ctx.bin_file.cast(link.File.Coff)) |coff_file| {163 } else if (ctx.bin_file.cast(link.File.Coff)) |coff_file| {
160 const atom = coff_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err|164 const atom = coff_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err|
161 return ctx.fail("{s} creating lazy symbol", .{@errorName(err)});165 return ctx.fail("{s} creating lazy symbol", .{@errorName(err)});
...@@ -10951,10 +10955,12 @@ fn genCall(self: *Self, info: union(enum) {...@@ -10951,10 +10955,12 @@ fn genCall(self: *Self, info: union(enum) {
10951 try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym_index });10955 try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym_index });
10952 try self.asmRegister(.{ ._, .call }, .rax);10956 try self.asmRegister(.{ ._, .call }, .rax);
10953 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {10957 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
10954 const atom = try macho_file.getOrCreateAtomForDecl(func.owner_decl);10958 _ = macho_file;
10955 const sym_index = macho_file.getAtom(atom).getSymbolIndex().?;10959 @panic("TODO genCall");
10956 try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym_index });10960 // const atom = try macho_file.getOrCreateAtomForDecl(func.owner_decl);
10957 try self.asmRegister(.{ ._, .call }, .rax);10961 // const sym_index = macho_file.getAtom(atom).getSymbolIndex().?;
10962 // try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym_index });
10963 // try self.asmRegister(.{ ._, .call }, .rax);
10958 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {10964 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
10959 const atom_index = try p9.seeDecl(func.owner_decl);10965 const atom_index = try p9.seeDecl(func.owner_decl);
10960 const atom = p9.getAtom(atom_index);10966 const atom = p9.getAtom(atom_index);
...@@ -13814,11 +13820,15 @@ fn genExternSymbolRef(...@@ -13814,11 +13820,15 @@ fn genExternSymbolRef(
13814 _ = try self.addInst(.{13820 _ = try self.addInst(.{
13815 .tag = .call,13821 .tag = .call,
13816 .ops = .extern_fn_reloc,13822 .ops = .extern_fn_reloc,
13817 .data = .{ .reloc = .{13823 .data = .{
13818 .atom_index = atom_index,13824 .reloc = .{
13819 .sym_index = link.File.MachO.global_symbol_bit | global_index,13825 .atom_index = atom_index,
13820 } },13826 // .sym_index = link.File.MachO.global_symbol_bit | global_index,
13827 .sym_index = global_index,
13828 },
13829 },
13821 });13830 });
13831 @panic("TODO genExternSymbolRef");
13822 } else return self.fail("TODO implement calling extern functions", .{});13832 } else return self.fail("TODO implement calling extern functions", .{});
13823}13833}
1382413834
...@@ -13906,19 +13916,21 @@ fn genLazySymbolRef(...@@ -13906,19 +13916,21 @@ fn genLazySymbolRef(
13906 else => unreachable,13916 else => unreachable,
13907 }13917 }
13908 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {13918 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
13909 const atom_index = macho_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err|13919 _ = macho_file;
13910 return self.fail("{s} creating lazy symbol", .{@errorName(err)});13920 @panic("TODO genLazySymbolRef");
13911 const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?;13921 // const atom_index = macho_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err|
13912 switch (tag) {13922 // return self.fail("{s} creating lazy symbol", .{@errorName(err)});
13913 .lea, .call => try self.genSetReg(reg, Type.usize, .{ .lea_got = sym_index }),13923 // const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?;
13914 .mov => try self.genSetReg(reg, Type.usize, .{ .load_got = sym_index }),13924 // switch (tag) {
13915 else => unreachable,13925 // .lea, .call => try self.genSetReg(reg, Type.usize, .{ .lea_got = sym_index }),
13916 }13926 // .mov => try self.genSetReg(reg, Type.usize, .{ .load_got = sym_index }),
13917 switch (tag) {13927 // else => unreachable,
13918 .lea, .mov => {},13928 // }
13919 .call => try self.asmRegister(.{ ._, .call }, reg),13929 // switch (tag) {
13920 else => unreachable,13930 // .lea, .mov => {},
13921 }13931 // .call => try self.asmRegister(.{ ._, .call }, reg),
13932 // else => unreachable,
13933 // }
13922 } else {13934 } else {
13923 return self.fail("TODO implement genLazySymbol for x86_64 {s}", .{@tagName(self.bin_file.tag)});13935 return self.fail("TODO implement genLazySymbol for x86_64 {s}", .{@tagName(self.bin_file.tag)});
13924 }13936 }
src/arch/x86_64/Emit.zig+38-34
...@@ -49,21 +49,23 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -49,21 +49,23 @@ pub fn emitMir(emit: *Emit) Error!void {
49 .r_addend = -4,49 .r_addend = -4,
50 });50 });
51 } else if (emit.lower.bin_file.cast(link.File.MachO)) |macho_file| {51 } else if (emit.lower.bin_file.cast(link.File.MachO)) |macho_file| {
52 // Add relocation to the decl.52 _ = macho_file;
53 const atom_index =53 @panic("TODO emitMir");
54 macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?;54 // // Add relocation to the decl.
55 const target = if (link.File.MachO.global_symbol_bit & symbol.sym_index != 0)55 // const atom_index =
56 macho_file.getGlobalByIndex(link.File.MachO.global_symbol_mask & symbol.sym_index)56 // macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?;
57 else57 // const target = if (link.File.MachO.global_symbol_bit & symbol.sym_index != 0)
58 link.File.MachO.SymbolWithLoc{ .sym_index = symbol.sym_index };58 // macho_file.getGlobalByIndex(link.File.MachO.global_symbol_mask & symbol.sym_index)
59 try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{59 // else
60 .type = .branch,60 // link.File.MachO.SymbolWithLoc{ .sym_index = symbol.sym_index };
61 .target = target,61 // try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
62 .offset = end_offset - 4,62 // .type = .branch,
63 .addend = 0,63 // .target = target,
64 .pcrel = true,64 // .offset = end_offset - 4,
65 .length = 2,65 // .addend = 0,
66 });66 // .pcrel = true,
67 // .length = 2,
68 // });
67 } else if (emit.lower.bin_file.cast(link.File.Coff)) |coff_file| {69 } else if (emit.lower.bin_file.cast(link.File.Coff)) |coff_file| {
68 // Add relocation to the decl.70 // Add relocation to the decl.
69 const atom_index = coff_file.getAtomIndexForSymbol(71 const atom_index = coff_file.getAtomIndexForSymbol(
...@@ -157,25 +159,27 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -157,25 +159,27 @@ pub fn emitMir(emit: *Emit) Error!void {
157 => |symbol| if (emit.lower.bin_file.cast(link.File.Elf)) |_| {159 => |symbol| if (emit.lower.bin_file.cast(link.File.Elf)) |_| {
158 unreachable;160 unreachable;
159 } else if (emit.lower.bin_file.cast(link.File.MachO)) |macho_file| {161 } else if (emit.lower.bin_file.cast(link.File.MachO)) |macho_file| {
160 const atom_index =162 _ = macho_file;
161 macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?;163 @panic("TODO emitMir");
162 const target = if (link.File.MachO.global_symbol_bit & symbol.sym_index != 0)164 // const atom_index =
163 macho_file.getGlobalByIndex(link.File.MachO.global_symbol_mask & symbol.sym_index)165 // macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?;
164 else166 // const target = if (link.File.MachO.global_symbol_bit & symbol.sym_index != 0)
165 link.File.MachO.SymbolWithLoc{ .sym_index = symbol.sym_index };167 // macho_file.getGlobalByIndex(link.File.MachO.global_symbol_mask & symbol.sym_index)
166 try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{168 // else
167 .type = switch (lowered_relocs[0].target) {169 // link.File.MachO.SymbolWithLoc{ .sym_index = symbol.sym_index };
168 .linker_got => .got,170 // try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
169 .linker_direct => .signed,171 // .type = switch (lowered_relocs[0].target) {
170 .linker_tlv => .tlv,172 // .linker_got => .got,
171 else => unreachable,173 // .linker_direct => .signed,
172 },174 // .linker_tlv => .tlv,
173 .target = target,175 // else => unreachable,
174 .offset = @intCast(end_offset - 4),176 // },
175 .addend = 0,177 // .target = target,
176 .pcrel = true,178 // .offset = @intCast(end_offset - 4),
177 .length = 2,179 // .addend = 0,
178 });180 // .pcrel = true,
181 // .length = 2,
182 // });
179 } else if (emit.lower.bin_file.cast(link.File.Coff)) |coff_file| {183 } else if (emit.lower.bin_file.cast(link.File.Coff)) |coff_file| {
180 const atom_index = coff_file.getAtomIndexForSymbol(.{184 const atom_index = coff_file.getAtomIndexForSymbol(.{
181 .sym_index = symbol.atom_index,185 .sym_index = symbol.atom_index,
src/codegen.zig+13-11
...@@ -984,20 +984,22 @@ fn genDeclRef(...@@ -984,20 +984,22 @@ fn genDeclRef(
984 }984 }
985 return GenResult.mcv(.{ .load_symbol = sym.esym_index });985 return GenResult.mcv(.{ .load_symbol = sym.esym_index });
986 } else if (lf.cast(link.File.MachO)) |macho_file| {986 } else if (lf.cast(link.File.MachO)) |macho_file| {
987 _ = macho_file;
987 if (is_extern) {988 if (is_extern) {
988 // TODO make this part of getGlobalSymbol989 // TODO make this part of getGlobalSymbol
989 const name = zcu.intern_pool.stringToSlice(decl.name);990 // const name = zcu.intern_pool.stringToSlice(decl.name);
990 const sym_name = try std.fmt.allocPrint(gpa, "_{s}", .{name});991 // const sym_name = try std.fmt.allocPrint(gpa, "_{s}", .{name});
991 defer gpa.free(sym_name);992 // defer gpa.free(sym_name);
992 const global_index = try macho_file.addUndefined(sym_name, .{ .add_got = true });993 // const global_index = try macho_file.addUndefined(sym_name, .{ .add_got = true });
993 return GenResult.mcv(.{ .load_got = link.File.MachO.global_symbol_bit | global_index });994 // return GenResult.mcv(.{ .load_got = link.File.MachO.global_symbol_bit | global_index });
994 }
995 const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index);
996 const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?;
997 if (is_threadlocal) {
998 return GenResult.mcv(.{ .load_tlv = sym_index });
999 }995 }
1000 return GenResult.mcv(.{ .load_got = sym_index });996 // const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index);
997 // const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?;
998 // if (is_threadlocal) {
999 // return GenResult.mcv(.{ .load_tlv = sym_index });
1000 // }
1001 // return GenResult.mcv(.{ .load_got = sym_index });
1002 @panic("TODO genDeclRef");
1001 } else if (lf.cast(link.File.Coff)) |coff_file| {1003 } else if (lf.cast(link.File.Coff)) |coff_file| {
1002 if (is_extern) {1004 if (is_extern) {
1003 const name = zcu.intern_pool.stringToSlice(decl.name);1005 const name = zcu.intern_pool.stringToSlice(decl.name);
src/link/MachO.zig+516-5090
...@@ -7,8 +7,6 @@ llvm_object: ?*LlvmObject = null,...@@ -7,8 +7,6 @@ llvm_object: ?*LlvmObject = null,
7/// Debug symbols bundle (or dSym).7/// Debug symbols bundle (or dSym).
8d_sym: ?DebugSymbols = null,8d_sym: ?DebugSymbols = null,
99
10mode: Mode,
11
12dyld_info_cmd: macho.dyld_info_command = .{},10dyld_info_cmd: macho.dyld_info_command = .{},
13symtab_cmd: macho.symtab_command = .{},11symtab_cmd: macho.symtab_command = .{},
14dysymtab_cmd: macho.dysymtab_command = .{},12dysymtab_cmd: macho.dysymtab_command = .{},
...@@ -17,12 +15,6 @@ data_in_code_cmd: macho.linkedit_data_command = .{ .cmd = .DATA_IN_CODE },...@@ -17,12 +15,6 @@ data_in_code_cmd: macho.linkedit_data_command = .{ .cmd = .DATA_IN_CODE },
17uuid_cmd: macho.uuid_command = .{ .uuid = [_]u8{0} ** 16 },15uuid_cmd: macho.uuid_command = .{ .uuid = [_]u8{0} ** 16 },
18codesig_cmd: macho.linkedit_data_command = .{ .cmd = .CODE_SIGNATURE },16codesig_cmd: macho.linkedit_data_command = .{ .cmd = .CODE_SIGNATURE },
1917
20objects: std.ArrayListUnmanaged(Object) = .{},
21archives: std.ArrayListUnmanaged(Archive) = .{},
22dylibs: std.ArrayListUnmanaged(Dylib) = .{},
23dylibs_map: std.StringHashMapUnmanaged(u16) = .{},
24referenced_dylibs: std.AutoArrayHashMapUnmanaged(u16, void) = .{},
25
26segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},18segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
27sections: std.MultiArrayList(Section) = .{},19sections: std.MultiArrayList(Section) = .{},
2820
...@@ -48,93 +40,11 @@ got_section_index: ?u8 = null,...@@ -48,93 +40,11 @@ got_section_index: ?u8 = null,
48la_symbol_ptr_section_index: ?u8 = null,40la_symbol_ptr_section_index: ?u8 = null,
49tlv_ptr_section_index: ?u8 = null,41tlv_ptr_section_index: ?u8 = null,
5042
51locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
52globals: std.ArrayListUnmanaged(SymbolWithLoc) = .{},
53resolver: std.StringHashMapUnmanaged(u32) = .{},
54unresolved: std.AutoArrayHashMapUnmanaged(u32, void) = .{},
55
56locals_free_list: std.ArrayListUnmanaged(u32) = .{},
57globals_free_list: std.ArrayListUnmanaged(u32) = .{},
58
59dyld_stub_binder_index: ?u32 = null,
60dyld_private_atom_index: ?Atom.Index = null,
61
62strtab: StringTable = .{},43strtab: StringTable = .{},
6344
64got_table: TableSection(SymbolWithLoc) = .{},
65stub_table: TableSection(SymbolWithLoc) = .{},
66tlv_ptr_table: TableSection(SymbolWithLoc) = .{},
67
68thunk_table: std.AutoHashMapUnmanaged(Atom.Index, thunks.Thunk.Index) = .{},
69thunks: std.ArrayListUnmanaged(thunks.Thunk) = .{},
70
71segment_table_dirty: bool = false,
72got_table_count_dirty: bool = false,
73got_table_contents_dirty: bool = false,
74stub_table_count_dirty: bool = false,
75stub_table_contents_dirty: bool = false,
76stub_helper_preamble_allocated: bool = false,
77
78/// List of atoms that are either synthetic or map directly to the Zig source program.45/// List of atoms that are either synthetic or map directly to the Zig source program.
79atoms: std.ArrayListUnmanaged(Atom) = .{},46atoms: std.ArrayListUnmanaged(Atom) = .{},
8047
81/// Table of atoms indexed by the symbol index.
82atom_by_index_table: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{},
83
84/// Table of unnamed constants associated with a parent `Decl`.
85/// We store them here so that we can free the constants whenever the `Decl`
86/// needs updating or is freed.
87///
88/// For example,
89///
90/// ```zig
91/// const Foo = struct{
92/// a: u8,
93/// };
94///
95/// pub fn main() void {
96/// var foo = Foo{ .a = 1 };
97/// _ = foo;
98/// }
99/// ```
100///
101/// value assigned to label `foo` is an unnamed constant belonging/associated
102/// with `Decl` `main`, and lives as long as that `Decl`.
103unnamed_const_atoms: UnnamedConstTable = .{},
104anon_decls: AnonDeclTable = .{},
105
106/// A table of relocations indexed by the owning them `Atom`.
107/// Note that once we refactor `Atom`'s lifetime and ownership rules,
108/// this will be a table indexed by index into the list of Atoms.
109relocs: RelocationTable = .{},
110/// TODO I do not have time to make this right but this will go once
111/// MachO linker is rewritten more-or-less to feature the same resolution
112/// mechanism as the ELF linker.
113actions: ActionTable = .{},
114
115/// A table of rebases indexed by the owning them `Atom`.
116/// Note that once we refactor `Atom`'s lifetime and ownership rules,
117/// this will be a table indexed by index into the list of Atoms.
118rebases: RebaseTable = .{},
119
120/// A table of bindings indexed by the owning them `Atom`.
121/// Note that once we refactor `Atom`'s lifetime and ownership rules,
122/// this will be a table indexed by index into the list of Atoms.
123bindings: BindingTable = .{},
124
125/// Table of tracked LazySymbols.
126lazy_syms: LazySymbolTable = .{},
127
128/// Table of tracked Decls.
129decls: DeclTable = .{},
130
131/// Table of threadlocal variables descriptors.
132/// They are emitted in the `__thread_vars` section.
133tlv_table: TlvSymbolTable = .{},
134
135/// Hot-code swapping state.
136hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{},
137
138sdk_layout: ?SdkLayout,48sdk_layout: ?SdkLayout,
139/// Size of the __PAGEZERO segment.49/// Size of the __PAGEZERO segment.
140pagezero_vmsize: u64,50pagezero_vmsize: u64,
...@@ -152,6 +62,9 @@ install_name: ?[]const u8,...@@ -152,6 +62,9 @@ install_name: ?[]const u8,
152entitlements: ?[]const u8,62entitlements: ?[]const u8,
153compatibility_version: ?std.SemanticVersion,63compatibility_version: ?std.SemanticVersion,
15464
65/// Hot-code swapping state.
66hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{},
67
155/// When adding a new field, remember to update `hashAddFrameworks`.68/// When adding a new field, remember to update `hashAddFrameworks`.
156pub const Framework = struct {69pub const Framework = struct {
157 needed: bool = false,70 needed: bool = false,
...@@ -183,24 +96,18 @@ pub fn createEmpty(...@@ -183,24 +96,18 @@ pub fn createEmpty(
183) !*MachO {96) !*MachO {
184 const target = comp.root_mod.resolved_target.result;97 const target = comp.root_mod.resolved_target.result;
185 assert(target.ofmt == .macho);98 assert(target.ofmt == .macho);
186 const use_llvm = comp.config.use_llvm;99
187 const gpa = comp.gpa;100 const gpa = comp.gpa;
101 const use_llvm = comp.config.use_llvm;
102 const opt_zcu = comp.module;
188 const optimize_mode = comp.root_mod.optimize_mode;103 const optimize_mode = comp.root_mod.optimize_mode;
189 const output_mode = comp.config.output_mode;104 const output_mode = comp.config.output_mode;
190 const link_mode = comp.config.link_mode;105 const link_mode = comp.config.link_mode;
191106
192 // TODO: get rid of zld mode
193 const mode: Mode = if (use_llvm or !comp.config.have_zcu or comp.cache_use == .whole)
194 .zld
195 else
196 .incremental;
197
198 // If using "zld mode" to link, this code should produce an object file so that it
199 // can be passed to "zld mode". TODO: get rid of "zld mode".
200 // If using LLVM to generate the object file for the zig compilation unit,107 // If using LLVM to generate the object file for the zig compilation unit,
201 // we need a place to put the object file so that it can be subsequently108 // we need a place to put the object file so that it can be subsequently
202 // handled.109 // handled.
203 const zcu_object_sub_path = if (mode != .zld and !use_llvm)110 const zcu_object_sub_path = if (!use_llvm)
204 null111 null
205 else112 else
206 try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path});113 try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path});
...@@ -221,7 +128,6 @@ pub fn createEmpty(...@@ -221,7 +128,6 @@ pub fn createEmpty(
221 .build_id = options.build_id,128 .build_id = options.build_id,
222 .rpath_list = options.rpath_list,129 .rpath_list = options.rpath_list,
223 },130 },
224 .mode = mode,
225 .pagezero_vmsize = options.pagezero_size orelse default_pagezero_vmsize,131 .pagezero_vmsize = options.pagezero_size orelse default_pagezero_vmsize,
226 .headerpad_size = options.headerpad_size orelse default_headerpad_size,132 .headerpad_size = options.headerpad_size orelse default_headerpad_size,
227 .headerpad_max_install_names = options.headerpad_max_install_names,133 .headerpad_max_install_names = options.headerpad_max_install_names,
...@@ -243,62 +149,48 @@ pub fn createEmpty(...@@ -243,62 +149,48 @@ pub fn createEmpty(
243 }149 }
244 errdefer self.base.destroy();150 errdefer self.base.destroy();
245151
246 log.debug("selected linker mode '{s}'", .{@tagName(self.mode)});152 self.base.file = try emit.directory.handle.createFile(emit.sub_path, .{
247
248 if (mode == .zld) {
249 // TODO: get rid of zld mode
250 return self;
251 }
252
253 const file = try emit.directory.handle.createFile(emit.sub_path, .{
254 .truncate = true,153 .truncate = true,
255 .read = true,154 .read = true,
256 .mode = link.File.determineMode(false, output_mode, link_mode),155 .mode = link.File.determineMode(false, output_mode, link_mode),
257 });156 });
258 self.base.file = file;
259157
260 if (comp.config.debug_format != .strip and comp.module != null) {158 // Index 0 is always a null symbol.
261 // Create dSYM bundle.159 // try self.locals.append(gpa, null_sym);
262 log.debug("creating {s}.dSYM bundle", .{emit.sub_path});160 try self.strtab.buffer.append(gpa, 0);
263161
264 const d_sym_path = try std.fmt.allocPrint(162 // TODO: init
265 arena,
266 "{s}.dSYM" ++ fs.path.sep_str ++ "Contents" ++ fs.path.sep_str ++ "Resources" ++ fs.path.sep_str ++ "DWARF",
267 .{emit.sub_path},
268 );
269163
270 var d_sym_bundle = try emit.directory.handle.makeOpenPath(d_sym_path, .{});164 if (opt_zcu) |zcu| {
271 defer d_sym_bundle.close();165 if (!use_llvm) {
166 _ = zcu;
167 // TODO: create .zig_object
272168
273 const d_sym_file = try d_sym_bundle.createFile(emit.sub_path, .{169 if (comp.config.debug_format != .strip) {
274 .truncate = false,170 // Create dSYM bundle.
275 .read = true,171 log.debug("creating {s}.dSYM bundle", .{emit.sub_path});
276 });
277172
278 self.d_sym = .{173 const d_sym_path = try std.fmt.allocPrint(
279 .allocator = gpa,174 arena,
280 .dwarf = link.File.Dwarf.init(&self.base, .dwarf32),175 "{s}.dSYM" ++ fs.path.sep_str ++ "Contents" ++ fs.path.sep_str ++ "Resources" ++ fs.path.sep_str ++ "DWARF",
281 .file = d_sym_file,176 .{emit.sub_path},
282 };177 );
283 }
284178
285 // Index 0 is always a null symbol.179 var d_sym_bundle = try emit.directory.handle.makeOpenPath(d_sym_path, .{});
286 try self.locals.append(gpa, .{180 defer d_sym_bundle.close();
287 .n_strx = 0,
288 .n_type = 0,
289 .n_sect = 0,
290 .n_desc = 0,
291 .n_value = 0,
292 });
293 try self.strtab.buffer.append(gpa, 0);
294181
295 try self.populateMissingMetadata(.{182 const d_sym_file = try d_sym_bundle.createFile(emit.sub_path, .{
296 .symbol_count_hint = options.symbol_count_hint,183 .truncate = false,
297 .program_code_size_hint = options.program_code_size_hint,184 .read = true,
298 });185 });
299186
300 if (self.d_sym) |*d_sym| {187 self.d_sym = .{
301 try d_sym.populateMissingMetadata(self);188 .allocator = gpa,
189 .dwarf = link.File.Dwarf.init(&self.base, .dwarf32),
190 .file = d_sym_file,
191 };
192 }
193 }
302 }194 }
303195
304 return self;196 return self;
...@@ -316,26 +208,8 @@ pub fn open(...@@ -316,26 +208,8 @@ pub fn open(
316}208}
317209
318pub fn flush(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {210pub fn flush(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {
319 const comp = self.base.comp;211 // TODO: what else should we do in flush? Is it actually needed at all?
320 const gpa = comp.gpa;212 try self.flushModule(arena, prog_node);
321 const output_mode = comp.config.output_mode;
322
323 if (output_mode == .Lib and comp.config.link_mode == .Static) {
324 if (build_options.have_llvm) {
325 return self.base.linkAsArchive(arena, prog_node);
326 } else {
327 try comp.link_errors.ensureUnusedCapacity(gpa, 1);
328 comp.link_errors.appendAssumeCapacity(.{
329 .msg = try gpa.dupe(u8, "TODO: non-LLVM archiver for MachO object files"),
330 });
331 return error.FlushFailure;
332 }
333 }
334
335 switch (self.mode) {
336 .zld => return zld.linkWithZld(self, arena, prog_node),
337 .incremental => return self.flushModule(arena, prog_node),
338 }
339}213}
340214
341pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {215pub 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...@@ -344,261 +218,40 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
344218
345 const comp = self.base.comp;219 const comp = self.base.comp;
346 const gpa = comp.gpa;220 const gpa = comp.gpa;
221 _ = gpa;
347222
348 if (self.llvm_object) |llvm_object| {223 if (self.llvm_object) |llvm_object| {
349 try self.base.emitLlvmObject(arena, llvm_object, prog_node);224 try self.base.emitLlvmObject(arena, llvm_object, prog_node);
350 return;
351 }225 }
352226
353 var sub_prog_node = prog_node.start("MachO Flush", 0);227 var sub_prog_node = prog_node.start("MachO Flush", 0);
354 sub_prog_node.activate();228 sub_prog_node.activate();
355 defer sub_prog_node.end();229 defer sub_prog_node.end();
356230
357 const output_mode = comp.config.output_mode;
358 const module = comp.module orelse return error.LinkingWithoutZigSourceUnimplemented;
359 const target = comp.root_mod.resolved_target.result;231 const target = comp.root_mod.resolved_target.result;
360232 _ = target;
361 if (self.lazy_syms.getPtr(.none)) |metadata| {233 const directory = self.base.emit.directory;
362 // Most lazy symbols can be updated on first use, but234 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path});
363 // anyerror needs to wait for everything to be flushed.235 const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: {
364 if (metadata.text_state != .unused) self.updateLazySymbolAtom(236 if (fs.path.dirname(full_out_path)) |dirname| {
365 File.LazySymbol.initDecl(.code, null, module),237 break :blk try fs.path.join(arena, &.{ dirname, path });
366 metadata.text_atom,238 } else {
367 self.text_section_index.?,239 break :blk path;
368 ) catch |err| return switch (err) {
369 error.CodegenFail => error.FlushFailure,
370 else => |e| e,
371 };
372 if (metadata.data_const_state != .unused) self.updateLazySymbolAtom(
373 File.LazySymbol.initDecl(.const_data, null, module),
374 metadata.data_const_atom,
375 self.data_const_section_index.?,
376 ) catch |err| return switch (err) {
377 error.CodegenFail => error.FlushFailure,
378 else => |e| e,
379 };
380 }
381 for (self.lazy_syms.values()) |*metadata| {
382 if (metadata.text_state != .unused) metadata.text_state = .flushed;
383 if (metadata.data_const_state != .unused) metadata.data_const_state = .flushed;
384 }
385
386 if (self.d_sym) |*d_sym| {
387 try d_sym.dwarf.flushModule(module);
388 }
389
390 var libs = std.StringArrayHashMap(link.SystemLib).init(arena);
391 try self.resolveLibSystem(arena, comp, &libs);
392
393 self.base.releaseLock();
394
395 for (self.dylibs.items) |*dylib| {
396 dylib.deinit(gpa);
397 }
398 self.dylibs.clearRetainingCapacity();
399 self.dylibs_map.clearRetainingCapacity();
400 self.referenced_dylibs.clearRetainingCapacity();
401
402 var dependent_libs = std.fifo.LinearFifo(DylibReExportInfo, .Dynamic).init(arena);
403
404 for (libs.keys(), libs.values()) |path, lib| {
405 const in_file = try std.fs.cwd().openFile(path, .{});
406 defer in_file.close();
407
408 var parse_ctx = ParseErrorCtx.init(gpa);
409 defer parse_ctx.deinit();
410
411 self.parseLibrary(
412 in_file,
413 path,
414 lib,
415 false,
416 false,
417 null,
418 &dependent_libs,
419 &parse_ctx,
420 ) catch |err| try self.handleAndReportParseError(path, err, &parse_ctx);
421 }
422
423 try self.parseDependentLibs(&dependent_libs);
424
425 try self.resolveSymbols();
426
427 if (self.getEntryPoint() == null) {
428 comp.link_error_flags.no_entry_point_found = true;
429 }
430 if (self.unresolved.count() > 0) {
431 try self.reportUndefined();
432 return error.FlushFailure;
433 }
434
435 {
436 var it = self.actions.iterator();
437 while (it.next()) |entry| {
438 const global_index = entry.key_ptr.*;
439 const global = self.globals.items[global_index];
440 const flags = entry.value_ptr.*;
441 if (flags.add_got) try self.addGotEntry(global);
442 if (flags.add_stub) try self.addStubEntry(global);
443 }
444 }
445
446 try self.createDyldPrivateAtom();
447 try self.writeStubHelperPreamble();
448
449 if (output_mode == .Exe and self.getEntryPoint() != null) {
450 const global = self.getEntryPoint().?;
451 if (self.getSymbol(global).undf()) {
452 // We do one additional check here in case the entry point was found in one of the dylibs.
453 // (I actually have no idea what this would imply but it is a possible outcome and so we
454 // support it.)
455 try self.addStubEntry(global);
456 }
457 }
458
459 try self.allocateSpecialSymbols();
460
461 for (self.relocs.keys()) |atom_index| {
462 const relocs = self.relocs.get(atom_index).?;
463 const needs_update = for (relocs.items) |reloc| {
464 if (reloc.dirty) break true;
465 } else false;
466
467 if (!needs_update) continue;
468
469 const atom = self.getAtom(atom_index);
470 const sym = atom.getSymbol(self);
471 const section = self.sections.get(sym.n_sect - 1).header;
472 const file_offset = section.offset + sym.n_value - section.addr;
473
474 var code = std.ArrayList(u8).init(gpa);
475 defer code.deinit();
476 try code.resize(math.cast(usize, atom.size) orelse return error.Overflow);
477
478 const amt = try self.base.file.?.preadAll(code.items, file_offset);
479 if (amt != code.items.len) return error.InputOutput;
480
481 try self.writeAtom(atom_index, code.items);
482 }
483
484 // Update GOT if it got moved in memory.
485 if (self.got_table_contents_dirty) {
486 for (self.got_table.entries.items, 0..) |entry, i| {
487 if (!self.got_table.lookup.contains(entry)) continue;
488 // TODO: write all in one go rather than incrementally.
489 try self.writeOffsetTableEntry(i);
490 }
491 self.got_table_contents_dirty = false;
492 }
493
494 // Update stubs if we moved any section in memory.
495 // TODO: we probably don't need to update all sections if only one got moved.
496 if (self.stub_table_contents_dirty) {
497 for (self.stub_table.entries.items, 0..) |entry, i| {
498 if (!self.stub_table.lookup.contains(entry)) continue;
499 // TODO: write all in one go rather than incrementally.
500 try self.writeStubTableEntry(i);
501 }
502 self.stub_table_contents_dirty = false;
503 }
504
505 if (build_options.enable_logging) {
506 self.logSymtab();
507 self.logSections();
508 self.logAtoms();
509 }
510
511 try self.writeLinkeditSegmentData();
512
513 var codesig: ?CodeSignature = if (self.requiresCodeSignature()) blk: {
514 // Preallocate space for the code signature.
515 // We need to do this at this stage so that we have the load commands with proper values
516 // written out to the file.
517 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
518 // where the code signature goes into.
519 var codesig = CodeSignature.init(getPageSize(target.cpu.arch));
520 codesig.code_directory.ident = self.base.emit.sub_path;
521 if (self.entitlements) |path| {
522 try codesig.addEntitlements(gpa, path);
523 }240 }
524 try self.writeCodeSignaturePadding(&codesig);
525 break :blk codesig;
526 } else null;241 } else null;
527 defer if (codesig) |*csig| csig.deinit(gpa);242 _ = module_obj_path;
528
529 // Write load commands
530 var lc_buffer = std.ArrayList(u8).init(arena);
531 const lc_writer = lc_buffer.writer();
532
533 try self.writeSegmentHeaders(lc_writer);
534 try lc_writer.writeStruct(self.dyld_info_cmd);
535 try lc_writer.writeStruct(self.symtab_cmd);
536 try lc_writer.writeStruct(self.dysymtab_cmd);
537 try load_commands.writeDylinkerLC(lc_writer);
538
539 switch (output_mode) {
540 .Exe => blk: {
541 const seg_id = self.header_segment_cmd_index.?;
542 const seg = self.segments.items[seg_id];
543 const global = self.getEntryPoint() orelse break :blk;
544 const sym = self.getSymbol(global);
545
546 const addr: u64 = if (sym.undf())
547 // In this case, the symbol has been resolved in one of dylibs and so we point
548 // to the stub as its vmaddr value.
549 self.getStubsEntryAddress(global).?
550 else
551 sym.n_value;
552
553 try lc_writer.writeStruct(macho.entry_point_command{
554 .entryoff = @as(u32, @intCast(addr - seg.vmaddr)),
555 .stacksize = self.base.stack_size,
556 });
557 },
558 .Lib => if (comp.config.link_mode == .Dynamic) {
559 try load_commands.writeDylibIdLC(self, lc_writer);
560 },
561 else => {},
562 }
563243
564 try load_commands.writeRpathLCs(self, lc_writer);244 // --verbose-link
565 try lc_writer.writeStruct(macho.source_version_command{245 if (comp.verbose_link) try self.dumpArgv(comp);
566 .version = 0,
567 });
568 {
569 const platform = Platform.fromTarget(target);
570 const sdk_version: ?std.SemanticVersion = load_commands.inferSdkVersion(self);
571 if (platform.isBuildVersionCompatible()) {
572 try load_commands.writeBuildVersionLC(platform, sdk_version, lc_writer);
573 } else if (platform.isVersionMinCompatible()) {
574 try load_commands.writeVersionMinLC(platform, sdk_version, lc_writer);
575 }
576 }
577
578 const uuid_cmd_offset = @sizeOf(macho.mach_header_64) + @as(u32, @intCast(lc_buffer.items.len));
579 try lc_writer.writeStruct(self.uuid_cmd);
580
581 try load_commands.writeLoadDylibLCs(self.dylibs.items, self.referenced_dylibs.keys(), lc_writer);
582
583 if (codesig != null) {
584 try lc_writer.writeStruct(self.codesig_cmd);
585 }
586246
587 const ncmds = load_commands.calcNumOfLCs(lc_buffer.items);247 @panic("TODO");
588 try self.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64));248}
589 try self.writeHeader(ncmds, @as(u32, @intCast(lc_buffer.items.len)));
590 try self.writeUuid(comp, uuid_cmd_offset, codesig != null);
591
592 if (codesig) |*csig| {
593 try self.writeCodeSignature(comp, csig); // code signing always comes last
594 const emit = self.base.emit;
595 try invalidateKernelCache(emit.directory.handle, emit.sub_path);
596 }
597249
598 if (self.d_sym) |*d_sym| {250/// --verbose-link output
599 // Flush debug symbols bundle.251fn dumpArgv(self: *MachO, comp: *Compilation) !void {
600 try d_sym.flushModule(self);252 _ = self;
601 }253 _ = comp;
254 @panic("TODO dumpArgv");
602}255}
603256
604/// XNU starting with Big Sur running on arm64 is caching inodes of running binaries.257/// XNU starting with Big Sur running on arm64 is caching inodes of running binaries.
...@@ -726,4441 +379,338 @@ pub fn parsePositional(...@@ -726,4441 +379,338 @@ pub fn parsePositional(
726 const tracy = trace(@src());379 const tracy = trace(@src());
727 defer tracy.end();380 defer tracy.end();
728381
729 if (Object.isObject(file)) {382 _ = self;
730 try self.parseObject(file, path, ctx);383 _ = file;
731 } else {384 _ = path;
732 try self.parseLibrary(file, path, .{385 _ = must_link;
733 .path = null,386 _ = dependent_libs;
734 .needed = false,387 _ = ctx;
735 .weak = false,
736 }, must_link, false, null, dependent_libs, ctx);
737 }
738}388}
739389
740fn parseObject(390pub fn deinit(self: *MachO) void {
741 self: *MachO,
742 file: std.fs.File,
743 path: []const u8,
744 ctx: *ParseErrorCtx,
745) ParseError!void {
746 const tracy = trace(@src());
747 defer tracy.end();
748
749 const gpa = self.base.comp.gpa;391 const gpa = self.base.comp.gpa;
750 const target = self.base.comp.root_mod.resolved_target.result;
751 const mtime: u64 = mtime: {
752 const stat = file.stat() catch break :mtime 0;
753 break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));
754 };
755 const file_stat = try file.stat();
756 const file_size = math.cast(usize, file_stat.size) orelse return error.Overflow;
757 const contents = try file.readToEndAllocOptions(gpa, file_size, file_size, @alignOf(u64), null);
758
759 var object = Object{
760 .name = try gpa.dupe(u8, path),
761 .mtime = mtime,
762 .contents = contents,
763 };
764 errdefer object.deinit(gpa);
765 try object.parse(gpa);
766392
767 const detected_cpu_arch: std.Target.Cpu.Arch = switch (object.header.cputype) {393 if (self.llvm_object) |llvm_object| llvm_object.deinit();
768 macho.CPU_TYPE_ARM64 => .aarch64,
769 macho.CPU_TYPE_X86_64 => .x86_64,
770 else => unreachable,
771 };
772 const detected_platform = object.getPlatform();
773 const this_cpu_arch = target.cpu.arch;
774 const this_platform = Platform.fromTarget(target);
775394
776 if (this_cpu_arch != detected_cpu_arch or395 if (self.d_sym) |*d_sym| {
777 (detected_platform != null and !detected_platform.?.eqlTarget(this_platform)))396 d_sym.deinit();
778 {
779 const platform = detected_platform orelse this_platform;
780 try ctx.detected_targets.append(try platform.allocPrintTarget(ctx.arena(), detected_cpu_arch));
781 return error.InvalidTarget;
782 }397 }
783398
784 try self.objects.append(gpa, object);399 self.strtab.deinit(gpa);
785}
786
787pub fn parseLibrary(
788 self: *MachO,
789 file: std.fs.File,
790 path: []const u8,
791 lib: link.SystemLib,
792 must_link: bool,
793 is_dependent: bool,
794 reexport_info: ?DylibReExportInfo,
795 dependent_libs: anytype,
796 ctx: *ParseErrorCtx,
797) ParseError!void {
798 const tracy = trace(@src());
799 defer tracy.end();
800400
801 const target = self.base.comp.root_mod.resolved_target.result;401 self.segments.deinit(gpa);
802402
803 if (fat.isFatLibrary(file)) {403 for (self.sections.items(.free_list)) |*list| {
804 const offset = try self.parseFatLibrary(file, target.cpu.arch, ctx);404 list.deinit(gpa);
805 try file.seekTo(offset);
806
807 if (Archive.isArchive(file, offset)) {
808 try self.parseArchive(path, offset, must_link, ctx);
809 } else if (Dylib.isDylib(file, offset)) {
810 try self.parseDylib(file, path, offset, dependent_libs, .{
811 .needed = lib.needed,
812 .weak = lib.weak,
813 .dependent = is_dependent,
814 .reexport_info = reexport_info,
815 }, ctx);
816 } else return error.UnknownFileType;
817 } else if (Archive.isArchive(file, 0)) {
818 try self.parseArchive(path, 0, must_link, ctx);
819 } else if (Dylib.isDylib(file, 0)) {
820 try self.parseDylib(file, path, 0, dependent_libs, .{
821 .needed = lib.needed,
822 .weak = lib.weak,
823 .dependent = is_dependent,
824 .reexport_info = reexport_info,
825 }, ctx);
826 } else {
827 self.parseLibStub(file, path, dependent_libs, .{
828 .needed = lib.needed,
829 .weak = lib.weak,
830 .dependent = is_dependent,
831 .reexport_info = reexport_info,
832 }, ctx) catch |err| switch (err) {
833 error.NotLibStub, error.UnexpectedToken => return error.UnknownFileType,
834 else => |e| return e,
835 };
836 }405 }
406 self.sections.deinit(gpa);
837}407}
838408
839pub fn parseFatLibrary(409fn freeAtom(self: *MachO, atom_index: Atom.Index) void {
840 self: *MachO,
841 file: std.fs.File,
842 cpu_arch: std.Target.Cpu.Arch,
843 ctx: *ParseErrorCtx,
844) ParseError!u64 {
845 const gpa = self.base.comp.gpa;410 const gpa = self.base.comp.gpa;
411 log.debug("freeAtom {d}", .{atom_index});
846412
847 const fat_archs = try fat.parseArchs(gpa, file);413 // Remove any relocs and base relocs associated with this Atom
848 defer gpa.free(fat_archs);414 Atom.freeRelocations(self, atom_index);
849415
850 const offset = for (fat_archs) |arch| {416 const atom = self.getAtom(atom_index);
851 if (arch.tag == cpu_arch) break arch.offset;417 const sect_id = atom.getSymbol(self).n_sect - 1;
852 } else {418 const free_list = &self.sections.items(.free_list)[sect_id];
853 try ctx.detected_targets.ensureUnusedCapacity(fat_archs.len);419 var already_have_free_list_node = false;
854 for (fat_archs) |arch| {420 {
855 ctx.detected_targets.appendAssumeCapacity(try ctx.arena().dupe(u8, @tagName(arch.tag)));421 var i: usize = 0;
422 // TODO turn free_list into a hash map
423 while (i < free_list.items.len) {
424 if (free_list.items[i] == atom_index) {
425 _ = free_list.swapRemove(i);
426 continue;
427 }
428 if (free_list.items[i] == atom.prev_index) {
429 already_have_free_list_node = true;
430 }
431 i += 1;
856 }432 }
857 return error.InvalidTargetFatLibrary;433 }
858 };
859 return offset;
860}
861
862fn parseArchive(
863 self: *MachO,
864 path: []const u8,
865 fat_offset: u64,
866 must_link: bool,
867 ctx: *ParseErrorCtx,
868) ParseError!void {
869 const gpa = self.base.comp.gpa;
870 const target = self.base.comp.root_mod.resolved_target.result;
871
872 // We take ownership of the file so that we can store it for the duration of symbol resolution.
873 // TODO we shouldn't need to do that and could pre-parse the archive like we do for zld/ELF?
874 const file = try std.fs.cwd().openFile(path, .{});
875 try file.seekTo(fat_offset);
876
877 var archive = Archive{
878 .file = file,
879 .fat_offset = fat_offset,
880 .name = try gpa.dupe(u8, path),
881 };
882 errdefer archive.deinit(gpa);
883434
884 try archive.parse(gpa, file.reader());435 const maybe_last_atom_index = &self.sections.items(.last_atom_index)[sect_id];
436 if (maybe_last_atom_index.*) |last_atom_index| {
437 if (last_atom_index == atom_index) {
438 if (atom.prev_index) |prev_index| {
439 // TODO shrink the section size here
440 maybe_last_atom_index.* = prev_index;
441 } else {
442 maybe_last_atom_index.* = null;
443 }
444 }
445 }
885446
886 // Verify arch and platform447 if (atom.prev_index) |prev_index| {
887 if (archive.toc.values().len > 0) {448 const prev = self.getAtomPtr(prev_index);
888 const offsets = archive.toc.values()[0].items;449 prev.next_index = atom.next_index;
889 assert(offsets.len > 0);
890 const off = offsets[0];
891 var object = try archive.parseObject(gpa, off); // TODO we are doing all this work to pull the header only!
892 defer object.deinit(gpa);
893450
894 const detected_cpu_arch: std.Target.Cpu.Arch = switch (object.header.cputype) {451 if (!already_have_free_list_node and prev.*.freeListEligible(self)) {
895 macho.CPU_TYPE_ARM64 => .aarch64,452 // The free list is heuristics, it doesn't have to be perfect, so we can ignore
896 macho.CPU_TYPE_X86_64 => .x86_64,453 // the OOM here.
897 else => unreachable,454 free_list.append(gpa, prev_index) catch {};
898 };
899 const detected_platform = object.getPlatform();
900 const this_cpu_arch = target.cpu.arch;
901 const this_platform = Platform.fromTarget(target);
902
903 if (this_cpu_arch != detected_cpu_arch or
904 (detected_platform != null and !detected_platform.?.eqlTarget(this_platform)))
905 {
906 const platform = detected_platform orelse this_platform;
907 try ctx.detected_targets.append(try platform.allocPrintTarget(gpa, detected_cpu_arch));
908 return error.InvalidTarget;
909 }455 }
456 } else {
457 self.getAtomPtr(atom_index).prev_index = null;
910 }458 }
911459
912 if (must_link) {460 if (atom.next_index) |next_index| {
913 // Get all offsets from the ToC461 self.getAtomPtr(next_index).prev_index = atom.prev_index;
914 var offsets = std.AutoArrayHashMap(u32, void).init(gpa);
915 defer offsets.deinit();
916 for (archive.toc.values()) |offs| {
917 for (offs.items) |off| {
918 _ = try offsets.getOrPut(off);
919 }
920 }
921 for (offsets.keys()) |off| {
922 const object = try archive.parseObject(gpa, off);
923 try self.objects.append(gpa, object);
924 }
925 } else {462 } else {
926 try self.archives.append(gpa, archive);463 self.getAtomPtr(atom_index).next_index = null;
927 }464 }
928}
929465
930pub const DylibReExportInfo = struct {466 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
931 id: Dylib.Id,467 const sym_index = atom.getSymbolIndex().?;
932 parent: u16,
933};
934468
935const DylibOpts = struct {469 self.locals_free_list.append(gpa, sym_index) catch {};
936 reexport_info: ?DylibReExportInfo = null,
937 dependent: bool = false,
938 needed: bool = false,
939 weak: bool = false,
940};
941470
942fn parseDylib(471 // Try freeing GOT atom if this decl had one
943 self: *MachO,472 self.got_table.freeEntry(gpa, .{ .sym_index = sym_index });
944 file: std.fs.File,
945 path: []const u8,
946 offset: u64,
947 dependent_libs: anytype,
948 dylib_options: DylibOpts,
949 ctx: *ParseErrorCtx,
950) ParseError!void {
951 const gpa = self.base.comp.gpa;
952 const target = self.base.comp.root_mod.resolved_target.result;
953 const file_stat = try file.stat();
954 const file_size = math.cast(usize, file_stat.size - offset) orelse return error.Overflow;
955
956 const contents = try file.readToEndAllocOptions(gpa, file_size, file_size, @alignOf(u64), null);
957 defer gpa.free(contents);
958
959 var dylib = Dylib{ .path = try gpa.dupe(u8, path), .weak = dylib_options.weak };
960 errdefer dylib.deinit(gpa);
961
962 try dylib.parseFromBinary(
963 gpa,
964 @intCast(self.dylibs.items.len), // TODO defer it till later
965 dependent_libs,
966 path,
967 contents,
968 );
969
970 const detected_cpu_arch: std.Target.Cpu.Arch = switch (dylib.header.?.cputype) {
971 macho.CPU_TYPE_ARM64 => .aarch64,
972 macho.CPU_TYPE_X86_64 => .x86_64,
973 else => unreachable,
974 };
975 const detected_platform = dylib.getPlatform(contents);
976 const this_cpu_arch = target.cpu.arch;
977 const this_platform = Platform.fromTarget(target);
978473
979 if (this_cpu_arch != detected_cpu_arch or474 if (self.d_sym) |*d_sym| {
980 (detected_platform != null and !detected_platform.?.eqlTarget(this_platform)))475 d_sym.swapRemoveRelocs(sym_index);
981 {
982 const platform = detected_platform orelse this_platform;
983 try ctx.detected_targets.append(try platform.allocPrintTarget(ctx.arena(), detected_cpu_arch));
984 return error.InvalidTarget;
985 }476 }
986477
987 try self.addDylib(dylib, dylib_options, ctx);478 self.locals.items[sym_index].n_type = 0;
479 _ = self.atom_by_index_table.remove(sym_index);
480 log.debug(" adding local symbol index {d} to free list", .{sym_index});
481 self.getAtomPtr(atom_index).sym_index = 0;
988}482}
989483
990fn parseLibStub(484fn shrinkAtom(self: *MachO, atom_index: Atom.Index, new_block_size: u64) void {
991 self: *MachO,485 _ = self;
992 file: std.fs.File,486 _ = atom_index;
993 path: []const u8,487 _ = new_block_size;
994 dependent_libs: anytype,488 // TODO check the new capacity, and if it crosses the size threshold into a big enough
995 dylib_options: DylibOpts,489 // capacity, insert a free list node for it.
996 ctx: *ParseErrorCtx,
997) ParseError!void {
998 const gpa = self.base.comp.gpa;
999 const target = self.base.comp.root_mod.resolved_target.result;
1000
1001 var lib_stub = try LibStub.loadFromFile(gpa, file);
1002 defer lib_stub.deinit();
1003
1004 if (lib_stub.inner.len == 0) return error.NotLibStub;
1005
1006 // Verify target
1007 {
1008 var matcher = try Dylib.TargetMatcher.init(gpa, target);
1009 defer matcher.deinit();
1010
1011 const first_tbd = lib_stub.inner[0];
1012 const targets = try first_tbd.targets(gpa);
1013 defer {
1014 for (targets) |t| gpa.free(t);
1015 gpa.free(targets);
1016 }
1017 if (!matcher.matchesTarget(targets)) {
1018 try ctx.detected_targets.ensureUnusedCapacity(targets.len);
1019 for (targets) |t| {
1020 ctx.detected_targets.appendAssumeCapacity(try ctx.arena().dupe(u8, t));
1021 }
1022 return error.InvalidTarget;
1023 }
1024 }
1025
1026 var dylib = Dylib{ .path = try gpa.dupe(u8, path), .weak = dylib_options.weak };
1027 errdefer dylib.deinit(gpa);
1028
1029 try dylib.parseFromStub(
1030 gpa,
1031 target,
1032 lib_stub,
1033 @intCast(self.dylibs.items.len), // TODO defer it till later
1034 dependent_libs,
1035 path,
1036 );
1037
1038 try self.addDylib(dylib, dylib_options, ctx);
1039}
1040
1041fn addDylib(self: *MachO, dylib: Dylib, dylib_options: DylibOpts, ctx: *ParseErrorCtx) ParseError!void {
1042 if (dylib_options.reexport_info) |reexport_info| {
1043 if (dylib.id.?.current_version < reexport_info.id.compatibility_version) {
1044 ctx.detected_dylib_id = .{
1045 .parent = reexport_info.parent,
1046 .required_version = reexport_info.id.compatibility_version,
1047 .found_version = dylib.id.?.current_version,
1048 };
1049 return error.IncompatibleDylibVersion;
1050 }
1051 }
1052
1053 const gpa = self.base.comp.gpa;
1054 const gop = try self.dylibs_map.getOrPut(gpa, dylib.id.?.name);
1055 if (gop.found_existing) return error.DylibAlreadyExists;
1056
1057 gop.value_ptr.* = @as(u16, @intCast(self.dylibs.items.len));
1058 try self.dylibs.append(gpa, dylib);
1059
1060 const should_link_dylib_even_if_unreachable = blk: {
1061 if (self.dead_strip_dylibs and !dylib_options.needed) break :blk false;
1062 break :blk !(dylib_options.dependent or self.referenced_dylibs.contains(gop.value_ptr.*));
1063 };
1064
1065 if (should_link_dylib_even_if_unreachable) {
1066 try self.referenced_dylibs.putNoClobber(gpa, gop.value_ptr.*, {});
1067 }
1068}
1069
1070pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype) !void {
1071 const tracy = trace(@src());
1072 defer tracy.end();
1073
1074 // At this point, we can now parse dependents of dylibs preserving the inclusion order of:
1075 // 1) anything on the linker line is parsed first
1076 // 2) afterwards, we parse dependents of the included dylibs
1077 // TODO this should not be performed if the user specifies `-flat_namespace` flag.
1078 // See ld64 manpages.
1079 const comp = self.base.comp;
1080 const gpa = comp.gpa;
1081
1082 while (dependent_libs.readItem()) |dep_id| {
1083 defer dep_id.id.deinit(gpa);
1084
1085 if (self.dylibs_map.contains(dep_id.id.name)) continue;
1086
1087 const parent = &self.dylibs.items[dep_id.parent];
1088 const weak = parent.weak;
1089 const dirname = fs.path.dirname(dep_id.id.name) orelse "";
1090 const stem = fs.path.stem(dep_id.id.name);
1091
1092 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
1093 defer arena_allocator.deinit();
1094 const arena = arena_allocator.allocator();
1095
1096 var test_path = std.ArrayList(u8).init(arena);
1097 var checked_paths = std.ArrayList([]const u8).init(arena);
1098
1099 success: {
1100 if (comp.sysroot) |root| {
1101 const dir = try fs.path.join(arena, &[_][]const u8{ root, dirname });
1102 if (try accessLibPath(gpa, &test_path, &checked_paths, dir, stem)) break :success;
1103 }
1104
1105 if (try accessLibPath(gpa, &test_path, &checked_paths, dirname, stem)) break :success;
1106
1107 try self.reportMissingLibraryError(
1108 checked_paths.items,
1109 "missing dynamic library dependency: '{s}'",
1110 .{dep_id.id.name},
1111 );
1112 continue;
1113 }
1114
1115 const full_path = test_path.items;
1116 const file = try std.fs.cwd().openFile(full_path, .{});
1117 defer file.close();
1118
1119 log.debug("parsing dependency {s} at fully resolved path {s}", .{ dep_id.id.name, full_path });
1120
1121 var parse_ctx = ParseErrorCtx.init(gpa);
1122 defer parse_ctx.deinit();
1123
1124 self.parseLibrary(file, full_path, .{
1125 .path = null,
1126 .needed = false,
1127 .weak = weak,
1128 }, false, true, dep_id, dependent_libs, &parse_ctx) catch |err|
1129 try self.handleAndReportParseError(full_path, err, &parse_ctx);
1130
1131 // TODO I think that it would be nice to rewrite this error to include metadata for failed dependency
1132 // in addition to parsing error
1133 }
1134}
1135
1136pub fn writeAtom(self: *MachO, atom_index: Atom.Index, code: []u8) !void {
1137 const atom = self.getAtom(atom_index);
1138 const sym = atom.getSymbol(self);
1139 const section = self.sections.get(sym.n_sect - 1);
1140 const file_offset = section.header.offset + sym.n_value - section.header.addr;
1141 log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset });
1142
1143 // Gather relocs which can be resolved.
1144 const gpa = self.base.comp.gpa;
1145 var relocs = std.ArrayList(*Relocation).init(gpa);
1146 defer relocs.deinit();
1147
1148 if (self.relocs.getPtr(atom_index)) |rels| {
1149 try relocs.ensureTotalCapacityPrecise(rels.items.len);
1150 for (rels.items) |*reloc| {
1151 if (reloc.isResolvable(self) and reloc.dirty) {
1152 relocs.appendAssumeCapacity(reloc);
1153 }
1154 }
1155 }
1156
1157 Atom.resolveRelocations(self, atom_index, relocs.items, code);
1158
1159 if (is_hot_update_compatible) {
1160 if (self.hot_state.mach_task) |task| {
1161 self.writeToMemory(task, section.segment_index, sym.n_value, code) catch |err| {
1162 log.warn("cannot hot swap: writing to memory failed: {s}", .{@errorName(err)});
1163 };
1164 }
1165 }
1166
1167 try self.base.file.?.pwriteAll(code, file_offset);
1168
1169 // Now we can mark the relocs as resolved.
1170 while (relocs.popOrNull()) |reloc| {
1171 reloc.dirty = false;
1172 }
1173}
1174
1175fn writeToMemory(self: *MachO, task: std.os.darwin.MachTask, segment_index: u8, addr: u64, code: []const u8) !void {
1176 const segment = self.segments.items[segment_index];
1177 const target = self.base.comp.root_mod.resolved_target.result;
1178 const cpu_arch = target.cpu.arch;
1179 const nwritten = if (!segment.isWriteable())
1180 try task.writeMemProtected(addr, code, cpu_arch)
1181 else
1182 try task.writeMem(addr, code, cpu_arch);
1183 if (nwritten != code.len) return error.InputOutput;
1184}
1185
1186fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
1187 const sect_id = self.got_section_index.?;
1188
1189 if (self.got_table_count_dirty) {
1190 const needed_size = self.got_table.entries.items.len * @sizeOf(u64);
1191 try self.growSection(sect_id, needed_size);
1192 self.got_table_count_dirty = false;
1193 }
1194
1195 const header = &self.sections.items(.header)[sect_id];
1196 const segment_index = self.sections.items(.segment_index)[sect_id];
1197 const entry = self.got_table.entries.items[index];
1198 const entry_value = self.getSymbol(entry).n_value;
1199 const entry_offset = index * @sizeOf(u64);
1200 const file_offset = header.offset + entry_offset;
1201 const vmaddr = header.addr + entry_offset;
1202
1203 log.debug("writing GOT entry {d}: @{x} => {x}", .{ index, vmaddr, entry_value });
1204
1205 var buf: [@sizeOf(u64)]u8 = undefined;
1206 mem.writeInt(u64, &buf, entry_value, .little);
1207 try self.base.file.?.pwriteAll(&buf, file_offset);
1208
1209 if (is_hot_update_compatible) {
1210 if (self.hot_state.mach_task) |task| {
1211 self.writeToMemory(task, segment_index, vmaddr, &buf) catch |err| {
1212 log.warn("cannot hot swap: writing to memory failed: {s}", .{@errorName(err)});
1213 };
1214 }
1215 }
1216}
1217
1218fn writeStubHelperPreamble(self: *MachO) !void {
1219 if (self.stub_helper_preamble_allocated) return;
1220
1221 const gpa = self.base.comp.gpa;
1222 const target = self.base.comp.root_mod.resolved_target.result;
1223 const cpu_arch = target.cpu.arch;
1224 const size = stubs.stubHelperPreambleSize(cpu_arch);
1225
1226 var buf = try std.ArrayList(u8).initCapacity(gpa, size);
1227 defer buf.deinit();
1228
1229 const dyld_private_addr = self.getAtom(self.dyld_private_atom_index.?).getSymbol(self).n_value;
1230 const dyld_stub_binder_got_addr = blk: {
1231 const index = self.got_table.lookup.get(self.getGlobalByIndex(self.dyld_stub_binder_index.?)).?;
1232 const header = self.sections.items(.header)[self.got_section_index.?];
1233 break :blk header.addr + @sizeOf(u64) * index;
1234 };
1235 const header = self.sections.items(.header)[self.stub_helper_section_index.?];
1236
1237 try stubs.writeStubHelperPreambleCode(.{
1238 .cpu_arch = cpu_arch,
1239 .source_addr = header.addr,
1240 .dyld_private_addr = dyld_private_addr,
1241 .dyld_stub_binder_got_addr = dyld_stub_binder_got_addr,
1242 }, buf.writer());
1243 try self.base.file.?.pwriteAll(buf.items, header.offset);
1244
1245 self.stub_helper_preamble_allocated = true;
1246}
1247
1248fn writeStubTableEntry(self: *MachO, index: usize) !void {
1249 const target = self.base.comp.root_mod.resolved_target.result;
1250 const stubs_sect_id = self.stubs_section_index.?;
1251 const stub_helper_sect_id = self.stub_helper_section_index.?;
1252 const laptr_sect_id = self.la_symbol_ptr_section_index.?;
1253
1254 const cpu_arch = target.cpu.arch;
1255 const stub_entry_size = stubs.stubSize(cpu_arch);
1256 const stub_helper_entry_size = stubs.stubHelperSize(cpu_arch);
1257 const stub_helper_preamble_size = stubs.stubHelperPreambleSize(cpu_arch);
1258
1259 if (self.stub_table_count_dirty) {
1260 // We grow all 3 sections one by one.
1261 {
1262 const needed_size = stub_entry_size * self.stub_table.entries.items.len;
1263 try self.growSection(stubs_sect_id, needed_size);
1264 }
1265 {
1266 const needed_size = stub_helper_preamble_size + stub_helper_entry_size * self.stub_table.entries.items.len;
1267 try self.growSection(stub_helper_sect_id, needed_size);
1268 }
1269 {
1270 const needed_size = @sizeOf(u64) * self.stub_table.entries.items.len;
1271 try self.growSection(laptr_sect_id, needed_size);
1272 }
1273 self.stub_table_count_dirty = false;
1274 }
1275
1276 const gpa = self.base.comp.gpa;
1277
1278 const stubs_header = self.sections.items(.header)[stubs_sect_id];
1279 const stub_helper_header = self.sections.items(.header)[stub_helper_sect_id];
1280 const laptr_header = self.sections.items(.header)[laptr_sect_id];
1281
1282 const entry = self.stub_table.entries.items[index];
1283 const stub_addr: u64 = stubs_header.addr + stub_entry_size * index;
1284 const stub_helper_addr: u64 = stub_helper_header.addr + stub_helper_preamble_size + stub_helper_entry_size * index;
1285 const laptr_addr: u64 = laptr_header.addr + @sizeOf(u64) * index;
1286
1287 log.debug("writing stub entry {d}: @{x} => '{s}'", .{ index, stub_addr, self.getSymbolName(entry) });
1288
1289 {
1290 var buf = try std.ArrayList(u8).initCapacity(gpa, stub_entry_size);
1291 defer buf.deinit();
1292 try stubs.writeStubCode(.{
1293 .cpu_arch = cpu_arch,
1294 .source_addr = stub_addr,
1295 .target_addr = laptr_addr,
1296 }, buf.writer());
1297 const off = stubs_header.offset + stub_entry_size * index;
1298 try self.base.file.?.pwriteAll(buf.items, off);
1299 }
1300
1301 {
1302 var buf = try std.ArrayList(u8).initCapacity(gpa, stub_helper_entry_size);
1303 defer buf.deinit();
1304 try stubs.writeStubHelperCode(.{
1305 .cpu_arch = cpu_arch,
1306 .source_addr = stub_helper_addr,
1307 .target_addr = stub_helper_header.addr,
1308 }, buf.writer());
1309 const off = stub_helper_header.offset + stub_helper_preamble_size + stub_helper_entry_size * index;
1310 try self.base.file.?.pwriteAll(buf.items, off);
1311 }
1312
1313 {
1314 var buf: [@sizeOf(u64)]u8 = undefined;
1315 mem.writeInt(u64, &buf, stub_helper_addr, .little);
1316 const off = laptr_header.offset + @sizeOf(u64) * index;
1317 try self.base.file.?.pwriteAll(&buf, off);
1318 }
1319
1320 // TODO: generating new stub entry will require pulling the address of the symbol from the
1321 // target dylib when updating directly in memory.
1322 if (is_hot_update_compatible) {
1323 if (self.hot_state.mach_task) |_| {
1324 @panic("TODO: update a stub entry in memory");
1325 }
1326 }
1327}
1328
1329fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void {
1330 log.debug("marking relocs dirty by target: {}", .{target});
1331 // TODO: reverse-lookup might come in handy here
1332 for (self.relocs.values()) |*relocs| {
1333 for (relocs.items) |*reloc| {
1334 if (!reloc.target.eql(target)) continue;
1335 reloc.dirty = true;
1336 }
1337 }
1338}
1339
1340fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void {
1341 log.debug("marking relocs dirty by address: {x}", .{addr});
1342
1343 const got_moved = blk: {
1344 const sect_id = self.got_section_index orelse break :blk false;
1345 break :blk self.sections.items(.header)[sect_id].addr > addr;
1346 };
1347 const stubs_moved = blk: {
1348 const sect_id = self.stubs_section_index orelse break :blk false;
1349 break :blk self.sections.items(.header)[sect_id].addr > addr;
1350 };
1351
1352 for (self.relocs.values()) |*relocs| {
1353 for (relocs.items) |*reloc| {
1354 if (reloc.isGotIndirection()) {
1355 reloc.dirty = reloc.dirty or got_moved;
1356 } else if (reloc.isStubTrampoline(self)) {
1357 reloc.dirty = reloc.dirty or stubs_moved;
1358 } else {
1359 const target_addr = reloc.getTargetBaseAddress(self) orelse continue;
1360 if (target_addr > addr) reloc.dirty = true;
1361 }
1362 }
1363 }
1364
1365 // TODO: dirty only really affected GOT cells
1366 for (self.got_table.entries.items) |entry| {
1367 const target_addr = self.getSymbol(entry).n_value;
1368 if (target_addr > addr) {
1369 self.got_table_contents_dirty = true;
1370 break;
1371 }
1372 }
1373
1374 {
1375 const stubs_addr = self.getSegment(self.stubs_section_index.?).vmaddr;
1376 const stub_helper_addr = self.getSegment(self.stub_helper_section_index.?).vmaddr;
1377 const laptr_addr = self.getSegment(self.la_symbol_ptr_section_index.?).vmaddr;
1378 if (stubs_addr > addr or stub_helper_addr > addr or laptr_addr > addr)
1379 self.stub_table_contents_dirty = true;
1380 }
1381}
1382
1383pub fn allocateSpecialSymbols(self: *MachO) !void {
1384 for (&[_][]const u8{
1385 "___dso_handle",
1386 "__mh_execute_header",
1387 }) |name| {
1388 const global = self.getGlobal(name) orelse continue;
1389 if (global.getFile() != null) continue;
1390 const sym = self.getSymbolPtr(global);
1391 const seg = self.getSegment(self.text_section_index.?);
1392 sym.n_sect = self.text_section_index.? + 1;
1393 sym.n_value = seg.vmaddr;
1394
1395 log.debug("allocating {s}(@0x{x},sect({d})) at the start of {s}", .{
1396 name,
1397 sym.n_value,
1398 sym.n_sect,
1399 seg.segName(),
1400 });
1401 }
1402
1403 for (self.globals.items) |global| {
1404 const sym = self.getSymbolPtr(global);
1405 if (sym.n_desc != N_BOUNDARY) continue;
1406 if (self.getSectionBoundarySymbol(global)) |bsym| {
1407 const sect_id = self.getSectionByName(bsym.segname, bsym.sectname) orelse {
1408 try self.reportUnresolvedBoundarySymbol(self.getSymbolName(global), "section not found: {s},{s}", .{
1409 bsym.segname, bsym.sectname,
1410 });
1411 continue;
1412 };
1413 const sect = self.sections.items(.header)[sect_id];
1414 sym.n_sect = sect_id + 1;
1415 sym.n_value = switch (bsym.kind) {
1416 .start => sect.addr,
1417 .stop => sect.addr + sect.size,
1418 };
1419
1420 log.debug("allocating {s} at @0x{x} sect({d})", .{
1421 self.getSymbolName(global),
1422 sym.n_value,
1423 sym.n_sect,
1424 });
1425
1426 continue;
1427 }
1428 if (self.getSegmentBoundarySymbol(global)) |bsym| {
1429 const seg_id = self.getSegmentByName(bsym.segname) orelse {
1430 try self.reportUnresolvedBoundarySymbol(self.getSymbolName(global), "segment not found: {s}", .{
1431 bsym.segname,
1432 });
1433
1434 continue;
1435 };
1436 const seg = self.segments.items[seg_id];
1437 sym.n_value = switch (bsym.kind) {
1438 .start => seg.vmaddr,
1439 .stop => seg.vmaddr + seg.vmsize,
1440 };
1441
1442 log.debug("allocating {s} at @0x{x} ", .{ self.getSymbolName(global), sym.n_value });
1443
1444 continue;
1445 }
1446 }
1447}
1448
1449const CreateAtomOpts = struct {
1450 size: u64 = 0,
1451 alignment: Alignment = .@"1",
1452};
1453
1454pub fn createAtom(self: *MachO, sym_index: u32, opts: CreateAtomOpts) !Atom.Index {
1455 const gpa = self.base.comp.gpa;
1456 const index = @as(Atom.Index, @intCast(self.atoms.items.len));
1457 const atom = try self.atoms.addOne(gpa);
1458 atom.* = .{};
1459 atom.sym_index = sym_index;
1460 atom.size = opts.size;
1461 atom.alignment = opts.alignment;
1462 log.debug("creating ATOM(%{d}) at index {d}", .{ sym_index, index });
1463 return index;
1464}
1465
1466pub fn createTentativeDefAtoms(self: *MachO) !void {
1467 const gpa = self.base.comp.gpa;
1468
1469 for (self.globals.items) |global| {
1470 const sym = self.getSymbolPtr(global);
1471 if (!sym.tentative()) continue;
1472 if (sym.n_desc == N_DEAD) continue;
1473 if (sym.n_desc == N_BOUNDARY) continue;
1474
1475 log.debug("creating tentative definition for ATOM(%{d}, '{s}') in object({?})", .{
1476 global.sym_index, self.getSymbolName(global), global.file,
1477 });
1478
1479 // Convert any tentative definition into a regular symbol and allocate
1480 // text blocks for each tentative definition.
1481 const size = sym.n_value;
1482 const alignment = (sym.n_desc >> 8) & 0x0f;
1483
1484 if (self.bss_section_index == null) {
1485 self.bss_section_index = try self.initSection("__DATA", "__bss", .{
1486 .flags = macho.S_ZEROFILL,
1487 });
1488 }
1489
1490 sym.* = .{
1491 .n_strx = sym.n_strx,
1492 .n_type = macho.N_SECT | macho.N_EXT,
1493 .n_sect = self.bss_section_index.? + 1,
1494 .n_desc = 0,
1495 .n_value = 0,
1496 };
1497
1498 const atom_index = try self.createAtom(global.sym_index, .{
1499 .size = size,
1500 .alignment = @enumFromInt(alignment),
1501 });
1502 const atom = self.getAtomPtr(atom_index);
1503 atom.file = global.file;
1504
1505 self.addAtomToSection(atom_index);
1506
1507 assert(global.getFile() != null);
1508 const object = &self.objects.items[global.getFile().?];
1509 try object.atoms.append(gpa, atom_index);
1510 object.atom_by_index_table[global.sym_index] = atom_index;
1511 }
1512}
1513
1514pub fn createDyldPrivateAtom(self: *MachO) !void {
1515 if (self.dyld_private_atom_index != null) return;
1516
1517 const sym_index = try self.allocateSymbol();
1518 const atom_index = try self.createAtom(sym_index, .{
1519 .size = @sizeOf(u64),
1520 .alignment = .@"8",
1521 });
1522 const gpa = self.base.comp.gpa;
1523 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index);
1524
1525 if (self.data_section_index == null) {
1526 self.data_section_index = try self.initSection("__DATA", "__data", .{});
1527 }
1528
1529 const atom = self.getAtom(atom_index);
1530 const sym = atom.getSymbolPtr(self);
1531 sym.n_type = macho.N_SECT;
1532 sym.n_sect = self.data_section_index.? + 1;
1533 self.dyld_private_atom_index = atom_index;
1534
1535 switch (self.mode) {
1536 .zld => self.addAtomToSection(atom_index),
1537 .incremental => {
1538 sym.n_value = try self.allocateAtom(atom_index, atom.size, .@"8");
1539 log.debug("allocated dyld_private atom at 0x{x}", .{sym.n_value});
1540 var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);
1541 try self.writeAtom(atom_index, &buffer);
1542 },
1543 }
1544}
1545
1546fn createThreadLocalDescriptorAtom(self: *MachO, sym_name: []const u8, target: SymbolWithLoc) !Atom.Index {
1547 const gpa = self.base.comp.gpa;
1548 const size = 3 * @sizeOf(u64);
1549 const required_alignment: Alignment = .@"1";
1550 const sym_index = try self.allocateSymbol();
1551 const atom_index = try self.createAtom(sym_index, .{});
1552 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index);
1553 self.getAtomPtr(atom_index).size = size;
1554
1555 const sym = self.getAtom(atom_index).getSymbolPtr(self);
1556 sym.n_type = macho.N_SECT;
1557 sym.n_sect = self.thread_vars_section_index.? + 1;
1558 sym.n_strx = try self.strtab.insert(gpa, sym_name);
1559 sym.n_value = try self.allocateAtom(atom_index, size, required_alignment);
1560
1561 log.debug("allocated threadlocal descriptor atom '{s}' at 0x{x}", .{ sym_name, sym.n_value });
1562
1563 try Atom.addRelocation(self, atom_index, .{
1564 .type = .tlv_initializer,
1565 .target = target,
1566 .offset = 0x10,
1567 .addend = 0,
1568 .pcrel = false,
1569 .length = 3,
1570 });
1571
1572 var code: [size]u8 = undefined;
1573 @memset(&code, 0);
1574 try self.writeAtom(atom_index, &code);
1575
1576 return atom_index;
1577}
1578
1579pub fn createMhExecuteHeaderSymbol(self: *MachO) !void {
1580 const output_mode = self.base.comp.config.output_mode;
1581 if (output_mode != .Exe) return;
1582
1583 const gpa = self.base.comp.gpa;
1584 const sym_index = try self.allocateSymbol();
1585 const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
1586 const sym = self.getSymbolPtr(sym_loc);
1587 sym.* = .{
1588 .n_strx = try self.strtab.insert(gpa, "__mh_execute_header"),
1589 .n_type = macho.N_SECT | macho.N_EXT,
1590 .n_sect = 0,
1591 .n_desc = macho.REFERENCED_DYNAMICALLY,
1592 .n_value = 0,
1593 };
1594
1595 const gop = try self.getOrPutGlobalPtr("__mh_execute_header");
1596 if (gop.found_existing) {
1597 const global = gop.value_ptr.*;
1598 if (global.getFile()) |file| {
1599 const global_object = &self.objects.items[file];
1600 global_object.globals_lookup[global.sym_index] = self.getGlobalIndex("__mh_execute_header").?;
1601 }
1602 }
1603 gop.value_ptr.* = sym_loc;
1604}
1605
1606pub fn createDsoHandleSymbol(self: *MachO) !void {
1607 const global = self.getGlobalPtr("___dso_handle") orelse return;
1608 if (!self.getSymbol(global.*).undf()) return;
1609
1610 const gpa = self.base.comp.gpa;
1611 const sym_index = try self.allocateSymbol();
1612 const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
1613 const sym = self.getSymbolPtr(sym_loc);
1614 sym.* = .{
1615 .n_strx = try self.strtab.insert(gpa, "___dso_handle"),
1616 .n_type = macho.N_SECT | macho.N_EXT,
1617 .n_sect = 0,
1618 .n_desc = macho.N_WEAK_DEF,
1619 .n_value = 0,
1620 };
1621 const global_index = self.getGlobalIndex("___dso_handle").?;
1622 if (global.getFile()) |file| {
1623 const global_object = &self.objects.items[file];
1624 global_object.globals_lookup[global.sym_index] = global_index;
1625 }
1626 global.* = sym_loc;
1627 _ = self.unresolved.swapRemove(self.getGlobalIndex("___dso_handle").?);
1628}
1629
1630pub fn resolveSymbols(self: *MachO) !void {
1631 const comp = self.base.comp;
1632 const output_mode = comp.config.output_mode;
1633 // We add the specified entrypoint as the first unresolved symbols so that
1634 // we search for it in libraries should there be no object files specified
1635 // on the linker line.
1636 if (output_mode == .Exe) {
1637 if (self.entry_name) |entry_name| {
1638 _ = try self.addUndefined(entry_name, .{});
1639 }
1640 }
1641
1642 // Force resolution of any symbols requested by the user.
1643 for (comp.force_undefined_symbols.keys()) |sym_name| {
1644 _ = try self.addUndefined(sym_name, .{});
1645 }
1646
1647 for (self.objects.items, 0..) |_, object_id| {
1648 try self.resolveSymbolsInObject(@as(u32, @intCast(object_id)));
1649 }
1650
1651 try self.resolveSymbolsInArchives();
1652
1653 // Finally, force resolution of dyld_stub_binder if there are imports
1654 // requested.
1655 if (self.unresolved.count() > 0 and self.dyld_stub_binder_index == null) {
1656 self.dyld_stub_binder_index = try self.addUndefined("dyld_stub_binder", .{ .add_got = true });
1657 }
1658 if (comp.config.any_non_single_threaded and self.mode == .incremental) {
1659 _ = try self.addUndefined("__tlv_bootstrap", .{});
1660 }
1661
1662 try self.resolveSymbolsInDylibs();
1663
1664 try self.createMhExecuteHeaderSymbol();
1665 try self.createDsoHandleSymbol();
1666 try self.resolveSymbolsAtLoading();
1667
1668 // Final stop, check if unresolved contain any of the special magic boundary symbols
1669 // * section$start$
1670 // * section$stop$
1671 // * segment$start$
1672 // * segment$stop$
1673 try self.resolveBoundarySymbols();
1674}
1675
1676fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
1677 const gpa = self.base.comp.gpa;
1678 const sym = self.getSymbol(current);
1679 const sym_name = self.getSymbolName(current);
1680
1681 const gop = try self.getOrPutGlobalPtr(sym_name);
1682 if (!gop.found_existing) {
1683 gop.value_ptr.* = current;
1684 if (sym.undf() and !sym.tentative()) {
1685 try self.unresolved.putNoClobber(gpa, self.getGlobalIndex(sym_name).?, {});
1686 }
1687 return;
1688 }
1689 const global_index = self.getGlobalIndex(sym_name).?;
1690 const global = gop.value_ptr.*;
1691 const global_sym = self.getSymbol(global);
1692
1693 // Cases to consider: sym vs global_sym
1694 // 1. strong(sym) and strong(global_sym) => error
1695 // 2. strong(sym) and weak(global_sym) => sym
1696 // 3. strong(sym) and tentative(global_sym) => sym
1697 // 4. strong(sym) and undf(global_sym) => sym
1698 // 5. weak(sym) and strong(global_sym) => global_sym
1699 // 6. weak(sym) and tentative(global_sym) => sym
1700 // 7. weak(sym) and undf(global_sym) => sym
1701 // 8. tentative(sym) and strong(global_sym) => global_sym
1702 // 9. tentative(sym) and weak(global_sym) => global_sym
1703 // 10. tentative(sym) and tentative(global_sym) => pick larger
1704 // 11. tentative(sym) and undf(global_sym) => sym
1705 // 12. undf(sym) and * => global_sym
1706 //
1707 // Reduces to:
1708 // 1. strong(sym) and strong(global_sym) => error
1709 // 2. * and strong(global_sym) => global_sym
1710 // 3. weak(sym) and weak(global_sym) => global_sym
1711 // 4. tentative(sym) and tentative(global_sym) => pick larger
1712 // 5. undf(sym) and * => global_sym
1713 // 6. else => sym
1714
1715 const sym_is_strong = sym.sect() and !(sym.weakDef() or sym.pext());
1716 const global_is_strong = global_sym.sect() and !(global_sym.weakDef() or global_sym.pext());
1717 const sym_is_weak = sym.sect() and (sym.weakDef() or sym.pext());
1718 const global_is_weak = global_sym.sect() and (global_sym.weakDef() or global_sym.pext());
1719
1720 if (sym_is_strong and global_is_strong) {
1721 // TODO redo this logic with corresponding logic in updateExports to avoid this
1722 // ugly check.
1723 if (self.mode == .zld) {
1724 try self.reportSymbolCollision(global, current);
1725 }
1726 return error.MultipleSymbolDefinitions;
1727 }
1728
1729 if (current.getFile()) |file| {
1730 const object = &self.objects.items[file];
1731 object.globals_lookup[current.sym_index] = global_index;
1732 }
1733
1734 if (global_is_strong) return;
1735 if (sym_is_weak and global_is_weak) return;
1736 if (sym.tentative() and global_sym.tentative()) {
1737 if (global_sym.n_value >= sym.n_value) return;
1738 }
1739 if (sym.undf() and !sym.tentative()) return;
1740
1741 if (global.getFile()) |file| {
1742 const global_object = &self.objects.items[file];
1743 global_object.globals_lookup[global.sym_index] = global_index;
1744 }
1745 _ = self.unresolved.swapRemove(global_index);
1746
1747 gop.value_ptr.* = current;
1748}
1749
1750fn resolveSymbolsInObject(self: *MachO, object_id: u32) !void {
1751 const object = &self.objects.items[object_id];
1752 const in_symtab = object.in_symtab orelse return;
1753
1754 log.debug("resolving symbols in '{s}'", .{object.name});
1755
1756 var sym_index: u32 = 0;
1757 while (sym_index < in_symtab.len) : (sym_index += 1) {
1758 const sym = &object.symtab[sym_index];
1759 const sym_name = object.getSymbolName(sym_index);
1760 const sym_with_loc = SymbolWithLoc{
1761 .sym_index = sym_index,
1762 .file = object_id + 1,
1763 };
1764
1765 if (sym.stab() or sym.indr() or sym.abs()) {
1766 try self.reportUnhandledSymbolType(sym_with_loc);
1767 continue;
1768 }
1769
1770 if (sym.sect() and !sym.ext()) {
1771 log.debug("symbol '{s}' local to object {s}; skipping...", .{
1772 sym_name,
1773 object.name,
1774 });
1775 continue;
1776 }
1777
1778 self.resolveGlobalSymbol(.{
1779 .sym_index = sym_index,
1780 .file = object_id + 1,
1781 }) catch |err| switch (err) {
1782 error.MultipleSymbolDefinitions => return error.FlushFailure,
1783 else => |e| return e,
1784 };
1785 }
1786}
1787
1788fn resolveSymbolsInArchives(self: *MachO) !void {
1789 if (self.archives.items.len == 0) return;
1790
1791 const gpa = self.base.comp.gpa;
1792 var next_sym: usize = 0;
1793 loop: while (next_sym < self.unresolved.count()) {
1794 const global = self.globals.items[self.unresolved.keys()[next_sym]];
1795 const sym_name = self.getSymbolName(global);
1796
1797 for (self.archives.items) |archive| {
1798 // Check if the entry exists in a static archive.
1799 const offsets = archive.toc.get(sym_name) orelse {
1800 // No hit.
1801 continue;
1802 };
1803 assert(offsets.items.len > 0);
1804
1805 const object_id = @as(u16, @intCast(self.objects.items.len));
1806 const object = try archive.parseObject(gpa, offsets.items[0]);
1807 try self.objects.append(gpa, object);
1808 try self.resolveSymbolsInObject(object_id);
1809
1810 continue :loop;
1811 }
1812
1813 next_sym += 1;
1814 }
1815}
1816
1817fn resolveSymbolsInDylibs(self: *MachO) !void {
1818 if (self.dylibs.items.len == 0) return;
1819
1820 const gpa = self.base.comp.gpa;
1821 var next_sym: usize = 0;
1822 loop: while (next_sym < self.unresolved.count()) {
1823 const global_index = self.unresolved.keys()[next_sym];
1824 const global = self.globals.items[global_index];
1825 const sym = self.getSymbolPtr(global);
1826 const sym_name = self.getSymbolName(global);
1827
1828 for (self.dylibs.items, 0..) |dylib, id| {
1829 if (!dylib.symbols.contains(sym_name)) continue;
1830
1831 const dylib_id = @as(u16, @intCast(id));
1832 if (!self.referenced_dylibs.contains(dylib_id)) {
1833 try self.referenced_dylibs.putNoClobber(gpa, dylib_id, {});
1834 }
1835
1836 const ordinal = self.referenced_dylibs.getIndex(dylib_id) orelse unreachable;
1837 sym.n_type |= macho.N_EXT;
1838 sym.n_desc = @as(u16, @intCast(ordinal + 1)) * macho.N_SYMBOL_RESOLVER;
1839
1840 if (dylib.weak) {
1841 sym.n_desc |= macho.N_WEAK_REF;
1842 }
1843
1844 _ = self.unresolved.swapRemove(global_index);
1845
1846 continue :loop;
1847 }
1848
1849 next_sym += 1;
1850 }
1851}
1852
1853fn resolveSymbolsAtLoading(self: *MachO) !void {
1854 const output_mode = self.base.comp.config.output_mode;
1855 const is_lib = output_mode == .Lib;
1856 const is_dyn_lib = self.base.comp.config.link_mode == .Dynamic and is_lib;
1857 const allow_undef = is_dyn_lib and self.base.allow_shlib_undefined;
1858
1859 var next_sym: usize = 0;
1860 while (next_sym < self.unresolved.count()) {
1861 const global_index = self.unresolved.keys()[next_sym];
1862 const global = self.globals.items[global_index];
1863 const sym = self.getSymbolPtr(global);
1864
1865 if (sym.discarded()) {
1866 sym.* = .{
1867 .n_strx = 0,
1868 .n_type = macho.N_UNDF,
1869 .n_sect = 0,
1870 .n_desc = 0,
1871 .n_value = 0,
1872 };
1873 _ = self.unresolved.swapRemove(global_index);
1874 continue;
1875 } else if (allow_undef) {
1876 const n_desc = @as(
1877 u16,
1878 @bitCast(macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP * @as(i16, @intCast(macho.N_SYMBOL_RESOLVER))),
1879 );
1880 sym.n_type = macho.N_EXT;
1881 sym.n_desc = n_desc;
1882 _ = self.unresolved.swapRemove(global_index);
1883 continue;
1884 }
1885
1886 next_sym += 1;
1887 }
1888}
1889
1890fn resolveBoundarySymbols(self: *MachO) !void {
1891 const gpa = self.base.comp.gpa;
1892 var next_sym: usize = 0;
1893 while (next_sym < self.unresolved.count()) {
1894 const global_index = self.unresolved.keys()[next_sym];
1895 const global = &self.globals.items[global_index];
1896
1897 if (self.getSectionBoundarySymbol(global.*) != null or self.getSegmentBoundarySymbol(global.*) != null) {
1898 const sym_index = try self.allocateSymbol();
1899 const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
1900 const sym = self.getSymbolPtr(sym_loc);
1901 sym.* = .{
1902 .n_strx = try self.strtab.insert(gpa, self.getSymbolName(global.*)),
1903 .n_type = macho.N_SECT | macho.N_EXT,
1904 .n_sect = 0,
1905 .n_desc = N_BOUNDARY,
1906 .n_value = 0,
1907 };
1908 if (global.getFile()) |file| {
1909 const global_object = &self.objects.items[file];
1910 global_object.globals_lookup[global.sym_index] = global_index;
1911 }
1912 global.* = sym_loc;
1913 _ = self.unresolved.swapRemove(global_index);
1914 continue;
1915 }
1916
1917 next_sym += 1;
1918 }
1919}
1920
1921pub fn deinit(self: *MachO) void {
1922 const gpa = self.base.comp.gpa;
1923
1924 if (self.llvm_object) |llvm_object| llvm_object.deinit();
1925
1926 if (self.d_sym) |*d_sym| {
1927 d_sym.deinit();
1928 }
1929
1930 self.got_table.deinit(gpa);
1931 self.stub_table.deinit(gpa);
1932 self.tlv_ptr_table.deinit(gpa);
1933 self.thunk_table.deinit(gpa);
1934
1935 for (self.thunks.items) |*thunk| {
1936 thunk.deinit(gpa);
1937 }
1938 self.thunks.deinit(gpa);
1939
1940 self.strtab.deinit(gpa);
1941 self.locals.deinit(gpa);
1942 self.globals.deinit(gpa);
1943 self.locals_free_list.deinit(gpa);
1944 self.globals_free_list.deinit(gpa);
1945 self.unresolved.deinit(gpa);
1946
1947 {
1948 var it = self.resolver.keyIterator();
1949 while (it.next()) |key_ptr| {
1950 gpa.free(key_ptr.*);
1951 }
1952 self.resolver.deinit(gpa);
1953 }
1954
1955 for (self.objects.items) |*object| {
1956 object.deinit(gpa);
1957 }
1958 self.objects.deinit(gpa);
1959 for (self.archives.items) |*archive| {
1960 archive.deinit(gpa);
1961 }
1962 self.archives.deinit(gpa);
1963 for (self.dylibs.items) |*dylib| {
1964 dylib.deinit(gpa);
1965 }
1966 self.dylibs.deinit(gpa);
1967 self.dylibs_map.deinit(gpa);
1968 self.referenced_dylibs.deinit(gpa);
1969
1970 self.segments.deinit(gpa);
1971
1972 for (self.sections.items(.free_list)) |*list| {
1973 list.deinit(gpa);
1974 }
1975 self.sections.deinit(gpa);
1976
1977 self.atoms.deinit(gpa);
1978
1979 for (self.decls.values()) |*m| {
1980 m.exports.deinit(gpa);
1981 }
1982 self.decls.deinit(gpa);
1983
1984 self.lazy_syms.deinit(gpa);
1985 self.tlv_table.deinit(gpa);
1986
1987 for (self.unnamed_const_atoms.values()) |*atoms| {
1988 atoms.deinit(gpa);
1989 }
1990 self.unnamed_const_atoms.deinit(gpa);
1991
1992 {
1993 var it = self.anon_decls.iterator();
1994 while (it.next()) |entry| {
1995 entry.value_ptr.exports.deinit(gpa);
1996 }
1997 self.anon_decls.deinit(gpa);
1998 }
1999
2000 self.atom_by_index_table.deinit(gpa);
2001
2002 for (self.relocs.values()) |*relocs| {
2003 relocs.deinit(gpa);
2004 }
2005 self.relocs.deinit(gpa);
2006 self.actions.deinit(gpa);
2007
2008 for (self.rebases.values()) |*rebases| {
2009 rebases.deinit(gpa);
2010 }
2011 self.rebases.deinit(gpa);
2012
2013 for (self.bindings.values()) |*bindings| {
2014 bindings.deinit(gpa);
2015 }
2016 self.bindings.deinit(gpa);
2017}
2018
2019fn freeAtom(self: *MachO, atom_index: Atom.Index) void {
2020 const gpa = self.base.comp.gpa;
2021 log.debug("freeAtom {d}", .{atom_index});
2022
2023 // Remove any relocs and base relocs associated with this Atom
2024 Atom.freeRelocations(self, atom_index);
2025
2026 const atom = self.getAtom(atom_index);
2027 const sect_id = atom.getSymbol(self).n_sect - 1;
2028 const free_list = &self.sections.items(.free_list)[sect_id];
2029 var already_have_free_list_node = false;
2030 {
2031 var i: usize = 0;
2032 // TODO turn free_list into a hash map
2033 while (i < free_list.items.len) {
2034 if (free_list.items[i] == atom_index) {
2035 _ = free_list.swapRemove(i);
2036 continue;
2037 }
2038 if (free_list.items[i] == atom.prev_index) {
2039 already_have_free_list_node = true;
2040 }
2041 i += 1;
2042 }
2043 }
2044
2045 const maybe_last_atom_index = &self.sections.items(.last_atom_index)[sect_id];
2046 if (maybe_last_atom_index.*) |last_atom_index| {
2047 if (last_atom_index == atom_index) {
2048 if (atom.prev_index) |prev_index| {
2049 // TODO shrink the section size here
2050 maybe_last_atom_index.* = prev_index;
2051 } else {
2052 maybe_last_atom_index.* = null;
2053 }
2054 }
2055 }
2056
2057 if (atom.prev_index) |prev_index| {
2058 const prev = self.getAtomPtr(prev_index);
2059 prev.next_index = atom.next_index;
2060
2061 if (!already_have_free_list_node and prev.*.freeListEligible(self)) {
2062 // The free list is heuristics, it doesn't have to be perfect, so we can ignore
2063 // the OOM here.
2064 free_list.append(gpa, prev_index) catch {};
2065 }
2066 } else {
2067 self.getAtomPtr(atom_index).prev_index = null;
2068 }
2069
2070 if (atom.next_index) |next_index| {
2071 self.getAtomPtr(next_index).prev_index = atom.prev_index;
2072 } else {
2073 self.getAtomPtr(atom_index).next_index = null;
2074 }
2075
2076 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
2077 const sym_index = atom.getSymbolIndex().?;
2078
2079 self.locals_free_list.append(gpa, sym_index) catch {};
2080
2081 // Try freeing GOT atom if this decl had one
2082 self.got_table.freeEntry(gpa, .{ .sym_index = sym_index });
2083
2084 if (self.d_sym) |*d_sym| {
2085 d_sym.swapRemoveRelocs(sym_index);
2086 }
2087
2088 self.locals.items[sym_index].n_type = 0;
2089 _ = self.atom_by_index_table.remove(sym_index);
2090 log.debug(" adding local symbol index {d} to free list", .{sym_index});
2091 self.getAtomPtr(atom_index).sym_index = 0;
2092}
2093
2094fn shrinkAtom(self: *MachO, atom_index: Atom.Index, new_block_size: u64) void {
2095 _ = self;
2096 _ = atom_index;
2097 _ = new_block_size;
2098 // TODO check the new capacity, and if it crosses the size threshold into a big enough
2099 // capacity, insert a free list node for it.
2100}
2101
2102fn growAtom(self: *MachO, atom_index: Atom.Index, new_atom_size: u64, alignment: Alignment) !u64 {
2103 const atom = self.getAtom(atom_index);
2104 const sym = atom.getSymbol(self);
2105 const align_ok = alignment.check(sym.n_value);
2106 const need_realloc = !align_ok or new_atom_size > atom.capacity(self);
2107 if (!need_realloc) return sym.n_value;
2108 return self.allocateAtom(atom_index, new_atom_size, alignment);
2109}
2110
2111pub fn allocateSymbol(self: *MachO) !u32 {
2112 const gpa = self.base.comp.gpa;
2113 try self.locals.ensureUnusedCapacity(gpa, 1);
2114
2115 const index = blk: {
2116 if (self.locals_free_list.popOrNull()) |index| {
2117 log.debug(" (reusing symbol index {d})", .{index});
2118 break :blk index;
2119 } else {
2120 log.debug(" (allocating symbol index {d})", .{self.locals.items.len});
2121 const index = @as(u32, @intCast(self.locals.items.len));
2122 _ = self.locals.addOneAssumeCapacity();
2123 break :blk index;
2124 }
2125 };
2126
2127 self.locals.items[index] = .{
2128 .n_strx = 0,
2129 .n_type = 0,
2130 .n_sect = 0,
2131 .n_desc = 0,
2132 .n_value = 0,
2133 };
2134
2135 return index;
2136}
2137
2138fn allocateGlobal(self: *MachO) !u32 {
2139 const gpa = self.base.comp.gpa;
2140 try self.globals.ensureUnusedCapacity(gpa, 1);
2141
2142 const index = blk: {
2143 if (self.globals_free_list.popOrNull()) |index| {
2144 log.debug(" (reusing global index {d})", .{index});
2145 break :blk index;
2146 } else {
2147 log.debug(" (allocating symbol index {d})", .{self.globals.items.len});
2148 const index = @as(u32, @intCast(self.globals.items.len));
2149 _ = self.globals.addOneAssumeCapacity();
2150 break :blk index;
2151 }
2152 };
2153
2154 self.globals.items[index] = .{ .sym_index = 0 };
2155
2156 return index;
2157}
2158
2159pub fn addGotEntry(self: *MachO, reloc_target: SymbolWithLoc) !void {
2160 if (self.got_table.lookup.contains(reloc_target)) return;
2161 const gpa = self.base.comp.gpa;
2162 const got_index = try self.got_table.allocateEntry(gpa, reloc_target);
2163 if (self.got_section_index == null) {
2164 self.got_section_index = try self.initSection("__DATA_CONST", "__got", .{
2165 .flags = macho.S_NON_LAZY_SYMBOL_POINTERS,
2166 });
2167 }
2168 if (self.mode == .incremental) {
2169 try self.writeOffsetTableEntry(got_index);
2170 self.got_table_count_dirty = true;
2171 self.markRelocsDirtyByTarget(reloc_target);
2172 }
2173}
2174
2175pub fn addStubEntry(self: *MachO, reloc_target: SymbolWithLoc) !void {
2176 if (self.stub_table.lookup.contains(reloc_target)) return;
2177 const comp = self.base.comp;
2178 const gpa = comp.gpa;
2179 const cpu_arch = comp.root_mod.resolved_target.result.cpu.arch;
2180 const stub_index = try self.stub_table.allocateEntry(gpa, reloc_target);
2181 if (self.stubs_section_index == null) {
2182 self.stubs_section_index = try self.initSection("__TEXT", "__stubs", .{
2183 .flags = macho.S_SYMBOL_STUBS |
2184 macho.S_ATTR_PURE_INSTRUCTIONS |
2185 macho.S_ATTR_SOME_INSTRUCTIONS,
2186 .reserved2 = stubs.stubSize(cpu_arch),
2187 });
2188 self.stub_helper_section_index = try self.initSection("__TEXT", "__stub_helper", .{
2189 .flags = macho.S_REGULAR |
2190 macho.S_ATTR_PURE_INSTRUCTIONS |
2191 macho.S_ATTR_SOME_INSTRUCTIONS,
2192 });
2193 self.la_symbol_ptr_section_index = try self.initSection("__DATA", "__la_symbol_ptr", .{
2194 .flags = macho.S_LAZY_SYMBOL_POINTERS,
2195 });
2196 }
2197 if (self.mode == .incremental) {
2198 try self.writeStubTableEntry(stub_index);
2199 self.stub_table_count_dirty = true;
2200 self.markRelocsDirtyByTarget(reloc_target);
2201 }
2202}
2203
2204pub fn addTlvPtrEntry(self: *MachO, reloc_target: SymbolWithLoc) !void {
2205 if (self.tlv_ptr_table.lookup.contains(reloc_target)) return;
2206 const gpa = self.base.comp.gpa;
2207 _ = try self.tlv_ptr_table.allocateEntry(gpa, reloc_target);
2208 if (self.tlv_ptr_section_index == null) {
2209 self.tlv_ptr_section_index = try self.initSection("__DATA", "__thread_ptrs", .{
2210 .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
2211 });
2212 }
2213}
2214
2215pub fn updateFunc(self: *MachO, mod: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {
2216 if (build_options.skip_non_native and builtin.object_format != .macho) {
2217 @panic("Attempted to compile for object format that was disabled by build configuration");
2218 }
2219 if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(mod, func_index, air, liveness);
2220 const tracy = trace(@src());
2221 defer tracy.end();
2222
2223 const func = mod.funcInfo(func_index);
2224 const decl_index = func.owner_decl;
2225 const decl = mod.declPtr(decl_index);
2226
2227 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
2228 self.freeUnnamedConsts(decl_index);
2229 Atom.freeRelocations(self, atom_index);
2230
2231 const gpa = self.base.comp.gpa;
2232 var code_buffer = std.ArrayList(u8).init(gpa);
2233 defer code_buffer.deinit();
2234
2235 var decl_state = if (self.d_sym) |*d_sym|
2236 try d_sym.dwarf.initDeclState(mod, decl_index)
2237 else
2238 null;
2239 defer if (decl_state) |*ds| ds.deinit();
2240
2241 const res = if (decl_state) |*ds|
2242 try codegen.generateFunction(&self.base, decl.srcLoc(mod), func_index, air, liveness, &code_buffer, .{
2243 .dwarf = ds,
2244 })
2245 else
2246 try codegen.generateFunction(&self.base, decl.srcLoc(mod), func_index, air, liveness, &code_buffer, .none);
2247
2248 const code = switch (res) {
2249 .ok => code_buffer.items,
2250 .fail => |em| {
2251 decl.analysis = .codegen_failure;
2252 try mod.failed_decls.put(mod.gpa, decl_index, em);
2253 return;
2254 },
2255 };
2256
2257 const addr = try self.updateDeclCode(decl_index, code);
2258
2259 if (decl_state) |*ds| {
2260 try self.d_sym.?.dwarf.commitDeclState(
2261 mod,
2262 decl_index,
2263 addr,
2264 self.getAtom(atom_index).size,
2265 ds,
2266 );
2267 }
2268
2269 // Since we updated the vaddr and the size, each corresponding export symbol also
2270 // needs to be updated.
2271 try self.updateExports(mod, .{ .decl_index = decl_index }, mod.getDeclExports(decl_index));
2272}
2273
2274pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: InternPool.DeclIndex) !u32 {
2275 const gpa = self.base.comp.gpa;
2276 const mod = self.base.comp.module.?;
2277 const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index);
2278 if (!gop.found_existing) {
2279 gop.value_ptr.* = .{};
2280 }
2281 const unnamed_consts = gop.value_ptr;
2282 const decl = mod.declPtr(decl_index);
2283 const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod));
2284 const index = unnamed_consts.items.len;
2285 const name = try std.fmt.allocPrint(gpa, "___unnamed_{s}_{d}", .{ decl_name, index });
2286 defer gpa.free(name);
2287 const atom_index = switch (try self.lowerConst(name, typed_value, typed_value.ty.abiAlignment(mod), self.data_const_section_index.?, decl.srcLoc(mod))) {
2288 .ok => |atom_index| atom_index,
2289 .fail => |em| {
2290 decl.analysis = .codegen_failure;
2291 try mod.failed_decls.put(mod.gpa, decl_index, em);
2292 log.debug("{s}", .{em.msg});
2293 return error.CodegenFail;
2294 },
2295 };
2296 try unnamed_consts.append(gpa, atom_index);
2297 const atom = self.getAtomPtr(atom_index);
2298 return atom.getSymbolIndex().?;
2299}
2300
2301const LowerConstResult = union(enum) {
2302 ok: Atom.Index,
2303 fail: *Module.ErrorMsg,
2304};
2305
2306fn lowerConst(
2307 self: *MachO,
2308 name: []const u8,
2309 tv: TypedValue,
2310 required_alignment: InternPool.Alignment,
2311 sect_id: u8,
2312 src_loc: Module.SrcLoc,
2313) !LowerConstResult {
2314 const gpa = self.base.comp.gpa;
2315
2316 var code_buffer = std.ArrayList(u8).init(gpa);
2317 defer code_buffer.deinit();
2318
2319 log.debug("allocating symbol indexes for {s}", .{name});
2320
2321 const sym_index = try self.allocateSymbol();
2322 const atom_index = try self.createAtom(sym_index, .{});
2323 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index);
2324
2325 const res = try codegen.generateSymbol(&self.base, src_loc, tv, &code_buffer, .none, .{
2326 .parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?,
2327 });
2328 const code = switch (res) {
2329 .ok => code_buffer.items,
2330 .fail => |em| return .{ .fail = em },
2331 };
2332
2333 const atom = self.getAtomPtr(atom_index);
2334 atom.size = code.len;
2335 // TODO: work out logic for disambiguating functions from function pointers
2336 // const sect_id = self.getDeclOutputSection(decl_index);
2337 const symbol = atom.getSymbolPtr(self);
2338 const name_str_index = try self.strtab.insert(gpa, name);
2339 symbol.n_strx = name_str_index;
2340 symbol.n_type = macho.N_SECT;
2341 symbol.n_sect = sect_id + 1;
2342 symbol.n_value = try self.allocateAtom(atom_index, code.len, required_alignment);
2343 errdefer self.freeAtom(atom_index);
2344
2345 log.debug("allocated atom for {s} at 0x{x}", .{ name, symbol.n_value });
2346 log.debug(" (required alignment 0x{x})", .{required_alignment});
2347
2348 try self.writeAtom(atom_index, code);
2349 self.markRelocsDirtyByTarget(atom.getSymbolWithLoc());
2350
2351 return .{ .ok = atom_index };
2352}
2353
2354pub fn updateDecl(self: *MachO, mod: *Module, decl_index: InternPool.DeclIndex) !void {
2355 if (build_options.skip_non_native and builtin.object_format != .macho) {
2356 @panic("Attempted to compile for object format that was disabled by build configuration");
2357 }
2358 if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(mod, decl_index);
2359 const tracy = trace(@src());
2360 defer tracy.end();
2361
2362 const comp = self.base.comp;
2363 const gpa = comp.gpa;
2364 const decl = mod.declPtr(decl_index);
2365
2366 if (decl.val.getExternFunc(mod)) |_| {
2367 return;
2368 }
2369
2370 if (decl.isExtern(mod)) {
2371 // TODO make this part of getGlobalSymbol
2372 const name = mod.intern_pool.stringToSlice(decl.name);
2373 const sym_name = try std.fmt.allocPrint(gpa, "_{s}", .{name});
2374 defer gpa.free(sym_name);
2375 _ = try self.addUndefined(sym_name, .{ .add_got = true });
2376 return;
2377 }
2378
2379 const is_threadlocal = if (decl.val.getVariable(mod)) |variable|
2380 variable.is_threadlocal and comp.config.any_non_single_threaded
2381 else
2382 false;
2383 if (is_threadlocal) return self.updateThreadlocalVariable(mod, decl_index);
2384
2385 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
2386 const sym_index = self.getAtom(atom_index).getSymbolIndex().?;
2387 Atom.freeRelocations(self, atom_index);
2388
2389 var code_buffer = std.ArrayList(u8).init(gpa);
2390 defer code_buffer.deinit();
2391
2392 var decl_state: ?Dwarf.DeclState = if (self.d_sym) |*d_sym|
2393 try d_sym.dwarf.initDeclState(mod, decl_index)
2394 else
2395 null;
2396 defer if (decl_state) |*ds| ds.deinit();
2397
2398 const decl_val = if (decl.val.getVariable(mod)) |variable| Value.fromInterned(variable.init) else decl.val;
2399 const res = if (decl_state) |*ds|
2400 try codegen.generateSymbol(&self.base, decl.srcLoc(mod), .{
2401 .ty = decl.ty,
2402 .val = decl_val,
2403 }, &code_buffer, .{
2404 .dwarf = ds,
2405 }, .{
2406 .parent_atom_index = sym_index,
2407 })
2408 else
2409 try codegen.generateSymbol(&self.base, decl.srcLoc(mod), .{
2410 .ty = decl.ty,
2411 .val = decl_val,
2412 }, &code_buffer, .none, .{
2413 .parent_atom_index = sym_index,
2414 });
2415
2416 const code = switch (res) {
2417 .ok => code_buffer.items,
2418 .fail => |em| {
2419 decl.analysis = .codegen_failure;
2420 try mod.failed_decls.put(mod.gpa, decl_index, em);
2421 return;
2422 },
2423 };
2424 const addr = try self.updateDeclCode(decl_index, code);
2425
2426 if (decl_state) |*ds| {
2427 try self.d_sym.?.dwarf.commitDeclState(
2428 mod,
2429 decl_index,
2430 addr,
2431 self.getAtom(atom_index).size,
2432 ds,
2433 );
2434 }
2435
2436 // Since we updated the vaddr and the size, each corresponding export symbol also
2437 // needs to be updated.
2438 try self.updateExports(mod, .{ .decl_index = decl_index }, mod.getDeclExports(decl_index));
2439}
2440
2441fn updateLazySymbolAtom(
2442 self: *MachO,
2443 sym: File.LazySymbol,
2444 atom_index: Atom.Index,
2445 section_index: u8,
2446) !void {
2447 const gpa = self.base.comp.gpa;
2448 const mod = self.base.comp.module.?;
2449
2450 var required_alignment: Alignment = .none;
2451 var code_buffer = std.ArrayList(u8).init(gpa);
2452 defer code_buffer.deinit();
2453
2454 const name_str_index = blk: {
2455 const name = try std.fmt.allocPrint(gpa, "___lazy_{s}_{}", .{
2456 @tagName(sym.kind),
2457 sym.ty.fmt(mod),
2458 });
2459 defer gpa.free(name);
2460 break :blk try self.strtab.insert(gpa, name);
2461 };
2462 const name = self.strtab.get(name_str_index).?;
2463
2464 const atom = self.getAtomPtr(atom_index);
2465 const local_sym_index = atom.getSymbolIndex().?;
2466
2467 const src = if (sym.ty.getOwnerDeclOrNull(mod)) |owner_decl|
2468 mod.declPtr(owner_decl).srcLoc(mod)
2469 else
2470 Module.SrcLoc{
2471 .file_scope = undefined,
2472 .parent_decl_node = undefined,
2473 .lazy = .unneeded,
2474 };
2475 const res = try codegen.generateLazySymbol(
2476 &self.base,
2477 src,
2478 sym,
2479 &required_alignment,
2480 &code_buffer,
2481 .none,
2482 .{ .parent_atom_index = local_sym_index },
2483 );
2484 const code = switch (res) {
2485 .ok => code_buffer.items,
2486 .fail => |em| {
2487 log.debug("{s}", .{em.msg});
2488 return error.CodegenFail;
2489 },
2490 };
2491
2492 const symbol = atom.getSymbolPtr(self);
2493 symbol.n_strx = name_str_index;
2494 symbol.n_type = macho.N_SECT;
2495 symbol.n_sect = section_index + 1;
2496 symbol.n_desc = 0;
2497
2498 const vaddr = try self.allocateAtom(atom_index, code.len, required_alignment);
2499 errdefer self.freeAtom(atom_index);
2500
2501 log.debug("allocated atom for {s} at 0x{x}", .{ name, vaddr });
2502 log.debug(" (required alignment 0x{x})", .{required_alignment});
2503
2504 atom.size = code.len;
2505 symbol.n_value = vaddr;
2506
2507 try self.addGotEntry(.{ .sym_index = local_sym_index });
2508 try self.writeAtom(atom_index, code);
2509}
2510
2511pub fn getOrCreateAtomForLazySymbol(self: *MachO, sym: File.LazySymbol) !Atom.Index {
2512 const mod = self.base.comp.module.?;
2513 const gpa = self.base.comp.gpa;
2514 const gop = try self.lazy_syms.getOrPut(gpa, sym.getDecl(mod));
2515 errdefer _ = if (!gop.found_existing) self.lazy_syms.pop();
2516 if (!gop.found_existing) gop.value_ptr.* = .{};
2517 const metadata: struct { atom: *Atom.Index, state: *LazySymbolMetadata.State } = switch (sym.kind) {
2518 .code => .{ .atom = &gop.value_ptr.text_atom, .state = &gop.value_ptr.text_state },
2519 .const_data => .{
2520 .atom = &gop.value_ptr.data_const_atom,
2521 .state = &gop.value_ptr.data_const_state,
2522 },
2523 };
2524 switch (metadata.state.*) {
2525 .unused => {
2526 const sym_index = try self.allocateSymbol();
2527 metadata.atom.* = try self.createAtom(sym_index, .{});
2528 try self.atom_by_index_table.putNoClobber(gpa, sym_index, metadata.atom.*);
2529 },
2530 .pending_flush => return metadata.atom.*,
2531 .flushed => {},
2532 }
2533 metadata.state.* = .pending_flush;
2534 const atom = metadata.atom.*;
2535 // anyerror needs to be deferred until flushModule
2536 if (sym.getDecl(mod) != .none) try self.updateLazySymbolAtom(sym, atom, switch (sym.kind) {
2537 .code => self.text_section_index.?,
2538 .const_data => self.data_const_section_index.?,
2539 });
2540 return atom;
2541}
2542
2543fn updateThreadlocalVariable(self: *MachO, module: *Module, decl_index: InternPool.DeclIndex) !void {
2544 const mod = self.base.comp.module.?;
2545 // Lowering a TLV on macOS involves two stages:
2546 // 1. first we lower the initializer into appopriate section (__thread_data or __thread_bss)
2547 // 2. next, we create a corresponding threadlocal variable descriptor in __thread_vars
2548
2549 // 1. Lower the initializer value.
2550 const init_atom_index = try self.getOrCreateAtomForDecl(decl_index);
2551 const init_atom = self.getAtomPtr(init_atom_index);
2552 const init_sym_index = init_atom.getSymbolIndex().?;
2553 Atom.freeRelocations(self, init_atom_index);
2554
2555 const gpa = self.base.comp.gpa;
2556
2557 var code_buffer = std.ArrayList(u8).init(gpa);
2558 defer code_buffer.deinit();
2559
2560 var decl_state: ?Dwarf.DeclState = if (self.d_sym) |*d_sym|
2561 try d_sym.dwarf.initDeclState(module, decl_index)
2562 else
2563 null;
2564 defer if (decl_state) |*ds| ds.deinit();
2565
2566 const decl = module.declPtr(decl_index);
2567 const decl_metadata = self.decls.get(decl_index).?;
2568 const decl_val = Value.fromInterned(decl.val.getVariable(mod).?.init);
2569 const res = if (decl_state) |*ds|
2570 try codegen.generateSymbol(&self.base, decl.srcLoc(mod), .{
2571 .ty = decl.ty,
2572 .val = decl_val,
2573 }, &code_buffer, .{
2574 .dwarf = ds,
2575 }, .{
2576 .parent_atom_index = init_sym_index,
2577 })
2578 else
2579 try codegen.generateSymbol(&self.base, decl.srcLoc(mod), .{
2580 .ty = decl.ty,
2581 .val = decl_val,
2582 }, &code_buffer, .none, .{
2583 .parent_atom_index = init_sym_index,
2584 });
2585
2586 const code = switch (res) {
2587 .ok => code_buffer.items,
2588 .fail => |em| {
2589 decl.analysis = .codegen_failure;
2590 try module.failed_decls.put(module.gpa, decl_index, em);
2591 return;
2592 },
2593 };
2594
2595 const required_alignment = decl.getAlignment(mod);
2596
2597 const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(module));
2598
2599 const init_sym_name = try std.fmt.allocPrint(gpa, "{s}$tlv$init", .{decl_name});
2600 defer gpa.free(init_sym_name);
2601
2602 const sect_id = decl_metadata.section;
2603 const init_sym = init_atom.getSymbolPtr(self);
2604 init_sym.n_strx = try self.strtab.insert(gpa, init_sym_name);
2605 init_sym.n_type = macho.N_SECT;
2606 init_sym.n_sect = sect_id + 1;
2607 init_sym.n_desc = 0;
2608 init_atom.size = code.len;
2609
2610 init_sym.n_value = try self.allocateAtom(init_atom_index, code.len, required_alignment);
2611 errdefer self.freeAtom(init_atom_index);
2612
2613 log.debug("allocated atom for {s} at 0x{x}", .{ init_sym_name, init_sym.n_value });
2614 log.debug(" (required alignment 0x{x})", .{required_alignment});
2615
2616 try self.writeAtom(init_atom_index, code);
2617
2618 if (decl_state) |*ds| {
2619 try self.d_sym.?.dwarf.commitDeclState(
2620 module,
2621 decl_index,
2622 init_sym.n_value,
2623 self.getAtom(init_atom_index).size,
2624 ds,
2625 );
2626 }
2627
2628 try self.updateExports(module, .{ .decl_index = decl_index }, module.getDeclExports(decl_index));
2629
2630 // 2. Create a TLV descriptor.
2631 const init_atom_sym_loc = init_atom.getSymbolWithLoc();
2632 const gop = try self.tlv_table.getOrPut(gpa, init_atom_sym_loc);
2633 assert(!gop.found_existing);
2634 gop.value_ptr.* = try self.createThreadLocalDescriptorAtom(decl_name, init_atom_sym_loc);
2635 self.markRelocsDirtyByTarget(init_atom_sym_loc);
2636}
2637
2638pub fn getOrCreateAtomForDecl(self: *MachO, decl_index: InternPool.DeclIndex) !Atom.Index {
2639 const gpa = self.base.comp.gpa;
2640 const gop = try self.decls.getOrPut(gpa, decl_index);
2641 if (!gop.found_existing) {
2642 const sym_index = try self.allocateSymbol();
2643 const atom_index = try self.createAtom(sym_index, .{});
2644 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index);
2645 gop.value_ptr.* = .{
2646 .atom = atom_index,
2647 .section = self.getDeclOutputSection(decl_index),
2648 .exports = .{},
2649 };
2650 }
2651 return gop.value_ptr.atom;
2652}
2653
2654fn getDeclOutputSection(self: *MachO, decl_index: InternPool.DeclIndex) u8 {
2655 const decl = self.base.comp.module.?.declPtr(decl_index);
2656 const ty = decl.ty;
2657 const val = decl.val;
2658 const mod = self.base.comp.module.?;
2659 const zig_ty = ty.zigTypeTag(mod);
2660 const any_non_single_threaded = self.base.comp.config.any_non_single_threaded;
2661 const optimize_mode = self.base.comp.root_mod.optimize_mode;
2662 const sect_id: u8 = blk: {
2663 // TODO finish and audit this function
2664 if (val.isUndefDeep(mod)) {
2665 if (optimize_mode == .ReleaseFast or optimize_mode == .ReleaseSmall) {
2666 @panic("TODO __DATA,__bss");
2667 } else {
2668 break :blk self.data_section_index.?;
2669 }
2670 }
2671
2672 if (val.getVariable(mod)) |variable| {
2673 if (variable.is_threadlocal and any_non_single_threaded) {
2674 break :blk self.thread_data_section_index.?;
2675 }
2676 break :blk self.data_section_index.?;
2677 }
2678
2679 switch (zig_ty) {
2680 // TODO: what if this is a function pointer?
2681 .Fn => break :blk self.text_section_index.?,
2682 else => {
2683 if (val.getVariable(mod)) |_| {
2684 break :blk self.data_section_index.?;
2685 }
2686 break :blk self.data_const_section_index.?;
2687 },
2688 }
2689 };
2690 return sect_id;
2691}
2692
2693fn updateDeclCode(self: *MachO, decl_index: InternPool.DeclIndex, code: []u8) !u64 {
2694 const gpa = self.base.comp.gpa;
2695 const mod = self.base.comp.module.?;
2696 const decl = mod.declPtr(decl_index);
2697
2698 const required_alignment = decl.getAlignment(mod);
2699
2700 const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod));
2701
2702 const decl_metadata = self.decls.get(decl_index).?;
2703 const atom_index = decl_metadata.atom;
2704 const atom = self.getAtom(atom_index);
2705 const sym_index = atom.getSymbolIndex().?;
2706 const sect_id = decl_metadata.section;
2707 const header = &self.sections.items(.header)[sect_id];
2708 const segment = self.getSegment(sect_id);
2709 const code_len = code.len;
2710
2711 if (atom.size != 0) {
2712 const sym = atom.getSymbolPtr(self);
2713 sym.n_strx = try self.strtab.insert(gpa, decl_name);
2714 sym.n_type = macho.N_SECT;
2715 sym.n_sect = sect_id + 1;
2716 sym.n_desc = 0;
2717
2718 const capacity = atom.capacity(self);
2719 const need_realloc = code_len > capacity or !required_alignment.check(sym.n_value);
2720
2721 if (need_realloc) {
2722 const vaddr = try self.growAtom(atom_index, code_len, required_alignment);
2723 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ decl_name, sym.n_value, vaddr });
2724 log.debug(" (required alignment 0x{x})", .{required_alignment});
2725
2726 if (vaddr != sym.n_value) {
2727 sym.n_value = vaddr;
2728 log.debug(" (updating GOT entry)", .{});
2729 const got_atom_index = self.got_table.lookup.get(.{ .sym_index = sym_index }).?;
2730 try self.writeOffsetTableEntry(got_atom_index);
2731 self.markRelocsDirtyByTarget(.{ .sym_index = sym_index });
2732 }
2733 } else if (code_len < atom.size) {
2734 self.shrinkAtom(atom_index, code_len);
2735 } else if (atom.next_index == null) {
2736 const needed_size = (sym.n_value + code_len) - segment.vmaddr;
2737 header.size = needed_size;
2738 }
2739 self.getAtomPtr(atom_index).size = code_len;
2740 } else {
2741 const sym = atom.getSymbolPtr(self);
2742 sym.n_strx = try self.strtab.insert(gpa, decl_name);
2743 sym.n_type = macho.N_SECT;
2744 sym.n_sect = sect_id + 1;
2745 sym.n_desc = 0;
2746
2747 const vaddr = try self.allocateAtom(atom_index, code_len, required_alignment);
2748 errdefer self.freeAtom(atom_index);
2749
2750 log.debug("allocated atom for {s} at 0x{x}", .{ decl_name, vaddr });
2751 log.debug(" (required alignment 0x{x})", .{required_alignment});
2752
2753 self.getAtomPtr(atom_index).size = code_len;
2754 sym.n_value = vaddr;
2755
2756 try self.addGotEntry(.{ .sym_index = sym_index });
2757 }
2758
2759 try self.writeAtom(atom_index, code);
2760
2761 return atom.getSymbol(self).n_value;
2762}
2763
2764pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl_index: InternPool.DeclIndex) !void {
2765 if (self.d_sym) |*d_sym| {
2766 try d_sym.dwarf.updateDeclLineNumber(module, decl_index);
2767 }
2768}
2769
2770pub fn updateExports(
2771 self: *MachO,
2772 mod: *Module,
2773 exported: Module.Exported,
2774 exports: []const *Module.Export,
2775) File.UpdateExportsError!void {
2776 if (build_options.skip_non_native and builtin.object_format != .macho) {
2777 @panic("Attempted to compile for object format that was disabled by build configuration");
2778 }
2779 if (self.llvm_object) |llvm_object|
2780 return llvm_object.updateExports(mod, exported, exports);
2781
2782 const tracy = trace(@src());
2783 defer tracy.end();
2784
2785 const gpa = self.base.comp.gpa;
2786
2787 const metadata = switch (exported) {
2788 .decl_index => |decl_index| blk: {
2789 _ = try self.getOrCreateAtomForDecl(decl_index);
2790 break :blk self.decls.getPtr(decl_index).?;
2791 },
2792 .value => |value| self.anon_decls.getPtr(value) orelse blk: {
2793 const first_exp = exports[0];
2794 const res = try self.lowerAnonDecl(value, .none, first_exp.getSrcLoc(mod));
2795 switch (res) {
2796 .ok => {},
2797 .fail => |em| {
2798 // TODO maybe it's enough to return an error here and let Module.processExportsInner
2799 // handle the error?
2800 try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1);
2801 mod.failed_exports.putAssumeCapacityNoClobber(first_exp, em);
2802 return;
2803 },
2804 }
2805 break :blk self.anon_decls.getPtr(value).?;
2806 },
2807 };
2808 const atom_index = metadata.atom;
2809 const atom = self.getAtom(atom_index);
2810 const sym = atom.getSymbol(self);
2811
2812 for (exports) |exp| {
2813 const exp_name = try std.fmt.allocPrint(gpa, "_{}", .{
2814 exp.opts.name.fmt(&mod.intern_pool),
2815 });
2816 defer gpa.free(exp_name);
2817
2818 log.debug("adding new export '{s}'", .{exp_name});
2819
2820 if (exp.opts.section.unwrap()) |section_name| {
2821 if (!mod.intern_pool.stringEqlSlice(section_name, "__text")) {
2822 try mod.failed_exports.putNoClobber(mod.gpa, exp, try Module.ErrorMsg.create(
2823 gpa,
2824 exp.getSrcLoc(mod),
2825 "Unimplemented: ExportOptions.section",
2826 .{},
2827 ));
2828 continue;
2829 }
2830 }
2831
2832 if (exp.opts.linkage == .LinkOnce) {
2833 try mod.failed_exports.putNoClobber(mod.gpa, exp, try Module.ErrorMsg.create(
2834 gpa,
2835 exp.getSrcLoc(mod),
2836 "Unimplemented: GlobalLinkage.LinkOnce",
2837 .{},
2838 ));
2839 continue;
2840 }
2841
2842 const global_sym_index = metadata.getExport(self, exp_name) orelse blk: {
2843 const global_sym_index = if (self.getGlobalIndex(exp_name)) |global_index| ind: {
2844 const global = self.globals.items[global_index];
2845 // TODO this is just plain wrong as it all should happen in a single `resolveSymbols`
2846 // pass. This will go away once we abstact away Zig's incremental compilation into
2847 // its own module.
2848 if (global.getFile() == null and self.getSymbol(global).undf()) {
2849 _ = self.unresolved.swapRemove(global_index);
2850 break :ind global.sym_index;
2851 }
2852 break :ind try self.allocateSymbol();
2853 } else try self.allocateSymbol();
2854 try metadata.exports.append(gpa, global_sym_index);
2855 break :blk global_sym_index;
2856 };
2857 const global_sym_loc = SymbolWithLoc{ .sym_index = global_sym_index };
2858 const global_sym = self.getSymbolPtr(global_sym_loc);
2859 global_sym.* = .{
2860 .n_strx = try self.strtab.insert(gpa, exp_name),
2861 .n_type = macho.N_SECT | macho.N_EXT,
2862 .n_sect = metadata.section + 1,
2863 .n_desc = 0,
2864 .n_value = sym.n_value,
2865 };
2866
2867 switch (exp.opts.linkage) {
2868 .Internal => {
2869 // Symbol should be hidden, or in MachO lingo, private extern.
2870 // We should also mark the symbol as Weak: n_desc == N_WEAK_DEF.
2871 global_sym.n_type |= macho.N_PEXT;
2872 global_sym.n_desc |= macho.N_WEAK_DEF;
2873 },
2874 .Strong => {},
2875 .Weak => {
2876 // Weak linkage is specified as part of n_desc field.
2877 // Symbol's n_type is like for a symbol with strong linkage.
2878 global_sym.n_desc |= macho.N_WEAK_DEF;
2879 },
2880 else => unreachable,
2881 }
2882
2883 self.resolveGlobalSymbol(global_sym_loc) catch |err| switch (err) {
2884 error.MultipleSymbolDefinitions => {
2885 // TODO: this needs rethinking
2886 const global = self.getGlobal(exp_name).?;
2887 if (global_sym_loc.sym_index != global.sym_index and global.getFile() != null) {
2888 _ = try mod.failed_exports.put(mod.gpa, exp, try Module.ErrorMsg.create(
2889 gpa,
2890 exp.getSrcLoc(mod),
2891 \\LinkError: symbol '{s}' defined multiple times
2892 ,
2893 .{exp_name},
2894 ));
2895 }
2896 },
2897 else => |e| return e,
2898 };
2899 }
2900}
2901
2902pub fn deleteDeclExport(
2903 self: *MachO,
2904 decl_index: InternPool.DeclIndex,
2905 name: InternPool.NullTerminatedString,
2906) Allocator.Error!void {
2907 if (self.llvm_object) |_| return;
2908 const metadata = self.decls.getPtr(decl_index) orelse return;
2909
2910 const gpa = self.base.comp.gpa;
2911 const mod = self.base.comp.module.?;
2912 const exp_name = try std.fmt.allocPrint(gpa, "_{s}", .{mod.intern_pool.stringToSlice(name)});
2913 defer gpa.free(exp_name);
2914 const sym_index = metadata.getExportPtr(self, exp_name) orelse return;
2915
2916 const sym_loc = SymbolWithLoc{ .sym_index = sym_index.* };
2917 const sym = self.getSymbolPtr(sym_loc);
2918 log.debug("deleting export '{s}'", .{exp_name});
2919 assert(sym.sect() and sym.ext());
2920 sym.* = .{
2921 .n_strx = 0,
2922 .n_type = 0,
2923 .n_sect = 0,
2924 .n_desc = 0,
2925 .n_value = 0,
2926 };
2927 self.locals_free_list.append(gpa, sym_index.*) catch {};
2928
2929 if (self.resolver.fetchRemove(exp_name)) |entry| {
2930 defer gpa.free(entry.key);
2931 self.globals_free_list.append(gpa, entry.value) catch {};
2932 self.globals.items[entry.value] = .{ .sym_index = 0 };
2933 }
2934
2935 sym_index.* = 0;
2936}
2937
2938fn freeUnnamedConsts(self: *MachO, decl_index: InternPool.DeclIndex) void {
2939 const gpa = self.base.comp.gpa;
2940 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;
2941 for (unnamed_consts.items) |atom| {
2942 self.freeAtom(atom);
2943 }
2944 unnamed_consts.clearAndFree(gpa);
2945}
2946
2947pub fn freeDecl(self: *MachO, decl_index: InternPool.DeclIndex) void {
2948 if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index);
2949 const gpa = self.base.comp.gpa;
2950 const mod = self.base.comp.module.?;
2951 const decl = mod.declPtr(decl_index);
2952
2953 log.debug("freeDecl {*}", .{decl});
2954
2955 if (self.decls.fetchSwapRemove(decl_index)) |const_kv| {
2956 var kv = const_kv;
2957 self.freeAtom(kv.value.atom);
2958 self.freeUnnamedConsts(decl_index);
2959 kv.value.exports.deinit(gpa);
2960 }
2961
2962 if (self.d_sym) |*d_sym| {
2963 d_sym.dwarf.freeDecl(decl_index);
2964 }
2965}
2966
2967pub fn getDeclVAddr(self: *MachO, decl_index: InternPool.DeclIndex, reloc_info: File.RelocInfo) !u64 {
2968 assert(self.llvm_object == null);
2969
2970 const this_atom_index = try self.getOrCreateAtomForDecl(decl_index);
2971 const sym_index = self.getAtom(this_atom_index).getSymbolIndex().?;
2972 const atom_index = self.getAtomIndexForSymbol(.{ .sym_index = reloc_info.parent_atom_index }).?;
2973 try Atom.addRelocation(self, atom_index, .{
2974 .type = .unsigned,
2975 .target = .{ .sym_index = sym_index },
2976 .offset = @as(u32, @intCast(reloc_info.offset)),
2977 .addend = reloc_info.addend,
2978 .pcrel = false,
2979 .length = 3,
2980 });
2981 try Atom.addRebase(self, atom_index, @as(u32, @intCast(reloc_info.offset)));
2982
2983 return 0;
2984}
2985
2986pub fn lowerAnonDecl(
2987 self: *MachO,
2988 decl_val: InternPool.Index,
2989 explicit_alignment: InternPool.Alignment,
2990 src_loc: Module.SrcLoc,
2991) !codegen.Result {
2992 const gpa = self.base.comp.gpa;
2993 const mod = self.base.comp.module.?;
2994 const ty = Type.fromInterned(mod.intern_pool.typeOf(decl_val));
2995 const decl_alignment = switch (explicit_alignment) {
2996 .none => ty.abiAlignment(mod),
2997 else => explicit_alignment,
2998 };
2999 if (self.anon_decls.get(decl_val)) |metadata| {
3000 const existing_addr = self.getAtom(metadata.atom).getSymbol(self).n_value;
3001 if (decl_alignment.check(existing_addr))
3002 return .ok;
3003 }
3004
3005 const val = Value.fromInterned(decl_val);
3006 const tv = TypedValue{ .ty = ty, .val = val };
3007 var name_buf: [32]u8 = undefined;
3008 const name = std.fmt.bufPrint(&name_buf, "__anon_{d}", .{
3009 @intFromEnum(decl_val),
3010 }) catch unreachable;
3011 const res = self.lowerConst(
3012 name,
3013 tv,
3014 decl_alignment,
3015 self.data_const_section_index.?,
3016 src_loc,
3017 ) catch |err| switch (err) {
3018 error.OutOfMemory => return error.OutOfMemory,
3019 else => |e| return .{ .fail = try Module.ErrorMsg.create(
3020 gpa,
3021 src_loc,
3022 "unable to lower constant value: {s}",
3023 .{@errorName(e)},
3024 ) },
3025 };
3026 const atom_index = switch (res) {
3027 .ok => |atom_index| atom_index,
3028 .fail => |em| return .{ .fail = em },
3029 };
3030 try self.anon_decls.put(gpa, decl_val, .{
3031 .atom = atom_index,
3032 .section = self.data_const_section_index.?,
3033 });
3034 return .ok;
3035}
3036
3037pub fn getAnonDeclVAddr(self: *MachO, decl_val: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 {
3038 assert(self.llvm_object == null);
3039
3040 const this_atom_index = self.anon_decls.get(decl_val).?.atom;
3041 const sym_index = self.getAtom(this_atom_index).getSymbolIndex().?;
3042 const atom_index = self.getAtomIndexForSymbol(.{ .sym_index = reloc_info.parent_atom_index }).?;
3043 try Atom.addRelocation(self, atom_index, .{
3044 .type = .unsigned,
3045 .target = .{ .sym_index = sym_index },
3046 .offset = @as(u32, @intCast(reloc_info.offset)),
3047 .addend = reloc_info.addend,
3048 .pcrel = false,
3049 .length = 3,
3050 });
3051 try Atom.addRebase(self, atom_index, @as(u32, @intCast(reloc_info.offset)));
3052
3053 return 0;
3054}
3055
3056const PopulateMissingMetadataOptions = struct {
3057 symbol_count_hint: u64,
3058 program_code_size_hint: u64,
3059};
3060
3061fn populateMissingMetadata(self: *MachO, options: PopulateMissingMetadataOptions) !void {
3062 assert(self.mode == .incremental);
3063
3064 const comp = self.base.comp;
3065 const gpa = comp.gpa;
3066 const target = comp.root_mod.resolved_target.result;
3067 const cpu_arch = target.cpu.arch;
3068 const pagezero_vmsize = self.calcPagezeroSize();
3069
3070 if (self.pagezero_segment_cmd_index == null) {
3071 if (pagezero_vmsize > 0) {
3072 self.pagezero_segment_cmd_index = @as(u8, @intCast(self.segments.items.len));
3073 try self.segments.append(gpa, .{
3074 .segname = makeStaticString("__PAGEZERO"),
3075 .vmsize = pagezero_vmsize,
3076 .cmdsize = @sizeOf(macho.segment_command_64),
3077 });
3078 }
3079 }
3080
3081 if (self.header_segment_cmd_index == null) {
3082 // The first __TEXT segment is immovable and covers MachO header and load commands.
3083 self.header_segment_cmd_index = @as(u8, @intCast(self.segments.items.len));
3084 const ideal_size = self.headerpad_size;
3085 const needed_size = mem.alignForward(u64, padToIdeal(ideal_size), getPageSize(cpu_arch));
3086
3087 log.debug("found __TEXT segment (header-only) free space 0x{x} to 0x{x}", .{ 0, needed_size });
3088
3089 try self.segments.append(gpa, .{
3090 .segname = makeStaticString("__TEXT"),
3091 .vmaddr = pagezero_vmsize,
3092 .vmsize = needed_size,
3093 .filesize = needed_size,
3094 .maxprot = macho.PROT.READ | macho.PROT.EXEC,
3095 .initprot = macho.PROT.READ | macho.PROT.EXEC,
3096 .cmdsize = @sizeOf(macho.segment_command_64),
3097 });
3098 self.segment_table_dirty = true;
3099 }
3100
3101 if (self.text_section_index == null) {
3102 // Sadly, segments need unique string identfiers for some reason.
3103 self.text_section_index = try self.allocateSection("__TEXT1", "__text", .{
3104 .size = options.program_code_size_hint,
3105 .alignment = switch (cpu_arch) {
3106 .x86_64 => 1,
3107 .aarch64 => @sizeOf(u32),
3108 else => unreachable, // unhandled architecture type
3109 },
3110 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
3111 .prot = macho.PROT.READ | macho.PROT.EXEC,
3112 });
3113 self.segment_table_dirty = true;
3114 }
3115
3116 if (self.stubs_section_index == null) {
3117 const stub_size = stubs.stubSize(cpu_arch);
3118 self.stubs_section_index = try self.allocateSection("__TEXT2", "__stubs", .{
3119 .size = stub_size,
3120 .alignment = stubs.stubAlignment(cpu_arch),
3121 .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
3122 .reserved2 = stub_size,
3123 .prot = macho.PROT.READ | macho.PROT.EXEC,
3124 });
3125 self.segment_table_dirty = true;
3126 }
3127
3128 if (self.stub_helper_section_index == null) {
3129 self.stub_helper_section_index = try self.allocateSection("__TEXT3", "__stub_helper", .{
3130 .size = @sizeOf(u32),
3131 .alignment = stubs.stubAlignment(cpu_arch),
3132 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
3133 .prot = macho.PROT.READ | macho.PROT.EXEC,
3134 });
3135 self.segment_table_dirty = true;
3136 }
3137
3138 if (self.got_section_index == null) {
3139 self.got_section_index = try self.allocateSection("__DATA_CONST", "__got", .{
3140 .size = @sizeOf(u64) * options.symbol_count_hint,
3141 .alignment = @alignOf(u64),
3142 .flags = macho.S_NON_LAZY_SYMBOL_POINTERS,
3143 .prot = macho.PROT.READ | macho.PROT.WRITE,
3144 });
3145 self.segment_table_dirty = true;
3146 }
3147
3148 if (self.data_const_section_index == null) {
3149 self.data_const_section_index = try self.allocateSection("__DATA_CONST1", "__const", .{
3150 .size = @sizeOf(u64),
3151 .alignment = @alignOf(u64),
3152 .flags = macho.S_REGULAR,
3153 .prot = macho.PROT.READ | macho.PROT.WRITE,
3154 });
3155 self.segment_table_dirty = true;
3156 }
3157
3158 if (self.la_symbol_ptr_section_index == null) {
3159 self.la_symbol_ptr_section_index = try self.allocateSection("__DATA", "__la_symbol_ptr", .{
3160 .size = @sizeOf(u64),
3161 .alignment = @alignOf(u64),
3162 .flags = macho.S_LAZY_SYMBOL_POINTERS,
3163 .prot = macho.PROT.READ | macho.PROT.WRITE,
3164 });
3165 self.segment_table_dirty = true;
3166 }
3167
3168 if (self.data_section_index == null) {
3169 self.data_section_index = try self.allocateSection("__DATA1", "__data", .{
3170 .size = @sizeOf(u64),
3171 .alignment = @alignOf(u64),
3172 .flags = macho.S_REGULAR,
3173 .prot = macho.PROT.READ | macho.PROT.WRITE,
3174 });
3175 self.segment_table_dirty = true;
3176 }
3177
3178 if (comp.config.any_non_single_threaded) {
3179 if (self.thread_vars_section_index == null) {
3180 self.thread_vars_section_index = try self.allocateSection("__DATA2", "__thread_vars", .{
3181 .size = @sizeOf(u64) * 3,
3182 .alignment = @sizeOf(u64),
3183 .flags = macho.S_THREAD_LOCAL_VARIABLES,
3184 .prot = macho.PROT.READ | macho.PROT.WRITE,
3185 });
3186 self.segment_table_dirty = true;
3187 }
3188
3189 if (self.thread_data_section_index == null) {
3190 self.thread_data_section_index = try self.allocateSection("__DATA3", "__thread_data", .{
3191 .size = @sizeOf(u64),
3192 .alignment = @alignOf(u64),
3193 .flags = macho.S_THREAD_LOCAL_REGULAR,
3194 .prot = macho.PROT.READ | macho.PROT.WRITE,
3195 });
3196 self.segment_table_dirty = true;
3197 }
3198 }
3199
3200 if (self.linkedit_segment_cmd_index == null) {
3201 self.linkedit_segment_cmd_index = @as(u8, @intCast(self.segments.items.len));
3202
3203 try self.segments.append(gpa, .{
3204 .segname = makeStaticString("__LINKEDIT"),
3205 .maxprot = macho.PROT.READ,
3206 .initprot = macho.PROT.READ,
3207 .cmdsize = @sizeOf(macho.segment_command_64),
3208 });
3209 }
3210}
3211
3212fn calcPagezeroSize(self: *MachO) u64 {
3213 const output_mode = self.base.comp.config.output_mode;
3214 const target = self.base.comp.root_mod.resolved_target.result;
3215 const page_size = getPageSize(target.cpu.arch);
3216 const aligned_pagezero_vmsize = mem.alignBackward(u64, self.pagezero_vmsize, page_size);
3217 if (output_mode == .Lib) return 0;
3218 if (aligned_pagezero_vmsize == 0) return 0;
3219 if (aligned_pagezero_vmsize != self.pagezero_vmsize) {
3220 log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{self.pagezero_vmsize});
3221 log.warn(" rounding down to 0x{x}", .{aligned_pagezero_vmsize});
3222 }
3223 return aligned_pagezero_vmsize;
3224}
3225
3226const InitSectionOpts = struct {
3227 flags: u32 = macho.S_REGULAR,
3228 reserved1: u32 = 0,
3229 reserved2: u32 = 0,
3230};
3231
3232pub fn initSection(self: *MachO, segname: []const u8, sectname: []const u8, opts: InitSectionOpts) !u8 {
3233 log.debug("creating section '{s},{s}'", .{ segname, sectname });
3234 const index = @as(u8, @intCast(self.sections.slice().len));
3235 const gpa = self.base.comp.gpa;
3236 try self.sections.append(gpa, .{
3237 .segment_index = undefined, // Segments will be created automatically later down the pipeline
3238 .header = .{
3239 .sectname = makeStaticString(sectname),
3240 .segname = makeStaticString(segname),
3241 .flags = opts.flags,
3242 .reserved1 = opts.reserved1,
3243 .reserved2 = opts.reserved2,
3244 },
3245 });
3246 return index;
3247}
3248
3249fn allocateSection(self: *MachO, segname: []const u8, sectname: []const u8, opts: struct {
3250 size: u64 = 0,
3251 alignment: u32 = 0,
3252 prot: macho.vm_prot_t = macho.PROT.NONE,
3253 flags: u32 = macho.S_REGULAR,
3254 reserved2: u32 = 0,
3255}) !u8 {
3256 const gpa = self.base.comp.gpa;
3257 const target = self.base.comp.root_mod.resolved_target.result;
3258 const page_size = getPageSize(target.cpu.arch);
3259 // In incremental context, we create one section per segment pairing. This way,
3260 // we can move the segment in raw file as we please.
3261 const segment_id = @as(u8, @intCast(self.segments.items.len));
3262 const vmaddr = blk: {
3263 const prev_segment = self.segments.items[segment_id - 1];
3264 break :blk mem.alignForward(u64, prev_segment.vmaddr + prev_segment.vmsize, page_size);
3265 };
3266 // We commit more memory than needed upfront so that we don't have to reallocate too soon.
3267 const vmsize = mem.alignForward(u64, opts.size, page_size);
3268 const off = self.findFreeSpace(opts.size, page_size);
3269
3270 log.debug("found {s},{s} free space 0x{x} to 0x{x} (0x{x} - 0x{x})", .{
3271 segname,
3272 sectname,
3273 off,
3274 off + opts.size,
3275 vmaddr,
3276 vmaddr + vmsize,
3277 });
3278
3279 const seg = try self.segments.addOne(gpa);
3280 seg.* = .{
3281 .segname = makeStaticString(segname),
3282 .vmaddr = vmaddr,
3283 .vmsize = vmsize,
3284 .fileoff = off,
3285 .filesize = vmsize,
3286 .maxprot = opts.prot,
3287 .initprot = opts.prot,
3288 .nsects = 1,
3289 .cmdsize = @sizeOf(macho.segment_command_64) + @sizeOf(macho.section_64),
3290 };
3291
3292 const sect_id = try self.initSection(segname, sectname, .{
3293 .flags = opts.flags,
3294 .reserved2 = opts.reserved2,
3295 });
3296 const section = &self.sections.items(.header)[sect_id];
3297 section.addr = mem.alignForward(u64, vmaddr, opts.alignment);
3298 section.offset = mem.alignForward(u32, @as(u32, @intCast(off)), opts.alignment);
3299 section.size = opts.size;
3300 section.@"align" = math.log2(opts.alignment);
3301 self.sections.items(.segment_index)[sect_id] = segment_id;
3302 assert(!section.isZerofill()); // TODO zerofill sections
3303
3304 return sect_id;
3305}
3306
3307fn growSection(self: *MachO, sect_id: u8, needed_size: u64) !void {
3308 const header = &self.sections.items(.header)[sect_id];
3309 const segment_index = self.sections.items(.segment_index)[sect_id];
3310 const segment = &self.segments.items[segment_index];
3311 const maybe_last_atom_index = self.sections.items(.last_atom_index)[sect_id];
3312 const sect_capacity = self.allocatedSize(header.offset);
3313 const target = self.base.comp.root_mod.resolved_target.result;
3314 const page_size = getPageSize(target.cpu.arch);
3315
3316 if (needed_size > sect_capacity) {
3317 const new_offset = self.findFreeSpace(needed_size, page_size);
3318 const current_size = if (maybe_last_atom_index) |last_atom_index| blk: {
3319 const last_atom = self.getAtom(last_atom_index);
3320 const sym = last_atom.getSymbol(self);
3321 break :blk (sym.n_value + last_atom.size) - segment.vmaddr;
3322 } else header.size;
3323
3324 log.debug("moving {s},{s} from 0x{x} to 0x{x}", .{
3325 header.segName(),
3326 header.sectName(),
3327 header.offset,
3328 new_offset,
3329 });
3330
3331 const amt = try self.base.file.?.copyRangeAll(
3332 header.offset,
3333 self.base.file.?,
3334 new_offset,
3335 current_size,
3336 );
3337 if (amt != current_size) return error.InputOutput;
3338 header.offset = @as(u32, @intCast(new_offset));
3339 segment.fileoff = new_offset;
3340 }
3341
3342 const sect_vm_capacity = self.allocatedVirtualSize(segment.vmaddr);
3343 if (needed_size > sect_vm_capacity) {
3344 self.markRelocsDirtyByAddress(segment.vmaddr + segment.vmsize);
3345 try self.growSectionVirtualMemory(sect_id, needed_size);
3346 }
3347
3348 header.size = needed_size;
3349 segment.filesize = mem.alignForward(u64, needed_size, page_size);
3350 segment.vmsize = mem.alignForward(u64, needed_size, page_size);
3351}
3352
3353fn growSectionVirtualMemory(self: *MachO, sect_id: u8, needed_size: u64) !void {
3354 const target = self.base.comp.root_mod.resolved_target.result;
3355 const page_size = getPageSize(target.cpu.arch);
3356 const header = &self.sections.items(.header)[sect_id];
3357 const segment = self.getSegmentPtr(sect_id);
3358 const increased_size = padToIdeal(needed_size);
3359 const old_aligned_end = segment.vmaddr + segment.vmsize;
3360 const new_aligned_end = segment.vmaddr + mem.alignForward(u64, increased_size, page_size);
3361 const diff = new_aligned_end - old_aligned_end;
3362 log.debug("shifting every segment after {s},{s} in virtual memory by {x}", .{
3363 header.segName(),
3364 header.sectName(),
3365 diff,
3366 });
3367
3368 // TODO: enforce order by increasing VM addresses in self.sections container.
3369 for (self.sections.items(.header)[sect_id + 1 ..], 0..) |*next_header, next_sect_id| {
3370 const index = @as(u8, @intCast(sect_id + 1 + next_sect_id));
3371 const next_segment = self.getSegmentPtr(index);
3372 next_header.addr += diff;
3373 next_segment.vmaddr += diff;
3374
3375 const maybe_last_atom_index = &self.sections.items(.last_atom_index)[index];
3376 if (maybe_last_atom_index.*) |last_atom_index| {
3377 var atom_index = last_atom_index;
3378 while (true) {
3379 const atom = self.getAtom(atom_index);
3380 const sym = atom.getSymbolPtr(self);
3381 sym.n_value += diff;
3382
3383 if (atom.prev_index) |prev_index| {
3384 atom_index = prev_index;
3385 } else break;
3386 }
3387 }
3388 }
3389}
3390
3391pub fn addAtomToSection(self: *MachO, atom_index: Atom.Index) void {
3392 assert(self.mode == .zld);
3393 const atom = self.getAtomPtr(atom_index);
3394 const sym = self.getSymbol(atom.getSymbolWithLoc());
3395 var section = self.sections.get(sym.n_sect - 1);
3396 if (section.header.size > 0) {
3397 const last_atom = self.getAtomPtr(section.last_atom_index.?);
3398 last_atom.next_index = atom_index;
3399 atom.prev_index = section.last_atom_index;
3400 } else {
3401 section.first_atom_index = atom_index;
3402 }
3403 section.last_atom_index = atom_index;
3404 section.header.size += atom.size;
3405 self.sections.set(sym.n_sect - 1, section);
3406}
3407
3408fn allocateAtom(self: *MachO, atom_index: Atom.Index, new_atom_size: u64, alignment: Alignment) !u64 {
3409 const tracy = trace(@src());
3410 defer tracy.end();
3411
3412 assert(self.mode == .incremental);
3413
3414 const atom = self.getAtom(atom_index);
3415 const sect_id = atom.getSymbol(self).n_sect - 1;
3416 const segment = self.getSegmentPtr(sect_id);
3417 const header = &self.sections.items(.header)[sect_id];
3418 const free_list = &self.sections.items(.free_list)[sect_id];
3419 const maybe_last_atom_index = &self.sections.items(.last_atom_index)[sect_id];
3420 const requires_padding = blk: {
3421 if (!header.isCode()) break :blk false;
3422 if (header.isSymbolStubs()) break :blk false;
3423 if (mem.eql(u8, "__stub_helper", header.sectName())) break :blk false;
3424 break :blk true;
3425 };
3426 const new_atom_ideal_capacity = if (requires_padding) padToIdeal(new_atom_size) else new_atom_size;
3427
3428 // We use these to indicate our intention to update metadata, placing the new atom,
3429 // and possibly removing a free list node.
3430 // It would be simpler to do it inside the for loop below, but that would cause a
3431 // problem if an error was returned later in the function. So this action
3432 // is actually carried out at the end of the function, when errors are no longer possible.
3433 var atom_placement: ?Atom.Index = null;
3434 var free_list_removal: ?usize = null;
3435
3436 // First we look for an appropriately sized free list node.
3437 // The list is unordered. We'll just take the first thing that works.
3438 const vaddr = blk: {
3439 var i: usize = 0;
3440 while (i < free_list.items.len) {
3441 const big_atom_index = free_list.items[i];
3442 const big_atom = self.getAtom(big_atom_index);
3443 // We now have a pointer to a live atom that has too much capacity.
3444 // Is it enough that we could fit this new atom?
3445 const sym = big_atom.getSymbol(self);
3446 const capacity = big_atom.capacity(self);
3447 const ideal_capacity = if (requires_padding) padToIdeal(capacity) else capacity;
3448 const ideal_capacity_end_vaddr = math.add(u64, sym.n_value, ideal_capacity) catch ideal_capacity;
3449 const capacity_end_vaddr = sym.n_value + capacity;
3450 const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity;
3451 const new_start_vaddr = alignment.backward(new_start_vaddr_unaligned);
3452 if (new_start_vaddr < ideal_capacity_end_vaddr) {
3453 // Additional bookkeeping here to notice if this free list node
3454 // should be deleted because the atom that it points to has grown to take up
3455 // more of the extra capacity.
3456 if (!big_atom.freeListEligible(self)) {
3457 _ = free_list.swapRemove(i);
3458 } else {
3459 i += 1;
3460 }
3461 continue;
3462 }
3463 // At this point we know that we will place the new atom here. But the
3464 // remaining question is whether there is still yet enough capacity left
3465 // over for there to still be a free list node.
3466 const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr;
3467 const keep_free_list_node = remaining_capacity >= min_text_capacity;
3468
3469 // Set up the metadata to be updated, after errors are no longer possible.
3470 atom_placement = big_atom_index;
3471 if (!keep_free_list_node) {
3472 free_list_removal = i;
3473 }
3474 break :blk new_start_vaddr;
3475 } else if (maybe_last_atom_index.*) |last_index| {
3476 const last = self.getAtom(last_index);
3477 const last_symbol = last.getSymbol(self);
3478 const ideal_capacity = if (requires_padding) padToIdeal(last.size) else last.size;
3479 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;
3480 const new_start_vaddr = alignment.forward(ideal_capacity_end_vaddr);
3481 atom_placement = last_index;
3482 break :blk new_start_vaddr;
3483 } else {
3484 break :blk alignment.forward(segment.vmaddr);
3485 }
3486 };
3487
3488 const expand_section = if (atom_placement) |placement_index|
3489 self.getAtom(placement_index).next_index == null
3490 else
3491 true;
3492 if (expand_section) {
3493 const needed_size = (vaddr + new_atom_size) - segment.vmaddr;
3494 try self.growSection(sect_id, needed_size);
3495 maybe_last_atom_index.* = atom_index;
3496 self.segment_table_dirty = true;
3497 }
3498
3499 assert(alignment != .none);
3500 header.@"align" = @min(header.@"align", @intFromEnum(alignment));
3501 self.getAtomPtr(atom_index).size = new_atom_size;
3502
3503 if (atom.prev_index) |prev_index| {
3504 const prev = self.getAtomPtr(prev_index);
3505 prev.next_index = atom.next_index;
3506 }
3507 if (atom.next_index) |next_index| {
3508 const next = self.getAtomPtr(next_index);
3509 next.prev_index = atom.prev_index;
3510 }
3511
3512 if (atom_placement) |big_atom_index| {
3513 const big_atom = self.getAtomPtr(big_atom_index);
3514 const atom_ptr = self.getAtomPtr(atom_index);
3515 atom_ptr.prev_index = big_atom_index;
3516 atom_ptr.next_index = big_atom.next_index;
3517 big_atom.next_index = atom_index;
3518 } else {
3519 const atom_ptr = self.getAtomPtr(atom_index);
3520 atom_ptr.prev_index = null;
3521 atom_ptr.next_index = null;
3522 }
3523 if (free_list_removal) |i| {
3524 _ = free_list.swapRemove(i);
3525 }
3526
3527 return vaddr;
3528}
3529
3530pub fn getGlobalSymbol(self: *MachO, name: []const u8, lib_name: ?[]const u8) !u32 {
3531 _ = lib_name;
3532 const gpa = self.base.comp.gpa;
3533 const sym_name = try std.fmt.allocPrint(gpa, "_{s}", .{name});
3534 defer gpa.free(sym_name);
3535 return self.addUndefined(sym_name, .{ .add_stub = true });
3536}
3537
3538pub fn writeSegmentHeaders(self: *MachO, writer: anytype) !void {
3539 for (self.segments.items, 0..) |seg, i| {
3540 const indexes = self.getSectionIndexes(@intCast(i));
3541 var out_seg = seg;
3542 out_seg.cmdsize = @sizeOf(macho.segment_command_64);
3543 out_seg.nsects = 0;
3544
3545 // Update section headers count; any section with size of 0 is excluded
3546 // since it doesn't have any data in the final binary file.
3547 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
3548 if (header.size == 0) continue;
3549 out_seg.cmdsize += @sizeOf(macho.section_64);
3550 out_seg.nsects += 1;
3551 }
3552
3553 if (out_seg.nsects == 0 and
3554 (mem.eql(u8, out_seg.segName(), "__DATA_CONST") or
3555 mem.eql(u8, out_seg.segName(), "__DATA"))) continue;
3556
3557 try writer.writeStruct(out_seg);
3558 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
3559 if (header.size == 0) continue;
3560 try writer.writeStruct(header);
3561 }
3562 }
3563}
3564
3565pub fn writeLinkeditSegmentData(self: *MachO) !void {
3566 const target = self.base.comp.root_mod.resolved_target.result;
3567 const page_size = getPageSize(target.cpu.arch);
3568 const seg = self.getLinkeditSegmentPtr();
3569 seg.filesize = 0;
3570 seg.vmsize = 0;
3571
3572 for (self.segments.items, 0..) |segment, id| {
3573 if (self.linkedit_segment_cmd_index.? == @as(u8, @intCast(id))) continue;
3574 if (seg.vmaddr < segment.vmaddr + segment.vmsize) {
3575 seg.vmaddr = mem.alignForward(u64, segment.vmaddr + segment.vmsize, page_size);
3576 }
3577 if (seg.fileoff < segment.fileoff + segment.filesize) {
3578 seg.fileoff = mem.alignForward(u64, segment.fileoff + segment.filesize, page_size);
3579 }
3580 }
3581
3582 try self.writeDyldInfoData();
3583 // TODO handle this better
3584 if (self.mode == .zld) {
3585 try self.writeFunctionStarts();
3586 try self.writeDataInCode();
3587 }
3588 try self.writeSymtabs();
3589
3590 seg.vmsize = mem.alignForward(u64, seg.filesize, page_size);
3591}
3592
3593fn collectRebaseDataFromTableSection(self: *MachO, sect_id: u8, rebase: *Rebase, table: anytype) !void {
3594 const gpa = self.base.comp.gpa;
3595 const header = self.sections.items(.header)[sect_id];
3596 const segment_index = self.sections.items(.segment_index)[sect_id];
3597 const segment = self.segments.items[segment_index];
3598 const base_offset = header.addr - segment.vmaddr;
3599 const is_got = if (self.got_section_index) |index| index == sect_id else false;
3600
3601 try rebase.entries.ensureUnusedCapacity(gpa, table.entries.items.len);
3602
3603 for (table.entries.items, 0..) |entry, i| {
3604 if (!table.lookup.contains(entry)) continue;
3605 const sym = self.getSymbol(entry);
3606 if (is_got and sym.undf()) continue;
3607 const offset = i * @sizeOf(u64);
3608 log.debug(" | rebase at {x}", .{base_offset + offset});
3609 rebase.entries.appendAssumeCapacity(.{
3610 .offset = base_offset + offset,
3611 .segment_id = segment_index,
3612 });
3613 }
3614}
3615
3616fn collectRebaseData(self: *MachO, rebase: *Rebase) !void {
3617 const gpa = self.base.comp.gpa;
3618 const slice = self.sections.slice();
3619
3620 for (self.rebases.keys(), 0..) |atom_index, i| {
3621 const atom = self.getAtom(atom_index);
3622 log.debug(" ATOM(%{?d}, '{s}')", .{ atom.getSymbolIndex(), atom.getName(self) });
3623
3624 const sym = atom.getSymbol(self);
3625 const segment_index = slice.items(.segment_index)[sym.n_sect - 1];
3626 const seg = self.getSegment(sym.n_sect - 1);
3627
3628 const base_offset = sym.n_value - seg.vmaddr;
3629
3630 const rebases = self.rebases.values()[i];
3631 try rebase.entries.ensureUnusedCapacity(gpa, rebases.items.len);
3632
3633 for (rebases.items) |offset| {
3634 log.debug(" | rebase at {x}", .{base_offset + offset});
3635
3636 rebase.entries.appendAssumeCapacity(.{
3637 .offset = base_offset + offset,
3638 .segment_id = segment_index,
3639 });
3640 }
3641 }
3642
3643 // Unpack GOT entries
3644 if (self.got_section_index) |sect_id| {
3645 try self.collectRebaseDataFromTableSection(sect_id, rebase, self.got_table);
3646 }
3647
3648 // Next, unpack __la_symbol_ptr entries
3649 if (self.la_symbol_ptr_section_index) |sect_id| {
3650 try self.collectRebaseDataFromTableSection(sect_id, rebase, self.stub_table);
3651 }
3652
3653 // Finally, unpack the rest.
3654 const target = self.base.comp.root_mod.resolved_target.result;
3655 const cpu_arch = target.cpu.arch;
3656 for (self.objects.items) |*object| {
3657 for (object.atoms.items) |atom_index| {
3658 const atom = self.getAtom(atom_index);
3659 const sym = self.getSymbol(atom.getSymbolWithLoc());
3660 if (sym.n_desc == N_DEAD) continue;
3661 if (sym.n_desc == N_BOUNDARY) continue;
3662
3663 const sect_id = sym.n_sect - 1;
3664 const section = self.sections.items(.header)[sect_id];
3665 const segment_id = self.sections.items(.segment_index)[sect_id];
3666 const segment = self.segments.items[segment_id];
3667 if (segment.maxprot & macho.PROT.WRITE == 0) continue;
3668 switch (section.type()) {
3669 macho.S_LITERAL_POINTERS,
3670 macho.S_REGULAR,
3671 macho.S_MOD_INIT_FUNC_POINTERS,
3672 macho.S_MOD_TERM_FUNC_POINTERS,
3673 => {},
3674 else => continue,
3675 }
3676
3677 log.debug(" ATOM({d}, %{d}, '{s}')", .{
3678 atom_index,
3679 atom.sym_index,
3680 self.getSymbolName(atom.getSymbolWithLoc()),
3681 });
3682
3683 const code = Atom.getAtomCode(self, atom_index);
3684 const relocs = Atom.getAtomRelocs(self, atom_index);
3685 const ctx = Atom.getRelocContext(self, atom_index);
3686
3687 for (relocs) |rel| {
3688 switch (cpu_arch) {
3689 .aarch64 => {
3690 const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
3691 if (rel_type != .ARM64_RELOC_UNSIGNED) continue;
3692 if (rel.r_length != 3) continue;
3693 },
3694 .x86_64 => {
3695 const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
3696 if (rel_type != .X86_64_RELOC_UNSIGNED) continue;
3697 if (rel.r_length != 3) continue;
3698 },
3699 else => unreachable,
3700 }
3701 const reloc_target = Atom.parseRelocTarget(self, .{
3702 .object_id = atom.getFile().?,
3703 .rel = rel,
3704 .code = code,
3705 .base_offset = ctx.base_offset,
3706 .base_addr = ctx.base_addr,
3707 });
3708 const target_sym = self.getSymbol(reloc_target);
3709 if (target_sym.undf()) continue;
3710
3711 const base_offset = @as(i32, @intCast(sym.n_value - segment.vmaddr));
3712 const rel_offset = rel.r_address - ctx.base_offset;
3713 const offset = @as(u64, @intCast(base_offset + rel_offset));
3714 log.debug(" | rebase at {x}", .{offset});
3715
3716 try rebase.entries.append(gpa, .{
3717 .offset = offset,
3718 .segment_id = segment_id,
3719 });
3720 }
3721 }
3722 }
3723
3724 try rebase.finalize(gpa);
3725}
3726
3727fn collectBindDataFromTableSection(self: *MachO, sect_id: u8, bind: anytype, table: anytype) !void {
3728 const gpa = self.base.comp.gpa;
3729 const header = self.sections.items(.header)[sect_id];
3730 const segment_index = self.sections.items(.segment_index)[sect_id];
3731 const segment = self.segments.items[segment_index];
3732 const base_offset = header.addr - segment.vmaddr;
3733
3734 try bind.entries.ensureUnusedCapacity(gpa, table.entries.items.len);
3735
3736 for (table.entries.items, 0..) |entry, i| {
3737 if (!table.lookup.contains(entry)) continue;
3738 const bind_sym = self.getSymbol(entry);
3739 if (!bind_sym.undf()) continue;
3740 const offset = i * @sizeOf(u64);
3741 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
3742 base_offset + offset,
3743 self.getSymbolName(entry),
3744 @divTrunc(@as(i16, @bitCast(bind_sym.n_desc)), macho.N_SYMBOL_RESOLVER),
3745 });
3746 if (bind_sym.weakRef()) {
3747 log.debug(" | marking as weak ref ", .{});
3748 }
3749 bind.entries.appendAssumeCapacity(.{
3750 .target = entry,
3751 .offset = base_offset + offset,
3752 .segment_id = segment_index,
3753 .addend = 0,
3754 });
3755 }
3756}
3757
3758fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void {
3759 const gpa = self.base.comp.gpa;
3760 const slice = self.sections.slice();
3761
3762 for (raw_bindings.keys(), 0..) |atom_index, i| {
3763 const atom = self.getAtom(atom_index);
3764 log.debug(" ATOM(%{?d}, '{s}')", .{ atom.getSymbolIndex(), atom.getName(self) });
3765
3766 const sym = atom.getSymbol(self);
3767 const segment_index = slice.items(.segment_index)[sym.n_sect - 1];
3768 const seg = self.getSegment(sym.n_sect - 1);
3769
3770 const base_offset = sym.n_value - seg.vmaddr;
3771
3772 const bindings = raw_bindings.values()[i];
3773 try bind.entries.ensureUnusedCapacity(gpa, bindings.items.len);
3774
3775 for (bindings.items) |binding| {
3776 const bind_sym = self.getSymbol(binding.target);
3777 const bind_sym_name = self.getSymbolName(binding.target);
3778 const dylib_ordinal = @divTrunc(
3779 @as(i16, @bitCast(bind_sym.n_desc)),
3780 macho.N_SYMBOL_RESOLVER,
3781 );
3782 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
3783 binding.offset + base_offset,
3784 bind_sym_name,
3785 dylib_ordinal,
3786 });
3787 if (bind_sym.weakRef()) {
3788 log.debug(" | marking as weak ref ", .{});
3789 }
3790 bind.entries.appendAssumeCapacity(.{
3791 .target = binding.target,
3792 .offset = binding.offset + base_offset,
3793 .segment_id = segment_index,
3794 .addend = 0,
3795 });
3796 }
3797 }
3798
3799 // Unpack GOT pointers
3800 if (self.got_section_index) |sect_id| {
3801 try self.collectBindDataFromTableSection(sect_id, bind, self.got_table);
3802 }
3803
3804 // Next, unpack TLV pointers section
3805 if (self.tlv_ptr_section_index) |sect_id| {
3806 try self.collectBindDataFromTableSection(sect_id, bind, self.tlv_ptr_table);
3807 }
3808
3809 // Finally, unpack the rest.
3810 const target = self.base.comp.root_mod.resolved_target.result;
3811 const cpu_arch = target.cpu.arch;
3812 for (self.objects.items) |*object| {
3813 for (object.atoms.items) |atom_index| {
3814 const atom = self.getAtom(atom_index);
3815 const sym = self.getSymbol(atom.getSymbolWithLoc());
3816 if (sym.n_desc == N_DEAD) continue;
3817 if (sym.n_desc == N_BOUNDARY) continue;
3818
3819 const sect_id = sym.n_sect - 1;
3820 const section = self.sections.items(.header)[sect_id];
3821 const segment_id = self.sections.items(.segment_index)[sect_id];
3822 const segment = self.segments.items[segment_id];
3823 if (segment.maxprot & macho.PROT.WRITE == 0) continue;
3824 switch (section.type()) {
3825 macho.S_LITERAL_POINTERS,
3826 macho.S_REGULAR,
3827 macho.S_MOD_INIT_FUNC_POINTERS,
3828 macho.S_MOD_TERM_FUNC_POINTERS,
3829 => {},
3830 else => continue,
3831 }
3832
3833 log.debug(" ATOM({d}, %{d}, '{s}')", .{
3834 atom_index,
3835 atom.sym_index,
3836 self.getSymbolName(atom.getSymbolWithLoc()),
3837 });
3838
3839 const code = Atom.getAtomCode(self, atom_index);
3840 const relocs = Atom.getAtomRelocs(self, atom_index);
3841 const ctx = Atom.getRelocContext(self, atom_index);
3842
3843 for (relocs) |rel| {
3844 switch (cpu_arch) {
3845 .aarch64 => {
3846 const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
3847 if (rel_type != .ARM64_RELOC_UNSIGNED) continue;
3848 if (rel.r_length != 3) continue;
3849 },
3850 .x86_64 => {
3851 const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
3852 if (rel_type != .X86_64_RELOC_UNSIGNED) continue;
3853 if (rel.r_length != 3) continue;
3854 },
3855 else => unreachable,
3856 }
3857
3858 const global = Atom.parseRelocTarget(self, .{
3859 .object_id = atom.getFile().?,
3860 .rel = rel,
3861 .code = code,
3862 .base_offset = ctx.base_offset,
3863 .base_addr = ctx.base_addr,
3864 });
3865 const bind_sym_name = self.getSymbolName(global);
3866 const bind_sym = self.getSymbol(global);
3867 if (!bind_sym.undf()) continue;
3868
3869 const base_offset = sym.n_value - segment.vmaddr;
3870 const rel_offset = @as(u32, @intCast(rel.r_address - ctx.base_offset));
3871 const offset = @as(u64, @intCast(base_offset + rel_offset));
3872 const addend = mem.readInt(i64, code[rel_offset..][0..8], .little);
3873
3874 const dylib_ordinal = @divTrunc(@as(i16, @bitCast(bind_sym.n_desc)), macho.N_SYMBOL_RESOLVER);
3875 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
3876 base_offset,
3877 bind_sym_name,
3878 dylib_ordinal,
3879 });
3880 log.debug(" | with addend {x}", .{addend});
3881 if (bind_sym.weakRef()) {
3882 log.debug(" | marking as weak ref ", .{});
3883 }
3884 try bind.entries.append(gpa, .{
3885 .target = global,
3886 .offset = offset,
3887 .segment_id = segment_id,
3888 .addend = addend,
3889 });
3890 }
3891 }
3892 }
3893
3894 try bind.finalize(gpa, self);
3895}
3896
3897fn collectLazyBindData(self: *MachO, bind: anytype) !void {
3898 const sect_id = self.la_symbol_ptr_section_index orelse return;
3899 const gpa = self.base.comp.gpa;
3900 try self.collectBindDataFromTableSection(sect_id, bind, self.stub_table);
3901 try bind.finalize(gpa, self);
3902}
3903
3904fn collectExportData(self: *MachO, trie: *Trie) !void {
3905 const gpa = self.base.comp.gpa;
3906
3907 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
3908 log.debug("generating export trie", .{});
3909
3910 const exec_segment = self.segments.items[self.header_segment_cmd_index.?];
3911 const base_address = exec_segment.vmaddr;
3912
3913 for (self.globals.items) |global| {
3914 const sym = self.getSymbol(global);
3915
3916 if (sym.undf()) continue;
3917 assert(sym.ext());
3918 if (sym.n_desc == N_DEAD) continue;
3919 if (sym.n_desc == N_BOUNDARY) continue;
3920
3921 const sym_name = self.getSymbolName(global);
3922 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
3923 try trie.put(gpa, .{
3924 .name = sym_name,
3925 .vmaddr_offset = sym.n_value - base_address,
3926 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
3927 });
3928 }
3929
3930 try trie.finalize(gpa);
3931}
3932
3933fn writeDyldInfoData(self: *MachO) !void {
3934 const tracy = trace(@src());
3935 defer tracy.end();
3936
3937 const gpa = self.base.comp.gpa;
3938
3939 var rebase = Rebase{};
3940 defer rebase.deinit(gpa);
3941 try self.collectRebaseData(&rebase);
3942
3943 var bind = Bind{};
3944 defer bind.deinit(gpa);
3945 try self.collectBindData(&bind, self.bindings);
3946
3947 var lazy_bind = LazyBind{};
3948 defer lazy_bind.deinit(gpa);
3949 try self.collectLazyBindData(&lazy_bind);
3950
3951 var trie: Trie = .{};
3952 defer trie.deinit(gpa);
3953 try trie.init(gpa);
3954 try self.collectExportData(&trie);
3955
3956 const link_seg = self.getLinkeditSegmentPtr();
3957 assert(mem.isAlignedGeneric(u64, link_seg.fileoff, @alignOf(u64)));
3958 const rebase_off = link_seg.fileoff;
3959 const rebase_size = rebase.size();
3960 const rebase_size_aligned = mem.alignForward(u64, rebase_size, @alignOf(u64));
3961 log.debug("writing rebase info from 0x{x} to 0x{x}", .{ rebase_off, rebase_off + rebase_size_aligned });
3962
3963 const bind_off = rebase_off + rebase_size_aligned;
3964 const bind_size = bind.size();
3965 const bind_size_aligned = mem.alignForward(u64, bind_size, @alignOf(u64));
3966 log.debug("writing bind info from 0x{x} to 0x{x}", .{ bind_off, bind_off + bind_size_aligned });
3967
3968 const lazy_bind_off = bind_off + bind_size_aligned;
3969 const lazy_bind_size = lazy_bind.size();
3970 const lazy_bind_size_aligned = mem.alignForward(u64, lazy_bind_size, @alignOf(u64));
3971 log.debug("writing lazy bind info from 0x{x} to 0x{x}", .{
3972 lazy_bind_off,
3973 lazy_bind_off + lazy_bind_size_aligned,
3974 });
3975
3976 const export_off = lazy_bind_off + lazy_bind_size_aligned;
3977 const export_size = trie.size;
3978 const export_size_aligned = mem.alignForward(u64, export_size, @alignOf(u64));
3979 log.debug("writing export trie from 0x{x} to 0x{x}", .{ export_off, export_off + export_size_aligned });
3980
3981 const needed_size = math.cast(usize, export_off + export_size_aligned - rebase_off) orelse
3982 return error.Overflow;
3983 link_seg.filesize = needed_size;
3984 assert(mem.isAlignedGeneric(u64, link_seg.fileoff + link_seg.filesize, @alignOf(u64)));
3985
3986 const buffer = try gpa.alloc(u8, needed_size);
3987 defer gpa.free(buffer);
3988 @memset(buffer, 0);
3989
3990 var stream = std.io.fixedBufferStream(buffer);
3991 const writer = stream.writer();
3992
3993 try rebase.write(writer);
3994 try stream.seekTo(bind_off - rebase_off);
3995
3996 try bind.write(writer);
3997 try stream.seekTo(lazy_bind_off - rebase_off);
3998
3999 try lazy_bind.write(writer);
4000 try stream.seekTo(export_off - rebase_off);
4001
4002 _ = try trie.write(writer);
4003
4004 log.debug("writing dyld info from 0x{x} to 0x{x}", .{
4005 rebase_off,
4006 rebase_off + needed_size,
4007 });
4008
4009 try self.base.file.?.pwriteAll(buffer, rebase_off);
4010 try self.populateLazyBindOffsetsInStubHelper(lazy_bind);
4011
4012 self.dyld_info_cmd.rebase_off = @as(u32, @intCast(rebase_off));
4013 self.dyld_info_cmd.rebase_size = @as(u32, @intCast(rebase_size_aligned));
4014 self.dyld_info_cmd.bind_off = @as(u32, @intCast(bind_off));
4015 self.dyld_info_cmd.bind_size = @as(u32, @intCast(bind_size_aligned));
4016 self.dyld_info_cmd.lazy_bind_off = @as(u32, @intCast(lazy_bind_off));
4017 self.dyld_info_cmd.lazy_bind_size = @as(u32, @intCast(lazy_bind_size_aligned));
4018 self.dyld_info_cmd.export_off = @as(u32, @intCast(export_off));
4019 self.dyld_info_cmd.export_size = @as(u32, @intCast(export_size_aligned));
4020}
4021
4022fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: anytype) !void {
4023 if (lazy_bind.size() == 0) return;
4024
4025 const stub_helper_section_index = self.stub_helper_section_index.?;
4026 // assert(ctx.stub_helper_preamble_allocated);
4027
4028 const header = self.sections.items(.header)[stub_helper_section_index];
4029
4030 const target = self.base.comp.root_mod.resolved_target.result;
4031 const cpu_arch = target.cpu.arch;
4032 const preamble_size = stubs.stubHelperPreambleSize(cpu_arch);
4033 const stub_size = stubs.stubHelperSize(cpu_arch);
4034 const stub_offset = stubs.stubOffsetInStubHelper(cpu_arch);
4035 const base_offset = header.offset + preamble_size;
4036
4037 for (lazy_bind.offsets.items, 0..) |bind_offset, index| {
4038 const file_offset = base_offset + index * stub_size + stub_offset;
4039
4040 log.debug("writing lazy bind offset 0x{x} ({s}) in stub helper at 0x{x}", .{
4041 bind_offset,
4042 self.getSymbolName(lazy_bind.entries.items[index].target),
4043 file_offset,
4044 });
4045
4046 try self.base.file.?.pwriteAll(mem.asBytes(&bind_offset), file_offset);
4047 }
4048}
4049
4050const asc_u64 = std.sort.asc(u64);
4051
4052fn addSymbolToFunctionStarts(self: *MachO, sym_loc: SymbolWithLoc, addresses: *std.ArrayList(u64)) !void {
4053 const sym = self.getSymbol(sym_loc);
4054 if (sym.n_strx == 0) return;
4055 if (sym.n_desc == N_DEAD) return;
4056 if (sym.n_desc == N_BOUNDARY) return;
4057 if (self.symbolIsTemp(sym_loc)) return;
4058 try addresses.append(sym.n_value);
4059}
4060
4061fn writeFunctionStarts(self: *MachO) !void {
4062 const gpa = self.base.comp.gpa;
4063 const seg = self.segments.items[self.header_segment_cmd_index.?];
4064
4065 // We need to sort by address first
4066 var addresses = std.ArrayList(u64).init(gpa);
4067 defer addresses.deinit();
4068
4069 for (self.objects.items) |object| {
4070 for (object.exec_atoms.items) |atom_index| {
4071 const atom = self.getAtom(atom_index);
4072 const sym_loc = atom.getSymbolWithLoc();
4073 try self.addSymbolToFunctionStarts(sym_loc, &addresses);
4074
4075 var it = Atom.getInnerSymbolsIterator(self, atom_index);
4076 while (it.next()) |inner_sym_loc| {
4077 try self.addSymbolToFunctionStarts(inner_sym_loc, &addresses);
4078 }
4079 }
4080 }
4081
4082 mem.sort(u64, addresses.items, {}, asc_u64);
4083
4084 var offsets = std.ArrayList(u32).init(gpa);
4085 defer offsets.deinit();
4086 try offsets.ensureTotalCapacityPrecise(addresses.items.len);
4087
4088 var last_off: u32 = 0;
4089 for (addresses.items) |addr| {
4090 const offset = @as(u32, @intCast(addr - seg.vmaddr));
4091 const diff = offset - last_off;
4092
4093 if (diff == 0) continue;
4094
4095 offsets.appendAssumeCapacity(diff);
4096 last_off = offset;
4097 }
4098
4099 var buffer = std.ArrayList(u8).init(gpa);
4100 defer buffer.deinit();
4101
4102 const max_size = @as(usize, @intCast(offsets.items.len * @sizeOf(u64)));
4103 try buffer.ensureTotalCapacity(max_size);
4104
4105 for (offsets.items) |offset| {
4106 try std.leb.writeULEB128(buffer.writer(), offset);
4107 }
4108
4109 const link_seg = self.getLinkeditSegmentPtr();
4110 const offset = link_seg.fileoff + link_seg.filesize;
4111 assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
4112 const needed_size = buffer.items.len;
4113 const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64));
4114 const padding = math.cast(usize, needed_size_aligned - needed_size) orelse return error.Overflow;
4115 if (padding > 0) {
4116 try buffer.ensureUnusedCapacity(padding);
4117 buffer.appendNTimesAssumeCapacity(0, padding);
4118 }
4119 link_seg.filesize = offset + needed_size_aligned - link_seg.fileoff;
4120
4121 log.debug("writing function starts info from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned });
4122
4123 try self.base.file.?.pwriteAll(buffer.items, offset);
4124
4125 self.function_starts_cmd.dataoff = @as(u32, @intCast(offset));
4126 self.function_starts_cmd.datasize = @as(u32, @intCast(needed_size_aligned));
4127}
4128
4129fn filterDataInCode(
4130 dices: []const macho.data_in_code_entry,
4131 start_addr: u64,
4132 end_addr: u64,
4133) []const macho.data_in_code_entry {
4134 const Predicate = struct {
4135 addr: u64,
4136
4137 pub fn predicate(self: @This(), dice: macho.data_in_code_entry) bool {
4138 return dice.offset >= self.addr;
4139 }
4140 };
4141
4142 const start = MachO.lsearch(macho.data_in_code_entry, dices, Predicate{ .addr = start_addr });
4143 const end = MachO.lsearch(macho.data_in_code_entry, dices[start..], Predicate{ .addr = end_addr }) + start;
4144
4145 return dices[start..end];
4146}
4147
4148pub fn writeDataInCode(self: *MachO) !void {
4149 const gpa = self.base.comp.gpa;
4150 var out_dice = std.ArrayList(macho.data_in_code_entry).init(gpa);
4151 defer out_dice.deinit();
4152
4153 const text_sect_id = self.text_section_index orelse return;
4154 const text_sect_header = self.sections.items(.header)[text_sect_id];
4155
4156 for (self.objects.items) |object| {
4157 if (!object.hasDataInCode()) continue;
4158 const dice = object.data_in_code.items;
4159 try out_dice.ensureUnusedCapacity(dice.len);
4160
4161 for (object.exec_atoms.items) |atom_index| {
4162 const atom = self.getAtom(atom_index);
4163 const sym = self.getSymbol(atom.getSymbolWithLoc());
4164 if (sym.n_desc == N_DEAD) continue;
4165 if (sym.n_desc == N_BOUNDARY) return;
4166
4167 const source_addr = if (object.getSourceSymbol(atom.sym_index)) |source_sym|
4168 source_sym.n_value
4169 else blk: {
4170 const nbase = @as(u32, @intCast(object.in_symtab.?.len));
4171 const source_sect_id = @as(u8, @intCast(atom.sym_index - nbase));
4172 break :blk object.getSourceSection(source_sect_id).addr;
4173 };
4174 const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size);
4175 const base = math.cast(u32, sym.n_value - text_sect_header.addr + text_sect_header.offset) orelse
4176 return error.Overflow;
4177
4178 for (filtered_dice) |single| {
4179 const offset = math.cast(u32, single.offset - source_addr + base) orelse
4180 return error.Overflow;
4181 out_dice.appendAssumeCapacity(.{
4182 .offset = offset,
4183 .length = single.length,
4184 .kind = single.kind,
4185 });
4186 }
4187 }
4188 }
4189
4190 const seg = self.getLinkeditSegmentPtr();
4191 const offset = seg.fileoff + seg.filesize;
4192 assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
4193 const needed_size = out_dice.items.len * @sizeOf(macho.data_in_code_entry);
4194 const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64));
4195 seg.filesize = offset + needed_size_aligned - seg.fileoff;
4196
4197 const buffer = try gpa.alloc(u8, math.cast(usize, needed_size_aligned) orelse return error.Overflow);
4198 defer gpa.free(buffer);
4199 {
4200 const src = mem.sliceAsBytes(out_dice.items);
4201 @memcpy(buffer[0..src.len], src);
4202 @memset(buffer[src.len..], 0);
4203 }
4204
4205 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned });
4206
4207 try self.base.file.?.pwriteAll(buffer, offset);
4208
4209 self.data_in_code_cmd.dataoff = @as(u32, @intCast(offset));
4210 self.data_in_code_cmd.datasize = @as(u32, @intCast(needed_size_aligned));
4211}
4212
4213fn writeSymtabs(self: *MachO) !void {
4214 var ctx = try self.writeSymtab();
4215 defer ctx.imports_table.deinit();
4216 try self.writeDysymtab(ctx);
4217 try self.writeStrtab();
4218}
4219
4220fn addLocalToSymtab(self: *MachO, sym_loc: SymbolWithLoc, locals: *std.ArrayList(macho.nlist_64)) !void {
4221 const sym = self.getSymbol(sym_loc);
4222 if (sym.n_strx == 0) return; // no name, skip
4223 if (sym.n_desc == N_DEAD) return; // garbage-collected, skip
4224 if (sym.n_desc == N_BOUNDARY) return; // boundary symbol, skip
4225 if (sym.ext()) return; // an export lands in its own symtab section, skip
4226 if (self.symbolIsTemp(sym_loc)) return; // local temp symbol, skip
4227 const gpa = self.base.comp.gpa;
4228 var out_sym = sym;
4229 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(sym_loc));
4230 try locals.append(out_sym);
4231}
4232
4233fn writeSymtab(self: *MachO) !SymtabCtx {
4234 const comp = self.base.comp;
4235 const gpa = comp.gpa;
4236
4237 var locals = std.ArrayList(macho.nlist_64).init(gpa);
4238 defer locals.deinit();
4239
4240 for (0..self.locals.items.len) |sym_id| {
4241 try self.addLocalToSymtab(.{ .sym_index = @intCast(sym_id) }, &locals);
4242 }
4243
4244 for (self.objects.items) |object| {
4245 for (object.atoms.items) |atom_index| {
4246 const atom = self.getAtom(atom_index);
4247 const sym_loc = atom.getSymbolWithLoc();
4248 try self.addLocalToSymtab(sym_loc, &locals);
4249
4250 var it = Atom.getInnerSymbolsIterator(self, atom_index);
4251 while (it.next()) |inner_sym_loc| {
4252 try self.addLocalToSymtab(inner_sym_loc, &locals);
4253 }
4254 }
4255 }
4256
4257 var exports = std.ArrayList(macho.nlist_64).init(gpa);
4258 defer exports.deinit();
4259
4260 for (self.globals.items) |global| {
4261 const sym = self.getSymbol(global);
4262 if (sym.undf()) continue; // import, skip
4263 if (sym.n_desc == N_DEAD) continue;
4264 if (sym.n_desc == N_BOUNDARY) continue;
4265 var out_sym = sym;
4266 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
4267 try exports.append(out_sym);
4268 }
4269
4270 var imports = std.ArrayList(macho.nlist_64).init(gpa);
4271 defer imports.deinit();
4272
4273 var imports_table = std.AutoHashMap(SymbolWithLoc, u32).init(gpa);
4274
4275 for (self.globals.items) |global| {
4276 const sym = self.getSymbol(global);
4277 if (sym.n_strx == 0) continue; // no name, skip
4278 if (!sym.undf()) continue; // not an import, skip
4279 if (sym.n_desc == N_DEAD) continue;
4280 if (sym.n_desc == N_BOUNDARY) continue;
4281 const new_index = @as(u32, @intCast(imports.items.len));
4282 var out_sym = sym;
4283 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
4284 try imports.append(out_sym);
4285 try imports_table.putNoClobber(global, new_index);
4286 }
4287
4288 // We generate stabs last in order to ensure that the strtab always has debug info
4289 // strings trailing
4290 if (comp.config.debug_format != .strip) {
4291 for (self.objects.items) |object| {
4292 assert(self.d_sym == null); // TODO
4293 try self.generateSymbolStabs(object, &locals);
4294 }
4295 }
4296
4297 const nlocals = @as(u32, @intCast(locals.items.len));
4298 const nexports = @as(u32, @intCast(exports.items.len));
4299 const nimports = @as(u32, @intCast(imports.items.len));
4300 const nsyms = nlocals + nexports + nimports;
4301
4302 const seg = self.getLinkeditSegmentPtr();
4303 const offset = seg.fileoff + seg.filesize;
4304 assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
4305 const needed_size = nsyms * @sizeOf(macho.nlist_64);
4306 seg.filesize = offset + needed_size - seg.fileoff;
4307 assert(mem.isAlignedGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64)));
4308
4309 var buffer = std.ArrayList(u8).init(gpa);
4310 defer buffer.deinit();
4311 try buffer.ensureTotalCapacityPrecise(needed_size);
4312 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(locals.items));
4313 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(exports.items));
4314 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(imports.items));
4315
4316 log.debug("writing symtab from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
4317 try self.base.file.?.pwriteAll(buffer.items, offset);
4318
4319 self.symtab_cmd.symoff = @as(u32, @intCast(offset));
4320 self.symtab_cmd.nsyms = nsyms;
4321
4322 return SymtabCtx{
4323 .nlocalsym = nlocals,
4324 .nextdefsym = nexports,
4325 .nundefsym = nimports,
4326 .imports_table = imports_table,
4327 };
4328}490}
4329491
4330// TODO this function currently skips generating symbol stabs in case errors are encountered in DWARF data.492fn growAtom(self: *MachO, atom_index: Atom.Index, new_atom_size: u64, alignment: Alignment) !u64 {
4331// I think we should actually report those errors to the user and let them decide if they want to strip debug info493 _ = self;
4332// in that case or not.494 _ = atom_index;
4333fn generateSymbolStabs(495 _ = new_atom_size;
4334 self: *MachO,496 _ = alignment;
4335 object: Object,497 @panic("TODO growAtom");
4336 locals: *std.ArrayList(macho.nlist_64),
4337) !void {
4338 log.debug("generating stabs for '{s}'", .{object.name});
4339
4340 const gpa = self.base.comp.gpa;
4341 var debug_info = object.parseDwarfInfo();
4342
4343 var lookup = DwarfInfo.AbbrevLookupTable.init(gpa);
4344 defer lookup.deinit();
4345 try lookup.ensureUnusedCapacity(std.math.maxInt(u8));
4346
4347 // We assume there is only one CU.
4348 var cu_it = debug_info.getCompileUnitIterator();
4349 const compile_unit = while (try cu_it.next()) |cu| {
4350 const offset = math.cast(usize, cu.cuh.debug_abbrev_offset) orelse return error.Overflow;
4351 try debug_info.genAbbrevLookupByKind(offset, &lookup);
4352 break cu;
4353 } else {
4354 log.debug("no compile unit found in debug info in {s}; skipping", .{object.name});
4355 return;
4356 };
4357
4358 var abbrev_it = compile_unit.getAbbrevEntryIterator(debug_info);
4359 const maybe_cu_entry: ?DwarfInfo.AbbrevEntry = blk: {
4360 while (abbrev_it.next(lookup) catch break :blk null) |entry| switch (entry.tag) {
4361 dwarf.TAG.compile_unit => break :blk entry,
4362 else => continue,
4363 } else break :blk null;
4364 };
4365
4366 const cu_entry = maybe_cu_entry orelse {
4367 log.debug("missing DWARF_TAG_compile_unit tag in {s}; skipping", .{object.name});
4368 return;
4369 };
4370
4371 var maybe_tu_name: ?[]const u8 = null;
4372 var maybe_tu_comp_dir: ?[]const u8 = null;
4373 var attr_it = cu_entry.getAttributeIterator(debug_info, compile_unit.cuh);
4374
4375 blk: {
4376 while (attr_it.next() catch break :blk) |attr| switch (attr.name) {
4377 dwarf.AT.comp_dir => maybe_tu_comp_dir = attr.getString(debug_info, compile_unit.cuh) orelse continue,
4378 dwarf.AT.name => maybe_tu_name = attr.getString(debug_info, compile_unit.cuh) orelse continue,
4379 else => continue,
4380 };
4381 }
4382
4383 if (maybe_tu_name == null or maybe_tu_comp_dir == null) {
4384 log.debug("missing DWARF_AT_comp_dir and DWARF_AT_name attributes {s}; skipping", .{object.name});
4385 return;
4386 }
4387
4388 const tu_name = maybe_tu_name.?;
4389 const tu_comp_dir = maybe_tu_comp_dir.?;
4390
4391 // Open scope
4392 try locals.ensureUnusedCapacity(3);
4393 locals.appendAssumeCapacity(.{
4394 .n_strx = try self.strtab.insert(gpa, tu_comp_dir),
4395 .n_type = macho.N_SO,
4396 .n_sect = 0,
4397 .n_desc = 0,
4398 .n_value = 0,
4399 });
4400 locals.appendAssumeCapacity(.{
4401 .n_strx = try self.strtab.insert(gpa, tu_name),
4402 .n_type = macho.N_SO,
4403 .n_sect = 0,
4404 .n_desc = 0,
4405 .n_value = 0,
4406 });
4407 locals.appendAssumeCapacity(.{
4408 .n_strx = try self.strtab.insert(gpa, object.name),
4409 .n_type = macho.N_OSO,
4410 .n_sect = 0,
4411 .n_desc = 1,
4412 .n_value = object.mtime,
4413 });
4414
4415 var stabs_buf: [4]macho.nlist_64 = undefined;
4416
4417 var name_lookup: ?DwarfInfo.SubprogramLookupByName = if (object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS == 0) blk: {
4418 var name_lookup = DwarfInfo.SubprogramLookupByName.init(gpa);
4419 errdefer name_lookup.deinit();
4420 try name_lookup.ensureUnusedCapacity(@as(u32, @intCast(object.atoms.items.len)));
4421 debug_info.genSubprogramLookupByName(compile_unit, lookup, &name_lookup) catch |err| switch (err) {
4422 error.UnhandledDwFormValue => {}, // TODO I don't like the fact we constantly re-iterate and hit this; we should validate once a priori
4423 else => |e| return e,
4424 };
4425 break :blk name_lookup;
4426 } else null;
4427 defer if (name_lookup) |*nl| nl.deinit();
4428
4429 for (object.atoms.items) |atom_index| {
4430 const atom = self.getAtom(atom_index);
4431 const stabs = try self.generateSymbolStabsForSymbol(
4432 atom_index,
4433 atom.getSymbolWithLoc(),
4434 name_lookup,
4435 &stabs_buf,
4436 );
4437 try locals.appendSlice(stabs);
4438
4439 var it = Atom.getInnerSymbolsIterator(self, atom_index);
4440 while (it.next()) |sym_loc| {
4441 const contained_stabs = try self.generateSymbolStabsForSymbol(
4442 atom_index,
4443 sym_loc,
4444 name_lookup,
4445 &stabs_buf,
4446 );
4447 try locals.appendSlice(contained_stabs);
4448 }
4449 }
4450
4451 // Close scope
4452 try locals.append(.{
4453 .n_strx = 0,
4454 .n_type = macho.N_SO,
4455 .n_sect = 0,
4456 .n_desc = 0,
4457 .n_value = 0,
4458 });
4459}498}
4460499
4461fn generateSymbolStabsForSymbol(500pub fn updateFunc(self: *MachO, mod: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {
4462 self: *MachO,501 if (build_options.skip_non_native and builtin.object_format != .macho) {
4463 atom_index: Atom.Index,502 @panic("Attempted to compile for object format that was disabled by build configuration");
4464 sym_loc: SymbolWithLoc,
4465 lookup: ?DwarfInfo.SubprogramLookupByName,
4466 buf: *[4]macho.nlist_64,
4467) ![]const macho.nlist_64 {
4468 const gpa = self.base.comp.gpa;
4469 const object = self.objects.items[sym_loc.getFile().?];
4470 const sym = self.getSymbol(sym_loc);
4471 const sym_name = self.getSymbolName(sym_loc);
4472 const header = self.sections.items(.header)[sym.n_sect - 1];
4473
4474 if (sym.n_strx == 0) return buf[0..0];
4475 if (self.symbolIsTemp(sym_loc)) return buf[0..0];
4476
4477 if (!header.isCode()) {
4478 // Since we are not dealing with machine code, it's either a global or a static depending
4479 // on the linkage scope.
4480 if (sym.sect() and sym.ext()) {
4481 // Global gets an N_GSYM stab type.
4482 buf[0] = .{
4483 .n_strx = try self.strtab.insert(gpa, sym_name),
4484 .n_type = macho.N_GSYM,
4485 .n_sect = sym.n_sect,
4486 .n_desc = 0,
4487 .n_value = 0,
4488 };
4489 } else {
4490 // Local static gets an N_STSYM stab type.
4491 buf[0] = .{
4492 .n_strx = try self.strtab.insert(gpa, sym_name),
4493 .n_type = macho.N_STSYM,
4494 .n_sect = sym.n_sect,
4495 .n_desc = 0,
4496 .n_value = sym.n_value,
4497 };
4498 }
4499 return buf[0..1];
4500 }503 }
504 if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(mod, func_index, air, liveness);
4501505
4502 const size: u64 = size: {506 @panic("TODO updateFunc");
4503 if (object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0) {
4504 break :size self.getAtom(atom_index).size;
4505 }
4506
4507 // Since we don't have subsections to work with, we need to infer the size of each function
4508 // the slow way by scanning the debug info for matching symbol names and extracting
4509 // the symbol's DWARF_AT_low_pc and DWARF_AT_high_pc values.
4510 const source_sym = object.getSourceSymbol(sym_loc.sym_index) orelse return buf[0..0];
4511 const subprogram = lookup.?.get(sym_name[1..]) orelse return buf[0..0];
4512
4513 if (subprogram.addr <= source_sym.n_value and source_sym.n_value < subprogram.addr + subprogram.size) {
4514 break :size subprogram.size;
4515 } else {
4516 log.debug("no stab found for {s}", .{sym_name});
4517 return buf[0..0];
4518 }
4519 };
4520
4521 buf[0] = .{
4522 .n_strx = 0,
4523 .n_type = macho.N_BNSYM,
4524 .n_sect = sym.n_sect,
4525 .n_desc = 0,
4526 .n_value = sym.n_value,
4527 };
4528 buf[1] = .{
4529 .n_strx = try self.strtab.insert(gpa, sym_name),
4530 .n_type = macho.N_FUN,
4531 .n_sect = sym.n_sect,
4532 .n_desc = 0,
4533 .n_value = sym.n_value,
4534 };
4535 buf[2] = .{
4536 .n_strx = 0,
4537 .n_type = macho.N_FUN,
4538 .n_sect = 0,
4539 .n_desc = 0,
4540 .n_value = size,
4541 };
4542 buf[3] = .{
4543 .n_strx = 0,
4544 .n_type = macho.N_ENSYM,
4545 .n_sect = sym.n_sect,
4546 .n_desc = 0,
4547 .n_value = size,
4548 };
4549
4550 return buf;
4551}507}
4552508
4553pub fn writeStrtab(self: *MachO) !void {509pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: InternPool.DeclIndex) !u32 {
4554 const gpa = self.base.comp.gpa;510 _ = self;
4555 const seg = self.getLinkeditSegmentPtr();511 _ = typed_value;
4556 const offset = seg.fileoff + seg.filesize;512 _ = decl_index;
4557 assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
4558 const needed_size = self.strtab.buffer.items.len;
4559 const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64));
4560 seg.filesize = offset + needed_size_aligned - seg.fileoff;
4561
4562 log.debug("writing string table from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned });
4563
4564 const buffer = try gpa.alloc(u8, math.cast(usize, needed_size_aligned) orelse return error.Overflow);
4565 defer gpa.free(buffer);
4566 @memcpy(buffer[0..self.strtab.buffer.items.len], self.strtab.buffer.items);
4567 @memset(buffer[self.strtab.buffer.items.len..], 0);
4568
4569 try self.base.file.?.pwriteAll(buffer, offset);
4570513
4571 self.symtab_cmd.stroff = @as(u32, @intCast(offset));514 @panic("TODO lowerUnnamedConst");
4572 self.symtab_cmd.strsize = @as(u32, @intCast(needed_size_aligned));
4573}515}
4574516
4575const SymtabCtx = struct {517const LowerConstResult = union(enum) {
4576 nlocalsym: u32,518 ok: Atom.Index,
4577 nextdefsym: u32,519 fail: *Module.ErrorMsg,
4578 nundefsym: u32,
4579 imports_table: std.AutoHashMap(SymbolWithLoc, u32),
4580};520};
4581521
4582pub fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {522fn lowerConst(
4583 const gpa = self.base.comp.gpa;523 self: *MachO,
4584 const nstubs = @as(u32, @intCast(self.stub_table.lookup.count()));524 name: []const u8,
4585 const ngot_entries = @as(u32, @intCast(self.got_table.lookup.count()));525 tv: TypedValue,
4586 const nindirectsyms = nstubs * 2 + ngot_entries;526 required_alignment: InternPool.Alignment,
4587 const iextdefsym = ctx.nlocalsym;527 sect_id: u8,
4588 const iundefsym = iextdefsym + ctx.nextdefsym;528 src_loc: Module.SrcLoc,
4589529) !LowerConstResult {
4590 const seg = self.getLinkeditSegmentPtr();530 _ = self;
4591 const offset = seg.fileoff + seg.filesize;531 _ = name;
4592 assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));532 _ = tv;
4593 const needed_size = nindirectsyms * @sizeOf(u32);533 _ = required_alignment;
4594 const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64));534 _ = sect_id;
4595 seg.filesize = offset + needed_size_aligned - seg.fileoff;535 _ = src_loc;
4596
4597 log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned });
4598
4599 var buf = std.ArrayList(u8).init(gpa);
4600 defer buf.deinit();
4601 try buf.ensureTotalCapacity(math.cast(usize, needed_size_aligned) orelse return error.Overflow);
4602 const writer = buf.writer();
4603
4604 if (self.stubs_section_index) |sect_id| {
4605 const stubs_header = &self.sections.items(.header)[sect_id];
4606 stubs_header.reserved1 = 0;
4607 for (self.stub_table.entries.items) |entry| {
4608 if (!self.stub_table.lookup.contains(entry)) continue;
4609 const target_sym = self.getSymbol(entry);
4610 assert(target_sym.undf());
4611 try writer.writeInt(u32, iundefsym + ctx.imports_table.get(entry).?, .little);
4612 }
4613 }
4614
4615 if (self.got_section_index) |sect_id| {
4616 const got = &self.sections.items(.header)[sect_id];
4617 got.reserved1 = nstubs;
4618 for (self.got_table.entries.items) |entry| {
4619 if (!self.got_table.lookup.contains(entry)) continue;
4620 const target_sym = self.getSymbol(entry);
4621 if (target_sym.undf()) {
4622 try writer.writeInt(u32, iundefsym + ctx.imports_table.get(entry).?, .little);
4623 } else {
4624 try writer.writeInt(u32, macho.INDIRECT_SYMBOL_LOCAL, .little);
4625 }
4626 }
4627 }
4628
4629 if (self.la_symbol_ptr_section_index) |sect_id| {
4630 const la_symbol_ptr = &self.sections.items(.header)[sect_id];
4631 la_symbol_ptr.reserved1 = nstubs + ngot_entries;
4632 for (self.stub_table.entries.items) |entry| {
4633 if (!self.stub_table.lookup.contains(entry)) continue;
4634 const target_sym = self.getSymbol(entry);
4635 assert(target_sym.undf());
4636 try writer.writeInt(u32, iundefsym + ctx.imports_table.get(entry).?, .little);
4637 }
4638 }
4639
4640 const padding = math.cast(usize, needed_size_aligned - needed_size) orelse return error.Overflow;
4641 if (padding > 0) {
4642 buf.appendNTimesAssumeCapacity(0, padding);
4643 }
4644
4645 assert(buf.items.len == needed_size_aligned);
4646 try self.base.file.?.pwriteAll(buf.items, offset);
4647
4648 self.dysymtab_cmd.nlocalsym = ctx.nlocalsym;
4649 self.dysymtab_cmd.iextdefsym = iextdefsym;
4650 self.dysymtab_cmd.nextdefsym = ctx.nextdefsym;
4651 self.dysymtab_cmd.iundefsym = iundefsym;
4652 self.dysymtab_cmd.nundefsym = ctx.nundefsym;
4653 self.dysymtab_cmd.indirectsymoff = @as(u32, @intCast(offset));
4654 self.dysymtab_cmd.nindirectsyms = nindirectsyms;
4655}
4656
4657pub fn writeUuid(self: *MachO, comp: *const Compilation, uuid_cmd_offset: u32, has_codesig: bool) !void {
4658 const file_size = if (!has_codesig) blk: {
4659 const seg = self.getLinkeditSegmentPtr();
4660 break :blk seg.fileoff + seg.filesize;
4661 } else self.codesig_cmd.dataoff;
4662 try calcUuid(comp, self.base.file.?, file_size, &self.uuid_cmd.uuid);
4663 const offset = uuid_cmd_offset + @sizeOf(macho.load_command);
4664 try self.base.file.?.pwriteAll(&self.uuid_cmd.uuid, offset);
4665}
4666
4667pub fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {
4668 const target = self.base.comp.root_mod.resolved_target.result;
4669 const seg = self.getLinkeditSegmentPtr();
4670 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file
4671 // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271
4672 const offset = mem.alignForward(u64, seg.fileoff + seg.filesize, 16);
4673 const needed_size = code_sig.estimateSize(offset);
4674 seg.filesize = offset + needed_size - seg.fileoff;
4675 seg.vmsize = mem.alignForward(u64, seg.filesize, getPageSize(target.cpu.arch));
4676 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
4677 // Pad out the space. We need to do this to calculate valid hashes for everything in the file
4678 // except for code signature data.
4679 try self.base.file.?.pwriteAll(&[_]u8{0}, offset + needed_size - 1);
4680
4681 self.codesig_cmd.dataoff = @as(u32, @intCast(offset));
4682 self.codesig_cmd.datasize = @as(u32, @intCast(needed_size));
4683}
4684
4685pub fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *CodeSignature) !void {
4686 const output_mode = self.base.comp.config.output_mode;
4687 const seg_id = self.header_segment_cmd_index.?;
4688 const seg = self.segments.items[seg_id];
4689 const offset = self.codesig_cmd.dataoff;
4690
4691 const gpa = self.base.comp.gpa;
4692 var buffer = std.ArrayList(u8).init(gpa);
4693 defer buffer.deinit();
4694 try buffer.ensureTotalCapacityPrecise(code_sig.size());
4695 try code_sig.writeAdhocSignature(comp, .{
4696 .file = self.base.file.?,
4697 .exec_seg_base = seg.fileoff,
4698 .exec_seg_limit = seg.filesize,
4699 .file_size = offset,
4700 .output_mode = output_mode,
4701 }, buffer.writer());
4702 assert(buffer.items.len == code_sig.size());
4703
4704 log.debug("writing code signature from 0x{x} to 0x{x}", .{
4705 offset,
4706 offset + buffer.items.len,
4707 });
4708
4709 try self.base.file.?.pwriteAll(buffer.items, offset);
4710}
4711
4712/// Writes Mach-O file header.
4713pub fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {
4714 const output_mode = self.base.comp.config.output_mode;
4715
4716 var header: macho.mach_header_64 = .{};
4717 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;
4718
4719 const target = self.base.comp.root_mod.resolved_target.result;
4720 switch (target.cpu.arch) {
4721 .aarch64 => {
4722 header.cputype = macho.CPU_TYPE_ARM64;
4723 header.cpusubtype = macho.CPU_SUBTYPE_ARM_ALL;
4724 },
4725 .x86_64 => {
4726 header.cputype = macho.CPU_TYPE_X86_64;
4727 header.cpusubtype = macho.CPU_SUBTYPE_X86_64_ALL;
4728 },
4729 else => unreachable,
4730 }
4731
4732 switch (output_mode) {
4733 .Exe => {
4734 header.filetype = macho.MH_EXECUTE;
4735 },
4736 .Lib => {
4737 // By this point, it can only be a dylib.
4738 header.filetype = macho.MH_DYLIB;
4739 header.flags |= macho.MH_NO_REEXPORTED_DYLIBS;
4740 },
4741 else => unreachable,
4742 }
4743
4744 if (self.thread_vars_section_index) |sect_id| {
4745 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
4746 if (self.sections.items(.header)[sect_id].size > 0) {
4747 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
4748 }
4749 }
4750
4751 header.ncmds = ncmds;
4752 header.sizeofcmds = sizeofcmds;
4753
4754 log.debug("writing Mach-O header {}", .{header});
4755
4756 try self.base.file.?.pwriteAll(mem.asBytes(&header), 0);
4757}
4758
4759pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
4760 return actual_size +| (actual_size / ideal_factor);
4761}
4762
4763fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
4764 // TODO: header and load commands have to be part of the __TEXT segment
4765 const header_size = self.segments.items[self.header_segment_cmd_index.?].filesize;
4766 if (start < header_size)
4767 return header_size;
4768
4769 const end = start + padToIdeal(size);
4770
4771 for (self.sections.items(.header)) |header| {
4772 const tight_size = header.size;
4773 const increased_size = padToIdeal(tight_size);
4774 const test_end = header.offset + increased_size;
4775 if (end > header.offset and start < test_end) {
4776 return test_end;
4777 }
4778 }
4779
4780 return null;
4781}
4782
4783fn allocatedSize(self: *MachO, start: u64) u64 {
4784 if (start == 0)
4785 return 0;
4786 var min_pos: u64 = std.math.maxInt(u64);
4787 for (self.sections.items(.header)) |header| {
4788 if (header.offset <= start) continue;
4789 if (header.offset < min_pos) min_pos = header.offset;
4790 }
4791 return min_pos - start;
4792}
4793
4794fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 {
4795 var start: u64 = 0;
4796 while (self.detectAllocCollision(start, object_size)) |item_end| {
4797 start = mem.alignForward(u64, item_end, min_alignment);
4798 }
4799 return start;
4800}
4801
4802pub fn allocatedVirtualSize(self: *MachO, start: u64) u64 {
4803 if (start == 0)
4804 return 0;
4805 var min_pos: u64 = std.math.maxInt(u64);
4806 for (self.sections.items(.segment_index)) |seg_id| {
4807 const segment = self.segments.items[seg_id];
4808 if (segment.vmaddr <= start) continue;
4809 if (segment.vmaddr < min_pos) min_pos = segment.vmaddr;
4810 }
4811 return min_pos - start;
4812}
4813
4814pub fn ptraceAttach(self: *MachO, pid: std.os.pid_t) !void {
4815 if (!is_hot_update_compatible) return;
4816
4817 const mach_task = try std.os.darwin.machTaskForPid(pid);
4818 log.debug("Mach task for pid {d}: {any}", .{ pid, mach_task });
4819 self.hot_state.mach_task = mach_task;
4820
4821 // TODO start exception handler in another thread
4822
4823 // TODO enable ones we register for exceptions
4824 // try std.os.ptrace(std.os.darwin.PT.ATTACHEXC, pid, 0, 0);
4825}
4826
4827pub fn ptraceDetach(self: *MachO, pid: std.os.pid_t) !void {
4828 if (!is_hot_update_compatible) return;
4829
4830 _ = pid;
4831
4832 // TODO stop exception handler
4833
4834 // TODO see comment in ptraceAttach
4835 // try std.os.ptrace(std.os.darwin.PT.DETACH, pid, 0, 0);
4836
4837 self.hot_state.mach_task = null;
4838}
4839
4840pub fn addUndefined(self: *MachO, name: []const u8, flags: RelocFlags) !u32 {
4841 const gpa = self.base.comp.gpa;
4842
4843 const gop = try self.getOrPutGlobalPtr(name);
4844 const global_index = self.getGlobalIndex(name).?;
4845
4846 if (gop.found_existing) {
4847 try self.updateRelocActions(global_index, flags);
4848 return global_index;
4849 }
4850
4851 const sym_index = try self.allocateSymbol();
4852 const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
4853 gop.value_ptr.* = sym_loc;
4854
4855 const sym = self.getSymbolPtr(sym_loc);
4856 sym.n_strx = try self.strtab.insert(gpa, name);
4857 sym.n_type = macho.N_EXT | macho.N_UNDF;
4858
4859 try self.unresolved.putNoClobber(gpa, global_index, {});
4860 try self.updateRelocActions(global_index, flags);
4861
4862 return global_index;
4863}
4864
4865fn updateRelocActions(self: *MachO, global_index: u32, flags: RelocFlags) !void {
4866 const gpa = self.base.comp.gpa;
4867 const act_gop = try self.actions.getOrPut(gpa, global_index);
4868 if (!act_gop.found_existing) {
4869 act_gop.value_ptr.* = .{};
4870 }
4871 act_gop.value_ptr.add_got = act_gop.value_ptr.add_got or flags.add_got;
4872 act_gop.value_ptr.add_stub = act_gop.value_ptr.add_stub or flags.add_stub;
4873}
4874
4875pub fn makeStaticString(bytes: []const u8) [16]u8 {
4876 var buf = [_]u8{0} ** 16;
4877 @memcpy(buf[0..bytes.len], bytes);
4878 return buf;
4879}
4880
4881pub fn getSegmentByName(self: MachO, segname: []const u8) ?u8 {
4882 for (self.segments.items, 0..) |seg, i| {
4883 if (mem.eql(u8, segname, seg.segName())) return @as(u8, @intCast(i));
4884 } else return null;
4885}
4886
4887pub fn getSegment(self: MachO, sect_id: u8) macho.segment_command_64 {
4888 const index = self.sections.items(.segment_index)[sect_id];
4889 return self.segments.items[index];
4890}
4891
4892pub fn getSegmentPtr(self: *MachO, sect_id: u8) *macho.segment_command_64 {
4893 const index = self.sections.items(.segment_index)[sect_id];
4894 return &self.segments.items[index];
4895}
4896
4897pub fn getLinkeditSegmentPtr(self: *MachO) *macho.segment_command_64 {
4898 const index = self.linkedit_segment_cmd_index.?;
4899 return &self.segments.items[index];
4900}
4901
4902pub fn getSectionByName(self: MachO, segname: []const u8, sectname: []const u8) ?u8 {
4903 // TODO investigate caching with a hashmap
4904 for (self.sections.items(.header), 0..) |header, i| {
4905 if (mem.eql(u8, header.segName(), segname) and mem.eql(u8, header.sectName(), sectname))
4906 return @as(u8, @intCast(i));
4907 } else return null;
4908}
4909
4910pub fn getSectionIndexes(self: MachO, segment_index: u8) struct { start: u8, end: u8 } {
4911 var start: u8 = 0;
4912 const nsects = for (self.segments.items, 0..) |seg, i| {
4913 if (i == segment_index) break @as(u8, @intCast(seg.nsects));
4914 start += @as(u8, @intCast(seg.nsects));
4915 } else 0;
4916 return .{ .start = start, .end = start + nsects };
4917}
4918
4919pub fn symbolIsTemp(self: *MachO, sym_with_loc: SymbolWithLoc) bool {
4920 const sym = self.getSymbol(sym_with_loc);
4921 if (!sym.sect()) return false;
4922 if (sym.ext()) return false;
4923 const sym_name = self.getSymbolName(sym_with_loc);
4924 return mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L");
4925}
4926
4927/// Returns pointer-to-symbol described by `sym_with_loc` descriptor.
4928pub fn getSymbolPtr(self: *MachO, sym_with_loc: SymbolWithLoc) *macho.nlist_64 {
4929 if (sym_with_loc.getFile()) |file| {
4930 const object = &self.objects.items[file];
4931 return &object.symtab[sym_with_loc.sym_index];
4932 } else {
4933 return &self.locals.items[sym_with_loc.sym_index];
4934 }
4935}
4936
4937/// Returns symbol described by `sym_with_loc` descriptor.
4938pub fn getSymbol(self: *const MachO, sym_with_loc: SymbolWithLoc) macho.nlist_64 {
4939 if (sym_with_loc.getFile()) |file| {
4940 const object = &self.objects.items[file];
4941 return object.symtab[sym_with_loc.sym_index];
4942 } else {
4943 return self.locals.items[sym_with_loc.sym_index];
4944 }
4945}
4946536
4947/// Returns name of the symbol described by `sym_with_loc` descriptor.537 @panic("TODO lowerConst");
4948pub fn getSymbolName(self: *const MachO, sym_with_loc: SymbolWithLoc) []const u8 {
4949 if (sym_with_loc.getFile()) |file| {
4950 const object = self.objects.items[file];
4951 return object.getSymbolName(sym_with_loc.sym_index);
4952 } else {
4953 const sym = self.locals.items[sym_with_loc.sym_index];
4954 return self.strtab.get(sym.n_strx).?;
4955 }
4956}538}
4957539
4958const BoundarySymbolKind = enum {540pub fn updateDecl(self: *MachO, mod: *Module, decl_index: InternPool.DeclIndex) !void {
4959 start,541 if (build_options.skip_non_native and builtin.object_format != .macho) {
4960 stop,542 @panic("Attempted to compile for object format that was disabled by build configuration");
4961};
4962
4963const SectionBoundarySymbol = struct {
4964 kind: BoundarySymbolKind,
4965 segname: []const u8,
4966 sectname: []const u8,
4967};
4968
4969pub fn getSectionBoundarySymbol(self: *const MachO, sym_with_loc: SymbolWithLoc) ?SectionBoundarySymbol {
4970 const sym_name = self.getSymbolName(sym_with_loc);
4971 if (mem.startsWith(u8, sym_name, "section$")) {
4972 const trailing = sym_name["section$".len..];
4973 const kind: BoundarySymbolKind = kind: {
4974 if (mem.startsWith(u8, trailing, "start$")) break :kind .start;
4975 if (mem.startsWith(u8, trailing, "stop$")) break :kind .stop;
4976 return null;
4977 };
4978 const names = trailing[@tagName(kind).len + 1 ..];
4979 const sep_idx = mem.indexOf(u8, names, "$") orelse return null;
4980 const segname = names[0..sep_idx];
4981 const sectname = names[sep_idx + 1 ..];
4982 return .{ .kind = kind, .segname = segname, .sectname = sectname };
4983 }543 }
4984 return null;544 if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(mod, decl_index);
4985}
4986545
4987const SegmentBoundarySymbol = struct {546 const tracy = trace(@src());
4988 kind: BoundarySymbolKind,547 defer tracy.end();
4989 segname: []const u8,
4990};
4991548
4992pub fn getSegmentBoundarySymbol(self: *const MachO, sym_with_loc: SymbolWithLoc) ?SegmentBoundarySymbol {549 @panic("TODO updateDecl");
4993 const sym_name = self.getSymbolName(sym_with_loc);
4994 if (mem.startsWith(u8, sym_name, "segment$")) {
4995 const trailing = sym_name["segment$".len..];
4996 const kind: BoundarySymbolKind = kind: {
4997 if (mem.startsWith(u8, trailing, "start$")) break :kind .start;
4998 if (mem.startsWith(u8, trailing, "stop$")) break :kind .stop;
4999 return null;
5000 };
5001 const segname = trailing[@tagName(kind).len + 1 ..];
5002 return .{ .kind = kind, .segname = segname };
5003 }
5004 return null;
5005}550}
5006551
5007/// Returns pointer to the global entry for `name` if one exists.552fn updateLazySymbolAtom(
5008pub fn getGlobalPtr(self: *MachO, name: []const u8) ?*SymbolWithLoc {553 self: *MachO,
5009 const global_index = self.resolver.get(name) orelse return null;554 sym: File.LazySymbol,
5010 return &self.globals.items[global_index];555 atom_index: Atom.Index,
556 section_index: u8,
557) !void {
558 _ = self;
559 _ = sym;
560 _ = atom_index;
561 _ = section_index;
562 @panic("TODO updateLazySymbolAtom");
5011}563}
5012564
5013/// Returns the global entry for `name` if one exists.565pub fn getOrCreateAtomForLazySymbol(self: *MachO, sym: File.LazySymbol) !Atom.Index {
5014pub fn getGlobal(self: *const MachO, name: []const u8) ?SymbolWithLoc {566 _ = self;
5015 const global_index = self.resolver.get(name) orelse return null;567 _ = sym;
5016 return self.globals.items[global_index];568 @panic("TODO getOrCreateAtomForLazySymbol");
5017}569}
5018570
5019/// Returns the index of the global entry for `name` if one exists.571pub fn getOrCreateAtomForDecl(self: *MachO, decl_index: InternPool.DeclIndex) !Atom.Index {
5020pub fn getGlobalIndex(self: *const MachO, name: []const u8) ?u32 {572 _ = self;
5021 return self.resolver.get(name);573 _ = decl_index;
574 @panic("TODO getOrCreateAtomForDecl");
5022}575}
5023576
5024/// Returns global entry at `index`.577fn getDeclOutputSection(self: *MachO, decl_index: InternPool.DeclIndex) u8 {
5025pub fn getGlobalByIndex(self: *const MachO, index: u32) SymbolWithLoc {578 _ = self;
5026 assert(index < self.globals.items.len);579 _ = decl_index;
5027 return self.globals.items[index];580 @panic("TODO getDeclOutputSection");
5028}581}
5029582
5030const GetOrPutGlobalPtrResult = struct {583fn updateDeclCode(self: *MachO, decl_index: InternPool.DeclIndex, code: []u8) !u64 {
5031 found_existing: bool,584 _ = self;
5032 value_ptr: *SymbolWithLoc,585 _ = decl_index;
5033};586 _ = code;
587 @panic("TODO updateDeclCode");
588}
5034589
5035/// Used only for disambiguating local from global at relocation level.590pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl_index: InternPool.DeclIndex) !void {
5036/// TODO this must go away.591 if (self.d_sym) |*d_sym| {
5037pub const global_symbol_bit: u32 = 0x80000000;592 try d_sym.dwarf.updateDeclLineNumber(module, decl_index);
5038pub const global_symbol_mask: u32 = 0x7fffffff;
5039
5040/// Return pointer to the global entry for `name` if one exists.
5041/// Puts a new global entry for `name` if one doesn't exist, and
5042/// returns a pointer to it.
5043pub fn getOrPutGlobalPtr(self: *MachO, name: []const u8) !GetOrPutGlobalPtrResult {
5044 if (self.getGlobalPtr(name)) |ptr| {
5045 return GetOrPutGlobalPtrResult{ .found_existing = true, .value_ptr = ptr };
5046 }593 }
5047 const gpa = self.base.comp.gpa;
5048 const global_index = try self.allocateGlobal();
5049 const global_name = try gpa.dupe(u8, name);
5050 _ = try self.resolver.put(gpa, global_name, global_index);
5051 const ptr = &self.globals.items[global_index];
5052 return GetOrPutGlobalPtrResult{ .found_existing = false, .value_ptr = ptr };
5053}594}
5054595
5055pub fn getAtom(self: *MachO, atom_index: Atom.Index) Atom {596pub fn updateExports(
5056 assert(atom_index < self.atoms.items.len);597 self: *MachO,
5057 return self.atoms.items[atom_index];598 mod: *Module,
599 exported: Module.Exported,
600 exports: []const *Module.Export,
601) File.UpdateExportsError!void {
602 if (build_options.skip_non_native and builtin.object_format != .macho) {
603 @panic("Attempted to compile for object format that was disabled by build configuration");
604 }
605 if (self.llvm_object) |llvm_object|
606 return llvm_object.updateExports(mod, exported, exports);
607
608 @panic("TODO updateExports");
5058}609}
5059610
5060pub fn getAtomPtr(self: *MachO, atom_index: Atom.Index) *Atom {611pub fn deleteDeclExport(
5061 assert(atom_index < self.atoms.items.len);612 self: *MachO,
5062 return &self.atoms.items[atom_index];613 decl_index: InternPool.DeclIndex,
614 name: InternPool.NullTerminatedString,
615) Allocator.Error!void {
616 if (self.llvm_object) |_| return;
617 _ = decl_index;
618 _ = name;
619 @panic("TODO deleteDeclExport");
5063}620}
5064621
5065/// Returns atom if there is an atom referenced by the symbol described by `sym_with_loc` descriptor.622fn freeUnnamedConsts(self: *MachO, decl_index: InternPool.DeclIndex) void {
5066/// Returns null on failure.623 _ = self;
5067pub fn getAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?Atom.Index {624 _ = decl_index;
5068 assert(sym_with_loc.getFile() == null);625 @panic("TODO freeUnnamedConst");
5069 return self.atom_by_index_table.get(sym_with_loc.sym_index);
5070}626}
5071627
5072pub fn getGotEntryAddress(self: *MachO, sym_with_loc: SymbolWithLoc) ?u64 {628pub fn freeDecl(self: *MachO, decl_index: InternPool.DeclIndex) void {
5073 const index = self.got_table.lookup.get(sym_with_loc) orelse return null;629 if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index);
5074 const header = self.sections.items(.header)[self.got_section_index.?];630 @panic("TODO freeDecl");
5075 return header.addr + @sizeOf(u64) * index;
5076}631}
5077632
5078pub fn getTlvPtrEntryAddress(self: *MachO, sym_with_loc: SymbolWithLoc) ?u64 {633pub fn getDeclVAddr(self: *MachO, decl_index: InternPool.DeclIndex, reloc_info: File.RelocInfo) !u64 {
5079 const index = self.tlv_ptr_table.lookup.get(sym_with_loc) orelse return null;634 assert(self.llvm_object == null);
5080 const header = self.sections.items(.header)[self.tlv_ptr_section_index.?];635 _ = decl_index;
5081 return header.addr + @sizeOf(u64) * index;636 _ = reloc_info;
637 @panic("TODO getDeclVAddr");
5082}638}
5083639
5084pub fn getStubsEntryAddress(self: *MachO, sym_with_loc: SymbolWithLoc) ?u64 {640pub fn lowerAnonDecl(
5085 const target = self.base.comp.root_mod.resolved_target.result;641 self: *MachO,
5086 const index = self.stub_table.lookup.get(sym_with_loc) orelse return null;642 decl_val: InternPool.Index,
5087 const header = self.sections.items(.header)[self.stubs_section_index.?];643 explicit_alignment: InternPool.Alignment,
5088 return header.addr + stubs.stubSize(target.cpu.arch) * index;644 src_loc: Module.SrcLoc,
645) !codegen.Result {
646 _ = self;
647 _ = decl_val;
648 _ = explicit_alignment;
649 _ = src_loc;
650 @panic("TODO lowerAnonDecl");
5089}651}
5090652
5091/// Returns symbol location corresponding to the set entrypoint if any.653pub fn getAnonDeclVAddr(self: *MachO, decl_val: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 {
5092/// Asserts output mode is executable.654 assert(self.llvm_object == null);
5093pub fn getEntryPoint(self: MachO) ?SymbolWithLoc {655 _ = decl_val;
5094 const entry_name = self.entry_name orelse return null;656 _ = reloc_info;
5095 const global = self.getGlobal(entry_name) orelse return null;657 @panic("TODO getAnonDeclVAddr");
5096 return global;
5097}658}
5098659
5099pub fn getDebugSymbols(self: *MachO) ?*DebugSymbols {660pub fn getGlobalSymbol(self: *MachO, name: []const u8, lib_name: ?[]const u8) !u32 {
5100 if (self.d_sym == null) return null;661 _ = self;
5101 return &self.d_sym.?;662 _ = name;
663 _ = lib_name;
664 @panic("TODO getGlobalSymbol");
5102}665}
5103666
5104pub inline fn getPageSize(cpu_arch: std.Target.Cpu.Arch) u16 {667pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
5105 return switch (cpu_arch) {668 return actual_size +| (actual_size / ideal_factor);
5106 .aarch64 => 0x4000,
5107 .x86_64 => 0x1000,
5108 else => unreachable,
5109 };
5110}669}
5111670
5112pub fn requiresCodeSignature(m: *MachO) bool {671fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
5113 if (m.entitlements) |_| return true;672 // TODO: header and load commands have to be part of the __TEXT segment
5114 const comp = m.base.comp;673 const header_size = self.segments.items[self.header_segment_cmd_index.?].filesize;
5115 const target = comp.root_mod.resolved_target.result;674 if (start < header_size)
5116 const cpu_arch = target.cpu.arch;675 return header_size;
5117 const os_tag = target.os.tag;676
5118 const abi = target.abi;677 const end = start + padToIdeal(size);
5119 if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) return true;678
5120 return false;679 for (self.sections.items(.header)) |header| {
680 const tight_size = header.size;
681 const increased_size = padToIdeal(tight_size);
682 const test_end = header.offset + increased_size;
683 if (end > header.offset and start < test_end) {
684 return test_end;
685 }
686 }
687
688 return null;
5121}689}
5122690
5123pub fn getSegmentPrecedence(segname: []const u8) u4 {691fn allocatedSize(self: *MachO, start: u64) u64 {
5124 if (mem.eql(u8, segname, "__PAGEZERO")) return 0x0;692 if (start == 0)
5125 if (mem.eql(u8, segname, "__TEXT")) return 0x1;693 return 0;
5126 if (mem.eql(u8, segname, "__DATA_CONST")) return 0x2;694 var min_pos: u64 = std.math.maxInt(u64);
5127 if (mem.eql(u8, segname, "__DATA")) return 0x3;695 for (self.sections.items(.header)) |header| {
5128 if (mem.eql(u8, segname, "__LINKEDIT")) return 0x5;696 if (header.offset <= start) continue;
5129 return 0x4;697 if (header.offset < min_pos) min_pos = header.offset;
698 }
699 return min_pos - start;
5130}700}
5131701
5132pub fn getSegmentMemoryProtection(segname: []const u8) macho.vm_prot_t {702fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 {
5133 if (mem.eql(u8, segname, "__PAGEZERO")) return macho.PROT.NONE;703 var start: u64 = 0;
5134 if (mem.eql(u8, segname, "__TEXT")) return macho.PROT.READ | macho.PROT.EXEC;704 while (self.detectAllocCollision(start, object_size)) |item_end| {
5135 if (mem.eql(u8, segname, "__LINKEDIT")) return macho.PROT.READ;705 start = mem.alignForward(u64, item_end, min_alignment);
5136 return macho.PROT.READ | macho.PROT.WRITE;706 }
707 return start;
5137}708}
5138709
5139pub fn getSectionPrecedence(header: macho.section_64) u8 {710pub fn makeStaticString(bytes: []const u8) [16]u8 {
5140 const segment_precedence: u4 = getSegmentPrecedence(header.segName());711 var buf = [_]u8{0} ** 16;
5141 const section_precedence: u4 = blk: {712 @memcpy(buf[0..bytes.len], bytes);
5142 if (header.isCode()) {713 return buf;
5143 if (mem.eql(u8, "__text", header.sectName())) break :blk 0x0;
5144 if (header.type() == macho.S_SYMBOL_STUBS) break :blk 0x1;
5145 break :blk 0x2;
5146 }
5147 switch (header.type()) {
5148 macho.S_NON_LAZY_SYMBOL_POINTERS,
5149 macho.S_LAZY_SYMBOL_POINTERS,
5150 => break :blk 0x0,
5151 macho.S_MOD_INIT_FUNC_POINTERS => break :blk 0x1,
5152 macho.S_MOD_TERM_FUNC_POINTERS => break :blk 0x2,
5153 macho.S_ZEROFILL => break :blk 0xf,
5154 macho.S_THREAD_LOCAL_REGULAR => break :blk 0xd,
5155 macho.S_THREAD_LOCAL_ZEROFILL => break :blk 0xe,
5156 else => {
5157 if (mem.eql(u8, "__unwind_info", header.sectName())) break :blk 0xe;
5158 if (mem.eql(u8, "__eh_frame", header.sectName())) break :blk 0xf;
5159 break :blk 0x3;
5160 },
5161 }
5162 };
5163 return (@as(u8, @intCast(segment_precedence)) << 4) + section_precedence;
5164}714}
5165715
5166pub const ParseErrorCtx = struct {716pub const ParseErrorCtx = struct {
...@@ -5354,308 +904,105 @@ pub fn reportUndefined(self: *MachO) error{OutOfMemory}!void {...@@ -5354,308 +904,105 @@ pub fn reportUndefined(self: *MachO) error{OutOfMemory}!void {
5354 }904 }
5355}905}
5356906
5357fn reportSymbolCollision(907// fn reportSymbolCollision(
5358 self: *MachO,908// self: *MachO,
5359 first: SymbolWithLoc,909// first: SymbolWithLoc,
5360 other: SymbolWithLoc,910// other: SymbolWithLoc,
5361) error{OutOfMemory}!void {911// ) error{OutOfMemory}!void {
5362 const comp = self.base.comp;912// const comp = self.base.comp;
5363 const gpa = comp.gpa;913// const gpa = comp.gpa;
5364 try comp.link_errors.ensureUnusedCapacity(gpa, 1);914// try comp.link_errors.ensureUnusedCapacity(gpa, 1);
5365915
5366 var notes = try std.ArrayList(File.ErrorMsg).initCapacity(gpa, 2);916// var notes = try std.ArrayList(File.ErrorMsg).initCapacity(gpa, 2);
5367 defer notes.deinit();917// defer notes.deinit();
5368918
5369 if (first.getFile()) |file| {919// if (first.getFile()) |file| {
5370 const note = try std.fmt.allocPrint(gpa, "first definition in {s}", .{920// const note = try std.fmt.allocPrint(gpa, "first definition in {s}", .{
5371 self.objects.items[file].name,921// self.objects.items[file].name,
5372 });922// });
5373 notes.appendAssumeCapacity(.{ .msg = note });923// notes.appendAssumeCapacity(.{ .msg = note });
5374 }924// }
5375 if (other.getFile()) |file| {925// if (other.getFile()) |file| {
5376 const note = try std.fmt.allocPrint(gpa, "next definition in {s}", .{926// const note = try std.fmt.allocPrint(gpa, "next definition in {s}", .{
5377 self.objects.items[file].name,927// self.objects.items[file].name,
5378 });928// });
5379 notes.appendAssumeCapacity(.{ .msg = note });929// notes.appendAssumeCapacity(.{ .msg = note });
5380 }930// }
5381931
5382 var err_msg = File.ErrorMsg{ .msg = try std.fmt.allocPrint(gpa, "symbol {s} defined multiple times", .{932// var err_msg = File.ErrorMsg{ .msg = try std.fmt.allocPrint(gpa, "symbol {s} defined multiple times", .{
5383 self.getSymbolName(first),933// self.getSymbolName(first),
5384 }) };934// }) };
5385 err_msg.notes = try notes.toOwnedSlice();935// err_msg.notes = try notes.toOwnedSlice();
5386936
5387 comp.link_errors.appendAssumeCapacity(err_msg);937// comp.link_errors.appendAssumeCapacity(err_msg);
5388}938// }
5389939
5390fn reportUnhandledSymbolType(self: *MachO, sym_with_loc: SymbolWithLoc) error{OutOfMemory}!void {940// fn reportUnhandledSymbolType(self: *MachO, sym_with_loc: SymbolWithLoc) error{OutOfMemory}!void {
5391 const comp = self.base.comp;941// const comp = self.base.comp;
5392 const gpa = comp.gpa;942// const gpa = comp.gpa;
5393 try comp.link_errors.ensureUnusedCapacity(gpa, 1);943// try comp.link_errors.ensureUnusedCapacity(gpa, 1);
5394944
5395 const notes = try gpa.alloc(File.ErrorMsg, 1);945// const notes = try gpa.alloc(File.ErrorMsg, 1);
5396 errdefer gpa.free(notes);946// errdefer gpa.free(notes);
5397947
5398 const file = sym_with_loc.getFile().?;948// const file = sym_with_loc.getFile().?;
5399 notes[0] = .{ .msg = try std.fmt.allocPrint(gpa, "defined in {s}", .{self.objects.items[file].name}) };949// notes[0] = .{ .msg = try std.fmt.allocPrint(gpa, "defined in {s}", .{self.objects.items[file].name}) };
950
951// const sym = self.getSymbol(sym_with_loc);
952// const sym_type = if (sym.stab())
953// "stab"
954// else if (sym.indr())
955// "indirect"
956// else if (sym.abs())
957// "absolute"
958// else
959// unreachable;
960
961// comp.link_errors.appendAssumeCapacity(.{
962// .msg = try std.fmt.allocPrint(gpa, "unhandled symbol type: '{s}' has type {s}", .{
963// self.getSymbolName(sym_with_loc),
964// sym_type,
965// }),
966// .notes = notes,
967// });
968// }
5400969
5401 const sym = self.getSymbol(sym_with_loc);970pub fn getDebugSymbols(self: *MachO) ?*DebugSymbols {
5402 const sym_type = if (sym.stab())971 if (self.d_sym) |*ds| {
5403 "stab"972 return ds;
5404 else if (sym.indr())973 } else return null;
5405 "indirect"
5406 else if (sym.abs())
5407 "absolute"
5408 else
5409 unreachable;
5410
5411 comp.link_errors.appendAssumeCapacity(.{
5412 .msg = try std.fmt.allocPrint(gpa, "unhandled symbol type: '{s}' has type {s}", .{
5413 self.getSymbolName(sym_with_loc),
5414 sym_type,
5415 }),
5416 .notes = notes,
5417 });
5418}
5419
5420/// Binary search
5421pub fn bsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize {
5422 if (!@hasDecl(@TypeOf(predicate), "predicate"))
5423 @compileError("Predicate is required to define fn predicate(@This(), T) bool");
5424
5425 var min: usize = 0;
5426 var max: usize = haystack.len;
5427 while (min < max) {
5428 const index = (min + max) / 2;
5429 const curr = haystack[index];
5430 if (predicate.predicate(curr)) {
5431 min = index + 1;
5432 } else {
5433 max = index;
5434 }
5435 }
5436 return min;
5437}974}
5438975
5439/// Linear search976pub fn ptraceAttach(self: *MachO, pid: std.os.pid_t) !void {
5440pub fn lsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize {977 if (!is_hot_update_compatible) return;
5441 if (!@hasDecl(@TypeOf(predicate), "predicate"))
5442 @compileError("Predicate is required to define fn predicate(@This(), T) bool");
5443
5444 var i: usize = 0;
5445 while (i < haystack.len) : (i += 1) {
5446 if (predicate.predicate(haystack[i])) break;
5447 }
5448 return i;
5449}
5450978
5451pub fn logSegments(self: *MachO) void {979 const mach_task = try std.os.darwin.machTaskForPid(pid);
5452 log.debug("segments:", .{});980 log.debug("Mach task for pid {d}: {any}", .{ pid, mach_task });
5453 for (self.segments.items, 0..) |segment, i| {981 self.hot_state.mach_task = mach_task;
5454 log.debug(" segment({d}): {s} @{x} ({x}), sizeof({x})", .{
5455 i,
5456 segment.segName(),
5457 segment.fileoff,
5458 segment.vmaddr,
5459 segment.vmsize,
5460 });
5461 }
5462}
5463982
5464pub fn logSections(self: *MachO) void {983 // TODO start exception handler in another thread
5465 log.debug("sections:", .{});
5466 for (self.sections.items(.header), 0..) |header, i| {
5467 log.debug(" sect({d}): {s},{s} @{x} ({x}), sizeof({x})", .{
5468 i + 1,
5469 header.segName(),
5470 header.sectName(),
5471 header.offset,
5472 header.addr,
5473 header.size,
5474 });
5475 }
5476}
5477984
5478fn logSymAttributes(sym: macho.nlist_64, buf: []u8) []const u8 {985 // TODO enable ones we register for exceptions
5479 if (sym.sect()) {986 // try std.os.ptrace(std.os.darwin.PT.ATTACHEXC, pid, 0, 0);
5480 buf[0] = 's';
5481 }
5482 if (sym.ext()) {
5483 if (sym.weakDef() or sym.pext()) {
5484 buf[1] = 'w';
5485 } else {
5486 buf[1] = 'e';
5487 }
5488 }
5489 if (sym.tentative()) {
5490 buf[2] = 't';
5491 }
5492 if (sym.undf()) {
5493 buf[3] = 'u';
5494 }
5495 return buf[0..];
5496}987}
5497988
5498pub fn logSymtab(self: *MachO) void {989pub fn ptraceDetach(self: *MachO, pid: std.os.pid_t) !void {
5499 var buf: [4]u8 = undefined;990 if (!is_hot_update_compatible) return;
5500
5501 const scoped_log = std.log.scoped(.symtab);
5502
5503 scoped_log.debug("locals:", .{});
5504 for (self.objects.items, 0..) |object, id| {
5505 scoped_log.debug(" object({d}): {s}", .{ id, object.name });
5506 if (object.in_symtab == null) continue;
5507 for (object.symtab, 0..) |sym, sym_id| {
5508 @memset(&buf, '_');
5509 scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s}", .{
5510 sym_id,
5511 object.getSymbolName(@as(u32, @intCast(sym_id))),
5512 sym.n_value,
5513 sym.n_sect,
5514 logSymAttributes(sym, &buf),
5515 });
5516 }
5517 }
5518 scoped_log.debug(" object(-1)", .{});
5519 for (self.locals.items, 0..) |sym, sym_id| {
5520 if (sym.undf()) continue;
5521 scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s}", .{
5522 sym_id,
5523 self.strtab.get(sym.n_strx).?,
5524 sym.n_value,
5525 sym.n_sect,
5526 logSymAttributes(sym, &buf),
5527 });
5528 }
5529
5530 scoped_log.debug("exports:", .{});
5531 for (self.globals.items, 0..) |global, i| {
5532 const sym = self.getSymbol(global);
5533 if (sym.undf()) continue;
5534 if (sym.n_desc == N_DEAD) continue;
5535 if (sym.n_desc == N_BOUNDARY) continue;
5536 scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s} (def in object({?}))", .{
5537 i,
5538 self.getSymbolName(global),
5539 sym.n_value,
5540 sym.n_sect,
5541 logSymAttributes(sym, &buf),
5542 global.file,
5543 });
5544 }
5545
5546 scoped_log.debug("imports:", .{});
5547 for (self.globals.items, 0..) |global, i| {
5548 const sym = self.getSymbol(global);
5549 if (!sym.undf()) continue;
5550 if (sym.n_desc == N_DEAD) continue;
5551 if (sym.n_desc == N_BOUNDARY) continue;
5552 const ord = @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER);
5553 scoped_log.debug(" %{d}: {s} @{x} in ord({d}), {s}", .{
5554 i,
5555 self.getSymbolName(global),
5556 sym.n_value,
5557 ord,
5558 logSymAttributes(sym, &buf),
5559 });
5560 }
5561
5562 scoped_log.debug("GOT entries:", .{});
5563 scoped_log.debug("{}", .{self.got_table});
5564
5565 scoped_log.debug("TLV pointers:", .{});
5566 scoped_log.debug("{}", .{self.tlv_ptr_table});
5567
5568 scoped_log.debug("stubs entries:", .{});
5569 scoped_log.debug("{}", .{self.stub_table});
5570
5571 scoped_log.debug("thunks:", .{});
5572 for (self.thunks.items, 0..) |thunk, i| {
5573 scoped_log.debug(" thunk({d})", .{i});
5574 const slice = thunk.targets.slice();
5575 for (slice.items(.tag), slice.items(.target), 0..) |tag, target, j| {
5576 const atom_index = @as(u32, @intCast(thunk.getStartAtomIndex() + j));
5577 const atom = self.getAtom(atom_index);
5578 const atom_sym = self.getSymbol(atom.getSymbolWithLoc());
5579 const target_addr = switch (tag) {
5580 .stub => self.getStubsEntryAddress(target).?,
5581 .atom => self.getSymbol(target).n_value,
5582 };
5583 scoped_log.debug(" {d}@{x} => {s}({s}@{x})", .{
5584 j,
5585 atom_sym.n_value,
5586 @tagName(tag),
5587 self.getSymbolName(target),
5588 target_addr,
5589 });
5590 }
5591 }
5592}
5593991
5594pub fn logAtoms(self: *MachO) void {992 _ = pid;
5595 log.debug("atoms:", .{});
5596 const slice = self.sections.slice();
5597 for (slice.items(.first_atom_index), 0..) |first_atom_index, sect_id| {
5598 var atom_index = first_atom_index orelse continue;
5599 const header = slice.items(.header)[sect_id];
5600993
5601 log.debug("{s},{s}", .{ header.segName(), header.sectName() });994 // TODO stop exception handler
5602995
5603 while (true) {996 // TODO see comment in ptraceAttach
5604 const atom = self.getAtom(atom_index);997 // try std.os.ptrace(std.os.darwin.PT.DETACH, pid, 0, 0);
5605 self.logAtom(atom_index, log);
5606998
5607 if (atom.next_index) |next_index| {999 self.hot_state.mach_task = null;
5608 atom_index = next_index;
5609 } else break;
5610 }
5611 }
5612}1000}
56131001
5614pub fn logAtom(self: *MachO, atom_index: Atom.Index, logger: anytype) void {1002const is_hot_update_compatible = switch (builtin.target.os.tag) {
5615 if (!build_options.enable_logging) return;1003 .macos => true,
56161004 else => false,
5617 const atom = self.getAtom(atom_index);1005};
5618 const sym = self.getSymbol(atom.getSymbolWithLoc());
5619 const sym_name = self.getSymbolName(atom.getSymbolWithLoc());
5620 logger.debug(" ATOM({d}, %{d}, '{s}') @ {x} (sizeof({x}), alignof({x})) in object({?}) in sect({d})", .{
5621 atom_index,
5622 atom.sym_index,
5623 sym_name,
5624 sym.n_value,
5625 atom.size,
5626 atom.alignment,
5627 atom.getFile(),
5628 sym.n_sect,
5629 });
5630
5631 if (atom.getFile() != null) {
5632 var it = Atom.getInnerSymbolsIterator(self, atom_index);
5633 while (it.next()) |sym_loc| {
5634 const inner = self.getSymbol(sym_loc);
5635 const inner_name = self.getSymbolName(sym_loc);
5636 const offset = Atom.calcInnerSymbolOffset(self, atom_index, sym_loc.sym_index);
5637
5638 logger.debug(" (%{d}, '{s}') @ {x} ({x})", .{
5639 sym_loc.sym_index,
5640 inner_name,
5641 inner.n_value,
5642 offset,
5643 });
5644 }
5645
5646 if (Atom.getSectionAlias(self, atom_index)) |sym_loc| {
5647 const alias = self.getSymbol(sym_loc);
5648 const alias_name = self.getSymbolName(sym_loc);
5649
5650 logger.debug(" (%{d}, '{s}') @ {x} ({x})", .{
5651 sym_loc.sym_index,
5652 alias_name,
5653 alias.n_value,
5654 0,
5655 });
5656 }
5657 }
5658}
56591006
5660const default_entry_symbol_name = "_main";1007const default_entry_symbol_name = "_main";
56611008
...@@ -5663,17 +1010,6 @@ pub const base_tag: File.Tag = File.Tag.macho;...@@ -5663,17 +1010,6 @@ pub const base_tag: File.Tag = File.Tag.macho;
5663pub const N_DEAD: u16 = @as(u16, @bitCast(@as(i16, -1)));1010pub const N_DEAD: u16 = @as(u16, @bitCast(@as(i16, -1)));
5664pub const N_BOUNDARY: u16 = @as(u16, @bitCast(@as(i16, -2)));1011pub const N_BOUNDARY: u16 = @as(u16, @bitCast(@as(i16, -2)));
56651012
5666/// Mode of operation of the linker.
5667pub const Mode = enum {
5668 /// Incremental mode will preallocate segments/sections and is compatible with
5669 /// watch and HCS modes of operation.
5670 incremental,
5671 /// Zld mode will link relocatables in a traditional, one-shot
5672 /// fashion (default for LLVM backend). It acts as a drop-in replacement for
5673 /// LLD.
5674 zld,
5675};
5676
5677pub const Section = struct {1013pub const Section = struct {
5678 header: macho.section_64,1014 header: macho.section_64,
5679 segment_index: u8,1015 segment_index: u8,
...@@ -5698,11 +1034,6 @@ pub const Section = struct {...@@ -5698,11 +1034,6 @@ pub const Section = struct {
5698 free_list: std.ArrayListUnmanaged(Atom.Index) = .{},1034 free_list: std.ArrayListUnmanaged(Atom.Index) = .{},
5699};1035};
57001036
5701const is_hot_update_compatible = switch (builtin.target.os.tag) {
5702 .macos => true,
5703 else => false,
5704};
5705
5706const LazySymbolTable = std.AutoArrayHashMapUnmanaged(InternPool.OptionalDeclIndex, LazySymbolMetadata);1037const LazySymbolTable = std.AutoArrayHashMapUnmanaged(InternPool.OptionalDeclIndex, LazySymbolMetadata);
57071038
5708const LazySymbolMetadata = struct {1039const LazySymbolMetadata = struct {
...@@ -5713,8 +1044,6 @@ const LazySymbolMetadata = struct {...@@ -5713,8 +1044,6 @@ const LazySymbolMetadata = struct {
5713 data_const_state: State = .unused,1044 data_const_state: State = .unused,
5714};1045};
57151046
5716const TlvSymbolTable = std.AutoArrayHashMapUnmanaged(SymbolWithLoc, Atom.Index);
5717
5718const DeclMetadata = struct {1047const DeclMetadata = struct {
5719 atom: Atom.Index,1048 atom: Atom.Index,
5720 section: u8,1049 section: u8,
...@@ -5737,38 +1066,143 @@ const DeclMetadata = struct {...@@ -5737,38 +1066,143 @@ const DeclMetadata = struct {
5737 }1066 }
5738};1067};
57391068
5740const DeclTable = std.AutoArrayHashMapUnmanaged(InternPool.DeclIndex, DeclMetadata);1069const HotUpdateState = struct {
5741const AnonDeclTable = std.AutoHashMapUnmanaged(InternPool.Index, DeclMetadata);1070 mach_task: ?std.os.darwin.MachTask = null,
5742const BindingTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Atom.Binding));1071};
5743const UnnamedConstTable = std.AutoArrayHashMapUnmanaged(InternPool.DeclIndex, std.ArrayListUnmanaged(Atom.Index));1072
5744const RebaseTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(u32));1073pub const SymtabCtx = struct {
5745const RelocationTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Relocation));1074 ilocal: u32 = 0,
5746const ActionTable = std.AutoHashMapUnmanaged(u32, RelocFlags);1075 istab: u32 = 0,
57471076 iexport: u32 = 0,
5748pub const RelocFlags = packed struct {1077 iimport: u32 = 0,
5749 add_got: bool = false,1078 nlocals: u32 = 0,
5750 add_stub: bool = false,1079 nstabs: u32 = 0,
1080 nexports: u32 = 0,
1081 nimports: u32 = 0,
1082 strsize: u32 = 0,
5751};1083};
57521084
5753pub const SymbolWithLoc = extern struct {1085pub const null_sym = macho.nlist_64{
5754 // Index into the respective symbol table.1086 .n_strx = 0,
5755 sym_index: u32,1087 .n_type = 0,
1088 .n_sect = 0,
1089 .n_desc = 0,
1090 .n_value = 0,
1091};
1092
1093pub const Platform = struct {
1094 platform: macho.PLATFORM,
1095 version: Version,
1096
1097 /// Using Apple's ld64 as our blueprint, `min_version` as well as `sdk_version` are set to
1098 /// the extracted minimum platform version.
1099 pub fn fromLoadCommand(lc: macho.LoadCommandIterator.LoadCommand) Platform {
1100 switch (lc.cmd()) {
1101 .BUILD_VERSION => {
1102 const lc_cmd = lc.cast(macho.build_version_command).?;
1103 return .{
1104 .platform = lc_cmd.platform,
1105 .version = .{ .value = lc_cmd.minos },
1106 };
1107 },
1108 .VERSION_MIN_MACOSX,
1109 .VERSION_MIN_IPHONEOS,
1110 .VERSION_MIN_TVOS,
1111 .VERSION_MIN_WATCHOS,
1112 => {
1113 const lc_cmd = lc.cast(macho.version_min_command).?;
1114 return .{
1115 .platform = switch (lc.cmd()) {
1116 .VERSION_MIN_MACOSX => .MACOS,
1117 .VERSION_MIN_IPHONEOS => .IOS,
1118 .VERSION_MIN_TVOS => .TVOS,
1119 .VERSION_MIN_WATCHOS => .WATCHOS,
1120 else => unreachable,
1121 },
1122 .version = .{ .value = lc_cmd.version },
1123 };
1124 },
1125 else => unreachable,
1126 }
1127 }
1128
1129 pub fn isBuildVersionCompatible(plat: Platform) bool {
1130 inline for (supported_platforms) |sup_plat| {
1131 if (sup_plat[0] == plat.platform) {
1132 return sup_plat[1] <= plat.version.value;
1133 }
1134 }
1135 return false;
1136 }
1137};
1138
1139pub const Version = struct {
1140 value: u32,
1141
1142 pub fn major(v: Version) u16 {
1143 return @as(u16, @truncate(v.value >> 16));
1144 }
57561145
5757 // 0 means it's a synthetic global.1146 pub fn minor(v: Version) u8 {
5758 file: u32 = 0,1147 return @as(u8, @truncate(v.value >> 8));
1148 }
1149
1150 pub fn patch(v: Version) u8 {
1151 return @as(u8, @truncate(v.value));
1152 }
1153
1154 pub fn parse(raw: []const u8) ?Version {
1155 var parsed: [3]u16 = [_]u16{0} ** 3;
1156 var count: usize = 0;
1157 var it = std.mem.splitAny(u8, raw, ".");
1158 while (it.next()) |comp| {
1159 if (count >= 3) return null;
1160 parsed[count] = std.fmt.parseInt(u16, comp, 10) catch return null;
1161 count += 1;
1162 }
1163 if (count == 0) return null;
1164 const maj = parsed[0];
1165 const min = std.math.cast(u8, parsed[1]) orelse return null;
1166 const pat = std.math.cast(u8, parsed[2]) orelse return null;
1167 return Version.new(maj, min, pat);
1168 }
57591169
5760 pub fn getFile(self: SymbolWithLoc) ?u32 {1170 pub fn new(maj: u16, min: u8, pat: u8) Version {
5761 if (self.file == 0) return null;1171 return .{ .value = (@as(u32, @intCast(maj)) << 16) | (@as(u32, @intCast(min)) << 8) | pat };
5762 return self.file - 1;
5763 }1172 }
57641173
5765 pub fn eql(self: SymbolWithLoc, other: SymbolWithLoc) bool {1174 pub fn format(
5766 return self.file == other.file and self.sym_index == other.sym_index;1175 v: Version,
1176 comptime unused_fmt_string: []const u8,
1177 options: std.fmt.FormatOptions,
1178 writer: anytype,
1179 ) !void {
1180 _ = unused_fmt_string;
1181 _ = options;
1182 try writer.print("{d}.{d}.{d}", .{
1183 v.major(),
1184 v.minor(),
1185 v.patch(),
1186 });
5767 }1187 }
5768};1188};
57691189
5770const HotUpdateState = struct {1190const SupportedPlatforms = struct {
5771 mach_task: ?std.os.darwin.MachTask = null,1191 macho.PLATFORM, // Platform identifier
1192 u32, // Min platform version for which to emit LC_BUILD_VERSION
1193 u32, // Min supported platform version
1194 ?[]const u8, // Env var to look for
1195};
1196
1197// Source: https://github.com/apple-oss-distributions/ld64/blob/59a99ab60399c5e6c49e6945a9e1049c42b71135/src/ld/PlatformSupport.cpp#L52
1198const supported_platforms = [_]SupportedPlatforms{
1199 .{ .MACOS, 0xA0E00, 0xA0800, "MACOSX_DEPLOYMENT_TARGET" },
1200 .{ .IOS, 0xC0000, 0x70000, "IPHONEOS_DEPLOYMENT_TARGET" },
1201 .{ .TVOS, 0xC0000, 0x70000, "TVOS_DEPLOYMENT_TARGET" },
1202 .{ .WATCHOS, 0x50000, 0x20000, "WATCHOS_DEPLOYMENT_TARGET" },
1203 .{ .IOSSIMULATOR, 0xD0000, 0x80000, null },
1204 .{ .TVOSSIMULATOR, 0xD0000, 0x80000, null },
1205 .{ .WATCHOSSIMULATOR, 0x60000, 0x20000, null },
5772};1206};
57731207
5774/// When allocating, the ideal_capacity is calculated by1208/// When allocating, the ideal_capacity is calculated by
...@@ -5812,20 +1246,20 @@ const fat = @import("MachO/fat.zig");...@@ -5812,20 +1246,20 @@ const fat = @import("MachO/fat.zig");
5812const link = @import("../link.zig");1246const link = @import("../link.zig");
5813const llvm_backend = @import("../codegen/llvm.zig");1247const llvm_backend = @import("../codegen/llvm.zig");
5814const load_commands = @import("MachO/load_commands.zig");1248const load_commands = @import("MachO/load_commands.zig");
5815const stubs = @import("MachO/stubs.zig");
5816const tapi = @import("tapi.zig");1249const tapi = @import("tapi.zig");
5817const target_util = @import("../target.zig");1250const target_util = @import("../target.zig");
5818const thunks = @import("MachO/thunks.zig");1251const thunks = @import("MachO/thunks.zig");
5819const trace = @import("../tracy.zig").trace;1252const trace = @import("../tracy.zig").trace;
5820const zld = @import("MachO/zld.zig");
58211253
5822const Air = @import("../Air.zig");1254const Air = @import("../Air.zig");
1255const Alignment = Atom.Alignment;
5823const Allocator = mem.Allocator;1256const Allocator = mem.Allocator;
5824const Archive = @import("MachO/Archive.zig");1257const Archive = @import("MachO/Archive.zig");
5825pub const Atom = @import("MachO/Atom.zig");1258pub const Atom = @import("MachO/Atom.zig");
5826const Cache = std.Build.Cache;1259const Cache = std.Build.Cache;
5827const CodeSignature = @import("MachO/CodeSignature.zig");1260const CodeSignature = @import("MachO/CodeSignature.zig");
5828const Compilation = @import("../Compilation.zig");1261const Compilation = @import("../Compilation.zig");
1262pub const DebugSymbols = @import("MachO/DebugSymbols.zig");
5829const Dwarf = File.Dwarf;1263const Dwarf = File.Dwarf;
5830const DwarfInfo = @import("MachO/DwarfInfo.zig");1264const DwarfInfo = @import("MachO/DwarfInfo.zig");
5831const Dylib = @import("MachO/Dylib.zig");1265const Dylib = @import("MachO/Dylib.zig");
...@@ -5837,17 +1271,9 @@ const LlvmObject = @import("../codegen/llvm.zig").Object;...@@ -5837,17 +1271,9 @@ const LlvmObject = @import("../codegen/llvm.zig").Object;
5837const Md5 = std.crypto.hash.Md5;1271const Md5 = std.crypto.hash.Md5;
5838const Module = @import("../Module.zig");1272const Module = @import("../Module.zig");
5839const InternPool = @import("../InternPool.zig");1273const InternPool = @import("../InternPool.zig");
5840const Platform = load_commands.Platform;
5841const Relocation = @import("MachO/Relocation.zig");1274const Relocation = @import("MachO/Relocation.zig");
5842const StringTable = @import("StringTable.zig");1275const StringTable = @import("StringTable.zig");
5843const TableSection = @import("table_section.zig").TableSection;1276const TableSection = @import("table_section.zig").TableSection;
5844const Trie = @import("MachO/Trie.zig");
5845const Type = @import("../type.zig").Type;1277const Type = @import("../type.zig").Type;
5846const TypedValue = @import("../TypedValue.zig");1278const TypedValue = @import("../TypedValue.zig");
5847const Value = @import("../value.zig").Value;1279const Value = @import("../value.zig").Value;
5848const Alignment = Atom.Alignment;
5849
5850pub const DebugSymbols = @import("MachO/DebugSymbols.zig");
5851pub const Bind = @import("MachO/dyld_info/bind.zig").Bind(*const MachO, SymbolWithLoc);
5852pub const LazyBind = @import("MachO/dyld_info/bind.zig").LazyBind(*const MachO, SymbolWithLoc);
5853pub const Rebase = @import("MachO/dyld_info/Rebase.zig");
src/link/MachO/Atom.zig+45-39
...@@ -449,7 +449,7 @@ fn resolveRelocInner(...@@ -449,7 +449,7 @@ fn resolveRelocInner(
449 if (rel.getTargetSymbol(macho_file).flags.got) {449 if (rel.getTargetSymbol(macho_file).flags.got) {
450 try writer.writeInt(i32, @intCast(G + A - P), .little);450 try writer.writeInt(i32, @intCast(G + A - P), .little);
451 } else {451 } else {
452 try relaxGotLoad(code[rel_offset - 3 ..]);452 try x86_64.relaxGotLoad(code[rel_offset - 3 ..]);
453 try writer.writeInt(i32, @intCast(S + A - P), .little);453 try writer.writeInt(i32, @intCast(S + A - P), .little);
454 }454 }
455 },455 },
...@@ -463,7 +463,7 @@ fn resolveRelocInner(...@@ -463,7 +463,7 @@ fn resolveRelocInner(
463 const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file));463 const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file));
464 try writer.writeInt(i32, @intCast(S_ + A - P), .little);464 try writer.writeInt(i32, @intCast(S_ + A - P), .little);
465 } else {465 } else {
466 try relaxTlv(code[rel_offset - 3 ..]);466 try x86_64.relaxTlv(code[rel_offset - 3 ..]);
467 try writer.writeInt(i32, @intCast(S + A - P), .little);467 try writer.writeInt(i32, @intCast(S + A - P), .little);
468 }468 }
469 },469 },
...@@ -631,43 +631,51 @@ fn resolveRelocInner(...@@ -631,43 +631,51 @@ fn resolveRelocInner(
631 }631 }
632}632}
633633
634fn relaxGotLoad(code: []u8) error{RelaxFail}!void {634const x86_64 = struct {
635 const old_inst = disassemble(code) orelse return error.RelaxFail;635 fn relaxGotLoad(code: []u8) error{RelaxFail}!void {
636 switch (old_inst.encoding.mnemonic) {636 const old_inst = disassemble(code) orelse return error.RelaxFail;
637 .mov => {637 switch (old_inst.encoding.mnemonic) {
638 const inst = Instruction.new(old_inst.prefix, .lea, &old_inst.ops) catch return error.RelaxFail;638 .mov => {
639 relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding });639 const inst = Instruction.new(old_inst.prefix, .lea, &old_inst.ops) catch return error.RelaxFail;
640 encode(&.{inst}, code) catch return error.RelaxFail;640 relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding });
641 },641 encode(&.{inst}, code) catch return error.RelaxFail;
642 else => return error.RelaxFail,642 },
643 else => return error.RelaxFail,
644 }
643 }645 }
644}
645646
646fn relaxTlv(code: []u8) error{RelaxFail}!void {647 fn relaxTlv(code: []u8) error{RelaxFail}!void {
647 const old_inst = disassemble(code) orelse return error.RelaxFail;648 const old_inst = disassemble(code) orelse return error.RelaxFail;
648 switch (old_inst.encoding.mnemonic) {649 switch (old_inst.encoding.mnemonic) {
649 .mov => {650 .mov => {
650 const inst = Instruction.new(old_inst.prefix, .lea, &old_inst.ops) catch return error.RelaxFail;651 const inst = Instruction.new(old_inst.prefix, .lea, &old_inst.ops) catch return error.RelaxFail;
651 relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding });652 relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding });
652 encode(&.{inst}, code) catch return error.RelaxFail;653 encode(&.{inst}, code) catch return error.RelaxFail;
653 },654 },
654 else => return error.RelaxFail,655 else => return error.RelaxFail,
656 }
655 }657 }
656}
657658
658fn disassemble(code: []const u8) ?Instruction {659 fn disassemble(code: []const u8) ?Instruction {
659 var disas = Disassembler.init(code);660 var disas = Disassembler.init(code);
660 const inst = disas.next() catch return null;661 const inst = disas.next() catch return null;
661 return inst;662 return inst;
662}663 }
663664
664fn encode(insts: []const Instruction, code: []u8) !void {665 fn encode(insts: []const Instruction, code: []u8) !void {
665 var stream = std.io.fixedBufferStream(code);666 var stream = std.io.fixedBufferStream(code);
666 const writer = stream.writer();667 const writer = stream.writer();
667 for (insts) |inst| {668 for (insts) |inst| {
668 try inst.encode(writer, .{});669 try inst.encode(writer, .{});
670 }
669 }671 }
670}672
673 const bits = @import("../../arch/x86_64/bits.zig");
674 const encoder = @import("../../arch/x86_64/encoder.zig");
675 const Disassembler = @import("../../arch/x86_64/Disassembler.zig");
676 const Immediate = bits.Immediate;
677 const Instruction = encoder.Instruction;
678};
671679
672pub fn calcNumRelocs(self: Atom, macho_file: *MachO) u32 {680pub fn calcNumRelocs(self: Atom, macho_file: *MachO) u32 {
673 switch (macho_file.options.cpu_arch.?) {681 switch (macho_file.options.cpu_arch.?) {
...@@ -879,24 +887,22 @@ pub const Loc = struct {...@@ -879,24 +887,22 @@ pub const Loc = struct {
879 len: usize = 0,887 len: usize = 0,
880};888};
881889
882const aarch64 = @import("../aarch64.zig");890pub const Alignment = @import("../../InternPool.zig").Alignment;
891
892const aarch64 = @import("../../arch/aarch64/bits.zig");
883const assert = std.debug.assert;893const assert = std.debug.assert;
884const bind = @import("dyld_info/bind.zig");894const bind = @import("dyld_info/bind.zig");
885const dis_x86_64 = @import("dis_x86_64");
886const macho = std.macho;895const macho = std.macho;
887const math = std.math;896const math = std.math;
888const mem = std.mem;897const mem = std.mem;
889const log = std.log.scoped(.link);898const log = std.log.scoped(.link);
890const relocs_log = std.log.scoped(.relocs);899const relocs_log = std.log.scoped(.relocs);
891const std = @import("std");900const std = @import("std");
892const trace = @import("../tracy.zig").trace;901const trace = @import("../../tracy.zig").trace;
893902
894const Allocator = mem.Allocator;903const Allocator = mem.Allocator;
895const Atom = @This();904const Atom = @This();
896const Disassembler = dis_x86_64.Disassembler;
897const File = @import("file.zig").File;905const File = @import("file.zig").File;
898const Instruction = dis_x86_64.Instruction;
899const Immediate = dis_x86_64.Immediate;
900const MachO = @import("../MachO.zig");906const MachO = @import("../MachO.zig");
901const Object = @import("Object.zig");907const Object = @import("Object.zig");
902const Relocation = @import("Relocation.zig");908const Relocation = @import("Relocation.zig");
src/link/MachO/CodeSignature.zig-1
...@@ -11,7 +11,6 @@ const Allocator = mem.Allocator;...@@ -11,7 +11,6 @@ const Allocator = mem.Allocator;
11const Hasher = @import("hasher.zig").ParallelHasher;11const Hasher = @import("hasher.zig").ParallelHasher;
12const MachO = @import("../MachO.zig");12const MachO = @import("../MachO.zig");
13const Sha256 = std.crypto.hash.sha2.Sha256;13const Sha256 = std.crypto.hash.sha2.Sha256;
14const Zld = @import("../Zld.zig");
1514
16const hash_size = Sha256.digest_length;15const hash_size = Sha256.digest_length;
1716
src/link/MachO/DwarfInfo.zig+1-1
...@@ -461,7 +461,7 @@ const leb = std.leb;...@@ -461,7 +461,7 @@ const leb = std.leb;
461const log = std.log.scoped(.link);461const log = std.log.scoped(.link);
462const mem = std.mem;462const mem = std.mem;
463const std = @import("std");463const std = @import("std");
464const trace = @import("../tracy.zig").trace;464const trace = @import("../../tracy.zig").trace;
465465
466const Allocator = mem.Allocator;466const Allocator = mem.Allocator;
467const DwarfInfo = @This();467const DwarfInfo = @This();
src/link/MachO/Dylib.zig+2-2
...@@ -12,7 +12,7 @@ symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},...@@ -12,7 +12,7 @@ symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
12dependents: std.ArrayListUnmanaged(Id) = .{},12dependents: std.ArrayListUnmanaged(Id) = .{},
13rpaths: std.StringArrayHashMapUnmanaged(void) = .{},13rpaths: std.StringArrayHashMapUnmanaged(void) = .{},
14umbrella: File.Index = 0,14umbrella: File.Index = 0,
15platform: ?MachO.Options.Platform = null,15platform: ?MachO.Platform = null,
1616
17needed: bool,17needed: bool,
18weak: bool,18weak: bool,
...@@ -815,7 +815,7 @@ const macho = std.macho;...@@ -815,7 +815,7 @@ const macho = std.macho;
815const math = std.math;815const math = std.math;
816const mem = std.mem;816const mem = std.mem;
817const tapi = @import("../tapi.zig");817const tapi = @import("../tapi.zig");
818const trace = @import("../tracy.zig").trace;818const trace = @import("../../tracy.zig").trace;
819const std = @import("std");819const std = @import("std");
820820
821const Allocator = mem.Allocator;821const Allocator = mem.Allocator;
src/link/MachO/Object.zig+2-3
...@@ -12,7 +12,7 @@ strtab: []const u8 = &[0]u8{},...@@ -12,7 +12,7 @@ strtab: []const u8 = &[0]u8{},
12symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},12symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
13atoms: std.ArrayListUnmanaged(Atom.Index) = .{},13atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
1414
15platform: ?MachO.Options.Platform = null,15platform: ?MachO.Platform = null,
16dwarf_info: ?DwarfInfo = null,16dwarf_info: ?DwarfInfo = null,
17stab_files: std.ArrayListUnmanaged(StabFile) = .{},17stab_files: std.ArrayListUnmanaged(StabFile) = .{},
1818
...@@ -2075,7 +2075,7 @@ const log = std.log.scoped(.link);...@@ -2075,7 +2075,7 @@ const log = std.log.scoped(.link);
2075const macho = std.macho;2075const macho = std.macho;
2076const math = std.math;2076const math = std.math;
2077const mem = std.mem;2077const mem = std.mem;
2078const trace = @import("../tracy.zig").trace;2078const trace = @import("../../tracy.zig").trace;
2079const std = @import("std");2079const std = @import("std");
20802080
2081const Allocator = mem.Allocator;2081const Allocator = mem.Allocator;
...@@ -2088,6 +2088,5 @@ const LoadCommandIterator = macho.LoadCommandIterator;...@@ -2088,6 +2088,5 @@ const LoadCommandIterator = macho.LoadCommandIterator;
2088const MachO = @import("../MachO.zig");2088const MachO = @import("../MachO.zig");
2089const Object = @This();2089const Object = @This();
2090const Relocation = @import("Relocation.zig");2090const Relocation = @import("Relocation.zig");
2091const StringTable = @import("../strtab.zig").StringTable;
2092const Symbol = @import("Symbol.zig");2091const Symbol = @import("Symbol.zig");
2093const UnwindInfo = @import("UnwindInfo.zig");2092const UnwindInfo = @import("UnwindInfo.zig");
src/link/MachO/UnwindInfo.zig+1-1
...@@ -670,7 +670,7 @@ const log = std.log.scoped(.link);...@@ -670,7 +670,7 @@ const log = std.log.scoped(.link);
670const macho = std.macho;670const macho = std.macho;
671const math = std.math;671const math = std.math;
672const mem = std.mem;672const mem = std.mem;
673const trace = @import("../tracy.zig").trace;673const trace = @import("../../tracy.zig").trace;
674674
675const Allocator = mem.Allocator;675const Allocator = mem.Allocator;
676const Atom = @import("Atom.zig");676const Atom = @import("Atom.zig");
src/link/MachO/dead_strip.zig+1-1
...@@ -193,7 +193,7 @@ const log = std.log.scoped(.dead_strip);...@@ -193,7 +193,7 @@ const log = std.log.scoped(.dead_strip);
193const macho = std.macho;193const macho = std.macho;
194const math = std.math;194const math = std.math;
195const mem = std.mem;195const mem = std.mem;
196const trace = @import("../tracy.zig").trace;196const trace = @import("../../tracy.zig").trace;
197const track_live_log = std.log.scoped(.dead_strip_track_live);197const track_live_log = std.log.scoped(.dead_strip_track_live);
198const std = @import("std");198const std = @import("std");
199199
src/link/MachO/eh_frame.zig+1-1
...@@ -559,7 +559,7 @@ const macho = std.macho;...@@ -559,7 +559,7 @@ const macho = std.macho;
559const math = std.math;559const math = std.math;
560const mem = std.mem;560const mem = std.mem;
561const std = @import("std");561const std = @import("std");
562const trace = @import("../tracy.zig").trace;562const trace = @import("../../tracy.zig").trace;
563563
564const Allocator = std.mem.Allocator;564const Allocator = std.mem.Allocator;
565const Atom = @import("Atom.zig");565const Atom = @import("Atom.zig");
src/link/MachO/hasher.zig+1-1
...@@ -67,7 +67,7 @@ const assert = std.debug.assert;...@@ -67,7 +67,7 @@ const assert = std.debug.assert;
67const fs = std.fs;67const fs = std.fs;
68const mem = std.mem;68const mem = std.mem;
69const std = @import("std");69const std = @import("std");
70const trace = @import("../tracy.zig").trace;70const trace = @import("../../tracy.zig").trace;
7171
72const Allocator = mem.Allocator;72const Allocator = mem.Allocator;
73const ThreadPool = std.Thread.Pool;73const ThreadPool = std.Thread.Pool;
src/link/MachO/thunks.zig+2-2
...@@ -160,14 +160,14 @@ const max_distance = (1 << (jump_bits - 1));...@@ -160,14 +160,14 @@ const max_distance = (1 << (jump_bits - 1));
160/// and assume margin to be 5MiB.160/// and assume margin to be 5MiB.
161const max_allowed_distance = max_distance - 0x500_000;161const max_allowed_distance = max_distance - 0x500_000;
162162
163const aarch64 = @import("../aarch64.zig");163const aarch64 = @import("../../arch/aarch64/bits.zig");
164const assert = std.debug.assert;164const assert = std.debug.assert;
165const log = std.log.scoped(.link);165const log = std.log.scoped(.link);
166const macho = std.macho;166const macho = std.macho;
167const math = std.math;167const math = std.math;
168const mem = std.mem;168const mem = std.mem;
169const std = @import("std");169const std = @import("std");
170const trace = @import("../tracy.zig").trace;170const trace = @import("../../tracy.zig").trace;
171171
172const Allocator = mem.Allocator;172const Allocator = mem.Allocator;
173const Atom = @import("Atom.zig");173const Atom = @import("Atom.zig");
src/link/MachO/uuid.zig+1-1
...@@ -47,7 +47,7 @@ inline fn conform(out: *[Md5.digest_length]u8) void {...@@ -47,7 +47,7 @@ inline fn conform(out: *[Md5.digest_length]u8) void {
47const fs = std.fs;47const fs = std.fs;
48const mem = std.mem;48const mem = std.mem;
49const std = @import("std");49const std = @import("std");
50const trace = @import("../tracy.zig").trace;50const trace = @import("../../tracy.zig").trace;
5151
52const Allocator = mem.Allocator;52const Allocator = mem.Allocator;
53const Md5 = std.crypto.hash.Md5;53const Md5 = std.crypto.hash.Md5;