tkzn.io

Windows のx64呼び出し規則

Updated: 2022-10-30, Posted: 2022-10-30

まずは、x64はどんなレジスタがあるのか。

64bit 下位32bit 下位16bit 下位8bit 用途
RAX EAX AX AL 汎用
RCX ECX CX CL 汎用
RDX EDX DX DL 汎用
RBX EBX BX BL 汎用
RSI ESI SI SIL 汎用
RDI EDI DI DIL 汎用
RSP ESP SP SPL スタックポインタレジスタ
RBP EBP BP BPL ベースポインタレジスタ
R8 R8D R8W R8B 汎用
R9 R9D R9W R9B 汎用
R10 R10D R10W R10B 汎用
R11 R11D R11W R11B 汎用
R12 R12D R12W R12B 汎用
R13 R13D R13W R13B 汎用
R14 R14D R14W R14B 汎用
R15 R15D R15W R15B 汎用
RIP インストラクションポインタ

引数の渡し方

RCX、RDX、R8、および R9

呼び出し元保存

R10,R11

呼び出し先保存

R12,R13,R14,R15,RDI,RSI,RBX,RBP

void func()の場合

void entry()
{
  func();
}

void func()
{
  return;
}
0000000000401000 <entry>:
  401000:       48 83 ec 28             sub    $0x28,%rsp
  401004:       e8 07 00 00 00          callq  401010 <func>
  401009:       90                      nop
  40100a:       48 83 c4 28             add    $0x28,%rsp
  40100e:       c3                      retq
  40100f:       90                      nop

0000000000401010 <func>:
  401010:       c3                      retq
  401011:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)

void func()の場合、関数内部で変数宣言

0x100+8確保されている
8はアライメントのためかもしれない。(16byteでアライメントしないといけないため)

pushはSPを引いてからストア

スタック 内容
0x1000 str[0] ここが必ず16byteでオフセットされる。
0x10FF str[256]
0x1108 アライメント
0x1110 リターンアドレス
// clang abi_test_002.c -o a.exe -eentry -nostartfiles -nostdlib -mwindows -luser32
// objdump -d a.exe

void func()
{
  char str[256];
  return;
}

void entry()
{
  func();
}
0000000000401000 <func>:
  401000:       48 81 ec 08 01 00 00    sub    $0x108,%rsp
  401007:       48 81 c4 08 01 00 00    add    $0x108,%rsp
  40100e:       c3                      retq
  40100f:       90                      nop

0000000000401010 <entry>:
  401010:       48 83 ec 28             sub    $0x28,%rsp
  401014:       e8 e7 ff ff ff          callq  401000 <func>
  401019:       90                      nop
  40101a:       48 83 c4 28             add    $0x28,%rsp
  40101e:       c3                      retq
  40101f:       90                      nop

int func()の場合 戻り値はEAXに入れている

// clang abi_test_003.c -o a.exe -eentry -nostartfiles -nostdlib -mwindows -luser32
// objdump -d a.exe

int func()
{
  return 1;
}

void entry()
{
  func();
}
0000000000401000 <func>:
  401000:       b8 01 00 00 00          mov    $0x1,%eax
  401005:       c3                      retq
  401006:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  40100d:       00 00 00

0000000000401010 <entry>:
  401010:       48 83 ec 28             sub    $0x28,%rsp
  401014:       e8 e7 ff ff ff          callq  401000 <func>
  401019:       90                      nop
  40101a:       48 83 c4 28             add    $0x28,%rsp
  40101e:       c3                      retq
  40101f:       90                      nop

int func(int)の場合 引数あり

スタック 内容 RSPの参照先
0x1108 RAXの値 <-ここ 
0x1110 リターンアドレス  
スタック 内容 RSPの参照先
0x1108 RAXの値(下位32bitは破壊される) <-ここ 
0x110C 引数1  
0x1110 リターンアドレス  

401000、401009のpush、popが意味分からないが、 
おそらくsubするより早いから使っているのだろう。RAX,RCXに意味はないのだろう。

