UNIX/Linux 平台可执行文件格式分析(4)

  COFF 文件头部中魔数与其它两种格式的意义不太一样,它是表示针对的机器类型,例如 0x014c 相对于 I386 平台,而 0x268 相对于 Motorola 68000系列等。当 COFF 文件为可执行文件时,字段 f_flags 的值为 F_EXEC(0X00002),同时也表示此文件没有未解析的符号,换句话说,也就是重定位在连接时就已经完成。由此也可以看出,原始的 COFF 格式不支持动态连接。为了解决这个问题以及增加一些新的特性,一些操作系统对 COFF 格式进行了扩展。Microsoft 设计了名为 PE(Portable Executable)的文件格式,主要扩展是在 COFF 文件头部之上增加了一些专用头部,具体细节请参阅参考资料 18,某些 UNIX 系统也对 COFF 格式进行了扩展,如 XCOFF(extended common object file format)格式,支持动态连接,请参阅参考资料 5。

  紧接文件头部的是可选头部,COFF 文件格式规范中规定可选头部的长度可以为 0,但在 LINUX 系统下可选头部是必须存在的。下面是 LINUX 下可选头部的数据结构:

  typedef struct
  {
  char magic[2]; /* 魔数 */
  char vstamp[2]; /* 版本号 */
  char tsize[4]; /* 文本段长度 */
  char dsize[4]; /* 已初始化数据段长度 */
  char bsize[4]; /* 未初始化数据段长度 */
  char entry[4]; /* 程序进入点 */
  char text_start[4]; /* 文本段基地址 */
  char data_start[4]; /* 数据段基地址 */
  }
  COFF_AOUTHDR;

  字段 magic 为 0413 时表示 COFF 文件是可执行的,注意到可选头部中显式定义了程序进入点,标准的 COFF 文件没有明确的定义程序进入点的值,通常是从 .text 节开始执行,但这种设计并不好。

  前面我们提到,COFF 格式比 a.out 格式多了一个节段表,一个节头条目描述一个节数据的细节,因此 COFF 格式能包含更多的节,或者说可以根据实际需要,增加特定的节,具体表现在 COFF 格式本身的定义以及稍早提及的 COFF 格式扩展。我个人认为,节段表的出现可能是 COFF 格式相对 a.out 格式最大的进步。下面我们将简单描述 COFF 文件中节的数据结构,因为节的意义更多体现在程序的编译和连接上,所以本文不对其做更多的描述。此外,ELF 格式和 COFF格式对节的定义非常相似,在随后的 ELF 格式分析中,我们将省略相关讨论。

  struct COFF_scnhdr
  {
  char s_name[8]; /* 节名称 */
  char s_paddr[4]; /* 物理地址 */
  char s_vaddr[4]; /* 虚拟地址 */
  char s_size[4]; /* 节长度 */
  char s_scnptr[4]; /* 节数据相对文件的偏移量 */
  char s_relptr[4]; /* 节重定位信息偏移量 */
  char s_lnnoptr[4]; /* 节行信息偏移量 */
  char s_nreloc[2]; /* 节重定位条目数 */
  char s_nlnno[2]; /* 节行信息条目数 */
  char s_flags[4]; /* 段标记 */
  };

  有一点需要注意:LINUX系统中头文件coff.h中对字段s_paddr的注释是"physical address",但似乎应该理解为"节被加载到内存中所占用的空间长度"。字段s_flags标记该节的类型,如文本段、数据段、BSS段等。在 COFF的节中也出现了行信息,行信息描述了二进制代码与源代码的行号之间的对映关系,在调试时很有用。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/28562.html