2014년 10월 17일 금요일

Vim 파일 저장 오류 __ 3

먼저 Vim의 소스를 직접 빌드하여 디버깅 해 보기로 결정.

vim의 소스는 다음 링크에서...
ftp://ftp.vim.org/pub/vim/pc

MS-DOS의 경우에는 vim 7.1이 최종 소스이므로 vim71src.zip을 다운로드 하면 된다.

소스를 풀어서 src 디렉토리를 살펴보면 INSTALLpc.txt라는 파일에 소스 빌드에 관한 안내가 되어 있다.

그 가운데 MS-DOS에 관한 부분.
INSTALLpc.txt
네모친 부분을 읽어 보면 Borland C++을 사용하는게 제일 속편할 듯...

그리고 부가적으로 spawno라는 라이브러리가 필요하다고 한다.
위에서는 simtel에서 다운로드 하라고 하는데, 이걸 만든 저자 Ralf Brown의 사이트를 본 적이 있어서 그곳에서 다운로드 받았다.

http://www.cs.cmu.edu/~ralf/files.html (예전에 MS-DOS 인터럽트를 정리해 둔 곳이라고 했던 URL. 이 페이지를 살펴보면 spawno 라이브러리를 찾을 수 있다.)
http://www.cs.cmu.edu/~ralf/pub-files/spwno413.zip (spawno 라이브러리)

project라는 서브 디렉토리 아래에 모두 풀어 놓았다.
디렉토리 모습
vim과 spawno 트리

소스를 빌드하기 위한 작업은,
1. 당연한 사항이지만 Borland C++을 설치해서 path 지정해 두기
2. src/Make_bc3.mak를 Makefile로 복사(또는 이름 바꾸기)
3. Makefile 수정하기
Makefile 수정
4. make

빌드는 성공적으로 완료.
디버깅을 위해 터보디버거를 이용해 구동.
하지만 ...메모리 부족 에러 발생ㅠㅠ
터보디버거 에러

고민하며 둘러보다 Makefile을 추가적으로 수정해야 함을 알게 됨.
주석에 씌어진 대로 80386 코드로 생성하도록 "-1-"을 "-3"으로, 소스 디버깅을 위해서 "-v" 옵션을 추가.
Makefile 추가 수정

하지만 결과는 마찬가지...

이제 해 볼 수 있는 방법은,
td386.exe라는 386용 터보디버거를 사용하는 방법과
소스에 메시지를 넣어서 메시지로 디버깅하는 방법.
전자는 터보디버거에 대한 매뉴얼을 좀 읽어 봐야 할 수 있을 것 같으니 후자를 먼저 해 보는 것이 좋을 듯...

Vim 파일 저장 오류 __ 2

문제의 에러코드를 이용해 소스 확인 fileio.c

오류 메시지의 해당 소스

mch_open()이라는 함수의 실패로 인한 오류 메시지였음.

mch_open()은 open() 함수

open()의 man page

open()의 man page, mode와 관련된 부분.

DosBox에서 Vim으로 새로운 파일을 만들어 본다.

내용을 입력하고 저장

정상적으로 저장이 되었음

Ubuntu의 file system에서 확인해 보면 파일의 권한이 644임을 알 수 있음

Vim에서는 수정이 안되지만, edit라는 다른 프로그램으로 수정을 함

정상적으로 수정이 되었음을 알 수 있다

어째서 vim 계열의 프로그램들만 파일의 수정에 문제가 생기는 것일까?
(vim외에도 elvis라는 프로그램도 문제가 발생한다.)
어째서 다른 에디터로는 문제가 없는 것일까?

이 문제의 원인을 어떻게 찾아낼 것인가?
MS-DOS 상에서 소스를 빌드하고 디버깅을 해야 할까?
DOSBox를 디버그 모드로 실행하고 MS-DOS Interrupt를 break point로 해서 확인을 해야 할까?

이 문제의 원인을 찾아내서 문제의 원인이 DOSBox인 경우, vim인 경우에 각각 어떻게 할 것인가?
아니다 이 문제는 DOSBox 뿐 아니라 Virtual Box의 경우에도 발생하므로 vim의 문제로 보는 것이 맞을 것이다.
단지, vim에서 FAT을 기준으로 작동하는 코드, 그리고 Linux상의 MS-DOS 에뮬레이터들이 FAT을 다루는 부분이 서로 충돌해서 빚어진 것이 아닐까 싶다.

2014년 10월 15일 수요일

