从程序员的角度看ELF.docx

上传人:b****0 文档编号:17536821 上传时间:2023-07-26 格式:DOCX 页数:43 大小:33.43KB
下载 相关 举报
从程序员的角度看ELF.docx_第1页
第1页 / 共43页
从程序员的角度看ELF.docx_第2页
第2页 / 共43页
从程序员的角度看ELF.docx_第3页
第3页 / 共43页
从程序员的角度看ELF.docx_第4页
第4页 / 共43页
从程序员的角度看ELF.docx_第5页
第5页 / 共43页
从程序员的角度看ELF.docx_第6页
第6页 / 共43页
从程序员的角度看ELF.docx_第7页
第7页 / 共43页
从程序员的角度看ELF.docx_第8页
第8页 / 共43页
从程序员的角度看ELF.docx_第9页
第9页 / 共43页
从程序员的角度看ELF.docx_第10页
第10页 / 共43页
从程序员的角度看ELF.docx_第11页
第11页 / 共43页
从程序员的角度看ELF.docx_第12页
第12页 / 共43页
从程序员的角度看ELF.docx_第13页
第13页 / 共43页
从程序员的角度看ELF.docx_第14页
第14页 / 共43页
从程序员的角度看ELF.docx_第15页
第15页 / 共43页
从程序员的角度看ELF.docx_第16页
第16页 / 共43页
从程序员的角度看ELF.docx_第17页
第17页 / 共43页
从程序员的角度看ELF.docx_第18页
第18页 / 共43页
从程序员的角度看ELF.docx_第19页
第19页 / 共43页
从程序员的角度看ELF.docx_第20页
第20页 / 共43页
亲,该文档总共43页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

从程序员的角度看ELF.docx

《从程序员的角度看ELF.docx》由会员分享,可在线阅读,更多相关《从程序员的角度看ELF.docx(43页珍藏版)》请在冰点文库上搜索。

从程序员的角度看ELF.docx

从程序员的角度看ELF

★概要:

这篇文档从程序员的角度讨论了linux的ELF二进制格式。

介绍了一些ELF执行

文件在运行控制的技术。

展示了如何使用动态连接器和如何动态装载ELF。

我们也演示了如何在LINUX使用GNUC/C++编译器和一些其他工具来创建共享的

C/C++库。

★1前言

最初,UNIX系统实验室(USL)开发和发布了ExecutableandlinkingFormat

(ELF)这样的二进制格式。

在SVR4和Solaris2.x上,都做为可执行文件默认的

二进制格式。

ELF比a.out和COFF更强大更灵活。

结合一些适当的工具,程序员

使用ELF就可以在运行时控制程序的流程。

★2ELF类型

三种主要的ELF文件类型:

.可执行文件:

包含了代码和数据。

具有可执行的程序。

    例如这样一个程序

    

    #filedltest

    dltest:

ELF32-bitLSBexecutable,Intel80386,version1,

        dynamicallylinked(usessharedlibs),notstripped

.可重定位文件:

包含了代码和数据(这些数据是和其他重定位文件和共享的

    object文件一起连接时使用的)

    例如这样文件

    #filelibfoo.o

    libfoo.o:

ELF32-bitLSBrelocatable,Intel80386,version1,

      notstripped

.共享object文件(又可叫做共享库):

包含了代码和数据(这些数据是在连接

    时候被连接器ld和运行时动态连接器使用的)。

动态连接器可能称为

    ld.so.1,libc.so.1或者ld-linux.so.1。

    例如这样文件

    

    #filelibfoo.so

    libfoo.so:

ELF32-bitLSBsharedobject,Intel80386,version

    1,notstripped

ELFsection部分是非常有用的。

使用一些正确的工具和技术,程序员就能

熟练的操作可执行文件的执行。

★3.init和.finisections

在ELF系统上,一个程序是由可执行文件或者还加上一些共享object文件组成。

为了执行这样的程序,系统使用那些文件创建进程的内存映象。

进程映象

有一些段(segment),包含了可执行指令,数据,等等。

为了使一个ELF文件

