2015-09-25 50 views
0

测试是在32-bit x86。我编译了代码gcc 4.2,优化级别o2。我将C代码编译为二进制文件,然后使用objdump对其进行反汇编。这两个函数序言指令序列有什么区别?

下面是用于函数序言说明两个序列:

0804a6f0 <quotearg_n>: 
804a6f0:  8b 44 24 04    mov 0x4(%esp),%eax 
804a6f4:  b9 ff ff ff ff   mov $0xffffffff,%ecx 
804a6f9:  8b 54 24 08    mov 0x8(%esp),%edx 
804a6fd:  c7 44 24 04 40 e1 04 movl $0x804e140,0x4(%esp) 
804a704:  08 
804a705:  e9 c6 fa ff ff   jmp 804a1d0 <quotearg_n_options> 
804a70a:  8d b6 00 00 00 00  lea 0x0(%esi),%esi 


0804a730 <quotearg>: 
804a730:  83 ec 1c    sub $0x1c,%esp 
804a733:  8b 44 24 20    mov 0x20(%esp),%eax 
804a737:  c7 04 24 00 00 00 00 movl $0x0,(%esp) 
804a73e:  89 44 24 04    mov %eax,0x4(%esp) 
804a742:  e8 a9 ff ff ff   call 804a6f0 <quotearg_n> 
804a747:  83 c4 1c    add $0x1c,%esp 
804a74a:  c3      ret 
804a74b:  90      nop 
804a74c:  8d 74 26 00    lea 0x0(%esi,%eiz,1),%esi 

注意,在功能quotearg,注册esp0x1c减少它被用来访问堆栈,并得到一些参数之前。据我的经验,我认为sub然后access模式是相当普遍的O2编译指令。

但是请注意,在函数quotearg_n中,寄存器esp直接与0x4相加以访问堆栈。 (我认为地址为0x804a6f0的指令的含义是将呼叫站点的返回地址注册为eax,我是对的吗?)根据我的观察,第一个函数使用的模式很少见,约为5%的gccO2编译中等大小的C程序。

因此,这里是我的问题:

为什么编译器生成类似quoterag_n方式函数序言说明?前三条指令的确切含义是从地址0x804a6f0开始的?

为什么编译器总是在sub然后access模式之后生成函数序言指令? (如quoterag

我清楚吗?非常感谢

+0

显示生成此代码或引用“quotearg_n_options”代码可能很有用(可能是GNUlib可移植性库或许多等价的函数)知道这些函数是如何定义的,可以让我们准确地知道它在做什么 –

回答

1

sub %esp是一个典型的make-some-room-on-the-stack,然后把args放在那里,然后调用一个函数。您也可以这样做来预留空间以将寄存器中的本地变量溢出到内存中。

第一个是tail-call优化,在将一些值加载到寄存器后跳到quotearg_n_options。最后调用的参数与我们输入quotearg_n时堆栈中的参数相同。

它可以摆脱这一点,因为它不需要任何本地变量的堆栈空间。

我不确定它到底在做什么4(%esp)。我认为这就是其中一个参数存在的地方,因为它将其设置为一个常数。 IIRC,(%esp)是返回地址,4(%esp)是第一个arg。它将第一个参数设置为常量,可能是缓冲区的地址。我不清楚为什么它可能会做其他一些事情,除非它跳到quotearg_n_options的中间,其中%edx的值可能很重要。 (并且可以解释为什么它将堆栈中的其他参数加载到被调用者可以破坏的寄存器中

+0

这可能是gcc针对win32'__vectorcall'的ABI,前两个参数在'ecx'和' edx'。 –