성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
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'>C# - 재현 가능한 빌드(reproducible builds) == Deterministic builds</h1> <p> 아래의 글에,<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='http://www.jiniya.net/ng/2018/07/reproducible-builds'>http://www.jiniya.net/ng/2018/07/reproducible-builds</a> </pre> <br /> "재현 가능한 빌드"에 대한 이야기가 나옵니다.<br /> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> 윈도우 10부터 <span style='color: blue; font-weight: bold'>재현 가능한 빌드(reproducible builds)</span>로 방향을 정했고, 그 정책의 일환으로 타임스탬프를 <a target='tab' href='https://devblogs.microsoft.com/oldnewthing/20180103-00/?p=97705'>타임스탬프가 아닌 해시 값으로 변경</a>했다는 것이다. 재현 가능한 빌드라는 건 한마디로 정리하면 <span style='color: blue; font-weight: bold'>동일 소스 코드로 컴파일을 하면 동일 바이너리가 생성된다는 것을 보장</span>한다는 개념이다. 즉, 니가 소스 코드를 한 줄도 안 고쳤다면 넌 어디에서건 다시 컴파일해도 동일한 바이너리를 가지게 된다는 의미다 </div><br /> <br /> 오호... 그렇다면 C# 컴파일러에도 동일한 기능이 구현되어 있지 않았을까요? 역시나 검색해 보니 다음의 글이 나옵니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Deterministic builds in Roslyn - April 5th, 2016 ; <a target='tab' href='http://blog.paranoidcoding.com/2016/04/05/deterministic-builds-in-roslyn.html'>http://blog.paranoidcoding.com/2016/04/05/deterministic-builds-in-roslyn.html</a> </pre> <br /> 즉, C# 컴파일러도 "/deterministic" 옵션을 제공하며 이것을 켠 경우에 다음의 3가지 값들이 "동일한 소스 코드"라면 바뀌지 않고 유지된다는 것입니다.<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'>MVID</span>: a GUID identifying the PE which is newly generated for every PE produced by the compiler 1. <span style='color: blue; font-weight: bold'>PDB ID</span>: a GUID identifying the PDB matching PDB which is newly generated on every build. <span style='color: blue; font-weight: bold'>Date / Time stamp</span>: Seconds since the epoch which is calculated on every build. </pre> <br /> 실제로 한번 테스트를 해볼까요? ^^<br /> <br /> Visual Studio로 기본 콘솔 프로젝트를 만들고 빌드 후, dumpbin.exe로 확인해 보면 PDB ID와 Date / Time stamp를 확인할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\> <span style='color: blue; font-weight: bold'>dumpbin /HEADERS ConsoleApp1.exe</span> Microsoft (R) COFF/PE Dumper Version 14.14.26433.0 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file ConsoleApp1.exe PE signature found File Type: EXECUTABLE IMAGE FILE HEADER VALUES 14C machine (x86) 3 number of sections <span style='color: blue; font-weight: bold'>5B568E67 time date stamp Tue Jul 24 11:26:47 2018</span> ... [생략]... Debug Directories <span style='color: blue; font-weight: bold'>Time</span> Type Size RVA Pointer -------- ------- -------- -------- -------- <span style='color: blue; font-weight: bold'>5B568E67</span> cv 11C 00002648 848 Format: RSDS, <span style='color: blue; font-weight: bold'>{EE2C5EFD-C1B4-46D9-8730-6CA1889D1B18}</span>, 1, E:\ConsoleApp1\ConsoleApp1\obj\Debug\ConsoleApp1.pdb ... [생략]... </pre> <br /> MVID의 경우에는 dumpbin으로는 안 나오고 ildasm.exe를 실행해 보면 역어셈블된 il 파일에 다음과 같이 주석으로 담겨 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > ...[생략]... .module ConsoleApp1.exe <span style='color: blue; font-weight: bold'>// MVID: {4AC6DAEA-D641-4696-AB54-DCB39211C9B2}</span> .imagebase 0x00400000 .file alignment 0x00000200 ...[생략]... </pre> <br /> 일단, 소스 코드 변경 없이 그대로 다시 "Rebuild"를 한 후 PDB ID, MVID, time stamp를 확인해 보면 다음과 같이 바뀐 것을 볼 수 있습니다.<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'>5B568F18</span> time date stamp Tue Jul 24 11:29:44 2018 Debug Directories Time Type Size RVA Pointer -------- ------- -------- -------- -------- <span style='color: blue; font-weight: bold'>5B568F18</span> cv 11C 00002648 848 Format: RSDS, <span style='color: blue; font-weight: bold'>{F97D62CB-E0D6-4944-BCC5-CC4B49F11D01}</span>, 1, E:\ConsoleApp1\ConsoleApp1\obj\Debug\ConsoleApp1.pdb // MVID: <span style='color: blue; font-weight: bold'>{691860F5-CE27-4483-846C-5F35EAC80AEC}</span> </pre> <br /> 자, 그럼 이제 deterministic 빌드를 해볼까요? ^^ csc.exe에서는 /deterministic 옵션을 주면 되지만 Visual Studio에서는 msbuild를 이용하기 때문에 "<a target='tab' href='http://blog.paranoidcoding.com/2016/04/05/deterministic-builds-in-roslyn.html'>Deterministic builds in Roslyn</a>" 글의 설명에 따라 csproj 프로젝트 파일에 다음의 옵션을 추가하면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <Deterministic>true</Deterministic> </pre> <br /> 위의 옵션을 적용 후 최초 빌드 시에는 PDB ID, MVID, timestamp 모두 바뀝니다.<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'>F99E2A84</span> time date stamp Debug Directories Time Type Size RVA Pointer -------- ------- -------- -------- -------- <span style='color: blue; font-weight: bold'>94630E89</span> cv 76 00002664 864 Format: RSDS, <span style='color: blue; font-weight: bold'>{A3EC929A-FDFB-4EB2-AA83-70D680502520}</span>, 1, E:\ConsoleApp1\ConsoleApp1\obj\Debug\ConsoleApp1.pdb // MVID: {78A79916-27E0-4CE0-AD63-70ABB20217AD} </pre> <br /> 하지만 그 후부터는 Rebuild를 하면, 즉 이미 출력되어 있던 바이너리를 모두 삭제하고 동일한 소스 코드로부터 입력받은 경우 동일한 바이너리를 생성합니다. 실제로 출력된 바이너리에 대해 hex 비교를 해도 완전히 동일하다고 나옵니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 혹시 적용 후 생각지 못한 점이 있을까요? 한 가지 있다면, deterministic 빌드인 경우 timestamp가 더 이상 시간 값이 아닌 hash라는 점입니다. <br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [deterministic 적용 전 - 시간 데이터 출력] 5B568E67 time date stamp Tue Jul 24 11:26:47 2018 [deterministic 적용 후 - hash 값이기 때문에 시간 데이터를 출력 못함] F99E2A84 time date stamp </pre> <br /> 이 때문에 PDB Viewer 같은 도구를 이용해 해당 바이너리의 생성 시간을 알 수 없습니다. 아마 대부분의 개발 업체에서 바이너리로부터 생성 시간을 얻어와 어떤 자료로 이용하는 경우는 없을 것이기 때문에 크게 문제 될 것은 없어 보입니다.<br /> <br /> 또한, "<a target='tab' href='http://blog.paranoidcoding.com/2016/04/05/deterministic-builds-in-roslyn.html'>Deterministic builds in Roslyn</a>" 글에서도 언급하고 있지만 AssemblyVersionAttribute이나 AssemblyFileVersionAttribute 특성에 "*"를 지정해 자동 빌드 번호 증가를 명시한 경우에는 버전 정보가 달라지므로 당연히 deterministic 빌드 옵션을 켜도 매번 바뀔 수밖에 없다고 합니다. 아마도 이것 역시 대부분의 개발 업체에서는 명시적인 버전 지정을 할 것이기 때문에 제약 사항이 되진 않을 것입니다.<br /> <br /> 따라서, 이제부터는 특별한 사유가 없다면 닷넷 프로젝트의 경우 "Deterministic" 옵션을 켜두시는 것이 권장됩니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 소스 코드의 변경은 컴파일러가 받아들이는 토큰 값을 기준으로 한 것이 아닙니다. 가령, 코드 끝에 공백을 삽입하고 저장한 경우 그것은 소스 코드가 변경된 것이나 다름없습니다. 그래서 그런 경우에도 동일 바이너리가 생성되지는 않습니다.<br /> <br /> 그 외에, /pathmap 옵션도 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > /pathmap - <PathMap>source=dest</PathMap> </pre> <br /> 이 옵션은 PDB를 위한 것인데요, PDB의 경우 디버깅뿐만 아니라 콜 스택을 뜨는 상황(ex: 예외 발생에 따른 이벤트 로그)에서 소스 코드가 위치한 파일 및 라인 번호를 보여주는데 사용됩니다. 따라서 소스 파일의 위치가 바뀌면 pdb도 바뀌게 되어 바이너리가 달라질 수밖에 없습니다. 그럴 때 pathmap을 지정해 공통 경로를 가질 수 있게 할 수 있어 역시나 pdb 파일이 바뀔 필요가 없어지는 것입니다.<br /> <br /> 그나저나, 이 옵션이 Visual Studio 2015의 두 번째 업데이트부터 추가되었었군요. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > New C# and VB features in Visual Studio 2015 Update 2 ; <a target='tab' href='https://devblogs.microsoft.com/dotnet/whats-new-for-c-and-vb-in-visual-studio/'>https://devblogs.microsoft.com/dotnet/whats-new-for-c-and-vb-in-visual-studio/</a> </pre> <br /> <hr style='width: 50%' /><br /> <br /> (2024-04-01 업데이트) 그리고 이것이 윈도우 10 운영체제부터 DLL 빌드에도 반영돼 PE 헤더의 TimeDateStamp가 더 이상 날짜를 의미하지 않습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Why are the module timestamps in Windows 10 so nonsensical? ; <a target='tab' href='https://devblogs.microsoft.com/oldnewthing/20180103-00/?p=97705'>https://devblogs.microsoft.com/oldnewthing/20180103-00/?p=97705</a> Instead of putting a hash in the Portable Executable timestamp field, why not create a separate field for the hash? ; <a target='tab' href='https://devblogs.microsoft.com/oldnewthing/20240815-00/?p=110131'>https://devblogs.microsoft.com/oldnewthing/20240815-00/?p=110131</a> </pre> </p><br /> <br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
7473
(왼쪽의 숫자를 입력해야 합니다.)