利用函数参数地址寻址的问题

qc1iu published this page · Last modified:

之前在用C语言编程的时候,有些时候会利用stack的布局做一些操作.我之前在 实现垃圾收集器算法的时候就会使用函数参数的地址,用这个地址作为基址在 stack上寻找我想要的数据. 这篇文章 里面详细介绍了stack的布局,以及编译器在编译函数时自动产生的前言和结语等问题. 对这个垃圾收集算法感兴趣的可以看 这里. 我在写这个算法的时候就发现当使用clang编译器编译后,函数参数在stack中的布局 跟预期的不一样.这是 sf上提出的一个问题

后来在工作中涉及到了不少关于stack balance的问题,其中有些需求让我不得不 读反编译后的代码.

对于这样一个函数

 int add(int a, int b)
 {
      return a + b;
 }

其用clang编译后,反编译结果如下

    080483c0 <add>:
    80483c0:   55                      push   %ebp
    80483c1:   89 e5                   mov    %esp,%ebp
    80483c3:   83 ec 08                sub    $0x8,%esp
    80483c6:   8b 45 0c                mov    0xc(%ebp),%eax
    80483c9:   8b 4d 08                mov    0x8(%ebp),%ecx
    80483cc:   89 4d fc                mov    %ecx,-0x4(%ebp)
    80483cf:   89 45 f8                mov    %eax,-0x8(%ebp)
    80483d2:   8b 45 fc                mov    -0x4(%ebp),%eax
    80483d5:   03 45 f8                add    -0x8(%ebp),%eax
    80483d8:   83 c4 08                add    $0x8,%esp
    80483db:   5d                      pop    %ebp
    80483dc:   c3                      ret
    80483dd:   0f 1f 00                nopl   (%eax)

可以看到中间有4句mov指令做了stack拷贝操作.拷贝后stack布局变成


 +---------+
 |high     |
 +---------+
 |101      |<-arg2
 +---------+
 |99       |<-arg1
 +---------+
 |ret      |
 +---------+
 |ebp      |
 +---------+
 |99       |<-a
 +---------+
 |101      |<-b
 +---------+
 |low      |
 +---------+

这一步拷贝操作导致用参数的地址作为质址来寻址会遇出错.当时在实现垃圾收集 算法的时候还以为clang的参数计算顺序问题,或者是ABI的问题,现在看来还是图 样.

回过头来看这个问题,clang这么做在效率上会有损失,但是的确是符合逻辑的. 这样的一个参数拷贝操作之后,使得函数的参数都在自己的frame当中,而不像之 前一样,函数的参数在其caller的frame中. 这个问题也说明,利用函数参数地址来进行stack上的寻址操作是有潜在风险的.