嵌入式系统

时间:2017-04-19 09:07:01

标签: c++ linux linker yocto zynq

我有一些关于嵌入式系统编译问题的相关问题。我的问题不仅仅是关于如何做某事,而是更多关于为什么,因为我有我的问题的解决方案(但也许有更好的问题?),但不知道为什么有些东西在某些条件下工作,并且不起作用其他。我已经花了一些时间用这个,但直到昨天我做了一些盲目的尝试和错误,并且不知道我在做什么。是时候停止了!请帮忙。

方案

我想在Zedboard上为Xilinx的Zynq ARM处理器开发一个应用程序。该应用程序将涉及多线程,一些音频操作和httpserver。所以我需要pthread,alsa,sndfile和microhttpd库。我用yocto创建了rootfs。在原始的conf.local文件中,我添加/修改了这些行:

BB_NUMBER_THREADS ?= "${@oe.utils.cpu_count()}"
PARALLEL_MAKE ?= "-j ${@oe.utils.cpu_count()}"
MACHINE ?= "zedboard-zynq7"
PACKAGE_CLASSES ?= "package_deb"
EXTRA_IMAGE_FEATURES = "debug-tweaks eclipse-debug"
IMAGE_INSTALL_append = "libgcc alsa-utils mpg123 libstdc++ sthttpd libmicrohttpd libsndfile1"
LICENSE_FLAGS_WHITELIST = "commercial_mpg123"

我还必须在bblayers.conf中添加一些额外的图层(当然还要下载它们):

meta-xilinx
meta-multimedia (from meta-openembedded)
meta-oe         (from meta-openembedded)
meta-webserver  (from meta-openembedded)

最后,我使用bitbake生成了core-image-minimal。

这与Linux内核以及其他单独编译的东西一起启动并正常工作。

问题

1。这个rootfs

的简单应用程序

它是Zynq的应用程序,因此我使用XSDK,它是来自Xilinx的SDK,基于Eclipse。我创建了新的Application项目。在对话框窗口中,我选择Linux作为平台,C ++作为语言,我提供了解压缩rootfs的路径(就像系统启动的那个,通过NFS)。我的rootfs路径是/home/stas/ZedboardPetalinuxFS(它不是Petalinux,我只是习惯使用它,这个文件夹名称仍然相同)。这为rootfs中的库和标头搜索设置了正确的路径。 我开始时非常简单:

#include <pthread.h>

int main()
{
    int i;
    i = 1;
    return 0;
}

我还为链接器添加了pthread库(在Eclipse设置中)。此时链接命令:

arm-linux-gnueabihf-g++ -L"/home/stas/ZedboardPetalinuxFS/usr/lib" -L"/home/stas/ZedboardPetalinuxFS/lib" -o "test.elf"  ./src/main.o   -lpthread

此时它会编译。但是当我添加sndfile库时它停止了

#include <sndfile.h>

这是合理的,因为这个rootfs没有所有标头。我需要添加另一个搜索标头的路径。所以我在yocto tmp文件夹中添加了路径,该文件夹包含构建rootfs所需的所有头文件。添加后,它再次成功编译。但是当我添加用于链接的sndfile库时,问题就开始了。这是链接命令和错误:

arm-linux-gnueabihf-g++ -L"/home/stas/ZedboardPetalinuxFS/usr/lib" -L"/home/stas/ZedboardPetalinuxFS/lib" -o "test.elf"  ./src/main.o   -lpthread -lsndfile
/opt/Xilinx/SDK/2016.4/gnu/aarch32/lin/gcc-arm-linux-gnueabi/bin/../lib/gcc/arm-linux-gnueabihf/5.2.1/../../../../arm-linux-gnueabihf/bin/ld: cannot find -lsndfile

我找了usr / lib来检查libsndfile.so是否存在,我发现只有libsndfile.so.1和ibsndfile.so.1.27。但是对于pthread也是如此,并且链接器不会抱怨。我决定手动创建libsndfile.so(我将它链接到libsndfile.so.1)。 Linker不再抱怨它,但开始抱怨它的依赖性。因此,我还为所有依赖项及其依赖项创建了.so文件,并添加它们以进行链接。然后它成功了。最后,链接命令看起来像这样:

arm-linux-gnueabihf-g++ -L"/home/stas/ZedboardPetalinuxFS/usr/lib" -L"/home/stas/ZedboardPetalinuxFS/lib" -o "test.elf"  ./src/main.o   -lpthread -lvorbisenc -lvorbis -logg -lFLAC -lsndfile

所以这里是第一个问题 - 为什么我不需要.so文件用于pthread,但需要它用于所有其他库?或者更一般 - 我什么时候需要.so文件,什么时候.so.X文件就足够了?

