authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-01-09 20:38:55+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-01-13 23:55:18+01:00
log7d40aaad2b514703995458909b315f222543c4cd
tree063c91b13265579d66a15c268e4546209fd79247
parentb86d0e488b9dfc7d943da86a34998360e24da225

macho: document more code + add test case


4 files changed, 71 insertions(+), 62 deletions(-)

src/codegen.zig+10-13
...@@ -1865,19 +1865,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1865,19 +1865,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1865 // If it doesn't, it will get autofreed when we clean up the extern symbol table.1865 // If it doesn't, it will get autofreed when we clean up the extern symbol table.
1866 const decl_name = try std.fmt.allocPrint(self.bin_file.allocator, "_{s}", .{decl.name});1866 const decl_name = try std.fmt.allocPrint(self.bin_file.allocator, "_{s}", .{decl.name});
1867 const already_defined = macho_file.extern_lazy_symbols.contains(decl_name);1867 const already_defined = macho_file.extern_lazy_symbols.contains(decl_name);
1868 const symbol: u32 = blk: {1868 const symbol: u32 = if (macho_file.extern_lazy_symbols.getIndex(decl_name)) |index| blk: {
1869 if (macho_file.extern_lazy_symbols.get(decl_name)) |sym| {1869 self.bin_file.allocator.free(decl_name);
1870 self.bin_file.allocator.free(decl_name);1870 break :blk @intCast(u32, index);
1871 break :blk sym.index;1871 } else blk: {
1872 } else {1872 const index = @intCast(u32, macho_file.extern_lazy_symbols.items().len);
1873 const index = @intCast(u32, macho_file.extern_lazy_symbols.items().len);1873 try macho_file.extern_lazy_symbols.putNoClobber(self.bin_file.allocator, decl_name, .{
1874 try macho_file.extern_lazy_symbols.putNoClobber(self.bin_file.allocator, decl_name, .{1874 .name = decl_name,
1875 .name = decl_name,1875 .dylib_ordinal = 1, // TODO this is now hardcoded, since we only support libSystem.
1876 .dylib_ordinal = 1, // TODO this is now hardcoded, since we only support libSystem.1876 });
1877 .index = index,1877 break :blk index;
1878 });
1879 break :blk index;
1880 }
1881 };1878 };
1882 try macho_file.stub_fixups.append(self.bin_file.allocator, .{1879 try macho_file.stub_fixups.append(self.bin_file.allocator, .{
1883 .symbol = symbol,1880 .symbol = symbol,
src/link/MachO.zig+39-23
...@@ -159,19 +159,29 @@ last_text_block: ?*TextBlock = null,...@@ -159,19 +159,29 @@ last_text_block: ?*TextBlock = null,
159/// prior to calling `generateSymbol`, and then immediately deallocated159/// prior to calling `generateSymbol`, and then immediately deallocated
160/// rather than sitting in the global scope.160/// rather than sitting in the global scope.
161pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{},161pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{},
162162/// A list of all stub (extern decls) fixups required for this run of the linker.
163/// Warning, this is currently NOT thread-safe. See the TODO below.
164/// TODO Move this list inside `updateDecl` where it should be allocated
165/// prior to calling `generateSymbol`, and then immediately deallocated
166/// rather than sitting in the global scope.
163stub_fixups: std.ArrayListUnmanaged(StubFixup) = .{},167stub_fixups: std.ArrayListUnmanaged(StubFixup) = .{},
164168
165pub const StubFixup = struct {169pub const PieFixup = struct {
166 symbol: u32,170 /// Target address we wanted to address in absolute terms.
167 already_defined: bool,171 address: u64,
172 /// Where in the byte stream we should perform the fixup.
168 start: usize,173 start: usize,
174 /// The length of the byte stream. For x86_64, this will be
175 /// variable. For aarch64, it will be fixed at 4 bytes.
169 len: usize,176 len: usize,
170};177};
171178
172pub const PieFixup = struct {179pub const StubFixup = struct {
173 /// Target address we wanted to address in absolute terms.180 /// Id of extern (lazy) symbol.
174 address: u64,181 symbol: u32,
182 /// Signals whether the symbol has already been declared before. If so,
183 /// then there is no need to rewrite the stub entry and related.
184 already_defined: bool,
175 /// Where in the byte stream we should perform the fixup.185 /// Where in the byte stream we should perform the fixup.
176 start: usize,186 start: usize,
177 /// The length of the byte stream. For x86_64, this will be187 /// The length of the byte stream. For x86_64, this will be
...@@ -1030,13 +1040,20 @@ pub fn deinit(self: *MachO) void {...@@ -1030,13 +1040,20 @@ pub fn deinit(self: *MachO) void {
1030 if (self.d_sym) |*ds| {1040 if (self.d_sym) |*ds| {
1031 ds.deinit(self.base.allocator);1041 ds.deinit(self.base.allocator);
1032 }1042 }
1043 for (self.extern_lazy_symbols.items()) |*entry| {
1044 entry.value.deinit(self.base.allocator);
1045 }
1046 self.extern_lazy_symbols.deinit(self.base.allocator);
1047 for (self.extern_nonlazy_symbols.items()) |*entry| {
1048 entry.value.deinit(self.base.allocator);
1049 }
1050 self.extern_nonlazy_symbols.deinit(self.base.allocator);
1033 self.pie_fixups.deinit(self.base.allocator);1051 self.pie_fixups.deinit(self.base.allocator);
1052 self.stub_fixups.deinit(self.base.allocator);
1034 self.text_block_free_list.deinit(self.base.allocator);1053 self.text_block_free_list.deinit(self.base.allocator);
1035 self.offset_table.deinit(self.base.allocator);1054 self.offset_table.deinit(self.base.allocator);
1036 self.offset_table_free_list.deinit(self.base.allocator);1055 self.offset_table_free_list.deinit(self.base.allocator);
1037 self.string_table.deinit(self.base.allocator);1056 self.string_table.deinit(self.base.allocator);
1038 self.extern_lazy_symbols.deinit(self.base.allocator);
1039 self.extern_nonlazy_symbols.deinit(self.base.allocator);
1040 self.global_symbols.deinit(self.base.allocator);1057 self.global_symbols.deinit(self.base.allocator);
1041 self.global_symbol_free_list.deinit(self.base.allocator);1058 self.global_symbol_free_list.deinit(self.base.allocator);
1042 self.local_symbols.deinit(self.base.allocator);1059 self.local_symbols.deinit(self.base.allocator);
...@@ -2047,7 +2064,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -2047,7 +2064,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
2047 try self.extern_nonlazy_symbols.putNoClobber(self.base.allocator, name, .{2064 try self.extern_nonlazy_symbols.putNoClobber(self.base.allocator, name, .{
2048 .name = name,2065 .name = name,
2049 .dylib_ordinal = 1, // TODO this is currently hardcoded.2066 .dylib_ordinal = 1, // TODO this is currently hardcoded.
2050 .index = index,
2051 .segment = self.data_const_segment_cmd_index.?,2067 .segment = self.data_const_segment_cmd_index.?,
2052 .offset = index * @sizeOf(u64),2068 .offset = index * @sizeOf(u64),
2053 });2069 });
...@@ -2582,12 +2598,12 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {...@@ -2582,12 +2598,12 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
2582}2598}
25832599
2584fn writeIndirectSymbolTable(self: *MachO) !void {2600fn writeIndirectSymbolTable(self: *MachO) !void {
2585 const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;2601 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2586 const stubs = &text_seg.sections.items[self.stubs_section_index.?];2602 const stubs = &text_segment.sections.items[self.stubs_section_index.?];
2587 const dc_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;2603 const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2588 const got = &dc_seg.sections.items[self.data_got_section_index.?];2604 const got = &data_const_seg.sections.items[self.data_got_section_index.?];
2589 const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;2605 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2590 const la = &data_seg.sections.items[self.la_symbol_ptr_section_index.?];2606 const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
2591 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;2607 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
2592 dysymtab.nindirectsyms = 0;2608 dysymtab.nindirectsyms = 0;
25932609
...@@ -2595,8 +2611,8 @@ fn writeIndirectSymbolTable(self: *MachO) !void {...@@ -2595,8 +2611,8 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
2595 var off = dysymtab.indirectsymoff;2611 var off = dysymtab.indirectsymoff;
25962612
2597 stubs.reserved1 = 0;2613 stubs.reserved1 = 0;
2598 for (self.extern_lazy_symbols.items()) |entry| {2614 for (self.extern_lazy_symbols.items()) |_, i| {
2599 const symtab_idx = @intCast(u32, dysymtab.iundefsym + entry.value.index);2615 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
2600 mem.writeIntLittle(u32, &buf, symtab_idx);2616 mem.writeIntLittle(u32, &buf, symtab_idx);
2601 try self.base.file.?.pwriteAll(&buf, off);2617 try self.base.file.?.pwriteAll(&buf, off);
2602 off += @sizeOf(u32);2618 off += @sizeOf(u32);
...@@ -2605,17 +2621,17 @@ fn writeIndirectSymbolTable(self: *MachO) !void {...@@ -2605,17 +2621,17 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
26052621
2606 const base_id = @intCast(u32, self.extern_lazy_symbols.items().len);2622 const base_id = @intCast(u32, self.extern_lazy_symbols.items().len);
2607 got.reserved1 = base_id;2623 got.reserved1 = base_id;
2608 for (self.extern_nonlazy_symbols.items()) |entry| {2624 for (self.extern_nonlazy_symbols.items()) |_, i| {
2609 const symtab_idx = @intCast(u32, dysymtab.iundefsym + entry.value.index + base_id);2625 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i + base_id);
2610 mem.writeIntLittle(u32, &buf, symtab_idx);2626 mem.writeIntLittle(u32, &buf, symtab_idx);
2611 try self.base.file.?.pwriteAll(&buf, off);2627 try self.base.file.?.pwriteAll(&buf, off);
2612 off += @sizeOf(u32);2628 off += @sizeOf(u32);
2613 dysymtab.nindirectsyms += 1;2629 dysymtab.nindirectsyms += 1;
2614 }2630 }
26152631
2616 la.reserved1 = got.reserved1 + @intCast(u32, self.extern_nonlazy_symbols.items().len);2632 la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, self.extern_nonlazy_symbols.items().len);
2617 for (self.extern_lazy_symbols.items()) |entry| {2633 for (self.extern_lazy_symbols.items()) |_, i| {
2618 const symtab_idx = @intCast(u32, dysymtab.iundefsym + entry.value.index);2634 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
2619 mem.writeIntLittle(u32, &buf, symtab_idx);2635 mem.writeIntLittle(u32, &buf, symtab_idx);
2620 try self.base.file.?.pwriteAll(&buf, off);2636 try self.base.file.?.pwriteAll(&buf, off);
2621 off += @sizeOf(u32);2637 off += @sizeOf(u32);
src/link/MachO/imports.zig+5-26
...@@ -20,13 +20,15 @@ pub const ExternSymbol = struct {...@@ -20,13 +20,15 @@ pub const ExternSymbol = struct {
20 /// dylibs.20 /// dylibs.
21 dylib_ordinal: i64 = 0,21 dylib_ordinal: i64 = 0,
2222
23 /// Id of the segment where this symbol is defined (will have its address
24 /// resolved).
23 segment: u16 = 0,25 segment: u16 = 0,
26
27 /// Offset relative to the start address of the `segment`.
24 offset: u32 = 0,28 offset: u32 = 0,
25 addend: ?i32 = null,
26 index: u32,
2729
28 pub fn deinit(self: *ExternSymbol, allocator: *Allocator) void {30 pub fn deinit(self: *ExternSymbol, allocator: *Allocator) void {
29 if (self.name) |*name| {31 if (self.name) |name| {
30 allocator.free(name);32 allocator.free(name);
31 }33 }
32 }34 }
...@@ -77,12 +79,6 @@ pub fn bindInfoSize(symbols: []*const ExternSymbol) !u64 {...@@ -77,12 +79,6 @@ pub fn bindInfoSize(symbols: []*const ExternSymbol) !u64 {
7779
78 size += 1;80 size += 1;
79 try leb.writeILEB128(writer, symbol.offset);81 try leb.writeILEB128(writer, symbol.offset);
80
81 if (symbol.addend) |addend| {
82 size += 1;
83 try leb.writeILEB128(writer, addend);
84 }
85
86 size += 2;82 size += 2;
87 }83 }
8884
...@@ -110,12 +106,6 @@ pub fn writeBindInfo(symbols: []*const ExternSymbol, writer: anytype) !void {...@@ -110,12 +106,6 @@ pub fn writeBindInfo(symbols: []*const ExternSymbol, writer: anytype) !void {
110106
111 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));107 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
112 try leb.writeILEB128(writer, symbol.offset);108 try leb.writeILEB128(writer, symbol.offset);
113
114 if (symbol.addend) |addend| {
115 try writer.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB);
116 try leb.writeILEB128(writer, addend);
117 }
118
119 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);109 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
120 try writer.writeByte(macho.BIND_OPCODE_DONE);110 try writer.writeByte(macho.BIND_OPCODE_DONE);
121 }111 }
...@@ -129,12 +119,6 @@ pub fn lazyBindInfoSize(symbols: []*const ExternSymbol) !u64 {...@@ -129,12 +119,6 @@ pub fn lazyBindInfoSize(symbols: []*const ExternSymbol) !u64 {
129 for (symbols) |symbol| {119 for (symbols) |symbol| {
130 size += 1;120 size += 1;
131 try leb.writeILEB128(writer, symbol.offset);121 try leb.writeILEB128(writer, symbol.offset);
132
133 if (symbol.addend) |addend| {
134 size += 1;
135 try leb.writeILEB128(writer, addend);
136 }
137
138 size += 1;122 size += 1;
139 if (symbol.dylib_ordinal > 15) {123 if (symbol.dylib_ordinal > 15) {
140 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));124 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
...@@ -156,11 +140,6 @@ pub fn writeLazyBindInfo(symbols: []*const ExternSymbol, writer: anytype) !void...@@ -156,11 +140,6 @@ pub fn writeLazyBindInfo(symbols: []*const ExternSymbol, writer: anytype) !void
156 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));140 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
157 try leb.writeILEB128(writer, symbol.offset);141 try leb.writeILEB128(writer, symbol.offset);
158142
159 if (symbol.addend) |addend| {
160 try writer.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB);
161 try leb.writeILEB128(writer, addend);
162 }
163
164 if (symbol.dylib_ordinal > 15) {143 if (symbol.dylib_ordinal > 15) {
165 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);144 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
166 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));145 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
test/stage2/aarch64.zig+17
...@@ -199,4 +199,21 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -199,4 +199,21 @@ pub fn addCases(ctx: *TestContext) !void {
199 "",199 "",
200 );200 );
201 }201 }
202
203 {
204 var case = ctx.exe("hello world linked to libc", macos_aarch64);
205
206 // TODO rewrite this test once we handle more int conversions and return args.
207 case.addCompareOutput(
208 \\extern "c" fn write(usize, usize, usize) void;
209 \\extern "c" fn exit(usize) noreturn;
210 \\
211 \\export fn _start() noreturn {
212 \\ write(1, @ptrToInt("Hello, World!\n"), 14);
213 \\ exit(0);
214 \\}
215 ,
216 "Hello, World!\n",
217 );
218 }
202}219}