성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] VT sequences to "CONOUT$" vs. STD_O...
[정성태] NetCoreDbg is a managed code debugg...
[정성태] Evaluating tail call elimination in...
[정성태] What’s new in System.Text.Json in ....
[정성태] What's new in .NET 9: Cryptography ...
[정성태] 아... 제시해 주신 "https://akrzemi1.wordp...
[정성태] 다시 질문을 정리할 필요가 있을 것 같습니다. 제가 본문에...
[이승준] 완전히 잘못 짚었습니다. 댓글 지우고 싶네요. 검색을 해보...
[정성태] 우선 답글 감사합니다. ^^ 그런데, 사실 저 예제는 (g...
[이승준] 수정이 안되어서... byteArray는 BYTE* 타입입니다...
글쓰기
제목
이름
암호
전자우편
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'>.NET 7 - AOT(ahead-of-time) 컴파일</h1> <p> 여기서 잠깐, 닷넷 AOT 역사에 대해 잠깐 훑어볼까요? ^^ <br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Conversation about crossgen2 ; <a target='tab' href='https://devblogs.microsoft.com/dotnet/conversation-about-crossgen2/'>https://devblogs.microsoft.com/dotnet/conversation-about-crossgen2/</a> </pre> <br /> 가장 처음에, ".NET Framework"의 NGen이 있었습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Ngen.exe (Native Image Generator) ; <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/framework/tools/ngen-exe-native-image-generator'>https://learn.microsoft.com/en-us/dotnet/framework/tools/ngen-exe-native-image-generator</a> </pre> <br /> NGen은 윈도우 플랫폼 종속적이었고, 비록 native 코드를 생성하긴 했지만 그래도 managed 모듈을 필요로 했습니다.<br /> <br /> 이후 "Windows Phone"이 나와 크로스-플랫폼을 지원해야 하면서 별도로 AOT 코드 생성기가 개발됩니다. 얼마 안 돼 윈도우 폰은 사라졌지만, 다행히 그 역사를 .NET Core의 CrossGen으로 세대를 이어갑니다. 이후 닷넷 6에 포함된 <a target='tab' href='https://devblogs.microsoft.com/dotnet/announcing-net-6-preview-1/#crossgen2'>CrossGen2</a>는 이전 CrossGen을 (몇몇 기능 향상과 함께) C# 언어로 포팅한 것에 해당합니다.<br /> <br /> 하지만, NGen부터 CrossGen2까지의 공통점이 있다면, 바로 <a target='tab' href='https://www.sysnet.pe.kr/2/0/13159#r2r'>native + managed 모듈이 함께 필요</a>하다는 점입니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 자, 그리고 이제 .NET 7에서 진정한 네이티브 코드 생성기인 AOT 컴파일 기능이 공식적으로 포함됐습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Native AOT Deployment ; <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/'>https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/</a> </pre> <br /> 실제로 테스트를 해볼까요? ^^<br /> <a name='publish_aot'></a> <br /> 간단한 닷넷 7 콘솔 애플리케이션을 만들고, csproj에 PublishAot 옵션을 추가합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net7.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> <span style='color: blue; font-weight: bold'><PublishAot>true</PublishAot></span> <<a target='tab' href='https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/?tabs=net8plus%2Cwindows#aot-compatibility-analyzers'>IsAotCompatible</a>>true</IsAotCompatible> <!-- IsTrimmable, EnableTrimAnalyzer, EnableSingleFileAnalyzer, EnableAotAnalyzer 4개의 값을 true로 설정한 효과 (.NET 8부터 지원) ----> </PropertyGroup> </Project> </pre> <br /> 이후 "dotnet publish" 해주면 ".\bin\Debug\net7.0\win-x64\publish" 디렉터리에 실행 파일과 PDB 파일이 생성됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // 디버그 빌드 기준 net7_pubone_sample.exe, 약 4.69MB net7_pubone_sample.pdb, 약 15.9MB // 릴리스 빌드 기준 net7_pubone_sample.exe, 약 2.85MB net7_pubone_sample.pdb, 약 12.1MB </pre> <br /> 당연히, net7_pubone_sample.exe만 (닷넷 런타임이 설치되지 않은) 다른 PC에 복사해 실행할 수 있습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 이렇게 완전히 AOT로 빌드된 모듈은 역어셈블이 가능할까요?<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# - PublishSingleFile로 배포한 이미지의 역어셈블 가능 여부 (난독화 필요성) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13161'>https://www.sysnet.pe.kr/2/0/13161</a> </pre> <br /> 위의 글에서 살펴본 .NET 6와는 달리, 이제 Windbg는 mscordaccore.dll을 로드하려고 해도,<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\mscordaccore.dll이 있는 경우, 0:004> <span style='color: blue; font-weight: bold'>.cordll -lp c:\temp</span> CLR DLL status: No load attempts </pre> <br /> 실패합니다. 즉, 이제는 대상 프로세스를 .NET 프로세스로 볼 수 없는 것입니다. 비록 관리 Heap을 사용해 GC는 발생하겠지만 그 외의 모든 면에서 일반 native 프로세스와 동일한데요, 달리 말하면, 그냥 여러분들이 C/C++로 GC 기능을 구현한 다음 그것을 활용해 코딩한 것과 같은 것입니다.<br /> <br /> 따라서, AOT로 빌드한 경우라면 이제 native 모드로 디버깅을 해야 하는데,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:005> <span style='color: blue; font-weight: bold'>~</span> 0 Id: 2df8.1424 Suspend: 1 Teb: 00000036`a2077000 Unfrozen 1 Id: 2df8.2144 Suspend: 1 Teb: 00000036`a2079000 Unfrozen 2 Id: 2df8.21c8 Suspend: 1 Teb: 00000036`a207b000 Unfrozen 3 Id: 2df8.2210 Suspend: 1 Teb: 00000036`a207d000 Unfrozen 4 Id: 2df8.2e44 Suspend: 1 Teb: 00000036`a207f000 Unfrozen . 5 Id: 2df8.1ddc Suspend: 1 Teb: 00000036`a2081000 Unfrozen 0:005> <span style='color: blue; font-weight: bold'>~0s</span> rax=0000000000000006 rbx=00000036a22ff748 rcx=0000000000000050 rdx=0000000000000000 rsi=0000000000000050 rdi=0000000000000000 rip=00007ffabe40d184 rsp=00000036a22ff478 rbp=00000036a22ff5c0 r8=0000000000000020 r9=0000000000000001 r10=0000000000000000 r11=00000188b1aeffe0 r12=0000000000000000 r13=0000000000000000 r14=0000000000000000 r15=0000000000000000 iopl=0 nv up ei pl zr na po nc cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000244 ntdll!NtReadFile+0x14: 00007ffa`be40d184 c3 ret 0:000> <span style='color: blue; font-weight: bold'>k</span> # Child-SP RetAddr Call Site 00 00000036`a22ff478 00007ffa`bbaf5593 ntdll!NtReadFile+0x14 01 00000036`a22ff480 00007ff6`5f259a0d KERNELBASE!ReadFile+0x73 02 00000036`a22ff500 00007ff6`5f25953f net7_pubone_sample!System_Console_Interop_Kernel32___ReadFile_g____PInvoke_43_0+0x7d 03 00000036`a22ff5d0 00007ff6`5f25ad36 net7_pubone_sample!System_Console_Interop_Kernel32__ReadFile+0x5f [/_/src/libraries/System.Console/src/Microsoft.Interop.LibraryImportGenerator/Microsoft.Interop.LibraryImportGenerator/LibraryImports.g.cs @ 412] 04 00000036`a22ff640 00007ff6`5f25ab6b net7_pubone_sample!System_Console_System_ConsolePal_WindowsConsoleStream__ReadFileNative+0xc6 [/_/src/libraries/System.Console/src/System/ConsolePal.Windows.cs @ 1156] 05 00000036`a22ff6f0 00007ff6`5f25e86c net7_pubone_sample!System_Console_System_ConsolePal_WindowsConsoleStream__Read+0x6b [/_/src/libraries/System.Console/src/System/ConsolePal.Windows.cs @ 1114] 06 00000036`a22ff770 00007ff6`5f1dcbef net7_pubone_sample!System_Console_System_IO_ConsoleStream__Read+0x8c [/_/src/libraries/System.Console/src/System/IO/ConsoleStream.cs @ 76] 07 00000036`a22ff7e0 00007ff6`5f1dcd6d net7_pubone_sample!S_P_CoreLib_System_IO_StreamReader__ReadBuffer+0x1af [/_/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @ 616] 08 00000036`a22ff8a0 00007ff6`5f25ee63 net7_pubone_sample!S_P_CoreLib_System_IO_StreamReader__ReadLine+0x7d [/_/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @ 785] 09 00000036`a22ff960 00007ff6`5f25a1f5 net7_pubone_sample!System_Console_System_IO_SyncTextReader__ReadLine+0x63 [/_/src/libraries/System.Console/src/System/IO/SyncTextReader.cs @ 77] 0a 00000036`a22ff9c0 00007ff6`5f25f688 net7_pubone_sample!System_Console_System_Console__ReadLine+0x35 [/_/src/libraries/System.Console/src/System/Console.cs @ 722] 0b 00000036`a22ffa10 00007ff6`5f2ceaa7 net7_pubone_sample!net7_pubone_sample_net7_pubone_sample_Program__Main+0x28 [E:\net7_pubone_sample\net7_pubone_sample\Program.cs @ 8] 0c 00000036`a22ffa50 00007ff6`5f2ceb3a net7_pubone_sample!net7_pubone_sample__Module___MainMethodWrapper+0x17 0d 00000036`a22ffa80 00007ff6`5f0ceb6e net7_pubone_sample!net7_pubone_sample__Module___StartupCodeMain+0x8a 0e 00000036`a22ffae0 00007ff6`5f0bdf54 net7_pubone_sample!wmain+0xae [D:\a\_work\1\s\src\coreclr\nativeaot\Bootstrap\main.cpp @ 205] 0f (Inline Function) --------`-------- net7_pubone_sample!invoke_main+0x22 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 90] 10 00000036`a22ffb30 00007ffa`bcba74b4 net7_pubone_sample!__scrt_common_main_seh+0x10c [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288] 11 00000036`a22ffb70 00007ffa`be3c26a1 KERNEL32!BaseThreadInitThunk+0x14 12 00000036`a22ffba0 00000000`00000000 ntdll!RtlUserThreadStart+0x21 </pre> <br /> sos 확장의 도움 없이 닷넷 프로그램을 문제 분석하는 경험은... 그다지 유쾌하진 않을 것입니다. ^^ 참고로, dotnet-dump 역시 Native AOT로 빌드된 프로세스는 인식하지 못합니다.<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'>dotnet-dump ps</span> No supported .NET processes were found </pre> <br /> (그래도, GC Heap 관련 정보는 디버깅할 수 있는 방법을 제공해줘야 하지 않을까요? ^^;)<br /> <br /> <hr style='width: 50%' /><br /> <br /> <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/#limitations-of-native-aot-deployment'>문서</a>에 나오지만,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Limitations of Native AOT deployment <ul><li>No dynamic loading (for example, Assembly.LoadFile)</li><li>No runtime code generation (for example, System.Reflection.Emit)</li><li>No C++/CLI</li><li>No built-in COM (only applies to Windows)</li><li>Requires trimming, which has <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/core/deploying/trimming/incompatibilities'>limitations</a></li><li>Implies compilation into a single file, which has known <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/core/deploying/single-file/overview?tabs=cli#api-incompatibility'>incompatibilities</a></li><li>Apps include required runtime libraries (just like <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/core/deploying/#publish-self-contained'>self-contained apps</a>, increasing their size, as compared to framework-dependent apps)</li></ul></pre> <br /> 당연히 .NET Reflection과 같은 기능들은 AOT 지원이 안 됩니다. 그리고 .NET 7의 BCL 중에서도 위와 같은 조건에 걸리는 타입들이 많기 때문에 아직은 AOT가 WPF/WinForm 등의 응용 프로그램은 지원하지 못합니다. 사실 저런 조건들이 AOT 입장에서 제약이지, WPF/WinForm 등의 개발 프레임워크를 만들 때는 유용하게 쓸 수 있는 기술들이어서 제법 광범위하게 사용했던 것입니다. 그런 이유에서, <a target='tab' href='https://www.sysnet.pe.kr/2/0/13470#cp4'>Native AOT가 과연 "아직" WPF/WinForm을 지원하지 못한 것인지</a>, "영원히" 지원하지 못할 것인지는 두고 봐야 알 수 있을 것입니다. ^^<br /> <br /> 결국 현재의 닷넷 7 AOT는 "Console Application"을 대상으로 한 프로젝트만 지원하고 WPF/WinForm 등의 프로젝트에서 PublishAot 옵션을 주면 "error NETSDK1168: WPF is not supported or recommended with trimming enabled. Please go to <a target='tab' href='https://aka.ms/dotnet-illink/wpf'>https://aka.ms/dotnet-illink/wpf</a> for more details." 빌드 오류가 발생합니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 참고로, <a target='tab' href='https://www.sysnet.pe.kr/2/0/13464'>AOT 빌드는 UnmanagedCallersOnlyAttribute 특성과 어우러져</a> Native API(윈도우의 경우 Win32 DLL)로 동작하는 라이브러리를 만들기에는 아주 이상적인 환경을 제공합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Build native libraries ; <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/#build-native-libraries'>https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/#build-native-libraries</a> 관련 예제 코드 ; <a target='tab' href='https://github.com/dotnet/samples/tree/main/core/nativeaot/NativeLibrary'>https://github.com/dotnet/samples/tree/main/core/nativeaot/NativeLibrary</a> </pre> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
5025
(왼쪽의 숫자를 입력해야 합니다.)