文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>从程序员角度看ELF (3)

从程序员角度看ELF (3)

时间:2007-04-09  来源:Echo CHEN

★5.3 扩展的GCC特性 

GCC有许多扩展的特性。有些对ELF特别的有用。其中一个就是__attribute__。 使用__attribute__可以使一个函数放到__CTOR_LIST__或者__DTOR_LIST__里。 
例如: 

[alert7@redhat62 dl]# cat miss.c 

#include <stdio.h> 
#include <stdlib.h> 

static void foo(void) __attribute__ ((constructor)); 
static void bar(void) __attribute__ ((destructor)); 

int main(int argc, char *argv[]) 

        printf("foo == %p\n", foo); 
        printf("bar == %p\n", bar); 

        exit(EXIT_SUCCESS); 


void foo(void) 

        printf("hi dear njlily!\n"); 


void bar(void) 

        printf("missing u! goodbye!\n"); 


[alert7@redhat62 dl]# gcc -o miss miss.c 
[alert7@redhat62 dl]# ./miss 
hi dear njlily! 
foo == 0x8048434 
bar == 0x8048448 
missing u! goodbye! 

我们来看看是否加到了.ctors和.dtors中。 
[alert7@redhat62 dl]# objdump -s -j .ctors miss 

miss:     file format elf32-i386 

Contents of section .ctors: 
8049504 ffffffff 34840408 00000000           ....4....... 

[alert7@redhat62 dl]# objdump -s -j .dtors miss 

miss:     file format elf32-i386 

Contents of section .dtors: 
8049510 ffffffff 48840408 00000000           ....H....... 

已经把foo和bar地址分别放到了.ctors和.dors,显示34840408只是因为 
x86上是LSB编码的,小端序。 

__attribute__ ((constructor))促使函数foo在进入main之前会被自动调用。__attribute__ ((destructor))促使函数bar在main返回或者exit调用之后会被自动调用。foo和bar必须是不能带参数的而且必须是static void类型的函数。在ELF下,这个特性在一般的可执行文件和共享库中都能很好的工作。 

我们也可以创建自己的section,在这里我创建了一个alert7 section. 
[alert7@redhat62 dl]# cat test.c 
#include <stdio.h> 
#include <stdlib.h> 

static void foo(void) __attribute__ ((section ("alert7"))); 
static void bar(void) __attribute__ ((section ("alert7"))); 

int main(int argc, char *argv[]) 

        foo(); 

        printf("foo == %p\n", foo); 
        printf("bar == %p\n", bar); 
        bar(); 
        exit(EXIT_SUCCESS); 


void foo(void) 

        printf("hi dear njlily!\n"); 

void bar(void) 

        printf("missing u! goodbye!\n"); 

[alert7@redhat62 dl]# gcc -o test test.c 
[alert7@redhat62 dl]# ./test 
hi dear njlily! 
foo == 0x804847c 
bar == 0x8048490 
missing u! goodbye! 

[alert7@redhat62 dl]# objdump -x test 
.... 
Sections: 
Idx Name          Size      VMA       LMA       File off  Algn 
  0 .interp       00000013  080480f4  080480f4  000000f4  2**0 
                  CONTENTS, ALLOC, LOAD, READONLY, DATA 
... 
12 alert7        00000026  0804847c  0804847c  0000047c  2**2 
                  CONTENTS, ALLOC, LOAD, READONLY, CODE 
... 

[alert7@redhat62 dl]# objdump -D test 
Disassembly of section alert7: 

0804847c <foo>: 
804847c:       55                      push   %ebp 
804847d:       89 e5                   mov    %esp,%ebp 
804847f:       68 de 84 04 08          push   $0x80484de 
8048484:       e8 a3 fe ff ff          call   804832c <_init+0x70> 
8048489:       83 c4 04                add    $0x4,%esp 
804848c:       c9                      leave 
804848d:       c3                      ret 
804848e:       89 f6                   mov    %esi,%esi 

08048490 <bar>: 
8048490:       55                      push   %ebp 
8048491:       89 e5                   mov    %esp,%ebp 
8048493:       68 ef 84 04 08          push   $0x80484ef 
8048498:       e8 8f fe ff ff          call   804832c <_init+0x70> 
804849d:       83 c4 04                add    $0x4,%esp 
80484a0:       c9                      leave 
80484a1:       c3                      ret 

在这里,我创建了一个自己的alert7 section,并把foo,bar两个函数放到了这个section中。一般定义的函数都会放在.text section中。 

★5.3.1 在C库中的初始化函数 

另外一个GCC的特性是__attribute__( section ("sectionname") ).使用这个,能把一个函数或者是数据结构放到任何的section中。 

static void 
foo (int argc,char **argc,char **envp) 
    __attribute__ ((section ("_libc_foo"))); 

static void 
foo (int argc,char **argv,char **envp) 



static void 
bar (int argc,char **argv,char **envp) 



static void * __libc_subinit_bar__ 
    __attribute__ (( section ("_libc_subinit")))=&(bar); 

这里,我们把foo放到了_libc_foo section,把__libc_subinit_bar__放到了_libc_subinit section中。在Linux C库中,_libc_subinit 是一个特别的section,它包含了一个函数指针(有如下原型)的数组。 

void (*) (int argc,char **argv,char **envp); 

