ELF文件格式解析

2018-10-09 14:27 IoT安全 移动安全

前言

之前看过很多人写的有关ELF文件解析的文章,但是很少看到有一篇文章能够把ELF文件内部的数据关系用图清晰地表达出来,基本上都是从头到尾说了一大堆,有时候看的晕乎乎的,笔者这里自己画了一幅图方便读者理解ELF文件格式,这篇文章也算是自己的一篇学习笔记吧。

ELF文件简图

本文将沿着这幅图的逻辑对ELF文件进行阐述;
ELF文件由ELF文件头作为开始部分,ELF文件头会引出程序头表(Program Header)和节头表(Section Header)的偏移和大小;

程序头表 是一个数组,每一个元素称为程序头,而每个程序头描述了一个”段”或者用于准备执行程序的信息,而目标文件中的”段”包含一个或者若干个”节”(这里的节指的是节头表里面的单个元素);

节头表本身也是一个数组 ,每个元素称为一个”节”,每个节都指向ELF文件里的一个”节”(也称为”段”,比如.text段,.hash段等);而程序头表和节头表这两个表又会引出ELF文件中的其他数据;

ELF文件头

首先看一看ELF文件头结构:

#define EI_NIDENT 16
typedef struct{
unsigned char e_ident{EI_NIDENT};
unit 16_t e_type;
unit16_t e_machine;
unit32_t e_version;
ELFN_Addr e_entry;
ELFN_Off e_phoff;
ELFN_Off e_shoff;
unit32_t e_flags;
unit16_t e_ehsize;
unit16_t e_phentsize;
unit16_t e_phnum;
unit16_t e_shentsize;
unit16_t e_shnum;
unit16_t e_shstrndx;
}ELFN_Ehdr;

e_ident{EI_NIDENT}:一共16个字节,前4个字节是魔术字节(类似于PE文件中的”PE”):固定为0x7f,0x45,0x4c,0x46(与”.ELF”对应);

第5个至第9个字节分别表示EI_CLASS(0表示非法目标文件,01表示32位程序,02表示64位程序),EI_DATA(字节序是大端还是小端,0表示非法编码格式,1表示小端,2表示大端),EI_VERSION(表示文件版本号,一般为1,表示当前版本),EI_OSABI(osabi,和操作系统有关),EI_ABIVERSION(OSABI版本),第十字节和第十六字节都是填充字节,一般都是0;

图1

图2
e_type: 代表该文件类型,elf文件有4种类型: (0:表示未知文件类型)

  • 1.表示ET_REL—>可重定位文件(这里不要与PE中的重定位表混淆!这里的可重定位,这里的可重定位文件包含代码和数据,用来链接成可执行文件或共享目标文件,静态链接库就是可重定位文件对应于linux的.o,windows下的.obj)
  • 2.表示可执行文件ET_EXEC:Linux下可执行文件一般没有后缀名(例如/bin/bash),Windows下一般为.exe文件
  • 3.表示共享目标库文件(动态链接库文件),这种文件包含代码和数据,链接器可以使用这种文件跟其他可重定位文件的共享目标文件链接,产生新的目标文件;动态链接器可以将这种共享目标文件与可执行文件结合,作为进程映像来运行,对应于Linux里面的.so文件,Windows下的.dll文件
  • 4.表示核心转储文件(ET_CORE):Core Dump File,当进程意外终止,系统可以将该进程地址空间的内容及终止时的一些信息转存到核心转储文件。 对应 Linux 下的core dump。
    可以知道:图1里面的文件类型为可执行文件,图2里面的文件类型为.so文件

    图3
    e_machine:代表着该文件所需要的架构,EM_86_64代表这是AMD x86-64上运行的elf文件;EM_860(用7表示)表示这是在Intel80860上面运行的elf文件;(这里一般为固定值)

e_version:代表文件的版本(0:EV_NONE表示非法版本号,1:EV_CURRENT表示当前版本号);

e_entry:代表程序入口的虚拟地址;当文件被加载至进程空间里后,入口程序在进程地址空间里的地址;对于可执行程序文件来说,当ELF文件完成加载之后,程序将在这里开始运行;而对于其他文件来说,此值为0;(其实就是elf头的偏移)

e_phoff:表示的是程序头表(program_header table)开始处在文件中的偏移量,如果没有程序头表,该值设为0;

e_shoff:表示的是节头表的偏移;

