Microsoft MVP성태의 닷넷 이야기
.NET Framework: 495. CorElementType의 요소 값 설명 [링크 복사], [링크+제목 복사],
조회: 16532
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 2개 있습니다.)
(시리즈 글이 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




CorElementType의 요소 값 설명

CorElementType은 enum 형식으로 다음의 문서에 기본적인 설명이 있습니다.

CorElementType Enumeration
; https://learn.microsoft.com/en-us/dotnet/framework/unmanaged-api/metadata/corelementtype-enumeration

기본적인 타입들은 이렇게 매핑됩니다.

ELEMENT_TYPE_VOID:      "System.Void"
ELEMENT_TYPE_BOOLEAN:   "System.Boolean"
ELEMENT_TYPE_I1:        "System.SByte"
ELEMENT_TYPE_U1:        "System.Byte"
ELEMENT_TYPE_I2:        "System.Int16"
ELEMENT_TYPE_U2:        "System.UInt16"
ELEMENT_TYPE_CHAR:      "System.Char"
ELEMENT_TYPE_I4:        "System.Int32"
ELEMENT_TYPE_U4:        "System.UInt32"
ELEMENT_TYPE_I8:        "System.Int64"
ELEMENT_TYPE_U8:        "System.UInt64"
ELEMENT_TYPE_R4:        "System.Single"
ELEMENT_TYPE_R8:        "System.Double"
ELEMENT_TYPE_OBJECT:    "System.Object"
ELEMENT_TYPE_STRING:    "System.String"
ELEMENT_TYPE_I:         "System.IntPtr"
ELEMENT_TYPE_U:         "System.UIntPtr"
ELEMENT_TYPE_SZARRAY:   1차원 배열 Type []
ELEMENT_TYPE_ARRAY:     다차원 배열 Type [,]

이제 그 외의 것을 좀 더 자세하게 알아보겠습니다. 우선 ELEMENT_TYPE_VAR, ELEMENT_TYPE_MVAR인데요. 전자는 타입(class,struct)에 사용된 generic을 의미하고, 후자는 메서드에 사용된 generic입니다.

ELEMENT_TYPE_VAR:   class<T>           (generic class)
ELEMENT_TYPE_MVAR:  void Method<T>(T obj);  (generic method)

관련해서 ELEMENT_TYPE_GENERICINST도 있는데요. 이것은 제네릭 인자를 포함하는 타입인 경우에 해당합니다.

ELEMENT_TYPE_GENERICINST:  void Method(List<int>);

닷넷의 IL 코드를 심도 있게 공부하다 보면, 왜 자바에서 generic을 단순히 object로 처리했는지를 조금이나마 이해하게 됩니다. (닷넷도 generic을 object로 구현했다면 IL 코드가 더 간단해졌을 거라는 의미입니다.)

그리고, 값 형식과 참조 형식을 나누는 요소가 있습니다.

ELEMENT_TYPE_VALUETYPE:     값 형식
ELEMENT_TYPE_CLASS:         참조 형식

더 설명할 필요가 없겠죠? ^^

ELEMENT_TYPE_BYREF 값은 좀 재미있습니다. 이것은 out, ref C# 키워드가 사용되었을 때 지정됩니다. 이것을 달리 말하면? C#은 그 자체 내의 문법에서 out, ref를 구분하는 것일 뿐 IL 코드 입장에서 보면 동일하게 ELEMENT_TYPE_BYREF으로 취급된다는 점입니다.

또한, ELEMENT_TYPE_PTR 값은 unsafe 구문에서 포인터를 다루는 경우 사용됩니다.

일단, 여기까지는 그런대로 쉽게 이해할 수 있는 요소들인데!




이 부분부터는 주로 C++/CLI로 인해 도입된 것인데 C# 등의 언어로 프로그램할 때는 볼 수 없습니다. 우선, 다음의 값을 볼까요?

ELEMENT_TYPE_CMOD_REQD:     C language required modifier.
ELEMENT_TYPE_CMOD_OPT:      C language optional modifier.

이 2개의 값이 잘 설명된 문서는, 14년 동안 "C++ At Work" 컬럼을 담당했던 Paul DiLascia의 마지막 글입니다.

Rationales, Highlights, and a Farewell (C++/CLI의 설계 원리와 발전 과정)
; https://learn.microsoft.com/en-us/archive/msdn-magazine/2007/february/c-at-work-rationales-highlights-and-a-farewell (영문)
; https://learn.microsoft.com/ko-kr/archive/msdn-magazine/2007/february/c-at-work-rationales-highlights-and-a-farewell

우선, ELEMENT_TYPE_CMOD_OPT 옵션은 .NET Reflector 등의 툴로 보면 modopt 선언자로 표현되는데, 이 옵션이 사용된 구문을 호출 측에서 알지 못해도 전체적인 실행 방식에는 영향이 없을 때 사용하는 것입니다. (여기서 '호출 측'이라는 것은 엄밀히 닷넷 기반의 '언어 개발자'가 해당할 것입니다.)

Modopt (optional modifier) is an MSIL declarator that says to CLI consumers: if you understand what this thing is, great; if not, you can safely ignore it.


"Rationales, Highlights, and a Farewell" 글에서는 modopt의 사례로 C++/CLI의 const 예약어가 사용된 구문을 보여줍니다. 즉, 아래의 g_private 변수는,

// C++/CLI
ref class A {
   static const int g_private = 1;
};

컴파일 후 C++의 const 예약어가 사용되었음을 호출 측에 힌트를 줄 수 있도록 다음과 같은 IL 코드로 변환됩니다.

field public static int32 
    modopt([mscorlib]System.Runtime.CompilerServices.IsConst)
        g_private = int32(0x00000001)

반면, ELEMENT_TYPE_CMOD_REQD는 modreq로 표현되고,

modreq (required modifier) says: if you don't understand this thing, you can't use this function. Volatile is an example of a modreq.


반드시 호출 측에서 이에 대한 의미를 알아야만 사용할 수 있음을 의미합니다. 일례로 volatile 예약어가 지정된 사례를 들고 있습니다.

아울러, "Rationales, Highlights, and a Farewell" 글에서 왜 C++/CLI에서 const 함수가 문법적으로 허용되지 않는지를 설명하고 있는데요. 왜냐하면 CLI는 이 2개의 modifier가 함수에 적용되는 것은 허용하지 않기 때문입니다. (즉, 필드나 함수의 인자에만 적용할 수 있습니다.)

modopt가 사용되는 또 다른 예제를 wikipedia에 포함된 코드에서 볼 수 있는데, 다음과 같이 C++/CLI로 정의된 전역 함수의 경우 기본 C++ calling convention에 따라 Cdecl을 따르고,

class A {
   // public: virtual void __stdcall meth() { }
};

void test_pointer_operations(int param) { }

이를 IL 코드로 컴파일된 후에 살펴보면 Cdecl 호출 관례 정보가 modopt를 이용해 기록된 것을 볼 수 있습니다.

.method assembly static void modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl)
         test_pointer_operations(int32 param) cil managed

