2

我正在编写一个简单但有点具体的程序:

目的:从它的阶乘计算数字
要求:所有计算必须在 gcc 内联 asm 上完成(at&t 语法)

源代码:

#include <iostream>

int main()
{
        unsigned n = 0, f = 0;

        std::cin >> n;

        asm
        (
                "mov %0, %%eax \n"
                "mov %%eax, %%ecx \n"
                "mov 1, %%ebx \n"
                "mov 1, %%eax \n"
                "jmp cycle_start\n"
                "cycle:\n"
                "inc %%ebx\n"
                "mul %%ebx\n"
                "cycle_start:\n"
                "cmp %%ecx, %%eax\n"
                "jnz cycle\n"
                "mov %%ebx, %1 \n":

                "=r" (n):
                 "r" (f)

       );

       std::cout << f;

       return 0;
}

此代码导致 SIGSEV。

英特尔 asm 语法(http://pastebin.com/2EqJmGAV)上的相同程序运行良好。为什么我的“AT&T 计划”失败了,我该如何解决?

#include <iostream>

int main()
{
   unsigned n = 0, f = 0;

   std::cin >> n;

   __asm
   {
      mov eax, n

      mov ecx, eax
      mov eax, 1
      mov ebx, 1
      jmp cycle_start

      cycle:

      inc ebx
      mul ebx

      cycle_start:

      cmp eax, ecx
      jnz cycle

      mov f, ebx
   };
   std::cout << f;

   return 0;
}

UPD:推入堆栈并恢复使用过的寄存器会产生相同的结果:SIGSEV

4

2 回答 2

4

您的输入和输出方式错误。

所以,从改变开始

            "=r" (n):
             "r" (f)

至:

             "=r" (f) :
             "r" (n)

然后我怀疑你会想告诉编译器关于clobbers(你正在使用的不是输入或输出的寄存器):

所以添加:

  : "eax", "ebx", "ecx" 

在上面的两行之后。

我个人会做一些其他的改变:

  1. 使用本地标签(1:2:),这允许在没有“重复标签”的情况下复制代码。
  2. 使用%1而不是%%ebx- 这样,您就不会使用额外的寄存器。
  3. 直接移动%0%%ecx。您稍后将加载1%%eax两个指令中,那么它有什么用途%%eax呢?

【现在,我写的太多了,别人先回答了……】

编辑:而且,正如 Anton 指出的那样,您需要$1加载常量 1,1即从地址 1 读取,这不能很好地工作,并且很可能是您的问题的原因

于 2013-02-03T22:49:12.797 回答
1

希望没有要求只使用 gcc 内联 asm 来解决它。您可以使用 翻译您的 AT&T 示例nasm,然后使用反汇编objdump并查看正确的语法。

我似乎记得如果您的意思是字面常量而不是内存引用,那mov 1,%eax应该是。mov $1,%eax

@MatsPetersson 的回答对于您的内联程序集与编译器(破坏/输入/输出寄存器)的交互非常有用。我专注于你得到的原因SIGSEGV,阅读地址1确实回答了这个问题。

于 2013-02-03T22:47:49.443 回答