Ask Your Question

Revision history [back]

Link fails when cross compiling OpenCV 3.3.1 for Raspberry Pi Zero even though libraries are present

I've been attempting to build OpenCV 3.3.1 for the Raspberry PI Zero (Raspbian stretch) on a Ubuntu 16.04 machine. I've been following the steps documented here https://github.com/HesselM/rpicross_notes with the major exception that instead of using the tools from https://github.com/raspberrypi/tools which have GCC 4.9.3 I used crosstool-ng to build a GCC 6.3.0 toolchain to match the GCC version shipped with Raspbian stretch. I've installed all the OpenCV prerequistites on a RaspberryPi Zero and then taken a copy of the filesystem to use as the system root for cross-compilation on the host.

CMake confliguration appears to work but the make build fails when linking libopencv_freetype.so as the harfbuzz and freetype libraries are not found.

/opt/cross/x-tools/arm-unknown-linux-gnueabihf/bin/../lib/gcc/arm-unknown-linux-gnueabihf/6.3.0/../../../../arm-unknown-linux-gnueabihf/bin/ld: cannot find -lfreetype
/opt/cross/x-tools/arm-unknown-linux-gnueabihf/bin/../lib/gcc/arm-unknown-linux-gnueabihf/6.3.0/../../../../arm-unknown-linux-gnueabihf/bin/ld: cannot find -lharfbuzz
collect2: error: ld returned 1 exit status
modules/freetype/CMakeFiles/opencv_freetype.dir/build.make:97: recipe for target 'lib/libopencv_freetype.so.3.3.1' failed

These libraries are present in my RaspberryPI system in usr/lib/arm-linux-gnueabihf

-rw-r--r-- 1 dsnowdon dsnowdon 717178 Apr 27  2017 libfreetype.a
-rw-r--r-- 1 dsnowdon dsnowdon    909 Apr 27  2017 libfreetype.la
lrwxrwxrwx 1 dsnowdon dsnowdon     21 Apr 27  2017 libfreetype.so -> libfreetype.so.6.12.3
lrwxrwxrwx 1 dsnowdon dsnowdon     21 Apr 27  2017 libfreetype.so.6 -> libfreetype.so.6.12.3
-rw-r--r-- 1 dsnowdon dsnowdon 567844 Apr 27  2017 libfreetype.so.6.12.3
-rw-r--r-- 1 dsnowdon dsnowdon 739360 Jan 24  2017 libharfbuzz.a
lrwxrwxrwx 1 dsnowdon dsnowdon     24 Jan 24  2017 libharfbuzz.so -> libharfbuzz.so.0.10400.2
lrwxrwxrwx 1 dsnowdon dsnowdon     24 Jan 24  2017 libharfbuzz.so.0 -> libharfbuzz.so.0.10400.2
-rw-r--r-- 1 dsnowdon dsnowdon 551256 Jan 24  2017 libharfbuzz.so.0.10400.2

Building with make VERBOSE=1 shows that the failure occurs when this command is run:

/usr/bin/cmake -E cmake_link_script CMakeFiles/opencv_freetype.dir/link.txt --verbose=1

Which generates this:

/opt/cross/x-tools/arm-unknown-linux-gnueabihf/bin/arm-unknown-linux-gnueabihf-g++ -fPIC   -isystem /opt/software/raspberrypi/rpi-cross/rootfs/usr/include/arm-linux-gnueabihf -isystem /opt/software/raspberrypi/rpi-cross/rootfs/usr/include -isystem /opt/software/raspberrypi/rpi-cross/rootfs/usr/local/include   -fsigned-char -W -Wall -Werror=return-type -Werror=non-virtual-dtor -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wundef -Winit-self -Wpointer-arith -Wshadow -Wsign-promo -Wuninitialized -Winit-self -Wno-narrowing -Wno-delete-non-virtual-dtor -Wno-comment -fdiagnostics-show-option -pthread -fomit-frame-pointer -ffunction-sections  -mfp16-format=ieee -fvisibility=hidden -fvisibility-inlines-hidden -O3 -DNDEBUG  -DNDEBUG  -shared -Wl,-soname,libopencv_freetype.so.3.3 -o ../../lib/libopencv_freetype.so.3.3.1 CMakeFiles/opencv_freetype.dir/src/freetype.cpp.o ../../lib/libopencv_imgproc.so.3.3.1 -ldl -lm -lpthread -lrt ../../3rdparty/lib/libtegra_hal.a -lfreetype -lharfbuzz ../../lib/libopencv_core.so.3.3.1

However if I manually add -L${RPI_ROOTFS}/usr/lib/arm-linux-gnueabihf to give the following then the link works:

