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...@@ -89,6 +89,7 @@ set(ZIG_TARGET_MCPU "baseline" CACHE STRING "-mcpu parameter to output binaries
89set(ZIG_EXECUTABLE "" CACHE STRING "(when cross compiling) path to already-built zig binary")89set(ZIG_EXECUTABLE "" CACHE STRING "(when cross compiling) path to already-built zig binary")
90set(ZIG_SINGLE_THREADED off CACHE BOOL "limit the zig compiler to use only 1 thread")90set(ZIG_SINGLE_THREADED off CACHE BOOL "limit the zig compiler to use only 1 thread")
91set(ZIG_OMIT_STAGE2 off CACHE BOOL "omit the stage2 backend from stage1")91set(ZIG_OMIT_STAGE2 off CACHE BOOL "omit the stage2 backend from stage1")
92set(ZIG_ENABLE_LOGGING off CACHE BOOL "enable logging")
9293
93if("${ZIG_TARGET_TRIPLE}" STREQUAL "native")94if("${ZIG_TARGET_TRIPLE}" STREQUAL "native")
94 set(ZIG_USE_LLVM_CONFIG ON CACHE BOOL "use llvm-config to find LLVM libraries")95 set(ZIG_USE_LLVM_CONFIG ON CACHE BOOL "use llvm-config to find LLVM libraries")
...@@ -564,7 +565,14 @@ set(ZIG_STAGE2_SOURCES...@@ -564,7 +565,14 @@ set(ZIG_STAGE2_SOURCES
564 "${CMAKE_SOURCE_DIR}/src/link/Coff.zig"565 "${CMAKE_SOURCE_DIR}/src/link/Coff.zig"
565 "${CMAKE_SOURCE_DIR}/src/link/Elf.zig"566 "${CMAKE_SOURCE_DIR}/src/link/Elf.zig"
566 "${CMAKE_SOURCE_DIR}/src/link/MachO.zig"567 "${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"
567 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"572 "${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"
568 "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"576 "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"
569 "${CMAKE_SOURCE_DIR}/src/link/C/zig.h"577 "${CMAKE_SOURCE_DIR}/src/link/C/zig.h"
570 "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin"578 "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin"
...@@ -600,6 +608,12 @@ else()...@@ -600,6 +608,12 @@ else()
600 set(ZIG_OMIT_STAGE2_BOOL "false")608 set(ZIG_OMIT_STAGE2_BOOL "false")
601endif()609endif()
602610
611if(ZIG_ENABLE_LOGGING)
612 set(ZIG_ENABLE_LOGGING_BOOL "true")
613else()
614 set(ZIG_ENABLE_LOGGING_BOOL "false")
615endif()
616
603configure_file (617configure_file (
604 "${CMAKE_SOURCE_DIR}/src/stage1/config.h.in"618 "${CMAKE_SOURCE_DIR}/src/stage1/config.h.in"
605 "${ZIG_CONFIG_H_OUT}"619 "${ZIG_CONFIG_H_OUT}"
...@@ -728,12 +742,14 @@ if(MSVC OR MINGW)...@@ -728,12 +742,14 @@ if(MSVC OR MINGW)
728 target_link_libraries(zigstage1 LINK_PUBLIC version)742 target_link_libraries(zigstage1 LINK_PUBLIC version)
729endif()743endif()
730744
731add_executable(zig0 ${ZIG0_SOURCES})745if("${ZIG_EXECUTABLE}" STREQUAL "")
732set_target_properties(zig0 PROPERTIES746 add_executable(zig0 ${ZIG0_SOURCES})
733 COMPILE_FLAGS ${EXE_CFLAGS}747 set_target_properties(zig0 PROPERTIES
734 LINK_FLAGS ${EXE_LDFLAGS}748 COMPILE_FLAGS ${EXE_CFLAGS}
735)749 LINK_FLAGS ${EXE_LDFLAGS}
736target_link_libraries(zig0 zigstage1)750 )
751 target_link_libraries(zig0 zigstage1)
752endif()
737753
738if(MSVC)754if(MSVC)
739 set(ZIG1_OBJECT "${CMAKE_BINARY_DIR}/zig1.obj")755 set(ZIG1_OBJECT "${CMAKE_BINARY_DIR}/zig1.obj")
...@@ -782,7 +798,6 @@ if("${ZIG_EXECUTABLE}" STREQUAL "")...@@ -782,7 +798,6 @@ if("${ZIG_EXECUTABLE}" STREQUAL "")
782else()798else()
783 add_custom_command(799 add_custom_command(
784 OUTPUT "${ZIG1_OBJECT}"800 OUTPUT "${ZIG1_OBJECT}"
785 BYPRODUCTS "${ZIG1_OBJECT}"
786 COMMAND "${ZIG_EXECUTABLE}" "build-obj" ${BUILD_ZIG1_ARGS}801 COMMAND "${ZIG_EXECUTABLE}" "build-obj" ${BUILD_ZIG1_ARGS}
787 DEPENDS ${ZIG_STAGE2_SOURCES}802 DEPENDS ${ZIG_STAGE2_SOURCES}
788 COMMENT STATUS "Building self-hosted component ${ZIG1_OBJECT}"803 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:...@@ -12,6 +12,19 @@ jobs:
12 - script: ci/azure/macos_script12 - script: ci/azure/macos_script
13 name: main13 name: main
14 displayName: 'Build and test'14 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'
15- job: BuildLinux28- job: BuildLinux
16 pool:29 pool:
17 vmImage: 'ubuntu-18.04'30 vmImage: 'ubuntu-18.04'
...@@ -31,7 +44,7 @@ jobs:...@@ -31,7 +44,7 @@ jobs:
31 timeoutInMinutes: 36044 timeoutInMinutes: 360
32 steps:45 steps:
33 - powershell: |46 - 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")
35 .\sfx.exe -y -o\48 .\sfx.exe -y -o\
36 del sfx.exe49 del sfx.exe
37 displayName: Download/Extract/Install MSYS250 displayName: Download/Extract/Install MSYS2
ci/azure/windows_msvc_install+1-1
...@@ -3,7 +3,7 @@...@@ -3,7 +3,7 @@
3set -x3set -x
4set -e4set -e
55
6pacman -Su --needed --noconfirm6pacman -Suy --needed --noconfirm
7pacman -S --needed --noconfirm wget p7zip python3-pip tar xz7pacman -S --needed --noconfirm wget p7zip python3-pip tar xz
88
9pip install s3cmd9pip install s3cmd
doc/docgen.zig+18-5
...@@ -4,6 +4,7 @@ const io = std.io;...@@ -4,6 +4,7 @@ const io = std.io;
4const fs = std.fs;4const fs = std.fs;
5const process = std.process;5const process = std.process;
6const ChildProcess = std.ChildProcess;6const ChildProcess = std.ChildProcess;
7const Progress = std.Progress;
7const print = std.debug.print;8const print = std.debug.print;
8const mem = std.mem;9const mem = std.mem;
9const testing = std.testing;10const testing = std.testing;
...@@ -234,7 +235,7 @@ fn parseError(tokenizer: *Tokenizer, token: Token, comptime fmt: []const u8, arg...@@ -234,7 +235,7 @@ fn parseError(tokenizer: *Tokenizer, token: Token, comptime fmt: []const u8, arg
234 }235 }
235 }236 }
236 {237 {
237 const caret_count = token.end - token.start;238 const caret_count = std.math.min(token.end, loc.line_end) - token.start;
238 var i: usize = 0;239 var i: usize = 0;
239 while (i < caret_count) : (i += 1) {240 while (i < caret_count) : (i += 1) {
240 print("~", .{});241 print("~", .{});
...@@ -1012,6 +1013,9 @@ fn tokenizeAndPrint(docgen_tokenizer: *Tokenizer, out: anytype, source_token: To...@@ -1012,6 +1013,9 @@ fn tokenizeAndPrint(docgen_tokenizer: *Tokenizer, out: anytype, source_token: To
10121013
1013fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: anytype, zig_exe: []const u8, do_code_tests: bool) !void {1014fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: anytype, zig_exe: []const u8, do_code_tests: bool) !void {
1014 var code_progress_index: usize = 0;1015 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
1016 var env_map = try process.getEnvMap(allocator);1020 var env_map = try process.getEnvMap(allocator);
1017 try env_map.set("ZIG_DEBUG_COLOR", "1");1021 try env_map.set("ZIG_DEBUG_COLOR", "1");
...@@ -1058,8 +1062,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any...@@ -1058,8 +1062,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
1058 try tokenizeAndPrint(tokenizer, out, content_tok);1062 try tokenizeAndPrint(tokenizer, out, content_tok);
1059 },1063 },
1060 .Code => |code| {1064 .Code => |code| {
1061 code_progress_index += 1;1065 root_node.completeOne();
1062 print("docgen example code {}/{}...", .{ code_progress_index, tokenizer.code_node_count });
10631066
1064 const raw_source = tokenizer.buffer[code.source_token.start..code.source_token.end];1067 const raw_source = tokenizer.buffer[code.source_token.start..code.source_token.end];
1065 const trimmed_raw_source = mem.trim(u8, raw_source, " \n");1068 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...@@ -1071,7 +1074,6 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
1071 try out.writeAll("</pre>");1074 try out.writeAll("</pre>");
10721075
1073 if (!do_code_tests) {1076 if (!do_code_tests) {
1074 print("SKIP\n", .{});
1075 continue;1077 continue;
1076 }1078 }
10771079
...@@ -1133,12 +1135,14 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any...@@ -1133,12 +1135,14 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
1133 switch (result.term) {1135 switch (result.term) {
1134 .Exited => |exit_code| {1136 .Exited => |exit_code| {
1135 if (exit_code == 0) {1137 if (exit_code == 0) {
1138 progress.log("", .{});
1136 print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});1139 print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});
1137 dumpArgs(build_args.items);1140 dumpArgs(build_args.items);
1138 return parseError(tokenizer, code.source_token, "example incorrectly compiled", .{});1141 return parseError(tokenizer, code.source_token, "example incorrectly compiled", .{});
1139 }1142 }
1140 },1143 },
1141 else => {1144 else => {
1145 progress.log("", .{});
1142 print("{s}\nThe following command crashed:\n", .{result.stderr});1146 print("{s}\nThe following command crashed:\n", .{result.stderr});
1143 dumpArgs(build_args.items);1147 dumpArgs(build_args.items);
1144 return parseError(tokenizer, code.source_token, "example compile crashed", .{});1148 return parseError(tokenizer, code.source_token, "example compile crashed", .{});
...@@ -1187,6 +1191,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any...@@ -1187,6 +1191,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
1187 switch (result.term) {1191 switch (result.term) {
1188 .Exited => |exit_code| {1192 .Exited => |exit_code| {
1189 if (exit_code == 0) {1193 if (exit_code == 0) {
1194 progress.log("", .{});
1190 print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});1195 print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});
1191 dumpArgs(run_args);1196 dumpArgs(run_args);
1192 return parseError(tokenizer, code.source_token, "example incorrectly compiled", .{});1197 return parseError(tokenizer, code.source_token, "example incorrectly compiled", .{});
...@@ -1266,18 +1271,21 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any...@@ -1266,18 +1271,21 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
1266 switch (result.term) {1271 switch (result.term) {
1267 .Exited => |exit_code| {1272 .Exited => |exit_code| {
1268 if (exit_code == 0) {1273 if (exit_code == 0) {
1274 progress.log("", .{});
1269 print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});1275 print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});
1270 dumpArgs(test_args.items);1276 dumpArgs(test_args.items);
1271 return parseError(tokenizer, code.source_token, "example incorrectly compiled", .{});1277 return parseError(tokenizer, code.source_token, "example incorrectly compiled", .{});
1272 }1278 }
1273 },1279 },
1274 else => {1280 else => {
1281 progress.log("", .{});
1275 print("{s}\nThe following command crashed:\n", .{result.stderr});1282 print("{s}\nThe following command crashed:\n", .{result.stderr});
1276 dumpArgs(test_args.items);1283 dumpArgs(test_args.items);
1277 return parseError(tokenizer, code.source_token, "example compile crashed", .{});1284 return parseError(tokenizer, code.source_token, "example compile crashed", .{});
1278 },1285 },
1279 }1286 }
1280 if (mem.indexOf(u8, result.stderr, error_match) == null) {1287 if (mem.indexOf(u8, result.stderr, error_match) == null) {
1288 progress.log("", .{});
1281 print("{s}\nExpected to find '{s}' in stderr\n", .{ result.stderr, error_match });1289 print("{s}\nExpected to find '{s}' in stderr\n", .{ result.stderr, error_match });
1282 return parseError(tokenizer, code.source_token, "example did not have expected compile error", .{});1290 return parseError(tokenizer, code.source_token, "example did not have expected compile error", .{});
1283 }1291 }
...@@ -1321,18 +1329,21 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any...@@ -1321,18 +1329,21 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
1321 switch (result.term) {1329 switch (result.term) {
1322 .Exited => |exit_code| {1330 .Exited => |exit_code| {
1323 if (exit_code == 0) {1331 if (exit_code == 0) {
1332 progress.log("", .{});
1324 print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});1333 print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});
1325 dumpArgs(test_args.items);1334 dumpArgs(test_args.items);
1326 return parseError(tokenizer, code.source_token, "example test incorrectly succeeded", .{});1335 return parseError(tokenizer, code.source_token, "example test incorrectly succeeded", .{});
1327 }1336 }
1328 },1337 },
1329 else => {1338 else => {
1339 progress.log("", .{});
1330 print("{s}\nThe following command crashed:\n", .{result.stderr});1340 print("{s}\nThe following command crashed:\n", .{result.stderr});
1331 dumpArgs(test_args.items);1341 dumpArgs(test_args.items);
1332 return parseError(tokenizer, code.source_token, "example compile crashed", .{});1342 return parseError(tokenizer, code.source_token, "example compile crashed", .{});
1333 },1343 },
1334 }1344 }
1335 if (mem.indexOf(u8, result.stderr, error_match) == null) {1345 if (mem.indexOf(u8, result.stderr, error_match) == null) {
1346 progress.log("", .{});
1336 print("{s}\nExpected to find '{s}' in stderr\n", .{ result.stderr, error_match });1347 print("{s}\nExpected to find '{s}' in stderr\n", .{ result.stderr, error_match });
1337 return parseError(tokenizer, code.source_token, "example did not have expected runtime safety error message", .{});1348 return parseError(tokenizer, code.source_token, "example did not have expected runtime safety error message", .{});
1338 }1349 }
...@@ -1400,18 +1411,21 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any...@@ -1400,18 +1411,21 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
1400 switch (result.term) {1411 switch (result.term) {
1401 .Exited => |exit_code| {1412 .Exited => |exit_code| {
1402 if (exit_code == 0) {1413 if (exit_code == 0) {
1414 progress.log("", .{});
1403 print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});1415 print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});
1404 dumpArgs(build_args.items);1416 dumpArgs(build_args.items);
1405 return parseError(tokenizer, code.source_token, "example build incorrectly succeeded", .{});1417 return parseError(tokenizer, code.source_token, "example build incorrectly succeeded", .{});
1406 }1418 }
1407 },1419 },
1408 else => {1420 else => {
1421 progress.log("", .{});
1409 print("{s}\nThe following command crashed:\n", .{result.stderr});1422 print("{s}\nThe following command crashed:\n", .{result.stderr});
1410 dumpArgs(build_args.items);1423 dumpArgs(build_args.items);
1411 return parseError(tokenizer, code.source_token, "example compile crashed", .{});1424 return parseError(tokenizer, code.source_token, "example compile crashed", .{});
1412 },1425 },
1413 }1426 }
1414 if (mem.indexOf(u8, result.stderr, error_match) == null) {1427 if (mem.indexOf(u8, result.stderr, error_match) == null) {
1428 progress.log("", .{});
1415 print("{s}\nExpected to find '{s}' in stderr\n", .{ result.stderr, error_match });1429 print("{s}\nExpected to find '{s}' in stderr\n", .{ result.stderr, error_match });
1416 return parseError(tokenizer, code.source_token, "example did not have expected compile error message", .{});1430 return parseError(tokenizer, code.source_token, "example did not have expected compile error message", .{});
1417 }1431 }
...@@ -1461,7 +1475,6 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any...@@ -1461,7 +1475,6 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
1461 try out.print("\n{s}{s}</code></pre>\n", .{ escaped_stderr, escaped_stdout });1475 try out.print("\n{s}{s}</code></pre>\n", .{ escaped_stderr, escaped_stdout });
1462 },1476 },
1463 }1477 }
1464 print("OK\n", .{});
1465 },1478 },
1466 }1479 }
1467 }1480 }
doc/langref.html.in+3-3
...@@ -9952,9 +9952,9 @@ export fn decode_base_64(...@@ -9952,9 +9952,9 @@ export fn decode_base_64(
9952) usize {9952) usize {
9953 const src = source_ptr[0..source_len];9953 const src = source_ptr[0..source_len];
9954 const dest = dest_ptr[0..dest_len];9954 const dest = dest_ptr[0..dest_len];
9955 const base64_decoder = base64.standard_decoder_unsafe;9955 const base64_decoder = base64.standard.Decoder;
9956 const decoded_size = base64_decoder.calcSize(src);9956 const decoded_size = base64_decoder.calcSizeForSlice(src) catch unreachable;
9957 base64_decoder.decode(dest[0..decoded_size], src);9957 base64_decoder.decode(dest[0..decoded_size], src) catch unreachable;
9958 return decoded_size;9958 return decoded_size;
9959}9959}
9960 {#code_end#}9960 {#code_end#}
lib/std/array_hash_map.zig+13-13
...@@ -687,8 +687,9 @@ pub fn ArrayHashMapUnmanaged(...@@ -687,8 +687,9 @@ pub fn ArrayHashMapUnmanaged(
687687
688 /// Removes the last inserted `Entry` in the hash map and returns it.688 /// Removes the last inserted `Entry` in the hash map and returns it.
689 pub fn pop(self: *Self) Entry {689 pub fn pop(self: *Self) Entry {
690 const top = self.entries.pop();690 const top = self.entries.items[self.entries.items.len - 1];
691 _ = self.removeWithHash(top.key, top.hash, .index_only);691 _ = self.removeWithHash(top.key, top.hash, .index_only);
692 self.entries.items.len -= 1;
692 return top;693 return top;
693 }694 }
694695
...@@ -1258,19 +1259,18 @@ test "pop" {...@@ -1258,19 +1259,18 @@ test "pop" {
1258 var map = AutoArrayHashMap(i32, i32).init(std.testing.allocator);1259 var map = AutoArrayHashMap(i32, i32).init(std.testing.allocator);
1259 defer map.deinit();1260 defer map.deinit();
12601261
1261 testing.expect((try map.fetchPut(1, 11)) == null);1262 // Insert just enough entries so that the map expands. Afterwards,
1262 testing.expect((try map.fetchPut(2, 22)) == null);1263 // pop all entries out of the map.
1263 testing.expect((try map.fetchPut(3, 33)) == null);
1264 testing.expect((try map.fetchPut(4, 44)) == null);
12651264
1266 const pop1 = map.pop();1265 var i: i32 = 0;
1267 testing.expect(pop1.key == 4 and pop1.value == 44);1266 while (i < 9) : (i += 1) {
1268 const pop2 = map.pop();1267 testing.expect((try map.fetchPut(i, i)) == null);
1269 testing.expect(pop2.key == 3 and pop2.value == 33);1268 }
1270 const pop3 = map.pop();1269
1271 testing.expect(pop3.key == 2 and pop3.value == 22);1270 while (i > 0) : (i -= 1) {
1272 const pop4 = map.pop();1271 const pop = map.pop();
1273 testing.expect(pop4.key == 1 and pop4.value == 11);1272 testing.expect(pop.key == i - 1 and pop.value == i - 1);
1273 }
1274}1274}
12751275
1276test "reIndex" {1276test "reIndex" {
lib/std/base64.zig+322-324
...@@ -8,454 +8,452 @@ const assert = std.debug.assert;...@@ -8,454 +8,452 @@ const assert = std.debug.assert;
8const testing = std.testing;8const testing = std.testing;
9const mem = std.mem;9const mem = std.mem;
1010
11pub const standard_alphabet_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";11pub const Error = error{
12pub const standard_pad_char = '=';12 InvalidCharacter,
13pub const standard_encoder = Base64Encoder.init(standard_alphabet_chars, standard_pad_char);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
15pub const Base64Encoder = struct {81pub const Base64Encoder = struct {
16 alphabet_chars: []const u8,82 alphabet_chars: [64]u8,
17 pad_char: u8,83 pad_char: ?u8,
1884
19 /// a bunch of assertions, then simply pass the data right through.85 /// A bunch of assertions, then simply pass the data right through.
20 pub fn init(alphabet_chars: []const u8, pad_char: u8) Base64Encoder {86 pub fn init(alphabet_chars: [64]u8, pad_char: ?u8) Base64Encoder {
21 assert(alphabet_chars.len == 64);87 assert(alphabet_chars.len == 64);
22 var char_in_alphabet = [_]bool{false} ** 256;88 var char_in_alphabet = [_]bool{false} ** 256;
23 for (alphabet_chars) |c| {89 for (alphabet_chars) |c| {
24 assert(!char_in_alphabet[c]);90 assert(!char_in_alphabet[c]);
25 assert(c != pad_char);91 assert(pad_char == null or c != pad_char.?);
26 char_in_alphabet[c] = true;92 char_in_alphabet[c] = true;
27 }93 }
28
29 return Base64Encoder{94 return Base64Encoder{
30 .alphabet_chars = alphabet_chars,95 .alphabet_chars = alphabet_chars,
31 .pad_char = pad_char,96 .pad_char = pad_char,
32 };97 };
33 }98 }
3499
35 /// ceil(source_len * 4/3)100 /// Compute the encoded length
36 pub fn calcSize(source_len: usize) usize {101 pub fn calcSize(encoder: *const Base64Encoder, source_len: usize) usize {
37 return @divTrunc(source_len + 2, 3) * 4;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 }
38 }108 }
39109
40 /// dest.len must be what you get from ::calcSize.110 /// dest.len must at least be what you get from ::calcSize.
41 pub fn encode(encoder: *const Base64Encoder, dest: []u8, source: []const u8) []const u8 {111 pub fn encode(encoder: *const Base64Encoder, dest: []u8, source: []const u8) []const u8 {
42 assert(dest.len >= Base64Encoder.calcSize(source.len));112 const out_len = encoder.calcSize(source.len);
43113 assert(dest.len >= out_len);
44 var i: usize = 0;114
45 var out_index: usize = 0;115 const nibbles = source.len / 3;
46 while (i + 2 < source.len) : (i += 3) {116 const leftover = source.len - 3 * nibbles;
47 dest[out_index] = encoder.alphabet_chars[(source[i] >> 2) & 0x3f];117
48 out_index += 1;118 var acc: u12 = 0;
49119 var acc_len: u4 = 0;
50 dest[out_index] = encoder.alphabet_chars[((source[i] & 0x3) << 4) | ((source[i + 1] & 0xf0) >> 4)];120 var out_idx: usize = 0;
51 out_index += 1;121 for (source) |v| {
52122 acc = (acc << 8) + v;
53 dest[out_index] = encoder.alphabet_chars[((source[i + 1] & 0xf) << 2) | ((source[i + 2] & 0xc0) >> 6)];123 acc_len += 8;
54 out_index += 1;124 while (acc_len >= 6) {
55125 acc_len -= 6;
56 dest[out_index] = encoder.alphabet_chars[source[i + 2] & 0x3f];126 dest[out_idx] = encoder.alphabet_chars[@truncate(u6, (acc >> acc_len))];
57 out_index += 1;127 out_idx += 1;
128 }
58 }129 }
59130 if (acc_len > 0) {
60 if (i < source.len) {131 dest[out_idx] = encoder.alphabet_chars[@truncate(u6, (acc << 6 - acc_len))];
61 dest[out_index] = encoder.alphabet_chars[(source[i] >> 2) & 0x3f];132 out_idx += 1;
62 out_index += 1;133 }
63134 if (encoder.pad_char) |pad_char| {
64 if (i + 1 == source.len) {135 for (dest[out_idx..]) |*pad| {
65 dest[out_index] = encoder.alphabet_chars[(source[i] & 0x3) << 4];136 pad.* = pad_char;
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;
76 }137 }
77
78 dest[out_index] = encoder.pad_char;
79 out_index += 1;
80 }138 }
81 return dest[0..out_index];139 return dest[0..out_len];
82 }140 }
83};141};
84142
85pub const standard_decoder = Base64Decoder.init(standard_alphabet_chars, standard_pad_char);
86
87pub const Base64Decoder = struct {143pub const Base64Decoder = struct {
144 const invalid_char: u8 = 0xff;
145
88 /// e.g. 'A' => 0.146 /// 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.
90 char_to_index: [256]u8,148 char_to_index: [256]u8,
149 pad_char: ?u8,
91150
92 /// true only for the 64 chars in the alphabet, not the pad char.151 pub fn init(alphabet_chars: [64]u8, pad_char: ?u8) Base64Decoder {
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
99 var result = Base64Decoder{152 var result = Base64Decoder{
100 .char_to_index = undefined,153 .char_to_index = [_]u8{invalid_char} ** 256,
101 .char_in_alphabet = [_]bool{false} ** 256,
102 .pad_char = pad_char,154 .pad_char = pad_char,
103 };155 };
104156
157 var char_in_alphabet = [_]bool{false} ** 256;
105 for (alphabet_chars) |c, i| {158 for (alphabet_chars) |c, i| {
106 assert(!result.char_in_alphabet[c]);159 assert(!char_in_alphabet[c]);
107 assert(c != pad_char);160 assert(pad_char == null or c != pad_char.?);
108161
109 result.char_to_index[c] = @intCast(u8, i);162 result.char_to_index[c] = @intCast(u8, i);
110 result.char_in_alphabet[c] = true;163 char_in_alphabet[c] = true;
111 }164 }
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 }
113 return result;179 return result;
114 }180 }
115181
116 /// If the encoded buffer is detected to be invalid, returns error.InvalidPadding.182 /// Return the exact decoded size for a slice.
117 pub fn calcSize(decoder: *const Base64Decoder, source: []const u8) !usize {183 /// `InvalidPadding` is returned if the input length is not valid.
118 if (source.len % 4 != 0) return error.InvalidPadding;184 pub fn calcSizeForSlice(decoder: *const Base64Decoder, source: []const u8) Error!usize {
119 return calcDecodedSizeExactUnsafe(source, decoder.pad_char);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;
120 }192 }
121193
122 /// dest.len must be what you get from ::calcSize.194 /// dest.len must be what you get from ::calcSize.
123 /// invalid characters result in error.InvalidCharacter.195 /// invalid characters result in error.InvalidCharacter.
124 /// invalid padding results in error.InvalidPadding.196 /// invalid padding results in error.InvalidPadding.
125 pub fn decode(decoder: *const Base64Decoder, dest: []u8, source: []const u8) !void {197 pub fn decode(decoder: *const Base64Decoder, dest: []u8, source: []const u8) Error!void {
126 assert(dest.len == (decoder.calcSize(source) catch unreachable));198 if (decoder.pad_char != null and source.len % 4 != 0) return error.InvalidPadding;
127 assert(source.len % 4 == 0);199 var acc: u12 = 0;
128200 var acc_len: u4 = 0;
129 var src_cursor: usize = 0;201 var dest_idx: usize = 0;
130 var dest_cursor: usize = 0;202 var leftover_idx: ?usize = null;
131203 for (source) |c, src_idx| {
132 while (src_cursor < source.len) : (src_cursor += 4) {204 const d = decoder.char_to_index[c];
133 if (!decoder.char_in_alphabet[source[src_cursor + 0]]) return error.InvalidCharacter;205 if (d == invalid_char) {
134 if (!decoder.char_in_alphabet[source[src_cursor + 1]]) return error.InvalidCharacter;206 if (decoder.pad_char == null or c != decoder.pad_char.?) return error.InvalidCharacter;
135 if (src_cursor < source.len - 4 or source[src_cursor + 3] != decoder.pad_char) {207 leftover_idx = src_idx;
136 // common case208 break;
137 if (!decoder.char_in_alphabet[source[src_cursor + 2]]) return error.InvalidCharacter;209 }
138 if (!decoder.char_in_alphabet[source[src_cursor + 3]]) return error.InvalidCharacter;210 acc = (acc << 6) + d;
139 dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 | decoder.char_to_index[source[src_cursor + 1]] >> 4;211 acc_len += 6;
140 dest[dest_cursor + 1] = decoder.char_to_index[source[src_cursor + 1]] << 4 | decoder.char_to_index[source[src_cursor + 2]] >> 2;212 if (acc_len >= 8) {
141 dest[dest_cursor + 2] = decoder.char_to_index[source[src_cursor + 2]] << 6 | decoder.char_to_index[source[src_cursor + 3]];213 acc_len -= 8;
142 dest_cursor += 3;214 dest[dest_idx] = @truncate(u8, acc >> acc_len);
143 } else if (source[src_cursor + 2] != decoder.pad_char) {215 dest_idx += 1;
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;
155 }216 }
156 }217 }
157218 if (acc_len > 4 or (acc & (@as(u12, 1) << acc_len) - 1) != 0) {
158 assert(src_cursor == source.len);219 return error.InvalidPadding;
159 assert(dest_cursor == dest.len);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 }
160 }235 }
161};236};
162237
163pub const Base64DecoderWithIgnore = struct {238pub const Base64DecoderWithIgnore = struct {
164 decoder: Base64Decoder,239 decoder: Base64Decoder,
165 char_is_ignored: [256]bool,240 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 {
167 var result = Base64DecoderWithIgnore{243 var result = Base64DecoderWithIgnore{
168 .decoder = Base64Decoder.init(alphabet_chars, pad_char),244 .decoder = Base64Decoder.init(alphabet_chars, pad_char),
169 .char_is_ignored = [_]bool{false} ** 256,245 .char_is_ignored = [_]bool{false} ** 256,
170 };246 };
171
172 for (ignore_chars) |c| {247 for (ignore_chars) |c| {
173 assert(!result.decoder.char_in_alphabet[c]);248 assert(result.decoder.char_to_index[c] == Base64Decoder.invalid_char);
174 assert(!result.char_is_ignored[c]);249 assert(!result.char_is_ignored[c]);
175 assert(result.decoder.pad_char != c);250 assert(result.decoder.pad_char != c);
176 result.char_is_ignored[c] = true;251 result.char_is_ignored[c] = true;
177 }252 }
178
179 return result;253 return result;
180 }254 }
181255
182 /// If no characters end up being ignored or padding, this will be the exact decoded size.256 /// Return the maximum possible decoded size for a given input length - The actual length may be less if the input includes padding
183 pub fn calcSizeUpperBound(encoded_len: usize) usize {257 /// `InvalidPadding` is returned if the input length is not valid.
184 return @divTrunc(encoded_len, 4) * 3;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;
185 }265 }
186266
187 /// Invalid characters that are not ignored result in error.InvalidCharacter.267 /// Invalid characters that are not ignored result in error.InvalidCharacter.
188 /// Invalid padding results in error.InvalidPadding.268 /// 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.
190 /// Returns the number of bytes written to dest.270 /// 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 {
192 const decoder = &decoder_with_ignore.decoder;272 const decoder = &decoder_with_ignore.decoder;
193273 var acc: u12 = 0;
194 var src_cursor: usize = 0;274 var acc_len: u4 = 0;
195 var dest_cursor: usize = 0;275 var dest_idx: usize = 0;
196276 var leftover_idx: ?usize = null;
197 while (true) {277 for (source) |c, src_idx| {
198 // get the next 4 chars, if available278 if (decoder_with_ignore.char_is_ignored[c]) continue;
199 var next_4_chars: [4]u8 = undefined;279 const d = decoder.char_to_index[c];
200 var available_chars: usize = 0;280 if (d == Base64Decoder.invalid_char) {
201 var pad_char_count: usize = 0;281 if (decoder.pad_char == null or c != decoder.pad_char.?) return error.InvalidCharacter;
202 while (available_chars < 4 and src_cursor < source.len) {282 leftover_idx = src_idx;
203 var c = source[src_cursor];283 break;
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;
229 }284 }
230285 acc = (acc << 6) + d;
231 switch (available_chars) {286 acc_len += 6;
232 4 => {287 if (acc_len >= 8) {
233 // common case288 if (dest_idx == dest.len) return error.NoSpaceLeft;
234 if (dest_cursor + 3 > dest.len) return error.OutputTooSmall;289 acc_len -= 8;
235 assert(pad_char_count == 0);290 dest[dest_idx] = @truncate(u8, acc >> acc_len);
236 dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 | decoder.char_to_index[next_4_chars[1]] >> 4;291 dest_idx += 1;
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,
267 }292 }
268 }293 }
269294 if (acc_len > 4 or (acc & (@as(u12, 1) << acc_len) - 1) != 0) {
270 assert(src_cursor == source.len);295 return error.InvalidPadding;
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);
293 }296 }
294 return result;297 const padding_len = acc_len / 2;
295 }298 if (leftover_idx == null) {
296299 if (decoder.pad_char != null and padding_len != 0) return error.InvalidPadding;
297 /// The source buffer must be valid.300 return dest_idx;
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;
313 }301 }
314302 var leftover = source[leftover_idx.?..];
315 while (in_buf_len > 4) {303 if (decoder.pad_char) |pad_char| {
316 dest[dest_index] = decoder.char_to_index[source[src_index + 0]] << 2 | decoder.char_to_index[source[src_index + 1]] >> 4;304 var padding_chars: usize = 0;
317 dest_index += 1;305 var i: usize = 0;
318306 for (leftover) |c| {
319 dest[dest_index] = decoder.char_to_index[source[src_index + 1]] << 4 | decoder.char_to_index[source[src_index + 2]] >> 2;307 if (decoder_with_ignore.char_is_ignored[c]) continue;
320 dest_index += 1;308 if (c != pad_char) {
321309 return if (c == Base64Decoder.invalid_char) error.InvalidCharacter else error.InvalidPadding;
322 dest[dest_index] = decoder.char_to_index[source[src_index + 2]] << 6 | decoder.char_to_index[source[src_index + 3]];310 }
323 dest_index += 1;311 padding_chars += 1;
324312 }
325 src_index += 4;313 if (padding_chars != padding_len) return error.InvalidPadding;
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;
340 }314 }
315 return dest_idx;
341 }316 }
342};317};
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
356test "base64" {319test "base64" {
357 @setEvalBranchQuota(8000);320 @setEvalBranchQuota(8000);
358 testBase64() catch unreachable;321 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;
360}329}
361330
362fn testBase64() !void {331fn testBase64() !void {
363 try testAllApis("", "");332 const codecs = standard;
364 try testAllApis("f", "Zg==");333
365 try testAllApis("fo", "Zm8=");334 try testAllApis(codecs, "", "");
366 try testAllApis("foo", "Zm9v");335 try testAllApis(codecs, "f", "Zg==");
367 try testAllApis("foob", "Zm9vYg==");336 try testAllApis(codecs, "fo", "Zm8=");
368 try testAllApis("fooba", "Zm9vYmE=");337 try testAllApis(codecs, "foo", "Zm9v");
369 try testAllApis("foobar", "Zm9vYmFy");338 try testAllApis(codecs, "foob", "Zm9vYg==");
370339 try testAllApis(codecs, "fooba", "Zm9vYmE=");
371 try testDecodeIgnoreSpace("", " ");340 try testAllApis(codecs, "foobar", "Zm9vYmFy");
372 try testDecodeIgnoreSpace("f", "Z g= =");341
373 try testDecodeIgnoreSpace("fo", " Zm8=");342 try testDecodeIgnoreSpace(codecs, "", " ");
374 try testDecodeIgnoreSpace("foo", "Zm9v ");343 try testDecodeIgnoreSpace(codecs, "f", "Z g= =");
375 try testDecodeIgnoreSpace("foob", "Zm9vYg = = ");344 try testDecodeIgnoreSpace(codecs, "fo", " Zm8=");
376 try testDecodeIgnoreSpace("fooba", "Zm9v YmE=");345 try testDecodeIgnoreSpace(codecs, "foo", "Zm9v ");
377 try testDecodeIgnoreSpace("foobar", " Z m 9 v Y m F y ");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
379 // test getting some api errors386 // test getting some api errors
380 try testError("A", error.InvalidPadding);387 try testError(codecs, "A", error.InvalidPadding);
381 try testError("AA", error.InvalidPadding);388 try testError(codecs, "AAA=", error.InvalidCharacter);
382 try testError("AAA", error.InvalidPadding);389 try testError(codecs, "A..A", error.InvalidCharacter);
383 try testError("A..A", error.InvalidCharacter);390 try testError(codecs, "AA=A", error.InvalidCharacter);
384 try testError("AA=A", error.InvalidCharacter);391 try testError(codecs, "AA/=", error.InvalidCharacter);
385 try testError("AA/=", error.InvalidPadding);392 try testError(codecs, "A/==", error.InvalidCharacter);
386 try testError("A/==", error.InvalidPadding);393 try testError(codecs, "A===", error.InvalidCharacter);
387 try testError("A===", error.InvalidCharacter);394 try testError(codecs, "====", error.InvalidCharacter);
388 try testError("====", error.InvalidCharacter);395
389396 try testNoSpaceLeftError(codecs, "AA");
390 try testOutputTooSmallError("AA==");397 try testNoSpaceLeftError(codecs, "AAA");
391 try testOutputTooSmallError("AAA=");398 try testNoSpaceLeftError(codecs, "AAAA");
392 try testOutputTooSmallError("AAAA");399 try testNoSpaceLeftError(codecs, "AAAAAA");
393 try testOutputTooSmallError("AAAAAA==");
394}400}
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 {
397 // Base64Encoder403 // Base64Encoder
398 {404 {
399 var buffer: [0x100]u8 = undefined;405 var buffer: [0x100]u8 = undefined;
400 const encoded = standard_encoder.encode(&buffer, expected_decoded);406 const encoded = codecs.Encoder.encode(&buffer, expected_decoded);
401 testing.expectEqualSlices(u8, expected_encoded, encoded);407 testing.expectEqualSlices(u8, expected_encoded, encoded);
402 }408 }
403409
404 // Base64Decoder410 // Base64Decoder
405 {411 {
406 var buffer: [0x100]u8 = undefined;412 var buffer: [0x100]u8 = undefined;
407 var decoded = buffer[0..try standard_decoder.calcSize(expected_encoded)];413 var decoded = buffer[0..try codecs.Decoder.calcSizeForSlice(expected_encoded)];
408 try standard_decoder.decode(decoded, expected_encoded);414 try codecs.Decoder.decode(decoded, expected_encoded);
409 testing.expectEqualSlices(u8, expected_decoded, decoded);415 testing.expectEqualSlices(u8, expected_decoded, decoded);
410 }416 }
411417
412 // Base64DecoderWithIgnore418 // Base64DecoderWithIgnore
413 {419 {
414 const standard_decoder_ignore_nothing = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, "");420 const decoder_ignore_nothing = codecs.decoderWithIgnore("");
415 var buffer: [0x100]u8 = undefined;421 var buffer: [0x100]u8 = undefined;
416 var decoded = buffer[0..Base64DecoderWithIgnore.calcSizeUpperBound(expected_encoded.len)];422 var decoded = buffer[0..try decoder_ignore_nothing.calcSizeUpperBound(expected_encoded.len)];
417 var written = try standard_decoder_ignore_nothing.decode(decoded, expected_encoded);423 var written = try decoder_ignore_nothing.decode(decoded, expected_encoded);
418 testing.expect(written <= decoded.len);424 testing.expect(written <= decoded.len);
419 testing.expectEqualSlices(u8, expected_decoded, decoded[0..written]);425 testing.expectEqualSlices(u8, expected_decoded, decoded[0..written]);
420 }426 }
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 }
429}427}
430428
431fn testDecodeIgnoreSpace(expected_decoded: []const u8, encoded: []const u8) !void {429fn testDecodeIgnoreSpace(codecs: Codecs, expected_decoded: []const u8, encoded: []const u8) !void {
432 const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, " ");430 const decoder_ignore_space = codecs.decoderWithIgnore(" ");
433 var buffer: [0x100]u8 = undefined;431 var buffer: [0x100]u8 = undefined;
434 var decoded = buffer[0..Base64DecoderWithIgnore.calcSizeUpperBound(encoded.len)];432 var decoded = buffer[0..try decoder_ignore_space.calcSizeUpperBound(encoded.len)];
435 var written = try standard_decoder_ignore_space.decode(decoded, encoded);433 var written = try decoder_ignore_space.decode(decoded, encoded);
436 testing.expectEqualSlices(u8, expected_decoded, decoded[0..written]);434 testing.expectEqualSlices(u8, expected_decoded, decoded[0..written]);
437}435}
438436
439fn testError(encoded: []const u8, expected_err: anyerror) !void {437fn testError(codecs: Codecs, encoded: []const u8, expected_err: anyerror) !void {
440 const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, " ");438 const decoder_ignore_space = codecs.decoderWithIgnore(" ");
441 var buffer: [0x100]u8 = undefined;439 var buffer: [0x100]u8 = undefined;
442 if (standard_decoder.calcSize(encoded)) |decoded_size| {440 if (codecs.Decoder.calcSizeForSlice(encoded)) |decoded_size| {
443 var decoded = buffer[0..decoded_size];441 var decoded = buffer[0..decoded_size];
444 if (standard_decoder.decode(decoded, encoded)) |_| {442 if (codecs.Decoder.decode(decoded, encoded)) |_| {
445 return error.ExpectedError;443 return error.ExpectedError;
446 } else |err| if (err != expected_err) return err;444 } else |err| if (err != expected_err) return err;
447 } else |err| if (err != expected_err) return err;445 } 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)) |_| {
450 return error.ExpectedError;448 return error.ExpectedError;
451 } else |err| if (err != expected_err) return err;449 } else |err| if (err != expected_err) return err;
452}450}
453451
454fn testOutputTooSmallError(encoded: []const u8) !void {452fn testNoSpaceLeftError(codecs: Codecs, encoded: []const u8) !void {
455 const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, " ");453 const decoder_ignore_space = codecs.decoderWithIgnore(" ");
456 var buffer: [0x100]u8 = undefined;454 var buffer: [0x100]u8 = undefined;
457 var decoded = buffer[0 .. calcDecodedSizeExactUnsafe(encoded, standard_pad_char) - 1];455 var decoded = buffer[0 .. (try codecs.Decoder.calcSizeForSlice(encoded)) - 1];
458 if (standard_decoder_ignore_space.decode(decoded, encoded)) |_| {456 if (decoder_ignore_space.decode(decoded, encoded)) |_| {
459 return error.ExpectedError;457 return error.ExpectedError;
460 } else |err| if (err != error.OutputTooSmall) return err;458 } else |err| if (err != error.NoSpaceLeft) return err;
461}459}
lib/std/bit_set.zig+21-7
...@@ -176,7 +176,7 @@ pub fn IntegerBitSet(comptime size: u16) type {...@@ -176,7 +176,7 @@ pub fn IntegerBitSet(comptime size: u16) type {
176 /// The default options (.{}) will iterate indices of set bits in176 /// The default options (.{}) will iterate indices of set bits in
177 /// ascending order. Modifications to the underlying bit set may177 /// ascending order. Modifications to the underlying bit set may
178 /// or may not be observed by the iterator.178 /// or may not be observed by the iterator.
179 pub fn iterator(self: *const Self, comptime options: IteratorOptions) Iterator(options.direction) {179 pub fn iterator(self: *const Self, comptime options: IteratorOptions) Iterator(options) {
180 return .{180 return .{
181 .bits_remain = switch (options.kind) {181 .bits_remain = switch (options.kind) {
182 .set => self.mask,182 .set => self.mask,
...@@ -185,7 +185,11 @@ pub fn IntegerBitSet(comptime size: u16) type {...@@ -185,7 +185,11 @@ pub fn IntegerBitSet(comptime size: u16) type {
185 };185 };
186 }186 }
187187
188 fn Iterator(comptime direction: IteratorOptions.Direction) type {188 pub fn Iterator(comptime options: IteratorOptions) type {
189 return SingleWordIterator(options.direction);
190 }
191
192 fn SingleWordIterator(comptime direction: IteratorOptions.Direction) type {
189 return struct {193 return struct {
190 const IterSelf = @This();194 const IterSelf = @This();
191 // all bits which have not yet been iterated over195 // all bits which have not yet been iterated over
...@@ -425,8 +429,12 @@ pub fn ArrayBitSet(comptime MaskIntType: type, comptime size: usize) type {...@@ -425,8 +429,12 @@ pub fn ArrayBitSet(comptime MaskIntType: type, comptime size: usize) type {
425 /// The default options (.{}) will iterate indices of set bits in429 /// The default options (.{}) will iterate indices of set bits in
426 /// ascending order. Modifications to the underlying bit set may430 /// ascending order. Modifications to the underlying bit set may
427 /// or may not be observed by the iterator.431 /// or may not be observed by the iterator.
428 pub fn iterator(self: *const Self, comptime options: IteratorOptions) BitSetIterator(MaskInt, options) {432 pub fn iterator(self: *const Self, comptime options: IteratorOptions) Iterator(options) {
429 return BitSetIterator(MaskInt, options).init(&self.masks, last_item_mask);433 return Iterator(options).init(&self.masks, last_item_mask);
434 }
435
436 pub fn Iterator(comptime options: IteratorOptions) type {
437 return BitSetIterator(MaskInt, options);
430 }438 }
431439
432 fn maskBit(index: usize) MaskInt {440 fn maskBit(index: usize) MaskInt {
...@@ -700,11 +708,15 @@ pub const DynamicBitSetUnmanaged = struct {...@@ -700,11 +708,15 @@ pub const DynamicBitSetUnmanaged = struct {
700 /// ascending order. Modifications to the underlying bit set may708 /// ascending order. Modifications to the underlying bit set may
701 /// or may not be observed by the iterator. Resizing the underlying709 /// or may not be observed by the iterator. Resizing the underlying
702 /// bit set invalidates the iterator.710 /// bit set invalidates the iterator.
703 pub fn iterator(self: *const Self, comptime options: IteratorOptions) BitSetIterator(MaskInt, options) {711 pub fn iterator(self: *const Self, comptime options: IteratorOptions) Iterator(options) {
704 const num_masks = numMasks(self.bit_length);712 const num_masks = numMasks(self.bit_length);
705 const padding_bits = num_masks * @bitSizeOf(MaskInt) - self.bit_length;713 const padding_bits = num_masks * @bitSizeOf(MaskInt) - self.bit_length;
706 const last_item_mask = (~@as(MaskInt, 0)) >> @intCast(ShiftInt, padding_bits);714 const last_item_mask = (~@as(MaskInt, 0)) >> @intCast(ShiftInt, padding_bits);
707 return BitSetIterator(MaskInt, options).init(self.masks[0..num_masks], last_item_mask);715 return Iterator(options).init(self.masks[0..num_masks], last_item_mask);
716 }
717
718 pub fn Iterator(comptime options: IteratorOptions) type {
719 return BitSetIterator(MaskInt, options);
708 }720 }
709721
710 fn maskBit(index: usize) MaskInt {722 fn maskBit(index: usize) MaskInt {
...@@ -858,9 +870,11 @@ pub const DynamicBitSet = struct {...@@ -858,9 +870,11 @@ pub const DynamicBitSet = struct {
858 /// ascending order. Modifications to the underlying bit set may870 /// ascending order. Modifications to the underlying bit set may
859 /// or may not be observed by the iterator. Resizing the underlying871 /// or may not be observed by the iterator. Resizing the underlying
860 /// bit set invalidates the iterator.872 /// bit set invalidates the iterator.
861 pub fn iterator(self: *Self, comptime options: IteratorOptions) BitSetIterator(MaskInt, options) {873 pub fn iterator(self: *Self, comptime options: IteratorOptions) Iterator(options) {
862 return self.unmanaged.iterator(options);874 return self.unmanaged.iterator(options);
863 }875 }
876
877 pub const Iterator = DynamicBitSetUnmanaged.Iterator;
864};878};
865879
866/// Options for configuring an iterator over a bit set880/// Options for configuring an iterator over a bit set
lib/std/build.zig+7-16
...@@ -51,7 +51,7 @@ pub const Builder = struct {...@@ -51,7 +51,7 @@ pub const Builder = struct {
51 default_step: *Step,51 default_step: *Step,
52 env_map: *BufMap,52 env_map: *BufMap,
53 top_level_steps: ArrayList(*TopLevelStep),53 top_level_steps: ArrayList(*TopLevelStep),
54 install_prefix: ?[]const u8,54 install_prefix: []const u8,
55 dest_dir: ?[]const u8,55 dest_dir: ?[]const u8,
56 lib_dir: []const u8,56 lib_dir: []const u8,
57 exe_dir: []const u8,57 exe_dir: []const u8,
...@@ -156,7 +156,7 @@ pub const Builder = struct {...@@ -156,7 +156,7 @@ pub const Builder = struct {
156 .default_step = undefined,156 .default_step = undefined,
157 .env_map = env_map,157 .env_map = env_map,
158 .search_prefixes = ArrayList([]const u8).init(allocator),158 .search_prefixes = ArrayList([]const u8).init(allocator),
159 .install_prefix = null,159 .install_prefix = undefined,
160 .lib_dir = undefined,160 .lib_dir = undefined,
161 .exe_dir = undefined,161 .exe_dir = undefined,
162 .h_dir = undefined,162 .h_dir = undefined,
...@@ -190,22 +190,13 @@ pub const Builder = struct {...@@ -190,22 +190,13 @@ pub const Builder = struct {
190 }190 }
191191
192 /// This function is intended to be called by std/special/build_runner.zig, not a build.zig file.192 /// 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 {193 pub fn resolveInstallPrefix(self: *Builder, install_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 {
199 if (self.dest_dir) |dest_dir| {194 if (self.dest_dir) |dest_dir| {
200 const install_prefix = self.install_prefix orelse "/usr";195 self.install_prefix = install_prefix orelse "/usr";
201 self.install_path = fs.path.join(self.allocator, &[_][]const u8{ dest_dir, install_prefix }) catch unreachable;196 self.install_path = fs.path.join(self.allocator, &[_][]const u8{ dest_dir, self.install_prefix }) catch unreachable;
202 } else {197 } else {
203 const install_prefix = self.install_prefix orelse blk: {198 self.install_prefix = install_prefix orelse self.cache_root;
204 const p = self.cache_root;199 self.install_path = self.install_prefix;
205 self.install_prefix = p;
206 break :blk p;
207 };
208 self.install_path = install_prefix;
209 }200 }
210 self.lib_dir = fs.path.join(self.allocator, &[_][]const u8{ self.install_path, "lib" }) catch unreachable;201 self.lib_dir = fs.path.join(self.allocator, &[_][]const u8{ self.install_path, "lib" }) catch unreachable;
211 self.exe_dir = fs.path.join(self.allocator, &[_][]const u8{ self.install_path, "bin" }) catch unreachable;202 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(...@@ -295,9 +295,9 @@ pub extern "c" fn kevent(
295) c_int;295) c_int;
296296
297pub extern "c" fn getaddrinfo(297pub extern "c" fn getaddrinfo(
298 noalias node: [*:0]const u8,298 noalias node: ?[*:0]const u8,
299 noalias service: [*:0]const u8,299 noalias service: ?[*:0]const u8,
300 noalias hints: *const addrinfo,300 noalias hints: ?*const addrinfo,
301 noalias res: **addrinfo,301 noalias res: **addrinfo,
302) EAI;302) 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...@@ -140,7 +140,7 @@ pub fn __builtin_object_size(ptr: ?*const c_void, ty: c_int) callconv(.Inline) u
140 // If it is not possible to determine which objects ptr points to at compile time,140 // If it is not possible to determine which objects ptr points to at compile time,
141 // __builtin_object_size should return (size_t) -1 for type 0 or 1 and (size_t) 0141 // __builtin_object_size should return (size_t) -1 for type 0 or 1 and (size_t) 0
142 // for type 2 or 3.142 // for type 2 or 3.
143 if (ty == 0 or ty == 1) return @bitCast(usize, -@as(c_long, 1));143 if (ty == 0 or ty == 1) return @bitCast(usize, -@as(isize, 1));
144 if (ty == 2 or ty == 3) return 0;144 if (ty == 2 or ty == 3) return 0;
145 unreachable;145 unreachable;
146}146}
...@@ -188,3 +188,9 @@ pub fn __builtin_memcpy(...@@ -188,3 +188,9 @@ pub fn __builtin_memcpy(
188pub fn __builtin_expect(expr: c_long, c: c_long) callconv(.Inline) c_long {188pub fn __builtin_expect(expr: c_long, c: c_long) callconv(.Inline) c_long {
189 return expr;189 return expr;
190}190}
191
192// __builtin_alloca_with_align is not currently implemented.
193// It is used in a run-translated-c test and a test-translate-c test to ensure that non-implemented
194// builtins are correctly demoted. If you implement __builtin_alloca_with_align, please update the
195// run-translated-c test and the test-translate-c test to use a different non-implemented builtin.
196// pub fn __builtin_alloca_with_align(size: usize, alignment: usize) callconv(.Inline) *c_void {}
lib/std/crypto.zig+17-3
...@@ -24,8 +24,12 @@ pub const aead = struct {...@@ -24,8 +24,12 @@ pub const aead = struct {
24 pub const Gimli = @import("crypto/gimli.zig").Aead;24 pub const Gimli = @import("crypto/gimli.zig").Aead;
2525
26 pub const chacha_poly = struct {26 pub const chacha_poly = struct {
27 pub const ChaCha20Poly1305 = @import("crypto/chacha20.zig").Chacha20Poly1305;27 pub const ChaCha20Poly1305 = @import("crypto/chacha20.zig").ChaCha20Poly1305;
28 pub const XChaCha20Poly1305 = @import("crypto/chacha20.zig").XChacha20Poly1305;28 pub const ChaCha12Poly1305 = @import("crypto/chacha20.zig").ChaCha12Poly1305;
29 pub const ChaCha8Poly1305 = @import("crypto/chacha20.zig").ChaCha8Poly1305;
30 pub const XChaCha20Poly1305 = @import("crypto/chacha20.zig").XChaCha20Poly1305;
31 pub const XChaCha12Poly1305 = @import("crypto/chacha20.zig").XChaCha12Poly1305;
32 pub const XChaCha8Poly1305 = @import("crypto/chacha20.zig").XChaCha8Poly1305;
29 };33 };
3034
31 pub const isap = @import("crypto/isap.zig");35 pub const isap = @import("crypto/isap.zig");
...@@ -119,8 +123,14 @@ pub const sign = struct {...@@ -119,8 +123,14 @@ pub const sign = struct {
119pub const stream = struct {123pub const stream = struct {
120 pub const chacha = struct {124 pub const chacha = struct {
121 pub const ChaCha20IETF = @import("crypto/chacha20.zig").ChaCha20IETF;125 pub const ChaCha20IETF = @import("crypto/chacha20.zig").ChaCha20IETF;
126 pub const ChaCha12IETF = @import("crypto/chacha20.zig").ChaCha12IETF;
127 pub const ChaCha8IETF = @import("crypto/chacha20.zig").ChaCha8IETF;
122 pub const ChaCha20With64BitNonce = @import("crypto/chacha20.zig").ChaCha20With64BitNonce;128 pub const ChaCha20With64BitNonce = @import("crypto/chacha20.zig").ChaCha20With64BitNonce;
129 pub const ChaCha12With64BitNonce = @import("crypto/chacha20.zig").ChaCha12With64BitNonce;
130 pub const ChaCha8With64BitNonce = @import("crypto/chacha20.zig").ChaCha8With64BitNonce;
123 pub const XChaCha20IETF = @import("crypto/chacha20.zig").XChaCha20IETF;131 pub const XChaCha20IETF = @import("crypto/chacha20.zig").XChaCha20IETF;
132 pub const XChaCha12IETF = @import("crypto/chacha20.zig").XChaCha12IETF;
133 pub const XChaCha8IETF = @import("crypto/chacha20.zig").XChaCha8IETF;
124 };134 };
125135
126 pub const salsa = struct {136 pub const salsa = struct {
...@@ -144,6 +154,8 @@ pub const random = &@import("crypto/tlcsprng.zig").interface;...@@ -144,6 +154,8 @@ pub const random = &@import("crypto/tlcsprng.zig").interface;
144154
145const std = @import("std.zig");155const std = @import("std.zig");
146156
157pub const Error = @import("crypto/error.zig").Error;
158
147test "crypto" {159test "crypto" {
148 const please_windows_dont_oom = std.Target.current.os.tag == .windows;160 const please_windows_dont_oom = std.Target.current.os.tag == .windows;
149 if (please_windows_dont_oom) return error.SkipZigTest;161 if (please_windows_dont_oom) return error.SkipZigTest;
...@@ -151,7 +163,9 @@ test "crypto" {...@@ -151,7 +163,9 @@ test "crypto" {
151 inline for (std.meta.declarations(@This())) |decl| {163 inline for (std.meta.declarations(@This())) |decl| {
152 switch (decl.data) {164 switch (decl.data) {
153 .Type => |t| {165 .Type => |t| {
154 std.testing.refAllDecls(t);166 if (@typeInfo(t) != .ErrorSet) {
167 std.testing.refAllDecls(t);
168 }
155 },169 },
156 .Var => |v| {170 .Var => |v| {
157 _ = v;171 _ = v;
lib/std/crypto/25519/curve25519.zig+7-6
...@@ -4,6 +4,7 @@...@@ -4,6 +4,7 @@
4// The MIT license requires this copyright notice to be included in all copies4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.5// and substantial portions of the software.
6const std = @import("std");6const std = @import("std");
7const Error = std.crypto.Error;
78
8/// Group operations over Curve25519.9/// Group operations over Curve25519.
9pub const Curve25519 = struct {10pub const Curve25519 = struct {
...@@ -28,12 +29,12 @@ pub const Curve25519 = struct {...@@ -28,12 +29,12 @@ pub const Curve25519 = struct {
28 pub const basePoint = Curve25519{ .x = Fe.curve25519BasePoint };29 pub const basePoint = Curve25519{ .x = Fe.curve25519BasePoint };
2930
30 /// Check that the encoding of a Curve25519 point is canonical.31 /// 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 {
32 return Fe.rejectNonCanonical(s, false);33 return Fe.rejectNonCanonical(s, false);
33 }34 }
3435
35 /// Reject the neutral element.36 /// Reject the neutral element.
36 pub fn rejectIdentity(p: Curve25519) !void {37 pub fn rejectIdentity(p: Curve25519) Error!void {
37 if (p.x.isZero()) {38 if (p.x.isZero()) {
38 return error.IdentityElement;39 return error.IdentityElement;
39 }40 }
...@@ -44,7 +45,7 @@ pub const Curve25519 = struct {...@@ -44,7 +45,7 @@ pub const Curve25519 = struct {
44 return p.dbl().dbl().dbl();45 return p.dbl().dbl().dbl();
45 }46 }
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 {
48 var x1 = p.x;49 var x1 = p.x;
49 var x2 = Fe.one;50 var x2 = Fe.one;
50 var z2 = Fe.zero;51 var z2 = Fe.zero;
...@@ -85,7 +86,7 @@ pub const Curve25519 = struct {...@@ -85,7 +86,7 @@ pub const Curve25519 = struct {
85 /// way to use Curve25519 for a DH operation.86 /// way to use Curve25519 for a DH operation.
86 /// Return error.IdentityElement if the resulting point is87 /// Return error.IdentityElement if the resulting point is
87 /// the identity element.88 /// the identity element.
88 pub fn clampedMul(p: Curve25519, s: [32]u8) !Curve25519 {89 pub fn clampedMul(p: Curve25519, s: [32]u8) Error!Curve25519 {
89 var t: [32]u8 = s;90 var t: [32]u8 = s;
90 scalar.clamp(&t);91 scalar.clamp(&t);
91 return try ladder(p, t, 255);92 return try ladder(p, t, 255);
...@@ -95,14 +96,14 @@ pub const Curve25519 = struct {...@@ -95,14 +96,14 @@ pub const Curve25519 = struct {
95 /// Return error.IdentityElement if the resulting point is96 /// Return error.IdentityElement if the resulting point is
96 /// the identity element or error.WeakPublicKey if the public97 /// the identity element or error.WeakPublicKey if the public
97 /// key is a low-order point.98 /// 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 {
99 const cofactor = [_]u8{8} ++ [_]u8{0} ** 31;100 const cofactor = [_]u8{8} ++ [_]u8{0} ** 31;
100 _ = ladder(p, cofactor, 4) catch |_| return error.WeakPublicKey;101 _ = ladder(p, cofactor, 4) catch |_| return error.WeakPublicKey;
101 return try ladder(p, s, 256);102 return try ladder(p, s, 256);
102 }103 }
103104
104 /// Compute the Curve25519 equivalent to an Edwards25519 point.105 /// 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 {
106 try p.clearCofactor().rejectIdentity();107 try p.clearCofactor().rejectIdentity();
107 const one = std.crypto.ecc.Edwards25519.Fe.one;108 const one = std.crypto.ecc.Edwards25519.Fe.one;
108 const x = one.add(p.y).mul(one.sub(p.y).invert()); // xMont=(1+yEd)/(1-yEd)109 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;...@@ -8,7 +8,8 @@ const crypto = std.crypto;
8const debug = std.debug;8const debug = std.debug;
9const fmt = std.fmt;9const fmt = std.fmt;
10const mem = std.mem;10const mem = std.mem;
11const Sha512 = std.crypto.hash.sha2.Sha512;11const Sha512 = crypto.hash.sha2.Sha512;
12const Error = crypto.Error;
1213
13/// Ed25519 (EdDSA) signatures.14/// Ed25519 (EdDSA) signatures.
14pub const Ed25519 = struct {15pub const Ed25519 = struct {
...@@ -40,7 +41,7 @@ pub const Ed25519 = struct {...@@ -40,7 +41,7 @@ pub const Ed25519 = struct {
40 ///41 ///
41 /// For this reason, an EdDSA secret key is commonly called a seed,42 /// For this reason, an EdDSA secret key is commonly called a seed,
42 /// from which the actual secret is derived.43 /// 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 {
44 const ss = seed orelse ss: {45 const ss = seed orelse ss: {
45 var random_seed: [seed_length]u8 = undefined;46 var random_seed: [seed_length]u8 = undefined;
46 crypto.random.bytes(&random_seed);47 crypto.random.bytes(&random_seed);
...@@ -71,7 +72,7 @@ pub const Ed25519 = struct {...@@ -71,7 +72,7 @@ pub const Ed25519 = struct {
71 /// Sign a message using a key pair, and optional random noise.72 /// Sign a message using a key pair, and optional random noise.
72 /// Having noise creates non-standard, non-deterministic signatures,73 /// Having noise creates non-standard, non-deterministic signatures,
73 /// but has been proven to increase resilience against fault attacks.74 /// 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 {
75 const seed = key_pair.secret_key[0..seed_length];76 const seed = key_pair.secret_key[0..seed_length];
76 const public_key = key_pair.secret_key[seed_length..];77 const public_key = key_pair.secret_key[seed_length..];
77 if (!mem.eql(u8, public_key, &key_pair.public_key)) {78 if (!mem.eql(u8, public_key, &key_pair.public_key)) {
...@@ -111,8 +112,8 @@ pub const Ed25519 = struct {...@@ -111,8 +112,8 @@ pub const Ed25519 = struct {
111 }112 }
112113
113 /// Verify an Ed25519 signature given a message and a public key.114 /// Verify an Ed25519 signature given a message and a public key.
114 /// Returns error.InvalidSignature is the signature verification failed.115 /// Returns error.SignatureVerificationFailed is the signature verification failed.
115 pub fn verify(sig: [signature_length]u8, msg: []const u8, public_key: [public_length]u8) !void {116 pub fn verify(sig: [signature_length]u8, msg: []const u8, public_key: [public_length]u8) Error!void {
116 const r = sig[0..32];117 const r = sig[0..32];
117 const s = sig[32..64];118 const s = sig[32..64];
118 try Curve.scalar.rejectNonCanonical(s.*);119 try Curve.scalar.rejectNonCanonical(s.*);
...@@ -133,7 +134,7 @@ pub const Ed25519 = struct {...@@ -133,7 +134,7 @@ pub const Ed25519 = struct {
133 const ah = try a.neg().mulPublic(hram);134 const ah = try a.neg().mulPublic(hram);
134 const sb_ah = (try Curve.basePoint.mulPublic(s.*)).add(ah);135 const sb_ah = (try Curve.basePoint.mulPublic(s.*)).add(ah);
135 if (expected_r.sub(sb_ah).clearCofactor().rejectIdentity()) |_| {136 if (expected_r.sub(sb_ah).clearCofactor().rejectIdentity()) |_| {
136 return error.InvalidSignature;137 return error.SignatureVerificationFailed;
137 } else |_| {}138 } else |_| {}
138 }139 }
139140
...@@ -145,7 +146,7 @@ pub const Ed25519 = struct {...@@ -145,7 +146,7 @@ pub const Ed25519 = struct {
145 };146 };
146147
147 /// Verify several signatures in a single operation, much faster than verifying signatures one-by-one148 /// 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 {
149 var r_batch: [count][32]u8 = undefined;150 var r_batch: [count][32]u8 = undefined;
150 var s_batch: [count][32]u8 = undefined;151 var s_batch: [count][32]u8 = undefined;
151 var a_batch: [count]Curve = undefined;152 var a_batch: [count]Curve = undefined;
...@@ -200,7 +201,7 @@ pub const Ed25519 = struct {...@@ -200,7 +201,7 @@ pub const Ed25519 = struct {
200201
201 const zsb = try Curve.basePoint.mulPublic(zs_sum);202 const zsb = try Curve.basePoint.mulPublic(zs_sum);
202 if (zr.add(zah).sub(zsb).rejectIdentity()) |_| {203 if (zr.add(zah).sub(zsb).rejectIdentity()) |_| {
203 return error.InvalidSignature;204 return error.SignatureVerificationFailed;
204 } else |_| {}205 } else |_| {}
205 }206 }
206};207};
...@@ -223,7 +224,7 @@ test "ed25519 signature" {...@@ -223,7 +224,7 @@ test "ed25519 signature" {
223 var buf: [128]u8 = undefined;224 var buf: [128]u8 = undefined;
224 std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&sig)}), "10A442B4A80CC4225B154F43BEF28D2472CA80221951262EB8E0DF9091575E2687CC486E77263C3418C757522D54F84B0359236ABBBD4ACD20DC297FDCA66808");225 std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&sig)}), "10A442B4A80CC4225B154F43BEF28D2472CA80221951262EB8E0DF9091575E2687CC486E77263C3418C757522D54F84B0359236ABBBD4ACD20DC297FDCA66808");
225 try Ed25519.verify(sig, "test", key_pair.public_key);226 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));
227}228}
228229
229test "ed25519 batch verification" {230test "ed25519 batch verification" {
...@@ -251,7 +252,7 @@ test "ed25519 batch verification" {...@@ -251,7 +252,7 @@ test "ed25519 batch verification" {
251 try Ed25519.verifyBatch(2, signature_batch);252 try Ed25519.verifyBatch(2, signature_batch);
252253
253 signature_batch[1].sig = sig1;254 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));
255 }256 }
256}257}
257258
...@@ -316,7 +317,7 @@ test "ed25519 test vectors" {...@@ -316,7 +317,7 @@ test "ed25519 test vectors" {
316 .msg_hex = "9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41",317 .msg_hex = "9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41",
317 .public_key_hex = "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43",318 .public_key_hex = "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43",
318 .sig_hex = "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03be9678ac102edcd92b0210bb34d7428d12ffc5df5f37e359941266a4e35f0f",319 .sig_hex = "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03be9678ac102edcd92b0210bb34d7428d12ffc5df5f37e359941266a4e35f0f",
319 .expected = error.InvalidSignature, // 8 - non-canonical R320 .expected = error.SignatureVerificationFailed, // 8 - non-canonical R
320 },321 },
321 Vec{322 Vec{
322 .msg_hex = "9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41",323 .msg_hex = "9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41",
lib/std/crypto/25519/edwards25519.zig+11-10
...@@ -7,6 +7,7 @@ const std = @import("std");...@@ -7,6 +7,7 @@ const std = @import("std");
7const debug = std.debug;7const debug = std.debug;
8const fmt = std.fmt;8const fmt = std.fmt;
9const mem = std.mem;9const mem = std.mem;
10const Error = std.crypto.Error;
1011
11/// Group operations over Edwards25519.12/// Group operations over Edwards25519.
12pub const Edwards25519 = struct {13pub const Edwards25519 = struct {
...@@ -25,7 +26,7 @@ pub const Edwards25519 = struct {...@@ -25,7 +26,7 @@ pub const Edwards25519 = struct {
25 is_base: bool = false,26 is_base: bool = false,
2627
27 /// Decode an Edwards25519 point from its compressed (Y+sign) coordinates.28 /// 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 {
29 const z = Fe.one;30 const z = Fe.one;
30 const y = Fe.fromBytes(s);31 const y = Fe.fromBytes(s);
31 var u = y.sq();32 var u = y.sq();
...@@ -55,7 +56,7 @@ pub const Edwards25519 = struct {...@@ -55,7 +56,7 @@ pub const Edwards25519 = struct {
55 }56 }
5657
57 /// Check that the encoding of a point is canonical.58 /// 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 {
59 return Fe.rejectNonCanonical(s, true);60 return Fe.rejectNonCanonical(s, true);
60 }61 }
6162
...@@ -80,7 +81,7 @@ pub const Edwards25519 = struct {...@@ -80,7 +81,7 @@ pub const Edwards25519 = struct {
80 const identityElement = Edwards25519{ .x = Fe.zero, .y = Fe.one, .z = Fe.one, .t = Fe.zero };81 const identityElement = Edwards25519{ .x = Fe.zero, .y = Fe.one, .z = Fe.one, .t = Fe.zero };
8182
82 /// Reject the neutral element.83 /// Reject the neutral element.
83 pub fn rejectIdentity(p: Edwards25519) !void {84 pub fn rejectIdentity(p: Edwards25519) Error!void {
84 if (p.x.isZero()) {85 if (p.x.isZero()) {
85 return error.IdentityElement;86 return error.IdentityElement;
86 }87 }
...@@ -176,7 +177,7 @@ pub const Edwards25519 = struct {...@@ -176,7 +177,7 @@ pub const Edwards25519 = struct {
176 // Based on real-world benchmarks, we only use this for multi-scalar multiplication.177 // Based on real-world benchmarks, we only use this for multi-scalar multiplication.
177 // NAF could be useful to half the size of precomputation tables, but we intentionally178 // NAF could be useful to half the size of precomputation tables, but we intentionally
178 // avoid these to keep the standard library lightweight.179 // 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 {
180 std.debug.assert(vartime);181 std.debug.assert(vartime);
181 const e = nonAdjacentForm(s);182 const e = nonAdjacentForm(s);
182 var q = Edwards25519.identityElement;183 var q = Edwards25519.identityElement;
...@@ -196,7 +197,7 @@ pub const Edwards25519 = struct {...@@ -196,7 +197,7 @@ pub const Edwards25519 = struct {
196 }197 }
197198
198 // Scalar multiplication with a 4-bit window and the first 15 multiples.199 // 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 {
200 var q = Edwards25519.identityElement;201 var q = Edwards25519.identityElement;
201 var pos: usize = 252;202 var pos: usize = 252;
202 while (true) : (pos -= 4) {203 while (true) : (pos -= 4) {
...@@ -234,7 +235,7 @@ pub const Edwards25519 = struct {...@@ -234,7 +235,7 @@ pub const Edwards25519 = struct {
234 /// Multiply an Edwards25519 point by a scalar without clamping it.235 /// Multiply an Edwards25519 point by a scalar without clamping it.
235 /// Return error.WeakPublicKey if the resulting point is236 /// Return error.WeakPublicKey if the resulting point is
236 /// the identity element.237 /// the identity element.
237 pub fn mul(p: Edwards25519, s: [32]u8) !Edwards25519 {238 pub fn mul(p: Edwards25519, s: [32]u8) Error!Edwards25519 {
238 const pc = if (p.is_base) basePointPc else pc: {239 const pc = if (p.is_base) basePointPc else pc: {
239 const xpc = precompute(p, 15);240 const xpc = precompute(p, 15);
240 xpc[4].rejectIdentity() catch |_| return error.WeakPublicKey;241 xpc[4].rejectIdentity() catch |_| return error.WeakPublicKey;
...@@ -245,7 +246,7 @@ pub const Edwards25519 = struct {...@@ -245,7 +246,7 @@ pub const Edwards25519 = struct {
245246
246 /// Multiply an Edwards25519 point by a *PUBLIC* scalar *IN VARIABLE TIME*247 /// Multiply an Edwards25519 point by a *PUBLIC* scalar *IN VARIABLE TIME*
247 /// This can be used for signature verification.248 /// 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 {
249 if (p.is_base) {250 if (p.is_base) {
250 return pcMul16(basePointPc, s, true);251 return pcMul16(basePointPc, s, true);
251 } else {252 } else {
...@@ -257,7 +258,7 @@ pub const Edwards25519 = struct {...@@ -257,7 +258,7 @@ pub const Edwards25519 = struct {
257258
258 /// Multiscalar multiplication *IN VARIABLE TIME* for public data259 /// Multiscalar multiplication *IN VARIABLE TIME* for public data
259 /// Computes ps0*ss0 + ps1*ss1 + ps2*ss2... faster than doing many of these operations individually260 /// 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 {
261 var pcs: [count][9]Edwards25519 = undefined;262 var pcs: [count][9]Edwards25519 = undefined;
262 for (ps) |p, i| {263 for (ps) |p, i| {
263 if (p.is_base) {264 if (p.is_base) {
...@@ -296,14 +297,14 @@ pub const Edwards25519 = struct {...@@ -296,14 +297,14 @@ pub const Edwards25519 = struct {
296 /// This is strongly recommended for DH operations.297 /// This is strongly recommended for DH operations.
297 /// Return error.WeakPublicKey if the resulting point is298 /// Return error.WeakPublicKey if the resulting point is
298 /// the identity element.299 /// the identity element.
299 pub fn clampedMul(p: Edwards25519, s: [32]u8) !Edwards25519 {300 pub fn clampedMul(p: Edwards25519, s: [32]u8) Error!Edwards25519 {
300 var t: [32]u8 = s;301 var t: [32]u8 = s;
301 scalar.clamp(&t);302 scalar.clamp(&t);
302 return mul(p, t);303 return mul(p, t);
303 }304 }
304305
305 // montgomery -- recover y = sqrt(x^3 + A*x^2 + x)306 // montgomery -- recover y = sqrt(x^3 + A*x^2 + x)
306 fn xmontToYmont(x: Fe) !Fe {307 fn xmontToYmont(x: Fe) Error!Fe {
307 var x2 = x.sq();308 var x2 = x.sq();
308 const x3 = x.mul(x2);309 const x3 = x.mul(x2);
309 x2 = x2.mul32(Fe.edwards25519a_32);310 x2 = x2.mul32(Fe.edwards25519a_32);
lib/std/crypto/25519/field.zig+3-2
...@@ -6,6 +6,7 @@...@@ -6,6 +6,7 @@
6const std = @import("std");6const std = @import("std");
7const readIntLittle = std.mem.readIntLittle;7const readIntLittle = std.mem.readIntLittle;
8const writeIntLittle = std.mem.writeIntLittle;8const writeIntLittle = std.mem.writeIntLittle;
9const Error = std.crypto.Error;
910
10pub const Fe = struct {11pub const Fe = struct {
11 limbs: [5]u64,12 limbs: [5]u64,
...@@ -112,7 +113,7 @@ pub const Fe = struct {...@@ -112,7 +113,7 @@ pub const Fe = struct {
112 }113 }
113114
114 /// Reject non-canonical encodings of an element, possibly ignoring the top bit115 /// 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 {
116 var c: u16 = (s[31] & 0x7f) ^ 0x7f;117 var c: u16 = (s[31] & 0x7f) ^ 0x7f;
117 comptime var i = 30;118 comptime var i = 30;
118 inline while (i > 0) : (i -= 1) {119 inline while (i > 0) : (i -= 1) {
...@@ -412,7 +413,7 @@ pub const Fe = struct {...@@ -412,7 +413,7 @@ pub const Fe = struct {
412 }413 }
413414
414 /// Compute the square root of `x2`, returning `error.NotSquare` if `x2` was not a square415 /// 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 {
416 var x2_copy = x2;417 var x2_copy = x2;
417 const x = x2.uncheckedSqrt();418 const x = x2.uncheckedSqrt();
418 const check = x.sq().sub(x2_copy);419 const check = x.sq().sub(x2_copy);
lib/std/crypto/25519/ristretto255.zig+5-4
...@@ -5,6 +5,7 @@...@@ -5,6 +5,7 @@
5// and substantial portions of the software.5// and substantial portions of the software.
6const std = @import("std");6const std = @import("std");
7const fmt = std.fmt;7const fmt = std.fmt;
8const Error = std.crypto.Error;
89
9/// Group operations over Edwards25519.10/// Group operations over Edwards25519.
10pub const Ristretto255 = struct {11pub const Ristretto255 = struct {
...@@ -34,7 +35,7 @@ pub const Ristretto255 = struct {...@@ -34,7 +35,7 @@ pub const Ristretto255 = struct {
34 return .{ .ratio_is_square = @boolToInt(has_m_root) | @boolToInt(has_p_root), .root = x.abs() };35 return .{ .ratio_is_square = @boolToInt(has_m_root) | @boolToInt(has_p_root), .root = x.abs() };
35 }36 }
3637
37 fn rejectNonCanonical(s: [encoded_length]u8) !void {38 fn rejectNonCanonical(s: [encoded_length]u8) Error!void {
38 if ((s[0] & 1) != 0) {39 if ((s[0] & 1) != 0) {
39 return error.NonCanonical;40 return error.NonCanonical;
40 }41 }
...@@ -42,7 +43,7 @@ pub const Ristretto255 = struct {...@@ -42,7 +43,7 @@ pub const Ristretto255 = struct {
42 }43 }
4344
44 /// Reject the neutral element.45 /// Reject the neutral element.
45 pub fn rejectIdentity(p: Ristretto255) callconv(.Inline) !void {46 pub fn rejectIdentity(p: Ristretto255) callconv(.Inline) Error!void {
46 return p.p.rejectIdentity();47 return p.p.rejectIdentity();
47 }48 }
4849
...@@ -50,7 +51,7 @@ pub const Ristretto255 = struct {...@@ -50,7 +51,7 @@ pub const Ristretto255 = struct {
50 pub const basePoint = Ristretto255{ .p = Curve.basePoint };51 pub const basePoint = Ristretto255{ .p = Curve.basePoint };
5152
52 /// Decode a Ristretto255 representative.53 /// Decode a Ristretto255 representative.
53 pub fn fromBytes(s: [encoded_length]u8) !Ristretto255 {54 pub fn fromBytes(s: [encoded_length]u8) Error!Ristretto255 {
54 try rejectNonCanonical(s);55 try rejectNonCanonical(s);
55 const s_ = Fe.fromBytes(s);56 const s_ = Fe.fromBytes(s);
56 const ss = s_.sq(); // s^257 const ss = s_.sq(); // s^2
...@@ -153,7 +154,7 @@ pub const Ristretto255 = struct {...@@ -153,7 +154,7 @@ pub const Ristretto255 = struct {
153 /// Multiply a Ristretto255 element with a scalar.154 /// Multiply a Ristretto255 element with a scalar.
154 /// Return error.WeakPublicKey if the resulting element is155 /// Return error.WeakPublicKey if the resulting element is
155 /// the identity element.156 /// 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 {
157 return Ristretto255{ .p = try p.p.mul(s) };158 return Ristretto255{ .p = try p.p.mul(s) };
158 }159 }
159160
lib/std/crypto/25519/scalar.zig+2-1
...@@ -5,6 +5,7 @@...@@ -5,6 +5,7 @@
5// and substantial portions of the software.5// and substantial portions of the software.
6const std = @import("std");6const std = @import("std");
7const mem = std.mem;7const mem = std.mem;
8const Error = std.crypto.Error;
89
9/// 2^252 + 2774231777737235353585193779088364849310/// 2^252 + 27742317777372353535851937790883648493
10pub const field_size = [32]u8{11pub const field_size = [32]u8{
...@@ -18,7 +19,7 @@ pub const CompressedScalar = [32]u8;...@@ -18,7 +19,7 @@ pub const CompressedScalar = [32]u8;
18pub const zero = [_]u8{0} ** 32;19pub const zero = [_]u8{0} ** 32;
1920
20/// Reject a scalar whose encoding is not canonical.21/// Reject a scalar whose encoding is not canonical.
21pub fn rejectNonCanonical(s: [32]u8) !void {22pub fn rejectNonCanonical(s: [32]u8) Error!void {
22 var c: u8 = 0;23 var c: u8 = 0;
23 var n: u8 = 1;24 var n: u8 = 1;
24 var i: usize = 31;25 var i: usize = 31;
lib/std/crypto/25519/x25519.zig+6-5
...@@ -9,6 +9,7 @@ const mem = std.mem;...@@ -9,6 +9,7 @@ const mem = std.mem;
9const fmt = std.fmt;9const fmt = std.fmt;
1010
11const Sha512 = crypto.hash.sha2.Sha512;11const Sha512 = crypto.hash.sha2.Sha512;
12const Error = crypto.Error;
1213
13/// X25519 DH function.14/// X25519 DH function.
14pub const X25519 = struct {15pub const X25519 = struct {
...@@ -31,7 +32,7 @@ pub const X25519 = struct {...@@ -31,7 +32,7 @@ pub const X25519 = struct {
31 secret_key: [secret_length]u8,32 secret_key: [secret_length]u8,
3233
33 /// Create a new key pair using an optional seed.34 /// 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 {
35 const sk = seed orelse sk: {36 const sk = seed orelse sk: {
36 var random_seed: [seed_length]u8 = undefined;37 var random_seed: [seed_length]u8 = undefined;
37 crypto.random.bytes(&random_seed);38 crypto.random.bytes(&random_seed);
...@@ -44,7 +45,7 @@ pub const X25519 = struct {...@@ -44,7 +45,7 @@ pub const X25519 = struct {
44 }45 }
4546
46 /// Create a key pair from an Ed25519 key pair47 /// 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 {
48 const seed = ed25519_key_pair.secret_key[0..32];49 const seed = ed25519_key_pair.secret_key[0..32];
49 var az: [Sha512.digest_length]u8 = undefined;50 var az: [Sha512.digest_length]u8 = undefined;
50 Sha512.hash(seed, &az, .{});51 Sha512.hash(seed, &az, .{});
...@@ -59,13 +60,13 @@ pub const X25519 = struct {...@@ -59,13 +60,13 @@ pub const X25519 = struct {
59 };60 };
6061
61 /// Compute the public key for a given private key.62 /// 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 {
63 const q = try Curve.basePoint.clampedMul(secret_key);64 const q = try Curve.basePoint.clampedMul(secret_key);
64 return q.toBytes();65 return q.toBytes();
65 }66 }
6667
67 /// Compute the X25519 equivalent to an Ed25519 public eky.68 /// 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 {
69 const pk_ed = try crypto.ecc.Edwards25519.fromBytes(ed25519_public_key);70 const pk_ed = try crypto.ecc.Edwards25519.fromBytes(ed25519_public_key);
70 const pk = try Curve.fromEdwards25519(pk_ed);71 const pk = try Curve.fromEdwards25519(pk_ed);
71 return pk.toBytes();72 return pk.toBytes();
...@@ -74,7 +75,7 @@ pub const X25519 = struct {...@@ -74,7 +75,7 @@ pub const X25519 = struct {
74 /// Compute the scalar product of a public key and a secret scalar.75 /// Compute the scalar product of a public key and a secret scalar.
75 /// Note that the output should not be used as a shared secret without76 /// Note that the output should not be used as a shared secret without
76 /// hashing it first.77 /// 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 {
78 const q = try Curve.fromBytes(public_key).clampedMul(secret_key);79 const q = try Curve.fromBytes(public_key).clampedMul(secret_key);
79 return q.toBytes();80 return q.toBytes();
80 }81 }
lib/std/crypto/aegis.zig+3-2
...@@ -8,6 +8,7 @@ const std = @import("std");...@@ -8,6 +8,7 @@ const std = @import("std");
8const mem = std.mem;8const mem = std.mem;
9const assert = std.debug.assert;9const assert = std.debug.assert;
10const AesBlock = std.crypto.core.aes.Block;10const AesBlock = std.crypto.core.aes.Block;
11const Error = std.crypto.Error;
1112
12const State128L = struct {13const State128L = struct {
13 blocks: [8]AesBlock,14 blocks: [8]AesBlock,
...@@ -136,7 +137,7 @@ pub const Aegis128L = struct {...@@ -136,7 +137,7 @@ pub const Aegis128L = struct {
136 /// ad: Associated Data137 /// ad: Associated Data
137 /// npub: public nonce138 /// npub: public nonce
138 /// k: private key139 /// 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 {
140 assert(c.len == m.len);141 assert(c.len == m.len);
141 var state = State128L.init(key, npub);142 var state = State128L.init(key, npub);
142 var src: [32]u8 align(16) = undefined;143 var src: [32]u8 align(16) = undefined;
...@@ -298,7 +299,7 @@ pub const Aegis256 = struct {...@@ -298,7 +299,7 @@ pub const Aegis256 = struct {
298 /// ad: Associated Data299 /// ad: Associated Data
299 /// npub: public nonce300 /// npub: public nonce
300 /// k: private key301 /// 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 {
302 assert(c.len == m.len);303 assert(c.len == m.len);
303 var state = State256.init(key, npub);304 var state = State256.init(key, npub);
304 var src: [16]u8 align(16) = undefined;305 var src: [16]u8 align(16) = undefined;
lib/std/crypto/aes_gcm.zig+2-1
...@@ -12,6 +12,7 @@ const debug = std.debug;...@@ -12,6 +12,7 @@ const debug = std.debug;
12const Ghash = std.crypto.onetimeauth.Ghash;12const Ghash = std.crypto.onetimeauth.Ghash;
13const mem = std.mem;13const mem = std.mem;
14const modes = crypto.core.modes;14const modes = crypto.core.modes;
15const Error = crypto.Error;
1516
16pub const Aes128Gcm = AesGcm(crypto.core.aes.Aes128);17pub const Aes128Gcm = AesGcm(crypto.core.aes.Aes128);
17pub const Aes256Gcm = AesGcm(crypto.core.aes.Aes256);18pub const Aes256Gcm = AesGcm(crypto.core.aes.Aes256);
...@@ -59,7 +60,7 @@ fn AesGcm(comptime Aes: anytype) type {...@@ -59,7 +60,7 @@ fn AesGcm(comptime Aes: anytype) type {
59 }60 }
60 }61 }
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 {
63 assert(c.len == m.len);64 assert(c.len == m.len);
6465
65 const aes = Aes.initEnc(key);66 const aes = Aes.initEnc(key);
lib/std/crypto/aes_ocb.zig+2-1
...@@ -10,6 +10,7 @@ const aes = crypto.core.aes;...@@ -10,6 +10,7 @@ const aes = crypto.core.aes;
10const assert = std.debug.assert;10const assert = std.debug.assert;
11const math = std.math;11const math = std.math;
12const mem = std.mem;12const mem = std.mem;
13const Error = crypto.Error;
1314
14pub const Aes128Ocb = AesOcb(aes.Aes128);15pub const Aes128Ocb = AesOcb(aes.Aes128);
15pub const Aes256Ocb = AesOcb(aes.Aes256);16pub const Aes256Ocb = AesOcb(aes.Aes256);
...@@ -178,7 +179,7 @@ fn AesOcb(comptime Aes: anytype) type {...@@ -178,7 +179,7 @@ fn AesOcb(comptime Aes: anytype) type {
178 /// ad: Associated Data179 /// ad: Associated Data
179 /// npub: public nonce180 /// npub: public nonce
180 /// k: secret key181 /// 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 {
182 assert(c.len == m.len);183 assert(c.len == m.len);
183184
184 const aes_enc_ctx = Aes.initEnc(key);185 const aes_enc_ctx = Aes.initEnc(key);
lib/std/crypto/bcrypt.zig+8-14
...@@ -11,7 +11,8 @@ const math = std.math;...@@ -11,7 +11,8 @@ const math = std.math;
11const mem = std.mem;11const mem = std.mem;
12const debug = std.debug;12const debug = std.debug;
13const testing = std.testing;13const testing = std.testing;
14const utils = std.crypto.utils;14const utils = crypto.utils;
15const Error = crypto.Error;
1516
16const salt_length: usize = 16;17const salt_length: usize = 16;
17const salt_str_length: usize = 22;18const salt_str_length: usize = 22;
...@@ -21,13 +22,6 @@ const ct_length: usize = 24;...@@ -21,13 +22,6 @@ const ct_length: usize = 24;
21/// Length (in bytes) of a password hash22/// Length (in bytes) of a password hash
22pub const hash_length: usize = 60;23pub 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
31const State = struct {25const State = struct {
32 sboxes: [4][256]u32 = [4][256]u32{26 sboxes: [4][256]u32 = [4][256]u32{
33 .{ 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 },27 .{ 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 {...@@ -185,7 +179,7 @@ const Codec = struct {
185 debug.assert(j == b64.len);179 debug.assert(j == b64.len);
186 }180 }
187181
188 fn decode(bin: []u8, b64: []const u8) BcryptError!void {182 fn decode(bin: []u8, b64: []const u8) Error!void {
189 var i: usize = 0;183 var i: usize = 0;
190 var j: usize = 0;184 var j: usize = 0;
191 while (j < bin.len) {185 while (j < bin.len) {
...@@ -210,7 +204,7 @@ const Codec = struct {...@@ -210,7 +204,7 @@ const Codec = struct {
210 }204 }
211};205};
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 {
214 var state = State{};208 var state = State{};
215 var password_buf: [73]u8 = undefined;209 var password_buf: [73]u8 = undefined;
216 const trimmed_len = math.min(password.len, password_buf.len - 1);210 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)...@@ -258,14 +252,14 @@ fn strHashInternal(password: []const u8, rounds_log: u6, salt: [salt_length]u8)
258/// IMPORTANT: by design, bcrypt silently truncates passwords to 72 bytes.252/// IMPORTANT: by design, bcrypt silently truncates passwords to 72 bytes.
259/// If this is an issue for your application, hash the password first using a function such as SHA-512,253/// If this is an issue for your application, hash the password first using a function such as SHA-512,
260/// and then use the resulting hash as the password parameter for bcrypt.254/// 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 {
262 var salt: [salt_length]u8 = undefined;256 var salt: [salt_length]u8 = undefined;
263 crypto.random.bytes(&salt);257 crypto.random.bytes(&salt);
264 return strHashInternal(password, rounds_log, salt);258 return strHashInternal(password, rounds_log, salt);
265}259}
266260
267/// Verify that a previously computed hash is valid for a given password.261/// 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 {
269 if (!mem.eql(u8, "$2", h[0..2])) return error.InvalidEncoding;263 if (!mem.eql(u8, "$2", h[0..2])) return error.InvalidEncoding;
270 if (h[3] != '$' or h[6] != '$') return error.InvalidEncoding;264 if (h[3] != '$' or h[6] != '$') return error.InvalidEncoding;
271 const rounds_log_str = h[4..][0..2];265 const rounds_log_str = h[4..][0..2];
...@@ -275,7 +269,7 @@ pub fn strVerify(h: [hash_length]u8, password: []const u8) BcryptError!void {...@@ -275,7 +269,7 @@ pub fn strVerify(h: [hash_length]u8, password: []const u8) BcryptError!void {
275 const rounds_log = fmt.parseInt(u6, rounds_log_str[0..], 10) catch return error.InvalidEncoding;269 const rounds_log = fmt.parseInt(u6, rounds_log_str[0..], 10) catch return error.InvalidEncoding;
276 const wanted_s = try strHashInternal(password, rounds_log, salt);270 const wanted_s = try strHashInternal(password, rounds_log, salt);
277 if (!mem.eql(u8, wanted_s[0..], h[0..])) {271 if (!mem.eql(u8, wanted_s[0..], h[0..])) {
278 return error.InvalidPassword;272 return error.PasswordVerificationFailed;
279 }273 }
280}274}
281275
...@@ -292,7 +286,7 @@ test "bcrypt codec" {...@@ -292,7 +286,7 @@ test "bcrypt codec" {
292test "bcrypt" {286test "bcrypt" {
293 const s = try strHash("password", 5);287 const s = try strHash("password", 5);
294 try strVerify(s, "password");288 try strVerify(s, "password");
295 testing.expectError(error.InvalidPassword, strVerify(s, "invalid password"));289 testing.expectError(error.PasswordVerificationFailed, strVerify(s, "invalid password"));
296290
297 const long_s = try strHash("password" ** 100, 5);291 const long_s = try strHash("password" ** 100, 5);
298 try strVerify(long_s, "password" ** 100);292 try strVerify(long_s, "password" ** 100);
lib/std/crypto/benchmark.zig+1
...@@ -202,6 +202,7 @@ pub fn benchmarkBatchSignatureVerification(comptime Signature: anytype, comptime...@@ -202,6 +202,7 @@ pub fn benchmarkBatchSignatureVerification(comptime Signature: anytype, comptime
202const aeads = [_]Crypto{202const aeads = [_]Crypto{
203 Crypto{ .ty = crypto.aead.chacha_poly.ChaCha20Poly1305, .name = "chacha20Poly1305" },203 Crypto{ .ty = crypto.aead.chacha_poly.ChaCha20Poly1305, .name = "chacha20Poly1305" },
204 Crypto{ .ty = crypto.aead.chacha_poly.XChaCha20Poly1305, .name = "xchacha20Poly1305" },204 Crypto{ .ty = crypto.aead.chacha_poly.XChaCha20Poly1305, .name = "xchacha20Poly1305" },
205 Crypto{ .ty = crypto.aead.chacha_poly.XChaCha8Poly1305, .name = "xchacha8Poly1305" },
205 Crypto{ .ty = crypto.aead.salsa_poly.XSalsa20Poly1305, .name = "xsalsa20Poly1305" },206 Crypto{ .ty = crypto.aead.salsa_poly.XSalsa20Poly1305, .name = "xsalsa20Poly1305" },
206 Crypto{ .ty = crypto.aead.Gimli, .name = "gimli-aead" },207 Crypto{ .ty = crypto.aead.Gimli, .name = "gimli-aead" },
207 Crypto{ .ty = crypto.aead.aegis.Aegis128L, .name = "aegis-128l" },208 Crypto{ .ty = crypto.aead.aegis.Aegis128L, .name = "aegis-128l" },
lib/std/crypto/chacha20.zig+599-571
...@@ -13,287 +13,359 @@ const testing = std.testing;...@@ -13,287 +13,359 @@ const testing = std.testing;
13const maxInt = math.maxInt;13const maxInt = math.maxInt;
14const Vector = std.meta.Vector;14const Vector = std.meta.Vector;
15const Poly1305 = std.crypto.onetimeauth.Poly1305;15const 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
17// Vectorized implementation of the core function83// Vectorized implementation of the core function
18const ChaCha20VecImpl = struct {84fn ChaChaVecImpl(comptime rounds_nb: usize) type {
19 const Lane = Vector(4, u32);85 return struct {
20 const BlockVec = [4]Lane;86 const Lane = Vector(4, u32);
2187 const BlockVec = [4]Lane;
22 fn initContext(key: [8]u32, d: [4]u32) BlockVec {88
23 const c = "expand 32-byte k";89 fn initContext(key: [8]u32, d: [4]u32) BlockVec {
24 const constant_le = comptime Lane{90 const c = "expand 32-byte k";
25 mem.readIntLittle(u32, c[0..4]),91 const constant_le = comptime Lane{
26 mem.readIntLittle(u32, c[4..8]),92 mem.readIntLittle(u32, c[0..4]),
27 mem.readIntLittle(u32, c[8..12]),93 mem.readIntLittle(u32, c[4..8]),
28 mem.readIntLittle(u32, c[12..16]),94 mem.readIntLittle(u32, c[8..12]),
29 };95 mem.readIntLittle(u32, c[12..16]),
30 return BlockVec{96 };
31 constant_le,97 return BlockVec{
32 Lane{ key[0], key[1], key[2], key[3] },98 constant_le,
33 Lane{ key[4], key[5], key[6], key[7] },99 Lane{ key[0], key[1], key[2], key[3] },
34 Lane{ d[0], d[1], d[2], d[3] },100 Lane{ key[4], key[5], key[6], key[7] },
35 };101 Lane{ d[0], d[1], d[2], d[3] },
36 }102 };
103 }
37104
38 fn chacha20Core(x: *BlockVec, input: BlockVec) callconv(.Inline) void {105 fn chacha20Core(x: *BlockVec, input: BlockVec) callconv(.Inline) void {
39 x.* = input;106 x.* = input;
40107
41 var r: usize = 0;108 var r: usize = 0;
42 while (r < 20) : (r += 2) {109 while (r < rounds_nb) : (r += 2) {
43 x[0] +%= x[1];110 x[0] +%= x[1];
44 x[3] ^= x[0];111 x[3] ^= x[0];
45 x[3] = math.rotl(Lane, x[3], 16);112 x[3] = math.rotl(Lane, x[3], 16);
46113
47 x[2] +%= x[3];114 x[2] +%= x[3];
48 x[1] ^= x[2];115 x[1] ^= x[2];
49 x[1] = math.rotl(Lane, x[1], 12);116 x[1] = math.rotl(Lane, x[1], 12);
50117
51 x[0] +%= x[1];118 x[0] +%= x[1];
52 x[3] ^= x[0];119 x[3] ^= x[0];
53 x[0] = @shuffle(u32, x[0], undefined, [_]i32{ 3, 0, 1, 2 });120 x[0] = @shuffle(u32, x[0], undefined, [_]i32{ 3, 0, 1, 2 });
54 x[3] = math.rotl(Lane, x[3], 8);121 x[3] = math.rotl(Lane, x[3], 8);
55122
56 x[2] +%= x[3];123 x[2] +%= x[3];
57 x[3] = @shuffle(u32, x[3], undefined, [_]i32{ 2, 3, 0, 1 });124 x[3] = @shuffle(u32, x[3], undefined, [_]i32{ 2, 3, 0, 1 });
58 x[1] ^= x[2];125 x[1] ^= x[2];
59 x[2] = @shuffle(u32, x[2], undefined, [_]i32{ 1, 2, 3, 0 });126 x[2] = @shuffle(u32, x[2], undefined, [_]i32{ 1, 2, 3, 0 });
60 x[1] = math.rotl(Lane, x[1], 7);127 x[1] = math.rotl(Lane, x[1], 7);
61128
62 x[0] +%= x[1];129 x[0] +%= x[1];
63 x[3] ^= x[0];130 x[3] ^= x[0];
64 x[3] = math.rotl(Lane, x[3], 16);131 x[3] = math.rotl(Lane, x[3], 16);
65132
66 x[2] +%= x[3];133 x[2] +%= x[3];
67 x[1] ^= x[2];134 x[1] ^= x[2];
68 x[1] = math.rotl(Lane, x[1], 12);135 x[1] = math.rotl(Lane, x[1], 12);
69136
70 x[0] +%= x[1];137 x[0] +%= x[1];
71 x[3] ^= x[0];138 x[3] ^= x[0];
72 x[0] = @shuffle(u32, x[0], undefined, [_]i32{ 1, 2, 3, 0 });139 x[0] = @shuffle(u32, x[0], undefined, [_]i32{ 1, 2, 3, 0 });
73 x[3] = math.rotl(Lane, x[3], 8);140 x[3] = math.rotl(Lane, x[3], 8);
74141
75 x[2] +%= x[3];142 x[2] +%= x[3];
76 x[3] = @shuffle(u32, x[3], undefined, [_]i32{ 2, 3, 0, 1 });143 x[3] = @shuffle(u32, x[3], undefined, [_]i32{ 2, 3, 0, 1 });
77 x[1] ^= x[2];144 x[1] ^= x[2];
78 x[2] = @shuffle(u32, x[2], undefined, [_]i32{ 3, 0, 1, 2 });145 x[2] = @shuffle(u32, x[2], undefined, [_]i32{ 3, 0, 1, 2 });
79 x[1] = math.rotl(Lane, x[1], 7);146 x[1] = math.rotl(Lane, x[1], 7);
147 }
80 }148 }
81 }
82149
83 fn hashToBytes(out: *[64]u8, x: BlockVec) callconv(.Inline) void {150 fn hashToBytes(out: *[64]u8, x: BlockVec) callconv(.Inline) void {
84 var i: usize = 0;151 var i: usize = 0;
85 while (i < 4) : (i += 1) {152 while (i < 4) : (i += 1) {
86 mem.writeIntLittle(u32, out[16 * i + 0 ..][0..4], x[i][0]);153 mem.writeIntLittle(u32, out[16 * i + 0 ..][0..4], x[i][0]);
87 mem.writeIntLittle(u32, out[16 * i + 4 ..][0..4], x[i][1]);154 mem.writeIntLittle(u32, out[16 * i + 4 ..][0..4], x[i][1]);
88 mem.writeIntLittle(u32, out[16 * i + 8 ..][0..4], x[i][2]);155 mem.writeIntLittle(u32, out[16 * i + 8 ..][0..4], x[i][2]);
89 mem.writeIntLittle(u32, out[16 * i + 12 ..][0..4], x[i][3]);156 mem.writeIntLittle(u32, out[16 * i + 12 ..][0..4], x[i][3]);
157 }
90 }158 }
91 }
92159
93 fn contextFeedback(x: *BlockVec, ctx: BlockVec) callconv(.Inline) void {160 fn contextFeedback(x: *BlockVec, ctx: BlockVec) callconv(.Inline) void {
94 x[0] +%= ctx[0];161 x[0] +%= ctx[0];
95 x[1] +%= ctx[1];162 x[1] +%= ctx[1];
96 x[2] +%= ctx[2];163 x[2] +%= ctx[2];
97 x[3] +%= ctx[3];164 x[3] +%= ctx[3];
98 }165 }
99166
100 fn chacha20Xor(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) void {167 fn chacha20Xor(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) void {
101 var ctx = initContext(key, counter);168 var ctx = initContext(key, counter);
102 var x: BlockVec = undefined;169 var x: BlockVec = undefined;
103 var buf: [64]u8 = undefined;170 var buf: [64]u8 = undefined;
104 var i: usize = 0;171 var i: usize = 0;
105 while (i + 64 <= in.len) : (i += 64) {172 while (i + 64 <= in.len) : (i += 64) {
106 chacha20Core(x[0..], ctx);173 chacha20Core(x[0..], ctx);
107 contextFeedback(&x, ctx);174 contextFeedback(&x, ctx);
108 hashToBytes(buf[0..], x);175 hashToBytes(buf[0..], x);
109176
110 var xout = out[i..];177 var xout = out[i..];
111 const xin = in[i..];178 const xin = in[i..];
112 var j: usize = 0;179 var j: usize = 0;
113 while (j < 64) : (j += 1) {180 while (j < 64) : (j += 1) {
114 xout[j] = xin[j];181 xout[j] = xin[j];
115 }182 }
116 j = 0;183 j = 0;
117 while (j < 64) : (j += 1) {184 while (j < 64) : (j += 1) {
118 xout[j] ^= buf[j];185 xout[j] ^= buf[j];
186 }
187 ctx[3][0] += 1;
119 }188 }
120 ctx[3][0] += 1;189 if (i < in.len) {
121 }190 chacha20Core(x[0..], ctx);
122 if (i < in.len) {191 contextFeedback(&x, ctx);
123 chacha20Core(x[0..], ctx);192 hashToBytes(buf[0..], x);
124 contextFeedback(&x, ctx);193
125 hashToBytes(buf[0..], x);194 var xout = out[i..];
126195 const xin = in[i..];
127 var xout = out[i..];196 var j: usize = 0;
128 const xin = in[i..];197 while (j < in.len % 64) : (j += 1) {
129 var j: usize = 0;198 xout[j] = xin[j] ^ buf[j];
130 while (j < in.len % 64) : (j += 1) {199 }
131 xout[j] = xin[j] ^ buf[j];
132 }200 }
133 }201 }
134 }
135202
136 fn hchacha20(input: [16]u8, key: [32]u8) [32]u8 {203 fn hchacha20(input: [16]u8, key: [32]u8) [32]u8 {
137 var c: [4]u32 = undefined;204 var c: [4]u32 = undefined;
138 for (c) |_, i| {205 for (c) |_, i| {
139 c[i] = mem.readIntLittle(u32, input[4 * i ..][0..4]);206 c[i] = mem.readIntLittle(u32, input[4 * i ..][0..4]);
207 }
208 const ctx = initContext(keyToWords(key), c);
209 var x: BlockVec = undefined;
210 chacha20Core(x[0..], ctx);
211 var out: [32]u8 = undefined;
212 mem.writeIntLittle(u32, out[0..4], x[0][0]);
213 mem.writeIntLittle(u32, out[4..8], x[0][1]);
214 mem.writeIntLittle(u32, out[8..12], x[0][2]);
215 mem.writeIntLittle(u32, out[12..16], x[0][3]);
216 mem.writeIntLittle(u32, out[16..20], x[3][0]);
217 mem.writeIntLittle(u32, out[20..24], x[3][1]);
218 mem.writeIntLittle(u32, out[24..28], x[3][2]);
219 mem.writeIntLittle(u32, out[28..32], x[3][3]);
220 return out;
140 }221 }
141 const ctx = initContext(keyToWords(key), c);222 };
142 var x: BlockVec = undefined;223}
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};
156224
157// Non-vectorized implementation of the core function225// Non-vectorized implementation of the core function
158const ChaCha20NonVecImpl = struct {226fn ChaChaNonVecImpl(comptime rounds_nb: usize) type {
159 const BlockVec = [16]u32;227 return struct {
160228 const BlockVec = [16]u32;
161 fn initContext(key: [8]u32, d: [4]u32) BlockVec {229
162 const c = "expand 32-byte k";230 fn initContext(key: [8]u32, d: [4]u32) BlockVec {
163 const constant_le = comptime [4]u32{231 const c = "expand 32-byte k";
164 mem.readIntLittle(u32, c[0..4]),232 const constant_le = comptime [4]u32{
165 mem.readIntLittle(u32, c[4..8]),233 mem.readIntLittle(u32, c[0..4]),
166 mem.readIntLittle(u32, c[8..12]),234 mem.readIntLittle(u32, c[4..8]),
167 mem.readIntLittle(u32, c[12..16]),235 mem.readIntLittle(u32, c[8..12]),
168 };236 mem.readIntLittle(u32, c[12..16]),
169 return BlockVec{237 };
170 constant_le[0], constant_le[1], constant_le[2], constant_le[3],238 return BlockVec{
171 key[0], key[1], key[2], key[3],239 constant_le[0], constant_le[1], constant_le[2], constant_le[3],
172 key[4], key[5], key[6], key[7],240 key[0], key[1], key[2], key[3],
173 d[0], d[1], d[2], d[3],241 key[4], key[5], key[6], key[7],
174 };242 d[0], d[1], d[2], d[3],
175 }243 };
176244 }
177 const QuarterRound = struct {
178 a: usize,
179 b: usize,
180 c: usize,
181 d: usize,
182 };
183245
184 fn Rp(a: usize, b: usize, c: usize, d: usize) QuarterRound {246 const QuarterRound = struct {
185 return QuarterRound{247 a: usize,
186 .a = a,248 b: usize,
187 .b = b,249 c: usize,
188 .c = c,250 d: usize,
189 .d = d,
190 };251 };
191 }
192252
193 fn chacha20Core(x: *BlockVec, input: BlockVec) callconv(.Inline) void {253 fn Rp(a: usize, b: usize, c: usize, d: usize) QuarterRound {
194 x.* = input;254 return QuarterRound{
195255 .a = a,
196 const rounds = comptime [_]QuarterRound{256 .b = b,
197 Rp(0, 4, 8, 12),257 .c = c,
198 Rp(1, 5, 9, 13),258 .d = d,
199 Rp(2, 6, 10, 14),259 };
200 Rp(3, 7, 11, 15),260 }
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 };
206261
207 comptime var j: usize = 0;262 fn chacha20Core(x: *BlockVec, input: BlockVec) callconv(.Inline) void {
208 inline while (j < 20) : (j += 2) {263 x.* = input;
209 inline for (rounds) |r| {264
210 x[r.a] +%= x[r.b];265 const rounds = comptime [_]QuarterRound{
211 x[r.d] = math.rotl(u32, x[r.d] ^ x[r.a], @as(u32, 16));266 Rp(0, 4, 8, 12),
212 x[r.c] +%= x[r.d];267 Rp(1, 5, 9, 13),
213 x[r.b] = math.rotl(u32, x[r.b] ^ x[r.c], @as(u32, 12));268 Rp(2, 6, 10, 14),
214 x[r.a] +%= x[r.b];269 Rp(3, 7, 11, 15),
215 x[r.d] = math.rotl(u32, x[r.d] ^ x[r.a], @as(u32, 8));270 Rp(0, 5, 10, 15),
216 x[r.c] +%= x[r.d];271 Rp(1, 6, 11, 12),
217 x[r.b] = math.rotl(u32, x[r.b] ^ x[r.c], @as(u32, 7));272 Rp(2, 7, 8, 13),
273 Rp(3, 4, 9, 14),
274 };
275
276 comptime var j: usize = 0;
277 inline while (j < rounds_nb) : (j += 2) {
278 inline for (rounds) |r| {
279 x[r.a] +%= x[r.b];
280 x[r.d] = math.rotl(u32, x[r.d] ^ x[r.a], @as(u32, 16));
281 x[r.c] +%= x[r.d];
282 x[r.b] = math.rotl(u32, x[r.b] ^ x[r.c], @as(u32, 12));
283 x[r.a] +%= x[r.b];
284 x[r.d] = math.rotl(u32, x[r.d] ^ x[r.a], @as(u32, 8));
285 x[r.c] +%= x[r.d];
286 x[r.b] = math.rotl(u32, x[r.b] ^ x[r.c], @as(u32, 7));
287 }
218 }288 }
219 }289 }
220 }
221290
222 fn hashToBytes(out: *[64]u8, x: BlockVec) callconv(.Inline) void {291 fn hashToBytes(out: *[64]u8, x: BlockVec) callconv(.Inline) void {
223 var i: usize = 0;292 var i: usize = 0;
224 while (i < 4) : (i += 1) {293 while (i < 4) : (i += 1) {
225 mem.writeIntLittle(u32, out[16 * i + 0 ..][0..4], x[i * 4 + 0]);294 mem.writeIntLittle(u32, out[16 * i + 0 ..][0..4], x[i * 4 + 0]);
226 mem.writeIntLittle(u32, out[16 * i + 4 ..][0..4], x[i * 4 + 1]);295 mem.writeIntLittle(u32, out[16 * i + 4 ..][0..4], x[i * 4 + 1]);
227 mem.writeIntLittle(u32, out[16 * i + 8 ..][0..4], x[i * 4 + 2]);296 mem.writeIntLittle(u32, out[16 * i + 8 ..][0..4], x[i * 4 + 2]);
228 mem.writeIntLittle(u32, out[16 * i + 12 ..][0..4], x[i * 4 + 3]);297 mem.writeIntLittle(u32, out[16 * i + 12 ..][0..4], x[i * 4 + 3]);
298 }
229 }299 }
230 }
231300
232 fn contextFeedback(x: *BlockVec, ctx: BlockVec) callconv(.Inline) void {301 fn contextFeedback(x: *BlockVec, ctx: BlockVec) callconv(.Inline) void {
233 var i: usize = 0;302 var i: usize = 0;
234 while (i < 16) : (i += 1) {303 while (i < 16) : (i += 1) {
235 x[i] +%= ctx[i];304 x[i] +%= ctx[i];
305 }
236 }306 }
237 }
238307
239 fn chacha20Xor(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) void {308 fn chacha20Xor(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) void {
240 var ctx = initContext(key, counter);309 var ctx = initContext(key, counter);
241 var x: BlockVec = undefined;310 var x: BlockVec = undefined;
242 var buf: [64]u8 = undefined;311 var buf: [64]u8 = undefined;
243 var i: usize = 0;312 var i: usize = 0;
244 while (i + 64 <= in.len) : (i += 64) {313 while (i + 64 <= in.len) : (i += 64) {
245 chacha20Core(x[0..], ctx);314 chacha20Core(x[0..], ctx);
246 contextFeedback(&x, ctx);315 contextFeedback(&x, ctx);
247 hashToBytes(buf[0..], x);316 hashToBytes(buf[0..], x);
248317
249 var xout = out[i..];318 var xout = out[i..];
250 const xin = in[i..];319 const xin = in[i..];
251 var j: usize = 0;320 var j: usize = 0;
252 while (j < 64) : (j += 1) {321 while (j < 64) : (j += 1) {
253 xout[j] = xin[j];322 xout[j] = xin[j];
254 }323 }
255 j = 0;324 j = 0;
256 while (j < 64) : (j += 1) {325 while (j < 64) : (j += 1) {
257 xout[j] ^= buf[j];326 xout[j] ^= buf[j];
327 }
328 ctx[12] += 1;
258 }329 }
259 ctx[12] += 1;330 if (i < in.len) {
260 }331 chacha20Core(x[0..], ctx);
261 if (i < in.len) {332 contextFeedback(&x, ctx);
262 chacha20Core(x[0..], ctx);333 hashToBytes(buf[0..], x);
263 contextFeedback(&x, ctx);334
264 hashToBytes(buf[0..], x);335 var xout = out[i..];
265336 const xin = in[i..];
266 var xout = out[i..];337 var j: usize = 0;
267 const xin = in[i..];338 while (j < in.len % 64) : (j += 1) {
268 var j: usize = 0;339 xout[j] = xin[j] ^ buf[j];
269 while (j < in.len % 64) : (j += 1) {340 }
270 xout[j] = xin[j] ^ buf[j];
271 }341 }
272 }342 }
273 }
274343
275 fn hchacha20(input: [16]u8, key: [32]u8) [32]u8 {344 fn hchacha20(input: [16]u8, key: [32]u8) [32]u8 {
276 var c: [4]u32 = undefined;345 var c: [4]u32 = undefined;
277 for (c) |_, i| {346 for (c) |_, i| {
278 c[i] = mem.readIntLittle(u32, input[4 * i ..][0..4]);347 c[i] = mem.readIntLittle(u32, input[4 * i ..][0..4]);
348 }
349 const ctx = initContext(keyToWords(key), c);
350 var x: BlockVec = undefined;
351 chacha20Core(x[0..], ctx);
352 var out: [32]u8 = undefined;
353 mem.writeIntLittle(u32, out[0..4], x[0]);
354 mem.writeIntLittle(u32, out[4..8], x[1]);
355 mem.writeIntLittle(u32, out[8..12], x[2]);
356 mem.writeIntLittle(u32, out[12..16], x[3]);
357 mem.writeIntLittle(u32, out[16..20], x[12]);
358 mem.writeIntLittle(u32, out[20..24], x[13]);
359 mem.writeIntLittle(u32, out[24..28], x[14]);
360 mem.writeIntLittle(u32, out[28..32], x[15]);
361 return out;
279 }362 }
280 const ctx = initContext(keyToWords(key), c);363 };
281 var x: BlockVec = undefined;364}
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};
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
298fn keyToWords(key: [32]u8) [8]u32 {370fn keyToWords(key: [32]u8) [8]u32 {
299 var k: [8]u32 = undefined;371 var k: [8]u32 = undefined;
...@@ -304,68 +376,239 @@ fn keyToWords(key: [32]u8) [8]u32 {...@@ -304,68 +376,239 @@ fn keyToWords(key: [32]u8) [8]u32 {
304 return k;376 return k;
305}377}
306378
307/// ChaCha20 avoids the possibility of timing attacks, as there are no branches379fn extend(key: [32]u8, nonce: [24]u8, comptime rounds_nb: usize) struct { key: [32]u8, nonce: [12]u8 } {
308/// on secret key data.380 var subnonce: [12]u8 = undefined;
309///381 mem.set(u8, subnonce[0..4], 0);
310/// in and out should be the same length.382 mem.copy(u8, subnonce[4..], nonce[16..24]);
311/// counter should generally be 0 or 1383 return .{
312///384 .key = ChaChaImpl(rounds_nb).hchacha20(nonce[0..16].*, key),
313/// ChaCha20 is self-reversing. To decrypt just run the cipher with the same385 .nonce = subnonce,
314/// counter, nonce, and key.386 };
315pub const ChaCha20IETF = struct {387}
316 pub fn xor(out: []u8, in: []const u8, counter: u32, key: [32]u8, nonce: [12]u8) void {388
317 assert(in.len == out.len);389fn ChaChaIETF(comptime rounds_nb: usize) type {
318 assert((in.len >> 6) + counter <= maxInt(u32));390 return struct {
319391 /// Nonce length in bytes.
320 var c: [4]u32 = undefined;392 pub const nonce_length = 12;
321 c[0] = counter;393 /// Key length in bytes.
322 c[1] = mem.readIntLittle(u32, nonce[0..4]);394 pub const key_length = 32;
323 c[2] = mem.readIntLittle(u32, nonce[4..8]);395
324 c[3] = mem.readIntLittle(u32, nonce[8..12]);396 /// Add the output of the ChaCha20 stream cipher to `in` and stores the result into `out`.
325 ChaCha20Impl.chacha20Xor(out, in, keyToWords(key), c);397 /// WARNING: This function doesn't provide authenticated encryption.
326 }398 /// Using the AEAD or one of the `box` versions is usually preferred.
327};399 pub fn xor(out: []u8, in: []const u8, counter: u32, key: [key_length]u8, nonce: [nonce_length]u8) void {
328400 assert(in.len == out.len);
329/// This is the original ChaCha20 before RFC 7539, which recommends using the401 assert(in.len / 64 <= (1 << 32 - 1) - counter);
330/// orgininal version on applications such as disk or file encryption that might402
331/// exceed the 256 GiB limit of the 96-bit nonce version.403 var d: [4]u32 = undefined;
332pub const ChaCha20With64BitNonce = struct {404 d[0] = counter;
333 pub fn xor(out: []u8, in: []const u8, counter: u64, key: [32]u8, nonce: [8]u8) void {405 d[1] = mem.readIntLittle(u32, nonce[0..4]);
334 assert(in.len == out.len);406 d[2] = mem.readIntLittle(u32, nonce[4..8]);
335 assert(counter +% (in.len >> 6) >= counter);407 d[3] = mem.readIntLittle(u32, nonce[8..12]);
336408 ChaChaImpl(rounds_nb).chacha20Xor(out, in, keyToWords(key), d);
337 var cursor: usize = 0;409 }
338 const k = keyToWords(key);410 };
339 var c: [4]u32 = undefined;411}
340 c[0] = @truncate(u32, counter);412
341 c[1] = @truncate(u32, counter >> 32);413fn ChaChaWith64BitNonce(comptime rounds_nb: usize) type {
342 c[2] = mem.readIntLittle(u32, nonce[0..4]);414 return struct {
343 c[3] = mem.readIntLittle(u32, nonce[4..8]);415 /// Nonce length in bytes.
344416 pub const nonce_length = 8;
345 const block_length = (1 << 6);417 /// Key length in bytes.
346 // The full block size is greater than the address space on a 32bit machine418 pub const key_length = 32;
347 const big_block = if (@sizeOf(usize) > 4) (block_length << 32) else maxInt(usize);419
348420 /// Add the output of the ChaCha20 stream cipher to `in` and stores the result into `out`.
349 // first partial big block421 /// WARNING: This function doesn't provide authenticated encryption.
350 if (((@intCast(u64, maxInt(u32) - @truncate(u32, counter)) + 1) << 6) < in.len) {422 /// Using the AEAD or one of the `box` versions is usually preferred.
351 ChaCha20Impl.chacha20Xor(out[cursor..big_block], in[cursor..big_block], k, c);423 pub fn xor(out: []u8, in: []const u8, counter: u64, key: [key_length]u8, nonce: [nonce_length]u8) void {
352 cursor = big_block - cursor;424 assert(in.len == out.len);
353 c[1] += 1;425 assert(in.len / 64 <= (1 << 64 - 1) - counter);
354 if (comptime @sizeOf(usize) > 4) {426
355 // A big block is giant: 256 GiB, but we can avoid this limitation427 var cursor: usize = 0;
356 var remaining_blocks: u32 = @intCast(u32, (in.len / big_block));428 const k = keyToWords(key);
357 var i: u32 = 0;429 var c: [4]u32 = undefined;
358 while (remaining_blocks > 0) : (remaining_blocks -= 1) {430 c[0] = @truncate(u32, counter);
359 ChaCha20Impl.chacha20Xor(out[cursor .. cursor + big_block], in[cursor .. cursor + big_block], k, c);431 c[1] = @truncate(u32, counter >> 32);
360 c[1] += 1; // upper 32-bit of counter, generic chacha20Xor() doesn't know about this.432 c[2] = mem.readIntLittle(u32, nonce[0..4]);
361 cursor += big_block;433 c[3] = mem.readIntLittle(u32, nonce[4..8]);
434
435 const block_length = (1 << 6);
436 // The full block size is greater than the address space on a 32bit machine
437 const big_block = if (@sizeOf(usize) > 4) (block_length << 32) else maxInt(usize);
438
439 // first partial big block
440 if (((@intCast(u64, maxInt(u32) - @truncate(u32, counter)) + 1) << 6) < in.len) {
441 ChaChaImpl(rounds_nb).chacha20Xor(out[cursor..big_block], in[cursor..big_block], k, c);
442 cursor = big_block - cursor;
443 c[1] += 1;
444 if (comptime @sizeOf(usize) > 4) {
445 // A big block is giant: 256 GiB, but we can avoid this limitation
446 var remaining_blocks: u32 = @intCast(u32, (in.len / big_block));
447 var i: u32 = 0;
448 while (remaining_blocks > 0) : (remaining_blocks -= 1) {
449 ChaChaImpl(rounds_nb).chacha20Xor(out[cursor .. cursor + big_block], in[cursor .. cursor + big_block], k, c);
450 c[1] += 1; // upper 32-bit of counter, generic chacha20Xor() doesn't know about this.
451 cursor += big_block;
452 }
362 }453 }
363 }454 }
455 ChaChaImpl(rounds_nb).chacha20Xor(out[cursor..], in[cursor..], k, c);
456 }
457 };
458}
459
460fn XChaChaIETF(comptime rounds_nb: usize) type {
461 return struct {
462 /// Nonce length in bytes.
463 pub const nonce_length = 24;
464 /// Key length in bytes.
465 pub const key_length = 32;
466
467 /// Add the output of the XChaCha20 stream cipher to `in` and stores the result into `out`.
468 /// WARNING: This function doesn't provide authenticated encryption.
469 /// Using the AEAD or one of the `box` versions is usually preferred.
470 pub fn xor(out: []u8, in: []const u8, counter: u32, key: [key_length]u8, nonce: [nonce_length]u8) void {
471 const extended = extend(key, nonce, rounds_nb);
472 ChaChaIETF(rounds_nb).xor(out, in, counter, extended.key, extended.nonce);
473 }
474 };
475}
476
477fn ChaChaPoly1305(comptime rounds_nb: usize) type {
478 return struct {
479 pub const tag_length = 16;
480 pub const nonce_length = 12;
481 pub const key_length = 32;
482
483 /// c: ciphertext: output buffer should be of size m.len
484 /// tag: authentication tag: output MAC
485 /// m: message
486 /// ad: Associated Data
487 /// npub: public nonce
488 /// k: private key
489 pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
490 assert(c.len == m.len);
491
492 var polyKey = [_]u8{0} ** 32;
493 ChaChaIETF(rounds_nb).xor(polyKey[0..], polyKey[0..], 0, k, npub);
494
495 ChaChaIETF(rounds_nb).xor(c[0..m.len], m, 1, k, npub);
496
497 var mac = Poly1305.init(polyKey[0..]);
498 mac.update(ad);
499 if (ad.len % 16 != 0) {
500 const zeros = [_]u8{0} ** 16;
501 const padding = 16 - (ad.len % 16);
502 mac.update(zeros[0..padding]);
503 }
504 mac.update(c[0..m.len]);
505 if (m.len % 16 != 0) {
506 const zeros = [_]u8{0} ** 16;
507 const padding = 16 - (m.len % 16);
508 mac.update(zeros[0..padding]);
509 }
510 var lens: [16]u8 = undefined;
511 mem.writeIntLittle(u64, lens[0..8], ad.len);
512 mem.writeIntLittle(u64, lens[8..16], m.len);
513 mac.update(lens[0..]);
514 mac.final(tag);
515 }
516
517 /// m: message: output buffer should be of size c.len
518 /// c: ciphertext
519 /// tag: authentication tag
520 /// ad: Associated Data
521 /// npub: public nonce
522 /// k: private key
523 /// NOTE: the check of the authentication tag is currently not done in constant time
524 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) Error!void {
525 assert(c.len == m.len);
526
527 var polyKey = [_]u8{0} ** 32;
528 ChaChaIETF(rounds_nb).xor(polyKey[0..], polyKey[0..], 0, k, npub);
529
530 var mac = Poly1305.init(polyKey[0..]);
531
532 mac.update(ad);
533 if (ad.len % 16 != 0) {
534 const zeros = [_]u8{0} ** 16;
535 const padding = 16 - (ad.len % 16);
536 mac.update(zeros[0..padding]);
537 }
538 mac.update(c);
539 if (c.len % 16 != 0) {
540 const zeros = [_]u8{0} ** 16;
541 const padding = 16 - (c.len % 16);
542 mac.update(zeros[0..padding]);
543 }
544 var lens: [16]u8 = undefined;
545 mem.writeIntLittle(u64, lens[0..8], ad.len);
546 mem.writeIntLittle(u64, lens[8..16], c.len);
547 mac.update(lens[0..]);
548 var computedTag: [16]u8 = undefined;
549 mac.final(computedTag[0..]);
550
551 var acc: u8 = 0;
552 for (computedTag) |_, i| {
553 acc |= computedTag[i] ^ tag[i];
554 }
555 if (acc != 0) {
556 return error.AuthenticationFailed;
557 }
558 ChaChaIETF(rounds_nb).xor(m[0..c.len], c, 1, k, npub);
559 }
560 };
561}
562
563fn XChaChaPoly1305(comptime rounds_nb: usize) type {
564 return struct {
565 pub const tag_length = 16;
566 pub const nonce_length = 24;
567 pub const key_length = 32;
568
569 /// c: ciphertext: output buffer should be of size m.len
570 /// tag: authentication tag: output MAC
571 /// m: message
572 /// ad: Associated Data
573 /// npub: public nonce
574 /// k: private key
575 pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
576 const extended = extend(k, npub, rounds_nb);
577 return ChaChaPoly1305(rounds_nb).encrypt(c, tag, m, ad, extended.nonce, extended.key);
364 }578 }
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));
367 }610 }
368};611}
369612
370// https://tools.ietf.org/html/rfc7539#section-2.4.2613// https://tools.ietf.org/html/rfc7539#section-2.4.2
371test "crypto.chacha20 test vector sunscreen" {614test "crypto.chacha20 test vector sunscreen" {
...@@ -386,7 +629,7 @@ test "crypto.chacha20 test vector sunscreen" {...@@ -386,7 +629,7 @@ test "crypto.chacha20 test vector sunscreen" {
386 0xb4, 0x0b, 0x8e, 0xed, 0xf2, 0x78, 0x5e, 0x42,629 0xb4, 0x0b, 0x8e, 0xed, 0xf2, 0x78, 0x5e, 0x42,
387 0x87, 0x4d,630 0x87, 0x4d,
388 };631 };
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.";
390 var result: [114]u8 = undefined;633 var result: [114]u8 = undefined;
391 const key = [_]u8{634 const key = [_]u8{
392 0, 1, 2, 3, 4, 5, 6, 7,635 0, 1, 2, 3, 4, 5, 6, 7,
...@@ -400,13 +643,12 @@ test "crypto.chacha20 test vector sunscreen" {...@@ -400,13 +643,12 @@ test "crypto.chacha20 test vector sunscreen" {
400 0, 0, 0, 0,643 0, 0, 0, 0,
401 };644 };
402645
403 ChaCha20IETF.xor(result[0..], input[0..], 1, key, nonce);646 ChaCha20IETF.xor(result[0..], m[0..], 1, key, nonce);
404 testing.expectEqualSlices(u8, &expected_result, &result);647 testing.expectEqualSlices(u8, &expected_result, &result);
405648
406 // Chacha20 is self-reversing.649 var m2: [114]u8 = undefined;
407 var plaintext: [114]u8 = undefined;650 ChaCha20IETF.xor(m2[0..], result[0..], 1, key, nonce);
408 ChaCha20IETF.xor(plaintext[0..], result[0..], 1, key, nonce);651 testing.expect(mem.order(u8, m, &m2) == .eq);
409 testing.expect(mem.order(u8, input, &plaintext) == .eq);
410}652}
411653
412// https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7654// https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7
...@@ -421,7 +663,7 @@ test "crypto.chacha20 test vector 1" {...@@ -421,7 +663,7 @@ test "crypto.chacha20 test vector 1" {
421 0x6a, 0x43, 0xb8, 0xf4, 0x15, 0x18, 0xa1, 0x1c,663 0x6a, 0x43, 0xb8, 0xf4, 0x15, 0x18, 0xa1, 0x1c,
422 0xc3, 0x87, 0xb6, 0x69, 0xb2, 0xee, 0x65, 0x86,664 0xc3, 0x87, 0xb6, 0x69, 0xb2, 0xee, 0x65, 0x86,
423 };665 };
424 const input = [_]u8{666 const m = [_]u8{
425 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,667 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
426 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,668 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
427 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,669 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
...@@ -440,7 +682,7 @@ test "crypto.chacha20 test vector 1" {...@@ -440,7 +682,7 @@ test "crypto.chacha20 test vector 1" {
440 };682 };
441 const nonce = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0 };683 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);
444 testing.expectEqualSlices(u8, &expected_result, &result);686 testing.expectEqualSlices(u8, &expected_result, &result);
445}687}
446688
...@@ -455,7 +697,7 @@ test "crypto.chacha20 test vector 2" {...@@ -455,7 +697,7 @@ test "crypto.chacha20 test vector 2" {
455 0x53, 0xd7, 0x92, 0xb1, 0xc4, 0x3f, 0xea, 0x81,697 0x53, 0xd7, 0x92, 0xb1, 0xc4, 0x3f, 0xea, 0x81,
456 0x7e, 0x9a, 0xd2, 0x75, 0xae, 0x54, 0x69, 0x63,698 0x7e, 0x9a, 0xd2, 0x75, 0xae, 0x54, 0x69, 0x63,
457 };699 };
458 const input = [_]u8{700 const m = [_]u8{
459 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,701 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
460 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,702 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
461 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,703 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
...@@ -474,7 +716,7 @@ test "crypto.chacha20 test vector 2" {...@@ -474,7 +716,7 @@ test "crypto.chacha20 test vector 2" {
474 };716 };
475 const nonce = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0 };717 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);
478 testing.expectEqualSlices(u8, &expected_result, &result);720 testing.expectEqualSlices(u8, &expected_result, &result);
479}721}
480722
...@@ -489,7 +731,7 @@ test "crypto.chacha20 test vector 3" {...@@ -489,7 +731,7 @@ test "crypto.chacha20 test vector 3" {
489 0x52, 0x77, 0x06, 0x2e, 0xb7, 0xa0, 0x43, 0x3e,731 0x52, 0x77, 0x06, 0x2e, 0xb7, 0xa0, 0x43, 0x3e,
490 0x44, 0x5f, 0x41, 0xe3,732 0x44, 0x5f, 0x41, 0xe3,
491 };733 };
492 const input = [_]u8{734 const m = [_]u8{
493 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,735 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
494 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,736 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
495 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,737 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
...@@ -508,7 +750,7 @@ test "crypto.chacha20 test vector 3" {...@@ -508,7 +750,7 @@ test "crypto.chacha20 test vector 3" {
508 };750 };
509 const nonce = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 1 };751 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);
512 testing.expectEqualSlices(u8, &expected_result, &result);754 testing.expectEqualSlices(u8, &expected_result, &result);
513}755}
514756
...@@ -523,7 +765,7 @@ test "crypto.chacha20 test vector 4" {...@@ -523,7 +765,7 @@ test "crypto.chacha20 test vector 4" {
523 0x5d, 0xdc, 0x49, 0x7a, 0x0b, 0x46, 0x6e, 0x7d,765 0x5d, 0xdc, 0x49, 0x7a, 0x0b, 0x46, 0x6e, 0x7d,
524 0x6b, 0xbd, 0xb0, 0x04, 0x1b, 0x2f, 0x58, 0x6b,766 0x6b, 0xbd, 0xb0, 0x04, 0x1b, 0x2f, 0x58, 0x6b,
525 };767 };
526 const input = [_]u8{768 const m = [_]u8{
527 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,769 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
528 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,770 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
529 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,771 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
...@@ -542,7 +784,7 @@ test "crypto.chacha20 test vector 4" {...@@ -542,7 +784,7 @@ test "crypto.chacha20 test vector 4" {
542 };784 };
543 const nonce = [_]u8{ 1, 0, 0, 0, 0, 0, 0, 0 };785 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);
546 testing.expectEqualSlices(u8, &expected_result, &result);788 testing.expectEqualSlices(u8, &expected_result, &result);
547}789}
548790
...@@ -584,7 +826,7 @@ test "crypto.chacha20 test vector 5" {...@@ -584,7 +826,7 @@ test "crypto.chacha20 test vector 5" {
584 0x87, 0x46, 0xd4, 0x52, 0x4d, 0x38, 0x40, 0x7a,826 0x87, 0x46, 0xd4, 0x52, 0x4d, 0x38, 0x40, 0x7a,
585 0x6d, 0xeb, 0x3a, 0xb7, 0x8f, 0xab, 0x78, 0xc9,827 0x6d, 0xeb, 0x3a, 0xb7, 0x8f, 0xab, 0x78, 0xc9,
586 };828 };
587 const input = [_]u8{829 const m = [_]u8{
588 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,830 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
589 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,831 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
590 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,832 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
...@@ -614,147 +856,14 @@ test "crypto.chacha20 test vector 5" {...@@ -614,147 +856,14 @@ test "crypto.chacha20 test vector 5" {
614 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,856 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
615 };857 };
616858
617 ChaCha20With64BitNonce.xor(result[0..], input[0..], 0, key, nonce);859 ChaCha20With64BitNonce.xor(result[0..], m[0..], 0, key, nonce);
618 testing.expectEqualSlices(u8, &expected_result, &result);860 testing.expectEqualSlices(u8, &expected_result, &result);
619}861}
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
754test "seal" {863test "seal" {
755 {864 {
756 const plaintext = "";865 const m = "";
757 const data = "";866 const ad = "";
758 const key = [_]u8{867 const key = [_]u8{
759 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,868 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
760 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,869 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
...@@ -763,11 +872,11 @@ test "seal" {...@@ -763,11 +872,11 @@ test "seal" {
763 const exp_out = [_]u8{ 0xa0, 0x78, 0x4d, 0x7a, 0x47, 0x16, 0xf3, 0xfe, 0xb4, 0xf6, 0x4e, 0x7f, 0x4b, 0x39, 0xbf, 0x4 };872 const exp_out = [_]u8{ 0xa0, 0x78, 0x4d, 0x7a, 0x47, 0x16, 0xf3, 0xfe, 0xb4, 0xf6, 0x4e, 0x7f, 0x4b, 0x39, 0xbf, 0x4 };
764873
765 var out: [exp_out.len]u8 = undefined;874 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);
767 testing.expectEqualSlices(u8, exp_out[0..], out[0..]);876 testing.expectEqualSlices(u8, exp_out[0..], out[0..]);
768 }877 }
769 {878 {
770 const plaintext = [_]u8{879 const m = [_]u8{
771 0x4c, 0x61, 0x64, 0x69, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x47, 0x65, 0x6e, 0x74, 0x6c,880 0x4c, 0x61, 0x64, 0x69, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x47, 0x65, 0x6e, 0x74, 0x6c,
772 0x65, 0x6d, 0x65, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x61, 0x73,881 0x65, 0x6d, 0x65, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x61, 0x73,
773 0x73, 0x20, 0x6f, 0x66, 0x20, 0x27, 0x39, 0x39, 0x3a, 0x20, 0x49, 0x66, 0x20, 0x49, 0x20, 0x63,882 0x73, 0x20, 0x6f, 0x66, 0x20, 0x27, 0x39, 0x39, 0x3a, 0x20, 0x49, 0x66, 0x20, 0x49, 0x20, 0x63,
...@@ -777,7 +886,7 @@ test "seal" {...@@ -777,7 +886,7 @@ test "seal" {
777 0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69,886 0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69,
778 0x74, 0x2e,887 0x74, 0x2e,
779 };888 };
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 };
781 const key = [_]u8{890 const key = [_]u8{
782 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,891 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
783 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,892 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
...@@ -796,15 +905,15 @@ test "seal" {...@@ -796,15 +905,15 @@ test "seal" {
796 };905 };
797906
798 var out: [exp_out.len]u8 = undefined;907 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);
800 testing.expectEqualSlices(u8, exp_out[0..], out[0..]);909 testing.expectEqualSlices(u8, exp_out[0..], out[0..]);
801 }910 }
802}911}
803912
804test "open" {913test "open" {
805 {914 {
806 const ciphertext = [_]u8{ 0xa0, 0x78, 0x4d, 0x7a, 0x47, 0x16, 0xf3, 0xfe, 0xb4, 0xf6, 0x4e, 0x7f, 0x4b, 0x39, 0xbf, 0x4 };915 const c = [_]u8{ 0xa0, 0x78, 0x4d, 0x7a, 0x47, 0x16, 0xf3, 0xfe, 0xb4, 0xf6, 0x4e, 0x7f, 0x4b, 0x39, 0xbf, 0x4 };
807 const data = "";916 const ad = "";
808 const key = [_]u8{917 const key = [_]u8{
809 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,918 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
810 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,919 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
...@@ -813,11 +922,11 @@ test "open" {...@@ -813,11 +922,11 @@ test "open" {
813 const exp_out = "";922 const exp_out = "";
814923
815 var out: [exp_out.len]u8 = undefined;924 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);
817 testing.expectEqualSlices(u8, exp_out[0..], out[0..]);926 testing.expectEqualSlices(u8, exp_out[0..], out[0..]);
818 }927 }
819 {928 {
820 const ciphertext = [_]u8{929 const c = [_]u8{
821 0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb, 0x7b, 0x86, 0xaf, 0xbc, 0x53, 0xef, 0x7e, 0xc2,930 0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb, 0x7b, 0x86, 0xaf, 0xbc, 0x53, 0xef, 0x7e, 0xc2,
822 0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x8, 0xfe, 0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6,931 0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x8, 0xfe, 0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6,
823 0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67, 0x12, 0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b,932 0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67, 0x12, 0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b,
...@@ -828,7 +937,7 @@ test "open" {...@@ -828,7 +937,7 @@ test "open" {
828 0x61, 0x16, 0x1a, 0xe1, 0xb, 0x59, 0x4f, 0x9, 0xe2, 0x6a, 0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60,937 0x61, 0x16, 0x1a, 0xe1, 0xb, 0x59, 0x4f, 0x9, 0xe2, 0x6a, 0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60,
829 0x6, 0x91,938 0x6, 0x91,
830 };939 };
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 };
832 const key = [_]u8{941 const key = [_]u8{
833 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,942 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
834 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,943 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
...@@ -846,126 +955,45 @@ test "open" {...@@ -846,126 +955,45 @@ test "open" {
846 };955 };
847956
848 var out: [exp_out.len]u8 = undefined;957 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);
850 testing.expectEqualSlices(u8, exp_out[0..], out[0..]);959 testing.expectEqualSlices(u8, exp_out[0..], out[0..]);
851960
852 // corrupting the ciphertext, data, key, or nonce should cause a failure961 // corrupting the ciphertext, data, key, or nonce should cause a failure
853 var bad_ciphertext = ciphertext;962 var bad_c = c;
854 bad_ciphertext[0] ^= 1;963 bad_c[0] ^= 1;
855 testing.expectError(error.AuthenticationFailed, chacha20poly1305Open(out[0..], bad_ciphertext[0..], data[0..], key, nonce));964 testing.expectError(error.AuthenticationFailed, ChaCha20Poly1305.decrypt(out[0..], bad_c[0..out.len], bad_c[out.len..].*, ad[0..], nonce, key));
856 var bad_data = data;965 var bad_ad = ad;
857 bad_data[0] ^= 1;966 bad_ad[0] ^= 1;
858 testing.expectError(error.AuthenticationFailed, chacha20poly1305Open(out[0..], ciphertext[0..], bad_data[0..], key, nonce));967 testing.expectError(error.AuthenticationFailed, ChaCha20Poly1305.decrypt(out[0..], c[0..out.len], c[out.len..].*, bad_ad[0..], nonce, key));
859 var bad_key = key;968 var bad_key = key;
860 bad_key[0] ^= 1;969 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));
862 var bad_nonce = nonce;971 var bad_nonce = nonce;
863 bad_nonce[0] ^= 1;972 bad_nonce[0] ^= 1;
864 testing.expectError(error.AuthenticationFailed, chacha20poly1305Open(out[0..], ciphertext[0..], data[0..], key, bad_nonce));973 testing.expectError(error.AuthenticationFailed, ChaCha20Poly1305.decrypt(out[0..], c[0..out.len], c[out.len..].*, ad[0..], bad_nonce, key));
865
866 // a short ciphertext should result in a different error
867 testing.expectError(error.InvalidMessage, chacha20poly1305Open(out[0..], "", data[0..], key, bad_nonce));
868 }974 }
869}975}
870976
871test "crypto.xchacha20" {977test "crypto.xchacha20" {
872 const key = [_]u8{69} ** 32;978 const key = [_]u8{69} ** 32;
873 const nonce = [_]u8{42} ** 24;979 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.";
875 {981 {
876 var ciphertext: [input.len]u8 = undefined;982 var c: [m.len]u8 = undefined;
877 XChaCha20IETF.xor(ciphertext[0..], input[0..], 0, key, nonce);983 XChaCha20IETF.xor(c[0..], m[0..], 0, key, nonce);
878 var buf: [2 * ciphertext.len]u8 = undefined;984 var buf: [2 * c.len]u8 = undefined;
879 testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&ciphertext)}), "E0A1BCF939654AFDBDC1746EC49832647C19D891F0D1A81FC0C1703B4514BDEA584B512F6908C2C5E9DD18D5CBC1805DE5803FE3B9CA5F193FB8359E91FAB0C3BB40309A292EB1CF49685C65C4A3ADF4F11DB0CD2B6B67FBC174BC2E860E8F769FD3565BBFAD1C845E05A0FED9BE167C240D");985 testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&c)}), "E0A1BCF939654AFDBDC1746EC49832647C19D891F0D1A81FC0C1703B4514BDEA584B512F6908C2C5E9DD18D5CBC1805DE5803FE3B9CA5F193FB8359E91FAB0C3BB40309A292EB1CF49685C65C4A3ADF4F11DB0CD2B6B67FBC174BC2E860E8F769FD3565BBFAD1C845E05A0FED9BE167C240D");
880 }986 }
881 {987 {
882 const data = "Additional data";988 const ad = "Additional data";
883 var ciphertext: [input.len + xchacha20poly1305_tag_length]u8 = undefined;989 var c: [m.len + XChaCha20Poly1305.tag_length]u8 = undefined;
884 xchacha20poly1305Seal(ciphertext[0..], input, data, key, nonce);990 XChaCha20Poly1305.encrypt(c[0..m.len], c[m.len..], m, ad, nonce, key);
885 var out: [input.len]u8 = undefined;991 var out: [m.len]u8 = undefined;
886 try xchacha20poly1305Open(out[0..], ciphertext[0..], data, key, nonce);992 try XChaCha20Poly1305.decrypt(out[0..], c[0..m.len], c[m.len..].*, ad, nonce, key);
887 var buf: [2 * ciphertext.len]u8 = undefined;993 var buf: [2 * c.len]u8 = undefined;
888 testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&ciphertext)}), "994D2DD32333F48E53650C02C7A2ABB8E018B0836D7175AEC779F52E961780768F815C58F1AA52D211498DB89B9216763F569C9433A6BBFCEFB4D4A49387A4C5207FBB3B5A92B5941294DF30588C6740D39DC16FA1F0E634F7246CF7CDCB978E44347D89381B7A74EB7084F754B90BDE9AAF5A94B8F2A85EFD0B50692AE2D425E234");994 testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&c)}), "994D2DD32333F48E53650C02C7A2ABB8E018B0836D7175AEC779F52E961780768F815C58F1AA52D211498DB89B9216763F569C9433A6BBFCEFB4D4A49387A4C5207FBB3B5A92B5941294DF30588C6740D39DC16FA1F0E634F7246CF7CDCB978E44347D89381B7A74EB7084F754B90BDE9AAF5A94B8F2A85EFD0B50692AE2D425E234");
889 testing.expectEqualSlices(u8, out[0..], input);995 testing.expectEqualSlices(u8, out[0..], m);
890 ciphertext[0] += 1;996 c[0] += 1;
891 testing.expectError(error.AuthenticationFailed, xchacha20poly1305Open(out[0..], ciphertext[0..], data, key, nonce));997 testing.expectError(error.AuthenticationFailed, XChaCha20Poly1305.decrypt(out[0..], c[0..m.len], c[m.len..].*, ad, nonce, key));
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));
970 }998 }
971}999}
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;...@@ -20,6 +20,7 @@ const assert = std.debug.assert;
20const testing = std.testing;20const testing = std.testing;
21const htest = @import("test.zig");21const htest = @import("test.zig");
22const Vector = std.meta.Vector;22const Vector = std.meta.Vector;
23const Error = std.crypto.Error;
2324
24pub const State = struct {25pub const State = struct {
25 pub const BLOCKBYTES = 48;26 pub const BLOCKBYTES = 48;
...@@ -392,7 +393,7 @@ pub const Aead = struct {...@@ -392,7 +393,7 @@ pub const Aead = struct {
392 /// npub: public nonce393 /// npub: public nonce
393 /// k: private key394 /// k: private key
394 /// NOTE: the check of the authentication tag is currently not done in constant time395 /// 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 {
396 assert(c.len == m.len);397 assert(c.len == m.len);
397398
398 var state = Aead.init(ad, npub, k);399 var state = Aead.init(ad, npub, k);
...@@ -429,7 +430,7 @@ pub const Aead = struct {...@@ -429,7 +430,7 @@ pub const Aead = struct {
429 // TODO: use a constant-time equality check here, see https://github.com/ziglang/zig/issues/1776430 // TODO: use a constant-time equality check here, see https://github.com/ziglang/zig/issues/1776
430 if (!mem.eql(u8, buf[0..State.RATE], &tag)) {431 if (!mem.eql(u8, buf[0..State.RATE], &tag)) {
431 @memset(m.ptr, undefined, m.len);432 @memset(m.ptr, undefined, m.len);
432 return error.InvalidMessage;433 return error.AuthenticationFailed;
433 }434 }
434 }435 }
435};436};
lib/std/crypto/isap.zig+2-1
...@@ -3,6 +3,7 @@ const debug = std.debug;...@@ -3,6 +3,7 @@ const debug = std.debug;
3const mem = std.mem;3const mem = std.mem;
4const math = std.math;4const math = std.math;
5const testing = std.testing;5const testing = std.testing;
6const Error = std.crypto.Error;
67
7/// ISAPv2 is an authenticated encryption system hardened against side channels and fault attacks.8/// ISAPv2 is an authenticated encryption system hardened against side channels and fault attacks.
8/// https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/round-2/spec-doc-rnd2/isap-spec-round2.pdf9/// 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 {...@@ -217,7 +218,7 @@ pub const IsapA128A = struct {
217 tag.* = mac(c, ad, npub, key);218 tag.* = mac(c, ad, npub, key);
218 }219 }
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 {
221 var computed_tag = mac(c, ad, npub, key);222 var computed_tag = mac(c, ad, npub, key);
222 var acc: u8 = 0;223 var acc: u8 = 0;
223 for (computed_tag) |_, j| {224 for (computed_tag) |_, j| {
lib/std/crypto/pbkdf2.zig+70-80
...@@ -7,6 +7,7 @@...@@ -7,6 +7,7 @@
7const std = @import("std");7const std = @import("std");
8const mem = std.mem;8const mem = std.mem;
9const maxInt = std.math.maxInt;9const maxInt = std.math.maxInt;
10const Error = std.crypto.Error;
1011
11// RFC 2898 Section 5.212// RFC 2898 Section 5.2
12//13//
...@@ -19,36 +20,28 @@ const maxInt = std.math.maxInt;...@@ -19,36 +20,28 @@ const maxInt = std.math.maxInt;
19// pseudorandom function. See Appendix B.1 for further discussion.)20// pseudorandom function. See Appendix B.1 for further discussion.)
20// PBKDF2 is recommended for new applications.21// PBKDF2 is recommended for new applications.
21//22//
22// PBKDF2 (P, S, c, dkLen)23// PBKDF2 (P, S, c, dk_len)
23//24//
24// Options: PRF underlying pseudorandom function (hLen25// Options: PRF underlying pseudorandom function (h_len
25// denotes the length in octets of the26// denotes the length in octets of the
26// pseudorandom function output)27// pseudorandom function output)
27//28//
28// Input: P password, an octet string29// Input: P password, an octet string
29// S salt, an octet string30// S salt, an octet string
30// c iteration count, a positive integer31// c iteration count, a positive integer
31// dkLen intended length in octets of the derived32// dk_len intended length in octets of the derived
32// key, a positive integer, at most33// key, a positive integer, at most
33// (2^32 - 1) * hLen34// (2^32 - 1) * h_len
34//35//
35// Output: DK derived key, a dkLen-octet string36// Output: DK derived key, a dk_len-octet string
3637
37// Based on Apple's CommonKeyDerivation, based originally on code by Damien Bergamini.38// Based on Apple's CommonKeyDerivation, based originally on code by Damien Bergamini.
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
47/// Apply PBKDF2 to generate a key from a password.40/// Apply PBKDF2 to generate a key from a password.
48///41///
49/// PBKDF2 is defined in RFC 2898, and is a recommendation of NIST SP 800-132.42/// PBKDF2 is defined in RFC 2898, and is a recommendation of NIST SP 800-132.
50///43///
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.
52/// May be uninitialized. All bytes will be overwritten.45/// May be uninitialized. All bytes will be overwritten.
53/// Maximum size is `maxInt(u32) * Hash.digest_length`46/// Maximum size is `maxInt(u32) * Hash.digest_length`
54/// It is a programming error to pass buffer longer than the maximum size.47/// It is a programming error to pass buffer longer than the maximum size.
...@@ -59,43 +52,38 @@ pub const Pbkdf2Error = error{...@@ -59,43 +52,38 @@ pub const Pbkdf2Error = error{
59///52///
60/// rounds: Iteration count. Must be greater than 0. Common values range from 1,000 to 100,000.53/// rounds: Iteration count. Must be greater than 0. Common values range from 1,000 to 100,000.
61/// Larger iteration counts improve security by increasing the time required to compute54/// 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.
63///56///
64/// Prf: Pseudo-random function to use. A common choice is `std.crypto.auth.hmac.HmacSha256`.57/// Prf: Pseudo-random function to use. A common choice is `std.crypto.auth.hmac.HmacSha256`.
65pub fn pbkdf2(derivedKey: []u8, password: []const u8, salt: []const u8, rounds: u32, comptime Prf: type) Pbkdf2Error!void {58pub fn pbkdf2(dk: []u8, password: []const u8, salt: []const u8, rounds: u32, comptime Prf: type) Error!void {
66 if (rounds < 1) return error.TooFewRounds;59 if (rounds < 1) return error.WeakParameters;
6760
68 const dkLen = derivedKey.len;61 const dk_len = dk.len;
69 const hLen = Prf.mac_length;62 const h_len = Prf.mac_length;
70 comptime std.debug.assert(hLen >= 1);63 comptime std.debug.assert(h_len >= 1);
7164
72 // FromSpec:65 // FromSpec:
73 //66 //
74 // 1. If dkLen > maxInt(u32) * hLen, output "derived key too long" and67 // 1. If dk_len > maxInt(u32) * h_len, output "derived key too long" and
75 // stop.68 // stop.
76 //69 //
77 if (comptime (maxInt(usize) > maxInt(u32) * hLen) and (dkLen > @as(usize, maxInt(u32) * hLen))) {70 if (dk_len / h_len >= maxInt(u32)) {
78 // If maxInt(usize) is less than `maxInt(u32) * hLen` then dkLen is always inbounds71 // Counter starts at 1 and is 32 bit, so if we have to return more blocks, we would overflow
79 return error.DerivedKeyTooLong;72 return error.OutputTooLong;
80 }73 }
8174
82 // FromSpec:75 // FromSpec:
83 //76 //
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,
85 // rounding up, and let r be the number of bytes in the last78 // rounding up, and let r be the number of bytes in the last
86 // block79 // block
87 //80 //
8881
89 // l will not overflow, proof:82 const blocks_count = @intCast(u32, std.math.divCeil(usize, dk_len, h_len) catch unreachable);
90 // let `L(dkLen, hLen) = (dkLen + hLen - 1) / hLen`83 var r = dk_len % h_len;
91 // then `L^-1(l, hLen) = l*hLen - hLen + 1`84 if (r == 0) {
92 // 1) L^-1(maxInt(u32), hLen) <= maxInt(u32)*hLen85 r = h_len;
93 // 2) maxInt(u32)*hLen - hLen + 1 <= maxInt(u32)*hLen // subtract maxInt(u32)*hLen + 186 }
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_;
9987
100 // FromSpec:88 // FromSpec:
101 //89 //
...@@ -125,37 +113,38 @@ pub fn pbkdf2(derivedKey: []u8, password: []const u8, salt: []const u8, rounds:...@@ -125,37 +113,38 @@ pub fn pbkdf2(derivedKey: []u8, password: []const u8, salt: []const u8, rounds:
125 // Here, INT (i) is a four-octet encoding of the integer i, most113 // Here, INT (i) is a four-octet encoding of the integer i, most
126 // significant octet first.114 // significant octet first.
127 //115 //
128 // 4. Concatenate the blocks and extract the first dkLen octets to116 // 4. Concatenate the blocks and extract the first dk_len octets to
129 // produce a derived key DK:117 // produce a derived key DK:
130 //118 //
131 // DK = T_1 || T_2 || ... || T_l<0..r-1>119 // DK = T_1 || T_2 || ... || T_l<0..r-1>
132 var block: u32 = 0; // Spec limits to u32120
133 while (block < l) : (block += 1) {121 var block: u32 = 0;
134 var prevBlock: [hLen]u8 = undefined;122 while (block < blocks_count) : (block += 1) {
135 var newBlock: [hLen]u8 = undefined;123 var prev_block: [h_len]u8 = undefined;
124 var new_block: [h_len]u8 = undefined;
136125
137 // U_1 = PRF (P, S || INT (i))126 // U_1 = PRF (P, S || INT (i))
138 const blockIndex = mem.toBytes(mem.nativeToBig(u32, block + 1)); // Block index starts at 0001127 const block_index = mem.toBytes(mem.nativeToBig(u32, block + 1)); // Block index starts at 0001
139 var ctx = Prf.init(password);128 var ctx = Prf.init(password);
140 ctx.update(salt);129 ctx.update(salt);
141 ctx.update(blockIndex[0..]);130 ctx.update(block_index[0..]);
142 ctx.final(prevBlock[0..]);131 ctx.final(prev_block[0..]);
143132
144 // Choose portion of DK to write into (T_n) and initialize133 // Choose portion of DK to write into (T_n) and initialize
145 const offset = block * hLen;134 const offset = block * h_len;
146 const blockLen = if (block != l - 1) hLen else r;135 const block_len = if (block != blocks_count - 1) h_len else r;
147 const dkBlock: []u8 = derivedKey[offset..][0..blockLen];136 const dk_block: []u8 = dk[offset..][0..block_len];
148 mem.copy(u8, dkBlock, prevBlock[0..dkBlock.len]);137 mem.copy(u8, dk_block, prev_block[0..dk_block.len]);
149138
150 var i: u32 = 1;139 var i: u32 = 1;
151 while (i < rounds) : (i += 1) {140 while (i < rounds) : (i += 1) {
152 // U_c = PRF (P, U_{c-1})141 // U_c = PRF (P, U_{c-1})
153 Prf.create(&newBlock, prevBlock[0..], password);142 Prf.create(&new_block, prev_block[0..], password);
154 mem.copy(u8, prevBlock[0..], newBlock[0..]);143 mem.copy(u8, prev_block[0..], new_block[0..]);
155144
156 // F (P, S, c, i) = U_1 \xor U_2 \xor ... \xor U_c145 // F (P, S, c, i) = U_1 \xor U_2 \xor ... \xor U_c
157 for (dkBlock) |_, j| {146 for (dk_block) |_, j| {
158 dkBlock[j] ^= newBlock[j];147 dk_block[j] ^= new_block[j];
159 }148 }
160 }149 }
161 }150 }
...@@ -165,49 +154,50 @@ const htest = @import("test.zig");...@@ -165,49 +154,50 @@ const htest = @import("test.zig");
165const HmacSha1 = std.crypto.auth.hmac.HmacSha1;154const HmacSha1 = std.crypto.auth.hmac.HmacSha1;
166155
167// RFC 6070 PBKDF2 HMAC-SHA1 Test Vectors156// RFC 6070 PBKDF2 HMAC-SHA1 Test Vectors
157
168test "RFC 6070 one iteration" {158test "RFC 6070 one iteration" {
169 const p = "password";159 const p = "password";
170 const s = "salt";160 const s = "salt";
171 const c = 1;161 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
178 const expected = "0c60c80f961f0e71f3a9b524af6012062fe037a6";168 const expected = "0c60c80f961f0e71f3a9b524af6012062fe037a6";
179169
180 htest.assertEqual(expected, derivedKey[0..]);170 htest.assertEqual(expected, dk[0..]);
181}171}
182172
183test "RFC 6070 two iterations" {173test "RFC 6070 two iterations" {
184 const p = "password";174 const p = "password";
185 const s = "salt";175 const s = "salt";
186 const c = 2;176 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
193 const expected = "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957";183 const expected = "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957";
194184
195 htest.assertEqual(expected, derivedKey[0..]);185 htest.assertEqual(expected, dk[0..]);
196}186}
197187
198test "RFC 6070 4096 iterations" {188test "RFC 6070 4096 iterations" {
199 const p = "password";189 const p = "password";
200 const s = "salt";190 const s = "salt";
201 const c = 4096;191 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
208 const expected = "4b007901b765489abead49d926f721d065a429c1";198 const expected = "4b007901b765489abead49d926f721d065a429c1";
209199
210 htest.assertEqual(expected, derivedKey[0..]);200 htest.assertEqual(expected, dk[0..]);
211}201}
212202
213test "RFC 6070 16,777,216 iterations" {203test "RFC 6070 16,777,216 iterations" {
...@@ -219,48 +209,48 @@ test "RFC 6070 16,777,216 iterations" {...@@ -219,48 +209,48 @@ test "RFC 6070 16,777,216 iterations" {
219 const p = "password";209 const p = "password";
220 const s = "salt";210 const s = "salt";
221 const c = 16777216;211 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
228 const expected = "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984";218 const expected = "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984";
229219
230 htest.assertEqual(expected, derivedKey[0..]);220 htest.assertEqual(expected, dk[0..]);
231}221}
232222
233test "RFC 6070 multi-block salt and password" {223test "RFC 6070 multi-block salt and password" {
234 const p = "passwordPASSWORDpassword";224 const p = "passwordPASSWORDpassword";
235 const s = "saltSALTsaltSALTsaltSALTsaltSALTsalt";225 const s = "saltSALTsaltSALTsaltSALTsaltSALTsalt";
236 const c = 4096;226 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
243 const expected = "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038";233 const expected = "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038";
244234
245 htest.assertEqual(expected, derivedKey[0..]);235 htest.assertEqual(expected, dk[0..]);
246}236}
247237
248test "RFC 6070 embedded NUL" {238test "RFC 6070 embedded NUL" {
249 const p = "pass\x00word";239 const p = "pass\x00word";
250 const s = "sa\x00lt";240 const s = "sa\x00lt";
251 const c = 4096;241 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
258 const expected = "56fa6aa75548099dcc37d7f03425e0c3";248 const expected = "56fa6aa75548099dcc37d7f03425e0c3";
259249
260 htest.assertEqual(expected, derivedKey[0..]);250 htest.assertEqual(expected, dk[0..]);
261}251}
262252
263test "Very large dkLen" {253test "Very large dk_len" {
264 // This test allocates 8GB of memory and is expected to take several hours to run.254 // This test allocates 8GB of memory and is expected to take several hours to run.
265 if (true) {255 if (true) {
266 return error.SkipZigTest;256 return error.SkipZigTest;
...@@ -268,13 +258,13 @@ test "Very large dkLen" {...@@ -268,13 +258,13 @@ test "Very large dkLen" {
268 const p = "password";258 const p = "password";
269 const s = "salt";259 const s = "salt";
270 const c = 1;260 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);
274 defer {264 defer {
275 std.testing.allocator.free(derivedKey);265 std.testing.allocator.free(dk);
276 }266 }
277267
278 try pbkdf2(derivedKey, p, s, c, HmacSha1);
279 // Just verify this doesn't crash with an overflow268 // Just verify this doesn't crash with an overflow
269 try pbkdf2(dk, p, s, c, HmacSha1);
280}270}
lib/std/crypto/salsa20.zig+8-7
...@@ -15,6 +15,7 @@ const Vector = std.meta.Vector;...@@ -15,6 +15,7 @@ const Vector = std.meta.Vector;
15const Poly1305 = crypto.onetimeauth.Poly1305;15const Poly1305 = crypto.onetimeauth.Poly1305;
16const Blake2b = crypto.hash.blake2.Blake2b;16const Blake2b = crypto.hash.blake2.Blake2b;
17const X25519 = crypto.dh.X25519;17const X25519 = crypto.dh.X25519;
18const Error = crypto.Error;
1819
19const Salsa20VecImpl = struct {20const Salsa20VecImpl = struct {
20 const Lane = Vector(4, u32);21 const Lane = Vector(4, u32);
...@@ -398,7 +399,7 @@ pub const XSalsa20Poly1305 = struct {...@@ -398,7 +399,7 @@ pub const XSalsa20Poly1305 = struct {
398 /// ad: Associated Data399 /// ad: Associated Data
399 /// npub: public nonce400 /// npub: public nonce
400 /// k: private key401 /// 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 {
402 debug.assert(c.len == m.len);403 debug.assert(c.len == m.len);
403 const extended = extend(k, npub);404 const extended = extend(k, npub);
404 var block0 = [_]u8{0} ** 64;405 var block0 = [_]u8{0} ** 64;
...@@ -446,7 +447,7 @@ pub const SecretBox = struct {...@@ -446,7 +447,7 @@ pub const SecretBox = struct {
446447
447 /// Verify and decrypt `c` using a nonce `npub` and a key `k`.448 /// Verify and decrypt `c` using a nonce `npub` and a key `k`.
448 /// `m` must be exactly `tag_length` smaller than `c`, as `c` includes an authentication tag in addition to the encrypted message.449 /// `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 {
450 if (c.len < tag_length) {451 if (c.len < tag_length) {
451 return error.AuthenticationFailed;452 return error.AuthenticationFailed;
452 }453 }
...@@ -481,20 +482,20 @@ pub const Box = struct {...@@ -481,20 +482,20 @@ pub const Box = struct {
481 pub const KeyPair = X25519.KeyPair;482 pub const KeyPair = X25519.KeyPair;
482483
483 /// Compute a secret suitable for `secretbox` given a recipent's public key and a sender's secret key.484 /// 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 {
485 const p = try X25519.scalarmult(secret_key, public_key);486 const p = try X25519.scalarmult(secret_key, public_key);
486 const zero = [_]u8{0} ** 16;487 const zero = [_]u8{0} ** 16;
487 return Salsa20Impl.hsalsa20(zero, p);488 return Salsa20Impl.hsalsa20(zero, p);
488 }489 }
489490
490 /// Encrypt and authenticate a message using a recipient's public key `public_key` and a sender's `secret_key`.491 /// 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 {
492 const shared_key = try createSharedSecret(public_key, secret_key);493 const shared_key = try createSharedSecret(public_key, secret_key);
493 return SecretBox.seal(c, m, npub, shared_key);494 return SecretBox.seal(c, m, npub, shared_key);
494 }495 }
495496
496 /// Verify and decrypt a message using a recipient's secret key `public_key` and a sender's `public_key`.497 /// 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 {
498 const shared_key = try createSharedSecret(public_key, secret_key);499 const shared_key = try createSharedSecret(public_key, secret_key);
499 return SecretBox.open(m, c, npub, shared_key);500 return SecretBox.open(m, c, npub, shared_key);
500 }501 }
...@@ -527,7 +528,7 @@ pub const SealedBox = struct {...@@ -527,7 +528,7 @@ pub const SealedBox = struct {
527528
528 /// Encrypt a message `m` for a recipient whose public key is `public_key`.529 /// Encrypt a message `m` for a recipient whose public key is `public_key`.
529 /// `c` must be `seal_length` bytes larger than `m`, so that the required metadata can be added.530 /// `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 {
531 debug.assert(c.len == m.len + seal_length);532 debug.assert(c.len == m.len + seal_length);
532 var ekp = try KeyPair.create(null);533 var ekp = try KeyPair.create(null);
533 const nonce = createNonce(ekp.public_key, public_key);534 const nonce = createNonce(ekp.public_key, public_key);
...@@ -538,7 +539,7 @@ pub const SealedBox = struct {...@@ -538,7 +539,7 @@ pub const SealedBox = struct {
538539
539 /// Decrypt a message using a key pair.540 /// Decrypt a message using a key pair.
540 /// `m` must be exactly `seal_length` bytes smaller than `c`, as `c` also includes metadata.541 /// `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 {
542 if (c.len < seal_length) {543 if (c.len < seal_length) {
543 return error.AuthenticationFailed;544 return error.AuthenticationFailed;
544 }545 }
lib/std/debug.zig-18
...@@ -250,24 +250,6 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c...@@ -250,24 +250,6 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c
250 resetSegfaultHandler();250 resetSegfaultHandler();
251 }251 }
252252
253 if (comptime std.Target.current.isDarwin() and std.Target.current.cpu.arch == .aarch64)
254 nosuspend {
255 // As a workaround for not having threadlocal variable support in LLD for this target,
256 // we have a simpler panic implementation that does not use threadlocal variables.
257 // TODO https://github.com/ziglang/zig/issues/7527
258 const stderr = io.getStdErr().writer();
259 if (@atomicRmw(u8, &panicking, .Add, 1, .SeqCst) == 0) {
260 stderr.print("panic: " ++ format ++ "\n", args) catch os.abort();
261 if (trace) |t| {
262 dumpStackTrace(t.*);
263 }
264 dumpCurrentStackTrace(first_trace_addr);
265 } else {
266 stderr.print("Panicked during a panic. Aborting.\n", .{}) catch os.abort();
267 }
268 os.abort();
269 };
270
271 nosuspend switch (panic_stage) {253 nosuspend switch (panic_stage) {
272 0 => {254 0 => {
273 panic_stage = 1;255 panic_stage = 1;
lib/std/enums.zig created+1281
...@@ -0,0 +1,1281 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2021 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6
7//! This module contains utilities and data structures for working with enums.
8
9const std = @import("std.zig");
10const assert = std.debug.assert;
11const testing = std.testing;
12const EnumField = std.builtin.TypeInfo.EnumField;
13
14/// Returns a struct with a field matching each unique named enum element.
15/// If the enum is extern and has multiple names for the same value, only
16/// the first name is used. Each field is of type Data and has the provided
17/// default, which may be undefined.
18pub fn EnumFieldStruct(comptime E: type, comptime Data: type, comptime field_default: ?Data) type {
19 const StructField = std.builtin.TypeInfo.StructField;
20 var fields: []const StructField = &[_]StructField{};
21 for (uniqueFields(E)) |field, i| {
22 fields = fields ++ &[_]StructField{.{
23 .name = field.name,
24 .field_type = Data,
25 .default_value = field_default,
26 .is_comptime = false,
27 .alignment = if (@sizeOf(Data) > 0) @alignOf(Data) else 0,
28 }};
29 }
30 return @Type(.{ .Struct = .{
31 .layout = .Auto,
32 .fields = fields,
33 .decls = &[_]std.builtin.TypeInfo.Declaration{},
34 .is_tuple = false,
35 }});
36}
37
38/// Looks up the supplied fields in the given enum type.
39/// Uses only the field names, field values are ignored.
40/// The result array is in the same order as the input.
41pub fn valuesFromFields(comptime E: type, comptime fields: []const EnumField) []const E {
42 comptime {
43 var result: [fields.len]E = undefined;
44 for (fields) |f, i| {
45 result[i] = @field(E, f.name);
46 }
47 return &result;
48 }
49}
50
51test "std.enums.valuesFromFields" {
52 const E = extern enum { a, b, c, d = 0 };
53 const fields = valuesFromFields(E, &[_]EnumField{
54 .{ .name = "b", .value = undefined },
55 .{ .name = "a", .value = undefined },
56 .{ .name = "a", .value = undefined },
57 .{ .name = "d", .value = undefined },
58 });
59 testing.expectEqual(E.b, fields[0]);
60 testing.expectEqual(E.a, fields[1]);
61 testing.expectEqual(E.d, fields[2]); // a == d
62 testing.expectEqual(E.d, fields[3]);
63}
64
65/// Returns the set of all named values in the given enum, in
66/// declaration order.
67pub fn values(comptime E: type) []const E {
68 return comptime valuesFromFields(E, @typeInfo(E).Enum.fields);
69}
70
71test "std.enum.values" {
72 const E = extern enum { a, b, c, d = 0 };
73 testing.expectEqualSlices(E, &.{.a, .b, .c, .d}, values(E));
74}
75
76/// Returns the set of all unique named values in the given enum, in
77/// declaration order. For repeated values in extern enums, only the
78/// first name for each value is included.
79pub fn uniqueValues(comptime E: type) []const E {
80 return comptime valuesFromFields(E, uniqueFields(E));
81}
82
83test "std.enum.uniqueValues" {
84 const E = extern enum { a, b, c, d = 0, e, f = 3 };
85 testing.expectEqualSlices(E, &.{.a, .b, .c, .f}, uniqueValues(E));
86
87 const F = enum { a, b, c };
88 testing.expectEqualSlices(F, &.{.a, .b, .c}, uniqueValues(F));
89}
90
91/// Returns the set of all unique field values in the given enum, in
92/// declaration order. For repeated values in extern enums, only the
93/// first name for each value is included.
94pub fn uniqueFields(comptime E: type) []const EnumField {
95 comptime {
96 const info = @typeInfo(E).Enum;
97 const raw_fields = info.fields;
98 // Only extern enums can contain duplicates,
99 // so fast path other types.
100 if (info.layout != .Extern) {
101 return raw_fields;
102 }
103
104 var unique_fields: []const EnumField = &[_]EnumField{};
105 outer:
106 for (raw_fields) |candidate| {
107 for (unique_fields) |u| {
108 if (u.value == candidate.value)
109 continue :outer;
110 }
111 unique_fields = unique_fields ++ &[_]EnumField{candidate};
112 }
113
114 return unique_fields;
115 }
116}
117
118/// Determines the length of a direct-mapped enum array, indexed by
119/// @intCast(usize, @enumToInt(enum_value)). The enum must be exhaustive.
120/// If the enum contains any fields with values that cannot be represented
121/// by usize, a compile error is issued. The max_unused_slots parameter limits
122/// the total number of items which have no matching enum key (holes in the enum
123/// numbering). So for example, if an enum has values 1, 2, 5, and 6, max_unused_slots
124/// must be at least 3, to allow unused slots 0, 3, and 4.
125fn directEnumArrayLen(comptime E: type, comptime max_unused_slots: comptime_int) comptime_int {
126 const info = @typeInfo(E).Enum;
127 if (!info.is_exhaustive) {
128 @compileError("Cannot create direct array of non-exhaustive enum "++@typeName(E));
129 }
130
131 var max_value: comptime_int = -1;
132 const max_usize: comptime_int = ~@as(usize, 0);
133 const fields = uniqueFields(E);
134 for (fields) |f| {
135 if (f.value < 0) {
136 @compileError("Cannot create a direct enum array for "++@typeName(E)++", field ."++f.name++" has a negative value.");
137 }
138 if (f.value > max_value) {
139 if (f.value > max_usize) {
140 @compileError("Cannot create a direct enum array for "++@typeName(E)++", field ."++f.name++" is larger than the max value of usize.");
141 }
142 max_value = f.value;
143 }
144 }
145
146 const unused_slots = max_value + 1 - fields.len;
147 if (unused_slots > max_unused_slots) {
148 const unused_str = std.fmt.comptimePrint("{d}", .{unused_slots});
149 const allowed_str = std.fmt.comptimePrint("{d}", .{max_unused_slots});
150 @compileError("Cannot create a direct enum array for "++@typeName(E)++". It would have "++unused_str++" unused slots, but only "++allowed_str++" are allowed.");
151 }
152
153 return max_value + 1;
154}
155
156/// Initializes an array of Data which can be indexed by
157/// @intCast(usize, @enumToInt(enum_value)). The enum must be exhaustive.
158/// If the enum contains any fields with values that cannot be represented
159/// by usize, a compile error is issued. The max_unused_slots parameter limits
160/// the total number of items which have no matching enum key (holes in the enum
161/// numbering). So for example, if an enum has values 1, 2, 5, and 6, max_unused_slots
162/// must be at least 3, to allow unused slots 0, 3, and 4.
163/// The init_values parameter must be a struct with field names that match the enum values.
164/// If the enum has multiple fields with the same value, the name of the first one must
165/// be used.
166pub fn directEnumArray(
167 comptime E: type,
168 comptime Data: type,
169 comptime max_unused_slots: comptime_int,
170 init_values: EnumFieldStruct(E, Data, null),
171) [directEnumArrayLen(E, max_unused_slots)]Data {
172 return directEnumArrayDefault(E, Data, null, max_unused_slots, init_values);
173}
174
175test "std.enums.directEnumArray" {
176 const E = enum(i4) { a = 4, b = 6, c = 2 };
177 var runtime_false: bool = false;
178 const array = directEnumArray(E, bool, 4, .{
179 .a = true,
180 .b = runtime_false,
181 .c = true,
182 });
183
184 testing.expectEqual([7]bool, @TypeOf(array));
185 testing.expectEqual(true, array[4]);
186 testing.expectEqual(false, array[6]);
187 testing.expectEqual(true, array[2]);
188}
189
190/// Initializes an array of Data which can be indexed by
191/// @intCast(usize, @enumToInt(enum_value)). The enum must be exhaustive.
192/// If the enum contains any fields with values that cannot be represented
193/// by usize, a compile error is issued. The max_unused_slots parameter limits
194/// the total number of items which have no matching enum key (holes in the enum
195/// numbering). So for example, if an enum has values 1, 2, 5, and 6, max_unused_slots
196/// must be at least 3, to allow unused slots 0, 3, and 4.
197/// The init_values parameter must be a struct with field names that match the enum values.
198/// If the enum has multiple fields with the same value, the name of the first one must
199/// be used.
200pub fn directEnumArrayDefault(
201 comptime E: type,
202 comptime Data: type,
203 comptime default: ?Data,
204 comptime max_unused_slots: comptime_int,
205 init_values: EnumFieldStruct(E, Data, default),
206) [directEnumArrayLen(E, max_unused_slots)]Data {
207 const len = comptime directEnumArrayLen(E, max_unused_slots);
208 var result: [len]Data = if (default) |d| [_]Data{d} ** len else undefined;
209 inline for (@typeInfo(@TypeOf(init_values)).Struct.fields) |f, i| {
210 const enum_value = @field(E, f.name);
211 const index = @intCast(usize, @enumToInt(enum_value));
212 result[index] = @field(init_values, f.name);
213 }
214 return result;
215}
216
217test "std.enums.directEnumArrayDefault" {
218 const E = enum(i4) { a = 4, b = 6, c = 2 };
219 var runtime_false: bool = false;
220 const array = directEnumArrayDefault(E, bool, false, 4, .{
221 .a = true,
222 .b = runtime_false,
223 });
224
225 testing.expectEqual([7]bool, @TypeOf(array));
226 testing.expectEqual(true, array[4]);
227 testing.expectEqual(false, array[6]);
228 testing.expectEqual(false, array[2]);
229}
230
231/// Cast an enum literal, value, or string to the enum value of type E
232/// with the same name.
233pub fn nameCast(comptime E: type, comptime value: anytype) E {
234 comptime {
235 const V = @TypeOf(value);
236 if (V == E) return value;
237 var name: ?[]const u8 = switch (@typeInfo(V)) {
238 .EnumLiteral, .Enum => @tagName(value),
239 .Pointer => if (std.meta.trait.isZigString(V)) value else null,
240 else => null,
241 };
242 if (name) |n| {
243 if (@hasField(E, n)) {
244 return @field(E, n);
245 }
246 @compileError("Enum "++@typeName(E)++" has no field named "++n);
247 }
248 @compileError("Cannot cast from "++@typeName(@TypeOf(value))++" to "++@typeName(E));
249 }
250}
251
252test "std.enums.nameCast" {
253 const A = enum { a = 0, b = 1 };
254 const B = enum { a = 1, b = 0 };
255 testing.expectEqual(A.a, nameCast(A, .a));
256 testing.expectEqual(A.a, nameCast(A, A.a));
257 testing.expectEqual(A.a, nameCast(A, B.a));
258 testing.expectEqual(A.a, nameCast(A, "a"));
259 testing.expectEqual(A.a, nameCast(A, @as(*const[1]u8, "a")));
260 testing.expectEqual(A.a, nameCast(A, @as([:0]const u8, "a")));
261 testing.expectEqual(A.a, nameCast(A, @as([]const u8, "a")));
262
263 testing.expectEqual(B.a, nameCast(B, .a));
264 testing.expectEqual(B.a, nameCast(B, A.a));
265 testing.expectEqual(B.a, nameCast(B, B.a));
266 testing.expectEqual(B.a, nameCast(B, "a"));
267
268 testing.expectEqual(B.b, nameCast(B, .b));
269 testing.expectEqual(B.b, nameCast(B, A.b));
270 testing.expectEqual(B.b, nameCast(B, B.b));
271 testing.expectEqual(B.b, nameCast(B, "b"));
272}
273
274/// A set of enum elements, backed by a bitfield. If the enum
275/// is not dense, a mapping will be constructed from enum values
276/// to dense indices. This type does no dynamic allocation and
277/// can be copied by value.
278pub fn EnumSet(comptime E: type) type {
279 const mixin = struct {
280 fn EnumSetExt(comptime Self: type) type {
281 const Indexer = Self.Indexer;
282 return struct {
283 /// Initializes the set using a struct of bools
284 pub fn init(init_values: EnumFieldStruct(E, bool, false)) Self {
285 var result = Self{};
286 comptime var i: usize = 0;
287 inline while (i < Self.len) : (i += 1) {
288 comptime const key = Indexer.keyForIndex(i);
289 comptime const tag = @tagName(key);
290 if (@field(init_values, tag)) {
291 result.bits.set(i);
292 }
293 }
294 return result;
295 }
296 };
297 }
298 };
299 return IndexedSet(EnumIndexer(E), mixin.EnumSetExt);
300}
301
302/// A map keyed by an enum, backed by a bitfield and a dense array.
303/// If the enum is not dense, a mapping will be constructed from
304/// enum values to dense indices. This type does no dynamic
305/// allocation and can be copied by value.
306pub fn EnumMap(comptime E: type, comptime V: type) type {
307 const mixin = struct {
308 fn EnumMapExt(comptime Self: type) type {
309 const Indexer = Self.Indexer;
310 return struct {
311 /// Initializes the map using a sparse struct of optionals
312 pub fn init(init_values: EnumFieldStruct(E, ?V, @as(?V, null))) Self {
313 var result = Self{};
314 comptime var i: usize = 0;
315 inline while (i < Self.len) : (i += 1) {
316 comptime const key = Indexer.keyForIndex(i);
317 comptime const tag = @tagName(key);
318 if (@field(init_values, tag)) |*v| {
319 result.bits.set(i);
320 result.values[i] = v.*;
321 }
322 }
323 return result;
324 }
325 /// Initializes a full mapping with all keys set to value.
326 /// Consider using EnumArray instead if the map will remain full.
327 pub fn initFull(value: V) Self {
328 var result = Self{
329 .bits = Self.BitSet.initFull(),
330 .values = undefined,
331 };
332 std.mem.set(V, &result.values, value);
333 return result;
334 }
335 /// Initializes a full mapping with supplied values.
336 /// Consider using EnumArray instead if the map will remain full.
337 pub fn initFullWith(init_values: EnumFieldStruct(E, V, @as(?V, null))) Self {
338 return initFullWithDefault(@as(?V, null), init_values);
339 }
340 /// Initializes a full mapping with a provided default.
341 /// Consider using EnumArray instead if the map will remain full.
342 pub fn initFullWithDefault(comptime default: ?V, init_values: EnumFieldStruct(E, V, default)) Self {
343 var result = Self{
344 .bits = Self.BitSet.initFull(),
345 .values = undefined,
346 };
347 comptime var i: usize = 0;
348 inline while (i < Self.len) : (i += 1) {
349 comptime const key = Indexer.keyForIndex(i);
350 comptime const tag = @tagName(key);
351 result.values[i] = @field(init_values, tag);
352 }
353 return result;
354 }
355 };
356 }
357 };
358 return IndexedMap(EnumIndexer(E), V, mixin.EnumMapExt);
359}
360
361/// An array keyed by an enum, backed by a dense array.
362/// If the enum is not dense, a mapping will be constructed from
363/// enum values to dense indices. This type does no dynamic
364/// allocation and can be copied by value.
365pub fn EnumArray(comptime E: type, comptime V: type) type {
366 const mixin = struct {
367 fn EnumArrayExt(comptime Self: type) type {
368 const Indexer = Self.Indexer;
369 return struct {
370 /// Initializes all values in the enum array
371 pub fn init(init_values: EnumFieldStruct(E, V, @as(?V, null))) Self {
372 return initDefault(@as(?V, null), init_values);
373 }
374
375 /// Initializes values in the enum array, with the specified default.
376 pub fn initDefault(comptime default: ?V, init_values: EnumFieldStruct(E, V, default)) Self {
377 var result = Self{ .values = undefined };
378 comptime var i: usize = 0;
379 inline while (i < Self.len) : (i += 1) {
380 const key = comptime Indexer.keyForIndex(i);
381 const tag = @tagName(key);
382 result.values[i] = @field(init_values, tag);
383 }
384 return result;
385 }
386 };
387 }
388 };
389 return IndexedArray(EnumIndexer(E), V, mixin.EnumArrayExt);
390}
391
392/// Pass this function as the Ext parameter to Indexed* if you
393/// do not want to attach any extensions. This parameter was
394/// originally an optional, but optional generic functions
395/// seem to be broken at the moment.
396/// TODO: Once #8169 is fixed, consider switching this param
397/// back to an optional.
398pub fn NoExtension(comptime Self: type) type {
399 return NoExt;
400}
401const NoExt = struct{};
402
403/// A set type with an Indexer mapping from keys to indices.
404/// Presence or absence is stored as a dense bitfield. This
405/// type does no allocation and can be copied by value.
406pub fn IndexedSet(comptime I: type, comptime Ext: fn(type)type) type {
407 comptime ensureIndexer(I);
408 return struct {
409 const Self = @This();
410
411 pub usingnamespace Ext(Self);
412
413 /// The indexing rules for converting between keys and indices.
414 pub const Indexer = I;
415 /// The element type for this set.
416 pub const Key = Indexer.Key;
417
418 const BitSet = std.StaticBitSet(Indexer.count);
419
420 /// The maximum number of items in this set.
421 pub const len = Indexer.count;
422
423 bits: BitSet = BitSet.initEmpty(),
424
425 /// Returns a set containing all possible keys.
426 pub fn initFull() Self {
427 return .{ .bits = BitSet.initFull() };
428 }
429
430 /// Returns the number of keys in the set.
431 pub fn count(self: Self) usize {
432 return self.bits.count();
433 }
434
435 /// Checks if a key is in the set.
436 pub fn contains(self: Self, key: Key) bool {
437 return self.bits.isSet(Indexer.indexOf(key));
438 }
439
440 /// Puts a key in the set.
441 pub fn insert(self: *Self, key: Key) void {
442 self.bits.set(Indexer.indexOf(key));
443 }
444
445 /// Removes a key from the set.
446 pub fn remove(self: *Self, key: Key) void {
447 self.bits.unset(Indexer.indexOf(key));
448 }
449
450 /// Changes the presence of a key in the set to match the passed bool.
451 pub fn setPresent(self: *Self, key: Key, present: bool) void {
452 self.bits.setValue(Indexer.indexOf(key), present);
453 }
454
455 /// Toggles the presence of a key in the set. If the key is in
456 /// the set, removes it. Otherwise adds it.
457 pub fn toggle(self: *Self, key: Key) void {
458 self.bits.toggle(Indexer.indexOf(key));
459 }
460
461 /// Toggles the presence of all keys in the passed set.
462 pub fn toggleSet(self: *Self, other: Self) void {
463 self.bits.toggleSet(other.bits);
464 }
465
466 /// Toggles all possible keys in the set.
467 pub fn toggleAll(self: *Self) void {
468 self.bits.toggleAll();
469 }
470
471 /// Adds all keys in the passed set to this set.
472 pub fn setUnion(self: *Self, other: Self) void {
473 self.bits.setUnion(other.bits);
474 }
475
476 /// Removes all keys which are not in the passed set.
477 pub fn setIntersection(self: *Self, other: Self) void {
478 self.bits.setIntersection(other.bits);
479 }
480
481 /// Returns an iterator over this set, which iterates in
482 /// index order. Modifications to the set during iteration
483 /// may or may not be observed by the iterator, but will
484 /// not invalidate it.
485 pub fn iterator(self: *Self) Iterator {
486 return .{ .inner = self.bits.iterator(.{}) };
487 }
488
489 pub const Iterator = struct {
490 inner: BitSet.Iterator(.{}),
491
492 pub fn next(self: *Iterator) ?Key {
493 return if (self.inner.next()) |index|
494 Indexer.keyForIndex(index)
495 else null;
496 }
497 };
498 };
499}
500
501/// A map from keys to values, using an index lookup. Uses a
502/// bitfield to track presence and a dense array of values.
503/// This type does no allocation and can be copied by value.
504pub fn IndexedMap(comptime I: type, comptime V: type, comptime Ext: fn(type)type) type {
505 comptime ensureIndexer(I);
506 return struct {
507 const Self = @This();
508
509 pub usingnamespace Ext(Self);
510
511 /// The index mapping for this map
512 pub const Indexer = I;
513 /// The key type used to index this map
514 pub const Key = Indexer.Key;
515 /// The value type stored in this map
516 pub const Value = V;
517 /// The number of possible keys in the map
518 pub const len = Indexer.count;
519
520 const BitSet = std.StaticBitSet(Indexer.count);
521
522 /// Bits determining whether items are in the map
523 bits: BitSet = BitSet.initEmpty(),
524 /// Values of items in the map. If the associated
525 /// bit is zero, the value is undefined.
526 values: [Indexer.count]Value = undefined,
527
528 /// The number of items in the map.
529 pub fn count(self: Self) usize {
530 return self.bits.count();
531 }
532
533 /// Checks if the map contains an item.
534 pub fn contains(self: Self, key: Key) bool {
535 return self.bits.isSet(Indexer.indexOf(key));
536 }
537
538 /// Gets the value associated with a key.
539 /// If the key is not in the map, returns null.
540 pub fn get(self: Self, key: Key) ?Value {
541 const index = Indexer.indexOf(key);
542 return if (self.bits.isSet(index)) self.values[index] else null;
543 }
544
545 /// Gets the value associated with a key, which must
546 /// exist in the map.
547 pub fn getAssertContains(self: Self, key: Key) Value {
548 const index = Indexer.indexOf(key);
549 assert(self.bits.isSet(index));
550 return self.values[index];
551 }
552
553 /// Gets the address of the value associated with a key.
554 /// If the key is not in the map, returns null.
555 pub fn getPtr(self: *Self, key: Key) ?*Value {
556 const index = Indexer.indexOf(key);
557 return if (self.bits.isSet(index)) &self.values[index] else null;
558 }
559
560 /// Gets the address of the const value associated with a key.
561 /// If the key is not in the map, returns null.
562 pub fn getPtrConst(self: *const Self, key: Key) ?*const Value {
563 const index = Indexer.indexOf(key);
564 return if (self.bits.isSet(index)) &self.values[index] else null;
565 }
566
567 /// Gets the address of the value associated with a key.
568 /// The key must be present in the map.
569 pub fn getPtrAssertContains(self: *Self, key: Key) *Value {
570 const index = Indexer.indexOf(key);
571 assert(self.bits.isSet(index));
572 return &self.values[index];
573 }
574
575 /// Adds the key to the map with the supplied value.
576 /// If the key is already in the map, overwrites the value.
577 pub fn put(self: *Self, key: Key, value: Value) void {
578 const index = Indexer.indexOf(key);
579 self.bits.set(index);
580 self.values[index] = value;
581 }
582
583 /// Adds the key to the map with an undefined value.
584 /// If the key is already in the map, the value becomes undefined.
585 /// A pointer to the value is returned, which should be
586 /// used to initialize the value.
587 pub fn putUninitialized(self: *Self, key: Key) *Value {
588 const index = Indexer.indexOf(key);
589 self.bits.set(index);
590 self.values[index] = undefined;
591 return &self.values[index];
592 }
593
594 /// Sets the value associated with the key in the map,
595 /// and returns the old value. If the key was not in
596 /// the map, returns null.
597 pub fn fetchPut(self: *Self, key: Key, value: Value) ?Value {
598 const index = Indexer.indexOf(key);
599 const result: ?Value = if (self.bits.isSet(index)) self.values[index] else null;
600 self.bits.set(index);
601 self.values[index] = value;
602 return result;
603 }
604
605 /// Removes a key from the map. If the key was not in the map,
606 /// does nothing.
607 pub fn remove(self: *Self, key: Key) void {
608 const index = Indexer.indexOf(key);
609 self.bits.unset(index);
610 self.values[index] = undefined;
611 }
612
613 /// Removes a key from the map, and returns the old value.
614 /// If the key was not in the map, returns null.
615 pub fn fetchRemove(self: *Self, key: Key) ?Value {
616 const index = Indexer.indexOf(key);
617 const result: ?Value = if (self.bits.isSet(index)) self.values[index] else null;
618 self.bits.unset(index);
619 self.values[index] = undefined;
620 return result;
621 }
622
623 /// Returns an iterator over the map, which visits items in index order.
624 /// Modifications to the underlying map may or may not be observed by
625 /// the iterator, but will not invalidate it.
626 pub fn iterator(self: *Self) Iterator {
627 return .{
628 .inner = self.bits.iterator(.{}),
629 .values = &self.values,
630 };
631 }
632
633 /// An entry in the map.
634 pub const Entry = struct {
635 /// The key associated with this entry.
636 /// Modifying this key will not change the map.
637 key: Key,
638
639 /// A pointer to the value in the map associated
640 /// with this key. Modifications through this
641 /// pointer will modify the underlying data.
642 value: *Value,
643 };
644
645 pub const Iterator = struct {
646 inner: BitSet.Iterator(.{}),
647 values: *[Indexer.count]Value,
648
649 pub fn next(self: *Iterator) ?Entry {
650 return if (self.inner.next()) |index|
651 Entry{
652 .key = Indexer.keyForIndex(index),
653 .value = &self.values[index],
654 }
655 else null;
656 }
657 };
658 };
659}
660
661/// A dense array of values, using an indexed lookup.
662/// This type does no allocation and can be copied by value.
663pub fn IndexedArray(comptime I: type, comptime V: type, comptime Ext: fn(type)type) type {
664 comptime ensureIndexer(I);
665 return struct {
666 const Self = @This();
667
668 pub usingnamespace Ext(Self);
669
670 /// The index mapping for this map
671 pub const Indexer = I;
672 /// The key type used to index this map
673 pub const Key = Indexer.Key;
674 /// The value type stored in this map
675 pub const Value = V;
676 /// The number of possible keys in the map
677 pub const len = Indexer.count;
678
679 values: [Indexer.count]Value,
680
681 pub fn initUndefined() Self {
682 return Self{ .values = undefined };
683 }
684
685 pub fn initFill(v: Value) Self {
686 var self: Self = undefined;
687 std.mem.set(Value, &self.values, v);
688 return self;
689 }
690
691 /// Returns the value in the array associated with a key.
692 pub fn get(self: Self, key: Key) Value {
693 return self.values[Indexer.indexOf(key)];
694 }
695
696 /// Returns a pointer to the slot in the array associated with a key.
697 pub fn getPtr(self: *Self, key: Key) *Value {
698 return &self.values[Indexer.indexOf(key)];
699 }
700
701 /// Returns a const pointer to the slot in the array associated with a key.
702 pub fn getPtrConst(self: *const Self, key: Key) *const Value {
703 return &self.values[Indexer.indexOf(key)];
704 }
705
706 /// Sets the value in the slot associated with a key.
707 pub fn set(self: *Self, key: Key, value: Value) void {
708 self.values[Indexer.indexOf(key)] = value;
709 }
710
711 /// Iterates over the items in the array, in index order.
712 pub fn iterator(self: *Self) Iterator {
713 return .{
714 .values = &self.values,
715 };
716 }
717
718 /// An entry in the array.
719 pub const Entry = struct {
720 /// The key associated with this entry.
721 /// Modifying this key will not change the array.
722 key: Key,
723
724 /// A pointer to the value in the array associated
725 /// with this key. Modifications through this
726 /// pointer will modify the underlying data.
727 value: *Value,
728 };
729
730 pub const Iterator = struct {
731 index: usize = 0,
732 values: *[Indexer.count]Value,
733
734 pub fn next(self: *Iterator) ?Entry {
735 const index = self.index;
736 if (index < Indexer.count) {
737 self.index += 1;
738 return Entry{
739 .key = Indexer.keyForIndex(index),
740 .value = &self.values[index],
741 };
742 }
743 return null;
744 }
745 };
746 };
747}
748
749/// Verifies that a type is a valid Indexer, providing a helpful
750/// compile error if not. An Indexer maps a comptime known set
751/// of keys to a dense set of zero-based indices.
752/// The indexer interface must look like this:
753/// ```
754/// struct {
755/// /// The key type which this indexer converts to indices
756/// pub const Key: type,
757/// /// The number of indexes in the dense mapping
758/// pub const count: usize,
759/// /// Converts from a key to an index
760/// pub fn indexOf(Key) usize;
761/// /// Converts from an index to a key
762/// pub fn keyForIndex(usize) Key;
763/// }
764/// ```
765pub fn ensureIndexer(comptime T: type) void {
766 comptime {
767 if (!@hasDecl(T, "Key")) @compileError("Indexer must have decl Key: type.");
768 if (@TypeOf(T.Key) != type) @compileError("Indexer.Key must be a type.");
769 if (!@hasDecl(T, "count")) @compileError("Indexer must have decl count: usize.");
770 if (@TypeOf(T.count) != usize) @compileError("Indexer.count must be a usize.");
771 if (!@hasDecl(T, "indexOf")) @compileError("Indexer.indexOf must be a fn(Key)usize.");
772 if (@TypeOf(T.indexOf) != fn(T.Key)usize) @compileError("Indexer must have decl indexOf: fn(Key)usize.");
773 if (!@hasDecl(T, "keyForIndex")) @compileError("Indexer must have decl keyForIndex: fn(usize)Key.");
774 if (@TypeOf(T.keyForIndex) != fn(usize)T.Key) @compileError("Indexer.keyForIndex must be a fn(usize)Key.");
775 }
776}
777
778test "std.enums.ensureIndexer" {
779 ensureIndexer(struct {
780 pub const Key = u32;
781 pub const count: usize = 8;
782 pub fn indexOf(k: Key) usize {
783 return @intCast(usize, k);
784 }
785 pub fn keyForIndex(index: usize) Key {
786 return @intCast(Key, index);
787 }
788 });
789}
790
791fn ascByValue(ctx: void, comptime a: EnumField, comptime b: EnumField) bool {
792 return a.value < b.value;
793}
794pub fn EnumIndexer(comptime E: type) type {
795 if (!@typeInfo(E).Enum.is_exhaustive) {
796 @compileError("Cannot create an enum indexer for a non-exhaustive enum.");
797 }
798
799 const const_fields = uniqueFields(E);
800 var fields = const_fields[0..const_fields.len].*;
801 if (fields.len == 0) {
802 return struct {
803 pub const Key = E;
804 pub const count: usize = 0;
805 pub fn indexOf(e: E) usize { unreachable; }
806 pub fn keyForIndex(i: usize) E { unreachable; }
807 };
808 }
809 std.sort.sort(EnumField, &fields, {}, ascByValue);
810 const min = fields[0].value;
811 const max = fields[fields.len-1].value;
812 if (max - min == fields.len-1) {
813 return struct {
814 pub const Key = E;
815 pub const count = fields.len;
816 pub fn indexOf(e: E) usize {
817 return @intCast(usize, @enumToInt(e) - min);
818 }
819 pub fn keyForIndex(i: usize) E {
820 // TODO fix addition semantics. This calculation
821 // gives up some safety to avoid artificially limiting
822 // the range of signed enum values to max_isize.
823 const enum_value = if (min < 0) @bitCast(isize, i) +% min else i + min;
824 return @intToEnum(E, @intCast(std.meta.Tag(E), enum_value));
825 }
826 };
827 }
828
829 const keys = valuesFromFields(E, &fields);
830
831 return struct {
832 pub const Key = E;
833 pub const count = fields.len;
834 pub fn indexOf(e: E) usize {
835 for (keys) |k, i| {
836 if (k == e) return i;
837 }
838 unreachable;
839 }
840 pub fn keyForIndex(i: usize) E {
841 return keys[i];
842 }
843 };
844}
845
846test "std.enums.EnumIndexer dense zeroed" {
847 const E = enum{ b = 1, a = 0, c = 2 };
848 const Indexer = EnumIndexer(E);
849 ensureIndexer(Indexer);
850 testing.expectEqual(E, Indexer.Key);
851 testing.expectEqual(@as(usize, 3), Indexer.count);
852
853 testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a));
854 testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b));
855 testing.expectEqual(@as(usize, 2), Indexer.indexOf(.c));
856
857 testing.expectEqual(E.a, Indexer.keyForIndex(0));
858 testing.expectEqual(E.b, Indexer.keyForIndex(1));
859 testing.expectEqual(E.c, Indexer.keyForIndex(2));
860}
861
862test "std.enums.EnumIndexer dense positive" {
863 const E = enum(u4) { c = 6, a = 4, b = 5 };
864 const Indexer = EnumIndexer(E);
865 ensureIndexer(Indexer);
866 testing.expectEqual(E, Indexer.Key);
867 testing.expectEqual(@as(usize, 3), Indexer.count);
868
869 testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a));
870 testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b));
871 testing.expectEqual(@as(usize, 2), Indexer.indexOf(.c));
872
873 testing.expectEqual(E.a, Indexer.keyForIndex(0));
874 testing.expectEqual(E.b, Indexer.keyForIndex(1));
875 testing.expectEqual(E.c, Indexer.keyForIndex(2));
876}
877
878test "std.enums.EnumIndexer dense negative" {
879 const E = enum(i4) { a = -6, c = -4, b = -5 };
880 const Indexer = EnumIndexer(E);
881 ensureIndexer(Indexer);
882 testing.expectEqual(E, Indexer.Key);
883 testing.expectEqual(@as(usize, 3), Indexer.count);
884
885 testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a));
886 testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b));
887 testing.expectEqual(@as(usize, 2), Indexer.indexOf(.c));
888
889 testing.expectEqual(E.a, Indexer.keyForIndex(0));
890 testing.expectEqual(E.b, Indexer.keyForIndex(1));
891 testing.expectEqual(E.c, Indexer.keyForIndex(2));
892}
893
894test "std.enums.EnumIndexer sparse" {
895 const E = enum(i4) { a = -2, c = 6, b = 4 };
896 const Indexer = EnumIndexer(E);
897 ensureIndexer(Indexer);
898 testing.expectEqual(E, Indexer.Key);
899 testing.expectEqual(@as(usize, 3), Indexer.count);
900
901 testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a));
902 testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b));
903 testing.expectEqual(@as(usize, 2), Indexer.indexOf(.c));
904
905 testing.expectEqual(E.a, Indexer.keyForIndex(0));
906 testing.expectEqual(E.b, Indexer.keyForIndex(1));
907 testing.expectEqual(E.c, Indexer.keyForIndex(2));
908}
909
910test "std.enums.EnumIndexer repeats" {
911 const E = extern enum{ a = -2, c = 6, b = 4, b2 = 4 };
912 const Indexer = EnumIndexer(E);
913 ensureIndexer(Indexer);
914 testing.expectEqual(E, Indexer.Key);
915 testing.expectEqual(@as(usize, 3), Indexer.count);
916
917 testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a));
918 testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b));
919 testing.expectEqual(@as(usize, 2), Indexer.indexOf(.c));
920
921 testing.expectEqual(E.a, Indexer.keyForIndex(0));
922 testing.expectEqual(E.b, Indexer.keyForIndex(1));
923 testing.expectEqual(E.c, Indexer.keyForIndex(2));
924}
925
926test "std.enums.EnumSet" {
927 const E = extern enum { a, b, c, d, e = 0 };
928 const Set = EnumSet(E);
929 testing.expectEqual(E, Set.Key);
930 testing.expectEqual(EnumIndexer(E), Set.Indexer);
931 testing.expectEqual(@as(usize, 4), Set.len);
932
933 // Empty sets
934 const empty = Set{};
935 comptime testing.expect(empty.count() == 0);
936
937 var empty_b = Set.init(.{});
938 testing.expect(empty_b.count() == 0);
939
940 const empty_c = comptime Set.init(.{});
941 comptime testing.expect(empty_c.count() == 0);
942
943 const full = Set.initFull();
944 testing.expect(full.count() == Set.len);
945
946 const full_b = comptime Set.initFull();
947 comptime testing.expect(full_b.count() == Set.len);
948
949 testing.expectEqual(false, empty.contains(.a));
950 testing.expectEqual(false, empty.contains(.b));
951 testing.expectEqual(false, empty.contains(.c));
952 testing.expectEqual(false, empty.contains(.d));
953 testing.expectEqual(false, empty.contains(.e));
954 {
955 var iter = empty_b.iterator();
956 testing.expectEqual(@as(?E, null), iter.next());
957 }
958
959 var mut = Set.init(.{
960 .a=true, .c=true,
961 });
962 testing.expectEqual(@as(usize, 2), mut.count());
963 testing.expectEqual(true, mut.contains(.a));
964 testing.expectEqual(false, mut.contains(.b));
965 testing.expectEqual(true, mut.contains(.c));
966 testing.expectEqual(false, mut.contains(.d));
967 testing.expectEqual(true, mut.contains(.e)); // aliases a
968 {
969 var it = mut.iterator();
970 testing.expectEqual(@as(?E, .a), it.next());
971 testing.expectEqual(@as(?E, .c), it.next());
972 testing.expectEqual(@as(?E, null), it.next());
973 }
974
975 mut.toggleAll();
976 testing.expectEqual(@as(usize, 2), mut.count());
977 testing.expectEqual(false, mut.contains(.a));
978 testing.expectEqual(true, mut.contains(.b));
979 testing.expectEqual(false, mut.contains(.c));
980 testing.expectEqual(true, mut.contains(.d));
981 testing.expectEqual(false, mut.contains(.e)); // aliases a
982 {
983 var it = mut.iterator();
984 testing.expectEqual(@as(?E, .b), it.next());
985 testing.expectEqual(@as(?E, .d), it.next());
986 testing.expectEqual(@as(?E, null), it.next());
987 }
988
989 mut.toggleSet(Set.init(.{ .a=true, .b=true }));
990 testing.expectEqual(@as(usize, 2), mut.count());
991 testing.expectEqual(true, mut.contains(.a));
992 testing.expectEqual(false, mut.contains(.b));
993 testing.expectEqual(false, mut.contains(.c));
994 testing.expectEqual(true, mut.contains(.d));
995 testing.expectEqual(true, mut.contains(.e)); // aliases a
996
997 mut.setUnion(Set.init(.{ .a=true, .b=true }));
998 testing.expectEqual(@as(usize, 3), mut.count());
999 testing.expectEqual(true, mut.contains(.a));
1000 testing.expectEqual(true, mut.contains(.b));
1001 testing.expectEqual(false, mut.contains(.c));
1002 testing.expectEqual(true, mut.contains(.d));
1003
1004 mut.remove(.c);
1005 mut.remove(.b);
1006 testing.expectEqual(@as(usize, 2), mut.count());
1007 testing.expectEqual(true, mut.contains(.a));
1008 testing.expectEqual(false, mut.contains(.b));
1009 testing.expectEqual(false, mut.contains(.c));
1010 testing.expectEqual(true, mut.contains(.d));
1011
1012 mut.setIntersection(Set.init(.{ .a=true, .b=true }));
1013 testing.expectEqual(@as(usize, 1), mut.count());
1014 testing.expectEqual(true, mut.contains(.a));
1015 testing.expectEqual(false, mut.contains(.b));
1016 testing.expectEqual(false, mut.contains(.c));
1017 testing.expectEqual(false, mut.contains(.d));
1018
1019 mut.insert(.a);
1020 mut.insert(.b);
1021 testing.expectEqual(@as(usize, 2), mut.count());
1022 testing.expectEqual(true, mut.contains(.a));
1023 testing.expectEqual(true, mut.contains(.b));
1024 testing.expectEqual(false, mut.contains(.c));
1025 testing.expectEqual(false, mut.contains(.d));
1026
1027 mut.setPresent(.a, false);
1028 mut.toggle(.b);
1029 mut.toggle(.c);
1030 mut.setPresent(.d, true);
1031 testing.expectEqual(@as(usize, 2), mut.count());
1032 testing.expectEqual(false, mut.contains(.a));
1033 testing.expectEqual(false, mut.contains(.b));
1034 testing.expectEqual(true, mut.contains(.c));
1035 testing.expectEqual(true, mut.contains(.d));
1036}
1037
1038test "std.enums.EnumArray void" {
1039 const E = extern enum { a, b, c, d, e = 0 };
1040 const ArrayVoid = EnumArray(E, void);
1041 testing.expectEqual(E, ArrayVoid.Key);
1042 testing.expectEqual(EnumIndexer(E), ArrayVoid.Indexer);
1043 testing.expectEqual(void, ArrayVoid.Value);
1044 testing.expectEqual(@as(usize, 4), ArrayVoid.len);
1045
1046 const undef = ArrayVoid.initUndefined();
1047 var inst = ArrayVoid.initFill({});
1048 const inst2 = ArrayVoid.init(.{ .a = {}, .b = {}, .c = {}, .d = {} });
1049 const inst3 = ArrayVoid.initDefault({}, .{});
1050
1051 _ = inst.get(.a);
1052 _ = inst.getPtr(.b);
1053 _ = inst.getPtrConst(.c);
1054 inst.set(.a, {});
1055
1056 var it = inst.iterator();
1057 testing.expectEqual(E.a, it.next().?.key);
1058 testing.expectEqual(E.b, it.next().?.key);
1059 testing.expectEqual(E.c, it.next().?.key);
1060 testing.expectEqual(E.d, it.next().?.key);
1061 testing.expect(it.next() == null);
1062}
1063
1064test "std.enums.EnumArray sized" {
1065 const E = extern enum { a, b, c, d, e = 0 };
1066 const Array = EnumArray(E, usize);
1067 testing.expectEqual(E, Array.Key);
1068 testing.expectEqual(EnumIndexer(E), Array.Indexer);
1069 testing.expectEqual(usize, Array.Value);
1070 testing.expectEqual(@as(usize, 4), Array.len);
1071
1072 const undef = Array.initUndefined();
1073 var inst = Array.initFill(5);
1074 const inst2 = Array.init(.{ .a = 1, .b = 2, .c = 3, .d = 4 });
1075 const inst3 = Array.initDefault(6, .{.b = 4, .c = 2});
1076
1077 testing.expectEqual(@as(usize, 5), inst.get(.a));
1078 testing.expectEqual(@as(usize, 5), inst.get(.b));
1079 testing.expectEqual(@as(usize, 5), inst.get(.c));
1080 testing.expectEqual(@as(usize, 5), inst.get(.d));
1081
1082 testing.expectEqual(@as(usize, 1), inst2.get(.a));
1083 testing.expectEqual(@as(usize, 2), inst2.get(.b));
1084 testing.expectEqual(@as(usize, 3), inst2.get(.c));
1085 testing.expectEqual(@as(usize, 4), inst2.get(.d));
1086
1087 testing.expectEqual(@as(usize, 6), inst3.get(.a));
1088 testing.expectEqual(@as(usize, 4), inst3.get(.b));
1089 testing.expectEqual(@as(usize, 2), inst3.get(.c));
1090 testing.expectEqual(@as(usize, 6), inst3.get(.d));
1091
1092 testing.expectEqual(&inst.values[0], inst.getPtr(.a));
1093 testing.expectEqual(&inst.values[1], inst.getPtr(.b));
1094 testing.expectEqual(&inst.values[2], inst.getPtr(.c));
1095 testing.expectEqual(&inst.values[3], inst.getPtr(.d));
1096
1097 testing.expectEqual(@as(*const usize, &inst.values[0]), inst.getPtrConst(.a));
1098 testing.expectEqual(@as(*const usize, &inst.values[1]), inst.getPtrConst(.b));
1099 testing.expectEqual(@as(*const usize, &inst.values[2]), inst.getPtrConst(.c));
1100 testing.expectEqual(@as(*const usize, &inst.values[3]), inst.getPtrConst(.d));
1101
1102 inst.set(.c, 8);
1103 testing.expectEqual(@as(usize, 5), inst.get(.a));
1104 testing.expectEqual(@as(usize, 5), inst.get(.b));
1105 testing.expectEqual(@as(usize, 8), inst.get(.c));
1106 testing.expectEqual(@as(usize, 5), inst.get(.d));
1107
1108 var it = inst.iterator();
1109 const Entry = Array.Entry;
1110 testing.expectEqual(@as(?Entry, Entry{
1111 .key = .a,
1112 .value = &inst.values[0],
1113 }), it.next());
1114 testing.expectEqual(@as(?Entry, Entry{
1115 .key = .b,
1116 .value = &inst.values[1],
1117 }), it.next());
1118 testing.expectEqual(@as(?Entry, Entry{
1119 .key = .c,
1120 .value = &inst.values[2],
1121 }), it.next());
1122 testing.expectEqual(@as(?Entry, Entry{
1123 .key = .d,
1124 .value = &inst.values[3],
1125 }), it.next());
1126 testing.expectEqual(@as(?Entry, null), it.next());
1127}
1128
1129test "std.enums.EnumMap void" {
1130 const E = extern enum { a, b, c, d, e = 0 };
1131 const Map = EnumMap(E, void);
1132 testing.expectEqual(E, Map.Key);
1133 testing.expectEqual(EnumIndexer(E), Map.Indexer);
1134 testing.expectEqual(void, Map.Value);
1135 testing.expectEqual(@as(usize, 4), Map.len);
1136
1137 const b = Map.initFull({});
1138 testing.expectEqual(@as(usize, 4), b.count());
1139
1140 const c = Map.initFullWith(.{ .a = {}, .b = {}, .c = {}, .d = {} });
1141 testing.expectEqual(@as(usize, 4), c.count());
1142
1143 const d = Map.initFullWithDefault({}, .{ .b = {} });
1144 testing.expectEqual(@as(usize, 4), d.count());
1145
1146 var a = Map.init(.{ .b = {}, .d = {} });
1147 testing.expectEqual(@as(usize, 2), a.count());
1148 testing.expectEqual(false, a.contains(.a));
1149 testing.expectEqual(true, a.contains(.b));
1150 testing.expectEqual(false, a.contains(.c));
1151 testing.expectEqual(true, a.contains(.d));
1152 testing.expect(a.get(.a) == null);
1153 testing.expect(a.get(.b) != null);
1154 testing.expect(a.get(.c) == null);
1155 testing.expect(a.get(.d) != null);
1156 testing.expect(a.getPtr(.a) == null);
1157 testing.expect(a.getPtr(.b) != null);
1158 testing.expect(a.getPtr(.c) == null);
1159 testing.expect(a.getPtr(.d) != null);
1160 testing.expect(a.getPtrConst(.a) == null);
1161 testing.expect(a.getPtrConst(.b) != null);
1162 testing.expect(a.getPtrConst(.c) == null);
1163 testing.expect(a.getPtrConst(.d) != null);
1164 _ = a.getPtrAssertContains(.b);
1165 _ = a.getAssertContains(.d);
1166
1167 a.put(.a, {});
1168 a.put(.a, {});
1169 a.putUninitialized(.c).* = {};
1170 a.putUninitialized(.c).* = {};
1171
1172 testing.expectEqual(@as(usize, 4), a.count());
1173 testing.expect(a.get(.a) != null);
1174 testing.expect(a.get(.b) != null);
1175 testing.expect(a.get(.c) != null);
1176 testing.expect(a.get(.d) != null);
1177
1178 a.remove(.a);
1179 _ = a.fetchRemove(.c);
1180
1181 var iter = a.iterator();
1182 const Entry = Map.Entry;
1183 testing.expectEqual(E.b, iter.next().?.key);
1184 testing.expectEqual(E.d, iter.next().?.key);
1185 testing.expect(iter.next() == null);
1186}
1187
1188test "std.enums.EnumMap sized" {
1189 const E = extern enum { a, b, c, d, e = 0 };
1190 const Map = EnumMap(E, usize);
1191 testing.expectEqual(E, Map.Key);
1192 testing.expectEqual(EnumIndexer(E), Map.Indexer);
1193 testing.expectEqual(usize, Map.Value);
1194 testing.expectEqual(@as(usize, 4), Map.len);
1195
1196 const b = Map.initFull(5);
1197 testing.expectEqual(@as(usize, 4), b.count());
1198 testing.expect(b.contains(.a));
1199 testing.expect(b.contains(.b));
1200 testing.expect(b.contains(.c));
1201 testing.expect(b.contains(.d));
1202 testing.expectEqual(@as(?usize, 5), b.get(.a));
1203 testing.expectEqual(@as(?usize, 5), b.get(.b));
1204 testing.expectEqual(@as(?usize, 5), b.get(.c));
1205 testing.expectEqual(@as(?usize, 5), b.get(.d));
1206
1207 const c = Map.initFullWith(.{ .a = 1, .b = 2, .c = 3, .d = 4 });
1208 testing.expectEqual(@as(usize, 4), c.count());
1209 testing.expect(c.contains(.a));
1210 testing.expect(c.contains(.b));
1211 testing.expect(c.contains(.c));
1212 testing.expect(c.contains(.d));
1213 testing.expectEqual(@as(?usize, 1), c.get(.a));
1214 testing.expectEqual(@as(?usize, 2), c.get(.b));
1215 testing.expectEqual(@as(?usize, 3), c.get(.c));
1216 testing.expectEqual(@as(?usize, 4), c.get(.d));
1217
1218 const d = Map.initFullWithDefault(6, .{ .b = 2, .c = 4 });
1219 testing.expectEqual(@as(usize, 4), d.count());
1220 testing.expect(d.contains(.a));
1221 testing.expect(d.contains(.b));
1222 testing.expect(d.contains(.c));
1223 testing.expect(d.contains(.d));
1224 testing.expectEqual(@as(?usize, 6), d.get(.a));
1225 testing.expectEqual(@as(?usize, 2), d.get(.b));
1226 testing.expectEqual(@as(?usize, 4), d.get(.c));
1227 testing.expectEqual(@as(?usize, 6), d.get(.d));
1228
1229 var a = Map.init(.{ .b = 2, .d = 4 });
1230 testing.expectEqual(@as(usize, 2), a.count());
1231 testing.expectEqual(false, a.contains(.a));
1232 testing.expectEqual(true, a.contains(.b));
1233 testing.expectEqual(false, a.contains(.c));
1234 testing.expectEqual(true, a.contains(.d));
1235
1236 testing.expectEqual(@as(?usize, null), a.get(.a));
1237 testing.expectEqual(@as(?usize, 2), a.get(.b));
1238 testing.expectEqual(@as(?usize, null), a.get(.c));
1239 testing.expectEqual(@as(?usize, 4), a.get(.d));
1240
1241 testing.expectEqual(@as(?*usize, null), a.getPtr(.a));
1242 testing.expectEqual(@as(?*usize, &a.values[1]), a.getPtr(.b));
1243 testing.expectEqual(@as(?*usize, null), a.getPtr(.c));
1244 testing.expectEqual(@as(?*usize, &a.values[3]), a.getPtr(.d));
1245
1246 testing.expectEqual(@as(?*const usize, null), a.getPtrConst(.a));
1247 testing.expectEqual(@as(?*const usize, &a.values[1]), a.getPtrConst(.b));
1248 testing.expectEqual(@as(?*const usize, null), a.getPtrConst(.c));
1249 testing.expectEqual(@as(?*const usize, &a.values[3]), a.getPtrConst(.d));
1250
1251 testing.expectEqual(@as(*const usize, &a.values[1]), a.getPtrAssertContains(.b));
1252 testing.expectEqual(@as(*const usize, &a.values[3]), a.getPtrAssertContains(.d));
1253 testing.expectEqual(@as(usize, 2), a.getAssertContains(.b));
1254 testing.expectEqual(@as(usize, 4), a.getAssertContains(.d));
1255
1256 a.put(.a, 3);
1257 a.put(.a, 5);
1258 a.putUninitialized(.c).* = 7;
1259 a.putUninitialized(.c).* = 9;
1260
1261 testing.expectEqual(@as(usize, 4), a.count());
1262 testing.expectEqual(@as(?usize, 5), a.get(.a));
1263 testing.expectEqual(@as(?usize, 2), a.get(.b));
1264 testing.expectEqual(@as(?usize, 9), a.get(.c));
1265 testing.expectEqual(@as(?usize, 4), a.get(.d));
1266
1267 a.remove(.a);
1268 testing.expectEqual(@as(?usize, null), a.fetchRemove(.a));
1269 testing.expectEqual(@as(?usize, 9), a.fetchRemove(.c));
1270 a.remove(.c);
1271
1272 var iter = a.iterator();
1273 const Entry = Map.Entry;
1274 testing.expectEqual(@as(?Entry, Entry{
1275 .key = .b, .value = &a.values[1],
1276 }), iter.next());
1277 testing.expectEqual(@as(?Entry, Entry{
1278 .key = .d, .value = &a.values[3],
1279 }), iter.next());
1280 testing.expectEqual(@as(?Entry, null), iter.next());
1281}
lib/std/fmt.zig+7-2
...@@ -1250,9 +1250,9 @@ fn formatDuration(ns: u64, comptime fmt: []const u8, options: std.fmt.FormatOpti...@@ -1250,9 +1250,9 @@ fn formatDuration(ns: u64, comptime fmt: []const u8, options: std.fmt.FormatOpti
1250 const kunits = ns_remaining * 1000 / unit.ns;1250 const kunits = ns_remaining * 1000 / unit.ns;
1251 if (kunits >= 1000) {1251 if (kunits >= 1000) {
1252 try formatInt(kunits / 1000, 10, false, .{}, writer);1252 try formatInt(kunits / 1000, 10, false, .{}, writer);
1253 if (kunits > 1000) {1253 const frac = kunits % 1000;
1254 if (frac > 0) {
1254 // Write up to 3 decimal places1255 // Write up to 3 decimal places
1255 const frac = kunits % 1000;
1256 var buf = [_]u8{ '.', 0, 0, 0 };1256 var buf = [_]u8{ '.', 0, 0, 0 };
1257 _ = formatIntBuf(buf[1..], frac, 10, false, .{ .fill = '0', .width = 3 });1257 _ = formatIntBuf(buf[1..], frac, 10, false, .{ .fill = '0', .width = 3 });
1258 var end: usize = 4;1258 var end: usize = 4;
...@@ -1286,9 +1286,14 @@ test "fmtDuration" {...@@ -1286,9 +1286,14 @@ test "fmtDuration" {
1286 .{ .s = "1us", .d = std.time.ns_per_us },1286 .{ .s = "1us", .d = std.time.ns_per_us },
1287 .{ .s = "1.45us", .d = 1450 },1287 .{ .s = "1.45us", .d = 1450 },
1288 .{ .s = "1.5us", .d = 3 * std.time.ns_per_us / 2 },1288 .{ .s = "1.5us", .d = 3 * std.time.ns_per_us / 2 },
1289 .{ .s = "14.5us", .d = 14500 },
1290 .{ .s = "145us", .d = 145000 },
1289 .{ .s = "999.999us", .d = std.time.ns_per_ms - 1 },1291 .{ .s = "999.999us", .d = std.time.ns_per_ms - 1 },
1290 .{ .s = "1ms", .d = std.time.ns_per_ms + 1 },1292 .{ .s = "1ms", .d = std.time.ns_per_ms + 1 },
1291 .{ .s = "1.5ms", .d = 3 * std.time.ns_per_ms / 2 },1293 .{ .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 },
1292 .{ .s = "999.999ms", .d = std.time.ns_per_s - 1 },1297 .{ .s = "999.999ms", .d = std.time.ns_per_s - 1 },
1293 .{ .s = "1s", .d = std.time.ns_per_s },1298 .{ .s = "1s", .d = std.time.ns_per_s },
1294 .{ .s = "59.999s", .d = std.time.ns_per_min - 1 },1299 .{ .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) {...@@ -50,13 +50,13 @@ pub const MAX_PATH_BYTES = switch (builtin.os.tag) {
50 else => @compileError("Unsupported OS"),50 else => @compileError("Unsupported OS"),
51};51};
5252
53pub const base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";53pub const base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".*;
5454
55/// Base64 encoder, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem.55/// 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
58/// Base64 decoder, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem.58/// 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
61/// Whether or not async file system syscalls need a dedicated thread because the operating61/// Whether or not async file system syscalls need a dedicated thread because the operating
62/// system does not support non-blocking I/O on the file system.62/// 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:...@@ -77,7 +77,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
77 const dirname = path.dirname(new_path) orelse ".";77 const dirname = path.dirname(new_path) orelse ".";
7878
79 var rand_buf: [AtomicFile.RANDOM_BYTES]u8 = undefined;79 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));
81 defer allocator.free(tmp_path);81 defer allocator.free(tmp_path);
82 mem.copy(u8, tmp_path[0..], dirname);82 mem.copy(u8, tmp_path[0..], dirname);
83 tmp_path[dirname.len] = path.sep;83 tmp_path[dirname.len] = path.sep;
...@@ -142,7 +142,7 @@ pub const AtomicFile = struct {...@@ -142,7 +142,7 @@ pub const AtomicFile = struct {
142 const InitError = File.OpenError;142 const InitError = File.OpenError;
143143
144 const RANDOM_BYTES = 12;144 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
147 /// Note that the `Dir.atomicFile` API may be more handy than this lower-level function.147 /// Note that the `Dir.atomicFile` API may be more handy than this lower-level function.
148 pub fn init(148 pub fn init(
lib/std/fs/path.zig+11-1
...@@ -92,7 +92,7 @@ pub fn join(allocator: *Allocator, paths: []const []const u8) ![]u8 {...@@ -92,7 +92,7 @@ pub fn join(allocator: *Allocator, paths: []const []const u8) ![]u8 {
92/// Naively combines a series of paths with the native path seperator and null terminator.92/// Naively combines a series of paths with the native path seperator and null terminator.
93/// Allocates memory for the result, which must be freed by the caller.93/// Allocates memory for the result, which must be freed by the caller.
94pub fn joinZ(allocator: *Allocator, paths: []const []const u8) ![:0]u8 {94pub fn joinZ(allocator: *Allocator, paths: []const []const u8) ![:0]u8 {
95 const out = joinSepMaybeZ(allocator, sep, isSep, paths, true);95 const out = try joinSepMaybeZ(allocator, sep, isSep, paths, true);
96 return out[0 .. out.len - 1 :0];96 return out[0 .. out.len - 1 :0];
97}97}
9898
...@@ -119,6 +119,16 @@ fn testJoinMaybeZPosix(paths: []const []const u8, expected: []const u8, zero: bo...@@ -119,6 +119,16 @@ fn testJoinMaybeZPosix(paths: []const []const u8, expected: []const u8, zero: bo
119}119}
120120
121test "join" {121test "join" {
122 {
123 const actual: []u8 = try join(testing.allocator, &[_][]const u8{});
124 defer testing.allocator.free(actual);
125 testing.expectEqualSlices(u8, "", actual);
126 }
127 {
128 const actual: [:0]u8 = try joinZ(testing.allocator, &[_][]const u8{});
129 defer testing.allocator.free(actual);
130 testing.expectEqualSlices(u8, "", actual);
131 }
122 for (&[_]bool{ false, true }) |zero| {132 for (&[_]bool{ false, true }) |zero| {
123 testJoinMaybeZWindows(&[_][]const u8{}, "", zero);133 testJoinMaybeZWindows(&[_][]const u8{}, "", zero);
124 testJoinMaybeZWindows(&[_][]const u8{ "c:\\a\\b", "c" }, "c:\\a\\b\\c", zero);134 testJoinMaybeZWindows(&[_][]const u8{ "c:\\a\\b", "c" }, "c:\\a\\b\\c", zero);
lib/std/hash/auto_hash.zig+1-1
...@@ -95,7 +95,7 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {...@@ -95,7 +95,7 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
95 .EnumLiteral,95 .EnumLiteral,
96 .Frame,96 .Frame,
97 .Float,97 .Float,
98 => @compileError("cannot hash this type"),98 => @compileError("unable to hash type " ++ @typeName(Key)),
9999
100 // Help the optimizer see that hashing an int is easy by inlining!100 // Help the optimizer see that hashing an int is easy by inlining!
101 // TODO Check if the situation is better after #561 is resolved.101 // 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;...@@ -1227,6 +1227,24 @@ pub const S_ATTR_EXT_RELOC = 0x200;
1227/// section has local relocation entries1227/// section has local relocation entries
1228pub const S_ATTR_LOC_RELOC = 0x100;1228pub const S_ATTR_LOC_RELOC = 0x100;
12291229
1230/// template of initial values for TLVs
1231pub const S_THREAD_LOCAL_REGULAR = 0x11;
1232
1233/// template of initial values for TLVs
1234pub const S_THREAD_LOCAL_ZEROFILL = 0x12;
1235
1236/// TLV descriptors
1237pub const S_THREAD_LOCAL_VARIABLES = 0x13;
1238
1239/// pointers to TLV descriptors
1240pub const S_THREAD_LOCAL_VARIABLE_POINTERS = 0x14;
1241
1242/// functions to call to initialize TLV values
1243pub const S_THREAD_LOCAL_INIT_FUNCTION_POINTERS = 0x15;
1244
1245/// 32-bit offsets to initializers
1246pub const S_INIT_FUNC_OFFSETS = 0x16;
1247
1230pub const cpu_type_t = integer_t;1248pub const cpu_type_t = integer_t;
1231pub const cpu_subtype_t = integer_t;1249pub const cpu_subtype_t = integer_t;
1232pub const integer_t = c_int;1250pub const integer_t = c_int;
...@@ -1422,6 +1440,14 @@ pub const EXPORT_SYMBOL_FLAGS_KIND_WEAK_DEFINITION: u8 = 0x04;...@@ -1422,6 +1440,14 @@ pub const EXPORT_SYMBOL_FLAGS_KIND_WEAK_DEFINITION: u8 = 0x04;
1422pub const EXPORT_SYMBOL_FLAGS_REEXPORT: u8 = 0x08;1440pub const EXPORT_SYMBOL_FLAGS_REEXPORT: u8 = 0x08;
1423pub const EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER: u8 = 0x10;1441pub const EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER: u8 = 0x10;
14241442
1443// An indirect symbol table entry is simply a 32bit index into the symbol table
1444// to the symbol that the pointer or stub is refering to. Unless it is for a
1445// non-lazy symbol pointer section for a defined symbol which strip(1) as
1446// removed. In which case it has the value INDIRECT_SYMBOL_LOCAL. If the
1447// symbol was also absolute INDIRECT_SYMBOL_ABS is or'ed with that.
1448pub const INDIRECT_SYMBOL_LOCAL: u32 = 0x80000000;
1449pub const INDIRECT_SYMBOL_ABS: u32 = 0x40000000;
1450
1425// Codesign consts and structs taken from:1451// Codesign consts and structs taken from:
1426// https://opensource.apple.com/source/xnu/xnu-6153.81.5/osfmk/kern/cs_blobs.h.auto.html1452// https://opensource.apple.com/source/xnu/xnu-6153.81.5/osfmk/kern/cs_blobs.h.auto.html
14271453
...@@ -1589,3 +1615,17 @@ pub const GenericBlob = extern struct {...@@ -1589,3 +1615,17 @@ pub const GenericBlob = extern struct {
1589 /// Total length of blob1615 /// Total length of blob
1590 length: u32,1616 length: u32,
1591};1617};
1618
1619/// The LC_DATA_IN_CODE load commands uses a linkedit_data_command
1620/// to point to an array of data_in_code_entry entries. Each entry
1621/// describes a range of data in a code section.
1622pub const data_in_code_entry = extern struct {
1623 /// From mach_header to start of data range.
1624 offset: u32,
1625
1626 /// Number of bytes in data range.
1627 length: u16,
1628
1629 /// A DICE_KIND value.
1630 kind: u16,
1631};
lib/std/mem.zig+19
...@@ -1373,6 +1373,20 @@ test "mem.tokenize (multibyte)" {...@@ -1373,6 +1373,20 @@ test "mem.tokenize (multibyte)" {
1373 testing.expect(it.next() == null);1373 testing.expect(it.next() == null);
1374}1374}
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
1376/// Returns an iterator that iterates over the slices of `buffer` that1390/// Returns an iterator that iterates over the slices of `buffer` that
1377/// are separated by bytes in `delimiter`.1391/// are separated by bytes in `delimiter`.
1378/// split("abc|def||ghi", "|")1392/// split("abc|def||ghi", "|")
...@@ -1471,6 +1485,11 @@ pub const TokenIterator = struct {...@@ -1471,6 +1485,11 @@ pub const TokenIterator = struct {
1471 return self.buffer[index..];1485 return self.buffer[index..];
1472 }1486 }
14731487
1488 /// Resets the iterator to the initial token.
1489 pub fn reset(self: *TokenIterator) void {
1490 self.index = 0;
1491 }
1492
1474 fn isSplitByte(self: TokenIterator, byte: u8) bool {1493 fn isSplitByte(self: TokenIterator, byte: u8) bool {
1475 for (self.delimiter_bytes) |delimiter_byte| {1494 for (self.delimiter_bytes) |delimiter_byte| {
1476 if (byte == delimiter_byte) {1495 if (byte == delimiter_byte) {
lib/std/meta.zig+51-18
...@@ -888,19 +888,20 @@ pub fn Vector(comptime len: u32, comptime child: type) type {...@@ -888,19 +888,20 @@ pub fn Vector(comptime len: u32, comptime child: type) type {
888/// Given a type and value, cast the value to the type as c would.888/// Given a type and value, cast the value to the type as c would.
889/// This is for translate-c and is not intended for general use.889/// This is for translate-c and is not intended for general use.
890pub fn cast(comptime DestType: type, target: anytype) DestType {890pub fn cast(comptime DestType: type, target: anytype) DestType {
891 const TargetType = @TypeOf(target);891 // this function should behave like transCCast in translate-c, except it's for macros
892 const SourceType = @TypeOf(target);
892 switch (@typeInfo(DestType)) {893 switch (@typeInfo(DestType)) {
893 .Pointer => |dest_ptr| {894 .Pointer => {
894 switch (@typeInfo(TargetType)) {895 switch (@typeInfo(SourceType)) {
895 .Int, .ComptimeInt => {896 .Int, .ComptimeInt => {
896 return @intToPtr(DestType, target);897 return @intToPtr(DestType, target);
897 },898 },
898 .Pointer => |ptr| {899 .Pointer => {
899 return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target));900 return castPtr(DestType, target);
900 },901 },
901 .Optional => |opt| {902 .Optional => |opt| {
902 if (@typeInfo(opt.child) == .Pointer) {903 if (@typeInfo(opt.child) == .Pointer) {
903 return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target));904 return castPtr(DestType, target);
904 }905 }
905 },906 },
906 else => {},907 else => {},
...@@ -908,17 +909,16 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {...@@ -908,17 +909,16 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {
908 },909 },
909 .Optional => |dest_opt| {910 .Optional => |dest_opt| {
910 if (@typeInfo(dest_opt.child) == .Pointer) {911 if (@typeInfo(dest_opt.child) == .Pointer) {
911 const dest_ptr = @typeInfo(dest_opt.child).Pointer;912 switch (@typeInfo(SourceType)) {
912 switch (@typeInfo(TargetType)) {
913 .Int, .ComptimeInt => {913 .Int, .ComptimeInt => {
914 return @intToPtr(DestType, target);914 return @intToPtr(DestType, target);
915 },915 },
916 .Pointer => {916 .Pointer => {
917 return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target));917 return castPtr(DestType, target);
918 },918 },
919 .Optional => |target_opt| {919 .Optional => |target_opt| {
920 if (@typeInfo(target_opt.child) == .Pointer) {920 if (@typeInfo(target_opt.child) == .Pointer) {
921 return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target));921 return castPtr(DestType, target);
922 }922 }
923 },923 },
924 else => {},924 else => {},
...@@ -926,25 +926,25 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {...@@ -926,25 +926,25 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {
926 }926 }
927 },927 },
928 .Enum => {928 .Enum => {
929 if (@typeInfo(TargetType) == .Int or @typeInfo(TargetType) == .ComptimeInt) {929 if (@typeInfo(SourceType) == .Int or @typeInfo(SourceType) == .ComptimeInt) {
930 return @intToEnum(DestType, target);930 return @intToEnum(DestType, target);
931 }931 }
932 },932 },
933 .Int, .ComptimeInt => {933 .Int => {
934 switch (@typeInfo(TargetType)) {934 switch (@typeInfo(SourceType)) {
935 .Pointer => {935 .Pointer => {
936 return @intCast(DestType, @ptrToInt(target));936 return castInt(DestType, @ptrToInt(target));
937 },937 },
938 .Optional => |opt| {938 .Optional => |opt| {
939 if (@typeInfo(opt.child) == .Pointer) {939 if (@typeInfo(opt.child) == .Pointer) {
940 return @intCast(DestType, @ptrToInt(target));940 return castInt(DestType, @ptrToInt(target));
941 }941 }
942 },942 },
943 .Enum => {943 .Enum => {
944 return @intCast(DestType, @enumToInt(target));944 return castInt(DestType, @enumToInt(target));
945 },945 },
946 .Int, .ComptimeInt => {946 .Int => {
947 return @intCast(DestType, target);947 return castInt(DestType, target);
948 },948 },
949 else => {},949 else => {},
950 }950 }
...@@ -954,6 +954,34 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {...@@ -954,6 +954,34 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {
954 return @as(DestType, target);954 return @as(DestType, target);
955}955}
956956
957fn castInt(comptime DestType: type, target: anytype) DestType {
958 const dest = @typeInfo(DestType).Int;
959 const source = @typeInfo(@TypeOf(target)).Int;
960
961 if (dest.bits < source.bits)
962 return @bitCast(DestType, @truncate(Int(source.signedness, dest.bits), target))
963 else
964 return @bitCast(DestType, @as(Int(source.signedness, dest.bits), target));
965}
966
967fn castPtr(comptime DestType: type, target: anytype) DestType {
968 const dest = ptrInfo(DestType);
969 const source = ptrInfo(@TypeOf(target));
970
971 if (source.is_const and !dest.is_const or source.is_volatile and !dest.is_volatile)
972 return @intToPtr(DestType, @ptrToInt(target))
973 else
974 return @ptrCast(DestType, @alignCast(dest.alignment, target));
975}
976
977fn ptrInfo(comptime PtrType: type) TypeInfo.Pointer {
978 return switch(@typeInfo(PtrType)){
979 .Optional => |opt_info| @typeInfo(opt_info.child).Pointer,
980 .Pointer => |ptr_info| ptr_info,
981 else => unreachable,
982 };
983}
984
957test "std.meta.cast" {985test "std.meta.cast" {
958 const E = enum(u2) {986 const E = enum(u2) {
959 Zero,987 Zero,
...@@ -977,6 +1005,11 @@ test "std.meta.cast" {...@@ -977,6 +1005,11 @@ test "std.meta.cast" {
977 testing.expectEqual(@as(u32, 4), cast(u32, @intToPtr(?*u32, 4)));1005 testing.expectEqual(@as(u32, 4), cast(u32, @intToPtr(?*u32, 4)));
978 testing.expectEqual(@as(u32, 10), cast(u32, @as(u64, 10)));1006 testing.expectEqual(@as(u32, 10), cast(u32, @as(u64, 10)));
979 testing.expectEqual(@as(u8, 2), cast(u8, E.Two));1007 testing.expectEqual(@as(u8, 2), cast(u8, E.Two));
1008
1009 testing.expectEqual(@bitCast(i32, @as(u32, 0x8000_0000)), cast(i32, @as(u32, 0x8000_0000)));
1010
1011 testing.expectEqual(@intToPtr(*u8, 2), cast(*u8, @intToPtr(*const u8, 2)));
1012 testing.expectEqual(@intToPtr(*u8, 2), cast(*u8, @intToPtr(*volatile u8, 2)));
980}1013}
9811014
982/// Given a value returns its size as C's sizeof operator would.1015/// Given a value returns its size as C's sizeof operator would.
lib/std/meta/trait.zig+78
...@@ -408,6 +408,84 @@ test "std.meta.trait.isTuple" {...@@ -408,6 +408,84 @@ test "std.meta.trait.isTuple" {
408 testing.expect(isTuple(@TypeOf(t3)));408 testing.expect(isTuple(@TypeOf(t3)));
409}409}
410410
411/// Returns true if the passed type will coerce to []const u8.
412/// Any of the following are considered strings:
413/// ```
414/// []const u8, [:S]const u8, *const [N]u8, *const [N:S]u8,
415/// []u8, [:S]u8, *[:S]u8, *[N:S]u8.
416/// ```
417/// These types are not considered strings:
418/// ```
419/// u8, [N]u8, [*]const u8, [*:0]const u8,
420/// [*]const [N]u8, []const u16, []const i8,
421/// *const u8, ?[]const u8, ?*const [N]u8.
422/// ```
423pub fn isZigString(comptime T: type) bool {
424 comptime {
425 // Only pointer types can be strings, no optionals
426 const info = @typeInfo(T);
427 if (info != .Pointer) return false;
428
429 const ptr = &info.Pointer;
430 // Check for CV qualifiers that would prevent coerction to []const u8
431 if (ptr.is_volatile or ptr.is_allowzero) return false;
432
433 // If it's already a slice, simple check.
434 if (ptr.size == .Slice) {
435 return ptr.child == u8;
436 }
437
438 // Otherwise check if it's an array type that coerces to slice.
439 if (ptr.size == .One) {
440 const child = @typeInfo(ptr.child);
441 if (child == .Array) {
442 const arr = &child.Array;
443 return arr.child == u8;
444 }
445 }
446
447 return false;
448 }
449}
450
451test "std.meta.trait.isZigString" {
452 testing.expect(isZigString([]const u8));
453 testing.expect(isZigString([]u8));
454 testing.expect(isZigString([:0]const u8));
455 testing.expect(isZigString([:0]u8));
456 testing.expect(isZigString([:5]const u8));
457 testing.expect(isZigString([:5]u8));
458 testing.expect(isZigString(*const [0]u8));
459 testing.expect(isZigString(*[0]u8));
460 testing.expect(isZigString(*const [0:0]u8));
461 testing.expect(isZigString(*[0:0]u8));
462 testing.expect(isZigString(*const [0:5]u8));
463 testing.expect(isZigString(*[0:5]u8));
464 testing.expect(isZigString(*const [10]u8));
465 testing.expect(isZigString(*[10]u8));
466 testing.expect(isZigString(*const [10:0]u8));
467 testing.expect(isZigString(*[10:0]u8));
468 testing.expect(isZigString(*const [10:5]u8));
469 testing.expect(isZigString(*[10:5]u8));
470
471 testing.expect(!isZigString(u8));
472 testing.expect(!isZigString([4]u8));
473 testing.expect(!isZigString([4:0]u8));
474 testing.expect(!isZigString([*]const u8));
475 testing.expect(!isZigString([*]const [4]u8));
476 testing.expect(!isZigString([*c]const u8));
477 testing.expect(!isZigString([*c]const [4]u8));
478 testing.expect(!isZigString([*:0]const u8));
479 testing.expect(!isZigString([*:0]const u8));
480 testing.expect(!isZigString(*[]const u8));
481 testing.expect(!isZigString(?[]const u8));
482 testing.expect(!isZigString(?*const [4]u8));
483 testing.expect(!isZigString([]allowzero u8));
484 testing.expect(!isZigString([]volatile u8));
485 testing.expect(!isZigString(*allowzero [4]u8));
486 testing.expect(!isZigString(*volatile [4]u8));
487}
488
411pub fn hasDecls(comptime T: type, comptime names: anytype) bool {489pub fn hasDecls(comptime T: type, comptime names: anytype) bool {
412 inline for (names) |name| {490 inline for (names) |name| {
413 if (!@hasDecl(T, name))491 if (!@hasDecl(T, name))
lib/std/os.zig+3-2
...@@ -2879,7 +2879,7 @@ pub fn bind(sock: socket_t, addr: *const sockaddr, len: socklen_t) BindError!voi...@@ -2879,7 +2879,7 @@ pub fn bind(sock: socket_t, addr: *const sockaddr, len: socklen_t) BindError!voi
2879 unreachable;2879 unreachable;
2880}2880}
28812881
2882const ListenError = error{2882pub const ListenError = error{
2883 /// Another socket is already listening on the same port.2883 /// Another socket is already listening on the same port.
2884 /// For Internet domain sockets, the socket referred to by sockfd had not previously2884 /// For Internet domain sockets, the socket referred to by sockfd had not previously
2885 /// been bound to an address and, upon attempting to bind it to an ephemeral port, it2885 /// been bound to an address and, upon attempting to bind it to an ephemeral port, it
...@@ -5610,6 +5610,7 @@ pub fn recvfrom(...@@ -5610,6 +5610,7 @@ pub fn recvfrom(
5610 EAGAIN => return error.WouldBlock,5610 EAGAIN => return error.WouldBlock,
5611 ENOMEM => return error.SystemResources,5611 ENOMEM => return error.SystemResources,
5612 ECONNREFUSED => return error.ConnectionRefused,5612 ECONNREFUSED => return error.ConnectionRefused,
5613 ECONNRESET => return error.ConnectionResetByPeer,
5613 else => |err| return unexpectedErrno(err),5614 else => |err| return unexpectedErrno(err),
5614 }5615 }
5615 }5616 }
...@@ -5827,7 +5828,7 @@ pub fn tcsetattr(handle: fd_t, optional_action: TCSA, termios_p: termios) Termio...@@ -5827,7 +5828,7 @@ pub fn tcsetattr(handle: fd_t, optional_action: TCSA, termios_p: termios) Termio
5827 }5828 }
5828}5829}
58295830
5830const IoCtl_SIOCGIFINDEX_Error = error{5831pub const IoCtl_SIOCGIFINDEX_Error = error{
5831 FileSystem,5832 FileSystem,
5832 InterfaceNotFound,5833 InterfaceNotFound,
5833} || UnexpectedError;5834} || UnexpectedError;
lib/std/os/linux/io_uring.zig+1-1
...@@ -1353,7 +1353,7 @@ test "timeout (after a relative time)" {...@@ -1353,7 +1353,7 @@ test "timeout (after a relative time)" {
1353 .res = -linux.ETIME,1353 .res = -linux.ETIME,
1354 .flags = 0,1354 .flags = 0,
1355 }, cqe);1355 }, cqe);
1356 testing.expectWithinMargin(@intToFloat(f64, ms), @intToFloat(f64, stopped - started), margin);1356 testing.expectApproxEqAbs(@intToFloat(f64, ms), @intToFloat(f64, stopped - started), margin);
1357}1357}
13581358
1359test "timeout (after a number of completions)" {1359test "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,...@@ -115,6 +115,9 @@ pub fn syscall5(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize,
115 );115 );
116}116}
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
118pub fn syscall6(121pub fn syscall6(
119 number: SYS,122 number: SYS,
120 arg1: usize,123 arg1: usize,
...@@ -146,6 +149,40 @@ pub fn syscall6(...@@ -146,6 +149,40 @@ pub fn syscall6(
146 );149 );
147}150}
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
149/// This matches the libc clone function.186/// This matches the libc clone function.
150pub extern fn clone(func: fn (arg: usize) callconv(.C) u8, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize;187pub 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 {...@@ -78,7 +78,8 @@ pub const BootServices = extern struct {
78 /// Returns an array of handles that support a specified protocol.78 /// Returns an array of handles that support a specified protocol.
79 locateHandle: fn (LocateSearchType, ?*align(8) const Guid, ?*const c_void, *usize, [*]Handle) callconv(.C) Status,79 locateHandle: fn (LocateSearchType, ?*align(8) const Guid, ?*const c_void, *usize, [*]Handle) callconv(.C) Status,
8080
81 locateDevicePath: Status, // TODO81 /// 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,
82 installConfigurationTable: Status, // TODO83 installConfigurationTable: Status, // TODO
8384
84 /// Loads an EFI image into memory.85 /// 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:...@@ -373,7 +373,7 @@ pub fn createWindowExA(dwExStyle: u32, lpClassName: [*:0]const u8, lpWindowName:
373}373}
374374
375pub 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;375pub 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;
377pub 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 {377pub 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 {
378 const function = selectSymbol(CreateWindowExW, pfnCreateWindowExW, .win2k);378 const function = selectSymbol(CreateWindowExW, pfnCreateWindowExW, .win2k);
379 const window = function(dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWindParent, hMenu, hInstance, lpParam);379 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 {...@@ -60,6 +60,7 @@ pub fn main() !void {
60 const stderr_stream = io.getStdErr().writer();60 const stderr_stream = io.getStdErr().writer();
61 const stdout_stream = io.getStdOut().writer();61 const stdout_stream = io.getStdOut().writer();
6262
63 var install_prefix: ?[]const u8 = null;
63 while (nextArg(args, &arg_idx)) |arg| {64 while (nextArg(args, &arg_idx)) |arg| {
64 if (mem.startsWith(u8, arg, "-D")) {65 if (mem.startsWith(u8, arg, "-D")) {
65 const option_contents = arg[2..];66 const option_contents = arg[2..];
...@@ -82,7 +83,7 @@ pub fn main() !void {...@@ -82,7 +83,7 @@ pub fn main() !void {
82 } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {83 } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
83 return usage(builder, false, stdout_stream);84 return usage(builder, false, stdout_stream);
84 } else if (mem.eql(u8, arg, "--prefix")) {85 } else if (mem.eql(u8, arg, "--prefix")) {
85 builder.install_prefix = nextArg(args, &arg_idx) orelse {86 install_prefix = nextArg(args, &arg_idx) orelse {
86 warn("Expected argument after --prefix\n\n", .{});87 warn("Expected argument after --prefix\n\n", .{});
87 return usageAndErr(builder, false, stderr_stream);88 return usageAndErr(builder, false, stderr_stream);
88 };89 };
...@@ -134,7 +135,7 @@ pub fn main() !void {...@@ -134,7 +135,7 @@ pub fn main() !void {
134 }135 }
135 }136 }
136137
137 builder.resolveInstallPrefix();138 builder.resolveInstallPrefix(install_prefix);
138 try runBuild(builder);139 try runBuild(builder);
139140
140 if (builder.validateUserInputDidItFail())141 if (builder.validateUserInputDidItFail())
...@@ -162,8 +163,7 @@ fn runBuild(builder: *Builder) anyerror!void {...@@ -162,8 +163,7 @@ fn runBuild(builder: *Builder) anyerror!void {
162fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void {163fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void {
163 // run the build script to collect the options164 // run the build script to collect the options
164 if (!already_ran_build) {165 if (!already_ran_build) {
165 builder.setInstallPrefix(null);166 builder.resolveInstallPrefix(null);
166 builder.resolveInstallPrefix();
167 try runBuild(builder);167 try runBuild(builder);
168 }168 }
169169
lib/std/std.zig+4
...@@ -20,6 +20,9 @@ pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringM...@@ -20,6 +20,9 @@ pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringM
20pub const DynLib = @import("dynamic_library.zig").DynLib;20pub const DynLib = @import("dynamic_library.zig").DynLib;
21pub const DynamicBitSet = bit_set.DynamicBitSet;21pub const DynamicBitSet = bit_set.DynamicBitSet;
22pub const DynamicBitSetUnmanaged = bit_set.DynamicBitSetUnmanaged;22pub const DynamicBitSetUnmanaged = bit_set.DynamicBitSetUnmanaged;
23pub const EnumArray = enums.EnumArray;
24pub const EnumMap = enums.EnumMap;
25pub const EnumSet = enums.EnumSet;
23pub const HashMap = hash_map.HashMap;26pub const HashMap = hash_map.HashMap;
24pub const HashMapUnmanaged = hash_map.HashMapUnmanaged;27pub const HashMapUnmanaged = hash_map.HashMapUnmanaged;
25pub const MultiArrayList = @import("multi_array_list.zig").MultiArrayList;28pub const MultiArrayList = @import("multi_array_list.zig").MultiArrayList;
...@@ -54,6 +57,7 @@ pub const cstr = @import("cstr.zig");...@@ -54,6 +57,7 @@ pub const cstr = @import("cstr.zig");
54pub const debug = @import("debug.zig");57pub const debug = @import("debug.zig");
55pub const dwarf = @import("dwarf.zig");58pub const dwarf = @import("dwarf.zig");
56pub const elf = @import("elf.zig");59pub const elf = @import("elf.zig");
60pub const enums = @import("enums.zig");
57pub const event = @import("event.zig");61pub const event = @import("event.zig");
58pub const fifo = @import("fifo.zig");62pub const fifo = @import("fifo.zig");
59pub const fmt = @import("fmt.zig");63pub const fmt = @import("fmt.zig");
lib/std/testing.zig+39-37
...@@ -200,67 +200,69 @@ pub fn expectFmt(expected: []const u8, comptime template: []const u8, args: anyt...@@ -200,67 +200,69 @@ pub fn expectFmt(expected: []const u8, comptime template: []const u8, args: anyt
200 return error.TestFailed;200 return error.TestFailed;
201}201}
202202
203/// This function is intended to be used only in tests. When the actual value is not203pub const expectWithinMargin = @compileError("expectWithinMargin is deprecated, use expectApproxEqAbs or expectApproxEqRel");
204/// within the margin of the expected value,204pub const expectWithinEpsilon = @compileError("expectWithinEpsilon is deprecated, use expectApproxEqAbs or expectApproxEqRel");
205/// prints diagnostics to stderr to show exactly how they are not equal, then aborts.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.
206/// The types must be floating point210/// The types must be floating point
207pub fn expectWithinMargin(expected: anytype, actual: @TypeOf(expected), margin: @TypeOf(expected)) void {211pub fn expectApproxEqAbs(expected: anytype, actual: @TypeOf(expected), tolerance: @TypeOf(expected)) void {
208 std.debug.assert(margin >= 0.0);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 },
218 else => @compileError("Unable to compare non floating point values"),220 else => @compileError("Unable to compare non floating point values"),
219 }221 }
220}222}
221223
222test "expectWithinMargin" {224test "expectApproxEqAbs" {
223 inline for ([_]type{ f16, f32, f64, f128 }) |T| {225 inline for ([_]type{ f16, f32, f64, f128 }) |T| {
224 const pos_x: T = 12.0;226 const pos_x: T = 12.0;
225 const pos_y: T = 12.06;227 const pos_y: T = 12.06;
226 const neg_x: T = -12.0;228 const neg_x: T = -12.0;
227 const neg_y: T = -12.06;229 const neg_y: T = -12.06;
228230
229 expectWithinMargin(pos_x, pos_y, 0.1);231 expectApproxEqAbs(pos_x, pos_y, 0.1);
230 expectWithinMargin(neg_x, neg_y, 0.1);232 expectApproxEqAbs(neg_x, neg_y, 0.1);
231 }233 }
232}234}
233235
234/// This function is intended to be used only in tests. When the actual value is not236/// This function is intended to be used only in tests. When the actual value is
235/// within the epsilon of the expected value,237/// not approximately equal to the expected value, prints diagnostics to stderr
236/// prints diagnostics to stderr to show exactly how they are not equal, then aborts.238/// to show exactly how they are not equal, then aborts.
239/// See `math.approxEqRel` for more informations on the tolerance parameter.
237/// The types must be floating point240/// The types must be floating point
238pub fn expectWithinEpsilon(expected: anytype, actual: @TypeOf(expected), epsilon: @TypeOf(expected)) void {241pub fn expectApproxEqRel(expected: anytype, actual: @TypeOf(expected), tolerance: @TypeOf(expected)) void {
239 std.debug.assert(epsilon >= 0.0 and epsilon <= 1.0);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 },
251 else => @compileError("Unable to compare non floating point values"),250 else => @compileError("Unable to compare non floating point values"),
252 }251 }
253}252}
254253
255test "expectWithinEpsilon" {254test "expectApproxEqRel" {
256 inline for ([_]type{ f16, f32, f64, f128 }) |T| {255 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
257 const pos_x: T = 12.0;259 const pos_x: T = 12.0;
258 const pos_y: T = 13.2;260 const pos_y: T = pos_x + 2 * eps_value;
259 const neg_x: T = -12.0;261 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);264 expectApproxEqRel(pos_x, pos_y, sqrt_eps_value);
263 expectWithinEpsilon(neg_x, neg_y, 0.1);265 expectApproxEqRel(neg_x, neg_y, sqrt_eps_value);
264 }266 }
265}267}
266268
...@@ -296,7 +298,7 @@ pub const TmpDir = struct {...@@ -296,7 +298,7 @@ pub const TmpDir = struct {
296 sub_path: [sub_path_len]u8,298 sub_path: [sub_path_len]u8,
297299
298 const random_bytes_count = 12;300 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
301 pub fn cleanup(self: *TmpDir) void {303 pub fn cleanup(self: *TmpDir) void {
302 self.dir.close();304 self.dir.close();
lib/std/zig/parser_test.zig+319
...@@ -4,6 +4,31 @@...@@ -4,6 +4,31 @@
4// The MIT license requires this copyright notice to be included in all copies4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.5// and substantial portions of the software.
66
7test "zig fmt: respect line breaks in struct field value declaration" {
8 try testCanonical(
9 \\const Foo = struct {
10 \\ bar: u32 =
11 \\ 42,
12 \\ bar: u32 =
13 \\ // a comment
14 \\ 42,
15 \\ bar: u32 =
16 \\ 42,
17 \\ // a comment
18 \\ bar: []const u8 =
19 \\ \\ foo
20 \\ \\ bar
21 \\ \\ baz
22 \\ ,
23 \\ bar: u32 =
24 \\ blk: {
25 \\ break :blk 42;
26 \\ },
27 \\};
28 \\
29 );
30}
31
7// TODO Remove this after zig 0.9.0 is released.32// TODO Remove this after zig 0.9.0 is released.
8test "zig fmt: rewrite inline functions as callconv(.Inline)" {33test "zig fmt: rewrite inline functions as callconv(.Inline)" {
9 try testTransform(34 try testTransform(
...@@ -3038,6 +3063,54 @@ test "zig fmt: switch" {...@@ -3038,6 +3063,54 @@ test "zig fmt: switch" {
3038 \\}3063 \\}
3039 \\3064 \\
3040 );3065 );
3066
3067 try testTransform(
3068 \\test {
3069 \\ switch (x) {
3070 \\ foo =>
3071 \\ "bar",
3072 \\ }
3073 \\}
3074 \\
3075 ,
3076 \\test {
3077 \\ switch (x) {
3078 \\ foo => "bar",
3079 \\ }
3080 \\}
3081 \\
3082 );
3083}
3084
3085test "zig fmt: switch multiline string" {
3086 try testCanonical(
3087 \\test "switch multiline string" {
3088 \\ const x: u32 = 0;
3089 \\ const str = switch (x) {
3090 \\ 1 => "one",
3091 \\ 2 =>
3092 \\ \\ Comma after the multiline string
3093 \\ \\ is needed
3094 \\ ,
3095 \\ 3 => "three",
3096 \\ else => "else",
3097 \\ };
3098 \\
3099 \\ const Union = union(enum) {
3100 \\ Int: i64,
3101 \\ Float: f64,
3102 \\ };
3103 \\
3104 \\ const str = switch (u) {
3105 \\ Union.Int => |int|
3106 \\ \\ Comma after the multiline string
3107 \\ \\ is needed
3108 \\ ,
3109 \\ Union.Float => |*float| unreachable,
3110 \\ };
3111 \\}
3112 \\
3113 );
3041}3114}
30423115
3043test "zig fmt: while" {3116test "zig fmt: while" {
...@@ -3068,6 +3141,11 @@ test "zig fmt: while" {...@@ -3068,6 +3141,11 @@ test "zig fmt: while" {
3068 \\ while (i < 10) : ({3141 \\ while (i < 10) : ({
3069 \\ i += 1;3142 \\ i += 1;
3070 \\ j += 1;3143 \\ j += 1;
3144 \\ }) continue;
3145 \\
3146 \\ while (i < 10) : ({
3147 \\ i += 1;
3148 \\ j += 1;
3071 \\ }) {3149 \\ }) {
3072 \\ continue;3150 \\ continue;
3073 \\ }3151 \\ }
...@@ -3184,6 +3262,156 @@ test "zig fmt: for" {...@@ -3184,6 +3262,156 @@ test "zig fmt: for" {
3184 );3262 );
3185}3263}
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
3187test "zig fmt: if" {3415test "zig fmt: if" {
3188 try testCanonical(3416 try testCanonical(
3189 \\test "if" {3417 \\test "if" {
...@@ -3233,6 +3461,82 @@ test "zig fmt: if" {...@@ -3233,6 +3461,82 @@ test "zig fmt: if" {
3233 );3461 );
3234}3462}
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
3236test "zig fmt: defer" {3540test "zig fmt: defer" {
3237 try testCanonical(3541 try testCanonical(
3238 \\test "defer" {3542 \\test "defer" {
...@@ -3820,6 +4124,7 @@ test "zig fmt: comments in ternary ifs" {...@@ -3820,6 +4124,7 @@ test "zig fmt: comments in ternary ifs" {
3820 \\ // Comment4124 \\ // Comment
3821 \\ 14125 \\ 1
3822 \\else4126 \\else
4127 \\ // Comment
3823 \\ 0;4128 \\ 0;
3824 \\4129 \\
3825 \\pub extern "c" fn printf(format: [*:0]const u8, ...) c_int;4130 \\pub extern "c" fn printf(format: [*:0]const u8, ...) c_int;
...@@ -3827,6 +4132,20 @@ test "zig fmt: comments in ternary ifs" {...@@ -3827,6 +4132,20 @@ test "zig fmt: comments in ternary ifs" {
3827 );4132 );
3828}4133}
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
3830test "zig fmt: test comments in field access chain" {4149test "zig fmt: test comments in field access chain" {
3831 try testCanonical(4150 try testCanonical(
3832 \\pub const str = struct {4151 \\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....@@ -1018,147 +1018,14 @@ fn renderWhile(gpa: *Allocator, ais: *Ais, tree: ast.Tree, while_node: ast.full.
1018 try renderToken(ais, tree, inline_token, .space); // inline1018 try renderToken(ais, tree, inline_token, .space); // inline
1019 }1019 }
10201020
1021 try renderToken(ais, tree, while_node.ast.while_token, .space); // if1021 try renderToken(ais, tree, while_node.ast.while_token, .space); // if/for/while
1022 try renderToken(ais, tree, while_node.ast.while_token + 1, .none); // lparen1022 try renderToken(ais, tree, while_node.ast.while_token + 1, .none); // lparen
1023 try renderExpression(gpa, ais, tree, while_node.ast.cond_expr, .none); // condition1023 try renderExpression(gpa, ais, tree, while_node.ast.cond_expr, .none); // condition
10241024
1025 const then_tag = node_tags[while_node.ast.then_expr];1025 var last_prefix_token = tree.lastToken(while_node.ast.cond_expr) + 1; // rparen
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.
11581026
1159 if (while_node.payload_token) |payload_token| {1027 if (while_node.payload_token) |payload_token| {
1160 assert(payload_token - 2 == rparen);1028 try renderToken(ais, tree, last_prefix_token, .space);
1161 try renderToken(ais, tree, payload_token - 2, .space); // )
1162 try renderToken(ais, tree, payload_token - 1, .none); // |1029 try renderToken(ais, tree, payload_token - 1, .none); // |
1163 const ident = blk: {1030 const ident = blk: {
1164 if (token_tags[payload_token] == .asterisk) {1031 if (token_tags[payload_token] == .asterisk) {
...@@ -1178,33 +1045,67 @@ fn renderWhile(gpa: *Allocator, ais: *Ais, tree: ast.Tree, while_node: ast.full....@@ -1178,33 +1045,67 @@ fn renderWhile(gpa: *Allocator, ais: *Ais, tree: ast.Tree, while_node: ast.full.
1178 break :blk ident + 1;1045 break :blk ident + 1;
1179 }1046 }
1180 };1047 };
1181 try renderToken(ais, tree, pipe, .space); // |1048 last_prefix_token = pipe;
1182 } else {
1183 try renderToken(ais, tree, rparen, .space); // )
1184 }1049 }
11851050
1186 if (while_node.ast.cont_expr != 0) {1051 if (while_node.ast.cont_expr != 0) {
1187 const cont_rparen = tree.lastToken(while_node.ast.cont_expr) + 1;1052 try renderToken(ais, tree, last_prefix_token, .space);
1188 const cont_lparen = tree.firstToken(while_node.ast.cont_expr) - 1;1053 const lparen = tree.firstToken(while_node.ast.cont_expr) - 1;
1189 try renderToken(ais, tree, cont_lparen - 1, .space); // :1054 try renderToken(ais, tree, lparen - 1, .space); // :
1190 try renderToken(ais, tree, cont_lparen, .none); // lparen1055 try renderToken(ais, tree, lparen, .none); // lparen
1191 try renderExpression(gpa, ais, tree, while_node.ast.cont_expr, .none);1056 try renderExpression(gpa, ais, tree, while_node.ast.cont_expr, .none);
1192 try renderToken(ais, tree, cont_rparen, .space); // rparen1057 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);
1193 }1069 }
11941070
1195 if (while_node.ast.else_expr != 0) {1071 if (while_node.ast.else_expr != 0) {
1196 try renderExpression(gpa, ais, tree, while_node.ast.then_expr, .space);1072 const first_else_expr_tok = tree.firstToken(while_node.ast.else_expr);
1197 try renderToken(ais, tree, while_node.else_token, .space); // else1073
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
1199 if (while_node.error_token) |error_token| {1084 if (while_node.error_token) |error_token| {
1085 try renderToken(ais, tree, while_node.else_token, .space); // else
1200 try renderToken(ais, tree, error_token - 1, .none); // |1086 try renderToken(ais, tree, error_token - 1, .none); // |
1201 try renderToken(ais, tree, error_token, .none); // identifier1087 try renderToken(ais, tree, error_token, .none); // identifier
1202 try renderToken(ais, tree, error_token + 1, .space); // |1088 last_else_token = error_token + 1; // |
1203 }1089 }
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 }
1206 } else {1103 } 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 }
1208 }1109 }
1209}1110}
12101111
...@@ -1258,8 +1159,29 @@ fn renderContainerField(...@@ -1258,8 +1159,29 @@ fn renderContainerField(
1258 try renderToken(ais, tree, rparen_token, .space); // )1159 try renderToken(ais, tree, rparen_token, .space); // )
1259 }1160 }
1260 const eq_token = tree.firstToken(field.ast.value_expr) - 1;1161 const eq_token = tree.firstToken(field.ast.value_expr) - 1;
1261 try renderToken(ais, tree, eq_token, .space); // =1162 const eq_space: Space = if (tree.tokensOnSameLine(eq_token, eq_token + 1)) .space else .newline;
1262 return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value1163 {
1164 ais.pushIndent();
1165 try renderToken(ais, tree, eq_token, eq_space); // =
1166 ais.popIndent();
1167 }
1168
1169 if (eq_space == .space)
1170 return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value
1171
1172 const token_tags = tree.tokens.items(.tag);
1173 const maybe_comma = tree.lastToken(field.ast.value_expr) + 1;
1174
1175 if (token_tags[maybe_comma] == .comma) {
1176 ais.pushIndent();
1177 try renderExpression(gpa, ais, tree, field.ast.value_expr, .none); // value
1178 ais.popIndent();
1179 try renderToken(ais, tree, maybe_comma, space);
1180 } else {
1181 ais.pushIndent();
1182 try renderExpression(gpa, ais, tree, field.ast.value_expr, space); // value
1183 ais.popIndent();
1184 }
1263}1185}
12641186
1265fn renderBuiltinCall(1187fn renderBuiltinCall(
...@@ -1522,6 +1444,7 @@ fn renderSwitchCase(...@@ -1522,6 +1444,7 @@ fn renderSwitchCase(
1522 switch_case: ast.full.SwitchCase,1444 switch_case: ast.full.SwitchCase,
1523 space: Space,1445 space: Space,
1524) Error!void {1446) Error!void {
1447 const node_tags = tree.nodes.items(.tag);
1525 const token_tags = tree.tokens.items(.tag);1448 const token_tags = tree.tokens.items(.tag);
1526 const trailing_comma = token_tags[switch_case.ast.arrow_token - 1] == .comma;1449 const trailing_comma = token_tags[switch_case.ast.arrow_token - 1] == .comma;
15271450
...@@ -1544,17 +1467,23 @@ fn renderSwitchCase(...@@ -1544,17 +1467,23 @@ fn renderSwitchCase(
1544 }1467 }
15451468
1546 // Render the arrow and everything after it1469 // 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
1549 if (switch_case.payload_token) |payload_token| {1478 if (switch_case.payload_token) |payload_token| {
1550 try renderToken(ais, tree, payload_token - 1, .none); // pipe1479 try renderToken(ais, tree, payload_token - 1, .none); // pipe
1551 if (token_tags[payload_token] == .asterisk) {1480 if (token_tags[payload_token] == .asterisk) {
1552 try renderToken(ais, tree, payload_token, .none); // asterisk1481 try renderToken(ais, tree, payload_token, .none); // asterisk
1553 try renderToken(ais, tree, payload_token + 1, .none); // identifier1482 try renderToken(ais, tree, payload_token + 1, .none); // identifier
1554 try renderToken(ais, tree, payload_token + 2, .space); // pipe1483 try renderToken(ais, tree, payload_token + 2, pre_target_space); // pipe
1555 } else {1484 } else {
1556 try renderToken(ais, tree, payload_token, .none); // identifier1485 try renderToken(ais, tree, payload_token, .none); // identifier
1557 try renderToken(ais, tree, payload_token + 1, .space); // pipe1486 try renderToken(ais, tree, payload_token + 1, pre_target_space); // pipe
1558 }1487 }
1559 }1488 }
15601489
...@@ -2493,6 +2422,21 @@ fn nodeIsBlock(tag: ast.Node.Tag) bool {...@@ -2493,6 +2422,21 @@ fn nodeIsBlock(tag: ast.Node.Tag) bool {
2493 .block_semicolon,2422 .block_semicolon,
2494 .block_two,2423 .block_two,
2495 .block_two_semicolon,2424 .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) {
2496 .@"if",2440 .@"if",
2497 .if_simple,2441 .if_simple,
2498 .@"for",2442 .@"for",
...@@ -2507,13 +2451,6 @@ fn nodeIsBlock(tag: ast.Node.Tag) bool {...@@ -2507,13 +2451,6 @@ fn nodeIsBlock(tag: ast.Node.Tag) bool {
2507 };2451 };
2508}2452}
25092453
2510fn nodeIsIf(tag: ast.Node.Tag) bool {
2511 return switch (tag) {
2512 .@"if", .if_simple => true,
2513 else => false,
2514 };
2515}
2516
2517fn nodeCausesSliceOpSpace(tag: ast.Node.Tag) bool {2454fn nodeCausesSliceOpSpace(tag: ast.Node.Tag) bool {
2518 return switch (tag) {2455 return switch (tag) {
2519 .@"catch",2456 .@"catch",
src/BuiltinFn.zig+1-1
...@@ -477,7 +477,7 @@ pub const list = list: {...@@ -477,7 +477,7 @@ pub const list = list: {
477 "@intCast",477 "@intCast",
478 .{478 .{
479 .tag = .int_cast,479 .tag = .int_cast,
480 .param_count = 1,480 .param_count = 2,
481 },481 },
482 },482 },
483 .{483 .{
src/Compilation.zig+17-6
...@@ -3180,7 +3180,11 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node...@@ -3180,7 +3180,11 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
3180 id_symlink_basename,3180 id_symlink_basename,
3181 &prev_digest_buf,3181 &prev_digest_buf,
3182 ) catch |err| blk: {3182 ) 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 });
3184 // Handle this as a cache miss.3188 // Handle this as a cache miss.
3185 break :blk prev_digest_buf[0..0];3189 break :blk prev_digest_buf[0..0];
3186 };3190 };
...@@ -3188,10 +3192,13 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node...@@ -3188,10 +3192,13 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
3188 if (!mem.eql(u8, prev_digest[0..digest.len], &digest))3192 if (!mem.eql(u8, prev_digest[0..digest.len], &digest))
3189 break :hit;3193 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 });
3192 var flags_bytes: [1]u8 = undefined;3199 var flags_bytes: [1]u8 = undefined;
3193 _ = std.fmt.hexToBytes(&flags_bytes, prev_digest[digest.len..]) catch {3200 _ = 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)});
3195 break :hit;3202 break :hit;
3196 };3203 };
31973204
...@@ -3211,7 +3218,11 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node...@@ -3211,7 +3218,11 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
3211 mod.stage1_flags = @bitCast(@TypeOf(mod.stage1_flags), flags_bytes[0]);3218 mod.stage1_flags = @bitCast(@TypeOf(mod.stage1_flags), flags_bytes[0]);
3212 return;3219 return;
3213 }3220 }
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 });
3215 man.unhit(prev_hash_state, input_file_count);3226 man.unhit(prev_hash_state, input_file_count);
3216 }3227 }
32173228
...@@ -3358,8 +3369,8 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node...@@ -3358,8 +3369,8 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
3358 // Update the small file with the digest. If it fails we can continue; it only3369 // Update the small file with the digest. If it fails we can continue; it only
3359 // means that the next invocation will have an unnecessary cache miss.3370 // means that the next invocation will have an unnecessary cache miss.
3360 const stage1_flags_byte = @bitCast(u8, mod.stage1_flags);3371 const stage1_flags_byte = @bitCast(u8, mod.stage1_flags);
3361 log.debug("stage1 {s} final digest={} flags={x}", .{3372 log.debug("stage1 {s} final digest={s} flags={x}", .{
3362 mod.root_pkg.root_src_path, digest, stage1_flags_byte,3373 mod.root_pkg.root_src_path, std.fmt.fmtSliceHexLower(&digest), stage1_flags_byte,
3363 });3374 });
3364 var digest_plus_flags: [digest.len + 2]u8 = undefined;3375 var digest_plus_flags: [digest.len + 2]u8 = undefined;
3365 digest_plus_flags[0..digest.len].* = digest;3376 digest_plus_flags[0..digest.len].* = digest;
src/clang.zig+5
...@@ -537,6 +537,11 @@ pub const FunctionType = opaque {...@@ -537,6 +537,11 @@ pub const FunctionType = opaque {
537 extern fn ZigClangFunctionType_getReturnType(*const FunctionType) QualType;537 extern fn ZigClangFunctionType_getReturnType(*const FunctionType) QualType;
538};538};
539539
540pub const GenericSelectionExpr = opaque {
541 pub const getResultExpr = ZigClangGenericSelectionExpr_getResultExpr;
542 extern fn ZigClangGenericSelectionExpr_getResultExpr(*const GenericSelectionExpr) *const Expr;
543};
544
540pub const IfStmt = opaque {545pub const IfStmt = opaque {
541 pub const getThen = ZigClangIfStmt_getThen;546 pub const getThen = ZigClangIfStmt_getThen;
542 extern fn ZigClangIfStmt_getThen(*const IfStmt) *const Stmt;547 extern fn ZigClangIfStmt_getThen(*const IfStmt) *const Stmt;
src/clang_options_data.zig+8-1
...@@ -2415,7 +2415,14 @@ flagpd1("dwarf-ext-refs"),...@@ -2415,7 +2415,14 @@ flagpd1("dwarf-ext-refs"),
2415sepd1("dylib_file"),2415sepd1("dylib_file"),
2416flagpd1("dylinker"),2416flagpd1("dylinker"),
2417flagpd1("dynamic"),2417flagpd1("dynamic"),
2418flagpd1("dynamiclib"),2418.{
2419 .name = "dynamiclib",
2420 .syntax = .flag,
2421 .zig_equivalent = .shared,
2422 .pd1 = true,
2423 .pd2 = false,
2424 .psl = false,
2425},
2419flagpd1("emit-ast"),2426flagpd1("emit-ast"),
2420flagpd1("emit-codegen-only"),2427flagpd1("emit-codegen-only"),
2421flagpd1("emit-header-module"),2428flagpd1("emit-header-module"),
src/codegen.zig+61-134
...@@ -2132,9 +2132,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2132,9 +2132,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2132 if (inst.func.value()) |func_value| {2132 if (inst.func.value()) |func_value| {
2133 if (func_value.castTag(.function)) |func_payload| {2133 if (func_value.castTag(.function)) |func_payload| {
2134 const func = func_payload.data;2134 const func = func_payload.data;
2135 const text_segment = &macho_file.load_commands.items[macho_file.text_segment_cmd_index.?].Segment;2135 const got_addr = blk: {
2136 const got = &text_segment.sections.items[macho_file.got_section_index.?];2136 const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
2137 const got_addr = got.addr + func.owner_decl.link.macho.offset_table_index * @sizeOf(u64);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});
2138 switch (arch) {2141 switch (arch) {
2139 .x86_64 => {2142 .x86_64 => {
2140 try self.genSetReg(inst.base.src, Type.initTag(.u32), .rax, .{ .memory = got_addr });2143 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 {...@@ -2152,8 +2155,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2152 const decl = func_payload.data;2155 const decl = func_payload.data;
2153 const decl_name = try std.fmt.allocPrint(self.bin_file.allocator, "_{s}", .{decl.name});2156 const decl_name = try std.fmt.allocPrint(self.bin_file.allocator, "_{s}", .{decl.name});
2154 defer self.bin_file.allocator.free(decl_name);2157 defer self.bin_file.allocator.free(decl_name);
2155 const already_defined = macho_file.extern_lazy_symbols.contains(decl_name);2158 const already_defined = macho_file.lazy_imports.contains(decl_name);
2156 const symbol: u32 = if (macho_file.extern_lazy_symbols.getIndex(decl_name)) |index|2159 const symbol: u32 = if (macho_file.lazy_imports.getIndex(decl_name)) |index|
2157 @intCast(u32, index)2160 @intCast(u32, index)
2158 else2161 else
2159 try macho_file.addExternSymbol(decl_name);2162 try macho_file.addExternSymbol(decl_name);
...@@ -3111,7 +3114,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3111,7 +3114,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3111 4, 8 => {3114 4, 8 => {
3112 const offset = if (math.cast(i9, adj_off)) |imm|3115 const offset = if (math.cast(i9, adj_off)) |imm|
3113 Instruction.LoadStoreOffset.imm_post_index(-imm)3116 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 }));
3115 const rn: Register = switch (arch) {3119 const rn: Register = switch (arch) {
3116 .aarch64, .aarch64_be => .x29,3120 .aarch64, .aarch64_be => .x29,
3117 .aarch64_32 => .w29,3121 .aarch64_32 => .w29,
...@@ -3302,80 +3306,32 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3302,80 +3306,32 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3302 },3306 },
3303 .memory => |addr| {3307 .memory => |addr| {
3304 if (self.bin_file.options.pie) {3308 if (self.bin_file.options.pie) {
3305 // For MachO, the binary, with the exception of object files, has to be a PIE.3309 // PC-relative displacement to the entry in the GOT table.
3306 // Therefore we cannot load an absolute address.3310 // TODO we should come up with our own, backend independent relocation types
3307 // Instead, we need to make use of PC-relative addressing.3311 // which each backend (Elf, MachO, etc.) would then translate into an actual
3308 if (reg.id() == 0) { // x0 is special-cased3312 // fixup when linking.
3309 // TODO This needs to be optimised in the stack usage (perhaps use a shadow stack3313 // adrp reg, pages
3310 // like described here:3314 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3311 // https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/using-the-stack-in-aarch64-implementing-push-and-pop)3315 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3312 // str x28, [sp, #-16]3316 .target_addr = addr,
3313 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.str(.x28, Register.sp, .{3317 .offset = self.code.items.len,
3314 .offset = Instruction.LoadStoreOffset.imm_pre_index(-16),3318 .size = 4,
3315 }).toU32());3319 });
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());
3343 } else {3320 } else {
3344 // stp x0, x28, [sp, #-16]3321 return self.fail(src, "TODO implement genSetReg for PIE GOT indirection on this platform", .{});
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());
3378 }3322 }
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());
3379 } else {3335 } else {
3380 // The value is in memory at a hard-coded address.3336 // The value is in memory at a hard-coded address.
3381 // If the type is a pointer, it means the pointer address is at this memory location.3337 // 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 {...@@ -3559,62 +3515,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3559 },3515 },
3560 .memory => |x| {3516 .memory => |x| {
3561 if (self.bin_file.options.pie) {3517 if (self.bin_file.options.pie) {
3562 // For MachO, the binary, with the exception of object files, has to be a PIE.3518 // RIP-relative displacement to the entry in the GOT table.
3563 // Therefore, we cannot load an absolute address.3519 // TODO we should come up with our own, backend independent relocation types
3564 assert(x > math.maxInt(u32)); // 32bit direct addressing is not supported by MachO.3520 // which each backend (Elf, MachO, etc.) would then translate into an actual
3565 // The plan here is to use unconditional relative jump to GOT entry, where we store3521 // fixup when linking.
3566 // pre-calculated and stored effective address to load into the target register.3522 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3567 // We leave the actual displacement information empty (0-padded) and fixing it up3523 try macho_file.pie_fixups.append(self.bin_file.allocator, .{
3568 // later in the linker.3524 .target_addr = x,
3569 if (reg.id() == 0) { // %rax is special-cased3525 .offset = self.code.items.len + 3,
3570 try self.code.ensureCapacity(self.code.items.len + 5);3526 .size = 4,
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,
3587 });3527 });
3588 } else {3528 } else {
3589 try self.code.ensureCapacity(self.code.items.len + 10);3529 return self.fail(src, "TODO implement genSetReg for PIE GOT indirection on this platform", .{});
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});
3617 }3530 }
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 });
3618 } else if (x <= math.maxInt(u32)) {3543 } else if (x <= math.maxInt(u32)) {
3619 // Moving from memory to a register is a variant of `8B /r`.3544 // Moving from memory to a register is a variant of `8B /r`.
3620 // Since we're using 64-bit moves, we require a REX.3545 // Since we're using 64-bit moves, we require a REX.
...@@ -3777,9 +3702,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3777,9 +3702,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3777 return MCValue{ .memory = got_addr };3702 return MCValue{ .memory = got_addr };
3778 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {3703 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
3779 const decl = payload.data;3704 const decl = payload.data;
3780 const text_segment = &macho_file.load_commands.items[macho_file.text_segment_cmd_index.?].Segment;3705 const got_addr = blk: {
3781 const got = &text_segment.sections.items[macho_file.got_section_index.?];3706 const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
3782 const got_addr = got.addr + decl.link.macho.offset_table_index * ptr_bytes;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 };
3783 return MCValue{ .memory = got_addr };3710 return MCValue{ .memory = got_addr };
3784 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {3711 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
3785 const decl = payload.data;3712 const decl = payload.data;
src/codegen/aarch64.zig+4-1
...@@ -221,7 +221,8 @@ pub const Instruction = union(enum) {...@@ -221,7 +221,8 @@ pub const Instruction = union(enum) {
221 offset: u12,221 offset: u12,
222 opc: u2,222 opc: u2,
223 op1: u2,223 op1: u2,
224 fixed: u4 = 0b111_0,224 v: u1,
225 fixed: u3 = 0b111,
225 size: u2,226 size: u2,
226 },227 },
227 LoadStorePairOfRegisters: packed struct {228 LoadStorePairOfRegisters: packed struct {
...@@ -505,6 +506,7 @@ pub const Instruction = union(enum) {...@@ -505,6 +506,7 @@ pub const Instruction = union(enum) {
505 .offset = offset.toU12(),506 .offset = offset.toU12(),
506 .opc = opc,507 .opc = opc,
507 .op1 = op1,508 .op1 = op1,
509 .v = 0,
508 .size = 0b10,510 .size = 0b10,
509 },511 },
510 };512 };
...@@ -517,6 +519,7 @@ pub const Instruction = union(enum) {...@@ -517,6 +519,7 @@ pub const Instruction = union(enum) {
517 .offset = offset.toU12(),519 .offset = offset.toU12(),
518 .opc = opc,520 .opc = opc,
519 .op1 = op1,521 .op1 = op1,
522 .v = 0,
520 .size = 0b11,523 .size = 0b11,
521 },524 },
522 };525 };
src/codegen/llvm.zig+11-11
...@@ -222,7 +222,7 @@ pub const LLVMIRModule = struct {...@@ -222,7 +222,7 @@ pub const LLVMIRModule = struct {
222222
223 var error_message: [*:0]const u8 = undefined;223 var error_message: [*:0]const u8 = undefined;
224 var target: *const llvm.Target = undefined;224 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()) {
226 defer llvm.disposeMessage(error_message);226 defer llvm.disposeMessage(error_message);
227227
228 const stderr = std.io.getStdErr().writer();228 const stderr = std.io.getStdErr().writer();
...@@ -306,7 +306,7 @@ pub const LLVMIRModule = struct {...@@ -306,7 +306,7 @@ pub const LLVMIRModule = struct {
306 // verifyModule always allocs the error_message even if there is no error306 // verifyModule always allocs the error_message even if there is no error
307 defer llvm.disposeMessage(error_message);307 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()) {
310 const stderr = std.io.getStdErr().writer();310 const stderr = std.io.getStdErr().writer();
311 try stderr.print("broken LLVM module found: {s}\nThis is a bug in the Zig compiler.", .{error_message});311 try stderr.print("broken LLVM module found: {s}\nThis is a bug in the Zig compiler.", .{error_message});
312 return error.BrokenLLVMModule;312 return error.BrokenLLVMModule;
...@@ -322,7 +322,7 @@ pub const LLVMIRModule = struct {...@@ -322,7 +322,7 @@ pub const LLVMIRModule = struct {
322 object_pathZ.ptr,322 object_pathZ.ptr,
323 .ObjectFile,323 .ObjectFile,
324 &error_message,324 &error_message,
325 )) {325 ).toBool()) {
326 defer llvm.disposeMessage(error_message);326 defer llvm.disposeMessage(error_message);
327327
328 const stderr = std.io.getStdErr().writer();328 const stderr = std.io.getStdErr().writer();
...@@ -617,7 +617,7 @@ pub const LLVMIRModule = struct {...@@ -617,7 +617,7 @@ pub const LLVMIRModule = struct {
617617
618 var indices: [2]*const llvm.Value = .{618 var indices: [2]*const llvm.Value = .{
619 index_type.constNull(),619 index_type.constNull(),
620 index_type.constInt(1, false),620 index_type.constInt(1, .False),
621 };621 };
622622
623 return self.builder.buildLoad(self.builder.buildInBoundsGEP(operand, &indices, 2, ""), "");623 return self.builder.buildLoad(self.builder.buildInBoundsGEP(operand, &indices, 2, ""), "");
...@@ -679,7 +679,7 @@ pub const LLVMIRModule = struct {...@@ -679,7 +679,7 @@ pub const LLVMIRModule = struct {
679 const signed = inst.base.ty.isSignedInt();679 const signed = inst.base.ty.isSignedInt();
680 // TODO: Should we use intcast here or just a simple bitcast?680 // TODO: Should we use intcast here or just a simple bitcast?
681 // LLVM does truncation vs bitcast (+signed extension) in the intcast depending on the sizes681 // 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), "");
683 }683 }
684684
685 fn genBitCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value {685 fn genBitCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value {
...@@ -785,7 +785,7 @@ pub const LLVMIRModule = struct {...@@ -785,7 +785,7 @@ pub const LLVMIRModule = struct {
785 if (bigint.limbs.len != 1) {785 if (bigint.limbs.len != 1) {
786 return self.fail(src, "TODO implement bigger bigint", .{});786 return self.fail(src, "TODO implement bigger bigint", .{});
787 }787 }
788 const llvm_int = llvm_type.constInt(bigint.limbs[0], false);788 const llvm_int = llvm_type.constInt(bigint.limbs[0], .False);
789 if (!bigint.positive) {789 if (!bigint.positive) {
790 return llvm.constNeg(llvm_int);790 return llvm.constNeg(llvm_int);
791 }791 }
...@@ -823,7 +823,7 @@ pub const LLVMIRModule = struct {...@@ -823,7 +823,7 @@ pub const LLVMIRModule = struct {
823 return self.fail(src, "TODO handle other sentinel values", .{});823 return self.fail(src, "TODO handle other sentinel values", .{});
824 } else false;824 } 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));
827 } else {827 } else {
828 return self.fail(src, "TODO handle more array values", .{});828 return self.fail(src, "TODO handle more array values", .{});
829 }829 }
...@@ -839,13 +839,13 @@ pub const LLVMIRModule = struct {...@@ -839,13 +839,13 @@ pub const LLVMIRModule = struct {
839 llvm_child_type.constNull(),839 llvm_child_type.constNull(),
840 self.context.intType(1).constNull(),840 self.context.intType(1).constNull(),
841 };841 };
842 return self.context.constStruct(&optional_values, 2, false);842 return self.context.constStruct(&optional_values, 2, .False);
843 } else {843 } else {
844 var optional_values: [2]*const llvm.Value = .{844 var optional_values: [2]*const llvm.Value = .{
845 try self.genTypedValue(src, .{ .ty = child_type, .val = tv.val }),845 try self.genTypedValue(src, .{ .ty = child_type, .val = tv.val }),
846 self.context.intType(1).constAllOnes(),846 self.context.intType(1).constAllOnes(),
847 };847 };
848 return self.context.constStruct(&optional_values, 2, false);848 return self.context.constStruct(&optional_values, 2, .False);
849 }849 }
850 } else {850 } else {
851 return self.fail(src, "TODO implement const of optional pointer", .{});851 return self.fail(src, "TODO implement const of optional pointer", .{});
...@@ -885,7 +885,7 @@ pub const LLVMIRModule = struct {...@@ -885,7 +885,7 @@ pub const LLVMIRModule = struct {
885 try self.getLLVMType(child_type, src),885 try self.getLLVMType(child_type, src),
886 self.context.intType(1),886 self.context.intType(1),
887 };887 };
888 return self.context.structType(&optional_types, 2, false);888 return self.context.structType(&optional_types, 2, .False);
889 } else {889 } else {
890 return self.fail(src, "TODO implement optional pointers as actual pointers", .{});890 return self.fail(src, "TODO implement optional pointers as actual pointers", .{});
891 }891 }
...@@ -937,7 +937,7 @@ pub const LLVMIRModule = struct {...@@ -937,7 +937,7 @@ pub const LLVMIRModule = struct {
937 try self.getLLVMType(return_type, src),937 try self.getLLVMType(return_type, src),
938 if (fn_param_len == 0) null else llvm_param.ptr,938 if (fn_param_len == 0) null else llvm_param.ptr,
939 @intCast(c_uint, fn_param_len),939 @intCast(c_uint, fn_param_len),
940 false,940 .False,
941 );941 );
942 const llvm_fn = self.llvm_module.addFunction(func.name, fn_type);942 const llvm_fn = self.llvm_module.addFunction(func.name, fn_type);
943943
src/codegen/llvm/bindings.zig+23-10
...@@ -1,7 +1,20 @@...@@ -1,7 +1,20 @@
1//! We do this instead of @cImport because the self-hosted compiler is easier1//! We do this instead of @cImport because the self-hosted compiler is easier
2//! to bootstrap if it does not depend on translate-c.2//! to bootstrap if it does not depend on translate-c.
33
4const LLVMBool = bool;4/// Do not compare directly to .True, use toBool() instead.
5pub const Bool = enum(c_int) {
6 False,
7 True,
8 _,
9
10 pub fn fromBool(b: bool) Bool {
11 return @intToEnum(Bool, @boolToInt(b));
12 }
13
14 pub fn toBool(b: Bool) bool {
15 return b != .False;
16 }
17};
5pub const AttributeIndex = c_uint;18pub const AttributeIndex = c_uint;
619
7/// Make sure to use the *InContext functions instead of the global ones.20/// Make sure to use the *InContext functions instead of the global ones.
...@@ -22,13 +35,13 @@ pub const Context = opaque {...@@ -22,13 +35,13 @@ pub const Context = opaque {
22 extern fn LLVMVoidTypeInContext(C: *const Context) *const Type;35 extern fn LLVMVoidTypeInContext(C: *const Context) *const Type;
2336
24 pub const structType = LLVMStructTypeInContext;37 pub const structType = LLVMStructTypeInContext;
25 extern fn LLVMStructTypeInContext(C: *const Context, ElementTypes: [*]*const Type, ElementCount: c_uint, Packed: LLVMBool) *const Type;38 extern fn LLVMStructTypeInContext(C: *const Context, ElementTypes: [*]*const Type, ElementCount: c_uint, Packed: Bool) *const Type;
2639
27 pub const constString = LLVMConstStringInContext;40 pub const constString = LLVMConstStringInContext;
28 extern fn LLVMConstStringInContext(C: *const Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: LLVMBool) *const Value;41 extern fn LLVMConstStringInContext(C: *const Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: Bool) *const Value;
2942
30 pub const constStruct = LLVMConstStructInContext;43 pub const constStruct = LLVMConstStructInContext;
31 extern fn LLVMConstStructInContext(C: *const Context, ConstantVals: [*]*const Value, Count: c_uint, Packed: LLVMBool) *const Value;44 extern fn LLVMConstStructInContext(C: *const Context, ConstantVals: [*]*const Value, Count: c_uint, Packed: Bool) *const Value;
3245
33 pub const createBasicBlock = LLVMCreateBasicBlockInContext;46 pub const createBasicBlock = LLVMCreateBasicBlockInContext;
34 extern fn LLVMCreateBasicBlockInContext(C: *const Context, Name: [*:0]const u8) *const BasicBlock;47 extern fn LLVMCreateBasicBlockInContext(C: *const Context, Name: [*:0]const u8) *const BasicBlock;
...@@ -59,7 +72,7 @@ pub const Value = opaque {...@@ -59,7 +72,7 @@ pub const Value = opaque {
5972
60pub const Type = opaque {73pub const Type = opaque {
61 pub const functionType = LLVMFunctionType;74 pub const functionType = LLVMFunctionType;
62 extern fn LLVMFunctionType(ReturnType: *const Type, ParamTypes: ?[*]*const Type, ParamCount: c_uint, IsVarArg: LLVMBool) *const Type;75 extern fn LLVMFunctionType(ReturnType: *const Type, ParamTypes: ?[*]*const Type, ParamCount: c_uint, IsVarArg: Bool) *const Type;
6376
64 pub const constNull = LLVMConstNull;77 pub const constNull = LLVMConstNull;
65 extern fn LLVMConstNull(Ty: *const Type) *const Value;78 extern fn LLVMConstNull(Ty: *const Type) *const Value;
...@@ -68,7 +81,7 @@ pub const Type = opaque {...@@ -68,7 +81,7 @@ pub const Type = opaque {
68 extern fn LLVMConstAllOnes(Ty: *const Type) *const Value;81 extern fn LLVMConstAllOnes(Ty: *const Type) *const Value;
6982
70 pub const constInt = LLVMConstInt;83 pub const constInt = LLVMConstInt;
71 extern fn LLVMConstInt(IntTy: *const Type, N: c_ulonglong, SignExtend: LLVMBool) *const Value;84 extern fn LLVMConstInt(IntTy: *const Type, N: c_ulonglong, SignExtend: Bool) *const Value;
7285
73 pub const constArray = LLVMConstArray;86 pub const constArray = LLVMConstArray;
74 extern fn LLVMConstArray(ElementTy: *const Type, ConstantVals: ?[*]*const Value, Length: c_uint) *const Value;87 extern fn LLVMConstArray(ElementTy: *const Type, ConstantVals: ?[*]*const Value, Length: c_uint) *const Value;
...@@ -91,7 +104,7 @@ pub const Module = opaque {...@@ -91,7 +104,7 @@ pub const Module = opaque {
91 extern fn LLVMDisposeModule(*const Module) void;104 extern fn LLVMDisposeModule(*const Module) void;
92105
93 pub const verify = LLVMVerifyModule;106 pub const verify = LLVMVerifyModule;
94 extern fn LLVMVerifyModule(*const Module, Action: VerifierFailureAction, OutMessage: *[*:0]const u8) LLVMBool;107 extern fn LLVMVerifyModule(*const Module, Action: VerifierFailureAction, OutMessage: *[*:0]const u8) Bool;
95108
96 pub const addFunction = LLVMAddFunction;109 pub const addFunction = LLVMAddFunction;
97 extern fn LLVMAddFunction(*const Module, Name: [*:0]const u8, FunctionTy: *const Type) *const Value;110 extern fn LLVMAddFunction(*const Module, Name: [*:0]const u8, FunctionTy: *const Type) *const Value;
...@@ -191,7 +204,7 @@ pub const Builder = opaque {...@@ -191,7 +204,7 @@ pub const Builder = opaque {
191 extern fn LLVMBuildNUWSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;204 extern fn LLVMBuildNUWSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
192205
193 pub const buildIntCast2 = LLVMBuildIntCast2;206 pub const buildIntCast2 = LLVMBuildIntCast2;
194 extern fn LLVMBuildIntCast2(*const Builder, Val: *const Value, DestTy: *const Type, IsSigned: LLVMBool, Name: [*:0]const u8) *const Value;207 extern fn LLVMBuildIntCast2(*const Builder, Val: *const Value, DestTy: *const Type, IsSigned: Bool, Name: [*:0]const u8) *const Value;
195208
196 pub const buildBitCast = LLVMBuildBitCast;209 pub const buildBitCast = LLVMBuildBitCast;
197 extern fn LLVMBuildBitCast(*const Builder, Val: *const Value, DestTy: *const Type, Name: [*:0]const u8) *const Value;210 extern fn LLVMBuildBitCast(*const Builder, Val: *const Value, DestTy: *const Type, Name: [*:0]const u8) *const Value;
...@@ -258,7 +271,7 @@ pub const TargetMachine = opaque {...@@ -258,7 +271,7 @@ pub const TargetMachine = opaque {
258 Filename: [*:0]const u8,271 Filename: [*:0]const u8,
259 codegen: CodeGenFileType,272 codegen: CodeGenFileType,
260 ErrorMessage: *[*:0]const u8,273 ErrorMessage: *[*:0]const u8,
261 ) LLVMBool;274 ) Bool;
262};275};
263276
264pub const CodeMode = extern enum {277pub const CodeMode = extern enum {
...@@ -295,7 +308,7 @@ pub const CodeGenFileType = extern enum {...@@ -295,7 +308,7 @@ pub const CodeGenFileType = extern enum {
295308
296pub const Target = opaque {309pub const Target = opaque {
297 pub const getFromTriple = LLVMGetTargetFromTriple;310 pub const getFromTriple = LLVMGetTargetFromTriple;
298 extern fn LLVMGetTargetFromTriple(Triple: [*:0]const u8, T: **const Target, ErrorMessage: *[*:0]const u8) LLVMBool;311 extern fn LLVMGetTargetFromTriple(Triple: [*:0]const u8, T: **const Target, ErrorMessage: *[*:0]const u8) Bool;
299};312};
300313
301extern fn LLVMInitializeAArch64TargetInfo() void;314extern fn LLVMInitializeAArch64TargetInfo() void;
src/codegen/wasm.zig+41-3
...@@ -95,7 +95,7 @@ pub const Context = struct {...@@ -95,7 +95,7 @@ pub const Context = struct {
95 return switch (ty.tag()) {95 return switch (ty.tag()) {
96 .f32 => wasm.valtype(.f32),96 .f32 => wasm.valtype(.f32),
97 .f64 => wasm.valtype(.f64),97 .f64 => wasm.valtype(.f64),
98 .u32, .i32 => wasm.valtype(.i32),98 .u32, .i32, .bool => wasm.valtype(.i32),
99 .u64, .i64 => wasm.valtype(.i64),99 .u64, .i64 => wasm.valtype(.i64),
100 else => self.fail(src, "TODO - Wasm genValtype for type '{s}'", .{ty.tag()}),100 else => self.fail(src, "TODO - Wasm genValtype for type '{s}'", .{ty.tag()}),
101 };101 };
...@@ -208,6 +208,7 @@ pub const Context = struct {...@@ -208,6 +208,7 @@ pub const Context = struct {
208 .alloc => self.genAlloc(inst.castTag(.alloc).?),208 .alloc => self.genAlloc(inst.castTag(.alloc).?),
209 .arg => self.genArg(inst.castTag(.arg).?),209 .arg => self.genArg(inst.castTag(.arg).?),
210 .block => self.genBlock(inst.castTag(.block).?),210 .block => self.genBlock(inst.castTag(.block).?),
211 .breakpoint => self.genBreakpoint(inst.castTag(.breakpoint).?),
211 .br => self.genBr(inst.castTag(.br).?),212 .br => self.genBr(inst.castTag(.br).?),
212 .call => self.genCall(inst.castTag(.call).?),213 .call => self.genCall(inst.castTag(.call).?),
213 .cmp_eq => self.genCmp(inst.castTag(.cmp_eq).?, .eq),214 .cmp_eq => self.genCmp(inst.castTag(.cmp_eq).?, .eq),
...@@ -221,9 +222,11 @@ pub const Context = struct {...@@ -221,9 +222,11 @@ pub const Context = struct {
221 .dbg_stmt => WValue.none,222 .dbg_stmt => WValue.none,
222 .load => self.genLoad(inst.castTag(.load).?),223 .load => self.genLoad(inst.castTag(.load).?),
223 .loop => self.genLoop(inst.castTag(.loop).?),224 .loop => self.genLoop(inst.castTag(.loop).?),
225 .not => self.genNot(inst.castTag(.not).?),
224 .ret => self.genRet(inst.castTag(.ret).?),226 .ret => self.genRet(inst.castTag(.ret).?),
225 .retvoid => WValue.none,227 .retvoid => WValue.none,
226 .store => self.genStore(inst.castTag(.store).?),228 .store => self.genStore(inst.castTag(.store).?),
229 .unreach => self.genUnreachable(inst.castTag(.unreach).?),
227 else => self.fail(inst.src, "TODO: Implement wasm inst: {s}", .{inst.tag}),230 else => self.fail(inst.src, "TODO: Implement wasm inst: {s}", .{inst.tag}),
228 };231 };
229 }232 }
...@@ -329,7 +332,7 @@ pub const Context = struct {...@@ -329,7 +332,7 @@ pub const Context = struct {
329 try writer.writeByte(wasm.opcode(.i32_const));332 try writer.writeByte(wasm.opcode(.i32_const));
330 try leb.writeILEB128(writer, inst.val.toUnsignedInt());333 try leb.writeILEB128(writer, inst.val.toUnsignedInt());
331 },334 },
332 .i32 => {335 .i32, .bool => {
333 try writer.writeByte(wasm.opcode(.i32_const));336 try writer.writeByte(wasm.opcode(.i32_const));
334 try leb.writeILEB128(writer, inst.val.toSignedInt());337 try leb.writeILEB128(writer, inst.val.toSignedInt());
335 },338 },
...@@ -414,7 +417,14 @@ pub const Context = struct {...@@ -414,7 +417,14 @@ pub const Context = struct {
414417
415 // insert blocks at the position of `offset` so418 // insert blocks at the position of `offset` so
416 // the condition can jump to it419 // 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 };
418 const block_ty = try self.genBlockType(condbr.base.src, condbr.base.ty);428 const block_ty = try self.genBlockType(condbr.base.src, condbr.base.ty);
419 try self.startBlock(.block, block_ty, offset);429 try self.startBlock(.block, block_ty, offset);
420430
...@@ -523,4 +533,32 @@ pub const Context = struct {...@@ -523,4 +533,32 @@ pub const Context = struct {
523533
524 return .none;534 return .none;
525 }535 }
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 }
526};564};
src/config.zig.in+1-1
...@@ -1,7 +1,7 @@...@@ -1,7 +1,7 @@
1pub const have_llvm = true;1pub const have_llvm = true;
2pub const version: [:0]const u8 = "@ZIG_VERSION@";2pub const version: [:0]const u8 = "@ZIG_VERSION@";
3pub const semver = try @import("std").SemanticVersion.parse(version);3pub const semver = try @import("std").SemanticVersion.parse(version);
4pub const enable_logging: bool = false;4pub const enable_logging: bool = @ZIG_ENABLE_LOGGING_BOOL@;
5pub const enable_tracy = false;5pub const enable_tracy = false;
6pub const is_stage1 = true;6pub const is_stage1 = true;
7pub const skip_non_native = false;7pub const skip_non_native = false;
src/introspect.zig+8
...@@ -61,6 +61,14 @@ pub fn findZigLibDirFromSelfExe(...@@ -61,6 +61,14 @@ pub fn findZigLibDirFromSelfExe(
6161
62/// Caller owns returned memory.62/// Caller owns returned memory.
63pub fn resolveGlobalCacheDir(allocator: *mem.Allocator) ![]u8 {63pub 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
64 const appname = "zig";72 const appname = "zig";
6573
66 if (std.Target.current.os.tag != .windows) {74 if (std.Target.current.os.tag != .windows) {
src/link/MachO.zig+551-540
...@@ -11,7 +11,9 @@ const codegen = @import("../codegen.zig");...@@ -11,7 +11,9 @@ const codegen = @import("../codegen.zig");
11const aarch64 = @import("../codegen/aarch64.zig");11const aarch64 = @import("../codegen/aarch64.zig");
12const math = std.math;12const math = std.math;
13const mem = std.mem;13const mem = std.mem;
14const meta = std.meta;
1415
16const bind = @import("MachO/bind.zig");
15const trace = @import("../tracy.zig").trace;17const trace = @import("../tracy.zig").trace;
16const build_options = @import("build_options");18const build_options = @import("build_options");
17const Module = @import("../Module.zig");19const Module = @import("../Module.zig");
...@@ -24,9 +26,9 @@ const target_util = @import("../target.zig");...@@ -24,9 +26,9 @@ const target_util = @import("../target.zig");
24const DebugSymbols = @import("MachO/DebugSymbols.zig");26const DebugSymbols = @import("MachO/DebugSymbols.zig");
25const Trie = @import("MachO/Trie.zig");27const Trie = @import("MachO/Trie.zig");
26const CodeSignature = @import("MachO/CodeSignature.zig");28const CodeSignature = @import("MachO/CodeSignature.zig");
29const Zld = @import("MachO/Zld.zig");
2730
28usingnamespace @import("MachO/commands.zig");31usingnamespace @import("MachO/commands.zig");
29usingnamespace @import("MachO/imports.zig");
3032
31pub const base_tag: File.Tag = File.Tag.macho;33pub const base_tag: File.Tag = File.Tag.macho;
3234
...@@ -87,14 +89,12 @@ code_signature_cmd_index: ?u16 = null,...@@ -87,14 +89,12 @@ code_signature_cmd_index: ?u16 = null,
8789
88/// Index into __TEXT,__text section.90/// Index into __TEXT,__text section.
89text_section_index: ?u16 = null,91text_section_index: ?u16 = null,
90/// Index into __TEXT,__ziggot section.
91got_section_index: ?u16 = null,
92/// Index into __TEXT,__stubs section.92/// Index into __TEXT,__stubs section.
93stubs_section_index: ?u16 = null,93stubs_section_index: ?u16 = null,
94/// Index into __TEXT,__stub_helper section.94/// Index into __TEXT,__stub_helper section.
95stub_helper_section_index: ?u16 = null,95stub_helper_section_index: ?u16 = null,
96/// Index into __DATA_CONST,__got section.96/// Index into __DATA_CONST,__got section.
97data_got_section_index: ?u16 = null,97got_section_index: ?u16 = null,
98/// Index into __DATA,__la_symbol_ptr section.98/// Index into __DATA,__la_symbol_ptr section.
99la_symbol_ptr_section_index: ?u16 = null,99la_symbol_ptr_section_index: ?u16 = null,
100/// Index into __DATA,__data section.100/// Index into __DATA,__data section.
...@@ -104,16 +104,16 @@ entry_addr: ?u64 = null,...@@ -104,16 +104,16 @@ entry_addr: ?u64 = null,
104104
105/// Table of all local symbols105/// Table of all local symbols
106/// Internally references string table for names (which are optional).106/// Internally references string table for names (which are optional).
107local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},107locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
108/// Table of all global symbols108/// Table of all global symbols
109global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},109globals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
110/// Table of all extern nonlazy symbols, indexed by name.110/// Table of all extern nonlazy symbols, indexed by name.
111extern_nonlazy_symbols: std.StringArrayHashMapUnmanaged(ExternSymbol) = .{},111nonlazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},
112/// Table of all extern lazy symbols, indexed by name.112/// Table of all extern lazy symbols, indexed by name.
113extern_lazy_symbols: std.StringArrayHashMapUnmanaged(ExternSymbol) = .{},113lazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},
114114
115local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},115locals_free_list: std.ArrayListUnmanaged(u32) = .{},
116global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},116globals_free_list: std.ArrayListUnmanaged(u32) = .{},
117offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},117offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
118118
119stub_helper_stubs_start_off: ?u64 = null,119stub_helper_stubs_start_off: ?u64 = null,
...@@ -122,8 +122,8 @@ stub_helper_stubs_start_off: ?u64 = null,...@@ -122,8 +122,8 @@ stub_helper_stubs_start_off: ?u64 = null,
122string_table: std.ArrayListUnmanaged(u8) = .{},122string_table: std.ArrayListUnmanaged(u8) = .{},
123string_table_directory: std.StringHashMapUnmanaged(u32) = .{},123string_table_directory: std.StringHashMapUnmanaged(u32) = .{},
124124
125/// Table of trampolines to the actual symbols in __text section.125/// Table of GOT entries.
126offset_table: std.ArrayListUnmanaged(u64) = .{},126offset_table: std.ArrayListUnmanaged(GOTEntry) = .{},
127127
128error_flags: File.ErrorFlags = File.ErrorFlags{},128error_flags: File.ErrorFlags = File.ErrorFlags{},
129129
...@@ -154,14 +154,19 @@ string_table_needs_relocation: bool = false,...@@ -154,14 +154,19 @@ string_table_needs_relocation: bool = false,
154/// allocate a fresh text block, which will have ideal capacity, and then grow it154/// allocate a fresh text block, which will have ideal capacity, and then grow it
155/// by 1 byte. It will then have -1 overcapacity.155/// by 1 byte. It will then have -1 overcapacity.
156text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},156text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
157
157/// Pointer to the last allocated text block158/// Pointer to the last allocated text block
158last_text_block: ?*TextBlock = null,159last_text_block: ?*TextBlock = null,
160
159/// A list of all PIE fixups required for this run of the linker.161/// A list of all PIE fixups required for this run of the linker.
160/// Warning, this is currently NOT thread-safe. See the TODO below.162/// Warning, this is currently NOT thread-safe. See the TODO below.
161/// TODO Move this list inside `updateDecl` where it should be allocated163/// TODO Move this list inside `updateDecl` where it should be allocated
162/// prior to calling `generateSymbol`, and then immediately deallocated164/// prior to calling `generateSymbol`, and then immediately deallocated
163/// rather than sitting in the global scope.165/// rather than sitting in the global scope.
164pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{},166/// TODO We should also rewrite this using generic relocations common to all
167/// backends.
168pie_fixups: std.ArrayListUnmanaged(PIEFixup) = .{},
169
165/// A list of all stub (extern decls) fixups required for this run of the linker.170/// A list of all stub (extern decls) fixups required for this run of the linker.
166/// Warning, this is currently NOT thread-safe. See the TODO below.171/// Warning, this is currently NOT thread-safe. See the TODO below.
167/// TODO Move this list inside `updateDecl` where it should be allocated172/// TODO Move this list inside `updateDecl` where it should be allocated
...@@ -169,14 +174,42 @@ pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{},...@@ -169,14 +174,42 @@ pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{},
169/// rather than sitting in the global scope.174/// rather than sitting in the global scope.
170stub_fixups: std.ArrayListUnmanaged(StubFixup) = .{},175stub_fixups: std.ArrayListUnmanaged(StubFixup) = .{},
171176
172pub const PieFixup = struct {177pub const GOTEntry = struct {
173 /// Target address we wanted to address in absolute terms.178 /// GOT entry can either be a local pointer or an extern (nonlazy) import.
174 address: u64,179 kind: enum {
175 /// Where in the byte stream we should perform the fixup.180 Local,
176 start: usize,181 Extern,
177 /// The length of the byte stream. For x86_64, this will be182 },
178 /// variable. For aarch64, it will be fixed at 4 bytes.183
179 len: usize,184 /// Id to the macho.nlist_64 from the respective table: either locals or nonlazy imports.
185 /// TODO I'm more and more inclined to just manage a single, max two symbol tables
186 /// rather than 4 as we currently do, but I'll follow up in the future PR.
187 symbol: u32,
188
189 /// Index of this entry in the GOT.
190 index: u32,
191};
192
193pub const Import = struct {
194 /// MachO symbol table entry.
195 symbol: macho.nlist_64,
196
197 /// Id of the dynamic library where the specified entries can be found.
198 dylib_ordinal: i64,
199
200 /// Index of this import within the import list.
201 index: u32,
202};
203
204pub const PIEFixup = struct {
205 /// Target VM address of this relocation.
206 target_addr: u64,
207
208 /// Offset within the byte stream.
209 offset: usize,
210
211 /// Size of the relocation.
212 size: usize,
180};213};
181214
182pub const StubFixup = struct {215pub const StubFixup = struct {
...@@ -260,9 +293,9 @@ pub const TextBlock = struct {...@@ -260,9 +293,9 @@ pub const TextBlock = struct {
260 /// File offset relocation happens transparently, so it is not included in293 /// File offset relocation happens transparently, so it is not included in
261 /// this calculation.294 /// this calculation.
262 fn capacity(self: TextBlock, macho_file: MachO) u64 {295 fn capacity(self: TextBlock, macho_file: MachO) u64 {
263 const self_sym = macho_file.local_symbols.items[self.local_sym_index];296 const self_sym = macho_file.locals.items[self.local_sym_index];
264 if (self.next) |next| {297 if (self.next) |next| {
265 const next_sym = macho_file.local_symbols.items[next.local_sym_index];298 const next_sym = macho_file.locals.items[next.local_sym_index];
266 return next_sym.n_value - self_sym.n_value;299 return next_sym.n_value - self_sym.n_value;
267 } else {300 } else {
268 // We are the last block.301 // We are the last block.
...@@ -274,8 +307,8 @@ pub const TextBlock = struct {...@@ -274,8 +307,8 @@ pub const TextBlock = struct {
274 fn freeListEligible(self: TextBlock, macho_file: MachO) bool {307 fn freeListEligible(self: TextBlock, macho_file: MachO) bool {
275 // No need to keep a free list node for the last block.308 // No need to keep a free list node for the last block.
276 const next = self.next orelse return false;309 const next = self.next orelse return false;
277 const self_sym = macho_file.local_symbols.items[self.local_sym_index];310 const self_sym = macho_file.locals.items[self.local_sym_index];
278 const next_sym = macho_file.local_symbols.items[next.local_sym_index];311 const next_sym = macho_file.locals.items[next.local_sym_index];
279 const cap = next_sym.n_value - self_sym.n_value;312 const cap = next_sym.n_value - self_sym.n_value;
280 const ideal_cap = padToIdeal(self.size);313 const ideal_cap = padToIdeal(self.size);
281 if (cap <= ideal_cap) return false;314 if (cap <= ideal_cap) return false;
...@@ -344,7 +377,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -344,7 +377,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
344 };377 };
345378
346 // Index 0 is always a null symbol.379 // Index 0 is always a null symbol.
347 try self.local_symbols.append(allocator, .{380 try self.locals.append(allocator, .{
348 .n_strx = 0,381 .n_strx = 0,
349 .n_type = 0,382 .n_type = 0,
350 .n_sect = 0,383 .n_sect = 0,
...@@ -600,7 +633,74 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -600,7 +633,74 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
600 if (!mem.eql(u8, the_object_path, full_out_path)) {633 if (!mem.eql(u8, the_object_path, full_out_path)) {
601 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});634 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
602 }635 }
603 } else {636 } else outer: {
637 const use_zld = blk: {
638 if (self.base.options.is_native_os and self.base.options.system_linker_hack) {
639 // If the user forces the use of ld64, make sure we are running native!
640 break :blk false;
641 }
642
643 if (self.base.options.target.cpu.arch == .aarch64) {
644 // On aarch64, always use zld.
645 break :blk true;
646 }
647
648 if (self.base.options.link_libcpp or
649 self.base.options.output_mode == .Lib or
650 self.base.options.linker_script != null)
651 {
652 // Fallback to LLD in this handful of cases on x86_64 only.
653 break :blk false;
654 }
655
656 break :blk true;
657 };
658
659 if (use_zld) {
660 var zld = Zld.init(self.base.allocator);
661 defer zld.deinit();
662 zld.arch = target.cpu.arch;
663
664 var input_files = std.ArrayList([]const u8).init(self.base.allocator);
665 defer input_files.deinit();
666 // Positional arguments to the linker such as object files.
667 try input_files.appendSlice(self.base.options.objects);
668 for (comp.c_object_table.items()) |entry| {
669 try input_files.append(entry.key.status.success.object_path);
670 }
671 if (module_obj_path) |p| {
672 try input_files.append(p);
673 }
674 try input_files.append(comp.compiler_rt_static_lib.?.full_object_path);
675 // libc++ dep
676 if (self.base.options.link_libcpp) {
677 try input_files.append(comp.libcxxabi_static_lib.?.full_object_path);
678 try input_files.append(comp.libcxx_static_lib.?.full_object_path);
679 }
680
681 if (self.base.options.verbose_link) {
682 var argv = std.ArrayList([]const u8).init(self.base.allocator);
683 defer argv.deinit();
684
685 try argv.append("zig");
686 try argv.append("ld");
687
688 try argv.ensureCapacity(input_files.items.len);
689 for (input_files.items) |f| {
690 argv.appendAssumeCapacity(f);
691 }
692
693 try argv.append("-o");
694 try argv.append(full_out_path);
695
696 Compilation.dump_argv(argv.items);
697 }
698
699 try zld.link(input_files.items, full_out_path);
700
701 break :outer;
702 }
703
604 // Create an LLD command line and invoke it.704 // Create an LLD command line and invoke it.
605 var argv = std.ArrayList([]const u8).init(self.base.allocator);705 var argv = std.ArrayList([]const u8).init(self.base.allocator);
606 defer argv.deinit();706 defer argv.deinit();
...@@ -644,9 +744,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -644,9 +744,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
644 try argv.append("defs");744 try argv.append("defs");
645 }745 }
646746
647 if (is_dyn_lib) {747 if (is_exe_or_dyn_lib) {
648 try argv.append("-static");
649 } else {
650 try argv.append("-dynamic");748 try argv.append("-dynamic");
651 }749 }
652750
...@@ -836,7 +934,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -836,7 +934,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
836 }934 }
837 },935 },
838 else => {936 else => {
839 log.err("{s} terminated", .{ argv.items[0] });937 log.err("{s} terminated", .{argv.items[0]});
840 return error.LLDCrashed;938 return error.LLDCrashed;
841 },939 },
842 }940 }
...@@ -873,119 +971,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -873,119 +971,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
873 log.warn("unexpected LLD stderr:\n{s}", .{stderr});971 log.warn("unexpected LLD stderr:\n{s}", .{stderr});
874 }972 }
875 }973 }
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 }
989 }974 }
990 }975 }
991976
...@@ -1021,14 +1006,14 @@ pub fn deinit(self: *MachO) void {...@@ -1021,14 +1006,14 @@ pub fn deinit(self: *MachO) void {
1021 if (self.d_sym) |*ds| {1006 if (self.d_sym) |*ds| {
1022 ds.deinit(self.base.allocator);1007 ds.deinit(self.base.allocator);
1023 }1008 }
1024 for (self.extern_lazy_symbols.items()) |*entry| {1009 for (self.lazy_imports.items()) |*entry| {
1025 self.base.allocator.free(entry.key);1010 self.base.allocator.free(entry.key);
1026 }1011 }
1027 self.extern_lazy_symbols.deinit(self.base.allocator);1012 self.lazy_imports.deinit(self.base.allocator);
1028 for (self.extern_nonlazy_symbols.items()) |*entry| {1013 for (self.nonlazy_imports.items()) |*entry| {
1029 self.base.allocator.free(entry.key);1014 self.base.allocator.free(entry.key);
1030 }1015 }
1031 self.extern_nonlazy_symbols.deinit(self.base.allocator);1016 self.nonlazy_imports.deinit(self.base.allocator);
1032 self.pie_fixups.deinit(self.base.allocator);1017 self.pie_fixups.deinit(self.base.allocator);
1033 self.stub_fixups.deinit(self.base.allocator);1018 self.stub_fixups.deinit(self.base.allocator);
1034 self.text_block_free_list.deinit(self.base.allocator);1019 self.text_block_free_list.deinit(self.base.allocator);
...@@ -1042,10 +1027,10 @@ pub fn deinit(self: *MachO) void {...@@ -1042,10 +1027,10 @@ pub fn deinit(self: *MachO) void {
1042 }1027 }
1043 self.string_table_directory.deinit(self.base.allocator);1028 self.string_table_directory.deinit(self.base.allocator);
1044 self.string_table.deinit(self.base.allocator);1029 self.string_table.deinit(self.base.allocator);
1045 self.global_symbols.deinit(self.base.allocator);1030 self.globals.deinit(self.base.allocator);
1046 self.global_symbol_free_list.deinit(self.base.allocator);1031 self.globals_free_list.deinit(self.base.allocator);
1047 self.local_symbols.deinit(self.base.allocator);1032 self.locals.deinit(self.base.allocator);
1048 self.local_symbol_free_list.deinit(self.base.allocator);1033 self.locals_free_list.deinit(self.base.allocator);
1049 for (self.load_commands.items) |*lc| {1034 for (self.load_commands.items) |*lc| {
1050 lc.deinit(self.base.allocator);1035 lc.deinit(self.base.allocator);
1051 }1036 }
...@@ -1100,7 +1085,7 @@ fn shrinkTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64) vo...@@ -1100,7 +1085,7 @@ fn shrinkTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64) vo
1100}1085}
11011086
1102fn growTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {1087fn growTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
1103 const sym = self.local_symbols.items[text_block.local_sym_index];1088 const sym = self.locals.items[text_block.local_sym_index];
1104 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;1089 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;
1105 const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);1090 const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);
1106 if (!need_realloc) return sym.n_value;1091 if (!need_realloc) return sym.n_value;
...@@ -1110,34 +1095,41 @@ fn growTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alig...@@ -1110,34 +1095,41 @@ fn growTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alig
1110pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {1095pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
1111 if (decl.link.macho.local_sym_index != 0) return;1096 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);
1114 try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);1099 try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
11151100
1116 if (self.local_symbol_free_list.popOrNull()) |i| {1101 if (self.locals_free_list.popOrNull()) |i| {
1117 log.debug("reusing symbol index {d} for {s}", .{ i, decl.name });1102 log.debug("reusing symbol index {d} for {s}", .{ i, decl.name });
1118 decl.link.macho.local_sym_index = i;1103 decl.link.macho.local_sym_index = i;
1119 } else {1104 } else {
1120 log.debug("allocating symbol index {d} for {s}", .{ self.local_symbols.items.len, decl.name });1105 log.debug("allocating symbol index {d} for {s}", .{ self.locals.items.len, decl.name });
1121 decl.link.macho.local_sym_index = @intCast(u32, self.local_symbols.items.len);1106 decl.link.macho.local_sym_index = @intCast(u32, self.locals.items.len);
1122 _ = self.local_symbols.addOneAssumeCapacity();1107 _ = self.locals.addOneAssumeCapacity();
1123 }1108 }
11241109
1125 if (self.offset_table_free_list.popOrNull()) |i| {1110 if (self.offset_table_free_list.popOrNull()) |i| {
1111 log.debug("reusing offset table entry index {d} for {s}", .{ i, decl.name });
1126 decl.link.macho.offset_table_index = i;1112 decl.link.macho.offset_table_index = i;
1127 } else {1113 } else {
1114 log.debug("allocating offset table entry index {d} for {s}", .{ self.offset_table.items.len, decl.name });
1128 decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len);1115 decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len);
1129 _ = self.offset_table.addOneAssumeCapacity();1116 _ = self.offset_table.addOneAssumeCapacity();
1130 self.offset_table_count_dirty = true;1117 self.offset_table_count_dirty = true;
1118 self.rebase_info_dirty = true;
1131 }1119 }
11321120
1133 self.local_symbols.items[decl.link.macho.local_sym_index] = .{1121 self.locals.items[decl.link.macho.local_sym_index] = .{
1134 .n_strx = 0,1122 .n_strx = 0,
1135 .n_type = 0,1123 .n_type = 0,
1136 .n_sect = 0,1124 .n_sect = 0,
1137 .n_desc = 0,1125 .n_desc = 0,
1138 .n_value = 0,1126 .n_value = 0,
1139 };1127 };
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 };
1141}1133}
11421134
1143pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {1135pub 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 {...@@ -1180,8 +1172,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1180 .externally_managed => |x| x,1172 .externally_managed => |x| x,
1181 .appended => code_buffer.items,1173 .appended => code_buffer.items,
1182 .fail => |em| {1174 .fail => |em| {
1183 // Clear any PIE fixups and stub fixups for this decl.1175 // Clear any PIE fixups for this decl.
1184 self.pie_fixups.shrinkRetainingCapacity(0);1176 self.pie_fixups.shrinkRetainingCapacity(0);
1177 // Clear any stub fixups for this decl.
1185 self.stub_fixups.shrinkRetainingCapacity(0);1178 self.stub_fixups.shrinkRetainingCapacity(0);
1186 decl.analysis = .codegen_failure;1179 decl.analysis = .codegen_failure;
1187 try module.failed_decls.put(module.gpa, decl, em);1180 try module.failed_decls.put(module.gpa, decl, em);
...@@ -1191,7 +1184,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1191,7 +1184,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
11911184
1192 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);1185 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
1193 assert(decl.link.macho.local_sym_index != 0); // Caller forgot to call allocateDeclIndexes()1186 assert(decl.link.macho.local_sym_index != 0); // Caller forgot to call allocateDeclIndexes()
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
1196 if (decl.link.macho.size != 0) {1189 if (decl.link.macho.size != 0) {
1197 const capacity = decl.link.macho.capacity(self.*);1190 const capacity = decl.link.macho.capacity(self.*);
...@@ -1200,9 +1193,12 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1200,9 +1193,12 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1200 const vaddr = try self.growTextBlock(&decl.link.macho, code.len, required_alignment);1193 const vaddr = try self.growTextBlock(&decl.link.macho, code.len, required_alignment);
1201 log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr });1194 log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr });
1202 if (vaddr != symbol.n_value) {1195 if (vaddr != symbol.n_value) {
1203 symbol.n_value = vaddr;
1204 log.debug(" (writing new offset table entry)", .{});1196 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 };
1206 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);1202 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
1207 }1203 }
1208 } else if (code.len < decl.link.macho.size) {1204 } else if (code.len < decl.link.macho.size) {
...@@ -1231,7 +1227,11 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1231,7 +1227,11 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1231 .n_desc = 0,1227 .n_desc = 0,
1232 .n_value = addr,1228 .n_value = addr,
1233 };1229 };
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
1236 try self.writeLocalSymbol(decl.link.macho.local_sym_index);1236 try self.writeLocalSymbol(decl.link.macho.local_sym_index);
1237 if (self.d_sym) |*ds|1237 if (self.d_sym) |*ds|
...@@ -1239,30 +1239,48 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1239,30 +1239,48 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1239 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);1239 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
1240 }1240 }
12411241
1242 // Perform PIE fixups (if any)1242 // Calculate displacements to target addr (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.?];
1245 while (self.pie_fixups.popOrNull()) |fixup| {1243 while (self.pie_fixups.popOrNull()) |fixup| {
1246 const target_addr = fixup.address;1244 assert(fixup.size == 4);
1247 const this_addr = symbol.n_value + fixup.start;1245 const this_addr = symbol.n_value + fixup.offset;
1246 const target_addr = fixup.target_addr;
1247
1248 switch (self.base.options.target.cpu.arch) {1248 switch (self.base.options.target.cpu.arch) {
1249 .x86_64 => {1249 .x86_64 => {
1250 assert(target_addr >= this_addr + fixup.len);1250 const displacement = try math.cast(u32, target_addr - this_addr - 4);
1251 const displacement = try math.cast(u32, target_addr - this_addr - fixup.len);1251 mem.writeIntLittle(u32, code_buffer.items[fixup.offset..][0..4], displacement);
1252 var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)];
1253 mem.writeIntSliceLittle(u32, placeholder, displacement);
1254 },1252 },
1255 .aarch64 => {1253 .aarch64 => {
1256 assert(target_addr >= this_addr);1254 // TODO optimize instruction based on jump length (use ldr(literal) + nop if possible).
1257 const displacement = try math.cast(u27, target_addr - this_addr);1255 {
1258 var placeholder = code_buffer.items[fixup.start..][0..fixup.len];1256 const inst = code_buffer.items[fixup.offset..][0..4];
1259 mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.b(@as(i28, displacement)).toU32());1257 var parsed = mem.bytesAsValue(meta.TagPayload(
1258 aarch64.Instruction,
1259 aarch64.Instruction.PCRelativeAddress,
1260 ), inst);
1261 const this_page = @intCast(i32, this_addr >> 12);
1262 const target_page = @intCast(i32, target_addr >> 12);
1263 const pages = @bitCast(u21, @intCast(i21, target_page - this_page));
1264 parsed.immhi = @truncate(u19, pages >> 2);
1265 parsed.immlo = @truncate(u2, pages);
1266 }
1267 {
1268 const inst = code_buffer.items[fixup.offset + 4 ..][0..4];
1269 var parsed = mem.bytesAsValue(meta.TagPayload(
1270 aarch64.Instruction,
1271 aarch64.Instruction.LoadStoreRegister,
1272 ), inst);
1273 const narrowed = @truncate(u12, target_addr);
1274 const offset = try math.divExact(u12, narrowed, 8);
1275 parsed.offset = offset;
1276 }
1260 },1277 },
1261 else => unreachable, // unsupported target architecture1278 else => unreachable, // unsupported target architecture
1262 }1279 }
1263 }1280 }
12641281
1265 // Resolve stubs (if any)1282 // Resolve stubs (if any)
1283 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1266 const stubs = text_segment.sections.items[self.stubs_section_index.?];1284 const stubs = text_segment.sections.items[self.stubs_section_index.?];
1267 for (self.stub_fixups.items) |fixup| {1285 for (self.stub_fixups.items) |fixup| {
1268 const stub_addr = stubs.addr + fixup.symbol * stubs.reserved2;1286 const stub_addr = stubs.addr + fixup.symbol * stubs.reserved2;
...@@ -1287,9 +1305,6 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1287,9 +1305,6 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1287 try self.writeStubInStubHelper(fixup.symbol);1305 try self.writeStubInStubHelper(fixup.symbol);
1288 try self.writeLazySymbolPointer(fixup.symbol);1306 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);
1293 self.rebase_info_dirty = true;1308 self.rebase_info_dirty = true;
1294 self.lazy_binding_info_dirty = true;1309 self.lazy_binding_info_dirty = true;
1295 }1310 }
...@@ -1331,9 +1346,9 @@ pub fn updateDeclExports(...@@ -1331,9 +1346,9 @@ pub fn updateDeclExports(
1331 const tracy = trace(@src());1346 const tracy = trace(@src());
1332 defer tracy.end();1347 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);
1335 if (decl.link.macho.local_sym_index == 0) return;1350 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
1338 for (exports) |exp| {1353 for (exports) |exp| {
1339 if (exp.options.section) |section_name| {1354 if (exp.options.section) |section_name| {
...@@ -1366,7 +1381,7 @@ pub fn updateDeclExports(...@@ -1366,7 +1381,7 @@ pub fn updateDeclExports(
1366 };1381 };
1367 const n_type = decl_sym.n_type | macho.N_EXT;1382 const n_type = decl_sym.n_type | macho.N_EXT;
1368 if (exp.link.macho.sym_index) |i| {1383 if (exp.link.macho.sym_index) |i| {
1369 const sym = &self.global_symbols.items[i];1384 const sym = &self.globals.items[i];
1370 sym.* = .{1385 sym.* = .{
1371 .n_strx = try self.updateString(sym.n_strx, exp.options.name),1386 .n_strx = try self.updateString(sym.n_strx, exp.options.name),
1372 .n_type = n_type,1387 .n_type = n_type,
...@@ -1376,12 +1391,12 @@ pub fn updateDeclExports(...@@ -1376,12 +1391,12 @@ pub fn updateDeclExports(
1376 };1391 };
1377 } else {1392 } else {
1378 const name_str_index = try self.makeString(exp.options.name);1393 const name_str_index = try self.makeString(exp.options.name);
1379 const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {1394 const i = if (self.globals_free_list.popOrNull()) |i| i else blk: {
1380 _ = self.global_symbols.addOneAssumeCapacity();1395 _ = self.globals.addOneAssumeCapacity();
1381 self.export_info_dirty = true;1396 self.export_info_dirty = true;
1382 break :blk self.global_symbols.items.len - 1;1397 break :blk self.globals.items.len - 1;
1383 };1398 };
1384 self.global_symbols.items[i] = .{1399 self.globals.items[i] = .{
1385 .n_strx = name_str_index,1400 .n_strx = name_str_index,
1386 .n_type = n_type,1401 .n_type = n_type,
1387 .n_sect = @intCast(u8, self.text_section_index.?) + 1,1402 .n_sect = @intCast(u8, self.text_section_index.?) + 1,
...@@ -1396,18 +1411,18 @@ pub fn updateDeclExports(...@@ -1396,18 +1411,18 @@ pub fn updateDeclExports(
13961411
1397pub fn deleteExport(self: *MachO, exp: Export) void {1412pub fn deleteExport(self: *MachO, exp: Export) void {
1398 const sym_index = exp.sym_index orelse return;1413 const sym_index = exp.sym_index orelse return;
1399 self.global_symbol_free_list.append(self.base.allocator, sym_index) catch {};1414 self.globals_free_list.append(self.base.allocator, sym_index) catch {};
1400 self.global_symbols.items[sym_index].n_type = 0;1415 self.globals.items[sym_index].n_type = 0;
1401}1416}
14021417
1403pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {1418pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {
1404 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.1419 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
1405 self.freeTextBlock(&decl.link.macho);1420 self.freeTextBlock(&decl.link.macho);
1406 if (decl.link.macho.local_sym_index != 0) {1421 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 {};
1408 self.offset_table_free_list.append(self.base.allocator, decl.link.macho.offset_table_index) catch {};1423 self.offset_table_free_list.append(self.base.allocator, decl.link.macho.offset_table_index) catch {};
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
1412 decl.link.macho.local_sym_index = 0;1427 decl.link.macho.local_sym_index = 0;
1413 }1428 }
...@@ -1415,7 +1430,7 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {...@@ -1415,7 +1430,7 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {
14151430
1416pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {1431pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {
1417 assert(decl.link.macho.local_sym_index != 0);1432 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;
1419}1434}
14201435
1421pub fn populateMissingMetadata(self: *MachO) !void {1436pub fn populateMissingMetadata(self: *MachO) !void {
...@@ -1555,39 +1570,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1555,39 +1570,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1555 self.header_dirty = true;1570 self.header_dirty = true;
1556 self.load_commands_dirty = true;1571 self.load_commands_dirty = true;
1557 }1572 }
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 }
1591 if (self.stubs_section_index == null) {1573 if (self.stubs_section_index == null) {
1592 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;1574 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1593 self.stubs_section_index = @intCast(u16, text_segment.sections.items.len);1575 self.stubs_section_index = @intCast(u16, text_segment.sections.items.len);
...@@ -1599,7 +1581,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1599,7 +1581,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1599 };1581 };
1600 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {1582 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {
1601 .x86_64 => 6,1583 .x86_64 => 6,
1602 .aarch64 => 2 * @sizeOf(u32),1584 .aarch64 => 3 * @sizeOf(u32),
1603 else => unreachable, // unhandled architecture type1585 else => unreachable, // unhandled architecture type
1604 };1586 };
1605 const flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;1587 const flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
...@@ -1688,9 +1670,9 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1688,9 +1670,9 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1688 self.header_dirty = true;1670 self.header_dirty = true;
1689 self.load_commands_dirty = true;1671 self.load_commands_dirty = true;
1690 }1672 }
1691 if (self.data_got_section_index == null) {1673 if (self.got_section_index == null) {
1692 const dc_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;1674 const dc_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
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
1695 const flags = macho.S_NON_LAZY_SYMBOL_POINTERS;1677 const flags = macho.S_NON_LAZY_SYMBOL_POINTERS;
1696 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;1678 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
...@@ -2062,12 +2044,12 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -2062,12 +2044,12 @@ pub fn populateMissingMetadata(self: *MachO) !void {
2062 self.header_dirty = true;2044 self.header_dirty = true;
2063 self.load_commands_dirty = true;2045 self.load_commands_dirty = true;
2064 }2046 }
2065 if (!self.extern_nonlazy_symbols.contains("dyld_stub_binder")) {2047 if (!self.nonlazy_imports.contains("dyld_stub_binder")) {
2066 const index = @intCast(u32, self.extern_nonlazy_symbols.items().len);2048 const index = @intCast(u32, self.nonlazy_imports.items().len);
2067 const name = try self.base.allocator.dupe(u8, "dyld_stub_binder");2049 const name = try self.base.allocator.dupe(u8, "dyld_stub_binder");
2068 const offset = try self.makeString("dyld_stub_binder");2050 const offset = try self.makeString("dyld_stub_binder");
2069 try self.extern_nonlazy_symbols.putNoClobber(self.base.allocator, name, .{2051 try self.nonlazy_imports.putNoClobber(self.base.allocator, name, .{
2070 .inner = .{2052 .symbol = .{
2071 .n_strx = offset,2053 .n_strx = offset,
2072 .n_type = std.macho.N_UNDF | std.macho.N_EXT,2054 .n_type = std.macho.N_UNDF | std.macho.N_EXT,
2073 .n_sect = 0,2055 .n_sect = 0,
...@@ -2075,68 +2057,19 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -2075,68 +2057,19 @@ pub fn populateMissingMetadata(self: *MachO) !void {
2075 .n_value = 0,2057 .n_value = 0,
2076 },2058 },
2077 .dylib_ordinal = 1, // TODO this is currently hardcoded.2059 .dylib_ordinal = 1, // TODO this is currently hardcoded.
2078 .segment = self.data_const_segment_cmd_index.?,2060 .index = index,
2079 .offset = index * @sizeOf(u64),2061 });
2062 const off_index = @intCast(u32, self.offset_table.items.len);
2063 try self.offset_table.append(self.base.allocator, .{
2064 .kind = .Extern,
2065 .symbol = index,
2066 .index = off_index,
2080 });2067 });
2068 try self.writeOffsetTableEntry(off_index);
2081 self.binding_info_dirty = true;2069 self.binding_info_dirty = true;
2082 }2070 }
2083 if (self.stub_helper_stubs_start_off == null) {2071 if (self.stub_helper_stubs_start_off == null) {
2084 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;2072 try self.writeStubHelperPreamble();
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 }
2140 }2073 }
2141}2074}
21422075
...@@ -2161,7 +2094,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,...@@ -2161,7 +2094,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
2161 const big_block = self.text_block_free_list.items[i];2094 const big_block = self.text_block_free_list.items[i];
2162 // We now have a pointer to a live text block that has too much capacity.2095 // We now have a pointer to a live text block that has too much capacity.
2163 // Is it enough that we could fit this new text block?2096 // 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];
2165 const capacity = big_block.capacity(self.*);2098 const capacity = big_block.capacity(self.*);
2166 const ideal_capacity = padToIdeal(capacity);2099 const ideal_capacity = padToIdeal(capacity);
2167 const ideal_capacity_end_vaddr = sym.n_value + ideal_capacity;2100 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,...@@ -2192,7 +2125,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
2192 }2125 }
2193 break :blk new_start_vaddr;2126 break :blk new_start_vaddr;
2194 } else if (self.last_text_block) |last| {2127 } 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];
2196 // TODO We should pad out the excess capacity with NOPs. For executables,2129 // TODO We should pad out the excess capacity with NOPs. For executables,
2197 // no padding seems to be OK, but it will probably not be for objects.2130 // no padding seems to be OK, but it will probably not be for objects.
2198 const ideal_capacity = padToIdeal(last.size);2131 const ideal_capacity = padToIdeal(last.size);
...@@ -2290,12 +2223,12 @@ fn updateString(self: *MachO, old_str_off: u32, new_name: []const u8) !u32 {...@@ -2290,12 +2223,12 @@ fn updateString(self: *MachO, old_str_off: u32, new_name: []const u8) !u32 {
2290}2223}
22912224
2292pub fn addExternSymbol(self: *MachO, name: []const u8) !u32 {2225pub 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);
2294 const offset = try self.makeString(name);2227 const offset = try self.makeString(name);
2295 const sym_name = try self.base.allocator.dupe(u8, name);2228 const sym_name = try self.base.allocator.dupe(u8, name);
2296 const dylib_ordinal = 1; // TODO this is now hardcoded, since we only support libSystem.2229 const dylib_ordinal = 1; // TODO this is now hardcoded, since we only support libSystem.
2297 try self.extern_lazy_symbols.putNoClobber(self.base.allocator, sym_name, .{2230 try self.lazy_imports.putNoClobber(self.base.allocator, sym_name, .{
2298 .inner = .{2231 .symbol = .{
2299 .n_strx = offset,2232 .n_strx = offset,
2300 .n_type = macho.N_UNDF | macho.N_EXT,2233 .n_type = macho.N_UNDF | macho.N_EXT,
2301 .n_sect = 0,2234 .n_sect = 0,
...@@ -2303,6 +2236,7 @@ pub fn addExternSymbol(self: *MachO, name: []const u8) !u32 {...@@ -2303,6 +2236,7 @@ pub fn addExternSymbol(self: *MachO, name: []const u8) !u32 {
2303 .n_value = 0,2236 .n_value = 0,
2304 },2237 },
2305 .dylib_ordinal = dylib_ordinal,2238 .dylib_ordinal = dylib_ordinal,
2239 .index = index,
2306 });2240 });
2307 log.debug("adding new extern symbol '{s}' with dylib ordinal '{}'", .{ name, dylib_ordinal });2241 log.debug("adding new extern symbol '{s}' with dylib ordinal '{}'", .{ name, dylib_ordinal });
2308 return index;2242 return index;
...@@ -2461,41 +2395,29 @@ fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16, sta...@@ -2461,41 +2395,29 @@ fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16, sta
2461}2395}
24622396
2463fn writeOffsetTableEntry(self: *MachO, index: usize) !void {2397fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
2464 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;2398 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2465 const sect = &text_segment.sections.items[self.got_section_index.?];2399 const sect = &seg.sections.items[self.got_section_index.?];
2466 const off = sect.offset + @sizeOf(u64) * index;2400 const off = sect.offset + @sizeOf(u64) * index;
2467 const vmaddr = sect.addr + @sizeOf(u64) * index;
24682401
2469 if (self.offset_table_count_dirty) {2402 if (self.offset_table_count_dirty) {
2470 // TODO relocate.2403 // TODO relocate.
2471 self.offset_table_count_dirty = false;2404 self.offset_table_count_dirty = false;
2472 }2405 }
24732406
2474 var code: [8]u8 = undefined;2407 const got_entry = self.offset_table.items[index];
2475 switch (self.base.options.target.cpu.arch) {2408 const sym = blk: {
2476 .x86_64 => {2409 switch (got_entry.kind) {
2477 const pos_symbol_off = try math.cast(u31, vmaddr - self.offset_table.items[index] + 7);2410 .Local => {
2478 const symbol_off = @bitCast(u32, @as(i32, pos_symbol_off) * -1);2411 break :blk self.locals.items[got_entry.symbol];
2479 // lea %rax, [rip - disp]2412 },
2480 code[0] = 0x48;2413 .Extern => {
2481 code[1] = 0x8D;2414 break :blk self.nonlazy_imports.items()[got_entry.symbol].value.symbol;
2482 code[2] = 0x5;2415 },
2483 mem.writeIntLittle(u32, code[3..7], symbol_off);2416 }
2484 // ret2417 };
2485 code[7] = 0xC3;2418 const sym_name = self.getString(sym.n_strx);
2486 },2419 log.debug("writing offset table entry [ 0x{x} => 0x{x} ({s}) ]", .{ off, sym.n_value, sym_name });
2487 .aarch64 => {2420 try self.base.file.?.pwriteAll(mem.asBytes(&sym.n_value), off);
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);
2499}2421}
25002422
2501fn writeLazySymbolPointer(self: *MachO, index: u32) !void {2423fn writeLazySymbolPointer(self: *MachO, index: u32) !void {
...@@ -2518,6 +2440,133 @@ fn writeLazySymbolPointer(self: *MachO, index: u32) !void {...@@ -2518,6 +2440,133 @@ fn writeLazySymbolPointer(self: *MachO, index: u32) !void {
2518 try self.base.file.?.pwriteAll(&buf, off);2440 try self.base.file.?.pwriteAll(&buf, off);
2519}2441}
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
2521fn writeStub(self: *MachO, index: u32) !void {2570fn writeStub(self: *MachO, index: u32) !void {
2522 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;2571 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2523 const stubs = text_segment.sections.items[self.stubs_section_index.?];2572 const stubs = text_segment.sections.items[self.stubs_section_index.?];
...@@ -2527,9 +2576,12 @@ fn writeStub(self: *MachO, index: u32) !void {...@@ -2527,9 +2576,12 @@ fn writeStub(self: *MachO, index: u32) !void {
2527 const stub_off = stubs.offset + index * stubs.reserved2;2576 const stub_off = stubs.offset + index * stubs.reserved2;
2528 const stub_addr = stubs.addr + index * stubs.reserved2;2577 const stub_addr = stubs.addr + index * stubs.reserved2;
2529 const la_ptr_addr = la_symbol_ptr.addr + index * @sizeOf(u64);2578 const la_ptr_addr = la_symbol_ptr.addr + index * @sizeOf(u64);
2579
2530 log.debug("writing stub at 0x{x}", .{stub_off});2580 log.debug("writing stub at 0x{x}", .{stub_off});
2581
2531 var code = try self.base.allocator.alloc(u8, stubs.reserved2);2582 var code = try self.base.allocator.alloc(u8, stubs.reserved2);
2532 defer self.base.allocator.free(code);2583 defer self.base.allocator.free(code);
2584
2533 switch (self.base.options.target.cpu.arch) {2585 switch (self.base.options.target.cpu.arch) {
2534 .x86_64 => {2586 .x86_64 => {
2535 assert(la_ptr_addr >= stub_addr + stubs.reserved2);2587 assert(la_ptr_addr >= stub_addr + stubs.reserved2);
...@@ -2541,12 +2593,50 @@ fn writeStub(self: *MachO, index: u32) !void {...@@ -2541,12 +2593,50 @@ fn writeStub(self: *MachO, index: u32) !void {
2541 },2593 },
2542 .aarch64 => {2594 .aarch64 => {
2543 assert(la_ptr_addr >= stub_addr);2595 assert(la_ptr_addr >= stub_addr);
2544 const displacement = try math.divExact(u64, la_ptr_addr - stub_addr, 4);2596 outer: {
2545 const literal = try math.cast(u19, displacement);2597 const this_addr = stub_addr;
2546 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{2598 const target_addr = la_ptr_addr;
2547 .literal = literal,2599 inner: {
2548 }).toU32());2600 const displacement = math.divExact(u64, target_addr - this_addr, 4) catch |_| break :inner;
2549 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.br(.x16).toU32());2601 const literal = math.cast(u18, displacement) catch |_| break :inner;
2602 // ldr x16, literal
2603 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{
2604 .literal = literal,
2605 }).toU32());
2606 // nop
2607 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32());
2608 break :outer;
2609 }
2610 inner: {
2611 const new_this_addr = this_addr + @sizeOf(u32);
2612 const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch |_| break :inner;
2613 const literal = math.cast(u18, displacement) catch |_| break :inner;
2614 // nop
2615 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32());
2616 // ldr x16, literal
2617 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(.x16, .{
2618 .literal = literal,
2619 }).toU32());
2620 break :outer;
2621 }
2622 // Use adrp followed by ldr(register).
2623 const this_page = @intCast(i32, this_addr >> 12);
2624 const target_page = @intCast(i32, target_addr >> 12);
2625 const pages = @intCast(i21, target_page - this_page);
2626 // adrp x16, pages
2627 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x16, pages).toU32());
2628 const narrowed = @truncate(u12, target_addr);
2629 const offset = try math.divExact(u12, narrowed, 8);
2630 // ldr x16, x16, offset
2631 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(.x16, .{
2632 .register = .{
2633 .rn = .x16,
2634 .offset = aarch64.Instruction.LoadStoreOffset.imm(offset),
2635 },
2636 }).toU32());
2637 }
2638 // br x16
2639 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.br(.x16).toU32());
2550 },2640 },
2551 else => unreachable,2641 else => unreachable,
2552 }2642 }
...@@ -2563,8 +2653,10 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {...@@ -2563,8 +2653,10 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {
2563 else => unreachable,2653 else => unreachable,
2564 };2654 };
2565 const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size;2655 const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size;
2656
2566 var code = try self.base.allocator.alloc(u8, stub_size);2657 var code = try self.base.allocator.alloc(u8, stub_size);
2567 defer self.base.allocator.free(code);2658 defer self.base.allocator.free(code);
2659
2568 switch (self.base.options.target.cpu.arch) {2660 switch (self.base.options.target.cpu.arch) {
2569 .x86_64 => {2661 .x86_64 => {
2570 const displacement = try math.cast(2662 const displacement = try math.cast(
...@@ -2579,12 +2671,19 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {...@@ -2579,12 +2671,19 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {
2579 mem.writeIntLittle(u32, code[6..][0..4], @bitCast(u32, displacement));2671 mem.writeIntLittle(u32, code[6..][0..4], @bitCast(u32, displacement));
2580 },2672 },
2581 .aarch64 => {2673 .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
2583 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{2679 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{
2584 .literal = @divExact(stub_size - @sizeOf(u32), 4),2680 .literal = literal,
2585 }).toU32());2681 }).toU32());
2682 const displacement = try math.cast(i28, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4);
2683 // b disp
2586 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(displacement).toU32());2684 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);
2588 },2687 },
2589 else => unreachable,2688 else => unreachable,
2590 }2689 }
...@@ -2593,9 +2692,9 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {...@@ -2593,9 +2692,9 @@ fn writeStubInStubHelper(self: *MachO, index: u32) !void {
25932692
2594fn relocateSymbolTable(self: *MachO) !void {2693fn relocateSymbolTable(self: *MachO) !void {
2595 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;2694 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2596 const nlocals = self.local_symbols.items.len;2695 const nlocals = self.locals.items.len;
2597 const nglobals = self.global_symbols.items.len;2696 const nglobals = self.globals.items.len;
2598 const nundefs = self.extern_lazy_symbols.items().len + self.extern_nonlazy_symbols.items().len;2697 const nundefs = self.lazy_imports.items().len + self.nonlazy_imports.items().len;
2599 const nsyms = nlocals + nglobals + nundefs;2698 const nsyms = nlocals + nglobals + nundefs;
26002699
2601 if (symtab.nsyms < nsyms) {2700 if (symtab.nsyms < nsyms) {
...@@ -2630,7 +2729,7 @@ fn writeLocalSymbol(self: *MachO, index: usize) !void {...@@ -2630,7 +2729,7 @@ fn writeLocalSymbol(self: *MachO, index: usize) !void {
2630 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;2729 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2631 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;2730 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;
2632 log.debug("writing local symbol {} at 0x{x}", .{ index, off });2731 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);
2634}2733}
26352734
2636fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {2735fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
...@@ -2639,18 +2738,18 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {...@@ -2639,18 +2738,18 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
26392738
2640 try self.relocateSymbolTable();2739 try self.relocateSymbolTable();
2641 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;2740 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2642 const nlocals = self.local_symbols.items.len;2741 const nlocals = self.locals.items.len;
2643 const nglobals = self.global_symbols.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;
2646 var undefs = std.ArrayList(macho.nlist_64).init(self.base.allocator);2745 var undefs = std.ArrayList(macho.nlist_64).init(self.base.allocator);
2647 defer undefs.deinit();2746 defer undefs.deinit();
2648 try undefs.ensureCapacity(nundefs);2747 try undefs.ensureCapacity(nundefs);
2649 for (self.extern_lazy_symbols.items()) |entry| {2748 for (self.lazy_imports.items()) |entry| {
2650 undefs.appendAssumeCapacity(entry.value.inner);2749 undefs.appendAssumeCapacity(entry.value.symbol);
2651 }2750 }
2652 for (self.extern_nonlazy_symbols.items()) |entry| {2751 for (self.nonlazy_imports.items()) |entry| {
2653 undefs.appendAssumeCapacity(entry.value.inner);2752 undefs.appendAssumeCapacity(entry.value.symbol);
2654 }2753 }
26552754
2656 const locals_off = symtab.symoff;2755 const locals_off = symtab.symoff;
...@@ -2659,7 +2758,7 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {...@@ -2659,7 +2758,7 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
2659 const globals_off = locals_off + locals_size;2758 const globals_off = locals_off + locals_size;
2660 const globals_size = nglobals * @sizeOf(macho.nlist_64);2759 const globals_size = nglobals * @sizeOf(macho.nlist_64);
2661 log.debug("writing global symbols from 0x{x} to 0x{x}", .{ globals_off, globals_size + globals_off });2760 log.debug("writing global symbols from 0x{x} to 0x{x}", .{ globals_off, globals_size + globals_off });
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
2664 const undefs_off = globals_off + globals_size;2763 const undefs_off = globals_off + globals_size;
2665 const undefs_size = nundefs * @sizeOf(macho.nlist_64);2764 const undefs_size = nundefs * @sizeOf(macho.nlist_64);
...@@ -2685,15 +2784,15 @@ fn writeIndirectSymbolTable(self: *MachO) !void {...@@ -2685,15 +2784,15 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
2685 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;2784 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2686 const stubs = &text_segment.sections.items[self.stubs_section_index.?];2785 const stubs = &text_segment.sections.items[self.stubs_section_index.?];
2687 const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;2786 const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
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.?];
2689 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;2788 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2690 const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];2789 const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
2691 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;2790 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
26922791
2693 const lazy = self.extern_lazy_symbols.items();2792 const lazy = self.lazy_imports.items();
2694 const nonlazy = self.extern_nonlazy_symbols.items();2793 const got_entries = self.offset_table.items;
2695 const allocated_size = self.allocatedSizeLinkedit(dysymtab.indirectsymoff);2794 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);
2697 const needed_size = @intCast(u32, nindirectsyms * @sizeOf(u32));2796 const needed_size = @intCast(u32, nindirectsyms * @sizeOf(u32));
26982797
2699 if (needed_size > allocated_size) {2798 if (needed_size > allocated_size) {
...@@ -2712,20 +2811,27 @@ fn writeIndirectSymbolTable(self: *MachO) !void {...@@ -2712,20 +2811,27 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
2712 var writer = stream.writer();2811 var writer = stream.writer();
27132812
2714 stubs.reserved1 = 0;2813 stubs.reserved1 = 0;
2715 for (self.extern_lazy_symbols.items()) |_, i| {2814 for (lazy) |_, i| {
2716 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);2815 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
2717 try writer.writeIntLittle(u32, symtab_idx);2816 try writer.writeIntLittle(u32, symtab_idx);
2718 }2817 }
27192818
2720 const base_id = @intCast(u32, lazy.len);2819 const base_id = @intCast(u32, lazy.len);
2721 got.reserved1 = base_id;2820 got.reserved1 = base_id;
2722 for (self.extern_nonlazy_symbols.items()) |_, i| {2821 for (got_entries) |entry| {
2723 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i + base_id);2822 switch (entry.kind) {
2724 try writer.writeIntLittle(u32, symtab_idx);2823 .Local => {
2824 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
2825 },
2826 .Extern => {
2827 const symtab_idx = @intCast(u32, dysymtab.iundefsym + entry.index + base_id);
2828 try writer.writeIntLittle(u32, symtab_idx);
2829 },
2830 }
2725 }2831 }
27262832
2727 la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, nonlazy.len);2833 la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, got_entries.len);
2728 for (self.extern_lazy_symbols.items()) |_, i| {2834 for (lazy) |_, i| {
2729 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);2835 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
2730 try writer.writeIntLittle(u32, symtab_idx);2836 try writer.writeIntLittle(u32, symtab_idx);
2731 }2837 }
...@@ -2791,7 +2897,7 @@ fn writeCodeSignature(self: *MachO) !void {...@@ -2791,7 +2897,7 @@ fn writeCodeSignature(self: *MachO) !void {
27912897
2792fn writeExportTrie(self: *MachO) !void {2898fn writeExportTrie(self: *MachO) !void {
2793 if (!self.export_info_dirty) return;2899 if (!self.export_info_dirty) return;
2794 if (self.global_symbols.items.len == 0) return;2900 if (self.globals.items.len == 0) return;
27952901
2796 const tracy = trace(@src());2902 const tracy = trace(@src());
2797 defer tracy.end();2903 defer tracy.end();
...@@ -2800,7 +2906,7 @@ fn writeExportTrie(self: *MachO) !void {...@@ -2800,7 +2906,7 @@ fn writeExportTrie(self: *MachO) !void {
2800 defer trie.deinit();2906 defer trie.deinit();
28012907
2802 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;2908 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2803 for (self.global_symbols.items) |symbol| {2909 for (self.globals.items) |symbol| {
2804 // TODO figure out if we should put all global symbols into the export trie2910 // TODO figure out if we should put all global symbols into the export trie
2805 const name = self.getString(symbol.n_strx);2911 const name = self.getString(symbol.n_strx);
2806 assert(symbol.n_value >= text_segment.inner.vmaddr);2912 assert(symbol.n_value >= text_segment.inner.vmaddr);
...@@ -2842,14 +2948,48 @@ fn writeRebaseInfoTable(self: *MachO) !void {...@@ -2842,14 +2948,48 @@ fn writeRebaseInfoTable(self: *MachO) !void {
2842 const tracy = trace(@src());2948 const tracy = trace(@src());
2843 defer tracy.end();2949 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);
2846 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));2987 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
2847 defer self.base.allocator.free(buffer);2988 defer self.base.allocator.free(buffer);
28482989
2849 var stream = std.io.fixedBufferStream(buffer);2990 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;
2853 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;2993 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2854 const allocated_size = self.allocatedSizeLinkedit(dyld_info.rebase_off);2994 const allocated_size = self.allocatedSizeLinkedit(dyld_info.rebase_off);
2855 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));2995 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
...@@ -2874,14 +3014,34 @@ fn writeBindingInfoTable(self: *MachO) !void {...@@ -2874,14 +3014,34 @@ fn writeBindingInfoTable(self: *MachO) !void {
2874 const tracy = trace(@src());3014 const tracy = trace(@src());
2875 defer tracy.end();3015 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);
2878 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));3039 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
2879 defer self.base.allocator.free(buffer);3040 defer self.base.allocator.free(buffer);
28803041
2881 var stream = std.io.fixedBufferStream(buffer);3042 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;
2885 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;3045 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2886 const allocated_size = self.allocatedSizeLinkedit(dyld_info.bind_off);3046 const allocated_size = self.allocatedSizeLinkedit(dyld_info.bind_off);
2887 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));3047 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
...@@ -2903,14 +3063,36 @@ fn writeBindingInfoTable(self: *MachO) !void {...@@ -2903,14 +3063,36 @@ fn writeBindingInfoTable(self: *MachO) !void {
2903fn writeLazyBindingInfoTable(self: *MachO) !void {3063fn writeLazyBindingInfoTable(self: *MachO) !void {
2904 if (!self.lazy_binding_info_dirty) return;3064 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);
2907 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));3090 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
2908 defer self.base.allocator.free(buffer);3091 defer self.base.allocator.free(buffer);
29093092
2910 var stream = std.io.fixedBufferStream(buffer);3093 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;
2914 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;3096 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2915 const allocated_size = self.allocatedSizeLinkedit(dyld_info.lazy_bind_off);3097 const allocated_size = self.allocatedSizeLinkedit(dyld_info.lazy_bind_off);
2916 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));3098 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
...@@ -2931,7 +3113,7 @@ fn writeLazyBindingInfoTable(self: *MachO) !void {...@@ -2931,7 +3113,7 @@ fn writeLazyBindingInfoTable(self: *MachO) !void {
2931}3113}
29323114
2933fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {3115fn 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
2936 var stream = std.io.fixedBufferStream(buffer);3118 var stream = std.io.fixedBufferStream(buffer);
2937 var reader = stream.reader();3119 var reader = stream.reader();
...@@ -2977,7 +3159,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -2977,7 +3159,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
2977 else => {},3159 else => {},
2978 }3160 }
2979 }3161 }
2980 assert(self.extern_lazy_symbols.items().len <= offsets.items.len);3162 assert(self.lazy_imports.items().len <= offsets.items.len);
29813163
2982 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {3164 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {
2983 .x86_64 => 10,3165 .x86_64 => 10,
...@@ -2990,7 +3172,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -2990,7 +3172,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
2990 else => unreachable,3172 else => unreachable,
2991 };3173 };
2992 var buf: [@sizeOf(u32)]u8 = undefined;3174 var buf: [@sizeOf(u32)]u8 = undefined;
2993 for (self.extern_lazy_symbols.items()) |_, i| {3175 for (self.lazy_imports.items()) |_, i| {
2994 const placeholder_off = self.stub_helper_stubs_start_off.? + i * stub_size + off;3176 const placeholder_off = self.stub_helper_stubs_start_off.? + i * stub_size + off;
2995 mem.writeIntLittle(u32, &buf, offsets.items[i]);3177 mem.writeIntLittle(u32, &buf, offsets.items[i]);
2996 try self.base.file.?.pwriteAll(&buf, placeholder_off);3178 try self.base.file.?.pwriteAll(&buf, placeholder_off);
...@@ -3104,177 +3286,6 @@ fn writeHeader(self: *MachO) !void {...@@ -3104,177 +3286,6 @@ fn writeHeader(self: *MachO) !void {
3104 self.header_dirty = false;3286 self.header_dirty = false;
3105}3287}
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
3278pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {3289pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
3279 // TODO https://github.com/ziglang/zig/issues/12843290 // TODO https://github.com/ziglang/zig/issues/1284
3280 return std.math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch3291 return std.math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch
src/link/MachO/Archive.zig created+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...@@ -839,8 +839,8 @@ fn findFreeSpaceLinkedit(self: *DebugSymbols, object_size: u64, min_alignment: u
839839
840fn relocateSymbolTable(self: *DebugSymbols) !void {840fn relocateSymbolTable(self: *DebugSymbols) !void {
841 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;841 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
842 const nlocals = self.base.local_symbols.items.len;842 const nlocals = self.base.locals.items.len;
843 const nglobals = self.base.global_symbols.items.len;843 const nglobals = self.base.globals.items.len;
844 const nsyms = nlocals + nglobals;844 const nsyms = nlocals + nglobals;
845845
846 if (symtab.nsyms < nsyms) {846 if (symtab.nsyms < nsyms) {
...@@ -875,7 +875,7 @@ pub fn writeLocalSymbol(self: *DebugSymbols, index: usize) !void {...@@ -875,7 +875,7 @@ pub fn writeLocalSymbol(self: *DebugSymbols, index: usize) !void {
875 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;875 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
876 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;876 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;
877 log.debug("writing dSym local symbol {} at 0x{x}", .{ index, off });877 log.debug("writing dSym local symbol {} at 0x{x}", .{ index, off });
878 try self.file.pwriteAll(mem.asBytes(&self.base.local_symbols.items[index]), off);878 try self.file.pwriteAll(mem.asBytes(&self.base.locals.items[index]), off);
879}879}
880880
881fn writeStringTable(self: *DebugSymbols) !void {881fn writeStringTable(self: *DebugSymbols) !void {
...@@ -1057,7 +1057,7 @@ pub fn commitDeclDebugInfo(...@@ -1057,7 +1057,7 @@ pub fn commitDeclDebugInfo(
1057 var dbg_info_buffer = &debug_buffers.dbg_info_buffer;1057 var dbg_info_buffer = &debug_buffers.dbg_info_buffer;
1058 var dbg_info_type_relocs = &debug_buffers.dbg_info_type_relocs;1058 var dbg_info_type_relocs = &debug_buffers.dbg_info_type_relocs;
10591059
1060 const symbol = self.base.local_symbols.items[decl.link.macho.local_sym_index];1060 const symbol = self.base.locals.items[decl.link.macho.local_sym_index];
1061 const text_block = &decl.link.macho;1061 const text_block = &decl.link.macho;
1062 // If the Decl is a function, we need to update the __debug_line program.1062 // If the Decl is a function, we need to update the __debug_line program.
1063 const typed_value = decl.typed_value.most_recent.typed_value;1063 const typed_value = decl.typed_value.most_recent.typed_value;
src/link/MachO/Object.zig created+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(...@@ -557,7 +557,7 @@ fn buildOutputType(
557 var test_filter: ?[]const u8 = null;557 var test_filter: ?[]const u8 = null;
558 var test_name_prefix: ?[]const u8 = null;558 var test_name_prefix: ?[]const u8 = null;
559 var override_local_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LOCAL_CACHE_DIR");559 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;
561 var override_lib_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LIB_DIR");561 var override_lib_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LIB_DIR");
562 var main_pkg_path: ?[]const u8 = null;562 var main_pkg_path: ?[]const u8 = null;
563 var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no;563 var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no;
...@@ -841,7 +841,11 @@ fn buildOutputType(...@@ -841,7 +841,11 @@ fn buildOutputType(
841 } else if (mem.eql(u8, arg, "--debug-log")) {841 } else if (mem.eql(u8, arg, "--debug-log")) {
842 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});842 if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
843 i += 1;843 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 }
845 } else if (mem.eql(u8, arg, "-fcompiler-rt")) {849 } else if (mem.eql(u8, arg, "-fcompiler-rt")) {
846 want_compiler_rt = true;850 want_compiler_rt = true;
847 } else if (mem.eql(u8, arg, "-fno-compiler-rt")) {851 } else if (mem.eql(u8, arg, "-fno-compiler-rt")) {
...@@ -2633,6 +2637,50 @@ fn argvCmd(allocator: *Allocator, argv: []const []const u8) ![]u8 {...@@ -2633,6 +2637,50 @@ fn argvCmd(allocator: *Allocator, argv: []const []const u8) ![]u8 {
2633 return cmd.toOwnedSlice();2637 return cmd.toOwnedSlice();
2634}2638}
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
2636pub const usage_fmt =2684pub const usage_fmt =
2637 \\Usage: zig fmt [file]...2685 \\Usage: zig fmt [file]...
2638 \\2686 \\
...@@ -2704,9 +2752,10 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {...@@ -2704,9 +2752,10 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
2704 fatal("cannot use --stdin with positional arguments", .{});2752 fatal("cannot use --stdin with positional arguments", .{});
2705 }2753 }
27062754
2707 const stdin = io.getStdIn().reader();2755 const stdin = io.getStdIn();
27082756 const source_code = readSourceFileToEndAlloc(gpa, &stdin, null) catch |err| {
2709 const source_code = try stdin.readAllAlloc(gpa, max_src_size);2757 fatal("unable to read stdin: {s}", .{err});
2758 };
2710 defer gpa.free(source_code);2759 defer gpa.free(source_code);
27112760
2712 var tree = std.zig.parse(gpa, source_code) catch |err| {2761 var tree = std.zig.parse(gpa, source_code) catch |err| {
...@@ -2781,6 +2830,7 @@ const FmtError = error{...@@ -2781,6 +2830,7 @@ const FmtError = error{
2781 EndOfStream,2830 EndOfStream,
2782 Unseekable,2831 Unseekable,
2783 NotOpenForWriting,2832 NotOpenForWriting,
2833 UnsupportedEncoding,
2784} || fs.File.OpenError;2834} || fs.File.OpenError;
27852835
2786fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void {2836fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void {
...@@ -2846,21 +2896,15 @@ fn fmtPathFile(...@@ -2846,21 +2896,15 @@ fn fmtPathFile(
2846 if (stat.kind == .Directory)2896 if (stat.kind == .Directory)
2847 return error.IsDir;2897 return error.IsDir;
28482898
2849 const source_code = source_file.readToEndAllocOptions(2899 const source_code = try readSourceFileToEndAlloc(
2850 fmt.gpa,2900 fmt.gpa,
2851 max_src_size,2901 &source_file,
2852 std.math.cast(usize, stat.size) catch return error.FileTooBig,2902 std.math.cast(usize, stat.size) catch return error.FileTooBig,
2853 @alignOf(u8),2903 );
2854 null,2904 defer fmt.gpa.free(source_code);
2855 ) catch |err| switch (err) {2905
2856 error.ConnectionResetByPeer => unreachable,
2857 error.ConnectionTimedOut => unreachable,
2858 error.NotOpenForReading => unreachable,
2859 else => |e| return e,
2860 };
2861 source_file.close();2906 source_file.close();
2862 file_closed = true;2907 file_closed = true;
2863 defer fmt.gpa.free(source_code);
28642908
2865 // Add to set after no longer possible to get error.IsDir.2909 // Add to set after no longer possible to get error.IsDir.
2866 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;2910 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
...@@ -3237,7 +3281,8 @@ pub const ClangArgIterator = struct {...@@ -3237,7 +3281,8 @@ pub const ClangArgIterator = struct {
3237 self.zig_equivalent = clang_arg.zig_equivalent;3281 self.zig_equivalent = clang_arg.zig_equivalent;
3238 break :find_clang_arg;3282 break :find_clang_arg;
3239 },3283 },
3240 } else {3284 }
3285 else {
3241 fatal("Unknown Clang option: '{s}'", .{arg});3286 fatal("Unknown Clang option: '{s}'", .{arg});
3242 }3287 }
3243 }3288 }
src/stage1/all_types.hpp+21-8
...@@ -391,6 +391,8 @@ enum LazyValueId {...@@ -391,6 +391,8 @@ enum LazyValueId {
391 LazyValueIdAlignOf,391 LazyValueIdAlignOf,
392 LazyValueIdSizeOf,392 LazyValueIdSizeOf,
393 LazyValueIdPtrType,393 LazyValueIdPtrType,
394 LazyValueIdPtrTypeSimple,
395 LazyValueIdPtrTypeSimpleConst,
394 LazyValueIdOptType,396 LazyValueIdOptType,
395 LazyValueIdSliceType,397 LazyValueIdSliceType,
396 LazyValueIdFnType,398 LazyValueIdFnType,
...@@ -467,6 +469,13 @@ struct LazyValuePtrType {...@@ -467,6 +469,13 @@ struct LazyValuePtrType {
467 bool is_allowzero;469 bool is_allowzero;
468};470};
469471
472struct LazyValuePtrTypeSimple {
473 LazyValue base;
474
475 IrAnalyze *ira;
476 IrInstGen *elem_type;
477};
478
470struct LazyValueOptType {479struct LazyValueOptType {
471 LazyValue base;480 LazyValue base;
472481
...@@ -2130,10 +2139,6 @@ struct CodeGen {...@@ -2130,10 +2139,6 @@ struct CodeGen {
2130 Buf llvm_ir_file_output_path;2139 Buf llvm_ir_file_output_path;
2131 Buf analysis_json_output_path;2140 Buf analysis_json_output_path;
2132 Buf docs_output_path;2141 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
2138 Buf *builtin_zig_path;2143 Buf *builtin_zig_path;
2139 Buf *zig_std_special_dir; // Cannot be overridden; derived from zig_lib_dir.2144 Buf *zig_std_special_dir; // Cannot be overridden; derived from zig_lib_dir.
...@@ -2610,7 +2615,8 @@ enum IrInstSrcId {...@@ -2610,7 +2615,8 @@ enum IrInstSrcId {
2610 IrInstSrcIdEnumToInt,2615 IrInstSrcIdEnumToInt,
2611 IrInstSrcIdIntToErr,2616 IrInstSrcIdIntToErr,
2612 IrInstSrcIdErrToInt,2617 IrInstSrcIdErrToInt,
2613 IrInstSrcIdCheckSwitchProngs,2618 IrInstSrcIdCheckSwitchProngsUnderYes,
2619 IrInstSrcIdCheckSwitchProngsUnderNo,
2614 IrInstSrcIdCheckStatementIsVoid,2620 IrInstSrcIdCheckStatementIsVoid,
2615 IrInstSrcIdTypeName,2621 IrInstSrcIdTypeName,
2616 IrInstSrcIdDeclRef,2622 IrInstSrcIdDeclRef,
...@@ -2624,12 +2630,15 @@ enum IrInstSrcId {...@@ -2624,12 +2630,15 @@ enum IrInstSrcId {
2624 IrInstSrcIdHasField,2630 IrInstSrcIdHasField,
2625 IrInstSrcIdSetEvalBranchQuota,2631 IrInstSrcIdSetEvalBranchQuota,
2626 IrInstSrcIdPtrType,2632 IrInstSrcIdPtrType,
2633 IrInstSrcIdPtrTypeSimple,
2634 IrInstSrcIdPtrTypeSimpleConst,
2627 IrInstSrcIdAlignCast,2635 IrInstSrcIdAlignCast,
2628 IrInstSrcIdImplicitCast,2636 IrInstSrcIdImplicitCast,
2629 IrInstSrcIdResolveResult,2637 IrInstSrcIdResolveResult,
2630 IrInstSrcIdResetResult,2638 IrInstSrcIdResetResult,
2631 IrInstSrcIdSetAlignStack,2639 IrInstSrcIdSetAlignStack,
2632 IrInstSrcIdArgType,2640 IrInstSrcIdArgTypeAllowVarFalse,
2641 IrInstSrcIdArgTypeAllowVarTrue,
2633 IrInstSrcIdExport,2642 IrInstSrcIdExport,
2634 IrInstSrcIdExtern,2643 IrInstSrcIdExtern,
2635 IrInstSrcIdErrorReturnTrace,2644 IrInstSrcIdErrorReturnTrace,
...@@ -3294,6 +3303,12 @@ struct IrInstSrcArrayType {...@@ -3294,6 +3303,12 @@ struct IrInstSrcArrayType {
3294 IrInstSrc *child_type;3303 IrInstSrc *child_type;
3295};3304};
32963305
3306struct IrInstSrcPtrTypeSimple {
3307 IrInstSrc base;
3308
3309 IrInstSrc *child_type;
3310};
3311
3297struct IrInstSrcPtrType {3312struct IrInstSrcPtrType {
3298 IrInstSrc base;3313 IrInstSrc base;
32993314
...@@ -4020,7 +4035,6 @@ struct IrInstSrcCheckSwitchProngs {...@@ -4020,7 +4035,6 @@ struct IrInstSrcCheckSwitchProngs {
4020 IrInstSrcCheckSwitchProngsRange *ranges;4035 IrInstSrcCheckSwitchProngsRange *ranges;
4021 size_t range_count;4036 size_t range_count;
4022 AstNode* else_prong;4037 AstNode* else_prong;
4023 bool have_underscore_prong;
4024};4038};
40254039
4026struct IrInstSrcCheckStatementIsVoid {4040struct IrInstSrcCheckStatementIsVoid {
...@@ -4144,7 +4158,6 @@ struct IrInstSrcArgType {...@@ -4144,7 +4158,6 @@ struct IrInstSrcArgType {
41444158
4145 IrInstSrc *fn_type;4159 IrInstSrc *fn_type;
4146 IrInstSrc *arg_index;4160 IrInstSrc *arg_index;
4147 bool allow_var;
4148};4161};
41494162
4150struct IrInstSrcExport {4163struct IrInstSrcExport {
src/stage1/analyze.cpp+48-1
...@@ -1237,6 +1237,22 @@ Error type_val_resolve_zero_bits(CodeGen *g, ZigValue *type_val, ZigType *parent...@@ -1237,6 +1237,22 @@ Error type_val_resolve_zero_bits(CodeGen *g, ZigValue *type_val, ZigType *parent
1237 parent_type_val, is_zero_bits);1237 parent_type_val, is_zero_bits);
1238 }1238 }
1239 }1239 }
1240 case LazyValueIdPtrTypeSimple:
1241 case LazyValueIdPtrTypeSimpleConst: {
1242 LazyValuePtrTypeSimple *lazy_ptr_type = reinterpret_cast<LazyValuePtrTypeSimple *>(type_val->data.x_lazy);
1243
1244 if (parent_type_val == lazy_ptr_type->elem_type->value) {
1245 // Does a struct which contains a pointer field to itself have bits? Yes.
1246 *is_zero_bits = false;
1247 return ErrorNone;
1248 } else {
1249 if (parent_type_val == nullptr) {
1250 parent_type_val = type_val;
1251 }
1252 return type_val_resolve_zero_bits(g, lazy_ptr_type->elem_type->value, parent_type,
1253 parent_type_val, is_zero_bits);
1254 }
1255 }
1240 case LazyValueIdArrayType: {1256 case LazyValueIdArrayType: {
1241 LazyValueArrayType *lazy_array_type =1257 LazyValueArrayType *lazy_array_type =
1242 reinterpret_cast<LazyValueArrayType *>(type_val->data.x_lazy);1258 reinterpret_cast<LazyValueArrayType *>(type_val->data.x_lazy);
...@@ -1285,6 +1301,8 @@ Error type_val_resolve_is_opaque_type(CodeGen *g, ZigValue *type_val, bool *is_o...@@ -1285,6 +1301,8 @@ Error type_val_resolve_is_opaque_type(CodeGen *g, ZigValue *type_val, bool *is_o
1285 zig_unreachable();1301 zig_unreachable();
1286 case LazyValueIdSliceType:1302 case LazyValueIdSliceType:
1287 case LazyValueIdPtrType:1303 case LazyValueIdPtrType:
1304 case LazyValueIdPtrTypeSimple:
1305 case LazyValueIdPtrTypeSimpleConst:
1288 case LazyValueIdFnType:1306 case LazyValueIdFnType:
1289 case LazyValueIdOptType:1307 case LazyValueIdOptType:
1290 case LazyValueIdErrUnionType:1308 case LazyValueIdErrUnionType:
...@@ -1313,6 +1331,11 @@ static ReqCompTime type_val_resolve_requires_comptime(CodeGen *g, ZigValue *type...@@ -1313,6 +1331,11 @@ static ReqCompTime type_val_resolve_requires_comptime(CodeGen *g, ZigValue *type
1313 LazyValuePtrType *lazy_ptr_type = reinterpret_cast<LazyValuePtrType *>(type_val->data.x_lazy);1331 LazyValuePtrType *lazy_ptr_type = reinterpret_cast<LazyValuePtrType *>(type_val->data.x_lazy);
1314 return type_val_resolve_requires_comptime(g, lazy_ptr_type->elem_type->value);1332 return type_val_resolve_requires_comptime(g, lazy_ptr_type->elem_type->value);
1315 }1333 }
1334 case LazyValueIdPtrTypeSimple:
1335 case LazyValueIdPtrTypeSimpleConst: {
1336 LazyValuePtrTypeSimple *lazy_ptr_type = reinterpret_cast<LazyValuePtrTypeSimple *>(type_val->data.x_lazy);
1337 return type_val_resolve_requires_comptime(g, lazy_ptr_type->elem_type->value);
1338 }
1316 case LazyValueIdOptType: {1339 case LazyValueIdOptType: {
1317 LazyValueOptType *lazy_opt_type = reinterpret_cast<LazyValueOptType *>(type_val->data.x_lazy);1340 LazyValueOptType *lazy_opt_type = reinterpret_cast<LazyValueOptType *>(type_val->data.x_lazy);
1318 return type_val_resolve_requires_comptime(g, lazy_opt_type->payload_type->value);1341 return type_val_resolve_requires_comptime(g, lazy_opt_type->payload_type->value);
...@@ -1413,6 +1436,24 @@ start_over:...@@ -1413,6 +1436,24 @@ start_over:
1413 }1436 }
1414 return ErrorNone;1437 return ErrorNone;
1415 }1438 }
1439 case LazyValueIdPtrTypeSimple:
1440 case LazyValueIdPtrTypeSimpleConst: {
1441 LazyValuePtrTypeSimple *lazy_ptr_type = reinterpret_cast<LazyValuePtrTypeSimple *>(type_val->data.x_lazy);
1442 bool is_zero_bits;
1443 if ((err = type_val_resolve_zero_bits(g, lazy_ptr_type->elem_type->value, nullptr,
1444 nullptr, &is_zero_bits)))
1445 {
1446 return err;
1447 }
1448 if (is_zero_bits) {
1449 *abi_size = 0;
1450 *size_in_bits = 0;
1451 } else {
1452 *abi_size = g->builtin_types.entry_usize->abi_size;
1453 *size_in_bits = g->builtin_types.entry_usize->size_in_bits;
1454 }
1455 return ErrorNone;
1456 }
1416 case LazyValueIdFnType:1457 case LazyValueIdFnType:
1417 *abi_size = g->builtin_types.entry_usize->abi_size;1458 *abi_size = g->builtin_types.entry_usize->abi_size;
1418 *size_in_bits = g->builtin_types.entry_usize->size_in_bits;1459 *size_in_bits = g->builtin_types.entry_usize->size_in_bits;
...@@ -1449,6 +1490,8 @@ Error type_val_resolve_abi_align(CodeGen *g, AstNode *source_node, ZigValue *typ...@@ -1449,6 +1490,8 @@ Error type_val_resolve_abi_align(CodeGen *g, AstNode *source_node, ZigValue *typ
1449 zig_unreachable();1490 zig_unreachable();
1450 case LazyValueIdSliceType:1491 case LazyValueIdSliceType:
1451 case LazyValueIdPtrType:1492 case LazyValueIdPtrType:
1493 case LazyValueIdPtrTypeSimple:
1494 case LazyValueIdPtrTypeSimpleConst:
1452 case LazyValueIdFnType:1495 case LazyValueIdFnType:
1453 *abi_align = g->builtin_types.entry_usize->abi_align;1496 *abi_align = g->builtin_types.entry_usize->abi_align;
1454 return ErrorNone;1497 return ErrorNone;
...@@ -1506,7 +1549,9 @@ static OnePossibleValue type_val_resolve_has_one_possible_value(CodeGen *g, ZigV...@@ -1506,7 +1549,9 @@ static OnePossibleValue type_val_resolve_has_one_possible_value(CodeGen *g, ZigV
1506 return OnePossibleValueYes;1549 return OnePossibleValueYes;
1507 return type_val_resolve_has_one_possible_value(g, lazy_array_type->elem_type->value);1550 return type_val_resolve_has_one_possible_value(g, lazy_array_type->elem_type->value);
1508 }1551 }
1509 case LazyValueIdPtrType: {1552 case LazyValueIdPtrType:
1553 case LazyValueIdPtrTypeSimple:
1554 case LazyValueIdPtrTypeSimpleConst: {
1510 Error err;1555 Error err;
1511 bool zero_bits;1556 bool zero_bits;
1512 if ((err = type_val_resolve_zero_bits(g, type_val, nullptr, nullptr, &zero_bits))) {1557 if ((err = type_val_resolve_zero_bits(g, type_val, nullptr, nullptr, &zero_bits))) {
...@@ -5758,6 +5803,8 @@ static bool can_mutate_comptime_var_state(ZigValue *value) {...@@ -5758,6 +5803,8 @@ static bool can_mutate_comptime_var_state(ZigValue *value) {
5758 case LazyValueIdAlignOf:5803 case LazyValueIdAlignOf:
5759 case LazyValueIdSizeOf:5804 case LazyValueIdSizeOf:
5760 case LazyValueIdPtrType:5805 case LazyValueIdPtrType:
5806 case LazyValueIdPtrTypeSimple:
5807 case LazyValueIdPtrTypeSimpleConst:
5761 case LazyValueIdOptType:5808 case LazyValueIdOptType:
5762 case LazyValueIdSliceType:5809 case LazyValueIdSliceType:
5763 case LazyValueIdFnType:5810 case LazyValueIdFnType:
src/stage1/ir.cpp+140-26
...@@ -476,7 +476,8 @@ static void destroy_instruction_src(IrInstSrc *inst) {...@@ -476,7 +476,8 @@ static void destroy_instruction_src(IrInstSrc *inst) {
476 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcIntToErr *>(inst));476 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcIntToErr *>(inst));
477 case IrInstSrcIdErrToInt:477 case IrInstSrcIdErrToInt:
478 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcErrToInt *>(inst));478 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcErrToInt *>(inst));
479 case IrInstSrcIdCheckSwitchProngs:479 case IrInstSrcIdCheckSwitchProngsUnderNo:
480 case IrInstSrcIdCheckSwitchProngsUnderYes:
480 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCheckSwitchProngs *>(inst));481 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCheckSwitchProngs *>(inst));
481 case IrInstSrcIdCheckStatementIsVoid:482 case IrInstSrcIdCheckStatementIsVoid:
482 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCheckStatementIsVoid *>(inst));483 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCheckStatementIsVoid *>(inst));
...@@ -486,6 +487,9 @@ static void destroy_instruction_src(IrInstSrc *inst) {...@@ -486,6 +487,9 @@ static void destroy_instruction_src(IrInstSrc *inst) {
486 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTagName *>(inst));487 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTagName *>(inst));
487 case IrInstSrcIdPtrType:488 case IrInstSrcIdPtrType:
488 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPtrType *>(inst));489 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPtrType *>(inst));
490 case IrInstSrcIdPtrTypeSimple:
491 case IrInstSrcIdPtrTypeSimpleConst:
492 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPtrTypeSimple *>(inst));
489 case IrInstSrcIdDeclRef:493 case IrInstSrcIdDeclRef:
490 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcDeclRef *>(inst));494 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcDeclRef *>(inst));
491 case IrInstSrcIdPanic:495 case IrInstSrcIdPanic:
...@@ -514,7 +518,8 @@ static void destroy_instruction_src(IrInstSrc *inst) {...@@ -514,7 +518,8 @@ static void destroy_instruction_src(IrInstSrc *inst) {
514 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcResetResult *>(inst));518 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcResetResult *>(inst));
515 case IrInstSrcIdSetAlignStack:519 case IrInstSrcIdSetAlignStack:
516 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSetAlignStack *>(inst));520 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSetAlignStack *>(inst));
517 case IrInstSrcIdArgType:521 case IrInstSrcIdArgTypeAllowVarFalse:
522 case IrInstSrcIdArgTypeAllowVarTrue:
518 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcArgType *>(inst));523 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcArgType *>(inst));
519 case IrInstSrcIdExport:524 case IrInstSrcIdExport:
520 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcExport *>(inst));525 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcExport *>(inst));
...@@ -1470,10 +1475,6 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrToInt *) {...@@ -1470,10 +1475,6 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrToInt *) {
1470 return IrInstSrcIdErrToInt;1475 return IrInstSrcIdErrToInt;
1471}1476}
14721477
1473static constexpr IrInstSrcId ir_inst_id(IrInstSrcCheckSwitchProngs *) {
1474 return IrInstSrcIdCheckSwitchProngs;
1475}
1476
1477static constexpr IrInstSrcId ir_inst_id(IrInstSrcCheckStatementIsVoid *) {1478static constexpr IrInstSrcId ir_inst_id(IrInstSrcCheckStatementIsVoid *) {
1478 return IrInstSrcIdCheckStatementIsVoid;1479 return IrInstSrcIdCheckStatementIsVoid;
1479}1480}
...@@ -1546,10 +1547,6 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetAlignStack *) {...@@ -1546,10 +1547,6 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetAlignStack *) {
1546 return IrInstSrcIdSetAlignStack;1547 return IrInstSrcIdSetAlignStack;
1547}1548}
15481549
1549static constexpr IrInstSrcId ir_inst_id(IrInstSrcArgType *) {
1550 return IrInstSrcIdArgType;
1551}
1552
1553static constexpr IrInstSrcId ir_inst_id(IrInstSrcExport *) {1550static constexpr IrInstSrcId ir_inst_id(IrInstSrcExport *) {
1554 return IrInstSrcIdExport;1551 return IrInstSrcIdExport;
1555}1552}
...@@ -2615,11 +2612,35 @@ static IrInstGen *ir_build_br_gen(IrAnalyze *ira, IrInst *source_instr, IrBasicB...@@ -2615,11 +2612,35 @@ static IrInstGen *ir_build_br_gen(IrAnalyze *ira, IrInst *source_instr, IrBasicB
2615 return &inst->base;2612 return &inst->base;
2616}2613}
26172614
2615static IrInstSrc *ir_build_ptr_type_simple(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
2616 IrInstSrc *child_type, bool is_const)
2617{
2618 IrInstSrcPtrTypeSimple *inst = heap::c_allocator.create<IrInstSrcPtrTypeSimple>();
2619 inst->base.id = is_const ? IrInstSrcIdPtrTypeSimpleConst : IrInstSrcIdPtrTypeSimple;
2620 inst->base.base.scope = scope;
2621 inst->base.base.source_node = source_node;
2622 inst->base.base.debug_id = exec_next_debug_id(irb->exec);
2623 inst->base.owner_bb = irb->current_basic_block;
2624 ir_instruction_append(irb->current_basic_block, &inst->base);
2625
2626 inst->child_type = child_type;
2627
2628 ir_ref_instruction(child_type, irb->current_basic_block);
2629
2630 return &inst->base;
2631}
2632
2618static IrInstSrc *ir_build_ptr_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,2633static IrInstSrc *ir_build_ptr_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
2619 IrInstSrc *child_type, bool is_const, bool is_volatile, PtrLen ptr_len,2634 IrInstSrc *child_type, bool is_const, bool is_volatile, PtrLen ptr_len,
2620 IrInstSrc *sentinel, IrInstSrc *align_value,2635 IrInstSrc *sentinel, IrInstSrc *align_value,
2621 uint32_t bit_offset_start, uint32_t host_int_bytes, bool is_allow_zero)2636 uint32_t bit_offset_start, uint32_t host_int_bytes, bool is_allow_zero)
2622{2637{
2638 if (!is_volatile && ptr_len == PtrLenSingle && sentinel == nullptr && align_value == nullptr &&
2639 bit_offset_start == 0 && host_int_bytes == 0 && is_allow_zero == 0)
2640 {
2641 return ir_build_ptr_type_simple(irb, scope, source_node, child_type, is_const);
2642 }
2643
2623 IrInstSrcPtrType *inst = ir_build_instruction<IrInstSrcPtrType>(irb, scope, source_node);2644 IrInstSrcPtrType *inst = ir_build_instruction<IrInstSrcPtrType>(irb, scope, source_node);
2624 inst->sentinel = sentinel;2645 inst->sentinel = sentinel;
2625 inst->align_value = align_value;2646 inst->align_value = align_value;
...@@ -4354,13 +4375,19 @@ static IrInstSrc *ir_build_check_switch_prongs(IrBuilderSrc *irb, Scope *scope,...@@ -4354,13 +4375,19 @@ static IrInstSrc *ir_build_check_switch_prongs(IrBuilderSrc *irb, Scope *scope,
4354 IrInstSrc *target_value, IrInstSrcCheckSwitchProngsRange *ranges, size_t range_count,4375 IrInstSrc *target_value, IrInstSrcCheckSwitchProngsRange *ranges, size_t range_count,
4355 AstNode* else_prong, bool have_underscore_prong)4376 AstNode* else_prong, bool have_underscore_prong)
4356{4377{
4357 IrInstSrcCheckSwitchProngs *instruction = ir_build_instruction<IrInstSrcCheckSwitchProngs>(4378 IrInstSrcCheckSwitchProngs *instruction = heap::c_allocator.create<IrInstSrcCheckSwitchProngs>();
4358 irb, scope, source_node);4379 instruction->base.id = have_underscore_prong ?
4380 IrInstSrcIdCheckSwitchProngsUnderYes : IrInstSrcIdCheckSwitchProngsUnderNo;
4381 instruction->base.base.scope = scope;
4382 instruction->base.base.source_node = source_node;
4383 instruction->base.base.debug_id = exec_next_debug_id(irb->exec);
4384 instruction->base.owner_bb = irb->current_basic_block;
4385 ir_instruction_append(irb->current_basic_block, &instruction->base);
4386
4359 instruction->target_value = target_value;4387 instruction->target_value = target_value;
4360 instruction->ranges = ranges;4388 instruction->ranges = ranges;
4361 instruction->range_count = range_count;4389 instruction->range_count = range_count;
4362 instruction->else_prong = else_prong;4390 instruction->else_prong = else_prong;
4363 instruction->have_underscore_prong = have_underscore_prong;
43644391
4365 ir_ref_instruction(target_value, irb->current_basic_block);4392 ir_ref_instruction(target_value, irb->current_basic_block);
4366 for (size_t i = 0; i < range_count; i += 1) {4393 for (size_t i = 0; i < range_count; i += 1) {
...@@ -4590,10 +4617,17 @@ static IrInstSrc *ir_build_set_align_stack(IrBuilderSrc *irb, Scope *scope, AstN...@@ -4590,10 +4617,17 @@ static IrInstSrc *ir_build_set_align_stack(IrBuilderSrc *irb, Scope *scope, AstN
4590static IrInstSrc *ir_build_arg_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,4617static IrInstSrc *ir_build_arg_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
4591 IrInstSrc *fn_type, IrInstSrc *arg_index, bool allow_var)4618 IrInstSrc *fn_type, IrInstSrc *arg_index, bool allow_var)
4592{4619{
4593 IrInstSrcArgType *instruction = ir_build_instruction<IrInstSrcArgType>(irb, scope, source_node);4620 IrInstSrcArgType *instruction = heap::c_allocator.create<IrInstSrcArgType>();
4621 instruction->base.id = allow_var ?
4622 IrInstSrcIdArgTypeAllowVarTrue : IrInstSrcIdArgTypeAllowVarFalse;
4623 instruction->base.base.scope = scope;
4624 instruction->base.base.source_node = source_node;
4625 instruction->base.base.debug_id = exec_next_debug_id(irb->exec);
4626 instruction->base.owner_bb = irb->current_basic_block;
4627 ir_instruction_append(irb->current_basic_block, &instruction->base);
4628
4594 instruction->fn_type = fn_type;4629 instruction->fn_type = fn_type;
4595 instruction->arg_index = arg_index;4630 instruction->arg_index = arg_index;
4596 instruction->allow_var = allow_var;
45974631
4598 ir_ref_instruction(fn_type, irb->current_basic_block);4632 ir_ref_instruction(fn_type, irb->current_basic_block);
4599 ir_ref_instruction(arg_index, irb->current_basic_block);4633 ir_ref_instruction(arg_index, irb->current_basic_block);
...@@ -29702,7 +29736,7 @@ static IrInstGen *ir_analyze_instruction_test_comptime(IrAnalyze *ira, IrInstSrc...@@ -29702,7 +29736,7 @@ static IrInstGen *ir_analyze_instruction_test_comptime(IrAnalyze *ira, IrInstSrc
29702}29736}
2970329737
29704static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,29738static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
29705 IrInstSrcCheckSwitchProngs *instruction)29739 IrInstSrcCheckSwitchProngs *instruction, bool have_underscore_prong)
29706{29740{
29707 IrInstGen *target_value = instruction->target_value->child;29741 IrInstGen *target_value = instruction->target_value->child;
29708 ZigType *switch_type = target_value->value->type;29742 ZigType *switch_type = target_value->value->type;
...@@ -29767,7 +29801,7 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,...@@ -29767,7 +29801,7 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
29767 bigint_incr(&field_index);29801 bigint_incr(&field_index);
29768 }29802 }
29769 }29803 }
29770 if (instruction->have_underscore_prong) {29804 if (have_underscore_prong) {
29771 if (!switch_type->data.enumeration.non_exhaustive) {29805 if (!switch_type->data.enumeration.non_exhaustive) {
29772 ir_add_error(ira, &instruction->base.base,29806 ir_add_error(ira, &instruction->base.base,
29773 buf_sprintf("switch on exhaustive enum has `_` prong"));29807 buf_sprintf("switch on exhaustive enum has `_` prong"));
...@@ -30871,6 +30905,24 @@ static IrInstGen *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstSrcPtr...@@ -30871,6 +30905,24 @@ static IrInstGen *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstSrcPtr
30871 return ir_build_ptr_to_int_gen(ira, &instruction->base.base, target);30905 return ir_build_ptr_to_int_gen(ira, &instruction->base.base, target);
30872}30906}
3087330907
30908static IrInstGen *ir_analyze_instruction_ptr_type_simple(IrAnalyze *ira,
30909 IrInstSrcPtrTypeSimple *instruction, bool is_const)
30910{
30911 IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_type);
30912 result->value->special = ConstValSpecialLazy;
30913
30914 LazyValuePtrTypeSimple *lazy_ptr_type = heap::c_allocator.create<LazyValuePtrTypeSimple>();
30915 lazy_ptr_type->ira = ira; ira_ref(ira);
30916 result->value->data.x_lazy = &lazy_ptr_type->base;
30917 lazy_ptr_type->base.id = is_const ? LazyValueIdPtrTypeSimpleConst : LazyValueIdPtrTypeSimple;
30918
30919 lazy_ptr_type->elem_type = instruction->child_type->child;
30920 if (ir_resolve_type_lazy(ira, lazy_ptr_type->elem_type) == nullptr)
30921 return ira->codegen->invalid_inst_gen;
30922
30923 return result;
30924}
30925
30874static IrInstGen *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstSrcPtrType *instruction) {30926static IrInstGen *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstSrcPtrType *instruction) {
30875 IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_type);30927 IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_type);
30876 result->value->special = ConstValSpecialLazy;30928 result->value->special = ConstValSpecialLazy;
...@@ -30976,7 +31028,9 @@ static IrInstGen *ir_analyze_instruction_set_align_stack(IrAnalyze *ira, IrInstS...@@ -30976,7 +31028,9 @@ static IrInstGen *ir_analyze_instruction_set_align_stack(IrAnalyze *ira, IrInstS
30976 return ir_const_void(ira, &instruction->base.base);31028 return ir_const_void(ira, &instruction->base.base);
30977}31029}
3097831030
30979static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgType *instruction) {31031static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgType *instruction,
31032 bool allow_var)
31033{
30980 IrInstGen *fn_type_inst = instruction->fn_type->child;31034 IrInstGen *fn_type_inst = instruction->fn_type->child;
30981 ZigType *fn_type = ir_resolve_type(ira, fn_type_inst);31035 ZigType *fn_type = ir_resolve_type(ira, fn_type_inst);
30982 if (type_is_invalid(fn_type))31036 if (type_is_invalid(fn_type))
...@@ -30998,7 +31052,7 @@ static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgTy...@@ -30998,7 +31052,7 @@ static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgTy
3099831052
30999 FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;31053 FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
31000 if (arg_index >= fn_type_id->param_count) {31054 if (arg_index >= fn_type_id->param_count) {
31001 if (instruction->allow_var) {31055 if (allow_var) {
31002 // TODO remove this with var args31056 // TODO remove this with var args
31003 return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_anytype);31057 return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_anytype);
31004 }31058 }
...@@ -31013,7 +31067,7 @@ static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgTy...@@ -31013,7 +31067,7 @@ static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgTy
31013 // Args are only unresolved if our function is generic.31067 // Args are only unresolved if our function is generic.
31014 ir_assert(fn_type->data.fn.is_generic, &instruction->base.base);31068 ir_assert(fn_type->data.fn.is_generic, &instruction->base.base);
3101531069
31016 if (instruction->allow_var) {31070 if (allow_var) {
31017 return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_anytype);31071 return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_anytype);
31018 } else {31072 } else {
31019 ir_add_error(ira, &arg_index_inst->base,31073 ir_add_error(ira, &arg_index_inst->base,
...@@ -32341,8 +32395,10 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc...@@ -32341,8 +32395,10 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc
32341 return ir_analyze_instruction_fn_proto(ira, (IrInstSrcFnProto *)instruction);32395 return ir_analyze_instruction_fn_proto(ira, (IrInstSrcFnProto *)instruction);
32342 case IrInstSrcIdTestComptime:32396 case IrInstSrcIdTestComptime:
32343 return ir_analyze_instruction_test_comptime(ira, (IrInstSrcTestComptime *)instruction);32397 return ir_analyze_instruction_test_comptime(ira, (IrInstSrcTestComptime *)instruction);
32344 case IrInstSrcIdCheckSwitchProngs:32398 case IrInstSrcIdCheckSwitchProngsUnderNo:
32345 return ir_analyze_instruction_check_switch_prongs(ira, (IrInstSrcCheckSwitchProngs *)instruction);32399 return ir_analyze_instruction_check_switch_prongs(ira, (IrInstSrcCheckSwitchProngs *)instruction, false);
32400 case IrInstSrcIdCheckSwitchProngsUnderYes:
32401 return ir_analyze_instruction_check_switch_prongs(ira, (IrInstSrcCheckSwitchProngs *)instruction, true);
32346 case IrInstSrcIdCheckStatementIsVoid:32402 case IrInstSrcIdCheckStatementIsVoid:
32347 return ir_analyze_instruction_check_statement_is_void(ira, (IrInstSrcCheckStatementIsVoid *)instruction);32403 return ir_analyze_instruction_check_statement_is_void(ira, (IrInstSrcCheckStatementIsVoid *)instruction);
32348 case IrInstSrcIdDeclRef:32404 case IrInstSrcIdDeclRef:
...@@ -32373,6 +32429,10 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc...@@ -32373,6 +32429,10 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc
32373 return ir_analyze_instruction_set_eval_branch_quota(ira, (IrInstSrcSetEvalBranchQuota *)instruction);32429 return ir_analyze_instruction_set_eval_branch_quota(ira, (IrInstSrcSetEvalBranchQuota *)instruction);
32374 case IrInstSrcIdPtrType:32430 case IrInstSrcIdPtrType:
32375 return ir_analyze_instruction_ptr_type(ira, (IrInstSrcPtrType *)instruction);32431 return ir_analyze_instruction_ptr_type(ira, (IrInstSrcPtrType *)instruction);
32432 case IrInstSrcIdPtrTypeSimple:
32433 return ir_analyze_instruction_ptr_type_simple(ira, (IrInstSrcPtrTypeSimple *)instruction, false);
32434 case IrInstSrcIdPtrTypeSimpleConst:
32435 return ir_analyze_instruction_ptr_type_simple(ira, (IrInstSrcPtrTypeSimple *)instruction, true);
32376 case IrInstSrcIdAlignCast:32436 case IrInstSrcIdAlignCast:
32377 return ir_analyze_instruction_align_cast(ira, (IrInstSrcAlignCast *)instruction);32437 return ir_analyze_instruction_align_cast(ira, (IrInstSrcAlignCast *)instruction);
32378 case IrInstSrcIdImplicitCast:32438 case IrInstSrcIdImplicitCast:
...@@ -32383,8 +32443,10 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc...@@ -32383,8 +32443,10 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc
32383 return ir_analyze_instruction_reset_result(ira, (IrInstSrcResetResult *)instruction);32443 return ir_analyze_instruction_reset_result(ira, (IrInstSrcResetResult *)instruction);
32384 case IrInstSrcIdSetAlignStack:32444 case IrInstSrcIdSetAlignStack:
32385 return ir_analyze_instruction_set_align_stack(ira, (IrInstSrcSetAlignStack *)instruction);32445 return ir_analyze_instruction_set_align_stack(ira, (IrInstSrcSetAlignStack *)instruction);
32386 case IrInstSrcIdArgType:32446 case IrInstSrcIdArgTypeAllowVarFalse:
32387 return ir_analyze_instruction_arg_type(ira, (IrInstSrcArgType *)instruction);32447 return ir_analyze_instruction_arg_type(ira, (IrInstSrcArgType *)instruction, false);
32448 case IrInstSrcIdArgTypeAllowVarTrue:
32449 return ir_analyze_instruction_arg_type(ira, (IrInstSrcArgType *)instruction, true);
32388 case IrInstSrcIdExport:32450 case IrInstSrcIdExport:
32389 return ir_analyze_instruction_export(ira, (IrInstSrcExport *)instruction);32451 return ir_analyze_instruction_export(ira, (IrInstSrcExport *)instruction);
32390 case IrInstSrcIdExtern:32452 case IrInstSrcIdExtern:
...@@ -32737,12 +32799,15 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) {...@@ -32737,12 +32799,15 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) {
32737 case IrInstSrcIdMemcpy:32799 case IrInstSrcIdMemcpy:
32738 case IrInstSrcIdBreakpoint:32800 case IrInstSrcIdBreakpoint:
32739 case IrInstSrcIdOverflowOp: // TODO when we support multiple returns this can be side effect free32801 case IrInstSrcIdOverflowOp: // TODO when we support multiple returns this can be side effect free
32740 case IrInstSrcIdCheckSwitchProngs:32802 case IrInstSrcIdCheckSwitchProngsUnderNo:
32803 case IrInstSrcIdCheckSwitchProngsUnderYes:
32741 case IrInstSrcIdCheckStatementIsVoid:32804 case IrInstSrcIdCheckStatementIsVoid:
32742 case IrInstSrcIdCheckRuntimeScope:32805 case IrInstSrcIdCheckRuntimeScope:
32743 case IrInstSrcIdPanic:32806 case IrInstSrcIdPanic:
32744 case IrInstSrcIdSetEvalBranchQuota:32807 case IrInstSrcIdSetEvalBranchQuota:
32745 case IrInstSrcIdPtrType:32808 case IrInstSrcIdPtrType:
32809 case IrInstSrcIdPtrTypeSimple:
32810 case IrInstSrcIdPtrTypeSimpleConst:
32746 case IrInstSrcIdSetAlignStack:32811 case IrInstSrcIdSetAlignStack:
32747 case IrInstSrcIdExport:32812 case IrInstSrcIdExport:
32748 case IrInstSrcIdExtern:32813 case IrInstSrcIdExtern:
...@@ -32826,7 +32891,8 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) {...@@ -32826,7 +32891,8 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) {
32826 case IrInstSrcIdAlignCast:32891 case IrInstSrcIdAlignCast:
32827 case IrInstSrcIdImplicitCast:32892 case IrInstSrcIdImplicitCast:
32828 case IrInstSrcIdResolveResult:32893 case IrInstSrcIdResolveResult:
32829 case IrInstSrcIdArgType:32894 case IrInstSrcIdArgTypeAllowVarFalse:
32895 case IrInstSrcIdArgTypeAllowVarTrue:
32830 case IrInstSrcIdErrorReturnTrace:32896 case IrInstSrcIdErrorReturnTrace:
32831 case IrInstSrcIdErrorUnion:32897 case IrInstSrcIdErrorUnion:
32832 case IrInstSrcIdFloatOp:32898 case IrInstSrcIdFloatOp:
...@@ -33249,6 +33315,54 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) {...@@ -33249,6 +33315,54 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) {
33249 // We can't free the lazy value here, because multiple other ZigValues might be pointing to it.33315 // We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
33250 return ErrorNone;33316 return ErrorNone;
33251 }33317 }
33318 case LazyValueIdPtrTypeSimple: {
33319 LazyValuePtrTypeSimple *lazy_ptr_type = reinterpret_cast<LazyValuePtrTypeSimple *>(val->data.x_lazy);
33320 IrAnalyze *ira = lazy_ptr_type->ira;
33321
33322 ZigType *elem_type = ir_resolve_type(ira, lazy_ptr_type->elem_type);
33323 if (type_is_invalid(elem_type))
33324 return ErrorSemanticAnalyzeFail;
33325
33326 if (elem_type->id == ZigTypeIdUnreachable) {
33327 ir_add_error(ira, &lazy_ptr_type->elem_type->base,
33328 buf_create_from_str("pointer to noreturn not allowed"));
33329 return ErrorSemanticAnalyzeFail;
33330 }
33331
33332 assert(val->type->id == ZigTypeIdMetaType);
33333 val->data.x_type = get_pointer_to_type_extra2(ira->codegen, elem_type,
33334 false, false, PtrLenSingle, 0,
33335 0, 0,
33336 false, VECTOR_INDEX_NONE, nullptr, nullptr);
33337 val->special = ConstValSpecialStatic;
33338
33339 // We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
33340 return ErrorNone;
33341 }
33342 case LazyValueIdPtrTypeSimpleConst: {
33343 LazyValuePtrTypeSimple *lazy_ptr_type = reinterpret_cast<LazyValuePtrTypeSimple *>(val->data.x_lazy);
33344 IrAnalyze *ira = lazy_ptr_type->ira;
33345
33346 ZigType *elem_type = ir_resolve_type(ira, lazy_ptr_type->elem_type);
33347 if (type_is_invalid(elem_type))
33348 return ErrorSemanticAnalyzeFail;
33349
33350 if (elem_type->id == ZigTypeIdUnreachable) {
33351 ir_add_error(ira, &lazy_ptr_type->elem_type->base,
33352 buf_create_from_str("pointer to noreturn not allowed"));
33353 return ErrorSemanticAnalyzeFail;
33354 }
33355
33356 assert(val->type->id == ZigTypeIdMetaType);
33357 val->data.x_type = get_pointer_to_type_extra2(ira->codegen, elem_type,
33358 true, false, PtrLenSingle, 0,
33359 0, 0,
33360 false, VECTOR_INDEX_NONE, nullptr, nullptr);
33361 val->special = ConstValSpecialStatic;
33362
33363 // We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
33364 return ErrorNone;
33365 }
33252 case LazyValueIdArrayType: {33366 case LazyValueIdArrayType: {
33253 LazyValueArrayType *lazy_array_type = reinterpret_cast<LazyValueArrayType *>(val->data.x_lazy);33367 LazyValueArrayType *lazy_array_type = reinterpret_cast<LazyValueArrayType *>(val->data.x_lazy);
33254 IrAnalyze *ira = lazy_array_type->ira;33368 IrAnalyze *ira = lazy_array_type->ira;
src/stage1/ir_print.cpp+49-10
...@@ -270,8 +270,10 @@ const char* ir_inst_src_type_str(IrInstSrcId id) {...@@ -270,8 +270,10 @@ const char* ir_inst_src_type_str(IrInstSrcId id) {
270 return "SrcIntToErr";270 return "SrcIntToErr";
271 case IrInstSrcIdErrToInt:271 case IrInstSrcIdErrToInt:
272 return "SrcErrToInt";272 return "SrcErrToInt";
273 case IrInstSrcIdCheckSwitchProngs:273 case IrInstSrcIdCheckSwitchProngsUnderNo:
274 return "SrcCheckSwitchProngs";274 return "SrcCheckSwitchProngsUnderNo";
275 case IrInstSrcIdCheckSwitchProngsUnderYes:
276 return "SrcCheckSwitchProngsUnderYes";
275 case IrInstSrcIdCheckStatementIsVoid:277 case IrInstSrcIdCheckStatementIsVoid:
276 return "SrcCheckStatementIsVoid";278 return "SrcCheckStatementIsVoid";
277 case IrInstSrcIdTypeName:279 case IrInstSrcIdTypeName:
...@@ -298,6 +300,10 @@ const char* ir_inst_src_type_str(IrInstSrcId id) {...@@ -298,6 +300,10 @@ const char* ir_inst_src_type_str(IrInstSrcId id) {
298 return "SrcSetEvalBranchQuota";300 return "SrcSetEvalBranchQuota";
299 case IrInstSrcIdPtrType:301 case IrInstSrcIdPtrType:
300 return "SrcPtrType";302 return "SrcPtrType";
303 case IrInstSrcIdPtrTypeSimple:
304 return "SrcPtrTypeSimple";
305 case IrInstSrcIdPtrTypeSimpleConst:
306 return "SrcPtrTypeSimpleConst";
301 case IrInstSrcIdAlignCast:307 case IrInstSrcIdAlignCast:
302 return "SrcAlignCast";308 return "SrcAlignCast";
303 case IrInstSrcIdImplicitCast:309 case IrInstSrcIdImplicitCast:
...@@ -308,8 +314,10 @@ const char* ir_inst_src_type_str(IrInstSrcId id) {...@@ -308,8 +314,10 @@ const char* ir_inst_src_type_str(IrInstSrcId id) {
308 return "SrcResetResult";314 return "SrcResetResult";
309 case IrInstSrcIdSetAlignStack:315 case IrInstSrcIdSetAlignStack:
310 return "SrcSetAlignStack";316 return "SrcSetAlignStack";
311 case IrInstSrcIdArgType:317 case IrInstSrcIdArgTypeAllowVarFalse:
312 return "SrcArgType";318 return "SrcArgTypeAllowVarFalse";
319 case IrInstSrcIdArgTypeAllowVarTrue:
320 return "SrcArgTypeAllowVarTrue";
313 case IrInstSrcIdExport:321 case IrInstSrcIdExport:
314 return "SrcExport";322 return "SrcExport";
315 case IrInstSrcIdExtern:323 case IrInstSrcIdExtern:
...@@ -2187,7 +2195,9 @@ static void ir_print_err_to_int(IrPrintGen *irp, IrInstGenErrToInt *instruction)...@@ -2187,7 +2195,9 @@ static void ir_print_err_to_int(IrPrintGen *irp, IrInstGenErrToInt *instruction)
2187 ir_print_other_inst_gen(irp, instruction->target);2195 ir_print_other_inst_gen(irp, instruction->target);
2188}2196}
21892197
2190static void ir_print_check_switch_prongs(IrPrintSrc *irp, IrInstSrcCheckSwitchProngs *instruction) {2198static void ir_print_check_switch_prongs(IrPrintSrc *irp, IrInstSrcCheckSwitchProngs *instruction,
2199 bool have_underscore_prong)
2200{
2191 fprintf(irp->f, "@checkSwitchProngs(");2201 fprintf(irp->f, "@checkSwitchProngs(");
2192 ir_print_other_inst_src(irp, instruction->target_value);2202 ir_print_other_inst_src(irp, instruction->target_value);
2193 fprintf(irp->f, ",");2203 fprintf(irp->f, ",");
...@@ -2200,6 +2210,8 @@ static void ir_print_check_switch_prongs(IrPrintSrc *irp, IrInstSrcCheckSwitchPr...@@ -2200,6 +2210,8 @@ static void ir_print_check_switch_prongs(IrPrintSrc *irp, IrInstSrcCheckSwitchPr
2200 }2210 }
2201 const char *have_else_str = instruction->else_prong != nullptr ? "yes" : "no";2211 const char *have_else_str = instruction->else_prong != nullptr ? "yes" : "no";
2202 fprintf(irp->f, ")else:%s", have_else_str);2212 fprintf(irp->f, ")else:%s", have_else_str);
2213 const char *have_under_str = have_underscore_prong ? "yes" : "no";
2214 fprintf(irp->f, " _:%s", have_under_str);
2203}2215}
22042216
2205static void ir_print_check_statement_is_void(IrPrintSrc *irp, IrInstSrcCheckStatementIsVoid *instruction) {2217static void ir_print_check_statement_is_void(IrPrintSrc *irp, IrInstSrcCheckStatementIsVoid *instruction) {
...@@ -2237,6 +2249,15 @@ static void ir_print_ptr_type(IrPrintSrc *irp, IrInstSrcPtrType *instruction) {...@@ -2237,6 +2249,15 @@ static void ir_print_ptr_type(IrPrintSrc *irp, IrInstSrcPtrType *instruction) {
2237 ir_print_other_inst_src(irp, instruction->child_type);2249 ir_print_other_inst_src(irp, instruction->child_type);
2238}2250}
22392251
2252static void ir_print_ptr_type_simple(IrPrintSrc *irp, IrInstSrcPtrTypeSimple *instruction,
2253 bool is_const)
2254{
2255 fprintf(irp->f, "&");
2256 const char *const_str = is_const ? "const " : "";
2257 fprintf(irp->f, "*%s", const_str);
2258 ir_print_other_inst_src(irp, instruction->child_type);
2259}
2260
2240static void ir_print_decl_ref(IrPrintSrc *irp, IrInstSrcDeclRef *instruction) {2261static void ir_print_decl_ref(IrPrintSrc *irp, IrInstSrcDeclRef *instruction) {
2241 const char *ptr_str = (instruction->lval != LValNone) ? "ptr " : "";2262 const char *ptr_str = (instruction->lval != LValNone) ? "ptr " : "";
2242 fprintf(irp->f, "declref %s%s", ptr_str, buf_ptr(instruction->tld->name));2263 fprintf(irp->f, "declref %s%s", ptr_str, buf_ptr(instruction->tld->name));
...@@ -2344,11 +2365,17 @@ static void ir_print_set_align_stack(IrPrintSrc *irp, IrInstSrcSetAlignStack *in...@@ -2344,11 +2365,17 @@ static void ir_print_set_align_stack(IrPrintSrc *irp, IrInstSrcSetAlignStack *in
2344 fprintf(irp->f, ")");2365 fprintf(irp->f, ")");
2345}2366}
23462367
2347static void ir_print_arg_type(IrPrintSrc *irp, IrInstSrcArgType *instruction) {2368static void ir_print_arg_type(IrPrintSrc *irp, IrInstSrcArgType *instruction, bool allow_var) {
2348 fprintf(irp->f, "@ArgType(");2369 fprintf(irp->f, "@ArgType(");
2349 ir_print_other_inst_src(irp, instruction->fn_type);2370 ir_print_other_inst_src(irp, instruction->fn_type);
2350 fprintf(irp->f, ",");2371 fprintf(irp->f, ",");
2351 ir_print_other_inst_src(irp, instruction->arg_index);2372 ir_print_other_inst_src(irp, instruction->arg_index);
2373 fprintf(irp->f, ",");
2374 if (allow_var) {
2375 fprintf(irp->f, "allow_var=true");
2376 } else {
2377 fprintf(irp->f, "allow_var=false");
2378 }
2352 fprintf(irp->f, ")");2379 fprintf(irp->f, ")");
2353}2380}
23542381
...@@ -2885,8 +2912,11 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai...@@ -2885,8 +2912,11 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai
2885 case IrInstSrcIdErrToInt:2912 case IrInstSrcIdErrToInt:
2886 ir_print_err_to_int(irp, (IrInstSrcErrToInt *)instruction);2913 ir_print_err_to_int(irp, (IrInstSrcErrToInt *)instruction);
2887 break;2914 break;
2888 case IrInstSrcIdCheckSwitchProngs:2915 case IrInstSrcIdCheckSwitchProngsUnderNo:
2889 ir_print_check_switch_prongs(irp, (IrInstSrcCheckSwitchProngs *)instruction);2916 ir_print_check_switch_prongs(irp, (IrInstSrcCheckSwitchProngs *)instruction, false);
2917 break;
2918 case IrInstSrcIdCheckSwitchProngsUnderYes:
2919 ir_print_check_switch_prongs(irp, (IrInstSrcCheckSwitchProngs *)instruction, true);
2890 break;2920 break;
2891 case IrInstSrcIdCheckStatementIsVoid:2921 case IrInstSrcIdCheckStatementIsVoid:
2892 ir_print_check_statement_is_void(irp, (IrInstSrcCheckStatementIsVoid *)instruction);2922 ir_print_check_statement_is_void(irp, (IrInstSrcCheckStatementIsVoid *)instruction);
...@@ -2900,6 +2930,12 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai...@@ -2900,6 +2930,12 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai
2900 case IrInstSrcIdPtrType:2930 case IrInstSrcIdPtrType:
2901 ir_print_ptr_type(irp, (IrInstSrcPtrType *)instruction);2931 ir_print_ptr_type(irp, (IrInstSrcPtrType *)instruction);
2902 break;2932 break;
2933 case IrInstSrcIdPtrTypeSimple:
2934 ir_print_ptr_type_simple(irp, (IrInstSrcPtrTypeSimple *)instruction, false);
2935 break;
2936 case IrInstSrcIdPtrTypeSimpleConst:
2937 ir_print_ptr_type_simple(irp, (IrInstSrcPtrTypeSimple *)instruction, true);
2938 break;
2903 case IrInstSrcIdDeclRef:2939 case IrInstSrcIdDeclRef:
2904 ir_print_decl_ref(irp, (IrInstSrcDeclRef *)instruction);2940 ir_print_decl_ref(irp, (IrInstSrcDeclRef *)instruction);
2905 break;2941 break;
...@@ -2942,8 +2978,11 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai...@@ -2942,8 +2978,11 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai
2942 case IrInstSrcIdSetAlignStack:2978 case IrInstSrcIdSetAlignStack:
2943 ir_print_set_align_stack(irp, (IrInstSrcSetAlignStack *)instruction);2979 ir_print_set_align_stack(irp, (IrInstSrcSetAlignStack *)instruction);
2944 break;2980 break;
2945 case IrInstSrcIdArgType:2981 case IrInstSrcIdArgTypeAllowVarFalse:
2946 ir_print_arg_type(irp, (IrInstSrcArgType *)instruction);2982 ir_print_arg_type(irp, (IrInstSrcArgType *)instruction, false);
2983 break;
2984 case IrInstSrcIdArgTypeAllowVarTrue:
2985 ir_print_arg_type(irp, (IrInstSrcArgType *)instruction, true);
2947 break;2986 break;
2948 case IrInstSrcIdExport:2987 case IrInstSrcIdExport:
2949 ir_print_export(irp, (IrInstSrcExport *)instruction);2988 ir_print_export(irp, (IrInstSrcExport *)instruction);
src/translate_c.zig+254-115
...@@ -11,6 +11,7 @@ const math = std.math;...@@ -11,6 +11,7 @@ const math = std.math;
11const ast = @import("translate_c/ast.zig");11const ast = @import("translate_c/ast.zig");
12const Node = ast.Node;12const Node = ast.Node;
13const Tag = Node.Tag;13const Tag = Node.Tag;
14const c_builtins = std.c.builtins;
1415
15const CallingConvention = std.builtin.CallingConvention;16const CallingConvention = std.builtin.CallingConvention;
1617
...@@ -269,7 +270,10 @@ pub const Context = struct {...@@ -269,7 +270,10 @@ pub const Context = struct {
269 global_scope: *Scope.Root,270 global_scope: *Scope.Root,
270 clang_context: *clang.ASTContext,271 clang_context: *clang.ASTContext,
271 mangle_count: u32 = 0,272 mangle_count: u32 = 0,
273 /// Table of record decls that have been demoted to opaques.
272 opaque_demotes: std.AutoHashMapUnmanaged(usize, void) = .{},274 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
274 /// This one is different than the root scope's name table. This contains278 /// This one is different than the root scope's name table. This contains
275 /// a list of names that we found by visiting all the top level decls without279 /// a list of names that we found by visiting all the top level decls without
...@@ -337,6 +341,7 @@ pub fn translate(...@@ -337,6 +341,7 @@ pub fn translate(
337 context.alias_list.deinit();341 context.alias_list.deinit();
338 context.global_names.deinit(gpa);342 context.global_names.deinit(gpa);
339 context.opaque_demotes.deinit(gpa);343 context.opaque_demotes.deinit(gpa);
344 context.unnamed_typedefs.deinit(gpa);
340 context.global_scope.deinit();345 context.global_scope.deinit();
341 }346 }
342347
...@@ -400,6 +405,51 @@ fn declVisitorNamesOnly(c: *Context, decl: *const clang.Decl) Error!void {...@@ -400,6 +405,51 @@ fn declVisitorNamesOnly(c: *Context, decl: *const clang.Decl) Error!void {
400 if (decl.castToNamedDecl()) |named_decl| {405 if (decl.castToNamedDecl()) |named_decl| {
401 const decl_name = try c.str(named_decl.getName_bytes_begin());406 const decl_name = try c.str(named_decl.getName_bytes_begin());
402 try c.global_names.put(c.gpa, decl_name, {});407 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 }
403 }453 }
404}454}
405455
...@@ -635,7 +685,7 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co...@@ -635,7 +685,7 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co
635 if (has_init) trans_init: {685 if (has_init) trans_init: {
636 if (decl_init) |expr| {686 if (decl_init) |expr| {
637 const node_or_error = if (expr.getStmtClass() == .StringLiteralClass)687 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)
639 else689 else
640 transExprCoercing(c, scope, expr, .used);690 transExprCoercing(c, scope, expr, .used);
641 init_node = node_or_error catch |err| switch (err) {691 init_node = node_or_error catch |err| switch (err) {
...@@ -751,17 +801,10 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD...@@ -751,17 +801,10 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD
751 const toplevel = scope.id == .root;801 const toplevel = scope.id == .root;
752 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined;802 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;
764 var is_union = false;804 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
765 if (record_decl.isUnion()) {808 if (record_decl.isUnion()) {
766 container_kind_name = "union";809 container_kind_name = "union";
767 is_union = true;810 is_union = true;
...@@ -772,7 +815,20 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD...@@ -772,7 +815,20 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD
772 return failDecl(c, record_loc, bare_name, "record {s} is not a struct or union", .{bare_name});815 return failDecl(c, record_loc, bare_name, "record {s} is not a struct or union", .{bare_name});
773 }816 }
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 }
776 if (!toplevel) name = try bs.makeMangledName(c, name);832 if (!toplevel) name = try bs.makeMangledName(c, name);
777 try c.decl_table.putNoClobber(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), name);833 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...@@ -873,14 +929,19 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: *const clang.EnumDecl) E
873 const toplevel = scope.id == .root;929 const toplevel = scope.id == .root;
874 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined;930 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());
877 var is_unnamed = false;932 var is_unnamed = false;
878 if (bare_name.len == 0) {933 var bare_name: []const u8 = try c.str(@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin());
879 bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()});934 var name = bare_name;
880 is_unnamed = true;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});
881 }944 }
882
883 var name: []const u8 = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name});
884 if (!toplevel) _ = try bs.makeMangledName(c, name);945 if (!toplevel) _ = try bs.makeMangledName(c, name);
885 try c.decl_table.putNoClobber(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), name);946 try c.decl_table.putNoClobber(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), name);
886947
...@@ -1058,6 +1119,11 @@ fn transStmt(...@@ -1058,6 +1119,11 @@ fn transStmt(
1058 const compound_literal = @ptrCast(*const clang.CompoundLiteralExpr, stmt);1119 const compound_literal = @ptrCast(*const clang.CompoundLiteralExpr, stmt);
1059 return transExpr(c, scope, compound_literal.getInitializer(), result_used);1120 return transExpr(c, scope, compound_literal.getInitializer(), result_used);
1060 },1121 },
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()
1061 else => {1127 else => {
1062 return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO implement translation of stmt class {s}", .{@tagName(sc)});1128 return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO implement translation of stmt class {s}", .{@tagName(sc)});
1063 },1129 },
...@@ -1407,7 +1473,7 @@ fn transDeclStmtOne(...@@ -1407,7 +1473,7 @@ fn transDeclStmtOne(
14071473
1408 var init_node = if (decl_init) |expr|1474 var init_node = if (decl_init) |expr|
1409 if (expr.getStmtClass() == .StringLiteralClass)1475 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)
1411 else1477 else
1412 try transExprCoercing(c, scope, expr, .used)1478 try transExprCoercing(c, scope, expr, .used)
1413 else1479 else
...@@ -1522,7 +1588,7 @@ fn transImplicitCastExpr(...@@ -1522,7 +1588,7 @@ fn transImplicitCastExpr(
1522 return maybeSuppressResult(c, scope, result_used, ne);1588 return maybeSuppressResult(c, scope, result_used, ne);
1523 },1589 },
1524 .BuiltinFnToFnPtr => {1590 .BuiltinFnToFnPtr => {
1525 return transExpr(c, scope, sub_expr, result_used);1591 return transBuiltinFnExpr(c, scope, sub_expr, result_used);
1526 },1592 },
1527 .ToVoid => {1593 .ToVoid => {
1528 // Should only appear in the rhs and lhs of a ConditionalOperator1594 // Should only appear in the rhs and lhs of a ConditionalOperator
...@@ -1538,6 +1604,22 @@ fn transImplicitCastExpr(...@@ -1538,6 +1604,22 @@ fn transImplicitCastExpr(
1538 }1604 }
1539}1605}
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
1541fn transBoolExpr(1623fn transBoolExpr(
1542 c: *Context,1624 c: *Context,
1543 scope: *Scope,1625 scope: *Scope,
...@@ -1582,6 +1664,10 @@ fn exprIsNarrowStringLiteral(expr: *const clang.Expr) bool {...@@ -1582,6 +1664,10 @@ fn exprIsNarrowStringLiteral(expr: *const clang.Expr) bool {
1582 const op_expr = @ptrCast(*const clang.ParenExpr, expr).getSubExpr();1664 const op_expr = @ptrCast(*const clang.ParenExpr, expr).getSubExpr();
1583 return exprIsNarrowStringLiteral(op_expr);1665 return exprIsNarrowStringLiteral(op_expr);
1584 },1666 },
1667 .GenericSelectionExprClass => {
1668 const gen_sel = @ptrCast(*const clang.GenericSelectionExpr, expr);
1669 return exprIsNarrowStringLiteral(gen_sel.getResultExpr());
1670 },
1585 else => return false,1671 else => return false,
1586 }1672 }
1587}1673}
...@@ -1733,6 +1819,20 @@ fn transReturnStmt(...@@ -1733,6 +1819,20 @@ fn transReturnStmt(
1733 return Tag.@"return".create(c.arena, rhs);1819 return Tag.@"return".create(c.arena, rhs);
1734}1820}
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
1736fn transStringLiteral(1836fn transStringLiteral(
1737 c: *Context,1837 c: *Context,
1738 scope: *Scope,1838 scope: *Scope,
...@@ -1741,19 +1841,14 @@ fn transStringLiteral(...@@ -1741,19 +1841,14 @@ fn transStringLiteral(
1741) TransError!Node {1841) TransError!Node {
1742 const kind = stmt.getKind();1842 const kind = stmt.getKind();
1743 switch (kind) {1843 switch (kind) {
1744 .Ascii, .UTF8 => {1844 .Ascii, .UTF8 => return transNarrowStringLiteral(c, scope, stmt, result_used),
1745 var len: usize = undefined;
1746 const bytes_ptr = stmt.getString_bytes_begin_size(&len);
1747
1748 const str = try std.fmt.allocPrint(c.arena, "\"{}\"", .{std.zig.fmtEscapes(bytes_ptr[0..len])});
1749 const node = try Tag.string_literal.create(c.arena, str);
1750 return maybeSuppressResult(c, scope, result_used, node);
1751 },
1752 .UTF16, .UTF32, .Wide => {1845 .UTF16, .UTF32, .Wide => {
1753 const str_type = @tagName(stmt.getKind());1846 const str_type = @tagName(stmt.getKind());
1754 const name = try std.fmt.allocPrint(c.arena, "zig.{s}_string_{d}", .{ str_type, c.getMangle() });1847 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);
1757 const decl = try Tag.var_simple.create(c.arena, .{ .name = name, .init = lit_array });1852 const decl = try Tag.var_simple.create(c.arena, .{ .name = name, .init = lit_array });
1758 try scope.appendNode(decl);1853 try scope.appendNode(decl);
1759 const node = try Tag.identifier.create(c.arena, name);1854 const node = try Tag.identifier.create(c.arena, name);
...@@ -1762,52 +1857,67 @@ fn transStringLiteral(...@@ -1762,52 +1857,67 @@ fn transStringLiteral(
1762 }1857 }
1763}1858}
17641859
1765/// Parse the size of an array back out from an ast Node.1860fn getArrayPayload(array_type: Node) ast.Payload.Array.ArrayTypeInfo {
1766fn zigArraySize(c: *Context, node: Node) TransError!usize {1861 return (array_type.castTag(.array_type) orelse array_type.castTag(.null_sentinel_array_type).?).data;
1767 if (node.castTag(.array_type)) |array| {
1768 return array.data.len;
1769 }
1770 return error.UnsupportedTranslation;
1771}1862}
17721863
1773/// Translate a string literal to an array of integers. Used when an1864/// Translate a string literal that is initializing an array. In general narrow string
1774/// array is initialized from a string literal. `array_size` is the1865/// literals become `"<string>".*` or `"<string>"[0..<size>].*` if they need truncation.
1775/// size of the array being initialized. If the string literal is larger1866/// Wide string literals become an array of integers. zero-fillers pad out the array to
1776/// than the array, truncate the string. If the array is larger than the1867/// the appropriate length, if necessary.
1777/// string literal, pad the array with 0's1868fn transStringLiteralInitializer(
1778fn transStringLiteralAsArray(
1779 c: *Context,1869 c: *Context,
1780 scope: *Scope,1870 scope: *Scope,
1781 stmt: *const clang.StringLiteral,1871 stmt: *const clang.StringLiteral,
1782 array_size: usize,1872 array_type: Node,
1783) TransError!Node {1873) 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
1786 const str_length = stmt.getLength();1878 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);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
1789 const ty = expr_base.getType().getTypePtr();1908 assert(array_size > str_length); // If array_size <= str_length, `num_inits == array_size` and we've already returned.
1790 const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, ty);
17911909
1792 const elem_type = try transQualType(c, scope, const_arr_ty.getElementType(), expr_base.getBeginLoc());1910 const filler_node = try Tag.array_filler.create(c.arena, .{
1793 const arr_type = try Tag.array_type.create(c.arena, .{ .len = array_size, .elem_type = elem_type });1911 .type = elem_type,
1794 const init_list = try c.arena.alloc(Node, array_size);1912 .filler = Tag.zero_literal.init(),
1913 .count = array_size - str_length,
1914 });
17951915
1796 var i: c_uint = 0;1916 if (init_node) |some| {
1797 const kind = stmt.getKind();1917 return Tag.array_cat.create(c.arena, .{ .lhs = some, .rhs = filler_node });
1798 const narrow = kind == .Ascii or kind == .UTF8;1918 } else {
1799 while (i < str_length and i < array_size) : (i += 1) {1919 return filler_node;
1800 const code_unit = stmt.getCodeUnit(i);
1801 init_list[i] = try transCreateCharLitNode(c, narrow, code_unit);
1802 }
1803 while (i < array_size) : (i += 1) {
1804 init_list[i] = try transCreateNodeNumber(c, 0, .int);
1805 }1920 }
1806
1807 return Tag.array_init.create(c.arena, .{
1808 .cond = arr_type,
1809 .cases = init_list,
1810 });
1811}1921}
18121922
1813/// determine whether `stmt` is a "pointer subtraction expression" - a subtraction where1923/// determine whether `stmt` is a "pointer subtraction expression" - a subtraction where
...@@ -1836,6 +1946,7 @@ fn cIntTypeForEnum(enum_qt: clang.QualType) clang.QualType {...@@ -1836,6 +1946,7 @@ fn cIntTypeForEnum(enum_qt: clang.QualType) clang.QualType {
1836 return enum_decl.getIntegerType();1946 return enum_decl.getIntegerType();
1837}1947}
18381948
1949// when modifying this function, make sure to also update std.meta.cast
1839fn transCCast(1950fn transCCast(
1840 c: *Context,1951 c: *Context,
1841 scope: *Scope,1952 scope: *Scope,
...@@ -2192,6 +2303,35 @@ fn transImplicitValueInitExpr(...@@ -2192,6 +2303,35 @@ fn transImplicitValueInitExpr(
2192 return transZeroInitExpr(c, scope, source_loc, ty);2303 return transZeroInitExpr(c, scope, source_loc, ty);
2193}2304}
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
2195fn transIfStmt(2335fn transIfStmt(
2196 c: *Context,2336 c: *Context,
2197 scope: *Scope,2337 scope: *Scope,
...@@ -2209,9 +2349,10 @@ fn transIfStmt(...@@ -2209,9 +2349,10 @@ fn transIfStmt(
2209 const cond_expr = @ptrCast(*const clang.Expr, stmt.getCond());2349 const cond_expr = @ptrCast(*const clang.Expr, stmt.getCond());
2210 const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used);2350 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
2213 const else_body = if (stmt.getElse()) |expr|2354 const else_body = if (stmt.getElse()) |expr|
2214 try transStmt(c, scope, expr, .unused)2355 try maybeBlockify(c, scope, expr)
2215 else2356 else
2216 null;2357 null;
2217 return Tag.@"if".create(c.arena, .{ .cond = cond, .then = then_body, .@"else" = else_body });2358 return Tag.@"if".create(c.arena, .{ .cond = cond, .then = then_body, .@"else" = else_body });
...@@ -2236,7 +2377,7 @@ fn transWhileLoop(...@@ -2236,7 +2377,7 @@ fn transWhileLoop(
2236 .parent = scope,2377 .parent = scope,
2237 .id = .loop,2378 .id = .loop,
2238 };2379 };
2239 const body = try transStmt(c, &loop_scope, stmt.getBody(), .unused);2380 const body = try maybeBlockify(c, &loop_scope, stmt.getBody());
2240 return Tag.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = null });2381 return Tag.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = null });
2241}2382}
22422383
...@@ -2262,7 +2403,7 @@ fn transDoWhileLoop(...@@ -2262,7 +2403,7 @@ fn transDoWhileLoop(
2262 const if_not_break = switch (cond.tag()) {2403 const if_not_break = switch (cond.tag()) {
2263 .false_literal => return transStmt(c, scope, stmt.getBody(), .unused),2404 .false_literal => return transStmt(c, scope, stmt.getBody(), .unused),
2264 .true_literal => {2405 .true_literal => {
2265 const body_node = try transStmt(c, scope, stmt.getBody(), .unused);2406 const body_node = try maybeBlockify(c, scope, stmt.getBody());
2266 return Tag.while_true.create(c.arena, body_node);2407 return Tag.while_true.create(c.arena, body_node);
2267 },2408 },
2268 else => try Tag.if_not_break.create(c.arena, cond),2409 else => try Tag.if_not_break.create(c.arena, cond),
...@@ -2338,7 +2479,7 @@ fn transForLoop(...@@ -2338,7 +2479,7 @@ fn transForLoop(
2338 else2479 else
2339 null;2480 null;
23402481
2341 const body = try transStmt(c, &loop_scope, stmt.getBody(), .unused);2482 const body = try maybeBlockify(c, &loop_scope, stmt.getBody());
2342 const while_node = try Tag.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = cont_expr });2483 const while_node = try Tag.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = cont_expr });
2343 if (block_scope) |*bs| {2484 if (block_scope) |*bs| {
2344 try bs.statements.append(while_node);2485 try bs.statements.append(while_node);
...@@ -2725,6 +2866,10 @@ fn cIsFunctionDeclRef(expr: *const clang.Expr) bool {...@@ -2725,6 +2866,10 @@ fn cIsFunctionDeclRef(expr: *const clang.Expr) bool {
2725 const opcode = un_op.getOpcode();2866 const opcode = un_op.getOpcode();
2726 return (opcode == .AddrOf or opcode == .Deref) and cIsFunctionDeclRef(un_op.getSubExpr());2867 return (opcode == .AddrOf or opcode == .Deref) and cIsFunctionDeclRef(un_op.getSubExpr());
2727 },2868 },
2869 .GenericSelectionExprClass => {
2870 const gen_sel = @ptrCast(*const clang.GenericSelectionExpr, expr);
2871 return cIsFunctionDeclRef(gen_sel.getResultExpr());
2872 },
2728 else => return false,2873 else => return false,
2729 }2874 }
2730}2875}
...@@ -3052,43 +3197,34 @@ fn transCreateCompoundAssign(...@@ -3052,43 +3197,34 @@ fn transCreateCompoundAssign(
3052 const requires_int_cast = blk: {3197 const requires_int_cast = blk: {
3053 const are_integers = cIsInteger(lhs_qt) and cIsInteger(rhs_qt);3198 const are_integers = cIsInteger(lhs_qt) and cIsInteger(rhs_qt);
3054 const are_same_sign = cIsSignedInteger(lhs_qt) == cIsSignedInteger(rhs_qt);3199 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);
3056 };3201 };
3202
3057 if (used == .unused) {3203 if (used == .unused) {
3058 // common case3204 // common case
3059 // c: lhs += rhs3205 // c: lhs += rhs
3060 // zig: lhs += rhs3206 // 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
3061 if ((is_mod or is_div) and is_signed) {3211 if ((is_mod or is_div) and is_signed) {
3062 const lhs_node = try transExpr(c, scope, lhs, .used);3212 if (requires_int_cast) rhs_node = try transCCast(c, scope, loc, lhs_qt, rhs_qt, rhs_node);
3063 const rhs_node = try transExpr(c, scope, rhs, .used);3213 const operands = .{ .lhs = lhs_node, .rhs = rhs_node };
3064 const builtin = if (is_mod)3214 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)
3066 else3216 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
3069 return transCreateNodeInfixOp(c, scope, .assign, lhs_node, builtin, .used);3219 return transCreateNodeInfixOp(c, scope, .assign, lhs_node, builtin, .used);
3070 }3220 }
30713221
3072 const lhs_node = try transExpr(c, scope, lhs, .used);3222 if (is_shift) {
3073 var rhs_node = if (is_shift or requires_int_cast)3223 const cast_to_type = try qualTypeToLog2IntRef(c, scope, rhs_qt, loc);
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
3089 rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node });3224 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);
3090 }3227 }
3091
3092 return transCreateNodeInfixOp(c, scope, op, lhs_node, rhs_node, .used);3228 return transCreateNodeInfixOp(c, scope, op, lhs_node, rhs_node, .used);
3093 }3229 }
3094 // worst case3230 // worst case
...@@ -3110,29 +3246,24 @@ fn transCreateCompoundAssign(...@@ -3110,29 +3246,24 @@ fn transCreateCompoundAssign(
3110 const lhs_node = try Tag.identifier.create(c.arena, ref);3246 const lhs_node = try Tag.identifier.create(c.arena, ref);
3111 const ref_node = try Tag.deref.create(c.arena, lhs_node);3247 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);
3113 if ((is_mod or is_div) and is_signed) {3251 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 };
3115 const builtin = if (is_mod)3254 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)
3117 else3256 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
3120 const assign = try transCreateNodeInfixOp(c, &block_scope.base, .assign, ref_node, builtin, .used);3259 const assign = try transCreateNodeInfixOp(c, &block_scope.base, .assign, ref_node, builtin, .used);
3121 try block_scope.statements.append(assign);3260 try block_scope.statements.append(assign);
3122 } else {3261 } else {
3123 var rhs_node = try transExpr(c, &block_scope.base, rhs, .used);3262 if (is_shift) {
31243263 const cast_to_type = try qualTypeToLog2IntRef(c, &block_scope.base, rhs_qt, loc);
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
3132 rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node });3264 rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node });
3133 }3265 } else if (requires_int_cast) {
3134 if (is_ptr_op_signed) {3266 rhs_node = try transCCast(c, &block_scope.base, loc, lhs_qt, rhs_qt, rhs_node);
3135 rhs_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node);
3136 }3267 }
31373268
3138 const assign = try transCreateNodeInfixOp(c, &block_scope.base, op, ref_node, rhs_node, .used);3269 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...@@ -3194,11 +3325,11 @@ fn transFloatingLiteral(c: *Context, scope: *Scope, stmt: *const clang.FloatingL
3194 var dbl = stmt.getValueAsApproximateDouble();3325 var dbl = stmt.getValueAsApproximateDouble();
3195 const is_negative = dbl < 0;3326 const is_negative = dbl < 0;
3196 if (is_negative) dbl = -dbl;3327 if (is_negative) dbl = -dbl;
3197 const str = try std.fmt.allocPrint(c.arena, "{d}", .{dbl});3328 const str = if (dbl == std.math.floor(dbl))
3198 var node = if (dbl == std.math.floor(dbl))3329 try std.fmt.allocPrint(c.arena, "{d}.0", .{dbl})
3199 try Tag.integer_literal.create(c.arena, str)
3200 else3330 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);
3202 if (is_negative) node = try Tag.negate.create(c.arena, node);3333 if (is_negative) node = try Tag.negate.create(c.arena, node);
3203 return maybeSuppressResult(c, scope, used, node);3334 return maybeSuppressResult(c, scope, used, node);
3204}3335}
...@@ -3312,9 +3443,8 @@ fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: Node) !void {...@@ -3312,9 +3443,8 @@ fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: Node) !void {
3312 try c.global_scope.nodes.append(decl_node);3443 try c.global_scope.nodes.append(decl_node);
3313}3444}
33143445
3315/// Translate a qual type for a variable with an initializer. The initializer3446/// Translate a qualtype for a variable with an initializer. This only matters
3316/// only matters for incomplete arrays, since the size of the array is determined3447/// for incomplete arrays, since the initializer determines the size of the array.
3317/// by the size of the initializer
3318fn transQualTypeInitialized(3448fn transQualTypeInitialized(
3319 c: *Context,3449 c: *Context,
3320 scope: *Scope,3450 scope: *Scope,
...@@ -3330,9 +3460,14 @@ fn transQualTypeInitialized(...@@ -3330,9 +3460,14 @@ fn transQualTypeInitialized(
3330 switch (decl_init.getStmtClass()) {3460 switch (decl_init.getStmtClass()) {
3331 .StringLiteralClass => {3461 .StringLiteralClass => {
3332 const string_lit = @ptrCast(*const clang.StringLiteral, decl_init);3462 const string_lit = @ptrCast(*const clang.StringLiteral, decl_init);
3333 const string_lit_size = string_lit.getLength() + 1; // +1 for null terminator3463 const string_lit_size = string_lit.getLength();
3334 const array_size = @intCast(usize, string_lit_size);3464 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 });
3336 },3471 },
3337 .InitListExprClass => {3472 .InitListExprClass => {
3338 const init_expr = @ptrCast(*const clang.InitListExpr, decl_init);3473 const init_expr = @ptrCast(*const clang.InitListExpr, decl_init);
...@@ -4746,6 +4881,10 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!N...@@ -4746,6 +4881,10 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!N
4746 },4881 },
4747 .Identifier => {4882 .Identifier => {
4748 const mangled_name = scope.getAlias(slice);4883 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 }
4749 return Tag.identifier.create(c.arena, builtin_typedef_map.get(mangled_name) orelse mangled_name);4888 return Tag.identifier.create(c.arena, builtin_typedef_map.get(mangled_name) orelse mangled_name);
4750 },4889 },
4751 .LParen => {4890 .LParen => {
src/translate_c/ast.zig+83-3
...@@ -40,6 +40,8 @@ pub const Node = extern union {...@@ -40,6 +40,8 @@ pub const Node = extern union {
40 string_literal,40 string_literal,
41 char_literal,41 char_literal,
42 enum_literal,42 enum_literal,
43 /// "string"[0..end]
44 string_slice,
43 identifier,45 identifier,
44 @"if",46 @"if",
45 /// if (!operand) break;47 /// if (!operand) break;
...@@ -176,6 +178,7 @@ pub const Node = extern union {...@@ -176,6 +178,7 @@ pub const Node = extern union {
176 c_pointer,178 c_pointer,
177 single_pointer,179 single_pointer,
178 array_type,180 array_type,
181 null_sentinel_array_type,
179182
180 /// @import("std").meta.sizeof(operand)183 /// @import("std").meta.sizeof(operand)
181 std_meta_sizeof,184 std_meta_sizeof,
...@@ -334,7 +337,7 @@ pub const Node = extern union {...@@ -334,7 +337,7 @@ pub const Node = extern union {
334 .std_meta_promoteIntLiteral => Payload.PromoteIntLiteral,337 .std_meta_promoteIntLiteral => Payload.PromoteIntLiteral,
335 .block => Payload.Block,338 .block => Payload.Block,
336 .c_pointer, .single_pointer => Payload.Pointer,339 .c_pointer, .single_pointer => Payload.Pointer,
337 .array_type => Payload.Array,340 .array_type, .null_sentinel_array_type => Payload.Array,
338 .arg_redecl, .alias, .fail_decl => Payload.ArgRedecl,341 .arg_redecl, .alias, .fail_decl => Payload.ArgRedecl,
339 .log2_int_type => Payload.Log2IntType,342 .log2_int_type => Payload.Log2IntType,
340 .var_simple, .pub_var_simple => Payload.SimpleVarDecl,343 .var_simple, .pub_var_simple => Payload.SimpleVarDecl,
...@@ -342,6 +345,7 @@ pub const Node = extern union {...@@ -342,6 +345,7 @@ pub const Node = extern union {
342 .array_filler => Payload.ArrayFiller,345 .array_filler => Payload.ArrayFiller,
343 .pub_inline_fn => Payload.PubInlineFn,346 .pub_inline_fn => Payload.PubInlineFn,
344 .field_access => Payload.FieldAccess,347 .field_access => Payload.FieldAccess,
348 .string_slice => Payload.StringSlice,
345 };349 };
346 }350 }
347351
...@@ -584,10 +588,12 @@ pub const Payload = struct {...@@ -584,10 +588,12 @@ pub const Payload = struct {
584588
585 pub const Array = struct {589 pub const Array = struct {
586 base: Payload,590 base: Payload,
587 data: struct {591 data: ArrayTypeInfo,
592
593 pub const ArrayTypeInfo = struct {
588 elem_type: Node,594 elem_type: Node,
589 len: usize,595 len: usize,
590 },596 };
591 };597 };
592598
593 pub const Pointer = struct {599 pub const Pointer = struct {
...@@ -664,6 +670,14 @@ pub const Payload = struct {...@@ -664,6 +670,14 @@ pub const Payload = struct {
664 radix: Node,670 radix: Node,
665 },671 },
666 };672 };
673
674 pub const StringSlice = struct {
675 base: Payload,
676 data: struct {
677 string: Node,
678 end: usize,
679 },
680 };
667};681};
668682
669/// Converts the nodes into a Zig ast.683/// Converts the nodes into a Zig ast.
...@@ -1015,6 +1029,36 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {...@@ -1015,6 +1029,36 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
1015 .data = undefined,1029 .data = undefined,
1016 });1030 });
1017 },1031 },
1032 .string_slice => {
1033 const payload = node.castTag(.string_slice).?.data;
1034
1035 const string = try renderNode(c, payload.string);
1036 const l_bracket = try c.addToken(.l_bracket, "[");
1037 const start = try c.addNode(.{
1038 .tag = .integer_literal,
1039 .main_token = try c.addToken(.integer_literal, "0"),
1040 .data = undefined,
1041 });
1042 _ = try c.addToken(.ellipsis2, "..");
1043 const end = try c.addNode(.{
1044 .tag = .integer_literal,
1045 .main_token = try c.addTokenFmt(.integer_literal, "{d}", .{payload.end}),
1046 .data = undefined,
1047 });
1048 _ = try c.addToken(.r_bracket, "]");
1049
1050 return c.addNode(.{
1051 .tag = .slice,
1052 .main_token = l_bracket,
1053 .data = .{
1054 .lhs = string,
1055 .rhs = try c.addExtra(std.zig.ast.Node.Slice{
1056 .start = start,
1057 .end = end,
1058 }),
1059 },
1060 });
1061 },
1018 .fail_decl => {1062 .fail_decl => {
1019 const payload = node.castTag(.fail_decl).?.data;1063 const payload = node.castTag(.fail_decl).?.data;
1020 // pub const name = @compileError(msg);1064 // pub const name = @compileError(msg);
...@@ -1581,6 +1625,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {...@@ -1581,6 +1625,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
1581 const payload = node.castTag(.array_type).?.data;1625 const payload = node.castTag(.array_type).?.data;
1582 return renderArrayType(c, payload.len, payload.elem_type);1626 return renderArrayType(c, payload.len, payload.elem_type);
1583 },1627 },
1628 .null_sentinel_array_type => {
1629 const payload = node.castTag(.null_sentinel_array_type).?.data;
1630 return renderNullSentinelArrayType(c, payload.len, payload.elem_type);
1631 },
1584 .array_filler => {1632 .array_filler => {
1585 const payload = node.castTag(.array_filler).?.data;1633 const payload = node.castTag(.array_filler).?.data;
15861634
...@@ -1946,6 +1994,36 @@ fn renderArrayType(c: *Context, len: usize, elem_type: Node) !NodeIndex {...@@ -1946,6 +1994,36 @@ fn renderArrayType(c: *Context, len: usize, elem_type: Node) !NodeIndex {
1946 });1994 });
1947}1995}
19481996
1997fn renderNullSentinelArrayType(c: *Context, len: usize, elem_type: Node) !NodeIndex {
1998 const l_bracket = try c.addToken(.l_bracket, "[");
1999 const len_expr = try c.addNode(.{
2000 .tag = .integer_literal,
2001 .main_token = try c.addTokenFmt(.integer_literal, "{d}", .{len}),
2002 .data = undefined,
2003 });
2004 _ = try c.addToken(.colon, ":");
2005
2006 const sentinel_expr = try c.addNode(.{
2007 .tag = .integer_literal,
2008 .main_token = try c.addToken(.integer_literal, "0"),
2009 .data = undefined,
2010 });
2011
2012 _ = try c.addToken(.r_bracket, "]");
2013 const elem_type_expr = try renderNode(c, elem_type);
2014 return c.addNode(.{
2015 .tag = .array_type_sentinel,
2016 .main_token = l_bracket,
2017 .data = .{
2018 .lhs = len_expr,
2019 .rhs = try c.addExtra(std.zig.ast.Node.ArrayTypeSentinel {
2020 .sentinel = sentinel_expr,
2021 .elem_type = elem_type_expr,
2022 }),
2023 },
2024 });
2025}
2026
1949fn addSemicolonIfNeeded(c: *Context, node: Node) !void {2027fn addSemicolonIfNeeded(c: *Context, node: Node) !void {
1950 switch (node.tag()) {2028 switch (node.tag()) {
1951 .warning => unreachable,2029 .warning => unreachable,
...@@ -2014,6 +2092,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {...@@ -2014,6 +2092,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
2014 .integer_literal,2092 .integer_literal,
2015 .float_literal,2093 .float_literal,
2016 .string_literal,2094 .string_literal,
2095 .string_slice,
2017 .char_literal,2096 .char_literal,
2018 .enum_literal,2097 .enum_literal,
2019 .identifier,2098 .identifier,
...@@ -2035,6 +2114,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {...@@ -2035,6 +2114,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
2035 .func,2114 .func,
2036 .call,2115 .call,
2037 .array_type,2116 .array_type,
2117 .null_sentinel_array_type,
2038 .bool_to_int,2118 .bool_to_int,
2039 .div_exact,2119 .div_exact,
2040 .byte_offset_of,2120 .byte_offset_of,
src/zig_clang.cpp+5
...@@ -2459,6 +2459,11 @@ struct ZigClangQualType ZigClangFunctionType_getReturnType(const struct ZigClang...@@ -2459,6 +2459,11 @@ struct ZigClangQualType ZigClangFunctionType_getReturnType(const struct ZigClang
2459 return bitcast(casted->getReturnType());2459 return bitcast(casted->getReturnType());
2460}2460}
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
2462bool ZigClangFunctionProtoType_isVariadic(const struct ZigClangFunctionProtoType *self) {2467bool ZigClangFunctionProtoType_isVariadic(const struct ZigClangFunctionProtoType *self) {
2463 auto casted = reinterpret_cast<const clang::FunctionProtoType *>(self);2468 auto casted = reinterpret_cast<const clang::FunctionProtoType *>(self);
2464 return casted->isVariadic();2469 return casted->isVariadic();
src/zig_clang.h+2
...@@ -1123,6 +1123,8 @@ ZIG_EXTERN_C bool ZigClangFunctionType_getNoReturnAttr(const struct ZigClangFunc...@@ -1123,6 +1123,8 @@ ZIG_EXTERN_C bool ZigClangFunctionType_getNoReturnAttr(const struct ZigClangFunc
1123ZIG_EXTERN_C enum ZigClangCallingConv ZigClangFunctionType_getCallConv(const struct ZigClangFunctionType *self);1123ZIG_EXTERN_C enum ZigClangCallingConv ZigClangFunctionType_getCallConv(const struct ZigClangFunctionType *self);
1124ZIG_EXTERN_C struct ZigClangQualType ZigClangFunctionType_getReturnType(const struct ZigClangFunctionType *self);1124ZIG_EXTERN_C struct ZigClangQualType ZigClangFunctionType_getReturnType(const struct ZigClangFunctionType *self);
11251125
1126ZIG_EXTERN_C const struct ZigClangExpr *ZigClangGenericSelectionExpr_getResultExpr(const struct ZigClangGenericSelectionExpr *self);
1127
1126ZIG_EXTERN_C bool ZigClangFunctionProtoType_isVariadic(const struct ZigClangFunctionProtoType *self);1128ZIG_EXTERN_C bool ZigClangFunctionProtoType_isVariadic(const struct ZigClangFunctionProtoType *self);
1127ZIG_EXTERN_C unsigned ZigClangFunctionProtoType_getNumParams(const struct ZigClangFunctionProtoType *self);1129ZIG_EXTERN_C unsigned ZigClangFunctionProtoType_getNumParams(const struct ZigClangFunctionProtoType *self);
1128ZIG_EXTERN_C struct ZigClangQualType ZigClangFunctionProtoType_getParamType(const struct ZigClangFunctionProtoType *self, unsigned i);1130ZIG_EXTERN_C struct ZigClangQualType ZigClangFunctionProtoType_getParamType(const struct ZigClangFunctionProtoType *self, unsigned i);
test/cli.zig+11
...@@ -28,6 +28,8 @@ pub fn main() !void {...@@ -28,6 +28,8 @@ pub fn main() !void {
28 const zig_exe = try fs.path.resolve(a, &[_][]const u8{zig_exe_rel});28 const zig_exe = try fs.path.resolve(a, &[_][]const u8{zig_exe_rel});
2929
30 const dir_path = try fs.path.join(a, &[_][]const u8{ cache_root, "clitest" });30 const dir_path = try fs.path.join(a, &[_][]const u8{ cache_root, "clitest" });
31 defer fs.cwd().deleteTree(dir_path) catch {};
32
31 const TestFn = fn ([]const u8, []const u8) anyerror!void;33 const TestFn = fn ([]const u8, []const u8) anyerror!void;
32 const test_fns = [_]TestFn{34 const test_fns = [_]TestFn{
33 testZigInitLib,35 testZigInitLib,
...@@ -174,4 +176,13 @@ fn testZigFmt(zig_exe: []const u8, dir_path: []const u8) !void {...@@ -174,4 +176,13 @@ fn testZigFmt(zig_exe: []const u8, dir_path: []const u8) !void {
174 const run_result3 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path });176 const run_result3 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path });
175 // both files have been formatted, nothing should change now177 // both files have been formatted, nothing should change now
176 testing.expect(run_result3.stdout.len == 0);178 testing.expect(run_result3.stdout.len == 0);
179
180 // Check UTF-16 decoding
181 const fmt4_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt4.zig" });
182 var unformatted_code_utf16 = "\xff\xfe \x00 \x00 \x00 \x00/\x00/\x00 \x00n\x00o\x00 \x00r\x00e\x00a\x00s\x00o\x00n\x00";
183 try fs.cwd().writeFile(fmt4_zig_path, unformatted_code_utf16);
184
185 const run_result4 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path });
186 testing.expect(std.mem.startsWith(u8, run_result4.stdout, fmt4_zig_path));
187 testing.expect(run_result4.stdout.len == fmt4_zig_path.len + 1 and run_result4.stdout[run_result4.stdout.len - 1] == '\n');
177}188}
test/run_translated_c.zig+121
...@@ -3,6 +3,17 @@ const tests = @import("tests.zig");...@@ -3,6 +3,17 @@ const tests = @import("tests.zig");
3const nl = std.cstr.line_sep;3const nl = std.cstr.line_sep;
44
5pub fn addCases(cases: *tests.RunTranslatedCContext) void {5pub fn addCases(cases: *tests.RunTranslatedCContext) void {
6 cases.add("division of floating literals",
7 \\#define _NO_CRT_STDIO_INLINE 1
8 \\#include <stdio.h>
9 \\#define PI 3.14159265358979323846f
10 \\#define DEG2RAD (PI/180.0f)
11 \\int main(void) {
12 \\ printf("DEG2RAD is: %f\n", DEG2RAD);
13 \\ return 0;
14 \\}
15 , "DEG2RAD is: 0.017453" ++ nl);
16
6 cases.add("use global scope for record/enum/typedef type transalation if needed",17 cases.add("use global scope for record/enum/typedef type transalation if needed",
7 \\void bar(void);18 \\void bar(void);
8 \\void baz(void);19 \\void baz(void);
...@@ -1187,4 +1198,114 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {...@@ -1187,4 +1198,114 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
1187 \\ return 0;1198 \\ return 0;
1188 \\}1199 \\}
1189 , "");1200 , "");
1201
1202 cases.add("Generic selections",
1203 \\#include <stdlib.h>
1204 \\#include <string.h>
1205 \\#include <stdint.h>
1206 \\#define my_generic_fn(X) _Generic((X), \
1207 \\ int: abs, \
1208 \\ char *: strlen, \
1209 \\ size_t: malloc, \
1210 \\ default: free \
1211 \\)(X)
1212 \\#define my_generic_val(X) _Generic((X), \
1213 \\ int: 1, \
1214 \\ const char *: "bar" \
1215 \\)
1216 \\int main(void) {
1217 \\ if (my_generic_val(100) != 1) abort();
1218 \\
1219 \\ const char *foo = "foo";
1220 \\ const char *bar = my_generic_val(foo);
1221 \\ if (strcmp(bar, "bar") != 0) abort();
1222 \\
1223 \\ if (my_generic_fn(-42) != 42) abort();
1224 \\ if (my_generic_fn("hello") != 5) abort();
1225 \\
1226 \\ size_t size = 8192;
1227 \\ uint8_t *mem = my_generic_fn(size);
1228 \\ memset(mem, 42, size);
1229 \\ if (mem[size - 1] != 42) abort();
1230 \\ my_generic_fn(mem);
1231 \\
1232 \\ return 0;
1233 \\}
1234 , "");
1235
1236 // See __builtin_alloca_with_align comment in std.c.builtins
1237 cases.add("use of unimplemented builtin in unused function does not prevent compilation",
1238 \\#include <stdlib.h>
1239 \\void unused() {
1240 \\ __builtin_alloca_with_align(1, 8);
1241 \\}
1242 \\int main(void) {
1243 \\ if (__builtin_sqrt(1.0) != 1.0) abort();
1244 \\ return 0;
1245 \\}
1246 , "");
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 , "");
1190}1311}
test/stage1/behavior/vector.zig+8-4
...@@ -4,7 +4,7 @@ const mem = std.mem;...@@ -4,7 +4,7 @@ const mem = std.mem;
4const math = std.math;4const math = std.math;
5const expect = std.testing.expect;5const expect = std.testing.expect;
6const expectEqual = std.testing.expectEqual;6const expectEqual = std.testing.expectEqual;
7const expectWithinEpsilon = std.testing.expectWithinEpsilon;7const expectApproxEqRel = std.testing.expectApproxEqRel;
8const Vector = std.meta.Vector;8const Vector = std.meta.Vector;
99
10test "implicit cast vector to array - bool" {10test "implicit cast vector to array - bool" {
...@@ -527,10 +527,14 @@ test "vector reduce operation" {...@@ -527,10 +527,14 @@ test "vector reduce operation" {
527 switch (@typeInfo(TX)) {527 switch (@typeInfo(TX)) {
528 .Int, .Bool => expectEqual(expected, r),528 .Int, .Bool => expectEqual(expected, r),
529 .Float => {529 .Float => {
530 if (math.isNan(expected) != math.isNan(r)) {530 const expected_nan = math.isNan(expected);
531 std.debug.panic("unexpected NaN value!\n", .{});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.
532 } else {536 } else {
533 expectWithinEpsilon(expected, r, 0.001);537 expectApproxEqRel(expected, r, math.sqrt(math.epsilon(TX)));
534 }538 }
535 },539 },
536 else => unreachable,540 else => unreachable,
test/stage2/cbe.zig+1-1
...@@ -51,7 +51,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -51,7 +51,7 @@ pub fn addCases(ctx: *TestContext) !void {
51 \\ _ = printf("Hello, %s!\n", "world");51 \\ _ = printf("Hello, %s!\n", "world");
52 \\ return 0;52 \\ return 0;
53 \\}53 \\}
54 , "Hello, world!\n");54 , "Hello, world!" ++ std.cstr.line_sep);
55 }55 }
5656
57 {57 {
test/stage2/wasm.zig+35
...@@ -175,6 +175,41 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -175,6 +175,41 @@ pub fn addCases(ctx: *TestContext) !void {
175 \\ return i;175 \\ return i;
176 \\}176 \\}
177 , "31\n");177 , "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 , "");
178 }213 }
179214
180 {215 {
test/standalone.zig+4-1
...@@ -9,7 +9,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void {...@@ -9,7 +9,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
9 cases.add("test/standalone/main_return_error/error_u8.zig");9 cases.add("test/standalone/main_return_error/error_u8.zig");
10 cases.add("test/standalone/main_return_error/error_u8_non_zero.zig");10 cases.add("test/standalone/main_return_error/error_u8_non_zero.zig");
11 cases.addBuildFile("test/standalone/main_pkg_path/build.zig");11 cases.addBuildFile("test/standalone/main_pkg_path/build.zig");
12 cases.addBuildFile("test/standalone/shared_library/build.zig");12 if (std.Target.current.os.tag != .macos) {
13 // TODO zld cannot link shared libraries yet.
14 cases.addBuildFile("test/standalone/shared_library/build.zig");
15 }
13 cases.addBuildFile("test/standalone/mix_o_files/build.zig");16 cases.addBuildFile("test/standalone/mix_o_files/build.zig");
14 cases.addBuildFile("test/standalone/global_linkage/build.zig");17 cases.addBuildFile("test/standalone/global_linkage/build.zig");
15 cases.addBuildFile("test/standalone/static_c_lib/build.zig");18 cases.addBuildFile("test/standalone/static_c_lib/build.zig");
test/standalone/mix_o_files/base64.zig+3-3
...@@ -3,9 +3,9 @@ const base64 = @import("std").base64;...@@ -3,9 +3,9 @@ const base64 = @import("std").base64;
3export fn decode_base_64(dest_ptr: [*]u8, dest_len: usize, source_ptr: [*]const u8, source_len: usize) usize {3export fn decode_base_64(dest_ptr: [*]u8, dest_len: usize, source_ptr: [*]const u8, source_len: usize) usize {
4 const src = source_ptr[0..source_len];4 const src = source_ptr[0..source_len];
5 const dest = dest_ptr[0..dest_len];5 const dest = dest_ptr[0..dest_len];
6 const base64_decoder = base64.standard_decoder_unsafe;6 const base64_decoder = base64.standard.Decoder;
7 const decoded_size = base64_decoder.calcSize(src);7 const decoded_size = base64_decoder.calcSizeForSlice(src) catch unreachable;
8 base64_decoder.decode(dest[0..decoded_size], src);8 base64_decoder.decode(dest[0..decoded_size], src) catch unreachable;
9 return decoded_size;9 return decoded_size;
10}10}
1111
test/translate_c.zig+126-76
...@@ -3,6 +3,28 @@ const std = @import("std");...@@ -3,6 +3,28 @@ const std = @import("std");
3const CrossTarget = std.zig.CrossTarget;3const CrossTarget = std.zig.CrossTarget;
44
5pub fn addCases(cases: *tests.TranslateCContext) void {5pub 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
6 cases.add("if as while stmt has semicolon",28 cases.add("if as while stmt has semicolon",
7 \\void foo() {29 \\void foo() {
8 \\ while (1) if (1) {30 \\ while (1) if (1) {
...@@ -218,9 +240,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -218,9 +240,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
218 \\} Bar;240 \\} Bar;
219 , &[_][]const u8{241 , &[_][]const u8{
220 \\source.h:1:9: warning: struct demoted to opaque type - unable to translate type of field foo242 \\source.h:1:9: warning: struct demoted to opaque type - unable to translate type of field foo
221 \\const struct_unnamed_1 = opaque {};243 \\pub const Foo = opaque {};
222 \\pub const Foo = struct_unnamed_1;244 \\pub const Bar = extern struct {
223 \\const struct_unnamed_2 = extern struct {
224 \\ bar: ?*Foo,245 \\ bar: ?*Foo,
225 \\};246 \\};
226 });247 });
...@@ -519,17 +540,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -519,17 +540,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
519 \\} outer;540 \\} outer;
520 \\void foo(outer *x) { x->y = x->x; }541 \\void foo(outer *x) { x->y = x->x; }
521 , &[_][]const u8{542 , &[_][]const u8{
522 \\const struct_unnamed_3 = extern struct {543 \\const struct_unnamed_2 = extern struct {
523 \\ y: c_int,544 \\ y: c_int,
524 \\};545 \\};
525 \\const union_unnamed_2 = extern union {546 \\const union_unnamed_1 = extern union {
526 \\ x: u8,547 \\ x: u8,
527 \\ unnamed_0: struct_unnamed_3,548 \\ unnamed_0: struct_unnamed_2,
528 \\};549 \\};
529 \\const struct_unnamed_1 = extern struct {550 \\pub const outer = extern struct {
530 \\ unnamed_0: union_unnamed_2,551 \\ unnamed_0: union_unnamed_1,
531 \\};552 \\};
532 \\pub const outer = struct_unnamed_1;
533 \\pub export fn foo(arg_x: [*c]outer) void {553 \\pub export fn foo(arg_x: [*c]outer) void {
534 \\ var x = arg_x;554 \\ var x = arg_x;
535 \\ x.*.unnamed_0.unnamed_0.y = @bitCast(c_int, @as(c_uint, x.*.unnamed_0.x));555 \\ 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 {...@@ -565,21 +585,20 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
565 \\struct {int x,y;} s2 = {.y = 2, .x=1};585 \\struct {int x,y;} s2 = {.y = 2, .x=1};
566 \\foo s3 = { 123 };586 \\foo s3 = { 123 };
567 , &[_][]const u8{587 , &[_][]const u8{
568 \\const struct_unnamed_1 = extern struct {588 \\pub const foo = extern struct {
569 \\ x: c_int,589 \\ x: c_int,
570 \\};590 \\};
571 \\pub const foo = struct_unnamed_1;591 \\const struct_unnamed_1 = extern struct {
572 \\const struct_unnamed_2 = extern struct {
573 \\ x: f64,592 \\ x: f64,
574 \\ y: f64,593 \\ y: f64,
575 \\ z: f64,594 \\ z: f64,
576 \\};595 \\};
577 \\pub export var s0: struct_unnamed_2 = struct_unnamed_2{596 \\pub export var s0: struct_unnamed_1 = struct_unnamed_1{
578 \\ .x = 1.2,597 \\ .x = 1.2,
579 \\ .y = 1.3,598 \\ .y = 1.3,
580 \\ .z = 0,599 \\ .z = 0,
581 \\};600 \\};
582 \\const struct_unnamed_3 = extern struct {601 \\const struct_unnamed_2 = extern struct {
583 \\ sec: c_int,602 \\ sec: c_int,
584 \\ min: c_int,603 \\ min: c_int,
585 \\ hour: c_int,604 \\ hour: c_int,
...@@ -587,7 +606,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -587,7 +606,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
587 \\ mon: c_int,606 \\ mon: c_int,
588 \\ year: c_int,607 \\ year: c_int,
589 \\};608 \\};
590 \\pub export var s1: struct_unnamed_3 = struct_unnamed_3{609 \\pub export var s1: struct_unnamed_2 = struct_unnamed_2{
591 \\ .sec = @as(c_int, 30),610 \\ .sec = @as(c_int, 30),
592 \\ .min = @as(c_int, 15),611 \\ .min = @as(c_int, 15),
593 \\ .hour = @as(c_int, 17),612 \\ .hour = @as(c_int, 17),
...@@ -595,11 +614,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -595,11 +614,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
595 \\ .mon = @as(c_int, 12),614 \\ .mon = @as(c_int, 12),
596 \\ .year = @as(c_int, 2014),615 \\ .year = @as(c_int, 2014),
597 \\};616 \\};
598 \\const struct_unnamed_4 = extern struct {617 \\const struct_unnamed_3 = extern struct {
599 \\ x: c_int,618 \\ x: c_int,
600 \\ y: c_int,619 \\ y: c_int,
601 \\};620 \\};
602 \\pub export var s2: struct_unnamed_4 = struct_unnamed_4{621 \\pub export var s2: struct_unnamed_3 = struct_unnamed_3{
603 \\ .x = @as(c_int, 1),622 \\ .x = @as(c_int, 1),
604 \\ .y = @as(c_int, 2),623 \\ .y = @as(c_int, 2),
605 \\};624 \\};
...@@ -745,14 +764,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -745,14 +764,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
745 \\ static const char v2[] = "2.2.2";764 \\ static const char v2[] = "2.2.2";
746 \\}765 \\}
747 , &[_][]const u8{766 , &[_][]const u8{
748 \\const v2: [6]u8 = [6]u8{767 \\const v2: [5:0]u8 = "2.2.2".*;
749 \\ '2',
750 \\ '.',
751 \\ '2',
752 \\ '.',
753 \\ '2',
754 \\ 0,
755 \\};
756 \\pub export fn foo() void {}768 \\pub export fn foo() void {}
757 });769 });
758770
...@@ -1600,30 +1612,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1600,30 +1612,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1600 \\static char arr1[] = "hello";1612 \\static char arr1[] = "hello";
1601 \\char arr2[] = "hello";1613 \\char arr2[] = "hello";
1602 , &[_][]const u8{1614 , &[_][]const u8{
1603 \\pub export var arr0: [6]u8 = [6]u8{1615 \\pub export var arr0: [5:0]u8 = "hello".*;
1604 \\ 'h',1616 \\pub var arr1: [5:0]u8 = "hello".*;
1605 \\ 'e',1617 \\pub export var arr2: [5:0]u8 = "hello".*;
1606 \\ 'l',
1607 \\ 'l',
1608 \\ 'o',
1609 \\ 0,
1610 \\};
1611 \\pub var arr1: [6]u8 = [6]u8{
1612 \\ 'h',
1613 \\ 'e',
1614 \\ 'l',
1615 \\ 'l',
1616 \\ 'o',
1617 \\ 0,
1618 \\};
1619 \\pub export var arr2: [6]u8 = [6]u8{
1620 \\ 'h',
1621 \\ 'e',
1622 \\ 'l',
1623 \\ 'l',
1624 \\ 'o',
1625 \\ 0,
1626 \\};
1627 });1618 });
16281619
1629 cases.add("array initializer expr",1620 cases.add("array initializer expr",
...@@ -1667,37 +1658,36 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1667,37 +1658,36 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1667 \\ p,1658 \\ p,
1668 \\};1659 \\};
1669 , &[_][]const u8{1660 , &[_][]const u8{
1670 \\const enum_unnamed_1 = extern enum(c_int) {1661 \\pub const d = extern enum(c_int) {
1671 \\ a,1662 \\ a,
1672 \\ b,1663 \\ b,
1673 \\ c,1664 \\ c,
1674 \\ _,1665 \\ _,
1675 \\};1666 \\};
1676 \\pub const a = @enumToInt(enum_unnamed_1.a);1667 \\pub const a = @enumToInt(d.a);
1677 \\pub const b = @enumToInt(enum_unnamed_1.b);1668 \\pub const b = @enumToInt(d.b);
1678 \\pub const c = @enumToInt(enum_unnamed_1.c);1669 \\pub const c = @enumToInt(d.c);
1679 \\pub const d = enum_unnamed_1;1670 \\const enum_unnamed_1 = extern enum(c_int) {
1680 \\const enum_unnamed_2 = extern enum(c_int) {
1681 \\ e = 0,1671 \\ e = 0,
1682 \\ f = 4,1672 \\ f = 4,
1683 \\ g = 5,1673 \\ g = 5,
1684 \\ _,1674 \\ _,
1685 \\};1675 \\};
1686 \\pub const e = @enumToInt(enum_unnamed_2.e);1676 \\pub const e = @enumToInt(enum_unnamed_1.e);
1687 \\pub const f = @enumToInt(enum_unnamed_2.f);1677 \\pub const f = @enumToInt(enum_unnamed_1.f);
1688 \\pub const g = @enumToInt(enum_unnamed_2.g);1678 \\pub const g = @enumToInt(enum_unnamed_1.g);
1689 \\pub export var h: enum_unnamed_2 = @intToEnum(enum_unnamed_2, e);1679 \\pub export var h: enum_unnamed_1 = @intToEnum(enum_unnamed_1, e);
1690 \\const enum_unnamed_3 = extern enum(c_int) {1680 \\const enum_unnamed_2 = extern enum(c_int) {
1691 \\ i,1681 \\ i,
1692 \\ j,1682 \\ j,
1693 \\ k,1683 \\ k,
1694 \\ _,1684 \\ _,
1695 \\};1685 \\};
1696 \\pub const i = @enumToInt(enum_unnamed_3.i);1686 \\pub const i = @enumToInt(enum_unnamed_2.i);
1697 \\pub const j = @enumToInt(enum_unnamed_3.j);1687 \\pub const j = @enumToInt(enum_unnamed_2.j);
1698 \\pub const k = @enumToInt(enum_unnamed_3.k);1688 \\pub const k = @enumToInt(enum_unnamed_2.k);
1699 \\pub const struct_Baz = extern struct {1689 \\pub const struct_Baz = extern struct {
1700 \\ l: enum_unnamed_3,1690 \\ l: enum_unnamed_2,
1701 \\ m: d,1691 \\ m: d,
1702 \\};1692 \\};
1703 \\pub const enum_i = extern enum(c_int) {1693 \\pub const enum_i = extern enum(c_int) {
...@@ -1962,7 +1952,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1962,7 +1952,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1962 , &[_][]const u8{1952 , &[_][]const u8{
1963 \\pub export fn foo() c_int {1953 \\pub export fn foo() c_int {
1964 \\ var a: c_int = 5;1954 \\ var a: c_int = 5;
1965 \\ while (true) a = 2;1955 \\ while (true) {
1956 \\ a = 2;
1957 \\ }
1966 \\ while (true) {1958 \\ while (true) {
1967 \\ var a_1: c_int = 4;1959 \\ var a_1: c_int = 4;
1968 \\ a_1 = 9;1960 \\ a_1 = 9;
...@@ -1975,7 +1967,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1975,7 +1967,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1975 \\ var a_1: c_int = 2;1967 \\ var a_1: c_int = 2;
1976 \\ a_1 = 12;1968 \\ a_1 = 12;
1977 \\ }1969 \\ }
1978 \\ while (true) a = 7;1970 \\ while (true) {
1971 \\ a = 7;
1972 \\ }
1979 \\ return 0;1973 \\ return 0;
1980 \\}1974 \\}
1981 });1975 });
...@@ -2036,7 +2030,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2036,7 +2030,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2036 \\}2030 \\}
2037 , &[_][]const u8{2031 , &[_][]const u8{
2038 \\pub export fn bar() c_int {2032 \\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 \\ }
2040 \\ return if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6);2036 \\ return if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6);
2041 \\}2037 \\}
2042 });2038 });
...@@ -2417,7 +2413,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2417,7 +2413,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2417 \\pub const yes = [*c]u8;2413 \\pub const yes = [*c]u8;
2418 \\pub export fn foo() void {2414 \\pub export fn foo() void {
2419 \\ var a: yes = undefined;2415 \\ var a: yes = undefined;
2420 \\ if (a != null) _ = @as(c_int, 2);2416 \\ if (a != null) {
2417 \\ _ = @as(c_int, 2);
2418 \\ }
2421 \\}2419 \\}
2422 });2420 });
24232421
...@@ -2456,7 +2454,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2456,7 +2454,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2456 \\ b: c_int,2454 \\ b: c_int,
2457 \\};2455 \\};
2458 \\pub extern var a: struct_Foo;2456 \\pub extern var a: struct_Foo;
2459 \\pub export var b: f32 = 2;2457 \\pub export var b: f32 = 2.0;
2460 \\pub export fn foo() void {2458 \\pub export fn foo() void {
2461 \\ var c: [*c]struct_Foo = undefined;2459 \\ var c: [*c]struct_Foo = undefined;
2462 \\ _ = a.b;2460 \\ _ = a.b;
...@@ -2768,7 +2766,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2768,7 +2766,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2768 \\ var a = arg_a;2766 \\ var a = arg_a;
2769 \\ var i: c_int = 0;2767 \\ var i: c_int = 0;
2770 \\ while (a > @bitCast(c_uint, @as(c_int, 0))) {2768 \\ 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));
2772 \\ }2770 \\ }
2773 \\ return i;2771 \\ return i;
2774 \\}2772 \\}
...@@ -2788,7 +2786,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2788,7 +2786,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2788 \\ var a = arg_a;2786 \\ var a = arg_a;
2789 \\ var i: c_int = 0;2787 \\ var i: c_int = 0;
2790 \\ while (a > @bitCast(c_uint, @as(c_int, 0))) {2788 \\ 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));
2792 \\ }2790 \\ }
2793 \\ return i;2791 \\ return i;
2794 \\}2792 \\}
...@@ -3020,17 +3018,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -3020,17 +3018,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
3020 \\pub extern fn fn_bool(x: bool) void;3018 \\pub extern fn fn_bool(x: bool) void;
3021 \\pub extern fn fn_ptr(x: ?*c_void) void;3019 \\pub extern fn fn_ptr(x: ?*c_void) void;
3022 \\pub export fn call() void {3020 \\pub export fn call() void {
3023 \\ fn_int(@floatToInt(c_int, 3));3021 \\ fn_int(@floatToInt(c_int, 3.0));
3024 \\ fn_int(@floatToInt(c_int, 3));3022 \\ fn_int(@floatToInt(c_int, 3.0));
3025 \\ fn_int(@floatToInt(c_int, 3));3023 \\ fn_int(@floatToInt(c_int, 3.0));
3026 \\ fn_int(@as(c_int, 1094861636));3024 \\ fn_int(@as(c_int, 1094861636));
3027 \\ fn_f32(@intToFloat(f32, @as(c_int, 3)));3025 \\ fn_f32(@intToFloat(f32, @as(c_int, 3)));
3028 \\ fn_f64(@intToFloat(f64, @as(c_int, 3)));3026 \\ fn_f64(@intToFloat(f64, @as(c_int, 3)));
3029 \\ fn_char(@bitCast(u8, @truncate(i8, @as(c_int, '3'))));3027 \\ fn_char(@bitCast(u8, @truncate(i8, @as(c_int, '3'))));
3030 \\ fn_char(@bitCast(u8, @truncate(i8, @as(c_int, '\x01'))));3028 \\ fn_char(@bitCast(u8, @truncate(i8, @as(c_int, '\x01'))));
3031 \\ fn_char(@bitCast(u8, @truncate(i8, @as(c_int, 0))));3029 \\ fn_char(@bitCast(u8, @truncate(i8, @as(c_int, 0))));
3032 \\ fn_f32(3);3030 \\ fn_f32(3.0);
3033 \\ fn_f64(3);3031 \\ fn_f64(3.0);
3034 \\ fn_bool(@as(c_int, 123) != 0);3032 \\ fn_bool(@as(c_int, 123) != 0);
3035 \\ fn_bool(@as(c_int, 0) != 0);3033 \\ fn_bool(@as(c_int, 0) != 0);
3036 \\ fn_bool(@ptrToInt(fn_int) != 0);3034 \\ fn_bool(@ptrToInt(fn_int) != 0);
...@@ -3418,4 +3416,56 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -3418,4 +3416,56 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
3418 \\pub const MAY_NEED_PROMOTION_HEX = @import("std").meta.promoteIntLiteral(c_int, 0x80000000, .hexadecimal);3416 \\pub const MAY_NEED_PROMOTION_HEX = @import("std").meta.promoteIntLiteral(c_int, 0x80000000, .hexadecimal);
3419 \\pub const MAY_NEED_PROMOTION_OCT = @import("std").meta.promoteIntLiteral(c_int, 0o20000000000, .octal);3417 \\pub const MAY_NEED_PROMOTION_OCT = @import("std").meta.promoteIntLiteral(c_int, 0o20000000000, .octal);
3420 });3418 });
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 });
3421}3471}
tools/update_clang_options.zig+4
...@@ -332,6 +332,10 @@ const known_options = [_]KnownOpt{...@@ -332,6 +332,10 @@ const known_options = [_]KnownOpt{
332 .name = "s",332 .name = "s",
333 .ident = "strip",333 .ident = "strip",
334 },334 },
335 .{
336 .name = "dynamiclib",
337 .ident = "shared",
338 },
335};339};
336340
337const blacklisted_options = [_][]const u8{};341const blacklisted_options = [_][]const u8{};