CMake交叉编译CLAPACK
CLAPACK是LAPACK(Linear Algebra PACKage)的C语言接口版本。
CLAPACK源码和相关文档下载地址:
本例交叉编译所用的CLAPACK软件包为 “clapack-3.2.1-CMAKE.tgz”。
软件包内已经包含了CMakeLists.txt和Makefile配置,在本地环境中,可以按照包内README.install文件的指引直接使用make编译出libf2c.a、libblas.a、liblapack.a三个静态库,在使用时,只需在自己的工程静态链接三个静态库,并复制CLAPACK包内INCLUDE中的头文件并include包含,如无意外即可使用。
或通过cmake编译,例如在linux+cmake环境中:
# cd到工程目录下
cd clapack-3.2.1-CMAKE
mkdir build
cd build
cmake ..
make
编译成功后在build内相关目录可以找到上面那三个生成静态库。
这篇blog主要记录和整理博主在交叉编译clapack,嵌入式移植中遇到的一些问题和解决方法。
1. linux下交叉编译工具链的下载
下载步骤:
1) 进入下载列表


2) 根据本地和目标平台架构选择对应工具链版本
我使用的本地环境是WSL1 ubuntu 20.04,目标环境是arm架构,裸金属(raw-metal,即非linux系统),没有hf(hard float?),所以选的是arm-none-eabi

