msys2 / MINGW-packages

Package scripts for MinGW-w64 targets to build under MSYS2.
https://packages.msys2.org
BSD 3-Clause "New" or "Revised" License
2.29k stars 1.22k forks source link

std::filesystem::temp_directory_path fails on mingw32 but not with mingw64 #5598

Open nurelin opened 5 years ago

nurelin commented 5 years ago
#include <iostream>
#include <filesystem>

int main()
{
  std::wcout << std::filesystem::temp_directory_path() << "\n";
}

Mingw32:

test@test-PC MINGW32 ~
$ g++ -v
Using built-in specs.
COLLECT_GCC=C:\msys32_test\mingw32\bin\g++.exe
COLLECT_LTO_WRAPPER=C:/msys32_test/mingw32/bin/../lib/gcc/i686-w64-mingw32/9.1.0/lto-wrapper.exe
Target: i686-w64-mingw32
Configured with: ../gcc-9.1.0/configure --prefix=/mingw32 --with-local-prefix=/mingw32/local --build=i686-w64-mingw32 --host=i686-w64-mingw32 --target=i686-w64-mingw32 --with-native-system-header-dir=/mingw32/i686-w64-mingw32/include --libexecdir=/mingw32/lib --enable-bootstrap --with-arch=i686 --with-tune=generic --enable-languages=c,lto,c++,fortran,ada,objc,obj-c++ --enable-shared --enable-static --enable-libatomic --enable-threads=posix --enable-graphite --enable-fully-dynamic-string --enable-libstdcxx-filesystem-ts=yes --enable-libstdcxx-time=yes --disable-libstdcxx-pch --disable-libstdcxx-debug --disable-isl-version-check --enable-lto --enable-libgomp --disable-multilib --enable-checking=release --disable-rpath --disable-win32-registry --disable-nls --disable-werror --disable-symvers --enable-plugin --with-libiconv --with-system-zlib --with-gmp=/mingw32 --with-mpfr=/mingw32 --with-mpc=/mingw32 --with-isl=/mingw32 --with-pkgversion='Rev3, Built by MSYS2 project' --with-bugurl=https://sourceforge.net/projects/msys2 --with-gnu-as --with-gnu-ld --disable-sjlj-exceptions --with-dwarf2
Thread model: posix
gcc version 9.1.0 (Rev3, Built by MSYS2 project)

test@test-PC MINGW32 ~
$ g++ -std=c++17 test.cpp

test@test-PC MINGW32 ~
$ ./a.exe

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
terminate called after throwing an instance of 'std::filesystem::__cxx11::filesystem_error'
  what():  filesystem error: temp_directory_path

Mingw64:

test@test-PC MINGW64 ~
$ g++ -v
Using built-in specs.
COLLECT_GCC=C:\msys64\mingw64\bin\g++.exe
COLLECT_LTO_WRAPPER=C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/9.1.0/lto-wrapper.exe
Target: x86_64-w64-mingw32
Configured with: ../gcc-9.1.0/configure --prefix=/mingw64 --with-local-prefix=/mingw64/local --build=x86_64-w64-mingw32 --host=x86_64-w64-mingw32 --target=x86_64-w64-mingw32 --with-native-system-header-dir=/mingw64/x86_64-w64-mingw32/include --libexecdir=/mingw64/lib --enable-bootstrap --with-arch=x86-64 --with-tune=generic --enable-languages=c,lto,c++,fortran,ada,objc,obj-c++ --enable-shared --enable-static --enable-libatomic --enable-threads=posix --enable-graphite --enable-fully-dynamic-string --enable-libstdcxx-filesystem-ts=yes --enable-libstdcxx-time=yes --disable-libstdcxx-pch --disable-libstdcxx-debug --disable-isl-version-check --enable-lto --enable-libgomp --disable-multilib --enable-checking=release --disable-rpath --disable-win32-registry --disable-nls --disable-werror --disable-symvers --enable-plugin --with-libiconv --with-system-zlib --with-gmp=/mingw64 --with-mpfr=/mingw64 --with-mpc=/mingw64 --with-isl=/mingw64 --with-pkgversion='Rev3, Built by MSYS2 project' --with-bugurl=https://sourceforge.net/projects/msys2 --with-gnu-as --with-gnu-ld
Thread model: posix
gcc version 9.1.0 (Rev3, Built by MSYS2 project)

