在gcc上的内联asm中使用printf函数

时间:2012-07-07 19:54:20

标签: c gcc assembly segmentation-fault inline-assembly

我想在gcc上测试内联asm capabilty。 所以,我在ubuntu 12.04 64位上输入和编译以下代码 但系统在运行时会在屏幕上显示“分段故障”。 我不知道导致问题的原因。

#include <stdio.h>
char Format[]="Hello world %d\n";
int main()
{
    asm
    (
        "movl $3,4(%esp);"
        "movl $Format,(%esp);"
        "call printf;"
    );
    return 0;
}

谢谢你们帮我一个程序新手。 我使用Code :: blocks作为IDE来编写此代码。我曾尝试使用像%rdx这样的64位寄存器,但编译代码时Build消息的日志显示“Error:bad register name`%rdx'”。我认为这意味着Code :: blocks调用的gcc是3​​2位版本,因此无法识别这些寄存器。 我修改代码以保留堆栈空间

#include <stdio.h>
char Format[]="Hello world %d\n";
int main()
{
    asm
    (
        "subl $8,%esp;"         //I  don't know  $4, $8, $12, $16, $20 which is correct
                                        //but I had tried them all but results are still  ''segmentation fault."
        "movl $3,4(%esp);"
        "movl $Format,(%esp);"
        "call printf;"
        "movl %ebp,%esp;"
    );
    return 0;
}

甚至使用-m32作为编译器选项,但它仍然显示“分段错误”。

再次感谢谁帮助。

2 个答案:

答案 0 :(得分:6)

System 64 ABI for x64要求函数的前六个整数/指针参数应该放在寄存器%rdi%rsi%rdx%rcx,{{ 1}}和%r8。堆栈用于传递更多参数。它还要求在调用具有可变数量参数的函数(如%r9)时,printf应设置为%rax寄存器中传递的浮点参数的总数。在您的案例中调用XMM的正确顺序是:

printf()

xorl %eax, %eax movl $Format, %edi movl $3, %esi call printf 应设置为%rax,因为没有传递浮点参数。此代码还使用以下事实:初始化数据的VA通常位于前4 GiB中的某个位置,因此使用较短的32位指令。当然0仍将检查printf的完整内容,以确定格式字符串在内存中的位置。

您的代码使用32位调用约定,理论上如果使用%rdi交叉编译为32位,但您应首先使用-m32之类的内容为参数保留堆栈空间并恢复在使用subl $20, %esp调用之后,否则您要么覆盖addl %20, %esp的堆栈,要么main()将选择错误的返回地址。这是一个完全工作(测试)的C / asm代码,可以在32位模式下编译和运行:

ret

备注:我在每个流水线末尾使用#include <stdio.h> char Format[] = "Hello world, %d\n"; int main (void) { asm ( // Make stack space for arguments to printf "subl $8, %esp\n" "movl $3, 4(%esp)\n" "movl $Format, (%esp)\n" "call printf\n" // Clean-up the stack "addl $8, %esp\n" ); return 0; } $ gcc -m32 -o test.x test.c $ ./test.x Hello world, 3 而不是\n只是为了提高编译器程序集输出的可读性 - 它与代码的正确性无关。

答案 1 :(得分:4)

首先尝试查看一个普通的C程序,看看它给出的是什么(你可以使用gcc -S得到它。)

然后,确定printf调用所需的ASM部分,并在原始程序中重现它。

您在此处遇到calling convention错误。