Linux汇编语言中标号的作用

 

1.               Abstract

.

2.               Introduction

在看一本内核代码分析书籍的时候,发现有一个地方总是不理解:在 arch/x86

/boot/header.S 文件里,有如下跳转指令 (2.6.24 内核中的 115 )

              .byte      0xeb             # short (2-byte) jump

              .byte      start_of_setup-1f

因为之前写汇编都是 jmp label 这样的写法,故而总是想不明白这里为什么要有两个标号去减。

其实原因是,我们这里的 jmp 都是 short jump( 对此不理解的请看汇编的书籍或在网络上搜寻 jmp 指令的含义 ) ,而 short jump 包括两部分,第一个部分是操作码,对于 short jump 而言就是 0xeb ,第二个部分是跳转距离,此距离指的是从当前汇编指令下面的一条汇编指令的起始地址开始计算的距离,也就是, short jump 的作用实际上是 : PC + JUMP_VALUE ,此 JUMP_VALUE 就是第二部分,即操作数部分。

如果我们在汇编代码里写的是 jmp label ,汇编编译程序会自动把 label 到当前汇编下一条汇编指令的地址作为操作数;

如果我们在汇编代码里涌了如 header.S 中的硬编码,那么 start_of_setup-1f 就是这两个 label 之间的距离了。

这么说吧,其实各个 label 都是一个地址,此地址在编译后为从 0 开始的地址,在链接之后成为一个从链接地址开始的地址,而 jmp label 的写法,汇编编译程序会自动变成 label – 下一个汇编指令的起始地址。

3.               分析和验证过程

3.1        主验证过程分析

如下的代码:

.code16

.section ".text", "ax"

 

.globl     _start

_start:

              jmp 1f

              jmp start_of_setup

              .byte      0xeb

              .byte      start_of_setup-1f

1:

              mov $0x0000, %ax

label_test:

              jmp start_of_setup

                           

start_of_setup:

              jmp 1b

 

编译、链接命令:

as --gstabs -o asm-test.o asm-test.s

ld -o asm-test asm-test.o

( 注,用 gcc 直接编译可以用如下命令: gcc -Wp,-MD -nostdinc -D__ASSEMBLY__   -Wa   -c -o asm-test.o asm-test.s)

然后再反编译:

objdump -d asm-test

就会得到如下的汇编代码:

asm-test:     file format elf32-i386

 

Disassembly of section .text:

 

08048054 <_start>:

  8048054:       eb 04                   jmp    804805a <_start+0x6>

  8048056:       eb 07                   jmp    804805f <start_of_setup>

  8048058:       eb 05                   jmp    804805f <start_of_setup>

  804805a:       b8 00 00 eb 00          mov    $0xeb0000,%eax

 

0804805d <label_test>:

  804805d:       eb 00                   jmp    804805f <start_of_setup>

 

0804805f <start_of_setup>:

  804805f:       eb f9                   jmp    804805a <_start+0x6>

由此可见,此处如下的两个写法完全是等效的:

              1). jmp start_of_setup

              2). .byte      0xeb

                     .byte      start_of_setup-1f

同时,我们也可以看出,这里要求标号 1 一定要紧跟着硬编码的 jmp 指令之后,否则就跳转不到 start_of_setup 处了,比如说我们把汇编源代码略加修改,将标号 1 放后一点:

.code16

.section ".text", "ax"

 

.globl     _start

_start:

              jmp 1f

              jmp start_of_setup

              .byte      0xeb

              .byte      start_of_setup-1f

label_test:

              jmp start_of_setup

1:

              mov $0x0000, %ax

start_of_setup:

              jmp 1b

然后经过编译、链接和反汇编的过程,得到的是如下的结果:

asm-test:     file format elf32-i386

 

Disassembly of section .text:

 

08048054 <_start>:

  8048054:        eb 06                   jmp    804805c <label_test+0x2>

  8048056:       eb 07                   jmp    804805f <start_of_setup>

  8048058:       eb 03                   jmp    804805d <label_test+0x3>

 

0804805a <label_test>:

  804805a:       eb 03                    jmp    804805f <start_of_setup>

  804805c:       b8 00 00 eb fb          mov    $0xfbeb0000,%eax

 

0804805f <start_of_setup>:

  804805f:       eb fb                   jmp    804805c <label_test+0x2>

看到了吧,当前硬编码的 jmp 指令跳转到了 804805d 处,而这是 mov 指令的第二个字节,这是因为 start_of_setup 减去 1 的结果为 3 了。

3.2        编译和链接的区别

我再继续废话一点儿吧,估计这个所有的人都清楚,就是编译和链接结果的区别,编译之后,原则上说,所有的地址和 label 都是从 0 开始计算的,而链接过后,都是从链接地址开始计算的,我们以最开始的汇编源代码举例:

.code16

.section ".text", "ax"

 

.globl     _start

_start:

              jmp 1f

              jmp start_of_setup

              .byte      0xeb

              .byte      start_of_setup-1f

1:

              mov $0x0000, %ax

label_test:

              jmp start_of_setup

start_of_setup:

              jmp 1b

编译、链接命令:

as --gstabs -o asm-test.o asm-test.s

ld -o asm-test asm-test.o

然后先反编译 编译的结果 asm-test.o

命令: objdump -d asm-test.o

asm-test.o:     file format elf32-i386

 

Disassembly of section .text:

 

00000000 <_start>:

   0:   eb 04                   jmp    6 <_start+0x6>

   2:   eb 07                   jmp    b <start_of_setup>

   4:   eb 05                   jmp    b <start_of_setup>

   6:   b8 00 00 eb 00          mov    $0xeb0000,%eax

 

00000009 <label_test>:

   9:   eb 00                   jmp    b <start_of_setup>

 

0000000b <start_of_setup>:

   b:   eb f9                   jmp    6 <_start+0x6>

可以看到所有地址都是从 0 地址开始计算的。

然后先反编译 链接的结果 asm-test

命令: objdump -d asm-test

asm-test:     file format elf32-i386

 

Disassembly of section .text:

 

08048054 <_start>:

  8048054:       eb 04                   jmp    804805a <_start+0x6>

  8048056:       eb 07                   jmp    804805f <start_of_setup>

  8048058:       eb 05                   jmp    804805f <start_of_setup>

  804805a:       b8 00 00 eb 00          mov    $0xeb0000,%eax

 

0804805d <label_test>:

  804805d:       eb 00                   jmp    804805f <start_of_setup>

 

0804805f <start_of_setup>:

  804805f:       eb f9                   jmp    804805a <_start+0x6>

可以看到所有的地址都是从链接地址 0x08048054 开始计算的,而 Linux ELF 文件的缺省链接地址大多都是 0x08048xxx 这样的地址。

 

4.               References

[1].   Linux 汇编语言开发指南 , http://www.ibm.com/developerworks/cn/linux/l-assembly/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值