side by side assembly는 윈도우 운영체제의 구조적인 문제 중 하나였던 DLL hell 문제를 해결하기 위해 윈도우 XP에서부터 처음으로 도입한 기술입니다. (그 후 2003, 비스타 등등..)
전통적으로 Win32 EXE가 symbol import table을 통해 읽어들이도록 지정되어 로드 타임 때(런타임이 아닌) 실제로 읽어들이는 외부 dll은
해당 EXE/모듈이 있는 디렉토리, 커런트 디렉토리, 윈도우 및 윈도우 시스템 디렉토리, path 환경변수가 걸린 디렉토리 등등의 순서대로 탐색합니다. 물론 kernel32, gdi32, user32 같은 시스템 dll도 다 여기에 속하고요.
런타임이 아니라 로드(load) 타임이기 때문에
이런 dll 파일이 존재하지 않거나 한 심볼이라도 읽어들이지 못했다면
그 프로그램은 그냥 속수무책으로 전혀 실행되지 못합니다.
그런 상황에서의 처리를 프로그래머가 능동적으로 전혀 할 수 없다는 게 큰 약점입니다.
* * * * * * *
자, 이런 전통적인 로딩 방식은 잠재적인 문제를 품고 있습니다.
먼저, 한 회사에서 만들어서 여러 디렉토리에 상주하는 다수의 프로그램들이 자기네끼리 한데 공유하는 dll의 경로를 지정할 수가 없습니다. 기껏 잘 해 봐야 path 환경변수 지정이 고작인데, 이는 정량적인 방법은 아닙니다. 그 경로 지정 자체가 이미 '런타임'에만 가능한데, dll 로딩 경로는 '로드 타임'에 결정되어야만 하기 때문입니다.
이런 딜레마로 인해 조금이라도 한데 공유하는 dll은 반드시 탐색한다는 게 보장되는 윈도우 시스템 디렉토리에다 집어넣는 게 무조건 장땡이었고, 이 때문에 거기는 운영체제가 기본 제공하는 dll과, 응용 프로그램들이 집어넣은 dll로 인해 몇천 개의 수백 MB에 달하는 dll들로 난장판이 되어 버렸습니다.
둘째, dll을 식별하는 수단이 이름이 유일한지라, 이름이 같지만 다른 위치에 있거나 버전이 다른 엉뚱한 dll이 로딩되었을 때 대처할 방법이 전혀 없습니다. 이는 보안상으로도 매우 위험합니다. 이게 바로 말 그대로 DLL hell의 근본 원인입니다.
msvcrt.dll, mfc42.dll, comctl32.dll 이런 것들은 윈도우 98 이래로 이름은 같지만 그동안 버전별로 프로토콜, 인터페이스가 내부적으로 상당히 바뀐 게 많습니다. 내부적으로 문서화되지 않은 동작 방식이야 두말할 것도 없겠죠. 그런데 본이 아니게 그런 문서화되지 않은 동작 방식에 의존하던 프로그램이 다른 버전의 dll을 읽어들이면 그때부터 프로그램의 안정성은 뻑이 나기 시작한다는 것입니다.
* * * * * * *
side by side assembly 기술은 위의 두 문제를 모두 원천적으로 해결하는 과정에서 나온 해결책입니다.
첫째, 이제 MS에서는 지금까지 내놓았던 시스템 dll 이후로 제 3자 응용 프로그램의 고유 dll이 윈도우 시스템 디렉토리에다 들어가는 것을 비추(discourage) 행위로 규정했습니다.
호환성 때문에 저걸 완전 금지할 수는 없구요. <날개셋>의 경우 윈도우 IME가 반드시 시스템 디렉토리에만 있어야 하기 때문에 시스템 디렉토리를 건드립니다.
그런 특수한 경우가 아니면, 응용 프로그램이 사용하는 모든 dll은 가능하면 응용 프로그램 디렉토리에다가만 두어라! 시스템 디렉토리는 이제 운영체제만 자체적으로 쓰겠다. 이곳의 포화는 이제 그만!
메모리가 부족한 상황에서 자원을 하나라도 더 공유하려고 시스템 디렉토리를 두었지만 이제 그런 정책까지 과감히 포기한 것입니다.
이게 파격이 큰 이유는, 심지어 준시스템 dll이나 다름없는 개발툴의 런타임 dll들까지 이제 시스템 디렉토리에다 넣지 않기로 했기 때문입니다. 비주얼 C++ 기준으로 6.0이 끝입니다. 6.0으로 만든 EXE만이 mfc42.dll이나 msvcrt.dll 정도는 윈도우 98 이래로 어디에나 편재해(ubiquotous) 있다고 간주할 수 있습니다. 즉 'known dll'인 것입니다.
그 이후로, 닷넷으로 만든 EXE는 자신이 사용하는 msvcr71, msvcr80, mfc71 등등을 배포 패키지에다 자체 내장해야 합니다. 윈도우 XP, 비스타의 시스템 디렉토리에 기본으로 들어있지 않습니다. 응용 프로그램이 자기가 쓰는 런타임 dll은 무조건 복사본을 자기 디렉토리에다 심어야 합니다.
둘째. 첫째가 상당히 자비심 없는 정책이 되었는데 그럼 대책이 뭐냐 하면,
내가 정확하게 읽어들이고자 하는 DLL의 고유 식별자, CPU 아키텍쳐, 언어, 정확한 버전 번호를 exe 내부에다 리소스의 형태로 xml 문서로 명시해 놓는 것입니다. 한 컴퓨터의 디렉토리 구조와는 완전히 무관하게 말입니다. 메니페스트 xml이라고들 하죠.
그 메니페스트용으로 등록된 핵심 dll들은 윈도우 시스템 디렉토리에 있는 게 아니라 WinSxS 밑에 각 버전과 아키텍쳐별로 별개의 디렉토리에 저장됩니다.
윈도우 XP라면 gdiplus, comctl32 이런 dll이 존재할 텐데요. 이름이 같은데 윈도우 XP 원본이 갖고 있던 comctl32, SP2가 갖고 있던 comctl32 이런 버전별 '스냅샷'들이 전부 따로 저장되어 있는 걸 알 수 있습니다. 비스타에는 이보다 훨씬 더 많이 있습니다. 내가 만들어서 공유하기를 원하는 핵심 dll도 이런 식으로 등록, 배포할 수 있습니다.
* * * * * * *
윈도우 XP에서 이런 새로운 방식으로 곧바로 사용되기 시작한 대표적인 운영체제 컴포넌트가 바로 comctl32.dll 입니다. advapi, shell32 같은 거 말고 comctl32만 이런 형태가 됐습니다.
얘는 윈도우 9x 시절부터도 인터넷 익스플로러와 함께 곧잘 업데이트되었던 파일이고 특히 윈도우 9x의 안정성을 떨어뜨리는 주범이기도 했습니다.
이 파일이 특히 윈도우 XP에서는 비주얼 스타일 테마의 등장으로 인해 구조가 완전히 뒤바뀌었는데, 정작 새로운 파일은 WinSxS에 있고, system 디렉토리에 있는 파일은 버전이 여전히 5.82이고 옛날 IE 5.x 시절과 별 차이 없습니다. 한 마디로, system 디렉토리에 있는 파일은 옛날 프로그램과의 호환성을 위해 시간이 그 상태 그대로 정지해 버린 것입니다.
그 반면, 메니페스트에다가 새로운 comctl32를 사용하도록 지정을 해 놓은 exe만이 대화상자에서 윈도우 XP 이후의 비주얼 스타일 적용을 받을 수 있는 구조가 됐습니다. comctl32를 이 방법을 통해 최신 버전으로 지정하지 않으면, 옛날 프로그램에서는 알록달록한 테마만 나오지 않는 게 아니라 하이퍼링크 컨트롤 같은 XP에서 새로 추가된 컨트롤을 쓸 수도 없고, 비스타에서 그 유명한 TaskDialog 함수조차도 쓸 수 없습니다.
* * * * * * *
유행 따라서 윈도우 XP 비주얼 스타일을 쓰려고 으레 '리소스' 형태로 따로 작성해 온 메니페스트가 비주얼 스튜디오 2005에서는 개발툴 차원에서 통합적으로 지원되는 요소가 되었습니다. 2005는 이제 배포 대상 플랫폼으로서 윈도우 9x의 지원을 완전히 제껴 버리고 CRT와 MFC DLL 역시 side by side assembly 형식으로"만" 로딩하게끔 정책이 바뀌었습니다.
이게 언뜻 보기에 골때리는 이유가 뭐냐 하면, 이제 2005로 만든 exe는 msvcr80.dll이 시스템 디렉토리나 심지어 그 exe 디렉토리에 같이 있다고 하더라도 winsxs 설정이 안 되어 있으면, '응용 프로그램 구성이 잘못됐다'며 실행 안 되기 때문입니다. (msvcr80이 자체적으로 퇴짜를 때리는 듯.)
그냥 msvcr80, mfc80 같이 슬쩍 복사해 놓는 방식으로는 실행 못합니다. 이들을 winsxs 정식 등록을 해 주든가, 아니면 앗싸리 MS에서 배포하는 VS 2005 redistributable 프로그램을 같이 배포해야 합니다.
그런데 도대체가 VS 2005 redist.도 따로 있고, VS 2005 sp1 resit.도 따로 있습니다.
윈도우 비스타에 기본 내장되어 있는 msvcr80의 버전이 다르고, 오피스 2007이 사용하는 msvcr80의 버전이 다릅니다. 세부 버전 숫자가 하나라도 다르면 로딩 안 되는 게 일단 보안과 안정성 면에서는 나아진 점입니다만, 도대체 같은 기능을 하는 CRT DLL을 왜 자꾸 이렇게 바꿔 대는지, 도대체 앞으로는 또 어느 장단에 맞춰 춤을 추라는 소리인지 헷갈립니다.
MFC도 아니고 CRT가 뭐가 그렇게 자주 바뀌어야 하는지.. 그냥 아무 걱정 없이 윈도우 시스템 디렉토리의 msvcrt만 쓰던 시절이 살짝 그립기도 합니다. strcpy, malloc 이런 고전적인 함수가 도대체 뭐 변할 일이 있나요? (물론 보안 쪽으로 계속 더욱 강력해져 오긴 했죠. strtok_s, strcpy_s 이런 것도 마음에 듦.)
비주얼 스튜디오 2005로 처음으로 만들어 본 <날개셋> 4.8 64비트 바이너리들은 이런 잡음을 원천 봉쇄하기 위해, 메모리 낭비를 감수하고 일단 모든 바이너리에 CRT 라이브러리를 static 링크를 해 버렸습니다. 64비트 에디션을 앞으로 계속 이렇게 만들지는 미지수입니다.
다만, 이도 저도 못한 신세가 되어 버린 게 비주얼 스튜디오 2002/2003의 CRT DLL인 msvcr71입니다. side by side assembly 형식으로 완전히 넘어간 것도 아니면서, 운영체제의 기본 지원도 못 받아서 이 파일의 배포와 로딩은 재래식으로 응응 프로그램이 그냥 알아서 해야 합니다. 이제 VS 2005 쓰라고 64비트 지원도 중단됐고 그냥 과도기 DLL로 역사 속으로 사라지게 되었습니다.
VS 2003도 서비스 팩 1이 나온지라 버전이 살짝 다른 msvcr71.dll 두 버전이 존재하게 되었지만 겉보기 상으로 차이는 없는 듯합니다.
Posted by 사무엘