mjansson / foundation_lib

Cross-platform public domain foundation library in C providing basic support data types and functions to write applications and games in a platform-independent fashion.
The Unlicense
301 stars 23 forks source link

Problems building foundation_lib using MSYS2 (MinGW) #13

Closed gschmottlach closed 8 years ago

gschmottlach commented 8 years ago

My workflow typically involves building Windows libraries from a Linux host using MSYS2 and a cross GCC tool-chain (e.g. cross compile on Linux for a Windows target). Microsoft's Visual Studio is not available.

First Question: Has anyone tried this work-flow or alternate Microsoft compiler?

I put together a GNU makefile to compile foundation_lib directly because in addition to the static archive (libfoundation.a) I also needed a shared library variant (libfoundation.so). On a Linux host (for a Linux target) this works fine - both static and shared libraries are produced using the makefile.

When building the Windows variant of foundation_lib I've encountered the following problem. It appears (somehow) that any file I build within foundation_lib has extra symbols defined (T) in the object file even though they're never referenced in that file. This seems to occur whenever #include <foundation/platform.h> is included in the source file. Here's a simple example function:

File: test.c

#include <string.h>
#include <stdio.h>
#include <math.h>

void test_it(char* buf)
{
        strcpy(buf, "hello");
        sprintf(buf, "%f", fabs(5.0));
        return;
}

Running nm on the resulting test.o I see the following:

0000000000000000 b .bss
0000000000000000 d .data
0000000000000000 N .debug_abbrev
0000000000000000 N .debug_aranges
0000000000000000 N .debug_frame
0000000000000000 N .debug_info
0000000000000000 N .debug_line
0000000000000000 N .debug_loc
0000000000000000 p .pdata
0000000000000000 r .rdata
0000000000000000 r .rdata$zzz
0000000000000000 t .text
0000000000000000 r .xdata
                 U sprintf
0000000000000000 T test_it

If I add one line to the file to include <foundation/platform.h> . . .

#include <foundation/platform.h>
#include <string.h>
#include <stdio.h>
#include <math.h>

void test_it(char* buf)
{
        strcpy(buf, "hello");
        sprintf(buf, "%f", fabs(5.0));
        return;
}

and recompile and execute nm again to see the symbols I see . . .

0000000000000000 b .bss
0000000000000000 d .data
0000000000000000 N .debug_abbrev
0000000000000000 N .debug_aranges
0000000000000000 N .debug_frame
0000000000000000 N .debug_info
0000000000000000 N .debug_line
0000000000000000 N .debug_loc
0000000000000000 N .debug_str
0000000000000000 p .pdata
0000000000000000 r .rdata
0000000000000000 r .rdata$zzz
0000000000000000 t .text
0000000000000000 r .xdata
0000000000000030 T _Exit
00000000000000e0 T __fpclassify
0000000000000140 T __fpclassifyf
00000000000000d0 T __fpclassifyl
                 U __imp__stricmp
                 U __imp__strnicmp
0000000000000180 T __isnan
00000000000001b0 T __isnanf
00000000000001d0 T __isnanl
                 U __mingw_vsprintf
00000000000001f0 T __signbit
0000000000000200 T __signbitf
0000000000000210 T __signbitl
                 U _exit
0000000000000330 T copysign
0000000000000380 T copysignf
                 U cosh
0000000000000250 T coshf
                 U exp
0000000000000290 T expf
00000000000000c0 T fabs
00000000000000a0 T fabsf
00000000000000b0 T fabsl
                 U frexp
00000000000002b0 T frexpf
                 U hypot
00000000000002f0 T hypotf
0000000000000080 T imaxabs
                 U ldexp
00000000000002d0 T ldexpf
0000000000000040 T llabs
                 U pow
0000000000000310 T powf
                 U sinh
0000000000000230 T sinhf
0000000000000000 t sprintf.constprop.0
0000000000000070 T strcasecmp
0000000000000060 T strncasecmp
                 U tanh
0000000000000270 T tanhf
00000000000003b0 T test_it