그 외에, 우리가 너무나 많이 사용하고 있는 System.Data.dll 어셈블리에도 SNINativeMethodWrapper 타입에 MarshalConsumerInfo 메서드의 signature에 modopt가 있는 것을 볼 수 있습니다.

// Declaring Type: SNINativeMethodWrapper 
// Assembly: System.Data, Version=4.0.0.0 

[ResourceExposure(ResourceScope.None)]
private static unsafe void MarshalConsumerInfo(ConsumerInfo consumerInfo, Sni_Consumer_Info* modopt(IsImplicitlyDereferenced) native_consumerInfo)
{
    // ...[생략]...

    // 심지어 형변환 코드에도 사용됩니다.
    native_consumerInfo[0] = (Sni_Consumer_Info* modopt(IsImplicitlyDereferenced)) consumerInfo.defaultBufferSize;
    // ...[생략]...
}

그런데, 여기서 제가 한 가지 풀지 못한 것이 있습니다. 보면 "IsImplicitlyDereferenced"라는 타입이 modopt에 부가적인 값으로 전달되고 있는데 이에 대한 정보를 메서드의 SIGNATURE 값에서는 찾을 수 없었습니다. (MarshalConsumerInfo 메서드의 signature 분석은 아래에서 나옵니다.)



그다음, ELEMENT_TYPE_MODIFIER에 대해 알아보겠습니다. 이것은 단독으로 정의되는 경우는 없고 ELEMENT_TYPE_PINNED, ELEMENT_TYPE_SENTINEL과 OR(|)연산자로 함께 사용됩니다.
이것이 사용된 사례를 위에서 이미 소개한 SNINativeMethodWrapper.MarshalConsumerInfo 메서드의 signature에서 볼 수 있습니다.


