如何在Linux 64位上从C编写简单的内联asm指令?

时间:2010-07-27 06:41:59

标签: assembly x86 x86-64 cpu-registers

我正在编写一个简单的c程序,我的要求是从程序的某些功能打印RIP(指令指针)。我不想使用ptrace。

我尝试使用内联asm的一件事是: asm(“movl %% rip,%0;”:“= r”(val)) 这应该将我的rip寄存器值复制到变量val,但是我收到了编译错误。

如果我使用ebp / esp作为32位机器的基本指针和堆栈指针,我不会得到任何编译错误,并且我的val已经分配了一些十六进制数。

我在这里几乎没有问题:

1)因为我的机器是63位,上面的指令怎么能读取32位寄存器?

2)为什么我无法读取64位的任何寄存器,是否有任何问题b'caz 'r'

3)当我使用32位的eip时出现编译错误,是否意味着IP寄存器被限制读取?

2 个答案:

答案 0 :(得分:9)

  1. 您的计算机 64 位,而不是63位=)。您能够读取32位寄存器,因为您正在编译程序并将其作为32位可执行文件运行[1]。
  2. 您无法读取64位寄存器,因为您正在编译程序并将程序作为32位可执行文件运行。是的,你有一个64位处理器,但它仍然可以运行32位可执行文件(如果你的操作系统支持它,你明确地支持它),显然你的编译器工具链默认构建32位。如果您使用的是gcc,请尝试使用-m64标志,或阅读编译器文档以获取更多信息。
  3. rip不同,无法直接访问eip寄存器。您可以在答案中以 Jim 描述的方式获取eip的值。
  4. [1]无论如何,您将能够从64位可执行文件读取32位寄存器; 32位寄存器仍可在64位模式下使用,就像您可以在32位模式下访问16位寄存器一样。


    您的示例中仍存在一些问题:

    首先,虽然可以在64位模式下访问rip,但它可以作为寻址模式访问;这不是一个正常的登记册。如果要加载其值,则需要使用LEA,而不是MOV

    其次,由于rip是64位寄存器,因此您需要在指令上使用q后缀而不是l。这是一个解决了这两个问题的示例程序:

    #include <stdio.h>
    #include <inttypes.h>
    
    int main(int argc, char *argv[]) {
        uint64_t ip;
        asm("leaq (%%rip), %0;": "=r"(ip));
        printf("rip is 0x%016" PRIx64 "\n", ip);
        return 0;
    }
    

    这似乎在我的机器上运行得很好。

答案 1 :(得分:5)

您可以通过执行以下操作获取EIP寄存器的值:

    call a  ; this pushes the value of EIP onto the stack
a:          
    pop ebx ; pops the value of EIP into register ebx

然后你就可以阅读ebx了。