| author | |
| committer | |
| log | f5aca4a6a1ba867d3bc343a3740454468a7eff13 |
| tree | 48f43c64fa9ce61c294e3062758ec35bb0c9f544 |
| parent | 66245ac834969b84548ec325ee20a6910456e5ec |
| parent | 96ae451bbe78cd35a62e00e3fb48a32f24ebd315 |
I need the enum arrays that were just merged into master.44 files changed, 7419 insertions(+), 1697 deletions(-)
CMakeLists.txt+7| ... | ... | @@ -564,7 +564,14 @@ set(ZIG_STAGE2_SOURCES |
| 564 | 564 | "${CMAKE_SOURCE_DIR}/src/link/Coff.zig" |
| 565 | 565 | "${CMAKE_SOURCE_DIR}/src/link/Elf.zig" |
| 566 | 566 | "${CMAKE_SOURCE_DIR}/src/link/MachO.zig" |
| 567 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig" | |
| 568 | "${CMAKE_SOURCE_DIR}/src/link/MachO/CodeSignature.zig" | |
| 569 | "${CMAKE_SOURCE_DIR}/src/link/MachO/DebugSymbols.zig" | |
| 570 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig" | |
| 567 | 571 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig" |
| 572 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Zld.zig" | |
| 573 | "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig" | |
| 574 | "${CMAKE_SOURCE_DIR}/src/link/MachO/commands.zig" | |
| 568 | 575 | "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig" |
| 569 | 576 | "${CMAKE_SOURCE_DIR}/src/link/C/zig.h" |
| 570 | 577 | "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin" |
lib/std/bit_set.zig+21-7| ... | ... | @@ -176,7 +176,7 @@ pub fn IntegerBitSet(comptime size: u16) type { |
| 176 | 176 | /// The default options (.{}) will iterate indices of set bits in |
| 177 | 177 | /// ascending order. Modifications to the underlying bit set may |
| 178 | 178 | /// or may not be observed by the iterator. |
| 179 | pub fn iterator(self: *const Self, comptime options: IteratorOptions) Iterator(options.direction) { | |
| 179 | pub fn iterator(self: *const Self, comptime options: IteratorOptions) Iterator(options) { | |
| 180 | 180 | return .{ |
| 181 | 181 | .bits_remain = switch (options.kind) { |
| 182 | 182 | .set => self.mask, |
| ... | ... | @@ -185,7 +185,11 @@ pub fn IntegerBitSet(comptime size: u16) type { |
| 185 | 185 | }; |
| 186 | 186 | } |
| 187 | 187 | |
| 188 | fn Iterator(comptime direction: IteratorOptions.Direction) type { | |
| 188 | pub fn Iterator(comptime options: IteratorOptions) type { | |
| 189 | return SingleWordIterator(options.direction); | |
| 190 | } | |
| 191 | ||
| 192 | fn SingleWordIterator(comptime direction: IteratorOptions.Direction) type { | |
| 189 | 193 | return struct { |
| 190 | 194 | const IterSelf = @This(); |
| 191 | 195 | // all bits which have not yet been iterated over |
| ... | ... | @@ -425,8 +429,12 @@ pub fn ArrayBitSet(comptime MaskIntType: type, comptime size: usize) type { |
| 425 | 429 | /// The default options (.{}) will iterate indices of set bits in |
| 426 | 430 | /// ascending order. Modifications to the underlying bit set may |
| 427 | 431 | /// 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); | |
| 430 | 438 | } |
| 431 | 439 | |
| 432 | 440 | fn maskBit(index: usize) MaskInt { |
| ... | ... | @@ -700,11 +708,15 @@ pub const DynamicBitSetUnmanaged = struct { |
| 700 | 708 | /// ascending order. Modifications to the underlying bit set may |
| 701 | 709 | /// or may not be observed by the iterator. Resizing the underlying |
| 702 | 710 | /// bit set invalidates the iterator. |
| 703 | pub fn iterator(self: *const Self, comptime options: IteratorOptions) BitSetIterator(MaskInt, options) { | |
| 711 | pub fn iterator(self: *const Self, comptime options: IteratorOptions) Iterator(options) { | |
| 704 | 712 | const num_masks = numMasks(self.bit_length); |
| 705 | 713 | const padding_bits = num_masks * @bitSizeOf(MaskInt) - self.bit_length; |
| 706 | 714 | const last_item_mask = (~@as(MaskInt, 0)) >> @intCast(ShiftInt, padding_bits); |
| 707 | return BitSetIterator(MaskInt, options).init(self.masks[0..num_masks], last_item_mask); | |
| 715 | return Iterator(options).init(self.masks[0..num_masks], last_item_mask); | |
| 716 | } | |
| 717 | ||
| 718 | pub fn Iterator(comptime options: IteratorOptions) type { | |
| 719 | return BitSetIterator(MaskInt, options); | |
| 708 | 720 | } |
| 709 | 721 | |
| 710 | 722 | fn maskBit(index: usize) MaskInt { |
| ... | ... | @@ -858,9 +870,11 @@ pub const DynamicBitSet = struct { |
| 858 | 870 | /// ascending order. Modifications to the underlying bit set may |
| 859 | 871 | /// or may not be observed by the iterator. Resizing the underlying |
| 860 | 872 | /// bit set invalidates the iterator. |
| 861 | pub fn iterator(self: *Self, comptime options: IteratorOptions) BitSetIterator(MaskInt, options) { | |
| 873 | pub fn iterator(self: *Self, comptime options: IteratorOptions) Iterator(options) { | |
| 862 | 874 | return self.unmanaged.iterator(options); |
| 863 | 875 | } |
| 876 | ||
| 877 | pub const Iterator = DynamicBitSetUnmanaged.Iterator; | |
| 864 | 878 | }; |
| 865 | 879 | |
| 866 | 880 | /// Options for configuring an iterator over a bit set |
lib/std/c/builtins.zig+7-1| ... | ... | @@ -140,7 +140,7 @@ pub fn __builtin_object_size(ptr: ?*const c_void, ty: c_int) callconv(.Inline) u |
| 140 | 140 | // If it is not possible to determine which objects ptr points to at compile time, |
| 141 | 141 | // __builtin_object_size should return (size_t) -1 for type 0 or 1 and (size_t) 0 |
| 142 | 142 | // for type 2 or 3. |
| 143 | if (ty == 0 or ty == 1) return @bitCast(usize, -@as(c_long, 1)); | |
| 143 | if (ty == 0 or ty == 1) return @bitCast(usize, -@as(isize, 1)); | |
| 144 | 144 | if (ty == 2 or ty == 3) return 0; |
| 145 | 145 | unreachable; |
| 146 | 146 | } |
| ... | ... | @@ -188,3 +188,9 @@ pub fn __builtin_memcpy( |
| 188 | 188 | pub fn __builtin_expect(expr: c_long, c: c_long) callconv(.Inline) c_long { |
| 189 | 189 | return expr; |
| 190 | 190 | } |
| 191 | ||
| 192 | // __builtin_alloca_with_align is not currently implemented. | |
| 193 | // It is used in a run-translated-c test and a test-translate-c test to ensure that non-implemented | |
| 194 | // builtins are correctly demoted. If you implement __builtin_alloca_with_align, please update the | |
| 195 | // run-translated-c test and the test-translate-c test to use a different non-implemented builtin. | |
| 196 | // pub fn __builtin_alloca_with_align(size: usize, alignment: usize) callconv(.Inline) *c_void {} |
lib/std/crypto.zig+12-2| ... | ... | @@ -24,8 +24,12 @@ pub const aead = struct { |
| 24 | 24 | pub const Gimli = @import("crypto/gimli.zig").Aead; |
| 25 | 25 | |
| 26 | 26 | 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; | |
| 29 | 33 | }; |
| 30 | 34 | |
| 31 | 35 | pub const isap = @import("crypto/isap.zig"); |
| ... | ... | @@ -119,8 +123,14 @@ pub const sign = struct { |
| 119 | 123 | pub const stream = struct { |
| 120 | 124 | pub const chacha = struct { |
| 121 | 125 | pub const ChaCha20IETF = @import("crypto/chacha20.zig").ChaCha20IETF; |
| 126 | pub const ChaCha12IETF = @import("crypto/chacha20.zig").ChaCha12IETF; | |
| 127 | pub const ChaCha8IETF = @import("crypto/chacha20.zig").ChaCha8IETF; | |
| 122 | 128 | pub const ChaCha20With64BitNonce = @import("crypto/chacha20.zig").ChaCha20With64BitNonce; |
| 129 | pub const ChaCha12With64BitNonce = @import("crypto/chacha20.zig").ChaCha12With64BitNonce; | |
| 130 | pub const ChaCha8With64BitNonce = @import("crypto/chacha20.zig").ChaCha8With64BitNonce; | |
| 123 | 131 | pub const XChaCha20IETF = @import("crypto/chacha20.zig").XChaCha20IETF; |
| 132 | pub const XChaCha12IETF = @import("crypto/chacha20.zig").XChaCha12IETF; | |
| 133 | pub const XChaCha8IETF = @import("crypto/chacha20.zig").XChaCha8IETF; | |
| 124 | 134 | }; |
| 125 | 135 | |
| 126 | 136 | pub const salsa = struct { |
lib/std/crypto/benchmark.zig+1| ... | ... | @@ -202,6 +202,7 @@ pub fn benchmarkBatchSignatureVerification(comptime Signature: anytype, comptime |
| 202 | 202 | const aeads = [_]Crypto{ |
| 203 | 203 | Crypto{ .ty = crypto.aead.chacha_poly.ChaCha20Poly1305, .name = "chacha20Poly1305" }, |
| 204 | 204 | Crypto{ .ty = crypto.aead.chacha_poly.XChaCha20Poly1305, .name = "xchacha20Poly1305" }, |
| 205 | Crypto{ .ty = crypto.aead.chacha_poly.XChaCha8Poly1305, .name = "xchacha8Poly1305" }, | |
| 205 | 206 | Crypto{ .ty = crypto.aead.salsa_poly.XSalsa20Poly1305, .name = "xsalsa20Poly1305" }, |
| 206 | 207 | Crypto{ .ty = crypto.aead.Gimli, .name = "gimli-aead" }, |
| 207 | 208 | Crypto{ .ty = crypto.aead.aegis.Aegis128L, .name = "aegis-128l" }, |
lib/std/crypto/chacha20.zig+598-571| ... | ... | @@ -15,286 +15,357 @@ const Vector = std.meta.Vector; |
| 15 | 15 | const Poly1305 = std.crypto.onetimeauth.Poly1305; |
| 16 | 16 | const Error = std.crypto.Error; |
| 17 | 17 | |
| 18 | /// IETF-variant of the ChaCha20 stream cipher, as designed for TLS. | |
| 19 | pub 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. | |
| 24 | pub 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. | |
| 29 | pub const ChaCha8IETF = ChaChaIETF(8); | |
| 30 | ||
| 31 | /// Original ChaCha20 stream cipher. | |
| 32 | pub 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. | |
| 37 | pub 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. | |
| 42 | pub const ChaCha8With64BitNonce = ChaChaWith64BitNonce(8); | |
| 43 | ||
| 44 | /// XChaCha20 (nonce-extended version of the IETF ChaCha20 variant) stream cipher | |
| 45 | pub 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. | |
| 50 | pub 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. | |
| 55 | pub const XChaCha8IETF = XChaChaIETF(8); | |
| 56 | ||
| 57 | /// ChaCha20-Poly1305 authenticated cipher, as designed for TLS | |
| 58 | pub 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. | |
| 63 | pub 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. | |
| 68 | pub const ChaCha8Poly1305 = ChaChaPoly1305(8); | |
| 69 | ||
| 70 | /// XChaCha20-Poly1305 authenticated cipher | |
| 71 | pub 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. | |
| 76 | pub 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. | |
| 81 | pub const XChaCha8Poly1305 = XChaChaPoly1305(8); | |
| 82 | ||
| 18 | 83 | // Vectorized implementation of the core function |
| 19 | const 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 | } | |
| 84 | fn 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 | } | |
| 38 | 104 | |
| 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 | } | |
| 81 | 148 | } |
| 82 | } | |
| 83 | 149 | |
| 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 | } | |
| 91 | 158 | } |
| 92 | } | |
| 93 | 159 | |
| 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 | } | |
| 100 | 166 | |
| 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; | |
| 120 | 188 | } |
| 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 | } | |
| 133 | 200 | } |
| 134 | 201 | } |
| 135 | } | |
| 136 | 202 | |
| 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; | |
| 141 | 221 | } |
| 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 | } | |
| 157 | 224 | |
| 158 | 225 | // Non-vectorized implementation of the core function |
| 159 | const 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 | }; | |
| 226 | fn 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 | } | |
| 184 | 245 | |
| 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, | |
| 191 | 251 | }; |
| 192 | } | |
| 193 | 252 | |
| 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 | } | |
| 207 | 261 | |
| 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 | } | |
| 219 | 288 | } |
| 220 | 289 | } |
| 221 | } | |
| 222 | 290 | |
| 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 | } | |
| 230 | 299 | } |
| 231 | } | |
| 232 | 300 | |
| 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 | } | |
| 237 | 306 | } |
| 238 | } | |
| 239 | 307 | |
| 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; | |
| 259 | 329 | } |
| 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 | } | |
| 272 | 341 | } |
| 273 | 342 | } |
| 274 | } | |
| 275 | 343 | |
| 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; | |
| 280 | 362 | } |
| 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 | } | |
| 296 | 365 | |
| 297 | const ChaCha20Impl = if (std.Target.current.cpu.arch == .x86_64) ChaCha20VecImpl else ChaCha20NonVecImpl; | |
| 366 | fn ChaChaImpl(comptime rounds_nb: usize) type { | |
| 367 | return if (std.Target.current.cpu.arch == .x86_64) ChaChaVecImpl(rounds_nb) else ChaChaNonVecImpl(rounds_nb); | |
| 368 | } | |
| 298 | 369 | |
| 299 | 370 | fn keyToWords(key: [32]u8) [8]u32 { |
| 300 | 371 | var k: [8]u32 = undefined; |
| ... | ... | @@ -305,68 +376,239 @@ fn keyToWords(key: [32]u8) [8]u32 { |
| 305 | 376 | return k; |
| 306 | 377 | } |
| 307 | 378 | |
| 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. | |
| 316 | pub 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. | |
| 333 | pub 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; | |
| 379 | fn 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 | ||
| 389 | fn 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 | ||
| 413 | fn 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 | } | |
| 363 | 453 | } |
| 364 | 454 | } |
| 455 | ChaChaImpl(rounds_nb).chacha20Xor(out[cursor..], in[cursor..], k, c); | |
| 456 | } | |
| 457 | }; | |
| 458 | } | |
| 459 | ||
| 460 | fn 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 | ||
| 477 | fn 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 | ||
| 563 | fn XChaChaPoly1305(comptime rounds_nb: usize) type { | |
| 564 | return struct { | |
| 565 | pub const tag_length = 16; | |
| 566 | pub const nonce_length = 24; | |
| 567 | pub const key_length = 32; | |
| 568 | ||
| 569 | /// c: ciphertext: output buffer should be of size m.len | |
| 570 | /// tag: authentication tag: output MAC | |
| 571 | /// m: message | |
| 572 | /// ad: Associated Data | |
| 573 | /// npub: public nonce | |
| 574 | /// k: private key | |
| 575 | pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void { | |
| 576 | const extended = extend(k, npub, rounds_nb); | |
| 577 | return ChaChaPoly1305(rounds_nb).encrypt(c, tag, m, ad, extended.nonce, extended.key); | |
| 365 | 578 | } |
| 366 | 579 | |
| 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 | ||
| 593 | test "chacha20 AEAD API" { | |
| 594 | const aeads = [_]type{ ChaCha20Poly1305, XChaCha20Poly1305 }; | |
| 595 | const m = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."; | |
| 596 | const ad = "Additional data"; | |
| 597 | ||
| 598 | inline for (aeads) |aead| { | |
| 599 | const key = [_]u8{69} ** aead.key_length; | |
| 600 | const nonce = [_]u8{42} ** aead.nonce_length; | |
| 601 | var c: [m.len]u8 = undefined; | |
| 602 | var tag: [aead.tag_length]u8 = undefined; | |
| 603 | var out: [m.len]u8 = undefined; | |
| 604 | ||
| 605 | aead.encrypt(c[0..], tag[0..], m, ad, nonce, key); | |
| 606 | try aead.decrypt(out[0..], c[0..], tag, ad[0..], nonce, key); | |
| 607 | testing.expectEqualSlices(u8, out[0..], m); | |
| 608 | c[0] += 1; | |
| 609 | testing.expectError(error.AuthenticationFailed, aead.decrypt(out[0..], c[0..], tag, ad[0..], nonce, key)); | |
| 368 | 610 | } |
| 369 | }; | |
| 611 | } | |
| 370 | 612 | |
| 371 | 613 | // https://tools.ietf.org/html/rfc7539#section-2.4.2 |
| 372 | 614 | test "crypto.chacha20 test vector sunscreen" { |
| ... | ... | @@ -387,7 +629,7 @@ test "crypto.chacha20 test vector sunscreen" { |
| 387 | 629 | 0xb4, 0x0b, 0x8e, 0xed, 0xf2, 0x78, 0x5e, 0x42, |
| 388 | 630 | 0x87, 0x4d, |
| 389 | 631 | }; |
| 390 | const input = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."; | |
| 632 | const m = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."; | |
| 391 | 633 | var result: [114]u8 = undefined; |
| 392 | 634 | const key = [_]u8{ |
| 393 | 635 | 0, 1, 2, 3, 4, 5, 6, 7, |
| ... | ... | @@ -401,13 +643,12 @@ test "crypto.chacha20 test vector sunscreen" { |
| 401 | 643 | 0, 0, 0, 0, |
| 402 | 644 | }; |
| 403 | 645 | |
| 404 | ChaCha20IETF.xor(result[0..], input[0..], 1, key, nonce); | |
| 646 | ChaCha20IETF.xor(result[0..], m[0..], 1, key, nonce); | |
| 405 | 647 | testing.expectEqualSlices(u8, &expected_result, &result); |
| 406 | 648 | |
| 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); | |
| 411 | 652 | } |
| 412 | 653 | |
| 413 | 654 | // https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7 |
| ... | ... | @@ -422,7 +663,7 @@ test "crypto.chacha20 test vector 1" { |
| 422 | 663 | 0x6a, 0x43, 0xb8, 0xf4, 0x15, 0x18, 0xa1, 0x1c, |
| 423 | 664 | 0xc3, 0x87, 0xb6, 0x69, 0xb2, 0xee, 0x65, 0x86, |
| 424 | 665 | }; |
| 425 | const input = [_]u8{ | |
| 666 | const m = [_]u8{ | |
| 426 | 667 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 427 | 668 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 428 | 669 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| ... | ... | @@ -441,7 +682,7 @@ test "crypto.chacha20 test vector 1" { |
| 441 | 682 | }; |
| 442 | 683 | const nonce = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0 }; |
| 443 | 684 | |
| 444 | ChaCha20With64BitNonce.xor(result[0..], input[0..], 0, key, nonce); | |
| 685 | ChaCha20With64BitNonce.xor(result[0..], m[0..], 0, key, nonce); | |
| 445 | 686 | testing.expectEqualSlices(u8, &expected_result, &result); |
| 446 | 687 | } |
| 447 | 688 | |
| ... | ... | @@ -456,7 +697,7 @@ test "crypto.chacha20 test vector 2" { |
| 456 | 697 | 0x53, 0xd7, 0x92, 0xb1, 0xc4, 0x3f, 0xea, 0x81, |
| 457 | 698 | 0x7e, 0x9a, 0xd2, 0x75, 0xae, 0x54, 0x69, 0x63, |
| 458 | 699 | }; |
| 459 | const input = [_]u8{ | |
| 700 | const m = [_]u8{ | |
| 460 | 701 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 461 | 702 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 462 | 703 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| ... | ... | @@ -475,7 +716,7 @@ test "crypto.chacha20 test vector 2" { |
| 475 | 716 | }; |
| 476 | 717 | const nonce = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0 }; |
| 477 | 718 | |
| 478 | ChaCha20With64BitNonce.xor(result[0..], input[0..], 0, key, nonce); | |
| 719 | ChaCha20With64BitNonce.xor(result[0..], m[0..], 0, key, nonce); | |
| 479 | 720 | testing.expectEqualSlices(u8, &expected_result, &result); |
| 480 | 721 | } |
| 481 | 722 | |
| ... | ... | @@ -490,7 +731,7 @@ test "crypto.chacha20 test vector 3" { |
| 490 | 731 | 0x52, 0x77, 0x06, 0x2e, 0xb7, 0xa0, 0x43, 0x3e, |
| 491 | 732 | 0x44, 0x5f, 0x41, 0xe3, |
| 492 | 733 | }; |
| 493 | const input = [_]u8{ | |
| 734 | const m = [_]u8{ | |
| 494 | 735 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 495 | 736 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 496 | 737 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| ... | ... | @@ -509,7 +750,7 @@ test "crypto.chacha20 test vector 3" { |
| 509 | 750 | }; |
| 510 | 751 | const nonce = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 1 }; |
| 511 | 752 | |
| 512 | ChaCha20With64BitNonce.xor(result[0..], input[0..], 0, key, nonce); | |
| 753 | ChaCha20With64BitNonce.xor(result[0..], m[0..], 0, key, nonce); | |
| 513 | 754 | testing.expectEqualSlices(u8, &expected_result, &result); |
| 514 | 755 | } |
| 515 | 756 | |
| ... | ... | @@ -524,7 +765,7 @@ test "crypto.chacha20 test vector 4" { |
| 524 | 765 | 0x5d, 0xdc, 0x49, 0x7a, 0x0b, 0x46, 0x6e, 0x7d, |
| 525 | 766 | 0x6b, 0xbd, 0xb0, 0x04, 0x1b, 0x2f, 0x58, 0x6b, |
| 526 | 767 | }; |
| 527 | const input = [_]u8{ | |
| 768 | const m = [_]u8{ | |
| 528 | 769 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 529 | 770 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 530 | 771 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| ... | ... | @@ -543,7 +784,7 @@ test "crypto.chacha20 test vector 4" { |
| 543 | 784 | }; |
| 544 | 785 | const nonce = [_]u8{ 1, 0, 0, 0, 0, 0, 0, 0 }; |
| 545 | 786 | |
| 546 | ChaCha20With64BitNonce.xor(result[0..], input[0..], 0, key, nonce); | |
| 787 | ChaCha20With64BitNonce.xor(result[0..], m[0..], 0, key, nonce); | |
| 547 | 788 | testing.expectEqualSlices(u8, &expected_result, &result); |
| 548 | 789 | } |
| 549 | 790 | |
| ... | ... | @@ -585,7 +826,7 @@ test "crypto.chacha20 test vector 5" { |
| 585 | 826 | 0x87, 0x46, 0xd4, 0x52, 0x4d, 0x38, 0x40, 0x7a, |
| 586 | 827 | 0x6d, 0xeb, 0x3a, 0xb7, 0x8f, 0xab, 0x78, 0xc9, |
| 587 | 828 | }; |
| 588 | const input = [_]u8{ | |
| 829 | const m = [_]u8{ | |
| 589 | 830 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 590 | 831 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 591 | 832 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| ... | ... | @@ -615,147 +856,14 @@ test "crypto.chacha20 test vector 5" { |
| 615 | 856 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, |
| 616 | 857 | }; |
| 617 | 858 | |
| 618 | ChaCha20With64BitNonce.xor(result[0..], input[0..], 0, key, nonce); | |
| 859 | ChaCha20With64BitNonce.xor(result[0..], m[0..], 0, key, nonce); | |
| 619 | 860 | testing.expectEqualSlices(u8, &expected_result, &result); |
| 620 | 861 | } |
| 621 | 862 | |
| 622 | pub const chacha20poly1305_tag_length = 16; | |
| 623 | ||
| 624 | fn 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 | ||
| 655 | fn 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. | |
| 660 | fn 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. | |
| 706 | fn 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 | ||
| 714 | fn 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 | ||
| 724 | pub 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 | ||
| 731 | pub const xchacha20poly1305_tag_length = 16; | |
| 732 | ||
| 733 | fn 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 | ||
| 738 | fn 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. | |
| 744 | fn 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. | |
| 750 | fn 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 | ||
| 755 | 863 | test "seal" { |
| 756 | 864 | { |
| 757 | const plaintext = ""; | |
| 758 | const data = ""; | |
| 865 | const m = ""; | |
| 866 | const ad = ""; | |
| 759 | 867 | const key = [_]u8{ |
| 760 | 868 | 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, |
| 761 | 869 | 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, |
| ... | ... | @@ -764,11 +872,11 @@ test "seal" { |
| 764 | 872 | const exp_out = [_]u8{ 0xa0, 0x78, 0x4d, 0x7a, 0x47, 0x16, 0xf3, 0xfe, 0xb4, 0xf6, 0x4e, 0x7f, 0x4b, 0x39, 0xbf, 0x4 }; |
| 765 | 873 | |
| 766 | 874 | var out: [exp_out.len]u8 = undefined; |
| 767 | chacha20poly1305Seal(out[0..], plaintext, data, key, nonce); | |
| 875 | ChaCha20Poly1305.encrypt(out[0..m.len], out[m.len..], m, ad, nonce, key); | |
| 768 | 876 | testing.expectEqualSlices(u8, exp_out[0..], out[0..]); |
| 769 | 877 | } |
| 770 | 878 | { |
| 771 | const plaintext = [_]u8{ | |
| 879 | const m = [_]u8{ | |
| 772 | 880 | 0x4c, 0x61, 0x64, 0x69, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x47, 0x65, 0x6e, 0x74, 0x6c, |
| 773 | 881 | 0x65, 0x6d, 0x65, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x61, 0x73, |
| 774 | 882 | 0x73, 0x20, 0x6f, 0x66, 0x20, 0x27, 0x39, 0x39, 0x3a, 0x20, 0x49, 0x66, 0x20, 0x49, 0x20, 0x63, |
| ... | ... | @@ -778,7 +886,7 @@ test "seal" { |
| 778 | 886 | 0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69, |
| 779 | 887 | 0x74, 0x2e, |
| 780 | 888 | }; |
| 781 | const data = [_]u8{ 0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7 }; | |
| 889 | const ad = [_]u8{ 0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7 }; | |
| 782 | 890 | const key = [_]u8{ |
| 783 | 891 | 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, |
| 784 | 892 | 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, |
| ... | ... | @@ -797,15 +905,15 @@ test "seal" { |
| 797 | 905 | }; |
| 798 | 906 | |
| 799 | 907 | var out: [exp_out.len]u8 = undefined; |
| 800 | chacha20poly1305Seal(out[0..], plaintext[0..], data[0..], key, nonce); | |
| 908 | ChaCha20Poly1305.encrypt(out[0..m.len], out[m.len..], m[0..], ad[0..], nonce, key); | |
| 801 | 909 | testing.expectEqualSlices(u8, exp_out[0..], out[0..]); |
| 802 | 910 | } |
| 803 | 911 | } |
| 804 | 912 | |
| 805 | 913 | test "open" { |
| 806 | 914 | { |
| 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 = ""; | |
| 809 | 917 | const key = [_]u8{ |
| 810 | 918 | 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, |
| 811 | 919 | 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, |
| ... | ... | @@ -814,11 +922,11 @@ test "open" { |
| 814 | 922 | const exp_out = ""; |
| 815 | 923 | |
| 816 | 924 | var out: [exp_out.len]u8 = undefined; |
| 817 | try chacha20poly1305Open(out[0..], ciphertext[0..], data, key, nonce); | |
| 925 | try ChaCha20Poly1305.decrypt(out[0..], c[0..exp_out.len], c[exp_out.len..].*, ad[0..], nonce, key); | |
| 818 | 926 | testing.expectEqualSlices(u8, exp_out[0..], out[0..]); |
| 819 | 927 | } |
| 820 | 928 | { |
| 821 | const ciphertext = [_]u8{ | |
| 929 | const c = [_]u8{ | |
| 822 | 930 | 0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb, 0x7b, 0x86, 0xaf, 0xbc, 0x53, 0xef, 0x7e, 0xc2, |
| 823 | 931 | 0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x8, 0xfe, 0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6, |
| 824 | 932 | 0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67, 0x12, 0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b, |
| ... | ... | @@ -829,7 +937,7 @@ test "open" { |
| 829 | 937 | 0x61, 0x16, 0x1a, 0xe1, 0xb, 0x59, 0x4f, 0x9, 0xe2, 0x6a, 0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60, |
| 830 | 938 | 0x6, 0x91, |
| 831 | 939 | }; |
| 832 | const data = [_]u8{ 0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7 }; | |
| 940 | const ad = [_]u8{ 0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7 }; | |
| 833 | 941 | const key = [_]u8{ |
| 834 | 942 | 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, |
| 835 | 943 | 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, |
| ... | ... | @@ -847,126 +955,45 @@ test "open" { |
| 847 | 955 | }; |
| 848 | 956 | |
| 849 | 957 | var out: [exp_out.len]u8 = undefined; |
| 850 | try chacha20poly1305Open(out[0..], ciphertext[0..], data[0..], key, nonce); | |
| 958 | try ChaCha20Poly1305.decrypt(out[0..], c[0..exp_out.len], c[exp_out.len..].*, ad[0..], nonce, key); | |
| 851 | 959 | testing.expectEqualSlices(u8, exp_out[0..], out[0..]); |
| 852 | 960 | |
| 853 | 961 | // 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)); | |
| 860 | 968 | var bad_key = key; |
| 861 | 969 | bad_key[0] ^= 1; |
| 862 | testing.expectError(error.AuthenticationFailed, chacha20poly1305Open(out[0..], ciphertext[0..], data[0..], bad_key, nonce)); | |
| 970 | testing.expectError(error.AuthenticationFailed, ChaCha20Poly1305.decrypt(out[0..], c[0..out.len], c[out.len..].*, ad[0..], nonce, bad_key)); | |
| 863 | 971 | var bad_nonce = nonce; |
| 864 | 972 | 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)); | |
| 869 | 974 | } |
| 870 | 975 | } |
| 871 | 976 | |
| 872 | 977 | test "crypto.xchacha20" { |
| 873 | 978 | const key = [_]u8{69} ** 32; |
| 874 | 979 | const nonce = [_]u8{42} ** 24; |
| 875 | const input = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."; | |
| 980 | const m = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."; | |
| 876 | 981 | { |
| 877 | var ciphertext: [input.len]u8 = undefined; | |
| 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"); | |
| 881 | 986 | } |
| 882 | 987 | { |
| 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 | ||
| 896 | pub 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 | ||
| 925 | pub 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 | ||
| 954 | test "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)); | |
| 971 | 998 | } |
| 972 | 999 | } |
lib/std/crypto/pbkdf2.zig+67-70| ... | ... | @@ -20,20 +20,20 @@ const Error = std.crypto.Error; |
| 20 | 20 | // pseudorandom function. See Appendix B.1 for further discussion.) |
| 21 | 21 | // PBKDF2 is recommended for new applications. |
| 22 | 22 | // |
| 23 | // PBKDF2 (P, S, c, dkLen) | |
| 23 | // PBKDF2 (P, S, c, dk_len) | |
| 24 | 24 | // |
| 25 | // Options: PRF underlying pseudorandom function (hLen | |
| 25 | // Options: PRF underlying pseudorandom function (h_len | |
| 26 | 26 | // denotes the length in octets of the |
| 27 | 27 | // pseudorandom function output) |
| 28 | 28 | // |
| 29 | 29 | // Input: P password, an octet string |
| 30 | 30 | // S salt, an octet string |
| 31 | 31 | // 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 | |
| 33 | 33 | // key, a positive integer, at most |
| 34 | // (2^32 - 1) * hLen | |
| 34 | // (2^32 - 1) * h_len | |
| 35 | 35 | // |
| 36 | // Output: DK derived key, a dkLen-octet string | |
| 36 | // Output: DK derived key, a dk_len-octet string | |
| 37 | 37 | |
| 38 | 38 | // Based on Apple's CommonKeyDerivation, based originally on code by Damien Bergamini. |
| 39 | 39 | |
| ... | ... | @@ -41,7 +41,7 @@ const Error = std.crypto.Error; |
| 41 | 41 | /// |
| 42 | 42 | /// PBKDF2 is defined in RFC 2898, and is a recommendation of NIST SP 800-132. |
| 43 | 43 | /// |
| 44 | /// derivedKey: Slice of appropriate size for generated key. Generally 16 or 32 bytes in length. | |
| 44 | /// dk: Slice of appropriate size for generated key. Generally 16 or 32 bytes in length. | |
| 45 | 45 | /// May be uninitialized. All bytes will be overwritten. |
| 46 | 46 | /// Maximum size is `maxInt(u32) * Hash.digest_length` |
| 47 | 47 | /// It is a programming error to pass buffer longer than the maximum size. |
| ... | ... | @@ -52,43 +52,38 @@ const Error = std.crypto.Error; |
| 52 | 52 | /// |
| 53 | 53 | /// rounds: Iteration count. Must be greater than 0. Common values range from 1,000 to 100,000. |
| 54 | 54 | /// Larger iteration counts improve security by increasing the time required to compute |
| 55 | /// the derivedKey. It is common to tune this parameter to achieve approximately 100ms. | |
| 55 | /// the dk. It is common to tune this parameter to achieve approximately 100ms. | |
| 56 | 56 | /// |
| 57 | 57 | /// Prf: Pseudo-random function to use. A common choice is `std.crypto.auth.hmac.HmacSha256`. |
| 58 | pub fn pbkdf2(derivedKey: []u8, password: []const u8, salt: []const u8, rounds: u32, comptime Prf: type) Error!void { | |
| 58 | pub fn pbkdf2(dk: []u8, password: []const u8, salt: []const u8, rounds: u32, comptime Prf: type) Error!void { | |
| 59 | 59 | if (rounds < 1) return error.WeakParameters; |
| 60 | 60 | |
| 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); | |
| 64 | 64 | |
| 65 | 65 | // FromSpec: |
| 66 | 66 | // |
| 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 | |
| 68 | 68 | // stop. |
| 69 | 69 | // |
| 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 | |
| 72 | 72 | return error.OutputTooLong; |
| 73 | 73 | } |
| 74 | 74 | |
| 75 | 75 | // FromSpec: |
| 76 | 76 | // |
| 77 | // 2. Let l be the number of hLen-long blocks of bytes in the derived key, | |
| 77 | // 2. Let l be the number of h_len-long blocks of bytes in the derived key, | |
| 78 | 78 | // rounding up, and let r be the number of bytes in the last |
| 79 | 79 | // block |
| 80 | 80 | // |
| 81 | 81 | |
| 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 | } | |
| 92 | 87 | |
| 93 | 88 | // FromSpec: |
| 94 | 89 | // |
| ... | ... | @@ -118,37 +113,38 @@ pub fn pbkdf2(derivedKey: []u8, password: []const u8, salt: []const u8, rounds: |
| 118 | 113 | // Here, INT (i) is a four-octet encoding of the integer i, most |
| 119 | 114 | // significant octet first. |
| 120 | 115 | // |
| 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 | |
| 122 | 117 | // produce a derived key DK: |
| 123 | 118 | // |
| 124 | 119 | // 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; | |
| 129 | 125 | |
| 130 | 126 | // 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 | |
| 132 | 128 | var ctx = Prf.init(password); |
| 133 | 129 | 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..]); | |
| 136 | 132 | |
| 137 | 133 | // 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]); | |
| 142 | 138 | |
| 143 | 139 | var i: u32 = 1; |
| 144 | 140 | while (i < rounds) : (i += 1) { |
| 145 | 141 | // 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..]); | |
| 148 | 144 | |
| 149 | 145 | // 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]; | |
| 152 | 148 | } |
| 153 | 149 | } |
| 154 | 150 | } |
| ... | ... | @@ -158,49 +154,50 @@ const htest = @import("test.zig"); |
| 158 | 154 | const HmacSha1 = std.crypto.auth.hmac.HmacSha1; |
| 159 | 155 | |
| 160 | 156 | // RFC 6070 PBKDF2 HMAC-SHA1 Test Vectors |
| 157 | ||
| 161 | 158 | test "RFC 6070 one iteration" { |
| 162 | 159 | const p = "password"; |
| 163 | 160 | const s = "salt"; |
| 164 | 161 | const c = 1; |
| 165 | const dkLen = 20; | |
| 162 | const dk_len = 20; | |
| 166 | 163 | |
| 167 | var derivedKey: [dkLen]u8 = undefined; | |
| 164 | var dk: [dk_len]u8 = undefined; | |
| 168 | 165 | |
| 169 | try pbkdf2(&derivedKey, p, s, c, HmacSha1); | |
| 166 | try pbkdf2(&dk, p, s, c, HmacSha1); | |
| 170 | 167 | |
| 171 | 168 | const expected = "0c60c80f961f0e71f3a9b524af6012062fe037a6"; |
| 172 | 169 | |
| 173 | htest.assertEqual(expected, derivedKey[0..]); | |
| 170 | htest.assertEqual(expected, dk[0..]); | |
| 174 | 171 | } |
| 175 | 172 | |
| 176 | 173 | test "RFC 6070 two iterations" { |
| 177 | 174 | const p = "password"; |
| 178 | 175 | const s = "salt"; |
| 179 | 176 | const c = 2; |
| 180 | const dkLen = 20; | |
| 177 | const dk_len = 20; | |
| 181 | 178 | |
| 182 | var derivedKey: [dkLen]u8 = undefined; | |
| 179 | var dk: [dk_len]u8 = undefined; | |
| 183 | 180 | |
| 184 | try pbkdf2(&derivedKey, p, s, c, HmacSha1); | |
| 181 | try pbkdf2(&dk, p, s, c, HmacSha1); | |
| 185 | 182 | |
| 186 | 183 | const expected = "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957"; |
| 187 | 184 | |
| 188 | htest.assertEqual(expected, derivedKey[0..]); | |
| 185 | htest.assertEqual(expected, dk[0..]); | |
| 189 | 186 | } |
| 190 | 187 | |
| 191 | 188 | test "RFC 6070 4096 iterations" { |
| 192 | 189 | const p = "password"; |
| 193 | 190 | const s = "salt"; |
| 194 | 191 | const c = 4096; |
| 195 | const dkLen = 20; | |
| 192 | const dk_len = 20; | |
| 196 | 193 | |
| 197 | var derivedKey: [dkLen]u8 = undefined; | |
| 194 | var dk: [dk_len]u8 = undefined; | |
| 198 | 195 | |
| 199 | try pbkdf2(&derivedKey, p, s, c, HmacSha1); | |
| 196 | try pbkdf2(&dk, p, s, c, HmacSha1); | |
| 200 | 197 | |
| 201 | 198 | const expected = "4b007901b765489abead49d926f721d065a429c1"; |
| 202 | 199 | |
| 203 | htest.assertEqual(expected, derivedKey[0..]); | |
| 200 | htest.assertEqual(expected, dk[0..]); | |
| 204 | 201 | } |
| 205 | 202 | |
| 206 | 203 | test "RFC 6070 16,777,216 iterations" { |
| ... | ... | @@ -212,48 +209,48 @@ test "RFC 6070 16,777,216 iterations" { |
| 212 | 209 | const p = "password"; |
| 213 | 210 | const s = "salt"; |
| 214 | 211 | const c = 16777216; |
| 215 | const dkLen = 20; | |
| 212 | const dk_len = 20; | |
| 216 | 213 | |
| 217 | var derivedKey = [_]u8{0} ** dkLen; | |
| 214 | var dk = [_]u8{0} ** dk_len; | |
| 218 | 215 | |
| 219 | try pbkdf2(&derivedKey, p, s, c, HmacSha1); | |
| 216 | try pbkdf2(&dk, p, s, c, HmacSha1); | |
| 220 | 217 | |
| 221 | 218 | const expected = "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984"; |
| 222 | 219 | |
| 223 | htest.assertEqual(expected, derivedKey[0..]); | |
| 220 | htest.assertEqual(expected, dk[0..]); | |
| 224 | 221 | } |
| 225 | 222 | |
| 226 | 223 | test "RFC 6070 multi-block salt and password" { |
| 227 | 224 | const p = "passwordPASSWORDpassword"; |
| 228 | 225 | const s = "saltSALTsaltSALTsaltSALTsaltSALTsalt"; |
| 229 | 226 | const c = 4096; |
| 230 | const dkLen = 25; | |
| 227 | const dk_len = 25; | |
| 231 | 228 | |
| 232 | var derivedKey: [dkLen]u8 = undefined; | |
| 229 | var dk: [dk_len]u8 = undefined; | |
| 233 | 230 | |
| 234 | try pbkdf2(&derivedKey, p, s, c, HmacSha1); | |
| 231 | try pbkdf2(&dk, p, s, c, HmacSha1); | |
| 235 | 232 | |
| 236 | 233 | const expected = "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038"; |
| 237 | 234 | |
| 238 | htest.assertEqual(expected, derivedKey[0..]); | |
| 235 | htest.assertEqual(expected, dk[0..]); | |
| 239 | 236 | } |
| 240 | 237 | |
| 241 | 238 | test "RFC 6070 embedded NUL" { |
| 242 | 239 | const p = "pass\x00word"; |
| 243 | 240 | const s = "sa\x00lt"; |
| 244 | 241 | const c = 4096; |
| 245 | const dkLen = 16; | |
| 242 | const dk_len = 16; | |
| 246 | 243 | |
| 247 | var derivedKey: [dkLen]u8 = undefined; | |
| 244 | var dk: [dk_len]u8 = undefined; | |
| 248 | 245 | |
| 249 | try pbkdf2(&derivedKey, p, s, c, HmacSha1); | |
| 246 | try pbkdf2(&dk, p, s, c, HmacSha1); | |
| 250 | 247 | |
| 251 | 248 | const expected = "56fa6aa75548099dcc37d7f03425e0c3"; |
| 252 | 249 | |
| 253 | htest.assertEqual(expected, derivedKey[0..]); | |
| 250 | htest.assertEqual(expected, dk[0..]); | |
| 254 | 251 | } |
| 255 | 252 | |
| 256 | test "Very large dkLen" { | |
| 253 | test "Very large dk_len" { | |
| 257 | 254 | // This test allocates 8GB of memory and is expected to take several hours to run. |
| 258 | 255 | if (true) { |
| 259 | 256 | return error.SkipZigTest; |
| ... | ... | @@ -261,13 +258,13 @@ test "Very large dkLen" { |
| 261 | 258 | const p = "password"; |
| 262 | 259 | const s = "salt"; |
| 263 | 260 | const c = 1; |
| 264 | const dkLen = 1 << 33; | |
| 261 | const dk_len = 1 << 33; | |
| 265 | 262 | |
| 266 | var derivedKey = try std.testing.allocator.alloc(u8, dkLen); | |
| 263 | var dk = try std.testing.allocator.alloc(u8, dk_len); | |
| 267 | 264 | defer { |
| 268 | std.testing.allocator.free(derivedKey); | |
| 265 | std.testing.allocator.free(dk); | |
| 269 | 266 | } |
| 270 | 267 | |
| 271 | try pbkdf2(derivedKey, p, s, c, HmacSha1); | |
| 272 | 268 | // Just verify this doesn't crash with an overflow |
| 269 | try pbkdf2(dk, p, s, c, HmacSha1); | |
| 273 | 270 | } |
lib/std/debug.zig-18| ... | ... | @@ -250,24 +250,6 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c |
| 250 | 250 | resetSegfaultHandler(); |
| 251 | 251 | } |
| 252 | 252 | |
| 253 | if (comptime std.Target.current.isDarwin() and std.Target.current.cpu.arch == .aarch64) | |
| 254 | nosuspend { | |
| 255 | // As a workaround for not having threadlocal variable support in LLD for this target, | |
| 256 | // we have a simpler panic implementation that does not use threadlocal variables. | |
| 257 | // TODO https://github.com/ziglang/zig/issues/7527 | |
| 258 | const stderr = io.getStdErr().writer(); | |
| 259 | if (@atomicRmw(u8, &panicking, .Add, 1, .SeqCst) == 0) { | |
| 260 | stderr.print("panic: " ++ format ++ "\n", args) catch os.abort(); | |
| 261 | if (trace) |t| { | |
| 262 | dumpStackTrace(t.*); | |
| 263 | } | |
| 264 | dumpCurrentStackTrace(first_trace_addr); | |
| 265 | } else { | |
| 266 | stderr.print("Panicked during a panic. Aborting.\n", .{}) catch os.abort(); | |
| 267 | } | |
| 268 | os.abort(); | |
| 269 | }; | |
| 270 | ||
| 271 | 253 | nosuspend switch (panic_stage) { |
| 272 | 254 | 0 => { |
| 273 | 255 | panic_stage = 1; |
lib/std/enums.zig created+1281| ... | ... | @@ -0,0 +1,1281 @@ |
| 1 | // SPDX-License-Identifier: MIT | |
| 2 | // Copyright (c) 2015-2021 Zig Contributors | |
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | |
| 4 | // The MIT license requires this copyright notice to be included in all copies | |
| 5 | // and substantial portions of the software. | |
| 6 | ||
| 7 | //! This module contains utilities and data structures for working with enums. | |
| 8 | ||
| 9 | const std = @import("std.zig"); | |
| 10 | const assert = std.debug.assert; | |
| 11 | const testing = std.testing; | |
| 12 | const 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. | |
| 18 | pub 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. | |
| 41 | pub 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 | ||
| 51 | test "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. | |
| 67 | pub fn values(comptime E: type) []const E { | |
| 68 | return comptime valuesFromFields(E, @typeInfo(E).Enum.fields); | |
| 69 | } | |
| 70 | ||
| 71 | test "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. | |
| 79 | pub fn uniqueValues(comptime E: type) []const E { | |
| 80 | return comptime valuesFromFields(E, uniqueFields(E)); | |
| 81 | } | |
| 82 | ||
| 83 | test "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. | |
| 94 | pub 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. | |
| 125 | fn 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. | |
| 166 | pub 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 | ||
| 175 | test "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. | |
| 200 | pub 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 | ||
| 217 | test "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. | |
| 233 | pub 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 | ||
| 252 | test "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. | |
| 278 | pub 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. | |
| 306 | pub 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. | |
| 365 | pub 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. | |
| 398 | pub fn NoExtension(comptime Self: type) type { | |
| 399 | return NoExt; | |
| 400 | } | |
| 401 | const 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. | |
| 406 | pub 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. | |
| 504 | pub 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. | |
| 663 | pub 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 | /// ``` | |
| 765 | pub 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 | ||
| 778 | test "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 | ||
| 791 | fn ascByValue(ctx: void, comptime a: EnumField, comptime b: EnumField) bool { | |
| 792 | return a.value < b.value; | |
| 793 | } | |
| 794 | pub 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 | ||
| 846 | test "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 | ||
| 862 | test "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 | ||
| 878 | test "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 | ||
| 894 | test "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 | ||
| 910 | test "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 | ||
| 926 | test "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 | ||
| 1038 | test "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 | ||
| 1064 | test "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 | ||
| 1129 | test "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 | ||
| 1188 | test "std.enums.EnumMap sized" { | |
| 1189 | const E = extern enum { a, b, c, d, e = 0 }; | |
| 1190 | const Map = EnumMap(E, usize); | |
| 1191 | testing.expectEqual(E, Map.Key); | |
| 1192 | testing.expectEqual(EnumIndexer(E), Map.Indexer); | |
| 1193 | testing.expectEqual(usize, Map.Value); | |
| 1194 | testing.expectEqual(@as(usize, 4), Map.len); | |
| 1195 | ||
| 1196 | const b = Map.initFull(5); | |
| 1197 | testing.expectEqual(@as(usize, 4), b.count()); | |
| 1198 | testing.expect(b.contains(.a)); | |
| 1199 | testing.expect(b.contains(.b)); | |
| 1200 | testing.expect(b.contains(.c)); | |
| 1201 | testing.expect(b.contains(.d)); | |
| 1202 | testing.expectEqual(@as(?usize, 5), b.get(.a)); | |
| 1203 | testing.expectEqual(@as(?usize, 5), b.get(.b)); | |
| 1204 | testing.expectEqual(@as(?usize, 5), b.get(.c)); | |
| 1205 | testing.expectEqual(@as(?usize, 5), b.get(.d)); | |
| 1206 | ||
| 1207 | const c = Map.initFullWith(.{ .a = 1, .b = 2, .c = 3, .d = 4 }); | |
| 1208 | testing.expectEqual(@as(usize, 4), c.count()); | |
| 1209 | testing.expect(c.contains(.a)); | |
| 1210 | testing.expect(c.contains(.b)); | |
| 1211 | testing.expect(c.contains(.c)); | |
| 1212 | testing.expect(c.contains(.d)); | |
| 1213 | testing.expectEqual(@as(?usize, 1), c.get(.a)); | |
| 1214 | testing.expectEqual(@as(?usize, 2), c.get(.b)); | |
| 1215 | testing.expectEqual(@as(?usize, 3), c.get(.c)); | |
| 1216 | testing.expectEqual(@as(?usize, 4), c.get(.d)); | |
| 1217 | ||
| 1218 | const d = Map.initFullWithDefault(6, .{ .b = 2, .c = 4 }); | |
| 1219 | testing.expectEqual(@as(usize, 4), d.count()); | |
| 1220 | testing.expect(d.contains(.a)); | |
| 1221 | testing.expect(d.contains(.b)); | |
| 1222 | testing.expect(d.contains(.c)); | |
| 1223 | testing.expect(d.contains(.d)); | |
| 1224 | testing.expectEqual(@as(?usize, 6), d.get(.a)); | |
| 1225 | testing.expectEqual(@as(?usize, 2), d.get(.b)); | |
| 1226 | testing.expectEqual(@as(?usize, 4), d.get(.c)); | |
| 1227 | testing.expectEqual(@as(?usize, 6), d.get(.d)); | |
| 1228 | ||
| 1229 | var a = Map.init(.{ .b = 2, .d = 4 }); | |
| 1230 | testing.expectEqual(@as(usize, 2), a.count()); | |
| 1231 | testing.expectEqual(false, a.contains(.a)); | |
| 1232 | testing.expectEqual(true, a.contains(.b)); | |
| 1233 | testing.expectEqual(false, a.contains(.c)); | |
| 1234 | testing.expectEqual(true, a.contains(.d)); | |
| 1235 | ||
| 1236 | testing.expectEqual(@as(?usize, null), a.get(.a)); | |
| 1237 | testing.expectEqual(@as(?usize, 2), a.get(.b)); | |
| 1238 | testing.expectEqual(@as(?usize, null), a.get(.c)); | |
| 1239 | testing.expectEqual(@as(?usize, 4), a.get(.d)); | |
| 1240 | ||
| 1241 | testing.expectEqual(@as(?*usize, null), a.getPtr(.a)); | |
| 1242 | testing.expectEqual(@as(?*usize, &a.values[1]), a.getPtr(.b)); | |
| 1243 | testing.expectEqual(@as(?*usize, null), a.getPtr(.c)); | |
| 1244 | testing.expectEqual(@as(?*usize, &a.values[3]), a.getPtr(.d)); | |
| 1245 | ||
| 1246 | testing.expectEqual(@as(?*const usize, null), a.getPtrConst(.a)); | |
| 1247 | testing.expectEqual(@as(?*const usize, &a.values[1]), a.getPtrConst(.b)); | |
| 1248 | testing.expectEqual(@as(?*const usize, null), a.getPtrConst(.c)); | |
| 1249 | testing.expectEqual(@as(?*const usize, &a.values[3]), a.getPtrConst(.d)); | |
| 1250 | ||
| 1251 | testing.expectEqual(@as(*const usize, &a.values[1]), a.getPtrAssertContains(.b)); | |
| 1252 | testing.expectEqual(@as(*const usize, &a.values[3]), a.getPtrAssertContains(.d)); | |
| 1253 | testing.expectEqual(@as(usize, 2), a.getAssertContains(.b)); | |
| 1254 | testing.expectEqual(@as(usize, 4), a.getAssertContains(.d)); | |
| 1255 | ||
| 1256 | a.put(.a, 3); | |
| 1257 | a.put(.a, 5); | |
| 1258 | a.putUninitialized(.c).* = 7; | |
| 1259 | a.putUninitialized(.c).* = 9; | |
| 1260 | ||
| 1261 | testing.expectEqual(@as(usize, 4), a.count()); | |
| 1262 | testing.expectEqual(@as(?usize, 5), a.get(.a)); | |
| 1263 | testing.expectEqual(@as(?usize, 2), a.get(.b)); | |
| 1264 | testing.expectEqual(@as(?usize, 9), a.get(.c)); | |
| 1265 | testing.expectEqual(@as(?usize, 4), a.get(.d)); | |
| 1266 | ||
| 1267 | a.remove(.a); | |
| 1268 | testing.expectEqual(@as(?usize, null), a.fetchRemove(.a)); | |
| 1269 | testing.expectEqual(@as(?usize, 9), a.fetchRemove(.c)); | |
| 1270 | a.remove(.c); | |
| 1271 | ||
| 1272 | var iter = a.iterator(); | |
| 1273 | const Entry = Map.Entry; | |
| 1274 | testing.expectEqual(@as(?Entry, Entry{ | |
| 1275 | .key = .b, .value = &a.values[1], | |
| 1276 | }), iter.next()); | |
| 1277 | testing.expectEqual(@as(?Entry, Entry{ | |
| 1278 | .key = .d, .value = &a.values[3], | |
| 1279 | }), iter.next()); | |
| 1280 | testing.expectEqual(@as(?Entry, null), iter.next()); | |
| 1281 | } |
lib/std/fs/path.zig+11-1| ... | ... | @@ -92,7 +92,7 @@ pub fn join(allocator: *Allocator, paths: []const []const u8) ![]u8 { |
| 92 | 92 | /// Naively combines a series of paths with the native path seperator and null terminator. |
| 93 | 93 | /// Allocates memory for the result, which must be freed by the caller. |
| 94 | 94 | pub fn joinZ(allocator: *Allocator, paths: []const []const u8) ![:0]u8 { |
| 95 | const out = joinSepMaybeZ(allocator, sep, isSep, paths, true); | |
| 95 | const out = try joinSepMaybeZ(allocator, sep, isSep, paths, true); | |
| 96 | 96 | return out[0 .. out.len - 1 :0]; |
| 97 | 97 | } |
| 98 | 98 | |
| ... | ... | @@ -119,6 +119,16 @@ fn testJoinMaybeZPosix(paths: []const []const u8, expected: []const u8, zero: bo |
| 119 | 119 | } |
| 120 | 120 | |
| 121 | 121 | test "join" { |
| 122 | { | |
| 123 | const actual: []u8 = try join(testing.allocator, &[_][]const u8{}); | |
| 124 | defer testing.allocator.free(actual); | |
| 125 | testing.expectEqualSlices(u8, "", actual); | |
| 126 | } | |
| 127 | { | |
| 128 | const actual: [:0]u8 = try joinZ(testing.allocator, &[_][]const u8{}); | |
| 129 | defer testing.allocator.free(actual); | |
| 130 | testing.expectEqualSlices(u8, "", actual); | |
| 131 | } | |
| 122 | 132 | for (&[_]bool{ false, true }) |zero| { |
| 123 | 133 | testJoinMaybeZWindows(&[_][]const u8{}, "", zero); |
| 124 | 134 | testJoinMaybeZWindows(&[_][]const u8{ "c:\\a\\b", "c" }, "c:\\a\\b\\c", zero); |
lib/std/macho.zig+40| ... | ... | @@ -1227,6 +1227,24 @@ pub const S_ATTR_EXT_RELOC = 0x200; |
| 1227 | 1227 | /// section has local relocation entries |
| 1228 | 1228 | pub const S_ATTR_LOC_RELOC = 0x100; |
| 1229 | 1229 | |
| 1230 | /// template of initial values for TLVs | |
| 1231 | pub const S_THREAD_LOCAL_REGULAR = 0x11; | |
| 1232 | ||
| 1233 | /// template of initial values for TLVs | |
| 1234 | pub const S_THREAD_LOCAL_ZEROFILL = 0x12; | |
| 1235 | ||
| 1236 | /// TLV descriptors | |
| 1237 | pub const S_THREAD_LOCAL_VARIABLES = 0x13; | |
| 1238 | ||
| 1239 | /// pointers to TLV descriptors | |
| 1240 | pub const S_THREAD_LOCAL_VARIABLE_POINTERS = 0x14; | |
| 1241 | ||
| 1242 | /// functions to call to initialize TLV values | |
| 1243 | pub const S_THREAD_LOCAL_INIT_FUNCTION_POINTERS = 0x15; | |
| 1244 | ||
| 1245 | /// 32-bit offsets to initializers | |
| 1246 | pub const S_INIT_FUNC_OFFSETS = 0x16; | |
| 1247 | ||
| 1230 | 1248 | pub const cpu_type_t = integer_t; |
| 1231 | 1249 | pub const cpu_subtype_t = integer_t; |
| 1232 | 1250 | pub const integer_t = c_int; |
| ... | ... | @@ -1422,6 +1440,14 @@ pub const EXPORT_SYMBOL_FLAGS_KIND_WEAK_DEFINITION: u8 = 0x04; |
| 1422 | 1440 | pub const EXPORT_SYMBOL_FLAGS_REEXPORT: u8 = 0x08; |
| 1423 | 1441 | pub const EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER: u8 = 0x10; |
| 1424 | 1442 | |
| 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. | |
| 1448 | pub const INDIRECT_SYMBOL_LOCAL: u32 = 0x80000000; | |
| 1449 | pub const INDIRECT_SYMBOL_ABS: u32 = 0x40000000; | |
| 1450 | ||
| 1425 | 1451 | // Codesign consts and structs taken from: |
| 1426 | 1452 | // https://opensource.apple.com/source/xnu/xnu-6153.81.5/osfmk/kern/cs_blobs.h.auto.html |
| 1427 | 1453 | |
| ... | ... | @@ -1589,3 +1615,17 @@ pub const GenericBlob = extern struct { |
| 1589 | 1615 | /// Total length of blob |
| 1590 | 1616 | length: u32, |
| 1591 | 1617 | }; |
| 1618 | ||
| 1619 | /// The LC_DATA_IN_CODE load commands uses a linkedit_data_command | |
| 1620 | /// to point to an array of data_in_code_entry entries. Each entry | |
| 1621 | /// describes a range of data in a code section. | |
| 1622 | pub const data_in_code_entry = extern struct { | |
| 1623 | /// From mach_header to start of data range. | |
| 1624 | offset: u32, | |
| 1625 | ||
| 1626 | /// Number of bytes in data range. | |
| 1627 | length: u16, | |
| 1628 | ||
| 1629 | /// A DICE_KIND value. | |
| 1630 | kind: u16, | |
| 1631 | }; |
lib/std/meta.zig+51-18| ... | ... | @@ -888,19 +888,20 @@ pub fn Vector(comptime len: u32, comptime child: type) type { |
| 888 | 888 | /// Given a type and value, cast the value to the type as c would. |
| 889 | 889 | /// This is for translate-c and is not intended for general use. |
| 890 | 890 | pub fn cast(comptime DestType: type, target: anytype) DestType { |
| 891 | const TargetType = @TypeOf(target); | |
| 891 | // this function should behave like transCCast in translate-c, except it's for macros | |
| 892 | const SourceType = @TypeOf(target); | |
| 892 | 893 | switch (@typeInfo(DestType)) { |
| 893 | .Pointer => |dest_ptr| { | |
| 894 | switch (@typeInfo(TargetType)) { | |
| 894 | .Pointer => { | |
| 895 | switch (@typeInfo(SourceType)) { | |
| 895 | 896 | .Int, .ComptimeInt => { |
| 896 | 897 | return @intToPtr(DestType, target); |
| 897 | 898 | }, |
| 898 | .Pointer => |ptr| { | |
| 899 | return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target)); | |
| 899 | .Pointer => { | |
| 900 | return castPtr(DestType, target); | |
| 900 | 901 | }, |
| 901 | 902 | .Optional => |opt| { |
| 902 | 903 | if (@typeInfo(opt.child) == .Pointer) { |
| 903 | return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target)); | |
| 904 | return castPtr(DestType, target); | |
| 904 | 905 | } |
| 905 | 906 | }, |
| 906 | 907 | else => {}, |
| ... | ... | @@ -908,17 +909,16 @@ pub fn cast(comptime DestType: type, target: anytype) DestType { |
| 908 | 909 | }, |
| 909 | 910 | .Optional => |dest_opt| { |
| 910 | 911 | if (@typeInfo(dest_opt.child) == .Pointer) { |
| 911 | const dest_ptr = @typeInfo(dest_opt.child).Pointer; | |
| 912 | switch (@typeInfo(TargetType)) { | |
| 912 | switch (@typeInfo(SourceType)) { | |
| 913 | 913 | .Int, .ComptimeInt => { |
| 914 | 914 | return @intToPtr(DestType, target); |
| 915 | 915 | }, |
| 916 | 916 | .Pointer => { |
| 917 | return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target)); | |
| 917 | return castPtr(DestType, target); | |
| 918 | 918 | }, |
| 919 | 919 | .Optional => |target_opt| { |
| 920 | 920 | if (@typeInfo(target_opt.child) == .Pointer) { |
| 921 | return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target)); | |
| 921 | return castPtr(DestType, target); | |
| 922 | 922 | } |
| 923 | 923 | }, |
| 924 | 924 | else => {}, |
| ... | ... | @@ -926,25 +926,25 @@ pub fn cast(comptime DestType: type, target: anytype) DestType { |
| 926 | 926 | } |
| 927 | 927 | }, |
| 928 | 928 | .Enum => { |
| 929 | if (@typeInfo(TargetType) == .Int or @typeInfo(TargetType) == .ComptimeInt) { | |
| 929 | if (@typeInfo(SourceType) == .Int or @typeInfo(SourceType) == .ComptimeInt) { | |
| 930 | 930 | return @intToEnum(DestType, target); |
| 931 | 931 | } |
| 932 | 932 | }, |
| 933 | .Int, .ComptimeInt => { | |
| 934 | switch (@typeInfo(TargetType)) { | |
| 933 | .Int => { | |
| 934 | switch (@typeInfo(SourceType)) { | |
| 935 | 935 | .Pointer => { |
| 936 | return @intCast(DestType, @ptrToInt(target)); | |
| 936 | return castInt(DestType, @ptrToInt(target)); | |
| 937 | 937 | }, |
| 938 | 938 | .Optional => |opt| { |
| 939 | 939 | if (@typeInfo(opt.child) == .Pointer) { |
| 940 | return @intCast(DestType, @ptrToInt(target)); | |
| 940 | return castInt(DestType, @ptrToInt(target)); | |
| 941 | 941 | } |
| 942 | 942 | }, |
| 943 | 943 | .Enum => { |
| 944 | return @intCast(DestType, @enumToInt(target)); | |
| 944 | return castInt(DestType, @enumToInt(target)); | |
| 945 | 945 | }, |
| 946 | .Int, .ComptimeInt => { | |
| 947 | return @intCast(DestType, target); | |
| 946 | .Int => { | |
| 947 | return castInt(DestType, target); | |
| 948 | 948 | }, |
| 949 | 949 | else => {}, |
| 950 | 950 | } |
| ... | ... | @@ -954,6 +954,34 @@ pub fn cast(comptime DestType: type, target: anytype) DestType { |
| 954 | 954 | return @as(DestType, target); |
| 955 | 955 | } |
| 956 | 956 | |
| 957 | fn 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 | ||
| 967 | fn 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 | ||
| 977 | fn 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 | ||
| 957 | 985 | test "std.meta.cast" { |
| 958 | 986 | const E = enum(u2) { |
| 959 | 987 | Zero, |
| ... | ... | @@ -977,6 +1005,11 @@ test "std.meta.cast" { |
| 977 | 1005 | testing.expectEqual(@as(u32, 4), cast(u32, @intToPtr(?*u32, 4))); |
| 978 | 1006 | testing.expectEqual(@as(u32, 10), cast(u32, @as(u64, 10))); |
| 979 | 1007 | testing.expectEqual(@as(u8, 2), cast(u8, E.Two)); |
| 1008 | ||
| 1009 | testing.expectEqual(@bitCast(i32, @as(u32, 0x8000_0000)), cast(i32, @as(u32, 0x8000_0000))); | |
| 1010 | ||
| 1011 | testing.expectEqual(@intToPtr(*u8, 2), cast(*u8, @intToPtr(*const u8, 2))); | |
| 1012 | testing.expectEqual(@intToPtr(*u8, 2), cast(*u8, @intToPtr(*volatile u8, 2))); | |
| 980 | 1013 | } |
| 981 | 1014 | |
| 982 | 1015 | /// Given a value returns its size as C's sizeof operator would. |
lib/std/meta/trait.zig+78| ... | ... | @@ -408,6 +408,84 @@ test "std.meta.trait.isTuple" { |
| 408 | 408 | testing.expect(isTuple(@TypeOf(t3))); |
| 409 | 409 | } |
| 410 | 410 | |
| 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 | /// ``` | |
| 423 | pub 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 | ||
| 451 | test "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 | ||
| 411 | 489 | pub fn hasDecls(comptime T: type, comptime names: anytype) bool { |
| 412 | 490 | inline for (names) |name| { |
| 413 | 491 | if (!@hasDecl(T, name)) |
lib/std/os.zig+2-2| ... | ... | @@ -2879,7 +2879,7 @@ pub fn bind(sock: socket_t, addr: *const sockaddr, len: socklen_t) BindError!voi |
| 2879 | 2879 | unreachable; |
| 2880 | 2880 | } |
| 2881 | 2881 | |
| 2882 | const ListenError = error{ | |
| 2882 | pub const ListenError = error{ | |
| 2883 | 2883 | /// Another socket is already listening on the same port. |
| 2884 | 2884 | /// For Internet domain sockets, the socket referred to by sockfd had not previously |
| 2885 | 2885 | /// been bound to an address and, upon attempting to bind it to an ephemeral port, it |
| ... | ... | @@ -5827,7 +5827,7 @@ pub fn tcsetattr(handle: fd_t, optional_action: TCSA, termios_p: termios) Termio |
| 5827 | 5827 | } |
| 5828 | 5828 | } |
| 5829 | 5829 | |
| 5830 | const IoCtl_SIOCGIFINDEX_Error = error{ | |
| 5830 | pub const IoCtl_SIOCGIFINDEX_Error = error{ | |
| 5831 | 5831 | FileSystem, |
| 5832 | 5832 | InterfaceNotFound, |
| 5833 | 5833 | } || UnexpectedError; |
lib/std/std.zig+4| ... | ... | @@ -20,6 +20,9 @@ pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringM |
| 20 | 20 | pub const DynLib = @import("dynamic_library.zig").DynLib; |
| 21 | 21 | pub const DynamicBitSet = bit_set.DynamicBitSet; |
| 22 | 22 | pub const DynamicBitSetUnmanaged = bit_set.DynamicBitSetUnmanaged; |
| 23 | pub const EnumArray = enums.EnumArray; | |
| 24 | pub const EnumMap = enums.EnumMap; | |
| 25 | pub const EnumSet = enums.EnumSet; | |
| 23 | 26 | pub const HashMap = hash_map.HashMap; |
| 24 | 27 | pub const HashMapUnmanaged = hash_map.HashMapUnmanaged; |
| 25 | 28 | pub const MultiArrayList = @import("multi_array_list.zig").MultiArrayList; |
| ... | ... | @@ -54,6 +57,7 @@ pub const cstr = @import("cstr.zig"); |
| 54 | 57 | pub const debug = @import("debug.zig"); |
| 55 | 58 | pub const dwarf = @import("dwarf.zig"); |
| 56 | 59 | pub const elf = @import("elf.zig"); |
| 60 | pub const enums = @import("enums.zig"); | |
| 57 | 61 | pub const event = @import("event.zig"); |
| 58 | 62 | pub const fifo = @import("fifo.zig"); |
| 59 | 63 | pub const fmt = @import("fmt.zig"); |
lib/std/zig/parser_test.zig+73| ... | ... | @@ -4,6 +4,31 @@ |
| 4 | 4 | // The MIT license requires this copyright notice to be included in all copies |
| 5 | 5 | // and substantial portions of the software. |
| 6 | 6 | |
| 7 | test "zig fmt: respect line breaks in struct field value declaration" { | |
| 8 | try testCanonical( | |
| 9 | \\const Foo = struct { | |
| 10 | \\ bar: u32 = | |
| 11 | \\ 42, | |
| 12 | \\ bar: u32 = | |
| 13 | \\ // a comment | |
| 14 | \\ 42, | |
| 15 | \\ bar: u32 = | |
| 16 | \\ 42, | |
| 17 | \\ // a comment | |
| 18 | \\ bar: []const u8 = | |
| 19 | \\ \\ foo | |
| 20 | \\ \\ bar | |
| 21 | \\ \\ baz | |
| 22 | \\ , | |
| 23 | \\ bar: u32 = | |
| 24 | \\ blk: { | |
| 25 | \\ break :blk 42; | |
| 26 | \\ }, | |
| 27 | \\}; | |
| 28 | \\ | |
| 29 | ); | |
| 30 | } | |
| 31 | ||
| 7 | 32 | // TODO Remove this after zig 0.9.0 is released. |
| 8 | 33 | test "zig fmt: rewrite inline functions as callconv(.Inline)" { |
| 9 | 34 | try testTransform( |
| ... | ... | @@ -3038,6 +3063,54 @@ test "zig fmt: switch" { |
| 3038 | 3063 | \\} |
| 3039 | 3064 | \\ |
| 3040 | 3065 | ); |
| 3066 | ||
| 3067 | try testTransform( | |
| 3068 | \\test { | |
| 3069 | \\ switch (x) { | |
| 3070 | \\ foo => | |
| 3071 | \\ "bar", | |
| 3072 | \\ } | |
| 3073 | \\} | |
| 3074 | \\ | |
| 3075 | , | |
| 3076 | \\test { | |
| 3077 | \\ switch (x) { | |
| 3078 | \\ foo => "bar", | |
| 3079 | \\ } | |
| 3080 | \\} | |
| 3081 | \\ | |
| 3082 | ); | |
| 3083 | } | |
| 3084 | ||
| 3085 | test "zig fmt: switch multiline string" { | |
| 3086 | try testCanonical( | |
| 3087 | \\test "switch multiline string" { | |
| 3088 | \\ const x: u32 = 0; | |
| 3089 | \\ const str = switch (x) { | |
| 3090 | \\ 1 => "one", | |
| 3091 | \\ 2 => | |
| 3092 | \\ \\ Comma after the multiline string | |
| 3093 | \\ \\ is needed | |
| 3094 | \\ , | |
| 3095 | \\ 3 => "three", | |
| 3096 | \\ else => "else", | |
| 3097 | \\ }; | |
| 3098 | \\ | |
| 3099 | \\ const Union = union(enum) { | |
| 3100 | \\ Int: i64, | |
| 3101 | \\ Float: f64, | |
| 3102 | \\ }; | |
| 3103 | \\ | |
| 3104 | \\ const str = switch (u) { | |
| 3105 | \\ Union.Int => |int| | |
| 3106 | \\ \\ Comma after the multiline string | |
| 3107 | \\ \\ is needed | |
| 3108 | \\ , | |
| 3109 | \\ Union.Float => |*float| unreachable, | |
| 3110 | \\ }; | |
| 3111 | \\} | |
| 3112 | \\ | |
| 3113 | ); | |
| 3041 | 3114 | } |
| 3042 | 3115 | |
| 3043 | 3116 | test "zig fmt: while" { |
lib/std/zig/render.zig+33-5| ... | ... | @@ -1159,8 +1159,29 @@ fn renderContainerField( |
| 1159 | 1159 | try renderToken(ais, tree, rparen_token, .space); // ) |
| 1160 | 1160 | } |
| 1161 | 1161 | 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 | } | |
| 1164 | 1185 | } |
| 1165 | 1186 | |
| 1166 | 1187 | fn renderBuiltinCall( |
| ... | ... | @@ -1423,6 +1444,7 @@ fn renderSwitchCase( |
| 1423 | 1444 | switch_case: ast.full.SwitchCase, |
| 1424 | 1445 | space: Space, |
| 1425 | 1446 | ) Error!void { |
| 1447 | const node_tags = tree.nodes.items(.tag); | |
| 1426 | 1448 | const token_tags = tree.tokens.items(.tag); |
| 1427 | 1449 | const trailing_comma = token_tags[switch_case.ast.arrow_token - 1] == .comma; |
| 1428 | 1450 | |
| ... | ... | @@ -1445,17 +1467,23 @@ fn renderSwitchCase( |
| 1445 | 1467 | } |
| 1446 | 1468 | |
| 1447 | 1469 | // 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); | |
| 1449 | 1477 | |
| 1450 | 1478 | if (switch_case.payload_token) |payload_token| { |
| 1451 | 1479 | try renderToken(ais, tree, payload_token - 1, .none); // pipe |
| 1452 | 1480 | if (token_tags[payload_token] == .asterisk) { |
| 1453 | 1481 | try renderToken(ais, tree, payload_token, .none); // asterisk |
| 1454 | 1482 | 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 | |
| 1456 | 1484 | } else { |
| 1457 | 1485 | 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 | |
| 1459 | 1487 | } |
| 1460 | 1488 | } |
| 1461 | 1489 |
src/BuiltinFn.zig+1-1| ... | ... | @@ -477,7 +477,7 @@ pub const list = list: { |
| 477 | 477 | "@intCast", |
| 478 | 478 | .{ |
| 479 | 479 | .tag = .int_cast, |
| 480 | .param_count = 1, | |
| 480 | .param_count = 2, | |
| 481 | 481 | }, |
| 482 | 482 | }, |
| 483 | 483 | .{ |
src/clang.zig+5| ... | ... | @@ -537,6 +537,11 @@ pub const FunctionType = opaque { |
| 537 | 537 | extern fn ZigClangFunctionType_getReturnType(*const FunctionType) QualType; |
| 538 | 538 | }; |
| 539 | 539 | |
| 540 | pub const GenericSelectionExpr = opaque { | |
| 541 | pub const getResultExpr = ZigClangGenericSelectionExpr_getResultExpr; | |
| 542 | extern fn ZigClangGenericSelectionExpr_getResultExpr(*const GenericSelectionExpr) *const Expr; | |
| 543 | }; | |
| 544 | ||
| 540 | 545 | pub const IfStmt = opaque { |
| 541 | 546 | pub const getThen = ZigClangIfStmt_getThen; |
| 542 | 547 | extern fn ZigClangIfStmt_getThen(*const IfStmt) *const Stmt; |
src/codegen.zig+59-133| ... | ... | @@ -2133,9 +2133,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2133 | 2133 | if (inst.func.value()) |func_value| { |
| 2134 | 2134 | if (func_value.castTag(.function)) |func_payload| { |
| 2135 | 2135 | 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}); | |
| 2139 | 2142 | switch (arch) { |
| 2140 | 2143 | .x86_64 => { |
| 2141 | 2144 | try self.genSetReg(inst.base.src, Type.initTag(.u32), .rax, .{ .memory = got_addr }); |
| ... | ... | @@ -2153,8 +2156,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2153 | 2156 | const decl = func_payload.data; |
| 2154 | 2157 | const decl_name = try std.fmt.allocPrint(self.bin_file.allocator, "_{s}", .{decl.name}); |
| 2155 | 2158 | 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| | |
| 2158 | 2161 | @intCast(u32, index) |
| 2159 | 2162 | else |
| 2160 | 2163 | try macho_file.addExternSymbol(decl_name); |
| ... | ... | @@ -3304,80 +3307,32 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3304 | 3307 | }, |
| 3305 | 3308 | .memory => |addr| { |
| 3306 | 3309 | 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 | }); | |
| 3345 | 3321 | } 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", .{}); | |
| 3380 | 3323 | } |
| 3324 | mem.writeIntLittle( | |
| 3325 | u32, | |
| 3326 | try self.code.addManyAsArray(4), | |
| 3327 | Instruction.adrp(reg, 0).toU32(), | |
| 3328 | ); | |
| 3329 | // ldr reg, reg, offset | |
| 3330 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ | |
| 3331 | .register = .{ | |
| 3332 | .rn = reg, | |
| 3333 | .offset = Instruction.LoadStoreOffset.imm(0), | |
| 3334 | }, | |
| 3335 | }).toU32()); | |
| 3381 | 3336 | } else { |
| 3382 | 3337 | // The value is in memory at a hard-coded address. |
| 3383 | 3338 | // If the type is a pointer, it means the pointer address is at this memory location. |
| ... | ... | @@ -3561,62 +3516,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3561 | 3516 | }, |
| 3562 | 3517 | .memory => |x| { |
| 3563 | 3518 | 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, | |
| 3589 | 3528 | }); |
| 3590 | 3529 | } 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", .{}); | |
| 3619 | 3531 | } |
| 3532 | try self.code.ensureCapacity(self.code.items.len + 7); | |
| 3533 | self.rex(.{ .w = reg.size() == 64, .r = reg.isExtended() }); | |
| 3534 | self.code.appendSliceAssumeCapacity(&[_]u8{ | |
| 3535 | 0x8D, | |
| 3536 | 0x05 | (@as(u8, reg.id() & 0b111) << 3), | |
| 3537 | }); | |
| 3538 | mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), 0); | |
| 3539 | ||
| 3540 | try self.code.ensureCapacity(self.code.items.len + 3); | |
| 3541 | self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended(), .r = reg.isExtended() }); | |
| 3542 | const RM = (@as(u8, reg.id() & 0b111) << 3) | @truncate(u3, reg.id()); | |
| 3543 | self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, RM }); | |
| 3620 | 3544 | } else if (x <= math.maxInt(u32)) { |
| 3621 | 3545 | // Moving from memory to a register is a variant of `8B /r`. |
| 3622 | 3546 | // Since we're using 64-bit moves, we require a REX. |
| ... | ... | @@ -3779,9 +3703,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3779 | 3703 | return MCValue{ .memory = got_addr }; |
| 3780 | 3704 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { |
| 3781 | 3705 | 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 | }; | |
| 3785 | 3711 | return MCValue{ .memory = got_addr }; |
| 3786 | 3712 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { |
| 3787 | 3713 | const decl = payload.data; |
src/codegen/aarch64.zig+4-1| ... | ... | @@ -221,7 +221,8 @@ pub const Instruction = union(enum) { |
| 221 | 221 | offset: u12, |
| 222 | 222 | opc: u2, |
| 223 | 223 | op1: u2, |
| 224 | fixed: u4 = 0b111_0, | |
| 224 | v: u1, | |
| 225 | fixed: u3 = 0b111, | |
| 225 | 226 | size: u2, |
| 226 | 227 | }, |
| 227 | 228 | LoadStorePairOfRegisters: packed struct { |
| ... | ... | @@ -505,6 +506,7 @@ pub const Instruction = union(enum) { |
| 505 | 506 | .offset = offset.toU12(), |
| 506 | 507 | .opc = opc, |
| 507 | 508 | .op1 = op1, |
| 509 | .v = 0, | |
| 508 | 510 | .size = 0b10, |
| 509 | 511 | }, |
| 510 | 512 | }; |
| ... | ... | @@ -517,6 +519,7 @@ pub const Instruction = union(enum) { |
| 517 | 519 | .offset = offset.toU12(), |
| 518 | 520 | .opc = opc, |
| 519 | 521 | .op1 = op1, |
| 522 | .v = 0, | |
| 520 | 523 | .size = 0b11, |
| 521 | 524 | }, |
| 522 | 525 | }; |
src/codegen/llvm.zig+11-11| ... | ... | @@ -219,7 +219,7 @@ pub const LLVMIRModule = struct { |
| 219 | 219 | |
| 220 | 220 | var error_message: [*:0]const u8 = undefined; |
| 221 | 221 | var target: *const llvm.Target = undefined; |
| 222 | if (llvm.Target.getFromTriple(llvm_target_triple.ptr, &target, &error_message)) { | |
| 222 | if (llvm.Target.getFromTriple(llvm_target_triple.ptr, &target, &error_message).toBool()) { | |
| 223 | 223 | defer llvm.disposeMessage(error_message); |
| 224 | 224 | |
| 225 | 225 | const stderr = std.io.getStdErr().writer(); |
| ... | ... | @@ -303,7 +303,7 @@ pub const LLVMIRModule = struct { |
| 303 | 303 | // verifyModule always allocs the error_message even if there is no error |
| 304 | 304 | defer llvm.disposeMessage(error_message); |
| 305 | 305 | |
| 306 | if (self.llvm_module.verify(.ReturnStatus, &error_message)) { | |
| 306 | if (self.llvm_module.verify(.ReturnStatus, &error_message).toBool()) { | |
| 307 | 307 | const stderr = std.io.getStdErr().writer(); |
| 308 | 308 | try stderr.print("broken LLVM module found: {s}\nThis is a bug in the Zig compiler.", .{error_message}); |
| 309 | 309 | return error.BrokenLLVMModule; |
| ... | ... | @@ -319,7 +319,7 @@ pub const LLVMIRModule = struct { |
| 319 | 319 | object_pathZ.ptr, |
| 320 | 320 | .ObjectFile, |
| 321 | 321 | &error_message, |
| 322 | )) { | |
| 322 | ).toBool()) { | |
| 323 | 323 | defer llvm.disposeMessage(error_message); |
| 324 | 324 | |
| 325 | 325 | const stderr = std.io.getStdErr().writer(); |
| ... | ... | @@ -614,7 +614,7 @@ pub const LLVMIRModule = struct { |
| 614 | 614 | |
| 615 | 615 | var indices: [2]*const llvm.Value = .{ |
| 616 | 616 | index_type.constNull(), |
| 617 | index_type.constInt(1, false), | |
| 617 | index_type.constInt(1, .False), | |
| 618 | 618 | }; |
| 619 | 619 | |
| 620 | 620 | return self.builder.buildLoad(self.builder.buildInBoundsGEP(operand, &indices, 2, ""), ""); |
| ... | ... | @@ -676,7 +676,7 @@ pub const LLVMIRModule = struct { |
| 676 | 676 | const signed = inst.base.ty.isSignedInt(); |
| 677 | 677 | // TODO: Should we use intcast here or just a simple bitcast? |
| 678 | 678 | // LLVM does truncation vs bitcast (+signed extension) in the intcast depending on the sizes |
| 679 | return self.builder.buildIntCast2(val, try self.getLLVMType(inst.base.ty, inst.base.src), signed, ""); | |
| 679 | return self.builder.buildIntCast2(val, try self.getLLVMType(inst.base.ty, inst.base.src), llvm.Bool.fromBool(signed), ""); | |
| 680 | 680 | } |
| 681 | 681 | |
| 682 | 682 | fn genBitCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value { |
| ... | ... | @@ -782,7 +782,7 @@ pub const LLVMIRModule = struct { |
| 782 | 782 | if (bigint.limbs.len != 1) { |
| 783 | 783 | return self.fail(src, "TODO implement bigger bigint", .{}); |
| 784 | 784 | } |
| 785 | const llvm_int = llvm_type.constInt(bigint.limbs[0], false); | |
| 785 | const llvm_int = llvm_type.constInt(bigint.limbs[0], .False); | |
| 786 | 786 | if (!bigint.positive) { |
| 787 | 787 | return llvm.constNeg(llvm_int); |
| 788 | 788 | } |
| ... | ... | @@ -820,7 +820,7 @@ pub const LLVMIRModule = struct { |
| 820 | 820 | return self.fail(src, "TODO handle other sentinel values", .{}); |
| 821 | 821 | } else false; |
| 822 | 822 | |
| 823 | return self.context.constString(payload.data.ptr, @intCast(c_uint, payload.data.len), !zero_sentinel); | |
| 823 | return self.context.constString(payload.data.ptr, @intCast(c_uint, payload.data.len), llvm.Bool.fromBool(!zero_sentinel)); | |
| 824 | 824 | } else { |
| 825 | 825 | return self.fail(src, "TODO handle more array values", .{}); |
| 826 | 826 | } |
| ... | ... | @@ -836,13 +836,13 @@ pub const LLVMIRModule = struct { |
| 836 | 836 | llvm_child_type.constNull(), |
| 837 | 837 | self.context.intType(1).constNull(), |
| 838 | 838 | }; |
| 839 | return self.context.constStruct(&optional_values, 2, false); | |
| 839 | return self.context.constStruct(&optional_values, 2, .False); | |
| 840 | 840 | } else { |
| 841 | 841 | var optional_values: [2]*const llvm.Value = .{ |
| 842 | 842 | try self.genTypedValue(src, .{ .ty = child_type, .val = tv.val }), |
| 843 | 843 | self.context.intType(1).constAllOnes(), |
| 844 | 844 | }; |
| 845 | return self.context.constStruct(&optional_values, 2, false); | |
| 845 | return self.context.constStruct(&optional_values, 2, .False); | |
| 846 | 846 | } |
| 847 | 847 | } else { |
| 848 | 848 | return self.fail(src, "TODO implement const of optional pointer", .{}); |
| ... | ... | @@ -882,7 +882,7 @@ pub const LLVMIRModule = struct { |
| 882 | 882 | try self.getLLVMType(child_type, src), |
| 883 | 883 | self.context.intType(1), |
| 884 | 884 | }; |
| 885 | return self.context.structType(&optional_types, 2, false); | |
| 885 | return self.context.structType(&optional_types, 2, .False); | |
| 886 | 886 | } else { |
| 887 | 887 | return self.fail(src, "TODO implement optional pointers as actual pointers", .{}); |
| 888 | 888 | } |
| ... | ... | @@ -934,7 +934,7 @@ pub const LLVMIRModule = struct { |
| 934 | 934 | try self.getLLVMType(return_type, src), |
| 935 | 935 | if (fn_param_len == 0) null else llvm_param.ptr, |
| 936 | 936 | @intCast(c_uint, fn_param_len), |
| 937 | false, | |
| 937 | .False, | |
| 938 | 938 | ); |
| 939 | 939 | const llvm_fn = self.llvm_module.addFunction(func.name, fn_type); |
| 940 | 940 |
src/codegen/llvm/bindings.zig+23-10| ... | ... | @@ -1,7 +1,20 @@ |
| 1 | 1 | //! We do this instead of @cImport because the self-hosted compiler is easier |
| 2 | 2 | //! to bootstrap if it does not depend on translate-c. |
| 3 | 3 | |
| 4 | const LLVMBool = bool; | |
| 4 | /// Do not compare directly to .True, use toBool() instead. | |
| 5 | pub 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 | }; | |
| 5 | 18 | pub const AttributeIndex = c_uint; |
| 6 | 19 | |
| 7 | 20 | /// Make sure to use the *InContext functions instead of the global ones. |
| ... | ... | @@ -22,13 +35,13 @@ pub const Context = opaque { |
| 22 | 35 | extern fn LLVMVoidTypeInContext(C: *const Context) *const Type; |
| 23 | 36 | |
| 24 | 37 | pub const structType = LLVMStructTypeInContext; |
| 25 | extern fn LLVMStructTypeInContext(C: *const Context, ElementTypes: [*]*const Type, ElementCount: c_uint, Packed: LLVMBool) *const Type; | |
| 38 | extern fn LLVMStructTypeInContext(C: *const Context, ElementTypes: [*]*const Type, ElementCount: c_uint, Packed: Bool) *const Type; | |
| 26 | 39 | |
| 27 | 40 | pub const constString = LLVMConstStringInContext; |
| 28 | extern fn LLVMConstStringInContext(C: *const Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: LLVMBool) *const Value; | |
| 41 | extern fn LLVMConstStringInContext(C: *const Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: Bool) *const Value; | |
| 29 | 42 | |
| 30 | 43 | pub const constStruct = LLVMConstStructInContext; |
| 31 | extern fn LLVMConstStructInContext(C: *const Context, ConstantVals: [*]*const Value, Count: c_uint, Packed: LLVMBool) *const Value; | |
| 44 | extern fn LLVMConstStructInContext(C: *const Context, ConstantVals: [*]*const Value, Count: c_uint, Packed: Bool) *const Value; | |
| 32 | 45 | |
| 33 | 46 | pub const createBasicBlock = LLVMCreateBasicBlockInContext; |
| 34 | 47 | extern fn LLVMCreateBasicBlockInContext(C: *const Context, Name: [*:0]const u8) *const BasicBlock; |
| ... | ... | @@ -59,7 +72,7 @@ pub const Value = opaque { |
| 59 | 72 | |
| 60 | 73 | pub const Type = opaque { |
| 61 | 74 | pub const functionType = LLVMFunctionType; |
| 62 | extern fn LLVMFunctionType(ReturnType: *const Type, ParamTypes: ?[*]*const Type, ParamCount: c_uint, IsVarArg: LLVMBool) *const Type; | |
| 75 | extern fn LLVMFunctionType(ReturnType: *const Type, ParamTypes: ?[*]*const Type, ParamCount: c_uint, IsVarArg: Bool) *const Type; | |
| 63 | 76 | |
| 64 | 77 | pub const constNull = LLVMConstNull; |
| 65 | 78 | extern fn LLVMConstNull(Ty: *const Type) *const Value; |
| ... | ... | @@ -68,7 +81,7 @@ pub const Type = opaque { |
| 68 | 81 | extern fn LLVMConstAllOnes(Ty: *const Type) *const Value; |
| 69 | 82 | |
| 70 | 83 | pub const constInt = LLVMConstInt; |
| 71 | extern fn LLVMConstInt(IntTy: *const Type, N: c_ulonglong, SignExtend: LLVMBool) *const Value; | |
| 84 | extern fn LLVMConstInt(IntTy: *const Type, N: c_ulonglong, SignExtend: Bool) *const Value; | |
| 72 | 85 | |
| 73 | 86 | pub const constArray = LLVMConstArray; |
| 74 | 87 | extern fn LLVMConstArray(ElementTy: *const Type, ConstantVals: ?[*]*const Value, Length: c_uint) *const Value; |
| ... | ... | @@ -91,7 +104,7 @@ pub const Module = opaque { |
| 91 | 104 | extern fn LLVMDisposeModule(*const Module) void; |
| 92 | 105 | |
| 93 | 106 | pub const verify = LLVMVerifyModule; |
| 94 | extern fn LLVMVerifyModule(*const Module, Action: VerifierFailureAction, OutMessage: *[*:0]const u8) LLVMBool; | |
| 107 | extern fn LLVMVerifyModule(*const Module, Action: VerifierFailureAction, OutMessage: *[*:0]const u8) Bool; | |
| 95 | 108 | |
| 96 | 109 | pub const addFunction = LLVMAddFunction; |
| 97 | 110 | extern fn LLVMAddFunction(*const Module, Name: [*:0]const u8, FunctionTy: *const Type) *const Value; |
| ... | ... | @@ -191,7 +204,7 @@ pub const Builder = opaque { |
| 191 | 204 | extern fn LLVMBuildNUWSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; |
| 192 | 205 | |
| 193 | 206 | pub const buildIntCast2 = LLVMBuildIntCast2; |
| 194 | extern fn LLVMBuildIntCast2(*const Builder, Val: *const Value, DestTy: *const Type, IsSigned: LLVMBool, Name: [*:0]const u8) *const Value; | |
| 207 | extern fn LLVMBuildIntCast2(*const Builder, Val: *const Value, DestTy: *const Type, IsSigned: Bool, Name: [*:0]const u8) *const Value; | |
| 195 | 208 | |
| 196 | 209 | pub const buildBitCast = LLVMBuildBitCast; |
| 197 | 210 | extern fn LLVMBuildBitCast(*const Builder, Val: *const Value, DestTy: *const Type, Name: [*:0]const u8) *const Value; |
| ... | ... | @@ -258,7 +271,7 @@ pub const TargetMachine = opaque { |
| 258 | 271 | Filename: [*:0]const u8, |
| 259 | 272 | codegen: CodeGenFileType, |
| 260 | 273 | ErrorMessage: *[*:0]const u8, |
| 261 | ) LLVMBool; | |
| 274 | ) Bool; | |
| 262 | 275 | }; |
| 263 | 276 | |
| 264 | 277 | pub const CodeMode = extern enum { |
| ... | ... | @@ -295,7 +308,7 @@ pub const CodeGenFileType = extern enum { |
| 295 | 308 | |
| 296 | 309 | pub const Target = opaque { |
| 297 | 310 | pub const getFromTriple = LLVMGetTargetFromTriple; |
| 298 | extern fn LLVMGetTargetFromTriple(Triple: [*:0]const u8, T: **const Target, ErrorMessage: *[*:0]const u8) LLVMBool; | |
| 311 | extern fn LLVMGetTargetFromTriple(Triple: [*:0]const u8, T: **const Target, ErrorMessage: *[*:0]const u8) Bool; | |
| 299 | 312 | }; |
| 300 | 313 | |
| 301 | 314 | extern fn LLVMInitializeAArch64TargetInfo() void; |
src/link/MachO.zig+549-536| ... | ... | @@ -11,7 +11,9 @@ const codegen = @import("../codegen.zig"); |
| 11 | 11 | const aarch64 = @import("../codegen/aarch64.zig"); |
| 12 | 12 | const math = std.math; |
| 13 | 13 | const mem = std.mem; |
| 14 | const meta = std.meta; | |
| 14 | 15 | |
| 16 | const bind = @import("MachO/bind.zig"); | |
| 15 | 17 | const trace = @import("../tracy.zig").trace; |
| 16 | 18 | const build_options = @import("build_options"); |
| 17 | 19 | const Module = @import("../Module.zig"); |
| ... | ... | @@ -24,9 +26,9 @@ const target_util = @import("../target.zig"); |
| 24 | 26 | const DebugSymbols = @import("MachO/DebugSymbols.zig"); |
| 25 | 27 | const Trie = @import("MachO/Trie.zig"); |
| 26 | 28 | const CodeSignature = @import("MachO/CodeSignature.zig"); |
| 29 | const Zld = @import("MachO/Zld.zig"); | |
| 27 | 30 | |
| 28 | 31 | usingnamespace @import("MachO/commands.zig"); |
| 29 | usingnamespace @import("MachO/imports.zig"); | |
| 30 | 32 | |
| 31 | 33 | pub const base_tag: File.Tag = File.Tag.macho; |
| 32 | 34 | |
| ... | ... | @@ -87,14 +89,12 @@ code_signature_cmd_index: ?u16 = null, |
| 87 | 89 | |
| 88 | 90 | /// Index into __TEXT,__text section. |
| 89 | 91 | text_section_index: ?u16 = null, |
| 90 | /// Index into __TEXT,__ziggot section. | |
| 91 | got_section_index: ?u16 = null, | |
| 92 | 92 | /// Index into __TEXT,__stubs section. |
| 93 | 93 | stubs_section_index: ?u16 = null, |
| 94 | 94 | /// Index into __TEXT,__stub_helper section. |
| 95 | 95 | stub_helper_section_index: ?u16 = null, |
| 96 | 96 | /// Index into __DATA_CONST,__got section. |
| 97 | data_got_section_index: ?u16 = null, | |
| 97 | got_section_index: ?u16 = null, | |
| 98 | 98 | /// Index into __DATA,__la_symbol_ptr section. |
| 99 | 99 | la_symbol_ptr_section_index: ?u16 = null, |
| 100 | 100 | /// Index into __DATA,__data section. |
| ... | ... | @@ -104,16 +104,16 @@ entry_addr: ?u64 = null, |
| 104 | 104 | |
| 105 | 105 | /// Table of all local symbols |
| 106 | 106 | /// Internally references string table for names (which are optional). |
| 107 | local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | |
| 107 | locals: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | |
| 108 | 108 | /// Table of all global symbols |
| 109 | global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | |
| 109 | globals: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | |
| 110 | 110 | /// Table of all extern nonlazy symbols, indexed by name. |
| 111 | extern_nonlazy_symbols: std.StringArrayHashMapUnmanaged(ExternSymbol) = .{}, | |
| 111 | nonlazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{}, | |
| 112 | 112 | /// Table of all extern lazy symbols, indexed by name. |
| 113 | extern_lazy_symbols: std.StringArrayHashMapUnmanaged(ExternSymbol) = .{}, | |
| 113 | lazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{}, | |
| 114 | 114 | |
| 115 | local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{}, | |
| 116 | global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{}, | |
| 115 | locals_free_list: std.ArrayListUnmanaged(u32) = .{}, | |
| 116 | globals_free_list: std.ArrayListUnmanaged(u32) = .{}, | |
| 117 | 117 | offset_table_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 118 | 118 | |
| 119 | 119 | stub_helper_stubs_start_off: ?u64 = null, |
| ... | ... | @@ -122,8 +122,8 @@ stub_helper_stubs_start_off: ?u64 = null, |
| 122 | 122 | string_table: std.ArrayListUnmanaged(u8) = .{}, |
| 123 | 123 | string_table_directory: std.StringHashMapUnmanaged(u32) = .{}, |
| 124 | 124 | |
| 125 | /// Table of trampolines to the actual symbols in __text section. | |
| 126 | offset_table: std.ArrayListUnmanaged(u64) = .{}, | |
| 125 | /// Table of GOT entries. | |
| 126 | offset_table: std.ArrayListUnmanaged(GOTEntry) = .{}, | |
| 127 | 127 | |
| 128 | 128 | error_flags: File.ErrorFlags = File.ErrorFlags{}, |
| 129 | 129 | |
| ... | ... | @@ -154,14 +154,19 @@ string_table_needs_relocation: bool = false, |
| 154 | 154 | /// allocate a fresh text block, which will have ideal capacity, and then grow it |
| 155 | 155 | /// by 1 byte. It will then have -1 overcapacity. |
| 156 | 156 | text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{}, |
| 157 | ||
| 157 | 158 | /// Pointer to the last allocated text block |
| 158 | 159 | last_text_block: ?*TextBlock = null, |
| 160 | ||
| 159 | 161 | /// A list of all PIE fixups required for this run of the linker. |
| 160 | 162 | /// Warning, this is currently NOT thread-safe. See the TODO below. |
| 161 | 163 | /// TODO Move this list inside `updateDecl` where it should be allocated |
| 162 | 164 | /// prior to calling `generateSymbol`, and then immediately deallocated |
| 163 | 165 | /// rather than sitting in the global scope. |
| 164 | pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{}, | |
| 166 | /// TODO We should also rewrite this using generic relocations common to all | |
| 167 | /// backends. | |
| 168 | pie_fixups: std.ArrayListUnmanaged(PIEFixup) = .{}, | |
| 169 | ||
| 165 | 170 | /// A list of all stub (extern decls) fixups required for this run of the linker. |
| 166 | 171 | /// Warning, this is currently NOT thread-safe. See the TODO below. |
| 167 | 172 | /// TODO Move this list inside `updateDecl` where it should be allocated |
| ... | ... | @@ -169,14 +174,42 @@ pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{}, |
| 169 | 174 | /// rather than sitting in the global scope. |
| 170 | 175 | stub_fixups: std.ArrayListUnmanaged(StubFixup) = .{}, |
| 171 | 176 | |
| 172 | pub 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, | |
| 177 | pub 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 | ||
| 193 | pub 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 | ||
| 204 | pub const PIEFixup = struct { | |
| 205 | /// Target VM address of this relocation. | |
| 206 | target_addr: u64, | |
| 207 | ||
| 208 | /// Offset within the byte stream. | |
| 209 | offset: usize, | |
| 210 | ||
| 211 | /// Size of the relocation. | |
| 212 | size: usize, | |
| 180 | 213 | }; |
| 181 | 214 | |
| 182 | 215 | pub const StubFixup = struct { |
| ... | ... | @@ -260,9 +293,9 @@ pub const TextBlock = struct { |
| 260 | 293 | /// File offset relocation happens transparently, so it is not included in |
| 261 | 294 | /// this calculation. |
| 262 | 295 | fn capacity(self: TextBlock, macho_file: MachO) u64 { |
| 263 | const self_sym = macho_file.local_symbols.items[self.local_sym_index]; | |
| 296 | const self_sym = macho_file.locals.items[self.local_sym_index]; | |
| 264 | 297 | if (self.next) |next| { |
| 265 | const next_sym = macho_file.local_symbols.items[next.local_sym_index]; | |
| 298 | const next_sym = macho_file.locals.items[next.local_sym_index]; | |
| 266 | 299 | return next_sym.n_value - self_sym.n_value; |
| 267 | 300 | } else { |
| 268 | 301 | // We are the last block. |
| ... | ... | @@ -274,8 +307,8 @@ pub const TextBlock = struct { |
| 274 | 307 | fn freeListEligible(self: TextBlock, macho_file: MachO) bool { |
| 275 | 308 | // No need to keep a free list node for the last block. |
| 276 | 309 | 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]; | |
| 279 | 312 | const cap = next_sym.n_value - self_sym.n_value; |
| 280 | 313 | const ideal_cap = padToIdeal(self.size); |
| 281 | 314 | if (cap <= ideal_cap) return false; |
| ... | ... | @@ -344,7 +377,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 344 | 377 | }; |
| 345 | 378 | |
| 346 | 379 | // Index 0 is always a null symbol. |
| 347 | try self.local_symbols.append(allocator, .{ | |
| 380 | try self.locals.append(allocator, .{ | |
| 348 | 381 | .n_strx = 0, |
| 349 | 382 | .n_type = 0, |
| 350 | 383 | .n_sect = 0, |
| ... | ... | @@ -600,7 +633,74 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 600 | 633 | if (!mem.eql(u8, the_object_path, full_out_path)) { |
| 601 | 634 | try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{}); |
| 602 | 635 | } |
| 603 | } else { | |
| 636 | } else outer: { | |
| 637 | const use_zld = blk: { | |
| 638 | if (self.base.options.is_native_os and self.base.options.system_linker_hack) { | |
| 639 | // If the user forces the use of ld64, make sure we are running native! | |
| 640 | break :blk false; | |
| 641 | } | |
| 642 | ||
| 643 | if (self.base.options.target.cpu.arch == .aarch64) { | |
| 644 | // On aarch64, always use zld. | |
| 645 | break :blk true; | |
| 646 | } | |
| 647 | ||
| 648 | if (self.base.options.link_libcpp or | |
| 649 | self.base.options.output_mode == .Lib or | |
| 650 | self.base.options.linker_script != null) | |
| 651 | { | |
| 652 | // Fallback to LLD in this handful of cases on x86_64 only. | |
| 653 | break :blk false; | |
| 654 | } | |
| 655 | ||
| 656 | break :blk true; | |
| 657 | }; | |
| 658 | ||
| 659 | if (use_zld) { | |
| 660 | var zld = Zld.init(self.base.allocator); | |
| 661 | defer zld.deinit(); | |
| 662 | zld.arch = target.cpu.arch; | |
| 663 | ||
| 664 | var input_files = std.ArrayList([]const u8).init(self.base.allocator); | |
| 665 | defer input_files.deinit(); | |
| 666 | // Positional arguments to the linker such as object files. | |
| 667 | try input_files.appendSlice(self.base.options.objects); | |
| 668 | for (comp.c_object_table.items()) |entry| { | |
| 669 | try input_files.append(entry.key.status.success.object_path); | |
| 670 | } | |
| 671 | if (module_obj_path) |p| { | |
| 672 | try input_files.append(p); | |
| 673 | } | |
| 674 | try input_files.append(comp.compiler_rt_static_lib.?.full_object_path); | |
| 675 | // libc++ dep | |
| 676 | if (self.base.options.link_libcpp) { | |
| 677 | try input_files.append(comp.libcxxabi_static_lib.?.full_object_path); | |
| 678 | try input_files.append(comp.libcxx_static_lib.?.full_object_path); | |
| 679 | } | |
| 680 | ||
| 681 | if (self.base.options.verbose_link) { | |
| 682 | var argv = std.ArrayList([]const u8).init(self.base.allocator); | |
| 683 | defer argv.deinit(); | |
| 684 | ||
| 685 | try argv.append("zig"); | |
| 686 | try argv.append("ld"); | |
| 687 | ||
| 688 | try argv.ensureCapacity(input_files.items.len); | |
| 689 | for (input_files.items) |f| { | |
| 690 | argv.appendAssumeCapacity(f); | |
| 691 | } | |
| 692 | ||
| 693 | try argv.append("-o"); | |
| 694 | try argv.append(full_out_path); | |
| 695 | ||
| 696 | Compilation.dump_argv(argv.items); | |
| 697 | } | |
| 698 | ||
| 699 | try zld.link(input_files.items, full_out_path); | |
| 700 | ||
| 701 | break :outer; | |
| 702 | } | |
| 703 | ||
| 604 | 704 | // Create an LLD command line and invoke it. |
| 605 | 705 | var argv = std.ArrayList([]const u8).init(self.base.allocator); |
| 606 | 706 | defer argv.deinit(); |
| ... | ... | @@ -871,119 +971,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 871 | 971 | log.warn("unexpected LLD stderr:\n{s}", .{stderr}); |
| 872 | 972 | } |
| 873 | 973 | } |
| 874 | ||
| 875 | // At this stage, LLD has done its job. It is time to patch the resultant | |
| 876 | // binaries up! | |
| 877 | const out_file = try directory.handle.openFile(self.base.options.emit.?.sub_path, .{ .write = true }); | |
| 878 | try self.parseFromFile(out_file); | |
| 879 | ||
| 880 | if (self.libsystem_cmd_index == null and self.header.?.filetype == macho.MH_EXECUTE) { | |
| 881 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 882 | const text_section = text_segment.sections.items[self.text_section_index.?]; | |
| 883 | const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64); | |
| 884 | const needed_size = padToIdeal(@sizeOf(macho.linkedit_data_command)); | |
| 885 | ||
| 886 | if (needed_size + after_last_cmd_offset > text_section.offset) { | |
| 887 | log.err("Unable to extend padding between the end of load commands and start of __text section.", .{}); | |
| 888 | log.err("Re-run the linker with '-headerpad 0x{x}' option if available, or", .{needed_size}); | |
| 889 | log.err("fall back to the system linker by exporting 'ZIG_SYSTEM_LINKER_HACK=1'.", .{}); | |
| 890 | return error.NotEnoughPadding; | |
| 891 | } | |
| 892 | ||
| 893 | // Calculate next available dylib ordinal. | |
| 894 | const next_ordinal = blk: { | |
| 895 | var ordinal: u32 = 1; | |
| 896 | for (self.load_commands.items) |cmd| { | |
| 897 | switch (cmd) { | |
| 898 | .Dylib => ordinal += 1, | |
| 899 | else => {}, | |
| 900 | } | |
| 901 | } | |
| 902 | break :blk ordinal; | |
| 903 | }; | |
| 904 | ||
| 905 | // Add load dylib load command | |
| 906 | self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 907 | const cmdsize = @intCast(u32, mem.alignForwardGeneric( | |
| 908 | u64, | |
| 909 | @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH), | |
| 910 | @sizeOf(u64), | |
| 911 | )); | |
| 912 | // TODO Find a way to work out runtime version from the OS version triple stored in std.Target. | |
| 913 | // In the meantime, we're gonna hardcode to the minimum compatibility version of 0.0.0. | |
| 914 | const min_version = 0x0; | |
| 915 | var dylib_cmd = emptyGenericCommandWithData(macho.dylib_command{ | |
| 916 | .cmd = macho.LC_LOAD_DYLIB, | |
| 917 | .cmdsize = cmdsize, | |
| 918 | .dylib = .{ | |
| 919 | .name = @sizeOf(macho.dylib_command), | |
| 920 | .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files | |
| 921 | .current_version = min_version, | |
| 922 | .compatibility_version = min_version, | |
| 923 | }, | |
| 924 | }); | |
| 925 | dylib_cmd.data = try self.base.allocator.alloc(u8, cmdsize - dylib_cmd.inner.dylib.name); | |
| 926 | mem.set(u8, dylib_cmd.data, 0); | |
| 927 | mem.copy(u8, dylib_cmd.data, mem.spanZ(LIB_SYSTEM_PATH)); | |
| 928 | try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd }); | |
| 929 | self.header_dirty = true; | |
| 930 | self.load_commands_dirty = true; | |
| 931 | ||
| 932 | if (self.symtab_cmd_index == null or self.dysymtab_cmd_index == null) { | |
| 933 | log.err("Incomplete Mach-O binary: no LC_SYMTAB or LC_DYSYMTAB load command found!", .{}); | |
| 934 | log.err("Without the symbol table, it is not possible to patch up the binary for cross-compilation.", .{}); | |
| 935 | return error.NoSymbolTableFound; | |
| 936 | } | |
| 937 | ||
| 938 | // Patch dyld info | |
| 939 | try self.fixupBindInfo(next_ordinal); | |
| 940 | try self.fixupLazyBindInfo(next_ordinal); | |
| 941 | ||
| 942 | // Write updated load commands and the header | |
| 943 | try self.writeLoadCommands(); | |
| 944 | try self.writeHeader(); | |
| 945 | ||
| 946 | assert(!self.header_dirty); | |
| 947 | assert(!self.load_commands_dirty); | |
| 948 | } | |
| 949 | if (self.code_signature_cmd_index == null) outer: { | |
| 950 | if (target.cpu.arch != .aarch64) break :outer; // This is currently needed only for aarch64 targets. | |
| 951 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 952 | const text_section = text_segment.sections.items[self.text_section_index.?]; | |
| 953 | const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64); | |
| 954 | const needed_size = padToIdeal(@sizeOf(macho.linkedit_data_command)); | |
| 955 | ||
| 956 | if (needed_size + after_last_cmd_offset > text_section.offset) { | |
| 957 | log.err("Unable to extend padding between the end of load commands and start of __text section.", .{}); | |
| 958 | log.err("Re-run the linker with '-headerpad 0x{x}' option if available, or", .{needed_size}); | |
| 959 | log.err("fall back to the system linker by exporting 'ZIG_SYSTEM_LINKER_HACK=1'.", .{}); | |
| 960 | return error.NotEnoughPadding; | |
| 961 | } | |
| 962 | ||
| 963 | // Add code signature load command | |
| 964 | self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 965 | try self.load_commands.append(self.base.allocator, .{ | |
| 966 | .LinkeditData = .{ | |
| 967 | .cmd = macho.LC_CODE_SIGNATURE, | |
| 968 | .cmdsize = @sizeOf(macho.linkedit_data_command), | |
| 969 | .dataoff = 0, | |
| 970 | .datasize = 0, | |
| 971 | }, | |
| 972 | }); | |
| 973 | self.header_dirty = true; | |
| 974 | self.load_commands_dirty = true; | |
| 975 | ||
| 976 | // Pad out space for code signature | |
| 977 | try self.writeCodeSignaturePadding(); | |
| 978 | // Write updated load commands and the header | |
| 979 | try self.writeLoadCommands(); | |
| 980 | try self.writeHeader(); | |
| 981 | // Generate adhoc code signature | |
| 982 | try self.writeCodeSignature(); | |
| 983 | ||
| 984 | assert(!self.header_dirty); | |
| 985 | assert(!self.load_commands_dirty); | |
| 986 | } | |
| 987 | 974 | } |
| 988 | 975 | } |
| 989 | 976 | |
| ... | ... | @@ -1019,14 +1006,14 @@ pub fn deinit(self: *MachO) void { |
| 1019 | 1006 | if (self.d_sym) |*ds| { |
| 1020 | 1007 | ds.deinit(self.base.allocator); |
| 1021 | 1008 | } |
| 1022 | for (self.extern_lazy_symbols.items()) |*entry| { | |
| 1009 | for (self.lazy_imports.items()) |*entry| { | |
| 1023 | 1010 | self.base.allocator.free(entry.key); |
| 1024 | 1011 | } |
| 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| { | |
| 1027 | 1014 | self.base.allocator.free(entry.key); |
| 1028 | 1015 | } |
| 1029 | self.extern_nonlazy_symbols.deinit(self.base.allocator); | |
| 1016 | self.nonlazy_imports.deinit(self.base.allocator); | |
| 1030 | 1017 | self.pie_fixups.deinit(self.base.allocator); |
| 1031 | 1018 | self.stub_fixups.deinit(self.base.allocator); |
| 1032 | 1019 | self.text_block_free_list.deinit(self.base.allocator); |
| ... | ... | @@ -1040,10 +1027,10 @@ pub fn deinit(self: *MachO) void { |
| 1040 | 1027 | } |
| 1041 | 1028 | self.string_table_directory.deinit(self.base.allocator); |
| 1042 | 1029 | 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); | |
| 1047 | 1034 | for (self.load_commands.items) |*lc| { |
| 1048 | 1035 | lc.deinit(self.base.allocator); |
| 1049 | 1036 | } |
| ... | ... | @@ -1098,7 +1085,7 @@ fn shrinkTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64) vo |
| 1098 | 1085 | } |
| 1099 | 1086 | |
| 1100 | 1087 | fn growTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { |
| 1101 | const sym = self.local_symbols.items[text_block.local_sym_index]; | |
| 1088 | const sym = self.locals.items[text_block.local_sym_index]; | |
| 1102 | 1089 | const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value; |
| 1103 | 1090 | const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*); |
| 1104 | 1091 | if (!need_realloc) return sym.n_value; |
| ... | ... | @@ -1108,34 +1095,41 @@ fn growTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alig |
| 1108 | 1095 | pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void { |
| 1109 | 1096 | if (decl.link.macho.local_sym_index != 0) return; |
| 1110 | 1097 | |
| 1111 | try self.local_symbols.ensureCapacity(self.base.allocator, self.local_symbols.items.len + 1); | |
| 1098 | try self.locals.ensureCapacity(self.base.allocator, self.locals.items.len + 1); | |
| 1112 | 1099 | try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); |
| 1113 | 1100 | |
| 1114 | if (self.local_symbol_free_list.popOrNull()) |i| { | |
| 1101 | if (self.locals_free_list.popOrNull()) |i| { | |
| 1115 | 1102 | log.debug("reusing symbol index {d} for {s}", .{ i, decl.name }); |
| 1116 | 1103 | decl.link.macho.local_sym_index = i; |
| 1117 | 1104 | } 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(); | |
| 1121 | 1108 | } |
| 1122 | 1109 | |
| 1123 | 1110 | if (self.offset_table_free_list.popOrNull()) |i| { |
| 1111 | log.debug("reusing offset table entry index {d} for {s}", .{ i, decl.name }); | |
| 1124 | 1112 | decl.link.macho.offset_table_index = i; |
| 1125 | 1113 | } else { |
| 1114 | log.debug("allocating offset table entry index {d} for {s}", .{ self.offset_table.items.len, decl.name }); | |
| 1126 | 1115 | decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len); |
| 1127 | 1116 | _ = self.offset_table.addOneAssumeCapacity(); |
| 1128 | 1117 | self.offset_table_count_dirty = true; |
| 1118 | self.rebase_info_dirty = true; | |
| 1129 | 1119 | } |
| 1130 | 1120 | |
| 1131 | self.local_symbols.items[decl.link.macho.local_sym_index] = .{ | |
| 1121 | self.locals.items[decl.link.macho.local_sym_index] = .{ | |
| 1132 | 1122 | .n_strx = 0, |
| 1133 | 1123 | .n_type = 0, |
| 1134 | 1124 | .n_sect = 0, |
| 1135 | 1125 | .n_desc = 0, |
| 1136 | 1126 | .n_value = 0, |
| 1137 | 1127 | }; |
| 1138 | self.offset_table.items[decl.link.macho.offset_table_index] = 0; | |
| 1128 | self.offset_table.items[decl.link.macho.offset_table_index] = .{ | |
| 1129 | .kind = .Local, | |
| 1130 | .symbol = decl.link.macho.local_sym_index, | |
| 1131 | .index = decl.link.macho.offset_table_index, | |
| 1132 | }; | |
| 1139 | 1133 | } |
| 1140 | 1134 | |
| 1141 | 1135 | pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| ... | ... | @@ -1178,8 +1172,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1178 | 1172 | .externally_managed => |x| x, |
| 1179 | 1173 | .appended => code_buffer.items, |
| 1180 | 1174 | .fail => |em| { |
| 1181 | // Clear any PIE fixups and stub fixups for this decl. | |
| 1175 | // Clear any PIE fixups for this decl. | |
| 1182 | 1176 | self.pie_fixups.shrinkRetainingCapacity(0); |
| 1177 | // Clear any stub fixups for this decl. | |
| 1183 | 1178 | self.stub_fixups.shrinkRetainingCapacity(0); |
| 1184 | 1179 | decl.analysis = .codegen_failure; |
| 1185 | 1180 | try module.failed_decls.put(module.gpa, decl, em); |
| ... | ... | @@ -1189,7 +1184,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1189 | 1184 | |
| 1190 | 1185 | const required_alignment = typed_value.ty.abiAlignment(self.base.options.target); |
| 1191 | 1186 | assert(decl.link.macho.local_sym_index != 0); // Caller forgot to call allocateDeclIndexes() |
| 1192 | const symbol = &self.local_symbols.items[decl.link.macho.local_sym_index]; | |
| 1187 | const symbol = &self.locals.items[decl.link.macho.local_sym_index]; | |
| 1193 | 1188 | |
| 1194 | 1189 | if (decl.link.macho.size != 0) { |
| 1195 | 1190 | const capacity = decl.link.macho.capacity(self.*); |
| ... | ... | @@ -1198,9 +1193,12 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1198 | 1193 | const vaddr = try self.growTextBlock(&decl.link.macho, code.len, required_alignment); |
| 1199 | 1194 | log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr }); |
| 1200 | 1195 | if (vaddr != symbol.n_value) { |
| 1201 | symbol.n_value = vaddr; | |
| 1202 | 1196 | log.debug(" (writing new offset table entry)", .{}); |
| 1203 | self.offset_table.items[decl.link.macho.offset_table_index] = vaddr; | |
| 1197 | self.offset_table.items[decl.link.macho.offset_table_index] = .{ | |
| 1198 | .kind = .Local, | |
| 1199 | .symbol = decl.link.macho.local_sym_index, | |
| 1200 | .index = decl.link.macho.offset_table_index, | |
| 1201 | }; | |
| 1204 | 1202 | try self.writeOffsetTableEntry(decl.link.macho.offset_table_index); |
| 1205 | 1203 | } |
| 1206 | 1204 | } else if (code.len < decl.link.macho.size) { |
| ... | ... | @@ -1229,7 +1227,11 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1229 | 1227 | .n_desc = 0, |
| 1230 | 1228 | .n_value = addr, |
| 1231 | 1229 | }; |
| 1232 | self.offset_table.items[decl.link.macho.offset_table_index] = addr; | |
| 1230 | self.offset_table.items[decl.link.macho.offset_table_index] = .{ | |
| 1231 | .kind = .Local, | |
| 1232 | .symbol = decl.link.macho.local_sym_index, | |
| 1233 | .index = decl.link.macho.offset_table_index, | |
| 1234 | }; | |
| 1233 | 1235 | |
| 1234 | 1236 | try self.writeLocalSymbol(decl.link.macho.local_sym_index); |
| 1235 | 1237 | if (self.d_sym) |*ds| |
| ... | ... | @@ -1237,30 +1239,48 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1237 | 1239 | try self.writeOffsetTableEntry(decl.link.macho.offset_table_index); |
| 1238 | 1240 | } |
| 1239 | 1241 | |
| 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). | |
| 1243 | 1243 | 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 | ||
| 1246 | 1248 | switch (self.base.options.target.cpu.arch) { |
| 1247 | 1249 | .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); | |
| 1252 | 1252 | }, |
| 1253 | 1253 | .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 | } | |
| 1258 | 1277 | }, |
| 1259 | 1278 | else => unreachable, // unsupported target architecture |
| 1260 | 1279 | } |
| 1261 | 1280 | } |
| 1262 | 1281 | |
| 1263 | 1282 | // Resolve stubs (if any) |
| 1283 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1264 | 1284 | const stubs = text_segment.sections.items[self.stubs_section_index.?]; |
| 1265 | 1285 | for (self.stub_fixups.items) |fixup| { |
| 1266 | 1286 | const stub_addr = stubs.addr + fixup.symbol * stubs.reserved2; |
| ... | ... | @@ -1285,9 +1305,6 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1285 | 1305 | try self.writeStubInStubHelper(fixup.symbol); |
| 1286 | 1306 | try self.writeLazySymbolPointer(fixup.symbol); |
| 1287 | 1307 | |
| 1288 | const extern_sym = &self.extern_lazy_symbols.items()[fixup.symbol].value; | |
| 1289 | extern_sym.segment = self.data_segment_cmd_index.?; | |
| 1290 | extern_sym.offset = fixup.symbol * @sizeOf(u64); | |
| 1291 | 1308 | self.rebase_info_dirty = true; |
| 1292 | 1309 | self.lazy_binding_info_dirty = true; |
| 1293 | 1310 | } |
| ... | ... | @@ -1329,9 +1346,9 @@ pub fn updateDeclExports( |
| 1329 | 1346 | const tracy = trace(@src()); |
| 1330 | 1347 | defer tracy.end(); |
| 1331 | 1348 | |
| 1332 | try self.global_symbols.ensureCapacity(self.base.allocator, self.global_symbols.items.len + exports.len); | |
| 1349 | try self.globals.ensureCapacity(self.base.allocator, self.globals.items.len + exports.len); | |
| 1333 | 1350 | if (decl.link.macho.local_sym_index == 0) return; |
| 1334 | const decl_sym = &self.local_symbols.items[decl.link.macho.local_sym_index]; | |
| 1351 | const decl_sym = &self.locals.items[decl.link.macho.local_sym_index]; | |
| 1335 | 1352 | |
| 1336 | 1353 | for (exports) |exp| { |
| 1337 | 1354 | if (exp.options.section) |section_name| { |
| ... | ... | @@ -1364,7 +1381,7 @@ pub fn updateDeclExports( |
| 1364 | 1381 | }; |
| 1365 | 1382 | const n_type = decl_sym.n_type | macho.N_EXT; |
| 1366 | 1383 | if (exp.link.macho.sym_index) |i| { |
| 1367 | const sym = &self.global_symbols.items[i]; | |
| 1384 | const sym = &self.globals.items[i]; | |
| 1368 | 1385 | sym.* = .{ |
| 1369 | 1386 | .n_strx = try self.updateString(sym.n_strx, exp.options.name), |
| 1370 | 1387 | .n_type = n_type, |
| ... | ... | @@ -1374,12 +1391,12 @@ pub fn updateDeclExports( |
| 1374 | 1391 | }; |
| 1375 | 1392 | } else { |
| 1376 | 1393 | const name_str_index = try self.makeString(exp.options.name); |
| 1377 | const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: { | |
| 1378 | _ = self.global_symbols.addOneAssumeCapacity(); | |
| 1394 | const i = if (self.globals_free_list.popOrNull()) |i| i else blk: { | |
| 1395 | _ = self.globals.addOneAssumeCapacity(); | |
| 1379 | 1396 | self.export_info_dirty = true; |
| 1380 | break :blk self.global_symbols.items.len - 1; | |
| 1397 | break :blk self.globals.items.len - 1; | |
| 1381 | 1398 | }; |
| 1382 | self.global_symbols.items[i] = .{ | |
| 1399 | self.globals.items[i] = .{ | |
| 1383 | 1400 | .n_strx = name_str_index, |
| 1384 | 1401 | .n_type = n_type, |
| 1385 | 1402 | .n_sect = @intCast(u8, self.text_section_index.?) + 1, |
| ... | ... | @@ -1394,18 +1411,18 @@ pub fn updateDeclExports( |
| 1394 | 1411 | |
| 1395 | 1412 | pub fn deleteExport(self: *MachO, exp: Export) void { |
| 1396 | 1413 | 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; | |
| 1399 | 1416 | } |
| 1400 | 1417 | |
| 1401 | 1418 | pub fn freeDecl(self: *MachO, decl: *Module.Decl) void { |
| 1402 | 1419 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. |
| 1403 | 1420 | self.freeTextBlock(&decl.link.macho); |
| 1404 | 1421 | if (decl.link.macho.local_sym_index != 0) { |
| 1405 | self.local_symbol_free_list.append(self.base.allocator, decl.link.macho.local_sym_index) catch {}; | |
| 1422 | self.locals_free_list.append(self.base.allocator, decl.link.macho.local_sym_index) catch {}; | |
| 1406 | 1423 | self.offset_table_free_list.append(self.base.allocator, decl.link.macho.offset_table_index) catch {}; |
| 1407 | 1424 | |
| 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; | |
| 1409 | 1426 | |
| 1410 | 1427 | decl.link.macho.local_sym_index = 0; |
| 1411 | 1428 | } |
| ... | ... | @@ -1413,7 +1430,7 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void { |
| 1413 | 1430 | |
| 1414 | 1431 | pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 { |
| 1415 | 1432 | assert(decl.link.macho.local_sym_index != 0); |
| 1416 | return self.local_symbols.items[decl.link.macho.local_sym_index].n_value; | |
| 1433 | return self.locals.items[decl.link.macho.local_sym_index].n_value; | |
| 1417 | 1434 | } |
| 1418 | 1435 | |
| 1419 | 1436 | pub fn populateMissingMetadata(self: *MachO) !void { |
| ... | ... | @@ -1553,39 +1570,6 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1553 | 1570 | self.header_dirty = true; |
| 1554 | 1571 | self.load_commands_dirty = true; |
| 1555 | 1572 | } |
| 1556 | if (self.got_section_index == null) { | |
| 1557 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1558 | self.got_section_index = @intCast(u16, text_segment.sections.items.len); | |
| 1559 | ||
| 1560 | const alignment: u2 = switch (self.base.options.target.cpu.arch) { | |
| 1561 | .x86_64 => 0, | |
| 1562 | .aarch64 => 2, | |
| 1563 | else => unreachable, // unhandled architecture type | |
| 1564 | }; | |
| 1565 | const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS; | |
| 1566 | const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint; | |
| 1567 | const off = text_segment.findFreeSpace(needed_size, @alignOf(u64), self.header_pad); | |
| 1568 | assert(off + needed_size <= text_segment.inner.fileoff + text_segment.inner.filesize); // TODO Must expand __TEXT segment. | |
| 1569 | ||
| 1570 | log.debug("found __ziggot section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); | |
| 1571 | ||
| 1572 | try text_segment.addSection(self.base.allocator, .{ | |
| 1573 | .sectname = makeStaticString("__ziggot"), | |
| 1574 | .segname = makeStaticString("__TEXT"), | |
| 1575 | .addr = text_segment.inner.vmaddr + off, | |
| 1576 | .size = needed_size, | |
| 1577 | .offset = @intCast(u32, off), | |
| 1578 | .@"align" = alignment, | |
| 1579 | .reloff = 0, | |
| 1580 | .nreloc = 0, | |
| 1581 | .flags = flags, | |
| 1582 | .reserved1 = 0, | |
| 1583 | .reserved2 = 0, | |
| 1584 | .reserved3 = 0, | |
| 1585 | }); | |
| 1586 | self.header_dirty = true; | |
| 1587 | self.load_commands_dirty = true; | |
| 1588 | } | |
| 1589 | 1573 | if (self.stubs_section_index == null) { |
| 1590 | 1574 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1591 | 1575 | self.stubs_section_index = @intCast(u16, text_segment.sections.items.len); |
| ... | ... | @@ -1597,7 +1581,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1597 | 1581 | }; |
| 1598 | 1582 | const stub_size: u4 = switch (self.base.options.target.cpu.arch) { |
| 1599 | 1583 | .x86_64 => 6, |
| 1600 | .aarch64 => 2 * @sizeOf(u32), | |
| 1584 | .aarch64 => 3 * @sizeOf(u32), | |
| 1601 | 1585 | else => unreachable, // unhandled architecture type |
| 1602 | 1586 | }; |
| 1603 | 1587 | const flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS; |
| ... | ... | @@ -1686,9 +1670,9 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1686 | 1670 | self.header_dirty = true; |
| 1687 | 1671 | self.load_commands_dirty = true; |
| 1688 | 1672 | } |
| 1689 | if (self.data_got_section_index == null) { | |
| 1673 | if (self.got_section_index == null) { | |
| 1690 | 1674 | const dc_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; |
| 1691 | self.data_got_section_index = @intCast(u16, dc_segment.sections.items.len); | |
| 1675 | self.got_section_index = @intCast(u16, dc_segment.sections.items.len); | |
| 1692 | 1676 | |
| 1693 | 1677 | const flags = macho.S_NON_LAZY_SYMBOL_POINTERS; |
| 1694 | 1678 | const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint; |
| ... | ... | @@ -2060,12 +2044,12 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 2060 | 2044 | self.header_dirty = true; |
| 2061 | 2045 | self.load_commands_dirty = true; |
| 2062 | 2046 | } |
| 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); | |
| 2065 | 2049 | const name = try self.base.allocator.dupe(u8, "dyld_stub_binder"); |
| 2066 | 2050 | 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 = .{ | |
| 2069 | 2053 | .n_strx = offset, |
| 2070 | 2054 | .n_type = std.macho.N_UNDF | std.macho.N_EXT, |
| 2071 | 2055 | .n_sect = 0, |
| ... | ... | @@ -2073,68 +2057,19 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 2073 | 2057 | .n_value = 0, |
| 2074 | 2058 | }, |
| 2075 | 2059 | .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, | |
| 2078 | 2067 | }); |
| 2068 | try self.writeOffsetTableEntry(off_index); | |
| 2079 | 2069 | self.binding_info_dirty = true; |
| 2080 | 2070 | } |
| 2081 | 2071 | 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(); | |
| 2138 | 2073 | } |
| 2139 | 2074 | } |
| 2140 | 2075 | |
| ... | ... | @@ -2159,7 +2094,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 2159 | 2094 | const big_block = self.text_block_free_list.items[i]; |
| 2160 | 2095 | // We now have a pointer to a live text block that has too much capacity. |
| 2161 | 2096 | // Is it enough that we could fit this new text block? |
| 2162 | const sym = self.local_symbols.items[big_block.local_sym_index]; | |
| 2097 | const sym = self.locals.items[big_block.local_sym_index]; | |
| 2163 | 2098 | const capacity = big_block.capacity(self.*); |
| 2164 | 2099 | const ideal_capacity = padToIdeal(capacity); |
| 2165 | 2100 | const ideal_capacity_end_vaddr = sym.n_value + ideal_capacity; |
| ... | ... | @@ -2190,7 +2125,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 2190 | 2125 | } |
| 2191 | 2126 | break :blk new_start_vaddr; |
| 2192 | 2127 | } else if (self.last_text_block) |last| { |
| 2193 | const last_symbol = self.local_symbols.items[last.local_sym_index]; | |
| 2128 | const last_symbol = self.locals.items[last.local_sym_index]; | |
| 2194 | 2129 | // TODO We should pad out the excess capacity with NOPs. For executables, |
| 2195 | 2130 | // no padding seems to be OK, but it will probably not be for objects. |
| 2196 | 2131 | const ideal_capacity = padToIdeal(last.size); |
| ... | ... | @@ -2288,12 +2223,12 @@ fn updateString(self: *MachO, old_str_off: u32, new_name: []const u8) !u32 { |
| 2288 | 2223 | } |
| 2289 | 2224 | |
| 2290 | 2225 | pub fn addExternSymbol(self: *MachO, name: []const u8) !u32 { |
| 2291 | const index = @intCast(u32, self.extern_lazy_symbols.items().len); | |
| 2226 | const index = @intCast(u32, self.lazy_imports.items().len); | |
| 2292 | 2227 | const offset = try self.makeString(name); |
| 2293 | 2228 | const sym_name = try self.base.allocator.dupe(u8, name); |
| 2294 | 2229 | const dylib_ordinal = 1; // TODO this is now hardcoded, since we only support libSystem. |
| 2295 | try self.extern_lazy_symbols.putNoClobber(self.base.allocator, sym_name, .{ | |
| 2296 | .inner = .{ | |
| 2230 | try self.lazy_imports.putNoClobber(self.base.allocator, sym_name, .{ | |
| 2231 | .symbol = .{ | |
| 2297 | 2232 | .n_strx = offset, |
| 2298 | 2233 | .n_type = macho.N_UNDF | macho.N_EXT, |
| 2299 | 2234 | .n_sect = 0, |
| ... | ... | @@ -2301,6 +2236,7 @@ pub fn addExternSymbol(self: *MachO, name: []const u8) !u32 { |
| 2301 | 2236 | .n_value = 0, |
| 2302 | 2237 | }, |
| 2303 | 2238 | .dylib_ordinal = dylib_ordinal, |
| 2239 | .index = index, | |
| 2304 | 2240 | }); |
| 2305 | 2241 | log.debug("adding new extern symbol '{s}' with dylib ordinal '{}'", .{ name, dylib_ordinal }); |
| 2306 | 2242 | return index; |
| ... | ... | @@ -2459,41 +2395,29 @@ fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16, sta |
| 2459 | 2395 | } |
| 2460 | 2396 | |
| 2461 | 2397 | fn 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.?]; | |
| 2464 | 2400 | const off = sect.offset + @sizeOf(u64) * index; |
| 2465 | const vmaddr = sect.addr + @sizeOf(u64) * index; | |
| 2466 | 2401 | |
| 2467 | 2402 | if (self.offset_table_count_dirty) { |
| 2468 | 2403 | // TODO relocate. |
| 2469 | 2404 | self.offset_table_count_dirty = false; |
| 2470 | 2405 | } |
| 2471 | 2406 | |
| 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); | |
| 2497 | 2421 | } |
| 2498 | 2422 | |
| 2499 | 2423 | fn writeLazySymbolPointer(self: *MachO, index: u32) !void { |
| ... | ... | @@ -2516,6 +2440,133 @@ fn writeLazySymbolPointer(self: *MachO, index: u32) !void { |
| 2516 | 2440 | try self.base.file.?.pwriteAll(&buf, off); |
| 2517 | 2441 | } |
| 2518 | 2442 | |
| 2443 | fn 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 | ||
| 2519 | 2570 | fn writeStub(self: *MachO, index: u32) !void { |
| 2520 | 2571 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 2521 | 2572 | const stubs = text_segment.sections.items[self.stubs_section_index.?]; |
| ... | ... | @@ -2525,9 +2576,12 @@ fn writeStub(self: *MachO, index: u32) !void { |
| 2525 | 2576 | const stub_off = stubs.offset + index * stubs.reserved2; |
| 2526 | 2577 | const stub_addr = stubs.addr + index * stubs.reserved2; |
| 2527 | 2578 | const la_ptr_addr = la_symbol_ptr.addr + index * @sizeOf(u64); |
| 2579 | ||
| 2528 | 2580 | log.debug("writing stub at 0x{x}", .{stub_off}); |
| 2581 | ||
| 2529 | 2582 | var code = try self.base.allocator.alloc(u8, stubs.reserved2); |
| 2530 | 2583 | defer self.base.allocator.free(code); |
| 2584 | ||
| 2531 | 2585 | switch (self.base.options.target.cpu.arch) { |
| 2532 | 2586 | .x86_64 => { |
| 2533 | 2587 | assert(la_ptr_addr >= stub_addr + stubs.reserved2); |
| ... | ... | @@ -2539,12 +2593,50 @@ fn writeStub(self: *MachO, index: u32) !void { |
| 2539 | 2593 | }, |
| 2540 | 2594 | .aarch64 => { |
| 2541 | 2595 | 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()); | |
| 2548 | 2640 | }, |
| 2549 | 2641 | else => unreachable, |
| 2550 | 2642 | } |
| ... | ... | @@ -2561,8 +2653,10 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void { |
| 2561 | 2653 | else => unreachable, |
| 2562 | 2654 | }; |
| 2563 | 2655 | const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size; |
| 2656 | ||
| 2564 | 2657 | var code = try self.base.allocator.alloc(u8, stub_size); |
| 2565 | 2658 | defer self.base.allocator.free(code); |
| 2659 | ||
| 2566 | 2660 | switch (self.base.options.target.cpu.arch) { |
| 2567 | 2661 | .x86_64 => { |
| 2568 | 2662 | const displacement = try math.cast( |
| ... | ... | @@ -2577,12 +2671,19 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void { |
| 2577 | 2671 | mem.writeIntLittle(u32, code[6..][0..4], @bitCast(u32, displacement)); |
| 2578 | 2672 | }, |
| 2579 | 2673 | .aarch64 => { |
| 2580 | const displacement = try math.cast(i28, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4); | |
| 2674 | const literal = blk: { | |
| 2675 | const div_res = try math.divExact(u64, stub_size - @sizeOf(u32), 4); | |
| 2676 | break :blk try math.cast(u18, div_res); | |
| 2677 | }; | |
| 2678 | // ldr w16, literal | |
| 2581 | 2679 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{ |
| 2582 | .literal = @divExact(stub_size - @sizeOf(u32), 4), | |
| 2680 | .literal = literal, | |
| 2583 | 2681 | }).toU32()); |
| 2682 | const displacement = try math.cast(i28, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4); | |
| 2683 | // b disp | |
| 2584 | 2684 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(displacement).toU32()); |
| 2585 | mem.writeIntLittle(u32, code[8..12], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`. | |
| 2685 | // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`. | |
| 2686 | mem.writeIntLittle(u32, code[8..12], 0x0); | |
| 2586 | 2687 | }, |
| 2587 | 2688 | else => unreachable, |
| 2588 | 2689 | } |
| ... | ... | @@ -2591,9 +2692,9 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void { |
| 2591 | 2692 | |
| 2592 | 2693 | fn relocateSymbolTable(self: *MachO) !void { |
| 2593 | 2694 | 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; | |
| 2597 | 2698 | const nsyms = nlocals + nglobals + nundefs; |
| 2598 | 2699 | |
| 2599 | 2700 | if (symtab.nsyms < nsyms) { |
| ... | ... | @@ -2628,7 +2729,7 @@ fn writeLocalSymbol(self: *MachO, index: usize) !void { |
| 2628 | 2729 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 2629 | 2730 | const off = symtab.symoff + @sizeOf(macho.nlist_64) * index; |
| 2630 | 2731 | log.debug("writing local symbol {} at 0x{x}", .{ index, off }); |
| 2631 | try self.base.file.?.pwriteAll(mem.asBytes(&self.local_symbols.items[index]), off); | |
| 2732 | try self.base.file.?.pwriteAll(mem.asBytes(&self.locals.items[index]), off); | |
| 2632 | 2733 | } |
| 2633 | 2734 | |
| 2634 | 2735 | fn writeAllGlobalAndUndefSymbols(self: *MachO) !void { |
| ... | ... | @@ -2637,18 +2738,18 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void { |
| 2637 | 2738 | |
| 2638 | 2739 | try self.relocateSymbolTable(); |
| 2639 | 2740 | 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; | |
| 2642 | 2743 | |
| 2643 | const nundefs = self.extern_lazy_symbols.items().len + self.extern_nonlazy_symbols.items().len; | |
| 2744 | const nundefs = self.lazy_imports.items().len + self.nonlazy_imports.items().len; | |
| 2644 | 2745 | var undefs = std.ArrayList(macho.nlist_64).init(self.base.allocator); |
| 2645 | 2746 | defer undefs.deinit(); |
| 2646 | 2747 | 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); | |
| 2649 | 2750 | } |
| 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); | |
| 2652 | 2753 | } |
| 2653 | 2754 | |
| 2654 | 2755 | const locals_off = symtab.symoff; |
| ... | ... | @@ -2657,7 +2758,7 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void { |
| 2657 | 2758 | const globals_off = locals_off + locals_size; |
| 2658 | 2759 | const globals_size = nglobals * @sizeOf(macho.nlist_64); |
| 2659 | 2760 | log.debug("writing global symbols from 0x{x} to 0x{x}", .{ globals_off, globals_size + globals_off }); |
| 2660 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.global_symbols.items), globals_off); | |
| 2761 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.globals.items), globals_off); | |
| 2661 | 2762 | |
| 2662 | 2763 | const undefs_off = globals_off + globals_size; |
| 2663 | 2764 | const undefs_size = nundefs * @sizeOf(macho.nlist_64); |
| ... | ... | @@ -2683,15 +2784,15 @@ fn writeIndirectSymbolTable(self: *MachO) !void { |
| 2683 | 2784 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 2684 | 2785 | const stubs = &text_segment.sections.items[self.stubs_section_index.?]; |
| 2685 | 2786 | const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; |
| 2686 | const got = &data_const_seg.sections.items[self.data_got_section_index.?]; | |
| 2787 | const got = &data_const_seg.sections.items[self.got_section_index.?]; | |
| 2687 | 2788 | const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; |
| 2688 | 2789 | const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?]; |
| 2689 | 2790 | const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; |
| 2690 | 2791 | |
| 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; | |
| 2693 | 2794 | const allocated_size = self.allocatedSizeLinkedit(dysymtab.indirectsymoff); |
| 2694 | const nindirectsyms = @intCast(u32, lazy.len * 2 + nonlazy.len); | |
| 2795 | const nindirectsyms = @intCast(u32, lazy.len * 2 + got_entries.len); | |
| 2695 | 2796 | const needed_size = @intCast(u32, nindirectsyms * @sizeOf(u32)); |
| 2696 | 2797 | |
| 2697 | 2798 | if (needed_size > allocated_size) { |
| ... | ... | @@ -2710,20 +2811,27 @@ fn writeIndirectSymbolTable(self: *MachO) !void { |
| 2710 | 2811 | var writer = stream.writer(); |
| 2711 | 2812 | |
| 2712 | 2813 | stubs.reserved1 = 0; |
| 2713 | for (self.extern_lazy_symbols.items()) |_, i| { | |
| 2814 | for (lazy) |_, i| { | |
| 2714 | 2815 | const symtab_idx = @intCast(u32, dysymtab.iundefsym + i); |
| 2715 | 2816 | try writer.writeIntLittle(u32, symtab_idx); |
| 2716 | 2817 | } |
| 2717 | 2818 | |
| 2718 | 2819 | const base_id = @intCast(u32, lazy.len); |
| 2719 | 2820 | 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 | } | |
| 2723 | 2831 | } |
| 2724 | 2832 | |
| 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| { | |
| 2727 | 2835 | const symtab_idx = @intCast(u32, dysymtab.iundefsym + i); |
| 2728 | 2836 | try writer.writeIntLittle(u32, symtab_idx); |
| 2729 | 2837 | } |
| ... | ... | @@ -2789,7 +2897,7 @@ fn writeCodeSignature(self: *MachO) !void { |
| 2789 | 2897 | |
| 2790 | 2898 | fn writeExportTrie(self: *MachO) !void { |
| 2791 | 2899 | if (!self.export_info_dirty) return; |
| 2792 | if (self.global_symbols.items.len == 0) return; | |
| 2900 | if (self.globals.items.len == 0) return; | |
| 2793 | 2901 | |
| 2794 | 2902 | const tracy = trace(@src()); |
| 2795 | 2903 | defer tracy.end(); |
| ... | ... | @@ -2798,7 +2906,7 @@ fn writeExportTrie(self: *MachO) !void { |
| 2798 | 2906 | defer trie.deinit(); |
| 2799 | 2907 | |
| 2800 | 2908 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 2801 | for (self.global_symbols.items) |symbol| { | |
| 2909 | for (self.globals.items) |symbol| { | |
| 2802 | 2910 | // TODO figure out if we should put all global symbols into the export trie |
| 2803 | 2911 | const name = self.getString(symbol.n_strx); |
| 2804 | 2912 | assert(symbol.n_value >= text_segment.inner.vmaddr); |
| ... | ... | @@ -2840,14 +2948,48 @@ fn writeRebaseInfoTable(self: *MachO) !void { |
| 2840 | 2948 | const tracy = trace(@src()); |
| 2841 | 2949 | defer tracy.end(); |
| 2842 | 2950 | |
| 2843 | const size = try rebaseInfoSize(self.extern_lazy_symbols.items()); | |
| 2951 | var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator); | |
| 2952 | defer pointers.deinit(); | |
| 2953 | ||
| 2954 | if (self.got_section_index) |idx| { | |
| 2955 | const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 2956 | const sect = seg.sections.items[idx]; | |
| 2957 | const base_offset = sect.addr - seg.inner.vmaddr; | |
| 2958 | const segment_id = self.data_const_segment_cmd_index.?; | |
| 2959 | ||
| 2960 | for (self.offset_table.items) |entry| { | |
| 2961 | if (entry.kind == .Extern) continue; | |
| 2962 | try pointers.append(.{ | |
| 2963 | .offset = base_offset + entry.index * @sizeOf(u64), | |
| 2964 | .segment_id = segment_id, | |
| 2965 | }); | |
| 2966 | } | |
| 2967 | } | |
| 2968 | ||
| 2969 | if (self.la_symbol_ptr_section_index) |idx| { | |
| 2970 | try pointers.ensureCapacity(pointers.items.len + self.lazy_imports.items().len); | |
| 2971 | const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2972 | const sect = seg.sections.items[idx]; | |
| 2973 | const base_offset = sect.addr - seg.inner.vmaddr; | |
| 2974 | const segment_id = self.data_segment_cmd_index.?; | |
| 2975 | ||
| 2976 | for (self.lazy_imports.items()) |entry| { | |
| 2977 | pointers.appendAssumeCapacity(.{ | |
| 2978 | .offset = base_offset + entry.value.index * @sizeOf(u64), | |
| 2979 | .segment_id = segment_id, | |
| 2980 | }); | |
| 2981 | } | |
| 2982 | } | |
| 2983 | ||
| 2984 | std.sort.sort(bind.Pointer, pointers.items, {}, bind.pointerCmp); | |
| 2985 | ||
| 2986 | const size = try bind.rebaseInfoSize(pointers.items); | |
| 2844 | 2987 | var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); |
| 2845 | 2988 | defer self.base.allocator.free(buffer); |
| 2846 | 2989 | |
| 2847 | 2990 | var stream = std.io.fixedBufferStream(buffer); |
| 2848 | try writeRebaseInfo(self.extern_lazy_symbols.items(), stream.writer()); | |
| 2991 | try bind.writeRebaseInfo(pointers.items, stream.writer()); | |
| 2849 | 2992 | |
| 2850 | const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 2851 | 2993 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; |
| 2852 | 2994 | const allocated_size = self.allocatedSizeLinkedit(dyld_info.rebase_off); |
| 2853 | 2995 | const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)); |
| ... | ... | @@ -2872,14 +3014,34 @@ fn writeBindingInfoTable(self: *MachO) !void { |
| 2872 | 3014 | const tracy = trace(@src()); |
| 2873 | 3015 | defer tracy.end(); |
| 2874 | 3016 | |
| 2875 | const size = try bindInfoSize(self.extern_nonlazy_symbols.items()); | |
| 3017 | var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator); | |
| 3018 | defer pointers.deinit(); | |
| 3019 | ||
| 3020 | if (self.got_section_index) |idx| { | |
| 3021 | const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 3022 | const sect = seg.sections.items[idx]; | |
| 3023 | const base_offset = sect.addr - seg.inner.vmaddr; | |
| 3024 | const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); | |
| 3025 | ||
| 3026 | for (self.offset_table.items) |entry| { | |
| 3027 | if (entry.kind == .Local) continue; | |
| 3028 | const import = self.nonlazy_imports.items()[entry.symbol]; | |
| 3029 | try pointers.append(.{ | |
| 3030 | .offset = base_offset + entry.index * @sizeOf(u64), | |
| 3031 | .segment_id = segment_id, | |
| 3032 | .dylib_ordinal = import.value.dylib_ordinal, | |
| 3033 | .name = import.key, | |
| 3034 | }); | |
| 3035 | } | |
| 3036 | } | |
| 3037 | ||
| 3038 | const size = try bind.bindInfoSize(pointers.items); | |
| 2876 | 3039 | var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); |
| 2877 | 3040 | defer self.base.allocator.free(buffer); |
| 2878 | 3041 | |
| 2879 | 3042 | var stream = std.io.fixedBufferStream(buffer); |
| 2880 | try writeBindInfo(self.extern_nonlazy_symbols.items(), stream.writer()); | |
| 3043 | try bind.writeBindInfo(pointers.items, stream.writer()); | |
| 2881 | 3044 | |
| 2882 | const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 2883 | 3045 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; |
| 2884 | 3046 | const allocated_size = self.allocatedSizeLinkedit(dyld_info.bind_off); |
| 2885 | 3047 | const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)); |
| ... | ... | @@ -2901,14 +3063,36 @@ fn writeBindingInfoTable(self: *MachO) !void { |
| 2901 | 3063 | fn writeLazyBindingInfoTable(self: *MachO) !void { |
| 2902 | 3064 | if (!self.lazy_binding_info_dirty) return; |
| 2903 | 3065 | |
| 2904 | const size = try lazyBindInfoSize(self.extern_lazy_symbols.items()); | |
| 3066 | const tracy = trace(@src()); | |
| 3067 | defer tracy.end(); | |
| 3068 | ||
| 3069 | var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator); | |
| 3070 | defer pointers.deinit(); | |
| 3071 | ||
| 3072 | if (self.la_symbol_ptr_section_index) |idx| { | |
| 3073 | try pointers.ensureCapacity(self.lazy_imports.items().len); | |
| 3074 | const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 3075 | const sect = seg.sections.items[idx]; | |
| 3076 | const base_offset = sect.addr - seg.inner.vmaddr; | |
| 3077 | const segment_id = @intCast(u16, self.data_segment_cmd_index.?); | |
| 3078 | ||
| 3079 | for (self.lazy_imports.items()) |entry| { | |
| 3080 | pointers.appendAssumeCapacity(.{ | |
| 3081 | .offset = base_offset + entry.value.index * @sizeOf(u64), | |
| 3082 | .segment_id = segment_id, | |
| 3083 | .dylib_ordinal = entry.value.dylib_ordinal, | |
| 3084 | .name = entry.key, | |
| 3085 | }); | |
| 3086 | } | |
| 3087 | } | |
| 3088 | ||
| 3089 | const size = try bind.lazyBindInfoSize(pointers.items); | |
| 2905 | 3090 | var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); |
| 2906 | 3091 | defer self.base.allocator.free(buffer); |
| 2907 | 3092 | |
| 2908 | 3093 | var stream = std.io.fixedBufferStream(buffer); |
| 2909 | try writeLazyBindInfo(self.extern_lazy_symbols.items(), stream.writer()); | |
| 3094 | try bind.writeLazyBindInfo(pointers.items, stream.writer()); | |
| 2910 | 3095 | |
| 2911 | const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 2912 | 3096 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; |
| 2913 | 3097 | const allocated_size = self.allocatedSizeLinkedit(dyld_info.lazy_bind_off); |
| 2914 | 3098 | const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)); |
| ... | ... | @@ -2929,7 +3113,7 @@ fn writeLazyBindingInfoTable(self: *MachO) !void { |
| 2929 | 3113 | } |
| 2930 | 3114 | |
| 2931 | 3115 | fn 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; | |
| 2933 | 3117 | |
| 2934 | 3118 | var stream = std.io.fixedBufferStream(buffer); |
| 2935 | 3119 | var reader = stream.reader(); |
| ... | ... | @@ -2975,7 +3159,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void { |
| 2975 | 3159 | else => {}, |
| 2976 | 3160 | } |
| 2977 | 3161 | } |
| 2978 | assert(self.extern_lazy_symbols.items().len <= offsets.items.len); | |
| 3162 | assert(self.lazy_imports.items().len <= offsets.items.len); | |
| 2979 | 3163 | |
| 2980 | 3164 | const stub_size: u4 = switch (self.base.options.target.cpu.arch) { |
| 2981 | 3165 | .x86_64 => 10, |
| ... | ... | @@ -2988,7 +3172,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void { |
| 2988 | 3172 | else => unreachable, |
| 2989 | 3173 | }; |
| 2990 | 3174 | var buf: [@sizeOf(u32)]u8 = undefined; |
| 2991 | for (self.extern_lazy_symbols.items()) |_, i| { | |
| 3175 | for (self.lazy_imports.items()) |_, i| { | |
| 2992 | 3176 | const placeholder_off = self.stub_helper_stubs_start_off.? + i * stub_size + off; |
| 2993 | 3177 | mem.writeIntLittle(u32, &buf, offsets.items[i]); |
| 2994 | 3178 | try self.base.file.?.pwriteAll(&buf, placeholder_off); |
| ... | ... | @@ -3102,177 +3286,6 @@ fn writeHeader(self: *MachO) !void { |
| 3102 | 3286 | self.header_dirty = false; |
| 3103 | 3287 | } |
| 3104 | 3288 | |
| 3105 | /// Parse MachO contents from existing binary file. | |
| 3106 | fn 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 | ||
| 3182 | fn 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 | ||
| 3187 | fn 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 | ||
| 3205 | fn 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 | ||
| 3217 | fn 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 | ||
| 3227 | fn 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 | ||
| 3237 | fn 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 | ||
| 3276 | 3289 | pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { |
| 3277 | 3290 | // TODO https://github.com/ziglang/zig/issues/1284 |
| 3278 | 3291 | 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 @@ |
| 1 | const Archive = @This(); | |
| 2 | ||
| 3 | const std = @import("std"); | |
| 4 | const assert = std.debug.assert; | |
| 5 | const fs = std.fs; | |
| 6 | const log = std.log.scoped(.archive); | |
| 7 | const macho = std.macho; | |
| 8 | const mem = std.mem; | |
| 9 | ||
| 10 | const Allocator = mem.Allocator; | |
| 11 | const Object = @import("Object.zig"); | |
| 12 | const parseName = @import("Zld.zig").parseName; | |
| 13 | ||
| 14 | usingnamespace @import("commands.zig"); | |
| 15 | ||
| 16 | allocator: *Allocator, | |
| 17 | file: fs.File, | |
| 18 | header: ar_hdr, | |
| 19 | name: []u8, | |
| 20 | ||
| 21 | objects: 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. | |
| 27 | const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n"; | |
| 28 | /// Size of that string. | |
| 29 | const SARMAG: u4 = 8; | |
| 30 | ||
| 31 | /// String in ar_fmag at the end of each header. | |
| 32 | const ARFMAG: *const [2:0]u8 = "`\n"; | |
| 33 | ||
| 34 | const 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 | ||
| 85 | pub 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. | |
| 96 | pub 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 | ||
| 137 | fn 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 | ||
| 175 | fn 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 | ||
| 229 | fn 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 | ||
| 240 | fn getName(allocator: *Allocator, header: ar_hdr, reader: anytype) ![]u8 { | |
| 241 | const name_or_length = try header.nameOrLength(); | |
| 242 | var name: []u8 = undefined; | |
| 243 | switch (name_or_length) { | |
| 244 | .Name => |n| { | |
| 245 | name = try allocator.dupe(u8, n); | |
| 246 | }, | |
| 247 | .Length => |len| { | |
| 248 | var n = try allocator.alloc(u8, len); | |
| 249 | defer allocator.free(n); | |
| 250 | try reader.readNoEof(n); | |
| 251 | const actual_len = mem.indexOfScalar(u8, n, @as(u8, 0)); | |
| 252 | name = try allocator.dupe(u8, n[0..actual_len.?]); | |
| 253 | }, | |
| 254 | } | |
| 255 | return name; | |
| 256 | } |
src/link/MachO/DebugSymbols.zig+4-4| ... | ... | @@ -839,8 +839,8 @@ fn findFreeSpaceLinkedit(self: *DebugSymbols, object_size: u64, min_alignment: u |
| 839 | 839 | |
| 840 | 840 | fn relocateSymbolTable(self: *DebugSymbols) !void { |
| 841 | 841 | 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; | |
| 844 | 844 | const nsyms = nlocals + nglobals; |
| 845 | 845 | |
| 846 | 846 | if (symtab.nsyms < nsyms) { |
| ... | ... | @@ -875,7 +875,7 @@ pub fn writeLocalSymbol(self: *DebugSymbols, index: usize) !void { |
| 875 | 875 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 876 | 876 | const off = symtab.symoff + @sizeOf(macho.nlist_64) * index; |
| 877 | 877 | log.debug("writing dSym local symbol {} at 0x{x}", .{ index, off }); |
| 878 | try self.file.pwriteAll(mem.asBytes(&self.base.local_symbols.items[index]), off); | |
| 878 | try self.file.pwriteAll(mem.asBytes(&self.base.locals.items[index]), off); | |
| 879 | 879 | } |
| 880 | 880 | |
| 881 | 881 | fn writeStringTable(self: *DebugSymbols) !void { |
| ... | ... | @@ -1057,7 +1057,7 @@ pub fn commitDeclDebugInfo( |
| 1057 | 1057 | var dbg_info_buffer = &debug_buffers.dbg_info_buffer; |
| 1058 | 1058 | var dbg_info_type_relocs = &debug_buffers.dbg_info_type_relocs; |
| 1059 | 1059 | |
| 1060 | const symbol = self.base.local_symbols.items[decl.link.macho.local_sym_index]; | |
| 1060 | const symbol = self.base.locals.items[decl.link.macho.local_sym_index]; | |
| 1061 | 1061 | const text_block = &decl.link.macho; |
| 1062 | 1062 | // If the Decl is a function, we need to update the __debug_line program. |
| 1063 | 1063 | const typed_value = decl.typed_value.most_recent.typed_value; |
src/link/MachO/Object.zig created+228| ... | ... | @@ -0,0 +1,228 @@ |
| 1 | const Object = @This(); | |
| 2 | ||
| 3 | const std = @import("std"); | |
| 4 | const assert = std.debug.assert; | |
| 5 | const fs = std.fs; | |
| 6 | const io = std.io; | |
| 7 | const log = std.log.scoped(.object); | |
| 8 | const macho = std.macho; | |
| 9 | const mem = std.mem; | |
| 10 | ||
| 11 | const Allocator = mem.Allocator; | |
| 12 | const parseName = @import("Zld.zig").parseName; | |
| 13 | ||
| 14 | usingnamespace @import("commands.zig"); | |
| 15 | ||
| 16 | allocator: *Allocator, | |
| 17 | file: fs.File, | |
| 18 | name: []u8, | |
| 19 | ar_name: ?[]u8 = null, | |
| 20 | ||
| 21 | header: macho.mach_header_64, | |
| 22 | ||
| 23 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, | |
| 24 | ||
| 25 | segment_cmd_index: ?u16 = null, | |
| 26 | symtab_cmd_index: ?u16 = null, | |
| 27 | dysymtab_cmd_index: ?u16 = null, | |
| 28 | build_version_cmd_index: ?u16 = null, | |
| 29 | data_in_code_cmd_index: ?u16 = null, | |
| 30 | text_section_index: ?u16 = null, | |
| 31 | ||
| 32 | // __DWARF segment sections | |
| 33 | dwarf_debug_info_index: ?u16 = null, | |
| 34 | dwarf_debug_abbrev_index: ?u16 = null, | |
| 35 | dwarf_debug_str_index: ?u16 = null, | |
| 36 | dwarf_debug_line_index: ?u16 = null, | |
| 37 | dwarf_debug_ranges_index: ?u16 = null, | |
| 38 | ||
| 39 | symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | |
| 40 | strtab: std.ArrayListUnmanaged(u8) = .{}, | |
| 41 | ||
| 42 | data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{}, | |
| 43 | ||
| 44 | pub 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. | |
| 61 | pub 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 | ||
| 110 | pub const ReadOffset = struct { | |
| 111 | offset: ?u32 = null, | |
| 112 | }; | |
| 113 | ||
| 114 | pub 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 | ||
| 176 | pub 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 | ||
| 188 | pub 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 | ||
| 197 | pub 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 | ||
| 202 | pub 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 | ||
| 210 | pub 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 @@ |
| 1 | const Zld = @This(); | |
| 2 | ||
| 3 | const std = @import("std"); | |
| 4 | const assert = std.debug.assert; | |
| 5 | const dwarf = std.dwarf; | |
| 6 | const leb = std.leb; | |
| 7 | const mem = std.mem; | |
| 8 | const meta = std.meta; | |
| 9 | const fs = std.fs; | |
| 10 | const macho = std.macho; | |
| 11 | const math = std.math; | |
| 12 | const log = std.log.scoped(.zld); | |
| 13 | const aarch64 = @import("../../codegen/aarch64.zig"); | |
| 14 | ||
| 15 | const Allocator = mem.Allocator; | |
| 16 | const CodeSignature = @import("CodeSignature.zig"); | |
| 17 | const Archive = @import("Archive.zig"); | |
| 18 | const Object = @import("Object.zig"); | |
| 19 | const Trie = @import("Trie.zig"); | |
| 20 | ||
| 21 | usingnamespace @import("commands.zig"); | |
| 22 | usingnamespace @import("bind.zig"); | |
| 23 | ||
| 24 | allocator: *Allocator, | |
| 25 | ||
| 26 | arch: ?std.Target.Cpu.Arch = null, | |
| 27 | page_size: ?u16 = null, | |
| 28 | file: ?fs.File = null, | |
| 29 | out_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. | |
| 34 | objects: std.ArrayListUnmanaged(Object) = .{}, | |
| 35 | ||
| 36 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, | |
| 37 | ||
| 38 | pagezero_segment_cmd_index: ?u16 = null, | |
| 39 | text_segment_cmd_index: ?u16 = null, | |
| 40 | data_const_segment_cmd_index: ?u16 = null, | |
| 41 | data_segment_cmd_index: ?u16 = null, | |
| 42 | linkedit_segment_cmd_index: ?u16 = null, | |
| 43 | dyld_info_cmd_index: ?u16 = null, | |
| 44 | symtab_cmd_index: ?u16 = null, | |
| 45 | dysymtab_cmd_index: ?u16 = null, | |
| 46 | dylinker_cmd_index: ?u16 = null, | |
| 47 | libsystem_cmd_index: ?u16 = null, | |
| 48 | data_in_code_cmd_index: ?u16 = null, | |
| 49 | function_starts_cmd_index: ?u16 = null, | |
| 50 | main_cmd_index: ?u16 = null, | |
| 51 | version_min_cmd_index: ?u16 = null, | |
| 52 | source_version_cmd_index: ?u16 = null, | |
| 53 | uuid_cmd_index: ?u16 = null, | |
| 54 | code_signature_cmd_index: ?u16 = null, | |
| 55 | ||
| 56 | // __TEXT segment sections | |
| 57 | text_section_index: ?u16 = null, | |
| 58 | stubs_section_index: ?u16 = null, | |
| 59 | stub_helper_section_index: ?u16 = null, | |
| 60 | text_const_section_index: ?u16 = null, | |
| 61 | cstring_section_index: ?u16 = null, | |
| 62 | ||
| 63 | // __DATA segment sections | |
| 64 | got_section_index: ?u16 = null, | |
| 65 | tlv_section_index: ?u16 = null, | |
| 66 | tlv_data_section_index: ?u16 = null, | |
| 67 | tlv_bss_section_index: ?u16 = null, | |
| 68 | la_symbol_ptr_section_index: ?u16 = null, | |
| 69 | data_const_section_index: ?u16 = null, | |
| 70 | data_section_index: ?u16 = null, | |
| 71 | bss_section_index: ?u16 = null, | |
| 72 | ||
| 73 | locals: std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(Symbol)) = .{}, | |
| 74 | exports: std.StringArrayHashMapUnmanaged(macho.nlist_64) = .{}, | |
| 75 | nonlazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{}, | |
| 76 | lazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{}, | |
| 77 | tlv_bootstrap: ?Import = null, | |
| 78 | threadlocal_offsets: std.ArrayListUnmanaged(u64) = .{}, | |
| 79 | local_rebases: std.ArrayListUnmanaged(Pointer) = .{}, | |
| 80 | nonlazy_pointers: std.StringArrayHashMapUnmanaged(GotEntry) = .{}, | |
| 81 | ||
| 82 | strtab: std.ArrayListUnmanaged(u8) = .{}, | |
| 83 | ||
| 84 | stub_helper_stubs_start_off: ?u64 = null, | |
| 85 | ||
| 86 | mappings: std.AutoHashMapUnmanaged(MappingKey, SectionMapping) = .{}, | |
| 87 | unhandled_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. | |
| 92 | const max_local_got_indirections: u16 = 1000; | |
| 93 | ||
| 94 | const GotEntry = struct { | |
| 95 | index: u32, | |
| 96 | target_addr: u64, | |
| 97 | }; | |
| 98 | ||
| 99 | const MappingKey = struct { | |
| 100 | object_id: u16, | |
| 101 | source_sect_id: u16, | |
| 102 | }; | |
| 103 | ||
| 104 | const SectionMapping = struct { | |
| 105 | source_sect_id: u16, | |
| 106 | target_seg_id: u16, | |
| 107 | target_sect_id: u16, | |
| 108 | offset: u32, | |
| 109 | }; | |
| 110 | ||
| 111 | const 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 | ||
| 123 | const 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 | ||
| 187 | pub 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. | |
| 201 | const 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. | |
| 206 | const DEFAULT_LIB_SEARCH_PATH: []const u8 = "/usr/lib"; | |
| 207 | ||
| 208 | const 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 | |
| 210 | const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.dylib"; | |
| 211 | ||
| 212 | pub fn init(allocator: *Allocator) Zld { | |
| 213 | return .{ .allocator = allocator }; | |
| 214 | } | |
| 215 | ||
| 216 | pub 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 | ||
| 254 | pub 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 | ||
| 303 | fn 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 | ||
| 337 | fn 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 | ||
| 376 | fn 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 | ||
| 564 | const MatchingSection = struct { | |
| 565 | seg: u16, | |
| 566 | sect: u16, | |
| 567 | }; | |
| 568 | ||
| 569 | fn 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 | ||
| 645 | fn 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 | ||
| 743 | fn 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 | ||
| 831 | fn 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 | ||
| 882 | fn 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 | ||
| 900 | fn 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 | ||
| 917 | fn 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 | ||
| 924 | fn 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 | ||
| 944 | fn 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 | ||
| 1080 | fn 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 | ||
| 1100 | fn 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 | ||
| 1171 | fn 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 | ||
| 1212 | fn 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 | ||
| 1300 | fn 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 | ||
| 1858 | fn 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 | ||
| 1936 | fn 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 | ||
| 2337 | fn 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 | ||
| 2412 | fn 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 | ||
| 2450 | fn 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 | ||
| 2508 | fn 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 | ||
| 2559 | fn 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 | ||
| 2598 | fn 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 | ||
| 2664 | fn 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 | ||
| 2699 | fn 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 | ||
| 2847 | fn 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 | ||
| 2921 | fn 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 | ||
| 2975 | fn 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 | ||
| 2992 | fn 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 | ||
| 3033 | fn 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 | ||
| 3056 | fn 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 | ||
| 3080 | fn 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 | ||
| 3098 | fn 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 | ||
| 3136 | pub 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 | ||
| 3143 | fn 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 | ||
| 3152 | fn 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 | ||
| 3157 | pub 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 | ||
| 3162 | fn 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 | ||
| 3168 | fn 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 | ||
| 3174 | fn 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 | ||
| 3180 | fn 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 | ||
| 3185 | fn isWeakDef(sym: *const macho.nlist_64) callconv(.Inline) bool { | |
| 3186 | return (sym.n_desc & macho.N_WEAK_DEF) != 0; | |
| 3187 | } | |
| 3188 | ||
| 3189 | fn 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | const leb = std.leb; | |
| 3 | const macho = std.macho; | |
| 4 | ||
| 5 | pub const Pointer = struct { | |
| 6 | offset: u64, | |
| 7 | segment_id: u16, | |
| 8 | dylib_ordinal: ?i64 = null, | |
| 9 | name: ?[]const u8 = null, | |
| 10 | }; | |
| 11 | ||
| 12 | pub 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 | ||
| 20 | pub 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 | ||
| 35 | pub 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 | ||
| 46 | pub 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 | ||
| 72 | pub 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 | ||
| 97 | pub 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 | ||
| 123 | pub 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | const leb = std.leb; | |
| 3 | const macho = std.macho; | |
| 4 | const mem = std.mem; | |
| 5 | ||
| 6 | const assert = std.debug.assert; | |
| 7 | const Allocator = mem.Allocator; | |
| 8 | ||
| 9 | pub 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 | ||
| 27 | pub 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 | ||
| 42 | pub 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 | ||
| 53 | pub 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 | ||
| 80 | pub 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 | ||
| 105 | pub 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 | ||
| 130 | pub fn writeLazyBindInfo(symbols: anytype, writer: anytype) !void { | |
| 131 | for (symbols) |entry| { | |
| 132 | const symbol = entry.value; | |
| 133 | try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment)); | |
| 134 | try leb.writeILEB128(writer, symbol.offset); | |
| 135 | ||
| 136 | if (symbol.dylib_ordinal > 15) { | |
| 137 | try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB); | |
| 138 | try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal)); | |
| 139 | } else if (symbol.dylib_ordinal > 0) { | |
| 140 | try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal))); | |
| 141 | } else { | |
| 142 | try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal))); | |
| 143 | } | |
| 144 | ||
| 145 | try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags. | |
| 146 | try writer.writeAll(entry.key); | |
| 147 | try writer.writeByte(0); | |
| 148 | ||
| 149 | try writer.writeByte(macho.BIND_OPCODE_DO_BIND); | |
| 150 | try writer.writeByte(macho.BIND_OPCODE_DONE); | |
| 151 | } | |
| 152 | } |
src/main.zig+56-15| ... | ... | @@ -2637,6 +2637,50 @@ fn argvCmd(allocator: *Allocator, argv: []const []const u8) ![]u8 { |
| 2637 | 2637 | return cmd.toOwnedSlice(); |
| 2638 | 2638 | } |
| 2639 | 2639 | |
| 2640 | fn 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 | ||
| 2640 | 2684 | pub const usage_fmt = |
| 2641 | 2685 | \\Usage: zig fmt [file]... |
| 2642 | 2686 | \\ |
| ... | ... | @@ -2708,9 +2752,10 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void { |
| 2708 | 2752 | fatal("cannot use --stdin with positional arguments", .{}); |
| 2709 | 2753 | } |
| 2710 | 2754 | |
| 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 | }; | |
| 2714 | 2759 | defer gpa.free(source_code); |
| 2715 | 2760 | |
| 2716 | 2761 | var tree = std.zig.parse(gpa, source_code) catch |err| { |
| ... | ... | @@ -2785,6 +2830,7 @@ const FmtError = error{ |
| 2785 | 2830 | EndOfStream, |
| 2786 | 2831 | Unseekable, |
| 2787 | 2832 | NotOpenForWriting, |
| 2833 | UnsupportedEncoding, | |
| 2788 | 2834 | } || fs.File.OpenError; |
| 2789 | 2835 | |
| 2790 | 2836 | fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void { |
| ... | ... | @@ -2850,21 +2896,15 @@ fn fmtPathFile( |
| 2850 | 2896 | if (stat.kind == .Directory) |
| 2851 | 2897 | return error.IsDir; |
| 2852 | 2898 | |
| 2853 | const source_code = source_file.readToEndAllocOptions( | |
| 2899 | const source_code = try readSourceFileToEndAlloc( | |
| 2854 | 2900 | fmt.gpa, |
| 2855 | max_src_size, | |
| 2901 | &source_file, | |
| 2856 | 2902 | 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 | ||
| 2865 | 2906 | source_file.close(); |
| 2866 | 2907 | file_closed = true; |
| 2867 | defer fmt.gpa.free(source_code); | |
| 2868 | 2908 | |
| 2869 | 2909 | // Add to set after no longer possible to get error.IsDir. |
| 2870 | 2910 | if (try fmt.seen.fetchPut(stat.inode, {})) |_| return; |
| ... | ... | @@ -3241,7 +3281,8 @@ pub const ClangArgIterator = struct { |
| 3241 | 3281 | self.zig_equivalent = clang_arg.zig_equivalent; |
| 3242 | 3282 | break :find_clang_arg; |
| 3243 | 3283 | }, |
| 3244 | } else { | |
| 3284 | } | |
| 3285 | else { | |
| 3245 | 3286 | fatal("Unknown Clang option: '{s}'", .{arg}); |
| 3246 | 3287 | } |
| 3247 | 3288 | } |
src/stage1/all_types.hpp+21-4| ... | ... | @@ -391,6 +391,8 @@ enum LazyValueId { |
| 391 | 391 | LazyValueIdAlignOf, |
| 392 | 392 | LazyValueIdSizeOf, |
| 393 | 393 | LazyValueIdPtrType, |
| 394 | LazyValueIdPtrTypeSimple, | |
| 395 | LazyValueIdPtrTypeSimpleConst, | |
| 394 | 396 | LazyValueIdOptType, |
| 395 | 397 | LazyValueIdSliceType, |
| 396 | 398 | LazyValueIdFnType, |
| ... | ... | @@ -467,6 +469,13 @@ struct LazyValuePtrType { |
| 467 | 469 | bool is_allowzero; |
| 468 | 470 | }; |
| 469 | 471 | |
| 472 | struct LazyValuePtrTypeSimple { | |
| 473 | LazyValue base; | |
| 474 | ||
| 475 | IrAnalyze *ira; | |
| 476 | IrInstGen *elem_type; | |
| 477 | }; | |
| 478 | ||
| 470 | 479 | struct LazyValueOptType { |
| 471 | 480 | LazyValue base; |
| 472 | 481 | |
| ... | ... | @@ -2610,7 +2619,8 @@ enum IrInstSrcId { |
| 2610 | 2619 | IrInstSrcIdEnumToInt, |
| 2611 | 2620 | IrInstSrcIdIntToErr, |
| 2612 | 2621 | IrInstSrcIdErrToInt, |
| 2613 | IrInstSrcIdCheckSwitchProngs, | |
| 2622 | IrInstSrcIdCheckSwitchProngsUnderYes, | |
| 2623 | IrInstSrcIdCheckSwitchProngsUnderNo, | |
| 2614 | 2624 | IrInstSrcIdCheckStatementIsVoid, |
| 2615 | 2625 | IrInstSrcIdTypeName, |
| 2616 | 2626 | IrInstSrcIdDeclRef, |
| ... | ... | @@ -2624,12 +2634,15 @@ enum IrInstSrcId { |
| 2624 | 2634 | IrInstSrcIdHasField, |
| 2625 | 2635 | IrInstSrcIdSetEvalBranchQuota, |
| 2626 | 2636 | IrInstSrcIdPtrType, |
| 2637 | IrInstSrcIdPtrTypeSimple, | |
| 2638 | IrInstSrcIdPtrTypeSimpleConst, | |
| 2627 | 2639 | IrInstSrcIdAlignCast, |
| 2628 | 2640 | IrInstSrcIdImplicitCast, |
| 2629 | 2641 | IrInstSrcIdResolveResult, |
| 2630 | 2642 | IrInstSrcIdResetResult, |
| 2631 | 2643 | IrInstSrcIdSetAlignStack, |
| 2632 | IrInstSrcIdArgType, | |
| 2644 | IrInstSrcIdArgTypeAllowVarFalse, | |
| 2645 | IrInstSrcIdArgTypeAllowVarTrue, | |
| 2633 | 2646 | IrInstSrcIdExport, |
| 2634 | 2647 | IrInstSrcIdExtern, |
| 2635 | 2648 | IrInstSrcIdErrorReturnTrace, |
| ... | ... | @@ -3294,6 +3307,12 @@ struct IrInstSrcArrayType { |
| 3294 | 3307 | IrInstSrc *child_type; |
| 3295 | 3308 | }; |
| 3296 | 3309 | |
| 3310 | struct IrInstSrcPtrTypeSimple { | |
| 3311 | IrInstSrc base; | |
| 3312 | ||
| 3313 | IrInstSrc *child_type; | |
| 3314 | }; | |
| 3315 | ||
| 3297 | 3316 | struct IrInstSrcPtrType { |
| 3298 | 3317 | IrInstSrc base; |
| 3299 | 3318 | |
| ... | ... | @@ -4020,7 +4039,6 @@ struct IrInstSrcCheckSwitchProngs { |
| 4020 | 4039 | IrInstSrcCheckSwitchProngsRange *ranges; |
| 4021 | 4040 | size_t range_count; |
| 4022 | 4041 | AstNode* else_prong; |
| 4023 | bool have_underscore_prong; | |
| 4024 | 4042 | }; |
| 4025 | 4043 | |
| 4026 | 4044 | struct IrInstSrcCheckStatementIsVoid { |
| ... | ... | @@ -4144,7 +4162,6 @@ struct IrInstSrcArgType { |
| 4144 | 4162 | |
| 4145 | 4163 | IrInstSrc *fn_type; |
| 4146 | 4164 | IrInstSrc *arg_index; |
| 4147 | bool allow_var; | |
| 4148 | 4165 | }; |
| 4149 | 4166 | |
| 4150 | 4167 | struct IrInstSrcExport { |
src/stage1/analyze.cpp+48-1| ... | ... | @@ -1237,6 +1237,22 @@ Error type_val_resolve_zero_bits(CodeGen *g, ZigValue *type_val, ZigType *parent |
| 1237 | 1237 | parent_type_val, is_zero_bits); |
| 1238 | 1238 | } |
| 1239 | 1239 | } |
| 1240 | case LazyValueIdPtrTypeSimple: | |
| 1241 | case LazyValueIdPtrTypeSimpleConst: { | |
| 1242 | LazyValuePtrTypeSimple *lazy_ptr_type = reinterpret_cast<LazyValuePtrTypeSimple *>(type_val->data.x_lazy); | |
| 1243 | ||
| 1244 | if (parent_type_val == lazy_ptr_type->elem_type->value) { | |
| 1245 | // Does a struct which contains a pointer field to itself have bits? Yes. | |
| 1246 | *is_zero_bits = false; | |
| 1247 | return ErrorNone; | |
| 1248 | } else { | |
| 1249 | if (parent_type_val == nullptr) { | |
| 1250 | parent_type_val = type_val; | |
| 1251 | } | |
| 1252 | return type_val_resolve_zero_bits(g, lazy_ptr_type->elem_type->value, parent_type, | |
| 1253 | parent_type_val, is_zero_bits); | |
| 1254 | } | |
| 1255 | } | |
| 1240 | 1256 | case LazyValueIdArrayType: { |
| 1241 | 1257 | LazyValueArrayType *lazy_array_type = |
| 1242 | 1258 | reinterpret_cast<LazyValueArrayType *>(type_val->data.x_lazy); |
| ... | ... | @@ -1285,6 +1301,8 @@ Error type_val_resolve_is_opaque_type(CodeGen *g, ZigValue *type_val, bool *is_o |
| 1285 | 1301 | zig_unreachable(); |
| 1286 | 1302 | case LazyValueIdSliceType: |
| 1287 | 1303 | case LazyValueIdPtrType: |
| 1304 | case LazyValueIdPtrTypeSimple: | |
| 1305 | case LazyValueIdPtrTypeSimpleConst: | |
| 1288 | 1306 | case LazyValueIdFnType: |
| 1289 | 1307 | case LazyValueIdOptType: |
| 1290 | 1308 | case LazyValueIdErrUnionType: |
| ... | ... | @@ -1313,6 +1331,11 @@ static ReqCompTime type_val_resolve_requires_comptime(CodeGen *g, ZigValue *type |
| 1313 | 1331 | LazyValuePtrType *lazy_ptr_type = reinterpret_cast<LazyValuePtrType *>(type_val->data.x_lazy); |
| 1314 | 1332 | return type_val_resolve_requires_comptime(g, lazy_ptr_type->elem_type->value); |
| 1315 | 1333 | } |
| 1334 | case LazyValueIdPtrTypeSimple: | |
| 1335 | case LazyValueIdPtrTypeSimpleConst: { | |
| 1336 | LazyValuePtrTypeSimple *lazy_ptr_type = reinterpret_cast<LazyValuePtrTypeSimple *>(type_val->data.x_lazy); | |
| 1337 | return type_val_resolve_requires_comptime(g, lazy_ptr_type->elem_type->value); | |
| 1338 | } | |
| 1316 | 1339 | case LazyValueIdOptType: { |
| 1317 | 1340 | LazyValueOptType *lazy_opt_type = reinterpret_cast<LazyValueOptType *>(type_val->data.x_lazy); |
| 1318 | 1341 | return type_val_resolve_requires_comptime(g, lazy_opt_type->payload_type->value); |
| ... | ... | @@ -1413,6 +1436,24 @@ start_over: |
| 1413 | 1436 | } |
| 1414 | 1437 | return ErrorNone; |
| 1415 | 1438 | } |
| 1439 | case LazyValueIdPtrTypeSimple: | |
| 1440 | case LazyValueIdPtrTypeSimpleConst: { | |
| 1441 | LazyValuePtrTypeSimple *lazy_ptr_type = reinterpret_cast<LazyValuePtrTypeSimple *>(type_val->data.x_lazy); | |
| 1442 | bool is_zero_bits; | |
| 1443 | if ((err = type_val_resolve_zero_bits(g, lazy_ptr_type->elem_type->value, nullptr, | |
| 1444 | nullptr, &is_zero_bits))) | |
| 1445 | { | |
| 1446 | return err; | |
| 1447 | } | |
| 1448 | if (is_zero_bits) { | |
| 1449 | *abi_size = 0; | |
| 1450 | *size_in_bits = 0; | |
| 1451 | } else { | |
| 1452 | *abi_size = g->builtin_types.entry_usize->abi_size; | |
| 1453 | *size_in_bits = g->builtin_types.entry_usize->size_in_bits; | |
| 1454 | } | |
| 1455 | return ErrorNone; | |
| 1456 | } | |
| 1416 | 1457 | case LazyValueIdFnType: |
| 1417 | 1458 | *abi_size = g->builtin_types.entry_usize->abi_size; |
| 1418 | 1459 | *size_in_bits = g->builtin_types.entry_usize->size_in_bits; |
| ... | ... | @@ -1449,6 +1490,8 @@ Error type_val_resolve_abi_align(CodeGen *g, AstNode *source_node, ZigValue *typ |
| 1449 | 1490 | zig_unreachable(); |
| 1450 | 1491 | case LazyValueIdSliceType: |
| 1451 | 1492 | case LazyValueIdPtrType: |
| 1493 | case LazyValueIdPtrTypeSimple: | |
| 1494 | case LazyValueIdPtrTypeSimpleConst: | |
| 1452 | 1495 | case LazyValueIdFnType: |
| 1453 | 1496 | *abi_align = g->builtin_types.entry_usize->abi_align; |
| 1454 | 1497 | return ErrorNone; |
| ... | ... | @@ -1506,7 +1549,9 @@ static OnePossibleValue type_val_resolve_has_one_possible_value(CodeGen *g, ZigV |
| 1506 | 1549 | return OnePossibleValueYes; |
| 1507 | 1550 | return type_val_resolve_has_one_possible_value(g, lazy_array_type->elem_type->value); |
| 1508 | 1551 | } |
| 1509 | case LazyValueIdPtrType: { | |
| 1552 | case LazyValueIdPtrType: | |
| 1553 | case LazyValueIdPtrTypeSimple: | |
| 1554 | case LazyValueIdPtrTypeSimpleConst: { | |
| 1510 | 1555 | Error err; |
| 1511 | 1556 | bool zero_bits; |
| 1512 | 1557 | if ((err = type_val_resolve_zero_bits(g, type_val, nullptr, nullptr, &zero_bits))) { |
| ... | ... | @@ -5758,6 +5803,8 @@ static bool can_mutate_comptime_var_state(ZigValue *value) { |
| 5758 | 5803 | case LazyValueIdAlignOf: |
| 5759 | 5804 | case LazyValueIdSizeOf: |
| 5760 | 5805 | case LazyValueIdPtrType: |
| 5806 | case LazyValueIdPtrTypeSimple: | |
| 5807 | case LazyValueIdPtrTypeSimpleConst: | |
| 5761 | 5808 | case LazyValueIdOptType: |
| 5762 | 5809 | case LazyValueIdSliceType: |
| 5763 | 5810 | case LazyValueIdFnType: |
src/stage1/ir.cpp+140-26| ... | ... | @@ -476,7 +476,8 @@ static void destroy_instruction_src(IrInstSrc *inst) { |
| 476 | 476 | return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcIntToErr *>(inst)); |
| 477 | 477 | case IrInstSrcIdErrToInt: |
| 478 | 478 | return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcErrToInt *>(inst)); |
| 479 | case IrInstSrcIdCheckSwitchProngs: | |
| 479 | case IrInstSrcIdCheckSwitchProngsUnderNo: | |
| 480 | case IrInstSrcIdCheckSwitchProngsUnderYes: | |
| 480 | 481 | return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCheckSwitchProngs *>(inst)); |
| 481 | 482 | case IrInstSrcIdCheckStatementIsVoid: |
| 482 | 483 | return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCheckStatementIsVoid *>(inst)); |
| ... | ... | @@ -486,6 +487,9 @@ static void destroy_instruction_src(IrInstSrc *inst) { |
| 486 | 487 | return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTagName *>(inst)); |
| 487 | 488 | case IrInstSrcIdPtrType: |
| 488 | 489 | return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPtrType *>(inst)); |
| 490 | case IrInstSrcIdPtrTypeSimple: | |
| 491 | case IrInstSrcIdPtrTypeSimpleConst: | |
| 492 | return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPtrTypeSimple *>(inst)); | |
| 489 | 493 | case IrInstSrcIdDeclRef: |
| 490 | 494 | return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcDeclRef *>(inst)); |
| 491 | 495 | case IrInstSrcIdPanic: |
| ... | ... | @@ -514,7 +518,8 @@ static void destroy_instruction_src(IrInstSrc *inst) { |
| 514 | 518 | return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcResetResult *>(inst)); |
| 515 | 519 | case IrInstSrcIdSetAlignStack: |
| 516 | 520 | return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSetAlignStack *>(inst)); |
| 517 | case IrInstSrcIdArgType: | |
| 521 | case IrInstSrcIdArgTypeAllowVarFalse: | |
| 522 | case IrInstSrcIdArgTypeAllowVarTrue: | |
| 518 | 523 | return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcArgType *>(inst)); |
| 519 | 524 | case IrInstSrcIdExport: |
| 520 | 525 | return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcExport *>(inst)); |
| ... | ... | @@ -1470,10 +1475,6 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrToInt *) { |
| 1470 | 1475 | return IrInstSrcIdErrToInt; |
| 1471 | 1476 | } |
| 1472 | 1477 | |
| 1473 | static constexpr IrInstSrcId ir_inst_id(IrInstSrcCheckSwitchProngs *) { | |
| 1474 | return IrInstSrcIdCheckSwitchProngs; | |
| 1475 | } | |
| 1476 | ||
| 1477 | 1478 | static constexpr IrInstSrcId ir_inst_id(IrInstSrcCheckStatementIsVoid *) { |
| 1478 | 1479 | return IrInstSrcIdCheckStatementIsVoid; |
| 1479 | 1480 | } |
| ... | ... | @@ -1546,10 +1547,6 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetAlignStack *) { |
| 1546 | 1547 | return IrInstSrcIdSetAlignStack; |
| 1547 | 1548 | } |
| 1548 | 1549 | |
| 1549 | static constexpr IrInstSrcId ir_inst_id(IrInstSrcArgType *) { | |
| 1550 | return IrInstSrcIdArgType; | |
| 1551 | } | |
| 1552 | ||
| 1553 | 1550 | static constexpr IrInstSrcId ir_inst_id(IrInstSrcExport *) { |
| 1554 | 1551 | return IrInstSrcIdExport; |
| 1555 | 1552 | } |
| ... | ... | @@ -2615,11 +2612,35 @@ static IrInstGen *ir_build_br_gen(IrAnalyze *ira, IrInst *source_instr, IrBasicB |
| 2615 | 2612 | return &inst->base; |
| 2616 | 2613 | } |
| 2617 | 2614 | |
| 2615 | static 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 | ||
| 2618 | 2633 | static IrInstSrc *ir_build_ptr_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, |
| 2619 | 2634 | IrInstSrc *child_type, bool is_const, bool is_volatile, PtrLen ptr_len, |
| 2620 | 2635 | IrInstSrc *sentinel, IrInstSrc *align_value, |
| 2621 | 2636 | uint32_t bit_offset_start, uint32_t host_int_bytes, bool is_allow_zero) |
| 2622 | 2637 | { |
| 2638 | if (!is_volatile && ptr_len == PtrLenSingle && sentinel == nullptr && align_value == nullptr && | |
| 2639 | bit_offset_start == 0 && host_int_bytes == 0 && is_allow_zero == 0) | |
| 2640 | { | |
| 2641 | return ir_build_ptr_type_simple(irb, scope, source_node, child_type, is_const); | |
| 2642 | } | |
| 2643 | ||
| 2623 | 2644 | IrInstSrcPtrType *inst = ir_build_instruction<IrInstSrcPtrType>(irb, scope, source_node); |
| 2624 | 2645 | inst->sentinel = sentinel; |
| 2625 | 2646 | inst->align_value = align_value; |
| ... | ... | @@ -4354,13 +4375,19 @@ static IrInstSrc *ir_build_check_switch_prongs(IrBuilderSrc *irb, Scope *scope, |
| 4354 | 4375 | IrInstSrc *target_value, IrInstSrcCheckSwitchProngsRange *ranges, size_t range_count, |
| 4355 | 4376 | AstNode* else_prong, bool have_underscore_prong) |
| 4356 | 4377 | { |
| 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 | ||
| 4359 | 4387 | instruction->target_value = target_value; |
| 4360 | 4388 | instruction->ranges = ranges; |
| 4361 | 4389 | instruction->range_count = range_count; |
| 4362 | 4390 | instruction->else_prong = else_prong; |
| 4363 | instruction->have_underscore_prong = have_underscore_prong; | |
| 4364 | 4391 | |
| 4365 | 4392 | ir_ref_instruction(target_value, irb->current_basic_block); |
| 4366 | 4393 | for (size_t i = 0; i < range_count; i += 1) { |
| ... | ... | @@ -4590,10 +4617,17 @@ static IrInstSrc *ir_build_set_align_stack(IrBuilderSrc *irb, Scope *scope, AstN |
| 4590 | 4617 | static IrInstSrc *ir_build_arg_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, |
| 4591 | 4618 | IrInstSrc *fn_type, IrInstSrc *arg_index, bool allow_var) |
| 4592 | 4619 | { |
| 4593 | IrInstSrcArgType *instruction = ir_build_instruction<IrInstSrcArgType>(irb, scope, source_node); | |
| 4620 | IrInstSrcArgType *instruction = heap::c_allocator.create<IrInstSrcArgType>(); | |
| 4621 | instruction->base.id = allow_var ? | |
| 4622 | IrInstSrcIdArgTypeAllowVarTrue : IrInstSrcIdArgTypeAllowVarFalse; | |
| 4623 | instruction->base.base.scope = scope; | |
| 4624 | instruction->base.base.source_node = source_node; | |
| 4625 | instruction->base.base.debug_id = exec_next_debug_id(irb->exec); | |
| 4626 | instruction->base.owner_bb = irb->current_basic_block; | |
| 4627 | ir_instruction_append(irb->current_basic_block, &instruction->base); | |
| 4628 | ||
| 4594 | 4629 | instruction->fn_type = fn_type; |
| 4595 | 4630 | instruction->arg_index = arg_index; |
| 4596 | instruction->allow_var = allow_var; | |
| 4597 | 4631 | |
| 4598 | 4632 | ir_ref_instruction(fn_type, irb->current_basic_block); |
| 4599 | 4633 | ir_ref_instruction(arg_index, irb->current_basic_block); |
| ... | ... | @@ -29702,7 +29736,7 @@ static IrInstGen *ir_analyze_instruction_test_comptime(IrAnalyze *ira, IrInstSrc |
| 29702 | 29736 | } |
| 29703 | 29737 | |
| 29704 | 29738 | static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, |
| 29705 | IrInstSrcCheckSwitchProngs *instruction) | |
| 29739 | IrInstSrcCheckSwitchProngs *instruction, bool have_underscore_prong) | |
| 29706 | 29740 | { |
| 29707 | 29741 | IrInstGen *target_value = instruction->target_value->child; |
| 29708 | 29742 | ZigType *switch_type = target_value->value->type; |
| ... | ... | @@ -29767,7 +29801,7 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, |
| 29767 | 29801 | bigint_incr(&field_index); |
| 29768 | 29802 | } |
| 29769 | 29803 | } |
| 29770 | if (instruction->have_underscore_prong) { | |
| 29804 | if (have_underscore_prong) { | |
| 29771 | 29805 | if (!switch_type->data.enumeration.non_exhaustive) { |
| 29772 | 29806 | ir_add_error(ira, &instruction->base.base, |
| 29773 | 29807 | buf_sprintf("switch on exhaustive enum has `_` prong")); |
| ... | ... | @@ -30871,6 +30905,24 @@ static IrInstGen *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstSrcPtr |
| 30871 | 30905 | return ir_build_ptr_to_int_gen(ira, &instruction->base.base, target); |
| 30872 | 30906 | } |
| 30873 | 30907 | |
| 30908 | static 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 | ||
| 30874 | 30926 | static IrInstGen *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstSrcPtrType *instruction) { |
| 30875 | 30927 | IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_type); |
| 30876 | 30928 | result->value->special = ConstValSpecialLazy; |
| ... | ... | @@ -30976,7 +31028,9 @@ static IrInstGen *ir_analyze_instruction_set_align_stack(IrAnalyze *ira, IrInstS |
| 30976 | 31028 | return ir_const_void(ira, &instruction->base.base); |
| 30977 | 31029 | } |
| 30978 | 31030 | |
| 30979 | static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgType *instruction) { | |
| 31031 | static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgType *instruction, | |
| 31032 | bool allow_var) | |
| 31033 | { | |
| 30980 | 31034 | IrInstGen *fn_type_inst = instruction->fn_type->child; |
| 30981 | 31035 | ZigType *fn_type = ir_resolve_type(ira, fn_type_inst); |
| 30982 | 31036 | if (type_is_invalid(fn_type)) |
| ... | ... | @@ -30998,7 +31052,7 @@ static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgTy |
| 30998 | 31052 | |
| 30999 | 31053 | FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; |
| 31000 | 31054 | if (arg_index >= fn_type_id->param_count) { |
| 31001 | if (instruction->allow_var) { | |
| 31055 | if (allow_var) { | |
| 31002 | 31056 | // TODO remove this with var args |
| 31003 | 31057 | return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_anytype); |
| 31004 | 31058 | } |
| ... | ... | @@ -31013,7 +31067,7 @@ static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgTy |
| 31013 | 31067 | // Args are only unresolved if our function is generic. |
| 31014 | 31068 | ir_assert(fn_type->data.fn.is_generic, &instruction->base.base); |
| 31015 | 31069 | |
| 31016 | if (instruction->allow_var) { | |
| 31070 | if (allow_var) { | |
| 31017 | 31071 | return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_anytype); |
| 31018 | 31072 | } else { |
| 31019 | 31073 | ir_add_error(ira, &arg_index_inst->base, |
| ... | ... | @@ -32341,8 +32395,10 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc |
| 32341 | 32395 | return ir_analyze_instruction_fn_proto(ira, (IrInstSrcFnProto *)instruction); |
| 32342 | 32396 | case IrInstSrcIdTestComptime: |
| 32343 | 32397 | 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); | |
| 32346 | 32402 | case IrInstSrcIdCheckStatementIsVoid: |
| 32347 | 32403 | return ir_analyze_instruction_check_statement_is_void(ira, (IrInstSrcCheckStatementIsVoid *)instruction); |
| 32348 | 32404 | case IrInstSrcIdDeclRef: |
| ... | ... | @@ -32373,6 +32429,10 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc |
| 32373 | 32429 | return ir_analyze_instruction_set_eval_branch_quota(ira, (IrInstSrcSetEvalBranchQuota *)instruction); |
| 32374 | 32430 | case IrInstSrcIdPtrType: |
| 32375 | 32431 | return ir_analyze_instruction_ptr_type(ira, (IrInstSrcPtrType *)instruction); |
| 32432 | case IrInstSrcIdPtrTypeSimple: | |
| 32433 | return ir_analyze_instruction_ptr_type_simple(ira, (IrInstSrcPtrTypeSimple *)instruction, false); | |
| 32434 | case IrInstSrcIdPtrTypeSimpleConst: | |
| 32435 | return ir_analyze_instruction_ptr_type_simple(ira, (IrInstSrcPtrTypeSimple *)instruction, true); | |
| 32376 | 32436 | case IrInstSrcIdAlignCast: |
| 32377 | 32437 | return ir_analyze_instruction_align_cast(ira, (IrInstSrcAlignCast *)instruction); |
| 32378 | 32438 | case IrInstSrcIdImplicitCast: |
| ... | ... | @@ -32383,8 +32443,10 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc |
| 32383 | 32443 | return ir_analyze_instruction_reset_result(ira, (IrInstSrcResetResult *)instruction); |
| 32384 | 32444 | case IrInstSrcIdSetAlignStack: |
| 32385 | 32445 | 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); | |
| 32388 | 32450 | case IrInstSrcIdExport: |
| 32389 | 32451 | return ir_analyze_instruction_export(ira, (IrInstSrcExport *)instruction); |
| 32390 | 32452 | case IrInstSrcIdExtern: |
| ... | ... | @@ -32737,12 +32799,15 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) { |
| 32737 | 32799 | case IrInstSrcIdMemcpy: |
| 32738 | 32800 | case IrInstSrcIdBreakpoint: |
| 32739 | 32801 | case IrInstSrcIdOverflowOp: // TODO when we support multiple returns this can be side effect free |
| 32740 | case IrInstSrcIdCheckSwitchProngs: | |
| 32802 | case IrInstSrcIdCheckSwitchProngsUnderNo: | |
| 32803 | case IrInstSrcIdCheckSwitchProngsUnderYes: | |
| 32741 | 32804 | case IrInstSrcIdCheckStatementIsVoid: |
| 32742 | 32805 | case IrInstSrcIdCheckRuntimeScope: |
| 32743 | 32806 | case IrInstSrcIdPanic: |
| 32744 | 32807 | case IrInstSrcIdSetEvalBranchQuota: |
| 32745 | 32808 | case IrInstSrcIdPtrType: |
| 32809 | case IrInstSrcIdPtrTypeSimple: | |
| 32810 | case IrInstSrcIdPtrTypeSimpleConst: | |
| 32746 | 32811 | case IrInstSrcIdSetAlignStack: |
| 32747 | 32812 | case IrInstSrcIdExport: |
| 32748 | 32813 | case IrInstSrcIdExtern: |
| ... | ... | @@ -32826,7 +32891,8 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) { |
| 32826 | 32891 | case IrInstSrcIdAlignCast: |
| 32827 | 32892 | case IrInstSrcIdImplicitCast: |
| 32828 | 32893 | case IrInstSrcIdResolveResult: |
| 32829 | case IrInstSrcIdArgType: | |
| 32894 | case IrInstSrcIdArgTypeAllowVarFalse: | |
| 32895 | case IrInstSrcIdArgTypeAllowVarTrue: | |
| 32830 | 32896 | case IrInstSrcIdErrorReturnTrace: |
| 32831 | 32897 | case IrInstSrcIdErrorUnion: |
| 32832 | 32898 | case IrInstSrcIdFloatOp: |
| ... | ... | @@ -33249,6 +33315,54 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) { |
| 33249 | 33315 | // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. |
| 33250 | 33316 | return ErrorNone; |
| 33251 | 33317 | } |
| 33318 | case LazyValueIdPtrTypeSimple: { | |
| 33319 | LazyValuePtrTypeSimple *lazy_ptr_type = reinterpret_cast<LazyValuePtrTypeSimple *>(val->data.x_lazy); | |
| 33320 | IrAnalyze *ira = lazy_ptr_type->ira; | |
| 33321 | ||
| 33322 | ZigType *elem_type = ir_resolve_type(ira, lazy_ptr_type->elem_type); | |
| 33323 | if (type_is_invalid(elem_type)) | |
| 33324 | return ErrorSemanticAnalyzeFail; | |
| 33325 | ||
| 33326 | if (elem_type->id == ZigTypeIdUnreachable) { | |
| 33327 | ir_add_error(ira, &lazy_ptr_type->elem_type->base, | |
| 33328 | buf_create_from_str("pointer to noreturn not allowed")); | |
| 33329 | return ErrorSemanticAnalyzeFail; | |
| 33330 | } | |
| 33331 | ||
| 33332 | assert(val->type->id == ZigTypeIdMetaType); | |
| 33333 | val->data.x_type = get_pointer_to_type_extra2(ira->codegen, elem_type, | |
| 33334 | false, false, PtrLenSingle, 0, | |
| 33335 | 0, 0, | |
| 33336 | false, VECTOR_INDEX_NONE, nullptr, nullptr); | |
| 33337 | val->special = ConstValSpecialStatic; | |
| 33338 | ||
| 33339 | // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. | |
| 33340 | return ErrorNone; | |
| 33341 | } | |
| 33342 | case LazyValueIdPtrTypeSimpleConst: { | |
| 33343 | LazyValuePtrTypeSimple *lazy_ptr_type = reinterpret_cast<LazyValuePtrTypeSimple *>(val->data.x_lazy); | |
| 33344 | IrAnalyze *ira = lazy_ptr_type->ira; | |
| 33345 | ||
| 33346 | ZigType *elem_type = ir_resolve_type(ira, lazy_ptr_type->elem_type); | |
| 33347 | if (type_is_invalid(elem_type)) | |
| 33348 | return ErrorSemanticAnalyzeFail; | |
| 33349 | ||
| 33350 | if (elem_type->id == ZigTypeIdUnreachable) { | |
| 33351 | ir_add_error(ira, &lazy_ptr_type->elem_type->base, | |
| 33352 | buf_create_from_str("pointer to noreturn not allowed")); | |
| 33353 | return ErrorSemanticAnalyzeFail; | |
| 33354 | } | |
| 33355 | ||
| 33356 | assert(val->type->id == ZigTypeIdMetaType); | |
| 33357 | val->data.x_type = get_pointer_to_type_extra2(ira->codegen, elem_type, | |
| 33358 | true, false, PtrLenSingle, 0, | |
| 33359 | 0, 0, | |
| 33360 | false, VECTOR_INDEX_NONE, nullptr, nullptr); | |
| 33361 | val->special = ConstValSpecialStatic; | |
| 33362 | ||
| 33363 | // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. | |
| 33364 | return ErrorNone; | |
| 33365 | } | |
| 33252 | 33366 | case LazyValueIdArrayType: { |
| 33253 | 33367 | LazyValueArrayType *lazy_array_type = reinterpret_cast<LazyValueArrayType *>(val->data.x_lazy); |
| 33254 | 33368 | IrAnalyze *ira = lazy_array_type->ira; |
src/stage1/ir_print.cpp+49-10| ... | ... | @@ -270,8 +270,10 @@ const char* ir_inst_src_type_str(IrInstSrcId id) { |
| 270 | 270 | return "SrcIntToErr"; |
| 271 | 271 | case IrInstSrcIdErrToInt: |
| 272 | 272 | return "SrcErrToInt"; |
| 273 | case IrInstSrcIdCheckSwitchProngs: | |
| 274 | return "SrcCheckSwitchProngs"; | |
| 273 | case IrInstSrcIdCheckSwitchProngsUnderNo: | |
| 274 | return "SrcCheckSwitchProngsUnderNo"; | |
| 275 | case IrInstSrcIdCheckSwitchProngsUnderYes: | |
| 276 | return "SrcCheckSwitchProngsUnderYes"; | |
| 275 | 277 | case IrInstSrcIdCheckStatementIsVoid: |
| 276 | 278 | return "SrcCheckStatementIsVoid"; |
| 277 | 279 | case IrInstSrcIdTypeName: |
| ... | ... | @@ -298,6 +300,10 @@ const char* ir_inst_src_type_str(IrInstSrcId id) { |
| 298 | 300 | return "SrcSetEvalBranchQuota"; |
| 299 | 301 | case IrInstSrcIdPtrType: |
| 300 | 302 | return "SrcPtrType"; |
| 303 | case IrInstSrcIdPtrTypeSimple: | |
| 304 | return "SrcPtrTypeSimple"; | |
| 305 | case IrInstSrcIdPtrTypeSimpleConst: | |
| 306 | return "SrcPtrTypeSimpleConst"; | |
| 301 | 307 | case IrInstSrcIdAlignCast: |
| 302 | 308 | return "SrcAlignCast"; |
| 303 | 309 | case IrInstSrcIdImplicitCast: |
| ... | ... | @@ -308,8 +314,10 @@ const char* ir_inst_src_type_str(IrInstSrcId id) { |
| 308 | 314 | return "SrcResetResult"; |
| 309 | 315 | case IrInstSrcIdSetAlignStack: |
| 310 | 316 | return "SrcSetAlignStack"; |
| 311 | case IrInstSrcIdArgType: | |
| 312 | return "SrcArgType"; | |
| 317 | case IrInstSrcIdArgTypeAllowVarFalse: | |
| 318 | return "SrcArgTypeAllowVarFalse"; | |
| 319 | case IrInstSrcIdArgTypeAllowVarTrue: | |
| 320 | return "SrcArgTypeAllowVarTrue"; | |
| 313 | 321 | case IrInstSrcIdExport: |
| 314 | 322 | return "SrcExport"; |
| 315 | 323 | case IrInstSrcIdExtern: |
| ... | ... | @@ -2187,7 +2195,9 @@ static void ir_print_err_to_int(IrPrintGen *irp, IrInstGenErrToInt *instruction) |
| 2187 | 2195 | ir_print_other_inst_gen(irp, instruction->target); |
| 2188 | 2196 | } |
| 2189 | 2197 | |
| 2190 | static void ir_print_check_switch_prongs(IrPrintSrc *irp, IrInstSrcCheckSwitchProngs *instruction) { | |
| 2198 | static void ir_print_check_switch_prongs(IrPrintSrc *irp, IrInstSrcCheckSwitchProngs *instruction, | |
| 2199 | bool have_underscore_prong) | |
| 2200 | { | |
| 2191 | 2201 | fprintf(irp->f, "@checkSwitchProngs("); |
| 2192 | 2202 | ir_print_other_inst_src(irp, instruction->target_value); |
| 2193 | 2203 | fprintf(irp->f, ","); |
| ... | ... | @@ -2200,6 +2210,8 @@ static void ir_print_check_switch_prongs(IrPrintSrc *irp, IrInstSrcCheckSwitchPr |
| 2200 | 2210 | } |
| 2201 | 2211 | const char *have_else_str = instruction->else_prong != nullptr ? "yes" : "no"; |
| 2202 | 2212 | fprintf(irp->f, ")else:%s", have_else_str); |
| 2213 | const char *have_under_str = have_underscore_prong ? "yes" : "no"; | |
| 2214 | fprintf(irp->f, " _:%s", have_under_str); | |
| 2203 | 2215 | } |
| 2204 | 2216 | |
| 2205 | 2217 | static void ir_print_check_statement_is_void(IrPrintSrc *irp, IrInstSrcCheckStatementIsVoid *instruction) { |
| ... | ... | @@ -2237,6 +2249,15 @@ static void ir_print_ptr_type(IrPrintSrc *irp, IrInstSrcPtrType *instruction) { |
| 2237 | 2249 | ir_print_other_inst_src(irp, instruction->child_type); |
| 2238 | 2250 | } |
| 2239 | 2251 | |
| 2252 | static 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 | ||
| 2240 | 2261 | static void ir_print_decl_ref(IrPrintSrc *irp, IrInstSrcDeclRef *instruction) { |
| 2241 | 2262 | const char *ptr_str = (instruction->lval != LValNone) ? "ptr " : ""; |
| 2242 | 2263 | fprintf(irp->f, "declref %s%s", ptr_str, buf_ptr(instruction->tld->name)); |
| ... | ... | @@ -2344,11 +2365,17 @@ static void ir_print_set_align_stack(IrPrintSrc *irp, IrInstSrcSetAlignStack *in |
| 2344 | 2365 | fprintf(irp->f, ")"); |
| 2345 | 2366 | } |
| 2346 | 2367 | |
| 2347 | static void ir_print_arg_type(IrPrintSrc *irp, IrInstSrcArgType *instruction) { | |
| 2368 | static void ir_print_arg_type(IrPrintSrc *irp, IrInstSrcArgType *instruction, bool allow_var) { | |
| 2348 | 2369 | fprintf(irp->f, "@ArgType("); |
| 2349 | 2370 | ir_print_other_inst_src(irp, instruction->fn_type); |
| 2350 | 2371 | fprintf(irp->f, ","); |
| 2351 | 2372 | ir_print_other_inst_src(irp, instruction->arg_index); |
| 2373 | fprintf(irp->f, ","); | |
| 2374 | if (allow_var) { | |
| 2375 | fprintf(irp->f, "allow_var=true"); | |
| 2376 | } else { | |
| 2377 | fprintf(irp->f, "allow_var=false"); | |
| 2378 | } | |
| 2352 | 2379 | fprintf(irp->f, ")"); |
| 2353 | 2380 | } |
| 2354 | 2381 | |
| ... | ... | @@ -2885,8 +2912,11 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai |
| 2885 | 2912 | case IrInstSrcIdErrToInt: |
| 2886 | 2913 | ir_print_err_to_int(irp, (IrInstSrcErrToInt *)instruction); |
| 2887 | 2914 | 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); | |
| 2890 | 2920 | break; |
| 2891 | 2921 | case IrInstSrcIdCheckStatementIsVoid: |
| 2892 | 2922 | ir_print_check_statement_is_void(irp, (IrInstSrcCheckStatementIsVoid *)instruction); |
| ... | ... | @@ -2900,6 +2930,12 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai |
| 2900 | 2930 | case IrInstSrcIdPtrType: |
| 2901 | 2931 | ir_print_ptr_type(irp, (IrInstSrcPtrType *)instruction); |
| 2902 | 2932 | break; |
| 2933 | case IrInstSrcIdPtrTypeSimple: | |
| 2934 | ir_print_ptr_type_simple(irp, (IrInstSrcPtrTypeSimple *)instruction, false); | |
| 2935 | break; | |
| 2936 | case IrInstSrcIdPtrTypeSimpleConst: | |
| 2937 | ir_print_ptr_type_simple(irp, (IrInstSrcPtrTypeSimple *)instruction, true); | |
| 2938 | break; | |
| 2903 | 2939 | case IrInstSrcIdDeclRef: |
| 2904 | 2940 | ir_print_decl_ref(irp, (IrInstSrcDeclRef *)instruction); |
| 2905 | 2941 | break; |
| ... | ... | @@ -2942,8 +2978,11 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai |
| 2942 | 2978 | case IrInstSrcIdSetAlignStack: |
| 2943 | 2979 | ir_print_set_align_stack(irp, (IrInstSrcSetAlignStack *)instruction); |
| 2944 | 2980 | 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); | |
| 2947 | 2986 | break; |
| 2948 | 2987 | case IrInstSrcIdExport: |
| 2949 | 2988 | ir_print_export(irp, (IrInstSrcExport *)instruction); |
src/translate_c.zig+117-55| ... | ... | @@ -11,6 +11,7 @@ const math = std.math; |
| 11 | 11 | const ast = @import("translate_c/ast.zig"); |
| 12 | 12 | const Node = ast.Node; |
| 13 | 13 | const Tag = Node.Tag; |
| 14 | const c_builtins = std.c.builtins; | |
| 14 | 15 | |
| 15 | 16 | const CallingConvention = std.builtin.CallingConvention; |
| 16 | 17 | |
| ... | ... | @@ -635,7 +636,7 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co |
| 635 | 636 | if (has_init) trans_init: { |
| 636 | 637 | if (decl_init) |expr| { |
| 637 | 638 | const node_or_error = if (expr.getStmtClass() == .StringLiteralClass) |
| 638 | transStringLiteralAsArray(c, scope, @ptrCast(*const clang.StringLiteral, expr), zigArraySize(c, type_node) catch 0) | |
| 639 | transStringLiteralInitializer(c, scope, @ptrCast(*const clang.StringLiteral, expr), type_node) | |
| 639 | 640 | else |
| 640 | 641 | transExprCoercing(c, scope, expr, .used); |
| 641 | 642 | init_node = node_or_error catch |err| switch (err) { |
| ... | ... | @@ -1058,6 +1059,10 @@ fn transStmt( |
| 1058 | 1059 | const compound_literal = @ptrCast(*const clang.CompoundLiteralExpr, stmt); |
| 1059 | 1060 | return transExpr(c, scope, compound_literal.getInitializer(), result_used); |
| 1060 | 1061 | }, |
| 1062 | .GenericSelectionExprClass => { | |
| 1063 | const gen_sel = @ptrCast(*const clang.GenericSelectionExpr, stmt); | |
| 1064 | return transExpr(c, scope, gen_sel.getResultExpr(), result_used); | |
| 1065 | }, | |
| 1061 | 1066 | else => { |
| 1062 | 1067 | return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO implement translation of stmt class {s}", .{@tagName(sc)}); |
| 1063 | 1068 | }, |
| ... | ... | @@ -1407,7 +1412,7 @@ fn transDeclStmtOne( |
| 1407 | 1412 | |
| 1408 | 1413 | var init_node = if (decl_init) |expr| |
| 1409 | 1414 | if (expr.getStmtClass() == .StringLiteralClass) |
| 1410 | try transStringLiteralAsArray(c, scope, @ptrCast(*const clang.StringLiteral, expr), try zigArraySize(c, type_node)) | |
| 1415 | try transStringLiteralInitializer(c, scope, @ptrCast(*const clang.StringLiteral, expr), type_node) | |
| 1411 | 1416 | else |
| 1412 | 1417 | try transExprCoercing(c, scope, expr, .used) |
| 1413 | 1418 | else |
| ... | ... | @@ -1522,7 +1527,7 @@ fn transImplicitCastExpr( |
| 1522 | 1527 | return maybeSuppressResult(c, scope, result_used, ne); |
| 1523 | 1528 | }, |
| 1524 | 1529 | .BuiltinFnToFnPtr => { |
| 1525 | return transExpr(c, scope, sub_expr, result_used); | |
| 1530 | return transBuiltinFnExpr(c, scope, sub_expr, result_used); | |
| 1526 | 1531 | }, |
| 1527 | 1532 | .ToVoid => { |
| 1528 | 1533 | // Should only appear in the rhs and lhs of a ConditionalOperator |
| ... | ... | @@ -1538,6 +1543,22 @@ fn transImplicitCastExpr( |
| 1538 | 1543 | } |
| 1539 | 1544 | } |
| 1540 | 1545 | |
| 1546 | fn 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 | ||
| 1553 | fn 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 | ||
| 1541 | 1562 | fn transBoolExpr( |
| 1542 | 1563 | c: *Context, |
| 1543 | 1564 | scope: *Scope, |
| ... | ... | @@ -1582,6 +1603,10 @@ fn exprIsNarrowStringLiteral(expr: *const clang.Expr) bool { |
| 1582 | 1603 | const op_expr = @ptrCast(*const clang.ParenExpr, expr).getSubExpr(); |
| 1583 | 1604 | return exprIsNarrowStringLiteral(op_expr); |
| 1584 | 1605 | }, |
| 1606 | .GenericSelectionExprClass => { | |
| 1607 | const gen_sel = @ptrCast(*const clang.GenericSelectionExpr, expr); | |
| 1608 | return exprIsNarrowStringLiteral(gen_sel.getResultExpr()); | |
| 1609 | }, | |
| 1585 | 1610 | else => return false, |
| 1586 | 1611 | } |
| 1587 | 1612 | } |
| ... | ... | @@ -1733,6 +1758,20 @@ fn transReturnStmt( |
| 1733 | 1758 | return Tag.@"return".create(c.arena, rhs); |
| 1734 | 1759 | } |
| 1735 | 1760 | |
| 1761 | fn 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 | ||
| 1736 | 1775 | fn transStringLiteral( |
| 1737 | 1776 | c: *Context, |
| 1738 | 1777 | scope: *Scope, |
| ... | ... | @@ -1741,19 +1780,14 @@ fn transStringLiteral( |
| 1741 | 1780 | ) TransError!Node { |
| 1742 | 1781 | const kind = stmt.getKind(); |
| 1743 | 1782 | 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), | |
| 1752 | 1784 | .UTF16, .UTF32, .Wide => { |
| 1753 | 1785 | const str_type = @tagName(stmt.getKind()); |
| 1754 | 1786 | const name = try std.fmt.allocPrint(c.arena, "zig.{s}_string_{d}", .{ str_type, c.getMangle() }); |
| 1755 | const lit_array = try transStringLiteralAsArray(c, scope, stmt, stmt.getLength() + 1); | |
| 1756 | 1787 | |
| 1788 | const expr_base = @ptrCast(*const clang.Expr, stmt); | |
| 1789 | const array_type = try transQualTypeInitialized(c, scope, expr_base.getType(), expr_base, expr_base.getBeginLoc()); | |
| 1790 | const lit_array = try transStringLiteralInitializer(c, scope, stmt, array_type); | |
| 1757 | 1791 | const decl = try Tag.var_simple.create(c.arena, .{ .name = name, .init = lit_array }); |
| 1758 | 1792 | try scope.appendNode(decl); |
| 1759 | 1793 | const node = try Tag.identifier.create(c.arena, name); |
| ... | ... | @@ -1762,52 +1796,67 @@ fn transStringLiteral( |
| 1762 | 1796 | } |
| 1763 | 1797 | } |
| 1764 | 1798 | |
| 1765 | /// Parse the size of an array back out from an ast Node. | |
| 1766 | fn zigArraySize(c: *Context, node: Node) TransError!usize { | |
| 1767 | if (node.castTag(.array_type)) |array| { | |
| 1768 | return array.data.len; | |
| 1769 | } | |
| 1770 | return error.UnsupportedTranslation; | |
| 1799 | fn getArrayPayload(array_type: Node) ast.Payload.Array.ArrayTypeInfo { | |
| 1800 | return (array_type.castTag(.array_type) orelse array_type.castTag(.null_sentinel_array_type).?).data; | |
| 1771 | 1801 | } |
| 1772 | 1802 | |
| 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 | |
| 1778 | fn 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. | |
| 1807 | fn transStringLiteralInitializer( | |
| 1779 | 1808 | c: *Context, |
| 1780 | 1809 | scope: *Scope, |
| 1781 | 1810 | stmt: *const clang.StringLiteral, |
| 1782 | array_size: usize, | |
| 1811 | array_type: Node, | |
| 1783 | 1812 | ) TransError!Node { |
| 1784 | if (array_size == 0) return error.UnsupportedType; | |
| 1813 | assert(array_type.tag() == .array_type or array_type.tag() == .null_sentinel_array_type); | |
| 1814 | ||
| 1815 | const is_narrow = stmt.getKind() == .Ascii or stmt.getKind() == .UTF8; | |
| 1785 | 1816 | |
| 1786 | 1817 | const str_length = stmt.getLength(); |
| 1818 | const payload = getArrayPayload(array_type); | |
| 1819 | const array_size = payload.len; | |
| 1820 | const elem_type = payload.elem_type; | |
| 1821 | ||
| 1822 | if (array_size == 0) return Tag.empty_array.create(c.arena, elem_type); | |
| 1823 | ||
| 1824 | const num_inits = math.min(str_length, array_size); | |
| 1825 | const init_node = if (num_inits > 0) blk: { | |
| 1826 | if (is_narrow) { | |
| 1827 | // "string literal".* or string literal"[0..num_inits].* | |
| 1828 | var str = try transNarrowStringLiteral(c, scope, stmt, .used); | |
| 1829 | if (str_length != array_size) str = try Tag.string_slice.create(c.arena, .{ .string = str, .end = num_inits }); | |
| 1830 | break :blk try Tag.deref.create(c.arena, str); | |
| 1831 | } else { | |
| 1832 | const init_list = try c.arena.alloc(Node, num_inits); | |
| 1833 | var i: c_uint = 0; | |
| 1834 | while (i < num_inits) : (i += 1) { | |
| 1835 | init_list[i] = try transCreateCharLitNode(c, false, stmt.getCodeUnit(i)); | |
| 1836 | } | |
| 1837 | const init_args = .{ .len = num_inits, .elem_type = elem_type }; | |
| 1838 | const init_array_type = try if (array_type.tag() == .array_type) Tag.array_type.create(c.arena, init_args) else Tag.null_sentinel_array_type.create(c.arena, init_args); | |
| 1839 | break :blk try Tag.array_init.create(c.arena, .{ | |
| 1840 | .cond = init_array_type, | |
| 1841 | .cases = init_list, | |
| 1842 | }); | |
| 1843 | } | |
| 1844 | } else null; | |
| 1787 | 1845 | |
| 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. | |
| 1791 | 1848 | |
| 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 | }); | |
| 1795 | 1854 | |
| 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; | |
| 1805 | 1859 | } |
| 1806 | ||
| 1807 | return Tag.array_init.create(c.arena, .{ | |
| 1808 | .cond = arr_type, | |
| 1809 | .cases = init_list, | |
| 1810 | }); | |
| 1811 | 1860 | } |
| 1812 | 1861 | |
| 1813 | 1862 | /// determine whether `stmt` is a "pointer subtraction expression" - a subtraction where |
| ... | ... | @@ -1836,6 +1885,7 @@ fn cIntTypeForEnum(enum_qt: clang.QualType) clang.QualType { |
| 1836 | 1885 | return enum_decl.getIntegerType(); |
| 1837 | 1886 | } |
| 1838 | 1887 | |
| 1888 | // when modifying this function, make sure to also update std.meta.cast | |
| 1839 | 1889 | fn transCCast( |
| 1840 | 1890 | c: *Context, |
| 1841 | 1891 | scope: *Scope, |
| ... | ... | @@ -2725,6 +2775,10 @@ fn cIsFunctionDeclRef(expr: *const clang.Expr) bool { |
| 2725 | 2775 | const opcode = un_op.getOpcode(); |
| 2726 | 2776 | return (opcode == .AddrOf or opcode == .Deref) and cIsFunctionDeclRef(un_op.getSubExpr()); |
| 2727 | 2777 | }, |
| 2778 | .GenericSelectionExprClass => { | |
| 2779 | const gen_sel = @ptrCast(*const clang.GenericSelectionExpr, expr); | |
| 2780 | return cIsFunctionDeclRef(gen_sel.getResultExpr()); | |
| 2781 | }, | |
| 2728 | 2782 | else => return false, |
| 2729 | 2783 | } |
| 2730 | 2784 | } |
| ... | ... | @@ -3194,11 +3248,11 @@ fn transFloatingLiteral(c: *Context, scope: *Scope, stmt: *const clang.FloatingL |
| 3194 | 3248 | var dbl = stmt.getValueAsApproximateDouble(); |
| 3195 | 3249 | const is_negative = dbl < 0; |
| 3196 | 3250 | 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}) | |
| 3200 | 3253 | else |
| 3201 | try Tag.float_literal.create(c.arena, str); | |
| 3254 | try std.fmt.allocPrint(c.arena, "{d}", .{dbl}); | |
| 3255 | var node = try Tag.float_literal.create(c.arena, str); | |
| 3202 | 3256 | if (is_negative) node = try Tag.negate.create(c.arena, node); |
| 3203 | 3257 | return maybeSuppressResult(c, scope, used, node); |
| 3204 | 3258 | } |
| ... | ... | @@ -3312,9 +3366,8 @@ fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: Node) !void { |
| 3312 | 3366 | try c.global_scope.nodes.append(decl_node); |
| 3313 | 3367 | } |
| 3314 | 3368 | |
| 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. | |
| 3318 | 3371 | fn transQualTypeInitialized( |
| 3319 | 3372 | c: *Context, |
| 3320 | 3373 | scope: *Scope, |
| ... | ... | @@ -3330,9 +3383,14 @@ fn transQualTypeInitialized( |
| 3330 | 3383 | switch (decl_init.getStmtClass()) { |
| 3331 | 3384 | .StringLiteralClass => { |
| 3332 | 3385 | 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(); | |
| 3334 | 3387 | const array_size = @intCast(usize, string_lit_size); |
| 3335 | return Tag.array_type.create(c.arena, .{ .len = array_size, .elem_type = elem_ty }); | |
| 3388 | ||
| 3389 | // incomplete array initialized with empty string, will be translated as [1]T{0} | |
| 3390 | // see https://github.com/ziglang/zig/issues/8256 | |
| 3391 | if (array_size == 0) return Tag.array_type.create(c.arena, .{ .len = 1, .elem_type = elem_ty }); | |
| 3392 | ||
| 3393 | return Tag.null_sentinel_array_type.create(c.arena, .{ .len = array_size, .elem_type = elem_ty }); | |
| 3336 | 3394 | }, |
| 3337 | 3395 | .InitListExprClass => { |
| 3338 | 3396 | const init_expr = @ptrCast(*const clang.InitListExpr, decl_init); |
| ... | ... | @@ -4746,6 +4804,10 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!N |
| 4746 | 4804 | }, |
| 4747 | 4805 | .Identifier => { |
| 4748 | 4806 | const mangled_name = scope.getAlias(slice); |
| 4807 | if (mem.startsWith(u8, mangled_name, "__builtin_") and !isBuiltinDefined(mangled_name)) { | |
| 4808 | try m.fail(c, "TODO implement function '{s}' in std.c.builtins", .{mangled_name}); | |
| 4809 | return error.ParseError; | |
| 4810 | } | |
| 4749 | 4811 | return Tag.identifier.create(c.arena, builtin_typedef_map.get(mangled_name) orelse mangled_name); |
| 4750 | 4812 | }, |
| 4751 | 4813 | .LParen => { |
src/translate_c/ast.zig+83-3| ... | ... | @@ -40,6 +40,8 @@ pub const Node = extern union { |
| 40 | 40 | string_literal, |
| 41 | 41 | char_literal, |
| 42 | 42 | enum_literal, |
| 43 | /// "string"[0..end] | |
| 44 | string_slice, | |
| 43 | 45 | identifier, |
| 44 | 46 | @"if", |
| 45 | 47 | /// if (!operand) break; |
| ... | ... | @@ -176,6 +178,7 @@ pub const Node = extern union { |
| 176 | 178 | c_pointer, |
| 177 | 179 | single_pointer, |
| 178 | 180 | array_type, |
| 181 | null_sentinel_array_type, | |
| 179 | 182 | |
| 180 | 183 | /// @import("std").meta.sizeof(operand) |
| 181 | 184 | std_meta_sizeof, |
| ... | ... | @@ -334,7 +337,7 @@ pub const Node = extern union { |
| 334 | 337 | .std_meta_promoteIntLiteral => Payload.PromoteIntLiteral, |
| 335 | 338 | .block => Payload.Block, |
| 336 | 339 | .c_pointer, .single_pointer => Payload.Pointer, |
| 337 | .array_type => Payload.Array, | |
| 340 | .array_type, .null_sentinel_array_type => Payload.Array, | |
| 338 | 341 | .arg_redecl, .alias, .fail_decl => Payload.ArgRedecl, |
| 339 | 342 | .log2_int_type => Payload.Log2IntType, |
| 340 | 343 | .var_simple, .pub_var_simple => Payload.SimpleVarDecl, |
| ... | ... | @@ -342,6 +345,7 @@ pub const Node = extern union { |
| 342 | 345 | .array_filler => Payload.ArrayFiller, |
| 343 | 346 | .pub_inline_fn => Payload.PubInlineFn, |
| 344 | 347 | .field_access => Payload.FieldAccess, |
| 348 | .string_slice => Payload.StringSlice, | |
| 345 | 349 | }; |
| 346 | 350 | } |
| 347 | 351 | |
| ... | ... | @@ -584,10 +588,12 @@ pub const Payload = struct { |
| 584 | 588 | |
| 585 | 589 | pub const Array = struct { |
| 586 | 590 | base: Payload, |
| 587 | data: struct { | |
| 591 | data: ArrayTypeInfo, | |
| 592 | ||
| 593 | pub const ArrayTypeInfo = struct { | |
| 588 | 594 | elem_type: Node, |
| 589 | 595 | len: usize, |
| 590 | }, | |
| 596 | }; | |
| 591 | 597 | }; |
| 592 | 598 | |
| 593 | 599 | pub const Pointer = struct { |
| ... | ... | @@ -664,6 +670,14 @@ pub const Payload = struct { |
| 664 | 670 | radix: Node, |
| 665 | 671 | }, |
| 666 | 672 | }; |
| 673 | ||
| 674 | pub const StringSlice = struct { | |
| 675 | base: Payload, | |
| 676 | data: struct { | |
| 677 | string: Node, | |
| 678 | end: usize, | |
| 679 | }, | |
| 680 | }; | |
| 667 | 681 | }; |
| 668 | 682 | |
| 669 | 683 | /// Converts the nodes into a Zig ast. |
| ... | ... | @@ -1015,6 +1029,36 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { |
| 1015 | 1029 | .data = undefined, |
| 1016 | 1030 | }); |
| 1017 | 1031 | }, |
| 1032 | .string_slice => { | |
| 1033 | const payload = node.castTag(.string_slice).?.data; | |
| 1034 | ||
| 1035 | const string = try renderNode(c, payload.string); | |
| 1036 | const l_bracket = try c.addToken(.l_bracket, "["); | |
| 1037 | const start = try c.addNode(.{ | |
| 1038 | .tag = .integer_literal, | |
| 1039 | .main_token = try c.addToken(.integer_literal, "0"), | |
| 1040 | .data = undefined, | |
| 1041 | }); | |
| 1042 | _ = try c.addToken(.ellipsis2, ".."); | |
| 1043 | const end = try c.addNode(.{ | |
| 1044 | .tag = .integer_literal, | |
| 1045 | .main_token = try c.addTokenFmt(.integer_literal, "{d}", .{payload.end}), | |
| 1046 | .data = undefined, | |
| 1047 | }); | |
| 1048 | _ = try c.addToken(.r_bracket, "]"); | |
| 1049 | ||
| 1050 | return c.addNode(.{ | |
| 1051 | .tag = .slice, | |
| 1052 | .main_token = l_bracket, | |
| 1053 | .data = .{ | |
| 1054 | .lhs = string, | |
| 1055 | .rhs = try c.addExtra(std.zig.ast.Node.Slice{ | |
| 1056 | .start = start, | |
| 1057 | .end = end, | |
| 1058 | }), | |
| 1059 | }, | |
| 1060 | }); | |
| 1061 | }, | |
| 1018 | 1062 | .fail_decl => { |
| 1019 | 1063 | const payload = node.castTag(.fail_decl).?.data; |
| 1020 | 1064 | // pub const name = @compileError(msg); |
| ... | ... | @@ -1581,6 +1625,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { |
| 1581 | 1625 | const payload = node.castTag(.array_type).?.data; |
| 1582 | 1626 | return renderArrayType(c, payload.len, payload.elem_type); |
| 1583 | 1627 | }, |
| 1628 | .null_sentinel_array_type => { | |
| 1629 | const payload = node.castTag(.null_sentinel_array_type).?.data; | |
| 1630 | return renderNullSentinelArrayType(c, payload.len, payload.elem_type); | |
| 1631 | }, | |
| 1584 | 1632 | .array_filler => { |
| 1585 | 1633 | const payload = node.castTag(.array_filler).?.data; |
| 1586 | 1634 | |
| ... | ... | @@ -1946,6 +1994,36 @@ fn renderArrayType(c: *Context, len: usize, elem_type: Node) !NodeIndex { |
| 1946 | 1994 | }); |
| 1947 | 1995 | } |
| 1948 | 1996 | |
| 1997 | fn 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 | ||
| 1949 | 2027 | fn addSemicolonIfNeeded(c: *Context, node: Node) !void { |
| 1950 | 2028 | switch (node.tag()) { |
| 1951 | 2029 | .warning => unreachable, |
| ... | ... | @@ -2014,6 +2092,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { |
| 2014 | 2092 | .integer_literal, |
| 2015 | 2093 | .float_literal, |
| 2016 | 2094 | .string_literal, |
| 2095 | .string_slice, | |
| 2017 | 2096 | .char_literal, |
| 2018 | 2097 | .enum_literal, |
| 2019 | 2098 | .identifier, |
| ... | ... | @@ -2035,6 +2114,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { |
| 2035 | 2114 | .func, |
| 2036 | 2115 | .call, |
| 2037 | 2116 | .array_type, |
| 2117 | .null_sentinel_array_type, | |
| 2038 | 2118 | .bool_to_int, |
| 2039 | 2119 | .div_exact, |
| 2040 | 2120 | .byte_offset_of, |
src/zig_clang.cpp+5| ... | ... | @@ -2445,6 +2445,11 @@ struct ZigClangQualType ZigClangFunctionType_getReturnType(const struct ZigClang |
| 2445 | 2445 | return bitcast(casted->getReturnType()); |
| 2446 | 2446 | } |
| 2447 | 2447 | |
| 2448 | const 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 | ||
| 2448 | 2453 | bool ZigClangFunctionProtoType_isVariadic(const struct ZigClangFunctionProtoType *self) { |
| 2449 | 2454 | auto casted = reinterpret_cast<const clang::FunctionProtoType *>(self); |
| 2450 | 2455 | return casted->isVariadic(); |
src/zig_clang.h+2| ... | ... | @@ -1116,6 +1116,8 @@ ZIG_EXTERN_C bool ZigClangFunctionType_getNoReturnAttr(const struct ZigClangFunc |
| 1116 | 1116 | ZIG_EXTERN_C enum ZigClangCallingConv ZigClangFunctionType_getCallConv(const struct ZigClangFunctionType *self); |
| 1117 | 1117 | ZIG_EXTERN_C struct ZigClangQualType ZigClangFunctionType_getReturnType(const struct ZigClangFunctionType *self); |
| 1118 | 1118 | |
| 1119 | ZIG_EXTERN_C const struct ZigClangExpr *ZigClangGenericSelectionExpr_getResultExpr(const struct ZigClangGenericSelectionExpr *self); | |
| 1120 | ||
| 1119 | 1121 | ZIG_EXTERN_C bool ZigClangFunctionProtoType_isVariadic(const struct ZigClangFunctionProtoType *self); |
| 1120 | 1122 | ZIG_EXTERN_C unsigned ZigClangFunctionProtoType_getNumParams(const struct ZigClangFunctionProtoType *self); |
| 1121 | 1123 | ZIG_EXTERN_C struct ZigClangQualType ZigClangFunctionProtoType_getParamType(const struct ZigClangFunctionProtoType *self, unsigned i); |
test/cli.zig+11| ... | ... | @@ -28,6 +28,8 @@ pub fn main() !void { |
| 28 | 28 | const zig_exe = try fs.path.resolve(a, &[_][]const u8{zig_exe_rel}); |
| 29 | 29 | |
| 30 | 30 | const dir_path = try fs.path.join(a, &[_][]const u8{ cache_root, "clitest" }); |
| 31 | defer fs.cwd().deleteTree(dir_path) catch {}; | |
| 32 | ||
| 31 | 33 | const TestFn = fn ([]const u8, []const u8) anyerror!void; |
| 32 | 34 | const test_fns = [_]TestFn{ |
| 33 | 35 | testZigInitLib, |
| ... | ... | @@ -174,4 +176,13 @@ fn testZigFmt(zig_exe: []const u8, dir_path: []const u8) !void { |
| 174 | 176 | const run_result3 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path }); |
| 175 | 177 | // both files have been formatted, nothing should change now |
| 176 | 178 | testing.expect(run_result3.stdout.len == 0); |
| 179 | ||
| 180 | // Check UTF-16 decoding | |
| 181 | const fmt4_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt4.zig" }); | |
| 182 | var unformatted_code_utf16 = "\xff\xfe \x00 \x00 \x00 \x00/\x00/\x00 \x00n\x00o\x00 \x00r\x00e\x00a\x00s\x00o\x00n\x00"; | |
| 183 | try fs.cwd().writeFile(fmt4_zig_path, unformatted_code_utf16); | |
| 184 | ||
| 185 | const run_result4 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path }); | |
| 186 | testing.expect(std.mem.startsWith(u8, run_result4.stdout, fmt4_zig_path)); | |
| 187 | testing.expect(run_result4.stdout.len == fmt4_zig_path.len + 1 and run_result4.stdout[run_result4.stdout.len - 1] == '\n'); | |
| 177 | 188 | } |
test/run_translated_c.zig+57| ... | ... | @@ -3,6 +3,17 @@ const tests = @import("tests.zig"); |
| 3 | 3 | const nl = std.cstr.line_sep; |
| 4 | 4 | |
| 5 | 5 | pub fn addCases(cases: *tests.RunTranslatedCContext) void { |
| 6 | cases.add("division of floating literals", | |
| 7 | \\#define _NO_CRT_STDIO_INLINE 1 | |
| 8 | \\#include <stdio.h> | |
| 9 | \\#define PI 3.14159265358979323846f | |
| 10 | \\#define DEG2RAD (PI/180.0f) | |
| 11 | \\int main(void) { | |
| 12 | \\ printf("DEG2RAD is: %f\n", DEG2RAD); | |
| 13 | \\ return 0; | |
| 14 | \\} | |
| 15 | , "DEG2RAD is: 0.017453" ++ nl); | |
| 16 | ||
| 6 | 17 | cases.add("use global scope for record/enum/typedef type transalation if needed", |
| 7 | 18 | \\void bar(void); |
| 8 | 19 | \\void baz(void); |
| ... | ... | @@ -1187,4 +1198,50 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { |
| 1187 | 1198 | \\ return 0; |
| 1188 | 1199 | \\} |
| 1189 | 1200 | , ""); |
| 1201 | ||
| 1202 | cases.add("Generic selections", | |
| 1203 | \\#include <stdlib.h> | |
| 1204 | \\#include <string.h> | |
| 1205 | \\#include <stdint.h> | |
| 1206 | \\#define my_generic_fn(X) _Generic((X), \ | |
| 1207 | \\ int: abs, \ | |
| 1208 | \\ char *: strlen, \ | |
| 1209 | \\ size_t: malloc, \ | |
| 1210 | \\ default: free \ | |
| 1211 | \\)(X) | |
| 1212 | \\#define my_generic_val(X) _Generic((X), \ | |
| 1213 | \\ int: 1, \ | |
| 1214 | \\ const char *: "bar" \ | |
| 1215 | \\) | |
| 1216 | \\int main(void) { | |
| 1217 | \\ if (my_generic_val(100) != 1) abort(); | |
| 1218 | \\ | |
| 1219 | \\ const char *foo = "foo"; | |
| 1220 | \\ const char *bar = my_generic_val(foo); | |
| 1221 | \\ if (strcmp(bar, "bar") != 0) abort(); | |
| 1222 | \\ | |
| 1223 | \\ if (my_generic_fn(-42) != 42) abort(); | |
| 1224 | \\ if (my_generic_fn("hello") != 5) abort(); | |
| 1225 | \\ | |
| 1226 | \\ size_t size = 8192; | |
| 1227 | \\ uint8_t *mem = my_generic_fn(size); | |
| 1228 | \\ memset(mem, 42, size); | |
| 1229 | \\ if (mem[size - 1] != 42) abort(); | |
| 1230 | \\ my_generic_fn(mem); | |
| 1231 | \\ | |
| 1232 | \\ return 0; | |
| 1233 | \\} | |
| 1234 | , ""); | |
| 1235 | ||
| 1236 | // See __builtin_alloca_with_align comment in std.c.builtins | |
| 1237 | cases.add("use of unimplemented builtin in unused function does not prevent compilation", | |
| 1238 | \\#include <stdlib.h> | |
| 1239 | \\void unused() { | |
| 1240 | \\ __builtin_alloca_with_align(1, 8); | |
| 1241 | \\} | |
| 1242 | \\int main(void) { | |
| 1243 | \\ if (__builtin_sqrt(1.0) != 1.0) abort(); | |
| 1244 | \\ return 0; | |
| 1245 | \\} | |
| 1246 | , ""); | |
| 1190 | 1247 | } |
test/stage2/cbe.zig+1-1| ... | ... | @@ -51,7 +51,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 51 | 51 | \\ _ = printf("Hello, %s!\n", "world"); |
| 52 | 52 | \\ return 0; |
| 53 | 53 | \\} |
| 54 | , "Hello, world!\n"); | |
| 54 | , "Hello, world!" ++ std.cstr.line_sep); | |
| 55 | 55 | } |
| 56 | 56 | |
| 57 | 57 | { |
test/standalone.zig+4-1| ... | ... | @@ -9,7 +9,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void { |
| 9 | 9 | cases.add("test/standalone/main_return_error/error_u8.zig"); |
| 10 | 10 | cases.add("test/standalone/main_return_error/error_u8_non_zero.zig"); |
| 11 | 11 | cases.addBuildFile("test/standalone/main_pkg_path/build.zig"); |
| 12 | cases.addBuildFile("test/standalone/shared_library/build.zig"); | |
| 12 | if (std.Target.current.os.tag != .macos) { | |
| 13 | // TODO zld cannot link shared libraries yet. | |
| 14 | cases.addBuildFile("test/standalone/shared_library/build.zig"); | |
| 15 | } | |
| 13 | 16 | cases.addBuildFile("test/standalone/mix_o_files/build.zig"); |
| 14 | 17 | cases.addBuildFile("test/standalone/global_linkage/build.zig"); |
| 15 | 18 | cases.addBuildFile("test/standalone/static_c_lib/build.zig"); |
test/translate_c.zig+62-38| ... | ... | @@ -745,14 +745,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 745 | 745 | \\ static const char v2[] = "2.2.2"; |
| 746 | 746 | \\} |
| 747 | 747 | , &[_][]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".*; | |
| 756 | 749 | \\pub export fn foo() void {} |
| 757 | 750 | }); |
| 758 | 751 | |
| ... | ... | @@ -1600,30 +1593,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1600 | 1593 | \\static char arr1[] = "hello"; |
| 1601 | 1594 | \\char arr2[] = "hello"; |
| 1602 | 1595 | , &[_][]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".*; | |
| 1627 | 1599 | }); |
| 1628 | 1600 | |
| 1629 | 1601 | cases.add("array initializer expr", |
| ... | ... | @@ -2456,7 +2428,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2456 | 2428 | \\ b: c_int, |
| 2457 | 2429 | \\}; |
| 2458 | 2430 | \\pub extern var a: struct_Foo; |
| 2459 | \\pub export var b: f32 = 2; | |
| 2431 | \\pub export var b: f32 = 2.0; | |
| 2460 | 2432 | \\pub export fn foo() void { |
| 2461 | 2433 | \\ var c: [*c]struct_Foo = undefined; |
| 2462 | 2434 | \\ _ = a.b; |
| ... | ... | @@ -3020,17 +2992,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 3020 | 2992 | \\pub extern fn fn_bool(x: bool) void; |
| 3021 | 2993 | \\pub extern fn fn_ptr(x: ?*c_void) void; |
| 3022 | 2994 | \\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)); | |
| 3026 | 2998 | \\ fn_int(@as(c_int, 1094861636)); |
| 3027 | 2999 | \\ fn_f32(@intToFloat(f32, @as(c_int, 3))); |
| 3028 | 3000 | \\ fn_f64(@intToFloat(f64, @as(c_int, 3))); |
| 3029 | 3001 | \\ fn_char(@bitCast(u8, @truncate(i8, @as(c_int, '3')))); |
| 3030 | 3002 | \\ fn_char(@bitCast(u8, @truncate(i8, @as(c_int, '\x01')))); |
| 3031 | 3003 | \\ 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); | |
| 3034 | 3006 | \\ fn_bool(@as(c_int, 123) != 0); |
| 3035 | 3007 | \\ fn_bool(@as(c_int, 0) != 0); |
| 3036 | 3008 | \\ fn_bool(@ptrToInt(fn_int) != 0); |
| ... | ... | @@ -3418,4 +3390,56 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 3418 | 3390 | \\pub const MAY_NEED_PROMOTION_HEX = @import("std").meta.promoteIntLiteral(c_int, 0x80000000, .hexadecimal); |
| 3419 | 3391 | \\pub const MAY_NEED_PROMOTION_OCT = @import("std").meta.promoteIntLiteral(c_int, 0o20000000000, .octal); |
| 3420 | 3392 | }); |
| 3393 | ||
| 3394 | // See __builtin_alloca_with_align comment in std.c.builtins | |
| 3395 | cases.add("demote un-implemented builtins", | |
| 3396 | \\#define FOO(X) __builtin_alloca_with_align((X), 8) | |
| 3397 | , &[_][]const u8{ | |
| 3398 | \\pub const FOO = @compileError("TODO implement function '__builtin_alloca_with_align' in std.c.builtins"); | |
| 3399 | }); | |
| 3400 | ||
| 3401 | cases.add("null sentinel arrays when initialized from string literal. Issue #8256", | |
| 3402 | \\#include <stdint.h> | |
| 3403 | \\char zero[0] = "abc"; | |
| 3404 | \\uint32_t zero_w[0] = U"💯💯💯"; | |
| 3405 | \\char empty_incomplete[] = ""; | |
| 3406 | \\uint32_t empty_incomplete_w[] = U""; | |
| 3407 | \\char empty_constant[100] = ""; | |
| 3408 | \\uint32_t empty_constant_w[100] = U""; | |
| 3409 | \\char incomplete[] = "abc"; | |
| 3410 | \\uint32_t incomplete_w[] = U"💯💯💯"; | |
| 3411 | \\char truncated[1] = "abc"; | |
| 3412 | \\uint32_t truncated_w[1] = U"💯💯💯"; | |
| 3413 | \\char extend[5] = "a"; | |
| 3414 | \\uint32_t extend_w[5] = U"💯"; | |
| 3415 | \\char no_null[3] = "abc"; | |
| 3416 | \\uint32_t no_null_w[3] = U"💯💯💯"; | |
| 3417 | , &[_][]const u8{ | |
| 3418 | \\pub export var zero: [0]u8 = [0]u8{}; | |
| 3419 | \\pub export var zero_w: [0]u32 = [0]u32{}; | |
| 3420 | \\pub export var empty_incomplete: [1]u8 = [1]u8{0} ** 1; | |
| 3421 | \\pub export var empty_incomplete_w: [1]u32 = [1]u32{0} ** 1; | |
| 3422 | \\pub export var empty_constant: [100]u8 = [1]u8{0} ** 100; | |
| 3423 | \\pub export var empty_constant_w: [100]u32 = [1]u32{0} ** 100; | |
| 3424 | \\pub export var incomplete: [3:0]u8 = "abc".*; | |
| 3425 | \\pub export var incomplete_w: [3:0]u32 = [3:0]u32{ | |
| 3426 | \\ '\u{1f4af}', | |
| 3427 | \\ '\u{1f4af}', | |
| 3428 | \\ '\u{1f4af}', | |
| 3429 | \\}; | |
| 3430 | \\pub export var truncated: [1]u8 = "abc"[0..1].*; | |
| 3431 | \\pub export var truncated_w: [1]u32 = [1]u32{ | |
| 3432 | \\ '\u{1f4af}', | |
| 3433 | \\}; | |
| 3434 | \\pub export var extend: [5]u8 = "a"[0..1].* ++ [1]u8{0} ** 4; | |
| 3435 | \\pub export var extend_w: [5]u32 = [1]u32{ | |
| 3436 | \\ '\u{1f4af}', | |
| 3437 | \\} ++ [1]u32{0} ** 4; | |
| 3438 | \\pub export var no_null: [3]u8 = "abc".*; | |
| 3439 | \\pub export var no_null_w: [3]u32 = [3]u32{ | |
| 3440 | \\ '\u{1f4af}', | |
| 3441 | \\ '\u{1f4af}', | |
| 3442 | \\ '\u{1f4af}', | |
| 3443 | \\}; | |
| 3444 | }); | |
| 3421 | 3445 | } |