{
    Copyright (c) 1998-2002 by Pavel

    This unit finds the export defs from PE files

    C source code of DEWIN Windows disassembler (written by A. Milukov) was
    partially used

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

 ****************************************************************************
}
unit impdef;

{$ifndef STANDALONE}
  {$i fpcdefs.inc}
{$endif}

interface

   uses
     SysUtils;

   var
     as_name,
     ar_name : string;

    function makedef(const binname,
{$IFDEF STANDALONE}
                           textname,
{$ENDIF}
                           libname:string):longbool;


implementation

uses
  cfileutl;

{$IFDEF STANDALONE}
var
  __textname : string;
const
  kind : array[longbool] of pchar=('',' DATA');
{$ENDIF}

var
  f:file;
{$IFDEF STANDALONE}
  t:text;
  FileCreated:longbool;
{$ENDIF}
  lname:string;
  impname:string;
  TheWord:array[0..1]of char;
  PEoffset:cardinal;
  loaded:longint;

function DOSstubOK(var x:cardinal):longbool;
begin
  blockread(f,TheWord,2,loaded);
  if loaded<>2 then
   DOSstubOK:=false
  else
   begin
    DOSstubOK:=TheWord='MZ';
    seek(f,$3C);
    blockread(f,x,4,loaded);
    if(loaded<>4)or(x>filesize(f))then
     DOSstubOK:=false;
   end;
end;


function isPE(x:longint):longbool;
begin
  seek(f,x);
  blockread(f,TheWord,2,loaded);
  isPE:=(loaded=2)and(TheWord='PE');
end;


var
  cstring : array[0..127]of char;
function GetEdata(PE:cardinal):longbool;
type
  TObjInfo=packed record
   ObjName:array[0..7]of char;
   VirtSize,
   VirtAddr,
   RawSize,
   RawOffset,
   Reloc,
   LineNum:cardinal;
   RelCount,
   LineCount:word;
   flags:cardinal;
  end;
var
  i:cardinal;
  ObjOfs:cardinal;
  Obj:TObjInfo;
  APE_obj,APE_Optsize:word;
  ExportRVA:cardinal;
  delta:cardinal;
const
 IMAGE_SCN_CNT_CODE=$00000020;
 const
{$ifdef unix}
  DirSep = '/';
{$else}
  {$ifdef hasamiga}
  DirSep = '/';
  {$else}
  DirSep = '\';
  {$endif}
{$endif}
var
 path:string;
 _d:dirstr;
 _n:namestr;
 _e:extstr;
 common_created:longbool;
procedure cleardir(const s,ext:string);
 var
  ff:file;
  dir:searchrec;
  attr:word;
 begin
  findfirst(s+dirsep+ext,anyfile,dir);
  while (doserror=0) do
   begin
     assign(ff,s+dirsep+dir.name);
     GetFattr(ff,attr);
     if not((DOSError<>0)or(Attr and Directory<>0))then
      Erase(ff);
     findnext(dir);
   end;
  findclose(dir);
 end;
procedure CreateTempDir(const s:string);
 var
  attr:word;
  ff:file;
 begin
  assign(ff,s);
  GetFattr(ff,attr);
  if DosError=0 then
   begin
    cleardir(s,'*.sw');
    cleardir(s,'*.swo');
   end
 else
  begin
    {$push} {$I-}
     mkdir(s);
    {$pop}
    if ioresult<>0 then;
  end;
 end;
procedure call_as(const name:string);
 begin
  FlushOutput;
  RequotedExecuteProcess(as_name,'-o '+name+'o '+name);
 end;
procedure call_ar;
 var
  f:file;
  attr:word;
 begin
{$IFDEF STANDALONE}
  if impname='' then
   exit;
{$ENDIF}
  assign(f,impname);
  GetFAttr(f,attr);
  If DOSError=0 then
   erase(f);
  FlushOutput;
  RequotedExecuteProcess(ar_name,'rs '+impname+' '+path+dirsep+'*.swo');
  cleardir(path,'*.sw');
  cleardir(path,'*.swo');
  {$push} {$I-}
  RmDir(path);
  {$pop}
  if ioresult<>0 then;
 end;
