| ... | ... | @@ -7,6 +7,7 @@ const fs = std.fs; |
| 7 | 7 | const log = std.log.scoped(.link); |
| 8 | 8 | const macho = std.macho; |
| 9 | 9 | const codegen = @import("../codegen.zig"); |
| 10 | const aarch64 = @import("../codegen/aarch64.zig"); |
| 10 | 11 | const math = std.math; |
| 11 | 12 | const mem = std.mem; |
| 12 | 13 | |
| ... | ... | @@ -21,6 +22,7 @@ const Cache = @import("../Cache.zig"); |
| 21 | 22 | const target_util = @import("../target.zig"); |
| 22 | 23 | |
| 23 | 24 | const Trie = @import("MachO/Trie.zig"); |
| 25 | const CodeSignature = @import("MachO/CodeSignature.zig"); |
| 24 | 26 | |
| 25 | 27 | pub const base_tag: File.Tag = File.Tag.macho; |
| 26 | 28 | |
| ... | ... | @@ -33,6 +35,8 @@ const LoadCommand = union(enum) { |
| 33 | 35 | Dylinker: macho.dylinker_command, |
| 34 | 36 | Dylib: macho.dylib_command, |
| 35 | 37 | EntryPoint: macho.entry_point_command, |
| 38 | MinVersion: macho.version_min_command, |
| 39 | SourceVersion: macho.source_version_command, |
| 36 | 40 | |
| 37 | 41 | pub fn cmdsize(self: LoadCommand) u32 { |
| 38 | 42 | return switch (self) { |
| ... | ... | @@ -44,6 +48,8 @@ const LoadCommand = union(enum) { |
| 44 | 48 | .Dylinker => |x| x.cmdsize, |
| 45 | 49 | .Dylib => |x| x.cmdsize, |
| 46 | 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 | 63 | .Dylinker => |cmd| writeGeneric(cmd, file, offset), |
| 58 | 64 | .Dylib => |cmd| writeGeneric(cmd, file, offset), |
| 59 | 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 | 76 | |
| 69 | 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 | 83 | /// Table of all load commands |
| 72 | 84 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, |
| 73 | 85 | /// __PAGEZERO segment |
| ... | ... | @@ -96,6 +108,12 @@ function_starts_cmd_index: ?u16 = null, |
| 96 | 108 | /// Specifies offset wrt __TEXT segment start address to the main entry point |
| 97 | 109 | /// of the binary. |
| 98 | 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 | 118 | /// Table of all sections |
| 101 | 119 | sections: std.ArrayListUnmanaged(macho.section_64) = .{}, |
| ... | ... | @@ -108,6 +126,9 @@ got_section_index: ?u16 = null, |
| 108 | 126 | |
| 109 | 127 | entry_addr: ?u64 = null, |
| 110 | 128 | |
| 129 | // TODO move this into each Segment aggregator |
| 130 | linkedit_segment_next_offset: ?u32 = null, |
| 131 | |
| 111 | 132 | /// Table of all local symbols |
| 112 | 133 | /// Internally references string table for names (which are optional). |
| 113 | 134 | local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| ... | ... | @@ -154,6 +175,22 @@ libsystem_cmd_dirty: bool = false, |
| 154 | 175 | text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{}, |
| 155 | 176 | /// Pointer to the last allocated text block |
| 156 | 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 | 195 | /// `alloc_num / alloc_den` is the factor of padding when allocating. |
| 159 | 196 | const alloc_num = 4; |
| ... | ... | @@ -292,6 +329,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO { |
| 292 | 329 | .allocator = gpa, |
| 293 | 330 | .file = null, |
| 294 | 331 | }, |
| 332 | .page_size = if (options.target.cpu.arch == .aarch64) 0x4000 else 0x1000, |
| 295 | 333 | }; |
| 296 | 334 | return self; |
| 297 | 335 | } |
| ... | ... | @@ -312,35 +350,14 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 312 | 350 | const tracy = trace(@src()); |
| 313 | 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 | 353 | switch (self.base.options.output_mode) { |
| 323 | 354 | .Exe => { |
| 324 | | // Write export trie. |
| 325 | | try self.writeExportTrie(); |
| 326 | 355 | if (self.entry_addr) |addr| { |
| 327 | 356 | // Update LC_MAIN with entry offset. |
| 328 | 357 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 329 | 358 | const main_cmd = &self.load_commands.items[self.main_cmd_index.?].EntryPoint; |
| 330 | 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 | 361 | if (self.dylinker_cmd_dirty) { |
| 345 | 362 | // Write path to dyld loader. |
| 346 | 363 | var off: usize = @sizeOf(macho.mach_header_64); |
| ... | ... | @@ -367,20 +384,22 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 367 | 384 | try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), off); |
| 368 | 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 | 399 | .Obj => {}, |
| 372 | 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 | 403 | if (self.cmd_table_dirty) { |
| 385 | 404 | try self.writeCmdHeaders(); |
| 386 | 405 | try self.writeMachOHeader(); |
| ... | ... | @@ -398,6 +417,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 398 | 417 | assert(!self.cmd_table_dirty); |
| 399 | 418 | assert(!self.dylinker_cmd_dirty); |
| 400 | 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 | 427 | fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| ... | ... | @@ -823,6 +847,7 @@ fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 { |
| 823 | 847 | } |
| 824 | 848 | |
| 825 | 849 | pub fn deinit(self: *MachO) void { |
| 850 | self.pie_fixups.deinit(self.base.allocator); |
| 826 | 851 | self.text_block_free_list.deinit(self.base.allocator); |
| 827 | 852 | self.offset_table.deinit(self.base.allocator); |
| 828 | 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 | 980 | log.debug("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, symbol.n_value, vaddr }); |
| 956 | 981 | if (vaddr != symbol.n_value) { |
| 957 | 982 | symbol.n_value = vaddr; |
| 958 | | |
| 959 | 983 | log.debug(" (writing new offset table entry)\n", .{}); |
| 960 | 984 | self.offset_table.items[decl.link.macho.offset_table_index] = vaddr; |
| 961 | 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 | 992 | symbol.n_type = macho.N_SECT; |
| 969 | 993 | symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1; |
| 970 | 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 | 995 | } else { |
| 974 | 996 | const decl_name = mem.spanZ(decl.name); |
| 975 | 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 | 1007 | .n_value = addr, |
| 986 | 1008 | }; |
| 987 | 1009 | self.offset_table.items[decl.link.macho.offset_table_index] = addr; |
| 988 | | |
| 989 | | try self.writeSymbol(decl.link.macho.local_sym_index); |
| 990 | 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 | 1033 | const text_section = self.sections.items[self.text_section_index.?]; |
| 994 | 1034 | const section_offset = symbol.n_value - text_section.addr; |
| 995 | 1035 | const file_offset = text_section.offset + section_offset; |
| 996 | | |
| 997 | 1036 | try self.base.file.?.pwriteAll(code, file_offset); |
| 998 | 1037 | |
| 999 | 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 | 1166 | } |
| 1128 | 1167 | if (self.text_segment_cmd_index == null) { |
| 1129 | 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 | 1171 | try self.load_commands.append(self.base.allocator, .{ |
| 1132 | 1172 | .Segment = .{ |
| 1133 | 1173 | .cmd = macho.LC_SEGMENT_64, |
| ... | ... | @@ -1137,8 +1177,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1137 | 1177 | .vmsize = 0, |
| 1138 | 1178 | .fileoff = 0, |
| 1139 | 1179 | .filesize = 0, |
| 1140 | | .maxprot = prot, |
| 1141 | | .initprot = prot, |
| 1180 | .maxprot = maxprot, |
| 1181 | .initprot = initprot, |
| 1142 | 1182 | .nsects = 0, |
| 1143 | 1183 | .flags = 0, |
| 1144 | 1184 | }, |
| ... | ... | @@ -1148,11 +1188,10 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1148 | 1188 | if (self.text_section_index == null) { |
| 1149 | 1189 | self.text_section_index = @intCast(u16, self.sections.items.len); |
| 1150 | 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); |
| 1155 | | const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000)); // TODO maybe findFreeSpace should return u32 directly? |
| 1192 | const program_code_size_hint = self.base.options.program_code_size_hint; |
| 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 | 1195 | const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS; |
| 1157 | 1196 | |
| 1158 | 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 | 1202 | .addr = text_segment.vmaddr + off, |
| 1164 | 1203 | .size = file_size, |
| 1165 | 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 | 1206 | .reloff = 0, |
| 1168 | 1207 | .nreloc = 0, |
| 1169 | 1208 | .flags = flags, |
| ... | ... | @@ -1174,47 +1213,27 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1174 | 1213 | |
| 1175 | 1214 | text_segment.vmsize = file_size + off; // We add off here since __TEXT segment includes everything prior to __text section. |
| 1176 | 1215 | text_segment.filesize = file_size + off; |
| 1177 | | self.cmd_table_dirty = true; |
| 1178 | | } |
| 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 | | }); |
| 1216 | text_segment.cmdsize += @sizeOf(macho.section_64); |
| 1217 | text_segment.nsects += 1; |
| 1198 | 1218 | self.cmd_table_dirty = true; |
| 1199 | 1219 | } |
| 1200 | 1220 | if (self.got_section_index == null) { |
| 1201 | 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; |
| 1203 | | data_segment.cmdsize += @sizeOf(macho.section_64); |
| 1204 | | data_segment.nsects += 1; |
| 1222 | const text_section = &self.sections.items[self.text_section_index.?]; |
| 1205 | 1223 | |
| 1206 | 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 | 1228 | log.debug("found __got section free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| 1210 | 1229 | |
| 1211 | 1230 | try self.sections.append(self.base.allocator, .{ |
| 1212 | 1231 | .sectname = makeStaticString("__got"), |
| 1213 | | .segname = makeStaticString("__DATA"), |
| 1214 | | .addr = data_segment.vmaddr, |
| 1232 | .segname = makeStaticString("__TEXT"), |
| 1233 | .addr = text_section.addr + text_section.size, |
| 1215 | 1234 | .size = file_size, |
| 1216 | 1235 | .offset = off, |
| 1217 | | .@"align" = 3, // 2^3 = 8 |
| 1236 | .@"align" = if (self.base.options.target.cpu.arch == .aarch64) 2 else 0, |
| 1218 | 1237 | .reloff = 0, |
| 1219 | 1238 | .nreloc = 0, |
| 1220 | 1239 | .flags = macho.S_REGULAR, |
| ... | ... | @@ -1223,31 +1242,36 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1223 | 1242 | .reserved3 = 0, |
| 1224 | 1243 | }); |
| 1225 | 1244 | |
| 1226 | | const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000); |
| 1227 | | data_segment.vmsize = segment_size; |
| 1228 | | data_segment.filesize = segment_size; |
| 1229 | | data_segment.fileoff = off; |
| 1245 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1246 | const added_size = mem.alignForwardGeneric(u64, file_size, self.page_size); |
| 1247 | text_segment.vmsize += added_size; |
| 1248 | text_segment.filesize += added_size; |
| 1249 | text_segment.cmdsize += @sizeOf(macho.section_64); |
| 1250 | text_segment.nsects += 1; |
| 1230 | 1251 | self.cmd_table_dirty = true; |
| 1231 | 1252 | } |
| 1232 | 1253 | if (self.linkedit_segment_cmd_index == null) { |
| 1233 | 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; |
| 1235 | | const prot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE; |
| 1255 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 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 | 1259 | try self.load_commands.append(self.base.allocator, .{ |
| 1237 | 1260 | .Segment = .{ |
| 1238 | 1261 | .cmd = macho.LC_SEGMENT_64, |
| 1239 | 1262 | .cmdsize = @sizeOf(macho.segment_command_64), |
| 1240 | 1263 | .segname = makeStaticString("__LINKEDIT"), |
| 1241 | | .vmaddr = data_segment.vmaddr + data_segment.vmsize, |
| 1264 | .vmaddr = text_segment.vmaddr + text_segment.vmsize, |
| 1242 | 1265 | .vmsize = 0, |
| 1243 | | .fileoff = 0, |
| 1266 | .fileoff = off, |
| 1244 | 1267 | .filesize = 0, |
| 1245 | | .maxprot = prot, |
| 1246 | | .initprot = prot, |
| 1268 | .maxprot = maxprot, |
| 1269 | .initprot = initprot, |
| 1247 | 1270 | .nsects = 0, |
| 1248 | 1271 | .flags = 0, |
| 1249 | 1272 | }, |
| 1250 | 1273 | }); |
| 1274 | self.linkedit_segment_next_offset = @intCast(u32, off); |
| 1251 | 1275 | self.cmd_table_dirty = true; |
| 1252 | 1276 | } |
| 1253 | 1277 | if (self.dyld_info_cmd_index == null) { |
| ... | ... | @@ -1329,8 +1353,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1329 | 1353 | self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1330 | 1354 | const cmdsize = mem.alignForwardGeneric(u64, @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH), @sizeOf(u64)); |
| 1331 | 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. |
| 1333 | | const min_version = 0x10000; |
| 1356 | // In the meantime, we're gonna hardcode to the minimum compatibility version of 0.0.0. |
| 1357 | const min_version = 0x0; |
| 1334 | 1358 | const dylib = .{ |
| 1335 | 1359 | .name = @sizeOf(macho.dylib_command), |
| 1336 | 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 | 1383 | }); |
| 1360 | 1384 | self.cmd_table_dirty = true; |
| 1361 | 1385 | } |
| 1362 | | { |
| 1363 | | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 1364 | | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfo; |
| 1365 | | if (dyld_info.export_off == 0) { |
| 1366 | | const nsyms = self.base.options.symbol_count_hint; |
| 1367 | | const file_size = @sizeOf(u64) * nsyms; |
| 1368 | | const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000)); |
| 1369 | | log.debug("found export trie free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| 1370 | | dyld_info.export_off = off; |
| 1371 | | dyld_info.export_size = @intCast(u32, file_size); |
| 1372 | | |
| 1373 | | const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000); |
| 1374 | | linkedit.vmsize += segment_size; |
| 1375 | | linkedit.fileoff = off; |
| 1376 | | } |
| 1386 | if (self.version_min_cmd_index == null) { |
| 1387 | self.version_min_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1388 | const cmd: u32 = switch (self.base.options.target.os.tag) { |
| 1389 | .macos => macho.LC_VERSION_MIN_MACOSX, |
| 1390 | .ios => macho.LC_VERSION_MIN_IPHONEOS, |
| 1391 | .tvos => macho.LC_VERSION_MIN_TVOS, |
| 1392 | .watchos => macho.LC_VERSION_MIN_WATCHOS, |
| 1393 | else => unreachable, // wrong OS |
| 1394 | }; |
| 1395 | const ver = self.base.options.target.os.version_range.semver.min; |
| 1396 | const version = ver.major << 16 | ver.minor << 8 | ver.patch; |
| 1397 | try self.load_commands.append(self.base.allocator, .{ |
| 1398 | .MinVersion = .{ |
| 1399 | .cmd = cmd, |
| 1400 | .cmdsize = @sizeOf(macho.version_min_command), |
| 1401 | .version = version, |
| 1402 | .sdk = version, |
| 1403 | }, |
| 1404 | }); |
| 1377 | 1405 | } |
| 1378 | | { |
| 1379 | | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 1380 | | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 1381 | | if (symtab.symoff == 0) { |
| 1382 | | const nsyms = self.base.options.symbol_count_hint; |
| 1383 | | const file_size = @sizeOf(macho.nlist_64) * nsyms; |
| 1384 | | const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000)); |
| 1385 | | log.debug("found symbol table free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| 1386 | | symtab.symoff = off; |
| 1387 | | symtab.nsyms = @intCast(u32, nsyms); |
| 1388 | | |
| 1389 | | const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000); |
| 1390 | | linkedit.vmsize += segment_size; |
| 1391 | | } |
| 1392 | | if (symtab.stroff == 0) { |
| 1393 | | try self.string_table.append(self.base.allocator, 0); |
| 1394 | | const file_size = @intCast(u32, self.string_table.items.len); |
| 1395 | | const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000)); |
| 1396 | | log.debug("found string table free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| 1397 | | symtab.stroff = off; |
| 1398 | | symtab.strsize = file_size; |
| 1399 | | |
| 1400 | | const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000); |
| 1401 | | linkedit.vmsize += segment_size; |
| 1402 | | } |
| 1406 | if (self.source_version_cmd_index == null) { |
| 1407 | self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1408 | try self.load_commands.append(self.base.allocator, .{ |
| 1409 | .SourceVersion = .{ |
| 1410 | .cmd = macho.LC_SOURCE_VERSION, |
| 1411 | .cmdsize = @sizeOf(macho.source_version_command), |
| 1412 | .version = 0x0, |
| 1413 | }, |
| 1414 | }); |
| 1415 | } |
| 1416 | if (self.code_signature_cmd_index == null) { |
| 1417 | self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1418 | try self.load_commands.append(self.base.allocator, .{ |
| 1419 | .LinkeditData = .{ |
| 1420 | .cmd = macho.LC_CODE_SIGNATURE, |
| 1421 | .cmdsize = @sizeOf(macho.linkedit_data_command), |
| 1422 | .dataoff = 0, |
| 1423 | .datasize = 0, |
| 1424 | }, |
| 1425 | }); |
| 1403 | 1426 | } |
| 1404 | 1427 | if (self.dyld_stub_binder_index == null) { |
| 1405 | 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 | 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 | 1652 | fn writeOffsetTableEntry(self: *MachO, index: usize) !void { |
| 1641 | 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 | 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 | 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 | 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); |
| 1653 | | const file_size = self.global_symbols.items.len * @sizeOf(macho.nlist_64); |
| 1654 | | log.debug("writing global symbols from 0x{x} to 0x{x}\n", .{ off, file_size + off }); |
| 1655 | | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.global_symbols.items), off); |
| 1687 | |
| 1688 | const locals_off = self.linkedit_segment_next_offset.?; |
| 1689 | const locals_size = self.local_symbols.items.len * @sizeOf(macho.nlist_64); |
| 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 { |
| 1659 | | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 1660 | | const nlocals = self.local_symbols.items.len; |
| 1661 | | const nglobals = self.global_symbols.items.len; |
| 1662 | | const off = symtab.symoff + (nlocals + nglobals) * @sizeOf(macho.nlist_64); |
| 1663 | | const file_size = self.undef_symbols.items.len * @sizeOf(macho.nlist_64); |
| 1664 | | log.debug("writing undef symbols from 0x{x} to 0x{x}\n", .{ off, file_size + off }); |
| 1665 | | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.undef_symbols.items), off); |
| 1728 | fn writeCodeSignaturePadding(self: *MachO) !void { |
| 1729 | const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData; |
| 1730 | const fileoff = self.linkedit_segment_next_offset.?; |
| 1731 | const datasize: u32 = 0x1000; // TODO Calculate the expected size of the signature. |
| 1732 | code_sig_cmd.dataoff = fileoff; |
| 1733 | code_sig_cmd.datasize = datasize; |
| 1734 | |
| 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 | 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 | 1768 | var trie: Trie = .{}; |
| 1672 | 1769 | defer trie.deinit(self.base.allocator); |
| ... | ... | @@ -1689,28 +1786,51 @@ fn writeExportTrie(self: *MachO) !void { |
| 1689 | 1786 | try trie.writeULEB128Mem(self.base.allocator, &buffer); |
| 1690 | 1787 | |
| 1691 | 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 | 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 | 1811 | fn writeStringTable(self: *MachO) !void { |
| 1696 | 1812 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 1697 | | const allocated_size = self.allocatedSize(symtab.stroff); |
| 1698 | 1813 | const needed_size = self.string_table.items.len; |
| 1699 | 1814 | |
| 1700 | | if (needed_size > allocated_size) { |
| 1701 | | symtab.strsize = 0; |
| 1702 | | symtab.stroff = @intCast(u32, self.findFreeSpace(needed_size, 1)); |
| 1703 | | } |
| 1704 | | symtab.strsize = @intCast(u32, needed_size); |
| 1815 | symtab.stroff = self.linkedit_segment_next_offset.?; |
| 1816 | symtab.strsize = @intCast(u32, mem.alignForward(needed_size, @sizeOf(u64))); |
| 1705 | 1817 | |
| 1706 | 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 | 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 |
| 1711 | | // doing dynamic updates like this. |
| 1826 | self.linkedit_segment_next_offset = symtab.stroff + symtab.strsize; |
| 1827 | // Advance size of __LINKEDIT segment |
| 1712 | 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 | 1836 | fn writeCmdHeaders(self: *MachO) !void { |
| ... | ... | @@ -1726,7 +1846,6 @@ fn writeCmdHeaders(self: *MachO) !void { |
| 1726 | 1846 | last_cmd_offset += cmd.cmdsize(); |
| 1727 | 1847 | } |
| 1728 | 1848 | { |
| 1729 | | // write __text section header |
| 1730 | 1849 | const off = if (self.text_segment_cmd_index) |text_segment_index| blk: { |
| 1731 | 1850 | var i: usize = 0; |
| 1732 | 1851 | var cmdsize: usize = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64); |
| ... | ... | @@ -1739,27 +1858,11 @@ fn writeCmdHeaders(self: *MachO) !void { |
| 1739 | 1858 | // only one, noname segment to append this section header to. |
| 1740 | 1859 | return error.TODOImplementWritingObjFiles; |
| 1741 | 1860 | }; |
| 1742 | | const idx = self.text_section_index.?; |
| 1743 | | log.debug("writing text section header at 0x{x}\n", .{off}); |
| 1744 | | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[idx .. idx + 1]), off); |
| 1745 | | } |
| 1746 | | { |
| 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); |
| 1861 | // write sections belonging to __TEXT segment |
| 1862 | // TODO section indices should belong to each Segment, and we should iterate dynamically. |
| 1863 | const id = self.text_section_index.?; |
| 1864 | log.debug("writing __TEXT section headers at 0x{x}\n", .{off}); |
| 1865 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[id .. id + 2]), off); |
| 1763 | 1866 | } |
| 1764 | 1867 | } |
| 1765 | 1868 | |
| ... | ... | @@ -1807,8 +1910,14 @@ fn writeMachOHeader(self: *MachO) !void { |
| 1807 | 1910 | |
| 1808 | 1911 | hdr.sizeofcmds = sizeofcmds; |
| 1809 | 1912 | |
| 1810 | | // TODO should these be set to something else? |
| 1811 | | hdr.flags = 0; |
| 1913 | switch (self.base.options.output_mode) { |
| 1914 | .Exe => { |
| 1915 | hdr.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE; |
| 1916 | }, |
| 1917 | else => { |
| 1918 | hdr.flags = 0; |
| 1919 | }, |
| 1920 | } |
| 1812 | 1921 | hdr.reserved = 0; |
| 1813 | 1922 | |
| 1814 | 1923 | log.debug("writing Mach-O header {}\n", .{hdr}); |