authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-02-25 15:26:24+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-02-25 21:59:19+01:00
log1b8ed7842cc09ff687aa7386bf3af8565055a8d1
treec439e150d94ca8d9d4263094cb9473a24df0580b
parentbf6540ce50f8613386be09aac7dc03604af12e1e

macho: redo selection of segment/section for decls and consts

* fix alignment issues for consts with natural ABI alignment not matching that of the `ldr` instruction in `aarch64` - solved by preceeding the `ldr` with an additional `add` instruction to form the full address before dereferencing the pointer. * redo selection of segment/section for decls and consts based on combined type and value

6 files changed, 108 insertions(+), 112 deletions(-)

src/arch/aarch64/Emit.zig+21-5
......@@ -208,8 +208,8 @@ fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize {
208208 }
209209
210210 switch (tag) {
211 .load_memory_direct => return 3 * 4,
211212 .load_memory_got,
212 .load_memory_direct,
213213 .load_memory_ptr_got,
214214 .load_memory_ptr_direct,
215215 => return 2 * 4,
......@@ -654,15 +654,31 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
654654 const data = emit.mir.extraData(Mir.LoadMemoryPie, payload).data;
655655 const reg = @intToEnum(Register, data.register);
656656
657 // PC-relative displacement to the entry in the GOT table.
657 // PC-relative displacement to the entry in memory.
658658 // adrp
659659 const offset = @intCast(u32, emit.code.items.len);
660660 try emit.writeInstruction(Instruction.adrp(reg, 0));
661661
662662 switch (tag) {
663 .load_memory_got,
664 .load_memory_direct,
665 => {
663 .load_memory_got => {
664 // ldr reg, reg, offset
665 try emit.writeInstruction(Instruction.ldr(
666 reg,
667 reg,
668 Instruction.LoadStoreOffset.imm(0),
669 ));
670 },
671 .load_memory_direct => {
672 // We cannot load the offset directly as it may not be aligned properly.
673 // For example, load for 64bit register will require the target address offset
674 // to be 8-byte aligned, while the value might have non-8-byte natural alignment,
675 // meaning the linker might have put it at a non-8-byte aligned address. To circumvent
676 // this, we use `adrp, add` to form the address value which we then dereference with
677 // `ldr`.
678 // Note that this can potentially be optimised out by the codegen/linker if the
679 // target address is appropriately aligned.
680 // add reg, reg, offset
681 try emit.writeInstruction(Instruction.add(reg, reg, 0, false));
666682 // ldr reg, reg, offset
667683 try emit.writeInstruction(Instruction.ldr(
668684 reg,
src/link/MachO.zig+82-87
......@@ -3797,10 +3797,11 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl: *Module.De
37973797 atom.code.clearRetainingCapacity();
37983798 try atom.code.appendSlice(self.base.allocator, code);
37993799
3800 const match = try self.getMatchingSectionAtom(atom, typed_value.ty, typed_value.val);
3800 const match = try self.getMatchingSectionAtom(atom, decl_name, typed_value.ty, typed_value.val);
38013801 const addr = try self.allocateAtom(atom, code.len, required_alignment, match);
38023802
38033803 log.debug("allocated atom for {s} at 0x{x}", .{ name, addr });
3804 log.debug(" (required alignment 0x{x})", .{required_alignment});
38043805
38053806 errdefer self.freeAtom(atom, match, true);
38063807
......@@ -3903,28 +3904,60 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
39033904 try self.updateDeclExports(module, decl, decl_exports);
39043905}
39053906
3906fn isElemTyPointer(ty: Type) bool {
3907/// Checks if the value, or any of its embedded values stores a pointer, and thus requires
3908/// a rebase opcode for the dynamic linker.
3909fn needsPointerRebase(ty: Type, val: Value) bool {
3910 if (ty.zigTypeTag() == .Fn) {
3911 return false;
3912 }
3913 if (val.pointerDecl()) |_| {
3914 return true;
3915 }
3916
39073917 switch (ty.zigTypeTag()) {
3908 .Fn => return false,
3918 .Fn => unreachable,
39093919 .Pointer => return true,
3910 .Array => {
3911 const elem_ty = ty.elemType();
3912 return isElemTyPointer(elem_ty);
3920 .Array, .Vector => {
3921 if (ty.arrayLen() == 0) return false;
3922 const elem_ty = ty.childType();
3923 var elem_value_buf: Value.ElemValueBuffer = undefined;
3924 const elem_val = val.elemValueBuffer(0, &elem_value_buf);
3925 return needsPointerRebase(elem_ty, elem_val);
39133926 },
3914 .Struct, .Union => {
3915 const len = ty.structFieldCount();
3916 var i: usize = 0;
3917 while (i < len) : (i += 1) {
3918 const field_ty = ty.structFieldType(i);
3919 if (isElemTyPointer(field_ty)) return true;
3920 }
3921 return false;
3927 .Struct => {
3928 const fields = ty.structFields().values();
3929 if (fields.len == 0) return false;
3930 if (val.castTag(.@"struct")) |payload| {
3931 const field_values = payload.data;
3932 for (field_values) |field_val, i| {
3933 if (needsPointerRebase(fields[i].ty, field_val)) return true;
3934 } else return false;
3935 } else return false;
3936 },
3937 .Optional => {
3938 if (val.castTag(.opt_payload)) |payload| {
3939 const sub_val = payload.data;
3940 var buffer: Type.Payload.ElemType = undefined;
3941 const sub_ty = ty.optionalChild(&buffer);
3942 return needsPointerRebase(sub_ty, sub_val);
3943 } else return false;
3944 },
3945 .Union => {
3946 const union_obj = val.cast(Value.Payload.Union).?.data;
3947 const active_field_ty = ty.unionFieldType(union_obj.tag);
3948 return needsPointerRebase(active_field_ty, union_obj.val);
3949 },
3950 .ErrorUnion => {
3951 if (val.castTag(.eu_payload)) |payload| {
3952 const payload_ty = ty.errorUnionPayload();
3953 return needsPointerRebase(payload_ty, payload.data);
3954 } else return false;
39223955 },
39233956 else => return false,
39243957 }
39253958}
39263959
3927fn getMatchingSectionAtom(self: *MachO, atom: *Atom, ty: Type, val: Value) !MatchingSection {
3960fn getMatchingSectionAtom(self: *MachO, atom: *Atom, name: []const u8, ty: Type, val: Value) !MatchingSection {
39283961 const code = atom.code.items;
39293962 const alignment = ty.abiAlignment(self.base.options.target);
39303963 const align_log_2 = math.log2(alignment);
......@@ -3938,10 +3971,25 @@ fn getMatchingSectionAtom(self: *MachO, atom: *Atom, ty: Type, val: Value) !Matc
39383971 .seg = self.data_segment_cmd_index.?,
39393972 .sect = self.bss_section_index.?,
39403973 };
3974 } else {
3975 break :blk MatchingSection{
3976 .seg = self.data_segment_cmd_index.?,
3977 .sect = self.data_section_index.?,
3978 };
39413979 }
3980 }
3981
3982 if (val.castTag(.variable)) |_| {
3983 break :blk MatchingSection{
3984 .seg = self.data_segment_cmd_index.?,
3985 .sect = self.data_section_index.?,
3986 };
3987 }
3988
3989 if (needsPointerRebase(ty, val)) {
39423990 break :blk (try self.getMatchingSection(.{
3943 .segname = makeStaticString("__DATA"),
3944 .sectname = makeStaticString("__data"),
3991 .segname = makeStaticString("__DATA_CONST"),
3992 .sectname = makeStaticString("__const"),
39453993 .size = code.len,
39463994 .@"align" = align_log_2,
39473995 })).?;
......@@ -3954,8 +4002,8 @@ fn getMatchingSectionAtom(self: *MachO, atom: *Atom, ty: Type, val: Value) !Matc
39544002 .sect = self.text_section_index.?,
39554003 };
39564004 },
3957 .Array => switch (val.tag()) {
3958 .bytes => {
4005 .Array => {
4006 if (val.tag() == .bytes) {
39594007 switch (ty.tag()) {
39604008 .array_u8_sentinel_0,
39614009 .const_slice_u8_sentinel_0,
......@@ -3969,79 +4017,23 @@ fn getMatchingSectionAtom(self: *MachO, atom: *Atom, ty: Type, val: Value) !Matc
39694017 .@"align" = align_log_2,
39704018 })).?;
39714019 },
3972 else => {
3973 break :blk (try self.getMatchingSection(.{
3974 .segname = makeStaticString("__TEXT"),
3975 .sectname = makeStaticString("__const"),
3976 .size = code.len,
3977 .@"align" = align_log_2,
3978 })).?;
3979 },
3980 }
3981 },
3982 .array => {
3983 if (isElemTyPointer(ty)) {
3984 break :blk (try self.getMatchingSection(.{
3985 .segname = makeStaticString("__DATA_CONST"),
3986 .sectname = makeStaticString("__const"),
3987 .size = code.len,
3988 .@"align" = align_log_2,
3989 })).?;
3990 } else {
3991 break :blk (try self.getMatchingSection(.{
3992 .segname = makeStaticString("__TEXT"),
3993 .sectname = makeStaticString("__const"),
3994 .size = code.len,
3995 .@"align" = align_log_2,
3996 })).?;
4020 else => {},
39974021 }
3998 },
3999 else => {
4000 break :blk (try self.getMatchingSection(.{
4001 .segname = makeStaticString("__TEXT"),
4002 .sectname = makeStaticString("__const"),
4003 .size = code.len,
4004 .@"align" = align_log_2,
4005 })).?;
4006 },
4007 },
4008 .Pointer => {
4009 if (val.castTag(.variable)) |_| {
4010 break :blk MatchingSection{
4011 .seg = self.data_segment_cmd_index.?,
4012 .sect = self.data_section_index.?,
4013 };
4014 } else {
4015 break :blk (try self.getMatchingSection(.{
4016 .segname = makeStaticString("__DATA_CONST"),
4017 .sectname = makeStaticString("__const"),
4018 .size = code.len,
4019 .@"align" = align_log_2,
4020 })).?;
4021 }
4022 },
4023 else => {
4024 if (val.castTag(.variable)) |_| {
4025 break :blk MatchingSection{
4026 .seg = self.data_segment_cmd_index.?,
4027 .sect = self.data_section_index.?,
4028 };
4029 } else {
4030 break :blk (try self.getMatchingSection(.{
4031 .segname = makeStaticString("__TEXT"),
4032 .sectname = makeStaticString("__const"),
4033 .size = code.len,
4034 .@"align" = align_log_2,
4035 })).?;
40364022 }
40374023 },
4024 else => {},
40384025 }
4026 break :blk (try self.getMatchingSection(.{
4027 .segname = makeStaticString("__TEXT"),
4028 .sectname = makeStaticString("__const"),
4029 .size = code.len,
4030 .@"align" = align_log_2,
4031 })).?;
40394032 };
4040 const local = self.locals.items[atom.local_sym_index];
40414033 const seg = self.load_commands.items[match.seg].segment;
40424034 const sect = seg.sections.items[match.sect];
40434035 log.debug(" allocating atom '{s}' in '{s},{s}' ({d},{d})", .{
4044 self.getString(local.n_strx),
4036 name,
40454037 sect.segName(),
40464038 sect.sectName(),
40474039 match.seg,
......@@ -4055,13 +4047,14 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64
40554047 assert(decl.link.macho.local_sym_index != 0); // Caller forgot to call allocateDeclIndexes()
40564048 const symbol = &self.locals.items[decl.link.macho.local_sym_index];
40574049
4050 const sym_name = try decl.getFullyQualifiedName(self.base.allocator);
4051 defer self.base.allocator.free(sym_name);
4052
40584053 const decl_ptr = self.decls.getPtr(decl).?;
40594054 if (decl_ptr.* == null) {
4060 decl_ptr.* = try self.getMatchingSectionAtom(&decl.link.macho, decl.ty, decl.val);
4055 decl_ptr.* = try self.getMatchingSectionAtom(&decl.link.macho, sym_name, decl.ty, decl.val);
40614056 }
40624057 const match = decl_ptr.*.?;
4063 const sym_name = try decl.getFullyQualifiedName(self.base.allocator);
4064 defer self.base.allocator.free(sym_name);
40654058
40664059 if (decl.link.macho.size != 0) {
40674060 const capacity = decl.link.macho.capacity(self.*);
......@@ -4071,6 +4064,7 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64
40714064 const vaddr = try self.growAtom(&decl.link.macho, code_len, required_alignment, match);
40724065
40734066 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ sym_name, symbol.n_value, vaddr });
4067 log.debug(" (required alignment 0x{x})", .{required_alignment});
40744068
40754069 if (vaddr != symbol.n_value) {
40764070 log.debug(" (writing new GOT entry)", .{});
......@@ -4105,6 +4099,7 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64
41054099 const addr = try self.allocateAtom(&decl.link.macho, code_len, required_alignment, match);
41064100
41074101 log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, addr });
4102 log.debug(" (required alignment 0x{x})", .{required_alignment});
41084103
41094104 errdefer self.freeAtom(&decl.link.macho, match, false);
41104105
src/link/MachO/Atom.zig+5-5
......@@ -691,11 +691,11 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
691691
692692 if (is_via_got) {
693693 const got_index = macho_file.got_entries_table.get(rel.target) orelse {
694 const n_strx = switch (rel.target) {
695 .local => |sym_index| macho_file.locals.items[sym_index].n_strx,
696 .global => |n_strx| n_strx,
697 };
698 log.err("expected GOT entry for symbol '{s}'", .{macho_file.getString(n_strx)});
694 log.err("expected GOT entry for symbol", .{});
695 switch (rel.target) {
696 .local => |sym_index| log.err(" local @{d}", .{sym_index}),
697 .global => |n_strx| log.err(" global @'{s}'", .{macho_file.getString(n_strx)}),
698 }
699699 log.err(" this is an internal linker error", .{});
700700 return error.FailedToResolveRelocationTarget;
701701 };
test/behavior/align.zig-1
......@@ -7,7 +7,6 @@ var foo: u8 align(4) = 100;
77
88test "global variable alignment" {
99 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
10 if (builtin.zig_backend == .stage2_x86_64 and builtin.os.tag == .macos) return error.SkipZigTest;
1110
1211 comptime try expect(@typeInfo(@TypeOf(&foo)).Pointer.alignment == 4);
1312 comptime try expect(@TypeOf(&foo) == *align(4) u8);
test/behavior/basic.zig-3
......@@ -195,9 +195,6 @@ test "multiline string comments at multiple places" {
195195}
196196
197197test "string concatenation" {
198 if (builtin.zig_backend == .stage2_aarch64 and builtin.os.tag == .macos) return error.SkipZigTest;
199 if (builtin.zig_backend == .stage2_x86_64 and builtin.os.tag == .macos) return error.SkipZigTest;
200
201198 try expect(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED"));
202199}
203200
test/behavior/union.zig-11
......@@ -44,7 +44,6 @@ fn setInt(foo: *Foo, x: i32) void {
4444
4545test "comptime union field access" {
4646 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
47 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
4847
4948 comptime {
5049 var foo = Foo{ .int = 0 };
......@@ -77,14 +76,12 @@ const ExternPtrOrInt = extern union {
7776};
7877test "extern union size" {
7978 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
80 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
8179
8280 comptime try expect(@sizeOf(ExternPtrOrInt) == 8);
8381}
8482
8583test "0-sized extern union definition" {
8684 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
87 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
8885
8986 const U = extern union {
9087 a: void,
......@@ -115,9 +112,7 @@ const err = @as(anyerror!Agg, Agg{
115112const array = [_]Value{ v1, v2, v1, v2 };
116113
117114test "unions embedded in aggregate types" {
118 if (builtin.zig_backend == .stage2_x86_64 and builtin.os.tag == .macos) return error.SkipZigTest;
119115 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
120 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
121116
122117 switch (array[1]) {
123118 Value.Array => |arr| try expect(arr[4] == 3),
......@@ -131,7 +126,6 @@ test "unions embedded in aggregate types" {
131126
132127test "access a member of tagged union with conflicting enum tag name" {
133128 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
134 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
135129
136130 const Bar = union(enum) {
137131 A: A,
......@@ -176,7 +170,6 @@ const TaggedUnionWithPayload = union(enum) {
176170
177171test "union alignment" {
178172 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
179 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
180173
181174 comptime {
182175 try expect(@alignOf(AlignTestTaggedUnion) >= @alignOf([9]u8));
......@@ -276,7 +269,6 @@ fn testCastUnionToTag() !void {
276269
277270test "union field access gives the enum values" {
278271 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
279 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
280272
281273 try expect(TheUnion.A == TheTag.A);
282274 try expect(TheUnion.B == TheTag.B);
......@@ -352,7 +344,6 @@ const PackedPtrOrInt = packed union {
352344};
353345test "packed union size" {
354346 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
355 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
356347
357348 comptime try expect(@sizeOf(PackedPtrOrInt) == 8);
358349}
......@@ -362,7 +353,6 @@ const ZeroBits = union {
362353};
363354test "union with only 1 field which is void should be zero bits" {
364355 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
365 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
366356
367357 comptime try expect(@sizeOf(ZeroBits) == 0);
368358}
......@@ -422,7 +412,6 @@ test "union with only 1 field casted to its enum type" {
422412
423413test "union with one member defaults to u0 tag type" {
424414 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
425 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
426415
427416 const U0 = union(enum) {
428417 X: u32,