Cross-compiling SystemC libraries and linking to them

927 Views Asked by At

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?

2

There are 2 best solutions below

0
On BEST ANSWER

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.

0
On

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