Cross compiling Rust for Windows executables from Linux

Probably it won’t be very surprising if I’m here on the Habr IT site, I will say that I sometimes indulge in programming.


My main OS is Linux, but sometimes I have to collect executable files for Windows as well. And of course, I don’t really want to reboot into Windows just for exe assembly. There are no problems with the C and C ++ languages, for a long time there is a cross-compiler MinGWthat does this very well. About Pythonand Javaeven not worth mentioning, cross-platform in them initially. But last year I decided to try such a newfangled language as Rust. When assembling the executable file using the package manager included in the Rust distribution kit, it cargoseems to be enough to specify a key --targetwith which to specify the resulting processor, architecture and ABI and, when building from Linux, get exe, which will be the standard executable file for Windows. But trying to do so:


cargo build --target x86_64-pc-windows-gnu

I received only linker error messages:


error: linking with `gcc` failed: exit code: 1

[...]

  = note: /usr/bin/ld: unrecognized option '--nxcompat'
          /usr/bin/ld: use the --help option for usage information
          collect2: error: ld returned 1 exit status

error: aborting due to previous error

error: could not compile `foobar`.

If anyone is interested in how I overcame this and now I can easily cross-compile Rust programs for Windows without leaving Linux, welcome to cat.


Disclaimer

32bit 64bit pc-windows-gnu, pc-windows-msvc . Linux, , Fedora Linux 31, Linux . Rust The Rust toolchain installer, Fedora Rust , nightly Rust, , , .


First of all, we make sure that we have set the necessary goals by running the following command:


rustup target list

We get a list of all possible goals, and the goals that we have set:


aarch64-apple-ios
aarch64-fuchsia
[...]
i686-pc-windows-gnu (installed)
[...]
i686-unknown-linux-gnu (installed)
[...]
x86_64-pc-windows-gnu (installed)
x86_64-unknown-linux-gnu (installed)
[...]

To create executable files for Windows from Linux, we need targets i686-pc-windows-gnufor 32bit exe and x86_64-pc-windows-gnu64bit exe. If these goals are not marked as (installed), then we deliver them using the command


rustup target add _

After we make sure that we have a cross compiler installed by MinGWrunning


rpm -qa | grep mingw

or another package manager for our Linux distribution:


mingw32-gcc-9.2.1-1.fc31.x86_64
mingw32-binutils-2.32-6.fc31.x86_64
mingw64-gcc-9.2.1-1.fc31.x86_64
mingw-binutils-generic-2.32-6.fc31.x86_64
mingw-filesystem-base-110-1.fc31.noarch
mingw64-winpthreads-6.0.0-2.fc31.noarch
mingw32-winpthreads-6.0.0-2.fc31.noarch
mingw32-crt-6.0.0-2.fc31.noarch
mingw64-binutils-2.32-6.fc31.x86_64
mingw64-crt-6.0.0-2.fc31.noarch
mingw64-filesystem-110-1.fc31.noarch
mingw32-filesystem-110-1.fc31.noarch
mingw32-cpp-9.2.1-1.fc31.x86_64
mingw64-headers-6.0.0-2.fc31.noarch
mingw32-headers-6.0.0-2.fc31.noarch
mingw64-cpp-9.2.1-1.fc31.x86_64

If not, MinGWinstall the necessary packages by running


sudo dnf install mingw32-gcc mingw64-gcc

, (, - Test-Driven Development, :-)


Rust:


[pfemidi@pfemidi rust]$ cargo new foobar
     Created binary (application) `foobar` package
[pfemidi@pfemidi rust]$ cat foobar/src/main.rs 
fn main() {
    println!("Hello, world!");
}
[pfemidi@pfemidi rust]$

Linux:


[pfemidi@pfemidi foobar]$ cargo run
   Compiling foobar v0.1.0 (/home/pfemidi/mywork/rust/foobar)
    Finished dev [unoptimized + debuginfo] target(s) in 1.65s
     Running `target/debug/foobar`
Hello, world!
[pfemidi@pfemidi foobar]$ 

. x86_64-pc-windows-gnu:


cargo build --target x86_64-pc-windows-gnu

:


error: linking with `gcc` failed: exit code: 1

[...]

  = note: /usr/bin/ld: unrecognized option '--nxcompat'
          /usr/bin/ld: use the --help option for usage information
          collect2: error: ld returned 1 exit status

error: aborting due to previous error

error: could not compile `foobar`.

, MinGW, gcc. , .cargo config :


[pfemidi@pfemidi foobar]$ mkdir .cargo
[pfemidi@pfemidi foobar]$ cat > .cargo/config
[target.i686-pc-windows-gnu]
linker = "i686-w64-mingw32-gcc"
ar = "i686-w64-mingw32-ar"