/opt/cross/x-tools/arm-unknown-linux-gnueabihf/bin/arm-unknown-linux-gnueabihf-g++ -L/opt/software/raspberrypi/rpi-cross/rootfs/usr/lib/arm-linux-gnueabihf -fPIC   -isystem /opt/software/raspberrypi/rpi-cross/rootfs/usr/include/arm-linux-gnueabihf -isystem /opt/software/raspberrypi/rpi-cross/rootfs/usr/include -isystem /opt/software/raspberrypi/rpi-cross/rootfs/usr/local/include   -fsigned-char -W -Wall -Werror=return-type -Werror=non-virtual-dtor -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wundef -Winit-self -Wpointer-arith -Wshadow -Wsign-promo -Wuninitialized -Winit-self -Wno-narrowing -Wno-delete-non-virtual-dtor -Wno-comment -fdiagnostics-show-option -pthread -fomit-frame-pointer -ffunction-sections  -mfp16-format=ieee -fvisibility=hidden -fvisibility-inlines-hidden -O3 -DNDEBUG  -DNDEBUG  -shared -Wl,-soname,libopencv_freetype.so.3.3 -o ../../lib/libopencv_freetype.so.3.3.1 CMakeFiles/opencv_freetype.dir/src/freetype.cpp.o ../../lib/libopencv_imgproc.so.3.3.1 -ldl -lm -lpthread -lrt ../../3rdparty/lib/libtegra_hal.a -lfreetype -lharfbuzz ../../lib/libopencv_core.so.3.3.1

This led me to think that maybe pkg-config was being called in such a way it was not emitting the correct libdir so I wrote a wrapper script for pkg-config to dump all the invocations by CMake and the output of running the command.

Wrapper script:

#! /bin/bash
LOGFILE=/tmp/pkg-config-wrapper.log
basename="`basename "$0"`"  
timestamp=$(date)
printf "${timestamp} ${basename} $@\nPKG_CONFIG_DIR=${PKG_CONFIG_DIR}\nPKG_CONFIG_LIBDIR=${PKG_CONFIG_LIBDIR}\nPKG_CONFIG_SYSROOT_DIR=${PKG_CONFIG_SYSROOT_DIR}\n"  >> $LOGFILE
result=$(/usr/bin/pkg-config "$@")
printf "pkf-config = ${result}\n"  >> $LOGFILE
echo $result

The output of the wrapper script is here: https://gist.github.com/davesnowdon/548c87d735433d73c587210a27995b6c

As far as I can see pkg-config is being invoked correctly for cross-compilation so now I'm at a loss and am not sure what to try next.

I'm invoking CMake as follows:

cmake \
-D RPI_ROOTFS=${RPI_ROOTFS} \
-D BUILD_TESTS=NO \
-D BUILD_PERF_TESTS=NO \
-D BUILD_PYTHON_SUPPORT=ON \
-D OPENCV_EXTRA_MODULES_PATH=${RPI_CROSS_HOME}/src/opencv_contrib-3.3.1/modules \
-D CMAKE_TOOLCHAIN_FILE=${RPI_CROSS_HOME}/src/opencv-3.3.1/platforms/linux/arm.toolchain.cmake \
${RPI_CROSS_HOME}/src/opencv-3.3.1

Most of the changes to the OpenCV Cmake files are in opencv-3.3.1/cmake/OpenCVMinDepVersions.cmake as described in https://github.com/HesselM/rpicross_notes

set(MIN_VER_CMAKE 2.8.12.2)
set(MIN_VER_CUDA 6.5)
set(MIN_VER_PYTHON2 2.6)
set(MIN_VER_PYTHON3 3.2)
set(MIN_VER_ZLIB 1.2.3)
set(MIN_VER_GTK 2.18.0)

# install dir
set( CMAKE_INSTALL_PREFIX ${RPI_ROOTFS}/usr/local CACHE STRING "")
set( CMAKE_FIND_ROOT_PATH "${RPI_ROOTFS}" CACHE FILEPATH "")
set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER )

# compilers
set( CMAKE_C_COMPILER   "/opt/cross/x-tools/arm-unknown-linux-gnueabihf/bin/arm-unknown-linux-gnueabihf-gcc"    CACHE FILEPATH "")
set( CMAKE_CXX_COMPILER "/opt/cross/x-tools/arm-unknown-linux-gnueabihf/bin/arm-unknown-linux-gnueabihf-g++"    CACHE FILEPATH "")
set( CMAKE_AR           "/opt/cross/x-tools/arm-unknown-linux-gnueabihf/bin/arm-unknown-linux-gnueabihf-ar"     CACHE FILEPATH "")
set( CMAKE_RANLIB       "/opt/cross/x-tools/arm-unknown-linux-gnueabihf/bin/arm-unknown-linux-gnueabihf-ranlib" CACHE FILEPATH "")

