从汇编代码到C代码

从汇编代码到C代码,c,assembly,att,C,Assembly,Att,这是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) //putti

这是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是否是
intbar(intx)
{

如果(x我会这样注释它:

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代码,在手动稍微“优化”以消除存储/加载对之后,我得到了类似于您的汇编代码的东西,但很明显您当时错了。您从那时起就修复了它。

我不知道您从哪里得到了
x+(x-1)
用于递归调用参数。调用后有一个
add
,您似乎混淆了调用前发生的事情。
leal-1(%ebx),%eax
是一个有效加载的地址指令。它在x86手册中有记录(大部分在线)。它用于存储带偏移量的地址,但有时它用于通过一次移动和常量添加来保存一条指令。我不知道在堆栈上添加
eax
的位置。你必须将其移动到堆栈,这是将其作为参数传递给
bar
@潜伏者所必需的。我意识到我是这样做的我放弃了对它的递归调用,正在对它进行回溯。leal命令从ebx中减去1,并将其存储到eax中,虽然正确吗?是的,这是正确的。@潜伏者我重新查看了它,并再次尝试解决方案。我认为它实际上只是将值从x相加到1。我将简化
推送%ebp/mov%上的注释esp,%ebp
//堆栈帧样板文件
“调用堆栈”甚至不完全准确,因为它是在推送之后完成的。而且,“展开”意味着(对我来说)按照堆栈帧的链接列表将调用链备份到某个父函数的堆栈帧。我会说“将堆栈指针还原到我们保存寄存器的位置”之类的东西。@PeterCordes:很公平——我现在用“序言”和“尾声”替换了这些注释,我认为这是非常标准的。是的,我喜欢它。它不是函数逻辑的一部分,只是为ABI实现它的一部分(使用一些保留调用的寄存器和通常的堆栈帧约定)。这不是跟踪实际逻辑以查看函数的功能时需要关注的内容。
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
                         // }
int bar (int x)
{
  if (x > 1)
    return bar(x - 1) + x;

  return 1;
}