调用不带参数的 C 函数 [英] Calling C function which takes no parameters with parameters

查看:31
本文介绍了调用不带参数的 C 函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

关于 C 调用约定和 64/32 位编译之间可能未定义的行为,我有一些奇怪的问题.首先是我的代码:

I have some weird question about probably undefined behavior between C calling convention and 64/32 bits compilation. First here is my code:

int f() { return 0; }

int main()
{
    int x = 42;
    return f(x);
}

如您所见,我使用参数调用 f 而 f 不带参数.我的第一个问题是这个参数在调用它时是否真的给了 f.

As you can see I am calling f with an argument while f takes no parameters. My first question was does this argument is really given to f while calling it.

经过一点 objdump 后,我得到了奇怪的结果.将 x 作为 f 的参数传递时:

After a little objdump I obtained curious results. While passing x as argument of f:

00000000004004b6 <f>:
  4004b6:   55                      push   %rbp
  4004b7:   48 89 e5                mov    %rsp,%rbp
  4004ba:   b8 00 00 00 00          mov    $0x0,%eax
  4004bf:   5d                      pop    %rbp
  4004c0:   c3                      retq   

00000000004004c1 <main>:
  4004c1:   55                      push   %rbp
  4004c2:   48 89 e5                mov    %rsp,%rbp
  4004c5:   48 83 ec 10             sub    $0x10,%rsp
  4004c9:   c7 45 fc 2a 00 00 00    movl   $0x2a,-0x4(%rbp)
  4004d0:   8b 45 fc                mov    -0x4(%rbp),%eax
  4004d3:   89 c7                   mov    %eax,%edi
  4004d5:   b8 00 00 00 00          mov    $0x0,%eax
  4004da:   e8 d7 ff ff ff          callq  4004b6 <f>
  4004df:   c9                      leaveq 
  4004e0:   c3                      retq   
  4004e1:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  4004e8:   00 00 00 
  4004eb:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

不将 x 作为参数传递:

Without passing x as a argument:

00000000004004b6 <f>:
  4004b6:   55                      push   %rbp
  4004b7:   48 89 e5                mov    %rsp,%rbp
  4004ba:   b8 00 00 00 00          mov    $0x0,%eax
  4004bf:   5d                      pop    %rbp
  4004c0:   c3                      retq   

00000000004004c1 <main>:
  4004c1:   55                      push   %rbp
  4004c2:   48 89 e5                mov    %rsp,%rbp
  4004c5:   48 83 ec 10             sub    $0x10,%rsp
  4004c9:   c7 45 fc 2a 00 00 00    movl   $0x2a,-0x4(%rbp)
  4004d0:   b8 00 00 00 00          mov    $0x0,%eax
  4004d5:   e8 dc ff ff ff          callq  4004b6 <f>
  4004da:   c9                      leaveq 
  4004db:   c3                      retq   
  4004dc:   0f 1f 40 00             nopl   0x0(%rax)

如我们所见:

  4004d0:   8b 45 fc                mov    -0x4(%rbp),%eax
  4004d3:   89 c7                   mov    %eax,%edi

当我用 x 调用 f 时会发生这种情况,但因为我不太擅长汇编,所以我不太理解这些行.

happen when I call f with x but because I am not really good in assembly I don't really understand these lines.

否则我尝试了其他方法并开始打印我的程序堆栈.

Otherwise I tried something else and start printing the stack of my program.

将 x 赋予 f 的堆栈(以 64 位编译):

Stack with x given to f (compiled in 64bits):

Address of x: ffcf115c
  ffcf1128:          0          0
  ffcf1130:   -3206820          0
  ffcf1138:   -3206808  134513826
  ffcf1140:         42   -3206820
  ffcf1148: -145495616  134513915
  ffcf1150:          1   -3206636
  ffcf1158:   -3206628         42
  ffcf1160: -143903780   -3206784

未将 x 赋予 f 的堆栈(以 64 位编译):

Stack with x not given to f (compiled in 64bits):

Address of x: 3c19183c
  3c191818:          0          0
  3c191820: 1008277568      32766
  3c191828:    4195766          0
  3c191830: 1008277792      32766
  3c191838:          0         42
  3c191840:    4195776          0

出于某种原因,32 位 x 似乎被压入堆栈.

