Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1135
  • 博文数量: 6
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 10
  • 用 户 组: 普通用户
  • 注册时间: 2020-02-19 22:53
文章分类
文章存档

2020年(6)

我的朋友
最近访客

分类: 嵌入式

2020-02-19 22:56:19

1 基础知识
  参考csapp第七章 链接程序员的自我修养:(1)目标文件
1.1 百度百科目标文件:
  目标文件(objectfile)即存放目标代码的计算机文件,它常被“称作”二进制文件(binaries)。目标文件包含着机器代码(可直接被计算机中央处理器执行)以及代码在运行时使用的数据,如重定位信息,如用于链接或调试的程序符号表(变量和函数的名字),此外还包括其他调试信息。
1.2 Linux下有3种目标文件形式:
  可执行目标文件,可重定位目标文件和共享目标文件。也有对应的称为可执行文件,目标文件,共享库,只是说法不同,指的都是同样的东西。
  可执行目标文件:包含二进制代码和数据,可以在存储器中直接执行。
  可重定位目标文件:包含二进制代码和数据,可以在编译时与其他可重定位目标文件合并起来的,创建一个可执行目标文件。
  共享目标文件: 是一种特殊的可重定位目标文件,可以在加载或者运行时被动态加载到存储器并链接。
  事实上,译器和汇编器生成可重定位目标文件(包含共享目标文件)。链接器生成可执行目标文件。
1.3 Linux下目标文件文件格式:
  由于编译后的中间文件(Windows下的.obj和Linux下的.o),动态链接库和静态链接库(Windows下的.lib.dll和Linux下的.a.so),跟可执行文件的内容和结构相似,所以一般跟可执行文件一起采用同一种文件格式存储
1.4 Linux下有3种主要的可执行目标文件格式:
  a.out(assembler and link editor output 汇编器和链接编辑器的输出)、COFF(Common Object File Format 通用对象文件格式)、ELF(Executable and Linking Format 可执行和链接格式)。老的可执行文件格式缺乏可扩展性,如不能包含“现代”可执行文件中常见的调试信息,所以基本上已被ELF格式取代。现代Linux使用的可执行目标文件格式都是ELF
1.5 Linux下ELF格式的目标文件有4种主要类型:
  可重定位文件:可以被用来链接成可执行目标文件或共享目标文件的文件。扩展名.o或.a。
  可执行文件:可以直接执行的程序。ELF可执行文件一般没有扩展名。
  共享目标文件:可以在两种情况下使用:1 链接器(Linux系统中为ld)可以将它与其他可重定向目标文件、共享目标文件链接,形成新的目标文件;2 动态加载器(Linux系统中为/lib/ld-linux.so.2)(注:dynamic loader,在很多文章中将之称之为动态链接器,但我认为称为动态加载器比较好,这样不容易和链接器相混淆)(注:动态加载器所在的路径在可执行文件可以查到:readelf -l xxx | grep interpreter)可以将它与可执行目标文件结合,作为进程映像的一部分。扩展名.so。
  core dump文件:。
图片选自:进程中的地址是从何而来
1.6 Linux下ELF文件文件格式
  ELF文件格式提供了两种视图,分别是链接视图和执行视图。
  链接视图是以节(section)为单位,执行视图是以段(segment)为单位。链接视图就是在链接时用到的视图,而执行视图则是在执行时用到的视图。上图左侧的视角是从链接来看的,右侧的视角是执行来看的。总个文件可以分为四个部分:

点击(此处)折叠或打开
  1. -ELF header:描述整个文件的组织。
  2. -Program Header Table:描述文件中的各种segment,用来告诉系统如何创建进程映像的。
  3. -sections 或者 segments:segments是从运行的角度来描述elf文件,sections是从链接的角度来描述elf文件,也就是说,在链接阶段,我们可以忽略program header table来处理此文件,在运行阶段可以忽略section header table来处理此程序(所以很多加固手段删除了section header table)。从图中我们也可以看出,segments与sections是包含的关系,一个segment包含若干个section。
  4. -Section Header Table:包含了文件各个section的属性信息,section名字、section大小、section在elf文件中的偏移等。
选自:ELF文件格式解析
1.7 Linux下ELF文件中各个section的含义:
  ELF文件中各个section的含义如下图所示:

选自:进程中的地址是从何而来

