jmp指令

最近看链接器源码中,对位置无关代码PIC(共享库)的链接问题,发现对call和jmp很多不常用的用法,这里试验并总结了一下各种用法。

我们最常用的jmp形式,就是 jmp后面跟个标签!这个没什么可说的!

假如标签叫做mylabel,它的地址是0x8048377,而且有个全局变量b,b存储的内容就是mylabel的地址,而b的地址是0x80494A8。
即有这样的赋值(加载)语句:
movl $mylabel,%eax //把mylabel的地址加载到eax寄存器中
movl %eax,b //把mylabel的地址加载到b中
movl $b,%ebx //把b的地址加载到ebx寄存器中

我们考虑下面的语句:
1. jmp mylable
2. jmp 0x8048377
3. jmp %eax
4. jmp *%eax
5. jmp *(%ebx)
6. jmp *0x80494A8
7. jmp *b
8. jmp $0x5

这7句jmp语句!分别都做了什么?

1. 不用说,跳转到mylabel标签处继续执行代码,但是,是如何跳转的呢?就是PC加上了mylabel标签处对于jmp处的一个偏移地址!可执行的二进制代码是这样表示的:eb 03,就是说,pc+0x03就可以了。

2. 这里,0x8048377是mylabel的地址,我以前研究过,标签的作用,更他的地址的作用是等效的。所以,这里的执行效果跟1中的相同。但是,还有些不一样!这里的二进制代码成了:e9 03 00 00 00 这里用了32位表示了这个偏移,而在1中,只用了8位!

3. 在编译链接的时候,这句代码会有警告:warning:indirect jmp without '*'。间接跳转没有‘*’符号,但是,执行起来,还是没有错。看一下二进制的可执行文件的代码,发现,给补上了个‘*’号!而且二进制是:ff e0.

4. 其实,4是3的补充版,正常的形式就是4,而三是有警告的被补充的版本。

5. %ebx是b的地址,那么(%ebx)表示ebx的值为地址,指向的地方。这里指向了b的内容,也就是mylabel的地址!于是,化简后,5也就等效与2,但是,二进制表示是:ff 23。

6. 0x80494A8是b的地址,这里看做内存数,那么实质上,b指向的值是mylabel的地址,于是,化简后同2,二进制代码是:ff 25 a8 94 04 08。

7. b是标签,代表一个地址,所以,这里同6,二进制代码也同6

8. 这句话是错误的,jmp不支持立即数!

所以说,正确的写法有:

1. jmp mylable //eb 03
2. jmp 0x8048377 //e9 03 00 00 00

3. jmp *%eax //ff e0
4. jmp *(%ebx) //ff 23
5. jmp *0x80494A8 //ff 25 a8 94 04 08
6. jmp *b //ff 25 a8 94 04 08

1和2叫做间接寻址,就是算偏移量的。后面没有‘*’号,而是直接一个标签或者地址(标签就可以看做是地址),所以说,就是一个直接的地址的值。间接跳转的二进制代码是eb或者e9,是e开头的。
3,4,5,6叫做直接寻址,直接寻址的标识就是这个‘*’号!直接寻址,就是PC直接赋值某个地址,而不是加偏移量。所以,‘*’号后面的部分,其实是一个要付给PC的值,那么,取值的方式就好想象了!直接跳转的二进制代码是ff开头的。

3是寄存器直接取值;4是寄存器间接取值;5是内存数取值;6是标签取值(实质上同5)。

确实,有点意思~
call同jmp指令!

你可能感兴趣的:(Linux内核)