【记录所学】
本博客为学习Linux开发时的笔记。主要记录如何交叉编译程序。
内容会首先介绍程序运行的一些基础知识,其次介绍常见错误的解决方法,然后介绍交叉编译程序的万能命令,最后以一个实际例子介绍如何交叉编译程序。
简要说明,使用的开发板为:IMX6ULL
;
使用的是基于buildroot
的交叉编译工具链,前缀为arm-buildroot-linux-gnueabihf-
1. 程序运行的一些基础知识
-
编译程序时去哪找头文件?
(1)系统目录:就是交叉编译工具链里的某个include
目录;
(2)也可以自己指定:编译时用-I dir
选项指定。 -
链接时去哪找库文件?
(1)系统目录:就是交叉编译工具链里的某个lib
目录;
(2)也可以自己指定:链接时用-L dir
选项指定。 -
运行时去哪找库文件?
(1)系统目录:就是板子上的/lib、/usr/lib
目录;
(2)也可以自己指定:运行程序用环境变量LD_LIBRARY_PATH
指定。 -
运行时不需要头文件,所以头文件不用放到板子上
2. 常见错误的解决方法
2.1 头文件问题
编译时找不到头文件。在程序中这样包含头文件:#include <xxx.h>
。对于尖括号里的头文件,去哪里找它?
(1)系统目录:就是交叉编译工具链里的某个include
目录;
(2)也可以自己指定:编译时用-I dir
选项指定。
怎么确定“系统目录”?执行下面命令确定目录:
echo 'main(){}'| arm-buildroot-linux-gnueabihf-gcc -E -v -
它会列出头文件目录、库目录(LIBRARY_PATH
)
你需要在头文件目录中确定有没有这个文件,或是自己指定头文件目录。
2.2 库文件问题
链接程序时如果有这样的提示:undefined reference to xxx
,它表示xxx函数未定义。
那么解决方法有两种:
(1)去写出这个函数;
(2)或是使用库函数,那需要在链接时指定库
怎么指定库?
比如,想链接libabc.so
,那链接时加上:-labc
。
库在哪里?
(1)系统目录:就是交叉编译工具链里的某个lib
目录
(2)也可以自己指定:链接时用-L dir
选项指定
怎么确定“系统目录”?执行下面命令确定目录:
echo 'main(){}'| arm-buildroot-linux-gnueabihf-gcc -E -v -
它会列出头文件目录、库目录(LIBRARY_PATH
),你编译出库文件时,可以把它放入系统库目录。
2.3 运行问题
在板子上运行程序。
运行程序时找不到库,错误如下:
error while loading shared libraries: libxxx.so:
cannot open shared object file: No such file or directory
找不到库,库在哪?
(1)系统目录:就是板子上的/lib、/usr/lib
目录
(2)也可以自己指定:
运行程序用环境变量LD_LIBRARY_PATH
指定,执行以下的命令:
export LD_LIBRARY_PATH=/xxx_dir ; ./test
或
LD_LIBRARY_PATH=/xxx_dir ./test
3. 交叉编译程序的万能命令
如果交叉编辑工具链的前缀是arm-buildroot-linux-gnueabihf-
,比如arm-buildroot-linux-gnueabihf-gcc
,交叉编译开源软件时,如果它里面有configure
,万能命令如下:
./configure --host=arm-buildroot-linux-gnueabihf --prefix=$PWD/tmp
make
make install
就可以在当前目录的tmp
目录下看见bin, lib, include
等目录,里面存有可执行程序、库、头文件。
注意: 万能命令的前提是:开源软件中有configure
。根据不同的交叉工具链,万能命令中的host
有所不同。
(1)把头文件、库文件放到工具链目录里
如果你编译的是一个库,请把得到的头文件、库文件放入工具链的include、lib
目录里。别的程序要使用这些头文件、库时,会很方便。
工具链里可能有多个include、lib
目录,放到哪里去?
执行下面命令来确定目录:
echo 'main(){}'| arm-buildroot-linux-gnueabihf-gcc -E -v -
它会列出头文件目录、库目录(LIBRARY_PATH
)。把头文件、库文件放到任一个目录下即可。
(2)把库文件放到板子上的/lib
或/usr/lib
目录里
程序在板子上运行时,需要用到板子上/lib
或/usr/lib
下的库文件;
程序运行时不需要头文件。
4. 交叉编译实例:交叉编译freetype
给IMX6ULL
交叉编译freetype
。
Freetype是开源的字体引擎库,它提供统一的接口来访问多种字体格式文件,从而实现矢量字体显示。我们只需要移植这个字体引擎,调用对应的API接口,提供字体文件,就可以让freetype库帮我们取出关键点、实现闭合曲线,填充颜色,达到显示矢量字体的目的。
freetype
依赖于libpng
,libpng
又依赖于zlib
,所以我们应该:先编译安装zlib
,再编译安装libpng
,最后编译安装freetype
。
但是,有些工具链里有zlib
, 那就不用编译安装zlib
,比如STM32MP157
。
对于IMX6ULL
,由于版本原因,使用过两套工具链:精简版和完善版。对于精简版,里面没有zlib,需要先编译安装zlib。对于完善版,里面有zlib。可以跳过zlib安装。
下面将分别详细介绍IMX6ULL
开发板的两套工具链编译freetype
的过程。
4.1 精简版
精简版交叉编译工具链的环境配置如下:
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
export PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin
可以在终端直接执行环境配置命令,但是重启终端,还要重新执行命令。所以可以将上述命令添加到系统的bashrc
中,使命令永久有效。
确定头文件路径命令:
echo 'main(){}'| arm-buildroot-linux-gnueabihf-gcc -E -v -
可以确定头文件的系统目录为:
/home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/../arm-linux-gnueabihf/libc/usr/include
库文件的系统目录为:
/home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/../arm-linux-gnueabihf/libc/usr/lib/
4.1.1 交叉编译、安装zlib
libpng
依赖于zlib
,所以需要先编译、安装zlib
。命令如下:
tar xzf zlib-1.2.11.tar.gz
cd zlib-1.2.11/
export CC=arm-linux-gnueabihf-gcc
./configure --prefix=$PWD/tmp
make
make install
cd tmp/
#将编译好的头文件放到工具链的头文件目录中(之前确定的)
cp include/* -rf /home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/../arm-linux-gnueabihf/libc/usr/include
#将编译好的库文件放到工具链的库文件目录中(之前确定的)
cp lib/* -rfd /home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/../arm-linux-gnueabihf/libc/usr/lib/
如果在4.1.2中交叉编译、安装libpng时提示错误:configure: error: ZLib not installed
,则按照解决安装libpng的时候出现configure: error: ZLib not installed的问题即可解决。
4.1.2 交叉编译、安装libpng
freetype依赖于libpng,所以需要先编译、安装libpng。命令如下:
tar xJf libpng-1.6.37.tar.xz
cd libpng-1.6.37
# 交叉编译程序的万能命令
./configure --host=arm-linux-gnueabihf --prefix=$PWD/tmp
make
make install
cd tmp/
#将编译好的头文件放到工具链的头文件目录中(之前确定的)
cp include/* -rf /home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/../arm-linux-gnueabihf/libc/usr/include
#将编译好的库文件放到工具链的库文件目录中(之前确定的)
cp lib/* -rfd /home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/../arm-linux-gnueabihf/libc/usr/lib/
4.1.3 交叉编译、安装freetype
命令如下:
tar xJf freetype-2.10.2.tar.xz
cd freetype-2.10.2
# 交叉编译程序的万能命令
./configure --host=arm-linux-gnueabihf --prefix=$PWD/tmp
make
make install
cd tmp/
#将编译好的头文件放到工具链的头文件目录中(之前确定的)
cp include/* -rf /home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/../arm-linux-gnueabihf/libc/usr/include
#将编译好的库文件放到工具链的库文件目录中(之前确定的)
cp lib/* -rfd /home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/../arm-linux-gnueabihf/libc/usr/lib/
4.2 完善版
本文所使用的IMX6ULL
开发板的交叉工具链是完善版的。
精简版交叉编译工具链的环境配置如下:
export ARCH=arm
export CROSS_COMPILE=arm-buildroot-linux-gnueabihf-
export PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin
如下图确定头文件和库文件路径。
可以确定头文件的系统目录为:
/home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/../arm-linux-gnueabihf/libc/usr/include
库文件的系统目录为:
/home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/../arm-linux-gnueabihf/libc/usr/lib/
其实这两个路径不是唯一的,可以选择图片中的其他路径。
4.2.1 交叉编译、安装libpng
命令如下:
tar xJf libpng-1.6.37.tar.xz
cd libpng-1.6.37
# 交叉编译程序的万能命令
./configure --host=arm-buildroot-linux-gnueabihf --prefix=$PWD/tmp
make
make install
cd tmp/
#将编译好的头文件放到工具链的头文件目录中(之前确定的)
cp include/* -rf /home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/arm-buildroot-linux-gnueabihf/sysroot/usr/include
#将编译好的库文件放到工具链的库文件目录中(之前确定的)
cp lib/* -rfd /home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/arm-buildroot-linux-gnueabihf/sysroot/usr/lib/
4.2.2 交叉编译、安装freetype
命令如下:
tar xJf freetype-2.10.2.tar.xz
cd freetype-2.10.2
# 交叉编译程序的万能命令
./configure --host=arm-buildroot-linux-gnueabihf --prefix=$PWD/tmp
make
make install
cd tmp/
#将编译好的头文件放到工具链的头文件目录中(之前确定的)
cp include/* -rf /home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/arm-buildroot-linux-gnueabihf/sysroot/usr/include
#将编译好的库文件放到工具链的库文件目录中(之前确定的)
cp lib/* -rfd /home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/arm-buildroot-linux-gnueabihf/sysroot/usr/lib/
写在最后,一些心得:
交叉编译程序与本地编译程序的区别:
本地编译开源软件使用make
和make install
之后,相关的头文件和依赖文件会被加载到本机的/usr/include
以及 lib
或/usr/lib/
,之后其他程序需要使用时就会去这些路径中查找。
而交叉编译不能这样,因为交叉编译程序后得到的头文件和依赖文件不是给本机使用的。所以需要将得到的头文件和依赖文件放到交叉工具链的头文件目录和库文件目录!!!之后交叉编译自己的程序需要相关的头文件和库文件会去交叉工具链的头文件和库文件目录查找。