1

I'm starting from scratch and am following the main steps below:

1. Build and install a cross-compiler toolchain (host Linux, target Win64):

Get this MXE version, only changing plugins/gcc6/gcc6-overlay.mk with:

$(PKG)_VERSION  := 6.3.0
$(PKG)_CHECKSUM := f06ae7f3f790fbf0f018f6d40e844451e6bc3b7bc96e128e63b09825c1f8b29f

Then it's simple (only takes some time, use make --jobs=X JOBS=Y to speed up):

setenv MXE_SRC /path/to/where/you/extracted/mxe

cd $MXE_SRC
make MXE_TARGETS='x86_64-w64-mingw32.shared x86_64-w64-mingw32.static' MXE_PLUGIN_DIRS=plugins/gcc6 pthreads
setenv PATH $MXE_SRC/usr/bin:$PATH

2. Cross-compile and install the SystemC 2.3.3 libraries

This is just as simple (but a lot quicker!):

setenv SYSTEMC_SRC                  /path/to/where/you/extracted/systemc/
setenv SYSTEMC_STATICTOOLCHAIN_DEST /this/is/your/choice

cd $SYSTEMC_SRC
./configure --prefix=$SYSTEMC_STATICTOOLCHAIN_DEST --host=x86_64-w64-mingw32.static
make install

3. Build a trivial executable

Write a few lines of code in sc_main.cpp:

#include "systemc.h"

int sc_main (int argc, char* argv[])
{
  sc_clock clk("CLOCK", 1, SC_NS, 0.5);
  sc_start();
  return 0;
}

Now building with one final step:

x86_64-w64-mingw32.static-g++ sc_main.cpp -I$SYSTEMC_STATICTOOLCHAIN_DEST/include -L$SYSTEMC_STATICTOOLCHAIN_DEST/lib-mingw64 -lsystemc

I get a whole bunch of

libsystemc.a(sc_prim_channel.o):sc_prim_channel.cpp:(.text+0x44): undefined reference to `__imp_pthread_mutex_unlock'

(and variants of the same).

Can anyone reproduce and explain what's going on?

When the SystemC configure completes, it clearly says that it's not going to use Posix threads but WinFiber instead, so I'm a bit surprised to see these unresolved dependencies to phread (for the record, adding -lpthread at the end of the command line still produces the same result)

4. Bonus experiments building SystemC libraries in different ways

If I use a native Win64 toolchain to build SystemC libraries and then build my executable with the same command line:

 setenv SYSTEMC_NATIVETOOLCHAIN_DEST /path/to/systemc/libraries/built/with/native/toolchain
 x86_64-w64-mingw32.static-g++ sc_main.cpp -I$SYSTEMC_NATIVETOOLCHAIN_DEST/include -L$SYSTEMC_NATIVETOOLCHAIN_DEST/lib-mingw64 -lsystemc

then everything works nicely, as expected.

Also, if I cross-compile and install the SystemC libraries using cmake rather than configure:

cd $SYSTEMC_SRC
mkdir build && cd build && x86_64-w64-mingw32.static-cmake .. -DBUILD_SHARED_LIBS=OFF -DCMAKE_CXX_STANDARD=14 -DINSTALL_TO_LIB_TARGET_ARCH_DIR=ON -DCMAKE_INSTALL_PREFIX=$SYSTEMC_STATICTOOLCHAIN_DEST
make install

x86_64-w64-mingw32.static-g++ sc_main.cpp -I$SYSTEMC_STATICTOOLCHAIN_DEST/include -L$SYSTEMC_STATICTOOLCHAIN_DEST/lib-mingw64 -lsystemc

then, again, everything works nicely, as expected.

I have a suspicion that SystemC libraries aren't generated properly when cross-compiled. Can anyone confirm/deny?

TylerH
  • 20,799
  • 66
  • 75
  • 101
DaveC
  • 115
  • 8
  • A possible indication that cross-compiling SystemC libraries hasn't been thoroughly tested and verified: I forgot to mention that out of the box, cross-compilation fails because `systemc/src/sysc/kernel/sc_cmnhdr.h` contains `#include ` which isn't found (due to case sensitiveness on linux). A simple change to `#include ` fixes the problem. – DaveC Sep 14 '21 at 14:55
  • I believe I have identified the key difference between `cmake` and `configure` that makes or breaks the whole thing. `configure` compiles source files with -DDLL_EXPORT whereas `cmake` does not. I don't really understand why this is, but the effect is visible when looking at undefined symbols: `x86_64-w64-mingw32.static-nm -u libsystemc.a` shows `U pthread_mutex_lock` with the `cmake` build whereas it shows `U __imp_pthread_mutex_lock` with the `configure` build. Can any one explain why compiling with -DDLL_EXPORT would have this effect, and whether or not it's the right thing to do ? – DaveC Sep 16 '21 at 02:23
  • I'm not expert enough to know what's right or what's wrong but at least now I understand what's going on.... What's happening is that the header file `mingw-w64-libraries/winpthreads/include/pthread.h` has this `#if defined DLL_EXPORT #ifdef IN_WINPTHREAD #define WINPTHREAD_API __declspec(dllexport) #else #define WINPTHREAD_API __declspec(dllimport) #endif` As a result when compiling with '-DDLL_EXPORT', a whole bunch of `pthread_*` routines are declared as imports..... – DaveC Sep 16 '21 at 06:15