2 常见问题
2.1 Linux下ELF文件和.BIN文件的区别(注:通过如上对ELF的描述,ELF有两种视角:可执行、可链接,而通常讲的、也是这里讲的是ELF可执行的视角):
.BIN文件:是raw binary文件,这种文件只包含机器码。
     执行时,只需要将程序加载到其起始地址,一般为0x00地址,就可以执行。
ELF文件:除了机器码外,还包含其它额外的信息,如段的加载地址,运行地址,重定位表,符号表等。
     执行时,需要一个ELF Loader。正因为uboot和Linux kernel启动的时候是没有ELF Loader的,所以烧在flash上的文件只能是raw binary格式的,即镜像文件image。
     一般情况下,通过gcc编译出来的是elf文件,而通过objcpy可以把elf文件转换为bin文件。
小结:ELF文件包含了一些调试信息;.BIN文件是将ELF文件中的代码段、数据段,还有一些自定义段,抽取出来做成的一个内存镜像文件
参考:ELF文件和BIN文件
2.2 Linux下.ko文件和.o文件的区别(注:这里.o狭义的指用于内核使用的动态链接文件):
.o文件:是object文件。
.ko文件:是kernel object文件。是Linux 2.6内核使用的动态链接文件,用于在Linux系统启动时动态的加载卸载内核模块。
小结:.ko是linux 2.6内核编译之后生成的,相比linux 2.4内核编译生成的.o,其多了一些module信息,如author,license之类的。
2.3 Linux下ELF文件和.out文件的区别
历史由来:a.out是"assembler output"的缩写格式,代表汇编程序输出。在较早版本的类unix系统中,a.out是一种输出格式,用于可执行文件,目标文件和共享库。早期的 PDP-7系统上没有链接器,程序的创建过程是先把所有源文件连接成一个文件,然后进行汇编,产生的汇编程序保存在a.out中。这样a.out是名副其实的汇编输出。
     但到PDP-11之后,人们为其编写了链接器,程序的创建是先编译然后链接输出保存到a.out中,这时a.out其实已经是链接输出了,但输出的可执行文件仍然延续这个命名习惯。
     后来,因为构建a.out的复杂性,a.out格式被现在普遍使用的ELF格式所替代,但输出文件名仍旧是a.out。现在我们看到的a.out只是一个可执行文件,而不再是文件格式。
实际操作:当编译程序时,如果不加-o参数,生成的binary代码的名字都是默认的a.out。如:gcc helloworld.c
     当然,也可以使用-o选项给生成的文件起一个别的名字。如:gcc helloworld.c -o helloword.out,或者gcc helloworld.c -o helloword
     事实上,如上两种生成的a.out、helloworld.out、helloworld其实都是ELF文件格式的可执行目标文件。
参考:linux c/c++ a.out的由来
2.4 Linux下ELF结构文件的确定
ELF的可执行文件:第一个字节是八进制177也就是16进制的7F,紧跟其后的2,3,4字节是ELF三个字母。你可以输入od -c a.out | head查看一下。如:a.out、helloworld.out、helloworld
ELF的其他目标文件,除.a文件:第一个字节是八进制177也就是16进制的7F,紧跟其后的2,3,4字节是ELF三个字母。如:helloworld.o、helloworld.so
ELF的.a文件:包含八进制177也就是16进制的7F,紧跟其后的2,3,4字节是ELF三个字母。如:helloworld.a
参考:linux c/c++ a.out的由来
2.5 Linux下ELF文件的段和段表的区别关系
ELF文件最重要的一个概念就是“段”(segment or section),例如:.text段、.data段、.bss段等等。
而,描述ELF文件中各个“段”的是“段表”(Section Header Table),包含了描述每个段的属性信息,比如段的名字、段的大小、段在elf文件中的偏移等。
另外,可以通过readlef -S a.out或objdump -h a.out来查看ELF文件的段表;通过readelf -s a.out或objdump -t a.out来查看ELF文件的符号表。如下是,常用的查看命令:

查看ELF文件

查看所有内容

 

readelf -a (-a=all 相当于-e -r -s)

查看header

all header

objdump -x (-x=all-headers 包括filesection header以及符号表和重定位表)

readelf -e (-e=headers 包括fileprogramsection 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)目标文件
阅读(137) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~