一位朋友最近遇到一个棘手的问题,希望把共享库(SO)加载到指定的内存地址,目的可能是想通过prelink来加快应用程序的起动速度。他问我有没有什么方法。我知道Windows下是可以的,比如在VC6里设置/base的值就行了,所以相信在linux下也是可行的。
VC有编译选项可以设置,猜想gcc也应该有吧。gcc本身只是一个外壳,链接工作是由于ld完成的,当然是应该去阅读ld命令行选项文档。很快发现ld有个—image-base选项,可以设置动态库的加载地址。
通过Xlinker把这个参数传递给ld,但是ld不能识别这个选项:
gcc -g -shared test.c -Xlinker --image-base -Xlinker 0x00c00000 -o libtest.so
/usr/bin/ld: unrecognized option '--image-base'
/usr/bin/ld: use the --help option for usage information
collect2: ld returned 1 exit statu
s
再仔细看手册,原来这个选项只适用于PE文件,PE文件是Windows下专用的,在linux下自然用不了,看来得另想办法。
我知道ld script可以控制ld的行为,于是研究ld script的写法,按照手册里的说明,写了一个简单的ld script:
SECTIONS
{
. = 0x00c00000;
.text : { *(.text) }
.data : { *(.data) }
.bss : { *(.bss) }
}
|
按下列方式编译:
gcc -shared -g -Xlinker --script -Xlinker ld.s test.c -o libtest.so
gcc -g main.c -L./ -ltest -o test.exe
用ldd查看,加载地址正确。
[root@localhost lds]# ldd test.exe
linux-gate.so.1 =>(0x00aff000)
libtest.so => ./libtest.so (0x00c00000)
libc.so.6 => /lib/libc.so.6 (0x007fa000)
/lib/ld-linux.so.2 (0x007dd000)
但运行时会crash:
[root@localhost lds]# ./test.exe
Segmentation fault
调试运行可以发现:
(gdb) r
Starting program: /work/test/lds/test.exe
Reading symbols from shared object read from target memory...done.
Loaded system supplied DSO at 0x452000
Program received signal SIGSEGV, Segmentation fault.
0x007e7a10 in _dl_relocate_object () from /lib/ld-linux.so.2
(gdb) bt
#00x007e7a10 in _dl_relocate_object () from /lib/ld-linux.so.2
#10x007e0833 in dl_main () from /lib/ld-linux.so.2
#20x007f056b in _dl_sysdep_start () from /lib/ld-linux.so.2
#30x007df48f in _dl_start () from /lib/ld-linux.so.2
#40x007dd847 in _start () from /lib/ld-linux.so.2
猜想可能是ld.s写得不全,导致一些信息不正确。于是用ld –verbose导出一个默认的ld script。不出所料,默认的ld script非常冗长,下面是开头一段:
/* Script for -z combreloc: combine and sort reloc sections */
OUTPUT_FORMAT("elf32-i386", "elf32-i386",
"elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(_start)
SEARCH_DIR("/usr/i386-redhat-linux/lib"); SEARCH_DIR("/usr/local/lib"); SEARCH_DIR("/lib"); SEARCH_DIR("/usr/lib");
SECTIONS
{
/* Read-only sections, merged into text segment: */
PROVIDE (__executable_start = 0x08048000); . = 0x08048000 + SIZEOF_HEADERS;
.interp : { *(.interp) }
.hash : { *(.hash) }
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.gnu.version : { *(.gnu.version) }
.gnu.version_d: { *(.gnu.version_d) }
.gnu.version_r: { *(.gnu.version_r) }
|
按照ld script的语法,我把它修改为(红色部分为新增行):
/* Script for -z combreloc: combine and sort reloc sections */
OUTPUT_FORMAT("elf32-i386", "elf32-i386",
"elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(_start)
SEARCH_DIR("/usr/i386-redhat-linux/lib"); SEARCH_DIR("/usr/local/lib"); SEARCH_DIR("/lib"); SEARCH_DIR("/usr/lib");
SECTIONS
{
/* Read-only sections, merged into text segment: */
PROVIDE (__executable_start = 0x08048000); . = 0x08048000 + SIZEOF_HEADERS;
. = 0x00c00000;
.interp : { *(.interp) }
.hash : { *(.hash) }
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.gnu.version : { *(.gnu.version) }
.gnu.version_d: { *(.gnu.version_d) }
.gnu.version_r: { *(.gnu.version_r) }
|
用这个ld script再次测试,一切正常。又验证多个共享库的情况,也正常,下面是测试数据:
test.c
int test(intn)
{
returnn;
}
|
test1.c
inttest1(intn)
{
returnn;
}
|
main.c
externinttest(intn);
externinttest1(intn);
#include <stdio.h>
intmain(intargc, char* argv[])
{
printf("Hello: %d %d/n", test(100), test1(200));
getchar();
return 0;
}
|
Makefile
all:
gcc -shared -g -Xlinker --script -Xlinker ld.s test.c -o libtest.so
gcc -shared -g -Xlinker --script -Xlinker ld1.s test1.c -o libtest1.so
gcc -g main.c -L./ -ltest -ltest1 -o test.exe
clean:
rm -f *.so *.exe
|
libtest.so的加载地址为:0x00c00000
libtest1.so的加载地址为:0x00d00000
ldd显示结果:
linux-gate.so.1 =>(0x00aa3000)
libtest.so => ./libtest.so (0x00c00000)
libtest1.so => ./libtest1.so (0x00d00000)
libc.so.6 => /lib/libc.so.6 (0x007fa000)
/lib/ld-linux.so.2 (0x007dd000)
|
maps的内容为:
007dd000-007f6000 r-xp 00000000 03:01 521466 /lib/ld-2.4.so
007f6000-007f7000 r-xp 00018000 03:01 521466 /lib/ld-2.4.so
007f7000-007f8000 rwxp 00019000 03:01 521466 /lib/ld-2.4.so
007fa000-00926000 r-xp 00000000 03:01 523579 /lib/libc-2.4.so
00926000-00929000 r-xp 0012b000 03:01 523579 /lib/libc-2.4.so
00929000-0092a000 rwxp 0012e000 03:01 523579 /lib/libc-2.4.so
0092a000-0092d000 rwxp 0092a000 00:00 0
00c00000-00c01000 r-xp 00001000 03:03 16370 /work/test/ldsex/libtest.so
00c01000-00c02000 rwxp 00001000 03:03 16370 /work/test/ldsex/libtest.so
00cf1000-00cf2000 r-xp 00cf1000 00:00 0 [vdso]
00d00000-00d01000 r-xp 00001000 03:03 16373 /work/test/ldsex/libtest1.so
00d01000-00d02000 rwxp 00001000 03:03 16373 /work/test/ldsex/libtest1.so
08048000-08049000 r-xp 00000000 03:03 16374 /work/test/ldsex/test.exe
08049000-0804a000 rw-p 00000000 03:03 16374 /work/test/ldsex/test.exe
b7fdf000-b7fe0000 rw-p b7fdf000 00:00 0
b7fed000-b7ff0000 rw-p b7fed000 00:00 0
bf8db000-bf8f0000 rw-p bf8db000 00:00 0 [stack]
|
从以上测试结果可以看出,这种方法是可行的。
分享到:
相关推荐
加载 so 库到一个已经运行的进程中。 调用该 so 库中的自定义函数。 向该函数传递序列化的参数。 它包含三部分: hotpatch.h 头文件,libhotpatch.so 库和命令行辅助程序 hotpatcher。 目前的局限有: ...
具体说来,动态链接器ld.so按照下面的顺序来搜索需要的动态共享库: 1.ELF可执行文件中动态段中DT_RPATH所指定的路径。这实际上是通过一种不算很常用,却比较实用的方法所设置的:编译目标代码时,可以对gcc...
libsigar-amd64-linux.so,适用于linux 64位操作系统,用于sigar获取系统信息
使用此共享库的程序在运行时,共享库被动态加载到内存中并和主程序在内存中进行链接。多个可执行程序可以共享库文件的代码段(不共享数据段)。 共享库的成员对象可以被执行(由libdl.so提供支持)。 2)如何...
在Windows操作系统下,本机库文件的扩展名通常是`.dll`(动态链接库),而在Linux操作系统下,本机库文件的扩展名通常是`.so`(共享对象)。这是由于不同操作系统使用不同的二进制格式和加载机制。 原因如下: 1. ...
Linux下动态库文件的文件名形如 libxxx.so,其中so是 Shared Object 的缩写,即可以共享的目标文件。 在链接动态库生成可执行文件时,并不会把动态库的代码复制到执行文件中,而是在执行文件中记录对动态库的引用...
环境是centos7.6,将整个解决过程复制黏贴了进去。
Linux下动态库文件的文件名形如 libxxx.so,其中so是 Shared Object 的缩写,即可以共享的目标文件。 在链接动态库生成可执行文件时,并不会把动态库的代码复制到执行文件中,而是在执行文件中记录对动态库的引用。 ...
使用此共享库的程序在运行时,共享库被动态加载到内存中并和主程序在内存中进行链接。多个可执行程序可以共享库文件的代码段(不共享数据段)。 共享库的成员对象可以被执行(由libdl.so提供支持)。 2)如何...
用于将共享库注入Linux或Windows进程的库 Linux 注意:请勿在生产环境中使用此库。 这可能会永远停止进程。 请参阅。 我受到启发,基本思想由此而来。 但是,在libc.so.6调用__libc_dlopen_mode的方式完全不同。 ...
中,那么操作系统将暂时停止您的进程,将其他内存转存到硬盘中,从硬盘上加载被请求的内存,然后再重新启动您的进程。这样,每个进程都获得了自己可以使用的地址空间,可以访问比您物理上安装的内存更多的内存。 ...
则需要将该库文件复制到/usr/local/lib文件夹中,并修改/etc/ld.so.conf文件,在该文件的末尾增加/usr/local/lib,为链接程序ld指定共享库文件的搜索文件夹,然后执行ldconfig命令,以使新的ld配置加载生效。...
Windows具有rundll,rundotso用于linux加载共享库(.so)并运行功能。 您可以运行rundotso.c来加载共享库并运行该函数。 它喜欢Windows操作系统中的rundll.exe。 在android ndk下编译和测试。 您可以使用任何arm...
预加载的内存泄漏检测库该库旨在通过预加载来检测任何应用程序中的内存泄漏。用法示例LD_PRELOAD='/usr/local/lib/libmemleak.so' ./a.out 可能还需要预加载libbfd和libdl,因此请使用LD_PRELOAD='/usr/local/lib/...
通用装载机该加载器提供了一个统一的Go接口,用于从Windows,OSX和Linux上的内存加载共享库。 还包括一个跨平台的Call()实现,使您可以轻松地从那些库中调用导出的符号。基本用法libraryPath设置为lib.so为Linux, ...
并且,如果地址在硬盘上而不是在 RAM 中,那么操作系统将暂时停止您的进程,将其他内存转存到硬盘中,从硬盘上加载被请求的内存,然后再重新启动您的进程。这样,每个进程都获得了自己可以使用的地址空间,可以访问...
智能回复Google的智能回复运行时(支持gmail和助手)模型的非官方端口,将其移植到python,从而使开发人员能够将智能智能回复作为支持Linux,加载器(ld.so),... 下面列出了该库的依赖项:linux-vdso.so.1 (0x00007ff
使用 dlopen 在运行时加载共享库 i在 dlopen 然后睡眠后设置为 1。 dlclose 现在成功运行,没有分段错误。 $ make clean > /dev/null 2>&1 && make test_runtime cc -o runtime_load runtime_load.c -ldl go build...
在使用mount这个指令时,至少要先知道下列三种信息:要加载对象的文件系统类型、要加载对象的设备名称及要将设备加载到哪个目录下。 (1)Linux可以识别的文件系统 ◆ Windows 95/98常用的FAT 32文件系统:vfat ;...
sharedlibpp 是一个小型的跨平台库,用于为不同平台(Linux/Mac/Windows)创建和加载共享库。 sharedlibpp 提供了一种简单且可移植的方法来创建插件,这些插件将您的 c++ 类封装在共享库(so、dylib、dll)中。 原始...