procedure makeasm(index:cardinal;name:pchar;isData:longbool);
 type
  tt=array[1..1]of pchar;
  pt=^tt;
 const
  fn_template:array[1..24]of pchar=(
   '.section .idata$2',
   '.rva        .L4',
   '.long       0,0',
   '.rva        ',
   '.rva        .L5',
   '.section .idata$4',
   '.L4:',
   '.rva        .L6',
   '.long       0',
   '.section .idata$5',
   '.L5:',
   '.text',
   '.globl      ',
   ':',
   'jmp *.L7',
   '.balign 4,144',
   '.section .idata$5',
   '.L7:',
   '.rva        .L6',
   '.long       0',
   '.section .idata$6',
   '.L6:',
   '.short      0',
   '.ascii      "\000"'
  );
  var_template:array[1..19]of pchar=(
   '.section .idata$2',
   '.rva        .L7',
   '.long       0,0',
   '.rva        ',
   '.rva        .L8',
   '.section .idata$4',
   '.L7:',
   '.rva        .L9',
   '.long       0',
   '.section .idata$5',
   '.L8:',
   '.globl      ',
   ':',
   '.rva        .L9',
   '.long       0',
   '.section .idata$6',
   '.L9:',
   '.short      0',
   '.ascii      "\000"'
  );
  __template:array[longbool]of pointer=(@fn_template,@var_template);
  common_part:array[1..5]of pchar=(
   '.balign 2,0',
   '.section .idata$7',
   '.globl      ',
   ':',
   '.ascii      "\000"'
  );
  posit:array[longbool,1..4]of longint=((4,13,14,24),(4,12,13,19));
 var
  template:array[longbool]of pt absolute __template;
  f:text;
  s:string;
  i:longint;
  n:string;
  common_name,asmout:string;
  __d:dirstr;
  __n:namestr;
  __x:extstr;
 begin
  if not common_created then
   begin
    common_name:='_$'+_n+'@common';
    asmout:=path+dirsep+'0.sw';
    assign(f,asmout);
    rewrite(f);
    for i:=1 to 5 do
     begin
      s:=StrPas(Common_part[i]);
      case i of
       3:
        s:=s+common_name;
       4:
        s:=common_name+s;
       5:
        begin
         fsplit(lname,__d,__n,__x);
         insert(__n+__x,s,9);
        end;
      end;
      writeln(f,s);
     end;
    close(f);
    call_as(asmout);
    common_created:=true;
   end;
  n:=strpas(name);
  str(succ(index):0,s);
  asmout:=path+dirsep+s+'.sw';
  assign(f,asmout);
  rewrite(f);
  for i:=1 to posit[isData,4]do
   begin
    s:=StrPas(template[isData]^[i]);
    if i=posit[isData,1]then
     s:=s+common_name
    else if i=posit[isData,2]then
     s:=s+n
    else if i=posit[isData,3]then
     s:=n+s
    else if i=posit[isData,4]then
     insert(n,s,9);
    writeln(f,s);
   end;
  close(f);
  call_as(asmout);
 end;
