GCC/X86,相对跳跃问题
我正试图在x86汇编中进行一次相对的跳跃,但是我无法让它工作。似乎出于某种原因,我的跳跃一直被改写为绝对跳跃或其他什么 我尝试做的一个简单示例程序如下:GCC/X86,相对跳跃问题,c,gcc,x86,C,Gcc,X86,我正试图在x86汇编中进行一次相对的跳跃,但是我无法让它工作。似乎出于某种原因,我的跳跃一直被改写为绝对跳跃或其他什么 我尝试做的一个简单示例程序如下: .global main main: jmp 0x4 ret 由于jmp指令的长度为4字节,且相对跳转与跳转+1的地址偏移,因此这应该是一个奇特的no-op。但是,编译和运行此代码将导致分段错误 对我来说,真正的难题是将其编译到对象级别,然后反汇编对象文件,这表明汇编程序似乎正确地进行了相对跳转,但在编译文件后,链接器将其更
.global main
main:
jmp 0x4
ret
由于jmp指令的长度为4字节,且相对跳转与跳转+1的地址偏移,因此这应该是一个奇特的no-op。但是,编译和运行此代码将导致分段错误
对我来说,真正的难题是将其编译到对象级别,然后反汇编对象文件,这表明汇编程序似乎正确地进行了相对跳转,但在编译文件后,链接器将其更改为另一种类型的跳转
例如,如果上述代码位于名为asmtest.s的文件中:
$gcc -c asmtest.s
$objdump -D asmtest.o
... Some info from objdump
00000000 <main>:
0: e9 00 00 00 00 jmp 5 <main+0x5>
5: c3 ret
$gcc-c asmtest.s
$objdump-D asmtest.o
... 来自objdump的一些信息
00000000 :
0:e9 00 jmp 5
5:c3 ret
这看起来像是汇编程序正确地进行了一次相对跳转,尽管怀疑jmp指令中填充了0
然后,我使用gcc链接它,然后将其分解,得到以下结果:
$gcc -o asmtest asmtest.o
$objdump -d asmtest
...Extra info and other disassembled functions
08048394 <main>:
8048394: e9 6b 7c fb f7 jmp 4 <_init-0x8048274>
8048399: c3 ret
$gcc-o asmtest asmtest.o
$objdump-d asmtest
…额外信息和其他已分解的功能
08048394 :
8048394:e9 6b 7c fb f7 jmp 4
8048399:c3 ret
在我看来,这就像链接器重写了jmp语句,或者用5-in替换了另一个地址
所以我的问题归结到,我做错了什么
我是否错误地指定了偏移量?我是否误解了相对跳跃的工作原理?gcc是否试图确保我在代码中不做危险的事情?事实上,汇编程序认为您在尝试进行绝对跳跃。然而,
jmp
操作码在金属层面上是相对的。因此,汇编器不知道在0xe9字节后写什么,因为汇编器不知道代码将在哪个地址结束
汇编程序不知道,但链接程序知道。因此,汇编器在asmtest.o
头中的某个地方为链接器编写了一个请求,如下所示:“当您知道代码将加载到哪个地址时,请调整0xe9后面的字节,以便它们适合从该点(使用相对寻址)跳到绝对地址“4”。链接器就是这样做的。它看到0xe9位于地址0x08048394,下一个操作码位于0x08048399,并计算:要从0x08048399变为0x00000004,必须减去0x08048395,这相当于添加(在32位机器上)0xf7fb7c6b。因此,结果二进制文件中的“6b 7c fb f7”序列
您可以“手动”对相对跳转进行编码,如下所示:
.global main
main:
.byte 0xe9
.long 0x4
ret
因此,汇编程序不会注意到您的0xe9实际上是一个jmp
,它也不会试图胜过您。在二进制文件中,您将获得所需的“e9 04 00”序列,并且没有链接器交互
请注意,上面的代码可能会崩溃,因为相对偏移量是从紧接偏移量之后的地址(即下一个操作码的地址,此处为
ret
)开始计算的。这将在ret
之后跳转到无人区4字节,可能出现segfault或奇怪的情况。我认为汇编程序正在为您获取绝对地址并计算地址偏移量。第一种情况中的零可能存在,因为它是修复表的一部分,并且在链接阶段计算偏移量
我的汇编语言技能有些生疏,但我认为你可以这样做:
.global main
main:
jmp getouttahere
getouttahere:
ret
或者如果你真的想让它看起来相对:
.global main
main:
jmp .+5
ret
如果我错了,请温柔一点;这已经有很长一段时间了。如果您使用的是默认使用AT&T语法的GCC气体汇编程序,则相对寻址的语法(非常类似于英特尔/MASM汇编语法中使用的
$
伪符号)。您应该能够通过以下方式获得相对跳跃:
jmp . + 5
例如,以下函数:
void foo(void)
{
__asm__ (
"jmp . + 5\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
);
}
组装到:
71 0000 55 pushl %ebp
72 0001 89E5 movl %esp, %ebp
74 LM2:
75 /APP
76 0003 EB03 jmp . + 5
77 0005 90 nop
78 0006 90 nop
79 0007 90 nop
80 0008 90 nop
81 0009 90 nop
82
84 LM3:
85 /NO_APP
86 000a 5D popl %ebp
87 000b C3 ret
跳转到命名标签是在程序集中通常进行跳转的方式。这是非常基本的。我假设OP已经知道了这一点,并且出于某种未指明的原因,真的希望手工编写一个相对跳转的代码。我怀疑该评论是对我的评论的回应。我删除了它,因为我把我的评论移到了一个答案上。谢谢你的帮助,这也正是我所需要的。也谢谢你指出它跳得太远的问题,我有点不确定指令是从哪里计算出来的。@Ian:欢迎来到机器代码编程(汇编语言是为三色堇而设计的)!请等待轮到您使用前面板开关进入程序。同时,您可以使用参数化的延迟函数,该函数使用磁鼓内存对延迟进行计时:这对于尝试计算绝对地址是有意义的。不幸的是,由于各种原因,我无法使用标签进行跳转,$+5的语法无效。谢谢你的帮助。@Ian Kelly:汇编程序的语法可能会有所不同。我认为“+5”可能有用。