自我修养——目标文件.o有什么

自我修养——编译和链接 来源于 程序员的自我修改-链接.装载.库 一书 ,又看了一章~

撰写不易,转载需注明出处:http://blog.csdn.net/jscese/article/details/50161675本文来自 【jscese】的博客!


文件类型

executable
windows: PE(Portable executable)
linux: EIF(Executable Linkable Format)


Relocatable file : linux:.o ; windows:.obj

Executable file : linux:/bin/xxx ; windows:.exe

Share Object file : linux:.so ; windows:.dll

从编译流程上来说,最终一个或多个relocatable file 经过link 打包成为executable file ,也就是linux 下的EIF

一般指的目标文件,在linux上来说 即是编译完成得到的 .o


.o file construction

首先以一个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;   
}

先不纠结 为什么这么写,编译为.o

gcc -c simplesection.c

同目录下得到 simplesection.o

查看simplesection.o 的组成可用到两个工具 readelf objdump

.o中数据分 段-section 存放,可使用objdump查看段表如下:

jscese@:~/jscese_code/object_file_construct$ objdump -h simplesection.o

simplesection.o:     file format elf64-x86-64

Sections:
Idx Name          Size      VMA               LMA               File offset  Algn
  0 .text         00000050  0000000000000000  0000000000000000  00000040  2**2
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
  1 .data         00000008  0000000000000000  0000000000000000  00000090  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  2 .bss          00000004  0000000000000000  0000000000000000  00000098  2**2
                  ALLOC
  3 .rodata       00000004  0000000000000000  0000000000000000  00000098  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .comment      0000002b  0000000000000000  0000000000000000  0000009c  2**0
                  CONTENTS, READONLY
  5 .note.GNU-stack 00000000  0000000000000000  0000000000000000  000000c7  2**0
                  CONTENTS, READONLY
  6 .eh_frame     00000058  0000000000000000  0000000000000000  000000c8  2**3
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA

使用readelf 查看命令:readelf -S simplesection.o 格式有点不同而已 ,具体的段表信息含义可参考
\kernel\include\uapi\linux\elf.h 中定义的 Elf64_Shdr 结构体

这里可以看到的有6个section
可根据每一个的size 以及对应后面的 file offset 的大小,按照顺次排布在文件中,容易理解

其中内容分布规则如下:

编译的机器指令-代码段 —— .code or .text
已初始化 全局/局部静态变量 - 数据段 —— .data
未初始化 全局/局部静态变量 —— . bss
只读的数据内容 —— .rodata

特别注意的是CONTENTS代表是否有内容,.bss 很特殊的section,提前说明一下:

未初始 运行时开辟 默认都为0,bss作为记录未初始化全局以及局部静态所占大小的作用,
不真实存在于文件中的section,所以不占文件空间,在section table 段表中记录
【有的不在bss段预留全局未初始化变量,而是预留一个common 全局变量符号,看编译器】

这里有个小点,上面表信息中.text section大小为0x50 即为80个byte ,当使用size 命令查看simplesection.o时:

jscese@:~/jscese_code/object_file_construct$ size simplesection.o
   text    data     bss     dec     hex filename
    172       8       4     184      b8 simplesection.o

显示的.text 为172,原因如下:
size默认是运行在”Berkeley compatibility mode”下。在这种模式下,会将不可执行的拥有”ALLOC”属性的只读段归到.text段下,
很典型的就是.rodata段。如果你使用”size -A obj.o”,
那么size会运行在”System V compatibility mode”,此时,用objdump -h和size显示的.text段大小就差不多了。

jscese@:~/jscese_code/object_file_construct$ size -A  simplesection.o
simplesection.o  :
section           size   addr
.text               80      0
.data                8      0
.bss                 4      0
.rodata              4      0
.comment            43      0
.note.GNU-stack      0      0
.eh_frame           88      0
Total              227

这就对了!


划分section目的

分开存放 指令区域 数据区域 目的:
1:文件被装载执行, 指令 数据分别映射到两个虚拟区域,指令对应的区域设置为只读,数据区域设置为可读写,保证指令被恶心修改

2:cache 命中率问题,分开存放 ,提升CPU取指命中率

3:程序多副本运行时的 指令部分只需存在一份即可,只读共享,节省内存,同时一些只读的数据部分也是可以共享,存单一副本


查看section

查看.o 中的 代码段 .text section 内容,使用 objdump -s 查看十六机制,-d 反汇编为汇编代码:

jscese@:~/jscese_code/object_file_construct$ objdump -s 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 10c745f8 01000000  .UH..H....E.....
 0030 8b150000 00008b05 00000000 01d00345  ...............E
 0040 f80345fc 89c7e800 0000008b 45f8c9c3  ..E.........E...
