LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 8036|回复: 25

从程序员角度看ELF[z

[复制链接]
发表于 2003-6-24 23:16:47 | 显示全部楼层 |阅读模式
转自http://www.xfocus.net/articles/200109/260.html

这篇文章从ELF的文件格式讨论到了ELF的构成及动态库的构成
是任何一个想深入了解UNIX编程的程序员应该深读的



创建时间:2001-09-15
文章属性:翻译
文章来源:http://www.xfocus.org
文章提交:alert7 (sztcww_at_sina.com)

从程序员角度看ELF

原文:《 ELF:From The Programmer's Perspective》

作者:Hongjiu Lu <mailto: hjl@nynexst.com>
NYNEX Science & Technology, Inc.
500 Westchester Avenue
White Plains, NY 10604, USA

翻译:alert7 <mailto: alert7@21cn.com
alert7@xfocus.org
>

主页: http://www.xfocus.org
时间: 2001-9-10


_________________
 楼主| 发表于 2003-6-24 23:17:11 | 显示全部楼层
★2 ELF类型

三种主要的ELF文件类型:

.可执行文件:包含了代码和数据。具有可执行的程序。
    例如这样一个程序
   
    # file dltest
    dltest: ELF 32-bit LSB executable, Intel 80386, version 1,
        dynamically linked (uses shared libs), not stripped

.可重定位文件:包含了代码和数据(这些数据是和其他重定位文件和共享的
    object文件一起连接时使用的)
    例如这样文件

    # file libfoo.o
    libfoo.o: ELF 32-bit LSB relocatable, Intel 80386, version 1,
       not stripped

.共享object文件(又可叫做共享库):包含了代码和数据(这些数据是在连接
    时候被连接器ld和运行时动态连接器使用的)。动态连接器可能称为
    ld.so.1,libc.so.1 或者 ld-linux.so.1。
    例如这样文件
   
    # file libfoo.so
    libfoo.so: ELF 32-bit LSB shared object, Intel 80386, version
    1, not stripped

ELF section部分是非常有用的。使用一些正确的工具和技术,程序员就能
熟练的操作可执行文件的执行。
 楼主| 发表于 2003-6-24 23:17:23 | 显示全部楼层
★3 .init和.fini sections

在ELF系统上,一个程序是由可执行文件或者还加上一些共享object文件组成。
为了执行这样的程序,系统使用那些文件创建进程的内存映象。进程映象
有一些段(segment),包含了可执行指令,数据,等等。为了使一个ELF文件
装载到内存,必须有一个program header(该program header是一个描述段
信息的结构数组和一些为程序运行准备的信息)。

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

每个可执行文件或者是共享object文件一般包含一个section table,该表
是描述ELF文件里sections的结构数组。这里有几个在ELF文档中定义的比较
特别的sections.以下这些是对程序特别有用的:

.fini
    该section保存着进程终止代码指令。因此,当一个程序正常退出时,        
    系统安排执行这个section的中的代码。
.init   
    该section保存着可执行指令,它构成了进程的初始化代码。
    因此,当一个程序开始运行时,在main函数被调用之前(c语言称为
    main),系统安排执行这个section的中的代码。

.init和.fini sections的存在有着特别的目的。假如一个函数放到
.init section,在main函数执行前系统就会执行它。同理,假如一
个函数放到.fini section,在main函数返回后该函数就会执行。
该特性被C++编译器使用,完成全局的构造和析构函数功能。

当ELF可执行文件被执行,系统将在把控制权交给可执行文件前装载所以相关
的共享object文件。构造正确的.init和.fini sections,构造函数和析构函数
将以正确的次序被调用。
 楼主| 发表于 2003-6-24 23:17:40 | 显示全部楼层
★3.1 在c++中全局的构造函数和析构函数

在c++中全局的构造函数和析构函数必须非常小心的处理碰到的语言规范问题。
构造函数必须在main函数之前被调用。析构函数必须在main函数返回之后
被调用。例如,除了一般的两个辅助启动文件crti.o和crtn.o外,GNU C/C++
编译器--gcc还提供两个辅助启动文件一个称为crtbegin.o,还有一个被称为
crtend.o。结合.ctors和.dtors两个section,c++全局的构造函数和析构函数
能以运行时最小的负载,正确的顺序执行。


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

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

ctrbegin.o
    有四个section:
    1 .ctors section
    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:
    file format elf32-i386
    Contents of section .ctors:
    0000 ffffffff                             ....
    这里说的dummy元素应该就是指的是ffffffff
    ]

    2 .dtors section
    local标号__DTOR_LIST__指向全局析构函数的指针数组头。在
    ctrbegin.o中的该数组仅有也只有一个dummy元素。

    3 .text section
    只包含了__do_global_dtors_aux函数,该函数遍历__DTOR_LIST__
    列表,调用列表中的每个析构函数。
