linux下把共享库(SO)加载到指定的内存地址

转载 2007年09月12日 10:32:00

作者:absurd   出处:http://blog.csdn.net/absurd



一位朋友最近遇到一个棘手的问题,希望把共享库(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(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]
 
从以上测试结果可以看出,这种方法是可行的。

 

linux下动态链接库(.so)的显式调用和隐式调用

进入主题前,先看看两点预备知识。 一、显示调用和隐式调用的区别         我们知道,动态库相比静态库的区别是:静态库是编译时就加载到可执行文件中的,而动态库是在程序运行时完成加载的,所以使用动态...
  • lc_910927
  • lc_910927
  • 2015年02月04日 17:25
  • 10015

Linux动态链接库.so文件的创建与使用

1. 介绍        使用GNU的工具我们如何在Linux下创建自己的程序函数库?一个“程序函数库”简单的说就是一个文件包含了一些编译好的代码和数据,这些编译好的代码和数据可以在事后供其他的程序使...
  • sunboy_2050
  • sunboy_2050
  • 2012年03月12日 19:32
  • 46313

关于Linux下.so的介绍和编写过程

 Linux下的.so是基于Linux下的动态链接,其功能和作用类似与windows下.dll文件。  下面是关于.so的介绍: 一、引言通常情况下,对函数库的链接是放在编译时期(compile ti...
  • ytz_linuxer
  • ytz_linuxer
  • 2009年10月28日 23:21
  • 20782

linux下的.a和.so,对比windows下的lib和dll

【原文】http://blog.chinaunix.net/uid-66572-id-2688627.html 两种库 Linux下的静态库以.a结尾(Winodws下为.lib)Linux下...
  • u012234115
  • u012234115
  • 2015年01月13日 13:07
  • 2788

Linux动态链接(3)so文件映射地址

原文地址:http://tsecer.blog.163.com/blog/static/1501817201241392950708/ 一、so文件的加载地址 so文件一般在程序刚启动的时候由动态...
  • jxgz_leo
  • jxgz_leo
  • 2016年08月03日 21:22
  • 393

Linux环境下动态库的生成和使用 (.so文件)

前面说了linux下的静态库。现在来说一说linux下的动态库。      A shared library (also known as a shared object, or as a dy...
  • haojiahuo50401
  • haojiahuo50401
  • 2011年12月25日 21:29
  • 14578

Linux之so加载原理分析

程序的链接和装入存在着多种方法,而如今最为流行的当属动态链接、动态装入方法。本文首先回顾了链接器和装入器的基本工作原理及这一技术的发展历史,然后通过实际的例子剖析了Linux系统下动态链接的实现。了解...
  • sauphy
  • sauphy
  • 2015年11月30日 23:46
  • 612

Linux动态库(.so)符号表

动态库(.so)符号表 最近编译libbinder.so发现system/lib/libbinder.so只有358K,但单独编译生成的obj/SHARED_LIBRARIES/libbinder_...
  • wuzoujing
  • wuzoujing
  • 2017年02月24日 10:22
  • 3156

linux下so动态库一些不为人知的秘密(上)

原文地址::http://blog.chinaunix.net/uid-27105712-id-3313293.html 相关文章 1、linux下so动态库一些不为人知的秘密(中) --...
  • xqhrs232
  • xqhrs232
  • 2016年05月03日 23:59
  • 374

linux .o a .so .la .lo的区别

o: 编译的目标文件-X/S3fmSH]X0a: 静态库,其实就是把若干o文件打了个包kz3noU VpR0so: 动态链接库(共享库)中国电子顶级开发网----最专业的电子论坛、最专...
  • clozxy
  • clozxy
  • 2010年03月16日 10:00
  • 3995
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:linux下把共享库(SO)加载到指定的内存地址
举报原因:
原因补充:

(最多只允许输入30个字)