| ... | @@ -326,14 +326,36 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { | ... | @@ -326,14 +326,36 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 326 | const tracy = trace(@src()); | 326 | const tracy = trace(@src()); |
| 327 | defer tracy.end(); | 327 | defer tracy.end(); |
| 328 | | 328 | |
| | 329 | // Unfortunately these have to be buffered and done at the end because MachO does not allow |
| | 330 | // mixing local, global and undefined symbols within a symbol table. |
| | 331 | try self.writeAllGlobalSymbols(); |
| | 332 | try self.writeAllUndefSymbols(); |
| | 333 | |
| | 334 | // TODO uncomment when we add our own codesigning mechanim |
| | 335 | // try self.writeStringTable(); |
| | 336 | |
| 329 | switch (self.base.options.output_mode) { | 337 | switch (self.base.options.output_mode) { |
| 330 | .Exe => { | 338 | .Exe => { |
| | 339 | // Write export trie. |
| | 340 | try self.writeExportTrie(); |
| 331 | if (self.entry_addr) |addr| { | 341 | if (self.entry_addr) |addr| { |
| 332 | // Update LC_MAIN with entry offset. | 342 | // Update LC_MAIN with entry offset. |
| 333 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | 343 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 334 | const main_cmd = &self.load_commands.items[self.main_cmd_index.?].EntryPoint; | 344 | const main_cmd = &self.load_commands.items[self.main_cmd_index.?].EntryPoint; |
| 335 | main_cmd.entryoff = addr - text_segment.vmaddr; | 345 | main_cmd.entryoff = addr - text_segment.vmaddr; |
| 336 | } | 346 | } |
| | 347 | { |
| | 348 | // Update dynamic symbol table. |
| | 349 | const nlocals = @intCast(u32, self.local_symbols.items.len); |
| | 350 | const nglobals = @intCast(u32, self.global_symbols.items.len); |
| | 351 | const nundefs = @intCast(u32, self.undef_symbols.items.len); |
| | 352 | const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; |
| | 353 | dysymtab.nlocalsym = nlocals; |
| | 354 | dysymtab.iextdefsym = nlocals; |
| | 355 | dysymtab.nextdefsym = nglobals; |
| | 356 | dysymtab.iundefsym = nlocals + nglobals; |
| | 357 | dysymtab.nundefsym = nundefs; |
| | 358 | } |
| 337 | if (self.dylinker_cmd_dirty) { | 359 | if (self.dylinker_cmd_dirty) { |
| 338 | // Write path to dyld loader. | 360 | // Write path to dyld loader. |
| 339 | var off: usize = @sizeOf(macho.mach_header_64); | 361 | var off: usize = @sizeOf(macho.mach_header_64); |
| ... | @@ -360,40 +382,25 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { | ... | @@ -360,40 +382,25 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 360 | try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), off); | 382 | try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), off); |
| 361 | self.libsystem_cmd_dirty = false; | 383 | self.libsystem_cmd_dirty = false; |
| 362 | } | 384 | } |
| 363 | | | |
| 364 | // Write export trie. | | |
| 365 | try self.writeExportTrie(); | | |
| 366 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | | |
| 367 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | | |
| 368 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfo; | | |
| 369 | linkedit.fileoff = dyld_info.export_off; | | |
| 370 | symtab.symoff = dyld_info.export_off + dyld_info.export_size; | | |
| 371 | }, | 385 | }, |
| 372 | .Obj => {}, | 386 | .Obj => {}, |
| 373 | .Lib => return error.TODOImplementWritingLibFiles, | 387 | .Lib => return error.TODOImplementWritingLibFiles, |
| 374 | } | 388 | } |
| 375 | | 389 | |
| 376 | // Unfortunately these have to be buffered and done at the end because MachO does not allow | | |
| 377 | // mixing local, global and undefined symbols within a symbol table. | | |
| 378 | try self.writeSymbolTable(); | | |
| 379 | try self.writeStringTable(); | | |
| 380 | | | |
| 381 | { | | |
| 382 | // Seal __DATA,__got section size | | |
| 383 | const got = &self.sections.items[self.got_section_index.?]; | | |
| 384 | got.size = @intCast(u32, self.offset_table.items.len * @sizeOf(u64)); | | |
| 385 | } | | |
| 386 | | | |
| 387 | { | 390 | { |
| 388 | // TODO rework how we preallocate space for the entire __LINKEDIT segment instead of | 391 | // Update symbol table. |
| 389 | // doing dynamic updates like this. | 392 | const nlocals = @intCast(u32, self.local_symbols.items.len); |
| 390 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | 393 | const nglobals = @intCast(u32, self.global_symbols.items.len); |
| | 394 | const nundefs = @intCast(u32, self.undef_symbols.items.len); |
| 391 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | 395 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 392 | const file_size = symtab.stroff + symtab.strsize - linkedit.fileoff; | 396 | symtab.nsyms = nlocals + nglobals + nundefs; |
| 393 | linkedit.filesize = file_size; | 397 | // TODO could we drop this when we add our own codesigning mechanims? |
| 394 | linkedit.vmsize = mem.alignForwardGeneric(u64, file_size, self.page_size); | 398 | symtab.stroff = symtab.symoff + symtab.nsyms * @sizeOf(macho.nlist_64); |
| 395 | } | 399 | } |
| 396 | | 400 | |
| | 401 | // TODO remove when we add our own codesigning mechanism |
| | 402 | try self.writeStringTable(); |
| | 403 | |
| 397 | if (self.cmd_table_dirty) { | 404 | if (self.cmd_table_dirty) { |
| 398 | try self.writeCmdHeaders(); | 405 | try self.writeCmdHeaders(); |
| 399 | try self.writeMachOHeader(); | 406 | try self.writeMachOHeader(); |
| ... | @@ -982,7 +989,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { | ... | @@ -982,7 +989,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 982 | symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1; | 989 | symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1; |
| 983 | symbol.n_desc = 0; | 990 | symbol.n_desc = 0; |
| 984 | // TODO this write could be avoided if no fields of the symbol were changed. | 991 | // TODO this write could be avoided if no fields of the symbol were changed. |
| 985 | // try self.writeSymbol(decl.link.macho.local_sym_index); | 992 | try self.writeSymbol(decl.link.macho.local_sym_index); |
| 986 | } else { | 993 | } else { |
| 987 | const decl_name = mem.spanZ(decl.name); | 994 | const decl_name = mem.spanZ(decl.name); |
| 988 | const name_str_index = try self.makeString(decl_name); | 995 | const name_str_index = try self.makeString(decl_name); |
| ... | @@ -999,7 +1006,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { | ... | @@ -999,7 +1006,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 999 | }; | 1006 | }; |
| 1000 | self.offset_table.items[decl.link.macho.offset_table_index] = addr; | 1007 | self.offset_table.items[decl.link.macho.offset_table_index] = addr; |
| 1001 | | 1008 | |
| 1002 | // try self.writeSymbol(decl.link.macho.local_sym_index); | 1009 | try self.writeSymbol(decl.link.macho.local_sym_index); |
| 1003 | try self.writeOffsetTableEntry(decl.link.macho.offset_table_index); | 1010 | try self.writeOffsetTableEntry(decl.link.macho.offset_table_index); |
| 1004 | } | 1011 | } |
| 1005 | | 1012 | |
| ... | @@ -1220,8 +1227,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1220,8 +1227,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1220 | data_segment.nsects += 1; | 1227 | data_segment.nsects += 1; |
| 1221 | | 1228 | |
| 1222 | const file_size = @sizeOf(u64) * self.base.options.symbol_count_hint; | 1229 | const file_size = @sizeOf(u64) * self.base.options.symbol_count_hint; |
| 1223 | // const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000)); | 1230 | const off = @intCast(u32, self.findFreeSpace(file_size, self.page_size)); |
| 1224 | const off = @intCast(u32, data_segment.fileoff); | | |
| 1225 | | 1231 | |
| 1226 | log.debug("found __got section free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); | 1232 | log.debug("found __got section free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| 1227 | | 1233 | |
| ... | @@ -1288,6 +1294,22 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1288,6 +1294,22 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1288 | }); | 1294 | }); |
| 1289 | self.cmd_table_dirty = true; | 1295 | self.cmd_table_dirty = true; |
| 1290 | } | 1296 | } |
| | 1297 | { |
| | 1298 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| | 1299 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfo; |
| | 1300 | if (dyld_info.export_off == 0) { |
| | 1301 | const nsyms = self.base.options.symbol_count_hint; |
| | 1302 | const file_size = @sizeOf(u64) * nsyms; |
| | 1303 | const off = @intCast(u32, self.findFreeSpace(file_size, self.page_size)); |
| | 1304 | log.debug("found export trie free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| | 1305 | dyld_info.export_off = off; |
| | 1306 | dyld_info.export_size = @intCast(u32, file_size); |
| | 1307 | |
| | 1308 | const segment_size = mem.alignForwardGeneric(u64, file_size, self.page_size); |
| | 1309 | linkedit.vmsize += segment_size; |
| | 1310 | linkedit.fileoff = off; |
| | 1311 | } |
| | 1312 | } |
| 1291 | if (self.symtab_cmd_index == null) { | 1313 | if (self.symtab_cmd_index == null) { |
| 1292 | self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len); | 1314 | self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1293 | try self.load_commands.append(self.base.allocator, .{ | 1315 | try self.load_commands.append(self.base.allocator, .{ |
| ... | @@ -1330,6 +1352,35 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1330,6 +1352,35 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1330 | }); | 1352 | }); |
| 1331 | self.cmd_table_dirty = true; | 1353 | self.cmd_table_dirty = true; |
| 1332 | } | 1354 | } |
| | 1355 | { |
| | 1356 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| | 1357 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| | 1358 | if (symtab.symoff == 0) { |
| | 1359 | const nsyms = self.base.options.symbol_count_hint; |
| | 1360 | const file_size = @sizeOf(macho.nlist_64) * nsyms; |
| | 1361 | const off = @intCast(u32, self.findFreeSpace(file_size, self.page_size)); |
| | 1362 | log.debug("found symbol table free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| | 1363 | symtab.symoff = off; |
| | 1364 | symtab.nsyms = @intCast(u32, nsyms); |
| | 1365 | |
| | 1366 | const segment_size = mem.alignForwardGeneric(u64, file_size, self.page_size); |
| | 1367 | linkedit.vmsize += segment_size; |
| | 1368 | // TODO this is needed to please codesign_allocate |
| | 1369 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfo; |
| | 1370 | dyld_info.export_size = off - dyld_info.export_off; |
| | 1371 | } |
| | 1372 | if (symtab.stroff == 0) { |
| | 1373 | try self.string_table.append(self.base.allocator, 0); |
| | 1374 | const file_size = @intCast(u32, self.string_table.items.len); |
| | 1375 | const off = @intCast(u32, self.findFreeSpace(file_size, self.page_size)); |
| | 1376 | log.debug("found string table free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| | 1377 | symtab.stroff = off; |
| | 1378 | symtab.strsize = file_size; |
| | 1379 | |
| | 1380 | const segment_size = mem.alignForwardGeneric(u64, file_size, self.page_size); |
| | 1381 | linkedit.vmsize += segment_size; |
| | 1382 | } |
| | 1383 | } |
| 1333 | if (self.dylinker_cmd_index == null) { | 1384 | if (self.dylinker_cmd_index == null) { |
| 1334 | self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len); | 1385 | self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1335 | const cmdsize = mem.alignForwardGeneric(u64, @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH), @sizeOf(u64)); | 1386 | const cmdsize = mem.alignForwardGeneric(u64, @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH), @sizeOf(u64)); |
| ... | @@ -1653,53 +1704,6 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void { | ... | @@ -1653,53 +1704,6 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void { |
| 1653 | try self.base.file.?.pwriteAll(&buf, off); | 1704 | try self.base.file.?.pwriteAll(&buf, off); |
| 1654 | } | 1705 | } |
| 1655 | | 1706 | |
| 1656 | fn writeSymbolTable(self: *MachO) !void { | | |
| 1657 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | | |
| 1658 | const locals_off = symtab.symoff; | | |
| 1659 | | | |
| 1660 | var locals = try self.base.allocator.alloc(macho.nlist_64, self.local_symbols.items.len - 1); | | |
| 1661 | defer self.base.allocator.free(locals); | | |
| 1662 | | | |
| 1663 | for (locals) |*sym, i| { | | |
| 1664 | sym.* = .{ | | |
| 1665 | .n_strx = self.local_symbols.items[i + 1].n_strx, | | |
| 1666 | .n_type = self.local_symbols.items[i + 1].n_type, | | |
| 1667 | .n_sect = self.local_symbols.items[i + 1].n_sect, | | |
| 1668 | .n_desc = self.local_symbols.items[i + 1].n_desc, | | |
| 1669 | .n_value = self.local_symbols.items[i + 1].n_value, | | |
| 1670 | }; | | |
| 1671 | } | | |
| 1672 | | | |
| 1673 | const locals_size = locals.len * @sizeOf(macho.nlist_64); | | |
| 1674 | log.debug("writing local symbols from 0x{x} to 0x{x}\n", .{ locals_off, locals_size + locals_off }); | | |
| 1675 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(locals), locals_off); | | |
| 1676 | | | |
| 1677 | const globals_off = locals_off + locals_size; | | |
| 1678 | const globals_size = self.global_symbols.items.len * @sizeOf(macho.nlist_64); | | |
| 1679 | log.debug("writing global symbols from 0x{x} to 0x{x}\n", .{ globals_off, globals_size + globals_off }); | | |
| 1680 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.global_symbols.items), globals_off); | | |
| 1681 | | | |
| 1682 | const undefs_off = globals_off + globals_size; | | |
| 1683 | const undefs_size = self.undef_symbols.items.len * @sizeOf(macho.nlist_64); | | |
| 1684 | log.debug("writing undef symbols from 0x{x} to 0x{x}\n", .{ undefs_off, undefs_size + undefs_off }); | | |
| 1685 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.undef_symbols.items), undefs_off); | | |
| 1686 | | | |
| 1687 | // Update symbol table. | | |
| 1688 | const nlocals = @intCast(u32, locals.len); | | |
| 1689 | const nglobals = @intCast(u32, self.global_symbols.items.len); | | |
| 1690 | const nundefs = @intCast(u32, self.undef_symbols.items.len); | | |
| 1691 | symtab.nsyms = nlocals + nglobals + nundefs; | | |
| 1692 | symtab.stroff = symtab.symoff + symtab.nsyms * @sizeOf(macho.nlist_64); | | |
| 1693 | | | |
| 1694 | // Update dynamic symbol table. | | |
| 1695 | const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; | | |
| 1696 | dysymtab.nlocalsym = nlocals; | | |
| 1697 | dysymtab.iextdefsym = nlocals; | | |
| 1698 | dysymtab.nextdefsym = nglobals; | | |
| 1699 | dysymtab.iundefsym = nlocals + nglobals; | | |
| 1700 | dysymtab.nundefsym = nundefs; | | |
| 1701 | } | | |
| 1702 | | | |
| 1703 | fn writeAllGlobalSymbols(self: *MachO) !void { | 1707 | fn writeAllGlobalSymbols(self: *MachO) !void { |
| 1704 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | 1708 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 1705 | const off = symtab.symoff + self.local_symbols.items.len * @sizeOf(macho.nlist_64); | 1709 | const off = symtab.symoff + self.local_symbols.items.len * @sizeOf(macho.nlist_64); |
| ... | @@ -1741,26 +1745,27 @@ fn writeExportTrie(self: *MachO) !void { | ... | @@ -1741,26 +1745,27 @@ fn writeExportTrie(self: *MachO) !void { |
| 1741 | | 1745 | |
| 1742 | try trie.writeULEB128Mem(self.base.allocator, &buffer); | 1746 | try trie.writeULEB128Mem(self.base.allocator, &buffer); |
| 1743 | | 1747 | |
| 1744 | const data = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | | |
| 1745 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfo; | 1748 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfo; |
| 1746 | dyld_info.export_off = @intCast(u32, data.fileoff + data.filesize); | | |
| 1747 | dyld_info.export_size = mem.alignForwardGeneric(u32, @intCast(u32, buffer.items.len), @sizeOf(u64)); | | |
| 1748 | try self.base.file.?.pwriteAll(buffer.items, dyld_info.export_off); | 1749 | try self.base.file.?.pwriteAll(buffer.items, dyld_info.export_off); |
| 1749 | } | 1750 | } |
| 1750 | | 1751 | |
| 1751 | fn writeStringTable(self: *MachO) !void { | 1752 | fn writeStringTable(self: *MachO) !void { |
| 1752 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | 1753 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 1753 | // const allocated_size = self.allocatedSize(symtab.stroff); | 1754 | const allocated_size = self.allocatedSize(symtab.stroff); |
| 1754 | const needed_size = self.string_table.items.len; | 1755 | const needed_size = self.string_table.items.len; |
| 1755 | | 1756 | |
| 1756 | // if (needed_size > allocated_size) { | 1757 | if (needed_size > allocated_size) { |
| 1757 | // symtab.strsize = 0; | 1758 | symtab.strsize = 0; |
| 1758 | // symtab.stroff = @intCast(u32, self.findFreeSpace(needed_size, 1)); | 1759 | symtab.stroff = @intCast(u32, self.findFreeSpace(needed_size, 1)); |
| 1759 | // } | 1760 | } |
| 1760 | symtab.strsize = mem.alignForwardGeneric(u32, @intCast(u32, needed_size), @sizeOf(u64)); | 1761 | symtab.strsize = @intCast(u32, needed_size); |
| 1761 | try self.base.file.?.pwriteAll(&[_]u8{0}, symtab.stroff + symtab.strsize - 1); | | |
| 1762 | log.debug("writing string table from 0x{x} to 0x{x}\n", .{ symtab.stroff, symtab.stroff + needed_size }); | 1762 | log.debug("writing string table from 0x{x} to 0x{x}\n", .{ symtab.stroff, symtab.stroff + needed_size }); |
| 1763 | try self.base.file.?.pwriteAll(self.string_table.items, symtab.stroff); | 1763 | try self.base.file.?.pwriteAll(self.string_table.items, symtab.stroff); |
| | 1764 | |
| | 1765 | // TODO rework how we preallocate space for the entire __LINKEDIT segment instead of |
| | 1766 | // doing dynamic updates like this. |
| | 1767 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| | 1768 | linkedit.filesize = symtab.stroff + symtab.strsize - linkedit.fileoff; |
| 1764 | } | 1769 | } |
| 1765 | | 1770 | |
| 1766 | fn writeCmdHeaders(self: *MachO) !void { | 1771 | fn writeCmdHeaders(self: *MachO) !void { |