如何在C __asm __()中调用printf?

时间:2019-04-16 17:58:09

标签: c assembly

我正在尝试使用c AAAA打印__asm__,如下所示:

#include <stdio.h>

int main()
{
__asm__("sub $0x150, %rsp\n\t"
        "mov   $0x0,%rax\n\t"
        "lea   -0x140(%rbp), %rax\n\t"
        "movl   $0x41414141,(%rax)\n\t"
        "movb   $0x0, 0x4(%rax)\n\t"
        "lea   -0x140(%rbp), %rax\n\t"
        "mov    %rax, %rdi\n\t"
        "call  printf\n\t");

    return 0;
}

反汇编:

Dump of assembler code for function main:                                                                        
 0x0000000000400536 <+0>:     push   %rbp                                                                      
 0x0000000000400537 <+1>:     mov    %rsp,%rbp                                                                 
 0x000000000040053a <+4>:     sub    $0x150,%rsp                                                               
 0x0000000000400541 <+11>:    mov    $0x0,%rax                                                                 
 0x0000000000400548 <+18>:    lea    -0x140(%rbp),%rax                                                         
 0x000000000040054f <+25>:    movl   $0x41414141,(%rax)                                                        
 0x0000000000400555 <+31>:    movb   $0x0,0x4(%rax)                                                            
 0x0000000000400559 <+35>:    lea    -0x140(%rbp),%rax                                                         
 0x0000000000400560 <+42>:    mov    %rax,%rdi                                                                 
 0x0000000000400563 <+45>:    callq  0x400410 <printf@plt>                                                     
 0x0000000000400568 <+50>:    mov    $0x0,%eax                                                                 
 0x000000000040056d <+55>:    pop    %rbp                                                                      
 0x000000000040056e <+56>:    retq                                                                             
End of assembler dump.

在运行代码时,基本上存在两个问题。 #1不会打印“ AAAA”,#1 RIP到达retq时会抛出segmentation fault

我有什么想念的吗?

1 个答案:

答案 0 :(得分:8)

您的代码存在以下问题:

  • 您的代码的主要问题是调用printf后无法将堆栈指针恢复为其先前的值。编译器不知道您是否修改了堆栈指针,并尝试返回到(%rsp)处的任何地址,从而使程序崩溃。要解决此问题,请将rsp恢复为asm语句开头的值。

  • 您忘记将al设置为0表示没有浮点值被传递到printf。由于printf是可变函数,因此需要此函数。虽然将al设置为一个很高的值(并且所有值都小于或等于0)不会造成任何问题,但是正确设置al仍然是一个好习惯。要解决此问题,请在调用al之前将printf设置为0。

  • 也就是说,您不能安全地假定编译器将建立基本指针。要解决此问题,请确保仅引用rsp而不是rbp或使用扩展的asm来让编译器弄清楚这一点。

  • 请注意不要覆盖堆栈。请注意,rsp下的128个字节称为红色区域,并且也必须保留。在您的代码中这样做很好,因为您分配了足够的堆栈空间来避免此问题。

  • 您的代码默认假定堆栈在输入asm语句时与16个字节的倍数对齐。这也是您无法做出的假设。要解决此问题,请在调用printf之前将堆栈指针对准16字节的倍数。

  • 您覆盖了一堆寄存器;除raxrdi之外,对printf的调用可能会覆盖任何保存有呼叫者的寄存器。编译器不知道您是否这样做,并可能假定所有寄存器都保留了以前的值。要解决此问题,请声明一个适当的清单列表,或者保存并还原您打算覆盖的所有寄存器,包括所有保存有呼叫者的寄存器。

所以TL; DR:不要从内联汇编中调用函数,也不要将内联汇编用作学习工具!很难做到正确,并且不会教给您有关汇编编程的任何有用信息。

纯组装示例

这是我如何在常规汇编中编写您的代码。我建议您这样做:

        .section .rodata     # enter the read-only data section
str:    .string "AAAA"       # and place the string we want to print there

        .section .text       # enter the text section
        .global main         # make main visible to the link editor
main:   push %rbp            # establish...
        mov %rsp, %rbp       # ...a stack frame (and align rsp to 16 bytes)

        lea str(%rip), %rdi  # load the effective address of str to rdi
        xor %al, %al         # tell printf we have no floating point args
        call printf          # call printf(str)

        leave                # tear down the stack frame
        ret                  # return

内联汇编示例

这是在内联汇编中调用函数的方式。了解您永远都不要这样做。甚至不是出于教育目的。这样做太可怕了。如果要使用C调用函数,请使用C代码而不是内联汇编。

也就是说,您可以执行类似的操作。请注意,我们使用扩展程序集使生活更轻松:

int main(void)
{
    char fpargs = 0;         /* dummy variable: no fp arguments */
    const char *str = "AAAA";/* the string we want to print */

    __asm__ volatile (       /* volatile means we do more than just
                                returning a result and ban the compiler
                                from optimising our asm statement away */
        "mov %%rsp, %%rbx;"  /* save the stack pointer */
        "and $~0xf, %%rsp;"  /* align stack to 16 bytes */
        "sub $128, %%rsp;"   /* skip red zone */
        "call printf;"       /* do the actual function call */
        "mov %%rbx, %%rsp"   /* restore the stack pointer */
        :                    /* (pseudo) output operands: */
        "+a"(fpargs),        /* al must be 0 (no FP arguments) */
        "+D"(str)            /* rdi contains pointer to string "AAAA" */
        :                    /* input operands: none */
        :                    /* clobber list */
        "rsi", "rdx",        /* all other registers... */
        "rcx", "r8", "r9",   /* ...the function printf... */
        "r10", "r11",        /* ...is allowed to overwrite */
        "rbx",               /* and rbx which we use for scratch space */
        "cc",                /* and flags */
        "memory");           /* and arbitrary memory regions */

    return (0);              /* wrap this up */
}