Subject: Re: Fast pentium/Borland Pascal 7.0/crt bug question
         Date: Fri, 10 Jul 1998 11:34:22 +0100
         From: "Frank Peelo" <fpeelo@indigo.ie>
 Organization: EUnet Ireland customer
   Newsgroups: comp.lang.pascal.borland


Taylors Surveyors wrote in message

>>>... 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.



I don't have such a program but if you need to write one it
shouldn't be difficult. The following info might be of use:

- 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



     Subject: Testing .EXEs for RTE200
        Date: 25 Oct 1998 21:44:44 GMT
       From: "Frank Peelo" <fpeelo@indigo.ie>
 Organization:Indigo
 Newsgroups: comp.lang.pascal.borland

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