这里的argc,argv,envp跟在main中的有同样的意义。该section中的函数在进入main函数之前就会被调用。这是很有用的,可用来在Linux C库中初始化一些全局变量。 

    [译注:_libc_subinit section真有这个特别的功能吗?我是没有试成功,如果有人试成功或者认为我理解有误的地方,千万记得mail给我:) 
    测试程序如下: 
    #include <stdio.h> 
    #include <stdlib.h> 
    static void foo(int argc,char **argv,char **envp) 
    { 
        printf("hi dear njlily!\n"); 
    } 
     
    int main(int argc, char *argv[]) 
    { 
        printf("foo == %p\n", foo); 
        exit(EXIT_SUCCESS); 
    } 
     
    static void * __libc_subinit_bar__ 
            __attribute__ (( section ("_libc_subinit")))=&(foo); 
     
    [alert7@redhat62 dl]# gcc -o test1 test1.c 
    [alert7@redhat62 dl]# ./test1 
    foo == 0x8048400 
    :( 用objdump,显示已经创建了一个_libc_subinit section,并且 
    该section前四个字节就是foo地址0x8048400 
     
    ] 
★5.4 利用GCC和GNU ld 

这一些命令行的选项对GCC和GNU ld创建ELF时特别有用。-shared告诉gcc产生一个共享库,该共享库能在连接时和其他的共享文件一起形成可执行文件,该共享库也能在运行时装载进可执行文件的地址空间。使用-shared是创建一个共享ELF库的首选方法。 

另外一个有用的命令行选项是-Wl,ldoption,传递参数ldoption作为连接器的选项。假如ldoption包含多个逗号,将分离成多个选项。 

-static选项将产生一个和static库一道连接的可执行文件。当没有开启-static选项时,连接器首先试着用共享库,假如共享版本不可用,然后再试着用静态(static)库。 

这里还有些特别的命令行选项对ELF来说特别的或者说是有用的。 

-dynamic-linker file 
    设置动态连接器(dynamic linker)的名字。默认的动态连接器 或者是/usr/lib/libc.so.1或者是/usr/lib/libd1.so.1 
     

-export-dynamic 
    告诉连接器使在可执行文件中的所有标号对动态连接器可用。当一个动态装载进的共享库参考可执行文件中的标号,该标号一般在动态连接时是不可用时,这时候就特别有用。 

-lfile 
    加文件到需要连接的列表中。该选项可用在许多时候。ld将搜索它的path-list查找文件libfile.so(也就是说假如库为libbar.so,那么使用的时候就这样使用,-lbar),或者libfile.a(static版本的)。 
    一些情况下,共享库名libfile.so会被存储在resulting executable 或者是共享库中。当resulting executable或者是共享库被装载进内存,动态连接器也将把使用记录过的共享库装载到进程的地址空间去。在以后的事情情况下,把必要的函数和数据被拷贝到可执行文件,减少代码长度。 

-m emulation 
    仿效emulation连接器r.-V参数可列出所有可用的选项. 

-M | -Map mapfile 
    把连接map输出到标准输出或者一个mapfile文件里,该连接map含有关于标号被ld映象到了哪里的一些诊断信息,还有全局共同的存储分配信息。 

-rpath directory 
    加一个目录到运行时library的搜索路径。所有的-rpath参数被连接在一起然后传给动态连接器。它们被用来在运行时定位共享库。 

-soname name 
    当创建一个共享库时,指定的名字被放在共享库中。当和这个共享库连接的可执行文件被运行,动态连接器将试着map记录着的指定名字的共享库而不是传给连接器的文件名。 

-static 
    告诉连接器不要和任何共享库连接。 

-verbose 
    告诉连接器打印它每个要打开的文件名。 

linux下gcc beta版本使用-dynamic-linker file选项设置动态连接器为/lib/ld-linker.so.1。该选项可以使ELF和a.out共享库很好的共存。 

有件事情是另人感兴趣的。 

[alert7@redhat62 dl]# gcc -shared -o libbar.so libbar.o -lfoo 

假如libfoo.so被用来创建共享库时,有趣的时候就会发生了。当libbar.so被映象到进程的地址空间的时候,动态连接器也把libfoo.so映象到内存。假如libbar.so需要libfoo.so的时候,这个特性非常有用。实际上使用libbars.o库的程序编译时是不需要-lfoo的。假如archive版本的libfoo.a被使用,当在libbar.a中的标号被libbar.o引用时,它将会被搜索到。假使在libbar.so中包含libfoo.a甚至它们根本不被libbar.o使用,在这样的情况下必须逐步把.o文件加到libbar.o中: 

# rm -rf /tmp/foo 
# mkdir /tmp/foo 
# (cd /tmp/foo/;ar -x ....../libfoo.a) 
# gcc -shared -o libbar.so libbar.o /tmp/foo/*.o 
# rm -rf /tmp/foo 

在libfoo.a中的.o文件必须用-fPIC编译或者至少和PIC(位置无关)是兼容的。 

当使用 

static void * __libc_subinit_bar__ 
    __attribute__    ((section ("_libc_subinit")))=&(bar); 

来把一个标号放到一个没有被连接器定义的section中(在这里是_libc_subinit).连接器将所有在_libc_subinit section中的标号共同创建两个标号,一个是__start__libc_subinit和__stop__libc_subinit,它们作为C的标志符被使用。 

警告: 
下面是完全可能的:连接器可能不能搜索到包含_libc_subinit section的文件(该section中没有程序执行需要的标号)。这就使程序要确定使_libc_subinit section能被连接器搜索得到。 

一种解决的办法是:把一个dummy标号放到_libc_subinit section中,在文件中定义它,使它参考引用_libc_subinit section. 
相关阅读 更多 +
排行榜 更多 +
盛世天下

盛世天下

角色扮演 下载
镇魂街破晓手游

镇魂街破晓手游

角色扮演 下载
磨碎物品

磨碎物品

休闲益智 下载