libiconv冲突 - Petalinux构建

问题现象:
    使用petalinux-build构建时突然报告undefined reference to 'libiconv'等错误。

问题原因:

    编译机器上同时安装了glibc和GNU libiconv。libiconv的头文件iconv.h通过宏将iconv定义成了libiconv,并且被优先包含。同时链接选项中没有加入链接libiconv的选项。Glibc定义的符号是iconv,不用显式链接。Libiconv定义的符号是libiconv,需要显式链接。

解决方案:

    1. 在gcc的默认搜索路径,同时优先于libiconv的头文件路径中创建一个Glibc的iconv.h副本,使用iconv符号,默认链接到glibc。该方案需要root权限,并且会影响到所有用户。

    2. 理论上更好的方案是通过CPATH或者C_INCLUDE_PATH变量。但是petalinux使用的是Yocto,外部shell定义的环境变量不能直接使用。Yocto定义了BB_ENV_EXTRAWHITE可以来传递外部环境变量。在.bashr中加入两行

export C_INCLUDE_PATH=/your/path/to/include/
# Append the name of environment variables which will be imported into Yocto
export BB_ENV_EXTRAWHITE="$BB_ENV_EXTRAWHITE C_INCLUDE_PATH"

在petalinux工程local.conf文件.../components/yocto/conf/local.conf中加入export C_INCLUDE_PATH。这样无论是系统自带gcc,还是petalinux的gcc都会先搜索C_INCLUDE_PATH下的文件。这时虽然会报告Taskhash mismatch的错误,但还是能成功构建出相应的文件。

知识总结:
    Petalinux构建过程中代码的存放路径位于工程目录的tmp/子目录下,可以通过petalinux-config进行配置该路径。
    Petalinux编译出错时,日志build.log可以提供很多信息。
    libiconv和glibc中iconv的关系。
    GCC的头文件搜索顺序。可以通过cpp -v /dev/null -o /dev/nullecho | gcc -E -Wp,-v -来获取。

    Enable Buildtools Extended这个选项使能之后,会使用petalinux安装目录下的gcc进行编译,这时和系统自带的gcc搜索的路径不完全一样。


调查过程:
    网上有人说原因是没有安装iconv库,或者是没有加上链接选项-liconv,由于之前没有发现这个错误,并且构建命令是petalinux自动生成的,不知道怎么修改,因此暂时没有顺着这个调查方向。
    查看petalinux工程目录下build/build.log记录的构建过程,显示是在链接unzip时出现该错误。因为使用的Ubuntu-20.04上已经有了unzip工具,所以要先弄清楚为什么会构建unzip,是不是之前没有编译unzip就没有报错。
    因为之前直接从bsp文件创建工程后是可以构建成功的,所以怀疑是工程被修改后造成构建不过。之前创建了工程后,使用petalinux-config导入过不同的XSA文件,以及修改了某些编译项,例如离线编译包路径等。换用其它XSA文件,仍然出现该错误。删除了当前构建不过的工程,重新从bsp文件再创建一次工程目录之后,仍然出现同样错误。
    使用petalinux-config命令时,有一项配置路径"Yoco settings ---> Enable Buildtools Extended",帮助信息显示是用于控制是否编译gcc和其它工具。怀疑是使能了该配置所以会编译unzip。在重新从bsp文件创建工程后运行petalinux-config命令,显示这个选项默认是关闭的。
    再次查看build.log,显示出了编译unzip对应的bitbake脚本所在的路径,位于components/yocto/layers/core/meta/recipes-extended/unzip/unzip_6.0.bb。脚本中显示了编译使用的源码以及一系列patch文件。怀疑Enable Buildtools Extended是否会影响创建相应的bitbake脚本。使用bsp文件创建了两个工程目录,分别关闭和打开这个选项,显示都会创建相应的目录和bitbake脚本。
    通过bitbake脚本中显示的源码在离线包downloads目录下找到了unzip60.tar.gz这个目录,将目录拷贝到编译机器并解压,使用make -f unix/Makefile generic,没有出现错误。查看unix/unix.c源码,没有出现对libiconv的调用。网上说iconv是针对unzip一个补丁,bitbake脚本显示的一个patch文件带有iconv,有可能和这个文件相关,但是还没有找到对应的patch文件。
    接下来就要想办法编译加入补丁后的unzip。再次查看build.log的信息,其中显示出了构建unzip的make命令工作目录,build/tmp/work/x86_64-linux/unzip-native/1_6.0-r5/unzip60。进入这个目录确实是一个unzip的构建目录,使用make -f unix/Makefile generic也得到了同样的错误。同时在上一级目录1_6.0-r5下还发现了一个带有iconv的文件,其内容显示确定有调用iconv。
    查看该目录下的unix.c文件,发现调用的是iconv/iconv_open/iconv_close等函数,但是链接时却报告的是libiconv_xxx未定义等错误。怀疑是被宏定义替换掉了,上网搜索后验证了该想法。有篇文章提到包含了iconv.h文件,使用的是/usr/local/include/路径下,其中就包含了这样的宏定义。而另一个位置的头文件,/usr/include/iconv.h,则没有包含这样的宏定义。查看自己的编译机器上相同位置的头文件,确实如此。
    创建了一个最简单的测试文件包含iconv_open/iconv_close的调用,编译也是报告libiconv_open未定义。GNU libiconv提供的符号以libiconv_xxx命令,Glibc提供的符号以iconv_xxx命名。GNU libiconv提供了字符集转换功能。安装GNU libiconv之后,会在/usr/local/include下安装相应的头文件。
    libiconv提供了两种使用方式,第一种是库方式,第二种是插件方式。库方式下其定义的符号就是libiconv_xxx。第二种是插件方式,其定义的符号还是iconv_xxx,和glibc一样。如果要使用libiconv,可以通过控制LD的行为来选择优先加载libiconv,而不是glibc。在GNU/Linux上可以使用LD_PRELOAD。
    在编译机器上使用gcc -v选项打印出头文件搜索路径及顺序,发现/usr/local/include优先于/usr/include
    在编译机器上使用的libicon是以库方式安装的。因此解决方案之一就是要优先包含glibc定义的头文件iconv.h。在优先于/usr/local/include的目录下创建一个iconv.h的副本后,问题消失。

以下是从Yocto官方手册摘抄出来的解释。

15.15.

When I try to build a native recipe, the build fails with iconv.h problems.

 

If you get an error message that indicates GNU libiconv is not in use but iconv.h has been included from libiconv, you need to check to see if you have a previously installed version of the header file in /usr/local/include.

     #error GNU libiconv not in use but included iconv.h is from libiconv
                

If you find a previously installed file, you should either uninstall it or temporarily rename it and try the build again.

This issue is just a single manifestation of "system leakage" issues caused when the OpenEmbedded build system finds and uses previously installed files during a native build. This type of issue might not be limited to iconv.h. Be sure that leakage cannot occur from /usr/local/include and /opt locations.

 

 

posted @ 2022-02-22 11:15  watsondd  阅读(652)  评论(0编辑  收藏  举报