4

我正在使用交叉预链接来预链接将 Qt 用于嵌入式 ARM 设备的大型 C++ 可执行文件。请注意,我使用的不是Yocto,而是自定义发行版 - 所以我目前正在手动运行预链接。

查看 prelink 的输出,它似乎有效:

$ prelink --verbose --ld-library-path=/opt/<product>/lib:/usr/local/Qt-5.3.1/lib --root=$PRODUCT_TARGET_ROOT/<product>/rfs/ /path/to/binary
Laying out 56 libraries in virtual address space 41000000-50000000
Assigned virtual address space slots for libraries:
/lib/ld-linux.so.3                                           41000000-41027908
/opt/<product>/lib/lib<product>common.so.1                   41030000-41cf0fd0
/lib/libc.so.6                                               442b0000-443e3980
/usr/local/Qt-5.3.1/lib/libQt5Qml.so.5                       434f0000-4380ee84
[..]
Prelinking /lib/ld-2.17.so
Prelinking /lib/libc-2.17.so
Prelinking /path/to/binary
Prelinking /<product>/lib/lib<product>common.so.1.0.0
Prelinking /usr/local/Qt-5.3.1/lib/libQt5Qml.so.5.3.1 
[..]

当库被加载时,至少 libQt5Qml.so 和 libproductcommon.so 似乎被加载到预链接设置的首选加载地址:

$ cat /proc/`pidof binary`/maps
2ab49000-2ab4a000 r--p 0001e000 07:00 9357       /roroot/lib/ld-2.17.so
2ab4a000-2ab4b000 rw-p 0001f000 07:00 9357       /roroot/lib/ld-2.17.so
2b0fd000-2b223000 r-xp 00000000 07:00 9730       /roroot/lib/libc-2.17.so
2b223000-2b22a000 ---p 00126000 07:00 9730       /roroot/lib/libc-2.17.so
2b22a000-2b22c000 r--p 00125000 07:00 9730       /roroot/lib/libc-2.17.so
2b22c000-2b22d000 rw-p 00127000 07:00 9730       /roroot/lib/libc-2.17.so
41030000-41ce7000 r-xp 00000000 07:00 9305       /roroot/<product>/lib/lib<product>common.so.1.0.0
41ce7000-41cef000 ---p 00cb7000 07:00 9305       /roroot/<product>/lib/lib<product>common.so.1.0.0
41cef000-41cf1000 rw-p 00cb7000 07:00 9305       /roroot/<product>/lib/lib<product>common.so.1.0.0
434f0000-437f8000 r-xp 00000000 07:00 1355       /roroot/usr/local/Qt-5.3.1/lib/libQt5Qml.so.5.3.1
437f8000-437ff000 ---p 00308000 07:00 1355       /roroot/usr/local/Qt-5.3.1/lib/libQt5Qml.so.5.3.1
437ff000-4380e000 rw-p 00307000 07:00 1355       /roroot/usr/local/Qt-5.3.1/lib/libQt5Qml.so.5.3.1
[..]

现在,我预计搬迁数量会有所减少:

$ LD_DEBUG=statistics /path/to/binary
    20453:                      number of relocations: 66379
    20453:           number of relocations from cache: 38995
    20453:             number of relative relocations: 21690

$ LD_USE_LOAD_BIAS=0 LD_DEBUG=statistics /path/to/binary
    20478:                      number of relocations: 66379
    20478:           number of relocations from cache: 38995
    20478:             number of relative relocations: 62981

这表明由于预链接,只有相对 重定位减少了,而正常重定位(可能需要符号查找)没有减少。我对减少其他搬迁特别感兴趣,因为那些可能是更昂贵的搬迁。

现在我的问题:

  1. 预链接甚至能够减少正常的重定位吗?一篇LWN 文章在预链接后显示了 0 次正常重定位,所以我认为这是可能的。
  2. 我可能做错了什么,因此非相对重定位不会为我预先链接?我应该从哪里开始调试?
4

2 回答 2

3

好的,事实证明问题是某些库未正确预链接,如我原来的问题所示,其中例如 libc.so 未在正确的加载地址加载。

似乎预链接是一种全有或全无的方法:如果可执行文件的依赖项之一未正确预链接或无法在首选地址加载,则可执行文件和库都无法利用预链接符号重定位,并且仅利用预链接的相对重定位。

除上述内容外,还应检查库是否正确预链接:

# readelf --dynamic usr/lib/someLibrary.so 
[..]
0x6ffffdf5 (GNU_PRELINKED)              2014-12-15T14:16:56
[..]

# readelf --program-headers usr/lib/someLibrary.so
Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  [..]
  LOAD           0x000000 0x44bf0000 0x44bf0000 0xb56d4 0xb56d4 R E 0x8000
  [..]

输出的地址prelink --verbosereadelf --program-headers需要cat /proc/PID/maps匹配。

我的错误是我没有检查readelf- 如果我这样做了,我会意识到目标设备上的某些库没有预链接,因为构建系统中的错误导致预链接的版本被非预链接的版本覆盖...

在修复了我的构建系统问题后,正常的重定位确实降到了 0:

# LD_DEBUG=statistics /path/to/binary
  5089:                      number of relocations: 0
  5089:           number of relocations from cache: 19477
  5089:             number of relative relocations: 0
于 2014-12-15T15:33:30.643 回答
2

似乎某些链接库没有预链接,预链接信息已过时或分配的地址存在冲突。

要么,要么你可能运气不好,遭受类似http://lwn.net/Articles/341313/的事情:

在 i686 上大约 10% 到 50% 的时间,预链接的这种好处被 [vdso] 的随机放置所破坏,也称为 linux-gate.so。如果内核为 [vdso] 选择的页面与任何预先链接的所需共享库重叠,则 ld-linux 无法避免处理该库的重定位。通常成本会滚雪球,因为没有获得预链接页面的库会被移动,从而干扰后续的库。[在 x86_64 上,vdso 位于不能冲突的特殊固定地址。]

从https://bugzilla.redhat.com/show_bug.cgi?id=162797试试这个例子

    for i in 0 1 2 3 4 5 6 7 8 9; do
      for j in 0 1 2 3 4 5 6 7 8 9; do
        for k in 0 1 2 3 4 5 6 7 8 9; do
          ldd /bin/cat
        done
      done 
    done  |  grep libc  |  sort  |  uniq -c

对于当前 i686 上的 Fedora 11,我看到大约 10% 的时间存在冲突,仅涉及 ld-linux、libc 和 [vdso]。这意味着无论如何 glibc 必须在大约 10% 的时间里动态重定位,即使 glibc 已经预先链接,即使 /bin/cat 几乎没有使用共享库。当 GNOME 应用程序使用 50 个或更多预链接的共享库时,如该主题的另一个线程中所述,则更有可能发生运行时冲突和费用。

请注意,arm 没有 [vdso],但有一个代码页,其中包含此处记录的实用功能https://www.kernel.org/doc/Documentation/arm/kernel_user_helpers.txt它们位于 [vectors] ffff0000- 内ffff1000。

我会尝试直接在平台上运行预链接阶段,虽然它看起来是一个带有只读 fs 的嵌入式阶段,但我认为值得一试。

请注意,这些重定位只会影响程序的启动时间。

于 2014-12-03T13:01:14.983 回答