e_flags:目前还没定义;

e_ehsize:表示的是elf头的字节数大小;

e_phentsize:单个程序头的字节数;

e_phnum: 程序头的数目;

e_shentsize:单个节头表的字节数;

e_phnum:节的数目

e_shstrndx:字节字符串所在节 在节头中的下标!
为了便于理解ELF文件里面几个组成部分的关系,下面借用非虫老师的图对ELF头部分进行总结:

图4

图5

图中:
e_type值为0x00 02
e_machine值为:0x00 28
e_version值为:0x00 00 00 01
e_entry值为:0x00 00 83 00(代表程序入口的虚拟地址;当文件被加载至进程空间里后,入口程序在进程地址空间里的地址)
e_phoff值为0x00 00 00 34,表示的是程序头表(program_header table)开始处在文件中的偏移量,如果没有程序头表,该值设为0;(在图4中可以得到验证)

e_shoff:表示的是节头表在文件中的偏移(见图5,这里为0x00 00 05 0C)
e_flags:暂时未定;
e_ehsize:ox34(表示的是elf头的字节数大小)
e_phentsize:单个程序头的字节数;(这里是0x20)
e_phnum: 程序头的数目;(这里是0x06)
e_shentsize:单个节头表的字节数;(这里是0x28)
e_phnum:节的数目(这里是0x15);(见图5)
e_shstrndx:字节字符串所在节 在节头表中的下标!(这里为0x12,即:在节头表里面的第0x12项所对应的是.shstrtab表项!)

程序头表

32位:

typedef struct{

Elf32_Word p_type;

Elf32_Off p_offset;

Elf32_Addr p_vaddr;

Elf32_Addr p_paddr;

Elf32_Word p_filesz;

Elf32_Word p_memsz;

Elf32_Word p_flags;

Elf32_Word p_align;

}Elf32_Phdr;
64位:

typedef struct{

Elf32_Word p_type;

Elf32_Word p_flags;

Elf32_Off p_offset;

Elf32_Addr p_vaddr;

Elf32_Addr p_paddr;

Elf32_Word p_filesz;

Elf32_Word p_memsz;

Elf32_Word p_align;

}Elf64_Phdr; 

一个可执行文件或者共享目标文件的程序头表是一个数组,数组中的每一个元素称为”程序头”,每一个程序头描述了一个”段”或者一块用于准备执行程序的信息;

一个目标文件中的”段”包含一个或者多个”节”;程序头只对可执行文件 或者共享目标文件有意义,对于其他类型的目标文件,该信息可以忽略;在目标文件的文件头(elf_header)里面,e_phentsize和e_phnum成员指定了程序头的大小;
p_type:

此数据成员说明了本程序头所描述的段的类型或者如何解析本程序头的信息:

PT_NULL:0,此类型表明程序头是未使用的,本程序头内的其他成员值均无意义;具有此种类型的程序头应该被忽略;

PT_LOAD(0x1),代表这段是要加载或映射至内存里面的;对一个动态链接的可执行ELF文件,一般都会包含两个这样的段:

1.程序的代码段
2.全局变量数据段和动态链接信息

此类型表明本程序头指向一个可以装载的段;段的内容会被从文件中拷贝至内存里面,段在文件中大小是p_filesz,在内存中大小为p_memsz;如果p_memsz大于p_filesz,在内存中多出的空间应该用0填补(段在内存中可以比在文件中占用空间大),p_filesz永远不应该比p_memsz大,如果这样的话,段在内存里面将无法完整地映射段的内容,所有PT_LOAD类型的程序头按照p_vaddr的值做程序排列;

PT_DYNAMIC(0x2):代表这部分保存着动态链接信息,例如程序运行时所需要的共享库的列表

PT_INTERP(0x3),该部分是指向解析器路径的ascii字符串,以0x0结尾,这个字符串是ELF解析器的路径,这种段类型只对可执行文件有意义,当他出现在共享文件中的时候,无意义;在一个ELF文件中,它最多出现一次,而且其必须在其他可装载的表项之前

PT_NOTE(0x4),辅助信息,本段指向一个以”null”结尾的字符串,这个字符串包含一些附加的信息

PT_PHDR(0x6),程序头本身这一块.此类型的程序头如果存在的话,它表明的是其自身所在的程序头表在文件中或内存中的位置或者大小,这样的段在文件中可以不存在,只有当所在程序头表所覆盖的段只是整个程序的一部分,才会出现这种表,并且这种表项的出现在其他可装载的表项之前.

