| ... | @@ -89,7 +89,7 @@ weak_defines: bool = false, | ... | @@ -89,7 +89,7 @@ weak_defines: bool = false, |
| 89 | /// SDK layout | 89 | /// SDK layout |
| 90 | sdk_layout: ?SdkLayout, | 90 | sdk_layout: ?SdkLayout, |
| 91 | /// Size of the __PAGEZERO segment. | 91 | /// Size of the __PAGEZERO segment. |
| 92 | pagezero_vmsize: ?u64, | 92 | pagezero_size: ?u64, |
| 93 | /// Minimum space for future expansion of the load commands. | 93 | /// Minimum space for future expansion of the load commands. |
| 94 | headerpad_size: ?u32, | 94 | headerpad_size: ?u32, |
| 95 | /// Set enough space as if all paths were MATPATHLEN. | 95 | /// Set enough space as if all paths were MATPATHLEN. |
| ... | @@ -170,7 +170,7 @@ pub fn createEmpty( | ... | @@ -170,7 +170,7 @@ pub fn createEmpty( |
| 170 | .build_id = options.build_id, | 170 | .build_id = options.build_id, |
| 171 | .rpath_list = options.rpath_list, | 171 | .rpath_list = options.rpath_list, |
| 172 | }, | 172 | }, |
| 173 | .pagezero_vmsize = options.pagezero_size, | 173 | .pagezero_size = options.pagezero_size, |
| 174 | .headerpad_size = options.headerpad_size, | 174 | .headerpad_size = options.headerpad_size, |
| 175 | .headerpad_max_install_names = options.headerpad_max_install_names, | 175 | .headerpad_max_install_names = options.headerpad_max_install_names, |
| 176 | .dead_strip_dylibs = options.dead_strip_dylibs, | 176 | .dead_strip_dylibs = options.dead_strip_dylibs, |
| ... | @@ -527,6 +527,11 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node | ... | @@ -527,6 +527,11 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node |
| 527 | | 527 | |
| 528 | try self.initOutputSections(); | 528 | try self.initOutputSections(); |
| 529 | try self.initSyntheticSections(); | 529 | try self.initSyntheticSections(); |
| | 530 | try self.sortSections(); |
| | 531 | try self.addAtomsToSections(); |
| | 532 | try self.calcSectionSizes(); |
| | 533 | try self.generateUnwindInfo(); |
| | 534 | try self.initSegments(); |
| 530 | | 535 | |
| 531 | state_log.debug("{}", .{self.dumpState()}); | 536 | state_log.debug("{}", .{self.dumpState()}); |
| 532 | | 537 | |
| ... | @@ -613,7 +618,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { | ... | @@ -613,7 +618,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { |
| 613 | try argv.append(rpath); | 618 | try argv.append(rpath); |
| 614 | } | 619 | } |
| 615 | | 620 | |
| 616 | if (self.pagezero_vmsize) |size| { | 621 | if (self.pagezero_size) |size| { |
| 617 | try argv.append("-pagezero_size"); | 622 | try argv.append("-pagezero_size"); |
| 618 | try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{size})); | 623 | try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{size})); |
| 619 | } | 624 | } |
| ... | @@ -1667,6 +1672,350 @@ fn getSegmentProt(segname: []const u8) macho.vm_prot_t { | ... | @@ -1667,6 +1672,350 @@ fn getSegmentProt(segname: []const u8) macho.vm_prot_t { |
| 1667 | return macho.PROT.READ | macho.PROT.WRITE; | 1672 | return macho.PROT.READ | macho.PROT.WRITE; |
| 1668 | } | 1673 | } |
| 1669 | | 1674 | |
| | 1675 | fn getSegmentRank(segname: []const u8) u4 { |
| | 1676 | if (mem.eql(u8, segname, "__PAGEZERO")) return 0x0; |
| | 1677 | if (mem.eql(u8, segname, "__TEXT")) return 0x1; |
| | 1678 | if (mem.eql(u8, segname, "__DATA_CONST")) return 0x2; |
| | 1679 | if (mem.eql(u8, segname, "__DATA")) return 0x3; |
| | 1680 | if (mem.eql(u8, segname, "__LINKEDIT")) return 0x5; |
| | 1681 | return 0x4; |
| | 1682 | } |
| | 1683 | |
| | 1684 | fn getSectionRank(self: *MachO, sect_index: u8) u8 { |
| | 1685 | const header = self.sections.items(.header)[sect_index]; |
| | 1686 | const segment_rank = getSegmentRank(header.segName()); |
| | 1687 | const section_rank: u4 = blk: { |
| | 1688 | if (header.isCode()) { |
| | 1689 | if (mem.eql(u8, "__text", header.sectName())) break :blk 0x0; |
| | 1690 | if (header.type() == macho.S_SYMBOL_STUBS) break :blk 0x1; |
| | 1691 | break :blk 0x2; |
| | 1692 | } |
| | 1693 | switch (header.type()) { |
| | 1694 | macho.S_NON_LAZY_SYMBOL_POINTERS, |
| | 1695 | macho.S_LAZY_SYMBOL_POINTERS, |
| | 1696 | => break :blk 0x0, |
| | 1697 | |
| | 1698 | macho.S_MOD_INIT_FUNC_POINTERS => break :blk 0x1, |
| | 1699 | macho.S_MOD_TERM_FUNC_POINTERS => break :blk 0x2, |
| | 1700 | macho.S_ZEROFILL => break :blk 0xf, |
| | 1701 | macho.S_THREAD_LOCAL_REGULAR => break :blk 0xd, |
| | 1702 | macho.S_THREAD_LOCAL_ZEROFILL => break :blk 0xe, |
| | 1703 | |
| | 1704 | else => { |
| | 1705 | if (mem.eql(u8, "__unwind_info", header.sectName())) break :blk 0xe; |
| | 1706 | if (mem.eql(u8, "__compact_unwind", header.sectName())) break :blk 0xe; |
| | 1707 | if (mem.eql(u8, "__eh_frame", header.sectName())) break :blk 0xf; |
| | 1708 | break :blk 0x3; |
| | 1709 | }, |
| | 1710 | } |
| | 1711 | }; |
| | 1712 | return (@as(u8, @intCast(segment_rank)) << 4) + section_rank; |
| | 1713 | } |
| | 1714 | |
| | 1715 | pub fn sortSections(self: *MachO) !void { |
| | 1716 | const Entry = struct { |
| | 1717 | index: u8, |
| | 1718 | |
| | 1719 | pub fn lessThan(macho_file: *MachO, lhs: @This(), rhs: @This()) bool { |
| | 1720 | return macho_file.getSectionRank(lhs.index) < macho_file.getSectionRank(rhs.index); |
| | 1721 | } |
| | 1722 | }; |
| | 1723 | |
| | 1724 | const gpa = self.base.comp.gpa; |
| | 1725 | |
| | 1726 | var entries = try std.ArrayList(Entry).initCapacity(gpa, self.sections.slice().len); |
| | 1727 | defer entries.deinit(); |
| | 1728 | for (0..self.sections.slice().len) |index| { |
| | 1729 | entries.appendAssumeCapacity(.{ .index = @intCast(index) }); |
| | 1730 | } |
| | 1731 | |
| | 1732 | mem.sort(Entry, entries.items, self, Entry.lessThan); |
| | 1733 | |
| | 1734 | const backlinks = try gpa.alloc(u8, entries.items.len); |
| | 1735 | defer gpa.free(backlinks); |
| | 1736 | for (entries.items, 0..) |entry, i| { |
| | 1737 | backlinks[entry.index] = @intCast(i); |
| | 1738 | } |
| | 1739 | |
| | 1740 | var slice = self.sections.toOwnedSlice(); |
| | 1741 | defer slice.deinit(gpa); |
| | 1742 | |
| | 1743 | try self.sections.ensureTotalCapacity(gpa, slice.len); |
| | 1744 | for (entries.items) |sorted| { |
| | 1745 | self.sections.appendAssumeCapacity(slice.get(sorted.index)); |
| | 1746 | } |
| | 1747 | |
| | 1748 | for (self.objects.items) |index| { |
| | 1749 | for (self.getFile(index).?.object.atoms.items) |atom_index| { |
| | 1750 | const atom = self.getAtom(atom_index) orelse continue; |
| | 1751 | if (!atom.flags.alive) continue; |
| | 1752 | atom.out_n_sect = backlinks[atom.out_n_sect]; |
| | 1753 | } |
| | 1754 | } |
| | 1755 | if (self.getInternalObject()) |object| { |
| | 1756 | for (object.atoms.items) |atom_index| { |
| | 1757 | const atom = self.getAtom(atom_index) orelse continue; |
| | 1758 | if (!atom.flags.alive) continue; |
| | 1759 | atom.out_n_sect = backlinks[atom.out_n_sect]; |
| | 1760 | } |
| | 1761 | } |
| | 1762 | |
| | 1763 | for (&[_]*?u8{ |
| | 1764 | &self.data_sect_index, |
| | 1765 | &self.got_sect_index, |
| | 1766 | &self.stubs_sect_index, |
| | 1767 | &self.stubs_helper_sect_index, |
| | 1768 | &self.la_symbol_ptr_sect_index, |
| | 1769 | &self.tlv_ptr_sect_index, |
| | 1770 | &self.eh_frame_sect_index, |
| | 1771 | &self.unwind_info_sect_index, |
| | 1772 | &self.objc_stubs_sect_index, |
| | 1773 | }) |maybe_index| { |
| | 1774 | if (maybe_index.*) |*index| { |
| | 1775 | index.* = backlinks[index.*]; |
| | 1776 | } |
| | 1777 | } |
| | 1778 | } |
| | 1779 | |
| | 1780 | pub fn addAtomsToSections(self: *MachO) !void { |
| | 1781 | const tracy = trace(@src()); |
| | 1782 | defer tracy.end(); |
| | 1783 | |
| | 1784 | for (self.objects.items) |index| { |
| | 1785 | const object = self.getFile(index).?.object; |
| | 1786 | for (object.atoms.items) |atom_index| { |
| | 1787 | const atom = self.getAtom(atom_index) orelse continue; |
| | 1788 | if (!atom.flags.alive) continue; |
| | 1789 | const atoms = &self.sections.items(.atoms)[atom.out_n_sect]; |
| | 1790 | try atoms.append(self.base.comp.gpa, atom_index); |
| | 1791 | } |
| | 1792 | for (object.symbols.items) |sym_index| { |
| | 1793 | const sym = self.getSymbol(sym_index); |
| | 1794 | const atom = sym.getAtom(self) orelse continue; |
| | 1795 | if (!atom.flags.alive) continue; |
| | 1796 | if (sym.getFile(self).?.getIndex() != index) continue; |
| | 1797 | sym.out_n_sect = atom.out_n_sect; |
| | 1798 | } |
| | 1799 | } |
| | 1800 | if (self.getInternalObject()) |object| { |
| | 1801 | for (object.atoms.items) |atom_index| { |
| | 1802 | const atom = self.getAtom(atom_index) orelse continue; |
| | 1803 | if (!atom.flags.alive) continue; |
| | 1804 | const atoms = &self.sections.items(.atoms)[atom.out_n_sect]; |
| | 1805 | try atoms.append(self.base.comp.gpa, atom_index); |
| | 1806 | } |
| | 1807 | for (object.symbols.items) |sym_index| { |
| | 1808 | const sym = self.getSymbol(sym_index); |
| | 1809 | const atom = sym.getAtom(self) orelse continue; |
| | 1810 | if (!atom.flags.alive) continue; |
| | 1811 | if (sym.getFile(self).?.getIndex() != object.index) continue; |
| | 1812 | sym.out_n_sect = atom.out_n_sect; |
| | 1813 | } |
| | 1814 | } |
| | 1815 | } |
| | 1816 | |
| | 1817 | fn calcSectionSizes(self: *MachO) !void { |
| | 1818 | const tracy = trace(@src()); |
| | 1819 | defer tracy.end(); |
| | 1820 | |
| | 1821 | const cpu_arch = self.getTarget().cpu.arch; |
| | 1822 | |
| | 1823 | if (self.data_sect_index) |idx| { |
| | 1824 | const header = &self.sections.items(.header)[idx]; |
| | 1825 | header.size += @sizeOf(u64); |
| | 1826 | header.@"align" = 3; |
| | 1827 | } |
| | 1828 | |
| | 1829 | const slice = self.sections.slice(); |
| | 1830 | for (slice.items(.header), slice.items(.atoms)) |*header, atoms| { |
| | 1831 | if (atoms.items.len == 0) continue; |
| | 1832 | if (self.requiresThunks() and header.isCode()) continue; |
| | 1833 | |
| | 1834 | for (atoms.items) |atom_index| { |
| | 1835 | const atom = self.getAtom(atom_index).?; |
| | 1836 | const atom_alignment = atom.alignment.toByteUnits(1); |
| | 1837 | const offset = mem.alignForward(u64, header.size, atom_alignment); |
| | 1838 | const padding = offset - header.size; |
| | 1839 | atom.value = offset; |
| | 1840 | header.size += padding + atom.size; |
| | 1841 | header.@"align" = @max(header.@"align", atom.alignment.toLog2Units()); |
| | 1842 | } |
| | 1843 | } |
| | 1844 | |
| | 1845 | if (self.requiresThunks()) { |
| | 1846 | for (slice.items(.header), slice.items(.atoms), 0..) |header, atoms, i| { |
| | 1847 | if (!header.isCode()) continue; |
| | 1848 | if (atoms.items.len == 0) continue; |
| | 1849 | |
| | 1850 | // Create jump/branch range extenders if needed. |
| | 1851 | try thunks.createThunks(@intCast(i), self); |
| | 1852 | } |
| | 1853 | } |
| | 1854 | |
| | 1855 | if (self.got_sect_index) |idx| { |
| | 1856 | const header = &self.sections.items(.header)[idx]; |
| | 1857 | header.size = self.got.size(); |
| | 1858 | header.@"align" = 3; |
| | 1859 | } |
| | 1860 | |
| | 1861 | if (self.stubs_sect_index) |idx| { |
| | 1862 | const header = &self.sections.items(.header)[idx]; |
| | 1863 | header.size = self.stubs.size(self); |
| | 1864 | header.@"align" = switch (cpu_arch) { |
| | 1865 | .x86_64 => 0, |
| | 1866 | .aarch64 => 2, |
| | 1867 | else => 0, |
| | 1868 | }; |
| | 1869 | } |
| | 1870 | |
| | 1871 | if (self.stubs_helper_sect_index) |idx| { |
| | 1872 | const header = &self.sections.items(.header)[idx]; |
| | 1873 | header.size = self.stubs_helper.size(self); |
| | 1874 | header.@"align" = switch (cpu_arch) { |
| | 1875 | .x86_64 => 0, |
| | 1876 | .aarch64 => 2, |
| | 1877 | else => 0, |
| | 1878 | }; |
| | 1879 | } |
| | 1880 | |
| | 1881 | if (self.la_symbol_ptr_sect_index) |idx| { |
| | 1882 | const header = &self.sections.items(.header)[idx]; |
| | 1883 | header.size = self.la_symbol_ptr.size(self); |
| | 1884 | header.@"align" = 3; |
| | 1885 | } |
| | 1886 | |
| | 1887 | if (self.tlv_ptr_sect_index) |idx| { |
| | 1888 | const header = &self.sections.items(.header)[idx]; |
| | 1889 | header.size = self.tlv_ptr.size(); |
| | 1890 | header.@"align" = 3; |
| | 1891 | } |
| | 1892 | |
| | 1893 | if (self.objc_stubs_sect_index) |idx| { |
| | 1894 | const header = &self.sections.items(.header)[idx]; |
| | 1895 | header.size = self.objc_stubs.size(self); |
| | 1896 | header.@"align" = switch (cpu_arch) { |
| | 1897 | .x86_64 => 0, |
| | 1898 | .aarch64 => 2, |
| | 1899 | else => 0, |
| | 1900 | }; |
| | 1901 | } |
| | 1902 | } |
| | 1903 | |
| | 1904 | fn generateUnwindInfo(self: *MachO) !void { |
| | 1905 | const tracy = trace(@src()); |
| | 1906 | defer tracy.end(); |
| | 1907 | |
| | 1908 | if (self.eh_frame_sect_index) |index| { |
| | 1909 | const sect = &self.sections.items(.header)[index]; |
| | 1910 | sect.size = try eh_frame.calcSize(self); |
| | 1911 | sect.@"align" = 3; |
| | 1912 | } |
| | 1913 | if (self.unwind_info_sect_index) |index| { |
| | 1914 | const sect = &self.sections.items(.header)[index]; |
| | 1915 | self.unwind_info.generate(self) catch |err| switch (err) { |
| | 1916 | error.TooManyPersonalities => return self.reportUnexpectedError( |
| | 1917 | "too many personalities in unwind info", |
| | 1918 | .{}, |
| | 1919 | ), |
| | 1920 | else => |e| return e, |
| | 1921 | }; |
| | 1922 | sect.size = self.unwind_info.calcSize(); |
| | 1923 | sect.@"align" = 2; |
| | 1924 | } |
| | 1925 | } |
| | 1926 | |
| | 1927 | fn initSegments(self: *MachO) !void { |
| | 1928 | const gpa = self.base.comp.gpa; |
| | 1929 | const slice = self.sections.slice(); |
| | 1930 | |
| | 1931 | // First, create segments required by sections |
| | 1932 | for (slice.items(.header)) |header| { |
| | 1933 | const segname = header.segName(); |
| | 1934 | if (self.getSegmentByName(segname) == null) { |
| | 1935 | const prot = getSegmentProt(segname); |
| | 1936 | try self.segments.append(gpa, .{ |
| | 1937 | .cmdsize = @sizeOf(macho.segment_command_64), |
| | 1938 | .segname = makeStaticString(segname), |
| | 1939 | .maxprot = prot, |
| | 1940 | .initprot = prot, |
| | 1941 | }); |
| | 1942 | } |
| | 1943 | } |
| | 1944 | |
| | 1945 | // Add __PAGEZERO if required |
| | 1946 | const pagezero_size = self.pagezero_size orelse default_pagezero_size; |
| | 1947 | const aligned_pagezero_size = mem.alignBackward(u64, pagezero_size, self.getPageSize()); |
| | 1948 | if (!self.base.isDynLib() and aligned_pagezero_size > 0) { |
| | 1949 | if (aligned_pagezero_size != pagezero_size) { |
| | 1950 | // TODO convert into a warning |
| | 1951 | log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_size}); |
| | 1952 | log.warn(" rounding down to 0x{x}", .{aligned_pagezero_size}); |
| | 1953 | } |
| | 1954 | try self.segments.append(gpa, .{ |
| | 1955 | .cmdsize = @sizeOf(macho.segment_command_64), |
| | 1956 | .segname = makeStaticString("__PAGEZERO"), |
| | 1957 | .vmsize = aligned_pagezero_size, |
| | 1958 | }); |
| | 1959 | } |
| | 1960 | |
| | 1961 | // Add __LINKEDIT |
| | 1962 | { |
| | 1963 | const protection = getSegmentProt("__LINKEDIT"); |
| | 1964 | self.linkedit_seg_index = @intCast(self.segments.items.len); |
| | 1965 | try self.segments.append(gpa, .{ |
| | 1966 | .cmdsize = @sizeOf(macho.segment_command_64), |
| | 1967 | .segname = makeStaticString("__LINKEDIT"), |
| | 1968 | .maxprot = protection, |
| | 1969 | .initprot = protection, |
| | 1970 | }); |
| | 1971 | } |
| | 1972 | |
| | 1973 | // __TEXT segment is non-optional |
| | 1974 | if (self.getSegmentByName("__TEXT") == null) { |
| | 1975 | const protection = getSegmentProt("__TEXT"); |
| | 1976 | try self.segments.append(gpa, .{ |
| | 1977 | .cmdsize = @sizeOf(macho.segment_command_64), |
| | 1978 | .segname = makeStaticString("__TEXT"), |
| | 1979 | .maxprot = protection, |
| | 1980 | .initprot = protection, |
| | 1981 | }); |
| | 1982 | } |
| | 1983 | |
| | 1984 | const sortFn = struct { |
| | 1985 | fn sortFn(ctx: void, lhs: macho.segment_command_64, rhs: macho.segment_command_64) bool { |
| | 1986 | _ = ctx; |
| | 1987 | return getSegmentRank(lhs.segName()) < getSegmentRank(rhs.segName()); |
| | 1988 | } |
| | 1989 | }.sortFn; |
| | 1990 | |
| | 1991 | // Sort segments |
| | 1992 | mem.sort(macho.segment_command_64, self.segments.items, {}, sortFn); |
| | 1993 | |
| | 1994 | // Attach sections to segments |
| | 1995 | for (slice.items(.header), slice.items(.segment_id)) |header, *seg_id| { |
| | 1996 | const segname = header.segName(); |
| | 1997 | const segment_id = self.getSegmentByName(segname) orelse blk: { |
| | 1998 | const segment_id = @as(u8, @intCast(self.segments.items.len)); |
| | 1999 | const protection = getSegmentProt(segname); |
| | 2000 | try self.segments.append(gpa, .{ |
| | 2001 | .cmdsize = @sizeOf(macho.segment_command_64), |
| | 2002 | .segname = makeStaticString(segname), |
| | 2003 | .maxprot = protection, |
| | 2004 | .initprot = protection, |
| | 2005 | }); |
| | 2006 | break :blk segment_id; |
| | 2007 | }; |
| | 2008 | const segment = &self.segments.items[segment_id]; |
| | 2009 | segment.cmdsize += @sizeOf(macho.section_64); |
| | 2010 | segment.nsects += 1; |
| | 2011 | seg_id.* = segment_id; |
| | 2012 | } |
| | 2013 | |
| | 2014 | self.pagezero_seg_index = self.getSegmentByName("__PAGEZERO"); |
| | 2015 | self.text_seg_index = self.getSegmentByName("__TEXT").?; |
| | 2016 | self.linkedit_seg_index = self.getSegmentByName("__LINKEDIT").?; |
| | 2017 | } |
| | 2018 | |
| 1670 | fn shrinkAtom(self: *MachO, atom_index: Atom.Index, new_block_size: u64) void { | 2019 | fn shrinkAtom(self: *MachO, atom_index: Atom.Index, new_block_size: u64) void { |
| 1671 | _ = self; | 2020 | _ = self; |
| 1672 | _ = atom_index; | 2021 | _ = atom_index; |
| ... | @@ -2767,7 +3116,7 @@ pub const min_text_capacity = padToIdeal(minimum_text_block_size); | ... | @@ -2767,7 +3116,7 @@ pub const min_text_capacity = padToIdeal(minimum_text_block_size); |
| 2767 | | 3116 | |
| 2768 | /// Default virtual memory offset corresponds to the size of __PAGEZERO segment and | 3117 | /// Default virtual memory offset corresponds to the size of __PAGEZERO segment and |
| 2769 | /// start of __TEXT segment. | 3118 | /// start of __TEXT segment. |
| 2770 | pub const default_pagezero_vmsize: u64 = 0x100000000; | 3119 | pub const default_pagezero_size: u64 = 0x100000000; |
| 2771 | | 3120 | |
| 2772 | /// We commit 0x1000 = 4096 bytes of space to the header and | 3121 | /// We commit 0x1000 = 4096 bytes of space to the header and |
| 2773 | /// the table of load commands. This should be plenty for any | 3122 | /// the table of load commands. This should be plenty for any |