[target.x86_64-pc-windows-gnu]
linker = "x86_64-w64-mingw32-gcc"
ar = "x86_64-w64-mingw32-ar"
[pfemidi@pfemidi foobar]$

Windows gcc, MinGW.


:


cargo build --target x86_64-pc-windows-gnu

, x86_64-w64-mingw32-gcc:


error: linking with `x86_64-w64-mingw32-gcc` failed: exit code: 1

[...]

  = note: /usr/lib/gcc/x86_64-w64-mingw32/9.2.1/../../../../x86_64-w64-mingw32/bin/ld: cannot find -lpthread
          collect2: error: ld returned 1 exit status

error: aborting due to previous error

error: could not compile `foobar`.

, Rust - , mingw32-winpthreads mingw64-winpthreads, dnf mingw32-gcc mingw64-gcc mingw32-winpthreads-static mingw64-winpthreads-static, -lpthread . :


sudo dnf install mingw??-winpthreads-static

:


cargo build --target x86_64-pc-windows-gnu

! :


error: linking with `x86_64-w64-mingw32-gcc` failed: exit code: 1

[...]

  = note: /usr/lib/gcc/x86_64-w64-mingw32/9.2.1/../../../../x86_64-w64-mingw32/bin/ld: /home/pfemidi/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-pc-windows-gnu/lib/crt2.o:crtexe.c:(.rdata$.refptr.__onexitbegin[.refptr.__onexitbegin]+0x0): undefined reference to `__onexitbegin'
          /usr/lib/gcc/x86_64-w64-mingw32/9.2.1/../../../../x86_64-w64-mingw32/bin/ld: /home/pfemidi/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-pc-windows-gnu/lib/crt2.o:crtexe.c:(.rdata$.refptr.__onexitend[.refptr.__onexitend]+0x0): undefined reference to `__onexitend'
          collect2: error: ld returned 1 exit status

error: aborting due to previous error

error: could not compile `foobar`.

__onexitbegin __onexitend ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-pc-windows-gnu/lib/crt2.o, x86_64-pc-windows-gnu. , , Rust, Rust, Rust : Rust Windows, pc-windows-gnu, MinGW 6.3.0, Fedora Linux 31 MinGW 9.2.1, CRT. Ok, crt2.o MinGW Rust x86_64-pc-windows-gnu. crt2.o dllcrt2.o, :


[pfemidi@pfemidi foobar]$ cd ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-pc-windows-gnu/lib/
[pfemidi@pfemidi lib]$ cp /usr/x86_64-w64-mingw32/sys-root/mingw/lib/crt2.o .
[pfemidi@pfemidi lib]$ cp /usr/x86_64-w64-mingw32/sys-root/mingw/lib/dllcrt2.o .
[pfemidi@pfemidi lib]$ cd -
/home/pfemidi/mywork/rust/foobar
[pfemidi@pfemidi foobar]$ 

Rust:


pfemidi@pfemidi foobar]$ cargo build --target x86_64-pc-windows-gnu
   Compiling foobar v0.1.0 (/home/pfemidi/mywork/rust/foobar)
    Finished dev [unoptimized + debuginfo] target(s) in 4.46s
[pfemidi@pfemidi foobar]$

! ! .. wine, :


[pfemidi@pfemidi foobar]$ cargo run --target x86_64-pc-windows-gnu
    Finished dev [unoptimized + debuginfo] target(s) in 0.38s
     Running `target/x86_64-pc-windows-gnu/debug/foobar.exe`
Hello, world!
[pfemidi@pfemidi foobar]$

! 32bit Windows, run build:


error: linking with `i686-w64-mingw32-gcc` failed: exit code: 1

[...]

  = note: /usr/lib/gcc/i686-w64-mingw32/9.2.1/../../../../i686-w64-mingw32/bin/ld: /home/pfemidi/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/i686-pc-windows-gnu/lib/crt2.o:crtexe.c:(.text+0x75): undefined reference to `__onexitend'
          /usr/lib/gcc/i686-w64-mingw32/9.2.1/../../../../i686-w64-mingw32/bin/ld: /home/pfemidi/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/i686-pc-windows-gnu/lib/crt2.o:crtexe.c:(.text+0x7a): undefined reference to `__onexitbegin'
          collect2: error: ld returned 1 exit status

error: aborting due to previous error

error: could not compile `foobar`.

__onexitbegin __onexitend ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/i686-pc-windows-gnu/lib/crt2.o , , 64bit crt2.o dllcrt2.o , MinGW Fedora:


[pfemidi@pfemidi foobar]$ cd ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/i686-pc-windows-gnu/lib/
[pfemidi@pfemidi lib]$ cp /usr/i686-w64-mingw32/sys-root/mingw/lib/crt2.o .
[pfemidi@pfemidi lib]$ cp /usr/i686-w64-mingw32/sys-root/mingw/lib/dllcrt2.o .
[pfemidi@pfemidi lib]$ cd -
/home/pfemidi/mywork/rust/foobar
[pfemidi@pfemidi foobar]$ 

