블로그 이미지
대갈장군

calendar

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
31

Notice

'프로그래밍/Dependency Walker'에 해당되는 글 2

  1. 2012.12.14 Dependency Walker 아이콘 & 에러와 경고의 해석
  2. 2012.12.14 Dependency Walker의 검색 가능한 의존성의 타입들
2012. 12. 14. 03:48 프로그래밍/Dependency Walker

정상적인 경우의 아이콘들


bullet

에러가 없는 정상적인 모듈들


bullet

Duplicate module. 이 모듈은 이미 어디선가 처리되어 임포트 되어 있는 모듈. 


bulletForwarded module. 다른 모듈을 호출하는 Forwarded module

bullet

Delay-load module. 런타임에 실질적으로 함수가 호출되면 로드 되는 '지연 로드 모듈'


bullet

Dynamic module. 다이나믹 모듈. 소스코드에서 LoadLibrary()를 통해 호출되는 경우를 말한다. 누가 호출했는지 모를경우에는 아마도 Profiling을 하면 찾을 수 있는듯.


bullet

다이나믹 모듈이긴 한데 LoadLibraryEx 함수를 이용하되 DONT_RESOLVE_DLL_REFERENCES플래그나 LOAD_LIBRARY_AS_DATAFILE 플래그가 셋 된 상태로 호출된 경우를 말한다. 


bullet

64-bit module. 64 운영 체제용 모듈이라는 말.



에러나 경고


bulletMissing module. 모듈이 없음. 검색 경로를 검사해 봤지만 모듈이 없는 경우. 

bullet

Invalid module. 유효하지 않은 모듈. 


bullet

Module warning. 경고! 이 모듈은 부모 모듈에서 요구하는 하나 혹은 그 이상의 호출 함수가 없는 경우이거나 또는 CPU 타입이 틀렸거나, 또는 로드 될때 초기화에 실패한 경우다. Parent Import Function List View를 이용하는 빠진 함수가 어떤 놈인지 확인 가능하다.


bullet

Delay-load module warning. 이 모듈은 부모 모듈에서 요구하는 한개 이상의 호출 함수가 없는 경우이거나 잘못된 CPU 타입인 경우이다. 마찬가지로 Parent Import Function List View를 이용하면 빠진 함수가 어떤 녀석인지 확인 가능하다. 


bulletDynamic module warning. 이 놈도 마찬가지인데 다만 이것은 그냥 경고에서 그칠 경우가 많다. 왜냐면 다이나믹하게 연결하기 때문에 실행중에는 문제없이 돌아갈 수 있다는 점. 

 

Dependency Walker는 수많은 에러와 경고 메세지를 출력해 내는데 몇몇은 아주 중요하고 나머지는 무시해도 되거나 문제가 되지 않는 놈들이다. 대부분의 에러 메세지는 두 개중에 하나다. Load-time failure 혹은 Run-time failure.


Load-time failure이라 함은 어플리케이션이 시작도 못해보고 실패한다는 말이다. 어플리케이션을 실행하기 위해 꼭 필요한 모듈들이 없거나 문제가 있어서 시작도 못했다는 것. 정확하게 말하자면 implict나 forward 의존을 하는 모듈들이 존재 하지 않거나 잘못되어 운영체제가 함수를 호출하는 entry point를 전혀 얻어내지 못한 경우다. 또 다른 형태의 load-time failure로는 윈도우가 기반이 아닌 모듈을 호출하거나 CPU 타입이 다른 모듈을 호출하거나 하는것이다. 


자, 몇몇 에러 메세지를 보자. 


bulletThe dynamic link library BAR.DLL could not be found in the specified path...

bulletThe procedure entry point FOO could not be located in the dynamic link library BAR.DLL.

bulletThe application or DLL BAR.DLL is not a valid Windows image.

bulletThe application failed to initialize properly.

bulletInitialization of the dynamic link library BAR.DLL failed. The process is terminating abnormally.

