magiblot / tvision

A modern port of Turbo Vision 2.0, the classical framework for text-based user interfaces. Now cross-platform and with Unicode support.
Other
2.05k stars 152 forks source link

Exception in DOS #22

Open sduensin opened 4 years ago

sduensin commented 4 years ago

Problem user here again. :-) This is the demo program from the TV C++ manual. Pressing F4 should open a window that displays a source file. Instead it throws an exception.

image

Pressing F4 should open a dialog but I get another exception:

image

I am able to pull down menus but that's it.

(To be sure it's not DOSBox, I also tried it in VirtualBox in an NT 4.0 command prompt.)

magiblot commented 4 years ago

Well, this is where it stops being funny. These are not C++ exceptions, but memory safety issues. DPMI32 executables are also unstable for me. I don't know if this is a memory issue in Turbo Vision that can be easily fixed, or if it's just a limitation of the DPMI32 model.

I'm not even sure this can be debugged with td32. Does DOSBox-X have a debugger? I wonder if it can help.

I also tried it in VirtualBox in an NT 4.0 command prompt.

Then it should be possible to debug with td32. Build Turbo Vision with -DDEBUG -DTVDEBUG; build your application with -v and link against TV32D.LIB. Run td32 in NT 4.0, and open your application from there. Then reproduce the issue and see if you can get any useful information.

This is the demo program from the TV C++ manual.

Did you copy it manually from the manual? The C++ manual was written for Turbo Vision 1.0, so the program could contain a bug. If you are familiar with Linux development and valgrind, you can try it there first.

My point is: as long as the program logic does not depend on DOS, it can be tested on a platform that's easier to debug for. I said Linux, but this is valid for Visual Studio as well.

sduensin commented 4 years ago

Building my code against the Borland-supplied TV works. If nothing else, I can fall back to that for DOS.

The code I'm using is basically what is in the TV C++ manual but sanity checked against the tutorial code provided by this port: https://github.com/set-soft/tvision/tree/master/tvision/examples/tutorial

I'll try to get a debugger going after I get some more TV under my belt. Right now I only know enough to be dangerous.

Maxwelldoug commented 4 years ago

Hey Sduensin. as far as I can tell, this port is closer to original then set-soft's (based on the readme anyways) I'll tell you the same on discord as well.

magiblot commented 4 years ago

Okay, I can reproduce. It seems that you can work around the issue by changing project/makefile as follows:

 !if $d(DOS32)
 # This is done in several variables to work around the 'Command arguments too 
-EXCLUDE2 = TVEXPOSD.CPP TVWRITE.ASM
+EXCLUDE2 = TVEXPOSD.CPP TVWRITE.CPP
 EXCLUDE1 = EDITS.ASM FRAMELIN.ASM TVCURSOR.ASM TGRMV.ASM TTPRVLNS.ASM
 !else

as far as I can tell, this port is closer to original then set-soft's (based on the readme anyways)

Yes, this is right. This port is backward-compatible, and most code that compiles for the original also compiles for this one.

magiblot commented 4 years ago

I recompiled Turbo Vision, and I can no longer reproduce. This is a serious issue, because I haven't changed a single line of code (not even the makefile suggestion above).

tvguid16.zip

magiblot commented 4 years ago

If you can reproduce the issue with a debug build, please share the binary.

mooskagh commented 4 years ago

Probably useless, but here are disassembles of the instructions with illegal memory access (typed from screenshots, disassembled at https://defuse.ca/online-x86-assembler.htm)

At least it's surely not the code in tvwrite.asm (no test (e)ax,(e)ax or clc there).

Raw Hex (zero bytes in bold):

45F88945C88B45C88810837B2E000F84   

String Literal:

"\x45\xF8\x89\x45\xC8\x8B\x45\xC8\x88\x10\x83\x7B\x2E\x00\x0F\x84"

Array Literal:

{ 0x45, 0xF8, 0x89, 0x45, 0xC8, 0x8B, 0x45, 0xC8, 0x88, 0x10, 0x83, 0x7B, 0x2E, 0x00, 0x0F, 0x84 }
Disassembly:

0:  45                      inc    ebp
1:  f8                      clc
2:  89 45 c8                mov    DWORD PTR [ebp-0x38],eax
5:  8b 45 c8                mov    eax,DWORD PTR [ebp-0x38]
8:  88 10                   mov    BYTE PTR [eax],dl         <--------- THIS
a:  83 7b 2e 00             cmp    DWORD PTR [ebx+0x2e],0x0
e:  0f                      .byte 0xf
f:  84                      .byte 0x84
Raw Hex (zero bytes in bold):

578B450885C0747F8B50FCF6C2017477   

String Literal:

"\x57\x8B\x45\x08\x85\xC0\x74\x7F\x8B\x50\xFC\xF6\xC2\x01\x74\x77"

Array Literal:

{ 0x57, 0x8B, 0x45, 0x08, 0x85, 0xC0, 0x74, 0x7F, 0x8B, 0x50, 0xFC, 0xF6, 0xC2, 0x01, 0x74, 0x77 }
Disassembly:

0:  57                      push   edi
1:  8b 45 08                mov    eax,DWORD PTR [ebp+0x8]
4:  85 c0                   test   eax,eax
6:  74 7f                   je     0x87
8:  8b 50 fc                mov    edx,DWORD PTR [eax-0x4]    <------- THIS
b:  f6 c2 01                test   dl,0x1
e:  74 77                   je     0x87
mooskagh commented 4 years ago

But it indeed does look like it overruns some buffer, and it's just a matter of luck whether memory allocator already allocated adjacent pages from OS (in that case it goes unnoticed) or not (in which case memory protection triggers).

magiblot commented 4 years ago

Hi @mooskagh, thanks for investigating. I was able to get a trace the first time I reproduced the issue, and it was something like this:

(most recent first)
memcpy
[...] // No debug symbols
TView::writeLine // implemented in tvwrite.cpp
[...] // No debug symbols

This made me suspect of tvwrite.cpp, which is the C++ translation of tvwrite.asm. Indeed, you can't see these instructions in tvwrite.asm because the default build uses the C++ version and my suggestion was to switch to the ASM one. But the issue could be really anywhere else.

But I absolutely wasn't expecting the issue to disappear after rebuilding. I cannot reproduce it anymore. So if anyone else can reproduce with a debug build, please share the binaries.