| ... | @@ -7,6 +7,7 @@ const fs = std.fs; | ... | @@ -7,6 +7,7 @@ const fs = std.fs; |
| 7 | const log = std.log.scoped(.link); | 7 | const log = std.log.scoped(.link); |
| 8 | const macho = std.macho; | 8 | const macho = std.macho; |
| 9 | const codegen = @import("../codegen.zig"); | 9 | const codegen = @import("../codegen.zig"); |
| | 10 | const aarch64 = @import("../codegen/aarch64.zig"); |
| 10 | const math = std.math; | 11 | const math = std.math; |
| 11 | const mem = std.mem; | 12 | const mem = std.mem; |
| 12 | | 13 | |
| ... | @@ -21,6 +22,7 @@ const Cache = @import("../Cache.zig"); | ... | @@ -21,6 +22,7 @@ const Cache = @import("../Cache.zig"); |
| 21 | const target_util = @import("../target.zig"); | 22 | const target_util = @import("../target.zig"); |
| 22 | | 23 | |
| 23 | const Trie = @import("MachO/Trie.zig"); | 24 | const Trie = @import("MachO/Trie.zig"); |
| | 25 | const CodeSignature = @import("MachO/CodeSignature.zig"); |
| 24 | | 26 | |
| 25 | pub const base_tag: File.Tag = File.Tag.macho; | 27 | pub const base_tag: File.Tag = File.Tag.macho; |
| 26 | | 28 | |
| ... | @@ -33,6 +35,8 @@ const LoadCommand = union(enum) { | ... | @@ -33,6 +35,8 @@ const LoadCommand = union(enum) { |
| 33 | Dylinker: macho.dylinker_command, | 35 | Dylinker: macho.dylinker_command, |
| 34 | Dylib: macho.dylib_command, | 36 | Dylib: macho.dylib_command, |
| 35 | EntryPoint: macho.entry_point_command, | 37 | EntryPoint: macho.entry_point_command, |
| | 38 | MinVersion: macho.version_min_command, |
| | 39 | SourceVersion: macho.source_version_command, |
| 36 | | 40 | |
| 37 | pub fn cmdsize(self: LoadCommand) u32 { | 41 | pub fn cmdsize(self: LoadCommand) u32 { |
| 38 | return switch (self) { | 42 | return switch (self) { |
| ... | @@ -44,6 +48,8 @@ const LoadCommand = union(enum) { | ... | @@ -44,6 +48,8 @@ const LoadCommand = union(enum) { |
| 44 | .Dylinker => |x| x.cmdsize, | 48 | .Dylinker => |x| x.cmdsize, |
| 45 | .Dylib => |x| x.cmdsize, | 49 | .Dylib => |x| x.cmdsize, |
| 46 | .EntryPoint => |x| x.cmdsize, | 50 | .EntryPoint => |x| x.cmdsize, |
| | 51 | .MinVersion => |x| x.cmdsize, |
| | 52 | .SourceVersion => |x| x.cmdsize, |
| 47 | }; | 53 | }; |
| 48 | } | 54 | } |
| 49 | | 55 | |
| ... | @@ -57,6 +63,8 @@ const LoadCommand = union(enum) { | ... | @@ -57,6 +63,8 @@ const LoadCommand = union(enum) { |
| 57 | .Dylinker => |cmd| writeGeneric(cmd, file, offset), | 63 | .Dylinker => |cmd| writeGeneric(cmd, file, offset), |
| 58 | .Dylib => |cmd| writeGeneric(cmd, file, offset), | 64 | .Dylib => |cmd| writeGeneric(cmd, file, offset), |
| 59 | .EntryPoint => |cmd| writeGeneric(cmd, file, offset), | 65 | .EntryPoint => |cmd| writeGeneric(cmd, file, offset), |
| | 66 | .MinVersion => |cmd| writeGeneric(cmd, file, offset), |
| | 67 | .SourceVersion => |cmd| writeGeneric(cmd, file, offset), |
| 60 | }; | 68 | }; |
| 61 | } | 69 | } |
| 62 | | 70 | |
| ... | @@ -68,6 +76,10 @@ const LoadCommand = union(enum) { | ... | @@ -68,6 +76,10 @@ const LoadCommand = union(enum) { |
| 68 | | 76 | |
| 69 | base: File, | 77 | base: File, |
| 70 | | 78 | |
| | 79 | /// Page size is dependent on the target cpu architecture. |
| | 80 | /// For x86_64 that's 4KB, whereas for aarch64, that's 16KB. |
| | 81 | page_size: u16, |
| | 82 | |
| 71 | /// Table of all load commands | 83 | /// Table of all load commands |
| 72 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, | 84 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, |
| 73 | /// __PAGEZERO segment | 85 | /// __PAGEZERO segment |
| ... | @@ -96,6 +108,12 @@ function_starts_cmd_index: ?u16 = null, | ... | @@ -96,6 +108,12 @@ function_starts_cmd_index: ?u16 = null, |
| 96 | /// Specifies offset wrt __TEXT segment start address to the main entry point | 108 | /// Specifies offset wrt __TEXT segment start address to the main entry point |
| 97 | /// of the binary. | 109 | /// of the binary. |
| 98 | main_cmd_index: ?u16 = null, | 110 | main_cmd_index: ?u16 = null, |
| | 111 | /// Minimum OS version |
| | 112 | version_min_cmd_index: ?u16 = null, |
| | 113 | /// Source version |
| | 114 | source_version_cmd_index: ?u16 = null, |
| | 115 | /// Code signature |
| | 116 | code_signature_cmd_index: ?u16 = null, |
| 99 | | 117 | |
| 100 | /// Table of all sections | 118 | /// Table of all sections |
| 101 | sections: std.ArrayListUnmanaged(macho.section_64) = .{}, | 119 | sections: std.ArrayListUnmanaged(macho.section_64) = .{}, |
| ... | @@ -108,6 +126,9 @@ got_section_index: ?u16 = null, | ... | @@ -108,6 +126,9 @@ got_section_index: ?u16 = null, |
| 108 | | 126 | |
| 109 | entry_addr: ?u64 = null, | 127 | entry_addr: ?u64 = null, |
| 110 | | 128 | |
| | 129 | // TODO move this into each Segment aggregator |
| | 130 | linkedit_segment_next_offset: ?u32 = null, |
| | 131 | |
| 111 | /// Table of all local symbols | 132 | /// Table of all local symbols |
| 112 | /// Internally references string table for names (which are optional). | 133 | /// Internally references string table for names (which are optional). |
| 113 | local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | 134 | local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| ... | @@ -154,6 +175,22 @@ libsystem_cmd_dirty: bool = false, | ... | @@ -154,6 +175,22 @@ libsystem_cmd_dirty: bool = false, |
| 154 | text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{}, | 175 | text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{}, |
| 155 | /// Pointer to the last allocated text block | 176 | /// Pointer to the last allocated text block |
| 156 | last_text_block: ?*TextBlock = null, | 177 | last_text_block: ?*TextBlock = null, |
| | 178 | /// A list of all PIE fixups required for this run of the linker. |
| | 179 | /// Warning, this is currently NOT thread-safe. See the TODO below. |
| | 180 | /// TODO Move this list inside `updateDecl` where it should be allocated |
| | 181 | /// prior to calling `generateSymbol`, and then immediately deallocated |
| | 182 | /// rather than sitting in the global scope. |
| | 183 | pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{}, |
| | 184 | |
| | 185 | pub const PieFixup = struct { |
| | 186 | /// Target address we wanted to address in absolute terms. |
| | 187 | address: u64, |
| | 188 | /// Where in the byte stream we should perform the fixup. |
| | 189 | start: usize, |
| | 190 | /// The length of the byte stream. For x86_64, this will be |
| | 191 | /// variable. For aarch64, it will be fixed at 4 bytes. |
| | 192 | len: usize, |
| | 193 | }; |
| 157 | | 194 | |
| 158 | /// `alloc_num / alloc_den` is the factor of padding when allocating. | 195 | /// `alloc_num / alloc_den` is the factor of padding when allocating. |
| 159 | const alloc_num = 4; | 196 | const alloc_num = 4; |
| ... | @@ -292,6 +329,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO { | ... | @@ -292,6 +329,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO { |
| 292 | .allocator = gpa, | 329 | .allocator = gpa, |
| 293 | .file = null, | 330 | .file = null, |
| 294 | }, | 331 | }, |
| | 332 | .page_size = if (options.target.cpu.arch == .aarch64) 0x4000 else 0x1000, |
| 295 | }; | 333 | }; |
| 296 | return self; | 334 | return self; |
| 297 | } | 335 | } |
| ... | @@ -312,35 +350,14 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { | ... | @@ -312,35 +350,14 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 312 | const tracy = trace(@src()); | 350 | const tracy = trace(@src()); |
| 313 | defer tracy.end(); | 351 | defer tracy.end(); |
| 314 | | 352 | |
| 315 | // Unfortunately these have to be buffered and done at the end because MachO does not allow | | |
| 316 | // mixing local, global and undefined symbols within a symbol table. | | |
| 317 | try self.writeAllGlobalSymbols(); | | |
| 318 | try self.writeAllUndefSymbols(); | | |
| 319 | | | |
| 320 | try self.writeStringTable(); | | |
| 321 | | | |
| 322 | switch (self.base.options.output_mode) { | 353 | switch (self.base.options.output_mode) { |
| 323 | .Exe => { | 354 | .Exe => { |
| 324 | // Write export trie. | | |
| 325 | try self.writeExportTrie(); | | |
| 326 | if (self.entry_addr) |addr| { | 355 | if (self.entry_addr) |addr| { |
| 327 | // Update LC_MAIN with entry offset. | 356 | // Update LC_MAIN with entry offset. |
| 328 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | 357 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 329 | const main_cmd = &self.load_commands.items[self.main_cmd_index.?].EntryPoint; | 358 | const main_cmd = &self.load_commands.items[self.main_cmd_index.?].EntryPoint; |
| 330 | main_cmd.entryoff = addr - text_segment.vmaddr; | 359 | main_cmd.entryoff = addr - text_segment.vmaddr; |
| 331 | } | 360 | } |
| 332 | { | | |
| 333 | // Update dynamic symbol table. | | |
| 334 | const nlocals = @intCast(u32, self.local_symbols.items.len); | | |
| 335 | const nglobals = @intCast(u32, self.global_symbols.items.len); | | |
| 336 | const nundefs = @intCast(u32, self.undef_symbols.items.len); | | |
| 337 | const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; | | |
| 338 | dysymtab.nlocalsym = nlocals; | | |
| 339 | dysymtab.iextdefsym = nlocals; | | |
| 340 | dysymtab.nextdefsym = nglobals; | | |
| 341 | dysymtab.iundefsym = nlocals + nglobals; | | |
| 342 | dysymtab.nundefsym = nundefs; | | |
| 343 | } | | |
| 344 | if (self.dylinker_cmd_dirty) { | 361 | if (self.dylinker_cmd_dirty) { |
| 345 | // Write path to dyld loader. | 362 | // Write path to dyld loader. |
| 346 | var off: usize = @sizeOf(macho.mach_header_64); | 363 | var off: usize = @sizeOf(macho.mach_header_64); |
| ... | @@ -367,20 +384,22 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { | ... | @@ -367,20 +384,22 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 367 | try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), off); | 384 | try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), off); |
| 368 | self.libsystem_cmd_dirty = false; | 385 | self.libsystem_cmd_dirty = false; |
| 369 | } | 386 | } |
| | 387 | |
| | 388 | try self.writeExportTrie(); |
| | 389 | try self.writeSymbolTable(); |
| | 390 | try self.writeStringTable(); |
| | 391 | |
| | 392 | // Preallocate space for the code signature. |
| | 393 | // We need to do this at this stage so that we have the load commands with proper values |
| | 394 | // written out to the file. |
| | 395 | // The most important here is to have the correct vm and filesize of the __LINKEDIT segment |
| | 396 | // where the code signature goes into. |
| | 397 | try self.writeCodeSignaturePadding(); |
| 370 | }, | 398 | }, |
| 371 | .Obj => {}, | 399 | .Obj => {}, |
| 372 | .Lib => return error.TODOImplementWritingLibFiles, | 400 | .Lib => return error.TODOImplementWritingLibFiles, |
| 373 | } | 401 | } |
| 374 | | 402 | |
| 375 | { | | |
| 376 | // Update symbol table. | | |
| 377 | const nlocals = @intCast(u32, self.local_symbols.items.len); | | |
| 378 | const nglobals = @intCast(u32, self.global_symbols.items.len); | | |
| 379 | const nundefs = @intCast(u32, self.undef_symbols.items.len); | | |
| 380 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | | |
| 381 | symtab.nsyms = nlocals + nglobals + nundefs; | | |
| 382 | } | | |
| 383 | | | |
| 384 | if (self.cmd_table_dirty) { | 403 | if (self.cmd_table_dirty) { |
| 385 | try self.writeCmdHeaders(); | 404 | try self.writeCmdHeaders(); |
| 386 | try self.writeMachOHeader(); | 405 | try self.writeMachOHeader(); |
| ... | @@ -398,6 +417,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { | ... | @@ -398,6 +417,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 398 | assert(!self.cmd_table_dirty); | 417 | assert(!self.cmd_table_dirty); |
| 399 | assert(!self.dylinker_cmd_dirty); | 418 | assert(!self.dylinker_cmd_dirty); |
| 400 | assert(!self.libsystem_cmd_dirty); | 419 | assert(!self.libsystem_cmd_dirty); |
| | 420 | |
| | 421 | switch (self.base.options.output_mode) { |
| | 422 | .Exe, .Lib => try self.writeCodeSignature(), // code signing always comes last |
| | 423 | else => {}, |
| | 424 | } |
| 401 | } | 425 | } |
| 402 | | 426 | |
| 403 | fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | 427 | fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| ... | @@ -823,6 +847,7 @@ fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 { | ... | @@ -823,6 +847,7 @@ fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 { |
| 823 | } | 847 | } |
| 824 | | 848 | |
| 825 | pub fn deinit(self: *MachO) void { | 849 | pub fn deinit(self: *MachO) void { |
| | 850 | self.pie_fixups.deinit(self.base.allocator); |
| 826 | self.text_block_free_list.deinit(self.base.allocator); | 851 | self.text_block_free_list.deinit(self.base.allocator); |
| 827 | self.offset_table.deinit(self.base.allocator); | 852 | self.offset_table.deinit(self.base.allocator); |
| 828 | self.offset_table_free_list.deinit(self.base.allocator); | 853 | self.offset_table_free_list.deinit(self.base.allocator); |
| ... | @@ -955,7 +980,6 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { | ... | @@ -955,7 +980,6 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 955 | log.debug("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, symbol.n_value, vaddr }); | 980 | log.debug("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, symbol.n_value, vaddr }); |
| 956 | if (vaddr != symbol.n_value) { | 981 | if (vaddr != symbol.n_value) { |
| 957 | symbol.n_value = vaddr; | 982 | symbol.n_value = vaddr; |
| 958 | | | |
| 959 | log.debug(" (writing new offset table entry)\n", .{}); | 983 | log.debug(" (writing new offset table entry)\n", .{}); |
| 960 | self.offset_table.items[decl.link.macho.offset_table_index] = vaddr; | 984 | self.offset_table.items[decl.link.macho.offset_table_index] = vaddr; |
| 961 | try self.writeOffsetTableEntry(decl.link.macho.offset_table_index); | 985 | try self.writeOffsetTableEntry(decl.link.macho.offset_table_index); |
| ... | @@ -968,8 +992,6 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { | ... | @@ -968,8 +992,6 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 968 | symbol.n_type = macho.N_SECT; | 992 | symbol.n_type = macho.N_SECT; |
| 969 | symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1; | 993 | symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1; |
| 970 | symbol.n_desc = 0; | 994 | symbol.n_desc = 0; |
| 971 | // TODO this write could be avoided if no fields of the symbol were changed. | | |
| 972 | try self.writeSymbol(decl.link.macho.local_sym_index); | | |
| 973 | } else { | 995 | } else { |
| 974 | const decl_name = mem.spanZ(decl.name); | 996 | const decl_name = mem.spanZ(decl.name); |
| 975 | const name_str_index = try self.makeString(decl_name); | 997 | const name_str_index = try self.makeString(decl_name); |
| ... | @@ -985,15 +1007,32 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { | ... | @@ -985,15 +1007,32 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 985 | .n_value = addr, | 1007 | .n_value = addr, |
| 986 | }; | 1008 | }; |
| 987 | self.offset_table.items[decl.link.macho.offset_table_index] = addr; | 1009 | self.offset_table.items[decl.link.macho.offset_table_index] = addr; |
| 988 | | | |
| 989 | try self.writeSymbol(decl.link.macho.local_sym_index); | | |
| 990 | try self.writeOffsetTableEntry(decl.link.macho.offset_table_index); | 1010 | try self.writeOffsetTableEntry(decl.link.macho.offset_table_index); |
| 991 | } | 1011 | } |
| 992 | | 1012 | |
| | 1013 | // Perform PIE fixups (if any) |
| | 1014 | const got_section = self.sections.items[self.got_section_index.?]; |
| | 1015 | while (self.pie_fixups.popOrNull()) |fixup| { |
| | 1016 | const target_addr = fixup.address; |
| | 1017 | const this_addr = symbol.n_value + fixup.start; |
| | 1018 | switch (self.base.options.target.cpu.arch) { |
| | 1019 | .x86_64 => { |
| | 1020 | const displacement = @intCast(u32, target_addr - this_addr - fixup.len); |
| | 1021 | var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)]; |
| | 1022 | mem.writeIntSliceLittle(u32, placeholder, displacement); |
| | 1023 | }, |
| | 1024 | .aarch64 => { |
| | 1025 | const displacement = @intCast(u27, target_addr - this_addr); |
| | 1026 | var placeholder = code_buffer.items[fixup.start..][0..fixup.len]; |
| | 1027 | mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.b(@intCast(i28, displacement)).toU32()); |
| | 1028 | }, |
| | 1029 | else => unreachable, // unsupported target architecture |
| | 1030 | } |
| | 1031 | } |
| | 1032 | |
| 993 | const text_section = self.sections.items[self.text_section_index.?]; | 1033 | const text_section = self.sections.items[self.text_section_index.?]; |
| 994 | const section_offset = symbol.n_value - text_section.addr; | 1034 | const section_offset = symbol.n_value - text_section.addr; |
| 995 | const file_offset = text_section.offset + section_offset; | 1035 | const file_offset = text_section.offset + section_offset; |
| 996 | | | |
| 997 | try self.base.file.?.pwriteAll(code, file_offset); | 1036 | try self.base.file.?.pwriteAll(code, file_offset); |
| 998 | | 1037 | |
| 999 | // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. | 1038 | // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. |
| ... | @@ -1127,7 +1166,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1127,7 +1166,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1127 | } | 1166 | } |
| 1128 | if (self.text_segment_cmd_index == null) { | 1167 | if (self.text_segment_cmd_index == null) { |
| 1129 | self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | 1168 | self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1130 | const prot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE; | 1169 | const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE; |
| | 1170 | const initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE; |
| 1131 | try self.load_commands.append(self.base.allocator, .{ | 1171 | try self.load_commands.append(self.base.allocator, .{ |
| 1132 | .Segment = .{ | 1172 | .Segment = .{ |
| 1133 | .cmd = macho.LC_SEGMENT_64, | 1173 | .cmd = macho.LC_SEGMENT_64, |
| ... | @@ -1137,8 +1177,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1137,8 +1177,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1137 | .vmsize = 0, | 1177 | .vmsize = 0, |
| 1138 | .fileoff = 0, | 1178 | .fileoff = 0, |
| 1139 | .filesize = 0, | 1179 | .filesize = 0, |
| 1140 | .maxprot = prot, | 1180 | .maxprot = maxprot, |
| 1141 | .initprot = prot, | 1181 | .initprot = initprot, |
| 1142 | .nsects = 0, | 1182 | .nsects = 0, |
| 1143 | .flags = 0, | 1183 | .flags = 0, |
| 1144 | }, | 1184 | }, |
| ... | @@ -1148,11 +1188,10 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1148,11 +1188,10 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1148 | if (self.text_section_index == null) { | 1188 | if (self.text_section_index == null) { |
| 1149 | self.text_section_index = @intCast(u16, self.sections.items.len); | 1189 | self.text_section_index = @intCast(u16, self.sections.items.len); |
| 1150 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | 1190 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1151 | text_segment.cmdsize += @sizeOf(macho.section_64); | | |
| 1152 | text_segment.nsects += 1; | | |
| 1153 | | 1191 | |
| 1154 | const file_size = mem.alignForwardGeneric(u64, self.base.options.program_code_size_hint, 0x1000); | 1192 | const program_code_size_hint = self.base.options.program_code_size_hint; |
| 1155 | const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000)); // TODO maybe findFreeSpace should return u32 directly? | 1193 | const file_size = mem.alignForwardGeneric(u64, program_code_size_hint, self.page_size); |
| | 1194 | const off = @intCast(u32, self.findFreeSpace(file_size, self.page_size)); // TODO maybe findFreeSpace should return u32 directly? |
| 1156 | const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS; | 1195 | const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS; |
| 1157 | | 1196 | |
| 1158 | log.debug("found __text section free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); | 1197 | log.debug("found __text section free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| ... | @@ -1163,7 +1202,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1163,7 +1202,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1163 | .addr = text_segment.vmaddr + off, | 1202 | .addr = text_segment.vmaddr + off, |
| 1164 | .size = file_size, | 1203 | .size = file_size, |
| 1165 | .offset = off, | 1204 | .offset = off, |
| 1166 | .@"align" = 12, // 2^12 = 4096 | 1205 | .@"align" = if (self.base.options.target.cpu.arch == .aarch64) 2 else 0, // 2^2 for aarch64, 2^0 for x86_64 |
| 1167 | .reloff = 0, | 1206 | .reloff = 0, |
| 1168 | .nreloc = 0, | 1207 | .nreloc = 0, |
| 1169 | .flags = flags, | 1208 | .flags = flags, |
| ... | @@ -1174,47 +1213,27 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1174,47 +1213,27 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1174 | | 1213 | |
| 1175 | text_segment.vmsize = file_size + off; // We add off here since __TEXT segment includes everything prior to __text section. | 1214 | text_segment.vmsize = file_size + off; // We add off here since __TEXT segment includes everything prior to __text section. |
| 1176 | text_segment.filesize = file_size + off; | 1215 | text_segment.filesize = file_size + off; |
| 1177 | self.cmd_table_dirty = true; | 1216 | text_segment.cmdsize += @sizeOf(macho.section_64); |
| 1178 | } | 1217 | text_segment.nsects += 1; |
| 1179 | if (self.data_segment_cmd_index == null) { | | |
| 1180 | self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | | |
| 1181 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | | |
| 1182 | const prot = macho.VM_PROT_READ | macho.VM_PROT_WRITE; | | |
| 1183 | try self.load_commands.append(self.base.allocator, .{ | | |
| 1184 | .Segment = .{ | | |
| 1185 | .cmd = macho.LC_SEGMENT_64, | | |
| 1186 | .cmdsize = @sizeOf(macho.segment_command_64), | | |
| 1187 | .segname = makeStaticString("__DATA"), | | |
| 1188 | .vmaddr = text_segment.vmaddr + text_segment.vmsize, | | |
| 1189 | .vmsize = 0, | | |
| 1190 | .fileoff = 0, | | |
| 1191 | .filesize = 0, | | |
| 1192 | .maxprot = prot, | | |
| 1193 | .initprot = prot, | | |
| 1194 | .nsects = 0, | | |
| 1195 | .flags = 0, | | |
| 1196 | }, | | |
| 1197 | }); | | |
| 1198 | self.cmd_table_dirty = true; | 1218 | self.cmd_table_dirty = true; |
| 1199 | } | 1219 | } |
| 1200 | if (self.got_section_index == null) { | 1220 | if (self.got_section_index == null) { |
| 1201 | self.got_section_index = @intCast(u16, self.sections.items.len); | 1221 | self.got_section_index = @intCast(u16, self.sections.items.len); |
| 1202 | const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | 1222 | const text_section = &self.sections.items[self.text_section_index.?]; |
| 1203 | data_segment.cmdsize += @sizeOf(macho.section_64); | | |
| 1204 | data_segment.nsects += 1; | | |
| 1205 | | 1223 | |
| 1206 | const file_size = @sizeOf(u64) * self.base.options.symbol_count_hint; | 1224 | const file_size = @sizeOf(u64) * self.base.options.symbol_count_hint; |
| 1207 | const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000)); | 1225 | // TODO looking for free space should be done *within* a segment it belongs to |
| | 1226 | const off = @intCast(u32, text_section.offset + text_section.size); |
| 1208 | | 1227 | |
| 1209 | log.debug("found __got section free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); | 1228 | log.debug("found __got section free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| 1210 | | 1229 | |
| 1211 | try self.sections.append(self.base.allocator, .{ | 1230 | try self.sections.append(self.base.allocator, .{ |
| 1212 | .sectname = makeStaticString("__got"), | 1231 | .sectname = makeStaticString("__got"), |
| 1213 | .segname = makeStaticString("__DATA"), | 1232 | .segname = makeStaticString("__TEXT"), |
| 1214 | .addr = data_segment.vmaddr, | 1233 | .addr = text_section.addr + text_section.size, |
| 1215 | .size = file_size, | 1234 | .size = file_size, |
| 1216 | .offset = off, | 1235 | .offset = off, |
| 1217 | .@"align" = 3, // 2^3 = 8 | 1236 | .@"align" = if (self.base.options.target.cpu.arch == .aarch64) 2 else 0, |
| 1218 | .reloff = 0, | 1237 | .reloff = 0, |
| 1219 | .nreloc = 0, | 1238 | .nreloc = 0, |
| 1220 | .flags = macho.S_REGULAR, | 1239 | .flags = macho.S_REGULAR, |
| ... | @@ -1223,31 +1242,36 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1223,31 +1242,36 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1223 | .reserved3 = 0, | 1242 | .reserved3 = 0, |
| 1224 | }); | 1243 | }); |
| 1225 | | 1244 | |
| 1226 | const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000); | 1245 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1227 | data_segment.vmsize = segment_size; | 1246 | const added_size = mem.alignForwardGeneric(u64, file_size, self.page_size); |
| 1228 | data_segment.filesize = segment_size; | 1247 | text_segment.vmsize += added_size; |
| 1229 | data_segment.fileoff = off; | 1248 | text_segment.filesize += added_size; |
| | 1249 | text_segment.cmdsize += @sizeOf(macho.section_64); |
| | 1250 | text_segment.nsects += 1; |
| 1230 | self.cmd_table_dirty = true; | 1251 | self.cmd_table_dirty = true; |
| 1231 | } | 1252 | } |
| 1232 | if (self.linkedit_segment_cmd_index == null) { | 1253 | if (self.linkedit_segment_cmd_index == null) { |
| 1233 | self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | 1254 | self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1234 | const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | 1255 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1235 | const prot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE; | 1256 | const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE; |
| | 1257 | const initprot = macho.VM_PROT_READ; |
| | 1258 | const off = text_segment.fileoff + text_segment.filesize; |
| 1236 | try self.load_commands.append(self.base.allocator, .{ | 1259 | try self.load_commands.append(self.base.allocator, .{ |
| 1237 | .Segment = .{ | 1260 | .Segment = .{ |
| 1238 | .cmd = macho.LC_SEGMENT_64, | 1261 | .cmd = macho.LC_SEGMENT_64, |
| 1239 | .cmdsize = @sizeOf(macho.segment_command_64), | 1262 | .cmdsize = @sizeOf(macho.segment_command_64), |
| 1240 | .segname = makeStaticString("__LINKEDIT"), | 1263 | .segname = makeStaticString("__LINKEDIT"), |
| 1241 | .vmaddr = data_segment.vmaddr + data_segment.vmsize, | 1264 | .vmaddr = text_segment.vmaddr + text_segment.vmsize, |
| 1242 | .vmsize = 0, | 1265 | .vmsize = 0, |
| 1243 | .fileoff = 0, | 1266 | .fileoff = off, |
| 1244 | .filesize = 0, | 1267 | .filesize = 0, |
| 1245 | .maxprot = prot, | 1268 | .maxprot = maxprot, |
| 1246 | .initprot = prot, | 1269 | .initprot = initprot, |
| 1247 | .nsects = 0, | 1270 | .nsects = 0, |
| 1248 | .flags = 0, | 1271 | .flags = 0, |
| 1249 | }, | 1272 | }, |
| 1250 | }); | 1273 | }); |
| | 1274 | self.linkedit_segment_next_offset = @intCast(u32, off); |
| 1251 | self.cmd_table_dirty = true; | 1275 | self.cmd_table_dirty = true; |
| 1252 | } | 1276 | } |
| 1253 | if (self.dyld_info_cmd_index == null) { | 1277 | if (self.dyld_info_cmd_index == null) { |
| ... | @@ -1329,8 +1353,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1329,8 +1353,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1329 | self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len); | 1353 | self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1330 | const cmdsize = mem.alignForwardGeneric(u64, @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH), @sizeOf(u64)); | 1354 | const cmdsize = mem.alignForwardGeneric(u64, @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH), @sizeOf(u64)); |
| 1331 | // TODO Find a way to work out runtime version from the OS version triple stored in std.Target. | 1355 | // TODO Find a way to work out runtime version from the OS version triple stored in std.Target. |
| 1332 | // In the meantime, we're gonna hardcode to the minimum compatibility version of 1.0.0. | 1356 | // In the meantime, we're gonna hardcode to the minimum compatibility version of 0.0.0. |
| 1333 | const min_version = 0x10000; | 1357 | const min_version = 0x0; |
| 1334 | const dylib = .{ | 1358 | const dylib = .{ |
| 1335 | .name = @sizeOf(macho.dylib_command), | 1359 | .name = @sizeOf(macho.dylib_command), |
| 1336 | .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files | 1360 | .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files |
| ... | @@ -1359,47 +1383,46 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1359,47 +1383,46 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1359 | }); | 1383 | }); |
| 1360 | self.cmd_table_dirty = true; | 1384 | self.cmd_table_dirty = true; |
| 1361 | } | 1385 | } |
| 1362 | { | 1386 | if (self.version_min_cmd_index == null) { |
| 1363 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | 1387 | self.version_min_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1364 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfo; | 1388 | const cmd: u32 = switch (self.base.options.target.os.tag) { |
| 1365 | if (dyld_info.export_off == 0) { | 1389 | .macos => macho.LC_VERSION_MIN_MACOSX, |
| 1366 | const nsyms = self.base.options.symbol_count_hint; | 1390 | .ios => macho.LC_VERSION_MIN_IPHONEOS, |
| 1367 | const file_size = @sizeOf(u64) * nsyms; | 1391 | .tvos => macho.LC_VERSION_MIN_TVOS, |
| 1368 | const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000)); | 1392 | .watchos => macho.LC_VERSION_MIN_WATCHOS, |
| 1369 | log.debug("found export trie free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); | 1393 | else => unreachable, // wrong OS |
| 1370 | dyld_info.export_off = off; | 1394 | }; |
| 1371 | dyld_info.export_size = @intCast(u32, file_size); | 1395 | const ver = self.base.options.target.os.version_range.semver.min; |
| 1372 | | 1396 | const version = ver.major << 16 | ver.minor << 8 | ver.patch; |
| 1373 | const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000); | 1397 | try self.load_commands.append(self.base.allocator, .{ |
| 1374 | linkedit.vmsize += segment_size; | 1398 | .MinVersion = .{ |
| 1375 | linkedit.fileoff = off; | 1399 | .cmd = cmd, |
| 1376 | } | 1400 | .cmdsize = @sizeOf(macho.version_min_command), |
| | 1401 | .version = version, |
| | 1402 | .sdk = version, |
| | 1403 | }, |
| | 1404 | }); |
| 1377 | } | 1405 | } |
| 1378 | { | 1406 | if (self.source_version_cmd_index == null) { |
| 1379 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | 1407 | self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1380 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | 1408 | try self.load_commands.append(self.base.allocator, .{ |
| 1381 | if (symtab.symoff == 0) { | 1409 | .SourceVersion = .{ |
| 1382 | const nsyms = self.base.options.symbol_count_hint; | 1410 | .cmd = macho.LC_SOURCE_VERSION, |
| 1383 | const file_size = @sizeOf(macho.nlist_64) * nsyms; | 1411 | .cmdsize = @sizeOf(macho.source_version_command), |
| 1384 | const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000)); | 1412 | .version = 0x0, |
| 1385 | log.debug("found symbol table free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); | 1413 | }, |
| 1386 | symtab.symoff = off; | 1414 | }); |
| 1387 | symtab.nsyms = @intCast(u32, nsyms); | 1415 | } |
| 1388 | | 1416 | if (self.code_signature_cmd_index == null) { |
| 1389 | const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000); | 1417 | self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1390 | linkedit.vmsize += segment_size; | 1418 | try self.load_commands.append(self.base.allocator, .{ |
| 1391 | } | 1419 | .LinkeditData = .{ |
| 1392 | if (symtab.stroff == 0) { | 1420 | .cmd = macho.LC_CODE_SIGNATURE, |
| 1393 | try self.string_table.append(self.base.allocator, 0); | 1421 | .cmdsize = @sizeOf(macho.linkedit_data_command), |
| 1394 | const file_size = @intCast(u32, self.string_table.items.len); | 1422 | .dataoff = 0, |
| 1395 | const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000)); | 1423 | .datasize = 0, |
| 1396 | log.debug("found string table free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); | 1424 | }, |
| 1397 | symtab.stroff = off; | 1425 | }); |
| 1398 | symtab.strsize = file_size; | | |
| 1399 | | | |
| 1400 | const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000); | | |
| 1401 | linkedit.vmsize += segment_size; | | |
| 1402 | } | | |
| 1403 | } | 1426 | } |
| 1404 | if (self.dyld_stub_binder_index == null) { | 1427 | if (self.dyld_stub_binder_index == null) { |
| 1405 | self.dyld_stub_binder_index = @intCast(u16, self.undef_symbols.items.len); | 1428 | self.dyld_stub_binder_index = @intCast(u16, self.undef_symbols.items.len); |
| ... | @@ -1626,47 +1649,121 @@ fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u16) u64 { | ... | @@ -1626,47 +1649,121 @@ fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u16) u64 { |
| 1626 | return start; | 1649 | return start; |
| 1627 | } | 1650 | } |
| 1628 | | 1651 | |
| 1629 | fn writeSymbol(self: *MachO, index: usize) !void { | | |
| 1630 | const tracy = trace(@src()); | | |
| 1631 | defer tracy.end(); | | |
| 1632 | | | |
| 1633 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | | |
| 1634 | const sym = [1]macho.nlist_64{self.local_symbols.items[index]}; | | |
| 1635 | const off = symtab.symoff + @sizeOf(macho.nlist_64) * index; | | |
| 1636 | log.debug("writing symbol {} at 0x{x}\n", .{ sym[0], off }); | | |
| 1637 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off); | | |
| 1638 | } | | |
| 1639 | | | |
| 1640 | fn writeOffsetTableEntry(self: *MachO, index: usize) !void { | 1652 | fn writeOffsetTableEntry(self: *MachO, index: usize) !void { |
| 1641 | const sect = &self.sections.items[self.got_section_index.?]; | 1653 | const sect = &self.sections.items[self.got_section_index.?]; |
| 1642 | const endian = self.base.options.target.cpu.arch.endian(); | | |
| 1643 | var buf: [@sizeOf(u64)]u8 = undefined; | | |
| 1644 | mem.writeInt(u64, &buf, self.offset_table.items[index], endian); | | |
| 1645 | const off = sect.offset + @sizeOf(u64) * index; | 1654 | const off = sect.offset + @sizeOf(u64) * index; |
| | 1655 | const vmaddr = sect.addr + @sizeOf(u64) * index; |
| | 1656 | |
| | 1657 | var code: [8]u8 = undefined; |
| | 1658 | switch (self.base.options.target.cpu.arch) { |
| | 1659 | .x86_64 => { |
| | 1660 | const pos_symbol_off = @intCast(u31, vmaddr - self.offset_table.items[index] + 7); |
| | 1661 | const symbol_off = @bitCast(u32, @intCast(i32, pos_symbol_off) * -1); |
| | 1662 | // lea %rax, [rip - disp] |
| | 1663 | code[0] = 0x48; |
| | 1664 | code[1] = 0x8D; |
| | 1665 | code[2] = 0x5; |
| | 1666 | mem.writeIntLittle(u32, code[3..7], symbol_off); |
| | 1667 | // ret |
| | 1668 | code[7] = 0xC3; |
| | 1669 | }, |
| | 1670 | .aarch64 => { |
| | 1671 | const pos_symbol_off = @intCast(u20, vmaddr - self.offset_table.items[index]); |
| | 1672 | const symbol_off = @intCast(i21, pos_symbol_off) * -1; |
| | 1673 | // adr x0, #-disp |
| | 1674 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x0, symbol_off).toU32()); |
| | 1675 | // ret x28 |
| | 1676 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ret(.x28).toU32()); |
| | 1677 | }, |
| | 1678 | else => unreachable, // unsupported target architecture |
| | 1679 | } |
| 1646 | log.debug("writing offset table entry 0x{x} at 0x{x}\n", .{ self.offset_table.items[index], off }); | 1680 | log.debug("writing offset table entry 0x{x} at 0x{x}\n", .{ self.offset_table.items[index], off }); |
| 1647 | try self.base.file.?.pwriteAll(&buf, off); | 1681 | try self.base.file.?.pwriteAll(&code, off); |
| 1648 | } | 1682 | } |
| 1649 | | 1683 | |
| 1650 | fn writeAllGlobalSymbols(self: *MachO) !void { | 1684 | fn writeSymbolTable(self: *MachO) !void { |
| | 1685 | // TODO workout how we can cache these so that we only overwrite symbols that were updated |
| 1651 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | 1686 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 1652 | const off = symtab.symoff + self.local_symbols.items.len * @sizeOf(macho.nlist_64); | 1687 | |
| 1653 | const file_size = self.global_symbols.items.len * @sizeOf(macho.nlist_64); | 1688 | const locals_off = self.linkedit_segment_next_offset.?; |
| 1654 | log.debug("writing global symbols from 0x{x} to 0x{x}\n", .{ off, file_size + off }); | 1689 | const locals_size = self.local_symbols.items.len * @sizeOf(macho.nlist_64); |
| 1655 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.global_symbols.items), off); | 1690 | log.debug("writing local symbols from 0x{x} to 0x{x}\n", .{ locals_off, locals_size + locals_off }); |
| | 1691 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.local_symbols.items), locals_off); |
| | 1692 | |
| | 1693 | const globals_off = locals_off + locals_size; |
| | 1694 | const globals_size = self.global_symbols.items.len * @sizeOf(macho.nlist_64); |
| | 1695 | log.debug("writing global symbols from 0x{x} to 0x{x}\n", .{ globals_off, globals_size + globals_off }); |
| | 1696 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.global_symbols.items), globals_off); |
| | 1697 | |
| | 1698 | const undefs_off = globals_off + globals_size; |
| | 1699 | const undefs_size = self.undef_symbols.items.len * @sizeOf(macho.nlist_64); |
| | 1700 | log.debug("writing undef symbols from 0x{x} to 0x{x}\n", .{ undefs_off, undefs_size + undefs_off }); |
| | 1701 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.undef_symbols.items), undefs_off); |
| | 1702 | |
| | 1703 | // Update symbol table. |
| | 1704 | const nlocals = @intCast(u32, self.local_symbols.items.len); |
| | 1705 | const nglobals = @intCast(u32, self.global_symbols.items.len); |
| | 1706 | const nundefs = @intCast(u32, self.undef_symbols.items.len); |
| | 1707 | symtab.symoff = self.linkedit_segment_next_offset.?; |
| | 1708 | symtab.nsyms = nlocals + nglobals + nundefs; |
| | 1709 | self.linkedit_segment_next_offset = symtab.symoff + symtab.nsyms * @sizeOf(macho.nlist_64); |
| | 1710 | |
| | 1711 | // Update dynamic symbol table. |
| | 1712 | const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; |
| | 1713 | dysymtab.nlocalsym = nlocals; |
| | 1714 | dysymtab.iextdefsym = nlocals; |
| | 1715 | dysymtab.nextdefsym = nglobals; |
| | 1716 | dysymtab.iundefsym = nlocals + nglobals; |
| | 1717 | dysymtab.nundefsym = nundefs; |
| | 1718 | |
| | 1719 | // Advance size of __LINKEDIT segment |
| | 1720 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| | 1721 | linkedit.filesize += symtab.nsyms * @sizeOf(macho.nlist_64); |
| | 1722 | if (linkedit.vmsize < linkedit.filesize) { |
| | 1723 | linkedit.vmsize = mem.alignForwardGeneric(u64, linkedit.filesize, self.page_size); |
| | 1724 | } |
| | 1725 | self.cmd_table_dirty = true; |
| 1656 | } | 1726 | } |
| 1657 | | 1727 | |
| 1658 | fn writeAllUndefSymbols(self: *MachO) !void { | 1728 | fn writeCodeSignaturePadding(self: *MachO) !void { |
| 1659 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | 1729 | const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData; |
| 1660 | const nlocals = self.local_symbols.items.len; | 1730 | const fileoff = self.linkedit_segment_next_offset.?; |
| 1661 | const nglobals = self.global_symbols.items.len; | 1731 | const datasize: u32 = 0x1000; // TODO Calculate the expected size of the signature. |
| 1662 | const off = symtab.symoff + (nlocals + nglobals) * @sizeOf(macho.nlist_64); | 1732 | code_sig_cmd.dataoff = fileoff; |
| 1663 | const file_size = self.undef_symbols.items.len * @sizeOf(macho.nlist_64); | 1733 | code_sig_cmd.datasize = datasize; |
| 1664 | log.debug("writing undef symbols from 0x{x} to 0x{x}\n", .{ off, file_size + off }); | 1734 | |
| 1665 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.undef_symbols.items), off); | 1735 | self.linkedit_segment_next_offset = fileoff + datasize; |
| | 1736 | // Advance size of __LINKEDIT segment |
| | 1737 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| | 1738 | linkedit.filesize += datasize; |
| | 1739 | if (linkedit.vmsize < linkedit.filesize) { |
| | 1740 | linkedit.vmsize = mem.alignForwardGeneric(u64, linkedit.filesize, self.page_size); |
| | 1741 | } |
| | 1742 | log.debug("writing code signature padding from 0x{x} to 0x{x}\n", .{ fileoff, fileoff + datasize }); |
| | 1743 | // Pad out the space. We need to do this to calculate valid hashes for everything in the file |
| | 1744 | // except for code signature data. |
| | 1745 | try self.base.file.?.pwriteAll(&[_]u8{0}, fileoff + datasize - 1); |
| | 1746 | } |
| | 1747 | |
| | 1748 | fn writeCodeSignature(self: *MachO) !void { |
| | 1749 | const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData; |
| | 1750 | var code_sig = CodeSignature.init(self.base.allocator); |
| | 1751 | defer code_sig.deinit(); |
| | 1752 | |
| | 1753 | try code_sig.calcAdhocSignature(self); |
| | 1754 | |
| | 1755 | var buffer = try self.base.allocator.alloc(u8, code_sig.size()); |
| | 1756 | defer self.base.allocator.free(buffer); |
| | 1757 | |
| | 1758 | code_sig.write(buffer); |
| | 1759 | |
| | 1760 | log.debug("writing code signature from 0x{x} to 0x{x}\n", .{ code_sig_cmd.dataoff, code_sig_cmd.dataoff + buffer.len }); |
| | 1761 | |
| | 1762 | try self.base.file.?.pwriteAll(buffer, code_sig_cmd.dataoff); |
| 1666 | } | 1763 | } |
| 1667 | | 1764 | |
| 1668 | fn writeExportTrie(self: *MachO) !void { | 1765 | fn writeExportTrie(self: *MachO) !void { |
| 1669 | if (self.global_symbols.items.len == 0) return; // No exports, nothing to do. | 1766 | if (self.global_symbols.items.len == 0) return; |
| 1670 | | 1767 | |
| 1671 | var trie: Trie = .{}; | 1768 | var trie: Trie = .{}; |
| 1672 | defer trie.deinit(self.base.allocator); | 1769 | defer trie.deinit(self.base.allocator); |
| ... | @@ -1689,28 +1786,51 @@ fn writeExportTrie(self: *MachO) !void { | ... | @@ -1689,28 +1786,51 @@ fn writeExportTrie(self: *MachO) !void { |
| 1689 | try trie.writeULEB128Mem(self.base.allocator, &buffer); | 1786 | try trie.writeULEB128Mem(self.base.allocator, &buffer); |
| 1690 | | 1787 | |
| 1691 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfo; | 1788 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfo; |
| | 1789 | const export_size = @intCast(u32, mem.alignForward(buffer.items.len, @sizeOf(u64))); |
| | 1790 | dyld_info.export_off = self.linkedit_segment_next_offset.?; |
| | 1791 | dyld_info.export_size = export_size; |
| | 1792 | |
| | 1793 | log.debug("writing export trie from 0x{x} to 0x{x}\n", .{ dyld_info.export_off, dyld_info.export_off + export_size }); |
| | 1794 | |
| | 1795 | if (export_size > buffer.items.len) { |
| | 1796 | // Pad out to align(8). |
| | 1797 | try self.base.file.?.pwriteAll(&[_]u8{0}, dyld_info.export_off + export_size); |
| | 1798 | } |
| 1692 | try self.base.file.?.pwriteAll(buffer.items, dyld_info.export_off); | 1799 | try self.base.file.?.pwriteAll(buffer.items, dyld_info.export_off); |
| | 1800 | |
| | 1801 | self.linkedit_segment_next_offset = dyld_info.export_off + dyld_info.export_size; |
| | 1802 | // Advance size of __LINKEDIT segment |
| | 1803 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| | 1804 | linkedit.filesize += dyld_info.export_size; |
| | 1805 | if (linkedit.vmsize < linkedit.filesize) { |
| | 1806 | linkedit.vmsize = mem.alignForwardGeneric(u64, linkedit.filesize, self.page_size); |
| | 1807 | } |
| | 1808 | self.cmd_table_dirty = true; |
| 1693 | } | 1809 | } |
| 1694 | | 1810 | |
| 1695 | fn writeStringTable(self: *MachO) !void { | 1811 | fn writeStringTable(self: *MachO) !void { |
| 1696 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | 1812 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 1697 | const allocated_size = self.allocatedSize(symtab.stroff); | | |
| 1698 | const needed_size = self.string_table.items.len; | 1813 | const needed_size = self.string_table.items.len; |
| 1699 | | 1814 | |
| 1700 | if (needed_size > allocated_size) { | 1815 | symtab.stroff = self.linkedit_segment_next_offset.?; |
| 1701 | symtab.strsize = 0; | 1816 | symtab.strsize = @intCast(u32, mem.alignForward(needed_size, @sizeOf(u64))); |
| 1702 | symtab.stroff = @intCast(u32, self.findFreeSpace(needed_size, 1)); | | |
| 1703 | } | | |
| 1704 | symtab.strsize = @intCast(u32, needed_size); | | |
| 1705 | | 1817 | |
| 1706 | log.debug("writing string table from 0x{x} to 0x{x}\n", .{ symtab.stroff, symtab.stroff + symtab.strsize }); | 1818 | log.debug("writing string table from 0x{x} to 0x{x}\n", .{ symtab.stroff, symtab.stroff + symtab.strsize }); |
| 1707 | | 1819 | |
| | 1820 | if (symtab.strsize > needed_size) { |
| | 1821 | // Pad out to align(8); |
| | 1822 | try self.base.file.?.pwriteAll(&[_]u8{0}, symtab.stroff + symtab.strsize); |
| | 1823 | } |
| 1708 | try self.base.file.?.pwriteAll(self.string_table.items, symtab.stroff); | 1824 | try self.base.file.?.pwriteAll(self.string_table.items, symtab.stroff); |
| 1709 | | 1825 | |
| 1710 | // TODO rework how we preallocate space for the entire __LINKEDIT segment instead of | 1826 | self.linkedit_segment_next_offset = symtab.stroff + symtab.strsize; |
| 1711 | // doing dynamic updates like this. | 1827 | // Advance size of __LINKEDIT segment |
| 1712 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | 1828 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 1713 | linkedit.filesize = symtab.stroff + symtab.strsize - linkedit.fileoff; | 1829 | linkedit.filesize += symtab.strsize; |
| | 1830 | if (linkedit.vmsize < linkedit.filesize) { |
| | 1831 | linkedit.vmsize = mem.alignForwardGeneric(u64, linkedit.filesize, self.page_size); |
| | 1832 | } |
| | 1833 | self.cmd_table_dirty = true; |
| 1714 | } | 1834 | } |
| 1715 | | 1835 | |
| 1716 | fn writeCmdHeaders(self: *MachO) !void { | 1836 | fn writeCmdHeaders(self: *MachO) !void { |
| ... | @@ -1726,7 +1846,6 @@ fn writeCmdHeaders(self: *MachO) !void { | ... | @@ -1726,7 +1846,6 @@ fn writeCmdHeaders(self: *MachO) !void { |
| 1726 | last_cmd_offset += cmd.cmdsize(); | 1846 | last_cmd_offset += cmd.cmdsize(); |
| 1727 | } | 1847 | } |
| 1728 | { | 1848 | { |
| 1729 | // write __text section header | | |
| 1730 | const off = if (self.text_segment_cmd_index) |text_segment_index| blk: { | 1849 | const off = if (self.text_segment_cmd_index) |text_segment_index| blk: { |
| 1731 | var i: usize = 0; | 1850 | var i: usize = 0; |
| 1732 | var cmdsize: usize = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64); | 1851 | var cmdsize: usize = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64); |
| ... | @@ -1739,27 +1858,11 @@ fn writeCmdHeaders(self: *MachO) !void { | ... | @@ -1739,27 +1858,11 @@ fn writeCmdHeaders(self: *MachO) !void { |
| 1739 | // only one, noname segment to append this section header to. | 1858 | // only one, noname segment to append this section header to. |
| 1740 | return error.TODOImplementWritingObjFiles; | 1859 | return error.TODOImplementWritingObjFiles; |
| 1741 | }; | 1860 | }; |
| 1742 | const idx = self.text_section_index.?; | 1861 | // write sections belonging to __TEXT segment |
| 1743 | log.debug("writing text section header at 0x{x}\n", .{off}); | 1862 | // TODO section indices should belong to each Segment, and we should iterate dynamically. |
| 1744 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[idx .. idx + 1]), off); | 1863 | const id = self.text_section_index.?; |
| 1745 | } | 1864 | log.debug("writing __TEXT section headers at 0x{x}\n", .{off}); |
| 1746 | { | 1865 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[id .. id + 2]), off); |
| 1747 | // write __got section header | | |
| 1748 | const off = if (self.data_segment_cmd_index) |data_segment_index| blk: { | | |
| 1749 | var i: usize = 0; | | |
| 1750 | var cmdsize: usize = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64); | | |
| 1751 | while (i < data_segment_index) : (i += 1) { | | |
| 1752 | cmdsize += self.load_commands.items[i].cmdsize(); | | |
| 1753 | } | | |
| 1754 | break :blk cmdsize; | | |
| 1755 | } else { | | |
| 1756 | // If we've landed in here, we are building a MachO object file, so we have | | |
| 1757 | // only one, noname segment to append this section header to. | | |
| 1758 | return error.TODOImplementWritingObjFiles; | | |
| 1759 | }; | | |
| 1760 | const idx = self.got_section_index.?; | | |
| 1761 | log.debug("writing got section header at 0x{x}\n", .{off}); | | |
| 1762 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[idx .. idx + 1]), off); | | |
| 1763 | } | 1866 | } |
| 1764 | } | 1867 | } |
| 1765 | | 1868 | |
| ... | @@ -1807,8 +1910,14 @@ fn writeMachOHeader(self: *MachO) !void { | ... | @@ -1807,8 +1910,14 @@ fn writeMachOHeader(self: *MachO) !void { |
| 1807 | | 1910 | |
| 1808 | hdr.sizeofcmds = sizeofcmds; | 1911 | hdr.sizeofcmds = sizeofcmds; |
| 1809 | | 1912 | |
| 1810 | // TODO should these be set to something else? | 1913 | switch (self.base.options.output_mode) { |
| 1811 | hdr.flags = 0; | 1914 | .Exe => { |
| | 1915 | hdr.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE; |
| | 1916 | }, |
| | 1917 | else => { |
| | 1918 | hdr.flags = 0; |
| | 1919 | }, |
| | 1920 | } |
| 1812 | hdr.reserved = 0; | 1921 | hdr.reserved = 0; |
| 1813 | | 1922 | |
| 1814 | log.debug("writing Mach-O header {}\n", .{hdr}); | 1923 | log.debug("writing Mach-O header {}\n", .{hdr}); |