2. 工具链解压和环境变量配置
在/usr/local目录下新建arm文件夹:
cd /usr/local
mkdir arm && cd arm
把 arm-gnu-toolchain-12.3.rel1-x86_64-arm-none-eabi.tar.xz 移动到 /usr/local/arm目录下并解压:
sudo tar -vxf arm-gnu-toolchain-12.3.rel1-x86_64-arm-none-eabi.tar.xz
添加环境变量:
sudo vim ~/.bashrc
末尾添加两行,保持退出,注意根据自己的路径和交叉编译工具名做适当调整:
export PATH=$PATH:/usr/local/arm/arm-gnu-toolchain-12.3.rel1-x86_64-arm-none-eabi/bin
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/arm/arm-gnu-toolchain-12.3.rel1-x86_64-arm-none-eabi/lib
使用source命令重新加载生效该配置文件
source ~/.bashrc
测试是否配置成功:
arm-none-eabi-gcc -v
正常情况下会输出正确版本信息。
2. cmake 工具链配置文件
由于与目标环境(Arm)不同的主机环境(在本例中是WSL1 ubuntu)上配置和构建代码,所以我们需要向CMake提供关于目标环境的信息,这些信息记录在toolchain.cmake文件中
参考链接: https://www.bookstack.cn/read/CMake-Cookbook/content-chapter13-13.2-chinese.md
toolchain.cmake 工具链文件通过 cmake 命令行参数 --toolchain path/to/file 或-DCMAKE_TOOLCHAIN_FILE=path/to/file引用:
cmake -DCMAKE_TOOLCHAIN_FILE=path/to/file ..
工具链配置参数文件事先给编译器设置相关变量,CMake 交叉编译时 CMAKE_CROSSCOMPILING 为 true
(https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling)。
2.1 在工程目录下创建一个toolchain.cmake文件,内容:
# the name of the target operating system
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
# which compilers to use
set(CMAKE_C_COMPILER arm-none-eabi-gcc)
set(CMAKE_CXX_COMPILER arm-none-eabi-g++)
set(CMAKE_AR arm-none-eabi-ar)
set(CMAKE_AS arm-none-eabi-as)
set(CMAKE_LD arm-none-eabi-ld)
set(CMAKE_NM arm-none-eabi-nm)
set(CMAKE_STRIP arm-none-eabi-strip)
# adjust the default behaviour of the find commands:
# search headers and libraries in the target environment
set(CMAKE_FIND_ROOT_PATH
"/home/cjliu/clapack-3.2.1-CMAKE/build")
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
# search programs in the host environment
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# $ arm-none-eabi-gcc --specs=nosys.specs $(OTHER_LINK_OPTIONS)
# add_compile_options(--specs=nosys.specs -nostdlib)
set(CMAKE_SHARED_LINKER_FLAGS "-specs=nosys.specs")
set(CMAKE_EXE_LINKER_FLAGS "-specs=nosys.specs")
参考 https://www.cnblogs.com/flyinggod/p/13713646.html。
参数说明
CMAKE_SYSTEM_NAME
这个参数是利用cmake进行交叉编译必须设置的,通常都是Linux或者Windows,声明要利用cmake创建运行在目标系统上的文件。如果要创建运行在没有操作系统的嵌入式环境,该参数要设置成Generic,如果CMAKE_SYSTEM_NAME进行了预先设置,CMAKE_CROSSCOMPILING被自动置位为True,所以可以被用来测试时候在交叉编译环境进行编译。
CMAKE_TRY_COMPILE_TARGET_TYPE
cmake 在 configure 阶段会编译一个测试程序,我刚开始没有设置该参数得到了如下链接错误:
The C compiler
"/usr/local/arm/arm-gnu-toolchain-12.3.rel1-x86_64-arm-none-eabi/bin/arm-none-eabi-gcc"
is not able to compile a simple test program.
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)告诉编译器仅进行静态库链接
CMAKE_C_COMPILER
指定 C 编译器,其他 CMAKE_AR、CMAKE_LD 等都是指定链接器、库打包器之类的
CMAKE_FIND_ROOT_PATH
"Semicolon-separated list of root paths to search on the filesystem.
This variable is most useful when cross-compiling. CMake uses the paths in this list as alternative roots to find filesystem items with find_package(), find_library() etc."
CMAKE_FIND_ROOT_PATH默认设置为空。如果设置之后,cmake的find_xx()默认会首先搜索这个位置下文件,之后去其他位置查找(toolchain工具下的文件或本地环境下的文件)。
如果你的工程不仅需要toolchain里面的工具,还有其他目标平台的附加库,则需要对CMAKE_FIND_ROOT_PATH添加相应的位置目录。
CMAKE_FIND_ROOT_PATH_MODE_INCLUDE、CMAKE_FIND_ROOT_PATH_MODE_LIBRARY
设置find_file()、file_path()和find_library()等命令的查找模式选项,可以是NEVER,ONLY,BOTH(默认)
NEVER:CMAKE_ROOT_PATH将会被FIND_PROGRAM()忽略,除非显式使能。
ONLY:FIND_PROGRAM只在CMAKE_FIND_ROOT_PATH指定的目录下查找需要的文件,或toolchain工具链中查找,忽略本地环境文件;
BOTH:先在CMAKE_FIND_ROOT_PATH查找之后再去其他位置(本地环境等)。
交叉编译中,需要链接的库文件、头文件等文件通常都是针对目标平台,所以一般设置为ONLY。
CMAKE_FIND_ROOT_PATH_MODE_PROGRAM
设置find_program()等命令的查找模式选项,find_program()一般用于查找可执行程序,一些工程的编译过程中可能会调用EXECUTABLE_PROCESS() 或者 ADD_CUSTOM_COMMAND()执行一些程序。所以在大多数情况下,这些编译中调用的可执行程序是必须运行在本机环境的,所以CMAKE_FIND_ROOT_PATH_MODE_PROGRAM通常被设置成为NEVER。参考CMake: (三) 交叉编译
CMAKE_SHARED_LINKER_FLAGS、CMAKE_EXE_LINKER_FLAGS
用于设置链接阶段时的链接命令参数。我在刚开始用arm-none-eabi-gcc交叉编译Clapack时没有设置这两项参数,make编译链接阶段得到了以下错误:
libc.a(libc_a-exit.o): in function `exit':
exit.c:(.text.exit+0x28): undefined reference to `_exit'
即_exit函数未定义,这个函数在linux中的标准C库中是有的,但是裸金属交叉编译使用不了,toolchain工具链中的libc.a没有找到该函数的定义。经查,arm-none-eabi工具链提供了一些预构建C库,:一个是标准的newlib,另一个是newlib-nano优化过代码大小:

其中libnosys.a好像是使用retarget重定位定义了_exit等的一些基本函数的,librdimon.a 好像是一种semi-host机制,我也不太搞得清楚,参考链接:
Semihosting is a mechanism that enables code running on an ARM target to communicate and use the Input/Output facilities on a host computer that is running a debugger。
他们都有_exit的定义,通过nm指令查看:

