Search code examples
gccgoogletestelf

How to make compiler generate a "elf32-x86-64" format object file?


First, some background info about elf32-x86-64 format.

It is a format that leverages 64-bit hardware while enforcing 32-bit pointers. Ref1 and Ref2.

Question

I am trying to link the Google Test framework binaries to my project.

I use objdump -f to check the format of Google Test binaries and my binaries.

Google Test format is elf64-x86-64. Mine elf32-x86-64. So they cannot be linked together.

Then I add below content to the google test's internal_utils.cmake file:

set(ZEPHYR_LINK_FLAGS "-Wl,--oformat=elf32-x86-64")
set(CMAKE_EXE_LINKER_FLAGS  "${CMAKE_EXE_LINKER_FLAGS} ${ZEPHYR_LINK_FLAGS}")

I hope the linker flag can change the output format to elf32-x86-64.

But google test build failed with below error:

/usr/lib/gcc/x86_64-linux-gnu/7/libstdc++.so: error adding symbols: File in wrong format

The /usr/lib/gcc/x86_64-linux-gnu/7/libstdc++.so is also a elf64-x86-64 format.

And I checked the generated object file, such as: ./googletest/CMakeFiles/gtest_main.dir/src/gtest_main.cc.o

It is still elf64-x86-64.

So it seems the linker flag doesn't affect the object file format.

I remember the linker ld will choose the output format based on its first encountered object file. So I guess I need to tell the compiler to output a elf32-x86-64 format.

How can I ask the compiler to output a elf32-x86-64 object file?

ADD 1 - 3:29 PM 11/1/2019

I have managed to compile the Google Test as elf32-x86-64 with below tuning:

  • Add compile flag -mx32
  • And add link flag -Wl,--oformat=elf32-x86-64

Now the output binaries libgtest.a, libgtest_main.a are elf32-x86-64. But they need to be linked to libstdc++.so. So far, it is elf64-x86-64 on my system. And I haven't found a elf32-x86-64 one. Thus below error:

/usr/lib/gcc/x86_64-linux-gnu/7/libstdc++.so: error adding symbols: File in wrong format

ADD 2 - 3:47 PM 11/1/2019

After installing sudo apt-get install gcc-multilib g++-multilib (ref), I got a elf32-x86-64 version of libstdc++.so at below location:

/usr/lib/gcc/x86_64-linux-gnu/7/x32/libstdc++.so

And it ultimately points to /usr/libx32/libstdc++.so.6.0.25

Now it seems I just need to find a way to tell the linker to use it... So close!

ADD 3 - 2:44 PM 11/4/2019

Thanks to Florian and EmployedRussian, I change Google Test's internal_utils.cmake file to add below 4 lines:

set(MY_COMPILE_FLAGS "-mx32")
set(cxx_base_flags "${cxx_base_flags} ${MY_COMPILE_FLAGS}") 
set(MY_LINK_FLAGS "-mx32")
set(CMAKE_EXE_LINKER_FLAGS  "${CMAKE_EXE_LINKER_FLAGS} ${MY_LINK_FLAGS}")

Now the generated executable are elf32_x86-64 format.

So basically, I add the -mx32 to both compile and link flags.

And in the generated rules.ninja file, the link rule goes like this:

command = $PRE_LINK && /usr/bin/c++ $FLAGS $LINK_FLAGS $in -o $TARGET_FILE $LINK_PATH $LINK_LIBRARIES && $POST_BUILD

The $FLAGS and $LINK_FLAGS are defined in the build.ninja file as below:

FLAGS = -Wall -Wshadow -Werror  -mx32 ...
LINK_FLAGS = -mx32 ...

So essentially, there are 2 -mx32 options in the ninja command definition contributed by the $FLAGS $LINK_FLAGS respectively.

So why do I need to specify the -mx32 for twice??

And I don't understand why I can specify -mx32 for CMAKE_EXE_LINKER_FLAGS.

First, -mx32 is only a compile option (ref), not a linker option.

Second, from the link rule definition, the $LINK_FLAGS are passed to usr/bin/c++ without a -Wl, prefix, so even the option can be appreciated by the linker, it won't be passed to the linker.


Solution

  • GCC will adjust the linker command line accordingly if you invoke it as gcc -mx32. It is more than just a compiler flag.