authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-01-19 19:49:51+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-01-19 19:49:51+01:00
log989b0e620be22278d32ec848069c17667cbd2f4f
treedb956d0f30778310fa0e164a128e1d4aa4a6b20b
parentefbb6128bbfdd0b731578dcc55d4a55732d20d8a
parentbcd16b270861e814e59c699837b1e35db84ab091
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #14369 from ziglang/macho-dyld-ops

macho+zld: add improved dyld opcodes emitters

7 files changed, 1465 insertions(+), 451 deletions(-)

CMakeLists.txt+2-1
......@@ -594,7 +594,8 @@ set(ZIG_STAGE2_SOURCES
594594 "${CMAKE_SOURCE_DIR}/src/link/MachO/Relocation.zig"
595595 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
596596 "${CMAKE_SOURCE_DIR}/src/link/MachO/ZldAtom.zig"
597 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"
597 "${CMAKE_SOURCE_DIR}/src/link/MachO/dyld_info/bind.zig"
598 "${CMAKE_SOURCE_DIR}/src/link/MachO/dyld_info/Rebase.zig"
598599 "${CMAKE_SOURCE_DIR}/src/link/MachO/dead_strip.zig"
599600 "${CMAKE_SOURCE_DIR}/src/link/MachO/fat.zig"
600601 "${CMAKE_SOURCE_DIR}/src/link/MachO/load_commands.zig"
src/link/MachO.zig+59-151
......@@ -14,7 +14,6 @@ const mem = std.mem;
1414const meta = std.meta;
1515
1616const aarch64 = @import("../arch/aarch64/bits.zig");
17const bind = @import("MachO/bind.zig");
1817const codegen = @import("../codegen.zig");
1918const dead_strip = @import("MachO/dead_strip.zig");
2019const fat = @import("MachO/fat.zig");
......@@ -50,6 +49,10 @@ const Value = @import("../value.zig").Value;
5049
5150pub const DebugSymbols = @import("MachO/DebugSymbols.zig");
5251
52const Bind = @import("MachO/dyld_info/bind.zig").Bind(*const MachO, MachO.SymbolWithLoc);
53const LazyBind = @import("MachO/dyld_info/bind.zig").LazyBind(*const MachO, MachO.SymbolWithLoc);
54const Rebase = @import("MachO/dyld_info/Rebase.zig");
55
5356pub const base_tag: File.Tag = File.Tag.macho;
5457
5558pub const SearchStrategy = enum {
......@@ -3192,32 +3195,14 @@ fn writeLinkeditSegmentData(self: *MachO) !void {
31923195 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
31933196}
31943197
3195const AtomLessThanByAddressContext = struct {
3196 macho_file: *MachO,
3197};
3198
3199fn atomLessThanByAddress(ctx: AtomLessThanByAddressContext, lhs: *Atom, rhs: *Atom) bool {
3200 return lhs.getSymbol(ctx.macho_file).n_value < rhs.getSymbol(ctx.macho_file).n_value;
3201}
3202
3203fn collectRebaseData(self: *MachO, pointers: *std.ArrayList(bind.Pointer)) !void {
3198fn collectRebaseData(self: *MachO, rebase: *Rebase) !void {
32043199 const gpa = self.base.allocator;
3205
3206 var sorted_atoms_by_address = std.ArrayList(*Atom).init(gpa);
3207 defer sorted_atoms_by_address.deinit();
3208 try sorted_atoms_by_address.ensureTotalCapacityPrecise(self.rebases.count());
3209
3200 const slice = self.sections.slice();
32103201 var it = self.rebases.keyIterator();
3211 while (it.next()) |key_ptr| {
3212 sorted_atoms_by_address.appendAssumeCapacity(key_ptr.*);
3213 }
32143202
3215 std.sort.sort(*Atom, sorted_atoms_by_address.items, AtomLessThanByAddressContext{
3216 .macho_file = self,
3217 }, atomLessThanByAddress);
3203 while (it.next()) |key_ptr| {
3204 const atom = key_ptr.*;
32183205
3219 const slice = self.sections.slice();
3220 for (sorted_atoms_by_address.items) |atom| {
32213206 log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, atom.getName(self) });
32223207
32233208 const sym = atom.getSymbol(self);
......@@ -3227,36 +3212,29 @@ fn collectRebaseData(self: *MachO, pointers: *std.ArrayList(bind.Pointer)) !void
32273212 const base_offset = sym.n_value - seg.vmaddr;
32283213
32293214 const rebases = self.rebases.get(atom).?;
3230 try pointers.ensureUnusedCapacity(rebases.items.len);
3215 try rebase.entries.ensureUnusedCapacity(gpa, rebases.items.len);
3216
32313217 for (rebases.items) |offset| {
32323218 log.debug(" | rebase at {x}", .{base_offset + offset});
32333219
3234 pointers.appendAssumeCapacity(.{
3220 rebase.entries.appendAssumeCapacity(.{
32353221 .offset = base_offset + offset,
32363222 .segment_id = segment_index,
32373223 });
32383224 }
32393225 }
3226
3227 try rebase.finalize(gpa);
32403228}
32413229
3242fn collectBindData(self: *MachO, pointers: *std.ArrayList(bind.Pointer), raw_bindings: anytype) !void {
3230fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void {
32433231 const gpa = self.base.allocator;
3244
3245 var sorted_atoms_by_address = std.ArrayList(*Atom).init(gpa);
3246 defer sorted_atoms_by_address.deinit();
3247 try sorted_atoms_by_address.ensureTotalCapacityPrecise(raw_bindings.count());
3248
3232 const slice = self.sections.slice();
32493233 var it = raw_bindings.keyIterator();
3250 while (it.next()) |key_ptr| {
3251 sorted_atoms_by_address.appendAssumeCapacity(key_ptr.*);
3252 }
32533234
3254 std.sort.sort(*Atom, sorted_atoms_by_address.items, AtomLessThanByAddressContext{
3255 .macho_file = self,
3256 }, atomLessThanByAddress);
3235 while (it.next()) |key_ptr| {
3236 const atom = key_ptr.*;
32573237
3258 const slice = self.sections.slice();
3259 for (sorted_atoms_by_address.items) |atom| {
32603238 log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, atom.getName(self) });
32613239
32623240 const sym = atom.getSymbol(self);
......@@ -3266,7 +3244,8 @@ fn collectBindData(self: *MachO, pointers: *std.ArrayList(bind.Pointer), raw_bin
32663244 const base_offset = sym.n_value - seg.vmaddr;
32673245
32683246 const bindings = raw_bindings.get(atom).?;
3269 try pointers.ensureUnusedCapacity(bindings.items.len);
3247 try bind.entries.ensureUnusedCapacity(gpa, bindings.items.len);
3248
32703249 for (bindings.items) |binding| {
32713250 const bind_sym = self.getSymbol(binding.target);
32723251 const bind_sym_name = self.getSymbolName(binding.target);
......@@ -3274,7 +3253,6 @@ fn collectBindData(self: *MachO, pointers: *std.ArrayList(bind.Pointer), raw_bin
32743253 @bitCast(i16, bind_sym.n_desc),
32753254 macho.N_SYMBOL_RESOLVER,
32763255 );
3277 var flags: u4 = 0;
32783256 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
32793257 binding.offset + base_offset,
32803258 bind_sym_name,
......@@ -3282,17 +3260,17 @@ fn collectBindData(self: *MachO, pointers: *std.ArrayList(bind.Pointer), raw_bin
32823260 });
32833261 if (bind_sym.weakRef()) {
32843262 log.debug(" | marking as weak ref ", .{});
3285 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
32863263 }
3287 pointers.appendAssumeCapacity(.{
3264 bind.entries.appendAssumeCapacity(.{
3265 .target = binding.target,
32883266 .offset = binding.offset + base_offset,
32893267 .segment_id = segment_index,
3290 .dylib_ordinal = dylib_ordinal,
3291 .name = bind_sym_name,
3292 .bind_flags = flags,
3268 .addend = 0,
32933269 });
32943270 }
32953271 }
3272
3273 try bind.finalize(gpa, self);
32963274}
32973275
32983276fn collectExportData(self: *MachO, trie: *Trie) !void {
......@@ -3345,17 +3323,17 @@ fn writeDyldInfoData(self: *MachO) !void {
33453323
33463324 const gpa = self.base.allocator;
33473325
3348 var rebase_pointers = std.ArrayList(bind.Pointer).init(gpa);
3349 defer rebase_pointers.deinit();
3350 try self.collectRebaseData(&rebase_pointers);
3326 var rebase = Rebase{};
3327 defer rebase.deinit(gpa);
3328 try self.collectRebaseData(&rebase);
33513329
3352 var bind_pointers = std.ArrayList(bind.Pointer).init(gpa);
3353 defer bind_pointers.deinit();
3354 try self.collectBindData(&bind_pointers, self.bindings);
3330 var bind = Bind{};
3331 defer bind.deinit(gpa);
3332 try self.collectBindData(&bind, self.bindings);
33553333
3356 var lazy_bind_pointers = std.ArrayList(bind.Pointer).init(gpa);
3357 defer lazy_bind_pointers.deinit();
3358 try self.collectBindData(&lazy_bind_pointers, self.lazy_bindings);
3334 var lazy_bind = LazyBind{};
3335 defer lazy_bind.deinit(gpa);
3336 try self.collectBindData(&lazy_bind, self.lazy_bindings);
33593337
33603338 var trie: Trie = .{};
33613339 defer trie.deinit(gpa);
......@@ -3364,17 +3342,17 @@ fn writeDyldInfoData(self: *MachO) !void {
33643342 const link_seg = self.getLinkeditSegmentPtr();
33653343 assert(mem.isAlignedGeneric(u64, link_seg.fileoff, @alignOf(u64)));
33663344 const rebase_off = link_seg.fileoff;
3367 const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items);
3345 const rebase_size = rebase.size();
33683346 const rebase_size_aligned = mem.alignForwardGeneric(u64, rebase_size, @alignOf(u64));
33693347 log.debug("writing rebase info from 0x{x} to 0x{x}", .{ rebase_off, rebase_off + rebase_size_aligned });
33703348
33713349 const bind_off = rebase_off + rebase_size_aligned;
3372 const bind_size = try bind.bindInfoSize(bind_pointers.items);
3350 const bind_size = bind.size();
33733351 const bind_size_aligned = mem.alignForwardGeneric(u64, bind_size, @alignOf(u64));
33743352 log.debug("writing bind info from 0x{x} to 0x{x}", .{ bind_off, bind_off + bind_size_aligned });
33753353
33763354 const lazy_bind_off = bind_off + bind_size_aligned;
3377 const lazy_bind_size = try bind.lazyBindInfoSize(lazy_bind_pointers.items);
3355 const lazy_bind_size = lazy_bind.size();
33783356 const lazy_bind_size_aligned = mem.alignForwardGeneric(u64, lazy_bind_size, @alignOf(u64));
33793357 log.debug("writing lazy bind info from 0x{x} to 0x{x}", .{
33803358 lazy_bind_off,
......@@ -3398,13 +3376,13 @@ fn writeDyldInfoData(self: *MachO) !void {
33983376 var stream = std.io.fixedBufferStream(buffer);
33993377 const writer = stream.writer();
34003378
3401 try bind.writeRebaseInfo(rebase_pointers.items, writer);
3379 try rebase.write(writer);
34023380 try stream.seekTo(bind_off - rebase_off);
34033381
3404 try bind.writeBindInfo(bind_pointers.items, writer);
3382 try bind.write(writer);
34053383 try stream.seekTo(lazy_bind_off - rebase_off);
34063384
3407 try bind.writeLazyBindInfo(lazy_bind_pointers.items, writer);
3385 try lazy_bind.write(writer);
34083386 try stream.seekTo(export_off - rebase_off);
34093387
34103388 _ = try trie.write(writer);
......@@ -3415,9 +3393,7 @@ fn writeDyldInfoData(self: *MachO) !void {
34153393 });
34163394
34173395 try self.base.file.?.pwriteAll(buffer, rebase_off);
3418 const start = math.cast(usize, lazy_bind_off - rebase_off) orelse return error.Overflow;
3419 const end = start + (math.cast(usize, lazy_bind_size) orelse return error.Overflow);
3420 try self.populateLazyBindOffsetsInStubHelper(buffer[start..end]);
3396 try self.populateLazyBindOffsetsInStubHelper(lazy_bind);
34213397
34223398 self.dyld_info_cmd.rebase_off = @intCast(u32, rebase_off);
34233399 self.dyld_info_cmd.rebase_size = @intCast(u32, rebase_size_aligned);
......@@ -3429,102 +3405,33 @@ fn writeDyldInfoData(self: *MachO) !void {
34293405 self.dyld_info_cmd.export_size = @intCast(u32, export_size_aligned);
34303406}
34313407
3432fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
3433 const gpa = self.base.allocator;
3408fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void {
3409 if (lazy_bind.size() == 0) return;
34343410
3435 const stub_helper_section_index = self.stub_helper_section_index orelse return;
3436 if (self.stub_helper_preamble_atom == null) return;
3411 const stub_helper_section_index = self.stub_helper_section_index.?;
3412 assert(self.stub_helper_preamble_atom != null);
34373413
34383414 const section = self.sections.get(stub_helper_section_index);
3439 const last_atom = section.last_atom orelse return;
3440 if (last_atom == self.stub_helper_preamble_atom.?) return; // TODO is this a redundant check?
3441
3442 var table = std.AutoHashMap(i64, *Atom).init(gpa);
3443 defer table.deinit();
34443415
3445 {
3446 var stub_atom = last_atom;
3447 var laptr_atom = self.sections.items(.last_atom)[self.la_symbol_ptr_section_index.?].?;
3448 const base_addr = self.getSegment(self.la_symbol_ptr_section_index.?).vmaddr;
3449
3450 while (true) {
3451 const laptr_off = blk: {
3452 const sym = laptr_atom.getSymbol(self);
3453 break :blk @intCast(i64, sym.n_value - base_addr);
3454 };
3455 try table.putNoClobber(laptr_off, stub_atom);
3456 if (laptr_atom.prev) |prev| {
3457 laptr_atom = prev;
3458 stub_atom = stub_atom.prev.?;
3459 } else break;
3460 }
3461 }
3462
3463 var stream = std.io.fixedBufferStream(buffer);
3464 var reader = stream.reader();
3465 var offsets = std.ArrayList(struct { sym_offset: i64, offset: u32 }).init(gpa);
3466 try offsets.append(.{ .sym_offset = undefined, .offset = 0 });
3467 defer offsets.deinit();
3468 var valid_block = false;
3469
3470 while (true) {
3471 const inst = reader.readByte() catch |err| switch (err) {
3472 error.EndOfStream => break,
3473 };
3474 const opcode: u8 = inst & macho.BIND_OPCODE_MASK;
3475
3476 switch (opcode) {
3477 macho.BIND_OPCODE_DO_BIND => {
3478 valid_block = true;
3479 },
3480 macho.BIND_OPCODE_DONE => {
3481 if (valid_block) {
3482 const offset = try stream.getPos();
3483 try offsets.append(.{ .sym_offset = undefined, .offset = @intCast(u32, offset) });
3484 }
3485 valid_block = false;
3486 },
3487 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
3488 var next = try reader.readByte();
3489 while (next != @as(u8, 0)) {
3490 next = try reader.readByte();
3491 }
3492 },
3493 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
3494 var inserted = offsets.pop();
3495 inserted.sym_offset = try std.leb.readILEB128(i64, reader);
3496 try offsets.append(inserted);
3497 },
3498 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
3499 _ = try std.leb.readULEB128(u64, reader);
3500 },
3501 macho.BIND_OPCODE_SET_ADDEND_SLEB => {
3502 _ = try std.leb.readILEB128(i64, reader);
3503 },
3504 else => {},
3505 }
3506 }
3507
3508 const header = self.sections.items(.header)[stub_helper_section_index];
35093416 const stub_offset: u4 = switch (self.base.options.target.cpu.arch) {
35103417 .x86_64 => 1,
35113418 .aarch64 => 2 * @sizeOf(u32),
35123419 else => unreachable,
35133420 };
3514 var buf: [@sizeOf(u32)]u8 = undefined;
3515 _ = offsets.pop();
3421 const header = section.header;
3422 var atom = section.last_atom.?;
35163423
3517 while (offsets.popOrNull()) |bind_offset| {
3518 const atom = table.get(bind_offset.sym_offset).?;
3424 var index: usize = 0;
3425 while (index < lazy_bind.offsets.items.len) : (index += 1) {
35193426 const sym = atom.getSymbol(self);
35203427 const file_offset = header.offset + sym.n_value - header.addr + stub_offset;
3521 mem.writeIntLittle(u32, &buf, bind_offset.offset);
3522 log.debug("writing lazy bind offset in stub helper of 0x{x} for symbol {s} at offset 0x{x}", .{
3523 bind_offset.offset,
3524 atom.getName(self),
3525 file_offset,
3526 });
3527 try self.base.file.?.pwriteAll(&buf, file_offset);
3428 const bind_offset = lazy_bind.offsets.items[index];
3429
3430 log.debug("writing lazy bind offset 0x{x} in stub helper at 0x{x}", .{ bind_offset, file_offset });
3431
3432 try self.base.file.?.pwriteAll(mem.asBytes(&bind_offset), file_offset);
3433
3434 atom = atom.prev.?;
35283435 }
35293436}
35303437
......@@ -3912,12 +3819,13 @@ pub fn getSymbolPtr(self: *MachO, sym_with_loc: SymbolWithLoc) *macho.nlist_64 {
39123819}
39133820
39143821/// Returns symbol described by `sym_with_loc` descriptor.
3915pub fn getSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) macho.nlist_64 {
3916 return self.getSymbolPtr(sym_with_loc).*;
3822pub fn getSymbol(self: *const MachO, sym_with_loc: SymbolWithLoc) macho.nlist_64 {
3823 assert(sym_with_loc.file == null);
3824 return self.locals.items[sym_with_loc.sym_index];
39173825}
39183826
39193827/// Returns name of the symbol described by `sym_with_loc` descriptor.
3920pub fn getSymbolName(self: *MachO, sym_with_loc: SymbolWithLoc) []const u8 {
3828pub fn getSymbolName(self: *const MachO, sym_with_loc: SymbolWithLoc) []const u8 {
39213829 assert(sym_with_loc.file == null);
39223830 const sym = self.locals.items[sym_with_loc.sym_index];
39233831 return self.strtab.get(sym.n_strx).?;
src/link/MachO/bind.zig deleted-138
......@@ -1,138 +0,0 @@
1const std = @import("std");
2const leb = std.leb;
3const macho = std.macho;
4
5pub const Pointer = struct {
6 offset: u64,
7 segment_id: u16,
8 dylib_ordinal: ?i64 = null,
9 name: ?[]const u8 = null,
10 bind_flags: u4 = 0,
11};
12
13pub fn rebaseInfoSize(pointers: []const Pointer) !u64 {
14 var stream = std.io.countingWriter(std.io.null_writer);
15 var writer = stream.writer();
16 var size: u64 = 0;
17
18 for (pointers) |pointer| {
19 size += 2;
20 try leb.writeILEB128(writer, pointer.offset);
21 size += 1;
22 }
23
24 size += 1 + stream.bytes_written;
25 return size;
26}
27
28pub fn writeRebaseInfo(pointers: []const Pointer, writer: anytype) !void {
29 for (pointers) |pointer| {
30 try writer.writeByte(macho.REBASE_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.REBASE_TYPE_POINTER));
31 try writer.writeByte(macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, pointer.segment_id));
32
33 try leb.writeILEB128(writer, pointer.offset);
34 try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | @truncate(u4, 1));
35 }
36 try writer.writeByte(macho.REBASE_OPCODE_DONE);
37}
38
39pub fn bindInfoSize(pointers: []const Pointer) !u64 {
40 var stream = std.io.countingWriter(std.io.null_writer);
41 var writer = stream.writer();
42 var size: u64 = 0;
43
44 for (pointers) |pointer| {
45 size += 1;
46 if (pointer.dylib_ordinal.? > 15) {
47 try leb.writeULEB128(writer, @bitCast(u64, pointer.dylib_ordinal.?));
48 }
49 size += 1;
50
51 size += 1;
52 size += pointer.name.?.len;
53 size += 1;
54
55 size += 1;
56
57 try leb.writeILEB128(writer, pointer.offset);
58 size += 1;
59 }
60
61 size += stream.bytes_written + 1;
62 return size;
63}
64
65pub fn writeBindInfo(pointers: []const Pointer, writer: anytype) !void {
66 for (pointers) |pointer| {
67 if (pointer.dylib_ordinal.? > 15) {
68 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
69 try leb.writeULEB128(writer, @bitCast(u64, pointer.dylib_ordinal.?));
70 } else if (pointer.dylib_ordinal.? > 0) {
71 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, pointer.dylib_ordinal.?)));
72 } else {
73 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, pointer.dylib_ordinal.?)));
74 }
75 try writer.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.BIND_TYPE_POINTER));
76
77 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | pointer.bind_flags);
78 try writer.writeAll(pointer.name.?);
79 try writer.writeByte(0);
80
81 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, pointer.segment_id));
82
83 try leb.writeILEB128(writer, pointer.offset);
84 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
85 }
86
87 try writer.writeByte(macho.BIND_OPCODE_DONE);
88}
89
90pub fn lazyBindInfoSize(pointers: []const Pointer) !u64 {
91 var stream = std.io.countingWriter(std.io.null_writer);
92 var writer = stream.writer();
93 var size: u64 = 0;
94
95 for (pointers) |pointer| {
96 size += 1;
97
98 try leb.writeILEB128(writer, pointer.offset);
99
100 size += 1;
101 if (pointer.dylib_ordinal.? > 15) {
102 try leb.writeULEB128(writer, @bitCast(u64, pointer.dylib_ordinal.?));
103 }
104
105 size += 1;
106 size += pointer.name.?.len;
107 size += 1;
108
109 size += 2;
110 }
111
112 size += stream.bytes_written;
113 return size;
114}
115
116pub fn writeLazyBindInfo(pointers: []const Pointer, writer: anytype) !void {
117 for (pointers) |pointer| {
118 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, pointer.segment_id));
119
120 try leb.writeILEB128(writer, pointer.offset);
121
122 if (pointer.dylib_ordinal.? > 15) {
123 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
124 try leb.writeULEB128(writer, @bitCast(u64, pointer.dylib_ordinal.?));
125 } else if (pointer.dylib_ordinal.? > 0) {
126 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, pointer.dylib_ordinal.?)));
127 } else {
128 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, pointer.dylib_ordinal.?)));
129 }
130
131 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | pointer.bind_flags);
132 try writer.writeAll(pointer.name.?);
133 try writer.writeByte(0);
134
135 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
136 try writer.writeByte(macho.BIND_OPCODE_DONE);
137 }
138}
src/link/MachO/dyld_info/Rebase.zig created+574
......@@ -0,0 +1,574 @@
1const Rebase = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const leb = std.leb;
6const log = std.log.scoped(.dyld_info);
7const macho = std.macho;
8const testing = std.testing;
9
10const Allocator = std.mem.Allocator;
11
12entries: std.ArrayListUnmanaged(Entry) = .{},
13buffer: std.ArrayListUnmanaged(u8) = .{},
14
15const Entry = struct {
16 offset: u64,
17 segment_id: u8,
18
19 pub fn lessThan(ctx: void, entry: Entry, other: Entry) bool {
20 _ = ctx;
21 if (entry.segment_id == other.segment_id) {
22 return entry.offset < other.offset;
23 }
24 return entry.segment_id < other.segment_id;
25 }
26};
27
28pub fn deinit(rebase: *Rebase, gpa: Allocator) void {
29 rebase.entries.deinit(gpa);
30 rebase.buffer.deinit(gpa);
31}
32
33pub fn size(rebase: Rebase) u64 {
34 return @intCast(u64, rebase.buffer.items.len);
35}
36
37pub fn finalize(rebase: *Rebase, gpa: Allocator) !void {
38 if (rebase.entries.items.len == 0) return;
39
40 const writer = rebase.buffer.writer(gpa);
41
42 std.sort.sort(Entry, rebase.entries.items, {}, Entry.lessThan);
43
44 try setTypePointer(writer);
45
46 var start: usize = 0;
47 var seg_id: ?u8 = null;
48 for (rebase.entries.items) |entry, i| {
49 if (seg_id != null and seg_id.? == entry.segment_id) continue;
50 try finalizeSegment(rebase.entries.items[start..i], writer);
51 seg_id = entry.segment_id;
52 start = i;
53 }
54
55 try finalizeSegment(rebase.entries.items[start..], writer);
56 try done(writer);
57}
58
59fn finalizeSegment(entries: []const Entry, writer: anytype) !void {
60 if (entries.len == 0) return;
61
62 const segment_id = entries[0].segment_id;
63 var offset = entries[0].offset;
64 try setSegmentOffset(segment_id, offset, writer);
65
66 var count: usize = 0;
67 var skip: u64 = 0;
68 var state: enum {
69 start,
70 times,
71 times_skip,
72 } = .times;
73
74 var i: usize = 0;
75 while (i < entries.len) : (i += 1) {
76 log.debug("{x}, {d}, {x}, {s}", .{ offset, count, skip, @tagName(state) });
77 const current_offset = entries[i].offset;
78 log.debug(" => {x}", .{current_offset});
79 switch (state) {
80 .start => {
81 if (offset < current_offset) {
82 const delta = current_offset - offset;
83 try addAddr(delta, writer);
84 offset += delta;
85 }
86 state = .times;
87 offset += @sizeOf(u64);
88 count = 1;
89 },
90 .times => {
91 const delta = current_offset - offset;
92 if (delta == 0) {
93 count += 1;
94 offset += @sizeOf(u64);
95 continue;
96 }
97 if (count == 1) {
98 state = .times_skip;
99 skip = delta;
100 offset += skip;
101 i -= 1;
102 } else {
103 try rebaseTimes(count, writer);
104 state = .start;
105 i -= 1;
106 }
107 },
108 .times_skip => {
109 if (current_offset < offset) {
110 count -= 1;
111 if (count == 1) {
112 try rebaseAddAddr(skip, writer);
113 } else {
114 try rebaseTimesSkip(count, skip, writer);
115 }
116 state = .start;
117 offset = offset - (@sizeOf(u64) + skip);
118 i -= 2;
119 continue;
120 }
121
122 const delta = current_offset - offset;
123 if (delta == 0) {
124 count += 1;
125 offset += @sizeOf(u64) + skip;
126 } else {
127 try rebaseTimesSkip(count, skip, writer);
128 state = .start;
129 i -= 1;
130 }
131 },
132 }
133 }
134
135 switch (state) {
136 .start => unreachable,
137 .times => {
138 try rebaseTimes(count, writer);
139 },
140 .times_skip => {
141 try rebaseTimesSkip(count, skip, writer);
142 },
143 }
144}
145
146fn setTypePointer(writer: anytype) !void {
147 log.debug(">>> set type: {d}", .{macho.REBASE_TYPE_POINTER});
148 try writer.writeByte(macho.REBASE_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.REBASE_TYPE_POINTER));
149}
150
151fn setSegmentOffset(segment_id: u8, offset: u64, writer: anytype) !void {
152 log.debug(">>> set segment: {d} and offset: {x}", .{ segment_id, offset });
153 try writer.writeByte(macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, segment_id));
154 try std.leb.writeULEB128(writer, offset);
155}
156
157fn rebaseAddAddr(addr: u64, writer: anytype) !void {
158 log.debug(">>> rebase with add: {x}", .{addr});
159 try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB);
160 try std.leb.writeULEB128(writer, addr);
161}
162
163fn rebaseTimes(count: usize, writer: anytype) !void {
164 log.debug(">>> rebase with count: {d}", .{count});
165 if (count <= 0xf) {
166 try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | @truncate(u4, count));
167 } else {
168 try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES);
169 try std.leb.writeULEB128(writer, count);
170 }
171}
172
173fn rebaseTimesSkip(count: usize, skip: u64, writer: anytype) !void {
174 log.debug(">>> rebase with count: {d} and skip: {x}", .{ count, skip });
175 try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB);
176 try std.leb.writeULEB128(writer, count);
177 try std.leb.writeULEB128(writer, skip);
178}
179
180fn addAddr(addr: u64, writer: anytype) !void {
181 log.debug(">>> add: {x}", .{addr});
182 if (std.mem.isAlignedGeneric(u64, addr, @sizeOf(u64))) {
183 const imm = @divExact(addr, @sizeOf(u64));
184 if (imm <= 0xf) {
185 try writer.writeByte(macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED | @truncate(u4, imm));
186 return;
187 }
188 }
189 try writer.writeByte(macho.REBASE_OPCODE_ADD_ADDR_ULEB);
190 try std.leb.writeULEB128(writer, addr);
191}
192
193fn done(writer: anytype) !void {
194 log.debug(">>> done", .{});
195 try writer.writeByte(macho.REBASE_OPCODE_DONE);
196}
197
198pub fn write(rebase: Rebase, writer: anytype) !void {
199 if (rebase.size() == 0) return;
200 try writer.writeAll(rebase.buffer.items);
201}
202
203test "rebase - no entries" {
204 const gpa = testing.allocator;
205
206 var rebase = Rebase{};
207 defer rebase.deinit(gpa);
208
209 try rebase.finalize(gpa);
210 try testing.expectEqual(@as(u64, 0), rebase.size());
211}
212
213test "rebase - single entry" {
214 const gpa = testing.allocator;
215
216 var rebase = Rebase{};
217 defer rebase.deinit(gpa);
218 try rebase.entries.append(gpa, .{
219 .segment_id = 1,
220 .offset = 0x10,
221 });
222 try rebase.finalize(gpa);
223 try testing.expectEqualSlices(u8, &[_]u8{
224 macho.REBASE_OPCODE_SET_TYPE_IMM | macho.REBASE_TYPE_POINTER,
225 macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 1,
226 0x10,
227 macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | 1,
228 macho.REBASE_OPCODE_DONE,
229 }, rebase.buffer.items);
230}
231
232test "rebase - emitTimes - IMM" {
233 const gpa = testing.allocator;
234
235 var rebase = Rebase{};
236 defer rebase.deinit(gpa);
237
238 var i: u64 = 0;
239 while (i < 10) : (i += 1) {
240 try rebase.entries.append(gpa, .{
241 .segment_id = 1,
242 .offset = i * @sizeOf(u64),
243 });
244 }
245
246 try rebase.finalize(gpa);
247
248 try testing.expectEqualSlices(u8, &[_]u8{
249 macho.REBASE_OPCODE_SET_TYPE_IMM | macho.REBASE_TYPE_POINTER,
250 macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 1,
251 0x0,
252 macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | 10,
253 macho.REBASE_OPCODE_DONE,
254 }, rebase.buffer.items);
255}
256
257test "rebase - emitTimes - ULEB" {
258 const gpa = testing.allocator;
259
260 var rebase = Rebase{};
261 defer rebase.deinit(gpa);
262
263 var i: u64 = 0;
264 while (i < 100) : (i += 1) {
265 try rebase.entries.append(gpa, .{
266 .segment_id = 1,
267 .offset = i * @sizeOf(u64),
268 });
269 }
270
271 try rebase.finalize(gpa);
272
273 try testing.expectEqualSlices(u8, &[_]u8{
274 macho.REBASE_OPCODE_SET_TYPE_IMM | macho.REBASE_TYPE_POINTER,
275 macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 1,
276 0x0,
277 macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES,
278 0x64,
279 macho.REBASE_OPCODE_DONE,
280 }, rebase.buffer.items);
281}
282
283test "rebase - emitTimes followed by addAddr followed by emitTimes" {
284 const gpa = testing.allocator;
285
286 var rebase = Rebase{};
287 defer rebase.deinit(gpa);
288
289 var offset: u64 = 0;
290 var i: u64 = 0;
291 while (i < 15) : (i += 1) {
292 try rebase.entries.append(gpa, .{
293 .segment_id = 1,
294 .offset = offset,
295 });
296 offset += @sizeOf(u64);
297 }
298
299 offset += @sizeOf(u64);
300
301 try rebase.entries.append(gpa, .{
302 .segment_id = 1,
303 .offset = offset,
304 });
305
306 try rebase.finalize(gpa);
307
308 try testing.expectEqualSlices(u8, &[_]u8{
309 macho.REBASE_OPCODE_SET_TYPE_IMM | macho.REBASE_TYPE_POINTER,
310 macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 1,
311 0x0,
312 macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | 15,
313 macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED | 1,
314 macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | 1,
315 macho.REBASE_OPCODE_DONE,
316 }, rebase.buffer.items);
317}
318
319test "rebase - emitTimesSkip" {
320 const gpa = testing.allocator;
321
322 var rebase = Rebase{};
323 defer rebase.deinit(gpa);
324
325 var offset: u64 = 0;
326 var i: u64 = 0;
327 while (i < 15) : (i += 1) {
328 try rebase.entries.append(gpa, .{
329 .segment_id = 1,
330 .offset = offset,
331 });
332 offset += 2 * @sizeOf(u64);
333 }
334
335 try rebase.finalize(gpa);
336
337 try testing.expectEqualSlices(u8, &[_]u8{
338 macho.REBASE_OPCODE_SET_TYPE_IMM | macho.REBASE_TYPE_POINTER,
339 macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 1,
340 0x0,
341 macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB,
342 0xf,
343 0x8,
344 macho.REBASE_OPCODE_DONE,
345 }, rebase.buffer.items);
346}
347
348test "rebase - complex" {
349 const gpa = testing.allocator;
350
351 var rebase = Rebase{};
352 defer rebase.deinit(gpa);
353
354 try rebase.entries.append(gpa, .{
355 .segment_id = 1,
356 .offset = 0,
357 });
358 try rebase.entries.append(gpa, .{
359 .segment_id = 1,
360 .offset = 0x10,
361 });
362 try rebase.entries.append(gpa, .{
363 .segment_id = 1,
364 .offset = 0x40,
365 });
366 try rebase.entries.append(gpa, .{
367 .segment_id = 1,
368 .offset = 0x48,
369 });
370 try rebase.entries.append(gpa, .{
371 .segment_id = 1,
372 .offset = 0x50,
373 });
374 try rebase.entries.append(gpa, .{
375 .segment_id = 1,
376 .offset = 0x58,
377 });
378 try rebase.entries.append(gpa, .{
379 .segment_id = 1,
380 .offset = 0x70,
381 });
382 try rebase.finalize(gpa);
383
384 try testing.expectEqualSlices(u8, &[_]u8{
385 macho.REBASE_OPCODE_SET_TYPE_IMM | macho.REBASE_TYPE_POINTER,
386 macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 1,
387 0x0,
388 macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB,
389 0x2,
390 0x8,
391 macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED | 4,
392 macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | 4,
393 macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED | 2,
394 macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | 1,
395 macho.REBASE_OPCODE_DONE,
396 }, rebase.buffer.items);
397}
398
399test "rebase - complex 2" {
400 const gpa = testing.allocator;
401
402 var rebase = Rebase{};
403 defer rebase.deinit(gpa);
404
405 try rebase.entries.append(gpa, .{
406 .segment_id = 1,
407 .offset = 0,
408 });
409 try rebase.entries.append(gpa, .{
410 .segment_id = 1,
411 .offset = 0x10,
412 });
413 try rebase.entries.append(gpa, .{
414 .segment_id = 1,
415 .offset = 0x28,
416 });
417 try rebase.entries.append(gpa, .{
418 .segment_id = 1,
419 .offset = 0x48,
420 });
421 try rebase.entries.append(gpa, .{
422 .segment_id = 1,
423 .offset = 0x78,
424 });
425 try rebase.entries.append(gpa, .{
426 .segment_id = 1,
427 .offset = 0xb8,
428 });
429 try rebase.entries.append(gpa, .{
430 .segment_id = 2,
431 .offset = 0x0,
432 });
433 try rebase.entries.append(gpa, .{
434 .segment_id = 2,
435 .offset = 0x8,
436 });
437 try rebase.entries.append(gpa, .{
438 .segment_id = 2,
439 .offset = 0x10,
440 });
441 try rebase.entries.append(gpa, .{
442 .segment_id = 2,
443 .offset = 0x18,
444 });
445 try rebase.entries.append(gpa, .{
446 .segment_id = 3,
447 .offset = 0x0,
448 });
449 try rebase.entries.append(gpa, .{
450 .segment_id = 3,
451 .offset = 0x20,
452 });
453 try rebase.entries.append(gpa, .{
454 .segment_id = 3,
455 .offset = 0x40,
456 });
457 try rebase.entries.append(gpa, .{
458 .segment_id = 3,
459 .offset = 0x60,
460 });
461 try rebase.entries.append(gpa, .{
462 .segment_id = 3,
463 .offset = 0x68,
464 });
465 try rebase.finalize(gpa);
466
467 try testing.expectEqualSlices(u8, &[_]u8{
468 macho.REBASE_OPCODE_SET_TYPE_IMM | macho.REBASE_TYPE_POINTER,
469 macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 1,
470 0x0,
471 macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB,
472 0x2,
473 0x8,
474 macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED | 1,
475 macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB,
476 0x2,
477 0x18,
478 macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED | 2,
479 macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB,
480 0x2,
481 0x38,
482 macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 2,
483 0x0,
484 macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | 4,
485 macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 3,
486 0x0,
487 macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB,
488 0x3,
489 0x18,
490 macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | 2,
491 macho.REBASE_OPCODE_DONE,
492 }, rebase.buffer.items);
493}
494
495test "rebase - composite" {
496 const gpa = testing.allocator;
497
498 var rebase = Rebase{};
499 defer rebase.deinit(gpa);
500
501 try rebase.entries.append(gpa, .{
502 .segment_id = 1,
503 .offset = 0x8,
504 });
505 try rebase.entries.append(gpa, .{
506 .segment_id = 1,
507 .offset = 0x38,
508 });
509 try rebase.entries.append(gpa, .{
510 .segment_id = 1,
511 .offset = 0xa0,
512 });
513 try rebase.entries.append(gpa, .{
514 .segment_id = 1,
515 .offset = 0xa8,
516 });
517 try rebase.entries.append(gpa, .{
518 .segment_id = 1,
519 .offset = 0xb0,
520 });
521 try rebase.entries.append(gpa, .{
522 .segment_id = 1,
523 .offset = 0xc0,
524 });
525 try rebase.entries.append(gpa, .{
526 .segment_id = 1,
527 .offset = 0xc8,
528 });
529 try rebase.entries.append(gpa, .{
530 .segment_id = 1,
531 .offset = 0xd0,
532 });
533 try rebase.entries.append(gpa, .{
534 .segment_id = 1,
535 .offset = 0xd8,
536 });
537 try rebase.entries.append(gpa, .{
538 .segment_id = 1,
539 .offset = 0xe0,
540 });
541 try rebase.entries.append(gpa, .{
542 .segment_id = 1,
543 .offset = 0xe8,
544 });
545 try rebase.entries.append(gpa, .{
546 .segment_id = 1,
547 .offset = 0xf0,
548 });
549 try rebase.entries.append(gpa, .{
550 .segment_id = 1,
551 .offset = 0xf8,
552 });
553 try rebase.entries.append(gpa, .{
554 .segment_id = 1,
555 .offset = 0x108,
556 });
557 try rebase.finalize(gpa);
558
559 try testing.expectEqualSlices(u8, &[_]u8{
560 macho.REBASE_OPCODE_SET_TYPE_IMM | macho.REBASE_TYPE_POINTER,
561 macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 1,
562 0x8,
563 macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB,
564 0x2,
565 0x28,
566 macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED | 7,
567 macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | 3,
568 macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED | 1,
569 macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | 8,
570 macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED | 1,
571 macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | 1,
572 macho.REBASE_OPCODE_DONE,
573 }, rebase.buffer.items);
574}
src/link/MachO/dyld_info/bind.zig created+740
......@@ -0,0 +1,740 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const leb = std.leb;
4const log = std.log.scoped(.dyld_info);
5const macho = std.macho;
6const testing = std.testing;
7
8const Allocator = std.mem.Allocator;
9
10pub fn Bind(comptime Ctx: type, comptime Target: type) type {
11 return struct {
12 entries: std.ArrayListUnmanaged(Entry) = .{},
13 buffer: std.ArrayListUnmanaged(u8) = .{},
14
15 const Self = @This();
16
17 const Entry = struct {
18 target: Target,
19 offset: u64,
20 segment_id: u8,
21 addend: i64,
22
23 pub fn lessThan(ctx: Ctx, entry: Entry, other: Entry) bool {
24 if (entry.segment_id == other.segment_id) {
25 if (entry.target.eql(other.target)) {
26 return entry.offset < other.offset;
27 }
28 const entry_name = ctx.getSymbolName(entry.target);
29 const other_name = ctx.getSymbolName(other.target);
30 return std.mem.lessThan(u8, entry_name, other_name);
31 }
32 return entry.segment_id < other.segment_id;
33 }
34 };
35
36 pub fn deinit(self: *Self, gpa: Allocator) void {
37 self.entries.deinit(gpa);
38 self.buffer.deinit(gpa);
39 }
40
41 pub fn size(self: Self) u64 {
42 return @intCast(u64, self.buffer.items.len);
43 }
44
45 pub fn finalize(self: *Self, gpa: Allocator, ctx: Ctx) !void {
46 if (self.entries.items.len == 0) return;
47
48 const writer = self.buffer.writer(gpa);
49
50 std.sort.sort(Entry, self.entries.items, ctx, Entry.lessThan);
51
52 var start: usize = 0;
53 var seg_id: ?u8 = null;
54 for (self.entries.items) |entry, i| {
55 if (seg_id != null and seg_id.? == entry.segment_id) continue;
56 try finalizeSegment(self.entries.items[start..i], ctx, writer);
57 seg_id = entry.segment_id;
58 start = i;
59 }
60
61 try finalizeSegment(self.entries.items[start..], ctx, writer);
62 try done(writer);
63 }
64
65 fn finalizeSegment(entries: []const Entry, ctx: Ctx, writer: anytype) !void {
66 if (entries.len == 0) return;
67
68 const seg_id = entries[0].segment_id;
69 try setSegmentOffset(seg_id, 0, writer);
70
71 var offset: u64 = 0;
72 var addend: i64 = 0;
73 var count: usize = 0;
74 var skip: u64 = 0;
75 var target: ?Target = null;
76
77 var state: enum {
78 start,
79 bind_single,
80 bind_times_skip,
81 } = .start;
82
83 var i: usize = 0;
84 while (i < entries.len) : (i += 1) {
85 const current = entries[i];
86 if (target == null or !target.?.eql(current.target)) {
87 switch (state) {
88 .start => {},
89 .bind_single => try doBind(writer),
90 .bind_times_skip => try doBindTimesSkip(count, skip, writer),
91 }
92 state = .start;
93 target = current.target;
94
95 const sym = ctx.getSymbol(current.target);
96 const name = ctx.getSymbolName(current.target);
97 const flags: u8 = if (sym.weakRef()) macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT else 0;
98 const ordinal = @divTrunc(@bitCast(i16, sym.n_desc), macho.N_SYMBOL_RESOLVER);
99
100 try setSymbol(name, flags, writer);
101 try setTypePointer(writer);
102 try setDylibOrdinal(ordinal, writer);
103
104 if (current.addend != addend) {
105 addend = current.addend;
106 try setAddend(addend, writer);
107 }
108 }
109
110 log.debug("{x}, {d}, {x}, {?x}, {s}", .{ offset, count, skip, addend, @tagName(state) });
111 log.debug(" => {x}", .{current.offset});
112 switch (state) {
113 .start => {
114 if (current.offset < offset) {
115 try addAddr(@bitCast(u64, @intCast(i64, current.offset) - @intCast(i64, offset)), writer);
116 offset = offset - (offset - current.offset);
117 } else if (current.offset > offset) {
118 const delta = current.offset - offset;
119 try addAddr(delta, writer);
120 offset += delta;
121 }
122 state = .bind_single;
123 offset += @sizeOf(u64);
124 count = 1;
125 },
126 .bind_single => {
127 if (current.offset == offset) {
128 try doBind(writer);
129 state = .start;
130 } else if (current.offset > offset) {
131 const delta = current.offset - offset;
132 state = .bind_times_skip;
133 skip = @intCast(u64, delta);
134 offset += skip;
135 } else unreachable;
136 i -= 1;
137 },
138 .bind_times_skip => {
139 if (current.offset < offset) {
140 count -= 1;
141 if (count == 1) {
142 try doBindAddAddr(skip, writer);
143 } else {
144 try doBindTimesSkip(count, skip, writer);
145 }
146 state = .start;
147 offset = offset - (@sizeOf(u64) + skip);
148 i -= 2;
149 } else if (current.offset == offset) {
150 count += 1;
151 offset += @sizeOf(u64) + skip;
152 } else {
153 try doBindTimesSkip(count, skip, writer);
154 state = .start;
155 i -= 1;
156 }
157 },
158 }
159 }
160
161 switch (state) {
162 .start => unreachable,
163 .bind_single => try doBind(writer),
164 .bind_times_skip => try doBindTimesSkip(count, skip, writer),
165 }
166 }
167
168 pub fn write(self: Self, writer: anytype) !void {
169 if (self.size() == 0) return;
170 try writer.writeAll(self.buffer.items);
171 }
172 };
173}
174
175pub fn LazyBind(comptime Ctx: type, comptime Target: type) type {
176 return struct {
177 entries: std.ArrayListUnmanaged(Entry) = .{},
178 buffer: std.ArrayListUnmanaged(u8) = .{},
179 offsets: std.ArrayListUnmanaged(u32) = .{},
180
181 const Self = @This();
182
183 const Entry = struct {
184 target: Target,
185 offset: u64,
186 segment_id: u8,
187 addend: i64,
188 };
189
190 pub fn deinit(self: *Self, gpa: Allocator) void {
191 self.entries.deinit(gpa);
192 self.buffer.deinit(gpa);
193 self.offsets.deinit(gpa);
194 }
195
196 pub fn size(self: Self) u64 {
197 return @intCast(u64, self.buffer.items.len);
198 }
199
200 pub fn finalize(self: *Self, gpa: Allocator, ctx: Ctx) !void {
201 if (self.entries.items.len == 0) return;
202
203 try self.offsets.ensureTotalCapacityPrecise(gpa, self.entries.items.len);
204
205 var cwriter = std.io.countingWriter(self.buffer.writer(gpa));
206 const writer = cwriter.writer();
207
208 var addend: i64 = 0;
209
210 for (self.entries.items) |entry| {
211 self.offsets.appendAssumeCapacity(@intCast(u32, cwriter.bytes_written));
212
213 const sym = ctx.getSymbol(entry.target);
214 const name = ctx.getSymbolName(entry.target);
215 const flags: u8 = if (sym.weakRef()) macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT else 0;
216 const ordinal = @divTrunc(@bitCast(i16, sym.n_desc), macho.N_SYMBOL_RESOLVER);
217
218 try setSegmentOffset(entry.segment_id, entry.offset, writer);
219 try setSymbol(name, flags, writer);
220 try setDylibOrdinal(ordinal, writer);
221
222 if (entry.addend != addend) {
223 try setAddend(entry.addend, writer);
224 addend = entry.addend;
225 }
226
227 try doBind(writer);
228 try done(writer);
229 }
230 }
231
232 pub fn write(self: Self, writer: anytype) !void {
233 if (self.size() == 0) return;
234 try writer.writeAll(self.buffer.items);
235 }
236 };
237}
238
239fn setSegmentOffset(segment_id: u8, offset: u64, writer: anytype) !void {
240 log.debug(">>> set segment: {d} and offset: {x}", .{ segment_id, offset });
241 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, segment_id));
242 try std.leb.writeULEB128(writer, offset);
243}
244
245fn setSymbol(name: []const u8, flags: u8, writer: anytype) !void {
246 log.debug(">>> set symbol: {s} with flags: {x}", .{ name, flags });
247 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | @truncate(u4, flags));
248 try writer.writeAll(name);
249 try writer.writeByte(0);
250}
251
252fn setTypePointer(writer: anytype) !void {
253 log.debug(">>> set type: {d}", .{macho.BIND_TYPE_POINTER});
254 try writer.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.BIND_TYPE_POINTER));
255}
256
257fn setDylibOrdinal(ordinal: i16, writer: anytype) !void {
258 if (ordinal <= 0) {
259 switch (ordinal) {
260 macho.BIND_SPECIAL_DYLIB_SELF,
261 macho.BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE,
262 macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP,
263 => {},
264 else => unreachable, // Invalid dylib special binding
265 }
266 log.debug(">>> set dylib special: {d}", .{ordinal});
267 const cast = @bitCast(u16, ordinal);
268 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, cast));
269 } else {
270 const cast = @bitCast(u16, ordinal);
271 log.debug(">>> set dylib ordinal: {d}", .{ordinal});
272 if (cast <= 0xf) {
273 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, cast));
274 } else {
275 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
276 try std.leb.writeULEB128(writer, cast);
277 }
278 }
279}
280
281fn setAddend(addend: i64, writer: anytype) !void {
282 log.debug(">>> set addend: {x}", .{addend});
283 try writer.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB);
284 try std.leb.writeILEB128(writer, addend);
285}
286
287fn doBind(writer: anytype) !void {
288 log.debug(">>> bind", .{});
289 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
290}
291
292fn doBindAddAddr(addr: u64, writer: anytype) !void {
293 log.debug(">>> bind with add: {x}", .{addr});
294 if (std.mem.isAlignedGeneric(u64, addr, @sizeOf(u64))) {
295 const imm = @divExact(addr, @sizeOf(u64));
296 if (imm <= 0xf) {
297 try writer.writeByte(
298 macho.BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED | @truncate(u4, imm),
299 );
300 return;
301 }
302 }
303 try writer.writeByte(macho.BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB);
304 try std.leb.writeULEB128(writer, addr);
305}
306
307fn doBindTimesSkip(count: usize, skip: u64, writer: anytype) !void {
308 log.debug(">>> bind with count: {d} and skip: {x}", .{ count, skip });
309 try writer.writeByte(macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB);
310 try std.leb.writeULEB128(writer, count);
311 try std.leb.writeULEB128(writer, skip);
312}
313
314fn addAddr(addr: u64, writer: anytype) !void {
315 log.debug(">>> add: {x}", .{addr});
316 try writer.writeByte(macho.BIND_OPCODE_ADD_ADDR_ULEB);
317 try std.leb.writeULEB128(writer, addr);
318}
319
320fn done(writer: anytype) !void {
321 log.debug(">>> done", .{});
322 try writer.writeByte(macho.BIND_OPCODE_DONE);
323}
324
325const TestContext = struct {
326 symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
327 strtab: std.ArrayListUnmanaged(u8) = .{},
328
329 const Target = struct {
330 index: u32,
331
332 fn eql(this: Target, other: Target) bool {
333 return this.index == other.index;
334 }
335 };
336
337 fn deinit(ctx: *TestContext, gpa: Allocator) void {
338 ctx.symbols.deinit(gpa);
339 ctx.strtab.deinit(gpa);
340 }
341
342 fn addSymbol(ctx: *TestContext, gpa: Allocator, name: []const u8, ordinal: i16, flags: u16) !void {
343 const n_strx = try ctx.addString(gpa, name);
344 var n_desc = @bitCast(u16, ordinal * macho.N_SYMBOL_RESOLVER);
345 n_desc |= flags;
346 try ctx.symbols.append(gpa, .{
347 .n_value = 0,
348 .n_strx = n_strx,
349 .n_desc = n_desc,
350 .n_type = macho.N_EXT,
351 .n_sect = 0,
352 });
353 }
354
355 fn addString(ctx: *TestContext, gpa: Allocator, name: []const u8) !u32 {
356 const n_strx = @intCast(u32, ctx.strtab.items.len);
357 try ctx.strtab.appendSlice(gpa, name);
358 try ctx.strtab.append(gpa, 0);
359 return n_strx;
360 }
361
362 fn getSymbol(ctx: TestContext, target: Target) macho.nlist_64 {
363 return ctx.symbols.items[target.index];
364 }
365
366 fn getSymbolName(ctx: TestContext, target: Target) []const u8 {
367 const sym = ctx.getSymbol(target);
368 assert(sym.n_strx < ctx.strtab.items.len);
369 return std.mem.sliceTo(@ptrCast([*:0]const u8, ctx.strtab.items.ptr + sym.n_strx), 0);
370 }
371};
372
373fn generateTestContext() !TestContext {
374 const gpa = testing.allocator;
375 var ctx = TestContext{};
376 try ctx.addSymbol(gpa, "_import_1", 1, 0);
377 try ctx.addSymbol(gpa, "_import_2", 1, 0);
378 try ctx.addSymbol(gpa, "_import_3", 1, 0);
379 try ctx.addSymbol(gpa, "_import_4", 2, 0);
380 try ctx.addSymbol(gpa, "_import_5_weak", 2, macho.N_WEAK_REF);
381 try ctx.addSymbol(gpa, "_import_6", 2, 0);
382 return ctx;
383}
384
385test "bind - no entries" {
386 const gpa = testing.allocator;
387
388 var test_context = try generateTestContext();
389 defer test_context.deinit(gpa);
390
391 var bind = Bind(TestContext, TestContext.Target){};
392 defer bind.deinit(gpa);
393
394 try bind.finalize(gpa, test_context);
395 try testing.expectEqual(@as(u64, 0), bind.size());
396}
397
398test "bind - single entry" {
399 const gpa = testing.allocator;
400
401 var test_context = try generateTestContext();
402 defer test_context.deinit(gpa);
403
404 var bind = Bind(TestContext, TestContext.Target){};
405 defer bind.deinit(gpa);
406
407 try bind.entries.append(gpa, .{
408 .offset = 0x10,
409 .segment_id = 1,
410 .target = TestContext.Target{ .index = 0 },
411 .addend = 0,
412 });
413 try bind.finalize(gpa, test_context);
414 try testing.expectEqualSlices(u8, &[_]u8{
415 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 1,
416 0x0,
417 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | 0,
418 0x5f,
419 0x69,
420 0x6d,
421 0x70,
422 0x6f,
423 0x72,
424 0x74,
425 0x5f,
426 0x31,
427 0x0,
428 macho.BIND_OPCODE_SET_TYPE_IMM | 1,
429 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | 1,
430 macho.BIND_OPCODE_ADD_ADDR_ULEB,
431 0x10,
432 macho.BIND_OPCODE_DO_BIND,
433 macho.BIND_OPCODE_DONE,
434 }, bind.buffer.items);
435}
436
437test "bind - multiple occurrences within the same segment" {
438 const gpa = testing.allocator;
439
440 var test_context = try generateTestContext();
441 defer test_context.deinit(gpa);
442
443 var bind = Bind(TestContext, TestContext.Target){};
444 defer bind.deinit(gpa);
445
446 try bind.entries.append(gpa, .{
447 .offset = 0x10,
448 .segment_id = 1,
449 .target = TestContext.Target{ .index = 0 },
450 .addend = 0,
451 });
452 try bind.entries.append(gpa, .{
453 .offset = 0x18,
454 .segment_id = 1,
455 .target = TestContext.Target{ .index = 0 },
456 .addend = 0,
457 });
458 try bind.entries.append(gpa, .{
459 .offset = 0x20,
460 .segment_id = 1,
461 .target = TestContext.Target{ .index = 0 },
462 .addend = 0,
463 });
464 try bind.entries.append(gpa, .{
465 .offset = 0x28,
466 .segment_id = 1,
467 .target = TestContext.Target{ .index = 0 },
468 .addend = 0,
469 });
470
471 try bind.finalize(gpa, test_context);
472 try testing.expectEqualSlices(u8, &[_]u8{
473 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 1,
474 0x0,
475 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | 0,
476 0x5f,
477 0x69,
478 0x6d,
479 0x70,
480 0x6f,
481 0x72,
482 0x74,
483 0x5f,
484 0x31,
485 0x0,
486 macho.BIND_OPCODE_SET_TYPE_IMM | 1,
487 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | 1,
488 macho.BIND_OPCODE_ADD_ADDR_ULEB,
489 0x10,
490 macho.BIND_OPCODE_DO_BIND,
491 macho.BIND_OPCODE_DO_BIND,
492 macho.BIND_OPCODE_DO_BIND,
493 macho.BIND_OPCODE_DO_BIND,
494 macho.BIND_OPCODE_DONE,
495 }, bind.buffer.items);
496}
497
498test "bind - multiple occurrences with skip and addend" {
499 const gpa = testing.allocator;
500
501 var test_context = try generateTestContext();
502 defer test_context.deinit(gpa);
503
504 var bind = Bind(TestContext, TestContext.Target){};
505 defer bind.deinit(gpa);
506
507 try bind.entries.append(gpa, .{
508 .offset = 0x0,
509 .segment_id = 1,
510 .target = TestContext.Target{ .index = 0 },
511 .addend = 0x10,
512 });
513 try bind.entries.append(gpa, .{
514 .offset = 0x10,
515 .segment_id = 1,
516 .target = TestContext.Target{ .index = 0 },
517 .addend = 0x10,
518 });
519 try bind.entries.append(gpa, .{
520 .offset = 0x20,
521 .segment_id = 1,
522 .target = TestContext.Target{ .index = 0 },
523 .addend = 0x10,
524 });
525 try bind.entries.append(gpa, .{
526 .offset = 0x30,
527 .segment_id = 1,
528 .target = TestContext.Target{ .index = 0 },
529 .addend = 0x10,
530 });
531
532 try bind.finalize(gpa, test_context);
533 try testing.expectEqualSlices(u8, &[_]u8{
534 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 1,
535 0x0,
536 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | 0,
537 0x5f,
538 0x69,
539 0x6d,
540 0x70,
541 0x6f,
542 0x72,
543 0x74,
544 0x5f,
545 0x31,
546 0x0,
547 macho.BIND_OPCODE_SET_TYPE_IMM | 1,
548 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | 1,
549 macho.BIND_OPCODE_SET_ADDEND_SLEB,
550 0x10,
551 macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB,
552 0x4,
553 0x8,
554 macho.BIND_OPCODE_DONE,
555 }, bind.buffer.items);
556}
557
558test "bind - complex" {
559 const gpa = testing.allocator;
560
561 var test_context = try generateTestContext();
562 defer test_context.deinit(gpa);
563
564 var bind = Bind(TestContext, TestContext.Target){};
565 defer bind.deinit(gpa);
566
567 try bind.entries.append(gpa, .{
568 .offset = 0x58,
569 .segment_id = 1,
570 .target = TestContext.Target{ .index = 0 },
571 .addend = 0,
572 });
573 try bind.entries.append(gpa, .{
574 .offset = 0x100,
575 .segment_id = 1,
576 .target = TestContext.Target{ .index = 1 },
577 .addend = 0x10,
578 });
579 try bind.entries.append(gpa, .{
580 .offset = 0x110,
581 .segment_id = 1,
582 .target = TestContext.Target{ .index = 1 },
583 .addend = 0x10,
584 });
585 try bind.entries.append(gpa, .{
586 .offset = 0x130,
587 .segment_id = 1,
588 .target = TestContext.Target{ .index = 1 },
589 .addend = 0x10,
590 });
591 try bind.entries.append(gpa, .{
592 .offset = 0x140,
593 .segment_id = 1,
594 .target = TestContext.Target{ .index = 1 },
595 .addend = 0x10,
596 });
597 try bind.entries.append(gpa, .{
598 .offset = 0x148,
599 .segment_id = 1,
600 .target = TestContext.Target{ .index = 2 },
601 .addend = 0,
602 });
603
604 try bind.finalize(gpa, test_context);
605 try testing.expectEqualSlices(u8, &[_]u8{
606 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 1,
607 0x0,
608 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | 0,
609 0x5f,
610 0x69,
611 0x6d,
612 0x70,
613 0x6f,
614 0x72,
615 0x74,
616 0x5f,
617 0x31,
618 0x0,
619 macho.BIND_OPCODE_SET_TYPE_IMM | 1,
620 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | 1,
621 macho.BIND_OPCODE_ADD_ADDR_ULEB,
622 0x58,
623 macho.BIND_OPCODE_DO_BIND,
624 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | 0,
625 0x5f,
626 0x69,
627 0x6d,
628 0x70,
629 0x6f,
630 0x72,
631 0x74,
632 0x5f,
633 0x32,
634 0x0,
635 macho.BIND_OPCODE_SET_TYPE_IMM | 1,
636 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | 1,
637 macho.BIND_OPCODE_SET_ADDEND_SLEB,
638 0x10,
639 macho.BIND_OPCODE_ADD_ADDR_ULEB,
640 0xa0,
641 0x1,
642 macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB,
643 0x2,
644 0x8,
645 macho.BIND_OPCODE_ADD_ADDR_ULEB,
646 0x10,
647 macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB,
648 0x2,
649 0x8,
650 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | 0,
651 0x5f,
652 0x69,
653 0x6d,
654 0x70,
655 0x6f,
656 0x72,
657 0x74,
658 0x5f,
659 0x33,
660 0x0,
661 macho.BIND_OPCODE_SET_TYPE_IMM | 1,
662 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | 1,
663 macho.BIND_OPCODE_SET_ADDEND_SLEB,
664 0x0,
665 macho.BIND_OPCODE_ADD_ADDR_ULEB,
666 0xf8,
667 0xff,
668 0xff,
669 0xff,
670 0xff,
671 0xff,
672 0xff,
673 0xff,
674 0xff,
675 0x1,
676 macho.BIND_OPCODE_DO_BIND,
677 macho.BIND_OPCODE_DONE,
678 }, bind.buffer.items);
679}
680
681test "lazy bind" {
682 const gpa = testing.allocator;
683
684 var test_context = try generateTestContext();
685 defer test_context.deinit(gpa);
686
687 var bind = LazyBind(TestContext, TestContext.Target){};
688 defer bind.deinit(gpa);
689
690 try bind.entries.append(gpa, .{
691 .offset = 0x10,
692 .segment_id = 1,
693 .target = TestContext.Target{ .index = 0 },
694 .addend = 0,
695 });
696 try bind.entries.append(gpa, .{
697 .offset = 0x20,
698 .segment_id = 2,
699 .target = TestContext.Target{ .index = 1 },
700 .addend = 0x10,
701 });
702
703 try bind.finalize(gpa, test_context);
704 try testing.expectEqualSlices(u8, &[_]u8{
705 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 1,
706 0x10,
707 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | 0,
708 0x5f,
709 0x69,
710 0x6d,
711 0x70,
712 0x6f,
713 0x72,
714 0x74,
715 0x5f,
716 0x31,
717 0x0,
718 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | 1,
719 macho.BIND_OPCODE_DO_BIND,
720 macho.BIND_OPCODE_DONE,
721 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | 2,
722 0x20,
723 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | 0,
724 0x5f,
725 0x69,
726 0x6d,
727 0x70,
728 0x6f,
729 0x72,
730 0x74,
731 0x5f,
732 0x32,
733 0x0,
734 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | 1,
735 macho.BIND_OPCODE_SET_ADDEND_SLEB,
736 0x10,
737 macho.BIND_OPCODE_DO_BIND,
738 macho.BIND_OPCODE_DONE,
739 }, bind.buffer.items);
740}
src/link/MachO/zld.zig+84-155
......@@ -9,7 +9,6 @@ const math = std.math;
99const mem = std.mem;
1010
1111const aarch64 = @import("../../arch/aarch64/bits.zig");
12const bind = @import("bind.zig");
1312const dead_strip = @import("dead_strip.zig");
1413const fat = @import("fat.zig");
1514const link = @import("../../link.zig");
......@@ -32,6 +31,10 @@ const Object = @import("Object.zig");
3231const StringTable = @import("../strtab.zig").StringTable;
3332const Trie = @import("Trie.zig");
3433
34const Bind = @import("dyld_info/bind.zig").Bind(*const Zld, SymbolWithLoc);
35const LazyBind = @import("dyld_info/bind.zig").LazyBind(*const Zld, SymbolWithLoc);
36const Rebase = @import("dyld_info/Rebase.zig");
37
3538pub const Zld = struct {
3639 gpa: Allocator,
3740 file: fs.File,
......@@ -1778,14 +1781,14 @@ pub const Zld = struct {
17781781 fn collectRebaseDataFromContainer(
17791782 self: *Zld,
17801783 sect_id: u8,
1781 pointers: *std.ArrayList(bind.Pointer),
1784 rebase: *Rebase,
17821785 container: anytype,
17831786 ) !void {
17841787 const slice = self.sections.slice();
17851788 const segment_index = slice.items(.segment_index)[sect_id];
17861789 const seg = self.getSegment(sect_id);
17871790
1788 try pointers.ensureUnusedCapacity(container.items.len);
1791 try rebase.entries.ensureUnusedCapacity(self.gpa, container.items.len);
17891792
17901793 for (container.items) |entry| {
17911794 const target_sym = entry.getTargetSymbol(self);
......@@ -1796,19 +1799,19 @@ pub const Zld = struct {
17961799
17971800 log.debug(" | rebase at {x}", .{base_offset});
17981801
1799 pointers.appendAssumeCapacity(.{
1802 rebase.entries.appendAssumeCapacity(.{
18001803 .offset = base_offset,
18011804 .segment_id = segment_index,
18021805 });
18031806 }
18041807 }
18051808
1806 fn collectRebaseData(self: *Zld, pointers: *std.ArrayList(bind.Pointer)) !void {
1809 fn collectRebaseData(self: *Zld, rebase: *Rebase, reverse_lookups: [][]u32) !void {
18071810 log.debug("collecting rebase data", .{});
18081811
18091812 // First, unpack GOT entries
18101813 if (self.getSectionByName("__DATA_CONST", "__got")) |sect_id| {
1811 try self.collectRebaseDataFromContainer(sect_id, pointers, self.got_entries);
1814 try self.collectRebaseDataFromContainer(sect_id, rebase, self.got_entries);
18121815 }
18131816
18141817 const slice = self.sections.slice();
......@@ -1820,7 +1823,7 @@ pub const Zld = struct {
18201823 const seg = self.getSegment(sect_id);
18211824 var atom_index = slice.items(.first_atom_index)[sect_id];
18221825
1823 try pointers.ensureUnusedCapacity(self.stubs.items.len);
1826 try rebase.entries.ensureUnusedCapacity(self.gpa, self.stubs.items.len);
18241827
18251828 while (true) {
18261829 const atom = self.getAtom(atom_index);
......@@ -1829,7 +1832,7 @@ pub const Zld = struct {
18291832
18301833 log.debug(" | rebase at {x}", .{base_offset});
18311834
1832 pointers.appendAssumeCapacity(.{
1835 rebase.entries.appendAssumeCapacity(.{
18331836 .offset = base_offset,
18341837 .segment_id = segment_index,
18351838 });
......@@ -1896,13 +1899,16 @@ pub const Zld = struct {
18961899 },
18971900 else => unreachable,
18981901 }
1902 const target = Atom.parseRelocTarget(self, atom_index, rel, reverse_lookups[atom.getFile().?]);
1903 const target_sym = self.getSymbol(target);
1904 if (target_sym.undf()) continue;
18991905
19001906 const base_offset = @intCast(i32, sym.n_value - segment.vmaddr);
19011907 const rel_offset = rel.r_address - base_rel_offset;
19021908 const offset = @intCast(u64, base_offset + rel_offset);
19031909 log.debug(" | rebase at {x}", .{offset});
19041910
1905 try pointers.append(.{
1911 try rebase.entries.append(self.gpa, .{
19061912 .offset = offset,
19071913 .segment_id = segment_index,
19081914 });
......@@ -1914,19 +1920,21 @@ pub const Zld = struct {
19141920 } else break;
19151921 }
19161922 }
1923
1924 try rebase.finalize(self.gpa);
19171925 }
19181926
19191927 fn collectBindDataFromContainer(
19201928 self: *Zld,
19211929 sect_id: u8,
1922 pointers: *std.ArrayList(bind.Pointer),
1930 bind: *Bind,
19231931 container: anytype,
19241932 ) !void {
19251933 const slice = self.sections.slice();
19261934 const segment_index = slice.items(.segment_index)[sect_id];
19271935 const seg = self.getSegment(sect_id);
19281936
1929 try pointers.ensureUnusedCapacity(container.items.len);
1937 try bind.entries.ensureUnusedCapacity(self.gpa, container.items.len);
19301938
19311939 for (container.items) |entry| {
19321940 const bind_sym_name = entry.getTargetSymbolName(self);
......@@ -1937,7 +1945,6 @@ pub const Zld = struct {
19371945 const base_offset = sym.n_value - seg.vmaddr;
19381946
19391947 const dylib_ordinal = @divTrunc(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER);
1940 var flags: u4 = 0;
19411948 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
19421949 base_offset,
19431950 bind_sym_name,
......@@ -1945,29 +1952,27 @@ pub const Zld = struct {
19451952 });
19461953 if (bind_sym.weakRef()) {
19471954 log.debug(" | marking as weak ref ", .{});
1948 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
19491955 }
1950 pointers.appendAssumeCapacity(.{
1956 bind.entries.appendAssumeCapacity(.{
1957 .target = entry.target,
19511958 .offset = base_offset,
19521959 .segment_id = segment_index,
1953 .dylib_ordinal = dylib_ordinal,
1954 .name = bind_sym_name,
1955 .bind_flags = flags,
1960 .addend = 0,
19561961 });
19571962 }
19581963 }
19591964
1960 fn collectBindData(self: *Zld, pointers: *std.ArrayList(bind.Pointer), reverse_lookups: [][]u32) !void {
1965 fn collectBindData(self: *Zld, bind: *Bind, reverse_lookups: [][]u32) !void {
19611966 log.debug("collecting bind data", .{});
19621967
19631968 // First, unpack GOT section
19641969 if (self.getSectionByName("__DATA_CONST", "__got")) |sect_id| {
1965 try self.collectBindDataFromContainer(sect_id, pointers, self.got_entries);
1970 try self.collectBindDataFromContainer(sect_id, bind, self.got_entries);
19661971 }
19671972
19681973 // Next, unpack TLV pointers section
19691974 if (self.getSectionByName("__DATA", "__thread_ptrs")) |sect_id| {
1970 try self.collectBindDataFromContainer(sect_id, pointers, self.tlv_ptr_entries);
1975 try self.collectBindDataFromContainer(sect_id, bind, self.tlv_ptr_entries);
19711976 }
19721977
19731978 // Finally, unpack the rest.
......@@ -2033,27 +2038,27 @@ pub const Zld = struct {
20332038 const bind_sym = self.getSymbol(global);
20342039 if (!bind_sym.undf()) continue;
20352040
2036 const base_offset = @intCast(i32, sym.n_value - segment.vmaddr);
2037 const rel_offset = rel.r_address - base_rel_offset;
2041 const base_offset = sym.n_value - segment.vmaddr;
2042 const rel_offset = @intCast(u32, rel.r_address - base_rel_offset);
20382043 const offset = @intCast(u64, base_offset + rel_offset);
2044 const code = Atom.getAtomCode(self, atom_index);
2045 const addend = mem.readIntLittle(i64, code[rel_offset..][0..8]);
20392046
20402047 const dylib_ordinal = @divTrunc(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER);
2041 var flags: u4 = 0;
20422048 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
20432049 base_offset,
20442050 bind_sym_name,
20452051 dylib_ordinal,
20462052 });
2053 log.debug(" | with addend {x}", .{addend});
20472054 if (bind_sym.weakRef()) {
20482055 log.debug(" | marking as weak ref ", .{});
2049 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
20502056 }
2051 try pointers.append(.{
2057 try bind.entries.append(self.gpa, .{
2058 .target = global,
20522059 .offset = offset,
20532060 .segment_id = segment_index,
2054 .dylib_ordinal = dylib_ordinal,
2055 .name = bind_sym_name,
2056 .bind_flags = flags,
2061 .addend = addend,
20572062 });
20582063 }
20592064 }
......@@ -2062,9 +2067,11 @@ pub const Zld = struct {
20622067 } else break;
20632068 }
20642069 }
2070
2071 try bind.finalize(self.gpa, self);
20652072 }
20662073
2067 fn collectLazyBindData(self: *Zld, pointers: *std.ArrayList(bind.Pointer)) !void {
2074 fn collectLazyBindData(self: *Zld, lazy_bind: *LazyBind) !void {
20682075 const sect_id = self.getSectionByName("__DATA", "__la_symbol_ptr") orelse return;
20692076
20702077 log.debug("collecting lazy bind data", .{});
......@@ -2075,7 +2082,7 @@ pub const Zld = struct {
20752082 var atom_index = slice.items(.first_atom_index)[sect_id];
20762083
20772084 // TODO: we actually don't need to store lazy pointer atoms as they are synthetically generated by the linker
2078 try pointers.ensureUnusedCapacity(self.stubs.items.len);
2085 try lazy_bind.entries.ensureUnusedCapacity(self.gpa, self.stubs.items.len);
20792086
20802087 var count: u32 = 0;
20812088 while (true) : (count += 1) {
......@@ -2090,7 +2097,6 @@ pub const Zld = struct {
20902097 const bind_sym = stub_entry.getTargetSymbol(self);
20912098 const bind_sym_name = stub_entry.getTargetSymbolName(self);
20922099 const dylib_ordinal = @divTrunc(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER);
2093 var flags: u4 = 0;
20942100 log.debug(" | lazy bind at {x}, import('{s}') in dylib({d})", .{
20952101 base_offset,
20962102 bind_sym_name,
......@@ -2098,20 +2104,20 @@ pub const Zld = struct {
20982104 });
20992105 if (bind_sym.weakRef()) {
21002106 log.debug(" | marking as weak ref ", .{});
2101 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
21022107 }
2103 pointers.appendAssumeCapacity(.{
2108 lazy_bind.entries.appendAssumeCapacity(.{
2109 .target = stub_entry.target,
21042110 .offset = base_offset,
21052111 .segment_id = segment_index,
2106 .dylib_ordinal = dylib_ordinal,
2107 .name = bind_sym_name,
2108 .bind_flags = flags,
2112 .addend = 0,
21092113 });
21102114
21112115 if (atom.next_index) |next_index| {
21122116 atom_index = next_index;
21132117 } else break;
21142118 }
2119
2120 try lazy_bind.finalize(self.gpa, self);
21152121 }
21162122
21172123 fn collectExportData(self: *Zld, trie: *Trie) !void {
......@@ -2161,17 +2167,17 @@ pub const Zld = struct {
21612167 fn writeDyldInfoData(self: *Zld, reverse_lookups: [][]u32) !void {
21622168 const gpa = self.gpa;
21632169
2164 var rebase_pointers = std.ArrayList(bind.Pointer).init(gpa);
2165 defer rebase_pointers.deinit();
2166 try self.collectRebaseData(&rebase_pointers);
2170 var rebase = Rebase{};
2171 defer rebase.deinit(gpa);
2172 try self.collectRebaseData(&rebase, reverse_lookups);
21672173
2168 var bind_pointers = std.ArrayList(bind.Pointer).init(gpa);
2169 defer bind_pointers.deinit();
2170 try self.collectBindData(&bind_pointers, reverse_lookups);
2174 var bind = Bind{};
2175 defer bind.deinit(gpa);
2176 try self.collectBindData(&bind, reverse_lookups);
21712177
2172 var lazy_bind_pointers = std.ArrayList(bind.Pointer).init(gpa);
2173 defer lazy_bind_pointers.deinit();
2174 try self.collectLazyBindData(&lazy_bind_pointers);
2178 var lazy_bind = LazyBind{};
2179 defer lazy_bind.deinit(gpa);
2180 try self.collectLazyBindData(&lazy_bind);
21752181
21762182 var trie = Trie{};
21772183 defer trie.deinit(gpa);
......@@ -2180,17 +2186,17 @@ pub const Zld = struct {
21802186 const link_seg = self.getLinkeditSegmentPtr();
21812187 assert(mem.isAlignedGeneric(u64, link_seg.fileoff, @alignOf(u64)));
21822188 const rebase_off = link_seg.fileoff;
2183 const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items);
2189 const rebase_size = rebase.size();
21842190 const rebase_size_aligned = mem.alignForwardGeneric(u64, rebase_size, @alignOf(u64));
21852191 log.debug("writing rebase info from 0x{x} to 0x{x}", .{ rebase_off, rebase_off + rebase_size_aligned });
21862192
21872193 const bind_off = rebase_off + rebase_size_aligned;
2188 const bind_size = try bind.bindInfoSize(bind_pointers.items);
2194 const bind_size = bind.size();
21892195 const bind_size_aligned = mem.alignForwardGeneric(u64, bind_size, @alignOf(u64));
21902196 log.debug("writing bind info from 0x{x} to 0x{x}", .{ bind_off, bind_off + bind_size_aligned });
21912197
21922198 const lazy_bind_off = bind_off + bind_size_aligned;
2193 const lazy_bind_size = try bind.lazyBindInfoSize(lazy_bind_pointers.items);
2199 const lazy_bind_size = lazy_bind.size();
21942200 const lazy_bind_size_aligned = mem.alignForwardGeneric(u64, lazy_bind_size, @alignOf(u64));
21952201 log.debug("writing lazy bind info from 0x{x} to 0x{x}", .{
21962202 lazy_bind_off,
......@@ -2214,13 +2220,13 @@ pub const Zld = struct {
22142220 var stream = std.io.fixedBufferStream(buffer);
22152221 const writer = stream.writer();
22162222
2217 try bind.writeRebaseInfo(rebase_pointers.items, writer);
2223 try rebase.write(writer);
22182224 try stream.seekTo(bind_off - rebase_off);
22192225
2220 try bind.writeBindInfo(bind_pointers.items, writer);
2226 try bind.write(writer);
22212227 try stream.seekTo(lazy_bind_off - rebase_off);
22222228
2223 try bind.writeLazyBindInfo(lazy_bind_pointers.items, writer);
2229 try lazy_bind.write(writer);
22242230 try stream.seekTo(export_off - rebase_off);
22252231
22262232 _ = try trie.write(writer);
......@@ -2231,10 +2237,7 @@ pub const Zld = struct {
22312237 });
22322238
22332239 try self.file.pwriteAll(buffer, rebase_off);
2234
2235 const offset = math.cast(usize, lazy_bind_off - rebase_off) orelse return error.Overflow;
2236 const size = math.cast(usize, lazy_bind_size) orelse return error.Overflow;
2237 try self.populateLazyBindOffsetsInStubHelper(buffer[offset..][0..size]);
2240 try self.populateLazyBindOffsetsInStubHelper(lazy_bind);
22382241
22392242 self.dyld_info_cmd.rebase_off = @intCast(u32, rebase_off);
22402243 self.dyld_info_cmd.rebase_size = @intCast(u32, rebase_size_aligned);
......@@ -2246,116 +2249,37 @@ pub const Zld = struct {
22462249 self.dyld_info_cmd.export_size = @intCast(u32, export_size_aligned);
22472250 }
22482251
2249 fn populateLazyBindOffsetsInStubHelper(self: *Zld, buffer: []const u8) !void {
2250 const gpa = self.gpa;
2251
2252 const stub_helper_section_index = self.getSectionByName("__TEXT", "__stub_helper") orelse return;
2253 const la_symbol_ptr_section_index = self.getSectionByName("__DATA", "__la_symbol_ptr") orelse return;
2252 fn populateLazyBindOffsetsInStubHelper(self: *Zld, lazy_bind: LazyBind) !void {
2253 if (lazy_bind.size() == 0) return;
22542254
2255 if (self.stub_helper_preamble_sym_index == null) return;
2255 const stub_helper_section_index = self.getSectionByName("__TEXT", "__stub_helper").?;
2256 assert(self.stub_helper_preamble_sym_index != null);
22562257
22572258 const section = self.sections.get(stub_helper_section_index);
2258 const last_atom_index = section.last_atom_index;
2259
2260 var table = std.AutoHashMap(i64, AtomIndex).init(gpa);
2261 defer table.deinit();
2262
2263 {
2264 var stub_atom_index = last_atom_index;
2265 var laptr_atom_index = self.sections.items(.last_atom_index)[la_symbol_ptr_section_index];
2266
2267 const base_addr = blk: {
2268 const segment_index = self.getSegmentByName("__DATA").?;
2269 const seg = self.segments.items[segment_index];
2270 break :blk seg.vmaddr;
2271 };
2272
2273 while (true) {
2274 const stub_atom = self.getAtom(stub_atom_index);
2275 const laptr_atom = self.getAtom(laptr_atom_index);
2276 const laptr_off = blk: {
2277 const sym = self.getSymbolPtr(laptr_atom.getSymbolWithLoc());
2278 break :blk @intCast(i64, sym.n_value - base_addr);
2279 };
2280
2281 try table.putNoClobber(laptr_off, stub_atom_index);
2282
2283 if (laptr_atom.prev_index) |prev_index| {
2284 laptr_atom_index = prev_index;
2285 stub_atom_index = stub_atom.prev_index.?;
2286 } else break;
2287 }
2288 }
2289
2290 var stream = std.io.fixedBufferStream(buffer);
2291 var reader = stream.reader();
2292 var offsets = std.ArrayList(struct { sym_offset: i64, offset: u32 }).init(gpa);
2293 try offsets.append(.{ .sym_offset = undefined, .offset = 0 });
2294 defer offsets.deinit();
2295 var valid_block = false;
2296
2297 while (true) {
2298 const inst = reader.readByte() catch |err| switch (err) {
2299 error.EndOfStream => break,
2300 };
2301 const opcode: u8 = inst & macho.BIND_OPCODE_MASK;
2302
2303 switch (opcode) {
2304 macho.BIND_OPCODE_DO_BIND => {
2305 valid_block = true;
2306 },
2307 macho.BIND_OPCODE_DONE => {
2308 if (valid_block) {
2309 const offset = try stream.getPos();
2310 try offsets.append(.{ .sym_offset = undefined, .offset = @intCast(u32, offset) });
2311 }
2312 valid_block = false;
2313 },
2314 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
2315 var next = try reader.readByte();
2316 while (next != @as(u8, 0)) {
2317 next = try reader.readByte();
2318 }
2319 },
2320 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
2321 var inserted = offsets.pop();
2322 inserted.sym_offset = try std.leb.readILEB128(i64, reader);
2323 try offsets.append(inserted);
2324 },
2325 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
2326 _ = try std.leb.readULEB128(u64, reader);
2327 },
2328 macho.BIND_OPCODE_SET_ADDEND_SLEB => {
2329 _ = try std.leb.readILEB128(i64, reader);
2330 },
2331 else => {},
2332 }
2333 }
2334
2335 const header = self.sections.items(.header)[stub_helper_section_index];
23362259 const stub_offset: u4 = switch (self.options.target.cpu.arch) {
23372260 .x86_64 => 1,
23382261 .aarch64 => 2 * @sizeOf(u32),
23392262 else => unreachable,
23402263 };
2341 var buf: [@sizeOf(u32)]u8 = undefined;
2342 _ = offsets.pop();
2264 const header = section.header;
2265 var atom_index = section.first_atom_index;
2266 atom_index = self.getAtom(atom_index).next_index.?; // skip preamble
23432267
2344 while (offsets.popOrNull()) |bind_offset| {
2345 const atom_index = table.get(bind_offset.sym_offset).?;
2268 var index: usize = 0;
2269 while (true) {
23462270 const atom = self.getAtom(atom_index);
2347 const sym = self.getSymbol(atom.getSymbolWithLoc());
2271 const atom_sym = self.getSymbol(atom.getSymbolWithLoc());
2272 const file_offset = header.offset + atom_sym.n_value - header.addr + stub_offset;
2273 const bind_offset = lazy_bind.offsets.items[index];
23482274
2349 const file_offset = header.offset + sym.n_value - header.addr + stub_offset;
2350 mem.writeIntLittle(u32, &buf, bind_offset.offset);
2275 log.debug("writing lazy bind offset 0x{x} in stub helper at 0x{x}", .{ bind_offset, file_offset });
23512276
2352 log.debug("writing lazy bind offset in stub helper of 0x{x} for symbol {s} at offset 0x{x}", .{
2353 bind_offset.offset,
2354 self.getSymbolName(atom.getSymbolWithLoc()),
2355 file_offset,
2356 });
2277 try self.file.pwriteAll(mem.asBytes(&bind_offset), file_offset);
23572278
2358 try self.file.pwriteAll(&buf, file_offset);
2279 if (atom.next_index) |next_index| {
2280 atom_index = next_index;
2281 index += 1;
2282 } else break;
23592283 }
23602284 }
23612285
......@@ -3018,12 +2942,17 @@ pub const Zld = struct {
30182942 }
30192943
30202944 /// Returns symbol described by `sym_with_loc` descriptor.
3021 pub fn getSymbol(self: *Zld, sym_with_loc: SymbolWithLoc) macho.nlist_64 {
3022 return self.getSymbolPtr(sym_with_loc).*;
2945 pub fn getSymbol(self: *const Zld, sym_with_loc: SymbolWithLoc) macho.nlist_64 {
2946 if (sym_with_loc.getFile()) |file| {
2947 const object = &self.objects.items[file];
2948 return object.symtab[sym_with_loc.sym_index];
2949 } else {
2950 return self.locals.items[sym_with_loc.sym_index];
2951 }
30232952 }
30242953
30252954 /// Returns name of the symbol described by `sym_with_loc` descriptor.
3026 pub fn getSymbolName(self: *Zld, sym_with_loc: SymbolWithLoc) []const u8 {
2955 pub fn getSymbolName(self: *const Zld, sym_with_loc: SymbolWithLoc) []const u8 {
30272956 if (sym_with_loc.getFile()) |file| {
30282957 const object = self.objects.items[file];
30292958 return object.getSymbolName(sym_with_loc.sym_index);
test/link/macho/uuid/build.zig+6-6
......@@ -12,18 +12,18 @@ pub fn build(b: *Builder) void {
1212 .os_tag = .macos,
1313 };
1414
15 testUuid(b, test_step, .ReleaseSafe, aarch64_macos, "46b333df88f5314686fc0cba3b939ca8");
16 testUuid(b, test_step, .ReleaseFast, aarch64_macos, "46b333df88f5314686fc0cba3b939ca8");
17 testUuid(b, test_step, .ReleaseSmall, aarch64_macos, "46b333df88f5314686fc0cba3b939ca8");
15 testUuid(b, test_step, .ReleaseSafe, aarch64_macos, "af0f4c21a07c30daba59213d80262e45");
16 testUuid(b, test_step, .ReleaseFast, aarch64_macos, "af0f4c21a07c30daba59213d80262e45");
17 testUuid(b, test_step, .ReleaseSmall, aarch64_macos, "af0f4c21a07c30daba59213d80262e45");
1818
1919 const x86_64_macos = std.zig.CrossTarget{
2020 .cpu_arch = .x86_64,
2121 .os_tag = .macos,
2222 };
2323
24 testUuid(b, test_step, .ReleaseSafe, x86_64_macos, "342ac765194131e1bad5692b9e0e54a4");
25 testUuid(b, test_step, .ReleaseFast, x86_64_macos, "342ac765194131e1bad5692b9e0e54a4");
26 testUuid(b, test_step, .ReleaseSmall, x86_64_macos, "f119310e24773ecf8ec42e09d0379dad");
24 testUuid(b, test_step, .ReleaseSafe, x86_64_macos, "63f47191c7153f5fba48bd63cb2f5f57");
25 testUuid(b, test_step, .ReleaseFast, x86_64_macos, "63f47191c7153f5fba48bd63cb2f5f57");
26 testUuid(b, test_step, .ReleaseSmall, x86_64_macos, "e7bba66220e33eda9e73ab293ccf93d2");
2727}
2828
2929fn testUuid(