#Pkg-config settings
set( RPI_PKGCONFIG_LIBDIR "${RPI_PKGCONFIG_LIBDIR}:${RPI_ROOTFS}/usr/lib/arm-linux-gnueabihf/pkgconfig" )
set( RPI_PKGCONFIG_LIBDIR "${RPI_PKGCONFIG_LIBDIR}:${RPI_ROOTFS}/usr/share/pkgconfig" )
set( RPI_PKGCONFIG_LIBDIR "${RPI_PKGCONFIG_LIBDIR}:${RPI_ROOTFS}/opt/vc/lib/pkgconfig" )

#set( PKG_CONFIG_EXECUTABLE "/usr/bin/pkg-config" CACHE FILEPATH "")
set( PKG_CONFIG_EXECUTABLE "/opt/cross/pkg-config-wrapper.sh" CACHE FILEPATH "")
set( ENV{PKG_CONFIG_DIR}         "" CACHE FILEPATH "")
set( ENV{PKG_CONFIG_LIBDIR}      "${RPI_PKGCONFIG_LIBDIR}" CACHE FILEPATH "")
set( ENV{PKG_CONFIG_SYSROOT_DIR} "${RPI_ROOTFS}" CACHE FILEPATH "")

# setup rpi (target) directories for compiler
set( RPI_INCLUDE_DIR "${RPI_INCLUDE_DIR} -isystem ${RPI_ROOTFS}/usr/include/arm-linux-gnueabihf")
set( RPI_INCLUDE_DIR "${RPI_INCLUDE_DIR} -isystem ${RPI_ROOTFS}/usr/include")
set( RPI_INCLUDE_DIR "${RPI_INCLUDE_DIR} -isystem ${RPI_ROOTFS}/usr/local/include")

set( RPI_LIBRARY_DIR "${RPI_LIBRARY_DIR} -Wl,-rpath ${RPI_ROOTFS}/usr/lib/arm-linux-gnueabihf")
set( RPI_LIBRARY_DIR "${RPI_LIBRARY_DIR} -Wl,-rpath ${RPI_ROOTFS}/lib/arm-linux-gnueabihf")

# Setup C/CXX flags.
set( CMAKE_CXX_FLAGS        "${CMAKE_CXX_FLAGS} ${RPI_INCLUDE_DIR}" CACHE STRING "" FORCE)
set( CMAKE_C_FLAGS          "${CMAKE_CXX_FLAGS} ${RPI_INCLUDE_DIR}" CACHE STRING "" FORCE)
set( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${RPI_LIBRARY_DIR}" CACHE STRING "" FORCE)

#Python2.7
set( PYTHON_EXECUTABLE          /usr/bin/python2.7 CACHE STRING "")
set( PYTHON_LIBRARY_DEBUG       "${RPI_ROOTFS}/usr/lib/arm-linux-gnueabihf/libpython2.7.so" CACHE STRING "")
set( PYTHON_LIBRARY_RELEASE     "${RPI_ROOTFS}/usr/lib/arm-linux-gnueabihf/libpython2.7.so" CACHE STRING "")
set( PYTHON_LIBRARY             "${RPI_ROOTFS}/usr/lib/arm-linux-gnueabihf/libpython2.7.so" CACHE STRING "")
set( PYTHON_INCLUDE_DIR         "${RPI_ROOTFS}/usr/include/python2.7" CACHE STRING "")
set( PYTHON2_NUMPY_INCLUDE_DIRS "${RPI_ROOTFS}/usr/lib/python2.7/dist-packages/numpy/core/include" CACHE STRING "")
set( PYTHON2_PACKAGES_PATH      "${RPI_ROOTFS}/usr/local/lib/python2.7/site-packages" CACHE STRING "")

I also changed opencv-3.3.1/platforms/linux/arm.toolchain.cmake according to https://github.com/HesselM/rpicross_notes replacing "-mthumb" with "-marm" to give this

if(COMMAND toolchain_save_config)
  return() # prevent recursive call
endif()

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_VERSION 1)
if(NOT DEFINED CMAKE_SYSTEM_PROCESSOR)
  set(CMAKE_SYSTEM_PROCESSOR arm)
else()
  #message("CMAKE_SYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR}")
endif()

include("${CMAKE_CURRENT_LIST_DIR}/gnu.toolchain.cmake")

if(CMAKE_SYSTEM_PROCESSOR STREQUAL arm AND NOT ARM_IGNORE_FP)
  set(FLOAT_ABI_SUFFIX "")
  if(NOT SOFTFP)
set(FLOAT_ABI_SUFFIX "hf")
  endif()
