Skip to content

MinGW: undefined reference to _imp___wassert #60912

Closed
@spl

Description

@spl

There is a very particular set of circumstances that leads to the following error when compiling some C++ files and linking them into a Rust executable targeted at stable-i686-pc-windows-gnu:

$ cargo run -vv
[...]
  = note: C:\...\out\libval.a(val.o): In function `ZN3val9do_assertEv':
          C:\....\src\val.h:8: undefined reference to `_imp___wassert'

Summary: I believe the Rust MinGW toolchain needs to be rebuilt or updated to include _imp___wassert (which I believe is the dynamically linked _wassert).

I used the repository https://github.com/spl/rust-assert-unicode-test on a Windows VM with MSYS2 and MingW32 installed. Unfortunately, I could not reproduce the issue on Travis-CI or AppVeyor, but it happens reliably on my VM. (I don't have MSVC installed, but I don't know if that's the reason.)

These are the key files:

src/val.h:

#include <cassert>
class val {
    int mv;
public:
    val(int v) : mv(v) { }
    void do_assert() { assert(mv > 0); }
};

src/val.cpp:

#include "val.h"
void use_val() {
    val v(42);
    v.do_assert();
}

build.rs:

extern crate cc;
fn main() {
    println!("cargo:rerun-if-env-changed=UNICODE");
    let mut cfg = cc::Build::new();
    if std::env::var_os("UNICODE").is_some() {
        cfg.define("UNICODE", None);
    }
    cfg.file("src/val.cpp").cpp(true).compile("libval.a");
}

For context, here is the snippet that defines assert() in assert.h from mingw-w64:

