出处:http://www.groad.net/bbs/read.php?tid-3000.html 有增删
对于 CPU 来说,它对指令和数据本质上是不区分的。数据也可以当成是指令执行。 在硬编码的一个简单示例里,揭示了这一点。
下面是一个普通的汇编程序:
.section .text
.global _start
_start:
nop
nop
jmp start_of_setup
nop
nop
nop
start_of_setup:
movl $42, %edx
int $0x80
movl $1, %eax
movl $0, %ebx
int $0x80
这里,我们只关心 jmp 这条指令,上面的应用跳转到 start_of_setup 这条标号下执行指令。通过 objdump 查看编译后生成的目标文件:
$ objdump -d hcode.o
hcode.o: file format elf32-i386
Disassembly of section .text:
00000000 <_start>:
0: 90 nop
1: 90 nop
2: eb 03 jmp 7
4: 90 nop
5: 90 nop
6: 90 nop
00000007 :
7: ba 2a 00 00 00 mov $0x2a,%edx
c: cd 80 int $0x80
e: b8 01 00 00 00 mov $0x1,%eax
13: bb 00 00 00 00 mov $0x0,%ebx
18: cd 80 int $0x80
可以看到 jmp start_of_setup 的指令码为两个字节:eb 03 。其中 eb 是 jmp 指令本身的指令码;03 则是跳转的偏移,也就是说 jmp 的跳转地址是从紧随其后的那条 nop 指令处开始计算的,偏移 3 个字节后的那条,即 mov $0x2a, %edx。
现在我们用硬编码来实现这个跳转。所谓的硬编码,通俗的讲,就是直接用指令码来表示。修改上面的程序:
.section .text
.global _start
_start:
nop
nop
.byte 0xeb
.byte start_of_setup-4
nop
nop
nop
start_of_setup:
movl $42, %edx
int $0x80
movl $1, %eax
movl $0, %ebx
int $0x80
上面直接将原来程序中的 jmp 指令处用两个 .byte 的定义来取代。对于一般场合来说,.byte 会用来指定一个字节数据,而它也就仅会当成数据来使用。但在这里,它就变成了指令。再用 objdump 看一下,发现指令码和第一个程序中的是一模一样的,也就是我们在此处使用了硬编码。另外,.byte start_of_setup-4 实际上计算的也就是 start_of_setup 和 .byte start_of_setup-4 之间的距离(3个nop,3个字节)。
那么,既然可以用指令来完成的事情,为什么非要用硬编码的方式做呢?这样做有什么好处呢?我们来看下arch/x86/boot/header.S中的一段注释: