authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-01-09 19:29:33+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-01-13 23:55:06+01:00
logb86d0e488b9dfc7d943da86a34998360e24da225
tree7dd75cbe0d1ce9c8a4daa5296978aa7ba687a71e
parent21c7217e09b48bf3bf9656fb1e8e69b85422b02e

macho: refactor writing and managing externs


3 files changed, 359 insertions(+), 490 deletions(-)

src/codegen.zig+12-14
...@@ -1861,29 +1861,27 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1861,29 +1861,27 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1861 }1861 }
1862 } else if (func_value.castTag(.extern_fn)) |func_payload| {1862 } else if (func_value.castTag(.extern_fn)) |func_payload| {
1863 const decl = func_payload.data;1863 const decl = func_payload.data;
1864 // We don't free the decl_name immediately unless it already exists.
1865 // If it doesn't, it will get autofreed when we clean up the extern symbol table.
1864 const decl_name = try std.fmt.allocPrint(self.bin_file.allocator, "_{s}", .{decl.name});1866 const decl_name = try std.fmt.allocPrint(self.bin_file.allocator, "_{s}", .{decl.name});
1865 const exists: bool = macho_file.externs.contains(decl_name);1867 const already_defined = macho_file.extern_lazy_symbols.contains(decl_name);
1866 const symbol: u32 = blk: {1868 const symbol: u32 = blk: {
1867 if (macho_file.externs.get(decl_name)) |index| {1869 if (macho_file.extern_lazy_symbols.get(decl_name)) |sym| {
1868 self.bin_file.allocator.free(decl_name);1870 self.bin_file.allocator.free(decl_name);
1869 break :blk index;1871 break :blk sym.index;
1870 } else {1872 } else {
1871 const extern_index = @intCast(u32, macho_file.undef_symbols.items.len - 1); // TODO1873 const index = @intCast(u32, macho_file.extern_lazy_symbols.items().len);
1872 try macho_file.externs.putNoClobber(self.bin_file.allocator, decl_name, extern_index);1874 try macho_file.extern_lazy_symbols.putNoClobber(self.bin_file.allocator, decl_name, .{
1873 const name = try macho_file.makeString(decl_name);1875 .name = decl_name,
1874 try macho_file.undef_symbols.append(self.bin_file.allocator, .{1876 .dylib_ordinal = 1, // TODO this is now hardcoded, since we only support libSystem.
1875 .n_strx = name,1877 .index = index,
1876 .n_type = std.macho.N_UNDF | std.macho.N_EXT,
1877 .n_sect = 0,
1878 .n_desc = std.macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | std.macho.N_SYMBOL_RESOLVER,
1879 .n_value = 0,
1880 });1878 });
1881 break :blk extern_index;1879 break :blk index;
1882 }1880 }
1883 };1881 };
1884 try macho_file.stub_fixups.append(self.bin_file.allocator, .{1882 try macho_file.stub_fixups.append(self.bin_file.allocator, .{
1885 .symbol = symbol,1883 .symbol = symbol,
1886 .exists = exists,1884 .already_defined = already_defined,
1887 .start = self.code.items.len,1885 .start = self.code.items.len,
1888 .len = 4,1886 .len = 4,
1889 });1887 });
src/link/MachO.zig+221-162
...@@ -105,16 +105,17 @@ entry_addr: ?u64 = null,...@@ -105,16 +105,17 @@ entry_addr: ?u64 = null,
105/// Table of all local symbols105/// Table of all local symbols
106/// Internally references string table for names (which are optional).106/// Internally references string table for names (which are optional).
107local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},107local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
108/// Table of all defined global symbols108/// Table of all global symbols
109global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},109global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
110/// Table of all undefined symbols110/// Table of all extern nonlazy symbols, indexed by name.
111undef_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},111extern_nonlazy_symbols: std.StringArrayHashMapUnmanaged(ExternSymbol) = .{},
112/// Table of all extern lazy symbols, indexed by name.
113extern_lazy_symbols: std.StringArrayHashMapUnmanaged(ExternSymbol) = .{},
112114
113local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},115local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
114global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},116global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
115offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},117offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
116118
117dyld_stub_binder_index: ?u16 = null,
118stub_helper_stubs_start_off: ?u64 = null,119stub_helper_stubs_start_off: ?u64 = null,
119120
120/// Table of symbol names aka the string table.121/// Table of symbol names aka the string table.
...@@ -123,13 +124,6 @@ string_table: std.ArrayListUnmanaged(u8) = .{},...@@ -123,13 +124,6 @@ string_table: std.ArrayListUnmanaged(u8) = .{},
123/// Table of trampolines to the actual symbols in __text section.124/// Table of trampolines to the actual symbols in __text section.
124offset_table: std.ArrayListUnmanaged(u64) = .{},125offset_table: std.ArrayListUnmanaged(u64) = .{},
125126
126/// Table of rebase info entries.
127rebase_info_table: RebaseInfoTable = .{},
128/// Table of binding info entries.
129binding_info_table: BindingInfoTable = .{},
130/// Table of lazy binding info entries.
131lazy_binding_info_table: LazyBindingInfoTable = .{},
132
133error_flags: File.ErrorFlags = File.ErrorFlags{},127error_flags: File.ErrorFlags = File.ErrorFlags{},
134128
135offset_table_count_dirty: bool = false,129offset_table_count_dirty: bool = false,
...@@ -167,11 +161,10 @@ last_text_block: ?*TextBlock = null,...@@ -167,11 +161,10 @@ last_text_block: ?*TextBlock = null,
167pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{},161pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{},
168162
169stub_fixups: std.ArrayListUnmanaged(StubFixup) = .{},163stub_fixups: std.ArrayListUnmanaged(StubFixup) = .{},
170externs: std.StringHashMapUnmanaged(u32) = .{},
171164
172pub const StubFixup = struct {165pub const StubFixup = struct {
173 symbol: u32,166 symbol: u32,
174 exists: bool,167 already_defined: bool,
175 start: usize,168 start: usize,
176 len: usize,169 len: usize,
177};170};
...@@ -920,42 +913,42 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -920,42 +913,42 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
920 return error.NoSymbolTableFound;913 return error.NoSymbolTableFound;
921 }914 }
922915
923 // Parse dyld info916 // // Parse dyld info
924 try self.parseBindingInfoTable();917 // try self.parseBindingInfoTable();
925 try self.parseLazyBindingInfoTable();918 // try self.parseLazyBindingInfoTable();
926919
927 // Update the dylib ordinals.920 // // Update the dylib ordinals.
928 self.binding_info_table.dylib_ordinal = next_ordinal;921 // self.binding_info_table.dylib_ordinal = next_ordinal;
929 for (self.lazy_binding_info_table.symbols.items) |*symbol| {922 // for (self.lazy_binding_info_table.symbols.items) |*symbol| {
930 symbol.dylib_ordinal = next_ordinal;923 // symbol.dylib_ordinal = next_ordinal;
931 }924 // }
932925
933 // Write updated dyld info.926 // // Write updated dyld info.
934 const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;927 // const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
935 {928 // {
936 const size = try self.binding_info_table.calcSize();929 // const size = try self.binding_info_table.calcSize();
937 assert(dyld_info.bind_size >= size);930 // assert(dyld_info.bind_size >= size);
938931
939 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));932 // var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
940 defer self.base.allocator.free(buffer);933 // defer self.base.allocator.free(buffer);
941934
942 var stream = std.io.fixedBufferStream(buffer);935 // var stream = std.io.fixedBufferStream(buffer);
943 try self.binding_info_table.write(stream.writer());936 // try self.binding_info_table.write(stream.writer());
944937
945 try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off);938 // try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off);
946 }939 // }
947 {940 // {
948 const size = try self.lazy_binding_info_table.calcSize();941 // const size = try self.lazy_binding_info_table.calcSize();
949 assert(dyld_info.lazy_bind_size >= size);942 // assert(dyld_info.lazy_bind_size >= size);
950943
951 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));944 // var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
952 defer self.base.allocator.free(buffer);945 // defer self.base.allocator.free(buffer);
953946
954 var stream = std.io.fixedBufferStream(buffer);947 // var stream = std.io.fixedBufferStream(buffer);
955 try self.lazy_binding_info_table.write(stream.writer());948 // try self.lazy_binding_info_table.write(stream.writer());
956949
957 try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off);950 // try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off);
958 }951 // }
959952
960 // Write updated load commands and the header953 // Write updated load commands and the header
961 try self.writeLoadCommands();954 try self.writeLoadCommands();
...@@ -1037,14 +1030,13 @@ pub fn deinit(self: *MachO) void {...@@ -1037,14 +1030,13 @@ pub fn deinit(self: *MachO) void {
1037 if (self.d_sym) |*ds| {1030 if (self.d_sym) |*ds| {
1038 ds.deinit(self.base.allocator);1031 ds.deinit(self.base.allocator);
1039 }1032 }
1040 self.binding_info_table.deinit(self.base.allocator);
1041 self.lazy_binding_info_table.deinit(self.base.allocator);
1042 self.pie_fixups.deinit(self.base.allocator);1033 self.pie_fixups.deinit(self.base.allocator);
1043 self.text_block_free_list.deinit(self.base.allocator);1034 self.text_block_free_list.deinit(self.base.allocator);
1044 self.offset_table.deinit(self.base.allocator);1035 self.offset_table.deinit(self.base.allocator);
1045 self.offset_table_free_list.deinit(self.base.allocator);1036 self.offset_table_free_list.deinit(self.base.allocator);
1046 self.string_table.deinit(self.base.allocator);1037 self.string_table.deinit(self.base.allocator);
1047 self.undef_symbols.deinit(self.base.allocator);1038 self.extern_lazy_symbols.deinit(self.base.allocator);
1039 self.extern_nonlazy_symbols.deinit(self.base.allocator);
1048 self.global_symbols.deinit(self.base.allocator);1040 self.global_symbols.deinit(self.base.allocator);
1049 self.global_symbol_free_list.deinit(self.base.allocator);1041 self.global_symbol_free_list.deinit(self.base.allocator);
1050 self.local_symbols.deinit(self.base.allocator);1042 self.local_symbols.deinit(self.base.allocator);
...@@ -1261,59 +1253,28 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1261,59 +1253,28 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1261 }1253 }
12621254
1263 // Resolve stubs (if any)1255 // Resolve stubs (if any)
1264 const stubs = &text_segment.sections.items[self.stubs_section_index.?];1256 const stubs = text_segment.sections.items[self.stubs_section_index.?];
1265 const stub_h = &text_segment.sections.items[self.stub_helper_section_index.?];
1266 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
1267 const la_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
1268 for (self.stub_fixups.items) |fixup| {1257 for (self.stub_fixups.items) |fixup| {
1269 // TODO increment offset for stub writing
1270 const stub_addr = stubs.addr + fixup.symbol * stubs.reserved2;1258 const stub_addr = stubs.addr + fixup.symbol * stubs.reserved2;
1271 const text_addr = symbol.n_value + fixup.start;1259 const text_addr = symbol.n_value + fixup.start;
1272 const displacement = @intCast(u32, stub_addr - text_addr);1260 const displacement = @intCast(u32, stub_addr - text_addr);
1273 var placeholder = code_buffer.items[fixup.start..][0..fixup.len];1261 var placeholder = code_buffer.items[fixup.start..][0..fixup.len];
1274 mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.bl(@intCast(i28, displacement)).toU32());1262 switch (self.base.options.target.cpu.arch) {
12751263 .x86_64 => return error.TODOImplementStubFixupsForx86_64,
1276 if (!fixup.exists) {1264 .aarch64 => {
1277 const stub_off = self.stub_helper_stubs_start_off.? + fixup.symbol * 3 * @sizeOf(u32);1265 mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.bl(@intCast(i28, displacement)).toU32());
1278 const end = stub_h.addr + stub_off - stub_h.offset;1266 },
1279 var buf: [@sizeOf(u64)]u8 = undefined;1267 else => unreachable,
1280 mem.writeIntLittle(u64, &buf, end);1268 }
1281 try self.base.file.?.pwriteAll(&buf, la_ptr.offset + fixup.symbol * @sizeOf(u64));1269 if (!fixup.already_defined) {
12821270 try self.writeStub(fixup.symbol);
1283 const la_ptr_addr = la_ptr.addr + fixup.symbol * @sizeOf(u64);1271 try self.writeStubInStubHelper(fixup.symbol);
1284 const displacement2 = la_ptr_addr - stub_addr;1272 try self.writeLazySymbolPointer(fixup.symbol);
1285 var ccode: [2 * @sizeOf(u32)]u8 = undefined;1273
1286 mem.writeIntLittle(u32, ccode[0..4], aarch64.Instruction.ldr(.x16, .{1274 const extern_sym = &self.extern_lazy_symbols.items()[fixup.symbol].value;
1287 .literal = @intCast(u19, displacement2 / 4),1275 extern_sym.segment = self.data_segment_cmd_index.?;
1288 }).toU32());1276 extern_sym.offset = fixup.symbol * @sizeOf(u64);
1289 mem.writeIntLittle(u32, ccode[4..8], aarch64.Instruction.br(.x16).toU32());
1290 try self.base.file.?.pwriteAll(&ccode, stubs.offset + fixup.symbol * stubs.reserved2);
1291
1292 const displacement3 = @intCast(i64, stub_h.addr) - @intCast(i64, end + 4);
1293 var cccode: [3 * @sizeOf(u32)]u8 = undefined;
1294 mem.writeIntLittle(u32, cccode[0..4], aarch64.Instruction.ldr(.w16, .{
1295 .literal = 0x2,
1296 }).toU32());
1297 mem.writeIntLittle(u32, cccode[4..8], aarch64.Instruction.b(@intCast(i28, displacement3)).toU32());
1298 mem.writeIntLittle(u32, cccode[8..12], fixup.symbol * 0xd);
1299 try self.base.file.?.pwriteAll(&cccode, stub_off);
1300
1301 try self.rebase_info_table.symbols.append(self.base.allocator, .{
1302 .segment = 3,
1303 .offset = fixup.symbol * stubs.reserved2,
1304 });
1305 self.rebase_info_dirty = true;1277 self.rebase_info_dirty = true;
1306
1307 const sym = self.undef_symbols.items[fixup.symbol + 1];
1308 const name_str = self.getString(sym.n_strx);
1309 var name = try self.base.allocator.alloc(u8, name_str.len);
1310 mem.copy(u8, name, name_str);
1311 try self.lazy_binding_info_table.symbols.append(self.base.allocator, .{
1312 .segment = 3,
1313 .offset = fixup.symbol * @sizeOf(u64),
1314 .dylib_ordinal = 1,
1315 .name = name,
1316 });
1317 self.lazy_binding_info_dirty = true;1278 self.lazy_binding_info_dirty = true;
1318 }1279 }
1319 }1280 }
...@@ -2080,51 +2041,51 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -2080,51 +2041,51 @@ pub fn populateMissingMetadata(self: *MachO) !void {
2080 self.header_dirty = true;2041 self.header_dirty = true;
2081 self.load_commands_dirty = true;2042 self.load_commands_dirty = true;
2082 }2043 }
2083 if (self.dyld_stub_binder_index == null) {2044 if (!self.extern_nonlazy_symbols.contains("dyld_stub_binder")) {
2084 self.dyld_stub_binder_index = @intCast(u16, self.undef_symbols.items.len);2045 const index = @intCast(u32, self.extern_nonlazy_symbols.items().len);
2085 const name = try self.makeString("dyld_stub_binder");2046 const name = try std.fmt.allocPrint(self.base.allocator, "dyld_stub_binder", .{});
2086 try self.undef_symbols.append(self.base.allocator, .{2047 try self.extern_nonlazy_symbols.putNoClobber(self.base.allocator, name, .{
2087 .n_strx = name,2048 .name = name,
2088 .n_type = macho.N_UNDF | macho.N_EXT,2049 .dylib_ordinal = 1, // TODO this is currently hardcoded.
2089 .n_sect = 0,2050 .index = index,
2090 .n_desc = macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | macho.N_SYMBOL_RESOLVER,2051 .segment = self.data_const_segment_cmd_index.?,
2091 .n_value = 0,2052 .offset = index * @sizeOf(u64),
2092 });
2093
2094 self.binding_info_table.dylib_ordinal = 1;
2095 const nn = self.getString(name);
2096 var n = try self.base.allocator.alloc(u8, nn.len);
2097 mem.copy(u8, n, nn);
2098 try self.binding_info_table.symbols.append(self.base.allocator, .{
2099 .name = n,
2100 .segment = 2,
2101 .offset = 0,
2102 });2053 });
2103 self.binding_info_dirty = true;2054 self.binding_info_dirty = true;
2104 }2055 }
2105 if (self.stub_helper_stubs_start_off == null) {2056 if (self.stub_helper_stubs_start_off == null) {
2106 const text = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;2057 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2107 const sh = &text.sections.items[self.stub_helper_section_index.?];2058 const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?];
2108 const data = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;2059 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2109 const data_data = &data.sections.items[self.data_section_index.?];2060 const data = &data_segment.sections.items[self.data_section_index.?];
2110 const displacement = data_data.addr - sh.addr;2061 const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2111 var code: [4 * @sizeOf(u32)]u8 = undefined;2062 const got = &data_const_segment.sections.items[self.data_got_section_index.?];
2112 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, @intCast(i21, displacement)).toU32());2063 switch (self.base.options.target.cpu.arch) {
2113 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.stp(2064 .x86_64 => return error.TODOImplementStubHelperForX86_64,
2114 .x16,2065 .aarch64 => {
2115 .x17,2066 var code: [4 * @sizeOf(u32)]u8 = undefined;
2116 aarch64.Register.sp,2067 {
2117 aarch64.Instruction.LoadStorePairOffset.pre_index(-16),2068 const displacement = data.addr - stub_helper.addr;
2118 ).toU32());2069 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, @intCast(i21, displacement)).toU32());
2119 const dc = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;2070 }
2120 const got = &dc.sections.items[self.data_got_section_index.?];2071 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.stp(
2121 const displacement2 = got.addr - sh.addr - 2 * @sizeOf(u32);2072 .x16,
2122 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.ldr(.x16, .{2073 .x17,
2123 .literal = @intCast(u19, displacement2 / 4),2074 aarch64.Register.sp,
2124 }).toU32());2075 aarch64.Instruction.LoadStorePairOffset.pre_index(-16),
2125 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.br(.x16).toU32());2076 ).toU32());
2126 self.stub_helper_stubs_start_off = sh.offset + 4 * @sizeOf(u32);2077 {
2127 try self.base.file.?.pwriteAll(&code, sh.offset);2078 const displacement = got.addr - stub_helper.addr - 2 * @sizeOf(u32);
2079 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.ldr(.x16, .{
2080 .literal = @intCast(u19, displacement / 4),
2081 }).toU32());
2082 }
2083 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.br(.x16).toU32());
2084 self.stub_helper_stubs_start_off = stub_helper.offset + 4 * @sizeOf(u32);
2085 try self.base.file.?.pwriteAll(&code, stub_helper.offset);
2086 },
2087 else => unreachable,
2088 }
2128 }2089 }
2129}2090}
21302091
...@@ -2460,11 +2421,73 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void {...@@ -2460,11 +2421,73 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
2460 try self.base.file.?.pwriteAll(&code, off);2421 try self.base.file.?.pwriteAll(&code, off);
2461}2422}
24622423
2424fn writeLazySymbolPointer(self: *MachO, index: u32) !void {
2425 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2426 const stub_helper = text_segment.sections.items[self.stub_helper_section_index.?];
2427 const data_segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2428 const la_symbol_ptr = data_segment.sections.items[self.la_symbol_ptr_section_index.?];
2429
2430 const stub_off = self.stub_helper_stubs_start_off.? + index * 3 * @sizeOf(u32);
2431 const end = stub_helper.addr + stub_off - stub_helper.offset;
2432 var buf: [@sizeOf(u64)]u8 = undefined;
2433 mem.writeIntLittle(u64, &buf, end);
2434 const off = la_symbol_ptr.offset + index * @sizeOf(u64);
2435 log.debug("writing lazy symbol pointer entry 0x{x} at 0x{x}", .{ end, off });
2436 try self.base.file.?.pwriteAll(&buf, off);
2437}
2438
2439fn writeStub(self: *MachO, index: u32) !void {
2440 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2441 const stubs = text_segment.sections.items[self.stubs_section_index.?];
2442 const data_segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2443 const la_symbol_ptr = data_segment.sections.items[self.la_symbol_ptr_section_index.?];
2444
2445 const stub_off = stubs.offset + index * stubs.reserved2;
2446 const stub_addr = stubs.addr + index * stubs.reserved2;
2447 const la_ptr_addr = la_symbol_ptr.addr + index * @sizeOf(u64);
2448 const displacement = la_ptr_addr - stub_addr;
2449 log.debug("writing stub at 0x{x}", .{stub_off});
2450 switch (self.base.options.target.cpu.arch) {
2451 .x86_64 => return error.TODOImplementWritingStubsForx86_64,
2452 .aarch64 => {
2453 var code: [2 * @sizeOf(u32)]u8 = undefined;
2454 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{
2455 .literal = @intCast(u19, displacement / 4),
2456 }).toU32());
2457 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.br(.x16).toU32());
2458 try self.base.file.?.pwriteAll(&code, stub_off);
2459 },
2460 else => unreachable,
2461 }
2462}
2463
2464fn writeStubInStubHelper(self: *MachO, index: u32) !void {
2465 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2466 const stub_helper = text_segment.sections.items[self.stub_helper_section_index.?];
2467
2468 const stub_off = self.stub_helper_stubs_start_off.? + index * 3 * @sizeOf(u32);
2469 const end = stub_helper.addr + stub_off - stub_helper.offset;
2470 const displacement = @intCast(i64, stub_helper.addr) - @intCast(i64, end + 4);
2471 switch (self.base.options.target.cpu.arch) {
2472 .x86_64 => return error.TODOImplementWritingStubsInStubHelperForx86_64,
2473 .aarch64 => {
2474 var code: [3 * @sizeOf(u32)]u8 = undefined;
2475 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{
2476 .literal = 0x2,
2477 }).toU32());
2478 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(@intCast(i28, displacement)).toU32());
2479 mem.writeIntLittle(u32, code[8..12], index * 0xd); // TODO This is the size of lazy binding opcode block.
2480 try self.base.file.?.pwriteAll(&code, stub_off);
2481 },
2482 else => unreachable,
2483 }
2484}
2485
2463fn relocateSymbolTable(self: *MachO) !void {2486fn relocateSymbolTable(self: *MachO) !void {
2464 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;2487 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2465 const nlocals = self.local_symbols.items.len;2488 const nlocals = self.local_symbols.items.len;
2466 const nglobals = self.global_symbols.items.len;2489 const nglobals = self.global_symbols.items.len;
2467 const nundefs = self.undef_symbols.items.len;2490 const nundefs = self.extern_lazy_symbols.items().len + self.extern_nonlazy_symbols.items().len;
2468 const nsyms = nlocals + nglobals + nundefs;2491 const nsyms = nlocals + nglobals + nundefs;
24692492
2470 if (symtab.nsyms < nsyms) {2493 if (symtab.nsyms < nsyms) {
...@@ -2509,7 +2532,31 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {...@@ -2509,7 +2532,31 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
2509 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;2532 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2510 const nlocals = self.local_symbols.items.len;2533 const nlocals = self.local_symbols.items.len;
2511 const nglobals = self.global_symbols.items.len;2534 const nglobals = self.global_symbols.items.len;
2512 const nundefs = self.undef_symbols.items.len;2535
2536 const nundefs = self.extern_lazy_symbols.items().len + self.extern_nonlazy_symbols.items().len;
2537 var undefs = std.ArrayList(macho.nlist_64).init(self.base.allocator);
2538 defer undefs.deinit();
2539 try undefs.ensureCapacity(nundefs);
2540 for (self.extern_lazy_symbols.items()) |entry| {
2541 const name = try self.makeString(entry.key);
2542 undefs.appendAssumeCapacity(.{
2543 .n_strx = name,
2544 .n_type = std.macho.N_UNDF | std.macho.N_EXT,
2545 .n_sect = 0,
2546 .n_desc = std.macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | std.macho.N_SYMBOL_RESOLVER,
2547 .n_value = 0,
2548 });
2549 }
2550 for (self.extern_nonlazy_symbols.items()) |entry| {
2551 const name = try self.makeString(entry.key);
2552 undefs.appendAssumeCapacity(.{
2553 .n_strx = name,
2554 .n_type = std.macho.N_UNDF | std.macho.N_EXT,
2555 .n_sect = 0,
2556 .n_desc = std.macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | std.macho.N_SYMBOL_RESOLVER,
2557 .n_value = 0,
2558 });
2559 }
25132560
2514 const locals_off = symtab.symoff;2561 const locals_off = symtab.symoff;
2515 const locals_size = nlocals * @sizeOf(macho.nlist_64);2562 const locals_size = nlocals * @sizeOf(macho.nlist_64);
...@@ -2521,8 +2568,8 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {...@@ -2521,8 +2568,8 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
25212568
2522 const undefs_off = globals_off + globals_size;2569 const undefs_off = globals_off + globals_size;
2523 const undefs_size = nundefs * @sizeOf(macho.nlist_64);2570 const undefs_size = nundefs * @sizeOf(macho.nlist_64);
2524 log.debug("writing undef symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off });2571 log.debug("writing extern symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off });
2525 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.undef_symbols.items), undefs_off);2572 try self.base.file.?.pwriteAll(mem.sliceAsBytes(undefs.items), undefs_off);
25262573
2527 // Update dynamic symbol table.2574 // Update dynamic symbol table.
2528 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;2575 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
...@@ -2546,42 +2593,33 @@ fn writeIndirectSymbolTable(self: *MachO) !void {...@@ -2546,42 +2593,33 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
25462593
2547 var buf: [@sizeOf(u32)]u8 = undefined;2594 var buf: [@sizeOf(u32)]u8 = undefined;
2548 var off = dysymtab.indirectsymoff;2595 var off = dysymtab.indirectsymoff;
2549 var idx: u32 = 0;
25502596
2551 stubs.reserved1 = 0;2597 stubs.reserved1 = 0;
2552 for (self.undef_symbols.items) |sym, i| {2598 for (self.extern_lazy_symbols.items()) |entry| {
2553 if (i == self.dyld_stub_binder_index.?) {2599 const symtab_idx = @intCast(u32, dysymtab.iundefsym + entry.value.index);
2554 continue;
2555 }
2556 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
2557 mem.writeIntLittle(u32, &buf, symtab_idx);2600 mem.writeIntLittle(u32, &buf, symtab_idx);
2558 try self.base.file.?.pwriteAll(&buf, off);2601 try self.base.file.?.pwriteAll(&buf, off);
2559 off += @sizeOf(u32);2602 off += @sizeOf(u32);
2560 dysymtab.nindirectsyms += 1;2603 dysymtab.nindirectsyms += 1;
2561 idx += 1;
2562 }2604 }
25632605
2564 got.reserved1 = @intCast(u32, self.undef_symbols.items.len - 1);2606 const base_id = @intCast(u32, self.extern_lazy_symbols.items().len);
2565 if (self.dyld_stub_binder_index) |i| {2607 got.reserved1 = base_id;
2566 const symtab_idx = i + dysymtab.iundefsym;2608 for (self.extern_nonlazy_symbols.items()) |entry| {
2609 const symtab_idx = @intCast(u32, dysymtab.iundefsym + entry.value.index + base_id);
2567 mem.writeIntLittle(u32, &buf, symtab_idx);2610 mem.writeIntLittle(u32, &buf, symtab_idx);
2568 try self.base.file.?.pwriteAll(&buf, off);2611 try self.base.file.?.pwriteAll(&buf, off);
2569 off += @sizeOf(u32);2612 off += @sizeOf(u32);
2570 dysymtab.nindirectsyms += 1;2613 dysymtab.nindirectsyms += 1;
2571 idx += 1;
2572 }2614 }
25732615
2574 la.reserved1 = got.reserved1 + 1;2616 la.reserved1 = got.reserved1 + @intCast(u32, self.extern_nonlazy_symbols.items().len);
2575 for (self.undef_symbols.items) |sym, i| {2617 for (self.extern_lazy_symbols.items()) |entry| {
2576 if (i == self.dyld_stub_binder_index.?) {2618 const symtab_idx = @intCast(u32, dysymtab.iundefsym + entry.value.index);
2577 continue;
2578 }
2579 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
2580 mem.writeIntLittle(u32, &buf, symtab_idx);2619 mem.writeIntLittle(u32, &buf, symtab_idx);
2581 try self.base.file.?.pwriteAll(&buf, off);2620 try self.base.file.?.pwriteAll(&buf, off);
2582 off += @sizeOf(u32);2621 off += @sizeOf(u32);
2583 dysymtab.nindirectsyms += 1;2622 dysymtab.nindirectsyms += 1;
2584 idx += 1;
2585 }2623 }
2586}2624}
25872625
...@@ -2689,12 +2727,19 @@ fn writeRebaseInfoTable(self: *MachO) !void {...@@ -2689,12 +2727,19 @@ fn writeRebaseInfoTable(self: *MachO) !void {
2689 const tracy = trace(@src());2727 const tracy = trace(@src());
2690 defer tracy.end();2728 defer tracy.end();
26912729
2692 const size = try self.rebase_info_table.calcSize();2730 var symbols = try self.base.allocator.alloc(*const ExternSymbol, self.extern_lazy_symbols.items().len);
2731 defer self.base.allocator.free(symbols);
2732
2733 for (self.extern_lazy_symbols.items()) |*entry, i| {
2734 symbols[i] = &entry.value;
2735 }
2736
2737 const size = try rebaseInfoSize(symbols);
2693 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));2738 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
2694 defer self.base.allocator.free(buffer);2739 defer self.base.allocator.free(buffer);
26952740
2696 var stream = std.io.fixedBufferStream(buffer);2741 var stream = std.io.fixedBufferStream(buffer);
2697 try self.rebase_info_table.write(stream.writer());2742 try writeRebaseInfo(symbols, stream.writer());
26982743
2699 const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;2744 const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2700 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;2745 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
...@@ -2720,12 +2765,19 @@ fn writeBindingInfoTable(self: *MachO) !void {...@@ -2720,12 +2765,19 @@ fn writeBindingInfoTable(self: *MachO) !void {
2720 const tracy = trace(@src());2765 const tracy = trace(@src());
2721 defer tracy.end();2766 defer tracy.end();
27222767
2723 const size = try self.binding_info_table.calcSize();2768 var symbols = try self.base.allocator.alloc(*const ExternSymbol, self.extern_nonlazy_symbols.items().len);
2769 defer self.base.allocator.free(symbols);
2770
2771 for (self.extern_nonlazy_symbols.items()) |*entry, i| {
2772 symbols[i] = &entry.value;
2773 }
2774
2775 const size = try bindInfoSize(symbols);
2724 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));2776 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
2725 defer self.base.allocator.free(buffer);2777 defer self.base.allocator.free(buffer);
27262778
2727 var stream = std.io.fixedBufferStream(buffer);2779 var stream = std.io.fixedBufferStream(buffer);
2728 try self.binding_info_table.write(stream.writer());2780 try writeBindInfo(symbols, stream.writer());
27292781
2730 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;2782 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2731 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;2783 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
...@@ -2748,12 +2800,19 @@ fn writeBindingInfoTable(self: *MachO) !void {...@@ -2748,12 +2800,19 @@ fn writeBindingInfoTable(self: *MachO) !void {
2748fn writeLazyBindingInfoTable(self: *MachO) !void {2800fn writeLazyBindingInfoTable(self: *MachO) !void {
2749 if (!self.lazy_binding_info_dirty) return;2801 if (!self.lazy_binding_info_dirty) return;
27502802
2751 const size = try self.lazy_binding_info_table.calcSize();2803 var symbols = try self.base.allocator.alloc(*const ExternSymbol, self.extern_lazy_symbols.items().len);
2804 defer self.base.allocator.free(symbols);
2805
2806 for (self.extern_lazy_symbols.items()) |*entry, i| {
2807 symbols[i] = &entry.value;
2808 }
2809
2810 const size = try lazyBindInfoSize(symbols);
2752 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));2811 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
2753 defer self.base.allocator.free(buffer);2812 defer self.base.allocator.free(buffer);
27542813
2755 var stream = std.io.fixedBufferStream(buffer);2814 var stream = std.io.fixedBufferStream(buffer);
2756 try self.lazy_binding_info_table.write(stream.writer());2815 try writeLazyBindInfo(symbols, stream.writer());
27572816
2758 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;2817 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2759 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;2818 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
...@@ -3001,7 +3060,7 @@ fn parseBindingInfoTable(self: *MachO) !void {...@@ -3001,7 +3060,7 @@ fn parseBindingInfoTable(self: *MachO) !void {
3001 assert(nread == buffer.len);3060 assert(nread == buffer.len);
30023061
3003 var stream = std.io.fixedBufferStream(buffer);3062 var stream = std.io.fixedBufferStream(buffer);
3004 try self.binding_info_table.read(stream.reader(), self.base.allocator);3063 // try self.binding_info_table.read(stream.reader(), self.base.allocator);
3005}3064}
30063065
3007fn parseLazyBindingInfoTable(self: *MachO) !void {3066fn parseLazyBindingInfoTable(self: *MachO) !void {
...@@ -3012,5 +3071,5 @@ fn parseLazyBindingInfoTable(self: *MachO) !void {...@@ -3012,5 +3071,5 @@ fn parseLazyBindingInfoTable(self: *MachO) !void {
3012 assert(nread == buffer.len);3071 assert(nread == buffer.len);
30133072
3014 var stream = std.io.fixedBufferStream(buffer);3073 var stream = std.io.fixedBufferStream(buffer);
3015 try self.lazy_binding_info_table.read(stream.reader(), self.base.allocator);3074 // try self.lazy_binding_info_table.read(stream.reader(), self.base.allocator);
3016}3075}
src/link/MachO/imports.zig+126-314
...@@ -6,365 +6,177 @@ const mem = std.mem;...@@ -6,365 +6,177 @@ const mem = std.mem;
6const assert = std.debug.assert;6const assert = std.debug.assert;
7const Allocator = mem.Allocator;7const Allocator = mem.Allocator;
88
9pub const RebaseInfoTable = struct {9pub const ExternSymbol = struct {
10 rebase_type: u8 = macho.REBASE_TYPE_POINTER,10 /// Symbol name.
11 symbols: std.ArrayListUnmanaged(Symbol) = .{},11 /// We own the memory, therefore we'll need to free it by calling `deinit`.
12 /// In self-hosted, we don't expect it to be null ever.
13 /// However, this is for backwards compatibility with LLD when
14 /// we'll be patching things up post mortem.
15 name: ?[]u8 = null,
1216
13 pub const Symbol = struct {17 /// Id of the dynamic library where the specified entries can be found.
14 segment: u8,18 /// Id of 0 means self.
15 offset: i64,19 /// TODO this should really be an id into the table of all defined
16 };20 /// dylibs.
1721 dylib_ordinal: i64 = 0,
18 pub fn deinit(self: *RebaseInfoTable, allocator: *Allocator) void {
19 self.symbols.deinit(allocator);
20 }
21
22 /// Write the rebase info table to byte stream.
23 pub fn write(self: RebaseInfoTable, writer: anytype) !void {
24 for (self.symbols.items) |symbol| {
25 try writer.writeByte(macho.REBASE_OPCODE_SET_TYPE_IMM | @truncate(u4, self.rebase_type));
26 try writer.writeByte(macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
27 try leb.writeILEB128(writer, symbol.offset);
28 try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | @truncate(u4, 1));
29 }
30
31 try writer.writeByte(macho.REBASE_OPCODE_DONE);
32 }
3322
34 /// Calculate size in bytes of this rebase info table.23 segment: u16 = 0,
35 pub fn calcSize(self: *RebaseInfoTable) !u64 {24 offset: u32 = 0,
36 var stream = std.io.countingWriter(std.io.null_writer);25 addend: ?i32 = null,
37 var writer = stream.writer();26 index: u32,
38 var size: u64 = 0;
3927
40 for (self.symbols.items) |symbol| {28 pub fn deinit(self: *ExternSymbol, allocator: *Allocator) void {
41 size += 2;29 if (self.name) |*name| {
42 try leb.writeILEB128(writer, symbol.offset);30 allocator.free(name);
43 size += 1;
44 }31 }
45
46 size += 1 + stream.bytes_written;
47 return size;
48 }32 }
49};33};
5034
51/// Table of binding info entries used to tell the dyld which35pub fn rebaseInfoSize(symbols: []*const ExternSymbol) !u64 {
52/// symbols to bind at loading time.36 var stream = std.io.countingWriter(std.io.null_writer);
53pub const BindingInfoTable = struct {37 var writer = stream.writer();
54 /// Id of the dynamic library where the specified entries can be found.38 var size: u64 = 0;
55 dylib_ordinal: i64 = 0,
56
57 /// Binding type; defaults to pointer type.
58 binding_type: u8 = macho.BIND_TYPE_POINTER,
59
60 symbols: std.ArrayListUnmanaged(Symbol) = .{},
6139
62 pub const Symbol = struct {40 for (symbols) |symbol| {
63 /// Symbol name.41 size += 2;
64 name: ?[]u8 = null,42 try leb.writeILEB128(writer, symbol.offset);
43 size += 1;
44 }
6545
66 /// Id of the segment where to bind this symbol to.46 size += 1 + stream.bytes_written;
67 segment: u8,47 return size;
48}
6849
69 /// Offset of this symbol wrt to the segment id encoded in `segment`.50pub fn writeRebaseInfo(symbols: []*const ExternSymbol, writer: anytype) !void {
70 offset: i64,51 for (symbols) |symbol| {
52 try writer.writeByte(macho.REBASE_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.REBASE_TYPE_POINTER));
53 try writer.writeByte(macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
54 try leb.writeILEB128(writer, symbol.offset);
55 try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | @truncate(u4, 1));
56 }
57 try writer.writeByte(macho.REBASE_OPCODE_DONE);
58}
7159
72 /// Addend value (if any).60pub fn bindInfoSize(symbols: []*const ExternSymbol) !u64 {
73 addend: ?i64 = null,61 var stream = std.io.countingWriter(std.io.null_writer);
74 };62 var writer = stream.writer();
63 var size: u64 = 0;
7564
76 pub fn deinit(self: *BindingInfoTable, allocator: *Allocator) void {65 for (symbols) |symbol| {
77 for (self.symbols.items) |*symbol| {66 size += 1;
78 if (symbol.name) |name| {67 if (symbol.dylib_ordinal > 15) {
79 allocator.free(name);68 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
80 }
81 }69 }
82 self.symbols.deinit(allocator);70 size += 1;
83 }
8471
85 /// Parse the binding info table from byte stream.72 if (symbol.name) |name| {
86 pub fn read(self: *BindingInfoTable, reader: anytype, allocator: *Allocator) !void {73 size += 1;
87 var symbol: Symbol = .{74 size += name.len;
88 .segment = 0,75 size += 1;
89 .offset = 0,76 }
90 };
9177
92 var dylib_ordinal_set = false;78 size += 1;
93 var done = false;79 try leb.writeILEB128(writer, symbol.offset);
94 while (true) {
95 const inst = reader.readByte() catch |err| switch (err) {
96 error.EndOfStream => break,
97 else => return err,
98 };
99 const imm: u8 = inst & macho.BIND_IMMEDIATE_MASK;
100 const opcode: u8 = inst & macho.BIND_OPCODE_MASK;
10180
102 switch (opcode) {81 if (symbol.addend) |addend| {
103 macho.BIND_OPCODE_DO_BIND => {82 size += 1;
104 try self.symbols.append(allocator, symbol);83 try leb.writeILEB128(writer, addend);
105 symbol = .{
106 .segment = 0,
107 .offset = 0,
108 };
109 },
110 macho.BIND_OPCODE_DONE => {
111 done = true;
112 break;
113 },
114 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
115 var name = std.ArrayList(u8).init(allocator);
116 var next = try reader.readByte();
117 while (next != @as(u8, 0)) {
118 try name.append(next);
119 next = try reader.readByte();
120 }
121 symbol.name = name.toOwnedSlice();
122 },
123 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
124 symbol.segment = imm;
125 symbol.offset = try leb.readILEB128(i64, reader);
126 },
127 macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => {
128 assert(!dylib_ordinal_set);
129 self.dylib_ordinal = imm;
130 },
131 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
132 assert(!dylib_ordinal_set);
133 self.dylib_ordinal = try leb.readILEB128(i64, reader);
134 },
135 macho.BIND_OPCODE_SET_TYPE_IMM => {
136 self.binding_type = imm;
137 },
138 macho.BIND_OPCODE_SET_ADDEND_SLEB => {
139 symbol.addend = try leb.readILEB128(i64, reader);
140 },
141 else => {
142 std.log.warn("unhandled BIND_OPCODE_: 0x{x}", .{opcode});
143 },
144 }
145 }84 }
146 assert(done);85
86 size += 2;
147 }87 }
14888
149 /// Write the binding info table to byte stream.89 size += stream.bytes_written;
150 pub fn write(self: BindingInfoTable, writer: anytype) !void {90 return size;
151 if (self.dylib_ordinal > 15) {91}
92
93pub fn writeBindInfo(symbols: []*const ExternSymbol, writer: anytype) !void {
94 for (symbols) |symbol| {
95 if (symbol.dylib_ordinal > 15) {
152 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);96 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
153 try leb.writeULEB128(writer, @bitCast(u64, self.dylib_ordinal));97 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
154 } else if (self.dylib_ordinal > 0) {98 } else if (symbol.dylib_ordinal > 0) {
155 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, self.dylib_ordinal)));99 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
156 } else {100 } else {
157 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, self.dylib_ordinal)));101 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
158 }102 }
159 try writer.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @truncate(u4, self.binding_type));103 try writer.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.BIND_TYPE_POINTER));
160
161 for (self.symbols.items) |symbol| {
162 if (symbol.name) |name| {
163 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
164 try writer.writeAll(name);
165 try writer.writeByte(0);
166 }
167104
168 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));105 if (symbol.name) |name| {
169 try leb.writeILEB128(writer, symbol.offset);106 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
107 try writer.writeAll(name);
108 try writer.writeByte(0);
109 }
170110
171 if (symbol.addend) |addend| {111 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
172 try writer.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB);112 try leb.writeILEB128(writer, symbol.offset);
173 try leb.writeILEB128(writer, addend);
174 }
175113
176 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);114 if (symbol.addend) |addend| {
115 try writer.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB);
116 try leb.writeILEB128(writer, addend);
177 }117 }
178118
119 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
179 try writer.writeByte(macho.BIND_OPCODE_DONE);120 try writer.writeByte(macho.BIND_OPCODE_DONE);
180 }121 }
122}
181123
182 /// Calculate size in bytes of this binding info table.124pub fn lazyBindInfoSize(symbols: []*const ExternSymbol) !u64 {
183 pub fn calcSize(self: *BindingInfoTable) !u64 {125 var stream = std.io.countingWriter(std.io.null_writer);
184 var stream = std.io.countingWriter(std.io.null_writer);126 var writer = stream.writer();
185 var writer = stream.writer();127 var size: u64 = 0;
186 var size: u64 = 1;
187
188 if (self.dylib_ordinal > 15) {
189 try leb.writeULEB128(writer, @bitCast(u64, self.dylib_ordinal));
190 }
191128
129 for (symbols) |symbol| {
192 size += 1;130 size += 1;
131 try leb.writeILEB128(writer, symbol.offset);
193132
194 for (self.symbols.items) |symbol| {133 if (symbol.addend) |addend| {
195 if (symbol.name) |name| {
196 size += 1;
197 size += name.len;
198 size += 1;
199 }
200
201 size += 1;
202 try leb.writeILEB128(writer, symbol.offset);
203
204 if (symbol.addend) |addend| {
205 size += 1;
206 try leb.writeILEB128(writer, addend);
207 }
208
209 size += 1;134 size += 1;
135 try leb.writeILEB128(writer, addend);
210 }136 }
211137
212 size += 1 + stream.bytes_written;138 size += 1;
213 return size;139 if (symbol.dylib_ordinal > 15) {
214 }140 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
215};
216
217/// Table of lazy binding info entries used to tell the dyld which
218/// symbols to lazily bind at first load of a dylib.
219pub const LazyBindingInfoTable = struct {
220 symbols: std.ArrayListUnmanaged(Symbol) = .{},
221
222 pub const Symbol = struct {
223 /// Symbol name.
224 name: ?[]u8 = null,
225
226 /// Offset of this symbol wrt to the segment id encoded in `segment`.
227 offset: i64,
228
229 /// Id of the dylib where this symbol is expected to reside.
230 /// Positive ordinals point at dylibs imported with LC_LOAD_DYLIB,
231 /// 0 means this binary, -1 the main executable, and -2 flat lookup.
232 dylib_ordinal: i64,
233
234 /// Id of the segment where to bind this symbol to.
235 segment: u8,
236
237 /// Addend value (if any).
238 addend: ?i64 = null,
239 };
240
241 pub fn deinit(self: *LazyBindingInfoTable, allocator: *Allocator) void {
242 for (self.symbols.items) |*symbol| {
243 if (symbol.name) |name| {
244 allocator.free(name);
245 }
246 }141 }
247 self.symbols.deinit(allocator);142 if (symbol.name) |name| {
248 }143 size += 1;
249144 size += name.len;
250 /// Parse the binding info table from byte stream.145 size += 1;
251 pub fn read(self: *LazyBindingInfoTable, reader: anytype, allocator: *Allocator) !void {
252 var symbol: Symbol = .{
253 .offset = 0,
254 .segment = 0,
255 .dylib_ordinal = 0,
256 };
257
258 var done = false;
259 while (true) {
260 const inst = reader.readByte() catch |err| switch (err) {
261 error.EndOfStream => break,
262 else => return err,
263 };
264 const imm: u8 = inst & macho.BIND_IMMEDIATE_MASK;
265 const opcode: u8 = inst & macho.BIND_OPCODE_MASK;
266
267 switch (opcode) {
268 macho.BIND_OPCODE_DO_BIND => {
269 try self.symbols.append(allocator, symbol);
270 },
271 macho.BIND_OPCODE_DONE => {
272 done = true;
273 symbol = .{
274 .offset = 0,
275 .segment = 0,
276 .dylib_ordinal = 0,
277 };
278 },
279 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
280 var name = std.ArrayList(u8).init(allocator);
281 var next = try reader.readByte();
282 while (next != @as(u8, 0)) {
283 try name.append(next);
284 next = try reader.readByte();
285 }
286 symbol.name = name.toOwnedSlice();
287 },
288 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
289 symbol.segment = imm;
290 symbol.offset = try leb.readILEB128(i64, reader);
291 },
292 macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => {
293 symbol.dylib_ordinal = imm;
294 },
295 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
296 symbol.dylib_ordinal = try leb.readILEB128(i64, reader);
297 },
298 macho.BIND_OPCODE_SET_ADDEND_SLEB => {
299 symbol.addend = try leb.readILEB128(i64, reader);
300 },
301 else => {
302 std.log.warn("unhandled BIND_OPCODE_: 0x{x}", .{opcode});
303 },
304 }
305 }146 }
306 assert(done);147 size += 2;
307 }148 }
308149
309 /// Write the binding info table to byte stream.150 size += stream.bytes_written;
310 pub fn write(self: LazyBindingInfoTable, writer: anytype) !void {151 return size;
311 for (self.symbols.items) |symbol| {152}
312 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
313 try leb.writeILEB128(writer, symbol.offset);
314
315 if (symbol.addend) |addend| {
316 try writer.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB);
317 try leb.writeILEB128(writer, addend);
318 }
319153
320 if (symbol.dylib_ordinal > 15) {154pub fn writeLazyBindInfo(symbols: []*const ExternSymbol, writer: anytype) !void {
321 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);155 for (symbols) |symbol| {
322 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));156 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
323 } else if (symbol.dylib_ordinal > 0) {157 try leb.writeILEB128(writer, symbol.offset);
324 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
325 } else {
326 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
327 }
328158
329 if (symbol.name) |name| {159 if (symbol.addend) |addend| {
330 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.160 try writer.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB);
331 try writer.writeAll(name);161 try leb.writeILEB128(writer, addend);
332 try writer.writeByte(0);
333 }
334
335 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
336 try writer.writeByte(macho.BIND_OPCODE_DONE);
337 }162 }
338 }
339163
340 /// Calculate size in bytes of this binding info table.164 if (symbol.dylib_ordinal > 15) {
341 pub fn calcSize(self: *LazyBindingInfoTable) !u64 {165 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
342 var stream = std.io.countingWriter(std.io.null_writer);166 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
343 var writer = stream.writer();167 } else if (symbol.dylib_ordinal > 0) {
344 var size: u64 = 0;168 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
345169 } else {
346 for (self.symbols.items) |symbol| {170 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
347 size += 1;171 }
348 try leb.writeILEB128(writer, symbol.offset);
349
350 if (symbol.addend) |addend| {
351 size += 1;
352 try leb.writeILEB128(writer, addend);
353 }
354172
355 size += 1;173 if (symbol.name) |name| {
356 if (symbol.dylib_ordinal > 15) {174 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
357 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));175 try writer.writeAll(name);
358 }176 try writer.writeByte(0);
359 if (symbol.name) |name| {
360 size += 1;
361 size += name.len;
362 size += 1;
363 }
364 size += 2;
365 }177 }
366178
367 size += stream.bytes_written;179 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
368 return size;180 try writer.writeByte(macho.BIND_OPCODE_DONE);
369 }181 }
370};182}