程序员的自我修养之目标文件

1. 什么是目标文件?
目标文件是经过预处理,编译,汇编后生成的文件。
目标文件经过链接后变为可执行文件。
目标文件与可执行文件格式几乎一模一样,linux 中统称为 ELF 文件。
在这里插入图片描述
在这里插入图片描述
2. 目标文件是什么样的呢?
|.text段| 执行语句编译生成的机器代码 |
|.data段|已初始化的全局变量和局部静态变量|
| .bss |未初始化的全局变量和局部静态变量 |

3. 目标文件这样分段存放有什么好处呢 ?
第一, 不同的段有不同的权限,比如 .data 段是可读写的, .text段是只读的,可以防止程序被无意修改。
第二,CPU 缓存被设计为数据缓存与指令缓存分离,所以程序的数据与指令分离可提高CPU的命中率,提高效率。
第三,省内存,当同一个程序被运行多次时,代码段都是一样的,只会被装载一次。

4. 题外话:如何通过一段程序确定一台机器是大端还是小端?
也许你会想到下面这段小程序:

    int main()
    {
        union
        { 
            int var1;
            char var2;
        } aa;
        aa.var1 = 0x00000001;
        if (aa.var2 == 1)
        {
            printf("this mechine is small endian");
        }
        else if (aa.var2 == 0)
        {
            printf("this mechine is big endian");
        }
        return 0;
    }

这个是OK的,但其实 还有一种方法是你可以随便写个小程序,比如 helloworld。

#include<stdio.h>
int main()
{
  printf("hello world\r\n");
  return 0;
}

然后编译:gcc -c hello.c -o hello.o 产生目标文件。
readelf hello.o -h:输出信息中会包含机器大小端。
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2’s complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: REL (Relocatable file)
Machine: Advanced Micro Devices X86-64

5. 目标文件中的重定位表
使用objdump 指令查看一下编译 程序生成的目标文件:

#include <stdio.h>
int global_int_var = 10;
int global_uninit;
void callfunc()
{
  printf("hello world");
}
int main()
{
  static int a = 2;
  static int b;

  callfunc();
  return 0;
}

编译生成目标文件:gcc -c test.c -o test.o
读取目标文件各个段信息:readelf -S test.o
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .text PROGBITS 0000000000000000 00000040
000000000000002a 0000000000000000 AX 0 0 1
[ 2] .rela.text RELA 0000000000000000 000002d0
0000000000000048 0000000000000018 I 10 1 8
[ 3] .data PROGBITS 0000000000000000 0000006c
0000000000000008 0000000000000000 WA 0 0 4
[ 4] .bss NOBITS 0000000000000000 00000074
0000000000000004 0000000000000000 WA 0 0 4
[ 5] .rodata PROGBITS 0000000000000000 00000074
000000000000000c 0000000000000000 A 0 0 1
[ 6] .comment PROGBITS 0000000000000000 00000080
000000000000002e 0000000000000001 MS 0 0 1
[ 7] .note.GNU-stack PROGBITS 0000000000000000 000000ae
0000000000000000 0000000000000000 0 0 1
[ 8] .eh_frame PROGBITS 0000000000000000 000000b0
0000000000000058 0000000000000000 A 0 0 8
[ 9] .rela.eh_frame RELA 0000000000000000 00000318
0000000000000030 0000000000000018 I 10 8 8
[10] .symtab SYMTAB 0000000000000000 00000108
0000000000000180 0000000000000018 11 11 8
[11] .strtab STRTAB 0000000000000000 00000288
0000000000000048 0000000000000000 0 0 1
[12] .shstrtab STRTAB 0000000000000000 00000348
0000000000000061 0000000000000000 0 0 1
.rela.text: type 是 RELA, 是一个重定位表。所有的重定位信息都存储在重定位表中,而且, rela.text是针对 text段的重定位表,helloworld 中 printf()函数需要重定位。
如果有全局变量,或者从其他模块中 extern 全局变量,会有相应的 .rela.data 段。
链接过程的核心任务是重定位,而重定位的目标就是 存储在 重定位表中的object。

  • 如何确定源代码中各个符号处在目标文件的哪一段呢?
    使用指令 readelf -s hello.o
    Num: Value Size Type Bind Vis Ndx Name
    0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
    1: 0000000000000000 0 FILE LOCAL DEFAULT ABS test.c
    2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
    3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
    4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
    5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
    6: 0000000000000000 4 OBJECT LOCAL DEFAULT 4 b.2183
    7: 0000000000000004 4 OBJECT LOCAL DEFAULT 3 a.2182
    8: 0000000000000000 0 SECTION LOCAL DEFAULT 7
    9: 0000000000000000 0 SECTION LOCAL DEFAULT 8
    10: 0000000000000000 0 SECTION LOCAL DEFAULT 6
    11: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 global_int_var
    12: 0000000000000004 4 OBJECT GLOBAL DEFAULT COM global_uninit
    13: 0000000000000000 21 FUNC GLOBAL DEFAULT 1 callfunc
    14: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND printf
    15: 0000000000000015 21 FUNC GLOBAL DEFAULT 1 main
    解读:

  • ABS 这一列标示 后面跟的符号所在的 session。例如: main ABS =1.
    在 “readelf -S test.o” show 出的结果看, .text 段的 下标是1. .data段的下标是 3.
    所以,main 在 .text段, global_int_var 在 .data 段。

  • printf 因为是外部引用函数,所以 ABS= UND,需要 在链接过程中重定位。

  • global_uninit ABS=COM, 标示 global_uninnit 是弱符号的。未初始化的变量都是弱符号,弱符号允许同一个变量被声明多次,且类型不同,最终选择占用空间大的符号。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值