home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / BONUS40.ZIP / EXEUTIL.ZIP / EXEINFO.PAS next >
Encoding:
Pascal/Delphi Source File  |  1988-01-27  |  9.9 KB  |  332 lines

  1. {$R-,S-,I-}
  2.  
  3. program ExeInfo;
  4.   {-Write information about a Turbo Pascal 4.0 EXE file}
  5.   {-Offer quick patches to heap and stack size}
  6.  
  7. type
  8.   ExeHeaderRec =             {Information describing EXE file}
  9.   record
  10.     Signature : Word;        {EXE file signature}
  11.     LengthRem : Word;        {Number of bytes in last page of EXE image}
  12.     LengthPages : Word;      {Number of 512 byte pages in EXE image}
  13.     NumReloc : Word;         {Number of relocation items}
  14.     HeaderSize : Word;       {Number of paragraphs in EXE header}
  15.     MinHeap, MaxHeap : Word; {Paragraphs to keep beyond end of image}
  16.     StackSeg, StackPtr : Word; {Initial SS:SP, StackSeg relative to image base}
  17.     CheckSum : Word;         {EXE file check sum, not used}
  18.     IpInit, CodeSeg : Word;  {Initial CS:IP, CodeSeg relative to image base}
  19.     RelocOfs : Word;         {Bytes into EXE for first relocation item}
  20.     OverlayNum : Word;       {Overlay number, not used here}
  21.   end;
  22.  
  23.   RelocRec =
  24.   record
  25.     Offset : Word;
  26.     Segment : Word;
  27.   end;
  28.  
  29. var
  30.   Patch : Boolean;
  31.   ShowFixups : Boolean;
  32.   ExeName : string[64];
  33.  
  34. const
  35.   Digits : array[0..$F] of Char = '0123456789ABCDEF';
  36.  
  37.   function HexW(W : Word) : string;
  38.     {-Return hex string for word}
  39.   begin
  40.     HexW[0] := #4;
  41.     HexW[1] := Digits[hi(W) shr 4];
  42.     HexW[2] := Digits[hi(W) and $F];
  43.     HexW[3] := Digits[lo(W) shr 4];
  44.     HexW[4] := Digits[lo(W) and $F];
  45.   end;
  46.  
  47.   function StUpcase(S : string) : string;
  48.     {-Return uppercase of string}
  49.   var
  50.     I : integer;
  51.   begin
  52.     for I := 1 to length(S) do
  53.       S[I] := upcase(S[I]);
  54.     StUpcase := S;
  55.   end;
  56.  
  57.   function HasExtension(Name : string; var DotPos : Word) : Boolean;
  58.     {-Return whether and position of extension separator dot in a pathname}
  59.   var
  60.     I : Word;
  61.   begin
  62.     DotPos := 0;
  63.     for I := Length(Name) downto 1 do
  64.       if (Name[I] = '.') and (DotPos = 0) then
  65.         DotPos := I;
  66.     HasExtension := (DotPos > 0) and (Pos('\', Copy(Name, Succ(DotPos), 64)) = 0);
  67.   end;
  68.  
  69.   function ForceExtension(Name, Ext : string) : string;
  70.     {-Return a pathname with the specified extension attached}
  71.   var
  72.     DotPos : Word;
  73.   begin
  74.     if HasExtension(Name, DotPos) then
  75.       ForceExtension := Copy(Name, 1, DotPos)+Ext
  76.     else
  77.       ForceExtension := Name+'.'+Ext;
  78.   end;
  79.  
  80.   procedure Error(Msg : string);
  81.     {-Report error and halt}
  82.   begin
  83.     if Msg <> '' then
  84.       WriteLn(^M^J, Msg);
  85.     Halt(1);
  86.   end;
  87.  
  88.   procedure WriteHelp;
  89.     {-Show a brief help screen}
  90.   begin
  91.     WriteLn;
  92.     WriteLn('Usage: EXEINFO [Options] ExeName');
  93.     WriteLn('Options:');
  94.     WriteLn('  /P    Prompt for new stack and heap sizes');
  95.     WriteLn('  /F    Show a detailed list of relocation fixups');
  96.     Halt(1);
  97.   end;
  98.  
  99.   procedure ParseCommandLine;
  100.     {-Analyze the command line from DOS}
  101.   var
  102.     I : Integer;
  103.     Arg : string;
  104.   begin
  105.     Patch := False;
  106.     ShowFixups := False;
  107.     ExeName := '';
  108.     I := 1;
  109.     while I <= ParamCount do begin
  110.       Arg := stupcase(ParamStr(I));
  111.       if (Arg = '/P') or (Arg = '-P') then
  112.         Patch := True
  113.       else if (Arg = '/F') or (Arg = '-F') then
  114.         ShowFixups := True
  115.       else if Length(ExeName) = 0 then
  116.         ExeName := ForceExtension(Arg, 'EXE')
  117.       else
  118.         Error('Invalid command line');
  119.       Inc(I);
  120.     end;
  121.     if Length(ExeName) = 0 then
  122.       WriteHelp;
  123.   end;
  124.  
  125.   function PtrDiff(HiPt, LoPt : Pointer) : LongInt;
  126.     {-Return the number of bytes between point HiPt^ and point LoPt^}
  127.   var
  128.     HiVal, LoVal : LongInt;
  129.   begin
  130.     HiVal := LongInt(Seg(HiPt^)) shl 4+LongInt(Ofs(HiPt^));
  131.     LoVal := LongInt(Seg(LoPt^)) shl 4+LongInt(Ofs(LoPt^));
  132.     PtrDiff := HiVal-LoVal;
  133.   end;
  134.  
  135.   function BlkRead(var F : file; var Buffer; Size : Word) : Boolean;
  136.     {-Convenient shell around BlockRead}
  137.   var
  138.     BytesRead : Word;
  139.   begin
  140.     BlockRead(F, Buffer, Size, BytesRead);
  141.     BlkRead := (IoResult = 0) and (BytesRead = Size);
  142.   end;
  143.  
  144.   function BlkWrite(var F : file; var Buffer; Size : Word) : Boolean;
  145.     {-Convenient shell around BlockWrite}
  146.   var
  147.     BytesWritten : Word;
  148.   begin
  149.     BlockWrite(F, Buffer, Size, BytesWritten);
  150.     BlkWrite := (IoResult = 0) and (BytesWritten = Size);
  151.   end;
  152.  
  153.   function GetDataSeg(var ExeF : file; ExeHeader : ExeHeaderRec) : Word;
  154.     {-Return the data segment of a Turbo EXE file}
  155.   type
  156.     FirstCallRec =
  157.     record
  158.       CallInstr : Byte;
  159.       Offset : Word;
  160.       Segment : Word;
  161.     end;
  162.     SetupDsRec =
  163.     record
  164.       MovInstr : Byte;
  165.       Segment : Word;
  166.     end;
  167.   var
  168.     Fcall : FirstCallRec;
  169.     SetupDs : SetupDsRec;
  170.     BaseCodeSeg : LongInt;
  171.     BytesRead : Word;
  172.   begin
  173.     Reset(ExeF, 1);
  174.  
  175.     with ExeHeader do begin
  176.       BaseCodeSeg := (LongInt(HeaderSize)+LongInt(CodeSeg)) shl 4;
  177.       Seek(ExeF, BaseCodeSeg+IpInit);
  178.       if IoResult <> 0 then
  179.         Error('Error during file seek');
  180.     end;
  181.     if not BlkRead(ExeF, Fcall, SizeOf(FirstCallRec)) then
  182.       Error('Error reading EXE file');
  183.  
  184.     {Interpret the first far call to the SYSTEM library initialization block}
  185.     with Fcall do begin
  186.       if CallInstr <> $9A then
  187.         Error('Not a Turbo Pascal 4.0 EXE file');
  188.       Seek(ExeF, BaseCodeSeg+(LongInt(Segment) shl 4)+LongInt(Offset));
  189.       if IoResult <> 0 then
  190.         Error('Error during file seek');
  191.     end;
  192.     if not BlkRead(ExeF, SetupDs, SizeOf(SetupDsRec)) then
  193.       Error('Error reading EXE file');
  194.  
  195.     {Interpret a MOV DX,dataseg instruction}
  196.     with SetupDs do begin
  197.       if MovInstr <> $BA then
  198.         Error('Not a Turbo Pascal 4.0 EXE file');
  199.       GetDataSeg := Segment;
  200.     end;
  201.   end;
  202.  
  203.   function ReadLongInt(Msg : string; default, min, max : LongInt) : LongInt;
  204.     {-Prompt for and get a long integer value}
  205.   var
  206.     s : string;
  207.     value : LongInt;
  208.     code : Word;
  209.   begin
  210.     repeat
  211.       Write(Msg, ' [', default, '] ');
  212.       ReadLn(s);
  213.       if s = '' then begin
  214.         ReadLongInt := default;
  215.         Exit;
  216.       end;
  217.       Val(s, value, code);
  218.       if code <> 0 then
  219.         WriteLn('Invalid integer')
  220.       else if (value < min) or (value > max) then
  221.         WriteLn('Value must be in range ', min, ' to ', max)
  222.       else begin
  223.         ReadLongInt := value;
  224.         Exit;
  225.       end;
  226.     until False;
  227.   end;
  228.  
  229.   procedure DumpExeHeader(ExeName : string);
  230.     {-Dump the EXE file header and relocation records}
  231.   var
  232.     ExeF : file;
  233.     ExeHeader : ExeHeaderRec;
  234.     BytesRead, I, LastSeg, ItemCount, DataSeg,
  235.     InitDataParas, UninitDataParas, StackAndStatic : Word;
  236.     ExeSize : LongInt;
  237.     MnHeap : LongInt;
  238.     MxHeap : LongInt;
  239.     L : LongInt;
  240.     Rel : RelocRec;
  241.   begin
  242.  
  243.     Assign(ExeF, ExeName);
  244.     Reset(ExeF, 1);
  245.     if IoResult <> 0 then
  246.       Error(ExeName+' not found');
  247.  
  248.     if not BlkRead(ExeF, ExeHeader, SizeOf(ExeHeaderRec)) then
  249.       Error('Error reading EXE file');
  250.  
  251.     with ExeHeader do begin
  252.  
  253.       if Signature <> $5A4D then
  254.         Error('File is not in EXE format');
  255.  
  256.       if LengthRem = 0 then
  257.         ExeSize := LongInt(LengthPages) shl 9
  258.       else
  259.         ExeSize := (LongInt(Pred(LengthPages)) shl 9)+LongInt(LengthRem);
  260.  
  261.       DataSeg := GetDataSeg(ExeF, ExeHeader);
  262.       InitDataParas := (ExeSize shr 4)-HeaderSize-DataSeg;
  263.       UninitDataParas := StackSeg-DataSeg-InitDataParas;
  264.       StackAndStatic := (StackPtr shr 4)+UninitDataParas;
  265.       MnHeap := LongInt(MinHeap-StackAndStatic) shl 4;
  266.       MxHeap := LongInt(MaxHeap-StackAndStatic) shl 4;
  267.  
  268.       WriteLn;
  269.       WriteLn('Code size:        ', PtrDiff(Ptr(DataSeg, 0), Ptr(CodeSeg, 0)), ' bytes');
  270.       WriteLn('Init data:        ', LongInt(InitDataParas) shl 4, ' bytes');
  271.       WriteLn('Uninit data:      ', LongInt(UninitDataParas) shl 4, ' bytes');
  272.       WriteLn('Stack:            ', StackPtr, ' bytes');
  273.       WriteLn('Min heap:         ', MnHeap, ' bytes');
  274.       WriteLn('Max heap:         ', MxHeap, ' bytes');
  275.       WriteLn;
  276.       WriteLn('EXE file size:    ', ExeSize, ' bytes');
  277.       WriteLn('Size of header:   ', LongInt(HeaderSize) shl 4, ' bytes');
  278.       WriteLn('Number of fixups: ', NumReloc);
  279.       WriteLn('Code start:       ', HexW(CodeSeg), ':', HexW(IpInit));
  280.       WriteLn('Data segment:     ', HexW(DataSeg), ':', HexW(0));
  281.       WriteLn('Initial stack:    ', HexW(StackSeg), ':', HexW(StackPtr));
  282.  
  283.       if Patch then begin
  284.         WriteLn;
  285.         StackPtr := ReadLongInt('Enter stack size in bytes', StackPtr, 0, 65500);
  286.         L := ReadLongInt('Enter minimum heap size in bytes', MnHeap, 0, 1048576);
  287.         StackAndStatic := (StackPtr shr 4)+UninitDataParas;
  288.         MinHeap := StackAndStatic+(L shr 4);
  289.         L := ReadLongInt('Enter maximum heap size in bytes', MxHeap, MnHeap, 1048576);
  290.         MaxHeap := StackAndStatic+(L shr 4);
  291.         Reset(ExeF, 1);
  292.         if not BlkWrite(ExeF, ExeHeader, SizeOf(ExeHeaderRec)) then
  293.           Error('Error writing EXE file');
  294.       end else if ShowFixups then begin
  295.         {Provide a detailed dump of segment fixups}
  296.         WriteLn;
  297.         {        123456789012345678901234567890}
  298.         {        ssss     nnnn   }
  299.         WriteLn('Segment  Fixups');
  300.  
  301.         Seek(ExeF, RelocOfs);
  302.         if IoResult <> 0 then
  303.           Error('Error during file seek');
  304.  
  305.         LastSeg := $FFFF;
  306.         ItemCount := 0;
  307.  
  308.         for I := 1 to NumReloc do begin
  309.           if not BlkRead(ExeF, Rel, SizeOf(RelocRec)) then
  310.             Error('Error reading EXE file');
  311.           with Rel do begin
  312.             if Segment <> LastSeg then begin
  313.               if ItemCount <> 0 then
  314.                 WriteLn('     ', ItemCount);
  315.               Write(HexW(Segment));
  316.               LastSeg := Segment;
  317.               ItemCount := 0;
  318.             end;
  319.             Inc(ItemCount);
  320.           end;
  321.         end;
  322.         WriteLn('     ', ItemCount);
  323.       end;
  324.     end;
  325.     Close(ExeF);
  326.   end;
  327.  
  328. begin
  329.   ParseCommandLine;
  330.   DumpExeHeader(ExeName);
  331. end.
  332.