authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-01-11 19:51:29+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-01-24 12:34:39+01:00
log0b2231998f5b95845c8414083622ec7913d60af6
tree70f425ad73fbce603dee37c3bd14589730b1e7e1
parentf0119ce37339ef72acc527e28b2b611712cf24f0

macho: init output and synthetic sections


1 files changed, 230 insertions(+), 20 deletions(-)

src/link/MachO.zig+230-20
......@@ -525,6 +525,9 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
525525 },
526526 };
527527
528 try self.initOutputSections();
529 try self.initSyntheticSections();
530
528531 state_log.debug("{}", .{self.dumpState()});
529532
530533 @panic("TODO");
......@@ -716,26 +719,6 @@ fn flushObject(self: *MachO, comp: *Compilation, module_obj_path: ?[]const u8) l
716719 return error.FlushFailure;
717720}
718721
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.
727pub 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
733inline 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
739722pub fn resolveLibSystem(
740723 self: *MachO,
741724 arena: Allocator,
......@@ -1556,6 +1539,134 @@ fn reportUndefs(self: *MachO) !void {
15561539 if (has_undefs) return error.HasUndefinedSymbols;
15571540}
15581541
1542fn 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
1563fn 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
1663fn 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
15591670fn shrinkAtom(self: *MachO, atom_index: Atom.Index, new_block_size: u64) void {
15601671 _ = self;
15611672 _ = atom_index;
......@@ -1786,12 +1897,111 @@ pub fn getTarget(self: MachO) std.Target {
17861897 return self.base.comp.root_mod.resolved_target.result;
17871898}
17881899
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.
1908pub 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
1914inline 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
1920pub 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
1928pub 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
1937inline fn requiresThunks(self: MachO) bool {
1938 return self.getTarget().cpu.arch == .aarch64;
1939}
1940
1941const AddSectionOpts = struct {
1942 flags: u32 = macho.S_REGULAR,
1943 reserved1: u32 = 0,
1944 reserved2: u32 = 0,
1945};
1946
1947pub 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
17891968pub fn makeStaticString(bytes: []const u8) [16]u8 {
17901969 var buf = [_]u8{0} ** 16;
17911970 @memcpy(buf[0..bytes.len], bytes);
17921971 return buf;
17931972}
17941973
1974pub 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
1980pub 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
1987pub 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
1997pub inline fn getTextSegment(self: *MachO) *macho.segment_command_64 {
1998 return &self.segments.items[self.text_seg_index.?];
1999}
2000
2001pub inline fn getLinkeditSegment(self: *MachO) *macho.segment_command_64 {
2002 return &self.segments.items[self.linkedit_seg_index.?];
2003}
2004
17952005pub fn getFile(self: *MachO, index: File.Index) ?File {
17962006 const tag = self.files.items(.tags)[index];
17972007 return switch (tag) {