authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-07-05 08:16:23+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-07-18 09:13:07+02:00
loge2bfd6fc691a92f9dc36597a8febb03293b0f5ad
treeb10018453568aff9e88e7b0ffb065ada040abfb9
parent101299e85625faf29b4afce07ad1e3522ea75421

macho: revamp how we compute dyld relocs


8 files changed, 628 insertions(+), 780 deletions(-)

src/link/MachO.zig+18-112
...@@ -82,11 +82,11 @@ stubs_helper: StubsHelperSection = .{},...@@ -82,11 +82,11 @@ stubs_helper: StubsHelperSection = .{},
82objc_stubs: ObjcStubsSection = .{},82objc_stubs: ObjcStubsSection = .{},
83la_symbol_ptr: LaSymbolPtrSection = .{},83la_symbol_ptr: LaSymbolPtrSection = .{},
84tlv_ptr: TlvPtrSection = .{},84tlv_ptr: TlvPtrSection = .{},
85rebase: RebaseSection = .{},85rebase: Rebase = .{},
86bind: BindSection = .{},86bind: Bind = .{},
87weak_bind: WeakBindSection = .{},87weak_bind: WeakBind = .{},
88lazy_bind: LazyBindSection = .{},88lazy_bind: LazyBind = .{},
89export_trie: ExportTrieSection = .{},89export_trie: ExportTrie = .{},
90unwind_info: UnwindInfo = .{},90unwind_info: UnwindInfo = .{},
9191
92/// Tracked loadable segments during incremental linking.92/// Tracked loadable segments during incremental linking.
...@@ -590,8 +590,6 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -590,8 +590,6 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
590 state_log.debug("{}", .{self.dumpState()});590 state_log.debug("{}", .{self.dumpState()});
591 }591 }
592592
593 try self.initDyldInfoSections();
594
595 // Beyond this point, everything has been allocated a virtual address and we can resolve593 // Beyond this point, everything has been allocated a virtual address and we can resolve
596 // the relocations, and commit objects to file.594 // the relocations, and commit objects to file.
597 if (self.getZigObject()) |zo| {595 if (self.getZigObject()) |zo| {
...@@ -2500,87 +2498,6 @@ fn allocateLinkeditSegment(self: *MachO) !void {...@@ -2500,87 +2498,6 @@ fn allocateLinkeditSegment(self: *MachO) !void {
2500 seg.fileoff = mem.alignForward(u64, fileoff, page_size);2498 seg.fileoff = mem.alignForward(u64, fileoff, page_size);
2501}2499}
25022500
2503fn initDyldInfoSections(self: *MachO) !void {
2504 const tracy = trace(@src());
2505 defer tracy.end();
2506
2507 const gpa = self.base.comp.gpa;
2508
2509 if (self.zig_got_sect_index != null) try self.zig_got.addDyldRelocs(self);
2510 if (self.got_sect_index != null) try self.got.addDyldRelocs(self);
2511 if (self.tlv_ptr_sect_index != null) try self.tlv_ptr.addDyldRelocs(self);
2512 if (self.la_symbol_ptr_sect_index != null) try self.la_symbol_ptr.addDyldRelocs(self);
2513 try self.initExportTrie();
2514
2515 var objects = try std.ArrayList(File.Index).initCapacity(gpa, self.objects.items.len + 1);
2516 defer objects.deinit();
2517 if (self.getZigObject()) |zo| objects.appendAssumeCapacity(zo.index);
2518 objects.appendSliceAssumeCapacity(self.objects.items);
2519
2520 var nrebases: usize = 0;
2521 var nbinds: usize = 0;
2522 var nweak_binds: usize = 0;
2523 for (objects.items) |index| {
2524 const ctx = switch (self.getFile(index).?) {
2525 .zig_object => |x| x.dynamic_relocs,
2526 .object => |x| x.dynamic_relocs,
2527 else => unreachable,
2528 };
2529 nrebases += ctx.rebase_relocs;
2530 nbinds += ctx.bind_relocs;
2531 nweak_binds += ctx.weak_bind_relocs;
2532 }
2533 if (self.getInternalObject()) |int| {
2534 nrebases += int.num_rebase_relocs;
2535 }
2536 try self.rebase.entries.ensureUnusedCapacity(gpa, nrebases);
2537 try self.bind.entries.ensureUnusedCapacity(gpa, nbinds);
2538 try self.weak_bind.entries.ensureUnusedCapacity(gpa, nweak_binds);
2539}
2540
2541fn initExportTrie(self: *MachO) !void {
2542 const tracy = trace(@src());
2543 defer tracy.end();
2544
2545 const gpa = self.base.comp.gpa;
2546 try self.export_trie.init(gpa);
2547
2548 const seg = self.getTextSegment();
2549 for (self.objects.items) |index| {
2550 for (self.getFile(index).?.getSymbols()) |sym_index| {
2551 const sym = self.getSymbol(sym_index);
2552 if (!sym.flags.@"export") continue;
2553 if (sym.getAtom(self)) |atom| if (!atom.flags.alive) continue;
2554 if (sym.getFile(self).?.getIndex() != index) continue;
2555 var flags: u64 = if (sym.flags.abs)
2556 macho.EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
2557 else if (sym.flags.tlv)
2558 macho.EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL
2559 else
2560 macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR;
2561 if (sym.flags.weak) {
2562 flags |= macho.EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION;
2563 self.weak_defines = true;
2564 self.binds_to_weak = true;
2565 }
2566 try self.export_trie.put(gpa, .{
2567 .name = sym.getName(self),
2568 .vmaddr_offset = sym.getAddress(.{ .stubs = false }, self) - seg.vmaddr,
2569 .export_flags = flags,
2570 });
2571 }
2572 }
2573
2574 if (self.mh_execute_header_index) |index| {
2575 const sym = self.getSymbol(index);
2576 try self.export_trie.put(gpa, .{
2577 .name = sym.getName(self),
2578 .vmaddr_offset = sym.getAddress(.{}, self) - seg.vmaddr,
2579 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
2580 });
2581 }
2582}
2583
2584fn writeAtoms(self: *MachO) !void {2501fn writeAtoms(self: *MachO) !void {
2585 const tracy = trace(@src());2502 const tracy = trace(@src());
2586 defer tracy.end();2503 defer tracy.end();
...@@ -2659,13 +2576,13 @@ fn writeUnwindInfo(self: *MachO) !void {...@@ -2659,13 +2576,13 @@ fn writeUnwindInfo(self: *MachO) !void {
2659fn finalizeDyldInfoSections(self: *MachO) !void {2576fn finalizeDyldInfoSections(self: *MachO) !void {
2660 const tracy = trace(@src());2577 const tracy = trace(@src());
2661 defer tracy.end();2578 defer tracy.end();
2662 const gpa = self.base.comp.gpa;2579 try self.rebase.updateSize(self);
26632580 try self.bind.updateSize(self);
2664 try self.rebase.finalize(gpa);2581 try self.weak_bind.updateSize(self);
2665 try self.bind.finalize(gpa, self);2582 if (self.la_symbol_ptr_sect_index) |_| {
2666 try self.weak_bind.finalize(gpa, self);2583 try self.lazy_bind.updateSize(self);
2667 try self.lazy_bind.finalize(gpa, self);2584 }
2668 try self.export_trie.finalize(gpa);2585 try self.export_trie.updateSize(self);
2669}2586}
26702587
2671fn writeSyntheticSections(self: *MachO) !void {2588fn writeSyntheticSections(self: *MachO) !void {
...@@ -2742,25 +2659,14 @@ fn writeDyldInfoSections(self: *MachO, off: u32) !u32 {...@@ -2742,25 +2659,14 @@ fn writeDyldInfoSections(self: *MachO, off: u32) !u32 {
2742 const gpa = self.base.comp.gpa;2659 const gpa = self.base.comp.gpa;
2743 const cmd = &self.dyld_info_cmd;2660 const cmd = &self.dyld_info_cmd;
2744 var needed_size: u32 = 0;2661 var needed_size: u32 = 0;
2745
2746 cmd.rebase_off = needed_size;
2747 cmd.rebase_size = mem.alignForward(u32, @intCast(self.rebase.size()), @alignOf(u64));
2748 needed_size += cmd.rebase_size;2662 needed_size += cmd.rebase_size;
2749
2750 cmd.bind_off = needed_size;2663 cmd.bind_off = needed_size;
2751 cmd.bind_size = mem.alignForward(u32, @intCast(self.bind.size()), @alignOf(u64));
2752 needed_size += cmd.bind_size;2664 needed_size += cmd.bind_size;
2753
2754 cmd.weak_bind_off = needed_size;2665 cmd.weak_bind_off = needed_size;
2755 cmd.weak_bind_size = mem.alignForward(u32, @intCast(self.weak_bind.size()), @alignOf(u64));
2756 needed_size += cmd.weak_bind_size;2666 needed_size += cmd.weak_bind_size;
2757
2758 cmd.lazy_bind_off = needed_size;2667 cmd.lazy_bind_off = needed_size;
2759 cmd.lazy_bind_size = mem.alignForward(u32, @intCast(self.lazy_bind.size()), @alignOf(u64));
2760 needed_size += cmd.lazy_bind_size;2668 needed_size += cmd.lazy_bind_size;
2761
2762 cmd.export_off = needed_size;2669 cmd.export_off = needed_size;
2763 cmd.export_size = mem.alignForward(u32, @intCast(self.export_trie.size), @alignOf(u64));
2764 needed_size += cmd.export_size;2670 needed_size += cmd.export_size;
27652671
2766 const buffer = try gpa.alloc(u8, needed_size);2672 const buffer = try gpa.alloc(u8, needed_size);
...@@ -2785,7 +2691,6 @@ fn writeDyldInfoSections(self: *MachO, off: u32) !u32 {...@@ -2785,7 +2691,6 @@ fn writeDyldInfoSections(self: *MachO, off: u32) !u32 {
2785 cmd.weak_bind_off += off;2691 cmd.weak_bind_off += off;
2786 cmd.lazy_bind_off += off;2692 cmd.lazy_bind_off += off;
2787 cmd.export_off += off;2693 cmd.export_off += off;
2788
2789 try self.base.file.?.pwriteAll(buffer, off);2694 try self.base.file.?.pwriteAll(buffer, off);
27902695
2791 return off + needed_size;2696 return off + needed_size;
...@@ -4831,6 +4736,7 @@ const mem = std.mem;...@@ -4831,6 +4736,7 @@ const mem = std.mem;
4831const meta = std.meta;4736const meta = std.meta;
48324737
4833const aarch64 = @import("../arch/aarch64/bits.zig");4738const aarch64 = @import("../arch/aarch64/bits.zig");
4739const bind = @import("MachO/dyld_info/bind.zig");
4834const calcUuid = @import("MachO/uuid.zig").calcUuid;4740const calcUuid = @import("MachO/uuid.zig").calcUuid;
4835const codegen = @import("../codegen.zig");4741const codegen = @import("../codegen.zig");
4836const dead_strip = @import("MachO/dead_strip.zig");4742const dead_strip = @import("MachO/dead_strip.zig");
...@@ -4851,13 +4757,13 @@ const Alignment = Atom.Alignment;...@@ -4851,13 +4757,13 @@ const Alignment = Atom.Alignment;
4851const Allocator = mem.Allocator;4757const Allocator = mem.Allocator;
4852const Archive = @import("MachO/Archive.zig");4758const Archive = @import("MachO/Archive.zig");
4853pub const Atom = @import("MachO/Atom.zig");4759pub const Atom = @import("MachO/Atom.zig");
4854const BindSection = synthetic.BindSection;4760const Bind = bind.Bind;
4855const Cache = std.Build.Cache;4761const Cache = std.Build.Cache;
4856const CodeSignature = @import("MachO/CodeSignature.zig");4762const CodeSignature = @import("MachO/CodeSignature.zig");
4857const Compilation = @import("../Compilation.zig");4763const Compilation = @import("../Compilation.zig");
4858pub const DebugSymbols = @import("MachO/DebugSymbols.zig");4764pub const DebugSymbols = @import("MachO/DebugSymbols.zig");
4859const Dylib = @import("MachO/Dylib.zig");4765const Dylib = @import("MachO/Dylib.zig");
4860const ExportTrieSection = synthetic.ExportTrieSection;4766const ExportTrie = @import("MachO/dyld_info/Trie.zig");
4861const File = @import("MachO/file.zig").File;4767const File = @import("MachO/file.zig").File;
4862const GotSection = synthetic.GotSection;4768const GotSection = synthetic.GotSection;
4863const Hash = std.hash.Wyhash;4769const Hash = std.hash.Wyhash;
...@@ -4865,7 +4771,7 @@ const Indsymtab = synthetic.Indsymtab;...@@ -4865,7 +4771,7 @@ const Indsymtab = synthetic.Indsymtab;
4865const InternalObject = @import("MachO/InternalObject.zig");4771const InternalObject = @import("MachO/InternalObject.zig");
4866const ObjcStubsSection = synthetic.ObjcStubsSection;4772const ObjcStubsSection = synthetic.ObjcStubsSection;
4867const Object = @import("MachO/Object.zig");4773const Object = @import("MachO/Object.zig");
4868const LazyBindSection = synthetic.LazyBindSection;4774const LazyBind = bind.LazyBind;
4869const LaSymbolPtrSection = synthetic.LaSymbolPtrSection;4775const LaSymbolPtrSection = synthetic.LaSymbolPtrSection;
4870const LibStub = tapi.LibStub;4776const LibStub = tapi.LibStub;
4871const Liveness = @import("../Liveness.zig");4777const Liveness = @import("../Liveness.zig");
...@@ -4875,7 +4781,7 @@ const Zcu = @import("../Zcu.zig");...@@ -4875,7 +4781,7 @@ const Zcu = @import("../Zcu.zig");
4875/// Deprecated.4781/// Deprecated.
4876const Module = Zcu;4782const Module = Zcu;
4877const InternPool = @import("../InternPool.zig");4783const InternPool = @import("../InternPool.zig");
4878const RebaseSection = synthetic.RebaseSection;4784const Rebase = @import("MachO/dyld_info/Rebase.zig");
4879pub const Relocation = @import("MachO/Relocation.zig");4785pub const Relocation = @import("MachO/Relocation.zig");
4880const StringTable = @import("StringTable.zig");4786const StringTable = @import("StringTable.zig");
4881const StubsSection = synthetic.StubsSection;4787const StubsSection = synthetic.StubsSection;
...@@ -4885,6 +4791,6 @@ const Thunk = thunks.Thunk;...@@ -4885,6 +4791,6 @@ const Thunk = thunks.Thunk;
4885const TlvPtrSection = synthetic.TlvPtrSection;4791const TlvPtrSection = synthetic.TlvPtrSection;
4886const Value = @import("../Value.zig");4792const Value = @import("../Value.zig");
4887const UnwindInfo = @import("MachO/UnwindInfo.zig");4793const UnwindInfo = @import("MachO/UnwindInfo.zig");
4888const WeakBindSection = synthetic.WeakBindSection;4794const WeakBind = bind.WeakBind;
4889const ZigGotSection = synthetic.ZigGotSection;4795const ZigGotSection = synthetic.ZigGotSection;
4890const ZigObject = @import("MachO/ZigObject.zig");4796const ZigObject = @import("MachO/ZigObject.zig");
src/link/MachO/Atom.zig+1-36
...@@ -460,11 +460,6 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {...@@ -460,11 +460,6 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
460 defer tracy.end();460 defer tracy.end();
461 assert(self.flags.alive);461 assert(self.flags.alive);
462462
463 const dynrel_ctx = switch (self.getFile(macho_file)) {
464 .zig_object => |x| &x.dynamic_relocs,
465 .object => |x| &x.dynamic_relocs,
466 else => unreachable,
467 };
468 const relocs = self.getRelocs(macho_file);463 const relocs = self.getRelocs(macho_file);
469464
470 for (relocs) |rel| {465 for (relocs) |rel| {
...@@ -537,21 +532,15 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {...@@ -537,21 +532,15 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
537 continue;532 continue;
538 }533 }
539 if (symbol.flags.import) {534 if (symbol.flags.import) {
540 dynrel_ctx.bind_relocs += 1;
541 if (symbol.flags.weak) {535 if (symbol.flags.weak) {
542 dynrel_ctx.weak_bind_relocs += 1;
543 macho_file.binds_to_weak = true;536 macho_file.binds_to_weak = true;
544 }537 }
545 continue;538 continue;
546 }539 }
547 if (symbol.flags.@"export" and symbol.flags.weak) {540 if (symbol.flags.@"export" and symbol.flags.weak) {
548 dynrel_ctx.weak_bind_relocs += 1;
549 macho_file.binds_to_weak = true;541 macho_file.binds_to_weak = true;
550 } else if (symbol.flags.interposable) {
551 dynrel_ctx.bind_relocs += 1;
552 }542 }
553 }543 }
554 dynrel_ctx.rebase_relocs += 1;
555 }544 }
556 },545 },
557546
...@@ -651,8 +640,6 @@ fn resolveRelocInner(...@@ -651,8 +640,6 @@ fn resolveRelocInner(
651) ResolveError!void {640) ResolveError!void {
652 const cpu_arch = macho_file.getTarget().cpu.arch;641 const cpu_arch = macho_file.getTarget().cpu.arch;
653 const rel_offset = math.cast(usize, rel.offset - self.off) orelse return error.Overflow;642 const rel_offset = math.cast(usize, rel.offset - self.off) orelse return error.Overflow;
654 const seg_id = macho_file.sections.items(.segment_id)[self.out_n_sect];
655 const seg = macho_file.segments.items[seg_id];
656 const P = @as(i64, @intCast(self.getAddress(macho_file))) + @as(i64, @intCast(rel_offset));643 const P = @as(i64, @intCast(self.getAddress(macho_file))) + @as(i64, @intCast(rel_offset));
657 const A = rel.addend + rel.getRelocAddend(cpu_arch);644 const A = rel.addend + rel.getRelocAddend(cpu_arch);
658 const S: i64 = @intCast(rel.getTargetAddress(macho_file));645 const S: i64 = @intCast(rel.getTargetAddress(macho_file));
...@@ -706,29 +693,8 @@ fn resolveRelocInner(...@@ -706,29 +693,8 @@ fn resolveRelocInner(
706 try writer.writeInt(u64, @intCast(S - TLS), .little);693 try writer.writeInt(u64, @intCast(S - TLS), .little);
707 return;694 return;
708 }695 }
709 const entry = bind.Entry{696 if (sym.flags.import) return;
710 .target = rel.target,
711 .offset = @as(u64, @intCast(P)) - seg.vmaddr,
712 .segment_id = seg_id,
713 .addend = A,
714 };
715 if (sym.flags.import) {
716 macho_file.bind.entries.appendAssumeCapacity(entry);
717 if (sym.flags.weak) {
718 macho_file.weak_bind.entries.appendAssumeCapacity(entry);
719 }
720 return;
721 }
722 if (sym.flags.@"export" and sym.flags.weak) {
723 macho_file.weak_bind.entries.appendAssumeCapacity(entry);
724 } else if (sym.flags.interposable) {
725 macho_file.bind.entries.appendAssumeCapacity(entry);
726 }
727 }697 }
728 macho_file.rebase.entries.appendAssumeCapacity(.{
729 .offset = @as(u64, @intCast(P)) - seg.vmaddr,
730 .segment_id = seg_id,
731 });
732 try writer.writeInt(u64, @bitCast(S + A - SUB), .little);698 try writer.writeInt(u64, @bitCast(S + A - SUB), .little);
733 } else if (rel.meta.length == 2) {699 } else if (rel.meta.length == 2) {
734 try writer.writeInt(u32, @bitCast(@as(i32, @truncate(S + A - SUB))), .little);700 try writer.writeInt(u32, @bitCast(@as(i32, @truncate(S + A - SUB))), .little);
...@@ -1239,7 +1205,6 @@ pub const Alignment = @import("../../InternPool.zig").Alignment;...@@ -1239,7 +1205,6 @@ pub const Alignment = @import("../../InternPool.zig").Alignment;
12391205
1240const aarch64 = @import("../aarch64.zig");1206const aarch64 = @import("../aarch64.zig");
1241const assert = std.debug.assert;1207const assert = std.debug.assert;
1242const bind = @import("dyld_info/bind.zig");
1243const macho = std.macho;1208const macho = std.macho;
1244const math = std.math;1209const math = std.math;
1245const mem = std.mem;1210const mem = std.mem;
src/link/MachO/Object.zig-1
...@@ -30,7 +30,6 @@ data_in_code: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},...@@ -30,7 +30,6 @@ data_in_code: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
30alive: bool = true,30alive: bool = true,
31hidden: bool = false,31hidden: bool = false,
3232
33dynamic_relocs: MachO.DynamicRelocs = .{},
34output_symtab_ctx: MachO.SymtabCtx = .{},33output_symtab_ctx: MachO.SymtabCtx = .{},
35output_ar_state: Archive.ArState = .{},34output_ar_state: Archive.ArState = .{},
3635
src/link/MachO/ZigObject.zig-1
...@@ -48,7 +48,6 @@ relocs: RelocationTable = .{},...@@ -48,7 +48,6 @@ relocs: RelocationTable = .{},
4848
49dwarf: ?Dwarf = null,49dwarf: ?Dwarf = null,
5050
51dynamic_relocs: MachO.DynamicRelocs = .{},
52output_symtab_ctx: MachO.SymtabCtx = .{},51output_symtab_ctx: MachO.SymtabCtx = .{},
53output_ar_state: Archive.ArState = .{},52output_ar_state: Archive.ArState = .{},
5453
src/link/MachO/dyld_info/Rebase.zig+100-15
...@@ -1,14 +1,3 @@...@@ -1,14 +1,3 @@
1const Rebase = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const leb = std.leb;
6const log = std.log.scoped(.link_dyld_info);
7const macho = std.macho;
8const testing = std.testing;
9
10const Allocator = std.mem.Allocator;
11
12entries: std.ArrayListUnmanaged(Entry) = .{},1entries: std.ArrayListUnmanaged(Entry) = .{},
13buffer: std.ArrayListUnmanaged(u8) = .{},2buffer: std.ArrayListUnmanaged(u8) = .{},
143
...@@ -30,11 +19,94 @@ pub fn deinit(rebase: *Rebase, gpa: Allocator) void {...@@ -30,11 +19,94 @@ pub fn deinit(rebase: *Rebase, gpa: Allocator) void {
30 rebase.buffer.deinit(gpa);19 rebase.buffer.deinit(gpa);
31}20}
3221
33pub fn size(rebase: Rebase) u64 {22pub fn updateSize(rebase: *Rebase, macho_file: *MachO) !void {
34 return @as(u64, @intCast(rebase.buffer.items.len));23 const tracy = trace(@src());
24 defer tracy.end();
25
26 const gpa = macho_file.base.comp.gpa;
27
28 var objects = try std.ArrayList(File.Index).initCapacity(gpa, macho_file.objects.items.len + 1);
29 defer objects.deinit();
30 objects.appendSliceAssumeCapacity(macho_file.objects.items);
31 if (macho_file.getInternalObject()) |obj| objects.appendAssumeCapacity(obj.index);
32
33 for (objects.items) |index| {
34 const file = macho_file.getFile(index).?;
35 for (file.getAtoms()) |atom_index| {
36 const atom = macho_file.getAtom(atom_index) orelse continue;
37 if (!atom.flags.alive) continue;
38 if (atom.getInputSection(macho_file).isZerofill()) continue;
39 const atom_addr = atom.getAddress(macho_file);
40 const seg_id = macho_file.sections.items(.segment_id)[atom.out_n_sect];
41 const seg = macho_file.segments.items[seg_id];
42 for (atom.getRelocs(macho_file)) |rel| {
43 if (rel.type != .unsigned or rel.meta.length != 3) continue;
44 if (rel.tag == .@"extern") {
45 const sym = rel.getTargetSymbol(macho_file);
46 if (sym.isTlvInit(macho_file)) continue;
47 if (sym.flags.import) continue;
48 }
49 const rel_offset = rel.offset - atom.off;
50 try rebase.entries.append(gpa, .{
51 .offset = atom_addr + rel_offset - seg.vmaddr,
52 .segment_id = seg_id,
53 });
54 }
55 }
56 }
57
58 if (macho_file.got_sect_index) |sid| {
59 const seg_id = macho_file.sections.items(.segment_id)[sid];
60 const seg = macho_file.segments.items[seg_id];
61 for (macho_file.got.symbols.items, 0..) |ref, idx| {
62 const sym = macho_file.getSymbol(ref);
63 const addr = macho_file.got.getAddress(@intCast(idx), macho_file);
64 if (!sym.flags.import) {
65 try rebase.entries.append(gpa, .{
66 .offset = addr - seg.vmaddr,
67 .segment_id = seg_id,
68 });
69 }
70 }
71 }
72
73 if (macho_file.la_symbol_ptr_sect_index) |sid| {
74 const sect = macho_file.sections.items(.header)[sid];
75 const seg_id = macho_file.sections.items(.segment_id)[sid];
76 const seg = macho_file.segments.items[seg_id];
77 for (macho_file.stubs.symbols.items, 0..) |ref, idx| {
78 const sym = macho_file.getSymbol(ref);
79 const addr = sect.addr + idx * @sizeOf(u64);
80 const rebase_entry = Rebase.Entry{
81 .offset = addr - seg.vmaddr,
82 .segment_id = seg_id,
83 };
84 if ((sym.flags.import and !sym.flags.weak) or !sym.flags.import) {
85 try rebase.entries.append(gpa, rebase_entry);
86 }
87 }
88 }
89
90 if (macho_file.tlv_ptr_sect_index) |sid| {
91 const seg_id = macho_file.sections.items(.segment_id)[sid];
92 const seg = macho_file.segments.items[seg_id];
93 for (macho_file.tlv_ptr.symbols.items, 0..) |ref, idx| {
94 const sym = macho_file.getSymbol(ref);
95 const addr = macho_file.tlv_ptr.getAddress(@intCast(idx), macho_file);
96 if (!sym.flags.import) {
97 try rebase.entries.append(gpa, .{
98 .offset = addr - seg.vmaddr,
99 .segment_id = seg_id,
100 });
101 }
102 }
103 }
104
105 try rebase.finalize(gpa);
106 macho_file.dyld_info_cmd.rebase_size = mem.alignForward(u32, @intCast(rebase.buffer.items.len), @alignOf(u64));
35}107}
36108
37pub fn finalize(rebase: *Rebase, gpa: Allocator) !void {109fn finalize(rebase: *Rebase, gpa: Allocator) !void {
38 if (rebase.entries.items.len == 0) return;110 if (rebase.entries.items.len == 0) return;
39111
40 const writer = rebase.buffer.writer(gpa);112 const writer = rebase.buffer.writer(gpa);
...@@ -198,7 +270,6 @@ fn done(writer: anytype) !void {...@@ -198,7 +270,6 @@ fn done(writer: anytype) !void {
198}270}
199271
200pub fn write(rebase: Rebase, writer: anytype) !void {272pub fn write(rebase: Rebase, writer: anytype) !void {
201 if (rebase.size() == 0) return;
202 try writer.writeAll(rebase.buffer.items);273 try writer.writeAll(rebase.buffer.items);
203}274}
204275
...@@ -574,3 +645,17 @@ test "rebase - composite" {...@@ -574,3 +645,17 @@ test "rebase - composite" {
574 macho.REBASE_OPCODE_DONE,645 macho.REBASE_OPCODE_DONE,
575 }, rebase.buffer.items);646 }, rebase.buffer.items);
576}647}
648
649const std = @import("std");
650const assert = std.debug.assert;
651const leb = std.leb;
652const log = std.log.scoped(.link_dyld_info);
653const macho = std.macho;
654const mem = std.mem;
655const testing = std.testing;
656const trace = @import("../../../tracy.zig").trace;
657
658const Allocator = mem.Allocator;
659const File = @import("../file.zig").File;
660const MachO = @import("../../MachO.zig");
661const Rebase = @This();
src/link/MachO/dyld_info/Trie.zig+266-455
...@@ -28,463 +28,312 @@...@@ -28,463 +28,312 @@
28//! After the optional exported symbol information is a byte of how many edges (0-255) that28//! After the optional exported symbol information is a byte of how many edges (0-255) that
29//! this node has leaving it, followed by each edge. Each edge is a zero terminated UTF8 of29//! this node has leaving it, followed by each edge. Each edge is a zero terminated UTF8 of
30//! the addition chars in the symbol, followed by a uleb128 offset for the node that edge points to.30//! the addition chars in the symbol, followed by a uleb128 offset for the node that edge points to.
31const Trie = @This();
32
33const std = @import("std");
34const mem = std.mem;
35const leb = std.leb;
36const log = std.log.scoped(.macho);
37const macho = std.macho;
38const testing = std.testing;
39const assert = std.debug.assert;
40const Allocator = mem.Allocator;
41
42pub const Node = struct {
43 base: *Trie,
44
45 /// Terminal info associated with this node.
46 /// If this node is not a terminal node, info is null.
47 terminal_info: ?struct {
48 /// Export flags associated with this exported symbol.
49 export_flags: u64,
50 /// VM address offset wrt to the section this symbol is defined against.
51 vmaddr_offset: u64,
52 } = null,
53
54 /// Offset of this node in the trie output byte stream.
55 trie_offset: ?u64 = null,
56
57 /// List of all edges originating from this node.
58 edges: std.ArrayListUnmanaged(Edge) = .{},
59
60 node_dirty: bool = true,
61
62 /// Edge connecting to nodes in the trie.
63 pub const Edge = struct {
64 from: *Node,
65 to: *Node,
66 label: []u8,
67
68 fn deinit(self: *Edge, allocator: Allocator) void {
69 self.to.deinit(allocator);
70 allocator.destroy(self.to);
71 allocator.free(self.label);
72 self.from = undefined;
73 self.to = undefined;
74 self.label = undefined;
75 }
76 };
77
78 fn deinit(self: *Node, allocator: Allocator) void {
79 for (self.edges.items) |*edge| {
80 edge.deinit(allocator);
81 }
82 self.edges.deinit(allocator);
83 }
84
85 /// Inserts a new node starting from `self`.
86 fn put(self: *Node, allocator: Allocator, label: []const u8) !*Node {
87 // Check for match with edges from this node.
88 for (self.edges.items) |*edge| {
89 const match = mem.indexOfDiff(u8, edge.label, label) orelse return edge.to;
90 if (match == 0) continue;
91 if (match == edge.label.len) return edge.to.put(allocator, label[match..]);
92
93 // Found a match, need to splice up nodes.
94 // From: A -> B
95 // To: A -> C -> B
96 const mid = try allocator.create(Node);
97 mid.* = .{ .base = self.base };
98 const to_label = try allocator.dupe(u8, edge.label[match..]);
99 allocator.free(edge.label);
100 const to_node = edge.to;
101 edge.to = mid;
102 edge.label = try allocator.dupe(u8, label[0..match]);
103 self.base.node_count += 1;
104
105 try mid.edges.append(allocator, .{
106 .from = mid,
107 .to = to_node,
108 .label = to_label,
109 });
110
111 return if (match == label.len) mid else mid.put(allocator, label[match..]);
112 }
11331
114 // Add a new node.32/// The root node of the trie.
115 const node = try allocator.create(Node);33root: ?Node.Index = null,
116 node.* = .{ .base = self.base };34buffer: std.ArrayListUnmanaged(u8) = .{},
117 self.base.node_count += 1;35nodes: std.MultiArrayList(Node) = .{},
36edges: std.ArrayListUnmanaged(Edge) = .{},
11837
119 try self.edges.append(allocator, .{38/// Insert a symbol into the trie, updating the prefixes in the process.
120 .from = self,39/// This operation may change the layout of the trie by splicing edges in
121 .to = node,40/// certain circumstances.
122 .label = try allocator.dupe(u8, label),41fn put(self: *Trie, allocator: Allocator, symbol: ExportSymbol) !void {
123 });42 // const tracy = trace(@src());
43 // defer tracy.end();
44
45 const node_index = try self.putNode(self.root.?, allocator, symbol.name);
46 const slice = self.nodes.slice();
47 slice.items(.is_terminal)[node_index] = true;
48 slice.items(.vmaddr_offset)[node_index] = symbol.vmaddr_offset;
49 slice.items(.export_flags)[node_index] = symbol.export_flags;
50}
12451
125 return node;52/// Inserts a new node starting at `node_index`.
53fn putNode(self: *Trie, node_index: Node.Index, allocator: Allocator, label: []const u8) !Node.Index {
54 // Check for match with edges from this node.
55 for (self.nodes.items(.edges)[node_index].items) |edge_index| {
56 const edge = &self.edges.items[edge_index];
57 const match = mem.indexOfDiff(u8, edge.label, label) orelse return edge.node;
58 if (match == 0) continue;
59 if (match == edge.label.len) return self.putNode(edge.node, allocator, label[match..]);
60
61 // Found a match, need to splice up nodes.
62 // From: A -> B
63 // To: A -> C -> B
64 const mid_index = try self.addNode(allocator);
65 const to_label = edge.label[match..];
66 const to_node = edge.node;
67 edge.node = mid_index;
68 edge.label = label[0..match];
69
70 const new_edge_index = try self.addEdge(allocator);
71 const new_edge = &self.edges.items[new_edge_index];
72 new_edge.node = to_node;
73 new_edge.label = to_label;
74 try self.nodes.items(.edges)[mid_index].append(allocator, new_edge_index);
75
76 return if (match == label.len) mid_index else self.putNode(mid_index, allocator, label[match..]);
126 }77 }
12778
128 /// Recursively parses the node from the input byte stream.79 // Add a new node.
129 fn read(self: *Node, allocator: Allocator, reader: anytype) Trie.ReadError!usize {80 const new_node_index = try self.addNode(allocator);
130 self.node_dirty = true;81 const new_edge_index = try self.addEdge(allocator);
131 const trie_offset = try reader.context.getPos();82 const new_edge = &self.edges.items[new_edge_index];
132 self.trie_offset = trie_offset;83 new_edge.node = new_node_index;
13384 new_edge.label = label;
134 var nread: usize = 0;85 try self.nodes.items(.edges)[node_index].append(allocator, new_edge_index);
135
136 const node_size = try leb.readUleb128(u64, reader);
137 if (node_size > 0) {
138 const export_flags = try leb.readUleb128(u64, reader);
139 // TODO Parse special flags.
140 assert(export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and
141 export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);
142
143 const vmaddr_offset = try leb.readUleb128(u64, reader);
144
145 self.terminal_info = .{
146 .export_flags = export_flags,
147 .vmaddr_offset = vmaddr_offset,
148 };
149 }
150
151 const nedges = try reader.readByte();
152 self.base.node_count += nedges;
153
154 nread += (try reader.context.getPos()) - trie_offset;
15586
156 var i: usize = 0;87 return new_node_index;
157 while (i < nedges) : (i += 1) {88}
158 const edge_start_pos = try reader.context.getPos();
159
160 const label = blk: {
161 var label_buf = std.ArrayList(u8).init(allocator);
162 while (true) {
163 const next = try reader.readByte();
164 if (next == @as(u8, 0))
165 break;
166 try label_buf.append(next);
167 }
168 break :blk try label_buf.toOwnedSlice();
169 };
170
171 const seek_to = try leb.readUleb128(u64, reader);
172 const return_pos = try reader.context.getPos();
173
174 nread += return_pos - edge_start_pos;
175 try reader.context.seekTo(seek_to);
176
177 const node = try allocator.create(Node);
178 node.* = .{ .base = self.base };
17989
180 nread += try node.read(allocator, reader);90pub fn updateSize(self: *Trie, macho_file: *MachO) !void {
181 try self.edges.append(allocator, .{91 const tracy = trace(@src());
182 .from = self,92 defer tracy.end();
183 .to = node,93
184 .label = label,94 const gpa = macho_file.base.comp.gpa;
95
96 try self.init(gpa);
97 // TODO
98 // try self.nodes.ensureUnusedCapacity(gpa, macho_file.resolver.values.items.len * 2);
99 // try self.edges.ensureUnusedCapacity(gpa, macho_file.resolver.values.items.len * 2);
100
101 const seg = macho_file.getTextSegment();
102 for (macho_file.objects.items) |index| {
103 for (macho_file.getFile(index).?.getSymbols()) |ref| {
104 const sym = macho_file.getSymbol(ref);
105 if (!sym.flags.@"export") continue;
106 if (sym.getAtom(macho_file)) |atom| if (!atom.flags.alive) continue;
107 var flags: u64 = if (sym.flags.abs)
108 macho.EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
109 else if (sym.flags.tlv)
110 macho.EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL
111 else
112 macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR;
113 if (sym.flags.weak) {
114 flags |= macho.EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION;
115 macho_file.weak_defines = true;
116 macho_file.binds_to_weak = true;
117 }
118 try self.put(gpa, .{
119 .name = sym.getName(macho_file),
120 .vmaddr_offset = sym.getAddress(.{ .stubs = false }, macho_file) - seg.vmaddr,
121 .export_flags = flags,
185 });122 });
186 try reader.context.seekTo(return_pos);
187 }
188
189 return nread;
190 }
191
192 /// Writes this node to a byte stream.
193 /// The children of this node *are* not written to the byte stream
194 /// recursively. To write all nodes to a byte stream in sequence,
195 /// iterate over `Trie.ordered_nodes` and call this method on each node.
196 /// This is one of the requirements of the MachO.
197 /// Panics if `finalize` was not called before calling this method.
198 fn write(self: Node, writer: anytype) !void {
199 assert(!self.node_dirty);
200 if (self.terminal_info) |info| {
201 // Terminal node info: encode export flags and vmaddr offset of this symbol.
202 var info_buf: [@sizeOf(u64) * 2]u8 = undefined;
203 var info_stream = std.io.fixedBufferStream(&info_buf);
204 // TODO Implement for special flags.
205 assert(info.export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and
206 info.export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);
207 try leb.writeUleb128(info_stream.writer(), info.export_flags);
208 try leb.writeUleb128(info_stream.writer(), info.vmaddr_offset);
209
210 // Encode the size of the terminal node info.
211 var size_buf: [@sizeOf(u64)]u8 = undefined;
212 var size_stream = std.io.fixedBufferStream(&size_buf);
213 try leb.writeUleb128(size_stream.writer(), info_stream.pos);
214
215 // Now, write them to the output stream.
216 try writer.writeAll(size_buf[0..size_stream.pos]);
217 try writer.writeAll(info_buf[0..info_stream.pos]);
218 } else {
219 // Non-terminal node is delimited by 0 byte.
220 try writer.writeByte(0);
221 }
222 // Write number of edges (max legal number of edges is 256).
223 try writer.writeByte(@as(u8, @intCast(self.edges.items.len)));
224
225 for (self.edges.items) |edge| {
226 // Write edge label and offset to next node in trie.
227 try writer.writeAll(edge.label);
228 try writer.writeByte(0);
229 try leb.writeUleb128(writer, edge.to.trie_offset.?);
230 }123 }
231 }124 }
232125
233 const FinalizeResult = struct {126 try self.finalize(gpa);
234 /// Current size of this node in bytes.
235 node_size: u64,
236
237 /// True if the trie offset of this node in the output byte stream
238 /// would need updating; false otherwise.
239 updated: bool,
240 };
241127
242 /// Updates offset of this node in the output byte stream.128 macho_file.dyld_info_cmd.export_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64));
243 fn finalize(self: *Node, offset_in_trie: u64) !FinalizeResult {
244 var stream = std.io.countingWriter(std.io.null_writer);
245 const writer = stream.writer();
246
247 var node_size: u64 = 0;
248 if (self.terminal_info) |info| {
249 try leb.writeUleb128(writer, info.export_flags);
250 try leb.writeUleb128(writer, info.vmaddr_offset);
251 try leb.writeUleb128(writer, stream.bytes_written);
252 } else {
253 node_size += 1; // 0x0 for non-terminal nodes
254 }
255 node_size += 1; // 1 byte for edge count
256
257 for (self.edges.items) |edge| {
258 const next_node_offset = edge.to.trie_offset orelse 0;
259 node_size += edge.label.len + 1;
260 try leb.writeUleb128(writer, next_node_offset);
261 }
262
263 const trie_offset = self.trie_offset orelse 0;
264 const updated = offset_in_trie != trie_offset;
265 self.trie_offset = offset_in_trie;
266 self.node_dirty = false;
267 node_size += stream.bytes_written;
268
269 return FinalizeResult{ .node_size = node_size, .updated = updated };
270 }
271};
272
273/// The root node of the trie.
274root: ?*Node = null,
275
276/// If you want to access nodes ordered in DFS fashion,
277/// you should call `finalize` first since the nodes
278/// in this container are not guaranteed to not be stale
279/// if more insertions took place after the last `finalize`
280/// call.
281ordered_nodes: std.ArrayListUnmanaged(*Node) = .{},
282
283/// The size of the trie in bytes.
284/// This value may be outdated if there were additional
285/// insertions performed after `finalize` was called.
286/// Call `finalize` before accessing this value to ensure
287/// it is up-to-date.
288size: u64 = 0,
289
290/// Number of nodes currently in the trie.
291node_count: usize = 0,
292
293trie_dirty: bool = true,
294
295/// Export symbol that is to be placed in the trie.
296pub const ExportSymbol = struct {
297 /// Name of the symbol.
298 name: []const u8,
299
300 /// Offset of this symbol's virtual memory address from the beginning
301 /// of the __TEXT segment.
302 vmaddr_offset: u64,
303
304 /// Export flags of this exported symbol.
305 export_flags: u64,
306};
307
308/// Insert a symbol into the trie, updating the prefixes in the process.
309/// This operation may change the layout of the trie by splicing edges in
310/// certain circumstances.
311pub fn put(self: *Trie, allocator: Allocator, symbol: ExportSymbol) !void {
312 const node = try self.root.?.put(allocator, symbol.name);
313 node.terminal_info = .{
314 .vmaddr_offset = symbol.vmaddr_offset,
315 .export_flags = symbol.export_flags,
316 };
317 self.trie_dirty = true;
318}129}
319130
320/// Finalizes this trie for writing to a byte stream.131/// Finalizes this trie for writing to a byte stream.
321/// This step performs multiple passes through the trie ensuring132/// This step performs multiple passes through the trie ensuring
322/// there are no gaps after every `Node` is ULEB128 encoded.133/// there are no gaps after every `Node` is ULEB128 encoded.
323/// Call this method before trying to `write` the trie to a byte stream.134/// Call this method before trying to `write` the trie to a byte stream.
324pub fn finalize(self: *Trie, allocator: Allocator) !void {135fn finalize(self: *Trie, allocator: Allocator) !void {
325 if (!self.trie_dirty) return;136 const tracy = trace(@src());
137 defer tracy.end();
326138
327 self.ordered_nodes.shrinkRetainingCapacity(0);139 var ordered_nodes = std.ArrayList(Node.Index).init(allocator);
328 try self.ordered_nodes.ensureTotalCapacity(allocator, self.node_count);140 defer ordered_nodes.deinit();
141 try ordered_nodes.ensureTotalCapacityPrecise(self.nodes.items(.is_terminal).len);
329142
330 var fifo = std.fifo.LinearFifo(*Node, .Dynamic).init(allocator);143 var fifo = std.fifo.LinearFifo(Node.Index, .Dynamic).init(allocator);
331 defer fifo.deinit();144 defer fifo.deinit();
332145
333 try fifo.writeItem(self.root.?);146 try fifo.writeItem(self.root.?);
334147
335 while (fifo.readItem()) |next| {148 while (fifo.readItem()) |next_index| {
336 for (next.edges.items) |*edge| {149 const edges = &self.nodes.items(.edges)[next_index];
337 try fifo.writeItem(edge.to);150 for (edges.items) |edge_index| {
151 const edge = self.edges.items[edge_index];
152 try fifo.writeItem(edge.node);
338 }153 }
339 self.ordered_nodes.appendAssumeCapacity(next);154 ordered_nodes.appendAssumeCapacity(next_index);
340 }155 }
341156
342 var more: bool = true;157 var more: bool = true;
158 var size: u32 = 0;
343 while (more) {159 while (more) {
344 self.size = 0;160 size = 0;
345 more = false;161 more = false;
346 for (self.ordered_nodes.items) |node| {162 for (ordered_nodes.items) |node_index| {
347 const res = try node.finalize(self.size);163 const res = try self.finalizeNode(node_index, size);
348 self.size += res.node_size;164 size += res.node_size;
349 if (res.updated) more = true;165 if (res.updated) more = true;
350 }166 }
351 }167 }
352168
353 self.trie_dirty = false;169 try self.buffer.ensureTotalCapacityPrecise(allocator, size);
170 for (ordered_nodes.items) |node_index| {
171 try self.writeNode(node_index, self.buffer.writer(allocator));
172 }
354}173}
355174
356const ReadError = error{175const FinalizeNodeResult = struct {
357 OutOfMemory,176 /// Current size of this node in bytes.
358 EndOfStream,177 node_size: u32,
359 Overflow,178
179 /// True if the trie offset of this node in the output byte stream
180 /// would need updating; false otherwise.
181 updated: bool,
360};182};
361183
362/// Parse the trie from a byte stream.184/// Updates offset of this node in the output byte stream.
363pub fn read(self: *Trie, allocator: Allocator, reader: anytype) ReadError!usize {185fn finalizeNode(self: *Trie, node_index: Node.Index, offset_in_trie: u32) !FinalizeNodeResult {
364 return self.root.?.read(allocator, reader);186 var stream = std.io.countingWriter(std.io.null_writer);
365}187 const writer = stream.writer();
188 const slice = self.nodes.slice();
189
190 var node_size: u32 = 0;
191 if (slice.items(.is_terminal)[node_index]) {
192 const export_flags = slice.items(.export_flags)[node_index];
193 const vmaddr_offset = slice.items(.vmaddr_offset)[node_index];
194 try leb.writeULEB128(writer, export_flags);
195 try leb.writeULEB128(writer, vmaddr_offset);
196 try leb.writeULEB128(writer, stream.bytes_written);
197 } else {
198 node_size += 1; // 0x0 for non-terminal nodes
199 }
200 node_size += 1; // 1 byte for edge count
366201
367/// Write the trie to a byte stream.202 for (slice.items(.edges)[node_index].items) |edge_index| {
368/// Panics if the trie was not finalized using `finalize` before calling this method.203 const edge = &self.edges.items[edge_index];
369pub fn write(self: Trie, writer: anytype) !void {204 const next_node_offset = slice.items(.trie_offset)[edge.node];
370 assert(!self.trie_dirty);205 node_size += @intCast(edge.label.len + 1);
371 for (self.ordered_nodes.items) |node| {206 try leb.writeULEB128(writer, next_node_offset);
372 try node.write(writer);
373 }207 }
208
209 const trie_offset = slice.items(.trie_offset)[node_index];
210 const updated = offset_in_trie != trie_offset;
211 slice.items(.trie_offset)[node_index] = offset_in_trie;
212 node_size += @intCast(stream.bytes_written);
213
214 return .{ .node_size = node_size, .updated = updated };
374}215}
375216
376pub fn init(self: *Trie, allocator: Allocator) !void {217fn init(self: *Trie, allocator: Allocator) !void {
377 assert(self.root == null);218 assert(self.root == null);
378 const root = try allocator.create(Node);219 self.root = try self.addNode(allocator);
379 root.* = .{ .base = self };
380 self.root = root;
381 self.node_count += 1;
382}220}
383221
384pub fn deinit(self: *Trie, allocator: Allocator) void {222pub fn deinit(self: *Trie, allocator: Allocator) void {
385 if (self.root) |root| {223 for (self.nodes.items(.edges)) |*edges| {
386 root.deinit(allocator);224 edges.deinit(allocator);
387 allocator.destroy(root);
388 }225 }
389 self.ordered_nodes.deinit(allocator);226 self.nodes.deinit(allocator);
227 self.edges.deinit(allocator);
228 self.buffer.deinit(allocator);
390}229}
391230
392test "Trie node count" {231pub fn write(self: Trie, writer: anytype) !void {
393 const gpa = testing.allocator;232 if (self.buffer.items.len == 0) return;
394 var trie: Trie = .{};233 try writer.writeAll(self.buffer.items);
395 defer trie.deinit(gpa);234}
396 try trie.init(gpa);
397235
398 try testing.expectEqual(@as(usize, 1), trie.node_count);236/// Writes this node to a byte stream.
399 try testing.expect(trie.root != null);237/// The children of this node *are* not written to the byte stream
238/// recursively. To write all nodes to a byte stream in sequence,
239/// iterate over `Trie.ordered_nodes` and call this method on each node.
240/// This is one of the requirements of the MachO.
241/// Panics if `finalize` was not called before calling this method.
242fn writeNode(self: *Trie, node_index: Node.Index, writer: anytype) !void {
243 const slice = self.nodes.slice();
244 const edges = slice.items(.edges)[node_index];
245 const is_terminal = slice.items(.is_terminal)[node_index];
246 const export_flags = slice.items(.export_flags)[node_index];
247 const vmaddr_offset = slice.items(.vmaddr_offset)[node_index];
248
249 if (is_terminal) {
250 // Terminal node info: encode export flags and vmaddr offset of this symbol.
251 var info_buf: [@sizeOf(u64) * 2]u8 = undefined;
252 var info_stream = std.io.fixedBufferStream(&info_buf);
253 // TODO Implement for special flags.
254 assert(export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and
255 export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);
256 try leb.writeULEB128(info_stream.writer(), export_flags);
257 try leb.writeULEB128(info_stream.writer(), vmaddr_offset);
258
259 // Encode the size of the terminal node info.
260 var size_buf: [@sizeOf(u64)]u8 = undefined;
261 var size_stream = std.io.fixedBufferStream(&size_buf);
262 try leb.writeULEB128(size_stream.writer(), info_stream.pos);
263
264 // Now, write them to the output stream.
265 try writer.writeAll(size_buf[0..size_stream.pos]);
266 try writer.writeAll(info_buf[0..info_stream.pos]);
267 } else {
268 // Non-terminal node is delimited by 0 byte.
269 try writer.writeByte(0);
270 }
271 // Write number of edges (max legal number of edges is 256).
272 try writer.writeByte(@as(u8, @intCast(edges.items.len)));
273
274 for (edges.items) |edge_index| {
275 const edge = self.edges.items[edge_index];
276 // Write edge label and offset to next node in trie.
277 try writer.writeAll(edge.label);
278 try writer.writeByte(0);
279 try leb.writeULEB128(writer, slice.items(.trie_offset)[edge.node]);
280 }
281}
400282
401 try trie.put(gpa, .{283fn addNode(self: *Trie, allocator: Allocator) !Node.Index {
402 .name = "_main",284 const index: Node.Index = @intCast(try self.nodes.addOne(allocator));
403 .vmaddr_offset = 0,285 self.nodes.set(index, .{});
404 .export_flags = 0,286 return index;
405 });287}
406 try testing.expectEqual(@as(usize, 2), trie.node_count);
407288
408 // Inserting the same node shouldn't update the trie.289fn addEdge(self: *Trie, allocator: Allocator) !Edge.Index {
409 try trie.put(gpa, .{290 const index: Edge.Index = @intCast(self.edges.items.len);
410 .name = "_main",291 const edge = try self.edges.addOne(allocator);
411 .vmaddr_offset = 0,292 edge.* = .{};
412 .export_flags = 0,293 return index;
413 });294}
414 try testing.expectEqual(@as(usize, 2), trie.node_count);
415295
416 try trie.put(gpa, .{296/// Export symbol that is to be placed in the trie.
417 .name = "__mh_execute_header",297pub const ExportSymbol = struct {
418 .vmaddr_offset = 0x1000,298 /// Name of the symbol.
419 .export_flags = 0,299 name: []const u8,
420 });
421 try testing.expectEqual(@as(usize, 4), trie.node_count);
422300
423 // Inserting the same node shouldn't update the trie.301 /// Offset of this symbol's virtual memory address from the beginning
424 try trie.put(gpa, .{302 /// of the __TEXT segment.
425 .name = "__mh_execute_header",303 vmaddr_offset: u64,
426 .vmaddr_offset = 0x1000,
427 .export_flags = 0,
428 });
429 try testing.expectEqual(@as(usize, 4), trie.node_count);
430 try trie.put(gpa, .{
431 .name = "_main",
432 .vmaddr_offset = 0,
433 .export_flags = 0,
434 });
435 try testing.expectEqual(@as(usize, 4), trie.node_count);
436}
437304
438test "Trie basic" {305 /// Export flags of this exported symbol.
439 const gpa = testing.allocator;306 export_flags: u64,
440 var trie: Trie = .{};307};
441 defer trie.deinit(gpa);
442 try trie.init(gpa);
443308
444 // root --- _st ---> node309const Node = struct {
445 try trie.put(gpa, .{310 is_terminal: bool = false,
446 .name = "_st",311
447 .vmaddr_offset = 0,312 /// Export flags associated with this exported symbol.
448 .export_flags = 0,313 export_flags: u64 = 0,
449 });314
450 try testing.expect(trie.root.?.edges.items.len == 1);315 /// VM address offset wrt to the section this symbol is defined against.
451 try testing.expect(mem.eql(u8, trie.root.?.edges.items[0].label, "_st"));316 vmaddr_offset: u64 = 0,
452317
453 {318 /// Offset of this node in the trie output byte stream.
454 // root --- _st ---> node --- art ---> node319 trie_offset: u32 = 0,
455 try trie.put(gpa, .{320
456 .name = "_start",321 /// List of all edges originating from this node.
457 .vmaddr_offset = 0,322 edges: std.ArrayListUnmanaged(Edge.Index) = .{},
458 .export_flags = 0,323
459 });324 const Index = u32;
460 try testing.expect(trie.root.?.edges.items.len == 1);325};
461326
462 const nextEdge = &trie.root.?.edges.items[0];327/// Edge connecting nodes in the trie.
463 try testing.expect(mem.eql(u8, nextEdge.label, "_st"));328const Edge = struct {
464 try testing.expect(nextEdge.to.edges.items.len == 1);329 /// Target node in the trie.
465 try testing.expect(mem.eql(u8, nextEdge.to.edges.items[0].label, "art"));330 node: Node.Index = 0,
466 }331
467 {332 /// Matching prefix.
468 // root --- _ ---> node --- st ---> node --- art ---> node333 label: []const u8 = "",
469 // |334
470 // | --- main ---> node335 const Index = u32;
471 try trie.put(gpa, .{336};
472 .name = "_main",
473 .vmaddr_offset = 0,
474 .export_flags = 0,
475 });
476 try testing.expect(trie.root.?.edges.items.len == 1);
477
478 const nextEdge = &trie.root.?.edges.items[0];
479 try testing.expect(mem.eql(u8, nextEdge.label, "_"));
480 try testing.expect(nextEdge.to.edges.items.len == 2);
481 try testing.expect(mem.eql(u8, nextEdge.to.edges.items[0].label, "st"));
482 try testing.expect(mem.eql(u8, nextEdge.to.edges.items[1].label, "main"));
483
484 const nextNextEdge = &nextEdge.to.edges.items[0];
485 try testing.expect(mem.eql(u8, nextNextEdge.to.edges.items[0].label, "art"));
486 }
487}
488337
489fn expectEqualHexStrings(expected: []const u8, given: []const u8) !void {338fn expectEqualHexStrings(expected: []const u8, given: []const u8) !void {
490 assert(expected.len > 0);339 assert(expected.len > 0);
...@@ -502,7 +351,7 @@ fn expectEqualHexStrings(expected: []const u8, given: []const u8) !void {...@@ -502,7 +351,7 @@ fn expectEqualHexStrings(expected: []const u8, given: []const u8) !void {
502}351}
503352
504test "write Trie to a byte stream" {353test "write Trie to a byte stream" {
505 var gpa = testing.allocator;354 const gpa = testing.allocator;
506 var trie: Trie = .{};355 var trie: Trie = .{};
507 defer trie.deinit(gpa);356 defer trie.deinit(gpa);
508 try trie.init(gpa);357 try trie.init(gpa);
...@@ -519,7 +368,6 @@ test "write Trie to a byte stream" {...@@ -519,7 +368,6 @@ test "write Trie to a byte stream" {
519 });368 });
520369
521 try trie.finalize(gpa);370 try trie.finalize(gpa);
522 try trie.finalize(gpa); // Finalizing mulitple times is a nop subsequently unless we add new nodes.
523371
524 const exp_buffer = [_]u8{372 const exp_buffer = [_]u8{
525 0x0, 0x1, // node root373 0x0, 0x1, // node root
...@@ -531,51 +379,7 @@ test "write Trie to a byte stream" {...@@ -531,51 +379,7 @@ test "write Trie to a byte stream" {
531 0x2, 0x0, 0x0, 0x0, // terminal node379 0x2, 0x0, 0x0, 0x0, // terminal node
532 0x3, 0x0, 0x80, 0x20, 0x0, // terminal node380 0x3, 0x0, 0x80, 0x20, 0x0, // terminal node
533 };381 };
534382 try expectEqualHexStrings(&exp_buffer, trie.buffer.items);
535 const buffer = try gpa.alloc(u8, trie.size);
536 defer gpa.free(buffer);
537 var stream = std.io.fixedBufferStream(buffer);
538 {
539 _ = try trie.write(stream.writer());
540 try expectEqualHexStrings(&exp_buffer, buffer);
541 }
542 {
543 // Writing finalized trie again should yield the same result.
544 try stream.seekTo(0);
545 _ = try trie.write(stream.writer());
546 try expectEqualHexStrings(&exp_buffer, buffer);
547 }
548}
549
550test "parse Trie from byte stream" {
551 const gpa = testing.allocator;
552
553 const in_buffer = [_]u8{
554 0x0, 0x1, // node root
555 0x5f, 0x0, 0x5, // edge '_'
556 0x0, 0x2, // non-terminal node
557 0x5f, 0x6d, 0x68, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, // edge '_mh_execute_header'
558 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x0, 0x21, // edge '_mh_execute_header'
559 0x6d, 0x61, 0x69, 0x6e, 0x0, 0x25, // edge 'main'
560 0x2, 0x0, 0x0, 0x0, // terminal node
561 0x3, 0x0, 0x80, 0x20, 0x0, // terminal node
562 };
563
564 var in_stream = std.io.fixedBufferStream(&in_buffer);
565 var trie: Trie = .{};
566 defer trie.deinit(gpa);
567 try trie.init(gpa);
568 const nread = try trie.read(gpa, in_stream.reader());
569
570 try testing.expect(nread == in_buffer.len);
571
572 try trie.finalize(gpa);
573
574 const out_buffer = try gpa.alloc(u8, trie.size);
575 defer gpa.free(out_buffer);
576 var out_stream = std.io.fixedBufferStream(out_buffer);
577 _ = try trie.write(out_stream.writer());
578 try expectEqualHexStrings(&in_buffer, out_buffer);
579}383}
580384
581test "ordering bug" {385test "ordering bug" {
...@@ -602,11 +406,18 @@ test "ordering bug" {...@@ -602,11 +406,18 @@ test "ordering bug" {
602 0x88, 0x80, 0x02, 0x01, 0x73, 0x53, 0x74, 0x72,406 0x88, 0x80, 0x02, 0x01, 0x73, 0x53, 0x74, 0x72,
603 0x00, 0x12, 0x03, 0x00, 0xD8, 0x0A, 0x00,407 0x00, 0x12, 0x03, 0x00, 0xD8, 0x0A, 0x00,
604 };408 };
605409 try expectEqualHexStrings(&exp_buffer, trie.buffer.items);
606 const buffer = try gpa.alloc(u8, trie.size);
607 defer gpa.free(buffer);
608 var stream = std.io.fixedBufferStream(buffer);
609 // Writing finalized trie again should yield the same result.
610 _ = try trie.write(stream.writer());
611 try expectEqualHexStrings(&exp_buffer, buffer);
612}410}
411
412const assert = std.debug.assert;
413const leb = std.leb;
414const log = std.log.scoped(.macho);
415const macho = std.macho;
416const mem = std.mem;
417const std = @import("std");
418const testing = std.testing;
419const trace = @import("../../../tracy.zig").trace;
420
421const Allocator = mem.Allocator;
422const MachO = @import("../../MachO.zig");
423const Trie = @This();
src/link/MachO/dyld_info/bind.zig+243-22
...@@ -1,14 +1,3 @@...@@ -1,14 +1,3 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const leb = std.leb;
4const log = std.log.scoped(.link_dyld_info);
5const macho = std.macho;
6const testing = std.testing;
7
8const Allocator = std.mem.Allocator;
9const MachO = @import("../../MachO.zig");
10const Symbol = @import("../Symbol.zig");
11
12pub const Entry = struct {1pub const Entry = struct {
13 target: Symbol.Index,2 target: Symbol.Index,
14 offset: u64,3 offset: u64,
...@@ -39,11 +28,108 @@ pub const Bind = struct {...@@ -39,11 +28,108 @@ pub const Bind = struct {
39 self.buffer.deinit(gpa);28 self.buffer.deinit(gpa);
40 }29 }
4130
42 pub fn size(self: Self) u64 {31 pub fn updateSize(self: *Self, macho_file: *MachO) !void {
43 return @intCast(self.buffer.items.len);32 const tracy = trace(@src());
33 defer tracy.end();
34
35 const gpa = macho_file.base.comp.gpa;
36 const cpu_arch = macho_file.getTarget().cpu.arch;
37
38 var objects = try std.ArrayList(File.Index).initCapacity(gpa, macho_file.objects.items.len + 1);
39 defer objects.deinit();
40 objects.appendSliceAssumeCapacity(macho_file.objects.items);
41 if (macho_file.getInternalObject()) |obj| objects.appendAssumeCapacity(obj.index);
42
43 for (objects.items) |index| {
44 const file = macho_file.getFile(index).?;
45 for (file.getAtoms()) |atom_index| {
46 const atom = macho_file.getAtom(atom_index) orelse continue;
47 if (!atom.flags.alive) continue;
48 if (atom.getInputSection(macho_file).isZerofill()) continue;
49 const atom_addr = atom.getAddress(macho_file);
50 const relocs = atom.getRelocs(macho_file);
51 const seg_id = macho_file.sections.items(.segment_id)[atom.out_n_sect];
52 const seg = macho_file.segments.items[seg_id];
53 for (relocs) |rel| {
54 if (rel.type != .unsigned or rel.meta.length != 3 or rel.tag != .@"extern") continue;
55 const rel_offset = rel.offset - atom.off;
56 const addend = rel.addend + rel.getRelocAddend(cpu_arch);
57 const sym = rel.getTargetSymbol(macho_file);
58 if (sym.isTlvInit(macho_file)) continue;
59 const entry = Entry{
60 .target = rel.target,
61 .offset = atom_addr + rel_offset - seg.vmaddr,
62 .segment_id = seg_id,
63 .addend = addend,
64 };
65 if (sym.flags.import or (!(sym.flags.@"export" and sym.flags.weak) and sym.flags.interposable)) {
66 try self.entries.append(gpa, entry);
67 }
68 }
69 }
70 }
71
72 if (macho_file.got_sect_index) |sid| {
73 const seg_id = macho_file.sections.items(.segment_id)[sid];
74 const seg = macho_file.segments.items[seg_id];
75 for (macho_file.got.symbols.items, 0..) |ref, idx| {
76 const sym = macho_file.getSymbol(ref);
77 const addr = macho_file.got.getAddress(@intCast(idx), macho_file);
78 const entry = Entry{
79 .target = ref,
80 .offset = addr - seg.vmaddr,
81 .segment_id = seg_id,
82 .addend = 0,
83 };
84 if (sym.flags.import or (sym.flags.@"export" and sym.flags.interposable and !sym.flags.weak)) {
85 try self.entries.append(gpa, entry);
86 }
87 }
88 }
89
90 if (macho_file.la_symbol_ptr_sect_index) |sid| {
91 const sect = macho_file.sections.items(.header)[sid];
92 const seg_id = macho_file.sections.items(.segment_id)[sid];
93 const seg = macho_file.segments.items[seg_id];
94 for (macho_file.stubs.symbols.items, 0..) |ref, idx| {
95 const sym = macho_file.getSymbol(ref);
96 const addr = sect.addr + idx * @sizeOf(u64);
97 const bind_entry = Entry{
98 .target = ref,
99 .offset = addr - seg.vmaddr,
100 .segment_id = seg_id,
101 .addend = 0,
102 };
103 if (sym.flags.import and sym.flags.weak) {
104 try self.entries.append(gpa, bind_entry);
105 }
106 }
107 }
108
109 if (macho_file.tlv_ptr_sect_index) |sid| {
110 const seg_id = macho_file.sections.items(.segment_id)[sid];
111 const seg = macho_file.segments.items[seg_id];
112
113 for (macho_file.tlv_ptr.symbols.items, 0..) |ref, idx| {
114 const sym = macho_file.getSymbol(ref);
115 const addr = macho_file.tlv_ptr.getAddress(@intCast(idx), macho_file);
116 const entry = Entry{
117 .target = ref,
118 .offset = addr - seg.vmaddr,
119 .segment_id = seg_id,
120 .addend = 0,
121 };
122 if (sym.flags.import or (sym.flags.@"export" and sym.flags.interposable and !sym.flags.weak)) {
123 try self.entries.append(gpa, entry);
124 }
125 }
126 }
127
128 try self.finalize(gpa, macho_file);
129 macho_file.dyld_info_cmd.bind_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64));
44 }130 }
45131
46 pub fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void {132 fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void {
47 if (self.entries.items.len == 0) return;133 if (self.entries.items.len == 0) return;
48134
49 const writer = self.buffer.writer(gpa);135 const writer = self.buffer.writer(gpa);
...@@ -178,7 +264,6 @@ pub const Bind = struct {...@@ -178,7 +264,6 @@ pub const Bind = struct {
178 }264 }
179265
180 pub fn write(self: Self, writer: anytype) !void {266 pub fn write(self: Self, writer: anytype) !void {
181 if (self.size() == 0) return;
182 try writer.writeAll(self.buffer.items);267 try writer.writeAll(self.buffer.items);
183 }268 }
184};269};
...@@ -194,11 +279,109 @@ pub const WeakBind = struct {...@@ -194,11 +279,109 @@ pub const WeakBind = struct {
194 self.buffer.deinit(gpa);279 self.buffer.deinit(gpa);
195 }280 }
196281
197 pub fn size(self: Self) u64 {282 pub fn updateSize(self: *Self, macho_file: *MachO) !void {
198 return @intCast(self.buffer.items.len);283 const tracy = trace(@src());
284 defer tracy.end();
285
286 const gpa = macho_file.base.comp.gpa;
287 const cpu_arch = macho_file.getTarget().cpu.arch;
288
289 var objects = try std.ArrayList(File.Index).initCapacity(gpa, macho_file.objects.items.len + 1);
290 defer objects.deinit();
291 objects.appendSliceAssumeCapacity(macho_file.objects.items);
292 if (macho_file.getInternalObject()) |obj| objects.appendAssumeCapacity(obj.index);
293
294 for (objects.items) |index| {
295 const file = macho_file.getFile(index).?;
296 for (file.getAtoms()) |atom_index| {
297 const atom = macho_file.getAtom(atom_index) orelse continue;
298 if (!atom.flags.alive) continue;
299 if (atom.getInputSection(macho_file).isZerofill()) continue;
300 const atom_addr = atom.getAddress(macho_file);
301 const relocs = atom.getRelocs(macho_file);
302 const seg_id = macho_file.sections.items(.segment_id)[atom.out_n_sect];
303 const seg = macho_file.segments.items[seg_id];
304 for (relocs) |rel| {
305 if (rel.type != .unsigned or rel.meta.length != 3 or rel.tag != .@"extern") continue;
306 const rel_offset = rel.offset - atom.off;
307 const addend = rel.addend + rel.getRelocAddend(cpu_arch);
308 const sym = rel.getTargetSymbol(macho_file);
309 if (sym.isTlvInit(macho_file)) continue;
310 const entry = Entry{
311 .target = rel.target,
312 .offset = atom_addr + rel_offset - seg.vmaddr,
313 .segment_id = seg_id,
314 .addend = addend,
315 };
316 if (!sym.isLocal() and sym.flags.weak) {
317 try self.entries.append(gpa, entry);
318 }
319 }
320 }
321 }
322
323 if (macho_file.got_sect_index) |sid| {
324 const seg_id = macho_file.sections.items(.segment_id)[sid];
325 const seg = macho_file.segments.items[seg_id];
326 for (macho_file.got.symbols.items, 0..) |ref, idx| {
327 const sym = macho_file.getSymbol(ref);
328 const addr = macho_file.got.getAddress(@intCast(idx), macho_file);
329 const entry = Entry{
330 .target = ref,
331 .offset = addr - seg.vmaddr,
332 .segment_id = seg_id,
333 .addend = 0,
334 };
335 if (sym.flags.weak) {
336 try self.entries.append(gpa, entry);
337 }
338 }
339 }
340
341 if (macho_file.la_symbol_ptr_sect_index) |sid| {
342 const sect = macho_file.sections.items(.header)[sid];
343 const seg_id = macho_file.sections.items(.segment_id)[sid];
344 const seg = macho_file.segments.items[seg_id];
345
346 for (macho_file.stubs.symbols.items, 0..) |ref, idx| {
347 const sym = macho_file.getSymbol(ref);
348 const addr = sect.addr + idx * @sizeOf(u64);
349 const bind_entry = Entry{
350 .target = ref,
351 .offset = addr - seg.vmaddr,
352 .segment_id = seg_id,
353 .addend = 0,
354 };
355 if (sym.flags.weak) {
356 try self.entries.append(gpa, bind_entry);
357 }
358 }
359 }
360
361 if (macho_file.tlv_ptr_sect_index) |sid| {
362 const seg_id = macho_file.sections.items(.segment_id)[sid];
363 const seg = macho_file.segments.items[seg_id];
364
365 for (macho_file.tlv_ptr.symbols.items, 0..) |ref, idx| {
366 const sym = macho_file.getSymbol(ref);
367 const addr = macho_file.tlv_ptr.getAddress(@intCast(idx), macho_file);
368 const entry = Entry{
369 .target = ref,
370 .offset = addr - seg.vmaddr,
371 .segment_id = seg_id,
372 .addend = 0,
373 };
374 if (sym.flags.weak) {
375 try self.entries.append(gpa, entry);
376 }
377 }
378 }
379
380 try self.finalize(gpa, macho_file);
381 macho_file.dyld_info_cmd.weak_bind_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64));
199 }382 }
200383
201 pub fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void {384 fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void {
202 if (self.entries.items.len == 0) return;385 if (self.entries.items.len == 0) return;
203386
204 const writer = self.buffer.writer(gpa);387 const writer = self.buffer.writer(gpa);
...@@ -322,7 +505,6 @@ pub const WeakBind = struct {...@@ -322,7 +505,6 @@ pub const WeakBind = struct {
322 }505 }
323506
324 pub fn write(self: Self, writer: anytype) !void {507 pub fn write(self: Self, writer: anytype) !void {
325 if (self.size() == 0) return;
326 try writer.writeAll(self.buffer.items);508 try writer.writeAll(self.buffer.items);
327 }509 }
328};510};
...@@ -340,11 +522,36 @@ pub const LazyBind = struct {...@@ -340,11 +522,36 @@ pub const LazyBind = struct {
340 self.offsets.deinit(gpa);522 self.offsets.deinit(gpa);
341 }523 }
342524
343 pub fn size(self: Self) u64 {525 pub fn updateSize(self: *Self, macho_file: *MachO) !void {
344 return @intCast(self.buffer.items.len);526 const tracy = trace(@src());
527 defer tracy.end();
528
529 const gpa = macho_file.base.comp.gpa;
530
531 const sid = macho_file.la_symbol_ptr_sect_index.?;
532 const sect = macho_file.sections.items(.header)[sid];
533 const seg_id = macho_file.sections.items(.segment_id)[sid];
534 const seg = macho_file.segments.items[seg_id];
535
536 for (macho_file.stubs.symbols.items, 0..) |ref, idx| {
537 const sym = macho_file.getSymbol(ref);
538 const addr = sect.addr + idx * @sizeOf(u64);
539 const bind_entry = Entry{
540 .target = ref,
541 .offset = addr - seg.vmaddr,
542 .segment_id = seg_id,
543 .addend = 0,
544 };
545 if ((sym.flags.import and !sym.flags.weak) or (sym.flags.interposable and !sym.flags.weak)) {
546 try self.entries.append(gpa, bind_entry);
547 }
548 }
549
550 try self.finalize(gpa, macho_file);
551 macho_file.dyld_info_cmd.lazy_bind_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64));
345 }552 }
346553
347 pub fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void {554 fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void {
348 try self.offsets.ensureTotalCapacityPrecise(gpa, self.entries.items.len);555 try self.offsets.ensureTotalCapacityPrecise(gpa, self.entries.items.len);
349556
350 const writer = self.buffer.writer(gpa);557 const writer = self.buffer.writer(gpa);
...@@ -474,3 +681,17 @@ fn done(writer: anytype) !void {...@@ -474,3 +681,17 @@ fn done(writer: anytype) !void {
474 log.debug(">>> done", .{});681 log.debug(">>> done", .{});
475 try writer.writeByte(macho.BIND_OPCODE_DONE);682 try writer.writeByte(macho.BIND_OPCODE_DONE);
476}683}
684
685const assert = std.debug.assert;
686const leb = std.leb;
687const log = std.log.scoped(.link_dyld_info);
688const macho = std.macho;
689const mem = std.mem;
690const testing = std.testing;
691const trace = @import("../../../tracy.zig").trace;
692const std = @import("std");
693
694const Allocator = mem.Allocator;
695const File = @import("../file.zig").File;
696const MachO = @import("../../MachO.zig");
697const Symbol = @import("../Symbol.zig");
src/link/MachO/synthetic.zig-138
...@@ -70,22 +70,6 @@ pub const ZigGotSection = struct {...@@ -70,22 +70,6 @@ pub const ZigGotSection = struct {
70 }70 }
71 }71 }
7272
73 pub fn addDyldRelocs(zig_got: ZigGotSection, macho_file: *MachO) !void {
74 const tracy = trace(@src());
75 defer tracy.end();
76 const gpa = macho_file.base.comp.gpa;
77 const seg_id = macho_file.sections.items(.segment_id)[macho_file.zig_got_sect_index.?];
78 const seg = macho_file.segments.items[seg_id];
79
80 for (0..zig_got.entries.items.len) |idx| {
81 const addr = zig_got.entryAddress(@intCast(idx), macho_file);
82 try macho_file.rebase.entries.append(gpa, .{
83 .offset = addr - seg.vmaddr,
84 .segment_id = seg_id,
85 });
86 }
87 }
88
89 const FormatCtx = struct {73 const FormatCtx = struct {
90 zig_got: ZigGotSection,74 zig_got: ZigGotSection,
91 macho_file: *MachO,75 macho_file: *MachO,
...@@ -146,41 +130,6 @@ pub const GotSection = struct {...@@ -146,41 +130,6 @@ pub const GotSection = struct {
146 return got.symbols.items.len * @sizeOf(u64);130 return got.symbols.items.len * @sizeOf(u64);
147 }131 }
148132
149 pub fn addDyldRelocs(got: GotSection, macho_file: *MachO) !void {
150 const tracy = trace(@src());
151 defer tracy.end();
152 const gpa = macho_file.base.comp.gpa;
153 const seg_id = macho_file.sections.items(.segment_id)[macho_file.got_sect_index.?];
154 const seg = macho_file.segments.items[seg_id];
155
156 for (got.symbols.items, 0..) |sym_index, idx| {
157 const sym = macho_file.getSymbol(sym_index);
158 const addr = got.getAddress(@intCast(idx), macho_file);
159 const entry = bind.Entry{
160 .target = sym_index,
161 .offset = addr - seg.vmaddr,
162 .segment_id = seg_id,
163 .addend = 0,
164 };
165 if (sym.flags.import) {
166 try macho_file.bind.entries.append(gpa, entry);
167 if (sym.flags.weak) {
168 try macho_file.weak_bind.entries.append(gpa, entry);
169 }
170 } else {
171 try macho_file.rebase.entries.append(gpa, .{
172 .offset = addr - seg.vmaddr,
173 .segment_id = seg_id,
174 });
175 if (sym.flags.weak) {
176 try macho_file.weak_bind.entries.append(gpa, entry);
177 } else if (sym.flags.interposable) {
178 try macho_file.bind.entries.append(gpa, entry);
179 }
180 }
181 }
182 }
183
184 pub fn write(got: GotSection, macho_file: *MachO, writer: anytype) !void {133 pub fn write(got: GotSection, macho_file: *MachO, writer: anytype) !void {
185 const tracy = trace(@src());134 const tracy = trace(@src());
186 defer tracy.end();135 defer tracy.end();
...@@ -446,49 +395,6 @@ pub const LaSymbolPtrSection = struct {...@@ -446,49 +395,6 @@ pub const LaSymbolPtrSection = struct {
446 return macho_file.stubs.symbols.items.len * @sizeOf(u64);395 return macho_file.stubs.symbols.items.len * @sizeOf(u64);
447 }396 }
448397
449 pub fn addDyldRelocs(laptr: LaSymbolPtrSection, macho_file: *MachO) !void {
450 const tracy = trace(@src());
451 defer tracy.end();
452 _ = laptr;
453 const gpa = macho_file.base.comp.gpa;
454
455 const sect = macho_file.sections.items(.header)[macho_file.la_symbol_ptr_sect_index.?];
456 const seg_id = macho_file.sections.items(.segment_id)[macho_file.la_symbol_ptr_sect_index.?];
457 const seg = macho_file.segments.items[seg_id];
458
459 for (macho_file.stubs.symbols.items, 0..) |sym_index, idx| {
460 const sym = macho_file.getSymbol(sym_index);
461 const addr = sect.addr + idx * @sizeOf(u64);
462 const rebase_entry = Rebase.Entry{
463 .offset = addr - seg.vmaddr,
464 .segment_id = seg_id,
465 };
466 const bind_entry = bind.Entry{
467 .target = sym_index,
468 .offset = addr - seg.vmaddr,
469 .segment_id = seg_id,
470 .addend = 0,
471 };
472 if (sym.flags.import) {
473 if (sym.flags.weak) {
474 try macho_file.bind.entries.append(gpa, bind_entry);
475 try macho_file.weak_bind.entries.append(gpa, bind_entry);
476 } else {
477 try macho_file.lazy_bind.entries.append(gpa, bind_entry);
478 try macho_file.rebase.entries.append(gpa, rebase_entry);
479 }
480 } else {
481 if (sym.flags.weak) {
482 try macho_file.rebase.entries.append(gpa, rebase_entry);
483 try macho_file.weak_bind.entries.append(gpa, bind_entry);
484 } else if (sym.flags.interposable) {
485 try macho_file.lazy_bind.entries.append(gpa, bind_entry);
486 try macho_file.rebase.entries.append(gpa, rebase_entry);
487 }
488 }
489 }
490 }
491
492 pub fn write(laptr: LaSymbolPtrSection, macho_file: *MachO, writer: anytype) !void {398 pub fn write(laptr: LaSymbolPtrSection, macho_file: *MachO, writer: anytype) !void {
493 const tracy = trace(@src());399 const tracy = trace(@src());
494 defer tracy.end();400 defer tracy.end();
...@@ -539,41 +445,6 @@ pub const TlvPtrSection = struct {...@@ -539,41 +445,6 @@ pub const TlvPtrSection = struct {
539 return tlv.symbols.items.len * @sizeOf(u64);445 return tlv.symbols.items.len * @sizeOf(u64);
540 }446 }
541447
542 pub fn addDyldRelocs(tlv: TlvPtrSection, macho_file: *MachO) !void {
543 const tracy = trace(@src());
544 defer tracy.end();
545 const gpa = macho_file.base.comp.gpa;
546 const seg_id = macho_file.sections.items(.segment_id)[macho_file.tlv_ptr_sect_index.?];
547 const seg = macho_file.segments.items[seg_id];
548
549 for (tlv.symbols.items, 0..) |sym_index, idx| {
550 const sym = macho_file.getSymbol(sym_index);
551 const addr = tlv.getAddress(@intCast(idx), macho_file);
552 const entry = bind.Entry{
553 .target = sym_index,
554 .offset = addr - seg.vmaddr,
555 .segment_id = seg_id,
556 .addend = 0,
557 };
558 if (sym.flags.import) {
559 try macho_file.bind.entries.append(gpa, entry);
560 if (sym.flags.weak) {
561 try macho_file.weak_bind.entries.append(gpa, entry);
562 }
563 } else {
564 try macho_file.rebase.entries.append(gpa, .{
565 .offset = addr - seg.vmaddr,
566 .segment_id = seg_id,
567 });
568 if (sym.flags.weak) {
569 try macho_file.weak_bind.entries.append(gpa, entry);
570 } else if (sym.flags.interposable) {
571 try macho_file.bind.entries.append(gpa, entry);
572 }
573 }
574 }
575 }
576
577 pub fn write(tlv: TlvPtrSection, macho_file: *MachO, writer: anytype) !void {448 pub fn write(tlv: TlvPtrSection, macho_file: *MachO, writer: anytype) !void {
578 const tracy = trace(@src());449 const tracy = trace(@src());
579 defer tracy.end();450 defer tracy.end();
...@@ -772,21 +643,12 @@ pub const Indsymtab = struct {...@@ -772,21 +643,12 @@ pub const Indsymtab = struct {
772 }643 }
773};644};
774645
775pub const RebaseSection = Rebase;
776pub const BindSection = bind.Bind;
777pub const WeakBindSection = bind.WeakBind;
778pub const LazyBindSection = bind.LazyBind;
779pub const ExportTrieSection = Trie;
780
781const aarch64 = @import("../aarch64.zig");646const aarch64 = @import("../aarch64.zig");
782const assert = std.debug.assert;647const assert = std.debug.assert;
783const bind = @import("dyld_info/bind.zig");
784const math = std.math;648const math = std.math;
785const std = @import("std");649const std = @import("std");
786const trace = @import("../../tracy.zig").trace;650const trace = @import("../../tracy.zig").trace;
787651
788const Allocator = std.mem.Allocator;652const Allocator = std.mem.Allocator;
789const MachO = @import("../MachO.zig");653const MachO = @import("../MachO.zig");
790const Rebase = @import("dyld_info/Rebase.zig");
791const Symbol = @import("Symbol.zig");654const Symbol = @import("Symbol.zig");
792const Trie = @import("dyld_info/Trie.zig");