bulletThe image file BAR.EXE is valid, but is for a machine type other than the current machine.


대부분의 load-time 문제들은 Dependency Walker를 통해 즉각적으로 탐지 가능하다. Dependency Walker를 이용하면 implicit, forward 그리고 delay-load dependency를 바로 체크하여 문제가 있으면 알려준다. Implicit와 Forward 의존의 경우 실행이 아예 불가능하고 delay-load 의존의 경우에는 load-time에 필요하지는 않아서 일단 프로그램이 돌아가더라도 문제가 있는 모듈을 호출하는 순간 프로그램이 뽁 날 것이다. 


Run-time 의존적 모듈들은 프로그램이 실행된 후에 연결 (로딩) 된다. 일반적으로 이런 경우는 소스 코드 내에서 LoadLibrary 같은 함수를 호출함으로써 이루어진다. 일단 모듈이 로딩 된 후에는 어플리케이션이 GetProcAddress 함수를 이용해서 특정한 함수의 위치를 얻어낸다. 만약 어플리케이션이 아주 안전하게 작성이 되어서 실패를 핸들링 할 수 있다면 경고는 무시된다.


Run-time 의존을 사용하는 이유는 많이 있다. 우선 load-time 퍼포먼스가 증가한다. 즉, 실행하려고 더블클릭 하는 순간의 시간을 단축할 수 있다는 말. 왜냐면 필요할때 연결하면 되니까. 예를 들면 프린팅 하는 함수를 가진 모듈은 사용자가 정말로 프린트를 하고 싶을때 연결하면 된다. 그리고 또 다른 장점으로는 모듈이나 함수가 존재하지 않는 경우에 사용할 수 있다. 무슨말이냐 하면, 윈도우 NT 특유의 함수를 호출하고자 한다고 치자. 만약 이 모듈을 암묵적으로 포함하게 되면 이 어플리케이션이 윈도우 XP에서 실행되는 경우, 해당 모듈을 없으므로 에러가 터지고 실행이 안된다. 하지만 런타임으로 연결하면 일단 해당 컴퓨터에 필요로 하는 모듈이 있는지 없는지가 실행중에 파악이 된다. 고로 없으면 없는대로 다른 메세지나 처리를 통할수 있다. 암묵적으로 연결하면 아예 실행이 안된다.


런타임 의존은 두 가지 타입이 있다. 하나는 Explicit dependency (혹은 Dynamic dependency) 그리고 나머지 하나는 Delay-load dependency 이다. 명시적 의존은 (Explicit Dependency) 어플리케이션이 실행되는 도중에 아무때나 로딩될 수 있는 것이다. 그래서 어떤 명시적 의존이 어플리케이션에서 사용되는지 확인 하는 방법은 프로그램을 실행해보는 방법밖에 없다. (혹은 profiling) 명시적 의존은 어플리케이션이 LoadLibrary 함수와 GetProcAddress 함수를 코드 내에서 호출할 때 비로소 발생한다.


Delay-load 의존들은 사실 Explicit 의존처럼 구현되지만 헬퍼 라이브러리와 링커가 대부분의 일을 한다. 대부분의 윈도우 모듈들은 자신의 모듈안에 import table이라 불리는 호출 함수들의 리스트를 가지고 있다. 이 테이블은 링커에 의해서 작성되고 운영체제에 의해 사용되는데 주어진 모듈의 어떤 모듈들이 implicit인지 혹은 forward 의존인지를 알아내는데 사용된다. 이 리스트에 있는 모듈중에 찾을 수 없는 모듈은 당연히 어플리케이션의 실패를 초래한다. 만약 내가 링커에게 하나의 모듈을 delay-load 의존으로 연결하라고 지시하면, 그 모듈의 정보를 import table에 저장하는 대신, 그 모듈의 정보를 분리된 delay-load import table에 저장한다. 그리고 실행 시 (Run-time)에 만약 하나의 모듈이 delay-load 의존 모듈을 호출하게 되면 이 호출은 헬퍼 라이브러리에 의해 처리된다. 헬퍼 라이브러리는 LoadLibrary 함수를 이용해 모듈을 로드하고 GetProcAddress 함수를 이용해 해당 모듈로의 함수 진입점을 불러온다. 일단 이 과정이 끝나면 해당 함수를 호출한 모듈은 무슨일이 일어난건지 전혀 모르는 채 계속 하던일을 자연스럽게 하게 된다. 이러한 일련의 과정이 한바탕 치러지고 나서 나중에 호출되는 같은 함수나 혹은 같은 모듈 내의 다른 함수들은 헬퍼 라이브러리를 거치지 않고 바로 즉각적으로 해당 함수로 접근이 가능하다. 


