Skip to content

Commit dd48537

Browse files
dpaoliellodlon
authored andcommitted
Add support for Windows ARM64
1 parent 92eab6f commit dd48537

File tree

25 files changed

+593
-43
lines changed

25 files changed

+593
-43
lines changed

.cargo/config.toml

+3
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ rustflags = ["-Ctarget-feature=+crt-static"]
33

44
[target.i686-pc-windows-msvc]
55
rustflags = ["-Ctarget-feature=+crt-static"]
6+
7+
[target.aarch64-pc-windows-msvc]
8+
rustflags = ["-Ctarget-feature=+crt-static"]

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
/dist-assets/openvpn.exe
2222
/dist-assets/aarch64-apple-darwin/
2323
/dist-assets/x86_64-apple-darwin/
24+
/dist-assets/aarch64-pc-windows-msvc/
2425
/windows/version.h
2526
/windows/**/bin/
2627
/windows/**/*.user

BuildInstructions.md

+26
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,21 @@ The host has to have the following installed:
110110

111111
[Git for Windows]: https://git-scm.com/download/win
112112

113+
### Experimental: Cross-compiling for ARM64
114+
115+
By default, the app will build for the host platform. It is also possible to cross-compile the app
116+
for ARM64 on x64. This requires:
117+
118+
- The ARM64 MSVC tools added to Visual Studio.
119+
120+
- `clang` (either directly from llvm.org or as part of Visual Studio) on the `PATH`.
121+
122+
- The `AArch64` target added to Rust:
123+
124+
```bash
125+
rustup target add aarch64-pc-windows-msvc
126+
```
127+
113128
## macOS
114129

