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 {
18611861 }
18621862 } else if (func_value.castTag(.extern_fn)) |func_payload| {
18631863 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.
18641866 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);
18661868 const symbol: u32 = blk: {
1867 if (macho_file.externs.get(decl_name)) |index| {
1869 if (macho_file.extern_lazy_symbols.get(decl_name)) |sym| {
18681870 self.bin_file.allocator.free(decl_name);
1869 break :blk index;
1871 break :blk sym.index;
18701872 } else {
1871 const extern_index = @intCast(u32, macho_file.undef_symbols.items.len - 1); // TODO
1872 try macho_file.externs.putNoClobber(self.bin_file.allocator, decl_name, extern_index);
1873 const name = try macho_file.makeString(decl_name);
1874 try macho_file.undef_symbols.append(self.bin_file.allocator, .{
1875 .n_strx = name,
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,
1873 const index = @intCast(u32, macho_file.extern_lazy_symbols.items().len);
1874 try macho_file.extern_lazy_symbols.putNoClobber(self.bin_file.allocator, decl_name, .{
1875 .name = decl_name,
1876 .dylib_ordinal = 1, // TODO this is now hardcoded, since we only support libSystem.
1877 .index = index,
18801878 });
1881 break :blk extern_index;
1879 break :blk index;
18821880 }
18831881 };
18841882 try macho_file.stub_fixups.append(self.bin_file.allocator, .{
18851883 .symbol = symbol,
1886 .exists = exists,
1884 .already_defined = already_defined,
18871885 .start = self.code.items.len,
18881886 .len = 4,
18891887 });
src/link/MachO.zig+221-162
......@@ -105,16 +105,17 @@ entry_addr: ?u64 = null,
105105/// Table of all local symbols
106106/// Internally references string table for names (which are optional).
107107local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
108/// Table of all defined global symbols
108/// Table of all global symbols
109109global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
110/// Table of all undefined symbols
111undef_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
110/// Table of all extern nonlazy symbols, indexed by name.
111extern_nonlazy_symbols: std.StringArrayHashMapUnmanaged(ExternSymbol) = .{},
112/// Table of all extern lazy symbols, indexed by name.
113extern_lazy_symbols: std.StringArrayHashMapUnmanaged(ExternSymbol) = .{},
112114
113115local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
114116global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
115117offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
116118
117dyld_stub_binder_index: ?u16 = null,
118119stub_helper_stubs_start_off: ?u64 = null,
119120
120121/// Table of symbol names aka the string table.
......@@ -123,13 +124,6 @@ string_table: std.ArrayListUnmanaged(u8) = .{},
123124/// Table of trampolines to the actual symbols in __text section.
124125offset_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
133127error_flags: File.ErrorFlags = File.ErrorFlags{},
134128
135129offset_table_count_dirty: bool = false,
......@@ -167,11 +161,10 @@ last_text_block: ?*TextBlock = null,
167161pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{},
168162
169163stub_fixups: std.ArrayListUnmanaged(StubFixup) = .{},
170externs: std.StringHashMapUnmanaged(u32) = .{},
171164
172165pub const StubFixup = struct {
173166 symbol: u32,
174 exists: bool,
167 already_defined: bool,
175168 start: usize,
176169 len: usize,
177170};
......@@ -920,42 +913,42 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
920913 return error.NoSymbolTableFound;
921914 }
922915
923 // Parse dyld info
924 try self.parseBindingInfoTable();
925 try self.parseLazyBindingInfoTable();
916 // // Parse dyld info
917 // try self.parseBindingInfoTable();
918 // try self.parseLazyBindingInfoTable();
926919
927 // Update the dylib ordinals.
928 self.binding_info_table.dylib_ordinal = next_ordinal;
929 for (self.lazy_binding_info_table.symbols.items) |*symbol| {
930 symbol.dylib_ordinal = next_ordinal;
931 }
920 // // Update the dylib ordinals.
921 // self.binding_info_table.dylib_ordinal = next_ordinal;
922 // for (self.lazy_binding_info_table.symbols.items) |*symbol| {
923 // symbol.dylib_ordinal = next_ordinal;
924 // }
932925
933 // Write updated dyld info.
934 const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
935 {
936 const size = try self.binding_info_table.calcSize();
937 assert(dyld_info.bind_size >= size);
926 // // Write updated dyld info.
927 // const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
928 // {
929 // const size = try self.binding_info_table.calcSize();
930 // assert(dyld_info.bind_size >= size);
938931
939 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
940 defer self.base.allocator.free(buffer);
932 // var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
933 // defer self.base.allocator.free(buffer);
941934
942 var stream = std.io.fixedBufferStream(buffer);
943 try self.binding_info_table.write(stream.writer());
935 // var stream = std.io.fixedBufferStream(buffer);
936 // try self.binding_info_table.write(stream.writer());
944937
945 try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off);
946 }
947 {
948 const size = try self.lazy_binding_info_table.calcSize();
949 assert(dyld_info.lazy_bind_size >= size);
938 // try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off);
939 // }
940 // {
941 // const size = try self.lazy_binding_info_table.calcSize();
942 // assert(dyld_info.lazy_bind_size >= size);
950943
951 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
952 defer self.base.allocator.free(buffer);
944 // var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
945 // defer self.base.allocator.free(buffer);
953946
954 var stream = std.io.fixedBufferStream(buffer);
955 try self.lazy_binding_info_table.write(stream.writer());
947 // var stream = std.io.fixedBufferStream(buffer);
948 // try self.lazy_binding_info_table.write(stream.writer());
956949
957 try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off);
958 }
950 // try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off);
951 // }
959952
960953 // Write updated load commands and the header
961954 try self.writeLoadCommands();
......@@ -1037,14 +1030,13 @@ pub fn deinit(self: *MachO) void {
10371030 if (self.d_sym) |*ds| {
10381031 ds.deinit(self.base.allocator);
10391032 }
1040 self.binding_info_table.deinit(self.base.allocator);
1041 self.lazy_binding_info_table.deinit(self.base.allocator);
10421033 self.pie_fixups.deinit(self.base.allocator);
10431034 self.text_block_free_list.deinit(self.base.allocator);
10441035 self.offset_table.deinit(self.base.allocator);
10451036 self.offset_table_free_list.deinit(self.base.allocator);
10461037 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);
10481040 self.global_symbols.deinit(self.base.allocator);
10491041 self.global_symbol_free_list.deinit(self.base.allocator);
10501042 self.local_symbols.deinit(self.base.allocator);
......@@ -1261,59 +1253,28 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
12611253 }
12621254
12631255 // Resolve stubs (if any)
1264 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.?];
1256 const stubs = text_segment.sections.items[self.stubs_section_index.?];
12681257 for (self.stub_fixups.items) |fixup| {
1269 // TODO increment offset for stub writing
12701258 const stub_addr = stubs.addr + fixup.symbol * stubs.reserved2;
12711259 const text_addr = symbol.n_value + fixup.start;
12721260 const displacement = @intCast(u32, stub_addr - text_addr);
12731261 var placeholder = code_buffer.items[fixup.start..][0..fixup.len];
1274 mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.bl(@intCast(i28, displacement)).toU32());
1275
1276 if (!fixup.exists) {
1277 const stub_off = self.stub_helper_stubs_start_off.? + fixup.symbol * 3 * @sizeOf(u32);
1278 const end = stub_h.addr + stub_off - stub_h.offset;
1279 var buf: [@sizeOf(u64)]u8 = undefined;
1280 mem.writeIntLittle(u64, &buf, end);
1281 try self.base.file.?.pwriteAll(&buf, la_ptr.offset + fixup.symbol * @sizeOf(u64));
1282
1283 const la_ptr_addr = la_ptr.addr + fixup.symbol * @sizeOf(u64);
1284 const displacement2 = la_ptr_addr - stub_addr;
1285 var ccode: [2 * @sizeOf(u32)]u8 = undefined;
1286 mem.writeIntLittle(u32, ccode[0..4], aarch64.Instruction.ldr(.x16, .{
1287 .literal = @intCast(u19, displacement2 / 4),
1288 }).toU32());
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 });
1262 switch (self.base.options.target.cpu.arch) {
1263 .x86_64 => return error.TODOImplementStubFixupsForx86_64,
1264 .aarch64 => {
1265 mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.bl(@intCast(i28, displacement)).toU32());
1266 },
1267 else => unreachable,
1268 }
1269 if (!fixup.already_defined) {
1270 try self.writeStub(fixup.symbol);
1271 try self.writeStubInStubHelper(fixup.symbol);
1272 try self.writeLazySymbolPointer(fixup.symbol);
1273
1274 const extern_sym = &self.extern_lazy_symbols.items()[fixup.symbol].value;
1275 extern_sym.segment = self.data_segment_cmd_index.?;
1276 extern_sym.offset = fixup.symbol * @sizeOf(u64);
13051277 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 });
13171278 self.lazy_binding_info_dirty = true;
13181279 }
13191280 }
......@@ -2080,51 +2041,51 @@ pub fn populateMissingMetadata(self: *MachO) !void {
20802041 self.header_dirty = true;
20812042 self.load_commands_dirty = true;
20822043 }
2083 if (self.dyld_stub_binder_index == null) {
2084 self.dyld_stub_binder_index = @intCast(u16, self.undef_symbols.items.len);
2085 const name = try self.makeString("dyld_stub_binder");
2086 try self.undef_symbols.append(self.base.allocator, .{
2087 .n_strx = name,
2088 .n_type = macho.N_UNDF | macho.N_EXT,
2089 .n_sect = 0,
2090 .n_desc = macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | macho.N_SYMBOL_RESOLVER,
2091 .n_value = 0,
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,
2044 if (!self.extern_nonlazy_symbols.contains("dyld_stub_binder")) {
2045 const index = @intCast(u32, self.extern_nonlazy_symbols.items().len);
2046 const name = try std.fmt.allocPrint(self.base.allocator, "dyld_stub_binder", .{});
2047 try self.extern_nonlazy_symbols.putNoClobber(self.base.allocator, name, .{
2048 .name = name,
2049 .dylib_ordinal = 1, // TODO this is currently hardcoded.
2050 .index = index,
2051 .segment = self.data_const_segment_cmd_index.?,
2052 .offset = index * @sizeOf(u64),
21022053 });
21032054 self.binding_info_dirty = true;
21042055 }
21052056 if (self.stub_helper_stubs_start_off == null) {
2106 const text = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2107 const sh = &text.sections.items[self.stub_helper_section_index.?];
2108 const data = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2109 const data_data = &data.sections.items[self.data_section_index.?];
2110 const displacement = data_data.addr - sh.addr;
2111 var code: [4 * @sizeOf(u32)]u8 = undefined;
2112 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, @intCast(i21, displacement)).toU32());
2113 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.stp(
2114 .x16,
2115 .x17,
2116 aarch64.Register.sp,
2117 aarch64.Instruction.LoadStorePairOffset.pre_index(-16),
2118 ).toU32());
2119 const dc = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2120 const got = &dc.sections.items[self.data_got_section_index.?];
2121 const displacement2 = got.addr - sh.addr - 2 * @sizeOf(u32);
2122 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.ldr(.x16, .{
2123 .literal = @intCast(u19, displacement2 / 4),
2124 }).toU32());
2125 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.br(.x16).toU32());
2126 self.stub_helper_stubs_start_off = sh.offset + 4 * @sizeOf(u32);
2127 try self.base.file.?.pwriteAll(&code, sh.offset);
2057 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2058 const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?];
2059 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2060 const data = &data_segment.sections.items[self.data_section_index.?];
2061 const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2062 const got = &data_const_segment.sections.items[self.data_got_section_index.?];
2063 switch (self.base.options.target.cpu.arch) {
2064 .x86_64 => return error.TODOImplementStubHelperForX86_64,
2065 .aarch64 => {
2066 var code: [4 * @sizeOf(u32)]u8 = undefined;
2067 {
2068 const displacement = data.addr - stub_helper.addr;
2069 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, @intCast(i21, displacement)).toU32());
2070 }
2071 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.stp(
2072 .x16,
2073 .x17,
2074 aarch64.Register.sp,
2075 aarch64.Instruction.LoadStorePairOffset.pre_index(-16),
2076 ).toU32());
2077 {
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 }
21282089 }
21292090}
21302091
......@@ -2460,11 +2421,73 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
24602421 try self.base.file.?.pwriteAll(&code, off);
24612422}
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
24632486fn relocateSymbolTable(self: *MachO) !void {
24642487 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
24652488 const nlocals = self.local_symbols.items.len;
24662489 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;
24682491 const nsyms = nlocals + nglobals + nundefs;
24692492
24702493 if (symtab.nsyms < nsyms) {
......@@ -2509,7 +2532,31 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
25092532 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
25102533 const nlocals = self.local_symbols.items.len;
25112534 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
25142561 const locals_off = symtab.symoff;
25152562 const locals_size = nlocals * @sizeOf(macho.nlist_64);
......@@ -2521,8 +2568,8 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
25212568
25222569 const undefs_off = globals_off + globals_size;
25232570 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 });
2525 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.undef_symbols.items), undefs_off);
2571 log.debug("writing extern symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off });
2572 try self.base.file.?.pwriteAll(mem.sliceAsBytes(undefs.items), undefs_off);
25262573
25272574 // Update dynamic symbol table.
25282575 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
......@@ -2546,42 +2593,33 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
25462593
25472594 var buf: [@sizeOf(u32)]u8 = undefined;
25482595 var off = dysymtab.indirectsymoff;
2549 var idx: u32 = 0;
25502596
25512597 stubs.reserved1 = 0;
2552 for (self.undef_symbols.items) |sym, i| {
2553 if (i == self.dyld_stub_binder_index.?) {
2554 continue;
2555 }
2556 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
2598 for (self.extern_lazy_symbols.items()) |entry| {
2599 const symtab_idx = @intCast(u32, dysymtab.iundefsym + entry.value.index);
25572600 mem.writeIntLittle(u32, &buf, symtab_idx);
25582601 try self.base.file.?.pwriteAll(&buf, off);
25592602 off += @sizeOf(u32);
25602603 dysymtab.nindirectsyms += 1;
2561 idx += 1;
25622604 }
25632605
2564 got.reserved1 = @intCast(u32, self.undef_symbols.items.len - 1);
2565 if (self.dyld_stub_binder_index) |i| {
2566 const symtab_idx = i + dysymtab.iundefsym;
2606 const base_id = @intCast(u32, self.extern_lazy_symbols.items().len);
2607 got.reserved1 = base_id;
2608 for (self.extern_nonlazy_symbols.items()) |entry| {
2609 const symtab_idx = @intCast(u32, dysymtab.iundefsym + entry.value.index + base_id);
25672610 mem.writeIntLittle(u32, &buf, symtab_idx);
25682611 try self.base.file.?.pwriteAll(&buf, off);
25692612 off += @sizeOf(u32);
25702613 dysymtab.nindirectsyms += 1;
2571 idx += 1;
25722614 }
25732615
2574 la.reserved1 = got.reserved1 + 1;
2575 for (self.undef_symbols.items) |sym, i| {
2576 if (i == self.dyld_stub_binder_index.?) {
2577 continue;
2578 }
2579 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
2616 la.reserved1 = got.reserved1 + @intCast(u32, self.extern_nonlazy_symbols.items().len);
2617 for (self.extern_lazy_symbols.items()) |entry| {
2618 const symtab_idx = @intCast(u32, dysymtab.iundefsym + entry.value.index);
25802619 mem.writeIntLittle(u32, &buf, symtab_idx);
25812620 try self.base.file.?.pwriteAll(&buf, off);
25822621 off += @sizeOf(u32);
25832622 dysymtab.nindirectsyms += 1;
2584 idx += 1;
25852623 }
25862624}
25872625
......@@ -2689,12 +2727,19 @@ fn writeRebaseInfoTable(self: *MachO) !void {
26892727 const tracy = trace(@src());
26902728 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);
26932738 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
26942739 defer self.base.allocator.free(buffer);
26952740
26962741 var stream = std.io.fixedBufferStream(buffer);
2697 try self.rebase_info_table.write(stream.writer());
2742 try writeRebaseInfo(symbols, stream.writer());
26982743
26992744 const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
27002745 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
......@@ -2720,12 +2765,19 @@ fn writeBindingInfoTable(self: *MachO) !void {
27202765 const tracy = trace(@src());
27212766 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);
27242776 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
27252777 defer self.base.allocator.free(buffer);
27262778
27272779 var stream = std.io.fixedBufferStream(buffer);
2728 try self.binding_info_table.write(stream.writer());
2780 try writeBindInfo(symbols, stream.writer());
27292781
27302782 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
27312783 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
......@@ -2748,12 +2800,19 @@ fn writeBindingInfoTable(self: *MachO) !void {
27482800fn writeLazyBindingInfoTable(self: *MachO) !void {
27492801 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);
27522811 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
27532812 defer self.base.allocator.free(buffer);
27542813
27552814 var stream = std.io.fixedBufferStream(buffer);
2756 try self.lazy_binding_info_table.write(stream.writer());
2815 try writeLazyBindInfo(symbols, stream.writer());
27572816
27582817 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
27592818 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
......@@ -3001,7 +3060,7 @@ fn parseBindingInfoTable(self: *MachO) !void {
30013060 assert(nread == buffer.len);
30023061
30033062 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);
30053064}
30063065
30073066fn parseLazyBindingInfoTable(self: *MachO) !void {
......@@ -3012,5 +3071,5 @@ fn parseLazyBindingInfoTable(self: *MachO) !void {
30123071 assert(nread == buffer.len);
30133072
30143073 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);
30163075}
src/link/MachO/imports.zig+126-314
......@@ -6,365 +6,177 @@ const mem = std.mem;
66const assert = std.debug.assert;
77const Allocator = mem.Allocator;
88
9pub const RebaseInfoTable = struct {
10 rebase_type: u8 = macho.REBASE_TYPE_POINTER,
11 symbols: std.ArrayListUnmanaged(Symbol) = .{},
9pub const ExternSymbol = struct {
10 /// Symbol name.
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 {
14 segment: u8,
15 offset: i64,
16 };
17
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 }
17 /// Id of the dynamic library where the specified entries can be found.
18 /// Id of 0 means self.
19 /// TODO this should really be an id into the table of all defined
20 /// dylibs.
21 dylib_ordinal: i64 = 0,
3322
34 /// Calculate size in bytes of this rebase info table.
35 pub fn calcSize(self: *RebaseInfoTable) !u64 {
36 var stream = std.io.countingWriter(std.io.null_writer);
37 var writer = stream.writer();
38 var size: u64 = 0;
23 segment: u16 = 0,
24 offset: u32 = 0,
25 addend: ?i32 = null,
26 index: u32,
3927
40 for (self.symbols.items) |symbol| {
41 size += 2;
42 try leb.writeILEB128(writer, symbol.offset);
43 size += 1;
28 pub fn deinit(self: *ExternSymbol, allocator: *Allocator) void {
29 if (self.name) |*name| {
30 allocator.free(name);
4431 }
45
46 size += 1 + stream.bytes_written;
47 return size;
4832 }
4933};
5034
51/// Table of binding info entries used to tell the dyld which
52/// symbols to bind at loading time.
53pub const BindingInfoTable = struct {
54 /// Id of the dynamic library where the specified entries can be found.
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) = .{},
35pub fn rebaseInfoSize(symbols: []*const ExternSymbol) !u64 {
36 var stream = std.io.countingWriter(std.io.null_writer);
37 var writer = stream.writer();
38 var size: u64 = 0;
6139
62 pub const Symbol = struct {
63 /// Symbol name.
64 name: ?[]u8 = null,
40 for (symbols) |symbol| {
41 size += 2;
42 try leb.writeILEB128(writer, symbol.offset);
43 size += 1;
44 }
6545
66 /// Id of the segment where to bind this symbol to.
67 segment: u8,
46 size += 1 + stream.bytes_written;
47 return size;
48}
6849
69 /// Offset of this symbol wrt to the segment id encoded in `segment`.
70 offset: i64,
50pub fn writeRebaseInfo(symbols: []*const ExternSymbol, writer: anytype) !void {
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).
73 addend: ?i64 = null,
74 };
60pub fn bindInfoSize(symbols: []*const ExternSymbol) !u64 {
61 var stream = std.io.countingWriter(std.io.null_writer);
62 var writer = stream.writer();
63 var size: u64 = 0;
7564
76 pub fn deinit(self: *BindingInfoTable, allocator: *Allocator) void {
77 for (self.symbols.items) |*symbol| {
78 if (symbol.name) |name| {
79 allocator.free(name);
80 }
65 for (symbols) |symbol| {
66 size += 1;
67 if (symbol.dylib_ordinal > 15) {
68 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
8169 }
82 self.symbols.deinit(allocator);
83 }
70 size += 1;
8471
85 /// Parse the binding info table from byte stream.
86 pub fn read(self: *BindingInfoTable, reader: anytype, allocator: *Allocator) !void {
87 var symbol: Symbol = .{
88 .segment = 0,
89 .offset = 0,
90 };
72 if (symbol.name) |name| {
73 size += 1;
74 size += name.len;
75 size += 1;
76 }
9177
92 var dylib_ordinal_set = false;
93 var done = false;
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;
78 size += 1;
79 try leb.writeILEB128(writer, symbol.offset);
10180
102 switch (opcode) {
103 macho.BIND_OPCODE_DO_BIND => {
104 try self.symbols.append(allocator, symbol);
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 }
81 if (symbol.addend) |addend| {
82 size += 1;
83 try leb.writeILEB128(writer, addend);
14584 }
146 assert(done);
85
86 size += 2;
14787 }
14888
149 /// Write the binding info table to byte stream.
150 pub fn write(self: BindingInfoTable, writer: anytype) !void {
151 if (self.dylib_ordinal > 15) {
89 size += stream.bytes_written;
90 return size;
91}
92
93pub fn writeBindInfo(symbols: []*const ExternSymbol, writer: anytype) !void {
94 for (symbols) |symbol| {
95 if (symbol.dylib_ordinal > 15) {
15296 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
153 try leb.writeULEB128(writer, @bitCast(u64, self.dylib_ordinal));
154 } else if (self.dylib_ordinal > 0) {
155 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, self.dylib_ordinal)));
97 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
98 } else if (symbol.dylib_ordinal > 0) {
99 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
156100 } 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)));
158102 }
159 try writer.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @truncate(u4, self.binding_type));
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 }
103 try writer.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.BIND_TYPE_POINTER));
167104
168 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
169 try leb.writeILEB128(writer, symbol.offset);
105 if (symbol.name) |name| {
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| {
172 try writer.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB);
173 try leb.writeILEB128(writer, addend);
174 }
111 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
112 try leb.writeILEB128(writer, symbol.offset);
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);
177117 }
178118
119 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
179120 try writer.writeByte(macho.BIND_OPCODE_DONE);
180121 }
122}
181123
182 /// Calculate size in bytes of this binding info table.
183 pub fn calcSize(self: *BindingInfoTable) !u64 {
184 var stream = std.io.countingWriter(std.io.null_writer);
185 var writer = stream.writer();
186 var size: u64 = 1;
187
188 if (self.dylib_ordinal > 15) {
189 try leb.writeULEB128(writer, @bitCast(u64, self.dylib_ordinal));
190 }
124pub fn lazyBindInfoSize(symbols: []*const ExternSymbol) !u64 {
125 var stream = std.io.countingWriter(std.io.null_writer);
126 var writer = stream.writer();
127 var size: u64 = 0;
191128
129 for (symbols) |symbol| {
192130 size += 1;
131 try leb.writeILEB128(writer, symbol.offset);
193132
194 for (self.symbols.items) |symbol| {
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
133 if (symbol.addend) |addend| {
209134 size += 1;
135 try leb.writeILEB128(writer, addend);
210136 }
211137
212 size += 1 + stream.bytes_written;
213 return size;
214 }
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 }
138 size += 1;
139 if (symbol.dylib_ordinal > 15) {
140 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
246141 }
247 self.symbols.deinit(allocator);
248 }
249
250 /// Parse the binding info table from byte stream.
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 }
142 if (symbol.name) |name| {
143 size += 1;
144 size += name.len;
145 size += 1;
305146 }
306 assert(done);
147 size += 2;
307148 }
308149
309 /// Write the binding info table to byte stream.
310 pub fn write(self: LazyBindingInfoTable, writer: anytype) !void {
311 for (self.symbols.items) |symbol| {
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 }
150 size += stream.bytes_written;
151 return size;
152}
319153
320 if (symbol.dylib_ordinal > 15) {
321 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
322 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
323 } else if (symbol.dylib_ordinal > 0) {
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 }
154pub fn writeLazyBindInfo(symbols: []*const ExternSymbol, writer: anytype) !void {
155 for (symbols) |symbol| {
156 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
157 try leb.writeILEB128(writer, symbol.offset);
328158
329 if (symbol.name) |name| {
330 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
331 try writer.writeAll(name);
332 try writer.writeByte(0);
333 }
334
335 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
336 try writer.writeByte(macho.BIND_OPCODE_DONE);
159 if (symbol.addend) |addend| {
160 try writer.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB);
161 try leb.writeILEB128(writer, addend);
337162 }
338 }
339163
340 /// Calculate size in bytes of this binding info table.
341 pub fn calcSize(self: *LazyBindingInfoTable) !u64 {
342 var stream = std.io.countingWriter(std.io.null_writer);
343 var writer = stream.writer();
344 var size: u64 = 0;
345
346 for (self.symbols.items) |symbol| {
347 size += 1;
348 try leb.writeILEB128(writer, symbol.offset);
349
350 if (symbol.addend) |addend| {
351 size += 1;
352 try leb.writeILEB128(writer, addend);
353 }
164 if (symbol.dylib_ordinal > 15) {
165 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
166 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
167 } else if (symbol.dylib_ordinal > 0) {
168 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
169 } else {
170 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
171 }
354172
355 size += 1;
356 if (symbol.dylib_ordinal > 15) {
357 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
358 }
359 if (symbol.name) |name| {
360 size += 1;
361 size += name.len;
362 size += 1;
363 }
364 size += 2;
173 if (symbol.name) |name| {
174 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
175 try writer.writeAll(name);
176 try writer.writeByte(0);
365177 }
366178
367 size += stream.bytes_written;
368 return size;
179 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
180 try writer.writeByte(macho.BIND_OPCODE_DONE);
369181 }
370};
182}