Vim 파일 저장 오류 __ 1

[개요]
Ubuntu 10.04 운영체제에서 MS-DOS용 Vim을 사용하면 파일의 저장에 문제가 발생 함.

[사용 환경]
Ubuntu 10.04
DosBox 0.74 (혹은 Oracle Virtual Box에 MS-DOS를 설치한 경우)
Vim 7.1 for MS-DOS 16 bit version

[문제 현상1]
1. MS-DOS 에뮬레이터를 구동한다.
2. 이 에뮬레이터 상에 Vim 7.1 16 bit version이 설치되어 있고, path에 지정되어 있도록 한다.
3. 임의의 텍스트 파일이 존재하는 곳으로 이동하여, "vim <파일이름>"을 입력한다.
4. 파일을 수정한 후에, command mode로 전환하고 ':w'를 입력하여 저장을 해 본다.
!!! 파일이 저장되지 않고 다음과 같은 오류 문자열을 출력한다.
"<파일이름> E212: Can't open file for writing"


[문제 현상2]

1. MS-DOS 에뮬레이터를 구동한다.
2. 이 에뮬레이터 상에 Vim 7.1 16 bit version이 설치되어 있고, path에 지정되어 있도록 한다.
3. 임의의 디렉토리에서, "vim <새 파일 이름>"을 입력한다.
4. 내용을 입력한 후에, command mode로 전환하고 ':w'를 입력하여 저장을 해 본다.
5. 파일이 저장되고 다음과 같은 메시지가 출력된다.
"<새 파일 이름> [New] xxL, yyC written" (xx는 줄의 수, yy는 문자 수)
6. 이제 새로운 내용을 추가한 후에, command mode로 전환하고 ':w'를 입력하여 저장을 해 본다.
!!! 파일이 저장되지 않고 다음과 같은 오류 문자열을 출력한다.
"<파일이름> E212: Can't open file for writing"


2013년 12월 22일 일요일

Appler 디버깅하기 ___ 버그 발생

Appler 디버깅하기
라는 제목으로 게시했던 1번부터 6번까지의 게시물을 무효화 하겠음.

무효화 이유는,
수정된 Appler의 작동에 문제가 발생했기 때문.
Apple 게임인 2400AD를 실행하면, 초기 로고화면이 지나고 게임 화면의 프레임만 그려진 상태에서 Appler가 Halt 됨.

정상적으로 시작된 게임 화면

수정된 Appler는 이 화면에서 멈추어 버림

7번 게시물에서 제시한 방법대로 DOSBox를 수정한 버전에서는, 원래의 Appler로 문제없이 진행이 됨을 확인하였음.
하지만 1~6 게시물의 방법으로 수정된 Appler는 어떤 DOSBox에서도 문제가 있었기에, 수정된 Appler의 문제임은 확실함.


추정 원인은,
마지막 6번 게시물에서 만든 수정코드가 아니라,
1번과 2번 게시물에서 소스를 빌드하는 과정에 컴파일 오류를 수정하는 과정에서 발생한 것으로 생각 됨.
왜냐하면, 6번게시물에서 수정한 부분은 단지 Disk Manager 화면에만 영향을 줄 뿐 임.


확인 해 본 사항들
  • 1번과 2번 게시물에서 수정했던 부분에 대한 이해가 부족했기에, 이 부분을 제대로 고치기 위해서 어떻게 작동하는지 확인해 보아야겠다고 생각했음.
  • 이를 위해서, 수정한 부분이 실행파일에 어떤 식으로 컴파일되어 들어가 있는지 확인해야 하며, 원래의 실행파일에는 어떻게 컴파일되어 들어가 있는지 확인을 해야 했음.
  • 일단 수정한 실행 파일과 원래의 실행 파일을 비교하니 전자는 .EXE 파일이었고, 후자는 .COM 파일이었음.
  • 더욱이 시작 코드는 더욱 달라서 전자는 xor al, al이었고 후자는 mov ax, 4xxxH 였음.
  • 아무래도 바로 비교는 힘들었기에 EXE를 COM으로 바꿔보기 위해서 EXE2BIN을 사용해 보았음.
  • FreeDOS용 EXE2BIN은 모두 실패했음
  • MS-DOS 6.22용 EXE2BIN은 DOSBox에서 버전 불일치로 실행이 안 됨. DOSBox의 MS-DOS 버전은 5.0으로 되어 있음.
  • MS-DOS 5.0용 EXE2BIN은 Insufficient Memory라는 에러를 내며 실패했음.
  • Sourcer를 사용해 보았지만, 앞서와 비슷하게 (xor al,al, mov ax,...)차이가 나기에 모든 소스를 다 이해하기 전에는 원하는 부분이 어떻게 컴파일되어 들어가 있는지 찾아내기는 거의 불가능해 보임.
  • 심지어는 과연 Appler의 소스가 실행파일의 소스가 맞는지 의심되기도 함.
  • Appler의 소스는 Zophar's Domain에서만 받을 수 있었고, Appler의 공식 사이는 없기에 신뢰성이 떨어짐.
  • Appler의 소스에 있는 문서로는 터보어셈블러 2.x와 4.x로 빌드 되는 것을 확인했다고 했는데, 나는 터보 어셈블러 5.0으로 빌드하였던 것이기에 터보 어셈블러 2.0을 구해서 빌드해 보았으나 더 많은 에러가 발생함. 이 때문에 소스에 대한 신뢰성이 더 떨어지게 되었음.