装载到内存,必须有一个programheader(该programheader是一个描述段

信息的结构数组和一些为程序运行准备的信息)。

一个段可能有多个section组成.这些section在程序员角度来看更显的重要。

每个可执行文件或者是共享object文件一般包含一个sectiontable,该表

是描述ELF文件里sections的结构数组。

这里有几个在ELF文档中定义的比较

特别的sections.以下这些是对程序特别有用的:

.fini

    该section保存着进程终止代码指令。

因此,当一个程序正常退出时,        

    系统安排执行这个section的中的代码。

.init    

    该section保存着可执行指令,它构成了进程的初始化代码。

    因此,当一个程序开始运行时,在main函数被调用之前(c语言称为

    main),系统安排执行这个section的中的代码。

.init和.finisections的存在有着特别的目的。

假如一个函数放到

.initsection,在main函数执行前系统就会执行它。

同理,假如一

个函数放到.finisection,在main函数返回后该函数就会执行。

该特性被C++编译器使用,完成全局的构造和析构函数功能。

当ELF可执行文件被执行,系统将在把控制权交给可执行文件前装载所以相关

的共享object文件。

构造正确的.init和.finisections,构造函数和析构函数

将以正确的次序被调用。

★3.1在c++中全局的构造函数和析构函数

在c++中全局的构造函数和析构函数必须非常小心的处理碰到的语言规范问题。

构造函数必须在main函数之前被调用。

析构函数必须在main函数返回之后

被调用。

例如,除了一般的两个辅助启动文件crti.o和crtn.o外,GNUC/C++

编译器--gcc还提供两个辅助启动文件一个称为crtbegin.o,还有一个被称为

crtend.o。

结合.ctors和.dtors两个section,c++全局的构造函数和析构函数

能以运行时最小的负载,正确的顺序执行。

.ctors

    该section保存着程序的全局的构造函数的指针数组。

.dtors

    该section保存着程序的全局的析构函数的指针数组。

    

ctrbegin.o

    有四个section:

    1.ctorssection

    local标号__CTOR_LIST__指向全局构造函数的指针数组头。

    ctrbegin.o中的该数组只有一个dummy元素。

    [译注:

    #objdump-s-j.ctors                

    /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/crtbegin.o

    /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/crtbegin.o:

    fileformatelf32-i386

    Contentsofsection.ctors:

    0000ffffffff                            ....

    这里说的dummy元素应该就是指的是ffffffff

    ]

    2.dtorssection

    local标号__DTOR_LIST__指向全局析构函数的指针数组头。

    ctrbegin.o中的该数组仅有也只有一个dummy元素。

    3.textsection

    只包含了__do_global_dtors_aux函数,该函数遍历__DTOR_LIST__

    列表,调用列表中的每个析构函数。

函数如下:

Disassemblyofsection.text:

00000000<__do_global_dtors_aux>:

  0:

  55                      push  %ebp

  1:

  89e5                  mov    %esp,%ebp

  3:

  833d0400000000    cmpl  x0,0x4

  a:

  7538                  jne    44<__do_global_dtors_aux+0x44>

  c:

  eb0f                  jmp    1d<__do_global_dtors_aux+0x1d>

  e:

  89f6                  mov    %esi,%esi

  10:

  8d5004                lea    0x4(%eax),%edx

  13:

  891500000000      mov    %edx,0x0

  19:

  8b00                  mov    (%eax),%eax

  1b:

  ffd0                  call  *%eax

  1d:

  a100000000          mov    0x0,%eax

  22:

  833800                cmpl  x0,(%eax)

  25:

  75e9                  jne    10<__do_global_dtors_aux+0x10>

  27:

  b800000000          mov    x0,%eax

  2c:

  85c0                  test  %eax,%eax

  2e:

  740a                  je    3a<__do_global_dtors_aux+0x3a>

  30:

  6800000000          push  x0

  35:

  e8fcffffff          call  36<__do_global_dtors_aux+0x36>

  3a:

  c7050400000001    movl  x1,0x4

  41:

  000000

  44:

  c9                      leave

  45:

  c3                      ret

  46:

  89f6                  mov    %esi,%esi

    4.finisection

    它只包含一个__do_global_dtors_aux的函数调用。