115130
The host has to have the following installed:
@@ -146,6 +161,17 @@ variable to `aarch64-unknown-linux-gnu`:
146161
TARGETS="aarch64-unknown-linux-gnu" ./build.sh
147162
```
148163

164+
### Experimental: Windows
165+
166+
ARM64 Windows is not yet fully working or supported.
167+
168+
To cross-compile for ARM64 rather than the current architecture, set the `TARGETS` environment
169+
variable to `aarch64-pc-windows-msvc`:
170+
171+
```bash
172+
TARGETS="aarch64-pc-windows-msvc" ./build.sh
173+
```
174+
149175
## Notes on building on ARM64 Linux hosts
150176

151177
Due to inability to build the management interface proto files on ARM64 (see

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ Line wrap the file at 100 chars. Th
2626
- Add DAITA (Defence against AI-guided Traffic Analysis) setting for Linux and macOS.
2727
- Add `--json` flag to `mullvad status` CLI.
2828

29+
#### Windows
30+
- Add experimental support for Windows ARM64.
31+
2932
### Changed
3033
- Ignore obfuscation protocol constraints when the obfuscation mode is set to auto.
3134

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build-windows-modules.sh

+1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ function get_solution_output_path {
6868
case $build_target in
6969
"x86") echo "$solution_root/bin/Win32-$build_mode";;
7070
"x64") echo "$solution_root/bin/x64-$build_mode";;
71+
"ARM64") echo "$solution_root/bin/ARM64-$build_mode";;
7172
*)
7273
echo "Unknown build target: $build_target"
7374
exit 1

build.sh

+34-12
Original file line numberDiff line numberDiff line change
@@ -285,21 +285,43 @@ function build {
285285
sign_win "$destination"
286286
fi
287287
done
288+
289+
if [[ "$current_target" == "aarch64-pc-windows-msvc" ]]; then
290+
# TODO: We still ship x64 OpenVPN with ARM64, so we need an x64 talpid-openvpn-plugin
291+
# to include in the package.
292+
log_info "Workaround: building x64 talpid-openvpn-plugin"
293+
cargo build --target x86_64-pc-windows-msvc "${CARGO_ARGS[@]}" -p talpid-openvpn-plugin --lib
294+
cp "$CARGO_TARGET_DIR/x86_64-pc-windows-msvc/$RUST_BUILD_MODE/talpid_openvpn_plugin.dll" "dist-assets/aarch64-pc-windows-msvc/talpid_openvpn_plugin.dll"
295+
if [[ "$SIGN" == "true" ]]; then
296+
sign_win "dist-assets/talpid_openvpn_plugin.dll"
297+
fi
298+
fi
288299
}
289300

290301
if [[ "$(uname -s)" == "MINGW"* ]]; then
291-
log_header "Building C++ code in $CPP_BUILD_MODE mode"
292-
CPP_BUILD_MODES=$CPP_BUILD_MODE IS_RELEASE=$IS_RELEASE ./build-windows-modules.sh
293-
294-
if [[ "$SIGN" == "true" ]]; then
295-
CPP_BINARIES=(
296-
"windows/winfw/bin/x64-$CPP_BUILD_MODE/winfw.dll"
297-
"windows/driverlogic/bin/x64-$CPP_BUILD_MODE/driverlogic.exe"
298-
# The nsis plugin is always built in 32 bit release mode
299-
windows/nsis-plugins/bin/Win32-Release/*.dll
300-
)
301-
sign_win "${CPP_BINARIES[@]}"
302-
fi
302+
for t in "${TARGETS[@]:-"x86_64-pc-windows-msvc"}"; do
303+
case $t in
304+
x86_64-pc-windows-msvc) CPP_BUILD_TARGET=x64;;
305+
aarch64-pc-windows-msvc) CPP_BUILD_TARGET=ARM64;;
306+
*)
307+
log_error "Unknown Windows target: $t"
308+
exit 1
309+
;;
310+
esac
311+
312+
log_header "Building C++ code in $CPP_BUILD_MODE mode for $CPP_BUILD_TARGET"
313+
CPP_BUILD_MODES=$CPP_BUILD_MODE CPP_BUILD_TARGETS=$CPP_BUILD_TARGET IS_RELEASE=$IS_RELEASE ./build-windows-modules.sh
314+
315+
if [[ "$SIGN" == "true" ]]; then
316+
CPP_BINARIES=(
317+
"windows/winfw/bin/$CPP_BUILD_TARGET-$CPP_BUILD_MODE/winfw.dll"
318+
"windows/driverlogic/bin/$CPP_BUILD_TARGET-$CPP_BUILD_MODE/driverlogic.exe"
319+
# The nsis plugin is always built in 32 bit release mode
320+
windows/nsis-plugins/bin/Win32-Release/*.dll
321+
)
322+
sign_win "${CPP_BINARIES[@]}"
323+
fi
324+
done
303325
fi
304326

305327
for t in "${TARGETS[@]:-""}"; do

ci/buildserver-build.sh

+4-1
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,9 @@ function build_ref {
204204

205205
case "$(uname -s)" in
206206
MINGW*|MSYS_NT*)
207+
echo "Building ARM64 installers"
208+
target=aarch64-pc-windows-msvc artifact_dir=$artifact_dir build "${build_args[@]}" || return 1
209+
207210
echo "Packaging all PDB files..."
208211
find ./windows/ \
209212
./target/release/mullvad-daemon.pdb \
@@ -222,7 +225,7 @@ function build_ref {
222225
# Pipes all matching names and their new name to mv
223226
pushd "$artifact_dir"
224227
for original_file in MullvadVPN-*-dev-*{.deb,.rpm,.exe,.pkg}; do
225-
new_file=$(echo "$original_file" | sed -nE "s/^(MullvadVPN-.*-dev-.*)(_amd64\.deb|_x86_64\.rpm|_arm64\.deb|_aarch64\.rpm|\.exe|\.pkg)$/\1$version_suffix\2/p")
228+
new_file=$(echo "$original_file" | sed -nE "s/^(MullvadVPN-.*-dev-.*)(_amd64\.deb|_x86_64\.rpm|_arm64\.deb|_aarch64\.rpm|_x64\.exe|_arm64\.exe|\.pkg)$/\1$version_suffix\2/p")
226229
mv "$original_file" "$new_file"
227230
done
228231
popd

dist-assets/windows/installer.nsh

+5-5
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@
8686
!macro ExtractDriverlogic
8787

8888
SetOutPath "$PLUGINSDIR"
89-
File "${BUILD_RESOURCES_DIR}\..\windows\driverlogic\bin\x64-$%CPP_BUILD_MODE%\driverlogic.exe"
89+
File "${BUILD_RESOURCES_DIR}\..\windows\driverlogic\bin\$%CPP_BUILD_TARGET%-$%CPP_BUILD_MODE%\driverlogic.exe"
9090

9191
!macroend
9292

@@ -100,8 +100,8 @@
100100
!macro ExtractWireGuard
101101

102102
SetOutPath "$PLUGINSDIR"
103-
File "${BUILD_RESOURCES_DIR}\binaries\x86_64-pc-windows-msvc\wintun\wintun.dll"
104-
File "${BUILD_RESOURCES_DIR}\binaries\x86_64-pc-windows-msvc\wireguard-nt\mullvad-wireguard.dll"
103+
File "${BUILD_RESOURCES_DIR}\binaries\$%TARGET_TRIPLE%\wintun\wintun.dll"
104+
File "${BUILD_RESOURCES_DIR}\binaries\$%TARGET_TRIPLE%\wireguard-nt\mullvad-wireguard.dll"
105105

106106
!macroend
107107

@@ -115,8 +115,8 @@
115115
!macro ExtractMullvadSetup
116116

117117
SetOutPath "$PLUGINSDIR"
118-
File "${BUILD_RESOURCES_DIR}\mullvad-setup.exe"
119-
File "${BUILD_RESOURCES_DIR}\..\windows\winfw\bin\x64-$%CPP_BUILD_MODE%\winfw.dll"
118+
File "${BUILD_RESOURCES_DIR}\$%SETUP_SUBDIR%\mullvad-setup.exe"
119+
File "${BUILD_RESOURCES_DIR}\..\windows\winfw\bin\$%CPP_BUILD_TARGET%-$%CPP_BUILD_MODE%\winfw.dll"
120120

121121
!macroend
122122

gui/tasks/distribution.js

+60-13
Original file line numberDiff line numberDiff line change
@@ -132,26 +132,30 @@ const config = {
132132
target: [
133133
{
134134
target: 'nsis',
135-
arch: ['x64'],
135+
arch: getWindowsTargetArch(),
136136
},
137137
],
138-
artifactName: 'MullvadVPN-${version}.${ext}',
138+
artifactName: 'MullvadVPN-${version}_${arch}.${ext}',
139139
publisherName: 'Mullvad VPN AB',
140140
extraResources: [
141-
{ from: distAssets('mullvad.exe'), to: '.' },
142-
{ from: distAssets('mullvad-problem-report.exe'), to: '.' },
143-
{ from: distAssets('mullvad-daemon.exe'), to: '.' },
144-
{ from: distAssets('talpid_openvpn_plugin.dll'), to: '.' },
141+
{ from: distAssets(path.join(getWindowsDistSubdir(), 'mullvad.exe')), to: '.' },
142+
{ from: distAssets(path.join(getWindowsDistSubdir(), 'mullvad-problem-report.exe')), to: '.' },
143+
{ from: distAssets(path.join(getWindowsDistSubdir(), 'mullvad-daemon.exe')), to: '.' },
144+
{ from: distAssets(path.join(getWindowsDistSubdir(), 'talpid_openvpn_plugin.dll')), to: '.' },
145145
{
146-
from: root(path.join('windows', 'winfw', 'bin', 'x64-${env.CPP_BUILD_MODE}', 'winfw.dll')),
146+
from: root(path.join('windows', 'winfw', 'bin', getWindowsTargetArch() + '-${env.CPP_BUILD_MODE}', 'winfw.dll')),
147147
to: '.',
148148
},
149+
// TODO: OpenVPN does not have an ARM64 build yet.
149150
{ from: distAssets('binaries/x86_64-pc-windows-msvc/openvpn.exe'), to: '.' },
150-
{ from: distAssets('binaries/x86_64-pc-windows-msvc/apisocks5.exe'), to: '.' },
151-
{ from: distAssets('binaries/x86_64-pc-windows-msvc/wintun/wintun.dll'), to: '.' },
152-
{ from: distAssets('binaries/x86_64-pc-windows-msvc/split-tunnel/mullvad-split-tunnel.sys'), to: '.' },
151+
{ from: distAssets(path.join('binaries', getWindowsTargetSubdir(), 'apisocks5.exe')), to: '.' },
152+
{ from: distAssets(path.join('binaries', getWindowsTargetSubdir(), 'wintun/wintun.dll')), to: '.' },
153+
{
154+
from: distAssets(path.join('binaries', getWindowsTargetSubdir(), 'split-tunnel/mullvad-split-tunnel.sys')),
155+
to: '.'
156+
},
153157
{
154-
from: distAssets('binaries/x86_64-pc-windows-msvc/wireguard-nt/mullvad-wireguard.dll'),
158+
from: distAssets(path.join('binaries', getWindowsTargetSubdir(), 'wireguard-nt/mullvad-wireguard.dll')),
155159
to: '.',
156160
},
157161
{ from: distAssets('maybenot_machines'), to: '.' },
@@ -250,6 +254,19 @@ function packWin() {
250254
asarUnpack: ['build/assets/images/menubar-icons/win32/lock-*.ico'],
251255
beforeBuild: (options) => {
252256
process.env.CPP_BUILD_MODE = release ? 'Release' : 'Debug';
257+
process.env.CPP_BUILD_TARGET = options.arch;
258+
switch (options.arch) {
259+
case 'x64':
260+
process.env.TARGET_TRIPLE = 'x86_64-pc-windows-msvc';
261+
process.env.SETUP_SUBDIR = '.';
262+
break;
263+
case 'arm64':
264+
process.env.TARGET_TRIPLE = 'aarch64-pc-windows-msvc';
265+
process.env.SETUP_SUBDIR = 'aarch64-pc-windows-msvc';
266+
break;
267+
default:
268+
throw new Error(`Invalid or unknown target (only one may be specified)`);
269+
}
253270
return true;
254271
},
255272
afterAllArtifactBuild: (buildResult) => {
@@ -392,8 +409,38 @@ function root(relativePath) {
392409
return path.join(path.resolve(__dirname, '../../'), relativePath);
393410
}
394411

412+
function getWindowsDistSubdir() {
413+
if (targets === 'aarch64-pc-windows-msvc') {
414+
return targets;
415+
} else {
416+
return '';
417+
}
418+
}
419+
420+
function getWindowsTargetArch() {
421+
if (targets && process.platform === 'win32') {
422+
if (targets === 'aarch64-pc-windows-msvc') {
423+
return 'arm64';
424+
}
425+
throw new Error(`Invalid or unknown target (only one may be specified)`);
426+
}
427+
// Use host architecture (we assume this is x64 since building on Arm64 isn't supported).
428+
return 'x64';
429+
}
430+
431+
function getWindowsTargetSubdir() {
432+
if (targets && process.platform === 'win32') {
433+
if (targets === 'aarch64-pc-windows-msvc') {
434+
return targets;
435+
}
436+
throw new Error(`Invalid or unknown target (only one may be specified)`);
437+
}
438+
// Use host architecture (we assume this is x64 since building on Arm64 isn't supported).
439+
return 'x86_64-pc-windows-msvc';
440+
}
441+
395442
function getLinuxTargetArch() {
396-
if (targets) {
443+
if (targets && process.platform === 'linux') {
397444
if (targets === 'aarch64-unknown-linux-gnu') {
398445
return 'arm64';
399446
}
@@ -404,7 +451,7 @@ function getLinuxTargetArch() {
404451
}
405452

406453
function getLinuxTargetSubdir() {
407-
if (targets) {
454+
if (targets && process.platform === 'linux') {
408455
if (targets === 'aarch64-unknown-linux-gnu') {
409456
return targets;
410457
}

mullvad-daemon/src/exception_logging/win.rs

+55-4
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,7 @@ use std::{
1010
};
1111
use talpid_types::ErrorExt;
1212
use talpid_windows::process::{ModuleEntry, ProcessSnapshot};
13-
use winapi::{
14-
um::winnt::{CONTEXT_CONTROL, CONTEXT_INTEGER, CONTEXT_SEGMENTS},
15-
vc::excpt::EXCEPTION_EXECUTE_HANDLER,
16-
};
13+
use winapi::vc::excpt::EXCEPTION_EXECUTE_HANDLER;
1714
use windows_sys::Win32::{
1815
Foundation::{BOOL, HANDLE},
1916
System::{
@@ -219,7 +216,61 @@ unsafe extern "system" fn logging_exception_filter(info: *const EXCEPTION_POINTE
219216
EXCEPTION_EXECUTE_HANDLER
220217
}
221218

219+
#[cfg(target_arch = "aarch64")]
220+
fn get_context_info(context: &CONTEXT) -> String {
221+
use winapi::um::winnt::{CONTEXT_CONTROL, CONTEXT_FLOATING_POINT, CONTEXT_INTEGER};
222+
223+
let mut context_str = "Context:\n".to_string();
224+
225+
if context.ContextFlags & CONTEXT_CONTROL != 0 {
226+
writeln!(
227+
&mut context_str,
228+
"\n\tFp: {:#x?}\n \
229+
\tLr: {:#x?}\n \
230+
\tSp: {:#x?}\n \
231+
\tPc: {:#x?}\n \
232+
\tCpsr: {:#x?}",
233+
unsafe { context.Anonymous.Anonymous.Fp },
234+
unsafe { context.Anonymous.Anonymous.Lr },
235+
context.Sp,
236+
context.Pc,
237+
context.Cpsr
238+
)
239+
.unwrap();
240+
}
241+
242+
if context.ContextFlags & CONTEXT_INTEGER != 0 {
243+
context_str.push('\n');
244+
for x in 0..=28 {
245+
writeln!(&mut context_str, "\tX{}: {:#x?}", x, unsafe {
246+
context.Anonymous.X[x]
247+
})
248+
.unwrap();
249+
}
250+
}
251+
if context.ContextFlags & CONTEXT_FLOATING_POINT != 0 {
252+
writeln!(
253+
&mut context_str,
254+
"\n\tFpcr: {:#x?}\n \
255+
\tFpsr: {:#x?}",
256+
context.Fpcr, context.Fpsr
257+
)
258+
.unwrap();
259+
for q in 0..=31 {
260+
writeln!(&mut context_str, "\tQ{}: {:#x?}", q, unsafe {
261+
context.V[q].B
262+
})
263+
.unwrap();
264+
}
265+
}
266+
267+
context_str
268+
}
269+
270+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
222271
fn get_context_info(context: &CONTEXT) -> String {
272+
use winapi::um::winnt::{CONTEXT_CONTROL, CONTEXT_INTEGER, CONTEXT_SEGMENTS};
273+
223274
let mut context_str = "Context:\n".to_string();
224275

225276
if context.ContextFlags & CONTEXT_CONTROL != 0 {

0 commit comments

Comments
 (0)