Home » Linux » How to cross compile for linux x86 with linux amd64, cmake and g++?

How to cross compile for linux x86 with linux amd64, cmake and g++?

Posted by: admin November 30, 2017 Leave a comment

Questions:

+1 for each piece of information that helps to complete the whole picture. You don’t need to know the whole answer. I’ll appreciate individual pieces of the puzzle just as much. Thanks.

I am about to attempt my first cross-compilation. I have searched both SO and the web and found many pieces of information, but I don’t always know how to put those pieces together because there are still some missing pieces.

My host: linux Kubuntu amd64.
Target: linux kubuntu x86 (32bit) (should be easy, no?)
Tools: g++ and cmake.

Here is the information I found:

How to compile a 32-bit binary on a 64-bit linux machine with gcc/cmake
mentions export CFLAGS=-m32. That’s one piece.

Cross-platform: selecting data types to use 32/64 bit
mentions data types. I may have to pay attention to that within my code.

#ifdef for 32-bit platform
#ifdef for 32-bit platform
links to the following, although I am not too sure yet how to use it:
http://predef.sourceforge.net/prearch.html

http://ww.ubuntuforums.org/showthread.php?t=1377396
I did: sudo apt-get install g++-multilib

missing pieces:

Ideally, when I do ‘make’ (with cmake), it should spit out both a amd64 binary and a x86 one.

Part of my CMakeLists.txt looks like this:

add_definitions(-Wall -pthread)
add_executable (../run.amd64 user.cpp time.cpp init.cpp utils.cpp main.cpp)
target_link_libraries(../run.amd64 cppcms dbixx config++ ctemplate)

How do I introduce the flag -m32 to create a second executable?

Should I want to make only one executable (e.g. for testing and debugging), how do I tell cmake to make either one or both binaries?

Also, you can see that I use some third party libraries, some of which I had to compile myself. Does this mean that I need to compile each of those binaries for the target host as well? Some use cmake and some use: ./configure; make;
How would I do about compiling those libraries for the target host (flags to use, etc.)?

Note: the dynamically linked libraries are already compiled and installed on the target computer, so maybe I don’t need to worry about this step… I am not sure: this is one of my missing pieces…

What I need is a kind of tutorial, or at least some of the missing pieces. I’ll update this post with more details on what I achieved and how.

Thanks.

P.S.

Is it possible at all?

Searching more, I found this:
http://www.mail-archive.com/[email protected]/msg26265.html
“The original design doesn’t seem to be designed for anything more than windows-linux or linux-windows cross compiles.”
cmake is NOT tested for linux amd64 to linux x86.

http://www.cmake.org/Wiki/CMake_Cross_Compiling#FAQ.2FPotential_Problems
“On mixed 32/64 bit Linux installations cross compilation cannot be used to build for 32/64 bit only.”

??

Answers:

If you want to use a toolchain file there is an easier solution (IMHO) than what is proposed by @auledoom. You do not need to write the shell wrapper scripts at all, simply put this in the toolchain file:

# the name of the target operating system
set(CMAKE_SYSTEM_NAME Linux)

# Which compilers to use for C and C++
set(CMAKE_C_COMPILER gcc -m32)
set(CMAKE_CXX_COMPILER g++ -m32)

This will make it a “list variable” in cmake. This solution works for me. Benefit of the toolchain file is that you can there also define paths for 32bit libraries etc, which is usually different from standard paths.

Questions:
Answers:

This solution will allow you cross-compile your cmake project on a linux64 host targeting 32bits, on systems with multi-arch support.
It’s uses a “fake” cmake toolchain so CMAKE somehow “believes” it’s on 32bit system, so no additional modifications are needed inside your cmake project files, no special configurations, no special settings (well almost).

  1. Install multilib support:

    $sudo apt-get install gcc-multilib
    
  2. Create a “fake” linux32 toolchain

First, we create a “fake” i686 compiler. Go where your CMakeLists.txt resides and create a bin directory. Open your preferred editor and create this simple bash script for gcc compiler.

#!/bin/sh
/usr/bin/gcc -m32 "[email protected]"

As you see, it’s just make a call to the system compiler adding the -m flag. Save this as i686-linux-gnu-gcc. Do the same for the g++ compiler

#!/bin/sh
/usr/bin/g++ -m32 "[email protected]"

Save it as i686-linux-gnu-g++. Remember to set the executable flags on this scrips

Create also a symlink to the system ar binary in this form

$ln /usr/bin/ar i686-linux-gnu-ar

At last create the toolchain-linux32.cmake file

# the name of the target operating system
set(CMAKE_SYSTEM_NAME Linux)

# Which compilers to use for C and C++
set(CMAKE_C_COMPILER ${CMAKE_SOURCE_DIR}/bin/i686-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER ${CMAKE_SOURCE_DIR}/bin/i686-linux-gnu-g++)

