authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-09-29 18:42:50+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-10-04 15:31:47+02:00
loga927f242019f22bda95fb4a74020966ac2bdb54e
tree79662c18b6dc20b2b923cf9d507c2a698467f55a
parent57a81bb5596f7487d728cf9a91013347e368912b

Generate more MachO exe boilerplate

* Convert draft to generate all relevant segments and sections in right places * Do not prealloc space in text blocks until we can NOP * Write out LC_LOAD_DYLINKER command * Add LC_LOAD_DYLIB command in order to specify to load libSystem * Redo update decl exports (similar to Elf globals, globals need to be contiguous in memory)

2 files changed, 355 insertions(+), 152 deletions(-)

lib/std/macho.zig+38
......@@ -1257,3 +1257,41 @@ 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;
src/link/MachO.zig+317-152
......@@ -76,7 +76,6 @@ text_segment_cmd_index: ?u16 = null,
7676data_segment_cmd_index: ?u16 = null,
7777/// __LINKEDIT segment
7878linkedit_segment_cmd_index: ?u16 = null,
79segment_cmd_index: ?u16 = null,
8079/// Dyld info
8180dyld_info_cmd_index: ?u16 = null,
8281/// Symbol table
......@@ -107,9 +106,10 @@ got_section_index: ?u16 = null,
107106
108107entry_addr: ?u64 = null,
109108
110/// Table of all symbols used.
109/// Table of all local symbols used.
111110/// Internally references string table for names (which are optional).
112symbol_table: std.ArrayListUnmanaged(macho.nlist_64) = .{},
111local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
112global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
113113
114114/// Table of symbol names aka the string table.
115115string_table: std.ArrayListUnmanaged(u8) = .{},
......@@ -227,87 +227,82 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
227227 defer tracy.end();
228228
229229 switch (self.base.options.output_mode) {
230 .Exe => {
231 var last_cmd_offset: usize = @sizeOf(macho.mach_header_64);
232 {
233 // Specify path to dynamic linker dyld
234 const cmdsize = commandSize(@sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH));
235 const load_dylinker = [1]macho.dylinker_command{
236 .{
237 .cmd = macho.LC_LOAD_DYLINKER,
238 .cmdsize = cmdsize,
239 .name = @sizeOf(macho.dylinker_command),
240 },
241 };
242
243 try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylinker[0..1]), last_cmd_offset);
244
245 const file_offset = last_cmd_offset + @sizeOf(macho.dylinker_command);
246 try self.addPadding(cmdsize - @sizeOf(macho.dylinker_command), file_offset);
247
248 try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), file_offset);
249 last_cmd_offset += cmdsize;
250 }
230 .Exe => {},
231 .Obj => return error.TODOImplementWritingObjFiles,
232 .Lib => return error.TODOImplementWritingLibFiles,
233 }
251234
252 {
253 // Link against libSystem
254 const cmdsize = commandSize(@sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH));
255 // TODO Find a way to work out runtime version from the OS version triple stored in std.Target.
256 // In the meantime, we're gonna hardcode to the minimum compatibility version of 1.0.0.
257 const min_version = 0x10000;
258 const dylib = .{
259 .name = @sizeOf(macho.dylib_command),
260 .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files
261 .current_version = min_version,
262 .compatibility_version = min_version,
263 };
264 const load_dylib = [1]macho.dylib_command{
265 .{
266 .cmd = macho.LC_LOAD_DYLIB,
267 .cmdsize = cmdsize,
268 .dylib = dylib,
269 },
270 };
271
272 try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylib[0..1]), last_cmd_offset);
273
274 const file_offset = last_cmd_offset + @sizeOf(macho.dylib_command);
275 try self.addPadding(cmdsize - @sizeOf(macho.dylib_command), file_offset);
276
277 try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), file_offset);
278 last_cmd_offset += cmdsize;
279 }
280 },
281 .Obj => {
282 {
283 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
284 symtab.nsyms = @intCast(u32, self.symbol_table.items.len);
285 const allocated_size = self.allocatedSize(symtab.stroff);
286 const needed_size = self.string_table.items.len;
287 log.debug("allocated_size = 0x{x}, needed_size = 0x{x}\n", .{ allocated_size, needed_size });
288
289 if (needed_size > allocated_size) {
290 symtab.strsize = 0;
291 symtab.stroff = @intCast(u32, self.findFreeSpace(needed_size, 1));
292 }
293 symtab.strsize = @intCast(u32, needed_size);
235 // Unfortunately these have to be buffered and done at the end because ELF does not allow
236 // mixing local and global symbols within a symbol table.
237 try self.writeAllGlobalSymbols();
294238
295 log.debug("writing string table from 0x{x} to 0x{x}\n", .{ symtab.stroff, symtab.stroff + symtab.strsize });
239 {
240 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
241 symtab.nsyms = @intCast(u32, self.local_symbols.items.len + self.global_symbols.items.len);
242 const allocated_size = self.allocatedSize(symtab.stroff);
243 const needed_size = self.string_table.items.len;
244 log.debug("allocated_size = 0x{x}, needed_size = 0x{x}\n", .{ allocated_size, needed_size });
245
246 if (needed_size > allocated_size) {
247 symtab.strsize = 0;
248 symtab.stroff = @intCast(u32, self.findFreeSpace(needed_size, 1));
249 }
250 symtab.strsize = @intCast(u32, needed_size);
296251
297 try self.base.file.?.pwriteAll(self.string_table.items, symtab.stroff);
298 }
252 log.debug("writing string table from 0x{x} to 0x{x}\n", .{ symtab.stroff, symtab.stroff + symtab.strsize });
299253
300 var last_cmd_offset: usize = @sizeOf(macho.mach_header_64);
301 for (self.load_commands.items) |cmd| {
302 try cmd.write(&self.base.file.?, last_cmd_offset);
303 last_cmd_offset += cmd.cmdsize();
304 }
305 const off = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64);
306 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items), off);
307 },
308 .Lib => return error.TODOImplementWritingLibFiles,
254 try self.base.file.?.pwriteAll(self.string_table.items, symtab.stroff);
255 }
256 {
257 var last_cmd_offset: usize = @sizeOf(macho.mach_header_64);
258 for (self.load_commands.items) |cmd| {
259 try cmd.write(&self.base.file.?, last_cmd_offset);
260 last_cmd_offset += cmd.cmdsize();
261 }
262 }
263 {
264 // write __text section
265 const off = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64) * 2;
266 log.debug("writing text section {} at 0x{x}\n", .{ self.sections.items[0..1], off });
267 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[0..1]), off);
268 }
269 {
270 // write __got section
271 const text = &self.load_commands.items[self.text_segment_cmd_index.?];
272 const off = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64) * 2 + text.cmdsize();
273 log.debug("writing got section {} at 0x{x}\n", .{ self.sections.items[1..2], off });
274 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[1..2]), off);
275 }
276 {
277 // write path to dyld
278 var off: usize = @sizeOf(macho.mach_header_64);
279 for (self.load_commands.items) |cmd| {
280 if (cmd == .Dylinker) break;
281 off += cmd.cmdsize();
282 }
283 const cmd = &self.load_commands.items[self.dylinker_cmd_index.?].Dylinker;
284 off += cmd.name;
285 const padding = cmd.cmdsize - @sizeOf(macho.dylinker_command);
286 log.debug("writing LC_LOAD_DYLINKER padding of size {} at 0x{x}\n", .{ padding, off });
287 try self.addPadding(padding, off);
288 log.debug("writing LC_LOAD_DYLINKER path to dyld at 0x{x}\n", .{off});
289 try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), off);
290 }
291 {
292 // write path to libSystem
293 var off: usize = @sizeOf(macho.mach_header_64);
294 for (self.load_commands.items) |cmd| {
295 if (cmd == .Dylib) break;
296 off += cmd.cmdsize();
297 }
298 const cmd = &self.load_commands.items[self.libsystem_cmd_index.?].Dylib;
299 off += cmd.dylib.name;
300 const padding = cmd.cmdsize - @sizeOf(macho.dylib_command);
301 log.debug("writing LC_LOAD_DYLIB padding of size {} at 0x{x}\n", .{ padding, off });
302 try self.addPadding(padding, off);
303 log.debug("writing LC_LOAD_DYLIB path to libSystem at 0x{x}\n", .{off});
304 try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), off);
309305 }
310
311306 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
312307 log.debug("flushing. no_entry_point_found = true\n", .{});
313308 self.error_flags.no_entry_point_found = true;
......@@ -699,7 +694,8 @@ fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 {
699694pub fn deinit(self: *MachO) void {
700695 self.offset_table.deinit(self.base.allocator);
701696 self.string_table.deinit(self.base.allocator);
702 self.symbol_table.deinit(self.base.allocator);
697 self.global_symbols.deinit(self.base.allocator);
698 self.local_symbols.deinit(self.base.allocator);
703699 self.sections.deinit(self.base.allocator);
704700 self.load_commands.deinit(self.base.allocator);
705701}
......@@ -707,17 +703,17 @@ pub fn deinit(self: *MachO) void {
707703pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
708704 if (decl.link.macho.symbol_table_index) |_| return;
709705
710 try self.symbol_table.ensureCapacity(self.base.allocator, self.symbol_table.items.len + 1);
706 try self.local_symbols.ensureCapacity(self.base.allocator, self.local_symbols.items.len + 1);
711707 try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
712708
713 log.debug("allocating symbol index {} for {}\n", .{ self.symbol_table.items.len, decl.name });
714 decl.link.macho.symbol_table_index = @intCast(u32, self.symbol_table.items.len);
715 _ = self.symbol_table.addOneAssumeCapacity();
709 log.debug("allocating symbol index {} for {}\n", .{ self.local_symbols.items.len, decl.name });
710 decl.link.macho.symbol_table_index = @intCast(u32, self.local_symbols.items.len);
711 _ = self.local_symbols.addOneAssumeCapacity();
716712
717713 decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len);
718714 _ = self.offset_table.addOneAssumeCapacity();
719715
720 self.symbol_table.items[decl.link.macho.symbol_table_index.?] = .{
716 self.local_symbols.items[decl.link.macho.symbol_table_index.?] = .{
721717 .n_strx = 0,
722718 .n_type = 0,
723719 .n_sect = 0,
......@@ -749,7 +745,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
749745 log.debug("generated code {}\n", .{code});
750746
751747 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
752 const symbol = &self.symbol_table.items[decl.link.macho.symbol_table_index.?];
748 const symbol = &self.local_symbols.items[decl.link.macho.symbol_table_index.?];
753749
754750 const decl_name = mem.spanZ(decl.name);
755751 const name_str_index = try self.makeString(decl_name);
......@@ -766,9 +762,6 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
766762 };
767763 self.offset_table.items[decl.link.macho.offset_table_index.?] = addr;
768764
769 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
770 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
771 try self.updateDeclExports(module, decl, decl_exports);
772765 try self.writeSymbol(decl.link.macho.symbol_table_index.?);
773766 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index.?);
774767
......@@ -778,6 +771,10 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
778771 log.debug("file_offset 0x{x}\n", .{file_offset});
779772
780773 try self.base.file.?.pwriteAll(code, file_offset);
774
775 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
776 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
777 try self.updateDeclExports(module, decl, decl_exports);
781778}
782779
783780pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {}
......@@ -791,22 +788,72 @@ pub fn updateDeclExports(
791788 const tracy = trace(@src());
792789 defer tracy.end();
793790
791 try self.global_symbols.ensureCapacity(self.base.allocator, self.global_symbols.items.len + exports.len);
794792 if (decl.link.macho.symbol_table_index == null) return;
795
796 const decl_sym = &self.symbol_table.items[decl.link.macho.symbol_table_index.?];
797 // TODO implement
798 if (exports.len == 0) return;
799
800 const exp = exports[0];
801 self.entry_addr = decl_sym.n_value;
802 decl_sym.n_type |= macho.N_EXT;
803 exp.link.sym_index = 0;
793 const decl_sym = &self.local_symbols.items[decl.link.macho.symbol_table_index.?];
794
795 for (exports) |exp| {
796 if (exp.options.section) |section_name| {
797 if (!mem.eql(u8, section_name, "__text")) {
798 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
799 module.failed_exports.putAssumeCapacityNoClobber(
800 exp,
801 try Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
802 );
803 continue;
804 }
805 }
806 const n_desc = switch (exp.options.linkage) {
807 .Internal => macho.REFERENCE_FLAG_PRIVATE_DEFINED,
808 .Strong => blk: {
809 if (mem.eql(u8, exp.options.name, "_start")) {
810 self.entry_addr = decl_sym.n_value;
811 const cmd = &self.load_commands.items[self.main_cmd_index.?].EntryPoint;
812 cmd.entryoff = decl_sym.n_value;
813 }
814 break :blk macho.REFERENCE_FLAG_DEFINED;
815 },
816 .Weak => macho.N_WEAK_REF,
817 .LinkOnce => {
818 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
819 module.failed_exports.putAssumeCapacityNoClobber(
820 exp,
821 try Module.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),
822 );
823 continue;
824 },
825 };
826 const n_type = decl_sym.n_type | macho.N_EXT;
827 if (exp.link.sym_index) |i| {
828 const sym = &self.global_symbols.items[i];
829 sym.* = .{
830 .n_strx = try self.updateString(sym.n_strx, exp.options.name),
831 .n_type = n_type,
832 .n_sect = @intCast(u8, self.text_section_index.?) + 1,
833 .n_desc = n_desc,
834 .n_value = decl_sym.n_value,
835 };
836 } else {
837 const name_str_index = try self.makeString(exp.options.name);
838 _ = self.global_symbols.addOneAssumeCapacity();
839 const i = self.global_symbols.items.len - 1;
840 self.global_symbols.items[i] = .{
841 .n_strx = name_str_index,
842 .n_type = n_type,
843 .n_sect = @intCast(u8, self.text_section_index.?) + 1,
844 .n_desc = n_desc,
845 .n_value = decl_sym.n_value,
846 };
847
848 exp.link.sym_index = @intCast(u32, i);
849 }
850 }
804851}
805852
806853pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {}
807854
808855pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {
809 return self.symbol_table.items[decl.link.macho.symbol_table_index.?].n_value;
856 return self.local_symbols.items[decl.link.macho.symbol_table_index.?].n_value;
810857}
811858
812859pub fn populateMissingMetadata(self: *MachO) !void {
......@@ -818,7 +865,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
818865 .cmdsize = @sizeOf(macho.segment_command_64),
819866 .segname = makeStaticString("__PAGEZERO"),
820867 .vmaddr = 0,
821 .vmsize = 0x1000, // size always set to 4GB
868 .vmsize = 0x100000000, // size always set to 4GB
822869 .fileoff = 0,
823870 .filesize = 0,
824871 .maxprot = 0,
......@@ -829,47 +876,34 @@ pub fn populateMissingMetadata(self: *MachO) !void {
829876 });
830877 self.cmd_table_dirty = true;
831878 }
832 if (self.segment_cmd_index == null) {
833 self.segment_cmd_index = @intCast(u16, self.load_commands.items.len);
879 if (self.text_segment_cmd_index == null) {
880 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
881 const prot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE;
834882 try self.load_commands.append(self.base.allocator, .{
835883 .Segment = .{
836884 .cmd = macho.LC_SEGMENT_64,
837885 .cmdsize = @sizeOf(macho.segment_command_64),
838 .segname = makeStaticString(""),
839 .vmaddr = 0,
886 .segname = makeStaticString("__TEXT"),
887 .vmaddr = 0x100000000, // always starts at 4GB
840888 .vmsize = 0,
841889 .fileoff = 0,
842890 .filesize = 0,
843 .maxprot = 0,
844 .initprot = 0,
891 .maxprot = prot,
892 .initprot = prot,
845893 .nsects = 0,
846894 .flags = 0,
847895 },
848896 });
849897 self.cmd_table_dirty = true;
850898 }
851 if (self.symtab_cmd_index == null) {
852 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
853 try self.load_commands.append(self.base.allocator, .{
854 .Symtab = .{
855 .cmd = macho.LC_SYMTAB,
856 .cmdsize = @sizeOf(macho.symtab_command),
857 .symoff = 0,
858 .nsyms = 0,
859 .stroff = 0,
860 .strsize = 0,
861 },
862 });
863 self.cmd_table_dirty = true;
864 }
865899 if (self.text_section_index == null) {
866900 self.text_section_index = @intCast(u16, self.sections.items.len);
867 const segment = &self.load_commands.items[self.segment_cmd_index.?].Segment;
868 segment.cmdsize += @sizeOf(macho.section_64);
869 segment.nsects += 1;
901 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
902 text_segment.cmdsize += @sizeOf(macho.section_64);
903 text_segment.nsects += 1;
870904
871905 const file_size = self.base.options.program_code_size_hint;
872 const off = @intCast(u32, self.findFreeSpace(file_size, 64));
906 const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000)); // TODO maybe findFreeSpace should return u32 directly?
873907 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
874908
875909 log.debug("found __text section free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
......@@ -877,7 +911,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
877911 try self.sections.append(self.base.allocator, .{
878912 .sectname = makeStaticString("__text"),
879913 .segname = makeStaticString("__TEXT"),
880 .addr = 0,
914 .addr = text_segment.vmaddr + off,
881915 .size = file_size,
882916 .offset = off,
883917 .@"align" = 12,
......@@ -889,32 +923,48 @@ pub fn populateMissingMetadata(self: *MachO) !void {
889923 .reserved3 = 0,
890924 });
891925
892 segment.vmsize += file_size;
893 segment.filesize += file_size;
894 segment.fileoff = off;
926 text_segment.vmsize = file_size + off;
927 text_segment.filesize = file_size + off;
895928
896929 log.debug("initial text section {}\n", .{self.sections.items[self.text_section_index.?]});
897 log.debug("update segment {}\n", .{segment});
930 log.debug("updated text segment {}\n", .{text_segment});
931 }
932 if (self.data_segment_cmd_index == null) {
933 self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
934 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
935 const prot = macho.VM_PROT_READ | macho.VM_PROT_WRITE;
936 try self.load_commands.append(self.base.allocator, .{
937 .Segment = .{
938 .cmd = macho.LC_SEGMENT_64,
939 .cmdsize = @sizeOf(macho.segment_command_64),
940 .segname = makeStaticString("__DATA"),
941 .vmaddr = text_segment.vmaddr + text_segment.vmsize, // TODO this should be found when running findFreeSpace
942 .vmsize = 0,
943 .fileoff = 0,
944 .filesize = 0,
945 .maxprot = prot,
946 .initprot = prot,
947 .nsects = 0,
948 .flags = 0,
949 },
950 });
951 self.cmd_table_dirty = true;
898952 }
899953 if (self.got_section_index == null) {
900954 self.got_section_index = @intCast(u16, self.sections.items.len);
901 const segment = &self.load_commands.items[self.segment_cmd_index.?].Segment;
902 const text_sect = &self.sections.items[self.text_section_index.?];
903 segment.cmdsize += @sizeOf(macho.section_64);
904 segment.nsects += 1;
955 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
956 data_segment.cmdsize += @sizeOf(macho.section_64);
957 data_segment.nsects += 1;
905958
906 const p_align = @sizeOf(u64);
907 const file_size = p_align * self.base.options.symbol_count_hint;
908 const off = @intCast(u32, self.findFreeSpace(file_size, p_align));
959 const file_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
960 const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000));
909961
910962 log.debug("found __got section free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
911963
912 const padding_size = off - text_sect.offset - text_sect.size;
913
914964 try self.sections.append(self.base.allocator, .{
915965 .sectname = makeStaticString("__got"),
916966 .segname = makeStaticString("__DATA"),
917 .addr = text_sect.addr + text_sect.size + padding_size,
967 .addr = data_segment.vmaddr,
918968 .size = file_size,
919969 .offset = off,
920970 .@"align" = 3,
......@@ -926,46 +976,140 @@ pub fn populateMissingMetadata(self: *MachO) !void {
926976 .reserved3 = 0,
927977 });
928978
929 segment.vmsize += file_size + padding_size;
930 segment.filesize += file_size + padding_size;
979 data_segment.vmsize = file_size;
980 data_segment.filesize = file_size;
981 data_segment.fileoff = off;
931982
932983 log.debug("initial got section {}\n", .{self.sections.items[self.got_section_index.?]});
933 log.debug("update segment {}\n", .{segment});
984 log.debug("updated data segment {}\n", .{data_segment});
985 }
986 if (self.linkedit_segment_cmd_index == null) {
987 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
988 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
989 const prot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE;
990 try self.load_commands.append(self.base.allocator, .{
991 .Segment = .{
992 .cmd = macho.LC_SEGMENT_64,
993 .cmdsize = @sizeOf(macho.segment_command_64),
994 .segname = makeStaticString("__LINKEDIT"),
995 .vmaddr = data_segment.vmaddr + data_segment.vmsize, // TODO this should be found when running findFreeSpace
996 .vmsize = 0,
997 .fileoff = 0,
998 .filesize = 0,
999 .maxprot = prot,
1000 .initprot = prot,
1001 .nsects = 0,
1002 .flags = 0,
1003 },
1004 });
1005 self.cmd_table_dirty = true;
1006 }
1007 if (self.symtab_cmd_index == null) {
1008 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
1009 try self.load_commands.append(self.base.allocator, .{
1010 .Symtab = .{
1011 .cmd = macho.LC_SYMTAB,
1012 .cmdsize = @sizeOf(macho.symtab_command),
1013 .symoff = 0,
1014 .nsyms = 0,
1015 .stroff = 0,
1016 .strsize = 0,
1017 },
1018 });
1019 self.cmd_table_dirty = true;
1020 }
1021 if (self.dylinker_cmd_index == null) {
1022 self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len);
1023 const cmdsize = commandSize(@sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH));
1024 try self.load_commands.append(self.base.allocator, .{
1025 .Dylinker = .{
1026 .cmd = macho.LC_LOAD_DYLINKER,
1027 .cmdsize = cmdsize,
1028 .name = @sizeOf(macho.dylinker_command),
1029 },
1030 });
1031 self.cmd_table_dirty = true;
1032 }
1033 if (self.libsystem_cmd_index == null) {
1034 self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len);
1035 const cmdsize = commandSize(@sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH));
1036 // TODO Find a way to work out runtime version from the OS version triple stored in std.Target.
1037 // In the meantime, we're gonna hardcode to the minimum compatibility version of 1.0.0.
1038 const min_version = 0x10000;
1039 const dylib = .{
1040 .name = @sizeOf(macho.dylib_command),
1041 .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files
1042 .current_version = min_version,
1043 .compatibility_version = min_version,
1044 };
1045 try self.load_commands.append(self.base.allocator, .{
1046 .Dylib = .{
1047 .cmd = macho.LC_LOAD_DYLIB,
1048 .cmdsize = cmdsize,
1049 .dylib = dylib,
1050 },
1051 });
1052 self.cmd_table_dirty = true;
1053 }
1054 if (self.main_cmd_index == null) {
1055 self.main_cmd_index = @intCast(u16, self.load_commands.items.len);
1056 try self.load_commands.append(self.base.allocator, .{
1057 .EntryPoint = .{
1058 .cmd = macho.LC_MAIN,
1059 .cmdsize = @sizeOf(macho.entry_point_command),
1060 .entryoff = 0x0,
1061 .stacksize = 0,
1062 },
1063 });
1064 self.cmd_table_dirty = true;
9341065 }
9351066 {
1067 const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
9361068 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
9371069 if (symtab.symoff == 0) {
938 const p_align = @sizeOf(macho.nlist_64);
9391070 const nsyms = self.base.options.symbol_count_hint;
940 const file_size = p_align * nsyms;
941 const off = @intCast(u32, self.findFreeSpace(file_size, p_align));
1071 const file_size = @sizeOf(macho.nlist_64) * nsyms;
1072 const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000));
9421073 log.debug("found symbol table free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
9431074 symtab.symoff = off;
9441075 symtab.nsyms = @intCast(u32, nsyms);
1076
1077 linkedit.vmsize += file_size;
1078 linkedit.fileoff = off;
1079 linkedit.filesize += file_size;
1080
1081 log.debug("updated linkedit segment {}\n", .{linkedit});
9451082 }
9461083 if (symtab.stroff == 0) {
9471084 try self.string_table.append(self.base.allocator, 0);
9481085 const file_size = @intCast(u32, self.string_table.items.len);
949 const off = @intCast(u32, self.findFreeSpace(file_size, 1));
1086 const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000));
9501087 log.debug("found string table free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
9511088 symtab.stroff = off;
9521089 symtab.strsize = file_size;
1090
1091 linkedit.vmsize += file_size;
1092 linkedit.filesize += file_size;
1093
1094 log.debug("updated linkedit segment {}\n", .{linkedit});
9531095 }
9541096 }
9551097}
9561098
9571099fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
958 const segment = &self.load_commands.items[self.segment_cmd_index.?].Segment;
9591100 const text_section = &self.sections.items[self.text_section_index.?];
9601101 const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den;
9611102
9621103 var block_placement: ?*TextBlock = null;
9631104 const addr = blk: {
9641105 if (self.last_text_block) |last| {
965 const last_symbol = self.symbol_table.items[last.symbol_table_index.?];
966 const ideal_capacity = last.size * alloc_num / alloc_den;
967 const ideal_capacity_end_addr = last_symbol.n_value + ideal_capacity;
968 const new_start_addr = mem.alignForwardGeneric(u64, ideal_capacity_end_addr, alignment);
1106 const last_symbol = self.local_symbols.items[last.symbol_table_index.?];
1107 // TODO pad out with NOPs and reenable
1108 // const ideal_capacity = last.size * alloc_num / alloc_den;
1109 // const ideal_capacity_end_addr = last_symbol.n_value + ideal_capacity;
1110 // const new_start_addr = mem.alignForwardGeneric(u64, ideal_capacity_end_addr, alignment);
1111 const end_addr = last_symbol.n_value + last.size;
1112 const new_start_addr = mem.alignForwardGeneric(u64, end_addr, alignment);
9691113 block_placement = last;
9701114 break :blk new_start_addr;
9711115 } else {
......@@ -982,6 +1126,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
9821126 assert(needed_size <= text_capacity); // TODO handle growth
9831127
9841128 self.last_text_block = text_block;
1129 text_section.size = needed_size; // TODO temp until we pad out with NOPs
9851130 }
9861131 text_block.size = new_block_size;
9871132
......@@ -1019,6 +1164,19 @@ fn makeString(self: *MachO, bytes: []const u8) !u32 {
10191164 return @intCast(u32, result);
10201165}
10211166
1167fn getString(self: *MachO, str_off: u32) []const u8 {
1168 assert(str_off < self.string_table.items.len);
1169 return mem.spanZ(@ptrCast([*:0]const u8, self.string_table.items.ptr + str_off));
1170}
1171
1172fn updateString(self: *MachO, old_str_off: u32, new_name: []const u8) !u32 {
1173 const existing_name = self.getString(old_str_off);
1174 if (mem.eql(u8, existing_name, new_name)) {
1175 return old_str_off;
1176 }
1177 return self.makeString(new_name);
1178}
1179
10221180fn alignSize(comptime Int: type, min_size: anytype, alignment: Int) Int {
10231181 const size = @intCast(Int, min_size);
10241182 if (size % alignment == 0) return size;
......@@ -1119,7 +1277,7 @@ fn writeSymbol(self: *MachO, index: usize) !void {
11191277 defer tracy.end();
11201278
11211279 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
1122 const sym = [1]macho.nlist_64{self.symbol_table.items[index]};
1280 const sym = [1]macho.nlist_64{self.local_symbols.items[index]};
11231281 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;
11241282 log.debug("writing symbol {} at 0x{x}\n", .{ sym[0], off });
11251283 try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
......@@ -1135,6 +1293,13 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
11351293 try self.base.file.?.pwriteAll(&buf, off);
11361294}
11371295
1296fn writeAllGlobalSymbols(self: *MachO) !void {
1297 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
1298 const off = symtab.symoff + self.local_symbols.items.len * @sizeOf(macho.nlist_64);
1299 log.debug("writing global symbols from 0x{x} to 0x{x}\n", .{ off, self.global_symbols.items.len + off });
1300 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.global_symbols.items), off);
1301}
1302
11381303/// Writes Mach-O file header.
11391304/// Should be invoked last as it needs up-to-date values of ncmds and sizeof_cmds bookkeeping
11401305/// variables.