Contents of section .data:
 0000 54000000 55000000                    T...U...        
Contents of section .rodata:
 0000 25640a00                             %d..    
...

最左为偏移,这里跟之前section在文件中的偏移要区分,这里只是section中数据的偏移值
如上面在文件中.text段的file offset 为00000040 ,那么文件中00000041 位置的字节应为0x55 ,00000090位置的为0xc3
中间四列即为0x50 80个字节,最后一列为ASCALL

反汇编 : objdump -d simplesection.o

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 f8 01 00 00 00    movl   $0x1,-0x8(%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:   01 d0                   add    %edx,%eax
  3e:   03 45 f8                add    -0x8(%rbp),%eax
  41:   03 45 fc                add    -0x4(%rbp),%eax
  44:   89 c7                   mov    %eax,%edi
  46:   e8 00 00 00 00          callq  4b <main+0x2a>
  4b:   8b 45 f8                mov    -0x8(%rbp),%eax
  4e:   c9                      leaveq 
  4f:   c3                      retq   

数据与汇编指令的对应关系,详情可去查汇编指令与对应机器码


自定义section的分配

  static int itest1=0;
  static int itest2=1;

itest1存.bss段 itest2 存.data段 ,
因为未初始化的变量默认即为0 ,在bss段预留位置,在文件中不占空间,优化将itest1放在bss段
查看符号表:

   jscese@:~/jscese_code/object_file_construct$ objdump -t simplesection.o

simplesection.o:     file format elf64-x86-64

SYMBOL TABLE:
0000000000000000 l    df *ABS*  0000000000000000 simplesection.c
0000000000000000 l    d  .text  0000000000000000 .text
0000000000000000 l    d  .data  0000000000000000 .data
0000000000000000 l    d  .bss   0000000000000000 .bss
0000000000000000 l    d  .rodata        0000000000000000 .rodata
0000000000000004 l     O .data  0000000000000004 static_var.1594
0000000000000000 l     O .bss   0000000000000004 static_var2.1595
0000000000000000 l    d  .note.GNU-stack        0000000000000000 .note.GNU-stack
0000000000000000 l    d  .eh_frame      0000000000000000 .eh_frame
0000000000000000 l    d  .comment       0000000000000000 .comment
0000000000000000 g     O .data  0000000000000004 global_init_var
0000000000000004       O *COM*  0000000000000004 global_uninit_var
0000000000000000 g     F .text  0000000000000021 func1
0000000000000000         *UND*  0000000000000000 printf
0000000000000021 g     F .text  000000000000002f main

可以看到 global_uninit_var 并没有存在.bss 中 ,.bss中只有static_var2 ,
这也是上面的section table中.bss只有4byte的原因,细心的应该注意到上面提到过特殊的编译器,不会把未定义全局变量放到.bss ,而是预留了一个COMMON的符号,这里就是了:
0000000000000004 O COM 0000000000000004 global_uninit_var

要是考虑一些地址布局等问题,我们可以自定义某个变量存放到某个段,gcc提供了这样的机制:

  __attribute__((section("name"))) //修饰

修改上面simplesection.c ,把初始化的全局变量 放到.bss段里面去:

 __attribute__((section(".bss"))) int global_init_var =84;

再次看下符号表:

SYMBOL TABLE:
0000000000000000 l    df *ABS*  0000000000000000 simplesection.c
0000000000000000 l    d  .text  0000000000000000 .text
0000000000000000 l    d  .data  0000000000000000 .data
0000000000000000 l    d  .bss   0000000000000000 .bss
0000000000000000 l    d  .rodata        0000000000000000 .rodata
0000000000000000 l     O .data  0000000000000004 static_var.1594
0000000000000004 l     O .bss   0000000000000004 static_var2.1595
0000000000000000 l    d  .note.GNU-stack        0000000000000000 .note.GNU-stack
0000000000000000 l    d  .eh_frame      0000000000000000 .eh_frame
0000000000000000 l    d  .comment       0000000000000000 .comment
0000000000000000 g     O .bss   0000000000000004 global_init_var
0000000000000004       O *COM*  0000000000000004 global_uninit_var
0000000000000000 g     F .text  0000000000000021 func1
0000000000000000         *UND*  0000000000000000 printf
0000000000000021 g     F .text  000000000000002f main

可以看到从.data 到了 .bss


讲过上面一系列的分析,应该对整个.o 文件有个整体的认识了,最后补一张原书中的结构图:
这里写图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值