请记住,它仅是

    一个函数调用而不返回的,因为crtbegin.o的.finisection是这个

    函数体的一部分。

函数如下:

Disassemblyofsection.fini:

00000000<.fini>:

  0:

  e8fcffffff          call  1<.fini+0x1>

crtend.o

    也有四个section:

    1.ctorssection

    local标号__CTOR_END__指向全局构造函数的指针数组尾部。

    2.dtorssection

    local标号__DTOR_END__指向全局析构函数的指针数组尾部。

    3.textsection

    只包含了__do_global_ctors_aux函数,该函数遍历__CTOR_LIST__

    列表,调用列表中的每个构造函数。

函数如下:

00000000<__do_global_ctors_aux>:

  0:

  55                      push  %ebp

  1:

  89e5                  mov    %esp,%ebp

  3:

  53                      push  %ebx

  4:

  bbfcffffff          mov    xfffffffc,%ebx

  9:

  833dfcffffffff    cmpl  xffffffff,0xfffffffc

  10:

  740c                  je    1e<__do_global_ctors_aux+0x1e>

  12:

  8b03                  mov    (%ebx),%eax

  14:

  ffd0                  call  *%eax

  16:

  83c3fc                add    xfffffffc,%ebx

  19:

  833bff                cmpl  xffffffff,(%ebx)

  1c:

  75f4                  jne    12<__do_global_ctors_aux+0x12>

  1e:

  8b5dfc                mov    0xfffffffc(%ebp),%ebx

  21:

  c9                      leave

  22:

  c3                      ret

  23:

  90                      nop

    4.initsection

    它只包含一个__do_global_ctors_aux的函数调用。

请记住,它仅是

    一个函数调用而不返回的,因为crtend.o的.initsection是这个函

    数体的一部分。

函数如下:

Disassemblyofsection.init:

00000000<.init>:

  0:

  e8fcffffff          call  1<.init+0x1>

crti.o

    在.initsection中仅是个_init的函数标号。

    在.finisection中的_fini函数标号。

crtn.o

    在.init和.finisection中仅是返回指令。

Disassemblyofsection.init:

00000000<.init>:

  0:

  8b5dfc                mov    0xfffffffc(%ebp),%ebx

  3:

  c9                      leave

  4:

  c3                      ret

Disassemblyofsection.fini:

00000000<.fini>:

  0:

  8b5dfc                mov    0xfffffffc(%ebp),%ebx

  3:

  c9                      leave

  4:

  c3                      ret

编译产生可重定位文件时,gcc把每个全局构造函数挂在__CTOR_LIST上

(通过把指向构造函数的指针放到.ctorssection中)。

它也把每个全局析构函挂在__DTOR_LIST上(通过把指向析构函的指针

放到.dtorssection中)。

连接时,gcc在所有重定位文件前处理crtbegin.o,在所有重定位文件后处理

crtend.o。

另外,crti.o在crtbegin.o之前被处理,crtn.o在crtend.o之后

被处理。

当产生可执行文件时,连接器ld分别的连接所有可重定位文件的ctors和

.dtorssection到__CTOR_LIST__和__DTOR_LIST__列表中。

.initsection

由所有的可重定位文件中_init函数组成。

.fini由_fini函数组成。

运行时,系统将在main函数之前执行_init函数,在main函数返回后执行

_fini函数。

★4ELF的动态连接与装载

★4.1动态连接

当在UNIX系统下,用C编译器把C源代码编译成可执行文件时,c编译驱动器一般

将调用C的预处理,编译器,汇编器和连接器。

.    c编译驱动器首先把C源代码传到C的预处理器,它以处理过的宏和

    指示器形式输出纯C语言代码。

.    c编译器把处理过的C语言代码翻译为机器相关的汇编代码。

.    汇编器把结果的汇编语言代码翻译成目标的机器指令。

结果这些

    机器指令就被存储成指定的二进制文件格式,在这里,我们使用的

    ELF格式。