p_flags:代表本段属性,其是三个属性PF_X(0x01),PF_W(0x02),PF_R(0x04)的组合,代码段一般都是PF_X和PF_R的组合 :

  • 可读与可执行是通用的,有其中一个就等于也有了另一个;
  • 可写权限是最高权限,可以覆盖另外两个,有了可写权限,所有权限就都有了;
    p_offset:此数据给出本段内容的开始位置相对于文件开头的偏移量;

p_vaddr:此数据成员给出本段内容的开始位置在进程空间中的虚拟地址;

p_paddr:此数据成员给出本段内容的开始位置在进程空间中的物理位置;对于目前大多数现代操作系统而言,应用程序中段的物理地址事先是不可知的,所以目前这个成员多数情况下保留不用,或者被操作系统改作它用 ;

p_filesz:此数据成员给出本段内容在文件中的大小,单位是字节,可以是0

p_memsz:此数据成员给出本段内容在内存镜像中的大小,单位是字节,可以是0

p_align:对于可装载的段来说,其 p_vaddr 和 p_offset 的值至少要向内存页面大小对齐。此数据成员指明本段内容如何在内存和文件中对齐。如果该值为 0 或 1,表明没有对齐要求;否则, p_align 应该是一个正整数,并且是 2 的幂次数。 p_vaddr 和p_offset 在对 p_align 取模后应该相等 ;

下面以一个实例对程序头进行说明:

这里以第一个程序头(总共6个)为例:
p_type:0x00 00 00 06,表示程序头本身这一块(就是这6个程序头所组成的一个区域).此类型的程序头如果存在的话,它表明的是其自身所在的程序头表在文件中或内存中的位置或者大小,这样的段在文件中可以不存在,只有当所在程序头表所覆盖的段只是整个程序的一部分,才会出现这种表,并且这种表项的出现在其他可装载的表项之前.
p_offset:指的是此数据给出本段内容的开始位置相对于文件开头的偏移量(这里为0x00 00 00 34) ;
p_vaddr:此数据成员给出本段内容的开始位置在进程空间中的虚拟地址(这里为0x00 00 80 34) ;
p_paddr:此数据成员给出本段内容的开始位置在进程空间中的物理位置,这里为0x00 00 80 34 ;
p_filesz:此数据成员给出本段内容在文件中的大小,单位是字节,可以是0(这里为0x00 00 00 C0(十进制为192 = (0x20)32_6)) ;
p_memsz:此数据成员给出本段内容在内存镜像中的大小,单位是字节,可以是0(这里为0x00 00 00 C0(十进制为192 = (0x20)32_6)) ;
p_flags:代表本段属性,这里为0x00 00 00 05(代表有可读可写可执行属性) ;
p_align:对于可装载的段来说,其 p_vaddrp_offset 的值至少要向内存页面大小对齐。此数据成员指明本段内容如何在内存和文件中对齐。如果该值为 0 或 1,表明没有对齐要求;否则, p_align 应该是一个正整数,并且是2的幂次数;p_vaddrp_offset 在对 p_align取模后应该相等(这里为0x00 00 00 04).

节头表

与Progarm Header类似,我们同样可以从ELF Header中得到索引地址(e_shoff)节区数量(e_shnum)表项大小(e_shentsize),还可以由名称节区索引(e_shstrndx)得到各节区的名称;

typedef struct{
uint32_t sh_name;
uint32_t sh_type;
uint64_t sh_flags;
Elf64_Addr sh_addr;
Elf64_Off sh_offset;
uint64_t sh_size;
uint32_t sh_link;
uint32_t sh_info;
uint64_t sh_addralign;
uint64_t sh_entsize;
}Elf64_Shdr;
typedef struct{
Elf32_Word sh_name;
Elf32_Word sh_type;
Elf32_Word sh_flags;
Elf32_Addr sh_addr;
Elf32_Off sh_offset;
Elf32_Word sh_size;
Elf32_Word sh_link;
Elf32_Word sh_info;
Elf32_Word sh_addralign;
Elf32_Word sh_entsize;
}Elf32_Shdr;
  • sh_name:节区名称,是节区头部字符串表节区的索引;名字是一个NULL结尾的字符串.

  • sh_type:节区类型:

sh_flags: 同Program Header的p_flags;