ildasm.exe를 이용해 signature를 보면 "SIG: 00 02 01 12 20 20 45 0f 11 30" 값이 나오는데, 이를 분석하면 다음과 같습니다.

00: static
02: 2개의 인자
01: (return type) void
12: ELEMENT_TYPE_CLASS 
20: ConsumerInfo type (class type token - compressed)
20: ELEMENT_TYPE_CMOD_OPT
45: IsImplicitlyDereferenced type (class type token - compressed)
0f: ELEMENT_TYPE_PTR
11: ELEMENT_TYPE_VALUETYPE
30: Sni_Consumer_Info (value type token - compressed)

타입 ConsumerInfo: 0x08 --> compressed로 바꾸면 0x20
타입 Sni_ConsumerInfo: 0x0C --> compressed로 바꾸면 0x30

위에서 ConsumerInfo와 같은 타입 토큰의 경우 compressed 유형으로 0x20인데, 이를 unpack 시키면 0x02000008(mdTypeDef == 0x02000000)값이 나옵니다. 실제로 CFF Explorer 같은 도구를 이용해 메타데이터 테이블의 TypeDef 영역을 조사해 보면 이렇게 8번 인덱스 위치에 ConsumerInfo 타입값이 정의된 것을 볼 수 있습니다.

element_type_1.png

본론으로 돌아와서 ELEMENT_TYPE_MODIFIER | ELEMENT_TYPE_PINNED 값이 사용되고 있는데 관련 설명을 찾아보면,

Processing the XML File
; https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/xmldoc/processing-the-xml-file

ELEMENT_TYPE_PINNED is represented as a '^' following the modified type. The C# compiler never generates this.


역시 C# 컴파일러로 인해 생성되는 일은 없고 C++/CLI의 "^" 연산자가 사용되는,

Hashtable^ hash = gcnew Hashtable();

이런 유형의 인자 타입을 받는 경우에 ELEMENT_TYPE_PINNED 값이 지정되는 것입니다. 참고로 다음과 같은 부가적인 자료도 나오는 군요.

PRB: A pinning pointer passed to variable-argument function causes System.BadImageFormatException 
; http://support.microsoft.com/kb/959389

마지막으로 남은 2가지 요소는 짧은 설명으로 대신합니다.

ELEMENT_TYPE_TYPEDBYREF: A typed reference. (System.TypedReference)
ELEMENT_TYPE_SENTINEL: A type modifier that is a sentinel for a list of a variable number of parameters.

(첨부한 파일은 compressed token값을 알아내는 소스코드를 포함합니다.)




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

[연관 글]






[최초 등록일: ]
[최종 수정일: 10/24/2023]

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

비밀번호

댓글 작성자
 