.    最后的阶段,连接器连接所有的object文件,加入所有的启动代码和

    在程序中引用的库函数。

    下面有两种方法使用lib库

    

    --staticlibrary

    一个集合,包含了那些object文件中包含的library例程和数据。

    该方法,连接时连接器将产生一个独立的object文件(这些

    object文件保存着程序所要引用的函数和数据)的copy。

    

    --sharedlibrary

    是共享文件,它包含了函数和数据。

用这样连接出来的程序仅在可执行

    程序中存储着共享库的名字和一些程序引用到的标号。

在运行时,动态

    连接器(在ELF中也叫做程序解释器)将把共享库映象到进程的虚拟

    地址空间里去,通过名字解析在共享库中的标号。

该处理过程也称为

    动态连接(dynamiclinking)

程序员不需要知道动态连接时用到的共享库做什么,每件事情对程序员都是

透明的。

★4.2动态装载(DynamicLoading)

动态装载是这样一个过程:

把共享库放到执行时进程的地址空间,在库中查找

函数的地址,然后调用那个函数,当不再需要的时候,卸载共享库。

它的执行

过程作为动态连接的服务接口。

在ELF下,程序接口通常在中被定义。

如下:

void*dlopen(constchar*filename,intflag);

constchar*dlerror(void);

constvoid*dlsym(voidhandle*,constchar*symbol);

intdlclose(void*handle);

这些函数包含在libdl.so中。

下面是个例子,展示动态装载是如何工作的。

主程序在运行时动态的装载共享库。

一方面可指出哪个共享库被使用,哪个

函数被调用。

一方面也能在访问共享库中的数据。

[alert7@redhat62dl]#catdltest.c

#include

#include

#include

#include

#include

typedefvoid(*func_t)(constchar*);

voiddltest(constchar*s)

{

    printf("Fromdltest:

");

    for(;*s;s++)

    {    

        putchar(toupper(*s));

    }

    putchar('\n');

}

main(intargc,char**argv)

{

void*handle;

func_tfptr;

char*libname="./libfoo.so";

char**name=NULL;

char*funcname="foo";

char*param="DynamicLoadingTest";

intch;

intmode=RTLD_LAZY;    

while((ch=getopt(argc,argv,"a:

b:

f:

l:

"))!

=EOF)

{

    switch(ch)

    {

    case'a':

/*argument*/

        param=optarg;

        break;

    case'b':

/*howtobind*/

        switch(*optarg)

        {

        case'l':

/*lazy*/

        mode=RTLD_LAZY;

        break;

        case'n':

/*now*/

        mode=RTLD_NOW;

        break;

        }

        break;

    case'l':

/*whichsharedlibrary*/

        libname=optarg;

        break;

    case'f':

/*whichfunction*/

        funcname=optarg;

    }

  }

handle=dlopen(libname,mode);

if(handle==NULL)

{

fprintf(stderr,"%s:

dlopen:

'%s'\n",libname,dlerror());

exit

(1);

}

fptr=(func_t)dlsym(handle,funcname);

if(fptr==NULL)

{

fprintf(stderr,"%s:

dlsym:

'%s'\n",funcname,dlerror());

exit

(1);

}

name=(char**)dlsym(handle,"libname");

if(name==NULL)

{

fprintf(stderr,"%s:

dlsym:

'libname'\n",dlerror());

exit

(1);

}

printf("Call'%s'in'%s':

\n",funcname,*name);

/*callthatfunctionwith'param'*/

(*fptr)(param);

dlclose(handle);

return0;

}

这里有两个共享库,一个是libfoo.so一个是libbar.so。

每个都用同样的全局

字符串变量libname,分别各自有foo和bar函数。

通过dlsym,对程序来说,他们

都是可用的。

[alert7@redhat62dl]#catlibbar.c

#include

externvoiddltest(constchar*);

constchar*constlibname="libbar.so";

voidbar(constchar*s)

{

dltest("Calledfromlibbar.");

printf("libbar:

%s\n",s);

}

[alert7@redhat62dl]#catlibfoo.c

#include

externvoiddltest(constchar*s);

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 医药卫生 > 药学

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2