레이블이 MS-DOS인 게시물을 표시합니다. 모든 게시물 표시
레이블이 MS-DOS인 게시물을 표시합니다. 모든 게시물 표시

2014년 10월 29일 수요일

Vim 파일 저장 오류 __ 9

stat() 함수는 왜 실패했을까?
Ubuntu에서 stat() 함수에 애초부터 문제가 있었던 건 아닐까?

간단하게 C program을 만들어서 이를 시험해 보기로 했다.

test.c
stat()의 대상 파일은 DOSBox에서 시험을 했던, 디버그 메시지로 출력된 그 파일을 사용했다.
출력 메시지에 자세한 정보를 넣으려 했더니 이상한 에러가 발생해서, 간단하게 성공 실패만 출력하게 만들었다.

빌드하고 시험해 보니....성공...(허무-_-)
test 결과
그렇다면....Ubuntu에서 stat() 함수는 정상적으로 작동한다고 정정해야 겠다.
그렇다면 DOSBox에서는 왜 실패했던걸까?
인자로 전달된 파일 이름에 무언가 보이지 않는 문제가 있었단 말인가?
그 문제를 확인하려면 실제 인자로 전달되는 메모리를 살펴봐야 하지 않을까 싶다.
그러기 위해서는 DOSBox를 디버깅해봐야 한다는 얘기가 되고...
DOSBox의 자체 디버거로 이것이 가능할까?

=================================================================
잠깐, 앞서 언급했던 이상한 에러에 대해서 살펴보자.


