Search code examples
ccmakecross-compilingmingw-w64tcc

Cross-compile libtcc


I'm trying to create a simple hello world program that uses libtcc to dynamically compile the code. It works fine when I compile it natively on linux. However, there are link errors when I try to cross-compile it from linux to windows with mingw-w64.

Here are the commands I use to compile for linux:

$ cmake -S . -B build && cmake --build build                         
-- The C compiler identification is GNU 12.2.0
-- The CXX compiler identification is GNU 12.2.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /workdir/project/build
[ 10%] Creating directories for 'tcc_proj'
[ 20%] No download step for 'tcc_proj'
[ 30%] No update step for 'tcc_proj'
[ 40%] No patch step for 'tcc_proj'
[ 50%] Performing configure step for 'tcc_proj'
Binary directory    /usr/local/bin
TinyCC directory    /usr/local/lib/tcc
Library directory   /usr/local/lib
Include directory   /usr/local/include
Manual directory    /usr/local/share/man
Info directory      /usr/local/share/info
Doc directory       /usr/local/share/doc
Source path         /workdir/project/tinycc
C compiler          gcc (12.2)
Target OS           Linux
CPU                 x86_64
Triplet             x86_64-linux-gnu
Creating config.mak and config.h
[ 60%] Performing build step for 'tcc_proj'
[ 70%] No install step for 'tcc_proj'
[ 80%] Completed 'tcc_proj'
[ 80%] Built target tcc_proj
[ 90%] Building C object CMakeFiles/hello.dir/hello.c.o
[100%] Linking C executable hello
[100%] Built target hello

And here are the commands I use to compile for windows:

