从程序集中返回函数参数

时间:2015-05-23 12:27:58

标签: c assembly x86-64 calling-convention

我正在使用AMD 64位(我认为它不是什么重要的架构)在Linux上,也是64位。用gcc编译到elf64。

我从C ABI看到整数参数通过通用寄存器传递给函数,我可以在我的代码(被调用者)的汇编端找到值。当我需要从被调用者检索调用者的结果时出现问题。

据我所知,RAX获得第一个整数返回值,我可以轻松找到并使用该值。第二个整数返回值通过RDX传递。这就是困扰我的一点。

我还可以从C ABI看到,RDX是用于将第三个整数函数参数从调用者传递给被调用者的寄存器,但我的函数不使用第三个参数。

如何从我的功能中取出RDX?我是否必须在函数中伪造一个参数才能在调用者端引用它?

定点乘法16.16:

从C调用的

看起来像:

typedef long int Fixedpoint;
Fixedpoint _FixedMul(Fixedpoint v1, Fixedpoint v2);

这是函数本身:

_FixedMul:
   push bp
   mov bp, sp
; entering the function EDI contains v1, ESI contains v2. So:
   mov eax, edi   ; eax = v1
   imul dword esi ; eax = v1 * v2
                  ; at this point EDX contains the higher part of the
                  ; imul moltiplication, EAX the lower one.
   add eax, 8000h ; round by adding 2^(-17)
   adc edx, 0     ; whole part of result is in DX
   shr eax, 16    ; put the fractional part in AX
   pop bp
   ret

来自System V Application Binary Interface AMD64 Architecture Processor Supplement

  

返回值算法:

     

根据以下

返回值      
      
  1. 使用分类算法对返回类型进行分类。
  2.   
  3. 如果类型具有类MEMORY,则调用者为返回提供空间   value并将该存储的地址传递给%rdi,就像它是第一个一样   函数的参数。实际上,这个地址变成了“隐藏的”第一个   gument。此存储不得与被叫方可见的任何数据重叠   除此论点之外的其他名称。   返回时,%rax将包含已传入的地址   来自%rdi的来电者。
  4.   
  5. 如果类是INTEGER,则序列%rax的下一个可用寄存器,   使用%rdx。
  6.   

我希望更清楚我的意思。

PS:很抱歉我在评论中提出了混淆。谢谢你的提示。

2 个答案:

答案 0 :(得分:2)

AMD64 calling conventions (System V ABI)指定寄存器RDX的双重用途:它可以用于传递第三个参数(如果有),并且可以用作第二个返回寄存器。 (请参阅第21页的图3.4)

根据功能签名的不同,这些角色既可以用作这些角色,也可以不使用。

那为什么要有2个返回寄存器(即RAX和RDX)?这样一来,函数可以在通用寄存器(例如__int128或具有两个8字节字段的struct)中返回最多128位的值。

因此,要从C调用站点访问RDX寄存器,您只需要调整函数的签名即可。这意味着代替

typedef long int Fixedpoint;
Fixedpoint _FixedMul(Fixedpoint v1, Fixedpoint v2);

声明为:

typedef long int Fixedpoint;
struct Fixedpoint_Pair { // when used as return value:
    Fixedpoint fst;      //   - passed in RAX
    Fixedpoint snd;      //   - passed in RDX
};
typedef struct Fixedpoint_Pair Fixedpoint_Pair;

Fixedpoint_Pair _FixedMul(Fixedpoint v1, Fixedpoint v2);

然后您可以在C代码中像这样访问RDX:

Fixedpoint_Pair r = _FixedMul(a, b);
printf("RDX: %ld\n", r.snd);

参考:

  • 第18,19页:尤其是“聚合(结构和数组)的分类”,第3点:

如果聚合的大小超过一个八字节,则将每个分类 分别。每个八字节初始化为类NO_CLASS。

  • 第22页:“价值的回报”,第3点:

如果该类是INTEGER,则序列%rax的下一个可用寄存器, 使用了%rdx。

答案 1 :(得分:1)

你的英语绝对没问题。

如何传递参数取决于已采用的调用约定 - 两个最常见的约定__stdcall__cdecl,使用堆栈传递所有参数,但例如{{1约定将使用前两个args的寄存器,而在x64中,它仍然不同。有关完整列表,请参阅here

实际返回值在__fastcall中返回;但是,如果该函数收到一些指向"返回"更多的值,这些指针将作为普通参数传递 - 如前所述,通常在堆栈上。