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

Net*_* Wu 1 c assembly gcc inline-assembly segmentation-fault

我想在gcc上测试内联asm capabilty.因此,我在64位的ubuntu 12.04上键入并编译以下代码,但系统在运行时会在屏幕上显示"分段错误".我不知道导致问题的原因.

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

谢谢你们帮我一个程序新手.我使用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;
}
Run Code Online (Sandbox Code Playgroud)

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

再次感谢谁的帮助.

Hri*_*iev 6

System V的ABI用于基于x64的任务是,前六个整数/指针参数的函数应该在寄存器中%rdi,%rsi,%rdx,%rcx,%r8%r9.堆栈用于传递更多参数.它还要求在调用具有可变数量的参数(如printf)的函数时,%rax应将其设置为XMM寄存器中传递的浮点参数的总数.printf()在您的情况下调用的正确顺序是:

xorl %eax, %eax
movl $Format, %edi
movl $3, %esi
call printf
Run Code Online (Sandbox Code Playgroud)

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

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

#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
Run Code Online (Sandbox Code Playgroud)

备注:我使用\n而不是;在每个装配线的末尾只是为了提高编译器程序集输出的可读性 - 它与代码的正确性无关.