函数如下:

Disassembly of section .text:

00000000 <__do_global_dtors_aux>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 3d 04 00 00 00 00    cmpl   $0x0,0x4
   a:   75 38                   jne    44 <__do_global_dtors_aux+0x44>
   c:   eb 0f                   jmp    1d <__do_global_dtors_aux+0x1d>
   e:   89 f6                   mov    %esi,%esi
  10:   8d 50 04                lea    0x4(%eax),%edx
  13:   89 15 00 00 00 00       mov    %edx,0x0
  19:   8b 00                   mov    (%eax),%eax
  1b:   ff d0                   call   *%eax
  1d:   a1 00 00 00 00          mov    0x0,%eax
  22:   83 38 00                cmpl   $0x0,(%eax)
  25:   75 e9                   jne    10 <__do_global_dtors_aux+0x10>
  27:   b8 00 00 00 00          mov    $0x0,%eax
  2c:   85 c0                   test   %eax,%eax
  2e:   74 0a                   je     3a <__do_global_dtors_aux+0x3a>
  30:   68 00 00 00 00          push   $0x0
  35:   e8 fc ff ff ff          call   36 <__do_global_dtors_aux+0x36>
  3a:   c7 05 04 00 00 00 01    movl   $0x1,0x4
  41:   00 00 00
  44:   c9                      leave
  45:   c3                      ret
  46:   89 f6                   mov    %esi,%esi


    4 .fini section
    它只包含一个__do_global_dtors_aux的函数调用。请记住,它仅是
    一个函数调用而不返回的,因为crtbegin.o的.fini section是这个
    函数体的一部分。
函数如下:
Disassembly of section .fini:

00000000 <.fini>:
   0:   e8 fc ff ff ff          call   1 <.fini+0x1>
 楼主| 发表于 2003-6-24 23:18:02 | 显示全部楼层
crtend.o
    也有四个section:

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

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

    3 .text section
    只包含了__do_global_ctors_aux函数,该函数遍历__CTOR_LIST__
    列表,调用列表中的每个构造函数。
函数如下:
00000000 <__do_global_ctors_aux>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   53                      push   %ebx
   4:   bb fc ff ff ff          mov    $0xfffffffc,%ebx
   9:   83 3d fc ff ff ff ff    cmpl   $0xffffffff,0xfffffffc
  10:   74 0c                   je     1e <__do_global_ctors_aux+0x1e>
  12:   8b 03                   mov    (%ebx),%eax
  14:   ff d0                   call   *%eax
  16:   83 c3 fc                add    $0xfffffffc,%ebx
  19:   83 3b ff                cmpl   $0xffffffff,(%ebx)
  1c:   75 f4                   jne    12 <__do_global_ctors_aux+0x12>
  1e:   8b 5d fc                mov    0xfffffffc(%ebp),%ebx
  21:   c9                      leave
  22:   c3                      ret
  23:   90                      nop

    4 .init section
    它只包含一个__do_global_ctors_aux的函数调用。请记住,它仅是
    一个函数调用而不返回的,因为crtend.o的.init section是这个函
    数体的一部分。
