[펌]바이트 정렬에 대한 디버깅 경험

거의 2주에 걸쳐 헤매다가 잡은 버그에 대한 경험이다.

DSAPI로 도미노 필터를 제작하고 있는데, 이상하게 어떤 클래스가 delete만 되면 죽어버리는 것이었다. 물론 중간에 어떤 포인터를 가져와서 호출하는 코드도 있었는데, 계속 그런 코드들에서 죽는 것이었다. 코드 로직을 통해서도 어디 포인터를 바뀌는 코드도 없고, 처음 생성하고는 read only로 읽기만 하기 때문에 사용하는 부분에서는 문제가 없었다. 그래서 혹시나 어디선가 할당된 이상으로 써버리는 것이 아닌가 하는 의심을 가지고 문제가 생기는 클래스를 추적하기 시작했다. 두 클래스가 걸렸는데 멤버가 내부적으로는 int, string 등 검증된 코드만을 쓰고 있어서 그럴리가 없다는 생각으로 다른 곳을 찾아보았지만 그 값들을 초기화 하는 부분 또한 기존에 많이 쓰던 검증된 코드로 통해 초기화 하고 있었고, 또한 이전에서도 이 코드를 사용하였기 떄문에 이 코드가 틀리다는 건 너무 이상했다. 실제로 프로젝트가 Common 한 정적 라이브러리와 이것을 사용하는 DSAPI 필터 라이브러리로 나누어져 있었는데 Common 은 것은 다른 곳에서도 쓰고, 거기서는 이상 없었기 때문에 그 코드에 대한 의심은 안해야 되지 않을까 싶었지만, 혹시나 해서 일말의 의구심을 가지고 보고 있었다.

그래서 추적해보기로 계속 해서 1주일 정도 delete 부분 바꿔보고, Common library 에 있는 두 클래스를 조금씩 수정해보는 식으로 하였는데, 어느날 이상한 현상을 발견하였다. bool 변수가 중간에 끼어 있었는데 이걸 위치를 바꿔보니, 에러가 안 나는 것이었다. (특정 변수 뒤로 선언했다.) 기존에 계속 이상한 값을 가지던 포인터도 제대로 값이 들어가고, 정상적으로 돌아가는 것이었다. 왜 위치만 바뀌면 제대로 되는 것일까라고 생각하고 드디어 어셈블리와 메모리 레벨에서 추적해서 들어가기 시작했다. 어셈블리와 메모리를 계산해서 추적해보니 이상하게 4바이트를 덜 가서 값을 가져오는 것이었다.그래서 값이 이상하고, 죽고 그러는데... 그래서 혹시나 해서 SP5 로 업데이트 하고(그 떄는 업데이트 안되서 기존 버전을 쓰고 있었다. 왜 안되었는지 잘...) 새로 리빌드 해보기도 했지만 그 현상을 마찬가지 였다. 왜 하필이면 4바이트일까...처음에는 컴파일러가 계산을 잘못해서 코드를 생성했나 싶었지만 아무리 생각해도 그게 이상일리는 없을 것 같고 해서 이것저것 바꿔보다가...결국 바이트 정렬 떄문이 아닐까 하는 생각이 들었다. 그런데 바이트 정렬이 구조체에서는 만약 적용해봤지만 클래스에서 내부적인 동작 도중에 문제 생긴다는 건 처음 들어본지라 너무 이상하다 싶었다. 너무 이상하다 싶어 주위에 베테랑급 사수들에게 물어봤지만 첨 들어보는 현상이라고 하였다. Sun이나 AIX에서 가끔 이상하게 동작하는 경우를 알려주긴 했지만 이번 경우에는 그게 아닌듯 싶고 하였다.

그래서 Common 라이브러리에 있는 bool 변수를 BOOL 즉 int 형으로 바꾸어보았더니 모두 OK 였다. 아무래도 바이트 정렬 문제가 확실했다. 그래서 일단 그렇게 하면서 계속 근본 원인을 뒤져보다 우연히 다른 함수 떄문에 도미노 제공 헤더를 뒤져보게 된 일이 있었는데, 거기서 정말 충격적인 코드를 보았다 헤더 차원에서 #pragma pack(1)를 해놓은 것이었다. 그제서야 어떻게 돌아가는 건지 이해가 되었다. 쩝 그런 것이었구나 하고...

원인은 이렇다. Common 한 라이브러리는 VC6 기본인 8바이트 정렬로 컴파일된다. 하지만 DSAPI 필터 프로젝트는 그 헤더 때문에 1바이트 정렬로 모두 컴파일이 되게 된다. 따라서 이 쪽에서는 1바이트 기준으로 new를 해서 메모리를 구성하고 실제로 링크된 다른 라이브러리 바이너리에서는 8바이트 기준으로 정렬해서 처리되기 떄문에, 그 사이에 미묘한 차이가 생겨버린 것이다. 그래서 컴파일러는 제대로 코드를 생성했지만 두 바이너리 생성 차이로 한 EXE내에서 두 가지 바이트 정렬된 부분이 프로그래머가 알지 못하게 생겨버리는 경우였다. 그래서 다시 강제적으로 8바이트 세팅해주고 나니 아주 잘 돌더라는 그런 뼈아픈 얘기였다.

나중에 알게된 사실이지만 DSAPI 튜토리얼에 아주 조그마한 하게 1바이트 정렬을 한다는 얘기가 있긴 했다. 하지만 제대로 강조되지 않았기에 모르고 넘어가고 이런 삽질을 경험하게 된 것인데...정말 맘 아픈 경우이다 ㅠ.ㅠ 뭐 이거 덕분에 어셈블리와 메모리 주소를 통한 디버깅을 자유자재로 할 수 있게 되어버렸다

by 루키 | 2007/10/02 17:56 | 프로그래밍팁 | 트랙백 | 덧글(1)

트랙백 주소 : http://includes.egloos.com/tb/1507333
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
Commented by CraigJin at 2007/11/02 10:54
찾기 힘든 버그네요. pack에 따른 이런 오류가 발생하다니..
그런데 조금 이해하기 힘든부분이 같은 모듈의 코드네에서 bool의 순서만 바꾼걸로 이런오류가 나다니
bool 사용의 중간에 다른 pack으로 된 모듈을 호출하는 부분이 있었나봅니다?
에러도 없이 이렇게 그냥 죽어버리는 버그찾는게 제일 미칠노릇이죠.
※ 로그인 사용자만 덧글을 남길 수 있습니다.

◀ 이전 페이지 다음 페이지 ▶