MSVC在x64模式下如何传参?

由于我需要写一个编译器,为了汇编程序和MSVC生成的C++进行交互,必须了解MSVC在x64下如何运作。查了一下基本没有中文的资料,MSDN是有文档的,但是讲的也不太清楚,这里根据这篇 进行一些试验,关键是弄懂 struct 是如何作为参数传递的。

首先你需要阅读上面那个连接,了解 Windows 下 64 位的基本调用规范,包括整数、浮点数参数如何传递等等,然后这篇文章是用来解答上面的文档中需要验证的地方。

实验1:

验证:

Structs/unions of size 8, 16, 32, or 64 bits and __m64 are passed as
if they were integers of the same size.

验证代码:// 我搞错了大小的单位,以为文档说的是 byte ,所以这四个 struct 只有第一个能验证 64 bit 的情况,不过让我们继续往下看。。

struct size8
{
    char pad[8];
};

struct size16
{
    char pad[16];
};

struct size32
{
    char pad[32];
};

struct size64
{
    char pad[64];
};

void size8_callee(struct size8 size_8_param)
{
    size_8_param.pad[0] = '8';
}

void size16_callee(struct size16 size_16_param)
{
    size_16_param.pad[0] = '6';
}

void size32_callee(struct size32 size_32_param)
{
    size_32_param.pad[0] = '2';
}

void size64_callee(struct size64 size_64_param)
{
    size_64_param.pad[0] = '4';
}

void caller()
{
    size8 size_8_argument;
    size16 size_16_argument;
    size32 size_32_argument;
    size64 size_64_argument;

    size8_callee(size_8_argument);
    size16_callee(size_16_argument);
    size32_callee(size_32_argument);
    size64_callee(size_64_argument);
}

这个时候我还没有试着编译,现在编译一下,并且要注意是x64,注意要输出列表文件:

这里写图片描述

然后编译没有通过,报了一个ERROR(MSVC你多管闲事)

使用了未初始化的局部变量“size_32_argument”

为了让生成出来得汇编代码简明扼要,我们不需要初始化,初始化了反而很麻烦。这个 error 其实是个 warning,只是它太严重了,但是有它在其实还可以编译。我发现可以把它 suppress 掉(错误编号是4700,输入4700到禁用特定警告即可):
这里写图片描述

这样就编译通过了。我们来找一找生成的汇编文件,

这里写图片描述

屎它!test1.cod。由于 VS 装插件比较麻烦,我们用 VS Code 来查看它。

我们先看一下 caller 的头部:

; Function compile flags: /Odtp /RTCsu /ZI
; File c:\users\kiritsugu emiya\source\repos\test_param\test_param\test1.cpp
;   COMDAT ?caller@@YAXXZ
_TEXT   SEGMENT
size_8_argument$ = 8
size_16_argument$ = 40
size_32_argument$ = 88
size_64_argument$ = 160
$T7 = 640
$T8 = 688
$T9 = 752
$T10 = 836
$T11 = 868
$T12 = 900
$T13 = 932

这里可以看到我们刚才在代码中出现的几个变量的名字,和几个叫 $T{x} 的名字。后面几个 $T 是干嘛的我还不太清楚,先看看我们关心的这几个参数 size_8_argumentsize_64_argument是怎么处理的。(我现在还没有看)
根据我的猜测,这应该是一系列的偏移量,因为我们把这些数字相减
40-8 = 3288-40 = 48160 - 88 = 72
32 = 16 + 16 48 = 32 + 16 72 = 64 + 8
恰好比结构实际的 size 大一些,多余的部分可能会填充一些 debug 信息。

可以略过跟这几个 $T 相关的变量,看看和第一个 arugment 相关的部分:

; 44   :    size8 size_8_argument;
; 45   :    size16 size_16_argument;
; 46   :    size32 size_32_argument;
; 47   :    size64 size_64_argument;
; 48   : 
; 49   :    size8_callee(size_8_argument);

  0003b 80 bd 44 03 00
    00 00        cmp     BYTE PTR $T10[rbp], 0
  00042 75 0c        jne     SHORT $LN3@caller
  00044 48 8d 0d 00 00
    00 00        lea     rcx, OFFSET FLAT:?caller@@YAXXZ$rtcName$0
  0004b e8 00 00 00 00   call    _RTC_UninitUse
$LN3@caller:
  00050 48 8b 4d 08  mov     rcx, QWORD PTR size_8_argument$[rbp]
  00054 e8 00 00 00 00   call    ?size8_callee@@YAXUsize8@@@Z ; size8_callee

这里我直接把汇编码复制过来,可以看到有一些奇奇怪怪的东西,可能是 Debug 模式下生成的用于调试的东西,现在管不了,我们可以看到,确实如同微软所言,直接放到了对应的整数的位置。

The first four integer arguments are passed in registers. 
Integer values are passed (in order left to right) in RCX, RDX, R8, and R9. 
Arguments five and higher are passed on the stack.

然后我们看看 callee 怎么接住这个参数:

; Function compile flags: /Odtp /RTCsu /ZI
; File c:\users\kiritsugu emiya\source\repos\test_param\test_param\test1.cpp
;   COMDAT ?size8_callee@@YAXUsize8@@@Z
_TEXT   SEGMENT
size_8_param$ = 224
?size8_callee@@YAXUsize8@@@Z PROC           ; size8_callee, COMDAT

; 23   : {
   

$LN3:
  00000 48 89 4c 24 08   mov     QWORD PTR [rsp+8], rcx
  00005 55       push    rbp
  00006 57       push    rdi
  00007 48 81 ec c8 00
    00 00        sub     rsp, 200       ; 000000c8H
  0000e 48 8b ec     mov     rbp, rsp
  00011 48 8b fc     mov     rdi, rsp
  00014 b9 32 00 00 00   mov     ecx, 50            ; 00000032H
  00019 b8 cc cc cc cc   mov     eax, -858993460        ; ccccccccH
  0001e f3 ab        rep stosd
  00020 48 8b 8c 24 e8
    00 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值