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 {
30183018 }
30193019 } else if (func_value.castTag(.extern_fn)) |func_payload| {
30203020 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));
30223022 const offset = blk: {
30233023 switch (arch) {
30243024 .x86_64 => {
......@@ -3039,14 +3039,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
30393039 // Add relocation to the decl.
30403040 try macho_file.active_decl.?.link.macho.relocs.append(self.bin_file.allocator, .{
30413041 .offset = offset,
3042 .where = switch (resolv.where) {
3043 .local => .local,
3044 .undef => .undef,
3042 .target = .{ .global = n_strx },
3043 .addend = 0,
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,
30453051 },
3046 .where_index = resolv.where_index,
3047 .payload = .{ .branch = .{
3048 .arch = arch,
3049 } },
30503052 });
30513053 } else {
30523054 return self.fail("TODO implement calling bitcasted functions", .{});
......@@ -4540,16 +4542,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
45404542 // Page reloc for adrp instruction.
45414543 try decl.link.macho.relocs.append(self.bin_file.allocator, .{
45424544 .offset = offset,
4543 .where = .local,
4544 .where_index = @intCast(u32, addr),
4545 .payload = .{ .page = .{ .kind = .got } },
4545 .target = .{ .local = @intCast(u32, addr) },
4546 .addend = 0,
4547 .subtractor = null,
4548 .pcrel = true,
4549 .length = 2,
4550 .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGE21),
45464551 });
45474552 // Pageoff reloc for adrp instruction.
45484553 try decl.link.macho.relocs.append(self.bin_file.allocator, .{
45494554 .offset = offset + 4,
4550 .where = .local,
4551 .where_index = @intCast(u32, addr),
4552 .payload = .{ .page_off = .{ .kind = .got } },
4555 .target = .{ .local = @intCast(u32, addr) },
4556 .addend = 0,
4557 .subtractor = null,
4558 .pcrel = false,
4559 .length = 2,
4560 .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGEOFF12),
45534561 });
45544562 } else {
45554563 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 {
48144822 // Load reloc for LEA instruction.
48154823 try decl.link.macho.relocs.append(self.bin_file.allocator, .{
48164824 .offset = offset - 4,
4817 .where = .local,
4818 .where_index = @intCast(u32, x),
4819 .payload = .{ .load = .{ .kind = .got } },
4825 .target = .{ .local = @intCast(u32, x) },
4826 .addend = 0,
4827 .subtractor = null,
4828 .pcrel = true,
4829 .length = 2,
4830 .@"type" = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT),
48204831 });
48214832 } else {
48224833 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,
161161strtab: std.ArrayListUnmanaged(u8) = .{},
162162strtab_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) = .{},
165165stubs_map: std.AutoArrayHashMapUnmanaged(u32, *Atom) = .{},
166166
167167error_flags: File.ErrorFlags = File.ErrorFlags{},
......@@ -234,14 +234,6 @@ const SymbolWithLoc = struct {
234234 file: ?u16 = null, // null means Zig module
235235};
236236
237pub const GotIndirectionKey = struct {
238 where: enum {
239 local,
240 undef,
241 },
242 where_index: u32,
243};
244
245237/// When allocating, the ideal_capacity is calculated by
246238/// actual_capacity + (actual_capacity / ideal_factor)
247239const ideal_factor = 4;
......@@ -842,26 +834,6 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {
842834 try self.createDsoHandleAtom();
843835 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
865837 for (self.unresolved.keys()) |index| {
866838 const sym = self.undefs.items[index];
867839 const sym_name = self.getString(sym.n_strx);
......@@ -879,6 +851,27 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {
879851 try self.createTentativeDefAtoms();
880852 try self.parseObjectsIntoAtoms();
881853 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
882875 try self.writeAtoms();
883876
884877 if (self.bss_section_index) |idx| {
......@@ -1866,7 +1859,7 @@ fn writeAtoms(self: *MachO) !void {
18661859 }
18671860}
18681861
1869pub fn createGotAtom(self: *MachO, key: GotIndirectionKey) !*Atom {
1862pub fn createGotAtom(self: *MachO, target: Atom.Relocation.Target) !*Atom {
18701863 const local_sym_index = @intCast(u32, self.locals.items.len);
18711864 try self.locals.append(self.base.allocator, .{
18721865 .n_strx = 0,
......@@ -1876,25 +1869,26 @@ pub fn createGotAtom(self: *MachO, key: GotIndirectionKey) !*Atom {
18761869 .n_value = 0,
18771870 });
18781871 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) {
18801886 .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 });
18931887 try atom.rebases.append(self.base.allocator, 0);
18941888 },
1895 .undef => {
1889 .global => |n_strx| {
18961890 try atom.bindings.append(self.base.allocator, .{
1897 .local_sym_index = key.where_index,
1891 .n_strx = n_strx,
18981892 .offset = 0,
18991893 });
19001894 },
......@@ -1958,14 +1952,12 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
19581952 atom.code.items[2] = 0x1d;
19591953 atom.relocs.appendAssumeCapacity(.{
19601954 .offset = 3,
1961 .where = .local,
1962 .where_index = dyld_private_sym_index,
1963 .payload = .{
1964 .signed = .{
1965 .addend = 0,
1966 .correction = 0,
1967 },
1968 },
1955 .target = .{ .local = dyld_private_sym_index },
1956 .addend = 0,
1957 .subtractor = null,
1958 .pcrel = true,
1959 .length = 2,
1960 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
19691961 });
19701962 // push %r11
19711963 atom.code.items[7] = 0x41;
......@@ -1975,14 +1967,12 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
19751967 atom.code.items[10] = 0x25;
19761968 atom.relocs.appendAssumeCapacity(.{
19771969 .offset = 11,
1978 .where = .undef,
1979 .where_index = self.dyld_stub_binder_index.?,
1980 .payload = .{
1981 .load = .{
1982 .kind = .got,
1983 .addend = 0,
1984 },
1985 },
1970 .target = .{ .global = self.undefs.items[self.dyld_stub_binder_index.?].n_strx },
1971 .addend = 0,
1972 .subtractor = null,
1973 .pcrel = true,
1974 .length = 2,
1975 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_GOT),
19861976 });
19871977 },
19881978 .aarch64 => {
......@@ -1991,28 +1981,23 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
19911981 mem.writeIntLittle(u32, atom.code.items[0..][0..4], aarch64.Instruction.adrp(.x17, 0).toU32());
19921982 atom.relocs.appendAssumeCapacity(.{
19931983 .offset = 0,
1994 .where = .local,
1995 .where_index = dyld_private_sym_index,
1996 .payload = .{
1997 .page = .{
1998 .kind = .page,
1999 .addend = 0,
2000 },
2001 },
1984 .target = .{ .local = dyld_private_sym_index },
1985 .addend = 0,
1986 .subtractor = null,
1987 .pcrel = true,
1988 .length = 2,
1989 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
20021990 });
20031991 // add x17, x17, 0
20041992 mem.writeIntLittle(u32, atom.code.items[4..][0..4], aarch64.Instruction.add(.x17, .x17, 0, false).toU32());
20051993 atom.relocs.appendAssumeCapacity(.{
20061994 .offset = 4,
2007 .where = .local,
2008 .where_index = dyld_private_sym_index,
2009 .payload = .{
2010 .page_off = .{
2011 .kind = .page,
2012 .addend = 0,
2013 .op_kind = .arithmetic,
2014 },
2015 },
1995 .target = .{ .local = dyld_private_sym_index },
1996 .addend = 0,
1997 .subtractor = null,
1998 .pcrel = false,
1999 .length = 2,
2000 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGEOFF12),
20162001 });
20172002 // stp x16, x17, [sp, #-16]!
20182003 mem.writeIntLittle(u32, atom.code.items[8..][0..4], aarch64.Instruction.stp(
......@@ -2025,14 +2010,12 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
20252010 mem.writeIntLittle(u32, atom.code.items[12..][0..4], aarch64.Instruction.adrp(.x16, 0).toU32());
20262011 atom.relocs.appendAssumeCapacity(.{
20272012 .offset = 12,
2028 .where = .undef,
2029 .where_index = self.dyld_stub_binder_index.?,
2030 .payload = .{
2031 .page = .{
2032 .kind = .got,
2033 .addend = 0,
2034 },
2035 },
2013 .target = .{ .global = self.undefs.items[self.dyld_stub_binder_index.?].n_strx },
2014 .addend = 0,
2015 .subtractor = null,
2016 .pcrel = true,
2017 .length = 2,
2018 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGE21),
20362019 });
20372020 // ldr x16, [x16, 0]
20382021 mem.writeIntLittle(u32, atom.code.items[16..][0..4], aarch64.Instruction.ldr(.x16, .{
......@@ -2043,14 +2026,12 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
20432026 }).toU32());
20442027 atom.relocs.appendAssumeCapacity(.{
20452028 .offset = 16,
2046 .where = .undef,
2047 .where_index = self.dyld_stub_binder_index.?,
2048 .payload = .{
2049 .page_off = .{
2050 .kind = .got,
2051 .addend = 0,
2052 },
2053 },
2029 .target = .{ .global = self.undefs.items[self.dyld_stub_binder_index.?].n_strx },
2030 .addend = 0,
2031 .subtractor = null,
2032 .pcrel = false,
2033 .length = 2,
2034 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGEOFF12),
20542035 });
20552036 // br x16
20562037 mem.writeIntLittle(u32, atom.code.items[20..][0..4], aarch64.Instruction.br(.x16).toU32());
......@@ -2101,11 +2082,12 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
21012082 atom.code.items[5] = 0xe9;
21022083 atom.relocs.appendAssumeCapacity(.{
21032084 .offset = 6,
2104 .where = .local,
2105 .where_index = self.stub_helper_preamble_atom.?.local_sym_index,
2106 .payload = .{
2107 .branch = .{ .arch = arch },
2108 },
2085 .target = .{ .local = self.stub_helper_preamble_atom.?.local_sym_index },
2086 .addend = 0,
2087 .subtractor = null,
2088 .pcrel = true,
2089 .length = 2,
2090 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
21092091 });
21102092 },
21112093 .aarch64 => {
......@@ -2121,11 +2103,12 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
21212103 mem.writeIntLittle(u32, atom.code.items[4..8], aarch64.Instruction.b(0).toU32());
21222104 atom.relocs.appendAssumeCapacity(.{
21232105 .offset = 4,
2124 .where = .local,
2125 .where_index = self.stub_helper_preamble_atom.?.local_sym_index,
2126 .payload = .{
2127 .branch = .{ .arch = arch },
2128 },
2106 .target = .{ .local = self.stub_helper_preamble_atom.?.local_sym_index },
2107 .addend = 0,
2108 .subtractor = null,
2109 .pcrel = true,
2110 .length = 2,
2111 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
21292112 });
21302113 // Next 4 bytes 8..12 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
21312114 },
......@@ -2135,7 +2118,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
21352118 return atom;
21362119}
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 {
21392122 const local_sym_index = @intCast(u32, self.locals.items.len);
21402123 try self.locals.append(self.base.allocator, .{
21412124 .n_strx = 0,
......@@ -2147,19 +2130,20 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, lazy_binding_sym
21472130 const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), 3);
21482131 try atom.relocs.append(self.base.allocator, .{
21492132 .offset = 0,
2150 .where = .local,
2151 .where_index = stub_sym_index,
2152 .payload = .{
2153 .unsigned = .{
2154 .subtractor = null,
2155 .addend = 0,
2156 .is_64bit = true,
2157 },
2133 .target = .{ .local = stub_sym_index },
2134 .addend = 0,
2135 .subtractor = null,
2136 .pcrel = false,
2137 .length = 3,
2138 .@"type" = switch (self.base.options.target.cpu.arch) {
2139 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
2140 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
2141 else => unreachable,
21582142 },
21592143 });
21602144 try atom.rebases.append(self.base.allocator, 0);
21612145 try atom.lazy_bindings.append(self.base.allocator, .{
2162 .local_sym_index = lazy_binding_sym_index,
2146 .n_strx = n_strx,
21632147 .offset = 0,
21642148 });
21652149 return atom;
......@@ -2193,11 +2177,12 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
21932177 atom.code.items[1] = 0x25;
21942178 try atom.relocs.append(self.base.allocator, .{
21952179 .offset = 2,
2196 .where = .local,
2197 .where_index = laptr_sym_index,
2198 .payload = .{
2199 .branch = .{ .arch = arch },
2200 },
2180 .target = .{ .local = laptr_sym_index },
2181 .addend = 0,
2182 .subtractor = null,
2183 .pcrel = true,
2184 .length = 2,
2185 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
22012186 });
22022187 },
22032188 .aarch64 => {
......@@ -2206,14 +2191,12 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
22062191 mem.writeIntLittle(u32, atom.code.items[0..4], aarch64.Instruction.adrp(.x16, 0).toU32());
22072192 atom.relocs.appendAssumeCapacity(.{
22082193 .offset = 0,
2209 .where = .local,
2210 .where_index = laptr_sym_index,
2211 .payload = .{
2212 .page = .{
2213 .kind = .page,
2214 .addend = 0,
2215 },
2216 },
2194 .target = .{ .local = laptr_sym_index },
2195 .addend = 0,
2196 .subtractor = null,
2197 .pcrel = true,
2198 .length = 2,
2199 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
22172200 });
22182201 // ldr x16, x16, offset
22192202 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 {
22242207 }).toU32());
22252208 atom.relocs.appendAssumeCapacity(.{
22262209 .offset = 4,
2227 .where = .local,
2228 .where_index = laptr_sym_index,
2229 .payload = .{
2230 .page_off = .{
2231 .kind = .page,
2232 .addend = 0,
2233 .op_kind = .load,
2234 },
2235 },
2210 .target = .{ .local = laptr_sym_index },
2211 .addend = 0,
2212 .subtractor = null,
2213 .pcrel = false,
2214 .length = 2,
2215 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGEOFF12),
22362216 });
22372217 // br x16
22382218 mem.writeIntLittle(u32, atom.code.items[8..12], aarch64.Instruction.br(.x16).toU32());
......@@ -2578,7 +2558,7 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
25782558 .none => {},
25792559 .got => return error.TODOGotHint,
25802560 .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;
25822562 const stub_helper_atom = blk: {
25832563 const match = MatchingSection{
25842564 .seg = self.text_segment_cmd_index.?,
......@@ -2599,7 +2579,7 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
25992579 };
26002580 const atom = try self.createLazyPointerAtom(
26012581 stub_helper_atom.local_sym_index,
2602 resolv.where_index,
2582 sym.n_strx,
26032583 );
26042584 const atom_sym = &self.locals.items[atom.local_sym_index];
26052585 const alignment = try math.powi(u32, 2, atom.alignment);
......@@ -2621,7 +2601,7 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
26212601 atom_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
26222602 break :blk atom;
26232603 };
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);
26252605 },
26262606 }
26272607 }
......@@ -2675,12 +2655,9 @@ fn resolveDyldStubBinder(self: *MachO) !void {
26752655 }
26762656
26772657 // Add dyld_stub_binder as the final GOT entry.
2678 const got_entry = GotIndirectionKey{
2679 .where = .undef,
2680 .where_index = self.dyld_stub_binder_index.?,
2681 };
2682 const atom = try self.createGotAtom(got_entry);
2683 try self.got_entries_map.putNoClobber(self.base.allocator, got_entry, atom);
2658 const target = Atom.Relocation.Target{ .global = n_strx };
2659 const atom = try self.createGotAtom(target);
2660 try self.got_entries_map.putNoClobber(self.base.allocator, target, atom);
26842661 const match = MatchingSection{
26852662 .seg = self.data_const_segment_cmd_index.?,
26862663 .sect = self.got_section_index.?,
......@@ -3081,12 +3058,9 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
30813058 };
30823059
30833060 // TODO try popping from free list first before allocating a new GOT atom.
3084 const key = GotIndirectionKey{
3085 .where = .local,
3086 .where_index = decl.link.macho.local_sym_index,
3087 };
3088 const got_atom = try self.createGotAtom(key);
3089 try self.got_entries_map.put(self.base.allocator, key, got_atom);
3061 const target = Atom.Relocation.Target{ .local = decl.link.macho.local_sym_index };
3062 const got_atom = try self.createGotAtom(target);
3063 try self.got_entries_map.put(self.base.allocator, target, got_atom);
30903064}
30913065
30923066pub 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
32673241
32683242 if (vaddr != symbol.n_value) {
32693243 log.debug(" (writing new GOT entry)", .{});
3270 const got_atom = self.got_entries_map.get(.{
3271 .where = .local,
3272 .where_index = decl.link.macho.local_sym_index,
3273 }) orelse unreachable;
3244 const got_atom = self.got_entries_map.get(.{ .local = decl.link.macho.local_sym_index }).?;
32743245 const got_sym = &self.locals.items[got_atom.local_sym_index];
32753246 const got_vaddr = try self.allocateAtom(got_atom, @sizeOf(u64), 8, .{
32763247 .seg = self.data_const_segment_cmd_index.?,
......@@ -3324,10 +3295,7 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64
33243295 .n_desc = 0,
33253296 .n_value = addr,
33263297 };
3327 const got_atom = self.got_entries_map.get(.{
3328 .where = .local,
3329 .where_index = decl.link.macho.local_sym_index,
3330 }) orelse unreachable;
3298 const got_atom = self.got_entries_map.get(.{ .local = decl.link.macho.local_sym_index }).?;
33313299 const got_sym = &self.locals.items[got_atom.local_sym_index];
33323300 const vaddr = try self.allocateAtom(got_atom, @sizeOf(u64), 8, .{
33333301 .seg = self.data_const_segment_cmd_index.?,
......@@ -4357,51 +4325,29 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, m
43574325 return vaddr;
43584326}
43594327
4360const AddExternFnRes = struct {
4361 where: enum {
4362 local,
4363 undef,
4364 },
4365 where_index: u32,
4366};
4367
4368pub fn addExternFn(self: *MachO, name: []const u8) !AddExternFnRes {
4328pub fn addExternFn(self: *MachO, name: []const u8) !u32 {
43694329 const sym_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{name});
43704330 defer self.base.allocator.free(sym_name);
43714331 const n_strx = try self.makeString(sym_name);
43724332
4373 if (self.symbol_resolver.get(n_strx)) |resolv| {
4374 return switch (resolv.where) {
4375 .global => AddExternFnRes{
4376 .where = .local,
4377 .where_index = resolv.local_sym_index,
4378 },
4379 .undef => AddExternFnRes{
4380 .where = .undef,
4381 .where_index = resolv.where_index,
4382 },
4383 };
4333 if (!self.symbol_resolver.contains(n_strx)) {
4334 log.debug("adding new extern function '{s}'", .{sym_name});
4335 const sym_index = @intCast(u32, self.undefs.items.len);
4336 try self.undefs.append(self.base.allocator, .{
4337 .n_strx = n_strx,
4338 .n_type = macho.N_UNDF,
4339 .n_sect = 0,
4340 .n_desc = 0,
4341 .n_value = 0,
4342 });
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);
43844348 }
43854349
4386 log.debug("adding new extern function '{s}'", .{sym_name});
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 };
4350 return n_strx;
44054351}
44064352
44074353const NextSegmentAddressAndOffset = struct {
......@@ -4496,23 +4442,47 @@ fn writeDyldInfoData(self: *MachO) !void {
44964442 }
44974443
44984444 for (atom.bindings.items) |binding| {
4499 const bind_sym = self.undefs.items[binding.local_sym_index];
4500 try bind_pointers.append(.{
4501 .offset = binding.offset + base_offset,
4502 .segment_id = match.seg,
4503 .dylib_ordinal = @divExact(bind_sym.n_desc, macho.N_SYMBOL_RESOLVER),
4504 .name = self.getString(bind_sym.n_strx),
4505 });
4445 const resolv = self.symbol_resolver.get(binding.n_strx).?;
4446 switch (resolv.where) {
4447 .global => {
4448 // Turn into a rebase.
4449 try rebase_pointers.append(.{
4450 .offset = base_offset + binding.offset,
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 }
45064464 }
45074465
45084466 for (atom.lazy_bindings.items) |binding| {
4509 const bind_sym = self.undefs.items[binding.local_sym_index];
4510 try lazy_bind_pointers.append(.{
4511 .offset = binding.offset + base_offset,
4512 .segment_id = match.seg,
4513 .dylib_ordinal = @divExact(bind_sym.n_desc, macho.N_SYMBOL_RESOLVER),
4514 .name = self.getString(bind_sym.n_strx),
4515 });
4467 const resolv = self.symbol_resolver.get(binding.n_strx).?;
4468 switch (resolv.where) {
4469 .global => {
4470 // Turn into a rebase.
4471 try rebase_pointers.append(.{
4472 .offset = base_offset + binding.offset,
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 }
45164486 }
45174487
45184488 if (atom.prev) |prev| {
......@@ -4607,19 +4577,37 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
46074577 }) orelse return;
46084578 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),
4611 // we need reverse the order of atom traversal here as well.
4612 // TODO figure out a less error prone mechanisms for this!
4613 var atom = last_atom;
4614 while (atom.prev) |prev| {
4615 atom = prev;
4580 var table = std.AutoHashMap(i64, *Atom).init(self.base.allocator);
4581 defer table.deinit();
4582
4583 {
4584 var stub_atom = last_atom;
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 }
46164605 }
4617 atom = atom.next.?;
46184606
46194607 var stream = std.io.fixedBufferStream(buffer);
46204608 var reader = stream.reader();
4621 var offsets = std.ArrayList(u32).init(self.base.allocator);
4622 try offsets.append(0);
4609 var offsets = std.ArrayList(struct { sym_offset: i64, offset: u32 }).init(self.base.allocator);
4610 try offsets.append(.{ .sym_offset = undefined, .offset = 0 });
46234611 defer offsets.deinit();
46244612 var valid_block = false;
46254613
......@@ -4637,7 +4625,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
46374625 macho.BIND_OPCODE_DONE => {
46384626 if (valid_block) {
46394627 const offset = try stream.getPos();
4640 try offsets.append(@intCast(u32, offset));
4628 try offsets.append(.{ .sym_offset = undefined, .offset = @intCast(u32, offset) });
46414629 }
46424630 valid_block = false;
46434631 },
......@@ -4648,7 +4636,9 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
46484636 }
46494637 },
46504638 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);
46524642 },
46534643 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
46544644 _ = try std.leb.readULEB128(u64, reader);
......@@ -4660,8 +4650,10 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
46604650 }
46614651 }
46624652
4663 const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
4664 const sect = seg.sections.items[self.stub_helper_section_index.?];
4653 const sect = blk: {
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 };
46654657 const stub_offset: u4 = switch (self.base.options.target.cpu.arch) {
46664658 .x86_64 => 1,
46674659 .aarch64 => 2 * @sizeOf(u32),
......@@ -4669,20 +4661,18 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
46694661 };
46704662 var buf: [@sizeOf(u32)]u8 = undefined;
46714663 _ = offsets.pop();
4664
46724665 while (offsets.popOrNull()) |bind_offset| {
4666 const atom = table.get(bind_offset.sym_offset).?;
46734667 const sym = self.locals.items[atom.local_sym_index];
46744668 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);
46764670 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,
46784672 self.getString(sym.n_strx),
46794673 file_offset,
46804674 });
46814675 try self.base.file.?.pwriteAll(&buf, file_offset);
4682
4683 if (atom.next) |next| {
4684 atom = next;
4685 } else break;
46864676 }
46874677}
46884678
......@@ -4875,24 +4865,34 @@ fn writeSymbolTable(self: *MachO) !void {
48754865
48764866 stubs.reserved1 = 0;
48774867 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 }
48794873 }
48804874
48814875 got.reserved1 = nstubs;
48824876 for (self.got_entries_map.keys()) |key| {
4883 switch (key.where) {
4884 .undef => {
4885 try writer.writeIntLittle(u32, dysymtab.iundefsym + key.where_index);
4886 },
4887 .local => {
4888 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
4877 switch (key) {
4878 .local => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL),
4879 .global => |n_strx| {
4880 const resolv = self.symbol_resolver.get(n_strx).?;
4881 switch (resolv.where) {
4882 .global => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL),
4883 .undef => try writer.writeIntLittle(u32, dysymtab.iundefsym + resolv.where_index),
4884 }
48894885 },
48904886 }
48914887 }
48924888
48934889 la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries;
48944890 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 }
48964896 }
48974897
48984898 try self.base.file.?.pwriteAll(buf, dysymtab.indirectsymoff);
src/link/MachO/Archive.zig+1-1
......@@ -3,7 +3,7 @@ const Archive = @This();
33const std = @import("std");
44const assert = std.debug.assert;
55const fs = std.fs;
6const log = std.log.scoped(.archive);
6const log = std.log.scoped(.link);
77const macho = std.macho;
88const mem = std.mem;
99const fat = @import("fat.zig");
src/link/MachO/Atom.zig+580-958
......@@ -5,7 +5,7 @@ const build_options = @import("build_options");
55const aarch64 = @import("../../arch/aarch64/bits.zig");
66const assert = std.debug.assert;
77const commands = @import("commands.zig");
8const log = std.log.scoped(.text_block);
8const log = std.log.scoped(.link);
99const macho = std.macho;
1010const math = std.math;
1111const mem = std.mem;
......@@ -54,10 +54,10 @@ rebases: std.ArrayListUnmanaged(u64) = .{},
5454/// List of offsets contained within this atom that will be dynamically bound
5555/// by the dynamic loader and contain pointers to resolved (at load time) extern
5656/// symbols (aka proxies aka imports)
57bindings: std.ArrayListUnmanaged(SymbolAtOffset) = .{},
57bindings: std.ArrayListUnmanaged(Binding) = .{},
5858
5959/// List of lazy bindings
60lazy_bindings: std.ArrayListUnmanaged(SymbolAtOffset) = .{},
60lazy_bindings: std.ArrayListUnmanaged(Binding) = .{},
6161
6262/// List of data-in-code entries. This is currently specific to x86_64 only.
6363dices: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
......@@ -83,25 +83,15 @@ dbg_info_len: u32,
8383
8484dirty: bool = true,
8585
86pub const Binding = struct {
87 n_strx: u32,
88 offset: u64,
89};
90
8691pub const SymbolAtOffset = struct {
8792 local_sym_index: u32,
8893 offset: u64,
8994 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 }
10595};
10696
10797pub const Stab = union(enum) {
......@@ -171,411 +161,26 @@ pub const Stab = union(enum) {
171161};
172162
173163pub const Relocation = struct {
164 pub const Target = union(enum) {
165 local: u32,
166 global: u32,
167 };
168
174169 /// Offset within the atom's code buffer.
175170 /// Note relocation size can be inferred by relocation's kind.
176171 offset: u32,
177172
178 where: enum {
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 };
173 target: Target,
295174
296 pub const Page = struct {
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,
175 addend: i64,
526176
527 pub fn resolve(self: Load, args: ResolveArgs) !void {
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 }
177 subtractor: ?u32,
538178
539 pub fn format(self: Load, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
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 };
179 pcrel: bool,
548180
549 pub fn resolve(self: Relocation, args: ResolveArgs) !void {
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 }
181 length: u2,
560182
561 pub fn format(self: Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
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 }
183 @"type": u4,
579184};
580185
581186pub const empty = Atom{
......@@ -641,526 +246,367 @@ pub fn freeListEligible(self: Atom, macho_file: MachO) bool {
641246
642247const RelocContext = struct {
643248 base_addr: u64 = 0,
644 base_offset: u64 = 0,
645249 allocator: *Allocator,
646250 object: *Object,
647251 macho_file: *MachO,
648252};
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
708254pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocContext) !void {
709255 const tracy = trace(@src());
710256 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;
719258 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| {
722 if (isAddend(rel, arch)) {
723 // Addend is not a relocation with effect on the TextBlock, so
724 // parse it and carry on.
725 assert(addend == 0); // Oh no, addend was not reset!
726 addend = rel.r_symbolnum;
727
728 // Verify ADDEND is followed by a PAGE21 or PAGEOFF12.
729 const next = @intToEnum(macho.reloc_type_arm64, it.peek().r_type);
730 switch (next) {
731 .ARM64_RELOC_PAGE21, .ARM64_RELOC_PAGEOFF12 => {},
732 else => {
733 log.err("unexpected relocation type: expected PAGE21 or PAGEOFF12, found {s}", .{next});
734 return error.UnexpectedRelocationType;
262 for (relocs) |rel, i| {
263 blk: {
264 switch (arch) {
265 .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
266 .ARM64_RELOC_ADDEND => {
267 assert(addend == 0);
268 addend = rel.r_symbolnum;
269 // Verify that it's followed by ARM64_RELOC_PAGE21 or ARM64_RELOC_PAGEOFF12.
270 if (relocs.len <= i + 1) {
271 log.err("no relocation after ARM64_RELOC_ADDEND", .{});
272 return error.UnexpectedRelocationType;
273 }
274 const next = @intToEnum(macho.reloc_type_arm64, relocs[i + 1].r_type);
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,
735292 },
293 else => unreachable,
736294 }
737 continue;
738 }
739295
740 if (isSubtractor(rel, arch)) {
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);
296 assert(subtractor == null);
745297 const sym = context.object.symtab.items[rel.r_symbolnum];
746 const sym_name = context.object.getString(sym.n_strx);
747
748298 if (MachO.symbolIsSect(sym) and !MachO.symbolIsExt(sym)) {
749 const where_index = context.object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable;
750 subtractor = where_index;
299 subtractor = context.object.symbol_mapping.get(rel.r_symbolnum).?;
751300 } else {
752 const n_strx = context.macho_file.strtab_dir.getKeyAdapted(@as([]const u8, sym_name), StringIndexAdapter{
753 .bytes = &context.macho_file.strtab,
754 }) orelse unreachable;
755 const resolv = context.macho_file.symbol_resolver.get(n_strx) orelse unreachable;
301 const sym_name = context.object.getString(sym.n_strx);
302 const n_strx = context.macho_file.strtab_dir.getKeyAdapted(
303 @as([]const u8, sym_name),
304 StringIndexAdapter{
305 .bytes = &context.macho_file.strtab,
306 },
307 ).?;
308 const resolv = context.macho_file.symbol_resolver.get(n_strx).?;
756309 assert(resolv.where == .global);
757310 subtractor = resolv.local_sym_index;
758311 }
759
760 // Verify SUBTRACTOR is followed by UNSIGNED.
312 // Verify that *_SUBTRACTOR is followed by *_UNSIGNED.
313 if (relocs.len <= i + 1) {
314 log.err("no relocation after *_RELOC_SUBTRACTOR", .{});
315 return error.UnexpectedRelocationType;
316 }
761317 switch (arch) {
762 .aarch64 => {
763 const next = @intToEnum(macho.reloc_type_arm64, it.peek().r_type);
764 if (next != .ARM64_RELOC_UNSIGNED) {
765 log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next});
318 .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, relocs[i + 1].r_type)) {
319 .ARM64_RELOC_UNSIGNED => {},
320 else => {
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)});
766324 return error.UnexpectedRelocationType;
767 }
325 },
768326 },
769 .x86_64 => {
770 const next = @intToEnum(macho.reloc_type_x86_64, it.peek().r_type);
771 if (next != .X86_64_RELOC_UNSIGNED) {
772 log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next});
327 .x86_64 => switch (@intToEnum(macho.reloc_type_x86_64, relocs[i + 1].r_type)) {
328 .X86_64_RELOC_UNSIGNED => {},
329 else => {
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)});
773333 return error.UnexpectedRelocationType;
774 }
334 },
775335 },
776336 else => unreachable,
777337 }
778338 continue;
779339 }
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
783386 switch (arch) {
784387 .aarch64 => {
785 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
786 switch (rel_type) {
787 .ARM64_RELOC_ADDEND => unreachable,
788 .ARM64_RELOC_SUBTRACTOR => unreachable,
388 switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
789389 .ARM64_RELOC_BRANCH26 => {
790 self.parseBranch(rel, &parsed_rel, context);
791 },
792 .ARM64_RELOC_UNSIGNED => {
793 self.parseUnsigned(rel, &parsed_rel, subtractor, context);
794 subtractor = null;
390 // TODO rewrite relocation
391 try addStub(target, context);
795392 },
796 .ARM64_RELOC_PAGE21,
797 .ARM64_RELOC_GOT_LOAD_PAGE21,
798 .ARM64_RELOC_TLVP_LOAD_PAGE21,
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;
393 .ARM64_RELOC_GOT_LOAD_PAGE21, .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => {
394 // TODO rewrite relocation
395 try addGotEntry(target, context);
811396 },
812 .ARM64_RELOC_POINTER_TO_GOT => {
813 self.parsePointerToGot(rel, &parsed_rel);
397 .ARM64_RELOC_UNSIGNED => {
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);
814404 },
405 else => {},
815406 }
816407 },
817408 .x86_64 => {
818 switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) {
819 .X86_64_RELOC_SUBTRACTOR => unreachable,
409 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
410 switch (rel_type) {
820411 .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]);
822419 },
823420 .X86_64_RELOC_UNSIGNED => {
824 self.parseUnsigned(rel, &parsed_rel, subtractor, context);
825 subtractor = null;
421 addend = if (rel.r_length == 3)
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);
826431 },
827432 .X86_64_RELOC_SIGNED,
828433 .X86_64_RELOC_SIGNED_1,
829434 .X86_64_RELOC_SIGNED_2,
830435 .X86_64_RELOC_SIGNED_4,
831436 => {
832 self.parseSigned(rel, &parsed_rel, context);
833 },
834 .X86_64_RELOC_GOT_LOAD,
835 .X86_64_RELOC_GOT,
836 .X86_64_RELOC_TLV,
837 => {
838 self.parseLoad(rel, &parsed_rel);
437 const correction: u3 = switch (rel_type) {
438 .X86_64_RELOC_SIGNED => 0,
439 .X86_64_RELOC_SIGNED_1 => 1,
440 .X86_64_RELOC_SIGNED_2 => 2,
441 .X86_64_RELOC_SIGNED_4 => 4,
442 else => unreachable,
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 }
839451 },
452 else => {},
840453 }
841454 },
842455 else => unreachable,
843456 }
844457
845 try self.relocs.append(context.allocator, parsed_rel);
846
847 const is_via_got = switch (parsed_rel.payload) {
848 .pointer_to_got => true,
849 .load => |load| load.kind == .got,
850 .page => |page| page.kind == .got,
851 .page_off => |page_off| page_off.kind == .got,
852 else => false,
853 };
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 };
458 try self.relocs.append(context.allocator, .{
459 .offset = offset,
460 .target = target,
461 .addend = addend,
462 .subtractor = subtractor,
463 .pcrel = rel.r_pcrel == 1,
464 .length = rel.r_length,
465 .@"type" = rel.r_type,
466 });
928467
929 if (should_rebase) {
930 try self.rebases.append(context.allocator, parsed_rel.offset);
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 }
468 addend = 0;
469 subtractor = null;
997470 }
998471}
999472
1000fn isAddend(rel: macho.relocation_info, arch: Arch) bool {
1001 if (arch != .aarch64) return false;
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,
473fn addPtrBindingOrRebase(
474 self: *Atom,
1015475 rel: macho.relocation_info,
1016 out: *Relocation,
1017 subtractor: ?u32,
476 target: Relocation.Target,
1018477 context: RelocContext,
1019) void {
1020 assert(rel.r_pcrel == 0);
1021
1022 const is_64bit: bool = switch (rel.r_length) {
1023 3 => true,
1024 2 => false,
1025 else => unreachable,
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,
478) !void {
479 switch (target) {
480 .global => |n_strx| {
481 try self.bindings.append(context.allocator, .{
482 .n_strx = n_strx,
483 .offset = @intCast(u32, rel.r_address),
484 });
1056485 },
1057 };
1058}
486 .local => {
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 {
1061 _ = self;
1062 assert(rel.r_pcrel == 1);
1063 assert(rel.r_length == 2);
1064
1065 out.payload = .{
1066 .page = .{
1067 .kind = switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
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}
512 if (!is_right_segment) break :rebase false;
513 if (sect_type != macho.S_LITERAL_POINTERS and
514 sect_type != macho.S_REGULAR and
515 sect_type != macho.S_MOD_INIT_FUNC_POINTERS and
516 sect_type != macho.S_MOD_TERM_FUNC_POINTERS)
517 {
518 break :rebase false;
519 }
1077520
1078fn parsePageOff(self: Atom, rel: macho.relocation_info, out: *Relocation, addend: u32) void {
1079 assert(rel.r_pcrel == 0);
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 };
521 break :rebase true;
522 };
1091523
1092 out.payload = .{
1093 .page_off = .{
1094 .kind = switch (rel_type) {
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,
524 if (should_rebase) {
525 try self.rebases.append(context.allocator, @intCast(u32, rel.r_address));
526 }
1102527 },
1103 };
528 }
1104529}
1105530
1106fn parsePointerToGot(self: Atom, rel: macho.relocation_info, out: *Relocation) void {
1107 _ = self;
1108 assert(rel.r_pcrel == 1);
1109 assert(rel.r_length == 2);
1110
1111 out.payload = .{
1112 .pointer_to_got = .{},
531fn addGotEntry(target: Relocation.Target, context: RelocContext) !void {
532 if (context.macho_file.got_entries_map.contains(target)) return;
533 const atom = try context.macho_file.createGotAtom(target);
534 try context.macho_file.got_entries_map.putNoClobber(context.macho_file.base.allocator, target, atom);
535 const match = MachO.MatchingSection{
536 .seg = context.macho_file.data_const_segment_cmd_index.?,
537 .sect = context.macho_file.got_section_index.?,
1113538 };
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 }
1114549}
1115550
1116fn parseSigned(self: Atom, rel: macho.relocation_info, out: *Relocation, context: RelocContext) void {
1117 assert(rel.r_pcrel == 1);
1118 assert(rel.r_length == 2);
1119
1120 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
1121 const correction: u3 = switch (rel_type) {
1122 .X86_64_RELOC_SIGNED => 0,
1123 .X86_64_RELOC_SIGNED_1 => 1,
1124 .X86_64_RELOC_SIGNED_2 => 2,
1125 .X86_64_RELOC_SIGNED_4 => 4,
1126 else => unreachable,
551fn addStub(target: Relocation.Target, context: RelocContext) !void {
552 if (target != .global) return;
553 if (context.macho_file.stubs_map.contains(target.global)) return;
554 // TODO clean this up!
555 const stub_helper_atom = atom: {
556 const atom = try context.macho_file.createStubHelperAtom();
557 const match = MachO.MatchingSection{
558 .seg = context.macho_file.text_segment_cmd_index.?,
559 .sect = context.macho_file.stub_helper_section_index.?,
560 };
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;
1127572 };
1128 var addend: i64 = mem.readIntLittle(i32, self.code.items[out.offset..][0..4]) + correction;
1129
1130 if (rel.r_extern == 0) {
1131 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].Segment;
1132 const target_sect_base_addr = seg.sections.items[rel.r_symbolnum - 1].addr;
1133 addend += @intCast(i64, context.base_addr + out.offset + correction + 4) - @intCast(i64, target_sect_base_addr);
1134 }
1135
1136 out.payload = .{
1137 .signed = .{
1138 .correction = correction,
1139 .addend = addend,
1140 },
573 const laptr_atom = atom: {
574 const atom = try context.macho_file.createLazyPointerAtom(
575 stub_helper_atom.local_sym_index,
576 target.global,
577 );
578 const match = MachO.MatchingSection{
579 .seg = context.macho_file.data_segment_cmd_index.?,
580 .sect = context.macho_file.la_symbol_ptr_section_index.?,
581 };
582 if (!context.object.start_atoms.contains(match)) {
583 try context.object.start_atoms.putNoClobber(context.allocator, match, atom);
584 }
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;
1141593 };
1142}
1143
1144fn parseLoad(self: Atom, rel: macho.relocation_info, out: *Relocation) void {
1145 assert(rel.r_pcrel == 1);
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 },
594 const atom = try context.macho_file.createStubAtom(laptr_atom.local_sym_index);
595 const match = MachO.MatchingSection{
596 .seg = context.macho_file.text_segment_cmd_index.?,
597 .sect = context.macho_file.stubs_section_index.?,
1163598 };
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);
1164610}
1165611
1166612pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
......@@ -1169,42 +615,42 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
1169615
1170616 for (self.relocs.items) |rel| {
1171617 log.debug("relocating {}", .{rel});
1172
618 const arch = macho_file.base.options.target.cpu.arch;
1173619 const source_addr = blk: {
1174620 const sym = macho_file.locals.items[self.local_sym_index];
1175621 break :blk sym.n_value + rel.offset;
1176622 };
1177623 const target_addr = blk: {
1178 const is_via_got = switch (rel.payload) {
1179 .pointer_to_got => true,
1180 .page => |page| page.kind == .got,
1181 .page_off => |page_off| page_off.kind == .got,
1182 .load => |load| load.kind == .got,
1183 else => false,
624 const is_via_got = got: {
625 switch (arch) {
626 .aarch64 => break :got switch (@intToEnum(macho.reloc_type_arm64, rel.@"type")) {
627 .ARM64_RELOC_GOT_LOAD_PAGE21, .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => true,
628 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 }
1184636 };
1185637
1186638 if (is_via_got) {
1187 const atom = macho_file.got_entries_map.get(.{
1188 .where = switch (rel.where) {
1189 .local => .local,
1190 .undef => .undef,
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],
639 const atom = macho_file.got_entries_map.get(rel.target) orelse {
640 const n_strx = switch (rel.target) {
641 .local => |sym_index| macho_file.locals.items[sym_index].n_strx,
642 .global => |n_strx| n_strx,
1197643 };
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)});
1199645 log.err(" this is an internal linker error", .{});
1200646 return error.FailedToResolveRelocationTarget;
1201647 };
1202648 break :blk macho_file.locals.items[atom.local_sym_index].n_value;
1203649 }
1204650
1205 switch (rel.where) {
1206 .local => {
1207 const sym = macho_file.locals.items[rel.where_index];
651 switch (rel.target) {
652 .local => |sym_index| {
653 const sym = macho_file.locals.items[sym_index];
1208654 const is_tlv = is_tlv: {
1209655 const source_sym = macho_file.locals.items[self.local_sym_index];
1210656 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 {
1233679 };
1234680 break :blk sym.n_value - base_address;
1235681 }
1236
1237682 break :blk sym.n_value;
1238683 },
1239 .undef => {
1240 const atom = macho_file.stubs_map.get(rel.where_index) orelse {
1241 // TODO this is required for incremental when we don't have every symbol
1242 // resolved when creating relocations. In this case, we will insert a branch
1243 // reloc to an undef symbol which may happen to be defined within the binary.
1244 // Then, the undef we point at will be a null symbol (free symbol) which we
1245 // should remove/repurpose. To circumvent this (for now), we check if the symbol
1246 // we point to is garbage, and if so we fall back to symbol resolver to find by name.
1247 const n_strx = macho_file.undefs.items[rel.where_index].n_strx;
1248 if (macho_file.symbol_resolver.get(n_strx)) |resolv| inner: {
1249 if (resolv.where != .global) break :inner;
1250 break :blk macho_file.globals.items[resolv.where_index].n_value;
1251 }
1252
1253 // TODO verify in TextBlock that the symbol is indeed dynamically bound.
1254 break :blk 0; // Dynamically bound by dyld.
1255 };
1256
1257 break :blk macho_file.locals.items[atom.local_sym_index].n_value;
684 .global => |n_strx| {
685 // TODO Still trying to figure out how to possibly use stubs for local symbol indirection with
686 // branching instructions. If it is not possible, then the best course of action is to
687 // resurrect the former approach of defering creating synthethic atoms in __got and __la_symbol_ptr
688 // sections until we resolve the relocations.
689 const resolv = macho_file.symbol_resolver.get(n_strx).?;
690 switch (resolv.where) {
691 .global => break :blk macho_file.globals.items[resolv.where_index].n_value,
692 .undef => {
693 break :blk if (macho_file.stubs_map.get(n_strx)) |atom|
694 macho_file.locals.items[atom.local_sym_index].n_value
695 else
696 0;
697 },
698 }
1258699 },
1259700 }
1260701 };
......@@ -1262,67 +703,248 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
1262703 log.debug(" | source_addr = 0x{x}", .{source_addr});
1263704 log.debug(" | target_addr = 0x{x}", .{target_addr});
1264705
1265 try rel.resolve(.{
1266 .block = self,
1267 .offset = rel.offset,
1268 .source_addr = source_addr,
1269 .target_addr = target_addr,
1270 .macho_file = macho_file,
1271 });
1272 }
1273}
1274
1275pub fn format(self: Atom, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
1276 _ = fmt;
1277 _ = options;
1278 try std.fmt.format(writer, "TextBlock {{ ", .{});
1279 try std.fmt.format(writer, ".local_sym_index = {d}, ", .{self.local_sym_index});
1280 try std.fmt.format(writer, ".aliases = {any}, ", .{self.aliases.items});
1281 try std.fmt.format(writer, ".contained = {any}, ", .{self.contained.items});
1282 try std.fmt.format(writer, ".code = {*}, ", .{self.code.items});
1283 try std.fmt.format(writer, ".size = {d}, ", .{self.size});
1284 try std.fmt.format(writer, ".alignment = {d}, ", .{self.alignment});
1285 try std.fmt.format(writer, ".relocs = {any}, ", .{self.relocs.items});
1286 try std.fmt.format(writer, ".rebases = {any}, ", .{self.rebases.items});
1287 try std.fmt.format(writer, ".bindings = {any}, ", .{self.bindings.items});
1288 try std.fmt.format(writer, ".dices = {any}, ", .{self.dices.items});
1289 if (self.stab) |stab| {
1290 try std.fmt.format(writer, ".stab = {any}, ", .{stab});
1291 }
1292 try std.fmt.format(writer, "}}", .{});
1293}
706 switch (arch) {
707 .aarch64 => {
708 switch (@intToEnum(macho.reloc_type_arm64, rel.@"type")) {
709 .ARM64_RELOC_BRANCH26 => {
710 const displacement = math.cast(
711 i28,
712 @intCast(i64, target_addr) - @intCast(i64, source_addr),
713 ) catch |err| switch (err) {
714 error.Overflow => {
715 log.err("jump too big to encode as i28 displacement value", .{});
716 log.err(" (target - source) = displacement => 0x{x} - 0x{x} = 0x{x}", .{
717 target_addr,
718 source_addr,
719 @intCast(i64, target_addr) - @intCast(i64, source_addr),
720 });
721 log.err(" TODO implement branch islands to extend jump distance for arm64", .{});
722 return error.TODOImplementBranchIslands;
723 },
724 };
725 const code = self.code.items[rel.offset..][0..4];
726 var inst = aarch64.Instruction{
727 .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload(
728 aarch64.Instruction,
729 aarch64.Instruction.unconditional_branch_immediate,
730 ), code),
731 };
732 inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2));
733 mem.writeIntLittle(u32, code, inst.toU32());
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 {
1296 buffer: []const macho.relocation_info,
1297 index: i32 = -1,
864 if (rel.length == 3) {
865 mem.writeIntLittle(u64, self.code.items[rel.offset..][0..8], @bitCast(u64, result));
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 {
1300 self.index += 1;
1301 if (self.index < self.buffer.len) {
1302 return self.buffer[@intCast(u32, self.index)];
932 if (rel.length == 3) {
933 mem.writeIntLittle(u64, self.code.items[rel.offset..][0..8], @bitCast(u64, result));
934 } else {
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,
1303946 }
1304 return null;
1305947 }
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];
1326948}
1327949
1328950inline fn isArithmeticOp(inst: *const [4]u8) bool {
src/link/MachO/Dylib.zig+1-1
......@@ -4,7 +4,7 @@ const std = @import("std");
44const assert = std.debug.assert;
55const fs = std.fs;
66const fmt = std.fmt;
7const log = std.log.scoped(.dylib);
7const log = std.log.scoped(.link);
88const macho = std.macho;
99const math = std.math;
1010const mem = std.mem;
src/link/MachO/Object.zig+8-3
......@@ -6,7 +6,7 @@ const assert = std.debug.assert;
66const dwarf = std.dwarf;
77const fs = std.fs;
88const io = std.io;
9const log = std.log.scoped(.object);
9const log = std.log.scoped(.link);
1010const macho = std.macho;
1111const math = std.math;
1212const mem = std.mem;
......@@ -458,9 +458,15 @@ pub fn parseIntoAtoms(self: *Object, allocator: *Allocator, macho_file: *MachO)
458458 // a temp one, unless we already did that when working out the relocations
459459 // of other atoms.
460460 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);
461467 const atom_local_sym_index = @intCast(u32, macho_file.locals.items.len);
462468 try macho_file.locals.append(allocator, .{
463 .n_strx = 0,
469 .n_strx = try macho_file.makeString(sym_name),
464470 .n_type = macho.N_SECT,
465471 .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1),
466472 .n_desc = 0,
......@@ -481,7 +487,6 @@ pub fn parseIntoAtoms(self: *Object, allocator: *Allocator, macho_file: *MachO)
481487
482488 try atom.parseRelocs(relocs, .{
483489 .base_addr = sect.addr,
484 .base_offset = 0,
485490 .allocator = allocator,
486491 .object = self,
487492 .macho_file = macho_file,