이상의 실험 결과, 주어진 소스를 활용한다는 것 자체에 의문이 들어,
DOSBox를 수정하는 방법인 7번 게시물만을 유효한 것으로 인정하고
1번 ~ 6번 게시물은 무효화 하기로 결정했음.

2013년 12월 19일 목요일

DOSBox의 커맨드 쉘(Command Shell) 버그 ___ 2

먼저 간단하게 정리한 GDB의 명령어들.




여기에 정리한 명령어 외에,
(gdb) until <line>
: <line>까지 실행
(gdb) return
: 현재 함수 벗어나기

이 정도의 명령어만 알고 있어도 디버깅이 가능했다.
(이번 디버깅에선 register, memory dump, call stack에 대한 접근이 불필요했기에...)
단, GDB에서의 source listing 기능은 아무래도 많이 부족하므로 별도의 소스 뷰어와 사용하면 좋을 듯 하다.


이제, 본격적으로 디버깅!

DOSBox를 타깃으로 gdb를 실행하고 바로 run.

DOSBox가 시작된 후에, 문제를 확인하기 위해 D drive로 바꾸어 둔다.

CTRL-C를 눌러서 잠시 중단시킨다.
break로 DOS_Shell::Execute에 브레이크포인트를 설정하고, c로 다시 실행을 재개한다.

문제의 "nc"를 입력한다.

앞선 포스팅에서 문제가 되는 부분의 함수 호출 순서를 정리했다.
다시 보면,

shell/shell.cpp 286 DOS_Shell::Run()
shell/shell.cpp 295 temp.ParseLine()
shell/shell.cpp 199 DOS_Shell::ParseLine()
shell/shell.cpp 251 DoCommand()
shell/shell_cmds.cpp 118 DOS_Shell::DoCommand()
shell/shell_cmd.cpp 153 if(Execute())
shell/shell_misc.cpp 358 DOS_Shell::Execute()
shell/shell_misc.cpp 387 Which()
shell/shell_misc.cpp 511 DOS_Shell::Which()
shell/shell_misc.cpp 572 if(DOS_FileExists())...

이 가운데 DOS_Shell::Execute()에 브레이크포인트를 설정해 둔 상태였고,
"nc"를 입력하여 이 브레이크포인트에서 멈추기를 기대하였다.


DOS_Shell::Execute에서 멈추었고, 이후의 소스를 확인한다.

중간과정은 관심밖이므로, 원하는 387 line Which()까지 실행하도록 u 387을 입력한다.

Which() 내부의 처리를 보기 위해 s(tep)을 입력하고 Which의 처리과정을 확인한다.
현재 위치(D:\)에서 확장자(.com, .exe, .bat)를 붙여가며 파일이 존재하는지 확인한다.
당연히 없으므로 Which() 함수에서의 처리가 계속된다.

이제는 PATH를 하나씩 순서대로 뒤져가며 파일을 찾는다.
중간에 확인하고 싶은 변수는 p(rint) 명령을 사용한다.

PATH 환경변수에서 하나의 경로만을 떼어내는 과정이었다.

첫번째 PATH인 Z:\에 대해서 입력된 nc를 붙여 다시 파일을 찾는다.

다음인 C:\FREEDOS도 실패하고,
그 다음인 C:\에 대해서는 "u 571"이 이전과는 달리 571 line에서 멈추지 않았다.

C:\NC가 아닌 C:\에서 "nc"라는 파일을 찾았다고 판단하게 된 것이 문제의 원인이었다.