sh_addr:节区索引地址;

sh_offset:节区相对于文件的偏移地址;
sh_size :节区的大小;
sh_link :此成员给出节区头部表索引链接;

sh_info :此成员给出附加信息 ;
根据节区类型的不同,sh_link 和 sh_info 的具体含义也有所不同:

sh_addralign :
某些节区带有地址对齐约束。例如,如果一个节区保存一个doubleword,那么系统必须保证整个节区能够按双字对齐。sh_addr 对sh_addralign 取模,结果必须为 0。目前仅允许取值为0和2的幂次数;数值0和1表示节区没有对齐约束.

sh_entsize :
某些节区中包含固定大小的项目,如符号表。对于这类节区,此成员给出每个表项的长度字节数.如果节区中并不包含固定长度表项的表格,此成员取值为0;

一般来说,节区索引为0,即第一个节区一般都是SHN_UNDEF,其各项值都固定为0;

重点:
1. 以“.”开头的节区名称是系统保留的。应用程序可以使用没有前缀的节区名称,以避免与系统节区冲突。

2.目标文件中也可以包含多个名字相同的节区。

3.保留给处理器体系结构的节区名称一般构成为:处理器体系结构名称简写 + 节区名称。

4.处理器名称应该与 e_machine 中使用的名称相同。例如 .FOO.psect 节区是由 FOO 体系结构定义的 psect 节区。

仍然以非虫老师的那张图为例对节头表进行说明:

这里以第二个节区为例:
sh_name:节区名称,是节区头部字符串表节区的索引,这里的值为0x001B,可以在.shstrtab中找到此节区名称为.interp

sh_type:节区类型:0x01;
sh_addr 节区索引地址:0x000080F4;
sh_offset 节区相对于文件的偏移地址:0x00F4;
sh_size 节区的大小:0x13,表示.interp这个节的大小为 0x13;
sh_addralign:0x01,表示此节区没有对齐约束!
sh_entsize:0x00,表示此节区中并不包含固定长度表项的表格.

实例

前面介绍了ELF文件格式,下面手动实践验证一下(源代码见附录部分):
readelf -a part.o

截取 part.o 文件的开始部分,逐条解析以下信息的字节码真实面目:

下面逐一解释各字节内容,以[xxxx]的格式来表示地址,地址值都是 16 进制数,但为了表达简洁,这里把地址的前缀”0x”省略掉。
[00 ~ 0f]
这 16 字节为 e_ident 信息。
[00 ~ 03]
这 4 个字节为“魔数”,即’0x7f’, ’E’, ’L’, ’F’4 个字符。
[04]
文件类型,这里是 1,即”ELFCLASS32”,此文件是 32 位目标文件。
[05]
文件编码格式,这里是 1,即”ELFDATA2LSB”, LSB 编码,即小头编码。
[06]
文件版本,是 1。
[07 ~ 0f]
这一段字节无意义,被 0 填充。
[10 ~ 11]
e_type,即 ELF 文件的种类。这里要注意一下, e_type 为 Elf_32_Half 类
型,占两字节,虽然字节表示为”0100”,但因为本文件是 LSB 编码,即低字节在
前,所以逻辑上实际的值为”0001”,即”ET_REL”,是可重定位文件。
[12 ~ 13]
e_machine,即处理器体系结构。这里是 3,即 EM_386, Intel 架构。
[14 ~ 17]
e_version,这里是 1。

[18 ~ 1b]
e_entry,程序入口的虚拟地址,这里是 0,因为想文件并非可执行文件。
[1c ~ 1f]
e_phoff,程序头表在文件中的偏移量,本文件没有程序头表,所以为 0。
[20 ~ 23]
e_shoff,节头表在文件中的偏移量,这里为”0x0300”。
[24 ~ 27]
e_flags,属性标志,对于 Intel 架构,这 4 个字节为 0。
[28 ~ 29]
e_ehsize, ELF 文件头大小,共 0x34 个字节

[2a ~ 2b]
e_phentsize,程序头表中表项的大小。由于本文件中没有程序头表,所以为
0。
[2c ~ 2d]
e_phnum,程序头表中的项数。由于本文件中没有程序头表,所以为 0。
[2e ~ 2f]
e_shentsize,节头表中表项的大小。这里为 0x28。
[30 ~ 31]
e_shnum,节头表中的表项数。这里为 0xe,即 14 项。
[32 ~ 33]
e_shstrndx,节头字符串表索引,此处为 9。

