성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Java - How to use the Foreign Funct...
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>C# - ELEMENT_TYPE_INTERNAL 유형의 사용 예</h1> <p> 제가 간간이, IL 코드 수준에서 메서드의 signature 분석을 다뤘었는데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > .NET 메서드의 Signature 바이트 코드 분석 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12379'>https://www.sysnet.pe.kr/2/0/12379</a> 값(struct) 형식의 제네릭(Generic) 타입이 박싱되는 경우의 메타데이터 토큰 값 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/1857#parse_sig'>https://www.sysnet.pe.kr/2/0/1857#parse_sig</a> 닷넷 Generic 타입의 메타 데이터 토큰 값 알아내는 방법 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/1848#parse_sig'>https://www.sysnet.pe.kr/2/0/1848#parse_sig</a> </pre> <br /> 이에 대한 사전 지식으로 필요한 것이 바로 CorElementType의 이해입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > CorElementType의 요소 값 설명 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/1860'>https://www.sysnet.pe.kr/2/0/1860</a> </pre> <br /> 개인적으로 CorElementType에 대한 이해는 아래의 글로 정리한 것이 전부인데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > ELEMENT_TYPE_MODIFIER의 조합 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/2894'>https://www.sysnet.pe.kr/2/0/2894</a> </pre> <br /> 사실 저 글을 쓸 때도 ELEMENT_TYPE_INTERNAL은 이제는 쓰이지 않는 것인 줄 알았습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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). <span style='color: blue; font-weight: bold'>ELEMENT_TYPE_INTERNAL = 0x21, // INTERNAL <typehandle></span> // 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; </pre> <br /> 왜냐하면, 간혹 보게 되는 CLI 글들이나 소스 코드 내의 주석에 deprecated 되는 기호에 대한 것들을 종종 봤기 때문에, 저는 ELEMENT_TYPE_INTERNAL도 그런 부류의 하나인 줄 알았습니다. 게다가 주석에도 나오지만 ("which will not be persisted in any way") 내부 CLR 동작 과정 중에 메모리상에만 존재할 듯 싶어 무시를 했었는데요.<br /> <br /> 그런데, 오늘 우연히 보게된 .NET 6.0의 System.Text.Json에서 사용 예를 발견했습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > c:\temp> <span style='color: blue; font-weight: bold'>ildasm /metadata=hex /bytes "C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.0\System.Text.Json.dll" /out=sample_il.txt</span> </pre> <br /> System.Text.Json.Serialization.Converters.ArrayConverter`2 타입의 Add 메서드를 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > protected override void Add(in TElement value, ref ReadStack state) { ((List<TElement>)state.Current.ReturnValue).Add(value); } </pre> <br /> C# signature 상으로는 별다른 것이 없는데, IL 코드 수준의 signature 구성을 보면 다음과 같습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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 <span style='color: blue; font-weight: bold'>21: ELEMENT_TYPE_INTERNAL</span> <span style='color: blue; font-weight: bold'>10: <typehandle></span> 13: ELEMENT_TYPE_VAR 01: 클래스 var의 첫 번째 generic 인자(TElement) [두 번째 인자의 형식] 10: ELEMENT_TYPE_BYREF 11: ELEMENT_TYPE_VALUETYPE 81 04: <type> System.Text.Json.ReadStack </pre> <br /> 여기서 문제는, ELEMENT_TYPE_INTERNAL의 경우 그다음 명시된 값이 "typehandle"이라고 나오는데, Profiler에서 제공하는 <a target='tab' href='https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/metadata/imetadataimport2-interface'>IMetaDataImport2</a> 인터페이스로는 typehandle 관련한 조회 함수를 전혀 제공하고 있지 않다는 점입니다.<br /> <br /> 그러고 보니, typehandle에 대한 설명을 예전 글에서 다뤘는데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# 코드로 접근하는 MethodDesc, MethodTable ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12142'>https://www.sysnet.pe.kr/2/0/12142</a> </pre> <br /> typehandle은 결국 MethodTable의 위치를 가리키는 포인터 값입니다. 반면 메서드 signature에서 나온 값은 단지 0x10이기 때문에 아마도 저 값은 MethodTable이 등록된 특정 메타데이터 테이블의 인덱스가 아닐까 싶은데... ^^;<br /> <br /> <hr style='width: 50%' /><br /> <br /> ildasm.exe를 이용해 덤프를 해보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\temp> <span style='color: blue; font-weight: bold'>ildasm /metadata=hex /bytes "C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.0\System.Text.Json.dll" /out=sample_il.txt</span> </pre> <br /> 다음과 같은 출력 결과를 얻을 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // 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 <span style='color: blue; font-weight: bold'>System.Runtime.InteropServices.InAttribute</span> 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) </pre> <br /> 그러니까, 아마도 typehandle == 0x10인 값은 메타데이터 내에 InAttribute 특성을 가리키고 있음을 유추할 수 있습니다. 하지만, 같은 메타데이터 내에서 InAttribute의 등록을 찾아보면 유일하게 TypeRef 영역에만 다음과 같이 기록돼 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <span style='color: blue; font-weight: bold'>// TypeRef #8 (01000008) // ------------------------------------------------------- // Token: 0x01000008 // ResolutionScope: 0x23000001 // TypeRefName: System.Runtime.InteropServices.InAttribute</span> // ...[생략]... // 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 </pre> <br /> 어허~~~ ^^; 0x10과 0x08 사이에서 어떤 연관성도 나오지 않는군요. <br /> <br /> 검색해 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Read DynamicMethod's LocalSignature: non standard type tokens? ; <a target='tab' href='https://stackoverflow.com/questions/10614484/read-dynamicmethods-localsignature-non-standard-type-tokens?rq=1'>https://stackoverflow.com/questions/10614484/read-dynamicmethods-localsignature-non-standard-type-tokens?rq=1</a> </pre> <br /> 자문자답하고 있는 글에서도, "uncompressed data"라고 답변을 하고 있습니다. 또한, 과거의 SSCLI 소스 코드를 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // <a target='tab' href='https://github.com/g15ecb/shared-source-cli-2.0/blob/master/clr/src/vm/siginfo.cpp#L1587'>https://github.com/g15ecb/shared-source-cli-2.0/blob/master/clr/src/vm/siginfo.cpp#L1587</a> case ELEMENT_TYPE_INTERNAL : { TypeHandle hType; CorSigUncompressPointer(psig.GetPtr(), (void**)&hType); // workaround unreachable code warning // RETURN(hType); thRet = hType; break;} } </pre> <br /> signature에 온 값을 4(또는 8) 바이트 포인터로 해석하고, 최신의 CoreCLR에서도,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // <a target='tab' href='https://github.com/dotnet/runtime/blob/main/src/coreclr/vm/jitinterface.cpp#L3095'>https://github.com/dotnet/runtime/blob/main/src/coreclr/vm/jitinterface.cpp#L3095</a> case DeclaringTypeHandleSlot: _ASSERTE(pTemplateMD != NULL); sigBuilder.AppendElementType(ELEMENT_TYPE_INTERNAL); sigBuilder.AppendPointer(pTemplateMD->GetMethodTable()); FALLTHROUGH; </pre> <br /> MethodTable의 포인터 값을 추가하고 있습니다. 참... 이걸 어떻게 다뤄야 할지...<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그나저나, 혹시 우리도 in 예약어를 사용하면 ELEMENT_TYPE_INTERNAL로 기록이 될까요? .NET 소스 코드에 따라,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // <a target='tab' href='https://github.com/dotnet/runtime/blob/main/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ArrayConverter.cs'>https://github.com/dotnet/runtime/blob/main/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ArrayConverter.cs</a> // ...[생략]... internal sealed class ArrayConverter<TCollection, TElement> : IEnumerableDefaultConverter<TElement[], TElement> { internal override bool CanHaveIdMetadata => false; <span style='color: blue; font-weight: bold'>protected override void Add(in TElement value, ref ReadStack state)</span> { ((List<TElement>)state.Current.ReturnValue!).Add(value); } // ...[생략]... } </pre> <br /> 우리도 유사하게 구성해 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > internal class Program { static void Main(string[] args) { Console.WriteLine("Hello World!"); } public class Test<T1, T2> { <span style='color: blue; font-weight: bold'>protected void Add(in T1 value, ref Program state)</span> { Console.WriteLine(value); Console.WriteLine(state); } } } // ildasm /metadata=hex /bytes "Console1.dll" /out=sample_il.txt </pre> <br /> 아쉽게도, 이때의 ildasm.exe 결과에는 0x21(ELEMENT_TYPE_INTERNAL) 코드가 산출되지 않습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // 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 : <span style='color: blue; font-weight: bold'>20 02 01 10 13 00 10 12 14</span> // 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) </pre> <br /> 사용 예는 확인했지만, 재현은 할 수가 없군요. ^^; 보는 바와 같이 C#의 경우에는 In 특성이 메서드 signature 레벨이 아닌, Parameter 정보에 <a target='tab' href='https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/metadata/corparamattr-enumeration'>CorParamAttr</a>::pdIn 속성으로 등록돼 있습니다.<br /> <br /> 파면 팔수록 모르는 것 투성이인 것을 보면, 저도 아직 한참 멀은 것 같습니다. ^^;<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
9590
(왼쪽의 숫자를 입력해야 합니다.)