cross compilation fails due to no "-rpath-link" linker flag generated by cmake
Use attached test case, and the following reproduce steps:
- download a cross-compile toolchain, for example, an aarch64 toolchain from here
- extract the toolchain to a directory and rename the directory name to aarch64-linux, for example, /tmp/aarch64-linux
- export CROSS_COMPILE=/tmp/aarch64-linux/bin/aarch64-linux-
- extract the test case, for example, to /tmp/cross_compile-link
- cd /tmp/cross_compile-link && ./test.sh
-- The C compiler identification is GNU 9.3.0
-- The CXX compiler identification is GNU 9.3.0
-- Check for working C compiler: /tmp/aarch64-linux/bin/aarch64-linux-gcc
-- Check for working C compiler: /tmp/aarch64-linux/bin/aarch64-linux-gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /tmp/aarch64-linux/bin/aarch64-linux-g++
-- Check for working CXX compiler: /tmp/aarch64-linux/bin/aarch64-linux-g++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /tmp/cross_compile-link/build
Scanning dependencies of target foo
[ 16%] Building C object CMakeFiles/foo.dir/foo.c.o
[ 33%] Linking C shared library libfoo/libfoo.so
[ 33%] Built target foo
Scanning dependencies of target bar
[ 50%] Building C object CMakeFiles/bar.dir/bar.c.o
[ 66%] Linking C shared library libbar/libbar.so
[ 66%] Built target bar
Scanning dependencies of target main
[ 83%] Building C object CMakeFiles/main.dir/main.c.o
[100%] Linking C executable main
/tmp/aarch64-linux/bin/../lib/gcc/aarch64-buildroot-linux-gnu/9.3.0/../../../../aarch64-buildroot-linux-gnu/bin/ld: warning: libfoo.so, needed by libbar/libbar.so, not found (try using -rpath or -rpath-link)
/tmp/aarch64-linux/bin/../lib/gcc/aarch64-buildroot-linux-gnu/9.3.0/../../../../aarch64-buildroot-linux-gnu/bin/ld: libbar/libbar.so: undefined reference to `foo'
collect2: error: ld returned 1 exit status
CMakeFiles/main.dir/build.make:95: recipe for target 'main' failed
make[2]: *** [main] Error 1
CMakeFiles/Makefile2:67: recipe for target 'CMakeFiles/main.dir/all' failed
make[1]: *** [CMakeFiles/main.dir/all] Error 2
Makefile:83: recipe for target 'all' failed
make: *** [all] Error 2
Check the linker command for "main":
> cat build/CMakeFiles/main.dir/link.txt
/tmp/aarch64-linux/bin/aarch64-linux-gcc -rdynamic CMakeFiles/main.dir/main.c.o -o main -Wl,-rpath,/tmp/cross_compile-link/build/libbar libbar/libbar.so
The warning says (try using -rpath or -rpath-link), but manually add "-Wl,-rpath,/tmp/cross_compile-link/build/libfoo" does not work:
> /tmp/aarch64-linux/bin/aarch64-linux-gcc -rdynamic CMakeFiles/main.dir/main.c.o -o main -Wl,-rpath,/tmp/cross_compile-link/build/libbar -Wl,-rpath,/tmp/cross_compile-link/build/libfoo libbar/libbar.so
/tmp/aarch64-linux/bin/../lib/gcc/aarch64-buildroot-linux-gnu/9.3.0/../../../../aarch64-buildroot-linux-gnu/bin/ld: warning: libfoo.so, needed by libbar/libbar.so, not found (try using -rpath or -rpath-link)
/tmp/aarch64-linux/bin/../lib/gcc/aarch64-buildroot-linux-gnu/9.3.0/../../../../aarch64-buildroot-linux-gnu/bin/ld: libbar/libbar.so: undefined reference to `foo'
collect2: error: ld returned 1 exit status
Adding "-Wl,-rpath-link,/tmp/cross_compile-link/build/libfoo" works.
> /tmp/aarch64-linux/bin/aarch64-linux-gcc -rdynamic CMakeFiles/main.dir/main.c.o -o main -Wl,-rpath,/tmp/cross_compile-link/build/libbar -Wl,-rpath-link,/tmp/cross_compile-link/build/libfoo libbar/libbar.so
But if I do a native build with the same test case, it succeeded:
- export CROSS_COMPILE=
- cd /tmp/cross_compile-link && ./test.sh
> ./test.sh
-- The C compiler identification is GNU 7.5.0
-- The CXX compiler identification is GNU 7.5.0
-- Check for working C compiler: /usr/bin/gcc
-- Check for working C compiler: /usr/bin/gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/g++
-- Check for working CXX compiler: /usr/bin/g++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /tmp/cross_compile-link/build
Scanning dependencies of target foo
[ 16%] Building C object CMakeFiles/foo.dir/foo.c.o
[ 33%] Linking C shared library libfoo/libfoo.so
[ 33%] Built target foo
Scanning dependencies of target bar
[ 50%] Building C object CMakeFiles/bar.dir/bar.c.o
[ 66%] Linking C shared library libbar/libbar.so
[ 66%] Built target bar
Scanning dependencies of target main
[ 83%] Building C object CMakeFiles/main.dir/main.c.o
[100%] Linking C executable main
[100%] Built target main
I googled on the internet and found the following 2 issues reported before seem to be similar to what I see:
- https://cmake.org/pipermail/cmake/2015-August/061433.html
- https://cmake.org/pipermail/cmake-developers/2018-July/030746.html
I found a workaround is to change the libbar dependency to libfoo from PRIVATE to PUBLIC, and the cross-compilation will succeed.
target_link_libraries(bar
PUBLIC foo
)
> ./test.sh
-- The C compiler identification is GNU 9.3.0
-- The CXX compiler identification is GNU 9.3.0
-- Check for working C compiler: /tmp/aarch64-linux/bin/aarch64-linux-gcc
-- Check for working C compiler: /tmp/aarch64-linux/bin/aarch64-linux-gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /tmp/aarch64-linux/bin/aarch64-linux-g++
-- Check for working CXX compiler: /tmp/aarch64-linux/bin/aarch64-linux-g++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /tmp/cross_compile-link/build
Scanning dependencies of target foo
[ 16%] Building C object CMakeFiles/foo.dir/foo.c.o
[ 33%] Linking C shared library libfoo/libfoo.so
[ 33%] Built target foo
Scanning dependencies of target bar
[ 50%] Building C object CMakeFiles/bar.dir/bar.c.o
[ 66%] Linking C shared library libbar/libbar.so
[ 66%] Built target bar
Scanning dependencies of target main
[ 83%] Building C object CMakeFiles/main.dir/main.c.o
[100%] Linking C executable main
[100%] Built target main
> file build/main
build/main: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 4.9.0, not stripped