【问题标题】:determining the return type of a function in assembly确定汇编中函数的返回类型
【发布时间】:2016-02-17 21:30:24
【问题描述】:

下面的代码是一个家庭作业,我理解的实际汇编命令。但是我不确定(并且一直在寻找)如何确定函数的返回类型以及函数需要多少参数。教授真的没过好这个(单调终身公主) 任务是读取汇编代码,然后根据汇编代码编写c代码。我再次理解了说明,只是无法弄清楚返回类型是什么或代码需要多少个参数

    .file   "hw5.c"
    .text
    .p2align 4,,15
.globl p1
    .type   p1, @function
p1:
.LFB0:
    .cfi_startproc
    addq    %rsi, %rdi
    addq    %rdi, %rdx
    movq    %rdx, %rax
    ret
    .cfi_endproc

【问题讨论】:

  • '单调终身公主'哈哈,为此点个赞吧:)
  • Assembly 没有“返回类型”的概念,也没有“参数”。
  • 你能进一步解释一下@immibis 吗?如果是这种情况,我发现很难理解如何基于程序集编写有意义的函数。我现在从这个问题中知道,stackoverflow.com/questions/6171172/… 用于返回的寄存器可以帮助确定返回类型,我仍在处理参数。
  • 汇编语言有一个调用约定,您可以使用它来确定上面传递的内容。您会看到使用了 rdirsirdx。碰巧的是,64 位调用约定使用前六个整数参数(从左起),它们按 RDI、RSI、RDX、RCX、R8 和 R9 的顺序传递。因此,在 RDI、RSI 和 RDX 中有 3 个参数传递给您的函数。
  • @immibis 这是我知道的,谢谢你的洞察力。我应该改写我的问题。我想说的是如何翻译 C 返回语句和从程序集传递的参数。我现在看到它在调用约定和返回寄存器中。

标签: c assembly return-type


【解决方案1】:

有关调用约定文档的链接,请参阅 https://stackoverflow.com/tags/x86/info。这看起来像标准的 SysV x86 ABI,被除 Windows 之外的所有设备使用。

这显然是做两次加法64位加法从三个输入寄存器生成结果,然后将结果放入%rax

但是,您无法判断结果是有符号还是无符号,因为代码是相同的。

此外,优化版本会跳过 mov,使用 lea 作为非破坏性添加,输出不是输入之一:

lea  (%rdi,%rdx), %rax

【讨论】:

  • 从输出中可以明显看出它是 Redhat Linux ;-)
  • @MichaelPetch:你是在取笑我的“显然”吗?这还算公平。 :) 我有时会对“简单”的问题感到恼火,并低声回答他们。当我阅读问题以查看它是否真的像看起来那么简单时,我已经有了一个答复。我经常把它作为评论开始,然后意识到它已经变成了答案,然后就这样发布了。
  • 我在最后放了一个眨眼的笑脸。我只是在开玩笑。你可能会说我为什么选择 Redhat Linux。无法从提供的代码中分辨出来,但我碰巧注意到了这一点:ideone.com/nuu1Ao(最底部表明输出是 Redhat)。我去搜索是因为我认为问题中可能遗漏了一些东西。
  • @MichaelPetch:我知道你在开玩笑,但我不知道是什么玩笑>。-O1?该文件中的p2 使用了无符号比较技巧(所以可能不是-O0),但p3 看起来优化得很糟糕(可能是imul $100, %edi, %eax / add %esi, %eax)。
  • 它是生成的代码,但我不太确定什么编译选项,但部分似乎已优化而部分没有。所以高于-O0 和编译器的年龄可能解释了一些糟糕/错过的优化。我猜-O1 也是。
【解决方案2】:

您应该查看处理器/平台的调用约定以了解低级实现。

在调用子程序之前,调用者应该保存指定为调用者保存的某些寄存器的内容。调用者保存的寄存器是 EAX、ECX、EDX[x86]。由于被调用的子程序可以修改这些寄存器,如果调用者在子程序返回后依赖它们的值,则调用者必须将这些寄存器中的值压入堆栈(以便子程序返回后可以恢复它们。 要将参数传递给子例程,请在调用之前将它们压入堆栈。参数应该以相反的顺序推送(即最后一个参数在前)。由于堆栈向下增长,第一个参数将存储在最低地址(这种参数反转在历史上用于允许函数传递可变数量的参数)。 [来自 x86 调用约定]

【讨论】:

  • 第二段在这里并不重要,这是一个叶函数,它只使用保存其参数的寄存器。
  • 程序集显然使用了x86-64,它具有完全不同的调用约定。
【解决方案3】:

有 3 个参数。它们是 rsi、rdi 和 rdx。返回值以 rax 为单位。函数对三个参数求和。参数可以有符号或无符号,因为 Addq 可以同时使用这两种方法。所有参数和返回值都应该被认为具有相同的类型(有符号或无符号 64 位整数)。

这是我在 x64 上从未见过的调用约定。因此,在转换为 C 之后,除非您指定自定义调用约定(假设编译器支持该约定),否则编译器不会生成相同的程序集。

一旦你定义了一个调用约定,你就可以说出参数的顺序是什么。在那之前,您不知道 rsi/rdi/rdx 是第一个、第二个还是第三个参数。

【讨论】:

  • 这是 SysV 调用约定,除 Windows 之外的所有设备都使用。 (Linux、OS X、Solaris 等)
  • X86_64 有一个定义的调用约定,所以你知道顺序是什么。
  • @DavidC.Rankin 我不知道调用约定 (SysV)。没有那个我不能说 args 的顺序是什么。顺便说一句,正如彼得指出的那样,没有单一的 x86_64 调用约定。
  • 我明白,这就是为什么你没有被它叮咬:)
【解决方案4】:

这篇文章在很大程度上回答了我的问题。 Return value of a C function to ASM 在我完全重新考虑我的搜索词选择之前,我没有看到这个。我仍在尝试找出传递给函数的参数数量。如果我是正确的`

addq    %rsi, %rdi
addq    %rdi, %rdx

这两行告诉我通过了两个参数。一个存储在&rsi 另一个存储在%rdi

【讨论】:

  • "addq %rdi, %rdx" 将 rdi 添加到 rdx。您认为 rdx 的初始值来自哪里? (提示:你可能认为它只有两个参数是错误的)。
  • @davmac 是的,你是对的,当我意识到我对第二个 add 命令很傻时,我注意到它是三个。谢谢!
  • @ChristopherJakob 查看System V ABI,它描述了哪个参数是哪个寄存器。
猜你喜欢
  • 1970-01-01
  • 2016-10-18
  • 1970-01-01
  • 1970-01-01
  • 2020-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-09
相关资源
最近更新 更多