| ... | ... | @@ -76,7 +76,6 @@ text_segment_cmd_index: ?u16 = null, |
| 76 | 76 | data_segment_cmd_index: ?u16 = null, |
| 77 | 77 | /// __LINKEDIT segment |
| 78 | 78 | linkedit_segment_cmd_index: ?u16 = null, |
| 79 | | segment_cmd_index: ?u16 = null, |
| 80 | 79 | /// Dyld info |
| 81 | 80 | dyld_info_cmd_index: ?u16 = null, |
| 82 | 81 | /// Symbol table |
| ... | ... | @@ -107,9 +106,10 @@ got_section_index: ?u16 = null, |
| 107 | 106 | |
| 108 | 107 | entry_addr: ?u64 = null, |
| 109 | 108 | |
| 110 | | /// Table of all symbols used. |
| 109 | /// Table of all local symbols used. |
| 111 | 110 | /// Internally references string table for names (which are optional). |
| 112 | | symbol_table: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 111 | local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 112 | global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 113 | 113 | |
| 114 | 114 | /// Table of symbol names aka the string table. |
| 115 | 115 | string_table: std.ArrayListUnmanaged(u8) = .{}, |
| ... | ... | @@ -227,87 +227,82 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 227 | 227 | defer tracy.end(); |
| 228 | 228 | |
| 229 | 229 | 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 | } |
| 251 | 234 | |
| 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(); |
| 294 | 238 | |
| 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); |
| 296 | 251 | |
| 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 }); |
| 299 | 253 | |
| 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); |
| 309 | 305 | } |
| 310 | | |
| 311 | 306 | if (self.entry_addr == null and self.base.options.output_mode == .Exe) { |
| 312 | 307 | log.debug("flushing. no_entry_point_found = true\n", .{}); |
| 313 | 308 | self.error_flags.no_entry_point_found = true; |
| ... | ... | @@ -699,7 +694,8 @@ fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 { |
| 699 | 694 | pub fn deinit(self: *MachO) void { |
| 700 | 695 | self.offset_table.deinit(self.base.allocator); |
| 701 | 696 | 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); |
| 703 | 699 | self.sections.deinit(self.base.allocator); |
| 704 | 700 | self.load_commands.deinit(self.base.allocator); |
| 705 | 701 | } |
| ... | ... | @@ -707,17 +703,17 @@ pub fn deinit(self: *MachO) void { |
| 707 | 703 | pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void { |
| 708 | 704 | if (decl.link.macho.symbol_table_index) |_| return; |
| 709 | 705 | |
| 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); |
| 711 | 707 | try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); |
| 712 | 708 | |
| 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(); |
| 716 | 712 | |
| 717 | 713 | decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len); |
| 718 | 714 | _ = self.offset_table.addOneAssumeCapacity(); |
| 719 | 715 | |
| 720 | | self.symbol_table.items[decl.link.macho.symbol_table_index.?] = .{ |
| 716 | self.local_symbols.items[decl.link.macho.symbol_table_index.?] = .{ |
| 721 | 717 | .n_strx = 0, |
| 722 | 718 | .n_type = 0, |
| 723 | 719 | .n_sect = 0, |
| ... | ... | @@ -749,7 +745,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 749 | 745 | log.debug("generated code {}\n", .{code}); |
| 750 | 746 | |
| 751 | 747 | 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.?]; |
| 753 | 749 | |
| 754 | 750 | const decl_name = mem.spanZ(decl.name); |
| 755 | 751 | const name_str_index = try self.makeString(decl_name); |
| ... | ... | @@ -766,9 +762,6 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 766 | 762 | }; |
| 767 | 763 | self.offset_table.items[decl.link.macho.offset_table_index.?] = addr; |
| 768 | 764 | |
| 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); |
| 772 | 765 | try self.writeSymbol(decl.link.macho.symbol_table_index.?); |
| 773 | 766 | try self.writeOffsetTableEntry(decl.link.macho.offset_table_index.?); |
| 774 | 767 | |
| ... | ... | @@ -778,6 +771,10 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 778 | 771 | log.debug("file_offset 0x{x}\n", .{file_offset}); |
| 779 | 772 | |
| 780 | 773 | 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); |
| 781 | 778 | } |
| 782 | 779 | |
| 783 | 780 | pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {} |
| ... | ... | @@ -791,22 +788,72 @@ pub fn updateDeclExports( |
| 791 | 788 | const tracy = trace(@src()); |
| 792 | 789 | defer tracy.end(); |
| 793 | 790 | |
| 791 | try self.global_symbols.ensureCapacity(self.base.allocator, self.global_symbols.items.len + exports.len); |
| 794 | 792 | 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 | } |
| 804 | 851 | } |
| 805 | 852 | |
| 806 | 853 | pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {} |
| 807 | 854 | |
| 808 | 855 | pub 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; |
| 810 | 857 | } |
| 811 | 858 | |
| 812 | 859 | pub fn populateMissingMetadata(self: *MachO) !void { |
| ... | ... | @@ -818,7 +865,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 818 | 865 | .cmdsize = @sizeOf(macho.segment_command_64), |
| 819 | 866 | .segname = makeStaticString("__PAGEZERO"), |
| 820 | 867 | .vmaddr = 0, |
| 821 | | .vmsize = 0x1000, // size always set to 4GB |
| 868 | .vmsize = 0x100000000, // size always set to 4GB |
| 822 | 869 | .fileoff = 0, |
| 823 | 870 | .filesize = 0, |
| 824 | 871 | .maxprot = 0, |
| ... | ... | @@ -829,47 +876,34 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 829 | 876 | }); |
| 830 | 877 | self.cmd_table_dirty = true; |
| 831 | 878 | } |
| 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; |
| 834 | 882 | try self.load_commands.append(self.base.allocator, .{ |
| 835 | 883 | .Segment = .{ |
| 836 | 884 | .cmd = macho.LC_SEGMENT_64, |
| 837 | 885 | .cmdsize = @sizeOf(macho.segment_command_64), |
| 838 | | .segname = makeStaticString(""), |
| 839 | | .vmaddr = 0, |
| 886 | .segname = makeStaticString("__TEXT"), |
| 887 | .vmaddr = 0x100000000, // always starts at 4GB |
| 840 | 888 | .vmsize = 0, |
| 841 | 889 | .fileoff = 0, |
| 842 | 890 | .filesize = 0, |
| 843 | | .maxprot = 0, |
| 844 | | .initprot = 0, |
| 891 | .maxprot = prot, |
| 892 | .initprot = prot, |
| 845 | 893 | .nsects = 0, |
| 846 | 894 | .flags = 0, |
| 847 | 895 | }, |
| 848 | 896 | }); |
| 849 | 897 | self.cmd_table_dirty = true; |
| 850 | 898 | } |
| 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 | | } |
| 865 | 899 | if (self.text_section_index == null) { |
| 866 | 900 | 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; |
| 870 | 904 | |
| 871 | 905 | 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? |
| 873 | 907 | const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS; |
| 874 | 908 | |
| 875 | 909 | 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 { |
| 877 | 911 | try self.sections.append(self.base.allocator, .{ |
| 878 | 912 | .sectname = makeStaticString("__text"), |
| 879 | 913 | .segname = makeStaticString("__TEXT"), |
| 880 | | .addr = 0, |
| 914 | .addr = text_segment.vmaddr + off, |
| 881 | 915 | .size = file_size, |
| 882 | 916 | .offset = off, |
| 883 | 917 | .@"align" = 12, |
| ... | ... | @@ -889,32 +923,48 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 889 | 923 | .reserved3 = 0, |
| 890 | 924 | }); |
| 891 | 925 | |
| 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; |
| 895 | 928 | |
| 896 | 929 | 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; |
| 898 | 952 | } |
| 899 | 953 | if (self.got_section_index == null) { |
| 900 | 954 | 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; |
| 905 | 958 | |
| 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)); |
| 909 | 961 | |
| 910 | 962 | log.debug("found __got section free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| 911 | 963 | |
| 912 | | const padding_size = off - text_sect.offset - text_sect.size; |
| 913 | | |
| 914 | 964 | try self.sections.append(self.base.allocator, .{ |
| 915 | 965 | .sectname = makeStaticString("__got"), |
| 916 | 966 | .segname = makeStaticString("__DATA"), |
| 917 | | .addr = text_sect.addr + text_sect.size + padding_size, |
| 967 | .addr = data_segment.vmaddr, |
| 918 | 968 | .size = file_size, |
| 919 | 969 | .offset = off, |
| 920 | 970 | .@"align" = 3, |
| ... | ... | @@ -926,46 +976,140 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 926 | 976 | .reserved3 = 0, |
| 927 | 977 | }); |
| 928 | 978 | |
| 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; |
| 931 | 982 | |
| 932 | 983 | 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; |
| 934 | 1065 | } |
| 935 | 1066 | { |
| 1067 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 936 | 1068 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 937 | 1069 | if (symtab.symoff == 0) { |
| 938 | | const p_align = @sizeOf(macho.nlist_64); |
| 939 | 1070 | 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)); |
| 942 | 1073 | log.debug("found symbol table free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| 943 | 1074 | symtab.symoff = off; |
| 944 | 1075 | 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}); |
| 945 | 1082 | } |
| 946 | 1083 | if (symtab.stroff == 0) { |
| 947 | 1084 | try self.string_table.append(self.base.allocator, 0); |
| 948 | 1085 | 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)); |
| 950 | 1087 | log.debug("found string table free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| 951 | 1088 | symtab.stroff = off; |
| 952 | 1089 | symtab.strsize = file_size; |
| 1090 | |
| 1091 | linkedit.vmsize += file_size; |
| 1092 | linkedit.filesize += file_size; |
| 1093 | |
| 1094 | log.debug("updated linkedit segment {}\n", .{linkedit}); |
| 953 | 1095 | } |
| 954 | 1096 | } |
| 955 | 1097 | } |
| 956 | 1098 | |
| 957 | 1099 | fn 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; |
| 959 | 1100 | const text_section = &self.sections.items[self.text_section_index.?]; |
| 960 | 1101 | const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den; |
| 961 | 1102 | |
| 962 | 1103 | var block_placement: ?*TextBlock = null; |
| 963 | 1104 | const addr = blk: { |
| 964 | 1105 | 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); |
| 969 | 1113 | block_placement = last; |
| 970 | 1114 | break :blk new_start_addr; |
| 971 | 1115 | } else { |
| ... | ... | @@ -982,6 +1126,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 982 | 1126 | assert(needed_size <= text_capacity); // TODO handle growth |
| 983 | 1127 | |
| 984 | 1128 | self.last_text_block = text_block; |
| 1129 | text_section.size = needed_size; // TODO temp until we pad out with NOPs |
| 985 | 1130 | } |
| 986 | 1131 | text_block.size = new_block_size; |
| 987 | 1132 | |
| ... | ... | @@ -1019,6 +1164,19 @@ fn makeString(self: *MachO, bytes: []const u8) !u32 { |
| 1019 | 1164 | return @intCast(u32, result); |
| 1020 | 1165 | } |
| 1021 | 1166 | |
| 1167 | fn 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 | |
| 1172 | fn 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 | |
| 1022 | 1180 | fn alignSize(comptime Int: type, min_size: anytype, alignment: Int) Int { |
| 1023 | 1181 | const size = @intCast(Int, min_size); |
| 1024 | 1182 | if (size % alignment == 0) return size; |
| ... | ... | @@ -1119,7 +1277,7 @@ fn writeSymbol(self: *MachO, index: usize) !void { |
| 1119 | 1277 | defer tracy.end(); |
| 1120 | 1278 | |
| 1121 | 1279 | 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]}; |
| 1123 | 1281 | const off = symtab.symoff + @sizeOf(macho.nlist_64) * index; |
| 1124 | 1282 | log.debug("writing symbol {} at 0x{x}\n", .{ sym[0], off }); |
| 1125 | 1283 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off); |
| ... | ... | @@ -1135,6 +1293,13 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void { |
| 1135 | 1293 | try self.base.file.?.pwriteAll(&buf, off); |
| 1136 | 1294 | } |
| 1137 | 1295 | |
| 1296 | fn 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 | |
| 1138 | 1303 | /// Writes Mach-O file header. |
| 1139 | 1304 | /// Should be invoked last as it needs up-to-date values of ncmds and sizeof_cmds bookkeeping |
| 1140 | 1305 | /// variables. |