Where did all these (unreferenced) functions like frexpf() or sinhf() come from??? When I build the entire Foundation library for Windows and try to link a shared library (DLL) the linker complains about multiple definitions since each *.o file contains these duplicate symbols. The command line I'm using to compile this example is:

x86_64-w64-mingw32.static-gcc  -c -Wall -Werror -Wextra -Wpedantic -std=c11 -Wno-unknown-pragmas -Wno-unused-parameter -Wno-format -Wno-unused-function -Wno-pedantic -Wno-strict-aliasing -funit-at-a-time -fstrict-aliasing -fno-math-errno -ffinite-math-only -funsafe-math-optimizations -fno-trapping-math -ffast-math -g -DFOUNDATION_COMPILE=1 -DBUILD_DEPLOY=1 -funroll-loops -O4 -m64 -I/home/swdev/project -o test.o  test.c

I am also attaching a file that contains the settings of all the macro definitions defined in the environment as well as in foundation/platform.h. This environment is derived by executing:

echo "#include  <foundation/platform.h>" | x86_64-w64-mingw32.static-gcc -I/home/swdev/project -E -dM - > env.txt

I've also run into some issues with MinGW where functions like _ftime_s() are not defined (had to fall back to using _ftime64()) and localtime_s() and gmtime_s() are not available either. Finally, there is also an issue with crash.c and specifically _crash_dump_file_buffer and _crash_dump_file. Apparently these aren't defined anywhere in the library either. Where are these defined in Windows?

Any help and/or suggestions would be very much appreciated. I'd love to be able to use this library but the Windows support under MinGW seems to be an issue at this time.

Thanks for your time and patience . . .

env.txt

mjansson commented 8 years ago

I've never tried cross-compiling. The library should compile under Windows with Mcrosoft compilers (VC 2013 or later), Intel compilers (version 11 or later) and clang. However, clang is a bit experimental at this point.

Regarding the referenced symbols, just by including the header it should ideally not pull in those symbols. I'll look into this.

Regarding crash_dump_file_buffer and crash_dump_file symbols, please try the develop branch and see if the change I pushed solved this problem at least.

gschmottlach commented 8 years ago

I updated my crash.c with the one from the develop branch. It almost works but I had to make the following modifications:

Before:

void
crash_debug_break(void) {
#if FOUNDATION_PLATFORM_WINDOWS
    DebugBreak();
#elif FOUNDATION_COMPILER_GCC || FOUNDATION_COMPILER_CLANG
    __builtin_trap();
#else
    (*(volatile int*)3 = 0);
#endif
}

After:

void
crash_debug_break(void) {
#if FOUNDATION_PLATFORM_WINDOWS
    DebugBreak();
#elif FOUNDATION_COMPILER_GCC || FOUNDATION_COMPILER_CLANG
    __builtin_trap();
#else
    (*(volatile int*)3 = 0);
#endif
    while(1);
}

I had to add the while (1) so the compiler woudl stop complaining that the "noreturn" function was in fact returning. Probably a problem because DebugBreak() may not be marked as noreturn.

I also had to fix crash.c:243 as follows:

Before:

_crash_exception_closure.name = name;

After:

_crash_exception_closure.name.str = name;
_crash_exception_closure.name.length = length;

You were trying to assign a const char* to a string_const_t structure and they're obviously not compatible.

Let me know if you can figure out what's going on with the duplicate symbols in every object file.

Thanks . . .

mjansson commented 8 years ago

Pushed fixes to crash.c (develop branch).

Regarding the symbols, it's something MinGW adds when _MSC_VER is defined (which platform.h declares to make sure the clang native Windows build works). You can see it by changing the test.c file to

#ifndef _MSC_VER
#  define _MSC_VER 1300
#endif
#include <string.h>
#include <stdio.h>
#include <math.h>

void test_it(char* buf)
{
        strcpy(buf, "hello");
        sprintf(buf, "%f", fabs(5.0));
        return;
}
gschmottlach commented 8 years ago

I commented out the two instance where _MSC_VER is defined as shown:

