【问题标题】:Going from Assembly to C code从汇编到 C 代码
【发布时间】:2015-12-15 02:33:14
【问题描述】:

这是 AT&T 语法

.global bar
.type bar, @function

bar:

pushl %ebp
movl %esp, %ebp
pushl %ebx
subl $20, %esp
movl 8($ebp), %ebx
movl $1, %eax
cmpl $1, %ebx
jle .L3
leal -1(%ebx), %eax  //subtracts 1 from ebx and stores into eax
movl %eax, (%esp)    //putting on stack frame
call bar             //recursive call
addl %ebx, %eax      // adds %ebx and %eax


.L3                  //returns %eax
addl $20, %esp
popl %ebx
popl %ebp
ret                  //end of bar

所以我认为这里发生的基本上是它检查 %ebx 是否

所以我的 C 代码是:

int bar (int x)
{
if (x<= 1)
return 1;
else
return x + bar(x-1);
}

递归调用真的把我骗到这里了。我意识到它使用新的 %eax 寄存器(基本上变成 x-1)调用 bar 。那么它只是返回数字的总和吗?

【问题讨论】:

  • 我看不到你在哪里得到 x+(x-1) 的递归调用参数。有一个add after 通话,您似乎与通话before 发生的事情混为一谈。 leal -1(%ebx),%eax 是加载有效地址指令。它记录在 x86 手册中(很多是在线的)。它用于存储带有偏移量的地址,但有时它用于通过一次执行移动和常量添加来保存一条指令。我没有看到 将eax 添加到堆栈 的位置。您必须将其移动到堆栈,这是将其作为arg传递给bar的必要条件。
  • @lurker 我意识到我搞砸了它的递归调用,我正在努力追溯它。 leal 命令是从 ebx 中减去 1 并将其存储到 eax 中,尽管正确吗?
  • 是的,没错。
  • @lurker 我重新查看了它并再次尝试了解决方案。我认为它实际上只是将 x 到 1 的值相加。

标签: c assembly att


【解决方案1】:

我会这样注释:

bar:                     // bar() {
    pushl %ebp           //   function prologue
    movl %esp, %ebp      //
    pushl %ebx           //
    subl $20, %esp       //
    movl 8($ebp), %ebx   //   %ebx = x
    movl $1, %eax        //   %eax = 1
    cmpl $1, %ebx        //   if (x > 1)
    jle .L3              //   {
    leal -1(%ebx), %eax  //     %eax = x - 1
    movl %eax, (%esp)    //     put (x - 1) on stack
    call bar             //     %eax = bar(x - 1)
    addl %ebx, %eax      //     %eax += x
.L3                      //   }
    addl $20, %esp       //   function epilogue
    popl %ebx            //
    popl %ebp            //
    ret                  //   return %eax
                         // }

所以 C 看起来与您发布的内容相当:

int bar (int x)
{
  if (x > 1)
    return bar(x - 1) + x;

  return 1;
}

出于历史兴趣:我使用 clang -m32 -S 编译了您的原始(不正确)C 代码,在手动“优化”以消除存储/加载对之后,我得到了类似于您的汇编代码的东西,但很清楚您那一刻错了。从那以后你就修好了。

【讨论】:

  • 我会将push %ebp / mov %esp, %ebp 上的cmets 简化为// stack frame boilerplate。 “所谓的堆栈”甚至不完全准确,因为它是在推送之后完成的。此外,“展开”意味着(无论如何对我来说)跟随堆栈帧的链接列表将调用链备份到某个父函数的堆栈帧。我会说“将堆栈指针恢复到我们保存寄存器的位置”之类的。
  • @PeterCordes:很公平——我现在用“prologue”和“epilogue”替换了那些cmets,我认为这很标准。
  • 是的,我喜欢它。它不是函数逻辑的一部分,只是为该 ABI 实现它的一部分(带有一些保留调用的寄存器和通常的堆栈帧约定)。在跟踪实际逻辑以查看函数的作用时,不需要关注什么。
【解决方案2】:
int bar(int x)
{
if (x<= 1)
return 1;
else
return x+bar(x-1);
}

按升序将 x 与 1 相加

【讨论】:

    猜你喜欢
    • 2023-04-10
    • 1970-01-01
    • 2012-03-04
    • 2016-06-01
    • 2018-05-31
    • 2015-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多