读书笔记——《程序员自我修养》——目标文件

    PC机上流行的可执行文件格式主要是windows下的PE(portable Executable)和Linux的ELF(Executable Linkable Format),它们都是COFF(Common file format)格式的变种。
不光是可执行文件,动态链接库以及静态链接库文件都是按照可执行文件格式存储的。

使用file命令来查看相应的格式:



SimpleSection.c

/*
*SimpleSection.c
*Linux 
*	gcc -c SimpleSection.c
*/
int printf(const char *format, ...);

int global_init_var = 84;
int global_uninit_var;

void func1(int i)
{
    printf("%d\n", i);
}

int main(void)
{
    static int static_var = 85;
    static int static_var2;
    
    int a = 1;
    int b;
    func1(static_var + static_var2 + a + b);
    return a;
}
使用GCC编译,参数-c 表示只编译不链接

可以使用objdump来查看文件结构,-h 可以打印出基本信息,-x会输出复杂信息

看一下文件的大小

画出基本的ELF结构:

一个专门的命令"size",它可以用来查看ELF文件的代码段、数据段和BSS段的长度,des表示十进制的和,hex表示十六进制的和

代码段
objdump的"-s"参数可以将所有端的内容以十六进制的方式打印出来,"-d"参数可以将所有包含指令的段反汇编。
root@mdd-desktop:/media/mdd0/slzsource/ddu/LinkLoader# objdump -s -d SimpleSection.o

SimpleSection.o:     file format elf64-x86-64

Contents of section .text:
 0000 554889e5 4883ec10 897dfc8b 45fc89c6  UH..H....}..E...
 0010 bf000000 00b80000 0000e800 000000c9  ................
 0020 c3554889 e54883ec 10c745fc 01000000  .UH..H....E.....
 0030 8b150000 00008b05 00000000 8d040203  ................
 0040 45fc0345 f889c7e8 00000000 8b45fcc9  E..E.........E..
 0050 c3                                   .               
Contents of section .data:
 0000 54000000 55000000                    T...U...        
Contents of section .rodata:
 0000 25640a00                             %d..            
Contents of section .comment:
 0000 00474343 3a202855 62756e74 7520342e  .GCC: (Ubuntu 4.
 0010 342e332d 34756275 6e747535 2e312920  4.3-4ubuntu5.1) 
 0020 342e342e 3300                        4.4.3.          
Contents of section .eh_frame:
 0000 14000000 00000000 017a5200 01781001  .........zR..x..
 0010 1b0c0708 90010000 18000000 1c000000  ................
 0020 00000000 21000000 00410e10 4386020d  ....!....A..C...
 0030 06000000 18000000 38000000 00000000  ........8.......
 0040 30000000 00410e10 4386020d 06000000  0....A..C.......

Disassembly of section .text:

0000000000000000 <func1>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   48 83 ec 10             sub    $0x10,%rsp
   8:   89 7d fc                mov    %edi,-0x4(%rbp)
   b:   8b 45 fc                mov    -0x4(%rbp),%eax
   e:   89 c6                   mov    %eax,%esi
  10:   bf 00 00 00 00          mov    $0x0,%edi
  15:   b8 00 00 00 00          mov    $0x0,%eax
  1a:   e8 00 00 00 00          callq  1f <func1+0x1f>
  1f:   c9                      leaveq 
  20:   c3                      retq   

0000000000000021 <main>:
  21:   55                      push   %rbp
  22:   48 89 e5                mov    %rsp,%rbp
  25:   48 83 ec 10             sub    $0x10,%rsp
  29:   c7 45 fc 01 00 00 00    movl   $0x1,-0x4(%rbp)
  30:   8b 15 00 00 00 00       mov    0x0(%rip),%edx        # 36 <main+0x15>
  36:   8b 05 00 00 00 00       mov    0x0(%rip),%eax        # 3c <main+0x1b>
  3c:   8d 04 02                lea    (%rdx,%rax,1),%eax
  3f:   03 45 fc                add    -0x4(%rbp),%eax
  42:   03 45 f8                add    -0x8(%rbp),%eax
  45:   89 c7                   mov    %eax,%edi
  47:   e8 00 00 00 00          callq  4c <main+0x2b>
  4c:   8b 45 fc                mov    -0x4(%rbp),%eax
  4f:   c9                      leaveq 
  50:   c3                      retq   

看一下代码段
Contents of section .text:
 0000 554889e5 4883ec10 897dfc8b 45fc89c6  UH..H....}..E...
 0010 bf000000 00b80000 0000e800 000000c9  ................
 0020 c3554889 e54883ec 10c745fc 01000000  .UH..H....E.....
 0030 8b150000 00008b05 00000000 8d040203  ................
 0040 45fc0345 f889c7e8 00000000 8b45fcc9  E..E.........E..
 0050 c3   
反汇编出来的代码正好对应着
开始  0:   55                      push   %rbp
结束  50:   c3                      retq  
可见.text段中存放的是代码的指令


数据段和只读数据段
.data段保存的是那些已经初始化了的全局静态变量和局部静态变量
int global_init_var = 84;
static int static_var = 85;
这两个变量每个4个字节,一共刚好8个字节,所以".data"这个段的大小为8个字节。

SimpleSection.c里面我们调用"printf"的时候,用到了一个字符串常量"%d\n"
,它是一种只读数据,所以被放到了".rodata"段,我们可以从输出结果看到".rodata"这个段的
4个字节刚好是这个字符串常量ASCII字节序,最后以\0结尾。

".rodata"段存放的是只读数据段,一般是程序里面的只读变量(如const修饰的变量)和字符串常量。

BSS段
.bss段存放的是未初始化的全局变量和局部静态变量,
如上诉代码中
int global_uninit_var;
static int static_var2;
这两个变量是存放在.bss段,但两个变量占内存大小只有4个字节,而实际是8个字节,这里实际上只是预留一个未定义的全局变量符号,
等到最终连接成可执行文件的时候再在.bss段分配空间。

ELF文件头
readelf命令查看ELF文件

0x7f为ASCII字符里面的DEL控制符,后面三个字节刚好是ELF这3个字母的ASCII码也就是0x45 0x4c 0x46。
接下来的一个字节是用来标示ELF文件类型的0x01标示32位的, 0x02表示64位的,第六个字节是字节序,规定
该ELF文件是大端的还是小端的

段表
ELF文件中有很多各种各样的段,这个段表(Section Header Table)就保存这些段的基本属性的结构。
readelf查看elf文件的段

.rela.text  重定位表(rRelocation Table)
链接器在处理目标文件时,须要对目标文件中某些部位进行重定位,即代码段和数据段中那些绝对地址引用的位置。这些重定位的信息都记录在ELF文件的重定位表里面,对于每个须要重定位的代码段或数据段,都会有一个相应的重定位表。比如SimpleSection.o中的".rela.text"就是针对".text"段的重定位表,因为".text"段中至少有一个绝对地址的引用,那就是"printf"函数调用。

.shstrtab字符串表

符号
目标文件的链接实际上是目标文件之间对地址的引用,即对函数和变量的地址的引用。
比如目标文件B要用到了目标文件A中的函数“foo”,那么我们就称目标文件A定义了函数"foo",称目标文件B引用了目标文件A中的函数"foo"。
在链接中,我们将函数和变量统称为“符号”,函数名或变量名就是符号名。
每一个目标文件都会有一个符号表"Symbol Value"


nm查看符号

readelf -s 查看符号


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值