Delay-load 헬퍼 라이브러리는 만약 실패가 있거나 오류가 있으면 유저에게 알려주는 메커니즘을 가지고 있다. Explicit 의존 (Dynamic 의존) 처럼 만약 어플리케이션이 이러한 오류나 실패에 이미 준비를 하고 있는 상황이라면 문제가 없다. 


요약하자면 ImplicitForward Dependencies 경우에는 반드시 모듈이 찾을 수 있는 위치에 존재해야 하며 또한 에러나 경고가 없어야 한다. ExplicitDelay-load Dependencies의 경우에는 모듈이 반드시 찾을 수 있는 위치에 있어야 하는 것도 아니고 또한 부모 모듈이 사용하고자하는 모든 함수를 다 Export 해야 하는 것도 아니다. 하지만, 만약 어플리케이션이 없는 Explict 모듈이나 Delay-load 모듈을 실행중에 사용하려고하면 이것은 Run-time Failure, 즉 실행중 프로그램 정지 (Crash)로 나타날 수 있다. Dependency Walker는 사용자가 소스 코드 내부에서 이러한 오류에 대한 방지책을 마련했는지 알 수 없으므로 모든 발생 가능한 오류에 대해 알려준다. 만약 어플리케이션이 아무 문제없이 잘 돌아가면 대부분의 경고 메시지는 무시해도 된다. 하지만 만약 어플리케이션이 실패하는 경우, 경고는 아마도 무엇이 실패를 가져오게 했는지에 대한 고찰을 하게 도와 줄 수 있을 것이다.


오호, Dependency Walker는 First and Second chance Exception에 대해서도 경고를 해준단다. 엑세스 바이얼레이션 (잘못된 메모리 참조) 같은 오류가 어플리케이션에서 발생하면 어플리케이션은 해당 예외를 처리할 한번의 기회를 얻는다. 이것을 흔히 First Chance Exception이라 한다. 만약 어플리케이션이 그 예외를 잘 처리하고 문제가 없으면 오케이지만 만약 어플리케이션이 이 예외를 처리하지 못하면 이것이 Second Chance Exception이 된다. 이 경우에는 주로 어플리케이션이 완전히 뽀싸진다. 크래쉬! 일반적으로 Second Chance Exception이 발생하면 운영체제는 사용자에게 다이얼로그 박스를 열어서 '야, 프로그램 엉망이라서 닫는다!'라고 알려주고 종료해야 한다고 말한다. 나에게는 종종 있는 일이다 ㅋㅋㅋ


Dependency Walker는 항상 이 Second Chance Exception을 기록한다. 물론 First Chance Exception도 기록할 수 있다. 많은 어플리케이션이 First Chance Exception을 일으키도록 작동하고 또 처리하고 한다. 고로 이것은 '완전 나쁜' 징후로 보지는 않는다. 









posted by 대갈장군
2012. 12. 14. 00:47 프로그래밍/Dependency Walker

의존성 검사 프로그램인 Dependency Walker는 다음과 같은 타입의 의존성을 검사할 수 있다.


