我使用cross-prelink预先链接将Qt用于嵌入式ARM设备的大型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
这表明只有相对 重定位由于预链接而减少,而不是正常的重定位(可能需要符号查找)。我特别感兴趣的是减少其他重新安置,因为那些可能是更昂贵的重新定位。
现在我的问题:
答案 0 :(得分: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内。
我会尝试直接在平台上运行prelink阶段,虽然看起来它是一个只读fs的嵌入式阶段,但我认为值得一试。
请注意,这些重定位只会影响程序的启动时间。
答案 1 :(得分:2)
好的,原来问题是某些库没有正确预链接,正如我原来的问题所示, 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 --verbose
,readelf --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