2。简单的应用程序 - 另一种方法

第一次尝试后,我想我应该制作另一张图片,这次更适合开发。幸运的是,在Yocto中它很容易 - 我只需修改一行:

EXTRA_IMAGE_FEATURES = "debug-tweaks eclipse-debug dev-pkgs"

dev-pkgs选项为所有已安装的软件包添加-dev软件包。 所以现在我有了所有需要头文件的rootfs,以及.so文件指向他们应该的位置 在编译之前,我删除了不必要的Include路径,只留下了来自rootfs的路径,并删除了除pthread和sndfile之外的所有库。但后来我遇到了新的错误:

arm-linux-gnueabihf-g++ -L"/home/stas/ZedboardPetalinuxFS/usr/lib" -L"/home/stas/ZedboardPetalinuxFS/lib" -o "test.elf"  ./src/main.o   -lsndfile -lpthread
/opt/Xilinx/SDK/2016.4/gnu/aarch32/lin/gcc-arm-linux-gnueabi/bin/../lib/gcc/arm-linux-gnueabihf/5.2.1/../../../../arm-linux-gnueabihf/bin/ld: cannot find /lib/libpthread.so.0
makefile:48: polecenia dla obiektu 'test.elf' nie powiodły się (commands for ‘test.elf’ did not succeed)
/opt/Xilinx/SDK/2016.4/gnu/aarch32/lin/gcc-arm-linux-gnueabi/bin/../lib/gcc/arm-linux-gnueabihf/5.2.1/../../../../arm-linux-gnueabihf/bin/ld: cannot find /usr/lib/libpthread_nonshared.a

我发现,它在我的根文件夹中查找库。谷歌快速搜索(和SO :))告诉我,我应该设置--sysroot变量。所以我将它添加到Eclipse选项(在链接器选项中的Miscelenious卡中),如:

--sysroot=/home/stas/ZedboardPetalinuxFS

所以现在链接器命令看起来像这样:

arm-linux-gnueabihf-g++ -L"/home/stas/ZedboardPetalinuxFS/usr/lib" -L"/home/stas/ZedboardPetalinuxFS/lib" --sysroot=/home/stas/ZedboardPetalinuxFS -o "test.elf"  ./src/main.o   -lsndfile -lpthread

一切都成功了!我还编写了使用pthreads和sndfile的简单示例,它也有效。但为什么?这引出了我的第二个问题: 在这种情况下,为什么我需要--sysroot选项?我什么时候需要使用此选项?为什么这次我没有必要将所有依赖项添加到链接命令?

3。另一个想法

此时,我有一个想法,检查会发生什么,如果我添加--sysroot选项,其中rootfs填充了旧的非开发图像。但这给了我新的错误:

arm-linux-gnueabihf-g++ -L"/home/stas/ZedboardPetalinuxFS/usr/lib" -L"/home/stas/ZedboardPetalinuxFS/lib" --sysroot=/home/stas/ZedboardPetalinuxFS -o "test.elf"  ./src/main.o   -lpthread -lvorbisenc -lvorbis -logg -lFLAC -lsndfile
/opt/Xilinx/SDK/2016.4/gnu/aarch32/lin/gcc-arm-linux-gnueabi/bin/../lib/gcc/arm-linux-gnueabihf/5.2.1/../../../../arm-linux-gnueabihf/bin/ld: cannot find crt1.o: No such file or directory
makefile:48: polecenia dla obiektu 'test.elf' nie powiodły się
/opt/Xilinx/SDK/2016.4/gnu/aarch32/lin/gcc-arm-linux-gnueabi/bin/../lib/gcc/arm-linux-gnueabihf/5.2.1/../../../../arm-linux-gnueabihf/bin/ld: cannot find crti.o: No such file or directory
/opt/Xilinx/SDK/2016.4/gnu/aarch32/lin/gcc-arm-linux-gnueabi/bin/../lib/gcc/arm-linux-gnueabihf/5.2.1/../../../../arm-linux-gnueabihf/bin/ld: cannot find -lpthread
/opt/Xilinx/SDK/2016.4/gnu/aarch32/lin/gcc-arm-linux-gnueabi/bin/../lib/gcc/arm-linux-gnueabihf/5.2.1/../../../../arm-linux-gnueabihf/bin/ld: cannot find -lm

所以第三个问题 - 这个错误是什么意思?

非常感谢!

3 个答案:

