1

我想在 gcc 上测试内联 asm 功能。因此,我在 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 来编写这段代码。我曾尝试使用 64 位寄存器,例如 %rdx,但编译代码时生成消息的日志显示“错误:错误的寄存器名称 `%rdx'”。我认为这意味着 Code::blocks 调用的 gcc 是 32 位版本,因此它无法识别这些寄存器。我修改代码以保留堆栈空间

#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 作为编译器选项,但它仍然显示“分段错误”。

再次感谢谁提供帮助。

4

2 回答 2

5

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

xorl %eax, %eax
movl $Format, %edi
movl $3, %esi
call printf

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

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

#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代替只是为了提高编译器装配输出的可读性 - 它与代码的正确性无关。;

于 2012-07-07T20:37:07.720 回答
4

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

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

你在这里有一个调用 约定错误。

于 2012-07-07T20:02:21.463 回答