嵌入式系统的c++编译问题

Issues in c++ compilation for embedded system

我有几个关于嵌入式系统编译问题的相关问题。我的问题不仅是关于如何做某事,更多的是关于为什么,因为我有解决问题的方法(但也许有更好的?),但不知道为什么有些东西在某些条件下有效,而在某些条件下无效其他。我已经花了一些时间在这上面,但直到昨天我还是有点盲目地做事,反复试验,不知道自己在做什么。是时候停止了!请帮忙。

场景

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

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。我创建了新的应用程序项目。在对话框 window 中,我选择 Linux 作为平台,C++ 作为语言,我提供了我解压的 rootfs 的路径(正是系统通过 NFS 启动的那个)。我的rootfs路径是/home/stas/ZedboardPetalinuxFS(不是Petalinux,我只是用来用的,这个文件夹名还是一样)。这为 rootfs 中的库和 headers 搜索设置了正确的路径。 我从非常简单的事情开始:

#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 library

时它停止了
#include <sndfile.h>

这是合理的,因为这个rootfs没有全部headers。我需要添加另一个路径来搜索 headers。所以我在 yocto tmp 文件夹中添加了路径,其中包含构建 rootfs 所需的所有 headers。我添加后,它再次编译成功。但是当我添加用于链接的 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)。链接器不再抱怨它,而是开始抱怨它的依赖项。所以我还为所有依赖项及其依赖项创建了 .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

第一个问题来了 – 为什么我不需要 pthread 的 .so 文件,但所有其他库都需要它?或者更一般的——我什么时候需要 .so 文件,什么时候 .so.X 文件就足够了?

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

第一次尝试后,我想我应该再做一个图像,这次更适合开发。幸运的是,在 Yocto 中这很容易——我只需要修改一行:

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

dev-pkgs 选项为所有已安装的包添加 -dev 包。 所以现在我有了 rootfs,其中包含所有需要的 headers,并且 .so 文件指向了它们应该指向的位置。
在编译之前,我删除了不必要的Include path,只留下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

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

--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

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

非常感谢!

  1. "why I did not needed .so file for pthread, but needed it for all other libraries?"

    实际上您确实需要 pthread.so 文件。您包括 pthread.h 但没有 link 与 -lpthread。所以您没有看到任何 linker 错误是正常的。

    "when do I need .so file, and when .so.X file is enough"

    当您为 g++ 提供“-lNAME”参数时,编译器会告诉 linker 在库搜索路径中查找 libNAME.so。由于可能存在同一个库的多个版本(libNAME.so.1,libNAME.so.1.20),*.so 文件link 到所需的实际库文件。 (Versioning of shared objects, ld man pages)

  2. "Why do I need --sysroot option in this case? When do I need to use this option in general? And why this time I didn't have to add all the dependencies to linking command?"

    EXTRA_IMAGE_FEATURES 中的 "dev-pkgs" 隐式更改您的 sysroot 以让您 link 针对开发包 (yoctoproject image-features)。这就是您需要 -sysroot 选项的原因。交叉编译时通常需要此选项,以便为 headers 和库提供标准搜索路径的根。您不需要它,因为您没有 dev-pkgs 图像功能来更改您的 sysroot

  3. "So third question – what does this errors mean?"

    即使是最基本的 hello world 代码也可以 link使用标准 c 库编辑(如果您没有另外指定)。 libm.so、libpthread.so 和 crt1.o 文件是 libc 库的一部分,并随 libc 开发包一起提供。所以 linker 从你的旧 sysroot

  4. 看不到标准库目录

why I did not needed .so file for pthread, but needed it for all other libraries?

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

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

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

Or more general – when do I need .so file, and when .so.X file is enough?

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

安装库时,通常使用全名安装实际文件,例如。 libmy.so.1.2,然后提供symlinks给libmy.so.1libmy.so.

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

如果您需要特定版本,您可以输入 -l:libmy.so.1。 ':' 表示文字文件名。

链接器脚本可能会影响某些事情,并且可能会导致选择特定版本,即使您确实指定了短名称也是如此。

Why do I need --sysroot option in this case? When do I need to use this option in general?

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

如果您指定了 sysroot,您可能不需要通过 -I 指定包含路径或通过 -L 指定 linker 路径,假设文件在它们的正常位置内在你的目标根目录中。

And why this time I didn't have to add all the dependencies to linking command?

一种可能的情况是,第一次 sndfile 用于静态而不是动态 linked。如果您的第一个根映像在 lib 目录或搜索路径的其他地方只有 sndfile.a,就会发生这种情况。为了满足 sndfile.a 的要求,您还需要 link 其他库。

当 linking 反对 sndfile.so 时,依赖项将通过动态 linking 过程自动加载。

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

So third question – what does this errors mean?

他们的意思是它甚至找不到 link 的 C 运行时库。

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

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

你做错了几件事:

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