节头表

从上面 ELF 文件头信息得知, part.o 文件中存在一个节头表,其起始位置在
0x0300 (e_shoff)处,节头表中总共有 0x0e(e_shnum)项;每一项大小为0x28,可以计算出节头表的大小为0x0e*0x28=0x0230,节
头表的位置区域为[0300 ~ 0530]。下面把这一段拷贝出来详细分析。

节头表信息可以通过linux里面的相关命令readelf -a part.o来获取:

这里以节头字符串表,字符串表,代码节,符号表这4个典型的节为例进行解释;

节头字符串表

此表提供了节头字符串表(.shstrtab)的信息,该表存储了所有节的名字,节头字符串表可以由上面的节头表信息可以查到它的偏移,其位 置在[0128 ~ 018b],截图如下:

sh_name:值为0x11,这个值是节头字符串表的索引,在上图(图中加黄线的部分)里面可以看到,第0x11个字节开始的就是shstrtab这个节的字符名称;

字符串表


此表项提供了字符串节(.strtab)的信息。在 节头表里面以绿色标出,里面主要放的是变量名和一些函数名;
sh_name,值为0x09,本节的名字,此值为节 头 字符串表的索引;
sh_type,值为0x03,即SHT_STRTAB,表明此表 项指向一个字符串表;这里不关心;
sh_addr,本节内容映射到进程空间里面的而起始地址,由于无重定位,所以此值为0;
sh_offset:本节在文件里面的偏移量,这里为0x028c;
sh_size,本节的大小(这里为0x39),即字符串表的长度;

小结

表的偏移可以通过节表信息里面可以获取,亦可以通过这个表本身里面的sh_offset可以获得; 名字可以通过sh_name这个索引在节头字符串表里面找到;这个表 里面存放的是变量和一些自定义的函数的名字;

代码节

这个表项里面提供了代码节(.text)的信息,就是这些函数的机器码,在节头表里面已经用紫红色做了标记,不过对于代码段我们只关心它的地址和长度;
sh_addr,本节内容映射到进程空间的起始位置;
sh_offset,本节在文件中的偏移量,值是0x34;
sh_size:本节在文件中的大小,这里为0x3e;

这是一段机器码 ,可以通过以下这个命令可以了解:

objdump -d part.o


part.o中有 两个函数的代码,它们的位置是相连的,总长度加在一起为0x3e,如果细心地去把每一个机器码与目标文件中的[034 ~ 072]这段字节相比较的话,发现它们是完全相同的。 .text 节中正是存储了这两个函数的机器码;

符号表

符号表(.symtab):符号表.symtab的表头在节表中的位置以天蓝色标出;
sh_size,符号表的大小为0x0100;

sh_entsize,值为0x10;即每个符号表项的大小为0x010;
符号表总大小为0x100,每一个表项的大小为0x010;故符号表总共包含16项;

上面这张 表里面包含了我们在程序里面定义的全局变量和函数,这里做个解释 :
先看 Ndx这一列,它所表示的是此符号与哪一个节相关联,这里可以理解成它定义在哪一节中。 g_int2 和g_str2是初始化过的全局变量,所以它们出现在.data节中,回看节头 表信息那张图,.data节的序号正是 3。而g_int1和 g_str1,它们也是全局变量,但是没有初始化,所以它们并不属于.data,在逻辑上它们是属于.bss,而.bss节在本目标文件中并没有被分配空间,根据tis发布的elf 制定标准书对 st_shndx 的描述,它们的st_shndx值都为 SHN_COMMON。 func_1 和 func_2 是函数,它们当然应该属于.text 节,即 st_shndx为1。
再来看 Value 这一列。 g_int1 和 g_str1 因为 st_shndx 值都为 SHN_COMMON,所以它们的 st_value 值为字节对齐数。而 g_int2 和 g_str2 的 value 值是它们在所定义的节中,即.data 节中的偏移量。.data节中只有这两个数据,所以它们的偏移量分别为 0 和 4。 func_1 和 func_2 是函数,它们在.text 节中的起始位置分别为 0x00 和 0x022,这在前面已经看过。
Size 数据更容易理解,整型数和指针的大小均为 4 字节,而函数的大小为其机器码的长度(注:34和28分别是以十进制数表示);
对于 printf 这个符号,它是库中的函数,定义并不在本目标文件中。它是一个待重定位的符号,所以它的位置和大小信息都是 0。

