authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-10-10 10:33:15+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-10-13 16:17:10+02:00
logfc302f00a9de5de0490f4a66720e75946763c695
tree3ac9dfd61b29a6dbab7054abe6e71daf7f085e6c
parenta3104a4a78089f3260c0dd3f4a96012c6d73a63b

macho: redo relocation handling and lazy bind globals

* apply late symbol resolution for globals - instead of resolving the exact location of a symbol in locals, globals or undefs, we postpone the exact resolution until we have a full picture for relocation resolution. * fixup stubs to defined symbols - this is currently a hack rather than a final solution. I'll need to work out the details to make it more approachable. Currently, we preemptively create a stub for a lazy bound global and fix up stub offsets in stub helper routine if the global turns out to be undefined only. This is quite wasteful in terms of space as we create stub, stub helper and lazy ptr atoms but don't use them for defined globals. * change log scope to .link for macho. * remove redundant code paths from Object and Atom. * drastically simplify the contents of Relocation struct (i.e., it is now a simple superset of macho.relocation_info), clean up relocation parsing and resolution logic.

6 files changed, 857 insertions(+), 1219 deletions(-)

src/codegen.zig+28-17
...@@ -3018,7 +3018,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3018,7 +3018,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3018 }3018 }
3019 } else if (func_value.castTag(.extern_fn)) |func_payload| {3019 } else if (func_value.castTag(.extern_fn)) |func_payload| {
3020 const decl = func_payload.data;3020 const decl = func_payload.data;
3021 const resolv = try macho_file.addExternFn(mem.spanZ(decl.name));3021 const n_strx = try macho_file.addExternFn(mem.spanZ(decl.name));
3022 const offset = blk: {3022 const offset = blk: {
3023 switch (arch) {3023 switch (arch) {
3024 .x86_64 => {3024 .x86_64 => {
...@@ -3039,14 +3039,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3039,14 +3039,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3039 // Add relocation to the decl.3039 // Add relocation to the decl.
3040 try macho_file.active_decl.?.link.macho.relocs.append(self.bin_file.allocator, .{3040 try macho_file.active_decl.?.link.macho.relocs.append(self.bin_file.allocator, .{
3041 .offset = offset,3041 .offset = offset,
3042 .where = switch (resolv.where) {3042 .target = .{ .global = n_strx },
3043 .local => .local,3043 .addend = 0,
3044 .undef => .undef,3044 .subtractor = null,
3045 .pcrel = true,
3046 .length = 2,
3047 .@"type" = switch (arch) {
3048 .aarch64 => @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
3049 .x86_64 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
3050 else => unreachable,
3045 },3051 },
3046 .where_index = resolv.where_index,
3047 .payload = .{ .branch = .{
3048 .arch = arch,
3049 } },
3050 });3052 });
3051 } else {3053 } else {
3052 return self.fail("TODO implement calling bitcasted functions", .{});3054 return self.fail("TODO implement calling bitcasted functions", .{});
...@@ -4540,16 +4542,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4540,16 +4542,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4540 // Page reloc for adrp instruction.4542 // Page reloc for adrp instruction.
4541 try decl.link.macho.relocs.append(self.bin_file.allocator, .{4543 try decl.link.macho.relocs.append(self.bin_file.allocator, .{
4542 .offset = offset,4544 .offset = offset,
4543 .where = .local,4545 .target = .{ .local = @intCast(u32, addr) },
4544 .where_index = @intCast(u32, addr),4546 .addend = 0,
4545 .payload = .{ .page = .{ .kind = .got } },4547 .subtractor = null,
4548 .pcrel = true,
4549 .length = 2,
4550 .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGE21),
4546 });4551 });
4547 // Pageoff reloc for adrp instruction.4552 // Pageoff reloc for adrp instruction.
4548 try decl.link.macho.relocs.append(self.bin_file.allocator, .{4553 try decl.link.macho.relocs.append(self.bin_file.allocator, .{
4549 .offset = offset + 4,4554 .offset = offset + 4,
4550 .where = .local,4555 .target = .{ .local = @intCast(u32, addr) },
4551 .where_index = @intCast(u32, addr),4556 .addend = 0,
4552 .payload = .{ .page_off = .{ .kind = .got } },4557 .subtractor = null,
4558 .pcrel = false,
4559 .length = 2,
4560 .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGEOFF12),
4553 });4561 });
4554 } else {4562 } else {
4555 return self.fail("TODO implement genSetReg for PIE GOT indirection on this platform", .{});4563 return self.fail("TODO implement genSetReg for PIE GOT indirection on this platform", .{});
...@@ -4814,9 +4822,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4814,9 +4822,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4814 // Load reloc for LEA instruction.4822 // Load reloc for LEA instruction.
4815 try decl.link.macho.relocs.append(self.bin_file.allocator, .{4823 try decl.link.macho.relocs.append(self.bin_file.allocator, .{
4816 .offset = offset - 4,4824 .offset = offset - 4,
4817 .where = .local,4825 .target = .{ .local = @intCast(u32, x) },
4818 .where_index = @intCast(u32, x),4826 .addend = 0,
4819 .payload = .{ .load = .{ .kind = .got } },4827 .subtractor = null,
4828 .pcrel = true,
4829 .length = 2,
4830 .@"type" = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT),
4820 });4831 });
4821 } else {4832 } else {
4822 return self.fail("TODO implement genSetReg for PIE GOT indirection on this platform", .{});4833 return self.fail("TODO implement genSetReg for PIE GOT indirection on this platform", .{});
src/link/MachO.zig+239-239
...@@ -161,7 +161,7 @@ stub_helper_preamble_atom: ?*Atom = null,...@@ -161,7 +161,7 @@ stub_helper_preamble_atom: ?*Atom = null,
161strtab: std.ArrayListUnmanaged(u8) = .{},161strtab: std.ArrayListUnmanaged(u8) = .{},
162strtab_dir: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage) = .{},162strtab_dir: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage) = .{},
163163
164got_entries_map: std.AutoArrayHashMapUnmanaged(GotIndirectionKey, *Atom) = .{},164got_entries_map: std.AutoArrayHashMapUnmanaged(Atom.Relocation.Target, *Atom) = .{},
165stubs_map: std.AutoArrayHashMapUnmanaged(u32, *Atom) = .{},165stubs_map: std.AutoArrayHashMapUnmanaged(u32, *Atom) = .{},
166166
167error_flags: File.ErrorFlags = File.ErrorFlags{},167error_flags: File.ErrorFlags = File.ErrorFlags{},
...@@ -234,14 +234,6 @@ const SymbolWithLoc = struct {...@@ -234,14 +234,6 @@ const SymbolWithLoc = struct {
234 file: ?u16 = null, // null means Zig module234 file: ?u16 = null, // null means Zig module
235};235};
236236
237pub const GotIndirectionKey = struct {
238 where: enum {
239 local,
240 undef,
241 },
242 where_index: u32,
243};
244
245/// When allocating, the ideal_capacity is calculated by237/// When allocating, the ideal_capacity is calculated by
246/// actual_capacity + (actual_capacity / ideal_factor)238/// actual_capacity + (actual_capacity / ideal_factor)
247const ideal_factor = 4;239const ideal_factor = 4;
...@@ -842,26 +834,6 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {...@@ -842,26 +834,6 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {
842 try self.createDsoHandleAtom();834 try self.createDsoHandleAtom();
843 try self.addCodeSignatureLC();835 try self.addCodeSignatureLC();
844836
845 // log.warn("locals:", .{});
846 // for (self.locals.items) |sym, id| {
847 // log.warn(" {d}: {s}: {}", .{ id, self.getString(sym.n_strx), sym });
848 // }
849 // log.warn("globals:", .{});
850 // for (self.globals.items) |sym, id| {
851 // log.warn(" {d}: {s}: {}", .{ id, self.getString(sym.n_strx), sym });
852 // }
853 // log.warn("undefs:", .{});
854 // for (self.undefs.items) |sym, id| {
855 // log.warn(" {d}: {s}: {}", .{ id, self.getString(sym.n_strx), sym });
856 // }
857 // {
858 // log.warn("resolver:", .{});
859 // var it = self.symbol_resolver.iterator();
860 // while (it.next()) |entry| {
861 // log.warn(" {s} => {}", .{ self.getString(entry.key_ptr.*), entry.value_ptr.* });
862 // }
863 // }
864
865 for (self.unresolved.keys()) |index| {837 for (self.unresolved.keys()) |index| {
866 const sym = self.undefs.items[index];838 const sym = self.undefs.items[index];
867 const sym_name = self.getString(sym.n_strx);839 const sym_name = self.getString(sym.n_strx);
...@@ -879,6 +851,27 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {...@@ -879,6 +851,27 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {
879 try self.createTentativeDefAtoms();851 try self.createTentativeDefAtoms();
880 try self.parseObjectsIntoAtoms();852 try self.parseObjectsIntoAtoms();
881 try self.allocateGlobalSymbols();853 try self.allocateGlobalSymbols();
854
855 log.debug("locals:", .{});
856 for (self.locals.items) |sym, id| {
857 log.debug(" {d}: {s}: {}", .{ id, self.getString(sym.n_strx), sym });
858 }
859 log.debug("globals:", .{});
860 for (self.globals.items) |sym, id| {
861 log.debug(" {d}: {s}: {}", .{ id, self.getString(sym.n_strx), sym });
862 }
863 log.debug("undefs:", .{});
864 for (self.undefs.items) |sym, id| {
865 log.debug(" {d}: {s}: {}", .{ id, self.getString(sym.n_strx), sym });
866 }
867 {
868 log.debug("resolver:", .{});
869 var it = self.symbol_resolver.iterator();
870 while (it.next()) |entry| {
871 log.debug(" {s} => {}", .{ self.getString(entry.key_ptr.*), entry.value_ptr.* });
872 }
873 }
874
882 try self.writeAtoms();875 try self.writeAtoms();
883876
884 if (self.bss_section_index) |idx| {877 if (self.bss_section_index) |idx| {
...@@ -1866,7 +1859,7 @@ fn writeAtoms(self: *MachO) !void {...@@ -1866,7 +1859,7 @@ fn writeAtoms(self: *MachO) !void {
1866 }1859 }
1867}1860}
18681861
1869pub fn createGotAtom(self: *MachO, key: GotIndirectionKey) !*Atom {1862pub fn createGotAtom(self: *MachO, target: Atom.Relocation.Target) !*Atom {
1870 const local_sym_index = @intCast(u32, self.locals.items.len);1863 const local_sym_index = @intCast(u32, self.locals.items.len);
1871 try self.locals.append(self.base.allocator, .{1864 try self.locals.append(self.base.allocator, .{
1872 .n_strx = 0,1865 .n_strx = 0,
...@@ -1876,25 +1869,26 @@ pub fn createGotAtom(self: *MachO, key: GotIndirectionKey) !*Atom {...@@ -1876,25 +1869,26 @@ pub fn createGotAtom(self: *MachO, key: GotIndirectionKey) !*Atom {
1876 .n_value = 0,1869 .n_value = 0,
1877 });1870 });
1878 const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), 3);1871 const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), 3);
1879 switch (key.where) {1872 try atom.relocs.append(self.base.allocator, .{
1873 .offset = 0,
1874 .target = target,
1875 .addend = 0,
1876 .subtractor = null,
1877 .pcrel = false,
1878 .length = 3,
1879 .@"type" = switch (self.base.options.target.cpu.arch) {
1880 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
1881 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
1882 else => unreachable,
1883 },
1884 });
1885 switch (target) {
1880 .local => {1886 .local => {
1881 try atom.relocs.append(self.base.allocator, .{
1882 .offset = 0,
1883 .where = .local,
1884 .where_index = key.where_index,
1885 .payload = .{
1886 .unsigned = .{
1887 .subtractor = null,
1888 .addend = 0,
1889 .is_64bit = true,
1890 },
1891 },
1892 });
1893 try atom.rebases.append(self.base.allocator, 0);1887 try atom.rebases.append(self.base.allocator, 0);
1894 },1888 },
1895 .undef => {1889 .global => |n_strx| {
1896 try atom.bindings.append(self.base.allocator, .{1890 try atom.bindings.append(self.base.allocator, .{
1897 .local_sym_index = key.where_index,1891 .n_strx = n_strx,
1898 .offset = 0,1892 .offset = 0,
1899 });1893 });
1900 },1894 },
...@@ -1958,14 +1952,12 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -1958,14 +1952,12 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
1958 atom.code.items[2] = 0x1d;1952 atom.code.items[2] = 0x1d;
1959 atom.relocs.appendAssumeCapacity(.{1953 atom.relocs.appendAssumeCapacity(.{
1960 .offset = 3,1954 .offset = 3,
1961 .where = .local,1955 .target = .{ .local = dyld_private_sym_index },
1962 .where_index = dyld_private_sym_index,1956 .addend = 0,
1963 .payload = .{1957 .subtractor = null,
1964 .signed = .{1958 .pcrel = true,
1965 .addend = 0,1959 .length = 2,
1966 .correction = 0,1960 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
1967 },
1968 },
1969 });1961 });
1970 // push %r111962 // push %r11
1971 atom.code.items[7] = 0x41;1963 atom.code.items[7] = 0x41;
...@@ -1975,14 +1967,12 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -1975,14 +1967,12 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
1975 atom.code.items[10] = 0x25;1967 atom.code.items[10] = 0x25;
1976 atom.relocs.appendAssumeCapacity(.{1968 atom.relocs.appendAssumeCapacity(.{
1977 .offset = 11,1969 .offset = 11,
1978 .where = .undef,1970 .target = .{ .global = self.undefs.items[self.dyld_stub_binder_index.?].n_strx },
1979 .where_index = self.dyld_stub_binder_index.?,1971 .addend = 0,
1980 .payload = .{1972 .subtractor = null,
1981 .load = .{1973 .pcrel = true,
1982 .kind = .got,1974 .length = 2,
1983 .addend = 0,1975 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_GOT),
1984 },
1985 },
1986 });1976 });
1987 },1977 },
1988 .aarch64 => {1978 .aarch64 => {
...@@ -1991,28 +1981,23 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -1991,28 +1981,23 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
1991 mem.writeIntLittle(u32, atom.code.items[0..][0..4], aarch64.Instruction.adrp(.x17, 0).toU32());1981 mem.writeIntLittle(u32, atom.code.items[0..][0..4], aarch64.Instruction.adrp(.x17, 0).toU32());
1992 atom.relocs.appendAssumeCapacity(.{1982 atom.relocs.appendAssumeCapacity(.{
1993 .offset = 0,1983 .offset = 0,
1994 .where = .local,1984 .target = .{ .local = dyld_private_sym_index },
1995 .where_index = dyld_private_sym_index,1985 .addend = 0,
1996 .payload = .{1986 .subtractor = null,
1997 .page = .{1987 .pcrel = true,
1998 .kind = .page,1988 .length = 2,
1999 .addend = 0,1989 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
2000 },
2001 },
2002 });1990 });
2003 // add x17, x17, 01991 // add x17, x17, 0
2004 mem.writeIntLittle(u32, atom.code.items[4..][0..4], aarch64.Instruction.add(.x17, .x17, 0, false).toU32());1992 mem.writeIntLittle(u32, atom.code.items[4..][0..4], aarch64.Instruction.add(.x17, .x17, 0, false).toU32());
2005 atom.relocs.appendAssumeCapacity(.{1993 atom.relocs.appendAssumeCapacity(.{
2006 .offset = 4,1994 .offset = 4,
2007 .where = .local,1995 .target = .{ .local = dyld_private_sym_index },
2008 .where_index = dyld_private_sym_index,1996 .addend = 0,
2009 .payload = .{1997 .subtractor = null,
2010 .page_off = .{1998 .pcrel = false,
2011 .kind = .page,1999 .length = 2,
2012 .addend = 0,2000 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGEOFF12),
2013 .op_kind = .arithmetic,
2014 },
2015 },
2016 });2001 });
2017 // stp x16, x17, [sp, #-16]!2002 // stp x16, x17, [sp, #-16]!
2018 mem.writeIntLittle(u32, atom.code.items[8..][0..4], aarch64.Instruction.stp(2003 mem.writeIntLittle(u32, atom.code.items[8..][0..4], aarch64.Instruction.stp(
...@@ -2025,14 +2010,12 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -2025,14 +2010,12 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
2025 mem.writeIntLittle(u32, atom.code.items[12..][0..4], aarch64.Instruction.adrp(.x16, 0).toU32());2010 mem.writeIntLittle(u32, atom.code.items[12..][0..4], aarch64.Instruction.adrp(.x16, 0).toU32());
2026 atom.relocs.appendAssumeCapacity(.{2011 atom.relocs.appendAssumeCapacity(.{
2027 .offset = 12,2012 .offset = 12,
2028 .where = .undef,2013 .target = .{ .global = self.undefs.items[self.dyld_stub_binder_index.?].n_strx },
2029 .where_index = self.dyld_stub_binder_index.?,2014 .addend = 0,
2030 .payload = .{2015 .subtractor = null,
2031 .page = .{2016 .pcrel = true,
2032 .kind = .got,2017 .length = 2,
2033 .addend = 0,2018 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGE21),
2034 },
2035 },
2036 });2019 });
2037 // ldr x16, [x16, 0]2020 // ldr x16, [x16, 0]
2038 mem.writeIntLittle(u32, atom.code.items[16..][0..4], aarch64.Instruction.ldr(.x16, .{2021 mem.writeIntLittle(u32, atom.code.items[16..][0..4], aarch64.Instruction.ldr(.x16, .{
...@@ -2043,14 +2026,12 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -2043,14 +2026,12 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
2043 }).toU32());2026 }).toU32());
2044 atom.relocs.appendAssumeCapacity(.{2027 atom.relocs.appendAssumeCapacity(.{
2045 .offset = 16,2028 .offset = 16,
2046 .where = .undef,2029 .target = .{ .global = self.undefs.items[self.dyld_stub_binder_index.?].n_strx },
2047 .where_index = self.dyld_stub_binder_index.?,2030 .addend = 0,
2048 .payload = .{2031 .subtractor = null,
2049 .page_off = .{2032 .pcrel = false,
2050 .kind = .got,2033 .length = 2,
2051 .addend = 0,2034 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGEOFF12),
2052 },
2053 },
2054 });2035 });
2055 // br x162036 // br x16
2056 mem.writeIntLittle(u32, atom.code.items[20..][0..4], aarch64.Instruction.br(.x16).toU32());2037 mem.writeIntLittle(u32, atom.code.items[20..][0..4], aarch64.Instruction.br(.x16).toU32());
...@@ -2101,11 +2082,12 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -2101,11 +2082,12 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
2101 atom.code.items[5] = 0xe9;2082 atom.code.items[5] = 0xe9;
2102 atom.relocs.appendAssumeCapacity(.{2083 atom.relocs.appendAssumeCapacity(.{
2103 .offset = 6,2084 .offset = 6,
2104 .where = .local,2085 .target = .{ .local = self.stub_helper_preamble_atom.?.local_sym_index },
2105 .where_index = self.stub_helper_preamble_atom.?.local_sym_index,2086 .addend = 0,
2106 .payload = .{2087 .subtractor = null,
2107 .branch = .{ .arch = arch },2088 .pcrel = true,
2108 },2089 .length = 2,
2090 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
2109 });2091 });
2110 },2092 },
2111 .aarch64 => {2093 .aarch64 => {
...@@ -2121,11 +2103,12 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -2121,11 +2103,12 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
2121 mem.writeIntLittle(u32, atom.code.items[4..8], aarch64.Instruction.b(0).toU32());2103 mem.writeIntLittle(u32, atom.code.items[4..8], aarch64.Instruction.b(0).toU32());
2122 atom.relocs.appendAssumeCapacity(.{2104 atom.relocs.appendAssumeCapacity(.{
2123 .offset = 4,2105 .offset = 4,
2124 .where = .local,2106 .target = .{ .local = self.stub_helper_preamble_atom.?.local_sym_index },
2125 .where_index = self.stub_helper_preamble_atom.?.local_sym_index,2107 .addend = 0,
2126 .payload = .{2108 .subtractor = null,
2127 .branch = .{ .arch = arch },2109 .pcrel = true,
2128 },2110 .length = 2,
2111 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
2129 });2112 });
2130 // Next 4 bytes 8..12 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.2113 // Next 4 bytes 8..12 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
2131 },2114 },
...@@ -2135,7 +2118,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -2135,7 +2118,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
2135 return atom;2118 return atom;
2136}2119}
21372120
2138pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, lazy_binding_sym_index: u32) !*Atom {2121pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, n_strx: u32) !*Atom {
2139 const local_sym_index = @intCast(u32, self.locals.items.len);2122 const local_sym_index = @intCast(u32, self.locals.items.len);
2140 try self.locals.append(self.base.allocator, .{2123 try self.locals.append(self.base.allocator, .{
2141 .n_strx = 0,2124 .n_strx = 0,
...@@ -2147,19 +2130,20 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, lazy_binding_sym...@@ -2147,19 +2130,20 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, lazy_binding_sym
2147 const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), 3);2130 const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), 3);
2148 try atom.relocs.append(self.base.allocator, .{2131 try atom.relocs.append(self.base.allocator, .{
2149 .offset = 0,2132 .offset = 0,
2150 .where = .local,2133 .target = .{ .local = stub_sym_index },
2151 .where_index = stub_sym_index,2134 .addend = 0,
2152 .payload = .{2135 .subtractor = null,
2153 .unsigned = .{2136 .pcrel = false,
2154 .subtractor = null,2137 .length = 3,
2155 .addend = 0,2138 .@"type" = switch (self.base.options.target.cpu.arch) {
2156 .is_64bit = true,2139 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
2157 },2140 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
2141 else => unreachable,
2158 },2142 },
2159 });2143 });
2160 try atom.rebases.append(self.base.allocator, 0);2144 try atom.rebases.append(self.base.allocator, 0);
2161 try atom.lazy_bindings.append(self.base.allocator, .{2145 try atom.lazy_bindings.append(self.base.allocator, .{
2162 .local_sym_index = lazy_binding_sym_index,2146 .n_strx = n_strx,
2163 .offset = 0,2147 .offset = 0,
2164 });2148 });
2165 return atom;2149 return atom;
...@@ -2193,11 +2177,12 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -2193,11 +2177,12 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
2193 atom.code.items[1] = 0x25;2177 atom.code.items[1] = 0x25;
2194 try atom.relocs.append(self.base.allocator, .{2178 try atom.relocs.append(self.base.allocator, .{
2195 .offset = 2,2179 .offset = 2,
2196 .where = .local,2180 .target = .{ .local = laptr_sym_index },
2197 .where_index = laptr_sym_index,2181 .addend = 0,
2198 .payload = .{2182 .subtractor = null,
2199 .branch = .{ .arch = arch },2183 .pcrel = true,
2200 },2184 .length = 2,
2185 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
2201 });2186 });
2202 },2187 },
2203 .aarch64 => {2188 .aarch64 => {
...@@ -2206,14 +2191,12 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -2206,14 +2191,12 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
2206 mem.writeIntLittle(u32, atom.code.items[0..4], aarch64.Instruction.adrp(.x16, 0).toU32());2191 mem.writeIntLittle(u32, atom.code.items[0..4], aarch64.Instruction.adrp(.x16, 0).toU32());
2207 atom.relocs.appendAssumeCapacity(.{2192 atom.relocs.appendAssumeCapacity(.{
2208 .offset = 0,2193 .offset = 0,
2209 .where = .local,2194 .target = .{ .local = laptr_sym_index },
2210 .where_index = laptr_sym_index,2195 .addend = 0,
2211 .payload = .{2196 .subtractor = null,
2212 .page = .{2197 .pcrel = true,
2213 .kind = .page,2198 .length = 2,
2214 .addend = 0,2199 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
2215 },
2216 },
2217 });2200 });
2218 // ldr x16, x16, offset2201 // ldr x16, x16, offset
2219 mem.writeIntLittle(u32, atom.code.items[4..8], aarch64.Instruction.ldr(.x16, .{2202 mem.writeIntLittle(u32, atom.code.items[4..8], aarch64.Instruction.ldr(.x16, .{
...@@ -2224,15 +2207,12 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -2224,15 +2207,12 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
2224 }).toU32());2207 }).toU32());
2225 atom.relocs.appendAssumeCapacity(.{2208 atom.relocs.appendAssumeCapacity(.{
2226 .offset = 4,2209 .offset = 4,
2227 .where = .local,2210 .target = .{ .local = laptr_sym_index },
2228 .where_index = laptr_sym_index,2211 .addend = 0,
2229 .payload = .{2212 .subtractor = null,
2230 .page_off = .{2213 .pcrel = false,
2231 .kind = .page,2214 .length = 2,
2232 .addend = 0,2215 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGEOFF12),
2233 .op_kind = .load,
2234 },
2235 },
2236 });2216 });
2237 // br x162217 // br x16
2238 mem.writeIntLittle(u32, atom.code.items[8..12], aarch64.Instruction.br(.x16).toU32());2218 mem.writeIntLittle(u32, atom.code.items[8..12], aarch64.Instruction.br(.x16).toU32());
...@@ -2578,7 +2558,7 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {...@@ -2578,7 +2558,7 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
2578 .none => {},2558 .none => {},
2579 .got => return error.TODOGotHint,2559 .got => return error.TODOGotHint,
2580 .stub => {2560 .stub => {
2581 if (self.stubs_map.contains(resolv.where_index)) break :outer_blk;2561 if (self.stubs_map.contains(sym.n_strx)) break :outer_blk;
2582 const stub_helper_atom = blk: {2562 const stub_helper_atom = blk: {
2583 const match = MatchingSection{2563 const match = MatchingSection{
2584 .seg = self.text_segment_cmd_index.?,2564 .seg = self.text_segment_cmd_index.?,
...@@ -2599,7 +2579,7 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {...@@ -2599,7 +2579,7 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
2599 };2579 };
2600 const atom = try self.createLazyPointerAtom(2580 const atom = try self.createLazyPointerAtom(
2601 stub_helper_atom.local_sym_index,2581 stub_helper_atom.local_sym_index,
2602 resolv.where_index,2582 sym.n_strx,
2603 );2583 );
2604 const atom_sym = &self.locals.items[atom.local_sym_index];2584 const atom_sym = &self.locals.items[atom.local_sym_index];
2605 const alignment = try math.powi(u32, 2, atom.alignment);2585 const alignment = try math.powi(u32, 2, atom.alignment);
...@@ -2621,7 +2601,7 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {...@@ -2621,7 +2601,7 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
2621 atom_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);2601 atom_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
2622 break :blk atom;2602 break :blk atom;
2623 };2603 };
2624 try self.stubs_map.putNoClobber(self.base.allocator, resolv.where_index, stub_atom);2604 try self.stubs_map.putNoClobber(self.base.allocator, sym.n_strx, stub_atom);
2625 },2605 },
2626 }2606 }
2627 }2607 }
...@@ -2675,12 +2655,9 @@ fn resolveDyldStubBinder(self: *MachO) !void {...@@ -2675,12 +2655,9 @@ fn resolveDyldStubBinder(self: *MachO) !void {
2675 }2655 }
26762656
2677 // Add dyld_stub_binder as the final GOT entry.2657 // Add dyld_stub_binder as the final GOT entry.
2678 const got_entry = GotIndirectionKey{2658 const target = Atom.Relocation.Target{ .global = n_strx };
2679 .where = .undef,2659 const atom = try self.createGotAtom(target);
2680 .where_index = self.dyld_stub_binder_index.?,2660 try self.got_entries_map.putNoClobber(self.base.allocator, target, atom);
2681 };
2682 const atom = try self.createGotAtom(got_entry);
2683 try self.got_entries_map.putNoClobber(self.base.allocator, got_entry, atom);
2684 const match = MatchingSection{2661 const match = MatchingSection{
2685 .seg = self.data_const_segment_cmd_index.?,2662 .seg = self.data_const_segment_cmd_index.?,
2686 .sect = self.got_section_index.?,2663 .sect = self.got_section_index.?,
...@@ -3081,12 +3058,9 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {...@@ -3081,12 +3058,9 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
3081 };3058 };
30823059
3083 // TODO try popping from free list first before allocating a new GOT atom.3060 // TODO try popping from free list first before allocating a new GOT atom.
3084 const key = GotIndirectionKey{3061 const target = Atom.Relocation.Target{ .local = decl.link.macho.local_sym_index };
3085 .where = .local,3062 const got_atom = try self.createGotAtom(target);
3086 .where_index = decl.link.macho.local_sym_index,3063 try self.got_entries_map.put(self.base.allocator, target, got_atom);
3087 };
3088 const got_atom = try self.createGotAtom(key);
3089 try self.got_entries_map.put(self.base.allocator, key, got_atom);
3090}3064}
30913065
3092pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {3066pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
...@@ -3267,10 +3241,7 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64...@@ -3267,10 +3241,7 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64
32673241
3268 if (vaddr != symbol.n_value) {3242 if (vaddr != symbol.n_value) {
3269 log.debug(" (writing new GOT entry)", .{});3243 log.debug(" (writing new GOT entry)", .{});
3270 const got_atom = self.got_entries_map.get(.{3244 const got_atom = self.got_entries_map.get(.{ .local = decl.link.macho.local_sym_index }).?;
3271 .where = .local,
3272 .where_index = decl.link.macho.local_sym_index,
3273 }) orelse unreachable;
3274 const got_sym = &self.locals.items[got_atom.local_sym_index];3245 const got_sym = &self.locals.items[got_atom.local_sym_index];
3275 const got_vaddr = try self.allocateAtom(got_atom, @sizeOf(u64), 8, .{3246 const got_vaddr = try self.allocateAtom(got_atom, @sizeOf(u64), 8, .{
3276 .seg = self.data_const_segment_cmd_index.?,3247 .seg = self.data_const_segment_cmd_index.?,
...@@ -3324,10 +3295,7 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64...@@ -3324,10 +3295,7 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64
3324 .n_desc = 0,3295 .n_desc = 0,
3325 .n_value = addr,3296 .n_value = addr,
3326 };3297 };
3327 const got_atom = self.got_entries_map.get(.{3298 const got_atom = self.got_entries_map.get(.{ .local = decl.link.macho.local_sym_index }).?;
3328 .where = .local,
3329 .where_index = decl.link.macho.local_sym_index,
3330 }) orelse unreachable;
3331 const got_sym = &self.locals.items[got_atom.local_sym_index];3299 const got_sym = &self.locals.items[got_atom.local_sym_index];
3332 const vaddr = try self.allocateAtom(got_atom, @sizeOf(u64), 8, .{3300 const vaddr = try self.allocateAtom(got_atom, @sizeOf(u64), 8, .{
3333 .seg = self.data_const_segment_cmd_index.?,3301 .seg = self.data_const_segment_cmd_index.?,
...@@ -4357,51 +4325,29 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, m...@@ -4357,51 +4325,29 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, m
4357 return vaddr;4325 return vaddr;
4358}4326}
43594327
4360const AddExternFnRes = struct {4328pub fn addExternFn(self: *MachO, name: []const u8) !u32 {
4361 where: enum {
4362 local,
4363 undef,
4364 },
4365 where_index: u32,
4366};
4367
4368pub fn addExternFn(self: *MachO, name: []const u8) !AddExternFnRes {
4369 const sym_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{name});4329 const sym_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{name});
4370 defer self.base.allocator.free(sym_name);4330 defer self.base.allocator.free(sym_name);
4371 const n_strx = try self.makeString(sym_name);4331 const n_strx = try self.makeString(sym_name);
43724332
4373 if (self.symbol_resolver.get(n_strx)) |resolv| {4333 if (!self.symbol_resolver.contains(n_strx)) {
4374 return switch (resolv.where) {4334 log.debug("adding new extern function '{s}'", .{sym_name});
4375 .global => AddExternFnRes{4335 const sym_index = @intCast(u32, self.undefs.items.len);
4376 .where = .local,4336 try self.undefs.append(self.base.allocator, .{
4377 .where_index = resolv.local_sym_index,4337 .n_strx = n_strx,
4378 },4338 .n_type = macho.N_UNDF,
4379 .undef => AddExternFnRes{4339 .n_sect = 0,
4380 .where = .undef,4340 .n_desc = 0,
4381 .where_index = resolv.where_index,4341 .n_value = 0,
4382 },4342 });
4383 };4343 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{
4344 .where = .undef,
4345 .where_index = sym_index,
4346 });
4347 try self.unresolved.putNoClobber(self.base.allocator, sym_index, .stub);
4384 }4348 }
43854349
4386 log.debug("adding new extern function '{s}'", .{sym_name});4350 return n_strx;
4387 const sym_index = @intCast(u32, self.undefs.items.len);
4388 try self.undefs.append(self.base.allocator, .{
4389 .n_strx = n_strx,
4390 .n_type = macho.N_UNDF,
4391 .n_sect = 0,
4392 .n_desc = 0,
4393 .n_value = 0,
4394 });
4395 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{
4396 .where = .undef,
4397 .where_index = sym_index,
4398 });
4399 try self.unresolved.putNoClobber(self.base.allocator, sym_index, .stub);
4400
4401 return AddExternFnRes{
4402 .where = .undef,
4403 .where_index = sym_index,
4404 };
4405}4351}
44064352
4407const NextSegmentAddressAndOffset = struct {4353const NextSegmentAddressAndOffset = struct {
...@@ -4496,23 +4442,47 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -4496,23 +4442,47 @@ fn writeDyldInfoData(self: *MachO) !void {
4496 }4442 }
44974443
4498 for (atom.bindings.items) |binding| {4444 for (atom.bindings.items) |binding| {
4499 const bind_sym = self.undefs.items[binding.local_sym_index];4445 const resolv = self.symbol_resolver.get(binding.n_strx).?;
4500 try bind_pointers.append(.{4446 switch (resolv.where) {
4501 .offset = binding.offset + base_offset,4447 .global => {
4502 .segment_id = match.seg,4448 // Turn into a rebase.
4503 .dylib_ordinal = @divExact(bind_sym.n_desc, macho.N_SYMBOL_RESOLVER),4449 try rebase_pointers.append(.{
4504 .name = self.getString(bind_sym.n_strx),4450 .offset = base_offset + binding.offset,
4505 });4451 .segment_id = match.seg,
4452 });
4453 },
4454 .undef => {
4455 const bind_sym = self.undefs.items[resolv.where_index];
4456 try bind_pointers.append(.{
4457 .offset = binding.offset + base_offset,
4458 .segment_id = match.seg,
4459 .dylib_ordinal = @divExact(bind_sym.n_desc, macho.N_SYMBOL_RESOLVER),
4460 .name = self.getString(bind_sym.n_strx),
4461 });
4462 },
4463 }
4506 }4464 }
45074465
4508 for (atom.lazy_bindings.items) |binding| {4466 for (atom.lazy_bindings.items) |binding| {
4509 const bind_sym = self.undefs.items[binding.local_sym_index];4467 const resolv = self.symbol_resolver.get(binding.n_strx).?;
4510 try lazy_bind_pointers.append(.{4468 switch (resolv.where) {
4511 .offset = binding.offset + base_offset,4469 .global => {
4512 .segment_id = match.seg,4470 // Turn into a rebase.
4513 .dylib_ordinal = @divExact(bind_sym.n_desc, macho.N_SYMBOL_RESOLVER),4471 try rebase_pointers.append(.{
4514 .name = self.getString(bind_sym.n_strx),4472 .offset = base_offset + binding.offset,
4515 });4473 .segment_id = match.seg,
4474 });
4475 },
4476 .undef => {
4477 const bind_sym = self.undefs.items[resolv.where_index];
4478 try lazy_bind_pointers.append(.{
4479 .offset = binding.offset + base_offset,
4480 .segment_id = match.seg,
4481 .dylib_ordinal = @divExact(bind_sym.n_desc, macho.N_SYMBOL_RESOLVER),
4482 .name = self.getString(bind_sym.n_strx),
4483 });
4484 },
4485 }
4516 }4486 }
45174487
4518 if (atom.prev) |prev| {4488 if (atom.prev) |prev| {
...@@ -4607,19 +4577,37 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -4607,19 +4577,37 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
4607 }) orelse return;4577 }) orelse return;
4608 if (last_atom == self.stub_helper_preamble_atom.?) return;4578 if (last_atom == self.stub_helper_preamble_atom.?) return;
46094579
4610 // Because we insert lazy binding opcodes in reverse order (from last to the first atom),4580 var table = std.AutoHashMap(i64, *Atom).init(self.base.allocator);
4611 // we need reverse the order of atom traversal here as well.4581 defer table.deinit();
4612 // TODO figure out a less error prone mechanisms for this!4582
4613 var atom = last_atom;4583 {
4614 while (atom.prev) |prev| {4584 var stub_atom = last_atom;
4615 atom = prev;4585 var laptr_atom = self.atoms.get(.{
4586 .seg = self.data_segment_cmd_index.?,
4587 .sect = self.la_symbol_ptr_section_index.?,
4588 }).?;
4589 const base_addr = blk: {
4590 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
4591 break :blk seg.inner.vmaddr;
4592 };
4593
4594 while (true) {
4595 const laptr_off = blk: {
4596 const sym = self.locals.items[laptr_atom.local_sym_index];
4597 break :blk @intCast(i64, sym.n_value - base_addr);
4598 };
4599 try table.putNoClobber(laptr_off, stub_atom);
4600 if (laptr_atom.prev) |prev| {
4601 laptr_atom = prev;
4602 stub_atom = stub_atom.prev.?;
4603 } else break;
4604 }
4616 }4605 }
4617 atom = atom.next.?;
46184606
4619 var stream = std.io.fixedBufferStream(buffer);4607 var stream = std.io.fixedBufferStream(buffer);
4620 var reader = stream.reader();4608 var reader = stream.reader();
4621 var offsets = std.ArrayList(u32).init(self.base.allocator);4609 var offsets = std.ArrayList(struct { sym_offset: i64, offset: u32 }).init(self.base.allocator);
4622 try offsets.append(0);4610 try offsets.append(.{ .sym_offset = undefined, .offset = 0 });
4623 defer offsets.deinit();4611 defer offsets.deinit();
4624 var valid_block = false;4612 var valid_block = false;
46254613
...@@ -4637,7 +4625,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -4637,7 +4625,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
4637 macho.BIND_OPCODE_DONE => {4625 macho.BIND_OPCODE_DONE => {
4638 if (valid_block) {4626 if (valid_block) {
4639 const offset = try stream.getPos();4627 const offset = try stream.getPos();
4640 try offsets.append(@intCast(u32, offset));4628 try offsets.append(.{ .sym_offset = undefined, .offset = @intCast(u32, offset) });
4641 }4629 }
4642 valid_block = false;4630 valid_block = false;
4643 },4631 },
...@@ -4648,7 +4636,9 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -4648,7 +4636,9 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
4648 }4636 }
4649 },4637 },
4650 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {4638 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
4651 _ = try std.leb.readULEB128(u64, reader);4639 var inserted = offsets.pop();
4640 inserted.sym_offset = try std.leb.readILEB128(i64, reader);
4641 try offsets.append(inserted);
4652 },4642 },
4653 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {4643 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
4654 _ = try std.leb.readULEB128(u64, reader);4644 _ = try std.leb.readULEB128(u64, reader);
...@@ -4660,8 +4650,10 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -4660,8 +4650,10 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
4660 }4650 }
4661 }4651 }
46624652
4663 const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;4653 const sect = blk: {
4664 const sect = seg.sections.items[self.stub_helper_section_index.?];4654 const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
4655 break :blk seg.sections.items[self.stub_helper_section_index.?];
4656 };
4665 const stub_offset: u4 = switch (self.base.options.target.cpu.arch) {4657 const stub_offset: u4 = switch (self.base.options.target.cpu.arch) {
4666 .x86_64 => 1,4658 .x86_64 => 1,
4667 .aarch64 => 2 * @sizeOf(u32),4659 .aarch64 => 2 * @sizeOf(u32),
...@@ -4669,20 +4661,18 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -4669,20 +4661,18 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
4669 };4661 };
4670 var buf: [@sizeOf(u32)]u8 = undefined;4662 var buf: [@sizeOf(u32)]u8 = undefined;
4671 _ = offsets.pop();4663 _ = offsets.pop();
4664
4672 while (offsets.popOrNull()) |bind_offset| {4665 while (offsets.popOrNull()) |bind_offset| {
4666 const atom = table.get(bind_offset.sym_offset).?;
4673 const sym = self.locals.items[atom.local_sym_index];4667 const sym = self.locals.items[atom.local_sym_index];
4674 const file_offset = sect.offset + sym.n_value - sect.addr + stub_offset;4668 const file_offset = sect.offset + sym.n_value - sect.addr + stub_offset;
4675 mem.writeIntLittle(u32, &buf, bind_offset);4669 mem.writeIntLittle(u32, &buf, bind_offset.offset);
4676 log.debug("writing lazy bind offset in stub helper of 0x{x} for symbol {s} at offset 0x{x}", .{4670 log.debug("writing lazy bind offset in stub helper of 0x{x} for symbol {s} at offset 0x{x}", .{
4677 bind_offset,4671 bind_offset.offset,
4678 self.getString(sym.n_strx),4672 self.getString(sym.n_strx),
4679 file_offset,4673 file_offset,
4680 });4674 });
4681 try self.base.file.?.pwriteAll(&buf, file_offset);4675 try self.base.file.?.pwriteAll(&buf, file_offset);
4682
4683 if (atom.next) |next| {
4684 atom = next;
4685 } else break;
4686 }4676 }
4687}4677}
46884678
...@@ -4875,24 +4865,34 @@ fn writeSymbolTable(self: *MachO) !void {...@@ -4875,24 +4865,34 @@ fn writeSymbolTable(self: *MachO) !void {
48754865
4876 stubs.reserved1 = 0;4866 stubs.reserved1 = 0;
4877 for (self.stubs_map.keys()) |key| {4867 for (self.stubs_map.keys()) |key| {
4878 try writer.writeIntLittle(u32, dysymtab.iundefsym + key);4868 const resolv = self.symbol_resolver.get(key).?;
4869 switch (resolv.where) {
4870 .global => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL),
4871 .undef => try writer.writeIntLittle(u32, dysymtab.iundefsym + resolv.where_index),
4872 }
4879 }4873 }
48804874
4881 got.reserved1 = nstubs;4875 got.reserved1 = nstubs;
4882 for (self.got_entries_map.keys()) |key| {4876 for (self.got_entries_map.keys()) |key| {
4883 switch (key.where) {4877 switch (key) {
4884 .undef => {4878 .local => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL),
4885 try writer.writeIntLittle(u32, dysymtab.iundefsym + key.where_index);4879 .global => |n_strx| {
4886 },4880 const resolv = self.symbol_resolver.get(n_strx).?;
4887 .local => {4881 switch (resolv.where) {
4888 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);4882 .global => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL),
4883 .undef => try writer.writeIntLittle(u32, dysymtab.iundefsym + resolv.where_index),
4884 }
4889 },4885 },
4890 }4886 }
4891 }4887 }
48924888
4893 la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries;4889 la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries;
4894 for (self.stubs_map.keys()) |key| {4890 for (self.stubs_map.keys()) |key| {
4895 try writer.writeIntLittle(u32, dysymtab.iundefsym + key);4891 const resolv = self.symbol_resolver.get(key).?;
4892 switch (resolv.where) {
4893 .global => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL),
4894 .undef => try writer.writeIntLittle(u32, dysymtab.iundefsym + resolv.where_index),
4895 }
4896 }4896 }
48974897
4898 try self.base.file.?.pwriteAll(buf, dysymtab.indirectsymoff);4898 try self.base.file.?.pwriteAll(buf, dysymtab.indirectsymoff);
src/link/MachO/Archive.zig+1-1
...@@ -3,7 +3,7 @@ const Archive = @This();...@@ -3,7 +3,7 @@ const Archive = @This();
3const std = @import("std");3const std = @import("std");
4const assert = std.debug.assert;4const assert = std.debug.assert;
5const fs = std.fs;5const fs = std.fs;
6const log = std.log.scoped(.archive);6const log = std.log.scoped(.link);
7const macho = std.macho;7const macho = std.macho;
8const mem = std.mem;8const mem = std.mem;
9const fat = @import("fat.zig");9const fat = @import("fat.zig");
src/link/MachO/Atom.zig+580-958
...@@ -5,7 +5,7 @@ const build_options = @import("build_options");...@@ -5,7 +5,7 @@ const build_options = @import("build_options");
5const aarch64 = @import("../../arch/aarch64/bits.zig");5const aarch64 = @import("../../arch/aarch64/bits.zig");
6const assert = std.debug.assert;6const assert = std.debug.assert;
7const commands = @import("commands.zig");7const commands = @import("commands.zig");
8const log = std.log.scoped(.text_block);8const log = std.log.scoped(.link);
9const macho = std.macho;9const macho = std.macho;
10const math = std.math;10const math = std.math;
11const mem = std.mem;11const mem = std.mem;
...@@ -54,10 +54,10 @@ rebases: std.ArrayListUnmanaged(u64) = .{},...@@ -54,10 +54,10 @@ rebases: std.ArrayListUnmanaged(u64) = .{},
54/// List of offsets contained within this atom that will be dynamically bound54/// List of offsets contained within this atom that will be dynamically bound
55/// by the dynamic loader and contain pointers to resolved (at load time) extern55/// by the dynamic loader and contain pointers to resolved (at load time) extern
56/// symbols (aka proxies aka imports)56/// symbols (aka proxies aka imports)
57bindings: std.ArrayListUnmanaged(SymbolAtOffset) = .{},57bindings: std.ArrayListUnmanaged(Binding) = .{},
5858
59/// List of lazy bindings59/// List of lazy bindings
60lazy_bindings: std.ArrayListUnmanaged(SymbolAtOffset) = .{},60lazy_bindings: std.ArrayListUnmanaged(Binding) = .{},
6161
62/// List of data-in-code entries. This is currently specific to x86_64 only.62/// List of data-in-code entries. This is currently specific to x86_64 only.
63dices: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},63dices: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
...@@ -83,25 +83,15 @@ dbg_info_len: u32,...@@ -83,25 +83,15 @@ dbg_info_len: u32,
8383
84dirty: bool = true,84dirty: bool = true,
8585
86pub const Binding = struct {
87 n_strx: u32,
88 offset: u64,
89};
90
86pub const SymbolAtOffset = struct {91pub const SymbolAtOffset = struct {
87 local_sym_index: u32,92 local_sym_index: u32,
88 offset: u64,93 offset: u64,
89 stab: ?Stab = null,94 stab: ?Stab = null,
90
91 pub fn format(
92 self: SymbolAtOffset,
93 comptime fmt: []const u8,
94 options: std.fmt.FormatOptions,
95 writer: anytype,
96 ) !void {
97 _ = fmt;
98 _ = options;
99 try std.fmt.format(writer, "{{ {d}: .offset = {d}", .{ self.local_sym_index, self.offset });
100 if (self.stab) |stab| {
101 try std.fmt.format(writer, ", .stab = {any}", .{stab});
102 }
103 try std.fmt.format(writer, " }}", .{});
104 }
105};95};
10696
107pub const Stab = union(enum) {97pub const Stab = union(enum) {
...@@ -171,411 +161,26 @@ pub const Stab = union(enum) {...@@ -171,411 +161,26 @@ pub const Stab = union(enum) {
171};161};
172162
173pub const Relocation = struct {163pub const Relocation = struct {
164 pub const Target = union(enum) {
165 local: u32,
166 global: u32,
167 };
168
174 /// Offset within the atom's code buffer.169 /// Offset within the atom's code buffer.
175 /// Note relocation size can be inferred by relocation's kind.170 /// Note relocation size can be inferred by relocation's kind.
176 offset: u32,171 offset: u32,
177172
178 where: enum {173 target: Target,
179 local,
180 undef,
181 },
182
183 where_index: u32,
184
185 payload: union(enum) {
186 unsigned: Unsigned,
187 branch: Branch,
188 page: Page,
189 page_off: PageOff,
190 pointer_to_got: PointerToGot,
191 signed: Signed,
192 load: Load,
193 },
194
195 const ResolveArgs = struct {
196 block: *Atom,
197 offset: u32,
198 source_addr: u64,
199 target_addr: u64,
200 macho_file: *MachO,
201 };
202
203 pub const Unsigned = struct {
204 subtractor: ?u32,
205
206 /// Addend embedded directly in the relocation slot
207 addend: i64,
208
209 /// Extracted from r_length:
210 /// => 3 implies true
211 /// => 2 implies false
212 /// => * is unreachable
213 is_64bit: bool,
214
215 pub fn resolve(self: Unsigned, args: ResolveArgs) !void {
216 const result = blk: {
217 if (self.subtractor) |subtractor| {
218 const sym = args.macho_file.locals.items[subtractor];
219 break :blk @intCast(i64, args.target_addr) - @intCast(i64, sym.n_value) + self.addend;
220 } else {
221 break :blk @intCast(i64, args.target_addr) + self.addend;
222 }
223 };
224
225 if (self.is_64bit) {
226 mem.writeIntLittle(u64, args.block.code.items[args.offset..][0..8], @bitCast(u64, result));
227 } else {
228 mem.writeIntLittle(u32, args.block.code.items[args.offset..][0..4], @truncate(u32, @bitCast(u64, result)));
229 }
230 }
231
232 pub fn format(self: Unsigned, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
233 _ = fmt;
234 _ = options;
235 try std.fmt.format(writer, "Unsigned {{ ", .{});
236 if (self.subtractor) |sub| {
237 try std.fmt.format(writer, ".subtractor = {}, ", .{sub});
238 }
239 try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
240 const length: usize = if (self.is_64bit) 8 else 4;
241 try std.fmt.format(writer, ".length = {}, ", .{length});
242 try std.fmt.format(writer, "}}", .{});
243 }
244 };
245
246 pub const Branch = struct {
247 arch: Arch,
248
249 pub fn resolve(self: Branch, args: ResolveArgs) !void {
250 switch (self.arch) {
251 .aarch64 => {
252 const displacement = math.cast(
253 i28,
254 @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr),
255 ) catch |err| switch (err) {
256 error.Overflow => {
257 log.err("jump too big to encode as i28 displacement value", .{});
258 log.err(" (target - source) = displacement => 0x{x} - 0x{x} = 0x{x}", .{
259 args.target_addr,
260 args.source_addr,
261 @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr),
262 });
263 log.err(" TODO implement branch islands to extend jump distance for arm64", .{});
264 return error.TODOImplementBranchIslands;
265 },
266 };
267 const code = args.block.code.items[args.offset..][0..4];
268 var inst = aarch64.Instruction{
269 .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload(
270 aarch64.Instruction,
271 aarch64.Instruction.unconditional_branch_immediate,
272 ), code),
273 };
274 inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2));
275 mem.writeIntLittle(u32, code, inst.toU32());
276 },
277 .x86_64 => {
278 const displacement = try math.cast(
279 i32,
280 @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4,
281 );
282 mem.writeIntLittle(u32, args.block.code.items[args.offset..][0..4], @bitCast(u32, displacement));
283 },
284 else => return error.UnsupportedCpuArchitecture,
285 }
286 }
287
288 pub fn format(self: Branch, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
289 _ = self;
290 _ = fmt;
291 _ = options;
292 try std.fmt.format(writer, "Branch {{}}", .{});
293 }
294 };
295174
296 pub const Page = struct {175 addend: i64,
297 kind: enum {
298 page,
299 got,
300 tlvp,
301 },
302 addend: u32 = 0,
303
304 pub fn resolve(self: Page, args: ResolveArgs) !void {
305 const target_addr = args.target_addr + self.addend;
306 const source_page = @intCast(i32, args.source_addr >> 12);
307 const target_page = @intCast(i32, target_addr >> 12);
308 const pages = @bitCast(u21, @intCast(i21, target_page - source_page));
309
310 const code = args.block.code.items[args.offset..][0..4];
311 var inst = aarch64.Instruction{
312 .pc_relative_address = mem.bytesToValue(meta.TagPayload(
313 aarch64.Instruction,
314 aarch64.Instruction.pc_relative_address,
315 ), code),
316 };
317 inst.pc_relative_address.immhi = @truncate(u19, pages >> 2);
318 inst.pc_relative_address.immlo = @truncate(u2, pages);
319
320 mem.writeIntLittle(u32, code, inst.toU32());
321 }
322
323 pub fn format(self: Page, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
324 _ = fmt;
325 _ = options;
326 try std.fmt.format(writer, "Page {{ ", .{});
327 switch (self.kind) {
328 .page => {},
329 .got => {
330 try std.fmt.format(writer, ".got, ", .{});
331 },
332 .tlvp => {
333 try std.fmt.format(writer, ".tlvp", .{});
334 },
335 }
336 try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
337 try std.fmt.format(writer, "}}", .{});
338 }
339 };
340
341 pub const PageOff = struct {
342 kind: enum {
343 page,
344 got,
345 tlvp,
346 },
347 addend: u32 = 0,
348 op_kind: ?OpKind = null,
349
350 pub const OpKind = enum {
351 arithmetic,
352 load,
353 };
354
355 pub fn resolve(self: PageOff, args: ResolveArgs) !void {
356 const code = args.block.code.items[args.offset..][0..4];
357
358 switch (self.kind) {
359 .page => {
360 const target_addr = args.target_addr + self.addend;
361 const narrowed = @truncate(u12, target_addr);
362
363 const op_kind = self.op_kind orelse unreachable;
364 var inst: aarch64.Instruction = blk: {
365 switch (op_kind) {
366 .arithmetic => {
367 break :blk .{
368 .add_subtract_immediate = mem.bytesToValue(meta.TagPayload(
369 aarch64.Instruction,
370 aarch64.Instruction.add_subtract_immediate,
371 ), code),
372 };
373 },
374 .load => {
375 break :blk .{
376 .load_store_register = mem.bytesToValue(meta.TagPayload(
377 aarch64.Instruction,
378 aarch64.Instruction.load_store_register,
379 ), code),
380 };
381 },
382 }
383 };
384
385 if (op_kind == .arithmetic) {
386 inst.add_subtract_immediate.imm12 = narrowed;
387 } else {
388 const offset: u12 = blk: {
389 if (inst.load_store_register.size == 0) {
390 if (inst.load_store_register.v == 1) {
391 // 128-bit SIMD is scaled by 16.
392 break :blk try math.divExact(u12, narrowed, 16);
393 }
394 // Otherwise, 8-bit SIMD or ldrb.
395 break :blk narrowed;
396 } else {
397 const denom: u4 = try math.powi(u4, 2, inst.load_store_register.size);
398 break :blk try math.divExact(u12, narrowed, denom);
399 }
400 };
401 inst.load_store_register.offset = offset;
402 }
403
404 mem.writeIntLittle(u32, code, inst.toU32());
405 },
406 .got => {
407 const narrowed = @truncate(u12, args.target_addr);
408 var inst: aarch64.Instruction = .{
409 .load_store_register = mem.bytesToValue(meta.TagPayload(
410 aarch64.Instruction,
411 aarch64.Instruction.load_store_register,
412 ), code),
413 };
414 const offset = try math.divExact(u12, narrowed, 8);
415 inst.load_store_register.offset = offset;
416 mem.writeIntLittle(u32, code, inst.toU32());
417 },
418 .tlvp => {
419 const RegInfo = struct {
420 rd: u5,
421 rn: u5,
422 size: u1,
423 };
424 const reg_info: RegInfo = blk: {
425 if (isArithmeticOp(code)) {
426 const inst = mem.bytesToValue(meta.TagPayload(
427 aarch64.Instruction,
428 aarch64.Instruction.add_subtract_immediate,
429 ), code);
430 break :blk .{
431 .rd = inst.rd,
432 .rn = inst.rn,
433 .size = inst.sf,
434 };
435 } else {
436 const inst = mem.bytesToValue(meta.TagPayload(
437 aarch64.Instruction,
438 aarch64.Instruction.load_store_register,
439 ), code);
440 break :blk .{
441 .rd = inst.rt,
442 .rn = inst.rn,
443 .size = @truncate(u1, inst.size),
444 };
445 }
446 };
447 const narrowed = @truncate(u12, args.target_addr);
448 var inst = aarch64.Instruction{
449 .add_subtract_immediate = .{
450 .rd = reg_info.rd,
451 .rn = reg_info.rn,
452 .imm12 = narrowed,
453 .sh = 0,
454 .s = 0,
455 .op = 0,
456 .sf = reg_info.size,
457 },
458 };
459 mem.writeIntLittle(u32, code, inst.toU32());
460 },
461 }
462 }
463
464 pub fn format(self: PageOff, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
465 _ = fmt;
466 _ = options;
467 try std.fmt.format(writer, "PageOff {{ ", .{});
468 switch (self.kind) {
469 .page => {},
470 .got => {
471 try std.fmt.format(writer, ".got, ", .{});
472 },
473 .tlvp => {
474 try std.fmt.format(writer, ".tlvp, ", .{});
475 },
476 }
477 try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
478 try std.fmt.format(writer, ".op_kind = {s}, ", .{self.op_kind});
479 try std.fmt.format(writer, "}}", .{});
480 }
481 };
482
483 pub const PointerToGot = struct {
484 pub fn resolve(_: PointerToGot, args: ResolveArgs) !void {
485 const result = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr));
486 mem.writeIntLittle(u32, args.block.code.items[args.offset..][0..4], @bitCast(u32, result));
487 }
488
489 pub fn format(self: PointerToGot, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
490 _ = self;
491 _ = fmt;
492 _ = options;
493 try std.fmt.format(writer, "PointerToGot {{}}", .{});
494 }
495 };
496
497 pub const Signed = struct {
498 addend: i64,
499 correction: u3,
500
501 pub fn resolve(self: Signed, args: ResolveArgs) !void {
502 const target_addr = @intCast(i64, args.target_addr) + self.addend;
503 const displacement = try math.cast(
504 i32,
505 target_addr - @intCast(i64, args.source_addr + self.correction + 4),
506 );
507 mem.writeIntLittle(u32, args.block.code.items[args.offset..][0..4], @bitCast(u32, displacement));
508 }
509
510 pub fn format(self: Signed, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
511 _ = fmt;
512 _ = options;
513 try std.fmt.format(writer, "Signed {{ ", .{});
514 try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
515 try std.fmt.format(writer, ".correction = {}, ", .{self.correction});
516 try std.fmt.format(writer, "}}", .{});
517 }
518 };
519
520 pub const Load = struct {
521 kind: enum {
522 got,
523 tlvp,
524 },
525 addend: i32 = 0,
526176
527 pub fn resolve(self: Load, args: ResolveArgs) !void {177 subtractor: ?u32,
528 if (self.kind == .tlvp) {
529 // We need to rewrite the opcode from movq to leaq.
530 args.block.code.items[args.offset - 2] = 0x8d;
531 }
532 const displacement = try math.cast(
533 i32,
534 @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4 + self.addend,
535 );
536 mem.writeIntLittle(u32, args.block.code.items[args.offset..][0..4], @bitCast(u32, displacement));
537 }
538178
539 pub fn format(self: Load, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {179 pcrel: bool,
540 _ = fmt;
541 _ = options;
542 try std.fmt.format(writer, "Load {{ ", .{});
543 try std.fmt.format(writer, "{s}, ", .{self.kind});
544 try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
545 try std.fmt.format(writer, "}}", .{});
546 }
547 };
548180
549 pub fn resolve(self: Relocation, args: ResolveArgs) !void {181 length: u2,
550 switch (self.payload) {
551 .unsigned => |unsigned| try unsigned.resolve(args),
552 .branch => |branch| try branch.resolve(args),
553 .page => |page| try page.resolve(args),
554 .page_off => |page_off| try page_off.resolve(args),
555 .pointer_to_got => |pointer_to_got| try pointer_to_got.resolve(args),
556 .signed => |signed| try signed.resolve(args),
557 .load => |load| try load.resolve(args),
558 }
559 }
560182
561 pub fn format(self: Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {183 @"type": u4,
562 try std.fmt.format(writer, "Relocation {{ ", .{});
563 try std.fmt.format(writer, ".offset = {}, ", .{self.offset});
564 try std.fmt.format(writer, ".where = {}, ", .{self.where});
565 try std.fmt.format(writer, ".where_index = {d}, ", .{self.where_index});
566
567 switch (self.payload) {
568 .unsigned => |unsigned| try unsigned.format(fmt, options, writer),
569 .branch => |branch| try branch.format(fmt, options, writer),
570 .page => |page| try page.format(fmt, options, writer),
571 .page_off => |page_off| try page_off.format(fmt, options, writer),
572 .pointer_to_got => |pointer_to_got| try pointer_to_got.format(fmt, options, writer),
573 .signed => |signed| try signed.format(fmt, options, writer),
574 .load => |load| try load.format(fmt, options, writer),
575 }
576
577 try std.fmt.format(writer, "}}", .{});
578 }
579};184};
580185
581pub const empty = Atom{186pub const empty = Atom{
...@@ -641,526 +246,367 @@ pub fn freeListEligible(self: Atom, macho_file: MachO) bool {...@@ -641,526 +246,367 @@ pub fn freeListEligible(self: Atom, macho_file: MachO) bool {
641246
642const RelocContext = struct {247const RelocContext = struct {
643 base_addr: u64 = 0,248 base_addr: u64 = 0,
644 base_offset: u64 = 0,
645 allocator: *Allocator,249 allocator: *Allocator,
646 object: *Object,250 object: *Object,
647 macho_file: *MachO,251 macho_file: *MachO,
648};252};
649253
650fn initRelocFromObject(rel: macho.relocation_info, context: RelocContext) !Relocation {
651 var parsed_rel = Relocation{
652 .offset = @intCast(u32, @intCast(u64, rel.r_address) - context.base_offset),
653 .where = undefined,
654 .where_index = undefined,
655 .payload = undefined,
656 };
657
658 if (rel.r_extern == 0) {
659 const sect_id = @intCast(u16, rel.r_symbolnum - 1);
660
661 const local_sym_index = context.object.sections_as_symbols.get(sect_id) orelse blk: {
662 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].Segment;
663 const sect = seg.sections.items[sect_id];
664 const match = (try context.macho_file.getMatchingSection(sect)) orelse unreachable;
665 const local_sym_index = @intCast(u32, context.macho_file.locals.items.len);
666 try context.macho_file.locals.append(context.allocator, .{
667 .n_strx = 0,
668 .n_type = macho.N_SECT,
669 .n_sect = @intCast(u8, context.macho_file.section_ordinals.getIndex(match).? + 1),
670 .n_desc = 0,
671 .n_value = 0,
672 });
673 try context.object.sections_as_symbols.putNoClobber(context.allocator, sect_id, local_sym_index);
674 break :blk local_sym_index;
675 };
676
677 parsed_rel.where = .local;
678 parsed_rel.where_index = local_sym_index;
679 } else {
680 const sym = context.object.symtab.items[rel.r_symbolnum];
681 const sym_name = context.object.getString(sym.n_strx);
682
683 if (MachO.symbolIsSect(sym) and !MachO.symbolIsExt(sym)) {
684 const where_index = context.object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable;
685 parsed_rel.where = .local;
686 parsed_rel.where_index = where_index;
687 } else {
688 const n_strx = context.macho_file.strtab_dir.getKeyAdapted(@as([]const u8, sym_name), StringIndexAdapter{
689 .bytes = &context.macho_file.strtab,
690 }) orelse unreachable;
691 const resolv = context.macho_file.symbol_resolver.get(n_strx) orelse unreachable;
692 switch (resolv.where) {
693 .global => {
694 parsed_rel.where = .local;
695 parsed_rel.where_index = resolv.local_sym_index;
696 },
697 .undef => {
698 parsed_rel.where = .undef;
699 parsed_rel.where_index = resolv.where_index;
700 },
701 }
702 }
703 }
704
705 return parsed_rel;
706}
707
708pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocContext) !void {254pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocContext) !void {
709 const tracy = trace(@src());255 const tracy = trace(@src());
710 defer tracy.end();256 defer tracy.end();
711257
712 const filtered_relocs = filterRelocs(relocs, context.base_offset, context.base_offset + self.size);
713 var it = RelocIterator{
714 .buffer = filtered_relocs,
715 };
716
717 var addend: u32 = 0;
718 var subtractor: ?u32 = null;
719 const arch = context.macho_file.base.options.target.cpu.arch;258 const arch = context.macho_file.base.options.target.cpu.arch;
259 var addend: i64 = 0;
260 var subtractor: ?u32 = null;
720261
721 while (it.next()) |rel| {262 for (relocs) |rel, i| {
722 if (isAddend(rel, arch)) {263 blk: {
723 // Addend is not a relocation with effect on the TextBlock, so264 switch (arch) {
724 // parse it and carry on.265 .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
725 assert(addend == 0); // Oh no, addend was not reset!266 .ARM64_RELOC_ADDEND => {
726 addend = rel.r_symbolnum;267 assert(addend == 0);
727268 addend = rel.r_symbolnum;
728 // Verify ADDEND is followed by a PAGE21 or PAGEOFF12.269 // Verify that it's followed by ARM64_RELOC_PAGE21 or ARM64_RELOC_PAGEOFF12.
729 const next = @intToEnum(macho.reloc_type_arm64, it.peek().r_type);270 if (relocs.len <= i + 1) {
730 switch (next) {271 log.err("no relocation after ARM64_RELOC_ADDEND", .{});
731 .ARM64_RELOC_PAGE21, .ARM64_RELOC_PAGEOFF12 => {},272 return error.UnexpectedRelocationType;
732 else => {273 }
733 log.err("unexpected relocation type: expected PAGE21 or PAGEOFF12, found {s}", .{next});274 const next = @intToEnum(macho.reloc_type_arm64, relocs[i + 1].r_type);
734 return error.UnexpectedRelocationType;275 switch (next) {
276 .ARM64_RELOC_PAGE21, .ARM64_RELOC_PAGEOFF12 => {},
277 else => {
278 log.err("unexpected relocation type after ARM64_RELOC_ADDEND", .{});
279 log.err(" expected ARM64_RELOC_PAGE21 or ARM64_RELOC_PAGEOFF12", .{});
280 log.err(" found {s}", .{next});
281 return error.UnexpectedRelocationType;
282 },
283 }
284 continue;
285 },
286 .ARM64_RELOC_SUBTRACTOR => {},
287 else => break :blk,
288 },
289 .x86_64 => switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) {
290 .X86_64_RELOC_SUBTRACTOR => {},
291 else => break :blk,
735 },292 },
293 else => unreachable,
736 }294 }
737 continue;
738 }
739295
740 if (isSubtractor(rel, arch)) {296 assert(subtractor == null);
741 // Subtractor is not a relocation with effect on the TextBlock, so
742 // parse it and carry on.
743 assert(subtractor == null); // Oh no, subtractor was not reset!
744 assert(rel.r_extern == 1);
745 const sym = context.object.symtab.items[rel.r_symbolnum];297 const sym = context.object.symtab.items[rel.r_symbolnum];
746 const sym_name = context.object.getString(sym.n_strx);
747
748 if (MachO.symbolIsSect(sym) and !MachO.symbolIsExt(sym)) {298 if (MachO.symbolIsSect(sym) and !MachO.symbolIsExt(sym)) {
749 const where_index = context.object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable;299 subtractor = context.object.symbol_mapping.get(rel.r_symbolnum).?;
750 subtractor = where_index;
751 } else {300 } else {
752 const n_strx = context.macho_file.strtab_dir.getKeyAdapted(@as([]const u8, sym_name), StringIndexAdapter{301 const sym_name = context.object.getString(sym.n_strx);
753 .bytes = &context.macho_file.strtab,302 const n_strx = context.macho_file.strtab_dir.getKeyAdapted(
754 }) orelse unreachable;303 @as([]const u8, sym_name),
755 const resolv = context.macho_file.symbol_resolver.get(n_strx) orelse unreachable;304 StringIndexAdapter{
305 .bytes = &context.macho_file.strtab,
306 },
307 ).?;
308 const resolv = context.macho_file.symbol_resolver.get(n_strx).?;
756 assert(resolv.where == .global);309 assert(resolv.where == .global);
757 subtractor = resolv.local_sym_index;310 subtractor = resolv.local_sym_index;
758 }311 }
759312 // Verify that *_SUBTRACTOR is followed by *_UNSIGNED.
760 // Verify SUBTRACTOR is followed by UNSIGNED.313 if (relocs.len <= i + 1) {
314 log.err("no relocation after *_RELOC_SUBTRACTOR", .{});
315 return error.UnexpectedRelocationType;
316 }
761 switch (arch) {317 switch (arch) {
762 .aarch64 => {318 .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, relocs[i + 1].r_type)) {
763 const next = @intToEnum(macho.reloc_type_arm64, it.peek().r_type);319 .ARM64_RELOC_UNSIGNED => {},
764 if (next != .ARM64_RELOC_UNSIGNED) {320 else => {
765 log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next});321 log.err("unexpected relocation type after ARM64_RELOC_ADDEND", .{});
322 log.err(" expected ARM64_RELOC_UNSIGNED", .{});
323 log.err(" found {s}", .{@intToEnum(macho.reloc_type_arm64, relocs[i + 1].r_type)});
766 return error.UnexpectedRelocationType;324 return error.UnexpectedRelocationType;
767 }325 },
768 },326 },
769 .x86_64 => {327 .x86_64 => switch (@intToEnum(macho.reloc_type_x86_64, relocs[i + 1].r_type)) {
770 const next = @intToEnum(macho.reloc_type_x86_64, it.peek().r_type);328 .X86_64_RELOC_UNSIGNED => {},
771 if (next != .X86_64_RELOC_UNSIGNED) {329 else => {
772 log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next});330 log.err("unexpected relocation type after X86_64_RELOC_ADDEND", .{});
331 log.err(" expected X86_64_RELOC_UNSIGNED", .{});
332 log.err(" found {s}", .{@intToEnum(macho.reloc_type_x86_64, relocs[i + 1].r_type)});
773 return error.UnexpectedRelocationType;333 return error.UnexpectedRelocationType;
774 }334 },
775 },335 },
776 else => unreachable,336 else => unreachable,
777 }337 }
778 continue;338 continue;
779 }339 }
780340
781 var parsed_rel = try initRelocFromObject(rel, context);341 const target = target: {
342 if (rel.r_extern == 0) {
343 const sect_id = @intCast(u16, rel.r_symbolnum - 1);
344 const local_sym_index = context.object.sections_as_symbols.get(sect_id) orelse blk: {
345 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].Segment;
346 const sect = seg.sections.items[sect_id];
347 const match = (try context.macho_file.getMatchingSection(sect)) orelse unreachable;
348 const sym_name = try std.fmt.allocPrint(context.allocator, "{s}_{s}_{s}", .{
349 context.object.name,
350 commands.segmentName(sect),
351 commands.sectionName(sect),
352 });
353 defer context.allocator.free(sym_name);
354 const local_sym_index = @intCast(u32, context.macho_file.locals.items.len);
355 try context.macho_file.locals.append(context.allocator, .{
356 .n_strx = try context.macho_file.makeString(sym_name),
357 .n_type = macho.N_SECT,
358 .n_sect = @intCast(u8, context.macho_file.section_ordinals.getIndex(match).? + 1),
359 .n_desc = 0,
360 .n_value = 0,
361 });
362 try context.object.sections_as_symbols.putNoClobber(context.allocator, sect_id, local_sym_index);
363 break :blk local_sym_index;
364 };
365 break :target Relocation.Target{ .local = local_sym_index };
366 }
367
368 const sym = context.object.symtab.items[rel.r_symbolnum];
369 const sym_name = context.object.getString(sym.n_strx);
370
371 if (MachO.symbolIsSect(sym) and !MachO.symbolIsExt(sym)) {
372 const sym_index = context.object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable;
373 break :target Relocation.Target{ .local = sym_index };
374 }
375
376 const n_strx = context.macho_file.strtab_dir.getKeyAdapted(
377 @as([]const u8, sym_name),
378 StringIndexAdapter{
379 .bytes = &context.macho_file.strtab,
380 },
381 ) orelse unreachable;
382 break :target Relocation.Target{ .global = n_strx };
383 };
384 const offset = @intCast(u32, rel.r_address);
782385
783 switch (arch) {386 switch (arch) {
784 .aarch64 => {387 .aarch64 => {
785 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);388 switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
786 switch (rel_type) {
787 .ARM64_RELOC_ADDEND => unreachable,
788 .ARM64_RELOC_SUBTRACTOR => unreachable,
789 .ARM64_RELOC_BRANCH26 => {389 .ARM64_RELOC_BRANCH26 => {
790 self.parseBranch(rel, &parsed_rel, context);390 // TODO rewrite relocation
791 },391 try addStub(target, context);
792 .ARM64_RELOC_UNSIGNED => {
793 self.parseUnsigned(rel, &parsed_rel, subtractor, context);
794 subtractor = null;
795 },392 },
796 .ARM64_RELOC_PAGE21,393 .ARM64_RELOC_GOT_LOAD_PAGE21, .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => {
797 .ARM64_RELOC_GOT_LOAD_PAGE21,394 // TODO rewrite relocation
798 .ARM64_RELOC_TLVP_LOAD_PAGE21,395 try addGotEntry(target, context);
799 => {
800 self.parsePage(rel, &parsed_rel, addend);
801 if (rel_type == .ARM64_RELOC_PAGE21)
802 addend = 0;
803 },
804 .ARM64_RELOC_PAGEOFF12,
805 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
806 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
807 => {
808 self.parsePageOff(rel, &parsed_rel, addend);
809 if (rel_type == .ARM64_RELOC_PAGEOFF12)
810 addend = 0;
811 },396 },
812 .ARM64_RELOC_POINTER_TO_GOT => {397 .ARM64_RELOC_UNSIGNED => {
813 self.parsePointerToGot(rel, &parsed_rel);398 assert(rel.r_extern == 1);
399 addend = if (rel.r_length == 3)
400 mem.readIntLittle(i64, self.code.items[offset..][0..8])
401 else
402 mem.readIntLittle(i32, self.code.items[offset..][0..4]);
403 try self.addPtrBindingOrRebase(rel, target, context);
814 },404 },
405 else => {},
815 }406 }
816 },407 },
817 .x86_64 => {408 .x86_64 => {
818 switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) {409 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
819 .X86_64_RELOC_SUBTRACTOR => unreachable,410 switch (rel_type) {
820 .X86_64_RELOC_BRANCH => {411 .X86_64_RELOC_BRANCH => {
821 self.parseBranch(rel, &parsed_rel, context);412 // TODO rewrite relocation
413 try addStub(target, context);
414 },
415 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => {
416 // TODO rewrite relocation
417 try addGotEntry(target, context);
418 addend = mem.readIntLittle(i32, self.code.items[offset..][0..4]);
822 },419 },
823 .X86_64_RELOC_UNSIGNED => {420 .X86_64_RELOC_UNSIGNED => {
824 self.parseUnsigned(rel, &parsed_rel, subtractor, context);421 addend = if (rel.r_length == 3)
825 subtractor = null;422 mem.readIntLittle(i64, self.code.items[offset..][0..8])
423 else
424 mem.readIntLittle(i32, self.code.items[offset..][0..4]);
425 if (rel.r_extern == 0) {
426 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].Segment;
427 const target_sect_base_addr = seg.sections.items[rel.r_symbolnum - 1].addr;
428 addend -= @intCast(i64, target_sect_base_addr);
429 }
430 try self.addPtrBindingOrRebase(rel, target, context);
826 },431 },
827 .X86_64_RELOC_SIGNED,432 .X86_64_RELOC_SIGNED,
828 .X86_64_RELOC_SIGNED_1,433 .X86_64_RELOC_SIGNED_1,
829 .X86_64_RELOC_SIGNED_2,434 .X86_64_RELOC_SIGNED_2,
830 .X86_64_RELOC_SIGNED_4,435 .X86_64_RELOC_SIGNED_4,
831 => {436 => {
832 self.parseSigned(rel, &parsed_rel, context);437 const correction: u3 = switch (rel_type) {
833 },438 .X86_64_RELOC_SIGNED => 0,
834 .X86_64_RELOC_GOT_LOAD,439 .X86_64_RELOC_SIGNED_1 => 1,
835 .X86_64_RELOC_GOT,440 .X86_64_RELOC_SIGNED_2 => 2,
836 .X86_64_RELOC_TLV,441 .X86_64_RELOC_SIGNED_4 => 4,
837 => {442 else => unreachable,
838 self.parseLoad(rel, &parsed_rel);443 };
444 addend = mem.readIntLittle(i32, self.code.items[offset..][0..4]) + correction;
445 if (rel.r_extern == 0) {
446 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].Segment;
447 const target_sect_base_addr = seg.sections.items[rel.r_symbolnum - 1].addr;
448 addend += @intCast(i64, context.base_addr + offset + correction + 4) -
449 @intCast(i64, target_sect_base_addr);
450 }
839 },451 },
452 else => {},
840 }453 }
841 },454 },
842 else => unreachable,455 else => unreachable,
843 }456 }
844457
845 try self.relocs.append(context.allocator, parsed_rel);458 try self.relocs.append(context.allocator, .{
846459 .offset = offset,
847 const is_via_got = switch (parsed_rel.payload) {460 .target = target,
848 .pointer_to_got => true,461 .addend = addend,
849 .load => |load| load.kind == .got,462 .subtractor = subtractor,
850 .page => |page| page.kind == .got,463 .pcrel = rel.r_pcrel == 1,
851 .page_off => |page_off| page_off.kind == .got,464 .length = rel.r_length,
852 else => false,465 .@"type" = rel.r_type,
853 };466 });
854
855 if (is_via_got) blk: {
856 const key = MachO.GotIndirectionKey{
857 .where = switch (parsed_rel.where) {
858 .local => .local,
859 .undef => .undef,
860 },
861 .where_index = parsed_rel.where_index,
862 };
863 if (context.macho_file.got_entries_map.contains(key)) break :blk;
864
865 const atom = try context.macho_file.createGotAtom(key);
866 try context.macho_file.got_entries_map.putNoClobber(context.macho_file.base.allocator, key, atom);
867 const match = MachO.MatchingSection{
868 .seg = context.macho_file.data_const_segment_cmd_index.?,
869 .sect = context.macho_file.got_section_index.?,
870 };
871
872 if (!context.object.start_atoms.contains(match)) {
873 try context.object.start_atoms.putNoClobber(context.allocator, match, atom);
874 }
875
876 if (context.object.end_atoms.getPtr(match)) |last| {
877 last.*.next = atom;
878 atom.prev = last.*;
879 last.* = atom;
880 } else {
881 try context.object.end_atoms.putNoClobber(context.allocator, match, atom);
882 }
883 } else if (parsed_rel.payload == .unsigned) {
884 switch (parsed_rel.where) {
885 .undef => {
886 try self.bindings.append(context.allocator, .{
887 .local_sym_index = parsed_rel.where_index,
888 .offset = parsed_rel.offset,
889 });
890 },
891 .local => {
892 const source_sym = context.macho_file.locals.items[self.local_sym_index];
893 const match = context.macho_file.section_ordinals.keys()[source_sym.n_sect - 1];
894 const seg = context.macho_file.load_commands.items[match.seg].Segment;
895 const sect = seg.sections.items[match.sect];
896 const sect_type = commands.sectionType(sect);
897
898 const should_rebase = rebase: {
899 if (!parsed_rel.payload.unsigned.is_64bit) break :rebase false;
900
901 // TODO actually, a check similar to what dyld is doing, that is, verifying
902 // that the segment is writable should be enough here.
903 const is_right_segment = blk: {
904 if (context.macho_file.data_segment_cmd_index) |idx| {
905 if (match.seg == idx) {
906 break :blk true;
907 }
908 }
909 if (context.macho_file.data_const_segment_cmd_index) |idx| {
910 if (match.seg == idx) {
911 break :blk true;
912 }
913 }
914 break :blk false;
915 };
916
917 if (!is_right_segment) break :rebase false;
918 if (sect_type != macho.S_LITERAL_POINTERS and
919 sect_type != macho.S_REGULAR and
920 sect_type != macho.S_MOD_INIT_FUNC_POINTERS and
921 sect_type != macho.S_MOD_TERM_FUNC_POINTERS)
922 {
923 break :rebase false;
924 }
925
926 break :rebase true;
927 };
928467
929 if (should_rebase) {468 addend = 0;
930 try self.rebases.append(context.allocator, parsed_rel.offset);469 subtractor = null;
931 }
932 },
933 }
934 } else if (parsed_rel.payload == .branch) blk: {
935 if (parsed_rel.where != .undef) break :blk;
936 if (context.macho_file.stubs_map.contains(parsed_rel.where_index)) break :blk;
937
938 // TODO clean this up!
939 const stub_helper_atom = atom: {
940 const atom = try context.macho_file.createStubHelperAtom();
941 const match = MachO.MatchingSection{
942 .seg = context.macho_file.text_segment_cmd_index.?,
943 .sect = context.macho_file.stub_helper_section_index.?,
944 };
945 if (!context.object.start_atoms.contains(match)) {
946 try context.object.start_atoms.putNoClobber(context.allocator, match, atom);
947 }
948 if (context.object.end_atoms.getPtr(match)) |last| {
949 last.*.next = atom;
950 atom.prev = last.*;
951 last.* = atom;
952 } else {
953 try context.object.end_atoms.putNoClobber(context.allocator, match, atom);
954 }
955 break :atom atom;
956 };
957 const laptr_atom = atom: {
958 const atom = try context.macho_file.createLazyPointerAtom(
959 stub_helper_atom.local_sym_index,
960 parsed_rel.where_index,
961 );
962 const match = MachO.MatchingSection{
963 .seg = context.macho_file.data_segment_cmd_index.?,
964 .sect = context.macho_file.la_symbol_ptr_section_index.?,
965 };
966 if (!context.object.start_atoms.contains(match)) {
967 try context.object.start_atoms.putNoClobber(context.allocator, match, atom);
968 }
969 if (context.object.end_atoms.getPtr(match)) |last| {
970 last.*.next = atom;
971 atom.prev = last.*;
972 last.* = atom;
973 } else {
974 try context.object.end_atoms.putNoClobber(context.allocator, match, atom);
975 }
976 break :atom atom;
977 };
978 {
979 const atom = try context.macho_file.createStubAtom(laptr_atom.local_sym_index);
980 const match = MachO.MatchingSection{
981 .seg = context.macho_file.text_segment_cmd_index.?,
982 .sect = context.macho_file.stubs_section_index.?,
983 };
984 if (!context.object.start_atoms.contains(match)) {
985 try context.object.start_atoms.putNoClobber(context.allocator, match, atom);
986 }
987 if (context.object.end_atoms.getPtr(match)) |last| {
988 last.*.next = atom;
989 atom.prev = last.*;
990 last.* = atom;
991 } else {
992 try context.object.end_atoms.putNoClobber(context.allocator, match, atom);
993 }
994 try context.macho_file.stubs_map.putNoClobber(context.allocator, parsed_rel.where_index, atom);
995 }
996 }
997 }470 }
998}471}
999472
1000fn isAddend(rel: macho.relocation_info, arch: Arch) bool {473fn addPtrBindingOrRebase(
1001 if (arch != .aarch64) return false;474 self: *Atom,
1002 return @intToEnum(macho.reloc_type_arm64, rel.r_type) == .ARM64_RELOC_ADDEND;
1003}
1004
1005fn isSubtractor(rel: macho.relocation_info, arch: Arch) bool {
1006 return switch (arch) {
1007 .aarch64 => @intToEnum(macho.reloc_type_arm64, rel.r_type) == .ARM64_RELOC_SUBTRACTOR,
1008 .x86_64 => @intToEnum(macho.reloc_type_x86_64, rel.r_type) == .X86_64_RELOC_SUBTRACTOR,
1009 else => unreachable,
1010 };
1011}
1012
1013fn parseUnsigned(
1014 self: Atom,
1015 rel: macho.relocation_info,475 rel: macho.relocation_info,
1016 out: *Relocation,476 target: Relocation.Target,
1017 subtractor: ?u32,
1018 context: RelocContext,477 context: RelocContext,
1019) void {478) !void {
1020 assert(rel.r_pcrel == 0);479 switch (target) {
1021480 .global => |n_strx| {
1022 const is_64bit: bool = switch (rel.r_length) {481 try self.bindings.append(context.allocator, .{
1023 3 => true,482 .n_strx = n_strx,
1024 2 => false,483 .offset = @intCast(u32, rel.r_address),
1025 else => unreachable,484 });
1026 };
1027
1028 var addend: i64 = if (is_64bit)
1029 mem.readIntLittle(i64, self.code.items[out.offset..][0..8])
1030 else
1031 mem.readIntLittle(i32, self.code.items[out.offset..][0..4]);
1032
1033 if (rel.r_extern == 0) {
1034 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].Segment;
1035 const target_sect_base_addr = seg.sections.items[rel.r_symbolnum - 1].addr;
1036 addend -= @intCast(i64, target_sect_base_addr);
1037 }
1038
1039 out.payload = .{
1040 .unsigned = .{
1041 .subtractor = subtractor,
1042 .is_64bit = is_64bit,
1043 .addend = addend,
1044 },
1045 };
1046}
1047
1048fn parseBranch(self: Atom, rel: macho.relocation_info, out: *Relocation, context: RelocContext) void {
1049 _ = self;
1050 assert(rel.r_pcrel == 1);
1051 assert(rel.r_length == 2);
1052
1053 out.payload = .{
1054 .branch = .{
1055 .arch = context.macho_file.base.options.target.cpu.arch,
1056 },485 },
1057 };486 .local => {
1058}487 const source_sym = context.macho_file.locals.items[self.local_sym_index];
488 const match = context.macho_file.section_ordinals.keys()[source_sym.n_sect - 1];
489 const seg = context.macho_file.load_commands.items[match.seg].Segment;
490 const sect = seg.sections.items[match.sect];
491 const sect_type = commands.sectionType(sect);
492
493 const should_rebase = rebase: {
494 if (rel.r_length != 3) break :rebase false;
495
496 // TODO actually, a check similar to what dyld is doing, that is, verifying
497 // that the segment is writable should be enough here.
498 const is_right_segment = blk: {
499 if (context.macho_file.data_segment_cmd_index) |idx| {
500 if (match.seg == idx) {
501 break :blk true;
502 }
503 }
504 if (context.macho_file.data_const_segment_cmd_index) |idx| {
505 if (match.seg == idx) {
506 break :blk true;
507 }
508 }
509 break :blk false;
510 };
1059511
1060fn parsePage(self: Atom, rel: macho.relocation_info, out: *Relocation, addend: u32) void {512 if (!is_right_segment) break :rebase false;
1061 _ = self;513 if (sect_type != macho.S_LITERAL_POINTERS and
1062 assert(rel.r_pcrel == 1);514 sect_type != macho.S_REGULAR and
1063 assert(rel.r_length == 2);515 sect_type != macho.S_MOD_INIT_FUNC_POINTERS and
1064516 sect_type != macho.S_MOD_TERM_FUNC_POINTERS)
1065 out.payload = .{517 {
1066 .page = .{518 break :rebase false;
1067 .kind = switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {519 }
1068 .ARM64_RELOC_PAGE21 => .page,
1069 .ARM64_RELOC_GOT_LOAD_PAGE21 => .got,
1070 .ARM64_RELOC_TLVP_LOAD_PAGE21 => .tlvp,
1071 else => unreachable,
1072 },
1073 .addend = addend,
1074 },
1075 };
1076}
1077520
1078fn parsePageOff(self: Atom, rel: macho.relocation_info, out: *Relocation, addend: u32) void {521 break :rebase true;
1079 assert(rel.r_pcrel == 0);522 };
1080 assert(rel.r_length == 2);
1081
1082 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
1083 const op_kind: ?Relocation.PageOff.OpKind = blk: {
1084 if (rel_type != .ARM64_RELOC_PAGEOFF12) break :blk null;
1085 const op_kind: Relocation.PageOff.OpKind = if (isArithmeticOp(self.code.items[out.offset..][0..4]))
1086 .arithmetic
1087 else
1088 .load;
1089 break :blk op_kind;
1090 };
1091523
1092 out.payload = .{524 if (should_rebase) {
1093 .page_off = .{525 try self.rebases.append(context.allocator, @intCast(u32, rel.r_address));
1094 .kind = switch (rel_type) {526 }
1095 .ARM64_RELOC_PAGEOFF12 => .page,
1096 .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => .got,
1097 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => .tlvp,
1098 else => unreachable,
1099 },
1100 .addend = addend,
1101 .op_kind = op_kind,
1102 },527 },
1103 };528 }
1104}529}
1105530
1106fn parsePointerToGot(self: Atom, rel: macho.relocation_info, out: *Relocation) void {531fn addGotEntry(target: Relocation.Target, context: RelocContext) !void {
1107 _ = self;532 if (context.macho_file.got_entries_map.contains(target)) return;
1108 assert(rel.r_pcrel == 1);533 const atom = try context.macho_file.createGotAtom(target);
1109 assert(rel.r_length == 2);534 try context.macho_file.got_entries_map.putNoClobber(context.macho_file.base.allocator, target, atom);
1110535 const match = MachO.MatchingSection{
1111 out.payload = .{536 .seg = context.macho_file.data_const_segment_cmd_index.?,
1112 .pointer_to_got = .{},537 .sect = context.macho_file.got_section_index.?,
1113 };538 };
539 if (!context.object.start_atoms.contains(match)) {
540 try context.object.start_atoms.putNoClobber(context.allocator, match, atom);
541 }
542 if (context.object.end_atoms.getPtr(match)) |last| {
543 last.*.next = atom;
544 atom.prev = last.*;
545 last.* = atom;
546 } else {
547 try context.object.end_atoms.putNoClobber(context.allocator, match, atom);
548 }
1114}549}
1115550
1116fn parseSigned(self: Atom, rel: macho.relocation_info, out: *Relocation, context: RelocContext) void {551fn addStub(target: Relocation.Target, context: RelocContext) !void {
1117 assert(rel.r_pcrel == 1);552 if (target != .global) return;
1118 assert(rel.r_length == 2);553 if (context.macho_file.stubs_map.contains(target.global)) return;
1119554 // TODO clean this up!
1120 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);555 const stub_helper_atom = atom: {
1121 const correction: u3 = switch (rel_type) {556 const atom = try context.macho_file.createStubHelperAtom();
1122 .X86_64_RELOC_SIGNED => 0,557 const match = MachO.MatchingSection{
1123 .X86_64_RELOC_SIGNED_1 => 1,558 .seg = context.macho_file.text_segment_cmd_index.?,
1124 .X86_64_RELOC_SIGNED_2 => 2,559 .sect = context.macho_file.stub_helper_section_index.?,
1125 .X86_64_RELOC_SIGNED_4 => 4,560 };
1126 else => unreachable,561 if (!context.object.start_atoms.contains(match)) {
562 try context.object.start_atoms.putNoClobber(context.allocator, match, atom);
563 }
564 if (context.object.end_atoms.getPtr(match)) |last| {
565 last.*.next = atom;
566 atom.prev = last.*;
567 last.* = atom;
568 } else {
569 try context.object.end_atoms.putNoClobber(context.allocator, match, atom);
570 }
571 break :atom atom;
1127 };572 };
1128 var addend: i64 = mem.readIntLittle(i32, self.code.items[out.offset..][0..4]) + correction;573 const laptr_atom = atom: {
1129574 const atom = try context.macho_file.createLazyPointerAtom(
1130 if (rel.r_extern == 0) {575 stub_helper_atom.local_sym_index,
1131 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].Segment;576 target.global,
1132 const target_sect_base_addr = seg.sections.items[rel.r_symbolnum - 1].addr;577 );
1133 addend += @intCast(i64, context.base_addr + out.offset + correction + 4) - @intCast(i64, target_sect_base_addr);578 const match = MachO.MatchingSection{
1134 }579 .seg = context.macho_file.data_segment_cmd_index.?,
1135580 .sect = context.macho_file.la_symbol_ptr_section_index.?,
1136 out.payload = .{581 };
1137 .signed = .{582 if (!context.object.start_atoms.contains(match)) {
1138 .correction = correction,583 try context.object.start_atoms.putNoClobber(context.allocator, match, atom);
1139 .addend = addend,584 }
1140 },585 if (context.object.end_atoms.getPtr(match)) |last| {
586 last.*.next = atom;
587 atom.prev = last.*;
588 last.* = atom;
589 } else {
590 try context.object.end_atoms.putNoClobber(context.allocator, match, atom);
591 }
592 break :atom atom;
1141 };593 };
1142}594 const atom = try context.macho_file.createStubAtom(laptr_atom.local_sym_index);
1143595 const match = MachO.MatchingSection{
1144fn parseLoad(self: Atom, rel: macho.relocation_info, out: *Relocation) void {596 .seg = context.macho_file.text_segment_cmd_index.?,
1145 assert(rel.r_pcrel == 1);597 .sect = context.macho_file.stubs_section_index.?,
1146 assert(rel.r_length == 2);
1147
1148 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
1149 const addend: i32 = if (rel_type == .X86_64_RELOC_GOT)
1150 mem.readIntLittle(i32, self.code.items[out.offset..][0..4])
1151 else
1152 0;
1153
1154 out.payload = .{
1155 .load = .{
1156 .kind = switch (rel_type) {
1157 .X86_64_RELOC_GOT_LOAD, .X86_64_RELOC_GOT => .got,
1158 .X86_64_RELOC_TLV => .tlvp,
1159 else => unreachable,
1160 },
1161 .addend = addend,
1162 },
1163 };598 };
599 if (!context.object.start_atoms.contains(match)) {
600 try context.object.start_atoms.putNoClobber(context.allocator, match, atom);
601 }
602 if (context.object.end_atoms.getPtr(match)) |last| {
603 last.*.next = atom;
604 atom.prev = last.*;
605 last.* = atom;
606 } else {
607 try context.object.end_atoms.putNoClobber(context.allocator, match, atom);
608 }
609 try context.macho_file.stubs_map.putNoClobber(context.allocator, target.global, atom);
1164}610}
1165611
1166pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {612pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
...@@ -1169,42 +615,42 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -1169,42 +615,42 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
1169615
1170 for (self.relocs.items) |rel| {616 for (self.relocs.items) |rel| {
1171 log.debug("relocating {}", .{rel});617 log.debug("relocating {}", .{rel});
1172618 const arch = macho_file.base.options.target.cpu.arch;
1173 const source_addr = blk: {619 const source_addr = blk: {
1174 const sym = macho_file.locals.items[self.local_sym_index];620 const sym = macho_file.locals.items[self.local_sym_index];
1175 break :blk sym.n_value + rel.offset;621 break :blk sym.n_value + rel.offset;
1176 };622 };
1177 const target_addr = blk: {623 const target_addr = blk: {
1178 const is_via_got = switch (rel.payload) {624 const is_via_got = got: {
1179 .pointer_to_got => true,625 switch (arch) {
1180 .page => |page| page.kind == .got,626 .aarch64 => break :got switch (@intToEnum(macho.reloc_type_arm64, rel.@"type")) {
1181 .page_off => |page_off| page_off.kind == .got,627 .ARM64_RELOC_GOT_LOAD_PAGE21, .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => true,
1182 .load => |load| load.kind == .got,628 else => false,
1183 else => false,629 },
630 .x86_64 => break :got switch (@intToEnum(macho.reloc_type_x86_64, rel.@"type")) {
631 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => true,
632 else => false,
633 },
634 else => unreachable,
635 }
1184 };636 };
1185637
1186 if (is_via_got) {638 if (is_via_got) {
1187 const atom = macho_file.got_entries_map.get(.{639 const atom = macho_file.got_entries_map.get(rel.target) orelse {
1188 .where = switch (rel.where) {640 const n_strx = switch (rel.target) {
1189 .local => .local,641 .local => |sym_index| macho_file.locals.items[sym_index].n_strx,
1190 .undef => .undef,642 .global => |n_strx| n_strx,
1191 },
1192 .where_index = rel.where_index,
1193 }) orelse {
1194 const sym = switch (rel.where) {
1195 .local => macho_file.locals.items[rel.where_index],
1196 .undef => macho_file.undefs.items[rel.where_index],
1197 };643 };
1198 log.err("expected GOT entry for symbol '{s}'", .{macho_file.getString(sym.n_strx)});644 log.err("expected GOT entry for symbol '{s}'", .{macho_file.getString(n_strx)});
1199 log.err(" this is an internal linker error", .{});645 log.err(" this is an internal linker error", .{});
1200 return error.FailedToResolveRelocationTarget;646 return error.FailedToResolveRelocationTarget;
1201 };647 };
1202 break :blk macho_file.locals.items[atom.local_sym_index].n_value;648 break :blk macho_file.locals.items[atom.local_sym_index].n_value;
1203 }649 }
1204650
1205 switch (rel.where) {651 switch (rel.target) {
1206 .local => {652 .local => |sym_index| {
1207 const sym = macho_file.locals.items[rel.where_index];653 const sym = macho_file.locals.items[sym_index];
1208 const is_tlv = is_tlv: {654 const is_tlv = is_tlv: {
1209 const source_sym = macho_file.locals.items[self.local_sym_index];655 const source_sym = macho_file.locals.items[self.local_sym_index];
1210 const match = macho_file.section_ordinals.keys()[source_sym.n_sect - 1];656 const match = macho_file.section_ordinals.keys()[source_sym.n_sect - 1];
...@@ -1233,28 +679,23 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -1233,28 +679,23 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
1233 };679 };
1234 break :blk sym.n_value - base_address;680 break :blk sym.n_value - base_address;
1235 }681 }
1236
1237 break :blk sym.n_value;682 break :blk sym.n_value;
1238 },683 },
1239 .undef => {684 .global => |n_strx| {
1240 const atom = macho_file.stubs_map.get(rel.where_index) orelse {685 // TODO Still trying to figure out how to possibly use stubs for local symbol indirection with
1241 // TODO this is required for incremental when we don't have every symbol686 // branching instructions. If it is not possible, then the best course of action is to
1242 // resolved when creating relocations. In this case, we will insert a branch687 // resurrect the former approach of defering creating synthethic atoms in __got and __la_symbol_ptr
1243 // reloc to an undef symbol which may happen to be defined within the binary.688 // sections until we resolve the relocations.
1244 // Then, the undef we point at will be a null symbol (free symbol) which we689 const resolv = macho_file.symbol_resolver.get(n_strx).?;
1245 // should remove/repurpose. To circumvent this (for now), we check if the symbol690 switch (resolv.where) {
1246 // we point to is garbage, and if so we fall back to symbol resolver to find by name.691 .global => break :blk macho_file.globals.items[resolv.where_index].n_value,
1247 const n_strx = macho_file.undefs.items[rel.where_index].n_strx;692 .undef => {
1248 if (macho_file.symbol_resolver.get(n_strx)) |resolv| inner: {693 break :blk if (macho_file.stubs_map.get(n_strx)) |atom|
1249 if (resolv.where != .global) break :inner;694 macho_file.locals.items[atom.local_sym_index].n_value
1250 break :blk macho_file.globals.items[resolv.where_index].n_value;695 else
1251 }696 0;
1252697 },
1253 // TODO verify in TextBlock that the symbol is indeed dynamically bound.698 }
1254 break :blk 0; // Dynamically bound by dyld.
1255 };
1256
1257 break :blk macho_file.locals.items[atom.local_sym_index].n_value;
1258 },699 },
1259 }700 }
1260 };701 };
...@@ -1262,67 +703,248 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -1262,67 +703,248 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
1262 log.debug(" | source_addr = 0x{x}", .{source_addr});703 log.debug(" | source_addr = 0x{x}", .{source_addr});
1263 log.debug(" | target_addr = 0x{x}", .{target_addr});704 log.debug(" | target_addr = 0x{x}", .{target_addr});
1264705
1265 try rel.resolve(.{706 switch (arch) {
1266 .block = self,707 .aarch64 => {
1267 .offset = rel.offset,708 switch (@intToEnum(macho.reloc_type_arm64, rel.@"type")) {
1268 .source_addr = source_addr,709 .ARM64_RELOC_BRANCH26 => {
1269 .target_addr = target_addr,710 const displacement = math.cast(
1270 .macho_file = macho_file,711 i28,
1271 });712 @intCast(i64, target_addr) - @intCast(i64, source_addr),
1272 }713 ) catch |err| switch (err) {
1273}714 error.Overflow => {
1274715 log.err("jump too big to encode as i28 displacement value", .{});
1275pub fn format(self: Atom, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {716 log.err(" (target - source) = displacement => 0x{x} - 0x{x} = 0x{x}", .{
1276 _ = fmt;717 target_addr,
1277 _ = options;718 source_addr,
1278 try std.fmt.format(writer, "TextBlock {{ ", .{});719 @intCast(i64, target_addr) - @intCast(i64, source_addr),
1279 try std.fmt.format(writer, ".local_sym_index = {d}, ", .{self.local_sym_index});720 });
1280 try std.fmt.format(writer, ".aliases = {any}, ", .{self.aliases.items});721 log.err(" TODO implement branch islands to extend jump distance for arm64", .{});
1281 try std.fmt.format(writer, ".contained = {any}, ", .{self.contained.items});722 return error.TODOImplementBranchIslands;
1282 try std.fmt.format(writer, ".code = {*}, ", .{self.code.items});723 },
1283 try std.fmt.format(writer, ".size = {d}, ", .{self.size});724 };
1284 try std.fmt.format(writer, ".alignment = {d}, ", .{self.alignment});725 const code = self.code.items[rel.offset..][0..4];
1285 try std.fmt.format(writer, ".relocs = {any}, ", .{self.relocs.items});726 var inst = aarch64.Instruction{
1286 try std.fmt.format(writer, ".rebases = {any}, ", .{self.rebases.items});727 .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload(
1287 try std.fmt.format(writer, ".bindings = {any}, ", .{self.bindings.items});728 aarch64.Instruction,
1288 try std.fmt.format(writer, ".dices = {any}, ", .{self.dices.items});729 aarch64.Instruction.unconditional_branch_immediate,
1289 if (self.stab) |stab| {730 ), code),
1290 try std.fmt.format(writer, ".stab = {any}, ", .{stab});731 };
1291 }732 inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2));
1292 try std.fmt.format(writer, "}}", .{});733 mem.writeIntLittle(u32, code, inst.toU32());
1293}734 },
735 .ARM64_RELOC_PAGE21,
736 .ARM64_RELOC_GOT_LOAD_PAGE21,
737 .ARM64_RELOC_TLVP_LOAD_PAGE21,
738 => {
739 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;
740 const source_page = @intCast(i32, source_addr >> 12);
741 const target_page = @intCast(i32, actual_target_addr >> 12);
742 const pages = @bitCast(u21, @intCast(i21, target_page - source_page));
743 const code = self.code.items[rel.offset..][0..4];
744 var inst = aarch64.Instruction{
745 .pc_relative_address = mem.bytesToValue(meta.TagPayload(
746 aarch64.Instruction,
747 aarch64.Instruction.pc_relative_address,
748 ), code),
749 };
750 inst.pc_relative_address.immhi = @truncate(u19, pages >> 2);
751 inst.pc_relative_address.immlo = @truncate(u2, pages);
752 mem.writeIntLittle(u32, code, inst.toU32());
753 },
754 .ARM64_RELOC_PAGEOFF12 => {
755 const code = self.code.items[rel.offset..][0..4];
756 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;
757 const narrowed = @truncate(u12, @intCast(u64, actual_target_addr));
758 if (isArithmeticOp(self.code.items[rel.offset..][0..4])) {
759 var inst = aarch64.Instruction{
760 .add_subtract_immediate = mem.bytesToValue(meta.TagPayload(
761 aarch64.Instruction,
762 aarch64.Instruction.add_subtract_immediate,
763 ), code),
764 };
765 inst.add_subtract_immediate.imm12 = narrowed;
766 mem.writeIntLittle(u32, code, inst.toU32());
767 } else {
768 var inst = aarch64.Instruction{
769 .load_store_register = mem.bytesToValue(meta.TagPayload(
770 aarch64.Instruction,
771 aarch64.Instruction.load_store_register,
772 ), code),
773 };
774 const offset: u12 = blk: {
775 if (inst.load_store_register.size == 0) {
776 if (inst.load_store_register.v == 1) {
777 // 128-bit SIMD is scaled by 16.
778 break :blk try math.divExact(u12, narrowed, 16);
779 }
780 // Otherwise, 8-bit SIMD or ldrb.
781 break :blk narrowed;
782 } else {
783 const denom: u4 = try math.powi(u4, 2, inst.load_store_register.size);
784 break :blk try math.divExact(u12, narrowed, denom);
785 }
786 };
787 inst.load_store_register.offset = offset;
788 mem.writeIntLittle(u32, code, inst.toU32());
789 }
790 },
791 .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => {
792 const code = self.code.items[rel.offset..][0..4];
793 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;
794 const narrowed = @truncate(u12, @intCast(u64, actual_target_addr));
795 var inst: aarch64.Instruction = .{
796 .load_store_register = mem.bytesToValue(meta.TagPayload(
797 aarch64.Instruction,
798 aarch64.Instruction.load_store_register,
799 ), code),
800 };
801 const offset = try math.divExact(u12, narrowed, 8);
802 inst.load_store_register.offset = offset;
803 mem.writeIntLittle(u32, code, inst.toU32());
804 },
805 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {
806 const code = self.code.items[rel.offset..][0..4];
807 const RegInfo = struct {
808 rd: u5,
809 rn: u5,
810 size: u1,
811 };
812 const reg_info: RegInfo = blk: {
813 if (isArithmeticOp(code)) {
814 const inst = mem.bytesToValue(meta.TagPayload(
815 aarch64.Instruction,
816 aarch64.Instruction.add_subtract_immediate,
817 ), code);
818 break :blk .{
819 .rd = inst.rd,
820 .rn = inst.rn,
821 .size = inst.sf,
822 };
823 } else {
824 const inst = mem.bytesToValue(meta.TagPayload(
825 aarch64.Instruction,
826 aarch64.Instruction.load_store_register,
827 ), code);
828 break :blk .{
829 .rd = inst.rt,
830 .rn = inst.rn,
831 .size = @truncate(u1, inst.size),
832 };
833 }
834 };
835 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;
836 const narrowed = @truncate(u12, @intCast(u64, actual_target_addr));
837 var inst = aarch64.Instruction{
838 .add_subtract_immediate = .{
839 .rd = reg_info.rd,
840 .rn = reg_info.rn,
841 .imm12 = narrowed,
842 .sh = 0,
843 .s = 0,
844 .op = 0,
845 .sf = reg_info.size,
846 },
847 };
848 mem.writeIntLittle(u32, code, inst.toU32());
849 },
850 .ARM64_RELOC_POINTER_TO_GOT => {
851 const result = try math.cast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr));
852 mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, result));
853 },
854 .ARM64_RELOC_UNSIGNED => {
855 const result = blk: {
856 if (rel.subtractor) |subtractor| {
857 const sym = macho_file.locals.items[subtractor];
858 break :blk @intCast(i64, target_addr) - @intCast(i64, sym.n_value) + rel.addend;
859 } else {
860 break :blk @intCast(i64, target_addr) + rel.addend;
861 }
862 };
1294863
1295const RelocIterator = struct {864 if (rel.length == 3) {
1296 buffer: []const macho.relocation_info,865 mem.writeIntLittle(u64, self.code.items[rel.offset..][0..8], @bitCast(u64, result));
1297 index: i32 = -1,866 } else {
867 mem.writeIntLittle(
868 u32,
869 self.code.items[rel.offset..][0..4],
870 @truncate(u32, @bitCast(u64, result)),
871 );
872 }
873 },
874 .ARM64_RELOC_SUBTRACTOR => unreachable,
875 .ARM64_RELOC_ADDEND => unreachable,
876 }
877 },
878 .x86_64 => {
879 switch (@intToEnum(macho.reloc_type_x86_64, rel.@"type")) {
880 .X86_64_RELOC_BRANCH => {
881 const displacement = try math.cast(
882 i32,
883 @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4,
884 );
885 mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, displacement));
886 },
887 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => {
888 const displacement = try math.cast(
889 i32,
890 @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4 + rel.addend,
891 );
892 mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, displacement));
893 },
894 .X86_64_RELOC_TLV => {
895 // We need to rewrite the opcode from movq to leaq.
896 self.code.items[rel.offset - 2] = 0x8d;
897 const displacement = try math.cast(
898 i32,
899 @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4 + rel.addend,
900 );
901 mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, displacement));
902 },
903 .X86_64_RELOC_SIGNED,
904 .X86_64_RELOC_SIGNED_1,
905 .X86_64_RELOC_SIGNED_2,
906 .X86_64_RELOC_SIGNED_4,
907 => {
908 const correction: u3 = switch (@intToEnum(macho.reloc_type_x86_64, rel.@"type")) {
909 .X86_64_RELOC_SIGNED => 0,
910 .X86_64_RELOC_SIGNED_1 => 1,
911 .X86_64_RELOC_SIGNED_2 => 2,
912 .X86_64_RELOC_SIGNED_4 => 4,
913 else => unreachable,
914 };
915 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;
916 const displacement = try math.cast(
917 i32,
918 actual_target_addr - @intCast(i64, source_addr + correction + 4),
919 );
920 mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, displacement));
921 },
922 .X86_64_RELOC_UNSIGNED => {
923 const result = blk: {
924 if (rel.subtractor) |subtractor| {
925 const sym = macho_file.locals.items[subtractor];
926 break :blk @intCast(i64, target_addr) - @intCast(i64, sym.n_value) + rel.addend;
927 } else {
928 break :blk @intCast(i64, target_addr) + rel.addend;
929 }
930 };
1298931
1299 pub fn next(self: *RelocIterator) ?macho.relocation_info {932 if (rel.length == 3) {
1300 self.index += 1;933 mem.writeIntLittle(u64, self.code.items[rel.offset..][0..8], @bitCast(u64, result));
1301 if (self.index < self.buffer.len) {934 } else {
1302 return self.buffer[@intCast(u32, self.index)];935 mem.writeIntLittle(
936 u32,
937 self.code.items[rel.offset..][0..4],
938 @truncate(u32, @bitCast(u64, result)),
939 );
940 }
941 },
942 .X86_64_RELOC_SUBTRACTOR => unreachable,
943 }
944 },
945 else => unreachable,
1303 }946 }
1304 return null;
1305 }947 }
1306
1307 pub fn peek(self: RelocIterator) macho.relocation_info {
1308 assert(self.index + 1 < self.buffer.len);
1309 return self.buffer[@intCast(u32, self.index + 1)];
1310 }
1311};
1312
1313fn filterRelocs(relocs: []macho.relocation_info, start_addr: u64, end_addr: u64) []macho.relocation_info {
1314 const Predicate = struct {
1315 addr: u64,
1316
1317 pub fn predicate(self: @This(), rel: macho.relocation_info) bool {
1318 return rel.r_address < self.addr;
1319 }
1320 };
1321
1322 const start = MachO.findFirst(macho.relocation_info, relocs, 0, Predicate{ .addr = end_addr });
1323 const end = MachO.findFirst(macho.relocation_info, relocs, start, Predicate{ .addr = start_addr });
1324
1325 return relocs[start..end];
1326}948}
1327949
1328inline fn isArithmeticOp(inst: *const [4]u8) bool {950inline fn isArithmeticOp(inst: *const [4]u8) bool {
src/link/MachO/Dylib.zig+1-1
...@@ -4,7 +4,7 @@ const std = @import("std");...@@ -4,7 +4,7 @@ const std = @import("std");
4const assert = std.debug.assert;4const assert = std.debug.assert;
5const fs = std.fs;5const fs = std.fs;
6const fmt = std.fmt;6const fmt = std.fmt;
7const log = std.log.scoped(.dylib);7const log = std.log.scoped(.link);
8const macho = std.macho;8const macho = std.macho;
9const math = std.math;9const math = std.math;
10const mem = std.mem;10const mem = std.mem;
src/link/MachO/Object.zig+8-3
...@@ -6,7 +6,7 @@ const assert = std.debug.assert;...@@ -6,7 +6,7 @@ const assert = std.debug.assert;
6const dwarf = std.dwarf;6const dwarf = std.dwarf;
7const fs = std.fs;7const fs = std.fs;
8const io = std.io;8const io = std.io;
9const log = std.log.scoped(.object);9const log = std.log.scoped(.link);
10const macho = std.macho;10const macho = std.macho;
11const math = std.math;11const math = std.math;
12const mem = std.mem;12const mem = std.mem;
...@@ -458,9 +458,15 @@ pub fn parseIntoAtoms(self: *Object, allocator: *Allocator, macho_file: *MachO)...@@ -458,9 +458,15 @@ pub fn parseIntoAtoms(self: *Object, allocator: *Allocator, macho_file: *MachO)
458 // a temp one, unless we already did that when working out the relocations458 // a temp one, unless we already did that when working out the relocations
459 // of other atoms.459 // of other atoms.
460 const atom_local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {460 const atom_local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {
461 const sym_name = try std.fmt.allocPrint(allocator, "{s}_{s}_{s}", .{
462 self.name,
463 segmentName(sect),
464 sectionName(sect),
465 });
466 defer allocator.free(sym_name);
461 const atom_local_sym_index = @intCast(u32, macho_file.locals.items.len);467 const atom_local_sym_index = @intCast(u32, macho_file.locals.items.len);
462 try macho_file.locals.append(allocator, .{468 try macho_file.locals.append(allocator, .{
463 .n_strx = 0,469 .n_strx = try macho_file.makeString(sym_name),
464 .n_type = macho.N_SECT,470 .n_type = macho.N_SECT,
465 .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1),471 .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1),
466 .n_desc = 0,472 .n_desc = 0,
...@@ -481,7 +487,6 @@ pub fn parseIntoAtoms(self: *Object, allocator: *Allocator, macho_file: *MachO)...@@ -481,7 +487,6 @@ pub fn parseIntoAtoms(self: *Object, allocator: *Allocator, macho_file: *MachO)
481487
482 try atom.parseRelocs(relocs, .{488 try atom.parseRelocs(relocs, .{
483 .base_addr = sect.addr,489 .base_addr = sect.addr,
484 .base_offset = 0,
485 .allocator = allocator,490 .allocator = allocator,
486 .object = self,491 .object = self,
487 .macho_file = macho_file,492 .macho_file = macho_file,