【问题标题】:Issues in c++ compilation for embedded system嵌入式系统的c++编译问题
【发布时间】:2017-04-19 09:07:00
【问题描述】:

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

场景

我想在 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。我创建了新的应用程序项目。在对话窗口中,我选择 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

我发现它在我的根文件夹中查找库。在 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

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

提前非常感谢!

【问题讨论】:

    标签: c++ linux linker yocto zynq


    【解决方案1】:
    1. "为什么我不需要 pthread 的 .so 文件,但所有人都需要它 其他图书馆?”

      其实你确实需要 pthread.so 文件。您包括了 pthread.h,但没有与 -lpthread 链接。因此,您看不到任何链接器错误是正常的。

      “什么时候需要.so文件,什么时候.so.X文件就够了”

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

    2. "为什么在这种情况下我需要 --sysroot 选项?一般我什么时候需要使用这个选项?为什么这次我不必将所有依赖项添加到链接命令中?"

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

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

      即使是最基本的 hello world 代码也会与标准 c 库链接(如果您没有另外指定)。 libm.so、libpthread.so 和 crt1.o 文件是 libc 库的一部分,并与 libc 开发包一起提供。因此,当链接器从旧的 sysroot 中查找时,它看不到标准库目录

    【讨论】:

    • 感谢您的回答!但我仍然不确定一些事情。 1.在链接器的命令中有-lpthread,所以它被链接了。它仍然正确编译。在其他一些项目中,我使用了这个非开发 rootfs,并且我在代码中有线程,它也可以工作。你能解释一下为什么吗? 2. dev-tool 如何改变我的 sysroot?它写在rootfs的某个地方吗?与普通的 rootfs 有何不同,即 sysroot 已更改?当我明确设置额外的库和头文件路径时,为什么我需要设置 --sysroot?
    • 3.好的,我知道,我总是需要 libc,但它甚至存在于非开发的 rootfs 中(是吗?我明天会仔细检查)。那么为什么当我设置 --sysroot 选项时它不起作用?
    【解决方案2】:

    为什么我不需要 .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。 ':' 表示文字文件名。

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

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

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

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

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

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

    当链接sndfile.so 时,依赖项将通过动态链接过程自动加载。

    目前这只是一个可行的理论。

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

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

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

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

    【讨论】:

    • 非常感谢您的回答!有了这方面的知识,我会在以后研究它,并尝试在你不得不猜测的地方找到具体的答案。
    【解决方案3】:

    你做错了几件事:

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

    【讨论】:

      猜你喜欢
      • 2015-06-20
      • 2015-12-24
      • 2012-09-12
      • 1970-01-01
      • 1970-01-01
      • 2010-10-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多