PC相对CALL与GCC内联汇编

时间:2014-08-22 23:27:31

标签: c gcc assembly x86-64 inline-assembly

我写了以下示例代码。但它产生了在r / m32"中给出的接近,绝对间接的地址。 (如[1]所示)调用指令的变体,由于地址被解释为绝对的,因此失败并出现分段错误。有没有办法用GCC内联汇编生成PC相对调用?顺便说一句,代码正确地计算了相对距离,所以没有问题。

#include <stdio.h>

void print_fn() {
  printf("Hello there..\n");
}

int main() {

  long relative = (long) ((unsigned char*)&&label - (unsigned char*)print_fn);
  __asm__ volatile ("call *%0;"
                :
                : "r"(relative)
                );
label:
 ;

 return 0;

}

反汇编输出如下。

Dump of assembler code for function main:
0x00000000004004d4 <+0>:     55 push   %rbp
0x00000000004004d5 <+1>:     48 89 e5   mov    %rsp,%rbp
0x00000000004004d8 <+4>:     ba f5 04 40 00 mov    $0x4004f5,%edx
0x00000000004004dd <+9>:     b8 c4 04 40 00 mov    $0x4004c4,%eax
0x00000000004004e2 <+14>:    48 89 d1   mov    %rdx,%rcx
0x00000000004004e5 <+17>:    48 29 c1   sub    %rax,%rcx
0x00000000004004e8 <+20>:    48 89 c8   mov    %rcx,%rax
0x00000000004004eb <+23>:    48 89 45 f8    mov    %rax,-0x8(%rbp)
0x00000000004004ef <+27>:    48 8b 45 f8    mov    -0x8(%rbp),%rax
0x00000000004004f3 <+31>:    ff d0  callq  *%rax
0x00000000004004f5 <+33>:    b8 00 00 00 00 mov    $0x0,%eax
0x00000000004004fa <+38>:    c9 leaveq 
0x00000000004004fb <+39>:    c3 retq

[1] http://x86.renejeschke.de/html/file_module_x86_id_26.html

2 个答案:

答案 0 :(得分:4)

所有近端和直接call指令都使用相对寻址。所以你真的不需要做任何事情:

 __asm__ volatile ("call %P0" : : "i" (print_fn)
                   : "rax", "rcx", "rdx", "rsi", "rdi",
                     "r8", "r9", "r10", "r11",
                     "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5",
                     "xmm6", "xmm7", "xmm8", "xmm9", "xmm10", "xmm11",
                     "xmm12", "xmm13", "xmm14", "xmm15", "memory", "cc");

这将创建call指令,其相对位移为print_fn。它还告诉编译器该指令将破坏内存和一大堆寄存器,即在Linux上的调用之间不保留的寄存器。

答案 1 :(得分:2)

实际上,您发布的链接包含答案。当您查看CALL的变体列表时,您会注意到所有使用间接寻址的CALL指令都会自动表示它被解释为绝对地址的地址。然而,在gcc的内联汇编中指定call *%0;意味着您希望汇编器使用它:间接寻址,在这种情况下通过寄存器。

不幸的是,除了使用标签之外,我不知道使gcc发出相对call指令的任何其他方法。但是,这自动意味着您需要将函数体添加到内联汇编中。