// clang  -o a.exe -eentry -nostartfiles -nostdlib -mwindows -luser32 abi_test_004.c
// objdump -d a.exe

int func(int a)
{
  return a;
}

void entry()
{
  func(10);
}
0000000000401000 <func>:
  401000:       50                      push   %rax
  401001:       89 4c 24 04             mov    %ecx,0x4(%rsp)
  401005:       8b 44 24 04             mov    0x4(%rsp),%eax
  401009:       59                      pop    %rcx
  40100a:       c3                      retq
  40100b:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

0000000000401010 <entry>:
  401010:       48 83 ec 28             sub    $0x28,%rsp
  401014:       b9 0a 00 00 00          mov    $0xa,%ecx
  401019:       e8 e2 ff ff ff          callq  401000 <func>
  40101e:       90                      nop
  40101f:       48 83 c4 28             add    $0x28,%rsp
  401023:       c3                      retq
  401024:       0f 1f 40 00             nopl   0x0(%rax)

int func(int)の場合 ローカル変数を確保

スタック 内容 RSPの参照先
0x1108 RAXの値 <-ここ 
0x1110 リターンアドレス  
スタック 内容 RSPの参照先
0x1108 RAXの値 <-ここ 
0x110C 引数1  
0x1110 リターンアドレス  
スタック 内容 RSPの参照先
0x1108 ローカル変数 <-ここ 
0x110C 引数1  
0x1110 リターンアドレス  

401011からのNOPが興味深い

// clang  -o a.exe -eentry -nostartfiles -nostdlib -mwindows -luser32 abi_test_005.c
// objdump -d a.exe

int func(int a)
{
  int b;
  b = a;
  return b;
}

void entry()
{
  func(10);
}
0000000000401000 <func>:
  401000:       50                      push   %rax
  401001:       89 4c 24 04             mov    %ecx,0x4(%rsp)
  401005:       8b 44 24 04             mov    0x4(%rsp),%eax
  401009:       89 04 24                mov    %eax,(%rsp)
  40100c:       8b 04 24                mov    (%rsp),%eax
  40100f:       59                      pop    %rcx
  401010:       c3                      retq
  401011:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  401018:       00 00 00
  40101b:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

0000000000401020 <entry>:
  401020:       48 83 ec 28             sub    $0x28,%rsp
  401024:       b9 0a 00 00 00          mov    $0xa,%ecx
  401029:       e8 d2 ff ff ff          callq  401000 <func>
  40102e:       90                      nop
  40102f:       48 83 c4 28             add    $0x28,%rsp
  401033:       c3                      retq
  401034:       0f 1f 40 00             nopl   0x0(%rax)

int func(long long int)の場合 ローカル変数(long long int)を確保

push,popがsub,addにやはり変わっている。

スタック 内容 RSPの参照先
0x1100 <-ここ
0x1108
0x1110 リターンアドレス
スタック 内容 RSPの参照先
0x1100 ローカル変数 <-ここ 
0x1108 rcx 引数1  
0x1110 リターンアドレス  
// clang  -o a.exe -eentry -nostartfiles -nostdlib -mwindows -luser32 abi_test_006.c
// objdump -d a.exe

int func(long long int a)
{
  long long int b;
  b = a;
  return b;
}

void entry()
{
  func(10);
}
0000000000401000 <func>:
  401000:       48 83 ec 10             sub    $0x10,%rsp
  401004:       48 89 4c 24 08          mov    %rcx,0x8(%rsp)
  401009:       48 8b 44 24 08          mov    0x8(%rsp),%rax
  40100e:       48 89 04 24             mov    %rax,(%rsp)
  401012:       48 8b 04 24             mov    (%rsp),%rax
  401016:       48 83 c4 10             add    $0x10,%rsp
  40101a:       c3                      retq
  40101b:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