2 Answers2

0

This is not going to be a proper answer, but at least I can describe the end-to-end mechanism that's causing the problem. I'm not familiar enough with all the intricacies of MingW, winpthread, etc... to really say exactly who's doing something wrong, but I'll leave it to the experts ;-)

So it all starts with SystemC's configure which generates Makefiles recipes defining DLL_EXPORT. At first sight it seems quite legitimate to define DLL_EXPORT, since the goal is to build a library.

In practice though, DLL_EXPORT isn't referenced anywhere in SystemC's code so it's not very clear what the intention is (and as a matter of fact, SystemC's cmake-based flow doesn't define it). If I trust comments in configure though, it is some kind of hack that has a good reason to be.

Next, as SystemC source files get compiled, mingw-w64-libraries/winpthreads/include/pthread.h is included at some point (via sc_host_mutex.h and <mutex>).

At this point the compiler sees the below:

#if defined DLL_EXPORT
#      ifdef IN_WINPTHREAD
#            define WINPTHREAD_API __declspec(dllexport)
#      else
#            define WINPTHREAD_API __declspec(dllimport)
#      endif
#else
#      define WINPTHREAD_API
#endif

Note there is similar code in mingw-w64-libraries/winpthreads/include/sched.h and mingw-w64-libraries/winpthreads/include/semaphore.h

Assuming that IN_WINPTHREAD is not defined (which I guess is always true except when building libwinpthread.dll), calls to the pthread API from the SystemC library are declared as 'dllimport' which ultimately leads to the infamous:

libsystemc.a(sc_prim_channel.o):sc_prim_channel.cpp:(.text+0x44): undefined reference to `__imp_pthread_mutex_unlock'
  

I wonder if the intention of the mingw-w64 header was a little different, perhaps something like:

#ifdef IN_WINPTHREAD
#    if defined DLL_EXPORT
#        define WINPTHREAD_API __declspec(dllexport)
#    else
#        define WINPTHREAD_API __declspec(dllimport)
#    endif
#else
#    define WINPTHREAD_API
#endif 

or even

#if defined DLL_EXPORT
#      ifdef IN_WINPTHREAD
#            define WINPTHREAD_API __declspec(dllexport)
#      else
#            define WINPTHREAD_API
#      endif
#else
#      define WINPTHREAD_API
#endif

Ultimately I can't decide between the following:

  • SystemC is to blame and configure should not define DLL_EXPORT
  • MinGW-w64 is to blame and pthread.h should provide a different mechanism so that calls to the pthread API are not automatically turned into 'dllimport', but default to normal static linking perhaps (an explicit mechanism with DLL_IMPORT could also be introduced).
  • Both the above
  • Something else
DaveC
  • 115
  • 8
0

In the last couple of days I've learnt a lot about libtool and how DLLEXPORT is meant to be used in a consistent manner.

It has made me realize that the root cause of the problem is the end-user (myself), as always...

To be fair, there is something inconsistent in the configure script that ships with SystemC. The below:

./configure --host=x86_64-w64-mingw32.static
  • will build a static library (libsystemc.a) as expected
  • but it will happily do so using settings for shared libraries by default (assuming --enable-shared)

It makes the end-user believe that everything is fine... when it isn't.

Once you understand that and you explicitly specify settings that are consistent with building a static library:

./configure --host=x86_64-w64-mingw32.static --disable-shared

then everything works just fine.

The SystemC cmake flow is more robust. It throws an error if you try to target Windows using the default settings, which leaves you no choice but to specify -DBUILD_SHARED_LIBS=OFF on the command line.

DaveC
  • 115
  • 8