Microsoft MVP성태의 닷넷 이야기
.NET Framework: 1130. C# - ELEMENT_TYPE_INTERNAL 유형의 사용 예 [링크 복사], [링크+제목 복사],
조회: 15513
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 

(시리즈 글이 9개 있습니다.)
.NET Framework: 491. 닷넷 Generic 타입의 메타 데이터 토큰 값 알아내는 방법
; https://www.sysnet.pe.kr/2/0/1848

.NET Framework: 494. 값(struct) 형식의 제네릭(Generic) 타입이 박싱되는 경우의 메타데이터 토큰 값
; https://www.sysnet.pe.kr/2/0/1857

.NET Framework: 495. CorElementType의 요소 값 설명
; https://www.sysnet.pe.kr/2/0/1860

.NET Framework: 509. ELEMENT_TYPE_MODIFIER의 조합
; https://www.sysnet.pe.kr/2/0/2894

.NET Framework: 510. 제네릭(Generic) 인자에 대한 메타데이터 등록 확인
; https://www.sysnet.pe.kr/2/0/2907

.NET Framework: 844. C# - 박싱과 언박싱
; https://www.sysnet.pe.kr/2/0/11943

.NET Framework: 955.  .NET 메서드의 Signature 바이트 코드 분석
; https://www.sysnet.pe.kr/2/0/12379

.NET Framework: 1130. C# - ELEMENT_TYPE_INTERNAL 유형의 사용 예
; https://www.sysnet.pe.kr/2/0/12903

.NET Framework: 1174. C# - ELEMENT_TYPE_FNPTR 유형의 사용 예
; https://www.sysnet.pe.kr/2/0/12998




C# - ELEMENT_TYPE_INTERNAL 유형의 사용 예

제가 간간이, IL 코드 수준에서 메서드의 signature 분석을 다뤘었는데요,

.NET 메서드의 Signature 바이트 코드 분석
; https://www.sysnet.pe.kr/2/0/12379

값(struct) 형식의 제네릭(Generic) 타입이 박싱되는 경우의 메타데이터 토큰 값
; https://www.sysnet.pe.kr/2/0/1857#parse_sig

닷넷 Generic 타입의 메타 데이터 토큰 값 알아내는 방법
; https://www.sysnet.pe.kr/2/0/1848#parse_sig

이에 대한 사전 지식으로 필요한 것이 바로 CorElementType의 이해입니다.

CorElementType의 요소 값 설명
; https://www.sysnet.pe.kr/2/0/1860

개인적으로 CorElementType에 대한 이해는 아래의 글로 정리한 것이 전부인데요,

ELEMENT_TYPE_MODIFIER의 조합
; https://www.sysnet.pe.kr/2/0/2894

사실 저 글을 쓸 때도 ELEMENT_TYPE_INTERNAL은 이제는 쓰이지 않는 것인 줄 알았습니다.

typedef enum CorElementType
{
    ELEMENT_TYPE_END            = 0x00,
    ELEMENT_TYPE_VOID           = 0x01,
    ELEMENT_TYPE_BOOLEAN        = 0x02,
    ELEMENT_TYPE_CHAR           = 0x03,
    // ...[생략]...

    // This is for signatures generated internally (which will not be persisted in any way).
    ELEMENT_TYPE_INTERNAL       = 0x21,     // INTERNAL <typehandle>

    // Note that this is the max of base type excluding modifiers
    ELEMENT_TYPE_MAX            = 0x22,     // first invalid element type


    ELEMENT_TYPE_MODIFIER       = 0x40,
    ELEMENT_TYPE_SENTINEL       = 0x01 | ELEMENT_TYPE_MODIFIER, // sentinel for varargs
    ELEMENT_TYPE_PINNED         = 0x05 | ELEMENT_TYPE_MODIFIER,

} CorElementType;

왜냐하면, 간혹 보게 되는 CLI 글들이나 소스 코드 내의 주석에 deprecated 되는 기호에 대한 것들을 종종 봤기 때문에, 저는 ELEMENT_TYPE_INTERNAL도 그런 부류의 하나인 줄 알았습니다. 게다가 주석에도 나오지만 ("which will not be persisted in any way") 내부 CLR 동작 과정 중에 메모리상에만 존재할 듯 싶어 무시를 했었는데요.

그런데, 오늘 우연히 보게된 .NET 6.0의 System.Text.Json에서 사용 예를 발견했습니다.

c:\temp> ildasm /metadata=hex /bytes "C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.0\System.Text.Json.dll" /out=sample_il.txt