1. Implicit Dependency (다른 말로는 load-time dependency): 굳이 직역하자면 '암묵적 의존'이라고 할 수 있겠다. 모듈 A가 컴파일 혹은 링크시에 모듈 B를 사용하기 위해 하나의 라이브러리 파일 (LIB)을 암묵적으로 내포했다면 모듈 A의 소스코드는 실질적으로 모듈 B의 함수들을 불러다 사용하는 것이다. 고로 모듈 B는 모듈 A의 '로드 타임 디펜던시'이다. 한국어로 설명하자면 모듈 B는 모듈 A를 실행하는 시점에 반드시 로딩 되어야 하는 필수적인 놈이다라는 말... 고로 모듈 B에 있는 함수를 실행중 (Run-time)에 사용하고 말건 간에 모듈 A는 반드시 모듈 B를 로딩해야 한다. 모듈 B는 모듈 A의 import table에 포함된다. 


2. Delay-load Dependency: '지연 로딩 의존'. 이 녀석은 1번과 거의 동일한데 한 가지 중요한 차이점이 있다면 모듈 A가 진짜로 모듈 B의 함수를 호출하면 그제서야 모듈 B를 로딩한다는 점! 고로 이름이 'Delay-load' (늦게 로딩)이다. 이 경우에는 모듈 A의 'Delay-load import table'에 모듈 B가 포함된다. 


3. Forward Dependency: '전진형 의존' (가수 전진 아님) ㅋㅋ 한국어로 바꾸자니 참 어이없네. 이 녀석의 경우 모듈 A가 모듈 B를 컴파일 혹은 링크시에 LIB 파일로 연결하고 모듈 A의 소스코드가 모듈 B의 함수를 호출하는 경우를 말하는데, (여기까지는 1과 2와 같다) 모듈 B의 함수가 실질적으로는 또 다른 모듈인 모듈 C의 함수를 연결해서 호출하는 경우를 말한다. 모듈 B와 모듈 C 둘 다 실질적으로 모듈 A의 필요한 라이브러리들이지만 모듈 B만 모듈 A의 import table에 이름을 올린다.


4. Explicit Dependency (혹은 다른 말로 다이나믹 의존 or 런타임 의존): '명시적 의존' 모듈 A가 모듈 B와 컴파일 혹은 링크 타임에 연결되지 않았으나 런타임에 모듈 A가 동적으로 모듈 B를 LoadLibrary() 같은 함수를 통해 소스코드 내부에서 임의로 호출하여 연결하는 경우를 말한다. 자, 이런 경우에는 모듈 B는 모듈 A의 'Run time dependency'이다. 즉, 모듈 B는 모듈 A를 실질적으로 실행하는 시점에 요구되는 것이라는 말. 하지만 모듈 A의 import table에는 모듈 B가 당연히 없다. 왜냐면 소스 코드 내부에서 임의로 사용자 맘대로 호출하니까. 이런 타입의 대표적인 것들이 OCXs, COM object 그리고 Visual Basic 어플리케이션들이다. 


5. System Hook Dependency (다른 말로 '주입된 의존'): 이 타입의 의존성은 다른 어플리케이션이 특정 이벤트를 프로세스 내에서 '후킹'할때 발생한다. 그 프로세스가 해당 이벤트를 발생시킬 때, 운영체제는 해당 이벤트를 처리하기 위해 해당 프로세스로 모듈을 'Inject' 즉, '주입'하게 된다. 이렇게 주입된 모듈을 다른 어떤 모듈에 의존적이진 않지만 주입된 프로세스의 주소 공간에 머무른다. 


1,2 그리고 3번의 경우는 쉽게 찾아낼 수 있다. 왜냐면 import table에 자신들의 이름을 당당히 밝히기 때문이다. 4번과 5번은 찾아내기 쉽지 않은데 Dependency Walker 2.0이후 버전의 Runtime Profiling을 이용하면 검색 가능하단다. 


http://www.dependencywalker.com/help/html/dependency_types.htm

posted by 대갈장군
prev 1 next