authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-18 15:52:12-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-18 15:52:12-07:00
logf5aca4a6a1ba867d3bc343a3740454468a7eff13
tree48f43c64fa9ce61c294e3062758ec35bb0c9f544
parent66245ac834969b84548ec325ee20a6910456e5ec
parent96ae451bbe78cd35a62e00e3fb48a32f24ebd315

Merge remote-tracking branch 'origin/master' into zir-memory-layout

I need the enum arrays that were just merged into master.

44 files changed, 7419 insertions(+), 1697 deletions(-)

CMakeLists.txt+7
...@@ -564,7 +564,14 @@ set(ZIG_STAGE2_SOURCES...@@ -564,7 +564,14 @@ set(ZIG_STAGE2_SOURCES
564 "${CMAKE_SOURCE_DIR}/src/link/Coff.zig"564 "${CMAKE_SOURCE_DIR}/src/link/Coff.zig"
565 "${CMAKE_SOURCE_DIR}/src/link/Elf.zig"565 "${CMAKE_SOURCE_DIR}/src/link/Elf.zig"
566 "${CMAKE_SOURCE_DIR}/src/link/MachO.zig"566 "${CMAKE_SOURCE_DIR}/src/link/MachO.zig"
567 "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig"
568 "${CMAKE_SOURCE_DIR}/src/link/MachO/CodeSignature.zig"
569 "${CMAKE_SOURCE_DIR}/src/link/MachO/DebugSymbols.zig"
570 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"
567 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"571 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
572 "${CMAKE_SOURCE_DIR}/src/link/MachO/Zld.zig"
573 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"
574 "${CMAKE_SOURCE_DIR}/src/link/MachO/commands.zig"
568 "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"575 "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"
569 "${CMAKE_SOURCE_DIR}/src/link/C/zig.h"576 "${CMAKE_SOURCE_DIR}/src/link/C/zig.h"
570 "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin"577 "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin"
lib/std/bit_set.zig+21-7
...@@ -176,7 +176,7 @@ pub fn IntegerBitSet(comptime size: u16) type {...@@ -176,7 +176,7 @@ pub fn IntegerBitSet(comptime size: u16) type {
176 /// The default options (.{}) will iterate indices of set bits in176 /// The default options (.{}) will iterate indices of set bits in
177 /// ascending order. Modifications to the underlying bit set may177 /// ascending order. Modifications to the underlying bit set may
178 /// or may not be observed by the iterator.178 /// or may not be observed by the iterator.
179 pub fn iterator(self: *const Self, comptime options: IteratorOptions) Iterator(options.direction) {179 pub fn iterator(self: *const Self, comptime options: IteratorOptions) Iterator(options) {
180 return .{180 return .{
181 .bits_remain = switch (options.kind) {181 .bits_remain = switch (options.kind) {
182 .set => self.mask,182 .set => self.mask,
...@@ -185,7 +185,11 @@ pub fn IntegerBitSet(comptime size: u16) type {...@@ -185,7 +185,11 @@ pub fn IntegerBitSet(comptime size: u16) type {
185 };185 };
186 }186 }
187187
188 fn Iterator(comptime direction: IteratorOptions.Direction) type {188 pub fn Iterator(comptime options: IteratorOptions) type {
189 return SingleWordIterator(options.direction);
190 }
191
192 fn SingleWordIterator(comptime direction: IteratorOptions.Direction) type {
189 return struct {193 return struct {
190 const IterSelf = @This();194 const IterSelf = @This();
191 // all bits which have not yet been iterated over195 // all bits which have not yet been iterated over
...@@ -425,8 +429,12 @@ pub fn ArrayBitSet(comptime MaskIntType: type, comptime size: usize) type {...@@ -425,8 +429,12 @@ pub fn ArrayBitSet(comptime MaskIntType: type, comptime size: usize) type {
425 /// The default options (.{}) will iterate indices of set bits in429 /// The default options (.{}) will iterate indices of set bits in
426 /// ascending order. Modifications to the underlying bit set may430 /// ascending order. Modifications to the underlying bit set may
427 /// or may not be observed by the iterator.431 /// or may not be observed by the iterator.
428 pub fn iterator(self: *const Self, comptime options: IteratorOptions) BitSetIterator(MaskInt, options) {432 pub fn iterator(self: *const Self, comptime options: IteratorOptions) Iterator(options) {
429 return BitSetIterator(MaskInt, options).init(&self.masks, last_item_mask);433 return Iterator(options).init(&self.masks, last_item_mask);
434 }
435
436 pub fn Iterator(comptime options: IteratorOptions) type {
437 return BitSetIterator(MaskInt, options);
430 }438 }
431439
432 fn maskBit(index: usize) MaskInt {440 fn maskBit(index: usize) MaskInt {
...@@ -700,11 +708,15 @@ pub const DynamicBitSetUnmanaged = struct {...@@ -700,11 +708,15 @@ pub const DynamicBitSetUnmanaged = struct {
700 /// ascending order. Modifications to the underlying bit set may708 /// ascending order. Modifications to the underlying bit set may
701 /// or may not be observed by the iterator. Resizing the underlying709 /// or may not be observed by the iterator. Resizing the underlying
702 /// bit set invalidates the iterator.710 /// bit set invalidates the iterator.
703 pub fn iterator(self: *const Self, comptime options: IteratorOptions) BitSetIterator(MaskInt, options) {711 pub fn iterator(self: *const Self, comptime options: IteratorOptions) Iterator(options) {
704 const num_masks = numMasks(self.bit_length);712 const num_masks = numMasks(self.bit_length);
705 const padding_bits = num_masks * @bitSizeOf(MaskInt) - self.bit_length;713 const padding_bits = num_masks * @bitSizeOf(MaskInt) - self.bit_length;
706 const last_item_mask = (~@as(MaskInt, 0)) >> @intCast(ShiftInt, padding_bits);714 const last_item_mask = (~@as(MaskInt, 0)) >> @intCast(ShiftInt, padding_bits);
707 return BitSetIterator(MaskInt, options).init(self.masks[0..num_masks], last_item_mask);715 return Iterator(options).init(self.masks[0..num_masks], last_item_mask);
716 }
717
718 pub fn Iterator(comptime options: IteratorOptions) type {
719 return BitSetIterator(MaskInt, options);
708 }720 }
709721
710 fn maskBit(index: usize) MaskInt {722 fn maskBit(index: usize) MaskInt {
...@@ -858,9 +870,11 @@ pub const DynamicBitSet = struct {...@@ -858,9 +870,11 @@ pub const DynamicBitSet = struct {
858 /// ascending order. Modifications to the underlying bit set may870 /// ascending order. Modifications to the underlying bit set may
859 /// or may not be observed by the iterator. Resizing the underlying871 /// or may not be observed by the iterator. Resizing the underlying
860 /// bit set invalidates the iterator.872 /// bit set invalidates the iterator.
861 pub fn iterator(self: *Self, comptime options: IteratorOptions) BitSetIterator(MaskInt, options) {873 pub fn iterator(self: *Self, comptime options: IteratorOptions) Iterator(options) {
862 return self.unmanaged.iterator(options);874 return self.unmanaged.iterator(options);
863 }875 }
876
877 pub const Iterator = DynamicBitSetUnmanaged.Iterator;
864};878};
865879
866/// Options for configuring an iterator over a bit set880/// Options for configuring an iterator over a bit set
lib/std/c/builtins.zig+7-1
...@@ -140,7 +140,7 @@ pub fn __builtin_object_size(ptr: ?*const c_void, ty: c_int) callconv(.Inline) u...@@ -140,7 +140,7 @@ pub fn __builtin_object_size(ptr: ?*const c_void, ty: c_int) callconv(.Inline) u
140 // If it is not possible to determine which objects ptr points to at compile time,140 // If it is not possible to determine which objects ptr points to at compile time,
141 // __builtin_object_size should return (size_t) -1 for type 0 or 1 and (size_t) 0141 // __builtin_object_size should return (size_t) -1 for type 0 or 1 and (size_t) 0
142 // for type 2 or 3.142 // for type 2 or 3.
143 if (ty == 0 or ty == 1) return @bitCast(usize, -@as(c_long, 1));143 if (ty == 0 or ty == 1) return @bitCast(usize, -@as(isize, 1));
144 if (ty == 2 or ty == 3) return 0;144 if (ty == 2 or ty == 3) return 0;
145 unreachable;145 unreachable;
146}146}
...@@ -188,3 +188,9 @@ pub fn __builtin_memcpy(...@@ -188,3 +188,9 @@ pub fn __builtin_memcpy(
188pub fn __builtin_expect(expr: c_long, c: c_long) callconv(.Inline) c_long {188pub fn __builtin_expect(expr: c_long, c: c_long) callconv(.Inline) c_long {
189 return expr;189 return expr;
190}190}
191
192// __builtin_alloca_with_align is not currently implemented.
193// It is used in a run-translated-c test and a test-translate-c test to ensure that non-implemented
194// builtins are correctly demoted. If you implement __builtin_alloca_with_align, please update the
195// run-translated-c test and the test-translate-c test to use a different non-implemented builtin.
196// pub fn __builtin_alloca_with_align(size: usize, alignment: usize) callconv(.Inline) *c_void {}
lib/std/crypto.zig+12-2
...@@ -24,8 +24,12 @@ pub const aead = struct {...@@ -24,8 +24,12 @@ pub const aead = struct {
24 pub const Gimli = @import("crypto/gimli.zig").Aead;24 pub const Gimli = @import("crypto/gimli.zig").Aead;
2525
26 pub const chacha_poly = struct {26 pub const chacha_poly = struct {
27 pub const ChaCha20Poly1305 = @import("crypto/chacha20.zig").Chacha20Poly1305;27 pub const ChaCha20Poly1305 = @import("crypto/chacha20.zig").ChaCha20Poly1305;
28 pub const XChaCha20Poly1305 = @import("crypto/chacha20.zig").XChacha20Poly1305;28 pub const ChaCha12Poly1305 = @import("crypto/chacha20.zig").ChaCha12Poly1305;
29 pub const ChaCha8Poly1305 = @import("crypto/chacha20.zig").ChaCha8Poly1305;
30 pub const XChaCha20Poly1305 = @import("crypto/chacha20.zig").XChaCha20Poly1305;
31 pub const XChaCha12Poly1305 = @import("crypto/chacha20.zig").XChaCha12Poly1305;
32 pub const XChaCha8Poly1305 = @import("crypto/chacha20.zig").XChaCha8Poly1305;
29 };33 };
3034
31 pub const isap = @import("crypto/isap.zig");35 pub const isap = @import("crypto/isap.zig");
...@@ -119,8 +123,14 @@ pub const sign = struct {...@@ -119,8 +123,14 @@ pub const sign = struct {
119pub const stream = struct {123pub const stream = struct {
120 pub const chacha = struct {124 pub const chacha = struct {
121 pub const ChaCha20IETF = @import("crypto/chacha20.zig").ChaCha20IETF;125 pub const ChaCha20IETF = @import("crypto/chacha20.zig").ChaCha20IETF;
126 pub const ChaCha12IETF = @import("crypto/chacha20.zig").ChaCha12IETF;
127 pub const ChaCha8IETF = @import("crypto/chacha20.zig").ChaCha8IETF;
122 pub const ChaCha20With64BitNonce = @import("crypto/chacha20.zig").ChaCha20With64BitNonce;128 pub const ChaCha20With64BitNonce = @import("crypto/chacha20.zig").ChaCha20With64BitNonce;
129 pub const ChaCha12With64BitNonce = @import("crypto/chacha20.zig").ChaCha12With64BitNonce;
130 pub const ChaCha8With64BitNonce = @import("crypto/chacha20.zig").ChaCha8With64BitNonce;
123 pub const XChaCha20IETF = @import("crypto/chacha20.zig").XChaCha20IETF;131 pub const XChaCha20IETF = @import("crypto/chacha20.zig").XChaCha20IETF;
132 pub const XChaCha12IETF = @import("crypto/chacha20.zig").XChaCha12IETF;
133 pub const XChaCha8IETF = @import("crypto/chacha20.zig").XChaCha8IETF;
124 };134 };
125135
126 pub const salsa = struct {136 pub const salsa = struct {
lib/std/crypto/benchmark.zig+1
...@@ -202,6 +202,7 @@ pub fn benchmarkBatchSignatureVerification(comptime Signature: anytype, comptime...@@ -202,6 +202,7 @@ pub fn benchmarkBatchSignatureVerification(comptime Signature: anytype, comptime
202const aeads = [_]Crypto{202const aeads = [_]Crypto{
203 Crypto{ .ty = crypto.aead.chacha_poly.ChaCha20Poly1305, .name = "chacha20Poly1305" },203 Crypto{ .ty = crypto.aead.chacha_poly.ChaCha20Poly1305, .name = "chacha20Poly1305" },
204 Crypto{ .ty = crypto.aead.chacha_poly.XChaCha20Poly1305, .name = "xchacha20Poly1305" },204 Crypto{ .ty = crypto.aead.chacha_poly.XChaCha20Poly1305, .name = "xchacha20Poly1305" },
205 Crypto{ .ty = crypto.aead.chacha_poly.XChaCha8Poly1305, .name = "xchacha8Poly1305" },
205 Crypto{ .ty = crypto.aead.salsa_poly.XSalsa20Poly1305, .name = "xsalsa20Poly1305" },206 Crypto{ .ty = crypto.aead.salsa_poly.XSalsa20Poly1305, .name = "xsalsa20Poly1305" },
206 Crypto{ .ty = crypto.aead.Gimli, .name = "gimli-aead" },207 Crypto{ .ty = crypto.aead.Gimli, .name = "gimli-aead" },
207 Crypto{ .ty = crypto.aead.aegis.Aegis128L, .name = "aegis-128l" },208 Crypto{ .ty = crypto.aead.aegis.Aegis128L, .name = "aegis-128l" },
lib/std/crypto/chacha20.zig+598-571
...@@ -15,286 +15,357 @@ const Vector = std.meta.Vector;...@@ -15,286 +15,357 @@ const Vector = std.meta.Vector;
15const Poly1305 = std.crypto.onetimeauth.Poly1305;15const Poly1305 = std.crypto.onetimeauth.Poly1305;
16const Error = std.crypto.Error;16const Error = std.crypto.Error;
1717
18/// IETF-variant of the ChaCha20 stream cipher, as designed for TLS.
19pub const ChaCha20IETF = ChaChaIETF(20);
20
21/// IETF-variant of the ChaCha20 stream cipher, reduced to 12 rounds.
22/// Reduced-rounds versions are faster than the full-round version, but have a lower security margin.
23/// However, ChaCha is still believed to have a comfortable security even with only with 8 rounds.
24pub const ChaCha12IETF = ChaChaIETF(12);
25
26/// IETF-variant of the ChaCha20 stream cipher, reduced to 8 rounds.
27/// Reduced-rounds versions are faster than the full-round version, but have a lower security margin.
28/// However, ChaCha is still believed to have a comfortable security even with only with 8 rounds.
29pub const ChaCha8IETF = ChaChaIETF(8);
30
31/// Original ChaCha20 stream cipher.
32pub const ChaCha20With64BitNonce = ChaChaWith64BitNonce(20);
33
34/// Original ChaCha20 stream cipher, reduced to 12 rounds.
35/// Reduced-rounds versions are faster than the full-round version, but have a lower security margin.
36/// However, ChaCha is still believed to have a comfortable security even with only with 8 rounds.
37pub const ChaCha12With64BitNonce = ChaChaWith64BitNonce(12);
38
39/// Original ChaCha20 stream cipher, reduced to 8 rounds.
40/// Reduced-rounds versions are faster than the full-round version, but have a lower security margin.
41/// However, ChaCha is still believed to have a comfortable security even with only with 8 rounds.
42pub const ChaCha8With64BitNonce = ChaChaWith64BitNonce(8);
43
44/// XChaCha20 (nonce-extended version of the IETF ChaCha20 variant) stream cipher
45pub const XChaCha20IETF = XChaChaIETF(20);
46
47/// XChaCha20 (nonce-extended version of the IETF ChaCha20 variant) stream cipher, reduced to 12 rounds
48/// Reduced-rounds versions are faster than the full-round version, but have a lower security margin.
49/// However, ChaCha is still believed to have a comfortable security even with only with 8 rounds.
50pub const XChaCha12IETF = XChaChaIETF(12);
51
52/// XChaCha20 (nonce-extended version of the IETF ChaCha20 variant) stream cipher, reduced to 8 rounds
53/// Reduced-rounds versions are faster than the full-round version, but have a lower security margin.
54/// However, ChaCha is still believed to have a comfortable security even with only with 8 rounds.
55pub const XChaCha8IETF = XChaChaIETF(8);
56
57/// ChaCha20-Poly1305 authenticated cipher, as designed for TLS
58pub const ChaCha20Poly1305 = ChaChaPoly1305(20);
59
60/// ChaCha20-Poly1305 authenticated cipher, reduced to 12 rounds
61/// Reduced-rounds versions are faster than the full-round version, but have a lower security margin.
62/// However, ChaCha is still believed to have a comfortable security even with only with 8 rounds.
63pub const ChaCha12Poly1305 = ChaChaPoly1305(12);
64
65/// ChaCha20-Poly1305 authenticated cipher, reduced to 8 rounds
66/// Reduced-rounds versions are faster than the full-round version, but have a lower security margin.
67/// However, ChaCha is still believed to have a comfortable security even with only with 8 rounds.
68pub const ChaCha8Poly1305 = ChaChaPoly1305(8);
69
70/// XChaCha20-Poly1305 authenticated cipher
71pub const XChaCha20Poly1305 = XChaChaPoly1305(20);
72
73/// XChaCha20-Poly1305 authenticated cipher
74/// Reduced-rounds versions are faster than the full-round version, but have a lower security margin.
75/// However, ChaCha is still believed to have a comfortable security even with only with 8 rounds.
76pub const XChaCha12Poly1305 = XChaChaPoly1305(12);
77
78/// XChaCha20-Poly1305 authenticated cipher
79/// Reduced-rounds versions are faster than the full-round version, but have a lower security margin.
80/// However, ChaCha is still believed to have a comfortable security even with only with 8 rounds.
81pub const XChaCha8Poly1305 = XChaChaPoly1305(8);
82
18// Vectorized implementation of the core function83// Vectorized implementation of the core function
19const ChaCha20VecImpl = struct {84fn ChaChaVecImpl(comptime rounds_nb: usize) type {
20 const Lane = Vector(4, u32);85 return struct {
21 const BlockVec = [4]Lane;86 const Lane = Vector(4, u32);
2287 const BlockVec = [4]Lane;
23 fn initContext(key: [8]u32, d: [4]u32) BlockVec {88
24 const c = "expand 32-byte k";89 fn initContext(key: [8]u32, d: [4]u32) BlockVec {
25 const constant_le = comptime Lane{90 const c = "expand 32-byte k";
26 mem.readIntLittle(u32, c[0..4]),91 const constant_le = comptime Lane{
27 mem.readIntLittle(u32, c[4..8]),92 mem.readIntLittle(u32, c[0..4]),
28 mem.readIntLittle(u32, c[8..12]),93 mem.readIntLittle(u32, c[4..8]),
29 mem.readIntLittle(u32, c[12..16]),94 mem.readIntLittle(u32, c[8..12]),
30 };95 mem.readIntLittle(u32, c[12..16]),
31 return BlockVec{96 };
32 constant_le,97 return BlockVec{
33 Lane{ key[0], key[1], key[2], key[3] },98 constant_le,
34 Lane{ key[4], key[5], key[6], key[7] },99 Lane{ key[0], key[1], key[2], key[3] },
35 Lane{ d[0], d[1], d[2], d[3] },100 Lane{ key[4], key[5], key[6], key[7] },
36 };101 Lane{ d[0], d[1], d[2], d[3] },
37 }102 };
103 }
38104
39 fn chacha20Core(x: *BlockVec, input: BlockVec) callconv(.Inline) void {105 fn chacha20Core(x: *BlockVec, input: BlockVec) callconv(.Inline) void {
40 x.* = input;106 x.* = input;
41107
42 var r: usize = 0;108 var r: usize = 0;
43 while (r < 20) : (r += 2) {109 while (r < rounds_nb) : (r += 2) {
44 x[0] +%= x[1];110 x[0] +%= x[1];
45 x[3] ^= x[0];111 x[3] ^= x[0];
46 x[3] = math.rotl(Lane, x[3], 16);112 x[3] = math.rotl(Lane, x[3], 16);
47113
48 x[2] +%= x[3];114 x[2] +%= x[3];
49 x[1] ^= x[2];115 x[1] ^= x[2];
50 x[1] = math.rotl(Lane, x[1], 12);116 x[1] = math.rotl(Lane, x[1], 12);
51117
52 x[0] +%= x[1];118 x[0] +%= x[1];
53 x[3] ^= x[0];119 x[3] ^= x[0];
54 x[0] = @shuffle(u32, x[0], undefined, [_]i32{ 3, 0, 1, 2 });120 x[0] = @shuffle(u32, x[0], undefined, [_]i32{ 3, 0, 1, 2 });
55 x[3] = math.rotl(Lane, x[3], 8);121 x[3] = math.rotl(Lane, x[3], 8);
56122
57 x[2] +%= x[3];123 x[2] +%= x[3];
58 x[3] = @shuffle(u32, x[3], undefined, [_]i32{ 2, 3, 0, 1 });124 x[3] = @shuffle(u32, x[3], undefined, [_]i32{ 2, 3, 0, 1 });
59 x[1] ^= x[2];125 x[1] ^= x[2];
60 x[2] = @shuffle(u32, x[2], undefined, [_]i32{ 1, 2, 3, 0 });126 x[2] = @shuffle(u32, x[2], undefined, [_]i32{ 1, 2, 3, 0 });
61 x[1] = math.rotl(Lane, x[1], 7);127 x[1] = math.rotl(Lane, x[1], 7);
62128
63 x[0] +%= x[1];129 x[0] +%= x[1];
64 x[3] ^= x[0];130 x[3] ^= x[0];
65 x[3] = math.rotl(Lane, x[3], 16);131 x[3] = math.rotl(Lane, x[3], 16);
66132
67 x[2] +%= x[3];133 x[2] +%= x[3];
68 x[1] ^= x[2];134 x[1] ^= x[2];
69 x[1] = math.rotl(Lane, x[1], 12);135 x[1] = math.rotl(Lane, x[1], 12);
70136
71 x[0] +%= x[1];137 x[0] +%= x[1];
72 x[3] ^= x[0];138 x[3] ^= x[0];
73 x[0] = @shuffle(u32, x[0], undefined, [_]i32{ 1, 2, 3, 0 });139 x[0] = @shuffle(u32, x[0], undefined, [_]i32{ 1, 2, 3, 0 });
74 x[3] = math.rotl(Lane, x[3], 8);140 x[3] = math.rotl(Lane, x[3], 8);
75141
76 x[2] +%= x[3];142 x[2] +%= x[3];
77 x[3] = @shuffle(u32, x[3], undefined, [_]i32{ 2, 3, 0, 1 });143 x[3] = @shuffle(u32, x[3], undefined, [_]i32{ 2, 3, 0, 1 });
78 x[1] ^= x[2];144 x[1] ^= x[2];
79 x[2] = @shuffle(u32, x[2], undefined, [_]i32{ 3, 0, 1, 2 });145 x[2] = @shuffle(u32, x[2], undefined, [_]i32{ 3, 0, 1, 2 });
80 x[1] = math.rotl(Lane, x[1], 7);146 x[1] = math.rotl(Lane, x[1], 7);
147 }
81 }148 }
82 }
83149
84 fn hashToBytes(out: *[64]u8, x: BlockVec) callconv(.Inline) void {150 fn hashToBytes(out: *[64]u8, x: BlockVec) callconv(.Inline) void {
85 var i: usize = 0;151 var i: usize = 0;
86 while (i < 4) : (i += 1) {152 while (i < 4) : (i += 1) {
87 mem.writeIntLittle(u32, out[16 * i + 0 ..][0..4], x[i][0]);153 mem.writeIntLittle(u32, out[16 * i + 0 ..][0..4], x[i][0]);
88 mem.writeIntLittle(u32, out[16 * i + 4 ..][0..4], x[i][1]);154 mem.writeIntLittle(u32, out[16 * i + 4 ..][0..4], x[i][1]);
89 mem.writeIntLittle(u32, out[16 * i + 8 ..][0..4], x[i][2]);155 mem.writeIntLittle(u32, out[16 * i + 8 ..][0..4], x[i][2]);
90 mem.writeIntLittle(u32, out[16 * i + 12 ..][0..4], x[i][3]);156 mem.writeIntLittle(u32, out[16 * i + 12 ..][0..4], x[i][3]);
157 }
91 }158 }
92 }
93159
94 fn contextFeedback(x: *BlockVec, ctx: BlockVec) callconv(.Inline) void {160 fn contextFeedback(x: *BlockVec, ctx: BlockVec) callconv(.Inline) void {
95 x[0] +%= ctx[0];161 x[0] +%= ctx[0];
96 x[1] +%= ctx[1];162 x[1] +%= ctx[1];
97 x[2] +%= ctx[2];163 x[2] +%= ctx[2];
98 x[3] +%= ctx[3];164 x[3] +%= ctx[3];
99 }165 }
100166
101 fn chacha20Xor(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) void {167 fn chacha20Xor(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) void {
102 var ctx = initContext(key, counter);168 var ctx = initContext(key, counter);
103 var x: BlockVec = undefined;169 var x: BlockVec = undefined;
104 var buf: [64]u8 = undefined;170 var buf: [64]u8 = undefined;
105 var i: usize = 0;171 var i: usize = 0;
106 while (i + 64 <= in.len) : (i += 64) {172 while (i + 64 <= in.len) : (i += 64) {
107 chacha20Core(x[0..], ctx);173 chacha20Core(x[0..], ctx);
108 contextFeedback(&x, ctx);174 contextFeedback(&x, ctx);
109 hashToBytes(buf[0..], x);175 hashToBytes(buf[0..], x);
110176
111 var xout = out[i..];177 var xout = out[i..];
112 const xin = in[i..];178 const xin = in[i..];
113 var j: usize = 0;179 var j: usize = 0;
114 while (j < 64) : (j += 1) {180 while (j < 64) : (j += 1) {
115 xout[j] = xin[j];181 xout[j] = xin[j];
116 }182 }
117 j = 0;183 j = 0;
118 while (j < 64) : (j += 1) {184 while (j < 64) : (j += 1) {
119 xout[j] ^= buf[j];185 xout[j] ^= buf[j];
186 }
187 ctx[3][0] += 1;
120 }188 }
121 ctx[3][0] += 1;189 if (i < in.len) {
122 }190 chacha20Core(x[0..], ctx);
123 if (i < in.len) {191 contextFeedback(&x, ctx);
124 chacha20Core(x[0..], ctx);192 hashToBytes(buf[0..], x);
125 contextFeedback(&x, ctx);193
126 hashToBytes(buf[0..], x);194 var xout = out[i..];
127195 const xin = in[i..];
128 var xout = out[i..];196 var j: usize = 0;
129 const xin = in[i..];197 while (j < in.len % 64) : (j += 1) {
130 var j: usize = 0;198 xout[j] = xin[j] ^ buf[j];
131 while (j < in.len % 64) : (j += 1) {199 }
132 xout[j] = xin[j] ^ buf[j];
133 }200 }
134 }201 }
135 }
136202
137 fn hchacha20(input: [16]u8, key: [32]u8) [32]u8 {203 fn hchacha20(input: [16]u8, key: [32]u8) [32]u8 {
138 var c: [4]u32 = undefined;204 var c: [4]u32 = undefined;
139 for (c) |_, i| {205 for (c) |_, i| {
140 c[i] = mem.readIntLittle(u32, input[4 * i ..][0..4]);206 c[i] = mem.readIntLittle(u32, input[4 * i ..][0..4]);
207 }
208 const ctx = initContext(keyToWords(key), c);
209 var x: BlockVec = undefined;
210 chacha20Core(x[0..], ctx);
211 var out: [32]u8 = undefined;
212 mem.writeIntLittle(u32, out[0..4], x[0][0]);
213 mem.writeIntLittle(u32, out[4..8], x[0][1]);
214 mem.writeIntLittle(u32, out[8..12], x[0][2]);
215 mem.writeIntLittle(u32, out[12..16], x[0][3]);
216 mem.writeIntLittle(u32, out[16..20], x[3][0]);
217 mem.writeIntLittle(u32, out[20..24], x[3][1]);
218 mem.writeIntLittle(u32, out[24..28], x[3][2]);
219 mem.writeIntLittle(u32, out[28..32], x[3][3]);
220 return out;
141 }221 }
142 const ctx = initContext(keyToWords(key), c);222 };
143 var x: BlockVec = undefined;223}
144 chacha20Core(x[0..], ctx);
145 var out: [32]u8 = undefined;
146 mem.writeIntLittle(u32, out[0..4], x[0][0]);
147 mem.writeIntLittle(u32, out[4..8], x[0][1]);
148 mem.writeIntLittle(u32, out[8..12], x[0][2]);
149 mem.writeIntLittle(u32, out[12..16], x[0][3]);
150 mem.writeIntLittle(u32, out[16..20], x[3][0]);
151 mem.writeIntLittle(u32, out[20..24], x[3][1]);
152 mem.writeIntLittle(u32, out[24..28], x[3][2]);
153 mem.writeIntLittle(u32, out[28..32], x[3][3]);
154 return out;
155 }
156};
157224
158// Non-vectorized implementation of the core function225// Non-vectorized implementation of the core function
159const ChaCha20NonVecImpl = struct {226fn ChaChaNonVecImpl(comptime rounds_nb: usize) type {
160 const BlockVec = [16]u32;227 return struct {
161228 const BlockVec = [16]u32;
162 fn initContext(key: [8]u32, d: [4]u32) BlockVec {229
163 const c = "expand 32-byte k";230 fn initContext(key: [8]u32, d: [4]u32) BlockVec {
164 const constant_le = comptime [4]u32{231 const c = "expand 32-byte k";
165 mem.readIntLittle(u32, c[0..4]),232 const constant_le = comptime [4]u32{
166 mem.readIntLittle(u32, c[4..8]),233 mem.readIntLittle(u32, c[0..4]),
167 mem.readIntLittle(u32, c[8..12]),234 mem.readIntLittle(u32, c[4..8]),
168 mem.readIntLittle(u32, c[12..16]),235 mem.readIntLittle(u32, c[8..12]),
169 };236 mem.readIntLittle(u32, c[12..16]),
170 return BlockVec{237 };
171 constant_le[0], constant_le[1], constant_le[2], constant_le[3],238 return BlockVec{
172 key[0], key[1], key[2], key[3],239 constant_le[0], constant_le[1], constant_le[2], constant_le[3],
173 key[4], key[5], key[6], key[7],240 key[0], key[1], key[2], key[3],
174 d[0], d[1], d[2], d[3],241 key[4], key[5], key[6], key[7],
175 };242 d[0], d[1], d[2], d[3],
176 }243 };
177244 }
178 const QuarterRound = struct {
179 a: usize,
180 b: usize,
181 c: usize,
182 d: usize,
183 };
184245
185 fn Rp(a: usize, b: usize, c: usize, d: usize) QuarterRound {246 const QuarterRound = struct {
186 return QuarterRound{247 a: usize,
187 .a = a,248 b: usize,
188 .b = b,249 c: usize,
189 .c = c,250 d: usize,
190 .d = d,
191 };251 };
192 }
193252
194 fn chacha20Core(x: *BlockVec, input: BlockVec) callconv(.Inline) void {253 fn Rp(a: usize, b: usize, c: usize, d: usize) QuarterRound {
195 x.* = input;254 return QuarterRound{
196255 .a = a,
197 const rounds = comptime [_]QuarterRound{256 .b = b,
198 Rp(0, 4, 8, 12),257 .c = c,
199 Rp(1, 5, 9, 13),258 .d = d,
200 Rp(2, 6, 10, 14),259 };
201 Rp(3, 7, 11, 15),260 }
202 Rp(0, 5, 10, 15),
203 Rp(1, 6, 11, 12),
204 Rp(2, 7, 8, 13),
205 Rp(3, 4, 9, 14),
206 };
207261
208 comptime var j: usize = 0;262 fn chacha20Core(x: *BlockVec, input: BlockVec) callconv(.Inline) void {
209 inline while (j < 20) : (j += 2) {263 x.* = input;
210 inline for (rounds) |r| {264
211 x[r.a] +%= x[r.b];265 const rounds = comptime [_]QuarterRound{
212 x[r.d] = math.rotl(u32, x[r.d] ^ x[r.a], @as(u32, 16));266 Rp(0, 4, 8, 12),
213 x[r.c] +%= x[r.d];267 Rp(1, 5, 9, 13),
214 x[r.b] = math.rotl(u32, x[r.b] ^ x[r.c], @as(u32, 12));268 Rp(2, 6, 10, 14),
215 x[r.a] +%= x[r.b];269 Rp(3, 7, 11, 15),
216 x[r.d] = math.rotl(u32, x[r.d] ^ x[r.a], @as(u32, 8));270 Rp(0, 5, 10, 15),
217 x[r.c] +%= x[r.d];271 Rp(1, 6, 11, 12),
218 x[r.b] = math.rotl(u32, x[r.b] ^ x[r.c], @as(u32, 7));272 Rp(2, 7, 8, 13),
273 Rp(3, 4, 9, 14),
274 };
275
276 comptime var j: usize = 0;
277 inline while (j < rounds_nb) : (j += 2) {
278 inline for (rounds) |r| {
279 x[r.a] +%= x[r.b];
280 x[r.d] = math.rotl(u32, x[r.d] ^ x[r.a], @as(u32, 16));
281 x[r.c] +%= x[r.d];
282 x[r.b] = math.rotl(u32, x[r.b] ^ x[r.c], @as(u32, 12));
283 x[r.a] +%= x[r.b];
284 x[r.d] = math.rotl(u32, x[r.d] ^ x[r.a], @as(u32, 8));
285 x[r.c] +%= x[r.d];
286 x[r.b] = math.rotl(u32, x[r.b] ^ x[r.c], @as(u32, 7));
287 }
219 }288 }
220 }289 }
221 }
222290
223 fn hashToBytes(out: *[64]u8, x: BlockVec) callconv(.Inline) void {291 fn hashToBytes(out: *[64]u8, x: BlockVec) callconv(.Inline) void {
224 var i: usize = 0;292 var i: usize = 0;
225 while (i < 4) : (i += 1) {293 while (i < 4) : (i += 1) {
226 mem.writeIntLittle(u32, out[16 * i + 0 ..][0..4], x[i * 4 + 0]);294 mem.writeIntLittle(u32, out[16 * i + 0 ..][0..4], x[i * 4 + 0]);
227 mem.writeIntLittle(u32, out[16 * i + 4 ..][0..4], x[i * 4 + 1]);295 mem.writeIntLittle(u32, out[16 * i + 4 ..][0..4], x[i * 4 + 1]);
228 mem.writeIntLittle(u32, out[16 * i + 8 ..][0..4], x[i * 4 + 2]);296 mem.writeIntLittle(u32, out[16 * i + 8 ..][0..4], x[i * 4 + 2]);
229 mem.writeIntLittle(u32, out[16 * i + 12 ..][0..4], x[i * 4 + 3]);297 mem.writeIntLittle(u32, out[16 * i + 12 ..][0..4], x[i * 4 + 3]);
298 }
230 }299 }
231 }
232300
233 fn contextFeedback(x: *BlockVec, ctx: BlockVec) callconv(.Inline) void {301 fn contextFeedback(x: *BlockVec, ctx: BlockVec) callconv(.Inline) void {
234 var i: usize = 0;302 var i: usize = 0;
235 while (i < 16) : (i += 1) {303 while (i < 16) : (i += 1) {
236 x[i] +%= ctx[i];304 x[i] +%= ctx[i];
305 }
237 }306 }
238 }
239307
240 fn chacha20Xor(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) void {308 fn chacha20Xor(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) void {
241 var ctx = initContext(key, counter);309 var ctx = initContext(key, counter);
242 var x: BlockVec = undefined;310 var x: BlockVec = undefined;
243 var buf: [64]u8 = undefined;311 var buf: [64]u8 = undefined;
244 var i: usize = 0;312 var i: usize = 0;
245 while (i + 64 <= in.len) : (i += 64) {313 while (i + 64 <= in.len) : (i += 64) {
246 chacha20Core(x[0..], ctx);314 chacha20Core(x[0..], ctx);
247 contextFeedback(&x, ctx);315 contextFeedback(&x, ctx);
248 hashToBytes(buf[0..], x);316 hashToBytes(buf[0..], x);
249317
250 var xout = out[i..];318 var xout = out[i..];
251 const xin = in[i..];319 const xin = in[i..];
252 var j: usize = 0;320 var j: usize = 0;
253 while (j < 64) : (j += 1) {321 while (j < 64) : (j += 1) {
254 xout[j] = xin[j];322 xout[j] = xin[j];
255 }323 }
256 j = 0;324 j = 0;
257 while (j < 64) : (j += 1) {325 while (j < 64) : (j += 1) {
258 xout[j] ^= buf[j];326 xout[j] ^= buf[j];
327 }
328 ctx[12] += 1;
259 }329 }
260 ctx[12] += 1;330 if (i < in.len) {
261 }331 chacha20Core(x[0..], ctx);
262 if (i < in.len) {332 contextFeedback(&x, ctx);
263 chacha20Core(x[0..], ctx);333 hashToBytes(buf[0..], x);
264 contextFeedback(&x, ctx);334
265 hashToBytes(buf[0..], x);335 var xout = out[i..];
266336 const xin = in[i..];
267 var xout = out[i..];337 var j: usize = 0;
268 const xin = in[i..];338 while (j < in.len % 64) : (j += 1) {
269 var j: usize = 0;339 xout[j] = xin[j] ^ buf[j];
270 while (j < in.len % 64) : (j += 1) {340 }
271 xout[j] = xin[j] ^ buf[j];
272 }341 }
273 }342 }
274 }
275343
276 fn hchacha20(input: [16]u8, key: [32]u8) [32]u8 {344 fn hchacha20(input: [16]u8, key: [32]u8) [32]u8 {
277 var c: [4]u32 = undefined;345 var c: [4]u32 = undefined;
278 for (c) |_, i| {346 for (c) |_, i| {
279 c[i] = mem.readIntLittle(u32, input[4 * i ..][0..4]);347 c[i] = mem.readIntLittle(u32, input[4 * i ..][0..4]);
348 }
349 const ctx = initContext(keyToWords(key), c);
350 var x: BlockVec = undefined;
351 chacha20Core(x[0..], ctx);
352 var out: [32]u8 = undefined;
353 mem.writeIntLittle(u32, out[0..4], x[0]);
354 mem.writeIntLittle(u32, out[4..8], x[1]);
355 mem.writeIntLittle(u32, out[8..12], x[2]);
356 mem.writeIntLittle(u32, out[12..16], x[3]);
357 mem.writeIntLittle(u32, out[16..20], x[12]);
358 mem.writeIntLittle(u32, out[20..24], x[13]);
359 mem.writeIntLittle(u32, out[24..28], x[14]);
360 mem.writeIntLittle(u32, out[28..32], x[15]);
361 return out;
280 }362 }
281 const ctx = initContext(keyToWords(key), c);363 };
282 var x: BlockVec = undefined;364}
283 chacha20Core(x[0..], ctx);
284 var out: [32]u8 = undefined;
285 mem.writeIntLittle(u32, out[0..4], x[0]);
286 mem.writeIntLittle(u32, out[4..8], x[1]);
287 mem.writeIntLittle(u32, out[8..12], x[2]);
288 mem.writeIntLittle(u32, out[12..16], x[3]);
289 mem.writeIntLittle(u32, out[16..20], x[12]);
290 mem.writeIntLittle(u32, out[20..24], x[13]);
291 mem.writeIntLittle(u32, out[24..28], x[14]);
292 mem.writeIntLittle(u32, out[28..32], x[15]);
293 return out;
294 }
295};
296365
297const ChaCha20Impl = if (std.Target.current.cpu.arch == .x86_64) ChaCha20VecImpl else ChaCha20NonVecImpl;366fn ChaChaImpl(comptime rounds_nb: usize) type {
367 return if (std.Target.current.cpu.arch == .x86_64) ChaChaVecImpl(rounds_nb) else ChaChaNonVecImpl(rounds_nb);
368}
298369
299fn keyToWords(key: [32]u8) [8]u32 {370fn keyToWords(key: [32]u8) [8]u32 {
300 var k: [8]u32 = undefined;371 var k: [8]u32 = undefined;
...@@ -305,68 +376,239 @@ fn keyToWords(key: [32]u8) [8]u32 {...@@ -305,68 +376,239 @@ fn keyToWords(key: [32]u8) [8]u32 {
305 return k;376 return k;
306}377}
307378
308/// ChaCha20 avoids the possibility of timing attacks, as there are no branches379fn extend(key: [32]u8, nonce: [24]u8, comptime rounds_nb: usize) struct { key: [32]u8, nonce: [12]u8 } {
309/// on secret key data.380 var subnonce: [12]u8 = undefined;
310///381 mem.set(u8, subnonce[0..4], 0);
311/// in and out should be the same length.382 mem.copy(u8, subnonce[4..], nonce[16..24]);
312/// counter should generally be 0 or 1383 return .{
313///384 .key = ChaChaImpl(rounds_nb).hchacha20(nonce[0..16].*, key),
314/// ChaCha20 is self-reversing. To decrypt just run the cipher with the same385 .nonce = subnonce,
315/// counter, nonce, and key.386 };
316pub const ChaCha20IETF = struct {387}
317 pub fn xor(out: []u8, in: []const u8, counter: u32, key: [32]u8, nonce: [12]u8) void {388
318 assert(in.len == out.len);389fn ChaChaIETF(comptime rounds_nb: usize) type {
319 assert((in.len >> 6) + counter <= maxInt(u32));390 return struct {
320391 /// Nonce length in bytes.
321 var c: [4]u32 = undefined;392 pub const nonce_length = 12;
322 c[0] = counter;393 /// Key length in bytes.
323 c[1] = mem.readIntLittle(u32, nonce[0..4]);394 pub const key_length = 32;
324 c[2] = mem.readIntLittle(u32, nonce[4..8]);395
325 c[3] = mem.readIntLittle(u32, nonce[8..12]);396 /// Add the output of the ChaCha20 stream cipher to `in` and stores the result into `out`.
326 ChaCha20Impl.chacha20Xor(out, in, keyToWords(key), c);397 /// WARNING: This function doesn't provide authenticated encryption.
327 }398 /// Using the AEAD or one of the `box` versions is usually preferred.
328};399 pub fn xor(out: []u8, in: []const u8, counter: u32, key: [key_length]u8, nonce: [nonce_length]u8) void {
329400 assert(in.len == out.len);
330/// This is the original ChaCha20 before RFC 7539, which recommends using the401 assert(in.len / 64 <= (1 << 32 - 1) - counter);
331/// orgininal version on applications such as disk or file encryption that might402
332/// exceed the 256 GiB limit of the 96-bit nonce version.403 var d: [4]u32 = undefined;
333pub const ChaCha20With64BitNonce = struct {404 d[0] = counter;
334 pub fn xor(out: []u8, in: []const u8, counter: u64, key: [32]u8, nonce: [8]u8) void {405 d[1] = mem.readIntLittle(u32, nonce[0..4]);
335 assert(in.len == out.len);406 d[2] = mem.readIntLittle(u32, nonce[4..8]);
336 assert(counter +% (in.len >> 6) >= counter);407 d[3] = mem.readIntLittle(u32, nonce[8..12]);
337408 ChaChaImpl(rounds_nb).chacha20Xor(out, in, keyToWords(key), d);
338 var cursor: usize = 0;409 }
339 const k = keyToWords(key);410 };
340 var c: [4]u32 = undefined;411}
341 c[0] = @truncate(u32, counter);412
342 c[1] = @truncate(u32, counter >> 32);413fn ChaChaWith64BitNonce(comptime rounds_nb: usize) type {
343 c[2] = mem.readIntLittle(u32, nonce[0..4]);414 return struct {
344 c[3] = mem.readIntLittle(u32, nonce[4..8]);415 /// Nonce length in bytes.
345416 pub const nonce_length = 8;
346 const block_length = (1 << 6);417 /// Key length in bytes.
347 // The full block size is greater than the address space on a 32bit machine418 pub const key_length = 32;
348 const big_block = if (@sizeOf(usize) > 4) (block_length << 32) else maxInt(usize);419
349420 /// Add the output of the ChaCha20 stream cipher to `in` and stores the result into `out`.
350 // first partial big block421 /// WARNING: This function doesn't provide authenticated encryption.
351 if (((@intCast(u64, maxInt(u32) - @truncate(u32, counter)) + 1) << 6) < in.len) {422 /// Using the AEAD or one of the `box` versions is usually preferred.
352 ChaCha20Impl.chacha20Xor(out[cursor..big_block], in[cursor..big_block], k, c);423 pub fn xor(out: []u8, in: []const u8, counter: u64, key: [key_length]u8, nonce: [nonce_length]u8) void {
353 cursor = big_block - cursor;424 assert(in.len == out.len);
354 c[1] += 1;425 assert(in.len / 64 <= (1 << 64 - 1) - counter);
355 if (comptime @sizeOf(usize) > 4) {426
356 // A big block is giant: 256 GiB, but we can avoid this limitation427 var cursor: usize = 0;
357 var remaining_blocks: u32 = @intCast(u32, (in.len / big_block));428 const k = keyToWords(key);
358 var i: u32 = 0;429 var c: [4]u32 = undefined;
359 while (remaining_blocks > 0) : (remaining_blocks -= 1) {430 c[0] = @truncate(u32, counter);
360 ChaCha20Impl.chacha20Xor(out[cursor .. cursor + big_block], in[cursor .. cursor + big_block], k, c);431 c[1] = @truncate(u32, counter >> 32);
361 c[1] += 1; // upper 32-bit of counter, generic chacha20Xor() doesn't know about this.432 c[2] = mem.readIntLittle(u32, nonce[0..4]);
362 cursor += big_block;433 c[3] = mem.readIntLittle(u32, nonce[4..8]);
434
435 const block_length = (1 << 6);
436 // The full block size is greater than the address space on a 32bit machine
437 const big_block = if (@sizeOf(usize) > 4) (block_length << 32) else maxInt(usize);
438
439 // first partial big block
440 if (((@intCast(u64, maxInt(u32) - @truncate(u32, counter)) + 1) << 6) < in.len) {
441 ChaChaImpl(rounds_nb).chacha20Xor(out[cursor..big_block], in[cursor..big_block], k, c);
442 cursor = big_block - cursor;
443 c[1] += 1;
444 if (comptime @sizeOf(usize) > 4) {
445 // A big block is giant: 256 GiB, but we can avoid this limitation
446 var remaining_blocks: u32 = @intCast(u32, (in.len / big_block));
447 var i: u32 = 0;
448 while (remaining_blocks > 0) : (remaining_blocks -= 1) {
449 ChaChaImpl(rounds_nb).chacha20Xor(out[cursor .. cursor + big_block], in[cursor .. cursor + big_block], k, c);
450 c[1] += 1; // upper 32-bit of counter, generic chacha20Xor() doesn't know about this.
451 cursor += big_block;
452 }
363 }453 }
364 }454 }
455 ChaChaImpl(rounds_nb).chacha20Xor(out[cursor..], in[cursor..], k, c);
456 }
457 };
458}
459
460fn XChaChaIETF(comptime rounds_nb: usize) type {
461 return struct {
462 /// Nonce length in bytes.
463 pub const nonce_length = 24;
464 /// Key length in bytes.
465 pub const key_length = 32;
466
467 /// Add the output of the XChaCha20 stream cipher to `in` and stores the result into `out`.
468 /// WARNING: This function doesn't provide authenticated encryption.
469 /// Using the AEAD or one of the `box` versions is usually preferred.
470 pub fn xor(out: []u8, in: []const u8, counter: u32, key: [key_length]u8, nonce: [nonce_length]u8) void {
471 const extended = extend(key, nonce, rounds_nb);
472 ChaChaIETF(rounds_nb).xor(out, in, counter, extended.key, extended.nonce);
473 }
474 };
475}
476
477fn ChaChaPoly1305(comptime rounds_nb: usize) type {
478 return struct {
479 pub const tag_length = 16;
480 pub const nonce_length = 12;
481 pub const key_length = 32;
482
483 /// c: ciphertext: output buffer should be of size m.len
484 /// tag: authentication tag: output MAC
485 /// m: message
486 /// ad: Associated Data
487 /// npub: public nonce
488 /// k: private key
489 pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
490 assert(c.len == m.len);
491
492 var polyKey = [_]u8{0} ** 32;
493 ChaChaIETF(rounds_nb).xor(polyKey[0..], polyKey[0..], 0, k, npub);
494
495 ChaChaIETF(rounds_nb).xor(c[0..m.len], m, 1, k, npub);
496
497 var mac = Poly1305.init(polyKey[0..]);
498 mac.update(ad);
499 if (ad.len % 16 != 0) {
500 const zeros = [_]u8{0} ** 16;
501 const padding = 16 - (ad.len % 16);
502 mac.update(zeros[0..padding]);
503 }
504 mac.update(c[0..m.len]);
505 if (m.len % 16 != 0) {
506 const zeros = [_]u8{0} ** 16;
507 const padding = 16 - (m.len % 16);
508 mac.update(zeros[0..padding]);
509 }
510 var lens: [16]u8 = undefined;
511 mem.writeIntLittle(u64, lens[0..8], ad.len);
512 mem.writeIntLittle(u64, lens[8..16], m.len);
513 mac.update(lens[0..]);
514 mac.final(tag);
515 }
516
517 /// m: message: output buffer should be of size c.len
518 /// c: ciphertext
519 /// tag: authentication tag
520 /// ad: Associated Data
521 /// npub: public nonce
522 /// k: private key
523 /// NOTE: the check of the authentication tag is currently not done in constant time
524 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) Error!void {
525 assert(c.len == m.len);
526
527 var polyKey = [_]u8{0} ** 32;
528 ChaChaIETF(rounds_nb).xor(polyKey[0..], polyKey[0..], 0, k, npub);
529
530 var mac = Poly1305.init(polyKey[0..]);
531
532 mac.update(ad);
533 if (ad.len % 16 != 0) {
534 const zeros = [_]u8{0} ** 16;
535 const padding = 16 - (ad.len % 16);
536 mac.update(zeros[0..padding]);
537 }
538 mac.update(c);
539 if (c.len % 16 != 0) {
540 const zeros = [_]u8{0} ** 16;
541 const padding = 16 - (c.len % 16);
542 mac.update(zeros[0..padding]);
543 }
544 var lens: [16]u8 = undefined;
545 mem.writeIntLittle(u64, lens[0..8], ad.len);
546 mem.writeIntLittle(u64, lens[8..16], c.len);
547 mac.update(lens[0..]);
548 var computedTag: [16]u8 = undefined;
549 mac.final(computedTag[0..]);
550
551 var acc: u8 = 0;
552 for (computedTag) |_, i| {
553 acc |= computedTag[i] ^ tag[i];
554 }
555 if (acc != 0) {
556 return error.AuthenticationFailed;
557 }
558 ChaChaIETF(rounds_nb).xor(m[0..c.len], c, 1, k, npub);
559 }
560 };
561}
562
563fn XChaChaPoly1305(comptime rounds_nb: usize) type {
564 return struct {
565 pub const tag_length = 16;
566 pub const nonce_length = 24;
567 pub const key_length = 32;
568
569 /// c: ciphertext: output buffer should be of size m.len
570 /// tag: authentication tag: output MAC
571 /// m: message
572 /// ad: Associated Data
573 /// npub: public nonce
574 /// k: private key
575 pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
576 const extended = extend(k, npub, rounds_nb);
577 return ChaChaPoly1305(rounds_nb).encrypt(c, tag, m, ad, extended.nonce, extended.key);
365 }578 }
366579
367 ChaCha20Impl.chacha20Xor(out[cursor..], in[cursor..], k, c);580 /// m: message: output buffer should be of size c.len
581 /// c: ciphertext
582 /// tag: authentication tag
583 /// ad: Associated Data
584 /// npub: public nonce
585 /// k: private key
586 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) Error!void {
587 const extended = extend(k, npub, rounds_nb);
588 return ChaChaPoly1305(rounds_nb).decrypt(m, c, tag, ad, extended.nonce, extended.key);
589 }
590 };
591}
592
593test "chacha20 AEAD API" {
594 const aeads = [_]type{ ChaCha20Poly1305, XChaCha20Poly1305 };
595 const m = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";
596 const ad = "Additional data";
597
598 inline for (aeads) |aead| {
599 const key = [_]u8{69} ** aead.key_length;
600 const nonce = [_]u8{42} ** aead.nonce_length;
601 var c: [m.len]u8 = undefined;
602 var tag: [aead.tag_length]u8 = undefined;
603 var out: [m.len]u8 = undefined;
604
605 aead.encrypt(c[0..], tag[0..], m, ad, nonce, key);
606 try aead.decrypt(out[0..], c[0..], tag, ad[0..], nonce, key);
607 testing.expectEqualSlices(u8, out[0..], m);
608 c[0] += 1;
609 testing.expectError(error.AuthenticationFailed, aead.decrypt(out[0..], c[0..], tag, ad[0..], nonce, key));
368 }610 }
369};611}
370612
371// https://tools.ietf.org/html/rfc7539#section-2.4.2613// https://tools.ietf.org/html/rfc7539#section-2.4.2
372test "crypto.chacha20 test vector sunscreen" {614test "crypto.chacha20 test vector sunscreen" {
...@@ -387,7 +629,7 @@ test "crypto.chacha20 test vector sunscreen" {...@@ -387,7 +629,7 @@ test "crypto.chacha20 test vector sunscreen" {
387 0xb4, 0x0b, 0x8e, 0xed, 0xf2, 0x78, 0x5e, 0x42,629 0xb4, 0x0b, 0x8e, 0xed, 0xf2, 0x78, 0x5e, 0x42,
388 0x87, 0x4d,630 0x87, 0x4d,
389 };631 };
390 const input = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";632 const m = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";
391 var result: [114]u8 = undefined;633 var result: [114]u8 = undefined;
392 const key = [_]u8{634 const key = [_]u8{
393 0, 1, 2, 3, 4, 5, 6, 7,635 0, 1, 2, 3, 4, 5, 6, 7,
...@@ -401,13 +643,12 @@ test "crypto.chacha20 test vector sunscreen" {...@@ -401,13 +643,12 @@ test "crypto.chacha20 test vector sunscreen" {
401 0, 0, 0, 0,643 0, 0, 0, 0,
402 };644 };
403645
404 ChaCha20IETF.xor(result[0..], input[0..], 1, key, nonce);646 ChaCha20IETF.xor(result[0..], m[0..], 1, key, nonce);
405 testing.expectEqualSlices(u8, &expected_result, &result);647 testing.expectEqualSlices(u8, &expected_result, &result);
406648
407 // Chacha20 is self-reversing.649 var m2: [114]u8 = undefined;
408 var plaintext: [114]u8 = undefined;650 ChaCha20IETF.xor(m2[0..], result[0..], 1, key, nonce);
409 ChaCha20IETF.xor(plaintext[0..], result[0..], 1, key, nonce);651 testing.expect(mem.order(u8, m, &m2) == .eq);
410 testing.expect(mem.order(u8, input, &plaintext) == .eq);
411}652}
412653
413// https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7654// https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7
...@@ -422,7 +663,7 @@ test "crypto.chacha20 test vector 1" {...@@ -422,7 +663,7 @@ test "crypto.chacha20 test vector 1" {
422 0x6a, 0x43, 0xb8, 0xf4, 0x15, 0x18, 0xa1, 0x1c,663 0x6a, 0x43, 0xb8, 0xf4, 0x15, 0x18, 0xa1, 0x1c,
423 0xc3, 0x87, 0xb6, 0x69, 0xb2, 0xee, 0x65, 0x86,664 0xc3, 0x87, 0xb6, 0x69, 0xb2, 0xee, 0x65, 0x86,
424 };665 };
425 const input = [_]u8{666 const m = [_]u8{
426 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,667 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
427 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,668 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
428 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,669 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
...@@ -441,7 +682,7 @@ test "crypto.chacha20 test vector 1" {...@@ -441,7 +682,7 @@ test "crypto.chacha20 test vector 1" {
441 };682 };
442 const nonce = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0 };683 const nonce = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0 };
443684
444 ChaCha20With64BitNonce.xor(result[0..], input[0..], 0, key, nonce);685 ChaCha20With64BitNonce.xor(result[0..], m[0..], 0, key, nonce);
445 testing.expectEqualSlices(u8, &expected_result, &result);686 testing.expectEqualSlices(u8, &expected_result, &result);
446}687}
447688
...@@ -456,7 +697,7 @@ test "crypto.chacha20 test vector 2" {...@@ -456,7 +697,7 @@ test "crypto.chacha20 test vector 2" {
456 0x53, 0xd7, 0x92, 0xb1, 0xc4, 0x3f, 0xea, 0x81,697 0x53, 0xd7, 0x92, 0xb1, 0xc4, 0x3f, 0xea, 0x81,
457 0x7e, 0x9a, 0xd2, 0x75, 0xae, 0x54, 0x69, 0x63,698 0x7e, 0x9a, 0xd2, 0x75, 0xae, 0x54, 0x69, 0x63,
458 };699 };
459 const input = [_]u8{700 const m = [_]u8{
460 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,701 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
461 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,702 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
462 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,703 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
...@@ -475,7 +716,7 @@ test "crypto.chacha20 test vector 2" {...@@ -475,7 +716,7 @@ test "crypto.chacha20 test vector 2" {
475 };716 };
476 const nonce = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0 };717 const nonce = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0 };
477718
478 ChaCha20With64BitNonce.xor(result[0..], input[0..], 0, key, nonce);719 ChaCha20With64BitNonce.xor(result[0..], m[0..], 0, key, nonce);
479 testing.expectEqualSlices(u8, &expected_result, &result);720 testing.expectEqualSlices(u8, &expected_result, &result);
480}721}
481722
...@@ -490,7 +731,7 @@ test "crypto.chacha20 test vector 3" {...@@ -490,7 +731,7 @@ test "crypto.chacha20 test vector 3" {
490 0x52, 0x77, 0x06, 0x2e, 0xb7, 0xa0, 0x43, 0x3e,731 0x52, 0x77, 0x06, 0x2e, 0xb7, 0xa0, 0x43, 0x3e,
491 0x44, 0x5f, 0x41, 0xe3,732 0x44, 0x5f, 0x41, 0xe3,
492 };733 };
493 const input = [_]u8{734 const m = [_]u8{
494 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,735 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
495 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,736 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
496 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,737 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
...@@ -509,7 +750,7 @@ test "crypto.chacha20 test vector 3" {...@@ -509,7 +750,7 @@ test "crypto.chacha20 test vector 3" {
509 };750 };
510 const nonce = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 1 };751 const nonce = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 1 };
511752
512 ChaCha20With64BitNonce.xor(result[0..], input[0..], 0, key, nonce);753 ChaCha20With64BitNonce.xor(result[0..], m[0..], 0, key, nonce);
513 testing.expectEqualSlices(u8, &expected_result, &result);754 testing.expectEqualSlices(u8, &expected_result, &result);
514}755}
515756
...@@ -524,7 +765,7 @@ test "crypto.chacha20 test vector 4" {...@@ -524,7 +765,7 @@ test "crypto.chacha20 test vector 4" {
524 0x5d, 0xdc, 0x49, 0x7a, 0x0b, 0x46, 0x6e, 0x7d,765 0x5d, 0xdc, 0x49, 0x7a, 0x0b, 0x46, 0x6e, 0x7d,
525 0x6b, 0xbd, 0xb0, 0x04, 0x1b, 0x2f, 0x58, 0x6b,766 0x6b, 0xbd, 0xb0, 0x04, 0x1b, 0x2f, 0x58, 0x6b,
526 };767 };
527 const input = [_]u8{768 const m = [_]u8{
528 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,769 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
529 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,770 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
530 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,771 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
...@@ -543,7 +784,7 @@ test "crypto.chacha20 test vector 4" {...@@ -543,7 +784,7 @@ test "crypto.chacha20 test vector 4" {
543 };784 };
544 const nonce = [_]u8{ 1, 0, 0, 0, 0, 0, 0, 0 };785 const nonce = [_]u8{ 1, 0, 0, 0, 0, 0, 0, 0 };
545786
546 ChaCha20With64BitNonce.xor(result[0..], input[0..], 0, key, nonce);787 ChaCha20With64BitNonce.xor(result[0..], m[0..], 0, key, nonce);
547 testing.expectEqualSlices(u8, &expected_result, &result);788 testing.expectEqualSlices(u8, &expected_result, &result);
548}789}
549790
...@@ -585,7 +826,7 @@ test "crypto.chacha20 test vector 5" {...@@ -585,7 +826,7 @@ test "crypto.chacha20 test vector 5" {
585 0x87, 0x46, 0xd4, 0x52, 0x4d, 0x38, 0x40, 0x7a,826 0x87, 0x46, 0xd4, 0x52, 0x4d, 0x38, 0x40, 0x7a,
586 0x6d, 0xeb, 0x3a, 0xb7, 0x8f, 0xab, 0x78, 0xc9,827 0x6d, 0xeb, 0x3a, 0xb7, 0x8f, 0xab, 0x78, 0xc9,
587 };828 };
588 const input = [_]u8{829 const m = [_]u8{
589 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,830 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
590 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,831 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
591 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,832 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
...@@ -615,147 +856,14 @@ test "crypto.chacha20 test vector 5" {...@@ -615,147 +856,14 @@ test "crypto.chacha20 test vector 5" {
615 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,856 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
616 };857 };
617858
618 ChaCha20With64BitNonce.xor(result[0..], input[0..], 0, key, nonce);859 ChaCha20With64BitNonce.xor(result[0..], m[0..], 0, key, nonce);
619 testing.expectEqualSlices(u8, &expected_result, &result);860 testing.expectEqualSlices(u8, &expected_result, &result);
620}861}
621862
622pub const chacha20poly1305_tag_length = 16;
623
624fn chacha20poly1305SealDetached(ciphertext: []u8, tag: *[chacha20poly1305_tag_length]u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) void {
625 assert(ciphertext.len == plaintext.len);
626
627 // derive poly1305 key
628 var polyKey = [_]u8{0} ** 32;
629 ChaCha20IETF.xor(polyKey[0..], polyKey[0..], 0, key, nonce);
630
631 // encrypt plaintext
632 ChaCha20IETF.xor(ciphertext[0..plaintext.len], plaintext, 1, key, nonce);
633
634 // construct mac
635 var mac = Poly1305.init(polyKey[0..]);
636 mac.update(data);
637 if (data.len % 16 != 0) {
638 const zeros = [_]u8{0} ** 16;
639 const padding = 16 - (data.len % 16);
640 mac.update(zeros[0..padding]);
641 }
642 mac.update(ciphertext[0..plaintext.len]);
643 if (plaintext.len % 16 != 0) {
644 const zeros = [_]u8{0} ** 16;
645 const padding = 16 - (plaintext.len % 16);
646 mac.update(zeros[0..padding]);
647 }
648 var lens: [16]u8 = undefined;
649 mem.writeIntLittle(u64, lens[0..8], data.len);
650 mem.writeIntLittle(u64, lens[8..16], plaintext.len);
651 mac.update(lens[0..]);
652 mac.final(tag);
653}
654
655fn chacha20poly1305Seal(ciphertextAndTag: []u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) void {
656 return chacha20poly1305SealDetached(ciphertextAndTag[0..plaintext.len], ciphertextAndTag[plaintext.len..][0..chacha20poly1305_tag_length], plaintext, data, key, nonce);
657}
658
659/// Verifies and decrypts an authenticated message produced by chacha20poly1305SealDetached.
660fn chacha20poly1305OpenDetached(dst: []u8, ciphertext: []const u8, tag: *const [chacha20poly1305_tag_length]u8, data: []const u8, key: [32]u8, nonce: [12]u8) Error!void {
661 // split ciphertext and tag
662 assert(dst.len == ciphertext.len);
663
664 // derive poly1305 key
665 var polyKey = [_]u8{0} ** 32;
666 ChaCha20IETF.xor(polyKey[0..], polyKey[0..], 0, key, nonce);
667
668 // construct mac
669 var mac = Poly1305.init(polyKey[0..]);
670
671 mac.update(data);
672 if (data.len % 16 != 0) {
673 const zeros = [_]u8{0} ** 16;
674 const padding = 16 - (data.len % 16);
675 mac.update(zeros[0..padding]);
676 }
677 mac.update(ciphertext);
678 if (ciphertext.len % 16 != 0) {
679 const zeros = [_]u8{0} ** 16;
680 const padding = 16 - (ciphertext.len % 16);
681 mac.update(zeros[0..padding]);
682 }
683 var lens: [16]u8 = undefined;
684 mem.writeIntLittle(u64, lens[0..8], data.len);
685 mem.writeIntLittle(u64, lens[8..16], ciphertext.len);
686 mac.update(lens[0..]);
687 var computedTag: [16]u8 = undefined;
688 mac.final(computedTag[0..]);
689
690 // verify mac in constant time
691 // TODO: we can't currently guarantee that this will run in constant time.
692 // See https://github.com/ziglang/zig/issues/1776
693 var acc: u8 = 0;
694 for (computedTag) |_, i| {
695 acc |= computedTag[i] ^ tag[i];
696 }
697 if (acc != 0) {
698 return error.AuthenticationFailed;
699 }
700
701 // decrypt ciphertext
702 ChaCha20IETF.xor(dst[0..ciphertext.len], ciphertext, 1, key, nonce);
703}
704
705/// Verifies and decrypts an authenticated message produced by chacha20poly1305Seal.
706fn chacha20poly1305Open(dst: []u8, ciphertextAndTag: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) Error!void {
707 if (ciphertextAndTag.len < chacha20poly1305_tag_length) {
708 return error.AuthenticationFailed;
709 }
710 const ciphertextLen = ciphertextAndTag.len - chacha20poly1305_tag_length;
711 return try chacha20poly1305OpenDetached(dst, ciphertextAndTag[0..ciphertextLen], ciphertextAndTag[ciphertextLen..][0..chacha20poly1305_tag_length], data, key, nonce);
712}
713
714fn extend(key: [32]u8, nonce: [24]u8) struct { key: [32]u8, nonce: [12]u8 } {
715 var subnonce: [12]u8 = undefined;
716 mem.set(u8, subnonce[0..4], 0);
717 mem.copy(u8, subnonce[4..], nonce[16..24]);
718 return .{
719 .key = ChaCha20Impl.hchacha20(nonce[0..16].*, key),
720 .nonce = subnonce,
721 };
722}
723
724pub const XChaCha20IETF = struct {
725 pub fn xor(out: []u8, in: []const u8, counter: u32, key: [32]u8, nonce: [24]u8) void {
726 const extended = extend(key, nonce);
727 ChaCha20IETF.xor(out, in, counter, extended.key, extended.nonce);
728 }
729};
730
731pub const xchacha20poly1305_tag_length = 16;
732
733fn xchacha20poly1305SealDetached(ciphertext: []u8, tag: *[chacha20poly1305_tag_length]u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [24]u8) void {
734 const extended = extend(key, nonce);
735 return chacha20poly1305SealDetached(ciphertext, tag, plaintext, data, extended.key, extended.nonce);
736}
737
738fn xchacha20poly1305Seal(ciphertextAndTag: []u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [24]u8) void {
739 const extended = extend(key, nonce);
740 return chacha20poly1305Seal(ciphertextAndTag, plaintext, data, extended.key, extended.nonce);
741}
742
743/// Verifies and decrypts an authenticated message produced by xchacha20poly1305SealDetached.
744fn xchacha20poly1305OpenDetached(plaintext: []u8, ciphertext: []const u8, tag: *const [chacha20poly1305_tag_length]u8, data: []const u8, key: [32]u8, nonce: [24]u8) Error!void {
745 const extended = extend(key, nonce);
746 return try chacha20poly1305OpenDetached(plaintext, ciphertext, tag, data, extended.key, extended.nonce);
747}
748
749/// Verifies and decrypts an authenticated message produced by xchacha20poly1305Seal.
750fn xchacha20poly1305Open(ciphertextAndTag: []u8, msgAndTag: []const u8, data: []const u8, key: [32]u8, nonce: [24]u8) Error!void {
751 const extended = extend(key, nonce);
752 return try chacha20poly1305Open(ciphertextAndTag, msgAndTag, data, extended.key, extended.nonce);
753}
754
755test "seal" {863test "seal" {
756 {864 {
757 const plaintext = "";865 const m = "";
758 const data = "";866 const ad = "";
759 const key = [_]u8{867 const key = [_]u8{
760 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,868 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
761 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,869 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
...@@ -764,11 +872,11 @@ test "seal" {...@@ -764,11 +872,11 @@ test "seal" {
764 const exp_out = [_]u8{ 0xa0, 0x78, 0x4d, 0x7a, 0x47, 0x16, 0xf3, 0xfe, 0xb4, 0xf6, 0x4e, 0x7f, 0x4b, 0x39, 0xbf, 0x4 };872 const exp_out = [_]u8{ 0xa0, 0x78, 0x4d, 0x7a, 0x47, 0x16, 0xf3, 0xfe, 0xb4, 0xf6, 0x4e, 0x7f, 0x4b, 0x39, 0xbf, 0x4 };
765873
766 var out: [exp_out.len]u8 = undefined;874 var out: [exp_out.len]u8 = undefined;
767 chacha20poly1305Seal(out[0..], plaintext, data, key, nonce);875 ChaCha20Poly1305.encrypt(out[0..m.len], out[m.len..], m, ad, nonce, key);
768 testing.expectEqualSlices(u8, exp_out[0..], out[0..]);876 testing.expectEqualSlices(u8, exp_out[0..], out[0..]);
769 }877 }
770 {878 {
771 const plaintext = [_]u8{879 const m = [_]u8{
772 0x4c, 0x61, 0x64, 0x69, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x47, 0x65, 0x6e, 0x74, 0x6c,880 0x4c, 0x61, 0x64, 0x69, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x47, 0x65, 0x6e, 0x74, 0x6c,
773 0x65, 0x6d, 0x65, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x61, 0x73,881 0x65, 0x6d, 0x65, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x61, 0x73,
774 0x73, 0x20, 0x6f, 0x66, 0x20, 0x27, 0x39, 0x39, 0x3a, 0x20, 0x49, 0x66, 0x20, 0x49, 0x20, 0x63,882 0x73, 0x20, 0x6f, 0x66, 0x20, 0x27, 0x39, 0x39, 0x3a, 0x20, 0x49, 0x66, 0x20, 0x49, 0x20, 0x63,
...@@ -778,7 +886,7 @@ test "seal" {...@@ -778,7 +886,7 @@ test "seal" {
778 0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69,886 0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69,
779 0x74, 0x2e,887 0x74, 0x2e,
780 };888 };
781 const data = [_]u8{ 0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7 };889 const ad = [_]u8{ 0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7 };
782 const key = [_]u8{890 const key = [_]u8{
783 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,891 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
784 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,892 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
...@@ -797,15 +905,15 @@ test "seal" {...@@ -797,15 +905,15 @@ test "seal" {
797 };905 };
798906
799 var out: [exp_out.len]u8 = undefined;907 var out: [exp_out.len]u8 = undefined;
800 chacha20poly1305Seal(out[0..], plaintext[0..], data[0..], key, nonce);908 ChaCha20Poly1305.encrypt(out[0..m.len], out[m.len..], m[0..], ad[0..], nonce, key);
801 testing.expectEqualSlices(u8, exp_out[0..], out[0..]);909 testing.expectEqualSlices(u8, exp_out[0..], out[0..]);
802 }910 }
803}911}
804912
805test "open" {913test "open" {
806 {914 {
807 const ciphertext = [_]u8{ 0xa0, 0x78, 0x4d, 0x7a, 0x47, 0x16, 0xf3, 0xfe, 0xb4, 0xf6, 0x4e, 0x7f, 0x4b, 0x39, 0xbf, 0x4 };915 const c = [_]u8{ 0xa0, 0x78, 0x4d, 0x7a, 0x47, 0x16, 0xf3, 0xfe, 0xb4, 0xf6, 0x4e, 0x7f, 0x4b, 0x39, 0xbf, 0x4 };
808 const data = "";916 const ad = "";
809 const key = [_]u8{917 const key = [_]u8{
810 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,918 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
811 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,919 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
...@@ -814,11 +922,11 @@ test "open" {...@@ -814,11 +922,11 @@ test "open" {
814 const exp_out = "";922 const exp_out = "";
815923
816 var out: [exp_out.len]u8 = undefined;924 var out: [exp_out.len]u8 = undefined;
817 try chacha20poly1305Open(out[0..], ciphertext[0..], data, key, nonce);925 try ChaCha20Poly1305.decrypt(out[0..], c[0..exp_out.len], c[exp_out.len..].*, ad[0..], nonce, key);
818 testing.expectEqualSlices(u8, exp_out[0..], out[0..]);926 testing.expectEqualSlices(u8, exp_out[0..], out[0..]);
819 }927 }
820 {928 {
821 const ciphertext = [_]u8{929 const c = [_]u8{
822 0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb, 0x7b, 0x86, 0xaf, 0xbc, 0x53, 0xef, 0x7e, 0xc2,930 0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb, 0x7b, 0x86, 0xaf, 0xbc, 0x53, 0xef, 0x7e, 0xc2,
823 0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x8, 0xfe, 0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6,931 0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x8, 0xfe, 0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6,
824 0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67, 0x12, 0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b,932 0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67, 0x12, 0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b,
...@@ -829,7 +937,7 @@ test "open" {...@@ -829,7 +937,7 @@ test "open" {
829 0x61, 0x16, 0x1a, 0xe1, 0xb, 0x59, 0x4f, 0x9, 0xe2, 0x6a, 0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60,937 0x61, 0x16, 0x1a, 0xe1, 0xb, 0x59, 0x4f, 0x9, 0xe2, 0x6a, 0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60,
830 0x6, 0x91,938 0x6, 0x91,
831 };939 };
832 const data = [_]u8{ 0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7 };940 const ad = [_]u8{ 0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7 };
833 const key = [_]u8{941 const key = [_]u8{
834 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,942 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
835 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,943 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
...@@ -847,126 +955,45 @@ test "open" {...@@ -847,126 +955,45 @@ test "open" {
847 };955 };
848956
849 var out: [exp_out.len]u8 = undefined;957 var out: [exp_out.len]u8 = undefined;
850 try chacha20poly1305Open(out[0..], ciphertext[0..], data[0..], key, nonce);958 try ChaCha20Poly1305.decrypt(out[0..], c[0..exp_out.len], c[exp_out.len..].*, ad[0..], nonce, key);
851 testing.expectEqualSlices(u8, exp_out[0..], out[0..]);959 testing.expectEqualSlices(u8, exp_out[0..], out[0..]);
852960
853 // corrupting the ciphertext, data, key, or nonce should cause a failure961 // corrupting the ciphertext, data, key, or nonce should cause a failure
854 var bad_ciphertext = ciphertext;962 var bad_c = c;
855 bad_ciphertext[0] ^= 1;963 bad_c[0] ^= 1;
856 testing.expectError(error.AuthenticationFailed, chacha20poly1305Open(out[0..], bad_ciphertext[0..], data[0..], key, nonce));964 testing.expectError(error.AuthenticationFailed, ChaCha20Poly1305.decrypt(out[0..], bad_c[0..out.len], bad_c[out.len..].*, ad[0..], nonce, key));
857 var bad_data = data;965 var bad_ad = ad;
858 bad_data[0] ^= 1;966 bad_ad[0] ^= 1;
859 testing.expectError(error.AuthenticationFailed, chacha20poly1305Open(out[0..], ciphertext[0..], bad_data[0..], key, nonce));967 testing.expectError(error.AuthenticationFailed, ChaCha20Poly1305.decrypt(out[0..], c[0..out.len], c[out.len..].*, bad_ad[0..], nonce, key));
860 var bad_key = key;968 var bad_key = key;
861 bad_key[0] ^= 1;969 bad_key[0] ^= 1;
862 testing.expectError(error.AuthenticationFailed, chacha20poly1305Open(out[0..], ciphertext[0..], data[0..], bad_key, nonce));970 testing.expectError(error.AuthenticationFailed, ChaCha20Poly1305.decrypt(out[0..], c[0..out.len], c[out.len..].*, ad[0..], nonce, bad_key));
863 var bad_nonce = nonce;971 var bad_nonce = nonce;
864 bad_nonce[0] ^= 1;972 bad_nonce[0] ^= 1;
865 testing.expectError(error.AuthenticationFailed, chacha20poly1305Open(out[0..], ciphertext[0..], data[0..], key, bad_nonce));973 testing.expectError(error.AuthenticationFailed, ChaCha20Poly1305.decrypt(out[0..], c[0..out.len], c[out.len..].*, ad[0..], bad_nonce, key));
866
867 // a short ciphertext should result in a different error
868 testing.expectError(error.AuthenticationFailed, chacha20poly1305Open(out[0..], "", data[0..], key, bad_nonce));
869 }974 }
870}975}
871976
872test "crypto.xchacha20" {977test "crypto.xchacha20" {
873 const key = [_]u8{69} ** 32;978 const key = [_]u8{69} ** 32;
874 const nonce = [_]u8{42} ** 24;979 const nonce = [_]u8{42} ** 24;
875 const input = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";980 const m = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";
876 {981 {
877 var ciphertext: [input.len]u8 = undefined;982 var c: [m.len]u8 = undefined;
878 XChaCha20IETF.xor(ciphertext[0..], input[0..], 0, key, nonce);983 XChaCha20IETF.xor(c[0..], m[0..], 0, key, nonce);
879 var buf: [2 * ciphertext.len]u8 = undefined;984 var buf: [2 * c.len]u8 = undefined;
880 testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&ciphertext)}), "E0A1BCF939654AFDBDC1746EC49832647C19D891F0D1A81FC0C1703B4514BDEA584B512F6908C2C5E9DD18D5CBC1805DE5803FE3B9CA5F193FB8359E91FAB0C3BB40309A292EB1CF49685C65C4A3ADF4F11DB0CD2B6B67FBC174BC2E860E8F769FD3565BBFAD1C845E05A0FED9BE167C240D");985 testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&c)}), "E0A1BCF939654AFDBDC1746EC49832647C19D891F0D1A81FC0C1703B4514BDEA584B512F6908C2C5E9DD18D5CBC1805DE5803FE3B9CA5F193FB8359E91FAB0C3BB40309A292EB1CF49685C65C4A3ADF4F11DB0CD2B6B67FBC174BC2E860E8F769FD3565BBFAD1C845E05A0FED9BE167C240D");
881 }986 }
882 {987 {
883 const data = "Additional data";988 const ad = "Additional data";
884 var ciphertext: [input.len + xchacha20poly1305_tag_length]u8 = undefined;989 var c: [m.len + XChaCha20Poly1305.tag_length]u8 = undefined;
885 xchacha20poly1305Seal(ciphertext[0..], input, data, key, nonce);990 XChaCha20Poly1305.encrypt(c[0..m.len], c[m.len..], m, ad, nonce, key);
886 var out: [input.len]u8 = undefined;991 var out: [m.len]u8 = undefined;
887 try xchacha20poly1305Open(out[0..], ciphertext[0..], data, key, nonce);992 try XChaCha20Poly1305.decrypt(out[0..], c[0..m.len], c[m.len..].*, ad, nonce, key);
888 var buf: [2 * ciphertext.len]u8 = undefined;993 var buf: [2 * c.len]u8 = undefined;
889 testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&ciphertext)}), "994D2DD32333F48E53650C02C7A2ABB8E018B0836D7175AEC779F52E961780768F815C58F1AA52D211498DB89B9216763F569C9433A6BBFCEFB4D4A49387A4C5207FBB3B5A92B5941294DF30588C6740D39DC16FA1F0E634F7246CF7CDCB978E44347D89381B7A74EB7084F754B90BDE9AAF5A94B8F2A85EFD0B50692AE2D425E234");994 testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&c)}), "994D2DD32333F48E53650C02C7A2ABB8E018B0836D7175AEC779F52E961780768F815C58F1AA52D211498DB89B9216763F569C9433A6BBFCEFB4D4A49387A4C5207FBB3B5A92B5941294DF30588C6740D39DC16FA1F0E634F7246CF7CDCB978E44347D89381B7A74EB7084F754B90BDE9AAF5A94B8F2A85EFD0B50692AE2D425E234");
890 testing.expectEqualSlices(u8, out[0..], input);995 testing.expectEqualSlices(u8, out[0..], m);
891 ciphertext[0] += 1;996 c[0] += 1;
892 testing.expectError(error.AuthenticationFailed, xchacha20poly1305Open(out[0..], ciphertext[0..], data, key, nonce));997 testing.expectError(error.AuthenticationFailed, XChaCha20Poly1305.decrypt(out[0..], c[0..m.len], c[m.len..].*, ad, nonce, key));
893 }
894}
895
896pub const Chacha20Poly1305 = struct {
897 pub const tag_length = 16;
898 pub const nonce_length = 12;
899 pub const key_length = 32;
900
901 /// c: ciphertext: output buffer should be of size m.len
902 /// tag: authentication tag: output MAC
903 /// m: message
904 /// ad: Associated Data
905 /// npub: public nonce
906 /// k: private key
907 pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
908 assert(c.len == m.len);
909 return chacha20poly1305SealDetached(c, tag, m, ad, k, npub);
910 }
911
912 /// m: message: output buffer should be of size c.len
913 /// c: ciphertext
914 /// tag: authentication tag
915 /// ad: Associated Data
916 /// npub: public nonce
917 /// k: private key
918 /// NOTE: the check of the authentication tag is currently not done in constant time
919 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) Error!void {
920 assert(c.len == m.len);
921 return try chacha20poly1305OpenDetached(m, c, tag[0..], ad, k, npub);
922 }
923};
924
925pub const XChacha20Poly1305 = struct {
926 pub const tag_length = 16;
927 pub const nonce_length = 24;
928 pub const key_length = 32;
929
930 /// c: ciphertext: output buffer should be of size m.len
931 /// tag: authentication tag: output MAC
932 /// m: message
933 /// ad: Associated Data
934 /// npub: public nonce
935 /// k: private key
936 pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
937 assert(c.len == m.len);
938 return xchacha20poly1305SealDetached(c, tag, m, ad, k, npub);
939 }
940
941 /// m: message: output buffer should be of size c.len
942 /// c: ciphertext
943 /// tag: authentication tag
944 /// ad: Associated Data
945 /// npub: public nonce
946 /// k: private key
947 /// NOTE: the check of the authentication tag is currently not done in constant time
948 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) Error!void {
949 assert(c.len == m.len);
950 return try xchacha20poly1305OpenDetached(m, c, tag[0..], ad, k, npub);
951 }
952};
953
954test "chacha20 AEAD API" {
955 const aeads = [_]type{ Chacha20Poly1305, XChacha20Poly1305 };
956 const input = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";
957 const data = "Additional data";
958
959 inline for (aeads) |aead| {
960 const key = [_]u8{69} ** aead.key_length;
961 const nonce = [_]u8{42} ** aead.nonce_length;
962 var ciphertext: [input.len]u8 = undefined;
963 var tag: [aead.tag_length]u8 = undefined;
964 var out: [input.len]u8 = undefined;
965
966 aead.encrypt(ciphertext[0..], tag[0..], input, data, nonce, key);
967 try aead.decrypt(out[0..], ciphertext[0..], tag, data[0..], nonce, key);
968 testing.expectEqualSlices(u8, out[0..], input);
969 ciphertext[0] += 1;
970 testing.expectError(error.AuthenticationFailed, aead.decrypt(out[0..], ciphertext[0..], tag, data[0..], nonce, key));
971 }998 }
972}999}
lib/std/crypto/pbkdf2.zig+67-70
...@@ -20,20 +20,20 @@ const Error = std.crypto.Error;...@@ -20,20 +20,20 @@ const Error = std.crypto.Error;
20// pseudorandom function. See Appendix B.1 for further discussion.)20// pseudorandom function. See Appendix B.1 for further discussion.)
21// PBKDF2 is recommended for new applications.21// PBKDF2 is recommended for new applications.
22//22//
23// PBKDF2 (P, S, c, dkLen)23// PBKDF2 (P, S, c, dk_len)
24//24//
25// Options: PRF underlying pseudorandom function (hLen25// Options: PRF underlying pseudorandom function (h_len
26// denotes the length in octets of the26// denotes the length in octets of the
27// pseudorandom function output)27// pseudorandom function output)
28//28//
29// Input: P password, an octet string29// Input: P password, an octet string
30// S salt, an octet string30// S salt, an octet string
31// c iteration count, a positive integer31// c iteration count, a positive integer
32// dkLen intended length in octets of the derived32// dk_len intended length in octets of the derived
33// key, a positive integer, at most33// key, a positive integer, at most
34// (2^32 - 1) * hLen34// (2^32 - 1) * h_len
35//35//
36// Output: DK derived key, a dkLen-octet string36// Output: DK derived key, a dk_len-octet string
3737
38// Based on Apple's CommonKeyDerivation, based originally on code by Damien Bergamini.38// Based on Apple's CommonKeyDerivation, based originally on code by Damien Bergamini.
3939
...@@ -41,7 +41,7 @@ const Error = std.crypto.Error;...@@ -41,7 +41,7 @@ const Error = std.crypto.Error;
41///41///
42/// PBKDF2 is defined in RFC 2898, and is a recommendation of NIST SP 800-132.42/// PBKDF2 is defined in RFC 2898, and is a recommendation of NIST SP 800-132.
43///43///
44/// derivedKey: Slice of appropriate size for generated key. Generally 16 or 32 bytes in length.44/// dk: Slice of appropriate size for generated key. Generally 16 or 32 bytes in length.
45/// May be uninitialized. All bytes will be overwritten.45/// May be uninitialized. All bytes will be overwritten.
46/// Maximum size is `maxInt(u32) * Hash.digest_length`46/// Maximum size is `maxInt(u32) * Hash.digest_length`
47/// It is a programming error to pass buffer longer than the maximum size.47/// It is a programming error to pass buffer longer than the maximum size.
...@@ -52,43 +52,38 @@ const Error = std.crypto.Error;...@@ -52,43 +52,38 @@ const Error = std.crypto.Error;
52///52///
53/// rounds: Iteration count. Must be greater than 0. Common values range from 1,000 to 100,000.53/// rounds: Iteration count. Must be greater than 0. Common values range from 1,000 to 100,000.
54/// Larger iteration counts improve security by increasing the time required to compute54/// Larger iteration counts improve security by increasing the time required to compute
55/// the derivedKey. It is common to tune this parameter to achieve approximately 100ms.55/// the dk. It is common to tune this parameter to achieve approximately 100ms.
56///56///
57/// Prf: Pseudo-random function to use. A common choice is `std.crypto.auth.hmac.HmacSha256`.57/// Prf: Pseudo-random function to use. A common choice is `std.crypto.auth.hmac.HmacSha256`.
58pub fn pbkdf2(derivedKey: []u8, password: []const u8, salt: []const u8, rounds: u32, comptime Prf: type) Error!void {58pub fn pbkdf2(dk: []u8, password: []const u8, salt: []const u8, rounds: u32, comptime Prf: type) Error!void {
59 if (rounds < 1) return error.WeakParameters;59 if (rounds < 1) return error.WeakParameters;
6060
61 const dkLen = derivedKey.len;61 const dk_len = dk.len;
62 const hLen = Prf.mac_length;62 const h_len = Prf.mac_length;
63 comptime std.debug.assert(hLen >= 1);63 comptime std.debug.assert(h_len >= 1);
6464
65 // FromSpec:65 // FromSpec:
66 //66 //
67 // 1. If dkLen > maxInt(u32) * hLen, output "derived key too long" and67 // 1. If dk_len > maxInt(u32) * h_len, output "derived key too long" and
68 // stop.68 // stop.
69 //69 //
70 if (comptime (maxInt(usize) > maxInt(u32) * hLen) and (dkLen > @as(usize, maxInt(u32) * hLen))) {70 if (dk_len / h_len >= maxInt(u32)) {
71 // If maxInt(usize) is less than `maxInt(u32) * hLen` then dkLen is always inbounds71 // Counter starts at 1 and is 32 bit, so if we have to return more blocks, we would overflow
72 return error.OutputTooLong;72 return error.OutputTooLong;
73 }73 }
7474
75 // FromSpec:75 // FromSpec:
76 //76 //
77 // 2. Let l be the number of hLen-long blocks of bytes in the derived key,77 // 2. Let l be the number of h_len-long blocks of bytes in the derived key,
78 // rounding up, and let r be the number of bytes in the last78 // rounding up, and let r be the number of bytes in the last
79 // block79 // block
80 //80 //
8181
82 // l will not overflow, proof:82 const blocks_count = @intCast(u32, std.math.divCeil(usize, dk_len, h_len) catch unreachable);
83 // let `L(dkLen, hLen) = (dkLen + hLen - 1) / hLen`83 var r = dk_len % h_len;
84 // then `L^-1(l, hLen) = l*hLen - hLen + 1`84 if (r == 0) {
85 // 1) L^-1(maxInt(u32), hLen) <= maxInt(u32)*hLen85 r = h_len;
86 // 2) maxInt(u32)*hLen - hLen + 1 <= maxInt(u32)*hLen // subtract maxInt(u32)*hLen + 186 }
87 // 3) -hLen <= -1 // multiply by -1
88 // 4) hLen >= 1
89 const r_ = dkLen % hLen;
90 const l = @intCast(u32, (dkLen / hLen) + @as(u1, if (r_ == 0) 0 else 1)); // original: (dkLen + hLen - 1) / hLen
91 const r = if (r_ == 0) hLen else r_;
9287
93 // FromSpec:88 // FromSpec:
94 //89 //
...@@ -118,37 +113,38 @@ pub fn pbkdf2(derivedKey: []u8, password: []const u8, salt: []const u8, rounds:...@@ -118,37 +113,38 @@ pub fn pbkdf2(derivedKey: []u8, password: []const u8, salt: []const u8, rounds:
118 // Here, INT (i) is a four-octet encoding of the integer i, most113 // Here, INT (i) is a four-octet encoding of the integer i, most
119 // significant octet first.114 // significant octet first.
120 //115 //
121 // 4. Concatenate the blocks and extract the first dkLen octets to116 // 4. Concatenate the blocks and extract the first dk_len octets to
122 // produce a derived key DK:117 // produce a derived key DK:
123 //118 //
124 // DK = T_1 || T_2 || ... || T_l<0..r-1>119 // DK = T_1 || T_2 || ... || T_l<0..r-1>
125 var block: u32 = 0; // Spec limits to u32120
126 while (block < l) : (block += 1) {121 var block: u32 = 0;
127 var prevBlock: [hLen]u8 = undefined;122 while (block < blocks_count) : (block += 1) {
128 var newBlock: [hLen]u8 = undefined;123 var prev_block: [h_len]u8 = undefined;
124 var new_block: [h_len]u8 = undefined;
129125
130 // U_1 = PRF (P, S || INT (i))126 // U_1 = PRF (P, S || INT (i))
131 const blockIndex = mem.toBytes(mem.nativeToBig(u32, block + 1)); // Block index starts at 0001127 const block_index = mem.toBytes(mem.nativeToBig(u32, block + 1)); // Block index starts at 0001
132 var ctx = Prf.init(password);128 var ctx = Prf.init(password);
133 ctx.update(salt);129 ctx.update(salt);
134 ctx.update(blockIndex[0..]);130 ctx.update(block_index[0..]);
135 ctx.final(prevBlock[0..]);131 ctx.final(prev_block[0..]);
136132
137 // Choose portion of DK to write into (T_n) and initialize133 // Choose portion of DK to write into (T_n) and initialize
138 const offset = block * hLen;134 const offset = block * h_len;
139 const blockLen = if (block != l - 1) hLen else r;135 const block_len = if (block != blocks_count - 1) h_len else r;
140 const dkBlock: []u8 = derivedKey[offset..][0..blockLen];136 const dk_block: []u8 = dk[offset..][0..block_len];
141 mem.copy(u8, dkBlock, prevBlock[0..dkBlock.len]);137 mem.copy(u8, dk_block, prev_block[0..dk_block.len]);
142138
143 var i: u32 = 1;139 var i: u32 = 1;
144 while (i < rounds) : (i += 1) {140 while (i < rounds) : (i += 1) {
145 // U_c = PRF (P, U_{c-1})141 // U_c = PRF (P, U_{c-1})
146 Prf.create(&newBlock, prevBlock[0..], password);142 Prf.create(&new_block, prev_block[0..], password);
147 mem.copy(u8, prevBlock[0..], newBlock[0..]);143 mem.copy(u8, prev_block[0..], new_block[0..]);
148144
149 // F (P, S, c, i) = U_1 \xor U_2 \xor ... \xor U_c145 // F (P, S, c, i) = U_1 \xor U_2 \xor ... \xor U_c
150 for (dkBlock) |_, j| {146 for (dk_block) |_, j| {
151 dkBlock[j] ^= newBlock[j];147 dk_block[j] ^= new_block[j];
152 }148 }
153 }149 }
154 }150 }
...@@ -158,49 +154,50 @@ const htest = @import("test.zig");...@@ -158,49 +154,50 @@ const htest = @import("test.zig");
158const HmacSha1 = std.crypto.auth.hmac.HmacSha1;154const HmacSha1 = std.crypto.auth.hmac.HmacSha1;
159155
160// RFC 6070 PBKDF2 HMAC-SHA1 Test Vectors156// RFC 6070 PBKDF2 HMAC-SHA1 Test Vectors
157
161test "RFC 6070 one iteration" {158test "RFC 6070 one iteration" {
162 const p = "password";159 const p = "password";
163 const s = "salt";160 const s = "salt";
164 const c = 1;161 const c = 1;
165 const dkLen = 20;162 const dk_len = 20;
166163
167 var derivedKey: [dkLen]u8 = undefined;164 var dk: [dk_len]u8 = undefined;
168165
169 try pbkdf2(&derivedKey, p, s, c, HmacSha1);166 try pbkdf2(&dk, p, s, c, HmacSha1);
170167
171 const expected = "0c60c80f961f0e71f3a9b524af6012062fe037a6";168 const expected = "0c60c80f961f0e71f3a9b524af6012062fe037a6";
172169
173 htest.assertEqual(expected, derivedKey[0..]);170 htest.assertEqual(expected, dk[0..]);
174}171}
175172
176test "RFC 6070 two iterations" {173test "RFC 6070 two iterations" {
177 const p = "password";174 const p = "password";
178 const s = "salt";175 const s = "salt";
179 const c = 2;176 const c = 2;
180 const dkLen = 20;177 const dk_len = 20;
181178
182 var derivedKey: [dkLen]u8 = undefined;179 var dk: [dk_len]u8 = undefined;
183180
184 try pbkdf2(&derivedKey, p, s, c, HmacSha1);181 try pbkdf2(&dk, p, s, c, HmacSha1);
185182
186 const expected = "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957";183 const expected = "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957";
187184
188 htest.assertEqual(expected, derivedKey[0..]);185 htest.assertEqual(expected, dk[0..]);
189}186}
190187
191test "RFC 6070 4096 iterations" {188test "RFC 6070 4096 iterations" {
192 const p = "password";189 const p = "password";
193 const s = "salt";190 const s = "salt";
194 const c = 4096;191 const c = 4096;
195 const dkLen = 20;192 const dk_len = 20;
196193
197 var derivedKey: [dkLen]u8 = undefined;194 var dk: [dk_len]u8 = undefined;
198195
199 try pbkdf2(&derivedKey, p, s, c, HmacSha1);196 try pbkdf2(&dk, p, s, c, HmacSha1);
200197
201 const expected = "4b007901b765489abead49d926f721d065a429c1";198 const expected = "4b007901b765489abead49d926f721d065a429c1";
202199
203 htest.assertEqual(expected, derivedKey[0..]);200 htest.assertEqual(expected, dk[0..]);
204}201}
205202
206test "RFC 6070 16,777,216 iterations" {203test "RFC 6070 16,777,216 iterations" {
...@@ -212,48 +209,48 @@ test "RFC 6070 16,777,216 iterations" {...@@ -212,48 +209,48 @@ test "RFC 6070 16,777,216 iterations" {
212 const p = "password";209 const p = "password";
213 const s = "salt";210 const s = "salt";
214 const c = 16777216;211 const c = 16777216;
215 const dkLen = 20;212 const dk_len = 20;
216213
217 var derivedKey = [_]u8{0} ** dkLen;214 var dk = [_]u8{0} ** dk_len;
218215
219 try pbkdf2(&derivedKey, p, s, c, HmacSha1);216 try pbkdf2(&dk, p, s, c, HmacSha1);
220217
221 const expected = "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984";218 const expected = "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984";
222219
223 htest.assertEqual(expected, derivedKey[0..]);220 htest.assertEqual(expected, dk[0..]);
224}221}
225222
226test "RFC 6070 multi-block salt and password" {223test "RFC 6070 multi-block salt and password" {
227 const p = "passwordPASSWORDpassword";224 const p = "passwordPASSWORDpassword";
228 const s = "saltSALTsaltSALTsaltSALTsaltSALTsalt";225 const s = "saltSALTsaltSALTsaltSALTsaltSALTsalt";
229 const c = 4096;226 const c = 4096;
230 const dkLen = 25;227 const dk_len = 25;
231228
232 var derivedKey: [dkLen]u8 = undefined;229 var dk: [dk_len]u8 = undefined;
233230
234 try pbkdf2(&derivedKey, p, s, c, HmacSha1);231 try pbkdf2(&dk, p, s, c, HmacSha1);
235232
236 const expected = "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038";233 const expected = "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038";
237234
238 htest.assertEqual(expected, derivedKey[0..]);235 htest.assertEqual(expected, dk[0..]);
239}236}
240237
241test "RFC 6070 embedded NUL" {238test "RFC 6070 embedded NUL" {
242 const p = "pass\x00word";239 const p = "pass\x00word";
243 const s = "sa\x00lt";240 const s = "sa\x00lt";
244 const c = 4096;241 const c = 4096;
245 const dkLen = 16;242 const dk_len = 16;
246243
247 var derivedKey: [dkLen]u8 = undefined;244 var dk: [dk_len]u8 = undefined;
248245
249 try pbkdf2(&derivedKey, p, s, c, HmacSha1);246 try pbkdf2(&dk, p, s, c, HmacSha1);
250247
251 const expected = "56fa6aa75548099dcc37d7f03425e0c3";248 const expected = "56fa6aa75548099dcc37d7f03425e0c3";
252249
253 htest.assertEqual(expected, derivedKey[0..]);250 htest.assertEqual(expected, dk[0..]);
254}251}
255252
256test "Very large dkLen" {253test "Very large dk_len" {
257 // This test allocates 8GB of memory and is expected to take several hours to run.254 // This test allocates 8GB of memory and is expected to take several hours to run.
258 if (true) {255 if (true) {
259 return error.SkipZigTest;256 return error.SkipZigTest;
...@@ -261,13 +258,13 @@ test "Very large dkLen" {...@@ -261,13 +258,13 @@ test "Very large dkLen" {
261 const p = "password";258 const p = "password";
262 const s = "salt";259 const s = "salt";
263 const c = 1;260 const c = 1;
264 const dkLen = 1 << 33;261 const dk_len = 1 << 33;
265262
266 var derivedKey = try std.testing.allocator.alloc(u8, dkLen);263 var dk = try std.testing.allocator.alloc(u8, dk_len);
267 defer {264 defer {
268 std.testing.allocator.free(derivedKey);265 std.testing.allocator.free(dk);
269 }266 }
270267
271 try pbkdf2(derivedKey, p, s, c, HmacSha1);
272 // Just verify this doesn't crash with an overflow268 // Just verify this doesn't crash with an overflow
269 try pbkdf2(dk, p, s, c, HmacSha1);
273}270}
lib/std/debug.zig-18
...@@ -250,24 +250,6 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c...@@ -250,24 +250,6 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c
250 resetSegfaultHandler();250 resetSegfaultHandler();
251 }251 }
252252
253 if (comptime std.Target.current.isDarwin() and std.Target.current.cpu.arch == .aarch64)
254 nosuspend {
255 // As a workaround for not having threadlocal variable support in LLD for this target,
256 // we have a simpler panic implementation that does not use threadlocal variables.
257 // TODO https://github.com/ziglang/zig/issues/7527
258 const stderr = io.getStdErr().writer();
259 if (@atomicRmw(u8, &panicking, .Add, 1, .SeqCst) == 0) {
260 stderr.print("panic: " ++ format ++ "\n", args) catch os.abort();
261 if (trace) |t| {
262 dumpStackTrace(t.*);
263 }
264 dumpCurrentStackTrace(first_trace_addr);
265 } else {
266 stderr.print("Panicked during a panic. Aborting.\n", .{}) catch os.abort();
267 }
268 os.abort();
269 };
270
271 nosuspend switch (panic_stage) {253 nosuspend switch (panic_stage) {
272 0 => {254 0 => {
273 panic_stage = 1;255 panic_stage = 1;
lib/std/enums.zig created+1281
...@@ -0,0 +1,1281 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2021 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6
7//! This module contains utilities and data structures for working with enums.
8
9const std = @import("std.zig");
10const assert = std.debug.assert;
11const testing = std.testing;
12const EnumField = std.builtin.TypeInfo.EnumField;
13
14/// Returns a struct with a field matching each unique named enum element.
15/// If the enum is extern and has multiple names for the same value, only
16/// the first name is used. Each field is of type Data and has the provided
17/// default, which may be undefined.
18pub fn EnumFieldStruct(comptime E: type, comptime Data: type, comptime field_default: ?Data) type {
19 const StructField = std.builtin.TypeInfo.StructField;
20 var fields: []const StructField = &[_]StructField{};
21 for (uniqueFields(E)) |field, i| {
22 fields = fields ++ &[_]StructField{.{
23 .name = field.name,
24 .field_type = Data,
25 .default_value = field_default,
26 .is_comptime = false,
27 .alignment = if (@sizeOf(Data) > 0) @alignOf(Data) else 0,
28 }};
29 }
30 return @Type(.{ .Struct = .{
31 .layout = .Auto,
32 .fields = fields,
33 .decls = &[_]std.builtin.TypeInfo.Declaration{},
34 .is_tuple = false,
35 }});
36}
37
38/// Looks up the supplied fields in the given enum type.
39/// Uses only the field names, field values are ignored.
40/// The result array is in the same order as the input.
41pub fn valuesFromFields(comptime E: type, comptime fields: []const EnumField) []const E {
42 comptime {
43 var result: [fields.len]E = undefined;
44 for (fields) |f, i| {
45 result[i] = @field(E, f.name);
46 }
47 return &result;
48 }
49}
50
51test "std.enums.valuesFromFields" {
52 const E = extern enum { a, b, c, d = 0 };
53 const fields = valuesFromFields(E, &[_]EnumField{
54 .{ .name = "b", .value = undefined },
55 .{ .name = "a", .value = undefined },
56 .{ .name = "a", .value = undefined },
57 .{ .name = "d", .value = undefined },
58 });
59 testing.expectEqual(E.b, fields[0]);
60 testing.expectEqual(E.a, fields[1]);
61 testing.expectEqual(E.d, fields[2]); // a == d
62 testing.expectEqual(E.d, fields[3]);
63}
64
65/// Returns the set of all named values in the given enum, in
66/// declaration order.
67pub fn values(comptime E: type) []const E {
68 return comptime valuesFromFields(E, @typeInfo(E).Enum.fields);
69}
70
71test "std.enum.values" {
72 const E = extern enum { a, b, c, d = 0 };
73 testing.expectEqualSlices(E, &.{.a, .b, .c, .d}, values(E));
74}
75
76/// Returns the set of all unique named values in the given enum, in
77/// declaration order. For repeated values in extern enums, only the
78/// first name for each value is included.
79pub fn uniqueValues(comptime E: type) []const E {
80 return comptime valuesFromFields(E, uniqueFields(E));
81}
82
83test "std.enum.uniqueValues" {
84 const E = extern enum { a, b, c, d = 0, e, f = 3 };
85 testing.expectEqualSlices(E, &.{.a, .b, .c, .f}, uniqueValues(E));
86
87 const F = enum { a, b, c };
88 testing.expectEqualSlices(F, &.{.a, .b, .c}, uniqueValues(F));
89}
90
91/// Returns the set of all unique field values in the given enum, in
92/// declaration order. For repeated values in extern enums, only the
93/// first name for each value is included.
94pub fn uniqueFields(comptime E: type) []const EnumField {
95 comptime {
96 const info = @typeInfo(E).Enum;
97 const raw_fields = info.fields;
98 // Only extern enums can contain duplicates,
99 // so fast path other types.
100 if (info.layout != .Extern) {
101 return raw_fields;
102 }
103
104 var unique_fields: []const EnumField = &[_]EnumField{};
105 outer:
106 for (raw_fields) |candidate| {
107 for (unique_fields) |u| {
108 if (u.value == candidate.value)
109 continue :outer;
110 }
111 unique_fields = unique_fields ++ &[_]EnumField{candidate};
112 }
113
114 return unique_fields;
115 }
116}
117
118/// Determines the length of a direct-mapped enum array, indexed by
119/// @intCast(usize, @enumToInt(enum_value)). The enum must be exhaustive.
120/// If the enum contains any fields with values that cannot be represented
121/// by usize, a compile error is issued. The max_unused_slots parameter limits
122/// the total number of items which have no matching enum key (holes in the enum
123/// numbering). So for example, if an enum has values 1, 2, 5, and 6, max_unused_slots
124/// must be at least 3, to allow unused slots 0, 3, and 4.
125fn directEnumArrayLen(comptime E: type, comptime max_unused_slots: comptime_int) comptime_int {
126 const info = @typeInfo(E).Enum;
127 if (!info.is_exhaustive) {
128 @compileError("Cannot create direct array of non-exhaustive enum "++@typeName(E));
129 }
130
131 var max_value: comptime_int = -1;
132 const max_usize: comptime_int = ~@as(usize, 0);
133 const fields = uniqueFields(E);
134 for (fields) |f| {
135 if (f.value < 0) {
136 @compileError("Cannot create a direct enum array for "++@typeName(E)++", field ."++f.name++" has a negative value.");
137 }
138 if (f.value > max_value) {
139 if (f.value > max_usize) {
140 @compileError("Cannot create a direct enum array for "++@typeName(E)++", field ."++f.name++" is larger than the max value of usize.");
141 }
142 max_value = f.value;
143 }
144 }
145
146 const unused_slots = max_value + 1 - fields.len;
147 if (unused_slots > max_unused_slots) {
148 const unused_str = std.fmt.comptimePrint("{d}", .{unused_slots});
149 const allowed_str = std.fmt.comptimePrint("{d}", .{max_unused_slots});
150 @compileError("Cannot create a direct enum array for "++@typeName(E)++". It would have "++unused_str++" unused slots, but only "++allowed_str++" are allowed.");
151 }
152
153 return max_value + 1;
154}
155
156/// Initializes an array of Data which can be indexed by
157/// @intCast(usize, @enumToInt(enum_value)). The enum must be exhaustive.
158/// If the enum contains any fields with values that cannot be represented
159/// by usize, a compile error is issued. The max_unused_slots parameter limits
160/// the total number of items which have no matching enum key (holes in the enum
161/// numbering). So for example, if an enum has values 1, 2, 5, and 6, max_unused_slots
162/// must be at least 3, to allow unused slots 0, 3, and 4.
163/// The init_values parameter must be a struct with field names that match the enum values.
164/// If the enum has multiple fields with the same value, the name of the first one must
165/// be used.
166pub fn directEnumArray(
167 comptime E: type,
168 comptime Data: type,
169 comptime max_unused_slots: comptime_int,
170 init_values: EnumFieldStruct(E, Data, null),
171) [directEnumArrayLen(E, max_unused_slots)]Data {
172 return directEnumArrayDefault(E, Data, null, max_unused_slots, init_values);
173}
174
175test "std.enums.directEnumArray" {
176 const E = enum(i4) { a = 4, b = 6, c = 2 };
177 var runtime_false: bool = false;
178 const array = directEnumArray(E, bool, 4, .{
179 .a = true,
180 .b = runtime_false,
181 .c = true,
182 });
183
184 testing.expectEqual([7]bool, @TypeOf(array));
185 testing.expectEqual(true, array[4]);
186 testing.expectEqual(false, array[6]);
187 testing.expectEqual(true, array[2]);
188}
189
190/// Initializes an array of Data which can be indexed by
191/// @intCast(usize, @enumToInt(enum_value)). The enum must be exhaustive.
192/// If the enum contains any fields with values that cannot be represented
193/// by usize, a compile error is issued. The max_unused_slots parameter limits
194/// the total number of items which have no matching enum key (holes in the enum
195/// numbering). So for example, if an enum has values 1, 2, 5, and 6, max_unused_slots
196/// must be at least 3, to allow unused slots 0, 3, and 4.
197/// The init_values parameter must be a struct with field names that match the enum values.
198/// If the enum has multiple fields with the same value, the name of the first one must
199/// be used.
200pub fn directEnumArrayDefault(
201 comptime E: type,
202 comptime Data: type,
203 comptime default: ?Data,
204 comptime max_unused_slots: comptime_int,
205 init_values: EnumFieldStruct(E, Data, default),
206) [directEnumArrayLen(E, max_unused_slots)]Data {
207 const len = comptime directEnumArrayLen(E, max_unused_slots);
208 var result: [len]Data = if (default) |d| [_]Data{d} ** len else undefined;
209 inline for (@typeInfo(@TypeOf(init_values)).Struct.fields) |f, i| {
210 const enum_value = @field(E, f.name);
211 const index = @intCast(usize, @enumToInt(enum_value));
212 result[index] = @field(init_values, f.name);
213 }
214 return result;
215}
216
217test "std.enums.directEnumArrayDefault" {
218 const E = enum(i4) { a = 4, b = 6, c = 2 };
219 var runtime_false: bool = false;
220 const array = directEnumArrayDefault(E, bool, false, 4, .{
221 .a = true,
222 .b = runtime_false,
223 });
224
225 testing.expectEqual([7]bool, @TypeOf(array));
226 testing.expectEqual(true, array[4]);
227 testing.expectEqual(false, array[6]);
228 testing.expectEqual(false, array[2]);
229}
230
231/// Cast an enum literal, value, or string to the enum value of type E
232/// with the same name.
233pub fn nameCast(comptime E: type, comptime value: anytype) E {
234 comptime {
235 const V = @TypeOf(value);
236 if (V == E) return value;
237 var name: ?[]const u8 = switch (@typeInfo(V)) {
238 .EnumLiteral, .Enum => @tagName(value),
239 .Pointer => if (std.meta.trait.isZigString(V)) value else null,
240 else => null,
241 };
242 if (name) |n| {
243 if (@hasField(E, n)) {
244 return @field(E, n);
245 }
246 @compileError("Enum "++@typeName(E)++" has no field named "++n);
247 }
248 @compileError("Cannot cast from "++@typeName(@TypeOf(value))++" to "++@typeName(E));
249 }
250}
251
252test "std.enums.nameCast" {
253 const A = enum { a = 0, b = 1 };
254 const B = enum { a = 1, b = 0 };
255 testing.expectEqual(A.a, nameCast(A, .a));
256 testing.expectEqual(A.a, nameCast(A, A.a));
257 testing.expectEqual(A.a, nameCast(A, B.a));
258 testing.expectEqual(A.a, nameCast(A, "a"));
259 testing.expectEqual(A.a, nameCast(A, @as(*const[1]u8, "a")));
260 testing.expectEqual(A.a, nameCast(A, @as([:0]const u8, "a")));
261 testing.expectEqual(A.a, nameCast(A, @as([]const u8, "a")));
262
263 testing.expectEqual(B.a, nameCast(B, .a));
264 testing.expectEqual(B.a, nameCast(B, A.a));
265 testing.expectEqual(B.a, nameCast(B, B.a));
266 testing.expectEqual(B.a, nameCast(B, "a"));
267
268 testing.expectEqual(B.b, nameCast(B, .b));
269 testing.expectEqual(B.b, nameCast(B, A.b));
270 testing.expectEqual(B.b, nameCast(B, B.b));
271 testing.expectEqual(B.b, nameCast(B, "b"));
272}
273
274/// A set of enum elements, backed by a bitfield. If the enum
275/// is not dense, a mapping will be constructed from enum values
276/// to dense indices. This type does no dynamic allocation and
277/// can be copied by value.
278pub fn EnumSet(comptime E: type) type {
279 const mixin = struct {
280 fn EnumSetExt(comptime Self: type) type {
281 const Indexer = Self.Indexer;
282 return struct {
283 /// Initializes the set using a struct of bools
284 pub fn init(init_values: EnumFieldStruct(E, bool, false)) Self {
285 var result = Self{};
286 comptime var i: usize = 0;
287 inline while (i < Self.len) : (i += 1) {
288 comptime const key = Indexer.keyForIndex(i);
289 comptime const tag = @tagName(key);
290 if (@field(init_values, tag)) {
291 result.bits.set(i);
292 }
293 }
294 return result;
295 }
296 };
297 }
298 };
299 return IndexedSet(EnumIndexer(E), mixin.EnumSetExt);
300}
301
302/// A map keyed by an enum, backed by a bitfield and a dense array.
303/// If the enum is not dense, a mapping will be constructed from
304/// enum values to dense indices. This type does no dynamic
305/// allocation and can be copied by value.
306pub fn EnumMap(comptime E: type, comptime V: type) type {
307 const mixin = struct {
308 fn EnumMapExt(comptime Self: type) type {
309 const Indexer = Self.Indexer;
310 return struct {
311 /// Initializes the map using a sparse struct of optionals
312 pub fn init(init_values: EnumFieldStruct(E, ?V, @as(?V, null))) Self {
313 var result = Self{};
314 comptime var i: usize = 0;
315 inline while (i < Self.len) : (i += 1) {
316 comptime const key = Indexer.keyForIndex(i);
317 comptime const tag = @tagName(key);
318 if (@field(init_values, tag)) |*v| {
319 result.bits.set(i);
320 result.values[i] = v.*;
321 }
322 }
323 return result;
324 }
325 /// Initializes a full mapping with all keys set to value.
326 /// Consider using EnumArray instead if the map will remain full.
327 pub fn initFull(value: V) Self {
328 var result = Self{
329 .bits = Self.BitSet.initFull(),
330 .values = undefined,
331 };
332 std.mem.set(V, &result.values, value);
333 return result;
334 }
335 /// Initializes a full mapping with supplied values.
336 /// Consider using EnumArray instead if the map will remain full.
337 pub fn initFullWith(init_values: EnumFieldStruct(E, V, @as(?V, null))) Self {
338 return initFullWithDefault(@as(?V, null), init_values);
339 }
340 /// Initializes a full mapping with a provided default.
341 /// Consider using EnumArray instead if the map will remain full.
342 pub fn initFullWithDefault(comptime default: ?V, init_values: EnumFieldStruct(E, V, default)) Self {
343 var result = Self{
344 .bits = Self.BitSet.initFull(),
345 .values = undefined,
346 };
347 comptime var i: usize = 0;
348 inline while (i < Self.len) : (i += 1) {
349 comptime const key = Indexer.keyForIndex(i);
350 comptime const tag = @tagName(key);
351 result.values[i] = @field(init_values, tag);
352 }
353 return result;
354 }
355 };
356 }
357 };
358 return IndexedMap(EnumIndexer(E), V, mixin.EnumMapExt);
359}
360
361/// An array keyed by an enum, backed by a dense array.
362/// If the enum is not dense, a mapping will be constructed from
363/// enum values to dense indices. This type does no dynamic
364/// allocation and can be copied by value.
365pub fn EnumArray(comptime E: type, comptime V: type) type {
366 const mixin = struct {
367 fn EnumArrayExt(comptime Self: type) type {
368 const Indexer = Self.Indexer;
369 return struct {
370 /// Initializes all values in the enum array
371 pub fn init(init_values: EnumFieldStruct(E, V, @as(?V, null))) Self {
372 return initDefault(@as(?V, null), init_values);
373 }
374
375 /// Initializes values in the enum array, with the specified default.
376 pub fn initDefault(comptime default: ?V, init_values: EnumFieldStruct(E, V, default)) Self {
377 var result = Self{ .values = undefined };
378 comptime var i: usize = 0;
379 inline while (i < Self.len) : (i += 1) {
380 const key = comptime Indexer.keyForIndex(i);
381 const tag = @tagName(key);
382 result.values[i] = @field(init_values, tag);
383 }
384 return result;
385 }
386 };
387 }
388 };
389 return IndexedArray(EnumIndexer(E), V, mixin.EnumArrayExt);
390}
391
392/// Pass this function as the Ext parameter to Indexed* if you
393/// do not want to attach any extensions. This parameter was
394/// originally an optional, but optional generic functions
395/// seem to be broken at the moment.
396/// TODO: Once #8169 is fixed, consider switching this param
397/// back to an optional.
398pub fn NoExtension(comptime Self: type) type {
399 return NoExt;
400}
401const NoExt = struct{};
402
403/// A set type with an Indexer mapping from keys to indices.
404/// Presence or absence is stored as a dense bitfield. This
405/// type does no allocation and can be copied by value.
406pub fn IndexedSet(comptime I: type, comptime Ext: fn(type)type) type {
407 comptime ensureIndexer(I);
408 return struct {
409 const Self = @This();
410
411 pub usingnamespace Ext(Self);
412
413 /// The indexing rules for converting between keys and indices.
414 pub const Indexer = I;
415 /// The element type for this set.
416 pub const Key = Indexer.Key;
417
418 const BitSet = std.StaticBitSet(Indexer.count);
419
420 /// The maximum number of items in this set.
421 pub const len = Indexer.count;
422
423 bits: BitSet = BitSet.initEmpty(),
424
425 /// Returns a set containing all possible keys.
426 pub fn initFull() Self {
427 return .{ .bits = BitSet.initFull() };
428 }
429
430 /// Returns the number of keys in the set.
431 pub fn count(self: Self) usize {
432 return self.bits.count();
433 }
434
435 /// Checks if a key is in the set.
436 pub fn contains(self: Self, key: Key) bool {
437 return self.bits.isSet(Indexer.indexOf(key));
438 }
439
440 /// Puts a key in the set.
441 pub fn insert(self: *Self, key: Key) void {
442 self.bits.set(Indexer.indexOf(key));
443 }
444
445 /// Removes a key from the set.
446 pub fn remove(self: *Self, key: Key) void {
447 self.bits.unset(Indexer.indexOf(key));
448 }
449
450 /// Changes the presence of a key in the set to match the passed bool.
451 pub fn setPresent(self: *Self, key: Key, present: bool) void {
452 self.bits.setValue(Indexer.indexOf(key), present);
453 }
454
455 /// Toggles the presence of a key in the set. If the key is in
456 /// the set, removes it. Otherwise adds it.
457 pub fn toggle(self: *Self, key: Key) void {
458 self.bits.toggle(Indexer.indexOf(key));
459 }
460
461 /// Toggles the presence of all keys in the passed set.
462 pub fn toggleSet(self: *Self, other: Self) void {
463 self.bits.toggleSet(other.bits);
464 }
465
466 /// Toggles all possible keys in the set.
467 pub fn toggleAll(self: *Self) void {
468 self.bits.toggleAll();
469 }
470
471 /// Adds all keys in the passed set to this set.
472 pub fn setUnion(self: *Self, other: Self) void {
473 self.bits.setUnion(other.bits);
474 }
475
476 /// Removes all keys which are not in the passed set.
477 pub fn setIntersection(self: *Self, other: Self) void {
478 self.bits.setIntersection(other.bits);
479 }
480
481 /// Returns an iterator over this set, which iterates in
482 /// index order. Modifications to the set during iteration
483 /// may or may not be observed by the iterator, but will
484 /// not invalidate it.
485 pub fn iterator(self: *Self) Iterator {
486 return .{ .inner = self.bits.iterator(.{}) };
487 }
488
489 pub const Iterator = struct {
490 inner: BitSet.Iterator(.{}),
491
492 pub fn next(self: *Iterator) ?Key {
493 return if (self.inner.next()) |index|
494 Indexer.keyForIndex(index)
495 else null;
496 }
497 };
498 };
499}
500
501/// A map from keys to values, using an index lookup. Uses a
502/// bitfield to track presence and a dense array of values.
503/// This type does no allocation and can be copied by value.
504pub fn IndexedMap(comptime I: type, comptime V: type, comptime Ext: fn(type)type) type {
505 comptime ensureIndexer(I);
506 return struct {
507 const Self = @This();
508
509 pub usingnamespace Ext(Self);
510
511 /// The index mapping for this map
512 pub const Indexer = I;
513 /// The key type used to index this map
514 pub const Key = Indexer.Key;
515 /// The value type stored in this map
516 pub const Value = V;
517 /// The number of possible keys in the map
518 pub const len = Indexer.count;
519
520 const BitSet = std.StaticBitSet(Indexer.count);
521
522 /// Bits determining whether items are in the map
523 bits: BitSet = BitSet.initEmpty(),
524 /// Values of items in the map. If the associated
525 /// bit is zero, the value is undefined.
526 values: [Indexer.count]Value = undefined,
527
528 /// The number of items in the map.
529 pub fn count(self: Self) usize {
530 return self.bits.count();
531 }
532
533 /// Checks if the map contains an item.
534 pub fn contains(self: Self, key: Key) bool {
535 return self.bits.isSet(Indexer.indexOf(key));
536 }
537
538 /// Gets the value associated with a key.
539 /// If the key is not in the map, returns null.
540 pub fn get(self: Self, key: Key) ?Value {
541 const index = Indexer.indexOf(key);
542 return if (self.bits.isSet(index)) self.values[index] else null;
543 }
544
545 /// Gets the value associated with a key, which must
546 /// exist in the map.
547 pub fn getAssertContains(self: Self, key: Key) Value {
548 const index = Indexer.indexOf(key);
549 assert(self.bits.isSet(index));
550 return self.values[index];
551 }
552
553 /// Gets the address of the value associated with a key.
554 /// If the key is not in the map, returns null.
555 pub fn getPtr(self: *Self, key: Key) ?*Value {
556 const index = Indexer.indexOf(key);
557 return if (self.bits.isSet(index)) &self.values[index] else null;
558 }
559
560 /// Gets the address of the const value associated with a key.
561 /// If the key is not in the map, returns null.
562 pub fn getPtrConst(self: *const Self, key: Key) ?*const Value {
563 const index = Indexer.indexOf(key);
564 return if (self.bits.isSet(index)) &self.values[index] else null;
565 }
566
567 /// Gets the address of the value associated with a key.
568 /// The key must be present in the map.
569 pub fn getPtrAssertContains(self: *Self, key: Key) *Value {
570 const index = Indexer.indexOf(key);
571 assert(self.bits.isSet(index));
572 return &self.values[index];
573 }
574
575 /// Adds the key to the map with the supplied value.
576 /// If the key is already in the map, overwrites the value.
577 pub fn put(self: *Self, key: Key, value: Value) void {
578 const index = Indexer.indexOf(key);
579 self.bits.set(index);
580 self.values[index] = value;
581 }
582
583 /// Adds the key to the map with an undefined value.
584 /// If the key is already in the map, the value becomes undefined.
585 /// A pointer to the value is returned, which should be
586 /// used to initialize the value.
587 pub fn putUninitialized(self: *Self, key: Key) *Value {
588 const index = Indexer.indexOf(key);
589 self.bits.set(index);
590 self.values[index] = undefined;
591 return &self.values[index];
592 }
593
594 /// Sets the value associated with the key in the map,
595 /// and returns the old value. If the key was not in
596 /// the map, returns null.
597 pub fn fetchPut(self: *Self, key: Key, value: Value) ?Value {
598 const index = Indexer.indexOf(key);
599 const result: ?Value = if (self.bits.isSet(index)) self.values[index] else null;
600 self.bits.set(index);
601 self.values[index] = value;
602 return result;
603 }
604
605 /// Removes a key from the map. If the key was not in the map,
606 /// does nothing.
607 pub fn remove(self: *Self, key: Key) void {
608 const index = Indexer.indexOf(key);
609 self.bits.unset(index);
610 self.values[index] = undefined;
611 }
612
613 /// Removes a key from the map, and returns the old value.
614 /// If the key was not in the map, returns null.
615 pub fn fetchRemove(self: *Self, key: Key) ?Value {
616 const index = Indexer.indexOf(key);
617 const result: ?Value = if (self.bits.isSet(index)) self.values[index] else null;
618 self.bits.unset(index);
619 self.values[index] = undefined;
620 return result;
621 }
622
623 /// Returns an iterator over the map, which visits items in index order.
624 /// Modifications to the underlying map may or may not be observed by
625 /// the iterator, but will not invalidate it.
626 pub fn iterator(self: *Self) Iterator {
627 return .{
628 .inner = self.bits.iterator(.{}),
629 .values = &self.values,
630 };
631 }
632
633 /// An entry in the map.
634 pub const Entry = struct {
635 /// The key associated with this entry.
636 /// Modifying this key will not change the map.
637 key: Key,
638
639 /// A pointer to the value in the map associated
640 /// with this key. Modifications through this
641 /// pointer will modify the underlying data.
642 value: *Value,
643 };
644
645 pub const Iterator = struct {
646 inner: BitSet.Iterator(.{}),
647 values: *[Indexer.count]Value,
648
649 pub fn next(self: *Iterator) ?Entry {
650 return if (self.inner.next()) |index|
651 Entry{
652 .key = Indexer.keyForIndex(index),
653 .value = &self.values[index],
654 }
655 else null;
656 }
657 };
658 };
659}
660
661/// A dense array of values, using an indexed lookup.
662/// This type does no allocation and can be copied by value.
663pub fn IndexedArray(comptime I: type, comptime V: type, comptime Ext: fn(type)type) type {
664 comptime ensureIndexer(I);
665 return struct {
666 const Self = @This();
667
668 pub usingnamespace Ext(Self);
669
670 /// The index mapping for this map
671 pub const Indexer = I;
672 /// The key type used to index this map
673 pub const Key = Indexer.Key;
674 /// The value type stored in this map
675 pub const Value = V;
676 /// The number of possible keys in the map
677 pub const len = Indexer.count;
678
679 values: [Indexer.count]Value,
680
681 pub fn initUndefined() Self {
682 return Self{ .values = undefined };
683 }
684
685 pub fn initFill(v: Value) Self {
686 var self: Self = undefined;
687 std.mem.set(Value, &self.values, v);
688 return self;
689 }
690
691 /// Returns the value in the array associated with a key.
692 pub fn get(self: Self, key: Key) Value {
693 return self.values[Indexer.indexOf(key)];
694 }
695
696 /// Returns a pointer to the slot in the array associated with a key.
697 pub fn getPtr(self: *Self, key: Key) *Value {
698 return &self.values[Indexer.indexOf(key)];
699 }
700
701 /// Returns a const pointer to the slot in the array associated with a key.
702 pub fn getPtrConst(self: *const Self, key: Key) *const Value {
703 return &self.values[Indexer.indexOf(key)];
704 }
705
706 /// Sets the value in the slot associated with a key.
707 pub fn set(self: *Self, key: Key, value: Value) void {
708 self.values[Indexer.indexOf(key)] = value;
709 }
710
711 /// Iterates over the items in the array, in index order.
712 pub fn iterator(self: *Self) Iterator {
713 return .{
714 .values = &self.values,
715 };
716 }
717
718 /// An entry in the array.
719 pub const Entry = struct {
720 /// The key associated with this entry.
721 /// Modifying this key will not change the array.
722 key: Key,
723
724 /// A pointer to the value in the array associated
725 /// with this key. Modifications through this
726 /// pointer will modify the underlying data.
727 value: *Value,
728 };
729
730 pub const Iterator = struct {
731 index: usize = 0,
732 values: *[Indexer.count]Value,
733
734 pub fn next(self: *Iterator) ?Entry {
735 const index = self.index;
736 if (index < Indexer.count) {
737 self.index += 1;
738 return Entry{
739 .key = Indexer.keyForIndex(index),
740 .value = &self.values[index],
741 };
742 }
743 return null;
744 }
745 };
746 };
747}
748
749/// Verifies that a type is a valid Indexer, providing a helpful
750/// compile error if not. An Indexer maps a comptime known set
751/// of keys to a dense set of zero-based indices.
752/// The indexer interface must look like this:
753/// ```
754/// struct {
755/// /// The key type which this indexer converts to indices
756/// pub const Key: type,
757/// /// The number of indexes in the dense mapping
758/// pub const count: usize,
759/// /// Converts from a key to an index
760/// pub fn indexOf(Key) usize;
761/// /// Converts from an index to a key
762/// pub fn keyForIndex(usize) Key;
763/// }
764/// ```
765pub fn ensureIndexer(comptime T: type) void {
766 comptime {
767 if (!@hasDecl(T, "Key")) @compileError("Indexer must have decl Key: type.");
768 if (@TypeOf(T.Key) != type) @compileError("Indexer.Key must be a type.");
769 if (!@hasDecl(T, "count")) @compileError("Indexer must have decl count: usize.");
770 if (@TypeOf(T.count) != usize) @compileError("Indexer.count must be a usize.");
771 if (!@hasDecl(T, "indexOf")) @compileError("Indexer.indexOf must be a fn(Key)usize.");
772 if (@TypeOf(T.indexOf) != fn(T.Key)usize) @compileError("Indexer must have decl indexOf: fn(Key)usize.");
773 if (!@hasDecl(T, "keyForIndex")) @compileError("Indexer must have decl keyForIndex: fn(usize)Key.");
774 if (@TypeOf(T.keyForIndex) != fn(usize)T.Key) @compileError("Indexer.keyForIndex must be a fn(usize)Key.");
775 }
776}
777
778test "std.enums.ensureIndexer" {
779 ensureIndexer(struct {
780 pub const Key = u32;
781 pub const count: usize = 8;
782 pub fn indexOf(k: Key) usize {
783 return @intCast(usize, k);
784 }
785 pub fn keyForIndex(index: usize) Key {
786 return @intCast(Key, index);
787 }
788 });
789}
790
791fn ascByValue(ctx: void, comptime a: EnumField, comptime b: EnumField) bool {
792 return a.value < b.value;
793}
794pub fn EnumIndexer(comptime E: type) type {
795 if (!@typeInfo(E).Enum.is_exhaustive) {
796 @compileError("Cannot create an enum indexer for a non-exhaustive enum.");
797 }
798
799 const const_fields = uniqueFields(E);
800 var fields = const_fields[0..const_fields.len].*;
801 if (fields.len == 0) {
802 return struct {
803 pub const Key = E;
804 pub const count: usize = 0;
805 pub fn indexOf(e: E) usize { unreachable; }
806 pub fn keyForIndex(i: usize) E { unreachable; }
807 };
808 }
809 std.sort.sort(EnumField, &fields, {}, ascByValue);
810 const min = fields[0].value;
811 const max = fields[fields.len-1].value;
812 if (max - min == fields.len-1) {
813 return struct {
814 pub const Key = E;
815 pub const count = fields.len;
816 pub fn indexOf(e: E) usize {
817 return @intCast(usize, @enumToInt(e) - min);
818 }
819 pub fn keyForIndex(i: usize) E {
820 // TODO fix addition semantics. This calculation
821 // gives up some safety to avoid artificially limiting
822 // the range of signed enum values to max_isize.
823 const enum_value = if (min < 0) @bitCast(isize, i) +% min else i + min;
824 return @intToEnum(E, @intCast(std.meta.Tag(E), enum_value));
825 }
826 };
827 }
828
829 const keys = valuesFromFields(E, &fields);
830
831 return struct {
832 pub const Key = E;
833 pub const count = fields.len;
834 pub fn indexOf(e: E) usize {
835 for (keys) |k, i| {
836 if (k == e) return i;
837 }
838 unreachable;
839 }
840 pub fn keyForIndex(i: usize) E {
841 return keys[i];
842 }
843 };
844}
845
846test "std.enums.EnumIndexer dense zeroed" {
847 const E = enum{ b = 1, a = 0, c = 2 };
848 const Indexer = EnumIndexer(E);
849 ensureIndexer(Indexer);
850 testing.expectEqual(E, Indexer.Key);
851 testing.expectEqual(@as(usize, 3), Indexer.count);
852
853 testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a));
854 testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b));
855 testing.expectEqual(@as(usize, 2), Indexer.indexOf(.c));
856
857 testing.expectEqual(E.a, Indexer.keyForIndex(0));
858 testing.expectEqual(E.b, Indexer.keyForIndex(1));
859 testing.expectEqual(E.c, Indexer.keyForIndex(2));
860}
861
862test "std.enums.EnumIndexer dense positive" {
863 const E = enum(u4) { c = 6, a = 4, b = 5 };
864 const Indexer = EnumIndexer(E);
865 ensureIndexer(Indexer);
866 testing.expectEqual(E, Indexer.Key);
867 testing.expectEqual(@as(usize, 3), Indexer.count);
868
869 testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a));
870 testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b));
871 testing.expectEqual(@as(usize, 2), Indexer.indexOf(.c));
872
873 testing.expectEqual(E.a, Indexer.keyForIndex(0));
874 testing.expectEqual(E.b, Indexer.keyForIndex(1));
875 testing.expectEqual(E.c, Indexer.keyForIndex(2));
876}
877
878test "std.enums.EnumIndexer dense negative" {
879 const E = enum(i4) { a = -6, c = -4, b = -5 };
880 const Indexer = EnumIndexer(E);
881 ensureIndexer(Indexer);
882 testing.expectEqual(E, Indexer.Key);
883 testing.expectEqual(@as(usize, 3), Indexer.count);
884
885 testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a));
886 testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b));
887 testing.expectEqual(@as(usize, 2), Indexer.indexOf(.c));
888
889 testing.expectEqual(E.a, Indexer.keyForIndex(0));
890 testing.expectEqual(E.b, Indexer.keyForIndex(1));
891 testing.expectEqual(E.c, Indexer.keyForIndex(2));
892}
893
894test "std.enums.EnumIndexer sparse" {
895 const E = enum(i4) { a = -2, c = 6, b = 4 };
896 const Indexer = EnumIndexer(E);
897 ensureIndexer(Indexer);
898 testing.expectEqual(E, Indexer.Key);
899 testing.expectEqual(@as(usize, 3), Indexer.count);
900
901 testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a));
902 testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b));
903 testing.expectEqual(@as(usize, 2), Indexer.indexOf(.c));
904
905 testing.expectEqual(E.a, Indexer.keyForIndex(0));
906 testing.expectEqual(E.b, Indexer.keyForIndex(1));
907 testing.expectEqual(E.c, Indexer.keyForIndex(2));
908}
909
910test "std.enums.EnumIndexer repeats" {
911 const E = extern enum{ a = -2, c = 6, b = 4, b2 = 4 };
912 const Indexer = EnumIndexer(E);
913 ensureIndexer(Indexer);
914 testing.expectEqual(E, Indexer.Key);
915 testing.expectEqual(@as(usize, 3), Indexer.count);
916
917 testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a));
918 testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b));
919 testing.expectEqual(@as(usize, 2), Indexer.indexOf(.c));
920
921 testing.expectEqual(E.a, Indexer.keyForIndex(0));
922 testing.expectEqual(E.b, Indexer.keyForIndex(1));
923 testing.expectEqual(E.c, Indexer.keyForIndex(2));
924}
925
926test "std.enums.EnumSet" {
927 const E = extern enum { a, b, c, d, e = 0 };
928 const Set = EnumSet(E);
929 testing.expectEqual(E, Set.Key);
930 testing.expectEqual(EnumIndexer(E), Set.Indexer);
931 testing.expectEqual(@as(usize, 4), Set.len);
932
933 // Empty sets
934 const empty = Set{};
935 comptime testing.expect(empty.count() == 0);
936
937 var empty_b = Set.init(.{});
938 testing.expect(empty_b.count() == 0);
939
940 const empty_c = comptime Set.init(.{});
941 comptime testing.expect(empty_c.count() == 0);
942
943 const full = Set.initFull();
944 testing.expect(full.count() == Set.len);
945
946 const full_b = comptime Set.initFull();
947 comptime testing.expect(full_b.count() == Set.len);
948
949 testing.expectEqual(false, empty.contains(.a));
950 testing.expectEqual(false, empty.contains(.b));
951 testing.expectEqual(false, empty.contains(.c));
952 testing.expectEqual(false, empty.contains(.d));
953 testing.expectEqual(false, empty.contains(.e));
954 {
955 var iter = empty_b.iterator();
956 testing.expectEqual(@as(?E, null), iter.next());
957 }
958
959 var mut = Set.init(.{
960 .a=true, .c=true,
961 });
962 testing.expectEqual(@as(usize, 2), mut.count());
963 testing.expectEqual(true, mut.contains(.a));
964 testing.expectEqual(false, mut.contains(.b));
965 testing.expectEqual(true, mut.contains(.c));
966 testing.expectEqual(false, mut.contains(.d));
967 testing.expectEqual(true, mut.contains(.e)); // aliases a
968 {
969 var it = mut.iterator();
970 testing.expectEqual(@as(?E, .a), it.next());
971 testing.expectEqual(@as(?E, .c), it.next());
972 testing.expectEqual(@as(?E, null), it.next());
973 }
974
975 mut.toggleAll();
976 testing.expectEqual(@as(usize, 2), mut.count());
977 testing.expectEqual(false, mut.contains(.a));
978 testing.expectEqual(true, mut.contains(.b));
979 testing.expectEqual(false, mut.contains(.c));
980 testing.expectEqual(true, mut.contains(.d));
981 testing.expectEqual(false, mut.contains(.e)); // aliases a
982 {
983 var it = mut.iterator();
984 testing.expectEqual(@as(?E, .b), it.next());
985 testing.expectEqual(@as(?E, .d), it.next());
986 testing.expectEqual(@as(?E, null), it.next());
987 }
988
989 mut.toggleSet(Set.init(.{ .a=true, .b=true }));
990 testing.expectEqual(@as(usize, 2), mut.count());
991 testing.expectEqual(true, mut.contains(.a));
992 testing.expectEqual(false, mut.contains(.b));
993 testing.expectEqual(false, mut.contains(.c));
994 testing.expectEqual(true, mut.contains(.d));
995 testing.expectEqual(true, mut.contains(.e)); // aliases a
996
997 mut.setUnion(Set.init(.{ .a=true, .b=true }));
998 testing.expectEqual(@as(usize, 3), mut.count());
999 testing.expectEqual(true, mut.contains(.a));
1000 testing.expectEqual(true, mut.contains(.b));
1001 testing.expectEqual(false, mut.contains(.c));
1002 testing.expectEqual(true, mut.contains(.d));
1003
1004 mut.remove(.c);
1005 mut.remove(.b);
1006 testing.expectEqual(@as(usize, 2), mut.count());
1007 testing.expectEqual(true, mut.contains(.a));
1008 testing.expectEqual(false, mut.contains(.b));
1009 testing.expectEqual(false, mut.contains(.c));
1010 testing.expectEqual(true, mut.contains(.d));
1011
1012 mut.setIntersection(Set.init(.{ .a=true, .b=true }));
1013 testing.expectEqual(@as(usize, 1), mut.count());
1014 testing.expectEqual(true, mut.contains(.a));
1015 testing.expectEqual(false, mut.contains(.b));
1016 testing.expectEqual(false, mut.contains(.c));
1017 testing.expectEqual(false, mut.contains(.d));
1018
1019 mut.insert(.a);
1020 mut.insert(.b);
1021 testing.expectEqual(@as(usize, 2), mut.count());
1022 testing.expectEqual(true, mut.contains(.a));
1023 testing.expectEqual(true, mut.contains(.b));
1024 testing.expectEqual(false, mut.contains(.c));
1025 testing.expectEqual(false, mut.contains(.d));
1026
1027 mut.setPresent(.a, false);
1028 mut.toggle(.b);
1029 mut.toggle(.c);
1030 mut.setPresent(.d, true);
1031 testing.expectEqual(@as(usize, 2), mut.count());
1032 testing.expectEqual(false, mut.contains(.a));
1033 testing.expectEqual(false, mut.contains(.b));
1034 testing.expectEqual(true, mut.contains(.c));
1035 testing.expectEqual(true, mut.contains(.d));
1036}
1037
1038test "std.enums.EnumArray void" {
1039 const E = extern enum { a, b, c, d, e = 0 };
1040 const ArrayVoid = EnumArray(E, void);
1041 testing.expectEqual(E, ArrayVoid.Key);
1042 testing.expectEqual(EnumIndexer(E), ArrayVoid.Indexer);
1043 testing.expectEqual(void, ArrayVoid.Value);
1044 testing.expectEqual(@as(usize, 4), ArrayVoid.len);
1045
1046 const undef = ArrayVoid.initUndefined();
1047 var inst = ArrayVoid.initFill({});
1048 const inst2 = ArrayVoid.init(.{ .a = {}, .b = {}, .c = {}, .d = {} });
1049 const inst3 = ArrayVoid.initDefault({}, .{});
1050
1051 _ = inst.get(.a);
1052 _ = inst.getPtr(.b);
1053 _ = inst.getPtrConst(.c);
1054 inst.set(.a, {});
1055
1056 var it = inst.iterator();
1057 testing.expectEqual(E.a, it.next().?.key);
1058 testing.expectEqual(E.b, it.next().?.key);
1059 testing.expectEqual(E.c, it.next().?.key);
1060 testing.expectEqual(E.d, it.next().?.key);
1061 testing.expect(it.next() == null);
1062}
1063
1064test "std.enums.EnumArray sized" {
1065 const E = extern enum { a, b, c, d, e = 0 };
1066 const Array = EnumArray(E, usize);
1067 testing.expectEqual(E, Array.Key);
1068 testing.expectEqual(EnumIndexer(E), Array.Indexer);
1069 testing.expectEqual(usize, Array.Value);
1070 testing.expectEqual(@as(usize, 4), Array.len);
1071
1072 const undef = Array.initUndefined();
1073 var inst = Array.initFill(5);
1074 const inst2 = Array.init(.{ .a = 1, .b = 2, .c = 3, .d = 4 });
1075 const inst3 = Array.initDefault(6, .{.b = 4, .c = 2});
1076
1077 testing.expectEqual(@as(usize, 5), inst.get(.a));
1078 testing.expectEqual(@as(usize, 5), inst.get(.b));
1079 testing.expectEqual(@as(usize, 5), inst.get(.c));
1080 testing.expectEqual(@as(usize, 5), inst.get(.d));
1081
1082 testing.expectEqual(@as(usize, 1), inst2.get(.a));
1083 testing.expectEqual(@as(usize, 2), inst2.get(.b));
1084 testing.expectEqual(@as(usize, 3), inst2.get(.c));
1085 testing.expectEqual(@as(usize, 4), inst2.get(.d));
1086
1087 testing.expectEqual(@as(usize, 6), inst3.get(.a));
1088 testing.expectEqual(@as(usize, 4), inst3.get(.b));
1089 testing.expectEqual(@as(usize, 2), inst3.get(.c));
1090 testing.expectEqual(@as(usize, 6), inst3.get(.d));
1091
1092 testing.expectEqual(&inst.values[0], inst.getPtr(.a));
1093 testing.expectEqual(&inst.values[1], inst.getPtr(.b));
1094 testing.expectEqual(&inst.values[2], inst.getPtr(.c));
1095 testing.expectEqual(&inst.values[3], inst.getPtr(.d));
1096
1097 testing.expectEqual(@as(*const usize, &inst.values[0]), inst.getPtrConst(.a));
1098 testing.expectEqual(@as(*const usize, &inst.values[1]), inst.getPtrConst(.b));
1099 testing.expectEqual(@as(*const usize, &inst.values[2]), inst.getPtrConst(.c));
1100 testing.expectEqual(@as(*const usize, &inst.values[3]), inst.getPtrConst(.d));
1101
1102 inst.set(.c, 8);
1103 testing.expectEqual(@as(usize, 5), inst.get(.a));
1104 testing.expectEqual(@as(usize, 5), inst.get(.b));
1105 testing.expectEqual(@as(usize, 8), inst.get(.c));
1106 testing.expectEqual(@as(usize, 5), inst.get(.d));
1107
1108 var it = inst.iterator();
1109 const Entry = Array.Entry;
1110 testing.expectEqual(@as(?Entry, Entry{
1111 .key = .a,
1112 .value = &inst.values[0],
1113 }), it.next());
1114 testing.expectEqual(@as(?Entry, Entry{
1115 .key = .b,
1116 .value = &inst.values[1],
1117 }), it.next());
1118 testing.expectEqual(@as(?Entry, Entry{
1119 .key = .c,
1120 .value = &inst.values[2],
1121 }), it.next());
1122 testing.expectEqual(@as(?Entry, Entry{
1123 .key = .d,
1124 .value = &inst.values[3],
1125 }), it.next());
1126 testing.expectEqual(@as(?Entry, null), it.next());
1127}
1128
1129test "std.enums.EnumMap void" {
1130 const E = extern enum { a, b, c, d, e = 0 };
1131 const Map = EnumMap(E, void);
1132 testing.expectEqual(E, Map.Key);
1133 testing.expectEqual(EnumIndexer(E), Map.Indexer);
1134 testing.expectEqual(void, Map.Value);
1135 testing.expectEqual(@as(usize, 4), Map.len);
1136
1137 const b = Map.initFull({});
1138 testing.expectEqual(@as(usize, 4), b.count());
1139
1140 const c = Map.initFullWith(.{ .a = {}, .b = {}, .c = {}, .d = {} });
1141 testing.expectEqual(@as(usize, 4), c.count());
1142
1143 const d = Map.initFullWithDefault({}, .{ .b = {} });
1144 testing.expectEqual(@as(usize, 4), d.count());
1145
1146 var a = Map.init(.{ .b = {}, .d = {} });
1147 testing.expectEqual(@as(usize, 2), a.count());
1148 testing.expectEqual(false, a.contains(.a));
1149 testing.expectEqual(true, a.contains(.b));
1150 testing.expectEqual(false, a.contains(.c));
1151 testing.expectEqual(true, a.contains(.d));
1152 testing.expect(a.get(.a) == null);
1153 testing.expect(a.get(.b) != null);
1154 testing.expect(a.get(.c) == null);
1155 testing.expect(a.get(.d) != null);
1156 testing.expect(a.getPtr(.a) == null);
1157 testing.expect(a.getPtr(.b) != null);
1158 testing.expect(a.getPtr(.c) == null);
1159 testing.expect(a.getPtr(.d) != null);
1160 testing.expect(a.getPtrConst(.a) == null);
1161 testing.expect(a.getPtrConst(.b) != null);
1162 testing.expect(a.getPtrConst(.c) == null);
1163 testing.expect(a.getPtrConst(.d) != null);
1164 _ = a.getPtrAssertContains(.b);
1165 _ = a.getAssertContains(.d);
1166
1167 a.put(.a, {});
1168 a.put(.a, {});
1169 a.putUninitialized(.c).* = {};
1170 a.putUninitialized(.c).* = {};
1171
1172 testing.expectEqual(@as(usize, 4), a.count());
1173 testing.expect(a.get(.a) != null);
1174 testing.expect(a.get(.b) != null);
1175 testing.expect(a.get(.c) != null);
1176 testing.expect(a.get(.d) != null);
1177
1178 a.remove(.a);
1179 _ = a.fetchRemove(.c);
1180
1181 var iter = a.iterator();
1182 const Entry = Map.Entry;
1183 testing.expectEqual(E.b, iter.next().?.key);
1184 testing.expectEqual(E.d, iter.next().?.key);
1185 testing.expect(iter.next() == null);
1186}
1187
1188test "std.enums.EnumMap sized" {
1189 const E = extern enum { a, b, c, d, e = 0 };
1190 const Map = EnumMap(E, usize);
1191 testing.expectEqual(E, Map.Key);
1192 testing.expectEqual(EnumIndexer(E), Map.Indexer);
1193 testing.expectEqual(usize, Map.Value);
1194 testing.expectEqual(@as(usize, 4), Map.len);
1195
1196 const b = Map.initFull(5);
1197 testing.expectEqual(@as(usize, 4), b.count());
1198 testing.expect(b.contains(.a));
1199 testing.expect(b.contains(.b));
1200 testing.expect(b.contains(.c));
1201 testing.expect(b.contains(.d));
1202 testing.expectEqual(@as(?usize, 5), b.get(.a));
1203 testing.expectEqual(@as(?usize, 5), b.get(.b));
1204 testing.expectEqual(@as(?usize, 5), b.get(.c));
1205 testing.expectEqual(@as(?usize, 5), b.get(.d));
1206
1207 const c = Map.initFullWith(.{ .a = 1, .b = 2, .c = 3, .d = 4 });
1208 testing.expectEqual(@as(usize, 4), c.count());
1209 testing.expect(c.contains(.a));
1210 testing.expect(c.contains(.b));
1211 testing.expect(c.contains(.c));
1212 testing.expect(c.contains(.d));
1213 testing.expectEqual(@as(?usize, 1), c.get(.a));
1214 testing.expectEqual(@as(?usize, 2), c.get(.b));
1215 testing.expectEqual(@as(?usize, 3), c.get(.c));
1216 testing.expectEqual(@as(?usize, 4), c.get(.d));
1217
1218 const d = Map.initFullWithDefault(6, .{ .b = 2, .c = 4 });
1219 testing.expectEqual(@as(usize, 4), d.count());
1220 testing.expect(d.contains(.a));
1221 testing.expect(d.contains(.b));
1222 testing.expect(d.contains(.c));
1223 testing.expect(d.contains(.d));
1224 testing.expectEqual(@as(?usize, 6), d.get(.a));
1225 testing.expectEqual(@as(?usize, 2), d.get(.b));
1226 testing.expectEqual(@as(?usize, 4), d.get(.c));
1227 testing.expectEqual(@as(?usize, 6), d.get(.d));
1228
1229 var a = Map.init(.{ .b = 2, .d = 4 });
1230 testing.expectEqual(@as(usize, 2), a.count());
1231 testing.expectEqual(false, a.contains(.a));
1232 testing.expectEqual(true, a.contains(.b));
1233 testing.expectEqual(false, a.contains(.c));
1234 testing.expectEqual(true, a.contains(.d));
1235
1236 testing.expectEqual(@as(?usize, null), a.get(.a));
1237 testing.expectEqual(@as(?usize, 2), a.get(.b));
1238 testing.expectEqual(@as(?usize, null), a.get(.c));
1239 testing.expectEqual(@as(?usize, 4), a.get(.d));
1240
1241 testing.expectEqual(@as(?*usize, null), a.getPtr(.a));
1242 testing.expectEqual(@as(?*usize, &a.values[1]), a.getPtr(.b));
1243 testing.expectEqual(@as(?*usize, null), a.getPtr(.c));
1244 testing.expectEqual(@as(?*usize, &a.values[3]), a.getPtr(.d));
1245
1246 testing.expectEqual(@as(?*const usize, null), a.getPtrConst(.a));
1247 testing.expectEqual(@as(?*const usize, &a.values[1]), a.getPtrConst(.b));
1248 testing.expectEqual(@as(?*const usize, null), a.getPtrConst(.c));
1249 testing.expectEqual(@as(?*const usize, &a.values[3]), a.getPtrConst(.d));
1250
1251 testing.expectEqual(@as(*const usize, &a.values[1]), a.getPtrAssertContains(.b));
1252 testing.expectEqual(@as(*const usize, &a.values[3]), a.getPtrAssertContains(.d));
1253 testing.expectEqual(@as(usize, 2), a.getAssertContains(.b));
1254 testing.expectEqual(@as(usize, 4), a.getAssertContains(.d));
1255
1256 a.put(.a, 3);
1257 a.put(.a, 5);
1258 a.putUninitialized(.c).* = 7;
1259 a.putUninitialized(.c).* = 9;
1260
1261 testing.expectEqual(@as(usize, 4), a.count());
1262 testing.expectEqual(@as(?usize, 5), a.get(.a));
1263 testing.expectEqual(@as(?usize, 2), a.get(.b));
1264 testing.expectEqual(@as(?usize, 9), a.get(.c));
1265 testing.expectEqual(@as(?usize, 4), a.get(.d));
1266
1267 a.remove(.a);
1268 testing.expectEqual(@as(?usize, null), a.fetchRemove(.a));
1269 testing.expectEqual(@as(?usize, 9), a.fetchRemove(.c));
1270 a.remove(.c);
1271
1272 var iter = a.iterator();
1273 const Entry = Map.Entry;
1274 testing.expectEqual(@as(?Entry, Entry{
1275 .key = .b, .value = &a.values[1],
1276 }), iter.next());
1277 testing.expectEqual(@as(?Entry, Entry{
1278 .key = .d, .value = &a.values[3],
1279 }), iter.next());
1280 testing.expectEqual(@as(?Entry, null), iter.next());
1281}
lib/std/fs/path.zig+11-1
...@@ -92,7 +92,7 @@ pub fn join(allocator: *Allocator, paths: []const []const u8) ![]u8 {...@@ -92,7 +92,7 @@ pub fn join(allocator: *Allocator, paths: []const []const u8) ![]u8 {
92/// Naively combines a series of paths with the native path seperator and null terminator.92/// Naively combines a series of paths with the native path seperator and null terminator.
93/// Allocates memory for the result, which must be freed by the caller.93/// Allocates memory for the result, which must be freed by the caller.
94pub fn joinZ(allocator: *Allocator, paths: []const []const u8) ![:0]u8 {94pub fn joinZ(allocator: *Allocator, paths: []const []const u8) ![:0]u8 {
95 const out = joinSepMaybeZ(allocator, sep, isSep, paths, true);95 const out = try joinSepMaybeZ(allocator, sep, isSep, paths, true);
96 return out[0 .. out.len - 1 :0];96 return out[0 .. out.len - 1 :0];
97}97}
9898
...@@ -119,6 +119,16 @@ fn testJoinMaybeZPosix(paths: []const []const u8, expected: []const u8, zero: bo...@@ -119,6 +119,16 @@ fn testJoinMaybeZPosix(paths: []const []const u8, expected: []const u8, zero: bo
119}119}
120120
121test "join" {121test "join" {
122 {
123 const actual: []u8 = try join(testing.allocator, &[_][]const u8{});
124 defer testing.allocator.free(actual);
125 testing.expectEqualSlices(u8, "", actual);
126 }
127 {
128 const actual: [:0]u8 = try joinZ(testing.allocator, &[_][]const u8{});
129 defer testing.allocator.free(actual);
130 testing.expectEqualSlices(u8, "", actual);
131 }
122 for (&[_]bool{ false, true }) |zero| {132 for (&[_]bool{ false, true }) |zero| {
123 testJoinMaybeZWindows(&[_][]const u8{}, "", zero);133 testJoinMaybeZWindows(&[_][]const u8{}, "", zero);
124 testJoinMaybeZWindows(&[_][]const u8{ "c:\\a\\b", "c" }, "c:\\a\\b\\c", zero);134 testJoinMaybeZWindows(&[_][]const u8{ "c:\\a\\b", "c" }, "c:\\a\\b\\c", zero);
lib/std/macho.zig+40
...@@ -1227,6 +1227,24 @@ pub const S_ATTR_EXT_RELOC = 0x200;...@@ -1227,6 +1227,24 @@ pub const S_ATTR_EXT_RELOC = 0x200;
1227/// section has local relocation entries1227/// section has local relocation entries
1228pub const S_ATTR_LOC_RELOC = 0x100;1228pub const S_ATTR_LOC_RELOC = 0x100;
12291229
1230/// template of initial values for TLVs
1231pub const S_THREAD_LOCAL_REGULAR = 0x11;
1232
1233/// template of initial values for TLVs
1234pub const S_THREAD_LOCAL_ZEROFILL = 0x12;
1235
1236/// TLV descriptors
1237pub const S_THREAD_LOCAL_VARIABLES = 0x13;
1238
1239/// pointers to TLV descriptors
1240pub const S_THREAD_LOCAL_VARIABLE_POINTERS = 0x14;
1241
1242/// functions to call to initialize TLV values
1243pub const S_THREAD_LOCAL_INIT_FUNCTION_POINTERS = 0x15;
1244
1245/// 32-bit offsets to initializers
1246pub const S_INIT_FUNC_OFFSETS = 0x16;
1247
1230pub const cpu_type_t = integer_t;1248pub const cpu_type_t = integer_t;
1231pub const cpu_subtype_t = integer_t;1249pub const cpu_subtype_t = integer_t;
1232pub const integer_t = c_int;1250pub const integer_t = c_int;
...@@ -1422,6 +1440,14 @@ pub const EXPORT_SYMBOL_FLAGS_KIND_WEAK_DEFINITION: u8 = 0x04;...@@ -1422,6 +1440,14 @@ pub const EXPORT_SYMBOL_FLAGS_KIND_WEAK_DEFINITION: u8 = 0x04;
1422pub const EXPORT_SYMBOL_FLAGS_REEXPORT: u8 = 0x08;1440pub const EXPORT_SYMBOL_FLAGS_REEXPORT: u8 = 0x08;
1423pub const EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER: u8 = 0x10;1441pub const EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER: u8 = 0x10;
14241442
1443// An indirect symbol table entry is simply a 32bit index into the symbol table
1444// to the symbol that the pointer or stub is refering to. Unless it is for a
1445// non-lazy symbol pointer section for a defined symbol which strip(1) as
1446// removed. In which case it has the value INDIRECT_SYMBOL_LOCAL. If the
1447// symbol was also absolute INDIRECT_SYMBOL_ABS is or'ed with that.
1448pub const INDIRECT_SYMBOL_LOCAL: u32 = 0x80000000;
1449pub const INDIRECT_SYMBOL_ABS: u32 = 0x40000000;
1450
1425// Codesign consts and structs taken from:1451// Codesign consts and structs taken from:
1426// https://opensource.apple.com/source/xnu/xnu-6153.81.5/osfmk/kern/cs_blobs.h.auto.html1452// https://opensource.apple.com/source/xnu/xnu-6153.81.5/osfmk/kern/cs_blobs.h.auto.html
14271453
...@@ -1589,3 +1615,17 @@ pub const GenericBlob = extern struct {...@@ -1589,3 +1615,17 @@ pub const GenericBlob = extern struct {
1589 /// Total length of blob1615 /// Total length of blob
1590 length: u32,1616 length: u32,
1591};1617};
1618
1619/// The LC_DATA_IN_CODE load commands uses a linkedit_data_command
1620/// to point to an array of data_in_code_entry entries. Each entry
1621/// describes a range of data in a code section.
1622pub const data_in_code_entry = extern struct {
1623 /// From mach_header to start of data range.
1624 offset: u32,
1625
1626 /// Number of bytes in data range.
1627 length: u16,
1628
1629 /// A DICE_KIND value.
1630 kind: u16,
1631};
lib/std/meta.zig+51-18
...@@ -888,19 +888,20 @@ pub fn Vector(comptime len: u32, comptime child: type) type {...@@ -888,19 +888,20 @@ pub fn Vector(comptime len: u32, comptime child: type) type {
888/// Given a type and value, cast the value to the type as c would.888/// Given a type and value, cast the value to the type as c would.
889/// This is for translate-c and is not intended for general use.889/// This is for translate-c and is not intended for general use.
890pub fn cast(comptime DestType: type, target: anytype) DestType {890pub fn cast(comptime DestType: type, target: anytype) DestType {
891 const TargetType = @TypeOf(target);891 // this function should behave like transCCast in translate-c, except it's for macros
892 const SourceType = @TypeOf(target);
892 switch (@typeInfo(DestType)) {893 switch (@typeInfo(DestType)) {
893 .Pointer => |dest_ptr| {894 .Pointer => {
894 switch (@typeInfo(TargetType)) {895 switch (@typeInfo(SourceType)) {
895 .Int, .ComptimeInt => {896 .Int, .ComptimeInt => {
896 return @intToPtr(DestType, target);897 return @intToPtr(DestType, target);
897 },898 },
898 .Pointer => |ptr| {899 .Pointer => {
899 return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target));900 return castPtr(DestType, target);
900 },901 },
901 .Optional => |opt| {902 .Optional => |opt| {
902 if (@typeInfo(opt.child) == .Pointer) {903 if (@typeInfo(opt.child) == .Pointer) {
903 return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target));904 return castPtr(DestType, target);
904 }905 }
905 },906 },
906 else => {},907 else => {},
...@@ -908,17 +909,16 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {...@@ -908,17 +909,16 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {
908 },909 },
909 .Optional => |dest_opt| {910 .Optional => |dest_opt| {
910 if (@typeInfo(dest_opt.child) == .Pointer) {911 if (@typeInfo(dest_opt.child) == .Pointer) {
911 const dest_ptr = @typeInfo(dest_opt.child).Pointer;912 switch (@typeInfo(SourceType)) {
912 switch (@typeInfo(TargetType)) {
913 .Int, .ComptimeInt => {913 .Int, .ComptimeInt => {
914 return @intToPtr(DestType, target);914 return @intToPtr(DestType, target);
915 },915 },
916 .Pointer => {916 .Pointer => {
917 return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target));917 return castPtr(DestType, target);
918 },918 },
919 .Optional => |target_opt| {919 .Optional => |target_opt| {
920 if (@typeInfo(target_opt.child) == .Pointer) {920 if (@typeInfo(target_opt.child) == .Pointer) {
921 return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target));921 return castPtr(DestType, target);
922 }922 }
923 },923 },
924 else => {},924 else => {},
...@@ -926,25 +926,25 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {...@@ -926,25 +926,25 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {
926 }926 }
927 },927 },
928 .Enum => {928 .Enum => {
929 if (@typeInfo(TargetType) == .Int or @typeInfo(TargetType) == .ComptimeInt) {929 if (@typeInfo(SourceType) == .Int or @typeInfo(SourceType) == .ComptimeInt) {
930 return @intToEnum(DestType, target);930 return @intToEnum(DestType, target);
931 }931 }
932 },932 },
933 .Int, .ComptimeInt => {933 .Int => {
934 switch (@typeInfo(TargetType)) {934 switch (@typeInfo(SourceType)) {
935 .Pointer => {935 .Pointer => {
936 return @intCast(DestType, @ptrToInt(target));936 return castInt(DestType, @ptrToInt(target));
937 },937 },
938 .Optional => |opt| {938 .Optional => |opt| {
939 if (@typeInfo(opt.child) == .Pointer) {939 if (@typeInfo(opt.child) == .Pointer) {
940 return @intCast(DestType, @ptrToInt(target));940 return castInt(DestType, @ptrToInt(target));
941 }941 }
942 },942 },
943 .Enum => {943 .Enum => {
944 return @intCast(DestType, @enumToInt(target));944 return castInt(DestType, @enumToInt(target));
945 },945 },
946 .Int, .ComptimeInt => {946 .Int => {
947 return @intCast(DestType, target);947 return castInt(DestType, target);
948 },948 },
949 else => {},949 else => {},
950 }950 }
...@@ -954,6 +954,34 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {...@@ -954,6 +954,34 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {
954 return @as(DestType, target);954 return @as(DestType, target);
955}955}
956956
957fn castInt(comptime DestType: type, target: anytype) DestType {
958 const dest = @typeInfo(DestType).Int;
959 const source = @typeInfo(@TypeOf(target)).Int;
960
961 if (dest.bits < source.bits)
962 return @bitCast(DestType, @truncate(Int(source.signedness, dest.bits), target))
963 else
964 return @bitCast(DestType, @as(Int(source.signedness, dest.bits), target));
965}
966
967fn castPtr(comptime DestType: type, target: anytype) DestType {
968 const dest = ptrInfo(DestType);
969 const source = ptrInfo(@TypeOf(target));
970
971 if (source.is_const and !dest.is_const or source.is_volatile and !dest.is_volatile)
972 return @intToPtr(DestType, @ptrToInt(target))
973 else
974 return @ptrCast(DestType, @alignCast(dest.alignment, target));
975}
976
977fn ptrInfo(comptime PtrType: type) TypeInfo.Pointer {
978 return switch(@typeInfo(PtrType)){
979 .Optional => |opt_info| @typeInfo(opt_info.child).Pointer,
980 .Pointer => |ptr_info| ptr_info,
981 else => unreachable,
982 };
983}
984
957test "std.meta.cast" {985test "std.meta.cast" {
958 const E = enum(u2) {986 const E = enum(u2) {
959 Zero,987 Zero,
...@@ -977,6 +1005,11 @@ test "std.meta.cast" {...@@ -977,6 +1005,11 @@ test "std.meta.cast" {
977 testing.expectEqual(@as(u32, 4), cast(u32, @intToPtr(?*u32, 4)));1005 testing.expectEqual(@as(u32, 4), cast(u32, @intToPtr(?*u32, 4)));
978 testing.expectEqual(@as(u32, 10), cast(u32, @as(u64, 10)));1006 testing.expectEqual(@as(u32, 10), cast(u32, @as(u64, 10)));
979 testing.expectEqual(@as(u8, 2), cast(u8, E.Two));1007 testing.expectEqual(@as(u8, 2), cast(u8, E.Two));
1008
1009 testing.expectEqual(@bitCast(i32, @as(u32, 0x8000_0000)), cast(i32, @as(u32, 0x8000_0000)));
1010
1011 testing.expectEqual(@intToPtr(*u8, 2), cast(*u8, @intToPtr(*const u8, 2)));
1012 testing.expectEqual(@intToPtr(*u8, 2), cast(*u8, @intToPtr(*volatile u8, 2)));
980}1013}
9811014
982/// Given a value returns its size as C's sizeof operator would.1015/// Given a value returns its size as C's sizeof operator would.
lib/std/meta/trait.zig+78
...@@ -408,6 +408,84 @@ test "std.meta.trait.isTuple" {...@@ -408,6 +408,84 @@ test "std.meta.trait.isTuple" {
408 testing.expect(isTuple(@TypeOf(t3)));408 testing.expect(isTuple(@TypeOf(t3)));
409}409}
410410
411/// Returns true if the passed type will coerce to []const u8.
412/// Any of the following are considered strings:
413/// ```
414/// []const u8, [:S]const u8, *const [N]u8, *const [N:S]u8,
415/// []u8, [:S]u8, *[:S]u8, *[N:S]u8.
416/// ```
417/// These types are not considered strings:
418/// ```
419/// u8, [N]u8, [*]const u8, [*:0]const u8,
420/// [*]const [N]u8, []const u16, []const i8,
421/// *const u8, ?[]const u8, ?*const [N]u8.
422/// ```
423pub fn isZigString(comptime T: type) bool {
424 comptime {
425 // Only pointer types can be strings, no optionals
426 const info = @typeInfo(T);
427 if (info != .Pointer) return false;
428
429 const ptr = &info.Pointer;
430 // Check for CV qualifiers that would prevent coerction to []const u8
431 if (ptr.is_volatile or ptr.is_allowzero) return false;
432
433 // If it's already a slice, simple check.
434 if (ptr.size == .Slice) {
435 return ptr.child == u8;
436 }
437
438 // Otherwise check if it's an array type that coerces to slice.
439 if (ptr.size == .One) {
440 const child = @typeInfo(ptr.child);
441 if (child == .Array) {
442 const arr = &child.Array;
443 return arr.child == u8;
444 }
445 }
446
447 return false;
448 }
449}
450
451test "std.meta.trait.isZigString" {
452 testing.expect(isZigString([]const u8));
453 testing.expect(isZigString([]u8));
454 testing.expect(isZigString([:0]const u8));
455 testing.expect(isZigString([:0]u8));
456 testing.expect(isZigString([:5]const u8));
457 testing.expect(isZigString([:5]u8));
458 testing.expect(isZigString(*const [0]u8));
459 testing.expect(isZigString(*[0]u8));
460 testing.expect(isZigString(*const [0:0]u8));
461 testing.expect(isZigString(*[0:0]u8));
462 testing.expect(isZigString(*const [0:5]u8));
463 testing.expect(isZigString(*[0:5]u8));
464 testing.expect(isZigString(*const [10]u8));
465 testing.expect(isZigString(*[10]u8));
466 testing.expect(isZigString(*const [10:0]u8));
467 testing.expect(isZigString(*[10:0]u8));
468 testing.expect(isZigString(*const [10:5]u8));
469 testing.expect(isZigString(*[10:5]u8));
470
471 testing.expect(!isZigString(u8));
472 testing.expect(!isZigString([4]u8));
473 testing.expect(!isZigString([4:0]u8));
474 testing.expect(!isZigString([*]const u8));
475 testing.expect(!isZigString([*]const [4]u8));
476 testing.expect(!isZigString([*c]const u8));
477 testing.expect(!isZigString([*c]const [4]u8));
478 testing.expect(!isZigString([*:0]const u8));
479 testing.expect(!isZigString([*:0]const u8));
480 testing.expect(!isZigString(*[]const u8));
481 testing.expect(!isZigString(?[]const u8));
482 testing.expect(!isZigString(?*const [4]u8));
483 testing.expect(!isZigString([]allowzero u8));
484 testing.expect(!isZigString([]volatile u8));
485 testing.expect(!isZigString(*allowzero [4]u8));
486 testing.expect(!isZigString(*volatile [4]u8));
487}
488
411pub fn hasDecls(comptime T: type, comptime names: anytype) bool {489pub fn hasDecls(comptime T: type, comptime names: anytype) bool {
412 inline for (names) |name| {490 inline for (names) |name| {
413 if (!@hasDecl(T, name))491 if (!@hasDecl(T, name))
lib/std/os.zig+2-2
...@@ -2879,7 +2879,7 @@ pub fn bind(sock: socket_t, addr: *const sockaddr, len: socklen_t) BindError!voi...@@ -2879,7 +2879,7 @@ pub fn bind(sock: socket_t, addr: *const sockaddr, len: socklen_t) BindError!voi
2879 unreachable;2879 unreachable;
2880}2880}
28812881
2882const ListenError = error{2882pub const ListenError = error{
2883 /// Another socket is already listening on the same port.2883 /// Another socket is already listening on the same port.
2884 /// For Internet domain sockets, the socket referred to by sockfd had not previously2884 /// For Internet domain sockets, the socket referred to by sockfd had not previously
2885 /// been bound to an address and, upon attempting to bind it to an ephemeral port, it2885 /// been bound to an address and, upon attempting to bind it to an ephemeral port, it
...@@ -5827,7 +5827,7 @@ pub fn tcsetattr(handle: fd_t, optional_action: TCSA, termios_p: termios) Termio...@@ -5827,7 +5827,7 @@ pub fn tcsetattr(handle: fd_t, optional_action: TCSA, termios_p: termios) Termio
5827 }5827 }
5828}5828}
58295829
5830const IoCtl_SIOCGIFINDEX_Error = error{5830pub const IoCtl_SIOCGIFINDEX_Error = error{
5831 FileSystem,5831 FileSystem,
5832 InterfaceNotFound,5832 InterfaceNotFound,
5833} || UnexpectedError;5833} || UnexpectedError;
lib/std/std.zig+4
...@@ -20,6 +20,9 @@ pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringM...@@ -20,6 +20,9 @@ pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringM
20pub const DynLib = @import("dynamic_library.zig").DynLib;20pub const DynLib = @import("dynamic_library.zig").DynLib;
21pub const DynamicBitSet = bit_set.DynamicBitSet;21pub const DynamicBitSet = bit_set.DynamicBitSet;
22pub const DynamicBitSetUnmanaged = bit_set.DynamicBitSetUnmanaged;22pub const DynamicBitSetUnmanaged = bit_set.DynamicBitSetUnmanaged;
23pub const EnumArray = enums.EnumArray;
24pub const EnumMap = enums.EnumMap;
25pub const EnumSet = enums.EnumSet;
23pub const HashMap = hash_map.HashMap;26pub const HashMap = hash_map.HashMap;
24pub const HashMapUnmanaged = hash_map.HashMapUnmanaged;27pub const HashMapUnmanaged = hash_map.HashMapUnmanaged;
25pub const MultiArrayList = @import("multi_array_list.zig").MultiArrayList;28pub const MultiArrayList = @import("multi_array_list.zig").MultiArrayList;
...@@ -54,6 +57,7 @@ pub const cstr = @import("cstr.zig");...@@ -54,6 +57,7 @@ pub const cstr = @import("cstr.zig");
54pub const debug = @import("debug.zig");57pub const debug = @import("debug.zig");
55pub const dwarf = @import("dwarf.zig");58pub const dwarf = @import("dwarf.zig");
56pub const elf = @import("elf.zig");59pub const elf = @import("elf.zig");
60pub const enums = @import("enums.zig");
57pub const event = @import("event.zig");61pub const event = @import("event.zig");
58pub const fifo = @import("fifo.zig");62pub const fifo = @import("fifo.zig");
59pub const fmt = @import("fmt.zig");63pub const fmt = @import("fmt.zig");
lib/std/zig/parser_test.zig+73
...@@ -4,6 +4,31 @@...@@ -4,6 +4,31 @@
4// The MIT license requires this copyright notice to be included in all copies4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.5// and substantial portions of the software.
66
7test "zig fmt: respect line breaks in struct field value declaration" {
8 try testCanonical(
9 \\const Foo = struct {
10 \\ bar: u32 =
11 \\ 42,
12 \\ bar: u32 =
13 \\ // a comment
14 \\ 42,
15 \\ bar: u32 =
16 \\ 42,
17 \\ // a comment
18 \\ bar: []const u8 =
19 \\ \\ foo
20 \\ \\ bar
21 \\ \\ baz
22 \\ ,
23 \\ bar: u32 =
24 \\ blk: {
25 \\ break :blk 42;
26 \\ },
27 \\};
28 \\
29 );
30}
31
7// TODO Remove this after zig 0.9.0 is released.32// TODO Remove this after zig 0.9.0 is released.
8test "zig fmt: rewrite inline functions as callconv(.Inline)" {33test "zig fmt: rewrite inline functions as callconv(.Inline)" {
9 try testTransform(34 try testTransform(
...@@ -3038,6 +3063,54 @@ test "zig fmt: switch" {...@@ -3038,6 +3063,54 @@ test "zig fmt: switch" {
3038 \\}3063 \\}
3039 \\3064 \\
3040 );3065 );
3066
3067 try testTransform(
3068 \\test {
3069 \\ switch (x) {
3070 \\ foo =>
3071 \\ "bar",
3072 \\ }
3073 \\}
3074 \\
3075 ,
3076 \\test {
3077 \\ switch (x) {
3078 \\ foo => "bar",
3079 \\ }
3080 \\}
3081 \\
3082 );
3083}
3084
3085test "zig fmt: switch multiline string" {
3086 try testCanonical(
3087 \\test "switch multiline string" {
3088 \\ const x: u32 = 0;
3089 \\ const str = switch (x) {
3090 \\ 1 => "one",
3091 \\ 2 =>
3092 \\ \\ Comma after the multiline string
3093 \\ \\ is needed
3094 \\ ,
3095 \\ 3 => "three",
3096 \\ else => "else",
3097 \\ };
3098 \\
3099 \\ const Union = union(enum) {
3100 \\ Int: i64,
3101 \\ Float: f64,
3102 \\ };
3103 \\
3104 \\ const str = switch (u) {
3105 \\ Union.Int => |int|
3106 \\ \\ Comma after the multiline string
3107 \\ \\ is needed
3108 \\ ,
3109 \\ Union.Float => |*float| unreachable,
3110 \\ };
3111 \\}
3112 \\
3113 );
3041}3114}
30423115
3043test "zig fmt: while" {3116test "zig fmt: while" {
lib/std/zig/render.zig+33-5
...@@ -1159,8 +1159,29 @@ fn renderContainerField(...@@ -1159,8 +1159,29 @@ fn renderContainerField(
1159 try renderToken(ais, tree, rparen_token, .space); // )1159 try renderToken(ais, tree, rparen_token, .space); // )
1160 }1160 }
1161 const eq_token = tree.firstToken(field.ast.value_expr) - 1;1161 const eq_token = tree.firstToken(field.ast.value_expr) - 1;
1162 try renderToken(ais, tree, eq_token, .space); // =1162 const eq_space: Space = if (tree.tokensOnSameLine(eq_token, eq_token + 1)) .space else .newline;
1163 return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value1163 {
1164 ais.pushIndent();
1165 try renderToken(ais, tree, eq_token, eq_space); // =
1166 ais.popIndent();
1167 }
1168
1169 if (eq_space == .space)
1170 return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value
1171
1172 const token_tags = tree.tokens.items(.tag);
1173 const maybe_comma = tree.lastToken(field.ast.value_expr) + 1;
1174
1175 if (token_tags[maybe_comma] == .comma) {
1176 ais.pushIndent();
1177 try renderExpression(gpa, ais, tree, field.ast.value_expr, .none); // value
1178 ais.popIndent();
1179 try renderToken(ais, tree, maybe_comma, space);
1180 } else {
1181 ais.pushIndent();
1182 try renderExpression(gpa, ais, tree, field.ast.value_expr, space); // value
1183 ais.popIndent();
1184 }
1164}1185}
11651186
1166fn renderBuiltinCall(1187fn renderBuiltinCall(
...@@ -1423,6 +1444,7 @@ fn renderSwitchCase(...@@ -1423,6 +1444,7 @@ fn renderSwitchCase(
1423 switch_case: ast.full.SwitchCase,1444 switch_case: ast.full.SwitchCase,
1424 space: Space,1445 space: Space,
1425) Error!void {1446) Error!void {
1447 const node_tags = tree.nodes.items(.tag);
1426 const token_tags = tree.tokens.items(.tag);1448 const token_tags = tree.tokens.items(.tag);
1427 const trailing_comma = token_tags[switch_case.ast.arrow_token - 1] == .comma;1449 const trailing_comma = token_tags[switch_case.ast.arrow_token - 1] == .comma;
14281450
...@@ -1445,17 +1467,23 @@ fn renderSwitchCase(...@@ -1445,17 +1467,23 @@ fn renderSwitchCase(
1445 }1467 }
14461468
1447 // Render the arrow and everything after it1469 // Render the arrow and everything after it
1448 try renderToken(ais, tree, switch_case.ast.arrow_token, .space);1470 const pre_target_space = if (node_tags[switch_case.ast.target_expr] == .multiline_string_literal)
1471 // Newline gets inserted when rendering the target expr.
1472 Space.none
1473 else
1474 Space.space;
1475 const after_arrow_space: Space = if (switch_case.payload_token == null) pre_target_space else .space;
1476 try renderToken(ais, tree, switch_case.ast.arrow_token, after_arrow_space);
14491477
1450 if (switch_case.payload_token) |payload_token| {1478 if (switch_case.payload_token) |payload_token| {
1451 try renderToken(ais, tree, payload_token - 1, .none); // pipe1479 try renderToken(ais, tree, payload_token - 1, .none); // pipe
1452 if (token_tags[payload_token] == .asterisk) {1480 if (token_tags[payload_token] == .asterisk) {
1453 try renderToken(ais, tree, payload_token, .none); // asterisk1481 try renderToken(ais, tree, payload_token, .none); // asterisk
1454 try renderToken(ais, tree, payload_token + 1, .none); // identifier1482 try renderToken(ais, tree, payload_token + 1, .none); // identifier
1455 try renderToken(ais, tree, payload_token + 2, .space); // pipe1483 try renderToken(ais, tree, payload_token + 2, pre_target_space); // pipe
1456 } else {1484 } else {
1457 try renderToken(ais, tree, payload_token, .none); // identifier1485 try renderToken(ais, tree, payload_token, .none); // identifier
1458 try renderToken(ais, tree, payload_token + 1, .space); // pipe1486 try renderToken(ais, tree, payload_token + 1, pre_target_space); // pipe
1459 }1487 }
1460 }1488 }
14611489
src/BuiltinFn.zig+1-1
...@@ -477,7 +477,7 @@ pub const list = list: {...@@ -477,7 +477,7 @@ pub const list = list: {
477 "@intCast",477 "@intCast",
478 .{478 .{
479 .tag = .int_cast,479 .tag = .int_cast,
480 .param_count = 1,480 .param_count = 2,
481 },481 },
482 },482 },
483 .{483 .{
src/clang.zig+5
...@@ -537,6 +537,11 @@ pub const FunctionType = opaque {...@@ -537,6 +537,11 @@ pub const FunctionType = opaque {
537 extern fn ZigClangFunctionType_getReturnType(*const FunctionType) QualType;537 extern fn ZigClangFunctionType_getReturnType(*const FunctionType) QualType;
538};538};
539539
540pub const GenericSelectionExpr = opaque {
541 pub const getResultExpr = ZigClangGenericSelectionExpr_getResultExpr;
542 extern fn ZigClangGenericSelectionExpr_getResultExpr(*const GenericSelectionExpr) *const Expr;
543};
544
540pub const IfStmt = opaque {545pub const IfStmt = opaque {
541 pub const getThen = ZigClangIfStmt_getThen;546 pub const getThen = ZigClangIfStmt_getThen;
542 extern fn ZigClangIfStmt_getThen(*const IfStmt) *const Stmt;547 extern fn ZigClangIfStmt_getThen(*const IfStmt) *const Stmt;
src/codegen.zig+59-133
...@@ -2133,9 +2133,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2133,9 +2133,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2133 if (inst.func.value()) |func_value| {2133 if (inst.func.value()) |func_value| {
2134 if (func_value.castTag(.function)) |func_payload| {2134 if (func_value.castTag(.function)) |func_payload| {
2135 const func = func_payload.data;2135 const func = func_payload.data;
2136 const text_segment = &macho_file.load_commands.items[macho_file.text_segment_cmd_index.?].Segment;2136 const got_addr = blk: {
2137 const got = &text_segment.sections.items[macho_file.got_section_index.?];2137 const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
2138 const got_addr = got.addr + func.owner_decl.link.macho.offset_table_index * @sizeOf(u64);2138 const got = seg.sections.items[macho_file.got_section_index.?];
2139 break :blk got.addr + func.owner_decl.link.macho.offset_table_index * @sizeOf(u64);
2140 };
2141 log.debug("got_addr = 0x{x}", .{got_addr});
2139 switch (arch) {2142 switch (arch) {
2140 .x86_64 => {2143 .x86_64 => {
2141 try self.genSetReg(inst.base.src, Type.initTag(.u32), .rax, .{ .memory = got_addr });2144 try self.genSetReg(inst.base.src, Type.initTag(.u32), .rax, .{ .memory = got_addr });
...@@ -2153,8 +2156,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2153,8 +2156,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2153 const decl = func_payload.data;2156 const decl = func_payload.data;
2154 const decl_name = try std.fmt.allocPrint(self.bin_file.allocator, "_{s}", .{decl.name});2157 const decl_name = try std.fmt.allocPrint(self.bin_file.allocator, "_{s}", .{decl.name});
2155 defer self.bin_file.allocator.free(decl_name);2158 defer self.bin_file.allocator.free(decl_name);
2156 const already_defined = macho_file.extern_lazy_symbols.contains(decl_name);2159 const already_defined = macho_file.lazy_imports.contains(decl_name);
2157 const symbol: u32 = if (macho_file.extern_lazy_symbols.getIndex(decl_name)) |index|2160 const symbol: u32 = if (macho_file.lazy_imports.getIndex(decl_name)) |index|
2158 @intCast(u32, index)2161 @intCast(u32, index)
2159 else2162 else
2160 try macho_file.addExternSymbol(decl_name);2163 try macho_file.addExternSymbol(decl_name);
...@@ -3304,80 +3307,32 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3304,80 +3307,32 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3304 },3307 },
3305 .memory => |addr| {3308 .memory => |addr| {
3306 if (self.bin_file.options.pie) {3309 if (self.bin_file.options.pie) {
3307 // For MachO, the binary, with the exception of object files, has to be a PIE.3310 // PC-relative displacement to the entry in the GOT table.
3308 // Therefore we cannot load an absolute address.3311 // TODO we should come up with our own, backend independent relocation types
3309 // Instead, we need to make use of PC-relative addressing.3312 // which each backend (Elf, MachO, etc.) would then translate into an actual
3310 if (reg.id() == 0) { // x0 is special-cased3313 // fixup when linking.
3311 // TODO This needs to be optimised in the stack usage (perhaps use a shadow stack3314 // adrp reg, pages
3312 // like described here:3315 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3313 // https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/using-the-stack-in-aarch64-implementing-push-and-pop)3316 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3314 // str x28, [sp, #-16]3317 .target_addr = addr,
3315 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.str(.x28, Register.sp, .{3318 .offset = self.code.items.len,
3316 .offset = Instruction.LoadStoreOffset.imm_pre_index(-16),3319 .size = 4,
3317 }).toU32());3320 });
3318 // adr x28, #8
3319 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.adr(.x28, 8).toU32());
3320 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3321 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3322 .address = addr,
3323 .start = self.code.items.len,
3324 .len = 4,
3325 });
3326 } else {
3327 return self.fail(src, "TODO implement genSetReg for PIE on this platform", .{});
3328 }
3329 // b [label]
3330 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.b(0).toU32());
3331 // mov r, x0
3332 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(
3333 reg,
3334 .xzr,
3335 .x0,
3336 Instruction.Shift.none,
3337 ).toU32());
3338 // ldr x28, [sp], #16
3339 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.x28, .{
3340 .register = .{
3341 .rn = Register.sp,
3342 .offset = Instruction.LoadStoreOffset.imm_post_index(16),
3343 },
3344 }).toU32());
3345 } else {3321 } else {
3346 // stp x0, x28, [sp, #-16]3322 return self.fail(src, "TODO implement genSetReg for PIE GOT indirection on this platform", .{});
3347 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.stp(
3348 .x0,
3349 .x28,
3350 Register.sp,
3351 Instruction.LoadStorePairOffset.pre_index(-16),
3352 ).toU32());
3353 // adr x28, #8
3354 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.adr(.x28, 8).toU32());
3355 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3356 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3357 .address = addr,
3358 .start = self.code.items.len,
3359 .len = 4,
3360 });
3361 } else {
3362 return self.fail(src, "TODO implement genSetReg for PIE on this platform", .{});
3363 }
3364 // b [label]
3365 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.b(0).toU32());
3366 // mov r, x0
3367 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(
3368 reg,
3369 .xzr,
3370 .x0,
3371 Instruction.Shift.none,
3372 ).toU32());
3373 // ldp x0, x28, [sp, #16]
3374 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldp(
3375 .x0,
3376 .x28,
3377 Register.sp,
3378 Instruction.LoadStorePairOffset.post_index(16),
3379 ).toU32());
3380 }3323 }
3324 mem.writeIntLittle(
3325 u32,
3326 try self.code.addManyAsArray(4),
3327 Instruction.adrp(reg, 0).toU32(),
3328 );
3329 // ldr reg, reg, offset
3330 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{
3331 .register = .{
3332 .rn = reg,
3333 .offset = Instruction.LoadStoreOffset.imm(0),
3334 },
3335 }).toU32());
3381 } else {3336 } else {
3382 // The value is in memory at a hard-coded address.3337 // The value is in memory at a hard-coded address.
3383 // If the type is a pointer, it means the pointer address is at this memory location.3338 // If the type is a pointer, it means the pointer address is at this memory location.
...@@ -3561,62 +3516,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3561,62 +3516,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3561 },3516 },
3562 .memory => |x| {3517 .memory => |x| {
3563 if (self.bin_file.options.pie) {3518 if (self.bin_file.options.pie) {
3564 // For MachO, the binary, with the exception of object files, has to be a PIE.3519 // RIP-relative displacement to the entry in the GOT table.
3565 // Therefore, we cannot load an absolute address.3520 // TODO we should come up with our own, backend independent relocation types
3566 assert(x > math.maxInt(u32)); // 32bit direct addressing is not supported by MachO.3521 // which each backend (Elf, MachO, etc.) would then translate into an actual
3567 // The plan here is to use unconditional relative jump to GOT entry, where we store3522 // fixup when linking.
3568 // pre-calculated and stored effective address to load into the target register.3523 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3569 // We leave the actual displacement information empty (0-padded) and fixing it up3524 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3570 // later in the linker.3525 .target_addr = x,
3571 if (reg.id() == 0) { // %rax is special-cased3526 .offset = self.code.items.len + 3,
3572 try self.code.ensureCapacity(self.code.items.len + 5);3527 .size = 4,
3573 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3574 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3575 .address = x,
3576 .start = self.code.items.len,
3577 .len = 5,
3578 });
3579 } else {
3580 return self.fail(src, "TODO implement genSetReg for PIE on this platform", .{});
3581 }
3582 // call [label]
3583 self.code.appendSliceAssumeCapacity(&[_]u8{
3584 0xE8,
3585 0x0,
3586 0x0,
3587 0x0,
3588 0x0,
3589 });3528 });
3590 } else {3529 } else {
3591 try self.code.ensureCapacity(self.code.items.len + 10);3530 return self.fail(src, "TODO implement genSetReg for PIE GOT indirection on this platform", .{});
3592 // push %rax
3593 self.code.appendSliceAssumeCapacity(&[_]u8{0x50});
3594 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3595 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3596 .address = x,
3597 .start = self.code.items.len,
3598 .len = 5,
3599 });
3600 } else {
3601 return self.fail(src, "TODO implement genSetReg for PIE on this platform", .{});
3602 }
3603 // call [label]
3604 self.code.appendSliceAssumeCapacity(&[_]u8{
3605 0xE8,
3606 0x0,
3607 0x0,
3608 0x0,
3609 0x0,
3610 });
3611 // mov %r, %rax
3612 self.code.appendSliceAssumeCapacity(&[_]u8{
3613 0x48,
3614 0x89,
3615 0xC0 | @as(u8, reg.id()),
3616 });
3617 // pop %rax
3618 self.code.appendSliceAssumeCapacity(&[_]u8{0x58});
3619 }3531 }
3532 try self.code.ensureCapacity(self.code.items.len + 7);
3533 self.rex(.{ .w = reg.size() == 64, .r = reg.isExtended() });
3534 self.code.appendSliceAssumeCapacity(&[_]u8{
3535 0x8D,
3536 0x05 | (@as(u8, reg.id() & 0b111) << 3),
3537 });
3538 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), 0);
3539
3540 try self.code.ensureCapacity(self.code.items.len + 3);
3541 self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended(), .r = reg.isExtended() });
3542 const RM = (@as(u8, reg.id() & 0b111) << 3) | @truncate(u3, reg.id());
3543 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, RM });
3620 } else if (x <= math.maxInt(u32)) {3544 } else if (x <= math.maxInt(u32)) {
3621 // Moving from memory to a register is a variant of `8B /r`.3545 // Moving from memory to a register is a variant of `8B /r`.
3622 // Since we're using 64-bit moves, we require a REX.3546 // Since we're using 64-bit moves, we require a REX.
...@@ -3779,9 +3703,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3779,9 +3703,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3779 return MCValue{ .memory = got_addr };3703 return MCValue{ .memory = got_addr };
3780 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {3704 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3781 const decl = payload.data;3705 const decl = payload.data;
3782 const text_segment = &macho_file.load_commands.items[macho_file.text_segment_cmd_index.?].Segment;3706 const got_addr = blk: {
3783 const got = &text_segment.sections.items[macho_file.got_section_index.?];3707 const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
3784 const got_addr = got.addr + decl.link.macho.offset_table_index * ptr_bytes;3708 const got = seg.sections.items[macho_file.got_section_index.?];
3709 break :blk got.addr + decl.link.macho.offset_table_index * ptr_bytes;
3710 };
3785 return MCValue{ .memory = got_addr };3711 return MCValue{ .memory = got_addr };
3786 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {3712 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
3787 const decl = payload.data;3713 const decl = payload.data;
src/codegen/aarch64.zig+4-1
...@@ -221,7 +221,8 @@ pub const Instruction = union(enum) {...@@ -221,7 +221,8 @@ pub const Instruction = union(enum) {
221 offset: u12,221 offset: u12,
222 opc: u2,222 opc: u2,
223 op1: u2,223 op1: u2,
224 fixed: u4 = 0b111_0,224 v: u1,
225 fixed: u3 = 0b111,
225 size: u2,226 size: u2,
226 },227 },
227 LoadStorePairOfRegisters: packed struct {228 LoadStorePairOfRegisters: packed struct {
...@@ -505,6 +506,7 @@ pub const Instruction = union(enum) {...@@ -505,6 +506,7 @@ pub const Instruction = union(enum) {
505 .offset = offset.toU12(),506 .offset = offset.toU12(),
506 .opc = opc,507 .opc = opc,
507 .op1 = op1,508 .op1 = op1,
509 .v = 0,
508 .size = 0b10,510 .size = 0b10,
509 },511 },
510 };512 };
...@@ -517,6 +519,7 @@ pub const Instruction = union(enum) {...@@ -517,6 +519,7 @@ pub const Instruction = union(enum) {
517 .offset = offset.toU12(),519 .offset = offset.toU12(),
518 .opc = opc,520 .opc = opc,
519 .op1 = op1,521 .op1 = op1,
522 .v = 0,
520 .size = 0b11,523 .size = 0b11,
521 },524 },
522 };525 };
src/codegen/llvm.zig+11-11
...@@ -219,7 +219,7 @@ pub const LLVMIRModule = struct {...@@ -219,7 +219,7 @@ pub const LLVMIRModule = struct {
219219
220 var error_message: [*:0]const u8 = undefined;220 var error_message: [*:0]const u8 = undefined;
221 var target: *const llvm.Target = undefined;221 var target: *const llvm.Target = undefined;
222 if (llvm.Target.getFromTriple(llvm_target_triple.ptr, &target, &error_message)) {222 if (llvm.Target.getFromTriple(llvm_target_triple.ptr, &target, &error_message).toBool()) {
223 defer llvm.disposeMessage(error_message);223 defer llvm.disposeMessage(error_message);
224224
225 const stderr = std.io.getStdErr().writer();225 const stderr = std.io.getStdErr().writer();
...@@ -303,7 +303,7 @@ pub const LLVMIRModule = struct {...@@ -303,7 +303,7 @@ pub const LLVMIRModule = struct {
303 // verifyModule always allocs the error_message even if there is no error303 // verifyModule always allocs the error_message even if there is no error
304 defer llvm.disposeMessage(error_message);304 defer llvm.disposeMessage(error_message);
305305
306 if (self.llvm_module.verify(.ReturnStatus, &error_message)) {306 if (self.llvm_module.verify(.ReturnStatus, &error_message).toBool()) {
307 const stderr = std.io.getStdErr().writer();307 const stderr = std.io.getStdErr().writer();
308 try stderr.print("broken LLVM module found: {s}\nThis is a bug in the Zig compiler.", .{error_message});308 try stderr.print("broken LLVM module found: {s}\nThis is a bug in the Zig compiler.", .{error_message});
309 return error.BrokenLLVMModule;309 return error.BrokenLLVMModule;
...@@ -319,7 +319,7 @@ pub const LLVMIRModule = struct {...@@ -319,7 +319,7 @@ pub const LLVMIRModule = struct {
319 object_pathZ.ptr,319 object_pathZ.ptr,
320 .ObjectFile,320 .ObjectFile,
321 &error_message,321 &error_message,
322 )) {322 ).toBool()) {
323 defer llvm.disposeMessage(error_message);323 defer llvm.disposeMessage(error_message);
324324
325 const stderr = std.io.getStdErr().writer();325 const stderr = std.io.getStdErr().writer();
...@@ -614,7 +614,7 @@ pub const LLVMIRModule = struct {...@@ -614,7 +614,7 @@ pub const LLVMIRModule = struct {
614614
615 var indices: [2]*const llvm.Value = .{615 var indices: [2]*const llvm.Value = .{
616 index_type.constNull(),616 index_type.constNull(),
617 index_type.constInt(1, false),617 index_type.constInt(1, .False),
618 };618 };
619619
620 return self.builder.buildLoad(self.builder.buildInBoundsGEP(operand, &indices, 2, ""), "");620 return self.builder.buildLoad(self.builder.buildInBoundsGEP(operand, &indices, 2, ""), "");
...@@ -676,7 +676,7 @@ pub const LLVMIRModule = struct {...@@ -676,7 +676,7 @@ pub const LLVMIRModule = struct {
676 const signed = inst.base.ty.isSignedInt();676 const signed = inst.base.ty.isSignedInt();
677 // TODO: Should we use intcast here or just a simple bitcast?677 // TODO: Should we use intcast here or just a simple bitcast?
678 // LLVM does truncation vs bitcast (+signed extension) in the intcast depending on the sizes678 // LLVM does truncation vs bitcast (+signed extension) in the intcast depending on the sizes
679 return self.builder.buildIntCast2(val, try self.getLLVMType(inst.base.ty, inst.base.src), signed, "");679 return self.builder.buildIntCast2(val, try self.getLLVMType(inst.base.ty, inst.base.src), llvm.Bool.fromBool(signed), "");
680 }680 }
681681
682 fn genBitCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value {682 fn genBitCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value {
...@@ -782,7 +782,7 @@ pub const LLVMIRModule = struct {...@@ -782,7 +782,7 @@ pub const LLVMIRModule = struct {
782 if (bigint.limbs.len != 1) {782 if (bigint.limbs.len != 1) {
783 return self.fail(src, "TODO implement bigger bigint", .{});783 return self.fail(src, "TODO implement bigger bigint", .{});
784 }784 }
785 const llvm_int = llvm_type.constInt(bigint.limbs[0], false);785 const llvm_int = llvm_type.constInt(bigint.limbs[0], .False);
786 if (!bigint.positive) {786 if (!bigint.positive) {
787 return llvm.constNeg(llvm_int);787 return llvm.constNeg(llvm_int);
788 }788 }
...@@ -820,7 +820,7 @@ pub const LLVMIRModule = struct {...@@ -820,7 +820,7 @@ pub const LLVMIRModule = struct {
820 return self.fail(src, "TODO handle other sentinel values", .{});820 return self.fail(src, "TODO handle other sentinel values", .{});
821 } else false;821 } else false;
822822
823 return self.context.constString(payload.data.ptr, @intCast(c_uint, payload.data.len), !zero_sentinel);823 return self.context.constString(payload.data.ptr, @intCast(c_uint, payload.data.len), llvm.Bool.fromBool(!zero_sentinel));
824 } else {824 } else {
825 return self.fail(src, "TODO handle more array values", .{});825 return self.fail(src, "TODO handle more array values", .{});
826 }826 }
...@@ -836,13 +836,13 @@ pub const LLVMIRModule = struct {...@@ -836,13 +836,13 @@ pub const LLVMIRModule = struct {
836 llvm_child_type.constNull(),836 llvm_child_type.constNull(),
837 self.context.intType(1).constNull(),837 self.context.intType(1).constNull(),
838 };838 };
839 return self.context.constStruct(&optional_values, 2, false);839 return self.context.constStruct(&optional_values, 2, .False);
840 } else {840 } else {
841 var optional_values: [2]*const llvm.Value = .{841 var optional_values: [2]*const llvm.Value = .{
842 try self.genTypedValue(src, .{ .ty = child_type, .val = tv.val }),842 try self.genTypedValue(src, .{ .ty = child_type, .val = tv.val }),
843 self.context.intType(1).constAllOnes(),843 self.context.intType(1).constAllOnes(),
844 };844 };
845 return self.context.constStruct(&optional_values, 2, false);845 return self.context.constStruct(&optional_values, 2, .False);
846 }846 }
847 } else {847 } else {
848 return self.fail(src, "TODO implement const of optional pointer", .{});848 return self.fail(src, "TODO implement const of optional pointer", .{});
...@@ -882,7 +882,7 @@ pub const LLVMIRModule = struct {...@@ -882,7 +882,7 @@ pub const LLVMIRModule = struct {
882 try self.getLLVMType(child_type, src),882 try self.getLLVMType(child_type, src),
883 self.context.intType(1),883 self.context.intType(1),
884 };884 };
885 return self.context.structType(&optional_types, 2, false);885 return self.context.structType(&optional_types, 2, .False);
886 } else {886 } else {
887 return self.fail(src, "TODO implement optional pointers as actual pointers", .{});887 return self.fail(src, "TODO implement optional pointers as actual pointers", .{});
888 }888 }
...@@ -934,7 +934,7 @@ pub const LLVMIRModule = struct {...@@ -934,7 +934,7 @@ pub const LLVMIRModule = struct {
934 try self.getLLVMType(return_type, src),934 try self.getLLVMType(return_type, src),
935 if (fn_param_len == 0) null else llvm_param.ptr,935 if (fn_param_len == 0) null else llvm_param.ptr,
936 @intCast(c_uint, fn_param_len),936 @intCast(c_uint, fn_param_len),
937 false,937 .False,
938 );938 );
939 const llvm_fn = self.llvm_module.addFunction(func.name, fn_type);939 const llvm_fn = self.llvm_module.addFunction(func.name, fn_type);
940940
src/codegen/llvm/bindings.zig+23-10
...@@ -1,7 +1,20 @@...@@ -1,7 +1,20 @@
1//! We do this instead of @cImport because the self-hosted compiler is easier1//! We do this instead of @cImport because the self-hosted compiler is easier
2//! to bootstrap if it does not depend on translate-c.2//! to bootstrap if it does not depend on translate-c.
33
4const LLVMBool = bool;4/// Do not compare directly to .True, use toBool() instead.
5pub const Bool = enum(c_int) {
6 False,
7 True,
8 _,
9
10 pub fn fromBool(b: bool) Bool {
11 return @intToEnum(Bool, @boolToInt(b));
12 }
13
14 pub fn toBool(b: Bool) bool {
15 return b != .False;
16 }
17};
5pub const AttributeIndex = c_uint;18pub const AttributeIndex = c_uint;
619
7/// Make sure to use the *InContext functions instead of the global ones.20/// Make sure to use the *InContext functions instead of the global ones.
...@@ -22,13 +35,13 @@ pub const Context = opaque {...@@ -22,13 +35,13 @@ pub const Context = opaque {
22 extern fn LLVMVoidTypeInContext(C: *const Context) *const Type;35 extern fn LLVMVoidTypeInContext(C: *const Context) *const Type;
2336
24 pub const structType = LLVMStructTypeInContext;37 pub const structType = LLVMStructTypeInContext;
25 extern fn LLVMStructTypeInContext(C: *const Context, ElementTypes: [*]*const Type, ElementCount: c_uint, Packed: LLVMBool) *const Type;38 extern fn LLVMStructTypeInContext(C: *const Context, ElementTypes: [*]*const Type, ElementCount: c_uint, Packed: Bool) *const Type;
2639
27 pub const constString = LLVMConstStringInContext;40 pub const constString = LLVMConstStringInContext;
28 extern fn LLVMConstStringInContext(C: *const Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: LLVMBool) *const Value;41 extern fn LLVMConstStringInContext(C: *const Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: Bool) *const Value;
2942
30 pub const constStruct = LLVMConstStructInContext;43 pub const constStruct = LLVMConstStructInContext;
31 extern fn LLVMConstStructInContext(C: *const Context, ConstantVals: [*]*const Value, Count: c_uint, Packed: LLVMBool) *const Value;44 extern fn LLVMConstStructInContext(C: *const Context, ConstantVals: [*]*const Value, Count: c_uint, Packed: Bool) *const Value;
3245
33 pub const createBasicBlock = LLVMCreateBasicBlockInContext;46 pub const createBasicBlock = LLVMCreateBasicBlockInContext;
34 extern fn LLVMCreateBasicBlockInContext(C: *const Context, Name: [*:0]const u8) *const BasicBlock;47 extern fn LLVMCreateBasicBlockInContext(C: *const Context, Name: [*:0]const u8) *const BasicBlock;
...@@ -59,7 +72,7 @@ pub const Value = opaque {...@@ -59,7 +72,7 @@ pub const Value = opaque {
5972
60pub const Type = opaque {73pub const Type = opaque {
61 pub const functionType = LLVMFunctionType;74 pub const functionType = LLVMFunctionType;
62 extern fn LLVMFunctionType(ReturnType: *const Type, ParamTypes: ?[*]*const Type, ParamCount: c_uint, IsVarArg: LLVMBool) *const Type;75 extern fn LLVMFunctionType(ReturnType: *const Type, ParamTypes: ?[*]*const Type, ParamCount: c_uint, IsVarArg: Bool) *const Type;
6376
64 pub const constNull = LLVMConstNull;77 pub const constNull = LLVMConstNull;
65 extern fn LLVMConstNull(Ty: *const Type) *const Value;78 extern fn LLVMConstNull(Ty: *const Type) *const Value;
...@@ -68,7 +81,7 @@ pub const Type = opaque {...@@ -68,7 +81,7 @@ pub const Type = opaque {
68 extern fn LLVMConstAllOnes(Ty: *const Type) *const Value;81 extern fn LLVMConstAllOnes(Ty: *const Type) *const Value;
6982
70 pub const constInt = LLVMConstInt;83 pub const constInt = LLVMConstInt;
71 extern fn LLVMConstInt(IntTy: *const Type, N: c_ulonglong, SignExtend: LLVMBool) *const Value;84 extern fn LLVMConstInt(IntTy: *const Type, N: c_ulonglong, SignExtend: Bool) *const Value;
7285
73 pub const constArray = LLVMConstArray;86 pub const constArray = LLVMConstArray;
74 extern fn LLVMConstArray(ElementTy: *const Type, ConstantVals: ?[*]*const Value, Length: c_uint) *const Value;87 extern fn LLVMConstArray(ElementTy: *const Type, ConstantVals: ?[*]*const Value, Length: c_uint) *const Value;
...@@ -91,7 +104,7 @@ pub const Module = opaque {...@@ -91,7 +104,7 @@ pub const Module = opaque {
91 extern fn LLVMDisposeModule(*const Module) void;104 extern fn LLVMDisposeModule(*const Module) void;
92105
93 pub const verify = LLVMVerifyModule;106 pub const verify = LLVMVerifyModule;
94 extern fn LLVMVerifyModule(*const Module, Action: VerifierFailureAction, OutMessage: *[*:0]const u8) LLVMBool;107 extern fn LLVMVerifyModule(*const Module, Action: VerifierFailureAction, OutMessage: *[*:0]const u8) Bool;
95108
96 pub const addFunction = LLVMAddFunction;109 pub const addFunction = LLVMAddFunction;
97 extern fn LLVMAddFunction(*const Module, Name: [*:0]const u8, FunctionTy: *const Type) *const Value;110 extern fn LLVMAddFunction(*const Module, Name: [*:0]const u8, FunctionTy: *const Type) *const Value;
...@@ -191,7 +204,7 @@ pub const Builder = opaque {...@@ -191,7 +204,7 @@ pub const Builder = opaque {
191 extern fn LLVMBuildNUWSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;204 extern fn LLVMBuildNUWSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
192205
193 pub const buildIntCast2 = LLVMBuildIntCast2;206 pub const buildIntCast2 = LLVMBuildIntCast2;
194 extern fn LLVMBuildIntCast2(*const Builder, Val: *const Value, DestTy: *const Type, IsSigned: LLVMBool, Name: [*:0]const u8) *const Value;207 extern fn LLVMBuildIntCast2(*const Builder, Val: *const Value, DestTy: *const Type, IsSigned: Bool, Name: [*:0]const u8) *const Value;
195208
196 pub const buildBitCast = LLVMBuildBitCast;209 pub const buildBitCast = LLVMBuildBitCast;
197 extern fn LLVMBuildBitCast(*const Builder, Val: *const Value, DestTy: *const Type, Name: [*:0]const u8) *const Value;210 extern fn LLVMBuildBitCast(*const Builder, Val: *const Value, DestTy: *const Type, Name: [*:0]const u8) *const Value;
...@@ -258,7 +271,7 @@ pub const TargetMachine = opaque {...@@ -258,7 +271,7 @@ pub const TargetMachine = opaque {
258 Filename: [*:0]const u8,271 Filename: [*:0]const u8,
259 codegen: CodeGenFileType,272 codegen: CodeGenFileType,
260 ErrorMessage: *[*:0]const u8,273 ErrorMessage: *[*:0]const u8,
261 ) LLVMBool;274 ) Bool;
262};275};
263276
264pub const CodeMode = extern enum {277pub const CodeMode = extern enum {
...@@ -295,7 +308,7 @@ pub const CodeGenFileType = extern enum {...@@ -295,7 +308,7 @@ pub const CodeGenFileType = extern enum {
295308
296pub const Target = opaque {309pub const Target = opaque {
297 pub const getFromTriple = LLVMGetTargetFromTriple;310 pub const getFromTriple = LLVMGetTargetFromTriple;
298 extern fn LLVMGetTargetFromTriple(Triple: [*:0]const u8, T: **const Target, ErrorMessage: *[*:0]const u8) LLVMBool;311 extern fn LLVMGetTargetFromTriple(Triple: [*:0]const u8, T: **const Target, ErrorMessage: *[*:0]const u8) Bool;
299};312};
300313
301extern fn LLVMInitializeAArch64TargetInfo() void;314extern fn LLVMInitializeAArch64TargetInfo() void;
src/link/MachO.zig+549-536
...@@ -11,7 +11,9 @@ const codegen = @import("../codegen.zig");...@@ -11,7 +11,9 @@ const codegen = @import("../codegen.zig");
11const aarch64 = @import("../codegen/aarch64.zig");11const aarch64 = @import("../codegen/aarch64.zig");
12const math = std.math;12const math = std.math;
13const mem = std.mem;13const mem = std.mem;
14const meta = std.meta;
1415
16const bind = @import("MachO/bind.zig");
15const trace = @import("../tracy.zig").trace;17const trace = @import("../tracy.zig").trace;
16const build_options = @import("build_options");18const build_options = @import("build_options");
17const Module = @import("../Module.zig");19const Module = @import("../Module.zig");
...@@ -24,9 +26,9 @@ const target_util = @import("../target.zig");...@@ -24,9 +26,9 @@ const target_util = @import("../target.zig");
24const DebugSymbols = @import("MachO/DebugSymbols.zig");26const DebugSymbols = @import("MachO/DebugSymbols.zig");
25const Trie = @import("MachO/Trie.zig");27const Trie = @import("MachO/Trie.zig");
26const CodeSignature = @import("MachO/CodeSignature.zig");28const CodeSignature = @import("MachO/CodeSignature.zig");
29const Zld = @import("MachO/Zld.zig");
2730
28usingnamespace @import("MachO/commands.zig");31usingnamespace @import("MachO/commands.zig");
29usingnamespace @import("MachO/imports.zig");
3032
31pub const base_tag: File.Tag = File.Tag.macho;33pub const base_tag: File.Tag = File.Tag.macho;
3234
...@@ -87,14 +89,12 @@ code_signature_cmd_index: ?u16 = null,...@@ -87,14 +89,12 @@ code_signature_cmd_index: ?u16 = null,
8789
88/// Index into __TEXT,__text section.90/// Index into __TEXT,__text section.
89text_section_index: ?u16 = null,91text_section_index: ?u16 = null,
90/// Index into __TEXT,__ziggot section.
91got_section_index: ?u16 = null,
92/// Index into __TEXT,__stubs section.92/// Index into __TEXT,__stubs section.
93stubs_section_index: ?u16 = null,93stubs_section_index: ?u16 = null,
94/// Index into __TEXT,__stub_helper section.94/// Index into __TEXT,__stub_helper section.
95stub_helper_section_index: ?u16 = null,95stub_helper_section_index: ?u16 = null,
96/// Index into __DATA_CONST,__got section.96/// Index into __DATA_CONST,__got section.
97data_got_section_index: ?u16 = null,97got_section_index: ?u16 = null,
98/// Index into __DATA,__la_symbol_ptr section.98/// Index into __DATA,__la_symbol_ptr section.
99la_symbol_ptr_section_index: ?u16 = null,99la_symbol_ptr_section_index: ?u16 = null,
100/// Index into __DATA,__data section.100/// Index into __DATA,__data section.
...@@ -104,16 +104,16 @@ entry_addr: ?u64 = null,...@@ -104,16 +104,16 @@ entry_addr: ?u64 = null,
104104
105/// Table of all local symbols105/// Table of all local symbols
106/// Internally references string table for names (which are optional).106/// Internally references string table for names (which are optional).
107local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},107locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
108/// Table of all global symbols108/// Table of all global symbols
109global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},109globals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
110/// Table of all extern nonlazy symbols, indexed by name.110/// Table of all extern nonlazy symbols, indexed by name.
111extern_nonlazy_symbols: std.StringArrayHashMapUnmanaged(ExternSymbol) = .{},111nonlazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},
112/// Table of all extern lazy symbols, indexed by name.112/// Table of all extern lazy symbols, indexed by name.
113extern_lazy_symbols: std.StringArrayHashMapUnmanaged(ExternSymbol) = .{},113lazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},
114114
115local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},115locals_free_list: std.ArrayListUnmanaged(u32) = .{},
116global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},116globals_free_list: std.ArrayListUnmanaged(u32) = .{},
117offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},117offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
118118
119stub_helper_stubs_start_off: ?u64 = null,119stub_helper_stubs_start_off: ?u64 = null,
...@@ -122,8 +122,8 @@ stub_helper_stubs_start_off: ?u64 = null,...@@ -122,8 +122,8 @@ stub_helper_stubs_start_off: ?u64 = null,
122string_table: std.ArrayListUnmanaged(u8) = .{},122string_table: std.ArrayListUnmanaged(u8) = .{},
123string_table_directory: std.StringHashMapUnmanaged(u32) = .{},123string_table_directory: std.StringHashMapUnmanaged(u32) = .{},
124124
125/// Table of trampolines to the actual symbols in __text section.125/// Table of GOT entries.
126offset_table: std.ArrayListUnmanaged(u64) = .{},126offset_table: std.ArrayListUnmanaged(GOTEntry) = .{},
127127
128error_flags: File.ErrorFlags = File.ErrorFlags{},128error_flags: File.ErrorFlags = File.ErrorFlags{},
129129
...@@ -154,14 +154,19 @@ string_table_needs_relocation: bool = false,...@@ -154,14 +154,19 @@ string_table_needs_relocation: bool = false,
154/// allocate a fresh text block, which will have ideal capacity, and then grow it154/// allocate a fresh text block, which will have ideal capacity, and then grow it
155/// by 1 byte. It will then have -1 overcapacity.155/// by 1 byte. It will then have -1 overcapacity.
156text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},156text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
157
157/// Pointer to the last allocated text block158/// Pointer to the last allocated text block
158last_text_block: ?*TextBlock = null,159last_text_block: ?*TextBlock = null,
160
159/// A list of all PIE fixups required for this run of the linker.161/// A list of all PIE fixups required for this run of the linker.
160/// Warning, this is currently NOT thread-safe. See the TODO below.162/// Warning, this is currently NOT thread-safe. See the TODO below.
161/// TODO Move this list inside `updateDecl` where it should be allocated163/// TODO Move this list inside `updateDecl` where it should be allocated
162/// prior to calling `generateSymbol`, and then immediately deallocated164/// prior to calling `generateSymbol`, and then immediately deallocated
163/// rather than sitting in the global scope.165/// rather than sitting in the global scope.
164pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{},166/// TODO We should also rewrite this using generic relocations common to all
167/// backends.
168pie_fixups: std.ArrayListUnmanaged(PIEFixup) = .{},
169
165/// A list of all stub (extern decls) fixups required for this run of the linker.170/// A list of all stub (extern decls) fixups required for this run of the linker.
166/// Warning, this is currently NOT thread-safe. See the TODO below.171/// Warning, this is currently NOT thread-safe. See the TODO below.
167/// TODO Move this list inside `updateDecl` where it should be allocated172/// TODO Move this list inside `updateDecl` where it should be allocated
...@@ -169,14 +174,42 @@ pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{},...@@ -169,14 +174,42 @@ pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{},
169/// rather than sitting in the global scope.174/// rather than sitting in the global scope.
170stub_fixups: std.ArrayListUnmanaged(StubFixup) = .{},175stub_fixups: std.ArrayListUnmanaged(StubFixup) = .{},
171176
172pub const PieFixup = struct {177pub const GOTEntry = struct {
173 /// Target address we wanted to address in absolute terms.178 /// GOT entry can either be a local pointer or an extern (nonlazy) import.
174 address: u64,179 kind: enum {
175 /// Where in the byte stream we should perform the fixup.180 Local,
176 start: usize,181 Extern,
177 /// The length of the byte stream. For x86_64, this will be182 },
178 /// variable. For aarch64, it will be fixed at 4 bytes.183
179 len: usize,184 /// Id to the macho.nlist_64 from the respective table: either locals or nonlazy imports.
185 /// TODO I'm more and more inclined to just manage a single, max two symbol tables
186 /// rather than 4 as we currently do, but I'll follow up in the future PR.
187 symbol: u32,
188
189 /// Index of this entry in the GOT.
190 index: u32,
191};
192
193pub const Import = struct {
194 /// MachO symbol table entry.
195 symbol: macho.nlist_64,
196
197 /// Id of the dynamic library where the specified entries can be found.
198 dylib_ordinal: i64,
199
200 /// Index of this import within the import list.
201 index: u32,
202};
203
204pub const PIEFixup = struct {
205 /// Target VM address of this relocation.
206 target_addr: u64,
207
208 /// Offset within the byte stream.
209 offset: usize,
210
211 /// Size of the relocation.
212 size: usize,
180};213};
181214
182pub const StubFixup = struct {215pub const StubFixup = struct {
...@@ -260,9 +293,9 @@ pub const TextBlock = struct {...@@ -260,9 +293,9 @@ pub const TextBlock = struct {
260 /// File offset relocation happens transparently, so it is not included in293 /// File offset relocation happens transparently, so it is not included in
261 /// this calculation.294 /// this calculation.
262 fn capacity(self: TextBlock, macho_file: MachO) u64 {295 fn capacity(self: TextBlock, macho_file: MachO) u64 {
263 const self_sym = macho_file.local_symbols.items[self.local_sym_index];296 const self_sym = macho_file.locals.items[self.local_sym_index];
264 if (self.next) |next| {297 if (self.next) |next| {
265 const next_sym = macho_file.local_symbols.items[next.local_sym_index];298 const next_sym = macho_file.locals.items[next.local_sym_index];
266 return next_sym.n_value - self_sym.n_value;299 return next_sym.n_value - self_sym.n_value;
267 } else {300 } else {
268 // We are the last block.301 // We are the last block.
...@@ -274,8 +307,8 @@ pub const TextBlock = struct {...@@ -274,8 +307,8 @@ pub const TextBlock = struct {
274 fn freeListEligible(self: TextBlock, macho_file: MachO) bool {307 fn freeListEligible(self: TextBlock, macho_file: MachO) bool {
275 // No need to keep a free list node for the last block.308 // No need to keep a free list node for the last block.
276 const next = self.next orelse return false;309 const next = self.next orelse return false;
277 const self_sym = macho_file.local_symbols.items[self.local_sym_index];310 const self_sym = macho_file.locals.items[self.local_sym_index];
278 const next_sym = macho_file.local_symbols.items[next.local_sym_index];311 const next_sym = macho_file.locals.items[next.local_sym_index];
279 const cap = next_sym.n_value - self_sym.n_value;312 const cap = next_sym.n_value - self_sym.n_value;
280 const ideal_cap = padToIdeal(self.size);313 const ideal_cap = padToIdeal(self.size);
281 if (cap <= ideal_cap) return false;314 if (cap <= ideal_cap) return false;
...@@ -344,7 +377,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -344,7 +377,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
344 };377 };
345378
346 // Index 0 is always a null symbol.379 // Index 0 is always a null symbol.
347 try self.local_symbols.append(allocator, .{380 try self.locals.append(allocator, .{
348 .n_strx = 0,381 .n_strx = 0,
349 .n_type = 0,382 .n_type = 0,
350 .n_sect = 0,383 .n_sect = 0,
...@@ -600,7 +633,74 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -600,7 +633,74 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
600 if (!mem.eql(u8, the_object_path, full_out_path)) {633 if (!mem.eql(u8, the_object_path, full_out_path)) {
601 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});634 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
602 }635 }
603 } else {636 } else outer: {
637 const use_zld = blk: {
638 if (self.base.options.is_native_os and self.base.options.system_linker_hack) {
639 // If the user forces the use of ld64, make sure we are running native!
640 break :blk false;
641 }
642
643 if (self.base.options.target.cpu.arch == .aarch64) {
644 // On aarch64, always use zld.
645 break :blk true;
646 }
647
648 if (self.base.options.link_libcpp or
649 self.base.options.output_mode == .Lib or
650 self.base.options.linker_script != null)
651 {
652 // Fallback to LLD in this handful of cases on x86_64 only.
653 break :blk false;
654 }
655
656 break :blk true;
657 };
658
659 if (use_zld) {
660 var zld = Zld.init(self.base.allocator);
661 defer zld.deinit();
662 zld.arch = target.cpu.arch;
663
664 var input_files = std.ArrayList([]const u8).init(self.base.allocator);
665 defer input_files.deinit();
666 // Positional arguments to the linker such as object files.
667 try input_files.appendSlice(self.base.options.objects);
668 for (comp.c_object_table.items()) |entry| {
669 try input_files.append(entry.key.status.success.object_path);
670 }
671 if (module_obj_path) |p| {
672 try input_files.append(p);
673 }
674 try input_files.append(comp.compiler_rt_static_lib.?.full_object_path);
675 // libc++ dep
676 if (self.base.options.link_libcpp) {
677 try input_files.append(comp.libcxxabi_static_lib.?.full_object_path);
678 try input_files.append(comp.libcxx_static_lib.?.full_object_path);
679 }
680
681 if (self.base.options.verbose_link) {
682 var argv = std.ArrayList([]const u8).init(self.base.allocator);
683 defer argv.deinit();
684
685 try argv.append("zig");
686 try argv.append("ld");
687
688 try argv.ensureCapacity(input_files.items.len);
689 for (input_files.items) |f| {
690 argv.appendAssumeCapacity(f);
691 }
692
693 try argv.append("-o");
694 try argv.append(full_out_path);
695
696 Compilation.dump_argv(argv.items);
697 }
698
699 try zld.link(input_files.items, full_out_path);
700
701 break :outer;
702 }
703
604 // Create an LLD command line and invoke it.704 // Create an LLD command line and invoke it.
605 var argv = std.ArrayList([]const u8).init(self.base.allocator);705 var argv = std.ArrayList([]const u8).init(self.base.allocator);
606 defer argv.deinit();706 defer argv.deinit();
...@@ -871,119 +971,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -871,119 +971,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
871 log.warn("unexpected LLD stderr:\n{s}", .{stderr});971 log.warn("unexpected LLD stderr:\n{s}", .{stderr});
872 }972 }
873 }973 }
874
875 // At this stage, LLD has done its job. It is time to patch the resultant
876 // binaries up!
877 const out_file = try directory.handle.openFile(self.base.options.emit.?.sub_path, .{ .write = true });
878 try self.parseFromFile(out_file);
879
880 if (self.libsystem_cmd_index == null and self.header.?.filetype == macho.MH_EXECUTE) {
881 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
882 const text_section = text_segment.sections.items[self.text_section_index.?];
883 const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64);
884 const needed_size = padToIdeal(@sizeOf(macho.linkedit_data_command));
885
886 if (needed_size + after_last_cmd_offset > text_section.offset) {
887 log.err("Unable to extend padding between the end of load commands and start of __text section.", .{});
888 log.err("Re-run the linker with '-headerpad 0x{x}' option if available, or", .{needed_size});
889 log.err("fall back to the system linker by exporting 'ZIG_SYSTEM_LINKER_HACK=1'.", .{});
890 return error.NotEnoughPadding;
891 }
892
893 // Calculate next available dylib ordinal.
894 const next_ordinal = blk: {
895 var ordinal: u32 = 1;
896 for (self.load_commands.items) |cmd| {
897 switch (cmd) {
898 .Dylib => ordinal += 1,
899 else => {},
900 }
901 }
902 break :blk ordinal;
903 };
904
905 // Add load dylib load command
906 self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len);
907 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
908 u64,
909 @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH),
910 @sizeOf(u64),
911 ));
912 // TODO Find a way to work out runtime version from the OS version triple stored in std.Target.
913 // In the meantime, we're gonna hardcode to the minimum compatibility version of 0.0.0.
914 const min_version = 0x0;
915 var dylib_cmd = emptyGenericCommandWithData(macho.dylib_command{
916 .cmd = macho.LC_LOAD_DYLIB,
917 .cmdsize = cmdsize,
918 .dylib = .{
919 .name = @sizeOf(macho.dylib_command),
920 .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files
921 .current_version = min_version,
922 .compatibility_version = min_version,
923 },
924 });
925 dylib_cmd.data = try self.base.allocator.alloc(u8, cmdsize - dylib_cmd.inner.dylib.name);
926 mem.set(u8, dylib_cmd.data, 0);
927 mem.copy(u8, dylib_cmd.data, mem.spanZ(LIB_SYSTEM_PATH));
928 try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd });
929 self.header_dirty = true;
930 self.load_commands_dirty = true;
931
932 if (self.symtab_cmd_index == null or self.dysymtab_cmd_index == null) {
933 log.err("Incomplete Mach-O binary: no LC_SYMTAB or LC_DYSYMTAB load command found!", .{});
934 log.err("Without the symbol table, it is not possible to patch up the binary for cross-compilation.", .{});
935 return error.NoSymbolTableFound;
936 }
937
938 // Patch dyld info
939 try self.fixupBindInfo(next_ordinal);
940 try self.fixupLazyBindInfo(next_ordinal);
941
942 // Write updated load commands and the header
943 try self.writeLoadCommands();
944 try self.writeHeader();
945
946 assert(!self.header_dirty);
947 assert(!self.load_commands_dirty);
948 }
949 if (self.code_signature_cmd_index == null) outer: {
950 if (target.cpu.arch != .aarch64) break :outer; // This is currently needed only for aarch64 targets.
951 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
952 const text_section = text_segment.sections.items[self.text_section_index.?];
953 const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64);
954 const needed_size = padToIdeal(@sizeOf(macho.linkedit_data_command));
955
956 if (needed_size + after_last_cmd_offset > text_section.offset) {
957 log.err("Unable to extend padding between the end of load commands and start of __text section.", .{});
958 log.err("Re-run the linker with '-headerpad 0x{x}' option if available, or", .{needed_size});
959 log.err("fall back to the system linker by exporting 'ZIG_SYSTEM_LINKER_HACK=1'.", .{});
960 return error.NotEnoughPadding;
961 }
962
963 // Add code signature load command
964 self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);
965 try self.load_commands.append(self.base.allocator, .{
966 .LinkeditData = .{
967 .cmd = macho.LC_CODE_SIGNATURE,
968 .cmdsize = @sizeOf(macho.linkedit_data_command),
969 .dataoff = 0,
970 .datasize = 0,
971 },
972 });
973 self.header_dirty = true;
974 self.load_commands_dirty = true;
975
976 // Pad out space for code signature
977 try self.writeCodeSignaturePadding();
978 // Write updated load commands and the header
979 try self.writeLoadCommands();
980 try self.writeHeader();
981 // Generate adhoc code signature
982 try self.writeCodeSignature();
983
984 assert(!self.header_dirty);
985 assert(!self.load_commands_dirty);
986 }
987 }974 }
988 }975 }
989976
...@@ -1019,14 +1006,14 @@ pub fn deinit(self: *MachO) void {...@@ -1019,14 +1006,14 @@ pub fn deinit(self: *MachO) void {
1019 if (self.d_sym) |*ds| {1006 if (self.d_sym) |*ds| {
1020 ds.deinit(self.base.allocator);1007 ds.deinit(self.base.allocator);
1021 }1008 }
1022 for (self.extern_lazy_symbols.items()) |*entry| {1009 for (self.lazy_imports.items()) |*entry| {
1023 self.base.allocator.free(entry.key);1010 self.base.allocator.free(entry.key);
1024 }1011 }
1025 self.extern_lazy_symbols.deinit(self.base.allocator);1012 self.lazy_imports.deinit(self.base.allocator);
1026 for (self.extern_nonlazy_symbols.items()) |*entry| {1013 for (self.nonlazy_imports.items()) |*entry| {
1027 self.base.allocator.free(entry.key);1014 self.base.allocator.free(entry.key);
1028 }1015 }
1029 self.extern_nonlazy_symbols.deinit(self.base.allocator);1016 self.nonlazy_imports.deinit(self.base.allocator);
1030 self.pie_fixups.deinit(self.base.allocator);1017 self.pie_fixups.deinit(self.base.allocator);
1031 self.stub_fixups.deinit(self.base.allocator);1018 self.stub_fixups.deinit(self.base.allocator);
1032 self.text_block_free_list.deinit(self.base.allocator);1019 self.text_block_free_list.deinit(self.base.allocator);
...@@ -1040,10 +1027,10 @@ pub fn deinit(self: *MachO) void {...@@ -1040,10 +1027,10 @@ pub fn deinit(self: *MachO) void {
1040 }1027 }
1041 self.string_table_directory.deinit(self.base.allocator);1028 self.string_table_directory.deinit(self.base.allocator);
1042 self.string_table.deinit(self.base.allocator);1029 self.string_table.deinit(self.base.allocator);
1043 self.global_symbols.deinit(self.base.allocator);1030 self.globals.deinit(self.base.allocator);
1044 self.global_symbol_free_list.deinit(self.base.allocator);1031 self.globals_free_list.deinit(self.base.allocator);
1045 self.local_symbols.deinit(self.base.allocator);1032 self.locals.deinit(self.base.allocator);
1046 self.local_symbol_free_list.deinit(self.base.allocator);1033 self.locals_free_list.deinit(self.base.allocator);
1047 for (self.load_commands.items) |*lc| {1034 for (self.load_commands.items) |*lc| {
1048 lc.deinit(self.base.allocator);1035 lc.deinit(self.base.allocator);
1049 }1036 }
...@@ -1098,7 +1085,7 @@ fn shrinkTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64) vo...@@ -1098,7 +1085,7 @@ fn shrinkTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64) vo
1098}1085}
10991086
1100fn growTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {1087fn growTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
1101 const sym = self.local_symbols.items[text_block.local_sym_index];1088 const sym = self.locals.items[text_block.local_sym_index];
1102 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;1089 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;
1103 const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);1090 const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);
1104 if (!need_realloc) return sym.n_value;1091 if (!need_realloc) return sym.n_value;
...@@ -1108,34 +1095,41 @@ fn growTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alig...@@ -1108,34 +1095,41 @@ fn growTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alig
1108pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {1095pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
1109 if (decl.link.macho.local_sym_index != 0) return;1096 if (decl.link.macho.local_sym_index != 0) return;
11101097
1111 try self.local_symbols.ensureCapacity(self.base.allocator, self.local_symbols.items.len + 1);1098 try self.locals.ensureCapacity(self.base.allocator, self.locals.items.len + 1);
1112 try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);1099 try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
11131100
1114 if (self.local_symbol_free_list.popOrNull()) |i| {1101 if (self.locals_free_list.popOrNull()) |i| {
1115 log.debug("reusing symbol index {d} for {s}", .{ i, decl.name });1102 log.debug("reusing symbol index {d} for {s}", .{ i, decl.name });
1116 decl.link.macho.local_sym_index = i;1103 decl.link.macho.local_sym_index = i;
1117 } else {1104 } else {
1118 log.debug("allocating symbol index {d} for {s}", .{ self.local_symbols.items.len, decl.name });1105 log.debug("allocating symbol index {d} for {s}", .{ self.locals.items.len, decl.name });
1119 decl.link.macho.local_sym_index = @intCast(u32, self.local_symbols.items.len);1106 decl.link.macho.local_sym_index = @intCast(u32, self.locals.items.len);
1120 _ = self.local_symbols.addOneAssumeCapacity();1107 _ = self.locals.addOneAssumeCapacity();
1121 }1108 }
11221109
1123 if (self.offset_table_free_list.popOrNull()) |i| {1110 if (self.offset_table_free_list.popOrNull()) |i| {
1111 log.debug("reusing offset table entry index {d} for {s}", .{ i, decl.name });
1124 decl.link.macho.offset_table_index = i;1112 decl.link.macho.offset_table_index = i;
1125 } else {1113 } else {
1114 log.debug("allocating offset table entry index {d} for {s}", .{ self.offset_table.items.len, decl.name });
1126 decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len);1115 decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len);
1127 _ = self.offset_table.addOneAssumeCapacity();1116 _ = self.offset_table.addOneAssumeCapacity();
1128 self.offset_table_count_dirty = true;1117 self.offset_table_count_dirty = true;
1118 self.rebase_info_dirty = true;
1129 }1119 }
11301120
1131 self.local_symbols.items[decl.link.macho.local_sym_index] = .{1121 self.locals.items[decl.link.macho.local_sym_index] = .{
1132 .n_strx = 0,1122 .n_strx = 0,
1133 .n_type = 0,1123 .n_type = 0,
1134 .n_sect = 0,1124 .n_sect = 0,
1135 .n_desc = 0,1125 .n_desc = 0,
1136 .n_value = 0,1126 .n_value = 0,
1137 };1127 };
1138 self.offset_table.items[decl.link.macho.offset_table_index] = 0;1128 self.offset_table.items[decl.link.macho.offset_table_index] = .{
1129 .kind = .Local,
1130 .symbol = decl.link.macho.local_sym_index,
1131 .index = decl.link.macho.offset_table_index,
1132 };
1139}1133}
11401134
1141pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {1135pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
...@@ -1178,8 +1172,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1178,8 +1172,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1178 .externally_managed => |x| x,1172 .externally_managed => |x| x,
1179 .appended => code_buffer.items,1173 .appended => code_buffer.items,
1180 .fail => |em| {1174 .fail => |em| {
1181 // Clear any PIE fixups and stub fixups for this decl.1175 // Clear any PIE fixups for this decl.
1182 self.pie_fixups.shrinkRetainingCapacity(0);1176 self.pie_fixups.shrinkRetainingCapacity(0);
1177 // Clear any stub fixups for this decl.
1183 self.stub_fixups.shrinkRetainingCapacity(0);1178 self.stub_fixups.shrinkRetainingCapacity(0);
1184 decl.analysis = .codegen_failure;1179 decl.analysis = .codegen_failure;
1185 try module.failed_decls.put(module.gpa, decl, em);1180 try module.failed_decls.put(module.gpa, decl, em);
...@@ -1189,7 +1184,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1189,7 +1184,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
11891184
1190 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);1185 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
1191 assert(decl.link.macho.local_sym_index != 0); // Caller forgot to call allocateDeclIndexes()1186 assert(decl.link.macho.local_sym_index != 0); // Caller forgot to call allocateDeclIndexes()
1192 const symbol = &self.local_symbols.items[decl.link.macho.local_sym_index];1187 const symbol = &self.locals.items[decl.link.macho.local_sym_index];
11931188
1194 if (decl.link.macho.size != 0) {1189 if (decl.link.macho.size != 0) {
1195 const capacity = decl.link.macho.capacity(self.*);1190 const capacity = decl.link.macho.capacity(self.*);
...@@ -1198,9 +1193,12 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1198,9 +1193,12 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1198 const vaddr = try self.growTextBlock(&decl.link.macho, code.len, required_alignment);1193 const vaddr = try self.growTextBlock(&decl.link.macho, code.len, required_alignment);
1199 log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr });1194 log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr });
1200 if (vaddr != symbol.n_value) {1195 if (vaddr != symbol.n_value) {
1201 symbol.n_value = vaddr;
1202 log.debug(" (writing new offset table entry)", .{});1196 log.debug(" (writing new offset table entry)", .{});
1203 self.offset_table.items[decl.link.macho.offset_table_index] = vaddr;1197 self.offset_table.items[decl.link.macho.offset_table_index] = .{
1198 .kind = .Local,
1199 .symbol = decl.link.macho.local_sym_index,
1200 .index = decl.link.macho.offset_table_index,
1201 };
1204 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);1202 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
1205 }1203 }
1206 } else if (code.len < decl.link.macho.size) {1204 } else if (code.len < decl.link.macho.size) {
...@@ -1229,7 +1227,11 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1229,7 +1227,11 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1229 .n_desc = 0,1227 .n_desc = 0,
1230 .n_value = addr,1228 .n_value = addr,
1231 };1229 };
1232 self.offset_table.items[decl.link.macho.offset_table_index] = addr;1230 self.offset_table.items[decl.link.macho.offset_table_index] = .{
1231 .kind = .Local,
1232 .symbol = decl.link.macho.local_sym_index,
1233 .index = decl.link.macho.offset_table_index,
1234 };
12331235
1234 try self.writeLocalSymbol(decl.link.macho.local_sym_index);1236 try self.writeLocalSymbol(decl.link.macho.local_sym_index);
1235 if (self.d_sym) |*ds|1237 if (self.d_sym) |*ds|
...@@ -1237,30 +1239,48 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1237,30 +1239,48 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1237 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);1239 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
1238 }1240 }
12391241
1240 // Perform PIE fixups (if any)1242 // Calculate displacements to target addr (if any).
1241 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1242 const got_section = text_segment.sections.items[self.got_section_index.?];
1243 while (self.pie_fixups.popOrNull()) |fixup| {1243 while (self.pie_fixups.popOrNull()) |fixup| {
1244 const target_addr = fixup.address;1244 assert(fixup.size == 4);
1245 const this_addr = symbol.n_value + fixup.start;1245 const this_addr = symbol.n_value + fixup.offset;
1246 const target_addr = fixup.target_addr;
1247
1246 switch (self.base.options.target.cpu.arch) {1248 switch (self.base.options.target.cpu.arch) {
1247 .x86_64 => {1249 .x86_64 => {
1248 assert(target_addr >= this_addr + fixup.len);1250 const displacement = try math.cast(u32, target_addr - this_addr - 4);
1249 const displacement = try math.cast(u32, target_addr - this_addr - fixup.len);1251 mem.writeIntLittle(u32, code_buffer.items[fixup.offset..][0..4], displacement);
1250 var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)];
1251 mem.writeIntSliceLittle(u32, placeholder, displacement);
1252 },1252 },
1253 .aarch64 => {1253 .aarch64 => {
1254 assert(target_addr >= this_addr);1254 // TODO optimize instruction based on jump length (use ldr(literal) + nop if possible).
1255 const displacement = try math.cast(u27, target_addr - this_addr);1255 {
1256 var placeholder = code_buffer.items[fixup.start..][0..fixup.len];1256 const inst = code_buffer.items[fixup.offset..][0..4];
1257 mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.b(@as(i28, displacement)).toU32());1257 var parsed = mem.bytesAsValue(meta.TagPayload(
1258 aarch64.Instruction,
1259 aarch64.Instruction.PCRelativeAddress,
1260 ), inst);
1261 const this_page = @intCast(i32, this_addr >> 12);
1262 const target_page = @intCast(i32, target_addr >> 12);
1263 const pages = @bitCast(u21, @intCast(i21, target_page - this_page));
1264 parsed.immhi = @truncate(u19, pages >> 2);
1265 parsed.immlo = @truncate(u2, pages);
1266 }
1267 {
1268 const inst = code_buffer.items[fixup.offset + 4 ..][0..4];
1269 var parsed = mem.bytesAsValue(meta.TagPayload(
1270 aarch64.Instruction,
1271 aarch64.Instruction.LoadStoreRegister,
1272 ), inst);
1273 const narrowed = @truncate(u12, target_addr);
1274 const offset = try math.divExact(u12, narrowed, 8);
1275 parsed.offset = offset;
1276 }
1258 },1277 },
1259 else => unreachable, // unsupported target architecture1278 else => unreachable, // unsupported target architecture
1260 }1279 }
1261 }1280 }
12621281
1263 // Resolve stubs (if any)1282 // Resolve stubs (if any)
1283 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1264 const stubs = text_segment.sections.items[self.stubs_section_index.?];1284 const stubs = text_segment.sections.items[self.stubs_section_index.?];
1265 for (self.stub_fixups.items) |fixup| {1285 for (self.stub_fixups.items) |fixup| {
1266 const stub_addr = stubs.addr + fixup.symbol * stubs.reserved2;1286 const stub_addr = stubs.addr + fixup.symbol * stubs.reserved2;
...@@ -1285,9 +1305,6 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1285,9 +1305,6 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1285 try self.writeStubInStubHelper(fixup.symbol);1305 try self.writeStubInStubHelper(fixup.symbol);
1286 try self.writeLazySymbolPointer(fixup.symbol);1306 try self.writeLazySymbolPointer(fixup.symbol);
12871307
1288 const extern_sym = &self.extern_lazy_symbols.items()[fixup.symbol].value;
1289 extern_sym.segment = self.data_segment_cmd_index.?;
1290 extern_sym.offset = fixup.symbol * @sizeOf(u64);
1291 self.rebase_info_dirty = true;1308 self.rebase_info_dirty = true;
1292 self.lazy_binding_info_dirty = true;1309 self.lazy_binding_info_dirty = true;
1293 }1310 }
...@@ -1329,9 +1346,9 @@ pub fn updateDeclExports(...@@ -1329,9 +1346,9 @@ pub fn updateDeclExports(
1329 const tracy = trace(@src());1346 const tracy = trace(@src());
1330 defer tracy.end();1347 defer tracy.end();
13311348
1332 try self.global_symbols.ensureCapacity(self.base.allocator, self.global_symbols.items.len + exports.len);1349 try self.globals.ensureCapacity(self.base.allocator, self.globals.items.len + exports.len);
1333 if (decl.link.macho.local_sym_index == 0) return;1350 if (decl.link.macho.local_sym_index == 0) return;
1334 const decl_sym = &self.local_symbols.items[decl.link.macho.local_sym_index];1351 const decl_sym = &self.locals.items[decl.link.macho.local_sym_index];
13351352
1336 for (exports) |exp| {1353 for (exports) |exp| {
1337 if (exp.options.section) |section_name| {1354 if (exp.options.section) |section_name| {
...@@ -1364,7 +1381,7 @@ pub fn updateDeclExports(...@@ -1364,7 +1381,7 @@ pub fn updateDeclExports(
1364 };1381 };
1365 const n_type = decl_sym.n_type | macho.N_EXT;1382 const n_type = decl_sym.n_type | macho.N_EXT;
1366 if (exp.link.macho.sym_index) |i| {1383 if (exp.link.macho.sym_index) |i| {
1367 const sym = &self.global_symbols.items[i];1384 const sym = &self.globals.items[i];
1368 sym.* = .{1385 sym.* = .{
1369 .n_strx = try self.updateString(sym.n_strx, exp.options.name),1386 .n_strx = try self.updateString(sym.n_strx, exp.options.name),
1370 .n_type = n_type,1387 .n_type = n_type,
...@@ -1374,12 +1391,12 @@ pub fn updateDeclExports(...@@ -1374,12 +1391,12 @@ pub fn updateDeclExports(
1374 };1391 };
1375 } else {1392 } else {
1376 const name_str_index = try self.makeString(exp.options.name);1393 const name_str_index = try self.makeString(exp.options.name);
1377 const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {1394 const i = if (self.globals_free_list.popOrNull()) |i| i else blk: {
1378 _ = self.global_symbols.addOneAssumeCapacity();1395 _ = self.globals.addOneAssumeCapacity();
1379 self.export_info_dirty = true;1396 self.export_info_dirty = true;
1380 break :blk self.global_symbols.items.len - 1;1397 break :blk self.globals.items.len - 1;
1381 };1398 };
1382 self.global_symbols.items[i] = .{1399 self.globals.items[i] = .{
1383 .n_strx = name_str_index,1400 .n_strx = name_str_index,
1384 .n_type = n_type,1401 .n_type = n_type,
1385 .n_sect = @intCast(u8, self.text_section_index.?) + 1,1402 .n_sect = @intCast(u8, self.text_section_index.?) + 1,
...@@ -1394,18 +1411,18 @@ pub fn updateDeclExports(...@@ -1394,18 +1411,18 @@ pub fn updateDeclExports(
13941411
1395pub fn deleteExport(self: *MachO, exp: Export) void {1412pub fn deleteExport(self: *MachO, exp: Export) void {
1396 const sym_index = exp.sym_index orelse return;1413 const sym_index = exp.sym_index orelse return;
1397 self.global_symbol_free_list.append(self.base.allocator, sym_index) catch {};1414 self.globals_free_list.append(self.base.allocator, sym_index) catch {};
1398 self.global_symbols.items[sym_index].n_type = 0;1415 self.globals.items[sym_index].n_type = 0;
1399}1416}
14001417
1401pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {1418pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {
1402 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.1419 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
1403 self.freeTextBlock(&decl.link.macho);1420 self.freeTextBlock(&decl.link.macho);
1404 if (decl.link.macho.local_sym_index != 0) {1421 if (decl.link.macho.local_sym_index != 0) {
1405 self.local_symbol_free_list.append(self.base.allocator, decl.link.macho.local_sym_index) catch {};1422 self.locals_free_list.append(self.base.allocator, decl.link.macho.local_sym_index) catch {};
1406 self.offset_table_free_list.append(self.base.allocator, decl.link.macho.offset_table_index) catch {};1423 self.offset_table_free_list.append(self.base.allocator, decl.link.macho.offset_table_index) catch {};
14071424
1408 self.local_symbols.items[decl.link.macho.local_sym_index].n_type = 0;1425 self.locals.items[decl.link.macho.local_sym_index].n_type = 0;
14091426
1410 decl.link.macho.local_sym_index = 0;1427 decl.link.macho.local_sym_index = 0;
1411 }1428 }
...@@ -1413,7 +1430,7 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {...@@ -1413,7 +1430,7 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {
14131430
1414pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {1431pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {
1415 assert(decl.link.macho.local_sym_index != 0);1432 assert(decl.link.macho.local_sym_index != 0);
1416 return self.local_symbols.items[decl.link.macho.local_sym_index].n_value;1433 return self.locals.items[decl.link.macho.local_sym_index].n_value;
1417}1434}
14181435
1419pub fn populateMissingMetadata(self: *MachO) !void {1436pub fn populateMissingMetadata(self: *MachO) !void {
...@@ -1553,39 +1570,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1553,39 +1570,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1553 self.header_dirty = true;1570 self.header_dirty = true;
1554 self.load_commands_dirty = true;1571 self.load_commands_dirty = true;
1555 }1572 }
1556 if (self.got_section_index == null) {
1557 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1558 self.got_section_index = @intCast(u16, text_segment.sections.items.len);
1559
1560 const alignment: u2 = switch (self.base.options.target.cpu.arch) {
1561 .x86_64 => 0,
1562 .aarch64 => 2,
1563 else => unreachable, // unhandled architecture type
1564 };
1565 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
1566 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
1567 const off = text_segment.findFreeSpace(needed_size, @alignOf(u64), self.header_pad);
1568 assert(off + needed_size <= text_segment.inner.fileoff + text_segment.inner.filesize); // TODO Must expand __TEXT segment.
1569
1570 log.debug("found __ziggot section free space 0x{x} to 0x{x}", .{ off, off + needed_size });
1571
1572 try text_segment.addSection(self.base.allocator, .{
1573 .sectname = makeStaticString("__ziggot"),
1574 .segname = makeStaticString("__TEXT"),
1575 .addr = text_segment.inner.vmaddr + off,
1576 .size = needed_size,
1577 .offset = @intCast(u32, off),
1578 .@"align" = alignment,
1579 .reloff = 0,
1580 .nreloc = 0,
1581 .flags = flags,
1582 .reserved1 = 0,
1583 .reserved2 = 0,
1584 .reserved3 = 0,
1585 });
1586 self.header_dirty = true;
1587 self.load_commands_dirty = true;
1588 }
1589 if (self.stubs_section_index == null) {1573 if (self.stubs_section_index == null) {
1590 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;1574 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1591 self.stubs_section_index = @intCast(u16, text_segment.sections.items.len);1575 self.stubs_section_index = @intCast(u16, text_segment.sections.items.len);
...@@ -1597,7 +1581,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1597,7 +1581,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1597 };1581 };
1598 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {1582 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {
1599 .x86_64 => 6,1583 .x86_64 => 6,
1600 .aarch64 => 2 * @sizeOf(u32),1584 .aarch64 => 3 * @sizeOf(u32),
1601 else => unreachable, // unhandled architecture type1585 else => unreachable, // unhandled architecture type
1602 };1586 };
1603 const flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;1587 const flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
...@@ -1686,9 +1670,9 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1686,9 +1670,9 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1686 self.header_dirty = true;1670 self.header_dirty = true;
1687 self.load_commands_dirty = true;1671 self.load_commands_dirty = true;
1688 }1672 }
1689 if (self.data_got_section_index == null) {1673 if (self.got_section_index == null) {
1690 const dc_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;1674 const dc_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
1691 self.data_got_section_index = @intCast(u16, dc_segment.sections.items.len);1675 self.got_section_index = @intCast(u16, dc_segment.sections.items.len);
16921676
1693 const flags = macho.S_NON_LAZY_SYMBOL_POINTERS;1677 const flags = macho.S_NON_LAZY_SYMBOL_POINTERS;
1694 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;1678 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
...@@ -2060,12 +2044,12 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -2060,12 +2044,12 @@ pub fn populateMissingMetadata(self: *MachO) !void {
2060 self.header_dirty = true;2044 self.header_dirty = true;
2061 self.load_commands_dirty = true;2045 self.load_commands_dirty = true;
2062 }2046 }
2063 if (!self.extern_nonlazy_symbols.contains("dyld_stub_binder")) {2047 if (!self.nonlazy_imports.contains("dyld_stub_binder")) {
2064 const index = @intCast(u32, self.extern_nonlazy_symbols.items().len);2048 const index = @intCast(u32, self.nonlazy_imports.items().len);
2065 const name = try self.base.allocator.dupe(u8, "dyld_stub_binder");2049 const name = try self.base.allocator.dupe(u8, "dyld_stub_binder");
2066 const offset = try self.makeString("dyld_stub_binder");2050 const offset = try self.makeString("dyld_stub_binder");
2067 try self.extern_nonlazy_symbols.putNoClobber(self.base.allocator, name, .{2051 try self.nonlazy_imports.putNoClobber(self.base.allocator, name, .{
2068 .inner = .{2052 .symbol = .{
2069 .n_strx = offset,2053 .n_strx = offset,
2070 .n_type = std.macho.N_UNDF | std.macho.N_EXT,2054 .n_type = std.macho.N_UNDF | std.macho.N_EXT,
2071 .n_sect = 0,2055 .n_sect = 0,
...@@ -2073,68 +2057,19 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -2073,68 +2057,19 @@ pub fn populateMissingMetadata(self: *MachO) !void {
2073 .n_value = 0,2057 .n_value = 0,
2074 },2058 },
2075 .dylib_ordinal = 1, // TODO this is currently hardcoded.2059 .dylib_ordinal = 1, // TODO this is currently hardcoded.
2076 .segment = self.data_const_segment_cmd_index.?,2060 .index = index,
2077 .offset = index * @sizeOf(u64),2061 });
2062 const off_index = @intCast(u32, self.offset_table.items.len);
2063 try self.offset_table.append(self.base.allocator, .{
2064 .kind = .Extern,
2065 .symbol = index,
2066 .index = off_index,
2078 });2067 });
2068 try self.writeOffsetTableEntry(off_index);
2079 self.binding_info_dirty = true;2069 self.binding_info_dirty = true;
2080 }2070 }
2081 if (self.stub_helper_stubs_start_off == null) {2071 if (self.stub_helper_stubs_start_off == null) {
2082 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;2072 try self.writeStubHelperPreamble();
2083 const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?];
2084 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2085 const data = &data_segment.sections.items[self.data_section_index.?];
2086 const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2087 const got = &data_const_segment.sections.items[self.data_got_section_index.?];
2088 switch (self.base.options.target.cpu.arch) {
2089 .x86_64 => {
2090 const code_size = 15;
2091 var code: [code_size]u8 = undefined;
2092 // lea %r11, [rip + disp]
2093 code[0] = 0x4c;
2094 code[1] = 0x8d;
2095 code[2] = 0x1d;
2096 {
2097 const displacement = try math.cast(u32, data.addr - stub_helper.addr - 7);
2098 mem.writeIntLittle(u32, code[3..7], displacement);
2099 }
2100 // push %r11
2101 code[7] = 0x41;
2102 code[8] = 0x53;
2103 // jmp [rip + disp]
2104 code[9] = 0xff;
2105 code[10] = 0x25;
2106 {
2107 const displacement = try math.cast(u32, got.addr - stub_helper.addr - code_size);
2108 mem.writeIntLittle(u32, code[11..], displacement);
2109 }
2110 self.stub_helper_stubs_start_off = stub_helper.offset + code_size;
2111 try self.base.file.?.pwriteAll(&code, stub_helper.offset);
2112 },
2113 .aarch64 => {
2114 var code: [4 * @sizeOf(u32)]u8 = undefined;
2115 {
2116 const displacement = try math.cast(i21, data.addr - stub_helper.addr);
2117 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, displacement).toU32());
2118 }
2119 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.stp(
2120 .x16,
2121 .x17,
2122 aarch64.Register.sp,
2123 aarch64.Instruction.LoadStorePairOffset.pre_index(-16),
2124 ).toU32());
2125 {
2126 const displacement = try math.divExact(u64, got.addr - stub_helper.addr - 2 * @sizeOf(u32), 4);
2127 const literal = try math.cast(u19, displacement);
2128 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.ldr(.x16, .{
2129 .literal = literal,
2130 }).toU32());
2131 }
2132 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.br(.x16).toU32());
2133 self.stub_helper_stubs_start_off = stub_helper.offset + 4 * @sizeOf(u32);
2134 try self.base.file.?.pwriteAll(&code, stub_helper.offset);
2135 },
2136 else => unreachable,
2137 }
2138 }2073 }
2139}2074}
21402075
...@@ -2159,7 +2094,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,...@@ -2159,7 +2094,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
2159 const big_block = self.text_block_free_list.items[i];2094 const big_block = self.text_block_free_list.items[i];
2160 // We now have a pointer to a live text block that has too much capacity.2095 // We now have a pointer to a live text block that has too much capacity.
2161 // Is it enough that we could fit this new text block?2096 // Is it enough that we could fit this new text block?
2162 const sym = self.local_symbols.items[big_block.local_sym_index];2097 const sym = self.locals.items[big_block.local_sym_index];
2163 const capacity = big_block.capacity(self.*);2098 const capacity = big_block.capacity(self.*);
2164 const ideal_capacity = padToIdeal(capacity);2099 const ideal_capacity = padToIdeal(capacity);
2165 const ideal_capacity_end_vaddr = sym.n_value + ideal_capacity;2100 const ideal_capacity_end_vaddr = sym.n_value + ideal_capacity;
...@@ -2190,7 +2125,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,...@@ -2190,7 +2125,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
2190 }2125 }
2191 break :blk new_start_vaddr;2126 break :blk new_start_vaddr;
2192 } else if (self.last_text_block) |last| {2127 } else if (self.last_text_block) |last| {
2193 const last_symbol = self.local_symbols.items[last.local_sym_index];2128 const last_symbol = self.locals.items[last.local_sym_index];
2194 // TODO We should pad out the excess capacity with NOPs. For executables,2129 // TODO We should pad out the excess capacity with NOPs. For executables,
2195 // no padding seems to be OK, but it will probably not be for objects.2130 // no padding seems to be OK, but it will probably not be for objects.
2196 const ideal_capacity = padToIdeal(last.size);2131 const ideal_capacity = padToIdeal(last.size);
...@@ -2288,12 +2223,12 @@ fn updateString(self: *MachO, old_str_off: u32, new_name: []const u8) !u32 {...@@ -2288,12 +2223,12 @@ fn updateString(self: *MachO, old_str_off: u32, new_name: []const u8) !u32 {
2288}2223}
22892224
2290pub fn addExternSymbol(self: *MachO, name: []const u8) !u32 {2225pub fn addExternSymbol(self: *MachO, name: []const u8) !u32 {
2291 const index = @intCast(u32, self.extern_lazy_symbols.items().len);2226 const index = @intCast(u32, self.lazy_imports.items().len);
2292 const offset = try self.makeString(name);2227 const offset = try self.makeString(name);
2293 const sym_name = try self.base.allocator.dupe(u8, name);2228 const sym_name = try self.base.allocator.dupe(u8, name);
2294 const dylib_ordinal = 1; // TODO this is now hardcoded, since we only support libSystem.2229 const dylib_ordinal = 1; // TODO this is now hardcoded, since we only support libSystem.
2295 try self.extern_lazy_symbols.putNoClobber(self.base.allocator, sym_name, .{2230 try self.lazy_imports.putNoClobber(self.base.allocator, sym_name, .{
2296 .inner = .{2231 .symbol = .{
2297 .n_strx = offset,2232 .n_strx = offset,
2298 .n_type = macho.N_UNDF | macho.N_EXT,2233 .n_type = macho.N_UNDF | macho.N_EXT,
2299 .n_sect = 0,2234 .n_sect = 0,
...@@ -2301,6 +2236,7 @@ pub fn addExternSymbol(self: *MachO, name: []const u8) !u32 {...@@ -2301,6 +2236,7 @@ pub fn addExternSymbol(self: *MachO, name: []const u8) !u32 {
2301 .n_value = 0,2236 .n_value = 0,
2302 },2237 },
2303 .dylib_ordinal = dylib_ordinal,2238 .dylib_ordinal = dylib_ordinal,
2239 .index = index,
2304 });2240 });
2305 log.debug("adding new extern symbol '{s}' with dylib ordinal '{}'", .{ name, dylib_ordinal });2241 log.debug("adding new extern symbol '{s}' with dylib ordinal '{}'", .{ name, dylib_ordinal });
2306 return index;2242 return index;
...@@ -2459,41 +2395,29 @@ fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16, sta...@@ -2459,41 +2395,29 @@ fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16, sta
2459}2395}
24602396
2461fn writeOffsetTableEntry(self: *MachO, index: usize) !void {2397fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
2462 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;2398 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2463 const sect = &text_segment.sections.items[self.got_section_index.?];2399 const sect = &seg.sections.items[self.got_section_index.?];
2464 const off = sect.offset + @sizeOf(u64) * index;2400 const off = sect.offset + @sizeOf(u64) * index;
2465 const vmaddr = sect.addr + @sizeOf(u64) * index;
24662401
2467 if (self.offset_table_count_dirty) {2402 if (self.offset_table_count_dirty) {
2468 // TODO relocate.2403 // TODO relocate.
2469 self.offset_table_count_dirty = false;2404 self.offset_table_count_dirty = false;
2470 }2405 }
24712406
2472 var code: [8]u8 = undefined;2407 const got_entry = self.offset_table.items[index];
2473 switch (self.base.options.target.cpu.arch) {2408 const sym = blk: {
2474 .x86_64 => {2409 switch (got_entry.kind) {
2475 const pos_symbol_off = try math.cast(u31, vmaddr - self.offset_table.items[index] + 7);2410 .Local => {
2476 const symbol_off = @bitCast(u32, @as(i32, pos_symbol_off) * -1);2411 break :blk self.locals.items[got_entry.symbol];
2477 // lea %rax, [rip - disp]2412 },
2478 code[0] = 0x48;2413 .Extern => {
2479 code[1] = 0x8D;2414 break :blk self.nonlazy_imports.items()[got_entry.symbol].value.symbol;
2480 code[2] = 0x5;2415 },
2481 mem.writeIntLittle(u32, code[3..7], symbol_off);2416 }
2482 // ret2417 };
2483 code[7] = 0xC3;2418 const sym_name = self.getString(sym.n_strx);
2484 },2419 log.debug("writing offset table entry [ 0x{x} => 0x{x} ({s}) ]", .{ off, sym.n_value, sym_name });
2485 .aarch64 => {2420 try self.base.file.?.pwriteAll(mem.asBytes(&sym.n_value), off);
2486 const pos_symbol_off = try math.cast(u20, vmaddr - self.offset_table.items[index]);
2487 const symbol_off = @as(i21, pos_symbol_off) * -1;
2488 // adr x0, #-disp
2489 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x0, symbol_off).toU32());
2490 // ret x28
2491 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ret(.x28).toU32());
2492 },
2493 else => unreachable, // unsupported target architecture
2494 }
2495 log.debug("writing offset table entry 0x{x} at 0x{x}", .{ self.offset_table.items[index], off });
2496 try self.base.file.?.pwriteAll(&code, off);
2497}2421}
24982422
2499fn writeLazySymbolPointer(self: *MachO, index: u32) !void {2423fn writeLazySymbolPointer(self: *MachO, index: u32) !void {
...@@ -2516,6 +2440,133 @@ fn writeLazySymbolPointer(self: *MachO, index: u32) !void {...@@ -2516,6 +2440,133 @@ fn writeLazySymbolPointer(self: *MachO, index: u32) !void {
2516 try self.base.file.?.pwriteAll(&buf, off);2440 try self.base.file.?.pwriteAll(&buf, off);
2517}2441}
25182442
2443fn writeStubHelperPreamble(self: *MachO) !void {
2444 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2445 const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?];
2446 const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2447 const got = &data_const_segment.sections.items[self.got_section_index.?];
2448 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2449 const data = &data_segment.sections.items[self.data_section_index.?];
2450
2451 switch (self.base.options.target.cpu.arch) {
2452 .x86_64 => {
2453 const code_size = 15;
2454 var code: [code_size]u8 = undefined;
2455 // lea %r11, [rip + disp]
2456 code[0] = 0x4c;
2457 code[1] = 0x8d;
2458 code[2] = 0x1d;
2459 {
2460 const target_addr = data.addr;
2461 const displacement = try math.cast(u32, target_addr - stub_helper.addr - 7);
2462 mem.writeIntLittle(u32, code[3..7], displacement);
2463 }
2464 // push %r11
2465 code[7] = 0x41;
2466 code[8] = 0x53;
2467 // jmp [rip + disp]
2468 code[9] = 0xff;
2469 code[10] = 0x25;
2470 {
2471 const displacement = try math.cast(u32, got.addr - stub_helper.addr - code_size);
2472 mem.writeIntLittle(u32, code[11..], displacement);
2473 }
2474 try self.base.file.?.pwriteAll(&code, stub_helper.offset);
2475 self.stub_helper_stubs_start_off = stub_helper.offset + code_size;
2476 },
2477 .aarch64 => {
2478 var code: [6 * @sizeOf(u32)]u8 = undefined;
2479
2480 data_blk_outer: {
2481 const this_addr = stub_helper.addr;
2482 const target_addr = data.addr;
2483 data_blk: {
2484 const displacement = math.cast(i21, target_addr - this_addr) catch |_| break :data_blk;
2485 // adr x17, disp
2486 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, displacement).toU32());
2487 // nop
2488 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32());
2489 break :data_blk_outer;
2490 }
2491 data_blk: {
2492 const new_this_addr = this_addr + @sizeOf(u32);
2493 const displacement = math.cast(i21, target_addr - new_this_addr) catch |_| break :data_blk;
2494 // nop
2495 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32());
2496 // adr x17, disp
2497 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.adr(.x17, displacement).toU32());
2498 break :data_blk_outer;
2499 }
2500 // Jump is too big, replace adr with adrp and add.
2501 const this_page = @intCast(i32, this_addr >> 12);
2502 const target_page = @intCast(i32, target_addr >> 12);
2503 const pages = @intCast(i21, target_page - this_page);
2504 // adrp x17, pages
2505 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x17, pages).toU32());
2506 const narrowed = @truncate(u12, target_addr);
2507 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.add(.x17, .x17, narrowed, false).toU32());
2508 }
2509
2510 // stp x16, x17, [sp, #-16]!
2511 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.stp(
2512 .x16,
2513 .x17,
2514 aarch64.Register.sp,
2515 aarch64.Instruction.LoadStorePairOffset.pre_index(-16),
2516 ).toU32());
2517
2518 binder_blk_outer: {
2519 const this_addr = stub_helper.addr + 3 * @sizeOf(u32);
2520 const target_addr = got.addr;
2521 binder_blk: {
2522 const displacement = math.divExact(u64, target_addr - this_addr, 4) catch |_| break :binder_blk;
2523 const literal = math.cast(u18, displacement) catch |_| break :binder_blk;
2524 // ldr x16, label
2525 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.ldr(.x16, .{
2526 .literal = literal,
2527 }).toU32());
2528 // nop
2529 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.nop().toU32());
2530 break :binder_blk_outer;
2531 }
2532 binder_blk: {
2533 const new_this_addr = this_addr + @sizeOf(u32);
2534 const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch |_| break :binder_blk;
2535 const literal = math.cast(u18, displacement) catch |_| break :binder_blk;
2536 // nop
2537 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.nop().toU32());
2538 // ldr x16, label
2539 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{
2540 .literal = literal,
2541 }).toU32());
2542 break :binder_blk_outer;
2543 }
2544 // Jump is too big, replace ldr with adrp and ldr(register).
2545 const this_page = @intCast(i32, this_addr >> 12);
2546 const target_page = @intCast(i32, target_addr >> 12);
2547 const pages = @intCast(i21, target_page - this_page);
2548 // adrp x16, pages
2549 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.adrp(.x16, pages).toU32());
2550 const narrowed = @truncate(u12, target_addr);
2551 const offset = try math.divExact(u12, narrowed, 8);
2552 // ldr x16, x16, offset
2553 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{
2554 .register = .{
2555 .rn = .x16,
2556 .offset = aarch64.Instruction.LoadStoreOffset.imm(offset),
2557 },
2558 }).toU32());
2559 }
2560
2561 // br x16
2562 mem.writeIntLittle(u32, code[20..24], aarch64.Instruction.br(.x16).toU32());
2563 try self.base.file.?.pwriteAll(&code, stub_helper.offset);
2564 self.stub_helper_stubs_start_off = stub_helper.offset + code.len;
2565 },
2566 else => unreachable,
2567 }
2568}
2569
2519fn writeStub(self: *MachO, index: u32) !void {2570fn writeStub(self: *MachO, index: u32) !void {
2520 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;2571 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2521 const stubs = text_segment.sections.items[self.stubs_section_index.?];2572 const stubs = text_segment.sections.items[self.stubs_section_index.?];
...@@ -2525,9 +2576,12 @@ fn writeStub(self: *MachO, index: u32) !void {...@@ -2525,9 +2576,12 @@ fn writeStub(self: *MachO, index: u32) !void {
2525 const stub_off = stubs.offset + index * stubs.reserved2;2576 const stub_off = stubs.offset + index * stubs.reserved2;
2526 const stub_addr = stubs.addr + index * stubs.reserved2;2577 const stub_addr = stubs.addr + index * stubs.reserved2;
2527 const la_ptr_addr = la_symbol_ptr.addr + index * @sizeOf(u64);2578 const la_ptr_addr = la_symbol_ptr.addr + index * @sizeOf(u64);
2579
2528 log.debug("writing stub at 0x{x}", .{stub_off});2580 log.debug("writing stub at 0x{x}", .{stub_off});
2581
2529 var code = try self.base.allocator.alloc(u8, stubs.reserved2);2582 var code = try self.base.allocator.alloc(u8, stubs.reserved2);
2530 defer self.base.allocator.free(code);2583 defer self.base.allocator.free(code);
2584
2531 switch (self.base.options.target.cpu.arch) {2585 switch (self.base.options.target.cpu.arch) {
2532 .x86_64 => {2586 .x86_64 => {
2533 assert(la_ptr_addr >= stub_addr + stubs.reserved2);2587 assert(la_ptr_addr >= stub_addr + stubs.reserved2);
...@@ -2539,12 +2593,50 @@ fn writeStub(self: *MachO, index: u32) !void {...@@ -2539,12 +2593,50 @@ fn writeStub(self: *MachO, index: u32) !void {
2539 },2593 },
2540 .aarch64 => {2594 .aarch64 => {
2541 assert(la_ptr_addr >= stub_addr);2595 assert(la_ptr_addr >= stub_addr);
2542 const displacement = try math.divExact(u64, la_ptr_addr - stub_addr, 4);2596 outer: {
2543 const literal = try math.cast(u19, displacement);2597 const this_addr = stub_addr;
2544 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{2598 const target_addr = la_ptr_addr;
2545 .literal = literal,2599 inner: {
2546 }).toU32());2600 const displacement = math.divExact(u64, target_addr - this_addr, 4) catch |_| break :inner;
2547 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.br(.x16).toU32());2601 const literal = math.cast(u18, displacement) catch |_| break :inner;
2602 // ldr x16, literal
2603 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{
2604 .literal = literal,
2605 }).toU32());
2606 // nop
2607 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32());
2608 break :outer;
2609 }
2610 inner: {
2611 const new_this_addr = this_addr + @sizeOf(u32);
2612 const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch |_| break :inner;
2613 const literal = math.cast(u18, displacement) catch |_| break :inner;
2614 // nop
2615 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32());
2616 // ldr x16, literal
2617 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(.x16, .{
2618 .literal = literal,
2619 }).toU32());
2620 break :outer;
2621 }
2622 // Use adrp followed by ldr(register).
2623 const this_page = @intCast(i32, this_addr >> 12);
2624 const target_page = @intCast(i32, target_addr >> 12);
2625 const pages = @intCast(i21, target_page - this_page);
2626 // adrp x16, pages
2627 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x16, pages).toU32());
2628 const narrowed = @truncate(u12, target_addr);
2629 const offset = try math.divExact(u12, narrowed, 8);
2630 // ldr x16, x16, offset
2631 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(.x16, .{
2632 .register = .{
2633 .rn = .x16,
2634 .offset = aarch64.Instruction.LoadStoreOffset.imm(offset),
2635 },
2636 }).toU32());
2637 }
2638 // br x16
2639 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.br(.x16).toU32());
2548 },2640 },
2549 else => unreachable,2641 else => unreachable,
2550 }2642 }
...@@ -2561,8 +2653,10 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {...@@ -2561,8 +2653,10 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {
2561 else => unreachable,2653 else => unreachable,
2562 };2654 };
2563 const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size;2655 const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size;
2656
2564 var code = try self.base.allocator.alloc(u8, stub_size);2657 var code = try self.base.allocator.alloc(u8, stub_size);
2565 defer self.base.allocator.free(code);2658 defer self.base.allocator.free(code);
2659
2566 switch (self.base.options.target.cpu.arch) {2660 switch (self.base.options.target.cpu.arch) {
2567 .x86_64 => {2661 .x86_64 => {
2568 const displacement = try math.cast(2662 const displacement = try math.cast(
...@@ -2577,12 +2671,19 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {...@@ -2577,12 +2671,19 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {
2577 mem.writeIntLittle(u32, code[6..][0..4], @bitCast(u32, displacement));2671 mem.writeIntLittle(u32, code[6..][0..4], @bitCast(u32, displacement));
2578 },2672 },
2579 .aarch64 => {2673 .aarch64 => {
2580 const displacement = try math.cast(i28, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4);2674 const literal = blk: {
2675 const div_res = try math.divExact(u64, stub_size - @sizeOf(u32), 4);
2676 break :blk try math.cast(u18, div_res);
2677 };
2678 // ldr w16, literal
2581 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{2679 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{
2582 .literal = @divExact(stub_size - @sizeOf(u32), 4),2680 .literal = literal,
2583 }).toU32());2681 }).toU32());
2682 const displacement = try math.cast(i28, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4);
2683 // b disp
2584 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(displacement).toU32());2684 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(displacement).toU32());
2585 mem.writeIntLittle(u32, code[8..12], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.2685 // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
2686 mem.writeIntLittle(u32, code[8..12], 0x0);
2586 },2687 },
2587 else => unreachable,2688 else => unreachable,
2588 }2689 }
...@@ -2591,9 +2692,9 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {...@@ -2591,9 +2692,9 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {
25912692
2592fn relocateSymbolTable(self: *MachO) !void {2693fn relocateSymbolTable(self: *MachO) !void {
2593 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;2694 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2594 const nlocals = self.local_symbols.items.len;2695 const nlocals = self.locals.items.len;
2595 const nglobals = self.global_symbols.items.len;2696 const nglobals = self.globals.items.len;
2596 const nundefs = self.extern_lazy_symbols.items().len + self.extern_nonlazy_symbols.items().len;2697 const nundefs = self.lazy_imports.items().len + self.nonlazy_imports.items().len;
2597 const nsyms = nlocals + nglobals + nundefs;2698 const nsyms = nlocals + nglobals + nundefs;
25982699
2599 if (symtab.nsyms < nsyms) {2700 if (symtab.nsyms < nsyms) {
...@@ -2628,7 +2729,7 @@ fn writeLocalSymbol(self: *MachO, index: usize) !void {...@@ -2628,7 +2729,7 @@ fn writeLocalSymbol(self: *MachO, index: usize) !void {
2628 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;2729 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2629 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;2730 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;
2630 log.debug("writing local symbol {} at 0x{x}", .{ index, off });2731 log.debug("writing local symbol {} at 0x{x}", .{ index, off });
2631 try self.base.file.?.pwriteAll(mem.asBytes(&self.local_symbols.items[index]), off);2732 try self.base.file.?.pwriteAll(mem.asBytes(&self.locals.items[index]), off);
2632}2733}
26332734
2634fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {2735fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
...@@ -2637,18 +2738,18 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {...@@ -2637,18 +2738,18 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
26372738
2638 try self.relocateSymbolTable();2739 try self.relocateSymbolTable();
2639 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;2740 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2640 const nlocals = self.local_symbols.items.len;2741 const nlocals = self.locals.items.len;
2641 const nglobals = self.global_symbols.items.len;2742 const nglobals = self.globals.items.len;
26422743
2643 const nundefs = self.extern_lazy_symbols.items().len + self.extern_nonlazy_symbols.items().len;2744 const nundefs = self.lazy_imports.items().len + self.nonlazy_imports.items().len;
2644 var undefs = std.ArrayList(macho.nlist_64).init(self.base.allocator);2745 var undefs = std.ArrayList(macho.nlist_64).init(self.base.allocator);
2645 defer undefs.deinit();2746 defer undefs.deinit();
2646 try undefs.ensureCapacity(nundefs);2747 try undefs.ensureCapacity(nundefs);
2647 for (self.extern_lazy_symbols.items()) |entry| {2748 for (self.lazy_imports.items()) |entry| {
2648 undefs.appendAssumeCapacity(entry.value.inner);2749 undefs.appendAssumeCapacity(entry.value.symbol);
2649 }2750 }
2650 for (self.extern_nonlazy_symbols.items()) |entry| {2751 for (self.nonlazy_imports.items()) |entry| {
2651 undefs.appendAssumeCapacity(entry.value.inner);2752 undefs.appendAssumeCapacity(entry.value.symbol);
2652 }2753 }
26532754
2654 const locals_off = symtab.symoff;2755 const locals_off = symtab.symoff;
...@@ -2657,7 +2758,7 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {...@@ -2657,7 +2758,7 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
2657 const globals_off = locals_off + locals_size;2758 const globals_off = locals_off + locals_size;
2658 const globals_size = nglobals * @sizeOf(macho.nlist_64);2759 const globals_size = nglobals * @sizeOf(macho.nlist_64);
2659 log.debug("writing global symbols from 0x{x} to 0x{x}", .{ globals_off, globals_size + globals_off });2760 log.debug("writing global symbols from 0x{x} to 0x{x}", .{ globals_off, globals_size + globals_off });
2660 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.global_symbols.items), globals_off);2761 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.globals.items), globals_off);
26612762
2662 const undefs_off = globals_off + globals_size;2763 const undefs_off = globals_off + globals_size;
2663 const undefs_size = nundefs * @sizeOf(macho.nlist_64);2764 const undefs_size = nundefs * @sizeOf(macho.nlist_64);
...@@ -2683,15 +2784,15 @@ fn writeIndirectSymbolTable(self: *MachO) !void {...@@ -2683,15 +2784,15 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
2683 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;2784 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2684 const stubs = &text_segment.sections.items[self.stubs_section_index.?];2785 const stubs = &text_segment.sections.items[self.stubs_section_index.?];
2685 const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;2786 const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2686 const got = &data_const_seg.sections.items[self.data_got_section_index.?];2787 const got = &data_const_seg.sections.items[self.got_section_index.?];
2687 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;2788 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2688 const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];2789 const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
2689 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;2790 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
26902791
2691 const lazy = self.extern_lazy_symbols.items();2792 const lazy = self.lazy_imports.items();
2692 const nonlazy = self.extern_nonlazy_symbols.items();2793 const got_entries = self.offset_table.items;
2693 const allocated_size = self.allocatedSizeLinkedit(dysymtab.indirectsymoff);2794 const allocated_size = self.allocatedSizeLinkedit(dysymtab.indirectsymoff);
2694 const nindirectsyms = @intCast(u32, lazy.len * 2 + nonlazy.len);2795 const nindirectsyms = @intCast(u32, lazy.len * 2 + got_entries.len);
2695 const needed_size = @intCast(u32, nindirectsyms * @sizeOf(u32));2796 const needed_size = @intCast(u32, nindirectsyms * @sizeOf(u32));
26962797
2697 if (needed_size > allocated_size) {2798 if (needed_size > allocated_size) {
...@@ -2710,20 +2811,27 @@ fn writeIndirectSymbolTable(self: *MachO) !void {...@@ -2710,20 +2811,27 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
2710 var writer = stream.writer();2811 var writer = stream.writer();
27112812
2712 stubs.reserved1 = 0;2813 stubs.reserved1 = 0;
2713 for (self.extern_lazy_symbols.items()) |_, i| {2814 for (lazy) |_, i| {
2714 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);2815 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
2715 try writer.writeIntLittle(u32, symtab_idx);2816 try writer.writeIntLittle(u32, symtab_idx);
2716 }2817 }
27172818
2718 const base_id = @intCast(u32, lazy.len);2819 const base_id = @intCast(u32, lazy.len);
2719 got.reserved1 = base_id;2820 got.reserved1 = base_id;
2720 for (self.extern_nonlazy_symbols.items()) |_, i| {2821 for (got_entries) |entry| {
2721 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i + base_id);2822 switch (entry.kind) {
2722 try writer.writeIntLittle(u32, symtab_idx);2823 .Local => {
2824 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
2825 },
2826 .Extern => {
2827 const symtab_idx = @intCast(u32, dysymtab.iundefsym + entry.index + base_id);
2828 try writer.writeIntLittle(u32, symtab_idx);
2829 },
2830 }
2723 }2831 }
27242832
2725 la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, nonlazy.len);2833 la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, got_entries.len);
2726 for (self.extern_lazy_symbols.items()) |_, i| {2834 for (lazy) |_, i| {
2727 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);2835 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
2728 try writer.writeIntLittle(u32, symtab_idx);2836 try writer.writeIntLittle(u32, symtab_idx);
2729 }2837 }
...@@ -2789,7 +2897,7 @@ fn writeCodeSignature(self: *MachO) !void {...@@ -2789,7 +2897,7 @@ fn writeCodeSignature(self: *MachO) !void {
27892897
2790fn writeExportTrie(self: *MachO) !void {2898fn writeExportTrie(self: *MachO) !void {
2791 if (!self.export_info_dirty) return;2899 if (!self.export_info_dirty) return;
2792 if (self.global_symbols.items.len == 0) return;2900 if (self.globals.items.len == 0) return;
27932901
2794 const tracy = trace(@src());2902 const tracy = trace(@src());
2795 defer tracy.end();2903 defer tracy.end();
...@@ -2798,7 +2906,7 @@ fn writeExportTrie(self: *MachO) !void {...@@ -2798,7 +2906,7 @@ fn writeExportTrie(self: *MachO) !void {
2798 defer trie.deinit();2906 defer trie.deinit();
27992907
2800 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;2908 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2801 for (self.global_symbols.items) |symbol| {2909 for (self.globals.items) |symbol| {
2802 // TODO figure out if we should put all global symbols into the export trie2910 // TODO figure out if we should put all global symbols into the export trie
2803 const name = self.getString(symbol.n_strx);2911 const name = self.getString(symbol.n_strx);
2804 assert(symbol.n_value >= text_segment.inner.vmaddr);2912 assert(symbol.n_value >= text_segment.inner.vmaddr);
...@@ -2840,14 +2948,48 @@ fn writeRebaseInfoTable(self: *MachO) !void {...@@ -2840,14 +2948,48 @@ fn writeRebaseInfoTable(self: *MachO) !void {
2840 const tracy = trace(@src());2948 const tracy = trace(@src());
2841 defer tracy.end();2949 defer tracy.end();
28422950
2843 const size = try rebaseInfoSize(self.extern_lazy_symbols.items());2951 var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);
2952 defer pointers.deinit();
2953
2954 if (self.got_section_index) |idx| {
2955 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2956 const sect = seg.sections.items[idx];
2957 const base_offset = sect.addr - seg.inner.vmaddr;
2958 const segment_id = self.data_const_segment_cmd_index.?;
2959
2960 for (self.offset_table.items) |entry| {
2961 if (entry.kind == .Extern) continue;
2962 try pointers.append(.{
2963 .offset = base_offset + entry.index * @sizeOf(u64),
2964 .segment_id = segment_id,
2965 });
2966 }
2967 }
2968
2969 if (self.la_symbol_ptr_section_index) |idx| {
2970 try pointers.ensureCapacity(pointers.items.len + self.lazy_imports.items().len);
2971 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2972 const sect = seg.sections.items[idx];
2973 const base_offset = sect.addr - seg.inner.vmaddr;
2974 const segment_id = self.data_segment_cmd_index.?;
2975
2976 for (self.lazy_imports.items()) |entry| {
2977 pointers.appendAssumeCapacity(.{
2978 .offset = base_offset + entry.value.index * @sizeOf(u64),
2979 .segment_id = segment_id,
2980 });
2981 }
2982 }
2983
2984 std.sort.sort(bind.Pointer, pointers.items, {}, bind.pointerCmp);
2985
2986 const size = try bind.rebaseInfoSize(pointers.items);
2844 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));2987 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
2845 defer self.base.allocator.free(buffer);2988 defer self.base.allocator.free(buffer);
28462989
2847 var stream = std.io.fixedBufferStream(buffer);2990 var stream = std.io.fixedBufferStream(buffer);
2848 try writeRebaseInfo(self.extern_lazy_symbols.items(), stream.writer());2991 try bind.writeRebaseInfo(pointers.items, stream.writer());
28492992
2850 const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2851 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;2993 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2852 const allocated_size = self.allocatedSizeLinkedit(dyld_info.rebase_off);2994 const allocated_size = self.allocatedSizeLinkedit(dyld_info.rebase_off);
2853 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));2995 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
...@@ -2872,14 +3014,34 @@ fn writeBindingInfoTable(self: *MachO) !void {...@@ -2872,14 +3014,34 @@ fn writeBindingInfoTable(self: *MachO) !void {
2872 const tracy = trace(@src());3014 const tracy = trace(@src());
2873 defer tracy.end();3015 defer tracy.end();
28743016
2875 const size = try bindInfoSize(self.extern_nonlazy_symbols.items());3017 var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);
3018 defer pointers.deinit();
3019
3020 if (self.got_section_index) |idx| {
3021 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
3022 const sect = seg.sections.items[idx];
3023 const base_offset = sect.addr - seg.inner.vmaddr;
3024 const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
3025
3026 for (self.offset_table.items) |entry| {
3027 if (entry.kind == .Local) continue;
3028 const import = self.nonlazy_imports.items()[entry.symbol];
3029 try pointers.append(.{
3030 .offset = base_offset + entry.index * @sizeOf(u64),
3031 .segment_id = segment_id,
3032 .dylib_ordinal = import.value.dylib_ordinal,
3033 .name = import.key,
3034 });
3035 }
3036 }
3037
3038 const size = try bind.bindInfoSize(pointers.items);
2876 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));3039 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
2877 defer self.base.allocator.free(buffer);3040 defer self.base.allocator.free(buffer);
28783041
2879 var stream = std.io.fixedBufferStream(buffer);3042 var stream = std.io.fixedBufferStream(buffer);
2880 try writeBindInfo(self.extern_nonlazy_symbols.items(), stream.writer());3043 try bind.writeBindInfo(pointers.items, stream.writer());
28813044
2882 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2883 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;3045 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2884 const allocated_size = self.allocatedSizeLinkedit(dyld_info.bind_off);3046 const allocated_size = self.allocatedSizeLinkedit(dyld_info.bind_off);
2885 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));3047 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
...@@ -2901,14 +3063,36 @@ fn writeBindingInfoTable(self: *MachO) !void {...@@ -2901,14 +3063,36 @@ fn writeBindingInfoTable(self: *MachO) !void {
2901fn writeLazyBindingInfoTable(self: *MachO) !void {3063fn writeLazyBindingInfoTable(self: *MachO) !void {
2902 if (!self.lazy_binding_info_dirty) return;3064 if (!self.lazy_binding_info_dirty) return;
29033065
2904 const size = try lazyBindInfoSize(self.extern_lazy_symbols.items());3066 const tracy = trace(@src());
3067 defer tracy.end();
3068
3069 var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);
3070 defer pointers.deinit();
3071
3072 if (self.la_symbol_ptr_section_index) |idx| {
3073 try pointers.ensureCapacity(self.lazy_imports.items().len);
3074 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
3075 const sect = seg.sections.items[idx];
3076 const base_offset = sect.addr - seg.inner.vmaddr;
3077 const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
3078
3079 for (self.lazy_imports.items()) |entry| {
3080 pointers.appendAssumeCapacity(.{
3081 .offset = base_offset + entry.value.index * @sizeOf(u64),
3082 .segment_id = segment_id,
3083 .dylib_ordinal = entry.value.dylib_ordinal,
3084 .name = entry.key,
3085 });
3086 }
3087 }
3088
3089 const size = try bind.lazyBindInfoSize(pointers.items);
2905 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));3090 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
2906 defer self.base.allocator.free(buffer);3091 defer self.base.allocator.free(buffer);
29073092
2908 var stream = std.io.fixedBufferStream(buffer);3093 var stream = std.io.fixedBufferStream(buffer);
2909 try writeLazyBindInfo(self.extern_lazy_symbols.items(), stream.writer());3094 try bind.writeLazyBindInfo(pointers.items, stream.writer());
29103095
2911 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2912 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;3096 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2913 const allocated_size = self.allocatedSizeLinkedit(dyld_info.lazy_bind_off);3097 const allocated_size = self.allocatedSizeLinkedit(dyld_info.lazy_bind_off);
2914 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));3098 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
...@@ -2929,7 +3113,7 @@ fn writeLazyBindingInfoTable(self: *MachO) !void {...@@ -2929,7 +3113,7 @@ fn writeLazyBindingInfoTable(self: *MachO) !void {
2929}3113}
29303114
2931fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {3115fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
2932 if (self.extern_lazy_symbols.items().len == 0) return;3116 if (self.lazy_imports.items().len == 0) return;
29333117
2934 var stream = std.io.fixedBufferStream(buffer);3118 var stream = std.io.fixedBufferStream(buffer);
2935 var reader = stream.reader();3119 var reader = stream.reader();
...@@ -2975,7 +3159,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -2975,7 +3159,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
2975 else => {},3159 else => {},
2976 }3160 }
2977 }3161 }
2978 assert(self.extern_lazy_symbols.items().len <= offsets.items.len);3162 assert(self.lazy_imports.items().len <= offsets.items.len);
29793163
2980 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {3164 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {
2981 .x86_64 => 10,3165 .x86_64 => 10,
...@@ -2988,7 +3172,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -2988,7 +3172,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
2988 else => unreachable,3172 else => unreachable,
2989 };3173 };
2990 var buf: [@sizeOf(u32)]u8 = undefined;3174 var buf: [@sizeOf(u32)]u8 = undefined;
2991 for (self.extern_lazy_symbols.items()) |_, i| {3175 for (self.lazy_imports.items()) |_, i| {
2992 const placeholder_off = self.stub_helper_stubs_start_off.? + i * stub_size + off;3176 const placeholder_off = self.stub_helper_stubs_start_off.? + i * stub_size + off;
2993 mem.writeIntLittle(u32, &buf, offsets.items[i]);3177 mem.writeIntLittle(u32, &buf, offsets.items[i]);
2994 try self.base.file.?.pwriteAll(&buf, placeholder_off);3178 try self.base.file.?.pwriteAll(&buf, placeholder_off);
...@@ -3102,177 +3286,6 @@ fn writeHeader(self: *MachO) !void {...@@ -3102,177 +3286,6 @@ fn writeHeader(self: *MachO) !void {
3102 self.header_dirty = false;3286 self.header_dirty = false;
3103}3287}
31043288
3105/// Parse MachO contents from existing binary file.
3106fn parseFromFile(self: *MachO, file: fs.File) !void {
3107 self.base.file = file;
3108 var reader = file.reader();
3109 const header = try reader.readStruct(macho.mach_header_64);
3110 try self.load_commands.ensureCapacity(self.base.allocator, header.ncmds);
3111 var i: u16 = 0;
3112 while (i < header.ncmds) : (i += 1) {
3113 const cmd = try LoadCommand.read(self.base.allocator, reader);
3114 switch (cmd.cmd()) {
3115 macho.LC_SEGMENT_64 => {
3116 const x = cmd.Segment;
3117 if (parseAndCmpName(&x.inner.segname, "__PAGEZERO")) {
3118 self.pagezero_segment_cmd_index = i;
3119 } else if (parseAndCmpName(&x.inner.segname, "__LINKEDIT")) {
3120 self.linkedit_segment_cmd_index = i;
3121 } else if (parseAndCmpName(&x.inner.segname, "__TEXT")) {
3122 self.text_segment_cmd_index = i;
3123 for (x.sections.items) |sect, j| {
3124 if (parseAndCmpName(&sect.sectname, "__text")) {
3125 self.text_section_index = @intCast(u16, j);
3126 }
3127 }
3128 } else if (parseAndCmpName(&x.inner.segname, "__DATA")) {
3129 self.data_segment_cmd_index = i;
3130 } else if (parseAndCmpName(&x.inner.segname, "__DATA_CONST")) {
3131 self.data_const_segment_cmd_index = i;
3132 }
3133 },
3134 macho.LC_DYLD_INFO_ONLY => {
3135 self.dyld_info_cmd_index = i;
3136 },
3137 macho.LC_SYMTAB => {
3138 self.symtab_cmd_index = i;
3139 },
3140 macho.LC_DYSYMTAB => {
3141 self.dysymtab_cmd_index = i;
3142 },
3143 macho.LC_LOAD_DYLINKER => {
3144 self.dylinker_cmd_index = i;
3145 },
3146 macho.LC_VERSION_MIN_MACOSX, macho.LC_VERSION_MIN_IPHONEOS, macho.LC_VERSION_MIN_WATCHOS, macho.LC_VERSION_MIN_TVOS => {
3147 self.version_min_cmd_index = i;
3148 },
3149 macho.LC_SOURCE_VERSION => {
3150 self.source_version_cmd_index = i;
3151 },
3152 macho.LC_UUID => {
3153 self.uuid_cmd_index = i;
3154 },
3155 macho.LC_MAIN => {
3156 self.main_cmd_index = i;
3157 },
3158 macho.LC_LOAD_DYLIB => {
3159 const x = cmd.Dylib;
3160 if (parseAndCmpName(x.data, mem.spanZ(LIB_SYSTEM_PATH))) {
3161 self.libsystem_cmd_index = i;
3162 }
3163 },
3164 macho.LC_FUNCTION_STARTS => {
3165 self.function_starts_cmd_index = i;
3166 },
3167 macho.LC_DATA_IN_CODE => {
3168 self.data_in_code_cmd_index = i;
3169 },
3170 macho.LC_CODE_SIGNATURE => {
3171 self.code_signature_cmd_index = i;
3172 },
3173 else => {
3174 log.warn("Unknown load command detected: 0x{x}.", .{cmd.cmd()});
3175 },
3176 }
3177 self.load_commands.appendAssumeCapacity(cmd);
3178 }
3179 self.header = header;
3180}
3181
3182fn parseAndCmpName(name: []const u8, needle: []const u8) bool {
3183 const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len;
3184 return mem.eql(u8, name[0..len], needle);
3185}
3186
3187fn parseSymbolTable(self: *MachO) !void {
3188 const symtab = self.load_commands.items[self.symtab_cmd_index.?].Symtab;
3189 const dysymtab = self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
3190
3191 var buffer = try self.base.allocator.alloc(macho.nlist_64, symtab.nsyms);
3192 defer self.base.allocator.free(buffer);
3193 const nread = try self.base.file.?.preadAll(@ptrCast([*]u8, buffer)[0 .. symtab.nsyms * @sizeOf(macho.nlist_64)], symtab.symoff);
3194 assert(@divExact(nread, @sizeOf(macho.nlist_64)) == buffer.len);
3195
3196 try self.local_symbols.ensureCapacity(self.base.allocator, dysymtab.nlocalsym);
3197 try self.global_symbols.ensureCapacity(self.base.allocator, dysymtab.nextdefsym);
3198 try self.undef_symbols.ensureCapacity(self.base.allocator, dysymtab.nundefsym);
3199
3200 self.local_symbols.appendSliceAssumeCapacity(buffer[dysymtab.ilocalsym .. dysymtab.ilocalsym + dysymtab.nlocalsym]);
3201 self.global_symbols.appendSliceAssumeCapacity(buffer[dysymtab.iextdefsym .. dysymtab.iextdefsym + dysymtab.nextdefsym]);
3202 self.undef_symbols.appendSliceAssumeCapacity(buffer[dysymtab.iundefsym .. dysymtab.iundefsym + dysymtab.nundefsym]);
3203}
3204
3205fn parseStringTable(self: *MachO) !void {
3206 const symtab = self.load_commands.items[self.symtab_cmd_index.?].Symtab;
3207
3208 var buffer = try self.base.allocator.alloc(u8, symtab.strsize);
3209 defer self.base.allocator.free(buffer);
3210 const nread = try self.base.file.?.preadAll(buffer, symtab.stroff);
3211 assert(nread == buffer.len);
3212
3213 try self.string_table.ensureCapacity(self.base.allocator, symtab.strsize);
3214 self.string_table.appendSliceAssumeCapacity(buffer);
3215}
3216
3217fn fixupBindInfo(self: *MachO, dylib_ordinal: u32) !void {
3218 const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
3219 var buffer = try self.base.allocator.alloc(u8, dyld_info.bind_size);
3220 defer self.base.allocator.free(buffer);
3221 const nread = try self.base.file.?.preadAll(buffer, dyld_info.bind_off);
3222 assert(nread == buffer.len);
3223 try self.fixupInfoCommon(buffer, dylib_ordinal);
3224 try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off);
3225}
3226
3227fn fixupLazyBindInfo(self: *MachO, dylib_ordinal: u32) !void {
3228 const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
3229 var buffer = try self.base.allocator.alloc(u8, dyld_info.lazy_bind_size);
3230 defer self.base.allocator.free(buffer);
3231 const nread = try self.base.file.?.preadAll(buffer, dyld_info.lazy_bind_off);
3232 assert(nread == buffer.len);
3233 try self.fixupInfoCommon(buffer, dylib_ordinal);
3234 try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off);
3235}
3236
3237fn fixupInfoCommon(self: *MachO, buffer: []u8, dylib_ordinal: u32) !void {
3238 var stream = std.io.fixedBufferStream(buffer);
3239 var reader = stream.reader();
3240
3241 while (true) {
3242 const inst = reader.readByte() catch |err| switch (err) {
3243 error.EndOfStream => break,
3244 else => return err,
3245 };
3246 const imm: u8 = inst & macho.BIND_IMMEDIATE_MASK;
3247 const opcode: u8 = inst & macho.BIND_OPCODE_MASK;
3248
3249 switch (opcode) {
3250 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
3251 var next = try reader.readByte();
3252 while (next != @as(u8, 0)) {
3253 next = try reader.readByte();
3254 }
3255 },
3256 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
3257 _ = try std.leb.readULEB128(u64, reader);
3258 },
3259 macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => {
3260 // Perform the fixup.
3261 try stream.seekBy(-1);
3262 var writer = stream.writer();
3263 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, dylib_ordinal));
3264 },
3265 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
3266 _ = try std.leb.readULEB128(u64, reader);
3267 },
3268 macho.BIND_OPCODE_SET_ADDEND_SLEB => {
3269 _ = try std.leb.readILEB128(i64, reader);
3270 },
3271 else => {},
3272 }
3273 }
3274}
3275
3276pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {3289pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
3277 // TODO https://github.com/ziglang/zig/issues/12843290 // TODO https://github.com/ziglang/zig/issues/1284
3278 return std.math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch3291 return std.math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch
src/link/MachO/Archive.zig created+256
...@@ -0,0 +1,256 @@
1const Archive = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const fs = std.fs;
6const log = std.log.scoped(.archive);
7const macho = std.macho;
8const mem = std.mem;
9
10const Allocator = mem.Allocator;
11const Object = @import("Object.zig");
12const parseName = @import("Zld.zig").parseName;
13
14usingnamespace @import("commands.zig");
15
16allocator: *Allocator,
17file: fs.File,
18header: ar_hdr,
19name: []u8,
20
21objects: std.ArrayListUnmanaged(Object) = .{},
22
23// Archive files start with the ARMAG identifying string. Then follows a
24// `struct ar_hdr', and as many bytes of member file data as its `ar_size'
25// member indicates, for each member file.
26/// String that begins an archive file.
27const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n";
28/// Size of that string.
29const SARMAG: u4 = 8;
30
31/// String in ar_fmag at the end of each header.
32const ARFMAG: *const [2:0]u8 = "`\n";
33
34const ar_hdr = extern struct {
35 /// Member file name, sometimes / terminated.
36 ar_name: [16]u8,
37
38 /// File date, decimal seconds since Epoch.
39 ar_date: [12]u8,
40
41 /// User ID, in ASCII format.
42 ar_uid: [6]u8,
43
44 /// Group ID, in ASCII format.
45 ar_gid: [6]u8,
46
47 /// File mode, in ASCII octal.
48 ar_mode: [8]u8,
49
50 /// File size, in ASCII decimal.
51 ar_size: [10]u8,
52
53 /// Always contains ARFMAG.
54 ar_fmag: [2]u8,
55
56 const NameOrLength = union(enum) {
57 Name: []const u8,
58 Length: u64,
59 };
60 pub fn nameOrLength(self: ar_hdr) !NameOrLength {
61 const value = getValue(&self.ar_name);
62 const slash_index = mem.indexOf(u8, value, "/") orelse return error.MalformedArchive;
63 const len = value.len;
64 if (slash_index == len - 1) {
65 // Name stored directly
66 return NameOrLength{ .Name = value };
67 } else {
68 // Name follows the header directly and its length is encoded in
69 // the name field.
70 const length = try std.fmt.parseInt(u64, value[slash_index + 1 ..], 10);
71 return NameOrLength{ .Length = length };
72 }
73 }
74
75 pub fn size(self: ar_hdr) !u64 {
76 const value = getValue(&self.ar_size);
77 return std.fmt.parseInt(u64, value, 10);
78 }
79
80 fn getValue(raw: []const u8) []const u8 {
81 return mem.trimRight(u8, raw, &[_]u8{@as(u8, 0x20)});
82 }
83};
84
85pub fn deinit(self: *Archive) void {
86 self.allocator.free(self.name);
87 for (self.objects.items) |*object| {
88 object.deinit();
89 }
90 self.objects.deinit(self.allocator);
91 self.file.close();
92}
93
94/// Caller owns the returned Archive instance and is responsible for calling
95/// `deinit` to free allocated memory.
96pub fn initFromFile(allocator: *Allocator, arch: std.Target.Cpu.Arch, ar_name: []const u8, file: fs.File) !Archive {
97 var reader = file.reader();
98 var magic = try readMagic(allocator, reader);
99 defer allocator.free(magic);
100
101 if (!mem.eql(u8, magic, ARMAG)) {
102 // Reset file cursor.
103 try file.seekTo(0);
104 return error.NotArchive;
105 }
106
107 const header = try reader.readStruct(ar_hdr);
108
109 if (!mem.eql(u8, &header.ar_fmag, ARFMAG))
110 return error.MalformedArchive;
111
112 var embedded_name = try getName(allocator, header, reader);
113 log.debug("parsing archive '{s}' at '{s}'", .{ embedded_name, ar_name });
114 defer allocator.free(embedded_name);
115
116 var name = try allocator.dupe(u8, ar_name);
117 var self = Archive{
118 .allocator = allocator,
119 .file = file,
120 .header = header,
121 .name = name,
122 };
123
124 var object_offsets = try self.readTableOfContents(reader);
125 defer self.allocator.free(object_offsets);
126
127 var i: usize = 1;
128 while (i < object_offsets.len) : (i += 1) {
129 const offset = object_offsets[i];
130 try reader.context.seekTo(offset);
131 try self.readObject(arch, ar_name, reader);
132 }
133
134 return self;
135}
136
137fn readTableOfContents(self: *Archive, reader: anytype) ![]u32 {
138 const symtab_size = try reader.readIntLittle(u32);
139 var symtab = try self.allocator.alloc(u8, symtab_size);
140 defer self.allocator.free(symtab);
141 try reader.readNoEof(symtab);
142
143 const strtab_size = try reader.readIntLittle(u32);
144 var strtab = try self.allocator.alloc(u8, strtab_size);
145 defer self.allocator.free(strtab);
146 try reader.readNoEof(strtab);
147
148 var symtab_stream = std.io.fixedBufferStream(symtab);
149 var symtab_reader = symtab_stream.reader();
150
151 var object_offsets = std.ArrayList(u32).init(self.allocator);
152 try object_offsets.append(0);
153 var last: usize = 0;
154
155 while (true) {
156 const n_strx = symtab_reader.readIntLittle(u32) catch |err| switch (err) {
157 error.EndOfStream => break,
158 else => |e| return e,
159 };
160 const object_offset = try symtab_reader.readIntLittle(u32);
161
162 // TODO Store the table of contents for later reuse.
163
164 // Here, we assume that symbols are NOT sorted in any way, and
165 // they point to objects in sequence.
166 if (object_offsets.items[last] != object_offset) {
167 try object_offsets.append(object_offset);
168 last += 1;
169 }
170 }
171
172 return object_offsets.toOwnedSlice();
173}
174
175fn readObject(self: *Archive, arch: std.Target.Cpu.Arch, ar_name: []const u8, reader: anytype) !void {
176 const object_header = try reader.readStruct(ar_hdr);
177
178 if (!mem.eql(u8, &object_header.ar_fmag, ARFMAG))
179 return error.MalformedArchive;
180
181 var object_name = try getName(self.allocator, object_header, reader);
182 log.debug("extracting object '{s}' from archive '{s}'", .{ object_name, self.name });
183
184 const offset = @intCast(u32, try reader.context.getPos());
185 const header = try reader.readStruct(macho.mach_header_64);
186
187 const this_arch: std.Target.Cpu.Arch = switch (header.cputype) {
188 macho.CPU_TYPE_ARM64 => .aarch64,
189 macho.CPU_TYPE_X86_64 => .x86_64,
190 else => |value| {
191 log.err("unsupported cpu architecture 0x{x}", .{value});
192 return error.UnsupportedCpuArchitecture;
193 },
194 };
195 if (this_arch != arch) {
196 log.err("mismatched cpu architecture: found {s}, expected {s}", .{ this_arch, arch });
197 return error.MismatchedCpuArchitecture;
198 }
199
200 // TODO Implement std.fs.File.clone() or similar.
201 var new_file = try fs.cwd().openFile(ar_name, .{});
202 var object = Object{
203 .allocator = self.allocator,
204 .name = object_name,
205 .ar_name = try mem.dupe(self.allocator, u8, ar_name),
206 .file = new_file,
207 .header = header,
208 };
209
210 try object.readLoadCommands(reader, .{ .offset = offset });
211
212 if (object.symtab_cmd_index != null) {
213 try object.readSymtab();
214 try object.readStrtab();
215 }
216
217 if (object.data_in_code_cmd_index != null) try object.readDataInCode();
218
219 log.debug("\n\n", .{});
220 log.debug("{s} defines symbols", .{object.name});
221 for (object.symtab.items) |sym| {
222 const symname = object.getString(sym.n_strx);
223 log.debug("'{s}': {}", .{ symname, sym });
224 }
225
226 try self.objects.append(self.allocator, object);
227}
228
229fn readMagic(allocator: *Allocator, reader: anytype) ![]u8 {
230 var magic = std.ArrayList(u8).init(allocator);
231 try magic.ensureCapacity(SARMAG);
232 var i: usize = 0;
233 while (i < SARMAG) : (i += 1) {
234 const next = try reader.readByte();
235 magic.appendAssumeCapacity(next);
236 }
237 return magic.toOwnedSlice();
238}
239
240fn getName(allocator: *Allocator, header: ar_hdr, reader: anytype) ![]u8 {
241 const name_or_length = try header.nameOrLength();
242 var name: []u8 = undefined;
243 switch (name_or_length) {
244 .Name => |n| {
245 name = try allocator.dupe(u8, n);
246 },
247 .Length => |len| {
248 var n = try allocator.alloc(u8, len);
249 defer allocator.free(n);
250 try reader.readNoEof(n);
251 const actual_len = mem.indexOfScalar(u8, n, @as(u8, 0));
252 name = try allocator.dupe(u8, n[0..actual_len.?]);
253 },
254 }
255 return name;
256}
src/link/MachO/DebugSymbols.zig+4-4
...@@ -839,8 +839,8 @@ fn findFreeSpaceLinkedit(self: *DebugSymbols, object_size: u64, min_alignment: u...@@ -839,8 +839,8 @@ fn findFreeSpaceLinkedit(self: *DebugSymbols, object_size: u64, min_alignment: u
839839
840fn relocateSymbolTable(self: *DebugSymbols) !void {840fn relocateSymbolTable(self: *DebugSymbols) !void {
841 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;841 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
842 const nlocals = self.base.local_symbols.items.len;842 const nlocals = self.base.locals.items.len;
843 const nglobals = self.base.global_symbols.items.len;843 const nglobals = self.base.globals.items.len;
844 const nsyms = nlocals + nglobals;844 const nsyms = nlocals + nglobals;
845845
846 if (symtab.nsyms < nsyms) {846 if (symtab.nsyms < nsyms) {
...@@ -875,7 +875,7 @@ pub fn writeLocalSymbol(self: *DebugSymbols, index: usize) !void {...@@ -875,7 +875,7 @@ pub fn writeLocalSymbol(self: *DebugSymbols, index: usize) !void {
875 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;875 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
876 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;876 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;
877 log.debug("writing dSym local symbol {} at 0x{x}", .{ index, off });877 log.debug("writing dSym local symbol {} at 0x{x}", .{ index, off });
878 try self.file.pwriteAll(mem.asBytes(&self.base.local_symbols.items[index]), off);878 try self.file.pwriteAll(mem.asBytes(&self.base.locals.items[index]), off);
879}879}
880880
881fn writeStringTable(self: *DebugSymbols) !void {881fn writeStringTable(self: *DebugSymbols) !void {
...@@ -1057,7 +1057,7 @@ pub fn commitDeclDebugInfo(...@@ -1057,7 +1057,7 @@ pub fn commitDeclDebugInfo(
1057 var dbg_info_buffer = &debug_buffers.dbg_info_buffer;1057 var dbg_info_buffer = &debug_buffers.dbg_info_buffer;
1058 var dbg_info_type_relocs = &debug_buffers.dbg_info_type_relocs;1058 var dbg_info_type_relocs = &debug_buffers.dbg_info_type_relocs;
10591059
1060 const symbol = self.base.local_symbols.items[decl.link.macho.local_sym_index];1060 const symbol = self.base.locals.items[decl.link.macho.local_sym_index];
1061 const text_block = &decl.link.macho;1061 const text_block = &decl.link.macho;
1062 // If the Decl is a function, we need to update the __debug_line program.1062 // If the Decl is a function, we need to update the __debug_line program.
1063 const typed_value = decl.typed_value.most_recent.typed_value;1063 const typed_value = decl.typed_value.most_recent.typed_value;
src/link/MachO/Object.zig created+228
...@@ -0,0 +1,228 @@
1const Object = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const fs = std.fs;
6const io = std.io;
7const log = std.log.scoped(.object);
8const macho = std.macho;
9const mem = std.mem;
10
11const Allocator = mem.Allocator;
12const parseName = @import("Zld.zig").parseName;
13
14usingnamespace @import("commands.zig");
15
16allocator: *Allocator,
17file: fs.File,
18name: []u8,
19ar_name: ?[]u8 = null,
20
21header: macho.mach_header_64,
22
23load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
24
25segment_cmd_index: ?u16 = null,
26symtab_cmd_index: ?u16 = null,
27dysymtab_cmd_index: ?u16 = null,
28build_version_cmd_index: ?u16 = null,
29data_in_code_cmd_index: ?u16 = null,
30text_section_index: ?u16 = null,
31
32// __DWARF segment sections
33dwarf_debug_info_index: ?u16 = null,
34dwarf_debug_abbrev_index: ?u16 = null,
35dwarf_debug_str_index: ?u16 = null,
36dwarf_debug_line_index: ?u16 = null,
37dwarf_debug_ranges_index: ?u16 = null,
38
39symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
40strtab: std.ArrayListUnmanaged(u8) = .{},
41
42data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
43
44pub fn deinit(self: *Object) void {
45 for (self.load_commands.items) |*lc| {
46 lc.deinit(self.allocator);
47 }
48 self.load_commands.deinit(self.allocator);
49 self.symtab.deinit(self.allocator);
50 self.strtab.deinit(self.allocator);
51 self.data_in_code_entries.deinit(self.allocator);
52 self.allocator.free(self.name);
53 if (self.ar_name) |v| {
54 self.allocator.free(v);
55 }
56 self.file.close();
57}
58
59/// Caller owns the returned Object instance and is responsible for calling
60/// `deinit` to free allocated memory.
61pub fn initFromFile(allocator: *Allocator, arch: std.Target.Cpu.Arch, name: []const u8, file: fs.File) !Object {
62 var reader = file.reader();
63 const header = try reader.readStruct(macho.mach_header_64);
64
65 if (header.filetype != macho.MH_OBJECT) {
66 // Reset file cursor.
67 try file.seekTo(0);
68 return error.NotObject;
69 }
70
71 const this_arch: std.Target.Cpu.Arch = switch (header.cputype) {
72 macho.CPU_TYPE_ARM64 => .aarch64,
73 macho.CPU_TYPE_X86_64 => .x86_64,
74 else => |value| {
75 log.err("unsupported cpu architecture 0x{x}", .{value});
76 return error.UnsupportedCpuArchitecture;
77 },
78 };
79 if (this_arch != arch) {
80 log.err("mismatched cpu architecture: found {s}, expected {s}", .{ this_arch, arch });
81 return error.MismatchedCpuArchitecture;
82 }
83
84 var self = Object{
85 .allocator = allocator,
86 .name = try allocator.dupe(u8, name),
87 .file = file,
88 .header = header,
89 };
90
91 try self.readLoadCommands(reader, .{});
92
93 if (self.symtab_cmd_index != null) {
94 try self.readSymtab();
95 try self.readStrtab();
96 }
97
98 if (self.data_in_code_cmd_index != null) try self.readDataInCode();
99
100 log.debug("\n\n", .{});
101 log.debug("{s} defines symbols", .{self.name});
102 for (self.symtab.items) |sym| {
103 const symname = self.getString(sym.n_strx);
104 log.debug("'{s}': {}", .{ symname, sym });
105 }
106
107 return self;
108}
109
110pub const ReadOffset = struct {
111 offset: ?u32 = null,
112};
113
114pub fn readLoadCommands(self: *Object, reader: anytype, offset: ReadOffset) !void {
115 const offset_mod = offset.offset orelse 0;
116 try self.load_commands.ensureCapacity(self.allocator, self.header.ncmds);
117
118 var i: u16 = 0;
119 while (i < self.header.ncmds) : (i += 1) {
120 var cmd = try LoadCommand.read(self.allocator, reader);
121 switch (cmd.cmd()) {
122 macho.LC_SEGMENT_64 => {
123 self.segment_cmd_index = i;
124 var seg = cmd.Segment;
125 for (seg.sections.items) |*sect, j| {
126 const index = @intCast(u16, j);
127 const segname = parseName(&sect.segname);
128 const sectname = parseName(&sect.sectname);
129 if (mem.eql(u8, segname, "__DWARF")) {
130 if (mem.eql(u8, sectname, "__debug_info")) {
131 self.dwarf_debug_info_index = index;
132 } else if (mem.eql(u8, sectname, "__debug_abbrev")) {
133 self.dwarf_debug_abbrev_index = index;
134 } else if (mem.eql(u8, sectname, "__debug_str")) {
135 self.dwarf_debug_str_index = index;
136 } else if (mem.eql(u8, sectname, "__debug_line")) {
137 self.dwarf_debug_line_index = index;
138 } else if (mem.eql(u8, sectname, "__debug_ranges")) {
139 self.dwarf_debug_ranges_index = index;
140 }
141 } else if (mem.eql(u8, segname, "__TEXT")) {
142 if (mem.eql(u8, sectname, "__text")) {
143 self.text_section_index = index;
144 }
145 }
146
147 sect.offset += offset_mod;
148 if (sect.reloff > 0)
149 sect.reloff += offset_mod;
150 }
151
152 seg.inner.fileoff += offset_mod;
153 },
154 macho.LC_SYMTAB => {
155 self.symtab_cmd_index = i;
156 cmd.Symtab.symoff += offset_mod;
157 cmd.Symtab.stroff += offset_mod;
158 },
159 macho.LC_DYSYMTAB => {
160 self.dysymtab_cmd_index = i;
161 },
162 macho.LC_BUILD_VERSION => {
163 self.build_version_cmd_index = i;
164 },
165 macho.LC_DATA_IN_CODE => {
166 self.data_in_code_cmd_index = i;
167 },
168 else => {
169 log.debug("Unknown load command detected: 0x{x}.", .{cmd.cmd()});
170 },
171 }
172 self.load_commands.appendAssumeCapacity(cmd);
173 }
174}
175
176pub fn readSymtab(self: *Object) !void {
177 const symtab_cmd = self.load_commands.items[self.symtab_cmd_index.?].Symtab;
178 var buffer = try self.allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms);
179 defer self.allocator.free(buffer);
180 _ = try self.file.preadAll(buffer, symtab_cmd.symoff);
181 try self.symtab.ensureCapacity(self.allocator, symtab_cmd.nsyms);
182 // TODO this align case should not be needed.
183 // Probably a bug in stage1.
184 const slice = @alignCast(@alignOf(macho.nlist_64), mem.bytesAsSlice(macho.nlist_64, buffer));
185 self.symtab.appendSliceAssumeCapacity(slice);
186}
187
188pub fn readStrtab(self: *Object) !void {
189 const symtab_cmd = self.load_commands.items[self.symtab_cmd_index.?].Symtab;
190 var buffer = try self.allocator.alloc(u8, symtab_cmd.strsize);
191 defer self.allocator.free(buffer);
192 _ = try self.file.preadAll(buffer, symtab_cmd.stroff);
193 try self.strtab.ensureCapacity(self.allocator, symtab_cmd.strsize);
194 self.strtab.appendSliceAssumeCapacity(buffer);
195}
196
197pub fn getString(self: *const Object, str_off: u32) []const u8 {
198 assert(str_off < self.strtab.items.len);
199 return mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr + str_off));
200}
201
202pub fn readSection(self: Object, allocator: *Allocator, index: u16) ![]u8 {
203 const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;
204 const sect = seg.sections.items[index];
205 var buffer = try allocator.alloc(u8, sect.size);
206 _ = try self.file.preadAll(buffer, sect.offset);
207 return buffer;
208}
209
210pub fn readDataInCode(self: *Object) !void {
211 const index = self.data_in_code_cmd_index orelse return;
212 const data_in_code = self.load_commands.items[index].LinkeditData;
213
214 var buffer = try self.allocator.alloc(u8, data_in_code.datasize);
215 defer self.allocator.free(buffer);
216
217 _ = try self.file.preadAll(buffer, data_in_code.dataoff);
218
219 var stream = io.fixedBufferStream(buffer);
220 var reader = stream.reader();
221 while (true) {
222 const dice = reader.readStruct(macho.data_in_code_entry) catch |err| switch (err) {
223 error.EndOfStream => break,
224 else => |e| return e,
225 };
226 try self.data_in_code_entries.append(self.allocator, dice);
227 }
228}
src/link/MachO/Zld.zig created+3192
...@@ -0,0 +1,3192 @@
1const Zld = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const dwarf = std.dwarf;
6const leb = std.leb;
7const mem = std.mem;
8const meta = std.meta;
9const fs = std.fs;
10const macho = std.macho;
11const math = std.math;
12const log = std.log.scoped(.zld);
13const aarch64 = @import("../../codegen/aarch64.zig");
14
15const Allocator = mem.Allocator;
16const CodeSignature = @import("CodeSignature.zig");
17const Archive = @import("Archive.zig");
18const Object = @import("Object.zig");
19const Trie = @import("Trie.zig");
20
21usingnamespace @import("commands.zig");
22usingnamespace @import("bind.zig");
23
24allocator: *Allocator,
25
26arch: ?std.Target.Cpu.Arch = null,
27page_size: ?u16 = null,
28file: ?fs.File = null,
29out_path: ?[]const u8 = null,
30
31// TODO Eventually, we will want to keep track of the archives themselves to be able to exclude objects
32// contained within from landing in the final artifact. For now however, since we don't optimise the binary
33// at all, we just move all objects from the archives into the final artifact.
34objects: std.ArrayListUnmanaged(Object) = .{},
35
36load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
37
38pagezero_segment_cmd_index: ?u16 = null,
39text_segment_cmd_index: ?u16 = null,
40data_const_segment_cmd_index: ?u16 = null,
41data_segment_cmd_index: ?u16 = null,
42linkedit_segment_cmd_index: ?u16 = null,
43dyld_info_cmd_index: ?u16 = null,
44symtab_cmd_index: ?u16 = null,
45dysymtab_cmd_index: ?u16 = null,
46dylinker_cmd_index: ?u16 = null,
47libsystem_cmd_index: ?u16 = null,
48data_in_code_cmd_index: ?u16 = null,
49function_starts_cmd_index: ?u16 = null,
50main_cmd_index: ?u16 = null,
51version_min_cmd_index: ?u16 = null,
52source_version_cmd_index: ?u16 = null,
53uuid_cmd_index: ?u16 = null,
54code_signature_cmd_index: ?u16 = null,
55
56// __TEXT segment sections
57text_section_index: ?u16 = null,
58stubs_section_index: ?u16 = null,
59stub_helper_section_index: ?u16 = null,
60text_const_section_index: ?u16 = null,
61cstring_section_index: ?u16 = null,
62
63// __DATA segment sections
64got_section_index: ?u16 = null,
65tlv_section_index: ?u16 = null,
66tlv_data_section_index: ?u16 = null,
67tlv_bss_section_index: ?u16 = null,
68la_symbol_ptr_section_index: ?u16 = null,
69data_const_section_index: ?u16 = null,
70data_section_index: ?u16 = null,
71bss_section_index: ?u16 = null,
72
73locals: std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(Symbol)) = .{},
74exports: std.StringArrayHashMapUnmanaged(macho.nlist_64) = .{},
75nonlazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},
76lazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},
77tlv_bootstrap: ?Import = null,
78threadlocal_offsets: std.ArrayListUnmanaged(u64) = .{},
79local_rebases: std.ArrayListUnmanaged(Pointer) = .{},
80nonlazy_pointers: std.StringArrayHashMapUnmanaged(GotEntry) = .{},
81
82strtab: std.ArrayListUnmanaged(u8) = .{},
83
84stub_helper_stubs_start_off: ?u64 = null,
85
86mappings: std.AutoHashMapUnmanaged(MappingKey, SectionMapping) = .{},
87unhandled_sections: std.AutoHashMapUnmanaged(MappingKey, u0) = .{},
88
89// TODO this will require scanning the relocations at least one to work out
90// the exact amount of local GOT indirections. For the time being, set some
91// default value.
92const max_local_got_indirections: u16 = 1000;
93
94const GotEntry = struct {
95 index: u32,
96 target_addr: u64,
97};
98
99const MappingKey = struct {
100 object_id: u16,
101 source_sect_id: u16,
102};
103
104const SectionMapping = struct {
105 source_sect_id: u16,
106 target_seg_id: u16,
107 target_sect_id: u16,
108 offset: u32,
109};
110
111const Symbol = struct {
112 inner: macho.nlist_64,
113 tt: Type,
114 object_id: u16,
115
116 const Type = enum {
117 Local,
118 WeakGlobal,
119 Global,
120 };
121};
122
123const DebugInfo = struct {
124 inner: dwarf.DwarfInfo,
125 debug_info: []u8,
126 debug_abbrev: []u8,
127 debug_str: []u8,
128 debug_line: []u8,
129 debug_ranges: []u8,
130
131 pub fn parseFromObject(allocator: *Allocator, object: Object) !?DebugInfo {
132 var debug_info = blk: {
133 const index = object.dwarf_debug_info_index orelse return null;
134 break :blk try object.readSection(allocator, index);
135 };
136 var debug_abbrev = blk: {
137 const index = object.dwarf_debug_abbrev_index orelse return null;
138 break :blk try object.readSection(allocator, index);
139 };
140 var debug_str = blk: {
141 const index = object.dwarf_debug_str_index orelse return null;
142 break :blk try object.readSection(allocator, index);
143 };
144 var debug_line = blk: {
145 const index = object.dwarf_debug_line_index orelse return null;
146 break :blk try object.readSection(allocator, index);
147 };
148 var debug_ranges = blk: {
149 if (object.dwarf_debug_ranges_index) |ind| {
150 break :blk try object.readSection(allocator, ind);
151 }
152 break :blk try allocator.alloc(u8, 0);
153 };
154
155 var inner: dwarf.DwarfInfo = .{
156 .endian = .Little,
157 .debug_info = debug_info,
158 .debug_abbrev = debug_abbrev,
159 .debug_str = debug_str,
160 .debug_line = debug_line,
161 .debug_ranges = debug_ranges,
162 };
163 try dwarf.openDwarfDebugInfo(&inner, allocator);
164
165 return DebugInfo{
166 .inner = inner,
167 .debug_info = debug_info,
168 .debug_abbrev = debug_abbrev,
169 .debug_str = debug_str,
170 .debug_line = debug_line,
171 .debug_ranges = debug_ranges,
172 };
173 }
174
175 pub fn deinit(self: *DebugInfo, allocator: *Allocator) void {
176 allocator.free(self.debug_info);
177 allocator.free(self.debug_abbrev);
178 allocator.free(self.debug_str);
179 allocator.free(self.debug_line);
180 allocator.free(self.debug_ranges);
181 self.inner.abbrev_table_list.deinit();
182 self.inner.compile_unit_list.deinit();
183 self.inner.func_list.deinit();
184 }
185};
186
187pub const Import = struct {
188 /// MachO symbol table entry.
189 symbol: macho.nlist_64,
190
191 /// Id of the dynamic library where the specified entries can be found.
192 dylib_ordinal: i64,
193
194 /// Index of this import within the import list.
195 index: u32,
196};
197
198/// Default path to dyld
199/// TODO instead of hardcoding it, we should probably look through some env vars and search paths
200/// instead but this will do for now.
201const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld";
202
203/// Default lib search path
204/// TODO instead of hardcoding it, we should probably look through some env vars and search paths
205/// instead but this will do for now.
206const DEFAULT_LIB_SEARCH_PATH: []const u8 = "/usr/lib";
207
208const LIB_SYSTEM_NAME: [*:0]const u8 = "System";
209/// TODO we should search for libSystem and fail if it doesn't exist, instead of hardcoding it
210const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.dylib";
211
212pub fn init(allocator: *Allocator) Zld {
213 return .{ .allocator = allocator };
214}
215
216pub fn deinit(self: *Zld) void {
217 self.threadlocal_offsets.deinit(self.allocator);
218 self.strtab.deinit(self.allocator);
219 self.local_rebases.deinit(self.allocator);
220 for (self.lazy_imports.items()) |*entry| {
221 self.allocator.free(entry.key);
222 }
223 self.lazy_imports.deinit(self.allocator);
224 for (self.nonlazy_imports.items()) |*entry| {
225 self.allocator.free(entry.key);
226 }
227 self.nonlazy_imports.deinit(self.allocator);
228 for (self.nonlazy_pointers.items()) |*entry| {
229 self.allocator.free(entry.key);
230 }
231 self.nonlazy_pointers.deinit(self.allocator);
232 for (self.exports.items()) |*entry| {
233 self.allocator.free(entry.key);
234 }
235 self.exports.deinit(self.allocator);
236 for (self.locals.items()) |*entry| {
237 self.allocator.free(entry.key);
238 entry.value.deinit(self.allocator);
239 }
240 self.locals.deinit(self.allocator);
241 for (self.objects.items) |*object| {
242 object.deinit();
243 }
244 self.objects.deinit(self.allocator);
245 for (self.load_commands.items) |*lc| {
246 lc.deinit(self.allocator);
247 }
248 self.load_commands.deinit(self.allocator);
249 self.mappings.deinit(self.allocator);
250 self.unhandled_sections.deinit(self.allocator);
251 if (self.file) |*f| f.close();
252}
253
254pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void {
255 if (files.len == 0) return error.NoInputFiles;
256 if (out_path.len == 0) return error.EmptyOutputPath;
257
258 if (self.arch == null) {
259 // Try inferring the arch from the object files.
260 self.arch = blk: {
261 const file = try fs.cwd().openFile(files[0], .{});
262 defer file.close();
263 var reader = file.reader();
264 const header = try reader.readStruct(macho.mach_header_64);
265 const arch: std.Target.Cpu.Arch = switch (header.cputype) {
266 macho.CPU_TYPE_X86_64 => .x86_64,
267 macho.CPU_TYPE_ARM64 => .aarch64,
268 else => |value| {
269 log.err("unsupported cpu architecture 0x{x}", .{value});
270 return error.UnsupportedCpuArchitecture;
271 },
272 };
273 break :blk arch;
274 };
275 }
276
277 self.page_size = switch (self.arch.?) {
278 .aarch64 => 0x4000,
279 .x86_64 => 0x1000,
280 else => unreachable,
281 };
282 self.out_path = out_path;
283 self.file = try fs.cwd().createFile(out_path, .{
284 .truncate = true,
285 .read = true,
286 .mode = if (std.Target.current.os.tag == .windows) 0 else 0o777,
287 });
288
289 try self.populateMetadata();
290 try self.parseInputFiles(files);
291 try self.sortSections();
292 try self.resolveImports();
293 try self.allocateTextSegment();
294 try self.allocateDataConstSegment();
295 try self.allocateDataSegment();
296 self.allocateLinkeditSegment();
297 try self.writeStubHelperCommon();
298 try self.resolveSymbols();
299 try self.doRelocs();
300 try self.flush();
301}
302
303fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
304 for (files) |file_name| {
305 const file = try fs.cwd().openFile(file_name, .{});
306
307 try_object: {
308 var object = Object.initFromFile(self.allocator, self.arch.?, file_name, file) catch |err| switch (err) {
309 error.NotObject => break :try_object,
310 else => |e| return e,
311 };
312 const index = @intCast(u16, self.objects.items.len);
313 try self.objects.append(self.allocator, object);
314 try self.updateMetadata(index);
315 continue;
316 }
317
318 try_archive: {
319 var archive = Archive.initFromFile(self.allocator, self.arch.?, file_name, file) catch |err| switch (err) {
320 error.NotArchive => break :try_archive,
321 else => |e| return e,
322 };
323 defer archive.deinit();
324 while (archive.objects.popOrNull()) |object| {
325 const index = @intCast(u16, self.objects.items.len);
326 try self.objects.append(self.allocator, object);
327 try self.updateMetadata(index);
328 }
329 continue;
330 }
331
332 log.err("unexpected file type: expected object '.o' or archive '.a': {s}", .{file_name});
333 return error.UnexpectedInputFileType;
334 }
335}
336
337fn mapAndUpdateSections(
338 self: *Zld,
339 object_id: u16,
340 source_sect_id: u16,
341 target_seg_id: u16,
342 target_sect_id: u16,
343) !void {
344 const object = self.objects.items[object_id];
345 const source_seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
346 const source_sect = source_seg.sections.items[source_sect_id];
347 const target_seg = &self.load_commands.items[target_seg_id].Segment;
348 const target_sect = &target_seg.sections.items[target_sect_id];
349
350 const alignment = try math.powi(u32, 2, target_sect.@"align");
351 const offset = mem.alignForwardGeneric(u64, target_sect.size, alignment);
352 const size = mem.alignForwardGeneric(u64, source_sect.size, alignment);
353 const key = MappingKey{
354 .object_id = object_id,
355 .source_sect_id = source_sect_id,
356 };
357 try self.mappings.putNoClobber(self.allocator, key, .{
358 .source_sect_id = source_sect_id,
359 .target_seg_id = target_seg_id,
360 .target_sect_id = target_sect_id,
361 .offset = @intCast(u32, offset),
362 });
363 log.debug("{s}: {s},{s} mapped to {s},{s} from 0x{x} to 0x{x}", .{
364 object.name,
365 parseName(&source_sect.segname),
366 parseName(&source_sect.sectname),
367 parseName(&target_sect.segname),
368 parseName(&target_sect.sectname),
369 offset,
370 offset + size,
371 });
372
373 target_sect.size = offset + size;
374}
375
376fn updateMetadata(self: *Zld, object_id: u16) !void {
377 const object = self.objects.items[object_id];
378 const object_seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
379 const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
380 const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
381 const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
382
383 // Create missing metadata
384 for (object_seg.sections.items) |source_sect, id| {
385 if (id == object.text_section_index.?) continue;
386 const segname = parseName(&source_sect.segname);
387 const sectname = parseName(&source_sect.sectname);
388 const flags = source_sect.flags;
389
390 switch (flags) {
391 macho.S_REGULAR, macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => {
392 if (mem.eql(u8, segname, "__TEXT")) {
393 if (self.text_const_section_index != null) continue;
394
395 self.text_const_section_index = @intCast(u16, text_seg.sections.items.len);
396 try text_seg.addSection(self.allocator, .{
397 .sectname = makeStaticString("__const"),
398 .segname = makeStaticString("__TEXT"),
399 .addr = 0,
400 .size = 0,
401 .offset = 0,
402 .@"align" = 0,
403 .reloff = 0,
404 .nreloc = 0,
405 .flags = macho.S_REGULAR,
406 .reserved1 = 0,
407 .reserved2 = 0,
408 .reserved3 = 0,
409 });
410 } else if (mem.eql(u8, segname, "__DATA")) {
411 if (!mem.eql(u8, sectname, "__const")) continue;
412 if (self.data_const_section_index != null) continue;
413
414 self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len);
415 try data_const_seg.addSection(self.allocator, .{
416 .sectname = makeStaticString("__const"),
417 .segname = makeStaticString("__DATA_CONST"),
418 .addr = 0,
419 .size = 0,
420 .offset = 0,
421 .@"align" = 0,
422 .reloff = 0,
423 .nreloc = 0,
424 .flags = macho.S_REGULAR,
425 .reserved1 = 0,
426 .reserved2 = 0,
427 .reserved3 = 0,
428 });
429 }
430 },
431 macho.S_CSTRING_LITERALS => {
432 if (!mem.eql(u8, segname, "__TEXT")) continue;
433 if (self.cstring_section_index != null) continue;
434
435 self.cstring_section_index = @intCast(u16, text_seg.sections.items.len);
436 try text_seg.addSection(self.allocator, .{
437 .sectname = makeStaticString("__cstring"),
438 .segname = makeStaticString("__TEXT"),
439 .addr = 0,
440 .size = 0,
441 .offset = 0,
442 .@"align" = 0,
443 .reloff = 0,
444 .nreloc = 0,
445 .flags = macho.S_CSTRING_LITERALS,
446 .reserved1 = 0,
447 .reserved2 = 0,
448 .reserved3 = 0,
449 });
450 },
451 macho.S_ZEROFILL => {
452 if (!mem.eql(u8, segname, "__DATA")) continue;
453 if (self.bss_section_index != null) continue;
454
455 self.bss_section_index = @intCast(u16, data_seg.sections.items.len);
456 try data_seg.addSection(self.allocator, .{
457 .sectname = makeStaticString("__bss"),
458 .segname = makeStaticString("__DATA"),
459 .addr = 0,
460 .size = 0,
461 .offset = 0,
462 .@"align" = 0,
463 .reloff = 0,
464 .nreloc = 0,
465 .flags = macho.S_ZEROFILL,
466 .reserved1 = 0,
467 .reserved2 = 0,
468 .reserved3 = 0,
469 });
470 },
471 macho.S_THREAD_LOCAL_VARIABLES => {
472 if (!mem.eql(u8, segname, "__DATA")) continue;
473 if (self.tlv_section_index != null) continue;
474
475 self.tlv_section_index = @intCast(u16, data_seg.sections.items.len);
476 try data_seg.addSection(self.allocator, .{
477 .sectname = makeStaticString("__thread_vars"),
478 .segname = makeStaticString("__DATA"),
479 .addr = 0,
480 .size = 0,
481 .offset = 0,
482 .@"align" = 0,
483 .reloff = 0,
484 .nreloc = 0,
485 .flags = macho.S_THREAD_LOCAL_VARIABLES,
486 .reserved1 = 0,
487 .reserved2 = 0,
488 .reserved3 = 0,
489 });
490 },
491 macho.S_THREAD_LOCAL_REGULAR => {
492 if (!mem.eql(u8, segname, "__DATA")) continue;
493 if (self.tlv_data_section_index != null) continue;
494
495 self.tlv_data_section_index = @intCast(u16, data_seg.sections.items.len);
496 try data_seg.addSection(self.allocator, .{
497 .sectname = makeStaticString("__thread_data"),
498 .segname = makeStaticString("__DATA"),
499 .addr = 0,
500 .size = 0,
501 .offset = 0,
502 .@"align" = 0,
503 .reloff = 0,
504 .nreloc = 0,
505 .flags = macho.S_THREAD_LOCAL_REGULAR,
506 .reserved1 = 0,
507 .reserved2 = 0,
508 .reserved3 = 0,
509 });
510 },
511 macho.S_THREAD_LOCAL_ZEROFILL => {
512 if (!mem.eql(u8, segname, "__DATA")) continue;
513 if (self.tlv_bss_section_index != null) continue;
514
515 self.tlv_bss_section_index = @intCast(u16, data_seg.sections.items.len);
516 try data_seg.addSection(self.allocator, .{
517 .sectname = makeStaticString("__thread_bss"),
518 .segname = makeStaticString("__DATA"),
519 .addr = 0,
520 .size = 0,
521 .offset = 0,
522 .@"align" = 0,
523 .reloff = 0,
524 .nreloc = 0,
525 .flags = macho.S_THREAD_LOCAL_ZEROFILL,
526 .reserved1 = 0,
527 .reserved2 = 0,
528 .reserved3 = 0,
529 });
530 },
531 else => {
532 log.debug("unhandled section type 0x{x} for '{s}/{s}'", .{ flags, segname, sectname });
533 },
534 }
535 }
536
537 // Find ideal section alignment.
538 for (object_seg.sections.items) |source_sect, id| {
539 if (self.getMatchingSection(source_sect)) |res| {
540 const target_seg = &self.load_commands.items[res.seg].Segment;
541 const target_sect = &target_seg.sections.items[res.sect];
542 target_sect.@"align" = math.max(target_sect.@"align", source_sect.@"align");
543 }
544 }
545
546 // Update section mappings
547 for (object_seg.sections.items) |source_sect, id| {
548 const source_sect_id = @intCast(u16, id);
549 if (self.getMatchingSection(source_sect)) |res| {
550 try self.mapAndUpdateSections(object_id, source_sect_id, res.seg, res.sect);
551 continue;
552 }
553
554 const segname = parseName(&source_sect.segname);
555 const sectname = parseName(&source_sect.sectname);
556 log.debug("section '{s}/{s}' will be unmapped", .{ segname, sectname });
557 try self.unhandled_sections.putNoClobber(self.allocator, .{
558 .object_id = object_id,
559 .source_sect_id = source_sect_id,
560 }, 0);
561 }
562}
563
564const MatchingSection = struct {
565 seg: u16,
566 sect: u16,
567};
568
569fn getMatchingSection(self: *Zld, section: macho.section_64) ?MatchingSection {
570 const segname = parseName(&section.segname);
571 const sectname = parseName(&section.sectname);
572 const res: ?MatchingSection = blk: {
573 switch (section.flags) {
574 macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => {
575 break :blk .{
576 .seg = self.text_segment_cmd_index.?,
577 .sect = self.text_const_section_index.?,
578 };
579 },
580 macho.S_CSTRING_LITERALS => {
581 break :blk .{
582 .seg = self.text_segment_cmd_index.?,
583 .sect = self.cstring_section_index.?,
584 };
585 },
586 macho.S_ZEROFILL => {
587 break :blk .{
588 .seg = self.data_segment_cmd_index.?,
589 .sect = self.bss_section_index.?,
590 };
591 },
592 macho.S_THREAD_LOCAL_VARIABLES => {
593 break :blk .{
594 .seg = self.data_segment_cmd_index.?,
595 .sect = self.tlv_section_index.?,
596 };
597 },
598 macho.S_THREAD_LOCAL_REGULAR => {
599 break :blk .{
600 .seg = self.data_segment_cmd_index.?,
601 .sect = self.tlv_data_section_index.?,
602 };
603 },
604 macho.S_THREAD_LOCAL_ZEROFILL => {
605 break :blk .{
606 .seg = self.data_segment_cmd_index.?,
607 .sect = self.tlv_bss_section_index.?,
608 };
609 },
610 macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS => {
611 break :blk .{
612 .seg = self.text_segment_cmd_index.?,
613 .sect = self.text_section_index.?,
614 };
615 },
616 macho.S_REGULAR => {
617 if (mem.eql(u8, segname, "__TEXT")) {
618 break :blk .{
619 .seg = self.text_segment_cmd_index.?,
620 .sect = self.text_const_section_index.?,
621 };
622 } else if (mem.eql(u8, segname, "__DATA")) {
623 if (mem.eql(u8, sectname, "__data")) {
624 break :blk .{
625 .seg = self.data_segment_cmd_index.?,
626 .sect = self.data_section_index.?,
627 };
628 } else if (mem.eql(u8, sectname, "__const")) {
629 break :blk .{
630 .seg = self.data_const_segment_cmd_index.?,
631 .sect = self.data_const_section_index.?,
632 };
633 }
634 }
635 break :blk null;
636 },
637 else => {
638 break :blk null;
639 },
640 }
641 };
642 return res;
643}
644
645fn sortSections(self: *Zld) !void {
646 var text_index_mapping = std.AutoHashMap(u16, u16).init(self.allocator);
647 defer text_index_mapping.deinit();
648 var data_const_index_mapping = std.AutoHashMap(u16, u16).init(self.allocator);
649 defer data_const_index_mapping.deinit();
650 var data_index_mapping = std.AutoHashMap(u16, u16).init(self.allocator);
651 defer data_index_mapping.deinit();
652
653 {
654 // __TEXT segment
655 const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
656 var sections = seg.sections.toOwnedSlice(self.allocator);
657 defer self.allocator.free(sections);
658 try seg.sections.ensureCapacity(self.allocator, sections.len);
659
660 const indices = &[_]*?u16{
661 &self.text_section_index,
662 &self.stubs_section_index,
663 &self.stub_helper_section_index,
664 &self.text_const_section_index,
665 &self.cstring_section_index,
666 };
667 for (indices) |maybe_index| {
668 const new_index: u16 = if (maybe_index.*) |index| blk: {
669 const idx = @intCast(u16, seg.sections.items.len);
670 seg.sections.appendAssumeCapacity(sections[index]);
671 try text_index_mapping.putNoClobber(index, idx);
672 break :blk idx;
673 } else continue;
674 maybe_index.* = new_index;
675 }
676 }
677
678 {
679 // __DATA_CONST segment
680 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
681 var sections = seg.sections.toOwnedSlice(self.allocator);
682 defer self.allocator.free(sections);
683 try seg.sections.ensureCapacity(self.allocator, sections.len);
684
685 const indices = &[_]*?u16{
686 &self.got_section_index,
687 &self.data_const_section_index,
688 };
689 for (indices) |maybe_index| {
690 const new_index: u16 = if (maybe_index.*) |index| blk: {
691 const idx = @intCast(u16, seg.sections.items.len);
692 seg.sections.appendAssumeCapacity(sections[index]);
693 try data_const_index_mapping.putNoClobber(index, idx);
694 break :blk idx;
695 } else continue;
696 maybe_index.* = new_index;
697 }
698 }
699
700 {
701 // __DATA segment
702 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
703 var sections = seg.sections.toOwnedSlice(self.allocator);
704 defer self.allocator.free(sections);
705 try seg.sections.ensureCapacity(self.allocator, sections.len);
706
707 // __DATA segment
708 const indices = &[_]*?u16{
709 &self.la_symbol_ptr_section_index,
710 &self.tlv_section_index,
711 &self.data_section_index,
712 &self.tlv_data_section_index,
713 &self.tlv_bss_section_index,
714 &self.bss_section_index,
715 };
716 for (indices) |maybe_index| {
717 const new_index: u16 = if (maybe_index.*) |index| blk: {
718 const idx = @intCast(u16, seg.sections.items.len);
719 seg.sections.appendAssumeCapacity(sections[index]);
720 try data_index_mapping.putNoClobber(index, idx);
721 break :blk idx;
722 } else continue;
723 maybe_index.* = new_index;
724 }
725 }
726
727 var it = self.mappings.iterator();
728 while (it.next()) |entry| {
729 const mapping = &entry.value;
730 if (self.text_segment_cmd_index.? == mapping.target_seg_id) {
731 const new_index = text_index_mapping.get(mapping.target_sect_id) orelse unreachable;
732 mapping.target_sect_id = new_index;
733 } else if (self.data_const_segment_cmd_index.? == mapping.target_seg_id) {
734 const new_index = data_const_index_mapping.get(mapping.target_sect_id) orelse unreachable;
735 mapping.target_sect_id = new_index;
736 } else if (self.data_segment_cmd_index.? == mapping.target_seg_id) {
737 const new_index = data_index_mapping.get(mapping.target_sect_id) orelse unreachable;
738 mapping.target_sect_id = new_index;
739 } else unreachable;
740 }
741}
742
743fn resolveImports(self: *Zld) !void {
744 var imports = std.StringArrayHashMap(bool).init(self.allocator);
745 defer imports.deinit();
746
747 for (self.objects.items) |object| {
748 for (object.symtab.items) |sym| {
749 if (isLocal(&sym)) continue;
750
751 const name = object.getString(sym.n_strx);
752 const res = try imports.getOrPut(name);
753 if (isExport(&sym)) {
754 res.entry.value = false;
755 continue;
756 }
757 if (res.found_existing and !res.entry.value)
758 continue;
759 res.entry.value = true;
760 }
761 }
762
763 for (imports.items()) |entry| {
764 if (!entry.value) continue;
765
766 const sym_name = entry.key;
767 const n_strx = try self.makeString(sym_name);
768 var new_sym: macho.nlist_64 = .{
769 .n_strx = n_strx,
770 .n_type = macho.N_UNDF | macho.N_EXT,
771 .n_value = 0,
772 .n_desc = macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | macho.N_SYMBOL_RESOLVER,
773 .n_sect = 0,
774 };
775 var key = try self.allocator.dupe(u8, sym_name);
776 // TODO handle symbol resolution from non-libc dylibs.
777 const dylib_ordinal = 1;
778
779 // TODO need to rework this. Perhaps should create a set of all possible libc
780 // symbols which are expected to be nonlazy?
781 if (mem.eql(u8, sym_name, "___stdoutp") or
782 mem.eql(u8, sym_name, "___stderrp") or
783 mem.eql(u8, sym_name, "___stdinp") or
784 mem.eql(u8, sym_name, "___stack_chk_guard") or
785 mem.eql(u8, sym_name, "_environ") or
786 mem.eql(u8, sym_name, "__DefaultRuneLocale") or
787 mem.eql(u8, sym_name, "_mach_task_self_"))
788 {
789 log.debug("writing nonlazy symbol '{s}'", .{sym_name});
790 const index = @intCast(u32, self.nonlazy_imports.items().len);
791 try self.nonlazy_imports.putNoClobber(self.allocator, key, .{
792 .symbol = new_sym,
793 .dylib_ordinal = dylib_ordinal,
794 .index = index,
795 });
796 } else if (mem.eql(u8, sym_name, "__tlv_bootstrap")) {
797 log.debug("writing threadlocal symbol '{s}'", .{sym_name});
798 self.tlv_bootstrap = .{
799 .symbol = new_sym,
800 .dylib_ordinal = dylib_ordinal,
801 .index = 0,
802 };
803 } else {
804 log.debug("writing lazy symbol '{s}'", .{sym_name});
805 const index = @intCast(u32, self.lazy_imports.items().len);
806 try self.lazy_imports.putNoClobber(self.allocator, key, .{
807 .symbol = new_sym,
808 .dylib_ordinal = dylib_ordinal,
809 .index = index,
810 });
811 }
812 }
813
814 const n_strx = try self.makeString("dyld_stub_binder");
815 const name = try self.allocator.dupe(u8, "dyld_stub_binder");
816 log.debug("writing nonlazy symbol 'dyld_stub_binder'", .{});
817 const index = @intCast(u32, self.nonlazy_imports.items().len);
818 try self.nonlazy_imports.putNoClobber(self.allocator, name, .{
819 .symbol = .{
820 .n_strx = n_strx,
821 .n_type = std.macho.N_UNDF | std.macho.N_EXT,
822 .n_sect = 0,
823 .n_desc = std.macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | std.macho.N_SYMBOL_RESOLVER,
824 .n_value = 0,
825 },
826 .dylib_ordinal = 1,
827 .index = index,
828 });
829}
830
831fn allocateTextSegment(self: *Zld) !void {
832 const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
833 const nexterns = @intCast(u32, self.lazy_imports.items().len);
834
835 const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].Segment.inner.vmsize;
836 seg.inner.fileoff = 0;
837 seg.inner.vmaddr = base_vmaddr;
838
839 // Set stubs and stub_helper sizes
840 const stubs = &seg.sections.items[self.stubs_section_index.?];
841 const stub_helper = &seg.sections.items[self.stub_helper_section_index.?];
842 stubs.size += nexterns * stubs.reserved2;
843
844 const stub_size: u4 = switch (self.arch.?) {
845 .x86_64 => 10,
846 .aarch64 => 3 * @sizeOf(u32),
847 else => unreachable,
848 };
849 stub_helper.size += nexterns * stub_size;
850
851 var sizeofcmds: u64 = 0;
852 for (self.load_commands.items) |lc| {
853 sizeofcmds += lc.cmdsize();
854 }
855
856 try self.allocateSegment(self.text_segment_cmd_index.?, @sizeOf(macho.mach_header_64) + sizeofcmds);
857
858 // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments.
859 var min_alignment: u32 = 0;
860 for (seg.sections.items) |sect| {
861 const alignment = try math.powi(u32, 2, sect.@"align");
862 min_alignment = math.max(min_alignment, alignment);
863 }
864
865 assert(min_alignment > 0);
866 const last_sect_idx = seg.sections.items.len - 1;
867 const last_sect = seg.sections.items[last_sect_idx];
868 const shift: u32 = blk: {
869 const diff = seg.inner.filesize - last_sect.offset - last_sect.size;
870 const factor = @divTrunc(diff, min_alignment);
871 break :blk @intCast(u32, factor * min_alignment);
872 };
873
874 if (shift > 0) {
875 for (seg.sections.items) |*sect| {
876 sect.offset += shift;
877 sect.addr += shift;
878 }
879 }
880}
881
882fn allocateDataConstSegment(self: *Zld) !void {
883 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
884 const nonlazy = @intCast(u32, self.nonlazy_imports.items().len);
885
886 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
887 seg.inner.fileoff = text_seg.inner.fileoff + text_seg.inner.filesize;
888 seg.inner.vmaddr = text_seg.inner.vmaddr + text_seg.inner.vmsize;
889
890 // Set got size
891 const got = &seg.sections.items[self.got_section_index.?];
892 // TODO this will require scanning the relocations at least one to work out
893 // the exact amount of local GOT indirections. For the time being, set some
894 // default value.
895 got.size += (max_local_got_indirections + nonlazy) * @sizeOf(u64);
896
897 try self.allocateSegment(self.data_const_segment_cmd_index.?, 0);
898}
899
900fn allocateDataSegment(self: *Zld) !void {
901 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
902 const lazy = @intCast(u32, self.lazy_imports.items().len);
903
904 const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
905 seg.inner.fileoff = data_const_seg.inner.fileoff + data_const_seg.inner.filesize;
906 seg.inner.vmaddr = data_const_seg.inner.vmaddr + data_const_seg.inner.vmsize;
907
908 // Set la_symbol_ptr and data size
909 const la_symbol_ptr = &seg.sections.items[self.la_symbol_ptr_section_index.?];
910 const data = &seg.sections.items[self.data_section_index.?];
911 la_symbol_ptr.size += lazy * @sizeOf(u64);
912 data.size += @sizeOf(u64); // TODO when do we need more?
913
914 try self.allocateSegment(self.data_segment_cmd_index.?, 0);
915}
916
917fn allocateLinkeditSegment(self: *Zld) void {
918 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
919 const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
920 seg.inner.fileoff = data_seg.inner.fileoff + data_seg.inner.filesize;
921 seg.inner.vmaddr = data_seg.inner.vmaddr + data_seg.inner.vmsize;
922}
923
924fn allocateSegment(self: *Zld, index: u16, offset: u64) !void {
925 const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].Segment.inner.vmsize;
926 const seg = &self.load_commands.items[index].Segment;
927
928 // Allocate the sections according to their alignment at the beginning of the segment.
929 var start: u64 = offset;
930 for (seg.sections.items) |*sect| {
931 const alignment = try math.powi(u32, 2, sect.@"align");
932 const start_aligned = mem.alignForwardGeneric(u64, start, alignment);
933 const end_aligned = mem.alignForwardGeneric(u64, start_aligned + sect.size, alignment);
934 sect.offset = @intCast(u32, seg.inner.fileoff + start_aligned);
935 sect.addr = seg.inner.vmaddr + start_aligned;
936 start = end_aligned;
937 }
938
939 const seg_size_aligned = mem.alignForwardGeneric(u64, start, self.page_size.?);
940 seg.inner.filesize = seg_size_aligned;
941 seg.inner.vmsize = seg_size_aligned;
942}
943
944fn writeStubHelperCommon(self: *Zld) !void {
945 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
946 const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?];
947 const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
948 const got = &data_const_segment.sections.items[self.got_section_index.?];
949 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
950 const data = &data_segment.sections.items[self.data_section_index.?];
951 const la_symbol_ptr = data_segment.sections.items[self.la_symbol_ptr_section_index.?];
952
953 self.stub_helper_stubs_start_off = blk: {
954 switch (self.arch.?) {
955 .x86_64 => {
956 const code_size = 15;
957 var code: [code_size]u8 = undefined;
958 // lea %r11, [rip + disp]
959 code[0] = 0x4c;
960 code[1] = 0x8d;
961 code[2] = 0x1d;
962 {
963 const target_addr = data.addr + data.size - @sizeOf(u64);
964 const displacement = try math.cast(u32, target_addr - stub_helper.addr - 7);
965 mem.writeIntLittle(u32, code[3..7], displacement);
966 }
967 // push %r11
968 code[7] = 0x41;
969 code[8] = 0x53;
970 // jmp [rip + disp]
971 code[9] = 0xff;
972 code[10] = 0x25;
973 {
974 const dyld_stub_binder = self.nonlazy_imports.get("dyld_stub_binder").?;
975 const addr = (got.addr + dyld_stub_binder.index * @sizeOf(u64));
976 const displacement = try math.cast(u32, addr - stub_helper.addr - code_size);
977 mem.writeIntLittle(u32, code[11..], displacement);
978 }
979 try self.file.?.pwriteAll(&code, stub_helper.offset);
980 break :blk stub_helper.offset + code_size;
981 },
982 .aarch64 => {
983 var code: [6 * @sizeOf(u32)]u8 = undefined;
984 data_blk_outer: {
985 const this_addr = stub_helper.addr;
986 const target_addr = data.addr + data.size - @sizeOf(u64);
987 data_blk: {
988 const displacement = math.cast(i21, target_addr - this_addr) catch |_| break :data_blk;
989 // adr x17, disp
990 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, displacement).toU32());
991 // nop
992 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32());
993 break :data_blk_outer;
994 }
995 data_blk: {
996 const new_this_addr = this_addr + @sizeOf(u32);
997 const displacement = math.cast(i21, target_addr - new_this_addr) catch |_| break :data_blk;
998 // nop
999 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32());
1000 // adr x17, disp
1001 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.adr(.x17, displacement).toU32());
1002 break :data_blk_outer;
1003 }
1004 // Jump is too big, replace adr with adrp and add.
1005 const this_page = @intCast(i32, this_addr >> 12);
1006 const target_page = @intCast(i32, target_addr >> 12);
1007 const pages = @intCast(i21, target_page - this_page);
1008 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x17, pages).toU32());
1009 const narrowed = @truncate(u12, target_addr);
1010 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.add(.x17, .x17, narrowed, false).toU32());
1011 }
1012 // stp x16, x17, [sp, #-16]!
1013 code[8] = 0xf0;
1014 code[9] = 0x47;
1015 code[10] = 0xbf;
1016 code[11] = 0xa9;
1017 binder_blk_outer: {
1018 const dyld_stub_binder = self.nonlazy_imports.get("dyld_stub_binder").?;
1019 const this_addr = stub_helper.addr + 3 * @sizeOf(u32);
1020 const target_addr = (got.addr + dyld_stub_binder.index * @sizeOf(u64));
1021 binder_blk: {
1022 const displacement = math.divExact(u64, target_addr - this_addr, 4) catch |_| break :binder_blk;
1023 const literal = math.cast(u18, displacement) catch |_| break :binder_blk;
1024 // ldr x16, label
1025 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.ldr(.x16, .{
1026 .literal = literal,
1027 }).toU32());
1028 // nop
1029 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.nop().toU32());
1030 break :binder_blk_outer;
1031 }
1032 binder_blk: {
1033 const new_this_addr = this_addr + @sizeOf(u32);
1034 const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch |_| break :binder_blk;
1035 const literal = math.cast(u18, displacement) catch |_| break :binder_blk;
1036 log.debug("2: disp=0x{x}, literal=0x{x}", .{ displacement, literal });
1037 // Pad with nop to please division.
1038 // nop
1039 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.nop().toU32());
1040 // ldr x16, label
1041 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{
1042 .literal = literal,
1043 }).toU32());
1044 break :binder_blk_outer;
1045 }
1046 // Use adrp followed by ldr(immediate).
1047 const this_page = @intCast(i32, this_addr >> 12);
1048 const target_page = @intCast(i32, target_addr >> 12);
1049 const pages = @intCast(i21, target_page - this_page);
1050 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.adrp(.x16, pages).toU32());
1051 const narrowed = @truncate(u12, target_addr);
1052 const offset = try math.divExact(u12, narrowed, 8);
1053 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{
1054 .register = .{
1055 .rn = .x16,
1056 .offset = aarch64.Instruction.LoadStoreOffset.imm(offset),
1057 },
1058 }).toU32());
1059 }
1060 // br x16
1061 code[20] = 0x00;
1062 code[21] = 0x02;
1063 code[22] = 0x1f;
1064 code[23] = 0xd6;
1065 try self.file.?.pwriteAll(&code, stub_helper.offset);
1066 break :blk stub_helper.offset + 6 * @sizeOf(u32);
1067 },
1068 else => unreachable,
1069 }
1070 };
1071
1072 for (self.lazy_imports.items()) |_, i| {
1073 const index = @intCast(u32, i);
1074 try self.writeLazySymbolPointer(index);
1075 try self.writeStub(index);
1076 try self.writeStubInStubHelper(index);
1077 }
1078}
1079
1080fn writeLazySymbolPointer(self: *Zld, index: u32) !void {
1081 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1082 const stub_helper = text_segment.sections.items[self.stub_helper_section_index.?];
1083 const data_segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
1084 const la_symbol_ptr = data_segment.sections.items[self.la_symbol_ptr_section_index.?];
1085
1086 const stub_size: u4 = switch (self.arch.?) {
1087 .x86_64 => 10,
1088 .aarch64 => 3 * @sizeOf(u32),
1089 else => unreachable,
1090 };
1091 const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size;
1092 const end = stub_helper.addr + stub_off - stub_helper.offset;
1093 var buf: [@sizeOf(u64)]u8 = undefined;
1094 mem.writeIntLittle(u64, &buf, end);
1095 const off = la_symbol_ptr.offset + index * @sizeOf(u64);
1096 log.debug("writing lazy symbol pointer entry 0x{x} at 0x{x}", .{ end, off });
1097 try self.file.?.pwriteAll(&buf, off);
1098}
1099
1100fn writeStub(self: *Zld, index: u32) !void {
1101 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1102 const stubs = text_segment.sections.items[self.stubs_section_index.?];
1103 const data_segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
1104 const la_symbol_ptr = data_segment.sections.items[self.la_symbol_ptr_section_index.?];
1105
1106 const stub_off = stubs.offset + index * stubs.reserved2;
1107 const stub_addr = stubs.addr + index * stubs.reserved2;
1108 const la_ptr_addr = la_symbol_ptr.addr + index * @sizeOf(u64);
1109 log.debug("writing stub at 0x{x}", .{stub_off});
1110 var code = try self.allocator.alloc(u8, stubs.reserved2);
1111 defer self.allocator.free(code);
1112 switch (self.arch.?) {
1113 .x86_64 => {
1114 assert(la_ptr_addr >= stub_addr + stubs.reserved2);
1115 const displacement = try math.cast(u32, la_ptr_addr - stub_addr - stubs.reserved2);
1116 // jmp
1117 code[0] = 0xff;
1118 code[1] = 0x25;
1119 mem.writeIntLittle(u32, code[2..][0..4], displacement);
1120 },
1121 .aarch64 => {
1122 assert(la_ptr_addr >= stub_addr);
1123 outer: {
1124 const this_addr = stub_addr;
1125 const target_addr = la_ptr_addr;
1126 inner: {
1127 const displacement = math.divExact(u64, target_addr - this_addr, 4) catch |_| break :inner;
1128 const literal = math.cast(u18, displacement) catch |_| break :inner;
1129 // ldr x16, literal
1130 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{
1131 .literal = literal,
1132 }).toU32());
1133 // nop
1134 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32());
1135 break :outer;
1136 }
1137 inner: {
1138 const new_this_addr = this_addr + @sizeOf(u32);
1139 const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch |_| break :inner;
1140 const literal = math.cast(u18, displacement) catch |_| break :inner;
1141 // nop
1142 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32());
1143 // ldr x16, literal
1144 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(.x16, .{
1145 .literal = literal,
1146 }).toU32());
1147 break :outer;
1148 }
1149 // Use adrp followed by ldr(immediate).
1150 const this_page = @intCast(i32, this_addr >> 12);
1151 const target_page = @intCast(i32, target_addr >> 12);
1152 const pages = @intCast(i21, target_page - this_page);
1153 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x16, pages).toU32());
1154 const narrowed = @truncate(u12, target_addr);
1155 const offset = try math.divExact(u12, narrowed, 8);
1156 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(.x16, .{
1157 .register = .{
1158 .rn = .x16,
1159 .offset = aarch64.Instruction.LoadStoreOffset.imm(offset),
1160 },
1161 }).toU32());
1162 }
1163 // br x16
1164 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.br(.x16).toU32());
1165 },
1166 else => unreachable,
1167 }
1168 try self.file.?.pwriteAll(code, stub_off);
1169}
1170
1171fn writeStubInStubHelper(self: *Zld, index: u32) !void {
1172 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1173 const stub_helper = text_segment.sections.items[self.stub_helper_section_index.?];
1174
1175 const stub_size: u4 = switch (self.arch.?) {
1176 .x86_64 => 10,
1177 .aarch64 => 3 * @sizeOf(u32),
1178 else => unreachable,
1179 };
1180 const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size;
1181 var code = try self.allocator.alloc(u8, stub_size);
1182 defer self.allocator.free(code);
1183 switch (self.arch.?) {
1184 .x86_64 => {
1185 const displacement = try math.cast(
1186 i32,
1187 @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - stub_size,
1188 );
1189 // pushq
1190 code[0] = 0x68;
1191 mem.writeIntLittle(u32, code[1..][0..4], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
1192 // jmpq
1193 code[5] = 0xe9;
1194 mem.writeIntLittle(u32, code[6..][0..4], @bitCast(u32, displacement));
1195 },
1196 .aarch64 => {
1197 const displacement = try math.cast(i28, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4);
1198 const literal = @divExact(stub_size - @sizeOf(u32), 4);
1199 // ldr w16, literal
1200 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{
1201 .literal = literal,
1202 }).toU32());
1203 // b disp
1204 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(displacement).toU32());
1205 mem.writeIntLittle(u32, code[8..12], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
1206 },
1207 else => unreachable,
1208 }
1209 try self.file.?.pwriteAll(code, stub_off);
1210}
1211
1212fn resolveSymbols(self: *Zld) !void {
1213 for (self.objects.items) |object, object_id| {
1214 const seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
1215 log.debug("\n\n", .{});
1216 log.debug("resolving symbols in {s}", .{object.name});
1217
1218 for (object.symtab.items) |sym| {
1219 if (isImport(&sym)) continue;
1220
1221 const sym_name = object.getString(sym.n_strx);
1222 const out_name = try self.allocator.dupe(u8, sym_name);
1223 const locs = try self.locals.getOrPut(self.allocator, out_name);
1224 defer {
1225 if (locs.found_existing) self.allocator.free(out_name);
1226 }
1227
1228 if (!locs.found_existing) {
1229 locs.entry.value = .{};
1230 }
1231
1232 const tt: Symbol.Type = blk: {
1233 if (isLocal(&sym)) {
1234 break :blk .Local;
1235 } else if (isWeakDef(&sym)) {
1236 break :blk .WeakGlobal;
1237 } else {
1238 break :blk .Global;
1239 }
1240 };
1241 if (tt == .Global) {
1242 for (locs.entry.value.items) |ss| {
1243 if (ss.tt == .Global) {
1244 log.debug("symbol already defined '{s}'", .{sym_name});
1245 continue;
1246 // log.err("symbol '{s}' defined multiple times: {}", .{ sym_name, sym });
1247 // return error.MultipleSymbolDefinitions;
1248 }
1249 }
1250 }
1251
1252 const source_sect_id = sym.n_sect - 1;
1253 const target_mapping = self.mappings.get(.{
1254 .object_id = @intCast(u16, object_id),
1255 .source_sect_id = source_sect_id,
1256 }) orelse {
1257 if (self.unhandled_sections.get(.{
1258 .object_id = @intCast(u16, object_id),
1259 .source_sect_id = source_sect_id,
1260 }) != null) continue;
1261
1262 log.err("section not mapped for symbol '{s}': {}", .{ sym_name, sym });
1263 return error.SectionNotMappedForSymbol;
1264 };
1265 const source_sect = seg.sections.items[source_sect_id];
1266 const target_seg = self.load_commands.items[target_mapping.target_seg_id].Segment;
1267 const target_sect = target_seg.sections.items[target_mapping.target_sect_id];
1268 const target_addr = target_sect.addr + target_mapping.offset;
1269 const n_value = sym.n_value - source_sect.addr + target_addr;
1270
1271 log.debug("resolving '{s}':{} as {s} symbol at 0x{x}", .{ sym_name, sym, tt, n_value });
1272
1273 // TODO there might be a more generic way of doing this.
1274 var n_sect: u16 = 0;
1275 for (self.load_commands.items) |cmd, cmd_id| {
1276 if (cmd != .Segment) break;
1277 if (cmd_id == target_mapping.target_seg_id) {
1278 n_sect += target_mapping.target_sect_id + 1;
1279 break;
1280 }
1281 n_sect += @intCast(u16, cmd.Segment.sections.items.len);
1282 }
1283
1284 const n_strx = try self.makeString(sym_name);
1285 try locs.entry.value.append(self.allocator, .{
1286 .inner = .{
1287 .n_strx = n_strx,
1288 .n_value = n_value,
1289 .n_type = macho.N_SECT,
1290 .n_desc = sym.n_desc,
1291 .n_sect = @intCast(u8, n_sect),
1292 },
1293 .tt = tt,
1294 .object_id = @intCast(u16, object_id),
1295 });
1296 }
1297 }
1298}
1299
1300fn doRelocs(self: *Zld) !void {
1301 for (self.objects.items) |object, object_id| {
1302 log.debug("\n\n", .{});
1303 log.debug("relocating object {s}", .{object.name});
1304
1305 const seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
1306
1307 for (seg.sections.items) |sect, source_sect_id| {
1308 const segname = parseName(&sect.segname);
1309 const sectname = parseName(&sect.sectname);
1310
1311 var code = try self.allocator.alloc(u8, sect.size);
1312 _ = try object.file.preadAll(code, sect.offset);
1313 defer self.allocator.free(code);
1314
1315 // Parse relocs (if any)
1316 var raw_relocs = try self.allocator.alloc(u8, @sizeOf(macho.relocation_info) * sect.nreloc);
1317 defer self.allocator.free(raw_relocs);
1318 _ = try object.file.preadAll(raw_relocs, sect.reloff);
1319 const relocs = mem.bytesAsSlice(macho.relocation_info, raw_relocs);
1320
1321 // Get mapping
1322 const target_mapping = self.mappings.get(.{
1323 .object_id = @intCast(u16, object_id),
1324 .source_sect_id = @intCast(u16, source_sect_id),
1325 }) orelse {
1326 log.debug("no mapping for {s},{s}; skipping", .{ segname, sectname });
1327 continue;
1328 };
1329 const target_seg = self.load_commands.items[target_mapping.target_seg_id].Segment;
1330 const target_sect = target_seg.sections.items[target_mapping.target_sect_id];
1331 const target_sect_addr = target_sect.addr + target_mapping.offset;
1332 const target_sect_off = target_sect.offset + target_mapping.offset;
1333
1334 var addend: ?u64 = null;
1335 var sub: ?i64 = null;
1336
1337 for (relocs) |rel| {
1338 const off = @intCast(u32, rel.r_address);
1339 const this_addr = target_sect_addr + off;
1340
1341 switch (self.arch.?) {
1342 .aarch64 => {
1343 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
1344 log.debug("{s}", .{rel_type});
1345 log.debug(" | source address 0x{x}", .{this_addr});
1346 log.debug(" | offset 0x{x}", .{off});
1347
1348 if (rel_type == .ARM64_RELOC_ADDEND) {
1349 addend = rel.r_symbolnum;
1350 log.debug(" | calculated addend = 0x{x}", .{addend});
1351 // TODO followed by either PAGE21 or PAGEOFF12 only.
1352 continue;
1353 }
1354 },
1355 .x86_64 => {
1356 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
1357 log.debug("{s}", .{rel_type});
1358 log.debug(" | source address 0x{x}", .{this_addr});
1359 log.debug(" | offset 0x{x}", .{off});
1360 },
1361 else => {},
1362 }
1363
1364 const target_addr = try self.relocTargetAddr(@intCast(u16, object_id), rel);
1365 log.debug(" | target address 0x{x}", .{target_addr});
1366 if (rel.r_extern == 1) {
1367 const target_symname = object.getString(object.symtab.items[rel.r_symbolnum].n_strx);
1368 log.debug(" | target symbol '{s}'", .{target_symname});
1369 } else {
1370 const target_sectname = seg.sections.items[rel.r_symbolnum - 1].sectname;
1371 log.debug(" | target section '{s}'", .{parseName(&target_sectname)});
1372 }
1373
1374 switch (self.arch.?) {
1375 .x86_64 => {
1376 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
1377
1378 switch (rel_type) {
1379 .X86_64_RELOC_BRANCH => {
1380 assert(rel.r_length == 2);
1381 const inst = code[off..][0..4];
1382 const displacement = @bitCast(u32, @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, this_addr) - 4));
1383 mem.writeIntLittle(u32, inst, displacement);
1384 },
1385 .X86_64_RELOC_GOT_LOAD => {
1386 assert(rel.r_length == 2);
1387 const inst = code[off..][0..4];
1388 const displacement = @bitCast(u32, @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, this_addr) - 4));
1389
1390 blk: {
1391 const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
1392 const got = data_const_seg.sections.items[self.got_section_index.?];
1393 if (got.addr <= target_addr and target_addr < got.addr + got.size) break :blk;
1394 log.debug(" | rewriting to leaq", .{});
1395 code[off - 2] = 0x8d;
1396 }
1397
1398 mem.writeIntLittle(u32, inst, displacement);
1399 },
1400 .X86_64_RELOC_GOT => {
1401 assert(rel.r_length == 2);
1402 // TODO Instead of referring to the target symbol directly, we refer to it
1403 // indirectly via GOT. Getting actual target address should be done in the
1404 // helper relocTargetAddr function rather than here.
1405 const sym = object.symtab.items[rel.r_symbolnum];
1406 const sym_name = try self.allocator.dupe(u8, object.getString(sym.n_strx));
1407 const res = try self.nonlazy_pointers.getOrPut(self.allocator, sym_name);
1408 defer if (res.found_existing) self.allocator.free(sym_name);
1409
1410 const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
1411 const got = data_const_seg.sections.items[self.got_section_index.?];
1412
1413 if (!res.found_existing) {
1414 const index = @intCast(u32, self.nonlazy_pointers.items().len) - 1;
1415 assert(index < max_local_got_indirections); // TODO This is just a temp solution.
1416 res.entry.value = .{
1417 .index = index,
1418 .target_addr = target_addr,
1419 };
1420 var buf: [@sizeOf(u64)]u8 = undefined;
1421 mem.writeIntLittle(u64, &buf, target_addr);
1422 const got_offset = got.offset + (index + self.nonlazy_imports.items().len) * @sizeOf(u64);
1423
1424 log.debug(" | GOT off 0x{x}", .{got.offset});
1425 log.debug(" | writing GOT entry 0x{x} at 0x{x}", .{ target_addr, got_offset });
1426
1427 try self.file.?.pwriteAll(&buf, got_offset);
1428 }
1429
1430 const index = res.entry.value.index + self.nonlazy_imports.items().len;
1431 const actual_target_addr = got.addr + index * @sizeOf(u64);
1432
1433 log.debug(" | GOT addr 0x{x}", .{got.addr});
1434 log.debug(" | actual target address in GOT 0x{x}", .{actual_target_addr});
1435
1436 const inst = code[off..][0..4];
1437 const displacement = @bitCast(u32, @intCast(i32, @intCast(i64, actual_target_addr) - @intCast(i64, this_addr) - 4));
1438 mem.writeIntLittle(u32, inst, displacement);
1439 },
1440 .X86_64_RELOC_TLV => {
1441 assert(rel.r_length == 2);
1442 // We need to rewrite the opcode from movq to leaq.
1443 code[off - 2] = 0x8d;
1444 // Add displacement.
1445 const inst = code[off..][0..4];
1446 const displacement = @bitCast(u32, @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, this_addr) - 4));
1447 mem.writeIntLittle(u32, inst, displacement);
1448 },
1449 .X86_64_RELOC_SIGNED,
1450 .X86_64_RELOC_SIGNED_1,
1451 .X86_64_RELOC_SIGNED_2,
1452 .X86_64_RELOC_SIGNED_4,
1453 => {
1454 assert(rel.r_length == 2);
1455 const inst = code[off..][0..4];
1456 const offset = @intCast(i64, mem.readIntLittle(i32, inst));
1457 log.debug(" | calculated addend 0x{x}", .{offset});
1458 const actual_target_addr = blk: {
1459 if (rel.r_extern == 1) {
1460 break :blk @intCast(i64, target_addr) + offset;
1461 } else {
1462 const correction: i4 = switch (rel_type) {
1463 .X86_64_RELOC_SIGNED => 0,
1464 .X86_64_RELOC_SIGNED_1 => 1,
1465 .X86_64_RELOC_SIGNED_2 => 2,
1466 .X86_64_RELOC_SIGNED_4 => 4,
1467 else => unreachable,
1468 };
1469 log.debug(" | calculated correction 0x{x}", .{correction});
1470
1471 // The value encoded in the instruction is a displacement - 4 - correction.
1472 // To obtain the adjusted target address in the final binary, we need
1473 // calculate the original target address within the object file, establish
1474 // what the offset from the original target section was, and apply this
1475 // offset to the resultant target section with this relocated binary.
1476 const orig_sect_id = @intCast(u16, rel.r_symbolnum - 1);
1477 const target_map = self.mappings.get(.{
1478 .object_id = @intCast(u16, object_id),
1479 .source_sect_id = orig_sect_id,
1480 }) orelse unreachable;
1481 const orig_seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
1482 const orig_sect = orig_seg.sections.items[orig_sect_id];
1483 const orig_offset = off + offset + 4 + correction - @intCast(i64, orig_sect.addr);
1484 log.debug(" | original offset 0x{x}", .{orig_offset});
1485 const adjusted = @intCast(i64, target_addr) + orig_offset;
1486 log.debug(" | adjusted target address 0x{x}", .{adjusted});
1487 break :blk adjusted - correction;
1488 }
1489 };
1490 const result = actual_target_addr - @intCast(i64, this_addr) - 4;
1491 const displacement = @bitCast(u32, @intCast(i32, result));
1492 mem.writeIntLittle(u32, inst, displacement);
1493 },
1494 .X86_64_RELOC_SUBTRACTOR => {
1495 sub = @intCast(i64, target_addr);
1496 },
1497 .X86_64_RELOC_UNSIGNED => {
1498 switch (rel.r_length) {
1499 3 => {
1500 const inst = code[off..][0..8];
1501 const offset = mem.readIntLittle(i64, inst);
1502
1503 const result = outer: {
1504 if (rel.r_extern == 1) {
1505 log.debug(" | calculated addend 0x{x}", .{offset});
1506 if (sub) |s| {
1507 break :outer @intCast(i64, target_addr) - s + offset;
1508 } else {
1509 break :outer @intCast(i64, target_addr) + offset;
1510 }
1511 } else {
1512 // The value encoded in the instruction is an absolute offset
1513 // from the start of MachO header to the target address in the
1514 // object file. To extract the address, we calculate the offset from
1515 // the beginning of the source section to the address, and apply it to
1516 // the target address value.
1517 const orig_sect_id = @intCast(u16, rel.r_symbolnum - 1);
1518 const target_map = self.mappings.get(.{
1519 .object_id = @intCast(u16, object_id),
1520 .source_sect_id = orig_sect_id,
1521 }) orelse unreachable;
1522 const orig_seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
1523 const orig_sect = orig_seg.sections.items[orig_sect_id];
1524 const orig_offset = offset - @intCast(i64, orig_sect.addr);
1525 const actual_target_addr = inner: {
1526 if (sub) |s| {
1527 break :inner @intCast(i64, target_addr) - s + orig_offset;
1528 } else {
1529 break :inner @intCast(i64, target_addr) + orig_offset;
1530 }
1531 };
1532 log.debug(" | adjusted target address 0x{x}", .{actual_target_addr});
1533 break :outer actual_target_addr;
1534 }
1535 };
1536 mem.writeIntLittle(u64, inst, @bitCast(u64, result));
1537 sub = null;
1538
1539 rebases: {
1540 var hit: bool = false;
1541 if (target_mapping.target_seg_id == self.data_segment_cmd_index.?) {
1542 if (self.data_section_index) |index| {
1543 if (index == target_mapping.target_sect_id) hit = true;
1544 }
1545 }
1546 if (target_mapping.target_seg_id == self.data_const_segment_cmd_index.?) {
1547 if (self.data_const_section_index) |index| {
1548 if (index == target_mapping.target_sect_id) hit = true;
1549 }
1550 }
1551
1552 if (!hit) break :rebases;
1553
1554 try self.local_rebases.append(self.allocator, .{
1555 .offset = this_addr - target_seg.inner.vmaddr,
1556 .segment_id = target_mapping.target_seg_id,
1557 });
1558 }
1559 // TLV is handled via a separate offset mechanism.
1560 // Calculate the offset to the initializer.
1561 if (target_sect.flags == macho.S_THREAD_LOCAL_VARIABLES) tlv: {
1562 assert(rel.r_extern == 1);
1563 const sym = object.symtab.items[rel.r_symbolnum];
1564 if (isImport(&sym)) break :tlv;
1565
1566 const base_addr = blk: {
1567 if (self.tlv_data_section_index) |index| {
1568 const tlv_data = target_seg.sections.items[index];
1569 break :blk tlv_data.addr;
1570 } else {
1571 const tlv_bss = target_seg.sections.items[self.tlv_bss_section_index.?];
1572 break :blk tlv_bss.addr;
1573 }
1574 };
1575 // Since we require TLV data to always preceed TLV bss section, we calculate
1576 // offsets wrt to the former if it is defined; otherwise, wrt to the latter.
1577 try self.threadlocal_offsets.append(self.allocator, target_addr - base_addr);
1578 }
1579 },
1580 2 => {
1581 const inst = code[off..][0..4];
1582 const offset = mem.readIntLittle(i32, inst);
1583 log.debug(" | calculated addend 0x{x}", .{offset});
1584 const result = if (sub) |s|
1585 @intCast(i64, target_addr) - s + offset
1586 else
1587 @intCast(i64, target_addr) + offset;
1588 mem.writeIntLittle(u32, inst, @truncate(u32, @bitCast(u64, result)));
1589 sub = null;
1590 },
1591 else => |len| {
1592 log.err("unexpected relocation length 0x{x}", .{len});
1593 return error.UnexpectedRelocationLength;
1594 },
1595 }
1596 },
1597 }
1598 },
1599 .aarch64 => {
1600 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
1601
1602 switch (rel_type) {
1603 .ARM64_RELOC_BRANCH26 => {
1604 assert(rel.r_length == 2);
1605 const inst = code[off..][0..4];
1606 const displacement = @intCast(
1607 i28,
1608 @intCast(i64, target_addr) - @intCast(i64, this_addr),
1609 );
1610 var parsed = mem.bytesAsValue(
1611 meta.TagPayload(
1612 aarch64.Instruction,
1613 aarch64.Instruction.UnconditionalBranchImmediate,
1614 ),
1615 inst,
1616 );
1617 parsed.imm26 = @truncate(u26, @bitCast(u28, displacement) >> 2);
1618 },
1619 .ARM64_RELOC_PAGE21,
1620 .ARM64_RELOC_GOT_LOAD_PAGE21,
1621 .ARM64_RELOC_TLVP_LOAD_PAGE21,
1622 => {
1623 assert(rel.r_length == 2);
1624 const inst = code[off..][0..4];
1625 const ta = if (addend) |a| target_addr + a else target_addr;
1626 const this_page = @intCast(i32, this_addr >> 12);
1627 const target_page = @intCast(i32, ta >> 12);
1628 const pages = @bitCast(u21, @intCast(i21, target_page - this_page));
1629 log.debug(" | moving by {} pages", .{pages});
1630 var parsed = mem.bytesAsValue(
1631 meta.TagPayload(
1632 aarch64.Instruction,
1633 aarch64.Instruction.PCRelativeAddress,
1634 ),
1635 inst,
1636 );
1637 parsed.immhi = @truncate(u19, pages >> 2);
1638 parsed.immlo = @truncate(u2, pages);
1639 addend = null;
1640 },
1641 .ARM64_RELOC_PAGEOFF12,
1642 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
1643 => {
1644 const inst = code[off..][0..4];
1645 if (aarch64IsArithmetic(inst)) {
1646 log.debug(" | detected ADD opcode", .{});
1647 // add
1648 var parsed = mem.bytesAsValue(
1649 meta.TagPayload(
1650 aarch64.Instruction,
1651 aarch64.Instruction.AddSubtractImmediate,
1652 ),
1653 inst,
1654 );
1655 const ta = if (addend) |a| target_addr + a else target_addr;
1656 const narrowed = @truncate(u12, ta);
1657 parsed.imm12 = narrowed;
1658 } else {
1659 log.debug(" | detected LDR/STR opcode", .{});
1660 // ldr/str
1661 var parsed = mem.bytesAsValue(
1662 meta.TagPayload(
1663 aarch64.Instruction,
1664 aarch64.Instruction.LoadStoreRegister,
1665 ),
1666 inst,
1667 );
1668
1669 const ta = if (addend) |a| target_addr + a else target_addr;
1670 const narrowed = @truncate(u12, ta);
1671 log.debug(" | narrowed 0x{x}", .{narrowed});
1672 log.debug(" | parsed.size 0x{x}", .{parsed.size});
1673
1674 if (rel_type == .ARM64_RELOC_GOT_LOAD_PAGEOFF12) blk: {
1675 const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
1676 const got = data_const_seg.sections.items[self.got_section_index.?];
1677 if (got.addr <= target_addr and target_addr < got.addr + got.size) break :blk;
1678
1679 log.debug(" | rewriting to add", .{});
1680 mem.writeIntLittle(u32, inst, aarch64.Instruction.add(
1681 @intToEnum(aarch64.Register, parsed.rt),
1682 @intToEnum(aarch64.Register, parsed.rn),
1683 narrowed,
1684 false,
1685 ).toU32());
1686 addend = null;
1687 continue;
1688 }
1689
1690 const offset: u12 = blk: {
1691 if (parsed.size == 0) {
1692 if (parsed.v == 1) {
1693 // 128-bit SIMD is scaled by 16.
1694 break :blk try math.divExact(u12, narrowed, 16);
1695 }
1696 // Otherwise, 8-bit SIMD or ldrb.
1697 break :blk narrowed;
1698 } else {
1699 const denom: u4 = try math.powi(u4, 2, parsed.size);
1700 break :blk try math.divExact(u12, narrowed, denom);
1701 }
1702 };
1703 parsed.offset = offset;
1704 }
1705 addend = null;
1706 },
1707 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {
1708 const RegInfo = struct {
1709 rd: u5,
1710 rn: u5,
1711 size: u1,
1712 };
1713 const inst = code[off..][0..4];
1714 const parsed: RegInfo = blk: {
1715 if (aarch64IsArithmetic(inst)) {
1716 const curr = mem.bytesAsValue(
1717 meta.TagPayload(
1718 aarch64.Instruction,
1719 aarch64.Instruction.AddSubtractImmediate,
1720 ),
1721 inst,
1722 );
1723 break :blk .{ .rd = curr.rd, .rn = curr.rn, .size = curr.sf };
1724 } else {
1725 const curr = mem.bytesAsValue(
1726 meta.TagPayload(
1727 aarch64.Instruction,
1728 aarch64.Instruction.LoadStoreRegister,
1729 ),
1730 inst,
1731 );
1732 break :blk .{ .rd = curr.rt, .rn = curr.rn, .size = @truncate(u1, curr.size) };
1733 }
1734 };
1735 const ta = if (addend) |a| target_addr + a else target_addr;
1736 const narrowed = @truncate(u12, ta);
1737 log.debug(" | rewriting TLV access to ADD opcode", .{});
1738 // For TLV, we always generate an add instruction.
1739 mem.writeIntLittle(u32, inst, aarch64.Instruction.add(
1740 @intToEnum(aarch64.Register, parsed.rd),
1741 @intToEnum(aarch64.Register, parsed.rn),
1742 narrowed,
1743 false,
1744 ).toU32());
1745 },
1746 .ARM64_RELOC_SUBTRACTOR => {
1747 sub = @intCast(i64, target_addr);
1748 },
1749 .ARM64_RELOC_UNSIGNED => {
1750 switch (rel.r_length) {
1751 3 => {
1752 const inst = code[off..][0..8];
1753 const offset = mem.readIntLittle(i64, inst);
1754 log.debug(" | calculated addend 0x{x}", .{offset});
1755 const result = if (sub) |s|
1756 @intCast(i64, target_addr) - s + offset
1757 else
1758 @intCast(i64, target_addr) + offset;
1759 mem.writeIntLittle(u64, inst, @bitCast(u64, result));
1760 sub = null;
1761
1762 rebases: {
1763 var hit: bool = false;
1764 if (target_mapping.target_seg_id == self.data_segment_cmd_index.?) {
1765 if (self.data_section_index) |index| {
1766 if (index == target_mapping.target_sect_id) hit = true;
1767 }
1768 }
1769 if (target_mapping.target_seg_id == self.data_const_segment_cmd_index.?) {
1770 if (self.data_const_section_index) |index| {
1771 if (index == target_mapping.target_sect_id) hit = true;
1772 }
1773 }
1774
1775 if (!hit) break :rebases;
1776
1777 try self.local_rebases.append(self.allocator, .{
1778 .offset = this_addr - target_seg.inner.vmaddr,
1779 .segment_id = target_mapping.target_seg_id,
1780 });
1781 }
1782 // TLV is handled via a separate offset mechanism.
1783 // Calculate the offset to the initializer.
1784 if (target_sect.flags == macho.S_THREAD_LOCAL_VARIABLES) tlv: {
1785 assert(rel.r_extern == 1);
1786 const sym = object.symtab.items[rel.r_symbolnum];
1787 if (isImport(&sym)) break :tlv;
1788
1789 const base_addr = blk: {
1790 if (self.tlv_data_section_index) |index| {
1791 const tlv_data = target_seg.sections.items[index];
1792 break :blk tlv_data.addr;
1793 } else {
1794 const tlv_bss = target_seg.sections.items[self.tlv_bss_section_index.?];
1795 break :blk tlv_bss.addr;
1796 }
1797 };
1798 // Since we require TLV data to always preceed TLV bss section, we calculate
1799 // offsets wrt to the former if it is defined; otherwise, wrt to the latter.
1800 try self.threadlocal_offsets.append(self.allocator, target_addr - base_addr);
1801 }
1802 },
1803 2 => {
1804 const inst = code[off..][0..4];
1805 const offset = mem.readIntLittle(i32, inst);
1806 log.debug(" | calculated addend 0x{x}", .{offset});
1807 const result = if (sub) |s|
1808 @intCast(i64, target_addr) - s + offset
1809 else
1810 @intCast(i64, target_addr) + offset;
1811 mem.writeIntLittle(u32, inst, @truncate(u32, @bitCast(u64, result)));
1812 sub = null;
1813 },
1814 else => |len| {
1815 log.err("unexpected relocation length 0x{x}", .{len});
1816 return error.UnexpectedRelocationLength;
1817 },
1818 }
1819 },
1820 .ARM64_RELOC_POINTER_TO_GOT => return error.TODOArm64RelocPointerToGot,
1821 else => unreachable,
1822 }
1823 },
1824 else => unreachable,
1825 }
1826 }
1827
1828 log.debug("writing contents of '{s},{s}' section from '{s}' from 0x{x} to 0x{x}", .{
1829 segname,
1830 sectname,
1831 object.name,
1832 target_sect_off,
1833 target_sect_off + code.len,
1834 });
1835
1836 if (target_sect.flags == macho.S_ZEROFILL or
1837 target_sect.flags == macho.S_THREAD_LOCAL_ZEROFILL or
1838 target_sect.flags == macho.S_THREAD_LOCAL_VARIABLES)
1839 {
1840 log.debug("zeroing out '{s},{s}' from 0x{x} to 0x{x}", .{
1841 parseName(&target_sect.segname),
1842 parseName(&target_sect.sectname),
1843 target_sect_off,
1844 target_sect_off + code.len,
1845 });
1846 // Zero-out the space
1847 var zeroes = try self.allocator.alloc(u8, code.len);
1848 defer self.allocator.free(zeroes);
1849 mem.set(u8, zeroes, 0);
1850 try self.file.?.pwriteAll(zeroes, target_sect_off);
1851 } else {
1852 try self.file.?.pwriteAll(code, target_sect_off);
1853 }
1854 }
1855 }
1856}
1857
1858fn relocTargetAddr(self: *Zld, object_id: u16, rel: macho.relocation_info) !u64 {
1859 const object = self.objects.items[object_id];
1860 const seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
1861 const target_addr = blk: {
1862 if (rel.r_extern == 1) {
1863 const sym = object.symtab.items[rel.r_symbolnum];
1864 if (isLocal(&sym) or isExport(&sym)) {
1865 // Relocate using section offsets only.
1866 const target_mapping = self.mappings.get(.{
1867 .object_id = object_id,
1868 .source_sect_id = sym.n_sect - 1,
1869 }) orelse unreachable;
1870 const source_sect = seg.sections.items[target_mapping.source_sect_id];
1871 const target_seg = self.load_commands.items[target_mapping.target_seg_id].Segment;
1872 const target_sect = target_seg.sections.items[target_mapping.target_sect_id];
1873 const target_sect_addr = target_sect.addr + target_mapping.offset;
1874 log.debug(" | symbol local to object", .{});
1875 break :blk target_sect_addr + sym.n_value - source_sect.addr;
1876 } else if (isImport(&sym)) {
1877 // Relocate to either the artifact's local symbol, or an import from
1878 // shared library.
1879 const sym_name = object.getString(sym.n_strx);
1880 if (self.locals.get(sym_name)) |locs| {
1881 var n_value: ?u64 = null;
1882 for (locs.items) |loc| {
1883 switch (loc.tt) {
1884 .Global => {
1885 n_value = loc.inner.n_value;
1886 break;
1887 },
1888 .WeakGlobal => {
1889 n_value = loc.inner.n_value;
1890 },
1891 .Local => {},
1892 }
1893 }
1894 if (n_value) |v| {
1895 break :blk v;
1896 }
1897 log.err("local symbol export '{s}' not found", .{sym_name});
1898 return error.LocalSymbolExportNotFound;
1899 } else if (self.lazy_imports.get(sym_name)) |ext| {
1900 const segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1901 const stubs = segment.sections.items[self.stubs_section_index.?];
1902 break :blk stubs.addr + ext.index * stubs.reserved2;
1903 } else if (self.nonlazy_imports.get(sym_name)) |ext| {
1904 const segment = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
1905 const got = segment.sections.items[self.got_section_index.?];
1906 break :blk got.addr + ext.index * @sizeOf(u64);
1907 } else if (mem.eql(u8, sym_name, "__tlv_bootstrap")) {
1908 const segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
1909 const tlv = segment.sections.items[self.tlv_section_index.?];
1910 break :blk tlv.addr + self.tlv_bootstrap.?.index * @sizeOf(u64);
1911 } else {
1912 log.err("failed to resolve symbol '{s}' as a relocation target", .{sym_name});
1913 return error.FailedToResolveRelocationTarget;
1914 }
1915 } else {
1916 log.err("unexpected symbol {}, {s}", .{ sym, object.getString(sym.n_strx) });
1917 return error.UnexpectedSymbolWhenRelocating;
1918 }
1919 } else {
1920 // TODO I think we need to reparse the relocation_info as scattered_relocation_info
1921 // here to get the actual section plus offset into that section of the relocated
1922 // symbol. Unless the fine-grained location is encoded within the cell in the code
1923 // buffer?
1924 const target_mapping = self.mappings.get(.{
1925 .object_id = object_id,
1926 .source_sect_id = @intCast(u16, rel.r_symbolnum - 1),
1927 }) orelse unreachable;
1928 const target_seg = self.load_commands.items[target_mapping.target_seg_id].Segment;
1929 const target_sect = target_seg.sections.items[target_mapping.target_sect_id];
1930 break :blk target_sect.addr + target_mapping.offset;
1931 }
1932 };
1933 return target_addr;
1934}
1935
1936fn populateMetadata(self: *Zld) !void {
1937 if (self.pagezero_segment_cmd_index == null) {
1938 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
1939 try self.load_commands.append(self.allocator, .{
1940 .Segment = SegmentCommand.empty(.{
1941 .cmd = macho.LC_SEGMENT_64,
1942 .cmdsize = @sizeOf(macho.segment_command_64),
1943 .segname = makeStaticString("__PAGEZERO"),
1944 .vmaddr = 0,
1945 .vmsize = 0x100000000, // size always set to 4GB
1946 .fileoff = 0,
1947 .filesize = 0,
1948 .maxprot = 0,
1949 .initprot = 0,
1950 .nsects = 0,
1951 .flags = 0,
1952 }),
1953 });
1954 }
1955
1956 if (self.text_segment_cmd_index == null) {
1957 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
1958 try self.load_commands.append(self.allocator, .{
1959 .Segment = SegmentCommand.empty(.{
1960 .cmd = macho.LC_SEGMENT_64,
1961 .cmdsize = @sizeOf(macho.segment_command_64),
1962 .segname = makeStaticString("__TEXT"),
1963 .vmaddr = 0x100000000, // always starts at 4GB
1964 .vmsize = 0,
1965 .fileoff = 0,
1966 .filesize = 0,
1967 .maxprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE,
1968 .initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE,
1969 .nsects = 0,
1970 .flags = 0,
1971 }),
1972 });
1973 }
1974
1975 if (self.text_section_index == null) {
1976 const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1977 self.text_section_index = @intCast(u16, text_seg.sections.items.len);
1978 const alignment: u2 = switch (self.arch.?) {
1979 .x86_64 => 0,
1980 .aarch64 => 2,
1981 else => unreachable, // unhandled architecture type
1982 };
1983 try text_seg.addSection(self.allocator, .{
1984 .sectname = makeStaticString("__text"),
1985 .segname = makeStaticString("__TEXT"),
1986 .addr = 0,
1987 .size = 0,
1988 .offset = 0,
1989 .@"align" = alignment,
1990 .reloff = 0,
1991 .nreloc = 0,
1992 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
1993 .reserved1 = 0,
1994 .reserved2 = 0,
1995 .reserved3 = 0,
1996 });
1997 }
1998
1999 if (self.stubs_section_index == null) {
2000 const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2001 self.stubs_section_index = @intCast(u16, text_seg.sections.items.len);
2002 const alignment: u2 = switch (self.arch.?) {
2003 .x86_64 => 0,
2004 .aarch64 => 2,
2005 else => unreachable, // unhandled architecture type
2006 };
2007 const stub_size: u4 = switch (self.arch.?) {
2008 .x86_64 => 6,
2009 .aarch64 => 3 * @sizeOf(u32),
2010 else => unreachable, // unhandled architecture type
2011 };
2012 try text_seg.addSection(self.allocator, .{
2013 .sectname = makeStaticString("__stubs"),
2014 .segname = makeStaticString("__TEXT"),
2015 .addr = 0,
2016 .size = 0,
2017 .offset = 0,
2018 .@"align" = alignment,
2019 .reloff = 0,
2020 .nreloc = 0,
2021 .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
2022 .reserved1 = 0,
2023 .reserved2 = stub_size,
2024 .reserved3 = 0,
2025 });
2026 }
2027
2028 if (self.stub_helper_section_index == null) {
2029 const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2030 self.stub_helper_section_index = @intCast(u16, text_seg.sections.items.len);
2031 const alignment: u2 = switch (self.arch.?) {
2032 .x86_64 => 0,
2033 .aarch64 => 2,
2034 else => unreachable, // unhandled architecture type
2035 };
2036 const stub_helper_size: u6 = switch (self.arch.?) {
2037 .x86_64 => 15,
2038 .aarch64 => 6 * @sizeOf(u32),
2039 else => unreachable,
2040 };
2041 try text_seg.addSection(self.allocator, .{
2042 .sectname = makeStaticString("__stub_helper"),
2043 .segname = makeStaticString("__TEXT"),
2044 .addr = 0,
2045 .size = stub_helper_size,
2046 .offset = 0,
2047 .@"align" = alignment,
2048 .reloff = 0,
2049 .nreloc = 0,
2050 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
2051 .reserved1 = 0,
2052 .reserved2 = 0,
2053 .reserved3 = 0,
2054 });
2055 }
2056
2057 if (self.data_const_segment_cmd_index == null) {
2058 self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
2059 try self.load_commands.append(self.allocator, .{
2060 .Segment = SegmentCommand.empty(.{
2061 .cmd = macho.LC_SEGMENT_64,
2062 .cmdsize = @sizeOf(macho.segment_command_64),
2063 .segname = makeStaticString("__DATA_CONST"),
2064 .vmaddr = 0,
2065 .vmsize = 0,
2066 .fileoff = 0,
2067 .filesize = 0,
2068 .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
2069 .initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
2070 .nsects = 0,
2071 .flags = 0,
2072 }),
2073 });
2074 }
2075
2076 if (self.got_section_index == null) {
2077 const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2078 self.got_section_index = @intCast(u16, data_const_seg.sections.items.len);
2079 try data_const_seg.addSection(self.allocator, .{
2080 .sectname = makeStaticString("__got"),
2081 .segname = makeStaticString("__DATA_CONST"),
2082 .addr = 0,
2083 .size = 0,
2084 .offset = 0,
2085 .@"align" = 3, // 2^3 = @sizeOf(u64)
2086 .reloff = 0,
2087 .nreloc = 0,
2088 .flags = macho.S_NON_LAZY_SYMBOL_POINTERS,
2089 .reserved1 = 0,
2090 .reserved2 = 0,
2091 .reserved3 = 0,
2092 });
2093 }
2094
2095 if (self.data_segment_cmd_index == null) {
2096 self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
2097 try self.load_commands.append(self.allocator, .{
2098 .Segment = SegmentCommand.empty(.{
2099 .cmd = macho.LC_SEGMENT_64,
2100 .cmdsize = @sizeOf(macho.segment_command_64),
2101 .segname = makeStaticString("__DATA"),
2102 .vmaddr = 0,
2103 .vmsize = 0,
2104 .fileoff = 0,
2105 .filesize = 0,
2106 .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
2107 .initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
2108 .nsects = 0,
2109 .flags = 0,
2110 }),
2111 });
2112 }
2113
2114 if (self.la_symbol_ptr_section_index == null) {
2115 const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2116 self.la_symbol_ptr_section_index = @intCast(u16, data_seg.sections.items.len);
2117 try data_seg.addSection(self.allocator, .{
2118 .sectname = makeStaticString("__la_symbol_ptr"),
2119 .segname = makeStaticString("__DATA"),
2120 .addr = 0,
2121 .size = 0,
2122 .offset = 0,
2123 .@"align" = 3, // 2^3 = @sizeOf(u64)
2124 .reloff = 0,
2125 .nreloc = 0,
2126 .flags = macho.S_LAZY_SYMBOL_POINTERS,
2127 .reserved1 = 0,
2128 .reserved2 = 0,
2129 .reserved3 = 0,
2130 });
2131 }
2132
2133 if (self.data_section_index == null) {
2134 const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2135 self.data_section_index = @intCast(u16, data_seg.sections.items.len);
2136 try data_seg.addSection(self.allocator, .{
2137 .sectname = makeStaticString("__data"),
2138 .segname = makeStaticString("__DATA"),
2139 .addr = 0,
2140 .size = 0,
2141 .offset = 0,
2142 .@"align" = 3, // 2^3 = @sizeOf(u64)
2143 .reloff = 0,
2144 .nreloc = 0,
2145 .flags = macho.S_REGULAR,
2146 .reserved1 = 0,
2147 .reserved2 = 0,
2148 .reserved3 = 0,
2149 });
2150 }
2151
2152 if (self.linkedit_segment_cmd_index == null) {
2153 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
2154 try self.load_commands.append(self.allocator, .{
2155 .Segment = SegmentCommand.empty(.{
2156 .cmd = macho.LC_SEGMENT_64,
2157 .cmdsize = @sizeOf(macho.segment_command_64),
2158 .segname = makeStaticString("__LINKEDIT"),
2159 .vmaddr = 0,
2160 .vmsize = 0,
2161 .fileoff = 0,
2162 .filesize = 0,
2163 .maxprot = macho.VM_PROT_READ,
2164 .initprot = macho.VM_PROT_READ,
2165 .nsects = 0,
2166 .flags = 0,
2167 }),
2168 });
2169 }
2170
2171 if (self.dyld_info_cmd_index == null) {
2172 self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len);
2173 try self.load_commands.append(self.allocator, .{
2174 .DyldInfoOnly = .{
2175 .cmd = macho.LC_DYLD_INFO_ONLY,
2176 .cmdsize = @sizeOf(macho.dyld_info_command),
2177 .rebase_off = 0,
2178 .rebase_size = 0,
2179 .bind_off = 0,
2180 .bind_size = 0,
2181 .weak_bind_off = 0,
2182 .weak_bind_size = 0,
2183 .lazy_bind_off = 0,
2184 .lazy_bind_size = 0,
2185 .export_off = 0,
2186 .export_size = 0,
2187 },
2188 });
2189 }
2190
2191 if (self.symtab_cmd_index == null) {
2192 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
2193 try self.load_commands.append(self.allocator, .{
2194 .Symtab = .{
2195 .cmd = macho.LC_SYMTAB,
2196 .cmdsize = @sizeOf(macho.symtab_command),
2197 .symoff = 0,
2198 .nsyms = 0,
2199 .stroff = 0,
2200 .strsize = 0,
2201 },
2202 });
2203 try self.strtab.append(self.allocator, 0);
2204 }
2205
2206 if (self.dysymtab_cmd_index == null) {
2207 self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len);
2208 try self.load_commands.append(self.allocator, .{
2209 .Dysymtab = .{
2210 .cmd = macho.LC_DYSYMTAB,
2211 .cmdsize = @sizeOf(macho.dysymtab_command),
2212 .ilocalsym = 0,
2213 .nlocalsym = 0,
2214 .iextdefsym = 0,
2215 .nextdefsym = 0,
2216 .iundefsym = 0,
2217 .nundefsym = 0,
2218 .tocoff = 0,
2219 .ntoc = 0,
2220 .modtaboff = 0,
2221 .nmodtab = 0,
2222 .extrefsymoff = 0,
2223 .nextrefsyms = 0,
2224 .indirectsymoff = 0,
2225 .nindirectsyms = 0,
2226 .extreloff = 0,
2227 .nextrel = 0,
2228 .locreloff = 0,
2229 .nlocrel = 0,
2230 },
2231 });
2232 }
2233
2234 if (self.dylinker_cmd_index == null) {
2235 self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len);
2236 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
2237 u64,
2238 @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH),
2239 @sizeOf(u64),
2240 ));
2241 var dylinker_cmd = emptyGenericCommandWithData(macho.dylinker_command{
2242 .cmd = macho.LC_LOAD_DYLINKER,
2243 .cmdsize = cmdsize,
2244 .name = @sizeOf(macho.dylinker_command),
2245 });
2246 dylinker_cmd.data = try self.allocator.alloc(u8, cmdsize - dylinker_cmd.inner.name);
2247 mem.set(u8, dylinker_cmd.data, 0);
2248 mem.copy(u8, dylinker_cmd.data, mem.spanZ(DEFAULT_DYLD_PATH));
2249 try self.load_commands.append(self.allocator, .{ .Dylinker = dylinker_cmd });
2250 }
2251
2252 if (self.libsystem_cmd_index == null) {
2253 self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len);
2254 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
2255 u64,
2256 @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH),
2257 @sizeOf(u64),
2258 ));
2259 // TODO Find a way to work out runtime version from the OS version triple stored in std.Target.
2260 // In the meantime, we're gonna hardcode to the minimum compatibility version of 0.0.0.
2261 const min_version = 0x0;
2262 var dylib_cmd = emptyGenericCommandWithData(macho.dylib_command{
2263 .cmd = macho.LC_LOAD_DYLIB,
2264 .cmdsize = cmdsize,
2265 .dylib = .{
2266 .name = @sizeOf(macho.dylib_command),
2267 .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files
2268 .current_version = min_version,
2269 .compatibility_version = min_version,
2270 },
2271 });
2272 dylib_cmd.data = try self.allocator.alloc(u8, cmdsize - dylib_cmd.inner.dylib.name);
2273 mem.set(u8, dylib_cmd.data, 0);
2274 mem.copy(u8, dylib_cmd.data, mem.spanZ(LIB_SYSTEM_PATH));
2275 try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
2276 }
2277
2278 if (self.main_cmd_index == null) {
2279 self.main_cmd_index = @intCast(u16, self.load_commands.items.len);
2280 try self.load_commands.append(self.allocator, .{
2281 .Main = .{
2282 .cmd = macho.LC_MAIN,
2283 .cmdsize = @sizeOf(macho.entry_point_command),
2284 .entryoff = 0x0,
2285 .stacksize = 0,
2286 },
2287 });
2288 }
2289
2290 if (self.source_version_cmd_index == null) {
2291 self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len);
2292 try self.load_commands.append(self.allocator, .{
2293 .SourceVersion = .{
2294 .cmd = macho.LC_SOURCE_VERSION,
2295 .cmdsize = @sizeOf(macho.source_version_command),
2296 .version = 0x0,
2297 },
2298 });
2299 }
2300
2301 if (self.uuid_cmd_index == null) {
2302 self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len);
2303 var uuid_cmd: macho.uuid_command = .{
2304 .cmd = macho.LC_UUID,
2305 .cmdsize = @sizeOf(macho.uuid_command),
2306 .uuid = undefined,
2307 };
2308 std.crypto.random.bytes(&uuid_cmd.uuid);
2309 try self.load_commands.append(self.allocator, .{ .Uuid = uuid_cmd });
2310 }
2311
2312 if (self.code_signature_cmd_index == null and self.arch.? == .aarch64) {
2313 self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);
2314 try self.load_commands.append(self.allocator, .{
2315 .LinkeditData = .{
2316 .cmd = macho.LC_CODE_SIGNATURE,
2317 .cmdsize = @sizeOf(macho.linkedit_data_command),
2318 .dataoff = 0,
2319 .datasize = 0,
2320 },
2321 });
2322 }
2323
2324 if (self.data_in_code_cmd_index == null and self.arch.? == .x86_64) {
2325 self.data_in_code_cmd_index = @intCast(u16, self.load_commands.items.len);
2326 try self.load_commands.append(self.allocator, .{
2327 .LinkeditData = .{
2328 .cmd = macho.LC_DATA_IN_CODE,
2329 .cmdsize = @sizeOf(macho.linkedit_data_command),
2330 .dataoff = 0,
2331 .datasize = 0,
2332 },
2333 });
2334 }
2335}
2336
2337fn flush(self: *Zld) !void {
2338 if (self.bss_section_index) |index| {
2339 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2340 const sect = &seg.sections.items[index];
2341 sect.offset = 0;
2342 }
2343
2344 if (self.tlv_bss_section_index) |index| {
2345 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2346 const sect = &seg.sections.items[index];
2347 sect.offset = 0;
2348 }
2349
2350 if (self.tlv_section_index) |index| {
2351 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2352 const sect = &seg.sections.items[index];
2353
2354 var buffer = try self.allocator.alloc(u8, sect.size);
2355 defer self.allocator.free(buffer);
2356 _ = try self.file.?.preadAll(buffer, sect.offset);
2357
2358 var stream = std.io.fixedBufferStream(buffer);
2359 var writer = stream.writer();
2360
2361 const seek_amt = 2 * @sizeOf(u64);
2362 while (self.threadlocal_offsets.popOrNull()) |offset| {
2363 try writer.context.seekBy(seek_amt);
2364 try writer.writeIntLittle(u64, offset);
2365 }
2366
2367 try self.file.?.pwriteAll(buffer, sect.offset);
2368 }
2369
2370 try self.setEntryPoint();
2371 try self.writeRebaseInfoTable();
2372 try self.writeBindInfoTable();
2373 try self.writeLazyBindInfoTable();
2374 try self.writeExportInfo();
2375 if (self.arch.? == .x86_64) {
2376 try self.writeDataInCode();
2377 }
2378
2379 {
2380 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2381 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2382 symtab.symoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
2383 }
2384
2385 try self.writeDebugInfo();
2386 try self.writeSymbolTable();
2387 try self.writeDynamicSymbolTable();
2388 try self.writeStringTable();
2389
2390 {
2391 // Seal __LINKEDIT size
2392 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2393 seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size.?);
2394 }
2395
2396 if (self.arch.? == .aarch64) {
2397 try self.writeCodeSignaturePadding();
2398 }
2399
2400 try self.writeLoadCommands();
2401 try self.writeHeader();
2402
2403 if (self.arch.? == .aarch64) {
2404 try self.writeCodeSignature();
2405 }
2406
2407 if (comptime std.Target.current.isDarwin() and std.Target.current.cpu.arch == .aarch64) {
2408 try fs.cwd().copyFile(self.out_path.?, fs.cwd(), self.out_path.?, .{});
2409 }
2410}
2411
2412fn setEntryPoint(self: *Zld) !void {
2413 // TODO we should respect the -entry flag passed in by the user to set a custom
2414 // entrypoint. For now, assume default of `_main`.
2415 const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2416 const text = seg.sections.items[self.text_section_index.?];
2417 const entry_syms = self.locals.get("_main") orelse return error.MissingMainEntrypoint;
2418
2419 var entry_sym: ?macho.nlist_64 = null;
2420 for (entry_syms.items) |es| {
2421 switch (es.tt) {
2422 .Global => {
2423 entry_sym = es.inner;
2424 break;
2425 },
2426 .WeakGlobal => {
2427 entry_sym = es.inner;
2428 },
2429 .Local => {},
2430 }
2431 }
2432 if (entry_sym == null) {
2433 log.err("no (weak) global definition of _main found", .{});
2434 return error.MissingMainEntrypoint;
2435 }
2436
2437 const name = try self.allocator.dupe(u8, "_main");
2438 try self.exports.putNoClobber(self.allocator, name, .{
2439 .n_strx = entry_sym.?.n_strx,
2440 .n_value = entry_sym.?.n_value,
2441 .n_type = macho.N_SECT | macho.N_EXT,
2442 .n_desc = entry_sym.?.n_desc,
2443 .n_sect = entry_sym.?.n_sect,
2444 });
2445
2446 const ec = &self.load_commands.items[self.main_cmd_index.?].Main;
2447 ec.entryoff = @intCast(u32, entry_sym.?.n_value - seg.inner.vmaddr);
2448}
2449
2450fn writeRebaseInfoTable(self: *Zld) !void {
2451 var pointers = std.ArrayList(Pointer).init(self.allocator);
2452 defer pointers.deinit();
2453
2454 try pointers.ensureCapacity(pointers.items.len + self.local_rebases.items.len);
2455 pointers.appendSliceAssumeCapacity(self.local_rebases.items);
2456
2457 if (self.got_section_index) |idx| {
2458 // TODO this should be cleaned up!
2459 try pointers.ensureCapacity(pointers.items.len + self.nonlazy_pointers.items().len);
2460 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2461 const sect = seg.sections.items[idx];
2462 const base_offset = sect.addr - seg.inner.vmaddr;
2463 const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
2464 const index_offset = @intCast(u32, self.nonlazy_imports.items().len);
2465 for (self.nonlazy_pointers.items()) |entry| {
2466 const index = index_offset + entry.value.index;
2467 pointers.appendAssumeCapacity(.{
2468 .offset = base_offset + index * @sizeOf(u64),
2469 .segment_id = segment_id,
2470 });
2471 }
2472 }
2473
2474 if (self.la_symbol_ptr_section_index) |idx| {
2475 try pointers.ensureCapacity(pointers.items.len + self.lazy_imports.items().len);
2476 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2477 const sect = seg.sections.items[idx];
2478 const base_offset = sect.addr - seg.inner.vmaddr;
2479 const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
2480 for (self.lazy_imports.items()) |entry| {
2481 pointers.appendAssumeCapacity(.{
2482 .offset = base_offset + entry.value.index * @sizeOf(u64),
2483 .segment_id = segment_id,
2484 });
2485 }
2486 }
2487
2488 std.sort.sort(Pointer, pointers.items, {}, pointerCmp);
2489
2490 const size = try rebaseInfoSize(pointers.items);
2491 var buffer = try self.allocator.alloc(u8, @intCast(usize, size));
2492 defer self.allocator.free(buffer);
2493
2494 var stream = std.io.fixedBufferStream(buffer);
2495 try writeRebaseInfo(pointers.items, stream.writer());
2496
2497 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2498 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2499 dyld_info.rebase_off = @intCast(u32, seg.inner.fileoff);
2500 dyld_info.rebase_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @sizeOf(u64)));
2501 seg.inner.filesize += dyld_info.rebase_size;
2502
2503 log.debug("writing rebase info from 0x{x} to 0x{x}", .{ dyld_info.rebase_off, dyld_info.rebase_off + dyld_info.rebase_size });
2504
2505 try self.file.?.pwriteAll(buffer, dyld_info.rebase_off);
2506}
2507
2508fn writeBindInfoTable(self: *Zld) !void {
2509 var pointers = std.ArrayList(Pointer).init(self.allocator);
2510 defer pointers.deinit();
2511
2512 if (self.got_section_index) |idx| {
2513 try pointers.ensureCapacity(pointers.items.len + self.nonlazy_imports.items().len);
2514 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2515 const sect = seg.sections.items[idx];
2516 const base_offset = sect.addr - seg.inner.vmaddr;
2517 const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
2518 for (self.nonlazy_imports.items()) |entry| {
2519 pointers.appendAssumeCapacity(.{
2520 .offset = base_offset + entry.value.index * @sizeOf(u64),
2521 .segment_id = segment_id,
2522 .dylib_ordinal = entry.value.dylib_ordinal,
2523 .name = entry.key,
2524 });
2525 }
2526 }
2527
2528 if (self.tlv_section_index) |idx| {
2529 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2530 const sect = seg.sections.items[idx];
2531 const base_offset = sect.addr - seg.inner.vmaddr;
2532 const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
2533 try pointers.append(.{
2534 .offset = base_offset + self.tlv_bootstrap.?.index * @sizeOf(u64),
2535 .segment_id = segment_id,
2536 .dylib_ordinal = self.tlv_bootstrap.?.dylib_ordinal,
2537 .name = "__tlv_bootstrap",
2538 });
2539 }
2540
2541 const size = try bindInfoSize(pointers.items);
2542 var buffer = try self.allocator.alloc(u8, @intCast(usize, size));
2543 defer self.allocator.free(buffer);
2544
2545 var stream = std.io.fixedBufferStream(buffer);
2546 try writeBindInfo(pointers.items, stream.writer());
2547
2548 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2549 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2550 dyld_info.bind_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
2551 dyld_info.bind_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)));
2552 seg.inner.filesize += dyld_info.bind_size;
2553
2554 log.debug("writing binding info from 0x{x} to 0x{x}", .{ dyld_info.bind_off, dyld_info.bind_off + dyld_info.bind_size });
2555
2556 try self.file.?.pwriteAll(buffer, dyld_info.bind_off);
2557}
2558
2559fn writeLazyBindInfoTable(self: *Zld) !void {
2560 var pointers = std.ArrayList(Pointer).init(self.allocator);
2561 defer pointers.deinit();
2562 try pointers.ensureCapacity(self.lazy_imports.items().len);
2563
2564 if (self.la_symbol_ptr_section_index) |idx| {
2565 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2566 const sect = seg.sections.items[idx];
2567 const base_offset = sect.addr - seg.inner.vmaddr;
2568 const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
2569 for (self.lazy_imports.items()) |entry| {
2570 pointers.appendAssumeCapacity(.{
2571 .offset = base_offset + entry.value.index * @sizeOf(u64),
2572 .segment_id = segment_id,
2573 .dylib_ordinal = entry.value.dylib_ordinal,
2574 .name = entry.key,
2575 });
2576 }
2577 }
2578
2579 const size = try lazyBindInfoSize(pointers.items);
2580 var buffer = try self.allocator.alloc(u8, @intCast(usize, size));
2581 defer self.allocator.free(buffer);
2582
2583 var stream = std.io.fixedBufferStream(buffer);
2584 try writeLazyBindInfo(pointers.items, stream.writer());
2585
2586 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2587 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2588 dyld_info.lazy_bind_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
2589 dyld_info.lazy_bind_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)));
2590 seg.inner.filesize += dyld_info.lazy_bind_size;
2591
2592 log.debug("writing lazy binding info from 0x{x} to 0x{x}", .{ dyld_info.lazy_bind_off, dyld_info.lazy_bind_off + dyld_info.lazy_bind_size });
2593
2594 try self.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off);
2595 try self.populateLazyBindOffsetsInStubHelper(buffer);
2596}
2597
2598fn populateLazyBindOffsetsInStubHelper(self: *Zld, buffer: []const u8) !void {
2599 var stream = std.io.fixedBufferStream(buffer);
2600 var reader = stream.reader();
2601 var offsets = std.ArrayList(u32).init(self.allocator);
2602 try offsets.append(0);
2603 defer offsets.deinit();
2604 var valid_block = false;
2605
2606 while (true) {
2607 const inst = reader.readByte() catch |err| switch (err) {
2608 error.EndOfStream => break,
2609 else => return err,
2610 };
2611 const imm: u8 = inst & macho.BIND_IMMEDIATE_MASK;
2612 const opcode: u8 = inst & macho.BIND_OPCODE_MASK;
2613
2614 switch (opcode) {
2615 macho.BIND_OPCODE_DO_BIND => {
2616 valid_block = true;
2617 },
2618 macho.BIND_OPCODE_DONE => {
2619 if (valid_block) {
2620 const offset = try stream.getPos();
2621 try offsets.append(@intCast(u32, offset));
2622 }
2623 valid_block = false;
2624 },
2625 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
2626 var next = try reader.readByte();
2627 while (next != @as(u8, 0)) {
2628 next = try reader.readByte();
2629 }
2630 },
2631 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
2632 _ = try leb.readULEB128(u64, reader);
2633 },
2634 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
2635 _ = try leb.readULEB128(u64, reader);
2636 },
2637 macho.BIND_OPCODE_SET_ADDEND_SLEB => {
2638 _ = try leb.readILEB128(i64, reader);
2639 },
2640 else => {},
2641 }
2642 }
2643 assert(self.lazy_imports.items().len <= offsets.items.len);
2644
2645 const stub_size: u4 = switch (self.arch.?) {
2646 .x86_64 => 10,
2647 .aarch64 => 3 * @sizeOf(u32),
2648 else => unreachable,
2649 };
2650 const off: u4 = switch (self.arch.?) {
2651 .x86_64 => 1,
2652 .aarch64 => 2 * @sizeOf(u32),
2653 else => unreachable,
2654 };
2655 var buf: [@sizeOf(u32)]u8 = undefined;
2656 for (self.lazy_imports.items()) |entry| {
2657 const symbol = entry.value;
2658 const placeholder_off = self.stub_helper_stubs_start_off.? + symbol.index * stub_size + off;
2659 mem.writeIntLittle(u32, &buf, offsets.items[symbol.index]);
2660 try self.file.?.pwriteAll(&buf, placeholder_off);
2661 }
2662}
2663
2664fn writeExportInfo(self: *Zld) !void {
2665 var trie = Trie.init(self.allocator);
2666 defer trie.deinit();
2667
2668 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2669 for (self.exports.items()) |entry| {
2670 const name = entry.key;
2671 const symbol = entry.value;
2672 // TODO figure out if we should put all exports into the export trie
2673 assert(symbol.n_value >= text_segment.inner.vmaddr);
2674 try trie.put(.{
2675 .name = name,
2676 .vmaddr_offset = symbol.n_value - text_segment.inner.vmaddr,
2677 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
2678 });
2679 }
2680
2681 try trie.finalize();
2682 var buffer = try self.allocator.alloc(u8, @intCast(usize, trie.size));
2683 defer self.allocator.free(buffer);
2684 var stream = std.io.fixedBufferStream(buffer);
2685 const nwritten = try trie.write(stream.writer());
2686 assert(nwritten == trie.size);
2687
2688 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2689 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2690 dyld_info.export_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
2691 dyld_info.export_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)));
2692 seg.inner.filesize += dyld_info.export_size;
2693
2694 log.debug("writing export info from 0x{x} to 0x{x}", .{ dyld_info.export_off, dyld_info.export_off + dyld_info.export_size });
2695
2696 try self.file.?.pwriteAll(buffer, dyld_info.export_off);
2697}
2698
2699fn writeDebugInfo(self: *Zld) !void {
2700 var stabs = std.ArrayList(macho.nlist_64).init(self.allocator);
2701 defer stabs.deinit();
2702
2703 for (self.objects.items) |object, object_id| {
2704 var debug_info = blk: {
2705 var di = try DebugInfo.parseFromObject(self.allocator, object);
2706 break :blk di orelse continue;
2707 };
2708 defer debug_info.deinit(self.allocator);
2709
2710 const compile_unit = try debug_info.inner.findCompileUnit(0x0); // We assume there is only one CU.
2711 const name = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_name);
2712 const comp_dir = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_comp_dir);
2713
2714 {
2715 const tu_path = try std.fs.path.join(self.allocator, &[_][]const u8{ comp_dir, name });
2716 defer self.allocator.free(tu_path);
2717 const dirname = std.fs.path.dirname(tu_path) orelse "./";
2718 // Current dir
2719 try stabs.append(.{
2720 .n_strx = try self.makeString(tu_path[0 .. dirname.len + 1]),
2721 .n_type = macho.N_SO,
2722 .n_sect = 0,
2723 .n_desc = 0,
2724 .n_value = 0,
2725 });
2726 // Artifact name
2727 try stabs.append(.{
2728 .n_strx = try self.makeString(tu_path[dirname.len + 1 ..]),
2729 .n_type = macho.N_SO,
2730 .n_sect = 0,
2731 .n_desc = 0,
2732 .n_value = 0,
2733 });
2734 // Path to object file with debug info
2735 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
2736 const full_path = blk: {
2737 if (object.ar_name) |prefix| {
2738 const path = try std.os.realpath(prefix, &buffer);
2739 break :blk try std.fmt.allocPrint(self.allocator, "{s}({s})", .{ path, object.name });
2740 } else {
2741 const path = try std.os.realpath(object.name, &buffer);
2742 break :blk try mem.dupe(self.allocator, u8, path);
2743 }
2744 };
2745 defer self.allocator.free(full_path);
2746 const stat = try object.file.stat();
2747 const mtime = @intCast(u64, @divFloor(stat.mtime, 1_000_000_000));
2748 try stabs.append(.{
2749 .n_strx = try self.makeString(full_path),
2750 .n_type = macho.N_OSO,
2751 .n_sect = 0,
2752 .n_desc = 1,
2753 .n_value = mtime,
2754 });
2755 }
2756 log.debug("analyzing debug info in '{s}'", .{object.name});
2757
2758 for (object.symtab.items) |source_sym| {
2759 const symname = object.getString(source_sym.n_strx);
2760 const source_addr = source_sym.n_value;
2761 const target_syms = self.locals.get(symname) orelse continue;
2762 const target_sym: Symbol = blk: {
2763 for (target_syms.items) |ts| {
2764 if (ts.object_id == @intCast(u16, object_id)) break :blk ts;
2765 } else continue;
2766 };
2767
2768 const maybe_size = blk: for (debug_info.inner.func_list.items) |func| {
2769 if (func.pc_range) |range| {
2770 if (source_addr >= range.start and source_addr < range.end) {
2771 break :blk range.end - range.start;
2772 }
2773 }
2774 } else null;
2775
2776 if (maybe_size) |size| {
2777 try stabs.append(.{
2778 .n_strx = 0,
2779 .n_type = macho.N_BNSYM,
2780 .n_sect = target_sym.inner.n_sect,
2781 .n_desc = 0,
2782 .n_value = target_sym.inner.n_value,
2783 });
2784 try stabs.append(.{
2785 .n_strx = target_sym.inner.n_strx,
2786 .n_type = macho.N_FUN,
2787 .n_sect = target_sym.inner.n_sect,
2788 .n_desc = 0,
2789 .n_value = target_sym.inner.n_value,
2790 });
2791 try stabs.append(.{
2792 .n_strx = 0,
2793 .n_type = macho.N_FUN,
2794 .n_sect = 0,
2795 .n_desc = 0,
2796 .n_value = size,
2797 });
2798 try stabs.append(.{
2799 .n_strx = 0,
2800 .n_type = macho.N_ENSYM,
2801 .n_sect = target_sym.inner.n_sect,
2802 .n_desc = 0,
2803 .n_value = size,
2804 });
2805 } else {
2806 // TODO need a way to differentiate symbols: global, static, local, etc.
2807 try stabs.append(.{
2808 .n_strx = target_sym.inner.n_strx,
2809 .n_type = macho.N_STSYM,
2810 .n_sect = target_sym.inner.n_sect,
2811 .n_desc = 0,
2812 .n_value = target_sym.inner.n_value,
2813 });
2814 }
2815 }
2816
2817 // Close the source file!
2818 try stabs.append(.{
2819 .n_strx = 0,
2820 .n_type = macho.N_SO,
2821 .n_sect = 0,
2822 .n_desc = 0,
2823 .n_value = 0,
2824 });
2825 }
2826
2827 if (stabs.items.len == 0) return;
2828
2829 // Write stabs into the symbol table
2830 const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2831 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2832
2833 symtab.nsyms = @intCast(u32, stabs.items.len);
2834
2835 const stabs_off = symtab.symoff;
2836 const stabs_size = symtab.nsyms * @sizeOf(macho.nlist_64);
2837 log.debug("writing symbol stabs from 0x{x} to 0x{x}", .{ stabs_off, stabs_size + stabs_off });
2838 try self.file.?.pwriteAll(mem.sliceAsBytes(stabs.items), stabs_off);
2839
2840 linkedit.inner.filesize += stabs_size;
2841
2842 // Update dynamic symbol table.
2843 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
2844 dysymtab.nlocalsym = symtab.nsyms;
2845}
2846
2847fn writeSymbolTable(self: *Zld) !void {
2848 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2849 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2850
2851 var locals = std.ArrayList(macho.nlist_64).init(self.allocator);
2852 defer locals.deinit();
2853
2854 for (self.locals.items()) |entries| {
2855 log.debug("'{s}': {} entries", .{ entries.key, entries.value.items.len });
2856 // var symbol: ?macho.nlist_64 = null;
2857 for (entries.value.items) |entry| {
2858 log.debug(" | {}", .{entry.inner});
2859 log.debug(" | {}", .{entry.tt});
2860 log.debug(" | {s}", .{self.objects.items[entry.object_id].name});
2861 try locals.append(entry.inner);
2862 }
2863 }
2864 const nlocals = locals.items.len;
2865
2866 const nexports = self.exports.items().len;
2867 var exports = std.ArrayList(macho.nlist_64).init(self.allocator);
2868 defer exports.deinit();
2869
2870 try exports.ensureCapacity(nexports);
2871 for (self.exports.items()) |entry| {
2872 exports.appendAssumeCapacity(entry.value);
2873 }
2874
2875 const has_tlv: bool = self.tlv_bootstrap != null;
2876
2877 var nundefs = self.lazy_imports.items().len + self.nonlazy_imports.items().len;
2878 if (has_tlv) nundefs += 1;
2879
2880 var undefs = std.ArrayList(macho.nlist_64).init(self.allocator);
2881 defer undefs.deinit();
2882
2883 try undefs.ensureCapacity(nundefs);
2884 for (self.lazy_imports.items()) |entry| {
2885 undefs.appendAssumeCapacity(entry.value.symbol);
2886 }
2887 for (self.nonlazy_imports.items()) |entry| {
2888 undefs.appendAssumeCapacity(entry.value.symbol);
2889 }
2890 if (has_tlv) {
2891 undefs.appendAssumeCapacity(self.tlv_bootstrap.?.symbol);
2892 }
2893
2894 const locals_off = symtab.symoff + symtab.nsyms * @sizeOf(macho.nlist_64);
2895 const locals_size = nlocals * @sizeOf(macho.nlist_64);
2896 log.debug("writing local symbols from 0x{x} to 0x{x}", .{ locals_off, locals_size + locals_off });
2897 try self.file.?.pwriteAll(mem.sliceAsBytes(locals.items), locals_off);
2898
2899 const exports_off = locals_off + locals_size;
2900 const exports_size = nexports * @sizeOf(macho.nlist_64);
2901 log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off });
2902 try self.file.?.pwriteAll(mem.sliceAsBytes(exports.items), exports_off);
2903
2904 const undefs_off = exports_off + exports_size;
2905 const undefs_size = nundefs * @sizeOf(macho.nlist_64);
2906 log.debug("writing undefined symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off });
2907 try self.file.?.pwriteAll(mem.sliceAsBytes(undefs.items), undefs_off);
2908
2909 symtab.nsyms += @intCast(u32, nlocals + nexports + nundefs);
2910 seg.inner.filesize += locals_size + exports_size + undefs_size;
2911
2912 // Update dynamic symbol table.
2913 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
2914 dysymtab.nlocalsym += @intCast(u32, nlocals);
2915 dysymtab.iextdefsym = dysymtab.nlocalsym;
2916 dysymtab.nextdefsym = @intCast(u32, nexports);
2917 dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym;
2918 dysymtab.nundefsym = @intCast(u32, nundefs);
2919}
2920
2921fn writeDynamicSymbolTable(self: *Zld) !void {
2922 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2923 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2924 const stubs = &text_segment.sections.items[self.stubs_section_index.?];
2925 const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2926 const got = &data_const_segment.sections.items[self.got_section_index.?];
2927 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2928 const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
2929 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
2930
2931 const lazy = self.lazy_imports.items();
2932 const nonlazy = self.nonlazy_imports.items();
2933 const got_locals = self.nonlazy_pointers.items();
2934 dysymtab.indirectsymoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
2935 dysymtab.nindirectsyms = @intCast(u32, lazy.len * 2 + nonlazy.len + got_locals.len);
2936 const needed_size = dysymtab.nindirectsyms * @sizeOf(u32);
2937 seg.inner.filesize += needed_size;
2938
2939 log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{
2940 dysymtab.indirectsymoff,
2941 dysymtab.indirectsymoff + needed_size,
2942 });
2943
2944 var buf = try self.allocator.alloc(u8, needed_size);
2945 defer self.allocator.free(buf);
2946 var stream = std.io.fixedBufferStream(buf);
2947 var writer = stream.writer();
2948
2949 stubs.reserved1 = 0;
2950 for (lazy) |_, i| {
2951 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
2952 try writer.writeIntLittle(u32, symtab_idx);
2953 }
2954
2955 const base_id = @intCast(u32, lazy.len);
2956 got.reserved1 = base_id;
2957 for (nonlazy) |_, i| {
2958 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i + base_id);
2959 try writer.writeIntLittle(u32, symtab_idx);
2960 }
2961 // TODO there should be one common set of GOT entries.
2962 for (got_locals) |_| {
2963 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
2964 }
2965
2966 la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, nonlazy.len) + @intCast(u32, got_locals.len);
2967 for (lazy) |_, i| {
2968 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
2969 try writer.writeIntLittle(u32, symtab_idx);
2970 }
2971
2972 try self.file.?.pwriteAll(buf, dysymtab.indirectsymoff);
2973}
2974
2975fn writeStringTable(self: *Zld) !void {
2976 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2977 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2978 symtab.stroff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
2979 symtab.strsize = @intCast(u32, mem.alignForwardGeneric(u64, self.strtab.items.len, @alignOf(u64)));
2980 seg.inner.filesize += symtab.strsize;
2981
2982 log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });
2983
2984 try self.file.?.pwriteAll(self.strtab.items, symtab.stroff);
2985
2986 if (symtab.strsize > self.strtab.items.len and self.arch.? == .x86_64) {
2987 // This is the last section, so we need to pad it out.
2988 try self.file.?.pwriteAll(&[_]u8{0}, seg.inner.fileoff + seg.inner.filesize - 1);
2989 }
2990}
2991
2992fn writeDataInCode(self: *Zld) !void {
2993 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2994 const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].LinkeditData;
2995 const fileoff = seg.inner.fileoff + seg.inner.filesize;
2996
2997 var buf = std.ArrayList(u8).init(self.allocator);
2998 defer buf.deinit();
2999
3000 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
3001 const text_sect = text_seg.sections.items[self.text_section_index.?];
3002 for (self.objects.items) |object, object_id| {
3003 const source_seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
3004 const source_sect = source_seg.sections.items[object.text_section_index.?];
3005 const target_mapping = self.mappings.get(.{
3006 .object_id = @intCast(u16, object_id),
3007 .source_sect_id = object.text_section_index.?,
3008 }) orelse continue;
3009
3010 try buf.ensureCapacity(
3011 buf.items.len + object.data_in_code_entries.items.len * @sizeOf(macho.data_in_code_entry),
3012 );
3013 for (object.data_in_code_entries.items) |dice| {
3014 const new_dice: macho.data_in_code_entry = .{
3015 .offset = text_sect.offset + target_mapping.offset + dice.offset,
3016 .length = dice.length,
3017 .kind = dice.kind,
3018 };
3019 buf.appendSliceAssumeCapacity(mem.asBytes(&new_dice));
3020 }
3021 }
3022 const datasize = @intCast(u32, buf.items.len);
3023
3024 dice_cmd.dataoff = @intCast(u32, fileoff);
3025 dice_cmd.datasize = datasize;
3026 seg.inner.filesize += datasize;
3027
3028 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ fileoff, fileoff + datasize });
3029
3030 try self.file.?.pwriteAll(buf.items, fileoff);
3031}
3032
3033fn writeCodeSignaturePadding(self: *Zld) !void {
3034 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
3035 const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData;
3036 const fileoff = seg.inner.fileoff + seg.inner.filesize;
3037 const needed_size = CodeSignature.calcCodeSignaturePaddingSize(
3038 self.out_path.?,
3039 fileoff,
3040 self.page_size.?,
3041 );
3042 code_sig_cmd.dataoff = @intCast(u32, fileoff);
3043 code_sig_cmd.datasize = needed_size;
3044
3045 // Advance size of __LINKEDIT segment
3046 seg.inner.filesize += needed_size;
3047 seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size.?);
3048
3049 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ fileoff, fileoff + needed_size });
3050
3051 // Pad out the space. We need to do this to calculate valid hashes for everything in the file
3052 // except for code signature data.
3053 try self.file.?.pwriteAll(&[_]u8{0}, fileoff + needed_size - 1);
3054}
3055
3056fn writeCodeSignature(self: *Zld) !void {
3057 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
3058 const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData;
3059
3060 var code_sig = CodeSignature.init(self.allocator, self.page_size.?);
3061 defer code_sig.deinit();
3062 try code_sig.calcAdhocSignature(
3063 self.file.?,
3064 self.out_path.?,
3065 text_seg.inner,
3066 code_sig_cmd,
3067 .Exe,
3068 );
3069
3070 var buffer = try self.allocator.alloc(u8, code_sig.size());
3071 defer self.allocator.free(buffer);
3072 var stream = std.io.fixedBufferStream(buffer);
3073 try code_sig.write(stream.writer());
3074
3075 log.debug("writing code signature from 0x{x} to 0x{x}", .{ code_sig_cmd.dataoff, code_sig_cmd.dataoff + buffer.len });
3076
3077 try self.file.?.pwriteAll(buffer, code_sig_cmd.dataoff);
3078}
3079
3080fn writeLoadCommands(self: *Zld) !void {
3081 var sizeofcmds: u32 = 0;
3082 for (self.load_commands.items) |lc| {
3083 sizeofcmds += lc.cmdsize();
3084 }
3085
3086 var buffer = try self.allocator.alloc(u8, sizeofcmds);
3087 defer self.allocator.free(buffer);
3088 var writer = std.io.fixedBufferStream(buffer).writer();
3089 for (self.load_commands.items) |lc| {
3090 try lc.write(writer);
3091 }
3092
3093 const off = @sizeOf(macho.mach_header_64);
3094 log.debug("writing {} load commands from 0x{x} to 0x{x}", .{ self.load_commands.items.len, off, off + sizeofcmds });
3095 try self.file.?.pwriteAll(buffer, off);
3096}
3097
3098fn writeHeader(self: *Zld) !void {
3099 var header: macho.mach_header_64 = undefined;
3100 header.magic = macho.MH_MAGIC_64;
3101
3102 const CpuInfo = struct {
3103 cpu_type: macho.cpu_type_t,
3104 cpu_subtype: macho.cpu_subtype_t,
3105 };
3106
3107 const cpu_info: CpuInfo = switch (self.arch.?) {
3108 .aarch64 => .{
3109 .cpu_type = macho.CPU_TYPE_ARM64,
3110 .cpu_subtype = macho.CPU_SUBTYPE_ARM_ALL,
3111 },
3112 .x86_64 => .{
3113 .cpu_type = macho.CPU_TYPE_X86_64,
3114 .cpu_subtype = macho.CPU_SUBTYPE_X86_64_ALL,
3115 },
3116 else => return error.UnsupportedCpuArchitecture,
3117 };
3118 header.cputype = cpu_info.cpu_type;
3119 header.cpusubtype = cpu_info.cpu_subtype;
3120 header.filetype = macho.MH_EXECUTE;
3121 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;
3122 header.reserved = 0;
3123
3124 if (self.tlv_section_index) |_|
3125 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
3126
3127 header.ncmds = @intCast(u32, self.load_commands.items.len);
3128 header.sizeofcmds = 0;
3129 for (self.load_commands.items) |cmd| {
3130 header.sizeofcmds += cmd.cmdsize();
3131 }
3132 log.debug("writing Mach-O header {}", .{header});
3133 try self.file.?.pwriteAll(mem.asBytes(&header), 0);
3134}
3135
3136pub fn makeStaticString(bytes: []const u8) [16]u8 {
3137 var buf = [_]u8{0} ** 16;
3138 assert(bytes.len <= buf.len);
3139 mem.copy(u8, &buf, bytes);
3140 return buf;
3141}
3142
3143fn makeString(self: *Zld, bytes: []const u8) !u32 {
3144 try self.strtab.ensureCapacity(self.allocator, self.strtab.items.len + bytes.len + 1);
3145 const offset = @intCast(u32, self.strtab.items.len);
3146 log.debug("writing new string '{s}' into string table at offset 0x{x}", .{ bytes, offset });
3147 self.strtab.appendSliceAssumeCapacity(bytes);
3148 self.strtab.appendAssumeCapacity(0);
3149 return offset;
3150}
3151
3152fn getString(self: *const Zld, str_off: u32) []const u8 {
3153 assert(str_off < self.strtab.items.len);
3154 return mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr + str_off));
3155}
3156
3157pub fn parseName(name: *const [16]u8) []const u8 {
3158 const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len;
3159 return name[0..len];
3160}
3161
3162fn isLocal(sym: *const macho.nlist_64) callconv(.Inline) bool {
3163 if (isExtern(sym)) return false;
3164 const tt = macho.N_TYPE & sym.n_type;
3165 return tt == macho.N_SECT;
3166}
3167
3168fn isExport(sym: *const macho.nlist_64) callconv(.Inline) bool {
3169 if (!isExtern(sym)) return false;
3170 const tt = macho.N_TYPE & sym.n_type;
3171 return tt == macho.N_SECT;
3172}
3173
3174fn isImport(sym: *const macho.nlist_64) callconv(.Inline) bool {
3175 if (!isExtern(sym)) return false;
3176 const tt = macho.N_TYPE & sym.n_type;
3177 return tt == macho.N_UNDF;
3178}
3179
3180fn isExtern(sym: *const macho.nlist_64) callconv(.Inline) bool {
3181 if ((sym.n_type & macho.N_EXT) == 0) return false;
3182 return (sym.n_type & macho.N_PEXT) == 0;
3183}
3184
3185fn isWeakDef(sym: *const macho.nlist_64) callconv(.Inline) bool {
3186 return (sym.n_desc & macho.N_WEAK_DEF) != 0;
3187}
3188
3189fn aarch64IsArithmetic(inst: *const [4]u8) callconv(.Inline) bool {
3190 const group_decode = @truncate(u5, inst[3]);
3191 return ((group_decode >> 2) == 4);
3192}
src/link/MachO/bind.zig created+145
...@@ -0,0 +1,145 @@
1const std = @import("std");
2const leb = std.leb;
3const macho = std.macho;
4
5pub const Pointer = struct {
6 offset: u64,
7 segment_id: u16,
8 dylib_ordinal: ?i64 = null,
9 name: ?[]const u8 = null,
10};
11
12pub fn pointerCmp(context: void, a: Pointer, b: Pointer) bool {
13 if (a.segment_id < b.segment_id) return true;
14 if (a.segment_id == b.segment_id) {
15 return a.offset < b.offset;
16 }
17 return false;
18}
19
20pub fn rebaseInfoSize(pointers: []const Pointer) !u64 {
21 var stream = std.io.countingWriter(std.io.null_writer);
22 var writer = stream.writer();
23 var size: u64 = 0;
24
25 for (pointers) |pointer| {
26 size += 2;
27 try leb.writeILEB128(writer, pointer.offset);
28 size += 1;
29 }
30
31 size += 1 + stream.bytes_written;
32 return size;
33}
34
35pub fn writeRebaseInfo(pointers: []const Pointer, writer: anytype) !void {
36 for (pointers) |pointer| {
37 try writer.writeByte(macho.REBASE_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.REBASE_TYPE_POINTER));
38 try writer.writeByte(macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, pointer.segment_id));
39
40 try leb.writeILEB128(writer, pointer.offset);
41 try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | @truncate(u4, 1));
42 }
43 try writer.writeByte(macho.REBASE_OPCODE_DONE);
44}
45
46pub fn bindInfoSize(pointers: []const Pointer) !u64 {
47 var stream = std.io.countingWriter(std.io.null_writer);
48 var writer = stream.writer();
49 var size: u64 = 0;
50
51 for (pointers) |pointer| {
52 size += 1;
53 if (pointer.dylib_ordinal.? > 15) {
54 try leb.writeULEB128(writer, @bitCast(u64, pointer.dylib_ordinal.?));
55 }
56 size += 1;
57
58 size += 1;
59 size += pointer.name.?.len;
60 size += 1;
61
62 size += 1;
63
64 try leb.writeILEB128(writer, pointer.offset);
65 size += 1;
66 }
67
68 size += stream.bytes_written + 1;
69 return size;
70}
71
72pub fn writeBindInfo(pointers: []const Pointer, writer: anytype) !void {
73 for (pointers) |pointer| {
74 if (pointer.dylib_ordinal.? > 15) {
75 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
76 try leb.writeULEB128(writer, @bitCast(u64, pointer.dylib_ordinal.?));
77 } else if (pointer.dylib_ordinal.? > 0) {
78 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, pointer.dylib_ordinal.?)));
79 } else {
80 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, pointer.dylib_ordinal.?)));
81 }
82 try writer.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.BIND_TYPE_POINTER));
83
84 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
85 try writer.writeAll(pointer.name.?);
86 try writer.writeByte(0);
87
88 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, pointer.segment_id));
89
90 try leb.writeILEB128(writer, pointer.offset);
91 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
92 }
93
94 try writer.writeByte(macho.BIND_OPCODE_DONE);
95}
96
97pub fn lazyBindInfoSize(pointers: []const Pointer) !u64 {
98 var stream = std.io.countingWriter(std.io.null_writer);
99 var writer = stream.writer();
100 var size: u64 = 0;
101
102 for (pointers) |pointer| {
103 size += 1;
104
105 try leb.writeILEB128(writer, pointer.offset);
106
107 size += 1;
108 if (pointer.dylib_ordinal.? > 15) {
109 try leb.writeULEB128(writer, @bitCast(u64, pointer.dylib_ordinal.?));
110 }
111
112 size += 1;
113 size += pointer.name.?.len;
114 size += 1;
115
116 size += 2;
117 }
118
119 size += stream.bytes_written;
120 return size;
121}
122
123pub fn writeLazyBindInfo(pointers: []const Pointer, writer: anytype) !void {
124 for (pointers) |pointer| {
125 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, pointer.segment_id));
126
127 try leb.writeILEB128(writer, pointer.offset);
128
129 if (pointer.dylib_ordinal.? > 15) {
130 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
131 try leb.writeULEB128(writer, @bitCast(u64, pointer.dylib_ordinal.?));
132 } else if (pointer.dylib_ordinal.? > 0) {
133 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, pointer.dylib_ordinal.?)));
134 } else {
135 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, pointer.dylib_ordinal.?)));
136 }
137
138 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
139 try writer.writeAll(pointer.name.?);
140 try writer.writeByte(0);
141
142 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
143 try writer.writeByte(macho.BIND_OPCODE_DONE);
144 }
145}
src/link/MachO/imports.zig deleted-152
...@@ -1,152 +0,0 @@
1const std = @import("std");
2const leb = std.leb;
3const macho = std.macho;
4const mem = std.mem;
5
6const assert = std.debug.assert;
7const Allocator = mem.Allocator;
8
9pub const ExternSymbol = struct {
10 /// MachO symbol table entry.
11 inner: macho.nlist_64,
12
13 /// Id of the dynamic library where the specified entries can be found.
14 /// Id of 0 means self.
15 /// TODO this should really be an id into the table of all defined
16 /// dylibs.
17 dylib_ordinal: i64 = 0,
18
19 /// Id of the segment where this symbol is defined (will have its address
20 /// resolved).
21 segment: u16 = 0,
22
23 /// Offset relative to the start address of the `segment`.
24 offset: u32 = 0,
25};
26
27pub fn rebaseInfoSize(symbols: anytype) !u64 {
28 var stream = std.io.countingWriter(std.io.null_writer);
29 var writer = stream.writer();
30 var size: u64 = 0;
31
32 for (symbols) |entry| {
33 size += 2;
34 try leb.writeILEB128(writer, entry.value.offset);
35 size += 1;
36 }
37
38 size += 1 + stream.bytes_written;
39 return size;
40}
41
42pub fn writeRebaseInfo(symbols: anytype, writer: anytype) !void {
43 for (symbols) |entry| {
44 const symbol = entry.value;
45 try writer.writeByte(macho.REBASE_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.REBASE_TYPE_POINTER));
46 try writer.writeByte(macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
47 try leb.writeILEB128(writer, symbol.offset);
48 try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | @truncate(u4, 1));
49 }
50 try writer.writeByte(macho.REBASE_OPCODE_DONE);
51}
52
53pub fn bindInfoSize(symbols: anytype) !u64 {
54 var stream = std.io.countingWriter(std.io.null_writer);
55 var writer = stream.writer();
56 var size: u64 = 0;
57
58 for (symbols) |entry| {
59 const symbol = entry.value;
60
61 size += 1;
62 if (symbol.dylib_ordinal > 15) {
63 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
64 }
65 size += 1;
66
67 size += 1;
68 size += entry.key.len;
69 size += 1;
70
71 size += 1;
72 try leb.writeILEB128(writer, symbol.offset);
73 size += 2;
74 }
75
76 size += stream.bytes_written;
77 return size;
78}
79
80pub fn writeBindInfo(symbols: anytype, writer: anytype) !void {
81 for (symbols) |entry| {
82 const symbol = entry.value;
83
84 if (symbol.dylib_ordinal > 15) {
85 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
86 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
87 } else if (symbol.dylib_ordinal > 0) {
88 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
89 } else {
90 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
91 }
92 try writer.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.BIND_TYPE_POINTER));
93
94 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
95 try writer.writeAll(entry.key);
96 try writer.writeByte(0);
97
98 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
99 try leb.writeILEB128(writer, symbol.offset);
100 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
101 try writer.writeByte(macho.BIND_OPCODE_DONE);
102 }
103}
104
105pub fn lazyBindInfoSize(symbols: anytype) !u64 {
106 var stream = std.io.countingWriter(std.io.null_writer);
107 var writer = stream.writer();
108 var size: u64 = 0;
109
110 for (symbols) |entry| {
111 const symbol = entry.value;
112 size += 1;
113 try leb.writeILEB128(writer, symbol.offset);
114 size += 1;
115 if (symbol.dylib_ordinal > 15) {
116 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
117 }
118
119 size += 1;
120 size += entry.key.len;
121 size += 1;
122
123 size += 2;
124 }
125
126 size += stream.bytes_written;
127 return size;
128}
129
130pub fn writeLazyBindInfo(symbols: anytype, writer: anytype) !void {
131 for (symbols) |entry| {
132 const symbol = entry.value;
133 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
134 try leb.writeILEB128(writer, symbol.offset);
135
136 if (symbol.dylib_ordinal > 15) {
137 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
138 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
139 } else if (symbol.dylib_ordinal > 0) {
140 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
141 } else {
142 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
143 }
144
145 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
146 try writer.writeAll(entry.key);
147 try writer.writeByte(0);
148
149 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
150 try writer.writeByte(macho.BIND_OPCODE_DONE);
151 }
152}
src/main.zig+56-15
...@@ -2637,6 +2637,50 @@ fn argvCmd(allocator: *Allocator, argv: []const []const u8) ![]u8 {...@@ -2637,6 +2637,50 @@ fn argvCmd(allocator: *Allocator, argv: []const []const u8) ![]u8 {
2637 return cmd.toOwnedSlice();2637 return cmd.toOwnedSlice();
2638}2638}
26392639
2640fn readSourceFileToEndAlloc(allocator: *mem.Allocator, input: *const fs.File, size_hint: ?usize) ![]const u8 {
2641 const source_code = input.readToEndAllocOptions(
2642 allocator,
2643 max_src_size,
2644 size_hint,
2645 @alignOf(u16),
2646 null,
2647 ) catch |err| switch (err) {
2648 error.ConnectionResetByPeer => unreachable,
2649 error.ConnectionTimedOut => unreachable,
2650 error.NotOpenForReading => unreachable,
2651 else => |e| return e,
2652 };
2653 errdefer allocator.free(source_code);
2654
2655 // Detect unsupported file types with their Byte Order Mark
2656 const unsupported_boms = [_][]const u8{
2657 "\xff\xfe\x00\x00", // UTF-32 little endian
2658 "\xfe\xff\x00\x00", // UTF-32 big endian
2659 "\xfe\xff", // UTF-16 big endian
2660 };
2661 for (unsupported_boms) |bom| {
2662 if (mem.startsWith(u8, source_code, bom)) {
2663 return error.UnsupportedEncoding;
2664 }
2665 }
2666
2667 // If the file starts with a UTF-16 little endian BOM, translate it to UTF-8
2668 if (mem.startsWith(u8, source_code, "\xff\xfe")) {
2669 const source_code_utf16_le = mem.bytesAsSlice(u16, source_code);
2670 const source_code_utf8 = std.unicode.utf16leToUtf8Alloc(allocator, source_code_utf16_le) catch |err| switch (err) {
2671 error.DanglingSurrogateHalf => error.UnsupportedEncoding,
2672 error.ExpectedSecondSurrogateHalf => error.UnsupportedEncoding,
2673 error.UnexpectedSecondSurrogateHalf => error.UnsupportedEncoding,
2674 else => |e| return e,
2675 };
2676
2677 allocator.free(source_code);
2678 return source_code_utf8;
2679 }
2680
2681 return source_code;
2682}
2683
2640pub const usage_fmt =2684pub const usage_fmt =
2641 \\Usage: zig fmt [file]...2685 \\Usage: zig fmt [file]...
2642 \\2686 \\
...@@ -2708,9 +2752,10 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {...@@ -2708,9 +2752,10 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
2708 fatal("cannot use --stdin with positional arguments", .{});2752 fatal("cannot use --stdin with positional arguments", .{});
2709 }2753 }
27102754
2711 const stdin = io.getStdIn().reader();2755 const stdin = io.getStdIn();
27122756 const source_code = readSourceFileToEndAlloc(gpa, &stdin, null) catch |err| {
2713 const source_code = try stdin.readAllAlloc(gpa, max_src_size);2757 fatal("unable to read stdin: {s}", .{err});
2758 };
2714 defer gpa.free(source_code);2759 defer gpa.free(source_code);
27152760
2716 var tree = std.zig.parse(gpa, source_code) catch |err| {2761 var tree = std.zig.parse(gpa, source_code) catch |err| {
...@@ -2785,6 +2830,7 @@ const FmtError = error{...@@ -2785,6 +2830,7 @@ const FmtError = error{
2785 EndOfStream,2830 EndOfStream,
2786 Unseekable,2831 Unseekable,
2787 NotOpenForWriting,2832 NotOpenForWriting,
2833 UnsupportedEncoding,
2788} || fs.File.OpenError;2834} || fs.File.OpenError;
27892835
2790fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void {2836fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void {
...@@ -2850,21 +2896,15 @@ fn fmtPathFile(...@@ -2850,21 +2896,15 @@ fn fmtPathFile(
2850 if (stat.kind == .Directory)2896 if (stat.kind == .Directory)
2851 return error.IsDir;2897 return error.IsDir;
28522898
2853 const source_code = source_file.readToEndAllocOptions(2899 const source_code = try readSourceFileToEndAlloc(
2854 fmt.gpa,2900 fmt.gpa,
2855 max_src_size,2901 &source_file,
2856 std.math.cast(usize, stat.size) catch return error.FileTooBig,2902 std.math.cast(usize, stat.size) catch return error.FileTooBig,
2857 @alignOf(u8),2903 );
2858 null,2904 defer fmt.gpa.free(source_code);
2859 ) catch |err| switch (err) {2905
2860 error.ConnectionResetByPeer => unreachable,
2861 error.ConnectionTimedOut => unreachable,
2862 error.NotOpenForReading => unreachable,
2863 else => |e| return e,
2864 };
2865 source_file.close();2906 source_file.close();
2866 file_closed = true;2907 file_closed = true;
2867 defer fmt.gpa.free(source_code);
28682908
2869 // Add to set after no longer possible to get error.IsDir.2909 // Add to set after no longer possible to get error.IsDir.
2870 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;2910 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
...@@ -3241,7 +3281,8 @@ pub const ClangArgIterator = struct {...@@ -3241,7 +3281,8 @@ pub const ClangArgIterator = struct {
3241 self.zig_equivalent = clang_arg.zig_equivalent;3281 self.zig_equivalent = clang_arg.zig_equivalent;
3242 break :find_clang_arg;3282 break :find_clang_arg;
3243 },3283 },
3244 } else {3284 }
3285 else {
3245 fatal("Unknown Clang option: '{s}'", .{arg});3286 fatal("Unknown Clang option: '{s}'", .{arg});
3246 }3287 }
3247 }3288 }
src/stage1/all_types.hpp+21-4
...@@ -391,6 +391,8 @@ enum LazyValueId {...@@ -391,6 +391,8 @@ enum LazyValueId {
391 LazyValueIdAlignOf,391 LazyValueIdAlignOf,
392 LazyValueIdSizeOf,392 LazyValueIdSizeOf,
393 LazyValueIdPtrType,393 LazyValueIdPtrType,
394 LazyValueIdPtrTypeSimple,
395 LazyValueIdPtrTypeSimpleConst,
394 LazyValueIdOptType,396 LazyValueIdOptType,
395 LazyValueIdSliceType,397 LazyValueIdSliceType,
396 LazyValueIdFnType,398 LazyValueIdFnType,
...@@ -467,6 +469,13 @@ struct LazyValuePtrType {...@@ -467,6 +469,13 @@ struct LazyValuePtrType {
467 bool is_allowzero;469 bool is_allowzero;
468};470};
469471
472struct LazyValuePtrTypeSimple {
473 LazyValue base;
474
475 IrAnalyze *ira;
476 IrInstGen *elem_type;
477};
478
470struct LazyValueOptType {479struct LazyValueOptType {
471 LazyValue base;480 LazyValue base;
472481
...@@ -2610,7 +2619,8 @@ enum IrInstSrcId {...@@ -2610,7 +2619,8 @@ enum IrInstSrcId {
2610 IrInstSrcIdEnumToInt,2619 IrInstSrcIdEnumToInt,
2611 IrInstSrcIdIntToErr,2620 IrInstSrcIdIntToErr,
2612 IrInstSrcIdErrToInt,2621 IrInstSrcIdErrToInt,
2613 IrInstSrcIdCheckSwitchProngs,2622 IrInstSrcIdCheckSwitchProngsUnderYes,
2623 IrInstSrcIdCheckSwitchProngsUnderNo,
2614 IrInstSrcIdCheckStatementIsVoid,2624 IrInstSrcIdCheckStatementIsVoid,
2615 IrInstSrcIdTypeName,2625 IrInstSrcIdTypeName,
2616 IrInstSrcIdDeclRef,2626 IrInstSrcIdDeclRef,
...@@ -2624,12 +2634,15 @@ enum IrInstSrcId {...@@ -2624,12 +2634,15 @@ enum IrInstSrcId {
2624 IrInstSrcIdHasField,2634 IrInstSrcIdHasField,
2625 IrInstSrcIdSetEvalBranchQuota,2635 IrInstSrcIdSetEvalBranchQuota,
2626 IrInstSrcIdPtrType,2636 IrInstSrcIdPtrType,
2637 IrInstSrcIdPtrTypeSimple,
2638 IrInstSrcIdPtrTypeSimpleConst,
2627 IrInstSrcIdAlignCast,2639 IrInstSrcIdAlignCast,
2628 IrInstSrcIdImplicitCast,2640 IrInstSrcIdImplicitCast,
2629 IrInstSrcIdResolveResult,2641 IrInstSrcIdResolveResult,
2630 IrInstSrcIdResetResult,2642 IrInstSrcIdResetResult,
2631 IrInstSrcIdSetAlignStack,2643 IrInstSrcIdSetAlignStack,
2632 IrInstSrcIdArgType,2644 IrInstSrcIdArgTypeAllowVarFalse,
2645 IrInstSrcIdArgTypeAllowVarTrue,
2633 IrInstSrcIdExport,2646 IrInstSrcIdExport,
2634 IrInstSrcIdExtern,2647 IrInstSrcIdExtern,
2635 IrInstSrcIdErrorReturnTrace,2648 IrInstSrcIdErrorReturnTrace,
...@@ -3294,6 +3307,12 @@ struct IrInstSrcArrayType {...@@ -3294,6 +3307,12 @@ struct IrInstSrcArrayType {
3294 IrInstSrc *child_type;3307 IrInstSrc *child_type;
3295};3308};
32963309
3310struct IrInstSrcPtrTypeSimple {
3311 IrInstSrc base;
3312
3313 IrInstSrc *child_type;
3314};
3315
3297struct IrInstSrcPtrType {3316struct IrInstSrcPtrType {
3298 IrInstSrc base;3317 IrInstSrc base;
32993318
...@@ -4020,7 +4039,6 @@ struct IrInstSrcCheckSwitchProngs {...@@ -4020,7 +4039,6 @@ struct IrInstSrcCheckSwitchProngs {
4020 IrInstSrcCheckSwitchProngsRange *ranges;4039 IrInstSrcCheckSwitchProngsRange *ranges;
4021 size_t range_count;4040 size_t range_count;
4022 AstNode* else_prong;4041 AstNode* else_prong;
4023 bool have_underscore_prong;
4024};4042};
40254043
4026struct IrInstSrcCheckStatementIsVoid {4044struct IrInstSrcCheckStatementIsVoid {
...@@ -4144,7 +4162,6 @@ struct IrInstSrcArgType {...@@ -4144,7 +4162,6 @@ struct IrInstSrcArgType {
41444162
4145 IrInstSrc *fn_type;4163 IrInstSrc *fn_type;
4146 IrInstSrc *arg_index;4164 IrInstSrc *arg_index;
4147 bool allow_var;
4148};4165};
41494166
4150struct IrInstSrcExport {4167struct IrInstSrcExport {
src/stage1/analyze.cpp+48-1
...@@ -1237,6 +1237,22 @@ Error type_val_resolve_zero_bits(CodeGen *g, ZigValue *type_val, ZigType *parent...@@ -1237,6 +1237,22 @@ Error type_val_resolve_zero_bits(CodeGen *g, ZigValue *type_val, ZigType *parent
1237 parent_type_val, is_zero_bits);1237 parent_type_val, is_zero_bits);
1238 }1238 }
1239 }1239 }
1240 case LazyValueIdPtrTypeSimple:
1241 case LazyValueIdPtrTypeSimpleConst: {
1242 LazyValuePtrTypeSimple *lazy_ptr_type = reinterpret_cast<LazyValuePtrTypeSimple *>(type_val->data.x_lazy);
1243
1244 if (parent_type_val == lazy_ptr_type->elem_type->value) {
1245 // Does a struct which contains a pointer field to itself have bits? Yes.
1246 *is_zero_bits = false;
1247 return ErrorNone;
1248 } else {
1249 if (parent_type_val == nullptr) {
1250 parent_type_val = type_val;
1251 }
1252 return type_val_resolve_zero_bits(g, lazy_ptr_type->elem_type->value, parent_type,
1253 parent_type_val, is_zero_bits);
1254 }
1255 }
1240 case LazyValueIdArrayType: {1256 case LazyValueIdArrayType: {
1241 LazyValueArrayType *lazy_array_type =1257 LazyValueArrayType *lazy_array_type =
1242 reinterpret_cast<LazyValueArrayType *>(type_val->data.x_lazy);1258 reinterpret_cast<LazyValueArrayType *>(type_val->data.x_lazy);
...@@ -1285,6 +1301,8 @@ Error type_val_resolve_is_opaque_type(CodeGen *g, ZigValue *type_val, bool *is_o...@@ -1285,6 +1301,8 @@ Error type_val_resolve_is_opaque_type(CodeGen *g, ZigValue *type_val, bool *is_o
1285 zig_unreachable();1301 zig_unreachable();
1286 case LazyValueIdSliceType:1302 case LazyValueIdSliceType:
1287 case LazyValueIdPtrType:1303 case LazyValueIdPtrType:
1304 case LazyValueIdPtrTypeSimple:
1305 case LazyValueIdPtrTypeSimpleConst:
1288 case LazyValueIdFnType:1306 case LazyValueIdFnType:
1289 case LazyValueIdOptType:1307 case LazyValueIdOptType:
1290 case LazyValueIdErrUnionType:1308 case LazyValueIdErrUnionType:
...@@ -1313,6 +1331,11 @@ static ReqCompTime type_val_resolve_requires_comptime(CodeGen *g, ZigValue *type...@@ -1313,6 +1331,11 @@ static ReqCompTime type_val_resolve_requires_comptime(CodeGen *g, ZigValue *type
1313 LazyValuePtrType *lazy_ptr_type = reinterpret_cast<LazyValuePtrType *>(type_val->data.x_lazy);1331 LazyValuePtrType *lazy_ptr_type = reinterpret_cast<LazyValuePtrType *>(type_val->data.x_lazy);
1314 return type_val_resolve_requires_comptime(g, lazy_ptr_type->elem_type->value);1332 return type_val_resolve_requires_comptime(g, lazy_ptr_type->elem_type->value);
1315 }1333 }
1334 case LazyValueIdPtrTypeSimple:
1335 case LazyValueIdPtrTypeSimpleConst: {
1336 LazyValuePtrTypeSimple *lazy_ptr_type = reinterpret_cast<LazyValuePtrTypeSimple *>(type_val->data.x_lazy);
1337 return type_val_resolve_requires_comptime(g, lazy_ptr_type->elem_type->value);
1338 }
1316 case LazyValueIdOptType: {1339 case LazyValueIdOptType: {
1317 LazyValueOptType *lazy_opt_type = reinterpret_cast<LazyValueOptType *>(type_val->data.x_lazy);1340 LazyValueOptType *lazy_opt_type = reinterpret_cast<LazyValueOptType *>(type_val->data.x_lazy);
1318 return type_val_resolve_requires_comptime(g, lazy_opt_type->payload_type->value);1341 return type_val_resolve_requires_comptime(g, lazy_opt_type->payload_type->value);
...@@ -1413,6 +1436,24 @@ start_over:...@@ -1413,6 +1436,24 @@ start_over:
1413 }1436 }
1414 return ErrorNone;1437 return ErrorNone;
1415 }1438 }
1439 case LazyValueIdPtrTypeSimple:
1440 case LazyValueIdPtrTypeSimpleConst: {
1441 LazyValuePtrTypeSimple *lazy_ptr_type = reinterpret_cast<LazyValuePtrTypeSimple *>(type_val->data.x_lazy);
1442 bool is_zero_bits;
1443 if ((err = type_val_resolve_zero_bits(g, lazy_ptr_type->elem_type->value, nullptr,
1444 nullptr, &is_zero_bits)))
1445 {
1446 return err;
1447 }
1448 if (is_zero_bits) {
1449 *abi_size = 0;
1450 *size_in_bits = 0;
1451 } else {
1452 *abi_size = g->builtin_types.entry_usize->abi_size;
1453 *size_in_bits = g->builtin_types.entry_usize->size_in_bits;
1454 }
1455 return ErrorNone;
1456 }
1416 case LazyValueIdFnType:1457 case LazyValueIdFnType:
1417 *abi_size = g->builtin_types.entry_usize->abi_size;1458 *abi_size = g->builtin_types.entry_usize->abi_size;
1418 *size_in_bits = g->builtin_types.entry_usize->size_in_bits;1459 *size_in_bits = g->builtin_types.entry_usize->size_in_bits;
...@@ -1449,6 +1490,8 @@ Error type_val_resolve_abi_align(CodeGen *g, AstNode *source_node, ZigValue *typ...@@ -1449,6 +1490,8 @@ Error type_val_resolve_abi_align(CodeGen *g, AstNode *source_node, ZigValue *typ
1449 zig_unreachable();1490 zig_unreachable();
1450 case LazyValueIdSliceType:1491 case LazyValueIdSliceType:
1451 case LazyValueIdPtrType:1492 case LazyValueIdPtrType:
1493 case LazyValueIdPtrTypeSimple:
1494 case LazyValueIdPtrTypeSimpleConst:
1452 case LazyValueIdFnType:1495 case LazyValueIdFnType:
1453 *abi_align = g->builtin_types.entry_usize->abi_align;1496 *abi_align = g->builtin_types.entry_usize->abi_align;
1454 return ErrorNone;1497 return ErrorNone;
...@@ -1506,7 +1549,9 @@ static OnePossibleValue type_val_resolve_has_one_possible_value(CodeGen *g, ZigV...@@ -1506,7 +1549,9 @@ static OnePossibleValue type_val_resolve_has_one_possible_value(CodeGen *g, ZigV
1506 return OnePossibleValueYes;1549 return OnePossibleValueYes;
1507 return type_val_resolve_has_one_possible_value(g, lazy_array_type->elem_type->value);1550 return type_val_resolve_has_one_possible_value(g, lazy_array_type->elem_type->value);
1508 }1551 }
1509 case LazyValueIdPtrType: {1552 case LazyValueIdPtrType:
1553 case LazyValueIdPtrTypeSimple:
1554 case LazyValueIdPtrTypeSimpleConst: {
1510 Error err;1555 Error err;
1511 bool zero_bits;1556 bool zero_bits;
1512 if ((err = type_val_resolve_zero_bits(g, type_val, nullptr, nullptr, &zero_bits))) {1557 if ((err = type_val_resolve_zero_bits(g, type_val, nullptr, nullptr, &zero_bits))) {
...@@ -5758,6 +5803,8 @@ static bool can_mutate_comptime_var_state(ZigValue *value) {...@@ -5758,6 +5803,8 @@ static bool can_mutate_comptime_var_state(ZigValue *value) {
5758 case LazyValueIdAlignOf:5803 case LazyValueIdAlignOf:
5759 case LazyValueIdSizeOf:5804 case LazyValueIdSizeOf:
5760 case LazyValueIdPtrType:5805 case LazyValueIdPtrType:
5806 case LazyValueIdPtrTypeSimple:
5807 case LazyValueIdPtrTypeSimpleConst:
5761 case LazyValueIdOptType:5808 case LazyValueIdOptType:
5762 case LazyValueIdSliceType:5809 case LazyValueIdSliceType:
5763 case LazyValueIdFnType:5810 case LazyValueIdFnType:
src/stage1/ir.cpp+140-26
...@@ -476,7 +476,8 @@ static void destroy_instruction_src(IrInstSrc *inst) {...@@ -476,7 +476,8 @@ static void destroy_instruction_src(IrInstSrc *inst) {
476 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcIntToErr *>(inst));476 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcIntToErr *>(inst));
477 case IrInstSrcIdErrToInt:477 case IrInstSrcIdErrToInt:
478 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcErrToInt *>(inst));478 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcErrToInt *>(inst));
479 case IrInstSrcIdCheckSwitchProngs:479 case IrInstSrcIdCheckSwitchProngsUnderNo:
480 case IrInstSrcIdCheckSwitchProngsUnderYes:
480 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCheckSwitchProngs *>(inst));481 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCheckSwitchProngs *>(inst));
481 case IrInstSrcIdCheckStatementIsVoid:482 case IrInstSrcIdCheckStatementIsVoid:
482 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCheckStatementIsVoid *>(inst));483 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCheckStatementIsVoid *>(inst));
...@@ -486,6 +487,9 @@ static void destroy_instruction_src(IrInstSrc *inst) {...@@ -486,6 +487,9 @@ static void destroy_instruction_src(IrInstSrc *inst) {
486 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTagName *>(inst));487 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTagName *>(inst));
487 case IrInstSrcIdPtrType:488 case IrInstSrcIdPtrType:
488 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPtrType *>(inst));489 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPtrType *>(inst));
490 case IrInstSrcIdPtrTypeSimple:
491 case IrInstSrcIdPtrTypeSimpleConst:
492 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPtrTypeSimple *>(inst));
489 case IrInstSrcIdDeclRef:493 case IrInstSrcIdDeclRef:
490 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcDeclRef *>(inst));494 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcDeclRef *>(inst));
491 case IrInstSrcIdPanic:495 case IrInstSrcIdPanic:
...@@ -514,7 +518,8 @@ static void destroy_instruction_src(IrInstSrc *inst) {...@@ -514,7 +518,8 @@ static void destroy_instruction_src(IrInstSrc *inst) {
514 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcResetResult *>(inst));518 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcResetResult *>(inst));
515 case IrInstSrcIdSetAlignStack:519 case IrInstSrcIdSetAlignStack:
516 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSetAlignStack *>(inst));520 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSetAlignStack *>(inst));
517 case IrInstSrcIdArgType:521 case IrInstSrcIdArgTypeAllowVarFalse:
522 case IrInstSrcIdArgTypeAllowVarTrue:
518 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcArgType *>(inst));523 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcArgType *>(inst));
519 case IrInstSrcIdExport:524 case IrInstSrcIdExport:
520 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcExport *>(inst));525 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcExport *>(inst));
...@@ -1470,10 +1475,6 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrToInt *) {...@@ -1470,10 +1475,6 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrToInt *) {
1470 return IrInstSrcIdErrToInt;1475 return IrInstSrcIdErrToInt;
1471}1476}
14721477
1473static constexpr IrInstSrcId ir_inst_id(IrInstSrcCheckSwitchProngs *) {
1474 return IrInstSrcIdCheckSwitchProngs;
1475}
1476
1477static constexpr IrInstSrcId ir_inst_id(IrInstSrcCheckStatementIsVoid *) {1478static constexpr IrInstSrcId ir_inst_id(IrInstSrcCheckStatementIsVoid *) {
1478 return IrInstSrcIdCheckStatementIsVoid;1479 return IrInstSrcIdCheckStatementIsVoid;
1479}1480}
...@@ -1546,10 +1547,6 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetAlignStack *) {...@@ -1546,10 +1547,6 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetAlignStack *) {
1546 return IrInstSrcIdSetAlignStack;1547 return IrInstSrcIdSetAlignStack;
1547}1548}
15481549
1549static constexpr IrInstSrcId ir_inst_id(IrInstSrcArgType *) {
1550 return IrInstSrcIdArgType;
1551}
1552
1553static constexpr IrInstSrcId ir_inst_id(IrInstSrcExport *) {1550static constexpr IrInstSrcId ir_inst_id(IrInstSrcExport *) {
1554 return IrInstSrcIdExport;1551 return IrInstSrcIdExport;
1555}1552}
...@@ -2615,11 +2612,35 @@ static IrInstGen *ir_build_br_gen(IrAnalyze *ira, IrInst *source_instr, IrBasicB...@@ -2615,11 +2612,35 @@ static IrInstGen *ir_build_br_gen(IrAnalyze *ira, IrInst *source_instr, IrBasicB
2615 return &inst->base;2612 return &inst->base;
2616}2613}
26172614
2615static IrInstSrc *ir_build_ptr_type_simple(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
2616 IrInstSrc *child_type, bool is_const)
2617{
2618 IrInstSrcPtrTypeSimple *inst = heap::c_allocator.create<IrInstSrcPtrTypeSimple>();
2619 inst->base.id = is_const ? IrInstSrcIdPtrTypeSimpleConst : IrInstSrcIdPtrTypeSimple;
2620 inst->base.base.scope = scope;
2621 inst->base.base.source_node = source_node;
2622 inst->base.base.debug_id = exec_next_debug_id(irb->exec);
2623 inst->base.owner_bb = irb->current_basic_block;
2624 ir_instruction_append(irb->current_basic_block, &inst->base);
2625
2626 inst->child_type = child_type;
2627
2628 ir_ref_instruction(child_type, irb->current_basic_block);
2629
2630 return &inst->base;
2631}
2632
2618static IrInstSrc *ir_build_ptr_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,2633static IrInstSrc *ir_build_ptr_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
2619 IrInstSrc *child_type, bool is_const, bool is_volatile, PtrLen ptr_len,2634 IrInstSrc *child_type, bool is_const, bool is_volatile, PtrLen ptr_len,
2620 IrInstSrc *sentinel, IrInstSrc *align_value,2635 IrInstSrc *sentinel, IrInstSrc *align_value,
2621 uint32_t bit_offset_start, uint32_t host_int_bytes, bool is_allow_zero)2636 uint32_t bit_offset_start, uint32_t host_int_bytes, bool is_allow_zero)
2622{2637{
2638 if (!is_volatile && ptr_len == PtrLenSingle && sentinel == nullptr && align_value == nullptr &&
2639 bit_offset_start == 0 && host_int_bytes == 0 && is_allow_zero == 0)
2640 {
2641 return ir_build_ptr_type_simple(irb, scope, source_node, child_type, is_const);
2642 }
2643
2623 IrInstSrcPtrType *inst = ir_build_instruction<IrInstSrcPtrType>(irb, scope, source_node);2644 IrInstSrcPtrType *inst = ir_build_instruction<IrInstSrcPtrType>(irb, scope, source_node);
2624 inst->sentinel = sentinel;2645 inst->sentinel = sentinel;
2625 inst->align_value = align_value;2646 inst->align_value = align_value;
...@@ -4354,13 +4375,19 @@ static IrInstSrc *ir_build_check_switch_prongs(IrBuilderSrc *irb, Scope *scope,...@@ -4354,13 +4375,19 @@ static IrInstSrc *ir_build_check_switch_prongs(IrBuilderSrc *irb, Scope *scope,
4354 IrInstSrc *target_value, IrInstSrcCheckSwitchProngsRange *ranges, size_t range_count,4375 IrInstSrc *target_value, IrInstSrcCheckSwitchProngsRange *ranges, size_t range_count,
4355 AstNode* else_prong, bool have_underscore_prong)4376 AstNode* else_prong, bool have_underscore_prong)
4356{4377{
4357 IrInstSrcCheckSwitchProngs *instruction = ir_build_instruction<IrInstSrcCheckSwitchProngs>(4378 IrInstSrcCheckSwitchProngs *instruction = heap::c_allocator.create<IrInstSrcCheckSwitchProngs>();
4358 irb, scope, source_node);4379 instruction->base.id = have_underscore_prong ?
4380 IrInstSrcIdCheckSwitchProngsUnderYes : IrInstSrcIdCheckSwitchProngsUnderNo;
4381 instruction->base.base.scope = scope;
4382 instruction->base.base.source_node = source_node;
4383 instruction->base.base.debug_id = exec_next_debug_id(irb->exec);
4384 instruction->base.owner_bb = irb->current_basic_block;
4385 ir_instruction_append(irb->current_basic_block, &instruction->base);
4386
4359 instruction->target_value = target_value;4387 instruction->target_value = target_value;
4360 instruction->ranges = ranges;4388 instruction->ranges = ranges;
4361 instruction->range_count = range_count;4389 instruction->range_count = range_count;
4362 instruction->else_prong = else_prong;4390 instruction->else_prong = else_prong;
4363 instruction->have_underscore_prong = have_underscore_prong;
43644391
4365 ir_ref_instruction(target_value, irb->current_basic_block);4392 ir_ref_instruction(target_value, irb->current_basic_block);
4366 for (size_t i = 0; i < range_count; i += 1) {4393 for (size_t i = 0; i < range_count; i += 1) {
...@@ -4590,10 +4617,17 @@ static IrInstSrc *ir_build_set_align_stack(IrBuilderSrc *irb, Scope *scope, AstN...@@ -4590,10 +4617,17 @@ static IrInstSrc *ir_build_set_align_stack(IrBuilderSrc *irb, Scope *scope, AstN
4590static IrInstSrc *ir_build_arg_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,4617static IrInstSrc *ir_build_arg_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
4591 IrInstSrc *fn_type, IrInstSrc *arg_index, bool allow_var)4618 IrInstSrc *fn_type, IrInstSrc *arg_index, bool allow_var)
4592{4619{
4593 IrInstSrcArgType *instruction = ir_build_instruction<IrInstSrcArgType>(irb, scope, source_node);4620 IrInstSrcArgType *instruction = heap::c_allocator.create<IrInstSrcArgType>();
4621 instruction->base.id = allow_var ?
4622 IrInstSrcIdArgTypeAllowVarTrue : IrInstSrcIdArgTypeAllowVarFalse;
4623 instruction->base.base.scope = scope;
4624 instruction->base.base.source_node = source_node;
4625 instruction->base.base.debug_id = exec_next_debug_id(irb->exec);
4626 instruction->base.owner_bb = irb->current_basic_block;
4627 ir_instruction_append(irb->current_basic_block, &instruction->base);
4628
4594 instruction->fn_type = fn_type;4629 instruction->fn_type = fn_type;
4595 instruction->arg_index = arg_index;4630 instruction->arg_index = arg_index;
4596 instruction->allow_var = allow_var;
45974631
4598 ir_ref_instruction(fn_type, irb->current_basic_block);4632 ir_ref_instruction(fn_type, irb->current_basic_block);
4599 ir_ref_instruction(arg_index, irb->current_basic_block);4633 ir_ref_instruction(arg_index, irb->current_basic_block);
...@@ -29702,7 +29736,7 @@ static IrInstGen *ir_analyze_instruction_test_comptime(IrAnalyze *ira, IrInstSrc...@@ -29702,7 +29736,7 @@ static IrInstGen *ir_analyze_instruction_test_comptime(IrAnalyze *ira, IrInstSrc
29702}29736}
2970329737
29704static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,29738static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
29705 IrInstSrcCheckSwitchProngs *instruction)29739 IrInstSrcCheckSwitchProngs *instruction, bool have_underscore_prong)
29706{29740{
29707 IrInstGen *target_value = instruction->target_value->child;29741 IrInstGen *target_value = instruction->target_value->child;
29708 ZigType *switch_type = target_value->value->type;29742 ZigType *switch_type = target_value->value->type;
...@@ -29767,7 +29801,7 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,...@@ -29767,7 +29801,7 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
29767 bigint_incr(&field_index);29801 bigint_incr(&field_index);
29768 }29802 }
29769 }29803 }
29770 if (instruction->have_underscore_prong) {29804 if (have_underscore_prong) {
29771 if (!switch_type->data.enumeration.non_exhaustive) {29805 if (!switch_type->data.enumeration.non_exhaustive) {
29772 ir_add_error(ira, &instruction->base.base,29806 ir_add_error(ira, &instruction->base.base,
29773 buf_sprintf("switch on exhaustive enum has `_` prong"));29807 buf_sprintf("switch on exhaustive enum has `_` prong"));
...@@ -30871,6 +30905,24 @@ static IrInstGen *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstSrcPtr...@@ -30871,6 +30905,24 @@ static IrInstGen *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstSrcPtr
30871 return ir_build_ptr_to_int_gen(ira, &instruction->base.base, target);30905 return ir_build_ptr_to_int_gen(ira, &instruction->base.base, target);
30872}30906}
3087330907
30908static IrInstGen *ir_analyze_instruction_ptr_type_simple(IrAnalyze *ira,
30909 IrInstSrcPtrTypeSimple *instruction, bool is_const)
30910{
30911 IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_type);
30912 result->value->special = ConstValSpecialLazy;
30913
30914 LazyValuePtrTypeSimple *lazy_ptr_type = heap::c_allocator.create<LazyValuePtrTypeSimple>();
30915 lazy_ptr_type->ira = ira; ira_ref(ira);
30916 result->value->data.x_lazy = &lazy_ptr_type->base;
30917 lazy_ptr_type->base.id = is_const ? LazyValueIdPtrTypeSimpleConst : LazyValueIdPtrTypeSimple;
30918
30919 lazy_ptr_type->elem_type = instruction->child_type->child;
30920 if (ir_resolve_type_lazy(ira, lazy_ptr_type->elem_type) == nullptr)
30921 return ira->codegen->invalid_inst_gen;
30922
30923 return result;
30924}
30925
30874static IrInstGen *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstSrcPtrType *instruction) {30926static IrInstGen *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstSrcPtrType *instruction) {
30875 IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_type);30927 IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_type);
30876 result->value->special = ConstValSpecialLazy;30928 result->value->special = ConstValSpecialLazy;
...@@ -30976,7 +31028,9 @@ static IrInstGen *ir_analyze_instruction_set_align_stack(IrAnalyze *ira, IrInstS...@@ -30976,7 +31028,9 @@ static IrInstGen *ir_analyze_instruction_set_align_stack(IrAnalyze *ira, IrInstS
30976 return ir_const_void(ira, &instruction->base.base);31028 return ir_const_void(ira, &instruction->base.base);
30977}31029}
3097831030
30979static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgType *instruction) {31031static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgType *instruction,
31032 bool allow_var)
31033{
30980 IrInstGen *fn_type_inst = instruction->fn_type->child;31034 IrInstGen *fn_type_inst = instruction->fn_type->child;
30981 ZigType *fn_type = ir_resolve_type(ira, fn_type_inst);31035 ZigType *fn_type = ir_resolve_type(ira, fn_type_inst);
30982 if (type_is_invalid(fn_type))31036 if (type_is_invalid(fn_type))
...@@ -30998,7 +31052,7 @@ static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgTy...@@ -30998,7 +31052,7 @@ static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgTy
3099831052
30999 FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;31053 FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
31000 if (arg_index >= fn_type_id->param_count) {31054 if (arg_index >= fn_type_id->param_count) {
31001 if (instruction->allow_var) {31055 if (allow_var) {
31002 // TODO remove this with var args31056 // TODO remove this with var args
31003 return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_anytype);31057 return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_anytype);
31004 }31058 }
...@@ -31013,7 +31067,7 @@ static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgTy...@@ -31013,7 +31067,7 @@ static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgTy
31013 // Args are only unresolved if our function is generic.31067 // Args are only unresolved if our function is generic.
31014 ir_assert(fn_type->data.fn.is_generic, &instruction->base.base);31068 ir_assert(fn_type->data.fn.is_generic, &instruction->base.base);
3101531069
31016 if (instruction->allow_var) {31070 if (allow_var) {
31017 return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_anytype);31071 return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_anytype);
31018 } else {31072 } else {
31019 ir_add_error(ira, &arg_index_inst->base,31073 ir_add_error(ira, &arg_index_inst->base,
...@@ -32341,8 +32395,10 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc...@@ -32341,8 +32395,10 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc
32341 return ir_analyze_instruction_fn_proto(ira, (IrInstSrcFnProto *)instruction);32395 return ir_analyze_instruction_fn_proto(ira, (IrInstSrcFnProto *)instruction);
32342 case IrInstSrcIdTestComptime:32396 case IrInstSrcIdTestComptime:
32343 return ir_analyze_instruction_test_comptime(ira, (IrInstSrcTestComptime *)instruction);32397 return ir_analyze_instruction_test_comptime(ira, (IrInstSrcTestComptime *)instruction);
32344 case IrInstSrcIdCheckSwitchProngs:32398 case IrInstSrcIdCheckSwitchProngsUnderNo:
32345 return ir_analyze_instruction_check_switch_prongs(ira, (IrInstSrcCheckSwitchProngs *)instruction);32399 return ir_analyze_instruction_check_switch_prongs(ira, (IrInstSrcCheckSwitchProngs *)instruction, false);
32400 case IrInstSrcIdCheckSwitchProngsUnderYes:
32401 return ir_analyze_instruction_check_switch_prongs(ira, (IrInstSrcCheckSwitchProngs *)instruction, true);
32346 case IrInstSrcIdCheckStatementIsVoid:32402 case IrInstSrcIdCheckStatementIsVoid:
32347 return ir_analyze_instruction_check_statement_is_void(ira, (IrInstSrcCheckStatementIsVoid *)instruction);32403 return ir_analyze_instruction_check_statement_is_void(ira, (IrInstSrcCheckStatementIsVoid *)instruction);
32348 case IrInstSrcIdDeclRef:32404 case IrInstSrcIdDeclRef:
...@@ -32373,6 +32429,10 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc...@@ -32373,6 +32429,10 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc
32373 return ir_analyze_instruction_set_eval_branch_quota(ira, (IrInstSrcSetEvalBranchQuota *)instruction);32429 return ir_analyze_instruction_set_eval_branch_quota(ira, (IrInstSrcSetEvalBranchQuota *)instruction);
32374 case IrInstSrcIdPtrType:32430 case IrInstSrcIdPtrType:
32375 return ir_analyze_instruction_ptr_type(ira, (IrInstSrcPtrType *)instruction);32431 return ir_analyze_instruction_ptr_type(ira, (IrInstSrcPtrType *)instruction);
32432 case IrInstSrcIdPtrTypeSimple:
32433 return ir_analyze_instruction_ptr_type_simple(ira, (IrInstSrcPtrTypeSimple *)instruction, false);
32434 case IrInstSrcIdPtrTypeSimpleConst:
32435 return ir_analyze_instruction_ptr_type_simple(ira, (IrInstSrcPtrTypeSimple *)instruction, true);
32376 case IrInstSrcIdAlignCast:32436 case IrInstSrcIdAlignCast:
32377 return ir_analyze_instruction_align_cast(ira, (IrInstSrcAlignCast *)instruction);32437 return ir_analyze_instruction_align_cast(ira, (IrInstSrcAlignCast *)instruction);
32378 case IrInstSrcIdImplicitCast:32438 case IrInstSrcIdImplicitCast:
...@@ -32383,8 +32443,10 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc...@@ -32383,8 +32443,10 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc
32383 return ir_analyze_instruction_reset_result(ira, (IrInstSrcResetResult *)instruction);32443 return ir_analyze_instruction_reset_result(ira, (IrInstSrcResetResult *)instruction);
32384 case IrInstSrcIdSetAlignStack:32444 case IrInstSrcIdSetAlignStack:
32385 return ir_analyze_instruction_set_align_stack(ira, (IrInstSrcSetAlignStack *)instruction);32445 return ir_analyze_instruction_set_align_stack(ira, (IrInstSrcSetAlignStack *)instruction);
32386 case IrInstSrcIdArgType:32446 case IrInstSrcIdArgTypeAllowVarFalse:
32387 return ir_analyze_instruction_arg_type(ira, (IrInstSrcArgType *)instruction);32447 return ir_analyze_instruction_arg_type(ira, (IrInstSrcArgType *)instruction, false);
32448 case IrInstSrcIdArgTypeAllowVarTrue:
32449 return ir_analyze_instruction_arg_type(ira, (IrInstSrcArgType *)instruction, true);
32388 case IrInstSrcIdExport:32450 case IrInstSrcIdExport:
32389 return ir_analyze_instruction_export(ira, (IrInstSrcExport *)instruction);32451 return ir_analyze_instruction_export(ira, (IrInstSrcExport *)instruction);
32390 case IrInstSrcIdExtern:32452 case IrInstSrcIdExtern:
...@@ -32737,12 +32799,15 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) {...@@ -32737,12 +32799,15 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) {
32737 case IrInstSrcIdMemcpy:32799 case IrInstSrcIdMemcpy:
32738 case IrInstSrcIdBreakpoint:32800 case IrInstSrcIdBreakpoint:
32739 case IrInstSrcIdOverflowOp: // TODO when we support multiple returns this can be side effect free32801 case IrInstSrcIdOverflowOp: // TODO when we support multiple returns this can be side effect free
32740 case IrInstSrcIdCheckSwitchProngs:32802 case IrInstSrcIdCheckSwitchProngsUnderNo:
32803 case IrInstSrcIdCheckSwitchProngsUnderYes:
32741 case IrInstSrcIdCheckStatementIsVoid:32804 case IrInstSrcIdCheckStatementIsVoid:
32742 case IrInstSrcIdCheckRuntimeScope:32805 case IrInstSrcIdCheckRuntimeScope:
32743 case IrInstSrcIdPanic:32806 case IrInstSrcIdPanic:
32744 case IrInstSrcIdSetEvalBranchQuota:32807 case IrInstSrcIdSetEvalBranchQuota:
32745 case IrInstSrcIdPtrType:32808 case IrInstSrcIdPtrType:
32809 case IrInstSrcIdPtrTypeSimple:
32810 case IrInstSrcIdPtrTypeSimpleConst:
32746 case IrInstSrcIdSetAlignStack:32811 case IrInstSrcIdSetAlignStack:
32747 case IrInstSrcIdExport:32812 case IrInstSrcIdExport:
32748 case IrInstSrcIdExtern:32813 case IrInstSrcIdExtern:
...@@ -32826,7 +32891,8 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) {...@@ -32826,7 +32891,8 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) {
32826 case IrInstSrcIdAlignCast:32891 case IrInstSrcIdAlignCast:
32827 case IrInstSrcIdImplicitCast:32892 case IrInstSrcIdImplicitCast:
32828 case IrInstSrcIdResolveResult:32893 case IrInstSrcIdResolveResult:
32829 case IrInstSrcIdArgType:32894 case IrInstSrcIdArgTypeAllowVarFalse:
32895 case IrInstSrcIdArgTypeAllowVarTrue:
32830 case IrInstSrcIdErrorReturnTrace:32896 case IrInstSrcIdErrorReturnTrace:
32831 case IrInstSrcIdErrorUnion:32897 case IrInstSrcIdErrorUnion:
32832 case IrInstSrcIdFloatOp:32898 case IrInstSrcIdFloatOp:
...@@ -33249,6 +33315,54 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) {...@@ -33249,6 +33315,54 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) {
33249 // We can't free the lazy value here, because multiple other ZigValues might be pointing to it.33315 // We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
33250 return ErrorNone;33316 return ErrorNone;
33251 }33317 }
33318 case LazyValueIdPtrTypeSimple: {
33319 LazyValuePtrTypeSimple *lazy_ptr_type = reinterpret_cast<LazyValuePtrTypeSimple *>(val->data.x_lazy);
33320 IrAnalyze *ira = lazy_ptr_type->ira;
33321
33322 ZigType *elem_type = ir_resolve_type(ira, lazy_ptr_type->elem_type);
33323 if (type_is_invalid(elem_type))
33324 return ErrorSemanticAnalyzeFail;
33325
33326 if (elem_type->id == ZigTypeIdUnreachable) {
33327 ir_add_error(ira, &lazy_ptr_type->elem_type->base,
33328 buf_create_from_str("pointer to noreturn not allowed"));
33329 return ErrorSemanticAnalyzeFail;
33330 }
33331
33332 assert(val->type->id == ZigTypeIdMetaType);
33333 val->data.x_type = get_pointer_to_type_extra2(ira->codegen, elem_type,
33334 false, false, PtrLenSingle, 0,
33335 0, 0,
33336 false, VECTOR_INDEX_NONE, nullptr, nullptr);
33337 val->special = ConstValSpecialStatic;
33338
33339 // We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
33340 return ErrorNone;
33341 }
33342 case LazyValueIdPtrTypeSimpleConst: {
33343 LazyValuePtrTypeSimple *lazy_ptr_type = reinterpret_cast<LazyValuePtrTypeSimple *>(val->data.x_lazy);
33344 IrAnalyze *ira = lazy_ptr_type->ira;
33345
33346 ZigType *elem_type = ir_resolve_type(ira, lazy_ptr_type->elem_type);
33347 if (type_is_invalid(elem_type))
33348 return ErrorSemanticAnalyzeFail;
33349
33350 if (elem_type->id == ZigTypeIdUnreachable) {
33351 ir_add_error(ira, &lazy_ptr_type->elem_type->base,
33352 buf_create_from_str("pointer to noreturn not allowed"));
33353 return ErrorSemanticAnalyzeFail;
33354 }
33355
33356 assert(val->type->id == ZigTypeIdMetaType);
33357 val->data.x_type = get_pointer_to_type_extra2(ira->codegen, elem_type,
33358 true, false, PtrLenSingle, 0,
33359 0, 0,
33360 false, VECTOR_INDEX_NONE, nullptr, nullptr);
33361 val->special = ConstValSpecialStatic;
33362
33363 // We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
33364 return ErrorNone;
33365 }
33252 case LazyValueIdArrayType: {33366 case LazyValueIdArrayType: {
33253 LazyValueArrayType *lazy_array_type = reinterpret_cast<LazyValueArrayType *>(val->data.x_lazy);33367 LazyValueArrayType *lazy_array_type = reinterpret_cast<LazyValueArrayType *>(val->data.x_lazy);
33254 IrAnalyze *ira = lazy_array_type->ira;33368 IrAnalyze *ira = lazy_array_type->ira;
src/stage1/ir_print.cpp+49-10
...@@ -270,8 +270,10 @@ const char* ir_inst_src_type_str(IrInstSrcId id) {...@@ -270,8 +270,10 @@ const char* ir_inst_src_type_str(IrInstSrcId id) {
270 return "SrcIntToErr";270 return "SrcIntToErr";
271 case IrInstSrcIdErrToInt:271 case IrInstSrcIdErrToInt:
272 return "SrcErrToInt";272 return "SrcErrToInt";
273 case IrInstSrcIdCheckSwitchProngs:273 case IrInstSrcIdCheckSwitchProngsUnderNo:
274 return "SrcCheckSwitchProngs";274 return "SrcCheckSwitchProngsUnderNo";
275 case IrInstSrcIdCheckSwitchProngsUnderYes:
276 return "SrcCheckSwitchProngsUnderYes";
275 case IrInstSrcIdCheckStatementIsVoid:277 case IrInstSrcIdCheckStatementIsVoid:
276 return "SrcCheckStatementIsVoid";278 return "SrcCheckStatementIsVoid";
277 case IrInstSrcIdTypeName:279 case IrInstSrcIdTypeName:
...@@ -298,6 +300,10 @@ const char* ir_inst_src_type_str(IrInstSrcId id) {...@@ -298,6 +300,10 @@ const char* ir_inst_src_type_str(IrInstSrcId id) {
298 return "SrcSetEvalBranchQuota";300 return "SrcSetEvalBranchQuota";
299 case IrInstSrcIdPtrType:301 case IrInstSrcIdPtrType:
300 return "SrcPtrType";302 return "SrcPtrType";
303 case IrInstSrcIdPtrTypeSimple:
304 return "SrcPtrTypeSimple";
305 case IrInstSrcIdPtrTypeSimpleConst:
306 return "SrcPtrTypeSimpleConst";
301 case IrInstSrcIdAlignCast:307 case IrInstSrcIdAlignCast:
302 return "SrcAlignCast";308 return "SrcAlignCast";
303 case IrInstSrcIdImplicitCast:309 case IrInstSrcIdImplicitCast:
...@@ -308,8 +314,10 @@ const char* ir_inst_src_type_str(IrInstSrcId id) {...@@ -308,8 +314,10 @@ const char* ir_inst_src_type_str(IrInstSrcId id) {
308 return "SrcResetResult";314 return "SrcResetResult";
309 case IrInstSrcIdSetAlignStack:315 case IrInstSrcIdSetAlignStack:
310 return "SrcSetAlignStack";316 return "SrcSetAlignStack";
311 case IrInstSrcIdArgType:317 case IrInstSrcIdArgTypeAllowVarFalse:
312 return "SrcArgType";318 return "SrcArgTypeAllowVarFalse";
319 case IrInstSrcIdArgTypeAllowVarTrue:
320 return "SrcArgTypeAllowVarTrue";
313 case IrInstSrcIdExport:321 case IrInstSrcIdExport:
314 return "SrcExport";322 return "SrcExport";
315 case IrInstSrcIdExtern:323 case IrInstSrcIdExtern:
...@@ -2187,7 +2195,9 @@ static void ir_print_err_to_int(IrPrintGen *irp, IrInstGenErrToInt *instruction)...@@ -2187,7 +2195,9 @@ static void ir_print_err_to_int(IrPrintGen *irp, IrInstGenErrToInt *instruction)
2187 ir_print_other_inst_gen(irp, instruction->target);2195 ir_print_other_inst_gen(irp, instruction->target);
2188}2196}
21892197
2190static void ir_print_check_switch_prongs(IrPrintSrc *irp, IrInstSrcCheckSwitchProngs *instruction) {2198static void ir_print_check_switch_prongs(IrPrintSrc *irp, IrInstSrcCheckSwitchProngs *instruction,
2199 bool have_underscore_prong)
2200{
2191 fprintf(irp->f, "@checkSwitchProngs(");2201 fprintf(irp->f, "@checkSwitchProngs(");
2192 ir_print_other_inst_src(irp, instruction->target_value);2202 ir_print_other_inst_src(irp, instruction->target_value);
2193 fprintf(irp->f, ",");2203 fprintf(irp->f, ",");
...@@ -2200,6 +2210,8 @@ static void ir_print_check_switch_prongs(IrPrintSrc *irp, IrInstSrcCheckSwitchPr...@@ -2200,6 +2210,8 @@ static void ir_print_check_switch_prongs(IrPrintSrc *irp, IrInstSrcCheckSwitchPr
2200 }2210 }
2201 const char *have_else_str = instruction->else_prong != nullptr ? "yes" : "no";2211 const char *have_else_str = instruction->else_prong != nullptr ? "yes" : "no";
2202 fprintf(irp->f, ")else:%s", have_else_str);2212 fprintf(irp->f, ")else:%s", have_else_str);
2213 const char *have_under_str = have_underscore_prong ? "yes" : "no";
2214 fprintf(irp->f, " _:%s", have_under_str);
2203}2215}
22042216
2205static void ir_print_check_statement_is_void(IrPrintSrc *irp, IrInstSrcCheckStatementIsVoid *instruction) {2217static void ir_print_check_statement_is_void(IrPrintSrc *irp, IrInstSrcCheckStatementIsVoid *instruction) {
...@@ -2237,6 +2249,15 @@ static void ir_print_ptr_type(IrPrintSrc *irp, IrInstSrcPtrType *instruction) {...@@ -2237,6 +2249,15 @@ static void ir_print_ptr_type(IrPrintSrc *irp, IrInstSrcPtrType *instruction) {
2237 ir_print_other_inst_src(irp, instruction->child_type);2249 ir_print_other_inst_src(irp, instruction->child_type);
2238}2250}
22392251
2252static void ir_print_ptr_type_simple(IrPrintSrc *irp, IrInstSrcPtrTypeSimple *instruction,
2253 bool is_const)
2254{
2255 fprintf(irp->f, "&");
2256 const char *const_str = is_const ? "const " : "";
2257 fprintf(irp->f, "*%s", const_str);
2258 ir_print_other_inst_src(irp, instruction->child_type);
2259}
2260
2240static void ir_print_decl_ref(IrPrintSrc *irp, IrInstSrcDeclRef *instruction) {2261static void ir_print_decl_ref(IrPrintSrc *irp, IrInstSrcDeclRef *instruction) {
2241 const char *ptr_str = (instruction->lval != LValNone) ? "ptr " : "";2262 const char *ptr_str = (instruction->lval != LValNone) ? "ptr " : "";
2242 fprintf(irp->f, "declref %s%s", ptr_str, buf_ptr(instruction->tld->name));2263 fprintf(irp->f, "declref %s%s", ptr_str, buf_ptr(instruction->tld->name));
...@@ -2344,11 +2365,17 @@ static void ir_print_set_align_stack(IrPrintSrc *irp, IrInstSrcSetAlignStack *in...@@ -2344,11 +2365,17 @@ static void ir_print_set_align_stack(IrPrintSrc *irp, IrInstSrcSetAlignStack *in
2344 fprintf(irp->f, ")");2365 fprintf(irp->f, ")");
2345}2366}
23462367
2347static void ir_print_arg_type(IrPrintSrc *irp, IrInstSrcArgType *instruction) {2368static void ir_print_arg_type(IrPrintSrc *irp, IrInstSrcArgType *instruction, bool allow_var) {
2348 fprintf(irp->f, "@ArgType(");2369 fprintf(irp->f, "@ArgType(");
2349 ir_print_other_inst_src(irp, instruction->fn_type);2370 ir_print_other_inst_src(irp, instruction->fn_type);
2350 fprintf(irp->f, ",");2371 fprintf(irp->f, ",");
2351 ir_print_other_inst_src(irp, instruction->arg_index);2372 ir_print_other_inst_src(irp, instruction->arg_index);
2373 fprintf(irp->f, ",");
2374 if (allow_var) {
2375 fprintf(irp->f, "allow_var=true");
2376 } else {
2377 fprintf(irp->f, "allow_var=false");
2378 }
2352 fprintf(irp->f, ")");2379 fprintf(irp->f, ")");
2353}2380}
23542381
...@@ -2885,8 +2912,11 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai...@@ -2885,8 +2912,11 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai
2885 case IrInstSrcIdErrToInt:2912 case IrInstSrcIdErrToInt:
2886 ir_print_err_to_int(irp, (IrInstSrcErrToInt *)instruction);2913 ir_print_err_to_int(irp, (IrInstSrcErrToInt *)instruction);
2887 break;2914 break;
2888 case IrInstSrcIdCheckSwitchProngs:2915 case IrInstSrcIdCheckSwitchProngsUnderNo:
2889 ir_print_check_switch_prongs(irp, (IrInstSrcCheckSwitchProngs *)instruction);2916 ir_print_check_switch_prongs(irp, (IrInstSrcCheckSwitchProngs *)instruction, false);
2917 break;
2918 case IrInstSrcIdCheckSwitchProngsUnderYes:
2919 ir_print_check_switch_prongs(irp, (IrInstSrcCheckSwitchProngs *)instruction, true);
2890 break;2920 break;
2891 case IrInstSrcIdCheckStatementIsVoid:2921 case IrInstSrcIdCheckStatementIsVoid:
2892 ir_print_check_statement_is_void(irp, (IrInstSrcCheckStatementIsVoid *)instruction);2922 ir_print_check_statement_is_void(irp, (IrInstSrcCheckStatementIsVoid *)instruction);
...@@ -2900,6 +2930,12 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai...@@ -2900,6 +2930,12 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai
2900 case IrInstSrcIdPtrType:2930 case IrInstSrcIdPtrType:
2901 ir_print_ptr_type(irp, (IrInstSrcPtrType *)instruction);2931 ir_print_ptr_type(irp, (IrInstSrcPtrType *)instruction);
2902 break;2932 break;
2933 case IrInstSrcIdPtrTypeSimple:
2934 ir_print_ptr_type_simple(irp, (IrInstSrcPtrTypeSimple *)instruction, false);
2935 break;
2936 case IrInstSrcIdPtrTypeSimpleConst:
2937 ir_print_ptr_type_simple(irp, (IrInstSrcPtrTypeSimple *)instruction, true);
2938 break;
2903 case IrInstSrcIdDeclRef:2939 case IrInstSrcIdDeclRef:
2904 ir_print_decl_ref(irp, (IrInstSrcDeclRef *)instruction);2940 ir_print_decl_ref(irp, (IrInstSrcDeclRef *)instruction);
2905 break;2941 break;
...@@ -2942,8 +2978,11 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai...@@ -2942,8 +2978,11 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai
2942 case IrInstSrcIdSetAlignStack:2978 case IrInstSrcIdSetAlignStack:
2943 ir_print_set_align_stack(irp, (IrInstSrcSetAlignStack *)instruction);2979 ir_print_set_align_stack(irp, (IrInstSrcSetAlignStack *)instruction);
2944 break;2980 break;
2945 case IrInstSrcIdArgType:2981 case IrInstSrcIdArgTypeAllowVarFalse:
2946 ir_print_arg_type(irp, (IrInstSrcArgType *)instruction);2982 ir_print_arg_type(irp, (IrInstSrcArgType *)instruction, false);
2983 break;
2984 case IrInstSrcIdArgTypeAllowVarTrue:
2985 ir_print_arg_type(irp, (IrInstSrcArgType *)instruction, true);
2947 break;2986 break;
2948 case IrInstSrcIdExport:2987 case IrInstSrcIdExport:
2949 ir_print_export(irp, (IrInstSrcExport *)instruction);2988 ir_print_export(irp, (IrInstSrcExport *)instruction);
src/translate_c.zig+117-55
...@@ -11,6 +11,7 @@ const math = std.math;...@@ -11,6 +11,7 @@ const math = std.math;
11const ast = @import("translate_c/ast.zig");11const ast = @import("translate_c/ast.zig");
12const Node = ast.Node;12const Node = ast.Node;
13const Tag = Node.Tag;13const Tag = Node.Tag;
14const c_builtins = std.c.builtins;
1415
15const CallingConvention = std.builtin.CallingConvention;16const CallingConvention = std.builtin.CallingConvention;
1617
...@@ -635,7 +636,7 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co...@@ -635,7 +636,7 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co
635 if (has_init) trans_init: {636 if (has_init) trans_init: {
636 if (decl_init) |expr| {637 if (decl_init) |expr| {
637 const node_or_error = if (expr.getStmtClass() == .StringLiteralClass)638 const node_or_error = if (expr.getStmtClass() == .StringLiteralClass)
638 transStringLiteralAsArray(c, scope, @ptrCast(*const clang.StringLiteral, expr), zigArraySize(c, type_node) catch 0)639 transStringLiteralInitializer(c, scope, @ptrCast(*const clang.StringLiteral, expr), type_node)
639 else640 else
640 transExprCoercing(c, scope, expr, .used);641 transExprCoercing(c, scope, expr, .used);
641 init_node = node_or_error catch |err| switch (err) {642 init_node = node_or_error catch |err| switch (err) {
...@@ -1058,6 +1059,10 @@ fn transStmt(...@@ -1058,6 +1059,10 @@ fn transStmt(
1058 const compound_literal = @ptrCast(*const clang.CompoundLiteralExpr, stmt);1059 const compound_literal = @ptrCast(*const clang.CompoundLiteralExpr, stmt);
1059 return transExpr(c, scope, compound_literal.getInitializer(), result_used);1060 return transExpr(c, scope, compound_literal.getInitializer(), result_used);
1060 },1061 },
1062 .GenericSelectionExprClass => {
1063 const gen_sel = @ptrCast(*const clang.GenericSelectionExpr, stmt);
1064 return transExpr(c, scope, gen_sel.getResultExpr(), result_used);
1065 },
1061 else => {1066 else => {
1062 return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO implement translation of stmt class {s}", .{@tagName(sc)});1067 return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO implement translation of stmt class {s}", .{@tagName(sc)});
1063 },1068 },
...@@ -1407,7 +1412,7 @@ fn transDeclStmtOne(...@@ -1407,7 +1412,7 @@ fn transDeclStmtOne(
14071412
1408 var init_node = if (decl_init) |expr|1413 var init_node = if (decl_init) |expr|
1409 if (expr.getStmtClass() == .StringLiteralClass)1414 if (expr.getStmtClass() == .StringLiteralClass)
1410 try transStringLiteralAsArray(c, scope, @ptrCast(*const clang.StringLiteral, expr), try zigArraySize(c, type_node))1415 try transStringLiteralInitializer(c, scope, @ptrCast(*const clang.StringLiteral, expr), type_node)
1411 else1416 else
1412 try transExprCoercing(c, scope, expr, .used)1417 try transExprCoercing(c, scope, expr, .used)
1413 else1418 else
...@@ -1522,7 +1527,7 @@ fn transImplicitCastExpr(...@@ -1522,7 +1527,7 @@ fn transImplicitCastExpr(
1522 return maybeSuppressResult(c, scope, result_used, ne);1527 return maybeSuppressResult(c, scope, result_used, ne);
1523 },1528 },
1524 .BuiltinFnToFnPtr => {1529 .BuiltinFnToFnPtr => {
1525 return transExpr(c, scope, sub_expr, result_used);1530 return transBuiltinFnExpr(c, scope, sub_expr, result_used);
1526 },1531 },
1527 .ToVoid => {1532 .ToVoid => {
1528 // Should only appear in the rhs and lhs of a ConditionalOperator1533 // Should only appear in the rhs and lhs of a ConditionalOperator
...@@ -1538,6 +1543,22 @@ fn transImplicitCastExpr(...@@ -1538,6 +1543,22 @@ fn transImplicitCastExpr(
1538 }1543 }
1539}1544}
15401545
1546fn isBuiltinDefined(name: []const u8) bool {
1547 inline for (std.meta.declarations(c_builtins)) |decl| {
1548 if (std.mem.eql(u8, name, decl.name)) return true;
1549 }
1550 return false;
1551}
1552
1553fn transBuiltinFnExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!Node {
1554 const node = try transExpr(c, scope, expr, used);
1555 if (node.castTag(.identifier)) |ident| {
1556 const name = ident.data;
1557 if (!isBuiltinDefined(name)) return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "TODO implement function '{s}' in std.c.builtins", .{name});
1558 }
1559 return node;
1560}
1561
1541fn transBoolExpr(1562fn transBoolExpr(
1542 c: *Context,1563 c: *Context,
1543 scope: *Scope,1564 scope: *Scope,
...@@ -1582,6 +1603,10 @@ fn exprIsNarrowStringLiteral(expr: *const clang.Expr) bool {...@@ -1582,6 +1603,10 @@ fn exprIsNarrowStringLiteral(expr: *const clang.Expr) bool {
1582 const op_expr = @ptrCast(*const clang.ParenExpr, expr).getSubExpr();1603 const op_expr = @ptrCast(*const clang.ParenExpr, expr).getSubExpr();
1583 return exprIsNarrowStringLiteral(op_expr);1604 return exprIsNarrowStringLiteral(op_expr);
1584 },1605 },
1606 .GenericSelectionExprClass => {
1607 const gen_sel = @ptrCast(*const clang.GenericSelectionExpr, expr);
1608 return exprIsNarrowStringLiteral(gen_sel.getResultExpr());
1609 },
1585 else => return false,1610 else => return false,
1586 }1611 }
1587}1612}
...@@ -1733,6 +1758,20 @@ fn transReturnStmt(...@@ -1733,6 +1758,20 @@ fn transReturnStmt(
1733 return Tag.@"return".create(c.arena, rhs);1758 return Tag.@"return".create(c.arena, rhs);
1734}1759}
17351760
1761fn transNarrowStringLiteral(
1762 c: *Context,
1763 scope: *Scope,
1764 stmt: *const clang.StringLiteral,
1765 result_used: ResultUsed,
1766) TransError!Node {
1767 var len: usize = undefined;
1768 const bytes_ptr = stmt.getString_bytes_begin_size(&len);
1769
1770 const str = try std.fmt.allocPrint(c.arena, "\"{}\"", .{std.zig.fmtEscapes(bytes_ptr[0..len])});
1771 const node = try Tag.string_literal.create(c.arena, str);
1772 return maybeSuppressResult(c, scope, result_used, node);
1773}
1774
1736fn transStringLiteral(1775fn transStringLiteral(
1737 c: *Context,1776 c: *Context,
1738 scope: *Scope,1777 scope: *Scope,
...@@ -1741,19 +1780,14 @@ fn transStringLiteral(...@@ -1741,19 +1780,14 @@ fn transStringLiteral(
1741) TransError!Node {1780) TransError!Node {
1742 const kind = stmt.getKind();1781 const kind = stmt.getKind();
1743 switch (kind) {1782 switch (kind) {
1744 .Ascii, .UTF8 => {1783 .Ascii, .UTF8 => return transNarrowStringLiteral(c, scope, stmt, result_used),
1745 var len: usize = undefined;
1746 const bytes_ptr = stmt.getString_bytes_begin_size(&len);
1747
1748 const str = try std.fmt.allocPrint(c.arena, "\"{}\"", .{std.zig.fmtEscapes(bytes_ptr[0..len])});
1749 const node = try Tag.string_literal.create(c.arena, str);
1750 return maybeSuppressResult(c, scope, result_used, node);
1751 },
1752 .UTF16, .UTF32, .Wide => {1784 .UTF16, .UTF32, .Wide => {
1753 const str_type = @tagName(stmt.getKind());1785 const str_type = @tagName(stmt.getKind());
1754 const name = try std.fmt.allocPrint(c.arena, "zig.{s}_string_{d}", .{ str_type, c.getMangle() });1786 const name = try std.fmt.allocPrint(c.arena, "zig.{s}_string_{d}", .{ str_type, c.getMangle() });
1755 const lit_array = try transStringLiteralAsArray(c, scope, stmt, stmt.getLength() + 1);
17561787
1788 const expr_base = @ptrCast(*const clang.Expr, stmt);
1789 const array_type = try transQualTypeInitialized(c, scope, expr_base.getType(), expr_base, expr_base.getBeginLoc());
1790 const lit_array = try transStringLiteralInitializer(c, scope, stmt, array_type);
1757 const decl = try Tag.var_simple.create(c.arena, .{ .name = name, .init = lit_array });1791 const decl = try Tag.var_simple.create(c.arena, .{ .name = name, .init = lit_array });
1758 try scope.appendNode(decl);1792 try scope.appendNode(decl);
1759 const node = try Tag.identifier.create(c.arena, name);1793 const node = try Tag.identifier.create(c.arena, name);
...@@ -1762,52 +1796,67 @@ fn transStringLiteral(...@@ -1762,52 +1796,67 @@ fn transStringLiteral(
1762 }1796 }
1763}1797}
17641798
1765/// Parse the size of an array back out from an ast Node.1799fn getArrayPayload(array_type: Node) ast.Payload.Array.ArrayTypeInfo {
1766fn zigArraySize(c: *Context, node: Node) TransError!usize {1800 return (array_type.castTag(.array_type) orelse array_type.castTag(.null_sentinel_array_type).?).data;
1767 if (node.castTag(.array_type)) |array| {
1768 return array.data.len;
1769 }
1770 return error.UnsupportedTranslation;
1771}1801}
17721802
1773/// Translate a string literal to an array of integers. Used when an1803/// Translate a string literal that is initializing an array. In general narrow string
1774/// array is initialized from a string literal. `array_size` is the1804/// literals become `"<string>".*` or `"<string>"[0..<size>].*` if they need truncation.
1775/// size of the array being initialized. If the string literal is larger1805/// Wide string literals become an array of integers. zero-fillers pad out the array to
1776/// than the array, truncate the string. If the array is larger than the1806/// the appropriate length, if necessary.
1777/// string literal, pad the array with 0's1807fn transStringLiteralInitializer(
1778fn transStringLiteralAsArray(
1779 c: *Context,1808 c: *Context,
1780 scope: *Scope,1809 scope: *Scope,
1781 stmt: *const clang.StringLiteral,1810 stmt: *const clang.StringLiteral,
1782 array_size: usize,1811 array_type: Node,
1783) TransError!Node {1812) TransError!Node {
1784 if (array_size == 0) return error.UnsupportedType;1813 assert(array_type.tag() == .array_type or array_type.tag() == .null_sentinel_array_type);
1814
1815 const is_narrow = stmt.getKind() == .Ascii or stmt.getKind() == .UTF8;
17851816
1786 const str_length = stmt.getLength();1817 const str_length = stmt.getLength();
1818 const payload = getArrayPayload(array_type);
1819 const array_size = payload.len;
1820 const elem_type = payload.elem_type;
1821
1822 if (array_size == 0) return Tag.empty_array.create(c.arena, elem_type);
1823
1824 const num_inits = math.min(str_length, array_size);
1825 const init_node = if (num_inits > 0) blk: {
1826 if (is_narrow) {
1827 // "string literal".* or string literal"[0..num_inits].*
1828 var str = try transNarrowStringLiteral(c, scope, stmt, .used);
1829 if (str_length != array_size) str = try Tag.string_slice.create(c.arena, .{ .string = str, .end = num_inits });
1830 break :blk try Tag.deref.create(c.arena, str);
1831 } else {
1832 const init_list = try c.arena.alloc(Node, num_inits);
1833 var i: c_uint = 0;
1834 while (i < num_inits) : (i += 1) {
1835 init_list[i] = try transCreateCharLitNode(c, false, stmt.getCodeUnit(i));
1836 }
1837 const init_args = .{ .len = num_inits, .elem_type = elem_type };
1838 const init_array_type = try if (array_type.tag() == .array_type) Tag.array_type.create(c.arena, init_args) else Tag.null_sentinel_array_type.create(c.arena, init_args);
1839 break :blk try Tag.array_init.create(c.arena, .{
1840 .cond = init_array_type,
1841 .cases = init_list,
1842 });
1843 }
1844 } else null;
17871845
1788 const expr_base = @ptrCast(*const clang.Expr, stmt);1846 if (num_inits == array_size) return init_node.?; // init_node is only null if num_inits == 0; but if num_inits == array_size == 0 we've already returned
1789 const ty = expr_base.getType().getTypePtr();1847 assert(array_size > str_length); // If array_size <= str_length, `num_inits == array_size` and we've already returned.
1790 const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, ty);
17911848
1792 const elem_type = try transQualType(c, scope, const_arr_ty.getElementType(), expr_base.getBeginLoc());1849 const filler_node = try Tag.array_filler.create(c.arena, .{
1793 const arr_type = try Tag.array_type.create(c.arena, .{ .len = array_size, .elem_type = elem_type });1850 .type = elem_type,
1794 const init_list = try c.arena.alloc(Node, array_size);1851 .filler = Tag.zero_literal.init(),
1852 .count = array_size - str_length,
1853 });
17951854
1796 var i: c_uint = 0;1855 if (init_node) |some| {
1797 const kind = stmt.getKind();1856 return Tag.array_cat.create(c.arena, .{ .lhs = some, .rhs = filler_node });
1798 const narrow = kind == .Ascii or kind == .UTF8;1857 } else {
1799 while (i < str_length and i < array_size) : (i += 1) {1858 return filler_node;
1800 const code_unit = stmt.getCodeUnit(i);
1801 init_list[i] = try transCreateCharLitNode(c, narrow, code_unit);
1802 }
1803 while (i < array_size) : (i += 1) {
1804 init_list[i] = try transCreateNodeNumber(c, 0, .int);
1805 }1859 }
1806
1807 return Tag.array_init.create(c.arena, .{
1808 .cond = arr_type,
1809 .cases = init_list,
1810 });
1811}1860}
18121861
1813/// determine whether `stmt` is a "pointer subtraction expression" - a subtraction where1862/// determine whether `stmt` is a "pointer subtraction expression" - a subtraction where
...@@ -1836,6 +1885,7 @@ fn cIntTypeForEnum(enum_qt: clang.QualType) clang.QualType {...@@ -1836,6 +1885,7 @@ fn cIntTypeForEnum(enum_qt: clang.QualType) clang.QualType {
1836 return enum_decl.getIntegerType();1885 return enum_decl.getIntegerType();
1837}1886}
18381887
1888// when modifying this function, make sure to also update std.meta.cast
1839fn transCCast(1889fn transCCast(
1840 c: *Context,1890 c: *Context,
1841 scope: *Scope,1891 scope: *Scope,
...@@ -2725,6 +2775,10 @@ fn cIsFunctionDeclRef(expr: *const clang.Expr) bool {...@@ -2725,6 +2775,10 @@ fn cIsFunctionDeclRef(expr: *const clang.Expr) bool {
2725 const opcode = un_op.getOpcode();2775 const opcode = un_op.getOpcode();
2726 return (opcode == .AddrOf or opcode == .Deref) and cIsFunctionDeclRef(un_op.getSubExpr());2776 return (opcode == .AddrOf or opcode == .Deref) and cIsFunctionDeclRef(un_op.getSubExpr());
2727 },2777 },
2778 .GenericSelectionExprClass => {
2779 const gen_sel = @ptrCast(*const clang.GenericSelectionExpr, expr);
2780 return cIsFunctionDeclRef(gen_sel.getResultExpr());
2781 },
2728 else => return false,2782 else => return false,
2729 }2783 }
2730}2784}
...@@ -3194,11 +3248,11 @@ fn transFloatingLiteral(c: *Context, scope: *Scope, stmt: *const clang.FloatingL...@@ -3194,11 +3248,11 @@ fn transFloatingLiteral(c: *Context, scope: *Scope, stmt: *const clang.FloatingL
3194 var dbl = stmt.getValueAsApproximateDouble();3248 var dbl = stmt.getValueAsApproximateDouble();
3195 const is_negative = dbl < 0;3249 const is_negative = dbl < 0;
3196 if (is_negative) dbl = -dbl;3250 if (is_negative) dbl = -dbl;
3197 const str = try std.fmt.allocPrint(c.arena, "{d}", .{dbl});3251 const str = if (dbl == std.math.floor(dbl))
3198 var node = if (dbl == std.math.floor(dbl))3252 try std.fmt.allocPrint(c.arena, "{d}.0", .{dbl})
3199 try Tag.integer_literal.create(c.arena, str)
3200 else3253 else
3201 try Tag.float_literal.create(c.arena, str);3254 try std.fmt.allocPrint(c.arena, "{d}", .{dbl});
3255 var node = try Tag.float_literal.create(c.arena, str);
3202 if (is_negative) node = try Tag.negate.create(c.arena, node);3256 if (is_negative) node = try Tag.negate.create(c.arena, node);
3203 return maybeSuppressResult(c, scope, used, node);3257 return maybeSuppressResult(c, scope, used, node);
3204}3258}
...@@ -3312,9 +3366,8 @@ fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: Node) !void {...@@ -3312,9 +3366,8 @@ fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: Node) !void {
3312 try c.global_scope.nodes.append(decl_node);3366 try c.global_scope.nodes.append(decl_node);
3313}3367}
33143368
3315/// Translate a qual type for a variable with an initializer. The initializer3369/// Translate a qualtype for a variable with an initializer. This only matters
3316/// only matters for incomplete arrays, since the size of the array is determined3370/// for incomplete arrays, since the initializer determines the size of the array.
3317/// by the size of the initializer
3318fn transQualTypeInitialized(3371fn transQualTypeInitialized(
3319 c: *Context,3372 c: *Context,
3320 scope: *Scope,3373 scope: *Scope,
...@@ -3330,9 +3383,14 @@ fn transQualTypeInitialized(...@@ -3330,9 +3383,14 @@ fn transQualTypeInitialized(
3330 switch (decl_init.getStmtClass()) {3383 switch (decl_init.getStmtClass()) {
3331 .StringLiteralClass => {3384 .StringLiteralClass => {
3332 const string_lit = @ptrCast(*const clang.StringLiteral, decl_init);3385 const string_lit = @ptrCast(*const clang.StringLiteral, decl_init);
3333 const string_lit_size = string_lit.getLength() + 1; // +1 for null terminator3386 const string_lit_size = string_lit.getLength();
3334 const array_size = @intCast(usize, string_lit_size);3387 const array_size = @intCast(usize, string_lit_size);
3335 return Tag.array_type.create(c.arena, .{ .len = array_size, .elem_type = elem_ty });3388
3389 // incomplete array initialized with empty string, will be translated as [1]T{0}
3390 // see https://github.com/ziglang/zig/issues/8256
3391 if (array_size == 0) return Tag.array_type.create(c.arena, .{ .len = 1, .elem_type = elem_ty });
3392
3393 return Tag.null_sentinel_array_type.create(c.arena, .{ .len = array_size, .elem_type = elem_ty });
3336 },3394 },
3337 .InitListExprClass => {3395 .InitListExprClass => {
3338 const init_expr = @ptrCast(*const clang.InitListExpr, decl_init);3396 const init_expr = @ptrCast(*const clang.InitListExpr, decl_init);
...@@ -4746,6 +4804,10 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!N...@@ -4746,6 +4804,10 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!N
4746 },4804 },
4747 .Identifier => {4805 .Identifier => {
4748 const mangled_name = scope.getAlias(slice);4806 const mangled_name = scope.getAlias(slice);
4807 if (mem.startsWith(u8, mangled_name, "__builtin_") and !isBuiltinDefined(mangled_name)) {
4808 try m.fail(c, "TODO implement function '{s}' in std.c.builtins", .{mangled_name});
4809 return error.ParseError;
4810 }
4749 return Tag.identifier.create(c.arena, builtin_typedef_map.get(mangled_name) orelse mangled_name);4811 return Tag.identifier.create(c.arena, builtin_typedef_map.get(mangled_name) orelse mangled_name);
4750 },4812 },
4751 .LParen => {4813 .LParen => {
src/translate_c/ast.zig+83-3
...@@ -40,6 +40,8 @@ pub const Node = extern union {...@@ -40,6 +40,8 @@ pub const Node = extern union {
40 string_literal,40 string_literal,
41 char_literal,41 char_literal,
42 enum_literal,42 enum_literal,
43 /// "string"[0..end]
44 string_slice,
43 identifier,45 identifier,
44 @"if",46 @"if",
45 /// if (!operand) break;47 /// if (!operand) break;
...@@ -176,6 +178,7 @@ pub const Node = extern union {...@@ -176,6 +178,7 @@ pub const Node = extern union {
176 c_pointer,178 c_pointer,
177 single_pointer,179 single_pointer,
178 array_type,180 array_type,
181 null_sentinel_array_type,
179182
180 /// @import("std").meta.sizeof(operand)183 /// @import("std").meta.sizeof(operand)
181 std_meta_sizeof,184 std_meta_sizeof,
...@@ -334,7 +337,7 @@ pub const Node = extern union {...@@ -334,7 +337,7 @@ pub const Node = extern union {
334 .std_meta_promoteIntLiteral => Payload.PromoteIntLiteral,337 .std_meta_promoteIntLiteral => Payload.PromoteIntLiteral,
335 .block => Payload.Block,338 .block => Payload.Block,
336 .c_pointer, .single_pointer => Payload.Pointer,339 .c_pointer, .single_pointer => Payload.Pointer,
337 .array_type => Payload.Array,340 .array_type, .null_sentinel_array_type => Payload.Array,
338 .arg_redecl, .alias, .fail_decl => Payload.ArgRedecl,341 .arg_redecl, .alias, .fail_decl => Payload.ArgRedecl,
339 .log2_int_type => Payload.Log2IntType,342 .log2_int_type => Payload.Log2IntType,
340 .var_simple, .pub_var_simple => Payload.SimpleVarDecl,343 .var_simple, .pub_var_simple => Payload.SimpleVarDecl,
...@@ -342,6 +345,7 @@ pub const Node = extern union {...@@ -342,6 +345,7 @@ pub const Node = extern union {
342 .array_filler => Payload.ArrayFiller,345 .array_filler => Payload.ArrayFiller,
343 .pub_inline_fn => Payload.PubInlineFn,346 .pub_inline_fn => Payload.PubInlineFn,
344 .field_access => Payload.FieldAccess,347 .field_access => Payload.FieldAccess,
348 .string_slice => Payload.StringSlice,
345 };349 };
346 }350 }
347351
...@@ -584,10 +588,12 @@ pub const Payload = struct {...@@ -584,10 +588,12 @@ pub const Payload = struct {
584588
585 pub const Array = struct {589 pub const Array = struct {
586 base: Payload,590 base: Payload,
587 data: struct {591 data: ArrayTypeInfo,
592
593 pub const ArrayTypeInfo = struct {
588 elem_type: Node,594 elem_type: Node,
589 len: usize,595 len: usize,
590 },596 };
591 };597 };
592598
593 pub const Pointer = struct {599 pub const Pointer = struct {
...@@ -664,6 +670,14 @@ pub const Payload = struct {...@@ -664,6 +670,14 @@ pub const Payload = struct {
664 radix: Node,670 radix: Node,
665 },671 },
666 };672 };
673
674 pub const StringSlice = struct {
675 base: Payload,
676 data: struct {
677 string: Node,
678 end: usize,
679 },
680 };
667};681};
668682
669/// Converts the nodes into a Zig ast.683/// Converts the nodes into a Zig ast.
...@@ -1015,6 +1029,36 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {...@@ -1015,6 +1029,36 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
1015 .data = undefined,1029 .data = undefined,
1016 });1030 });
1017 },1031 },
1032 .string_slice => {
1033 const payload = node.castTag(.string_slice).?.data;
1034
1035 const string = try renderNode(c, payload.string);
1036 const l_bracket = try c.addToken(.l_bracket, "[");
1037 const start = try c.addNode(.{
1038 .tag = .integer_literal,
1039 .main_token = try c.addToken(.integer_literal, "0"),
1040 .data = undefined,
1041 });
1042 _ = try c.addToken(.ellipsis2, "..");
1043 const end = try c.addNode(.{
1044 .tag = .integer_literal,
1045 .main_token = try c.addTokenFmt(.integer_literal, "{d}", .{payload.end}),
1046 .data = undefined,
1047 });
1048 _ = try c.addToken(.r_bracket, "]");
1049
1050 return c.addNode(.{
1051 .tag = .slice,
1052 .main_token = l_bracket,
1053 .data = .{
1054 .lhs = string,
1055 .rhs = try c.addExtra(std.zig.ast.Node.Slice{
1056 .start = start,
1057 .end = end,
1058 }),
1059 },
1060 });
1061 },
1018 .fail_decl => {1062 .fail_decl => {
1019 const payload = node.castTag(.fail_decl).?.data;1063 const payload = node.castTag(.fail_decl).?.data;
1020 // pub const name = @compileError(msg);1064 // pub const name = @compileError(msg);
...@@ -1581,6 +1625,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {...@@ -1581,6 +1625,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
1581 const payload = node.castTag(.array_type).?.data;1625 const payload = node.castTag(.array_type).?.data;
1582 return renderArrayType(c, payload.len, payload.elem_type);1626 return renderArrayType(c, payload.len, payload.elem_type);
1583 },1627 },
1628 .null_sentinel_array_type => {
1629 const payload = node.castTag(.null_sentinel_array_type).?.data;
1630 return renderNullSentinelArrayType(c, payload.len, payload.elem_type);
1631 },
1584 .array_filler => {1632 .array_filler => {
1585 const payload = node.castTag(.array_filler).?.data;1633 const payload = node.castTag(.array_filler).?.data;
15861634
...@@ -1946,6 +1994,36 @@ fn renderArrayType(c: *Context, len: usize, elem_type: Node) !NodeIndex {...@@ -1946,6 +1994,36 @@ fn renderArrayType(c: *Context, len: usize, elem_type: Node) !NodeIndex {
1946 });1994 });
1947}1995}
19481996
1997fn renderNullSentinelArrayType(c: *Context, len: usize, elem_type: Node) !NodeIndex {
1998 const l_bracket = try c.addToken(.l_bracket, "[");
1999 const len_expr = try c.addNode(.{
2000 .tag = .integer_literal,
2001 .main_token = try c.addTokenFmt(.integer_literal, "{d}", .{len}),
2002 .data = undefined,
2003 });
2004 _ = try c.addToken(.colon, ":");
2005
2006 const sentinel_expr = try c.addNode(.{
2007 .tag = .integer_literal,
2008 .main_token = try c.addToken(.integer_literal, "0"),
2009 .data = undefined,
2010 });
2011
2012 _ = try c.addToken(.r_bracket, "]");
2013 const elem_type_expr = try renderNode(c, elem_type);
2014 return c.addNode(.{
2015 .tag = .array_type_sentinel,
2016 .main_token = l_bracket,
2017 .data = .{
2018 .lhs = len_expr,
2019 .rhs = try c.addExtra(std.zig.ast.Node.ArrayTypeSentinel {
2020 .sentinel = sentinel_expr,
2021 .elem_type = elem_type_expr,
2022 }),
2023 },
2024 });
2025}
2026
1949fn addSemicolonIfNeeded(c: *Context, node: Node) !void {2027fn addSemicolonIfNeeded(c: *Context, node: Node) !void {
1950 switch (node.tag()) {2028 switch (node.tag()) {
1951 .warning => unreachable,2029 .warning => unreachable,
...@@ -2014,6 +2092,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {...@@ -2014,6 +2092,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
2014 .integer_literal,2092 .integer_literal,
2015 .float_literal,2093 .float_literal,
2016 .string_literal,2094 .string_literal,
2095 .string_slice,
2017 .char_literal,2096 .char_literal,
2018 .enum_literal,2097 .enum_literal,
2019 .identifier,2098 .identifier,
...@@ -2035,6 +2114,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {...@@ -2035,6 +2114,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
2035 .func,2114 .func,
2036 .call,2115 .call,
2037 .array_type,2116 .array_type,
2117 .null_sentinel_array_type,
2038 .bool_to_int,2118 .bool_to_int,
2039 .div_exact,2119 .div_exact,
2040 .byte_offset_of,2120 .byte_offset_of,
src/zig_clang.cpp+5
...@@ -2445,6 +2445,11 @@ struct ZigClangQualType ZigClangFunctionType_getReturnType(const struct ZigClang...@@ -2445,6 +2445,11 @@ struct ZigClangQualType ZigClangFunctionType_getReturnType(const struct ZigClang
2445 return bitcast(casted->getReturnType());2445 return bitcast(casted->getReturnType());
2446}2446}
24472447
2448const struct ZigClangExpr *ZigClangGenericSelectionExpr_getResultExpr(const struct ZigClangGenericSelectionExpr *self) {
2449 auto casted = reinterpret_cast<const clang::GenericSelectionExpr *>(self);
2450 return reinterpret_cast<const struct ZigClangExpr *>(casted->getResultExpr());
2451}
2452
2448bool ZigClangFunctionProtoType_isVariadic(const struct ZigClangFunctionProtoType *self) {2453bool ZigClangFunctionProtoType_isVariadic(const struct ZigClangFunctionProtoType *self) {
2449 auto casted = reinterpret_cast<const clang::FunctionProtoType *>(self);2454 auto casted = reinterpret_cast<const clang::FunctionProtoType *>(self);
2450 return casted->isVariadic();2455 return casted->isVariadic();
src/zig_clang.h+2
...@@ -1116,6 +1116,8 @@ ZIG_EXTERN_C bool ZigClangFunctionType_getNoReturnAttr(const struct ZigClangFunc...@@ -1116,6 +1116,8 @@ ZIG_EXTERN_C bool ZigClangFunctionType_getNoReturnAttr(const struct ZigClangFunc
1116ZIG_EXTERN_C enum ZigClangCallingConv ZigClangFunctionType_getCallConv(const struct ZigClangFunctionType *self);1116ZIG_EXTERN_C enum ZigClangCallingConv ZigClangFunctionType_getCallConv(const struct ZigClangFunctionType *self);
1117ZIG_EXTERN_C struct ZigClangQualType ZigClangFunctionType_getReturnType(const struct ZigClangFunctionType *self);1117ZIG_EXTERN_C struct ZigClangQualType ZigClangFunctionType_getReturnType(const struct ZigClangFunctionType *self);
11181118
1119ZIG_EXTERN_C const struct ZigClangExpr *ZigClangGenericSelectionExpr_getResultExpr(const struct ZigClangGenericSelectionExpr *self);
1120
1119ZIG_EXTERN_C bool ZigClangFunctionProtoType_isVariadic(const struct ZigClangFunctionProtoType *self);1121ZIG_EXTERN_C bool ZigClangFunctionProtoType_isVariadic(const struct ZigClangFunctionProtoType *self);
1120ZIG_EXTERN_C unsigned ZigClangFunctionProtoType_getNumParams(const struct ZigClangFunctionProtoType *self);1122ZIG_EXTERN_C unsigned ZigClangFunctionProtoType_getNumParams(const struct ZigClangFunctionProtoType *self);
1121ZIG_EXTERN_C struct ZigClangQualType ZigClangFunctionProtoType_getParamType(const struct ZigClangFunctionProtoType *self, unsigned i);1123ZIG_EXTERN_C struct ZigClangQualType ZigClangFunctionProtoType_getParamType(const struct ZigClangFunctionProtoType *self, unsigned i);
test/cli.zig+11
...@@ -28,6 +28,8 @@ pub fn main() !void {...@@ -28,6 +28,8 @@ pub fn main() !void {
28 const zig_exe = try fs.path.resolve(a, &[_][]const u8{zig_exe_rel});28 const zig_exe = try fs.path.resolve(a, &[_][]const u8{zig_exe_rel});
2929
30 const dir_path = try fs.path.join(a, &[_][]const u8{ cache_root, "clitest" });30 const dir_path = try fs.path.join(a, &[_][]const u8{ cache_root, "clitest" });
31 defer fs.cwd().deleteTree(dir_path) catch {};
32
31 const TestFn = fn ([]const u8, []const u8) anyerror!void;33 const TestFn = fn ([]const u8, []const u8) anyerror!void;
32 const test_fns = [_]TestFn{34 const test_fns = [_]TestFn{
33 testZigInitLib,35 testZigInitLib,
...@@ -174,4 +176,13 @@ fn testZigFmt(zig_exe: []const u8, dir_path: []const u8) !void {...@@ -174,4 +176,13 @@ fn testZigFmt(zig_exe: []const u8, dir_path: []const u8) !void {
174 const run_result3 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path });176 const run_result3 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path });
175 // both files have been formatted, nothing should change now177 // both files have been formatted, nothing should change now
176 testing.expect(run_result3.stdout.len == 0);178 testing.expect(run_result3.stdout.len == 0);
179
180 // Check UTF-16 decoding
181 const fmt4_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt4.zig" });
182 var unformatted_code_utf16 = "\xff\xfe \x00 \x00 \x00 \x00/\x00/\x00 \x00n\x00o\x00 \x00r\x00e\x00a\x00s\x00o\x00n\x00";
183 try fs.cwd().writeFile(fmt4_zig_path, unformatted_code_utf16);
184
185 const run_result4 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path });
186 testing.expect(std.mem.startsWith(u8, run_result4.stdout, fmt4_zig_path));
187 testing.expect(run_result4.stdout.len == fmt4_zig_path.len + 1 and run_result4.stdout[run_result4.stdout.len - 1] == '\n');
177}188}
test/run_translated_c.zig+57
...@@ -3,6 +3,17 @@ const tests = @import("tests.zig");...@@ -3,6 +3,17 @@ const tests = @import("tests.zig");
3const nl = std.cstr.line_sep;3const nl = std.cstr.line_sep;
44
5pub fn addCases(cases: *tests.RunTranslatedCContext) void {5pub fn addCases(cases: *tests.RunTranslatedCContext) void {
6 cases.add("division of floating literals",
7 \\#define _NO_CRT_STDIO_INLINE 1
8 \\#include <stdio.h>
9 \\#define PI 3.14159265358979323846f
10 \\#define DEG2RAD (PI/180.0f)
11 \\int main(void) {
12 \\ printf("DEG2RAD is: %f\n", DEG2RAD);
13 \\ return 0;
14 \\}
15 , "DEG2RAD is: 0.017453" ++ nl);
16
6 cases.add("use global scope for record/enum/typedef type transalation if needed",17 cases.add("use global scope for record/enum/typedef type transalation if needed",
7 \\void bar(void);18 \\void bar(void);
8 \\void baz(void);19 \\void baz(void);
...@@ -1187,4 +1198,50 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {...@@ -1187,4 +1198,50 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
1187 \\ return 0;1198 \\ return 0;
1188 \\}1199 \\}
1189 , "");1200 , "");
1201
1202 cases.add("Generic selections",
1203 \\#include <stdlib.h>
1204 \\#include <string.h>
1205 \\#include <stdint.h>
1206 \\#define my_generic_fn(X) _Generic((X), \
1207 \\ int: abs, \
1208 \\ char *: strlen, \
1209 \\ size_t: malloc, \
1210 \\ default: free \
1211 \\)(X)
1212 \\#define my_generic_val(X) _Generic((X), \
1213 \\ int: 1, \
1214 \\ const char *: "bar" \
1215 \\)
1216 \\int main(void) {
1217 \\ if (my_generic_val(100) != 1) abort();
1218 \\
1219 \\ const char *foo = "foo";
1220 \\ const char *bar = my_generic_val(foo);
1221 \\ if (strcmp(bar, "bar") != 0) abort();
1222 \\
1223 \\ if (my_generic_fn(-42) != 42) abort();
1224 \\ if (my_generic_fn("hello") != 5) abort();
1225 \\
1226 \\ size_t size = 8192;
1227 \\ uint8_t *mem = my_generic_fn(size);
1228 \\ memset(mem, 42, size);
1229 \\ if (mem[size - 1] != 42) abort();
1230 \\ my_generic_fn(mem);
1231 \\
1232 \\ return 0;
1233 \\}
1234 , "");
1235
1236 // See __builtin_alloca_with_align comment in std.c.builtins
1237 cases.add("use of unimplemented builtin in unused function does not prevent compilation",
1238 \\#include <stdlib.h>
1239 \\void unused() {
1240 \\ __builtin_alloca_with_align(1, 8);
1241 \\}
1242 \\int main(void) {
1243 \\ if (__builtin_sqrt(1.0) != 1.0) abort();
1244 \\ return 0;
1245 \\}
1246 , "");
1190}1247}
test/stage2/cbe.zig+1-1
...@@ -51,7 +51,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -51,7 +51,7 @@ pub fn addCases(ctx: *TestContext) !void {
51 \\ _ = printf("Hello, %s!\n", "world");51 \\ _ = printf("Hello, %s!\n", "world");
52 \\ return 0;52 \\ return 0;
53 \\}53 \\}
54 , "Hello, world!\n");54 , "Hello, world!" ++ std.cstr.line_sep);
55 }55 }
5656
57 {57 {
test/standalone.zig+4-1
...@@ -9,7 +9,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void {...@@ -9,7 +9,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
9 cases.add("test/standalone/main_return_error/error_u8.zig");9 cases.add("test/standalone/main_return_error/error_u8.zig");
10 cases.add("test/standalone/main_return_error/error_u8_non_zero.zig");10 cases.add("test/standalone/main_return_error/error_u8_non_zero.zig");
11 cases.addBuildFile("test/standalone/main_pkg_path/build.zig");11 cases.addBuildFile("test/standalone/main_pkg_path/build.zig");
12 cases.addBuildFile("test/standalone/shared_library/build.zig");12 if (std.Target.current.os.tag != .macos) {
13 // TODO zld cannot link shared libraries yet.
14 cases.addBuildFile("test/standalone/shared_library/build.zig");
15 }
13 cases.addBuildFile("test/standalone/mix_o_files/build.zig");16 cases.addBuildFile("test/standalone/mix_o_files/build.zig");
14 cases.addBuildFile("test/standalone/global_linkage/build.zig");17 cases.addBuildFile("test/standalone/global_linkage/build.zig");
15 cases.addBuildFile("test/standalone/static_c_lib/build.zig");18 cases.addBuildFile("test/standalone/static_c_lib/build.zig");
test/translate_c.zig+62-38
...@@ -745,14 +745,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -745,14 +745,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
745 \\ static const char v2[] = "2.2.2";745 \\ static const char v2[] = "2.2.2";
746 \\}746 \\}
747 , &[_][]const u8{747 , &[_][]const u8{
748 \\const v2: [6]u8 = [6]u8{748 \\const v2: [5:0]u8 = "2.2.2".*;
749 \\ '2',
750 \\ '.',
751 \\ '2',
752 \\ '.',
753 \\ '2',
754 \\ 0,
755 \\};
756 \\pub export fn foo() void {}749 \\pub export fn foo() void {}
757 });750 });
758751
...@@ -1600,30 +1593,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1600,30 +1593,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1600 \\static char arr1[] = "hello";1593 \\static char arr1[] = "hello";
1601 \\char arr2[] = "hello";1594 \\char arr2[] = "hello";
1602 , &[_][]const u8{1595 , &[_][]const u8{
1603 \\pub export var arr0: [6]u8 = [6]u8{1596 \\pub export var arr0: [5:0]u8 = "hello".*;
1604 \\ 'h',1597 \\pub var arr1: [5:0]u8 = "hello".*;
1605 \\ 'e',1598 \\pub export var arr2: [5:0]u8 = "hello".*;
1606 \\ 'l',
1607 \\ 'l',
1608 \\ 'o',
1609 \\ 0,
1610 \\};
1611 \\pub var arr1: [6]u8 = [6]u8{
1612 \\ 'h',
1613 \\ 'e',
1614 \\ 'l',
1615 \\ 'l',
1616 \\ 'o',
1617 \\ 0,
1618 \\};
1619 \\pub export var arr2: [6]u8 = [6]u8{
1620 \\ 'h',
1621 \\ 'e',
1622 \\ 'l',
1623 \\ 'l',
1624 \\ 'o',
1625 \\ 0,
1626 \\};
1627 });1599 });
16281600
1629 cases.add("array initializer expr",1601 cases.add("array initializer expr",
...@@ -2456,7 +2428,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2456,7 +2428,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2456 \\ b: c_int,2428 \\ b: c_int,
2457 \\};2429 \\};
2458 \\pub extern var a: struct_Foo;2430 \\pub extern var a: struct_Foo;
2459 \\pub export var b: f32 = 2;2431 \\pub export var b: f32 = 2.0;
2460 \\pub export fn foo() void {2432 \\pub export fn foo() void {
2461 \\ var c: [*c]struct_Foo = undefined;2433 \\ var c: [*c]struct_Foo = undefined;
2462 \\ _ = a.b;2434 \\ _ = a.b;
...@@ -3020,17 +2992,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -3020,17 +2992,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
3020 \\pub extern fn fn_bool(x: bool) void;2992 \\pub extern fn fn_bool(x: bool) void;
3021 \\pub extern fn fn_ptr(x: ?*c_void) void;2993 \\pub extern fn fn_ptr(x: ?*c_void) void;
3022 \\pub export fn call() void {2994 \\pub export fn call() void {
3023 \\ fn_int(@floatToInt(c_int, 3));2995 \\ fn_int(@floatToInt(c_int, 3.0));
3024 \\ fn_int(@floatToInt(c_int, 3));2996 \\ fn_int(@floatToInt(c_int, 3.0));
3025 \\ fn_int(@floatToInt(c_int, 3));2997 \\ fn_int(@floatToInt(c_int, 3.0));
3026 \\ fn_int(@as(c_int, 1094861636));2998 \\ fn_int(@as(c_int, 1094861636));
3027 \\ fn_f32(@intToFloat(f32, @as(c_int, 3)));2999 \\ fn_f32(@intToFloat(f32, @as(c_int, 3)));
3028 \\ fn_f64(@intToFloat(f64, @as(c_int, 3)));3000 \\ fn_f64(@intToFloat(f64, @as(c_int, 3)));
3029 \\ fn_char(@bitCast(u8, @truncate(i8, @as(c_int, '3'))));3001 \\ fn_char(@bitCast(u8, @truncate(i8, @as(c_int, '3'))));
3030 \\ fn_char(@bitCast(u8, @truncate(i8, @as(c_int, '\x01'))));3002 \\ fn_char(@bitCast(u8, @truncate(i8, @as(c_int, '\x01'))));
3031 \\ fn_char(@bitCast(u8, @truncate(i8, @as(c_int, 0))));3003 \\ fn_char(@bitCast(u8, @truncate(i8, @as(c_int, 0))));
3032 \\ fn_f32(3);3004 \\ fn_f32(3.0);
3033 \\ fn_f64(3);3005 \\ fn_f64(3.0);
3034 \\ fn_bool(@as(c_int, 123) != 0);3006 \\ fn_bool(@as(c_int, 123) != 0);
3035 \\ fn_bool(@as(c_int, 0) != 0);3007 \\ fn_bool(@as(c_int, 0) != 0);
3036 \\ fn_bool(@ptrToInt(fn_int) != 0);3008 \\ fn_bool(@ptrToInt(fn_int) != 0);
...@@ -3418,4 +3390,56 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -3418,4 +3390,56 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
3418 \\pub const MAY_NEED_PROMOTION_HEX = @import("std").meta.promoteIntLiteral(c_int, 0x80000000, .hexadecimal);3390 \\pub const MAY_NEED_PROMOTION_HEX = @import("std").meta.promoteIntLiteral(c_int, 0x80000000, .hexadecimal);
3419 \\pub const MAY_NEED_PROMOTION_OCT = @import("std").meta.promoteIntLiteral(c_int, 0o20000000000, .octal);3391 \\pub const MAY_NEED_PROMOTION_OCT = @import("std").meta.promoteIntLiteral(c_int, 0o20000000000, .octal);
3420 });3392 });
3393
3394 // See __builtin_alloca_with_align comment in std.c.builtins
3395 cases.add("demote un-implemented builtins",
3396 \\#define FOO(X) __builtin_alloca_with_align((X), 8)
3397 , &[_][]const u8{
3398 \\pub const FOO = @compileError("TODO implement function '__builtin_alloca_with_align' in std.c.builtins");
3399 });
3400
3401 cases.add("null sentinel arrays when initialized from string literal. Issue #8256",
3402 \\#include <stdint.h>
3403 \\char zero[0] = "abc";
3404 \\uint32_t zero_w[0] = U"💯💯💯";
3405 \\char empty_incomplete[] = "";
3406 \\uint32_t empty_incomplete_w[] = U"";
3407 \\char empty_constant[100] = "";
3408 \\uint32_t empty_constant_w[100] = U"";
3409 \\char incomplete[] = "abc";
3410 \\uint32_t incomplete_w[] = U"💯💯💯";
3411 \\char truncated[1] = "abc";
3412 \\uint32_t truncated_w[1] = U"💯💯💯";
3413 \\char extend[5] = "a";
3414 \\uint32_t extend_w[5] = U"💯";
3415 \\char no_null[3] = "abc";
3416 \\uint32_t no_null_w[3] = U"💯💯💯";
3417 , &[_][]const u8{
3418 \\pub export var zero: [0]u8 = [0]u8{};
3419 \\pub export var zero_w: [0]u32 = [0]u32{};
3420 \\pub export var empty_incomplete: [1]u8 = [1]u8{0} ** 1;
3421 \\pub export var empty_incomplete_w: [1]u32 = [1]u32{0} ** 1;
3422 \\pub export var empty_constant: [100]u8 = [1]u8{0} ** 100;
3423 \\pub export var empty_constant_w: [100]u32 = [1]u32{0} ** 100;
3424 \\pub export var incomplete: [3:0]u8 = "abc".*;
3425 \\pub export var incomplete_w: [3:0]u32 = [3:0]u32{
3426 \\ '\u{1f4af}',
3427 \\ '\u{1f4af}',
3428 \\ '\u{1f4af}',
3429 \\};
3430 \\pub export var truncated: [1]u8 = "abc"[0..1].*;
3431 \\pub export var truncated_w: [1]u32 = [1]u32{
3432 \\ '\u{1f4af}',
3433 \\};
3434 \\pub export var extend: [5]u8 = "a"[0..1].* ++ [1]u8{0} ** 4;
3435 \\pub export var extend_w: [5]u32 = [1]u32{
3436 \\ '\u{1f4af}',
3437 \\} ++ [1]u32{0} ** 4;
3438 \\pub export var no_null: [3]u8 = "abc".*;
3439 \\pub export var no_null_w: [3]u32 = [3]u32{
3440 \\ '\u{1f4af}',
3441 \\ '\u{1f4af}',
3442 \\ '\u{1f4af}',
3443 \\};
3444 });
3421}3445}