System.Text.Json.Serialization.Converters.ArrayConverter`2 타입의 Add 메서드를 보면,

protected override void Add(in TElement value, ref ReadStack state)
{
	((List<TElement>)state.Current.ReturnValue).Add(value);
}

C# signature 상으로는 별다른 것이 없는데, IL 코드 수준의 signature 구성을 보면 다음과 같습니다.

20 02 01 1f 21 10 13 01  10 11 81 04 

20: IMAGE_CEE_CS_CALLCONV_HASTHIS
02: 2개의 인자
01: 반환 타입 ELEMENT_TYPE_VOID

[첫 번째 인자의 형식]
1f: ELEMENT_TYPE_CMOD_REQD
21: ELEMENT_TYPE_INTERNAL
10: <typehandle>
13: ELEMENT_TYPE_VAR
01: 클래스 var의 첫 번째 generic 인자(TElement)

[두 번째 인자의 형식]
10: ELEMENT_TYPE_BYREF
11: ELEMENT_TYPE_VALUETYPE
81 04: <type> System.Text.Json.ReadStack

여기서 문제는, ELEMENT_TYPE_INTERNAL의 경우 그다음 명시된 값이 "typehandle"이라고 나오는데, Profiler에서 제공하는 IMetaDataImport2 인터페이스로는 typehandle 관련한 조회 함수를 전혀 제공하고 있지 않다는 점입니다.

그러고 보니, typehandle에 대한 설명을 예전 글에서 다뤘는데요,

C# 코드로 접근하는 MethodDesc, MethodTable
; https://www.sysnet.pe.kr/2/0/12142

typehandle은 결국 MethodTable의 위치를 가리키는 포인터 값입니다. 반면 메서드 signature에서 나온 값은 단지 0x10이기 때문에 아마도 저 값은 MethodTable이 등록된 특정 메타데이터 테이블의 인덱스가 아닐까 싶은데... ^^;




ildasm.exe를 이용해 덤프를 해보면,

C:\temp> ildasm /metadata=hex /bytes "C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.0\System.Text.Json.dll" /out=sample_il.txt

다음과 같은 출력 결과를 얻을 수 있습니다.

// 	Method #2 (060009ec) 
// 	-------------------------------------------------------
// 		MethodName: Add (060009EC)
// 		Flags     : [Family] [Virtual] [HideBySig] [ReuseSlot]  (000000c4)
// 		RVA       : 0x000f5e0c
// 		ImplFlags : [IL] [Managed]  (00000000)
// 		CallCnvntn: [DEFAULT]
// 		hasThis 
// 		ReturnType: Void
// 		2 Arguments
// 			Argument #1:  CMOD_REQD System.Runtime.InteropServices.InAttribute ByRef Var!1
// 			Argument #2:  ByRef ValueClass System.Text.Json.ReadStack
// 		Signature : 20 02 01 1f 21 10 13 01  10 11 81 04 
// 		2 Parameters
// 			(1) ParamToken : (08000b87) Name : value flags: [In]  (00000001)
// 			CustomAttribute #1 (0c0005bd)
// 			-------------------------------------------------------
// 				CustomAttribute Type: 0a000050
// 				CustomAttributeName: System.Runtime.CompilerServices.IsReadOnlyAttribute :: instance void .ctor()
// 				Length: 4
// 				Value : 01 00 00 00                                      >                <
// 				ctor args: ()
// 
// 			(2) ParamToken : (08000b88) Name : state flags: [none] (00000000)

그러니까, 아마도 typehandle == 0x10인 값은 메타데이터 내에 InAttribute 특성을 가리키고 있음을 유추할 수 있습니다. 하지만, 같은 메타데이터 내에서 InAttribute의 등록을 찾아보면 유일하게 TypeRef 영역에만 다음과 같이 기록돼 있습니다.

// TypeRef #8 (01000008)
// -------------------------------------------------------
// Token:             0x01000008
// ResolutionScope:   0x23000001
// TypeRefName:       System.Runtime.InteropServices.InAttribute
// 
...[생략]...
// TypeRef #47 (0100002f)
// -------------------------------------------------------
// Token:             0x0100002f
// ResolutionScope:   0x23000001
// TypeRefName:       System.Runtime.CompilerServices.IsReadOnlyAttribute
// 	MemberRef #1 (0a000050)
// 	-------------------------------------------------------
// 		Member: (0a000050) .ctor: 
// 		CallCnvntn: [DEFAULT]
// 		hasThis 
// 		ReturnType: Void
// 		No arguments.
// 		Signature : 20 00 01 

어허~~~ ^^; 0x10과 0x08 사이에서 어떤 연관성도 나오지 않는군요.

검색해 보면,

Read DynamicMethod's LocalSignature: non standard type tokens?
; https://stackoverflow.com/questions/10614484/read-dynamicmethods-localsignature-non-standard-type-tokens?rq=1

자문자답하고 있는 글에서도, "uncompressed data"라고 답변을 하고 있습니다. 또한, 과거의 SSCLI 소스 코드를 보면,

// https://github.com/g15ecb/shared-source-cli-2.0/blob/master/clr/src/vm/siginfo.cpp#L1587
case ELEMENT_TYPE_INTERNAL :
    {
        TypeHandle hType;
        CorSigUncompressPointer(psig.GetPtr(), (void**)&hType);
        // workaround unreachable code warning
        // RETURN(hType);
        thRet = hType;
        break;}
    }

signature에 온 값을 4(또는 8) 바이트 포인터로 해석하고, 최신의 CoreCLR에서도,

// https://github.com/dotnet/runtime/blob/main/src/coreclr/vm/jitinterface.cpp#L3095

case DeclaringTypeHandleSlot:
    _ASSERTE(pTemplateMD != NULL);
    sigBuilder.AppendElementType(ELEMENT_TYPE_INTERNAL);
    sigBuilder.AppendPointer(pTemplateMD->GetMethodTable());
    FALLTHROUGH;

MethodTable의 포인터 값을 추가하고 있습니다. 참... 이걸 어떻게 다뤄야 할지...




그나저나, 혹시 우리도 in 예약어를 사용하면 ELEMENT_TYPE_INTERNAL로 기록이 될까요? .NET 소스 코드에 따라,

// https://github.com/dotnet/runtime/blob/main/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ArrayConverter.cs

// ...[생략]...
internal sealed class ArrayConverter<TCollection, TElement> : IEnumerableDefaultConverter<TElement[], TElement>
{
    internal override bool CanHaveIdMetadata => false;

    protected override void Add(in TElement value, ref ReadStack state)
    {
        ((List<TElement>)state.Current.ReturnValue!).Add(value);
    }

    // ...[생략]...
}

우리도 유사하게 구성해 보면,

internal class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Hello World!");
    }

    public class Test<T1, T2>
    {
        protected void Add(in T1 value, ref Program state)
        {
            Console.WriteLine(value);
            Console.WriteLine(state);
        }
    }
}

// ildasm /metadata=hex /bytes "Console1.dll" /out=sample_il.txt

아쉽게도, 이때의 ildasm.exe 결과에는 0x21(ELEMENT_TYPE_INTERNAL) 코드가 산출되지 않습니다.

// 	Method #1 (06000007) 
// 	-------------------------------------------------------
// 		MethodName: Add (06000007)
// 		Flags     : [Family] [HideBySig] [ReuseSlot]  (00000084)
// 		RVA       : 0x000020a9
// 		ImplFlags : [IL] [Managed]  (00000000)
// 		CallCnvntn: [DEFAULT]
// 		hasThis 
// 		ReturnType: Void
// 		2 Arguments
// 			Argument #1:  ByRef Var!0
// 			Argument #2:  ByRef Class Program
// 		Signature : 20 02 01 10 13 00 10 12  14 
// 		2 Parameters
// 			(1) ParamToken : (08000002) Name : value flags: [In]  (00000001)
// 			CustomAttribute #1 (0c00000d)
// 			-------------------------------------------------------
// 				CustomAttribute Type: 0a00000d
// 				CustomAttributeName: System.Runtime.CompilerServices.IsReadOnlyAttribute :: instance void .ctor()
// 				Length: 4
// 				Value : 01 00 00 00                                      >                <
// 				ctor args: ()
// 
// 			(2) ParamToken : (08000003) Name : state flags: [none] (00000000)
// 		CustomAttribute #1 (0c000017)
// 		-------------------------------------------------------
// 			CustomAttribute Type: 06000004
// 			CustomAttributeName: System.Runtime.CompilerServices.NullableContextAttribute :: instance void .ctor(unsigned int8)
// 			Length: 5
// 			Value : 01 00 01 00 00                                   >                <
// 			ctor args: (1)

사용 예는 확인했지만, 재현은 할 수가 없군요. ^^; 보는 바와 같이 C#의 경우에는 In 특성이 메서드 signature 레벨이 아닌, Parameter 정보에 CorParamAttr::pdIn 속성으로 등록돼 있습니다.

파면 팔수록 모르는 것 투성이인 것을 보면, 저도 아직 한참 멀은 것 같습니다. ^^;




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







[최초 등록일: ]
[최종 수정일: 3/4/2024]

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

비밀번호

댓글 작성자
 




... 121  122  123  124  125  126  127  128  129  130  [131]  132  133  134  135  ...
NoWriterDateCnt.TitleFile(s)
1780정성태10/15/201424173오류 유형: 249. The application-specific permission settings do not grant Local Activation permission for the COM Server application with CLSID
1779정성태10/15/201419697오류 유형: 248. Active Directory에서 OU가 지워지지 않는 경우
1778정성태10/10/201418150오류 유형: 247. The Netlogon service could not create server share C:\Windows\SYSVOL\sysvol\[도메인명]\SCRIPTS.
1777정성태10/10/201421234오류 유형: 246. The processing of Group Policy failed. Windows attempted to read the file \\[도메인]\sysvol\[도메인]\Policies\{...GUID...}\gpt.ini
1776정성태10/10/201418286오류 유형: 245. 이벤트 로그 - Name resolution for the name _ldap._tcp.dc._msdcs.[도메인명]. timed out after none of the configured DNS servers responded.
1775정성태10/9/201419406오류 유형: 244. Visual Studio 디버깅 (2) - Unable to break execution. This process is not currently executing the type of code that you selected to debug.
1774정성태10/9/201426612개발 환경 구성: 246. IIS 작업자 프로세스의 20분 자동 재생(Recycle)을 끄는 방법
1773정성태10/8/201429774.NET Framework: 471. 웹 브라우저로 다운로드가 되는 파일을 왜 C# 코드로 하면 안되는 걸까요? [1]
1772정성태10/3/201418564.NET Framework: 470. C# 3.0의 기본 인자(default parameter)가 .NET 1.1/2.0에서도 실행될까? [3]
1771정성태10/2/201428059개발 환경 구성: 245. 실행된 프로세스(EXE)의 명령행 인자를 확인하고 싶다면 - Sysmon [4]
1770정성태10/2/201421683개발 환경 구성: 244. 매크로 정의를 이용해 파일 하나로 C++과 C#에서 공유하는 방법 [1]파일 다운로드1
1769정성태10/1/201424105개발 환경 구성: 243. Scala 개발 환경 구성(JVM, 닷넷) [1]
1768정성태10/1/201419527개발 환경 구성: 242. 배치 파일에서 Thread.Sleep 효과를 주는 방법 [5]
1767정성태10/1/201424628VS.NET IDE: 94. Visual Studio 2012/2013에서의 매크로 구현 - Visual Commander [2]
1766정성태10/1/201422474개발 환경 구성: 241. 책 "프로그래밍 클로저: Lisp"을 읽고 나서. [1]
1765정성태9/30/201426047.NET Framework: 469. Unity3d에서 transform을 변수에 할당해 사용하는 특별한 이유가 있을까요?
1764정성태9/30/201422280오류 유형: 243. 파일 삭제가 안 되는 경우 - The action can't be comleted because the file is open in System
1763정성태9/30/201423848.NET Framework: 468. PDB 파일을 연동해 소스 코드 라인 정보를 알아내는 방법파일 다운로드1
1762정성태9/30/201424547.NET Framework: 467. 닷넷에서 EIP/RIP 레지스터 값을 구하는 방법 [1]파일 다운로드1
1761정성태9/29/201421566.NET Framework: 466. 윈도우 운영체제의 보안 그룹 이름 및 설명 문자열을 바꾸는 방법파일 다운로드1
1760정성태9/28/201419827.NET Framework: 465. ICorProfilerInfo::GetILToNativeMapping 메서드가 0x80131358을 반환하는 경우
1759정성태9/27/201430972개발 환경 구성: 240. Visual C++ / x64 환경에서 inline-assembly를 매크로 어셈블리로 대체하는 방법파일 다운로드1
1758정성태9/23/201437877개발 환경 구성: 239. 원격 데스크톱 접속(RDP)을 기존의 콘솔 모드처럼 사용하는 방법 [1]
1757정성태9/23/201418399오류 유형: 242. Lync로 모임 참여 시 소리만 들리지 않는 경우 - 두 번째 이야기
1756정성태9/23/201427403기타: 48. NVidia 제품의 과다한 디스크 사용 [2]
1755정성태9/22/201434200오류 유형: 241. Unity Web Player를 설치해도 여전히 설치하라는 화면이 나오는 경우 [4]
... 121  122  123  124  125  126  127  128  129  130  [131]  132  133  134  135  ...