把共享库(SO)加载到指定的内存地址
2,186 views| 2008-11-18| 李先静| Linux Mobile| | 11 条评论转载时请注明出处和作者联系方式
文章出处:http://www.limodev.cn/blog
作者联系方式:李先静 <xianjimli@gmail.com>
一位朋友最近遇到一个棘手的问题,希望把共享库(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 status
再仔细看手册,原来这个选项只适用于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 #0 0x007e7a10 in _dl_relocate_object () from /lib/ld-linux.so.2 #1 0x007e0833 in dl_main () from /lib/ld-linux.so.2 #2 0x007f056b in _dl_sysdep_start () from /lib/ld-linux.so.2 #3 0x007df48f in _dl_start () from /lib/ld-linux.so.2 #4 0x007dd847 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(int n)
{
return n;
}
test1.c
int test1(int n)
{
return n;
}
main.c
extern int test(int n); extern int test1(int n); #includeint main(int argc, 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]
从以上测试结果可以看出,这种方法是可行的。
Share
Comments
Tags
Recent Posts
Most Viewed
- 系统程序员成长计划写作提纲 - 19,604 views
- Android IPC机制详解 - 6,277 views
- 系统程序员成长计划-走近专业程序员(上) - 6,253 views
- 系统程序员成长计划-写得又快又好的秘诀(一) - 5,390 views
- 系统程序员成长计划-背景知识 - 5,070 views
- i++循环与i–循环的执行效率 - 4,712 views
- 系统程序员成长计划-Write once, run anywhere(WORA)(上) - 4,700 views
- 系统程序员成长计划-走近专业程序员(下) - 4,254 views
- Linux下的调试工具 - 4,017 views
- Advanced Linux Sound Architecture (ALSA) 研究笔记 - 4,017 views
- 系统程序员成长计划-序 - 3,985 views
- 系统程序员成长计划-写得又快又好的秘诀(三) - 3,929 views
- 中国人与自由软件文化研究(搞笑版) - 3,735 views
- Android中的MessageQueue,Handler,Looper和Thread - 3,686 views
- 答复:我不会OOO,仍然可以XXX - 3,658 views
Categories
- Android (28)
- Broncho-A1-Hack (6)
- DirectFB (7)
- FTK(嵌入式GUI) (24)
- GTK+ (29)
- KVM hack notes (8)
- Linux Mobile (65)
- Management (5)
- Mozilla (9)
- Open Source (5)
- Programming (34)
- Tools (9)
- Uncategorized (23)
- Win32 (3)
- X Windows (31)
- 沉思录 (29)
- 系统程序员成长计划 (67)
Blogroll
gallery
Linux guru
推荐网站
Recent Comments
- Dig on 嵌入式GUI FTK设计与实现-事件源(FtkSource)
- 用心生活每一天 » GNU gprof: linux profiling tools 使用 on gcc profiling的工作原理
- JavaScript for: i++ vs i–-传播、沟通、分享-一直“有你” on i++循环与i–循环的执行效率
- Frankly Law on 嵌入式GUI FTK介绍(11)-交叉编译
- tracing on Linux下的调试工具
- ndljsn on FTK移植指南(初稿)
- tracing on 爬塘朗山
- tracing on GTK+(基于DirectFB)的字体处理
- Kely on 系统程序员成长计划写作提纲
- tracing on 爬塘朗山



November 18th, 2008
Dig
November 19th, 2008
文中没有红色部分,不支持色彩标注?
Dig
November 19th, 2008
如果给定的test1.so 的位置是 和 test.so 冲突,就会被自动替换到别的位置,请问有什么办法能确定一个.so 在内存中的长度呢?
举个例子,和李先生文中的例子,libtest1.so 被定位到 0x00c01000,编译厚,用ldd查看,得到 libtest1.so 被加载在 0xb8094000 ,如果不知道这种情况,是不是就会出错呢?
free porn
December 3rd, 2008
Wow very nice read indeed.
Olechka-persik
December 9th, 2008
Огромное спасибо за потрясающие идеи!!! Буду следить за блогом, много всего интересного. А мой блог о науке, надеюсь, тоже понравится
Sergey
February 20th, 2009
Действительно интересный блог. Автор, не желаете ли его продать? Свяжитесь со мной – 7812один. Сергей.
kenny
February 24th, 2009
Такой пост и распечатать не жалко, редко такое найдешь в инете, спасибо!
Sergey
March 11th, 2009
Автор! Почему не отвечаете на письма?! Я действительно заинтересован в покупке Вашего блога. Ваше видение цены на sergey-webmaster(собака)yandex.ru. С уважением, Сергей.
games-h
March 11th, 2009
Games-House – новинки компьютерных мини игр, скачать бесплатно . Квесты, аркады, логические, линии
games-house.ru
Jinny
March 17th, 2009
Greatings,
Where are you from? Is it a secret?
Thank you
Jinny
Zoran
March 23rd, 2009
Hi there,
Thank you! I would now go on this blog every day!
Have a nice day
Zoran
ekspekt
July 1st, 2009
interesting post, will come back here, bookmarked your site