| ... | @@ -525,6 +525,9 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node | ... | @@ -525,6 +525,9 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node |
| 525 | }, | 525 | }, |
| 526 | }; | 526 | }; |
| 527 | | 527 | |
| | 528 | try self.initOutputSections(); |
| | 529 | try self.initSyntheticSections(); |
| | 530 | |
| 528 | state_log.debug("{}", .{self.dumpState()}); | 531 | state_log.debug("{}", .{self.dumpState()}); |
| 529 | | 532 | |
| 530 | @panic("TODO"); | 533 | @panic("TODO"); |
| ... | @@ -716,26 +719,6 @@ fn flushObject(self: *MachO, comp: *Compilation, module_obj_path: ?[]const u8) l | ... | @@ -716,26 +719,6 @@ fn flushObject(self: *MachO, comp: *Compilation, module_obj_path: ?[]const u8) l |
| 716 | return error.FlushFailure; | 719 | return error.FlushFailure; |
| 717 | } | 720 | } |
| 718 | | 721 | |
| 719 | /// XNU starting with Big Sur running on arm64 is caching inodes of running binaries. | | |
| 720 | /// Any change to the binary will effectively invalidate the kernel's cache | | |
| 721 | /// resulting in a SIGKILL on each subsequent run. Since when doing incremental | | |
| 722 | /// linking we're modifying a binary in-place, this will end up with the kernel | | |
| 723 | /// killing it on every subsequent run. To circumvent it, we will copy the file | | |
| 724 | /// into a new inode, remove the original file, and rename the copy to match | | |
| 725 | /// the original file. This is super messy, but there doesn't seem any other | | |
| 726 | /// way to please the XNU. | | |
| 727 | pub fn invalidateKernelCache(dir: std.fs.Dir, sub_path: []const u8) !void { | | |
| 728 | if (comptime builtin.target.isDarwin() and builtin.target.cpu.arch == .aarch64) { | | |
| 729 | try dir.copyFile(sub_path, dir, sub_path, .{}); | | |
| 730 | } | | |
| 731 | } | | |
| 732 | | | |
| 733 | inline fn conformUuid(out: *[Md5.digest_length]u8) void { | | |
| 734 | // LC_UUID uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats | | |
| 735 | out[6] = (out[6] & 0x0F) | (3 << 4); | | |
| 736 | out[8] = (out[8] & 0x3F) | 0x80; | | |
| 737 | } | | |
| 738 | | | |
| 739 | pub fn resolveLibSystem( | 722 | pub fn resolveLibSystem( |
| 740 | self: *MachO, | 723 | self: *MachO, |
| 741 | arena: Allocator, | 724 | arena: Allocator, |
| ... | @@ -1556,6 +1539,134 @@ fn reportUndefs(self: *MachO) !void { | ... | @@ -1556,6 +1539,134 @@ fn reportUndefs(self: *MachO) !void { |
| 1556 | if (has_undefs) return error.HasUndefinedSymbols; | 1539 | if (has_undefs) return error.HasUndefinedSymbols; |
| 1557 | } | 1540 | } |
| 1558 | | 1541 | |
| | 1542 | fn initOutputSections(self: *MachO) !void { |
| | 1543 | for (self.objects.items) |index| { |
| | 1544 | const object = self.getFile(index).?.object; |
| | 1545 | for (object.atoms.items) |atom_index| { |
| | 1546 | const atom = self.getAtom(atom_index) orelse continue; |
| | 1547 | if (!atom.flags.alive) continue; |
| | 1548 | atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(self), self); |
| | 1549 | } |
| | 1550 | } |
| | 1551 | if (self.getInternalObject()) |object| { |
| | 1552 | for (object.atoms.items) |atom_index| { |
| | 1553 | const atom = self.getAtom(atom_index) orelse continue; |
| | 1554 | if (!atom.flags.alive) continue; |
| | 1555 | atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(self), self); |
| | 1556 | } |
| | 1557 | } |
| | 1558 | if (self.data_sect_index == null) { |
| | 1559 | self.data_sect_index = try self.addSection("__DATA", "__data", .{}); |
| | 1560 | } |
| | 1561 | } |
| | 1562 | |
| | 1563 | fn initSyntheticSections(self: *MachO) !void { |
| | 1564 | const cpu_arch = self.getTarget().cpu.arch; |
| | 1565 | |
| | 1566 | if (self.got.symbols.items.len > 0) { |
| | 1567 | self.got_sect_index = try self.addSection("__DATA_CONST", "__got", .{ |
| | 1568 | .flags = macho.S_NON_LAZY_SYMBOL_POINTERS, |
| | 1569 | .reserved1 = @intCast(self.stubs.symbols.items.len), |
| | 1570 | }); |
| | 1571 | } |
| | 1572 | |
| | 1573 | if (self.stubs.symbols.items.len > 0) { |
| | 1574 | self.stubs_sect_index = try self.addSection("__TEXT", "__stubs", .{ |
| | 1575 | .flags = macho.S_SYMBOL_STUBS | |
| | 1576 | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, |
| | 1577 | .reserved1 = 0, |
| | 1578 | .reserved2 = switch (cpu_arch) { |
| | 1579 | .x86_64 => 6, |
| | 1580 | .aarch64 => 3 * @sizeOf(u32), |
| | 1581 | else => 0, |
| | 1582 | }, |
| | 1583 | }); |
| | 1584 | self.stubs_helper_sect_index = try self.addSection("__TEXT", "__stub_helper", .{ |
| | 1585 | .flags = macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, |
| | 1586 | }); |
| | 1587 | self.la_symbol_ptr_sect_index = try self.addSection("__DATA", "__la_symbol_ptr", .{ |
| | 1588 | .flags = macho.S_LAZY_SYMBOL_POINTERS, |
| | 1589 | .reserved1 = @intCast(self.stubs.symbols.items.len + self.got.symbols.items.len), |
| | 1590 | }); |
| | 1591 | } |
| | 1592 | |
| | 1593 | if (self.objc_stubs.symbols.items.len > 0) { |
| | 1594 | self.objc_stubs_sect_index = try self.addSection("__TEXT", "__objc_stubs", .{ |
| | 1595 | .flags = macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, |
| | 1596 | }); |
| | 1597 | } |
| | 1598 | |
| | 1599 | if (self.tlv_ptr.symbols.items.len > 0) { |
| | 1600 | self.tlv_ptr_sect_index = try self.addSection("__DATA", "__thread_ptrs", .{ |
| | 1601 | .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS, |
| | 1602 | }); |
| | 1603 | } |
| | 1604 | |
| | 1605 | const needs_unwind_info = for (self.objects.items) |index| { |
| | 1606 | if (self.getFile(index).?.object.compact_unwind_sect_index != null) break true; |
| | 1607 | } else false; |
| | 1608 | if (needs_unwind_info) { |
| | 1609 | self.unwind_info_sect_index = try self.addSection("__TEXT", "__unwind_info", .{}); |
| | 1610 | } |
| | 1611 | |
| | 1612 | const needs_eh_frame = for (self.objects.items) |index| { |
| | 1613 | if (self.getFile(index).?.object.eh_frame_sect_index != null) break true; |
| | 1614 | } else false; |
| | 1615 | if (needs_eh_frame) { |
| | 1616 | assert(needs_unwind_info); |
| | 1617 | self.eh_frame_sect_index = try self.addSection("__TEXT", "__eh_frame", .{}); |
| | 1618 | } |
| | 1619 | |
| | 1620 | for (self.boundary_symbols.items) |sym_index| { |
| | 1621 | const gpa = self.base.comp.gpa; |
| | 1622 | const sym = self.getSymbol(sym_index); |
| | 1623 | const name = sym.getName(self); |
| | 1624 | |
| | 1625 | if (eatPrefix(name, "segment$start$")) |segname| { |
| | 1626 | if (self.getSegmentByName(segname) == null) { // TODO check segname is valid |
| | 1627 | const prot = getSegmentProt(segname); |
| | 1628 | _ = try self.segments.append(gpa, .{ |
| | 1629 | .cmdsize = @sizeOf(macho.segment_command_64), |
| | 1630 | .segname = makeStaticString(segname), |
| | 1631 | .initprot = prot, |
| | 1632 | .maxprot = prot, |
| | 1633 | }); |
| | 1634 | } |
| | 1635 | } else if (eatPrefix(name, "segment$stop$")) |segname| { |
| | 1636 | if (self.getSegmentByName(segname) == null) { // TODO check segname is valid |
| | 1637 | const prot = getSegmentProt(segname); |
| | 1638 | _ = try self.segments.append(gpa, .{ |
| | 1639 | .cmdsize = @sizeOf(macho.segment_command_64), |
| | 1640 | .segname = makeStaticString(segname), |
| | 1641 | .initprot = prot, |
| | 1642 | .maxprot = prot, |
| | 1643 | }); |
| | 1644 | } |
| | 1645 | } else if (eatPrefix(name, "section$start$")) |actual_name| { |
| | 1646 | const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic |
| | 1647 | const segname = actual_name[0..sep]; // TODO check segname is valid |
| | 1648 | const sectname = actual_name[sep + 1 ..]; // TODO check sectname is valid |
| | 1649 | if (self.getSectionByName(segname, sectname) == null) { |
| | 1650 | _ = try self.addSection(segname, sectname, .{}); |
| | 1651 | } |
| | 1652 | } else if (eatPrefix(name, "section$stop$")) |actual_name| { |
| | 1653 | const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic |
| | 1654 | const segname = actual_name[0..sep]; // TODO check segname is valid |
| | 1655 | const sectname = actual_name[sep + 1 ..]; // TODO check sectname is valid |
| | 1656 | if (self.getSectionByName(segname, sectname) == null) { |
| | 1657 | _ = try self.addSection(segname, sectname, .{}); |
| | 1658 | } |
| | 1659 | } else unreachable; |
| | 1660 | } |
| | 1661 | } |
| | 1662 | |
| | 1663 | fn getSegmentProt(segname: []const u8) macho.vm_prot_t { |
| | 1664 | if (mem.eql(u8, segname, "__PAGEZERO")) return macho.PROT.NONE; |
| | 1665 | if (mem.eql(u8, segname, "__TEXT")) return macho.PROT.READ | macho.PROT.EXEC; |
| | 1666 | if (mem.eql(u8, segname, "__LINKEDIT")) return macho.PROT.READ; |
| | 1667 | return macho.PROT.READ | macho.PROT.WRITE; |
| | 1668 | } |
| | 1669 | |
| 1559 | fn shrinkAtom(self: *MachO, atom_index: Atom.Index, new_block_size: u64) void { | 1670 | fn shrinkAtom(self: *MachO, atom_index: Atom.Index, new_block_size: u64) void { |
| 1560 | _ = self; | 1671 | _ = self; |
| 1561 | _ = atom_index; | 1672 | _ = atom_index; |
| ... | @@ -1786,12 +1897,111 @@ pub fn getTarget(self: MachO) std.Target { | ... | @@ -1786,12 +1897,111 @@ pub fn getTarget(self: MachO) std.Target { |
| 1786 | return self.base.comp.root_mod.resolved_target.result; | 1897 | return self.base.comp.root_mod.resolved_target.result; |
| 1787 | } | 1898 | } |
| 1788 | | 1899 | |
| | 1900 | /// XNU starting with Big Sur running on arm64 is caching inodes of running binaries. |
| | 1901 | /// Any change to the binary will effectively invalidate the kernel's cache |
| | 1902 | /// resulting in a SIGKILL on each subsequent run. Since when doing incremental |
| | 1903 | /// linking we're modifying a binary in-place, this will end up with the kernel |
| | 1904 | /// killing it on every subsequent run. To circumvent it, we will copy the file |
| | 1905 | /// into a new inode, remove the original file, and rename the copy to match |
| | 1906 | /// the original file. This is super messy, but there doesn't seem any other |
| | 1907 | /// way to please the XNU. |
| | 1908 | pub fn invalidateKernelCache(dir: std.fs.Dir, sub_path: []const u8) !void { |
| | 1909 | if (comptime builtin.target.isDarwin() and builtin.target.cpu.arch == .aarch64) { |
| | 1910 | try dir.copyFile(sub_path, dir, sub_path, .{}); |
| | 1911 | } |
| | 1912 | } |
| | 1913 | |
| | 1914 | inline fn conformUuid(out: *[Md5.digest_length]u8) void { |
| | 1915 | // LC_UUID uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats |
| | 1916 | out[6] = (out[6] & 0x0F) | (3 << 4); |
| | 1917 | out[8] = (out[8] & 0x3F) | 0x80; |
| | 1918 | } |
| | 1919 | |
| | 1920 | pub inline fn getPageSize(self: MachO) u16 { |
| | 1921 | return switch (self.getTarget().cpu.arch) { |
| | 1922 | .aarch64 => 0x4000, |
| | 1923 | .x86_64 => 0x1000, |
| | 1924 | else => unreachable, |
| | 1925 | }; |
| | 1926 | } |
| | 1927 | |
| | 1928 | pub fn requiresCodeSig(self: MachO) bool { |
| | 1929 | if (self.entitlements) |_| return true; |
| | 1930 | // if (self.options.adhoc_codesign) |cs| return cs; |
| | 1931 | return switch (self.getTarget().cpu.arch) { |
| | 1932 | .aarch64 => true, |
| | 1933 | else => false, |
| | 1934 | }; |
| | 1935 | } |
| | 1936 | |
| | 1937 | inline fn requiresThunks(self: MachO) bool { |
| | 1938 | return self.getTarget().cpu.arch == .aarch64; |
| | 1939 | } |
| | 1940 | |
| | 1941 | const AddSectionOpts = struct { |
| | 1942 | flags: u32 = macho.S_REGULAR, |
| | 1943 | reserved1: u32 = 0, |
| | 1944 | reserved2: u32 = 0, |
| | 1945 | }; |
| | 1946 | |
| | 1947 | pub fn addSection( |
| | 1948 | self: *MachO, |
| | 1949 | segname: []const u8, |
| | 1950 | sectname: []const u8, |
| | 1951 | opts: AddSectionOpts, |
| | 1952 | ) !u8 { |
| | 1953 | const gpa = self.base.comp.gpa; |
| | 1954 | const index = @as(u8, @intCast(try self.sections.addOne(gpa))); |
| | 1955 | self.sections.set(index, .{ |
| | 1956 | .segment_id = 0, // Segments will be created automatically later down the pipeline. |
| | 1957 | .header = .{ |
| | 1958 | .sectname = makeStaticString(sectname), |
| | 1959 | .segname = makeStaticString(segname), |
| | 1960 | .flags = opts.flags, |
| | 1961 | .reserved1 = opts.reserved1, |
| | 1962 | .reserved2 = opts.reserved2, |
| | 1963 | }, |
| | 1964 | }); |
| | 1965 | return index; |
| | 1966 | } |
| | 1967 | |
| 1789 | pub fn makeStaticString(bytes: []const u8) [16]u8 { | 1968 | pub fn makeStaticString(bytes: []const u8) [16]u8 { |
| 1790 | var buf = [_]u8{0} ** 16; | 1969 | var buf = [_]u8{0} ** 16; |
| 1791 | @memcpy(buf[0..bytes.len], bytes); | 1970 | @memcpy(buf[0..bytes.len], bytes); |
| 1792 | return buf; | 1971 | return buf; |
| 1793 | } | 1972 | } |
| 1794 | | 1973 | |
| | 1974 | pub fn getSegmentByName(self: MachO, segname: []const u8) ?u8 { |
| | 1975 | for (self.segments.items, 0..) |seg, i| { |
| | 1976 | if (mem.eql(u8, segname, seg.segName())) return @as(u8, @intCast(i)); |
| | 1977 | } else return null; |
| | 1978 | } |
| | 1979 | |
| | 1980 | pub fn getSectionByName(self: MachO, segname: []const u8, sectname: []const u8) ?u8 { |
| | 1981 | for (self.sections.items(.header), 0..) |header, i| { |
| | 1982 | if (mem.eql(u8, header.segName(), segname) and mem.eql(u8, header.sectName(), sectname)) |
| | 1983 | return @as(u8, @intCast(i)); |
| | 1984 | } else return null; |
| | 1985 | } |
| | 1986 | |
| | 1987 | pub fn getTlsAddress(self: MachO) u64 { |
| | 1988 | for (self.sections.items(.header)) |header| switch (header.type()) { |
| | 1989 | macho.S_THREAD_LOCAL_REGULAR, |
| | 1990 | macho.S_THREAD_LOCAL_ZEROFILL, |
| | 1991 | => return header.addr, |
| | 1992 | else => {}, |
| | 1993 | }; |
| | 1994 | return 0; |
| | 1995 | } |
| | 1996 | |
| | 1997 | pub inline fn getTextSegment(self: *MachO) *macho.segment_command_64 { |
| | 1998 | return &self.segments.items[self.text_seg_index.?]; |
| | 1999 | } |
| | 2000 | |
| | 2001 | pub inline fn getLinkeditSegment(self: *MachO) *macho.segment_command_64 { |
| | 2002 | return &self.segments.items[self.linkedit_seg_index.?]; |
| | 2003 | } |
| | 2004 | |
| 1795 | pub fn getFile(self: *MachO, index: File.Index) ?File { | 2005 | pub fn getFile(self: *MachO, index: File.Index) ?File { |
| 1796 | const tag = self.files.items(.tags)[index]; | 2006 | const tag = self.files.items(.tags)[index]; |
| 1797 | return switch (tag) { | 2007 | return switch (tag) { |