GCC / X86,相对跳跃的问题

时间:2010-04-14 15:34:35

标签: c gcc x86

我正在尝试在x86程序集中进行相对跳转,但是我无法让它工作。似乎由于某种原因,我的跳跃不断被重写为绝对跳跃或其他东西。

我正在尝试做的一个简单示例程序是:

.global main

main:
    jmp 0x4
    ret

由于jmp指令长4个字节且相对跳转偏离跳转地址+ 1,因此这应该是一个奇特的无操作。但是,编译和运行此代码将导致分段错误。

对我来说真正的益智之处在于将其编译到对象级别然后反汇编目标文件显示它看起来像汇编程序正在进行相对跳转,但是在编译文件后链接器将其更改为另一种类型跳跃

例如,如果上面的代码位于名为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   

这看起来像汇编程序正确地进行了相对跳转,尽管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

这对我看起来像链接器重写了jmp语句,或者将5 in替换为另一个地址。

所以我的问题归结为,我做错了什么?

我是否错误地指定了偏移量?我误解了相对跳跃是如何工作的吗? gcc是否试图确保我的代码中没有做危险的事情?

3 个答案:

答案 0 :(得分:17)

实际上,汇编程序认为你试图进行绝对跳转。但是,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 00 00'序列,并且没有链接器交互。

请注意,上面的代码可能会崩溃,因为相对偏移量是在偏移量之后的地址中计算的(即下一个操作码的地址,此处为ret)。这将在ret之后跳入4个字节的无人区,并且可能出现段错误或奇怪的事情。

答案 1 :(得分:15)

如果你正在使用默认使用AT&amp; T语法的GCC的GAS汇编程序,那么相对寻址uses the dot ('.') to represent the current address being assembled的语法(很像$伪符号用于Intel / 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

答案 2 :(得分:5)

我认为汇编程序正在获取一个绝对地址并为您计算地址偏移量。第一种情况下的零可能存在,因为它是修正表的一部分,偏移量在链接阶段计算。

我的汇编语言技能有点生疏,但我认为你可以这样做:

.global main

main:
    jmp getouttahere
getouttahere:
    ret

或者如果你真的希望它看起来相对:

.global main

main:
    jmp .+5
    ret

如果我错了,请温柔;已经很久了。