$ cmake -S . -B build-win -DCMAKE_TOOLCHAIN_FILE=win-x64-static.cmake && cmake --build build-win
-- Configuring done
-- Generating done
-- Build files have been written to: /workdir/project/build-win
[ 10%] Performing configure step for 'tcc_proj'
Binary directory    C:/Program Files/tcc
TinyCC directory    C:/Program Files/tcc
Library directory   C:/Program Files/tcc/libtcc
Doc directory       C:/Program Files/tcc/doc
Source path         /workdir/project/tinycc
C compiler          gcc (0.0)
Target OS           WIN32
CPU                 x86_64
Config              static WIN32
Creating config.mak and config.h
config.h is unchanged
[ 20%] Performing build step for 'tcc_proj'
[ 30%] No install step for 'tcc_proj'
[ 40%] Completed 'tcc_proj'
[ 80%] Built target tcc_proj
[ 90%] Linking C executable hello.exe
/usr/bin/x86_64-w64-mingw32-ld: CMakeFiles/hello.dir/objects.a(hello.c.obj):hello.c:(.text.startup+0xd): undefined reference to `tcc_new'
/usr/bin/x86_64-w64-mingw32-ld: CMakeFiles/hello.dir/objects.a(hello.c.obj):hello.c:(.text.startup+0x1d): undefined reference to `tcc_set_output_type'
/usr/bin/x86_64-w64-mingw32-ld: CMakeFiles/hello.dir/objects.a(hello.c.obj):hello.c:(.text.startup+0x2c): undefined reference to `tcc_compile_string'
/usr/bin/x86_64-w64-mingw32-ld: CMakeFiles/hello.dir/objects.a(hello.c.obj):hello.c:(.text.startup+0x36): undefined reference to `tcc_relocate'
/usr/bin/x86_64-w64-mingw32-ld: CMakeFiles/hello.dir/objects.a(hello.c.obj):hello.c:(.text.startup+0x4c): undefined reference to `tcc_relocate'
/usr/bin/x86_64-w64-mingw32-ld: CMakeFiles/hello.dir/objects.a(hello.c.obj):hello.c:(.text.startup+0x5b): undefined reference to `tcc_get_symbol'
/usr/bin/x86_64-w64-mingw32-ld: CMakeFiles/hello.dir/objects.a(hello.c.obj):hello.c:(.text.startup+0x66): undefined reference to `tcc_delete'
collect2: error: ld returned 1 exit status
gmake[2]: *** [CMakeFiles/hello.dir/build.make:101: hello.exe] Error 1
gmake[1]: *** [CMakeFiles/Makefile2:111: CMakeFiles/hello.dir/all] Error 2
gmake: *** [Makefile:91: all] Error 2

In both cases, the build process creates a build-win/tcc_proj-prefix/src/tcc_proj-build/libtcc.a file.

The directory structure of the project is as follows: on the root there is the tinycc repository, a hello.c file, a CMakeLists.txt file, and a win-x64-static.cmake file.

The url of the tinycc reposistory is: https://repo.or.cz/tinycc.git

The content of hello.c is:

#include <stdlib.h>
#include <stdio.h>

#include "libtcc.h"

char hello_code[] =
"#include <stdio.h>\n"
"\n"
"int main() {\n"
"    printf(\"Hello World\\n\");\n"
"    return 0;\n"
"};\n";

int main(int argc, char **argv)
{
    TCCState *s;
    int (*main_func)(int, char**);
    void *mem;

    s = tcc_new();
    tcc_set_output_type(s, TCC_OUTPUT_MEMORY);
    tcc_compile_string(s, hello_code);

    mem = malloc(tcc_relocate(s, NULL));
    tcc_relocate(s, mem);

    main_func = tcc_get_symbol(s, "main");

    tcc_delete(s);

    printf("main_func returned: %d\n", main_func(0, NULL));

    free(mem);
    return 0;
}

The content of CMakeFiles.txt is:

CMAKE_MINIMUM_REQUIRED(VERSION 3.0)
PROJECT(tcc)

include(ExternalProject)

if (WIN32)
    ExternalProject_Add(tcc_proj SOURCE_DIR ${CMAKE_SOURCE_DIR}/tinycc CONFIGURE_COMMAND "${CMAKE_SOURCE_DIR}/tinycc/configure" --enable-static --config-mingw32 BUILD_COMMAND make INSTALL_COMMAND "")
else ()
    ExternalProject_Add(tcc_proj  SOURCE_DIR ${CMAKE_SOURCE_DIR}/tinycc CONFIGURE_COMMAND "${CMAKE_SOURCE_DIR}/tinycc/configure" BUILD_COMMAND make INSTALL_COMMAND "")
endif (WIN32)

ExternalProject_Get_Property(tcc_proj source_dir)

list(APPEND LIB_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/tinycc/")
list(APPEND LIB_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/tinycc/include/")


add_library(tcc STATIC IMPORTED)

set(LIB_LOCATION tcc_proj-prefix/src/tcc_proj-build/libtcc.a)
set_target_properties(tcc PROPERTIES IMPORTED_LOCATION ${LIB_LOCATION} INTERFACE_INCLUDE_DIRECTORIES "${LIB_INCLUDE_DIRS}")

add_executable(hello "./hello.c")
target_link_libraries(hello PRIVATE tcc)

And the content of win-x64-static.cmake is:

# Toolchain to build from Linux to Windows

set(CMAKE_SYSTEM_NAME Windows)
set(TOOLCHAIN_PREFIX x86_64-w64-mingw32)

# cross compilers to use for C, C++ and Fortran
set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++)
set(CMAKE_Fortran_COMPILER ${TOOLCHAIN_PREFIX}-gfortran)
set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres)

# target environment on the build host system
set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX})

# modify default behavior of FIND_XXX() commands
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static -Os")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static -Os")

Solution

  • I was able to solve the problem. In order to cross-compile libtcc, it's required to specify the flag --cross-prefix, in addition to the --config-mingw32. So, the windows cmake configuration should be:

    ExternalProject_Add(tcc_proj SOURCE_DIR ${CMAKE_SOURCE_DIR}/tinycc CONFIGURE_COMMAND "${CMAKE_SOURCE_DIR}/tinycc/configure" --enable-static --config-mingw32 --cross-prefix=${TOOLCHAIN_PREFIX}- BUILD_COMMAND make INSTALL_COMMAND "")