authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-28 21:42:56-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-28 21:42:56-07:00
logb85ef2300fa72f5f4c73b8eb9e14f0218ada592d
treedaee8ab81eaefb5433f6ba3750656ba769a311a4
parent75080e351af8be45722bca50c1d5fcd503304d77
parent175adc0bd738c2e3a55bb71c6a53dcc920c203ba

Merge remote-tracking branch 'origin/master' into llvm12


87 files changed, 9017 insertions(+), 2483 deletions(-)

CMakeLists.txt+22-7
......@@ -89,6 +89,7 @@ set(ZIG_TARGET_MCPU "baseline" CACHE STRING "-mcpu parameter to output binaries
8989set(ZIG_EXECUTABLE "" CACHE STRING "(when cross compiling) path to already-built zig binary")
9090set(ZIG_SINGLE_THREADED off CACHE BOOL "limit the zig compiler to use only 1 thread")
9191set(ZIG_OMIT_STAGE2 off CACHE BOOL "omit the stage2 backend from stage1")
92set(ZIG_ENABLE_LOGGING off CACHE BOOL "enable logging")
9293
9394if("${ZIG_TARGET_TRIPLE}" STREQUAL "native")
9495 set(ZIG_USE_LLVM_CONFIG ON CACHE BOOL "use llvm-config to find LLVM libraries")
......@@ -564,7 +565,14 @@ set(ZIG_STAGE2_SOURCES
564565 "${CMAKE_SOURCE_DIR}/src/link/Coff.zig"
565566 "${CMAKE_SOURCE_DIR}/src/link/Elf.zig"
566567 "${CMAKE_SOURCE_DIR}/src/link/MachO.zig"
568 "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig"
569 "${CMAKE_SOURCE_DIR}/src/link/MachO/CodeSignature.zig"
570 "${CMAKE_SOURCE_DIR}/src/link/MachO/DebugSymbols.zig"
571 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"
567572 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
573 "${CMAKE_SOURCE_DIR}/src/link/MachO/Zld.zig"
574 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"
575 "${CMAKE_SOURCE_DIR}/src/link/MachO/commands.zig"
568576 "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"
569577 "${CMAKE_SOURCE_DIR}/src/link/C/zig.h"
570578 "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin"
......@@ -600,6 +608,12 @@ else()
600608 set(ZIG_OMIT_STAGE2_BOOL "false")
601609endif()
602610
611if(ZIG_ENABLE_LOGGING)
612 set(ZIG_ENABLE_LOGGING_BOOL "true")
613else()
614 set(ZIG_ENABLE_LOGGING_BOOL "false")
615endif()
616
603617configure_file (
604618 "${CMAKE_SOURCE_DIR}/src/stage1/config.h.in"
605619 "${ZIG_CONFIG_H_OUT}"
......@@ -728,12 +742,14 @@ if(MSVC OR MINGW)
728742 target_link_libraries(zigstage1 LINK_PUBLIC version)
729743endif()
730744
731add_executable(zig0 ${ZIG0_SOURCES})
732set_target_properties(zig0 PROPERTIES
733 COMPILE_FLAGS ${EXE_CFLAGS}
734 LINK_FLAGS ${EXE_LDFLAGS}
735)
736target_link_libraries(zig0 zigstage1)
745if("${ZIG_EXECUTABLE}" STREQUAL "")
746 add_executable(zig0 ${ZIG0_SOURCES})
747 set_target_properties(zig0 PROPERTIES
748 COMPILE_FLAGS ${EXE_CFLAGS}
749 LINK_FLAGS ${EXE_LDFLAGS}
750 )
751 target_link_libraries(zig0 zigstage1)
752endif()
737753
738754if(MSVC)
739755 set(ZIG1_OBJECT "${CMAKE_BINARY_DIR}/zig1.obj")
......@@ -782,7 +798,6 @@ if("${ZIG_EXECUTABLE}" STREQUAL "")
782798else()
783799 add_custom_command(
784800 OUTPUT "${ZIG1_OBJECT}"
785 BYPRODUCTS "${ZIG1_OBJECT}"
786801 COMMAND "${ZIG_EXECUTABLE}" "build-obj" ${BUILD_ZIG1_ARGS}
787802 DEPENDS ${ZIG_STAGE2_SOURCES}
788803 COMMENT STATUS "Building self-hosted component ${ZIG1_OBJECT}"
ci/azure/macos_arm64_script created+132
......@@ -0,0 +1,132 @@
1#!/bin/sh
2
3set -x
4set -e
5
6brew install s3cmd ninja gnu-tar
7
8ZIGDIR="$(pwd)"
9ARCH="aarch64"
10# {product}-{os}{sdk_version}-{arch}-{llvm_version}-{cmake_build_type}
11CACHE_HOST_BASENAME="llvm-macos10.15-x86_64-11.0.1-release"
12CACHE_ARM64_BASENAME="llvm-macos11.0-arm64-11.0.1-release"
13PREFIX_HOST="$HOME/$CACHE_HOST_BASENAME"
14PREFIX_ARM64="$HOME/$CACHE_ARM64_BASENAME"
15JOBS="-j2"
16
17rm -rf $PREFIX
18cd $HOME
19wget -nv "https://ziglang.org/deps/$CACHE_HOST_BASENAME.tar.xz"
20wget -nv "https://ziglang.org/deps/$CACHE_ARM64_BASENAME.tar.xz"
21
22gtar xf "$CACHE_HOST_BASENAME.tar.xz"
23gtar xf "$CACHE_ARM64_BASENAME.tar.xz"
24
25cd $ZIGDIR
26
27# Make the `zig version` number consistent.
28# This will affect the cmake command below.
29git config core.abbrev 9
30git fetch --unshallow || true
31git fetch --tags
32
33# Select xcode: latest version found on vmImage macOS-10.15 .
34DEVELOPER_DIR=/Applications/Xcode_12.4.app
35
36export ZIG_LOCAL_CACHE_DIR="$ZIGDIR/zig-cache"
37export ZIG_GLOBAL_CACHE_DIR="$ZIGDIR/zig-cache"
38
39# Build zig for host and use `Debug` type to make builds a little faster.
40
41cd $ZIGDIR
42mkdir build.host
43cd build.host
44cmake -G "Ninja" .. \
45 -DCMAKE_INSTALL_PREFIX="$(pwd)/release" \
46 -DCMAKE_PREFIX_PATH="$PREFIX_HOST" \
47 -DCMAKE_BUILD_TYPE="Debug" \
48 -DZIG_STATIC="OFF"
49
50# Build but do not install.
51ninja $JOBS
52
53ZIG_EXE="$ZIGDIR/build.host/zig"
54
55# Build zig for arm64 target.
56# - use `Release` type for published tarballs
57# - ad-hoc codesign with linker
58# - note: apple quarantine of downloads (eg. via safari) still apply
59
60cd $ZIGDIR
61mkdir build.arm64
62cd build.arm64
63cmake -G "Ninja" .. \
64 -DCMAKE_INSTALL_PREFIX="$(pwd)/release" \
65 -DCMAKE_PREFIX_PATH="$PREFIX_ARM64" \
66 -DCMAKE_BUILD_TYPE="Release" \
67 -DCMAKE_CROSSCOMPILING="True" \
68 -DCMAKE_SYSTEM_NAME="Darwin" \
69 -DCMAKE_C_FLAGS="-arch arm64" \
70 -DCMAKE_CXX_FLAGS="-arch arm64" \
71 -DCMAKE_EXE_LINKER_FLAGS="-lz -Xlinker -adhoc_codesign" \
72 -DZIG_USE_LLVM_CONFIG="OFF" \
73 -DZIG_EXECUTABLE="$ZIG_EXE" \
74 -DZIG_TARGET_TRIPLE="${ARCH}-macos" \
75 -DZIG_STATIC="OFF"
76
77ninja $JOBS install
78
79# Disable test because binary is foreign arch.
80#release/bin/zig build test
81
82if [ "${BUILD_REASON}" != "PullRequest" ]; then
83 mv ../LICENSE release/
84
85 # We do not run test suite but still need langref.
86 mkdir -p release/docs
87 $ZIG_EXE run ../doc/docgen.zig -- $ZIG_EXE ../doc/langref.html.in release/docs/langref.html
88
89 # Produce the experimental std lib documentation.
90 mkdir -p release/docs/std
91 $ZIG_EXE test ../lib/std/std.zig \
92 --override-lib-dir ../lib \
93 -femit-docs=release/docs/std \
94 -fno-emit-bin
95
96 # Remove the unnecessary bin dir in $prefix/bin/zig
97 mv release/bin/zig release/
98 rmdir release/bin
99
100 # Remove the unnecessary zig dir in $prefix/lib/zig/std/std.zig
101 mv release/lib/zig release/lib2
102 rmdir release/lib
103 mv release/lib2 release/lib
104
105 VERSION=$($ZIG_EXE version)
106 DIRNAME="zig-macos-$ARCH-$VERSION"
107 TARBALL="$DIRNAME.tar.xz"
108 gtar cJf "$TARBALL" release/ --owner=root --sort=name --transform="s,^release,${DIRNAME},"
109 ln "$TARBALL" "$BUILD_ARTIFACTSTAGINGDIRECTORY/."
110
111 mv "$DOWNLOADSECUREFILE_SECUREFILEPATH" "$HOME/.s3cfg"
112 s3cmd put -P --add-header="cache-control: public, max-age=31536000, immutable" "$TARBALL" s3://ziglang.org/builds/
113
114 SHASUM=$(shasum -a 256 $TARBALL | cut '-d ' -f1)
115 BYTESIZE=$(wc -c < $TARBALL)
116
117 JSONFILE="macos-$GITBRANCH.json"
118 touch $JSONFILE
119 echo "{\"tarball\": \"$TARBALL\"," >>$JSONFILE
120 echo "\"shasum\": \"$SHASUM\"," >>$JSONFILE
121 echo "\"size\": \"$BYTESIZE\"}" >>$JSONFILE
122
123 s3cmd put -P --add-header="Cache-Control: max-age=0, must-revalidate" "$JSONFILE" "s3://ziglang.org/builds/$JSONFILE"
124 s3cmd put -P "$JSONFILE" "s3://ziglang.org/builds/$ARCH-macos-$VERSION.json"
125
126 # `set -x` causes these variables to be mangled.
127 # See https://developercommunity.visualstudio.com/content/problem/375679/pipeline-variable-incorrectly-inserts-single-quote.html
128 set +x
129 echo "##vso[task.setvariable variable=tarball;isOutput=true]$TARBALL"
130 echo "##vso[task.setvariable variable=shasum;isOutput=true]$SHASUM"
131 echo "##vso[task.setvariable variable=bytesize;isOutput=true]$BYTESIZE"
132fi
ci/azure/pipelines.yml+14-1
......@@ -12,6 +12,19 @@ jobs:
1212 - script: ci/azure/macos_script
1313 name: main
1414 displayName: 'Build and test'
15- job: BuildMacOS_arm64
16 pool:
17 vmImage: 'macOS-10.15'
18
19 timeoutInMinutes: 60
20
21 steps:
22 - task: DownloadSecureFile@1
23 inputs:
24 secureFile: s3cfg
25 - script: ci/azure/macos_arm64_script
26 name: main
27 displayName: 'Build and cross-compile'
1528- job: BuildLinux
1629 pool:
1730 vmImage: 'ubuntu-18.04'
......@@ -31,7 +44,7 @@ jobs:
3144 timeoutInMinutes: 360
3245 steps:
3346 - powershell: |
34 (New-Object Net.WebClient).DownloadFile("https://github.com/msys2/msys2-installer/releases/download/2021-01-05/msys2-base-x86_64-20210105.sfx.exe", "sfx.exe")
47 (New-Object Net.WebClient).DownloadFile("https://github.com/msys2/msys2-installer/releases/download/2021-02-28/msys2-base-x86_64-20210228.sfx.exe", "sfx.exe")
3548 .\sfx.exe -y -o\
3649 del sfx.exe
3750 displayName: Download/Extract/Install MSYS2
ci/azure/windows_msvc_install+1-1
......@@ -3,7 +3,7 @@
33set -x
44set -e
55
6pacman -Su --needed --noconfirm
6pacman -Suy --needed --noconfirm
77pacman -S --needed --noconfirm wget p7zip python3-pip tar xz
88
99pip install s3cmd
doc/docgen.zig+18-5
......@@ -4,6 +4,7 @@ const io = std.io;
44const fs = std.fs;
55const process = std.process;
66const ChildProcess = std.ChildProcess;
7const Progress = std.Progress;
78const print = std.debug.print;
89const mem = std.mem;
910const testing = std.testing;
......@@ -234,7 +235,7 @@ fn parseError(tokenizer: *Tokenizer, token: Token, comptime fmt: []const u8, arg
234235 }
235236 }
236237 {
237 const caret_count = token.end - token.start;
238 const caret_count = std.math.min(token.end, loc.line_end) - token.start;
238239 var i: usize = 0;
239240 while (i < caret_count) : (i += 1) {
240241 print("~", .{});
......@@ -1012,6 +1013,9 @@ fn tokenizeAndPrint(docgen_tokenizer: *Tokenizer, out: anytype, source_token: To
10121013
10131014fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: anytype, zig_exe: []const u8, do_code_tests: bool) !void {
10141015 var code_progress_index: usize = 0;
1016 var progress = Progress{};
1017 const root_node = try progress.start("Generating docgen examples", toc.nodes.len);
1018 defer root_node.end();
10151019
10161020 var env_map = try process.getEnvMap(allocator);
10171021 try env_map.set("ZIG_DEBUG_COLOR", "1");
......@@ -1058,8 +1062,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
10581062 try tokenizeAndPrint(tokenizer, out, content_tok);
10591063 },
10601064 .Code => |code| {
1061 code_progress_index += 1;
1062 print("docgen example code {}/{}...", .{ code_progress_index, tokenizer.code_node_count });
1065 root_node.completeOne();
10631066
10641067 const raw_source = tokenizer.buffer[code.source_token.start..code.source_token.end];
10651068 const trimmed_raw_source = mem.trim(u8, raw_source, " \n");
......@@ -1071,7 +1074,6 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
10711074 try out.writeAll("</pre>");
10721075
10731076 if (!do_code_tests) {
1074 print("SKIP\n", .{});
10751077 continue;
10761078 }
10771079
......@@ -1133,12 +1135,14 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
11331135 switch (result.term) {
11341136 .Exited => |exit_code| {
11351137 if (exit_code == 0) {
1138 progress.log("", .{});
11361139 print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});
11371140 dumpArgs(build_args.items);
11381141 return parseError(tokenizer, code.source_token, "example incorrectly compiled", .{});
11391142 }
11401143 },
11411144 else => {
1145 progress.log("", .{});
11421146 print("{s}\nThe following command crashed:\n", .{result.stderr});
11431147 dumpArgs(build_args.items);
11441148 return parseError(tokenizer, code.source_token, "example compile crashed", .{});
......@@ -1187,6 +1191,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
11871191 switch (result.term) {
11881192 .Exited => |exit_code| {
11891193 if (exit_code == 0) {
1194 progress.log("", .{});
11901195 print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});
11911196 dumpArgs(run_args);
11921197 return parseError(tokenizer, code.source_token, "example incorrectly compiled", .{});
......@@ -1266,18 +1271,21 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
12661271 switch (result.term) {
12671272 .Exited => |exit_code| {
12681273 if (exit_code == 0) {
1274 progress.log("", .{});
12691275 print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});
12701276 dumpArgs(test_args.items);
12711277 return parseError(tokenizer, code.source_token, "example incorrectly compiled", .{});
12721278 }
12731279 },
12741280 else => {
1281 progress.log("", .{});
12751282 print("{s}\nThe following command crashed:\n", .{result.stderr});
12761283 dumpArgs(test_args.items);
12771284 return parseError(tokenizer, code.source_token, "example compile crashed", .{});
12781285 },
12791286 }
12801287 if (mem.indexOf(u8, result.stderr, error_match) == null) {
1288 progress.log("", .{});
12811289 print("{s}\nExpected to find '{s}' in stderr\n", .{ result.stderr, error_match });
12821290 return parseError(tokenizer, code.source_token, "example did not have expected compile error", .{});
12831291 }
......@@ -1321,18 +1329,21 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
13211329 switch (result.term) {
13221330 .Exited => |exit_code| {
13231331 if (exit_code == 0) {
1332 progress.log("", .{});
13241333 print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});
13251334 dumpArgs(test_args.items);
13261335 return parseError(tokenizer, code.source_token, "example test incorrectly succeeded", .{});
13271336 }
13281337 },
13291338 else => {
1339 progress.log("", .{});
13301340 print("{s}\nThe following command crashed:\n", .{result.stderr});
13311341 dumpArgs(test_args.items);
13321342 return parseError(tokenizer, code.source_token, "example compile crashed", .{});
13331343 },
13341344 }
13351345 if (mem.indexOf(u8, result.stderr, error_match) == null) {
1346 progress.log("", .{});
13361347 print("{s}\nExpected to find '{s}' in stderr\n", .{ result.stderr, error_match });
13371348 return parseError(tokenizer, code.source_token, "example did not have expected runtime safety error message", .{});
13381349 }
......@@ -1400,18 +1411,21 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
14001411 switch (result.term) {
14011412 .Exited => |exit_code| {
14021413 if (exit_code == 0) {
1414 progress.log("", .{});
14031415 print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});
14041416 dumpArgs(build_args.items);
14051417 return parseError(tokenizer, code.source_token, "example build incorrectly succeeded", .{});
14061418 }
14071419 },
14081420 else => {
1421 progress.log("", .{});
14091422 print("{s}\nThe following command crashed:\n", .{result.stderr});
14101423 dumpArgs(build_args.items);
14111424 return parseError(tokenizer, code.source_token, "example compile crashed", .{});
14121425 },
14131426 }
14141427 if (mem.indexOf(u8, result.stderr, error_match) == null) {
1428 progress.log("", .{});
14151429 print("{s}\nExpected to find '{s}' in stderr\n", .{ result.stderr, error_match });
14161430 return parseError(tokenizer, code.source_token, "example did not have expected compile error message", .{});
14171431 }
......@@ -1461,7 +1475,6 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
14611475 try out.print("\n{s}{s}</code></pre>\n", .{ escaped_stderr, escaped_stdout });
14621476 },
14631477 }
1464 print("OK\n", .{});
14651478 },
14661479 }
14671480 }
doc/langref.html.in+3-3
......@@ -9952,9 +9952,9 @@ export fn decode_base_64(
99529952) usize {
99539953 const src = source_ptr[0..source_len];
99549954 const dest = dest_ptr[0..dest_len];
9955 const base64_decoder = base64.standard_decoder_unsafe;
9956 const decoded_size = base64_decoder.calcSize(src);
9957 base64_decoder.decode(dest[0..decoded_size], src);
9955 const base64_decoder = base64.standard.Decoder;
9956 const decoded_size = base64_decoder.calcSizeForSlice(src) catch unreachable;
9957 base64_decoder.decode(dest[0..decoded_size], src) catch unreachable;
99589958 return decoded_size;
99599959}
99609960 {#code_end#}
lib/std/array_hash_map.zig+13-13
......@@ -687,8 +687,9 @@ pub fn ArrayHashMapUnmanaged(
687687
688688 /// Removes the last inserted `Entry` in the hash map and returns it.
689689 pub fn pop(self: *Self) Entry {
690 const top = self.entries.pop();
690 const top = self.entries.items[self.entries.items.len - 1];
691691 _ = self.removeWithHash(top.key, top.hash, .index_only);
692 self.entries.items.len -= 1;
692693 return top;
693694 }
694695
......@@ -1258,19 +1259,18 @@ test "pop" {
12581259 var map = AutoArrayHashMap(i32, i32).init(std.testing.allocator);
12591260 defer map.deinit();
12601261
1261 testing.expect((try map.fetchPut(1, 11)) == null);
1262 testing.expect((try map.fetchPut(2, 22)) == null);
1263 testing.expect((try map.fetchPut(3, 33)) == null);
1264 testing.expect((try map.fetchPut(4, 44)) == null);
1262 // Insert just enough entries so that the map expands. Afterwards,
1263 // pop all entries out of the map.
12651264
1266 const pop1 = map.pop();
1267 testing.expect(pop1.key == 4 and pop1.value == 44);
1268 const pop2 = map.pop();
1269 testing.expect(pop2.key == 3 and pop2.value == 33);
1270 const pop3 = map.pop();
1271 testing.expect(pop3.key == 2 and pop3.value == 22);
1272 const pop4 = map.pop();
1273 testing.expect(pop4.key == 1 and pop4.value == 11);
1265 var i: i32 = 0;
1266 while (i < 9) : (i += 1) {
1267 testing.expect((try map.fetchPut(i, i)) == null);
1268 }
1269
1270 while (i > 0) : (i -= 1) {
1271 const pop = map.pop();
1272 testing.expect(pop.key == i - 1 and pop.value == i - 1);
1273 }
12741274}
12751275
12761276test "reIndex" {
lib/std/base64.zig+322-324
......@@ -8,454 +8,452 @@ const assert = std.debug.assert;
88const testing = std.testing;
99const mem = std.mem;
1010
11pub const standard_alphabet_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
12pub const standard_pad_char = '=';
13pub const standard_encoder = Base64Encoder.init(standard_alphabet_chars, standard_pad_char);
11pub const Error = error{
12 InvalidCharacter,
13 InvalidPadding,
14 NoSpaceLeft,
15};
16
17/// Base64 codecs
18pub const Codecs = struct {
19 alphabet_chars: [64]u8,
20 pad_char: ?u8,
21 decoderWithIgnore: fn (ignore: []const u8) Base64DecoderWithIgnore,
22 Encoder: Base64Encoder,
23 Decoder: Base64Decoder,
24};
25
26pub const standard_alphabet_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".*;
27fn standardBase64DecoderWithIgnore(ignore: []const u8) Base64DecoderWithIgnore {
28 return Base64DecoderWithIgnore.init(standard_alphabet_chars, '=', ignore);
29}
30
31/// Standard Base64 codecs, with padding
32pub const standard = Codecs{
33 .alphabet_chars = standard_alphabet_chars,
34 .pad_char = '=',
35 .decoderWithIgnore = standardBase64DecoderWithIgnore,
36 .Encoder = Base64Encoder.init(standard_alphabet_chars, '='),
37 .Decoder = Base64Decoder.init(standard_alphabet_chars, '='),
38};
39
40/// Standard Base64 codecs, without padding
41pub const standard_no_pad = Codecs{
42 .alphabet_chars = standard_alphabet_chars,
43 .pad_char = null,
44 .decoderWithIgnore = standardBase64DecoderWithIgnore,
45 .Encoder = Base64Encoder.init(standard_alphabet_chars, null),
46 .Decoder = Base64Decoder.init(standard_alphabet_chars, null),
47};
48
49pub const url_safe_alphabet_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".*;
50fn urlSafeBase64DecoderWithIgnore(ignore: []const u8) Base64DecoderWithIgnore {
51 return Base64DecoderWithIgnore.init(url_safe_alphabet_chars, null, ignore);
52}
53
54/// URL-safe Base64 codecs, with padding
55pub const url_safe = Codecs{
56 .alphabet_chars = url_safe_alphabet_chars,
57 .pad_char = '=',
58 .decoderWithIgnore = urlSafeBase64DecoderWithIgnore,
59 .Encoder = Base64Encoder.init(url_safe_alphabet_chars, '='),
60 .Decoder = Base64Decoder.init(url_safe_alphabet_chars, '='),
61};
62
63/// URL-safe Base64 codecs, without padding
64pub const url_safe_no_pad = Codecs{
65 .alphabet_chars = url_safe_alphabet_chars,
66 .pad_char = null,
67 .decoderWithIgnore = urlSafeBase64DecoderWithIgnore,
68 .Encoder = Base64Encoder.init(url_safe_alphabet_chars, null),
69 .Decoder = Base64Decoder.init(url_safe_alphabet_chars, null),
70};
71
72// Backwards compatibility
73
74/// Deprecated - Use `standard.pad_char`
75pub const standard_pad_char = standard.pad_char;
76/// Deprecated - Use `standard.Encoder`
77pub const standard_encoder = standard.Encoder;
78/// Deprecated - Use `standard.Decoder`
79pub const standard_decoder = standard.Decoder;
1480
1581pub const Base64Encoder = struct {
16 alphabet_chars: []const u8,
17 pad_char: u8,
82 alphabet_chars: [64]u8,
83 pad_char: ?u8,
1884
19 /// a bunch of assertions, then simply pass the data right through.
20 pub fn init(alphabet_chars: []const u8, pad_char: u8) Base64Encoder {
85 /// A bunch of assertions, then simply pass the data right through.
86 pub fn init(alphabet_chars: [64]u8, pad_char: ?u8) Base64Encoder {
2187 assert(alphabet_chars.len == 64);
2288 var char_in_alphabet = [_]bool{false} ** 256;
2389 for (alphabet_chars) |c| {
2490 assert(!char_in_alphabet[c]);
25 assert(c != pad_char);
91 assert(pad_char == null or c != pad_char.?);
2692 char_in_alphabet[c] = true;
2793 }
28
2994 return Base64Encoder{
3095 .alphabet_chars = alphabet_chars,
3196 .pad_char = pad_char,
3297 };
3398 }
3499
35 /// ceil(source_len * 4/3)
36 pub fn calcSize(source_len: usize) usize {
37 return @divTrunc(source_len + 2, 3) * 4;
100 /// Compute the encoded length
101 pub fn calcSize(encoder: *const Base64Encoder, source_len: usize) usize {
102 if (encoder.pad_char != null) {
103 return @divTrunc(source_len + 2, 3) * 4;
104 } else {
105 const leftover = source_len % 3;
106 return @divTrunc(source_len, 3) * 4 + @divTrunc(leftover * 4 + 2, 3);
107 }
38108 }
39109
40 /// dest.len must be what you get from ::calcSize.
110 /// dest.len must at least be what you get from ::calcSize.
41111 pub fn encode(encoder: *const Base64Encoder, dest: []u8, source: []const u8) []const u8 {
42 assert(dest.len >= Base64Encoder.calcSize(source.len));
43
44 var i: usize = 0;
45 var out_index: usize = 0;
46 while (i + 2 < source.len) : (i += 3) {
47 dest[out_index] = encoder.alphabet_chars[(source[i] >> 2) & 0x3f];
48 out_index += 1;
49
50 dest[out_index] = encoder.alphabet_chars[((source[i] & 0x3) << 4) | ((source[i + 1] & 0xf0) >> 4)];
51 out_index += 1;
52
53 dest[out_index] = encoder.alphabet_chars[((source[i + 1] & 0xf) << 2) | ((source[i + 2] & 0xc0) >> 6)];
54 out_index += 1;
55
56 dest[out_index] = encoder.alphabet_chars[source[i + 2] & 0x3f];
57 out_index += 1;
112 const out_len = encoder.calcSize(source.len);
113 assert(dest.len >= out_len);
114
115 const nibbles = source.len / 3;
116 const leftover = source.len - 3 * nibbles;
117
118 var acc: u12 = 0;
119 var acc_len: u4 = 0;
120 var out_idx: usize = 0;
121 for (source) |v| {
122 acc = (acc << 8) + v;
123 acc_len += 8;
124 while (acc_len >= 6) {
125 acc_len -= 6;
126 dest[out_idx] = encoder.alphabet_chars[@truncate(u6, (acc >> acc_len))];
127 out_idx += 1;
128 }
58129 }
59
60 if (i < source.len) {
61 dest[out_index] = encoder.alphabet_chars[(source[i] >> 2) & 0x3f];
62 out_index += 1;
63
64 if (i + 1 == source.len) {
65 dest[out_index] = encoder.alphabet_chars[(source[i] & 0x3) << 4];
66 out_index += 1;
67
68 dest[out_index] = encoder.pad_char;
69 out_index += 1;
70 } else {
71 dest[out_index] = encoder.alphabet_chars[((source[i] & 0x3) << 4) | ((source[i + 1] & 0xf0) >> 4)];
72 out_index += 1;
73
74 dest[out_index] = encoder.alphabet_chars[(source[i + 1] & 0xf) << 2];
75 out_index += 1;
130 if (acc_len > 0) {
131 dest[out_idx] = encoder.alphabet_chars[@truncate(u6, (acc << 6 - acc_len))];
132 out_idx += 1;
133 }
134 if (encoder.pad_char) |pad_char| {
135 for (dest[out_idx..]) |*pad| {
136 pad.* = pad_char;
76137 }
77
78 dest[out_index] = encoder.pad_char;
79 out_index += 1;
80138 }
81 return dest[0..out_index];
139 return dest[0..out_len];
82140 }
83141};
84142
85pub const standard_decoder = Base64Decoder.init(standard_alphabet_chars, standard_pad_char);
86
87143pub const Base64Decoder = struct {
144 const invalid_char: u8 = 0xff;
145
88146 /// e.g. 'A' => 0.
89 /// undefined for any value not in the 64 alphabet chars.
147 /// `invalid_char` for any value not in the 64 alphabet chars.
90148 char_to_index: [256]u8,
149 pad_char: ?u8,
91150
92 /// true only for the 64 chars in the alphabet, not the pad char.
93 char_in_alphabet: [256]bool,
94 pad_char: u8,
95
96 pub fn init(alphabet_chars: []const u8, pad_char: u8) Base64Decoder {
97 assert(alphabet_chars.len == 64);
98
151 pub fn init(alphabet_chars: [64]u8, pad_char: ?u8) Base64Decoder {
99152 var result = Base64Decoder{
100 .char_to_index = undefined,
101 .char_in_alphabet = [_]bool{false} ** 256,
153 .char_to_index = [_]u8{invalid_char} ** 256,
102154 .pad_char = pad_char,
103155 };
104156
157 var char_in_alphabet = [_]bool{false} ** 256;
105158 for (alphabet_chars) |c, i| {
106 assert(!result.char_in_alphabet[c]);
107 assert(c != pad_char);
159 assert(!char_in_alphabet[c]);
160 assert(pad_char == null or c != pad_char.?);
108161
109162 result.char_to_index[c] = @intCast(u8, i);
110 result.char_in_alphabet[c] = true;
163 char_in_alphabet[c] = true;
111164 }
165 return result;
166 }
112167
168 /// Return the maximum possible decoded size for a given input length - The actual length may be less if the input includes padding.
169 /// `InvalidPadding` is returned if the input length is not valid.
170 pub fn calcSizeUpperBound(decoder: *const Base64Decoder, source_len: usize) Error!usize {
171 var result = source_len / 4 * 3;
172 const leftover = source_len % 4;
173 if (decoder.pad_char != null) {
174 if (leftover % 4 != 0) return error.InvalidPadding;
175 } else {
176 if (leftover % 4 == 1) return error.InvalidPadding;
177 result += leftover * 3 / 4;
178 }
113179 return result;
114180 }
115181
116 /// If the encoded buffer is detected to be invalid, returns error.InvalidPadding.
117 pub fn calcSize(decoder: *const Base64Decoder, source: []const u8) !usize {
118 if (source.len % 4 != 0) return error.InvalidPadding;
119 return calcDecodedSizeExactUnsafe(source, decoder.pad_char);
182 /// Return the exact decoded size for a slice.
183 /// `InvalidPadding` is returned if the input length is not valid.
184 pub fn calcSizeForSlice(decoder: *const Base64Decoder, source: []const u8) Error!usize {
185 const source_len = source.len;
186 var result = try decoder.calcSizeUpperBound(source_len);
187 if (decoder.pad_char) |pad_char| {
188 if (source_len >= 1 and source[source_len - 1] == pad_char) result -= 1;
189 if (source_len >= 2 and source[source_len - 2] == pad_char) result -= 1;
190 }
191 return result;
120192 }
121193
122194 /// dest.len must be what you get from ::calcSize.
123195 /// invalid characters result in error.InvalidCharacter.
124196 /// invalid padding results in error.InvalidPadding.
125 pub fn decode(decoder: *const Base64Decoder, dest: []u8, source: []const u8) !void {
126 assert(dest.len == (decoder.calcSize(source) catch unreachable));
127 assert(source.len % 4 == 0);
128
129 var src_cursor: usize = 0;
130 var dest_cursor: usize = 0;
131
132 while (src_cursor < source.len) : (src_cursor += 4) {
133 if (!decoder.char_in_alphabet[source[src_cursor + 0]]) return error.InvalidCharacter;
134 if (!decoder.char_in_alphabet[source[src_cursor + 1]]) return error.InvalidCharacter;
135 if (src_cursor < source.len - 4 or source[src_cursor + 3] != decoder.pad_char) {
136 // common case
137 if (!decoder.char_in_alphabet[source[src_cursor + 2]]) return error.InvalidCharacter;
138 if (!decoder.char_in_alphabet[source[src_cursor + 3]]) return error.InvalidCharacter;
139 dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 | decoder.char_to_index[source[src_cursor + 1]] >> 4;
140 dest[dest_cursor + 1] = decoder.char_to_index[source[src_cursor + 1]] << 4 | decoder.char_to_index[source[src_cursor + 2]] >> 2;
141 dest[dest_cursor + 2] = decoder.char_to_index[source[src_cursor + 2]] << 6 | decoder.char_to_index[source[src_cursor + 3]];
142 dest_cursor += 3;
143 } else if (source[src_cursor + 2] != decoder.pad_char) {
144 // one pad char
145 if (!decoder.char_in_alphabet[source[src_cursor + 2]]) return error.InvalidCharacter;
146 dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 | decoder.char_to_index[source[src_cursor + 1]] >> 4;
147 dest[dest_cursor + 1] = decoder.char_to_index[source[src_cursor + 1]] << 4 | decoder.char_to_index[source[src_cursor + 2]] >> 2;
148 if (decoder.char_to_index[source[src_cursor + 2]] << 6 != 0) return error.InvalidPadding;
149 dest_cursor += 2;
150 } else {
151 // two pad chars
152 dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 | decoder.char_to_index[source[src_cursor + 1]] >> 4;
153 if (decoder.char_to_index[source[src_cursor + 1]] << 4 != 0) return error.InvalidPadding;
154 dest_cursor += 1;
197 pub fn decode(decoder: *const Base64Decoder, dest: []u8, source: []const u8) Error!void {
198 if (decoder.pad_char != null and source.len % 4 != 0) return error.InvalidPadding;
199 var acc: u12 = 0;
200 var acc_len: u4 = 0;
201 var dest_idx: usize = 0;
202 var leftover_idx: ?usize = null;
203 for (source) |c, src_idx| {
204 const d = decoder.char_to_index[c];
205 if (d == invalid_char) {
206 if (decoder.pad_char == null or c != decoder.pad_char.?) return error.InvalidCharacter;
207 leftover_idx = src_idx;
208 break;
209 }
210 acc = (acc << 6) + d;
211 acc_len += 6;
212 if (acc_len >= 8) {
213 acc_len -= 8;
214 dest[dest_idx] = @truncate(u8, acc >> acc_len);
215 dest_idx += 1;
155216 }
156217 }
157
158 assert(src_cursor == source.len);
159 assert(dest_cursor == dest.len);
218 if (acc_len > 4 or (acc & (@as(u12, 1) << acc_len) - 1) != 0) {
219 return error.InvalidPadding;
220 }
221 if (leftover_idx == null) return;
222 var leftover = source[leftover_idx.?..];
223 if (decoder.pad_char) |pad_char| {
224 const padding_len = acc_len / 2;
225 var padding_chars: usize = 0;
226 var i: usize = 0;
227 for (leftover) |c| {
228 if (c != pad_char) {
229 return if (c == Base64Decoder.invalid_char) error.InvalidCharacter else error.InvalidPadding;
230 }
231 padding_chars += 1;
232 }
233 if (padding_chars != padding_len) return error.InvalidPadding;
234 }
160235 }
161236};
162237
163238pub const Base64DecoderWithIgnore = struct {
164239 decoder: Base64Decoder,
165240 char_is_ignored: [256]bool,
166 pub fn init(alphabet_chars: []const u8, pad_char: u8, ignore_chars: []const u8) Base64DecoderWithIgnore {
241
242 pub fn init(alphabet_chars: [64]u8, pad_char: ?u8, ignore_chars: []const u8) Base64DecoderWithIgnore {
167243 var result = Base64DecoderWithIgnore{
168244 .decoder = Base64Decoder.init(alphabet_chars, pad_char),
169245 .char_is_ignored = [_]bool{false} ** 256,
170246 };
171
172247 for (ignore_chars) |c| {
173 assert(!result.decoder.char_in_alphabet[c]);
248 assert(result.decoder.char_to_index[c] == Base64Decoder.invalid_char);
174249 assert(!result.char_is_ignored[c]);
175250 assert(result.decoder.pad_char != c);
176251 result.char_is_ignored[c] = true;
177252 }
178
179253 return result;
180254 }
181255
182 /// If no characters end up being ignored or padding, this will be the exact decoded size.
183 pub fn calcSizeUpperBound(encoded_len: usize) usize {
184 return @divTrunc(encoded_len, 4) * 3;
256 /// Return the maximum possible decoded size for a given input length - The actual length may be less if the input includes padding
257 /// `InvalidPadding` is returned if the input length is not valid.
258 pub fn calcSizeUpperBound(decoder_with_ignore: *const Base64DecoderWithIgnore, source_len: usize) Error!usize {
259 var result = source_len / 4 * 3;
260 if (decoder_with_ignore.decoder.pad_char == null) {
261 const leftover = source_len % 4;
262 result += leftover * 3 / 4;
263 }
264 return result;
185265 }
186266
187267 /// Invalid characters that are not ignored result in error.InvalidCharacter.
188268 /// Invalid padding results in error.InvalidPadding.
189 /// Decoding more data than can fit in dest results in error.OutputTooSmall. See also ::calcSizeUpperBound.
269 /// Decoding more data than can fit in dest results in error.NoSpaceLeft. See also ::calcSizeUpperBound.
190270 /// Returns the number of bytes written to dest.
191 pub fn decode(decoder_with_ignore: *const Base64DecoderWithIgnore, dest: []u8, source: []const u8) !usize {
271 pub fn decode(decoder_with_ignore: *const Base64DecoderWithIgnore, dest: []u8, source: []const u8) Error!usize {
192272 const decoder = &decoder_with_ignore.decoder;
193
194 var src_cursor: usize = 0;
195 var dest_cursor: usize = 0;
196
197 while (true) {
198 // get the next 4 chars, if available
199 var next_4_chars: [4]u8 = undefined;
200 var available_chars: usize = 0;
201 var pad_char_count: usize = 0;
202 while (available_chars < 4 and src_cursor < source.len) {
203 var c = source[src_cursor];
204 src_cursor += 1;
205
206 if (decoder.char_in_alphabet[c]) {
207 // normal char
208 next_4_chars[available_chars] = c;
209 available_chars += 1;
210 } else if (decoder_with_ignore.char_is_ignored[c]) {
211 // we're told to skip this one
212 continue;
213 } else if (c == decoder.pad_char) {
214 // the padding has begun. count the pad chars.
215 pad_char_count += 1;
216 while (src_cursor < source.len) {
217 c = source[src_cursor];
218 src_cursor += 1;
219 if (c == decoder.pad_char) {
220 pad_char_count += 1;
221 if (pad_char_count > 2) return error.InvalidCharacter;
222 } else if (decoder_with_ignore.char_is_ignored[c]) {
223 // we can even ignore chars during the padding
224 continue;
225 } else return error.InvalidCharacter;
226 }
227 break;
228 } else return error.InvalidCharacter;
273 var acc: u12 = 0;
274 var acc_len: u4 = 0;
275 var dest_idx: usize = 0;
276 var leftover_idx: ?usize = null;
277 for (source) |c, src_idx| {
278 if (decoder_with_ignore.char_is_ignored[c]) continue;
279 const d = decoder.char_to_index[c];
280 if (d == Base64Decoder.invalid_char) {
281 if (decoder.pad_char == null or c != decoder.pad_char.?) return error.InvalidCharacter;
282 leftover_idx = src_idx;
283 break;
229284 }
230
231 switch (available_chars) {
232 4 => {
233 // common case
234 if (dest_cursor + 3 > dest.len) return error.OutputTooSmall;
235 assert(pad_char_count == 0);
236 dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 | decoder.char_to_index[next_4_chars[1]] >> 4;
237 dest[dest_cursor + 1] = decoder.char_to_index[next_4_chars[1]] << 4 | decoder.char_to_index[next_4_chars[2]] >> 2;
238 dest[dest_cursor + 2] = decoder.char_to_index[next_4_chars[2]] << 6 | decoder.char_to_index[next_4_chars[3]];
239 dest_cursor += 3;
240 continue;
241 },
242 3 => {
243 if (dest_cursor + 2 > dest.len) return error.OutputTooSmall;
244 if (pad_char_count != 1) return error.InvalidPadding;
245 dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 | decoder.char_to_index[next_4_chars[1]] >> 4;
246 dest[dest_cursor + 1] = decoder.char_to_index[next_4_chars[1]] << 4 | decoder.char_to_index[next_4_chars[2]] >> 2;
247 if (decoder.char_to_index[next_4_chars[2]] << 6 != 0) return error.InvalidPadding;
248 dest_cursor += 2;
249 break;
250 },
251 2 => {
252 if (dest_cursor + 1 > dest.len) return error.OutputTooSmall;
253 if (pad_char_count != 2) return error.InvalidPadding;
254 dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 | decoder.char_to_index[next_4_chars[1]] >> 4;
255 if (decoder.char_to_index[next_4_chars[1]] << 4 != 0) return error.InvalidPadding;
256 dest_cursor += 1;
257 break;
258 },
259 1 => {
260 return error.InvalidPadding;
261 },
262 0 => {
263 if (pad_char_count != 0) return error.InvalidPadding;
264 break;
265 },
266 else => unreachable,
285 acc = (acc << 6) + d;
286 acc_len += 6;
287 if (acc_len >= 8) {
288 if (dest_idx == dest.len) return error.NoSpaceLeft;
289 acc_len -= 8;
290 dest[dest_idx] = @truncate(u8, acc >> acc_len);
291 dest_idx += 1;
267292 }
268293 }
269
270 assert(src_cursor == source.len);
271
272 return dest_cursor;
273 }
274};
275
276pub const standard_decoder_unsafe = Base64DecoderUnsafe.init(standard_alphabet_chars, standard_pad_char);
277
278pub const Base64DecoderUnsafe = struct {
279 /// e.g. 'A' => 0.
280 /// undefined for any value not in the 64 alphabet chars.
281 char_to_index: [256]u8,
282 pad_char: u8,
283
284 pub fn init(alphabet_chars: []const u8, pad_char: u8) Base64DecoderUnsafe {
285 assert(alphabet_chars.len == 64);
286 var result = Base64DecoderUnsafe{
287 .char_to_index = undefined,
288 .pad_char = pad_char,
289 };
290 for (alphabet_chars) |c, i| {
291 assert(c != pad_char);
292 result.char_to_index[c] = @intCast(u8, i);
294 if (acc_len > 4 or (acc & (@as(u12, 1) << acc_len) - 1) != 0) {
295 return error.InvalidPadding;
293296 }
294 return result;
295 }
296
297 /// The source buffer must be valid.
298 pub fn calcSize(decoder: *const Base64DecoderUnsafe, source: []const u8) usize {
299 return calcDecodedSizeExactUnsafe(source, decoder.pad_char);
300 }
301
302 /// dest.len must be what you get from ::calcDecodedSizeExactUnsafe.
303 /// invalid characters or padding will result in undefined values.
304 pub fn decode(decoder: *const Base64DecoderUnsafe, dest: []u8, source: []const u8) void {
305 assert(dest.len == decoder.calcSize(source));
306
307 var src_index: usize = 0;
308 var dest_index: usize = 0;
309 var in_buf_len: usize = source.len;
310
311 while (in_buf_len > 0 and source[in_buf_len - 1] == decoder.pad_char) {
312 in_buf_len -= 1;
297 const padding_len = acc_len / 2;
298 if (leftover_idx == null) {
299 if (decoder.pad_char != null and padding_len != 0) return error.InvalidPadding;
300 return dest_idx;
313301 }
314
315 while (in_buf_len > 4) {
316 dest[dest_index] = decoder.char_to_index[source[src_index + 0]] << 2 | decoder.char_to_index[source[src_index + 1]] >> 4;
317 dest_index += 1;
318
319 dest[dest_index] = decoder.char_to_index[source[src_index + 1]] << 4 | decoder.char_to_index[source[src_index + 2]] >> 2;
320 dest_index += 1;
321
322 dest[dest_index] = decoder.char_to_index[source[src_index + 2]] << 6 | decoder.char_to_index[source[src_index + 3]];
323 dest_index += 1;
324
325 src_index += 4;
326 in_buf_len -= 4;
327 }
328
329 if (in_buf_len > 1) {
330 dest[dest_index] = decoder.char_to_index[source[src_index + 0]] << 2 | decoder.char_to_index[source[src_index + 1]] >> 4;
331 dest_index += 1;
332 }
333 if (in_buf_len > 2) {
334 dest[dest_index] = decoder.char_to_index[source[src_index + 1]] << 4 | decoder.char_to_index[source[src_index + 2]] >> 2;
335 dest_index += 1;
336 }
337 if (in_buf_len > 3) {
338 dest[dest_index] = decoder.char_to_index[source[src_index + 2]] << 6 | decoder.char_to_index[source[src_index + 3]];
339 dest_index += 1;
302 var leftover = source[leftover_idx.?..];
303 if (decoder.pad_char) |pad_char| {
304 var padding_chars: usize = 0;
305 var i: usize = 0;
306 for (leftover) |c| {
307 if (decoder_with_ignore.char_is_ignored[c]) continue;
308 if (c != pad_char) {
309 return if (c == Base64Decoder.invalid_char) error.InvalidCharacter else error.InvalidPadding;
310 }
311 padding_chars += 1;
312 }
313 if (padding_chars != padding_len) return error.InvalidPadding;
340314 }
315 return dest_idx;
341316 }
342317};
343318
344fn calcDecodedSizeExactUnsafe(source: []const u8, pad_char: u8) usize {
345 if (source.len == 0) return 0;
346 var result = @divExact(source.len, 4) * 3;
347 if (source[source.len - 1] == pad_char) {
348 result -= 1;
349 if (source[source.len - 2] == pad_char) {
350 result -= 1;
351 }
352 }
353 return result;
354}
355
356319test "base64" {
357320 @setEvalBranchQuota(8000);
358321 testBase64() catch unreachable;
359 comptime (testBase64() catch unreachable);
322 comptime testAllApis(standard, "comptime", "Y29tcHRpbWU=") catch unreachable;
323}
324
325test "base64 url_safe_no_pad" {
326 @setEvalBranchQuota(8000);
327 testBase64UrlSafeNoPad() catch unreachable;
328 comptime testAllApis(url_safe_no_pad, "comptime", "Y29tcHRpbWU") catch unreachable;
360329}
361330
362331fn testBase64() !void {
363 try testAllApis("", "");
364 try testAllApis("f", "Zg==");
365 try testAllApis("fo", "Zm8=");
366 try testAllApis("foo", "Zm9v");
367 try testAllApis("foob", "Zm9vYg==");
368 try testAllApis("fooba", "Zm9vYmE=");
369 try testAllApis("foobar", "Zm9vYmFy");
370
371 try testDecodeIgnoreSpace("", " ");
372 try testDecodeIgnoreSpace("f", "Z g= =");
373 try testDecodeIgnoreSpace("fo", " Zm8=");
374 try testDecodeIgnoreSpace("foo", "Zm9v ");
375 try testDecodeIgnoreSpace("foob", "Zm9vYg = = ");
376 try testDecodeIgnoreSpace("fooba", "Zm9v YmE=");
377 try testDecodeIgnoreSpace("foobar", " Z m 9 v Y m F y ");
332 const codecs = standard;
333
334 try testAllApis(codecs, "", "");
335 try testAllApis(codecs, "f", "Zg==");
336 try testAllApis(codecs, "fo", "Zm8=");
337 try testAllApis(codecs, "foo", "Zm9v");
338 try testAllApis(codecs, "foob", "Zm9vYg==");
339 try testAllApis(codecs, "fooba", "Zm9vYmE=");
340 try testAllApis(codecs, "foobar", "Zm9vYmFy");
341
342 try testDecodeIgnoreSpace(codecs, "", " ");
343 try testDecodeIgnoreSpace(codecs, "f", "Z g= =");
344 try testDecodeIgnoreSpace(codecs, "fo", " Zm8=");
345 try testDecodeIgnoreSpace(codecs, "foo", "Zm9v ");
346 try testDecodeIgnoreSpace(codecs, "foob", "Zm9vYg = = ");
347 try testDecodeIgnoreSpace(codecs, "fooba", "Zm9v YmE=");
348 try testDecodeIgnoreSpace(codecs, "foobar", " Z m 9 v Y m F y ");
349
350 // test getting some api errors
351 try testError(codecs, "A", error.InvalidPadding);
352 try testError(codecs, "AA", error.InvalidPadding);
353 try testError(codecs, "AAA", error.InvalidPadding);
354 try testError(codecs, "A..A", error.InvalidCharacter);
355 try testError(codecs, "AA=A", error.InvalidPadding);
356 try testError(codecs, "AA/=", error.InvalidPadding);
357 try testError(codecs, "A/==", error.InvalidPadding);
358 try testError(codecs, "A===", error.InvalidPadding);
359 try testError(codecs, "====", error.InvalidPadding);
360
361 try testNoSpaceLeftError(codecs, "AA==");
362 try testNoSpaceLeftError(codecs, "AAA=");
363 try testNoSpaceLeftError(codecs, "AAAA");
364 try testNoSpaceLeftError(codecs, "AAAAAA==");
365}
366
367fn testBase64UrlSafeNoPad() !void {
368 const codecs = url_safe_no_pad;
369
370 try testAllApis(codecs, "", "");
371 try testAllApis(codecs, "f", "Zg");
372 try testAllApis(codecs, "fo", "Zm8");
373 try testAllApis(codecs, "foo", "Zm9v");
374 try testAllApis(codecs, "foob", "Zm9vYg");
375 try testAllApis(codecs, "fooba", "Zm9vYmE");
376 try testAllApis(codecs, "foobar", "Zm9vYmFy");
377
378 try testDecodeIgnoreSpace(codecs, "", " ");
379 try testDecodeIgnoreSpace(codecs, "f", "Z g ");
380 try testDecodeIgnoreSpace(codecs, "fo", " Zm8");
381 try testDecodeIgnoreSpace(codecs, "foo", "Zm9v ");
382 try testDecodeIgnoreSpace(codecs, "foob", "Zm9vYg ");
383 try testDecodeIgnoreSpace(codecs, "fooba", "Zm9v YmE");
384 try testDecodeIgnoreSpace(codecs, "foobar", " Z m 9 v Y m F y ");
378385
379386 // test getting some api errors
380 try testError("A", error.InvalidPadding);
381 try testError("AA", error.InvalidPadding);
382 try testError("AAA", error.InvalidPadding);
383 try testError("A..A", error.InvalidCharacter);
384 try testError("AA=A", error.InvalidCharacter);
385 try testError("AA/=", error.InvalidPadding);
386 try testError("A/==", error.InvalidPadding);
387 try testError("A===", error.InvalidCharacter);
388 try testError("====", error.InvalidCharacter);
389
390 try testOutputTooSmallError("AA==");
391 try testOutputTooSmallError("AAA=");
392 try testOutputTooSmallError("AAAA");
393 try testOutputTooSmallError("AAAAAA==");
387 try testError(codecs, "A", error.InvalidPadding);
388 try testError(codecs, "AAA=", error.InvalidCharacter);
389 try testError(codecs, "A..A", error.InvalidCharacter);
390 try testError(codecs, "AA=A", error.InvalidCharacter);
391 try testError(codecs, "AA/=", error.InvalidCharacter);
392 try testError(codecs, "A/==", error.InvalidCharacter);
393 try testError(codecs, "A===", error.InvalidCharacter);
394 try testError(codecs, "====", error.InvalidCharacter);
395
396 try testNoSpaceLeftError(codecs, "AA");
397 try testNoSpaceLeftError(codecs, "AAA");
398 try testNoSpaceLeftError(codecs, "AAAA");
399 try testNoSpaceLeftError(codecs, "AAAAAA");
394400}
395401
396fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) !void {
402fn testAllApis(codecs: Codecs, expected_decoded: []const u8, expected_encoded: []const u8) !void {
397403 // Base64Encoder
398404 {
399405 var buffer: [0x100]u8 = undefined;
400 const encoded = standard_encoder.encode(&buffer, expected_decoded);
406 const encoded = codecs.Encoder.encode(&buffer, expected_decoded);
401407 testing.expectEqualSlices(u8, expected_encoded, encoded);
402408 }
403409
404410 // Base64Decoder
405411 {
406412 var buffer: [0x100]u8 = undefined;
407 var decoded = buffer[0..try standard_decoder.calcSize(expected_encoded)];
408 try standard_decoder.decode(decoded, expected_encoded);
413 var decoded = buffer[0..try codecs.Decoder.calcSizeForSlice(expected_encoded)];
414 try codecs.Decoder.decode(decoded, expected_encoded);
409415 testing.expectEqualSlices(u8, expected_decoded, decoded);
410416 }
411417
412418 // Base64DecoderWithIgnore
413419 {
414 const standard_decoder_ignore_nothing = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, "");
420 const decoder_ignore_nothing = codecs.decoderWithIgnore("");
415421 var buffer: [0x100]u8 = undefined;
416 var decoded = buffer[0..Base64DecoderWithIgnore.calcSizeUpperBound(expected_encoded.len)];
417 var written = try standard_decoder_ignore_nothing.decode(decoded, expected_encoded);
422 var decoded = buffer[0..try decoder_ignore_nothing.calcSizeUpperBound(expected_encoded.len)];
423 var written = try decoder_ignore_nothing.decode(decoded, expected_encoded);
418424 testing.expect(written <= decoded.len);
419425 testing.expectEqualSlices(u8, expected_decoded, decoded[0..written]);
420426 }
421
422 // Base64DecoderUnsafe
423 {
424 var buffer: [0x100]u8 = undefined;
425 var decoded = buffer[0..standard_decoder_unsafe.calcSize(expected_encoded)];
426 standard_decoder_unsafe.decode(decoded, expected_encoded);
427 testing.expectEqualSlices(u8, expected_decoded, decoded);
428 }
429427}
430428
431fn testDecodeIgnoreSpace(expected_decoded: []const u8, encoded: []const u8) !void {
432 const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, " ");
429fn testDecodeIgnoreSpace(codecs: Codecs, expected_decoded: []const u8, encoded: []const u8) !void {
430 const decoder_ignore_space = codecs.decoderWithIgnore(" ");
433431 var buffer: [0x100]u8 = undefined;
434 var decoded = buffer[0..Base64DecoderWithIgnore.calcSizeUpperBound(encoded.len)];
435 var written = try standard_decoder_ignore_space.decode(decoded, encoded);
432 var decoded = buffer[0..try decoder_ignore_space.calcSizeUpperBound(encoded.len)];
433 var written = try decoder_ignore_space.decode(decoded, encoded);
436434 testing.expectEqualSlices(u8, expected_decoded, decoded[0..written]);
437435}
438436
439fn testError(encoded: []const u8, expected_err: anyerror) !void {
440 const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, " ");
437fn testError(codecs: Codecs, encoded: []const u8, expected_err: anyerror) !void {
438 const decoder_ignore_space = codecs.decoderWithIgnore(" ");
441439 var buffer: [0x100]u8 = undefined;
442 if (standard_decoder.calcSize(encoded)) |decoded_size| {
440 if (codecs.Decoder.calcSizeForSlice(encoded)) |decoded_size| {
443441 var decoded = buffer[0..decoded_size];
444 if (standard_decoder.decode(decoded, encoded)) |_| {
442 if (codecs.Decoder.decode(decoded, encoded)) |_| {
445443 return error.ExpectedError;
446444 } else |err| if (err != expected_err) return err;
447445 } else |err| if (err != expected_err) return err;
448446
449 if (standard_decoder_ignore_space.decode(buffer[0..], encoded)) |_| {
447 if (decoder_ignore_space.decode(buffer[0..], encoded)) |_| {
450448 return error.ExpectedError;
451449 } else |err| if (err != expected_err) return err;
452450}
453451
454fn testOutputTooSmallError(encoded: []const u8) !void {
455 const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, " ");
452fn testNoSpaceLeftError(codecs: Codecs, encoded: []const u8) !void {
453 const decoder_ignore_space = codecs.decoderWithIgnore(" ");
456454 var buffer: [0x100]u8 = undefined;
457 var decoded = buffer[0 .. calcDecodedSizeExactUnsafe(encoded, standard_pad_char) - 1];
458 if (standard_decoder_ignore_space.decode(decoded, encoded)) |_| {
455 var decoded = buffer[0 .. (try codecs.Decoder.calcSizeForSlice(encoded)) - 1];
456 if (decoder_ignore_space.decode(decoded, encoded)) |_| {
459457 return error.ExpectedError;
460 } else |err| if (err != error.OutputTooSmall) return err;
458 } else |err| if (err != error.NoSpaceLeft) return err;
461459}
lib/std/bit_set.zig+21-7
......@@ -176,7 +176,7 @@ pub fn IntegerBitSet(comptime size: u16) type {
176176 /// The default options (.{}) will iterate indices of set bits in
177177 /// ascending order. Modifications to the underlying bit set may
178178 /// or may not be observed by the iterator.
179 pub fn iterator(self: *const Self, comptime options: IteratorOptions) Iterator(options.direction) {
179 pub fn iterator(self: *const Self, comptime options: IteratorOptions) Iterator(options) {
180180 return .{
181181 .bits_remain = switch (options.kind) {
182182 .set => self.mask,
......@@ -185,7 +185,11 @@ pub fn IntegerBitSet(comptime size: u16) type {
185185 };
186186 }
187187
188 fn Iterator(comptime direction: IteratorOptions.Direction) type {
188 pub fn Iterator(comptime options: IteratorOptions) type {
189 return SingleWordIterator(options.direction);
190 }
191
192 fn SingleWordIterator(comptime direction: IteratorOptions.Direction) type {
189193 return struct {
190194 const IterSelf = @This();
191195 // all bits which have not yet been iterated over
......@@ -425,8 +429,12 @@ pub fn ArrayBitSet(comptime MaskIntType: type, comptime size: usize) type {
425429 /// The default options (.{}) will iterate indices of set bits in
426430 /// ascending order. Modifications to the underlying bit set may
427431 /// or may not be observed by the iterator.
428 pub fn iterator(self: *const Self, comptime options: IteratorOptions) BitSetIterator(MaskInt, options) {
429 return BitSetIterator(MaskInt, options).init(&self.masks, last_item_mask);
432 pub fn iterator(self: *const Self, comptime options: IteratorOptions) Iterator(options) {
433 return Iterator(options).init(&self.masks, last_item_mask);
434 }
435
436 pub fn Iterator(comptime options: IteratorOptions) type {
437 return BitSetIterator(MaskInt, options);
430438 }
431439
432440 fn maskBit(index: usize) MaskInt {
......@@ -700,11 +708,15 @@ pub const DynamicBitSetUnmanaged = struct {
700708 /// ascending order. Modifications to the underlying bit set may
701709 /// or may not be observed by the iterator. Resizing the underlying
702710 /// bit set invalidates the iterator.
703 pub fn iterator(self: *const Self, comptime options: IteratorOptions) BitSetIterator(MaskInt, options) {
711 pub fn iterator(self: *const Self, comptime options: IteratorOptions) Iterator(options) {
704712 const num_masks = numMasks(self.bit_length);
705713 const padding_bits = num_masks * @bitSizeOf(MaskInt) - self.bit_length;
706714 const last_item_mask = (~@as(MaskInt, 0)) >> @intCast(ShiftInt, padding_bits);
707 return BitSetIterator(MaskInt, options).init(self.masks[0..num_masks], last_item_mask);
715 return Iterator(options).init(self.masks[0..num_masks], last_item_mask);
716 }
717
718 pub fn Iterator(comptime options: IteratorOptions) type {
719 return BitSetIterator(MaskInt, options);
708720 }
709721
710722 fn maskBit(index: usize) MaskInt {
......@@ -858,9 +870,11 @@ pub const DynamicBitSet = struct {
858870 /// ascending order. Modifications to the underlying bit set may
859871 /// or may not be observed by the iterator. Resizing the underlying
860872 /// bit set invalidates the iterator.
861 pub fn iterator(self: *Self, comptime options: IteratorOptions) BitSetIterator(MaskInt, options) {
873 pub fn iterator(self: *Self, comptime options: IteratorOptions) Iterator(options) {
862874 return self.unmanaged.iterator(options);
863875 }
876
877 pub const Iterator = DynamicBitSetUnmanaged.Iterator;
864878};
865879
866880/// Options for configuring an iterator over a bit set
lib/std/build.zig+7-16
......@@ -51,7 +51,7 @@ pub const Builder = struct {
5151 default_step: *Step,
5252 env_map: *BufMap,
5353 top_level_steps: ArrayList(*TopLevelStep),
54 install_prefix: ?[]const u8,
54 install_prefix: []const u8,
5555 dest_dir: ?[]const u8,
5656 lib_dir: []const u8,
5757 exe_dir: []const u8,
......@@ -156,7 +156,7 @@ pub const Builder = struct {
156156 .default_step = undefined,
157157 .env_map = env_map,
158158 .search_prefixes = ArrayList([]const u8).init(allocator),
159 .install_prefix = null,
159 .install_prefix = undefined,
160160 .lib_dir = undefined,
161161 .exe_dir = undefined,
162162 .h_dir = undefined,
......@@ -190,22 +190,13 @@ pub const Builder = struct {
190190 }
191191
192192 /// This function is intended to be called by std/special/build_runner.zig, not a build.zig file.
193 pub fn setInstallPrefix(self: *Builder, optional_prefix: ?[]const u8) void {
194 self.install_prefix = optional_prefix;
195 }
196
197 /// This function is intended to be called by std/special/build_runner.zig, not a build.zig file.
198 pub fn resolveInstallPrefix(self: *Builder) void {
193 pub fn resolveInstallPrefix(self: *Builder, install_prefix: ?[]const u8) void {
199194 if (self.dest_dir) |dest_dir| {
200 const install_prefix = self.install_prefix orelse "/usr";
201 self.install_path = fs.path.join(self.allocator, &[_][]const u8{ dest_dir, install_prefix }) catch unreachable;
195 self.install_prefix = install_prefix orelse "/usr";
196 self.install_path = fs.path.join(self.allocator, &[_][]const u8{ dest_dir, self.install_prefix }) catch unreachable;
202197 } else {
203 const install_prefix = self.install_prefix orelse blk: {
204 const p = self.cache_root;
205 self.install_prefix = p;
206 break :blk p;
207 };
208 self.install_path = install_prefix;
198 self.install_prefix = install_prefix orelse self.cache_root;
199 self.install_path = self.install_prefix;
209200 }
210201 self.lib_dir = fs.path.join(self.allocator, &[_][]const u8{ self.install_path, "lib" }) catch unreachable;
211202 self.exe_dir = fs.path.join(self.allocator, &[_][]const u8{ self.install_path, "bin" }) catch unreachable;
lib/std/c.zig+3-3
......@@ -295,9 +295,9 @@ pub extern "c" fn kevent(
295295) c_int;
296296
297297pub extern "c" fn getaddrinfo(
298 noalias node: [*:0]const u8,
299 noalias service: [*:0]const u8,
300 noalias hints: *const addrinfo,
298 noalias node: ?[*:0]const u8,
299 noalias service: ?[*:0]const u8,
300 noalias hints: ?*const addrinfo,
301301 noalias res: **addrinfo,
302302) EAI;
303303
lib/std/c/builtins.zig+7-1
......@@ -140,7 +140,7 @@ pub fn __builtin_object_size(ptr: ?*const c_void, ty: c_int) callconv(.Inline) u
140140 // If it is not possible to determine which objects ptr points to at compile time,
141141 // __builtin_object_size should return (size_t) -1 for type 0 or 1 and (size_t) 0
142142 // for type 2 or 3.
143 if (ty == 0 or ty == 1) return @bitCast(usize, -@as(c_long, 1));
143 if (ty == 0 or ty == 1) return @bitCast(usize, -@as(isize, 1));
144144 if (ty == 2 or ty == 3) return 0;
145145 unreachable;
146146}
......@@ -188,3 +188,9 @@ pub fn __builtin_memcpy(
188188pub fn __builtin_expect(expr: c_long, c: c_long) callconv(.Inline) c_long {
189189 return expr;
190190}
191
192// __builtin_alloca_with_align is not currently implemented.
193// It is used in a run-translated-c test and a test-translate-c test to ensure that non-implemented
194// builtins are correctly demoted. If you implement __builtin_alloca_with_align, please update the
195// run-translated-c test and the test-translate-c test to use a different non-implemented builtin.
196// pub fn __builtin_alloca_with_align(size: usize, alignment: usize) callconv(.Inline) *c_void {}
lib/std/crypto.zig+17-3
......@@ -24,8 +24,12 @@ pub const aead = struct {
2424 pub const Gimli = @import("crypto/gimli.zig").Aead;
2525
2626 pub const chacha_poly = struct {
27 pub const ChaCha20Poly1305 = @import("crypto/chacha20.zig").Chacha20Poly1305;
28 pub const XChaCha20Poly1305 = @import("crypto/chacha20.zig").XChacha20Poly1305;
27 pub const ChaCha20Poly1305 = @import("crypto/chacha20.zig").ChaCha20Poly1305;
28 pub const ChaCha12Poly1305 = @import("crypto/chacha20.zig").ChaCha12Poly1305;
29 pub const ChaCha8Poly1305 = @import("crypto/chacha20.zig").ChaCha8Poly1305;
30 pub const XChaCha20Poly1305 = @import("crypto/chacha20.zig").XChaCha20Poly1305;
31 pub const XChaCha12Poly1305 = @import("crypto/chacha20.zig").XChaCha12Poly1305;
32 pub const XChaCha8Poly1305 = @import("crypto/chacha20.zig").XChaCha8Poly1305;
2933 };
3034
3135 pub const isap = @import("crypto/isap.zig");
......@@ -119,8 +123,14 @@ pub const sign = struct {
119123pub const stream = struct {
120124 pub const chacha = struct {
121125 pub const ChaCha20IETF = @import("crypto/chacha20.zig").ChaCha20IETF;
126 pub const ChaCha12IETF = @import("crypto/chacha20.zig").ChaCha12IETF;
127 pub const ChaCha8IETF = @import("crypto/chacha20.zig").ChaCha8IETF;
122128 pub const ChaCha20With64BitNonce = @import("crypto/chacha20.zig").ChaCha20With64BitNonce;
129 pub const ChaCha12With64BitNonce = @import("crypto/chacha20.zig").ChaCha12With64BitNonce;
130 pub const ChaCha8With64BitNonce = @import("crypto/chacha20.zig").ChaCha8With64BitNonce;
123131 pub const XChaCha20IETF = @import("crypto/chacha20.zig").XChaCha20IETF;
132 pub const XChaCha12IETF = @import("crypto/chacha20.zig").XChaCha12IETF;
133 pub const XChaCha8IETF = @import("crypto/chacha20.zig").XChaCha8IETF;
124134 };
125135
126136 pub const salsa = struct {
......@@ -144,6 +154,8 @@ pub const random = &@import("crypto/tlcsprng.zig").interface;
144154
145155const std = @import("std.zig");
146156
157pub const Error = @import("crypto/error.zig").Error;
158
147159test "crypto" {
148160 const please_windows_dont_oom = std.Target.current.os.tag == .windows;
149161 if (please_windows_dont_oom) return error.SkipZigTest;
......@@ -151,7 +163,9 @@ test "crypto" {
151163 inline for (std.meta.declarations(@This())) |decl| {
152164 switch (decl.data) {
153165 .Type => |t| {
154 std.testing.refAllDecls(t);
166 if (@typeInfo(t) != .ErrorSet) {
167 std.testing.refAllDecls(t);
168 }
155169 },
156170 .Var => |v| {
157171 _ = v;
lib/std/crypto/25519/curve25519.zig+7-6
......@@ -4,6 +4,7 @@
44// The MIT license requires this copyright notice to be included in all copies
55// and substantial portions of the software.
66const std = @import("std");
7const Error = std.crypto.Error;
78
89/// Group operations over Curve25519.
910pub const Curve25519 = struct {
......@@ -28,12 +29,12 @@ pub const Curve25519 = struct {
2829 pub const basePoint = Curve25519{ .x = Fe.curve25519BasePoint };
2930
3031 /// Check that the encoding of a Curve25519 point is canonical.
31 pub fn rejectNonCanonical(s: [32]u8) !void {
32 pub fn rejectNonCanonical(s: [32]u8) Error!void {
3233 return Fe.rejectNonCanonical(s, false);
3334 }
3435
3536 /// Reject the neutral element.
36 pub fn rejectIdentity(p: Curve25519) !void {
37 pub fn rejectIdentity(p: Curve25519) Error!void {
3738 if (p.x.isZero()) {
3839 return error.IdentityElement;
3940 }
......@@ -44,7 +45,7 @@ pub const Curve25519 = struct {
4445 return p.dbl().dbl().dbl();
4546 }
4647
47 fn ladder(p: Curve25519, s: [32]u8, comptime bits: usize) !Curve25519 {
48 fn ladder(p: Curve25519, s: [32]u8, comptime bits: usize) Error!Curve25519 {
4849 var x1 = p.x;
4950 var x2 = Fe.one;
5051 var z2 = Fe.zero;
......@@ -85,7 +86,7 @@ pub const Curve25519 = struct {
8586 /// way to use Curve25519 for a DH operation.
8687 /// Return error.IdentityElement if the resulting point is
8788 /// the identity element.
88 pub fn clampedMul(p: Curve25519, s: [32]u8) !Curve25519 {
89 pub fn clampedMul(p: Curve25519, s: [32]u8) Error!Curve25519 {
8990 var t: [32]u8 = s;
9091 scalar.clamp(&t);
9192 return try ladder(p, t, 255);
......@@ -95,14 +96,14 @@ pub const Curve25519 = struct {
9596 /// Return error.IdentityElement if the resulting point is
9697 /// the identity element or error.WeakPublicKey if the public
9798 /// key is a low-order point.
98 pub fn mul(p: Curve25519, s: [32]u8) !Curve25519 {
99 pub fn mul(p: Curve25519, s: [32]u8) Error!Curve25519 {
99100 const cofactor = [_]u8{8} ++ [_]u8{0} ** 31;
100101 _ = ladder(p, cofactor, 4) catch |_| return error.WeakPublicKey;
101102 return try ladder(p, s, 256);
102103 }
103104
104105 /// Compute the Curve25519 equivalent to an Edwards25519 point.
105 pub fn fromEdwards25519(p: std.crypto.ecc.Edwards25519) !Curve25519 {
106 pub fn fromEdwards25519(p: std.crypto.ecc.Edwards25519) Error!Curve25519 {
106107 try p.clearCofactor().rejectIdentity();
107108 const one = std.crypto.ecc.Edwards25519.Fe.one;
108109 const x = one.add(p.y).mul(one.sub(p.y).invert()); // xMont=(1+yEd)/(1-yEd)
lib/std/crypto/25519/ed25519.zig+12-11
......@@ -8,7 +8,8 @@ const crypto = std.crypto;
88const debug = std.debug;
99const fmt = std.fmt;
1010const mem = std.mem;
11const Sha512 = std.crypto.hash.sha2.Sha512;
11const Sha512 = crypto.hash.sha2.Sha512;
12const Error = crypto.Error;
1213
1314/// Ed25519 (EdDSA) signatures.
1415pub const Ed25519 = struct {
......@@ -40,7 +41,7 @@ pub const Ed25519 = struct {
4041 ///
4142 /// For this reason, an EdDSA secret key is commonly called a seed,
4243 /// from which the actual secret is derived.
43 pub fn create(seed: ?[seed_length]u8) !KeyPair {
44 pub fn create(seed: ?[seed_length]u8) Error!KeyPair {
4445 const ss = seed orelse ss: {
4546 var random_seed: [seed_length]u8 = undefined;
4647 crypto.random.bytes(&random_seed);
......@@ -71,7 +72,7 @@ pub const Ed25519 = struct {
7172 /// Sign a message using a key pair, and optional random noise.
7273 /// Having noise creates non-standard, non-deterministic signatures,
7374 /// but has been proven to increase resilience against fault attacks.
74 pub fn sign(msg: []const u8, key_pair: KeyPair, noise: ?[noise_length]u8) ![signature_length]u8 {
75 pub fn sign(msg: []const u8, key_pair: KeyPair, noise: ?[noise_length]u8) Error![signature_length]u8 {
7576 const seed = key_pair.secret_key[0..seed_length];
7677 const public_key = key_pair.secret_key[seed_length..];
7778 if (!mem.eql(u8, public_key, &key_pair.public_key)) {
......@@ -111,8 +112,8 @@ pub const Ed25519 = struct {
111112 }
112113
113114 /// Verify an Ed25519 signature given a message and a public key.
114 /// Returns error.InvalidSignature is the signature verification failed.
115 pub fn verify(sig: [signature_length]u8, msg: []const u8, public_key: [public_length]u8) !void {
115 /// Returns error.SignatureVerificationFailed is the signature verification failed.
116 pub fn verify(sig: [signature_length]u8, msg: []const u8, public_key: [public_length]u8) Error!void {
116117 const r = sig[0..32];
117118 const s = sig[32..64];
118119 try Curve.scalar.rejectNonCanonical(s.*);
......@@ -133,7 +134,7 @@ pub const Ed25519 = struct {
133134 const ah = try a.neg().mulPublic(hram);
134135 const sb_ah = (try Curve.basePoint.mulPublic(s.*)).add(ah);
135136 if (expected_r.sub(sb_ah).clearCofactor().rejectIdentity()) |_| {
136 return error.InvalidSignature;
137 return error.SignatureVerificationFailed;
137138 } else |_| {}
138139 }
139140
......@@ -145,7 +146,7 @@ pub const Ed25519 = struct {
145146 };
146147
147148 /// Verify several signatures in a single operation, much faster than verifying signatures one-by-one
148 pub fn verifyBatch(comptime count: usize, signature_batch: [count]BatchElement) !void {
149 pub fn verifyBatch(comptime count: usize, signature_batch: [count]BatchElement) Error!void {
149150 var r_batch: [count][32]u8 = undefined;
150151 var s_batch: [count][32]u8 = undefined;
151152 var a_batch: [count]Curve = undefined;
......@@ -200,7 +201,7 @@ pub const Ed25519 = struct {
200201
201202 const zsb = try Curve.basePoint.mulPublic(zs_sum);
202203 if (zr.add(zah).sub(zsb).rejectIdentity()) |_| {
203 return error.InvalidSignature;
204 return error.SignatureVerificationFailed;
204205 } else |_| {}
205206 }
206207};
......@@ -223,7 +224,7 @@ test "ed25519 signature" {
223224 var buf: [128]u8 = undefined;
224225 std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&sig)}), "10A442B4A80CC4225B154F43BEF28D2472CA80221951262EB8E0DF9091575E2687CC486E77263C3418C757522D54F84B0359236ABBBD4ACD20DC297FDCA66808");
225226 try Ed25519.verify(sig, "test", key_pair.public_key);
226 std.testing.expectError(error.InvalidSignature, Ed25519.verify(sig, "TEST", key_pair.public_key));
227 std.testing.expectError(error.SignatureVerificationFailed, Ed25519.verify(sig, "TEST", key_pair.public_key));
227228}
228229
229230test "ed25519 batch verification" {
......@@ -251,7 +252,7 @@ test "ed25519 batch verification" {
251252 try Ed25519.verifyBatch(2, signature_batch);
252253
253254 signature_batch[1].sig = sig1;
254 std.testing.expectError(error.InvalidSignature, Ed25519.verifyBatch(signature_batch.len, signature_batch));
255 std.testing.expectError(error.SignatureVerificationFailed, Ed25519.verifyBatch(signature_batch.len, signature_batch));
255256 }
256257}
257258
......@@ -316,7 +317,7 @@ test "ed25519 test vectors" {
316317 .msg_hex = "9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41",
317318 .public_key_hex = "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43",
318319 .sig_hex = "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03be9678ac102edcd92b0210bb34d7428d12ffc5df5f37e359941266a4e35f0f",
319 .expected = error.InvalidSignature, // 8 - non-canonical R
320 .expected = error.SignatureVerificationFailed, // 8 - non-canonical R
320321 },
321322 Vec{
322323 .msg_hex = "9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41",
lib/std/crypto/25519/edwards25519.zig+11-10
......@@ -7,6 +7,7 @@ const std = @import("std");
77const debug = std.debug;
88const fmt = std.fmt;
99const mem = std.mem;
10const Error = std.crypto.Error;
1011
1112/// Group operations over Edwards25519.
1213pub const Edwards25519 = struct {
......@@ -25,7 +26,7 @@ pub const Edwards25519 = struct {
2526 is_base: bool = false,
2627
2728 /// Decode an Edwards25519 point from its compressed (Y+sign) coordinates.
28 pub fn fromBytes(s: [encoded_length]u8) !Edwards25519 {
29 pub fn fromBytes(s: [encoded_length]u8) Error!Edwards25519 {
2930 const z = Fe.one;
3031 const y = Fe.fromBytes(s);
3132 var u = y.sq();
......@@ -55,7 +56,7 @@ pub const Edwards25519 = struct {
5556 }
5657
5758 /// Check that the encoding of a point is canonical.
58 pub fn rejectNonCanonical(s: [32]u8) !void {
59 pub fn rejectNonCanonical(s: [32]u8) Error!void {
5960 return Fe.rejectNonCanonical(s, true);
6061 }
6162
......@@ -80,7 +81,7 @@ pub const Edwards25519 = struct {
8081 const identityElement = Edwards25519{ .x = Fe.zero, .y = Fe.one, .z = Fe.one, .t = Fe.zero };
8182
8283 /// Reject the neutral element.
83 pub fn rejectIdentity(p: Edwards25519) !void {
84 pub fn rejectIdentity(p: Edwards25519) Error!void {
8485 if (p.x.isZero()) {
8586 return error.IdentityElement;
8687 }
......@@ -176,7 +177,7 @@ pub const Edwards25519 = struct {
176177 // Based on real-world benchmarks, we only use this for multi-scalar multiplication.
177178 // NAF could be useful to half the size of precomputation tables, but we intentionally
178179 // avoid these to keep the standard library lightweight.
179 fn pcMul(pc: [9]Edwards25519, s: [32]u8, comptime vartime: bool) !Edwards25519 {
180 fn pcMul(pc: [9]Edwards25519, s: [32]u8, comptime vartime: bool) Error!Edwards25519 {
180181 std.debug.assert(vartime);
181182 const e = nonAdjacentForm(s);
182183 var q = Edwards25519.identityElement;
......@@ -196,7 +197,7 @@ pub const Edwards25519 = struct {
196197 }
197198
198199 // Scalar multiplication with a 4-bit window and the first 15 multiples.
199 fn pcMul16(pc: [16]Edwards25519, s: [32]u8, comptime vartime: bool) !Edwards25519 {
200 fn pcMul16(pc: [16]Edwards25519, s: [32]u8, comptime vartime: bool) Error!Edwards25519 {
200201 var q = Edwards25519.identityElement;
201202 var pos: usize = 252;
202203 while (true) : (pos -= 4) {
......@@ -234,7 +235,7 @@ pub const Edwards25519 = struct {
234235 /// Multiply an Edwards25519 point by a scalar without clamping it.
235236 /// Return error.WeakPublicKey if the resulting point is
236237 /// the identity element.
237 pub fn mul(p: Edwards25519, s: [32]u8) !Edwards25519 {
238 pub fn mul(p: Edwards25519, s: [32]u8) Error!Edwards25519 {
238239 const pc = if (p.is_base) basePointPc else pc: {
239240 const xpc = precompute(p, 15);
240241 xpc[4].rejectIdentity() catch |_| return error.WeakPublicKey;
......@@ -245,7 +246,7 @@ pub const Edwards25519 = struct {
245246
246247 /// Multiply an Edwards25519 point by a *PUBLIC* scalar *IN VARIABLE TIME*
247248 /// This can be used for signature verification.
248 pub fn mulPublic(p: Edwards25519, s: [32]u8) !Edwards25519 {
249 pub fn mulPublic(p: Edwards25519, s: [32]u8) Error!Edwards25519 {
249250 if (p.is_base) {
250251 return pcMul16(basePointPc, s, true);
251252 } else {
......@@ -257,7 +258,7 @@ pub const Edwards25519 = struct {
257258
258259 /// Multiscalar multiplication *IN VARIABLE TIME* for public data
259260 /// Computes ps0*ss0 + ps1*ss1 + ps2*ss2... faster than doing many of these operations individually
260 pub fn mulMulti(comptime count: usize, ps: [count]Edwards25519, ss: [count][32]u8) !Edwards25519 {
261 pub fn mulMulti(comptime count: usize, ps: [count]Edwards25519, ss: [count][32]u8) Error!Edwards25519 {
261262 var pcs: [count][9]Edwards25519 = undefined;
262263 for (ps) |p, i| {
263264 if (p.is_base) {
......@@ -296,14 +297,14 @@ pub const Edwards25519 = struct {
296297 /// This is strongly recommended for DH operations.
297298 /// Return error.WeakPublicKey if the resulting point is
298299 /// the identity element.
299 pub fn clampedMul(p: Edwards25519, s: [32]u8) !Edwards25519 {
300 pub fn clampedMul(p: Edwards25519, s: [32]u8) Error!Edwards25519 {
300301 var t: [32]u8 = s;
301302 scalar.clamp(&t);
302303 return mul(p, t);
303304 }
304305
305306 // montgomery -- recover y = sqrt(x^3 + A*x^2 + x)
306 fn xmontToYmont(x: Fe) !Fe {
307 fn xmontToYmont(x: Fe) Error!Fe {
307308 var x2 = x.sq();
308309 const x3 = x.mul(x2);
309310 x2 = x2.mul32(Fe.edwards25519a_32);
lib/std/crypto/25519/field.zig+3-2
......@@ -6,6 +6,7 @@
66const std = @import("std");
77const readIntLittle = std.mem.readIntLittle;
88const writeIntLittle = std.mem.writeIntLittle;
9const Error = std.crypto.Error;
910
1011pub const Fe = struct {
1112 limbs: [5]u64,
......@@ -112,7 +113,7 @@ pub const Fe = struct {
112113 }
113114
114115 /// Reject non-canonical encodings of an element, possibly ignoring the top bit
115 pub fn rejectNonCanonical(s: [32]u8, comptime ignore_extra_bit: bool) !void {
116 pub fn rejectNonCanonical(s: [32]u8, comptime ignore_extra_bit: bool) Error!void {
116117 var c: u16 = (s[31] & 0x7f) ^ 0x7f;
117118 comptime var i = 30;
118119 inline while (i > 0) : (i -= 1) {
......@@ -412,7 +413,7 @@ pub const Fe = struct {
412413 }
413414
414415 /// Compute the square root of `x2`, returning `error.NotSquare` if `x2` was not a square
415 pub fn sqrt(x2: Fe) !Fe {
416 pub fn sqrt(x2: Fe) Error!Fe {
416417 var x2_copy = x2;
417418 const x = x2.uncheckedSqrt();
418419 const check = x.sq().sub(x2_copy);
lib/std/crypto/25519/ristretto255.zig+5-4
......@@ -5,6 +5,7 @@
55// and substantial portions of the software.
66const std = @import("std");
77const fmt = std.fmt;
8const Error = std.crypto.Error;
89
910/// Group operations over Edwards25519.
1011pub const Ristretto255 = struct {
......@@ -34,7 +35,7 @@ pub const Ristretto255 = struct {
3435 return .{ .ratio_is_square = @boolToInt(has_m_root) | @boolToInt(has_p_root), .root = x.abs() };
3536 }
3637
37 fn rejectNonCanonical(s: [encoded_length]u8) !void {
38 fn rejectNonCanonical(s: [encoded_length]u8) Error!void {
3839 if ((s[0] & 1) != 0) {
3940 return error.NonCanonical;
4041 }
......@@ -42,7 +43,7 @@ pub const Ristretto255 = struct {
4243 }
4344
4445 /// Reject the neutral element.
45 pub fn rejectIdentity(p: Ristretto255) callconv(.Inline) !void {
46 pub fn rejectIdentity(p: Ristretto255) callconv(.Inline) Error!void {
4647 return p.p.rejectIdentity();
4748 }
4849
......@@ -50,7 +51,7 @@ pub const Ristretto255 = struct {
5051 pub const basePoint = Ristretto255{ .p = Curve.basePoint };
5152
5253 /// Decode a Ristretto255 representative.
53 pub fn fromBytes(s: [encoded_length]u8) !Ristretto255 {
54 pub fn fromBytes(s: [encoded_length]u8) Error!Ristretto255 {
5455 try rejectNonCanonical(s);
5556 const s_ = Fe.fromBytes(s);
5657 const ss = s_.sq(); // s^2
......@@ -153,7 +154,7 @@ pub const Ristretto255 = struct {
153154 /// Multiply a Ristretto255 element with a scalar.
154155 /// Return error.WeakPublicKey if the resulting element is
155156 /// the identity element.
156 pub fn mul(p: Ristretto255, s: [encoded_length]u8) callconv(.Inline) !Ristretto255 {
157 pub fn mul(p: Ristretto255, s: [encoded_length]u8) callconv(.Inline) Error!Ristretto255 {
157158 return Ristretto255{ .p = try p.p.mul(s) };
158159 }
159160
lib/std/crypto/25519/scalar.zig+2-1
......@@ -5,6 +5,7 @@
55// and substantial portions of the software.
66const std = @import("std");
77const mem = std.mem;
8const Error = std.crypto.Error;
89
910/// 2^252 + 27742317777372353535851937790883648493
1011pub const field_size = [32]u8{
......@@ -18,7 +19,7 @@ pub const CompressedScalar = [32]u8;
1819pub const zero = [_]u8{0} ** 32;
1920
2021/// Reject a scalar whose encoding is not canonical.
21pub fn rejectNonCanonical(s: [32]u8) !void {
22pub fn rejectNonCanonical(s: [32]u8) Error!void {
2223 var c: u8 = 0;
2324 var n: u8 = 1;
2425 var i: usize = 31;
lib/std/crypto/25519/x25519.zig+6-5
......@@ -9,6 +9,7 @@ const mem = std.mem;
99const fmt = std.fmt;
1010
1111const Sha512 = crypto.hash.sha2.Sha512;
12const Error = crypto.Error;
1213
1314/// X25519 DH function.
1415pub const X25519 = struct {
......@@ -31,7 +32,7 @@ pub const X25519 = struct {
3132 secret_key: [secret_length]u8,
3233
3334 /// Create a new key pair using an optional seed.
34 pub fn create(seed: ?[seed_length]u8) !KeyPair {
35 pub fn create(seed: ?[seed_length]u8) Error!KeyPair {
3536 const sk = seed orelse sk: {
3637 var random_seed: [seed_length]u8 = undefined;
3738 crypto.random.bytes(&random_seed);
......@@ -44,7 +45,7 @@ pub const X25519 = struct {
4445 }
4546
4647 /// Create a key pair from an Ed25519 key pair
47 pub fn fromEd25519(ed25519_key_pair: crypto.sign.Ed25519.KeyPair) !KeyPair {
48 pub fn fromEd25519(ed25519_key_pair: crypto.sign.Ed25519.KeyPair) Error!KeyPair {
4849 const seed = ed25519_key_pair.secret_key[0..32];
4950 var az: [Sha512.digest_length]u8 = undefined;
5051 Sha512.hash(seed, &az, .{});
......@@ -59,13 +60,13 @@ pub const X25519 = struct {
5960 };
6061
6162 /// Compute the public key for a given private key.
62 pub fn recoverPublicKey(secret_key: [secret_length]u8) ![public_length]u8 {
63 pub fn recoverPublicKey(secret_key: [secret_length]u8) Error![public_length]u8 {
6364 const q = try Curve.basePoint.clampedMul(secret_key);
6465 return q.toBytes();
6566 }
6667
6768 /// Compute the X25519 equivalent to an Ed25519 public eky.
68 pub fn publicKeyFromEd25519(ed25519_public_key: [crypto.sign.Ed25519.public_length]u8) ![public_length]u8 {
69 pub fn publicKeyFromEd25519(ed25519_public_key: [crypto.sign.Ed25519.public_length]u8) Error![public_length]u8 {
6970 const pk_ed = try crypto.ecc.Edwards25519.fromBytes(ed25519_public_key);
7071 const pk = try Curve.fromEdwards25519(pk_ed);
7172 return pk.toBytes();
......@@ -74,7 +75,7 @@ pub const X25519 = struct {
7475 /// Compute the scalar product of a public key and a secret scalar.
7576 /// Note that the output should not be used as a shared secret without
7677 /// hashing it first.
77 pub fn scalarmult(secret_key: [secret_length]u8, public_key: [public_length]u8) ![shared_length]u8 {
78 pub fn scalarmult(secret_key: [secret_length]u8, public_key: [public_length]u8) Error![shared_length]u8 {
7879 const q = try Curve.fromBytes(public_key).clampedMul(secret_key);
7980 return q.toBytes();
8081 }
lib/std/crypto/aegis.zig+3-2
......@@ -8,6 +8,7 @@ const std = @import("std");
88const mem = std.mem;
99const assert = std.debug.assert;
1010const AesBlock = std.crypto.core.aes.Block;
11const Error = std.crypto.Error;
1112
1213const State128L = struct {
1314 blocks: [8]AesBlock,
......@@ -136,7 +137,7 @@ pub const Aegis128L = struct {
136137 /// ad: Associated Data
137138 /// npub: public nonce
138139 /// k: private key
139 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) !void {
140 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) Error!void {
140141 assert(c.len == m.len);
141142 var state = State128L.init(key, npub);
142143 var src: [32]u8 align(16) = undefined;
......@@ -298,7 +299,7 @@ pub const Aegis256 = struct {
298299 /// ad: Associated Data
299300 /// npub: public nonce
300301 /// k: private key
301 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) !void {
302 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) Error!void {
302303 assert(c.len == m.len);
303304 var state = State256.init(key, npub);
304305 var src: [16]u8 align(16) = undefined;
lib/std/crypto/aes_gcm.zig+2-1
......@@ -12,6 +12,7 @@ const debug = std.debug;
1212const Ghash = std.crypto.onetimeauth.Ghash;
1313const mem = std.mem;
1414const modes = crypto.core.modes;
15const Error = crypto.Error;
1516
1617pub const Aes128Gcm = AesGcm(crypto.core.aes.Aes128);
1718pub const Aes256Gcm = AesGcm(crypto.core.aes.Aes256);
......@@ -59,7 +60,7 @@ fn AesGcm(comptime Aes: anytype) type {
5960 }
6061 }
6162
62 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) !void {
63 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) Error!void {
6364 assert(c.len == m.len);
6465
6566 const aes = Aes.initEnc(key);
lib/std/crypto/aes_ocb.zig+2-1
......@@ -10,6 +10,7 @@ const aes = crypto.core.aes;
1010const assert = std.debug.assert;
1111const math = std.math;
1212const mem = std.mem;
13const Error = crypto.Error;
1314
1415pub const Aes128Ocb = AesOcb(aes.Aes128);
1516pub const Aes256Ocb = AesOcb(aes.Aes256);
......@@ -178,7 +179,7 @@ fn AesOcb(comptime Aes: anytype) type {
178179 /// ad: Associated Data
179180 /// npub: public nonce
180181 /// k: secret key
181 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) !void {
182 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) Error!void {
182183 assert(c.len == m.len);
183184
184185 const aes_enc_ctx = Aes.initEnc(key);
lib/std/crypto/bcrypt.zig+8-14
......@@ -11,7 +11,8 @@ const math = std.math;
1111const mem = std.mem;
1212const debug = std.debug;
1313const testing = std.testing;
14const utils = std.crypto.utils;
14const utils = crypto.utils;
15const Error = crypto.Error;
1516
1617const salt_length: usize = 16;
1718const salt_str_length: usize = 22;
......@@ -21,13 +22,6 @@ const ct_length: usize = 24;
2122/// Length (in bytes) of a password hash
2223pub const hash_length: usize = 60;
2324
24pub const BcryptError = error{
25 /// The hashed password cannot be decoded.
26 InvalidEncoding,
27 /// The hash is not valid for the given password.
28 InvalidPassword,
29};
30
3125const State = struct {
3226 sboxes: [4][256]u32 = [4][256]u32{
3327 .{ 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a },
......@@ -185,7 +179,7 @@ const Codec = struct {
185179 debug.assert(j == b64.len);
186180 }
187181
188 fn decode(bin: []u8, b64: []const u8) BcryptError!void {
182 fn decode(bin: []u8, b64: []const u8) Error!void {
189183 var i: usize = 0;
190184 var j: usize = 0;
191185 while (j < bin.len) {
......@@ -210,7 +204,7 @@ const Codec = struct {
210204 }
211205};
212206
213fn strHashInternal(password: []const u8, rounds_log: u6, salt: [salt_length]u8) BcryptError![hash_length]u8 {
207fn strHashInternal(password: []const u8, rounds_log: u6, salt: [salt_length]u8) Error![hash_length]u8 {
214208 var state = State{};
215209 var password_buf: [73]u8 = undefined;
216210 const trimmed_len = math.min(password.len, password_buf.len - 1);
......@@ -258,14 +252,14 @@ fn strHashInternal(password: []const u8, rounds_log: u6, salt: [salt_length]u8)
258252/// IMPORTANT: by design, bcrypt silently truncates passwords to 72 bytes.
259253/// If this is an issue for your application, hash the password first using a function such as SHA-512,
260254/// and then use the resulting hash as the password parameter for bcrypt.
261pub fn strHash(password: []const u8, rounds_log: u6) ![hash_length]u8 {
255pub fn strHash(password: []const u8, rounds_log: u6) Error![hash_length]u8 {
262256 var salt: [salt_length]u8 = undefined;
263257 crypto.random.bytes(&salt);
264258 return strHashInternal(password, rounds_log, salt);
265259}
266260
267261/// Verify that a previously computed hash is valid for a given password.
268pub fn strVerify(h: [hash_length]u8, password: []const u8) BcryptError!void {
262pub fn strVerify(h: [hash_length]u8, password: []const u8) Error!void {
269263 if (!mem.eql(u8, "$2", h[0..2])) return error.InvalidEncoding;
270264 if (h[3] != '$' or h[6] != '$') return error.InvalidEncoding;
271265 const rounds_log_str = h[4..][0..2];
......@@ -275,7 +269,7 @@ pub fn strVerify(h: [hash_length]u8, password: []const u8) BcryptError!void {
275269 const rounds_log = fmt.parseInt(u6, rounds_log_str[0..], 10) catch return error.InvalidEncoding;
276270 const wanted_s = try strHashInternal(password, rounds_log, salt);
277271 if (!mem.eql(u8, wanted_s[0..], h[0..])) {
278 return error.InvalidPassword;
272 return error.PasswordVerificationFailed;
279273 }
280274}
281275
......@@ -292,7 +286,7 @@ test "bcrypt codec" {
292286test "bcrypt" {
293287 const s = try strHash("password", 5);
294288 try strVerify(s, "password");
295 testing.expectError(error.InvalidPassword, strVerify(s, "invalid password"));
289 testing.expectError(error.PasswordVerificationFailed, strVerify(s, "invalid password"));
296290
297291 const long_s = try strHash("password" ** 100, 5);
298292 try strVerify(long_s, "password" ** 100);
lib/std/crypto/benchmark.zig+1
......@@ -202,6 +202,7 @@ pub fn benchmarkBatchSignatureVerification(comptime Signature: anytype, comptime
202202const aeads = [_]Crypto{
203203 Crypto{ .ty = crypto.aead.chacha_poly.ChaCha20Poly1305, .name = "chacha20Poly1305" },
204204 Crypto{ .ty = crypto.aead.chacha_poly.XChaCha20Poly1305, .name = "xchacha20Poly1305" },
205 Crypto{ .ty = crypto.aead.chacha_poly.XChaCha8Poly1305, .name = "xchacha8Poly1305" },
205206 Crypto{ .ty = crypto.aead.salsa_poly.XSalsa20Poly1305, .name = "xsalsa20Poly1305" },
206207 Crypto{ .ty = crypto.aead.Gimli, .name = "gimli-aead" },
207208 Crypto{ .ty = crypto.aead.aegis.Aegis128L, .name = "aegis-128l" },
lib/std/crypto/chacha20.zig+599-571
......@@ -13,287 +13,359 @@ const testing = std.testing;
1313const maxInt = math.maxInt;
1414const Vector = std.meta.Vector;
1515const Poly1305 = std.crypto.onetimeauth.Poly1305;
16const Error = std.crypto.Error;
17
18/// IETF-variant of the ChaCha20 stream cipher, as designed for TLS.
19pub const ChaCha20IETF = ChaChaIETF(20);
20
21/// IETF-variant of the ChaCha20 stream cipher, reduced to 12 rounds.
22/// Reduced-rounds versions are faster than the full-round version, but have a lower security margin.
23/// However, ChaCha is still believed to have a comfortable security even with only with 8 rounds.
24pub const ChaCha12IETF = ChaChaIETF(12);
25
26/// IETF-variant of the ChaCha20 stream cipher, reduced to 8 rounds.
27/// Reduced-rounds versions are faster than the full-round version, but have a lower security margin.
28/// However, ChaCha is still believed to have a comfortable security even with only with 8 rounds.
29pub const ChaCha8IETF = ChaChaIETF(8);
30
31/// Original ChaCha20 stream cipher.
32pub const ChaCha20With64BitNonce = ChaChaWith64BitNonce(20);
33
34/// Original ChaCha20 stream cipher, reduced to 12 rounds.
35/// Reduced-rounds versions are faster than the full-round version, but have a lower security margin.
36/// However, ChaCha is still believed to have a comfortable security even with only with 8 rounds.
37pub const ChaCha12With64BitNonce = ChaChaWith64BitNonce(12);
38
39/// Original ChaCha20 stream cipher, reduced to 8 rounds.
40/// Reduced-rounds versions are faster than the full-round version, but have a lower security margin.
41/// However, ChaCha is still believed to have a comfortable security even with only with 8 rounds.
42pub const ChaCha8With64BitNonce = ChaChaWith64BitNonce(8);
43
44/// XChaCha20 (nonce-extended version of the IETF ChaCha20 variant) stream cipher
45pub const XChaCha20IETF = XChaChaIETF(20);
46
47/// XChaCha20 (nonce-extended version of the IETF ChaCha20 variant) stream cipher, reduced to 12 rounds
48/// Reduced-rounds versions are faster than the full-round version, but have a lower security margin.
49/// However, ChaCha is still believed to have a comfortable security even with only with 8 rounds.
50pub const XChaCha12IETF = XChaChaIETF(12);
51
52/// XChaCha20 (nonce-extended version of the IETF ChaCha20 variant) stream cipher, reduced to 8 rounds
53/// Reduced-rounds versions are faster than the full-round version, but have a lower security margin.
54/// However, ChaCha is still believed to have a comfortable security even with only with 8 rounds.
55pub const XChaCha8IETF = XChaChaIETF(8);
56
57/// ChaCha20-Poly1305 authenticated cipher, as designed for TLS
58pub const ChaCha20Poly1305 = ChaChaPoly1305(20);
59
60/// ChaCha20-Poly1305 authenticated cipher, reduced to 12 rounds
61/// Reduced-rounds versions are faster than the full-round version, but have a lower security margin.
62/// However, ChaCha is still believed to have a comfortable security even with only with 8 rounds.
63pub const ChaCha12Poly1305 = ChaChaPoly1305(12);
64
65/// ChaCha20-Poly1305 authenticated cipher, reduced to 8 rounds
66/// Reduced-rounds versions are faster than the full-round version, but have a lower security margin.
67/// However, ChaCha is still believed to have a comfortable security even with only with 8 rounds.
68pub const ChaCha8Poly1305 = ChaChaPoly1305(8);
69
70/// XChaCha20-Poly1305 authenticated cipher
71pub const XChaCha20Poly1305 = XChaChaPoly1305(20);
72
73/// XChaCha20-Poly1305 authenticated cipher
74/// Reduced-rounds versions are faster than the full-round version, but have a lower security margin.
75/// However, ChaCha is still believed to have a comfortable security even with only with 8 rounds.
76pub const XChaCha12Poly1305 = XChaChaPoly1305(12);
77
78/// XChaCha20-Poly1305 authenticated cipher
79/// Reduced-rounds versions are faster than the full-round version, but have a lower security margin.
80/// However, ChaCha is still believed to have a comfortable security even with only with 8 rounds.
81pub const XChaCha8Poly1305 = XChaChaPoly1305(8);
1682
1783// Vectorized implementation of the core function
18const ChaCha20VecImpl = struct {
19 const Lane = Vector(4, u32);
20 const BlockVec = [4]Lane;
21
22 fn initContext(key: [8]u32, d: [4]u32) BlockVec {
23 const c = "expand 32-byte k";
24 const constant_le = comptime Lane{
25 mem.readIntLittle(u32, c[0..4]),
26 mem.readIntLittle(u32, c[4..8]),
27 mem.readIntLittle(u32, c[8..12]),
28 mem.readIntLittle(u32, c[12..16]),
29 };
30 return BlockVec{
31 constant_le,
32 Lane{ key[0], key[1], key[2], key[3] },
33 Lane{ key[4], key[5], key[6], key[7] },
34 Lane{ d[0], d[1], d[2], d[3] },
35 };
36 }
84fn ChaChaVecImpl(comptime rounds_nb: usize) type {
85 return struct {
86 const Lane = Vector(4, u32);
87 const BlockVec = [4]Lane;
88
89 fn initContext(key: [8]u32, d: [4]u32) BlockVec {
90 const c = "expand 32-byte k";
91 const constant_le = comptime Lane{
92 mem.readIntLittle(u32, c[0..4]),
93 mem.readIntLittle(u32, c[4..8]),
94 mem.readIntLittle(u32, c[8..12]),
95 mem.readIntLittle(u32, c[12..16]),
96 };
97 return BlockVec{
98 constant_le,
99 Lane{ key[0], key[1], key[2], key[3] },
100 Lane{ key[4], key[5], key[6], key[7] },
101 Lane{ d[0], d[1], d[2], d[3] },
102 };
103 }
37104
38 fn chacha20Core(x: *BlockVec, input: BlockVec) callconv(.Inline) void {
39 x.* = input;
40
41 var r: usize = 0;
42 while (r < 20) : (r += 2) {
43 x[0] +%= x[1];
44 x[3] ^= x[0];
45 x[3] = math.rotl(Lane, x[3], 16);
46
47 x[2] +%= x[3];
48 x[1] ^= x[2];
49 x[1] = math.rotl(Lane, x[1], 12);
50
51 x[0] +%= x[1];
52 x[3] ^= x[0];
53 x[0] = @shuffle(u32, x[0], undefined, [_]i32{ 3, 0, 1, 2 });
54 x[3] = math.rotl(Lane, x[3], 8);
55
56 x[2] +%= x[3];
57 x[3] = @shuffle(u32, x[3], undefined, [_]i32{ 2, 3, 0, 1 });
58 x[1] ^= x[2];
59 x[2] = @shuffle(u32, x[2], undefined, [_]i32{ 1, 2, 3, 0 });
60 x[1] = math.rotl(Lane, x[1], 7);
61
62 x[0] +%= x[1];
63 x[3] ^= x[0];
64 x[3] = math.rotl(Lane, x[3], 16);
65
66 x[2] +%= x[3];
67 x[1] ^= x[2];
68 x[1] = math.rotl(Lane, x[1], 12);
69
70 x[0] +%= x[1];
71 x[3] ^= x[0];
72 x[0] = @shuffle(u32, x[0], undefined, [_]i32{ 1, 2, 3, 0 });
73 x[3] = math.rotl(Lane, x[3], 8);
74
75 x[2] +%= x[3];
76 x[3] = @shuffle(u32, x[3], undefined, [_]i32{ 2, 3, 0, 1 });
77 x[1] ^= x[2];
78 x[2] = @shuffle(u32, x[2], undefined, [_]i32{ 3, 0, 1, 2 });
79 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 }
80148 }
81 }
82149
83 fn hashToBytes(out: *[64]u8, x: BlockVec) callconv(.Inline) void {
84 var i: usize = 0;
85 while (i < 4) : (i += 1) {
86 mem.writeIntLittle(u32, out[16 * i + 0 ..][0..4], x[i][0]);
87 mem.writeIntLittle(u32, out[16 * i + 4 ..][0..4], x[i][1]);
88 mem.writeIntLittle(u32, out[16 * i + 8 ..][0..4], x[i][2]);
89 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 }
90158 }
91 }
92159
93 fn contextFeedback(x: *BlockVec, ctx: BlockVec) callconv(.Inline) void {
94 x[0] +%= ctx[0];
95 x[1] +%= ctx[1];
96 x[2] +%= ctx[2];
97 x[3] +%= ctx[3];
98 }
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 }
99166
100 fn chacha20Xor(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) void {
101 var ctx = initContext(key, counter);
102 var x: BlockVec = undefined;
103 var buf: [64]u8 = undefined;
104 var i: usize = 0;
105 while (i + 64 <= in.len) : (i += 64) {
106 chacha20Core(x[0..], ctx);
107 contextFeedback(&x, ctx);
108 hashToBytes(buf[0..], x);
109
110 var xout = out[i..];
111 const xin = in[i..];
112 var j: usize = 0;
113 while (j < 64) : (j += 1) {
114 xout[j] = xin[j];
115 }
116 j = 0;
117 while (j < 64) : (j += 1) {
118 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;
119188 }
120 ctx[3][0] += 1;
121 }
122 if (i < in.len) {
123 chacha20Core(x[0..], ctx);
124 contextFeedback(&x, ctx);
125 hashToBytes(buf[0..], x);
126
127 var xout = out[i..];
128 const xin = in[i..];
129 var j: usize = 0;
130 while (j < in.len % 64) : (j += 1) {
131 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 }
132200 }
133201 }
134 }
135202
136 fn hchacha20(input: [16]u8, key: [32]u8) [32]u8 {
137 var c: [4]u32 = undefined;
138 for (c) |_, i| {
139 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;
140221 }
141 const ctx = initContext(keyToWords(key), c);
142 var x: BlockVec = undefined;
143 chacha20Core(x[0..], ctx);
144 var out: [32]u8 = undefined;
145 mem.writeIntLittle(u32, out[0..4], x[0][0]);
146 mem.writeIntLittle(u32, out[4..8], x[0][1]);
147 mem.writeIntLittle(u32, out[8..12], x[0][2]);
148 mem.writeIntLittle(u32, out[12..16], x[0][3]);
149 mem.writeIntLittle(u32, out[16..20], x[3][0]);
150 mem.writeIntLittle(u32, out[20..24], x[3][1]);
151 mem.writeIntLittle(u32, out[24..28], x[3][2]);
152 mem.writeIntLittle(u32, out[28..32], x[3][3]);
153 return out;
154 }
155};
222 };
223}
156224
157225// Non-vectorized implementation of the core function
158const ChaCha20NonVecImpl = struct {
159 const BlockVec = [16]u32;
160
161 fn initContext(key: [8]u32, d: [4]u32) BlockVec {
162 const c = "expand 32-byte k";
163 const constant_le = comptime [4]u32{
164 mem.readIntLittle(u32, c[0..4]),
165 mem.readIntLittle(u32, c[4..8]),
166 mem.readIntLittle(u32, c[8..12]),
167 mem.readIntLittle(u32, c[12..16]),
168 };
169 return BlockVec{
170 constant_le[0], constant_le[1], constant_le[2], constant_le[3],
171 key[0], key[1], key[2], key[3],
172 key[4], key[5], key[6], key[7],
173 d[0], d[1], d[2], d[3],
174 };
175 }
176
177 const QuarterRound = struct {
178 a: usize,
179 b: usize,
180 c: usize,
181 d: usize,
182 };
226fn ChaChaNonVecImpl(comptime rounds_nb: usize) type {
227 return struct {
228 const BlockVec = [16]u32;
229
230 fn initContext(key: [8]u32, d: [4]u32) BlockVec {
231 const c = "expand 32-byte k";
232 const constant_le = comptime [4]u32{
233 mem.readIntLittle(u32, c[0..4]),
234 mem.readIntLittle(u32, c[4..8]),
235 mem.readIntLittle(u32, c[8..12]),
236 mem.readIntLittle(u32, c[12..16]),
237 };
238 return BlockVec{
239 constant_le[0], constant_le[1], constant_le[2], constant_le[3],
240 key[0], key[1], key[2], key[3],
241 key[4], key[5], key[6], key[7],
242 d[0], d[1], d[2], d[3],
243 };
244 }
183245
184 fn Rp(a: usize, b: usize, c: usize, d: usize) QuarterRound {
185 return QuarterRound{
186 .a = a,
187 .b = b,
188 .c = c,
189 .d = d,
246 const QuarterRound = struct {
247 a: usize,
248 b: usize,
249 c: usize,
250 d: usize,
190251 };
191 }
192252
193 fn chacha20Core(x: *BlockVec, input: BlockVec) callconv(.Inline) void {
194 x.* = input;
195
196 const rounds = comptime [_]QuarterRound{
197 Rp(0, 4, 8, 12),
198 Rp(1, 5, 9, 13),
199 Rp(2, 6, 10, 14),
200 Rp(3, 7, 11, 15),
201 Rp(0, 5, 10, 15),
202 Rp(1, 6, 11, 12),
203 Rp(2, 7, 8, 13),
204 Rp(3, 4, 9, 14),
205 };
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 }
206261
207 comptime var j: usize = 0;
208 inline while (j < 20) : (j += 2) {
209 inline for (rounds) |r| {
210 x[r.a] +%= x[r.b];
211 x[r.d] = math.rotl(u32, x[r.d] ^ x[r.a], @as(u32, 16));
212 x[r.c] +%= x[r.d];
213 x[r.b] = math.rotl(u32, x[r.b] ^ x[r.c], @as(u32, 12));
214 x[r.a] +%= x[r.b];
215 x[r.d] = math.rotl(u32, x[r.d] ^ x[r.a], @as(u32, 8));
216 x[r.c] +%= x[r.d];
217 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 }
218288 }
219289 }
220 }
221290
222 fn hashToBytes(out: *[64]u8, x: BlockVec) callconv(.Inline) void {
223 var i: usize = 0;
224 while (i < 4) : (i += 1) {
225 mem.writeIntLittle(u32, out[16 * i + 0 ..][0..4], x[i * 4 + 0]);
226 mem.writeIntLittle(u32, out[16 * i + 4 ..][0..4], x[i * 4 + 1]);
227 mem.writeIntLittle(u32, out[16 * i + 8 ..][0..4], x[i * 4 + 2]);
228 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 }
229299 }
230 }
231300
232 fn contextFeedback(x: *BlockVec, ctx: BlockVec) callconv(.Inline) void {
233 var i: usize = 0;
234 while (i < 16) : (i += 1) {
235 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 }
236306 }
237 }
238307
239 fn chacha20Xor(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) void {
240 var ctx = initContext(key, counter);
241 var x: BlockVec = undefined;
242 var buf: [64]u8 = undefined;
243 var i: usize = 0;
244 while (i + 64 <= in.len) : (i += 64) {
245 chacha20Core(x[0..], ctx);
246 contextFeedback(&x, ctx);
247 hashToBytes(buf[0..], x);
248
249 var xout = out[i..];
250 const xin = in[i..];
251 var j: usize = 0;
252 while (j < 64) : (j += 1) {
253 xout[j] = xin[j];
254 }
255 j = 0;
256 while (j < 64) : (j += 1) {
257 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;
258329 }
259 ctx[12] += 1;
260 }
261 if (i < in.len) {
262 chacha20Core(x[0..], ctx);
263 contextFeedback(&x, ctx);
264 hashToBytes(buf[0..], x);
265
266 var xout = out[i..];
267 const xin = in[i..];
268 var j: usize = 0;
269 while (j < in.len % 64) : (j += 1) {
270 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 }
271341 }
272342 }
273 }
274343
275 fn hchacha20(input: [16]u8, key: [32]u8) [32]u8 {
276 var c: [4]u32 = undefined;
277 for (c) |_, i| {
278 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;
279362 }
280 const ctx = initContext(keyToWords(key), c);
281 var x: BlockVec = undefined;
282 chacha20Core(x[0..], ctx);
283 var out: [32]u8 = undefined;
284 mem.writeIntLittle(u32, out[0..4], x[0]);
285 mem.writeIntLittle(u32, out[4..8], x[1]);
286 mem.writeIntLittle(u32, out[8..12], x[2]);
287 mem.writeIntLittle(u32, out[12..16], x[3]);
288 mem.writeIntLittle(u32, out[16..20], x[12]);
289 mem.writeIntLittle(u32, out[20..24], x[13]);
290 mem.writeIntLittle(u32, out[24..28], x[14]);
291 mem.writeIntLittle(u32, out[28..32], x[15]);
292 return out;
293 }
294};
363 };
364}
295365
296const ChaCha20Impl = if (std.Target.current.cpu.arch == .x86_64) ChaCha20VecImpl else ChaCha20NonVecImpl;
366fn ChaChaImpl(comptime rounds_nb: usize) type {
367 return if (std.Target.current.cpu.arch == .x86_64) ChaChaVecImpl(rounds_nb) else ChaChaNonVecImpl(rounds_nb);
368}
297369
298370fn keyToWords(key: [32]u8) [8]u32 {
299371 var k: [8]u32 = undefined;
......@@ -304,68 +376,239 @@ fn keyToWords(key: [32]u8) [8]u32 {
304376 return k;
305377}
306378
307/// ChaCha20 avoids the possibility of timing attacks, as there are no branches
308/// on secret key data.
309///
310/// in and out should be the same length.
311/// counter should generally be 0 or 1
312///
313/// ChaCha20 is self-reversing. To decrypt just run the cipher with the same
314/// counter, nonce, and key.
315pub const ChaCha20IETF = struct {
316 pub fn xor(out: []u8, in: []const u8, counter: u32, key: [32]u8, nonce: [12]u8) void {
317 assert(in.len == out.len);
318 assert((in.len >> 6) + counter <= maxInt(u32));
319
320 var c: [4]u32 = undefined;
321 c[0] = counter;
322 c[1] = mem.readIntLittle(u32, nonce[0..4]);
323 c[2] = mem.readIntLittle(u32, nonce[4..8]);
324 c[3] = mem.readIntLittle(u32, nonce[8..12]);
325 ChaCha20Impl.chacha20Xor(out, in, keyToWords(key), c);
326 }
327};
328
329/// This is the original ChaCha20 before RFC 7539, which recommends using the
330/// orgininal version on applications such as disk or file encryption that might
331/// exceed the 256 GiB limit of the 96-bit nonce version.
332pub const ChaCha20With64BitNonce = struct {
333 pub fn xor(out: []u8, in: []const u8, counter: u64, key: [32]u8, nonce: [8]u8) void {
334 assert(in.len == out.len);
335 assert(counter +% (in.len >> 6) >= counter);
336
337 var cursor: usize = 0;
338 const k = keyToWords(key);
339 var c: [4]u32 = undefined;
340 c[0] = @truncate(u32, counter);
341 c[1] = @truncate(u32, counter >> 32);
342 c[2] = mem.readIntLittle(u32, nonce[0..4]);
343 c[3] = mem.readIntLittle(u32, nonce[4..8]);
344
345 const block_length = (1 << 6);
346 // The full block size is greater than the address space on a 32bit machine
347 const big_block = if (@sizeOf(usize) > 4) (block_length << 32) else maxInt(usize);
348
349 // first partial big block
350 if (((@intCast(u64, maxInt(u32) - @truncate(u32, counter)) + 1) << 6) < in.len) {
351 ChaCha20Impl.chacha20Xor(out[cursor..big_block], in[cursor..big_block], k, c);
352 cursor = big_block - cursor;
353 c[1] += 1;
354 if (comptime @sizeOf(usize) > 4) {
355 // A big block is giant: 256 GiB, but we can avoid this limitation
356 var remaining_blocks: u32 = @intCast(u32, (in.len / big_block));
357 var i: u32 = 0;
358 while (remaining_blocks > 0) : (remaining_blocks -= 1) {
359 ChaCha20Impl.chacha20Xor(out[cursor .. cursor + big_block], in[cursor .. cursor + big_block], k, c);
360 c[1] += 1; // upper 32-bit of counter, generic chacha20Xor() doesn't know about this.
361 cursor += big_block;
379fn extend(key: [32]u8, nonce: [24]u8, comptime rounds_nb: usize) struct { key: [32]u8, nonce: [12]u8 } {
380 var subnonce: [12]u8 = undefined;
381 mem.set(u8, subnonce[0..4], 0);
382 mem.copy(u8, subnonce[4..], nonce[16..24]);
383 return .{
384 .key = ChaChaImpl(rounds_nb).hchacha20(nonce[0..16].*, key),
385 .nonce = subnonce,
386 };
387}
388
389fn ChaChaIETF(comptime rounds_nb: usize) type {
390 return struct {
391 /// Nonce length in bytes.
392 pub const nonce_length = 12;
393 /// Key length in bytes.
394 pub const key_length = 32;
395
396 /// Add the output of the ChaCha20 stream cipher to `in` and stores the result into `out`.
397 /// WARNING: This function doesn't provide authenticated encryption.
398 /// Using the AEAD or one of the `box` versions is usually preferred.
399 pub fn xor(out: []u8, in: []const u8, counter: u32, key: [key_length]u8, nonce: [nonce_length]u8) void {
400 assert(in.len == out.len);
401 assert(in.len / 64 <= (1 << 32 - 1) - counter);
402
403 var d: [4]u32 = undefined;
404 d[0] = counter;
405 d[1] = mem.readIntLittle(u32, nonce[0..4]);
406 d[2] = mem.readIntLittle(u32, nonce[4..8]);
407 d[3] = mem.readIntLittle(u32, nonce[8..12]);
408 ChaChaImpl(rounds_nb).chacha20Xor(out, in, keyToWords(key), d);
409 }
410 };
411}
412
413fn ChaChaWith64BitNonce(comptime rounds_nb: usize) type {
414 return struct {
415 /// Nonce length in bytes.
416 pub const nonce_length = 8;
417 /// Key length in bytes.
418 pub const key_length = 32;
419
420 /// Add the output of the ChaCha20 stream cipher to `in` and stores the result into `out`.
421 /// WARNING: This function doesn't provide authenticated encryption.
422 /// Using the AEAD or one of the `box` versions is usually preferred.
423 pub fn xor(out: []u8, in: []const u8, counter: u64, key: [key_length]u8, nonce: [nonce_length]u8) void {
424 assert(in.len == out.len);
425 assert(in.len / 64 <= (1 << 64 - 1) - counter);
426
427 var cursor: usize = 0;
428 const k = keyToWords(key);
429 var c: [4]u32 = undefined;
430 c[0] = @truncate(u32, counter);
431 c[1] = @truncate(u32, counter >> 32);
432 c[2] = mem.readIntLittle(u32, nonce[0..4]);
433 c[3] = mem.readIntLittle(u32, nonce[4..8]);
434
435 const block_length = (1 << 6);
436 // The full block size is greater than the address space on a 32bit machine
437 const big_block = if (@sizeOf(usize) > 4) (block_length << 32) else maxInt(usize);
438
439 // first partial big block
440 if (((@intCast(u64, maxInt(u32) - @truncate(u32, counter)) + 1) << 6) < in.len) {
441 ChaChaImpl(rounds_nb).chacha20Xor(out[cursor..big_block], in[cursor..big_block], k, c);
442 cursor = big_block - cursor;
443 c[1] += 1;
444 if (comptime @sizeOf(usize) > 4) {
445 // A big block is giant: 256 GiB, but we can avoid this limitation
446 var remaining_blocks: u32 = @intCast(u32, (in.len / big_block));
447 var i: u32 = 0;
448 while (remaining_blocks > 0) : (remaining_blocks -= 1) {
449 ChaChaImpl(rounds_nb).chacha20Xor(out[cursor .. cursor + big_block], in[cursor .. cursor + big_block], k, c);
450 c[1] += 1; // upper 32-bit of counter, generic chacha20Xor() doesn't know about this.
451 cursor += big_block;
452 }
362453 }
363454 }
455 ChaChaImpl(rounds_nb).chacha20Xor(out[cursor..], in[cursor..], k, c);
456 }
457 };
458}
459
460fn XChaChaIETF(comptime rounds_nb: usize) type {
461 return struct {
462 /// Nonce length in bytes.
463 pub const nonce_length = 24;
464 /// Key length in bytes.
465 pub const key_length = 32;
466
467 /// Add the output of the XChaCha20 stream cipher to `in` and stores the result into `out`.
468 /// WARNING: This function doesn't provide authenticated encryption.
469 /// Using the AEAD or one of the `box` versions is usually preferred.
470 pub fn xor(out: []u8, in: []const u8, counter: u32, key: [key_length]u8, nonce: [nonce_length]u8) void {
471 const extended = extend(key, nonce, rounds_nb);
472 ChaChaIETF(rounds_nb).xor(out, in, counter, extended.key, extended.nonce);
473 }
474 };
475}
476
477fn ChaChaPoly1305(comptime rounds_nb: usize) type {
478 return struct {
479 pub const tag_length = 16;
480 pub const nonce_length = 12;
481 pub const key_length = 32;
482
483 /// c: ciphertext: output buffer should be of size m.len
484 /// tag: authentication tag: output MAC
485 /// m: message
486 /// ad: Associated Data
487 /// npub: public nonce
488 /// k: private key
489 pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
490 assert(c.len == m.len);
491
492 var polyKey = [_]u8{0} ** 32;
493 ChaChaIETF(rounds_nb).xor(polyKey[0..], polyKey[0..], 0, k, npub);
494
495 ChaChaIETF(rounds_nb).xor(c[0..m.len], m, 1, k, npub);
496
497 var mac = Poly1305.init(polyKey[0..]);
498 mac.update(ad);
499 if (ad.len % 16 != 0) {
500 const zeros = [_]u8{0} ** 16;
501 const padding = 16 - (ad.len % 16);
502 mac.update(zeros[0..padding]);
503 }
504 mac.update(c[0..m.len]);
505 if (m.len % 16 != 0) {
506 const zeros = [_]u8{0} ** 16;
507 const padding = 16 - (m.len % 16);
508 mac.update(zeros[0..padding]);
509 }
510 var lens: [16]u8 = undefined;
511 mem.writeIntLittle(u64, lens[0..8], ad.len);
512 mem.writeIntLittle(u64, lens[8..16], m.len);
513 mac.update(lens[0..]);
514 mac.final(tag);
515 }
516
517 /// m: message: output buffer should be of size c.len
518 /// c: ciphertext
519 /// tag: authentication tag
520 /// ad: Associated Data
521 /// npub: public nonce
522 /// k: private key
523 /// NOTE: the check of the authentication tag is currently not done in constant time
524 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) Error!void {
525 assert(c.len == m.len);
526
527 var polyKey = [_]u8{0} ** 32;
528 ChaChaIETF(rounds_nb).xor(polyKey[0..], polyKey[0..], 0, k, npub);
529
530 var mac = Poly1305.init(polyKey[0..]);
531
532 mac.update(ad);
533 if (ad.len % 16 != 0) {
534 const zeros = [_]u8{0} ** 16;
535 const padding = 16 - (ad.len % 16);
536 mac.update(zeros[0..padding]);
537 }
538 mac.update(c);
539 if (c.len % 16 != 0) {
540 const zeros = [_]u8{0} ** 16;
541 const padding = 16 - (c.len % 16);
542 mac.update(zeros[0..padding]);
543 }
544 var lens: [16]u8 = undefined;
545 mem.writeIntLittle(u64, lens[0..8], ad.len);
546 mem.writeIntLittle(u64, lens[8..16], c.len);
547 mac.update(lens[0..]);
548 var computedTag: [16]u8 = undefined;
549 mac.final(computedTag[0..]);
550
551 var acc: u8 = 0;
552 for (computedTag) |_, i| {
553 acc |= computedTag[i] ^ tag[i];
554 }
555 if (acc != 0) {
556 return error.AuthenticationFailed;
557 }
558 ChaChaIETF(rounds_nb).xor(m[0..c.len], c, 1, k, npub);
559 }
560 };
561}
562
563fn XChaChaPoly1305(comptime rounds_nb: usize) type {
564 return struct {
565 pub const tag_length = 16;
566 pub const nonce_length = 24;
567 pub const key_length = 32;
568
569 /// c: ciphertext: output buffer should be of size m.len
570 /// tag: authentication tag: output MAC
571 /// m: message
572 /// ad: Associated Data
573 /// npub: public nonce
574 /// k: private key
575 pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
576 const extended = extend(k, npub, rounds_nb);
577 return ChaChaPoly1305(rounds_nb).encrypt(c, tag, m, ad, extended.nonce, extended.key);
364578 }
365579
366 ChaCha20Impl.chacha20Xor(out[cursor..], in[cursor..], k, c);
580 /// m: message: output buffer should be of size c.len
581 /// c: ciphertext
582 /// tag: authentication tag
583 /// ad: Associated Data
584 /// npub: public nonce
585 /// k: private key
586 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) Error!void {
587 const extended = extend(k, npub, rounds_nb);
588 return ChaChaPoly1305(rounds_nb).decrypt(m, c, tag, ad, extended.nonce, extended.key);
589 }
590 };
591}
592
593test "chacha20 AEAD API" {
594 const aeads = [_]type{ ChaCha20Poly1305, XChaCha20Poly1305 };
595 const m = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";
596 const ad = "Additional data";
597
598 inline for (aeads) |aead| {
599 const key = [_]u8{69} ** aead.key_length;
600 const nonce = [_]u8{42} ** aead.nonce_length;
601 var c: [m.len]u8 = undefined;
602 var tag: [aead.tag_length]u8 = undefined;
603 var out: [m.len]u8 = undefined;
604
605 aead.encrypt(c[0..], tag[0..], m, ad, nonce, key);
606 try aead.decrypt(out[0..], c[0..], tag, ad[0..], nonce, key);
607 testing.expectEqualSlices(u8, out[0..], m);
608 c[0] += 1;
609 testing.expectError(error.AuthenticationFailed, aead.decrypt(out[0..], c[0..], tag, ad[0..], nonce, key));
367610 }
368};
611}
369612
370613// https://tools.ietf.org/html/rfc7539#section-2.4.2
371614test "crypto.chacha20 test vector sunscreen" {
......@@ -386,7 +629,7 @@ test "crypto.chacha20 test vector sunscreen" {
386629 0xb4, 0x0b, 0x8e, 0xed, 0xf2, 0x78, 0x5e, 0x42,
387630 0x87, 0x4d,
388631 };
389 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.";
390633 var result: [114]u8 = undefined;
391634 const key = [_]u8{
392635 0, 1, 2, 3, 4, 5, 6, 7,
......@@ -400,13 +643,12 @@ test "crypto.chacha20 test vector sunscreen" {
400643 0, 0, 0, 0,
401644 };
402645
403 ChaCha20IETF.xor(result[0..], input[0..], 1, key, nonce);
646 ChaCha20IETF.xor(result[0..], m[0..], 1, key, nonce);
404647 testing.expectEqualSlices(u8, &expected_result, &result);
405648
406 // Chacha20 is self-reversing.
407 var plaintext: [114]u8 = undefined;
408 ChaCha20IETF.xor(plaintext[0..], result[0..], 1, key, nonce);
409 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);
410652}
411653
412654// https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7
......@@ -421,7 +663,7 @@ test "crypto.chacha20 test vector 1" {
421663 0x6a, 0x43, 0xb8, 0xf4, 0x15, 0x18, 0xa1, 0x1c,
422664 0xc3, 0x87, 0xb6, 0x69, 0xb2, 0xee, 0x65, 0x86,
423665 };
424 const input = [_]u8{
666 const m = [_]u8{
425667 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
426668 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
427669 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
......@@ -440,7 +682,7 @@ test "crypto.chacha20 test vector 1" {
440682 };
441683 const nonce = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0 };
442684
443 ChaCha20With64BitNonce.xor(result[0..], input[0..], 0, key, nonce);
685 ChaCha20With64BitNonce.xor(result[0..], m[0..], 0, key, nonce);
444686 testing.expectEqualSlices(u8, &expected_result, &result);
445687}
446688
......@@ -455,7 +697,7 @@ test "crypto.chacha20 test vector 2" {
455697 0x53, 0xd7, 0x92, 0xb1, 0xc4, 0x3f, 0xea, 0x81,
456698 0x7e, 0x9a, 0xd2, 0x75, 0xae, 0x54, 0x69, 0x63,
457699 };
458 const input = [_]u8{
700 const m = [_]u8{
459701 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
460702 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
461703 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
......@@ -474,7 +716,7 @@ test "crypto.chacha20 test vector 2" {
474716 };
475717 const nonce = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0 };
476718
477 ChaCha20With64BitNonce.xor(result[0..], input[0..], 0, key, nonce);
719 ChaCha20With64BitNonce.xor(result[0..], m[0..], 0, key, nonce);
478720 testing.expectEqualSlices(u8, &expected_result, &result);
479721}
480722
......@@ -489,7 +731,7 @@ test "crypto.chacha20 test vector 3" {
489731 0x52, 0x77, 0x06, 0x2e, 0xb7, 0xa0, 0x43, 0x3e,
490732 0x44, 0x5f, 0x41, 0xe3,
491733 };
492 const input = [_]u8{
734 const m = [_]u8{
493735 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
494736 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
495737 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
......@@ -508,7 +750,7 @@ test "crypto.chacha20 test vector 3" {
508750 };
509751 const nonce = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 1 };
510752
511 ChaCha20With64BitNonce.xor(result[0..], input[0..], 0, key, nonce);
753 ChaCha20With64BitNonce.xor(result[0..], m[0..], 0, key, nonce);
512754 testing.expectEqualSlices(u8, &expected_result, &result);
513755}
514756
......@@ -523,7 +765,7 @@ test "crypto.chacha20 test vector 4" {
523765 0x5d, 0xdc, 0x49, 0x7a, 0x0b, 0x46, 0x6e, 0x7d,
524766 0x6b, 0xbd, 0xb0, 0x04, 0x1b, 0x2f, 0x58, 0x6b,
525767 };
526 const input = [_]u8{
768 const m = [_]u8{
527769 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
528770 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
529771 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
......@@ -542,7 +784,7 @@ test "crypto.chacha20 test vector 4" {
542784 };
543785 const nonce = [_]u8{ 1, 0, 0, 0, 0, 0, 0, 0 };
544786
545 ChaCha20With64BitNonce.xor(result[0..], input[0..], 0, key, nonce);
787 ChaCha20With64BitNonce.xor(result[0..], m[0..], 0, key, nonce);
546788 testing.expectEqualSlices(u8, &expected_result, &result);
547789}
548790
......@@ -584,7 +826,7 @@ test "crypto.chacha20 test vector 5" {
584826 0x87, 0x46, 0xd4, 0x52, 0x4d, 0x38, 0x40, 0x7a,
585827 0x6d, 0xeb, 0x3a, 0xb7, 0x8f, 0xab, 0x78, 0xc9,
586828 };
587 const input = [_]u8{
829 const m = [_]u8{
588830 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
589831 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
590832 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
......@@ -614,147 +856,14 @@ test "crypto.chacha20 test vector 5" {
614856 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
615857 };
616858
617 ChaCha20With64BitNonce.xor(result[0..], input[0..], 0, key, nonce);
859 ChaCha20With64BitNonce.xor(result[0..], m[0..], 0, key, nonce);
618860 testing.expectEqualSlices(u8, &expected_result, &result);
619861}
620862
621pub const chacha20poly1305_tag_length = 16;
622
623fn chacha20poly1305SealDetached(ciphertext: []u8, tag: *[chacha20poly1305_tag_length]u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) void {
624 assert(ciphertext.len == plaintext.len);
625
626 // derive poly1305 key
627 var polyKey = [_]u8{0} ** 32;
628 ChaCha20IETF.xor(polyKey[0..], polyKey[0..], 0, key, nonce);
629
630 // encrypt plaintext
631 ChaCha20IETF.xor(ciphertext[0..plaintext.len], plaintext, 1, key, nonce);
632
633 // construct mac
634 var mac = Poly1305.init(polyKey[0..]);
635 mac.update(data);
636 if (data.len % 16 != 0) {
637 const zeros = [_]u8{0} ** 16;
638 const padding = 16 - (data.len % 16);
639 mac.update(zeros[0..padding]);
640 }
641 mac.update(ciphertext[0..plaintext.len]);
642 if (plaintext.len % 16 != 0) {
643 const zeros = [_]u8{0} ** 16;
644 const padding = 16 - (plaintext.len % 16);
645 mac.update(zeros[0..padding]);
646 }
647 var lens: [16]u8 = undefined;
648 mem.writeIntLittle(u64, lens[0..8], data.len);
649 mem.writeIntLittle(u64, lens[8..16], plaintext.len);
650 mac.update(lens[0..]);
651 mac.final(tag);
652}
653
654fn chacha20poly1305Seal(ciphertextAndTag: []u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) void {
655 return chacha20poly1305SealDetached(ciphertextAndTag[0..plaintext.len], ciphertextAndTag[plaintext.len..][0..chacha20poly1305_tag_length], plaintext, data, key, nonce);
656}
657
658/// Verifies and decrypts an authenticated message produced by chacha20poly1305SealDetached.
659fn chacha20poly1305OpenDetached(dst: []u8, ciphertext: []const u8, tag: *const [chacha20poly1305_tag_length]u8, data: []const u8, key: [32]u8, nonce: [12]u8) !void {
660 // split ciphertext and tag
661 assert(dst.len == ciphertext.len);
662
663 // derive poly1305 key
664 var polyKey = [_]u8{0} ** 32;
665 ChaCha20IETF.xor(polyKey[0..], polyKey[0..], 0, key, nonce);
666
667 // construct mac
668 var mac = Poly1305.init(polyKey[0..]);
669
670 mac.update(data);
671 if (data.len % 16 != 0) {
672 const zeros = [_]u8{0} ** 16;
673 const padding = 16 - (data.len % 16);
674 mac.update(zeros[0..padding]);
675 }
676 mac.update(ciphertext);
677 if (ciphertext.len % 16 != 0) {
678 const zeros = [_]u8{0} ** 16;
679 const padding = 16 - (ciphertext.len % 16);
680 mac.update(zeros[0..padding]);
681 }
682 var lens: [16]u8 = undefined;
683 mem.writeIntLittle(u64, lens[0..8], data.len);
684 mem.writeIntLittle(u64, lens[8..16], ciphertext.len);
685 mac.update(lens[0..]);
686 var computedTag: [16]u8 = undefined;
687 mac.final(computedTag[0..]);
688
689 // verify mac in constant time
690 // TODO: we can't currently guarantee that this will run in constant time.
691 // See https://github.com/ziglang/zig/issues/1776
692 var acc: u8 = 0;
693 for (computedTag) |_, i| {
694 acc |= computedTag[i] ^ tag[i];
695 }
696 if (acc != 0) {
697 return error.AuthenticationFailed;
698 }
699
700 // decrypt ciphertext
701 ChaCha20IETF.xor(dst[0..ciphertext.len], ciphertext, 1, key, nonce);
702}
703
704/// Verifies and decrypts an authenticated message produced by chacha20poly1305Seal.
705fn chacha20poly1305Open(dst: []u8, ciphertextAndTag: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) !void {
706 if (ciphertextAndTag.len < chacha20poly1305_tag_length) {
707 return error.InvalidMessage;
708 }
709 const ciphertextLen = ciphertextAndTag.len - chacha20poly1305_tag_length;
710 return try chacha20poly1305OpenDetached(dst, ciphertextAndTag[0..ciphertextLen], ciphertextAndTag[ciphertextLen..][0..chacha20poly1305_tag_length], data, key, nonce);
711}
712
713fn extend(key: [32]u8, nonce: [24]u8) struct { key: [32]u8, nonce: [12]u8 } {
714 var subnonce: [12]u8 = undefined;
715 mem.set(u8, subnonce[0..4], 0);
716 mem.copy(u8, subnonce[4..], nonce[16..24]);
717 return .{
718 .key = ChaCha20Impl.hchacha20(nonce[0..16].*, key),
719 .nonce = subnonce,
720 };
721}
722
723pub const XChaCha20IETF = struct {
724 pub fn xor(out: []u8, in: []const u8, counter: u32, key: [32]u8, nonce: [24]u8) void {
725 const extended = extend(key, nonce);
726 ChaCha20IETF.xor(out, in, counter, extended.key, extended.nonce);
727 }
728};
729
730pub const xchacha20poly1305_tag_length = 16;
731
732fn xchacha20poly1305SealDetached(ciphertext: []u8, tag: *[chacha20poly1305_tag_length]u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [24]u8) void {
733 const extended = extend(key, nonce);
734 return chacha20poly1305SealDetached(ciphertext, tag, plaintext, data, extended.key, extended.nonce);
735}
736
737fn xchacha20poly1305Seal(ciphertextAndTag: []u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [24]u8) void {
738 const extended = extend(key, nonce);
739 return chacha20poly1305Seal(ciphertextAndTag, plaintext, data, extended.key, extended.nonce);
740}
741
742/// Verifies and decrypts an authenticated message produced by xchacha20poly1305SealDetached.
743fn xchacha20poly1305OpenDetached(plaintext: []u8, ciphertext: []const u8, tag: *const [chacha20poly1305_tag_length]u8, data: []const u8, key: [32]u8, nonce: [24]u8) !void {
744 const extended = extend(key, nonce);
745 return try chacha20poly1305OpenDetached(plaintext, ciphertext, tag, data, extended.key, extended.nonce);
746}
747
748/// Verifies and decrypts an authenticated message produced by xchacha20poly1305Seal.
749fn xchacha20poly1305Open(ciphertextAndTag: []u8, msgAndTag: []const u8, data: []const u8, key: [32]u8, nonce: [24]u8) !void {
750 const extended = extend(key, nonce);
751 return try chacha20poly1305Open(ciphertextAndTag, msgAndTag, data, extended.key, extended.nonce);
752}
753
754863test "seal" {
755864 {
756 const plaintext = "";
757 const data = "";
865 const m = "";
866 const ad = "";
758867 const key = [_]u8{
759868 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
760869 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
......@@ -763,11 +872,11 @@ test "seal" {
763872 const exp_out = [_]u8{ 0xa0, 0x78, 0x4d, 0x7a, 0x47, 0x16, 0xf3, 0xfe, 0xb4, 0xf6, 0x4e, 0x7f, 0x4b, 0x39, 0xbf, 0x4 };
764873
765874 var out: [exp_out.len]u8 = undefined;
766 chacha20poly1305Seal(out[0..], plaintext, data, key, nonce);
875 ChaCha20Poly1305.encrypt(out[0..m.len], out[m.len..], m, ad, nonce, key);
767876 testing.expectEqualSlices(u8, exp_out[0..], out[0..]);
768877 }
769878 {
770 const plaintext = [_]u8{
879 const m = [_]u8{
771880 0x4c, 0x61, 0x64, 0x69, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x47, 0x65, 0x6e, 0x74, 0x6c,
772881 0x65, 0x6d, 0x65, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x61, 0x73,
773882 0x73, 0x20, 0x6f, 0x66, 0x20, 0x27, 0x39, 0x39, 0x3a, 0x20, 0x49, 0x66, 0x20, 0x49, 0x20, 0x63,
......@@ -777,7 +886,7 @@ test "seal" {
777886 0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69,
778887 0x74, 0x2e,
779888 };
780 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 };
781890 const key = [_]u8{
782891 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
783892 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
......@@ -796,15 +905,15 @@ test "seal" {
796905 };
797906
798907 var out: [exp_out.len]u8 = undefined;
799 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);
800909 testing.expectEqualSlices(u8, exp_out[0..], out[0..]);
801910 }
802911}
803912
804913test "open" {
805914 {
806 const ciphertext = [_]u8{ 0xa0, 0x78, 0x4d, 0x7a, 0x47, 0x16, 0xf3, 0xfe, 0xb4, 0xf6, 0x4e, 0x7f, 0x4b, 0x39, 0xbf, 0x4 };
807 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 = "";
808917 const key = [_]u8{
809918 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
810919 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
......@@ -813,11 +922,11 @@ test "open" {
813922 const exp_out = "";
814923
815924 var out: [exp_out.len]u8 = undefined;
816 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);
817926 testing.expectEqualSlices(u8, exp_out[0..], out[0..]);
818927 }
819928 {
820 const ciphertext = [_]u8{
929 const c = [_]u8{
821930 0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb, 0x7b, 0x86, 0xaf, 0xbc, 0x53, 0xef, 0x7e, 0xc2,
822931 0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x8, 0xfe, 0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6,
823932 0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67, 0x12, 0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b,
......@@ -828,7 +937,7 @@ test "open" {
828937 0x61, 0x16, 0x1a, 0xe1, 0xb, 0x59, 0x4f, 0x9, 0xe2, 0x6a, 0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60,
829938 0x6, 0x91,
830939 };
831 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 };
832941 const key = [_]u8{
833942 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
834943 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
......@@ -846,126 +955,45 @@ test "open" {
846955 };
847956
848957 var out: [exp_out.len]u8 = undefined;
849 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);
850959 testing.expectEqualSlices(u8, exp_out[0..], out[0..]);
851960
852961 // corrupting the ciphertext, data, key, or nonce should cause a failure
853 var bad_ciphertext = ciphertext;
854 bad_ciphertext[0] ^= 1;
855 testing.expectError(error.AuthenticationFailed, chacha20poly1305Open(out[0..], bad_ciphertext[0..], data[0..], key, nonce));
856 var bad_data = data;
857 bad_data[0] ^= 1;
858 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));
859968 var bad_key = key;
860969 bad_key[0] ^= 1;
861 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));
862971 var bad_nonce = nonce;
863972 bad_nonce[0] ^= 1;
864 testing.expectError(error.AuthenticationFailed, chacha20poly1305Open(out[0..], ciphertext[0..], data[0..], key, bad_nonce));
865
866 // a short ciphertext should result in a different error
867 testing.expectError(error.InvalidMessage, 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));
868974 }
869975}
870976
871977test "crypto.xchacha20" {
872978 const key = [_]u8{69} ** 32;
873979 const nonce = [_]u8{42} ** 24;
874 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.";
875981 {
876 var ciphertext: [input.len]u8 = undefined;
877 XChaCha20IETF.xor(ciphertext[0..], input[0..], 0, key, nonce);
878 var buf: [2 * ciphertext.len]u8 = undefined;
879 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");
880986 }
881987 {
882 const data = "Additional data";
883 var ciphertext: [input.len + xchacha20poly1305_tag_length]u8 = undefined;
884 xchacha20poly1305Seal(ciphertext[0..], input, data, key, nonce);
885 var out: [input.len]u8 = undefined;
886 try xchacha20poly1305Open(out[0..], ciphertext[0..], data, key, nonce);
887 var buf: [2 * ciphertext.len]u8 = undefined;
888 testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&ciphertext)}), "994D2DD32333F48E53650C02C7A2ABB8E018B0836D7175AEC779F52E961780768F815C58F1AA52D211498DB89B9216763F569C9433A6BBFCEFB4D4A49387A4C5207FBB3B5A92B5941294DF30588C6740D39DC16FA1F0E634F7246CF7CDCB978E44347D89381B7A74EB7084F754B90BDE9AAF5A94B8F2A85EFD0B50692AE2D425E234");
889 testing.expectEqualSlices(u8, out[0..], input);
890 ciphertext[0] += 1;
891 testing.expectError(error.AuthenticationFailed, xchacha20poly1305Open(out[0..], ciphertext[0..], data, key, nonce));
892 }
893}
894
895pub const Chacha20Poly1305 = struct {
896 pub const tag_length = 16;
897 pub const nonce_length = 12;
898 pub const key_length = 32;
899
900 /// c: ciphertext: output buffer should be of size m.len
901 /// tag: authentication tag: output MAC
902 /// m: message
903 /// ad: Associated Data
904 /// npub: public nonce
905 /// k: private key
906 pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
907 assert(c.len == m.len);
908 return chacha20poly1305SealDetached(c, tag, m, ad, k, npub);
909 }
910
911 /// m: message: output buffer should be of size c.len
912 /// c: ciphertext
913 /// tag: authentication tag
914 /// ad: Associated Data
915 /// npub: public nonce
916 /// k: private key
917 /// NOTE: the check of the authentication tag is currently not done in constant time
918 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) !void {
919 assert(c.len == m.len);
920 return try chacha20poly1305OpenDetached(m, c, tag[0..], ad, k, npub);
921 }
922};
923
924pub const XChacha20Poly1305 = struct {
925 pub const tag_length = 16;
926 pub const nonce_length = 24;
927 pub const key_length = 32;
928
929 /// c: ciphertext: output buffer should be of size m.len
930 /// tag: authentication tag: output MAC
931 /// m: message
932 /// ad: Associated Data
933 /// npub: public nonce
934 /// k: private key
935 pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
936 assert(c.len == m.len);
937 return xchacha20poly1305SealDetached(c, tag, m, ad, k, npub);
938 }
939
940 /// m: message: output buffer should be of size c.len
941 /// c: ciphertext
942 /// tag: authentication tag
943 /// ad: Associated Data
944 /// npub: public nonce
945 /// k: private key
946 /// NOTE: the check of the authentication tag is currently not done in constant time
947 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) !void {
948 assert(c.len == m.len);
949 return try xchacha20poly1305OpenDetached(m, c, tag[0..], ad, k, npub);
950 }
951};
952
953test "chacha20 AEAD API" {
954 const aeads = [_]type{ Chacha20Poly1305, XChacha20Poly1305 };
955 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.";
956 const data = "Additional data";
957
958 inline for (aeads) |aead| {
959 const key = [_]u8{69} ** aead.key_length;
960 const nonce = [_]u8{42} ** aead.nonce_length;
961 var ciphertext: [input.len]u8 = undefined;
962 var tag: [aead.tag_length]u8 = undefined;
963 var out: [input.len]u8 = undefined;
964
965 aead.encrypt(ciphertext[0..], tag[0..], input, data, nonce, key);
966 try aead.decrypt(out[0..], ciphertext[0..], tag, data[0..], nonce, key);
967 testing.expectEqualSlices(u8, out[0..], input);
968 ciphertext[0] += 1;
969 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));
970998 }
971999}
lib/std/crypto/error.zig created+34
......@@ -0,0 +1,34 @@
1pub const Error = error{
2 /// MAC verification failed - The tag doesn't verify for the given ciphertext and secret key
3 AuthenticationFailed,
4
5 /// The requested output length is too long for the chosen algorithm
6 OutputTooLong,
7
8 /// Finite field operation returned the identity element
9 IdentityElement,
10
11 /// Encoded input cannot be decoded
12 InvalidEncoding,
13
14 /// The signature does't verify for the given message and public key
15 SignatureVerificationFailed,
16
17 /// Both a public and secret key have been provided, but they are incompatible
18 KeyMismatch,
19
20 /// Encoded input is not in canonical form
21 NonCanonical,
22
23 /// Square root has no solutions
24 NotSquare,
25
26 /// Verification string doesn't match the provided password and parameters
27 PasswordVerificationFailed,
28
29 /// Parameters would be insecure to use
30 WeakParameters,
31
32 /// Public key would be insecure to use
33 WeakPublicKey,
34};
lib/std/crypto/gimli.zig+3-2
......@@ -20,6 +20,7 @@ const assert = std.debug.assert;
2020const testing = std.testing;
2121const htest = @import("test.zig");
2222const Vector = std.meta.Vector;
23const Error = std.crypto.Error;
2324
2425pub const State = struct {
2526 pub const BLOCKBYTES = 48;
......@@ -392,7 +393,7 @@ pub const Aead = struct {
392393 /// npub: public nonce
393394 /// k: private key
394395 /// NOTE: the check of the authentication tag is currently not done in constant time
395 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) !void {
396 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 {
396397 assert(c.len == m.len);
397398
398399 var state = Aead.init(ad, npub, k);
......@@ -429,7 +430,7 @@ pub const Aead = struct {
429430 // TODO: use a constant-time equality check here, see https://github.com/ziglang/zig/issues/1776
430431 if (!mem.eql(u8, buf[0..State.RATE], &tag)) {
431432 @memset(m.ptr, undefined, m.len);
432 return error.InvalidMessage;
433 return error.AuthenticationFailed;
433434 }
434435 }
435436};
lib/std/crypto/isap.zig+2-1
......@@ -3,6 +3,7 @@ const debug = std.debug;
33const mem = std.mem;
44const math = std.math;
55const testing = std.testing;
6const Error = std.crypto.Error;
67
78/// ISAPv2 is an authenticated encryption system hardened against side channels and fault attacks.
89/// https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/round-2/spec-doc-rnd2/isap-spec-round2.pdf
......@@ -217,7 +218,7 @@ pub const IsapA128A = struct {
217218 tag.* = mac(c, ad, npub, key);
218219 }
219220
220 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) !void {
221 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) Error!void {
221222 var computed_tag = mac(c, ad, npub, key);
222223 var acc: u8 = 0;
223224 for (computed_tag) |_, j| {
lib/std/crypto/pbkdf2.zig+70-80
......@@ -7,6 +7,7 @@
77const std = @import("std");
88const mem = std.mem;
99const maxInt = std.math.maxInt;
10const Error = std.crypto.Error;
1011
1112// RFC 2898 Section 5.2
1213//
......@@ -19,36 +20,28 @@ const maxInt = std.math.maxInt;
1920// pseudorandom function. See Appendix B.1 for further discussion.)
2021// PBKDF2 is recommended for new applications.
2122//
22// PBKDF2 (P, S, c, dkLen)
23// PBKDF2 (P, S, c, dk_len)
2324//
24// Options: PRF underlying pseudorandom function (hLen
25// Options: PRF underlying pseudorandom function (h_len
2526// denotes the length in octets of the
2627// pseudorandom function output)
2728//
2829// Input: P password, an octet string
2930// S salt, an octet string
3031// c iteration count, a positive integer
31// dkLen intended length in octets of the derived
32// dk_len intended length in octets of the derived
3233// key, a positive integer, at most
33// (2^32 - 1) * hLen
34// (2^32 - 1) * h_len
3435//
35// Output: DK derived key, a dkLen-octet string
36// Output: DK derived key, a dk_len-octet string
3637
3738// Based on Apple's CommonKeyDerivation, based originally on code by Damien Bergamini.
3839
39pub const Pbkdf2Error = error{
40 /// At least one round is required
41 TooFewRounds,
42
43 /// Maximum length of the derived key is `maxInt(u32) * Prf.mac_length`
44 DerivedKeyTooLong,
45};
46
4740/// Apply PBKDF2 to generate a key from a password.
4841///
4942/// PBKDF2 is defined in RFC 2898, and is a recommendation of NIST SP 800-132.
5043///
51/// 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.
5245/// May be uninitialized. All bytes will be overwritten.
5346/// Maximum size is `maxInt(u32) * Hash.digest_length`
5447/// It is a programming error to pass buffer longer than the maximum size.
......@@ -59,43 +52,38 @@ pub const Pbkdf2Error = error{
5952///
6053/// rounds: Iteration count. Must be greater than 0. Common values range from 1,000 to 100,000.
6154/// Larger iteration counts improve security by increasing the time required to compute
62/// 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.
6356///
6457/// Prf: Pseudo-random function to use. A common choice is `std.crypto.auth.hmac.HmacSha256`.
65pub fn pbkdf2(derivedKey: []u8, password: []const u8, salt: []const u8, rounds: u32, comptime Prf: type) Pbkdf2Error!void {
66 if (rounds < 1) return error.TooFewRounds;
58pub fn pbkdf2(dk: []u8, password: []const u8, salt: []const u8, rounds: u32, comptime Prf: type) Error!void {
59 if (rounds < 1) return error.WeakParameters;
6760
68 const dkLen = derivedKey.len;
69 const hLen = Prf.mac_length;
70 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);
7164
7265 // FromSpec:
7366 //
74 // 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
7568 // stop.
7669 //
77 if (comptime (maxInt(usize) > maxInt(u32) * hLen) and (dkLen > @as(usize, maxInt(u32) * hLen))) {
78 // If maxInt(usize) is less than `maxInt(u32) * hLen` then dkLen is always inbounds
79 return error.DerivedKeyTooLong;
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 return error.OutputTooLong;
8073 }
8174
8275 // FromSpec:
8376 //
84 // 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,
8578 // rounding up, and let r be the number of bytes in the last
8679 // block
8780 //
8881
89 // l will not overflow, proof:
90 // let `L(dkLen, hLen) = (dkLen + hLen - 1) / hLen`
91 // then `L^-1(l, hLen) = l*hLen - hLen + 1`
92 // 1) L^-1(maxInt(u32), hLen) <= maxInt(u32)*hLen
93 // 2) maxInt(u32)*hLen - hLen + 1 <= maxInt(u32)*hLen // subtract maxInt(u32)*hLen + 1
94 // 3) -hLen <= -1 // multiply by -1
95 // 4) hLen >= 1
96 const r_ = dkLen % hLen;
97 const l = @intCast(u32, (dkLen / hLen) + @as(u1, if (r_ == 0) 0 else 1)); // original: (dkLen + hLen - 1) / hLen
98 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 }
9987
10088 // FromSpec:
10189 //
......@@ -125,37 +113,38 @@ pub fn pbkdf2(derivedKey: []u8, password: []const u8, salt: []const u8, rounds:
125113 // Here, INT (i) is a four-octet encoding of the integer i, most
126114 // significant octet first.
127115 //
128 // 4. Concatenate the blocks and extract the first dkLen octets to
116 // 4. Concatenate the blocks and extract the first dk_len octets to
129117 // produce a derived key DK:
130118 //
131119 // DK = T_1 || T_2 || ... || T_l<0..r-1>
132 var block: u32 = 0; // Spec limits to u32
133 while (block < l) : (block += 1) {
134 var prevBlock: [hLen]u8 = undefined;
135 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;
136125
137126 // U_1 = PRF (P, S || INT (i))
138 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
139128 var ctx = Prf.init(password);
140129 ctx.update(salt);
141 ctx.update(blockIndex[0..]);
142 ctx.final(prevBlock[0..]);
130 ctx.update(block_index[0..]);
131 ctx.final(prev_block[0..]);
143132
144133 // Choose portion of DK to write into (T_n) and initialize
145 const offset = block * hLen;
146 const blockLen = if (block != l - 1) hLen else r;
147 const dkBlock: []u8 = derivedKey[offset..][0..blockLen];
148 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]);
149138
150139 var i: u32 = 1;
151140 while (i < rounds) : (i += 1) {
152141 // U_c = PRF (P, U_{c-1})
153 Prf.create(&newBlock, prevBlock[0..], password);
154 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..]);
155144
156145 // F (P, S, c, i) = U_1 \xor U_2 \xor ... \xor U_c
157 for (dkBlock) |_, j| {
158 dkBlock[j] ^= newBlock[j];
146 for (dk_block) |_, j| {
147 dk_block[j] ^= new_block[j];
159148 }
160149 }
161150 }
......@@ -165,49 +154,50 @@ const htest = @import("test.zig");
165154const HmacSha1 = std.crypto.auth.hmac.HmacSha1;
166155
167156// RFC 6070 PBKDF2 HMAC-SHA1 Test Vectors
157
168158test "RFC 6070 one iteration" {
169159 const p = "password";
170160 const s = "salt";
171161 const c = 1;
172 const dkLen = 20;
162 const dk_len = 20;
173163
174 var derivedKey: [dkLen]u8 = undefined;
164 var dk: [dk_len]u8 = undefined;
175165
176 try pbkdf2(&derivedKey, p, s, c, HmacSha1);
166 try pbkdf2(&dk, p, s, c, HmacSha1);
177167
178168 const expected = "0c60c80f961f0e71f3a9b524af6012062fe037a6";
179169
180 htest.assertEqual(expected, derivedKey[0..]);
170 htest.assertEqual(expected, dk[0..]);
181171}
182172
183173test "RFC 6070 two iterations" {
184174 const p = "password";
185175 const s = "salt";
186176 const c = 2;
187 const dkLen = 20;
177 const dk_len = 20;
188178
189 var derivedKey: [dkLen]u8 = undefined;
179 var dk: [dk_len]u8 = undefined;
190180
191 try pbkdf2(&derivedKey, p, s, c, HmacSha1);
181 try pbkdf2(&dk, p, s, c, HmacSha1);
192182
193183 const expected = "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957";
194184
195 htest.assertEqual(expected, derivedKey[0..]);
185 htest.assertEqual(expected, dk[0..]);
196186}
197187
198188test "RFC 6070 4096 iterations" {
199189 const p = "password";
200190 const s = "salt";
201191 const c = 4096;
202 const dkLen = 20;
192 const dk_len = 20;
203193
204 var derivedKey: [dkLen]u8 = undefined;
194 var dk: [dk_len]u8 = undefined;
205195
206 try pbkdf2(&derivedKey, p, s, c, HmacSha1);
196 try pbkdf2(&dk, p, s, c, HmacSha1);
207197
208198 const expected = "4b007901b765489abead49d926f721d065a429c1";
209199
210 htest.assertEqual(expected, derivedKey[0..]);
200 htest.assertEqual(expected, dk[0..]);
211201}
212202
213203test "RFC 6070 16,777,216 iterations" {
......@@ -219,48 +209,48 @@ test "RFC 6070 16,777,216 iterations" {
219209 const p = "password";
220210 const s = "salt";
221211 const c = 16777216;
222 const dkLen = 20;
212 const dk_len = 20;
223213
224 var derivedKey = [_]u8{0} ** dkLen;
214 var dk = [_]u8{0} ** dk_len;
225215
226 try pbkdf2(&derivedKey, p, s, c, HmacSha1);
216 try pbkdf2(&dk, p, s, c, HmacSha1);
227217
228218 const expected = "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984";
229219
230 htest.assertEqual(expected, derivedKey[0..]);
220 htest.assertEqual(expected, dk[0..]);
231221}
232222
233223test "RFC 6070 multi-block salt and password" {
234224 const p = "passwordPASSWORDpassword";
235225 const s = "saltSALTsaltSALTsaltSALTsaltSALTsalt";
236226 const c = 4096;
237 const dkLen = 25;
227 const dk_len = 25;
238228
239 var derivedKey: [dkLen]u8 = undefined;
229 var dk: [dk_len]u8 = undefined;
240230
241 try pbkdf2(&derivedKey, p, s, c, HmacSha1);
231 try pbkdf2(&dk, p, s, c, HmacSha1);
242232
243233 const expected = "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038";
244234
245 htest.assertEqual(expected, derivedKey[0..]);
235 htest.assertEqual(expected, dk[0..]);
246236}
247237
248238test "RFC 6070 embedded NUL" {
249239 const p = "pass\x00word";
250240 const s = "sa\x00lt";
251241 const c = 4096;
252 const dkLen = 16;
242 const dk_len = 16;
253243
254 var derivedKey: [dkLen]u8 = undefined;
244 var dk: [dk_len]u8 = undefined;
255245
256 try pbkdf2(&derivedKey, p, s, c, HmacSha1);
246 try pbkdf2(&dk, p, s, c, HmacSha1);
257247
258248 const expected = "56fa6aa75548099dcc37d7f03425e0c3";
259249
260 htest.assertEqual(expected, derivedKey[0..]);
250 htest.assertEqual(expected, dk[0..]);
261251}
262252
263test "Very large dkLen" {
253test "Very large dk_len" {
264254 // This test allocates 8GB of memory and is expected to take several hours to run.
265255 if (true) {
266256 return error.SkipZigTest;
......@@ -268,13 +258,13 @@ test "Very large dkLen" {
268258 const p = "password";
269259 const s = "salt";
270260 const c = 1;
271 const dkLen = 1 << 33;
261 const dk_len = 1 << 33;
272262
273 var derivedKey = try std.testing.allocator.alloc(u8, dkLen);
263 var dk = try std.testing.allocator.alloc(u8, dk_len);
274264 defer {
275 std.testing.allocator.free(derivedKey);
265 std.testing.allocator.free(dk);
276266 }
277267
278 try pbkdf2(derivedKey, p, s, c, HmacSha1);
279268 // Just verify this doesn't crash with an overflow
269 try pbkdf2(dk, p, s, c, HmacSha1);
280270}
lib/std/crypto/salsa20.zig+8-7
......@@ -15,6 +15,7 @@ const Vector = std.meta.Vector;
1515const Poly1305 = crypto.onetimeauth.Poly1305;
1616const Blake2b = crypto.hash.blake2.Blake2b;
1717const X25519 = crypto.dh.X25519;
18const Error = crypto.Error;
1819
1920const Salsa20VecImpl = struct {
2021 const Lane = Vector(4, u32);
......@@ -398,7 +399,7 @@ pub const XSalsa20Poly1305 = struct {
398399 /// ad: Associated Data
399400 /// npub: public nonce
400401 /// k: private key
401 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) !void {
402 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 {
402403 debug.assert(c.len == m.len);
403404 const extended = extend(k, npub);
404405 var block0 = [_]u8{0} ** 64;
......@@ -446,7 +447,7 @@ pub const SecretBox = struct {
446447
447448 /// Verify and decrypt `c` using a nonce `npub` and a key `k`.
448449 /// `m` must be exactly `tag_length` smaller than `c`, as `c` includes an authentication tag in addition to the encrypted message.
449 pub fn open(m: []u8, c: []const u8, npub: [nonce_length]u8, k: [key_length]u8) !void {
450 pub fn open(m: []u8, c: []const u8, npub: [nonce_length]u8, k: [key_length]u8) Error!void {
450451 if (c.len < tag_length) {
451452 return error.AuthenticationFailed;
452453 }
......@@ -481,20 +482,20 @@ pub const Box = struct {
481482 pub const KeyPair = X25519.KeyPair;
482483
483484 /// Compute a secret suitable for `secretbox` given a recipent's public key and a sender's secret key.
484 pub fn createSharedSecret(public_key: [public_length]u8, secret_key: [secret_length]u8) ![shared_length]u8 {
485 pub fn createSharedSecret(public_key: [public_length]u8, secret_key: [secret_length]u8) Error![shared_length]u8 {
485486 const p = try X25519.scalarmult(secret_key, public_key);
486487 const zero = [_]u8{0} ** 16;
487488 return Salsa20Impl.hsalsa20(zero, p);
488489 }
489490
490491 /// Encrypt and authenticate a message using a recipient's public key `public_key` and a sender's `secret_key`.
491 pub fn seal(c: []u8, m: []const u8, npub: [nonce_length]u8, public_key: [public_length]u8, secret_key: [secret_length]u8) !void {
492 pub fn seal(c: []u8, m: []const u8, npub: [nonce_length]u8, public_key: [public_length]u8, secret_key: [secret_length]u8) Error!void {
492493 const shared_key = try createSharedSecret(public_key, secret_key);
493494 return SecretBox.seal(c, m, npub, shared_key);
494495 }
495496
496497 /// Verify and decrypt a message using a recipient's secret key `public_key` and a sender's `public_key`.
497 pub fn open(m: []u8, c: []const u8, npub: [nonce_length]u8, public_key: [public_length]u8, secret_key: [secret_length]u8) !void {
498 pub fn open(m: []u8, c: []const u8, npub: [nonce_length]u8, public_key: [public_length]u8, secret_key: [secret_length]u8) Error!void {
498499 const shared_key = try createSharedSecret(public_key, secret_key);
499500 return SecretBox.open(m, c, npub, shared_key);
500501 }
......@@ -527,7 +528,7 @@ pub const SealedBox = struct {
527528
528529 /// Encrypt a message `m` for a recipient whose public key is `public_key`.
529530 /// `c` must be `seal_length` bytes larger than `m`, so that the required metadata can be added.
530 pub fn seal(c: []u8, m: []const u8, public_key: [public_length]u8) !void {
531 pub fn seal(c: []u8, m: []const u8, public_key: [public_length]u8) Error!void {
531532 debug.assert(c.len == m.len + seal_length);
532533 var ekp = try KeyPair.create(null);
533534 const nonce = createNonce(ekp.public_key, public_key);
......@@ -538,7 +539,7 @@ pub const SealedBox = struct {
538539
539540 /// Decrypt a message using a key pair.
540541 /// `m` must be exactly `seal_length` bytes smaller than `c`, as `c` also includes metadata.
541 pub fn open(m: []u8, c: []const u8, keypair: KeyPair) !void {
542 pub fn open(m: []u8, c: []const u8, keypair: KeyPair) Error!void {
542543 if (c.len < seal_length) {
543544 return error.AuthenticationFailed;
544545 }
lib/std/debug.zig-18
......@@ -250,24 +250,6 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c
250250 resetSegfaultHandler();
251251 }
252252
253 if (comptime std.Target.current.isDarwin() and std.Target.current.cpu.arch == .aarch64)
254 nosuspend {
255 // As a workaround for not having threadlocal variable support in LLD for this target,
256 // we have a simpler panic implementation that does not use threadlocal variables.
257 // TODO https://github.com/ziglang/zig/issues/7527
258 const stderr = io.getStdErr().writer();
259 if (@atomicRmw(u8, &panicking, .Add, 1, .SeqCst) == 0) {
260 stderr.print("panic: " ++ format ++ "\n", args) catch os.abort();
261 if (trace) |t| {
262 dumpStackTrace(t.*);
263 }
264 dumpCurrentStackTrace(first_trace_addr);
265 } else {
266 stderr.print("Panicked during a panic. Aborting.\n", .{}) catch os.abort();
267 }
268 os.abort();
269 };
270
271253 nosuspend switch (panic_stage) {
272254 0 => {
273255 panic_stage = 1;
lib/std/enums.zig created+1281
......@@ -0,0 +1,1281 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2021 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6
7//! This module contains utilities and data structures for working with enums.
8
9const std = @import("std.zig");
10const assert = std.debug.assert;
11const testing = std.testing;
12const EnumField = std.builtin.TypeInfo.EnumField;
13
14/// Returns a struct with a field matching each unique named enum element.
15/// If the enum is extern and has multiple names for the same value, only
16/// the first name is used. Each field is of type Data and has the provided
17/// default, which may be undefined.
18pub fn EnumFieldStruct(comptime E: type, comptime Data: type, comptime field_default: ?Data) type {
19 const StructField = std.builtin.TypeInfo.StructField;
20 var fields: []const StructField = &[_]StructField{};
21 for (uniqueFields(E)) |field, i| {
22 fields = fields ++ &[_]StructField{.{
23 .name = field.name,
24 .field_type = Data,
25 .default_value = field_default,
26 .is_comptime = false,
27 .alignment = if (@sizeOf(Data) > 0) @alignOf(Data) else 0,
28 }};
29 }
30 return @Type(.{ .Struct = .{
31 .layout = .Auto,
32 .fields = fields,
33 .decls = &[_]std.builtin.TypeInfo.Declaration{},
34 .is_tuple = false,
35 }});
36}
37
38/// Looks up the supplied fields in the given enum type.
39/// Uses only the field names, field values are ignored.
40/// The result array is in the same order as the input.
41pub fn valuesFromFields(comptime E: type, comptime fields: []const EnumField) []const E {
42 comptime {
43 var result: [fields.len]E = undefined;
44 for (fields) |f, i| {
45 result[i] = @field(E, f.name);
46 }
47 return &result;
48 }
49}
50
51test "std.enums.valuesFromFields" {
52 const E = extern enum { a, b, c, d = 0 };
53 const fields = valuesFromFields(E, &[_]EnumField{
54 .{ .name = "b", .value = undefined },
55 .{ .name = "a", .value = undefined },
56 .{ .name = "a", .value = undefined },
57 .{ .name = "d", .value = undefined },
58 });
59 testing.expectEqual(E.b, fields[0]);
60 testing.expectEqual(E.a, fields[1]);
61 testing.expectEqual(E.d, fields[2]); // a == d
62 testing.expectEqual(E.d, fields[3]);
63}
64
65/// Returns the set of all named values in the given enum, in
66/// declaration order.
67pub fn values(comptime E: type) []const E {
68 return comptime valuesFromFields(E, @typeInfo(E).Enum.fields);
69}
70
71test "std.enum.values" {
72 const E = extern enum { a, b, c, d = 0 };
73 testing.expectEqualSlices(E, &.{.a, .b, .c, .d}, values(E));
74}
75
76/// Returns the set of all unique named values in the given enum, in
77/// declaration order. For repeated values in extern enums, only the
78/// first name for each value is included.
79pub fn uniqueValues(comptime E: type) []const E {
80 return comptime valuesFromFields(E, uniqueFields(E));
81}
82
83test "std.enum.uniqueValues" {
84 const E = extern enum { a, b, c, d = 0, e, f = 3 };
85 testing.expectEqualSlices(E, &.{.a, .b, .c, .f}, uniqueValues(E));
86
87 const F = enum { a, b, c };
88 testing.expectEqualSlices(F, &.{.a, .b, .c}, uniqueValues(F));
89}
90
91/// Returns the set of all unique field values in the given enum, in
92/// declaration order. For repeated values in extern enums, only the
93/// first name for each value is included.
94pub fn uniqueFields(comptime E: type) []const EnumField {
95 comptime {
96 const info = @typeInfo(E).Enum;
97 const raw_fields = info.fields;
98 // Only extern enums can contain duplicates,
99 // so fast path other types.
100 if (info.layout != .Extern) {
101 return raw_fields;
102 }
103
104 var unique_fields: []const EnumField = &[_]EnumField{};
105 outer:
106 for (raw_fields) |candidate| {
107 for (unique_fields) |u| {
108 if (u.value == candidate.value)
109 continue :outer;
110 }
111 unique_fields = unique_fields ++ &[_]EnumField{candidate};
112 }
113
114 return unique_fields;
115 }
116}
117
118/// Determines the length of a direct-mapped enum array, indexed by
119/// @intCast(usize, @enumToInt(enum_value)). The enum must be exhaustive.
120/// If the enum contains any fields with values that cannot be represented
121/// by usize, a compile error is issued. The max_unused_slots parameter limits
122/// the total number of items which have no matching enum key (holes in the enum
123/// numbering). So for example, if an enum has values 1, 2, 5, and 6, max_unused_slots
124/// must be at least 3, to allow unused slots 0, 3, and 4.
125fn directEnumArrayLen(comptime E: type, comptime max_unused_slots: comptime_int) comptime_int {
126 const info = @typeInfo(E).Enum;
127 if (!info.is_exhaustive) {
128 @compileError("Cannot create direct array of non-exhaustive enum "++@typeName(E));
129 }
130
131 var max_value: comptime_int = -1;
132 const max_usize: comptime_int = ~@as(usize, 0);
133 const fields = uniqueFields(E);
134 for (fields) |f| {
135 if (f.value < 0) {
136 @compileError("Cannot create a direct enum array for "++@typeName(E)++", field ."++f.name++" has a negative value.");
137 }
138 if (f.value > max_value) {
139 if (f.value > max_usize) {
140 @compileError("Cannot create a direct enum array for "++@typeName(E)++", field ."++f.name++" is larger than the max value of usize.");
141 }
142 max_value = f.value;
143 }
144 }
145
146 const unused_slots = max_value + 1 - fields.len;
147 if (unused_slots > max_unused_slots) {
148 const unused_str = std.fmt.comptimePrint("{d}", .{unused_slots});
149 const allowed_str = std.fmt.comptimePrint("{d}", .{max_unused_slots});
150 @compileError("Cannot create a direct enum array for "++@typeName(E)++". It would have "++unused_str++" unused slots, but only "++allowed_str++" are allowed.");
151 }
152
153 return max_value + 1;
154}
155
156/// Initializes an array of Data which can be indexed by
157/// @intCast(usize, @enumToInt(enum_value)). The enum must be exhaustive.
158/// If the enum contains any fields with values that cannot be represented
159/// by usize, a compile error is issued. The max_unused_slots parameter limits
160/// the total number of items which have no matching enum key (holes in the enum
161/// numbering). So for example, if an enum has values 1, 2, 5, and 6, max_unused_slots
162/// must be at least 3, to allow unused slots 0, 3, and 4.
163/// The init_values parameter must be a struct with field names that match the enum values.
164/// If the enum has multiple fields with the same value, the name of the first one must
165/// be used.
166pub fn directEnumArray(
167 comptime E: type,
168 comptime Data: type,
169 comptime max_unused_slots: comptime_int,
170 init_values: EnumFieldStruct(E, Data, null),
171) [directEnumArrayLen(E, max_unused_slots)]Data {
172 return directEnumArrayDefault(E, Data, null, max_unused_slots, init_values);
173}
174
175test "std.enums.directEnumArray" {
176 const E = enum(i4) { a = 4, b = 6, c = 2 };
177 var runtime_false: bool = false;
178 const array = directEnumArray(E, bool, 4, .{
179 .a = true,
180 .b = runtime_false,
181 .c = true,
182 });
183
184 testing.expectEqual([7]bool, @TypeOf(array));
185 testing.expectEqual(true, array[4]);
186 testing.expectEqual(false, array[6]);
187 testing.expectEqual(true, array[2]);
188}
189
190/// Initializes an array of Data which can be indexed by
191/// @intCast(usize, @enumToInt(enum_value)). The enum must be exhaustive.
192/// If the enum contains any fields with values that cannot be represented
193/// by usize, a compile error is issued. The max_unused_slots parameter limits
194/// the total number of items which have no matching enum key (holes in the enum
195/// numbering). So for example, if an enum has values 1, 2, 5, and 6, max_unused_slots
196/// must be at least 3, to allow unused slots 0, 3, and 4.
197/// The init_values parameter must be a struct with field names that match the enum values.
198/// If the enum has multiple fields with the same value, the name of the first one must
199/// be used.
200pub fn directEnumArrayDefault(
201 comptime E: type,
202 comptime Data: type,
203 comptime default: ?Data,
204 comptime max_unused_slots: comptime_int,
205 init_values: EnumFieldStruct(E, Data, default),
206) [directEnumArrayLen(E, max_unused_slots)]Data {
207 const len = comptime directEnumArrayLen(E, max_unused_slots);
208 var result: [len]Data = if (default) |d| [_]Data{d} ** len else undefined;
209 inline for (@typeInfo(@TypeOf(init_values)).Struct.fields) |f, i| {
210 const enum_value = @field(E, f.name);
211 const index = @intCast(usize, @enumToInt(enum_value));
212 result[index] = @field(init_values, f.name);
213 }
214 return result;
215}
216
217test "std.enums.directEnumArrayDefault" {
218 const E = enum(i4) { a = 4, b = 6, c = 2 };
219 var runtime_false: bool = false;
220 const array = directEnumArrayDefault(E, bool, false, 4, .{
221 .a = true,
222 .b = runtime_false,
223 });
224
225 testing.expectEqual([7]bool, @TypeOf(array));
226 testing.expectEqual(true, array[4]);
227 testing.expectEqual(false, array[6]);
228 testing.expectEqual(false, array[2]);
229}
230
231/// Cast an enum literal, value, or string to the enum value of type E
232/// with the same name.
233pub fn nameCast(comptime E: type, comptime value: anytype) E {
234 comptime {
235 const V = @TypeOf(value);
236 if (V == E) return value;
237 var name: ?[]const u8 = switch (@typeInfo(V)) {
238 .EnumLiteral, .Enum => @tagName(value),
239 .Pointer => if (std.meta.trait.isZigString(V)) value else null,
240 else => null,
241 };
242 if (name) |n| {
243 if (@hasField(E, n)) {
244 return @field(E, n);
245 }
246 @compileError("Enum "++@typeName(E)++" has no field named "++n);
247 }
248 @compileError("Cannot cast from "++@typeName(@TypeOf(value))++" to "++@typeName(E));
249 }
250}
251
252test "std.enums.nameCast" {
253 const A = enum { a = 0, b = 1 };
254 const B = enum { a = 1, b = 0 };
255 testing.expectEqual(A.a, nameCast(A, .a));
256 testing.expectEqual(A.a, nameCast(A, A.a));
257 testing.expectEqual(A.a, nameCast(A, B.a));
258 testing.expectEqual(A.a, nameCast(A, "a"));
259 testing.expectEqual(A.a, nameCast(A, @as(*const[1]u8, "a")));
260 testing.expectEqual(A.a, nameCast(A, @as([:0]const u8, "a")));
261 testing.expectEqual(A.a, nameCast(A, @as([]const u8, "a")));
262
263 testing.expectEqual(B.a, nameCast(B, .a));
264 testing.expectEqual(B.a, nameCast(B, A.a));
265 testing.expectEqual(B.a, nameCast(B, B.a));
266 testing.expectEqual(B.a, nameCast(B, "a"));
267
268 testing.expectEqual(B.b, nameCast(B, .b));
269 testing.expectEqual(B.b, nameCast(B, A.b));
270 testing.expectEqual(B.b, nameCast(B, B.b));
271 testing.expectEqual(B.b, nameCast(B, "b"));
272}
273
274/// A set of enum elements, backed by a bitfield. If the enum
275/// is not dense, a mapping will be constructed from enum values
276/// to dense indices. This type does no dynamic allocation and
277/// can be copied by value.
278pub fn EnumSet(comptime E: type) type {
279 const mixin = struct {
280 fn EnumSetExt(comptime Self: type) type {
281 const Indexer = Self.Indexer;
282 return struct {
283 /// Initializes the set using a struct of bools
284 pub fn init(init_values: EnumFieldStruct(E, bool, false)) Self {
285 var result = Self{};
286 comptime var i: usize = 0;
287 inline while (i < Self.len) : (i += 1) {
288 comptime const key = Indexer.keyForIndex(i);
289 comptime const tag = @tagName(key);
290 if (@field(init_values, tag)) {
291 result.bits.set(i);
292 }
293 }
294 return result;
295 }
296 };
297 }
298 };
299 return IndexedSet(EnumIndexer(E), mixin.EnumSetExt);
300}
301
302/// A map keyed by an enum, backed by a bitfield and a dense array.
303/// If the enum is not dense, a mapping will be constructed from
304/// enum values to dense indices. This type does no dynamic
305/// allocation and can be copied by value.
306pub fn EnumMap(comptime E: type, comptime V: type) type {
307 const mixin = struct {
308 fn EnumMapExt(comptime Self: type) type {
309 const Indexer = Self.Indexer;
310 return struct {
311 /// Initializes the map using a sparse struct of optionals
312 pub fn init(init_values: EnumFieldStruct(E, ?V, @as(?V, null))) Self {
313 var result = Self{};
314 comptime var i: usize = 0;
315 inline while (i < Self.len) : (i += 1) {
316 comptime const key = Indexer.keyForIndex(i);
317 comptime const tag = @tagName(key);
318 if (@field(init_values, tag)) |*v| {
319 result.bits.set(i);
320 result.values[i] = v.*;
321 }
322 }
323 return result;
324 }
325 /// Initializes a full mapping with all keys set to value.
326 /// Consider using EnumArray instead if the map will remain full.
327 pub fn initFull(value: V) Self {
328 var result = Self{
329 .bits = Self.BitSet.initFull(),
330 .values = undefined,
331 };
332 std.mem.set(V, &result.values, value);
333 return result;
334 }
335 /// Initializes a full mapping with supplied values.
336 /// Consider using EnumArray instead if the map will remain full.
337 pub fn initFullWith(init_values: EnumFieldStruct(E, V, @as(?V, null))) Self {
338 return initFullWithDefault(@as(?V, null), init_values);
339 }
340 /// Initializes a full mapping with a provided default.
341 /// Consider using EnumArray instead if the map will remain full.
342 pub fn initFullWithDefault(comptime default: ?V, init_values: EnumFieldStruct(E, V, default)) Self {
343 var result = Self{
344 .bits = Self.BitSet.initFull(),
345 .values = undefined,
346 };
347 comptime var i: usize = 0;
348 inline while (i < Self.len) : (i += 1) {
349 comptime const key = Indexer.keyForIndex(i);
350 comptime const tag = @tagName(key);
351 result.values[i] = @field(init_values, tag);
352 }
353 return result;
354 }
355 };
356 }
357 };
358 return IndexedMap(EnumIndexer(E), V, mixin.EnumMapExt);
359}
360
361/// An array keyed by an enum, backed by a dense array.
362/// If the enum is not dense, a mapping will be constructed from
363/// enum values to dense indices. This type does no dynamic
364/// allocation and can be copied by value.
365pub fn EnumArray(comptime E: type, comptime V: type) type {
366 const mixin = struct {
367 fn EnumArrayExt(comptime Self: type) type {
368 const Indexer = Self.Indexer;
369 return struct {
370 /// Initializes all values in the enum array
371 pub fn init(init_values: EnumFieldStruct(E, V, @as(?V, null))) Self {
372 return initDefault(@as(?V, null), init_values);
373 }
374
375 /// Initializes values in the enum array, with the specified default.
376 pub fn initDefault(comptime default: ?V, init_values: EnumFieldStruct(E, V, default)) Self {
377 var result = Self{ .values = undefined };
378 comptime var i: usize = 0;
379 inline while (i < Self.len) : (i += 1) {
380 const key = comptime Indexer.keyForIndex(i);
381 const tag = @tagName(key);
382 result.values[i] = @field(init_values, tag);
383 }
384 return result;
385 }
386 };
387 }
388 };
389 return IndexedArray(EnumIndexer(E), V, mixin.EnumArrayExt);
390}
391
392/// Pass this function as the Ext parameter to Indexed* if you
393/// do not want to attach any extensions. This parameter was
394/// originally an optional, but optional generic functions
395/// seem to be broken at the moment.
396/// TODO: Once #8169 is fixed, consider switching this param
397/// back to an optional.
398pub fn NoExtension(comptime Self: type) type {
399 return NoExt;
400}
401const NoExt = struct{};
402
403/// A set type with an Indexer mapping from keys to indices.
404/// Presence or absence is stored as a dense bitfield. This
405/// type does no allocation and can be copied by value.
406pub fn IndexedSet(comptime I: type, comptime Ext: fn(type)type) type {
407 comptime ensureIndexer(I);
408 return struct {
409 const Self = @This();
410
411 pub usingnamespace Ext(Self);
412
413 /// The indexing rules for converting between keys and indices.
414 pub const Indexer = I;
415 /// The element type for this set.
416 pub const Key = Indexer.Key;
417
418 const BitSet = std.StaticBitSet(Indexer.count);
419
420 /// The maximum number of items in this set.
421 pub const len = Indexer.count;
422
423 bits: BitSet = BitSet.initEmpty(),
424
425 /// Returns a set containing all possible keys.
426 pub fn initFull() Self {
427 return .{ .bits = BitSet.initFull() };
428 }
429
430 /// Returns the number of keys in the set.
431 pub fn count(self: Self) usize {
432 return self.bits.count();
433 }
434
435 /// Checks if a key is in the set.
436 pub fn contains(self: Self, key: Key) bool {
437 return self.bits.isSet(Indexer.indexOf(key));
438 }
439
440 /// Puts a key in the set.
441 pub fn insert(self: *Self, key: Key) void {
442 self.bits.set(Indexer.indexOf(key));
443 }
444
445 /// Removes a key from the set.
446 pub fn remove(self: *Self, key: Key) void {
447 self.bits.unset(Indexer.indexOf(key));
448 }
449
450 /// Changes the presence of a key in the set to match the passed bool.
451 pub fn setPresent(self: *Self, key: Key, present: bool) void {
452 self.bits.setValue(Indexer.indexOf(key), present);
453 }
454
455 /// Toggles the presence of a key in the set. If the key is in
456 /// the set, removes it. Otherwise adds it.
457 pub fn toggle(self: *Self, key: Key) void {
458 self.bits.toggle(Indexer.indexOf(key));
459 }
460
461 /// Toggles the presence of all keys in the passed set.
462 pub fn toggleSet(self: *Self, other: Self) void {
463 self.bits.toggleSet(other.bits);
464 }
465
466 /// Toggles all possible keys in the set.
467 pub fn toggleAll(self: *Self) void {
468 self.bits.toggleAll();
469 }
470
471 /// Adds all keys in the passed set to this set.
472 pub fn setUnion(self: *Self, other: Self) void {
473 self.bits.setUnion(other.bits);
474 }
475
476 /// Removes all keys which are not in the passed set.
477 pub fn setIntersection(self: *Self, other: Self) void {
478 self.bits.setIntersection(other.bits);
479 }
480
481 /// Returns an iterator over this set, which iterates in
482 /// index order. Modifications to the set during iteration
483 /// may or may not be observed by the iterator, but will
484 /// not invalidate it.
485 pub fn iterator(self: *Self) Iterator {
486 return .{ .inner = self.bits.iterator(.{}) };
487 }
488
489 pub const Iterator = struct {
490 inner: BitSet.Iterator(.{}),
491
492 pub fn next(self: *Iterator) ?Key {
493 return if (self.inner.next()) |index|
494 Indexer.keyForIndex(index)
495 else null;
496 }
497 };
498 };
499}
500
501/// A map from keys to values, using an index lookup. Uses a
502/// bitfield to track presence and a dense array of values.
503/// This type does no allocation and can be copied by value.
504pub fn IndexedMap(comptime I: type, comptime V: type, comptime Ext: fn(type)type) type {
505 comptime ensureIndexer(I);
506 return struct {
507 const Self = @This();
508
509 pub usingnamespace Ext(Self);
510
511 /// The index mapping for this map
512 pub const Indexer = I;
513 /// The key type used to index this map
514 pub const Key = Indexer.Key;
515 /// The value type stored in this map
516 pub const Value = V;
517 /// The number of possible keys in the map
518 pub const len = Indexer.count;
519
520 const BitSet = std.StaticBitSet(Indexer.count);
521
522 /// Bits determining whether items are in the map
523 bits: BitSet = BitSet.initEmpty(),
524 /// Values of items in the map. If the associated
525 /// bit is zero, the value is undefined.
526 values: [Indexer.count]Value = undefined,
527
528 /// The number of items in the map.
529 pub fn count(self: Self) usize {
530 return self.bits.count();
531 }
532
533 /// Checks if the map contains an item.
534 pub fn contains(self: Self, key: Key) bool {
535 return self.bits.isSet(Indexer.indexOf(key));
536 }
537
538 /// Gets the value associated with a key.
539 /// If the key is not in the map, returns null.
540 pub fn get(self: Self, key: Key) ?Value {
541 const index = Indexer.indexOf(key);
542 return if (self.bits.isSet(index)) self.values[index] else null;
543 }
544
545 /// Gets the value associated with a key, which must
546 /// exist in the map.
547 pub fn getAssertContains(self: Self, key: Key) Value {
548 const index = Indexer.indexOf(key);
549 assert(self.bits.isSet(index));
550 return self.values[index];
551 }
552
553 /// Gets the address of the value associated with a key.
554 /// If the key is not in the map, returns null.
555 pub fn getPtr(self: *Self, key: Key) ?*Value {
556 const index = Indexer.indexOf(key);
557 return if (self.bits.isSet(index)) &self.values[index] else null;
558 }
559
560 /// Gets the address of the const value associated with a key.
561 /// If the key is not in the map, returns null.
562 pub fn getPtrConst(self: *const Self, key: Key) ?*const Value {
563 const index = Indexer.indexOf(key);
564 return if (self.bits.isSet(index)) &self.values[index] else null;
565 }
566
567 /// Gets the address of the value associated with a key.
568 /// The key must be present in the map.
569 pub fn getPtrAssertContains(self: *Self, key: Key) *Value {
570 const index = Indexer.indexOf(key);
571 assert(self.bits.isSet(index));
572 return &self.values[index];
573 }
574
575 /// Adds the key to the map with the supplied value.
576 /// If the key is already in the map, overwrites the value.
577 pub fn put(self: *Self, key: Key, value: Value) void {
578 const index = Indexer.indexOf(key);
579 self.bits.set(index);
580 self.values[index] = value;
581 }
582
583 /// Adds the key to the map with an undefined value.
584 /// If the key is already in the map, the value becomes undefined.
585 /// A pointer to the value is returned, which should be
586 /// used to initialize the value.
587 pub fn putUninitialized(self: *Self, key: Key) *Value {
588 const index = Indexer.indexOf(key);
589 self.bits.set(index);
590 self.values[index] = undefined;
591 return &self.values[index];
592 }
593
594 /// Sets the value associated with the key in the map,
595 /// and returns the old value. If the key was not in
596 /// the map, returns null.
597 pub fn fetchPut(self: *Self, key: Key, value: Value) ?Value {
598 const index = Indexer.indexOf(key);
599 const result: ?Value = if (self.bits.isSet(index)) self.values[index] else null;
600 self.bits.set(index);
601 self.values[index] = value;
602 return result;
603 }
604
605 /// Removes a key from the map. If the key was not in the map,
606 /// does nothing.
607 pub fn remove(self: *Self, key: Key) void {
608 const index = Indexer.indexOf(key);
609 self.bits.unset(index);
610 self.values[index] = undefined;
611 }
612
613 /// Removes a key from the map, and returns the old value.
614 /// If the key was not in the map, returns null.
615 pub fn fetchRemove(self: *Self, key: Key) ?Value {
616 const index = Indexer.indexOf(key);
617 const result: ?Value = if (self.bits.isSet(index)) self.values[index] else null;
618 self.bits.unset(index);
619 self.values[index] = undefined;
620 return result;
621 }
622
623 /// Returns an iterator over the map, which visits items in index order.
624 /// Modifications to the underlying map may or may not be observed by
625 /// the iterator, but will not invalidate it.
626 pub fn iterator(self: *Self) Iterator {
627 return .{
628 .inner = self.bits.iterator(.{}),
629 .values = &self.values,
630 };
631 }
632
633 /// An entry in the map.
634 pub const Entry = struct {
635 /// The key associated with this entry.
636 /// Modifying this key will not change the map.
637 key: Key,
638
639 /// A pointer to the value in the map associated
640 /// with this key. Modifications through this
641 /// pointer will modify the underlying data.
642 value: *Value,
643 };
644
645 pub const Iterator = struct {
646 inner: BitSet.Iterator(.{}),
647 values: *[Indexer.count]Value,
648
649 pub fn next(self: *Iterator) ?Entry {
650 return if (self.inner.next()) |index|
651 Entry{
652 .key = Indexer.keyForIndex(index),
653 .value = &self.values[index],
654 }
655 else null;
656 }
657 };
658 };
659}
660
661/// A dense array of values, using an indexed lookup.
662/// This type does no allocation and can be copied by value.
663pub fn IndexedArray(comptime I: type, comptime V: type, comptime Ext: fn(type)type) type {
664 comptime ensureIndexer(I);
665 return struct {
666 const Self = @This();
667
668 pub usingnamespace Ext(Self);
669
670 /// The index mapping for this map
671 pub const Indexer = I;
672 /// The key type used to index this map
673 pub const Key = Indexer.Key;
674 /// The value type stored in this map
675 pub const Value = V;
676 /// The number of possible keys in the map
677 pub const len = Indexer.count;
678
679 values: [Indexer.count]Value,
680
681 pub fn initUndefined() Self {
682 return Self{ .values = undefined };
683 }
684
685 pub fn initFill(v: Value) Self {
686 var self: Self = undefined;
687 std.mem.set(Value, &self.values, v);
688 return self;
689 }
690
691 /// Returns the value in the array associated with a key.
692 pub fn get(self: Self, key: Key) Value {
693 return self.values[Indexer.indexOf(key)];
694 }
695
696 /// Returns a pointer to the slot in the array associated with a key.
697 pub fn getPtr(self: *Self, key: Key) *Value {
698 return &self.values[Indexer.indexOf(key)];
699 }
700
701 /// Returns a const pointer to the slot in the array associated with a key.
702 pub fn getPtrConst(self: *const Self, key: Key) *const Value {
703 return &self.values[Indexer.indexOf(key)];
704 }
705
706 /// Sets the value in the slot associated with a key.
707 pub fn set(self: *Self, key: Key, value: Value) void {
708 self.values[Indexer.indexOf(key)] = value;
709 }
710
711 /// Iterates over the items in the array, in index order.
712 pub fn iterator(self: *Self) Iterator {
713 return .{
714 .values = &self.values,
715 };
716 }
717
718 /// An entry in the array.
719 pub const Entry = struct {
720 /// The key associated with this entry.
721 /// Modifying this key will not change the array.
722 key: Key,
723
724 /// A pointer to the value in the array associated
725 /// with this key. Modifications through this
726 /// pointer will modify the underlying data.
727 value: *Value,
728 };
729
730 pub const Iterator = struct {
731 index: usize = 0,
732 values: *[Indexer.count]Value,
733
734 pub fn next(self: *Iterator) ?Entry {
735 const index = self.index;
736 if (index < Indexer.count) {
737 self.index += 1;
738 return Entry{
739 .key = Indexer.keyForIndex(index),
740 .value = &self.values[index],
741 };
742 }
743 return null;
744 }
745 };
746 };
747}
748
749/// Verifies that a type is a valid Indexer, providing a helpful
750/// compile error if not. An Indexer maps a comptime known set
751/// of keys to a dense set of zero-based indices.
752/// The indexer interface must look like this:
753/// ```
754/// struct {
755/// /// The key type which this indexer converts to indices
756/// pub const Key: type,
757/// /// The number of indexes in the dense mapping
758/// pub const count: usize,
759/// /// Converts from a key to an index
760/// pub fn indexOf(Key) usize;
761/// /// Converts from an index to a key
762/// pub fn keyForIndex(usize) Key;
763/// }
764/// ```
765pub fn ensureIndexer(comptime T: type) void {
766 comptime {
767 if (!@hasDecl(T, "Key")) @compileError("Indexer must have decl Key: type.");
768 if (@TypeOf(T.Key) != type) @compileError("Indexer.Key must be a type.");
769 if (!@hasDecl(T, "count")) @compileError("Indexer must have decl count: usize.");
770 if (@TypeOf(T.count) != usize) @compileError("Indexer.count must be a usize.");
771 if (!@hasDecl(T, "indexOf")) @compileError("Indexer.indexOf must be a fn(Key)usize.");
772 if (@TypeOf(T.indexOf) != fn(T.Key)usize) @compileError("Indexer must have decl indexOf: fn(Key)usize.");
773 if (!@hasDecl(T, "keyForIndex")) @compileError("Indexer must have decl keyForIndex: fn(usize)Key.");
774 if (@TypeOf(T.keyForIndex) != fn(usize)T.Key) @compileError("Indexer.keyForIndex must be a fn(usize)Key.");
775 }
776}
777
778test "std.enums.ensureIndexer" {
779 ensureIndexer(struct {
780 pub const Key = u32;
781 pub const count: usize = 8;
782 pub fn indexOf(k: Key) usize {
783 return @intCast(usize, k);
784 }
785 pub fn keyForIndex(index: usize) Key {
786 return @intCast(Key, index);
787 }
788 });
789}
790
791fn ascByValue(ctx: void, comptime a: EnumField, comptime b: EnumField) bool {
792 return a.value < b.value;
793}
794pub fn EnumIndexer(comptime E: type) type {
795 if (!@typeInfo(E).Enum.is_exhaustive) {
796 @compileError("Cannot create an enum indexer for a non-exhaustive enum.");
797 }
798
799 const const_fields = uniqueFields(E);
800 var fields = const_fields[0..const_fields.len].*;
801 if (fields.len == 0) {
802 return struct {
803 pub const Key = E;
804 pub const count: usize = 0;
805 pub fn indexOf(e: E) usize { unreachable; }
806 pub fn keyForIndex(i: usize) E { unreachable; }
807 };
808 }
809 std.sort.sort(EnumField, &fields, {}, ascByValue);
810 const min = fields[0].value;
811 const max = fields[fields.len-1].value;
812 if (max - min == fields.len-1) {
813 return struct {
814 pub const Key = E;
815 pub const count = fields.len;
816 pub fn indexOf(e: E) usize {
817 return @intCast(usize, @enumToInt(e) - min);
818 }
819 pub fn keyForIndex(i: usize) E {
820 // TODO fix addition semantics. This calculation
821 // gives up some safety to avoid artificially limiting
822 // the range of signed enum values to max_isize.
823 const enum_value = if (min < 0) @bitCast(isize, i) +% min else i + min;
824 return @intToEnum(E, @intCast(std.meta.Tag(E), enum_value));
825 }
826 };
827 }
828
829 const keys = valuesFromFields(E, &fields);
830
831 return struct {
832 pub const Key = E;
833 pub const count = fields.len;
834 pub fn indexOf(e: E) usize {
835 for (keys) |k, i| {
836 if (k == e) return i;
837 }
838 unreachable;
839 }
840 pub fn keyForIndex(i: usize) E {
841 return keys[i];
842 }
843 };
844}
845
846test "std.enums.EnumIndexer dense zeroed" {
847 const E = enum{ b = 1, a = 0, c = 2 };
848 const Indexer = EnumIndexer(E);
849 ensureIndexer(Indexer);
850 testing.expectEqual(E, Indexer.Key);
851 testing.expectEqual(@as(usize, 3), Indexer.count);
852
853 testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a));
854 testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b));
855 testing.expectEqual(@as(usize, 2), Indexer.indexOf(.c));
856
857 testing.expectEqual(E.a, Indexer.keyForIndex(0));
858 testing.expectEqual(E.b, Indexer.keyForIndex(1));
859 testing.expectEqual(E.c, Indexer.keyForIndex(2));
860}
861
862test "std.enums.EnumIndexer dense positive" {
863 const E = enum(u4) { c = 6, a = 4, b = 5 };
864 const Indexer = EnumIndexer(E);
865 ensureIndexer(Indexer);
866 testing.expectEqual(E, Indexer.Key);
867 testing.expectEqual(@as(usize, 3), Indexer.count);
868
869 testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a));
870 testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b));
871 testing.expectEqual(@as(usize, 2), Indexer.indexOf(.c));
872
873 testing.expectEqual(E.a, Indexer.keyForIndex(0));
874 testing.expectEqual(E.b, Indexer.keyForIndex(1));
875 testing.expectEqual(E.c, Indexer.keyForIndex(2));
876}
877
878test "std.enums.EnumIndexer dense negative" {
879 const E = enum(i4) { a = -6, c = -4, b = -5 };
880 const Indexer = EnumIndexer(E);
881 ensureIndexer(Indexer);
882 testing.expectEqual(E, Indexer.Key);
883 testing.expectEqual(@as(usize, 3), Indexer.count);
884
885 testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a));
886 testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b));
887 testing.expectEqual(@as(usize, 2), Indexer.indexOf(.c));
888
889 testing.expectEqual(E.a, Indexer.keyForIndex(0));
890 testing.expectEqual(E.b, Indexer.keyForIndex(1));
891 testing.expectEqual(E.c, Indexer.keyForIndex(2));
892}
893
894test "std.enums.EnumIndexer sparse" {
895 const E = enum(i4) { a = -2, c = 6, b = 4 };
896 const Indexer = EnumIndexer(E);
897 ensureIndexer(Indexer);
898 testing.expectEqual(E, Indexer.Key);
899 testing.expectEqual(@as(usize, 3), Indexer.count);
900
901 testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a));
902 testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b));
903 testing.expectEqual(@as(usize, 2), Indexer.indexOf(.c));
904
905 testing.expectEqual(E.a, Indexer.keyForIndex(0));
906 testing.expectEqual(E.b, Indexer.keyForIndex(1));
907 testing.expectEqual(E.c, Indexer.keyForIndex(2));
908}
909
910test "std.enums.EnumIndexer repeats" {
911 const E = extern enum{ a = -2, c = 6, b = 4, b2 = 4 };
912 const Indexer = EnumIndexer(E);
913 ensureIndexer(Indexer);
914 testing.expectEqual(E, Indexer.Key);
915 testing.expectEqual(@as(usize, 3), Indexer.count);
916
917 testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a));
918 testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b));
919 testing.expectEqual(@as(usize, 2), Indexer.indexOf(.c));
920
921 testing.expectEqual(E.a, Indexer.keyForIndex(0));
922 testing.expectEqual(E.b, Indexer.keyForIndex(1));
923 testing.expectEqual(E.c, Indexer.keyForIndex(2));
924}
925
926test "std.enums.EnumSet" {
927 const E = extern enum { a, b, c, d, e = 0 };
928 const Set = EnumSet(E);
929 testing.expectEqual(E, Set.Key);
930 testing.expectEqual(EnumIndexer(E), Set.Indexer);
931 testing.expectEqual(@as(usize, 4), Set.len);
932
933 // Empty sets
934 const empty = Set{};
935 comptime testing.expect(empty.count() == 0);
936
937 var empty_b = Set.init(.{});
938 testing.expect(empty_b.count() == 0);
939
940 const empty_c = comptime Set.init(.{});
941 comptime testing.expect(empty_c.count() == 0);
942
943 const full = Set.initFull();
944 testing.expect(full.count() == Set.len);
945
946 const full_b = comptime Set.initFull();
947 comptime testing.expect(full_b.count() == Set.len);
948
949 testing.expectEqual(false, empty.contains(.a));
950 testing.expectEqual(false, empty.contains(.b));
951 testing.expectEqual(false, empty.contains(.c));
952 testing.expectEqual(false, empty.contains(.d));
953 testing.expectEqual(false, empty.contains(.e));
954 {
955 var iter = empty_b.iterator();
956 testing.expectEqual(@as(?E, null), iter.next());
957 }
958
959 var mut = Set.init(.{
960 .a=true, .c=true,
961 });
962 testing.expectEqual(@as(usize, 2), mut.count());
963 testing.expectEqual(true, mut.contains(.a));
964 testing.expectEqual(false, mut.contains(.b));
965 testing.expectEqual(true, mut.contains(.c));
966 testing.expectEqual(false, mut.contains(.d));
967 testing.expectEqual(true, mut.contains(.e)); // aliases a
968 {
969 var it = mut.iterator();
970 testing.expectEqual(@as(?E, .a), it.next());
971 testing.expectEqual(@as(?E, .c), it.next());
972 testing.expectEqual(@as(?E, null), it.next());
973 }
974
975 mut.toggleAll();
976 testing.expectEqual(@as(usize, 2), mut.count());
977 testing.expectEqual(false, mut.contains(.a));
978 testing.expectEqual(true, mut.contains(.b));
979 testing.expectEqual(false, mut.contains(.c));
980 testing.expectEqual(true, mut.contains(.d));
981 testing.expectEqual(false, mut.contains(.e)); // aliases a
982 {
983 var it = mut.iterator();
984 testing.expectEqual(@as(?E, .b), it.next());
985 testing.expectEqual(@as(?E, .d), it.next());
986 testing.expectEqual(@as(?E, null), it.next());
987 }
988
989 mut.toggleSet(Set.init(.{ .a=true, .b=true }));
990 testing.expectEqual(@as(usize, 2), mut.count());
991 testing.expectEqual(true, mut.contains(.a));
992 testing.expectEqual(false, mut.contains(.b));
993 testing.expectEqual(false, mut.contains(.c));
994 testing.expectEqual(true, mut.contains(.d));
995 testing.expectEqual(true, mut.contains(.e)); // aliases a
996
997 mut.setUnion(Set.init(.{ .a=true, .b=true }));
998 testing.expectEqual(@as(usize, 3), mut.count());
999 testing.expectEqual(true, mut.contains(.a));
1000 testing.expectEqual(true, mut.contains(.b));
1001 testing.expectEqual(false, mut.contains(.c));
1002 testing.expectEqual(true, mut.contains(.d));
1003
1004 mut.remove(.c);
1005 mut.remove(.b);
1006 testing.expectEqual(@as(usize, 2), mut.count());
1007 testing.expectEqual(true, mut.contains(.a));
1008 testing.expectEqual(false, mut.contains(.b));
1009 testing.expectEqual(false, mut.contains(.c));
1010 testing.expectEqual(true, mut.contains(.d));
1011
1012 mut.setIntersection(Set.init(.{ .a=true, .b=true }));
1013 testing.expectEqual(@as(usize, 1), mut.count());
1014 testing.expectEqual(true, mut.contains(.a));
1015 testing.expectEqual(false, mut.contains(.b));
1016 testing.expectEqual(false, mut.contains(.c));
1017 testing.expectEqual(false, mut.contains(.d));
1018
1019 mut.insert(.a);
1020 mut.insert(.b);
1021 testing.expectEqual(@as(usize, 2), mut.count());
1022 testing.expectEqual(true, mut.contains(.a));
1023 testing.expectEqual(true, mut.contains(.b));
1024 testing.expectEqual(false, mut.contains(.c));
1025 testing.expectEqual(false, mut.contains(.d));
1026
1027 mut.setPresent(.a, false);
1028 mut.toggle(.b);
1029 mut.toggle(.c);
1030 mut.setPresent(.d, true);
1031 testing.expectEqual(@as(usize, 2), mut.count());
1032 testing.expectEqual(false, mut.contains(.a));
1033 testing.expectEqual(false, mut.contains(.b));
1034 testing.expectEqual(true, mut.contains(.c));
1035 testing.expectEqual(true, mut.contains(.d));
1036}
1037
1038test "std.enums.EnumArray void" {
1039 const E = extern enum { a, b, c, d, e = 0 };
1040 const ArrayVoid = EnumArray(E, void);
1041 testing.expectEqual(E, ArrayVoid.Key);
1042 testing.expectEqual(EnumIndexer(E), ArrayVoid.Indexer);
1043 testing.expectEqual(void, ArrayVoid.Value);
1044 testing.expectEqual(@as(usize, 4), ArrayVoid.len);
1045
1046 const undef = ArrayVoid.initUndefined();
1047 var inst = ArrayVoid.initFill({});
1048 const inst2 = ArrayVoid.init(.{ .a = {}, .b = {}, .c = {}, .d = {} });
1049 const inst3 = ArrayVoid.initDefault({}, .{});
1050
1051 _ = inst.get(.a);
1052 _ = inst.getPtr(.b);
1053 _ = inst.getPtrConst(.c);
1054 inst.set(.a, {});
1055
1056 var it = inst.iterator();
1057 testing.expectEqual(E.a, it.next().?.key);
1058 testing.expectEqual(E.b, it.next().?.key);
1059 testing.expectEqual(E.c, it.next().?.key);
1060 testing.expectEqual(E.d, it.next().?.key);
1061 testing.expect(it.next() == null);
1062}
1063
1064test "std.enums.EnumArray sized" {
1065 const E = extern enum { a, b, c, d, e = 0 };
1066 const Array = EnumArray(E, usize);
1067 testing.expectEqual(E, Array.Key);
1068 testing.expectEqual(EnumIndexer(E), Array.Indexer);
1069 testing.expectEqual(usize, Array.Value);
1070 testing.expectEqual(@as(usize, 4), Array.len);
1071
1072 const undef = Array.initUndefined();
1073 var inst = Array.initFill(5);
1074 const inst2 = Array.init(.{ .a = 1, .b = 2, .c = 3, .d = 4 });
1075 const inst3 = Array.initDefault(6, .{.b = 4, .c = 2});
1076
1077 testing.expectEqual(@as(usize, 5), inst.get(.a));
1078 testing.expectEqual(@as(usize, 5), inst.get(.b));
1079 testing.expectEqual(@as(usize, 5), inst.get(.c));
1080 testing.expectEqual(@as(usize, 5), inst.get(.d));
1081
1082 testing.expectEqual(@as(usize, 1), inst2.get(.a));
1083 testing.expectEqual(@as(usize, 2), inst2.get(.b));
1084 testing.expectEqual(@as(usize, 3), inst2.get(.c));
1085 testing.expectEqual(@as(usize, 4), inst2.get(.d));
1086
1087 testing.expectEqual(@as(usize, 6), inst3.get(.a));
1088 testing.expectEqual(@as(usize, 4), inst3.get(.b));
1089 testing.expectEqual(@as(usize, 2), inst3.get(.c));
1090 testing.expectEqual(@as(usize, 6), inst3.get(.d));
1091
1092 testing.expectEqual(&inst.values[0], inst.getPtr(.a));
1093 testing.expectEqual(&inst.values[1], inst.getPtr(.b));
1094 testing.expectEqual(&inst.values[2], inst.getPtr(.c));
1095 testing.expectEqual(&inst.values[3], inst.getPtr(.d));
1096
1097 testing.expectEqual(@as(*const usize, &inst.values[0]), inst.getPtrConst(.a));
1098 testing.expectEqual(@as(*const usize, &inst.values[1]), inst.getPtrConst(.b));
1099 testing.expectEqual(@as(*const usize, &inst.values[2]), inst.getPtrConst(.c));
1100 testing.expectEqual(@as(*const usize, &inst.values[3]), inst.getPtrConst(.d));
1101
1102 inst.set(.c, 8);
1103 testing.expectEqual(@as(usize, 5), inst.get(.a));
1104 testing.expectEqual(@as(usize, 5), inst.get(.b));
1105 testing.expectEqual(@as(usize, 8), inst.get(.c));
1106 testing.expectEqual(@as(usize, 5), inst.get(.d));
1107
1108 var it = inst.iterator();
1109 const Entry = Array.Entry;
1110 testing.expectEqual(@as(?Entry, Entry{
1111 .key = .a,
1112 .value = &inst.values[0],
1113 }), it.next());
1114 testing.expectEqual(@as(?Entry, Entry{
1115 .key = .b,
1116 .value = &inst.values[1],
1117 }), it.next());
1118 testing.expectEqual(@as(?Entry, Entry{
1119 .key = .c,
1120 .value = &inst.values[2],
1121 }), it.next());
1122 testing.expectEqual(@as(?Entry, Entry{
1123 .key = .d,
1124 .value = &inst.values[3],
1125 }), it.next());
1126 testing.expectEqual(@as(?Entry, null), it.next());
1127}
1128
1129test "std.enums.EnumMap void" {
1130 const E = extern enum { a, b, c, d, e = 0 };
1131 const Map = EnumMap(E, void);
1132 testing.expectEqual(E, Map.Key);
1133 testing.expectEqual(EnumIndexer(E), Map.Indexer);
1134 testing.expectEqual(void, Map.Value);
1135 testing.expectEqual(@as(usize, 4), Map.len);
1136
1137 const b = Map.initFull({});
1138 testing.expectEqual(@as(usize, 4), b.count());
1139
1140 const c = Map.initFullWith(.{ .a = {}, .b = {}, .c = {}, .d = {} });
1141 testing.expectEqual(@as(usize, 4), c.count());
1142
1143 const d = Map.initFullWithDefault({}, .{ .b = {} });
1144 testing.expectEqual(@as(usize, 4), d.count());
1145
1146 var a = Map.init(.{ .b = {}, .d = {} });
1147 testing.expectEqual(@as(usize, 2), a.count());
1148 testing.expectEqual(false, a.contains(.a));
1149 testing.expectEqual(true, a.contains(.b));
1150 testing.expectEqual(false, a.contains(.c));
1151 testing.expectEqual(true, a.contains(.d));
1152 testing.expect(a.get(.a) == null);
1153 testing.expect(a.get(.b) != null);
1154 testing.expect(a.get(.c) == null);
1155 testing.expect(a.get(.d) != null);
1156 testing.expect(a.getPtr(.a) == null);
1157 testing.expect(a.getPtr(.b) != null);
1158 testing.expect(a.getPtr(.c) == null);
1159 testing.expect(a.getPtr(.d) != null);
1160 testing.expect(a.getPtrConst(.a) == null);
1161 testing.expect(a.getPtrConst(.b) != null);
1162 testing.expect(a.getPtrConst(.c) == null);
1163 testing.expect(a.getPtrConst(.d) != null);
1164 _ = a.getPtrAssertContains(.b);
1165 _ = a.getAssertContains(.d);
1166
1167 a.put(.a, {});
1168 a.put(.a, {});
1169 a.putUninitialized(.c).* = {};
1170 a.putUninitialized(.c).* = {};
1171
1172 testing.expectEqual(@as(usize, 4), a.count());
1173 testing.expect(a.get(.a) != null);
1174 testing.expect(a.get(.b) != null);
1175 testing.expect(a.get(.c) != null);
1176 testing.expect(a.get(.d) != null);
1177
1178 a.remove(.a);
1179 _ = a.fetchRemove(.c);
1180
1181 var iter = a.iterator();
1182 const Entry = Map.Entry;
1183 testing.expectEqual(E.b, iter.next().?.key);
1184 testing.expectEqual(E.d, iter.next().?.key);
1185 testing.expect(iter.next() == null);
1186}
1187
1188test "std.enums.EnumMap sized" {
1189 const E = extern enum { a, b, c, d, e = 0 };
1190 const Map = EnumMap(E, usize);
1191 testing.expectEqual(E, Map.Key);
1192 testing.expectEqual(EnumIndexer(E), Map.Indexer);
1193 testing.expectEqual(usize, Map.Value);
1194 testing.expectEqual(@as(usize, 4), Map.len);
1195
1196 const b = Map.initFull(5);
1197 testing.expectEqual(@as(usize, 4), b.count());
1198 testing.expect(b.contains(.a));
1199 testing.expect(b.contains(.b));
1200 testing.expect(b.contains(.c));
1201 testing.expect(b.contains(.d));
1202 testing.expectEqual(@as(?usize, 5), b.get(.a));
1203 testing.expectEqual(@as(?usize, 5), b.get(.b));
1204 testing.expectEqual(@as(?usize, 5), b.get(.c));
1205 testing.expectEqual(@as(?usize, 5), b.get(.d));
1206
1207 const c = Map.initFullWith(.{ .a = 1, .b = 2, .c = 3, .d = 4 });
1208 testing.expectEqual(@as(usize, 4), c.count());
1209 testing.expect(c.contains(.a));
1210 testing.expect(c.contains(.b));
1211 testing.expect(c.contains(.c));
1212 testing.expect(c.contains(.d));
1213 testing.expectEqual(@as(?usize, 1), c.get(.a));
1214 testing.expectEqual(@as(?usize, 2), c.get(.b));
1215 testing.expectEqual(@as(?usize, 3), c.get(.c));
1216 testing.expectEqual(@as(?usize, 4), c.get(.d));
1217
1218 const d = Map.initFullWithDefault(6, .{ .b = 2, .c = 4 });
1219 testing.expectEqual(@as(usize, 4), d.count());
1220 testing.expect(d.contains(.a));
1221 testing.expect(d.contains(.b));
1222 testing.expect(d.contains(.c));
1223 testing.expect(d.contains(.d));
1224 testing.expectEqual(@as(?usize, 6), d.get(.a));
1225 testing.expectEqual(@as(?usize, 2), d.get(.b));
1226 testing.expectEqual(@as(?usize, 4), d.get(.c));
1227 testing.expectEqual(@as(?usize, 6), d.get(.d));
1228
1229 var a = Map.init(.{ .b = 2, .d = 4 });
1230 testing.expectEqual(@as(usize, 2), a.count());
1231 testing.expectEqual(false, a.contains(.a));
1232 testing.expectEqual(true, a.contains(.b));
1233 testing.expectEqual(false, a.contains(.c));
1234 testing.expectEqual(true, a.contains(.d));
1235
1236 testing.expectEqual(@as(?usize, null), a.get(.a));
1237 testing.expectEqual(@as(?usize, 2), a.get(.b));
1238 testing.expectEqual(@as(?usize, null), a.get(.c));
1239 testing.expectEqual(@as(?usize, 4), a.get(.d));
1240
1241 testing.expectEqual(@as(?*usize, null), a.getPtr(.a));
1242 testing.expectEqual(@as(?*usize, &a.values[1]), a.getPtr(.b));
1243 testing.expectEqual(@as(?*usize, null), a.getPtr(.c));
1244 testing.expectEqual(@as(?*usize, &a.values[3]), a.getPtr(.d));
1245
1246 testing.expectEqual(@as(?*const usize, null), a.getPtrConst(.a));
1247 testing.expectEqual(@as(?*const usize, &a.values[1]), a.getPtrConst(.b));
1248 testing.expectEqual(@as(?*const usize, null), a.getPtrConst(.c));
1249 testing.expectEqual(@as(?*const usize, &a.values[3]), a.getPtrConst(.d));
1250
1251 testing.expectEqual(@as(*const usize, &a.values[1]), a.getPtrAssertContains(.b));
1252 testing.expectEqual(@as(*const usize, &a.values[3]), a.getPtrAssertContains(.d));
1253 testing.expectEqual(@as(usize, 2), a.getAssertContains(.b));
1254 testing.expectEqual(@as(usize, 4), a.getAssertContains(.d));
1255
1256 a.put(.a, 3);
1257 a.put(.a, 5);
1258 a.putUninitialized(.c).* = 7;
1259 a.putUninitialized(.c).* = 9;
1260
1261 testing.expectEqual(@as(usize, 4), a.count());
1262 testing.expectEqual(@as(?usize, 5), a.get(.a));
1263 testing.expectEqual(@as(?usize, 2), a.get(.b));
1264 testing.expectEqual(@as(?usize, 9), a.get(.c));
1265 testing.expectEqual(@as(?usize, 4), a.get(.d));
1266
1267 a.remove(.a);
1268 testing.expectEqual(@as(?usize, null), a.fetchRemove(.a));
1269 testing.expectEqual(@as(?usize, 9), a.fetchRemove(.c));
1270 a.remove(.c);
1271
1272 var iter = a.iterator();
1273 const Entry = Map.Entry;
1274 testing.expectEqual(@as(?Entry, Entry{
1275 .key = .b, .value = &a.values[1],
1276 }), iter.next());
1277 testing.expectEqual(@as(?Entry, Entry{
1278 .key = .d, .value = &a.values[3],
1279 }), iter.next());
1280 testing.expectEqual(@as(?Entry, null), iter.next());
1281}
lib/std/fmt.zig+7-2
......@@ -1250,9 +1250,9 @@ fn formatDuration(ns: u64, comptime fmt: []const u8, options: std.fmt.FormatOpti
12501250 const kunits = ns_remaining * 1000 / unit.ns;
12511251 if (kunits >= 1000) {
12521252 try formatInt(kunits / 1000, 10, false, .{}, writer);
1253 if (kunits > 1000) {
1253 const frac = kunits % 1000;
1254 if (frac > 0) {
12541255 // Write up to 3 decimal places
1255 const frac = kunits % 1000;
12561256 var buf = [_]u8{ '.', 0, 0, 0 };
12571257 _ = formatIntBuf(buf[1..], frac, 10, false, .{ .fill = '0', .width = 3 });
12581258 var end: usize = 4;
......@@ -1286,9 +1286,14 @@ test "fmtDuration" {
12861286 .{ .s = "1us", .d = std.time.ns_per_us },
12871287 .{ .s = "1.45us", .d = 1450 },
12881288 .{ .s = "1.5us", .d = 3 * std.time.ns_per_us / 2 },
1289 .{ .s = "14.5us", .d = 14500 },
1290 .{ .s = "145us", .d = 145000 },
12891291 .{ .s = "999.999us", .d = std.time.ns_per_ms - 1 },
12901292 .{ .s = "1ms", .d = std.time.ns_per_ms + 1 },
12911293 .{ .s = "1.5ms", .d = 3 * std.time.ns_per_ms / 2 },
1294 .{ .s = "1.11ms", .d = 1110000 },
1295 .{ .s = "1.111ms", .d = 1111000 },
1296 .{ .s = "1.111ms", .d = 1111100 },
12921297 .{ .s = "999.999ms", .d = std.time.ns_per_s - 1 },
12931298 .{ .s = "1s", .d = std.time.ns_per_s },
12941299 .{ .s = "59.999s", .d = std.time.ns_per_min - 1 },
lib/std/fs.zig+5-5
......@@ -50,13 +50,13 @@ pub const MAX_PATH_BYTES = switch (builtin.os.tag) {
5050 else => @compileError("Unsupported OS"),
5151};
5252
53pub const base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
53pub const base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".*;
5454
5555/// Base64 encoder, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem.
56pub const base64_encoder = base64.Base64Encoder.init(base64_alphabet, base64.standard_pad_char);
56pub const base64_encoder = base64.Base64Encoder.init(base64_alphabet, null);
5757
5858/// Base64 decoder, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem.
59pub const base64_decoder = base64.Base64Decoder.init(base64_alphabet, base64.standard_pad_char);
59pub const base64_decoder = base64.Base64Decoder.init(base64_alphabet, null);
6060
6161/// Whether or not async file system syscalls need a dedicated thread because the operating
6262/// system does not support non-blocking I/O on the file system.
......@@ -77,7 +77,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
7777 const dirname = path.dirname(new_path) orelse ".";
7878
7979 var rand_buf: [AtomicFile.RANDOM_BYTES]u8 = undefined;
80 const tmp_path = try allocator.alloc(u8, dirname.len + 1 + base64.Base64Encoder.calcSize(rand_buf.len));
80 const tmp_path = try allocator.alloc(u8, dirname.len + 1 + base64_encoder.calcSize(rand_buf.len));
8181 defer allocator.free(tmp_path);
8282 mem.copy(u8, tmp_path[0..], dirname);
8383 tmp_path[dirname.len] = path.sep;
......@@ -142,7 +142,7 @@ pub const AtomicFile = struct {
142142 const InitError = File.OpenError;
143143
144144 const RANDOM_BYTES = 12;
145 const TMP_PATH_LEN = base64.Base64Encoder.calcSize(RANDOM_BYTES);
145 const TMP_PATH_LEN = base64_encoder.calcSize(RANDOM_BYTES);
146146
147147 /// Note that the `Dir.atomicFile` API may be more handy than this lower-level function.
148148 pub fn init(
lib/std/fs/path.zig+11-1
......@@ -92,7 +92,7 @@ pub fn join(allocator: *Allocator, paths: []const []const u8) ![]u8 {
9292/// Naively combines a series of paths with the native path seperator and null terminator.
9393/// Allocates memory for the result, which must be freed by the caller.
9494pub fn joinZ(allocator: *Allocator, paths: []const []const u8) ![:0]u8 {
95 const out = joinSepMaybeZ(allocator, sep, isSep, paths, true);
95 const out = try joinSepMaybeZ(allocator, sep, isSep, paths, true);
9696 return out[0 .. out.len - 1 :0];
9797}
9898
......@@ -119,6 +119,16 @@ fn testJoinMaybeZPosix(paths: []const []const u8, expected: []const u8, zero: bo
119119}
120120
121121test "join" {
122 {
123 const actual: []u8 = try join(testing.allocator, &[_][]const u8{});
124 defer testing.allocator.free(actual);
125 testing.expectEqualSlices(u8, "", actual);
126 }
127 {
128 const actual: [:0]u8 = try joinZ(testing.allocator, &[_][]const u8{});
129 defer testing.allocator.free(actual);
130 testing.expectEqualSlices(u8, "", actual);
131 }
122132 for (&[_]bool{ false, true }) |zero| {
123133 testJoinMaybeZWindows(&[_][]const u8{}, "", zero);
124134 testJoinMaybeZWindows(&[_][]const u8{ "c:\\a\\b", "c" }, "c:\\a\\b\\c", zero);
lib/std/hash/auto_hash.zig+1-1
......@@ -95,7 +95,7 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
9595 .EnumLiteral,
9696 .Frame,
9797 .Float,
98 => @compileError("cannot hash this type"),
98 => @compileError("unable to hash type " ++ @typeName(Key)),
9999
100100 // Help the optimizer see that hashing an int is easy by inlining!
101101 // TODO Check if the situation is better after #561 is resolved.
lib/std/macho.zig+40
......@@ -1227,6 +1227,24 @@ pub const S_ATTR_EXT_RELOC = 0x200;
12271227/// section has local relocation entries
12281228pub const S_ATTR_LOC_RELOC = 0x100;
12291229
1230/// template of initial values for TLVs
1231pub const S_THREAD_LOCAL_REGULAR = 0x11;
1232
1233/// template of initial values for TLVs
1234pub const S_THREAD_LOCAL_ZEROFILL = 0x12;
1235
1236/// TLV descriptors
1237pub const S_THREAD_LOCAL_VARIABLES = 0x13;
1238
1239/// pointers to TLV descriptors
1240pub const S_THREAD_LOCAL_VARIABLE_POINTERS = 0x14;
1241
1242/// functions to call to initialize TLV values
1243pub const S_THREAD_LOCAL_INIT_FUNCTION_POINTERS = 0x15;
1244
1245/// 32-bit offsets to initializers
1246pub const S_INIT_FUNC_OFFSETS = 0x16;
1247
12301248pub const cpu_type_t = integer_t;
12311249pub const cpu_subtype_t = integer_t;
12321250pub const integer_t = c_int;
......@@ -1422,6 +1440,14 @@ pub const EXPORT_SYMBOL_FLAGS_KIND_WEAK_DEFINITION: u8 = 0x04;
14221440pub const EXPORT_SYMBOL_FLAGS_REEXPORT: u8 = 0x08;
14231441pub const EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER: u8 = 0x10;
14241442
1443// An indirect symbol table entry is simply a 32bit index into the symbol table
1444// to the symbol that the pointer or stub is refering to. Unless it is for a
1445// non-lazy symbol pointer section for a defined symbol which strip(1) as
1446// removed. In which case it has the value INDIRECT_SYMBOL_LOCAL. If the
1447// symbol was also absolute INDIRECT_SYMBOL_ABS is or'ed with that.
1448pub const INDIRECT_SYMBOL_LOCAL: u32 = 0x80000000;
1449pub const INDIRECT_SYMBOL_ABS: u32 = 0x40000000;
1450
14251451// Codesign consts and structs taken from:
14261452// https://opensource.apple.com/source/xnu/xnu-6153.81.5/osfmk/kern/cs_blobs.h.auto.html
14271453
......@@ -1589,3 +1615,17 @@ pub const GenericBlob = extern struct {
15891615 /// Total length of blob
15901616 length: u32,
15911617};
1618
1619/// The LC_DATA_IN_CODE load commands uses a linkedit_data_command
1620/// to point to an array of data_in_code_entry entries. Each entry
1621/// describes a range of data in a code section.
1622pub const data_in_code_entry = extern struct {
1623 /// From mach_header to start of data range.
1624 offset: u32,
1625
1626 /// Number of bytes in data range.
1627 length: u16,
1628
1629 /// A DICE_KIND value.
1630 kind: u16,
1631};
lib/std/mem.zig+19
......@@ -1373,6 +1373,20 @@ test "mem.tokenize (multibyte)" {
13731373 testing.expect(it.next() == null);
13741374}
13751375
1376test "mem.tokenize (reset)" {
1377 var it = tokenize(" abc def ghi ", " ");
1378 testing.expect(eql(u8, it.next().?, "abc"));
1379 testing.expect(eql(u8, it.next().?, "def"));
1380 testing.expect(eql(u8, it.next().?, "ghi"));
1381
1382 it.reset();
1383
1384 testing.expect(eql(u8, it.next().?, "abc"));
1385 testing.expect(eql(u8, it.next().?, "def"));
1386 testing.expect(eql(u8, it.next().?, "ghi"));
1387 testing.expect(it.next() == null);
1388}
1389
13761390/// Returns an iterator that iterates over the slices of `buffer` that
13771391/// are separated by bytes in `delimiter`.
13781392/// split("abc|def||ghi", "|")
......@@ -1471,6 +1485,11 @@ pub const TokenIterator = struct {
14711485 return self.buffer[index..];
14721486 }
14731487
1488 /// Resets the iterator to the initial token.
1489 pub fn reset(self: *TokenIterator) void {
1490 self.index = 0;
1491 }
1492
14741493 fn isSplitByte(self: TokenIterator, byte: u8) bool {
14751494 for (self.delimiter_bytes) |delimiter_byte| {
14761495 if (byte == delimiter_byte) {
lib/std/meta.zig+51-18
......@@ -888,19 +888,20 @@ pub fn Vector(comptime len: u32, comptime child: type) type {
888888/// Given a type and value, cast the value to the type as c would.
889889/// This is for translate-c and is not intended for general use.
890890pub fn cast(comptime DestType: type, target: anytype) DestType {
891 const TargetType = @TypeOf(target);
891 // this function should behave like transCCast in translate-c, except it's for macros
892 const SourceType = @TypeOf(target);
892893 switch (@typeInfo(DestType)) {
893 .Pointer => |dest_ptr| {
894 switch (@typeInfo(TargetType)) {
894 .Pointer => {
895 switch (@typeInfo(SourceType)) {
895896 .Int, .ComptimeInt => {
896897 return @intToPtr(DestType, target);
897898 },
898 .Pointer => |ptr| {
899 return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target));
899 .Pointer => {
900 return castPtr(DestType, target);
900901 },
901902 .Optional => |opt| {
902903 if (@typeInfo(opt.child) == .Pointer) {
903 return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target));
904 return castPtr(DestType, target);
904905 }
905906 },
906907 else => {},
......@@ -908,17 +909,16 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {
908909 },
909910 .Optional => |dest_opt| {
910911 if (@typeInfo(dest_opt.child) == .Pointer) {
911 const dest_ptr = @typeInfo(dest_opt.child).Pointer;
912 switch (@typeInfo(TargetType)) {
912 switch (@typeInfo(SourceType)) {
913913 .Int, .ComptimeInt => {
914914 return @intToPtr(DestType, target);
915915 },
916916 .Pointer => {
917 return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target));
917 return castPtr(DestType, target);
918918 },
919919 .Optional => |target_opt| {
920920 if (@typeInfo(target_opt.child) == .Pointer) {
921 return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target));
921 return castPtr(DestType, target);
922922 }
923923 },
924924 else => {},
......@@ -926,25 +926,25 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {
926926 }
927927 },
928928 .Enum => {
929 if (@typeInfo(TargetType) == .Int or @typeInfo(TargetType) == .ComptimeInt) {
929 if (@typeInfo(SourceType) == .Int or @typeInfo(SourceType) == .ComptimeInt) {
930930 return @intToEnum(DestType, target);
931931 }
932932 },
933 .Int, .ComptimeInt => {
934 switch (@typeInfo(TargetType)) {
933 .Int => {
934 switch (@typeInfo(SourceType)) {
935935 .Pointer => {
936 return @intCast(DestType, @ptrToInt(target));
936 return castInt(DestType, @ptrToInt(target));
937937 },
938938 .Optional => |opt| {
939939 if (@typeInfo(opt.child) == .Pointer) {
940 return @intCast(DestType, @ptrToInt(target));
940 return castInt(DestType, @ptrToInt(target));
941941 }
942942 },
943943 .Enum => {
944 return @intCast(DestType, @enumToInt(target));
944 return castInt(DestType, @enumToInt(target));
945945 },
946 .Int, .ComptimeInt => {
947 return @intCast(DestType, target);
946 .Int => {
947 return castInt(DestType, target);
948948 },
949949 else => {},
950950 }
......@@ -954,6 +954,34 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {
954954 return @as(DestType, target);
955955}
956956
957fn castInt(comptime DestType: type, target: anytype) DestType {
958 const dest = @typeInfo(DestType).Int;
959 const source = @typeInfo(@TypeOf(target)).Int;
960
961 if (dest.bits < source.bits)
962 return @bitCast(DestType, @truncate(Int(source.signedness, dest.bits), target))
963 else
964 return @bitCast(DestType, @as(Int(source.signedness, dest.bits), target));
965}
966
967fn castPtr(comptime DestType: type, target: anytype) DestType {
968 const dest = ptrInfo(DestType);
969 const source = ptrInfo(@TypeOf(target));
970
971 if (source.is_const and !dest.is_const or source.is_volatile and !dest.is_volatile)
972 return @intToPtr(DestType, @ptrToInt(target))
973 else
974 return @ptrCast(DestType, @alignCast(dest.alignment, target));
975}
976
977fn ptrInfo(comptime PtrType: type) TypeInfo.Pointer {
978 return switch(@typeInfo(PtrType)){
979 .Optional => |opt_info| @typeInfo(opt_info.child).Pointer,
980 .Pointer => |ptr_info| ptr_info,
981 else => unreachable,
982 };
983}
984
957985test "std.meta.cast" {
958986 const E = enum(u2) {
959987 Zero,
......@@ -977,6 +1005,11 @@ test "std.meta.cast" {
9771005 testing.expectEqual(@as(u32, 4), cast(u32, @intToPtr(?*u32, 4)));
9781006 testing.expectEqual(@as(u32, 10), cast(u32, @as(u64, 10)));
9791007 testing.expectEqual(@as(u8, 2), cast(u8, E.Two));
1008
1009 testing.expectEqual(@bitCast(i32, @as(u32, 0x8000_0000)), cast(i32, @as(u32, 0x8000_0000)));
1010
1011 testing.expectEqual(@intToPtr(*u8, 2), cast(*u8, @intToPtr(*const u8, 2)));
1012 testing.expectEqual(@intToPtr(*u8, 2), cast(*u8, @intToPtr(*volatile u8, 2)));
9801013}
9811014
9821015/// Given a value returns its size as C's sizeof operator would.
lib/std/meta/trait.zig+78
......@@ -408,6 +408,84 @@ test "std.meta.trait.isTuple" {
408408 testing.expect(isTuple(@TypeOf(t3)));
409409}
410410
411/// Returns true if the passed type will coerce to []const u8.
412/// Any of the following are considered strings:
413/// ```
414/// []const u8, [:S]const u8, *const [N]u8, *const [N:S]u8,
415/// []u8, [:S]u8, *[:S]u8, *[N:S]u8.
416/// ```
417/// These types are not considered strings:
418/// ```
419/// u8, [N]u8, [*]const u8, [*:0]const u8,
420/// [*]const [N]u8, []const u16, []const i8,
421/// *const u8, ?[]const u8, ?*const [N]u8.
422/// ```
423pub fn isZigString(comptime T: type) bool {
424 comptime {
425 // Only pointer types can be strings, no optionals
426 const info = @typeInfo(T);
427 if (info != .Pointer) return false;
428
429 const ptr = &info.Pointer;
430 // Check for CV qualifiers that would prevent coerction to []const u8
431 if (ptr.is_volatile or ptr.is_allowzero) return false;
432
433 // If it's already a slice, simple check.
434 if (ptr.size == .Slice) {
435 return ptr.child == u8;
436 }
437
438 // Otherwise check if it's an array type that coerces to slice.
439 if (ptr.size == .One) {
440 const child = @typeInfo(ptr.child);
441 if (child == .Array) {
442 const arr = &child.Array;
443 return arr.child == u8;
444 }
445 }
446
447 return false;
448 }
449}
450
451test "std.meta.trait.isZigString" {
452 testing.expect(isZigString([]const u8));
453 testing.expect(isZigString([]u8));
454 testing.expect(isZigString([:0]const u8));
455 testing.expect(isZigString([:0]u8));
456 testing.expect(isZigString([:5]const u8));
457 testing.expect(isZigString([:5]u8));
458 testing.expect(isZigString(*const [0]u8));
459 testing.expect(isZigString(*[0]u8));
460 testing.expect(isZigString(*const [0:0]u8));
461 testing.expect(isZigString(*[0:0]u8));
462 testing.expect(isZigString(*const [0:5]u8));
463 testing.expect(isZigString(*[0:5]u8));
464 testing.expect(isZigString(*const [10]u8));
465 testing.expect(isZigString(*[10]u8));
466 testing.expect(isZigString(*const [10:0]u8));
467 testing.expect(isZigString(*[10:0]u8));
468 testing.expect(isZigString(*const [10:5]u8));
469 testing.expect(isZigString(*[10:5]u8));
470
471 testing.expect(!isZigString(u8));
472 testing.expect(!isZigString([4]u8));
473 testing.expect(!isZigString([4:0]u8));
474 testing.expect(!isZigString([*]const u8));
475 testing.expect(!isZigString([*]const [4]u8));
476 testing.expect(!isZigString([*c]const u8));
477 testing.expect(!isZigString([*c]const [4]u8));
478 testing.expect(!isZigString([*:0]const u8));
479 testing.expect(!isZigString([*:0]const u8));
480 testing.expect(!isZigString(*[]const u8));
481 testing.expect(!isZigString(?[]const u8));
482 testing.expect(!isZigString(?*const [4]u8));
483 testing.expect(!isZigString([]allowzero u8));
484 testing.expect(!isZigString([]volatile u8));
485 testing.expect(!isZigString(*allowzero [4]u8));
486 testing.expect(!isZigString(*volatile [4]u8));
487}
488
411489pub fn hasDecls(comptime T: type, comptime names: anytype) bool {
412490 inline for (names) |name| {
413491 if (!@hasDecl(T, name))
lib/std/os.zig+3-2
......@@ -2879,7 +2879,7 @@ pub fn bind(sock: socket_t, addr: *const sockaddr, len: socklen_t) BindError!voi
28792879 unreachable;
28802880}
28812881
2882const ListenError = error{
2882pub const ListenError = error{
28832883 /// Another socket is already listening on the same port.
28842884 /// For Internet domain sockets, the socket referred to by sockfd had not previously
28852885 /// been bound to an address and, upon attempting to bind it to an ephemeral port, it
......@@ -5610,6 +5610,7 @@ pub fn recvfrom(
56105610 EAGAIN => return error.WouldBlock,
56115611 ENOMEM => return error.SystemResources,
56125612 ECONNREFUSED => return error.ConnectionRefused,
5613 ECONNRESET => return error.ConnectionResetByPeer,
56135614 else => |err| return unexpectedErrno(err),
56145615 }
56155616 }
......@@ -5827,7 +5828,7 @@ pub fn tcsetattr(handle: fd_t, optional_action: TCSA, termios_p: termios) Termio
58275828 }
58285829}
58295830
5830const IoCtl_SIOCGIFINDEX_Error = error{
5831pub const IoCtl_SIOCGIFINDEX_Error = error{
58315832 FileSystem,
58325833 InterfaceNotFound,
58335834} || UnexpectedError;
lib/std/os/linux/io_uring.zig+1-1
......@@ -1353,7 +1353,7 @@ test "timeout (after a relative time)" {
13531353 .res = -linux.ETIME,
13541354 .flags = 0,
13551355 }, cqe);
1356 testing.expectWithinMargin(@intToFloat(f64, ms), @intToFloat(f64, stopped - started), margin);
1356 testing.expectApproxEqAbs(@intToFloat(f64, ms), @intToFloat(f64, stopped - started), margin);
13571357}
13581358
13591359test "timeout (after a number of completions)" {
lib/std/os/linux/mips.zig+37
......@@ -115,6 +115,9 @@ pub fn syscall5(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize,
115115 );
116116}
117117
118// NOTE: The o32 calling convention requires the callee to reserve 16 bytes for
119// the first four arguments even though they're passed in $a0-$a3.
120
118121pub fn syscall6(
119122 number: SYS,
120123 arg1: usize,
......@@ -146,6 +149,40 @@ pub fn syscall6(
146149 );
147150}
148151
152pub fn syscall7(
153 number: SYS,
154 arg1: usize,
155 arg2: usize,
156 arg3: usize,
157 arg4: usize,
158 arg5: usize,
159 arg6: usize,
160 arg7: usize,
161) usize {
162 return asm volatile (
163 \\ .set noat
164 \\ subu $sp, $sp, 32
165 \\ sw %[arg5], 16($sp)
166 \\ sw %[arg6], 20($sp)
167 \\ sw %[arg7], 24($sp)
168 \\ syscall
169 \\ addu $sp, $sp, 32
170 \\ blez $7, 1f
171 \\ subu $2, $0, $2
172 \\ 1:
173 : [ret] "={$2}" (-> usize)
174 : [number] "{$2}" (@enumToInt(number)),
175 [arg1] "{$4}" (arg1),
176 [arg2] "{$5}" (arg2),
177 [arg3] "{$6}" (arg3),
178 [arg4] "{$7}" (arg4),
179 [arg5] "r" (arg5),
180 [arg6] "r" (arg6),
181 [arg7] "r" (arg7)
182 : "memory", "cc", "$7"
183 );
184}
185
149186/// This matches the libc clone function.
150187pub extern fn clone(func: fn (arg: usize) callconv(.C) u8, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize;
151188
lib/std/os/uefi/tables/boot_services.zig+2-1
......@@ -78,7 +78,8 @@ pub const BootServices = extern struct {
7878 /// Returns an array of handles that support a specified protocol.
7979 locateHandle: fn (LocateSearchType, ?*align(8) const Guid, ?*const c_void, *usize, [*]Handle) callconv(.C) Status,
8080
81 locateDevicePath: Status, // TODO
81 /// Locates the handle to a device on the device path that supports the specified protocol
82 locateDevicePath: fn (*align(8) const Guid, **const DevicePathProtocol, *?Handle) callconv(.C) Status,
8283 installConfigurationTable: Status, // TODO
8384
8485 /// Loads an EFI image into memory.
lib/std/os/windows/user32.zig+1-1
......@@ -373,7 +373,7 @@ pub fn createWindowExA(dwExStyle: u32, lpClassName: [*:0]const u8, lpWindowName:
373373}
374374
375375pub extern "user32" fn CreateWindowExW(dwExStyle: DWORD, lpClassName: [*:0]const u16, lpWindowName: [*:0]const u16, dwStyle: DWORD, X: i32, Y: i32, nWidth: i32, nHeight: i32, hWindParent: ?HWND, hMenu: ?HMENU, hInstance: HINSTANCE, lpParam: ?LPVOID) callconv(WINAPI) ?HWND;
376pub var pfnCreateWindowExW: @TypeOf(RegisterClassExW) = undefined;
376pub var pfnCreateWindowExW: @TypeOf(CreateWindowExW) = undefined;
377377pub fn createWindowExW(dwExStyle: u32, lpClassName: [*:0]const u16, lpWindowName: [*:0]const u16, dwStyle: u32, X: i32, Y: i32, nWidth: i32, nHeight: i32, hWindParent: ?HWND, hMenu: ?HMENU, hInstance: HINSTANCE, lpParam: ?*c_void) !HWND {
378378 const function = selectSymbol(CreateWindowExW, pfnCreateWindowExW, .win2k);
379379 const window = function(dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWindParent, hMenu, hInstance, lpParam);
lib/std/special/build_runner.zig+4-4
......@@ -60,6 +60,7 @@ pub fn main() !void {
6060 const stderr_stream = io.getStdErr().writer();
6161 const stdout_stream = io.getStdOut().writer();
6262
63 var install_prefix: ?[]const u8 = null;
6364 while (nextArg(args, &arg_idx)) |arg| {
6465 if (mem.startsWith(u8, arg, "-D")) {
6566 const option_contents = arg[2..];
......@@ -82,7 +83,7 @@ pub fn main() !void {
8283 } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
8384 return usage(builder, false, stdout_stream);
8485 } else if (mem.eql(u8, arg, "--prefix")) {
85 builder.install_prefix = nextArg(args, &arg_idx) orelse {
86 install_prefix = nextArg(args, &arg_idx) orelse {
8687 warn("Expected argument after --prefix\n\n", .{});
8788 return usageAndErr(builder, false, stderr_stream);
8889 };
......@@ -134,7 +135,7 @@ pub fn main() !void {
134135 }
135136 }
136137
137 builder.resolveInstallPrefix();
138 builder.resolveInstallPrefix(install_prefix);
138139 try runBuild(builder);
139140
140141 if (builder.validateUserInputDidItFail())
......@@ -162,8 +163,7 @@ fn runBuild(builder: *Builder) anyerror!void {
162163fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void {
163164 // run the build script to collect the options
164165 if (!already_ran_build) {
165 builder.setInstallPrefix(null);
166 builder.resolveInstallPrefix();
166 builder.resolveInstallPrefix(null);
167167 try runBuild(builder);
168168 }
169169
lib/std/std.zig+4
......@@ -20,6 +20,9 @@ pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringM
2020pub const DynLib = @import("dynamic_library.zig").DynLib;
2121pub const DynamicBitSet = bit_set.DynamicBitSet;
2222pub const DynamicBitSetUnmanaged = bit_set.DynamicBitSetUnmanaged;
23pub const EnumArray = enums.EnumArray;
24pub const EnumMap = enums.EnumMap;
25pub const EnumSet = enums.EnumSet;
2326pub const HashMap = hash_map.HashMap;
2427pub const HashMapUnmanaged = hash_map.HashMapUnmanaged;
2528pub const MultiArrayList = @import("multi_array_list.zig").MultiArrayList;
......@@ -54,6 +57,7 @@ pub const cstr = @import("cstr.zig");
5457pub const debug = @import("debug.zig");
5558pub const dwarf = @import("dwarf.zig");
5659pub const elf = @import("elf.zig");
60pub const enums = @import("enums.zig");
5761pub const event = @import("event.zig");
5862pub const fifo = @import("fifo.zig");
5963pub const fmt = @import("fmt.zig");
lib/std/testing.zig+39-37
......@@ -200,67 +200,69 @@ pub fn expectFmt(expected: []const u8, comptime template: []const u8, args: anyt
200200 return error.TestFailed;
201201}
202202
203/// This function is intended to be used only in tests. When the actual value is not
204/// within the margin of the expected value,
205/// prints diagnostics to stderr to show exactly how they are not equal, then aborts.
203pub const expectWithinMargin = @compileError("expectWithinMargin is deprecated, use expectApproxEqAbs or expectApproxEqRel");
204pub const expectWithinEpsilon = @compileError("expectWithinEpsilon is deprecated, use expectApproxEqAbs or expectApproxEqRel");
205
206/// This function is intended to be used only in tests. When the actual value is
207/// not approximately equal to the expected value, prints diagnostics to stderr
208/// to show exactly how they are not equal, then aborts.
209/// See `math.approxEqAbs` for more informations on the tolerance parameter.
206210/// The types must be floating point
207pub fn expectWithinMargin(expected: anytype, actual: @TypeOf(expected), margin: @TypeOf(expected)) void {
208 std.debug.assert(margin >= 0.0);
211pub fn expectApproxEqAbs(expected: anytype, actual: @TypeOf(expected), tolerance: @TypeOf(expected)) void {
212 const T = @TypeOf(expected);
213
214 switch (@typeInfo(T)) {
215 .Float => if (!math.approxEqAbs(T, expected, actual, tolerance))
216 std.debug.panic("actual {}, not within absolute tolerance {} of expected {}", .{ actual, tolerance, expected }),
217
218 .ComptimeFloat => @compileError("Cannot approximately compare two comptime_float values"),
209219
210 switch (@typeInfo(@TypeOf(actual))) {
211 .Float,
212 .ComptimeFloat,
213 => {
214 if (@fabs(expected - actual) > margin) {
215 std.debug.panic("actual {}, not within margin {} of expected {}", .{ actual, margin, expected });
216 }
217 },
218220 else => @compileError("Unable to compare non floating point values"),
219221 }
220222}
221223
222test "expectWithinMargin" {
224test "expectApproxEqAbs" {
223225 inline for ([_]type{ f16, f32, f64, f128 }) |T| {
224226 const pos_x: T = 12.0;
225227 const pos_y: T = 12.06;
226228 const neg_x: T = -12.0;
227229 const neg_y: T = -12.06;
228230
229 expectWithinMargin(pos_x, pos_y, 0.1);
230 expectWithinMargin(neg_x, neg_y, 0.1);
231 expectApproxEqAbs(pos_x, pos_y, 0.1);
232 expectApproxEqAbs(neg_x, neg_y, 0.1);
231233 }
232234}
233235
234/// This function is intended to be used only in tests. When the actual value is not
235/// within the epsilon of the expected value,
236/// prints diagnostics to stderr to show exactly how they are not equal, then aborts.
236/// This function is intended to be used only in tests. When the actual value is
237/// not approximately equal to the expected value, prints diagnostics to stderr
238/// to show exactly how they are not equal, then aborts.
239/// See `math.approxEqRel` for more informations on the tolerance parameter.
237240/// The types must be floating point
238pub fn expectWithinEpsilon(expected: anytype, actual: @TypeOf(expected), epsilon: @TypeOf(expected)) void {
239 std.debug.assert(epsilon >= 0.0 and epsilon <= 1.0);
241pub fn expectApproxEqRel(expected: anytype, actual: @TypeOf(expected), tolerance: @TypeOf(expected)) void {
242 const T = @TypeOf(expected);
243
244 switch (@typeInfo(T)) {
245 .Float => if (!math.approxEqRel(T, expected, actual, tolerance))
246 std.debug.panic("actual {}, not within relative tolerance {} of expected {}", .{ actual, tolerance, expected }),
247
248 .ComptimeFloat => @compileError("Cannot approximately compare two comptime_float values"),
240249
241 // Relative epsilon test.
242 const margin = math.max(math.fabs(expected), math.fabs(actual)) * epsilon;
243 switch (@typeInfo(@TypeOf(actual))) {
244 .Float,
245 .ComptimeFloat,
246 => {
247 if (@fabs(expected - actual) > margin) {
248 std.debug.panic("actual {}, not within epsilon {}, of expected {}", .{ actual, epsilon, expected });
249 }
250 },
251250 else => @compileError("Unable to compare non floating point values"),
252251 }
253252}
254253
255test "expectWithinEpsilon" {
254test "expectApproxEqRel" {
256255 inline for ([_]type{ f16, f32, f64, f128 }) |T| {
256 const eps_value = comptime math.epsilon(T);
257 const sqrt_eps_value = comptime math.sqrt(eps_value);
258
257259 const pos_x: T = 12.0;
258 const pos_y: T = 13.2;
260 const pos_y: T = pos_x + 2 * eps_value;
259261 const neg_x: T = -12.0;
260 const neg_y: T = -13.2;
262 const neg_y: T = neg_x - 2 * eps_value;
261263
262 expectWithinEpsilon(pos_x, pos_y, 0.1);
263 expectWithinEpsilon(neg_x, neg_y, 0.1);
264 expectApproxEqRel(pos_x, pos_y, sqrt_eps_value);
265 expectApproxEqRel(neg_x, neg_y, sqrt_eps_value);
264266 }
265267}
266268
......@@ -296,7 +298,7 @@ pub const TmpDir = struct {
296298 sub_path: [sub_path_len]u8,
297299
298300 const random_bytes_count = 12;
299 const sub_path_len = std.base64.Base64Encoder.calcSize(random_bytes_count);
301 const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count);
300302
301303 pub fn cleanup(self: *TmpDir) void {
302304 self.dir.close();
lib/std/zig/parser_test.zig+319
......@@ -4,6 +4,31 @@
44// The MIT license requires this copyright notice to be included in all copies
55// and substantial portions of the software.
66
7test "zig fmt: respect line breaks in struct field value declaration" {
8 try testCanonical(
9 \\const Foo = struct {
10 \\ bar: u32 =
11 \\ 42,
12 \\ bar: u32 =
13 \\ // a comment
14 \\ 42,
15 \\ bar: u32 =
16 \\ 42,
17 \\ // a comment
18 \\ bar: []const u8 =
19 \\ \\ foo
20 \\ \\ bar
21 \\ \\ baz
22 \\ ,
23 \\ bar: u32 =
24 \\ blk: {
25 \\ break :blk 42;
26 \\ },
27 \\};
28 \\
29 );
30}
31
732// TODO Remove this after zig 0.9.0 is released.
833test "zig fmt: rewrite inline functions as callconv(.Inline)" {
934 try testTransform(
......@@ -3038,6 +3063,54 @@ test "zig fmt: switch" {
30383063 \\}
30393064 \\
30403065 );
3066
3067 try testTransform(
3068 \\test {
3069 \\ switch (x) {
3070 \\ foo =>
3071 \\ "bar",
3072 \\ }
3073 \\}
3074 \\
3075 ,
3076 \\test {
3077 \\ switch (x) {
3078 \\ foo => "bar",
3079 \\ }
3080 \\}
3081 \\
3082 );
3083}
3084
3085test "zig fmt: switch multiline string" {
3086 try testCanonical(
3087 \\test "switch multiline string" {
3088 \\ const x: u32 = 0;
3089 \\ const str = switch (x) {
3090 \\ 1 => "one",
3091 \\ 2 =>
3092 \\ \\ Comma after the multiline string
3093 \\ \\ is needed
3094 \\ ,
3095 \\ 3 => "three",
3096 \\ else => "else",
3097 \\ };
3098 \\
3099 \\ const Union = union(enum) {
3100 \\ Int: i64,
3101 \\ Float: f64,
3102 \\ };
3103 \\
3104 \\ const str = switch (u) {
3105 \\ Union.Int => |int|
3106 \\ \\ Comma after the multiline string
3107 \\ \\ is needed
3108 \\ ,
3109 \\ Union.Float => |*float| unreachable,
3110 \\ };
3111 \\}
3112 \\
3113 );
30413114}
30423115
30433116test "zig fmt: while" {
......@@ -3068,6 +3141,11 @@ test "zig fmt: while" {
30683141 \\ while (i < 10) : ({
30693142 \\ i += 1;
30703143 \\ j += 1;
3144 \\ }) continue;
3145 \\
3146 \\ while (i < 10) : ({
3147 \\ i += 1;
3148 \\ j += 1;
30713149 \\ }) {
30723150 \\ continue;
30733151 \\ }
......@@ -3184,6 +3262,156 @@ test "zig fmt: for" {
31843262 );
31853263}
31863264
3265test "zig fmt: for if" {
3266 try testCanonical(
3267 \\test {
3268 \\ for (a) |x| if (x) f(x);
3269 \\
3270 \\ for (a) |x| if (x)
3271 \\ f(x);
3272 \\
3273 \\ for (a) |x| if (x) {
3274 \\ f(x);
3275 \\ };
3276 \\
3277 \\ for (a) |x|
3278 \\ if (x)
3279 \\ f(x);
3280 \\
3281 \\ for (a) |x|
3282 \\ if (x) {
3283 \\ f(x);
3284 \\ };
3285 \\}
3286 \\
3287 );
3288}
3289
3290test "zig fmt: if for" {
3291 try testCanonical(
3292 \\test {
3293 \\ if (a) for (x) |x| f(x);
3294 \\
3295 \\ if (a) for (x) |x|
3296 \\ f(x);
3297 \\
3298 \\ if (a) for (x) |x| {
3299 \\ f(x);
3300 \\ };
3301 \\
3302 \\ if (a)
3303 \\ for (x) |x|
3304 \\ f(x);
3305 \\
3306 \\ if (a)
3307 \\ for (x) |x| {
3308 \\ f(x);
3309 \\ };
3310 \\}
3311 \\
3312 );
3313}
3314
3315test "zig fmt: while if" {
3316 try testCanonical(
3317 \\test {
3318 \\ while (a) if (x) f(x);
3319 \\
3320 \\ while (a) if (x)
3321 \\ f(x);
3322 \\
3323 \\ while (a) if (x) {
3324 \\ f(x);
3325 \\ };
3326 \\
3327 \\ while (a)
3328 \\ if (x)
3329 \\ f(x);
3330 \\
3331 \\ while (a)
3332 \\ if (x) {
3333 \\ f(x);
3334 \\ };
3335 \\}
3336 \\
3337 );
3338}
3339
3340test "zig fmt: if while" {
3341 try testCanonical(
3342 \\test {
3343 \\ if (a) while (x) : (cont) f(x);
3344 \\
3345 \\ if (a) while (x) : (cont)
3346 \\ f(x);
3347 \\
3348 \\ if (a) while (x) : (cont) {
3349 \\ f(x);
3350 \\ };
3351 \\
3352 \\ if (a)
3353 \\ while (x) : (cont)
3354 \\ f(x);
3355 \\
3356 \\ if (a)
3357 \\ while (x) : (cont) {
3358 \\ f(x);
3359 \\ };
3360 \\}
3361 \\
3362 );
3363}
3364
3365test "zig fmt: while for" {
3366 try testCanonical(
3367 \\test {
3368 \\ while (a) for (x) |x| f(x);
3369 \\
3370 \\ while (a) for (x) |x|
3371 \\ f(x);
3372 \\
3373 \\ while (a) for (x) |x| {
3374 \\ f(x);
3375 \\ };
3376 \\
3377 \\ while (a)
3378 \\ for (x) |x|
3379 \\ f(x);
3380 \\
3381 \\ while (a)
3382 \\ for (x) |x| {
3383 \\ f(x);
3384 \\ };
3385 \\}
3386 \\
3387 );
3388}
3389
3390test "zig fmt: for while" {
3391 try testCanonical(
3392 \\test {
3393 \\ for (a) |a| while (x) |x| f(x);
3394 \\
3395 \\ for (a) |a| while (x) |x|
3396 \\ f(x);
3397 \\
3398 \\ for (a) |a| while (x) |x| {
3399 \\ f(x);
3400 \\ };
3401 \\
3402 \\ for (a) |a|
3403 \\ while (x) |x|
3404 \\ f(x);
3405 \\
3406 \\ for (a) |a|
3407 \\ while (x) |x| {
3408 \\ f(x);
3409 \\ };
3410 \\}
3411 \\
3412 );
3413}
3414
31873415test "zig fmt: if" {
31883416 try testCanonical(
31893417 \\test "if" {
......@@ -3233,6 +3461,82 @@ test "zig fmt: if" {
32333461 );
32343462}
32353463
3464test "zig fmt: fix single statement if/for/while line breaks" {
3465 try testTransform(
3466 \\test {
3467 \\ if (cond) a
3468 \\ else b;
3469 \\
3470 \\ if (cond)
3471 \\ a
3472 \\ else b;
3473 \\
3474 \\ for (xs) |x| foo()
3475 \\ else bar();
3476 \\
3477 \\ for (xs) |x|
3478 \\ foo()
3479 \\ else bar();
3480 \\
3481 \\ while (a) : (b) foo()
3482 \\ else bar();
3483 \\
3484 \\ while (a) : (b)
3485 \\ foo()
3486 \\ else bar();
3487 \\}
3488 \\
3489 ,
3490 \\test {
3491 \\ if (cond) a else b;
3492 \\
3493 \\ if (cond)
3494 \\ a
3495 \\ else
3496 \\ b;
3497 \\
3498 \\ for (xs) |x| foo() else bar();
3499 \\
3500 \\ for (xs) |x|
3501 \\ foo()
3502 \\ else
3503 \\ bar();
3504 \\
3505 \\ while (a) : (b) foo() else bar();
3506 \\
3507 \\ while (a) : (b)
3508 \\ foo()
3509 \\ else
3510 \\ bar();
3511 \\}
3512 \\
3513 );
3514}
3515
3516test "zig fmt: anon struct/array literal in if" {
3517 try testCanonical(
3518 \\test {
3519 \\ const a = if (cond) .{
3520 \\ 1, 2,
3521 \\ 3, 4,
3522 \\ } else .{
3523 \\ 1,
3524 \\ 2,
3525 \\ 3,
3526 \\ };
3527 \\
3528 \\ const rl_and_tag: struct { rl: ResultLoc, tag: zir.Inst.Tag } = if (any_payload_is_ref) .{
3529 \\ .rl = .ref,
3530 \\ .tag = .switchbr_ref,
3531 \\ } else .{
3532 \\ .rl = .none,
3533 \\ .tag = .switchbr,
3534 \\ };
3535 \\}
3536 \\
3537 );
3538}
3539
32363540test "zig fmt: defer" {
32373541 try testCanonical(
32383542 \\test "defer" {
......@@ -3820,6 +4124,7 @@ test "zig fmt: comments in ternary ifs" {
38204124 \\ // Comment
38214125 \\ 1
38224126 \\else
4127 \\ // Comment
38234128 \\ 0;
38244129 \\
38254130 \\pub extern "c" fn printf(format: [*:0]const u8, ...) c_int;
......@@ -3827,6 +4132,20 @@ test "zig fmt: comments in ternary ifs" {
38274132 );
38284133}
38294134
4135test "zig fmt: while statement in blockless if" {
4136 try testCanonical(
4137 \\pub fn main() void {
4138 \\ const zoom_node = if (focused_node == layout_first)
4139 \\ while (it.next()) |node| {
4140 \\ if (!node.view.pending.float and !node.view.pending.fullscreen) break node;
4141 \\ } else null
4142 \\ else
4143 \\ focused_node;
4144 \\}
4145 \\
4146 );
4147}
4148
38304149test "zig fmt: test comments in field access chain" {
38314150 try testCanonical(
38324151 \\pub const str = struct {
lib/std/zig/render.zig+98-161
......@@ -1018,147 +1018,14 @@ fn renderWhile(gpa: *Allocator, ais: *Ais, tree: ast.Tree, while_node: ast.full.
10181018 try renderToken(ais, tree, inline_token, .space); // inline
10191019 }
10201020
1021 try renderToken(ais, tree, while_node.ast.while_token, .space); // if
1021 try renderToken(ais, tree, while_node.ast.while_token, .space); // if/for/while
10221022 try renderToken(ais, tree, while_node.ast.while_token + 1, .none); // lparen
10231023 try renderExpression(gpa, ais, tree, while_node.ast.cond_expr, .none); // condition
10241024
1025 const then_tag = node_tags[while_node.ast.then_expr];
1026 if (nodeIsBlock(then_tag) and !nodeIsIf(then_tag)) {
1027 if (while_node.payload_token) |payload_token| {
1028 try renderToken(ais, tree, payload_token - 2, .space); // rparen
1029 try renderToken(ais, tree, payload_token - 1, .none); // |
1030 const ident = blk: {
1031 if (token_tags[payload_token] == .asterisk) {
1032 try renderToken(ais, tree, payload_token, .none); // *
1033 break :blk payload_token + 1;
1034 } else {
1035 break :blk payload_token;
1036 }
1037 };
1038 try renderToken(ais, tree, ident, .none); // identifier
1039 const pipe = blk: {
1040 if (token_tags[ident + 1] == .comma) {
1041 try renderToken(ais, tree, ident + 1, .space); // ,
1042 try renderToken(ais, tree, ident + 2, .none); // index
1043 break :blk ident + 3;
1044 } else {
1045 break :blk ident + 1;
1046 }
1047 };
1048 const brace_space = if (while_node.ast.cont_expr == 0 and ais.isLineOverIndented())
1049 Space.newline
1050 else
1051 Space.space;
1052 try renderToken(ais, tree, pipe, brace_space); // |
1053 } else {
1054 const rparen = tree.lastToken(while_node.ast.cond_expr) + 1;
1055 const brace_space = if (while_node.ast.cont_expr == 0 and ais.isLineOverIndented())
1056 Space.newline
1057 else
1058 Space.space;
1059 try renderToken(ais, tree, rparen, brace_space); // rparen
1060 }
1061 if (while_node.ast.cont_expr != 0) {
1062 const rparen = tree.lastToken(while_node.ast.cont_expr) + 1;
1063 const lparen = tree.firstToken(while_node.ast.cont_expr) - 1;
1064 try renderToken(ais, tree, lparen - 1, .space); // :
1065 try renderToken(ais, tree, lparen, .none); // lparen
1066 try renderExpression(gpa, ais, tree, while_node.ast.cont_expr, .none);
1067 const brace_space: Space = if (ais.isLineOverIndented()) .newline else .space;
1068 try renderToken(ais, tree, rparen, brace_space); // rparen
1069 }
1070 if (while_node.ast.else_expr != 0) {
1071 try renderExpression(gpa, ais, tree, while_node.ast.then_expr, Space.space);
1072 try renderToken(ais, tree, while_node.else_token, .space); // else
1073 if (while_node.error_token) |error_token| {
1074 try renderToken(ais, tree, error_token - 1, .none); // |
1075 try renderToken(ais, tree, error_token, .none); // identifier
1076 try renderToken(ais, tree, error_token + 1, .space); // |
1077 }
1078 return renderExpression(gpa, ais, tree, while_node.ast.else_expr, space);
1079 } else {
1080 return renderExpression(gpa, ais, tree, while_node.ast.then_expr, space);
1081 }
1082 }
1083
1084 const rparen = tree.lastToken(while_node.ast.cond_expr) + 1;
1085 const last_then_token = tree.lastToken(while_node.ast.then_expr);
1086 const src_has_newline = !tree.tokensOnSameLine(rparen, last_then_token);
1087
1088 if (src_has_newline) {
1089 if (while_node.payload_token) |payload_token| {
1090 try renderToken(ais, tree, payload_token - 2, .space); // rparen
1091 try renderToken(ais, tree, payload_token - 1, .none); // |
1092 const ident = blk: {
1093 if (token_tags[payload_token] == .asterisk) {
1094 try renderToken(ais, tree, payload_token, .none); // *
1095 break :blk payload_token + 1;
1096 } else {
1097 break :blk payload_token;
1098 }
1099 };
1100 try renderToken(ais, tree, ident, .none); // identifier
1101 const pipe = blk: {
1102 if (token_tags[ident + 1] == .comma) {
1103 try renderToken(ais, tree, ident + 1, .space); // ,
1104 try renderToken(ais, tree, ident + 2, .none); // index
1105 break :blk ident + 3;
1106 } else {
1107 break :blk ident + 1;
1108 }
1109 };
1110 const after_space: Space = if (while_node.ast.cont_expr != 0) .space else .newline;
1111 try renderToken(ais, tree, pipe, after_space); // |
1112 } else {
1113 ais.pushIndent();
1114 const after_space: Space = if (while_node.ast.cont_expr != 0) .space else .newline;
1115 try renderToken(ais, tree, rparen, after_space); // rparen
1116 ais.popIndent();
1117 }
1118 if (while_node.ast.cont_expr != 0) {
1119 const cont_rparen = tree.lastToken(while_node.ast.cont_expr) + 1;
1120 const cont_lparen = tree.firstToken(while_node.ast.cont_expr) - 1;
1121 try renderToken(ais, tree, cont_lparen - 1, .space); // :
1122 try renderToken(ais, tree, cont_lparen, .none); // lparen
1123 try renderExpression(gpa, ais, tree, while_node.ast.cont_expr, .none);
1124 try renderToken(ais, tree, cont_rparen, .newline); // rparen
1125 }
1126 if (while_node.ast.else_expr != 0) {
1127 ais.pushIndent();
1128 try renderExpression(gpa, ais, tree, while_node.ast.then_expr, Space.newline);
1129 ais.popIndent();
1130 const else_is_block = nodeIsBlock(node_tags[while_node.ast.else_expr]);
1131 if (else_is_block) {
1132 try renderToken(ais, tree, while_node.else_token, .space); // else
1133 if (while_node.error_token) |error_token| {
1134 try renderToken(ais, tree, error_token - 1, .none); // |
1135 try renderToken(ais, tree, error_token, .none); // identifier
1136 try renderToken(ais, tree, error_token + 1, .space); // |
1137 }
1138 return renderExpression(gpa, ais, tree, while_node.ast.else_expr, space);
1139 } else {
1140 if (while_node.error_token) |error_token| {
1141 try renderToken(ais, tree, while_node.else_token, .space); // else
1142 try renderToken(ais, tree, error_token - 1, .none); // |
1143 try renderToken(ais, tree, error_token, .none); // identifier
1144 try renderToken(ais, tree, error_token + 1, .space); // |
1145 } else {
1146 try renderToken(ais, tree, while_node.else_token, .newline); // else
1147 }
1148 try renderExpressionIndented(gpa, ais, tree, while_node.ast.else_expr, space);
1149 return;
1150 }
1151 } else {
1152 try renderExpressionIndented(gpa, ais, tree, while_node.ast.then_expr, space);
1153 return;
1154 }
1155 }
1156
1157 // Render everything on a single line.
1025 var last_prefix_token = tree.lastToken(while_node.ast.cond_expr) + 1; // rparen
11581026
11591027 if (while_node.payload_token) |payload_token| {
1160 assert(payload_token - 2 == rparen);
1161 try renderToken(ais, tree, payload_token - 2, .space); // )
1028 try renderToken(ais, tree, last_prefix_token, .space);
11621029 try renderToken(ais, tree, payload_token - 1, .none); // |
11631030 const ident = blk: {
11641031 if (token_tags[payload_token] == .asterisk) {
......@@ -1178,33 +1045,67 @@ fn renderWhile(gpa: *Allocator, ais: *Ais, tree: ast.Tree, while_node: ast.full.
11781045 break :blk ident + 1;
11791046 }
11801047 };
1181 try renderToken(ais, tree, pipe, .space); // |
1182 } else {
1183 try renderToken(ais, tree, rparen, .space); // )
1048 last_prefix_token = pipe;
11841049 }
11851050
11861051 if (while_node.ast.cont_expr != 0) {
1187 const cont_rparen = tree.lastToken(while_node.ast.cont_expr) + 1;
1188 const cont_lparen = tree.firstToken(while_node.ast.cont_expr) - 1;
1189 try renderToken(ais, tree, cont_lparen - 1, .space); // :
1190 try renderToken(ais, tree, cont_lparen, .none); // lparen
1052 try renderToken(ais, tree, last_prefix_token, .space);
1053 const lparen = tree.firstToken(while_node.ast.cont_expr) - 1;
1054 try renderToken(ais, tree, lparen - 1, .space); // :
1055 try renderToken(ais, tree, lparen, .none); // lparen
11911056 try renderExpression(gpa, ais, tree, while_node.ast.cont_expr, .none);
1192 try renderToken(ais, tree, cont_rparen, .space); // rparen
1057 last_prefix_token = tree.lastToken(while_node.ast.cont_expr) + 1; // rparen
1058 }
1059
1060 const then_expr_is_block = nodeIsBlock(node_tags[while_node.ast.then_expr]);
1061 const indent_then_expr = !then_expr_is_block and
1062 !tree.tokensOnSameLine(last_prefix_token, tree.firstToken(while_node.ast.then_expr));
1063 if (indent_then_expr or (then_expr_is_block and ais.isLineOverIndented())) {
1064 ais.pushIndentNextLine();
1065 try renderToken(ais, tree, last_prefix_token, .newline);
1066 ais.popIndent();
1067 } else {
1068 try renderToken(ais, tree, last_prefix_token, .space);
11931069 }
11941070
11951071 if (while_node.ast.else_expr != 0) {
1196 try renderExpression(gpa, ais, tree, while_node.ast.then_expr, .space);
1197 try renderToken(ais, tree, while_node.else_token, .space); // else
1072 const first_else_expr_tok = tree.firstToken(while_node.ast.else_expr);
1073
1074 if (indent_then_expr) {
1075 ais.pushIndent();
1076 try renderExpression(gpa, ais, tree, while_node.ast.then_expr, .newline);
1077 ais.popIndent();
1078 } else {
1079 try renderExpression(gpa, ais, tree, while_node.ast.then_expr, .space);
1080 }
1081
1082 var last_else_token = while_node.else_token;
11981083
11991084 if (while_node.error_token) |error_token| {
1085 try renderToken(ais, tree, while_node.else_token, .space); // else
12001086 try renderToken(ais, tree, error_token - 1, .none); // |
12011087 try renderToken(ais, tree, error_token, .none); // identifier
1202 try renderToken(ais, tree, error_token + 1, .space); // |
1088 last_else_token = error_token + 1; // |
12031089 }
12041090
1205 return renderExpression(gpa, ais, tree, while_node.ast.else_expr, space);
1091 const indent_else_expr = indent_then_expr and
1092 !nodeIsBlock(node_tags[while_node.ast.else_expr]) and
1093 !nodeIsIfForWhileSwitch(node_tags[while_node.ast.else_expr]);
1094 if (indent_else_expr) {
1095 ais.pushIndentNextLine();
1096 try renderToken(ais, tree, last_else_token, .newline);
1097 ais.popIndent();
1098 try renderExpressionIndented(gpa, ais, tree, while_node.ast.else_expr, space);
1099 } else {
1100 try renderToken(ais, tree, last_else_token, .space);
1101 try renderExpression(gpa, ais, tree, while_node.ast.else_expr, space);
1102 }
12061103 } else {
1207 return renderExpression(gpa, ais, tree, while_node.ast.then_expr, space);
1104 if (indent_then_expr) {
1105 try renderExpressionIndented(gpa, ais, tree, while_node.ast.then_expr, space);
1106 } else {
1107 try renderExpression(gpa, ais, tree, while_node.ast.then_expr, space);
1108 }
12081109 }
12091110}
12101111
......@@ -1258,8 +1159,29 @@ fn renderContainerField(
12581159 try renderToken(ais, tree, rparen_token, .space); // )
12591160 }
12601161 const eq_token = tree.firstToken(field.ast.value_expr) - 1;
1261 try renderToken(ais, tree, eq_token, .space); // =
1262 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 }
12631185}
12641186
12651187fn renderBuiltinCall(
......@@ -1522,6 +1444,7 @@ fn renderSwitchCase(
15221444 switch_case: ast.full.SwitchCase,
15231445 space: Space,
15241446) Error!void {
1447 const node_tags = tree.nodes.items(.tag);
15251448 const token_tags = tree.tokens.items(.tag);
15261449 const trailing_comma = token_tags[switch_case.ast.arrow_token - 1] == .comma;
15271450
......@@ -1544,17 +1467,23 @@ fn renderSwitchCase(
15441467 }
15451468
15461469 // Render the arrow and everything after it
1547 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);
15481477
15491478 if (switch_case.payload_token) |payload_token| {
15501479 try renderToken(ais, tree, payload_token - 1, .none); // pipe
15511480 if (token_tags[payload_token] == .asterisk) {
15521481 try renderToken(ais, tree, payload_token, .none); // asterisk
15531482 try renderToken(ais, tree, payload_token + 1, .none); // identifier
1554 try renderToken(ais, tree, payload_token + 2, .space); // pipe
1483 try renderToken(ais, tree, payload_token + 2, pre_target_space); // pipe
15551484 } else {
15561485 try renderToken(ais, tree, payload_token, .none); // identifier
1557 try renderToken(ais, tree, payload_token + 1, .space); // pipe
1486 try renderToken(ais, tree, payload_token + 1, pre_target_space); // pipe
15581487 }
15591488 }
15601489
......@@ -2493,6 +2422,21 @@ fn nodeIsBlock(tag: ast.Node.Tag) bool {
24932422 .block_semicolon,
24942423 .block_two,
24952424 .block_two_semicolon,
2425 .struct_init_dot,
2426 .struct_init_dot_comma,
2427 .struct_init_dot_two,
2428 .struct_init_dot_two_comma,
2429 .array_init_dot,
2430 .array_init_dot_comma,
2431 .array_init_dot_two,
2432 .array_init_dot_two_comma,
2433 => true,
2434 else => false,
2435 };
2436}
2437
2438fn nodeIsIfForWhileSwitch(tag: ast.Node.Tag) bool {
2439 return switch (tag) {
24962440 .@"if",
24972441 .if_simple,
24982442 .@"for",
......@@ -2507,13 +2451,6 @@ fn nodeIsBlock(tag: ast.Node.Tag) bool {
25072451 };
25082452}
25092453
2510fn nodeIsIf(tag: ast.Node.Tag) bool {
2511 return switch (tag) {
2512 .@"if", .if_simple => true,
2513 else => false,
2514 };
2515}
2516
25172454fn nodeCausesSliceOpSpace(tag: ast.Node.Tag) bool {
25182455 return switch (tag) {
25192456 .@"catch",
src/BuiltinFn.zig+1-1
......@@ -477,7 +477,7 @@ pub const list = list: {
477477 "@intCast",
478478 .{
479479 .tag = .int_cast,
480 .param_count = 1,
480 .param_count = 2,
481481 },
482482 },
483483 .{
src/Compilation.zig+17-6
......@@ -3180,7 +3180,11 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
31803180 id_symlink_basename,
31813181 &prev_digest_buf,
31823182 ) catch |err| blk: {
3183 log.debug("stage1 {s} new_digest={} error: {s}", .{ mod.root_pkg.root_src_path, digest, @errorName(err) });
3183 log.debug("stage1 {s} new_digest={s} error: {s}", .{
3184 mod.root_pkg.root_src_path,
3185 std.fmt.fmtSliceHexLower(&digest),
3186 @errorName(err),
3187 });
31843188 // Handle this as a cache miss.
31853189 break :blk prev_digest_buf[0..0];
31863190 };
......@@ -3188,10 +3192,13 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
31883192 if (!mem.eql(u8, prev_digest[0..digest.len], &digest))
31893193 break :hit;
31903194
3191 log.debug("stage1 {s} digest={} match - skipping invocation", .{ mod.root_pkg.root_src_path, digest });
3195 log.debug("stage1 {s} digest={s} match - skipping invocation", .{
3196 mod.root_pkg.root_src_path,
3197 std.fmt.fmtSliceHexLower(&digest),
3198 });
31923199 var flags_bytes: [1]u8 = undefined;
31933200 _ = std.fmt.hexToBytes(&flags_bytes, prev_digest[digest.len..]) catch {
3194 log.warn("bad cache stage1 digest: '{s}'", .{prev_digest});
3201 log.warn("bad cache stage1 digest: '{s}'", .{std.fmt.fmtSliceHexLower(prev_digest)});
31953202 break :hit;
31963203 };
31973204
......@@ -3211,7 +3218,11 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
32113218 mod.stage1_flags = @bitCast(@TypeOf(mod.stage1_flags), flags_bytes[0]);
32123219 return;
32133220 }
3214 log.debug("stage1 {s} prev_digest={} new_digest={}", .{ mod.root_pkg.root_src_path, prev_digest, digest });
3221 log.debug("stage1 {s} prev_digest={s} new_digest={s}", .{
3222 mod.root_pkg.root_src_path,
3223 std.fmt.fmtSliceHexLower(prev_digest),
3224 std.fmt.fmtSliceHexLower(&digest),
3225 });
32153226 man.unhit(prev_hash_state, input_file_count);
32163227 }
32173228
......@@ -3358,8 +3369,8 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
33583369 // Update the small file with the digest. If it fails we can continue; it only
33593370 // means that the next invocation will have an unnecessary cache miss.
33603371 const stage1_flags_byte = @bitCast(u8, mod.stage1_flags);
3361 log.debug("stage1 {s} final digest={} flags={x}", .{
3362 mod.root_pkg.root_src_path, digest, stage1_flags_byte,
3372 log.debug("stage1 {s} final digest={s} flags={x}", .{
3373 mod.root_pkg.root_src_path, std.fmt.fmtSliceHexLower(&digest), stage1_flags_byte,
33633374 });
33643375 var digest_plus_flags: [digest.len + 2]u8 = undefined;
33653376 digest_plus_flags[0..digest.len].* = digest;
src/clang.zig+5
......@@ -537,6 +537,11 @@ pub const FunctionType = opaque {
537537 extern fn ZigClangFunctionType_getReturnType(*const FunctionType) QualType;
538538};
539539
540pub const GenericSelectionExpr = opaque {
541 pub const getResultExpr = ZigClangGenericSelectionExpr_getResultExpr;
542 extern fn ZigClangGenericSelectionExpr_getResultExpr(*const GenericSelectionExpr) *const Expr;
543};
544
540545pub const IfStmt = opaque {
541546 pub const getThen = ZigClangIfStmt_getThen;
542547 extern fn ZigClangIfStmt_getThen(*const IfStmt) *const Stmt;
src/clang_options_data.zig+8-1
......@@ -2415,7 +2415,14 @@ flagpd1("dwarf-ext-refs"),
24152415sepd1("dylib_file"),
24162416flagpd1("dylinker"),
24172417flagpd1("dynamic"),
2418flagpd1("dynamiclib"),
2418.{
2419 .name = "dynamiclib",
2420 .syntax = .flag,
2421 .zig_equivalent = .shared,
2422 .pd1 = true,
2423 .pd2 = false,
2424 .psl = false,
2425},
24192426flagpd1("emit-ast"),
24202427flagpd1("emit-codegen-only"),
24212428flagpd1("emit-header-module"),
src/codegen.zig+61-134
......@@ -2132,9 +2132,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
21322132 if (inst.func.value()) |func_value| {
21332133 if (func_value.castTag(.function)) |func_payload| {
21342134 const func = func_payload.data;
2135 const text_segment = &macho_file.load_commands.items[macho_file.text_segment_cmd_index.?].Segment;
2136 const got = &text_segment.sections.items[macho_file.got_section_index.?];
2137 const got_addr = got.addr + func.owner_decl.link.macho.offset_table_index * @sizeOf(u64);
2135 const got_addr = blk: {
2136 const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
2137 const got = seg.sections.items[macho_file.got_section_index.?];
2138 break :blk got.addr + func.owner_decl.link.macho.offset_table_index * @sizeOf(u64);
2139 };
2140 log.debug("got_addr = 0x{x}", .{got_addr});
21382141 switch (arch) {
21392142 .x86_64 => {
21402143 try self.genSetReg(inst.base.src, Type.initTag(.u32), .rax, .{ .memory = got_addr });
......@@ -2152,8 +2155,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
21522155 const decl = func_payload.data;
21532156 const decl_name = try std.fmt.allocPrint(self.bin_file.allocator, "_{s}", .{decl.name});
21542157 defer self.bin_file.allocator.free(decl_name);
2155 const already_defined = macho_file.extern_lazy_symbols.contains(decl_name);
2156 const symbol: u32 = if (macho_file.extern_lazy_symbols.getIndex(decl_name)) |index|
2158 const already_defined = macho_file.lazy_imports.contains(decl_name);
2159 const symbol: u32 = if (macho_file.lazy_imports.getIndex(decl_name)) |index|
21572160 @intCast(u32, index)
21582161 else
21592162 try macho_file.addExternSymbol(decl_name);
......@@ -3111,7 +3114,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
31113114 4, 8 => {
31123115 const offset = if (math.cast(i9, adj_off)) |imm|
31133116 Instruction.LoadStoreOffset.imm_post_index(-imm)
3114 else |_| Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u64), MCValue{ .immediate = adj_off }));
3117 else |_|
3118 Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u64), MCValue{ .immediate = adj_off }));
31153119 const rn: Register = switch (arch) {
31163120 .aarch64, .aarch64_be => .x29,
31173121 .aarch64_32 => .w29,
......@@ -3302,80 +3306,32 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
33023306 },
33033307 .memory => |addr| {
33043308 if (self.bin_file.options.pie) {
3305 // For MachO, the binary, with the exception of object files, has to be a PIE.
3306 // Therefore we cannot load an absolute address.
3307 // Instead, we need to make use of PC-relative addressing.
3308 if (reg.id() == 0) { // x0 is special-cased
3309 // TODO This needs to be optimised in the stack usage (perhaps use a shadow stack
3310 // like described here:
3311 // https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/using-the-stack-in-aarch64-implementing-push-and-pop)
3312 // str x28, [sp, #-16]
3313 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.str(.x28, Register.sp, .{
3314 .offset = Instruction.LoadStoreOffset.imm_pre_index(-16),
3315 }).toU32());
3316 // adr x28, #8
3317 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.adr(.x28, 8).toU32());
3318 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3319 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3320 .address = addr,
3321 .start = self.code.items.len,
3322 .len = 4,
3323 });
3324 } else {
3325 return self.fail(src, "TODO implement genSetReg for PIE on this platform", .{});
3326 }
3327 // b [label]
3328 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.b(0).toU32());
3329 // mov r, x0
3330 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(
3331 reg,
3332 .xzr,
3333 .x0,
3334 Instruction.Shift.none,
3335 ).toU32());
3336 // ldr x28, [sp], #16
3337 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.x28, .{
3338 .register = .{
3339 .rn = Register.sp,
3340 .offset = Instruction.LoadStoreOffset.imm_post_index(16),
3341 },
3342 }).toU32());
3309 // PC-relative displacement to the entry in the GOT table.
3310 // TODO we should come up with our own, backend independent relocation types
3311 // which each backend (Elf, MachO, etc.) would then translate into an actual
3312 // fixup when linking.
3313 // adrp reg, pages
3314 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3315 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3316 .target_addr = addr,
3317 .offset = self.code.items.len,
3318 .size = 4,
3319 });
33433320 } else {
3344 // stp x0, x28, [sp, #-16]
3345 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.stp(
3346 .x0,
3347 .x28,
3348 Register.sp,
3349 Instruction.LoadStorePairOffset.pre_index(-16),
3350 ).toU32());
3351 // adr x28, #8
3352 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.adr(.x28, 8).toU32());
3353 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3354 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3355 .address = addr,
3356 .start = self.code.items.len,
3357 .len = 4,
3358 });
3359 } else {
3360 return self.fail(src, "TODO implement genSetReg for PIE on this platform", .{});
3361 }
3362 // b [label]
3363 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.b(0).toU32());
3364 // mov r, x0
3365 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(
3366 reg,
3367 .xzr,
3368 .x0,
3369 Instruction.Shift.none,
3370 ).toU32());
3371 // ldp x0, x28, [sp, #16]
3372 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldp(
3373 .x0,
3374 .x28,
3375 Register.sp,
3376 Instruction.LoadStorePairOffset.post_index(16),
3377 ).toU32());
3321 return self.fail(src, "TODO implement genSetReg for PIE GOT indirection on this platform", .{});
33783322 }
3323 mem.writeIntLittle(
3324 u32,
3325 try self.code.addManyAsArray(4),
3326 Instruction.adrp(reg, 0).toU32(),
3327 );
3328 // ldr reg, reg, offset
3329 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{
3330 .register = .{
3331 .rn = reg,
3332 .offset = Instruction.LoadStoreOffset.imm(0),
3333 },
3334 }).toU32());
33793335 } else {
33803336 // The value is in memory at a hard-coded address.
33813337 // If the type is a pointer, it means the pointer address is at this memory location.
......@@ -3559,62 +3515,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
35593515 },
35603516 .memory => |x| {
35613517 if (self.bin_file.options.pie) {
3562 // For MachO, the binary, with the exception of object files, has to be a PIE.
3563 // Therefore, we cannot load an absolute address.
3564 assert(x > math.maxInt(u32)); // 32bit direct addressing is not supported by MachO.
3565 // The plan here is to use unconditional relative jump to GOT entry, where we store
3566 // pre-calculated and stored effective address to load into the target register.
3567 // We leave the actual displacement information empty (0-padded) and fixing it up
3568 // later in the linker.
3569 if (reg.id() == 0) { // %rax is special-cased
3570 try self.code.ensureCapacity(self.code.items.len + 5);
3571 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3572 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3573 .address = x,
3574 .start = self.code.items.len,
3575 .len = 5,
3576 });
3577 } else {
3578 return self.fail(src, "TODO implement genSetReg for PIE on this platform", .{});
3579 }
3580 // call [label]
3581 self.code.appendSliceAssumeCapacity(&[_]u8{
3582 0xE8,
3583 0x0,
3584 0x0,
3585 0x0,
3586 0x0,
3518 // RIP-relative displacement to the entry in the GOT table.
3519 // TODO we should come up with our own, backend independent relocation types
3520 // which each backend (Elf, MachO, etc.) would then translate into an actual
3521 // fixup when linking.
3522 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3523 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3524 .target_addr = x,
3525 .offset = self.code.items.len + 3,
3526 .size = 4,
35873527 });
35883528 } else {
3589 try self.code.ensureCapacity(self.code.items.len + 10);
3590 // push %rax
3591 self.code.appendSliceAssumeCapacity(&[_]u8{0x50});
3592 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3593 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3594 .address = x,
3595 .start = self.code.items.len,
3596 .len = 5,
3597 });
3598 } else {
3599 return self.fail(src, "TODO implement genSetReg for PIE on this platform", .{});
3600 }
3601 // call [label]
3602 self.code.appendSliceAssumeCapacity(&[_]u8{
3603 0xE8,
3604 0x0,
3605 0x0,
3606 0x0,
3607 0x0,
3608 });
3609 // mov %r, %rax
3610 self.code.appendSliceAssumeCapacity(&[_]u8{
3611 0x48,
3612 0x89,
3613 0xC0 | @as(u8, reg.id()),
3614 });
3615 // pop %rax
3616 self.code.appendSliceAssumeCapacity(&[_]u8{0x58});
3529 return self.fail(src, "TODO implement genSetReg for PIE GOT indirection on this platform", .{});
36173530 }
3531 try self.code.ensureCapacity(self.code.items.len + 7);
3532 self.rex(.{ .w = reg.size() == 64, .r = reg.isExtended() });
3533 self.code.appendSliceAssumeCapacity(&[_]u8{
3534 0x8D,
3535 0x05 | (@as(u8, reg.id() & 0b111) << 3),
3536 });
3537 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), 0);
3538
3539 try self.code.ensureCapacity(self.code.items.len + 3);
3540 self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended(), .r = reg.isExtended() });
3541 const RM = (@as(u8, reg.id() & 0b111) << 3) | @truncate(u3, reg.id());
3542 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, RM });
36183543 } else if (x <= math.maxInt(u32)) {
36193544 // Moving from memory to a register is a variant of `8B /r`.
36203545 // Since we're using 64-bit moves, we require a REX.
......@@ -3777,9 +3702,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
37773702 return MCValue{ .memory = got_addr };
37783703 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
37793704 const decl = payload.data;
3780 const text_segment = &macho_file.load_commands.items[macho_file.text_segment_cmd_index.?].Segment;
3781 const got = &text_segment.sections.items[macho_file.got_section_index.?];
3782 const got_addr = got.addr + decl.link.macho.offset_table_index * ptr_bytes;
3705 const got_addr = blk: {
3706 const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
3707 const got = seg.sections.items[macho_file.got_section_index.?];
3708 break :blk got.addr + decl.link.macho.offset_table_index * ptr_bytes;
3709 };
37833710 return MCValue{ .memory = got_addr };
37843711 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
37853712 const decl = payload.data;
src/codegen/aarch64.zig+4-1
......@@ -221,7 +221,8 @@ pub const Instruction = union(enum) {
221221 offset: u12,
222222 opc: u2,
223223 op1: u2,
224 fixed: u4 = 0b111_0,
224 v: u1,
225 fixed: u3 = 0b111,
225226 size: u2,
226227 },
227228 LoadStorePairOfRegisters: packed struct {
......@@ -505,6 +506,7 @@ pub const Instruction = union(enum) {
505506 .offset = offset.toU12(),
506507 .opc = opc,
507508 .op1 = op1,
509 .v = 0,
508510 .size = 0b10,
509511 },
510512 };
......@@ -517,6 +519,7 @@ pub const Instruction = union(enum) {
517519 .offset = offset.toU12(),
518520 .opc = opc,
519521 .op1 = op1,
522 .v = 0,
520523 .size = 0b11,
521524 },
522525 };
src/codegen/llvm.zig+11-11
......@@ -222,7 +222,7 @@ pub const LLVMIRModule = struct {
222222
223223 var error_message: [*:0]const u8 = undefined;
224224 var target: *const llvm.Target = undefined;
225 if (llvm.Target.getFromTriple(llvm_target_triple.ptr, &target, &error_message)) {
225 if (llvm.Target.getFromTriple(llvm_target_triple.ptr, &target, &error_message).toBool()) {
226226 defer llvm.disposeMessage(error_message);
227227
228228 const stderr = std.io.getStdErr().writer();
......@@ -306,7 +306,7 @@ pub const LLVMIRModule = struct {
306306 // verifyModule always allocs the error_message even if there is no error
307307 defer llvm.disposeMessage(error_message);
308308
309 if (self.llvm_module.verify(.ReturnStatus, &error_message)) {
309 if (self.llvm_module.verify(.ReturnStatus, &error_message).toBool()) {
310310 const stderr = std.io.getStdErr().writer();
311311 try stderr.print("broken LLVM module found: {s}\nThis is a bug in the Zig compiler.", .{error_message});
312312 return error.BrokenLLVMModule;
......@@ -322,7 +322,7 @@ pub const LLVMIRModule = struct {
322322 object_pathZ.ptr,
323323 .ObjectFile,
324324 &error_message,
325 )) {
325 ).toBool()) {
326326 defer llvm.disposeMessage(error_message);
327327
328328 const stderr = std.io.getStdErr().writer();
......@@ -617,7 +617,7 @@ pub const LLVMIRModule = struct {
617617
618618 var indices: [2]*const llvm.Value = .{
619619 index_type.constNull(),
620 index_type.constInt(1, false),
620 index_type.constInt(1, .False),
621621 };
622622
623623 return self.builder.buildLoad(self.builder.buildInBoundsGEP(operand, &indices, 2, ""), "");
......@@ -679,7 +679,7 @@ pub const LLVMIRModule = struct {
679679 const signed = inst.base.ty.isSignedInt();
680680 // TODO: Should we use intcast here or just a simple bitcast?
681681 // LLVM does truncation vs bitcast (+signed extension) in the intcast depending on the sizes
682 return self.builder.buildIntCast2(val, try self.getLLVMType(inst.base.ty, inst.base.src), signed, "");
682 return self.builder.buildIntCast2(val, try self.getLLVMType(inst.base.ty, inst.base.src), llvm.Bool.fromBool(signed), "");
683683 }
684684
685685 fn genBitCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value {
......@@ -785,7 +785,7 @@ pub const LLVMIRModule = struct {
785785 if (bigint.limbs.len != 1) {
786786 return self.fail(src, "TODO implement bigger bigint", .{});
787787 }
788 const llvm_int = llvm_type.constInt(bigint.limbs[0], false);
788 const llvm_int = llvm_type.constInt(bigint.limbs[0], .False);
789789 if (!bigint.positive) {
790790 return llvm.constNeg(llvm_int);
791791 }
......@@ -823,7 +823,7 @@ pub const LLVMIRModule = struct {
823823 return self.fail(src, "TODO handle other sentinel values", .{});
824824 } else false;
825825
826 return self.context.constString(payload.data.ptr, @intCast(c_uint, payload.data.len), !zero_sentinel);
826 return self.context.constString(payload.data.ptr, @intCast(c_uint, payload.data.len), llvm.Bool.fromBool(!zero_sentinel));
827827 } else {
828828 return self.fail(src, "TODO handle more array values", .{});
829829 }
......@@ -839,13 +839,13 @@ pub const LLVMIRModule = struct {
839839 llvm_child_type.constNull(),
840840 self.context.intType(1).constNull(),
841841 };
842 return self.context.constStruct(&optional_values, 2, false);
842 return self.context.constStruct(&optional_values, 2, .False);
843843 } else {
844844 var optional_values: [2]*const llvm.Value = .{
845845 try self.genTypedValue(src, .{ .ty = child_type, .val = tv.val }),
846846 self.context.intType(1).constAllOnes(),
847847 };
848 return self.context.constStruct(&optional_values, 2, false);
848 return self.context.constStruct(&optional_values, 2, .False);
849849 }
850850 } else {
851851 return self.fail(src, "TODO implement const of optional pointer", .{});
......@@ -885,7 +885,7 @@ pub const LLVMIRModule = struct {
885885 try self.getLLVMType(child_type, src),
886886 self.context.intType(1),
887887 };
888 return self.context.structType(&optional_types, 2, false);
888 return self.context.structType(&optional_types, 2, .False);
889889 } else {
890890 return self.fail(src, "TODO implement optional pointers as actual pointers", .{});
891891 }
......@@ -937,7 +937,7 @@ pub const LLVMIRModule = struct {
937937 try self.getLLVMType(return_type, src),
938938 if (fn_param_len == 0) null else llvm_param.ptr,
939939 @intCast(c_uint, fn_param_len),
940 false,
940 .False,
941941 );
942942 const llvm_fn = self.llvm_module.addFunction(func.name, fn_type);
943943
src/codegen/llvm/bindings.zig+23-10
......@@ -1,7 +1,20 @@
11//! We do this instead of @cImport because the self-hosted compiler is easier
22//! to bootstrap if it does not depend on translate-c.
33
4const LLVMBool = bool;
4/// Do not compare directly to .True, use toBool() instead.
5pub const Bool = enum(c_int) {
6 False,
7 True,
8 _,
9
10 pub fn fromBool(b: bool) Bool {
11 return @intToEnum(Bool, @boolToInt(b));
12 }
13
14 pub fn toBool(b: Bool) bool {
15 return b != .False;
16 }
17};
518pub const AttributeIndex = c_uint;
619
720/// Make sure to use the *InContext functions instead of the global ones.
......@@ -22,13 +35,13 @@ pub const Context = opaque {
2235 extern fn LLVMVoidTypeInContext(C: *const Context) *const Type;
2336
2437 pub const structType = LLVMStructTypeInContext;
25 extern fn LLVMStructTypeInContext(C: *const Context, ElementTypes: [*]*const Type, ElementCount: c_uint, Packed: LLVMBool) *const Type;
38 extern fn LLVMStructTypeInContext(C: *const Context, ElementTypes: [*]*const Type, ElementCount: c_uint, Packed: Bool) *const Type;
2639
2740 pub const constString = LLVMConstStringInContext;
28 extern fn LLVMConstStringInContext(C: *const Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: LLVMBool) *const Value;
41 extern fn LLVMConstStringInContext(C: *const Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: Bool) *const Value;
2942
3043 pub const constStruct = LLVMConstStructInContext;
31 extern fn LLVMConstStructInContext(C: *const Context, ConstantVals: [*]*const Value, Count: c_uint, Packed: LLVMBool) *const Value;
44 extern fn LLVMConstStructInContext(C: *const Context, ConstantVals: [*]*const Value, Count: c_uint, Packed: Bool) *const Value;
3245
3346 pub const createBasicBlock = LLVMCreateBasicBlockInContext;
3447 extern fn LLVMCreateBasicBlockInContext(C: *const Context, Name: [*:0]const u8) *const BasicBlock;
......@@ -59,7 +72,7 @@ pub const Value = opaque {
5972
6073pub const Type = opaque {
6174 pub const functionType = LLVMFunctionType;
62 extern fn LLVMFunctionType(ReturnType: *const Type, ParamTypes: ?[*]*const Type, ParamCount: c_uint, IsVarArg: LLVMBool) *const Type;
75 extern fn LLVMFunctionType(ReturnType: *const Type, ParamTypes: ?[*]*const Type, ParamCount: c_uint, IsVarArg: Bool) *const Type;
6376
6477 pub const constNull = LLVMConstNull;
6578 extern fn LLVMConstNull(Ty: *const Type) *const Value;
......@@ -68,7 +81,7 @@ pub const Type = opaque {
6881 extern fn LLVMConstAllOnes(Ty: *const Type) *const Value;
6982
7083 pub const constInt = LLVMConstInt;
71 extern fn LLVMConstInt(IntTy: *const Type, N: c_ulonglong, SignExtend: LLVMBool) *const Value;
84 extern fn LLVMConstInt(IntTy: *const Type, N: c_ulonglong, SignExtend: Bool) *const Value;
7285
7386 pub const constArray = LLVMConstArray;
7487 extern fn LLVMConstArray(ElementTy: *const Type, ConstantVals: ?[*]*const Value, Length: c_uint) *const Value;
......@@ -91,7 +104,7 @@ pub const Module = opaque {
91104 extern fn LLVMDisposeModule(*const Module) void;
92105
93106 pub const verify = LLVMVerifyModule;
94 extern fn LLVMVerifyModule(*const Module, Action: VerifierFailureAction, OutMessage: *[*:0]const u8) LLVMBool;
107 extern fn LLVMVerifyModule(*const Module, Action: VerifierFailureAction, OutMessage: *[*:0]const u8) Bool;
95108
96109 pub const addFunction = LLVMAddFunction;
97110 extern fn LLVMAddFunction(*const Module, Name: [*:0]const u8, FunctionTy: *const Type) *const Value;
......@@ -191,7 +204,7 @@ pub const Builder = opaque {
191204 extern fn LLVMBuildNUWSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
192205
193206 pub const buildIntCast2 = LLVMBuildIntCast2;
194 extern fn LLVMBuildIntCast2(*const Builder, Val: *const Value, DestTy: *const Type, IsSigned: LLVMBool, Name: [*:0]const u8) *const Value;
207 extern fn LLVMBuildIntCast2(*const Builder, Val: *const Value, DestTy: *const Type, IsSigned: Bool, Name: [*:0]const u8) *const Value;
195208
196209 pub const buildBitCast = LLVMBuildBitCast;
197210 extern fn LLVMBuildBitCast(*const Builder, Val: *const Value, DestTy: *const Type, Name: [*:0]const u8) *const Value;
......@@ -258,7 +271,7 @@ pub const TargetMachine = opaque {
258271 Filename: [*:0]const u8,
259272 codegen: CodeGenFileType,
260273 ErrorMessage: *[*:0]const u8,
261 ) LLVMBool;
274 ) Bool;
262275};
263276
264277pub const CodeMode = extern enum {
......@@ -295,7 +308,7 @@ pub const CodeGenFileType = extern enum {
295308
296309pub const Target = opaque {
297310 pub const getFromTriple = LLVMGetTargetFromTriple;
298 extern fn LLVMGetTargetFromTriple(Triple: [*:0]const u8, T: **const Target, ErrorMessage: *[*:0]const u8) LLVMBool;
311 extern fn LLVMGetTargetFromTriple(Triple: [*:0]const u8, T: **const Target, ErrorMessage: *[*:0]const u8) Bool;
299312};
300313
301314extern fn LLVMInitializeAArch64TargetInfo() void;
src/codegen/wasm.zig+41-3
......@@ -95,7 +95,7 @@ pub const Context = struct {
9595 return switch (ty.tag()) {
9696 .f32 => wasm.valtype(.f32),
9797 .f64 => wasm.valtype(.f64),
98 .u32, .i32 => wasm.valtype(.i32),
98 .u32, .i32, .bool => wasm.valtype(.i32),
9999 .u64, .i64 => wasm.valtype(.i64),
100100 else => self.fail(src, "TODO - Wasm genValtype for type '{s}'", .{ty.tag()}),
101101 };
......@@ -208,6 +208,7 @@ pub const Context = struct {
208208 .alloc => self.genAlloc(inst.castTag(.alloc).?),
209209 .arg => self.genArg(inst.castTag(.arg).?),
210210 .block => self.genBlock(inst.castTag(.block).?),
211 .breakpoint => self.genBreakpoint(inst.castTag(.breakpoint).?),
211212 .br => self.genBr(inst.castTag(.br).?),
212213 .call => self.genCall(inst.castTag(.call).?),
213214 .cmp_eq => self.genCmp(inst.castTag(.cmp_eq).?, .eq),
......@@ -221,9 +222,11 @@ pub const Context = struct {
221222 .dbg_stmt => WValue.none,
222223 .load => self.genLoad(inst.castTag(.load).?),
223224 .loop => self.genLoop(inst.castTag(.loop).?),
225 .not => self.genNot(inst.castTag(.not).?),
224226 .ret => self.genRet(inst.castTag(.ret).?),
225227 .retvoid => WValue.none,
226228 .store => self.genStore(inst.castTag(.store).?),
229 .unreach => self.genUnreachable(inst.castTag(.unreach).?),
227230 else => self.fail(inst.src, "TODO: Implement wasm inst: {s}", .{inst.tag}),
228231 };
229232 }
......@@ -329,7 +332,7 @@ pub const Context = struct {
329332 try writer.writeByte(wasm.opcode(.i32_const));
330333 try leb.writeILEB128(writer, inst.val.toUnsignedInt());
331334 },
332 .i32 => {
335 .i32, .bool => {
333336 try writer.writeByte(wasm.opcode(.i32_const));
334337 try leb.writeILEB128(writer, inst.val.toSignedInt());
335338 },
......@@ -414,7 +417,14 @@ pub const Context = struct {
414417
415418 // insert blocks at the position of `offset` so
416419 // the condition can jump to it
417 const offset = condition.code_offset;
420 const offset = switch (condition) {
421 .code_offset => |offset| offset,
422 else => blk: {
423 const offset = self.code.items.len;
424 try self.emitWValue(condition);
425 break :blk offset;
426 },
427 };
418428 const block_ty = try self.genBlockType(condbr.base.src, condbr.base.ty);
419429 try self.startBlock(.block, block_ty, offset);
420430
......@@ -523,4 +533,32 @@ pub const Context = struct {
523533
524534 return .none;
525535 }
536
537 fn genNot(self: *Context, not: *Inst.UnOp) InnerError!WValue {
538 const offset = self.code.items.len;
539
540 const operand = self.resolveInst(not.operand);
541 try self.emitWValue(operand);
542
543 // wasm does not have booleans nor the `not` instruction, therefore compare with 0
544 // to create the same logic
545 const writer = self.code.writer();
546 try writer.writeByte(wasm.opcode(.i32_const));
547 try leb.writeILEB128(writer, @as(i32, 0));
548
549 try writer.writeByte(wasm.opcode(.i32_eq));
550
551 return WValue{ .code_offset = offset };
552 }
553
554 fn genBreakpoint(self: *Context, breakpoint: *Inst.NoOp) InnerError!WValue {
555 // unsupported by wasm itself. Can be implemented once we support DWARF
556 // for wasm
557 return .none;
558 }
559
560 fn genUnreachable(self: *Context, unreach: *Inst.NoOp) InnerError!WValue {
561 try self.code.append(wasm.opcode(.@"unreachable"));
562 return .none;
563 }
526564};
src/config.zig.in+1-1
......@@ -1,7 +1,7 @@
11pub const have_llvm = true;
22pub const version: [:0]const u8 = "@ZIG_VERSION@";
33pub const semver = try @import("std").SemanticVersion.parse(version);
4pub const enable_logging: bool = false;
4pub const enable_logging: bool = @ZIG_ENABLE_LOGGING_BOOL@;
55pub const enable_tracy = false;
66pub const is_stage1 = true;
77pub const skip_non_native = false;
src/introspect.zig+8
......@@ -61,6 +61,14 @@ pub fn findZigLibDirFromSelfExe(
6161
6262/// Caller owns returned memory.
6363pub fn resolveGlobalCacheDir(allocator: *mem.Allocator) ![]u8 {
64 if (std.process.getEnvVarOwned(allocator, "ZIG_GLOBAL_CACHE_DIR")) |value| {
65 if (value.len > 0) {
66 return value;
67 } else {
68 allocator.free(value);
69 }
70 } else |_| {}
71
6472 const appname = "zig";
6573
6674 if (std.Target.current.os.tag != .windows) {
src/link/MachO.zig+551-540
......@@ -11,7 +11,9 @@ const codegen = @import("../codegen.zig");
1111const aarch64 = @import("../codegen/aarch64.zig");
1212const math = std.math;
1313const mem = std.mem;
14const meta = std.meta;
1415
16const bind = @import("MachO/bind.zig");
1517const trace = @import("../tracy.zig").trace;
1618const build_options = @import("build_options");
1719const Module = @import("../Module.zig");
......@@ -24,9 +26,9 @@ const target_util = @import("../target.zig");
2426const DebugSymbols = @import("MachO/DebugSymbols.zig");
2527const Trie = @import("MachO/Trie.zig");
2628const CodeSignature = @import("MachO/CodeSignature.zig");
29const Zld = @import("MachO/Zld.zig");
2730
2831usingnamespace @import("MachO/commands.zig");
29usingnamespace @import("MachO/imports.zig");
3032
3133pub const base_tag: File.Tag = File.Tag.macho;
3234
......@@ -87,14 +89,12 @@ code_signature_cmd_index: ?u16 = null,
8789
8890/// Index into __TEXT,__text section.
8991text_section_index: ?u16 = null,
90/// Index into __TEXT,__ziggot section.
91got_section_index: ?u16 = null,
9292/// Index into __TEXT,__stubs section.
9393stubs_section_index: ?u16 = null,
9494/// Index into __TEXT,__stub_helper section.
9595stub_helper_section_index: ?u16 = null,
9696/// Index into __DATA_CONST,__got section.
97data_got_section_index: ?u16 = null,
97got_section_index: ?u16 = null,
9898/// Index into __DATA,__la_symbol_ptr section.
9999la_symbol_ptr_section_index: ?u16 = null,
100100/// Index into __DATA,__data section.
......@@ -104,16 +104,16 @@ entry_addr: ?u64 = null,
104104
105105/// Table of all local symbols
106106/// Internally references string table for names (which are optional).
107local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
107locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
108108/// Table of all global symbols
109global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
109globals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
110110/// Table of all extern nonlazy symbols, indexed by name.
111extern_nonlazy_symbols: std.StringArrayHashMapUnmanaged(ExternSymbol) = .{},
111nonlazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},
112112/// Table of all extern lazy symbols, indexed by name.
113extern_lazy_symbols: std.StringArrayHashMapUnmanaged(ExternSymbol) = .{},
113lazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},
114114
115local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
116global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
115locals_free_list: std.ArrayListUnmanaged(u32) = .{},
116globals_free_list: std.ArrayListUnmanaged(u32) = .{},
117117offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
118118
119119stub_helper_stubs_start_off: ?u64 = null,
......@@ -122,8 +122,8 @@ stub_helper_stubs_start_off: ?u64 = null,
122122string_table: std.ArrayListUnmanaged(u8) = .{},
123123string_table_directory: std.StringHashMapUnmanaged(u32) = .{},
124124
125/// Table of trampolines to the actual symbols in __text section.
126offset_table: std.ArrayListUnmanaged(u64) = .{},
125/// Table of GOT entries.
126offset_table: std.ArrayListUnmanaged(GOTEntry) = .{},
127127
128128error_flags: File.ErrorFlags = File.ErrorFlags{},
129129
......@@ -154,14 +154,19 @@ string_table_needs_relocation: bool = false,
154154/// allocate a fresh text block, which will have ideal capacity, and then grow it
155155/// by 1 byte. It will then have -1 overcapacity.
156156text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
157
157158/// Pointer to the last allocated text block
158159last_text_block: ?*TextBlock = null,
160
159161/// A list of all PIE fixups required for this run of the linker.
160162/// Warning, this is currently NOT thread-safe. See the TODO below.
161163/// TODO Move this list inside `updateDecl` where it should be allocated
162164/// prior to calling `generateSymbol`, and then immediately deallocated
163165/// rather than sitting in the global scope.
164pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{},
166/// TODO We should also rewrite this using generic relocations common to all
167/// backends.
168pie_fixups: std.ArrayListUnmanaged(PIEFixup) = .{},
169
165170/// A list of all stub (extern decls) fixups required for this run of the linker.
166171/// Warning, this is currently NOT thread-safe. See the TODO below.
167172/// TODO Move this list inside `updateDecl` where it should be allocated
......@@ -169,14 +174,42 @@ pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{},
169174/// rather than sitting in the global scope.
170175stub_fixups: std.ArrayListUnmanaged(StubFixup) = .{},
171176
172pub const PieFixup = struct {
173 /// Target address we wanted to address in absolute terms.
174 address: u64,
175 /// Where in the byte stream we should perform the fixup.
176 start: usize,
177 /// The length of the byte stream. For x86_64, this will be
178 /// variable. For aarch64, it will be fixed at 4 bytes.
179 len: usize,
177pub const GOTEntry = struct {
178 /// GOT entry can either be a local pointer or an extern (nonlazy) import.
179 kind: enum {
180 Local,
181 Extern,
182 },
183
184 /// Id to the macho.nlist_64 from the respective table: either locals or nonlazy imports.
185 /// TODO I'm more and more inclined to just manage a single, max two symbol tables
186 /// rather than 4 as we currently do, but I'll follow up in the future PR.
187 symbol: u32,
188
189 /// Index of this entry in the GOT.
190 index: u32,
191};
192
193pub const Import = struct {
194 /// MachO symbol table entry.
195 symbol: macho.nlist_64,
196
197 /// Id of the dynamic library where the specified entries can be found.
198 dylib_ordinal: i64,
199
200 /// Index of this import within the import list.
201 index: u32,
202};
203
204pub const PIEFixup = struct {
205 /// Target VM address of this relocation.
206 target_addr: u64,
207
208 /// Offset within the byte stream.
209 offset: usize,
210
211 /// Size of the relocation.
212 size: usize,
180213};
181214
182215pub const StubFixup = struct {
......@@ -260,9 +293,9 @@ pub const TextBlock = struct {
260293 /// File offset relocation happens transparently, so it is not included in
261294 /// this calculation.
262295 fn capacity(self: TextBlock, macho_file: MachO) u64 {
263 const self_sym = macho_file.local_symbols.items[self.local_sym_index];
296 const self_sym = macho_file.locals.items[self.local_sym_index];
264297 if (self.next) |next| {
265 const next_sym = macho_file.local_symbols.items[next.local_sym_index];
298 const next_sym = macho_file.locals.items[next.local_sym_index];
266299 return next_sym.n_value - self_sym.n_value;
267300 } else {
268301 // We are the last block.
......@@ -274,8 +307,8 @@ pub const TextBlock = struct {
274307 fn freeListEligible(self: TextBlock, macho_file: MachO) bool {
275308 // No need to keep a free list node for the last block.
276309 const next = self.next orelse return false;
277 const self_sym = macho_file.local_symbols.items[self.local_sym_index];
278 const next_sym = macho_file.local_symbols.items[next.local_sym_index];
310 const self_sym = macho_file.locals.items[self.local_sym_index];
311 const next_sym = macho_file.locals.items[next.local_sym_index];
279312 const cap = next_sym.n_value - self_sym.n_value;
280313 const ideal_cap = padToIdeal(self.size);
281314 if (cap <= ideal_cap) return false;
......@@ -344,7 +377,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
344377 };
345378
346379 // Index 0 is always a null symbol.
347 try self.local_symbols.append(allocator, .{
380 try self.locals.append(allocator, .{
348381 .n_strx = 0,
349382 .n_type = 0,
350383 .n_sect = 0,
......@@ -600,7 +633,74 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
600633 if (!mem.eql(u8, the_object_path, full_out_path)) {
601634 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
602635 }
603 } else {
636 } else outer: {
637 const use_zld = blk: {
638 if (self.base.options.is_native_os and self.base.options.system_linker_hack) {
639 // If the user forces the use of ld64, make sure we are running native!
640 break :blk false;
641 }
642
643 if (self.base.options.target.cpu.arch == .aarch64) {
644 // On aarch64, always use zld.
645 break :blk true;
646 }
647
648 if (self.base.options.link_libcpp or
649 self.base.options.output_mode == .Lib or
650 self.base.options.linker_script != null)
651 {
652 // Fallback to LLD in this handful of cases on x86_64 only.
653 break :blk false;
654 }
655
656 break :blk true;
657 };
658
659 if (use_zld) {
660 var zld = Zld.init(self.base.allocator);
661 defer zld.deinit();
662 zld.arch = target.cpu.arch;
663
664 var input_files = std.ArrayList([]const u8).init(self.base.allocator);
665 defer input_files.deinit();
666 // Positional arguments to the linker such as object files.
667 try input_files.appendSlice(self.base.options.objects);
668 for (comp.c_object_table.items()) |entry| {
669 try input_files.append(entry.key.status.success.object_path);
670 }
671 if (module_obj_path) |p| {
672 try input_files.append(p);
673 }
674 try input_files.append(comp.compiler_rt_static_lib.?.full_object_path);
675 // libc++ dep
676 if (self.base.options.link_libcpp) {
677 try input_files.append(comp.libcxxabi_static_lib.?.full_object_path);
678 try input_files.append(comp.libcxx_static_lib.?.full_object_path);
679 }
680
681 if (self.base.options.verbose_link) {
682 var argv = std.ArrayList([]const u8).init(self.base.allocator);
683 defer argv.deinit();
684
685 try argv.append("zig");
686 try argv.append("ld");
687
688 try argv.ensureCapacity(input_files.items.len);
689 for (input_files.items) |f| {
690 argv.appendAssumeCapacity(f);
691 }
692
693 try argv.append("-o");
694 try argv.append(full_out_path);
695
696 Compilation.dump_argv(argv.items);
697 }
698
699 try zld.link(input_files.items, full_out_path);
700
701 break :outer;
702 }
703
604704 // Create an LLD command line and invoke it.
605705 var argv = std.ArrayList([]const u8).init(self.base.allocator);
606706 defer argv.deinit();
......@@ -644,9 +744,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
644744 try argv.append("defs");
645745 }
646746
647 if (is_dyn_lib) {
648 try argv.append("-static");
649 } else {
747 if (is_exe_or_dyn_lib) {
650748 try argv.append("-dynamic");
651749 }
652750
......@@ -836,7 +934,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
836934 }
837935 },
838936 else => {
839 log.err("{s} terminated", .{ argv.items[0] });
937 log.err("{s} terminated", .{argv.items[0]});
840938 return error.LLDCrashed;
841939 },
842940 }
......@@ -873,119 +971,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
873971 log.warn("unexpected LLD stderr:\n{s}", .{stderr});
874972 }
875973 }
876
877 // At this stage, LLD has done its job. It is time to patch the resultant
878 // binaries up!
879 const out_file = try directory.handle.openFile(self.base.options.emit.?.sub_path, .{ .write = true });
880 try self.parseFromFile(out_file);
881
882 if (self.libsystem_cmd_index == null and self.header.?.filetype == macho.MH_EXECUTE) {
883 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
884 const text_section = text_segment.sections.items[self.text_section_index.?];
885 const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64);
886 const needed_size = padToIdeal(@sizeOf(macho.linkedit_data_command));
887
888 if (needed_size + after_last_cmd_offset > text_section.offset) {
889 log.err("Unable to extend padding between the end of load commands and start of __text section.", .{});
890 log.err("Re-run the linker with '-headerpad 0x{x}' option if available, or", .{needed_size});
891 log.err("fall back to the system linker by exporting 'ZIG_SYSTEM_LINKER_HACK=1'.", .{});
892 return error.NotEnoughPadding;
893 }
894
895 // Calculate next available dylib ordinal.
896 const next_ordinal = blk: {
897 var ordinal: u32 = 1;
898 for (self.load_commands.items) |cmd| {
899 switch (cmd) {
900 .Dylib => ordinal += 1,
901 else => {},
902 }
903 }
904 break :blk ordinal;
905 };
906
907 // Add load dylib load command
908 self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len);
909 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
910 u64,
911 @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH),
912 @sizeOf(u64),
913 ));
914 // TODO Find a way to work out runtime version from the OS version triple stored in std.Target.
915 // In the meantime, we're gonna hardcode to the minimum compatibility version of 0.0.0.
916 const min_version = 0x0;
917 var dylib_cmd = emptyGenericCommandWithData(macho.dylib_command{
918 .cmd = macho.LC_LOAD_DYLIB,
919 .cmdsize = cmdsize,
920 .dylib = .{
921 .name = @sizeOf(macho.dylib_command),
922 .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files
923 .current_version = min_version,
924 .compatibility_version = min_version,
925 },
926 });
927 dylib_cmd.data = try self.base.allocator.alloc(u8, cmdsize - dylib_cmd.inner.dylib.name);
928 mem.set(u8, dylib_cmd.data, 0);
929 mem.copy(u8, dylib_cmd.data, mem.spanZ(LIB_SYSTEM_PATH));
930 try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd });
931 self.header_dirty = true;
932 self.load_commands_dirty = true;
933
934 if (self.symtab_cmd_index == null or self.dysymtab_cmd_index == null) {
935 log.err("Incomplete Mach-O binary: no LC_SYMTAB or LC_DYSYMTAB load command found!", .{});
936 log.err("Without the symbol table, it is not possible to patch up the binary for cross-compilation.", .{});
937 return error.NoSymbolTableFound;
938 }
939
940 // Patch dyld info
941 try self.fixupBindInfo(next_ordinal);
942 try self.fixupLazyBindInfo(next_ordinal);
943
944 // Write updated load commands and the header
945 try self.writeLoadCommands();
946 try self.writeHeader();
947
948 assert(!self.header_dirty);
949 assert(!self.load_commands_dirty);
950 }
951 if (self.code_signature_cmd_index == null) outer: {
952 if (target.cpu.arch != .aarch64) break :outer; // This is currently needed only for aarch64 targets.
953 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
954 const text_section = text_segment.sections.items[self.text_section_index.?];
955 const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64);
956 const needed_size = padToIdeal(@sizeOf(macho.linkedit_data_command));
957
958 if (needed_size + after_last_cmd_offset > text_section.offset) {
959 log.err("Unable to extend padding between the end of load commands and start of __text section.", .{});
960 log.err("Re-run the linker with '-headerpad 0x{x}' option if available, or", .{needed_size});
961 log.err("fall back to the system linker by exporting 'ZIG_SYSTEM_LINKER_HACK=1'.", .{});
962 return error.NotEnoughPadding;
963 }
964
965 // Add code signature load command
966 self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);
967 try self.load_commands.append(self.base.allocator, .{
968 .LinkeditData = .{
969 .cmd = macho.LC_CODE_SIGNATURE,
970 .cmdsize = @sizeOf(macho.linkedit_data_command),
971 .dataoff = 0,
972 .datasize = 0,
973 },
974 });
975 self.header_dirty = true;
976 self.load_commands_dirty = true;
977
978 // Pad out space for code signature
979 try self.writeCodeSignaturePadding();
980 // Write updated load commands and the header
981 try self.writeLoadCommands();
982 try self.writeHeader();
983 // Generate adhoc code signature
984 try self.writeCodeSignature();
985
986 assert(!self.header_dirty);
987 assert(!self.load_commands_dirty);
988 }
989974 }
990975 }
991976
......@@ -1021,14 +1006,14 @@ pub fn deinit(self: *MachO) void {
10211006 if (self.d_sym) |*ds| {
10221007 ds.deinit(self.base.allocator);
10231008 }
1024 for (self.extern_lazy_symbols.items()) |*entry| {
1009 for (self.lazy_imports.items()) |*entry| {
10251010 self.base.allocator.free(entry.key);
10261011 }
1027 self.extern_lazy_symbols.deinit(self.base.allocator);
1028 for (self.extern_nonlazy_symbols.items()) |*entry| {
1012 self.lazy_imports.deinit(self.base.allocator);
1013 for (self.nonlazy_imports.items()) |*entry| {
10291014 self.base.allocator.free(entry.key);
10301015 }
1031 self.extern_nonlazy_symbols.deinit(self.base.allocator);
1016 self.nonlazy_imports.deinit(self.base.allocator);
10321017 self.pie_fixups.deinit(self.base.allocator);
10331018 self.stub_fixups.deinit(self.base.allocator);
10341019 self.text_block_free_list.deinit(self.base.allocator);
......@@ -1042,10 +1027,10 @@ pub fn deinit(self: *MachO) void {
10421027 }
10431028 self.string_table_directory.deinit(self.base.allocator);
10441029 self.string_table.deinit(self.base.allocator);
1045 self.global_symbols.deinit(self.base.allocator);
1046 self.global_symbol_free_list.deinit(self.base.allocator);
1047 self.local_symbols.deinit(self.base.allocator);
1048 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);
10491034 for (self.load_commands.items) |*lc| {
10501035 lc.deinit(self.base.allocator);
10511036 }
......@@ -1100,7 +1085,7 @@ fn shrinkTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64) vo
11001085}
11011086
11021087fn growTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
1103 const sym = self.local_symbols.items[text_block.local_sym_index];
1088 const sym = self.locals.items[text_block.local_sym_index];
11041089 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;
11051090 const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);
11061091 if (!need_realloc) return sym.n_value;
......@@ -1110,34 +1095,41 @@ fn growTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alig
11101095pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
11111096 if (decl.link.macho.local_sym_index != 0) return;
11121097
1113 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);
11141099 try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
11151100
1116 if (self.local_symbol_free_list.popOrNull()) |i| {
1101 if (self.locals_free_list.popOrNull()) |i| {
11171102 log.debug("reusing symbol index {d} for {s}", .{ i, decl.name });
11181103 decl.link.macho.local_sym_index = i;
11191104 } else {
1120 log.debug("allocating symbol index {d} for {s}", .{ self.local_symbols.items.len, decl.name });
1121 decl.link.macho.local_sym_index = @intCast(u32, self.local_symbols.items.len);
1122 _ = 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();
11231108 }
11241109
11251110 if (self.offset_table_free_list.popOrNull()) |i| {
1111 log.debug("reusing offset table entry index {d} for {s}", .{ i, decl.name });
11261112 decl.link.macho.offset_table_index = i;
11271113 } else {
1114 log.debug("allocating offset table entry index {d} for {s}", .{ self.offset_table.items.len, decl.name });
11281115 decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len);
11291116 _ = self.offset_table.addOneAssumeCapacity();
11301117 self.offset_table_count_dirty = true;
1118 self.rebase_info_dirty = true;
11311119 }
11321120
1133 self.local_symbols.items[decl.link.macho.local_sym_index] = .{
1121 self.locals.items[decl.link.macho.local_sym_index] = .{
11341122 .n_strx = 0,
11351123 .n_type = 0,
11361124 .n_sect = 0,
11371125 .n_desc = 0,
11381126 .n_value = 0,
11391127 };
1140 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 };
11411133}
11421134
11431135pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
......@@ -1180,8 +1172,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
11801172 .externally_managed => |x| x,
11811173 .appended => code_buffer.items,
11821174 .fail => |em| {
1183 // Clear any PIE fixups and stub fixups for this decl.
1175 // Clear any PIE fixups for this decl.
11841176 self.pie_fixups.shrinkRetainingCapacity(0);
1177 // Clear any stub fixups for this decl.
11851178 self.stub_fixups.shrinkRetainingCapacity(0);
11861179 decl.analysis = .codegen_failure;
11871180 try module.failed_decls.put(module.gpa, decl, em);
......@@ -1191,7 +1184,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
11911184
11921185 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
11931186 assert(decl.link.macho.local_sym_index != 0); // Caller forgot to call allocateDeclIndexes()
1194 const symbol = &self.local_symbols.items[decl.link.macho.local_sym_index];
1187 const symbol = &self.locals.items[decl.link.macho.local_sym_index];
11951188
11961189 if (decl.link.macho.size != 0) {
11971190 const capacity = decl.link.macho.capacity(self.*);
......@@ -1200,9 +1193,12 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
12001193 const vaddr = try self.growTextBlock(&decl.link.macho, code.len, required_alignment);
12011194 log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr });
12021195 if (vaddr != symbol.n_value) {
1203 symbol.n_value = vaddr;
12041196 log.debug(" (writing new offset table entry)", .{});
1205 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 };
12061202 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
12071203 }
12081204 } else if (code.len < decl.link.macho.size) {
......@@ -1231,7 +1227,11 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
12311227 .n_desc = 0,
12321228 .n_value = addr,
12331229 };
1234 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 };
12351235
12361236 try self.writeLocalSymbol(decl.link.macho.local_sym_index);
12371237 if (self.d_sym) |*ds|
......@@ -1239,30 +1239,48 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
12391239 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
12401240 }
12411241
1242 // Perform PIE fixups (if any)
1243 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1244 const got_section = text_segment.sections.items[self.got_section_index.?];
1242 // Calculate displacements to target addr (if any).
12451243 while (self.pie_fixups.popOrNull()) |fixup| {
1246 const target_addr = fixup.address;
1247 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
12481248 switch (self.base.options.target.cpu.arch) {
12491249 .x86_64 => {
1250 assert(target_addr >= this_addr + fixup.len);
1251 const displacement = try math.cast(u32, target_addr - this_addr - fixup.len);
1252 var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)];
1253 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);
12541252 },
12551253 .aarch64 => {
1256 assert(target_addr >= this_addr);
1257 const displacement = try math.cast(u27, target_addr - this_addr);
1258 var placeholder = code_buffer.items[fixup.start..][0..fixup.len];
1259 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 }
12601277 },
12611278 else => unreachable, // unsupported target architecture
12621279 }
12631280 }
12641281
12651282 // Resolve stubs (if any)
1283 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
12661284 const stubs = text_segment.sections.items[self.stubs_section_index.?];
12671285 for (self.stub_fixups.items) |fixup| {
12681286 const stub_addr = stubs.addr + fixup.symbol * stubs.reserved2;
......@@ -1287,9 +1305,6 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
12871305 try self.writeStubInStubHelper(fixup.symbol);
12881306 try self.writeLazySymbolPointer(fixup.symbol);
12891307
1290 const extern_sym = &self.extern_lazy_symbols.items()[fixup.symbol].value;
1291 extern_sym.segment = self.data_segment_cmd_index.?;
1292 extern_sym.offset = fixup.symbol * @sizeOf(u64);
12931308 self.rebase_info_dirty = true;
12941309 self.lazy_binding_info_dirty = true;
12951310 }
......@@ -1331,9 +1346,9 @@ pub fn updateDeclExports(
13311346 const tracy = trace(@src());
13321347 defer tracy.end();
13331348
1334 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);
13351350 if (decl.link.macho.local_sym_index == 0) return;
1336 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];
13371352
13381353 for (exports) |exp| {
13391354 if (exp.options.section) |section_name| {
......@@ -1366,7 +1381,7 @@ pub fn updateDeclExports(
13661381 };
13671382 const n_type = decl_sym.n_type | macho.N_EXT;
13681383 if (exp.link.macho.sym_index) |i| {
1369 const sym = &self.global_symbols.items[i];
1384 const sym = &self.globals.items[i];
13701385 sym.* = .{
13711386 .n_strx = try self.updateString(sym.n_strx, exp.options.name),
13721387 .n_type = n_type,
......@@ -1376,12 +1391,12 @@ pub fn updateDeclExports(
13761391 };
13771392 } else {
13781393 const name_str_index = try self.makeString(exp.options.name);
1379 const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {
1380 _ = self.global_symbols.addOneAssumeCapacity();
1394 const i = if (self.globals_free_list.popOrNull()) |i| i else blk: {
1395 _ = self.globals.addOneAssumeCapacity();
13811396 self.export_info_dirty = true;
1382 break :blk self.global_symbols.items.len - 1;
1397 break :blk self.globals.items.len - 1;
13831398 };
1384 self.global_symbols.items[i] = .{
1399 self.globals.items[i] = .{
13851400 .n_strx = name_str_index,
13861401 .n_type = n_type,
13871402 .n_sect = @intCast(u8, self.text_section_index.?) + 1,
......@@ -1396,18 +1411,18 @@ pub fn updateDeclExports(
13961411
13971412pub fn deleteExport(self: *MachO, exp: Export) void {
13981413 const sym_index = exp.sym_index orelse return;
1399 self.global_symbol_free_list.append(self.base.allocator, sym_index) catch {};
1400 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;
14011416}
14021417
14031418pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {
14041419 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
14051420 self.freeTextBlock(&decl.link.macho);
14061421 if (decl.link.macho.local_sym_index != 0) {
1407 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 {};
14081423 self.offset_table_free_list.append(self.base.allocator, decl.link.macho.offset_table_index) catch {};
14091424
1410 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;
14111426
14121427 decl.link.macho.local_sym_index = 0;
14131428 }
......@@ -1415,7 +1430,7 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {
14151430
14161431pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {
14171432 assert(decl.link.macho.local_sym_index != 0);
1418 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;
14191434}
14201435
14211436pub fn populateMissingMetadata(self: *MachO) !void {
......@@ -1555,39 +1570,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
15551570 self.header_dirty = true;
15561571 self.load_commands_dirty = true;
15571572 }
1558 if (self.got_section_index == null) {
1559 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1560 self.got_section_index = @intCast(u16, text_segment.sections.items.len);
1561
1562 const alignment: u2 = switch (self.base.options.target.cpu.arch) {
1563 .x86_64 => 0,
1564 .aarch64 => 2,
1565 else => unreachable, // unhandled architecture type
1566 };
1567 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
1568 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
1569 const off = text_segment.findFreeSpace(needed_size, @alignOf(u64), self.header_pad);
1570 assert(off + needed_size <= text_segment.inner.fileoff + text_segment.inner.filesize); // TODO Must expand __TEXT segment.
1571
1572 log.debug("found __ziggot section free space 0x{x} to 0x{x}", .{ off, off + needed_size });
1573
1574 try text_segment.addSection(self.base.allocator, .{
1575 .sectname = makeStaticString("__ziggot"),
1576 .segname = makeStaticString("__TEXT"),
1577 .addr = text_segment.inner.vmaddr + off,
1578 .size = needed_size,
1579 .offset = @intCast(u32, off),
1580 .@"align" = alignment,
1581 .reloff = 0,
1582 .nreloc = 0,
1583 .flags = flags,
1584 .reserved1 = 0,
1585 .reserved2 = 0,
1586 .reserved3 = 0,
1587 });
1588 self.header_dirty = true;
1589 self.load_commands_dirty = true;
1590 }
15911573 if (self.stubs_section_index == null) {
15921574 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
15931575 self.stubs_section_index = @intCast(u16, text_segment.sections.items.len);
......@@ -1599,7 +1581,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
15991581 };
16001582 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {
16011583 .x86_64 => 6,
1602 .aarch64 => 2 * @sizeOf(u32),
1584 .aarch64 => 3 * @sizeOf(u32),
16031585 else => unreachable, // unhandled architecture type
16041586 };
16051587 const flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
......@@ -1688,9 +1670,9 @@ pub fn populateMissingMetadata(self: *MachO) !void {
16881670 self.header_dirty = true;
16891671 self.load_commands_dirty = true;
16901672 }
1691 if (self.data_got_section_index == null) {
1673 if (self.got_section_index == null) {
16921674 const dc_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
1693 self.data_got_section_index = @intCast(u16, dc_segment.sections.items.len);
1675 self.got_section_index = @intCast(u16, dc_segment.sections.items.len);
16941676
16951677 const flags = macho.S_NON_LAZY_SYMBOL_POINTERS;
16961678 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
......@@ -2062,12 +2044,12 @@ pub fn populateMissingMetadata(self: *MachO) !void {
20622044 self.header_dirty = true;
20632045 self.load_commands_dirty = true;
20642046 }
2065 if (!self.extern_nonlazy_symbols.contains("dyld_stub_binder")) {
2066 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);
20672049 const name = try self.base.allocator.dupe(u8, "dyld_stub_binder");
20682050 const offset = try self.makeString("dyld_stub_binder");
2069 try self.extern_nonlazy_symbols.putNoClobber(self.base.allocator, name, .{
2070 .inner = .{
2051 try self.nonlazy_imports.putNoClobber(self.base.allocator, name, .{
2052 .symbol = .{
20712053 .n_strx = offset,
20722054 .n_type = std.macho.N_UNDF | std.macho.N_EXT,
20732055 .n_sect = 0,
......@@ -2075,68 +2057,19 @@ pub fn populateMissingMetadata(self: *MachO) !void {
20752057 .n_value = 0,
20762058 },
20772059 .dylib_ordinal = 1, // TODO this is currently hardcoded.
2078 .segment = self.data_const_segment_cmd_index.?,
2079 .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,
20802067 });
2068 try self.writeOffsetTableEntry(off_index);
20812069 self.binding_info_dirty = true;
20822070 }
20832071 if (self.stub_helper_stubs_start_off == null) {
2084 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2085 const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?];
2086 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2087 const data = &data_segment.sections.items[self.data_section_index.?];
2088 const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2089 const got = &data_const_segment.sections.items[self.data_got_section_index.?];
2090 switch (self.base.options.target.cpu.arch) {
2091 .x86_64 => {
2092 const code_size = 15;
2093 var code: [code_size]u8 = undefined;
2094 // lea %r11, [rip + disp]
2095 code[0] = 0x4c;
2096 code[1] = 0x8d;
2097 code[2] = 0x1d;
2098 {
2099 const displacement = try math.cast(u32, data.addr - stub_helper.addr - 7);
2100 mem.writeIntLittle(u32, code[3..7], displacement);
2101 }
2102 // push %r11
2103 code[7] = 0x41;
2104 code[8] = 0x53;
2105 // jmp [rip + disp]
2106 code[9] = 0xff;
2107 code[10] = 0x25;
2108 {
2109 const displacement = try math.cast(u32, got.addr - stub_helper.addr - code_size);
2110 mem.writeIntLittle(u32, code[11..], displacement);
2111 }
2112 self.stub_helper_stubs_start_off = stub_helper.offset + code_size;
2113 try self.base.file.?.pwriteAll(&code, stub_helper.offset);
2114 },
2115 .aarch64 => {
2116 var code: [4 * @sizeOf(u32)]u8 = undefined;
2117 {
2118 const displacement = try math.cast(i21, data.addr - stub_helper.addr);
2119 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, displacement).toU32());
2120 }
2121 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.stp(
2122 .x16,
2123 .x17,
2124 aarch64.Register.sp,
2125 aarch64.Instruction.LoadStorePairOffset.pre_index(-16),
2126 ).toU32());
2127 {
2128 const displacement = try math.divExact(u64, got.addr - stub_helper.addr - 2 * @sizeOf(u32), 4);
2129 const literal = try math.cast(u19, displacement);
2130 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.ldr(.x16, .{
2131 .literal = literal,
2132 }).toU32());
2133 }
2134 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.br(.x16).toU32());
2135 self.stub_helper_stubs_start_off = stub_helper.offset + 4 * @sizeOf(u32);
2136 try self.base.file.?.pwriteAll(&code, stub_helper.offset);
2137 },
2138 else => unreachable,
2139 }
2072 try self.writeStubHelperPreamble();
21402073 }
21412074}
21422075
......@@ -2161,7 +2094,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
21612094 const big_block = self.text_block_free_list.items[i];
21622095 // We now have a pointer to a live text block that has too much capacity.
21632096 // Is it enough that we could fit this new text block?
2164 const sym = self.local_symbols.items[big_block.local_sym_index];
2097 const sym = self.locals.items[big_block.local_sym_index];
21652098 const capacity = big_block.capacity(self.*);
21662099 const ideal_capacity = padToIdeal(capacity);
21672100 const ideal_capacity_end_vaddr = sym.n_value + ideal_capacity;
......@@ -2192,7 +2125,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
21922125 }
21932126 break :blk new_start_vaddr;
21942127 } else if (self.last_text_block) |last| {
2195 const last_symbol = self.local_symbols.items[last.local_sym_index];
2128 const last_symbol = self.locals.items[last.local_sym_index];
21962129 // TODO We should pad out the excess capacity with NOPs. For executables,
21972130 // no padding seems to be OK, but it will probably not be for objects.
21982131 const ideal_capacity = padToIdeal(last.size);
......@@ -2290,12 +2223,12 @@ fn updateString(self: *MachO, old_str_off: u32, new_name: []const u8) !u32 {
22902223}
22912224
22922225pub fn addExternSymbol(self: *MachO, name: []const u8) !u32 {
2293 const index = @intCast(u32, self.extern_lazy_symbols.items().len);
2226 const index = @intCast(u32, self.lazy_imports.items().len);
22942227 const offset = try self.makeString(name);
22952228 const sym_name = try self.base.allocator.dupe(u8, name);
22962229 const dylib_ordinal = 1; // TODO this is now hardcoded, since we only support libSystem.
2297 try self.extern_lazy_symbols.putNoClobber(self.base.allocator, sym_name, .{
2298 .inner = .{
2230 try self.lazy_imports.putNoClobber(self.base.allocator, sym_name, .{
2231 .symbol = .{
22992232 .n_strx = offset,
23002233 .n_type = macho.N_UNDF | macho.N_EXT,
23012234 .n_sect = 0,
......@@ -2303,6 +2236,7 @@ pub fn addExternSymbol(self: *MachO, name: []const u8) !u32 {
23032236 .n_value = 0,
23042237 },
23052238 .dylib_ordinal = dylib_ordinal,
2239 .index = index,
23062240 });
23072241 log.debug("adding new extern symbol '{s}' with dylib ordinal '{}'", .{ name, dylib_ordinal });
23082242 return index;
......@@ -2461,41 +2395,29 @@ fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16, sta
24612395}
24622396
24632397fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
2464 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2465 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.?];
24662400 const off = sect.offset + @sizeOf(u64) * index;
2467 const vmaddr = sect.addr + @sizeOf(u64) * index;
24682401
24692402 if (self.offset_table_count_dirty) {
24702403 // TODO relocate.
24712404 self.offset_table_count_dirty = false;
24722405 }
24732406
2474 var code: [8]u8 = undefined;
2475 switch (self.base.options.target.cpu.arch) {
2476 .x86_64 => {
2477 const pos_symbol_off = try math.cast(u31, vmaddr - self.offset_table.items[index] + 7);
2478 const symbol_off = @bitCast(u32, @as(i32, pos_symbol_off) * -1);
2479 // lea %rax, [rip - disp]
2480 code[0] = 0x48;
2481 code[1] = 0x8D;
2482 code[2] = 0x5;
2483 mem.writeIntLittle(u32, code[3..7], symbol_off);
2484 // ret
2485 code[7] = 0xC3;
2486 },
2487 .aarch64 => {
2488 const pos_symbol_off = try math.cast(u20, vmaddr - self.offset_table.items[index]);
2489 const symbol_off = @as(i21, pos_symbol_off) * -1;
2490 // adr x0, #-disp
2491 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x0, symbol_off).toU32());
2492 // ret x28
2493 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ret(.x28).toU32());
2494 },
2495 else => unreachable, // unsupported target architecture
2496 }
2497 log.debug("writing offset table entry 0x{x} at 0x{x}", .{ self.offset_table.items[index], off });
2498 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);
24992421}
25002422
25012423fn writeLazySymbolPointer(self: *MachO, index: u32) !void {
......@@ -2518,6 +2440,133 @@ fn writeLazySymbolPointer(self: *MachO, index: u32) !void {
25182440 try self.base.file.?.pwriteAll(&buf, off);
25192441}
25202442
2443fn writeStubHelperPreamble(self: *MachO) !void {
2444 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2445 const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?];
2446 const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2447 const got = &data_const_segment.sections.items[self.got_section_index.?];
2448 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2449 const data = &data_segment.sections.items[self.data_section_index.?];
2450
2451 switch (self.base.options.target.cpu.arch) {
2452 .x86_64 => {
2453 const code_size = 15;
2454 var code: [code_size]u8 = undefined;
2455 // lea %r11, [rip + disp]
2456 code[0] = 0x4c;
2457 code[1] = 0x8d;
2458 code[2] = 0x1d;
2459 {
2460 const target_addr = data.addr;
2461 const displacement = try math.cast(u32, target_addr - stub_helper.addr - 7);
2462 mem.writeIntLittle(u32, code[3..7], displacement);
2463 }
2464 // push %r11
2465 code[7] = 0x41;
2466 code[8] = 0x53;
2467 // jmp [rip + disp]
2468 code[9] = 0xff;
2469 code[10] = 0x25;
2470 {
2471 const displacement = try math.cast(u32, got.addr - stub_helper.addr - code_size);
2472 mem.writeIntLittle(u32, code[11..], displacement);
2473 }
2474 try self.base.file.?.pwriteAll(&code, stub_helper.offset);
2475 self.stub_helper_stubs_start_off = stub_helper.offset + code_size;
2476 },
2477 .aarch64 => {
2478 var code: [6 * @sizeOf(u32)]u8 = undefined;
2479
2480 data_blk_outer: {
2481 const this_addr = stub_helper.addr;
2482 const target_addr = data.addr;
2483 data_blk: {
2484 const displacement = math.cast(i21, target_addr - this_addr) catch |_| break :data_blk;
2485 // adr x17, disp
2486 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, displacement).toU32());
2487 // nop
2488 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32());
2489 break :data_blk_outer;
2490 }
2491 data_blk: {
2492 const new_this_addr = this_addr + @sizeOf(u32);
2493 const displacement = math.cast(i21, target_addr - new_this_addr) catch |_| break :data_blk;
2494 // nop
2495 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32());
2496 // adr x17, disp
2497 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.adr(.x17, displacement).toU32());
2498 break :data_blk_outer;
2499 }
2500 // Jump is too big, replace adr with adrp and add.
2501 const this_page = @intCast(i32, this_addr >> 12);
2502 const target_page = @intCast(i32, target_addr >> 12);
2503 const pages = @intCast(i21, target_page - this_page);
2504 // adrp x17, pages
2505 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x17, pages).toU32());
2506 const narrowed = @truncate(u12, target_addr);
2507 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.add(.x17, .x17, narrowed, false).toU32());
2508 }
2509
2510 // stp x16, x17, [sp, #-16]!
2511 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.stp(
2512 .x16,
2513 .x17,
2514 aarch64.Register.sp,
2515 aarch64.Instruction.LoadStorePairOffset.pre_index(-16),
2516 ).toU32());
2517
2518 binder_blk_outer: {
2519 const this_addr = stub_helper.addr + 3 * @sizeOf(u32);
2520 const target_addr = got.addr;
2521 binder_blk: {
2522 const displacement = math.divExact(u64, target_addr - this_addr, 4) catch |_| break :binder_blk;
2523 const literal = math.cast(u18, displacement) catch |_| break :binder_blk;
2524 // ldr x16, label
2525 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.ldr(.x16, .{
2526 .literal = literal,
2527 }).toU32());
2528 // nop
2529 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.nop().toU32());
2530 break :binder_blk_outer;
2531 }
2532 binder_blk: {
2533 const new_this_addr = this_addr + @sizeOf(u32);
2534 const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch |_| break :binder_blk;
2535 const literal = math.cast(u18, displacement) catch |_| break :binder_blk;
2536 // nop
2537 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.nop().toU32());
2538 // ldr x16, label
2539 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{
2540 .literal = literal,
2541 }).toU32());
2542 break :binder_blk_outer;
2543 }
2544 // Jump is too big, replace ldr with adrp and ldr(register).
2545 const this_page = @intCast(i32, this_addr >> 12);
2546 const target_page = @intCast(i32, target_addr >> 12);
2547 const pages = @intCast(i21, target_page - this_page);
2548 // adrp x16, pages
2549 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.adrp(.x16, pages).toU32());
2550 const narrowed = @truncate(u12, target_addr);
2551 const offset = try math.divExact(u12, narrowed, 8);
2552 // ldr x16, x16, offset
2553 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{
2554 .register = .{
2555 .rn = .x16,
2556 .offset = aarch64.Instruction.LoadStoreOffset.imm(offset),
2557 },
2558 }).toU32());
2559 }
2560
2561 // br x16
2562 mem.writeIntLittle(u32, code[20..24], aarch64.Instruction.br(.x16).toU32());
2563 try self.base.file.?.pwriteAll(&code, stub_helper.offset);
2564 self.stub_helper_stubs_start_off = stub_helper.offset + code.len;
2565 },
2566 else => unreachable,
2567 }
2568}
2569
25212570fn writeStub(self: *MachO, index: u32) !void {
25222571 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
25232572 const stubs = text_segment.sections.items[self.stubs_section_index.?];
......@@ -2527,9 +2576,12 @@ fn writeStub(self: *MachO, index: u32) !void {
25272576 const stub_off = stubs.offset + index * stubs.reserved2;
25282577 const stub_addr = stubs.addr + index * stubs.reserved2;
25292578 const la_ptr_addr = la_symbol_ptr.addr + index * @sizeOf(u64);
2579
25302580 log.debug("writing stub at 0x{x}", .{stub_off});
2581
25312582 var code = try self.base.allocator.alloc(u8, stubs.reserved2);
25322583 defer self.base.allocator.free(code);
2584
25332585 switch (self.base.options.target.cpu.arch) {
25342586 .x86_64 => {
25352587 assert(la_ptr_addr >= stub_addr + stubs.reserved2);
......@@ -2541,12 +2593,50 @@ fn writeStub(self: *MachO, index: u32) !void {
25412593 },
25422594 .aarch64 => {
25432595 assert(la_ptr_addr >= stub_addr);
2544 const displacement = try math.divExact(u64, la_ptr_addr - stub_addr, 4);
2545 const literal = try math.cast(u19, displacement);
2546 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{
2547 .literal = literal,
2548 }).toU32());
2549 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());
25502640 },
25512641 else => unreachable,
25522642 }
......@@ -2563,8 +2653,10 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {
25632653 else => unreachable,
25642654 };
25652655 const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size;
2656
25662657 var code = try self.base.allocator.alloc(u8, stub_size);
25672658 defer self.base.allocator.free(code);
2659
25682660 switch (self.base.options.target.cpu.arch) {
25692661 .x86_64 => {
25702662 const displacement = try math.cast(
......@@ -2579,12 +2671,19 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {
25792671 mem.writeIntLittle(u32, code[6..][0..4], @bitCast(u32, displacement));
25802672 },
25812673 .aarch64 => {
2582 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
25832679 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{
2584 .literal = @divExact(stub_size - @sizeOf(u32), 4),
2680 .literal = literal,
25852681 }).toU32());
2682 const displacement = try math.cast(i28, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4);
2683 // b disp
25862684 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(displacement).toU32());
2587 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);
25882687 },
25892688 else => unreachable,
25902689 }
......@@ -2593,9 +2692,9 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {
25932692
25942693fn relocateSymbolTable(self: *MachO) !void {
25952694 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2596 const nlocals = self.local_symbols.items.len;
2597 const nglobals = self.global_symbols.items.len;
2598 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;
25992698 const nsyms = nlocals + nglobals + nundefs;
26002699
26012700 if (symtab.nsyms < nsyms) {
......@@ -2630,7 +2729,7 @@ fn writeLocalSymbol(self: *MachO, index: usize) !void {
26302729 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
26312730 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;
26322731 log.debug("writing local symbol {} at 0x{x}", .{ index, off });
2633 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);
26342733}
26352734
26362735fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
......@@ -2639,18 +2738,18 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
26392738
26402739 try self.relocateSymbolTable();
26412740 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2642 const nlocals = self.local_symbols.items.len;
2643 const nglobals = self.global_symbols.items.len;
2741 const nlocals = self.locals.items.len;
2742 const nglobals = self.globals.items.len;
26442743
2645 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;
26462745 var undefs = std.ArrayList(macho.nlist_64).init(self.base.allocator);
26472746 defer undefs.deinit();
26482747 try undefs.ensureCapacity(nundefs);
2649 for (self.extern_lazy_symbols.items()) |entry| {
2650 undefs.appendAssumeCapacity(entry.value.inner);
2748 for (self.lazy_imports.items()) |entry| {
2749 undefs.appendAssumeCapacity(entry.value.symbol);
26512750 }
2652 for (self.extern_nonlazy_symbols.items()) |entry| {
2653 undefs.appendAssumeCapacity(entry.value.inner);
2751 for (self.nonlazy_imports.items()) |entry| {
2752 undefs.appendAssumeCapacity(entry.value.symbol);
26542753 }
26552754
26562755 const locals_off = symtab.symoff;
......@@ -2659,7 +2758,7 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
26592758 const globals_off = locals_off + locals_size;
26602759 const globals_size = nglobals * @sizeOf(macho.nlist_64);
26612760 log.debug("writing global symbols from 0x{x} to 0x{x}", .{ globals_off, globals_size + globals_off });
2662 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);
26632762
26642763 const undefs_off = globals_off + globals_size;
26652764 const undefs_size = nundefs * @sizeOf(macho.nlist_64);
......@@ -2685,15 +2784,15 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
26852784 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
26862785 const stubs = &text_segment.sections.items[self.stubs_section_index.?];
26872786 const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2688 const got = &data_const_seg.sections.items[self.data_got_section_index.?];
2787 const got = &data_const_seg.sections.items[self.got_section_index.?];
26892788 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
26902789 const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
26912790 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
26922791
2693 const lazy = self.extern_lazy_symbols.items();
2694 const nonlazy = self.extern_nonlazy_symbols.items();
2792 const lazy = self.lazy_imports.items();
2793 const got_entries = self.offset_table.items;
26952794 const allocated_size = self.allocatedSizeLinkedit(dysymtab.indirectsymoff);
2696 const nindirectsyms = @intCast(u32, lazy.len * 2 + nonlazy.len);
2795 const nindirectsyms = @intCast(u32, lazy.len * 2 + got_entries.len);
26972796 const needed_size = @intCast(u32, nindirectsyms * @sizeOf(u32));
26982797
26992798 if (needed_size > allocated_size) {
......@@ -2712,20 +2811,27 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
27122811 var writer = stream.writer();
27132812
27142813 stubs.reserved1 = 0;
2715 for (self.extern_lazy_symbols.items()) |_, i| {
2814 for (lazy) |_, i| {
27162815 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
27172816 try writer.writeIntLittle(u32, symtab_idx);
27182817 }
27192818
27202819 const base_id = @intCast(u32, lazy.len);
27212820 got.reserved1 = base_id;
2722 for (self.extern_nonlazy_symbols.items()) |_, i| {
2723 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i + base_id);
2724 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 }
27252831 }
27262832
2727 la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, nonlazy.len);
2728 for (self.extern_lazy_symbols.items()) |_, i| {
2833 la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, got_entries.len);
2834 for (lazy) |_, i| {
27292835 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
27302836 try writer.writeIntLittle(u32, symtab_idx);
27312837 }
......@@ -2791,7 +2897,7 @@ fn writeCodeSignature(self: *MachO) !void {
27912897
27922898fn writeExportTrie(self: *MachO) !void {
27932899 if (!self.export_info_dirty) return;
2794 if (self.global_symbols.items.len == 0) return;
2900 if (self.globals.items.len == 0) return;
27952901
27962902 const tracy = trace(@src());
27972903 defer tracy.end();
......@@ -2800,7 +2906,7 @@ fn writeExportTrie(self: *MachO) !void {
28002906 defer trie.deinit();
28012907
28022908 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2803 for (self.global_symbols.items) |symbol| {
2909 for (self.globals.items) |symbol| {
28042910 // TODO figure out if we should put all global symbols into the export trie
28052911 const name = self.getString(symbol.n_strx);
28062912 assert(symbol.n_value >= text_segment.inner.vmaddr);
......@@ -2842,14 +2948,48 @@ fn writeRebaseInfoTable(self: *MachO) !void {
28422948 const tracy = trace(@src());
28432949 defer tracy.end();
28442950
2845 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);
28462987 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
28472988 defer self.base.allocator.free(buffer);
28482989
28492990 var stream = std.io.fixedBufferStream(buffer);
2850 try writeRebaseInfo(self.extern_lazy_symbols.items(), stream.writer());
2991 try bind.writeRebaseInfo(pointers.items, stream.writer());
28512992
2852 const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
28532993 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
28542994 const allocated_size = self.allocatedSizeLinkedit(dyld_info.rebase_off);
28552995 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
......@@ -2874,14 +3014,34 @@ fn writeBindingInfoTable(self: *MachO) !void {
28743014 const tracy = trace(@src());
28753015 defer tracy.end();
28763016
2877 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);
28783039 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
28793040 defer self.base.allocator.free(buffer);
28803041
28813042 var stream = std.io.fixedBufferStream(buffer);
2882 try writeBindInfo(self.extern_nonlazy_symbols.items(), stream.writer());
3043 try bind.writeBindInfo(pointers.items, stream.writer());
28833044
2884 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
28853045 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
28863046 const allocated_size = self.allocatedSizeLinkedit(dyld_info.bind_off);
28873047 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
......@@ -2903,14 +3063,36 @@ fn writeBindingInfoTable(self: *MachO) !void {
29033063fn writeLazyBindingInfoTable(self: *MachO) !void {
29043064 if (!self.lazy_binding_info_dirty) return;
29053065
2906 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);
29073090 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
29083091 defer self.base.allocator.free(buffer);
29093092
29103093 var stream = std.io.fixedBufferStream(buffer);
2911 try writeLazyBindInfo(self.extern_lazy_symbols.items(), stream.writer());
3094 try bind.writeLazyBindInfo(pointers.items, stream.writer());
29123095
2913 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
29143096 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
29153097 const allocated_size = self.allocatedSizeLinkedit(dyld_info.lazy_bind_off);
29163098 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
......@@ -2931,7 +3113,7 @@ fn writeLazyBindingInfoTable(self: *MachO) !void {
29313113}
29323114
29333115fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
2934 if (self.extern_lazy_symbols.items().len == 0) return;
3116 if (self.lazy_imports.items().len == 0) return;
29353117
29363118 var stream = std.io.fixedBufferStream(buffer);
29373119 var reader = stream.reader();
......@@ -2977,7 +3159,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
29773159 else => {},
29783160 }
29793161 }
2980 assert(self.extern_lazy_symbols.items().len <= offsets.items.len);
3162 assert(self.lazy_imports.items().len <= offsets.items.len);
29813163
29823164 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {
29833165 .x86_64 => 10,
......@@ -2990,7 +3172,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
29903172 else => unreachable,
29913173 };
29923174 var buf: [@sizeOf(u32)]u8 = undefined;
2993 for (self.extern_lazy_symbols.items()) |_, i| {
3175 for (self.lazy_imports.items()) |_, i| {
29943176 const placeholder_off = self.stub_helper_stubs_start_off.? + i * stub_size + off;
29953177 mem.writeIntLittle(u32, &buf, offsets.items[i]);
29963178 try self.base.file.?.pwriteAll(&buf, placeholder_off);
......@@ -3104,177 +3286,6 @@ fn writeHeader(self: *MachO) !void {
31043286 self.header_dirty = false;
31053287}
31063288
3107/// Parse MachO contents from existing binary file.
3108fn parseFromFile(self: *MachO, file: fs.File) !void {
3109 self.base.file = file;
3110 var reader = file.reader();
3111 const header = try reader.readStruct(macho.mach_header_64);
3112 try self.load_commands.ensureCapacity(self.base.allocator, header.ncmds);
3113 var i: u16 = 0;
3114 while (i < header.ncmds) : (i += 1) {
3115 const cmd = try LoadCommand.read(self.base.allocator, reader);
3116 switch (cmd.cmd()) {
3117 macho.LC_SEGMENT_64 => {
3118 const x = cmd.Segment;
3119 if (parseAndCmpName(&x.inner.segname, "__PAGEZERO")) {
3120 self.pagezero_segment_cmd_index = i;
3121 } else if (parseAndCmpName(&x.inner.segname, "__LINKEDIT")) {
3122 self.linkedit_segment_cmd_index = i;
3123 } else if (parseAndCmpName(&x.inner.segname, "__TEXT")) {
3124 self.text_segment_cmd_index = i;
3125 for (x.sections.items) |sect, j| {
3126 if (parseAndCmpName(&sect.sectname, "__text")) {
3127 self.text_section_index = @intCast(u16, j);
3128 }
3129 }
3130 } else if (parseAndCmpName(&x.inner.segname, "__DATA")) {
3131 self.data_segment_cmd_index = i;
3132 } else if (parseAndCmpName(&x.inner.segname, "__DATA_CONST")) {
3133 self.data_const_segment_cmd_index = i;
3134 }
3135 },
3136 macho.LC_DYLD_INFO_ONLY => {
3137 self.dyld_info_cmd_index = i;
3138 },
3139 macho.LC_SYMTAB => {
3140 self.symtab_cmd_index = i;
3141 },
3142 macho.LC_DYSYMTAB => {
3143 self.dysymtab_cmd_index = i;
3144 },
3145 macho.LC_LOAD_DYLINKER => {
3146 self.dylinker_cmd_index = i;
3147 },
3148 macho.LC_VERSION_MIN_MACOSX, macho.LC_VERSION_MIN_IPHONEOS, macho.LC_VERSION_MIN_WATCHOS, macho.LC_VERSION_MIN_TVOS => {
3149 self.version_min_cmd_index = i;
3150 },
3151 macho.LC_SOURCE_VERSION => {
3152 self.source_version_cmd_index = i;
3153 },
3154 macho.LC_UUID => {
3155 self.uuid_cmd_index = i;
3156 },
3157 macho.LC_MAIN => {
3158 self.main_cmd_index = i;
3159 },
3160 macho.LC_LOAD_DYLIB => {
3161 const x = cmd.Dylib;
3162 if (parseAndCmpName(x.data, mem.spanZ(LIB_SYSTEM_PATH))) {
3163 self.libsystem_cmd_index = i;
3164 }
3165 },
3166 macho.LC_FUNCTION_STARTS => {
3167 self.function_starts_cmd_index = i;
3168 },
3169 macho.LC_DATA_IN_CODE => {
3170 self.data_in_code_cmd_index = i;
3171 },
3172 macho.LC_CODE_SIGNATURE => {
3173 self.code_signature_cmd_index = i;
3174 },
3175 else => {
3176 log.warn("Unknown load command detected: 0x{x}.", .{cmd.cmd()});
3177 },
3178 }
3179 self.load_commands.appendAssumeCapacity(cmd);
3180 }
3181 self.header = header;
3182}
3183
3184fn parseAndCmpName(name: []const u8, needle: []const u8) bool {
3185 const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len;
3186 return mem.eql(u8, name[0..len], needle);
3187}
3188
3189fn parseSymbolTable(self: *MachO) !void {
3190 const symtab = self.load_commands.items[self.symtab_cmd_index.?].Symtab;
3191 const dysymtab = self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
3192
3193 var buffer = try self.base.allocator.alloc(macho.nlist_64, symtab.nsyms);
3194 defer self.base.allocator.free(buffer);
3195 const nread = try self.base.file.?.preadAll(@ptrCast([*]u8, buffer)[0 .. symtab.nsyms * @sizeOf(macho.nlist_64)], symtab.symoff);
3196 assert(@divExact(nread, @sizeOf(macho.nlist_64)) == buffer.len);
3197
3198 try self.local_symbols.ensureCapacity(self.base.allocator, dysymtab.nlocalsym);
3199 try self.global_symbols.ensureCapacity(self.base.allocator, dysymtab.nextdefsym);
3200 try self.undef_symbols.ensureCapacity(self.base.allocator, dysymtab.nundefsym);
3201
3202 self.local_symbols.appendSliceAssumeCapacity(buffer[dysymtab.ilocalsym .. dysymtab.ilocalsym + dysymtab.nlocalsym]);
3203 self.global_symbols.appendSliceAssumeCapacity(buffer[dysymtab.iextdefsym .. dysymtab.iextdefsym + dysymtab.nextdefsym]);
3204 self.undef_symbols.appendSliceAssumeCapacity(buffer[dysymtab.iundefsym .. dysymtab.iundefsym + dysymtab.nundefsym]);
3205}
3206
3207fn parseStringTable(self: *MachO) !void {
3208 const symtab = self.load_commands.items[self.symtab_cmd_index.?].Symtab;
3209
3210 var buffer = try self.base.allocator.alloc(u8, symtab.strsize);
3211 defer self.base.allocator.free(buffer);
3212 const nread = try self.base.file.?.preadAll(buffer, symtab.stroff);
3213 assert(nread == buffer.len);
3214
3215 try self.string_table.ensureCapacity(self.base.allocator, symtab.strsize);
3216 self.string_table.appendSliceAssumeCapacity(buffer);
3217}
3218
3219fn fixupBindInfo(self: *MachO, dylib_ordinal: u32) !void {
3220 const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
3221 var buffer = try self.base.allocator.alloc(u8, dyld_info.bind_size);
3222 defer self.base.allocator.free(buffer);
3223 const nread = try self.base.file.?.preadAll(buffer, dyld_info.bind_off);
3224 assert(nread == buffer.len);
3225 try self.fixupInfoCommon(buffer, dylib_ordinal);
3226 try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off);
3227}
3228
3229fn fixupLazyBindInfo(self: *MachO, dylib_ordinal: u32) !void {
3230 const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
3231 var buffer = try self.base.allocator.alloc(u8, dyld_info.lazy_bind_size);
3232 defer self.base.allocator.free(buffer);
3233 const nread = try self.base.file.?.preadAll(buffer, dyld_info.lazy_bind_off);
3234 assert(nread == buffer.len);
3235 try self.fixupInfoCommon(buffer, dylib_ordinal);
3236 try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off);
3237}
3238
3239fn fixupInfoCommon(self: *MachO, buffer: []u8, dylib_ordinal: u32) !void {
3240 var stream = std.io.fixedBufferStream(buffer);
3241 var reader = stream.reader();
3242
3243 while (true) {
3244 const inst = reader.readByte() catch |err| switch (err) {
3245 error.EndOfStream => break,
3246 else => return err,
3247 };
3248 const imm: u8 = inst & macho.BIND_IMMEDIATE_MASK;
3249 const opcode: u8 = inst & macho.BIND_OPCODE_MASK;
3250
3251 switch (opcode) {
3252 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
3253 var next = try reader.readByte();
3254 while (next != @as(u8, 0)) {
3255 next = try reader.readByte();
3256 }
3257 },
3258 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
3259 _ = try std.leb.readULEB128(u64, reader);
3260 },
3261 macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => {
3262 // Perform the fixup.
3263 try stream.seekBy(-1);
3264 var writer = stream.writer();
3265 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, dylib_ordinal));
3266 },
3267 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
3268 _ = try std.leb.readULEB128(u64, reader);
3269 },
3270 macho.BIND_OPCODE_SET_ADDEND_SLEB => {
3271 _ = try std.leb.readILEB128(i64, reader);
3272 },
3273 else => {},
3274 }
3275 }
3276}
3277
32783289pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
32793290 // TODO https://github.com/ziglang/zig/issues/1284
32803291 return std.math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch
src/link/MachO/Archive.zig created+278
......@@ -0,0 +1,278 @@
1const Archive = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const fs = std.fs;
6const log = std.log.scoped(.archive);
7const macho = std.macho;
8const mem = std.mem;
9
10const Allocator = mem.Allocator;
11const Object = @import("Object.zig");
12const parseName = @import("Zld.zig").parseName;
13
14usingnamespace @import("commands.zig");
15
16allocator: *Allocator,
17file: fs.File,
18header: ar_hdr,
19name: []u8,
20
21objects: std.ArrayListUnmanaged(Object) = .{},
22
23/// Parsed table of contents.
24/// Each symbol name points to a list of all definition
25/// sites within the current static archive.
26toc: std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(u32)) = .{},
27
28// Archive files start with the ARMAG identifying string. Then follows a
29// `struct ar_hdr', and as many bytes of member file data as its `ar_size'
30// member indicates, for each member file.
31/// String that begins an archive file.
32const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n";
33/// Size of that string.
34const SARMAG: u4 = 8;
35
36/// String in ar_fmag at the end of each header.
37const ARFMAG: *const [2:0]u8 = "`\n";
38
39const ar_hdr = extern struct {
40 /// Member file name, sometimes / terminated.
41 ar_name: [16]u8,
42
43 /// File date, decimal seconds since Epoch.
44 ar_date: [12]u8,
45
46 /// User ID, in ASCII format.
47 ar_uid: [6]u8,
48
49 /// Group ID, in ASCII format.
50 ar_gid: [6]u8,
51
52 /// File mode, in ASCII octal.
53 ar_mode: [8]u8,
54
55 /// File size, in ASCII decimal.
56 ar_size: [10]u8,
57
58 /// Always contains ARFMAG.
59 ar_fmag: [2]u8,
60
61 const NameOrLength = union(enum) {
62 Name: []const u8,
63 Length: u64,
64 };
65 pub fn nameOrLength(self: ar_hdr) !NameOrLength {
66 const value = getValue(&self.ar_name);
67 const slash_index = mem.indexOf(u8, value, "/") orelse return error.MalformedArchive;
68 const len = value.len;
69 if (slash_index == len - 1) {
70 // Name stored directly
71 return NameOrLength{ .Name = value };
72 } else {
73 // Name follows the header directly and its length is encoded in
74 // the name field.
75 const length = try std.fmt.parseInt(u64, value[slash_index + 1 ..], 10);
76 return NameOrLength{ .Length = length };
77 }
78 }
79
80 pub fn size(self: ar_hdr) !u64 {
81 const value = getValue(&self.ar_size);
82 return std.fmt.parseInt(u64, value, 10);
83 }
84
85 fn getValue(raw: []const u8) []const u8 {
86 return mem.trimRight(u8, raw, &[_]u8{@as(u8, 0x20)});
87 }
88};
89
90pub fn deinit(self: *Archive) void {
91 self.allocator.free(self.name);
92 for (self.objects.items) |*object| {
93 object.deinit();
94 }
95 self.objects.deinit(self.allocator);
96 for (self.toc.items()) |*entry| {
97 self.allocator.free(entry.key);
98 entry.value.deinit(self.allocator);
99 }
100 self.toc.deinit(self.allocator);
101 self.file.close();
102}
103
104/// Caller owns the returned Archive instance and is responsible for calling
105/// `deinit` to free allocated memory.
106pub fn initFromFile(allocator: *Allocator, arch: std.Target.Cpu.Arch, ar_name: []const u8, file: fs.File) !Archive {
107 var reader = file.reader();
108 var magic = try readMagic(allocator, reader);
109 defer allocator.free(magic);
110
111 if (!mem.eql(u8, magic, ARMAG)) {
112 // Reset file cursor.
113 try file.seekTo(0);
114 return error.NotArchive;
115 }
116
117 const header = try reader.readStruct(ar_hdr);
118
119 if (!mem.eql(u8, &header.ar_fmag, ARFMAG))
120 return error.MalformedArchive;
121
122 var embedded_name = try getName(allocator, header, reader);
123 log.debug("parsing archive '{s}' at '{s}'", .{ embedded_name, ar_name });
124 defer allocator.free(embedded_name);
125
126 var name = try allocator.dupe(u8, ar_name);
127 var self = Archive{
128 .allocator = allocator,
129 .file = file,
130 .header = header,
131 .name = name,
132 };
133
134 var object_offsets = try self.readTableOfContents(reader);
135 defer self.allocator.free(object_offsets);
136
137 var i: usize = 1;
138 while (i < object_offsets.len) : (i += 1) {
139 const offset = object_offsets[i];
140 try reader.context.seekTo(offset);
141 try self.readObject(arch, ar_name, reader);
142 }
143
144 return self;
145}
146
147fn readTableOfContents(self: *Archive, reader: anytype) ![]u32 {
148 const symtab_size = try reader.readIntLittle(u32);
149 var symtab = try self.allocator.alloc(u8, symtab_size);
150 defer self.allocator.free(symtab);
151 try reader.readNoEof(symtab);
152
153 const strtab_size = try reader.readIntLittle(u32);
154 var strtab = try self.allocator.alloc(u8, strtab_size);
155 defer self.allocator.free(strtab);
156 try reader.readNoEof(strtab);
157
158 var symtab_stream = std.io.fixedBufferStream(symtab);
159 var symtab_reader = symtab_stream.reader();
160
161 var object_offsets = std.ArrayList(u32).init(self.allocator);
162 try object_offsets.append(0);
163 var last: usize = 0;
164
165 while (true) {
166 const n_strx = symtab_reader.readIntLittle(u32) catch |err| switch (err) {
167 error.EndOfStream => break,
168 else => |e| return e,
169 };
170 const object_offset = try symtab_reader.readIntLittle(u32);
171
172 const sym_name = mem.spanZ(@ptrCast([*:0]const u8, strtab.ptr + n_strx));
173 const owned_name = try self.allocator.dupe(u8, sym_name);
174 const res = try self.toc.getOrPut(self.allocator, owned_name);
175 defer if (res.found_existing) self.allocator.free(owned_name);
176
177 if (!res.found_existing) {
178 res.entry.value = .{};
179 }
180
181 try res.entry.value.append(self.allocator, object_offset);
182
183 // TODO This will go once we properly use archive's TOC to pick
184 // an object which defines a missing symbol rather than pasting in
185 // all of the objects always.
186 // Here, we assume that symbols are NOT sorted in any way, and
187 // they point to objects in sequence.
188 if (object_offsets.items[last] != object_offset) {
189 try object_offsets.append(object_offset);
190 last += 1;
191 }
192 }
193
194 return object_offsets.toOwnedSlice();
195}
196
197fn readObject(self: *Archive, arch: std.Target.Cpu.Arch, ar_name: []const u8, reader: anytype) !void {
198 const object_header = try reader.readStruct(ar_hdr);
199
200 if (!mem.eql(u8, &object_header.ar_fmag, ARFMAG))
201 return error.MalformedArchive;
202
203 var object_name = try getName(self.allocator, object_header, reader);
204 log.debug("extracting object '{s}' from archive '{s}'", .{ object_name, self.name });
205
206 const offset = @intCast(u32, try reader.context.getPos());
207 const header = try reader.readStruct(macho.mach_header_64);
208
209 const this_arch: std.Target.Cpu.Arch = switch (header.cputype) {
210 macho.CPU_TYPE_ARM64 => .aarch64,
211 macho.CPU_TYPE_X86_64 => .x86_64,
212 else => |value| {
213 log.err("unsupported cpu architecture 0x{x}", .{value});
214 return error.UnsupportedCpuArchitecture;
215 },
216 };
217 if (this_arch != arch) {
218 log.err("mismatched cpu architecture: found {s}, expected {s}", .{ this_arch, arch });
219 return error.MismatchedCpuArchitecture;
220 }
221
222 // TODO Implement std.fs.File.clone() or similar.
223 var new_file = try fs.cwd().openFile(ar_name, .{});
224 var object = Object{
225 .allocator = self.allocator,
226 .name = object_name,
227 .ar_name = try mem.dupe(self.allocator, u8, ar_name),
228 .file = new_file,
229 .header = header,
230 };
231
232 try object.readLoadCommands(reader, .{ .offset = offset });
233
234 if (object.symtab_cmd_index != null) {
235 try object.readSymtab();
236 try object.readStrtab();
237 }
238
239 if (object.data_in_code_cmd_index != null) try object.readDataInCode();
240
241 log.debug("\n\n", .{});
242 log.debug("{s} defines symbols", .{object.name});
243 for (object.symtab.items) |sym| {
244 const symname = object.getString(sym.n_strx);
245 log.debug("'{s}': {}", .{ symname, sym });
246 }
247
248 try self.objects.append(self.allocator, object);
249}
250
251fn readMagic(allocator: *Allocator, reader: anytype) ![]u8 {
252 var magic = std.ArrayList(u8).init(allocator);
253 try magic.ensureCapacity(SARMAG);
254 var i: usize = 0;
255 while (i < SARMAG) : (i += 1) {
256 const next = try reader.readByte();
257 magic.appendAssumeCapacity(next);
258 }
259 return magic.toOwnedSlice();
260}
261
262fn getName(allocator: *Allocator, header: ar_hdr, reader: anytype) ![]u8 {
263 const name_or_length = try header.nameOrLength();
264 var name: []u8 = undefined;
265 switch (name_or_length) {
266 .Name => |n| {
267 name = try allocator.dupe(u8, n);
268 },
269 .Length => |len| {
270 var n = try allocator.alloc(u8, len);
271 defer allocator.free(n);
272 try reader.readNoEof(n);
273 const actual_len = mem.indexOfScalar(u8, n, @as(u8, 0)) orelse n.len;
274 name = try allocator.dupe(u8, n[0..actual_len]);
275 },
276 }
277 return name;
278}
src/link/MachO/DebugSymbols.zig+4-4
......@@ -839,8 +839,8 @@ fn findFreeSpaceLinkedit(self: *DebugSymbols, object_size: u64, min_alignment: u
839839
840840fn relocateSymbolTable(self: *DebugSymbols) !void {
841841 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
842 const nlocals = self.base.local_symbols.items.len;
843 const nglobals = self.base.global_symbols.items.len;
842 const nlocals = self.base.locals.items.len;
843 const nglobals = self.base.globals.items.len;
844844 const nsyms = nlocals + nglobals;
845845
846846 if (symtab.nsyms < nsyms) {
......@@ -875,7 +875,7 @@ pub fn writeLocalSymbol(self: *DebugSymbols, index: usize) !void {
875875 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
876876 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;
877877 log.debug("writing dSym local symbol {} at 0x{x}", .{ index, off });
878 try self.file.pwriteAll(mem.asBytes(&self.base.local_symbols.items[index]), off);
878 try self.file.pwriteAll(mem.asBytes(&self.base.locals.items[index]), off);
879879}
880880
881881fn writeStringTable(self: *DebugSymbols) !void {
......@@ -1057,7 +1057,7 @@ pub fn commitDeclDebugInfo(
10571057 var dbg_info_buffer = &debug_buffers.dbg_info_buffer;
10581058 var dbg_info_type_relocs = &debug_buffers.dbg_info_type_relocs;
10591059
1060 const symbol = self.base.local_symbols.items[decl.link.macho.local_sym_index];
1060 const symbol = self.base.locals.items[decl.link.macho.local_sym_index];
10611061 const text_block = &decl.link.macho;
10621062 // If the Decl is a function, we need to update the __debug_line program.
10631063 const typed_value = decl.typed_value.most_recent.typed_value;
src/link/MachO/Object.zig created+229
......@@ -0,0 +1,229 @@
1const Object = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const fs = std.fs;
6const io = std.io;
7const log = std.log.scoped(.object);
8const macho = std.macho;
9const mem = std.mem;
10
11const Allocator = mem.Allocator;
12const parseName = @import("Zld.zig").parseName;
13
14usingnamespace @import("commands.zig");
15
16allocator: *Allocator,
17file: fs.File,
18name: []u8,
19ar_name: ?[]u8 = null,
20
21header: macho.mach_header_64,
22
23load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
24
25segment_cmd_index: ?u16 = null,
26symtab_cmd_index: ?u16 = null,
27dysymtab_cmd_index: ?u16 = null,
28build_version_cmd_index: ?u16 = null,
29data_in_code_cmd_index: ?u16 = null,
30text_section_index: ?u16 = null,
31
32// __DWARF segment sections
33dwarf_debug_info_index: ?u16 = null,
34dwarf_debug_abbrev_index: ?u16 = null,
35dwarf_debug_str_index: ?u16 = null,
36dwarf_debug_line_index: ?u16 = null,
37dwarf_debug_ranges_index: ?u16 = null,
38
39symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
40strtab: std.ArrayListUnmanaged(u8) = .{},
41
42data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
43
44pub fn deinit(self: *Object) void {
45 for (self.load_commands.items) |*lc| {
46 lc.deinit(self.allocator);
47 }
48 self.load_commands.deinit(self.allocator);
49 self.symtab.deinit(self.allocator);
50 self.strtab.deinit(self.allocator);
51 self.data_in_code_entries.deinit(self.allocator);
52 self.allocator.free(self.name);
53 if (self.ar_name) |v| {
54 self.allocator.free(v);
55 }
56 self.file.close();
57}
58
59/// Caller owns the returned Object instance and is responsible for calling
60/// `deinit` to free allocated memory.
61pub fn initFromFile(allocator: *Allocator, arch: std.Target.Cpu.Arch, name: []const u8, file: fs.File) !Object {
62 var reader = file.reader();
63 const header = try reader.readStruct(macho.mach_header_64);
64
65 if (header.filetype != macho.MH_OBJECT) {
66 // Reset file cursor.
67 try file.seekTo(0);
68 return error.NotObject;
69 }
70
71 const this_arch: std.Target.Cpu.Arch = switch (header.cputype) {
72 macho.CPU_TYPE_ARM64 => .aarch64,
73 macho.CPU_TYPE_X86_64 => .x86_64,
74 else => |value| {
75 log.err("unsupported cpu architecture 0x{x}", .{value});
76 return error.UnsupportedCpuArchitecture;
77 },
78 };
79 if (this_arch != arch) {
80 log.err("mismatched cpu architecture: found {s}, expected {s}", .{ this_arch, arch });
81 return error.MismatchedCpuArchitecture;
82 }
83
84 var self = Object{
85 .allocator = allocator,
86 .name = try allocator.dupe(u8, name),
87 .file = file,
88 .header = header,
89 };
90
91 try self.readLoadCommands(reader, .{});
92
93 if (self.symtab_cmd_index != null) {
94 try self.readSymtab();
95 try self.readStrtab();
96 }
97
98 if (self.data_in_code_cmd_index != null) try self.readDataInCode();
99
100 log.debug("\n\n", .{});
101 log.debug("{s} defines symbols", .{self.name});
102 for (self.symtab.items) |sym| {
103 const symname = self.getString(sym.n_strx);
104 log.debug("'{s}': {}", .{ symname, sym });
105 }
106
107 return self;
108}
109
110pub const ReadOffset = struct {
111 offset: ?u32 = null,
112};
113
114pub fn readLoadCommands(self: *Object, reader: anytype, offset: ReadOffset) !void {
115 const offset_mod = offset.offset orelse 0;
116 try self.load_commands.ensureCapacity(self.allocator, self.header.ncmds);
117
118 var i: u16 = 0;
119 while (i < self.header.ncmds) : (i += 1) {
120 var cmd = try LoadCommand.read(self.allocator, reader);
121 switch (cmd.cmd()) {
122 macho.LC_SEGMENT_64 => {
123 self.segment_cmd_index = i;
124 var seg = cmd.Segment;
125 for (seg.sections.items) |*sect, j| {
126 const index = @intCast(u16, j);
127 const segname = parseName(&sect.segname);
128 const sectname = parseName(&sect.sectname);
129 if (mem.eql(u8, segname, "__DWARF")) {
130 if (mem.eql(u8, sectname, "__debug_info")) {
131 self.dwarf_debug_info_index = index;
132 } else if (mem.eql(u8, sectname, "__debug_abbrev")) {
133 self.dwarf_debug_abbrev_index = index;
134 } else if (mem.eql(u8, sectname, "__debug_str")) {
135 self.dwarf_debug_str_index = index;
136 } else if (mem.eql(u8, sectname, "__debug_line")) {
137 self.dwarf_debug_line_index = index;
138 } else if (mem.eql(u8, sectname, "__debug_ranges")) {
139 self.dwarf_debug_ranges_index = index;
140 }
141 } else if (mem.eql(u8, segname, "__TEXT")) {
142 if (mem.eql(u8, sectname, "__text")) {
143 self.text_section_index = index;
144 }
145 }
146
147 sect.offset += offset_mod;
148 if (sect.reloff > 0)
149 sect.reloff += offset_mod;
150 }
151
152 seg.inner.fileoff += offset_mod;
153 },
154 macho.LC_SYMTAB => {
155 self.symtab_cmd_index = i;
156 cmd.Symtab.symoff += offset_mod;
157 cmd.Symtab.stroff += offset_mod;
158 },
159 macho.LC_DYSYMTAB => {
160 self.dysymtab_cmd_index = i;
161 },
162 macho.LC_BUILD_VERSION => {
163 self.build_version_cmd_index = i;
164 },
165 macho.LC_DATA_IN_CODE => {
166 self.data_in_code_cmd_index = i;
167 cmd.LinkeditData.dataoff += offset_mod;
168 },
169 else => {
170 log.debug("Unknown load command detected: 0x{x}.", .{cmd.cmd()});
171 },
172 }
173 self.load_commands.appendAssumeCapacity(cmd);
174 }
175}
176
177pub fn readSymtab(self: *Object) !void {
178 const symtab_cmd = self.load_commands.items[self.symtab_cmd_index.?].Symtab;
179 var buffer = try self.allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms);
180 defer self.allocator.free(buffer);
181 _ = try self.file.preadAll(buffer, symtab_cmd.symoff);
182 try self.symtab.ensureCapacity(self.allocator, symtab_cmd.nsyms);
183 // TODO this align case should not be needed.
184 // Probably a bug in stage1.
185 const slice = @alignCast(@alignOf(macho.nlist_64), mem.bytesAsSlice(macho.nlist_64, buffer));
186 self.symtab.appendSliceAssumeCapacity(slice);
187}
188
189pub fn readStrtab(self: *Object) !void {
190 const symtab_cmd = self.load_commands.items[self.symtab_cmd_index.?].Symtab;
191 var buffer = try self.allocator.alloc(u8, symtab_cmd.strsize);
192 defer self.allocator.free(buffer);
193 _ = try self.file.preadAll(buffer, symtab_cmd.stroff);
194 try self.strtab.ensureCapacity(self.allocator, symtab_cmd.strsize);
195 self.strtab.appendSliceAssumeCapacity(buffer);
196}
197
198pub fn getString(self: *const Object, str_off: u32) []const u8 {
199 assert(str_off < self.strtab.items.len);
200 return mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr + str_off));
201}
202
203pub fn readSection(self: Object, allocator: *Allocator, index: u16) ![]u8 {
204 const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;
205 const sect = seg.sections.items[index];
206 var buffer = try allocator.alloc(u8, sect.size);
207 _ = try self.file.preadAll(buffer, sect.offset);
208 return buffer;
209}
210
211pub fn readDataInCode(self: *Object) !void {
212 const index = self.data_in_code_cmd_index orelse return;
213 const data_in_code = self.load_commands.items[index].LinkeditData;
214
215 var buffer = try self.allocator.alloc(u8, data_in_code.datasize);
216 defer self.allocator.free(buffer);
217
218 _ = try self.file.preadAll(buffer, data_in_code.dataoff);
219
220 var stream = io.fixedBufferStream(buffer);
221 var reader = stream.reader();
222 while (true) {
223 const dice = reader.readStruct(macho.data_in_code_entry) catch |err| switch (err) {
224 error.EndOfStream => break,
225 else => |e| return e,
226 };
227 try self.data_in_code_entries.append(self.allocator, dice);
228 }
229}
src/link/MachO/Zld.zig created+3294
......@@ -0,0 +1,3294 @@
1const Zld = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const dwarf = std.dwarf;
6const leb = std.leb;
7const mem = std.mem;
8const meta = std.meta;
9const fs = std.fs;
10const macho = std.macho;
11const math = std.math;
12const log = std.log.scoped(.zld);
13const aarch64 = @import("../../codegen/aarch64.zig");
14
15const Allocator = mem.Allocator;
16const CodeSignature = @import("CodeSignature.zig");
17const Archive = @import("Archive.zig");
18const Object = @import("Object.zig");
19const Trie = @import("Trie.zig");
20
21usingnamespace @import("commands.zig");
22usingnamespace @import("bind.zig");
23
24allocator: *Allocator,
25
26arch: ?std.Target.Cpu.Arch = null,
27page_size: ?u16 = null,
28file: ?fs.File = null,
29out_path: ?[]const u8 = null,
30
31// TODO Eventually, we will want to keep track of the archives themselves to be able to exclude objects
32// contained within from landing in the final artifact. For now however, since we don't optimise the binary
33// at all, we just move all objects from the archives into the final artifact.
34objects: std.ArrayListUnmanaged(Object) = .{},
35
36load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
37
38pagezero_segment_cmd_index: ?u16 = null,
39text_segment_cmd_index: ?u16 = null,
40data_const_segment_cmd_index: ?u16 = null,
41data_segment_cmd_index: ?u16 = null,
42linkedit_segment_cmd_index: ?u16 = null,
43dyld_info_cmd_index: ?u16 = null,
44symtab_cmd_index: ?u16 = null,
45dysymtab_cmd_index: ?u16 = null,
46dylinker_cmd_index: ?u16 = null,
47libsystem_cmd_index: ?u16 = null,
48data_in_code_cmd_index: ?u16 = null,
49function_starts_cmd_index: ?u16 = null,
50main_cmd_index: ?u16 = null,
51version_min_cmd_index: ?u16 = null,
52source_version_cmd_index: ?u16 = null,
53uuid_cmd_index: ?u16 = null,
54code_signature_cmd_index: ?u16 = null,
55
56// __TEXT segment sections
57text_section_index: ?u16 = null,
58stubs_section_index: ?u16 = null,
59stub_helper_section_index: ?u16 = null,
60text_const_section_index: ?u16 = null,
61cstring_section_index: ?u16 = null,
62
63// __DATA_CONST segment sections
64got_section_index: ?u16 = null,
65mod_init_func_section_index: ?u16 = null,
66mod_term_func_section_index: ?u16 = null,
67data_const_section_index: ?u16 = null,
68
69// __DATA segment sections
70tlv_section_index: ?u16 = null,
71tlv_data_section_index: ?u16 = null,
72tlv_bss_section_index: ?u16 = null,
73la_symbol_ptr_section_index: ?u16 = null,
74data_section_index: ?u16 = null,
75bss_section_index: ?u16 = null,
76
77locals: std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(Symbol)) = .{},
78exports: std.StringArrayHashMapUnmanaged(macho.nlist_64) = .{},
79nonlazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},
80lazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},
81tlv_bootstrap: ?Import = null,
82threadlocal_offsets: std.ArrayListUnmanaged(u64) = .{},
83local_rebases: std.ArrayListUnmanaged(Pointer) = .{},
84nonlazy_pointers: std.StringArrayHashMapUnmanaged(GotEntry) = .{},
85
86strtab: std.ArrayListUnmanaged(u8) = .{},
87
88stub_helper_stubs_start_off: ?u64 = null,
89
90mappings: std.AutoHashMapUnmanaged(MappingKey, SectionMapping) = .{},
91unhandled_sections: std.AutoHashMapUnmanaged(MappingKey, u0) = .{},
92
93// TODO this will require scanning the relocations at least one to work out
94// the exact amount of local GOT indirections. For the time being, set some
95// default value.
96const max_local_got_indirections: u16 = 1000;
97
98const GotEntry = struct {
99 index: u32,
100 target_addr: u64,
101};
102
103const MappingKey = struct {
104 object_id: u16,
105 source_sect_id: u16,
106};
107
108const SectionMapping = struct {
109 source_sect_id: u16,
110 target_seg_id: u16,
111 target_sect_id: u16,
112 offset: u32,
113};
114
115const Symbol = struct {
116 inner: macho.nlist_64,
117 tt: Type,
118 object_id: u16,
119
120 const Type = enum {
121 Local,
122 WeakGlobal,
123 Global,
124 };
125};
126
127const DebugInfo = struct {
128 inner: dwarf.DwarfInfo,
129 debug_info: []u8,
130 debug_abbrev: []u8,
131 debug_str: []u8,
132 debug_line: []u8,
133 debug_ranges: []u8,
134
135 pub fn parseFromObject(allocator: *Allocator, object: Object) !?DebugInfo {
136 var debug_info = blk: {
137 const index = object.dwarf_debug_info_index orelse return null;
138 break :blk try object.readSection(allocator, index);
139 };
140 var debug_abbrev = blk: {
141 const index = object.dwarf_debug_abbrev_index orelse return null;
142 break :blk try object.readSection(allocator, index);
143 };
144 var debug_str = blk: {
145 const index = object.dwarf_debug_str_index orelse return null;
146 break :blk try object.readSection(allocator, index);
147 };
148 var debug_line = blk: {
149 const index = object.dwarf_debug_line_index orelse return null;
150 break :blk try object.readSection(allocator, index);
151 };
152 var debug_ranges = blk: {
153 if (object.dwarf_debug_ranges_index) |ind| {
154 break :blk try object.readSection(allocator, ind);
155 }
156 break :blk try allocator.alloc(u8, 0);
157 };
158
159 var inner: dwarf.DwarfInfo = .{
160 .endian = .Little,
161 .debug_info = debug_info,
162 .debug_abbrev = debug_abbrev,
163 .debug_str = debug_str,
164 .debug_line = debug_line,
165 .debug_ranges = debug_ranges,
166 };
167 try dwarf.openDwarfDebugInfo(&inner, allocator);
168
169 return DebugInfo{
170 .inner = inner,
171 .debug_info = debug_info,
172 .debug_abbrev = debug_abbrev,
173 .debug_str = debug_str,
174 .debug_line = debug_line,
175 .debug_ranges = debug_ranges,
176 };
177 }
178
179 pub fn deinit(self: *DebugInfo, allocator: *Allocator) void {
180 allocator.free(self.debug_info);
181 allocator.free(self.debug_abbrev);
182 allocator.free(self.debug_str);
183 allocator.free(self.debug_line);
184 allocator.free(self.debug_ranges);
185 self.inner.abbrev_table_list.deinit();
186 self.inner.compile_unit_list.deinit();
187 self.inner.func_list.deinit();
188 }
189};
190
191pub const Import = struct {
192 /// MachO symbol table entry.
193 symbol: macho.nlist_64,
194
195 /// Id of the dynamic library where the specified entries can be found.
196 dylib_ordinal: i64,
197
198 /// Index of this import within the import list.
199 index: u32,
200};
201
202/// Default path to dyld
203/// TODO instead of hardcoding it, we should probably look through some env vars and search paths
204/// instead but this will do for now.
205const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld";
206
207/// Default lib search path
208/// TODO instead of hardcoding it, we should probably look through some env vars and search paths
209/// instead but this will do for now.
210const DEFAULT_LIB_SEARCH_PATH: []const u8 = "/usr/lib";
211
212const LIB_SYSTEM_NAME: [*:0]const u8 = "System";
213/// TODO we should search for libSystem and fail if it doesn't exist, instead of hardcoding it
214const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.dylib";
215
216pub fn init(allocator: *Allocator) Zld {
217 return .{ .allocator = allocator };
218}
219
220pub fn deinit(self: *Zld) void {
221 self.threadlocal_offsets.deinit(self.allocator);
222 self.strtab.deinit(self.allocator);
223 self.local_rebases.deinit(self.allocator);
224 for (self.lazy_imports.items()) |*entry| {
225 self.allocator.free(entry.key);
226 }
227 self.lazy_imports.deinit(self.allocator);
228 for (self.nonlazy_imports.items()) |*entry| {
229 self.allocator.free(entry.key);
230 }
231 self.nonlazy_imports.deinit(self.allocator);
232 for (self.nonlazy_pointers.items()) |*entry| {
233 self.allocator.free(entry.key);
234 }
235 self.nonlazy_pointers.deinit(self.allocator);
236 for (self.exports.items()) |*entry| {
237 self.allocator.free(entry.key);
238 }
239 self.exports.deinit(self.allocator);
240 for (self.locals.items()) |*entry| {
241 self.allocator.free(entry.key);
242 entry.value.deinit(self.allocator);
243 }
244 self.locals.deinit(self.allocator);
245 for (self.objects.items) |*object| {
246 object.deinit();
247 }
248 self.objects.deinit(self.allocator);
249 for (self.load_commands.items) |*lc| {
250 lc.deinit(self.allocator);
251 }
252 self.load_commands.deinit(self.allocator);
253 self.mappings.deinit(self.allocator);
254 self.unhandled_sections.deinit(self.allocator);
255 if (self.file) |*f| f.close();
256}
257
258pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void {
259 if (files.len == 0) return error.NoInputFiles;
260 if (out_path.len == 0) return error.EmptyOutputPath;
261
262 if (self.arch == null) {
263 // Try inferring the arch from the object files.
264 self.arch = blk: {
265 const file = try fs.cwd().openFile(files[0], .{});
266 defer file.close();
267 var reader = file.reader();
268 const header = try reader.readStruct(macho.mach_header_64);
269 const arch: std.Target.Cpu.Arch = switch (header.cputype) {
270 macho.CPU_TYPE_X86_64 => .x86_64,
271 macho.CPU_TYPE_ARM64 => .aarch64,
272 else => |value| {
273 log.err("unsupported cpu architecture 0x{x}", .{value});
274 return error.UnsupportedCpuArchitecture;
275 },
276 };
277 break :blk arch;
278 };
279 }
280
281 self.page_size = switch (self.arch.?) {
282 .aarch64 => 0x4000,
283 .x86_64 => 0x1000,
284 else => unreachable,
285 };
286 self.out_path = out_path;
287 self.file = try fs.cwd().createFile(out_path, .{
288 .truncate = true,
289 .read = true,
290 .mode = if (std.Target.current.os.tag == .windows) 0 else 0o777,
291 });
292
293 try self.populateMetadata();
294 try self.parseInputFiles(files);
295 try self.sortSections();
296 try self.resolveImports();
297 try self.allocateTextSegment();
298 try self.allocateDataConstSegment();
299 try self.allocateDataSegment();
300 self.allocateLinkeditSegment();
301 try self.writeStubHelperCommon();
302 try self.resolveSymbols();
303 try self.doRelocs();
304 try self.flush();
305}
306
307fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
308 for (files) |file_name| {
309 const file = try fs.cwd().openFile(file_name, .{});
310
311 try_object: {
312 var object = Object.initFromFile(self.allocator, self.arch.?, file_name, file) catch |err| switch (err) {
313 error.NotObject => break :try_object,
314 else => |e| return e,
315 };
316 const index = @intCast(u16, self.objects.items.len);
317 try self.objects.append(self.allocator, object);
318 try self.updateMetadata(index);
319 continue;
320 }
321
322 try_archive: {
323 var archive = Archive.initFromFile(self.allocator, self.arch.?, file_name, file) catch |err| switch (err) {
324 error.NotArchive => break :try_archive,
325 else => |e| return e,
326 };
327 defer archive.deinit();
328 while (archive.objects.popOrNull()) |object| {
329 const index = @intCast(u16, self.objects.items.len);
330 try self.objects.append(self.allocator, object);
331 try self.updateMetadata(index);
332 }
333 continue;
334 }
335
336 log.err("unexpected file type: expected object '.o' or archive '.a': {s}", .{file_name});
337 return error.UnexpectedInputFileType;
338 }
339}
340
341fn mapAndUpdateSections(
342 self: *Zld,
343 object_id: u16,
344 source_sect_id: u16,
345 target_seg_id: u16,
346 target_sect_id: u16,
347) !void {
348 const object = self.objects.items[object_id];
349 const source_seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
350 const source_sect = source_seg.sections.items[source_sect_id];
351 const target_seg = &self.load_commands.items[target_seg_id].Segment;
352 const target_sect = &target_seg.sections.items[target_sect_id];
353
354 const alignment = try math.powi(u32, 2, target_sect.@"align");
355 const offset = mem.alignForwardGeneric(u64, target_sect.size, alignment);
356 const size = mem.alignForwardGeneric(u64, source_sect.size, alignment);
357 const key = MappingKey{
358 .object_id = object_id,
359 .source_sect_id = source_sect_id,
360 };
361 try self.mappings.putNoClobber(self.allocator, key, .{
362 .source_sect_id = source_sect_id,
363 .target_seg_id = target_seg_id,
364 .target_sect_id = target_sect_id,
365 .offset = @intCast(u32, offset),
366 });
367 log.debug("{s}: {s},{s} mapped to {s},{s} from 0x{x} to 0x{x}", .{
368 object.name,
369 parseName(&source_sect.segname),
370 parseName(&source_sect.sectname),
371 parseName(&target_sect.segname),
372 parseName(&target_sect.sectname),
373 offset,
374 offset + size,
375 });
376
377 target_sect.size = offset + size;
378}
379
380fn updateMetadata(self: *Zld, object_id: u16) !void {
381 const object = self.objects.items[object_id];
382 const object_seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
383 const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
384 const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
385 const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
386
387 // Create missing metadata
388 for (object_seg.sections.items) |source_sect, id| {
389 if (id == object.text_section_index.?) continue;
390 const segname = parseName(&source_sect.segname);
391 const sectname = parseName(&source_sect.sectname);
392 const flags = source_sect.flags;
393
394 switch (flags) {
395 macho.S_REGULAR, macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => {
396 if (mem.eql(u8, segname, "__TEXT")) {
397 if (self.text_const_section_index != null) continue;
398
399 self.text_const_section_index = @intCast(u16, text_seg.sections.items.len);
400 try text_seg.addSection(self.allocator, .{
401 .sectname = makeStaticString("__const"),
402 .segname = makeStaticString("__TEXT"),
403 .addr = 0,
404 .size = 0,
405 .offset = 0,
406 .@"align" = 0,
407 .reloff = 0,
408 .nreloc = 0,
409 .flags = macho.S_REGULAR,
410 .reserved1 = 0,
411 .reserved2 = 0,
412 .reserved3 = 0,
413 });
414 } else if (mem.eql(u8, segname, "__DATA")) {
415 if (!mem.eql(u8, sectname, "__const")) continue;
416 if (self.data_const_section_index != null) continue;
417
418 self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len);
419 try data_const_seg.addSection(self.allocator, .{
420 .sectname = makeStaticString("__const"),
421 .segname = makeStaticString("__DATA_CONST"),
422 .addr = 0,
423 .size = 0,
424 .offset = 0,
425 .@"align" = 0,
426 .reloff = 0,
427 .nreloc = 0,
428 .flags = macho.S_REGULAR,
429 .reserved1 = 0,
430 .reserved2 = 0,
431 .reserved3 = 0,
432 });
433 }
434 },
435 macho.S_CSTRING_LITERALS => {
436 if (!mem.eql(u8, segname, "__TEXT")) continue;
437 if (self.cstring_section_index != null) continue;
438
439 self.cstring_section_index = @intCast(u16, text_seg.sections.items.len);
440 try text_seg.addSection(self.allocator, .{
441 .sectname = makeStaticString("__cstring"),
442 .segname = makeStaticString("__TEXT"),
443 .addr = 0,
444 .size = 0,
445 .offset = 0,
446 .@"align" = 0,
447 .reloff = 0,
448 .nreloc = 0,
449 .flags = macho.S_CSTRING_LITERALS,
450 .reserved1 = 0,
451 .reserved2 = 0,
452 .reserved3 = 0,
453 });
454 },
455 macho.S_MOD_INIT_FUNC_POINTERS => {
456 if (!mem.eql(u8, segname, "__DATA")) continue;
457 if (self.mod_init_func_section_index != null) continue;
458
459 self.mod_init_func_section_index = @intCast(u16, data_const_seg.sections.items.len);
460 try data_const_seg.addSection(self.allocator, .{
461 .sectname = makeStaticString("__mod_init_func"),
462 .segname = makeStaticString("__DATA_CONST"),
463 .addr = 0,
464 .size = 0,
465 .offset = 0,
466 .@"align" = 0,
467 .reloff = 0,
468 .nreloc = 0,
469 .flags = macho.S_MOD_INIT_FUNC_POINTERS,
470 .reserved1 = 0,
471 .reserved2 = 0,
472 .reserved3 = 0,
473 });
474 },
475 macho.S_MOD_TERM_FUNC_POINTERS => {
476 if (!mem.eql(u8, segname, "__DATA")) continue;
477 if (self.mod_term_func_section_index != null) continue;
478
479 self.mod_term_func_section_index = @intCast(u16, data_const_seg.sections.items.len);
480 try data_const_seg.addSection(self.allocator, .{
481 .sectname = makeStaticString("__mod_term_func"),
482 .segname = makeStaticString("__DATA_CONST"),
483 .addr = 0,
484 .size = 0,
485 .offset = 0,
486 .@"align" = 0,
487 .reloff = 0,
488 .nreloc = 0,
489 .flags = macho.S_MOD_TERM_FUNC_POINTERS,
490 .reserved1 = 0,
491 .reserved2 = 0,
492 .reserved3 = 0,
493 });
494 },
495 macho.S_ZEROFILL => {
496 if (!mem.eql(u8, segname, "__DATA")) continue;
497 if (self.bss_section_index != null) continue;
498
499 self.bss_section_index = @intCast(u16, data_seg.sections.items.len);
500 try data_seg.addSection(self.allocator, .{
501 .sectname = makeStaticString("__bss"),
502 .segname = makeStaticString("__DATA"),
503 .addr = 0,
504 .size = 0,
505 .offset = 0,
506 .@"align" = 0,
507 .reloff = 0,
508 .nreloc = 0,
509 .flags = macho.S_ZEROFILL,
510 .reserved1 = 0,
511 .reserved2 = 0,
512 .reserved3 = 0,
513 });
514 },
515 macho.S_THREAD_LOCAL_VARIABLES => {
516 if (!mem.eql(u8, segname, "__DATA")) continue;
517 if (self.tlv_section_index != null) continue;
518
519 self.tlv_section_index = @intCast(u16, data_seg.sections.items.len);
520 try data_seg.addSection(self.allocator, .{
521 .sectname = makeStaticString("__thread_vars"),
522 .segname = makeStaticString("__DATA"),
523 .addr = 0,
524 .size = 0,
525 .offset = 0,
526 .@"align" = 0,
527 .reloff = 0,
528 .nreloc = 0,
529 .flags = macho.S_THREAD_LOCAL_VARIABLES,
530 .reserved1 = 0,
531 .reserved2 = 0,
532 .reserved3 = 0,
533 });
534 },
535 macho.S_THREAD_LOCAL_REGULAR => {
536 if (!mem.eql(u8, segname, "__DATA")) continue;
537 if (self.tlv_data_section_index != null) continue;
538
539 self.tlv_data_section_index = @intCast(u16, data_seg.sections.items.len);
540 try data_seg.addSection(self.allocator, .{
541 .sectname = makeStaticString("__thread_data"),
542 .segname = makeStaticString("__DATA"),
543 .addr = 0,
544 .size = 0,
545 .offset = 0,
546 .@"align" = 0,
547 .reloff = 0,
548 .nreloc = 0,
549 .flags = macho.S_THREAD_LOCAL_REGULAR,
550 .reserved1 = 0,
551 .reserved2 = 0,
552 .reserved3 = 0,
553 });
554 },
555 macho.S_THREAD_LOCAL_ZEROFILL => {
556 if (!mem.eql(u8, segname, "__DATA")) continue;
557 if (self.tlv_bss_section_index != null) continue;
558
559 self.tlv_bss_section_index = @intCast(u16, data_seg.sections.items.len);
560 try data_seg.addSection(self.allocator, .{
561 .sectname = makeStaticString("__thread_bss"),
562 .segname = makeStaticString("__DATA"),
563 .addr = 0,
564 .size = 0,
565 .offset = 0,
566 .@"align" = 0,
567 .reloff = 0,
568 .nreloc = 0,
569 .flags = macho.S_THREAD_LOCAL_ZEROFILL,
570 .reserved1 = 0,
571 .reserved2 = 0,
572 .reserved3 = 0,
573 });
574 },
575 else => {
576 log.debug("unhandled section type 0x{x} for '{s}/{s}'", .{ flags, segname, sectname });
577 },
578 }
579 }
580
581 // Find ideal section alignment.
582 for (object_seg.sections.items) |source_sect, id| {
583 if (self.getMatchingSection(source_sect)) |res| {
584 const target_seg = &self.load_commands.items[res.seg].Segment;
585 const target_sect = &target_seg.sections.items[res.sect];
586 target_sect.@"align" = math.max(target_sect.@"align", source_sect.@"align");
587 }
588 }
589
590 // Update section mappings
591 for (object_seg.sections.items) |source_sect, id| {
592 const source_sect_id = @intCast(u16, id);
593 if (self.getMatchingSection(source_sect)) |res| {
594 try self.mapAndUpdateSections(object_id, source_sect_id, res.seg, res.sect);
595 continue;
596 }
597
598 const segname = parseName(&source_sect.segname);
599 const sectname = parseName(&source_sect.sectname);
600 log.debug("section '{s}/{s}' will be unmapped", .{ segname, sectname });
601 try self.unhandled_sections.putNoClobber(self.allocator, .{
602 .object_id = object_id,
603 .source_sect_id = source_sect_id,
604 }, 0);
605 }
606}
607
608const MatchingSection = struct {
609 seg: u16,
610 sect: u16,
611};
612
613fn getMatchingSection(self: *Zld, section: macho.section_64) ?MatchingSection {
614 const segname = parseName(&section.segname);
615 const sectname = parseName(&section.sectname);
616 const res: ?MatchingSection = blk: {
617 switch (section.flags) {
618 macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => {
619 break :blk .{
620 .seg = self.text_segment_cmd_index.?,
621 .sect = self.text_const_section_index.?,
622 };
623 },
624 macho.S_CSTRING_LITERALS => {
625 break :blk .{
626 .seg = self.text_segment_cmd_index.?,
627 .sect = self.cstring_section_index.?,
628 };
629 },
630 macho.S_MOD_INIT_FUNC_POINTERS => {
631 break :blk .{
632 .seg = self.data_const_segment_cmd_index.?,
633 .sect = self.mod_init_func_section_index.?,
634 };
635 },
636 macho.S_MOD_TERM_FUNC_POINTERS => {
637 break :blk .{
638 .seg = self.data_const_segment_cmd_index.?,
639 .sect = self.mod_term_func_section_index.?,
640 };
641 },
642 macho.S_ZEROFILL => {
643 break :blk .{
644 .seg = self.data_segment_cmd_index.?,
645 .sect = self.bss_section_index.?,
646 };
647 },
648 macho.S_THREAD_LOCAL_VARIABLES => {
649 break :blk .{
650 .seg = self.data_segment_cmd_index.?,
651 .sect = self.tlv_section_index.?,
652 };
653 },
654 macho.S_THREAD_LOCAL_REGULAR => {
655 break :blk .{
656 .seg = self.data_segment_cmd_index.?,
657 .sect = self.tlv_data_section_index.?,
658 };
659 },
660 macho.S_THREAD_LOCAL_ZEROFILL => {
661 break :blk .{
662 .seg = self.data_segment_cmd_index.?,
663 .sect = self.tlv_bss_section_index.?,
664 };
665 },
666 macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS => {
667 break :blk .{
668 .seg = self.text_segment_cmd_index.?,
669 .sect = self.text_section_index.?,
670 };
671 },
672 macho.S_REGULAR => {
673 if (mem.eql(u8, segname, "__TEXT")) {
674 break :blk .{
675 .seg = self.text_segment_cmd_index.?,
676 .sect = self.text_const_section_index.?,
677 };
678 } else if (mem.eql(u8, segname, "__DATA")) {
679 if (mem.eql(u8, sectname, "__data")) {
680 break :blk .{
681 .seg = self.data_segment_cmd_index.?,
682 .sect = self.data_section_index.?,
683 };
684 } else if (mem.eql(u8, sectname, "__const")) {
685 break :blk .{
686 .seg = self.data_const_segment_cmd_index.?,
687 .sect = self.data_const_section_index.?,
688 };
689 }
690 }
691 break :blk null;
692 },
693 else => {
694 break :blk null;
695 },
696 }
697 };
698 return res;
699}
700
701fn sortSections(self: *Zld) !void {
702 var text_index_mapping = std.AutoHashMap(u16, u16).init(self.allocator);
703 defer text_index_mapping.deinit();
704 var data_const_index_mapping = std.AutoHashMap(u16, u16).init(self.allocator);
705 defer data_const_index_mapping.deinit();
706 var data_index_mapping = std.AutoHashMap(u16, u16).init(self.allocator);
707 defer data_index_mapping.deinit();
708
709 {
710 // __TEXT segment
711 const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
712 var sections = seg.sections.toOwnedSlice(self.allocator);
713 defer self.allocator.free(sections);
714 try seg.sections.ensureCapacity(self.allocator, sections.len);
715
716 const indices = &[_]*?u16{
717 &self.text_section_index,
718 &self.stubs_section_index,
719 &self.stub_helper_section_index,
720 &self.text_const_section_index,
721 &self.cstring_section_index,
722 };
723 for (indices) |maybe_index| {
724 const new_index: u16 = if (maybe_index.*) |index| blk: {
725 const idx = @intCast(u16, seg.sections.items.len);
726 seg.sections.appendAssumeCapacity(sections[index]);
727 try text_index_mapping.putNoClobber(index, idx);
728 break :blk idx;
729 } else continue;
730 maybe_index.* = new_index;
731 }
732 }
733
734 {
735 // __DATA_CONST segment
736 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
737 var sections = seg.sections.toOwnedSlice(self.allocator);
738 defer self.allocator.free(sections);
739 try seg.sections.ensureCapacity(self.allocator, sections.len);
740
741 const indices = &[_]*?u16{
742 &self.got_section_index,
743 &self.mod_init_func_section_index,
744 &self.mod_term_func_section_index,
745 &self.data_const_section_index,
746 };
747 for (indices) |maybe_index| {
748 const new_index: u16 = if (maybe_index.*) |index| blk: {
749 const idx = @intCast(u16, seg.sections.items.len);
750 seg.sections.appendAssumeCapacity(sections[index]);
751 try data_const_index_mapping.putNoClobber(index, idx);
752 break :blk idx;
753 } else continue;
754 maybe_index.* = new_index;
755 }
756 }
757
758 {
759 // __DATA segment
760 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
761 var sections = seg.sections.toOwnedSlice(self.allocator);
762 defer self.allocator.free(sections);
763 try seg.sections.ensureCapacity(self.allocator, sections.len);
764
765 // __DATA segment
766 const indices = &[_]*?u16{
767 &self.la_symbol_ptr_section_index,
768 &self.tlv_section_index,
769 &self.data_section_index,
770 &self.tlv_data_section_index,
771 &self.tlv_bss_section_index,
772 &self.bss_section_index,
773 };
774 for (indices) |maybe_index| {
775 const new_index: u16 = if (maybe_index.*) |index| blk: {
776 const idx = @intCast(u16, seg.sections.items.len);
777 seg.sections.appendAssumeCapacity(sections[index]);
778 try data_index_mapping.putNoClobber(index, idx);
779 break :blk idx;
780 } else continue;
781 maybe_index.* = new_index;
782 }
783 }
784
785 var it = self.mappings.iterator();
786 while (it.next()) |entry| {
787 const mapping = &entry.value;
788 if (self.text_segment_cmd_index.? == mapping.target_seg_id) {
789 const new_index = text_index_mapping.get(mapping.target_sect_id) orelse unreachable;
790 mapping.target_sect_id = new_index;
791 } else if (self.data_const_segment_cmd_index.? == mapping.target_seg_id) {
792 const new_index = data_const_index_mapping.get(mapping.target_sect_id) orelse unreachable;
793 mapping.target_sect_id = new_index;
794 } else if (self.data_segment_cmd_index.? == mapping.target_seg_id) {
795 const new_index = data_index_mapping.get(mapping.target_sect_id) orelse unreachable;
796 mapping.target_sect_id = new_index;
797 } else unreachable;
798 }
799}
800
801fn resolveImports(self: *Zld) !void {
802 var imports = std.StringArrayHashMap(bool).init(self.allocator);
803 defer imports.deinit();
804
805 for (self.objects.items) |object| {
806 for (object.symtab.items) |sym| {
807 if (isLocal(&sym)) continue;
808
809 const name = object.getString(sym.n_strx);
810 const res = try imports.getOrPut(name);
811 if (isExport(&sym)) {
812 res.entry.value = false;
813 continue;
814 }
815 if (res.found_existing and !res.entry.value)
816 continue;
817 res.entry.value = true;
818 }
819 }
820
821 for (imports.items()) |entry| {
822 if (!entry.value) continue;
823
824 const sym_name = entry.key;
825 const n_strx = try self.makeString(sym_name);
826 var new_sym: macho.nlist_64 = .{
827 .n_strx = n_strx,
828 .n_type = macho.N_UNDF | macho.N_EXT,
829 .n_value = 0,
830 .n_desc = macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | macho.N_SYMBOL_RESOLVER,
831 .n_sect = 0,
832 };
833 var key = try self.allocator.dupe(u8, sym_name);
834 // TODO handle symbol resolution from non-libc dylibs.
835 const dylib_ordinal = 1;
836
837 // TODO need to rework this. Perhaps should create a set of all possible libc
838 // symbols which are expected to be nonlazy?
839 if (mem.eql(u8, sym_name, "___stdoutp") or
840 mem.eql(u8, sym_name, "___stderrp") or
841 mem.eql(u8, sym_name, "___stdinp") or
842 mem.eql(u8, sym_name, "___stack_chk_guard") or
843 mem.eql(u8, sym_name, "_environ") or
844 mem.eql(u8, sym_name, "__DefaultRuneLocale") or
845 mem.eql(u8, sym_name, "_mach_task_self_"))
846 {
847 log.debug("writing nonlazy symbol '{s}'", .{sym_name});
848 const index = @intCast(u32, self.nonlazy_imports.items().len);
849 try self.nonlazy_imports.putNoClobber(self.allocator, key, .{
850 .symbol = new_sym,
851 .dylib_ordinal = dylib_ordinal,
852 .index = index,
853 });
854 } else if (mem.eql(u8, sym_name, "__tlv_bootstrap")) {
855 log.debug("writing threadlocal symbol '{s}'", .{sym_name});
856 self.tlv_bootstrap = .{
857 .symbol = new_sym,
858 .dylib_ordinal = dylib_ordinal,
859 .index = 0,
860 };
861 } else {
862 log.debug("writing lazy symbol '{s}'", .{sym_name});
863 const index = @intCast(u32, self.lazy_imports.items().len);
864 try self.lazy_imports.putNoClobber(self.allocator, key, .{
865 .symbol = new_sym,
866 .dylib_ordinal = dylib_ordinal,
867 .index = index,
868 });
869 }
870 }
871
872 const n_strx = try self.makeString("dyld_stub_binder");
873 const name = try self.allocator.dupe(u8, "dyld_stub_binder");
874 log.debug("writing nonlazy symbol 'dyld_stub_binder'", .{});
875 const index = @intCast(u32, self.nonlazy_imports.items().len);
876 try self.nonlazy_imports.putNoClobber(self.allocator, name, .{
877 .symbol = .{
878 .n_strx = n_strx,
879 .n_type = std.macho.N_UNDF | std.macho.N_EXT,
880 .n_sect = 0,
881 .n_desc = std.macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | std.macho.N_SYMBOL_RESOLVER,
882 .n_value = 0,
883 },
884 .dylib_ordinal = 1,
885 .index = index,
886 });
887}
888
889fn allocateTextSegment(self: *Zld) !void {
890 const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
891 const nexterns = @intCast(u32, self.lazy_imports.items().len);
892
893 const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].Segment.inner.vmsize;
894 seg.inner.fileoff = 0;
895 seg.inner.vmaddr = base_vmaddr;
896
897 // Set stubs and stub_helper sizes
898 const stubs = &seg.sections.items[self.stubs_section_index.?];
899 const stub_helper = &seg.sections.items[self.stub_helper_section_index.?];
900 stubs.size += nexterns * stubs.reserved2;
901
902 const stub_size: u4 = switch (self.arch.?) {
903 .x86_64 => 10,
904 .aarch64 => 3 * @sizeOf(u32),
905 else => unreachable,
906 };
907 stub_helper.size += nexterns * stub_size;
908
909 var sizeofcmds: u64 = 0;
910 for (self.load_commands.items) |lc| {
911 sizeofcmds += lc.cmdsize();
912 }
913
914 try self.allocateSegment(self.text_segment_cmd_index.?, @sizeOf(macho.mach_header_64) + sizeofcmds);
915
916 // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments.
917 var min_alignment: u32 = 0;
918 for (seg.sections.items) |sect| {
919 const alignment = try math.powi(u32, 2, sect.@"align");
920 min_alignment = math.max(min_alignment, alignment);
921 }
922
923 assert(min_alignment > 0);
924 const last_sect_idx = seg.sections.items.len - 1;
925 const last_sect = seg.sections.items[last_sect_idx];
926 const shift: u32 = blk: {
927 const diff = seg.inner.filesize - last_sect.offset - last_sect.size;
928 const factor = @divTrunc(diff, min_alignment);
929 break :blk @intCast(u32, factor * min_alignment);
930 };
931
932 if (shift > 0) {
933 for (seg.sections.items) |*sect| {
934 sect.offset += shift;
935 sect.addr += shift;
936 }
937 }
938}
939
940fn allocateDataConstSegment(self: *Zld) !void {
941 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
942 const nonlazy = @intCast(u32, self.nonlazy_imports.items().len);
943
944 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
945 seg.inner.fileoff = text_seg.inner.fileoff + text_seg.inner.filesize;
946 seg.inner.vmaddr = text_seg.inner.vmaddr + text_seg.inner.vmsize;
947
948 // Set got size
949 const got = &seg.sections.items[self.got_section_index.?];
950 // TODO this will require scanning the relocations at least one to work out
951 // the exact amount of local GOT indirections. For the time being, set some
952 // default value.
953 got.size += (max_local_got_indirections + nonlazy) * @sizeOf(u64);
954
955 try self.allocateSegment(self.data_const_segment_cmd_index.?, 0);
956}
957
958fn allocateDataSegment(self: *Zld) !void {
959 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
960 const lazy = @intCast(u32, self.lazy_imports.items().len);
961
962 const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
963 seg.inner.fileoff = data_const_seg.inner.fileoff + data_const_seg.inner.filesize;
964 seg.inner.vmaddr = data_const_seg.inner.vmaddr + data_const_seg.inner.vmsize;
965
966 // Set la_symbol_ptr and data size
967 const la_symbol_ptr = &seg.sections.items[self.la_symbol_ptr_section_index.?];
968 const data = &seg.sections.items[self.data_section_index.?];
969 la_symbol_ptr.size += lazy * @sizeOf(u64);
970 data.size += @sizeOf(u64); // TODO when do we need more?
971
972 try self.allocateSegment(self.data_segment_cmd_index.?, 0);
973}
974
975fn allocateLinkeditSegment(self: *Zld) void {
976 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
977 const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
978 seg.inner.fileoff = data_seg.inner.fileoff + data_seg.inner.filesize;
979 seg.inner.vmaddr = data_seg.inner.vmaddr + data_seg.inner.vmsize;
980}
981
982fn allocateSegment(self: *Zld, index: u16, offset: u64) !void {
983 const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].Segment.inner.vmsize;
984 const seg = &self.load_commands.items[index].Segment;
985
986 // Allocate the sections according to their alignment at the beginning of the segment.
987 var start: u64 = offset;
988 for (seg.sections.items) |*sect| {
989 const alignment = try math.powi(u32, 2, sect.@"align");
990 const start_aligned = mem.alignForwardGeneric(u64, start, alignment);
991 const end_aligned = mem.alignForwardGeneric(u64, start_aligned + sect.size, alignment);
992 sect.offset = @intCast(u32, seg.inner.fileoff + start_aligned);
993 sect.addr = seg.inner.vmaddr + start_aligned;
994 start = end_aligned;
995 }
996
997 const seg_size_aligned = mem.alignForwardGeneric(u64, start, self.page_size.?);
998 seg.inner.filesize = seg_size_aligned;
999 seg.inner.vmsize = seg_size_aligned;
1000}
1001
1002fn writeStubHelperCommon(self: *Zld) !void {
1003 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1004 const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?];
1005 const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
1006 const got = &data_const_segment.sections.items[self.got_section_index.?];
1007 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
1008 const data = &data_segment.sections.items[self.data_section_index.?];
1009 const la_symbol_ptr = data_segment.sections.items[self.la_symbol_ptr_section_index.?];
1010
1011 self.stub_helper_stubs_start_off = blk: {
1012 switch (self.arch.?) {
1013 .x86_64 => {
1014 const code_size = 15;
1015 var code: [code_size]u8 = undefined;
1016 // lea %r11, [rip + disp]
1017 code[0] = 0x4c;
1018 code[1] = 0x8d;
1019 code[2] = 0x1d;
1020 {
1021 const target_addr = data.addr + data.size - @sizeOf(u64);
1022 const displacement = try math.cast(u32, target_addr - stub_helper.addr - 7);
1023 mem.writeIntLittle(u32, code[3..7], displacement);
1024 }
1025 // push %r11
1026 code[7] = 0x41;
1027 code[8] = 0x53;
1028 // jmp [rip + disp]
1029 code[9] = 0xff;
1030 code[10] = 0x25;
1031 {
1032 const dyld_stub_binder = self.nonlazy_imports.get("dyld_stub_binder").?;
1033 const addr = (got.addr + dyld_stub_binder.index * @sizeOf(u64));
1034 const displacement = try math.cast(u32, addr - stub_helper.addr - code_size);
1035 mem.writeIntLittle(u32, code[11..], displacement);
1036 }
1037 try self.file.?.pwriteAll(&code, stub_helper.offset);
1038 break :blk stub_helper.offset + code_size;
1039 },
1040 .aarch64 => {
1041 var code: [6 * @sizeOf(u32)]u8 = undefined;
1042 data_blk_outer: {
1043 const this_addr = stub_helper.addr;
1044 const target_addr = data.addr + data.size - @sizeOf(u64);
1045 data_blk: {
1046 const displacement = math.cast(i21, target_addr - this_addr) catch |_| break :data_blk;
1047 // adr x17, disp
1048 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, displacement).toU32());
1049 // nop
1050 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32());
1051 break :data_blk_outer;
1052 }
1053 data_blk: {
1054 const new_this_addr = this_addr + @sizeOf(u32);
1055 const displacement = math.cast(i21, target_addr - new_this_addr) catch |_| break :data_blk;
1056 // nop
1057 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32());
1058 // adr x17, disp
1059 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.adr(.x17, displacement).toU32());
1060 break :data_blk_outer;
1061 }
1062 // Jump is too big, replace adr with adrp and add.
1063 const this_page = @intCast(i32, this_addr >> 12);
1064 const target_page = @intCast(i32, target_addr >> 12);
1065 const pages = @intCast(i21, target_page - this_page);
1066 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x17, pages).toU32());
1067 const narrowed = @truncate(u12, target_addr);
1068 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.add(.x17, .x17, narrowed, false).toU32());
1069 }
1070 // stp x16, x17, [sp, #-16]!
1071 code[8] = 0xf0;
1072 code[9] = 0x47;
1073 code[10] = 0xbf;
1074 code[11] = 0xa9;
1075 binder_blk_outer: {
1076 const dyld_stub_binder = self.nonlazy_imports.get("dyld_stub_binder").?;
1077 const this_addr = stub_helper.addr + 3 * @sizeOf(u32);
1078 const target_addr = (got.addr + dyld_stub_binder.index * @sizeOf(u64));
1079 binder_blk: {
1080 const displacement = math.divExact(u64, target_addr - this_addr, 4) catch |_| break :binder_blk;
1081 const literal = math.cast(u18, displacement) catch |_| break :binder_blk;
1082 // ldr x16, label
1083 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.ldr(.x16, .{
1084 .literal = literal,
1085 }).toU32());
1086 // nop
1087 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.nop().toU32());
1088 break :binder_blk_outer;
1089 }
1090 binder_blk: {
1091 const new_this_addr = this_addr + @sizeOf(u32);
1092 const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch |_| break :binder_blk;
1093 const literal = math.cast(u18, displacement) catch |_| break :binder_blk;
1094 log.debug("2: disp=0x{x}, literal=0x{x}", .{ displacement, literal });
1095 // Pad with nop to please division.
1096 // nop
1097 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.nop().toU32());
1098 // ldr x16, label
1099 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{
1100 .literal = literal,
1101 }).toU32());
1102 break :binder_blk_outer;
1103 }
1104 // Use adrp followed by ldr(immediate).
1105 const this_page = @intCast(i32, this_addr >> 12);
1106 const target_page = @intCast(i32, target_addr >> 12);
1107 const pages = @intCast(i21, target_page - this_page);
1108 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.adrp(.x16, pages).toU32());
1109 const narrowed = @truncate(u12, target_addr);
1110 const offset = try math.divExact(u12, narrowed, 8);
1111 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{
1112 .register = .{
1113 .rn = .x16,
1114 .offset = aarch64.Instruction.LoadStoreOffset.imm(offset),
1115 },
1116 }).toU32());
1117 }
1118 // br x16
1119 code[20] = 0x00;
1120 code[21] = 0x02;
1121 code[22] = 0x1f;
1122 code[23] = 0xd6;
1123 try self.file.?.pwriteAll(&code, stub_helper.offset);
1124 break :blk stub_helper.offset + 6 * @sizeOf(u32);
1125 },
1126 else => unreachable,
1127 }
1128 };
1129
1130 for (self.lazy_imports.items()) |_, i| {
1131 const index = @intCast(u32, i);
1132 try self.writeLazySymbolPointer(index);
1133 try self.writeStub(index);
1134 try self.writeStubInStubHelper(index);
1135 }
1136}
1137
1138fn writeLazySymbolPointer(self: *Zld, index: u32) !void {
1139 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1140 const stub_helper = text_segment.sections.items[self.stub_helper_section_index.?];
1141 const data_segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
1142 const la_symbol_ptr = data_segment.sections.items[self.la_symbol_ptr_section_index.?];
1143
1144 const stub_size: u4 = switch (self.arch.?) {
1145 .x86_64 => 10,
1146 .aarch64 => 3 * @sizeOf(u32),
1147 else => unreachable,
1148 };
1149 const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size;
1150 const end = stub_helper.addr + stub_off - stub_helper.offset;
1151 var buf: [@sizeOf(u64)]u8 = undefined;
1152 mem.writeIntLittle(u64, &buf, end);
1153 const off = la_symbol_ptr.offset + index * @sizeOf(u64);
1154 log.debug("writing lazy symbol pointer entry 0x{x} at 0x{x}", .{ end, off });
1155 try self.file.?.pwriteAll(&buf, off);
1156}
1157
1158fn writeStub(self: *Zld, index: u32) !void {
1159 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1160 const stubs = text_segment.sections.items[self.stubs_section_index.?];
1161 const data_segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
1162 const la_symbol_ptr = data_segment.sections.items[self.la_symbol_ptr_section_index.?];
1163
1164 const stub_off = stubs.offset + index * stubs.reserved2;
1165 const stub_addr = stubs.addr + index * stubs.reserved2;
1166 const la_ptr_addr = la_symbol_ptr.addr + index * @sizeOf(u64);
1167 log.debug("writing stub at 0x{x}", .{stub_off});
1168 var code = try self.allocator.alloc(u8, stubs.reserved2);
1169 defer self.allocator.free(code);
1170 switch (self.arch.?) {
1171 .x86_64 => {
1172 assert(la_ptr_addr >= stub_addr + stubs.reserved2);
1173 const displacement = try math.cast(u32, la_ptr_addr - stub_addr - stubs.reserved2);
1174 // jmp
1175 code[0] = 0xff;
1176 code[1] = 0x25;
1177 mem.writeIntLittle(u32, code[2..][0..4], displacement);
1178 },
1179 .aarch64 => {
1180 assert(la_ptr_addr >= stub_addr);
1181 outer: {
1182 const this_addr = stub_addr;
1183 const target_addr = la_ptr_addr;
1184 inner: {
1185 const displacement = math.divExact(u64, target_addr - this_addr, 4) catch |_| break :inner;
1186 const literal = math.cast(u18, displacement) catch |_| break :inner;
1187 // ldr x16, literal
1188 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{
1189 .literal = literal,
1190 }).toU32());
1191 // nop
1192 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32());
1193 break :outer;
1194 }
1195 inner: {
1196 const new_this_addr = this_addr + @sizeOf(u32);
1197 const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch |_| break :inner;
1198 const literal = math.cast(u18, displacement) catch |_| break :inner;
1199 // nop
1200 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32());
1201 // ldr x16, literal
1202 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(.x16, .{
1203 .literal = literal,
1204 }).toU32());
1205 break :outer;
1206 }
1207 // Use adrp followed by ldr(immediate).
1208 const this_page = @intCast(i32, this_addr >> 12);
1209 const target_page = @intCast(i32, target_addr >> 12);
1210 const pages = @intCast(i21, target_page - this_page);
1211 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x16, pages).toU32());
1212 const narrowed = @truncate(u12, target_addr);
1213 const offset = try math.divExact(u12, narrowed, 8);
1214 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(.x16, .{
1215 .register = .{
1216 .rn = .x16,
1217 .offset = aarch64.Instruction.LoadStoreOffset.imm(offset),
1218 },
1219 }).toU32());
1220 }
1221 // br x16
1222 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.br(.x16).toU32());
1223 },
1224 else => unreachable,
1225 }
1226 try self.file.?.pwriteAll(code, stub_off);
1227}
1228
1229fn writeStubInStubHelper(self: *Zld, index: u32) !void {
1230 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1231 const stub_helper = text_segment.sections.items[self.stub_helper_section_index.?];
1232
1233 const stub_size: u4 = switch (self.arch.?) {
1234 .x86_64 => 10,
1235 .aarch64 => 3 * @sizeOf(u32),
1236 else => unreachable,
1237 };
1238 const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size;
1239 var code = try self.allocator.alloc(u8, stub_size);
1240 defer self.allocator.free(code);
1241 switch (self.arch.?) {
1242 .x86_64 => {
1243 const displacement = try math.cast(
1244 i32,
1245 @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - stub_size,
1246 );
1247 // pushq
1248 code[0] = 0x68;
1249 mem.writeIntLittle(u32, code[1..][0..4], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
1250 // jmpq
1251 code[5] = 0xe9;
1252 mem.writeIntLittle(u32, code[6..][0..4], @bitCast(u32, displacement));
1253 },
1254 .aarch64 => {
1255 const displacement = try math.cast(i28, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4);
1256 const literal = @divExact(stub_size - @sizeOf(u32), 4);
1257 // ldr w16, literal
1258 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{
1259 .literal = literal,
1260 }).toU32());
1261 // b disp
1262 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(displacement).toU32());
1263 mem.writeIntLittle(u32, code[8..12], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
1264 },
1265 else => unreachable,
1266 }
1267 try self.file.?.pwriteAll(code, stub_off);
1268}
1269
1270fn resolveSymbols(self: *Zld) !void {
1271 for (self.objects.items) |object, object_id| {
1272 const seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
1273 log.debug("\n\n", .{});
1274 log.debug("resolving symbols in {s}", .{object.name});
1275
1276 for (object.symtab.items) |sym| {
1277 if (isImport(&sym)) continue;
1278
1279 const sym_name = object.getString(sym.n_strx);
1280 const out_name = try self.allocator.dupe(u8, sym_name);
1281 const locs = try self.locals.getOrPut(self.allocator, out_name);
1282 defer {
1283 if (locs.found_existing) self.allocator.free(out_name);
1284 }
1285
1286 if (!locs.found_existing) {
1287 locs.entry.value = .{};
1288 }
1289
1290 const tt: Symbol.Type = blk: {
1291 if (isLocal(&sym)) {
1292 break :blk .Local;
1293 } else if (isWeakDef(&sym)) {
1294 break :blk .WeakGlobal;
1295 } else {
1296 break :blk .Global;
1297 }
1298 };
1299 if (tt == .Global) {
1300 for (locs.entry.value.items) |ss| {
1301 if (ss.tt == .Global) {
1302 log.debug("symbol already defined '{s}'", .{sym_name});
1303 continue;
1304 // log.err("symbol '{s}' defined multiple times: {}", .{ sym_name, sym });
1305 // return error.MultipleSymbolDefinitions;
1306 }
1307 }
1308 }
1309
1310 const source_sect_id = sym.n_sect - 1;
1311 const target_mapping = self.mappings.get(.{
1312 .object_id = @intCast(u16, object_id),
1313 .source_sect_id = source_sect_id,
1314 }) orelse {
1315 if (self.unhandled_sections.get(.{
1316 .object_id = @intCast(u16, object_id),
1317 .source_sect_id = source_sect_id,
1318 }) != null) continue;
1319
1320 log.err("section not mapped for symbol '{s}': {}", .{ sym_name, sym });
1321 return error.SectionNotMappedForSymbol;
1322 };
1323 const source_sect = seg.sections.items[source_sect_id];
1324 const target_seg = self.load_commands.items[target_mapping.target_seg_id].Segment;
1325 const target_sect = target_seg.sections.items[target_mapping.target_sect_id];
1326 const target_addr = target_sect.addr + target_mapping.offset;
1327 const n_value = sym.n_value - source_sect.addr + target_addr;
1328
1329 log.debug("resolving '{s}':{} as {s} symbol at 0x{x}", .{ sym_name, sym, tt, n_value });
1330
1331 // TODO there might be a more generic way of doing this.
1332 var n_sect: u16 = 0;
1333 for (self.load_commands.items) |cmd, cmd_id| {
1334 if (cmd != .Segment) break;
1335 if (cmd_id == target_mapping.target_seg_id) {
1336 n_sect += target_mapping.target_sect_id + 1;
1337 break;
1338 }
1339 n_sect += @intCast(u16, cmd.Segment.sections.items.len);
1340 }
1341
1342 const n_strx = try self.makeString(sym_name);
1343 try locs.entry.value.append(self.allocator, .{
1344 .inner = .{
1345 .n_strx = n_strx,
1346 .n_value = n_value,
1347 .n_type = macho.N_SECT,
1348 .n_desc = sym.n_desc,
1349 .n_sect = @intCast(u8, n_sect),
1350 },
1351 .tt = tt,
1352 .object_id = @intCast(u16, object_id),
1353 });
1354 }
1355 }
1356}
1357
1358fn doRelocs(self: *Zld) !void {
1359 for (self.objects.items) |object, object_id| {
1360 log.debug("\n\n", .{});
1361 log.debug("relocating object {s}", .{object.name});
1362
1363 const seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
1364
1365 for (seg.sections.items) |sect, source_sect_id| {
1366 const segname = parseName(&sect.segname);
1367 const sectname = parseName(&sect.sectname);
1368
1369 var code = try self.allocator.alloc(u8, sect.size);
1370 _ = try object.file.preadAll(code, sect.offset);
1371 defer self.allocator.free(code);
1372
1373 // Parse relocs (if any)
1374 var raw_relocs = try self.allocator.alloc(u8, @sizeOf(macho.relocation_info) * sect.nreloc);
1375 defer self.allocator.free(raw_relocs);
1376 _ = try object.file.preadAll(raw_relocs, sect.reloff);
1377 const relocs = mem.bytesAsSlice(macho.relocation_info, raw_relocs);
1378
1379 // Get mapping
1380 const target_mapping = self.mappings.get(.{
1381 .object_id = @intCast(u16, object_id),
1382 .source_sect_id = @intCast(u16, source_sect_id),
1383 }) orelse {
1384 log.debug("no mapping for {s},{s}; skipping", .{ segname, sectname });
1385 continue;
1386 };
1387 const target_seg = self.load_commands.items[target_mapping.target_seg_id].Segment;
1388 const target_sect = target_seg.sections.items[target_mapping.target_sect_id];
1389 const target_sect_addr = target_sect.addr + target_mapping.offset;
1390 const target_sect_off = target_sect.offset + target_mapping.offset;
1391
1392 var addend: ?u64 = null;
1393 var sub: ?i64 = null;
1394
1395 for (relocs) |rel| {
1396 const off = @intCast(u32, rel.r_address);
1397 const this_addr = target_sect_addr + off;
1398
1399 switch (self.arch.?) {
1400 .aarch64 => {
1401 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
1402 log.debug("{s}", .{rel_type});
1403 log.debug(" | source address 0x{x}", .{this_addr});
1404 log.debug(" | offset 0x{x}", .{off});
1405
1406 if (rel_type == .ARM64_RELOC_ADDEND) {
1407 addend = rel.r_symbolnum;
1408 log.debug(" | calculated addend = 0x{x}", .{addend});
1409 // TODO followed by either PAGE21 or PAGEOFF12 only.
1410 continue;
1411 }
1412 },
1413 .x86_64 => {
1414 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
1415 log.debug("{s}", .{rel_type});
1416 log.debug(" | source address 0x{x}", .{this_addr});
1417 log.debug(" | offset 0x{x}", .{off});
1418 },
1419 else => {},
1420 }
1421
1422 const target_addr = try self.relocTargetAddr(@intCast(u16, object_id), rel);
1423 log.debug(" | target address 0x{x}", .{target_addr});
1424 if (rel.r_extern == 1) {
1425 const target_symname = object.getString(object.symtab.items[rel.r_symbolnum].n_strx);
1426 log.debug(" | target symbol '{s}'", .{target_symname});
1427 } else {
1428 const target_sectname = seg.sections.items[rel.r_symbolnum - 1].sectname;
1429 log.debug(" | target section '{s}'", .{parseName(&target_sectname)});
1430 }
1431
1432 switch (self.arch.?) {
1433 .x86_64 => {
1434 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
1435
1436 switch (rel_type) {
1437 .X86_64_RELOC_BRANCH => {
1438 assert(rel.r_length == 2);
1439 const inst = code[off..][0..4];
1440 const displacement = @bitCast(u32, @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, this_addr) - 4));
1441 mem.writeIntLittle(u32, inst, displacement);
1442 },
1443 .X86_64_RELOC_GOT_LOAD => {
1444 assert(rel.r_length == 2);
1445 const inst = code[off..][0..4];
1446 const displacement = @bitCast(u32, @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, this_addr) - 4));
1447
1448 blk: {
1449 const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
1450 const got = data_const_seg.sections.items[self.got_section_index.?];
1451 if (got.addr <= target_addr and target_addr < got.addr + got.size) break :blk;
1452 log.debug(" | rewriting to leaq", .{});
1453 code[off - 2] = 0x8d;
1454 }
1455
1456 mem.writeIntLittle(u32, inst, displacement);
1457 },
1458 .X86_64_RELOC_GOT => {
1459 assert(rel.r_length == 2);
1460 // TODO Instead of referring to the target symbol directly, we refer to it
1461 // indirectly via GOT. Getting actual target address should be done in the
1462 // helper relocTargetAddr function rather than here.
1463 const sym = object.symtab.items[rel.r_symbolnum];
1464 const sym_name = try self.allocator.dupe(u8, object.getString(sym.n_strx));
1465 const res = try self.nonlazy_pointers.getOrPut(self.allocator, sym_name);
1466 defer if (res.found_existing) self.allocator.free(sym_name);
1467
1468 const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
1469 const got = data_const_seg.sections.items[self.got_section_index.?];
1470
1471 if (!res.found_existing) {
1472 const index = @intCast(u32, self.nonlazy_pointers.items().len) - 1;
1473 assert(index < max_local_got_indirections); // TODO This is just a temp solution.
1474 res.entry.value = .{
1475 .index = index,
1476 .target_addr = target_addr,
1477 };
1478 var buf: [@sizeOf(u64)]u8 = undefined;
1479 mem.writeIntLittle(u64, &buf, target_addr);
1480 const got_offset = got.offset + (index + self.nonlazy_imports.items().len) * @sizeOf(u64);
1481
1482 log.debug(" | GOT off 0x{x}", .{got.offset});
1483 log.debug(" | writing GOT entry 0x{x} at 0x{x}", .{ target_addr, got_offset });
1484
1485 try self.file.?.pwriteAll(&buf, got_offset);
1486 }
1487
1488 const index = res.entry.value.index + self.nonlazy_imports.items().len;
1489 const actual_target_addr = got.addr + index * @sizeOf(u64);
1490
1491 log.debug(" | GOT addr 0x{x}", .{got.addr});
1492 log.debug(" | actual target address in GOT 0x{x}", .{actual_target_addr});
1493
1494 const inst = code[off..][0..4];
1495 const displacement = @bitCast(u32, @intCast(i32, @intCast(i64, actual_target_addr) - @intCast(i64, this_addr) - 4));
1496 mem.writeIntLittle(u32, inst, displacement);
1497 },
1498 .X86_64_RELOC_TLV => {
1499 assert(rel.r_length == 2);
1500 // We need to rewrite the opcode from movq to leaq.
1501 code[off - 2] = 0x8d;
1502 // Add displacement.
1503 const inst = code[off..][0..4];
1504 const displacement = @bitCast(u32, @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, this_addr) - 4));
1505 mem.writeIntLittle(u32, inst, displacement);
1506 },
1507 .X86_64_RELOC_SIGNED,
1508 .X86_64_RELOC_SIGNED_1,
1509 .X86_64_RELOC_SIGNED_2,
1510 .X86_64_RELOC_SIGNED_4,
1511 => {
1512 assert(rel.r_length == 2);
1513 const inst = code[off..][0..4];
1514 const offset = @intCast(i64, mem.readIntLittle(i32, inst));
1515 log.debug(" | calculated addend 0x{x}", .{offset});
1516 const actual_target_addr = blk: {
1517 if (rel.r_extern == 1) {
1518 break :blk @intCast(i64, target_addr) + offset;
1519 } else {
1520 const correction: i4 = switch (rel_type) {
1521 .X86_64_RELOC_SIGNED => 0,
1522 .X86_64_RELOC_SIGNED_1 => 1,
1523 .X86_64_RELOC_SIGNED_2 => 2,
1524 .X86_64_RELOC_SIGNED_4 => 4,
1525 else => unreachable,
1526 };
1527 log.debug(" | calculated correction 0x{x}", .{correction});
1528
1529 // The value encoded in the instruction is a displacement - 4 - correction.
1530 // To obtain the adjusted target address in the final binary, we need
1531 // calculate the original target address within the object file, establish
1532 // what the offset from the original target section was, and apply this
1533 // offset to the resultant target section with this relocated binary.
1534 const orig_sect_id = @intCast(u16, rel.r_symbolnum - 1);
1535 const target_map = self.mappings.get(.{
1536 .object_id = @intCast(u16, object_id),
1537 .source_sect_id = orig_sect_id,
1538 }) orelse unreachable;
1539 const orig_seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
1540 const orig_sect = orig_seg.sections.items[orig_sect_id];
1541 const orig_offset = off + offset + 4 + correction - @intCast(i64, orig_sect.addr);
1542 log.debug(" | original offset 0x{x}", .{orig_offset});
1543 const adjusted = @intCast(i64, target_addr) + orig_offset;
1544 log.debug(" | adjusted target address 0x{x}", .{adjusted});
1545 break :blk adjusted - correction;
1546 }
1547 };
1548 const result = actual_target_addr - @intCast(i64, this_addr) - 4;
1549 const displacement = @bitCast(u32, @intCast(i32, result));
1550 mem.writeIntLittle(u32, inst, displacement);
1551 },
1552 .X86_64_RELOC_SUBTRACTOR => {
1553 sub = @intCast(i64, target_addr);
1554 },
1555 .X86_64_RELOC_UNSIGNED => {
1556 switch (rel.r_length) {
1557 3 => {
1558 const inst = code[off..][0..8];
1559 const offset = mem.readIntLittle(i64, inst);
1560
1561 const result = outer: {
1562 if (rel.r_extern == 1) {
1563 log.debug(" | calculated addend 0x{x}", .{offset});
1564 if (sub) |s| {
1565 break :outer @intCast(i64, target_addr) - s + offset;
1566 } else {
1567 break :outer @intCast(i64, target_addr) + offset;
1568 }
1569 } else {
1570 // The value encoded in the instruction is an absolute offset
1571 // from the start of MachO header to the target address in the
1572 // object file. To extract the address, we calculate the offset from
1573 // the beginning of the source section to the address, and apply it to
1574 // the target address value.
1575 const orig_sect_id = @intCast(u16, rel.r_symbolnum - 1);
1576 const target_map = self.mappings.get(.{
1577 .object_id = @intCast(u16, object_id),
1578 .source_sect_id = orig_sect_id,
1579 }) orelse unreachable;
1580 const orig_seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
1581 const orig_sect = orig_seg.sections.items[orig_sect_id];
1582 const orig_offset = offset - @intCast(i64, orig_sect.addr);
1583 const actual_target_addr = inner: {
1584 if (sub) |s| {
1585 break :inner @intCast(i64, target_addr) - s + orig_offset;
1586 } else {
1587 break :inner @intCast(i64, target_addr) + orig_offset;
1588 }
1589 };
1590 log.debug(" | adjusted target address 0x{x}", .{actual_target_addr});
1591 break :outer actual_target_addr;
1592 }
1593 };
1594 mem.writeIntLittle(u64, inst, @bitCast(u64, result));
1595 sub = null;
1596
1597 rebases: {
1598 var hit: bool = false;
1599 if (target_mapping.target_seg_id == self.data_segment_cmd_index.?) {
1600 if (self.data_section_index) |index| {
1601 if (index == target_mapping.target_sect_id) hit = true;
1602 }
1603 }
1604 if (target_mapping.target_seg_id == self.data_const_segment_cmd_index.?) {
1605 if (self.data_const_section_index) |index| {
1606 if (index == target_mapping.target_sect_id) hit = true;
1607 }
1608 }
1609
1610 if (!hit) break :rebases;
1611
1612 try self.local_rebases.append(self.allocator, .{
1613 .offset = this_addr - target_seg.inner.vmaddr,
1614 .segment_id = target_mapping.target_seg_id,
1615 });
1616 }
1617 // TLV is handled via a separate offset mechanism.
1618 // Calculate the offset to the initializer.
1619 if (target_sect.flags == macho.S_THREAD_LOCAL_VARIABLES) tlv: {
1620 assert(rel.r_extern == 1);
1621 const sym = object.symtab.items[rel.r_symbolnum];
1622 if (isImport(&sym)) break :tlv;
1623
1624 const base_addr = blk: {
1625 if (self.tlv_data_section_index) |index| {
1626 const tlv_data = target_seg.sections.items[index];
1627 break :blk tlv_data.addr;
1628 } else {
1629 const tlv_bss = target_seg.sections.items[self.tlv_bss_section_index.?];
1630 break :blk tlv_bss.addr;
1631 }
1632 };
1633 // Since we require TLV data to always preceed TLV bss section, we calculate
1634 // offsets wrt to the former if it is defined; otherwise, wrt to the latter.
1635 try self.threadlocal_offsets.append(self.allocator, target_addr - base_addr);
1636 }
1637 },
1638 2 => {
1639 const inst = code[off..][0..4];
1640 const offset = mem.readIntLittle(i32, inst);
1641 log.debug(" | calculated addend 0x{x}", .{offset});
1642 const result = if (sub) |s|
1643 @intCast(i64, target_addr) - s + offset
1644 else
1645 @intCast(i64, target_addr) + offset;
1646 mem.writeIntLittle(u32, inst, @truncate(u32, @bitCast(u64, result)));
1647 sub = null;
1648 },
1649 else => |len| {
1650 log.err("unexpected relocation length 0x{x}", .{len});
1651 return error.UnexpectedRelocationLength;
1652 },
1653 }
1654 },
1655 }
1656 },
1657 .aarch64 => {
1658 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
1659
1660 switch (rel_type) {
1661 .ARM64_RELOC_BRANCH26 => {
1662 assert(rel.r_length == 2);
1663 const inst = code[off..][0..4];
1664 const displacement = @intCast(
1665 i28,
1666 @intCast(i64, target_addr) - @intCast(i64, this_addr),
1667 );
1668 var parsed = mem.bytesAsValue(
1669 meta.TagPayload(
1670 aarch64.Instruction,
1671 aarch64.Instruction.UnconditionalBranchImmediate,
1672 ),
1673 inst,
1674 );
1675 parsed.imm26 = @truncate(u26, @bitCast(u28, displacement) >> 2);
1676 },
1677 .ARM64_RELOC_PAGE21,
1678 .ARM64_RELOC_GOT_LOAD_PAGE21,
1679 .ARM64_RELOC_TLVP_LOAD_PAGE21,
1680 => {
1681 assert(rel.r_length == 2);
1682 const inst = code[off..][0..4];
1683 const ta = if (addend) |a| target_addr + a else target_addr;
1684 const this_page = @intCast(i32, this_addr >> 12);
1685 const target_page = @intCast(i32, ta >> 12);
1686 const pages = @bitCast(u21, @intCast(i21, target_page - this_page));
1687 log.debug(" | moving by {} pages", .{pages});
1688 var parsed = mem.bytesAsValue(
1689 meta.TagPayload(
1690 aarch64.Instruction,
1691 aarch64.Instruction.PCRelativeAddress,
1692 ),
1693 inst,
1694 );
1695 parsed.immhi = @truncate(u19, pages >> 2);
1696 parsed.immlo = @truncate(u2, pages);
1697 addend = null;
1698 },
1699 .ARM64_RELOC_PAGEOFF12,
1700 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
1701 => {
1702 const inst = code[off..][0..4];
1703 if (aarch64IsArithmetic(inst)) {
1704 log.debug(" | detected ADD opcode", .{});
1705 // add
1706 var parsed = mem.bytesAsValue(
1707 meta.TagPayload(
1708 aarch64.Instruction,
1709 aarch64.Instruction.AddSubtractImmediate,
1710 ),
1711 inst,
1712 );
1713 const ta = if (addend) |a| target_addr + a else target_addr;
1714 const narrowed = @truncate(u12, ta);
1715 parsed.imm12 = narrowed;
1716 } else {
1717 log.debug(" | detected LDR/STR opcode", .{});
1718 // ldr/str
1719 var parsed = mem.bytesAsValue(
1720 meta.TagPayload(
1721 aarch64.Instruction,
1722 aarch64.Instruction.LoadStoreRegister,
1723 ),
1724 inst,
1725 );
1726
1727 const ta = if (addend) |a| target_addr + a else target_addr;
1728 const narrowed = @truncate(u12, ta);
1729 log.debug(" | narrowed 0x{x}", .{narrowed});
1730 log.debug(" | parsed.size 0x{x}", .{parsed.size});
1731
1732 if (rel_type == .ARM64_RELOC_GOT_LOAD_PAGEOFF12) blk: {
1733 const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
1734 const got = data_const_seg.sections.items[self.got_section_index.?];
1735 if (got.addr <= target_addr and target_addr < got.addr + got.size) break :blk;
1736
1737 log.debug(" | rewriting to add", .{});
1738 mem.writeIntLittle(u32, inst, aarch64.Instruction.add(
1739 @intToEnum(aarch64.Register, parsed.rt),
1740 @intToEnum(aarch64.Register, parsed.rn),
1741 narrowed,
1742 false,
1743 ).toU32());
1744 addend = null;
1745 continue;
1746 }
1747
1748 const offset: u12 = blk: {
1749 if (parsed.size == 0) {
1750 if (parsed.v == 1) {
1751 // 128-bit SIMD is scaled by 16.
1752 break :blk try math.divExact(u12, narrowed, 16);
1753 }
1754 // Otherwise, 8-bit SIMD or ldrb.
1755 break :blk narrowed;
1756 } else {
1757 const denom: u4 = try math.powi(u4, 2, parsed.size);
1758 break :blk try math.divExact(u12, narrowed, denom);
1759 }
1760 };
1761 parsed.offset = offset;
1762 }
1763 addend = null;
1764 },
1765 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {
1766 const RegInfo = struct {
1767 rd: u5,
1768 rn: u5,
1769 size: u1,
1770 };
1771 const inst = code[off..][0..4];
1772 const parsed: RegInfo = blk: {
1773 if (aarch64IsArithmetic(inst)) {
1774 const curr = mem.bytesAsValue(
1775 meta.TagPayload(
1776 aarch64.Instruction,
1777 aarch64.Instruction.AddSubtractImmediate,
1778 ),
1779 inst,
1780 );
1781 break :blk .{ .rd = curr.rd, .rn = curr.rn, .size = curr.sf };
1782 } else {
1783 const curr = mem.bytesAsValue(
1784 meta.TagPayload(
1785 aarch64.Instruction,
1786 aarch64.Instruction.LoadStoreRegister,
1787 ),
1788 inst,
1789 );
1790 break :blk .{ .rd = curr.rt, .rn = curr.rn, .size = @truncate(u1, curr.size) };
1791 }
1792 };
1793 const ta = if (addend) |a| target_addr + a else target_addr;
1794 const narrowed = @truncate(u12, ta);
1795 log.debug(" | rewriting TLV access to ADD opcode", .{});
1796 // For TLV, we always generate an add instruction.
1797 mem.writeIntLittle(u32, inst, aarch64.Instruction.add(
1798 @intToEnum(aarch64.Register, parsed.rd),
1799 @intToEnum(aarch64.Register, parsed.rn),
1800 narrowed,
1801 false,
1802 ).toU32());
1803 },
1804 .ARM64_RELOC_SUBTRACTOR => {
1805 sub = @intCast(i64, target_addr);
1806 },
1807 .ARM64_RELOC_UNSIGNED => {
1808 switch (rel.r_length) {
1809 3 => {
1810 const inst = code[off..][0..8];
1811 const offset = mem.readIntLittle(i64, inst);
1812 log.debug(" | calculated addend 0x{x}", .{offset});
1813 const result = if (sub) |s|
1814 @intCast(i64, target_addr) - s + offset
1815 else
1816 @intCast(i64, target_addr) + offset;
1817 mem.writeIntLittle(u64, inst, @bitCast(u64, result));
1818 sub = null;
1819
1820 rebases: {
1821 var hit: bool = false;
1822 if (target_mapping.target_seg_id == self.data_segment_cmd_index.?) {
1823 if (self.data_section_index) |index| {
1824 if (index == target_mapping.target_sect_id) hit = true;
1825 }
1826 }
1827 if (target_mapping.target_seg_id == self.data_const_segment_cmd_index.?) {
1828 if (self.data_const_section_index) |index| {
1829 if (index == target_mapping.target_sect_id) hit = true;
1830 }
1831 }
1832
1833 if (!hit) break :rebases;
1834
1835 try self.local_rebases.append(self.allocator, .{
1836 .offset = this_addr - target_seg.inner.vmaddr,
1837 .segment_id = target_mapping.target_seg_id,
1838 });
1839 }
1840 // TLV is handled via a separate offset mechanism.
1841 // Calculate the offset to the initializer.
1842 if (target_sect.flags == macho.S_THREAD_LOCAL_VARIABLES) tlv: {
1843 assert(rel.r_extern == 1);
1844 const sym = object.symtab.items[rel.r_symbolnum];
1845 if (isImport(&sym)) break :tlv;
1846
1847 const base_addr = blk: {
1848 if (self.tlv_data_section_index) |index| {
1849 const tlv_data = target_seg.sections.items[index];
1850 break :blk tlv_data.addr;
1851 } else {
1852 const tlv_bss = target_seg.sections.items[self.tlv_bss_section_index.?];
1853 break :blk tlv_bss.addr;
1854 }
1855 };
1856 // Since we require TLV data to always preceed TLV bss section, we calculate
1857 // offsets wrt to the former if it is defined; otherwise, wrt to the latter.
1858 try self.threadlocal_offsets.append(self.allocator, target_addr - base_addr);
1859 }
1860 },
1861 2 => {
1862 const inst = code[off..][0..4];
1863 const offset = mem.readIntLittle(i32, inst);
1864 log.debug(" | calculated addend 0x{x}", .{offset});
1865 const result = if (sub) |s|
1866 @intCast(i64, target_addr) - s + offset
1867 else
1868 @intCast(i64, target_addr) + offset;
1869 mem.writeIntLittle(u32, inst, @truncate(u32, @bitCast(u64, result)));
1870 sub = null;
1871 },
1872 else => |len| {
1873 log.err("unexpected relocation length 0x{x}", .{len});
1874 return error.UnexpectedRelocationLength;
1875 },
1876 }
1877 },
1878 .ARM64_RELOC_POINTER_TO_GOT => return error.TODOArm64RelocPointerToGot,
1879 else => unreachable,
1880 }
1881 },
1882 else => unreachable,
1883 }
1884 }
1885
1886 log.debug("writing contents of '{s},{s}' section from '{s}' from 0x{x} to 0x{x}", .{
1887 segname,
1888 sectname,
1889 object.name,
1890 target_sect_off,
1891 target_sect_off + code.len,
1892 });
1893
1894 if (target_sect.flags == macho.S_ZEROFILL or
1895 target_sect.flags == macho.S_THREAD_LOCAL_ZEROFILL or
1896 target_sect.flags == macho.S_THREAD_LOCAL_VARIABLES)
1897 {
1898 log.debug("zeroing out '{s},{s}' from 0x{x} to 0x{x}", .{
1899 parseName(&target_sect.segname),
1900 parseName(&target_sect.sectname),
1901 target_sect_off,
1902 target_sect_off + code.len,
1903 });
1904 // Zero-out the space
1905 var zeroes = try self.allocator.alloc(u8, code.len);
1906 defer self.allocator.free(zeroes);
1907 mem.set(u8, zeroes, 0);
1908 try self.file.?.pwriteAll(zeroes, target_sect_off);
1909 } else {
1910 try self.file.?.pwriteAll(code, target_sect_off);
1911 }
1912 }
1913 }
1914}
1915
1916fn relocTargetAddr(self: *Zld, object_id: u16, rel: macho.relocation_info) !u64 {
1917 const object = self.objects.items[object_id];
1918 const seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
1919 const target_addr = blk: {
1920 if (rel.r_extern == 1) {
1921 const sym = object.symtab.items[rel.r_symbolnum];
1922 if (isLocal(&sym) or isExport(&sym)) {
1923 // Relocate using section offsets only.
1924 const target_mapping = self.mappings.get(.{
1925 .object_id = object_id,
1926 .source_sect_id = sym.n_sect - 1,
1927 }) orelse unreachable;
1928 const source_sect = seg.sections.items[target_mapping.source_sect_id];
1929 const target_seg = self.load_commands.items[target_mapping.target_seg_id].Segment;
1930 const target_sect = target_seg.sections.items[target_mapping.target_sect_id];
1931 const target_sect_addr = target_sect.addr + target_mapping.offset;
1932 log.debug(" | symbol local to object", .{});
1933 break :blk target_sect_addr + sym.n_value - source_sect.addr;
1934 } else if (isImport(&sym)) {
1935 // Relocate to either the artifact's local symbol, or an import from
1936 // shared library.
1937 const sym_name = object.getString(sym.n_strx);
1938 if (self.locals.get(sym_name)) |locs| {
1939 var n_value: ?u64 = null;
1940 for (locs.items) |loc| {
1941 switch (loc.tt) {
1942 .Global => {
1943 n_value = loc.inner.n_value;
1944 break;
1945 },
1946 .WeakGlobal => {
1947 n_value = loc.inner.n_value;
1948 },
1949 .Local => {},
1950 }
1951 }
1952 if (n_value) |v| {
1953 break :blk v;
1954 }
1955 log.err("local symbol export '{s}' not found", .{sym_name});
1956 return error.LocalSymbolExportNotFound;
1957 } else if (self.lazy_imports.get(sym_name)) |ext| {
1958 const segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1959 const stubs = segment.sections.items[self.stubs_section_index.?];
1960 break :blk stubs.addr + ext.index * stubs.reserved2;
1961 } else if (self.nonlazy_imports.get(sym_name)) |ext| {
1962 const segment = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
1963 const got = segment.sections.items[self.got_section_index.?];
1964 break :blk got.addr + ext.index * @sizeOf(u64);
1965 } else if (mem.eql(u8, sym_name, "__tlv_bootstrap")) {
1966 const segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
1967 const tlv = segment.sections.items[self.tlv_section_index.?];
1968 break :blk tlv.addr + self.tlv_bootstrap.?.index * @sizeOf(u64);
1969 } else {
1970 log.err("failed to resolve symbol '{s}' as a relocation target", .{sym_name});
1971 return error.FailedToResolveRelocationTarget;
1972 }
1973 } else {
1974 log.err("unexpected symbol {}, {s}", .{ sym, object.getString(sym.n_strx) });
1975 return error.UnexpectedSymbolWhenRelocating;
1976 }
1977 } else {
1978 // TODO I think we need to reparse the relocation_info as scattered_relocation_info
1979 // here to get the actual section plus offset into that section of the relocated
1980 // symbol. Unless the fine-grained location is encoded within the cell in the code
1981 // buffer?
1982 const target_mapping = self.mappings.get(.{
1983 .object_id = object_id,
1984 .source_sect_id = @intCast(u16, rel.r_symbolnum - 1),
1985 }) orelse unreachable;
1986 const target_seg = self.load_commands.items[target_mapping.target_seg_id].Segment;
1987 const target_sect = target_seg.sections.items[target_mapping.target_sect_id];
1988 break :blk target_sect.addr + target_mapping.offset;
1989 }
1990 };
1991 return target_addr;
1992}
1993
1994fn populateMetadata(self: *Zld) !void {
1995 if (self.pagezero_segment_cmd_index == null) {
1996 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
1997 try self.load_commands.append(self.allocator, .{
1998 .Segment = SegmentCommand.empty(.{
1999 .cmd = macho.LC_SEGMENT_64,
2000 .cmdsize = @sizeOf(macho.segment_command_64),
2001 .segname = makeStaticString("__PAGEZERO"),
2002 .vmaddr = 0,
2003 .vmsize = 0x100000000, // size always set to 4GB
2004 .fileoff = 0,
2005 .filesize = 0,
2006 .maxprot = 0,
2007 .initprot = 0,
2008 .nsects = 0,
2009 .flags = 0,
2010 }),
2011 });
2012 }
2013
2014 if (self.text_segment_cmd_index == null) {
2015 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
2016 try self.load_commands.append(self.allocator, .{
2017 .Segment = SegmentCommand.empty(.{
2018 .cmd = macho.LC_SEGMENT_64,
2019 .cmdsize = @sizeOf(macho.segment_command_64),
2020 .segname = makeStaticString("__TEXT"),
2021 .vmaddr = 0x100000000, // always starts at 4GB
2022 .vmsize = 0,
2023 .fileoff = 0,
2024 .filesize = 0,
2025 .maxprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE,
2026 .initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE,
2027 .nsects = 0,
2028 .flags = 0,
2029 }),
2030 });
2031 }
2032
2033 if (self.text_section_index == null) {
2034 const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2035 self.text_section_index = @intCast(u16, text_seg.sections.items.len);
2036 const alignment: u2 = switch (self.arch.?) {
2037 .x86_64 => 0,
2038 .aarch64 => 2,
2039 else => unreachable, // unhandled architecture type
2040 };
2041 try text_seg.addSection(self.allocator, .{
2042 .sectname = makeStaticString("__text"),
2043 .segname = makeStaticString("__TEXT"),
2044 .addr = 0,
2045 .size = 0,
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.stubs_section_index == null) {
2058 const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2059 self.stubs_section_index = @intCast(u16, text_seg.sections.items.len);
2060 const alignment: u2 = switch (self.arch.?) {
2061 .x86_64 => 0,
2062 .aarch64 => 2,
2063 else => unreachable, // unhandled architecture type
2064 };
2065 const stub_size: u4 = switch (self.arch.?) {
2066 .x86_64 => 6,
2067 .aarch64 => 3 * @sizeOf(u32),
2068 else => unreachable, // unhandled architecture type
2069 };
2070 try text_seg.addSection(self.allocator, .{
2071 .sectname = makeStaticString("__stubs"),
2072 .segname = makeStaticString("__TEXT"),
2073 .addr = 0,
2074 .size = 0,
2075 .offset = 0,
2076 .@"align" = alignment,
2077 .reloff = 0,
2078 .nreloc = 0,
2079 .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
2080 .reserved1 = 0,
2081 .reserved2 = stub_size,
2082 .reserved3 = 0,
2083 });
2084 }
2085
2086 if (self.stub_helper_section_index == null) {
2087 const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2088 self.stub_helper_section_index = @intCast(u16, text_seg.sections.items.len);
2089 const alignment: u2 = switch (self.arch.?) {
2090 .x86_64 => 0,
2091 .aarch64 => 2,
2092 else => unreachable, // unhandled architecture type
2093 };
2094 const stub_helper_size: u6 = switch (self.arch.?) {
2095 .x86_64 => 15,
2096 .aarch64 => 6 * @sizeOf(u32),
2097 else => unreachable,
2098 };
2099 try text_seg.addSection(self.allocator, .{
2100 .sectname = makeStaticString("__stub_helper"),
2101 .segname = makeStaticString("__TEXT"),
2102 .addr = 0,
2103 .size = stub_helper_size,
2104 .offset = 0,
2105 .@"align" = alignment,
2106 .reloff = 0,
2107 .nreloc = 0,
2108 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
2109 .reserved1 = 0,
2110 .reserved2 = 0,
2111 .reserved3 = 0,
2112 });
2113 }
2114
2115 if (self.data_const_segment_cmd_index == null) {
2116 self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
2117 try self.load_commands.append(self.allocator, .{
2118 .Segment = SegmentCommand.empty(.{
2119 .cmd = macho.LC_SEGMENT_64,
2120 .cmdsize = @sizeOf(macho.segment_command_64),
2121 .segname = makeStaticString("__DATA_CONST"),
2122 .vmaddr = 0,
2123 .vmsize = 0,
2124 .fileoff = 0,
2125 .filesize = 0,
2126 .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
2127 .initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
2128 .nsects = 0,
2129 .flags = 0,
2130 }),
2131 });
2132 }
2133
2134 if (self.got_section_index == null) {
2135 const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2136 self.got_section_index = @intCast(u16, data_const_seg.sections.items.len);
2137 try data_const_seg.addSection(self.allocator, .{
2138 .sectname = makeStaticString("__got"),
2139 .segname = makeStaticString("__DATA_CONST"),
2140 .addr = 0,
2141 .size = 0,
2142 .offset = 0,
2143 .@"align" = 3, // 2^3 = @sizeOf(u64)
2144 .reloff = 0,
2145 .nreloc = 0,
2146 .flags = macho.S_NON_LAZY_SYMBOL_POINTERS,
2147 .reserved1 = 0,
2148 .reserved2 = 0,
2149 .reserved3 = 0,
2150 });
2151 }
2152
2153 if (self.data_segment_cmd_index == null) {
2154 self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
2155 try self.load_commands.append(self.allocator, .{
2156 .Segment = SegmentCommand.empty(.{
2157 .cmd = macho.LC_SEGMENT_64,
2158 .cmdsize = @sizeOf(macho.segment_command_64),
2159 .segname = makeStaticString("__DATA"),
2160 .vmaddr = 0,
2161 .vmsize = 0,
2162 .fileoff = 0,
2163 .filesize = 0,
2164 .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
2165 .initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
2166 .nsects = 0,
2167 .flags = 0,
2168 }),
2169 });
2170 }
2171
2172 if (self.la_symbol_ptr_section_index == null) {
2173 const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2174 self.la_symbol_ptr_section_index = @intCast(u16, data_seg.sections.items.len);
2175 try data_seg.addSection(self.allocator, .{
2176 .sectname = makeStaticString("__la_symbol_ptr"),
2177 .segname = makeStaticString("__DATA"),
2178 .addr = 0,
2179 .size = 0,
2180 .offset = 0,
2181 .@"align" = 3, // 2^3 = @sizeOf(u64)
2182 .reloff = 0,
2183 .nreloc = 0,
2184 .flags = macho.S_LAZY_SYMBOL_POINTERS,
2185 .reserved1 = 0,
2186 .reserved2 = 0,
2187 .reserved3 = 0,
2188 });
2189 }
2190
2191 if (self.data_section_index == null) {
2192 const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2193 self.data_section_index = @intCast(u16, data_seg.sections.items.len);
2194 try data_seg.addSection(self.allocator, .{
2195 .sectname = makeStaticString("__data"),
2196 .segname = makeStaticString("__DATA"),
2197 .addr = 0,
2198 .size = 0,
2199 .offset = 0,
2200 .@"align" = 3, // 2^3 = @sizeOf(u64)
2201 .reloff = 0,
2202 .nreloc = 0,
2203 .flags = macho.S_REGULAR,
2204 .reserved1 = 0,
2205 .reserved2 = 0,
2206 .reserved3 = 0,
2207 });
2208 }
2209
2210 if (self.linkedit_segment_cmd_index == null) {
2211 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
2212 try self.load_commands.append(self.allocator, .{
2213 .Segment = SegmentCommand.empty(.{
2214 .cmd = macho.LC_SEGMENT_64,
2215 .cmdsize = @sizeOf(macho.segment_command_64),
2216 .segname = makeStaticString("__LINKEDIT"),
2217 .vmaddr = 0,
2218 .vmsize = 0,
2219 .fileoff = 0,
2220 .filesize = 0,
2221 .maxprot = macho.VM_PROT_READ,
2222 .initprot = macho.VM_PROT_READ,
2223 .nsects = 0,
2224 .flags = 0,
2225 }),
2226 });
2227 }
2228
2229 if (self.dyld_info_cmd_index == null) {
2230 self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len);
2231 try self.load_commands.append(self.allocator, .{
2232 .DyldInfoOnly = .{
2233 .cmd = macho.LC_DYLD_INFO_ONLY,
2234 .cmdsize = @sizeOf(macho.dyld_info_command),
2235 .rebase_off = 0,
2236 .rebase_size = 0,
2237 .bind_off = 0,
2238 .bind_size = 0,
2239 .weak_bind_off = 0,
2240 .weak_bind_size = 0,
2241 .lazy_bind_off = 0,
2242 .lazy_bind_size = 0,
2243 .export_off = 0,
2244 .export_size = 0,
2245 },
2246 });
2247 }
2248
2249 if (self.symtab_cmd_index == null) {
2250 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
2251 try self.load_commands.append(self.allocator, .{
2252 .Symtab = .{
2253 .cmd = macho.LC_SYMTAB,
2254 .cmdsize = @sizeOf(macho.symtab_command),
2255 .symoff = 0,
2256 .nsyms = 0,
2257 .stroff = 0,
2258 .strsize = 0,
2259 },
2260 });
2261 try self.strtab.append(self.allocator, 0);
2262 }
2263
2264 if (self.dysymtab_cmd_index == null) {
2265 self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len);
2266 try self.load_commands.append(self.allocator, .{
2267 .Dysymtab = .{
2268 .cmd = macho.LC_DYSYMTAB,
2269 .cmdsize = @sizeOf(macho.dysymtab_command),
2270 .ilocalsym = 0,
2271 .nlocalsym = 0,
2272 .iextdefsym = 0,
2273 .nextdefsym = 0,
2274 .iundefsym = 0,
2275 .nundefsym = 0,
2276 .tocoff = 0,
2277 .ntoc = 0,
2278 .modtaboff = 0,
2279 .nmodtab = 0,
2280 .extrefsymoff = 0,
2281 .nextrefsyms = 0,
2282 .indirectsymoff = 0,
2283 .nindirectsyms = 0,
2284 .extreloff = 0,
2285 .nextrel = 0,
2286 .locreloff = 0,
2287 .nlocrel = 0,
2288 },
2289 });
2290 }
2291
2292 if (self.dylinker_cmd_index == null) {
2293 self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len);
2294 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
2295 u64,
2296 @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH),
2297 @sizeOf(u64),
2298 ));
2299 var dylinker_cmd = emptyGenericCommandWithData(macho.dylinker_command{
2300 .cmd = macho.LC_LOAD_DYLINKER,
2301 .cmdsize = cmdsize,
2302 .name = @sizeOf(macho.dylinker_command),
2303 });
2304 dylinker_cmd.data = try self.allocator.alloc(u8, cmdsize - dylinker_cmd.inner.name);
2305 mem.set(u8, dylinker_cmd.data, 0);
2306 mem.copy(u8, dylinker_cmd.data, mem.spanZ(DEFAULT_DYLD_PATH));
2307 try self.load_commands.append(self.allocator, .{ .Dylinker = dylinker_cmd });
2308 }
2309
2310 if (self.libsystem_cmd_index == null) {
2311 self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len);
2312 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
2313 u64,
2314 @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH),
2315 @sizeOf(u64),
2316 ));
2317 // TODO Find a way to work out runtime version from the OS version triple stored in std.Target.
2318 // In the meantime, we're gonna hardcode to the minimum compatibility version of 0.0.0.
2319 const min_version = 0x0;
2320 var dylib_cmd = emptyGenericCommandWithData(macho.dylib_command{
2321 .cmd = macho.LC_LOAD_DYLIB,
2322 .cmdsize = cmdsize,
2323 .dylib = .{
2324 .name = @sizeOf(macho.dylib_command),
2325 .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files
2326 .current_version = min_version,
2327 .compatibility_version = min_version,
2328 },
2329 });
2330 dylib_cmd.data = try self.allocator.alloc(u8, cmdsize - dylib_cmd.inner.dylib.name);
2331 mem.set(u8, dylib_cmd.data, 0);
2332 mem.copy(u8, dylib_cmd.data, mem.spanZ(LIB_SYSTEM_PATH));
2333 try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
2334 }
2335
2336 if (self.main_cmd_index == null) {
2337 self.main_cmd_index = @intCast(u16, self.load_commands.items.len);
2338 try self.load_commands.append(self.allocator, .{
2339 .Main = .{
2340 .cmd = macho.LC_MAIN,
2341 .cmdsize = @sizeOf(macho.entry_point_command),
2342 .entryoff = 0x0,
2343 .stacksize = 0,
2344 },
2345 });
2346 }
2347
2348 if (self.source_version_cmd_index == null) {
2349 self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len);
2350 try self.load_commands.append(self.allocator, .{
2351 .SourceVersion = .{
2352 .cmd = macho.LC_SOURCE_VERSION,
2353 .cmdsize = @sizeOf(macho.source_version_command),
2354 .version = 0x0,
2355 },
2356 });
2357 }
2358
2359 if (self.uuid_cmd_index == null) {
2360 self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len);
2361 var uuid_cmd: macho.uuid_command = .{
2362 .cmd = macho.LC_UUID,
2363 .cmdsize = @sizeOf(macho.uuid_command),
2364 .uuid = undefined,
2365 };
2366 std.crypto.random.bytes(&uuid_cmd.uuid);
2367 try self.load_commands.append(self.allocator, .{ .Uuid = uuid_cmd });
2368 }
2369
2370 if (self.code_signature_cmd_index == null and self.arch.? == .aarch64) {
2371 self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);
2372 try self.load_commands.append(self.allocator, .{
2373 .LinkeditData = .{
2374 .cmd = macho.LC_CODE_SIGNATURE,
2375 .cmdsize = @sizeOf(macho.linkedit_data_command),
2376 .dataoff = 0,
2377 .datasize = 0,
2378 },
2379 });
2380 }
2381
2382 if (self.data_in_code_cmd_index == null and self.arch.? == .x86_64) {
2383 self.data_in_code_cmd_index = @intCast(u16, self.load_commands.items.len);
2384 try self.load_commands.append(self.allocator, .{
2385 .LinkeditData = .{
2386 .cmd = macho.LC_DATA_IN_CODE,
2387 .cmdsize = @sizeOf(macho.linkedit_data_command),
2388 .dataoff = 0,
2389 .datasize = 0,
2390 },
2391 });
2392 }
2393}
2394
2395fn flush(self: *Zld) !void {
2396 if (self.bss_section_index) |index| {
2397 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2398 const sect = &seg.sections.items[index];
2399 sect.offset = 0;
2400 }
2401
2402 if (self.tlv_bss_section_index) |index| {
2403 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2404 const sect = &seg.sections.items[index];
2405 sect.offset = 0;
2406 }
2407
2408 if (self.tlv_section_index) |index| {
2409 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2410 const sect = &seg.sections.items[index];
2411
2412 var buffer = try self.allocator.alloc(u8, sect.size);
2413 defer self.allocator.free(buffer);
2414 _ = try self.file.?.preadAll(buffer, sect.offset);
2415
2416 var stream = std.io.fixedBufferStream(buffer);
2417 var writer = stream.writer();
2418
2419 const seek_amt = 2 * @sizeOf(u64);
2420 while (self.threadlocal_offsets.popOrNull()) |offset| {
2421 try writer.context.seekBy(seek_amt);
2422 try writer.writeIntLittle(u64, offset);
2423 }
2424
2425 try self.file.?.pwriteAll(buffer, sect.offset);
2426 }
2427
2428 try self.setEntryPoint();
2429 try self.writeRebaseInfoTable();
2430 try self.writeBindInfoTable();
2431 try self.writeLazyBindInfoTable();
2432 try self.writeExportInfo();
2433 if (self.arch.? == .x86_64) {
2434 try self.writeDataInCode();
2435 }
2436
2437 {
2438 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2439 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2440 symtab.symoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
2441 }
2442
2443 try self.writeDebugInfo();
2444 try self.writeSymbolTable();
2445 try self.writeDynamicSymbolTable();
2446 try self.writeStringTable();
2447
2448 {
2449 // Seal __LINKEDIT size
2450 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2451 seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size.?);
2452 }
2453
2454 if (self.arch.? == .aarch64) {
2455 try self.writeCodeSignaturePadding();
2456 }
2457
2458 try self.writeLoadCommands();
2459 try self.writeHeader();
2460
2461 if (self.arch.? == .aarch64) {
2462 try self.writeCodeSignature();
2463 }
2464
2465 if (comptime std.Target.current.isDarwin() and std.Target.current.cpu.arch == .aarch64) {
2466 try fs.cwd().copyFile(self.out_path.?, fs.cwd(), self.out_path.?, .{});
2467 }
2468}
2469
2470fn setEntryPoint(self: *Zld) !void {
2471 // TODO we should respect the -entry flag passed in by the user to set a custom
2472 // entrypoint. For now, assume default of `_main`.
2473 const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2474 const text = seg.sections.items[self.text_section_index.?];
2475 const entry_syms = self.locals.get("_main") orelse return error.MissingMainEntrypoint;
2476
2477 var entry_sym: ?macho.nlist_64 = null;
2478 for (entry_syms.items) |es| {
2479 switch (es.tt) {
2480 .Global => {
2481 entry_sym = es.inner;
2482 break;
2483 },
2484 .WeakGlobal => {
2485 entry_sym = es.inner;
2486 },
2487 .Local => {},
2488 }
2489 }
2490 if (entry_sym == null) {
2491 log.err("no (weak) global definition of _main found", .{});
2492 return error.MissingMainEntrypoint;
2493 }
2494
2495 const name = try self.allocator.dupe(u8, "_main");
2496 try self.exports.putNoClobber(self.allocator, name, .{
2497 .n_strx = entry_sym.?.n_strx,
2498 .n_value = entry_sym.?.n_value,
2499 .n_type = macho.N_SECT | macho.N_EXT,
2500 .n_desc = entry_sym.?.n_desc,
2501 .n_sect = entry_sym.?.n_sect,
2502 });
2503
2504 const ec = &self.load_commands.items[self.main_cmd_index.?].Main;
2505 ec.entryoff = @intCast(u32, entry_sym.?.n_value - seg.inner.vmaddr);
2506}
2507
2508fn writeRebaseInfoTable(self: *Zld) !void {
2509 var pointers = std.ArrayList(Pointer).init(self.allocator);
2510 defer pointers.deinit();
2511
2512 try pointers.ensureCapacity(pointers.items.len + self.local_rebases.items.len);
2513 pointers.appendSliceAssumeCapacity(self.local_rebases.items);
2514
2515 if (self.got_section_index) |idx| {
2516 // TODO this should be cleaned up!
2517 try pointers.ensureCapacity(pointers.items.len + self.nonlazy_pointers.items().len);
2518 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2519 const sect = seg.sections.items[idx];
2520 const base_offset = sect.addr - seg.inner.vmaddr;
2521 const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
2522 const index_offset = @intCast(u32, self.nonlazy_imports.items().len);
2523 for (self.nonlazy_pointers.items()) |entry| {
2524 const index = index_offset + entry.value.index;
2525 pointers.appendAssumeCapacity(.{
2526 .offset = base_offset + index * @sizeOf(u64),
2527 .segment_id = segment_id,
2528 });
2529 }
2530 }
2531
2532 if (self.mod_init_func_section_index) |idx| {
2533 // TODO audit and investigate this.
2534 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2535 const sect = seg.sections.items[idx];
2536 const npointers = sect.size * @sizeOf(u64);
2537 const base_offset = sect.addr - seg.inner.vmaddr;
2538 const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
2539
2540 try pointers.ensureCapacity(pointers.items.len + npointers);
2541 var i: usize = 0;
2542 while (i < npointers) : (i += 1) {
2543 pointers.appendAssumeCapacity(.{
2544 .offset = base_offset + i * @sizeOf(u64),
2545 .segment_id = segment_id,
2546 });
2547 }
2548 }
2549
2550 if (self.mod_term_func_section_index) |idx| {
2551 // TODO audit and investigate this.
2552 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2553 const sect = seg.sections.items[idx];
2554 const npointers = sect.size * @sizeOf(u64);
2555 const base_offset = sect.addr - seg.inner.vmaddr;
2556 const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
2557
2558 try pointers.ensureCapacity(pointers.items.len + npointers);
2559 var i: usize = 0;
2560 while (i < npointers) : (i += 1) {
2561 pointers.appendAssumeCapacity(.{
2562 .offset = base_offset + i * @sizeOf(u64),
2563 .segment_id = segment_id,
2564 });
2565 }
2566 }
2567
2568 if (self.la_symbol_ptr_section_index) |idx| {
2569 try pointers.ensureCapacity(pointers.items.len + self.lazy_imports.items().len);
2570 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2571 const sect = seg.sections.items[idx];
2572 const base_offset = sect.addr - seg.inner.vmaddr;
2573 const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
2574 for (self.lazy_imports.items()) |entry| {
2575 pointers.appendAssumeCapacity(.{
2576 .offset = base_offset + entry.value.index * @sizeOf(u64),
2577 .segment_id = segment_id,
2578 });
2579 }
2580 }
2581
2582 std.sort.sort(Pointer, pointers.items, {}, pointerCmp);
2583
2584 const size = try rebaseInfoSize(pointers.items);
2585 var buffer = try self.allocator.alloc(u8, @intCast(usize, size));
2586 defer self.allocator.free(buffer);
2587
2588 var stream = std.io.fixedBufferStream(buffer);
2589 try writeRebaseInfo(pointers.items, stream.writer());
2590
2591 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2592 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2593 dyld_info.rebase_off = @intCast(u32, seg.inner.fileoff);
2594 dyld_info.rebase_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @sizeOf(u64)));
2595 seg.inner.filesize += dyld_info.rebase_size;
2596
2597 log.debug("writing rebase info from 0x{x} to 0x{x}", .{ dyld_info.rebase_off, dyld_info.rebase_off + dyld_info.rebase_size });
2598
2599 try self.file.?.pwriteAll(buffer, dyld_info.rebase_off);
2600}
2601
2602fn writeBindInfoTable(self: *Zld) !void {
2603 var pointers = std.ArrayList(Pointer).init(self.allocator);
2604 defer pointers.deinit();
2605
2606 if (self.got_section_index) |idx| {
2607 try pointers.ensureCapacity(pointers.items.len + self.nonlazy_imports.items().len);
2608 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2609 const sect = seg.sections.items[idx];
2610 const base_offset = sect.addr - seg.inner.vmaddr;
2611 const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
2612 for (self.nonlazy_imports.items()) |entry| {
2613 pointers.appendAssumeCapacity(.{
2614 .offset = base_offset + entry.value.index * @sizeOf(u64),
2615 .segment_id = segment_id,
2616 .dylib_ordinal = entry.value.dylib_ordinal,
2617 .name = entry.key,
2618 });
2619 }
2620 }
2621
2622 if (self.tlv_section_index) |idx| {
2623 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2624 const sect = seg.sections.items[idx];
2625 const base_offset = sect.addr - seg.inner.vmaddr;
2626 const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
2627 try pointers.append(.{
2628 .offset = base_offset + self.tlv_bootstrap.?.index * @sizeOf(u64),
2629 .segment_id = segment_id,
2630 .dylib_ordinal = self.tlv_bootstrap.?.dylib_ordinal,
2631 .name = "__tlv_bootstrap",
2632 });
2633 }
2634
2635 const size = try bindInfoSize(pointers.items);
2636 var buffer = try self.allocator.alloc(u8, @intCast(usize, size));
2637 defer self.allocator.free(buffer);
2638
2639 var stream = std.io.fixedBufferStream(buffer);
2640 try writeBindInfo(pointers.items, stream.writer());
2641
2642 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2643 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2644 dyld_info.bind_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
2645 dyld_info.bind_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)));
2646 seg.inner.filesize += dyld_info.bind_size;
2647
2648 log.debug("writing binding info from 0x{x} to 0x{x}", .{ dyld_info.bind_off, dyld_info.bind_off + dyld_info.bind_size });
2649
2650 try self.file.?.pwriteAll(buffer, dyld_info.bind_off);
2651}
2652
2653fn writeLazyBindInfoTable(self: *Zld) !void {
2654 var pointers = std.ArrayList(Pointer).init(self.allocator);
2655 defer pointers.deinit();
2656 try pointers.ensureCapacity(self.lazy_imports.items().len);
2657
2658 if (self.la_symbol_ptr_section_index) |idx| {
2659 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2660 const sect = seg.sections.items[idx];
2661 const base_offset = sect.addr - seg.inner.vmaddr;
2662 const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
2663 for (self.lazy_imports.items()) |entry| {
2664 pointers.appendAssumeCapacity(.{
2665 .offset = base_offset + entry.value.index * @sizeOf(u64),
2666 .segment_id = segment_id,
2667 .dylib_ordinal = entry.value.dylib_ordinal,
2668 .name = entry.key,
2669 });
2670 }
2671 }
2672
2673 const size = try lazyBindInfoSize(pointers.items);
2674 var buffer = try self.allocator.alloc(u8, @intCast(usize, size));
2675 defer self.allocator.free(buffer);
2676
2677 var stream = std.io.fixedBufferStream(buffer);
2678 try writeLazyBindInfo(pointers.items, stream.writer());
2679
2680 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2681 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2682 dyld_info.lazy_bind_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
2683 dyld_info.lazy_bind_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)));
2684 seg.inner.filesize += dyld_info.lazy_bind_size;
2685
2686 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 });
2687
2688 try self.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off);
2689 try self.populateLazyBindOffsetsInStubHelper(buffer);
2690}
2691
2692fn populateLazyBindOffsetsInStubHelper(self: *Zld, buffer: []const u8) !void {
2693 var stream = std.io.fixedBufferStream(buffer);
2694 var reader = stream.reader();
2695 var offsets = std.ArrayList(u32).init(self.allocator);
2696 try offsets.append(0);
2697 defer offsets.deinit();
2698 var valid_block = false;
2699
2700 while (true) {
2701 const inst = reader.readByte() catch |err| switch (err) {
2702 error.EndOfStream => break,
2703 else => return err,
2704 };
2705 const imm: u8 = inst & macho.BIND_IMMEDIATE_MASK;
2706 const opcode: u8 = inst & macho.BIND_OPCODE_MASK;
2707
2708 switch (opcode) {
2709 macho.BIND_OPCODE_DO_BIND => {
2710 valid_block = true;
2711 },
2712 macho.BIND_OPCODE_DONE => {
2713 if (valid_block) {
2714 const offset = try stream.getPos();
2715 try offsets.append(@intCast(u32, offset));
2716 }
2717 valid_block = false;
2718 },
2719 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
2720 var next = try reader.readByte();
2721 while (next != @as(u8, 0)) {
2722 next = try reader.readByte();
2723 }
2724 },
2725 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
2726 _ = try leb.readULEB128(u64, reader);
2727 },
2728 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
2729 _ = try leb.readULEB128(u64, reader);
2730 },
2731 macho.BIND_OPCODE_SET_ADDEND_SLEB => {
2732 _ = try leb.readILEB128(i64, reader);
2733 },
2734 else => {},
2735 }
2736 }
2737 assert(self.lazy_imports.items().len <= offsets.items.len);
2738
2739 const stub_size: u4 = switch (self.arch.?) {
2740 .x86_64 => 10,
2741 .aarch64 => 3 * @sizeOf(u32),
2742 else => unreachable,
2743 };
2744 const off: u4 = switch (self.arch.?) {
2745 .x86_64 => 1,
2746 .aarch64 => 2 * @sizeOf(u32),
2747 else => unreachable,
2748 };
2749 var buf: [@sizeOf(u32)]u8 = undefined;
2750 for (self.lazy_imports.items()) |entry| {
2751 const symbol = entry.value;
2752 const placeholder_off = self.stub_helper_stubs_start_off.? + symbol.index * stub_size + off;
2753 mem.writeIntLittle(u32, &buf, offsets.items[symbol.index]);
2754 try self.file.?.pwriteAll(&buf, placeholder_off);
2755 }
2756}
2757
2758fn writeExportInfo(self: *Zld) !void {
2759 var trie = Trie.init(self.allocator);
2760 defer trie.deinit();
2761
2762 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2763 for (self.exports.items()) |entry| {
2764 const name = entry.key;
2765 const symbol = entry.value;
2766 // TODO figure out if we should put all exports into the export trie
2767 assert(symbol.n_value >= text_segment.inner.vmaddr);
2768 try trie.put(.{
2769 .name = name,
2770 .vmaddr_offset = symbol.n_value - text_segment.inner.vmaddr,
2771 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
2772 });
2773 }
2774
2775 try trie.finalize();
2776 var buffer = try self.allocator.alloc(u8, @intCast(usize, trie.size));
2777 defer self.allocator.free(buffer);
2778 var stream = std.io.fixedBufferStream(buffer);
2779 const nwritten = try trie.write(stream.writer());
2780 assert(nwritten == trie.size);
2781
2782 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2783 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2784 dyld_info.export_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
2785 dyld_info.export_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)));
2786 seg.inner.filesize += dyld_info.export_size;
2787
2788 log.debug("writing export info from 0x{x} to 0x{x}", .{ dyld_info.export_off, dyld_info.export_off + dyld_info.export_size });
2789
2790 try self.file.?.pwriteAll(buffer, dyld_info.export_off);
2791}
2792
2793fn writeDebugInfo(self: *Zld) !void {
2794 var stabs = std.ArrayList(macho.nlist_64).init(self.allocator);
2795 defer stabs.deinit();
2796
2797 for (self.objects.items) |object, object_id| {
2798 var debug_info = blk: {
2799 var di = try DebugInfo.parseFromObject(self.allocator, object);
2800 break :blk di orelse continue;
2801 };
2802 defer debug_info.deinit(self.allocator);
2803
2804 // We assume there is only one CU.
2805 const compile_unit = debug_info.inner.findCompileUnit(0x0) catch |err| switch (err) {
2806 error.MissingDebugInfo => {
2807 // TODO audit cases with missing debug info and audit our dwarf.zig module.
2808 log.debug("invalid or missing debug info in {s}; skipping", .{object.name});
2809 continue;
2810 },
2811 else => |e| return e,
2812 };
2813 const name = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_name);
2814 const comp_dir = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_comp_dir);
2815
2816 {
2817 const tu_path = try std.fs.path.join(self.allocator, &[_][]const u8{ comp_dir, name });
2818 defer self.allocator.free(tu_path);
2819 const dirname = std.fs.path.dirname(tu_path) orelse "./";
2820 // Current dir
2821 try stabs.append(.{
2822 .n_strx = try self.makeString(tu_path[0 .. dirname.len + 1]),
2823 .n_type = macho.N_SO,
2824 .n_sect = 0,
2825 .n_desc = 0,
2826 .n_value = 0,
2827 });
2828 // Artifact name
2829 try stabs.append(.{
2830 .n_strx = try self.makeString(tu_path[dirname.len + 1 ..]),
2831 .n_type = macho.N_SO,
2832 .n_sect = 0,
2833 .n_desc = 0,
2834 .n_value = 0,
2835 });
2836 // Path to object file with debug info
2837 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
2838 const full_path = blk: {
2839 if (object.ar_name) |prefix| {
2840 const path = try std.os.realpath(prefix, &buffer);
2841 break :blk try std.fmt.allocPrint(self.allocator, "{s}({s})", .{ path, object.name });
2842 } else {
2843 const path = try std.os.realpath(object.name, &buffer);
2844 break :blk try mem.dupe(self.allocator, u8, path);
2845 }
2846 };
2847 defer self.allocator.free(full_path);
2848 const stat = try object.file.stat();
2849 const mtime = @intCast(u64, @divFloor(stat.mtime, 1_000_000_000));
2850 try stabs.append(.{
2851 .n_strx = try self.makeString(full_path),
2852 .n_type = macho.N_OSO,
2853 .n_sect = 0,
2854 .n_desc = 1,
2855 .n_value = mtime,
2856 });
2857 }
2858 log.debug("analyzing debug info in '{s}'", .{object.name});
2859
2860 for (object.symtab.items) |source_sym| {
2861 const symname = object.getString(source_sym.n_strx);
2862 const source_addr = source_sym.n_value;
2863 const target_syms = self.locals.get(symname) orelse continue;
2864 const target_sym: Symbol = blk: {
2865 for (target_syms.items) |ts| {
2866 if (ts.object_id == @intCast(u16, object_id)) break :blk ts;
2867 } else continue;
2868 };
2869
2870 const maybe_size = blk: for (debug_info.inner.func_list.items) |func| {
2871 if (func.pc_range) |range| {
2872 if (source_addr >= range.start and source_addr < range.end) {
2873 break :blk range.end - range.start;
2874 }
2875 }
2876 } else null;
2877
2878 if (maybe_size) |size| {
2879 try stabs.append(.{
2880 .n_strx = 0,
2881 .n_type = macho.N_BNSYM,
2882 .n_sect = target_sym.inner.n_sect,
2883 .n_desc = 0,
2884 .n_value = target_sym.inner.n_value,
2885 });
2886 try stabs.append(.{
2887 .n_strx = target_sym.inner.n_strx,
2888 .n_type = macho.N_FUN,
2889 .n_sect = target_sym.inner.n_sect,
2890 .n_desc = 0,
2891 .n_value = target_sym.inner.n_value,
2892 });
2893 try stabs.append(.{
2894 .n_strx = 0,
2895 .n_type = macho.N_FUN,
2896 .n_sect = 0,
2897 .n_desc = 0,
2898 .n_value = size,
2899 });
2900 try stabs.append(.{
2901 .n_strx = 0,
2902 .n_type = macho.N_ENSYM,
2903 .n_sect = target_sym.inner.n_sect,
2904 .n_desc = 0,
2905 .n_value = size,
2906 });
2907 } else {
2908 // TODO need a way to differentiate symbols: global, static, local, etc.
2909 try stabs.append(.{
2910 .n_strx = target_sym.inner.n_strx,
2911 .n_type = macho.N_STSYM,
2912 .n_sect = target_sym.inner.n_sect,
2913 .n_desc = 0,
2914 .n_value = target_sym.inner.n_value,
2915 });
2916 }
2917 }
2918
2919 // Close the source file!
2920 try stabs.append(.{
2921 .n_strx = 0,
2922 .n_type = macho.N_SO,
2923 .n_sect = 0,
2924 .n_desc = 0,
2925 .n_value = 0,
2926 });
2927 }
2928
2929 if (stabs.items.len == 0) return;
2930
2931 // Write stabs into the symbol table
2932 const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2933 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2934
2935 symtab.nsyms = @intCast(u32, stabs.items.len);
2936
2937 const stabs_off = symtab.symoff;
2938 const stabs_size = symtab.nsyms * @sizeOf(macho.nlist_64);
2939 log.debug("writing symbol stabs from 0x{x} to 0x{x}", .{ stabs_off, stabs_size + stabs_off });
2940 try self.file.?.pwriteAll(mem.sliceAsBytes(stabs.items), stabs_off);
2941
2942 linkedit.inner.filesize += stabs_size;
2943
2944 // Update dynamic symbol table.
2945 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
2946 dysymtab.nlocalsym = symtab.nsyms;
2947}
2948
2949fn writeSymbolTable(self: *Zld) !void {
2950 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2951 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2952
2953 var locals = std.ArrayList(macho.nlist_64).init(self.allocator);
2954 defer locals.deinit();
2955
2956 for (self.locals.items()) |entries| {
2957 log.debug("'{s}': {} entries", .{ entries.key, entries.value.items.len });
2958 // var symbol: ?macho.nlist_64 = null;
2959 for (entries.value.items) |entry| {
2960 log.debug(" | {}", .{entry.inner});
2961 log.debug(" | {}", .{entry.tt});
2962 log.debug(" | {s}", .{self.objects.items[entry.object_id].name});
2963 try locals.append(entry.inner);
2964 }
2965 }
2966 const nlocals = locals.items.len;
2967
2968 const nexports = self.exports.items().len;
2969 var exports = std.ArrayList(macho.nlist_64).init(self.allocator);
2970 defer exports.deinit();
2971
2972 try exports.ensureCapacity(nexports);
2973 for (self.exports.items()) |entry| {
2974 exports.appendAssumeCapacity(entry.value);
2975 }
2976
2977 const has_tlv: bool = self.tlv_bootstrap != null;
2978
2979 var nundefs = self.lazy_imports.items().len + self.nonlazy_imports.items().len;
2980 if (has_tlv) nundefs += 1;
2981
2982 var undefs = std.ArrayList(macho.nlist_64).init(self.allocator);
2983 defer undefs.deinit();
2984
2985 try undefs.ensureCapacity(nundefs);
2986 for (self.lazy_imports.items()) |entry| {
2987 undefs.appendAssumeCapacity(entry.value.symbol);
2988 }
2989 for (self.nonlazy_imports.items()) |entry| {
2990 undefs.appendAssumeCapacity(entry.value.symbol);
2991 }
2992 if (has_tlv) {
2993 undefs.appendAssumeCapacity(self.tlv_bootstrap.?.symbol);
2994 }
2995
2996 const locals_off = symtab.symoff + symtab.nsyms * @sizeOf(macho.nlist_64);
2997 const locals_size = nlocals * @sizeOf(macho.nlist_64);
2998 log.debug("writing local symbols from 0x{x} to 0x{x}", .{ locals_off, locals_size + locals_off });
2999 try self.file.?.pwriteAll(mem.sliceAsBytes(locals.items), locals_off);
3000
3001 const exports_off = locals_off + locals_size;
3002 const exports_size = nexports * @sizeOf(macho.nlist_64);
3003 log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off });
3004 try self.file.?.pwriteAll(mem.sliceAsBytes(exports.items), exports_off);
3005
3006 const undefs_off = exports_off + exports_size;
3007 const undefs_size = nundefs * @sizeOf(macho.nlist_64);
3008 log.debug("writing undefined symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off });
3009 try self.file.?.pwriteAll(mem.sliceAsBytes(undefs.items), undefs_off);
3010
3011 symtab.nsyms += @intCast(u32, nlocals + nexports + nundefs);
3012 seg.inner.filesize += locals_size + exports_size + undefs_size;
3013
3014 // Update dynamic symbol table.
3015 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
3016 dysymtab.nlocalsym += @intCast(u32, nlocals);
3017 dysymtab.iextdefsym = dysymtab.nlocalsym;
3018 dysymtab.nextdefsym = @intCast(u32, nexports);
3019 dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym;
3020 dysymtab.nundefsym = @intCast(u32, nundefs);
3021}
3022
3023fn writeDynamicSymbolTable(self: *Zld) !void {
3024 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
3025 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
3026 const stubs = &text_segment.sections.items[self.stubs_section_index.?];
3027 const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
3028 const got = &data_const_segment.sections.items[self.got_section_index.?];
3029 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
3030 const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
3031 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
3032
3033 const lazy = self.lazy_imports.items();
3034 const nonlazy = self.nonlazy_imports.items();
3035 const got_locals = self.nonlazy_pointers.items();
3036 dysymtab.indirectsymoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
3037 dysymtab.nindirectsyms = @intCast(u32, lazy.len * 2 + nonlazy.len + got_locals.len);
3038 const needed_size = dysymtab.nindirectsyms * @sizeOf(u32);
3039 seg.inner.filesize += needed_size;
3040
3041 log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{
3042 dysymtab.indirectsymoff,
3043 dysymtab.indirectsymoff + needed_size,
3044 });
3045
3046 var buf = try self.allocator.alloc(u8, needed_size);
3047 defer self.allocator.free(buf);
3048 var stream = std.io.fixedBufferStream(buf);
3049 var writer = stream.writer();
3050
3051 stubs.reserved1 = 0;
3052 for (lazy) |_, i| {
3053 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
3054 try writer.writeIntLittle(u32, symtab_idx);
3055 }
3056
3057 const base_id = @intCast(u32, lazy.len);
3058 got.reserved1 = base_id;
3059 for (nonlazy) |_, i| {
3060 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i + base_id);
3061 try writer.writeIntLittle(u32, symtab_idx);
3062 }
3063 // TODO there should be one common set of GOT entries.
3064 for (got_locals) |_| {
3065 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
3066 }
3067
3068 la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, nonlazy.len) + @intCast(u32, got_locals.len);
3069 for (lazy) |_, i| {
3070 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
3071 try writer.writeIntLittle(u32, symtab_idx);
3072 }
3073
3074 try self.file.?.pwriteAll(buf, dysymtab.indirectsymoff);
3075}
3076
3077fn writeStringTable(self: *Zld) !void {
3078 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
3079 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
3080 symtab.stroff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
3081 symtab.strsize = @intCast(u32, mem.alignForwardGeneric(u64, self.strtab.items.len, @alignOf(u64)));
3082 seg.inner.filesize += symtab.strsize;
3083
3084 log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });
3085
3086 try self.file.?.pwriteAll(self.strtab.items, symtab.stroff);
3087
3088 if (symtab.strsize > self.strtab.items.len and self.arch.? == .x86_64) {
3089 // This is the last section, so we need to pad it out.
3090 try self.file.?.pwriteAll(&[_]u8{0}, seg.inner.fileoff + seg.inner.filesize - 1);
3091 }
3092}
3093
3094fn writeDataInCode(self: *Zld) !void {
3095 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
3096 const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].LinkeditData;
3097 const fileoff = seg.inner.fileoff + seg.inner.filesize;
3098
3099 var buf = std.ArrayList(u8).init(self.allocator);
3100 defer buf.deinit();
3101
3102 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
3103 const text_sect = text_seg.sections.items[self.text_section_index.?];
3104 for (self.objects.items) |object, object_id| {
3105 const source_seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
3106 const source_sect = source_seg.sections.items[object.text_section_index.?];
3107 const target_mapping = self.mappings.get(.{
3108 .object_id = @intCast(u16, object_id),
3109 .source_sect_id = object.text_section_index.?,
3110 }) orelse continue;
3111
3112 try buf.ensureCapacity(
3113 buf.items.len + object.data_in_code_entries.items.len * @sizeOf(macho.data_in_code_entry),
3114 );
3115 for (object.data_in_code_entries.items) |dice| {
3116 const new_dice: macho.data_in_code_entry = .{
3117 .offset = text_sect.offset + target_mapping.offset + dice.offset,
3118 .length = dice.length,
3119 .kind = dice.kind,
3120 };
3121 buf.appendSliceAssumeCapacity(mem.asBytes(&new_dice));
3122 }
3123 }
3124 const datasize = @intCast(u32, buf.items.len);
3125
3126 dice_cmd.dataoff = @intCast(u32, fileoff);
3127 dice_cmd.datasize = datasize;
3128 seg.inner.filesize += datasize;
3129
3130 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ fileoff, fileoff + datasize });
3131
3132 try self.file.?.pwriteAll(buf.items, fileoff);
3133}
3134
3135fn writeCodeSignaturePadding(self: *Zld) !void {
3136 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
3137 const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData;
3138 const fileoff = seg.inner.fileoff + seg.inner.filesize;
3139 const needed_size = CodeSignature.calcCodeSignaturePaddingSize(
3140 self.out_path.?,
3141 fileoff,
3142 self.page_size.?,
3143 );
3144 code_sig_cmd.dataoff = @intCast(u32, fileoff);
3145 code_sig_cmd.datasize = needed_size;
3146
3147 // Advance size of __LINKEDIT segment
3148 seg.inner.filesize += needed_size;
3149 seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size.?);
3150
3151 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ fileoff, fileoff + needed_size });
3152
3153 // Pad out the space. We need to do this to calculate valid hashes for everything in the file
3154 // except for code signature data.
3155 try self.file.?.pwriteAll(&[_]u8{0}, fileoff + needed_size - 1);
3156}
3157
3158fn writeCodeSignature(self: *Zld) !void {
3159 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
3160 const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData;
3161
3162 var code_sig = CodeSignature.init(self.allocator, self.page_size.?);
3163 defer code_sig.deinit();
3164 try code_sig.calcAdhocSignature(
3165 self.file.?,
3166 self.out_path.?,
3167 text_seg.inner,
3168 code_sig_cmd,
3169 .Exe,
3170 );
3171
3172 var buffer = try self.allocator.alloc(u8, code_sig.size());
3173 defer self.allocator.free(buffer);
3174 var stream = std.io.fixedBufferStream(buffer);
3175 try code_sig.write(stream.writer());
3176
3177 log.debug("writing code signature from 0x{x} to 0x{x}", .{ code_sig_cmd.dataoff, code_sig_cmd.dataoff + buffer.len });
3178
3179 try self.file.?.pwriteAll(buffer, code_sig_cmd.dataoff);
3180}
3181
3182fn writeLoadCommands(self: *Zld) !void {
3183 var sizeofcmds: u32 = 0;
3184 for (self.load_commands.items) |lc| {
3185 sizeofcmds += lc.cmdsize();
3186 }
3187
3188 var buffer = try self.allocator.alloc(u8, sizeofcmds);
3189 defer self.allocator.free(buffer);
3190 var writer = std.io.fixedBufferStream(buffer).writer();
3191 for (self.load_commands.items) |lc| {
3192 try lc.write(writer);
3193 }
3194
3195 const off = @sizeOf(macho.mach_header_64);
3196 log.debug("writing {} load commands from 0x{x} to 0x{x}", .{ self.load_commands.items.len, off, off + sizeofcmds });
3197 try self.file.?.pwriteAll(buffer, off);
3198}
3199
3200fn writeHeader(self: *Zld) !void {
3201 var header: macho.mach_header_64 = undefined;
3202 header.magic = macho.MH_MAGIC_64;
3203
3204 const CpuInfo = struct {
3205 cpu_type: macho.cpu_type_t,
3206 cpu_subtype: macho.cpu_subtype_t,
3207 };
3208
3209 const cpu_info: CpuInfo = switch (self.arch.?) {
3210 .aarch64 => .{
3211 .cpu_type = macho.CPU_TYPE_ARM64,
3212 .cpu_subtype = macho.CPU_SUBTYPE_ARM_ALL,
3213 },
3214 .x86_64 => .{
3215 .cpu_type = macho.CPU_TYPE_X86_64,
3216 .cpu_subtype = macho.CPU_SUBTYPE_X86_64_ALL,
3217 },
3218 else => return error.UnsupportedCpuArchitecture,
3219 };
3220 header.cputype = cpu_info.cpu_type;
3221 header.cpusubtype = cpu_info.cpu_subtype;
3222 header.filetype = macho.MH_EXECUTE;
3223 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;
3224 header.reserved = 0;
3225
3226 if (self.tlv_section_index) |_|
3227 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
3228
3229 header.ncmds = @intCast(u32, self.load_commands.items.len);
3230 header.sizeofcmds = 0;
3231 for (self.load_commands.items) |cmd| {
3232 header.sizeofcmds += cmd.cmdsize();
3233 }
3234 log.debug("writing Mach-O header {}", .{header});
3235 try self.file.?.pwriteAll(mem.asBytes(&header), 0);
3236}
3237
3238pub fn makeStaticString(bytes: []const u8) [16]u8 {
3239 var buf = [_]u8{0} ** 16;
3240 assert(bytes.len <= buf.len);
3241 mem.copy(u8, &buf, bytes);
3242 return buf;
3243}
3244
3245fn makeString(self: *Zld, bytes: []const u8) !u32 {
3246 try self.strtab.ensureCapacity(self.allocator, self.strtab.items.len + bytes.len + 1);
3247 const offset = @intCast(u32, self.strtab.items.len);
3248 log.debug("writing new string '{s}' into string table at offset 0x{x}", .{ bytes, offset });
3249 self.strtab.appendSliceAssumeCapacity(bytes);
3250 self.strtab.appendAssumeCapacity(0);
3251 return offset;
3252}
3253
3254fn getString(self: *const Zld, str_off: u32) []const u8 {
3255 assert(str_off < self.strtab.items.len);
3256 return mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr + str_off));
3257}
3258
3259pub fn parseName(name: *const [16]u8) []const u8 {
3260 const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len;
3261 return name[0..len];
3262}
3263
3264fn isLocal(sym: *const macho.nlist_64) callconv(.Inline) bool {
3265 if (isExtern(sym)) return false;
3266 const tt = macho.N_TYPE & sym.n_type;
3267 return tt == macho.N_SECT;
3268}
3269
3270fn isExport(sym: *const macho.nlist_64) callconv(.Inline) bool {
3271 if (!isExtern(sym)) return false;
3272 const tt = macho.N_TYPE & sym.n_type;
3273 return tt == macho.N_SECT;
3274}
3275
3276fn isImport(sym: *const macho.nlist_64) callconv(.Inline) bool {
3277 if (!isExtern(sym)) return false;
3278 const tt = macho.N_TYPE & sym.n_type;
3279 return tt == macho.N_UNDF;
3280}
3281
3282fn isExtern(sym: *const macho.nlist_64) callconv(.Inline) bool {
3283 if ((sym.n_type & macho.N_EXT) == 0) return false;
3284 return (sym.n_type & macho.N_PEXT) == 0;
3285}
3286
3287fn isWeakDef(sym: *const macho.nlist_64) callconv(.Inline) bool {
3288 return (sym.n_desc & macho.N_WEAK_DEF) != 0;
3289}
3290
3291fn aarch64IsArithmetic(inst: *const [4]u8) callconv(.Inline) bool {
3292 const group_decode = @truncate(u5, inst[3]);
3293 return ((group_decode >> 2) == 4);
3294}
src/link/MachO/bind.zig created+145
......@@ -0,0 +1,145 @@
1const std = @import("std");
2const leb = std.leb;
3const macho = std.macho;
4
5pub const Pointer = struct {
6 offset: u64,
7 segment_id: u16,
8 dylib_ordinal: ?i64 = null,
9 name: ?[]const u8 = null,
10};
11
12pub fn pointerCmp(context: void, a: Pointer, b: Pointer) bool {
13 if (a.segment_id < b.segment_id) return true;
14 if (a.segment_id == b.segment_id) {
15 return a.offset < b.offset;
16 }
17 return false;
18}
19
20pub fn rebaseInfoSize(pointers: []const Pointer) !u64 {
21 var stream = std.io.countingWriter(std.io.null_writer);
22 var writer = stream.writer();
23 var size: u64 = 0;
24
25 for (pointers) |pointer| {
26 size += 2;
27 try leb.writeILEB128(writer, pointer.offset);
28 size += 1;
29 }
30
31 size += 1 + stream.bytes_written;
32 return size;
33}
34
35pub fn writeRebaseInfo(pointers: []const Pointer, writer: anytype) !void {
36 for (pointers) |pointer| {
37 try writer.writeByte(macho.REBASE_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.REBASE_TYPE_POINTER));
38 try writer.writeByte(macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, pointer.segment_id));
39
40 try leb.writeILEB128(writer, pointer.offset);
41 try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | @truncate(u4, 1));
42 }
43 try writer.writeByte(macho.REBASE_OPCODE_DONE);
44}
45
46pub fn bindInfoSize(pointers: []const Pointer) !u64 {
47 var stream = std.io.countingWriter(std.io.null_writer);
48 var writer = stream.writer();
49 var size: u64 = 0;
50
51 for (pointers) |pointer| {
52 size += 1;
53 if (pointer.dylib_ordinal.? > 15) {
54 try leb.writeULEB128(writer, @bitCast(u64, pointer.dylib_ordinal.?));
55 }
56 size += 1;
57
58 size += 1;
59 size += pointer.name.?.len;
60 size += 1;
61
62 size += 1;
63
64 try leb.writeILEB128(writer, pointer.offset);
65 size += 1;
66 }
67
68 size += stream.bytes_written + 1;
69 return size;
70}
71
72pub fn writeBindInfo(pointers: []const Pointer, writer: anytype) !void {
73 for (pointers) |pointer| {
74 if (pointer.dylib_ordinal.? > 15) {
75 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
76 try leb.writeULEB128(writer, @bitCast(u64, pointer.dylib_ordinal.?));
77 } else if (pointer.dylib_ordinal.? > 0) {
78 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, pointer.dylib_ordinal.?)));
79 } else {
80 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, pointer.dylib_ordinal.?)));
81 }
82 try writer.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.BIND_TYPE_POINTER));
83
84 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
85 try writer.writeAll(pointer.name.?);
86 try writer.writeByte(0);
87
88 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, pointer.segment_id));
89
90 try leb.writeILEB128(writer, pointer.offset);
91 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
92 }
93
94 try writer.writeByte(macho.BIND_OPCODE_DONE);
95}
96
97pub fn lazyBindInfoSize(pointers: []const Pointer) !u64 {
98 var stream = std.io.countingWriter(std.io.null_writer);
99 var writer = stream.writer();
100 var size: u64 = 0;
101
102 for (pointers) |pointer| {
103 size += 1;
104
105 try leb.writeILEB128(writer, pointer.offset);
106
107 size += 1;
108 if (pointer.dylib_ordinal.? > 15) {
109 try leb.writeULEB128(writer, @bitCast(u64, pointer.dylib_ordinal.?));
110 }
111
112 size += 1;
113 size += pointer.name.?.len;
114 size += 1;
115
116 size += 2;
117 }
118
119 size += stream.bytes_written;
120 return size;
121}
122
123pub fn writeLazyBindInfo(pointers: []const Pointer, writer: anytype) !void {
124 for (pointers) |pointer| {
125 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, pointer.segment_id));
126
127 try leb.writeILEB128(writer, pointer.offset);
128
129 if (pointer.dylib_ordinal.? > 15) {
130 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
131 try leb.writeULEB128(writer, @bitCast(u64, pointer.dylib_ordinal.?));
132 } else if (pointer.dylib_ordinal.? > 0) {
133 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, pointer.dylib_ordinal.?)));
134 } else {
135 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, pointer.dylib_ordinal.?)));
136 }
137
138 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
139 try writer.writeAll(pointer.name.?);
140 try writer.writeByte(0);
141
142 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
143 try writer.writeByte(macho.BIND_OPCODE_DONE);
144 }
145}
src/link/MachO/imports.zig deleted-152
......@@ -1,152 +0,0 @@
1const std = @import("std");
2const leb = std.leb;
3const macho = std.macho;
4const mem = std.mem;
5
6const assert = std.debug.assert;
7const Allocator = mem.Allocator;
8
9pub const ExternSymbol = struct {
10 /// MachO symbol table entry.
11 inner: macho.nlist_64,
12
13 /// Id of the dynamic library where the specified entries can be found.
14 /// Id of 0 means self.
15 /// TODO this should really be an id into the table of all defined
16 /// dylibs.
17 dylib_ordinal: i64 = 0,
18
19 /// Id of the segment where this symbol is defined (will have its address
20 /// resolved).
21 segment: u16 = 0,
22
23 /// Offset relative to the start address of the `segment`.
24 offset: u32 = 0,
25};
26
27pub fn rebaseInfoSize(symbols: anytype) !u64 {
28 var stream = std.io.countingWriter(std.io.null_writer);
29 var writer = stream.writer();
30 var size: u64 = 0;
31
32 for (symbols) |entry| {
33 size += 2;
34 try leb.writeILEB128(writer, entry.value.offset);
35 size += 1;
36 }
37
38 size += 1 + stream.bytes_written;
39 return size;
40}
41
42pub fn writeRebaseInfo(symbols: anytype, writer: anytype) !void {
43 for (symbols) |entry| {
44 const symbol = entry.value;
45 try writer.writeByte(macho.REBASE_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.REBASE_TYPE_POINTER));
46 try writer.writeByte(macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
47 try leb.writeILEB128(writer, symbol.offset);
48 try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | @truncate(u4, 1));
49 }
50 try writer.writeByte(macho.REBASE_OPCODE_DONE);
51}
52
53pub fn bindInfoSize(symbols: anytype) !u64 {
54 var stream = std.io.countingWriter(std.io.null_writer);
55 var writer = stream.writer();
56 var size: u64 = 0;
57
58 for (symbols) |entry| {
59 const symbol = entry.value;
60
61 size += 1;
62 if (symbol.dylib_ordinal > 15) {
63 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
64 }
65 size += 1;
66
67 size += 1;
68 size += entry.key.len;
69 size += 1;
70
71 size += 1;
72 try leb.writeILEB128(writer, symbol.offset);
73 size += 2;
74 }
75
76 size += stream.bytes_written;
77 return size;
78}
79
80pub fn writeBindInfo(symbols: anytype, writer: anytype) !void {
81 for (symbols) |entry| {
82 const symbol = entry.value;
83
84 if (symbol.dylib_ordinal > 15) {
85 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
86 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
87 } else if (symbol.dylib_ordinal > 0) {
88 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
89 } else {
90 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
91 }
92 try writer.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.BIND_TYPE_POINTER));
93
94 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
95 try writer.writeAll(entry.key);
96 try writer.writeByte(0);
97
98 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
99 try leb.writeILEB128(writer, symbol.offset);
100 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
101 try writer.writeByte(macho.BIND_OPCODE_DONE);
102 }
103}
104
105pub fn lazyBindInfoSize(symbols: anytype) !u64 {
106 var stream = std.io.countingWriter(std.io.null_writer);
107 var writer = stream.writer();
108 var size: u64 = 0;
109
110 for (symbols) |entry| {
111 const symbol = entry.value;
112 size += 1;
113 try leb.writeILEB128(writer, symbol.offset);
114 size += 1;
115 if (symbol.dylib_ordinal > 15) {
116 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
117 }
118
119 size += 1;
120 size += entry.key.len;
121 size += 1;
122
123 size += 2;
124 }
125
126 size += stream.bytes_written;
127 return size;
128}
129
130pub fn writeLazyBindInfo(symbols: anytype, writer: anytype) !void {
131 for (symbols) |entry| {
132 const symbol = entry.value;
133 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
134 try leb.writeILEB128(writer, symbol.offset);
135
136 if (symbol.dylib_ordinal > 15) {
137 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
138 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
139 } else if (symbol.dylib_ordinal > 0) {
140 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
141 } else {
142 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
143 }
144
145 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
146 try writer.writeAll(entry.key);
147 try writer.writeByte(0);
148
149 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
150 try writer.writeByte(macho.BIND_OPCODE_DONE);
151 }
152}
src/main.zig+62-17
......@@ -557,7 +557,7 @@ fn buildOutputType(
557557 var test_filter: ?[]const u8 = null;
558558 var test_name_prefix: ?[]const u8 = null;
559559 var override_local_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LOCAL_CACHE_DIR");
560 var override_global_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_GLOBAL_CACHE_DIR");
560 var override_global_cache_dir: ?[]const u8 = null;
561561 var override_lib_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LIB_DIR");
562562 var main_pkg_path: ?[]const u8 = null;
563563 var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no;
......@@ -841,7 +841,11 @@ fn buildOutputType(
841841 } else if (mem.eql(u8, arg, "--debug-log")) {
842842 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
843843 i += 1;
844 try log_scopes.append(gpa, args[i]);
844 if (!build_options.enable_logging) {
845 std.log.warn("Zig was compiled without logging enabled (-Dlog). --debug-log has no effect.", .{});
846 } else {
847 try log_scopes.append(gpa, args[i]);
848 }
845849 } else if (mem.eql(u8, arg, "-fcompiler-rt")) {
846850 want_compiler_rt = true;
847851 } else if (mem.eql(u8, arg, "-fno-compiler-rt")) {
......@@ -2633,6 +2637,50 @@ fn argvCmd(allocator: *Allocator, argv: []const []const u8) ![]u8 {
26332637 return cmd.toOwnedSlice();
26342638}
26352639
2640fn readSourceFileToEndAlloc(allocator: *mem.Allocator, input: *const fs.File, size_hint: ?usize) ![]const u8 {
2641 const source_code = input.readToEndAllocOptions(
2642 allocator,
2643 max_src_size,
2644 size_hint,
2645 @alignOf(u16),
2646 null,
2647 ) catch |err| switch (err) {
2648 error.ConnectionResetByPeer => unreachable,
2649 error.ConnectionTimedOut => unreachable,
2650 error.NotOpenForReading => unreachable,
2651 else => |e| return e,
2652 };
2653 errdefer allocator.free(source_code);
2654
2655 // Detect unsupported file types with their Byte Order Mark
2656 const unsupported_boms = [_][]const u8{
2657 "\xff\xfe\x00\x00", // UTF-32 little endian
2658 "\xfe\xff\x00\x00", // UTF-32 big endian
2659 "\xfe\xff", // UTF-16 big endian
2660 };
2661 for (unsupported_boms) |bom| {
2662 if (mem.startsWith(u8, source_code, bom)) {
2663 return error.UnsupportedEncoding;
2664 }
2665 }
2666
2667 // If the file starts with a UTF-16 little endian BOM, translate it to UTF-8
2668 if (mem.startsWith(u8, source_code, "\xff\xfe")) {
2669 const source_code_utf16_le = mem.bytesAsSlice(u16, source_code);
2670 const source_code_utf8 = std.unicode.utf16leToUtf8Alloc(allocator, source_code_utf16_le) catch |err| switch (err) {
2671 error.DanglingSurrogateHalf => error.UnsupportedEncoding,
2672 error.ExpectedSecondSurrogateHalf => error.UnsupportedEncoding,
2673 error.UnexpectedSecondSurrogateHalf => error.UnsupportedEncoding,
2674 else => |e| return e,
2675 };
2676
2677 allocator.free(source_code);
2678 return source_code_utf8;
2679 }
2680
2681 return source_code;
2682}
2683
26362684pub const usage_fmt =
26372685 \\Usage: zig fmt [file]...
26382686 \\
......@@ -2704,9 +2752,10 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
27042752 fatal("cannot use --stdin with positional arguments", .{});
27052753 }
27062754
2707 const stdin = io.getStdIn().reader();
2708
2709 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 };
27102759 defer gpa.free(source_code);
27112760
27122761 var tree = std.zig.parse(gpa, source_code) catch |err| {
......@@ -2781,6 +2830,7 @@ const FmtError = error{
27812830 EndOfStream,
27822831 Unseekable,
27832832 NotOpenForWriting,
2833 UnsupportedEncoding,
27842834} || fs.File.OpenError;
27852835
27862836fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void {
......@@ -2846,21 +2896,15 @@ fn fmtPathFile(
28462896 if (stat.kind == .Directory)
28472897 return error.IsDir;
28482898
2849 const source_code = source_file.readToEndAllocOptions(
2899 const source_code = try readSourceFileToEndAlloc(
28502900 fmt.gpa,
2851 max_src_size,
2901 &source_file,
28522902 std.math.cast(usize, stat.size) catch return error.FileTooBig,
2853 @alignOf(u8),
2854 null,
2855 ) catch |err| switch (err) {
2856 error.ConnectionResetByPeer => unreachable,
2857 error.ConnectionTimedOut => unreachable,
2858 error.NotOpenForReading => unreachable,
2859 else => |e| return e,
2860 };
2903 );
2904 defer fmt.gpa.free(source_code);
2905
28612906 source_file.close();
28622907 file_closed = true;
2863 defer fmt.gpa.free(source_code);
28642908
28652909 // Add to set after no longer possible to get error.IsDir.
28662910 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
......@@ -3237,7 +3281,8 @@ pub const ClangArgIterator = struct {
32373281 self.zig_equivalent = clang_arg.zig_equivalent;
32383282 break :find_clang_arg;
32393283 },
3240 } else {
3284 }
3285 else {
32413286 fatal("Unknown Clang option: '{s}'", .{arg});
32423287 }
32433288 }
src/stage1/all_types.hpp+21-8
......@@ -391,6 +391,8 @@ enum LazyValueId {
391391 LazyValueIdAlignOf,
392392 LazyValueIdSizeOf,
393393 LazyValueIdPtrType,
394 LazyValueIdPtrTypeSimple,
395 LazyValueIdPtrTypeSimpleConst,
394396 LazyValueIdOptType,
395397 LazyValueIdSliceType,
396398 LazyValueIdFnType,
......@@ -467,6 +469,13 @@ struct LazyValuePtrType {
467469 bool is_allowzero;
468470};
469471
472struct LazyValuePtrTypeSimple {
473 LazyValue base;
474
475 IrAnalyze *ira;
476 IrInstGen *elem_type;
477};
478
470479struct LazyValueOptType {
471480 LazyValue base;
472481
......@@ -2130,10 +2139,6 @@ struct CodeGen {
21302139 Buf llvm_ir_file_output_path;
21312140 Buf analysis_json_output_path;
21322141 Buf docs_output_path;
2133 Buf *cache_dir;
2134 Buf *c_artifact_dir;
2135 const char **libc_include_dir_list;
2136 size_t libc_include_dir_len;
21372142
21382143 Buf *builtin_zig_path;
21392144 Buf *zig_std_special_dir; // Cannot be overridden; derived from zig_lib_dir.
......@@ -2610,7 +2615,8 @@ enum IrInstSrcId {
26102615 IrInstSrcIdEnumToInt,
26112616 IrInstSrcIdIntToErr,
26122617 IrInstSrcIdErrToInt,
2613 IrInstSrcIdCheckSwitchProngs,
2618 IrInstSrcIdCheckSwitchProngsUnderYes,
2619 IrInstSrcIdCheckSwitchProngsUnderNo,
26142620 IrInstSrcIdCheckStatementIsVoid,
26152621 IrInstSrcIdTypeName,
26162622 IrInstSrcIdDeclRef,
......@@ -2624,12 +2630,15 @@ enum IrInstSrcId {
26242630 IrInstSrcIdHasField,
26252631 IrInstSrcIdSetEvalBranchQuota,
26262632 IrInstSrcIdPtrType,
2633 IrInstSrcIdPtrTypeSimple,
2634 IrInstSrcIdPtrTypeSimpleConst,
26272635 IrInstSrcIdAlignCast,
26282636 IrInstSrcIdImplicitCast,
26292637 IrInstSrcIdResolveResult,
26302638 IrInstSrcIdResetResult,
26312639 IrInstSrcIdSetAlignStack,
2632 IrInstSrcIdArgType,
2640 IrInstSrcIdArgTypeAllowVarFalse,
2641 IrInstSrcIdArgTypeAllowVarTrue,
26332642 IrInstSrcIdExport,
26342643 IrInstSrcIdExtern,
26352644 IrInstSrcIdErrorReturnTrace,
......@@ -3294,6 +3303,12 @@ struct IrInstSrcArrayType {
32943303 IrInstSrc *child_type;
32953304};
32963305
3306struct IrInstSrcPtrTypeSimple {
3307 IrInstSrc base;
3308
3309 IrInstSrc *child_type;
3310};
3311
32973312struct IrInstSrcPtrType {
32983313 IrInstSrc base;
32993314
......@@ -4020,7 +4035,6 @@ struct IrInstSrcCheckSwitchProngs {
40204035 IrInstSrcCheckSwitchProngsRange *ranges;
40214036 size_t range_count;
40224037 AstNode* else_prong;
4023 bool have_underscore_prong;
40244038};
40254039
40264040struct IrInstSrcCheckStatementIsVoid {
......@@ -4144,7 +4158,6 @@ struct IrInstSrcArgType {
41444158
41454159 IrInstSrc *fn_type;
41464160 IrInstSrc *arg_index;
4147 bool allow_var;
41484161};
41494162
41504163struct IrInstSrcExport {
src/stage1/analyze.cpp+48-1
......@@ -1237,6 +1237,22 @@ Error type_val_resolve_zero_bits(CodeGen *g, ZigValue *type_val, ZigType *parent
12371237 parent_type_val, is_zero_bits);
12381238 }
12391239 }
1240 case LazyValueIdPtrTypeSimple:
1241 case LazyValueIdPtrTypeSimpleConst: {
1242 LazyValuePtrTypeSimple *lazy_ptr_type = reinterpret_cast<LazyValuePtrTypeSimple *>(type_val->data.x_lazy);
1243
1244 if (parent_type_val == lazy_ptr_type->elem_type->value) {
1245 // Does a struct which contains a pointer field to itself have bits? Yes.
1246 *is_zero_bits = false;
1247 return ErrorNone;
1248 } else {
1249 if (parent_type_val == nullptr) {
1250 parent_type_val = type_val;
1251 }
1252 return type_val_resolve_zero_bits(g, lazy_ptr_type->elem_type->value, parent_type,
1253 parent_type_val, is_zero_bits);
1254 }
1255 }
12401256 case LazyValueIdArrayType: {
12411257 LazyValueArrayType *lazy_array_type =
12421258 reinterpret_cast<LazyValueArrayType *>(type_val->data.x_lazy);
......@@ -1285,6 +1301,8 @@ Error type_val_resolve_is_opaque_type(CodeGen *g, ZigValue *type_val, bool *is_o
12851301 zig_unreachable();
12861302 case LazyValueIdSliceType:
12871303 case LazyValueIdPtrType:
1304 case LazyValueIdPtrTypeSimple:
1305 case LazyValueIdPtrTypeSimpleConst:
12881306 case LazyValueIdFnType:
12891307 case LazyValueIdOptType:
12901308 case LazyValueIdErrUnionType:
......@@ -1313,6 +1331,11 @@ static ReqCompTime type_val_resolve_requires_comptime(CodeGen *g, ZigValue *type
13131331 LazyValuePtrType *lazy_ptr_type = reinterpret_cast<LazyValuePtrType *>(type_val->data.x_lazy);
13141332 return type_val_resolve_requires_comptime(g, lazy_ptr_type->elem_type->value);
13151333 }
1334 case LazyValueIdPtrTypeSimple:
1335 case LazyValueIdPtrTypeSimpleConst: {
1336 LazyValuePtrTypeSimple *lazy_ptr_type = reinterpret_cast<LazyValuePtrTypeSimple *>(type_val->data.x_lazy);
1337 return type_val_resolve_requires_comptime(g, lazy_ptr_type->elem_type->value);
1338 }
13161339 case LazyValueIdOptType: {
13171340 LazyValueOptType *lazy_opt_type = reinterpret_cast<LazyValueOptType *>(type_val->data.x_lazy);
13181341 return type_val_resolve_requires_comptime(g, lazy_opt_type->payload_type->value);
......@@ -1413,6 +1436,24 @@ start_over:
14131436 }
14141437 return ErrorNone;
14151438 }
1439 case LazyValueIdPtrTypeSimple:
1440 case LazyValueIdPtrTypeSimpleConst: {
1441 LazyValuePtrTypeSimple *lazy_ptr_type = reinterpret_cast<LazyValuePtrTypeSimple *>(type_val->data.x_lazy);
1442 bool is_zero_bits;
1443 if ((err = type_val_resolve_zero_bits(g, lazy_ptr_type->elem_type->value, nullptr,
1444 nullptr, &is_zero_bits)))
1445 {
1446 return err;
1447 }
1448 if (is_zero_bits) {
1449 *abi_size = 0;
1450 *size_in_bits = 0;
1451 } else {
1452 *abi_size = g->builtin_types.entry_usize->abi_size;
1453 *size_in_bits = g->builtin_types.entry_usize->size_in_bits;
1454 }
1455 return ErrorNone;
1456 }
14161457 case LazyValueIdFnType:
14171458 *abi_size = g->builtin_types.entry_usize->abi_size;
14181459 *size_in_bits = g->builtin_types.entry_usize->size_in_bits;
......@@ -1449,6 +1490,8 @@ Error type_val_resolve_abi_align(CodeGen *g, AstNode *source_node, ZigValue *typ
14491490 zig_unreachable();
14501491 case LazyValueIdSliceType:
14511492 case LazyValueIdPtrType:
1493 case LazyValueIdPtrTypeSimple:
1494 case LazyValueIdPtrTypeSimpleConst:
14521495 case LazyValueIdFnType:
14531496 *abi_align = g->builtin_types.entry_usize->abi_align;
14541497 return ErrorNone;
......@@ -1506,7 +1549,9 @@ static OnePossibleValue type_val_resolve_has_one_possible_value(CodeGen *g, ZigV
15061549 return OnePossibleValueYes;
15071550 return type_val_resolve_has_one_possible_value(g, lazy_array_type->elem_type->value);
15081551 }
1509 case LazyValueIdPtrType: {
1552 case LazyValueIdPtrType:
1553 case LazyValueIdPtrTypeSimple:
1554 case LazyValueIdPtrTypeSimpleConst: {
15101555 Error err;
15111556 bool zero_bits;
15121557 if ((err = type_val_resolve_zero_bits(g, type_val, nullptr, nullptr, &zero_bits))) {
......@@ -5758,6 +5803,8 @@ static bool can_mutate_comptime_var_state(ZigValue *value) {
57585803 case LazyValueIdAlignOf:
57595804 case LazyValueIdSizeOf:
57605805 case LazyValueIdPtrType:
5806 case LazyValueIdPtrTypeSimple:
5807 case LazyValueIdPtrTypeSimpleConst:
57615808 case LazyValueIdOptType:
57625809 case LazyValueIdSliceType:
57635810 case LazyValueIdFnType:
src/stage1/ir.cpp+140-26
......@@ -476,7 +476,8 @@ static void destroy_instruction_src(IrInstSrc *inst) {
476476 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcIntToErr *>(inst));
477477 case IrInstSrcIdErrToInt:
478478 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcErrToInt *>(inst));
479 case IrInstSrcIdCheckSwitchProngs:
479 case IrInstSrcIdCheckSwitchProngsUnderNo:
480 case IrInstSrcIdCheckSwitchProngsUnderYes:
480481 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCheckSwitchProngs *>(inst));
481482 case IrInstSrcIdCheckStatementIsVoid:
482483 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCheckStatementIsVoid *>(inst));
......@@ -486,6 +487,9 @@ static void destroy_instruction_src(IrInstSrc *inst) {
486487 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTagName *>(inst));
487488 case IrInstSrcIdPtrType:
488489 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPtrType *>(inst));
490 case IrInstSrcIdPtrTypeSimple:
491 case IrInstSrcIdPtrTypeSimpleConst:
492 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPtrTypeSimple *>(inst));
489493 case IrInstSrcIdDeclRef:
490494 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcDeclRef *>(inst));
491495 case IrInstSrcIdPanic:
......@@ -514,7 +518,8 @@ static void destroy_instruction_src(IrInstSrc *inst) {
514518 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcResetResult *>(inst));
515519 case IrInstSrcIdSetAlignStack:
516520 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSetAlignStack *>(inst));
517 case IrInstSrcIdArgType:
521 case IrInstSrcIdArgTypeAllowVarFalse:
522 case IrInstSrcIdArgTypeAllowVarTrue:
518523 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcArgType *>(inst));
519524 case IrInstSrcIdExport:
520525 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcExport *>(inst));
......@@ -1470,10 +1475,6 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrToInt *) {
14701475 return IrInstSrcIdErrToInt;
14711476}
14721477
1473static constexpr IrInstSrcId ir_inst_id(IrInstSrcCheckSwitchProngs *) {
1474 return IrInstSrcIdCheckSwitchProngs;
1475}
1476
14771478static constexpr IrInstSrcId ir_inst_id(IrInstSrcCheckStatementIsVoid *) {
14781479 return IrInstSrcIdCheckStatementIsVoid;
14791480}
......@@ -1546,10 +1547,6 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetAlignStack *) {
15461547 return IrInstSrcIdSetAlignStack;
15471548}
15481549
1549static constexpr IrInstSrcId ir_inst_id(IrInstSrcArgType *) {
1550 return IrInstSrcIdArgType;
1551}
1552
15531550static constexpr IrInstSrcId ir_inst_id(IrInstSrcExport *) {
15541551 return IrInstSrcIdExport;
15551552}
......@@ -2615,11 +2612,35 @@ static IrInstGen *ir_build_br_gen(IrAnalyze *ira, IrInst *source_instr, IrBasicB
26152612 return &inst->base;
26162613}
26172614
2615static IrInstSrc *ir_build_ptr_type_simple(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
2616 IrInstSrc *child_type, bool is_const)
2617{
2618 IrInstSrcPtrTypeSimple *inst = heap::c_allocator.create<IrInstSrcPtrTypeSimple>();
2619 inst->base.id = is_const ? IrInstSrcIdPtrTypeSimpleConst : IrInstSrcIdPtrTypeSimple;
2620 inst->base.base.scope = scope;
2621 inst->base.base.source_node = source_node;
2622 inst->base.base.debug_id = exec_next_debug_id(irb->exec);
2623 inst->base.owner_bb = irb->current_basic_block;
2624 ir_instruction_append(irb->current_basic_block, &inst->base);
2625
2626 inst->child_type = child_type;
2627
2628 ir_ref_instruction(child_type, irb->current_basic_block);
2629
2630 return &inst->base;
2631}
2632
26182633static IrInstSrc *ir_build_ptr_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
26192634 IrInstSrc *child_type, bool is_const, bool is_volatile, PtrLen ptr_len,
26202635 IrInstSrc *sentinel, IrInstSrc *align_value,
26212636 uint32_t bit_offset_start, uint32_t host_int_bytes, bool is_allow_zero)
26222637{
2638 if (!is_volatile && ptr_len == PtrLenSingle && sentinel == nullptr && align_value == nullptr &&
2639 bit_offset_start == 0 && host_int_bytes == 0 && is_allow_zero == 0)
2640 {
2641 return ir_build_ptr_type_simple(irb, scope, source_node, child_type, is_const);
2642 }
2643
26232644 IrInstSrcPtrType *inst = ir_build_instruction<IrInstSrcPtrType>(irb, scope, source_node);
26242645 inst->sentinel = sentinel;
26252646 inst->align_value = align_value;
......@@ -4354,13 +4375,19 @@ static IrInstSrc *ir_build_check_switch_prongs(IrBuilderSrc *irb, Scope *scope,
43544375 IrInstSrc *target_value, IrInstSrcCheckSwitchProngsRange *ranges, size_t range_count,
43554376 AstNode* else_prong, bool have_underscore_prong)
43564377{
4357 IrInstSrcCheckSwitchProngs *instruction = ir_build_instruction<IrInstSrcCheckSwitchProngs>(
4358 irb, scope, source_node);
4378 IrInstSrcCheckSwitchProngs *instruction = heap::c_allocator.create<IrInstSrcCheckSwitchProngs>();
4379 instruction->base.id = have_underscore_prong ?
4380 IrInstSrcIdCheckSwitchProngsUnderYes : IrInstSrcIdCheckSwitchProngsUnderNo;
4381 instruction->base.base.scope = scope;
4382 instruction->base.base.source_node = source_node;
4383 instruction->base.base.debug_id = exec_next_debug_id(irb->exec);
4384 instruction->base.owner_bb = irb->current_basic_block;
4385 ir_instruction_append(irb->current_basic_block, &instruction->base);
4386
43594387 instruction->target_value = target_value;
43604388 instruction->ranges = ranges;
43614389 instruction->range_count = range_count;
43624390 instruction->else_prong = else_prong;
4363 instruction->have_underscore_prong = have_underscore_prong;
43644391
43654392 ir_ref_instruction(target_value, irb->current_basic_block);
43664393 for (size_t i = 0; i < range_count; i += 1) {
......@@ -4590,10 +4617,17 @@ static IrInstSrc *ir_build_set_align_stack(IrBuilderSrc *irb, Scope *scope, AstN
45904617static IrInstSrc *ir_build_arg_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
45914618 IrInstSrc *fn_type, IrInstSrc *arg_index, bool allow_var)
45924619{
4593 IrInstSrcArgType *instruction = ir_build_instruction<IrInstSrcArgType>(irb, scope, source_node);
4620 IrInstSrcArgType *instruction = heap::c_allocator.create<IrInstSrcArgType>();
4621 instruction->base.id = allow_var ?
4622 IrInstSrcIdArgTypeAllowVarTrue : IrInstSrcIdArgTypeAllowVarFalse;
4623 instruction->base.base.scope = scope;
4624 instruction->base.base.source_node = source_node;
4625 instruction->base.base.debug_id = exec_next_debug_id(irb->exec);
4626 instruction->base.owner_bb = irb->current_basic_block;
4627 ir_instruction_append(irb->current_basic_block, &instruction->base);
4628
45944629 instruction->fn_type = fn_type;
45954630 instruction->arg_index = arg_index;
4596 instruction->allow_var = allow_var;
45974631
45984632 ir_ref_instruction(fn_type, irb->current_basic_block);
45994633 ir_ref_instruction(arg_index, irb->current_basic_block);
......@@ -29702,7 +29736,7 @@ static IrInstGen *ir_analyze_instruction_test_comptime(IrAnalyze *ira, IrInstSrc
2970229736}
2970329737
2970429738static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
29705 IrInstSrcCheckSwitchProngs *instruction)
29739 IrInstSrcCheckSwitchProngs *instruction, bool have_underscore_prong)
2970629740{
2970729741 IrInstGen *target_value = instruction->target_value->child;
2970829742 ZigType *switch_type = target_value->value->type;
......@@ -29767,7 +29801,7 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
2976729801 bigint_incr(&field_index);
2976829802 }
2976929803 }
29770 if (instruction->have_underscore_prong) {
29804 if (have_underscore_prong) {
2977129805 if (!switch_type->data.enumeration.non_exhaustive) {
2977229806 ir_add_error(ira, &instruction->base.base,
2977329807 buf_sprintf("switch on exhaustive enum has `_` prong"));
......@@ -30871,6 +30905,24 @@ static IrInstGen *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstSrcPtr
3087130905 return ir_build_ptr_to_int_gen(ira, &instruction->base.base, target);
3087230906}
3087330907
30908static IrInstGen *ir_analyze_instruction_ptr_type_simple(IrAnalyze *ira,
30909 IrInstSrcPtrTypeSimple *instruction, bool is_const)
30910{
30911 IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_type);
30912 result->value->special = ConstValSpecialLazy;
30913
30914 LazyValuePtrTypeSimple *lazy_ptr_type = heap::c_allocator.create<LazyValuePtrTypeSimple>();
30915 lazy_ptr_type->ira = ira; ira_ref(ira);
30916 result->value->data.x_lazy = &lazy_ptr_type->base;
30917 lazy_ptr_type->base.id = is_const ? LazyValueIdPtrTypeSimpleConst : LazyValueIdPtrTypeSimple;
30918
30919 lazy_ptr_type->elem_type = instruction->child_type->child;
30920 if (ir_resolve_type_lazy(ira, lazy_ptr_type->elem_type) == nullptr)
30921 return ira->codegen->invalid_inst_gen;
30922
30923 return result;
30924}
30925
3087430926static IrInstGen *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstSrcPtrType *instruction) {
3087530927 IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_type);
3087630928 result->value->special = ConstValSpecialLazy;
......@@ -30976,7 +31028,9 @@ static IrInstGen *ir_analyze_instruction_set_align_stack(IrAnalyze *ira, IrInstS
3097631028 return ir_const_void(ira, &instruction->base.base);
3097731029}
3097831030
30979static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgType *instruction) {
31031static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgType *instruction,
31032 bool allow_var)
31033{
3098031034 IrInstGen *fn_type_inst = instruction->fn_type->child;
3098131035 ZigType *fn_type = ir_resolve_type(ira, fn_type_inst);
3098231036 if (type_is_invalid(fn_type))
......@@ -30998,7 +31052,7 @@ static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgTy
3099831052
3099931053 FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
3100031054 if (arg_index >= fn_type_id->param_count) {
31001 if (instruction->allow_var) {
31055 if (allow_var) {
3100231056 // TODO remove this with var args
3100331057 return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_anytype);
3100431058 }
......@@ -31013,7 +31067,7 @@ static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgTy
3101331067 // Args are only unresolved if our function is generic.
3101431068 ir_assert(fn_type->data.fn.is_generic, &instruction->base.base);
3101531069
31016 if (instruction->allow_var) {
31070 if (allow_var) {
3101731071 return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_anytype);
3101831072 } else {
3101931073 ir_add_error(ira, &arg_index_inst->base,
......@@ -32341,8 +32395,10 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc
3234132395 return ir_analyze_instruction_fn_proto(ira, (IrInstSrcFnProto *)instruction);
3234232396 case IrInstSrcIdTestComptime:
3234332397 return ir_analyze_instruction_test_comptime(ira, (IrInstSrcTestComptime *)instruction);
32344 case IrInstSrcIdCheckSwitchProngs:
32345 return ir_analyze_instruction_check_switch_prongs(ira, (IrInstSrcCheckSwitchProngs *)instruction);
32398 case IrInstSrcIdCheckSwitchProngsUnderNo:
32399 return ir_analyze_instruction_check_switch_prongs(ira, (IrInstSrcCheckSwitchProngs *)instruction, false);
32400 case IrInstSrcIdCheckSwitchProngsUnderYes:
32401 return ir_analyze_instruction_check_switch_prongs(ira, (IrInstSrcCheckSwitchProngs *)instruction, true);
3234632402 case IrInstSrcIdCheckStatementIsVoid:
3234732403 return ir_analyze_instruction_check_statement_is_void(ira, (IrInstSrcCheckStatementIsVoid *)instruction);
3234832404 case IrInstSrcIdDeclRef:
......@@ -32373,6 +32429,10 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc
3237332429 return ir_analyze_instruction_set_eval_branch_quota(ira, (IrInstSrcSetEvalBranchQuota *)instruction);
3237432430 case IrInstSrcIdPtrType:
3237532431 return ir_analyze_instruction_ptr_type(ira, (IrInstSrcPtrType *)instruction);
32432 case IrInstSrcIdPtrTypeSimple:
32433 return ir_analyze_instruction_ptr_type_simple(ira, (IrInstSrcPtrTypeSimple *)instruction, false);
32434 case IrInstSrcIdPtrTypeSimpleConst:
32435 return ir_analyze_instruction_ptr_type_simple(ira, (IrInstSrcPtrTypeSimple *)instruction, true);
3237632436 case IrInstSrcIdAlignCast:
3237732437 return ir_analyze_instruction_align_cast(ira, (IrInstSrcAlignCast *)instruction);
3237832438 case IrInstSrcIdImplicitCast:
......@@ -32383,8 +32443,10 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc
3238332443 return ir_analyze_instruction_reset_result(ira, (IrInstSrcResetResult *)instruction);
3238432444 case IrInstSrcIdSetAlignStack:
3238532445 return ir_analyze_instruction_set_align_stack(ira, (IrInstSrcSetAlignStack *)instruction);
32386 case IrInstSrcIdArgType:
32387 return ir_analyze_instruction_arg_type(ira, (IrInstSrcArgType *)instruction);
32446 case IrInstSrcIdArgTypeAllowVarFalse:
32447 return ir_analyze_instruction_arg_type(ira, (IrInstSrcArgType *)instruction, false);
32448 case IrInstSrcIdArgTypeAllowVarTrue:
32449 return ir_analyze_instruction_arg_type(ira, (IrInstSrcArgType *)instruction, true);
3238832450 case IrInstSrcIdExport:
3238932451 return ir_analyze_instruction_export(ira, (IrInstSrcExport *)instruction);
3239032452 case IrInstSrcIdExtern:
......@@ -32737,12 +32799,15 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) {
3273732799 case IrInstSrcIdMemcpy:
3273832800 case IrInstSrcIdBreakpoint:
3273932801 case IrInstSrcIdOverflowOp: // TODO when we support multiple returns this can be side effect free
32740 case IrInstSrcIdCheckSwitchProngs:
32802 case IrInstSrcIdCheckSwitchProngsUnderNo:
32803 case IrInstSrcIdCheckSwitchProngsUnderYes:
3274132804 case IrInstSrcIdCheckStatementIsVoid:
3274232805 case IrInstSrcIdCheckRuntimeScope:
3274332806 case IrInstSrcIdPanic:
3274432807 case IrInstSrcIdSetEvalBranchQuota:
3274532808 case IrInstSrcIdPtrType:
32809 case IrInstSrcIdPtrTypeSimple:
32810 case IrInstSrcIdPtrTypeSimpleConst:
3274632811 case IrInstSrcIdSetAlignStack:
3274732812 case IrInstSrcIdExport:
3274832813 case IrInstSrcIdExtern:
......@@ -32826,7 +32891,8 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) {
3282632891 case IrInstSrcIdAlignCast:
3282732892 case IrInstSrcIdImplicitCast:
3282832893 case IrInstSrcIdResolveResult:
32829 case IrInstSrcIdArgType:
32894 case IrInstSrcIdArgTypeAllowVarFalse:
32895 case IrInstSrcIdArgTypeAllowVarTrue:
3283032896 case IrInstSrcIdErrorReturnTrace:
3283132897 case IrInstSrcIdErrorUnion:
3283232898 case IrInstSrcIdFloatOp:
......@@ -33249,6 +33315,54 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) {
3324933315 // We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
3325033316 return ErrorNone;
3325133317 }
33318 case LazyValueIdPtrTypeSimple: {
33319 LazyValuePtrTypeSimple *lazy_ptr_type = reinterpret_cast<LazyValuePtrTypeSimple *>(val->data.x_lazy);
33320 IrAnalyze *ira = lazy_ptr_type->ira;
33321
33322 ZigType *elem_type = ir_resolve_type(ira, lazy_ptr_type->elem_type);
33323 if (type_is_invalid(elem_type))
33324 return ErrorSemanticAnalyzeFail;
33325
33326 if (elem_type->id == ZigTypeIdUnreachable) {
33327 ir_add_error(ira, &lazy_ptr_type->elem_type->base,
33328 buf_create_from_str("pointer to noreturn not allowed"));
33329 return ErrorSemanticAnalyzeFail;
33330 }
33331
33332 assert(val->type->id == ZigTypeIdMetaType);
33333 val->data.x_type = get_pointer_to_type_extra2(ira->codegen, elem_type,
33334 false, false, PtrLenSingle, 0,
33335 0, 0,
33336 false, VECTOR_INDEX_NONE, nullptr, nullptr);
33337 val->special = ConstValSpecialStatic;
33338
33339 // We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
33340 return ErrorNone;
33341 }
33342 case LazyValueIdPtrTypeSimpleConst: {
33343 LazyValuePtrTypeSimple *lazy_ptr_type = reinterpret_cast<LazyValuePtrTypeSimple *>(val->data.x_lazy);
33344 IrAnalyze *ira = lazy_ptr_type->ira;
33345
33346 ZigType *elem_type = ir_resolve_type(ira, lazy_ptr_type->elem_type);
33347 if (type_is_invalid(elem_type))
33348 return ErrorSemanticAnalyzeFail;
33349
33350 if (elem_type->id == ZigTypeIdUnreachable) {
33351 ir_add_error(ira, &lazy_ptr_type->elem_type->base,
33352 buf_create_from_str("pointer to noreturn not allowed"));
33353 return ErrorSemanticAnalyzeFail;
33354 }
33355
33356 assert(val->type->id == ZigTypeIdMetaType);
33357 val->data.x_type = get_pointer_to_type_extra2(ira->codegen, elem_type,
33358 true, false, PtrLenSingle, 0,
33359 0, 0,
33360 false, VECTOR_INDEX_NONE, nullptr, nullptr);
33361 val->special = ConstValSpecialStatic;
33362
33363 // We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
33364 return ErrorNone;
33365 }
3325233366 case LazyValueIdArrayType: {
3325333367 LazyValueArrayType *lazy_array_type = reinterpret_cast<LazyValueArrayType *>(val->data.x_lazy);
3325433368 IrAnalyze *ira = lazy_array_type->ira;
src/stage1/ir_print.cpp+49-10
......@@ -270,8 +270,10 @@ const char* ir_inst_src_type_str(IrInstSrcId id) {
270270 return "SrcIntToErr";
271271 case IrInstSrcIdErrToInt:
272272 return "SrcErrToInt";
273 case IrInstSrcIdCheckSwitchProngs:
274 return "SrcCheckSwitchProngs";
273 case IrInstSrcIdCheckSwitchProngsUnderNo:
274 return "SrcCheckSwitchProngsUnderNo";
275 case IrInstSrcIdCheckSwitchProngsUnderYes:
276 return "SrcCheckSwitchProngsUnderYes";
275277 case IrInstSrcIdCheckStatementIsVoid:
276278 return "SrcCheckStatementIsVoid";
277279 case IrInstSrcIdTypeName:
......@@ -298,6 +300,10 @@ const char* ir_inst_src_type_str(IrInstSrcId id) {
298300 return "SrcSetEvalBranchQuota";
299301 case IrInstSrcIdPtrType:
300302 return "SrcPtrType";
303 case IrInstSrcIdPtrTypeSimple:
304 return "SrcPtrTypeSimple";
305 case IrInstSrcIdPtrTypeSimpleConst:
306 return "SrcPtrTypeSimpleConst";
301307 case IrInstSrcIdAlignCast:
302308 return "SrcAlignCast";
303309 case IrInstSrcIdImplicitCast:
......@@ -308,8 +314,10 @@ const char* ir_inst_src_type_str(IrInstSrcId id) {
308314 return "SrcResetResult";
309315 case IrInstSrcIdSetAlignStack:
310316 return "SrcSetAlignStack";
311 case IrInstSrcIdArgType:
312 return "SrcArgType";
317 case IrInstSrcIdArgTypeAllowVarFalse:
318 return "SrcArgTypeAllowVarFalse";
319 case IrInstSrcIdArgTypeAllowVarTrue:
320 return "SrcArgTypeAllowVarTrue";
313321 case IrInstSrcIdExport:
314322 return "SrcExport";
315323 case IrInstSrcIdExtern:
......@@ -2187,7 +2195,9 @@ static void ir_print_err_to_int(IrPrintGen *irp, IrInstGenErrToInt *instruction)
21872195 ir_print_other_inst_gen(irp, instruction->target);
21882196}
21892197
2190static void ir_print_check_switch_prongs(IrPrintSrc *irp, IrInstSrcCheckSwitchProngs *instruction) {
2198static void ir_print_check_switch_prongs(IrPrintSrc *irp, IrInstSrcCheckSwitchProngs *instruction,
2199 bool have_underscore_prong)
2200{
21912201 fprintf(irp->f, "@checkSwitchProngs(");
21922202 ir_print_other_inst_src(irp, instruction->target_value);
21932203 fprintf(irp->f, ",");
......@@ -2200,6 +2210,8 @@ static void ir_print_check_switch_prongs(IrPrintSrc *irp, IrInstSrcCheckSwitchPr
22002210 }
22012211 const char *have_else_str = instruction->else_prong != nullptr ? "yes" : "no";
22022212 fprintf(irp->f, ")else:%s", have_else_str);
2213 const char *have_under_str = have_underscore_prong ? "yes" : "no";
2214 fprintf(irp->f, " _:%s", have_under_str);
22032215}
22042216
22052217static void ir_print_check_statement_is_void(IrPrintSrc *irp, IrInstSrcCheckStatementIsVoid *instruction) {
......@@ -2237,6 +2249,15 @@ static void ir_print_ptr_type(IrPrintSrc *irp, IrInstSrcPtrType *instruction) {
22372249 ir_print_other_inst_src(irp, instruction->child_type);
22382250}
22392251
2252static void ir_print_ptr_type_simple(IrPrintSrc *irp, IrInstSrcPtrTypeSimple *instruction,
2253 bool is_const)
2254{
2255 fprintf(irp->f, "&");
2256 const char *const_str = is_const ? "const " : "";
2257 fprintf(irp->f, "*%s", const_str);
2258 ir_print_other_inst_src(irp, instruction->child_type);
2259}
2260
22402261static void ir_print_decl_ref(IrPrintSrc *irp, IrInstSrcDeclRef *instruction) {
22412262 const char *ptr_str = (instruction->lval != LValNone) ? "ptr " : "";
22422263 fprintf(irp->f, "declref %s%s", ptr_str, buf_ptr(instruction->tld->name));
......@@ -2344,11 +2365,17 @@ static void ir_print_set_align_stack(IrPrintSrc *irp, IrInstSrcSetAlignStack *in
23442365 fprintf(irp->f, ")");
23452366}
23462367
2347static void ir_print_arg_type(IrPrintSrc *irp, IrInstSrcArgType *instruction) {
2368static void ir_print_arg_type(IrPrintSrc *irp, IrInstSrcArgType *instruction, bool allow_var) {
23482369 fprintf(irp->f, "@ArgType(");
23492370 ir_print_other_inst_src(irp, instruction->fn_type);
23502371 fprintf(irp->f, ",");
23512372 ir_print_other_inst_src(irp, instruction->arg_index);
2373 fprintf(irp->f, ",");
2374 if (allow_var) {
2375 fprintf(irp->f, "allow_var=true");
2376 } else {
2377 fprintf(irp->f, "allow_var=false");
2378 }
23522379 fprintf(irp->f, ")");
23532380}
23542381
......@@ -2885,8 +2912,11 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai
28852912 case IrInstSrcIdErrToInt:
28862913 ir_print_err_to_int(irp, (IrInstSrcErrToInt *)instruction);
28872914 break;
2888 case IrInstSrcIdCheckSwitchProngs:
2889 ir_print_check_switch_prongs(irp, (IrInstSrcCheckSwitchProngs *)instruction);
2915 case IrInstSrcIdCheckSwitchProngsUnderNo:
2916 ir_print_check_switch_prongs(irp, (IrInstSrcCheckSwitchProngs *)instruction, false);
2917 break;
2918 case IrInstSrcIdCheckSwitchProngsUnderYes:
2919 ir_print_check_switch_prongs(irp, (IrInstSrcCheckSwitchProngs *)instruction, true);
28902920 break;
28912921 case IrInstSrcIdCheckStatementIsVoid:
28922922 ir_print_check_statement_is_void(irp, (IrInstSrcCheckStatementIsVoid *)instruction);
......@@ -2900,6 +2930,12 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai
29002930 case IrInstSrcIdPtrType:
29012931 ir_print_ptr_type(irp, (IrInstSrcPtrType *)instruction);
29022932 break;
2933 case IrInstSrcIdPtrTypeSimple:
2934 ir_print_ptr_type_simple(irp, (IrInstSrcPtrTypeSimple *)instruction, false);
2935 break;
2936 case IrInstSrcIdPtrTypeSimpleConst:
2937 ir_print_ptr_type_simple(irp, (IrInstSrcPtrTypeSimple *)instruction, true);
2938 break;
29032939 case IrInstSrcIdDeclRef:
29042940 ir_print_decl_ref(irp, (IrInstSrcDeclRef *)instruction);
29052941 break;
......@@ -2942,8 +2978,11 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai
29422978 case IrInstSrcIdSetAlignStack:
29432979 ir_print_set_align_stack(irp, (IrInstSrcSetAlignStack *)instruction);
29442980 break;
2945 case IrInstSrcIdArgType:
2946 ir_print_arg_type(irp, (IrInstSrcArgType *)instruction);
2981 case IrInstSrcIdArgTypeAllowVarFalse:
2982 ir_print_arg_type(irp, (IrInstSrcArgType *)instruction, false);
2983 break;
2984 case IrInstSrcIdArgTypeAllowVarTrue:
2985 ir_print_arg_type(irp, (IrInstSrcArgType *)instruction, true);
29472986 break;
29482987 case IrInstSrcIdExport:
29492988 ir_print_export(irp, (IrInstSrcExport *)instruction);
src/translate_c.zig+254-115
......@@ -11,6 +11,7 @@ const math = std.math;
1111const ast = @import("translate_c/ast.zig");
1212const Node = ast.Node;
1313const Tag = Node.Tag;
14const c_builtins = std.c.builtins;
1415
1516const CallingConvention = std.builtin.CallingConvention;
1617
......@@ -269,7 +270,10 @@ pub const Context = struct {
269270 global_scope: *Scope.Root,
270271 clang_context: *clang.ASTContext,
271272 mangle_count: u32 = 0,
273 /// Table of record decls that have been demoted to opaques.
272274 opaque_demotes: std.AutoHashMapUnmanaged(usize, void) = .{},
275 /// Table of unnamed enums and records that are child types of typedefs.
276 unnamed_typedefs: std.AutoHashMapUnmanaged(usize, []const u8) = .{},
273277
274278 /// This one is different than the root scope's name table. This contains
275279 /// a list of names that we found by visiting all the top level decls without
......@@ -337,6 +341,7 @@ pub fn translate(
337341 context.alias_list.deinit();
338342 context.global_names.deinit(gpa);
339343 context.opaque_demotes.deinit(gpa);
344 context.unnamed_typedefs.deinit(gpa);
340345 context.global_scope.deinit();
341346 }
342347
......@@ -400,6 +405,51 @@ fn declVisitorNamesOnly(c: *Context, decl: *const clang.Decl) Error!void {
400405 if (decl.castToNamedDecl()) |named_decl| {
401406 const decl_name = try c.str(named_decl.getName_bytes_begin());
402407 try c.global_names.put(c.gpa, decl_name, {});
408
409 // Check for typedefs with unnamed enum/record child types.
410 if (decl.getKind() == .Typedef) {
411 const typedef_decl = @ptrCast(*const clang.TypedefNameDecl, decl);
412 var child_ty = typedef_decl.getUnderlyingType().getTypePtr();
413 const addr: usize = while (true) switch (child_ty.getTypeClass()) {
414 .Enum => {
415 const enum_ty = @ptrCast(*const clang.EnumType, child_ty);
416 const enum_decl = enum_ty.getDecl();
417 // check if this decl is unnamed
418 if (@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin()[0] != 0) return;
419 break @ptrToInt(enum_decl.getCanonicalDecl());
420 },
421 .Record => {
422 const record_ty = @ptrCast(*const clang.RecordType, child_ty);
423 const record_decl = record_ty.getDecl();
424 // check if this decl is unnamed
425 if (@ptrCast(*const clang.NamedDecl, record_decl).getName_bytes_begin()[0] != 0) return;
426 break @ptrToInt(record_decl.getCanonicalDecl());
427 },
428 .Elaborated => {
429 const elaborated_ty = @ptrCast(*const clang.ElaboratedType, child_ty);
430 child_ty = elaborated_ty.getNamedType().getTypePtr();
431 },
432 .Decayed => {
433 const decayed_ty = @ptrCast(*const clang.DecayedType, child_ty);
434 child_ty = decayed_ty.getDecayedType().getTypePtr();
435 },
436 .Attributed => {
437 const attributed_ty = @ptrCast(*const clang.AttributedType, child_ty);
438 child_ty = attributed_ty.getEquivalentType().getTypePtr();
439 },
440 .MacroQualified => {
441 const macroqualified_ty = @ptrCast(*const clang.MacroQualifiedType, child_ty);
442 child_ty = macroqualified_ty.getModifiedType().getTypePtr();
443 },
444 else => return,
445 } else unreachable;
446 // TODO https://github.com/ziglang/zig/issues/3756
447 // TODO https://github.com/ziglang/zig/issues/1802
448 const name = if (isZigPrimitiveType(decl_name)) try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ decl_name, c.getMangle() }) else decl_name;
449 try c.unnamed_typedefs.putNoClobber(c.gpa, addr, name);
450 // Put this typedef in the decl_table to avoid redefinitions.
451 try c.decl_table.putNoClobber(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), name);
452 }
403453 }
404454}
405455
......@@ -635,7 +685,7 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co
635685 if (has_init) trans_init: {
636686 if (decl_init) |expr| {
637687 const node_or_error = if (expr.getStmtClass() == .StringLiteralClass)
638 transStringLiteralAsArray(c, scope, @ptrCast(*const clang.StringLiteral, expr), zigArraySize(c, type_node) catch 0)
688 transStringLiteralInitializer(c, scope, @ptrCast(*const clang.StringLiteral, expr), type_node)
639689 else
640690 transExprCoercing(c, scope, expr, .used);
641691 init_node = node_or_error catch |err| switch (err) {
......@@ -751,17 +801,10 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD
751801 const toplevel = scope.id == .root;
752802 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined;
753803
754 var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, record_decl).getName_bytes_begin());
755 var is_unnamed = false;
756 // Record declarations such as `struct {...} x` have no name but they're not
757 // anonymous hence here isAnonymousStructOrUnion is not needed
758 if (bare_name.len == 0) {
759 bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()});
760 is_unnamed = true;
761 }
762
763 var container_kind_name: []const u8 = undefined;
764804 var is_union = false;
805 var container_kind_name: []const u8 = undefined;
806 var bare_name: []const u8 = try c.str(@ptrCast(*const clang.NamedDecl, record_decl).getName_bytes_begin());
807
765808 if (record_decl.isUnion()) {
766809 container_kind_name = "union";
767810 is_union = true;
......@@ -772,7 +815,20 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD
772815 return failDecl(c, record_loc, bare_name, "record {s} is not a struct or union", .{bare_name});
773816 }
774817
775 var name: []const u8 = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_kind_name, bare_name });
818 var is_unnamed = false;
819 var name = bare_name;
820 if (c.unnamed_typedefs.get(@ptrToInt(record_decl.getCanonicalDecl()))) |typedef_name| {
821 bare_name = typedef_name;
822 name = typedef_name;
823 } else {
824 // Record declarations such as `struct {...} x` have no name but they're not
825 // anonymous hence here isAnonymousStructOrUnion is not needed
826 if (bare_name.len == 0) {
827 bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()});
828 is_unnamed = true;
829 }
830 name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_kind_name, bare_name });
831 }
776832 if (!toplevel) name = try bs.makeMangledName(c, name);
777833 try c.decl_table.putNoClobber(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), name);
778834
......@@ -873,14 +929,19 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: *const clang.EnumDecl) E
873929 const toplevel = scope.id == .root;
874930 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined;
875931
876 var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin());
877932 var is_unnamed = false;
878 if (bare_name.len == 0) {
879 bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()});
880 is_unnamed = true;
933 var bare_name: []const u8 = try c.str(@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin());
934 var name = bare_name;
935 if (c.unnamed_typedefs.get(@ptrToInt(enum_decl.getCanonicalDecl()))) |typedef_name| {
936 bare_name = typedef_name;
937 name = typedef_name;
938 } else {
939 if (bare_name.len == 0) {
940 bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()});
941 is_unnamed = true;
942 }
943 name = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name});
881944 }
882
883 var name: []const u8 = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name});
884945 if (!toplevel) _ = try bs.makeMangledName(c, name);
885946 try c.decl_table.putNoClobber(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), name);
886947
......@@ -1058,6 +1119,11 @@ fn transStmt(
10581119 const compound_literal = @ptrCast(*const clang.CompoundLiteralExpr, stmt);
10591120 return transExpr(c, scope, compound_literal.getInitializer(), result_used);
10601121 },
1122 .GenericSelectionExprClass => {
1123 const gen_sel = @ptrCast(*const clang.GenericSelectionExpr, stmt);
1124 return transExpr(c, scope, gen_sel.getResultExpr(), result_used);
1125 },
1126 // When adding new cases here, see comment for maybeBlockify()
10611127 else => {
10621128 return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO implement translation of stmt class {s}", .{@tagName(sc)});
10631129 },
......@@ -1407,7 +1473,7 @@ fn transDeclStmtOne(
14071473
14081474 var init_node = if (decl_init) |expr|
14091475 if (expr.getStmtClass() == .StringLiteralClass)
1410 try transStringLiteralAsArray(c, scope, @ptrCast(*const clang.StringLiteral, expr), try zigArraySize(c, type_node))
1476 try transStringLiteralInitializer(c, scope, @ptrCast(*const clang.StringLiteral, expr), type_node)
14111477 else
14121478 try transExprCoercing(c, scope, expr, .used)
14131479 else
......@@ -1522,7 +1588,7 @@ fn transImplicitCastExpr(
15221588 return maybeSuppressResult(c, scope, result_used, ne);
15231589 },
15241590 .BuiltinFnToFnPtr => {
1525 return transExpr(c, scope, sub_expr, result_used);
1591 return transBuiltinFnExpr(c, scope, sub_expr, result_used);
15261592 },
15271593 .ToVoid => {
15281594 // Should only appear in the rhs and lhs of a ConditionalOperator
......@@ -1538,6 +1604,22 @@ fn transImplicitCastExpr(
15381604 }
15391605}
15401606
1607fn isBuiltinDefined(name: []const u8) bool {
1608 inline for (std.meta.declarations(c_builtins)) |decl| {
1609 if (std.mem.eql(u8, name, decl.name)) return true;
1610 }
1611 return false;
1612}
1613
1614fn transBuiltinFnExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!Node {
1615 const node = try transExpr(c, scope, expr, used);
1616 if (node.castTag(.identifier)) |ident| {
1617 const name = ident.data;
1618 if (!isBuiltinDefined(name)) return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "TODO implement function '{s}' in std.c.builtins", .{name});
1619 }
1620 return node;
1621}
1622
15411623fn transBoolExpr(
15421624 c: *Context,
15431625 scope: *Scope,
......@@ -1582,6 +1664,10 @@ fn exprIsNarrowStringLiteral(expr: *const clang.Expr) bool {
15821664 const op_expr = @ptrCast(*const clang.ParenExpr, expr).getSubExpr();
15831665 return exprIsNarrowStringLiteral(op_expr);
15841666 },
1667 .GenericSelectionExprClass => {
1668 const gen_sel = @ptrCast(*const clang.GenericSelectionExpr, expr);
1669 return exprIsNarrowStringLiteral(gen_sel.getResultExpr());
1670 },
15851671 else => return false,
15861672 }
15871673}
......@@ -1733,6 +1819,20 @@ fn transReturnStmt(
17331819 return Tag.@"return".create(c.arena, rhs);
17341820}
17351821
1822fn transNarrowStringLiteral(
1823 c: *Context,
1824 scope: *Scope,
1825 stmt: *const clang.StringLiteral,
1826 result_used: ResultUsed,
1827) TransError!Node {
1828 var len: usize = undefined;
1829 const bytes_ptr = stmt.getString_bytes_begin_size(&len);
1830
1831 const str = try std.fmt.allocPrint(c.arena, "\"{}\"", .{std.zig.fmtEscapes(bytes_ptr[0..len])});
1832 const node = try Tag.string_literal.create(c.arena, str);
1833 return maybeSuppressResult(c, scope, result_used, node);
1834}
1835
17361836fn transStringLiteral(
17371837 c: *Context,
17381838 scope: *Scope,
......@@ -1741,19 +1841,14 @@ fn transStringLiteral(
17411841) TransError!Node {
17421842 const kind = stmt.getKind();
17431843 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 },
1844 .Ascii, .UTF8 => return transNarrowStringLiteral(c, scope, stmt, result_used),
17521845 .UTF16, .UTF32, .Wide => {
17531846 const str_type = @tagName(stmt.getKind());
17541847 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);
17561848
1849 const expr_base = @ptrCast(*const clang.Expr, stmt);
1850 const array_type = try transQualTypeInitialized(c, scope, expr_base.getType(), expr_base, expr_base.getBeginLoc());
1851 const lit_array = try transStringLiteralInitializer(c, scope, stmt, array_type);
17571852 const decl = try Tag.var_simple.create(c.arena, .{ .name = name, .init = lit_array });
17581853 try scope.appendNode(decl);
17591854 const node = try Tag.identifier.create(c.arena, name);
......@@ -1762,52 +1857,67 @@ fn transStringLiteral(
17621857 }
17631858}
17641859
1765/// Parse the size of an array back out from an ast Node.
1766fn zigArraySize(c: *Context, node: Node) TransError!usize {
1767 if (node.castTag(.array_type)) |array| {
1768 return array.data.len;
1769 }
1770 return error.UnsupportedTranslation;
1860fn getArrayPayload(array_type: Node) ast.Payload.Array.ArrayTypeInfo {
1861 return (array_type.castTag(.array_type) orelse array_type.castTag(.null_sentinel_array_type).?).data;
17711862}
17721863
1773/// Translate a string literal to an array of integers. Used when an
1774/// array is initialized from a string literal. `array_size` is the
1775/// size of the array being initialized. If the string literal is larger
1776/// than the array, truncate the string. If the array is larger than the
1777/// string literal, pad the array with 0's
1778fn transStringLiteralAsArray(
1864/// Translate a string literal that is initializing an array. In general narrow string
1865/// literals become `"<string>".*` or `"<string>"[0..<size>].*` if they need truncation.
1866/// Wide string literals become an array of integers. zero-fillers pad out the array to
1867/// the appropriate length, if necessary.
1868fn transStringLiteralInitializer(
17791869 c: *Context,
17801870 scope: *Scope,
17811871 stmt: *const clang.StringLiteral,
1782 array_size: usize,
1872 array_type: Node,
17831873) TransError!Node {
1784 if (array_size == 0) return error.UnsupportedType;
1874 assert(array_type.tag() == .array_type or array_type.tag() == .null_sentinel_array_type);
1875
1876 const is_narrow = stmt.getKind() == .Ascii or stmt.getKind() == .UTF8;
17851877
17861878 const str_length = stmt.getLength();
1879 const payload = getArrayPayload(array_type);
1880 const array_size = payload.len;
1881 const elem_type = payload.elem_type;
1882
1883 if (array_size == 0) return Tag.empty_array.create(c.arena, elem_type);
1884
1885 const num_inits = math.min(str_length, array_size);
1886 const init_node = if (num_inits > 0) blk: {
1887 if (is_narrow) {
1888 // "string literal".* or string literal"[0..num_inits].*
1889 var str = try transNarrowStringLiteral(c, scope, stmt, .used);
1890 if (str_length != array_size) str = try Tag.string_slice.create(c.arena, .{ .string = str, .end = num_inits });
1891 break :blk try Tag.deref.create(c.arena, str);
1892 } else {
1893 const init_list = try c.arena.alloc(Node, num_inits);
1894 var i: c_uint = 0;
1895 while (i < num_inits) : (i += 1) {
1896 init_list[i] = try transCreateCharLitNode(c, false, stmt.getCodeUnit(i));
1897 }
1898 const init_args = .{ .len = num_inits, .elem_type = elem_type };
1899 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);
1900 break :blk try Tag.array_init.create(c.arena, .{
1901 .cond = init_array_type,
1902 .cases = init_list,
1903 });
1904 }
1905 } else null;
17871906
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);
1907 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
1908 assert(array_size > str_length); // If array_size <= str_length, `num_inits == array_size` and we've already returned.
17911909
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);
1910 const filler_node = try Tag.array_filler.create(c.arena, .{
1911 .type = elem_type,
1912 .filler = Tag.zero_literal.init(),
1913 .count = array_size - str_length,
1914 });
17951915
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);
1916 if (init_node) |some| {
1917 return Tag.array_cat.create(c.arena, .{ .lhs = some, .rhs = filler_node });
1918 } else {
1919 return filler_node;
18051920 }
1806
1807 return Tag.array_init.create(c.arena, .{
1808 .cond = arr_type,
1809 .cases = init_list,
1810 });
18111921}
18121922
18131923/// determine whether `stmt` is a "pointer subtraction expression" - a subtraction where
......@@ -1836,6 +1946,7 @@ fn cIntTypeForEnum(enum_qt: clang.QualType) clang.QualType {
18361946 return enum_decl.getIntegerType();
18371947}
18381948
1949// when modifying this function, make sure to also update std.meta.cast
18391950fn transCCast(
18401951 c: *Context,
18411952 scope: *Scope,
......@@ -2192,6 +2303,35 @@ fn transImplicitValueInitExpr(
21922303 return transZeroInitExpr(c, scope, source_loc, ty);
21932304}
21942305
2306/// If a statement can possibly translate to a Zig assignment (either directly because it's
2307/// an assignment in C or indirectly via result assignment to `_`) AND it's the sole statement
2308/// in the body of an if statement or loop, then we need to put the statement into its own block.
2309/// The `else` case here corresponds to statements that could result in an assignment. If a statement
2310/// class never needs a block, add its enum to the top prong.
2311fn maybeBlockify(c: *Context, scope: *Scope, stmt: *const clang.Stmt) TransError!Node {
2312 switch (stmt.getStmtClass()) {
2313 .BreakStmtClass,
2314 .CompoundStmtClass,
2315 .ContinueStmtClass,
2316 .DeclRefExprClass,
2317 .DeclStmtClass,
2318 .DoStmtClass,
2319 .ForStmtClass,
2320 .IfStmtClass,
2321 .ReturnStmtClass,
2322 .NullStmtClass,
2323 .WhileStmtClass,
2324 => return transStmt(c, scope, stmt, .unused),
2325 else => {
2326 var block_scope = try Scope.Block.init(c, scope, false);
2327 defer block_scope.deinit();
2328 const result = try transStmt(c, &block_scope.base, stmt, .unused);
2329 try block_scope.statements.append(result);
2330 return block_scope.complete(c);
2331 },
2332 }
2333}
2334
21952335fn transIfStmt(
21962336 c: *Context,
21972337 scope: *Scope,
......@@ -2209,9 +2349,10 @@ fn transIfStmt(
22092349 const cond_expr = @ptrCast(*const clang.Expr, stmt.getCond());
22102350 const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used);
22112351
2212 const then_body = try transStmt(c, scope, stmt.getThen(), .unused);
2352 const then_body = try maybeBlockify(c, scope, stmt.getThen());
2353
22132354 const else_body = if (stmt.getElse()) |expr|
2214 try transStmt(c, scope, expr, .unused)
2355 try maybeBlockify(c, scope, expr)
22152356 else
22162357 null;
22172358 return Tag.@"if".create(c.arena, .{ .cond = cond, .then = then_body, .@"else" = else_body });
......@@ -2236,7 +2377,7 @@ fn transWhileLoop(
22362377 .parent = scope,
22372378 .id = .loop,
22382379 };
2239 const body = try transStmt(c, &loop_scope, stmt.getBody(), .unused);
2380 const body = try maybeBlockify(c, &loop_scope, stmt.getBody());
22402381 return Tag.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = null });
22412382}
22422383
......@@ -2262,7 +2403,7 @@ fn transDoWhileLoop(
22622403 const if_not_break = switch (cond.tag()) {
22632404 .false_literal => return transStmt(c, scope, stmt.getBody(), .unused),
22642405 .true_literal => {
2265 const body_node = try transStmt(c, scope, stmt.getBody(), .unused);
2406 const body_node = try maybeBlockify(c, scope, stmt.getBody());
22662407 return Tag.while_true.create(c.arena, body_node);
22672408 },
22682409 else => try Tag.if_not_break.create(c.arena, cond),
......@@ -2338,7 +2479,7 @@ fn transForLoop(
23382479 else
23392480 null;
23402481
2341 const body = try transStmt(c, &loop_scope, stmt.getBody(), .unused);
2482 const body = try maybeBlockify(c, &loop_scope, stmt.getBody());
23422483 const while_node = try Tag.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = cont_expr });
23432484 if (block_scope) |*bs| {
23442485 try bs.statements.append(while_node);
......@@ -2725,6 +2866,10 @@ fn cIsFunctionDeclRef(expr: *const clang.Expr) bool {
27252866 const opcode = un_op.getOpcode();
27262867 return (opcode == .AddrOf or opcode == .Deref) and cIsFunctionDeclRef(un_op.getSubExpr());
27272868 },
2869 .GenericSelectionExprClass => {
2870 const gen_sel = @ptrCast(*const clang.GenericSelectionExpr, expr);
2871 return cIsFunctionDeclRef(gen_sel.getResultExpr());
2872 },
27282873 else => return false,
27292874 }
27302875}
......@@ -3052,43 +3197,34 @@ fn transCreateCompoundAssign(
30523197 const requires_int_cast = blk: {
30533198 const are_integers = cIsInteger(lhs_qt) and cIsInteger(rhs_qt);
30543199 const are_same_sign = cIsSignedInteger(lhs_qt) == cIsSignedInteger(rhs_qt);
3055 break :blk are_integers and !are_same_sign;
3200 break :blk are_integers and !(are_same_sign and cIntTypeCmp(lhs_qt, rhs_qt) == .eq);
30563201 };
3202
30573203 if (used == .unused) {
30583204 // common case
30593205 // c: lhs += rhs
30603206 // zig: lhs += rhs
3207 const lhs_node = try transExpr(c, scope, lhs, .used);
3208 var rhs_node = try transExpr(c, scope, rhs, .used);
3209 if (is_ptr_op_signed) rhs_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node);
3210
30613211 if ((is_mod or is_div) and is_signed) {
3062 const lhs_node = try transExpr(c, scope, lhs, .used);
3063 const rhs_node = try transExpr(c, scope, rhs, .used);
3212 if (requires_int_cast) rhs_node = try transCCast(c, scope, loc, lhs_qt, rhs_qt, rhs_node);
3213 const operands = .{ .lhs = lhs_node, .rhs = rhs_node };
30643214 const builtin = if (is_mod)
3065 try Tag.rem.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node })
3215 try Tag.rem.create(c.arena, operands)
30663216 else
3067 try Tag.div_trunc.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node });
3217 try Tag.div_trunc.create(c.arena, operands);
30683218
30693219 return transCreateNodeInfixOp(c, scope, .assign, lhs_node, builtin, .used);
30703220 }
30713221
3072 const lhs_node = try transExpr(c, scope, lhs, .used);
3073 var rhs_node = if (is_shift or requires_int_cast)
3074 try transExprCoercing(c, scope, rhs, .used)
3075 else
3076 try transExpr(c, scope, rhs, .used);
3077
3078 if (is_ptr_op_signed) {
3079 rhs_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node);
3080 }
3081
3082 if (is_shift or requires_int_cast) {
3083 // @intCast(rhs)
3084 const cast_to_type = if (is_shift)
3085 try qualTypeToLog2IntRef(c, scope, getExprQualType(c, rhs), loc)
3086 else
3087 try transQualType(c, scope, getExprQualType(c, lhs), loc);
3088
3222 if (is_shift) {
3223 const cast_to_type = try qualTypeToLog2IntRef(c, scope, rhs_qt, loc);
30893224 rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node });
3225 } else if (requires_int_cast) {
3226 rhs_node = try transCCast(c, scope, loc, lhs_qt, rhs_qt, rhs_node);
30903227 }
3091
30923228 return transCreateNodeInfixOp(c, scope, op, lhs_node, rhs_node, .used);
30933229 }
30943230 // worst case
......@@ -3110,29 +3246,24 @@ fn transCreateCompoundAssign(
31103246 const lhs_node = try Tag.identifier.create(c.arena, ref);
31113247 const ref_node = try Tag.deref.create(c.arena, lhs_node);
31123248
3249 var rhs_node = try transExpr(c, &block_scope.base, rhs, .used);
3250 if (is_ptr_op_signed) rhs_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node);
31133251 if ((is_mod or is_div) and is_signed) {
3114 const rhs_node = try transExpr(c, &block_scope.base, rhs, .used);
3252 if (requires_int_cast) rhs_node = try transCCast(c, scope, loc, lhs_qt, rhs_qt, rhs_node);
3253 const operands = .{ .lhs = ref_node, .rhs = rhs_node };
31153254 const builtin = if (is_mod)
3116 try Tag.rem.create(c.arena, .{ .lhs = ref_node, .rhs = rhs_node })
3255 try Tag.rem.create(c.arena, operands)
31173256 else
3118 try Tag.div_trunc.create(c.arena, .{ .lhs = ref_node, .rhs = rhs_node });
3257 try Tag.div_trunc.create(c.arena, operands);
31193258
31203259 const assign = try transCreateNodeInfixOp(c, &block_scope.base, .assign, ref_node, builtin, .used);
31213260 try block_scope.statements.append(assign);
31223261 } else {
3123 var rhs_node = try transExpr(c, &block_scope.base, rhs, .used);
3124
3125 if (is_shift or requires_int_cast) {
3126 // @intCast(rhs)
3127 const cast_to_type = if (is_shift)
3128 try qualTypeToLog2IntRef(c, scope, getExprQualType(c, rhs), loc)
3129 else
3130 try transQualType(c, scope, getExprQualType(c, lhs), loc);
3131
3262 if (is_shift) {
3263 const cast_to_type = try qualTypeToLog2IntRef(c, &block_scope.base, rhs_qt, loc);
31323264 rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node });
3133 }
3134 if (is_ptr_op_signed) {
3135 rhs_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node);
3265 } else if (requires_int_cast) {
3266 rhs_node = try transCCast(c, &block_scope.base, loc, lhs_qt, rhs_qt, rhs_node);
31363267 }
31373268
31383269 const assign = try transCreateNodeInfixOp(c, &block_scope.base, op, ref_node, rhs_node, .used);
......@@ -3194,11 +3325,11 @@ fn transFloatingLiteral(c: *Context, scope: *Scope, stmt: *const clang.FloatingL
31943325 var dbl = stmt.getValueAsApproximateDouble();
31953326 const is_negative = dbl < 0;
31963327 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)
3328 const str = if (dbl == std.math.floor(dbl))
3329 try std.fmt.allocPrint(c.arena, "{d}.0", .{dbl})
32003330 else
3201 try Tag.float_literal.create(c.arena, str);
3331 try std.fmt.allocPrint(c.arena, "{d}", .{dbl});
3332 var node = try Tag.float_literal.create(c.arena, str);
32023333 if (is_negative) node = try Tag.negate.create(c.arena, node);
32033334 return maybeSuppressResult(c, scope, used, node);
32043335}
......@@ -3312,9 +3443,8 @@ fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: Node) !void {
33123443 try c.global_scope.nodes.append(decl_node);
33133444}
33143445
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
3446/// Translate a qualtype for a variable with an initializer. This only matters
3447/// for incomplete arrays, since the initializer determines the size of the array.
33183448fn transQualTypeInitialized(
33193449 c: *Context,
33203450 scope: *Scope,
......@@ -3330,9 +3460,14 @@ fn transQualTypeInitialized(
33303460 switch (decl_init.getStmtClass()) {
33313461 .StringLiteralClass => {
33323462 const string_lit = @ptrCast(*const clang.StringLiteral, decl_init);
3333 const string_lit_size = string_lit.getLength() + 1; // +1 for null terminator
3463 const string_lit_size = string_lit.getLength();
33343464 const array_size = @intCast(usize, string_lit_size);
3335 return Tag.array_type.create(c.arena, .{ .len = array_size, .elem_type = elem_ty });
3465
3466 // incomplete array initialized with empty string, will be translated as [1]T{0}
3467 // see https://github.com/ziglang/zig/issues/8256
3468 if (array_size == 0) return Tag.array_type.create(c.arena, .{ .len = 1, .elem_type = elem_ty });
3469
3470 return Tag.null_sentinel_array_type.create(c.arena, .{ .len = array_size, .elem_type = elem_ty });
33363471 },
33373472 .InitListExprClass => {
33383473 const init_expr = @ptrCast(*const clang.InitListExpr, decl_init);
......@@ -4746,6 +4881,10 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!N
47464881 },
47474882 .Identifier => {
47484883 const mangled_name = scope.getAlias(slice);
4884 if (mem.startsWith(u8, mangled_name, "__builtin_") and !isBuiltinDefined(mangled_name)) {
4885 try m.fail(c, "TODO implement function '{s}' in std.c.builtins", .{mangled_name});
4886 return error.ParseError;
4887 }
47494888 return Tag.identifier.create(c.arena, builtin_typedef_map.get(mangled_name) orelse mangled_name);
47504889 },
47514890 .LParen => {
src/translate_c/ast.zig+83-3
......@@ -40,6 +40,8 @@ pub const Node = extern union {
4040 string_literal,
4141 char_literal,
4242 enum_literal,
43 /// "string"[0..end]
44 string_slice,
4345 identifier,
4446 @"if",
4547 /// if (!operand) break;
......@@ -176,6 +178,7 @@ pub const Node = extern union {
176178 c_pointer,
177179 single_pointer,
178180 array_type,
181 null_sentinel_array_type,
179182
180183 /// @import("std").meta.sizeof(operand)
181184 std_meta_sizeof,
......@@ -334,7 +337,7 @@ pub const Node = extern union {
334337 .std_meta_promoteIntLiteral => Payload.PromoteIntLiteral,
335338 .block => Payload.Block,
336339 .c_pointer, .single_pointer => Payload.Pointer,
337 .array_type => Payload.Array,
340 .array_type, .null_sentinel_array_type => Payload.Array,
338341 .arg_redecl, .alias, .fail_decl => Payload.ArgRedecl,
339342 .log2_int_type => Payload.Log2IntType,
340343 .var_simple, .pub_var_simple => Payload.SimpleVarDecl,
......@@ -342,6 +345,7 @@ pub const Node = extern union {
342345 .array_filler => Payload.ArrayFiller,
343346 .pub_inline_fn => Payload.PubInlineFn,
344347 .field_access => Payload.FieldAccess,
348 .string_slice => Payload.StringSlice,
345349 };
346350 }
347351
......@@ -584,10 +588,12 @@ pub const Payload = struct {
584588
585589 pub const Array = struct {
586590 base: Payload,
587 data: struct {
591 data: ArrayTypeInfo,
592
593 pub const ArrayTypeInfo = struct {
588594 elem_type: Node,
589595 len: usize,
590 },
596 };
591597 };
592598
593599 pub const Pointer = struct {
......@@ -664,6 +670,14 @@ pub const Payload = struct {
664670 radix: Node,
665671 },
666672 };
673
674 pub const StringSlice = struct {
675 base: Payload,
676 data: struct {
677 string: Node,
678 end: usize,
679 },
680 };
667681};
668682
669683/// Converts the nodes into a Zig ast.
......@@ -1015,6 +1029,36 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
10151029 .data = undefined,
10161030 });
10171031 },
1032 .string_slice => {
1033 const payload = node.castTag(.string_slice).?.data;
1034
1035 const string = try renderNode(c, payload.string);
1036 const l_bracket = try c.addToken(.l_bracket, "[");
1037 const start = try c.addNode(.{
1038 .tag = .integer_literal,
1039 .main_token = try c.addToken(.integer_literal, "0"),
1040 .data = undefined,
1041 });
1042 _ = try c.addToken(.ellipsis2, "..");
1043 const end = try c.addNode(.{
1044 .tag = .integer_literal,
1045 .main_token = try c.addTokenFmt(.integer_literal, "{d}", .{payload.end}),
1046 .data = undefined,
1047 });
1048 _ = try c.addToken(.r_bracket, "]");
1049
1050 return c.addNode(.{
1051 .tag = .slice,
1052 .main_token = l_bracket,
1053 .data = .{
1054 .lhs = string,
1055 .rhs = try c.addExtra(std.zig.ast.Node.Slice{
1056 .start = start,
1057 .end = end,
1058 }),
1059 },
1060 });
1061 },
10181062 .fail_decl => {
10191063 const payload = node.castTag(.fail_decl).?.data;
10201064 // pub const name = @compileError(msg);
......@@ -1581,6 +1625,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
15811625 const payload = node.castTag(.array_type).?.data;
15821626 return renderArrayType(c, payload.len, payload.elem_type);
15831627 },
1628 .null_sentinel_array_type => {
1629 const payload = node.castTag(.null_sentinel_array_type).?.data;
1630 return renderNullSentinelArrayType(c, payload.len, payload.elem_type);
1631 },
15841632 .array_filler => {
15851633 const payload = node.castTag(.array_filler).?.data;
15861634
......@@ -1946,6 +1994,36 @@ fn renderArrayType(c: *Context, len: usize, elem_type: Node) !NodeIndex {
19461994 });
19471995}
19481996
1997fn renderNullSentinelArrayType(c: *Context, len: usize, elem_type: Node) !NodeIndex {
1998 const l_bracket = try c.addToken(.l_bracket, "[");
1999 const len_expr = try c.addNode(.{
2000 .tag = .integer_literal,
2001 .main_token = try c.addTokenFmt(.integer_literal, "{d}", .{len}),
2002 .data = undefined,
2003 });
2004 _ = try c.addToken(.colon, ":");
2005
2006 const sentinel_expr = try c.addNode(.{
2007 .tag = .integer_literal,
2008 .main_token = try c.addToken(.integer_literal, "0"),
2009 .data = undefined,
2010 });
2011
2012 _ = try c.addToken(.r_bracket, "]");
2013 const elem_type_expr = try renderNode(c, elem_type);
2014 return c.addNode(.{
2015 .tag = .array_type_sentinel,
2016 .main_token = l_bracket,
2017 .data = .{
2018 .lhs = len_expr,
2019 .rhs = try c.addExtra(std.zig.ast.Node.ArrayTypeSentinel {
2020 .sentinel = sentinel_expr,
2021 .elem_type = elem_type_expr,
2022 }),
2023 },
2024 });
2025}
2026
19492027fn addSemicolonIfNeeded(c: *Context, node: Node) !void {
19502028 switch (node.tag()) {
19512029 .warning => unreachable,
......@@ -2014,6 +2092,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
20142092 .integer_literal,
20152093 .float_literal,
20162094 .string_literal,
2095 .string_slice,
20172096 .char_literal,
20182097 .enum_literal,
20192098 .identifier,
......@@ -2035,6 +2114,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
20352114 .func,
20362115 .call,
20372116 .array_type,
2117 .null_sentinel_array_type,
20382118 .bool_to_int,
20392119 .div_exact,
20402120 .byte_offset_of,
src/zig_clang.cpp+5
......@@ -2459,6 +2459,11 @@ struct ZigClangQualType ZigClangFunctionType_getReturnType(const struct ZigClang
24592459 return bitcast(casted->getReturnType());
24602460}
24612461
2462const struct ZigClangExpr *ZigClangGenericSelectionExpr_getResultExpr(const struct ZigClangGenericSelectionExpr *self) {
2463 auto casted = reinterpret_cast<const clang::GenericSelectionExpr *>(self);
2464 return reinterpret_cast<const struct ZigClangExpr *>(casted->getResultExpr());
2465}
2466
24622467bool ZigClangFunctionProtoType_isVariadic(const struct ZigClangFunctionProtoType *self) {
24632468 auto casted = reinterpret_cast<const clang::FunctionProtoType *>(self);
24642469 return casted->isVariadic();
src/zig_clang.h+2
......@@ -1123,6 +1123,8 @@ ZIG_EXTERN_C bool ZigClangFunctionType_getNoReturnAttr(const struct ZigClangFunc
11231123ZIG_EXTERN_C enum ZigClangCallingConv ZigClangFunctionType_getCallConv(const struct ZigClangFunctionType *self);
11241124ZIG_EXTERN_C struct ZigClangQualType ZigClangFunctionType_getReturnType(const struct ZigClangFunctionType *self);
11251125
1126ZIG_EXTERN_C const struct ZigClangExpr *ZigClangGenericSelectionExpr_getResultExpr(const struct ZigClangGenericSelectionExpr *self);
1127
11261128ZIG_EXTERN_C bool ZigClangFunctionProtoType_isVariadic(const struct ZigClangFunctionProtoType *self);
11271129ZIG_EXTERN_C unsigned ZigClangFunctionProtoType_getNumParams(const struct ZigClangFunctionProtoType *self);
11281130ZIG_EXTERN_C struct ZigClangQualType ZigClangFunctionProtoType_getParamType(const struct ZigClangFunctionProtoType *self, unsigned i);
test/cli.zig+11
......@@ -28,6 +28,8 @@ pub fn main() !void {
2828 const zig_exe = try fs.path.resolve(a, &[_][]const u8{zig_exe_rel});
2929
3030 const dir_path = try fs.path.join(a, &[_][]const u8{ cache_root, "clitest" });
31 defer fs.cwd().deleteTree(dir_path) catch {};
32
3133 const TestFn = fn ([]const u8, []const u8) anyerror!void;
3234 const test_fns = [_]TestFn{
3335 testZigInitLib,
......@@ -174,4 +176,13 @@ fn testZigFmt(zig_exe: []const u8, dir_path: []const u8) !void {
174176 const run_result3 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path });
175177 // both files have been formatted, nothing should change now
176178 testing.expect(run_result3.stdout.len == 0);
179
180 // Check UTF-16 decoding
181 const fmt4_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt4.zig" });
182 var unformatted_code_utf16 = "\xff\xfe \x00 \x00 \x00 \x00/\x00/\x00 \x00n\x00o\x00 \x00r\x00e\x00a\x00s\x00o\x00n\x00";
183 try fs.cwd().writeFile(fmt4_zig_path, unformatted_code_utf16);
184
185 const run_result4 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path });
186 testing.expect(std.mem.startsWith(u8, run_result4.stdout, fmt4_zig_path));
187 testing.expect(run_result4.stdout.len == fmt4_zig_path.len + 1 and run_result4.stdout[run_result4.stdout.len - 1] == '\n');
177188}
test/run_translated_c.zig+121
......@@ -3,6 +3,17 @@ const tests = @import("tests.zig");
33const nl = std.cstr.line_sep;
44
55pub fn addCases(cases: *tests.RunTranslatedCContext) void {
6 cases.add("division of floating literals",
7 \\#define _NO_CRT_STDIO_INLINE 1
8 \\#include <stdio.h>
9 \\#define PI 3.14159265358979323846f
10 \\#define DEG2RAD (PI/180.0f)
11 \\int main(void) {
12 \\ printf("DEG2RAD is: %f\n", DEG2RAD);
13 \\ return 0;
14 \\}
15 , "DEG2RAD is: 0.017453" ++ nl);
16
617 cases.add("use global scope for record/enum/typedef type transalation if needed",
718 \\void bar(void);
819 \\void baz(void);
......@@ -1187,4 +1198,114 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
11871198 \\ return 0;
11881199 \\}
11891200 , "");
1201
1202 cases.add("Generic selections",
1203 \\#include <stdlib.h>
1204 \\#include <string.h>
1205 \\#include <stdint.h>
1206 \\#define my_generic_fn(X) _Generic((X), \
1207 \\ int: abs, \
1208 \\ char *: strlen, \
1209 \\ size_t: malloc, \
1210 \\ default: free \
1211 \\)(X)
1212 \\#define my_generic_val(X) _Generic((X), \
1213 \\ int: 1, \
1214 \\ const char *: "bar" \
1215 \\)
1216 \\int main(void) {
1217 \\ if (my_generic_val(100) != 1) abort();
1218 \\
1219 \\ const char *foo = "foo";
1220 \\ const char *bar = my_generic_val(foo);
1221 \\ if (strcmp(bar, "bar") != 0) abort();
1222 \\
1223 \\ if (my_generic_fn(-42) != 42) abort();
1224 \\ if (my_generic_fn("hello") != 5) abort();
1225 \\
1226 \\ size_t size = 8192;
1227 \\ uint8_t *mem = my_generic_fn(size);
1228 \\ memset(mem, 42, size);
1229 \\ if (mem[size - 1] != 42) abort();
1230 \\ my_generic_fn(mem);
1231 \\
1232 \\ return 0;
1233 \\}
1234 , "");
1235
1236 // See __builtin_alloca_with_align comment in std.c.builtins
1237 cases.add("use of unimplemented builtin in unused function does not prevent compilation",
1238 \\#include <stdlib.h>
1239 \\void unused() {
1240 \\ __builtin_alloca_with_align(1, 8);
1241 \\}
1242 \\int main(void) {
1243 \\ if (__builtin_sqrt(1.0) != 1.0) abort();
1244 \\ return 0;
1245 \\}
1246 , "");
1247
1248 cases.add("convert single-statement bodies into blocks for if/else/for/while. issue #8159",
1249 \\#include <stdlib.h>
1250 \\int foo() { return 1; }
1251 \\int main(void) {
1252 \\ int i = 0;
1253 \\ if (i == 0) if (i == 0) if (i != 0) i = 1;
1254 \\ if (i != 0) i = 1; else if (i == 0) if (i == 0) i += 1;
1255 \\ for (; i < 10;) for (; i < 10;) i++;
1256 \\ while (i == 100) while (i == 100) foo();
1257 \\ if (0) do do "string"; while(1); while(1);
1258 \\ return 0;
1259 \\}
1260 , "");
1261
1262 cases.add("cast RHS of compound assignment if necessary, unused result",
1263 \\#include <stdlib.h>
1264 \\int main(void) {
1265 \\ signed short val = -1;
1266 \\ val += 1; if (val != 0) abort();
1267 \\ val -= 1; if (val != -1) abort();
1268 \\ val *= 2; if (val != -2) abort();
1269 \\ val /= 2; if (val != -1) abort();
1270 \\ val %= 2; if (val != -1) abort();
1271 \\ val <<= 1; if (val != -2) abort();
1272 \\ val >>= 1; if (val != -1) abort();
1273 \\ val += 100000000; // compile error if @truncate() not inserted
1274 \\ unsigned short uval = 1;
1275 \\ uval += 1; if (uval != 2) abort();
1276 \\ uval -= 1; if (uval != 1) abort();
1277 \\ uval *= 2; if (uval != 2) abort();
1278 \\ uval /= 2; if (uval != 1) abort();
1279 \\ uval %= 2; if (uval != 1) abort();
1280 \\ uval <<= 1; if (uval != 2) abort();
1281 \\ uval >>= 1; if (uval != 1) abort();
1282 \\ uval += 100000000; // compile error if @truncate() not inserted
1283 \\}
1284 , "");
1285
1286 cases.add("cast RHS of compound assignment if necessary, used result",
1287 \\#include <stdlib.h>
1288 \\int main(void) {
1289 \\ signed short foo;
1290 \\ signed short val = -1;
1291 \\ foo = (val += 1); if (foo != 0) abort();
1292 \\ foo = (val -= 1); if (foo != -1) abort();
1293 \\ foo = (val *= 2); if (foo != -2) abort();
1294 \\ foo = (val /= 2); if (foo != -1) abort();
1295 \\ foo = (val %= 2); if (foo != -1) abort();
1296 \\ foo = (val <<= 1); if (foo != -2) abort();
1297 \\ foo = (val >>= 1); if (foo != -1) abort();
1298 \\ foo = (val += 100000000); // compile error if @truncate() not inserted
1299 \\ unsigned short ufoo;
1300 \\ unsigned short uval = 1;
1301 \\ ufoo = (uval += 1); if (ufoo != 2) abort();
1302 \\ ufoo = (uval -= 1); if (ufoo != 1) abort();
1303 \\ ufoo = (uval *= 2); if (ufoo != 2) abort();
1304 \\ ufoo = (uval /= 2); if (ufoo != 1) abort();
1305 \\ ufoo = (uval %= 2); if (ufoo != 1) abort();
1306 \\ ufoo = (uval <<= 1); if (ufoo != 2) abort();
1307 \\ ufoo = (uval >>= 1); if (ufoo != 1) abort();
1308 \\ ufoo = (uval += 100000000); // compile error if @truncate() not inserted
1309 \\}
1310 , "");
11901311}
test/stage1/behavior/vector.zig+8-4
......@@ -4,7 +4,7 @@ const mem = std.mem;
44const math = std.math;
55const expect = std.testing.expect;
66const expectEqual = std.testing.expectEqual;
7const expectWithinEpsilon = std.testing.expectWithinEpsilon;
7const expectApproxEqRel = std.testing.expectApproxEqRel;
88const Vector = std.meta.Vector;
99
1010test "implicit cast vector to array - bool" {
......@@ -527,10 +527,14 @@ test "vector reduce operation" {
527527 switch (@typeInfo(TX)) {
528528 .Int, .Bool => expectEqual(expected, r),
529529 .Float => {
530 if (math.isNan(expected) != math.isNan(r)) {
531 std.debug.panic("unexpected NaN value!\n", .{});
530 const expected_nan = math.isNan(expected);
531 const got_nan = math.isNan(r);
532
533 if (expected_nan and got_nan) {
534 // Do this check explicitly as two NaN values are never
535 // equal.
532536 } else {
533 expectWithinEpsilon(expected, r, 0.001);
537 expectApproxEqRel(expected, r, math.sqrt(math.epsilon(TX)));
534538 }
535539 },
536540 else => unreachable,
test/stage2/cbe.zig+1-1
......@@ -51,7 +51,7 @@ pub fn addCases(ctx: *TestContext) !void {
5151 \\ _ = printf("Hello, %s!\n", "world");
5252 \\ return 0;
5353 \\}
54 , "Hello, world!\n");
54 , "Hello, world!" ++ std.cstr.line_sep);
5555 }
5656
5757 {
test/stage2/wasm.zig+35
......@@ -175,6 +175,41 @@ pub fn addCases(ctx: *TestContext) !void {
175175 \\ return i;
176176 \\}
177177 , "31\n");
178
179 case.addCompareOutput(
180 \\export fn _start() void {
181 \\ assert(foo(true) != @as(i32, 30));
182 \\}
183 \\
184 \\fn assert(ok: bool) void {
185 \\ if (!ok) unreachable;
186 \\}
187 \\
188 \\fn foo(ok: bool) i32 {
189 \\ const x = if(ok) @as(i32, 20) else @as(i32, 10);
190 \\ return x;
191 \\}
192 , "");
193
194 case.addCompareOutput(
195 \\export fn _start() void {
196 \\ assert(foo(false) == @as(i32, 20));
197 \\ assert(foo(true) == @as(i32, 30));
198 \\}
199 \\
200 \\fn assert(ok: bool) void {
201 \\ if (!ok) unreachable;
202 \\}
203 \\
204 \\fn foo(ok: bool) i32 {
205 \\ const val: i32 = blk: {
206 \\ var x: i32 = 1;
207 \\ if (!ok) break :blk x + @as(i32, 9);
208 \\ break :blk x + @as(i32, 19);
209 \\ };
210 \\ return val + 10;
211 \\}
212 , "");
178213 }
179214
180215 {
test/standalone.zig+4-1
......@@ -9,7 +9,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
99 cases.add("test/standalone/main_return_error/error_u8.zig");
1010 cases.add("test/standalone/main_return_error/error_u8_non_zero.zig");
1111 cases.addBuildFile("test/standalone/main_pkg_path/build.zig");
12 cases.addBuildFile("test/standalone/shared_library/build.zig");
12 if (std.Target.current.os.tag != .macos) {
13 // TODO zld cannot link shared libraries yet.
14 cases.addBuildFile("test/standalone/shared_library/build.zig");
15 }
1316 cases.addBuildFile("test/standalone/mix_o_files/build.zig");
1417 cases.addBuildFile("test/standalone/global_linkage/build.zig");
1518 cases.addBuildFile("test/standalone/static_c_lib/build.zig");
test/standalone/mix_o_files/base64.zig+3-3
......@@ -3,9 +3,9 @@ const base64 = @import("std").base64;
33export fn decode_base_64(dest_ptr: [*]u8, dest_len: usize, source_ptr: [*]const u8, source_len: usize) usize {
44 const src = source_ptr[0..source_len];
55 const dest = dest_ptr[0..dest_len];
6 const base64_decoder = base64.standard_decoder_unsafe;
7 const decoded_size = base64_decoder.calcSize(src);
8 base64_decoder.decode(dest[0..decoded_size], src);
6 const base64_decoder = base64.standard.Decoder;
7 const decoded_size = base64_decoder.calcSizeForSlice(src) catch unreachable;
8 base64_decoder.decode(dest[0..decoded_size], src) catch unreachable;
99 return decoded_size;
1010}
1111
test/translate_c.zig+126-76
......@@ -3,6 +3,28 @@ const std = @import("std");
33const CrossTarget = std.zig.CrossTarget;
44
55pub fn addCases(cases: *tests.TranslateCContext) void {
6 cases.add("unnamed child types of typedef receive typedef's name",
7 \\typedef enum {
8 \\ FooA,
9 \\ FooB,
10 \\} Foo;
11 \\typedef struct {
12 \\ int a, b;
13 \\} Bar;
14 , &[_][]const u8{
15 \\pub const Foo = extern enum(c_int) {
16 \\ A,
17 \\ B,
18 \\ _,
19 \\};
20 \\pub const FooA = @enumToInt(Foo.A);
21 \\pub const FooB = @enumToInt(Foo.B);
22 \\pub const Bar = extern struct {
23 \\ a: c_int,
24 \\ b: c_int,
25 \\};
26 });
27
628 cases.add("if as while stmt has semicolon",
729 \\void foo() {
830 \\ while (1) if (1) {
......@@ -218,9 +240,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
218240 \\} Bar;
219241 , &[_][]const u8{
220242 \\source.h:1:9: warning: struct demoted to opaque type - unable to translate type of field foo
221 \\const struct_unnamed_1 = opaque {};
222 \\pub const Foo = struct_unnamed_1;
223 \\const struct_unnamed_2 = extern struct {
243 \\pub const Foo = opaque {};
244 \\pub const Bar = extern struct {
224245 \\ bar: ?*Foo,
225246 \\};
226247 });
......@@ -519,17 +540,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
519540 \\} outer;
520541 \\void foo(outer *x) { x->y = x->x; }
521542 , &[_][]const u8{
522 \\const struct_unnamed_3 = extern struct {
543 \\const struct_unnamed_2 = extern struct {
523544 \\ y: c_int,
524545 \\};
525 \\const union_unnamed_2 = extern union {
546 \\const union_unnamed_1 = extern union {
526547 \\ x: u8,
527 \\ unnamed_0: struct_unnamed_3,
548 \\ unnamed_0: struct_unnamed_2,
528549 \\};
529 \\const struct_unnamed_1 = extern struct {
530 \\ unnamed_0: union_unnamed_2,
550 \\pub const outer = extern struct {
551 \\ unnamed_0: union_unnamed_1,
531552 \\};
532 \\pub const outer = struct_unnamed_1;
533553 \\pub export fn foo(arg_x: [*c]outer) void {
534554 \\ var x = arg_x;
535555 \\ x.*.unnamed_0.unnamed_0.y = @bitCast(c_int, @as(c_uint, x.*.unnamed_0.x));
......@@ -565,21 +585,20 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
565585 \\struct {int x,y;} s2 = {.y = 2, .x=1};
566586 \\foo s3 = { 123 };
567587 , &[_][]const u8{
568 \\const struct_unnamed_1 = extern struct {
588 \\pub const foo = extern struct {
569589 \\ x: c_int,
570590 \\};
571 \\pub const foo = struct_unnamed_1;
572 \\const struct_unnamed_2 = extern struct {
591 \\const struct_unnamed_1 = extern struct {
573592 \\ x: f64,
574593 \\ y: f64,
575594 \\ z: f64,
576595 \\};
577 \\pub export var s0: struct_unnamed_2 = struct_unnamed_2{
596 \\pub export var s0: struct_unnamed_1 = struct_unnamed_1{
578597 \\ .x = 1.2,
579598 \\ .y = 1.3,
580599 \\ .z = 0,
581600 \\};
582 \\const struct_unnamed_3 = extern struct {
601 \\const struct_unnamed_2 = extern struct {
583602 \\ sec: c_int,
584603 \\ min: c_int,
585604 \\ hour: c_int,
......@@ -587,7 +606,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
587606 \\ mon: c_int,
588607 \\ year: c_int,
589608 \\};
590 \\pub export var s1: struct_unnamed_3 = struct_unnamed_3{
609 \\pub export var s1: struct_unnamed_2 = struct_unnamed_2{
591610 \\ .sec = @as(c_int, 30),
592611 \\ .min = @as(c_int, 15),
593612 \\ .hour = @as(c_int, 17),
......@@ -595,11 +614,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
595614 \\ .mon = @as(c_int, 12),
596615 \\ .year = @as(c_int, 2014),
597616 \\};
598 \\const struct_unnamed_4 = extern struct {
617 \\const struct_unnamed_3 = extern struct {
599618 \\ x: c_int,
600619 \\ y: c_int,
601620 \\};
602 \\pub export var s2: struct_unnamed_4 = struct_unnamed_4{
621 \\pub export var s2: struct_unnamed_3 = struct_unnamed_3{
603622 \\ .x = @as(c_int, 1),
604623 \\ .y = @as(c_int, 2),
605624 \\};
......@@ -745,14 +764,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
745764 \\ static const char v2[] = "2.2.2";
746765 \\}
747766 , &[_][]const u8{
748 \\const v2: [6]u8 = [6]u8{
749 \\ '2',
750 \\ '.',
751 \\ '2',
752 \\ '.',
753 \\ '2',
754 \\ 0,
755 \\};
767 \\const v2: [5:0]u8 = "2.2.2".*;
756768 \\pub export fn foo() void {}
757769 });
758770
......@@ -1600,30 +1612,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
16001612 \\static char arr1[] = "hello";
16011613 \\char arr2[] = "hello";
16021614 , &[_][]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 \\};
1615 \\pub export var arr0: [5:0]u8 = "hello".*;
1616 \\pub var arr1: [5:0]u8 = "hello".*;
1617 \\pub export var arr2: [5:0]u8 = "hello".*;
16271618 });
16281619
16291620 cases.add("array initializer expr",
......@@ -1667,37 +1658,36 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
16671658 \\ p,
16681659 \\};
16691660 , &[_][]const u8{
1670 \\const enum_unnamed_1 = extern enum(c_int) {
1661 \\pub const d = extern enum(c_int) {
16711662 \\ a,
16721663 \\ b,
16731664 \\ c,
16741665 \\ _,
16751666 \\};
1676 \\pub const a = @enumToInt(enum_unnamed_1.a);
1677 \\pub const b = @enumToInt(enum_unnamed_1.b);
1678 \\pub const c = @enumToInt(enum_unnamed_1.c);
1679 \\pub const d = enum_unnamed_1;
1680 \\const enum_unnamed_2 = extern enum(c_int) {
1667 \\pub const a = @enumToInt(d.a);
1668 \\pub const b = @enumToInt(d.b);
1669 \\pub const c = @enumToInt(d.c);
1670 \\const enum_unnamed_1 = extern enum(c_int) {
16811671 \\ e = 0,
16821672 \\ f = 4,
16831673 \\ g = 5,
16841674 \\ _,
16851675 \\};
1686 \\pub const e = @enumToInt(enum_unnamed_2.e);
1687 \\pub const f = @enumToInt(enum_unnamed_2.f);
1688 \\pub const g = @enumToInt(enum_unnamed_2.g);
1689 \\pub export var h: enum_unnamed_2 = @intToEnum(enum_unnamed_2, e);
1690 \\const enum_unnamed_3 = extern enum(c_int) {
1676 \\pub const e = @enumToInt(enum_unnamed_1.e);
1677 \\pub const f = @enumToInt(enum_unnamed_1.f);
1678 \\pub const g = @enumToInt(enum_unnamed_1.g);
1679 \\pub export var h: enum_unnamed_1 = @intToEnum(enum_unnamed_1, e);
1680 \\const enum_unnamed_2 = extern enum(c_int) {
16911681 \\ i,
16921682 \\ j,
16931683 \\ k,
16941684 \\ _,
16951685 \\};
1696 \\pub const i = @enumToInt(enum_unnamed_3.i);
1697 \\pub const j = @enumToInt(enum_unnamed_3.j);
1698 \\pub const k = @enumToInt(enum_unnamed_3.k);
1686 \\pub const i = @enumToInt(enum_unnamed_2.i);
1687 \\pub const j = @enumToInt(enum_unnamed_2.j);
1688 \\pub const k = @enumToInt(enum_unnamed_2.k);
16991689 \\pub const struct_Baz = extern struct {
1700 \\ l: enum_unnamed_3,
1690 \\ l: enum_unnamed_2,
17011691 \\ m: d,
17021692 \\};
17031693 \\pub const enum_i = extern enum(c_int) {
......@@ -1962,7 +1952,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
19621952 , &[_][]const u8{
19631953 \\pub export fn foo() c_int {
19641954 \\ var a: c_int = 5;
1965 \\ while (true) a = 2;
1955 \\ while (true) {
1956 \\ a = 2;
1957 \\ }
19661958 \\ while (true) {
19671959 \\ var a_1: c_int = 4;
19681960 \\ a_1 = 9;
......@@ -1975,7 +1967,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
19751967 \\ var a_1: c_int = 2;
19761968 \\ a_1 = 12;
19771969 \\ }
1978 \\ while (true) a = 7;
1970 \\ while (true) {
1971 \\ a = 7;
1972 \\ }
19791973 \\ return 0;
19801974 \\}
19811975 });
......@@ -2036,7 +2030,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
20362030 \\}
20372031 , &[_][]const u8{
20382032 \\pub export fn bar() c_int {
2039 \\ if ((if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6)) != 0) _ = @as(c_int, 2);
2033 \\ if ((if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6)) != 0) {
2034 \\ _ = @as(c_int, 2);
2035 \\ }
20402036 \\ return if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6);
20412037 \\}
20422038 });
......@@ -2417,7 +2413,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
24172413 \\pub const yes = [*c]u8;
24182414 \\pub export fn foo() void {
24192415 \\ var a: yes = undefined;
2420 \\ if (a != null) _ = @as(c_int, 2);
2416 \\ if (a != null) {
2417 \\ _ = @as(c_int, 2);
2418 \\ }
24212419 \\}
24222420 });
24232421
......@@ -2456,7 +2454,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
24562454 \\ b: c_int,
24572455 \\};
24582456 \\pub extern var a: struct_Foo;
2459 \\pub export var b: f32 = 2;
2457 \\pub export var b: f32 = 2.0;
24602458 \\pub export fn foo() void {
24612459 \\ var c: [*c]struct_Foo = undefined;
24622460 \\ _ = a.b;
......@@ -2768,7 +2766,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
27682766 \\ var a = arg_a;
27692767 \\ var i: c_int = 0;
27702768 \\ while (a > @bitCast(c_uint, @as(c_int, 0))) {
2771 \\ a >>= @intCast(@import("std").math.Log2Int(c_int), 1);
2769 \\ a >>= @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1));
27722770 \\ }
27732771 \\ return i;
27742772 \\}
......@@ -2788,7 +2786,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
27882786 \\ var a = arg_a;
27892787 \\ var i: c_int = 0;
27902788 \\ while (a > @bitCast(c_uint, @as(c_int, 0))) {
2791 \\ a >>= @intCast(@import("std").math.Log2Int(c_int), 1);
2789 \\ a >>= @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1));
27922790 \\ }
27932791 \\ return i;
27942792 \\}
......@@ -3020,17 +3018,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
30203018 \\pub extern fn fn_bool(x: bool) void;
30213019 \\pub extern fn fn_ptr(x: ?*c_void) void;
30223020 \\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));
3021 \\ fn_int(@floatToInt(c_int, 3.0));
3022 \\ fn_int(@floatToInt(c_int, 3.0));
3023 \\ fn_int(@floatToInt(c_int, 3.0));
30263024 \\ fn_int(@as(c_int, 1094861636));
30273025 \\ fn_f32(@intToFloat(f32, @as(c_int, 3)));
30283026 \\ fn_f64(@intToFloat(f64, @as(c_int, 3)));
30293027 \\ fn_char(@bitCast(u8, @truncate(i8, @as(c_int, '3'))));
30303028 \\ fn_char(@bitCast(u8, @truncate(i8, @as(c_int, '\x01'))));
30313029 \\ fn_char(@bitCast(u8, @truncate(i8, @as(c_int, 0))));
3032 \\ fn_f32(3);
3033 \\ fn_f64(3);
3030 \\ fn_f32(3.0);
3031 \\ fn_f64(3.0);
30343032 \\ fn_bool(@as(c_int, 123) != 0);
30353033 \\ fn_bool(@as(c_int, 0) != 0);
30363034 \\ fn_bool(@ptrToInt(fn_int) != 0);
......@@ -3418,4 +3416,56 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
34183416 \\pub const MAY_NEED_PROMOTION_HEX = @import("std").meta.promoteIntLiteral(c_int, 0x80000000, .hexadecimal);
34193417 \\pub const MAY_NEED_PROMOTION_OCT = @import("std").meta.promoteIntLiteral(c_int, 0o20000000000, .octal);
34203418 });
3419
3420 // See __builtin_alloca_with_align comment in std.c.builtins
3421 cases.add("demote un-implemented builtins",
3422 \\#define FOO(X) __builtin_alloca_with_align((X), 8)
3423 , &[_][]const u8{
3424 \\pub const FOO = @compileError("TODO implement function '__builtin_alloca_with_align' in std.c.builtins");
3425 });
3426
3427 cases.add("null sentinel arrays when initialized from string literal. Issue #8256",
3428 \\#include <stdint.h>
3429 \\char zero[0] = "abc";
3430 \\uint32_t zero_w[0] = U"💯💯💯";
3431 \\char empty_incomplete[] = "";
3432 \\uint32_t empty_incomplete_w[] = U"";
3433 \\char empty_constant[100] = "";
3434 \\uint32_t empty_constant_w[100] = U"";
3435 \\char incomplete[] = "abc";
3436 \\uint32_t incomplete_w[] = U"💯💯💯";
3437 \\char truncated[1] = "abc";
3438 \\uint32_t truncated_w[1] = U"💯💯💯";
3439 \\char extend[5] = "a";
3440 \\uint32_t extend_w[5] = U"💯";
3441 \\char no_null[3] = "abc";
3442 \\uint32_t no_null_w[3] = U"💯💯💯";
3443 , &[_][]const u8{
3444 \\pub export var zero: [0]u8 = [0]u8{};
3445 \\pub export var zero_w: [0]u32 = [0]u32{};
3446 \\pub export var empty_incomplete: [1]u8 = [1]u8{0} ** 1;
3447 \\pub export var empty_incomplete_w: [1]u32 = [1]u32{0} ** 1;
3448 \\pub export var empty_constant: [100]u8 = [1]u8{0} ** 100;
3449 \\pub export var empty_constant_w: [100]u32 = [1]u32{0} ** 100;
3450 \\pub export var incomplete: [3:0]u8 = "abc".*;
3451 \\pub export var incomplete_w: [3:0]u32 = [3:0]u32{
3452 \\ '\u{1f4af}',
3453 \\ '\u{1f4af}',
3454 \\ '\u{1f4af}',
3455 \\};
3456 \\pub export var truncated: [1]u8 = "abc"[0..1].*;
3457 \\pub export var truncated_w: [1]u32 = [1]u32{
3458 \\ '\u{1f4af}',
3459 \\};
3460 \\pub export var extend: [5]u8 = "a"[0..1].* ++ [1]u8{0} ** 4;
3461 \\pub export var extend_w: [5]u32 = [1]u32{
3462 \\ '\u{1f4af}',
3463 \\} ++ [1]u32{0} ** 4;
3464 \\pub export var no_null: [3]u8 = "abc".*;
3465 \\pub export var no_null_w: [3]u32 = [3]u32{
3466 \\ '\u{1f4af}',
3467 \\ '\u{1f4af}',
3468 \\ '\u{1f4af}',
3469 \\};
3470 });
34213471}
tools/update_clang_options.zig+4
......@@ -332,6 +332,10 @@ const known_options = [_]KnownOpt{
332332 .name = "s",
333333 .ident = "strip",
334334 },
335 .{
336 .name = "dynamiclib",
337 .ident = "shared",
338 },
335339};
336340
337341const blacklisted_options = [_][]const u8{};