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) {
12571257 /// for thread local variables
12581258 X86_64_RELOC_TLV,
12591259};
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 {
15321532 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
15331533 const func = func_val.func;
15341534 const got = &macho_file.sections.items[macho_file.got_section_index.?];
1535 const ptr_bytes = 8;
1536 const got_addr = @intCast(u32, got.addr + func.owner_decl.link.macho.offset_table_index.? * ptr_bytes);
1537 // ff 14 25 xx xx xx xx call [addr]
1538 try self.code.ensureCapacity(self.code.items.len + 7);
1539 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
1540 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), got_addr);
1535 const got_addr = got.addr + func.owner_decl.link.macho.offset_table_index * @sizeOf(u64);
1536 // Here, we store the got address in %rax, and then call %rax
1537 // movabsq [addr], %rax
1538 try self.genSetReg(inst.base.src, .rax, .{ .memory = got_addr });
1539 // callq *%rax
1540 try self.code.ensureCapacity(self.code.items.len + 2);
1541 self.code.appendSliceAssumeCapacity(&[2]u8{ 0xff, 0xd0 });
15411542 } else {
15421543 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
15431544 }
......@@ -2590,7 +2591,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
25902591 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
25912592 const decl = payload.decl;
25922593 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;
25942595 return MCValue{ .memory = got_addr };
25952596 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
25962597 const decl = payload.decl;
src/link/MachO.zig+608-164
......@@ -27,6 +27,10 @@ const LoadCommand = union(enum) {
2727 LinkeditData: macho.linkedit_data_command,
2828 Symtab: macho.symtab_command,
2929 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
3135 pub fn cmdsize(self: LoadCommand) u32 {
3236 return switch (self) {
......@@ -34,6 +38,10 @@ const LoadCommand = union(enum) {
3438 .LinkeditData => |x| x.cmdsize,
3539 .Symtab => |x| x.cmdsize,
3640 .Dysymtab => |x| x.cmdsize,
41 .DyldInfo => |x| x.cmdsize,
42 .Dylinker => |x| x.cmdsize,
43 .Dylib => |x| x.cmdsize,
44 .EntryPoint => |x| x.cmdsize,
3745 };
3846 }
3947
......@@ -43,6 +51,10 @@ const LoadCommand = union(enum) {
4351 .LinkeditData => |cmd| writeGeneric(cmd, file, offset),
4452 .Symtab => |cmd| writeGeneric(cmd, file, offset),
4553 .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),
4658 };
4759 }
4860
......@@ -56,30 +68,52 @@ base: File,
5668
5769/// Table of all load commands
5870load_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
6082symtab_cmd_index: ?u16 = null,
83/// Dynamic symbol table
6184dysymtab_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
6290data_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
6498/// Table of all sections
6599sections: std.ArrayListUnmanaged(macho.section_64) = .{},
66100
67/// __TEXT segment sections
101/// __TEXT,__text section
68102text_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 sections
104/// __DATA,__got section
75105got_section_index: ?u16 = null,
76const_data_section_index: ?u16 = null,
77106
78107entry_addr: ?u64 = null,
79108
80/// Table of all symbols used.
109/// Table of all local symbols
81110/// 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
84118/// Table of symbol names aka the string table.
85119string_table: std.ArrayListUnmanaged(u8) = .{},
......@@ -115,19 +149,27 @@ const LIB_SYSTEM_NAME: [*:0]const u8 = "System";
115149const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.dylib";
116150
117151pub const TextBlock = struct {
118 /// Index into the symbol table
119 symbol_table_index: ?u32,
152 /// Each decl always gets a local symbol with the fully qualified name.
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,
120159 /// Index into offset table
121 offset_table_index: ?u32,
160 /// This field is undefined for symbols with size = 0.
161 offset_table_index: u32,
122162 /// 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.
123165 size: u64,
124166 /// Points to the previous and next neighbours
125167 prev: ?*TextBlock,
126168 next: ?*TextBlock,
127169
128170 pub const empty = TextBlock{
129 .symbol_table_index = null,
130 .offset_table_index = null,
171 .local_sym_index = 0,
172 .offset_table_index = undefined,
131173 .size = 0,
132174 .prev = null,
133175 .next = null,
......@@ -156,6 +198,15 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
156198
157199 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
159210 switch (options.output_mode) {
160211 .Exe => {},
161212 .Obj => {},
......@@ -196,88 +247,83 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
196247 const tracy = trace(@src());
197248 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
199257 switch (self.base.options.output_mode) {
200258 .Exe => {
201 var last_cmd_offset: usize = @sizeOf(macho.mach_header_64);
202 {
203 // Specify path to dynamic linker dyld
204 const cmdsize = commandSize(@sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH));
205 const load_dylinker = [1]macho.dylinker_command{
206 .{
207 .cmd = macho.LC_LOAD_DYLINKER,
208 .cmdsize = cmdsize,
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;
259 if (self.entry_addr) |addr| {
260 // Write export trie.
261 try self.writeExportTrie();
262
263 // Update LC_MAIN with entry offset
264 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
265 const main_cmd = &self.load_commands.items[self.main_cmd_index.?].EntryPoint;
266 main_cmd.entryoff = addr - text_segment.vmaddr;
220267 }
221268
222269 {
223 // Link against libSystem
224 const cmdsize = commandSize(@sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH));
225 // TODO Find a way to work out runtime version from the OS version triple stored in std.Target.
226 // In the meantime, we're gonna hardcode to the minimum compatibility version of 1.0.0.
227 const min_version = 0x10000;
228 const dylib = .{
229 .name = @sizeOf(macho.dylib_command),
230 .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files
231 .current_version = min_version,
232 .compatibility_version = min_version,
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;
270 // Update dynamic symbol table.
271 const nlocals = @intCast(u32, self.local_symbols.items.len);
272 const nglobals = @intCast(u32, self.global_symbols.items.len);
273 const nundefs = @intCast(u32, self.undef_symbols.items.len);
274 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
275 dysymtab.nlocalsym = nlocals;
276 dysymtab.iextdefsym = nlocals;
277 dysymtab.nextdefsym = nglobals;
278 dysymtab.iundefsym = nlocals + nglobals;
279 dysymtab.nundefsym = nundefs;
249280 }
250 },
251 .Obj => {
252281 {
253 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
254 symtab.nsyms = @intCast(u32, self.symbol_table.items.len);
255 const allocated_size = self.allocatedSize(symtab.stroff);
256 const needed_size = self.string_table.items.len;
257 log.debug("allocated_size = 0x{x}, needed_size = 0x{x}\n", .{ allocated_size, needed_size });
258
259 if (needed_size > allocated_size) {
260 symtab.strsize = 0;
261 symtab.stroff = @intCast(u32, self.findFreeSpace(needed_size, 1));
282 // Write path to dyld loader.
283 var off: usize = @sizeOf(macho.mach_header_64);
284 for (self.load_commands.items) |cmd| {
285 if (cmd == .Dylinker) break;
286 off += cmd.cmdsize();
262287 }
263 symtab.strsize = @intCast(u32, needed_size);
264
265 log.debug("writing string table from 0x{x} to 0x{x}\n", .{ symtab.stroff, symtab.stroff + symtab.strsize });
266
267 try self.base.file.?.pwriteAll(self.string_table.items, symtab.stroff);
288 const cmd = &self.load_commands.items[self.dylinker_cmd_index.?].Dylinker;
289 off += cmd.name;
290 const padding = cmd.cmdsize - @sizeOf(macho.dylinker_command);
291 log.debug("writing LC_LOAD_DYLINKER padding of size {} at 0x{x}\n", .{ padding, off });
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);
268295 }
269
270 var last_cmd_offset: usize = @sizeOf(macho.mach_header_64);
271 for (self.load_commands.items) |cmd| {
272 try cmd.write(&self.base.file.?, last_cmd_offset);
273 last_cmd_offset += cmd.cmdsize();
296 {
297 // Write path to libSystem.
298 var off: usize = @sizeOf(macho.mach_header_64);
299 for (self.load_commands.items) |cmd| {
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);
274310 }
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);
277311 },
312 .Obj => {},
278313 .Lib => return error.TODOImplementWritingLibFiles,
279314 }
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
281327 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
282328 log.debug("flushing. no_entry_point_found = true\n", .{});
283329 self.error_flags.no_entry_point_found = true;
......@@ -669,32 +715,34 @@ fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 {
669715pub fn deinit(self: *MachO) void {
670716 self.offset_table.deinit(self.base.allocator);
671717 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);
673721 self.sections.deinit(self.base.allocator);
674722 self.load_commands.deinit(self.base.allocator);
675723}
676724
677725pub 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);
681729 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 });
684 decl.link.macho.symbol_table_index = @intCast(u32, self.symbol_table.items.len);
685 _ = self.symbol_table.addOneAssumeCapacity();
731 log.debug("allocating symbol index {} for {}\n", .{ self.local_symbols.items.len, decl.name });
732 decl.link.macho.local_sym_index = @intCast(u32, self.local_symbols.items.len);
733 _ = self.local_symbols.addOneAssumeCapacity();
686734
687735 decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len);
688736 _ = 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] = .{
691739 .n_strx = 0,
692740 .n_type = 0,
693741 .n_sect = 0,
694742 .n_desc = 0,
695743 .n_value = 0,
696744 };
697 self.offset_table.items[decl.link.macho.offset_table_index.?] = 0;
745 self.offset_table.items[decl.link.macho.offset_table_index] = 0;
698746}
699747
700748pub 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 {
716764 return;
717765 },
718766 };
719 log.debug("generated code {}\n", .{code});
720767
721768 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
724771 const decl_name = mem.spanZ(decl.name);
725772 const name_str_index = try self.makeString(decl_name);
726773 const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment);
727774 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
730776 symbol.* = .{
731777 .n_strx = name_str_index,
......@@ -734,18 +780,20 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
734780 .n_desc = 0,
735781 .n_value = addr,
736782 };
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.
739 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
740 try self.updateDeclExports(module, decl, decl_exports);
741 try self.writeSymbol(decl.link.macho.symbol_table_index.?);
785 try self.writeSymbol(decl.link.macho.local_sym_index);
786 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
742787
743788 const text_section = self.sections.items[self.text_section_index.?];
744789 const section_offset = symbol.n_value - text_section.addr;
745790 const file_offset = text_section.offset + section_offset;
746 log.debug("file_offset 0x{x}\n", .{file_offset});
747791
748792 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);
749797}
750798
751799pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {}
......@@ -759,34 +807,89 @@ pub fn updateDeclExports(
759807 const tracy = trace(@src());
760808 defer tracy.end();
761809
762 if (decl.link.macho.symbol_table_index == null) return;
763
764 const decl_sym = &self.symbol_table.items[decl.link.macho.symbol_table_index.?];
765 // TODO implement
766 if (exports.len == 0) return;
767
768 const exp = exports[0];
769 self.entry_addr = decl_sym.n_value;
770 decl_sym.n_type |= macho.N_EXT;
771 exp.link.sym_index = 0;
810 try self.global_symbols.ensureCapacity(self.base.allocator, self.global_symbols.items.len + exports.len);
811 if (decl.link.macho.local_sym_index == 0) return;
812 const decl_sym = &self.local_symbols.items[decl.link.macho.local_sym_index];
813
814 for (exports) |exp| {
815 if (exp.options.section) |section_name| {
816 if (!mem.eql(u8, section_name, "__text")) {
817 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
818 module.failed_exports.putAssumeCapacityNoClobber(
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 }
772868}
773869
774870pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {}
775871
776872pub 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;
778875}
779876
780877pub fn populateMissingMetadata(self: *MachO) !void {
781 if (self.segment_cmd_index == null) {
782 self.segment_cmd_index = @intCast(u16, self.load_commands.items.len);
878 switch (self.base.options.output_mode) {
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);
783886 try self.load_commands.append(self.base.allocator, .{
784887 .Segment = .{
785888 .cmd = macho.LC_SEGMENT_64,
786889 .cmdsize = @sizeOf(macho.segment_command_64),
787 .segname = makeStaticString(""),
890 .segname = makeStaticString("__PAGEZERO"),
788891 .vmaddr = 0,
789 .vmsize = 0,
892 .vmsize = 0x100000000, // size always set to 4GB
790893 .fileoff = 0,
791894 .filesize = 0,
792895 .maxprot = 0,
......@@ -797,28 +900,34 @@ pub fn populateMissingMetadata(self: *MachO) !void {
797900 });
798901 self.cmd_table_dirty = true;
799902 }
800 if (self.symtab_cmd_index == null) {
801 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
903 if (self.text_segment_cmd_index == null) {
904 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
905 const prot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE;
802906 try self.load_commands.append(self.base.allocator, .{
803 .Symtab = .{
804 .cmd = macho.LC_SYMTAB,
805 .cmdsize = @sizeOf(macho.symtab_command),
806 .symoff = 0,
807 .nsyms = 0,
808 .stroff = 0,
809 .strsize = 0,
907 .Segment = .{
908 .cmd = macho.LC_SEGMENT_64,
909 .cmdsize = @sizeOf(macho.segment_command_64),
910 .segname = makeStaticString("__TEXT"),
911 .vmaddr = 0x100000000, // always starts at 4GB
912 .vmsize = 0,
913 .fileoff = 0,
914 .filesize = 0,
915 .maxprot = prot,
916 .initprot = prot,
917 .nsects = 0,
918 .flags = 0,
810919 },
811920 });
812921 self.cmd_table_dirty = true;
813922 }
814923 if (self.text_section_index == null) {
815924 self.text_section_index = @intCast(u16, self.sections.items.len);
816 const segment = &self.load_commands.items[self.segment_cmd_index.?].Segment;
817 segment.cmdsize += @sizeOf(macho.section_64);
818 segment.nsects += 1;
925 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
926 text_segment.cmdsize += @sizeOf(macho.section_64);
927 text_segment.nsects += 1;
819928
820 const file_size = self.base.options.program_code_size_hint;
821 const off = @intCast(u32, self.findFreeSpace(file_size, 1));
929 const file_size = mem.alignForwardGeneric(u64, self.base.options.program_code_size_hint, 0x1000);
930 const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000)); // TODO maybe findFreeSpace should return u32 directly?
822931 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
823932
824933 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 {
826935 try self.sections.append(self.base.allocator, .{
827936 .sectname = makeStaticString("__text"),
828937 .segname = makeStaticString("__TEXT"),
829 .addr = 0,
938 .addr = text_segment.vmaddr + off,
830939 .size = file_size,
831940 .offset = off,
832 .@"align" = 0x1000,
941 .@"align" = 12, // 2^12 = 4096
833942 .reloff = 0,
834943 .nreloc = 0,
835944 .flags = flags,
......@@ -838,43 +947,256 @@ pub fn populateMissingMetadata(self: *MachO) !void {
838947 .reserved3 = 0,
839948 });
840949
841 segment.vmsize += file_size;
842 segment.filesize += file_size;
843 segment.fileoff = off;
950 text_segment.vmsize = file_size + off; // We add off here since __TEXT segment includes everything prior to __text section.
951 text_segment.filesize = file_size + 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 }
8461148 }
8471149 {
1150 const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
8481151 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
8491152 if (symtab.symoff == 0) {
850 const p_align = @sizeOf(macho.nlist_64);
8511153 const nsyms = self.base.options.symbol_count_hint;
852 const file_size = p_align * nsyms;
853 const off = @intCast(u32, self.findFreeSpace(file_size, p_align));
1154 const file_size = @sizeOf(macho.nlist_64) * nsyms;
1155 const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000));
8541156 log.debug("found symbol table free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
8551157 symtab.symoff = off;
8561158 symtab.nsyms = @intCast(u32, nsyms);
1159
1160 const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000);
1161 linkedit.vmsize += segment_size;
8571162 }
8581163 if (symtab.stroff == 0) {
8591164 try self.string_table.append(self.base.allocator, 0);
8601165 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));
8621167 log.debug("found string table free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
8631168 symtab.stroff = off;
8641169 symtab.strsize = file_size;
1170
1171 const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000);
1172 linkedit.vmsize += segment_size;
8651173 }
8661174 }
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 }
8671186}
8681187
8691188fn 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;
8711189 const text_section = &self.sections.items[self.text_section_index.?];
8721190 const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den;
8731191
8741192 var block_placement: ?*TextBlock = null;
8751193 const addr = blk: {
8761194 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);
8781200 const end_addr = last_symbol.n_value + last.size;
8791201 const new_start_addr = mem.alignForwardGeneric(u64, end_addr, alignment);
8801202 block_placement = last;
......@@ -883,22 +1205,15 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
8831205 break :blk text_section.addr;
8841206 }
8851207 };
886 log.debug("computed symbol address 0x{x}\n", .{addr});
8871208
8881209 const expand_text_section = block_placement == null or block_placement.?.next == null;
8891210 if (expand_text_section) {
8901211 const text_capacity = self.allocatedSize(text_section.offset);
8911212 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 });
8931213 assert(needed_size <= text_capacity); // TODO handle growth
8941214
8951215 self.last_text_block = text_block;
896 text_section.size = needed_size;
897 segment.vmsize = needed_size;
898 segment.filesize = needed_size;
899 if (alignment < text_section.@"align") {
900 text_section.@"align" = @intCast(u32, alignment);
901 }
1216 text_section.size = needed_size; // TODO temp until we pad out with NOPs
9021217 }
9031218 text_block.size = new_block_size;
9041219
......@@ -936,16 +1251,17 @@ fn makeString(self: *MachO, bytes: []const u8) !u32 {
9361251 return @intCast(u32, result);
9371252}
9381253
939fn alignSize(comptime Int: type, min_size: anytype, alignment: Int) Int {
940 const size = @intCast(Int, min_size);
941 if (size % alignment == 0) return size;
942
943 const div = size / alignment;
944 return (div + 1) * alignment;
1254fn getString(self: *MachO, str_off: u32) []const u8 {
1255 assert(str_off < self.string_table.items.len);
1256 return mem.spanZ(@ptrCast([*:0]const u8, self.string_table.items.ptr + str_off));
9451257}
9461258
947fn commandSize(min_size: anytype) u32 {
948 return alignSize(u32, min_size, @sizeOf(u64));
1259fn updateString(self: *MachO, old_str_off: u32, new_name: []const u8) !u32 {
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);
9491265}
9501266
9511267fn addPadding(self: *MachO, size: u64, file_offset: u64) !void {
......@@ -961,11 +1277,8 @@ fn addPadding(self: *MachO, size: u64, file_offset: u64) !void {
9611277
9621278fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
9631279 const hdr_size: u64 = @sizeOf(macho.mach_header_64);
964 if (start < hdr_size)
965 return hdr_size;
966
1280 if (start < hdr_size) return hdr_size;
9671281 const end = start + satMul(size, alloc_num) / alloc_den;
968
9691282 {
9701283 const off = @sizeOf(macho.mach_header_64);
9711284 var tight_size: u64 = 0;
......@@ -978,7 +1291,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
9781291 return test_end;
9791292 }
9801293 }
981
9821294 for (self.sections.items) |section| {
9831295 const increased_size = satMul(section.size, alloc_num) / alloc_den;
9841296 const test_end = section.offset + increased_size;
......@@ -986,7 +1298,15 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
9861298 return test_end;
9871299 }
9881300 }
989
1301 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 }
9901310 if (self.symtab_cmd_index) |symtab_index| {
9911311 const symtab = self.load_commands.items[symtab_index].Symtab;
9921312 {
......@@ -1005,7 +1325,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
10051325 }
10061326 }
10071327 }
1008
10091328 return null;
10101329}
10111330
......@@ -1021,6 +1340,10 @@ fn allocatedSize(self: *MachO, start: u64) u64 {
10211340 if (section.offset <= start) continue;
10221341 if (section.offset < min_pos) min_pos = section.offset;
10231342 }
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 }
10241347 if (self.symtab_cmd_index) |symtab_index| {
10251348 const symtab = self.load_commands.items[symtab_index].Symtab;
10261349 if (symtab.symoff > start and symtab.symoff < min_pos) min_pos = symtab.symoff;
......@@ -1042,12 +1365,133 @@ fn writeSymbol(self: *MachO, index: usize) !void {
10421365 defer tracy.end();
10431366
10441367 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]};
10461369 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;
10471370 log.debug("writing symbol {} at 0x{x}\n", .{ sym[0], off });
10481371 try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
10491372}
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
10511495/// Writes Mach-O file header.
10521496/// Should be invoked last as it needs up-to-date values of ncmds and sizeof_cmds bookkeeping
10531497/// variables.
test/stage2/test.zig+39
......@@ -151,6 +151,45 @@ pub fn addCases(ctx: *TestContext) !void {
151151 {
152152 var case = ctx.exe("hello world", macosx_x64);
153153 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 );
154193 }
155194
156195 {