objdump -S,readelf -h浅析

使用objdump和readelf可以对目标文件进行详细的分析,这里只说objdump -S和readelf -h的内容。当然它们还有其他很多的选项,其他的选项可以查看它们的man文档。
因为接触的不久,这里以很简单的程序hello.c进行简单的分析。

#include <stdio.h>

int main()
{
	printf("hello world!\n");
	return 0;
}

一个源文件经过预编译、编译、汇编、链接后生成目标文件。目标文件可以通过加载器加载到内存中运行,这期间还会生成很多文件,这里不进行说明。

加载器要将用户程序加载到内存中运行都需要那些信息呢?
最重要的就是用户程序中各个段地址,因为要进行重定位,为什么要进行重定位这里说明,可以百度、google。比如上面hello.c,内核要将hello.c加载到内存,就需要知道hello.c存储在磁盘的那个位置,还需要知道当前内存中哪里空间是空闲的,以用来加载hello.c。hello.c要打印一个字符串“hello world!”这个字符串需要存储在内存中的一个段,这个段在在哪内核也需要知道,所以也会进行记录。

现在将hello.c使用gcc编译,生成hello目标文件。使用的环境是linux,所以生成的文件是ELF格式的,我们可以使用readelf来查看。
使用readelf -h hello

$ readelf -h hello
ELF 头:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  类别:                                                         ELF64
  数据:                                                         2 补码,小端序 (little endian)
  版本:                                                         1 (current)
  OS/ABI:                                                    UNIX - System V
  ABI 版本:                                                  0
  类型:                                                          DYN (共享目标文件)
  系统架构:                                                  Advanced Micro Devices X86-64
  版本:                                                           0x1
  入口点地址:                                          0x530
  程序头起点:                                          64 (bytes into file)
  Start of section headers:                   6448 (bytes into file)
  标志:                                                        0x0
  本头的大小:                                          64 (字节)
  程序头大小:                                          56 (字节)
  Number of program headers:         9
  节头大小:                                               64 (字节)
  节头数量:                                               29
  字符串表索引节头:                             28

编译器在将源文件编译成目标文件的时候,需要根据当前的内核、计算机的CPU类型等信息编译成对应的二进制文件。上面提到用户程序在加载到内存中时需要重定位,重定位需要起始地址、程序的大小。用户程序的信息存在另外的位置,这里也有指出。

下面使用objdump -S hello,将可执行程序反汇编。

$ objdump -S hello

hello:     文件格式 elf64-x86-64


Disassembly of section .init:

00000000000004e8 <_init>:

Disassembly of section .plt:

0000000000000500 <.plt>:

0000000000000510 <puts@plt>:

Disassembly of section .plt.got:

0000000000000520 <__cxa_finalize@plt>:

Disassembly of section .text:

0000000000000530 <_start>:
 54d:	48 8d 3d e6 00 00 00 	lea    0xe6(%rip),%rdi        # 63a <main>
 554:	ff 15 86 0a 20 00    	callq  *0x200a86(%rip)        # 200fe0 <__libc_start_main@GLIBC_2.2.5>
 
0000000000000560 <deregister_tm_clones>:

00000000000005a0 <register_tm_clones>:

00000000000005f0 <__do_global_dtors_aux>:

0000000000000630 <frame_dummy>:

000000000000063a <main>:

0000000000000660 <__libc_csu_init>:

00000000000006d0 <__libc_csu_fini>:

Disassembly of section .fini:

00000000000006d4 <_fini>:

这里将里面的汇编指令都删掉了,因为我们并不研究具体的汇编代码,而且着太多了,我也没太看懂。

内核的一个重要的功能就是加载用户程序,在加载用户程序之前需要进行一些初始化工作,比如初始化用户程序所要用到的段,和用户程序要用到的数据。可以看到前面有一个<_inti>标识。中间有很多我现在还看不懂,就不胡说了。程序的开始位置并不是所以为的main,而是从start开始可以再上面找到<_start>标识,里面的指令就是用户程序真正开始运行用到的指令。<_start>中会有一条跳转指令,跳转到,为了说明这一点,并没有将这条指令,可以再上面看到,linux下具体怎么跳转的我还不太会。这里的用户程序hello中有一个重要的功能是打印hello world!我们可以在上面找到,在这里进行打印。用户程序所用到的数据需要声明,在声明时编译器给了特定的标号,标号就是当前的地址,可以理解为一个指针。使用各这标号将字符串打印出来。怎么打印的这里不进行说明。

以上是我对readelf -h和objdump -S出来的内容进行的简单的分析。
可能会有错误的地方,希望能够指出来。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值