程序员的自我修养:(1)目标文件

本文深入探讨了目标文件的编译与链接过程,包括预编译、编译、汇编和链接的详细步骤。文章介绍了目标文件的四种类型,特别是ELF文件结构,包括文件头、段表和段的概念,并提供了常用命令来查看目标文件的详细信息。通过对一个简单的示例代码的分析,揭示了ELF文件的内部结构,帮助读者更好地理解程序的底层工作原理。
摘要由CSDN通过智能技术生成

程序员的自我修养:(1)目标文件

1.目标文件

1.1 编译与链接

在使用像Visual Studio或Qt Creator等IDE时,通常有一个叫做“构建”的按钮。当编辑完成要运行和测试时点一下它,程序就能跑起来了,所以我们很少关心编译和链接。其实,编译和链接合并在一起就称为 构建(Build)。简单的一次按键,实际背后却是异常复杂的过程:

  • 预编译(Preprocessing)
  • 编译(Compilation)
    • 扫描:算法类似有限状态机(FSM),将字符转换成Token。
    • 语法分析:分析Token生成语法树(Syntax Tree)。
    • 语义分析:静态语义(类型转换)或动态语义分析。
    • 源代码优化:直接在语法树做优化比较困难,优化器将语法树转换成中间代码。中间代码使得编译器分为前端和后端,前端负责产生机器无关的中间代码,后端将中间代码转换成目标机器代码。
    • 代码生成:完全依赖目标机器(字长、寄存器、数据类型等),生成目标代码。
    • 目标代码优化:优化寻址方式、用位移代替乘法运算、删除多余的指令等。
  • 汇编(Assembly)
  • 链接(Linking)
    • 地址和空间分配(Address/Space Allocation)
    • 符号解析(Symbol Resolution)
    • 重定位(Relocation)

1.2 目标文件简介

Object File没有一个很合适的中文名称,书中翻译为目标文件。这一小节介绍有关目标文件的最重要的几个知识:文件类型、段的概念、常用的查看工具。

1.2.1 四种文件类型

目标文件就是源代码编译后未进行链接的中间文件(Windows下的.obj和Linux下的.o),它跟可执行文件的内容和结构相似,所以一般跟可执行文件一起采用一种格式存储。不只是可执行文件,动态链接库和静态链接库(Windows下的.lib、.dll和Linux下的.a、.so)都按照可执行文件的格式存储。以ELF为例,这样ELF就有了下面四种类型:

  • 可重定位文件:可被用来链接成可执行文件或动态链接库的中间文件,静态链接库也可归为这一类。扩展名.o或.a。
  • 可执行文件:可直接执行的程序。ELF可执行文件一般没有扩展名。
  • 共享目标文件:可以在两种情况下使用:1)与其他可重定向文件、共享目标文件链接,形成新的目标文件;2)与可执行文件结合,作为进程映像的一部分。扩展名.so。
  • 核心转储文件:进程意外终止时,系统将进程地址空间的内容及终止时的其他信息转储。典型的是Linux下的core dump。

用file命令可以查看各种文件的类型:

$ file openfile.c
openfile.c: C source, ASCII text, with CRLF line terminators

$ gcc -c openfile.c
$ file openfile.o
openfile.o: ELF 32-bit LSB  relocatable, Intel 80386, version 1 (SYSV), not stripped

$ gcc -o openfile openfile.c
$ file openfile
openfile: ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), \for GNU/Linux 2.6.24, BuildID[sha1]=086b5fbe84778f5683f7ef4dbd710fe2837370db, not stripped

$ file /lib/i386-linux-gnu/ld-2.19.so
/lib/i386-linux-gnu/ld-2.19.so: ELF 32-bit LSB  shared object, Intel 80386, version 1 (SYSV), dynamically linked, BuildID[sha1]=12f5bdcd6abd5fa411d5db326afcece6044621c4, stripped

1.2.2 段和段表

讲目标文件是什么样子,先要了解的最重要的一个概念就是 “段”(Segment or Section)。例如,编译后的机器指令被放在代码段.text,全局变量和局部静态变量放在数据段.data。未初始化的全局变量和局部静态变量默认值都为0,本可以都放在.data段,但为了节省空间它们被放在一个.bss段中。ELF的文件头中包含了文件类型、入口地址、目标硬件,以及描述文件中各个段的 段表,后面会详细分析段表的作用。

为什么代码段和数据段要分开?
1)权限:程序装载后,数据和指令被映射到两个区域,可以分别设置为可读写和只读。
2)缓存:指令和数据分离有利于提高程序的局部性,提高缓存命中率。
3)共享:分离后的指令可以在多个进程间共享,节省大量内存。

1.2.3 常用查看命令

之前在六星经典CSAPP-笔记(7)加载与链接(上)中的“2.对象文件查看工具”已经简单列举了学习链接时常用的工具,像nm、objdump、readelf、ldd等。这里重点总结一下objdump和readelf的最常见命令:

  • 查看所有内容
    • readelf -a (-a=–all 相当于-e -r -s)
  • 查看header
    • all header
      • objdump -x (-x=–all-headers 包括file、section header以及符号表和重定位表)
      • readelf -e (-e=–headers 包括file、program、section header)
    • file header
      • objdump -f (-f=–file-headers)
      • readelf -h (-h=–file-header)
    • program header
      • readelf -l (-l=–program-header)
    • section header
      • objdump -h (-h=–section-headers)
      • readelf -S (-S=–section-headers)
      • readelf -t (-t=–section-details)
  • 查看section
    • 查看数据
      • objdump -s (-s=–full-contents 包括.text、.data、.rodata、.comment的二进制和ASCII码)
    • 查看代码
      • objdump -d -z -r -l (-d=–disassemble, -z=–disassemble-zeroes, -r=–reloc, -l=–line-numbers 只反汇编.text段)
      • objdump -D (-D=–disassemble-all 不只反汇编.text段,也把.data、.bss、.rodata、.comment当成代码反汇编)
      • 在objdump后加上 | grep -A15 “” 可以查看某个函数的15行反汇编代码
    • 查看符号表
      • objdump -t (-t=–syms)
      • readelf -s (-s=–syms)
    • 查看重定位表
      • objdump -r (-r=–reloc)
      • readelf -r (-r=–relocs)
    • 查看某section内容
      • objdump -j .text -s/-d (根据section是数据还是代码)

1.3 ELF文件结构详解

书上以一小段代码SimpleSection.c为例,里面包含了各种常见的元素,如外部引用printf()、内部引用func1()、全局变量global_init_var、未初始化的全局变量global_uninit_var、静态变量static_var、未初始化的静态变量static_var2等,详细研究了ELF文件的结构。

/*
 * SimpleSection.c
 *
 * Linux:
 *  gcc -c SimpleSection.c
 *
 * Windows:
 *  cl SimpleSection.c /c /Za
 */
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)
  • 6
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值