:


[pfemidi@pfemidi foobar]$ 
[pfemidi@pfemidi foobar]$ cargo run --target i686-pc-windows-gnu
   Compiling foobar v0.1.0 (/home/pfemidi/mywork/rust/foobar)
    Finished dev [unoptimized + debuginfo] target(s) in 5.12s
     Running `target/i686-pc-windows-gnu/debug/foobar.exe`
Hello, world!
[pfemidi@pfemidi foobar]$

.


, (macro panic!, expect ..) 32bit Windows. 64bit , 32bit .


:


[pfemidi@pfemidi foobar]$ cat src/main.rs 
fn main() {
    println!("Hello, world!");
    panic!("I'm panicked!");    //   !
}
[pfemidi@pfemidi foobar]

64bit Windows:


[pfemidi@pfemidi foobar]$ cargo run --target x86_64-pc-windows-gnu
   Compiling foobar v0.1.0 (/home/pfemidi/mywork/rust/foobar)
    Finished dev [unoptimized + debuginfo] target(s) in 2.95s
     Running `target/x86_64-pc-windows-gnu/debug/foobar.exe`
Hello, world!
thread 'main' panicked at 'I'm panicked!', src/main.rs:3:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
[pfemidi@pfemidi foobar]$

, , . , 32bit Windows.


:


[pfemidi@pfemidi foobar]$ cargo run --target i686-pc-windows-gnu
   Compiling foobar v0.1.0 (/home/pfemidi/mywork/rust/foobar)
error: linking with `i686-w64-mingw32-gcc` failed: exit code: 1

[...]

  = note: /usr/lib/gcc/i686-w64-mingw32/9.2.1/../../../../i686-w64-mingw32/bin/ld: /home/pfemidi/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/i686-pc-windows-gnu/lib/libpanic_unwind-1a1fb2d4d34efaf8.rlib(panic_unwind-1a1fb2d4d34efaf8.panic_unwind.2hbcqjo8-cgu.0.rcgu.o): in function `ZN12panic_unwind3imp5panic17hdaabfe6326236dacE':
          /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8\/src\libpanic_unwind/gcc.rs:73: undefined reference to `_Unwind_RaiseException'
          /usr/lib/gcc/i686-w64-mingw32/9.2.1/../../../../i686-w64-mingw32/bin/ld: /home/pfemidi/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/i686-pc-windows-gnu/lib/libpanic_unwind-1a1fb2d4d34efaf8.rlib(panic_unwind-1a1fb2d4d34efaf8.panic_unwind.2hbcqjo8-cgu.0.rcgu.o): in function `rust_eh_unwind_resume':
          /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8\/src\libpanic_unwind/gcc.rs:327: undefined reference to `_Unwind_Resume'
          collect2: error: ld returned 1 exit status

error: aborting due to previous error

error: could not compile `foobar`.

, _Unwind_RaiseException _Unwind_Resume libpanic Rust.


, , Rust, . .


Rust Dwarf 32bit Windows SEH 64bit Windows, MinGW Fedora Linux SJLJ 32bit Windows SEH 64bit Windows ( ). 64bit , 32bit . MinGW Dwarf SJLJ 32bit Windows.


I won’t go into details on how to reassemble MinGW, it’s not so difficult and not so interesting ( configureyou need to run it with a parameter --disable-sjlj-exceptions, the rest is trivial), I’ll say only one thing: after MinGWrebuilding with the stack unwinding, Dwarfinstead of SJLJit you need to take only one file called libgcc_eh.aand put it in the library directory for the purpose i686-pc-windows-gnu. After that, projects in which panic functions are used will begin to be collected not only for 64bit Windows purposes, but also for 32bit:


[pfemidi@pfemidi foobar]$ cd ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/i686-pc-windows-gnu/lib/
[pfemidi@pfemidi lib]$ cp ~/rpmbuild/BUILD/gcc-9.2.1-20190827/build_win32/i686-w64-mingw32/libgcc/libgcc_eh.a .
[pfemidi@pfemidi lib]$ cd -
/home/pfemidi/mywork/rust/foobar
[pfemidi@pfemidi foobar]$ cargo run --target i686-pc-windows-gnu
   Compiling foobar v0.1.0 (/home/pfemidi/mywork/rust/foobar)
    Finished dev [unoptimized + debuginfo] target(s) in 4.57s
     Running `target/i686-pc-windows-gnu/debug/foobar.exe`
Hello, world!
thread 'main' panicked at 'I'm panicked!', src/main.rs:3:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
[pfemidi@pfemidi foobar]$ 

Somehow like this.


All Articles