authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-10-04 19:05:45-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-10-04 19:05:45-04:00
log6aa668e0206da8b9233bfb90a2ffe1f692bf18bd
tree0f3a704908d3ae8c3e182e4aeba5fbeacf065454
parent302a69f127ae8542f49d9cd07c7cc49f3bbd6181
parentc4054f8e0a6a7dea8570507d2a38b38a392ddd0d
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #6476 from kubkon/macho-exe

Link basic MachO executables with stage2

4 files changed, 699 insertions(+), 171 deletions(-)

lib/std/macho.zig+44
...@@ -1257,3 +1257,47 @@ pub const reloc_type_x86_64 = packed enum(u4) {...@@ -1257,3 +1257,47 @@ pub const reloc_type_x86_64 = packed enum(u4) {
1257 /// for thread local variables1257 /// for thread local variables
1258 X86_64_RELOC_TLV,1258 X86_64_RELOC_TLV,
1259};1259};
1260
1261/// This symbol is a reference to an external non-lazy (data) symbol.
1262pub const REFERENCE_FLAG_UNDEFINED_NON_LAZY: u16 = 0x0;
1263
1264/// This symbol is a reference to an external lazy symbol—that is, to a function call.
1265pub const REFERENCE_FLAG_UNDEFINED_LAZY: u16 = 0x1;
1266
1267/// This symbol is defined in this module.
1268pub const REFERENCE_FLAG_DEFINED: u16 = 0x2;
1269
1270/// This symbol is defined in this module and is visible only to modules within this shared library.
1271pub const REFERENCE_FLAG_PRIVATE_DEFINED: u16 = 3;
1272
1273/// This symbol is defined in another module in this file, is a non-lazy (data) symbol, and is visible
1274/// only to modules within this shared library.
1275pub const REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY: u16 = 4;
1276
1277/// This symbol is defined in another module in this file, is a lazy (function) symbol, and is visible
1278/// only to modules within this shared library.
1279pub const REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY: u16 = 5;
1280
1281/// Must be set for any defined symbol that is referenced by dynamic-loader APIs (such as dlsym and
1282/// NSLookupSymbolInImage) and not ordinary undefined symbol references. The strip tool uses this bit
1283/// to avoid removing symbols that must exist: If the symbol has this bit set, strip does not strip it.
1284pub const REFERENCED_DYNAMICALLY: u16 = 0x10;
1285
1286/// Used by the dynamic linker at runtime. Do not set this bit.
1287pub const N_DESC_DISCARDED: u16 = 0x20;
1288
1289/// Indicates that this symbol is a weak reference. If the dynamic linker cannot find a definition
1290/// for this symbol, it sets the address of this symbol to 0. The static linker sets this symbol given
1291/// the appropriate weak-linking flags.
1292pub const N_WEAK_REF: u16 = 0x40;
1293
1294/// Indicates that this symbol is a weak definition. If the static linker or the dynamic linker finds
1295/// another (non-weak) definition for this symbol, the weak definition is ignored. Only symbols in a
1296/// coalesced section (page 23) can be marked as a weak definition.
1297pub const N_WEAK_DEF: u16 = 0x80;
1298
1299/// The N_SYMBOL_RESOLVER bit of the n_desc field indicates that the
1300/// that the function is actually a resolver function and should
1301/// be called to get the address of the real function to use.
1302/// This bit is only available in .o files (MH_OBJECT filetype)
1303pub const N_SYMBOL_RESOLVER: u16 = 0x100;
src/codegen.zig+8-7
...@@ -1532,12 +1532,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1532,12 +1532,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1532 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {1532 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
1533 const func = func_val.func;1533 const func = func_val.func;
1534 const got = &macho_file.sections.items[macho_file.got_section_index.?];1534 const got = &macho_file.sections.items[macho_file.got_section_index.?];
1535 const ptr_bytes = 8;1535 const got_addr = got.addr + func.owner_decl.link.macho.offset_table_index * @sizeOf(u64);
1536 const got_addr = @intCast(u32, got.addr + func.owner_decl.link.macho.offset_table_index.? * ptr_bytes);1536 // Here, we store the got address in %rax, and then call %rax
1537 // ff 14 25 xx xx xx xx call [addr]1537 // movabsq [addr], %rax
1538 try self.code.ensureCapacity(self.code.items.len + 7);1538 try self.genSetReg(inst.base.src, .rax, .{ .memory = got_addr });
1539 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });1539 // callq *%rax
1540 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), got_addr);1540 try self.code.ensureCapacity(self.code.items.len + 2);
1541 self.code.appendSliceAssumeCapacity(&[2]u8{ 0xff, 0xd0 });
1541 } else {1542 } else {
1542 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});1543 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
1543 }1544 }
...@@ -2590,7 +2591,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2590,7 +2591,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2590 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {2591 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
2591 const decl = payload.decl;2592 const decl = payload.decl;
2592 const got = &macho_file.sections.items[macho_file.got_section_index.?];2593 const got = &macho_file.sections.items[macho_file.got_section_index.?];
2593 const got_addr = got.addr + decl.link.macho.offset_table_index.? * ptr_bytes;2594 const got_addr = got.addr + decl.link.macho.offset_table_index * ptr_bytes;
2594 return MCValue{ .memory = got_addr };2595 return MCValue{ .memory = got_addr };
2595 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {2596 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
2596 const decl = payload.decl;2597 const decl = payload.decl;
src/link/MachO.zig+608-164
...@@ -27,6 +27,10 @@ const LoadCommand = union(enum) {...@@ -27,6 +27,10 @@ const LoadCommand = union(enum) {
27 LinkeditData: macho.linkedit_data_command,27 LinkeditData: macho.linkedit_data_command,
28 Symtab: macho.symtab_command,28 Symtab: macho.symtab_command,
29 Dysymtab: macho.dysymtab_command,29 Dysymtab: macho.dysymtab_command,
30 DyldInfo: macho.dyld_info_command,
31 Dylinker: macho.dylinker_command,
32 Dylib: macho.dylib_command,
33 EntryPoint: macho.entry_point_command,
3034
31 pub fn cmdsize(self: LoadCommand) u32 {35 pub fn cmdsize(self: LoadCommand) u32 {
32 return switch (self) {36 return switch (self) {
...@@ -34,6 +38,10 @@ const LoadCommand = union(enum) {...@@ -34,6 +38,10 @@ const LoadCommand = union(enum) {
34 .LinkeditData => |x| x.cmdsize,38 .LinkeditData => |x| x.cmdsize,
35 .Symtab => |x| x.cmdsize,39 .Symtab => |x| x.cmdsize,
36 .Dysymtab => |x| x.cmdsize,40 .Dysymtab => |x| x.cmdsize,
41 .DyldInfo => |x| x.cmdsize,
42 .Dylinker => |x| x.cmdsize,
43 .Dylib => |x| x.cmdsize,
44 .EntryPoint => |x| x.cmdsize,
37 };45 };
38 }46 }
3947
...@@ -43,6 +51,10 @@ const LoadCommand = union(enum) {...@@ -43,6 +51,10 @@ const LoadCommand = union(enum) {
43 .LinkeditData => |cmd| writeGeneric(cmd, file, offset),51 .LinkeditData => |cmd| writeGeneric(cmd, file, offset),
44 .Symtab => |cmd| writeGeneric(cmd, file, offset),52 .Symtab => |cmd| writeGeneric(cmd, file, offset),
45 .Dysymtab => |cmd| writeGeneric(cmd, file, offset),53 .Dysymtab => |cmd| writeGeneric(cmd, file, offset),
54 .DyldInfo => |cmd| writeGeneric(cmd, file, offset),
55 .Dylinker => |cmd| writeGeneric(cmd, file, offset),
56 .Dylib => |cmd| writeGeneric(cmd, file, offset),
57 .EntryPoint => |cmd| writeGeneric(cmd, file, offset),
46 };58 };
47 }59 }
4860
...@@ -56,30 +68,52 @@ base: File,...@@ -56,30 +68,52 @@ base: File,
5668
57/// Table of all load commands69/// Table of all load commands
58load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},70load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
59segment_cmd_index: ?u16 = null,71/// __PAGEZERO segment
72pagezero_segment_cmd_index: ?u16 = null,
73/// __TEXT segment
74text_segment_cmd_index: ?u16 = null,
75/// __DATA segment
76data_segment_cmd_index: ?u16 = null,
77/// __LINKEDIT segment
78linkedit_segment_cmd_index: ?u16 = null,
79/// Dyld info
80dyld_info_cmd_index: ?u16 = null,
81/// Symbol table
60symtab_cmd_index: ?u16 = null,82symtab_cmd_index: ?u16 = null,
83/// Dynamic symbol table
61dysymtab_cmd_index: ?u16 = null,84dysymtab_cmd_index: ?u16 = null,
85/// Path to dyld linker
86dylinker_cmd_index: ?u16 = null,
87/// Path to libSystem
88libsystem_cmd_index: ?u16 = null,
89/// Data-in-code section of __LINKEDIT segment
62data_in_code_cmd_index: ?u16 = null,90data_in_code_cmd_index: ?u16 = null,
91/// Address to entry point function
92function_starts_cmd_index: ?u16 = null,
93/// Main/entry point
94/// Specifies offset wrt __TEXT segment start address to the main entry point
95/// of the binary.
96main_cmd_index: ?u16 = null,
6397
64/// Table of all sections98/// Table of all sections
65sections: std.ArrayListUnmanaged(macho.section_64) = .{},99sections: std.ArrayListUnmanaged(macho.section_64) = .{},
66100
67/// __TEXT segment sections101/// __TEXT,__text section
68text_section_index: ?u16 = null,102text_section_index: ?u16 = null,
69cstring_section_index: ?u16 = null,
70const_text_section_index: ?u16 = null,
71stubs_section_index: ?u16 = null,
72stub_helper_section_index: ?u16 = null,
73103
74/// __DATA segment sections104/// __DATA,__got section
75got_section_index: ?u16 = null,105got_section_index: ?u16 = null,
76const_data_section_index: ?u16 = null,
77106
78entry_addr: ?u64 = null,107entry_addr: ?u64 = null,
79108
80/// Table of all symbols used.109/// Table of all local symbols
81/// Internally references string table for names (which are optional).110/// Internally references string table for names (which are optional).
82symbol_table: std.ArrayListUnmanaged(macho.nlist_64) = .{},111local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
112/// Table of all defined global symbols
113global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
114/// Table of all undefined symbols
115undef_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
116dyld_stub_binder_index: ?u16 = null,
83117
84/// Table of symbol names aka the string table.118/// Table of symbol names aka the string table.
85string_table: std.ArrayListUnmanaged(u8) = .{},119string_table: std.ArrayListUnmanaged(u8) = .{},
...@@ -115,19 +149,27 @@ const LIB_SYSTEM_NAME: [*:0]const u8 = "System";...@@ -115,19 +149,27 @@ const LIB_SYSTEM_NAME: [*:0]const u8 = "System";
115const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.dylib";149const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.dylib";
116150
117pub const TextBlock = struct {151pub const TextBlock = struct {
118 /// Index into the symbol table152 /// Each decl always gets a local symbol with the fully qualified name.
119 symbol_table_index: ?u32,153 /// The vaddr and size are found here directly.
154 /// The file offset is found by computing the vaddr offset from the section vaddr
155 /// the symbol references, and adding that to the file offset of the section.
156 /// If this field is 0, it means the codegen size = 0 and there is no symbol or
157 /// offset table entry.
158 local_sym_index: u32,
120 /// Index into offset table159 /// Index into offset table
121 offset_table_index: ?u32,160 /// This field is undefined for symbols with size = 0.
161 offset_table_index: u32,
122 /// Size of this text block162 /// Size of this text block
163 /// Unlike in Elf, we need to store the size of this symbol as part of
164 /// the TextBlock since macho.nlist_64 lacks this information.
123 size: u64,165 size: u64,
124 /// Points to the previous and next neighbours166 /// Points to the previous and next neighbours
125 prev: ?*TextBlock,167 prev: ?*TextBlock,
126 next: ?*TextBlock,168 next: ?*TextBlock,
127169
128 pub const empty = TextBlock{170 pub const empty = TextBlock{
129 .symbol_table_index = null,171 .local_sym_index = 0,
130 .offset_table_index = null,172 .offset_table_index = undefined,
131 .size = 0,173 .size = 0,
132 .prev = null,174 .prev = null,
133 .next = null,175 .next = null,
...@@ -156,6 +198,15 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -156,6 +198,15 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
156198
157 self.base.file = file;199 self.base.file = file;
158200
201 // Index 0 is always a null symbol.
202 try self.local_symbols.append(allocator, .{
203 .n_strx = 0,
204 .n_type = 0,
205 .n_sect = 0,
206 .n_desc = 0,
207 .n_value = 0,
208 });
209
159 switch (options.output_mode) {210 switch (options.output_mode) {
160 .Exe => {},211 .Exe => {},
161 .Obj => {},212 .Obj => {},
...@@ -196,88 +247,83 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {...@@ -196,88 +247,83 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
196 const tracy = trace(@src());247 const tracy = trace(@src());
197 defer tracy.end();248 defer tracy.end();
198249
250 // Unfortunately these have to be buffered and done at the end because MachO does not allow
251 // mixing local, global and undefined symbols within a symbol table.
252 try self.writeAllGlobalSymbols();
253 try self.writeAllUndefSymbols();
254
255 try self.writeStringTable();
256
199 switch (self.base.options.output_mode) {257 switch (self.base.options.output_mode) {
200 .Exe => {258 .Exe => {
201 var last_cmd_offset: usize = @sizeOf(macho.mach_header_64);259 if (self.entry_addr) |addr| {
202 {260 // Write export trie.
203 // Specify path to dynamic linker dyld261 try self.writeExportTrie();
204 const cmdsize = commandSize(@sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH));262
205 const load_dylinker = [1]macho.dylinker_command{263 // Update LC_MAIN with entry offset
206 .{264 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
207 .cmd = macho.LC_LOAD_DYLINKER,265 const main_cmd = &self.load_commands.items[self.main_cmd_index.?].EntryPoint;
208 .cmdsize = cmdsize,266 main_cmd.entryoff = addr - text_segment.vmaddr;
209 .name = @sizeOf(macho.dylinker_command),
210 },
211 };
212
213 try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylinker[0..1]), last_cmd_offset);
214
215 const file_offset = last_cmd_offset + @sizeOf(macho.dylinker_command);
216 try self.addPadding(cmdsize - @sizeOf(macho.dylinker_command), file_offset);
217
218 try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), file_offset);
219 last_cmd_offset += cmdsize;
220 }267 }
221268
222 {269 {
223 // Link against libSystem270 // Update dynamic symbol table.
224 const cmdsize = commandSize(@sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH));271 const nlocals = @intCast(u32, self.local_symbols.items.len);
225 // TODO Find a way to work out runtime version from the OS version triple stored in std.Target.272 const nglobals = @intCast(u32, self.global_symbols.items.len);
226 // In the meantime, we're gonna hardcode to the minimum compatibility version of 1.0.0.273 const nundefs = @intCast(u32, self.undef_symbols.items.len);
227 const min_version = 0x10000;274 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
228 const dylib = .{275 dysymtab.nlocalsym = nlocals;
229 .name = @sizeOf(macho.dylib_command),276 dysymtab.iextdefsym = nlocals;
230 .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files277 dysymtab.nextdefsym = nglobals;
231 .current_version = min_version,278 dysymtab.iundefsym = nlocals + nglobals;
232 .compatibility_version = min_version,279 dysymtab.nundefsym = nundefs;
233 };
234 const load_dylib = [1]macho.dylib_command{
235 .{
236 .cmd = macho.LC_LOAD_DYLIB,
237 .cmdsize = cmdsize,
238 .dylib = dylib,
239 },
240 };
241
242 try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylib[0..1]), last_cmd_offset);
243
244 const file_offset = last_cmd_offset + @sizeOf(macho.dylib_command);
245 try self.addPadding(cmdsize - @sizeOf(macho.dylib_command), file_offset);
246
247 try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), file_offset);
248 last_cmd_offset += cmdsize;
249 }280 }
250 },
251 .Obj => {
252 {281 {
253 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;282 // Write path to dyld loader.
254 symtab.nsyms = @intCast(u32, self.symbol_table.items.len);283 var off: usize = @sizeOf(macho.mach_header_64);
255 const allocated_size = self.allocatedSize(symtab.stroff);284 for (self.load_commands.items) |cmd| {
256 const needed_size = self.string_table.items.len;285 if (cmd == .Dylinker) break;
257 log.debug("allocated_size = 0x{x}, needed_size = 0x{x}\n", .{ allocated_size, needed_size });286 off += cmd.cmdsize();
258
259 if (needed_size > allocated_size) {
260 symtab.strsize = 0;
261 symtab.stroff = @intCast(u32, self.findFreeSpace(needed_size, 1));
262 }287 }
263 symtab.strsize = @intCast(u32, needed_size);288 const cmd = &self.load_commands.items[self.dylinker_cmd_index.?].Dylinker;
264289 off += cmd.name;
265 log.debug("writing string table from 0x{x} to 0x{x}\n", .{ symtab.stroff, symtab.stroff + symtab.strsize });290 const padding = cmd.cmdsize - @sizeOf(macho.dylinker_command);
266291 log.debug("writing LC_LOAD_DYLINKER padding of size {} at 0x{x}\n", .{ padding, off });
267 try self.base.file.?.pwriteAll(self.string_table.items, symtab.stroff);292 try self.addPadding(padding, off);
293 log.debug("writing LC_LOAD_DYLINKER path to dyld at 0x{x}\n", .{off});
294 try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), off);
268 }295 }
269296 {
270 var last_cmd_offset: usize = @sizeOf(macho.mach_header_64);297 // Write path to libSystem.
271 for (self.load_commands.items) |cmd| {298 var off: usize = @sizeOf(macho.mach_header_64);
272 try cmd.write(&self.base.file.?, last_cmd_offset);299 for (self.load_commands.items) |cmd| {
273 last_cmd_offset += cmd.cmdsize();300 if (cmd == .Dylib) break;
301 off += cmd.cmdsize();
302 }
303 const cmd = &self.load_commands.items[self.libsystem_cmd_index.?].Dylib;
304 off += cmd.dylib.name;
305 const padding = cmd.cmdsize - @sizeOf(macho.dylib_command);
306 log.debug("writing LC_LOAD_DYLIB padding of size {} at 0x{x}\n", .{ padding, off });
307 try self.addPadding(padding, off);
308 log.debug("writing LC_LOAD_DYLIB path to libSystem at 0x{x}\n", .{off});
309 try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), off);
274 }310 }
275 const off = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64);
276 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items), off);
277 },311 },
312 .Obj => {},
278 .Lib => return error.TODOImplementWritingLibFiles,313 .Lib => return error.TODOImplementWritingLibFiles,
279 }314 }
280315
316 if (self.cmd_table_dirty) try self.writeCmdHeaders();
317
318 {
319 // Update symbol table.
320 const nlocals = @intCast(u32, self.local_symbols.items.len);
321 const nglobals = @intCast(u32, self.global_symbols.items.len);
322 const nundefs = @intCast(u32, self.undef_symbols.items.len);
323 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
324 symtab.nsyms = nlocals + nglobals + nundefs;
325 }
326
281 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {327 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
282 log.debug("flushing. no_entry_point_found = true\n", .{});328 log.debug("flushing. no_entry_point_found = true\n", .{});
283 self.error_flags.no_entry_point_found = true;329 self.error_flags.no_entry_point_found = true;
...@@ -669,32 +715,34 @@ fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 {...@@ -669,32 +715,34 @@ fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 {
669pub fn deinit(self: *MachO) void {715pub fn deinit(self: *MachO) void {
670 self.offset_table.deinit(self.base.allocator);716 self.offset_table.deinit(self.base.allocator);
671 self.string_table.deinit(self.base.allocator);717 self.string_table.deinit(self.base.allocator);
672 self.symbol_table.deinit(self.base.allocator);718 self.undef_symbols.deinit(self.base.allocator);
719 self.global_symbols.deinit(self.base.allocator);
720 self.local_symbols.deinit(self.base.allocator);
673 self.sections.deinit(self.base.allocator);721 self.sections.deinit(self.base.allocator);
674 self.load_commands.deinit(self.base.allocator);722 self.load_commands.deinit(self.base.allocator);
675}723}
676724
677pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {725pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
678 if (decl.link.macho.symbol_table_index) |_| return;726 if (decl.link.macho.local_sym_index != 0) return;
679727
680 try self.symbol_table.ensureCapacity(self.base.allocator, self.symbol_table.items.len + 1);728 try self.local_symbols.ensureCapacity(self.base.allocator, self.local_symbols.items.len + 1);
681 try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);729 try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
682730
683 log.debug("allocating symbol index {} for {}\n", .{ self.symbol_table.items.len, decl.name });731 log.debug("allocating symbol index {} for {}\n", .{ self.local_symbols.items.len, decl.name });
684 decl.link.macho.symbol_table_index = @intCast(u32, self.symbol_table.items.len);732 decl.link.macho.local_sym_index = @intCast(u32, self.local_symbols.items.len);
685 _ = self.symbol_table.addOneAssumeCapacity();733 _ = self.local_symbols.addOneAssumeCapacity();
686734
687 decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len);735 decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len);
688 _ = self.offset_table.addOneAssumeCapacity();736 _ = self.offset_table.addOneAssumeCapacity();
689737
690 self.symbol_table.items[decl.link.macho.symbol_table_index.?] = .{738 self.local_symbols.items[decl.link.macho.local_sym_index] = .{
691 .n_strx = 0,739 .n_strx = 0,
692 .n_type = 0,740 .n_type = 0,
693 .n_sect = 0,741 .n_sect = 0,
694 .n_desc = 0,742 .n_desc = 0,
695 .n_value = 0,743 .n_value = 0,
696 };744 };
697 self.offset_table.items[decl.link.macho.offset_table_index.?] = 0;745 self.offset_table.items[decl.link.macho.offset_table_index] = 0;
698}746}
699747
700pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {748pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
...@@ -716,16 +764,14 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -716,16 +764,14 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
716 return;764 return;
717 },765 },
718 };766 };
719 log.debug("generated code {}\n", .{code});
720767
721 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);768 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
722 const symbol = &self.symbol_table.items[decl.link.macho.symbol_table_index.?];769 const symbol = &self.local_symbols.items[decl.link.macho.local_sym_index];
723770
724 const decl_name = mem.spanZ(decl.name);771 const decl_name = mem.spanZ(decl.name);
725 const name_str_index = try self.makeString(decl_name);772 const name_str_index = try self.makeString(decl_name);
726 const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment);773 const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment);
727 log.debug("allocated text block for {} at 0x{x}\n", .{ decl_name, addr });774 log.debug("allocated text block for {} at 0x{x}\n", .{ decl_name, addr });
728 log.debug("updated text section {}\n", .{self.sections.items[self.text_section_index.?]});
729775
730 symbol.* = .{776 symbol.* = .{
731 .n_strx = name_str_index,777 .n_strx = name_str_index,
...@@ -734,18 +780,20 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -734,18 +780,20 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
734 .n_desc = 0,780 .n_desc = 0,
735 .n_value = addr,781 .n_value = addr,
736 };782 };
783 self.offset_table.items[decl.link.macho.offset_table_index] = addr;
737784
738 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.785 try self.writeSymbol(decl.link.macho.local_sym_index);
739 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};786 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
740 try self.updateDeclExports(module, decl, decl_exports);
741 try self.writeSymbol(decl.link.macho.symbol_table_index.?);
742787
743 const text_section = self.sections.items[self.text_section_index.?];788 const text_section = self.sections.items[self.text_section_index.?];
744 const section_offset = symbol.n_value - text_section.addr;789 const section_offset = symbol.n_value - text_section.addr;
745 const file_offset = text_section.offset + section_offset;790 const file_offset = text_section.offset + section_offset;
746 log.debug("file_offset 0x{x}\n", .{file_offset});
747791
748 try self.base.file.?.pwriteAll(code, file_offset);792 try self.base.file.?.pwriteAll(code, file_offset);
793
794 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
795 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
796 try self.updateDeclExports(module, decl, decl_exports);
749}797}
750798
751pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {}799pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {}
...@@ -759,34 +807,89 @@ pub fn updateDeclExports(...@@ -759,34 +807,89 @@ pub fn updateDeclExports(
759 const tracy = trace(@src());807 const tracy = trace(@src());
760 defer tracy.end();808 defer tracy.end();
761809
762 if (decl.link.macho.symbol_table_index == null) return;810 try self.global_symbols.ensureCapacity(self.base.allocator, self.global_symbols.items.len + exports.len);
763811 if (decl.link.macho.local_sym_index == 0) return;
764 const decl_sym = &self.symbol_table.items[decl.link.macho.symbol_table_index.?];812 const decl_sym = &self.local_symbols.items[decl.link.macho.local_sym_index];
765 // TODO implement813
766 if (exports.len == 0) return;814 for (exports) |exp| {
767815 if (exp.options.section) |section_name| {
768 const exp = exports[0];816 if (!mem.eql(u8, section_name, "__text")) {
769 self.entry_addr = decl_sym.n_value;817 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
770 decl_sym.n_type |= macho.N_EXT;818 module.failed_exports.putAssumeCapacityNoClobber(
771 exp.link.sym_index = 0;819 exp,
820 try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
821 );
822 continue;
823 }
824 }
825 const n_desc = switch (exp.options.linkage) {
826 .Internal => macho.REFERENCE_FLAG_PRIVATE_DEFINED,
827 .Strong => blk: {
828 if (mem.eql(u8, exp.options.name, "_start")) {
829 self.entry_addr = decl_sym.n_value;
830 }
831 break :blk macho.REFERENCE_FLAG_DEFINED;
832 },
833 .Weak => macho.N_WEAK_REF,
834 .LinkOnce => {
835 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
836 module.failed_exports.putAssumeCapacityNoClobber(
837 exp,
838 try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),
839 );
840 continue;
841 },
842 };
843 const n_type = decl_sym.n_type | macho.N_EXT;
844 if (exp.link.sym_index) |i| {
845 const sym = &self.global_symbols.items[i];
846 sym.* = .{
847 .n_strx = try self.updateString(sym.n_strx, exp.options.name),
848 .n_type = n_type,
849 .n_sect = @intCast(u8, self.text_section_index.?) + 1,
850 .n_desc = n_desc,
851 .n_value = decl_sym.n_value,
852 };
853 } else {
854 const name_str_index = try self.makeString(exp.options.name);
855 _ = self.global_symbols.addOneAssumeCapacity();
856 const i = self.global_symbols.items.len - 1;
857 self.global_symbols.items[i] = .{
858 .n_strx = name_str_index,
859 .n_type = n_type,
860 .n_sect = @intCast(u8, self.text_section_index.?) + 1,
861 .n_desc = n_desc,
862 .n_value = decl_sym.n_value,
863 };
864
865 exp.link.sym_index = @intCast(u32, i);
866 }
867 }
772}868}
773869
774pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {}870pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {}
775871
776pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {872pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {
777 return self.symbol_table.items[decl.link.macho.symbol_table_index.?].n_value;873 assert(decl.link.macho.local_sym_index != 0);
874 return self.local_symbols.items[decl.link.macho.local_sym_index].n_value;
778}875}
779876
780pub fn populateMissingMetadata(self: *MachO) !void {877pub fn populateMissingMetadata(self: *MachO) !void {
781 if (self.segment_cmd_index == null) {878 switch (self.base.options.output_mode) {
782 self.segment_cmd_index = @intCast(u16, self.load_commands.items.len);879 .Exe => {},
880 .Obj => return error.TODOImplementWritingObjFiles,
881 .Lib => return error.TODOImplementWritingLibFiles,
882 }
883
884 if (self.pagezero_segment_cmd_index == null) {
885 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
783 try self.load_commands.append(self.base.allocator, .{886 try self.load_commands.append(self.base.allocator, .{
784 .Segment = .{887 .Segment = .{
785 .cmd = macho.LC_SEGMENT_64,888 .cmd = macho.LC_SEGMENT_64,
786 .cmdsize = @sizeOf(macho.segment_command_64),889 .cmdsize = @sizeOf(macho.segment_command_64),
787 .segname = makeStaticString(""),890 .segname = makeStaticString("__PAGEZERO"),
788 .vmaddr = 0,891 .vmaddr = 0,
789 .vmsize = 0,892 .vmsize = 0x100000000, // size always set to 4GB
790 .fileoff = 0,893 .fileoff = 0,
791 .filesize = 0,894 .filesize = 0,
792 .maxprot = 0,895 .maxprot = 0,
...@@ -797,28 +900,34 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -797,28 +900,34 @@ pub fn populateMissingMetadata(self: *MachO) !void {
797 });900 });
798 self.cmd_table_dirty = true;901 self.cmd_table_dirty = true;
799 }902 }
800 if (self.symtab_cmd_index == null) {903 if (self.text_segment_cmd_index == null) {
801 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);904 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
905 const prot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE;
802 try self.load_commands.append(self.base.allocator, .{906 try self.load_commands.append(self.base.allocator, .{
803 .Symtab = .{907 .Segment = .{
804 .cmd = macho.LC_SYMTAB,908 .cmd = macho.LC_SEGMENT_64,
805 .cmdsize = @sizeOf(macho.symtab_command),909 .cmdsize = @sizeOf(macho.segment_command_64),
806 .symoff = 0,910 .segname = makeStaticString("__TEXT"),
807 .nsyms = 0,911 .vmaddr = 0x100000000, // always starts at 4GB
808 .stroff = 0,912 .vmsize = 0,
809 .strsize = 0,913 .fileoff = 0,
914 .filesize = 0,
915 .maxprot = prot,
916 .initprot = prot,
917 .nsects = 0,
918 .flags = 0,
810 },919 },
811 });920 });
812 self.cmd_table_dirty = true;921 self.cmd_table_dirty = true;
813 }922 }
814 if (self.text_section_index == null) {923 if (self.text_section_index == null) {
815 self.text_section_index = @intCast(u16, self.sections.items.len);924 self.text_section_index = @intCast(u16, self.sections.items.len);
816 const segment = &self.load_commands.items[self.segment_cmd_index.?].Segment;925 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
817 segment.cmdsize += @sizeOf(macho.section_64);926 text_segment.cmdsize += @sizeOf(macho.section_64);
818 segment.nsects += 1;927 text_segment.nsects += 1;
819928
820 const file_size = self.base.options.program_code_size_hint;929 const file_size = mem.alignForwardGeneric(u64, self.base.options.program_code_size_hint, 0x1000);
821 const off = @intCast(u32, self.findFreeSpace(file_size, 1));930 const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000)); // TODO maybe findFreeSpace should return u32 directly?
822 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;931 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
823932
824 log.debug("found __text section free space 0x{x} to 0x{x}\n", .{ off, off + file_size });933 log.debug("found __text section free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
...@@ -826,10 +935,10 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -826,10 +935,10 @@ pub fn populateMissingMetadata(self: *MachO) !void {
826 try self.sections.append(self.base.allocator, .{935 try self.sections.append(self.base.allocator, .{
827 .sectname = makeStaticString("__text"),936 .sectname = makeStaticString("__text"),
828 .segname = makeStaticString("__TEXT"),937 .segname = makeStaticString("__TEXT"),
829 .addr = 0,938 .addr = text_segment.vmaddr + off,
830 .size = file_size,939 .size = file_size,
831 .offset = off,940 .offset = off,
832 .@"align" = 0x1000,941 .@"align" = 12, // 2^12 = 4096
833 .reloff = 0,942 .reloff = 0,
834 .nreloc = 0,943 .nreloc = 0,
835 .flags = flags,944 .flags = flags,
...@@ -838,43 +947,256 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -838,43 +947,256 @@ pub fn populateMissingMetadata(self: *MachO) !void {
838 .reserved3 = 0,947 .reserved3 = 0,
839 });948 });
840949
841 segment.vmsize += file_size;950 text_segment.vmsize = file_size + off; // We add off here since __TEXT segment includes everything prior to __text section.
842 segment.filesize += file_size;951 text_segment.filesize = file_size + off;
843 segment.fileoff = off;952 }
953 if (self.data_segment_cmd_index == null) {
954 self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
955 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
956 const prot = macho.VM_PROT_READ | macho.VM_PROT_WRITE;
957 try self.load_commands.append(self.base.allocator, .{
958 .Segment = .{
959 .cmd = macho.LC_SEGMENT_64,
960 .cmdsize = @sizeOf(macho.segment_command_64),
961 .segname = makeStaticString("__DATA"),
962 .vmaddr = text_segment.vmaddr + text_segment.vmsize,
963 .vmsize = 0,
964 .fileoff = 0,
965 .filesize = 0,
966 .maxprot = prot,
967 .initprot = prot,
968 .nsects = 0,
969 .flags = 0,
970 },
971 });
972 self.cmd_table_dirty = true;
973 }
974 if (self.got_section_index == null) {
975 self.got_section_index = @intCast(u16, self.sections.items.len);
976 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
977 data_segment.cmdsize += @sizeOf(macho.section_64);
978 data_segment.nsects += 1;
979
980 const file_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
981 const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000));
982
983 log.debug("found __got section free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
984
985 try self.sections.append(self.base.allocator, .{
986 .sectname = makeStaticString("__got"),
987 .segname = makeStaticString("__DATA"),
988 .addr = data_segment.vmaddr,
989 .size = file_size,
990 .offset = off,
991 .@"align" = 3, // 2^3 = 8
992 .reloff = 0,
993 .nreloc = 0,
994 .flags = macho.S_REGULAR,
995 .reserved1 = 0,
996 .reserved2 = 0,
997 .reserved3 = 0,
998 });
844999
845 log.debug("initial text section {}\n", .{self.sections.items[self.text_section_index.?]});1000 const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000);
1001 data_segment.vmsize = segment_size;
1002 data_segment.filesize = segment_size;
1003 data_segment.fileoff = off;
1004 }
1005 if (self.linkedit_segment_cmd_index == null) {
1006 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
1007 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
1008 const prot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE;
1009 try self.load_commands.append(self.base.allocator, .{
1010 .Segment = .{
1011 .cmd = macho.LC_SEGMENT_64,
1012 .cmdsize = @sizeOf(macho.segment_command_64),
1013 .segname = makeStaticString("__LINKEDIT"),
1014 .vmaddr = data_segment.vmaddr + data_segment.vmsize,
1015 .vmsize = 0,
1016 .fileoff = 0,
1017 .filesize = 0,
1018 .maxprot = prot,
1019 .initprot = prot,
1020 .nsects = 0,
1021 .flags = 0,
1022 },
1023 });
1024 self.cmd_table_dirty = true;
1025 }
1026 if (self.dyld_info_cmd_index == null) {
1027 self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len);
1028 try self.load_commands.append(self.base.allocator, .{
1029 .DyldInfo = .{
1030 .cmd = macho.LC_DYLD_INFO_ONLY,
1031 .cmdsize = @sizeOf(macho.dyld_info_command),
1032 .rebase_off = 0,
1033 .rebase_size = 0,
1034 .bind_off = 0,
1035 .bind_size = 0,
1036 .weak_bind_off = 0,
1037 .weak_bind_size = 0,
1038 .lazy_bind_off = 0,
1039 .lazy_bind_size = 0,
1040 .export_off = 0,
1041 .export_size = 0,
1042 },
1043 });
1044 self.cmd_table_dirty = true;
1045 }
1046 if (self.symtab_cmd_index == null) {
1047 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
1048 try self.load_commands.append(self.base.allocator, .{
1049 .Symtab = .{
1050 .cmd = macho.LC_SYMTAB,
1051 .cmdsize = @sizeOf(macho.symtab_command),
1052 .symoff = 0,
1053 .nsyms = 0,
1054 .stroff = 0,
1055 .strsize = 0,
1056 },
1057 });
1058 self.cmd_table_dirty = true;
1059 }
1060 if (self.dysymtab_cmd_index == null) {
1061 self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len);
1062 try self.load_commands.append(self.base.allocator, .{
1063 .Dysymtab = .{
1064 .cmd = macho.LC_DYSYMTAB,
1065 .cmdsize = @sizeOf(macho.dysymtab_command),
1066 .ilocalsym = 0,
1067 .nlocalsym = 0,
1068 .iextdefsym = 0,
1069 .nextdefsym = 0,
1070 .iundefsym = 0,
1071 .nundefsym = 0,
1072 .tocoff = 0,
1073 .ntoc = 0,
1074 .modtaboff = 0,
1075 .nmodtab = 0,
1076 .extrefsymoff = 0,
1077 .nextrefsyms = 0,
1078 .indirectsymoff = 0,
1079 .nindirectsyms = 0,
1080 .extreloff = 0,
1081 .nextrel = 0,
1082 .locreloff = 0,
1083 .nlocrel = 0,
1084 },
1085 });
1086 self.cmd_table_dirty = true;
1087 }
1088 if (self.dylinker_cmd_index == null) {
1089 self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len);
1090 const cmdsize = mem.alignForwardGeneric(u64, @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH), @sizeOf(u64));
1091 try self.load_commands.append(self.base.allocator, .{
1092 .Dylinker = .{
1093 .cmd = macho.LC_LOAD_DYLINKER,
1094 .cmdsize = @intCast(u32, cmdsize),
1095 .name = @sizeOf(macho.dylinker_command),
1096 },
1097 });
1098 self.cmd_table_dirty = true;
1099 }
1100 if (self.libsystem_cmd_index == null) {
1101 self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len);
1102 const cmdsize = mem.alignForwardGeneric(u64, @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH), @sizeOf(u64));
1103 // TODO Find a way to work out runtime version from the OS version triple stored in std.Target.
1104 // In the meantime, we're gonna hardcode to the minimum compatibility version of 1.0.0.
1105 const min_version = 0x10000;
1106 const dylib = .{
1107 .name = @sizeOf(macho.dylib_command),
1108 .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files
1109 .current_version = min_version,
1110 .compatibility_version = min_version,
1111 };
1112 try self.load_commands.append(self.base.allocator, .{
1113 .Dylib = .{
1114 .cmd = macho.LC_LOAD_DYLIB,
1115 .cmdsize = @intCast(u32, cmdsize),
1116 .dylib = dylib,
1117 },
1118 });
1119 self.cmd_table_dirty = true;
1120 }
1121 if (self.main_cmd_index == null) {
1122 self.main_cmd_index = @intCast(u16, self.load_commands.items.len);
1123 try self.load_commands.append(self.base.allocator, .{
1124 .EntryPoint = .{
1125 .cmd = macho.LC_MAIN,
1126 .cmdsize = @sizeOf(macho.entry_point_command),
1127 .entryoff = 0x0,
1128 .stacksize = 0,
1129 },
1130 });
1131 self.cmd_table_dirty = true;
1132 }
1133 {
1134 const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
1135 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfo;
1136 if (dyld_info.export_off == 0) {
1137 const nsyms = self.base.options.symbol_count_hint;
1138 const file_size = @sizeOf(u64) * nsyms;
1139 const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000));
1140 log.debug("found export trie free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
1141 dyld_info.export_off = off;
1142 dyld_info.export_size = @intCast(u32, file_size);
1143
1144 const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000);
1145 linkedit.vmsize += segment_size;
1146 linkedit.fileoff = off;
1147 }
846 }1148 }
847 {1149 {
1150 const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
848 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;1151 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
849 if (symtab.symoff == 0) {1152 if (symtab.symoff == 0) {
850 const p_align = @sizeOf(macho.nlist_64);
851 const nsyms = self.base.options.symbol_count_hint;1153 const nsyms = self.base.options.symbol_count_hint;
852 const file_size = p_align * nsyms;1154 const file_size = @sizeOf(macho.nlist_64) * nsyms;
853 const off = @intCast(u32, self.findFreeSpace(file_size, p_align));1155 const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000));
854 log.debug("found symbol table free space 0x{x} to 0x{x}\n", .{ off, off + file_size });1156 log.debug("found symbol table free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
855 symtab.symoff = off;1157 symtab.symoff = off;
856 symtab.nsyms = @intCast(u32, nsyms);1158 symtab.nsyms = @intCast(u32, nsyms);
1159
1160 const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000);
1161 linkedit.vmsize += segment_size;
857 }1162 }
858 if (symtab.stroff == 0) {1163 if (symtab.stroff == 0) {
859 try self.string_table.append(self.base.allocator, 0);1164 try self.string_table.append(self.base.allocator, 0);
860 const file_size = @intCast(u32, self.string_table.items.len);1165 const file_size = @intCast(u32, self.string_table.items.len);
861 const off = @intCast(u32, self.findFreeSpace(file_size, 1));1166 const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000));
862 log.debug("found string table free space 0x{x} to 0x{x}\n", .{ off, off + file_size });1167 log.debug("found string table free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
863 symtab.stroff = off;1168 symtab.stroff = off;
864 symtab.strsize = file_size;1169 symtab.strsize = file_size;
1170
1171 const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000);
1172 linkedit.vmsize += segment_size;
865 }1173 }
866 }1174 }
1175 if (self.dyld_stub_binder_index == null) {
1176 self.dyld_stub_binder_index = @intCast(u16, self.undef_symbols.items.len);
1177 const name = try self.makeString("dyld_stub_binder");
1178 try self.undef_symbols.append(self.base.allocator, .{
1179 .n_strx = name,
1180 .n_type = macho.N_UNDF | macho.N_EXT,
1181 .n_sect = 0,
1182 .n_desc = macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | macho.N_SYMBOL_RESOLVER,
1183 .n_value = 0,
1184 });
1185 }
867}1186}
8681187
869fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {1188fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
870 const segment = &self.load_commands.items[self.segment_cmd_index.?].Segment;
871 const text_section = &self.sections.items[self.text_section_index.?];1189 const text_section = &self.sections.items[self.text_section_index.?];
872 const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den;1190 const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den;
8731191
874 var block_placement: ?*TextBlock = null;1192 var block_placement: ?*TextBlock = null;
875 const addr = blk: {1193 const addr = blk: {
876 if (self.last_text_block) |last| {1194 if (self.last_text_block) |last| {
877 const last_symbol = self.symbol_table.items[last.symbol_table_index.?];1195 const last_symbol = self.local_symbols.items[last.local_sym_index];
1196 // TODO pad out with NOPs and reenable
1197 // const ideal_capacity = last.size * alloc_num / alloc_den;
1198 // const ideal_capacity_end_addr = last_symbol.n_value + ideal_capacity;
1199 // const new_start_addr = mem.alignForwardGeneric(u64, ideal_capacity_end_addr, alignment);
878 const end_addr = last_symbol.n_value + last.size;1200 const end_addr = last_symbol.n_value + last.size;
879 const new_start_addr = mem.alignForwardGeneric(u64, end_addr, alignment);1201 const new_start_addr = mem.alignForwardGeneric(u64, end_addr, alignment);
880 block_placement = last;1202 block_placement = last;
...@@ -883,22 +1205,15 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,...@@ -883,22 +1205,15 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
883 break :blk text_section.addr;1205 break :blk text_section.addr;
884 }1206 }
885 };1207 };
886 log.debug("computed symbol address 0x{x}\n", .{addr});
8871208
888 const expand_text_section = block_placement == null or block_placement.?.next == null;1209 const expand_text_section = block_placement == null or block_placement.?.next == null;
889 if (expand_text_section) {1210 if (expand_text_section) {
890 const text_capacity = self.allocatedSize(text_section.offset);1211 const text_capacity = self.allocatedSize(text_section.offset);
891 const needed_size = (addr + new_block_size) - text_section.addr;1212 const needed_size = (addr + new_block_size) - text_section.addr;
892 log.debug("text capacity 0x{x}, needed size 0x{x}\n", .{ text_capacity, needed_size });
893 assert(needed_size <= text_capacity); // TODO handle growth1213 assert(needed_size <= text_capacity); // TODO handle growth
8941214
895 self.last_text_block = text_block;1215 self.last_text_block = text_block;
896 text_section.size = needed_size;1216 text_section.size = needed_size; // TODO temp until we pad out with NOPs
897 segment.vmsize = needed_size;
898 segment.filesize = needed_size;
899 if (alignment < text_section.@"align") {
900 text_section.@"align" = @intCast(u32, alignment);
901 }
902 }1217 }
903 text_block.size = new_block_size;1218 text_block.size = new_block_size;
9041219
...@@ -936,16 +1251,17 @@ fn makeString(self: *MachO, bytes: []const u8) !u32 {...@@ -936,16 +1251,17 @@ fn makeString(self: *MachO, bytes: []const u8) !u32 {
936 return @intCast(u32, result);1251 return @intCast(u32, result);
937}1252}
9381253
939fn alignSize(comptime Int: type, min_size: anytype, alignment: Int) Int {1254fn getString(self: *MachO, str_off: u32) []const u8 {
940 const size = @intCast(Int, min_size);1255 assert(str_off < self.string_table.items.len);
941 if (size % alignment == 0) return size;1256 return mem.spanZ(@ptrCast([*:0]const u8, self.string_table.items.ptr + str_off));
942
943 const div = size / alignment;
944 return (div + 1) * alignment;
945}1257}
9461258
947fn commandSize(min_size: anytype) u32 {1259fn updateString(self: *MachO, old_str_off: u32, new_name: []const u8) !u32 {
948 return alignSize(u32, min_size, @sizeOf(u64));1260 const existing_name = self.getString(old_str_off);
1261 if (mem.eql(u8, existing_name, new_name)) {
1262 return old_str_off;
1263 }
1264 return self.makeString(new_name);
949}1265}
9501266
951fn addPadding(self: *MachO, size: u64, file_offset: u64) !void {1267fn addPadding(self: *MachO, size: u64, file_offset: u64) !void {
...@@ -961,11 +1277,8 @@ fn addPadding(self: *MachO, size: u64, file_offset: u64) !void {...@@ -961,11 +1277,8 @@ fn addPadding(self: *MachO, size: u64, file_offset: u64) !void {
9611277
962fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {1278fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
963 const hdr_size: u64 = @sizeOf(macho.mach_header_64);1279 const hdr_size: u64 = @sizeOf(macho.mach_header_64);
964 if (start < hdr_size)1280 if (start < hdr_size) return hdr_size;
965 return hdr_size;
966
967 const end = start + satMul(size, alloc_num) / alloc_den;1281 const end = start + satMul(size, alloc_num) / alloc_den;
968
969 {1282 {
970 const off = @sizeOf(macho.mach_header_64);1283 const off = @sizeOf(macho.mach_header_64);
971 var tight_size: u64 = 0;1284 var tight_size: u64 = 0;
...@@ -978,7 +1291,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {...@@ -978,7 +1291,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
978 return test_end;1291 return test_end;
979 }1292 }
980 }1293 }
981
982 for (self.sections.items) |section| {1294 for (self.sections.items) |section| {
983 const increased_size = satMul(section.size, alloc_num) / alloc_den;1295 const increased_size = satMul(section.size, alloc_num) / alloc_den;
984 const test_end = section.offset + increased_size;1296 const test_end = section.offset + increased_size;
...@@ -986,7 +1298,15 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {...@@ -986,7 +1298,15 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
986 return test_end;1298 return test_end;
987 }1299 }
988 }1300 }
9891301 if (self.dyld_info_cmd_index) |dyld_info_index| {
1302 const dyld_info = self.load_commands.items[dyld_info_index].DyldInfo;
1303 const tight_size = dyld_info.export_size;
1304 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
1305 const test_end = dyld_info.export_off + increased_size;
1306 if (end > dyld_info.export_off and start < test_end) {
1307 return test_end;
1308 }
1309 }
990 if (self.symtab_cmd_index) |symtab_index| {1310 if (self.symtab_cmd_index) |symtab_index| {
991 const symtab = self.load_commands.items[symtab_index].Symtab;1311 const symtab = self.load_commands.items[symtab_index].Symtab;
992 {1312 {
...@@ -1005,7 +1325,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {...@@ -1005,7 +1325,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
1005 }1325 }
1006 }1326 }
1007 }1327 }
1008
1009 return null;1328 return null;
1010}1329}
10111330
...@@ -1021,6 +1340,10 @@ fn allocatedSize(self: *MachO, start: u64) u64 {...@@ -1021,6 +1340,10 @@ fn allocatedSize(self: *MachO, start: u64) u64 {
1021 if (section.offset <= start) continue;1340 if (section.offset <= start) continue;
1022 if (section.offset < min_pos) min_pos = section.offset;1341 if (section.offset < min_pos) min_pos = section.offset;
1023 }1342 }
1343 if (self.dyld_info_cmd_index) |dyld_info_index| {
1344 const dyld_info = self.load_commands.items[dyld_info_index].DyldInfo;
1345 if (dyld_info.export_off > start and dyld_info.export_off < min_pos) min_pos = dyld_info.export_off;
1346 }
1024 if (self.symtab_cmd_index) |symtab_index| {1347 if (self.symtab_cmd_index) |symtab_index| {
1025 const symtab = self.load_commands.items[symtab_index].Symtab;1348 const symtab = self.load_commands.items[symtab_index].Symtab;
1026 if (symtab.symoff > start and symtab.symoff < min_pos) min_pos = symtab.symoff;1349 if (symtab.symoff > start and symtab.symoff < min_pos) min_pos = symtab.symoff;
...@@ -1042,12 +1365,133 @@ fn writeSymbol(self: *MachO, index: usize) !void {...@@ -1042,12 +1365,133 @@ fn writeSymbol(self: *MachO, index: usize) !void {
1042 defer tracy.end();1365 defer tracy.end();
10431366
1044 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;1367 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
1045 const sym = [1]macho.nlist_64{self.symbol_table.items[index]};1368 const sym = [1]macho.nlist_64{self.local_symbols.items[index]};
1046 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;1369 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;
1047 log.debug("writing symbol {} at 0x{x}\n", .{ sym[0], off });1370 log.debug("writing symbol {} at 0x{x}\n", .{ sym[0], off });
1048 try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);1371 try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
1049}1372}
10501373
1374fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
1375 const sect = &self.sections.items[self.got_section_index.?];
1376 const endian = self.base.options.target.cpu.arch.endian();
1377 var buf: [@sizeOf(u64)]u8 = undefined;
1378 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
1379 const off = sect.offset + @sizeOf(u64) * index;
1380 log.debug("writing offset table entry 0x{x} at 0x{x}\n", .{ self.offset_table.items[index], off });
1381 try self.base.file.?.pwriteAll(&buf, off);
1382}
1383
1384fn writeAllGlobalSymbols(self: *MachO) !void {
1385 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
1386 const off = symtab.symoff + self.local_symbols.items.len * @sizeOf(macho.nlist_64);
1387 const file_size = self.global_symbols.items.len * @sizeOf(macho.nlist_64);
1388 log.debug("writing global symbols from 0x{x} to 0x{x}\n", .{ off, file_size + off });
1389 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.global_symbols.items), off);
1390}
1391
1392fn writeAllUndefSymbols(self: *MachO) !void {
1393 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
1394 const nlocals = self.local_symbols.items.len;
1395 const nglobals = self.global_symbols.items.len;
1396 const off = symtab.symoff + (nlocals + nglobals) * @sizeOf(macho.nlist_64);
1397 const file_size = self.undef_symbols.items.len * @sizeOf(macho.nlist_64);
1398 log.debug("writing undef symbols from 0x{x} to 0x{x}\n", .{ off, file_size + off });
1399 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.undef_symbols.items), off);
1400}
1401
1402fn writeExportTrie(self: *MachO) !void {
1403 assert(self.entry_addr != null);
1404
1405 // TODO implement mechanism for generating a prefix tree of the exported symbols
1406 // single branch export trie
1407 var buf = [_]u8{0} ** 24;
1408 buf[0] = 0; // root node
1409 buf[1] = 1; // 1 branch from root
1410 mem.copy(u8, buf[2..], "_start");
1411 buf[8] = 0;
1412 buf[9] = 9 + 1;
1413
1414 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1415 const addr = self.entry_addr.? - text_segment.vmaddr;
1416 const written = try std.debug.leb.writeULEB128Mem(buf[12..], addr);
1417 buf[10] = @intCast(u8, written) + 1;
1418 buf[11] = 0;
1419
1420 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfo;
1421 try self.base.file.?.pwriteAll(buf[0..], dyld_info.export_off);
1422}
1423
1424fn writeStringTable(self: *MachO) !void {
1425 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
1426 const allocated_size = self.allocatedSize(symtab.stroff);
1427 const needed_size = self.string_table.items.len;
1428
1429 if (needed_size > allocated_size) {
1430 symtab.strsize = 0;
1431 symtab.stroff = @intCast(u32, self.findFreeSpace(needed_size, 1));
1432 }
1433 symtab.strsize = @intCast(u32, needed_size);
1434
1435 log.debug("writing string table from 0x{x} to 0x{x}\n", .{ symtab.stroff, symtab.stroff + symtab.strsize });
1436
1437 try self.base.file.?.pwriteAll(self.string_table.items, symtab.stroff);
1438
1439 // TODO rework how we preallocate space for the entire __LINKEDIT segment instead of
1440 // doing dynamic updates like this.
1441 const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
1442 linkedit.filesize = symtab.stroff + symtab.strsize - linkedit.fileoff;
1443}
1444
1445fn writeCmdHeaders(self: *MachO) !void {
1446 assert(self.cmd_table_dirty);
1447
1448 // Write all load command headers first.
1449 // Since command sizes are up-to-date and accurate, we will correctly
1450 // leave space for any section headers that any of the segment load
1451 // commands might consist of.
1452 var last_cmd_offset: usize = @sizeOf(macho.mach_header_64);
1453 for (self.load_commands.items) |cmd| {
1454 try cmd.write(&self.base.file.?, last_cmd_offset);
1455 last_cmd_offset += cmd.cmdsize();
1456 }
1457 {
1458 // write __text section header
1459 const off = if (self.text_segment_cmd_index) |text_segment_index| blk: {
1460 var i: usize = 0;
1461 var cmdsize: usize = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64);
1462 while (i < text_segment_index) : (i += 1) {
1463 cmdsize += self.load_commands.items[i].cmdsize();
1464 }
1465 break :blk cmdsize;
1466 } else {
1467 // If we've landed in here, we are building a MachO object file, so we have
1468 // only one, noname segment to append this section header to.
1469 return error.TODOImplementWritingObjFiles;
1470 };
1471 const idx = self.text_section_index.?;
1472 log.debug("writing text section {} at 0x{x}\n", .{ self.sections.items[idx .. idx + 1], off });
1473 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[idx .. idx + 1]), off);
1474 }
1475 {
1476 // write __got section header
1477 const off = if (self.data_segment_cmd_index) |data_segment_index| blk: {
1478 var i: usize = 0;
1479 var cmdsize: usize = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64);
1480 while (i < data_segment_index) : (i += 1) {
1481 cmdsize += self.load_commands.items[i].cmdsize();
1482 }
1483 break :blk cmdsize;
1484 } else {
1485 // If we've landed in here, we are building a MachO object file, so we have
1486 // only one, noname segment to append this section header to.
1487 return error.TODOImplementWritingObjFiles;
1488 };
1489 const idx = self.got_section_index.?;
1490 log.debug("writing got section {} at 0x{x}\n", .{ self.sections.items[idx .. idx + 1], off });
1491 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[idx .. idx + 1]), off);
1492 }
1493}
1494
1051/// Writes Mach-O file header.1495/// Writes Mach-O file header.
1052/// Should be invoked last as it needs up-to-date values of ncmds and sizeof_cmds bookkeeping1496/// Should be invoked last as it needs up-to-date values of ncmds and sizeof_cmds bookkeeping
1053/// variables.1497/// variables.
test/stage2/test.zig+39
...@@ -151,6 +151,45 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -151,6 +151,45 @@ pub fn addCases(ctx: *TestContext) !void {
151 {151 {
152 var case = ctx.exe("hello world", macosx_x64);152 var case = ctx.exe("hello world", macosx_x64);
153 case.addError("", &[_][]const u8{":1:1: error: no entry point found"});153 case.addError("", &[_][]const u8{":1:1: error: no entry point found"});
154
155 // Incorrect return type
156 case.addError(
157 \\export fn _start() noreturn {
158 \\}
159 , &[_][]const u8{":2:1: error: expected noreturn, found void"});
160
161 // Regular old hello world
162 case.addCompareOutput(
163 \\export fn _start() noreturn {
164 \\ print();
165 \\
166 \\ exit();
167 \\}
168 \\
169 \\fn print() void {
170 \\ asm volatile ("syscall"
171 \\ :
172 \\ : [number] "{rax}" (0x2000004),
173 \\ [arg1] "{rdi}" (1),
174 \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")),
175 \\ [arg3] "{rdx}" (14)
176 \\ : "memory"
177 \\ );
178 \\ return;
179 \\}
180 \\
181 \\fn exit() noreturn {
182 \\ asm volatile ("syscall"
183 \\ :
184 \\ : [number] "{rax}" (0x2000001),
185 \\ [arg1] "{rdi}" (0)
186 \\ : "memory"
187 \\ );
188 \\ unreachable;
189 \\}
190 ,
191 "Hello, World!\n",
192 );
154 }193 }
155194
156 {195 {