0000000000401020 <entry>:
  401020:       48 83 ec 28             sub    $0x28,%rsp
  401024:       b9 0a 00 00 00          mov    $0xa,%ecx
  401029:       e8 d2 ff ff ff          callq  401000 <func>
  40102e:       90                      nop
  40102f:       48 83 c4 28             add    $0x28,%rsp
  401033:       c3                      retq
  401034:       0f 1f 40 00             nopl   0x0(%rax)

int func(int,int)の場合 ローカル変数(int)を確保

スタック 内容 RSPの参照先
0x1100 <-ここ 
0x1104 ローカル変数  
0x1108 EDX  
0x110C ECX  
0x1110 リターンアドレス  
// clang  -o a.exe -eentry -nostartfiles -nostdlib -mwindows -luser32 abi_test_007.c
// objdump -d a.exe

int func(int a, int b)
{
  int c;
  c = a;
  return b;
}

void entry()
{
  func(10, 1);
}
0000000000401000 <func>:
  401000:       48 83 ec 10             sub    $0x10,%rsp
  401004:       89 4c 24 0c             mov    %ecx,0xc(%rsp)
  401008:       89 54 24 08             mov    %edx,0x8(%rsp)
  40100c:       8b 44 24 0c             mov    0xc(%rsp),%eax
  401010:       89 44 24 04             mov    %eax,0x4(%rsp)
  401014:       8b 44 24 08             mov    0x8(%rsp),%eax
  401018:       48 83 c4 10             add    $0x10,%rsp
  40101c:       c3                      retq
  40101d:       0f 1f 00                nopl   (%rax)

0000000000401020 <entry>:
  401020:       48 83 ec 28             sub    $0x28,%rsp
  401024:       b9 0a 00 00 00          mov    $0xa,%ecx
  401029:       ba 01 00 00 00          mov    $0x1,%edx
  40102e:       e8 cd ff ff ff          callq  401000 <func>
  401033:       90                      nop
  401034:       48 83 c4 28             add    $0x28,%rsp
  401038:       c3                      retq
  401039:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)

int func(int a, int b, int c, int d, int e, int f)の場合 ローカル変数(int)を確保

呼び出し元で確保している引数1,2,3,4用の領域をなぜか呼び出し先で使用していないのが興味深い。

entry()でcallする直前のスタック状態

スタック 内容 RSPの参照先
0x00 引数1用の予約領域 <-ここ
0x04  
0x08 引数2用の予約領域  
0x0C  
0x10 引数3用の予約領域  
0x14  
0x18 引数4用の予約領域  
0x1C  
0x20 引数5  
0x24  
0x28 引数6  
0x2C  

func()内でのスタック状態

スタック 内容 RSPの参照先
0x00 ローカル変数 <-ここ
0x08 引数4  
0x0C 引数3  
0x10 (EDX)引数2  
0x14 (ECX)引数1  
0x18 リターンアドレス  
0x20 引数1用の予約領域  
0x24  
0x28 引数2用の予約領域  
0x2C  
0x30 引数3用の予約領域  
0x34  
0x38 引数4用の予約領域  
0x3C  
0x40 引数5  
0x44  
0x48 引数6  
0x4C  
// clang  -o a.exe -eentry -nostartfiles -nostdlib -mwindows -luser32 abi_test_008.c
// objdump -d a.exe

int func(int a, int b, int c, int d, int e, int f)
{
  int g;
  g = a;
  return g;
}

void entry()
{
  func(1, 2, 3, 4, 5, 6);
}
0000000000401000 <func>:
  401000:       48 83 ec 18             sub    $0x18,%rsp
  401004:       8b 44 24 48             mov    0x48(%rsp),%eax
  401008:       44 8b 54 24 40          mov    0x40(%rsp),%r10d
  40100d:       89 4c 24 14             mov    %ecx,0x14(%rsp)
  401011:       89 54 24 10             mov    %edx,0x10(%rsp)
  401015:       44 89 44 24 0c          mov    %r8d,0xc(%rsp)
  40101a:       44 89 4c 24 08          mov    %r9d,0x8(%rsp)
  40101f:       8b 4c 24 14             mov    0x14(%rsp),%ecx
  401023:       89 4c 24 04             mov    %ecx,0x4(%rsp)
  401027:       8b 4c 24 04             mov    0x4(%rsp),%ecx
  40102b:       89 04 24                mov    %eax,(%rsp)
  40102e:       89 c8                   mov    %ecx,%eax
  401030:       48 83 c4 18             add    $0x18,%rsp
  401034:       c3                      retq
  401035:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  40103c:       00 00 00
  40103f:       90                      nop

