飘云阁

 找回密码
 加入我们

QQ登录

只需一步,快速开始

查看: 11476|回复: 6

[x64] X64 传参规则的研究

[复制链接]
  • TA的每日心情
    开心
    2015-8-2 16:07
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2014-12-23 12:16:09 | 显示全部楼层 |阅读模式
    本帖最后由 F8LEFT 于 2014-12-23 12:21 编辑

           可能这个标题没有多大的新意,实际内容也没有多少。不过我还是把我研究到的东西说一下好了,也可以当做是扫盲,就看看也是不错的。       一些基础的东西小Q老师已经说过了,请参考帖子:https://www.chinapyg.com/thread-74565-1-1.html
           我再补充一下其他的东西。
           测试软件 VS2012
           在x64上面,函数调用更加类似于 FastCall的调用模式,它首先会使用寄存器 rcx,rdx,r8,r9(整形参数) 或者 xmm0,xmm1,xmm2,xmm3 (浮点型参数)来传递参数,最后才是使用堆栈来进行传递。

           那么问题来了,如果函数的头4个参数是 包含 整形 与 浮点型 的,比如 double Test(LONGLONG i1, LONGLONG i2, double a3, double a4),那么是否使用的是 rcx,rdx,xmm0,xmm1这4个寄存器呢?在VC2012上进行相关的测试:
    double Test(LONGLONG i1, LONGLONG i2, double a3, double a4)
    {
            return i1 + i2 + a3 + a4;
    }

            调用Test的代码:

    1. 000000013F2B104D | F2 0F 10 1D 9B 58 00 00  | movsd xmm3,qword ptr ds:[<__real@4011851eb851eb85>] | ;a4 = 4.4
    2. 000000013F2B1055 | F2 0F 10 15 83 58 00 00  | movsd xmm2,qword ptr ds:[<__real@40091eb851eb851f>] | ;a3 = 3.3
    3. 000000013F2B105D | BA 02 00 00 00           | mov edx,2                                           | ;i2 = 2
    4. 000000013F2B1062 | B9 01 00 00 00           | mov ecx,1                                           | ;i1 = 1
    5. 000000013F2B1067 | E8 A3 FF FF FF           | call <x64text.@ILT+10(?Test@@YAN_J0NN@Z)>           | ;call Test
    复制代码
           call  Test中对参数的处理:
    1. 000000013F2B1130 | F2 0F 11 5C 24 20        | movsd qword ptr ss:[rsp+20],xmm3                    | ;a4 入栈
    2. 000000013F2B1136 | F2 0F 11 54 24 18        | movsd qword ptr ss:[rsp+18],xmm2                    | ;a3 入栈
    3. 000000013F2B113C | 48 89 54 24 10           | mov qword ptr ss:[rsp+10],rdx                       | ;i2 入栈
    4. 000000013F2B1141 | 48 89 4C 24 08           | mov qword ptr ss:[rsp+8],rcx                        | ;i1 入栈
    复制代码
           可以看到,实际使用的寄存器为 rcx,rdx,xmm2,xmm3,后面的参数都是使用堆栈。其他情况都是类似的。所以,实际传参情况是这样的。
    第1个参数使用 rcx(整形参数) 或 xmm0 (浮点型参数)
    第2个参数使用 rdx(整形参数) 或 xmm1 (浮点型参数)
    第3个参数使用 r8  (整形参数) 或 xmm2 (浮点型参数)
    第4个参数使用 r9  (整形参数) 或 xmm3 (浮点型参数)
    第5个参数使用堆栈
    第6个参数使用堆栈
    第7个........
            上面我说过了,X64的调用非常的像fastcall方式的。那么对于堆栈平衡问题是如何处理的呢?我们知道 cdcel是在call外通过 add rsp, 0x??,来平衡的, WINAPI 是在call内通过 ret 0x?? 来平衡的。好的,那么我们再测试一下。double WINAPI Test(LONGLONG i1, LONGLONG i2, double a3, double a4, double a5)
    {
            return i1 + i2 + a3 + a4;
    }
          汇编代码:
    1. 000000013F8D1045 | 48 83 EC 40              | sub rsp,40                                          |;新建堆栈空间
    2. ...........
    3. 000000013F8D105D | F2 0F 10 05 9B 58 00 00  | movsd xmm0,qword ptr ds:[<__real@4016000000000000>] | ;a5 = 5.5
    4. 000000013F8D1065 | F2 0F 11 44 24 20        | movsd qword ptr ss:[rsp+20],xmm0                    | ;a5 入栈
    5. 000000013F8D106B | F2 0F 10 1D 7D 58 00 00  | movsd xmm3,qword ptr ds:[<__real@401199999999999a>] | ;a4 = 4.4
    6. 000000013F8D1073 | F2 0F 10 15 65 58 00 00  | movsd xmm2,qword ptr ds:[<__real@400a666666666666>] | ;a3 = 3.3
    7. 000000013F8D107B | BA 02 00 00 00           | mov edx,2                                           | ;i2 = 2
    8. 000000013F8D1080 | B9 01 00 00 00           | mov ecx,1                                           | ;i1 = 1
    9. 000000013F8D1085 | E8 7B FF FF FF           | call <x64text.@ILT+0(?Test@@YAN_J0NNN@Z)>           | ;call Test
    10. 000000013F8D108A | F2 0F 2C C0              | cvttsd2si eax,xmm0                                  | ;eax:BaseThreadInitThunk
    11. .........
    12. 000000013F8D10AD | 48 83 C4 40              | add rsp,40                                          | ;释放堆栈空间
    复制代码
        程序不再是每经过一个call,堆栈就变化一次了。而是在每一个call开头,先计算出所需要的堆栈总空间,然后一次新建全部的空间,到最后才全部释放。在调用到其他call的时候就不会特别的再次新建堆栈空间来存放参数,而是直接利用前面新建的空间来进行,入栈操作也从 Push XXXXXXXX改为了 movsd ss:[rsp + ptr], XMM?,或者 mov ss:[rsp + ptr], r?x。因为堆栈不会再变了,所以你不需要非常郁闷的去看到rsp的变化了。
        那么call里面又是如何调用参数的呢:
        未处理参数时堆栈数据
    1. 000000000025F6A8 : 000000013F43108A return to XXXXXXXXXXXXXXXX
    2. 000000000025F6B0 : CCCCCCCCCCCCCCCC
    3. 000000000025F6B8 : CCCCCCCCCCCCCCCC
    4. 000000000025F6C0 : CCCCCCCCCCCCCCCC
    5. 000000000025F6C8 : CCCCCCCCCCCCCCCC
    6. 000000000025F6D0 : 4016000000000000
    复制代码
        处理的相关代码:
    1. 000000013F431150 | F2 0F 11 5C 24 20        | movsd qword ptr ss:[rsp+20],xmm3        | ;a4 入栈
    2. 000000013F431156 | F2 0F 11 54 24 18        | movsd qword ptr ss:[rsp+18],xmm2        | ;a3 入栈
    3. 000000013F43115C | 48 89 54 24 10           | mov qword ptr ss:[rsp+10],rdx           | ;i2 入栈
    4. 000000013F431161 | 48 89 4C 24 08           | mov qword ptr ss:[rsp+8],rcx            | ;i1 入栈
    复制代码
         处理后堆栈的数据:
    1. 000000000025F6A8 : 000000013F43108A return to XXXXXXXXXXXXXXXX
    2. 000000000025F6B0 : 0000000000000001
    3. 000000000025F6B8 : 0000000000000002
    4. 000000000025F6C0 : 400A666666666666
    5. 000000000025F6C8 : 401199999999999A
    6. 000000000025F6D0 : 4016000000000000
    复制代码
         rsp根本没有改变,只是将 rcx,rdx,xmm2,xmm3的值入堆栈而已!!!而且是通过mov的方式。而a5因为call前已经处理了所以call中就没有再弄了。
          call里面通过mov 把参数放到 寄存器 ,call外面通过mov 把参数放到堆栈。实际上与x86也是非常相似的,不过是少了堆栈的大小变化而已。
          所以,我想你应该可以很容易的知道call返回的方式了。
    1. pop edi
    2. ret
    复制代码
         这虽然是WINAPI,但也不再是 ret 0x??的形式了哈哈。因为入栈的时候没有了堆栈的变化了,退出时自然也不需要手动平衡了。
          就只那么多了,这几天以来的成果,如果有错误的话欢迎大家来指出O(∩_∩)O哈!


    评分

    参与人数 3威望 +96 飘云币 +96 收起 理由
    GeekCat + 8 + 8 赞一个!
    zaas + 8 + 8 很给力!
    small-q + 80 + 80 很给力!

    查看全部评分

    PYG19周年生日快乐!
  • TA的每日心情
    慵懒
    2015-8-14 00:08
  • 签到天数: 25 天

    [LV.4]偶尔看看III

    发表于 2014-12-23 13:35:24 | 显示全部楼层
    都开始玩64位的了,真是羡慕你们,可是我不会。。。。
    PYG19周年生日快乐!
  • TA的每日心情
    奋斗
    2016-1-13 12:25
  • 签到天数: 3 天

    [LV.2]偶尔看看I

    发表于 2014-12-24 08:25:17 | 显示全部楼层
    真给力!加油
    PYG19周年生日快乐!
  • TA的每日心情
    无聊
    2024-8-15 21:38
  • 签到天数: 48 天

    [LV.5]常住居民I

    发表于 2014-12-24 14:12:44 | 显示全部楼层
    先来顶顶了
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2014-12-24 18:38:30 | 显示全部楼层
    恩恩,好的谢谢
    PYG19周年生日快乐!
  • TA的每日心情
    无聊
    2017-1-18 03:05
  • 签到天数: 45 天

    [LV.5]常住居民I

    发表于 2014-12-27 08:35:45 | 显示全部楼层
    学习了。    64位目前研究的还不算多!
    PYG19周年生日快乐!
    您需要登录后才可以回帖 登录 | 加入我们

    本版积分规则

    快速回复 返回顶部 返回列表