>>>... also, does anyone know of a program that will simply identify
>>>those programs in need of fixing?
>Actually, I meant searching the .exe files. Sorry I didn't make
that
>clear.
- The .EXE file starts with a header of the form
tExeHdr = Record
Sig, Extra, Pages, nReloc, HdrSize, MinMem,
MaxMem, InitSS,
InitSP, Checksum, InitIP, InitCS, RelocTbl,
OverlayNum
: Word;
End;
- The program load image starts HdrSize*16 bytes into the
program.
- The program start code is at InitCS*16+InitIP bytes after the
start of the load image. (I think TurboPascal will always
have InitCS=0 but I'm not sure.)
- The program starts with a sequence of instructions like
9A0000CE00 CALL 00CE:0000
9A0D006400 CALL 0064:000D
which initialise the units that have initialisation sections.
(These segment values are from the .EXE file, after loading
into memory DOS will patch them to their loaded values.)
That
can be represented by records like
tInitUnit = Record
Inst : Byte; { $9A }
InitOffset : Word;
InitSeg : Word;
End;
- The offset value for the initialisation section of the CRT
unit does not change. A look at the BP7 RTL shows that the
CRT unit only has one Pascal routine coming before the
initialisation section in the CRT code segment, and it is
used by CRT.ASM so the linker cannot strip it out. So we
want
to look for units with offset $D. (BP 7.01) If a tInitUnit
is read with Inst<>$9A before one with InitOffset=$D
is found
then the .EXE does not use CRT and is OK.
- When the tInitUnit for the CRT unit is found, we need to find
its segment. The segment values read from the .EXE file
are
converted into offsets in the file by
FileOffset := (Hdr.HdrSize+InitUnit.InitSeg)*16;
- The offending code is like this, from $87 bytes into the CRT
segment:
0087 E83C02
CALL 02C6
008A F7D0
NOT AX
008C F7D2
NOT DX
008E B93700
MOV CX,0037
0091 F7F1
DIV CX
(the RTE is always at offset $91)
There are no segment fixups in this. So, if the bytes
E8 3C 02 F7 D0 F7 D2 B9 37 00 F7 F1
come at FileOffset+$87, your .EXE is faulty and needs to
be
rebuilt. Otherwise either the CRT unit is patched, or you
have another unit with the same offset. Either way this
unit does not have this bug (it may have others!) and you
continue the search with the next tInitUnit.
Take it step by step. There are a few steps but if you have a
known bad .EXE you can check your work at each step by
comparing what your program is reading with what you see if
you look at the .EXE file with TDUMP, TD (or even DEBUG) and
so on. This routine might be useful too:
Function Hex(Value : LongInt; NDigits : Byte) : String;
Const
HexDigits : Array[0..15] Of Char =
'0123456789ABCDEF';
Var
s : String;
Begin
s := '';
Repeat
s := HexDigits[Value and $F] + s;
Value := Value shr 4;
until (Value=0) and (length(s)>=nDigits);
Hex := s;
end;
...if you want your program to dump intermediate results.
FP
The program below will examine a .EXE file to see if it contains
the CRT
unit from TP7 or something very similar. A description of how it
works is
to be found on Franz Glaser's webpage
http://geo.meg-glaser.at/tp_pat.html in a message
that I
posted long ago and then forgot about until I saw it there. It
is for
real-mode programs only, I would be surprised if it gave meaningful
results
with protected mode .exes, should report errors reading the .exe
or the
like.
I know many of you could easily have written something like this,
or
better, but if you did you haven't posted it here. Comments are
invited.
Now the question is: if a bad .exe is detected, what would be the
best fix?
Remember, we're talking about fixing compiled .EXEs here, for which
source
is unavailable. My first attempt was to NOP out the div instruction,
which
is OK for my programs because I don't use DELAY. But hardly a general
solution.
Any suggestions?
FP
------------------------- cut here -------------------------
{ Program to examine an exe file to see if it has an unmodified
TP7
CRT unit, which suffers from the well-known Run-Time Error
200
during startup
}
Type
{ real-mode exe files always start with a header which starts
with
a block of this form
}
tExeHdr = Record
Sig, Extra, Pages, nReloc, HdrSize, MinMem,
MaxMem, InitSS,
InitSP, Checksum, InitIP, InitCS, RelocTbl,
OverlayNum
: Word;
End;
{ Real-mode TP7 programs start with a sequence of unit initialisation
calls that fit this template
}
tInitUnit = Record
Opcode: Byte;
Ofs : Word;
Sgm : Word;
End;
Const
CallOp = $9A; { opcode at start of call to unit initialiser
}
CrtOfs = $D; { Offset of initialisation of CRT unit
}
SigOfs = $8E; { address of identifying sequence }
nSig = 5;
SigBytes : Array[0..nSig-1] of Byte = ($B9,$37,$00,$F7,$F1);
{ identifier
}
NopOfs = $91; { offset of code to NOP out }
NopLgt = 2; { no. of bytes to wipe }
Procedure Abort(msg:String);
Begin
Writeln('RTE200 - examine .EXE for CRT bug');
Writeln('Syntax: RTE200 exename');
Writeln;
Writeln(msg);
Writeln('Program Halted');
Halt(1);
End;
Var
exe : File;
ExeHdr : tExeHdr;
nRead : Word;
CodeOffset : LongInt;
InitUnit : tInitUnit;
CrtBuf : Array[0..nSig-1] of Byte;
SavePos : LongInt;
IsCrt : Boolean;
i : Integer;
Begin
Assign(exe, ParamStr(1));
{$I-}
Reset(exe,1);
{$I+}
If IoResult<>0 then
Abort('Can''t open file '+ParamStr(1));
BlockRead(exe, ExeHdr, sizeof(ExeHdr), nRead);
If (nRead<>sizeof(ExeHdr)) or (ExeHdr.Sig<>$5A4D)
then
Abort('Can''t read .EXE header - may not be
valid .EXE file');
CodeOffset := LongInt(ExeHdr.HdrSize)*16 { start of load
image }
+LongInt(ExeHdr.InitCS)*16+ExeHdr.InitIP;
{$I-}
Seek(exe, CodeOffset);
{$I+}
If IoResult<>0 then
Abort('Can''t read start of program - may not
be a valid program');
IsCrt := False;
Repeat
BlockRead(exe, InitUnit, SizeOf(InitUnit), nRead);
If nRead<>SizeOf(InitUnit) then
Abort('Error reading program - file
may be truncated');
If InitUnit.Opcode<>CallOp then
break;
{ Check that this is really the CRT unit
(some other unit could start at
this offset)
}
SavePos := FilePos(exe);
CodeOffset := LongInt(ExeHdr.HdrSize)*16 { start
of load image }
+LongInt(InitUnit.Sgm)*16+SigOfs;
{$I-}
Seek(exe, CodeOffset);
{$I+}
If IoResult<>0 then
Abort('Error reading program - may
not be a valid program');
BlockRead(exe, CrtBuf, SizeOf(CrtBuf), nRead);
{ Abort if nRead is wrong? nah - could still
be valid. But not CRT. }
If nRead=SizeOf(CrtBuf) then
begin
IsCrt := True;
For i := 0 to nSig-1 do
IsCrt := IsCrt and (CrtBuf[i]
= SigBytes[i]);
If IsCrt then
break;
End;
Seek(exe, SavePos); { no error: we've been here
before }
until False;
Close(exe);
If IsCrt then
Writeln(ParamStr(1),' has code matching the
faulty CRT unit')
else
Writeln('Faulty CRT unit not found in ',ParamStr(1));
End.
back to TP-patch page -
Turbo Pascal Links http://geo.meg-glaser.at/tp.html