Menu

#1037 G++ reports linker failure without any diagnostic being printed

v1.0 (example)
open
nobody
None
7
2026-09-04
2026-09-04
Jeremy
No

Hello,
I was using mingw_w64 installed on msys2, and I encountered a bug where G++ would print:

  • collect2.exe: error: ld returned 1 exit status or
  • collect2.exe: error: ld returned 5 exit status
    without any diagnostic being printed beforehand.
    Which exitcode is printed seems to be random, but when it is 5, the error is printed after a few seconds of hanging.

I created this ticket because I was unable to find one with a similar topic using the sourceforge search bar on the tickets page.

Here's the exact setup:
main.cpp:

#include <cstdio>

void foo();

int main() {
    printf("Hello world\n");
    foo();
}

Compile and link:

D:\project>g++ main.cpp -c -o main.o

D:\project>g++ main.o -o main.exe
collect2.exe: error: ld returned 1 exit status

D:\project>g++ main.o -o main.exe
collect2.exe: error: ld returned 5 exit status

D:\project>echo %ERRORLEVEL%
1

The linker should have diagnosed an unresolved symbol foo, but instead silently fails, with some weird varying exitcodes.
Inspecting this with process explorer, when I run the g++ command and the exit code is 1, I get the following process hierarchy:

conhost.exe

    - g++.exe
        - collect2.exe
            - ld.exe
                - ld.exe

However, when the exitcode is 5, the process hierarchy becomes:

conhost.exe
    - g++.exe
        - collect2.exe
            - ld.exe
                - ld.exe
                - WerFault.exe

I set ProcDump from SysInternals as the Just in time AeDebug debugger on my system, and reran the above commands. When the exit code is 1, ProcDump doesn't trigger. However, when the exit code is 5, it produces two dumps, 1 for each of the ld.exe processes, the first of which shows that the crash was caused by a windows Access Violation exception (analyzed with WinDbg). Unfortunately I do not have the knowledge to inspect more the dumps myself. I attached the two crash dumps to this ticket.

I tried to run the linker command with the -v flag:

D:\project>g++ -v main.o -o main.exe
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=D:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0/lto-wrapper.exe
Target: x86_64-w64-mingw32
Configured with: ../gcc-16.1.0/configure --prefix=/ucrt64 --with-local-prefix=/ucrt64/local --libexecdir=/ucrt64/lib --enable-bootstrap --enable-checking=release --with-arch=nocona --with-tune=generic --enable-mingw-wildcard --enable-languages=c,c++,ada,fortran,lto,objc,obj-c++,jit --enable-shared --enable-static --enable-libatomic --enable-threads=posix --enable-tls --enable-graphite --enable-fully-dynamic-string --enable-libstdcxx-backtrace=yes --enable-libstdcxx-filesystem-ts --enable-libstdcxx-time --disable-libstdcxx-pch --enable-libgomp --disable-libssp --disable-multilib --disable-rpath --disable-win32-registry --disable-nls --disable-werror --disable-symvers --with-libiconv --with-system-zlib --with-gmp=/ucrt64 --with-mpfr=/ucrt64 --with-mpc=/ucrt64 --with-isl=/ucrt64 --with-pkgversion='Rev5, Built by MSYS2 project' --with-bugurl=https://github.com/msys2/MINGW-packages/issues --with-gnu-as --with-gnu-ld --with-libstdcxx-zoneinfo=yes --disable-libstdcxx-debug --enable-lto --with-boot-ldflags=-static-libstdc++ --with-stage1-ldflags=-static-libstdc++
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 16.1.0 (Rev5, Built by MSYS2 project)
COMPILER_PATH=D:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0/;D:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/;D:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0/../../../../x86_64-w64-mingw32/bin/
LIBRARY_PATH=D:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0/;D:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/;D:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0/../../../../x86_64-w64-mingw32/lib/../lib/;D:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0/../../../../lib/;D:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0/../../../../x86_64-w64-mingw32/lib/;D:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0/../../../
COLLECT_GCC_OPTIONS='-v' '-o' 'tests/bin/test_al_alloc.exe' '-shared-libgcc' '-mtune=generic' '-march=nocona' '-dumpdir' 'tests/bin/test_al_alloc.'
 D:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0/collect2.exe -plugin D:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0/liblto_plugin.dll -plugin-opt=D:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0/lto-wrapper.exe -plugin-opt=-fresolution=C:\Users\jlefo\AppData\Local\Temp\ccItCCUS.res -plugin-opt=-pass-through=-lmingw32 -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lmingwex -plugin-opt=-pass-through=-lmsvcrt -plugin-opt=-pass-through=-lkernel32 -plugin-opt=-pass-through=-lpthread -plugin-opt=-pass-through=-ladvapi32 -plugin-opt=-pass-through=-lshell32 -plugin-opt=-pass-through=-luser32 -plugin-opt=-pass-through=-lkernel32 -plugin-opt=-pass-through=-lmingw32 -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lmingwex -plugin-opt=-pass-through=-lmsvcrt -plugin-opt=-pass-through=-lkernel32 -m i386pep -Bdynamic -o tests/bin/test_al_alloc.exe D:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0/../../../../lib/crt2.o D:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0/crtbegin.o -LD:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0 -LD:/Programs/x64/msys64/ucrt64/bin/../lib/gcc -LD:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0/../../../../x86_64-w64-mingw32/lib/../lib -LD:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0/../../../../lib -LD:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0/../../../../x86_64-w64-mingw32/lib -LD:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0/../../.. tests/bin/test_al_alloc.o -lstdc++ -lmingw32 -lgcc_s -lgcc -lmingwex -lmsvcrt -lkernel32 -lpthread -ladvapi32 -lshell32 -luser32 -lkernel32 -lmingw32 -lgcc_s -lgcc -lmingwex -lmsvcrt -lkernel32 D:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0/../../../../lib/default-manifest.o D:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0/crtend.o
