CMake交叉编译CLAPACK

CLAPACK是LAPACK(Linear Algebra PACKage)的C语言接口版本。

CLAPACK源码和相关文档下载地址:

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下交叉编译工具链的下载

在ubuntu中系统自带的gcc编译器是基于x86架构的,通过该编译器编译出来的代码只能在x86架构上运行。如果需要在x86架构上编译arm架构的代码,就需要一个可以在x86架构上运行并且可以编译arm架构的交叉编译工具链。

Linaro GCC交叉编译链下载地址

下载步骤:

1) 进入下载列表

进入Linaro下载列表
进入Linaro下载列表

2) 根据本地和目标平台架构选择对应工具链版本

我使用的本地环境是WSL1 ubuntu 20.04,目标环境是arm架构,裸金属(raw-metal,即非linux系统),没有hf(hard float?),所以选的是arm-none-eabi
进入Linaro下载列表

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_CROSSCOMPILINGtrue
(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_INCLUDECMAKE_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_FLAGSCMAKE_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优化过代码大小:
arm-none-eabi-lib

其中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指令查看:
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 logsqrt

找到添加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库有该函数的定义:
nm

所以可以把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()
posted @ 2023-10-26 16:16  CasparLeo  阅读(578)  评论(0)    收藏  举报