Meson: Meson misdetects some functions with mingw-w64

Created on 2 Jun 2018  Â·  18Comments  Â·  Source: mesonbuild/meson

Meson detects for example strndup as available even though it is not, with mingw-w64 and clang as compiler and lld as linker.

Running compile:
Working directory:  /tmp/tmpdupt2xhm
Command line:  x86_64-w64-mingw32-gcc -L/home/epirat/Documents/Crosscompile/VLC/vlc/contrib/x86_64-w64-mingw32/lib /tmp/tmpdupt2xhm/testfile.c -pipe -o /tmp/tmpdupt2xhm/output.exe -O0 -std=gnu11 

Code:
 #define _REENTRANT 1
#define _THREAD_SAFE 1
#define _GNU_SOURCE 1
#define _POSIX_PTHREAD_SEMANTICS 1
#define __EXTENSIONS__ 1
#define _BSD_SOURCE 1
#define _FILE_OFFSET_BITS 64
#define UNICODE 1
#define _UNICODE 1
#define _WIN32_WINNT 0x0601
#define _WIN32_IE 0x0600
#define __USE_MINGW_ANSI_STDIO 1
#define _ISOC99_SOURCE 1
#define _ISOC11_SOURCE 1
#define _POSIX_SOURCE 1
#define _POSIX_C_SOURCE 200809L
#define _XOPEN_SOURCE 700
#define _XOPEN_SOURCE_EXTENDED 1
#define _BSD_SOURCE 1
#define _SVID_SOURCE 1
#include <string.h>
#include <limits.h>

        #if defined __stub_strndup || defined __stub___strndup
        fail fail fail this function is not going to work
        #endif

int main() {
            void *a = (void*) &strndup;
            long b = (long) a;
            return (int) b;
        }
Compiler stdout:

Compiler stderr:
 /tmp/tmpdupt2xhm/testfile.c:29:32: warning: implicitly declaring library function 'strndup' with type 'char *(const char *, unsigned long long)' [-Wimplicit-function-declaration]
            void *a = (void*) &strndup;
                               ^
/tmp/tmpdupt2xhm/testfile.c:29:32: note: include the header <string.h> or explicitly provide a declaration for 'strndup'
1 warning generated.
lld-link: error: /tmp/testfile-750040.o: undefined symbol: strndup
clang-7.0: error: linker command failed with exit code 1 (use -v to see invocation)

Running compile:
Working directory:  /tmp/tmpaytbse8x
Command line:  x86_64-w64-mingw32-gcc -L/home/epirat/Documents/Crosscompile/VLC/vlc/contrib/x86_64-w64-mingw32/lib /tmp/tmpaytbse8x/testfile.c -pipe -o /tmp/tmpaytbse8x/output.exe -O0 -std=gnu11 

Code:
 #define _REENTRANT 1
#define _THREAD_SAFE 1
#define _GNU_SOURCE 1
#define _POSIX_PTHREAD_SEMANTICS 1
#define __EXTENSIONS__ 1
#define _BSD_SOURCE 1
#define _FILE_OFFSET_BITS 64
#define UNICODE 1
#define _UNICODE 1
#define _WIN32_WINNT 0x0601
#define _WIN32_IE 0x0600
#define __USE_MINGW_ANSI_STDIO 1
#define _ISOC99_SOURCE 1
#define _ISOC11_SOURCE 1
#define _POSIX_SOURCE 1
#define _POSIX_C_SOURCE 200809L
#define _XOPEN_SOURCE 700
#define _XOPEN_SOURCE_EXTENDED 1
#define _BSD_SOURCE 1
#define _SVID_SOURCE 1
#include <string.h>
        int main() {
        #ifdef __has_builtin
            #if !__has_builtin(__builtin_strndup)
                #error "__builtin_strndup not found"
            #endif
        #elif ! defined(strndup)
            /* Check for __builtin_strndup only if no includes were added to the
             * prefix above, which means no definition of strndup can be found.
             * We would always check for this, but we get false positives on
             * MSYS2 if we do. Their toolchain is broken, but we can at least
             * give them a workaround. */
            #if 0
                __builtin_strndup;
            #else
                #error "No definition for __builtin_strndup found in the prefix"
            #endif
        #endif
        }
Compiler stdout:

Compiler stderr:

Checking for function "strndup": YES
windows bug compilers

All 18 comments

The __has_builtin(__builtin_strndup) is true which makes the second test succeed even though strndup is not actually usable and linking will fail when used.

It's a bit crazy how broken the MinGW toolchain is. This problem of partially-exposed unusable functions is really annoying. Maybe we should just use a totally different heuristic for finding functions on MinGW.

