ref 传递参数在汇编中的工作原理

How passing parameters by ref works in assembly

我对组装非常陌生,所以请多多包涵。
我有这段代码通过引用函数传递一个值以便修改它:

[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
static void Main()
{
    int a = 5 ;
    Modify(ref a); 
    Console.WriteLine(a); 
    
                      
}
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
static void Modify (ref int a )
{
 
    a = 77 ; 
}

该代码生成的程序集是

Program.Main()
L0000: push ebp
L0001: mov ebp, esp
L0003: push eax
L0004: xor eax, eax
L0006: mov [ebp-4], eax   ******* (the confusing part 1 )
L0009: mov dword ptr [ebp-4], 5  
L0010: lea ecx, [ebp-4]
L0013: call dword ptr [0xa9dc6ac]
L0019: mov ecx, [ebp-4]
L001c: call System.Console.WriteLine(Int32)
L0021: mov esp, ebp
L0023: pop ebp
L0024: ret

我的问题是:在指令 L0006 中:mov [ebp-4], eax 这会将寄存器 eax 的值复制到由 Ebp-4 但 Ebp-4 没有指向任何东西?我缺少什么?

发布未优化的程序集会使事情变得更加混乱,但基本上,行,

push ebp
mov ebp, esp

使 ebp 指向此函数堆栈帧的顶部附近。 (即将 EBP 设置为 帧指针 )。

在函数入口 [esp] 有 return 地址。任何 push 将首先将 esp 减 4(假设是 32 位环境)并将值存储在新的 [esp] 中。使用 push ebp[esp](减 4)存储 ebp 的旧值,而 mov ebp, esp 使 ebp = esp.

   push eax

此指令只是一种紧凑的方式来执行 sub esp, 4 为一个双字 (int) 大小的局部变量保留堆栈 space。在此之前,ESP = EBP,所以新的 space 位于地址 ebp-4,无论它恰好位于 run-time.

xor eax, eax                    ; eax = 0
mov [ebp-4], eax                ; store EAX, possibly to zero-init   int a ?
mov dword ptr [ebp-4], 5

您可以忽略前两行。它在那里是因为您决定关闭优化,这通常会使事情变得更加混乱。最后一行 mov dword ptr [ebp-4], 5 覆盖了 mov [ebp-4], eax.

写入的值

您不能写 mov dword ptr [ebp], 5,因为 [ebp] 正在存储 ebp 的旧值,正如我之前提到的。

根据 fastcall 调用约定,函数调用在 ECX 中获取其 arg。该寄存器使用 LEA 指令加载 指向 [ebp-4] 指针:

lea ecx, [ebp-4]          ; ECX = ebp-4.   (Not [ebp-4], it's not a load)

在C术语中,它就像&a,局部变量的地址。 LEA 计算地址,但随后将其放入目标寄存器而不是使用它从内存加载。

请注意 在第一个函数调用 return 之后 ,内存被重新加载:

mov ecx, [ebp-4]         ; load a  as the arg for the next function call
call System.Console.WriteLine(Int32)