Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/72.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
GCC/X86,相对跳跃问题_C_Gcc_X86 - Fatal编程技术网

GCC/X86,相对跳跃问题

GCC/X86,相对跳跃问题,c,gcc,x86,C,Gcc,X86,我正试图在x86汇编中进行一次相对的跳跃,但是我无法让它工作。似乎出于某种原因,我的跳跃一直被改写为绝对跳跃或其他什么 我尝试做的一个简单示例程序如下: .global main main: jmp 0x4 ret 由于jmp指令的长度为4字节,且相对跳转与跳转+1的地址偏移,因此这应该是一个奇特的no-op。但是,编译和运行此代码将导致分段错误 对我来说,真正的难题是将其编译到对象级别,然后反汇编对象文件,这表明汇编程序似乎正确地进行了相对跳转,但在编译文件后,链接器将其更

我正试图在x86汇编中进行一次相对的跳跃,但是我无法让它工作。似乎出于某种原因,我的跳跃一直被改写为绝对跳跃或其他什么

我尝试做的一个简单示例程序如下:

.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”可能有用。