Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 

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




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 11/27/2019]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 16  17  [18]  19  20  21  22  23  24  25  26  27  28  29  30  ...
NoWriterDateCnt.TitleFile(s)
13171정성태11/25/20224680Windows: 214. 윈도우 - 스레드 스택의 "red zone"
13170정성태11/24/20224985Windows: 213. 윈도우 - 싱글 스레드는 컨텍스트 스위칭이 없을까요?
13169정성태11/23/20225604Windows: 212. 윈도우의 Protected Process (Light) 보안 [1]파일 다운로드2
13168정성태11/22/20224876제니퍼 .NET: 31. 제니퍼 닷넷 적용 사례 (9) - DB 서비스에 부하가 걸렸다?!
13167정성태11/21/20224911.NET Framework: 2070. .NET 7 - Console.ReadKey와 리눅스의 터미널 타입
13166정성태11/20/20224636개발 환경 구성: 651. Windows 사용자 경험으로 WSL 환경에 dotnet 런타임/SDK 설치 방법
13165정성태11/18/20224544개발 환경 구성: 650. Azure - "scm" 프로세스와 엮인 서비스 모음
13164정성태11/18/20225461개발 환경 구성: 649. Azure - 비주얼 스튜디오를 이용한 AppService 원격 디버그 방법
13163정성태11/17/20225386개발 환경 구성: 648. 비주얼 스튜디오에서 안드로이드 기기 인식하는 방법
13162정성태11/15/20226461.NET Framework: 2069. .NET 7 - AOT(ahead-of-time) 컴파일
13161정성태11/14/20225702.NET Framework: 2068. C# - PublishSingleFile로 배포한 이미지의 역어셈블 가능 여부 (난독화 필요성) [4]
13160정성태11/11/20225629.NET Framework: 2067. C# - PublishSingleFile 적용 시 native/managed 모듈 통합 옵션
13159정성태11/10/20228801.NET Framework: 2066. C# - PublishSingleFile과 관련된 옵션 [3]
13158정성태11/9/20225112오류 유형: 826. Workload definition 'wasm-tools' in manifest 'microsoft.net.workload.mono.toolchain' [...] conflicts with manifest 'microsoft.net.workload.mono.toolchain.net7'
13157정성태11/8/20225764.NET Framework: 2065. C# - Mutex의 비동기 버전파일 다운로드1
13156정성태11/7/20226676.NET Framework: 2064. C# - Mutex와 Semaphore/SemaphoreSlim 차이점파일 다운로드1
13155정성태11/4/20226187디버깅 기술: 183. TCP 동시 접속 (연결이 아닌) 시도를 1개로 제한한 서버
13154정성태11/3/20225656.NET Framework: 2063. .NET 5+부터 지원되는 GC.GetGCMemoryInfo파일 다운로드1
13153정성태11/2/20226931.NET Framework: 2062. C# - 코드로 재현하는 소켓 상태(SYN_SENT, SYN_RECV)
13152정성태11/1/20225560.NET Framework: 2061. ASP.NET Core - DI로 추가한 클래스의 초기화 방법 [1]
13151정성태10/31/20225676C/C++: 161. Windows 11 환경에서 raw socket 테스트하는 방법파일 다운로드1
13150정성태10/30/20225713C/C++: 160. Visual Studio 2022로 빌드한 C++ 프로그램을 위한 다른 PC에서 실행하는 방법
13149정성태10/27/20225642오류 유형: 825. C# - CLR ETW 이벤트 수신이 GCHeapStats_V1/V2에 대해 안 되는 문제파일 다운로드1
13148정성태10/26/20225635오류 유형: 824. msbuild 에러 - error NETSDK1005: Assets file '...\project.assets.json' doesn't have a target for 'net5.0'. Ensure that restore has run and that you have included 'net5.0' in the TargetFramew
13147정성태10/25/20224760오류 유형: 823. Visual Studio 2022 - Unable to attach to CoreCLR. The debugger's protocol is incompatible with the debuggee.
13146정성태10/24/20225587.NET Framework: 2060. C# - Java의 Xmx와 유사한 힙 메모리 최댓값 제어 옵션 HeapHardLimit
... 16  17  [18]  19  20  21  22  23  24  25  26  27  28  29  30  ...