test@test-PC MINGW64 ~
$ g++ -std=c++17 test.cpp

test@test-PC MINGW64 ~
$ ./a.exe
"C:\\msys64\\tmp\\"

Am I the only one affected ?

nurelin commented 5 years ago

ok, i am not sure but I think this is caused by this line

https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/src/c%2B%2B17/fs_ops.cc#L1395

oscarfv commented 5 years ago

Why do you think the problem is with that line? The macro __MINGW64_VERSION_MAJOR has the same value for 32 and 64 bits variant (MINGW64 is about the Mingw-w64 project, not about 32/64 bits execution mode).

oscarfv commented 5 years ago

BTW, I can replicate the problem (after fixing the source code, please use copy&paste from the original test case).

Unfortunately, gdb does not help. catch throw does not work, and trying to step into the function doesn't work either. This is going to require a debug build of libstdc++.

nurelin commented 5 years ago

You are right, that makes no sense

nurelin commented 5 years ago

Ok, I have rebuilt mingw-w64-i686-gcc.

std::filesystem::__gnu_posix::stat (path=0xe774f8 L"C:\\Users\\nurelin\\AppData\\Local\\Temp\\", buffer=0x64fdbc) at ../../../../../../gcc-9.1.0/libstdc++-v3/src/c++17/../filesystem/ops-common.h:77
77        { return ::_wstat(path, buffer); }

This the call to _wstat which fails but I don't know if the function belongs to the mingw-w64-crt or to msvcrt.dll since I cannot step in the function.

oscarfv commented 5 years ago

@nurelin : is the pointer buffer valid before calling _wstat? That function is sensitive to the value of the macro _USE_32BIT_TIME_T. If the macro was defined where the structure pointed by buffer was allocated, it must be defined too on the call site to _wstat. Same for the undefined case.

I doubt that _wstat itself is the problem here.

nurelin commented 5 years ago
(gdb) p st
$2 = {st_dev = 1024, st_ino = 70, st_mode = 522, st_nlink = 32240, st_uid = 115, st_gid = -520, st_rdev = 6618804, st_size = 36, st_atime = 6618588, st_mtime = 0, st_ctime = 0}

(gdb) ptype st
type = struct _stat32 {
    _dev_t st_dev;
    _ino_t st_ino;
    unsigned short st_mode;
    short st_nlink;
    short st_uid;
    short st_gid;
    _dev_t st_rdev;
    _off_t st_size;
    __time32_t st_atime;
    __time32_t st_mtime;
    __time32_t st_ctime;
}

The values are wrong because I printed the variable when uninitialized. But from the types of the structure, it seems that it is using this

And since the status is defined by this typedef which point to this which would tells that the macro _USE_32BIT_TIME_T is likely to be defined.

But I do not know how to tell which version of *stat*() is called for sure. Maybe removing the inlining ?

Or do I need to recompile gcc after recompiling mingw-w64-i686-crt ?

oscarfv commented 5 years ago

That's 32 bit time_t indeed. You can try replacing the call to _wstat with a call to _wstat32 or _wstat32i64 depending on the size of the _off_t data member (32/64 bits) inside of std::filesystem::__gnu_posix::stat, rebuild libstdc++ and see if that the crash goes away.

And/Or you can add something like

printf("%d %d\n", &_wstat == &_wstat32, &_wstat == &_wstat32i64);

to check which variant is being called.

nurelin commented 5 years ago
In file included from ../../../../../gcc-9.1.0/libstdc++-v3/src/c++17/fs_ops.cc:57:
../../../../../gcc-9.1.0/libstdc++-v3/src/c++17/../filesystem/ops-common.h: In function 'int std::filesystem::__gnu_posix::stat(const wchar_t*, std::filesystem::__gnu_posix::stat_type*)':
../../../../../gcc-9.1.0/libstdc++-v3/src/c++17/../filesystem/ops-common.h:78:28: warning: self-comparison always evaluates to true [-Wtautological-compare]
   78 |    printf("%d %d", &_wstat == &_wstat32, &_wstat == &_wstat32i64);
      |                            ^~ ~~~~~~~~~
