从汇编到 C 代码

Posted

技术标签:

【中文标题】从汇编到 C 代码【英文标题】:Going from Assembly to C code 【发布时间】: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 的值相加。 【参考方案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 相加

【讨论】:

以上是关于从汇编到 C 代码的主要内容,如果未能解决你的问题,请参考以下文章

36.从汇编到C(bl1到bl2)

Keil stm32,使用汇编,分散文件和c。如何将c代码入口点导出到程序集?

从汇编到C

SDRAM和重定位---开始在汇编代码中调用 C 语言

从汇编代码和骨架 C 导出数组的大小

PWN