“段”存在于可执行文件和共享目标文件,其实就是程序头表所对应的内容;
一个段由若干个节组成。下面看一下libpart.somain_d 中的段,仍然看 readelf 的输出:

readelf -a libpart.so


图(a)

readelf -a main_d


图(b)
比较上面两图可以看出,在两个文件中各有两个类型为”LOAD”的可装载段(图(a)里面指的是00和01这两个segment,图(b)里面指的是02和03这两个segment),包含有较多的节,其中包含 有.text节的段就是我们所说的”代码段”,另一个含有.data的段就是”数据段”。
有一个需要注意的地方,在可执行文件里面,有”INTERP”段,而共享目标文件里面没有,这一段里面存储了ELF解析器的路径名,也就是动态链接器;由于动态链接器只有在程序准备运行的时候才起作用,故只有可执行文件才有;在其他文件里面没有

小结

只有.so文件和可执行文件才有程序头表;
只有可执行文件才有”interp”段,这一段里面存储了ELF解析器的路径名.

动态节

动态节是专门用于动态连接过程的节,所以它只会出现在经过连接的目标文件里面;在本例里面,*.o 文件里面不会有动态节,而在libpart.so和main_s以及main_d皆有;

动态节是用于在不同的目标文件之间连接符号用的;在本示例里面,libpart.so(类似于windows系统下的dll文件)是”导出”符号的一方,而main_d是”导入”符号的一方(main_d类似于windows系统下的exe文件)

图(c)libpart.so的动态符号表

图(d)main_d的动态符号表
main_d 中,func_1和func_2两个符号的Ndx均为UND,并且值为0,表明它们尚未被重定位,在这里还是未知的符号。由于main_d是通过动态连接构建出来的,它的运行依赖于libpart.so,所以,对于 func_1 和 func_2 的重定位将在运行期间发生。
相应地,在 libpart.so 中,func_1和func_2这两个符号也出现在.dynsym节中,因为它们是被导出的符号。在这里,func_1 和 func_2 的 Ndx 和 Value 值都是有意义的值,而且非零,说明它们正是定义在本目标文件里。而在 part.c 里调用的printf 函数,因为是其它库里的,所以对于 libpart.so 来说, printf 也是导入符号,它的值也为 0,在这里是未定义的符号。

总结

这篇文章主要梳理了ELF文件中主要数据之间的逻辑关系以及比较重要的几个节表的内容,可以由下面这幅图表示:

整个ELF文件以ELF文件头开始,然后由ELF文件头引出程序头表(只有.so文件和可执行文件才有 )和节头表,程序头表可以引出各段,节头表可以引出ELF文件里的各节;由于篇幅限制,还有一些有趣的内容这里没有写,比如ELF文件的装载链接过程,同时,由于笔者的水平有限,文章中难免会出现一些错误或者问题,还请指正!

附录

main.c:

#include "part.h"
extern int g_int1,g_int2;
extern char*g_str1,*g_str2;

int  main(){
int i = 0;
char *str = "abc";

func_1(i);
func_2(str);

g_int1 = 9;
g_str1 = "defg";
printf("Global integer is %d and %d,global string is %s and %s.\n",g_int1,g_int2,g_str1,g_str2);

return i;



}

part.c:

#include "part.h"

int g_int1,g_int2=5;
char *g_str1,*g_str2="xyz";

void  func_1(int i){
int j;

j = i;
printf("func_1:j = %d\n",j);

}

void func_2(char *str){
printf("func_2: str = %s\n",str);

part.h:


\#ifndef _PART_H_ #define _PART_H_ void func_1(int i); void func_2(char *str); #endif

Makefile:

all: main_s main_d
main_s: main.o part.o
    gcc main.o part.o -o main_s
main_d: main.o libpart.so
    gcc main.o -L. -lpart -o main_d
libpart.so: part.o
    gcc --shared part.o -o libpart.so
main.o: main.c
    gcc -c main.c -o main.o
part.o: part.c
    gcc -c part.c -o part.o
clean:
    rm -f *.o *.so main_s main_d

参考文献

1.Tool Interface Standard (TIS) Executable and Linking Format (ELF) Specification Version 1.2
2.Android ELF 文件格式(非虫)

作者:斗象能力中心TCC-richard.hu

评论(0)

暂无评论

发表评论