这个toolchain里面含有相应的GCC编译配置文件 specs, 用来控制gcc的行为,要使用这些选项,需要配置链接选项:
set(CMAKE_SHARED_LINKER_FLAGS "-specs=nosys.specs")
set(CMAKE_EXE_LINKER_FLAGS "-specs=nosys.specs")
在链接时就会加上-specs=nosys.specs命令参数。解决undefined reference to `_exit'的问题。
3. CMake交叉编译
交叉编译时先去掉TESTING测试代码,clapack-3.2.1-CMAKE/CMakeLists.txt注释下面一行
# add_subdirectory(TESTING)
toolchain.cmake配置好后,进入build目录下编译:
mkdir build
cd build
cmake -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake ..
得到以下编译错误:
3.1 arithchk.c:(.text+0x490): undefined reference to log、sqrt
找到添加arithchk.c的CMakeLists.txt文件,位于clapack-3.2.1-CMAKE/F2CLIBS/libf2c/CMakeLists.txt内:
# For INTEGER*8 support (which requires system-dependent adjustments to
# f2c.h), add ${QINT} to the OFILES assignment below...
add_executable(arithchk arithchk.c)
if(UNIX)
target_link_libraries(arithchk m)
endif()
set_target_properties(arithchk PROPERTIES COMPILE_DEFINITIONS
"NO_FPINIT;NO_LONG_LONG")
ADD_CUSTOM_COMMAND(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/arith.h
COMMAND arithchk > ${CMAKE_CURRENT_BINARY_DIR}/arith.h
DEPENDS arithchk
)
因为用交叉编译目标平台是裸机,UNIX未定义,所以arithchk缺少数学库链接,另外,ADD_CUSTOM_COMMAND()命令应该是调用刚生成的arithchk来自动生成头文件arith.h,但是目标平台的程序不能在本地环境中运行,所以必须改以下以上配置。
我的解决办法是,先利用本地编译器编译一遍Clapack,把生成的arithchk可执行程序保存下来,例如放在clapack-3.2.1-CMAKE工程目录中,然后修改clapack-3.2.1-CMAKE/F2CLIBS/libf2c/CMakeLists.txt:
# For INTEGER*8 support (which requires system-dependent adjustments to
# f2c.h), add ${QINT} to the OFILES assignment below...
if(CMAKE_CROSSCOMPILING)
add_executable(arithchk IMPORTED)
set_target_properties(arithchk PROPERTIES IMPORTED_LOCATION /home/cjliu/clapack-3.2.1-CMAKE/arithchk)
else()
add_executable(arithchk arithchk.c)
if(UNIX)
target_link_libraries(arithchk m)
endif()
set_target_properties(arithchk PROPERTIES COMPILE_DEFINITIONS
"NO_FPINIT;NO_LONG_LONG")
endif()
ADD_CUSTOM_COMMAND(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/arith.h
COMMAND arithchk > ${CMAKE_CURRENT_BINARY_DIR}/arith.h
DEPENDS arithchk
)
3.2 in function ***':***.c: undefined reference to 'sqrt
找到clapack-3.2.1-CMAKE/BLAS/SRC/CMakeLists.txt,修改:
add_library(blas ${ALLOBJ})
if(UNIX)
target_link_libraries(blas m)
endif()
# 改成:
add_library(blas ${ALLOBJ})
if(UNIX OR CMAKE_CROSSCOMPILING)
target_link_libraries(blas m)
endif()
3.3 in function t_runc':endfile.c:(.text+0x224): undefined reference to 'ftruncate
ftruncate函数未定义,经查,arm-none-eabi toolchain工具链中的librdimon_nano.a库有该函数的定义:

所以可以把toolchain.cmake文件的链接参数选项改成:
set(CMAKE_SHARED_LINKER_FLAGS "-specs=rdimon.specs")
set(CMAKE_EXE_LINKER_FLAGS "-specs=rdimon.specs")
如果编译工具链没有radmon.specs,Clapack设有一个宏定义NO_TRUNCATE,定义该宏后Clapack会使用其他方法替代ftruncate函数的调用(查看/clapack-3.2.1-CMAKE/F2CLIBS/libf2c/README),因此可以在toolchain.cmake配置文件里加入编译选项:
add_compile_options(-DNO_TRUNCATE)
或者在发生错误的CMake target上 用 target_compile_options添加编译参数:
# clapack-3.2.1-CMAKE/F2CLIBS/libf2c/CMakeLists.txt
if(CMAKE_CROSSCOMPILING)
target_compile_options(f2c PUBLIC "-DNO_TRUNCATE")
endif()

浙公网安备 33010602011771号