#ifdef NDEBUG
#if defined(_UNICODE) || defined(UNICODE)
#define assert(_Expression) \
  (void) \
  ((!!(_Expression)) || \
    (_wassert(_CRT_WIDE(#_Expression),_CRT_WIDE(__FILE__),__LINE__),0))
#else /* not unicode */
#define assert(_Expression) \
  (void) \
  ((!!(_Expression)) || \
    (_assert(#_Expression,__FILE__,__LINE__),0))
#endif /* _UNICODE||UNICODE */
#endif /* !defined (NDEBUG) */

With nothing in the environment, the build succeeds:

$ cargo build -vv
[...]
[rust-assert-unicode-test 0.1.0] running: "g++.exe" "-O0" "-ffunction-sections" "-fdata-sections" "-g" "-fno-omit-frame-pointer" "-m32" "-Wall" "-Wextra" "-o" "C:\\...\\rust-assert-unicode-test\\target\\debug\\build\\rust-assert-unicode-test-...\\out\\src\\val.o" "-c" "src/val.cpp"
[...]

It's useful to look at the symbols in the library:

$ nm target/debug/build/rust-assert-unicode-test-.../out/libval.a | grep assert
00000000 r .eh_frame$_ZN3val9do_assertEv
00000000 t .text$_ZN3val9do_assertEv
         U __imp___assert
00000000 T __ZN3val9do_assertEv

With -DUNICODE, the build fails:

$ export UNICODE=-DUNICODE
$ cargo build -vv
error: linking with `gcc` failed: exit code: 1
  |
  = note: "gcc" "-Wl,--enable-long-section-names" "-fno-use-linker-plugin" "-Wl,--nxcompat" "-nostdlib" "-Wl,--large-address-aware" "C:\\...\\.rustup\\toolchains\\stable-i686-pc-windows-gnu\\lib\\rustlib\\i686-pc-windows-gnu\\lib\\crt2.o" "C:\\...\\.rustup\\toolchains\\stable-i686-pc-windows-gnu\\lib\\rustlib\\i686-pc-windows-gnu\\lib\\rsbegin.o" "-L" "C:\\...\\.rustup\\toolchains\\stable-i686-pc-windows-gnu\\lib\\rustlib\\i686-pc-windows-gnu\\lib" "C:\\...\\rust-assert-unicode-test\\target\\debug\\deps\\rust_assert_unicode_test-....1y0whtr9r0u7l2ur.rcgu.o" "C:\\...\\rust-assert-unicode-test\\target\\debug\\deps\\rust_assert_unicode_test-....24fzsbft0yc0w6fe.rcgu.o" "C:\\...\\rust-assert-unicode-test\\target\\debug\\deps\\rust_assert_unicode_test-....2b0sh6ymgfums8kn.rcgu.o" "C:\\...\\rust-assert-unicode-test\\target\\debug\\deps\\rust_assert_unicode_test-....3vrz8j8szp1mh8k8.rcgu.o" "C:\\...\\rust-assert-unicode-test\\target\\debug\\deps\\rust_assert_unicode_test-....4mut1jzgau8wqplt.rcgu.o" "C:\\...\\rust-assert-unicode-test\\target\\debug\\deps\\rust_assert_unicode_test-....599jfg9iyzg2frg1.rcgu.o" "-o" "C:\\...\\rust-assert-unicode-test\\target\\debug\\deps\\rust_assert_unicode_test-....exe" "C:\\...\\rust-assert-unicode-test\\target\\debug\\deps\\rust_assert_unicode_test-....phl7t4fk0vvdm4y.rcgu.o" "-Wl,--gc-sections" "-nodefaultlibs" "-L" "C:\\...\\rust-assert-unicode-test\\target\\debug\\deps" "-L" "C:\\...\\rust-assert-unicode-test\\target\\debug\\build\\rust-assert-unicode-test-...\\out" "-L" "C:\\...\\.rustup\\toolchains\\stable-i686-pc-windows-gnu\\lib\\rustlib\\i686-pc-windows-gnu\\lib" "-Wl,-Bstatic" "-Wl,--whole-archive" "-lval" "-Wl,--no-whole-archive" "-Wl,-Bdynamic" "-lstdc++" "-Wl,--start-group" "-Wl,-Bstatic" "C:\\...\\.rustup\\toolchains\\stable-i686-pc-windows-gnu\\lib\\rustlib\\i686-pc-windows-gnu\\lib\\libstd-352224e4f0f52495.rlib" "C:\\...\\.rustup\\toolchains\\stable-i686-pc-windows-gnu\\lib\\rustlib\\i686-pc-windows-gnu\\lib\\libpanic_unwind-1df48bb698babe0f.rlib" "C:\\...\\.rustup\\toolchains\\stable-i686-pc-windows-gnu\\lib\\rustlib\\i686-pc-windows-gnu\\lib\\libbacktrace_sys-e33d2aecefe6e52a.rlib" "C:\\...\\.rustup\\toolchains\\stable-i686-pc-windows-gnu\\lib\\rustlib\\i686-pc-windows-gnu\\lib\\libunwind-6cd6f50b682d440b.rlib" "C:\\...\\.rustup\\toolchains\\stable-i686-pc-windows-gnu\\lib\\rustlib\\i686-pc-windows-gnu\\lib\\librustc_demangle-f70dc1fec692eefd.rlib" "C:\\...\\.rustup\\toolchains\\stable-i686-pc-windows-gnu\\lib\\rustlib\\i686-pc-windows-gnu\\lib\\liblibc-659fb6a5c340607f.rlib" "C:\\...\\.rustup\\toolchains\\stable-i686-pc-windows-gnu\\lib\\rustlib\\i686-pc-windows-gnu\\lib\\liballoc-9d1d98d6712272cf.rlib" "C:\\...\\.rustup\\toolchains\\stable-i686-pc-windows-gnu\\lib\\rustlib\\i686-pc-windows-gnu\\lib\\librustc_std_workspace_core-899502000089d7f2.rlib" "C:\\...\\.rustup\\toolchains\\stable-i686-pc-windows-gnu\\lib\\rustlib\\i686-pc-windows-gnu\\lib\\libcore-dcb03e66209691e3.rlib" "-Wl,--end-group" "C:\\...\\.rustup\\toolchains\\stable-i686-pc-windows-gnu\\lib\\rustlib\\i686-pc-windows-gnu\\lib\\libcompiler_builtins-d3bba173fb7c9a29.rlib" "-Wl,-Bdynamic" "-ladvapi32" "-lws2_32" "-luserenv" "-Wl,-Bstatic" "-lgcc_eh" "-lpthread" "-Wl,-Bdynamic" "-lmingwex" "-lmingw32" "-lgcc" "-lmsvcrt" "-lmsvcrt" "-luser32" "-lkernel32" "C:\\...\\.rustup\\toolchains\\stable-i686-pc-windows-gnu\\lib\\rustlib\\i686-pc-windows-gnu\\lib\\rsend.o"
  = note: C:\...\rust-assert-unicode-test\target\debug\build\rust-assert-unicode-test-...\out\libval.a(val.o): In function `ZN3val9do_assertEv':
          C:\...\rust-assert-unicode-test/src/val.h:8: undefined reference to `_imp___wassert'

Looking at the symbols in the library confirms that _imp___wassert is there (and that __imp___assert is not, as would be expected):

$ nm target/debug/build/rust-assert-unicode-test-.../out/libval.a | grep assert
00000000 r .eh_frame$_ZN3val9do_assertEv
00000000 t .text$_ZN3val9do_assertEv
         U __imp___wassert
00000000 T __ZN3val9do_assertEv

One way to work around this issue is to link directly to the MinGW msvcrt library:

$ RUSTFLAGS="-Clink-arg=C:/msys32/mingw32/i686-w64-mingw32/lib/libmsvcrt.a" cargo build -vv

Looking at what was linked with Process Monitor, I found that the bundled msvcrt library was being used. And, indeed, that library is missing __imp___wassert:

$ nm C:/.../.rustup/toolchains/stable-i686-pc-windows-gnu/lib/rustlib/i686-pc-windows-gnu/lib/libmsvcrt.a | grep assert
00000000 I __imp___assert

Looking at the symbols in MinGW's msvcrt, I can now see why linking to it works:

$ nm C:/msys32/mingw32/i686-w64-mingw32/lib/libmsvcrt.a | grep assert
00000000 T __assert
00000000 I __imp___assert
lib32_libmsvcrt_os_a-wassert.o:
         U __assert
00000000 D __imp___wassert
000000d0 t _init_wassert
00000000 t _mingw_wassert

There are no definitions of the __imp___wassert symbol in C:/.../.rustup/toolchains/stable-i686-pc-windows-gnu/lib/rustlib/i686-pc-windows-gnu/lib/*.a.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-linkageArea: linking into static, shared libraries and binariesC-bugCategory: This is a bug.O-windows-gnuToolchain: GNU, Operating system: WindowsT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions