首先来看一段Unix/Linux下的汇编代码:
#PURPOSE: This program finds the maximum number of a
# set of data items.
##VARIABLES: The registers have the following uses:
## %edi - Holds the index of the data item being examined
# %ebx - Largest data item found
# %eax - Current data item
## The following memory locations are used:
## data_items - contains the item data. A 0 is used
# to terminate the data
#
.section .data
data_items:
.long 3,67,34,222,45,75,54,34,44,33,22,11,66,0
.section .text
.globl _start
_start:
movl $0, %edi
movl data_items(,%edi,4), %eax
movl %eax, %ebx
start_loop:
cmpl $0, %eax
je loop_exit
incl %edi
movl data_items(,%edi,4), %eax
cmpl %ebx, %eax
jle start_loop
movl %eax, %ebx
jmp start_loop
loop_exit:
movl $1, %eax
int $0x80
被红色注释的部分就是汇编代码,先大概的解释一下这段代码的意思。.section .data表示的是数据段。
data_items:
.long 3,67,34,222,45,75,54,34,44,33,22,11,66,0
表示的是定义的一个long型的数组。
.section .text 表示的是代码区段。下面的部分就是真正的代码部分。
.globl _start 表示的定义了一个全局的标识符_start,相当C语言中的标签作用,_start要被链接器使用,相当C语言中的main函数一样,是整个程序的入口。
用%edi寄存器中的值表示数组下标
movl data_items(,%edi,4), %eax
movl %eax, %ebx
表示把取数组data_items中的第%edi的值,存放至寄存器%eax,同时%ebx表示的是数组中的最大值,初始化为数组的第一个元素。
start_loop,loop_exit都属于标签性质。
cmpl $0, %eax
je loop_exit
因为我们定义数组的时候,最后一个元素为0,即0为哨兵,比较%eax和0,如果相等的话,表示程序已经完成,跳转至loop_exit处执行。
incl %edi
movl data_items(,%edi,4), %eax
cmpl %ebx, %eax
否则移动数组下标,取数组的下一个元素值,与%ebx进行比较,若%eax中的值比%ebx值大,则将%eax值给%ebx,保持%ebx维持是数组的最大值。
movl $1, %eax
int $0x80
表示exit退出,并且退出码为1.
上面所讲的就是该段汇编代码的涵义。.
把上述的汇编代码编辑在test.s文档中,然后执行下面的命令:
as test.s -o test.o
ld test.o -o test
Unix/Linux中的可执行文件都是采用的ELF文件格式标准,但是有3中不同的类型:
<1>.可重定位的目标文件(Relocatable Object file)
<2>.可执行文件(Executable file)
<3>.共享库(Shared library)
我们通过分析ELF文件的组成,来了解编译和链接的过程。
我们用Linux下带的工具来查看elf文件,用下述命令即可:
$ readelf -a test.o
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: REL (Relocatable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 200 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 40 (bytes)
Number of section headers: 8
Section header string table index: 5
...
上面的就是被解读出来的elf文件信息。
ELF Header中描述了操作系统是UNIX,体系结构是80386。Section Header Table中有8个Section Header,在文件中的位置(或者叫文件地址)从200(0xc8)开始,每个40字节,共320字节,到文件地址0x207结束。这个目标文件没有Program Header。
如果想要从目标文件反汇编的话,可以执行下述命令即可。
$ objdump -d test.o
test.o: file format elf32-i386
Disassembly of section .text:
00000000 <_start>:
0: bf 00 00 00 00
5: 8b 04 bd 00 00 00 00
c: 89 c3
0000000e <start_loop>:
e: 83 f8 00
11: 74 10
13: 47
14: 8b 04 bd 00 00 00 00
1b: 39 d8
1d: 7e ef
1f: 89 c3
21: eb eb
00000023 <loop_exit>:
23: b8 01 00 00 00
28: cd 80
我们对可执行程序反汇编一下,就知道结果了:
$ objdump -d test
test: file format elf32-i386
Disassembly of section .text:
08048074 <_start>:
8048074: bf 00 00 00 00
8048079: 8b 04 bd a0 90 04 08
8048080: 89 c3
08048082 <start_loop>:
8048082: 83 f8 00
8048085: 74 10
8048087: 47
8048088: 8b 04 bd a0 90 04 08
804808f: 39 d8
8048091: 7e ef
8048093: 89 c3
8048095: eb eb
08048097 <loop_exit>:
8048097: b8 01 00 00 00
804809c: cd 80
现在反汇编的代码中已经把绝对地址都加在进程序中了。从这个角度来理解编译和链接应该更深入一些,编译只是检查语法错误,对应的地址都是虚拟地址(即被MMU单元截获了地址),链接时,把虚拟地址转化为绝对地址,加载进可执行文件中。