And for some reason in 32bits x seems to be push on the stack.

将 x 赋予 f 的堆栈(以 32 位编译):

Stack with x given to f (compiled in 32bits):

Address of x: ffdc8eac
  ffdc8e78:          0          0
  ffdc8e80:   -2322772          0
  ffdc8e88:   -2322760  134513826
  ffdc8e90:         42   -2322772
  ffdc8e98: -145086016  134513915
  ffdc8ea0:          1   -2322588
  ffdc8ea8:   -2322580         42
  ffdc8eb0: -143494180   -2322736

为什么 x 出现在 32 而不是 64 ???

Why the hell does x appear in 32 but not 64 ???

打印代码:http://paste.awesom.eu/yayg/QYw6&ln

我为什么要问这么愚蠢的问题?

  • 首先是因为我没有找到任何标准来回答我的问题
  • 其次,考虑在不给定参数数量的情况下在 C 中调用可变参数函数.
  • 最后但并非最不重要的一点是,我认为未定义的行为很有趣.

感谢您抽出时间阅读到这里并帮助我理解某些东西或让我意识到我的问题毫无意义.

Thank you for taking the time to read until here and for helping me understanding something or making me realize that my questions are pointless.

推荐答案

答案是,正如您所怀疑的,您正在做的是未定义的行为(在传递多余参数的情况下).

The answer is that, as you suspect, what you are doing is undefined behavior (in the case where the superfluous argument is passed).

实际行为在许多实现中是无害的.一个参数准备在堆栈上,被调用的函数忽略.被调用的函数不负责从堆栈中移除参数,所以没有危害(例如不平衡的堆栈指针).

The actual behavior in many implementations is harmless, however. An argument is prepared on the stack, and is ignored by the called function. The called function is not responsible for removing arguments from the stack, so there no harm (such as an unbalanced stack pointer).

这种无害的行为曾经让 C 黑客能够开发出一个变量参数列表工具,该工具曾经在古老的 Unix 版本中位于 #include 下C 库.

This harmless behavior was what enabled C hackers to develop, once upon a time, a variable argument list facility that used to be under #include <varargs.h> in ancient versions of the Unix C library.

这演变成 ANSI C .

This evolved into the ANSI C <stdarg.h>.

这个想法是:将额外的参数传递给函数,然后动态地遍历堆栈以检索它们.

The idea was: pass extra arguments into a function, and then march through the stack dynamically to retrieve them.

今天不行.例如,如您所见,参数实际上并未放入堆栈,而是加载到RDI 寄存器中.这是 GCC 在 x86-64 上使用的约定.如果您遍历堆栈,您将找不到前几个参数.相比之下,在 IA-32 上,GCC 使用堆栈传递参数:尽管您可以使用fastcall"约定获得基于寄存器的行为.

That won't work today. For instance, as you can see, the parameter is not in fact put into the stack, but loaded into the RDI register. This is the convention used by GCC on x86-64. If you march through the stack, you won't find the first several parameters. On IA-32, GCC passes parameters using the stack, by contrast: though you can get register-based behavior with the "fastcall" convention.

中的 va_arg 宏将正确考虑混合寄存器/堆栈参数传递约定.(或者,更确切地说,当您对可变参数函数使用正确的声明时,它可能会抑制寄存器中尾随参数的传递,以便 va_arg 可以 只是通过记忆.)

The va_arg macro from <stdarg.h> will correctly take into account the mixed register/stack parameter passing convention. (Or, rather, when you use the correct declaration for a variadic function, it will perhaps suppress the passage of the trailing arguments in registers, so that va_arg can just march through memory.)

附言如果您添加了一些优化,您的机器代码可能更容易理解.例如,序列

P.S. your machine code might be easier to follow if you added some optimization. For instance, the sequence

  4004c9:   c7 45 fc 2a 00 00 00    movl   $0x2a,-0x4(%rbp)
  4004d0:   8b 45 fc                mov    -0x4(%rbp),%eax
  4004d3:   89 c7                   mov    %eax,%edi
  4004d5:   b8 00 00 00 00          mov    $0x0,%eax

由于看起来像是一些浪费的数据移动而相当迟钝.

is fairly obtuse due to what look like some wasteful data moves.

这篇关于调用不带参数的 C 函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