| ... | ... | @@ -25,55 +25,9 @@ const Trie = @import("MachO/Trie.zig"); |
| 25 | 25 | const CodeSignature = @import("MachO/CodeSignature.zig"); |
| 26 | 26 | const Parser = @import("MachO/Parser.zig"); |
| 27 | 27 | |
| 28 | | pub const base_tag: File.Tag = File.Tag.macho; |
| 29 | | |
| 30 | | const LoadCommand = union(enum) { |
| 31 | | Segment: macho.segment_command_64, |
| 32 | | LinkeditData: macho.linkedit_data_command, |
| 33 | | Symtab: macho.symtab_command, |
| 34 | | Dysymtab: macho.dysymtab_command, |
| 35 | | DyldInfo: macho.dyld_info_command, |
| 36 | | Dylinker: macho.dylinker_command, |
| 37 | | Dylib: macho.dylib_command, |
| 38 | | EntryPoint: macho.entry_point_command, |
| 39 | | MinVersion: macho.version_min_command, |
| 40 | | SourceVersion: macho.source_version_command, |
| 41 | | |
| 42 | | pub fn cmdsize(self: LoadCommand) u32 { |
| 43 | | return switch (self) { |
| 44 | | .Segment => |x| x.cmdsize, |
| 45 | | .LinkeditData => |x| x.cmdsize, |
| 46 | | .Symtab => |x| x.cmdsize, |
| 47 | | .Dysymtab => |x| x.cmdsize, |
| 48 | | .DyldInfo => |x| x.cmdsize, |
| 49 | | .Dylinker => |x| x.cmdsize, |
| 50 | | .Dylib => |x| x.cmdsize, |
| 51 | | .EntryPoint => |x| x.cmdsize, |
| 52 | | .MinVersion => |x| x.cmdsize, |
| 53 | | .SourceVersion => |x| x.cmdsize, |
| 54 | | }; |
| 55 | | } |
| 56 | | |
| 57 | | pub fn write(self: LoadCommand, file: *fs.File, offset: u64) !void { |
| 58 | | return switch (self) { |
| 59 | | .Segment => |cmd| writeGeneric(cmd, file, offset), |
| 60 | | .LinkeditData => |cmd| writeGeneric(cmd, file, offset), |
| 61 | | .Symtab => |cmd| writeGeneric(cmd, file, offset), |
| 62 | | .Dysymtab => |cmd| writeGeneric(cmd, file, offset), |
| 63 | | .DyldInfo => |cmd| writeGeneric(cmd, file, offset), |
| 64 | | .Dylinker => |cmd| writeGeneric(cmd, file, offset), |
| 65 | | .Dylib => |cmd| writeGeneric(cmd, file, offset), |
| 66 | | .EntryPoint => |cmd| writeGeneric(cmd, file, offset), |
| 67 | | .MinVersion => |cmd| writeGeneric(cmd, file, offset), |
| 68 | | .SourceVersion => |cmd| writeGeneric(cmd, file, offset), |
| 69 | | }; |
| 70 | | } |
| 28 | usingnamespace @import("MachO/commands.zig"); |
| 71 | 29 | |
| 72 | | fn writeGeneric(cmd: anytype, file: *fs.File, offset: u64) !void { |
| 73 | | const slice = [1]@TypeOf(cmd){cmd}; |
| 74 | | return file.pwriteAll(mem.sliceAsBytes(slice[0..1]), offset); |
| 75 | | } |
| 76 | | }; |
| 30 | pub const base_tag: File.Tag = File.Tag.macho; |
| 77 | 31 | |
| 78 | 32 | base: File, |
| 79 | 33 | |
| ... | ... | @@ -116,18 +70,14 @@ source_version_cmd_index: ?u16 = null, |
| 116 | 70 | /// Code signature |
| 117 | 71 | code_signature_cmd_index: ?u16 = null, |
| 118 | 72 | |
| 119 | | /// Table of all sections |
| 120 | | sections: std.ArrayListUnmanaged(macho.section_64) = .{}, |
| 121 | | |
| 122 | | /// __TEXT,__text section |
| 73 | /// Index into __TEXT,__text section. |
| 123 | 74 | text_section_index: ?u16 = null, |
| 124 | | |
| 125 | | /// __DATA,__got section |
| 75 | /// Index into __TEXT,__got section. |
| 126 | 76 | got_section_index: ?u16 = null, |
| 127 | | |
| 77 | /// The absolute address of the entry point. |
| 128 | 78 | entry_addr: ?u64 = null, |
| 129 | 79 | |
| 130 | | // TODO move this into each Segment aggregator |
| 80 | /// TODO move this into each Segment aggregator |
| 131 | 81 | linkedit_segment_next_offset: ?u32 = null, |
| 132 | 82 | |
| 133 | 83 | /// Table of all local symbols |
| ... | ... | @@ -356,40 +306,12 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 356 | 306 | if (self.entry_addr) |addr| { |
| 357 | 307 | // Update LC_MAIN with entry offset. |
| 358 | 308 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 359 | | const main_cmd = &self.load_commands.items[self.main_cmd_index.?].EntryPoint; |
| 360 | | main_cmd.entryoff = addr - text_segment.vmaddr; |
| 361 | | } |
| 362 | | if (self.dylinker_cmd_dirty) { |
| 363 | | // Write path to dyld loader. |
| 364 | | var off: usize = @sizeOf(macho.mach_header_64); |
| 365 | | for (self.load_commands.items) |cmd| { |
| 366 | | if (cmd == .Dylinker) break; |
| 367 | | off += cmd.cmdsize(); |
| 368 | | } |
| 369 | | const cmd = &self.load_commands.items[self.dylinker_cmd_index.?].Dylinker; |
| 370 | | off += cmd.name; |
| 371 | | log.debug("writing LC_LOAD_DYLINKER path to dyld at 0x{x}\n", .{off}); |
| 372 | | try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), off); |
| 373 | | self.dylinker_cmd_dirty = false; |
| 309 | const main_cmd = &self.load_commands.items[self.main_cmd_index.?].Main; |
| 310 | main_cmd.entryoff = addr - text_segment.inner.vmaddr; |
| 374 | 311 | } |
| 375 | | if (self.libsystem_cmd_dirty) { |
| 376 | | // Write path to libSystem. |
| 377 | | var off: usize = @sizeOf(macho.mach_header_64); |
| 378 | | for (self.load_commands.items) |cmd| { |
| 379 | | if (cmd == .Dylib) break; |
| 380 | | off += cmd.cmdsize(); |
| 381 | | } |
| 382 | | const cmd = &self.load_commands.items[self.libsystem_cmd_index.?].Dylib; |
| 383 | | off += cmd.dylib.name; |
| 384 | | log.debug("writing LC_LOAD_DYLIB path to libSystem at 0x{x}\n", .{off}); |
| 385 | | try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), off); |
| 386 | | self.libsystem_cmd_dirty = false; |
| 387 | | } |
| 388 | | |
| 389 | 312 | try self.writeExportTrie(); |
| 390 | 313 | try self.writeSymbolTable(); |
| 391 | 314 | try self.writeStringTable(); |
| 392 | | |
| 393 | 315 | // Preallocate space for the code signature. |
| 394 | 316 | // We need to do this at this stage so that we have the load commands with proper values |
| 395 | 317 | // written out to the file. |
| ... | ... | @@ -402,7 +324,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 402 | 324 | } |
| 403 | 325 | |
| 404 | 326 | if (self.cmd_table_dirty) { |
| 405 | | try self.writeCmdHeaders(); |
| 327 | try self.writeLoadCommands(); |
| 406 | 328 | try self.writeMachOHeader(); |
| 407 | 329 | self.cmd_table_dirty = false; |
| 408 | 330 | } |
| ... | ... | @@ -416,8 +338,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 416 | 338 | } |
| 417 | 339 | |
| 418 | 340 | assert(!self.cmd_table_dirty); |
| 419 | | assert(!self.dylinker_cmd_dirty); |
| 420 | | assert(!self.libsystem_cmd_dirty); |
| 421 | 341 | |
| 422 | 342 | switch (self.base.options.output_mode) { |
| 423 | 343 | .Exe, .Lib => try self.writeCodeSignature(), // code signing always comes last |
| ... | ... | @@ -921,7 +841,9 @@ pub fn deinit(self: *MachO) void { |
| 921 | 841 | self.global_symbol_free_list.deinit(self.base.allocator); |
| 922 | 842 | self.local_symbols.deinit(self.base.allocator); |
| 923 | 843 | self.local_symbol_free_list.deinit(self.base.allocator); |
| 924 | | self.sections.deinit(self.base.allocator); |
| 844 | for (self.load_commands.items) |*lc| { |
| 845 | lc.deinit(self.base.allocator); |
| 846 | } |
| 925 | 847 | self.load_commands.deinit(self.base.allocator); |
| 926 | 848 | } |
| 927 | 849 | |
| ... | ... | @@ -1075,7 +997,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1075 | 997 | } |
| 1076 | 998 | |
| 1077 | 999 | // Perform PIE fixups (if any) |
| 1078 | | const got_section = self.sections.items[self.got_section_index.?]; |
| 1000 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1001 | const got_section = text_segment.sections.items[self.got_section_index.?]; |
| 1079 | 1002 | while (self.pie_fixups.popOrNull()) |fixup| { |
| 1080 | 1003 | const target_addr = fixup.address; |
| 1081 | 1004 | const this_addr = symbol.n_value + fixup.start; |
| ... | ... | @@ -1094,7 +1017,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1094 | 1017 | } |
| 1095 | 1018 | } |
| 1096 | 1019 | |
| 1097 | | const text_section = self.sections.items[self.text_section_index.?]; |
| 1020 | const text_section = text_segment.sections.items[self.text_section_index.?]; |
| 1098 | 1021 | const section_offset = symbol.n_value - text_section.addr; |
| 1099 | 1022 | const file_offset = text_section.offset + section_offset; |
| 1100 | 1023 | try self.base.file.?.pwriteAll(code, file_offset); |
| ... | ... | @@ -1212,7 +1135,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1212 | 1135 | if (self.pagezero_segment_cmd_index == null) { |
| 1213 | 1136 | self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1214 | 1137 | try self.load_commands.append(self.base.allocator, .{ |
| 1215 | | .Segment = .{ |
| 1138 | .Segment = SegmentCommand.empty(.{ |
| 1216 | 1139 | .cmd = macho.LC_SEGMENT_64, |
| 1217 | 1140 | .cmdsize = @sizeOf(macho.segment_command_64), |
| 1218 | 1141 | .segname = makeStaticString("__PAGEZERO"), |
| ... | ... | @@ -1224,7 +1147,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1224 | 1147 | .initprot = 0, |
| 1225 | 1148 | .nsects = 0, |
| 1226 | 1149 | .flags = 0, |
| 1227 | | }, |
| 1150 | }), |
| 1228 | 1151 | }); |
| 1229 | 1152 | self.cmd_table_dirty = true; |
| 1230 | 1153 | } |
| ... | ... | @@ -1233,7 +1156,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1233 | 1156 | const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE; |
| 1234 | 1157 | const initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE; |
| 1235 | 1158 | try self.load_commands.append(self.base.allocator, .{ |
| 1236 | | .Segment = .{ |
| 1159 | .Segment = SegmentCommand.empty(.{ |
| 1237 | 1160 | .cmd = macho.LC_SEGMENT_64, |
| 1238 | 1161 | .cmdsize = @sizeOf(macho.segment_command_64), |
| 1239 | 1162 | .segname = makeStaticString("__TEXT"), |
| ... | ... | @@ -1245,45 +1168,45 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1245 | 1168 | .initprot = initprot, |
| 1246 | 1169 | .nsects = 0, |
| 1247 | 1170 | .flags = 0, |
| 1248 | | }, |
| 1171 | }), |
| 1249 | 1172 | }); |
| 1250 | 1173 | self.cmd_table_dirty = true; |
| 1251 | 1174 | } |
| 1252 | 1175 | if (self.text_section_index == null) { |
| 1253 | | self.text_section_index = @intCast(u16, self.sections.items.len); |
| 1254 | 1176 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1177 | self.text_section_index = @intCast(u16, text_segment.sections.items.len); |
| 1255 | 1178 | |
| 1256 | 1179 | const program_code_size_hint = self.base.options.program_code_size_hint; |
| 1257 | 1180 | const file_size = mem.alignForwardGeneric(u64, program_code_size_hint, self.page_size); |
| 1258 | 1181 | const off = @intCast(u32, self.findFreeSpace(file_size, self.page_size)); // TODO maybe findFreeSpace should return u32 directly? |
| 1259 | | const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS; |
| 1260 | 1182 | |
| 1261 | 1183 | log.debug("found __text section free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| 1262 | 1184 | |
| 1263 | | try self.sections.append(self.base.allocator, .{ |
| 1185 | try text_segment.sections.append(self.base.allocator, .{ |
| 1264 | 1186 | .sectname = makeStaticString("__text"), |
| 1265 | 1187 | .segname = makeStaticString("__TEXT"), |
| 1266 | | .addr = text_segment.vmaddr + off, |
| 1188 | .addr = text_segment.inner.vmaddr + off, |
| 1267 | 1189 | .size = file_size, |
| 1268 | 1190 | .offset = off, |
| 1269 | 1191 | .@"align" = if (self.base.options.target.cpu.arch == .aarch64) 2 else 0, // 2^2 for aarch64, 2^0 for x86_64 |
| 1270 | 1192 | .reloff = 0, |
| 1271 | 1193 | .nreloc = 0, |
| 1272 | | .flags = flags, |
| 1194 | .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, |
| 1273 | 1195 | .reserved1 = 0, |
| 1274 | 1196 | .reserved2 = 0, |
| 1275 | 1197 | .reserved3 = 0, |
| 1276 | 1198 | }); |
| 1277 | 1199 | |
| 1278 | | text_segment.vmsize = file_size + off; // We add off here since __TEXT segment includes everything prior to __text section. |
| 1279 | | text_segment.filesize = file_size + off; |
| 1280 | | text_segment.cmdsize += @sizeOf(macho.section_64); |
| 1281 | | text_segment.nsects += 1; |
| 1200 | text_segment.inner.vmsize = file_size + off; // We add off here since __TEXT segment includes everything prior to __text section. |
| 1201 | text_segment.inner.filesize = file_size + off; |
| 1202 | text_segment.inner.cmdsize += @sizeOf(macho.section_64); |
| 1203 | text_segment.inner.nsects += 1; |
| 1282 | 1204 | self.cmd_table_dirty = true; |
| 1283 | 1205 | } |
| 1284 | 1206 | if (self.got_section_index == null) { |
| 1285 | | self.got_section_index = @intCast(u16, self.sections.items.len); |
| 1286 | | const text_section = &self.sections.items[self.text_section_index.?]; |
| 1207 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1208 | const text_section = &text_segment.sections.items[self.text_section_index.?]; |
| 1209 | self.got_section_index = @intCast(u16, text_segment.sections.items.len); |
| 1287 | 1210 | |
| 1288 | 1211 | const file_size = @sizeOf(u64) * self.base.options.symbol_count_hint; |
| 1289 | 1212 | // TODO looking for free space should be done *within* a segment it belongs to |
| ... | ... | @@ -1291,7 +1214,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1291 | 1214 | |
| 1292 | 1215 | log.debug("found __got section free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| 1293 | 1216 | |
| 1294 | | try self.sections.append(self.base.allocator, .{ |
| 1217 | try text_segment.sections.append(self.base.allocator, .{ |
| 1295 | 1218 | .sectname = makeStaticString("__got"), |
| 1296 | 1219 | .segname = makeStaticString("__TEXT"), |
| 1297 | 1220 | .addr = text_section.addr + text_section.size, |
| ... | ... | @@ -1300,18 +1223,17 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1300 | 1223 | .@"align" = if (self.base.options.target.cpu.arch == .aarch64) 2 else 0, |
| 1301 | 1224 | .reloff = 0, |
| 1302 | 1225 | .nreloc = 0, |
| 1303 | | .flags = macho.S_REGULAR, |
| 1226 | .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, |
| 1304 | 1227 | .reserved1 = 0, |
| 1305 | 1228 | .reserved2 = 0, |
| 1306 | 1229 | .reserved3 = 0, |
| 1307 | 1230 | }); |
| 1308 | 1231 | |
| 1309 | | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1310 | 1232 | const added_size = mem.alignForwardGeneric(u64, file_size, self.page_size); |
| 1311 | | text_segment.vmsize += added_size; |
| 1312 | | text_segment.filesize += added_size; |
| 1313 | | text_segment.cmdsize += @sizeOf(macho.section_64); |
| 1314 | | text_segment.nsects += 1; |
| 1233 | text_segment.inner.vmsize += added_size; |
| 1234 | text_segment.inner.filesize += added_size; |
| 1235 | text_segment.inner.cmdsize += @sizeOf(macho.section_64); |
| 1236 | text_segment.inner.nsects += 1; |
| 1315 | 1237 | self.cmd_table_dirty = true; |
| 1316 | 1238 | } |
| 1317 | 1239 | if (self.linkedit_segment_cmd_index == null) { |
| ... | ... | @@ -1319,13 +1241,13 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1319 | 1241 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1320 | 1242 | const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE; |
| 1321 | 1243 | const initprot = macho.VM_PROT_READ; |
| 1322 | | const off = text_segment.fileoff + text_segment.filesize; |
| 1244 | const off = text_segment.inner.fileoff + text_segment.inner.filesize; |
| 1323 | 1245 | try self.load_commands.append(self.base.allocator, .{ |
| 1324 | | .Segment = .{ |
| 1246 | .Segment = SegmentCommand.empty(.{ |
| 1325 | 1247 | .cmd = macho.LC_SEGMENT_64, |
| 1326 | 1248 | .cmdsize = @sizeOf(macho.segment_command_64), |
| 1327 | 1249 | .segname = makeStaticString("__LINKEDIT"), |
| 1328 | | .vmaddr = text_segment.vmaddr + text_segment.vmsize, |
| 1250 | .vmaddr = text_segment.inner.vmaddr + text_segment.inner.vmsize, |
| 1329 | 1251 | .vmsize = 0, |
| 1330 | 1252 | .fileoff = off, |
| 1331 | 1253 | .filesize = 0, |
| ... | ... | @@ -1333,7 +1255,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1333 | 1255 | .initprot = initprot, |
| 1334 | 1256 | .nsects = 0, |
| 1335 | 1257 | .flags = 0, |
| 1336 | | }, |
| 1258 | }), |
| 1337 | 1259 | }); |
| 1338 | 1260 | self.linkedit_segment_next_offset = @intCast(u32, off); |
| 1339 | 1261 | self.cmd_table_dirty = true; |
| ... | ... | @@ -1341,7 +1263,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1341 | 1263 | if (self.dyld_info_cmd_index == null) { |
| 1342 | 1264 | self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1343 | 1265 | try self.load_commands.append(self.base.allocator, .{ |
| 1344 | | .DyldInfo = .{ |
| 1266 | .DyldInfoOnly = .{ |
| 1345 | 1267 | .cmd = macho.LC_DYLD_INFO_ONLY, |
| 1346 | 1268 | .cmdsize = @sizeOf(macho.dyld_info_command), |
| 1347 | 1269 | .rebase_off = 0, |
| ... | ... | @@ -1403,15 +1325,16 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1403 | 1325 | if (self.dylinker_cmd_index == null) { |
| 1404 | 1326 | self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1405 | 1327 | const cmdsize = mem.alignForwardGeneric(u64, @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH), @sizeOf(u64)); |
| 1406 | | try self.load_commands.append(self.base.allocator, .{ |
| 1407 | | .Dylinker = .{ |
| 1408 | | .cmd = macho.LC_LOAD_DYLINKER, |
| 1409 | | .cmdsize = @intCast(u32, cmdsize), |
| 1410 | | .name = @sizeOf(macho.dylinker_command), |
| 1411 | | }, |
| 1328 | var dylinker_cmd = emptyGenericCommandWithData(macho.dylinker_command{ |
| 1329 | .cmd = macho.LC_LOAD_DYLINKER, |
| 1330 | .cmdsize = @intCast(u32, cmdsize), |
| 1331 | .name = @sizeOf(macho.dylinker_command), |
| 1412 | 1332 | }); |
| 1333 | dylinker_cmd.data = try self.base.allocator.alloc(u8, cmdsize - dylinker_cmd.inner.name); |
| 1334 | mem.set(u8, dylinker_cmd.data, 0); |
| 1335 | mem.copy(u8, dylinker_cmd.data, mem.spanZ(DEFAULT_DYLD_PATH)); |
| 1336 | try self.load_commands.append(self.base.allocator, .{ .Dylinker = dylinker_cmd }); |
| 1413 | 1337 | self.cmd_table_dirty = true; |
| 1414 | | self.dylinker_cmd_dirty = true; |
| 1415 | 1338 | } |
| 1416 | 1339 | if (self.libsystem_cmd_index == null) { |
| 1417 | 1340 | self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len); |
| ... | ... | @@ -1419,26 +1342,26 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1419 | 1342 | // TODO Find a way to work out runtime version from the OS version triple stored in std.Target. |
| 1420 | 1343 | // In the meantime, we're gonna hardcode to the minimum compatibility version of 0.0.0. |
| 1421 | 1344 | const min_version = 0x0; |
| 1422 | | const dylib = .{ |
| 1423 | | .name = @sizeOf(macho.dylib_command), |
| 1424 | | .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files |
| 1425 | | .current_version = min_version, |
| 1426 | | .compatibility_version = min_version, |
| 1427 | | }; |
| 1428 | | try self.load_commands.append(self.base.allocator, .{ |
| 1429 | | .Dylib = .{ |
| 1430 | | .cmd = macho.LC_LOAD_DYLIB, |
| 1431 | | .cmdsize = @intCast(u32, cmdsize), |
| 1432 | | .dylib = dylib, |
| 1345 | var dylib_cmd = emptyGenericCommandWithData(macho.dylib_command{ |
| 1346 | .cmd = macho.LC_LOAD_DYLIB, |
| 1347 | .cmdsize = @intCast(u32, cmdsize), |
| 1348 | .dylib = .{ |
| 1349 | .name = @sizeOf(macho.dylib_command), |
| 1350 | .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files |
| 1351 | .current_version = min_version, |
| 1352 | .compatibility_version = min_version, |
| 1433 | 1353 | }, |
| 1434 | 1354 | }); |
| 1355 | dylib_cmd.data = try self.base.allocator.alloc(u8, cmdsize - dylib_cmd.inner.dylib.name); |
| 1356 | mem.set(u8, dylib_cmd.data, 0); |
| 1357 | mem.copy(u8, dylib_cmd.data, mem.spanZ(LIB_SYSTEM_PATH)); |
| 1358 | try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd }); |
| 1435 | 1359 | self.cmd_table_dirty = true; |
| 1436 | | self.libsystem_cmd_dirty = true; |
| 1437 | 1360 | } |
| 1438 | 1361 | if (self.main_cmd_index == null) { |
| 1439 | 1362 | self.main_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1440 | 1363 | try self.load_commands.append(self.base.allocator, .{ |
| 1441 | | .EntryPoint = .{ |
| 1364 | .Main = .{ |
| 1442 | 1365 | .cmd = macho.LC_MAIN, |
| 1443 | 1366 | .cmdsize = @sizeOf(macho.entry_point_command), |
| 1444 | 1367 | .entryoff = 0x0, |
| ... | ... | @@ -1459,7 +1382,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1459 | 1382 | const ver = self.base.options.target.os.version_range.semver.min; |
| 1460 | 1383 | const version = ver.major << 16 | ver.minor << 8 | ver.patch; |
| 1461 | 1384 | try self.load_commands.append(self.base.allocator, .{ |
| 1462 | | .MinVersion = .{ |
| 1385 | .VersionMin = .{ |
| 1463 | 1386 | .cmd = cmd, |
| 1464 | 1387 | .cmdsize = @sizeOf(macho.version_min_command), |
| 1465 | 1388 | .version = version, |
| ... | ... | @@ -1502,7 +1425,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1502 | 1425 | } |
| 1503 | 1426 | |
| 1504 | 1427 | fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { |
| 1505 | | const text_section = &self.sections.items[self.text_section_index.?]; |
| 1428 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1429 | const text_section = &text_segment.sections.items[self.text_section_index.?]; |
| 1506 | 1430 | const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den; |
| 1507 | 1431 | |
| 1508 | 1432 | // We use these to indicate our intention to update metadata, placing the new block, |
| ... | ... | @@ -1644,15 +1568,18 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 { |
| 1644 | 1568 | return test_end; |
| 1645 | 1569 | } |
| 1646 | 1570 | } |
| 1647 | | for (self.sections.items) |section| { |
| 1648 | | const increased_size = satMul(section.size, alloc_num) / alloc_den; |
| 1649 | | const test_end = section.offset + increased_size; |
| 1650 | | if (end > section.offset and start < test_end) { |
| 1651 | | return test_end; |
| 1571 | if (self.text_segment_cmd_index) |text_index| { |
| 1572 | const text_segment = self.load_commands.items[text_index].Segment; |
| 1573 | for (text_segment.sections.items) |section| { |
| 1574 | const increased_size = satMul(section.size, alloc_num) / alloc_den; |
| 1575 | const test_end = section.offset + increased_size; |
| 1576 | if (end > section.offset and start < test_end) { |
| 1577 | return test_end; |
| 1578 | } |
| 1652 | 1579 | } |
| 1653 | 1580 | } |
| 1654 | 1581 | if (self.dyld_info_cmd_index) |dyld_info_index| { |
| 1655 | | const dyld_info = self.load_commands.items[dyld_info_index].DyldInfo; |
| 1582 | const dyld_info = self.load_commands.items[dyld_info_index].DyldInfoOnly; |
| 1656 | 1583 | const tight_size = dyld_info.export_size; |
| 1657 | 1584 | const increased_size = satMul(tight_size, alloc_num) / alloc_den; |
| 1658 | 1585 | const test_end = dyld_info.export_off + increased_size; |
| ... | ... | @@ -1689,12 +1616,15 @@ fn allocatedSize(self: *MachO, start: u64) u64 { |
| 1689 | 1616 | const off = @sizeOf(macho.mach_header_64); |
| 1690 | 1617 | if (off > start and off < min_pos) min_pos = off; |
| 1691 | 1618 | } |
| 1692 | | for (self.sections.items) |section| { |
| 1693 | | if (section.offset <= start) continue; |
| 1694 | | if (section.offset < min_pos) min_pos = section.offset; |
| 1619 | if (self.text_segment_cmd_index) |text_index| { |
| 1620 | const text_segment = self.load_commands.items[text_index].Segment; |
| 1621 | for (text_segment.sections.items) |section| { |
| 1622 | if (section.offset <= start) continue; |
| 1623 | if (section.offset < min_pos) min_pos = section.offset; |
| 1624 | } |
| 1695 | 1625 | } |
| 1696 | 1626 | if (self.dyld_info_cmd_index) |dyld_info_index| { |
| 1697 | | const dyld_info = self.load_commands.items[dyld_info_index].DyldInfo; |
| 1627 | const dyld_info = self.load_commands.items[dyld_info_index].DyldInfoOnly; |
| 1698 | 1628 | if (dyld_info.export_off > start and dyld_info.export_off < min_pos) min_pos = dyld_info.export_off; |
| 1699 | 1629 | } |
| 1700 | 1630 | if (self.symtab_cmd_index) |symtab_index| { |
| ... | ... | @@ -1714,7 +1644,8 @@ fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u16) u64 { |
| 1714 | 1644 | } |
| 1715 | 1645 | |
| 1716 | 1646 | fn writeOffsetTableEntry(self: *MachO, index: usize) !void { |
| 1717 | | const sect = &self.sections.items[self.got_section_index.?]; |
| 1647 | const text_semgent = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1648 | const sect = &text_semgent.sections.items[self.got_section_index.?]; |
| 1718 | 1649 | const off = sect.offset + @sizeOf(u64) * index; |
| 1719 | 1650 | const vmaddr = sect.addr + @sizeOf(u64) * index; |
| 1720 | 1651 | |
| ... | ... | @@ -1782,9 +1713,9 @@ fn writeSymbolTable(self: *MachO) !void { |
| 1782 | 1713 | |
| 1783 | 1714 | // Advance size of __LINKEDIT segment |
| 1784 | 1715 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 1785 | | linkedit.filesize += symtab.nsyms * @sizeOf(macho.nlist_64); |
| 1786 | | if (linkedit.vmsize < linkedit.filesize) { |
| 1787 | | linkedit.vmsize = mem.alignForwardGeneric(u64, linkedit.filesize, self.page_size); |
| 1716 | linkedit.inner.filesize += symtab.nsyms * @sizeOf(macho.nlist_64); |
| 1717 | if (linkedit.inner.vmsize < linkedit.inner.filesize) { |
| 1718 | linkedit.inner.vmsize = mem.alignForwardGeneric(u64, linkedit.inner.filesize, self.page_size); |
| 1788 | 1719 | } |
| 1789 | 1720 | self.cmd_table_dirty = true; |
| 1790 | 1721 | } |
| ... | ... | @@ -1799,9 +1730,9 @@ fn writeCodeSignaturePadding(self: *MachO) !void { |
| 1799 | 1730 | self.linkedit_segment_next_offset = fileoff + datasize; |
| 1800 | 1731 | // Advance size of __LINKEDIT segment |
| 1801 | 1732 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 1802 | | linkedit.filesize += datasize; |
| 1803 | | if (linkedit.vmsize < linkedit.filesize) { |
| 1804 | | linkedit.vmsize = mem.alignForwardGeneric(u64, linkedit.filesize, self.page_size); |
| 1733 | linkedit.inner.filesize += datasize; |
| 1734 | if (linkedit.inner.vmsize < linkedit.inner.filesize) { |
| 1735 | linkedit.inner.vmsize = mem.alignForwardGeneric(u64, linkedit.inner.filesize, self.page_size); |
| 1805 | 1736 | } |
| 1806 | 1737 | log.debug("writing code signature padding from 0x{x} to 0x{x}\n", .{ fileoff, fileoff + datasize }); |
| 1807 | 1738 | // Pad out the space. We need to do this to calculate valid hashes for everything in the file |
| ... | ... | @@ -1836,10 +1767,10 @@ fn writeExportTrie(self: *MachO) !void { |
| 1836 | 1767 | for (self.global_symbols.items) |symbol| { |
| 1837 | 1768 | // TODO figure out if we should put all global symbols into the export trie |
| 1838 | 1769 | const name = self.getString(symbol.n_strx); |
| 1839 | | assert(symbol.n_value >= text_segment.vmaddr); |
| 1770 | assert(symbol.n_value >= text_segment.inner.vmaddr); |
| 1840 | 1771 | try trie.put(self.base.allocator, .{ |
| 1841 | 1772 | .name = name, |
| 1842 | | .vmaddr_offset = symbol.n_value - text_segment.vmaddr, |
| 1773 | .vmaddr_offset = symbol.n_value - text_segment.inner.vmaddr, |
| 1843 | 1774 | .export_flags = 0, // TODO workout creation of export flags |
| 1844 | 1775 | }); |
| 1845 | 1776 | } |
| ... | ... | @@ -1849,7 +1780,7 @@ fn writeExportTrie(self: *MachO) !void { |
| 1849 | 1780 | |
| 1850 | 1781 | try trie.writeULEB128Mem(self.base.allocator, &buffer); |
| 1851 | 1782 | |
| 1852 | | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfo; |
| 1783 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; |
| 1853 | 1784 | const export_size = @intCast(u32, mem.alignForward(buffer.items.len, @sizeOf(u64))); |
| 1854 | 1785 | dyld_info.export_off = self.linkedit_segment_next_offset.?; |
| 1855 | 1786 | dyld_info.export_size = export_size; |
| ... | ... | @@ -1865,9 +1796,9 @@ fn writeExportTrie(self: *MachO) !void { |
| 1865 | 1796 | self.linkedit_segment_next_offset = dyld_info.export_off + dyld_info.export_size; |
| 1866 | 1797 | // Advance size of __LINKEDIT segment |
| 1867 | 1798 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 1868 | | linkedit.filesize += dyld_info.export_size; |
| 1869 | | if (linkedit.vmsize < linkedit.filesize) { |
| 1870 | | linkedit.vmsize = mem.alignForwardGeneric(u64, linkedit.filesize, self.page_size); |
| 1799 | linkedit.inner.filesize += dyld_info.export_size; |
| 1800 | if (linkedit.inner.vmsize < linkedit.inner.filesize) { |
| 1801 | linkedit.inner.vmsize = mem.alignForwardGeneric(u64, linkedit.inner.filesize, self.page_size); |
| 1871 | 1802 | } |
| 1872 | 1803 | self.cmd_table_dirty = true; |
| 1873 | 1804 | } |
| ... | ... | @@ -1890,49 +1821,31 @@ fn writeStringTable(self: *MachO) !void { |
| 1890 | 1821 | self.linkedit_segment_next_offset = symtab.stroff + symtab.strsize; |
| 1891 | 1822 | // Advance size of __LINKEDIT segment |
| 1892 | 1823 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 1893 | | linkedit.filesize += symtab.strsize; |
| 1894 | | if (linkedit.vmsize < linkedit.filesize) { |
| 1895 | | linkedit.vmsize = mem.alignForwardGeneric(u64, linkedit.filesize, self.page_size); |
| 1824 | linkedit.inner.filesize += symtab.strsize; |
| 1825 | if (linkedit.inner.vmsize < linkedit.inner.filesize) { |
| 1826 | linkedit.inner.vmsize = mem.alignForwardGeneric(u64, linkedit.inner.filesize, self.page_size); |
| 1896 | 1827 | } |
| 1897 | 1828 | self.cmd_table_dirty = true; |
| 1898 | 1829 | } |
| 1899 | 1830 | |
| 1900 | | fn writeCmdHeaders(self: *MachO) !void { |
| 1901 | | assert(self.cmd_table_dirty); |
| 1902 | | |
| 1903 | | // Write all load command headers first. |
| 1904 | | // Since command sizes are up-to-date and accurate, we will correctly |
| 1905 | | // leave space for any section headers that any of the segment load |
| 1906 | | // commands might consist of. |
| 1907 | | var last_cmd_offset: usize = @sizeOf(macho.mach_header_64); |
| 1908 | | for (self.load_commands.items) |cmd| { |
| 1909 | | try cmd.write(&self.base.file.?, last_cmd_offset); |
| 1910 | | last_cmd_offset += cmd.cmdsize(); |
| 1831 | /// Writes all load commands and section headers. |
| 1832 | fn writeLoadCommands(self: *MachO) !void { |
| 1833 | var sizeofcmds: usize = 0; |
| 1834 | for (self.load_commands.items) |lc| { |
| 1835 | sizeofcmds += lc.cmdsize(); |
| 1911 | 1836 | } |
| 1912 | | { |
| 1913 | | const off = if (self.text_segment_cmd_index) |text_segment_index| blk: { |
| 1914 | | var i: usize = 0; |
| 1915 | | var cmdsize: usize = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64); |
| 1916 | | while (i < text_segment_index) : (i += 1) { |
| 1917 | | cmdsize += self.load_commands.items[i].cmdsize(); |
| 1918 | | } |
| 1919 | | break :blk cmdsize; |
| 1920 | | } else { |
| 1921 | | // If we've landed in here, we are building a MachO object file, so we have |
| 1922 | | // only one, noname segment to append this section header to. |
| 1923 | | return error.TODOImplementWritingObjFiles; |
| 1924 | | }; |
| 1925 | | // write sections belonging to __TEXT segment |
| 1926 | | // TODO section indices should belong to each Segment, and we should iterate dynamically. |
| 1927 | | const id = self.text_section_index.?; |
| 1928 | | log.debug("writing __TEXT section headers at 0x{x}\n", .{off}); |
| 1929 | | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[id .. id + 2]), off); |
| 1837 | |
| 1838 | var buffer = try self.base.allocator.alloc(u8, sizeofcmds); |
| 1839 | defer self.base.allocator.free(buffer); |
| 1840 | var writer = std.io.fixedBufferStream(buffer).writer(); |
| 1841 | for (self.load_commands.items) |lc| { |
| 1842 | try lc.write(writer); |
| 1930 | 1843 | } |
| 1844 | |
| 1845 | try self.base.file.?.pwriteAll(buffer, @sizeOf(macho.mach_header_64)); |
| 1931 | 1846 | } |
| 1932 | 1847 | |
| 1933 | 1848 | /// Writes Mach-O file header. |
| 1934 | | /// Should be invoked last as it needs up-to-date values of ncmds and sizeof_cmds bookkeeping |
| 1935 | | /// variables. |
| 1936 | 1849 | fn writeMachOHeader(self: *MachO) !void { |
| 1937 | 1850 | var hdr: macho.mach_header_64 = undefined; |
| 1938 | 1851 | hdr.magic = macho.MH_MAGIC_64; |
| ... | ... | @@ -1994,10 +1907,3 @@ fn satMul(a: anytype, b: anytype) @TypeOf(a, b) { |
| 1994 | 1907 | const T = @TypeOf(a, b); |
| 1995 | 1908 | return std.math.mul(T, a, b) catch std.math.maxInt(T); |
| 1996 | 1909 | } |
| 1997 | | |
| 1998 | | test "" { |
| 1999 | | // TODO surprisingly this causes a linking error: |
| 2000 | | // _linkWithLLD symbol missing for arch |
| 2001 | | // _ = std.testing.refAllDecls(@This()); |
| 2002 | | _ = std.testing.refAllDecls(@import("MachO/commands.zig")); |
| 2003 | | } |