windbg - C# PInvoke 호출 시 마샬링을 담당하는 함수 분석 - OracleCommand.ExecuteReader에서 OpsSql.Prepare2 PInvoke 호출 분석
지난 경험을 바탕으로,
windbg - C# PInvoke 호출 시 마샬링을 담당하는 함수 분석
; https://www.sysnet.pe.kr/2/0/12065
PInvoke에서 OpsSql.Prepare2의 경우에도 파악해 단순히 기록 차원으로 남깁니다. 우선 이를 위해 간단한 예제를 만들어,
using Oracle.DataAccess.Client;
using System;
using System.Reflection;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
SetOdpnetPath();
string connectionString = "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=testsrv)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=XE)));User Id=hrtest;Password=hrtest;";
using (OracleConnection oracleConnection = new OracleConnection(connectionString))
{
oracleConnection.Open();
OracleCommand cmd = new OracleCommand();
cmd.Connection = oracleConnection;
cmd.CommandText = "SELECT * FROM mytable WHERE NAME = :NAME";
cmd.CommandTimeout = 30;
cmd.BindByName = true;
cmd.Parameters.Add(":NAME", "test");
Console.WriteLine("Debug...");
Console.ReadLine();
cmd.ExecuteReader();
oracleConnection.Close();
}
Console.WriteLine();
}
}
}
Console.ReadLine 시점에 덤프를 떠서 windbg로 열어 봅니다. Reflection 도구 등으로 Oracle.DataAccess.Client.OracleCommand.ExecuteReader의 소스 코드를 살펴보면,
public new OracleDataReader ExecuteReader()
{
if (OraTrace.m_TraceLevel != 0u)
{
OraTrace.Trace(1u, new string[]
{
" (ENTRY) OracleCommand::ExecuteReader()\n"
});
}
OracleDataReader result = this.ExecuteReader(true, false, CommandBehavior.Default);
if (OraTrace.m_TraceLevel != 0u)
{
OraTrace.Trace(1u, new string[]
{
" (EXIT) OracleCommand::ExecuteReader()\n"
});
}
return result;
}
OraTrace.m_TraceLevel에 따라 앞뒤로 Trace 메서드가 실행되는 것을 볼 수 있습니다. 재미를 위해, m_TraceLevel이 실행 중에 어떤 값을 가지고 있는지 확인해 볼까요?
windbg - 닷넷 메모리 덤프에서 정적(static) 필드 값을 조사하는 방법
; https://www.sysnet.pe.kr/2/0/11487
위의 글에 설명한대로 해당 타입의 EEClass를 먼저 구하고,
0:000> !name2ee *!Oracle.DataAccess.Client.OraTrace
...[생략]...
Module: 00007ff99b855bc8
Assembly: Oracle.DataAccess.dll
Token: 0000000002000109
MethodTable: 00007ff99ba11b98
EEClass: 00007ff99b9f7b50
Name: Oracle.DataAccess.Client.OraTrace
...[생략]...
덤프하면 출력에 m_TraceLevel의 값을 볼 수 있습니다.
0:000> !DumpClass /d 00007ff99b9f7b50
Class Name: Oracle.DataAccess.Client.OraTrace
mdToken: 0000000002000109
File: C:\temp\ConsoleApp1\ConsoleApp1\bin\Debug\Oracle.DataAccess.dll
Parent Class: 00007ff9f7682f68
Module: 00007ff99b855bc8
Method Table: 00007ff99ba11b98
Vtable Slots: 4
Total Method Slots: 6
Class Attributes: 100000
Transparency: Transparent
NumInstanceFields: 0
NumStaticFields: 1f
MT Field Offset Type VT Attr Value Name
00007ff9f76ab698 400080f 49c System.Boolean 1 static 0 m_RegistryRead
00007ff9f76a59c0 4000810 4a0 System.String 0 static 0000025919a41420 m_oraOpsDllPath
00007ff9f7720ab0 4000811 444 System.UInt32 1 static 0 m_TraceLevel
00007ff9f7720ab0 4000812 448 System.UInt32 1 static 0 m_TraceOption
00007ff9f7720ab0 4000813 44c System.UInt32 1 static 4096 m_udtCacheSize
00007ff9f76a85a0 4000814 450 System.Int32 1 static 0 m_StmtCacheSize
00007ff9f7720ab0 4000815 454 System.UInt32 1 static 1 m_checkConStatus
00007ff9f7720ab0 4000816 458 System.UInt32 1 static 0 m_dynamicEnlist
00007ff9f76a85a0 4000817 45c System.Int32 1 static 131072 m_FetchSize
00007ff9f76a85a0 4000818 460 System.Int32 1 static 0 m_ociEvents
00007ff9f76a85a0 4000819 464 System.Int32 1 static 1 m_stmtCacheWithUdts
...[생략]...
00007ff9f76a85a0 400082b 498 System.Int32 1 static 1 m_fetchArrayPooling
00007ff9f76ab698 400082c 49e System.Boolean 1 static 0 m_configSectionRead
00007ff9f76a5dd8 400082d 4c8 System.Object 0 static 0000025919a4c968 m_regReadSync
보는 바와 같이 0이군요. ^^
이후 this.ExecuteReader() 내부로 들어가 OpsSql.Prepare2를 호출해 native 코드 내부로 디버깅을 진행해 보면,
// ...[생략]...
if (this.m_addParam && this.m_parameters != null)
{
num5 = this.m_parameters.Count;
if (num5 > 0 && (this.m_addToStmtCache || this.m_pOpoPrmCtx == null || this.m_pOpoPrmCtx->NumValCtxElems < num5))
{
IntPtr zero2 = IntPtr.Zero;
// 최초 호출에는 ptr == null이어서, 6번째 인자 m_pooledCmdText 전달
// 이후에는 pNewCommandText != null이어서, null 전달
try
{
bool flag3 = ptr != null && ptr->pNewCommandText != IntPtr.Zero;
num4 = OpsSql.Prepare2(this.m_opsConCtx, ref this.m_opsErrCtx, ref this.m_opsSqlCtx, ref this.m_opsDacCtx, ref this.m_pOpoSqlValCtx, flag3 ? null : this.m_pooledCmdText, ref zero2, ref ptr, num5);
}
catch (Exception ex)
{
if (OraTrace.m_TraceLevel != 0u)
{
OraTrace.TraceExceptionInfo(ex);
}
num4 = ErrRes.INT_ERR;
throw;
}
finally
{
this.m_executeScalar = false;
if (zero2 != IntPtr.Zero)
{
try
{
Marshal.FreeCoTaskMem(zero2);
}
catch (Exception ex2)
{
if (OraTrace.m_TraceLevel != 0u)
{
OraTrace.TraceExceptionInfo(ex2);
}
}
}
...[생략]...
다음과 같은 callstack 상태까지 옵니다.
0:000> !clrstack
OS Thread Id: 0x5e58 (0)
Child SP IP Call Site
000000759992eac8 00000001800237a0 [InlinedCallFrame: 000000759992eac8] Oracle.DataAccess.Client.OpsSql.Prepare2(IntPtr, IntPtr ByRef, IntPtr ByRef, IntPtr ByRef, Oracle.DataAccess.Client.OpoSqlValCtx* ByRef, System.String, IntPtr ByRef, Oracle.DataAccess.Client.OpoMetValCtx* ByRef, Int32)
== OraOps11w!OpsSqlPrepare2
000000759992eac8 00007ff99b97c99a [InlinedCallFrame: 000000759992eac8] Oracle.DataAccess.Client.OpsSql.Prepare2(IntPtr, IntPtr ByRef, IntPtr ByRef, IntPtr ByRef, Oracle.DataAccess.Client.OpoSqlValCtx* ByRef, System.String, IntPtr ByRef, Oracle.DataAccess.Client.OpoMetValCtx* ByRef, Int32)
000000759992ea70 00007ff99b97c99a DomainBoundILStubClass.IL_STUB_PInvoke(IntPtr, IntPtr ByRef, IntPtr ByRef, IntPtr ByRef, Oracle.DataAccess.Client.OpoSqlValCtx* ByRef, System.String, IntPtr ByRef, Oracle.DataAccess.Client.OpoMetValCtx* ByRef, Int32)
000000759992eb90 00007ff99b979fbe Oracle.DataAccess.Client.OracleCommand.ExecuteReader(Boolean, Boolean, System.Data.CommandBehavior)
000000759992ed50 00007ff99b97933b Oracle.DataAccess.Client.OracleCommand.ExecuteReader()
000000759992ed90 00007ff99b9616b9 ConsoleApp1.Program.Main(System.String[]) [C:\temp\ConsoleApp1\ConsoleApp1\Program.cs @ 55]
000000759992f048 00007ff9faea6c53 [GCFrame: 000000759992f048]
이 시점에 "
windbg - C# PInvoke 호출 시 마샬링을 담당하는 함수 분석" 글에서 다룬 것을 그대로 적용해 pinvoke 층의 분석을 할 수 있습니다. 다음은 그 결과이고,
00000075`9992ea68 00007ff99b97c99a return to PInvoke frame of OraOps11w!OpsSqlPrepare2
00000075`9992ea70 0000025919addbe0 // [rsp + 0h] 4개 IntPtr == garbage
00000075`9992ea78 0023025917fe3460 // IntPtr ByRef == garbage
00000075`9992ea80 000000759992ef00 // IntPtr ByRef == garbage
00000075`9992ea88 000000759992ef78 // IntPtr ByRef == garbage
00000075`9992ea90 0000025919addbe8 // [rsp + 20h] == Oracle.DataAccess.Client.OpoSqlValCtx* ByRef
00000075`9992ea98 0000000000000000 // [rsp + 28h] == System.String
00000075`9992eaa0 000000759992ec90 // [rsp + 30h] == IntPtr ByRef
00000075`9992eaa8 000000759992ec98 // [rsp + 38h] == Oracle.DataAccess.Client.OpoMetValCtx* ByRef
00000075`9992eab0 0000000000000001 // [rsp + 40h] == Int32
00000075`9992eab8 000000759992f200
00000075`9992eac0 00002b6d7a3108bc
00000075`9992eac8 00007ff9fb66e9f0 clr!InlinedCallFrame::`vftable'
00000075`9992ead0 000000759992f048
00000075`9992ead8 00007ff99ba1f920 // [rbp - a8h] == [rbp - 80h] == 00007ff99ba1f920
00000075`9992eae0 00007ff99ba1f920 // [rbp - a0h]
00000075`9992eae8 000000759992ea70 // [rbp - 98h] == rsp
00000075`9992eaf0 00007ff99b97c99a // [rbp - 90h] return of OraOps11w!OpsSqlPrepare2
00000075`9992eaf8 000000759992eb80 // [rbp - 88h] == rbp
00000075`9992eb00 00007ff99ba1f920 // [rbp - 80h] == [rbp - a8h]
00000075`9992eb08 0000025918022140 // [rbp - 78h]
00000075`9992eb10 000000759992ec98 // [rbp - 70h] == pinvoke frame에 전달된 8번째 변수
00000075`9992eb18 000000759992ec90 // [rbp - 68h] == pinvoke frame에 전달된 7번째 변수
00000075`9992eb20 0000000000000000 // [rbp - 60h] == pinvoke frame에 전달된 6번째 변수
00000075`9992eb28 0000025919addbe8 // [rbp - 58h] == pinvoke frame에 전달된 5번째 변수
00000075`9992eb30 0000025919addbe0 // [rbp - 50h] // pinvoke frame의 4번째 인자
00000075`9992eb38 0000025919addbd8 // [rbp - 48h] // pinvoke frame의 3번째 인자
00000075`9992eb40 0000025919addbf0 // [rbp - 40h] // pinvoke frame의 2번째 인자 백업 이후 OpsSqlPrepare2의 두 번째 인자(rdx)로 전달
00000075`9992eb48 000000759992ef78 // push rbx [rbp - 38h]
00000075`9992eb50 0000000000000000 // push rsi [rbp - 30h]
00000075`9992eb58 000000759992ed08 // push rdi [rbp - 28h]
00000075`9992eb60 000000759992f200 // push r12 [rbp - 20h]
00000075`9992eb68 000000759992ef00 // push r13 [rbp - 18h]
00000075`9992eb70 000000759992ef78 // push r14 [rbp - 10h]
00000075`9992eb78 0000000000000004 // push r15 [rbp - 8h]
00000075`9992eb80 000000759992ed40 // push rbp == [pinvoke rbp + 0]
00000075`9992eb88 00007ff99b979fbe // return to Oracle.DataAccess.Client.OracleCommand.ExecuteReader
// Pinvoke 층 호출 후 rsp 위치
00000075`9992eb90 0000025919addc50 // [pinvoke rbp + 10h] == [ExecuteReader rsp + 0h] == garbage
// Pinvoke 층 호출 전 rsp 위치
00000075`9992eb98 00000259324e0420 // [pinvoke rbp + 18h] == [ExecuteReader rsp + 8h] == garbage
00000075`9992eba0 0000000007ffffff // [pinvoke rbp + 20h] == [ExecuteReader rsp + 10h] == garbage
00000075`9992eba8 00007ff99b8594e0 // [pinvoke rbp + 28h] == [ExecuteReader rsp + 18h] == garbage
00000075`9992ebb0 0000025919addbe8 // [pinvoke rbp + 30h] == [ExecuteReader rsp + 20h] pinvoke frame에 전달되는 5번째 변수
00000075`9992ebb8 0000000000000000 // [pinvoke rbp + 38h] == [ExecuteReader rsp + 28h] pinvoke frame에 전달되는 6번째 변수
00000075`9992ebc0 000000759992ec90 // [pinvoke rbp + 40h] == [ExecuteReader rsp + 30h] pinvoke frame에 전달되는 7번째 변수
00000075`9992ebc8 000000759992ec98 // [pinvoke rbp + 48h] == [ExecuteReader rsp + 38h] pinvoke frame에 전달되는 8번째 변수
00000075`9992ebd0 0000007500000001 // [pinvoke rbp + 50h] == [ExecuteReader rsp + 40h] pinvoke frame에 전달되는 9번째 변수
00000075`9992ebd8 0000025919adde38
00000075`9992ebe0 0000000000000000
00000075`9992ebe8 000000759992f200
00000075`9992ebf0 000000759992ef00
00000075`9992ebf8 000000759992ef78
00000075`9992ec00 0000025918022140
00000075`9992ec08 00007ff9f7c12977
00000075`9992ec10 00002b6d7a3108bc
00000075`9992ec18 000000759992eb90
00000075`9992ec20 00002b6d7a3108bc
00000075`9992ec28 00007ff9fb66e9f0
00000075`9992ec30 000000759992f048
00000075`9992ec38 0000000000000000
00000075`9992ec40 00007ff4b8ca0018
00000075`9992ec48 000000759992eb90
00000075`9992ec50 0000000000000000
00000075`9992ec58 000000759992ed40
00000075`9992ec60 0000000000000000
00000075`9992ec68 0000000000000000
00000075`9992ec70 0000000000000000
00000075`9992ec78 0000000000000000
00000075`9992ec80 0000000000000000
00000075`9992ec88 0000025918022140
00000075`9992ec90 0000000000000000
00000075`9992ec98 00000259324e0260 // ptr [rbp-0A8h]
00000075`9992eca0 0000000100000000
00000075`9992eca8 0000000100000001
00000075`9992ecb0 0000000000000000
00000075`9992ecb8 0000000000000000
00000075`9992ecc0 0000000000000000
00000075`9992ecc8 0000000000000000
00000075`9992ecd0 0000000000000001
00000075`9992ecd8 0000000000000000
00000075`9992ece0 0000000000000000
00000075`9992ece8 0000000000000000
00000075`9992ecf0 0000000000000000
00000075`9992ecf8 0000000000000000
00000075`9992ed00 0000000000000000
00000075`9992ed08 000000759992ef78 // push rbx
00000075`9992ed10 0000025919addb50 // push rsi
00000075`9992ed18 000000759992ee40 // push rdi
00000075`9992ed20 000000759992f200 // push r12
00000075`9992ed28 000000759992ef00 // push r13
00000075`9992ed30 000000759992ef78 // push r14 == [rbp - 10h]
00000075`9992ed38 0000000000000004 // push r15
00000075`9992ed40 000000759992ee50 // push rbp == [rbp + 0h]
00000075`9992ed48 00007ff99b97933b // return to ExecuteReader()
00000075`9992ed50 0000025919addb50 // [rbp + 10h] == Oracle.DataAccess.Client.OracleCommand
00000075`9992ed58 00007ff900000001
00000075`9992ed60 0000025919addb50
00000075`9992ed68 0000007500000000
아래의 코드 분석을 바탕으로 알아낸 것입니다. (
!U 명령은 sos.dll이 제공합니다.)
0:000> !U /d 00007ff99b97c99a
Normal JIT generated code
DomainBoundILStubClass.IL_STUB_PInvoke(IntPtr, IntPtr ByRef, IntPtr ByRef, IntPtr ByRef, Oracle.DataAccess.Client.OpoSqlValCtx* ByRef, System.String, IntPtr ByRef, Oracle.DataAccess.Client.OpoMetValCtx* ByRef, Int32)
Begin 00007ff99b97c890, size 141
00007ff9`9b97c890 55 push rbp
00007ff9`9b97c891 4157 push r15
00007ff9`9b97c893 4156 push r14
00007ff9`9b97c895 4155 push r13
00007ff9`9b97c897 4154 push r12
00007ff9`9b97c899 57 push rdi
00007ff9`9b97c89a 56 push rsi
00007ff9`9b97c89b 53 push rbx
00007ff9`9b97c89c 4881ecd8000000 sub rsp,0D8h
00007ff9`9b97c8a3 488dac2410010000 lea rbp,[rsp+110h] // rsp == 00000075`9992ea70 + 0x110 == 00000075`9992eb80
00007ff9`9b97c8ab 4c895580 mov qword ptr [rbp-80h],r10
00007ff9`9b97c8af 488bf1 mov rsi,rcx
00007ff9`9b97c8b2 488d7d90 lea rdi,[rbp-70h]
00007ff9`9b97c8b6 b90e000000 mov ecx,0Eh
00007ff9`9b97c8bb 33c0 xor eax,eax
00007ff9`9b97c8bd f3ab rep stos dword ptr [rdi]
00007ff9`9b97c8bf 488bce mov rcx,rsi
00007ff9`9b97c8c2 4c8bf1 mov r14,rcx
00007ff9`9b97c8c5 488bf2 mov rsi,rdx
00007ff9`9b97c8c8 498bf8 mov rdi,r8
00007ff9`9b97c8cb 498bd9 mov rbx,r9
00007ff9`9b97c8ce 4c8b7d38 mov r15,qword ptr [rbp+38h] // == pinvoke frame에 전달된 6번째 변수
00007ff9`9b97c8d2 4c8b6540 mov r12,qword ptr [rbp+40h] // == pinvoke frame에 전달된 7번째 변수
00007ff9`9b97c8d6 4c8b6d48 mov r13,qword ptr [rbp+48h] // == pinvoke frame에 전달된 8번째 변수
00007ff9`9b97c8da 488d8d48ffffff lea rcx,[rbp-0B8h]
00007ff9`9b97c8e1 498bd2 mov rdx,r10
00007ff9`9b97c8e4 e85785525f call clr!JIT_InitPInvokeFrame (00007ff9`faea4e40)
00007ff9`9b97c8e9 488bcc mov rcx,rsp
00007ff9`9b97c8ec 48898d68ffffff mov qword ptr [rbp-98h],rcx
00007ff9`9b97c8f3 488bcd mov rcx,rbp
00007ff9`9b97c8f6 48898d78ffffff mov qword ptr [rbp-88h],rcx
00007ff9`9b97c8fd 488d8d48ffffff lea rcx,[rbp-0B8h]
00007ff9`9b97c904 48894810 mov qword ptr [rax+10h],rcx
00007ff9`9b97c908 498bce mov rcx,r14 // OraOps11w!OpsSqlPrepare2 전달할 인자
00007ff9`9b97c90b 488975c0 mov qword ptr [rbp-40h],rsi
00007ff9`9b97c90f 48897db8 mov qword ptr [rbp-48h],rdi
00007ff9`9b97c913 48895db0 mov qword ptr [rbp-50h],rbx
00007ff9`9b97c917 488b7530 mov rsi,qword ptr [rbp+30h]
00007ff9`9b97c91b 488975a8 mov qword ptr [rbp-58h],rsi
00007ff9`9b97c91f 33d2 xor edx,edx
00007ff9`9b97c921 4d85ff test r15,r15
00007ff9`9b97c924 7408 je 00007ff9`9b97c92e
00007ff9`9b97c926 4c897da0 mov qword ptr [rbp-60h],r15
00007ff9`9b97c92a 498d570c lea rdx,[r15+0Ch]
00007ff9`9b97c92e 4c896598 mov qword ptr [rbp-68h],r12
00007ff9`9b97c932 4c896d90 mov qword ptr [rbp-70h],r13
00007ff9`9b97c936 8b7550 mov esi,dword ptr [rbp+50h]
00007ff9`9b97c939 4c63c6 movsxd r8,esi
00007ff9`9b97c93c 4c8b4d80 mov r9,qword ptr [rbp-80h]
00007ff9`9b97c940 4d8b4920 mov r9,qword ptr [r9+20h]
00007ff9`9b97c944 4d8b11 mov r10,qword ptr [r9]
00007ff9`9b97c947 4c8b4da8 mov r9,qword ptr [rbp-58h]
00007ff9`9b97c94b 4c894c2420 mov qword ptr [rsp+20h],r9
00007ff9`9b97c950 4889542428 mov qword ptr [rsp+28h],rdx
00007ff9`9b97c955 4c89642430 mov qword ptr [rsp+30h],r12
00007ff9`9b97c95a 4c896c2438 mov qword ptr [rsp+38h],r13
00007ff9`9b97c95f 4c89442440 mov qword ptr [rsp+40h],r8
00007ff9`9b97c964 488b55c0 mov rdx,qword ptr [rbp-40h] // OraOps11w!OpsSqlPrepare2 전달할 인자
00007ff9`9b97c968 4c8b45b8 mov r8,qword ptr [rbp-48h] // OraOps11w!OpsSqlPrepare2 전달할 인자
00007ff9`9b97c96c 4c8b4db0 mov r9,qword ptr [rbp-50h] // OraOps11w!OpsSqlPrepare2 전달할 인자
00007ff9`9b97c970 41bb28000000 mov r11d,28h
00007ff9`9b97c976 488b7580 mov rsi,qword ptr [rbp-80h]
00007ff9`9b97c97a 4889b558ffffff mov qword ptr [rbp-0A8h],rsi
00007ff9`9b97c981 488d3512000000 lea rsi,[00007ff9`9b97c99a]
00007ff9`9b97c988 4889b570ffffff mov qword ptr [rbp-90h],rsi // 00007ff99b97c99a: return of OraOps11w!OpsSqlPrepare2
00007ff9`9b97c98f 48894588 mov qword ptr [rbp-78h],rax // rbp-78, == rax == 0000025918022140
00007ff9`9b97c993 c6400c00 mov byte ptr [rax+0Ch],0
00007ff9`9b97c997 41ffd2 call r10
>>> 00007ff9`9b97c99a 488b7588 mov rsi,qword ptr [rbp-78h]
00007ff9`9b97c99e c6460c01 mov byte ptr [rsi+0Ch],1
00007ff9`9b97c9a2 833db396f35f00 cmp dword ptr [clr!g_TrapReturningThreads (00007ff9`fb8b605c)],0
00007ff9`9b97c9a9 7406 je 00007ff9`9b97c9b1
00007ff9`9b97c9ab ff1587a1f35f call qword ptr [clr!hlpDynamicFuncTable+0x68 (00007ff9`fb8b6b38)] (JitHelp: CORINFO_HELP_STOP_FOR_GC)
00007ff9`9b97c9b1 c6460c01 mov byte ptr [rsi+0Ch],1
00007ff9`9b97c9b5 488b9550ffffff mov rdx,qword ptr [rbp-0B0h]
00007ff9`9b97c9bc 48895610 mov qword ptr [rsi+10h],rdx
00007ff9`9b97c9c0 488d65c8 lea rsp,[rbp-38h]
00007ff9`9b97c9c4 5b pop rbx
00007ff9`9b97c9c5 5e pop rsi
00007ff9`9b97c9c6 5f pop rdi
00007ff9`9b97c9c7 415c pop r12
00007ff9`9b97c9c9 415d pop r13
00007ff9`9b97c9cb 415e pop r14
00007ff9`9b97c9cd 415f pop r15
00007ff9`9b97c9cf 5d pop rbp
00007ff9`9b97c9d0 c3 ret
그리고 다음은 this.ExecuteReader() 측에서 OpsSql.Prepare2를 호출할 당시의 코드와 레지스터의 내용을 정리한 것입니다.
00007ff9`9b979eea 4883b9b000000000 cmp qword ptr [rcx+0B0h],0
00007ff9`9b979ef2 7417 je 00007ff9`9b979f0b
00007ff9`9b979ef4 488b4d10 mov rcx,qword ptr [rbp+10h]
00007ff9`9b979ef8 488b89b0000000 mov rcx,qword ptr [rcx+0B0h]
00007ff9`9b979eff 8b4904 mov ecx,dword ptr [rcx+4]
00007ff9`9b979f02 3b4d90 cmp ecx,dword ptr [rbp-70h]
00007ff9`9b979f05 0f8df4000000 jge 00007ff9`9b979fff
00007ff9`9b979f0b 33c9 xor ecx,ecx
00007ff9`9b979f0d 48898d50ffffff mov qword ptr [rbp-0B0h],rcx
00007ff9`9b979f14 4883bd58ffffff00 cmp qword ptr [rbp-0A8h],0
00007ff9`9b979f1c 7417 je 00007ff9`9b979f35
00007ff9`9b979f1e 488b8d58ffffff mov rcx,qword ptr [rbp-0A8h] // rcx == 00000259324e0260
00007ff9`9b979f25 488b4940 mov rcx,qword ptr [rcx+40h] // rcx offset 40h == [00000259`324e02a0] == 00000259324fddb0
00007ff9`9b979f29 33d2 xor edx,edx
00007ff9`9b979f2b e8b0fe295c call mscorlib_ni+0x599de0 (00007ff9`f7c19de0) (System.IntPtr.op_Inequality(IntPtr, IntPtr), mdToken: 0000000006000f9b)
00007ff9`9b979f30 0fb6c8 movzx ecx,al
00007ff9`9b979f33 eb02 jmp 00007ff9`9b979f37
00007ff9`9b979f35 33c9 xor ecx,ecx
00007ff9`9b979f37 0fb6c9 movzx ecx,cl
00007ff9`9b979f3a 488b5510 mov rdx,qword ptr [rbp+10h]] // rdx == [rbp + 10h] Oracle.DataAccess.Client.OracleCommand
00007ff9`9b979f3e 488b9280000000 mov rdx,qword ptr [rdx+80h] // OracleCommand offset 0x80 == m_opsConCtx
00007ff9`9b979f45 4c8b4510 mov r8,qword ptr [rbp+10h]
00007ff9`9b979f49 4981c0a0000000 add r8,0A0h // r8 == m_opsErrCtx
00007ff9`9b979f50 4c8b4d10 mov r9,qword ptr [rbp+10h]
00007ff9`9b979f54 4981c188000000 add r9,88h
00007ff9`9b979f5b 488b4510 mov rax,qword ptr [rbp+10h]
00007ff9`9b979f5f 480590000000 add rax,90h
00007ff9`9b979f65 4c8b5510 mov r10,qword ptr [rbp+10h]
00007ff9`9b979f69 4981c298000000 add r10,98h
00007ff9`9b979f70 85c9 test ecx,ecx
00007ff9`9b979f72 750d jne 00007ff9`9b979f81
00007ff9`9b979f74 488bca mov rcx,rdx //
00007ff9`9b979f77 488b5510 mov rdx,qword ptr [rbp+10h] // rdx == [rbp + 10h] Oracle.DataAccess.Client.OracleCommand
00007ff9`9b979f7b 4c8b5a48 mov r11,qword ptr [rdx+48h] // OracleCommand + offset 0x48 == m_poooledCmdText
00007ff9`9b979f7f eb06 jmp 00007ff9`9b979f87
00007ff9`9b979f81 488bca mov rcx,rdx
00007ff9`9b979f84 4533db xor r11d,r11d
00007ff9`9b979f87 4c89542420 mov qword ptr [rsp+20h],r10
00007ff9`9b979f8c 4c895c2428 mov qword ptr [rsp+28h],r11
00007ff9`9b979f91 488d9550ffffff lea rdx,[rbp-0B0h]
00007ff9`9b979f98 4889542430 mov qword ptr [rsp+30h],rdx
00007ff9`9b979f9d 488d9558ffffff lea rdx,[rbp-0A8h]
00007ff9`9b979fa4 4889542438 mov qword ptr [rsp+38h],rdx
00007ff9`9b979fa9 8b5590 mov edx,dword ptr [rbp-70h]
00007ff9`9b979fac 89542440 mov dword ptr [rsp+40h],edx
00007ff9`9b979fb0 498bd0 mov rdx,r8
00007ff9`9b979fb3 4d8bc1 mov r8,r9
00007ff9`9b979fb6 4c8bc8 mov r9,rax
00007ff9`9b979fb9 e87adaffff call 00007ff9`9b977a38 (Oracle.DataAccess.Client.OpsSql.Prepare2(IntPtr, IntPtr ByRef, IntPtr ByRef, IntPtr ByRef, Oracle.DataAccess.Client.OpoSqlValCtx* ByRef, System.String, IntPtr ByRef, Oracle.DataAccess.Client.OpoMetValCtx* ByRef, Int32), mdToken: 00000000060002ee)
>>> 00007ff9`9b979fbe 894594 mov dword ptr [rbp-6Ch],eax
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]