Ubuntu에서 stat() 함수에 애초부터 문제가 있었던 건 아닐까?
간단하게 C program을 만들어서 이를 시험해 보기로 했다.
test.c |
출력 메시지에 자세한 정보를 넣으려 했더니 이상한 에러가 발생해서, 간단하게 성공 실패만 출력하게 만들었다.
빌드하고 시험해 보니....성공...(허무-_-)
test 결과 |
그렇다면 DOSBox에서는 왜 실패했던걸까?
인자로 전달된 파일 이름에 무언가 보이지 않는 문제가 있었단 말인가?
그 문제를 확인하려면 실제 인자로 전달되는 메모리를 살펴봐야 하지 않을까 싶다.
그러기 위해서는 DOSBox를 디버깅해봐야 한다는 얘기가 되고...
DOSBox의 자체 디버거로 이것이 가능할까?
=================================================================
잠깐, 앞서 언급했던 이상한 에러에 대해서 살펴보자.
test.c에서 printf()에 변수를 출력하기 위해 포맷문자(%)를 사용하면 문제가 생기는 것이었다.
앞선 소스에서 주석처리를 했던 부분을 다음과 같이 다시 살려놓고 빌드해 보았다.
test.c |
이게 ld의 문제라는 것인지 collect2의 문제라는 것인지도 모호하다.
test.c 빌드 에러 |
빌드 에러 검색 |
일단은 본래의 문제에만 집중하기로 하고, 이 문제는 잠시 접어두기로 하자.
=================================================================
문제 해결!
아주 간단하면서도 어이 없는 실수에 의한 문제였는데,
실패했을 경우를 위해 추가했던 [extern int errno;]와 관련이 있는 문제였다.
외부에 있다고 지정한 errno를 찾지 못해서 에러가 난 것.
이 문제를 해결하려면 [#include <errno.h>
창피한 실수이긴 하지만, 이런 경우의 에러 메시지를 내지 못하고 세그멘테이션 폴트가 나온다는 것도 이상하지 않냐고 반문해 보고 싶다.
=================================================================
이제 본류로 다시 돌아와서 디버깅에 대해 생각을 해보자.
하지만 오래 생각할 것도 없이, 이 디버깅은 DOSBox의 내장 디버거로는 불가능한 것이다.
DOSBox의 내장 디버거가 다룰 수 있는 범위는 DOSBox 위에서 돌아가는 프로그램들에 국한이 된 것이고, 이 문제는 DOSBox와 Ubuntu에 걸쳐 있는 문제이므로, 아얘 다른 도메인(?, 세상, 차원)에 속하는 것들인 셈이다.
세상 어떤 프로그램이 자기 자신을 완전히 디버깅 할 수 있단 말인가?
세상 어딴 프로그램이 자신의 기반이 되는 모(母)프로그램을 완전히 디버깅 할 수 있단 말인가?
재미있지 않은가?
따지고 보면 인간도 자기 자신을 완벽히 비판하고 파악하는 것은 불가능하다는 말이 되지 않을까?
간단히 말하면, 그래서 gdb를 써야 한다는 것이다.
지금 빌드해 놓은 DOSBox가 내장 디버거를 활성화 시킨 것이라 gdb에서 DOSBox를 바로 호출하게 되면 gdb와 DOSBox의 내장 디버거가 뒤엉킬것이므로, 먼저 DOSBox를 실행시킨 후에 gdb를 attach하기로 했다.
ps로 알아낸 프로세스 번호는 2400, --pid 옵션으로 gdb에 attach 시킨다.
gdb attach |
문제가 되었던 localDrive::GetFileAttr()에 설정할 수도 있겠지만 원하지 않을 경우에도 break가 걸리게 될 것이므로...
set breakpoint |
그러면 설정해 두었던 breakpoint에서 멈추게 된다.
이제 문제가 되었던 stat() 지점까지 n(ext)와 s(tep)으로 진행을 한다.
next/step |
gdb의 p(rint)로 문자열을 확인하면 모든 값들이 표시된다고 보면 될 것이다. 출력 불가능한 문자들의 경우에는 "\nnn "으로 표시해 주니까 중간에 이상한 문자가 끼었는지는 금방 알 수 있다.
확인해 보니 변수값에 이상한 문자가 없음을 확인할 수 있다.
next/step/print |
지난 번의 DOSBox 디버그 출력에도 있었는지 모르겠지만, vim에서 파일을 저장할 때 원본 파일과 <원본.~>파일 두개를 동시에 저장하려 하는 것으로 보인다.
step |
계속해서 s(tep)으로 들어가 보지만 더 이상 깊이 들어갈 곳은 없어 보인다.
c(ontinue)로 이어서 진행을 하자 이번에는 원본파일인 NEWFILE로 다시 한 번 break가 걸린다.
next/step again |
앞선 경우처럼 stat()에 사용된 인자가 바뀌었을까 이상해서 어셈블 코드를 확인해 보기로 했다.
"disassemble /m"으로 소스와 어셈블 코드를 표시해 본다.
일단 어셈블 코드가 좀 낯설다.
일단 대충 살펴보니, stat()을 호출하는 부분이 보이질 않는다.
단지 test %eax, %eax와 jne 0x80decc0
0x80decc0에서 stat()을 호출하는 걸까?
disassemble |
attr에 0을 넣고 디버그 메시지를 출력하는 코드로 이어진다.
그렇다면 stat()을 호출하는 곳은 어디에 있단 말인가?
앞의 어셈블 코드를 다시 살펴보니 stat()을 전후로 디스어셈블 되지 않은 번지들이 있음을 알게 되었다. 즉, 0x80dec66에서 0x80dec85로 약 0x1F(31) 바이트 정도가 보이질 않는다.
disassemble (계속) |
disassemble (계속) |
/m은 mixed로 소스와 함께 출력해야 하므로 출력이 제대로 되지 않고 /r은 hex data까지 표시되어 난잡해 보이니 아무 옵션 없이 출력하는 게 젤 나아 보인다.
disassemble (추가) |
Linux 어셈블러 공부마조 또 해야 하나?ㅠㅠ
댓글 없음:
댓글 쓰기