In file included from C:/msys32_test/mingw32/i686-w64-mingw32/include/wchar.h:424,
                 from C:/msys32_test/home/nurelin/MINGW-packages/mingw-w64-gcc/src/build-i686-w64-mingw32/i686-w64-mingw32/libstdc++-v3/include/cwchar:44,
                 from C:/msys32_test/home/nurelin/MINGW-packages/mingw-w64-gcc/src/build-i686-w64-mingw32/i686-w64-mingw32/libstdc++-v3/include/bits/postypes.h:40,
                 from C:/msys32_test/home/nurelin/MINGW-packages/mingw-w64-gcc/src/build-i686-w64-mingw32/i686-w64-mingw32/libstdc++-v3/include/iosfwd:40,
                 from C:/msys32_test/home/nurelin/MINGW-packages/mingw-w64-gcc/src/build-i686-w64-mingw32/i686-w64-mingw32/libstdc++-v3/include/system_error:40,
                 from C:/msys32_test/home/nurelin/MINGW-packages/mingw-w64-gcc/src/build-i686-w64-mingw32/i686-w64-mingw32/libstdc++-v3/include/bits/fs_fwd.h:35,
                 from C:/msys32_test/home/nurelin/MINGW-packages/mingw-w64-gcc/src/build-i686-w64-mingw32/i686-w64-mingw32/libstdc++-v3/include/filesystem:36,
                 from ../../../../../gcc-9.1.0/libstdc++-v3/src/c++17/fs_ops.cc:31:
../../../../../gcc-9.1.0/libstdc++-v3/src/c++17/../filesystem/ops-common.h:78:54: error: comparison between distinct pointer types 'int (__attribute__((cdecl)) *)(const wchar_t*, _stat32*)' and 'int (__attribute__((cdecl)) *)(const wchar_t*, _stati64*)' lacks a cast [-fpermissive]
   78 |    printf("%d %d", &_wstat == &_wstat32, &_wstat == &_wstat32i64);
      |                                                      ^~~~~~~~~~~
In file included from ../../../../../gcc-9.1.0/libstdc++-v3/src/c++17/fs_ops.cc:57,
                 from ../../../../../gcc-9.1.0/libstdc++-v3/src/c++17/cow-fs_ops.cc:26:
../../../../../gcc-9.1.0/libstdc++-v3/src/c++17/../filesystem/ops-common.h: In function 'int std::filesystem::__gnu_posix::stat(const wchar_t*, std::filesystem::__gnu_posix::stat_type*)':
../../../../../gcc-9.1.0/libstdc++-v3/src/c++17/../filesystem/ops-common.h:78:28: warning: self-comparison always evaluates to true [-Wtautological-compare]
   78 |    printf("%d %d", &_wstat == &_wstat32, &_wstat == &_wstat32i64);
      |                            ^~ ~~~~~~~~~
In file included from C:/msys32_test/mingw32/i686-w64-mingw32/include/wchar.h:424,
                 from C:/msys32_test/home/nurelin/MINGW-packages/mingw-w64-gcc/src/build-i686-w64-mingw32/i686-w64-mingw32/libstdc++-v3/include/cwchar:44,
                 from C:/msys32_test/home/nurelin/MINGW-packages/mingw-w64-gcc/src/build-i686-w64-mingw32/i686-w64-mingw32/libstdc++-v3/include/bits/postypes.h:40,
                 from C:/msys32_test/home/nurelin/MINGW-packages/mingw-w64-gcc/src/build-i686-w64-mingw32/i686-w64-mingw32/libstdc++-v3/include/iosfwd:40,
                 from C:/msys32_test/home/nurelin/MINGW-packages/mingw-w64-gcc/src/build-i686-w64-mingw32/i686-w64-mingw32/libstdc++-v3/include/system_error:40,
                 from C:/msys32_test/home/nurelin/MINGW-packages/mingw-w64-gcc/src/build-i686-w64-mingw32/i686-w64-mingw32/libstdc++-v3/include/bits/fs_fwd.h:35,
                 from C:/msys32_test/home/nurelin/MINGW-packages/mingw-w64-gcc/src/build-i686-w64-mingw32/i686-w64-mingw32/libstdc++-v3/include/filesystem:36,
                 from ../../../../../gcc-9.1.0/libstdc++-v3/src/c++17/fs_ops.cc:31,
                 from ../../../../../gcc-9.1.0/libstdc++-v3/src/c++17/cow-fs_ops.cc:26:
