我正在尝试使用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
我有什么想念的吗?
答案 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字节的倍数。
您覆盖了一堆寄存器;除rax
和rdi
之外,对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 */
}