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
564564 "${CMAKE_SOURCE_DIR}/src/link/Coff.zig"
565565 "${CMAKE_SOURCE_DIR}/src/link/Elf.zig"
566566 "${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"
567571 "${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"
568575 "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"
569576 "${CMAKE_SOURCE_DIR}/src/link/C/zig.h"
570577 "${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 {
176176 /// The default options (.{}) will iterate indices of set bits in
177177 /// ascending order. Modifications to the underlying bit set may
178178 /// 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) {
180180 return .{
181181 .bits_remain = switch (options.kind) {
182182 .set => self.mask,
......@@ -185,7 +185,11 @@ pub fn IntegerBitSet(comptime size: u16) type {
185185 };
186186 }
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 {
189193 return struct {
190194 const IterSelf = @This();
191195 // all bits which have not yet been iterated over
......@@ -425,8 +429,12 @@ pub fn ArrayBitSet(comptime MaskIntType: type, comptime size: usize) type {
425429 /// The default options (.{}) will iterate indices of set bits in
426430 /// ascending order. Modifications to the underlying bit set may
427431 /// or may not be observed by the iterator.
428 pub fn iterator(self: *const Self, comptime options: IteratorOptions) BitSetIterator(MaskInt, options) {
429 return BitSetIterator(MaskInt, options).init(&self.masks, last_item_mask);
432 pub fn iterator(self: *const Self, comptime options: IteratorOptions) Iterator(options) {
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);
430438 }
431439
432440 fn maskBit(index: usize) MaskInt {
......@@ -700,11 +708,15 @@ pub const DynamicBitSetUnmanaged = struct {
700708 /// ascending order. Modifications to the underlying bit set may
701709 /// or may not be observed by the iterator. Resizing the underlying
702710 /// 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) {
704712 const num_masks = numMasks(self.bit_length);
705713 const padding_bits = num_masks * @bitSizeOf(MaskInt) - self.bit_length;
706714 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);
708720 }
709721
710722 fn maskBit(index: usize) MaskInt {
......@@ -858,9 +870,11 @@ pub const DynamicBitSet = struct {
858870 /// ascending order. Modifications to the underlying bit set may
859871 /// or may not be observed by the iterator. Resizing the underlying
860872 /// 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) {
862874 return self.unmanaged.iterator(options);
863875 }
876
877 pub const Iterator = DynamicBitSetUnmanaged.Iterator;
864878};
865879
866880/// 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
140140 // If it is not possible to determine which objects ptr points to at compile time,
141141 // __builtin_object_size should return (size_t) -1 for type 0 or 1 and (size_t) 0
142142 // 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));
144144 if (ty == 2 or ty == 3) return 0;
145145 unreachable;
146146}
......@@ -188,3 +188,9 @@ pub fn __builtin_memcpy(
188188pub fn __builtin_expect(expr: c_long, c: c_long) callconv(.Inline) c_long {
189189 return expr;
190190}
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 {
2424 pub const Gimli = @import("crypto/gimli.zig").Aead;
2525
2626 pub const chacha_poly = struct {
27 pub const ChaCha20Poly1305 = @import("crypto/chacha20.zig").Chacha20Poly1305;
28 pub const XChaCha20Poly1305 = @import("crypto/chacha20.zig").XChacha20Poly1305;
27 pub const ChaCha20Poly1305 = @import("crypto/chacha20.zig").ChaCha20Poly1305;
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;
2933 };
3034
3135 pub const isap = @import("crypto/isap.zig");
......@@ -119,8 +123,14 @@ pub const sign = struct {
119123pub const stream = struct {
120124 pub const chacha = struct {
121125 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;
122128 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;
123131 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;
124134 };
125135
126136 pub const salsa = struct {
lib/std/crypto/benchmark.zig+1
......@@ -202,6 +202,7 @@ pub fn benchmarkBatchSignatureVerification(comptime Signature: anytype, comptime
202202const aeads = [_]Crypto{
203203 Crypto{ .ty = crypto.aead.chacha_poly.ChaCha20Poly1305, .name = "chacha20Poly1305" },
204204 Crypto{ .ty = crypto.aead.chacha_poly.XChaCha20Poly1305, .name = "xchacha20Poly1305" },
205 Crypto{ .ty = crypto.aead.chacha_poly.XChaCha8Poly1305, .name = "xchacha8Poly1305" },
205206 Crypto{ .ty = crypto.aead.salsa_poly.XSalsa20Poly1305, .name = "xsalsa20Poly1305" },
206207 Crypto{ .ty = crypto.aead.Gimli, .name = "gimli-aead" },
207208 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;
1515const Poly1305 = std.crypto.onetimeauth.Poly1305;
1616const 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
1883// Vectorized implementation of the core function
19const ChaCha20VecImpl = struct {
20 const Lane = Vector(4, u32);
21 const BlockVec = [4]Lane;
22
23 fn initContext(key: [8]u32, d: [4]u32) BlockVec {
24 const c = "expand 32-byte k";
25 const constant_le = comptime Lane{
26 mem.readIntLittle(u32, c[0..4]),
27 mem.readIntLittle(u32, c[4..8]),
28 mem.readIntLittle(u32, c[8..12]),
29 mem.readIntLittle(u32, c[12..16]),
30 };
31 return BlockVec{
32 constant_le,
33 Lane{ key[0], key[1], key[2], key[3] },
34 Lane{ key[4], key[5], key[6], key[7] },
35 Lane{ d[0], d[1], d[2], d[3] },
36 };
37 }
84fn ChaChaVecImpl(comptime rounds_nb: usize) type {
85 return struct {
86 const Lane = Vector(4, u32);
87 const BlockVec = [4]Lane;
88
89 fn initContext(key: [8]u32, d: [4]u32) BlockVec {
90 const c = "expand 32-byte k";
91 const constant_le = comptime Lane{
92 mem.readIntLittle(u32, c[0..4]),
93 mem.readIntLittle(u32, c[4..8]),
94 mem.readIntLittle(u32, c[8..12]),
95 mem.readIntLittle(u32, c[12..16]),
96 };
97 return BlockVec{
98 constant_le,
99 Lane{ key[0], key[1], key[2], key[3] },
100 Lane{ key[4], key[5], key[6], key[7] },
101 Lane{ d[0], d[1], d[2], d[3] },
102 };
103 }
38104
39 fn chacha20Core(x: *BlockVec, input: BlockVec) callconv(.Inline) void {
40 x.* = input;
41
42 var r: usize = 0;
43 while (r < 20) : (r += 2) {
44 x[0] +%= x[1];
45 x[3] ^= x[0];
46 x[3] = math.rotl(Lane, x[3], 16);
47
48 x[2] +%= x[3];
49 x[1] ^= x[2];
50 x[1] = math.rotl(Lane, x[1], 12);
51
52 x[0] +%= x[1];
53 x[3] ^= x[0];
54 x[0] = @shuffle(u32, x[0], undefined, [_]i32{ 3, 0, 1, 2 });
55 x[3] = math.rotl(Lane, x[3], 8);
56
57 x[2] +%= x[3];
58 x[3] = @shuffle(u32, x[3], undefined, [_]i32{ 2, 3, 0, 1 });
59 x[1] ^= x[2];
60 x[2] = @shuffle(u32, x[2], undefined, [_]i32{ 1, 2, 3, 0 });
61 x[1] = math.rotl(Lane, x[1], 7);
62
63 x[0] +%= x[1];
64 x[3] ^= x[0];
65 x[3] = math.rotl(Lane, x[3], 16);
66
67 x[2] +%= x[3];
68 x[1] ^= x[2];
69 x[1] = math.rotl(Lane, x[1], 12);
70
71 x[0] +%= x[1];
72 x[3] ^= x[0];
73 x[0] = @shuffle(u32, x[0], undefined, [_]i32{ 1, 2, 3, 0 });
74 x[3] = math.rotl(Lane, x[3], 8);
75
76 x[2] +%= x[3];
77 x[3] = @shuffle(u32, x[3], undefined, [_]i32{ 2, 3, 0, 1 });
78 x[1] ^= x[2];
79 x[2] = @shuffle(u32, x[2], undefined, [_]i32{ 3, 0, 1, 2 });
80 x[1] = math.rotl(Lane, x[1], 7);
105 fn chacha20Core(x: *BlockVec, input: BlockVec) callconv(.Inline) void {
106 x.* = input;
107
108 var r: usize = 0;
109 while (r < rounds_nb) : (r += 2) {
110 x[0] +%= x[1];
111 x[3] ^= x[0];
112 x[3] = math.rotl(Lane, x[3], 16);
113
114 x[2] +%= x[3];
115 x[1] ^= x[2];
116 x[1] = math.rotl(Lane, x[1], 12);
117
118 x[0] +%= x[1];
119 x[3] ^= x[0];
120 x[0] = @shuffle(u32, x[0], undefined, [_]i32{ 3, 0, 1, 2 });
121 x[3] = math.rotl(Lane, x[3], 8);
122
123 x[2] +%= x[3];
124 x[3] = @shuffle(u32, x[3], undefined, [_]i32{ 2, 3, 0, 1 });
125 x[1] ^= x[2];
126 x[2] = @shuffle(u32, x[2], undefined, [_]i32{ 1, 2, 3, 0 });
127 x[1] = math.rotl(Lane, x[1], 7);
128
129 x[0] +%= x[1];
130 x[3] ^= x[0];
131 x[3] = math.rotl(Lane, x[3], 16);
132
133 x[2] +%= x[3];
134 x[1] ^= x[2];
135 x[1] = math.rotl(Lane, x[1], 12);
136
137 x[0] +%= x[1];
138 x[3] ^= x[0];
139 x[0] = @shuffle(u32, x[0], undefined, [_]i32{ 1, 2, 3, 0 });
140 x[3] = math.rotl(Lane, x[3], 8);
141
142 x[2] +%= x[3];
143 x[3] = @shuffle(u32, x[3], undefined, [_]i32{ 2, 3, 0, 1 });
144 x[1] ^= x[2];
145 x[2] = @shuffle(u32, x[2], undefined, [_]i32{ 3, 0, 1, 2 });
146 x[1] = math.rotl(Lane, x[1], 7);
147 }
81148 }
82 }
83149
84 fn hashToBytes(out: *[64]u8, x: BlockVec) callconv(.Inline) void {
85 var i: usize = 0;
86 while (i < 4) : (i += 1) {
87 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]);
89 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]);
150 fn hashToBytes(out: *[64]u8, x: BlockVec) callconv(.Inline) void {
151 var i: usize = 0;
152 while (i < 4) : (i += 1) {
153 mem.writeIntLittle(u32, out[16 * i + 0 ..][0..4], x[i][0]);
154 mem.writeIntLittle(u32, out[16 * i + 4 ..][0..4], x[i][1]);
155 mem.writeIntLittle(u32, out[16 * i + 8 ..][0..4], x[i][2]);
156 mem.writeIntLittle(u32, out[16 * i + 12 ..][0..4], x[i][3]);
157 }
91158 }
92 }
93159
94 fn contextFeedback(x: *BlockVec, ctx: BlockVec) callconv(.Inline) void {
95 x[0] +%= ctx[0];
96 x[1] +%= ctx[1];
97 x[2] +%= ctx[2];
98 x[3] +%= ctx[3];
99 }
160 fn contextFeedback(x: *BlockVec, ctx: BlockVec) callconv(.Inline) void {
161 x[0] +%= ctx[0];
162 x[1] +%= ctx[1];
163 x[2] +%= ctx[2];
164 x[3] +%= ctx[3];
165 }
100166
101 fn chacha20Xor(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) void {
102 var ctx = initContext(key, counter);
103 var x: BlockVec = undefined;
104 var buf: [64]u8 = undefined;
105 var i: usize = 0;
106 while (i + 64 <= in.len) : (i += 64) {
107 chacha20Core(x[0..], ctx);
108 contextFeedback(&x, ctx);
109 hashToBytes(buf[0..], x);
110
111 var xout = out[i..];
112 const xin = in[i..];
113 var j: usize = 0;
114 while (j < 64) : (j += 1) {
115 xout[j] = xin[j];
116 }
117 j = 0;
118 while (j < 64) : (j += 1) {
119 xout[j] ^= buf[j];
167 fn chacha20Xor(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) void {
168 var ctx = initContext(key, counter);
169 var x: BlockVec = undefined;
170 var buf: [64]u8 = undefined;
171 var i: usize = 0;
172 while (i + 64 <= in.len) : (i += 64) {
173 chacha20Core(x[0..], ctx);
174 contextFeedback(&x, ctx);
175 hashToBytes(buf[0..], x);
176
177 var xout = out[i..];
178 const xin = in[i..];
179 var j: usize = 0;
180 while (j < 64) : (j += 1) {
181 xout[j] = xin[j];
182 }
183 j = 0;
184 while (j < 64) : (j += 1) {
185 xout[j] ^= buf[j];
186 }
187 ctx[3][0] += 1;
120188 }
121 ctx[3][0] += 1;
122 }
123 if (i < in.len) {
124 chacha20Core(x[0..], ctx);
125 contextFeedback(&x, ctx);
126 hashToBytes(buf[0..], x);
127
128 var xout = out[i..];
129 const xin = in[i..];
130 var j: usize = 0;
131 while (j < in.len % 64) : (j += 1) {
132 xout[j] = xin[j] ^ buf[j];
189 if (i < in.len) {
190 chacha20Core(x[0..], ctx);
191 contextFeedback(&x, ctx);
192 hashToBytes(buf[0..], x);
193
194 var xout = out[i..];
195 const xin = in[i..];
196 var j: usize = 0;
197 while (j < in.len % 64) : (j += 1) {
198 xout[j] = xin[j] ^ buf[j];
199 }
133200 }
134201 }
135 }
136202
137 fn hchacha20(input: [16]u8, key: [32]u8) [32]u8 {
138 var c: [4]u32 = undefined;
139 for (c) |_, i| {
140 c[i] = mem.readIntLittle(u32, input[4 * i ..][0..4]);
203 fn hchacha20(input: [16]u8, key: [32]u8) [32]u8 {
204 var c: [4]u32 = undefined;
205 for (c) |_, i| {
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;
141221 }
142 const ctx = initContext(keyToWords(key), c);
143 var x: BlockVec = undefined;
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};
222 };
223}
157224
158225// Non-vectorized implementation of the core function
159const ChaCha20NonVecImpl = struct {
160 const BlockVec = [16]u32;
161
162 fn initContext(key: [8]u32, d: [4]u32) BlockVec {
163 const c = "expand 32-byte k";
164 const constant_le = comptime [4]u32{
165 mem.readIntLittle(u32, c[0..4]),
166 mem.readIntLittle(u32, c[4..8]),
167 mem.readIntLittle(u32, c[8..12]),
168 mem.readIntLittle(u32, c[12..16]),
169 };
170 return BlockVec{
171 constant_le[0], constant_le[1], constant_le[2], constant_le[3],
172 key[0], key[1], key[2], key[3],
173 key[4], key[5], key[6], key[7],
174 d[0], d[1], d[2], d[3],
175 };
176 }
177
178 const QuarterRound = struct {
179 a: usize,
180 b: usize,
181 c: usize,
182 d: usize,
183 };
226fn ChaChaNonVecImpl(comptime rounds_nb: usize) type {
227 return struct {
228 const BlockVec = [16]u32;
229
230 fn initContext(key: [8]u32, d: [4]u32) BlockVec {
231 const c = "expand 32-byte k";
232 const constant_le = comptime [4]u32{
233 mem.readIntLittle(u32, c[0..4]),
234 mem.readIntLittle(u32, c[4..8]),
235 mem.readIntLittle(u32, c[8..12]),
236 mem.readIntLittle(u32, c[12..16]),
237 };
238 return BlockVec{
239 constant_le[0], constant_le[1], constant_le[2], constant_le[3],
240 key[0], key[1], key[2], key[3],
241 key[4], key[5], key[6], key[7],
242 d[0], d[1], d[2], d[3],
243 };
244 }
184245
185 fn Rp(a: usize, b: usize, c: usize, d: usize) QuarterRound {
186 return QuarterRound{
187 .a = a,
188 .b = b,
189 .c = c,
190 .d = d,
246 const QuarterRound = struct {
247 a: usize,
248 b: usize,
249 c: usize,
250 d: usize,
191251 };
192 }
193252
194 fn chacha20Core(x: *BlockVec, input: BlockVec) callconv(.Inline) void {
195 x.* = input;
196
197 const rounds = comptime [_]QuarterRound{
198 Rp(0, 4, 8, 12),
199 Rp(1, 5, 9, 13),
200 Rp(2, 6, 10, 14),
201 Rp(3, 7, 11, 15),
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 };
253 fn Rp(a: usize, b: usize, c: usize, d: usize) QuarterRound {
254 return QuarterRound{
255 .a = a,
256 .b = b,
257 .c = c,
258 .d = d,
259 };
260 }
207261
208 comptime var j: usize = 0;
209 inline while (j < 20) : (j += 2) {
210 inline for (rounds) |r| {
211 x[r.a] +%= x[r.b];
212 x[r.d] = math.rotl(u32, x[r.d] ^ x[r.a], @as(u32, 16));
213 x[r.c] +%= x[r.d];
214 x[r.b] = math.rotl(u32, x[r.b] ^ x[r.c], @as(u32, 12));
215 x[r.a] +%= x[r.b];
216 x[r.d] = math.rotl(u32, x[r.d] ^ x[r.a], @as(u32, 8));
217 x[r.c] +%= x[r.d];
218 x[r.b] = math.rotl(u32, x[r.b] ^ x[r.c], @as(u32, 7));
262 fn chacha20Core(x: *BlockVec, input: BlockVec) callconv(.Inline) void {
263 x.* = input;
264
265 const rounds = comptime [_]QuarterRound{
266 Rp(0, 4, 8, 12),
267 Rp(1, 5, 9, 13),
268 Rp(2, 6, 10, 14),
269 Rp(3, 7, 11, 15),
270 Rp(0, 5, 10, 15),
271 Rp(1, 6, 11, 12),
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 }
219288 }
220289 }
221 }
222290
223 fn hashToBytes(out: *[64]u8, x: BlockVec) callconv(.Inline) void {
224 var i: usize = 0;
225 while (i < 4) : (i += 1) {
226 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]);
228 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]);
291 fn hashToBytes(out: *[64]u8, x: BlockVec) callconv(.Inline) void {
292 var i: usize = 0;
293 while (i < 4) : (i += 1) {
294 mem.writeIntLittle(u32, out[16 * i + 0 ..][0..4], x[i * 4 + 0]);
295 mem.writeIntLittle(u32, out[16 * i + 4 ..][0..4], x[i * 4 + 1]);
296 mem.writeIntLittle(u32, out[16 * i + 8 ..][0..4], x[i * 4 + 2]);
297 mem.writeIntLittle(u32, out[16 * i + 12 ..][0..4], x[i * 4 + 3]);
298 }
230299 }
231 }
232300
233 fn contextFeedback(x: *BlockVec, ctx: BlockVec) callconv(.Inline) void {
234 var i: usize = 0;
235 while (i < 16) : (i += 1) {
236 x[i] +%= ctx[i];
301 fn contextFeedback(x: *BlockVec, ctx: BlockVec) callconv(.Inline) void {
302 var i: usize = 0;
303 while (i < 16) : (i += 1) {
304 x[i] +%= ctx[i];
305 }
237306 }
238 }
239307
240 fn chacha20Xor(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) void {
241 var ctx = initContext(key, counter);
242 var x: BlockVec = undefined;
243 var buf: [64]u8 = undefined;
244 var i: usize = 0;
245 while (i + 64 <= in.len) : (i += 64) {
246 chacha20Core(x[0..], ctx);
247 contextFeedback(&x, ctx);
248 hashToBytes(buf[0..], x);
249
250 var xout = out[i..];
251 const xin = in[i..];
252 var j: usize = 0;
253 while (j < 64) : (j += 1) {
254 xout[j] = xin[j];
255 }
256 j = 0;
257 while (j < 64) : (j += 1) {
258 xout[j] ^= buf[j];
308 fn chacha20Xor(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) void {
309 var ctx = initContext(key, counter);
310 var x: BlockVec = undefined;
311 var buf: [64]u8 = undefined;
312 var i: usize = 0;
313 while (i + 64 <= in.len) : (i += 64) {
314 chacha20Core(x[0..], ctx);
315 contextFeedback(&x, ctx);
316 hashToBytes(buf[0..], x);
317
318 var xout = out[i..];
319 const xin = in[i..];
320 var j: usize = 0;
321 while (j < 64) : (j += 1) {
322 xout[j] = xin[j];
323 }
324 j = 0;
325 while (j < 64) : (j += 1) {
326 xout[j] ^= buf[j];
327 }
328 ctx[12] += 1;
259329 }
260 ctx[12] += 1;
261 }
262 if (i < in.len) {
263 chacha20Core(x[0..], ctx);
264 contextFeedback(&x, ctx);
265 hashToBytes(buf[0..], x);
266
267 var xout = out[i..];
268 const xin = in[i..];
269 var j: usize = 0;
270 while (j < in.len % 64) : (j += 1) {
271 xout[j] = xin[j] ^ buf[j];
330 if (i < in.len) {
331 chacha20Core(x[0..], ctx);
332 contextFeedback(&x, ctx);
333 hashToBytes(buf[0..], x);
334
335 var xout = out[i..];
336 const xin = in[i..];
337 var j: usize = 0;
338 while (j < in.len % 64) : (j += 1) {
339 xout[j] = xin[j] ^ buf[j];
340 }
272341 }
273342 }
274 }
275343
276 fn hchacha20(input: [16]u8, key: [32]u8) [32]u8 {
277 var c: [4]u32 = undefined;
278 for (c) |_, i| {
279 c[i] = mem.readIntLittle(u32, input[4 * i ..][0..4]);
344 fn hchacha20(input: [16]u8, key: [32]u8) [32]u8 {
345 var c: [4]u32 = undefined;
346 for (c) |_, i| {
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;
280362 }
281 const ctx = initContext(keyToWords(key), c);
282 var x: BlockVec = undefined;
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};
363 };
364}
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
299370fn keyToWords(key: [32]u8) [8]u32 {
300371 var k: [8]u32 = undefined;
......@@ -305,68 +376,239 @@ fn keyToWords(key: [32]u8) [8]u32 {
305376 return k;
306377}
307378
308/// ChaCha20 avoids the possibility of timing attacks, as there are no branches
309/// on secret key data.
310///
311/// in and out should be the same length.
312/// counter should generally be 0 or 1
313///
314/// ChaCha20 is self-reversing. To decrypt just run the cipher with the same
315/// counter, nonce, and key.
316pub const ChaCha20IETF = struct {
317 pub fn xor(out: []u8, in: []const u8, counter: u32, key: [32]u8, nonce: [12]u8) void {
318 assert(in.len == out.len);
319 assert((in.len >> 6) + counter <= maxInt(u32));
320
321 var c: [4]u32 = undefined;
322 c[0] = counter;
323 c[1] = mem.readIntLittle(u32, nonce[0..4]);
324 c[2] = mem.readIntLittle(u32, nonce[4..8]);
325 c[3] = mem.readIntLittle(u32, nonce[8..12]);
326 ChaCha20Impl.chacha20Xor(out, in, keyToWords(key), c);
327 }
328};
329
330/// This is the original ChaCha20 before RFC 7539, which recommends using the
331/// orgininal version on applications such as disk or file encryption that might
332/// exceed the 256 GiB limit of the 96-bit nonce version.
333pub const ChaCha20With64BitNonce = struct {
334 pub fn xor(out: []u8, in: []const u8, counter: u64, key: [32]u8, nonce: [8]u8) void {
335 assert(in.len == out.len);
336 assert(counter +% (in.len >> 6) >= counter);
337
338 var cursor: usize = 0;
339 const k = keyToWords(key);
340 var c: [4]u32 = undefined;
341 c[0] = @truncate(u32, counter);
342 c[1] = @truncate(u32, counter >> 32);
343 c[2] = mem.readIntLittle(u32, nonce[0..4]);
344 c[3] = mem.readIntLittle(u32, nonce[4..8]);
345
346 const block_length = (1 << 6);
347 // The full block size is greater than the address space on a 32bit machine
348 const big_block = if (@sizeOf(usize) > 4) (block_length << 32) else maxInt(usize);
349
350 // first partial big block
351 if (((@intCast(u64, maxInt(u32) - @truncate(u32, counter)) + 1) << 6) < in.len) {
352 ChaCha20Impl.chacha20Xor(out[cursor..big_block], in[cursor..big_block], k, c);
353 cursor = big_block - cursor;
354 c[1] += 1;
355 if (comptime @sizeOf(usize) > 4) {
356 // A big block is giant: 256 GiB, but we can avoid this limitation
357 var remaining_blocks: u32 = @intCast(u32, (in.len / big_block));
358 var i: u32 = 0;
359 while (remaining_blocks > 0) : (remaining_blocks -= 1) {
360 ChaCha20Impl.chacha20Xor(out[cursor .. cursor + big_block], in[cursor .. cursor + big_block], k, c);
361 c[1] += 1; // upper 32-bit of counter, generic chacha20Xor() doesn't know about this.
362 cursor += big_block;
379fn extend(key: [32]u8, nonce: [24]u8, comptime rounds_nb: usize) struct { key: [32]u8, nonce: [12]u8 } {
380 var subnonce: [12]u8 = undefined;
381 mem.set(u8, subnonce[0..4], 0);
382 mem.copy(u8, subnonce[4..], nonce[16..24]);
383 return .{
384 .key = ChaChaImpl(rounds_nb).hchacha20(nonce[0..16].*, key),
385 .nonce = subnonce,
386 };
387}
388
389fn ChaChaIETF(comptime rounds_nb: usize) type {
390 return struct {
391 /// Nonce length in bytes.
392 pub const nonce_length = 12;
393 /// Key length in bytes.
394 pub const key_length = 32;
395
396 /// Add the output of the ChaCha20 stream cipher to `in` and stores the result into `out`.
397 /// WARNING: This function doesn't provide authenticated encryption.
398 /// Using the AEAD or one of the `box` versions is usually preferred.
399 pub fn xor(out: []u8, in: []const u8, counter: u32, key: [key_length]u8, nonce: [nonce_length]u8) void {
400 assert(in.len == out.len);
401 assert(in.len / 64 <= (1 << 32 - 1) - counter);
402
403 var d: [4]u32 = undefined;
404 d[0] = counter;
405 d[1] = mem.readIntLittle(u32, nonce[0..4]);
406 d[2] = mem.readIntLittle(u32, nonce[4..8]);
407 d[3] = mem.readIntLittle(u32, nonce[8..12]);
408 ChaChaImpl(rounds_nb).chacha20Xor(out, in, keyToWords(key), d);
409 }
410 };
411}
412
413fn ChaChaWith64BitNonce(comptime rounds_nb: usize) type {
414 return struct {
415 /// Nonce length in bytes.
416 pub const nonce_length = 8;
417 /// Key length in bytes.
418 pub const key_length = 32;
419
420 /// Add the output of the ChaCha20 stream cipher to `in` and stores the result into `out`.
421 /// WARNING: This function doesn't provide authenticated encryption.
422 /// Using the AEAD or one of the `box` versions is usually preferred.
423 pub fn xor(out: []u8, in: []const u8, counter: u64, key: [key_length]u8, nonce: [nonce_length]u8) void {
424 assert(in.len == out.len);
425 assert(in.len / 64 <= (1 << 64 - 1) - counter);
426
427 var cursor: usize = 0;
428 const k = keyToWords(key);
429 var c: [4]u32 = undefined;
430 c[0] = @truncate(u32, counter);
431 c[1] = @truncate(u32, counter >> 32);
432 c[2] = mem.readIntLittle(u32, nonce[0..4]);
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 }
363453 }
364454 }
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);
365578 }
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));
368610 }
369};
611}
370612
371613// https://tools.ietf.org/html/rfc7539#section-2.4.2
372614test "crypto.chacha20 test vector sunscreen" {
......@@ -387,7 +629,7 @@ test "crypto.chacha20 test vector sunscreen" {
387629 0xb4, 0x0b, 0x8e, 0xed, 0xf2, 0x78, 0x5e, 0x42,
388630 0x87, 0x4d,
389631 };
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.";
391633 var result: [114]u8 = undefined;
392634 const key = [_]u8{
393635 0, 1, 2, 3, 4, 5, 6, 7,
......@@ -401,13 +643,12 @@ test "crypto.chacha20 test vector sunscreen" {
401643 0, 0, 0, 0,
402644 };
403645
404 ChaCha20IETF.xor(result[0..], input[0..], 1, key, nonce);
646 ChaCha20IETF.xor(result[0..], m[0..], 1, key, nonce);
405647 testing.expectEqualSlices(u8, &expected_result, &result);
406648
407 // Chacha20 is self-reversing.
408 var plaintext: [114]u8 = undefined;
409 ChaCha20IETF.xor(plaintext[0..], result[0..], 1, key, nonce);
410 testing.expect(mem.order(u8, input, &plaintext) == .eq);
649 var m2: [114]u8 = undefined;
650 ChaCha20IETF.xor(m2[0..], result[0..], 1, key, nonce);
651 testing.expect(mem.order(u8, m, &m2) == .eq);
411652}
412653
413654// https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7
......@@ -422,7 +663,7 @@ test "crypto.chacha20 test vector 1" {
422663 0x6a, 0x43, 0xb8, 0xf4, 0x15, 0x18, 0xa1, 0x1c,
423664 0xc3, 0x87, 0xb6, 0x69, 0xb2, 0xee, 0x65, 0x86,
424665 };
425 const input = [_]u8{
666 const m = [_]u8{
426667 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
427668 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
428669 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
......@@ -441,7 +682,7 @@ test "crypto.chacha20 test vector 1" {
441682 };
442683 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);
445686 testing.expectEqualSlices(u8, &expected_result, &result);
446687}
447688
......@@ -456,7 +697,7 @@ test "crypto.chacha20 test vector 2" {
456697 0x53, 0xd7, 0x92, 0xb1, 0xc4, 0x3f, 0xea, 0x81,
457698 0x7e, 0x9a, 0xd2, 0x75, 0xae, 0x54, 0x69, 0x63,
458699 };
459 const input = [_]u8{
700 const m = [_]u8{
460701 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
461702 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
462703 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
......@@ -475,7 +716,7 @@ test "crypto.chacha20 test vector 2" {
475716 };
476717 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);
479720 testing.expectEqualSlices(u8, &expected_result, &result);
480721}
481722
......@@ -490,7 +731,7 @@ test "crypto.chacha20 test vector 3" {
490731 0x52, 0x77, 0x06, 0x2e, 0xb7, 0xa0, 0x43, 0x3e,
491732 0x44, 0x5f, 0x41, 0xe3,
492733 };
493 const input = [_]u8{
734 const m = [_]u8{
494735 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
495736 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
496737 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
......@@ -509,7 +750,7 @@ test "crypto.chacha20 test vector 3" {
509750 };
510751 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);
513754 testing.expectEqualSlices(u8, &expected_result, &result);
514755}
515756
......@@ -524,7 +765,7 @@ test "crypto.chacha20 test vector 4" {
524765 0x5d, 0xdc, 0x49, 0x7a, 0x0b, 0x46, 0x6e, 0x7d,
525766 0x6b, 0xbd, 0xb0, 0x04, 0x1b, 0x2f, 0x58, 0x6b,
526767 };
527 const input = [_]u8{
768 const m = [_]u8{
528769 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
529770 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
530771 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
......@@ -543,7 +784,7 @@ test "crypto.chacha20 test vector 4" {
543784 };
544785 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);
547788 testing.expectEqualSlices(u8, &expected_result, &result);
548789}
549790
......@@ -585,7 +826,7 @@ test "crypto.chacha20 test vector 5" {
585826 0x87, 0x46, 0xd4, 0x52, 0x4d, 0x38, 0x40, 0x7a,
586827 0x6d, 0xeb, 0x3a, 0xb7, 0x8f, 0xab, 0x78, 0xc9,
587828 };
588 const input = [_]u8{
829 const m = [_]u8{
589830 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
590831 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
591832 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" {
615856 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
616857 };
617858
618 ChaCha20With64BitNonce.xor(result[0..], input[0..], 0, key, nonce);
859 ChaCha20With64BitNonce.xor(result[0..], m[0..], 0, key, nonce);
619860 testing.expectEqualSlices(u8, &expected_result, &result);
620861}
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
755863test "seal" {
756864 {
757 const plaintext = "";
758 const data = "";
865 const m = "";
866 const ad = "";
759867 const key = [_]u8{
760868 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
761869 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
......@@ -764,11 +872,11 @@ test "seal" {
764872 const exp_out = [_]u8{ 0xa0, 0x78, 0x4d, 0x7a, 0x47, 0x16, 0xf3, 0xfe, 0xb4, 0xf6, 0x4e, 0x7f, 0x4b, 0x39, 0xbf, 0x4 };
765873
766874 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);
768876 testing.expectEqualSlices(u8, exp_out[0..], out[0..]);
769877 }
770878 {
771 const plaintext = [_]u8{
879 const m = [_]u8{
772880 0x4c, 0x61, 0x64, 0x69, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x47, 0x65, 0x6e, 0x74, 0x6c,
773881 0x65, 0x6d, 0x65, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x61, 0x73,
774882 0x73, 0x20, 0x6f, 0x66, 0x20, 0x27, 0x39, 0x39, 0x3a, 0x20, 0x49, 0x66, 0x20, 0x49, 0x20, 0x63,
......@@ -778,7 +886,7 @@ test "seal" {
778886 0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69,
779887 0x74, 0x2e,
780888 };
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 };
782890 const key = [_]u8{
783891 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
784892 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
......@@ -797,15 +905,15 @@ test "seal" {
797905 };
798906
799907 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);
801909 testing.expectEqualSlices(u8, exp_out[0..], out[0..]);
802910 }
803911}
804912
805913test "open" {
806914 {
807 const ciphertext = [_]u8{ 0xa0, 0x78, 0x4d, 0x7a, 0x47, 0x16, 0xf3, 0xfe, 0xb4, 0xf6, 0x4e, 0x7f, 0x4b, 0x39, 0xbf, 0x4 };
808 const data = "";
915 const c = [_]u8{ 0xa0, 0x78, 0x4d, 0x7a, 0x47, 0x16, 0xf3, 0xfe, 0xb4, 0xf6, 0x4e, 0x7f, 0x4b, 0x39, 0xbf, 0x4 };
916 const ad = "";
809917 const key = [_]u8{
810918 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
811919 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
......@@ -814,11 +922,11 @@ test "open" {
814922 const exp_out = "";
815923
816924 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);
818926 testing.expectEqualSlices(u8, exp_out[0..], out[0..]);
819927 }
820928 {
821 const ciphertext = [_]u8{
929 const c = [_]u8{
822930 0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb, 0x7b, 0x86, 0xaf, 0xbc, 0x53, 0xef, 0x7e, 0xc2,
823931 0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x8, 0xfe, 0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6,
824932 0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67, 0x12, 0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b,
......@@ -829,7 +937,7 @@ test "open" {
829937 0x61, 0x16, 0x1a, 0xe1, 0xb, 0x59, 0x4f, 0x9, 0xe2, 0x6a, 0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60,
830938 0x6, 0x91,
831939 };
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 };
833941 const key = [_]u8{
834942 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
835943 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
......@@ -847,126 +955,45 @@ test "open" {
847955 };
848956
849957 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);
851959 testing.expectEqualSlices(u8, exp_out[0..], out[0..]);
852960
853961 // corrupting the ciphertext, data, key, or nonce should cause a failure
854 var bad_ciphertext = ciphertext;
855 bad_ciphertext[0] ^= 1;
856 testing.expectError(error.AuthenticationFailed, chacha20poly1305Open(out[0..], bad_ciphertext[0..], data[0..], key, nonce));
857 var bad_data = data;
858 bad_data[0] ^= 1;
859 testing.expectError(error.AuthenticationFailed, chacha20poly1305Open(out[0..], ciphertext[0..], bad_data[0..], key, nonce));
962 var bad_c = c;
963 bad_c[0] ^= 1;
964 testing.expectError(error.AuthenticationFailed, ChaCha20Poly1305.decrypt(out[0..], bad_c[0..out.len], bad_c[out.len..].*, ad[0..], nonce, key));
965 var bad_ad = ad;
966 bad_ad[0] ^= 1;
967 testing.expectError(error.AuthenticationFailed, ChaCha20Poly1305.decrypt(out[0..], c[0..out.len], c[out.len..].*, bad_ad[0..], nonce, key));
860968 var bad_key = key;
861969 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));
863971 var bad_nonce = nonce;
864972 bad_nonce[0] ^= 1;
865 testing.expectError(error.AuthenticationFailed, chacha20poly1305Open(out[0..], ciphertext[0..], data[0..], key, bad_nonce));
866
867 // a short ciphertext should result in a different error
868 testing.expectError(error.AuthenticationFailed, chacha20poly1305Open(out[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));
869974 }
870975}
871976
872977test "crypto.xchacha20" {
873978 const key = [_]u8{69} ** 32;
874979 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.";
876981 {
877 var ciphertext: [input.len]u8 = undefined;
878 XChaCha20IETF.xor(ciphertext[0..], input[0..], 0, key, nonce);
879 var buf: [2 * ciphertext.len]u8 = undefined;
880 testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&ciphertext)}), "E0A1BCF939654AFDBDC1746EC49832647C19D891F0D1A81FC0C1703B4514BDEA584B512F6908C2C5E9DD18D5CBC1805DE5803FE3B9CA5F193FB8359E91FAB0C3BB40309A292EB1CF49685C65C4A3ADF4F11DB0CD2B6B67FBC174BC2E860E8F769FD3565BBFAD1C845E05A0FED9BE167C240D");
982 var c: [m.len]u8 = undefined;
983 XChaCha20IETF.xor(c[0..], m[0..], 0, key, nonce);
984 var buf: [2 * c.len]u8 = undefined;
985 testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&c)}), "E0A1BCF939654AFDBDC1746EC49832647C19D891F0D1A81FC0C1703B4514BDEA584B512F6908C2C5E9DD18D5CBC1805DE5803FE3B9CA5F193FB8359E91FAB0C3BB40309A292EB1CF49685C65C4A3ADF4F11DB0CD2B6B67FBC174BC2E860E8F769FD3565BBFAD1C845E05A0FED9BE167C240D");
881986 }
882987 {
883 const data = "Additional data";
884 var ciphertext: [input.len + xchacha20poly1305_tag_length]u8 = undefined;
885 xchacha20poly1305Seal(ciphertext[0..], input, data, key, nonce);
886 var out: [input.len]u8 = undefined;
887 try xchacha20poly1305Open(out[0..], ciphertext[0..], data, key, nonce);
888 var buf: [2 * ciphertext.len]u8 = undefined;
889 testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&ciphertext)}), "994D2DD32333F48E53650C02C7A2ABB8E018B0836D7175AEC779F52E961780768F815C58F1AA52D211498DB89B9216763F569C9433A6BBFCEFB4D4A49387A4C5207FBB3B5A92B5941294DF30588C6740D39DC16FA1F0E634F7246CF7CDCB978E44347D89381B7A74EB7084F754B90BDE9AAF5A94B8F2A85EFD0B50692AE2D425E234");
890 testing.expectEqualSlices(u8, out[0..], input);
891 ciphertext[0] += 1;
892 testing.expectError(error.AuthenticationFailed, xchacha20poly1305Open(out[0..], ciphertext[0..], data, key, nonce));
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));
988 const ad = "Additional data";
989 var c: [m.len + XChaCha20Poly1305.tag_length]u8 = undefined;
990 XChaCha20Poly1305.encrypt(c[0..m.len], c[m.len..], m, ad, nonce, key);
991 var out: [m.len]u8 = undefined;
992 try XChaCha20Poly1305.decrypt(out[0..], c[0..m.len], c[m.len..].*, ad, nonce, key);
993 var buf: [2 * c.len]u8 = undefined;
994 testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&c)}), "994D2DD32333F48E53650C02C7A2ABB8E018B0836D7175AEC779F52E961780768F815C58F1AA52D211498DB89B9216763F569C9433A6BBFCEFB4D4A49387A4C5207FBB3B5A92B5941294DF30588C6740D39DC16FA1F0E634F7246CF7CDCB978E44347D89381B7A74EB7084F754B90BDE9AAF5A94B8F2A85EFD0B50692AE2D425E234");
995 testing.expectEqualSlices(u8, out[0..], m);
996 c[0] += 1;
997 testing.expectError(error.AuthenticationFailed, XChaCha20Poly1305.decrypt(out[0..], c[0..m.len], c[m.len..].*, ad, nonce, key));
971998 }
972999}
lib/std/crypto/pbkdf2.zig+67-70
......@@ -20,20 +20,20 @@ const Error = std.crypto.Error;
2020// pseudorandom function. See Appendix B.1 for further discussion.)
2121// PBKDF2 is recommended for new applications.
2222//
23// PBKDF2 (P, S, c, dkLen)
23// PBKDF2 (P, S, c, dk_len)
2424//
25// Options: PRF underlying pseudorandom function (hLen
25// Options: PRF underlying pseudorandom function (h_len
2626// denotes the length in octets of the
2727// pseudorandom function output)
2828//
2929// Input: P password, an octet string
3030// S salt, an octet string
3131// c iteration count, a positive integer
32// dkLen intended length in octets of the derived
32// dk_len intended length in octets of the derived
3333// key, a positive integer, at most
34// (2^32 - 1) * hLen
34// (2^32 - 1) * h_len
3535//
36// Output: DK derived key, a dkLen-octet string
36// Output: DK derived key, a dk_len-octet string
3737
3838// Based on Apple's CommonKeyDerivation, based originally on code by Damien Bergamini.
3939
......@@ -41,7 +41,7 @@ const Error = std.crypto.Error;
4141///
4242/// PBKDF2 is defined in RFC 2898, and is a recommendation of NIST SP 800-132.
4343///
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.
4545/// May be uninitialized. All bytes will be overwritten.
4646/// Maximum size is `maxInt(u32) * Hash.digest_length`
4747/// It is a programming error to pass buffer longer than the maximum size.
......@@ -52,43 +52,38 @@ const Error = std.crypto.Error;
5252///
5353/// rounds: Iteration count. Must be greater than 0. Common values range from 1,000 to 100,000.
5454/// 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.
5656///
5757/// 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 {
5959 if (rounds < 1) return error.WeakParameters;
6060
61 const dkLen = derivedKey.len;
62 const hLen = Prf.mac_length;
63 comptime std.debug.assert(hLen >= 1);
61 const dk_len = dk.len;
62 const h_len = Prf.mac_length;
63 comptime std.debug.assert(h_len >= 1);
6464
6565 // FromSpec:
6666 //
67 // 1. If dkLen > maxInt(u32) * hLen, output "derived key too long" and
67 // 1. If dk_len > maxInt(u32) * h_len, output "derived key too long" and
6868 // stop.
6969 //
70 if (comptime (maxInt(usize) > maxInt(u32) * hLen) and (dkLen > @as(usize, maxInt(u32) * hLen))) {
71 // If maxInt(usize) is less than `maxInt(u32) * hLen` then dkLen is always inbounds
70 if (dk_len / h_len >= maxInt(u32)) {
71 // Counter starts at 1 and is 32 bit, so if we have to return more blocks, we would overflow
7272 return error.OutputTooLong;
7373 }
7474
7575 // FromSpec:
7676 //
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,
7878 // rounding up, and let r be the number of bytes in the last
7979 // block
8080 //
8181
82 // l will not overflow, proof:
83 // let `L(dkLen, hLen) = (dkLen + hLen - 1) / hLen`
84 // then `L^-1(l, hLen) = l*hLen - hLen + 1`
85 // 1) L^-1(maxInt(u32), hLen) <= maxInt(u32)*hLen
86 // 2) maxInt(u32)*hLen - hLen + 1 <= maxInt(u32)*hLen // subtract maxInt(u32)*hLen + 1
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_;
82 const blocks_count = @intCast(u32, std.math.divCeil(usize, dk_len, h_len) catch unreachable);
83 var r = dk_len % h_len;
84 if (r == 0) {
85 r = h_len;
86 }
9287
9388 // FromSpec:
9489 //
......@@ -118,37 +113,38 @@ pub fn pbkdf2(derivedKey: []u8, password: []const u8, salt: []const u8, rounds:
118113 // Here, INT (i) is a four-octet encoding of the integer i, most
119114 // significant octet first.
120115 //
121 // 4. Concatenate the blocks and extract the first dkLen octets to
116 // 4. Concatenate the blocks and extract the first dk_len octets to
122117 // produce a derived key DK:
123118 //
124119 // DK = T_1 || T_2 || ... || T_l<0..r-1>
125 var block: u32 = 0; // Spec limits to u32
126 while (block < l) : (block += 1) {
127 var prevBlock: [hLen]u8 = undefined;
128 var newBlock: [hLen]u8 = undefined;
120
121 var block: u32 = 0;
122 while (block < blocks_count) : (block += 1) {
123 var prev_block: [h_len]u8 = undefined;
124 var new_block: [h_len]u8 = undefined;
129125
130126 // U_1 = PRF (P, S || INT (i))
131 const blockIndex = mem.toBytes(mem.nativeToBig(u32, block + 1)); // Block index starts at 0001
127 const block_index = mem.toBytes(mem.nativeToBig(u32, block + 1)); // Block index starts at 0001
132128 var ctx = Prf.init(password);
133129 ctx.update(salt);
134 ctx.update(blockIndex[0..]);
135 ctx.final(prevBlock[0..]);
130 ctx.update(block_index[0..]);
131 ctx.final(prev_block[0..]);
136132
137133 // Choose portion of DK to write into (T_n) and initialize
138 const offset = block * hLen;
139 const blockLen = if (block != l - 1) hLen else r;
140 const dkBlock: []u8 = derivedKey[offset..][0..blockLen];
141 mem.copy(u8, dkBlock, prevBlock[0..dkBlock.len]);
134 const offset = block * h_len;
135 const block_len = if (block != blocks_count - 1) h_len else r;
136 const dk_block: []u8 = dk[offset..][0..block_len];
137 mem.copy(u8, dk_block, prev_block[0..dk_block.len]);
142138
143139 var i: u32 = 1;
144140 while (i < rounds) : (i += 1) {
145141 // U_c = PRF (P, U_{c-1})
146 Prf.create(&newBlock, prevBlock[0..], password);
147 mem.copy(u8, prevBlock[0..], newBlock[0..]);
142 Prf.create(&new_block, prev_block[0..], password);
143 mem.copy(u8, prev_block[0..], new_block[0..]);
148144
149145 // F (P, S, c, i) = U_1 \xor U_2 \xor ... \xor U_c
150 for (dkBlock) |_, j| {
151 dkBlock[j] ^= newBlock[j];
146 for (dk_block) |_, j| {
147 dk_block[j] ^= new_block[j];
152148 }
153149 }
154150 }
......@@ -158,49 +154,50 @@ const htest = @import("test.zig");
158154const HmacSha1 = std.crypto.auth.hmac.HmacSha1;
159155
160156// RFC 6070 PBKDF2 HMAC-SHA1 Test Vectors
157
161158test "RFC 6070 one iteration" {
162159 const p = "password";
163160 const s = "salt";
164161 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
171168 const expected = "0c60c80f961f0e71f3a9b524af6012062fe037a6";
172169
173 htest.assertEqual(expected, derivedKey[0..]);
170 htest.assertEqual(expected, dk[0..]);
174171}
175172
176173test "RFC 6070 two iterations" {
177174 const p = "password";
178175 const s = "salt";
179176 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
186183 const expected = "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957";
187184
188 htest.assertEqual(expected, derivedKey[0..]);
185 htest.assertEqual(expected, dk[0..]);
189186}
190187
191188test "RFC 6070 4096 iterations" {
192189 const p = "password";
193190 const s = "salt";
194191 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
201198 const expected = "4b007901b765489abead49d926f721d065a429c1";
202199
203 htest.assertEqual(expected, derivedKey[0..]);
200 htest.assertEqual(expected, dk[0..]);
204201}
205202
206203test "RFC 6070 16,777,216 iterations" {
......@@ -212,48 +209,48 @@ test "RFC 6070 16,777,216 iterations" {
212209 const p = "password";
213210 const s = "salt";
214211 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
221218 const expected = "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984";
222219
223 htest.assertEqual(expected, derivedKey[0..]);
220 htest.assertEqual(expected, dk[0..]);
224221}
225222
226223test "RFC 6070 multi-block salt and password" {
227224 const p = "passwordPASSWORDpassword";
228225 const s = "saltSALTsaltSALTsaltSALTsaltSALTsalt";
229226 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
236233 const expected = "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038";
237234
238 htest.assertEqual(expected, derivedKey[0..]);
235 htest.assertEqual(expected, dk[0..]);
239236}
240237
241238test "RFC 6070 embedded NUL" {
242239 const p = "pass\x00word";
243240 const s = "sa\x00lt";
244241 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
251248 const expected = "56fa6aa75548099dcc37d7f03425e0c3";
252249
253 htest.assertEqual(expected, derivedKey[0..]);
250 htest.assertEqual(expected, dk[0..]);
254251}
255252
256test "Very large dkLen" {
253test "Very large dk_len" {
257254 // This test allocates 8GB of memory and is expected to take several hours to run.
258255 if (true) {
259256 return error.SkipZigTest;
......@@ -261,13 +258,13 @@ test "Very large dkLen" {
261258 const p = "password";
262259 const s = "salt";
263260 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);
267264 defer {
268 std.testing.allocator.free(derivedKey);
265 std.testing.allocator.free(dk);
269266 }
270267
271 try pbkdf2(derivedKey, p, s, c, HmacSha1);
272268 // Just verify this doesn't crash with an overflow
269 try pbkdf2(dk, p, s, c, HmacSha1);
273270}
lib/std/debug.zig-18
......@@ -250,24 +250,6 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c
250250 resetSegfaultHandler();
251251 }
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
271253 nosuspend switch (panic_stage) {
272254 0 => {
273255 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 {
9292/// Naively combines a series of paths with the native path seperator and null terminator.
9393/// Allocates memory for the result, which must be freed by the caller.
9494pub 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);
9696 return out[0 .. out.len - 1 :0];
9797}
9898
......@@ -119,6 +119,16 @@ fn testJoinMaybeZPosix(paths: []const []const u8, expected: []const u8, zero: bo
119119}
120120
121121test "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 }
122132 for (&[_]bool{ false, true }) |zero| {
123133 testJoinMaybeZWindows(&[_][]const u8{}, "", zero);
124134 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;
12271227/// section has local relocation entries
12281228pub 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
12301248pub const cpu_type_t = integer_t;
12311249pub const cpu_subtype_t = integer_t;
12321250pub const integer_t = c_int;
......@@ -1422,6 +1440,14 @@ pub const EXPORT_SYMBOL_FLAGS_KIND_WEAK_DEFINITION: u8 = 0x04;
14221440pub const EXPORT_SYMBOL_FLAGS_REEXPORT: u8 = 0x08;
14231441pub 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
14251451// Codesign consts and structs taken from:
14261452// 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 {
15891615 /// Total length of blob
15901616 length: u32,
15911617};
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 {
888888/// Given a type and value, cast the value to the type as c would.
889889/// This is for translate-c and is not intended for general use.
890890pub 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);
892893 switch (@typeInfo(DestType)) {
893 .Pointer => |dest_ptr| {
894 switch (@typeInfo(TargetType)) {
894 .Pointer => {
895 switch (@typeInfo(SourceType)) {
895896 .Int, .ComptimeInt => {
896897 return @intToPtr(DestType, target);
897898 },
898 .Pointer => |ptr| {
899 return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target));
899 .Pointer => {
900 return castPtr(DestType, target);
900901 },
901902 .Optional => |opt| {
902903 if (@typeInfo(opt.child) == .Pointer) {
903 return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target));
904 return castPtr(DestType, target);
904905 }
905906 },
906907 else => {},
......@@ -908,17 +909,16 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {
908909 },
909910 .Optional => |dest_opt| {
910911 if (@typeInfo(dest_opt.child) == .Pointer) {
911 const dest_ptr = @typeInfo(dest_opt.child).Pointer;
912 switch (@typeInfo(TargetType)) {
912 switch (@typeInfo(SourceType)) {
913913 .Int, .ComptimeInt => {
914914 return @intToPtr(DestType, target);
915915 },
916916 .Pointer => {
917 return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target));
917 return castPtr(DestType, target);
918918 },
919919 .Optional => |target_opt| {
920920 if (@typeInfo(target_opt.child) == .Pointer) {
921 return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target));
921 return castPtr(DestType, target);
922922 }
923923 },
924924 else => {},
......@@ -926,25 +926,25 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {
926926 }
927927 },
928928 .Enum => {
929 if (@typeInfo(TargetType) == .Int or @typeInfo(TargetType) == .ComptimeInt) {
929 if (@typeInfo(SourceType) == .Int or @typeInfo(SourceType) == .ComptimeInt) {
930930 return @intToEnum(DestType, target);
931931 }
932932 },
933 .Int, .ComptimeInt => {
934 switch (@typeInfo(TargetType)) {
933 .Int => {
934 switch (@typeInfo(SourceType)) {
935935 .Pointer => {
936 return @intCast(DestType, @ptrToInt(target));
936 return castInt(DestType, @ptrToInt(target));
937937 },
938938 .Optional => |opt| {
939939 if (@typeInfo(opt.child) == .Pointer) {
940 return @intCast(DestType, @ptrToInt(target));
940 return castInt(DestType, @ptrToInt(target));
941941 }
942942 },
943943 .Enum => {
944 return @intCast(DestType, @enumToInt(target));
944 return castInt(DestType, @enumToInt(target));
945945 },
946 .Int, .ComptimeInt => {
947 return @intCast(DestType, target);
946 .Int => {
947 return castInt(DestType, target);
948948 },
949949 else => {},
950950 }
......@@ -954,6 +954,34 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {
954954 return @as(DestType, target);
955955}
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
957985test "std.meta.cast" {
958986 const E = enum(u2) {
959987 Zero,
......@@ -977,6 +1005,11 @@ test "std.meta.cast" {
9771005 testing.expectEqual(@as(u32, 4), cast(u32, @intToPtr(?*u32, 4)));
9781006 testing.expectEqual(@as(u32, 10), cast(u32, @as(u64, 10)));
9791007 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)));
9801013}
9811014
9821015/// 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" {
408408 testing.expect(isTuple(@TypeOf(t3)));
409409}
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
411489pub fn hasDecls(comptime T: type, comptime names: anytype) bool {
412490 inline for (names) |name| {
413491 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
28792879 unreachable;
28802880}
28812881
2882const ListenError = error{
2882pub const ListenError = error{
28832883 /// Another socket is already listening on the same port.
28842884 /// For Internet domain sockets, the socket referred to by sockfd had not previously
28852885 /// 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
58275827 }
58285828}
58295829
5830const IoCtl_SIOCGIFINDEX_Error = error{
5830pub const IoCtl_SIOCGIFINDEX_Error = error{
58315831 FileSystem,
58325832 InterfaceNotFound,
58335833} || UnexpectedError;
lib/std/std.zig+4
......@@ -20,6 +20,9 @@ pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringM
2020pub const DynLib = @import("dynamic_library.zig").DynLib;
2121pub const DynamicBitSet = bit_set.DynamicBitSet;
2222pub const DynamicBitSetUnmanaged = bit_set.DynamicBitSetUnmanaged;
23pub const EnumArray = enums.EnumArray;
24pub const EnumMap = enums.EnumMap;
25pub const EnumSet = enums.EnumSet;
2326pub const HashMap = hash_map.HashMap;
2427pub const HashMapUnmanaged = hash_map.HashMapUnmanaged;
2528pub const MultiArrayList = @import("multi_array_list.zig").MultiArrayList;
......@@ -54,6 +57,7 @@ pub const cstr = @import("cstr.zig");
5457pub const debug = @import("debug.zig");
5558pub const dwarf = @import("dwarf.zig");
5659pub const elf = @import("elf.zig");
60pub const enums = @import("enums.zig");
5761pub const event = @import("event.zig");
5862pub const fifo = @import("fifo.zig");
5963pub const fmt = @import("fmt.zig");
lib/std/zig/parser_test.zig+73
......@@ -4,6 +4,31 @@
44// The MIT license requires this copyright notice to be included in all copies
55// 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
732// TODO Remove this after zig 0.9.0 is released.
833test "zig fmt: rewrite inline functions as callconv(.Inline)" {
934 try testTransform(
......@@ -3038,6 +3063,54 @@ test "zig fmt: switch" {
30383063 \\}
30393064 \\
30403065 );
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 );
30413114}
30423115
30433116test "zig fmt: while" {
lib/std/zig/render.zig+33-5
......@@ -1159,8 +1159,29 @@ fn renderContainerField(
11591159 try renderToken(ais, tree, rparen_token, .space); // )
11601160 }
11611161 const eq_token = tree.firstToken(field.ast.value_expr) - 1;
1162 try renderToken(ais, tree, eq_token, .space); // =
1163 return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value
1162 const eq_space: Space = if (tree.tokensOnSameLine(eq_token, eq_token + 1)) .space else .newline;
1163 {
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 }
11641185}
11651186
11661187fn renderBuiltinCall(
......@@ -1423,6 +1444,7 @@ fn renderSwitchCase(
14231444 switch_case: ast.full.SwitchCase,
14241445 space: Space,
14251446) Error!void {
1447 const node_tags = tree.nodes.items(.tag);
14261448 const token_tags = tree.tokens.items(.tag);
14271449 const trailing_comma = token_tags[switch_case.ast.arrow_token - 1] == .comma;
14281450
......@@ -1445,17 +1467,23 @@ fn renderSwitchCase(
14451467 }
14461468
14471469 // 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
14501478 if (switch_case.payload_token) |payload_token| {
14511479 try renderToken(ais, tree, payload_token - 1, .none); // pipe
14521480 if (token_tags[payload_token] == .asterisk) {
14531481 try renderToken(ais, tree, payload_token, .none); // asterisk
14541482 try renderToken(ais, tree, payload_token + 1, .none); // identifier
1455 try renderToken(ais, tree, payload_token + 2, .space); // pipe
1483 try renderToken(ais, tree, payload_token + 2, pre_target_space); // pipe
14561484 } else {
14571485 try renderToken(ais, tree, payload_token, .none); // identifier
1458 try renderToken(ais, tree, payload_token + 1, .space); // pipe
1486 try renderToken(ais, tree, payload_token + 1, pre_target_space); // pipe
14591487 }
14601488 }
14611489
src/BuiltinFn.zig+1-1
......@@ -477,7 +477,7 @@ pub const list = list: {
477477 "@intCast",
478478 .{
479479 .tag = .int_cast,
480 .param_count = 1,
480 .param_count = 2,
481481 },
482482 },
483483 .{
src/clang.zig+5
......@@ -537,6 +537,11 @@ pub const FunctionType = opaque {
537537 extern fn ZigClangFunctionType_getReturnType(*const FunctionType) QualType;
538538};
539539
540pub const GenericSelectionExpr = opaque {
541 pub const getResultExpr = ZigClangGenericSelectionExpr_getResultExpr;
542 extern fn ZigClangGenericSelectionExpr_getResultExpr(*const GenericSelectionExpr) *const Expr;
543};
544
540545pub const IfStmt = opaque {
541546 pub const getThen = ZigClangIfStmt_getThen;
542547 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 {
21332133 if (inst.func.value()) |func_value| {
21342134 if (func_value.castTag(.function)) |func_payload| {
21352135 const func = func_payload.data;
2136 const text_segment = &macho_file.load_commands.items[macho_file.text_segment_cmd_index.?].Segment;
2137 const got = &text_segment.sections.items[macho_file.got_section_index.?];
2138 const got_addr = got.addr + func.owner_decl.link.macho.offset_table_index * @sizeOf(u64);
2136 const got_addr = blk: {
2137 const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
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});
21392142 switch (arch) {
21402143 .x86_64 => {
21412144 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 {
21532156 const decl = func_payload.data;
21542157 const decl_name = try std.fmt.allocPrint(self.bin_file.allocator, "_{s}", .{decl.name});
21552158 defer self.bin_file.allocator.free(decl_name);
2156 const already_defined = macho_file.extern_lazy_symbols.contains(decl_name);
2157 const symbol: u32 = if (macho_file.extern_lazy_symbols.getIndex(decl_name)) |index|
2159 const already_defined = macho_file.lazy_imports.contains(decl_name);
2160 const symbol: u32 = if (macho_file.lazy_imports.getIndex(decl_name)) |index|
21582161 @intCast(u32, index)
21592162 else
21602163 try macho_file.addExternSymbol(decl_name);
......@@ -3304,80 +3307,32 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
33043307 },
33053308 .memory => |addr| {
33063309 if (self.bin_file.options.pie) {
3307 // For MachO, the binary, with the exception of object files, has to be a PIE.
3308 // Therefore we cannot load an absolute address.
3309 // Instead, we need to make use of PC-relative addressing.
3310 if (reg.id() == 0) { // x0 is special-cased
3311 // TODO This needs to be optimised in the stack usage (perhaps use a shadow stack
3312 // like described here:
3313 // https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/using-the-stack-in-aarch64-implementing-push-and-pop)
3314 // str x28, [sp, #-16]
3315 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.str(.x28, Register.sp, .{
3316 .offset = Instruction.LoadStoreOffset.imm_pre_index(-16),
3317 }).toU32());
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());
3310 // PC-relative displacement to the entry in the GOT table.
3311 // TODO we should come up with our own, backend independent relocation types
3312 // which each backend (Elf, MachO, etc.) would then translate into an actual
3313 // fixup when linking.
3314 // adrp reg, pages
3315 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3316 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3317 .target_addr = addr,
3318 .offset = self.code.items.len,
3319 .size = 4,
3320 });
33453321 } else {
3346 // stp x0, x28, [sp, #-16]
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());
3322 return self.fail(src, "TODO implement genSetReg for PIE GOT indirection on this platform", .{});
33803323 }
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());
33813336 } else {
33823337 // The value is in memory at a hard-coded address.
33833338 // 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 {
35613516 },
35623517 .memory => |x| {
35633518 if (self.bin_file.options.pie) {
3564 // For MachO, the binary, with the exception of object files, has to be a PIE.
3565 // Therefore, we cannot load an absolute address.
3566 assert(x > math.maxInt(u32)); // 32bit direct addressing is not supported by MachO.
3567 // The plan here is to use unconditional relative jump to GOT entry, where we store
3568 // pre-calculated and stored effective address to load into the target register.
3569 // We leave the actual displacement information empty (0-padded) and fixing it up
3570 // later in the linker.
3571 if (reg.id() == 0) { // %rax is special-cased
3572 try self.code.ensureCapacity(self.code.items.len + 5);
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,
3519 // RIP-relative displacement to the entry in the GOT table.
3520 // TODO we should come up with our own, backend independent relocation types
3521 // which each backend (Elf, MachO, etc.) would then translate into an actual
3522 // fixup when linking.
3523 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3524 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3525 .target_addr = x,
3526 .offset = self.code.items.len + 3,
3527 .size = 4,
35893528 });
35903529 } else {
3591 try self.code.ensureCapacity(self.code.items.len + 10);
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});
3530 return self.fail(src, "TODO implement genSetReg for PIE GOT indirection on this platform", .{});
36193531 }
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 });
36203544 } else if (x <= math.maxInt(u32)) {
36213545 // Moving from memory to a register is a variant of `8B /r`.
36223546 // Since we're using 64-bit moves, we require a REX.
......@@ -3779,9 +3703,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
37793703 return MCValue{ .memory = got_addr };
37803704 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
37813705 const decl = payload.data;
3782 const text_segment = &macho_file.load_commands.items[macho_file.text_segment_cmd_index.?].Segment;
3783 const got = &text_segment.sections.items[macho_file.got_section_index.?];
3784 const got_addr = got.addr + decl.link.macho.offset_table_index * ptr_bytes;
3706 const got_addr = blk: {
3707 const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
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 };
37853711 return MCValue{ .memory = got_addr };
37863712 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
37873713 const decl = payload.data;
src/codegen/aarch64.zig+4-1
......@@ -221,7 +221,8 @@ pub const Instruction = union(enum) {
221221 offset: u12,
222222 opc: u2,
223223 op1: u2,
224 fixed: u4 = 0b111_0,
224 v: u1,
225 fixed: u3 = 0b111,
225226 size: u2,
226227 },
227228 LoadStorePairOfRegisters: packed struct {
......@@ -505,6 +506,7 @@ pub const Instruction = union(enum) {
505506 .offset = offset.toU12(),
506507 .opc = opc,
507508 .op1 = op1,
509 .v = 0,
508510 .size = 0b10,
509511 },
510512 };
......@@ -517,6 +519,7 @@ pub const Instruction = union(enum) {
517519 .offset = offset.toU12(),
518520 .opc = opc,
519521 .op1 = op1,
522 .v = 0,
520523 .size = 0b11,
521524 },
522525 };
src/codegen/llvm.zig+11-11
......@@ -219,7 +219,7 @@ pub const LLVMIRModule = struct {
219219
220220 var error_message: [*:0]const u8 = undefined;
221221 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()) {
223223 defer llvm.disposeMessage(error_message);
224224
225225 const stderr = std.io.getStdErr().writer();
......@@ -303,7 +303,7 @@ pub const LLVMIRModule = struct {
303303 // verifyModule always allocs the error_message even if there is no error
304304 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()) {
307307 const stderr = std.io.getStdErr().writer();
308308 try stderr.print("broken LLVM module found: {s}\nThis is a bug in the Zig compiler.", .{error_message});
309309 return error.BrokenLLVMModule;
......@@ -319,7 +319,7 @@ pub const LLVMIRModule = struct {
319319 object_pathZ.ptr,
320320 .ObjectFile,
321321 &error_message,
322 )) {
322 ).toBool()) {
323323 defer llvm.disposeMessage(error_message);
324324
325325 const stderr = std.io.getStdErr().writer();
......@@ -614,7 +614,7 @@ pub const LLVMIRModule = struct {
614614
615615 var indices: [2]*const llvm.Value = .{
616616 index_type.constNull(),
617 index_type.constInt(1, false),
617 index_type.constInt(1, .False),
618618 };
619619
620620 return self.builder.buildLoad(self.builder.buildInBoundsGEP(operand, &indices, 2, ""), "");
......@@ -676,7 +676,7 @@ pub const LLVMIRModule = struct {
676676 const signed = inst.base.ty.isSignedInt();
677677 // TODO: Should we use intcast here or just a simple bitcast?
678678 // 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), "");
680680 }
681681
682682 fn genBitCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value {
......@@ -782,7 +782,7 @@ pub const LLVMIRModule = struct {
782782 if (bigint.limbs.len != 1) {
783783 return self.fail(src, "TODO implement bigger bigint", .{});
784784 }
785 const llvm_int = llvm_type.constInt(bigint.limbs[0], false);
785 const llvm_int = llvm_type.constInt(bigint.limbs[0], .False);
786786 if (!bigint.positive) {
787787 return llvm.constNeg(llvm_int);
788788 }
......@@ -820,7 +820,7 @@ pub const LLVMIRModule = struct {
820820 return self.fail(src, "TODO handle other sentinel values", .{});
821821 } 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));
824824 } else {
825825 return self.fail(src, "TODO handle more array values", .{});
826826 }
......@@ -836,13 +836,13 @@ pub const LLVMIRModule = struct {
836836 llvm_child_type.constNull(),
837837 self.context.intType(1).constNull(),
838838 };
839 return self.context.constStruct(&optional_values, 2, false);
839 return self.context.constStruct(&optional_values, 2, .False);
840840 } else {
841841 var optional_values: [2]*const llvm.Value = .{
842842 try self.genTypedValue(src, .{ .ty = child_type, .val = tv.val }),
843843 self.context.intType(1).constAllOnes(),
844844 };
845 return self.context.constStruct(&optional_values, 2, false);
845 return self.context.constStruct(&optional_values, 2, .False);
846846 }
847847 } else {
848848 return self.fail(src, "TODO implement const of optional pointer", .{});
......@@ -882,7 +882,7 @@ pub const LLVMIRModule = struct {
882882 try self.getLLVMType(child_type, src),
883883 self.context.intType(1),
884884 };
885 return self.context.structType(&optional_types, 2, false);
885 return self.context.structType(&optional_types, 2, .False);
886886 } else {
887887 return self.fail(src, "TODO implement optional pointers as actual pointers", .{});
888888 }
......@@ -934,7 +934,7 @@ pub const LLVMIRModule = struct {
934934 try self.getLLVMType(return_type, src),
935935 if (fn_param_len == 0) null else llvm_param.ptr,
936936 @intCast(c_uint, fn_param_len),
937 false,
937 .False,
938938 );
939939 const llvm_fn = self.llvm_module.addFunction(func.name, fn_type);
940940
src/codegen/llvm/bindings.zig+23-10
......@@ -1,7 +1,20 @@
11//! We do this instead of @cImport because the self-hosted compiler is easier
22//! 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};
518pub const AttributeIndex = c_uint;
619
720/// Make sure to use the *InContext functions instead of the global ones.
......@@ -22,13 +35,13 @@ pub const Context = opaque {
2235 extern fn LLVMVoidTypeInContext(C: *const Context) *const Type;
2336
2437 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
2740 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
3043 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
3346 pub const createBasicBlock = LLVMCreateBasicBlockInContext;
3447 extern fn LLVMCreateBasicBlockInContext(C: *const Context, Name: [*:0]const u8) *const BasicBlock;
......@@ -59,7 +72,7 @@ pub const Value = opaque {
5972
6073pub const Type = opaque {
6174 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
6477 pub const constNull = LLVMConstNull;
6578 extern fn LLVMConstNull(Ty: *const Type) *const Value;
......@@ -68,7 +81,7 @@ pub const Type = opaque {
6881 extern fn LLVMConstAllOnes(Ty: *const Type) *const Value;
6982
7083 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
7386 pub const constArray = LLVMConstArray;
7487 extern fn LLVMConstArray(ElementTy: *const Type, ConstantVals: ?[*]*const Value, Length: c_uint) *const Value;
......@@ -91,7 +104,7 @@ pub const Module = opaque {
91104 extern fn LLVMDisposeModule(*const Module) void;
92105
93106 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
96109 pub const addFunction = LLVMAddFunction;
97110 extern fn LLVMAddFunction(*const Module, Name: [*:0]const u8, FunctionTy: *const Type) *const Value;
......@@ -191,7 +204,7 @@ pub const Builder = opaque {
191204 extern fn LLVMBuildNUWSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
192205
193206 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
196209 pub const buildBitCast = LLVMBuildBitCast;
197210 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 {
258271 Filename: [*:0]const u8,
259272 codegen: CodeGenFileType,
260273 ErrorMessage: *[*:0]const u8,
261 ) LLVMBool;
274 ) Bool;
262275};
263276
264277pub const CodeMode = extern enum {
......@@ -295,7 +308,7 @@ pub const CodeGenFileType = extern enum {
295308
296309pub const Target = opaque {
297310 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;
299312};
300313
301314extern fn LLVMInitializeAArch64TargetInfo() void;
src/link/MachO.zig+549-536
......@@ -11,7 +11,9 @@ const codegen = @import("../codegen.zig");
1111const aarch64 = @import("../codegen/aarch64.zig");
1212const math = std.math;
1313const mem = std.mem;
14const meta = std.meta;
1415
16const bind = @import("MachO/bind.zig");
1517const trace = @import("../tracy.zig").trace;
1618const build_options = @import("build_options");
1719const Module = @import("../Module.zig");
......@@ -24,9 +26,9 @@ const target_util = @import("../target.zig");
2426const DebugSymbols = @import("MachO/DebugSymbols.zig");
2527const Trie = @import("MachO/Trie.zig");
2628const CodeSignature = @import("MachO/CodeSignature.zig");
29const Zld = @import("MachO/Zld.zig");
2730
2831usingnamespace @import("MachO/commands.zig");
29usingnamespace @import("MachO/imports.zig");
3032
3133pub const base_tag: File.Tag = File.Tag.macho;
3234
......@@ -87,14 +89,12 @@ code_signature_cmd_index: ?u16 = null,
8789
8890/// Index into __TEXT,__text section.
8991text_section_index: ?u16 = null,
90/// Index into __TEXT,__ziggot section.
91got_section_index: ?u16 = null,
9292/// Index into __TEXT,__stubs section.
9393stubs_section_index: ?u16 = null,
9494/// Index into __TEXT,__stub_helper section.
9595stub_helper_section_index: ?u16 = null,
9696/// Index into __DATA_CONST,__got section.
97data_got_section_index: ?u16 = null,
97got_section_index: ?u16 = null,
9898/// Index into __DATA,__la_symbol_ptr section.
9999la_symbol_ptr_section_index: ?u16 = null,
100100/// Index into __DATA,__data section.
......@@ -104,16 +104,16 @@ entry_addr: ?u64 = null,
104104
105105/// Table of all local symbols
106106/// Internally references string table for names (which are optional).
107local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
107locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
108108/// Table of all global symbols
109global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
109globals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
110110/// Table of all extern nonlazy symbols, indexed by name.
111extern_nonlazy_symbols: std.StringArrayHashMapUnmanaged(ExternSymbol) = .{},
111nonlazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},
112112/// 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) = .{},
116global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
115locals_free_list: std.ArrayListUnmanaged(u32) = .{},
116globals_free_list: std.ArrayListUnmanaged(u32) = .{},
117117offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
118118
119119stub_helper_stubs_start_off: ?u64 = null,
......@@ -122,8 +122,8 @@ stub_helper_stubs_start_off: ?u64 = null,
122122string_table: std.ArrayListUnmanaged(u8) = .{},
123123string_table_directory: std.StringHashMapUnmanaged(u32) = .{},
124124
125/// Table of trampolines to the actual symbols in __text section.
126offset_table: std.ArrayListUnmanaged(u64) = .{},
125/// Table of GOT entries.
126offset_table: std.ArrayListUnmanaged(GOTEntry) = .{},
127127
128128error_flags: File.ErrorFlags = File.ErrorFlags{},
129129
......@@ -154,14 +154,19 @@ string_table_needs_relocation: bool = false,
154154/// allocate a fresh text block, which will have ideal capacity, and then grow it
155155/// by 1 byte. It will then have -1 overcapacity.
156156text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
157
157158/// Pointer to the last allocated text block
158159last_text_block: ?*TextBlock = null,
160
159161/// A list of all PIE fixups required for this run of the linker.
160162/// Warning, this is currently NOT thread-safe. See the TODO below.
161163/// TODO Move this list inside `updateDecl` where it should be allocated
162164/// prior to calling `generateSymbol`, and then immediately deallocated
163165/// 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
165170/// A list of all stub (extern decls) fixups required for this run of the linker.
166171/// Warning, this is currently NOT thread-safe. See the TODO below.
167172/// TODO Move this list inside `updateDecl` where it should be allocated
......@@ -169,14 +174,42 @@ pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{},
169174/// rather than sitting in the global scope.
170175stub_fixups: std.ArrayListUnmanaged(StubFixup) = .{},
171176
172pub const PieFixup = struct {
173 /// Target address we wanted to address in absolute terms.
174 address: u64,
175 /// Where in the byte stream we should perform the fixup.
176 start: usize,
177 /// The length of the byte stream. For x86_64, this will be
178 /// variable. For aarch64, it will be fixed at 4 bytes.
179 len: usize,
177pub const GOTEntry = struct {
178 /// GOT entry can either be a local pointer or an extern (nonlazy) import.
179 kind: enum {
180 Local,
181 Extern,
182 },
183
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,
180213};
181214
182215pub const StubFixup = struct {
......@@ -260,9 +293,9 @@ pub const TextBlock = struct {
260293 /// File offset relocation happens transparently, so it is not included in
261294 /// this calculation.
262295 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];
264297 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];
266299 return next_sym.n_value - self_sym.n_value;
267300 } else {
268301 // We are the last block.
......@@ -274,8 +307,8 @@ pub const TextBlock = struct {
274307 fn freeListEligible(self: TextBlock, macho_file: MachO) bool {
275308 // No need to keep a free list node for the last block.
276309 const next = self.next orelse return false;
277 const self_sym = macho_file.local_symbols.items[self.local_sym_index];
278 const next_sym = macho_file.local_symbols.items[next.local_sym_index];
310 const self_sym = macho_file.locals.items[self.local_sym_index];
311 const next_sym = macho_file.locals.items[next.local_sym_index];
279312 const cap = next_sym.n_value - self_sym.n_value;
280313 const ideal_cap = padToIdeal(self.size);
281314 if (cap <= ideal_cap) return false;
......@@ -344,7 +377,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
344377 };
345378
346379 // Index 0 is always a null symbol.
347 try self.local_symbols.append(allocator, .{
380 try self.locals.append(allocator, .{
348381 .n_strx = 0,
349382 .n_type = 0,
350383 .n_sect = 0,
......@@ -600,7 +633,74 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
600633 if (!mem.eql(u8, the_object_path, full_out_path)) {
601634 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
602635 }
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
604704 // Create an LLD command line and invoke it.
605705 var argv = std.ArrayList([]const u8).init(self.base.allocator);
606706 defer argv.deinit();
......@@ -871,119 +971,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
871971 log.warn("unexpected LLD stderr:\n{s}", .{stderr});
872972 }
873973 }
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 }
987974 }
988975 }
989976
......@@ -1019,14 +1006,14 @@ pub fn deinit(self: *MachO) void {
10191006 if (self.d_sym) |*ds| {
10201007 ds.deinit(self.base.allocator);
10211008 }
1022 for (self.extern_lazy_symbols.items()) |*entry| {
1009 for (self.lazy_imports.items()) |*entry| {
10231010 self.base.allocator.free(entry.key);
10241011 }
1025 self.extern_lazy_symbols.deinit(self.base.allocator);
1026 for (self.extern_nonlazy_symbols.items()) |*entry| {
1012 self.lazy_imports.deinit(self.base.allocator);
1013 for (self.nonlazy_imports.items()) |*entry| {
10271014 self.base.allocator.free(entry.key);
10281015 }
1029 self.extern_nonlazy_symbols.deinit(self.base.allocator);
1016 self.nonlazy_imports.deinit(self.base.allocator);
10301017 self.pie_fixups.deinit(self.base.allocator);
10311018 self.stub_fixups.deinit(self.base.allocator);
10321019 self.text_block_free_list.deinit(self.base.allocator);
......@@ -1040,10 +1027,10 @@ pub fn deinit(self: *MachO) void {
10401027 }
10411028 self.string_table_directory.deinit(self.base.allocator);
10421029 self.string_table.deinit(self.base.allocator);
1043 self.global_symbols.deinit(self.base.allocator);
1044 self.global_symbol_free_list.deinit(self.base.allocator);
1045 self.local_symbols.deinit(self.base.allocator);
1046 self.local_symbol_free_list.deinit(self.base.allocator);
1030 self.globals.deinit(self.base.allocator);
1031 self.globals_free_list.deinit(self.base.allocator);
1032 self.locals.deinit(self.base.allocator);
1033 self.locals_free_list.deinit(self.base.allocator);
10471034 for (self.load_commands.items) |*lc| {
10481035 lc.deinit(self.base.allocator);
10491036 }
......@@ -1098,7 +1085,7 @@ fn shrinkTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64) vo
10981085}
10991086
11001087fn 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];
11021089 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;
11031090 const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);
11041091 if (!need_realloc) return sym.n_value;
......@@ -1108,34 +1095,41 @@ fn growTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alig
11081095pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
11091096 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);
11121099 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| {
11151102 log.debug("reusing symbol index {d} for {s}", .{ i, decl.name });
11161103 decl.link.macho.local_sym_index = i;
11171104 } else {
1118 log.debug("allocating symbol index {d} for {s}", .{ self.local_symbols.items.len, decl.name });
1119 decl.link.macho.local_sym_index = @intCast(u32, self.local_symbols.items.len);
1120 _ = self.local_symbols.addOneAssumeCapacity();
1105 log.debug("allocating symbol index {d} for {s}", .{ self.locals.items.len, decl.name });
1106 decl.link.macho.local_sym_index = @intCast(u32, self.locals.items.len);
1107 _ = self.locals.addOneAssumeCapacity();
11211108 }
11221109
11231110 if (self.offset_table_free_list.popOrNull()) |i| {
1111 log.debug("reusing offset table entry index {d} for {s}", .{ i, decl.name });
11241112 decl.link.macho.offset_table_index = i;
11251113 } else {
1114 log.debug("allocating offset table entry index {d} for {s}", .{ self.offset_table.items.len, decl.name });
11261115 decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len);
11271116 _ = self.offset_table.addOneAssumeCapacity();
11281117 self.offset_table_count_dirty = true;
1118 self.rebase_info_dirty = true;
11291119 }
11301120
1131 self.local_symbols.items[decl.link.macho.local_sym_index] = .{
1121 self.locals.items[decl.link.macho.local_sym_index] = .{
11321122 .n_strx = 0,
11331123 .n_type = 0,
11341124 .n_sect = 0,
11351125 .n_desc = 0,
11361126 .n_value = 0,
11371127 };
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 };
11391133}
11401134
11411135pub 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 {
11781172 .externally_managed => |x| x,
11791173 .appended => code_buffer.items,
11801174 .fail => |em| {
1181 // Clear any PIE fixups and stub fixups for this decl.
1175 // Clear any PIE fixups for this decl.
11821176 self.pie_fixups.shrinkRetainingCapacity(0);
1177 // Clear any stub fixups for this decl.
11831178 self.stub_fixups.shrinkRetainingCapacity(0);
11841179 decl.analysis = .codegen_failure;
11851180 try module.failed_decls.put(module.gpa, decl, em);
......@@ -1189,7 +1184,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
11891184
11901185 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
11911186 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
11941189 if (decl.link.macho.size != 0) {
11951190 const capacity = decl.link.macho.capacity(self.*);
......@@ -1198,9 +1193,12 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
11981193 const vaddr = try self.growTextBlock(&decl.link.macho, code.len, required_alignment);
11991194 log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr });
12001195 if (vaddr != symbol.n_value) {
1201 symbol.n_value = vaddr;
12021196 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 };
12041202 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
12051203 }
12061204 } else if (code.len < decl.link.macho.size) {
......@@ -1229,7 +1227,11 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
12291227 .n_desc = 0,
12301228 .n_value = addr,
12311229 };
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
12341236 try self.writeLocalSymbol(decl.link.macho.local_sym_index);
12351237 if (self.d_sym) |*ds|
......@@ -1237,30 +1239,48 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
12371239 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
12381240 }
12391241
1240 // Perform PIE fixups (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.?];
1242 // Calculate displacements to target addr (if any).
12431243 while (self.pie_fixups.popOrNull()) |fixup| {
1244 const target_addr = fixup.address;
1245 const this_addr = symbol.n_value + fixup.start;
1244 assert(fixup.size == 4);
1245 const this_addr = symbol.n_value + fixup.offset;
1246 const target_addr = fixup.target_addr;
1247
12461248 switch (self.base.options.target.cpu.arch) {
12471249 .x86_64 => {
1248 assert(target_addr >= this_addr + fixup.len);
1249 const displacement = try math.cast(u32, target_addr - this_addr - fixup.len);
1250 var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)];
1251 mem.writeIntSliceLittle(u32, placeholder, displacement);
1250 const displacement = try math.cast(u32, target_addr - this_addr - 4);
1251 mem.writeIntLittle(u32, code_buffer.items[fixup.offset..][0..4], displacement);
12521252 },
12531253 .aarch64 => {
1254 assert(target_addr >= this_addr);
1255 const displacement = try math.cast(u27, target_addr - this_addr);
1256 var placeholder = code_buffer.items[fixup.start..][0..fixup.len];
1257 mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.b(@as(i28, displacement)).toU32());
1254 // TODO optimize instruction based on jump length (use ldr(literal) + nop if possible).
1255 {
1256 const inst = code_buffer.items[fixup.offset..][0..4];
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 }
12581277 },
12591278 else => unreachable, // unsupported target architecture
12601279 }
12611280 }
12621281
12631282 // Resolve stubs (if any)
1283 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
12641284 const stubs = text_segment.sections.items[self.stubs_section_index.?];
12651285 for (self.stub_fixups.items) |fixup| {
12661286 const stub_addr = stubs.addr + fixup.symbol * stubs.reserved2;
......@@ -1285,9 +1305,6 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
12851305 try self.writeStubInStubHelper(fixup.symbol);
12861306 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);
12911308 self.rebase_info_dirty = true;
12921309 self.lazy_binding_info_dirty = true;
12931310 }
......@@ -1329,9 +1346,9 @@ pub fn updateDeclExports(
13291346 const tracy = trace(@src());
13301347 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);
13331350 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
13361353 for (exports) |exp| {
13371354 if (exp.options.section) |section_name| {
......@@ -1364,7 +1381,7 @@ pub fn updateDeclExports(
13641381 };
13651382 const n_type = decl_sym.n_type | macho.N_EXT;
13661383 if (exp.link.macho.sym_index) |i| {
1367 const sym = &self.global_symbols.items[i];
1384 const sym = &self.globals.items[i];
13681385 sym.* = .{
13691386 .n_strx = try self.updateString(sym.n_strx, exp.options.name),
13701387 .n_type = n_type,
......@@ -1374,12 +1391,12 @@ pub fn updateDeclExports(
13741391 };
13751392 } else {
13761393 const name_str_index = try self.makeString(exp.options.name);
1377 const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {
1378 _ = self.global_symbols.addOneAssumeCapacity();
1394 const i = if (self.globals_free_list.popOrNull()) |i| i else blk: {
1395 _ = self.globals.addOneAssumeCapacity();
13791396 self.export_info_dirty = true;
1380 break :blk self.global_symbols.items.len - 1;
1397 break :blk self.globals.items.len - 1;
13811398 };
1382 self.global_symbols.items[i] = .{
1399 self.globals.items[i] = .{
13831400 .n_strx = name_str_index,
13841401 .n_type = n_type,
13851402 .n_sect = @intCast(u8, self.text_section_index.?) + 1,
......@@ -1394,18 +1411,18 @@ pub fn updateDeclExports(
13941411
13951412pub fn deleteExport(self: *MachO, exp: Export) void {
13961413 const sym_index = exp.sym_index orelse return;
1397 self.global_symbol_free_list.append(self.base.allocator, sym_index) catch {};
1398 self.global_symbols.items[sym_index].n_type = 0;
1414 self.globals_free_list.append(self.base.allocator, sym_index) catch {};
1415 self.globals.items[sym_index].n_type = 0;
13991416}
14001417
14011418pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {
14021419 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
14031420 self.freeTextBlock(&decl.link.macho);
14041421 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 {};
14061423 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
14101427 decl.link.macho.local_sym_index = 0;
14111428 }
......@@ -1413,7 +1430,7 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {
14131430
14141431pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {
14151432 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;
14171434}
14181435
14191436pub fn populateMissingMetadata(self: *MachO) !void {
......@@ -1553,39 +1570,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
15531570 self.header_dirty = true;
15541571 self.load_commands_dirty = true;
15551572 }
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 }
15891573 if (self.stubs_section_index == null) {
15901574 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
15911575 self.stubs_section_index = @intCast(u16, text_segment.sections.items.len);
......@@ -1597,7 +1581,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
15971581 };
15981582 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {
15991583 .x86_64 => 6,
1600 .aarch64 => 2 * @sizeOf(u32),
1584 .aarch64 => 3 * @sizeOf(u32),
16011585 else => unreachable, // unhandled architecture type
16021586 };
16031587 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 {
16861670 self.header_dirty = true;
16871671 self.load_commands_dirty = true;
16881672 }
1689 if (self.data_got_section_index == null) {
1673 if (self.got_section_index == null) {
16901674 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
16931677 const flags = macho.S_NON_LAZY_SYMBOL_POINTERS;
16941678 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
......@@ -2060,12 +2044,12 @@ pub fn populateMissingMetadata(self: *MachO) !void {
20602044 self.header_dirty = true;
20612045 self.load_commands_dirty = true;
20622046 }
2063 if (!self.extern_nonlazy_symbols.contains("dyld_stub_binder")) {
2064 const index = @intCast(u32, self.extern_nonlazy_symbols.items().len);
2047 if (!self.nonlazy_imports.contains("dyld_stub_binder")) {
2048 const index = @intCast(u32, self.nonlazy_imports.items().len);
20652049 const name = try self.base.allocator.dupe(u8, "dyld_stub_binder");
20662050 const offset = try self.makeString("dyld_stub_binder");
2067 try self.extern_nonlazy_symbols.putNoClobber(self.base.allocator, name, .{
2068 .inner = .{
2051 try self.nonlazy_imports.putNoClobber(self.base.allocator, name, .{
2052 .symbol = .{
20692053 .n_strx = offset,
20702054 .n_type = std.macho.N_UNDF | std.macho.N_EXT,
20712055 .n_sect = 0,
......@@ -2073,68 +2057,19 @@ pub fn populateMissingMetadata(self: *MachO) !void {
20732057 .n_value = 0,
20742058 },
20752059 .dylib_ordinal = 1, // TODO this is currently hardcoded.
2076 .segment = self.data_const_segment_cmd_index.?,
2077 .offset = index * @sizeOf(u64),
2060 .index = index,
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,
20782067 });
2068 try self.writeOffsetTableEntry(off_index);
20792069 self.binding_info_dirty = true;
20802070 }
20812071 if (self.stub_helper_stubs_start_off == null) {
2082 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
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 }
2072 try self.writeStubHelperPreamble();
21382073 }
21392074}
21402075
......@@ -2159,7 +2094,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
21592094 const big_block = self.text_block_free_list.items[i];
21602095 // We now have a pointer to a live text block that has too much capacity.
21612096 // 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];
21632098 const capacity = big_block.capacity(self.*);
21642099 const ideal_capacity = padToIdeal(capacity);
21652100 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,
21902125 }
21912126 break :blk new_start_vaddr;
21922127 } 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];
21942129 // TODO We should pad out the excess capacity with NOPs. For executables,
21952130 // no padding seems to be OK, but it will probably not be for objects.
21962131 const ideal_capacity = padToIdeal(last.size);
......@@ -2288,12 +2223,12 @@ fn updateString(self: *MachO, old_str_off: u32, new_name: []const u8) !u32 {
22882223}
22892224
22902225pub 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);
22922227 const offset = try self.makeString(name);
22932228 const sym_name = try self.base.allocator.dupe(u8, name);
22942229 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, .{
2296 .inner = .{
2230 try self.lazy_imports.putNoClobber(self.base.allocator, sym_name, .{
2231 .symbol = .{
22972232 .n_strx = offset,
22982233 .n_type = macho.N_UNDF | macho.N_EXT,
22992234 .n_sect = 0,
......@@ -2301,6 +2236,7 @@ pub fn addExternSymbol(self: *MachO, name: []const u8) !u32 {
23012236 .n_value = 0,
23022237 },
23032238 .dylib_ordinal = dylib_ordinal,
2239 .index = index,
23042240 });
23052241 log.debug("adding new extern symbol '{s}' with dylib ordinal '{}'", .{ name, dylib_ordinal });
23062242 return index;
......@@ -2459,41 +2395,29 @@ fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16, sta
24592395}
24602396
24612397fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
2462 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2463 const sect = &text_segment.sections.items[self.got_section_index.?];
2398 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2399 const sect = &seg.sections.items[self.got_section_index.?];
24642400 const off = sect.offset + @sizeOf(u64) * index;
2465 const vmaddr = sect.addr + @sizeOf(u64) * index;
24662401
24672402 if (self.offset_table_count_dirty) {
24682403 // TODO relocate.
24692404 self.offset_table_count_dirty = false;
24702405 }
24712406
2472 var code: [8]u8 = undefined;
2473 switch (self.base.options.target.cpu.arch) {
2474 .x86_64 => {
2475 const pos_symbol_off = try math.cast(u31, vmaddr - self.offset_table.items[index] + 7);
2476 const symbol_off = @bitCast(u32, @as(i32, pos_symbol_off) * -1);
2477 // lea %rax, [rip - disp]
2478 code[0] = 0x48;
2479 code[1] = 0x8D;
2480 code[2] = 0x5;
2481 mem.writeIntLittle(u32, code[3..7], symbol_off);
2482 // ret
2483 code[7] = 0xC3;
2484 },
2485 .aarch64 => {
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);
2407 const got_entry = self.offset_table.items[index];
2408 const sym = blk: {
2409 switch (got_entry.kind) {
2410 .Local => {
2411 break :blk self.locals.items[got_entry.symbol];
2412 },
2413 .Extern => {
2414 break :blk self.nonlazy_imports.items()[got_entry.symbol].value.symbol;
2415 },
2416 }
2417 };
2418 const sym_name = self.getString(sym.n_strx);
2419 log.debug("writing offset table entry [ 0x{x} => 0x{x} ({s}) ]", .{ off, sym.n_value, sym_name });
2420 try self.base.file.?.pwriteAll(mem.asBytes(&sym.n_value), off);
24972421}
24982422
24992423fn writeLazySymbolPointer(self: *MachO, index: u32) !void {
......@@ -2516,6 +2440,133 @@ fn writeLazySymbolPointer(self: *MachO, index: u32) !void {
25162440 try self.base.file.?.pwriteAll(&buf, off);
25172441}
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
25192570fn writeStub(self: *MachO, index: u32) !void {
25202571 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
25212572 const stubs = text_segment.sections.items[self.stubs_section_index.?];
......@@ -2525,9 +2576,12 @@ fn writeStub(self: *MachO, index: u32) !void {
25252576 const stub_off = stubs.offset + index * stubs.reserved2;
25262577 const stub_addr = stubs.addr + index * stubs.reserved2;
25272578 const la_ptr_addr = la_symbol_ptr.addr + index * @sizeOf(u64);
2579
25282580 log.debug("writing stub at 0x{x}", .{stub_off});
2581
25292582 var code = try self.base.allocator.alloc(u8, stubs.reserved2);
25302583 defer self.base.allocator.free(code);
2584
25312585 switch (self.base.options.target.cpu.arch) {
25322586 .x86_64 => {
25332587 assert(la_ptr_addr >= stub_addr + stubs.reserved2);
......@@ -2539,12 +2593,50 @@ fn writeStub(self: *MachO, index: u32) !void {
25392593 },
25402594 .aarch64 => {
25412595 assert(la_ptr_addr >= stub_addr);
2542 const displacement = try math.divExact(u64, la_ptr_addr - stub_addr, 4);
2543 const literal = try math.cast(u19, displacement);
2544 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{
2545 .literal = literal,
2546 }).toU32());
2547 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.br(.x16).toU32());
2596 outer: {
2597 const this_addr = stub_addr;
2598 const target_addr = la_ptr_addr;
2599 inner: {
2600 const displacement = math.divExact(u64, target_addr - this_addr, 4) catch |_| break :inner;
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());
25482640 },
25492641 else => unreachable,
25502642 }
......@@ -2561,8 +2653,10 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {
25612653 else => unreachable,
25622654 };
25632655 const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size;
2656
25642657 var code = try self.base.allocator.alloc(u8, stub_size);
25652658 defer self.base.allocator.free(code);
2659
25662660 switch (self.base.options.target.cpu.arch) {
25672661 .x86_64 => {
25682662 const displacement = try math.cast(
......@@ -2577,12 +2671,19 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {
25772671 mem.writeIntLittle(u32, code[6..][0..4], @bitCast(u32, displacement));
25782672 },
25792673 .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
25812679 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{
2582 .literal = @divExact(stub_size - @sizeOf(u32), 4),
2680 .literal = literal,
25832681 }).toU32());
2682 const displacement = try math.cast(i28, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4);
2683 // b disp
25842684 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);
25862687 },
25872688 else => unreachable,
25882689 }
......@@ -2591,9 +2692,9 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {
25912692
25922693fn relocateSymbolTable(self: *MachO) !void {
25932694 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2594 const nlocals = self.local_symbols.items.len;
2595 const nglobals = self.global_symbols.items.len;
2596 const nundefs = self.extern_lazy_symbols.items().len + self.extern_nonlazy_symbols.items().len;
2695 const nlocals = self.locals.items.len;
2696 const nglobals = self.globals.items.len;
2697 const nundefs = self.lazy_imports.items().len + self.nonlazy_imports.items().len;
25972698 const nsyms = nlocals + nglobals + nundefs;
25982699
25992700 if (symtab.nsyms < nsyms) {
......@@ -2628,7 +2729,7 @@ fn writeLocalSymbol(self: *MachO, index: usize) !void {
26282729 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
26292730 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;
26302731 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);
26322733}
26332734
26342735fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
......@@ -2637,18 +2738,18 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
26372738
26382739 try self.relocateSymbolTable();
26392740 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2640 const nlocals = self.local_symbols.items.len;
2641 const nglobals = self.global_symbols.items.len;
2741 const nlocals = self.locals.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;
26442745 var undefs = std.ArrayList(macho.nlist_64).init(self.base.allocator);
26452746 defer undefs.deinit();
26462747 try undefs.ensureCapacity(nundefs);
2647 for (self.extern_lazy_symbols.items()) |entry| {
2648 undefs.appendAssumeCapacity(entry.value.inner);
2748 for (self.lazy_imports.items()) |entry| {
2749 undefs.appendAssumeCapacity(entry.value.symbol);
26492750 }
2650 for (self.extern_nonlazy_symbols.items()) |entry| {
2651 undefs.appendAssumeCapacity(entry.value.inner);
2751 for (self.nonlazy_imports.items()) |entry| {
2752 undefs.appendAssumeCapacity(entry.value.symbol);
26522753 }
26532754
26542755 const locals_off = symtab.symoff;
......@@ -2657,7 +2758,7 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
26572758 const globals_off = locals_off + locals_size;
26582759 const globals_size = nglobals * @sizeOf(macho.nlist_64);
26592760 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
26622763 const undefs_off = globals_off + globals_size;
26632764 const undefs_size = nundefs * @sizeOf(macho.nlist_64);
......@@ -2683,15 +2784,15 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
26832784 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
26842785 const stubs = &text_segment.sections.items[self.stubs_section_index.?];
26852786 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.?];
26872788 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
26882789 const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
26892790 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
26902791
2691 const lazy = self.extern_lazy_symbols.items();
2692 const nonlazy = self.extern_nonlazy_symbols.items();
2792 const lazy = self.lazy_imports.items();
2793 const got_entries = self.offset_table.items;
26932794 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);
26952796 const needed_size = @intCast(u32, nindirectsyms * @sizeOf(u32));
26962797
26972798 if (needed_size > allocated_size) {
......@@ -2710,20 +2811,27 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
27102811 var writer = stream.writer();
27112812
27122813 stubs.reserved1 = 0;
2713 for (self.extern_lazy_symbols.items()) |_, i| {
2814 for (lazy) |_, i| {
27142815 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
27152816 try writer.writeIntLittle(u32, symtab_idx);
27162817 }
27172818
27182819 const base_id = @intCast(u32, lazy.len);
27192820 got.reserved1 = base_id;
2720 for (self.extern_nonlazy_symbols.items()) |_, i| {
2721 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i + base_id);
2722 try writer.writeIntLittle(u32, symtab_idx);
2821 for (got_entries) |entry| {
2822 switch (entry.kind) {
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 }
27232831 }
27242832
2725 la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, nonlazy.len);
2726 for (self.extern_lazy_symbols.items()) |_, i| {
2833 la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, got_entries.len);
2834 for (lazy) |_, i| {
27272835 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
27282836 try writer.writeIntLittle(u32, symtab_idx);
27292837 }
......@@ -2789,7 +2897,7 @@ fn writeCodeSignature(self: *MachO) !void {
27892897
27902898fn writeExportTrie(self: *MachO) !void {
27912899 if (!self.export_info_dirty) return;
2792 if (self.global_symbols.items.len == 0) return;
2900 if (self.globals.items.len == 0) return;
27932901
27942902 const tracy = trace(@src());
27952903 defer tracy.end();
......@@ -2798,7 +2906,7 @@ fn writeExportTrie(self: *MachO) !void {
27982906 defer trie.deinit();
27992907
28002908 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| {
28022910 // TODO figure out if we should put all global symbols into the export trie
28032911 const name = self.getString(symbol.n_strx);
28042912 assert(symbol.n_value >= text_segment.inner.vmaddr);
......@@ -2840,14 +2948,48 @@ fn writeRebaseInfoTable(self: *MachO) !void {
28402948 const tracy = trace(@src());
28412949 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);
28442987 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
28452988 defer self.base.allocator.free(buffer);
28462989
28472990 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;
28512993 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
28522994 const allocated_size = self.allocatedSizeLinkedit(dyld_info.rebase_off);
28532995 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
......@@ -2872,14 +3014,34 @@ fn writeBindingInfoTable(self: *MachO) !void {
28723014 const tracy = trace(@src());
28733015 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);
28763039 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
28773040 defer self.base.allocator.free(buffer);
28783041
28793042 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;
28833045 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
28843046 const allocated_size = self.allocatedSizeLinkedit(dyld_info.bind_off);
28853047 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
......@@ -2901,14 +3063,36 @@ fn writeBindingInfoTable(self: *MachO) !void {
29013063fn writeLazyBindingInfoTable(self: *MachO) !void {
29023064 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);
29053090 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
29063091 defer self.base.allocator.free(buffer);
29073092
29083093 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;
29123096 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
29133097 const allocated_size = self.allocatedSizeLinkedit(dyld_info.lazy_bind_off);
29143098 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
......@@ -2929,7 +3113,7 @@ fn writeLazyBindingInfoTable(self: *MachO) !void {
29293113}
29303114
29313115fn 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
29343118 var stream = std.io.fixedBufferStream(buffer);
29353119 var reader = stream.reader();
......@@ -2975,7 +3159,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
29753159 else => {},
29763160 }
29773161 }
2978 assert(self.extern_lazy_symbols.items().len <= offsets.items.len);
3162 assert(self.lazy_imports.items().len <= offsets.items.len);
29793163
29803164 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {
29813165 .x86_64 => 10,
......@@ -2988,7 +3172,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
29883172 else => unreachable,
29893173 };
29903174 var buf: [@sizeOf(u32)]u8 = undefined;
2991 for (self.extern_lazy_symbols.items()) |_, i| {
3175 for (self.lazy_imports.items()) |_, i| {
29923176 const placeholder_off = self.stub_helper_stubs_start_off.? + i * stub_size + off;
29933177 mem.writeIntLittle(u32, &buf, offsets.items[i]);
29943178 try self.base.file.?.pwriteAll(&buf, placeholder_off);
......@@ -3102,177 +3286,6 @@ fn writeHeader(self: *MachO) !void {
31023286 self.header_dirty = false;
31033287}
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
32763289pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
32773290 // TODO https://github.com/ziglang/zig/issues/1284
32783291 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
839839
840840fn relocateSymbolTable(self: *DebugSymbols) !void {
841841 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
842 const nlocals = self.base.local_symbols.items.len;
843 const nglobals = self.base.global_symbols.items.len;
842 const nlocals = self.base.locals.items.len;
843 const nglobals = self.base.globals.items.len;
844844 const nsyms = nlocals + nglobals;
845845
846846 if (symtab.nsyms < nsyms) {
......@@ -875,7 +875,7 @@ pub fn writeLocalSymbol(self: *DebugSymbols, index: usize) !void {
875875 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
876876 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;
877877 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);
879879}
880880
881881fn writeStringTable(self: *DebugSymbols) !void {
......@@ -1057,7 +1057,7 @@ pub fn commitDeclDebugInfo(
10571057 var dbg_info_buffer = &debug_buffers.dbg_info_buffer;
10581058 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];
10611061 const text_block = &decl.link.macho;
10621062 // If the Decl is a function, we need to update the __debug_line program.
10631063 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 {
26372637 return cmd.toOwnedSlice();
26382638}
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
26402684pub const usage_fmt =
26412685 \\Usage: zig fmt [file]...
26422686 \\
......@@ -2708,9 +2752,10 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
27082752 fatal("cannot use --stdin with positional arguments", .{});
27092753 }
27102754
2711 const stdin = io.getStdIn().reader();
2712
2713 const source_code = try stdin.readAllAlloc(gpa, max_src_size);
2755 const stdin = io.getStdIn();
2756 const source_code = readSourceFileToEndAlloc(gpa, &stdin, null) catch |err| {
2757 fatal("unable to read stdin: {s}", .{err});
2758 };
27142759 defer gpa.free(source_code);
27152760
27162761 var tree = std.zig.parse(gpa, source_code) catch |err| {
......@@ -2785,6 +2830,7 @@ const FmtError = error{
27852830 EndOfStream,
27862831 Unseekable,
27872832 NotOpenForWriting,
2833 UnsupportedEncoding,
27882834} || fs.File.OpenError;
27892835
27902836fn 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(
28502896 if (stat.kind == .Directory)
28512897 return error.IsDir;
28522898
2853 const source_code = source_file.readToEndAllocOptions(
2899 const source_code = try readSourceFileToEndAlloc(
28542900 fmt.gpa,
2855 max_src_size,
2901 &source_file,
28562902 std.math.cast(usize, stat.size) catch return error.FileTooBig,
2857 @alignOf(u8),
2858 null,
2859 ) catch |err| switch (err) {
2860 error.ConnectionResetByPeer => unreachable,
2861 error.ConnectionTimedOut => unreachable,
2862 error.NotOpenForReading => unreachable,
2863 else => |e| return e,
2864 };
2903 );
2904 defer fmt.gpa.free(source_code);
2905
28652906 source_file.close();
28662907 file_closed = true;
2867 defer fmt.gpa.free(source_code);
28682908
28692909 // Add to set after no longer possible to get error.IsDir.
28702910 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
......@@ -3241,7 +3281,8 @@ pub const ClangArgIterator = struct {
32413281 self.zig_equivalent = clang_arg.zig_equivalent;
32423282 break :find_clang_arg;
32433283 },
3244 } else {
3284 }
3285 else {
32453286 fatal("Unknown Clang option: '{s}'", .{arg});
32463287 }
32473288 }
src/stage1/all_types.hpp+21-4
......@@ -391,6 +391,8 @@ enum LazyValueId {
391391 LazyValueIdAlignOf,
392392 LazyValueIdSizeOf,
393393 LazyValueIdPtrType,
394 LazyValueIdPtrTypeSimple,
395 LazyValueIdPtrTypeSimpleConst,
394396 LazyValueIdOptType,
395397 LazyValueIdSliceType,
396398 LazyValueIdFnType,
......@@ -467,6 +469,13 @@ struct LazyValuePtrType {
467469 bool is_allowzero;
468470};
469471
472struct LazyValuePtrTypeSimple {
473 LazyValue base;
474
475 IrAnalyze *ira;
476 IrInstGen *elem_type;
477};
478
470479struct LazyValueOptType {
471480 LazyValue base;
472481
......@@ -2610,7 +2619,8 @@ enum IrInstSrcId {
26102619 IrInstSrcIdEnumToInt,
26112620 IrInstSrcIdIntToErr,
26122621 IrInstSrcIdErrToInt,
2613 IrInstSrcIdCheckSwitchProngs,
2622 IrInstSrcIdCheckSwitchProngsUnderYes,
2623 IrInstSrcIdCheckSwitchProngsUnderNo,
26142624 IrInstSrcIdCheckStatementIsVoid,
26152625 IrInstSrcIdTypeName,
26162626 IrInstSrcIdDeclRef,
......@@ -2624,12 +2634,15 @@ enum IrInstSrcId {
26242634 IrInstSrcIdHasField,
26252635 IrInstSrcIdSetEvalBranchQuota,
26262636 IrInstSrcIdPtrType,
2637 IrInstSrcIdPtrTypeSimple,
2638 IrInstSrcIdPtrTypeSimpleConst,
26272639 IrInstSrcIdAlignCast,
26282640 IrInstSrcIdImplicitCast,
26292641 IrInstSrcIdResolveResult,
26302642 IrInstSrcIdResetResult,
26312643 IrInstSrcIdSetAlignStack,
2632 IrInstSrcIdArgType,
2644 IrInstSrcIdArgTypeAllowVarFalse,
2645 IrInstSrcIdArgTypeAllowVarTrue,
26332646 IrInstSrcIdExport,
26342647 IrInstSrcIdExtern,
26352648 IrInstSrcIdErrorReturnTrace,
......@@ -3294,6 +3307,12 @@ struct IrInstSrcArrayType {
32943307 IrInstSrc *child_type;
32953308};
32963309
3310struct IrInstSrcPtrTypeSimple {
3311 IrInstSrc base;
3312
3313 IrInstSrc *child_type;
3314};
3315
32973316struct IrInstSrcPtrType {
32983317 IrInstSrc base;
32993318
......@@ -4020,7 +4039,6 @@ struct IrInstSrcCheckSwitchProngs {
40204039 IrInstSrcCheckSwitchProngsRange *ranges;
40214040 size_t range_count;
40224041 AstNode* else_prong;
4023 bool have_underscore_prong;
40244042};
40254043
40264044struct IrInstSrcCheckStatementIsVoid {
......@@ -4144,7 +4162,6 @@ struct IrInstSrcArgType {
41444162
41454163 IrInstSrc *fn_type;
41464164 IrInstSrc *arg_index;
4147 bool allow_var;
41484165};
41494166
41504167struct IrInstSrcExport {
src/stage1/analyze.cpp+48-1
......@@ -1237,6 +1237,22 @@ Error type_val_resolve_zero_bits(CodeGen *g, ZigValue *type_val, ZigType *parent
12371237 parent_type_val, is_zero_bits);
12381238 }
12391239 }
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 }
12401256 case LazyValueIdArrayType: {
12411257 LazyValueArrayType *lazy_array_type =
12421258 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
12851301 zig_unreachable();
12861302 case LazyValueIdSliceType:
12871303 case LazyValueIdPtrType:
1304 case LazyValueIdPtrTypeSimple:
1305 case LazyValueIdPtrTypeSimpleConst:
12881306 case LazyValueIdFnType:
12891307 case LazyValueIdOptType:
12901308 case LazyValueIdErrUnionType:
......@@ -1313,6 +1331,11 @@ static ReqCompTime type_val_resolve_requires_comptime(CodeGen *g, ZigValue *type
13131331 LazyValuePtrType *lazy_ptr_type = reinterpret_cast<LazyValuePtrType *>(type_val->data.x_lazy);
13141332 return type_val_resolve_requires_comptime(g, lazy_ptr_type->elem_type->value);
13151333 }
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 }
13161339 case LazyValueIdOptType: {
13171340 LazyValueOptType *lazy_opt_type = reinterpret_cast<LazyValueOptType *>(type_val->data.x_lazy);
13181341 return type_val_resolve_requires_comptime(g, lazy_opt_type->payload_type->value);
......@@ -1413,6 +1436,24 @@ start_over:
14131436 }
14141437 return ErrorNone;
14151438 }
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 }
14161457 case LazyValueIdFnType:
14171458 *abi_size = g->builtin_types.entry_usize->abi_size;
14181459 *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
14491490 zig_unreachable();
14501491 case LazyValueIdSliceType:
14511492 case LazyValueIdPtrType:
1493 case LazyValueIdPtrTypeSimple:
1494 case LazyValueIdPtrTypeSimpleConst:
14521495 case LazyValueIdFnType:
14531496 *abi_align = g->builtin_types.entry_usize->abi_align;
14541497 return ErrorNone;
......@@ -1506,7 +1549,9 @@ static OnePossibleValue type_val_resolve_has_one_possible_value(CodeGen *g, ZigV
15061549 return OnePossibleValueYes;
15071550 return type_val_resolve_has_one_possible_value(g, lazy_array_type->elem_type->value);
15081551 }
1509 case LazyValueIdPtrType: {
1552 case LazyValueIdPtrType:
1553 case LazyValueIdPtrTypeSimple:
1554 case LazyValueIdPtrTypeSimpleConst: {
15101555 Error err;
15111556 bool zero_bits;
15121557 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) {
57585803 case LazyValueIdAlignOf:
57595804 case LazyValueIdSizeOf:
57605805 case LazyValueIdPtrType:
5806 case LazyValueIdPtrTypeSimple:
5807 case LazyValueIdPtrTypeSimpleConst:
57615808 case LazyValueIdOptType:
57625809 case LazyValueIdSliceType:
57635810 case LazyValueIdFnType:
src/stage1/ir.cpp+140-26
......@@ -476,7 +476,8 @@ static void destroy_instruction_src(IrInstSrc *inst) {
476476 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcIntToErr *>(inst));
477477 case IrInstSrcIdErrToInt:
478478 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcErrToInt *>(inst));
479 case IrInstSrcIdCheckSwitchProngs:
479 case IrInstSrcIdCheckSwitchProngsUnderNo:
480 case IrInstSrcIdCheckSwitchProngsUnderYes:
480481 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCheckSwitchProngs *>(inst));
481482 case IrInstSrcIdCheckStatementIsVoid:
482483 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCheckStatementIsVoid *>(inst));
......@@ -486,6 +487,9 @@ static void destroy_instruction_src(IrInstSrc *inst) {
486487 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTagName *>(inst));
487488 case IrInstSrcIdPtrType:
488489 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));
489493 case IrInstSrcIdDeclRef:
490494 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcDeclRef *>(inst));
491495 case IrInstSrcIdPanic:
......@@ -514,7 +518,8 @@ static void destroy_instruction_src(IrInstSrc *inst) {
514518 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcResetResult *>(inst));
515519 case IrInstSrcIdSetAlignStack:
516520 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSetAlignStack *>(inst));
517 case IrInstSrcIdArgType:
521 case IrInstSrcIdArgTypeAllowVarFalse:
522 case IrInstSrcIdArgTypeAllowVarTrue:
518523 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcArgType *>(inst));
519524 case IrInstSrcIdExport:
520525 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcExport *>(inst));
......@@ -1470,10 +1475,6 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrToInt *) {
14701475 return IrInstSrcIdErrToInt;
14711476}
14721477
1473static constexpr IrInstSrcId ir_inst_id(IrInstSrcCheckSwitchProngs *) {
1474 return IrInstSrcIdCheckSwitchProngs;
1475}
1476
14771478static constexpr IrInstSrcId ir_inst_id(IrInstSrcCheckStatementIsVoid *) {
14781479 return IrInstSrcIdCheckStatementIsVoid;
14791480}
......@@ -1546,10 +1547,6 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetAlignStack *) {
15461547 return IrInstSrcIdSetAlignStack;
15471548}
15481549
1549static constexpr IrInstSrcId ir_inst_id(IrInstSrcArgType *) {
1550 return IrInstSrcIdArgType;
1551}
1552
15531550static constexpr IrInstSrcId ir_inst_id(IrInstSrcExport *) {
15541551 return IrInstSrcIdExport;
15551552}
......@@ -2615,11 +2612,35 @@ static IrInstGen *ir_build_br_gen(IrAnalyze *ira, IrInst *source_instr, IrBasicB
26152612 return &inst->base;
26162613}
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
26182633static IrInstSrc *ir_build_ptr_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
26192634 IrInstSrc *child_type, bool is_const, bool is_volatile, PtrLen ptr_len,
26202635 IrInstSrc *sentinel, IrInstSrc *align_value,
26212636 uint32_t bit_offset_start, uint32_t host_int_bytes, bool is_allow_zero)
26222637{
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
26232644 IrInstSrcPtrType *inst = ir_build_instruction<IrInstSrcPtrType>(irb, scope, source_node);
26242645 inst->sentinel = sentinel;
26252646 inst->align_value = align_value;
......@@ -4354,13 +4375,19 @@ static IrInstSrc *ir_build_check_switch_prongs(IrBuilderSrc *irb, Scope *scope,
43544375 IrInstSrc *target_value, IrInstSrcCheckSwitchProngsRange *ranges, size_t range_count,
43554376 AstNode* else_prong, bool have_underscore_prong)
43564377{
4357 IrInstSrcCheckSwitchProngs *instruction = ir_build_instruction<IrInstSrcCheckSwitchProngs>(
4358 irb, scope, source_node);
4378 IrInstSrcCheckSwitchProngs *instruction = heap::c_allocator.create<IrInstSrcCheckSwitchProngs>();
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
43594387 instruction->target_value = target_value;
43604388 instruction->ranges = ranges;
43614389 instruction->range_count = range_count;
43624390 instruction->else_prong = else_prong;
4363 instruction->have_underscore_prong = have_underscore_prong;
43644391
43654392 ir_ref_instruction(target_value, irb->current_basic_block);
43664393 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
45904617static IrInstSrc *ir_build_arg_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
45914618 IrInstSrc *fn_type, IrInstSrc *arg_index, bool allow_var)
45924619{
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
45944629 instruction->fn_type = fn_type;
45954630 instruction->arg_index = arg_index;
4596 instruction->allow_var = allow_var;
45974631
45984632 ir_ref_instruction(fn_type, irb->current_basic_block);
45994633 ir_ref_instruction(arg_index, irb->current_basic_block);
......@@ -29702,7 +29736,7 @@ static IrInstGen *ir_analyze_instruction_test_comptime(IrAnalyze *ira, IrInstSrc
2970229736}
2970329737
2970429738static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
29705 IrInstSrcCheckSwitchProngs *instruction)
29739 IrInstSrcCheckSwitchProngs *instruction, bool have_underscore_prong)
2970629740{
2970729741 IrInstGen *target_value = instruction->target_value->child;
2970829742 ZigType *switch_type = target_value->value->type;
......@@ -29767,7 +29801,7 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
2976729801 bigint_incr(&field_index);
2976829802 }
2976929803 }
29770 if (instruction->have_underscore_prong) {
29804 if (have_underscore_prong) {
2977129805 if (!switch_type->data.enumeration.non_exhaustive) {
2977229806 ir_add_error(ira, &instruction->base.base,
2977329807 buf_sprintf("switch on exhaustive enum has `_` prong"));
......@@ -30871,6 +30905,24 @@ static IrInstGen *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstSrcPtr
3087130905 return ir_build_ptr_to_int_gen(ira, &instruction->base.base, target);
3087230906}
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
3087430926static IrInstGen *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstSrcPtrType *instruction) {
3087530927 IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_type);
3087630928 result->value->special = ConstValSpecialLazy;
......@@ -30976,7 +31028,9 @@ static IrInstGen *ir_analyze_instruction_set_align_stack(IrAnalyze *ira, IrInstS
3097631028 return ir_const_void(ira, &instruction->base.base);
3097731029}
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{
3098031034 IrInstGen *fn_type_inst = instruction->fn_type->child;
3098131035 ZigType *fn_type = ir_resolve_type(ira, fn_type_inst);
3098231036 if (type_is_invalid(fn_type))
......@@ -30998,7 +31052,7 @@ static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgTy
3099831052
3099931053 FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
3100031054 if (arg_index >= fn_type_id->param_count) {
31001 if (instruction->allow_var) {
31055 if (allow_var) {
3100231056 // TODO remove this with var args
3100331057 return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_anytype);
3100431058 }
......@@ -31013,7 +31067,7 @@ static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgTy
3101331067 // Args are only unresolved if our function is generic.
3101431068 ir_assert(fn_type->data.fn.is_generic, &instruction->base.base);
3101531069
31016 if (instruction->allow_var) {
31070 if (allow_var) {
3101731071 return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_anytype);
3101831072 } else {
3101931073 ir_add_error(ira, &arg_index_inst->base,
......@@ -32341,8 +32395,10 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc
3234132395 return ir_analyze_instruction_fn_proto(ira, (IrInstSrcFnProto *)instruction);
3234232396 case IrInstSrcIdTestComptime:
3234332397 return ir_analyze_instruction_test_comptime(ira, (IrInstSrcTestComptime *)instruction);
32344 case IrInstSrcIdCheckSwitchProngs:
32345 return ir_analyze_instruction_check_switch_prongs(ira, (IrInstSrcCheckSwitchProngs *)instruction);
32398 case IrInstSrcIdCheckSwitchProngsUnderNo:
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);
3234632402 case IrInstSrcIdCheckStatementIsVoid:
3234732403 return ir_analyze_instruction_check_statement_is_void(ira, (IrInstSrcCheckStatementIsVoid *)instruction);
3234832404 case IrInstSrcIdDeclRef:
......@@ -32373,6 +32429,10 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc
3237332429 return ir_analyze_instruction_set_eval_branch_quota(ira, (IrInstSrcSetEvalBranchQuota *)instruction);
3237432430 case IrInstSrcIdPtrType:
3237532431 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);
3237632436 case IrInstSrcIdAlignCast:
3237732437 return ir_analyze_instruction_align_cast(ira, (IrInstSrcAlignCast *)instruction);
3237832438 case IrInstSrcIdImplicitCast:
......@@ -32383,8 +32443,10 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc
3238332443 return ir_analyze_instruction_reset_result(ira, (IrInstSrcResetResult *)instruction);
3238432444 case IrInstSrcIdSetAlignStack:
3238532445 return ir_analyze_instruction_set_align_stack(ira, (IrInstSrcSetAlignStack *)instruction);
32386 case IrInstSrcIdArgType:
32387 return ir_analyze_instruction_arg_type(ira, (IrInstSrcArgType *)instruction);
32446 case IrInstSrcIdArgTypeAllowVarFalse:
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);
3238832450 case IrInstSrcIdExport:
3238932451 return ir_analyze_instruction_export(ira, (IrInstSrcExport *)instruction);
3239032452 case IrInstSrcIdExtern:
......@@ -32737,12 +32799,15 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) {
3273732799 case IrInstSrcIdMemcpy:
3273832800 case IrInstSrcIdBreakpoint:
3273932801 case IrInstSrcIdOverflowOp: // TODO when we support multiple returns this can be side effect free
32740 case IrInstSrcIdCheckSwitchProngs:
32802 case IrInstSrcIdCheckSwitchProngsUnderNo:
32803 case IrInstSrcIdCheckSwitchProngsUnderYes:
3274132804 case IrInstSrcIdCheckStatementIsVoid:
3274232805 case IrInstSrcIdCheckRuntimeScope:
3274332806 case IrInstSrcIdPanic:
3274432807 case IrInstSrcIdSetEvalBranchQuota:
3274532808 case IrInstSrcIdPtrType:
32809 case IrInstSrcIdPtrTypeSimple:
32810 case IrInstSrcIdPtrTypeSimpleConst:
3274632811 case IrInstSrcIdSetAlignStack:
3274732812 case IrInstSrcIdExport:
3274832813 case IrInstSrcIdExtern:
......@@ -32826,7 +32891,8 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) {
3282632891 case IrInstSrcIdAlignCast:
3282732892 case IrInstSrcIdImplicitCast:
3282832893 case IrInstSrcIdResolveResult:
32829 case IrInstSrcIdArgType:
32894 case IrInstSrcIdArgTypeAllowVarFalse:
32895 case IrInstSrcIdArgTypeAllowVarTrue:
3283032896 case IrInstSrcIdErrorReturnTrace:
3283132897 case IrInstSrcIdErrorUnion:
3283232898 case IrInstSrcIdFloatOp:
......@@ -33249,6 +33315,54 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) {
3324933315 // We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
3325033316 return ErrorNone;
3325133317 }
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 }
3325233366 case LazyValueIdArrayType: {
3325333367 LazyValueArrayType *lazy_array_type = reinterpret_cast<LazyValueArrayType *>(val->data.x_lazy);
3325433368 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) {
270270 return "SrcIntToErr";
271271 case IrInstSrcIdErrToInt:
272272 return "SrcErrToInt";
273 case IrInstSrcIdCheckSwitchProngs:
274 return "SrcCheckSwitchProngs";
273 case IrInstSrcIdCheckSwitchProngsUnderNo:
274 return "SrcCheckSwitchProngsUnderNo";
275 case IrInstSrcIdCheckSwitchProngsUnderYes:
276 return "SrcCheckSwitchProngsUnderYes";
275277 case IrInstSrcIdCheckStatementIsVoid:
276278 return "SrcCheckStatementIsVoid";
277279 case IrInstSrcIdTypeName:
......@@ -298,6 +300,10 @@ const char* ir_inst_src_type_str(IrInstSrcId id) {
298300 return "SrcSetEvalBranchQuota";
299301 case IrInstSrcIdPtrType:
300302 return "SrcPtrType";
303 case IrInstSrcIdPtrTypeSimple:
304 return "SrcPtrTypeSimple";
305 case IrInstSrcIdPtrTypeSimpleConst:
306 return "SrcPtrTypeSimpleConst";
301307 case IrInstSrcIdAlignCast:
302308 return "SrcAlignCast";
303309 case IrInstSrcIdImplicitCast:
......@@ -308,8 +314,10 @@ const char* ir_inst_src_type_str(IrInstSrcId id) {
308314 return "SrcResetResult";
309315 case IrInstSrcIdSetAlignStack:
310316 return "SrcSetAlignStack";
311 case IrInstSrcIdArgType:
312 return "SrcArgType";
317 case IrInstSrcIdArgTypeAllowVarFalse:
318 return "SrcArgTypeAllowVarFalse";
319 case IrInstSrcIdArgTypeAllowVarTrue:
320 return "SrcArgTypeAllowVarTrue";
313321 case IrInstSrcIdExport:
314322 return "SrcExport";
315323 case IrInstSrcIdExtern:
......@@ -2187,7 +2195,9 @@ static void ir_print_err_to_int(IrPrintGen *irp, IrInstGenErrToInt *instruction)
21872195 ir_print_other_inst_gen(irp, instruction->target);
21882196}
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{
21912201 fprintf(irp->f, "@checkSwitchProngs(");
21922202 ir_print_other_inst_src(irp, instruction->target_value);
21932203 fprintf(irp->f, ",");
......@@ -2200,6 +2210,8 @@ static void ir_print_check_switch_prongs(IrPrintSrc *irp, IrInstSrcCheckSwitchPr
22002210 }
22012211 const char *have_else_str = instruction->else_prong != nullptr ? "yes" : "no";
22022212 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);
22032215}
22042216
22052217static 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) {
22372249 ir_print_other_inst_src(irp, instruction->child_type);
22382250}
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
22402261static void ir_print_decl_ref(IrPrintSrc *irp, IrInstSrcDeclRef *instruction) {
22412262 const char *ptr_str = (instruction->lval != LValNone) ? "ptr " : "";
22422263 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
23442365 fprintf(irp->f, ")");
23452366}
23462367
2347static void ir_print_arg_type(IrPrintSrc *irp, IrInstSrcArgType *instruction) {
2368static void ir_print_arg_type(IrPrintSrc *irp, IrInstSrcArgType *instruction, bool allow_var) {
23482369 fprintf(irp->f, "@ArgType(");
23492370 ir_print_other_inst_src(irp, instruction->fn_type);
23502371 fprintf(irp->f, ",");
23512372 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 }
23522379 fprintf(irp->f, ")");
23532380}
23542381
......@@ -2885,8 +2912,11 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai
28852912 case IrInstSrcIdErrToInt:
28862913 ir_print_err_to_int(irp, (IrInstSrcErrToInt *)instruction);
28872914 break;
2888 case IrInstSrcIdCheckSwitchProngs:
2889 ir_print_check_switch_prongs(irp, (IrInstSrcCheckSwitchProngs *)instruction);
2915 case IrInstSrcIdCheckSwitchProngsUnderNo:
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);
28902920 break;
28912921 case IrInstSrcIdCheckStatementIsVoid:
28922922 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
29002930 case IrInstSrcIdPtrType:
29012931 ir_print_ptr_type(irp, (IrInstSrcPtrType *)instruction);
29022932 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;
29032939 case IrInstSrcIdDeclRef:
29042940 ir_print_decl_ref(irp, (IrInstSrcDeclRef *)instruction);
29052941 break;
......@@ -2942,8 +2978,11 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai
29422978 case IrInstSrcIdSetAlignStack:
29432979 ir_print_set_align_stack(irp, (IrInstSrcSetAlignStack *)instruction);
29442980 break;
2945 case IrInstSrcIdArgType:
2946 ir_print_arg_type(irp, (IrInstSrcArgType *)instruction);
2981 case IrInstSrcIdArgTypeAllowVarFalse:
2982 ir_print_arg_type(irp, (IrInstSrcArgType *)instruction, false);
2983 break;
2984 case IrInstSrcIdArgTypeAllowVarTrue:
2985 ir_print_arg_type(irp, (IrInstSrcArgType *)instruction, true);
29472986 break;
29482987 case IrInstSrcIdExport:
29492988 ir_print_export(irp, (IrInstSrcExport *)instruction);
src/translate_c.zig+117-55
......@@ -11,6 +11,7 @@ const math = std.math;
1111const ast = @import("translate_c/ast.zig");
1212const Node = ast.Node;
1313const Tag = Node.Tag;
14const c_builtins = std.c.builtins;
1415
1516const CallingConvention = std.builtin.CallingConvention;
1617
......@@ -635,7 +636,7 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co
635636 if (has_init) trans_init: {
636637 if (decl_init) |expr| {
637638 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)
639640 else
640641 transExprCoercing(c, scope, expr, .used);
641642 init_node = node_or_error catch |err| switch (err) {
......@@ -1058,6 +1059,10 @@ fn transStmt(
10581059 const compound_literal = @ptrCast(*const clang.CompoundLiteralExpr, stmt);
10591060 return transExpr(c, scope, compound_literal.getInitializer(), result_used);
10601061 },
1062 .GenericSelectionExprClass => {
1063 const gen_sel = @ptrCast(*const clang.GenericSelectionExpr, stmt);
1064 return transExpr(c, scope, gen_sel.getResultExpr(), result_used);
1065 },
10611066 else => {
10621067 return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO implement translation of stmt class {s}", .{@tagName(sc)});
10631068 },
......@@ -1407,7 +1412,7 @@ fn transDeclStmtOne(
14071412
14081413 var init_node = if (decl_init) |expr|
14091414 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)
14111416 else
14121417 try transExprCoercing(c, scope, expr, .used)
14131418 else
......@@ -1522,7 +1527,7 @@ fn transImplicitCastExpr(
15221527 return maybeSuppressResult(c, scope, result_used, ne);
15231528 },
15241529 .BuiltinFnToFnPtr => {
1525 return transExpr(c, scope, sub_expr, result_used);
1530 return transBuiltinFnExpr(c, scope, sub_expr, result_used);
15261531 },
15271532 .ToVoid => {
15281533 // Should only appear in the rhs and lhs of a ConditionalOperator
......@@ -1538,6 +1543,22 @@ fn transImplicitCastExpr(
15381543 }
15391544}
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
15411562fn transBoolExpr(
15421563 c: *Context,
15431564 scope: *Scope,
......@@ -1582,6 +1603,10 @@ fn exprIsNarrowStringLiteral(expr: *const clang.Expr) bool {
15821603 const op_expr = @ptrCast(*const clang.ParenExpr, expr).getSubExpr();
15831604 return exprIsNarrowStringLiteral(op_expr);
15841605 },
1606 .GenericSelectionExprClass => {
1607 const gen_sel = @ptrCast(*const clang.GenericSelectionExpr, expr);
1608 return exprIsNarrowStringLiteral(gen_sel.getResultExpr());
1609 },
15851610 else => return false,
15861611 }
15871612}
......@@ -1733,6 +1758,20 @@ fn transReturnStmt(
17331758 return Tag.@"return".create(c.arena, rhs);
17341759}
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
17361775fn transStringLiteral(
17371776 c: *Context,
17381777 scope: *Scope,
......@@ -1741,19 +1780,14 @@ fn transStringLiteral(
17411780) TransError!Node {
17421781 const kind = stmt.getKind();
17431782 switch (kind) {
1744 .Ascii, .UTF8 => {
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 },
1783 .Ascii, .UTF8 => return transNarrowStringLiteral(c, scope, stmt, result_used),
17521784 .UTF16, .UTF32, .Wide => {
17531785 const str_type = @tagName(stmt.getKind());
17541786 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);
17571791 const decl = try Tag.var_simple.create(c.arena, .{ .name = name, .init = lit_array });
17581792 try scope.appendNode(decl);
17591793 const node = try Tag.identifier.create(c.arena, name);
......@@ -1762,52 +1796,67 @@ fn transStringLiteral(
17621796 }
17631797}
17641798
1765/// Parse the size of an array back out from an ast Node.
1766fn zigArraySize(c: *Context, node: Node) TransError!usize {
1767 if (node.castTag(.array_type)) |array| {
1768 return array.data.len;
1769 }
1770 return error.UnsupportedTranslation;
1799fn getArrayPayload(array_type: Node) ast.Payload.Array.ArrayTypeInfo {
1800 return (array_type.castTag(.array_type) orelse array_type.castTag(.null_sentinel_array_type).?).data;
17711801}
17721802
1773/// Translate a string literal to an array of integers. Used when an
1774/// array is initialized from a string literal. `array_size` is the
1775/// size of the array being initialized. If the string literal is larger
1776/// than the array, truncate the string. If the array is larger than the
1777/// string literal, pad the array with 0's
1778fn transStringLiteralAsArray(
1803/// Translate a string literal that is initializing an array. In general narrow string
1804/// literals become `"<string>".*` or `"<string>"[0..<size>].*` if they need truncation.
1805/// Wide string literals become an array of integers. zero-fillers pad out the array to
1806/// the appropriate length, if necessary.
1807fn transStringLiteralInitializer(
17791808 c: *Context,
17801809 scope: *Scope,
17811810 stmt: *const clang.StringLiteral,
1782 array_size: usize,
1811 array_type: Node,
17831812) 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
17861817 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);
1789 const ty = expr_base.getType().getTypePtr();
1790 const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, ty);
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
1847 assert(array_size > str_length); // If array_size <= str_length, `num_inits == array_size` and we've already returned.
17911848
1792 const elem_type = try transQualType(c, scope, const_arr_ty.getElementType(), expr_base.getBeginLoc());
1793 const arr_type = try Tag.array_type.create(c.arena, .{ .len = array_size, .elem_type = elem_type });
1794 const init_list = try c.arena.alloc(Node, array_size);
1849 const filler_node = try Tag.array_filler.create(c.arena, .{
1850 .type = elem_type,
1851 .filler = Tag.zero_literal.init(),
1852 .count = array_size - str_length,
1853 });
17951854
1796 var i: c_uint = 0;
1797 const kind = stmt.getKind();
1798 const narrow = kind == .Ascii or kind == .UTF8;
1799 while (i < str_length and i < array_size) : (i += 1) {
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);
1855 if (init_node) |some| {
1856 return Tag.array_cat.create(c.arena, .{ .lhs = some, .rhs = filler_node });
1857 } else {
1858 return filler_node;
18051859 }
1806
1807 return Tag.array_init.create(c.arena, .{
1808 .cond = arr_type,
1809 .cases = init_list,
1810 });
18111860}
18121861
18131862/// determine whether `stmt` is a "pointer subtraction expression" - a subtraction where
......@@ -1836,6 +1885,7 @@ fn cIntTypeForEnum(enum_qt: clang.QualType) clang.QualType {
18361885 return enum_decl.getIntegerType();
18371886}
18381887
1888// when modifying this function, make sure to also update std.meta.cast
18391889fn transCCast(
18401890 c: *Context,
18411891 scope: *Scope,
......@@ -2725,6 +2775,10 @@ fn cIsFunctionDeclRef(expr: *const clang.Expr) bool {
27252775 const opcode = un_op.getOpcode();
27262776 return (opcode == .AddrOf or opcode == .Deref) and cIsFunctionDeclRef(un_op.getSubExpr());
27272777 },
2778 .GenericSelectionExprClass => {
2779 const gen_sel = @ptrCast(*const clang.GenericSelectionExpr, expr);
2780 return cIsFunctionDeclRef(gen_sel.getResultExpr());
2781 },
27282782 else => return false,
27292783 }
27302784}
......@@ -3194,11 +3248,11 @@ fn transFloatingLiteral(c: *Context, scope: *Scope, stmt: *const clang.FloatingL
31943248 var dbl = stmt.getValueAsApproximateDouble();
31953249 const is_negative = dbl < 0;
31963250 if (is_negative) dbl = -dbl;
3197 const str = try std.fmt.allocPrint(c.arena, "{d}", .{dbl});
3198 var node = if (dbl == std.math.floor(dbl))
3199 try Tag.integer_literal.create(c.arena, str)
3251 const str = if (dbl == std.math.floor(dbl))
3252 try std.fmt.allocPrint(c.arena, "{d}.0", .{dbl})
32003253 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);
32023256 if (is_negative) node = try Tag.negate.create(c.arena, node);
32033257 return maybeSuppressResult(c, scope, used, node);
32043258}
......@@ -3312,9 +3366,8 @@ fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: Node) !void {
33123366 try c.global_scope.nodes.append(decl_node);
33133367}
33143368
3315/// Translate a qual type for a variable with an initializer. The initializer
3316/// only matters for incomplete arrays, since the size of the array is determined
3317/// by the size of the initializer
3369/// Translate a qualtype for a variable with an initializer. This only matters
3370/// for incomplete arrays, since the initializer determines the size of the array.
33183371fn transQualTypeInitialized(
33193372 c: *Context,
33203373 scope: *Scope,
......@@ -3330,9 +3383,14 @@ fn transQualTypeInitialized(
33303383 switch (decl_init.getStmtClass()) {
33313384 .StringLiteralClass => {
33323385 const string_lit = @ptrCast(*const clang.StringLiteral, decl_init);
3333 const string_lit_size = string_lit.getLength() + 1; // +1 for null terminator
3386 const string_lit_size = string_lit.getLength();
33343387 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 });
33363394 },
33373395 .InitListExprClass => {
33383396 const init_expr = @ptrCast(*const clang.InitListExpr, decl_init);
......@@ -4746,6 +4804,10 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!N
47464804 },
47474805 .Identifier => {
47484806 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 }
47494811 return Tag.identifier.create(c.arena, builtin_typedef_map.get(mangled_name) orelse mangled_name);
47504812 },
47514813 .LParen => {
src/translate_c/ast.zig+83-3
......@@ -40,6 +40,8 @@ pub const Node = extern union {
4040 string_literal,
4141 char_literal,
4242 enum_literal,
43 /// "string"[0..end]
44 string_slice,
4345 identifier,
4446 @"if",
4547 /// if (!operand) break;
......@@ -176,6 +178,7 @@ pub const Node = extern union {
176178 c_pointer,
177179 single_pointer,
178180 array_type,
181 null_sentinel_array_type,
179182
180183 /// @import("std").meta.sizeof(operand)
181184 std_meta_sizeof,
......@@ -334,7 +337,7 @@ pub const Node = extern union {
334337 .std_meta_promoteIntLiteral => Payload.PromoteIntLiteral,
335338 .block => Payload.Block,
336339 .c_pointer, .single_pointer => Payload.Pointer,
337 .array_type => Payload.Array,
340 .array_type, .null_sentinel_array_type => Payload.Array,
338341 .arg_redecl, .alias, .fail_decl => Payload.ArgRedecl,
339342 .log2_int_type => Payload.Log2IntType,
340343 .var_simple, .pub_var_simple => Payload.SimpleVarDecl,
......@@ -342,6 +345,7 @@ pub const Node = extern union {
342345 .array_filler => Payload.ArrayFiller,
343346 .pub_inline_fn => Payload.PubInlineFn,
344347 .field_access => Payload.FieldAccess,
348 .string_slice => Payload.StringSlice,
345349 };
346350 }
347351
......@@ -584,10 +588,12 @@ pub const Payload = struct {
584588
585589 pub const Array = struct {
586590 base: Payload,
587 data: struct {
591 data: ArrayTypeInfo,
592
593 pub const ArrayTypeInfo = struct {
588594 elem_type: Node,
589595 len: usize,
590 },
596 };
591597 };
592598
593599 pub const Pointer = struct {
......@@ -664,6 +670,14 @@ pub const Payload = struct {
664670 radix: Node,
665671 },
666672 };
673
674 pub const StringSlice = struct {
675 base: Payload,
676 data: struct {
677 string: Node,
678 end: usize,
679 },
680 };
667681};
668682
669683/// Converts the nodes into a Zig ast.
......@@ -1015,6 +1029,36 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
10151029 .data = undefined,
10161030 });
10171031 },
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 },
10181062 .fail_decl => {
10191063 const payload = node.castTag(.fail_decl).?.data;
10201064 // pub const name = @compileError(msg);
......@@ -1581,6 +1625,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
15811625 const payload = node.castTag(.array_type).?.data;
15821626 return renderArrayType(c, payload.len, payload.elem_type);
15831627 },
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 },
15841632 .array_filler => {
15851633 const payload = node.castTag(.array_filler).?.data;
15861634
......@@ -1946,6 +1994,36 @@ fn renderArrayType(c: *Context, len: usize, elem_type: Node) !NodeIndex {
19461994 });
19471995}
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
19492027fn addSemicolonIfNeeded(c: *Context, node: Node) !void {
19502028 switch (node.tag()) {
19512029 .warning => unreachable,
......@@ -2014,6 +2092,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
20142092 .integer_literal,
20152093 .float_literal,
20162094 .string_literal,
2095 .string_slice,
20172096 .char_literal,
20182097 .enum_literal,
20192098 .identifier,
......@@ -2035,6 +2114,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
20352114 .func,
20362115 .call,
20372116 .array_type,
2117 .null_sentinel_array_type,
20382118 .bool_to_int,
20392119 .div_exact,
20402120 .byte_offset_of,
src/zig_clang.cpp+5
......@@ -2445,6 +2445,11 @@ struct ZigClangQualType ZigClangFunctionType_getReturnType(const struct ZigClang
24452445 return bitcast(casted->getReturnType());
24462446}
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
24482453bool ZigClangFunctionProtoType_isVariadic(const struct ZigClangFunctionProtoType *self) {
24492454 auto casted = reinterpret_cast<const clang::FunctionProtoType *>(self);
24502455 return casted->isVariadic();
src/zig_clang.h+2
......@@ -1116,6 +1116,8 @@ ZIG_EXTERN_C bool ZigClangFunctionType_getNoReturnAttr(const struct ZigClangFunc
11161116ZIG_EXTERN_C enum ZigClangCallingConv ZigClangFunctionType_getCallConv(const struct ZigClangFunctionType *self);
11171117ZIG_EXTERN_C struct ZigClangQualType ZigClangFunctionType_getReturnType(const struct ZigClangFunctionType *self);
11181118
1119ZIG_EXTERN_C const struct ZigClangExpr *ZigClangGenericSelectionExpr_getResultExpr(const struct ZigClangGenericSelectionExpr *self);
1120
11191121ZIG_EXTERN_C bool ZigClangFunctionProtoType_isVariadic(const struct ZigClangFunctionProtoType *self);
11201122ZIG_EXTERN_C unsigned ZigClangFunctionProtoType_getNumParams(const struct ZigClangFunctionProtoType *self);
11211123ZIG_EXTERN_C struct ZigClangQualType ZigClangFunctionProtoType_getParamType(const struct ZigClangFunctionProtoType *self, unsigned i);
test/cli.zig+11
......@@ -28,6 +28,8 @@ pub fn main() !void {
2828 const zig_exe = try fs.path.resolve(a, &[_][]const u8{zig_exe_rel});
2929
3030 const dir_path = try fs.path.join(a, &[_][]const u8{ cache_root, "clitest" });
31 defer fs.cwd().deleteTree(dir_path) catch {};
32
3133 const TestFn = fn ([]const u8, []const u8) anyerror!void;
3234 const test_fns = [_]TestFn{
3335 testZigInitLib,
......@@ -174,4 +176,13 @@ fn testZigFmt(zig_exe: []const u8, dir_path: []const u8) !void {
174176 const run_result3 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path });
175177 // both files have been formatted, nothing should change now
176178 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');
177188}
test/run_translated_c.zig+57
......@@ -3,6 +3,17 @@ const tests = @import("tests.zig");
33const nl = std.cstr.line_sep;
44
55pub 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
617 cases.add("use global scope for record/enum/typedef type transalation if needed",
718 \\void bar(void);
819 \\void baz(void);
......@@ -1187,4 +1198,50 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
11871198 \\ return 0;
11881199 \\}
11891200 , "");
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 , "");
11901247}
test/stage2/cbe.zig+1-1
......@@ -51,7 +51,7 @@ pub fn addCases(ctx: *TestContext) !void {
5151 \\ _ = printf("Hello, %s!\n", "world");
5252 \\ return 0;
5353 \\}
54 , "Hello, world!\n");
54 , "Hello, world!" ++ std.cstr.line_sep);
5555 }
5656
5757 {
test/standalone.zig+4-1
......@@ -9,7 +9,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
99 cases.add("test/standalone/main_return_error/error_u8.zig");
1010 cases.add("test/standalone/main_return_error/error_u8_non_zero.zig");
1111 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 }
1316 cases.addBuildFile("test/standalone/mix_o_files/build.zig");
1417 cases.addBuildFile("test/standalone/global_linkage/build.zig");
1518 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 {
745745 \\ static const char v2[] = "2.2.2";
746746 \\}
747747 , &[_][]const u8{
748 \\const v2: [6]u8 = [6]u8{
749 \\ '2',
750 \\ '.',
751 \\ '2',
752 \\ '.',
753 \\ '2',
754 \\ 0,
755 \\};
748 \\const v2: [5:0]u8 = "2.2.2".*;
756749 \\pub export fn foo() void {}
757750 });
758751
......@@ -1600,30 +1593,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
16001593 \\static char arr1[] = "hello";
16011594 \\char arr2[] = "hello";
16021595 , &[_][]const u8{
1603 \\pub export var arr0: [6]u8 = [6]u8{
1604 \\ 'h',
1605 \\ 'e',
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 \\};
1596 \\pub export var arr0: [5:0]u8 = "hello".*;
1597 \\pub var arr1: [5:0]u8 = "hello".*;
1598 \\pub export var arr2: [5:0]u8 = "hello".*;
16271599 });
16281600
16291601 cases.add("array initializer expr",
......@@ -2456,7 +2428,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
24562428 \\ b: c_int,
24572429 \\};
24582430 \\pub extern var a: struct_Foo;
2459 \\pub export var b: f32 = 2;
2431 \\pub export var b: f32 = 2.0;
24602432 \\pub export fn foo() void {
24612433 \\ var c: [*c]struct_Foo = undefined;
24622434 \\ _ = a.b;
......@@ -3020,17 +2992,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
30202992 \\pub extern fn fn_bool(x: bool) void;
30212993 \\pub extern fn fn_ptr(x: ?*c_void) void;
30222994 \\pub export fn call() void {
3023 \\ fn_int(@floatToInt(c_int, 3));
3024 \\ fn_int(@floatToInt(c_int, 3));
3025 \\ fn_int(@floatToInt(c_int, 3));
2995 \\ fn_int(@floatToInt(c_int, 3.0));
2996 \\ fn_int(@floatToInt(c_int, 3.0));
2997 \\ fn_int(@floatToInt(c_int, 3.0));
30262998 \\ fn_int(@as(c_int, 1094861636));
30272999 \\ fn_f32(@intToFloat(f32, @as(c_int, 3)));
30283000 \\ fn_f64(@intToFloat(f64, @as(c_int, 3)));
30293001 \\ fn_char(@bitCast(u8, @truncate(i8, @as(c_int, '3'))));
30303002 \\ fn_char(@bitCast(u8, @truncate(i8, @as(c_int, '\x01'))));
30313003 \\ fn_char(@bitCast(u8, @truncate(i8, @as(c_int, 0))));
3032 \\ fn_f32(3);
3033 \\ fn_f64(3);
3004 \\ fn_f32(3.0);
3005 \\ fn_f64(3.0);
30343006 \\ fn_bool(@as(c_int, 123) != 0);
30353007 \\ fn_bool(@as(c_int, 0) != 0);
30363008 \\ fn_bool(@ptrToInt(fn_int) != 0);
......@@ -3418,4 +3390,56 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
34183390 \\pub const MAY_NEED_PROMOTION_HEX = @import("std").meta.promoteIntLiteral(c_int, 0x80000000, .hexadecimal);
34193391 \\pub const MAY_NEED_PROMOTION_OCT = @import("std").meta.promoteIntLiteral(c_int, 0o20000000000, .octal);
34203392 });
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 });
34213445}