__builtin_strndup and strndup are not the same. A platform might have one but not the other.

FWIW, clang also provides strndup as a builtin by default (it doesn't know what the current system headers happen to have defined or not), so __has_builtin(strndup) will still evaluate to true.

Given that a compiler might provide more builtins than the actual target SDK has available, it would kinda exclude using __has_builtin at all. For things that aren't testable by taking the address of an actual function (like alloca or va_start) but is a builtin, I guess the most reliable thing would be to compile and link an actual test app that uses them in the way the are intended to (an actual call to alloca or use of va_start in a real variadic function). If the compiler doesn't provide them as builtins, this will end up with undefined references when linking, while it should properly detect them otherwise. Unfortunately that requires custom per-function testing code...

This is also a problem with clang and the Android headers which only exposes libc functions at certain android api levels. e.g stpcpy is exposed for android >= 21 so checking for stpcpy gives a false positive with the __has_builtin check when compiling for android < 21.

@ystreet Could you test if #3902 fixes that?

Note that the PR only affects the check when you actually use the check + header, for example cc.has_function('getopt_long', prefix : '#include <getopt.h>'), not the check without the header in prefix.

@ePirat nope, doesn't work :)

That gets a warning: implicitly declaring library function 'stpcpy' with type 'char *(char *, const char *)' [-Wimplicit-function-declaration] however it still marks the test as succeeding. The previous test would fail at the first test void * f = (void *) &stpcpy but pass at the __builtin check.

Note that the PR only affects the check when you actually use the check + header, for example cc.has_function('getopt_long', prefix : '#include <getopt.h>'), not the check without the header in prefix.

stpcpy has an prefix : '#include <string.h>' here so that requirement is met.

I am confused as to why the test in #3902 succeeds but the current one properly fails… I've never worked with the android toolchain before.
You mentioned before "when compiling for android < 21", how exactly is that version set? Is there a compiler flag or a define or something used for that?

I am confused as to why the test in #3902 succeeds but the current one properly fails… I've never worked with the android toolchain before.
You mentioned before "when compiling for android < 21", how exactly is that version set? Is there a compiler flag or a define or something used for that?

-D__ANDROID_API__=$LEVEL

and the headers have e.g.:

#if __ANDROID_API__ >= 21
char* stpcpy(char* __dst, const char* __src) __INTRODUCED_IN(21);
#endif

This is a new thing from ndk-16 and android's new unified headers: https://android.googlesource.com/platform/ndk/+/ndk-release-r16/docs/UnifiedHeaders.md. Previously, each api level had their own header directory.

Thanks for the explanation. Can you provide me with the error message that makes the first part of the current test correctly fail?

First test currently fails with:

/tmp/tmp1qkw7h2i/testfile.c:9:32: note: include the header <string.h> or explicitly provide a declaration for 'stpcpy'
1 warning generated.
/tmp/tmp1qkw7h2i/testfile.c:0: error: undefined reference to 'stpcpy'

The reason the #3903 passes is cause the variable is unused as mentioned in the preceding comment

int main() {
            stpcpy;
        }
Compiler stdout:

Compiler stderr:
 /tmp/tmpaco_3462/testfile.c:9:13: warning: implicitly declaring library function 'stpcpy' with type 'char *(char *, const char *)' [-Wimplicit-function-declaration]
            stpcpy;
            ^
/tmp/tmpaco_3462/testfile.c:9:13: note: include the header <string.h> or explicitly provide a declaration for 'stpcpy'
/tmp/tmpaco_3462/testfile.c:9:13: warning: expression result unused [-Wunused-value]
            stpcpy;
            ^~~~~~
2 warnings generated.

Checking for function "stpcpy" : YES

FWIW, #5628 is another report of the exact same issue; there it's exposed with clang-cl with Visual Studio headers - so this isn't an issue with "MinGW being broken".

So yeah I am out of ideas then how to properly test for this as whatever way work fine with one toolchain/OS/compiler (like my suggested new approach in #3902 works fine for macOS/iOS) apparently fails for others (Android) where the current one works… 💥

Would #7116 help in this case as well?

It improves it a bit back to how bad the situation used to be.
In general I believe the check mixing builtins and functions makes it impossible for it to ever behave in a reasonable way that does not report things incorrectly.

I kind of gave up on this topic and will probably work on a completely new check for functions/builtins, as this is not going anywhere without breaking some usecases while fixing others…

This should be fixed since d7e20b1543499 (0.54.2). Fixed in the sense that clang and gcc should give the same result now.

Was this page helpful?
0 / 5 - 0 ratings