and create the build directory and call cmake with the toolchain file as argument

$mkdir build && cd build
$cmake -DCMAKE_TOOLCHAIN_FILE=../toolchain-linux32.cmake ..

and your done!!!!!

I’ll write a more complete guide here, which covers some problems i have with libraries not multi-lib compliant

Questions:
Answers:

this is a simplified version of what I use, and it does create x86 binaries:

set( TargetName myExe )
set( SOURCES a.cpp b.cpp )
add_executable( ${TargetName} ${SOURCES} )
target_link_libraries( ${TargetName} m pthread stdc++ )
set_target_properties( ${TargetName} PROPERTIES COMPILE_FLAGS -m32 LINK_FLAGS -m32 )

furthermore you’ll use add_definitions to set compiler flags like -W -Ox -Dxxx etc.

All the lines above are actually split in seperate cmake files, and to get one file to build a number of executables, I generate a master cmake file containing all different configurations I want to build:

project( myProject )
set( SOURCES a.cpp b.cpp )
if( ${ConfigurationType} strequal "Debugx86" )
  include( debugopts.cmake )
  include( x86.cmake )
  include( executable.cmake )
  ...
elseif( ${ConfigurationType} strequal "Releasex64" )
  include( debugopts.cmake )
  include( x86.cmake )
  include( executable.cmake )
  ...
etc

Then there’s a driver shell script to build it all. It takes commandline options to set some extra options and select to build everything or just one configuration. Here’s a piece of it:

if [ "$myConfig" = "all" -o "$myConfig" = "Debugx86" ]; then
  mkdir -p project_Debugx86
  cd project_Debugx86
  cmkake "$sourceDir" "$cmakeOpts" -DConfigurationType="Debugx86"
  make clean
  make "$makeopts"
fi
if [ "$myConfig" = "all" -o "$myConfig" = "Releasex64" ]; then
  mkdir -p project_Releasex64
  cd project_Releasex64
  cmkake "$sourceDir" "$cmakeOpts" -DConfigurationType="Releasex64
  make clean
  make "$makeopts"
fi

While this is not exactly what you ask for, it works flawlessly and does the same. (Not sure if it is possible in cmake to define any number of targets in cmake itself, and have them built all together by one file.) It just takes some time to write the generator for this files, but once that is done all I have to do is point the generator to a directory with sources, let ir run, then invoke the build script to make everything.

Questions:
Answers:

All you need is to add -m32 to CFLAGS and CXXFLAGS when running CMake. This can be done via environment variables:

$ CFLAGS=-m32 CXXFLAGS=-m32 cmake .

or by setting corresponding CMake variables:

$ cmake -DCMAKE_C_FLAGS=-m32 -DCMAKE_CXX_FLAGS=-m32 .

This can easily tested with a simple CMake project:

$ uname -m
x86_64
$ CFLAGS=-m32 CXXFLAGS=-m32 cmake .
-- The C compiler identification is GNU 4.8.1
-- The CXX compiler identification is GNU 4.8.1
....
$ make 
Scanning dependencies of target foo
[100%] Building CXX object CMakeFiles/foo.dir/foo.cc.o
Linking CXX executable foo
[100%] Built target foo
$ file foo
foo: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0x5b1871446c92cbdcbf905583e16189f68f3bf5f2, not stripped

where CMakeLists.txt is a trivial CMake file:

project(TEST)
add_executable(foo foo.cc)

and foo.cc is as follows:

int main () {}

Questions:
Answers:

Here is the basic recipe I use all the time for cmake projects..

OPTION(FORCE32 "Force a 32bit compile on 64bit" OFF)
IF(FORCE32)
    if(APPLE)
        SET(CMAKE_OSX_ARCHITECTURES "i386")
    else()
        SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32")
        SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32")
    endif()
ENDIF()

IF(APPLE)
    set(BIN_LIBROOT "macosx")
ELSE()
    if(CMAKE_SIZEOF_VOID_P MATCHES "8" AND NOT(FORCE32) )
        set(BIN_LIBROOT "linux64")
        set(CMAKE_EXECUTABLE_SUFFIX ".bin.x86_64")
        set(BIN_RPATH "$ORIGIN/lib64")
    else()
        set(BIN_LIBROOT "linux")
        set(CMAKE_EXECUTABLE_SUFFIX ".bin.x86")
        set(BIN_RPATH "$ORIGIN/lib")
    endif()

    set(CMAKE_SKIP_BUILD_RPATH TRUE)
    set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
    set(CMAKE_INSTALL_RPATH ${BIN_RPATH})
    set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)
ENDIF()

Then every target automatically has the .bin.${arch} extension and I never have to think about this for any targets I add. the ${BIN_LIBROOT} is useful if you have a bunch of precompiled libraries as you as you can use that to dynamically search for libs in your private lib dirs based on the target platform/arch.