... [46]  47  48  49  50  51  52  53  54  55  56  57  58  59  60  ...
NoWriterDateCnt.TitleFile(s)
12496정성태1/19/20219506.NET Framework: 1010. .NET Core 콘솔 프로젝트에서 Kestrel 호스팅 방법 [1]
12495정성태1/19/202111414웹: 40. IIS의 HTTP/2 지원 여부 - h2, h2c [1]
12494정성태1/19/202110733개발 환경 구성: 522. WSL2 인스턴스와 호스트 측의 Hyper-V에 운영 중인 VM과 네트워크 연결을 하는 방법 [2]
12493정성태1/18/20218962.NET Framework: 1009. .NET 5에서의 네트워크 라이브러리 개선 (1) - HTTP 관련 [1]파일 다운로드1
12492정성태1/17/20218401오류 유형: 695. ASP.NET 0x80131620 Failed to bind to address
12491정성태1/16/202110135.NET Framework: 1008. 배열을 반환하는 C# COM 개체의 메서드를 C++에서 사용 시 메모리 누수 현상 [1]파일 다운로드1
12490정성태1/15/20219645.NET Framework: 1007. C# - foreach에서 열거 변수의 타입을 var로 쓰면 object로 추론하는 문제 [1]파일 다운로드1
12489정성태1/13/202110612.NET Framework: 1006. C# - DB에 저장한 텍스트의 (이모티콘을 비롯해) 유니코드 문자가 '?'로 보인다면? [1]
12488정성태1/13/202110860.NET Framework: 1005. C# - string 타입은 shallow copy일까요? deep copy일까요? [2]파일 다운로드1
12487정성태1/13/20219274.NET Framework: 1004. C# - GC Heap에 위치한 참조 개체의 주소를 알아내는 방법파일 다운로드1
12486정성태1/12/202110248.NET Framework: 1003. x64 환경에서 참조형의 기본 메모리 소비는 얼마나 될까요? [1]
12485정성태1/11/202110991Graphics: 38. C# - OpenCvSharp.VideoWriter에 BMP 파일을 1초씩 출력하는 예제파일 다운로드1
12484정성태1/9/202111644.NET Framework: 1002. C# - ReadOnlySequence<T> 소개파일 다운로드1
12483정성태1/8/20218705개발 환경 구성: 521. dotPeek - 훌륭한 역어셈블 소스 코드 생성 도구
12482정성태1/8/202110281.NET Framework: 1001. C# - 제네릭 타입/메서드에서 사용 시 경우에 따라 CS8377 컴파일 에러
12481정성태1/7/20219933.NET Framework: 1000. C# - CS8344 컴파일 에러: ref struct 타입의 사용 제한 메서드파일 다운로드1
12480정성태1/6/202112581.NET Framework: 999. C# - ArrayPool<T>와 MemoryPool<T> 소개파일 다운로드1
12479정성태1/6/20219989.NET Framework: 998. C# - OWIN 예제 프로젝트 만들기
12478정성태1/5/202111623.NET Framework: 997. C# - ArrayPool<T> 소개파일 다운로드1
12477정성태1/5/202113943기타: 79. github 코드 검색 방법 [1]
12476정성태1/5/202110684.NET Framework: 996. C# - 닷넷 코어에서 다른 스레드의 callstack을 구하는 방법파일 다운로드1
12475정성태1/5/202113275.NET Framework: 995. C# - Span<T>와 Memory<T> [1]파일 다운로드1
12474정성태1/4/202110702.NET Framework: 994. C# - (.NET Core 2.2부터 가능한) 프로세스 내부에서 CLR ETW 이벤트 수신 [1]파일 다운로드1
12473정성태1/4/20219529.NET Framework: 993. .NET 런타임에 따라 달라지는 정적 필드의 초기화 유무 [1]파일 다운로드1
12472정성태1/3/20219850디버깅 기술: 178. windbg - 디버그 시작 시 스크립트 실행
12471정성태1/1/202110342.NET Framework: 992. C# - .NET Core 3.0 이상부터 제공하는 runtimeOptions의 rollForward 옵션 [1]
... [46]  47  48  49  50  51  52  53  54  55  56  57  58  59  60  ...