티스토리 뷰

Universal CRT

14년 6월 ms에서 기존CRT의 논리파트를 VCRuntime과 Stable로 나누었는데, VCRuntime은 프로세스 시작과 예외처리와 같은 것을 지원하며, stable은 CRT의 순수한 라이브러리를 지원하도록 설계했다. 여기서 stable은 또 AppCRT와 DesktopCRT로 나뉜다. ms가 이렇게 CRT구조를 나눈 이유는 in-place 서비스를 제공하기 위해서 나눴다고 했는데 15년 말에 ms가 두개로 나눈 stable 파트를 다시 하나의 라이브러리로 바꾸었는데 이게 바로 Universal CRT가 되겠다. (이 또한 in-place 서비스 제공을 위함이라 함)

Universal CRT는 기존 CRT와 다르게 Visual C++의 일부가 아닌 WindowsKits의 일부로 ucrtbase.dll 과 ucrtbased.dll의 이름을 가진다.

visual studio 2015에서는 default로 WindowsKits 디렉토리를 참조하도록 하여 ucrtbase.dll를 사용하도록 했으며, vs2015이전 버전에서는 아래와 같은 주소를 수동으로 경로를 추가해주어야 한다.

$(UniversalCRT_IncludePath)
$(UniversalCRT_LibraryPath_x86)
$(UniversalCRT_LibraryPath_x64)
$(UniversalCRT_LibraryPath_arm)

아래 표는 기존 라이브러리가 어떻게 대응 되는지 보여준다.

 Release DLLs  (/MD  ) :

 msvcrt.lib

 vcruntime.lib

 ucrt.lib

 Debug DLLs    (/MDd) :

 msvcrtd.lib

 vcruntimed.lib

 ucrtd.lib

 Release Static (/MT  ) :

 libcmt.lib

 libvcruntime.lib

 libucrt.lib

 Debug Static    (/MTd) :

 libcmtd.lib

 libvcruntimed.lib

 libucrtd.lib


결론적으로, Windows10에 들어스면서 visual studio 2015를 사용할 때, 기존 CRT라이브러리가 vcruntime과 ucrtbase로 나뉘었으며 CRT를 사용하기 위해 두가지 라이브러리를 써야한다는 것이다.

이전 OS(windowsXp, 7, 8, 8.1) 에서는 VCRedist(재배포 패키지)를 설치 한다면 Universal CRT를 사용할 수 있다.

//

//

 자 여기까진 MS에서 공지한 내용인데, 이게 말로는 재배포 패키지를 설치하면 윈도우 10 하위 OS에서 잘된다고 했는데, 이게 막상 적용해 보려고 하면 예상치 못하게 오류를 내뱉는 상황을 겪게 된다.

열의 아홉은 재배포 패키지를 설치하면 Universal CRT가 잘 적용되 아무런 문제 없이 되는데, 이게 윈도우 10 하위 OS에서 이상한 케이스에 걸리면 (윈도우 업데이트 오류 pc, dll손상을 의심케하는pc 등등) 재배포 패키지를 설치해도 오류를 뿜는 경우가 발생한다.

 내 케이스로 말하자면 Visual Studio 2015 Express에서 MT설정한 DLL을 만들고, 이를 델파이에서 갖다 쓰도록 프로그램을 짰는데, 이게 윈도우 xp, 윈도우7, 윈도우 8.1, 윈도우 10에서 테스트 했을때 아무런 문제없이 잘 작동하던게 특정 pc( 윈도우 7 sp1, dll내부손상 의심, 윈도우 업데이트 오류)에서 재배포 패키지를 설치하고도 VS2015에서 만든 DLL로드시 DLL 초기화 루틴 실패 (code 1114)를 일으키더라 초기엔 Dependency의 문제로 여겨 추가 Dependency walker로 Dependency 확인하고, 모두 제거해도 1114 에러 코드를 뱉어내고, DllMain 함수를 호출하지도 않고 이게 뭐가 문제인지 원인파악조차 안되 골머리 썩히던 찰나에 API Monitor와 windbg를 사용해 프로세스를 분석해보니

 내가 만든 DLL이 프로세스 시작 후 메모리에 적재 되는것 까지 확인하였고, 델파이에서 LoadLibrary 함수를 호출하여 메모리 적재된 DLL의 핸들을 얻어오는 과정에서 에러를 뱉어나는것을 확인하였다.

 API Monitor에서 확인해보면 GetProcAddress 함수를 사용해 "FlsAlloc"을 호출하는데 여기서 중복 호출에 따른 동기화 실패로 오류가 나더라 앞서 내가 MT설정으로 DLL을 만들었다고 했는데 MT(Multi Thread)를 사용하면 CRT는 멀티스레드 환경을 지원하기 위해서 FLS(Fiber Local Storage)라는 것을 사용한다고 한다.

 CRT 초기화 루틴에서 FlsAlloc(_freefls)를 호출해서 등록하고 이는 다시 CRT가 종료될 때 회수된다. 일반적인 DLL을 만들면 우리의 DllMain이 호출되기 전에 FlsALloc으로 flsindex를 할당 받고, DLL_PROCESS_DETACH가 호출되면 CRT가 그것을 정리하는 원리인 것이다. 

(레퍼런스 - http://www.jiniya.net/wp/archives/5253 여길읽어보라 아주 정확히 FlsAlloc에대해 설명해 줄 것이다.)

 근데 내가 만든 DLL은 바로 여기서! DllMain을 호출하기 전에! 초기화 루틴 실패로 에러를 뱉어내는 것이다. 그래서 CRT에 대해 알아보니, Universal CRT라는 놈을 알게 되었고 이놈이 바로 VS2015 부터 적용되었으며 (내가 VS2015를 사용했다) VS2015를 사용하여 만든 프로그램(DLL)은 반드시  Universal CRT를 사용한다는 것 까지 알게 되니 어떻게 문제를 해결해야 하는지 알겠더라. 이리 저리 테스트를 해보니 Universal CRT를 사용하지 않고(VS2015 아래버전으로 빌드) DLL을 만들면 어느 OS 환경에서도 잘 돌아가는 것을 확인했다. 

VS2015 Express로 빌드했던 환경을 VS2013으로 다운그레이드를 하고 문제의 PC에서 테스트한 결과! 

DLL로드 성공!! 델파이 프로그램 정상작동!!


요약하자면, VS2015로 빌드를하고 여러 테스트 환경을 구축하여 잘돌아가던 프로그램이 어떤 특정 PC (윈도우 업데이트 오류, dll손상이 의심가는)에서 문제가 된다면 그것은 Universal CRT를 도입하는 과정에서 MS의 실수로 인해 발생한 버그일 가능성이 있다는 것이며, 이는 어렵게 생각말고 VS2015이하 버전으로 다운그레이드 하여 Universal CRT를 사용하지 않는 환경으로 바뀌주면 해결된 가능성이 있다.


p.s 언제나 느끼지만, 문제가 발생하면 어렵게 생각말고 원인만 잘 찾으려 노력하면 문제는 쉽게 해결할 수 있는 것 같다.

'programming > c++' 카테고리의 다른 글

[c++] visual studio remote debug  (0) 2017.02.02
[c++] MT, MD 차이  (0) 2017.02.01
[c++] 접근 제어 지시자  (0) 2016.05.18
[c++] new 와 delete  (0) 2016.05.18
[c++] 함수 오버로딩  (0) 2016.05.18
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함