endif()

if(NOT "x${GCC_COMPILER_VERSION}" STREQUAL "x")
  set(__GCC_VER_SUFFIX "-${GCC_COMPILER_VERSION}")
endif()

if(NOT DEFINED CMAKE_C_COMPILER)
  find_program(CMAKE_C_COMPILER NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-gcc${__GCC_VER_SUFFIX})
else()
  #message(WARNING "CMAKE_C_COMPILER=${CMAKE_C_COMPILER} is defined")
endif()
if(NOT DEFINED CMAKE_CXX_COMPILER)
  find_program(CMAKE_CXX_COMPILER NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-g++${__GCC_VER_SUFFIX})
else()
  #message(WARNING "CMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} is defined")
endif()
if(NOT DEFINED CMAKE_LINKER)
  find_program(CMAKE_LINKER NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ld${__GCC_VER_SUFFIX} ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ld)
else()
  #message(WARNING "CMAKE_LINKER=${CMAKE_LINKER} is defined")
endif()
if(NOT DEFINED CMAKE_AR)
  find_program(CMAKE_AR NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ar${__GCC_VER_SUFFIX} ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ar)
else()
  #message(WARNING "CMAKE_AR=${CMAKE_AR} is defined")
endif()

if(NOT DEFINED ARM_LINUX_SYSROOT AND DEFINED GNU_MACHINE)
  set(ARM_LINUX_SYSROOT /usr/${GNU_MACHINE}${FLOAT_ABI_SUFFIX})
endif()

if(NOT DEFINED CMAKE_CXX_FLAGS)
  set(CMAKE_CXX_FLAGS           "" CACHE INTERAL "")
  set(CMAKE_C_FLAGS             "" CACHE INTERAL "")
  set(CMAKE_SHARED_LINKER_FLAGS "" CACHE INTERAL "")
  set(CMAKE_MODULE_LINKER_FLAGS "" CACHE INTERAL "")
  set(CMAKE_EXE_LINKER_FLAGS    "" CACHE INTERAL "")

  set(CMAKE_CXX_FLAGS           "${CMAKE_CXX_FLAGS} -fdata-sections -Wa,--noexecstack -fsigned-char -Wno-psabi")
  set(CMAKE_C_FLAGS             "${CMAKE_C_FLAGS} -fdata-sections -Wa,--noexecstack -fsigned-char -Wno-psabi")
  if(CMAKE_SYSTEM_PROCESSOR STREQUAL arm)
set(CMAKE_CXX_FLAGS           "-marm ${CMAKE_CXX_FLAGS}")
set(CMAKE_C_FLAGS             "-marm ${CMAKE_C_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS    "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,nocopyreloc")
  endif()
  if(CMAKE_SYSTEM_PROCESSOR STREQUAL arm)
set(ARM_LINKER_FLAGS "-Wl,--fix-cortex-a8 -Wl,--no-undefined -Wl,--gc-sections -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now")
  elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64)
set(ARM_LINKER_FLAGS "-Wl,--no-undefined -Wl,--gc-sections -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now")
  endif()
  set(CMAKE_SHARED_LINKER_FLAGS "${ARM_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS}")
  set(CMAKE_MODULE_LINKER_FLAGS "${ARM_LINKER_FLAGS} ${CMAKE_MODULE_LINKER_FLAGS}")
  set(CMAKE_EXE_LINKER_FLAGS    "${ARM_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}")
else()
  #message(WARNING "CMAKE_CXX_FLAGS='${CMAKE_CXX_FLAGS}' is defined")
endif()

if(USE_NEON)
  message(WARNING "You use obsolete variable USE_NEON to enable NEON instruction set. Use -DENABLE_NEON=ON instead." )
  set(ENABLE_NEON TRUE)
elseif(USE_VFPV3)
  message(WARNING "You use obsolete variable USE_VFPV3 to enable VFPV3 instruction set. Use -DENABLE_VFPV3=ON instead." )
  set(ENABLE_VFPV3 TRUE)
endif()

set(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} ${ARM_LINUX_SYSROOT})

if(EXISTS ${CUDA_TOOLKIT_ROOT_DIR})
  set(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} ${CUDA_TOOLKIT_ROOT_DIR})
endif()

set(TOOLCHAIN_CONFIG_VARS ${TOOLCHAIN_CONFIG_VARS}
ARM_LINUX_SYSROOT
ENABLE_NEON
ENABLE_VFPV3
CUDA_TOOLKIT_ROOT_DIR
)
toolchain_save_config()

The output from CMake is here: https://gist.github.com/davesnowdon/c9904c315c84863ea8840f2752145126

At this point I'm at a loss and don't know what to try next. What could be the problem?