Which() 함수는 대체로 다음의 순서대로 파일을 찾는다.

1) 현재 위치에서 "nc"
2) 현재 위치에서 "nc.COM"
3) 현재 위치에서 "nc.EXE"
4) 현재 위치에서 "nc.BAT"
5) PATH에 있는 각 경로에 대해서 위의 1) ~ 4)를 반복 검색

문제가 된 것은 "C:\" 경로에서 "nc"를 찾는 것이 성공했기 때문이다.
어째서 일까?
DOS_FileExists()는 다음과 같은 순서로 진행이 된다.

dos/dos_files.cpp 1176 DOS_FileExists()
dos/dos_files.cpp 1179 return Drives[drive]->FileExists(fullname);
dos/drive_local.cpp 374 localDrive::FileExists()
dos/drive_local.cpp 380 FILE* Temp=fopen(newname,"rb");

최종적으로 불리는 fopen() 함수의 인자로 주어진 경로는 파일이 아닌 폴더였지만, 폴더에 대해서도 fopen()이 성공한 것으로 처리했던 것이다.

nc와 pe2만이 문제가 되었던 것은, 서브디렉토리의 이름과 실행파일의 이름이 동일했기 때문이다.

자, 이제 문제의 원인이 밝혀졌으니 이를 해결할 방안은 세워야겠다.
1) Path와 실행파일의 이름이 같지 않도록 만든다.
2) 가장 depth가 큰 path가 앞에 오도록 path의 순서를 바꾼다. ( C:\;C:\NC -> C:\NC;C:\ )
3) Source 수정 : File인지 디렉토리인지 구분하여, 파일인 경우에만 존재여부를 확인 할 수 있도록 한다.
4) Source 수정 : File의 존재 여부를 확인할 때, 확장자가 없는 경우에는 확장자를 먼저 붙인 다음에 존재여부를 확인하도록 한다.

1)과 2)의 방법은 실제로 효과가 있음을 확인하였다.
하지만 3)과 4)의 방법은 시도도 하지 못했다.

디버깅을 하면서 느낀 점은, Which() 혹은 ParseLine()이라는 부분의 처리가 매우 "땜질"식으로 짜여져 있는 느낌을 받았기 때문이다.
비효율적인 부분(PATH를 매번 분리하는 작업), 중복처리(확장자를 붙여서 찾는 부분이 중복되어 "nc.COM.COM"을 찾기도 한다.) 등의 문제가 있는 것으로 미루어보아, 여러 문제가 발생해서 수정이 여러번 이루어졌으며 그나마도 한사람이 아닌 여러 사람이 고친 것으로 보였기 때문이다.
만약 내가 또 하나의 수정을 가하면, 점점 걸레화 되어갈 것이 뻔하다.
그리고 내가 모른 문제가 다시 튀어나올지도...

따라서 1)과 2)의 방법으로 문제를 해결하길 권한다.


마지막으로 이 문제가 어째서 M$-Window$에서는 발생하지 않는지 확인해 보았다.

직접 디버깅해 본 결과 M$-Window$에서는 fopen()의 인자로 폴더가 주어졌을 때, 실패한 것으로 처리가 됨을 확인했다.

2013년 12월 15일 일요일

DOSBox의 커맨드 쉘(Command Shell) 버그 ___ 1

DOSBox 0.74

Ubuntu Linux 10.04

Norton Commander를 PATH에 설정하였으나, 종종 nc.exe를 찾아서 실행하지 못하는 문제점 발생.

DOSBox의 내장 명령어인 imgmount를 실행 한 후,
Turbo Assembler로 프로젝트를 빌드한 후에 증상 발생.

프롬프트에서 "nc"를 입력하면
"Illegal command: nc." 이라는 오류를 내뱉는다.

환경변수인 PATH는 아무런 문제없이 정상으로 보인다.
프롬프트에서 "set"을 입력하면
"
COMSPEC=Z:\COMMAND.COM
BLASTER=A220 I7 D1 H5 T6
PATH=Z:\;C:\FREEDOS;C:\;C:\NC;C:\PE2;C:\TASM5\BIN
"

위의 PATH에서 문제가 되는 부분은 C:\NC와 C:\PE2 두 군데 뿐이다.
"nc"와 "pe2"를 입력하면 실행되지 않고 있는 것이다.

다음의 화면을 보면 문제점의 상황을 이해하기 쉬우리라 생각한다.

Autoexec.bat까지 실행된 초기 화면

