预链接只对相对重定位有效。

5
我正在使用cross-prelink为嵌入式ARM设备预链接一个使用Qt的大型C++可执行文件。请注意,我没有使用Yocto,而是使用自定义发行版 - 所以我目前手动运行prelink。
查看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,并将其加载到由prelink设置的首选加载地址:
$ 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

这表明只有相对重定位由于prelink而减少,但是正常的重定位(可能需要符号查找)没有减少。我特别希望减少其他重定位,因为那些可能更昂贵。
现在我的问题是:
  1. prelink能否减少正常的重定位? LWN article显示prelink后没有正常的重定位,所以我认为是可能的。
  2. 我做错了什么,以至于非相对重定位没有被预链接?我应该从哪里开始调试?
2个回答

4

好的,问题在于一些库没有正确预链接,正如我的原始问题所示,例如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-headerscat /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

3

看起来有些链接库没有预链接,预链接信息已过时或分配的地址冲突。

要么如此,要么你可能运气不佳,遭受类似于这个http://lwn.net/Articles/341313/的问题:

About 10% to 50% of the time on i686, this benefit of prelink is trashed by the randomization of the placement of [vdso], also known as linux-gate.so. If the page that the kernel chooses for [vdso] overlaps any pre-linked needed shared library, then ld-linux cannot avoid processing the relocations for that library. Often the cost snowballs as libraries that do not get their pre-linked pages are moved so that they interfere with subsequent libraries. [On x86_64 the vdso is at a special fixed address that cannot conflict.]

Try this example from 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

For current Fedora 11 on i686, I see a conflict about 10% of the time, involving only ld-linux, libc, and [vdso]. This means that glibc must be dynamically relocated about 10% of the time anyway, even though glibc has been pre-linked, and even though /bin/cat is near minimal in its use of shared libraries. When a GNOME app uses 50 or more pre-linked shared libs, as claimed in another thread on this subject, then runtime conflict and expense are even more likely.

请注意,ARM架构没有[vdso],但有一个代码页面,其中包含了一些实用函数。这些函数的文档可以在此处找到:https://www.kernel.org/doc/Documentation/arm/kernel_user_helpers.txt它们位于[vectors] ffff0000-ffff1000内。请注意,这些重定位只影响程序的启动时间。
我建议直接在平台上运行prelink阶段,尽管它似乎是一个带有只读文件系统的嵌入式系统,但我认为值得一试。

谢谢你的建议,结果证明不是vdso的问题。尽管你说得很对,"似乎有一些链接库没有预链接[...]",这让我更加深入地调查了一下。 - Thomas McGuire

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接