authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-01-10 16:02:07+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-01-10 16:02:07+01:00
loga4e6291fbdf83dec0d353af745c241bc7e01b3f2
treed7815bde6915af53e27531c5f1b3737c7fd61a5a
parent42ef95d79d9cb60fde8c83bfb2eef49969e7c8e3
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

stage2: enable zig test on x86_64-macos (#10551)

* stage2: put decls in different MachO sections Use `getDeclVAddrWithReloc` when targeting MachO backend rather than `getDeclVAddr` - this fn returns a zero vaddr and instead creates a relocation on the linker side which will get automatically updated whenever the target decl is moved in memory. This fn also records a rebase of the target pointer so that its value is correctly slid in presence of ASLR. This commit enables `zig test` on x86_64-macos. * stage2: fix output section selection for type,val pairs

3 files changed, 216 insertions(+), 47 deletions(-)

src/codegen.zig+9-3
...@@ -465,9 +465,15 @@ fn lowerDeclRef(...@@ -465,9 +465,15 @@ fn lowerDeclRef(
465465
466 if (decl.analysis != .complete) return error.AnalysisFail;466 if (decl.analysis != .complete) return error.AnalysisFail;
467 markDeclAlive(decl);467 markDeclAlive(decl);
468 // TODO handle the dependency of this symbol on the decl's vaddr.468 const vaddr = vaddr: {
469 // If the decl changes vaddr, then this symbol needs to get regenerated.469 if (bin_file.cast(link.File.MachO)) |macho_file| {
470 const vaddr = bin_file.getDeclVAddr(decl);470 break :vaddr try macho_file.getDeclVAddrWithReloc(decl, code.items.len);
471 }
472 // TODO handle the dependency of this symbol on the decl's vaddr.
473 // If the decl changes vaddr, then this symbol needs to get regenerated.
474 break :vaddr bin_file.getDeclVAddr(decl);
475 };
476
471 const endian = bin_file.options.target.cpu.arch.endian();477 const endian = bin_file.options.target.cpu.arch.endian();
472 switch (bin_file.options.target.cpu.arch.ptrBitWidth()) {478 switch (bin_file.options.target.cpu.arch.ptrBitWidth()) {
473 16 => mem.writeInt(u16, try code.addManyAsArray(2), @intCast(u16, vaddr), endian),479 16 => mem.writeInt(u16, try code.addManyAsArray(2), @intCast(u16, vaddr), endian),
src/link/MachO.zig+188-24
...@@ -38,6 +38,7 @@ const Module = @import("../Module.zig");...@@ -38,6 +38,7 @@ const Module = @import("../Module.zig");
38const StringIndexAdapter = std.hash_map.StringIndexAdapter;38const StringIndexAdapter = std.hash_map.StringIndexAdapter;
39const StringIndexContext = std.hash_map.StringIndexContext;39const StringIndexContext = std.hash_map.StringIndexContext;
40const Trie = @import("MachO/Trie.zig");40const Trie = @import("MachO/Trie.zig");
41const Type = @import("../type.zig").Type;
4142
42pub const TextBlock = Atom;43pub const TextBlock = Atom;
4344
...@@ -220,7 +221,7 @@ managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},...@@ -220,7 +221,7 @@ managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
220/// We store them here so that we can properly dispose of any allocated221/// We store them here so that we can properly dispose of any allocated
221/// memory within the atom in the incremental linker.222/// memory within the atom in the incremental linker.
222/// TODO consolidate this.223/// TODO consolidate this.
223decls: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{},224decls: std.AutoArrayHashMapUnmanaged(*Module.Decl, ?MatchingSection) = .{},
224225
225/// Currently active Module.Decl.226/// Currently active Module.Decl.
226/// TODO this might not be necessary if we figure out how to pass Module.Decl instance227/// TODO this might not be necessary if we figure out how to pass Module.Decl instance
...@@ -3450,7 +3451,7 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {...@@ -3450,7 +3451,7 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
3450 if (decl.link.macho.local_sym_index != 0) return;3451 if (decl.link.macho.local_sym_index != 0) return;
34513452
3452 try self.locals.ensureUnusedCapacity(self.base.allocator, 1);3453 try self.locals.ensureUnusedCapacity(self.base.allocator, 1);
3453 try self.decls.putNoClobber(self.base.allocator, decl, {});3454 try self.decls.putNoClobber(self.base.allocator, decl, null);
34543455
3455 if (self.locals_free_list.popOrNull()) |i| {3456 if (self.locals_free_list.popOrNull()) |i| {
3456 log.debug("reusing symbol index {d} for {s}", .{ i, decl.name });3457 log.debug("reusing symbol index {d} for {s}", .{ i, decl.name });
...@@ -3656,19 +3657,169 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -3656,19 +3657,169 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
3656 try self.updateDeclExports(module, decl, decl_exports);3657 try self.updateDeclExports(module, decl, decl_exports);
3657}3658}
36583659
3660fn isElemTyPointer(ty: Type) bool {
3661 switch (ty.zigTypeTag()) {
3662 .Fn => return false,
3663 .Pointer => return true,
3664 .Array => {
3665 const elem_ty = ty.elemType();
3666 return isElemTyPointer(elem_ty);
3667 },
3668 .Struct, .Union => {
3669 const len = ty.structFieldCount();
3670 var i: usize = 0;
3671 while (i < len) : (i += 1) {
3672 const field_ty = ty.structFieldType(i);
3673 if (isElemTyPointer(field_ty)) return true;
3674 }
3675 return false;
3676 },
3677 else => return false,
3678 }
3679}
3680
3681fn getMatchingSectionDecl(self: *MachO, decl: *Module.Decl) !MatchingSection {
3682 const code = decl.link.macho.code.items;
3683 const alignment = decl.ty.abiAlignment(self.base.options.target);
3684 const align_log_2 = math.log2(alignment);
3685 const ty = decl.ty;
3686 const zig_ty = ty.zigTypeTag();
3687 const val = decl.val;
3688 const mode = self.base.options.optimize_mode;
3689 const match: MatchingSection = blk: {
3690 // TODO finish and audit this function
3691 if (val.isUndefDeep()) {
3692 if (mode == .ReleaseFast or mode == .ReleaseSmall) {
3693 break :blk MatchingSection{
3694 .seg = self.data_segment_cmd_index.?,
3695 .sect = self.bss_section_index.?,
3696 };
3697 }
3698 break :blk (try self.getMatchingSection(.{
3699 .segname = makeStaticString("__DATA"),
3700 .sectname = makeStaticString("__data"),
3701 .size = code.len,
3702 .@"align" = align_log_2,
3703 })).?;
3704 }
3705
3706 switch (zig_ty) {
3707 .Fn => {
3708 break :blk MatchingSection{
3709 .seg = self.text_segment_cmd_index.?,
3710 .sect = self.text_section_index.?,
3711 };
3712 },
3713 .Array => switch (val.tag()) {
3714 .bytes => {
3715 switch (ty.tag()) {
3716 .array_u8_sentinel_0,
3717 .const_slice_u8_sentinel_0,
3718 .manyptr_const_u8_sentinel_0,
3719 => {
3720 break :blk (try self.getMatchingSection(.{
3721 .segname = makeStaticString("__TEXT"),
3722 .sectname = makeStaticString("__cstring"),
3723 .flags = macho.S_CSTRING_LITERALS,
3724 .size = code.len,
3725 .@"align" = align_log_2,
3726 })).?;
3727 },
3728 else => {
3729 break :blk (try self.getMatchingSection(.{
3730 .segname = makeStaticString("__TEXT"),
3731 .sectname = makeStaticString("__const"),
3732 .size = code.len,
3733 .@"align" = align_log_2,
3734 })).?;
3735 },
3736 }
3737 },
3738 .array => {
3739 if (isElemTyPointer(ty)) {
3740 break :blk (try self.getMatchingSection(.{
3741 .segname = makeStaticString("__DATA_CONST"),
3742 .sectname = makeStaticString("__const"),
3743 .size = code.len,
3744 .@"align" = 3, // TODO I think this should not be needed
3745 })).?;
3746 } else {
3747 break :blk (try self.getMatchingSection(.{
3748 .segname = makeStaticString("__TEXT"),
3749 .sectname = makeStaticString("__const"),
3750 .size = code.len,
3751 .@"align" = align_log_2,
3752 })).?;
3753 }
3754 },
3755 else => {
3756 break :blk (try self.getMatchingSection(.{
3757 .segname = makeStaticString("__TEXT"),
3758 .sectname = makeStaticString("__const"),
3759 .size = code.len,
3760 .@"align" = align_log_2,
3761 })).?;
3762 },
3763 },
3764 .Pointer => {
3765 if (val.castTag(.variable)) |_| {
3766 break :blk MatchingSection{
3767 .seg = self.data_segment_cmd_index.?,
3768 .sect = self.data_section_index.?,
3769 };
3770 } else {
3771 break :blk (try self.getMatchingSection(.{
3772 .segname = makeStaticString("__DATA_CONST"),
3773 .sectname = makeStaticString("__const"),
3774 .size = code.len,
3775 .@"align" = align_log_2,
3776 })).?;
3777 }
3778 },
3779 else => {
3780 if (val.castTag(.variable)) |_| {
3781 break :blk MatchingSection{
3782 .seg = self.data_segment_cmd_index.?,
3783 .sect = self.data_section_index.?,
3784 };
3785 } else {
3786 break :blk (try self.getMatchingSection(.{
3787 .segname = makeStaticString("__TEXT"),
3788 .sectname = makeStaticString("__const"),
3789 .size = code.len,
3790 .@"align" = align_log_2,
3791 })).?;
3792 }
3793 },
3794 }
3795 };
3796 const seg = self.load_commands.items[match.seg].segment;
3797 const sect = seg.sections.items[match.sect];
3798 log.debug(" allocating atom in '{s},{s}' ({d},{d})", .{
3799 sect.segName(),
3800 sect.sectName(),
3801 match.seg,
3802 match.sect,
3803 });
3804 return match;
3805}
3806
3659fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64 {3807fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64 {
3660 const required_alignment = decl.ty.abiAlignment(self.base.options.target);3808 const required_alignment = decl.ty.abiAlignment(self.base.options.target);
3661 assert(decl.link.macho.local_sym_index != 0); // Caller forgot to call allocateDeclIndexes()3809 assert(decl.link.macho.local_sym_index != 0); // Caller forgot to call allocateDeclIndexes()
3662 const symbol = &self.locals.items[decl.link.macho.local_sym_index];3810 const symbol = &self.locals.items[decl.link.macho.local_sym_index];
36633811
3812 const decl_ptr = self.decls.getPtr(decl).?;
3813 if (decl_ptr.* == null) {
3814 decl_ptr.* = try self.getMatchingSectionDecl(decl);
3815 }
3816 const match = decl_ptr.*.?;
3817
3664 if (decl.link.macho.size != 0) {3818 if (decl.link.macho.size != 0) {
3665 const capacity = decl.link.macho.capacity(self.*);3819 const capacity = decl.link.macho.capacity(self.*);
3666 const need_realloc = code_len > capacity or !mem.isAlignedGeneric(u64, symbol.n_value, required_alignment);3820 const need_realloc = code_len > capacity or !mem.isAlignedGeneric(u64, symbol.n_value, required_alignment);
3667 if (need_realloc) {3821 if (need_realloc) {
3668 const vaddr = try self.growAtom(&decl.link.macho, code_len, required_alignment, .{3822 const vaddr = try self.growAtom(&decl.link.macho, code_len, required_alignment, match);
3669 .seg = self.text_segment_cmd_index.?,
3670 .sect = self.text_section_index.?,
3671 });
36723823
3673 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr });3824 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr });
36743825
...@@ -3690,10 +3841,7 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64...@@ -3690,10 +3841,7 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64
36903841
3691 symbol.n_value = vaddr;3842 symbol.n_value = vaddr;
3692 } else if (code_len < decl.link.macho.size) {3843 } else if (code_len < decl.link.macho.size) {
3693 self.shrinkAtom(&decl.link.macho, code_len, .{3844 self.shrinkAtom(&decl.link.macho, code_len, match);
3694 .seg = self.text_segment_cmd_index.?,
3695 .sect = self.text_section_index.?,
3696 });
3697 }3845 }
3698 decl.link.macho.size = code_len;3846 decl.link.macho.size = code_len;
3699 decl.link.macho.dirty = true;3847 decl.link.macho.dirty = true;
...@@ -3714,22 +3862,16 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64...@@ -3714,22 +3862,16 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64
3714 defer self.base.allocator.free(decl_name);3862 defer self.base.allocator.free(decl_name);
37153863
3716 const name_str_index = try self.makeString(decl_name);3864 const name_str_index = try self.makeString(decl_name);
3717 const addr = try self.allocateAtom(&decl.link.macho, code_len, required_alignment, .{3865 const addr = try self.allocateAtom(&decl.link.macho, code_len, required_alignment, match);
3718 .seg = self.text_segment_cmd_index.?,
3719 .sect = self.text_section_index.?,
3720 });
37213866
3722 log.debug("allocated atom for {s} at 0x{x}", .{ decl_name, addr });3867 log.debug("allocated atom for {s} at 0x{x}", .{ decl_name, addr });
37233868
3724 errdefer self.freeAtom(&decl.link.macho, .{3869 errdefer self.freeAtom(&decl.link.macho, match);
3725 .seg = self.text_segment_cmd_index.?,
3726 .sect = self.text_section_index.?,
3727 });
37283870
3729 symbol.* = .{3871 symbol.* = .{
3730 .n_strx = name_str_index,3872 .n_strx = name_str_index,
3731 .n_type = macho.N_SECT,3873 .n_type = macho.N_SECT,
3732 .n_sect = @intCast(u8, self.text_section_index.?) + 1,3874 .n_sect = @intCast(u8, self.section_ordinals.getIndex(match).?) + 1,
3733 .n_desc = 0,3875 .n_desc = 0,
3734 .n_value = addr,3876 .n_value = addr,
3735 };3877 };
...@@ -3912,12 +4054,11 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {...@@ -3912,12 +4054,11 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {
3912 if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl);4054 if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl);
3913 }4055 }
3914 log.debug("freeDecl {*}", .{decl});4056 log.debug("freeDecl {*}", .{decl});
3915 _ = self.decls.swapRemove(decl);4057 const kv = self.decls.fetchSwapRemove(decl);
4058 if (kv.?.value) |match| {
4059 self.freeAtom(&decl.link.macho, match);
4060 }
3916 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.4061 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
3917 self.freeAtom(&decl.link.macho, .{
3918 .seg = self.text_segment_cmd_index.?,
3919 .sect = self.text_section_index.?,
3920 });
3921 if (decl.link.macho.local_sym_index != 0) {4062 if (decl.link.macho.local_sym_index != 0) {
3922 self.locals_free_list.append(self.base.allocator, decl.link.macho.local_sym_index) catch {};4063 self.locals_free_list.append(self.base.allocator, decl.link.macho.local_sym_index) catch {};
39234064
...@@ -3958,6 +4099,29 @@ pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {...@@ -3958,6 +4099,29 @@ pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {
3958 return self.locals.items[decl.link.macho.local_sym_index].n_value;4099 return self.locals.items[decl.link.macho.local_sym_index].n_value;
3959}4100}
39604101
4102pub fn getDeclVAddrWithReloc(self: *MachO, decl: *const Module.Decl, offset: u64) !u64 {
4103 assert(decl.link.macho.local_sym_index != 0);
4104 assert(self.active_decl != null);
4105
4106 const atom = &self.active_decl.?.link.macho;
4107 try atom.relocs.append(self.base.allocator, .{
4108 .offset = @intCast(u32, offset),
4109 .target = .{ .local = decl.link.macho.local_sym_index },
4110 .addend = 0,
4111 .subtractor = null,
4112 .pcrel = false,
4113 .length = 3,
4114 .@"type" = switch (self.base.options.target.cpu.arch) {
4115 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
4116 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
4117 else => unreachable,
4118 },
4119 });
4120 try atom.rebases.append(self.base.allocator, offset);
4121
4122 return 0;
4123}
4124
3961fn populateMissingMetadata(self: *MachO) !void {4125fn populateMissingMetadata(self: *MachO) !void {
3962 const cpu_arch = self.base.options.target.cpu.arch;4126 const cpu_arch = self.base.options.target.cpu.arch;
39634127
test/stage2/x86_64.zig+19-20
...@@ -1761,6 +1761,25 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1761,6 +1761,25 @@ pub fn addCases(ctx: *TestContext) !void {
1761 \\}1761 \\}
1762 , "");1762 , "");
1763 }1763 }
1764
1765 {
1766 var case = ctx.exe("access slice element by index - slice_elem_val", target);
1767 case.addCompareOutput(
1768 \\var array = [_]usize{ 0, 42, 123, 34 };
1769 \\var slice: []const usize = &array;
1770 \\
1771 \\pub fn main() void {
1772 \\ assert(slice[0] == 0);
1773 \\ assert(slice[1] == 42);
1774 \\ assert(slice[2] == 123);
1775 \\ assert(slice[3] == 34);
1776 \\}
1777 \\
1778 \\fn assert(ok: bool) void {
1779 \\ if (!ok) unreachable;
1780 \\}
1781 , "");
1782 }
1764 }1783 }
1765}1784}
17661785
...@@ -2014,26 +2033,6 @@ fn addLinuxTestCases(ctx: *TestContext) !void {...@@ -2014,26 +2033,6 @@ fn addLinuxTestCases(ctx: *TestContext) !void {
2014 \\}2033 \\}
2015 , "");2034 , "");
2016 }2035 }
2017
2018 {
2019 // TODO fixing this will enable zig test on macOS
2020 var case = ctx.exe("access slice element by index - slice_elem_val", linux_x64);
2021 case.addCompareOutput(
2022 \\var array = [_]usize{ 0, 42, 123, 34 };
2023 \\var slice: []const usize = &array;
2024 \\
2025 \\pub fn main() void {
2026 \\ assert(slice[0] == 0);
2027 \\ assert(slice[1] == 42);
2028 \\ assert(slice[2] == 123);
2029 \\ assert(slice[3] == 34);
2030 \\}
2031 \\
2032 \\fn assert(ok: bool) void {
2033 \\ if (!ok) unreachable;
2034 \\}
2035 , "");
2036 }
2037}2036}
20382037
2039fn addMacOsTestCases(ctx: *TestContext) !void {2038fn addMacOsTestCases(ctx: *TestContext) !void {