【问题标题】:how to call printf in C __asm__()? [duplicate]如何在 C __asm__() 中调用 printf? [复制]
【发布时间】:2019-04-16 17:58:09
【问题描述】:

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

#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

我有什么遗漏吗?

【问题讨论】:

  • printf之后可能需要在调用代码中重新调整栈
  • 为什么不写个C程序调用printf看看反汇编呢?
  • 我诚实的建议:不要从asm 语句中调用函数。这不是内联汇编的目的。内联汇编是学习汇编语言的一个很差的工具,所以我建议你在熟悉汇编编程之前不要使用它。
  • @amiTheregroot 这不是一个好的工具!只需在汇编文件中编写普通的汇编代码。使用内联汇编器,编译器可以在生成代码时做出各种奇怪的选择,并且需要对编译器如何工作有充分的了解才能使其正确。这不利于学习。
  • 嗯,不要用内联汇编来做C代码能做的事情,也不要用内联汇编来学习汇编。

标签: c assembly


【解决方案1】:

您的代码存在以下问题:

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

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

  • 也就是说,您不能安全地假设编译器会设置一个基指针。要解决此问题,请确保仅引用 rsp 而不是 rbp 或使用扩展 asm 让编译器解决此问题。

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

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

  • 你覆盖了一堆寄存器;除了raxrdi,对printf 的调用可能会覆盖任何调用者保存的寄存器。编译器不知道您这样做了,并且可能会假设所有寄存器都保留了它们之前的值。要解决此问题,请声明适当的 clobber 列表或保存并恢复您计划覆盖的所有寄存器,包括所有调用方保存的寄存器。

所以 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 */
}

【讨论】:

  • 抱歉回复晚了。非常适合组装的菜鸟,您还可以添加我的工作代码吗?
  • @amiTheregroot 我可以。但请记住,这绝不反映您在汇编中实际调用函数的方式。如果你答应我为了学习目的而放弃内联汇编,而是在汇编源文件中用普通汇编编程,我将添加一个示例。
  • lol 好吧会这样做,但仅供参考,无论是不同的源文件还是 c asm,每一滴知识都很重要。需要学习两者以了解优势和劣势。
  • 我能理解这种理想主义。但是,请记住,代码不会很有用,因为大部分代码只是需要在内联汇编中进行,否则不适用于此类事情。
  • @amiTheregroot 我已经添加了请求的示例。
猜你喜欢
  • 1970-01-01
  • 2014-08-02
  • 1970-01-01
  • 2011-05-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-04
  • 1970-01-01
相关资源
最近更新 更多