collect2.exe: error: ld returned 1 exit status

Surprisingly, manually running the collect2.exe command that g++ prints here works fine and the unresolved symbol is diagnosed properly before the linker exits. That also seems to never trigger the previous collect2.exe: error: ld returned 5 exit status message.

Manually running collect2.exe with the same options printed by g++ earlier:

D:\project>D:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0/collect2.exe -plugin D:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0/liblto_plugin.dll -plugin-opt=D:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0/lto-wrapper.exe -plugin-opt=-fresolution=C:\Users\jlefo\AppData\Local\Temp\ccItCCUS.res -plugin-opt=-pass-through=-lmingw32 -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lmingwex -plugin-opt=-pass-through=-lmsvcrt -plugin-opt=-pass-through=-lkernel32 -plugin-opt=-pass-through=-lpthread -plugin-opt=-pass-through=-ladvapi32 -plugin-opt=-pass-through=-lshell32 -plugin-opt=-pass-through=-luser32 -plugin-opt=-pass-through=-lkernel32 -plugin-opt=-pass-through=-lmingw32 -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lmingwex -plugin-opt=-pass-through=-lmsvcrt -plugin-opt=-pass-through=-lkernel32 -m i386pep -Bdynamic -o tests/bin/test_al_alloc.exe D:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0/../../../../lib/crt2.o D:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0/crtbegin.o -LD:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0 -LD:/Programs/x64/msys64/ucrt64/bin/../lib/gcc -LD:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0/../../../../x86_64-w64-mingw32/lib/../lib -LD:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0/../../../../lib -LD:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0/../../../../x86_64-w64-mingw32/lib -LD:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0/../../.. tests/bin/test_al_alloc.o -lstdc++ -lmingw32 -lgcc_s -lgcc -lmingwex -lmsvcrt -lkernel32 -lpthread -ladvapi32 -lshell32 -luser32 -lkernel32 -lmingw32 -lgcc_s -lgcc -lmingwex -lmsvcrt -lkernel32 D:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0/../../../../lib/default-manifest.o D:/Programs/x64/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/16.1.0/crtend.o
D:\Programs\x64\msys64\ucrt64\bin/ld.exe: main.o:main.cpp:(.text+0x1d): undefined reference to `foo()'
collect2.exe: error: ld returned 1 exit status

D:\project>echo %ERRORLEVEL%
1

I suspect that the Access Violation exit code (0xC0000005) was somehow truncated and interpreted as just 5 by collect2.exe.

My computer runs Windows 11 version 25H2. It runs x86_64 and intel core i7 CPU.
G++ version information:

>g++ --version
g++ (Rev5, Built by MSYS2 project) 16.1.0
Copyright (C) 2026 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

More info that might be useful:

D:\project>where g++
D:\Programs\x64\msys64\ucrt64\bin\g++.exe
D:\Programs\x64\mingw64\bin\g++.exe

D:\project>where ld
D:\Programs\x64\msys64\ucrt64\bin\ld.exe
D:\Programs\x64\mingw64\bin\ld.exe

Environment creation steps:

  • download/install msys2 from their website (I installed it in a custom directory).
  • open ucrt command prompt
  • install mingw with pacman -S mingw-w64-ucrt-x86_64-gcc
  • Add (msys install directory)/ucrt64/bin to my path
  • Run the build from bare cmd.exe

I believe the issue is very important because missing linker diagnostics can very easily confuse users of MinGW. I also believe that calling the linker manually is not a good solution to the problem because it requires manually figuring out the exact command line arguments needed to link with the stdlib and the compiler's ABI.

Hope that this can be solved,
Regards,
jerem2360

2 Attachments

Discussion


Log in to post a comment.