答案 0 :(得分:2)

  1.   

    &#34;为什么我不需要.so文件用于pthread,但需要它为所有人   其他图书馆?&#34;

    实际上你确实需要pthread.so文件。您包含了pthread.h但未与-lpthread链接。所以你没有看到任何链接器错误,这是正常的。

      

    &#34;我什么时候需要.so文件,当.so.X文件足够时#34;

    当您提供&#34; -lNAME&#34;参数到g ++,编译器告诉链接器在库搜索路径中找到libNAME.so。由于可能存在同一个库的多个版本(libNAME.so.1,libNAME.so.1.20),* .so文件链接到所需的实际库文件。 (Versioning of shared objectsld man pages

  2.   

    &#34;在这种情况下,为什么我需要--sysroot选项?我什么时候需要使用此选项?为什么这次我没有必要将所有依赖项添加到链接命令?&#34;

    &#34; dev-pkgs&#34;在EXTRA_IMAGE_FEATURES中隐式更改您的sysroot以允许您链接开发包(yoctoproject image-features)。这就是你需要-sysroot选项的原因。在交叉编译时,通常需要此选项,以便为标头和库的标准搜索路径提供根。你不需要它,因为你没有改变你的sysroot的dev-pkgs图像功能

  3.   

    &#34;所以第三个问题 - 这个错误意味着什么?&#34;

    即使您最基本的hello world代码也会与标准c库链接(如果您没有另行指定)。 libm.so,libpthread.so和crt1.o文件是libc库的一部分,并附带libc dev包。因此,当链接器从旧的sysroot查看时,链接器无法看到标准库目录

答案 1 :(得分:1)

  

为什么我不需要.so文件用于pthread,但需要它用于所有其他库?

交叉编译器通常会附带一个C运行时(包括pthread),通常位于交叉编译器安装的一部分目录中。

链接器内置了库的搜索路径。这些是关于sysroot的,默认设置为搜索交叉编译器自己包含的目标C运行时。如果您添加了任何-L选项,它将首先搜索这些选项,然后转到这些预定义的目录。

当您与pthread相关联时,它会在交叉编译器的库目录中找到至少libpthread.a

  

或者更一般 - 我什么时候需要.so文件,什么时候.so.X文件就足够了?

Linux中的共享库通常具有主要版本号和次要版本号。库在具有相同主要版本的不同次要版本之间是ABI兼容的,但在主要版本之间不兼容。有时候有三个级别的版本,但主体是相似的。

安装库时,通常使用全名安装实际文件,例如。 libmy.so.1.2,然后为libmy.so.1libmy.so提供符号链接。

如果您正在链接应用程序可以使用任何库版本,那么您只需指定名称,例如。 -lmy。在这种情况下,您需要从libmy.solibmy.so.1的符号链接。

如果您需要特定版本,请添加-l:libmy.so.1。 &#39;:&#39;表示文字文件名。

链接器脚本可能会影响事物,即使您指定了短名称,也可能导致选择特定版本。

  

在这种情况下,为什么我需要--sysroot选项?我什么时候需要使用   这个选项一般吗?

--sysroot所做的是将给定路径添加到通常用于搜索包含和库的所有搜索目录中。在交叉编译时(正如您现在所做的那样)最有用的是让编译器和链接器在目标根目录内搜索而不是构建主机自己的根目录。

如果您指定了一个sysroot,则可能不需要通过-I指定包含路径或通过-L指定链接器路径,假设这些文件位于目标根目录内的正常位置。

  

为什么这次我没有必要将所有依赖项添加到链接命令?

一种可能的情况是第一次sndfile静态而不是动态链接。如果您的第一个根映像在lib目录中只有sndfile.a,或者在搜索路径上的其他位置,则会发生这种情况。为了满足sndfile.a的要求,您还需要链接其他库。

当链接到sndfile.so时,依赖关系会自动通过动态链接过程加载。

目前这只是一种工作理论。

  

第三个问题 - 这个错误是什么意思?

它们意味着它甚至找不到要连接的C运行时库。

如第一个问题所述,之前在预定义的搜索路径(相对于预定义的sysroot)中找到了C运行时,它定位了交叉编译器提供的C运行时。

您通过提供自己的sysroot来扰乱这一点。它现在只搜索目标根。由于此目标根文件系统没有安装开发库,因此无法找到C运行库。

答案 2 :(得分:0)

你做错了几件事:

  1. 看起来您没有使用环境变量,而是直接调用交叉编译器。因此,您应该arm-linux-gnueabihf-g++ ...而不是使用$CXX ...进行编译。 CXX是yocto脚本设置的环境变量,用于设置交叉编译的环境。使用CXX,您无需手动传递--sysroot
  2. 您不应该使用-lpthread直接链接到pthread库。您应该使用-pthread