0000000000401040 <entry>:
  401040:       48 83 ec 38             sub    $0x38,%rsp
  401044:       b9 01 00 00 00          mov    $0x1,%ecx
  401049:       ba 02 00 00 00          mov    $0x2,%edx
  40104e:       41 b8 03 00 00 00       mov    $0x3,%r8d
  401054:       41 b9 04 00 00 00       mov    $0x4,%r9d
  40105a:       c7 44 24 20 05 00 00    movl   $0x5,0x20(%rsp)
  401061:       00
  401062:       c7 44 24 28 06 00 00    movl   $0x6,0x28(%rsp)
  401069:       00
  40106a:       e8 91 ff ff ff          callq  401000 <func>
  40106f:       90                      nop
  401070:       48 83 c4 38             add    $0x38,%rsp
  401074:       c3                      retq
  401075:       0f 1f 00                nopl   (%rax)

スタックを使う計算をやってみる

RSI→RDI→RBP→RBX→R15→R14→R13→R12と使用していく。
最終的にはスタックを使用する。
R10,R11は呼び出し元で保存しているはずなので自由に使っている。

// clang  -o a.exe -eentry -nostartfiles -nostdlib -mwindows -luser32 abi_test_008.c
// objdump -d a.exe

int func(int a, int b, int c, int d, int e, int f)
{
  int g;
  g = ((a * b) + ((c * d) + ((e * f) + ((a * c) + ((a * c) + ((a * c) + ((a * c) + ((a * c) + (a * c)))))))));
  return g;
}

void entry()
{
  func(1, 2, 3, 4, 5, 6);
}
0000000000401000 <func>:
  401000:       56                      push   %rsi
  401001:       57                      push   %rdi
  401002:       55                      push   %rbp
  401003:       53                      push   %rbx
  401004:       48 83 ec 18             sub    $0x18,%rsp
  401008:       8b 44 24 68             mov    0x68(%rsp),%eax
  40100c:       44 8b 54 24 60          mov    0x60(%rsp),%r10d
  401011:       89 4c 24 14             mov    %ecx,0x14(%rsp)
  401015:       89 54 24 10             mov    %edx,0x10(%rsp)
  401019:       44 89 44 24 0c          mov    %r8d,0xc(%rsp)
  40101e:       44 89 4c 24 08          mov    %r9d,0x8(%rsp)
  401023:       8b 4c 24 14             mov    0x14(%rsp),%ecx
  401027:       0f af 4c 24 10          imul   0x10(%rsp),%ecx
  40102c:       8b 54 24 0c             mov    0xc(%rsp),%edx
  401030:       0f af 54 24 08          imul   0x8(%rsp),%edx
  401035:       44 8b 44 24 60          mov    0x60(%rsp),%r8d
  40103a:       44 0f af 44 24 68       imul   0x68(%rsp),%r8d
  401040:       44 8b 4c 24 14          mov    0x14(%rsp),%r9d
  401045:       44 0f af 4c 24 0c       imul   0xc(%rsp),%r9d
  40104b:       44 8b 5c 24 14          mov    0x14(%rsp),%r11d
  401050:       44 0f af 5c 24 0c       imul   0xc(%rsp),%r11d
  401056:       8b 74 24 14             mov    0x14(%rsp),%esi
  40105a:       0f af 74 24 0c          imul   0xc(%rsp),%esi
  40105f:       8b 7c 24 14             mov    0x14(%rsp),%edi
  401063:       0f af 7c 24 0c          imul   0xc(%rsp),%edi
  401068:       8b 5c 24 14             mov    0x14(%rsp),%ebx
  40106c:       0f af 5c 24 0c          imul   0xc(%rsp),%ebx
  401071:       8b 6c 24 14             mov    0x14(%rsp),%ebp
  401075:       0f af 6c 24 0c          imul   0xc(%rsp),%ebp
  40107a:       01 eb                   add    %ebp,%ebx
  40107c:       01 df                   add    %ebx,%edi
  40107e:       01 fe                   add    %edi,%esi
  401080:       41 01 f3                add    %esi,%r11d
  401083:       45 01 d9                add    %r11d,%r9d
  401086:       45 01 c8                add    %r9d,%r8d
  401089:       44 01 c2                add    %r8d,%edx
  40108c:       01 d1                   add    %edx,%ecx
  40108e:       89 4c 24 04             mov    %ecx,0x4(%rsp)
  401092:       8b 4c 24 04             mov    0x4(%rsp),%ecx
  401096:       89 04 24                mov    %eax,(%rsp)
  401099:       89 c8                   mov    %ecx,%eax
  40109b:       48 83 c4 18             add    $0x18,%rsp
  40109f:       5b                      pop    %rbx
  4010a0:       5d                      pop    %rbp
  4010a1:       5f                      pop    %rdi
  4010a2:       5e                      pop    %rsi
  4010a3:       c3                      retq
  4010a4:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  4010ab:       00 00 00
  4010ae:       66 90                   xchg   %ax,%ax