test.c에서 printf()에 변수를 출력하기 위해 포맷문자(%)를 사용하면 문제가 생기는 것이었다.
앞선 소스에서 주석처리를 했던 부분을 다음과 같이 다시 살려놓고 빌드해 보았다.
test.c
컴파일에서는 아무런 문제가 없었지만 링킹 과정에서 문제가 생긴 듯한데, 처음보는 이상한 오류를 내놓았다.
이게 ld의 문제라는 것인지 collect2의 문제라는 것인지도 모호하다.
test.c 빌드 에러
google에 오류 메시지를 그대로 붙여넣고 검색해 보니 꽤 많은 결과들이 검색되기는 하지만 딱히 이 경우에 해당되는 문서는 아직 찾지 못했다.
빌드 에러 검색
문제 하나도 해결 못하고 여기까지 왔는데, 이상한 에러까지 더해지니 머리가 복잡해지려고 한다.
일단은 본래의 문제에만 집중하기로 하고, 이 문제는 잠시 접어두기로 하자.
=================================================================
문제 해결!
아주 간단하면서도 어이 없는 실수에 의한 문제였는데,
실패했을 경우를 위해 추가했던 [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
DOS_SetFileAttr()에 breakpoint를 설정한다.
문제가 되었던 localDrive::GetFileAttr()에 설정할 수도 있겠지만 원하지 않을 경우에도 break가 걸리게 될 것이므로...
set breakpoint
continue로 DOSBox를 실행시키고, vim에서 파일을 불러온 후에 수정하고 저장을 시도한다.
그러면 설정해 두었던 breakpoint에서 멈추게 된다.
이제 문제가 되었던 stat() 지점까지 n(ext)와 s(tep)으로 진행을 한다.
next/step
stat()을 호출하기 직전에 인자로 사용되는 변수 newname의 값이 어떤지 확인해 본다.
gdb의 p(rint)로 문자열을 확인하면 모든 값들이 표시된다고 보면 될 것이다. 출력 불가능한 문자들의 경우에는 "\nnn "으로 표시해 주니까 중간에 이상한 문자가 끼었는지는 금방 알 수 있다.
확인해 보니 변수값에 이상한 문자가 없음을 확인할 수 있다.
next/step/print
자세히 보면 그냥 NEWFILE이 아니라 NEWFILE.~임을 알 수 있다.
지난 번의 DOSBox 디버그 출력에도 있었는지 모르겠지만, vim에서 파일을 저장할 때 원본 파일과 <원본.~>파일 두개를 동시에 저장하려 하는 것으로 보인다.
step
그런데 stat()에서 s(tep)으로 진입을 해 보니, 인자의 값이 newname이 아닌 name의 값으로 표시가 된다.???
계속해서 s(tep)으로 들어가 보지만 더 이상 깊이 들어갈 곳은 없어 보인다.
c(ontinue)로 이어서 진행을 하자 이번에는 원본파일인 NEWFILE로 다시 한 번 break가 걸린다.
next/step again
이전과 같이 n(ext)와 s(tep)으로 stat()까지 진행.
앞선 경우처럼 stat()에 사용된 인자가 바뀌었을까 이상해서 어셈블 코드를 확인해 보기로 했다.

"disassemble /m"으로 소스와 어셈블 코드를 표시해 본다.

일단 어셈블 코드가 좀 낯설다.
일단 대충 살펴보니, stat()을 호출하는 부분이 보이질 않는다.
단지 test %eax, %eax와 jne 0x80decc0
0x80decc0에서 stat()을 호출하는 걸까?
disassemble
0x80decc0는 stat()의 처리가 실패했을 경우에 처리되는 부분이다.
attr에 0을 넣고 디버그 메시지를 출력하는 코드로 이어진다.
그렇다면 stat()을 호출하는 곳은 어디에 있단 말인가?
앞의 어셈블 코드를 다시 살펴보니 stat()을 전후로 디스어셈블 되지 않은 번지들이 있음을 알게 되었다. 즉, 0x80dec66에서 0x80dec85로 약 0x1F(31) 바이트 정도가 보이질 않는다.
disassemble (계속)

disassemble (계속)
그래서 강제로 번지수를 지정해서 디스어셈블 출력을 해 보았다.
/m은 mixed로 소스와 함께 출력해야 하므로 출력이 제대로 되지 않고 /r은 hex data까지 표시되어 난잡해 보이니 아무 옵션 없이 출력하는 게 젤 나아 보인다.
disassemble (추가)
하지만 역시나 Linux의 어셈블러 표기에 익숙하지 않아서 혼란스럽고 이해가 잘 안된다.
Linux 어셈블러 공부마조 또 해야 하나?ㅠㅠ

2014년 10월 25일 토요일

Vim 파일 저장 오류 __ 8

디버그 메시지를 확인하던 중, 메시지의 출력이 되지 않아 확인을 하지 못하고 중단이 되었었다.
SetFileAttr()
하지만 추가적으로 확인해 본 결과, 메시지의 출력은 정상적으로 되었다.
단지, 내가 추가한 부분에 걸리지 않았던 것 뿐.
DOS_SetFileAttr()이 호출되면 중간에는 아무런 문제가 없이 마지막의 GetFileAttr()까지 오는 것이었다.

하지만 소스에서 보듯이 GetFileAttr()은 함수포인터로 호출되므로 정확한 파일 이름을 알 수가 없다.
GetFileAttr() 검색
가상함수인 GetFileAttr()은 드라이브의 종류에 따라 각기 달리 구현되어 있다.
확인해 본 결과 이번 경우에는 drive_local.cpp에 있는 함수가 호출되는 것을 알게 되었다.

그래서 GetFileAttr()에 메시지 추가.
문제가 발생하는 부분은 stat()함수가 실패했기 때문이었다.
GetFileAttr()

stat() 도움말

stat() 도움말 (계속)

stat() 도움말 (계속)

다시 빌드하고 디버그 메시지 확인...

메시지를 봐도 특별히 이상한 점은 찾을 수가 없다.

위의 stat()에 대한 도움말을 읽어 보니, 실패했을 경우에는 errno에 에러 코드를 설정한다고 한다.
그래서 메시지에 errno의 값을 함께 출력하도록 수정.
GetFileAttr()에 메시지 추가

디버그 메시지로 출력된 errno의 값은 2.
debug 출력

errno = 2는 "No such file or directory"...
파일이 없다니....
errno 값의 의미

완전히 막혀버린 상황.
stat() 함수는 기본적인 C 함수이기 때문에, 더 이상의 디버깅을 해도 소용이 없게 된다.
이 문제는 vim의 문제도 아니고 DOSBox의 문제도 아닌 Ubuntu의 문제가 되기 때문이다.

지금으로썬 Ubuntu(혹은 Linux)에서의 DOSBox는 문제가 있다는 것으로 결론을 내릴 수 밖에 없다.

2014년 10월 24일 금요일

Vim 파일 저장 오류 __ 7

예전에 만들어 두었던 debug 버전의 DOSBox를 실행해 보았다.


디버그 터미널에는 여러가지 정보가 표시된다.
DOSBox 화면에서 Alt-Pause를 누르면 디버그 모드를 진입하게 된다.

지금은 디버그 모드로 진입하지 않고 단지 디버그 터미널에 출력되는 내용만 확인해 보았다.

먼저, 기존에 존재하던 "NEWFILE"이라는 파일을 vim으로 열었을 때 디버그 터미널에는 다음과 같은 내용이 출력된다.
"vim NEWFILE"을 실행

파일을 수정하고 ":w"를 입력하여 저장을 시도했다.
역시나 저장되지 않았고, 디버그 터미널에는 다음과 같은 내용이 출력되었다.
수정 후 저장 시도
[Set File Attribute]는 not supported라는 메시지를 출력한다.
아무래도 이 부분이 의심스럽다.

그렇다면 vim이 아닌 다른 에디터는 대체 어째서 문제가 없다는 것일까?
edit를 이용해서 같은 파일을 열어 보았다.
"edit NEWFILE"을 실행

수정을 하고 저장을 해 보았다.
수정 후 저장
Attribute 따위는 아무것도 없다.
file open command의 파라미터가 0인지 1인지에 따라 load/save를 수행하는 듯 하다.
그냥 저장만 하고 끝.

DOSBox의 디버그 메시지에 대해서 자세히 확인하기 위해서는 DOSBox의 소스에서 해당 메시지를 찾아 봐야 하겠다.

DOSBox 소스 검색
"file open command"는 dos_files.cpp의 DOS_OpenFile()에서 출력하는 메시지로, 다음에 오는 숫자는 다음과 같은 의미를 가지고 있다.
0 : read
1 : write
2 : read & write

"file create attributes"는 dos_files.cpp의 DOS_CreateFile()에서 출력하는 메시지로, 다음에 오는 숫자가 attribute를 나타낸다.

"Set File Attributes"는 dos.cpp의 DOS_21Handler()에서 출력하는 메시지이다.
MS-DOS Interrupt Handler를 에뮬레이션 하는 부분인데, 해당 메시지가 출력되는 Interrupt는
AH = 0x43, AL = 0x01, Set File Attribute라고 명시되어 있는 부분이다.

MS-DOS Interrupt, Set File Attributes

그런데 해당 소스를 보면 좀 이상하다.
Interrup Handler가 구현이 안 된것도 아닌데, 아래쪽의 처리를 하기도 전에 "not supported"라는 메시지를 출력하고 있다는 것이다.

일단, MS-DOS Interrupt 21h, ah = 0x43, al = 0x01에 대한 설명을 찾아보면,
디음과 같은 상세한 설명을 볼 수 있다.
MS-DOS Interrupt 21h, ah=0x43, al=0x01

DOS_SetFileAttr()의 소스는 다음과 같다.
소스를 보니, 주석에 설명한 바와 같이 실제로는 파일의 속성을 바꾸지는 않고 파일의 속성을 읽어 올 뿐이었다.
DOS_SetFIleAttr()

그러면 과연 어떤 부분에서 문제가 되어서 에러가 발생하는 것인지 확인하기 위해, 디버그 메시지를 추가해 보기로 했다.
dos.cpp에 메시지 추가

dos_files.cpp, DOS_SetFileAttr()에 메시지 추가

이제 DOSBox를 다시 빌드해야 하는데, 예전에 어떤 설정으로 빌드했는지 모르니 처음부터 다시해야 한다.
디버그 모드로 빌드하기 위해 "./configure --enable_debug=heavy"
DOSBox 빌드, configure

그리고 make
DOSBox, make

이제 DOSBox를 실행하고 아까와 동일하게 "vim NEWFILE" 실행한 경우의 디버그 메시지들을 확인해 보자.
"vim NEWFILE"의 디버그 메시지

그리고 저장했을 때의 디버그 메시지는...
":w"로 저장했을 때의 디버그 메시지
지정한 파일의 속성은 archive(0x20)이고, 에러 코드(2)의 의미는 "file not found"라고 한다.

그런데, 더 구체적인 오류 메시지인 DOS_SetFileAttr()의 메시지는 전혀 출력이 안되었다.
무슨....?
로그 메시지 출력에 어떤 제한이 있는지 다시 확인해 봐야 하겠다.
어차피 실제 속성을 바꾸지 않는 함수인만큼, 오류의 원인을 확인해 보고 별 문제가 아니라면 그냥 성공한 것으로 리턴하게 만들어야 하지 않을까?

나머지는 다음에...

2014년 10월 21일 화요일

Vim 파일 저장 오류 __ 6

open() 함수가 실패했을 때, 어떤 에러가 발생하는지도 확인해 봐야 할 듯해서...

open()의 help
함수 실패시의 리턴값은 -1이고 조금 더 자세한 에러 원인을 알기 위해서는 errno 변수를 확인해 봐야 한다.
errno 값에 대해서 조금 더 확인해 보면,
errno의 help

help화면엔 별것이 없고, ERRNO.H를 열어 보자.
ERRNO.H

그럼, 이제 소스를 고쳐서 errno를 확인해 보기로 하자.

FILEIO.C 메시지에 errno 추가

make

디버그메시지, errno = 19

errno = 19는 EINVAL

결론은 Invalid Argument...휴..

Fileio.c의 mch_open()의 인자들을 모두 확인해 보았고, 두번째 인자들의 flag들도 바꾸어 보았으나 문제가 해결되지 않았다.
비록 에러코드는 Invalid Argument 이지만, 더 low-level에서의 인자를 뜻하는 것이 아닐런지...

인자들만 바꾸어서 문제가 해결된다면, 앞서의 mode와 마찬가지로 UNIX/Windows 차이가 발생할리 없었을 것이다.

지금까지의 시험결과로는 DOSBox 내부에서 문제를 해결하는 것이 불가능해 보인다.
DOSBox와 UNIX 사이의 interface, 특히 파일시스템과 관련된 interface의 문제로 보인다.

문제는 이런 경우의 디버깅이 매우 어렵다는 점....

Vim 파일 저장 오류 __ 5

Borland C++의 매뉴얼은 워낙에 구석기 유물인지 찾기가 곤란...
그래서 BC++ IDE의 Help를 활용하기로 했다.

Borland C++ IDE help

_chmod()의 도움말 화면
_chmod의 help

두번째 인자 func가 0이면 get, 1이면 set 기능을 하게 된다.
_chmod의 help (계속)

세번째 인자인 DOS file-attribute는 다음과 같다.
file attribute
실제 값을 확인하기 위해서 DOS.H를 확인해 보았다.
DOS.H
여기서 지난번에 추가한 디버그 메시지를 다시 확인해 보자.
추가했던 디버그 메시지
위의 메시지에서 p=32(0x20)은 mch_open()에서 세번째 인자로 사용될 perm이라는 변수에 할당된 값이며, _chmod() 함수를 이용해 얻은 값이다.
즉, 변수의 이름은 UNIX 파일 시스템의 권한(permission)을 의미하는 듯이 보이지만, MS-DOS 파일 시스템에서는 권한이라는 것이 없기에 파일의 속성만을 나타내고 있는 것이다.

다시 한번 에러가 발생한 부분의 code를 살펴보자.
FILEIO.C
위의 mch_open()이 실패한 것이 문제였고, mch_open()의 세번째 인자로 perm 변수를 사용하고 있음을 볼 수 있다.
하지만 이 소스만 보아도 문제의 소지가 있음을 알 수 있는 것이, 0666, 0777은 UNIX 파일 시스템에서만 유효한 권한(permission)이기 때문이다.
DOS의 파일 속성(읽기,쓰기,숨김,시스템,디렉토리)과 UNIX의 권한(소유자,그룹,관리자의 읽기,쓰기,실행 권리)를 서로 같은 것으로 간주해서 다루고 있다는 것이다.

그러면 정말로 이렇게 온동하여 처리한 것이 문제가 되는 것일까?
앞서 디버그 메시지에서는 perm이 0x20이었음을 확인했다.
8진수인 0777은 0x1FF, AND 연산을 한다해도 여전히 0x20이 되므로 결과적으로는 문제가 되지 않을 것으로 보인다.
그러면 뭘까?

mch_open()을 다시 확인해 보자.
mch_open()은 open()로 연결되어 있다.
mch_open() 매크로
open()의 help를 확인해 보자.
open()의 help
세번째 인자인 mode는 생략이 가능한 것으로 나와있다.
open()의 help (계속)
mode를 더 확인해 보니,
mode의 help
_chmod()의 file-attribute와는 다른 상수들이다.
실제 값은 어떨까?
sys/stat.h의 mode
이렇게 보니 _chmod()와 open()은 전혀 다른 계통의 함수들로 연계하여 사용하기에는 문제가 있어보인다.
open()은 기존의 UNIX 파일 시스템과의 호환성을 염두에 두고 MS-DOS에 포팅이 되었다고 한다면 _chmod()는 MS-DOS의 파일시스템에서만 사용할 수 있게 독자적으로 만들어진 것으로 보인다.


그렇다면, mch_open()의 세번째 인자의 값을 바꾸어주면 문제가 없을까?
하지만 정말 이 부분이 문제였다면, Windows에서 DOSBox를 구동하여 vim을 사용했을 때에도 동일한 문제가 발생해야 한다.
암튼 일단은 세번째 인자를 고쳐서 해 보기로 하자.

vim의 소스들을 살펴보니 대부분 세번째 인자로 0을 사용하고 있으며, 위의 stat.h에서도 일반적인 파일은 0을 사용한다고 되어 있기에 무조건 0을 사용하도록 수정해 보았다.
FILEIO.C
DOSBox에서 다시 make
make
그리고 기존의 파일을 열어서 수정하고 다시 저장해보니...
save
역시 예상과 같이 이것이 문제가 아니였던듯....