函数如下:
Disassembly of section .init:

00000000 <.init>:
   0:   e8 fc ff ff ff          call   1 <.init+0x1>


crti.o
    在.init section中仅是个_init的函数标号。
    在.fini section中的_fini函数标号。

crtn.o
    在.init和.fini section中仅是返回指令。

Disassembly of section .init:

00000000 <.init>:
   0:   8b 5d fc                mov    0xfffffffc(%ebp),%ebx
   3:   c9                      leave
   4:   c3                      ret
Disassembly of section .fini:

00000000 <.fini>:
   0:   8b 5d fc                mov    0xfffffffc(%ebp),%ebx
   3:   c9                      leave
   4:   c3                      ret

编译产生可重定位文件时,gcc把每个全局构造函数挂在__CTOR_LIST上
(通过把指向构造函数的指针放到.ctors section中)。
它也把每个全局析构函挂在__DTOR_LIST上(通过把指向析构函的指针
放到.dtors section中)。

连接时,gcc在所有重定位文件前处理crtbegin.o,在所有重定位文件后处理
crtend.o。另外,crti.o在crtbegin.o之前被处理,crtn.o在crtend.o之后
被处理。

当产生可执行文件时,连接器ld分别的连接所有可重定位文件的ctors 和
.dtors section到__CTOR_LIST__和__DTOR_LIST__列表中。.init section
由所有的可重定位文件中_init函数组成。.fini由_fini函数组成。

运行时,系统将在main函数之前执行_init函数,在main函数返回后执行
_fini函数。
 楼主| 发表于 2003-6-24 23:18:17 | 显示全部楼层
★4 ELF的动态连接与装载

★4.1 动态连接

当在UNIX系统下,用C编译器把C源代码编译成可执行文件时,c编译驱动器一般
将调用C的预处理,编译器,汇编器和连接器。

.     c编译驱动器首先把C源代码传到C的预处理器,它以处理过的宏和
    指示器形式输出纯C语言代码。

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

.    汇编器把结果的汇编语言代码翻译成目标的机器指令。结果这些
    机器指令就被存储成指定的二进制文件格式,在这里,我们使用的
    ELF格式。

.    最后的阶段,连接器连接所有的object文件,加入所有的启动代码和
    在程序中引用的库函数。

    下面有两种方法使用lib库
   
    --static library
    一个集合,包含了那些object文件中包含的library例程和数据。用
    该方法,连接时连接器将产生一个独立的object文件(这些
    object文件保存着程序所要引用的函数和数据)的copy。
   
    --shared library
    是共享文件,它包含了函数和数据。用这样连接出来的程序仅在可执行
    程序中存储着共享库的名字和一些程序引用到的标号。在运行时,动态
    连接器(在ELF中也叫做程序解释器)将把共享库映象到进程的虚拟
    地址空间里去,通过名字解析在共享库中的标号。该处理过程也称为
    动态连接(dynamic linking)

程序员不需要知道动态连接时用到的共享库做什么,每件事情对程序员都是
透明的。
 楼主| 发表于 2003-6-24 23:18:33 | 显示全部楼层
★4.2 动态装载(Dynamic Loading)

动态装载是这样一个过程:把共享库放到执行时进程的地址空间,在库中查找
函数的地址,然后调用那个函数,当不再需要的时候,卸载共享库。它的执行
过程作为动态连接的服务接口。

在ELF下,程序接口通常在<dlfcn.h>中被定义。如下:

void *dlopen(const char * filename,int flag);
const char * dlerror(void);
const void * dlsym (void handle*,const char * symbol);
int dlclose(void * handle);

这些函数包含在libdl.so中。下面是个例子,展示动态装载是如何工作的。
主程序在运行时动态的装载共享库。一方面可指出哪个共享库被使用,哪个
函数被调用。一方面也能在访问共享库中的数据。

[alert7@redhat62 dl]# cat dltest.c
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <dlfcn.h>
#include <ctype.h>