00000000004010b0 <entry>:
  4010b0:       48 83 ec 38             sub    $0x38,%rsp
  4010b4:       b9 01 00 00 00          mov    $0x1,%ecx
  4010b9:       ba 02 00 00 00          mov    $0x2,%edx
  4010be:       41 b8 03 00 00 00       mov    $0x3,%r8d
  4010c4:       41 b9 04 00 00 00       mov    $0x4,%r9d
  4010ca:       c7 44 24 20 05 00 00    movl   $0x5,0x20(%rsp)
  4010d1:       00
  4010d2:       c7 44 24 28 06 00 00    movl   $0x6,0x28(%rsp)
  4010d9:       00
  4010da:       e8 21 ff ff ff          callq  401000 <func>
  4010df:       90                      nop
  4010e0:       48 83 c4 38             add    $0x38,%rsp
  4010e4:       c3                      retq
  4010e5:       0f 1f 00                nopl   (%rax)

複雑な関数のまとめ

  401000:       41 57                   push   %r15
  401002:       41 56                   push   %r14
  401004:       41 55                   push   %r13
  401006:       41 54                   push   %r12
  401008:       56                      push   %rsi
  401009:       57                      push   %rdi
  40100a:       55                      push   %rbp
  40100b:       53                      push   %rbx
  40100c:       48 83 ec 28             sub    $0x28,%rsp
  401010:       8b 84 24 98 00 00 00    mov    0x98(%rsp),%eax  引数6をEAXへ
  401017:       44 8b 94 24 90 00 00    mov    0x90(%rsp),%r10d 引数5をR10Dへ
  40101e:       00
  40101f:       89 4c 24 24             mov    %ecx,0x24(%rsp)
  401023:       89 54 24 20             mov    %edx,0x20(%rsp)
  401027:       44 89 44 24 1c          mov    %r8d,0x1c(%rsp)
  40102c:       44 89 4c 24 18          mov    %r9d,0x18(%rsp)

...なにかしらの処理...

  401111:       8b 44 24 14             mov    0x14(%rsp),%eax
  401115:       48 83 c4 28             add    $0x28,%rsp
  401119:       5b                      pop    %rbx
  40111a:       5d                      pop    %rbp
  40111b:       5f                      pop    %rdi
  40111c:       5e                      pop    %rsi
  40111d:       41 5c                   pop    %r12
  40111f:       41 5d                   pop    %r13
  401121:       41 5e                   pop    %r14
  401123:       41 5f                   pop    %r15
  401125:       c3                      retq