../../../../../gcc-9.1.0/libstdc++-v3/src/c++17/../filesystem/ops-common.h:78:54: error: comparison between distinct pointer types 'int (__attribute__((cdecl)) *)(const wchar_t*, _stat32*)' and 'int (__attribute__((cdecl)) *)(const wchar_t*, _stati64*)' lacks a cast [-fpermissive]
   78 |    printf("%d %d", &_wstat == &_wstat32, &_wstat == &_wstat32i64);
      |                                                      ^~~~~~~~~~~

Seems that the underlying function called is _wstat32() which further hints that the _USE_32BIT_TIME_T macro is defined.

I think that this _wstat() function should be called instead of the _wstat32() from msvcrt since it tries to fixup its argument using the _mingw_no_trailing_slash()function.

nurelin commented 5 years ago

I have something interesting:

In a mingw32 environment:

nurelin@PC MINGW32 ~
$ cat wstat_test.cpp
#include <iostream>
#include <sys/stat.h>

int main()
{
        std::wstring s { L"C:\\msys32_test\\tmp\\" };
        struct _stat32 buf;
        std::wcout << s << std::endl;
        int result = _wstat32(s.c_str(), &buf);
        std::cout << result << std::endl;

        std::wstring s2 { L"C:\\msys32_test\\tmp" };
        struct _stat32 buf2;
        std::wcout << s2 << std::endl;
        int result2 = _wstat32(s2.c_str(), &buf2);
        std::cout << result2 << std::endl;
}

nurelin@PC MINGW32 ~
$ g++ wstat_test.cpp

nurelin@PC MINGW32 ~
$ ./a.exe
C:\msys32_test\tmp\
-1
C:\msys32_test\tmp
0

but in a mingw64 environment:

nurelin@PC MINGW64 ~
$ cat wstat_test.cpp
#include <iostream>
#include <sys/stat.h>

int main()
{
        std::wstring s { L"C:\\msys32_test\\tmp\\" };
        struct _stat64i32 buf;
        std::wcout << s << std::endl;
        int result = _wstat64i32(s.c_str(), &buf);
        std::cout << result << std::endl;

        std::wstring s2 { L"C:\\msys32_test\\tmp" };
        struct _stat64i32 buf2;
        std::wcout << s2 << std::endl;
        int result2 = _wstat64i32(s2.c_str(), &buf2);
        std::cout << result2 << std::endl;
}

nurelin@PC MINGW64 ~
$ g++ wstat_test.cpp

nurelin@PC MINGW64 ~
$ ./a
C:\msys32_test\tmp\
0
C:\msys32_test\tmp
0

It seems that mingw-w64 has fixed the behavior of _wstat64i32() (https://sourceforge.net/p/mingw-w64/bugs/643/). Looks like _wstat32() could use something similar.

oscarfv commented 5 years ago

@nurelin : please make sure that the mingw-w64 folks know about this. Thanks.

mingwandroid commented 5 years ago

Pinging @katietz and @msarahan, though this is 32-bit only, and MSYS2, it might be nice to try to fix this as part of a final update of packages from MSYS2/mingw-64 i686, though I expect Kai would like to fix this anyway.

i686 GCC is rotting badly though (numerical precision is getting worse) and it's not just rotting on Windows, it is also the case on Linux IMHO.