Hello, Together with Kirill's help, I prepared changes which fixes mingw-w64 testcases when compiled against system os msvcrt20.dll and msvcrt40.dll libraries. All mingw-w64 fixes are in the attachment. The patches file was generated from the commit 55735d71ed1187098df4d80a8f6c98a7735b1cd5.
In attachment is also change which enable running CI testcases against msvcrt20.dll and msvcrt40.dll libraries. Kirill run all changes on github CI, tests passed, here is link: https://github.com/maiddaisuki/mingw-w64/actions/runs/21314225214 Pali
>From 14ec7c12615eca9086d94323f6923a019c8c5a23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <[email protected]> Date: Mon, 5 Jan 2026 19:43:38 +0100 Subject: [PATCH 01/10] crt: Provide emulation of _telli64 function for msvcrt20.dll Commit 13c45d08ab27c5ee122da6ebf8f587d5670b8c69 enabled _telli64 emulation for crtdll.dll and msvcrt10.dll, but incorrectly stated that _telli64 is available since msvcrt20.dll. But it is not available in msvcrt20.dll, it is available since msvcrt40.dll. --- mingw-w64-crt/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mingw-w64-crt/Makefile.am b/mingw-w64-crt/Makefile.am index 6810e6df2659..a6f0543ef537 100644 --- a/mingw-w64-crt/Makefile.am +++ b/mingw-w64-crt/Makefile.am @@ -894,7 +894,6 @@ src_pre_msvcrt20=\ misc/__p__winver.c \ misc/__timezone.c \ misc/__tzname.c \ - stdio/_telli64.c \ stdio/_wfindfirst32.c \ stdio/_wfindnext32.c \ stdio/_wstat32.c \ @@ -913,6 +912,7 @@ src_pre_msvcrt40=\ stdio/_fstat32i64.c \ stdio/_lseeki64.c \ stdio/_stat32i64.c \ + stdio/_telli64.c \ stdio/_wfindfirst32i64.c \ stdio/_wfindnext32i64.c \ stdio/_wstat32i64.c \ -- 2.20.1 >From 703afeb45a4cb392b77bd2843a58dd941f2a2743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <[email protected]> Date: Mon, 5 Jan 2026 20:41:49 +0100 Subject: [PATCH 02/10] crt: Fix overflow detection in mingw-w64 fgetpos() emulation Recent Windows versions have different behavior of 32-bit fgetpos and returns -1 with errno set to EINVAL when position does not fit into 32 bits. So check for EINVAL and convert it to EOVERFLOW. This fixes t_fseeki64.c test when compiled against msvcrt20.dll and run on Windows 10 against the system os version of msvcrt20.dll. --- mingw-w64-crt/stdio/fgetpos.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mingw-w64-crt/stdio/fgetpos.c b/mingw-w64-crt/stdio/fgetpos.c index aa3feda108e1..8c93deabf365 100644 --- a/mingw-w64-crt/stdio/fgetpos.c +++ b/mingw-w64-crt/stdio/fgetpos.c @@ -26,7 +26,10 @@ int __cdecl fgetpos(FILE *__restrict__ _File, fpos_t *__restrict__ _Pos) * then fgetpos32() returns -1, sets pos32 to -1 and do not change errno. * Non-zero value in [63:32] bits of 64-bit position offset does not trigger * any error condition. In other error cases fgetpos32() returns -1, - * sets pos32 to -1 and errno to error value. */ + * sets pos32 to -1 and errno to error value. + * Recent Windows versions have different behavior and returns -1 with errno + * set to EINVAL when position does not fit into 32 bits. + */ errno = 0; ret = fgetpos32(_File, &pos32); @@ -34,6 +37,8 @@ int __cdecl fgetpos(FILE *__restrict__ _File, fpos_t *__restrict__ _Pos) { if (errno == 0) /* This detects overflow when [31] bit is set. */ errno = EOVERFLOW; + else if (errno == EINVAL) /* Value does not fit into 32 bits. */ + errno = EOVERFLOW; *_Pos = pos32; return ret; } -- 2.20.1 >From ba4d3f5b2c47c61284d92f8e0a019244f2e3cb21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <[email protected]> Date: Tue, 6 Jan 2026 00:06:39 +0100 Subject: [PATCH 03/10] crt: Fix overflow detection in mingw-w64 _lseeki64() emulation On recent Windows versions when the current file position does not fit into the 32 bits then the _lseek() function returns error without setting new position. Reflect this behavior in _lseeki64() emulation and fallback to SetFilePointer() on error. This fixes t_lseeki64.c test when compiled against msvcrt20.dll and run on Windows 10 against the system os version of msvcrt20.dll. --- mingw-w64-crt/stdio/_lseeki64.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/mingw-w64-crt/stdio/_lseeki64.c b/mingw-w64-crt/stdio/_lseeki64.c index 747a390c9929..215689bd56b5 100644 --- a/mingw-w64-crt/stdio/_lseeki64.c +++ b/mingw-w64-crt/stdio/_lseeki64.c @@ -12,7 +12,7 @@ /* Define 64-bit _lseeki64() function via 32-bit CRT _lseek() function and 64-bit WinAPI SetFilePointer() function */ __int64 __cdecl _lseeki64(int fd, __int64 offset, int whence) { - const BOOL offset_overflowed = offset < LONG_MIN || offset > LONG_MAX; + BOOL offset_overflowed = offset < LONG_MIN || offset > LONG_MAX; if (!offset_overflowed) { @@ -21,10 +21,13 @@ __int64 __cdecl _lseeki64(int fd, __int64 offset, int whence) * Just the return value of new offset is truncated to 32 bits. * _lseek() does not signal truncation, so check here just for offset * overflow for SEEK_SET. For all other cases call SetFilePointer() - * to retrieve new 64-bit file offset. */ + * to retrieve new 64-bit file offset. On recent Windows versions + * when the current file position does not fit into the 32 bits then + * the _lseek() function returns error without setting new position. */ errno = 0; long ret = _lseek(fd, offset, whence); - if (whence == SEEK_SET || (ret == -1 && errno)) + offset_overflowed = (ret == -1 && errno); + if (whence == SEEK_SET && !offset_overflowed) return ret; } -- 2.20.1 >From 093c1d06f9fa085deb79d68e6c2c8c0ac2f4bd71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <[email protected]> Date: Tue, 6 Jan 2026 00:35:18 +0100 Subject: [PATCH 04/10] crt: test: Convert assert_winapi_seek to macro with caller information In assert message include handle/offset/method parameters and line number of caller. This allows to figure out on which line the assert failed. --- mingw-w64-crt/testcases/t_fseeki64.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/mingw-w64-crt/testcases/t_fseeki64.c b/mingw-w64-crt/testcases/t_fseeki64.c index 1daa2d13bcb7..4cc36a978344 100644 --- a/mingw-w64-crt/testcases/t_fseeki64.c +++ b/mingw-w64-crt/testcases/t_fseeki64.c @@ -5,11 +5,12 @@ #include <fcntl.h> #include <windows.h> -static void assert_winapi_seek(HANDLE handle, LONGLONG offset, DWORD method) { - LARGE_INTEGER li = { .QuadPart = offset }; - li.LowPart = SetFilePointer(handle, li.LowPart, &li.HighPart, method); - assert(li.LowPart != INVALID_SET_FILE_POINTER || GetLastError() == NO_ERROR); -} +#define assert_winapi_seek(handle, offset, method) do { \ + LARGE_INTEGER li = { .QuadPart = (offset) }; \ + li.LowPart = SetFilePointer((handle), li.LowPart, &li.HighPart, (method)); \ + if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) \ + _assert("SetFilePointer(" #handle ", LARGE_INTEGER(" #offset "), " #method ") failed", __FILE__, __LINE__); \ +} while (0) int main() { FILE *file; -- 2.20.1 >From a3dcc002a35932540bc624fd0dc81ff86940cad3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <[email protected]> Date: Tue, 6 Jan 2026 00:37:25 +0100 Subject: [PATCH 05/10] crt: test: Fix t_fseeki64.c for pre-msvcrt40 on DISK FULL pre-msvcrt40 _fseeki64() function calls fflush() which may return ENOSPC error. Handle this error and skip the test. --- mingw-w64-crt/testcases/t_fseeki64.c | 29 +++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/mingw-w64-crt/testcases/t_fseeki64.c b/mingw-w64-crt/testcases/t_fseeki64.c index 4cc36a978344..3b6b794b2e0f 100644 --- a/mingw-w64-crt/testcases/t_fseeki64.c +++ b/mingw-w64-crt/testcases/t_fseeki64.c @@ -12,6 +12,17 @@ _assert("SetFilePointer(" #handle ", LARGE_INTEGER(" #offset "), " #method ") failed", __FILE__, __LINE__); \ } while (0) +#define assert_crt_fseeki64(expected, ...) do { \ + __int64 ret = _fseeki64(__VA_ARGS__); \ + if (ret == -1 && errno == ENOSPC) { \ + /* skip the test for DISK FULL error */ \ + printf("DISK FULL\n"); \ + return 77; \ + } \ + if (ret != (expected)) \ + _assert(#expected " == _fseeki64(" #__VA_ARGS__ ")", __FILE__, __LINE__); \ +} while (0) + int main() { FILE *file; HANDLE handle; @@ -26,49 +37,49 @@ int main() { assert(_setmode(fileno(file), _O_TEXT) != -1); /* absolute offset which fits into 32-bit signed type */ - assert(_fseeki64(file, 0x10, SEEK_SET) == 0); + assert_crt_fseeki64(0, file, 0x10, SEEK_SET); assert(fputs("A\n", file) >= 0); assert(fputs("A", file) >= 0); /* relative offset which fits into 32-bit signed type, but absolute does not */ - assert(_fseeki64(file, 0x7FFFFFFF, SEEK_CUR) == 0); + assert_crt_fseeki64(0, file, 0x7FFFFFFF, SEEK_CUR); assert(fputs("B\n", file) >= 0); assert(fputs("B", file) >= 0); /* absolute offset which fits into 32-bit unsigned type but does not into 32-bit signed type */ - assert(_fseeki64(file, 0x90000000, SEEK_SET) == 0); + assert_crt_fseeki64(0, file, 0x90000000, SEEK_SET); assert(fputs("C\n", file) >= 0); assert(fputs("C", file) >= 0); /* TODO: SEEK_CUR does not work when current position does not fit into 32-bit signed type for pre-msvcrt40 */ #if __MSVCRT_VERSION__ >= 0x400 /* relative offset which fits into 32-bit unsigned type but absolute does not */ - assert(_fseeki64(file, 0xFFFFFFFF, SEEK_CUR) == 0); + assert_crt_fseeki64(0, file, 0xFFFFFFFF, SEEK_CUR); assert(fputs("D\n", file) >= 0); assert(fputs("D", file) >= 0); #else - assert(_fseeki64(file, 0xFFFFFFFF, SEEK_CUR) == -1); + assert_crt_fseeki64(-1, file, 0xFFFFFFFF, SEEK_CUR); assert(errno == EOVERFLOW); #endif /* absolute offset which does not fit into 32-bit unsigned type */ - assert(_fseeki64(file, 0x100000000, SEEK_SET) == 0); + assert_crt_fseeki64(0, file, 0x100000000, SEEK_SET); assert(fputs("E\n", file) >= 0); assert(fputs("E", file) >= 0); /* TODO: SEEK_CUR does not work when current position does not fit into 32-bit signed type for pre-msvcrt40 */ #if __MSVCRT_VERSION__ >= 0x400 /* relative offset which does not fit into 32-bit unsigned type */ - assert(_fseeki64(file, 0x100000000, SEEK_CUR) == 0); + assert_crt_fseeki64(0, file, 0x100000000, SEEK_CUR); assert(fputs("F\n", file) >= 0); assert(fputs("F", file) >= 0); #else - assert(_fseeki64(file, 0x100000000, SEEK_CUR) == -1); + assert_crt_fseeki64(-1, file, 0x100000000, SEEK_CUR); assert(errno == EOVERFLOW); #endif /* last absolute write, without the nul byte */ - assert(_fseeki64(file, 0x300000000, SEEK_SET) == 0); + assert_crt_fseeki64(0, file, 0x300000000, SEEK_SET); assert(fputs("_\n", file) >= 0); assert(fputs("_", file) >= 0); -- 2.20.1 >From 2b5b19961f884fc944bcb51814045fc35fef3e41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <[email protected]> Date: Sat, 17 Jan 2026 12:16:46 +0100 Subject: [PATCH 06/10] headers: Remove #ifdef _UCRT around quick_exit and at_quick_exit C++ header files are trying to put quick_exit and at_quick_exit functions into :: or std:: namespace. If those functions are not declared then compilation is failing with error: error: 'at_quick_exit' has not been declared in '::' And it does not matter if those functions are used or not. --- mingw-w64-headers/crt/stdlib.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/mingw-w64-headers/crt/stdlib.h b/mingw-w64-headers/crt/stdlib.h index 794ba7790575..78fa25741659 100644 --- a/mingw-w64-headers/crt/stdlib.h +++ b/mingw-w64-headers/crt/stdlib.h @@ -273,9 +273,7 @@ _CRTIMP int __cdecl ___mb_cur_max_func(void); #define _CRT_TERMINATE_DEFINED void __cdecl __MINGW_NOTHROW exit(int _Code) __MINGW_ATTRIB_NORETURN; void __cdecl __MINGW_NOTHROW _exit(int _Code) __MINGW_ATTRIB_NORETURN; -#ifdef _UCRT void __cdecl __MINGW_NOTHROW quick_exit(int _Code) __MINGW_ATTRIB_NORETURN; -#endif #if !defined __NO_ISOCEXT /* extern stub in static libmingwex.a */ /* C99 function name */ @@ -309,9 +307,7 @@ _CRTIMP int __cdecl ___mb_cur_max_func(void); #endif int __cdecl atexit(void (__cdecl *)(void)); -#ifdef _UCRT int __cdecl at_quick_exit(void (__cdecl *)(void)); -#endif #ifndef _CRT_ATOF_DEFINED #define _CRT_ATOF_DEFINED double __cdecl atof(const char *_String); -- 2.20.1 >From 6cf3888b54da14aac79cb093f36445ba004a9c89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <[email protected]> Date: Sat, 17 Jan 2026 14:07:23 +0100 Subject: [PATCH 07/10] crt: test: Skip t_utime.c test on msvcrt10-msvcrt40 Currently the t_utime.c test for msvcrt10.dll, msvcrt20.dll and msvcrt40.dll builds is failing because mingw-w64 emulation of _futime64() and _fstat64() functions is not compatible with native 32-bit time variant of those functions. Skip the test for now. --- mingw-w64-crt/testcases/t_utime.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mingw-w64-crt/testcases/t_utime.c b/mingw-w64-crt/testcases/t_utime.c index dc0895c23b3a..95aebceab460 100644 --- a/mingw-w64-crt/testcases/t_utime.c +++ b/mingw-w64-crt/testcases/t_utime.c @@ -16,6 +16,15 @@ int main() { struct _stat64 st64; + /* mingw-w64 64-bit _futime64() and _fstat64() functions are not compatible + * with msvcrt10-40 32-bit futime() and fstat() functions and test is failing. + * Skip it for now until mingw-w64 functions are fixed. + */ +#if __MSVCRT_VERSION__ >= 0x100 && __MSVCRT_VERSION__ <= 0x400 + return 77; +#endif + + /* prepare temporary file */ file = tmpfile(); -- 2.20.1 >From 11e82c1db681bbe62ecbc140053e0a89f0d0bd35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <[email protected]> Date: Sat, 20 Dec 2025 17:24:17 +0100 Subject: [PATCH 08/10] crt: Provide rand_s for all CRT libraries Function rand_s() is used by mingw-w64 ssp stack protector code and also by mingw-w64 mkstemp implementation, so include mingw-w64 emulation of rand_s() function into all CRT import libraries. Pre-msvcr80 CRT libraries do not have this function available at all. --- mingw-w64-crt/Makefile.am | 1 + mingw-w64-crt/secapi/rand_s.c | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/mingw-w64-crt/Makefile.am b/mingw-w64-crt/Makefile.am index a6f0543ef537..22f0bd5a7d0f 100644 --- a/mingw-w64-crt/Makefile.am +++ b/mingw-w64-crt/Makefile.am @@ -1000,6 +1000,7 @@ src_pre_msvcr80=\ misc/wassert.c \ misc/wcsnlen.c \ secapi/getenv_s.c \ + secapi/rand_s.c \ secapi/wcstok_s.c \ stdio/_findfirst64i32.c \ stdio/_findnext64i32.c \ diff --git a/mingw-w64-crt/secapi/rand_s.c b/mingw-w64-crt/secapi/rand_s.c index 52700ba4fa5f..2873fe8cd3ac 100644 --- a/mingw-w64-crt/secapi/rand_s.c +++ b/mingw-w64-crt/secapi/rand_s.c @@ -3,7 +3,10 @@ #include <windows.h> #include <ntsecapi.h> #include <errno.h> + +#ifdef __LIBMSVCRT_OS__ #include <msvcrt.h> +#endif static BOOLEAN (WINAPI *pRtlGenRandom)(void*,ULONG); @@ -24,11 +27,16 @@ rand_s(unsigned int *val) static errno_t __cdecl init_rand_s(unsigned int *val) { - int (__cdecl *func)(unsigned int*); + int (__cdecl *func)(unsigned int*) = NULL; +#ifdef __LIBMSVCRT_OS__ func = (void*)GetProcAddress(__mingw_get_msvcrt_handle(), "rand_s"); +#endif + if(!func) { func = mingw_rand_s; + /* Function RtlGenRandom() is located in library advapi32.dll under + * symbol "SystemFunction036" and is available since Windows XP. */ pRtlGenRandom = (void*)GetProcAddress(LoadLibraryW(L"advapi32.dll"), "SystemFunction036"); } -- 2.20.1 >From a8a982c43a8e5117653f5b75920d07cf7e237747 Mon Sep 17 00:00:00 2001 From: Kirill Makurin <[email protected]> Date: Sat, 17 Jan 2026 19:43:19 +0900 Subject: [PATCH 09/10] Try fix C++ linking --- mingw-w64-crt/Makefile.am | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mingw-w64-crt/Makefile.am b/mingw-w64-crt/Makefile.am index 22f0bd5a7d0f..3b81a53b0796 100644 --- a/mingw-w64-crt/Makefile.am +++ b/mingw-w64-crt/Makefile.am @@ -195,6 +195,7 @@ src_msvcrt_common=\ stdio/snwprintf_alias.c \ stdio/swprintf.c \ stdio/swprintf_alias.c \ + stdio/vsnprintf.c \ stdio/vsnprintf_alias.c \ stdio/vsnwprintf_alias.c \ stdio/vswprintf.c \ @@ -440,6 +441,7 @@ src_ucrtbase=\ misc/output_format.c \ misc/ucrt-access.c \ stdio/msvcr80plus_ftruncate64.c \ + stdio/vsnprintf.c \ stdio/ucrt___local_stdio_printf_options.c \ stdio/ucrt___local_stdio_scanf_options.c \ stdio/ucrt__scprintf.c \ @@ -1291,7 +1293,7 @@ src_libmingwex=\ \ stdio/snprintf.c stdio/snwprintf.c stdio/truncate.c stdio/truncate64.c \ stdio/ulltoa.c stdio/ulltow.c stdio/vasprintf.c \ - stdio/vsnprintf.c stdio/vsnwprintf.c \ + stdio/vsnwprintf.c \ stdio/wtoll.c stdio/mingw_asprintf.c stdio/mingw_vasprintf.c # these go into both 32 and 64 bit x86 versions: -- 2.20.1 >From 3650623c69797dd31b69de6d2bc3dde551d0875d Mon Sep 17 00:00:00 2001 From: Kirill Makurin <[email protected]> Date: Mon, 5 Jan 2026 23:46:28 +0900 Subject: [PATCH 10/10] Test msvcrt20.dll and msvcrt40.dll --- .github/workflows/build.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 001c4a818b54..2fc0627d598e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -700,6 +700,22 @@ jobs: make -j$(nproc) install make -j$(nproc) check -k -O --no-print-directory TESTS= make -j$(nproc) check -k -O --no-print-directory || { cat testcases/test-suite.log; exit 1; } + - name: Run testcases for msvcrt20.dll + if: ${{ matrix.arch == 'i686' && matrix.crt == 'ucrt' }} + run: | + export PATH=/gcc-mingw/bin:$PATH + cd mingw-w64-crt/build + make -j$(nproc) -C testcases clean + make -j$(nproc) check -k -O --no-print-directory CFLAGS="-mcrtdll=msvcrt20" CXXFLAGS="-mcrtdll=msvcrt20" LDFLAGS='-static' TESTS= + make -j$(nproc) check -k -O --no-print-directory CFLAGS="-mcrtdll=msvcrt20" CXXFLAGS="-mcrtdll=msvcrt20" LDFLAGS='-static' || { cat testcases/test-suite.log; exit 1; } + - name: Run testcases for msvcrt40.dll + if: ${{ matrix.arch == 'i686' && matrix.crt == 'ucrt' }} + run: | + export PATH=/gcc-mingw/bin:$PATH + cd mingw-w64-crt/build + make -j$(nproc) -C testcases clean + make -j$(nproc) check -k -O --no-print-directory CFLAGS="-mcrtdll=msvcrt40" CXXFLAGS="-mcrtdll=msvcrt40" LDFLAGS='-static' TESTS= + make -j$(nproc) check -k -O --no-print-directory CFLAGS="-mcrtdll=msvcrt40" CXXFLAGS="-mcrtdll=msvcrt40" LDFLAGS='-static' || { cat testcases/test-suite.log; exit 1; } - name: Build and test winpthreads run: | export PATH=/gcc-mingw/bin:$PATH -- 2.20.1
_______________________________________________ Mingw-w64-public mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/mingw-w64-public
