Cross-Compile a Rust Application from Linux to Windows

Cross-compile a Rust application from Linux to Windows

Other answers, while technically correct, are more difficult than they need to be. There's no need to use rustc (in fact it's discouraged, just use cargo), you only need rustup, cargo and your distribution's mingw-w64.

Add the target (you can also change this for whatever target you're cross compiling for):

rustup target add x86_64-pc-windows-gnu

You can build your crate easily with:

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

No need for messing around with ~/.cargo/config or anything else.

EDIT: Just wanted to add that while you can use the above it can also sometimes be a headache. I wanted to add that the rust tools team also maintains a project called cross: https://github.com/rust-embedded/cross
This might be another solution that you want to look into

Crosscompiling Rust from Fedora Linux host to Windows target does not find dependencies

After a little thought I was able to solve the problem to cross compile projects in Rust from the Linux host to the Windows target without using cross and similar tools but simply by specifying specific target in the cargo. Of course testing and running the project will fail (if you have no any emulator like wine installed), but just creating the Windows target goes smoothly. BTW Rust was developed to be cross compiler initially and therefore having the full working project and passing all the tests in Linux one can be almost sure that a simple cross compilation of this project in Windows will not break it in any way.

Since I run Fedora Linux 31 all of the following applies to this distribution. But I do not think that there are significant differences among other Linux distributions. And I only consider pc-windows-gnu Rust targets, pc-windows-msvc targets are not of my interest.

First, make sure all the necessary MinGW packages for cross compilation are installed:

[pfemidi@pfemidi ~]$ rpm -qa | grep mingw | sort
mingw32-binutils-2.32-6.fc31.x86_64
mingw32-cpp-9.2.1-1.fc31.x86_64
mingw32-crt-6.0.0-2.fc31.noarch
mingw32-filesystem-110-1.fc31.noarch
mingw32-gcc-9.2.1-1.fc31.x86_64
mingw32-headers-6.0.0-2.fc31.noarch
mingw32-winpthreads-6.0.0-2.fc31.noarch
mingw32-winpthreads-static-6.0.0-2.fc31.noarch
mingw64-binutils-2.32-6.fc31.x86_64
mingw64-cpp-9.2.1-1.fc31.x86_64
mingw64-crt-6.0.0-2.fc31.noarch
mingw64-filesystem-110-1.fc31.noarch
mingw64-gcc-9.2.1-1.fc31.x86_64
mingw64-headers-6.0.0-2.fc31.noarch
mingw64-winpthreads-6.0.0-2.fc31.noarch
mingw64-winpthreads-static-6.0.0-2.fc31.noarch
mingw-binutils-generic-2.32-6.fc31.x86_64
mingw-filesystem-base-110-1.fc31.noarch
[pfemidi@pfemidi ~]$

Lets create the simplest project in the Rust language:

[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]$

Create the .cargo directory with the config file in the project directory with the following contents:

[pfemidi@pfemidi rust]$ cd foobar
[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]$

This step is necessary so that when building Windows targets Rust uses not the linker from the default gcc installed but the MinGW linker.

Now try to build the project as the i686-pc-windows-gnu target:

[pfemidi@pfemidi foobar]$ cargo build --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: "i686-w64-mingw32-gcc" "-Wl,--enable-long-section-names" "-fno-use-linker-plugin" "-Wl,--nxcompat" "-nostdlib"

...

= 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`.

To learn more, run the command again with --verbose.
[pfemidi@pfemidi foobar]$

Error! Got "undefined reference to __onexitend" and "undefined reference to __onexitbegin" in the crt2.o file. The fact is that Rust component for pc-windows-gnu target has been built with MinGW 6.3.0 but there is MinGW version 9.2.1 in Fedora Linux 31. So there is a difference in the CRT due compiler versions mismatch. Ok, lets copy crt2.o from Fedora Linux MinGW repository to the Rust directory for the i686-pc-windows-gnu component. And along with crt2.o we also copy dllcrt2.o, it is the entry point for dynamic libraries as crt2.o is for standalone executable files. Just in case, we save the original files:

[pfemidi@pfemidi foobar]$ cd ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/i686-pc-windows-gnu/lib/
[pfemidi@pfemidi lib]$ mv crt2.o crt2.o.ori
[pfemidi@pfemidi lib]$ mv dllcrt2.o dllcrt2.o.ori
[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]$

Now try to build the project as the i686-pc-windows-gnu target again:

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

It worked!

Now try to do the same for x86_64-pc-windows-gnu target:

[pfemidi@pfemidi foobar]$ cargo build --target x86_64-pc-windows-gnu
Compiling foobar v0.1.0 (/home/pfemidi/mywork/rust/foobar)
error: linking with `x86_64-w64-mingw32-gcc` failed: exit code: 1
|
= note: "x86_64-w64-mingw32-gcc" "-Wl,--enable-long-section-names" "-fno-use-linker-plugin" "-Wl,--nxcompat" "-nostdlib" "-m64"

...

= 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`.

To learn more, run the command again with --verbose.
[pfemidi@pfemidi foobar]$

The error is exactly the same as for the i686-pc-windows-gnu target was noted before. Lets copy the files crt2.o and dllcrt2.o by analogy with the 32bit target above:

[pfemidi@pfemidi foobar]$ cd ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-pc-windows-gnu/lib/
[pfemidi@pfemidi lib]$ mv crt2.o crt2.o.ori
[pfemidi@pfemidi lib]$ mv dllcrt2.o dllcrt2.o.ori
[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]$

And try to build the project as the x86_64-pc-windows-gnu target again:

[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 0.20s
[pfemidi@pfemidi foobar]$

Yes! It worked also! So now we can easily build targets for Windows using the standard cargo included in the Rust distribution! And for x86_64 targets this is completely true. But for x86 targets everything will be fine only until we use in our project any functions that panic (f.e. macro panic!, expect functions, etc.).

Lets add panic to our simple project:

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

Build it as x86_64 target:

[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 0.16s
[pfemidi@pfemidi foobar]$

It is going perfectly (and works just as well, I checked). Now try to do the same for the x86 target:

[pfemidi@pfemidi foobar]$ cargo build --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: "i686-w64-mingw32-gcc" "-Wl,--enable-long-section-names" "-fno-use-linker-plugin" "-Wl,--nxcompat" "-nostdlib"

...

= 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-0c029c00da54fbf5.rlib(panic_unwind-0c029c00da54fbf5.panic_unwind.2hgzd7yq-cgu.0.rcgu.o): in function `ZN12panic_unwind3imp5panic17h03027a0e504502cdE':
/rustc/73528e339aae0f17a15ffa49a8ac608f50c6cf14\/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-0c029c00da54fbf5.rlib(panic_unwind-0c029c00da54fbf5.panic_unwind.2hgzd7yq-cgu.0.rcgu.o): in function `rust_eh_unwind_resume':
/rustc/73528e339aae0f17a15ffa49a8ac608f50c6cf14\/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`.

To learn more, run the command again with --verbose.
[pfemidi@pfemidi foobar]$

Oops! We got "undefined reference to _Unwind_RaiseException" and "undefined reference to _Unwind_Resume" in the libpanic_unwind module of the Rust standard library. But this problem is also solved, though not as simple as the previous one which is a mere replacement of CRT crt2.o and dllcrt2.o files.

For the stack unwinding Rust uses the dwarf method for 32bit Windows targets and seh method for 64bit Windows targets while MinGW from the standard Fedora Linux distribution uses the sjlj method for 32bit Windows targets and seh method for 64bit Windows targets (read about the difference here). Therefore 64bit targets are linked by MinGW linker without any problem but for 32bit ones there are no necessary symbols and object files to be linked correctly. To get these files and symbols it's necessary to rebuild MinGW with dwarf support instead of sjlj support by default for 32bit Windows targets.

I will not go into details on how to rebuild MinGW here, I will say only one thing: after MinGW is rebuilt with the dwarf stack unwinding instead of sjlj method you need to pick only one file called libgcc_eh.a from the MinGW just built and put it in the library directory for the Rust i686-pc-windows-gnu target. After doing that projects with any panic functions will build with no errors not only for 64bit Windows targets, but also for 32bit ones:

[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 build --target i686-pc-windows-gnu
Compiling foobar v0.1.0 (/home/pfemidi/mywork/rust/foobar)
Finished dev [unoptimized + debuginfo] target(s) in 0.30s
[pfemidi@pfemidi foobar]$

Happy RUSTing! :-)

Compile Rust application for Windows with cross and sqlite3 dependecy

It is because it is not bundled by default with rusqlite. You could enable bundled through a feature gate. You will need to add this to your Cargo.toml file.

[features]
windows = ["rusqlite/bundled"]

or by linking to winsqlite3

[features]
windows = ["rusqlite/winsqlite3"]

Then when cross-compiling enable that feature in windows. Using the command line options

cross build --target x86_64-pc-windows-gnu --features windows

Cross-compiling Rust from Windows to ARM Linux

Thanks to @Notlikethat's comment:

a) Yes you need to provide your own GCC cross-compiler.

b) You can get one here (select a mingw32 build).

Just unzip linaro's GCC then point cargo to it:

[target.armv7-unknown-linux-gnueabihf]
linker = "C:/Users/me/gcc-linaro-5.3.1-2016.05-i686-mingw32_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc.exe"

It seems to work even though it is arm- and not armv7-. I guess linking doesn't depend on the ISA. Actually I haven't run it yet, but it builds without errors!

Edit:

You can now use armv7-unknown-linux-musleabihf instead and get an actually portable binary (i.e. it doesn't depend on the GNU C library which often causes compatibility issues).

Cross-compile gtk crate for Windows

I managed to compile it by using this awesome tool.

Basically it is dockerized Linux Fedora with all necessary dependencies pre-installed.



Related Topics



Leave a reply



Submit