#    ifndef __GNUC__
#      ifndef _MSC_VER
#        define _MSC_VER 1300
#      endif
#    endif

It builds the static library fine but fails linking a DLL because of the scheme that main.c uses to callback into the application (e..g main_initialize(), main_run(), and main_finalize()).

In Linux, I can build a shared-library version of foundation_lib fine because the run-time loader/linker will resolve the missing main__xxxx symbols in the application that loaded it. Unfortunately, the Windows loader/linker is not as smart and it is difficult for a DLL to implicitly call directly into the application. One approach is described here.

My question, for you, is whether you anticipate supporting a shared library versions of foundation_lib? I'm wondering if you've ever considered changing the mechanism that the application uses to define functions that main.c should call. Perhaps it might be better if the application explicitly called an "initialize" function and passed in a function table (e.g. vtable) containing pointers to its implementation of main_initialize(), main_run(), and main_finalize(). This way foundation_lib never directly calls back into the application with the assumption these correctly named functions exist (e.g. what happens if my application uses these same names for something else?). The current scheme forces the application to reserve these names for foundation_lib when it's not entirely necessary (given a vtable approach).

Just interested on you thoughts here for shared library support. . .

mjansson commented 8 years ago

Building as a shared library has not been a goal, but if there is interest in it, why not? :)

The problem is that the library aims to abstract away all the different entry points for different platforms and application types. I guess the most logical thing in a shared library would be to simply not wrap any entry points. I'll look into it.

mjansson commented 8 years ago

Did some updates on develop branch, check it out and define BUILD_DYNAMICLINK=1 when building as a shared library. It will define the dll export and also disable the entry point wrapper code (and the need for the main* symbols).

gschmottlach commented 8 years ago

Thanks . . . I've been side-tracked by some other issues at the moment but I'll give it a try when I get the chance and let you know the results.

gschmottlach commented 8 years ago

I merged your development branch and after some work, managed to create both a static archive and shared library under MSYS2 using your updates and my standard GNU makefile. Unfortunately, I had to make a few changes when building the Windows 32-bit version of the library.

1) In hash.c I had to force it to use the definition for _rotl64(), e.g.

#elif FOUNDATION_COMPILER_GCC || FOUNDATION_COMPILER_CLANG
//#ifndef _rotl64
#undef _rotl64
#  define _rotl64(a, bits) (((a) << (uint64_t)(bits)) | ((a) >> (64ULL - (uint64_t)(bits))))
//#endif
#endif

If I don't do this I get the following error (for 32-bit only - the 64-bit Windows variant builds fine):

/home/swdev/project/foundation/hash.c:120:4: error: implicit declaration of function '__rolq' [-Werror=implicit-function-declaration]
    bmix64(h1, h2, k1, k2, c1, c2);
    ^
cc1: all warnings being treated as errors
Makefile:271: recipe for target '/home/swdev/project/output/win32/deploy/foundation/objs/hash.o' failed

2) In platform.h(~722) I have to define STDCALL to use __stdcall for the GNUC compiler, e.g.

#  if FOUNDATION_PLATFORM_WINDOWS
#    define STDCALL __stdcall
#    ifndef __USE_MINGW_ANSI_STDIO
#      define __USE_MINGW_ANSI_STDIO 1
#    endif
#    ifndef _CRT_SECURE_NO_WARNINGS
#      define _CRT_SECURE_NO_WARNINGS 1
#    endif
#    ifndef _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES
#      define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 0
#    endif
#  endif

Again, this seems to be 32-bit Windows specific but it's probably safe practice anyway to define it.

I haven't tried to build/link anything to these libraries yet but I thought you might be interested in the feedback. Thanks for taking on this challenge (shared library support) and making it a reality. Hopefully actually using the library will be the easy part.

mjansson commented 8 years ago

Pushed some additional changes to the develop branch based on your suggestions

mjansson commented 8 years ago

Furthe windows gcc & clang compatibility fixes have been made on the develop branch now.

mjansson commented 8 years ago

New 1.5 release has been compiled and verified with both GCC (MinGW-64) and Clang on Windows