D:\에서는 nc와 pe2 모두 실행되지 않는다.


왜 그럴까?

여러가지 실험 결과 "nc.exe", "pe2.exe"를 입력하면 문제없이 실행이 된다.

또 한가지, C:\에서는 정상적으로 실행이 된다.

조금 더 실험을 해 본 결과, 어떠한 작업도 하지 않았어도 이 문제는 발생하고 있으며,
C:\를 제외한 다른 곳에서는 항상 문제가 발생하는 것으로 보인다.

C:\에서 nc를 실행하면,

Norton Commander가 정상적으로 실행된다.

C:\에서 pe2를 실행하면,

Personal Editor2가 정상적으로 실행된다.



디버깅을 하기 위해 DOSBox의 소스를 검토해 보았다.

일단은 src/shell/shell_cmds.cpp의 DOS_Shell::DoCommand()가 가장 유력해 보인다.
이 함수의 호출 경로를 역추적 해 보니,

src/shell/shell.cpp의 DOS_Shell::ParseLine()

src/shell/shell.cpp의 DOS_Shell::Run()

함수의 호출 순서대로 경로를 정래하면 다음과 같다.


shell/shell.cpp 286 DOS_Shell::Run()
shell/shell.cpp 295 temp.ParseLine()
shell/shell.cpp 199 DOS_Shell::ParseLine()
shell/shell.cpp 251 DoCommand()
shell/shell_cmds.cpp 118 DOS_Shell::DoCommand()
shell/shell_cmd.cpp 153 if(Execute())
shell/shell_misc.cpp 358 DOS_Shell::Execute()
shell/shell_misc.cpp 387 Which()
shell/shell_misc.cpp 511 DOS_Shell::Which()
shell/shell_misc.cpp 572 if(DOS_FileExists())...


Ubuntu에서 사용할 수 있는 디버거는 GDB가 대표적이지만,
실행과 동시에 깝깝해지는 "(gdb) " 프롬프트만 껌벅이는 화면을 보게 된다.
메뉴 방식이 아닌 명령어 입력 방식인 GDB의 사용을 편리하게 하기 위해
DDD(Data Display Debugger)라는 GDB의 Frontend 프로그램이 있기도 하지만,
실제 사용해 본 결과, 메모리 사용량이나 느린 응답 속도 때문에 사용이 꺼려진다.

이번 기회에 GDB의 명령어를 배워서 직접 GDB로 디버깅을 해 봐야겠다.

2013년 11월 23일 토요일

Appler 디버깅하기 ___ 7

이미 수정까지 완료한 사항이지만, 지난 번과는 달리 이번에는 DOSBox를 수정해서 문제를 해결해 보았습니다.

지난 번에 수정을 했던 Ubuntu system의 HDD가 고장이 나서, 다른 HDD에 새로 시스템을 꾸미다 보니 결국은 같은 문제에 봉착하게 되었습니다.
그래서 이번에는 다른 방식으로 문제를 해결해 보고자 하는 생각에...


우선은 문제가 되는 그 화면
문제가 되는 화면, Path가 이상하고 Directory도 이상합니다.

이 문제는 DOS Interrupt 21h, AH=60h일 때의 오작동으로 인한 문제였음은 이미 앞선 포스팅에서 밝힌 바 입니다.

해당 부분을 처리하는 루틴은 dos.cpp의 DOS_21Handler()에 있습니다.
DOS_21Handler()

AH=60h (Canonicalize)가 해당하는 루틴입니다.

디버거를 이용해 trace를 해 본 결과 DOS_canonicalize()라는 함수를 수정하면 될 것으로 보였습니다.

DOS_files.cpp의 DOS_Canonicalize()


마지막에 Backslash만 붙여주면 되는 문제였기에, 문자열의 마지막에 Backslash가 있는지 없는지 검사해서 없을 때에만 붙여 주면 된다 생각했습니다.

그러나 이 루틴에 입력되는 문자열이 디렉토가 아닌 파일인 경우도 있기에, 무조건 Backslash를 넣어 주어서는 안됩니다.

몇번의 디버깅과 고민 끝에 입력 문자열의 마지막에 Backslash가 있다면 디렉토리이므로 이 경우에만 마지막에 Backslash를 검사하고 붙여주도록 수정하였습니다.

다음이 수정한 결과입니다.

위와 같이 수정하였습니다.

수정하고 빌드하여 확인한 결과 화면입니다.

이젠 정상 작동합니다.