authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-08-26 11:43:11+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-08-26 11:43:11+02:00
logd19d3342c29cc0f5d78cd91b61e5949a86811680
tree6ec73c2bbd0367ef503c365081173a0f14ff1f48
parentc12183b608c48ec4417c530916fc1a78b7d442f6

macho: save lazy binding info as part of the atom


3 files changed, 130 insertions(+), 96 deletions(-)

src/link/MachO.zig+77-80
......@@ -155,9 +155,7 @@ strtab: std.ArrayListUnmanaged(u8) = .{},
155155strtab_dir: std.HashMapUnmanaged(u32, u32, StringIndexContext, std.hash_map.default_max_load_percentage) = .{},
156156
157157got_entries_map: std.AutoArrayHashMapUnmanaged(GotIndirectionKey, *TextBlock) = .{},
158
159stubs: std.ArrayListUnmanaged(u32) = .{},
160stubs_map: std.AutoHashMapUnmanaged(u32, u32) = .{},
158stubs_map: std.AutoArrayHashMapUnmanaged(u32, *TextBlock) = .{},
161159
162160error_flags: File.ErrorFlags = File.ErrorFlags{},
163161
......@@ -783,27 +781,6 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {
783781
784782 try self.parseTextBlocks();
785783 try self.sortSections();
786
787 for (self.stubs.items) |_| {
788 const stub_helper_atom = try self.createStubHelperAtom();
789 try self.allocateAtomStage1(stub_helper_atom, .{
790 .seg = self.text_segment_cmd_index.?,
791 .sect = self.stub_helper_section_index.?,
792 });
793
794 const laptr_atom = try self.createLazyPointerAtom(stub_helper_atom.local_sym_index);
795 try self.allocateAtomStage1(laptr_atom, .{
796 .seg = self.data_segment_cmd_index.?,
797 .sect = self.la_symbol_ptr_section_index.?,
798 });
799
800 const stub_atom = try self.createStubAtom(laptr_atom.local_sym_index);
801 try self.allocateAtomStage1(stub_atom, .{
802 .seg = self.text_segment_cmd_index.?,
803 .sect = self.stubs_section_index.?,
804 });
805 }
806
807784 try self.allocateTextSegment();
808785 try self.allocateDataConstSegment();
809786 try self.allocateDataSegment();
......@@ -2159,7 +2136,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !*TextBlock {
21592136 return atom;
21602137}
21612138
2162fn createStubHelperAtom(self: *MachO) !*TextBlock {
2139pub fn createStubHelperAtom(self: *MachO) !*TextBlock {
21632140 const arch = self.base.options.target.cpu.arch;
21642141 const stub_size: u4 = switch (arch) {
21652142 .x86_64 => 10,
......@@ -2225,7 +2202,7 @@ fn createStubHelperAtom(self: *MachO) !*TextBlock {
22252202 return atom;
22262203}
22272204
2228fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32) !*TextBlock {
2205pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, lazy_binding_sym_index: u32) !*TextBlock {
22292206 const local_sym_index = @intCast(u32, self.locals.items.len);
22302207 try self.locals.append(self.base.allocator, .{
22312208 .n_strx = try self.makeString("lazy_ptr"),
......@@ -2248,11 +2225,15 @@ fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32) !*TextBlock {
22482225 },
22492226 });
22502227 try atom.rebases.append(self.base.allocator, 0);
2228 try atom.lazy_bindings.append(self.base.allocator, .{
2229 .local_sym_index = lazy_binding_sym_index,
2230 .offset = 0,
2231 });
22512232 self.lazy_binding_info_dirty = true;
22522233 return atom;
22532234}
22542235
2255fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*TextBlock {
2236pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*TextBlock {
22562237 const arch = self.base.options.target.cpu.arch;
22572238 const alignment: u2 = switch (arch) {
22582239 .x86_64 => 0,
......@@ -2661,7 +2642,10 @@ fn resolveSymbols(self: *MachO) !void {
26612642 break :blk atom;
26622643 };
26632644 const laptr_atom = blk: {
2664 const atom = try self.createLazyPointerAtom(stub_helper_atom.local_sym_index);
2645 const atom = try self.createLazyPointerAtom(
2646 stub_helper_atom.local_sym_index,
2647 resolv.where_index,
2648 );
26652649 const match = MatchingSection{
26662650 .seg = self.data_segment_cmd_index.?,
26672651 .sect = self.la_symbol_ptr_section_index.?,
......@@ -2990,8 +2974,6 @@ fn writeRebaseInfoTableZld(self: *MachO) !void {
29902974 }
29912975 }
29922976
2993 std.sort.sort(bind.Pointer, pointers.items, {}, bind.pointerCmp);
2994
29952977 const size = try bind.rebaseInfoSize(pointers.items);
29962978 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
29972979 defer self.base.allocator.free(buffer);
......@@ -3067,22 +3049,29 @@ fn writeLazyBindInfoTableZld(self: *MachO) !void {
30673049 var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);
30683050 defer pointers.deinit();
30693051
3070 if (self.la_symbol_ptr_section_index) |idx| {
3052 if (self.la_symbol_ptr_section_index) |sect| blk: {
3053 var atom = self.blocks.get(.{
3054 .seg = self.data_segment_cmd_index.?,
3055 .sect = sect,
3056 }) orelse break :blk;
30713057 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
3072 const sect = seg.sections.items[idx];
3073 const base_offset = sect.addr - seg.inner.vmaddr;
3074 const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
3075
3076 try pointers.ensureUnusedCapacity(self.stubs.items.len);
3077
3078 for (self.stubs.items) |import_id, i| {
3079 const sym = self.undefs.items[import_id];
3080 pointers.appendAssumeCapacity(.{
3081 .offset = base_offset + i * @sizeOf(u64),
3082 .segment_id = segment_id,
3083 .dylib_ordinal = @divExact(sym.n_desc, macho.N_SYMBOL_RESOLVER),
3084 .name = self.getString(sym.n_strx),
3085 });
3058
3059 while (true) {
3060 const sym = self.locals.items[atom.local_sym_index];
3061 const base_offset = sym.n_value - seg.inner.vmaddr;
3062
3063 for (atom.lazy_bindings.items) |binding| {
3064 const bind_sym = self.undefs.items[binding.local_sym_index];
3065 try pointers.append(.{
3066 .offset = binding.offset + base_offset,
3067 .segment_id = self.data_segment_cmd_index.?,
3068 .dylib_ordinal = @divExact(bind_sym.n_desc, macho.N_SYMBOL_RESOLVER),
3069 .name = self.getString(bind_sym.n_strx),
3070 });
3071 }
3072 if (atom.prev) |prev| {
3073 atom = prev;
3074 } else break;
30863075 }
30873076 }
30883077
......@@ -3245,7 +3234,7 @@ fn writeSymbolTable(self: *MachO) !void {
32453234 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
32463235 const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
32473236
3248 const nstubs = @intCast(u32, self.stubs.items.len);
3237 const nstubs = @intCast(u32, self.stubs_map.keys().len);
32493238 const ngot_entries = @intCast(u32, self.got_entries_map.keys().len);
32503239
32513240 dysymtab.indirectsymoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
......@@ -3266,8 +3255,8 @@ fn writeSymbolTable(self: *MachO) !void {
32663255 var writer = stream.writer();
32673256
32683257 stubs.reserved1 = 0;
3269 for (self.stubs.items) |id| {
3270 try writer.writeIntLittle(u32, dysymtab.iundefsym + id);
3258 for (self.stubs_map.keys()) |key| {
3259 try writer.writeIntLittle(u32, dysymtab.iundefsym + key);
32713260 }
32723261
32733262 got.reserved1 = nstubs;
......@@ -3283,8 +3272,8 @@ fn writeSymbolTable(self: *MachO) !void {
32833272 }
32843273
32853274 la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries;
3286 for (self.stubs.items) |id| {
3287 try writer.writeIntLittle(u32, dysymtab.iundefsym + id);
3275 for (self.stubs_map.keys()) |key| {
3276 try writer.writeIntLittle(u32, dysymtab.iundefsym + key);
32883277 }
32893278
32903279 try self.base.file.?.pwriteAll(buf, dysymtab.indirectsymoff);
......@@ -3301,7 +3290,6 @@ pub fn deinit(self: *MachO) void {
33013290
33023291 self.section_ordinals.deinit(self.base.allocator);
33033292 self.got_entries_map.deinit(self.base.allocator);
3304 self.stubs.deinit(self.base.allocator);
33053293 self.stubs_map.deinit(self.base.allocator);
33063294 self.strtab_dir.deinit(self.base.allocator);
33073295 self.strtab.deinit(self.base.allocator);
......@@ -4557,10 +4545,6 @@ pub fn addExternFn(self: *MachO, name: []const u8) !u32 {
45574545 });
45584546 try self.unresolved.putNoClobber(self.base.allocator, sym_index, .stub);
45594547
4560 const stubs_index = @intCast(u32, self.stubs.items.len);
4561 try self.stubs.append(self.base.allocator, sym_index);
4562 try self.stubs_map.putNoClobber(self.base.allocator, sym_index, stubs_index);
4563
45644548 return sym_index;
45654549}
45664550
......@@ -4803,7 +4787,7 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
48034787 const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
48044788 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
48054789
4806 const nstubs = @intCast(u32, self.stubs.items.len);
4790 const nstubs = @intCast(u32, self.stubs_map.keys().len);
48074791 const ngot_entries = @intCast(u32, self.got_entries_map.keys().len);
48084792 const allocated_size = self.allocatedSizeLinkedit(dysymtab.indirectsymoff);
48094793 const nindirectsyms = nstubs * 2 + ngot_entries;
......@@ -4825,8 +4809,8 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
48254809 var writer = stream.writer();
48264810
48274811 stubs.reserved1 = 0;
4828 for (self.stubs.items) |id| {
4829 try writer.writeIntLittle(u32, dysymtab.iundefsym + id);
4812 for (self.stubs_map.keys()) |key| {
4813 try writer.writeIntLittle(u32, dysymtab.iundefsym + key);
48304814 }
48314815
48324816 got.reserved1 = nstubs;
......@@ -4842,8 +4826,8 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
48424826 }
48434827
48444828 la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries;
4845 for (self.stubs.items) |id| {
4846 try writer.writeIntLittle(u32, dysymtab.iundefsym + id);
4829 for (self.stubs_map.keys()) |key| {
4830 try writer.writeIntLittle(u32, dysymtab.iundefsym + key);
48474831 }
48484832
48494833 try self.base.file.?.pwriteAll(buf, dysymtab.indirectsymoff);
......@@ -5050,8 +5034,6 @@ fn writeRebaseInfoTable(self: *MachO) !void {
50505034 }
50515035 }
50525036
5053 std.sort.sort(bind.Pointer, pointers.items, {}, bind.pointerCmp);
5054
50555037 const size = try bind.rebaseInfoSize(pointers.items);
50565038 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
50575039 defer self.base.allocator.free(buffer);
......@@ -5151,22 +5133,29 @@ fn writeLazyBindInfoTable(self: *MachO) !void {
51515133 var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);
51525134 defer pointers.deinit();
51535135
5154 if (self.la_symbol_ptr_section_index) |idx| {
5136 if (self.la_symbol_ptr_section_index) |sect| blk: {
5137 var atom = self.blocks.get(.{
5138 .seg = self.data_segment_cmd_index.?,
5139 .sect = sect,
5140 }) orelse break :blk;
51555141 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
5156 const sect = seg.sections.items[idx];
5157 const base_offset = sect.addr - seg.inner.vmaddr;
5158 const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
5159
5160 try pointers.ensureUnusedCapacity(self.stubs.items.len);
5161
5162 for (self.stubs.items) |import_id, i| {
5163 const sym = self.undefs.items[import_id];
5164 pointers.appendAssumeCapacity(.{
5165 .offset = base_offset + i * @sizeOf(u64),
5166 .segment_id = segment_id,
5167 .dylib_ordinal = @divExact(sym.n_desc, macho.N_SYMBOL_RESOLVER),
5168 .name = self.getString(sym.n_strx),
5169 });
5142
5143 while (true) {
5144 const sym = self.locals.items[atom.local_sym_index];
5145 const base_offset = sym.n_value - seg.inner.vmaddr;
5146
5147 for (atom.lazy_bindings.items) |binding| {
5148 const bind_sym = self.undefs.items[binding.local_sym_index];
5149 try pointers.append(.{
5150 .offset = binding.offset + base_offset,
5151 .segment_id = self.data_segment_cmd_index.?,
5152 .dylib_ordinal = @divExact(bind_sym.n_desc, macho.N_SYMBOL_RESOLVER),
5153 .name = self.getString(bind_sym.n_strx),
5154 });
5155 }
5156 if (atom.prev) |prev| {
5157 atom = prev;
5158 } else break;
51705159 }
51715160 }
51725161
......@@ -5203,6 +5192,15 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
52035192 }) orelse return;
52045193 if (last_atom.local_sym_index == self.stub_preamble_sym_index.?) return;
52055194
5195 // Because we insert lazy binding opcodes in reverse order (from last to the first atom),
5196 // we need reverse the order of atom traversal here as well.
5197 // TODO figure out a less error prone mechanims for this!
5198 var atom = last_atom;
5199 while (atom.prev) |prev| {
5200 atom = prev;
5201 }
5202 atom = atom.next.?;
5203
52065204 var stream = std.io.fixedBufferStream(buffer);
52075205 var reader = stream.reader();
52085206 var offsets = std.ArrayList(u32).init(self.base.allocator);
......@@ -5255,7 +5253,6 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
52555253 else => unreachable,
52565254 };
52575255 var buf: [@sizeOf(u32)]u8 = undefined;
5258 var atom = last_atom;
52595256 _ = offsets.pop();
52605257 while (offsets.popOrNull()) |bind_offset| {
52615258 const sym = self.locals.items[atom.local_sym_index];
......@@ -5268,8 +5265,8 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
52685265 });
52695266 try self.base.file.?.pwriteAll(&buf, file_offset);
52705267
5271 if (atom.prev) |prev| {
5272 atom = prev;
5268 if (atom.next) |next| {
5269 atom = next;
52735270 } else break;
52745271 }
52755272}
src/link/MachO/TextBlock.zig+53-7
......@@ -50,6 +50,9 @@ rebases: std.ArrayListUnmanaged(u64) = .{},
5050/// symbols (aka proxies aka imports)
5151bindings: std.ArrayListUnmanaged(SymbolAtOffset) = .{},
5252
53/// List of lazy bindings
54lazy_bindings: std.ArrayListUnmanaged(SymbolAtOffset) = .{},
55
5356/// List of data-in-code entries. This is currently specific to x86_64 only.
5457dices: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
5558
......@@ -570,6 +573,7 @@ pub const empty = TextBlock{
570573
571574pub fn deinit(self: *TextBlock, allocator: *Allocator) void {
572575 self.dices.deinit(allocator);
576 self.lazy_bindings.deinit(allocator);
573577 self.bindings.deinit(allocator);
574578 self.rebases.deinit(allocator);
575579 self.relocs.deinit(allocator);
......@@ -898,9 +902,53 @@ pub fn parseRelocs(self: *TextBlock, relocs: []macho.relocation_info, context: R
898902 if (parsed_rel.where != .undef) break :blk;
899903 if (context.macho_file.stubs_map.contains(parsed_rel.where_index)) break :blk;
900904
901 const stubs_index = @intCast(u32, context.macho_file.stubs.items.len);
902 try context.macho_file.stubs.append(context.allocator, parsed_rel.where_index);
903 try context.macho_file.stubs_map.putNoClobber(context.allocator, parsed_rel.where_index, stubs_index);
905 const stub_helper_atom = try context.macho_file.createStubHelperAtom();
906 const laptr_atom = try context.macho_file.createLazyPointerAtom(
907 stub_helper_atom.local_sym_index,
908 parsed_rel.where_index,
909 );
910 const stub_atom = try context.macho_file.createStubAtom(laptr_atom.local_sym_index);
911 try context.macho_file.stubs_map.putNoClobber(context.allocator, parsed_rel.where_index, stub_atom);
912
913 if (build_options.is_stage1 and context.macho_file.base.options.use_stage1) {
914 try context.macho_file.allocateAtomStage1(stub_helper_atom, .{
915 .seg = context.macho_file.text_segment_cmd_index.?,
916 .sect = context.macho_file.stub_helper_section_index.?,
917 });
918 try context.macho_file.allocateAtomStage1(laptr_atom, .{
919 .seg = context.macho_file.data_segment_cmd_index.?,
920 .sect = context.macho_file.la_symbol_ptr_section_index.?,
921 });
922 try context.macho_file.allocateAtomStage1(stub_atom, .{
923 .seg = context.macho_file.text_segment_cmd_index.?,
924 .sect = context.macho_file.stubs_section_index.?,
925 });
926 } else {
927 {
928 const match = MachO.MatchingSection{
929 .seg = context.macho_file.text_segment_cmd_index.?,
930 .sect = context.macho_file.stub_helper_section_index.?,
931 };
932 _ = try context.macho_file.allocateAtom(stub_helper_atom, match);
933 try context.macho_file.writeAtom(stub_helper_atom, match);
934 }
935 {
936 const match = MachO.MatchingSection{
937 .seg = context.macho_file.data_segment_cmd_index.?,
938 .sect = context.macho_file.la_symbol_ptr_section_index.?,
939 };
940 _ = try context.macho_file.allocateAtom(laptr_atom, match);
941 try context.macho_file.writeAtom(laptr_atom, match);
942 }
943 {
944 const match = MachO.MatchingSection{
945 .seg = context.macho_file.text_segment_cmd_index.?,
946 .sect = context.macho_file.stubs_section_index.?,
947 };
948 _ = try context.macho_file.allocateAtom(stub_atom, match);
949 try context.macho_file.writeAtom(stub_atom, match);
950 }
951 }
904952 }
905953 }
906954}
......@@ -1145,13 +1193,11 @@ pub fn resolveRelocs(self: *TextBlock, macho_file: *MachO) !void {
11451193 break :blk sym.n_value;
11461194 },
11471195 .undef => {
1148 const stubs_index = macho_file.stubs_map.get(rel.where_index) orelse {
1196 const atom = macho_file.stubs_map.get(rel.where_index) orelse {
11491197 // TODO verify in TextBlock that the symbol is indeed dynamically bound.
11501198 break :blk 0; // Dynamically bound by dyld.
11511199 };
1152 const segment = macho_file.load_commands.items[macho_file.text_segment_cmd_index.?].Segment;
1153 const stubs = segment.sections.items[macho_file.stubs_section_index.?];
1154 break :blk stubs.addr + stubs_index * stubs.reserved2;
1200 break :blk macho_file.locals.items[atom.local_sym_index].n_value;
11551201 },
11561202 }
11571203 };
src/link/MachO/bind.zig-9
......@@ -9,15 +9,6 @@ pub const Pointer = struct {
99 name: ?[]const u8 = null,
1010};
1111
12pub fn pointerCmp(context: void, a: Pointer, b: Pointer) bool {
13 _ = context;
14 if (a.segment_id < b.segment_id) return true;
15 if (a.segment_id == b.segment_id) {
16 return a.offset < b.offset;
17 }
18 return false;
19}
20
2112pub fn rebaseInfoSize(pointers: []const Pointer) !u64 {
2213 var stream = std.io.countingWriter(std.io.null_writer);
2314 var writer = stream.writer();