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

1

2

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.

Immortality answered 1/11, 2019 at 4:57 Comment(1)
Have you seen this askubuntu.com/questions/85978/…? export LDFLAGS='-m32 -L/usr/lib32'Lindgren
T
4

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

Talamantes answered 1/11, 2019 at 8:6 Comment(8)
To elaborate, adding link flag -Wl,--oformat=elf32-x86-64, is the wrong thing to do. Instead, add -mx32 to the link line.Sholeen
@EmployedRussian I tried adding -Wl, -mx32 for the linker. My ultimate link flags are -Wl,-mx32 -rdynamic. They are ultimately passed to /usr/bin/x86_64-linux-gnu-g++-7 and then /usr/bin/x86_64-linux-gnu-ld.bfd. But I got this error: /usr/bin/ld: unrecognised emulation mode: x32. It seems I shouldn't pass -mx32 to the linker because it is recognized as the -m link option (sourceware.org/binutils/docs/ld/…).Immortality
@FlorianWeimer If I only add the -mx32 to the g++ compile options, I got the .o file as elf32_x86-64, which is good. But the linker requires the output format to be elf64_x86-64. So I got this error /usr/bin/ld: i386:x64-32 architecture of input file "googletest/CMakeFiles/sample2_unittest.dir/samples/sample2_unittest.cc.o" is incompatible with i386:x86-64 output. So it seems I have to explicitly tell the linker to output as elf32_x86-64 format through the -Wl,--oformat=elf32-x86-64.Immortality
@Immortality You should pass -mx32 without -Wl, prefix. You must use -mx32 on both compile and link command lines.Sholeen
@EmployedRussian Thanks a lot! It seems to be working. Now I got the elf32_x86-64 executable after putting -mx32 for both compile and link flags. I will try more later. But I don't understand why I can pass the -mx32 to link command line? It is a compiler option (gcc.gnu.org/onlinedocs/gcc-6.1.0/gcc/…). But it is NOT listed as a ld option. And If I don't use the -Wl, prefix, it means it will NOT be passed to the linker. Then why linker would care about it?Immortality
@Immortality Just because the -mx32 isn't passed to the linker doesn't mean that is has no effect on the link line. In this case, presence of -mx32 on the link line causes gcc to pass -m elf32_x86_64 to ld. In addition, a different dynamic linker and different crt0.o are selected. Like the answer says: -mx32 is more than just a compiler flag.Sholeen
@EmployedRussian By link line, do you mean the command line options passed to the linker? IMHO, it will be much easier for me to understand if I only have to place the -mx32 for once in the compile flag. And also I tried to place -Wl, -melf32_x86_64 in the CMAKE_EXE_LINKER_FLAGS becuase -m is a listed linker option. But it complains /usr/bin/ld: cannot find -lstdc++. So weird...Immortality
@EmployedRussian I updated my question with some analysis of the generated ninja build file. It seems to me a really weird usage paradigm...Immortality

© 2022 - 2024 — McMap. All rights reserved.