procedure ProcessEdata;
  type
   a8=array[0..7]of char;
  function GetSectionName(rva:cardinal;var Flags:cardinal):a8;
   var
    i:cardinal;
    LocObjOfs:cardinal;
    LocObj:TObjInfo;
   begin
    GetSectionName:='';
    Flags:=0;
    LocObjOfs:=APE_OptSize+PEoffset+24;
    for i:=1 to APE_obj do
     begin
      seek(f,LocObjOfs);
      blockread(f,LocObj,sizeof(LocObj));
      if(rva>=LocObj.VirtAddr)and(rva<=LocObj.VirtAddr+LocObj.RawSize)then
       begin
        GetSectionName:=a8(LocObj.ObjName);
        Flags:=LocObj.flags;
       end;
     end;
   end;
  var
   j,Fl:cardinal;
   ulongval,procEntry:cardinal;
   Ordinal:word;
   isData:longbool;
   ExpDir:packed record
    flag,
    stamp:cardinal;
    Major,
    Minor:word;
    Name,
    Base,
    NumFuncs,
    NumNames,
    AddrFuncs,
    AddrNames,
    AddrOrds:cardinal;
   end;
  begin
   with Obj do
    begin
     seek(f,RawOffset+delta);
     blockread(f,ExpDir,sizeof(ExpDir));
     fsplit(impname,_d,_n,_e);
     path:=_d+_n+'.ils';
{$IFDEF STANDALONE}
     if impname<>'' then
{$ENDIF}
     CreateTempDir(path);
     Common_created:=false;
     for j:=0 to pred(ExpDir.NumNames)do
      begin
       seek(f,RawOffset-VirtAddr+ExpDir.AddrOrds+j*2);
       blockread(f,Ordinal,2);
       seek(f,RawOffset-VirtAddr+ExpDir.AddrFuncs+Cardinal(Ordinal*4));
       blockread(f,ProcEntry,4);
       seek(f,RawOffset-VirtAddr+ExpDir.AddrNames+j*4);
       blockread(f,ulongval,4);
       seek(f,RawOffset-VirtAddr+ulongval);
       blockread(f,cstring,sizeof(cstring));
{$IFDEF STANDALONE}
       if not FileCreated then
        begin
         FileCreated:=true;
         if(__textname<>'')or(impname='')then
          begin
           rewrite(t);
           writeln(t,'EXPORTS');
          end;
        end;
{$ENDIF}
       isData:=GetSectionName(procentry,Fl)='';
       if not isData then
        isData:=Fl and IMAGE_SCN_CNT_CODE<>IMAGE_SCN_CNT_CODE;
{$IFDEF STANDALONE}
       if(__textname<>'')or(impname='')then
        writeln(t,cstring,' @',succ(ordinal):0,' ',kind[isData]);
       if impname<>''then
{$ENDIF}
       makeasm(j,cstring,isData);
      end;
     call_ar;
   end;
  end;

begin
  GetEdata:=false;
{$IFDEF STANDALONE}
  FileCreated:=false;
{$ENDIF}
  seek(f,PE+120);
  blockread(f,ExportRVA,4);
  seek(f,PE+6);
  blockread(f,APE_Obj,2);
  seek(f,PE+20);
  blockread(f,APE_OptSize,2);
  ObjOfs:=APE_OptSize+PEoffset+24;
  for i:=1 to APE_obj do
   begin
    seek(f,ObjOfs);
    blockread(f,Obj,sizeof(Obj));
    inc(ObjOfs,sizeof(Obj));
    with Obj do
     if(VirtAddr<=ExportRva)and(ExportRva<VirtAddr+VirtSize)then
      begin
       delta:=ExportRva-VirtAddr;
       ProcessEdata;
       GetEdata:=true;
      end;
   end;
end;


function makedef(const binname,
{$IFDEF STANDALONE}
                       textname,
{$ENDIF}
                       libname:string):longbool;
var
  OldFileMode:longint;
begin
  assign(f,binname);
{$IFDEF STANDALONE}
  FileCreated:=false;
  assign(t,textname);
  __textname:=textname;
{$ENDIF}
  impname:=libname;
  lname:=binname;
  OldFileMode:=filemode;
  {$push} {$I-}
   filemode:=0;
   reset(f,1);
   filemode:=OldFileMode;
  {$pop}
  if IOResult<>0 then
   begin
     makedef:=false;
     exit;
   end;
  if not DOSstubOK(PEoffset)then
   makedef:=false
  else if not IsPE(PEoffset)then
   makedef:=false
  else
   makedef:=GetEdata(PEoffset);
  close(f);
{$IFDEF STANDALONE}
  if FileCreated then
   if(textname<>'')or(impname='')then
    close(t);
{$ENDIF}
end;

end.