typedef void (*func_t) (const char *);

void dltest(const char *s)
{
    printf("From dltest:");
    for (;*s;s++)
    {   
        putchar(toupper(*s));
    }
    putchar('\n');
}

main(int argc,char **argv)
{
void *handle;
func_t fptr;
char * libname = "./libfoo.so";
char **name=NULL;
char *funcname = "foo";
char *param= "Dynamic Loading Test";
int ch;
int mode=RTLD_LAZY;   

while ((ch = getopt(argc,argv,"a:b:f:l:"))!=EOF)
{
    switch(ch)
    {
    case 'a':/*argument*/
        param=optarg;
        break;
    case 'b':/*how to bind*/
        switch(*optarg)
        {
        case 'l':/*lazy*/
        mode = RTLD_LAZY;
        break;
        case 'n':/*now*/
        mode = RTLD_NOW;
        break;
        }
        break;
    case 'l':/*which shared library*/
        libname= optarg;
        break;
    case 'f':/*which function*/
        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);

/*call that function with 'param'*/
(*fptr)(param);

dlclose(handle);
return 0;

}

这里有两个共享库,一个是libfoo.so一个是libbar.so。每个都用同样的全局
字符串变量libname,分别各自有foo和bar函数。通过dlsym,对程序来说,他们
都是可用的。

[alert7@redhat62 dl]# cat libbar.c
#include <stdio.h>

extern void dltest(const char *);
const char * const libname = "libbar.so";

void bar (const char *s)
{
dltest("Called from libbar.");
printf("libbar:%s\n",s);
}


[alert7@redhat62 dl]# cat libfoo.c
#include <stdio.h>

extern void dltest (const char *s);
const char *const libname="libfoo.so";

void foo(const char *s)
{
    const char *saved=s;
   
    dltest("Called from libfoo");
    printf("libfoo:");
    for (;*s;s++);
    for (s--;s>=saved;s--)
    {
    putchar (*s);
    }
    putchar ('\n');
}

使用Makefile文件来编译共享库和主程序是很有用的。因为libbar.so和
libfoo.so也调用了主程序里的dltest函数。

[alert7@redhat62 dl] #cat Makefile
CC=gcc
LDFLAGS=-rdynamic
SHLDFLAGS=
RM=rm

all:dltest

libfoo.o:libfoo.c
    $(CC) -c -fPIC $<

libfoo.so:libfoo.o
    $(CC) $(SHLDFLAGS) -shared -o $@ $^

libbar: libbar.c
    $(CC) -c -fPIC $<

libbar.so:libbar.o
    $(CC) $(SHLDFLAGS) -shared -o $@ $^

dltest: dltest.o libbar.so libfoo.so
    $(CC) $(LDFLAGS) -o $@ dltest.o -ldl

clean:
    $(RM) *.o *.so dltest

处理流程:

[alert7@redhat62 dl]# export ELF_LD_LIBRARY_PATH=.
[alert7@redhat62 dl]# ./dltest
Call 'foo' in 'libfoo.so':
From dltest:CALLED FROM LIBFOO
libfoo:tseT gnidaoL cimanyD
[alert7@redhat62 dl]# ./dltest -f bar
bar:dlsym:'./libfoo.so: undefined symbol: bar'
[alert7@redhat62 dl]# ./dltest -f bar -l ./libbar.so
Call 'bar' in 'libbar.so':
From dltest:CALLED FROM LIBBAR.
libbarynamic Loading Test


在动态装载进程中调用的第一个函数就是dlopen,它使得共享可库对
运行着的进程可用。dlopen返回一个handle,该handle被后面的dlsym
和dlclose函数使用。dlopen的参数为NULL有特殊的意思---它使得在
程序导出的标号和当前已经装载进内存的共享库导出的标号通过dlsym
就可利用。

在一个共享库已经装载进运行着的进程的地址空间后,dlsym可用来
获得在共享库中导出的标号地址。然后就可以通过dlsym返回的地址
来访问里面的函数和数据。

当一个共享库不再需要使用的时候,就可以调用dlclose卸载该函数库。
假如共享库在启动时刻或者是通过其他的dlopen调用被装载的话,该
共享库不会从调用的进程的地址空间被移走。

假如dlclose操作成功,返回为0。dlopen和dlsym如果有错误,将返回
为NULL。为了获取诊断信息,可调用dlerror.
 楼主| 发表于 2003-6-24 23:18:50 | 显示全部楼层
★5 支持ELF的LINUX上的编译器GNU GCC

感谢Eric Youngdale (eric@aib.com),lan Lance Taylor (ian@cygnus.com)
还有许多为gcc支持ELF功能的默默做贡献的人。我们能用gcc和GNU的二进制
工具很容易的创建ELF可执行文件和共享库。

★5.1 共享C库 Shared C Library

在ELF下构造一个共享库比其他的容易的多。但是需要编译器,汇编器,
连接器的支持。首先,需要产生位置无关(position-independent)代码。
要做到这一点,gcc需要加上编译选项-fPIC
[alert7@redhat62 dl]# gcc -fPIC -O -c libbar.c

这时候就适合构造共享库了,加上-shared编译选项
[alert7@redhat62 dl]# gcc -shared -o libbar.so libbar.o

现在我们构造的libbar.so就可以被连接器(link editor)和动态连接器
(dynamic linker)。只要编译时带上-fPIC编译选项,可以把许多重定位
文件加到共享库中。为了把baz.o和共享库连接在一起,可以如下操作:
# gcc -O -c baz.c
# gcc -o baz baz.o -L. -lbar

在把libbar.so安装到动态连接器能找到的正确位置上之后,运行baz将
使libbar.so映象到baz的进程地址空间。内存中libbar.so的一份拷贝将
被所有的可执行文件(这些可执行程序连接时和它一块儿连接的或者
在运行时动态装载的)共享。
 楼主| 发表于 2003-6-24 23:19:03 | 显示全部楼层
★5.2 共享C++库 Shared C++ Library

在共享c++库中主要的困难是如何对待构造函数和析构函数。
在SunOS下,构造和使用一个共享的ELF C库是容易的,但是在SunOS下不能
构造共享的C++库,因为构造函数和析构函数有特别的需求。为止,在ELF
中的.init和.init section提供了完美的解决方法。

当构造共享C++库时,我们使用crtbegin.o和crtend.o这两个特殊的版本,
(它们已经是经过-fPIC的)。对于连接器(link editor)来说,构造共享
的C++库几乎是和一般的可执行文件一样的。全局的构造函数和析构函数
被.init和.fini section处理(在上面3.1节中已经讨论过)。

但一个共享库被映射到进程的地址空间时,动态连接器将在传控制权给程序
之前执行_init函数,并且将为_fini函数安排在共享库不再需要的时候被
执行。

连接选项-shared是告诉gcc以正确的顺序放置必要的辅助文件并且告诉它将
产生一个共享库。-v选项将显示什么文件什么选项被传到了连接器
(link editor).

[alert7@redhat62 dl]# gcc -v -shared -o libbar.so libbar.o
Reading specs from /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/specs
gcc version egcs-2.91.66 19990314/Linux (egcs-1.1.2 release)
/usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/collect2 -m elf_i386
-shared -o libbar.so /usr/lib/crti.o /usr/lib/gcc-lib/i386-redhat
    -linux/egcs-2.91.66/crtbeginS.o
-L/usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66
-L/usr/i386-redhat-linux/lib libbar.o -lgcc -lc --version-script
/usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/libgcc.map
-lgcc /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/crtendS.o
/usr/lib/crtn.o

crtbeginS.o和crtendS.o用-fPIC编译的两个特殊的版本。带上-shared
创建共享库是重要的,因为那些辅助的文件也提供其他服务。我们将在
5.3节中讨论。
 楼主| 发表于 2003-6-24 23:19:17 | 显示全部楼层
★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中。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表