fpc/compiler/link.pas
2003-12-11 17:53:03 +00:00

727 lines
20 KiB
ObjectPascal

{
$Id$
Copyright (c) 1998-2002 by Peter Vreman
This unit handles the linker and binder calls for programs and
libraries
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 link;
{$i fpcdefs.inc}
interface
uses
cclasses,
systems,
fmodule;
Type
TLinkerInfo=record
ExeCmd,
DllCmd : array[1..3] of string[100];
ResName : string[12];
ScriptName : string[12];
ExtraOptions : string;
DynamicLinker : string[100];
end;
TLinker = class(TAbstractLinker)
public
ObjectFiles,
SharedLibFiles,
StaticLibFiles : TStringList;
Constructor Create;virtual;
Destructor Destroy;override;
procedure AddModuleFiles(hp:tmodule);
Procedure AddObject(const S,unitpath : String;isunit:boolean);
Procedure AddStaticLibrary(const S : String);
Procedure AddSharedLibrary(S : String);
Procedure AddStaticCLibrary(const S : String);
Procedure AddSharedCLibrary(S : String);
Function MakeExecutable:boolean;virtual;
Function MakeSharedLibrary:boolean;virtual;
Function MakeStaticLibrary:boolean;virtual;
end;
TExternalLinker = class(TLinker)
public
Info : TLinkerInfo;
Constructor Create;override;
Destructor Destroy;override;
Function FindUtil(const s:string):String;
Function DoExec(const command,para:string;showinfo,useshell:boolean):boolean;
procedure SetDefaultInfo;virtual;
Function MakeStaticLibrary:boolean;override;
end;
TInternalLinker = class(TLinker)
private
procedure readobj(const fn:string);
public
Constructor Create;override;
Destructor Destroy;override;
Function MakeExecutable:boolean;override;
end;
var
Linker : TLinker;
function FindObjectFile(s : string;const unitpath:string;isunit:boolean) : string;
function FindLibraryFile(s:string;const prefix,ext:string;var foundfile : string) : boolean;
procedure InitLinker;
procedure DoneLinker;
Implementation
uses
{$ifdef Delphi}
dmisc,
{$else Delphi}
dos,
{$endif Delphi}
cutils,globtype,
script,globals,verbose,ppu,
aasmbase,aasmtai,aasmcpu,
ogbase,ogmap;
type
TLinkerClass = class of Tlinker;
{*****************************************************************************
Helpers
*****************************************************************************}
{ searches an object file }
function FindObjectFile(s:string;const unitpath:string;isunit:boolean) : string;
var
found : boolean;
foundfile : string;
begin
findobjectfile:='';
if s='' then
exit;
{ when it does not belong to the unit then check if
the specified file exists without searching any paths }
if not isunit then
begin
if FileExists(FixFileName(s)) then
begin
foundfile:=ScriptFixFileName(s);
found:=true;
end;
end;
if pos('.',s)=0 then
s:=s+target_info.objext;
{ find object file
1. specified unit path (if specified)
2. cwd
3. unit search path
4. local object path
5. global object path
6. exepath (not when linking on target) }
found:=false;
if unitpath<>'' then
found:=FindFile(s,unitpath,foundfile);
if (not found) then
found:=FindFile(s,'.'+source_info.DirSep,foundfile);
if (not found) then
found:=UnitSearchPath.FindFile(s,foundfile);
if (not found) then
found:=current_module.localobjectsearchpath.FindFile(s,foundfile);
if (not found) then
found:=objectsearchpath.FindFile(s,foundfile);
if not(cs_link_on_target in aktglobalswitches) and (not found) then
found:=FindFile(s,exepath,foundfile);
if not(cs_link_extern in aktglobalswitches) and (not found) then
Message1(exec_w_objfile_not_found,s);
findobjectfile:=ScriptFixFileName(foundfile);
end;
{ searches an library file }
function FindLibraryFile(s:string;const prefix,ext:string;var foundfile : string) : boolean;
var
found : boolean;
paths : string;
begin
findlibraryfile:=false;
foundfile:=s;
if s='' then
exit;
{ split path from filename }
paths:=SplitPath(s);
s:=SplitFileName(s);
{ add prefix 'lib' }
if (prefix<>'') and (Copy(s,1,length(prefix))<>prefix) then
s:=prefix+s;
{ add extension }
if (ext<>'') and (Copy(s,length(s)-length(ext)+1,length(ext))<>ext) then
s:=s+ext;
{ readd the split path }
s:=paths+s;
if FileExists(s) then
begin
foundfile:=ScriptFixFileName(s);
FindLibraryFile:=true;
exit;
end;
{ find libary
1. cwd
2. local libary dir
3. global libary dir
4. exe path of the compiler (not when linking on target) }
found:=FindFile(s,'.'+source_info.DirSep,foundfile);
if (not found) and (current_module.outputpath^<>'') then
found:=FindFile(s,current_module.outputpath^,foundfile);
if (not found) then
found:=current_module.locallibrarysearchpath.FindFile(s,foundfile);
if (not found) then
found:=librarysearchpath.FindFile(s,foundfile);
if not(cs_link_on_target in aktglobalswitches) and (not found) then
found:=FindFile(s,exepath,foundfile);
foundfile:=ScriptFixFileName(foundfile);
findlibraryfile:=found;
end;
{*****************************************************************************
TLINKER
*****************************************************************************}
Constructor TLinker.Create;
begin
Inherited Create;
ObjectFiles:=TStringList.Create_no_double;
SharedLibFiles:=TStringList.Create_no_double;
StaticLibFiles:=TStringList.Create_no_double;
end;
Destructor TLinker.Destroy;
begin
ObjectFiles.Free;
SharedLibFiles.Free;
StaticLibFiles.Free;
end;
procedure TLinker.AddModuleFiles(hp:tmodule);
var
mask : longint;
begin
with hp do
begin
{ link unit files }
if (flags and uf_no_link)=0 then
begin
{ create mask which unit files need linking }
mask:=link_allways;
{ static linking ? }
if (cs_link_static in aktglobalswitches) then
begin
if (flags and uf_static_linked)=0 then
begin
{ if smart not avail then try static linking }
if (flags and uf_smart_linked)<>0 then
begin
Message1(exec_t_unit_not_static_linkable_switch_to_smart,modulename^);
mask:=mask or link_smart;
end
else
Message1(exec_e_unit_not_smart_or_static_linkable,modulename^);
end
else
mask:=mask or link_static;
end;
{ smart linking ? }
if (cs_link_smart in aktglobalswitches) then
begin
if (flags and uf_smart_linked)=0 then
begin
{ if smart not avail then try static linking }
if (flags and uf_static_linked)<>0 then
begin
Message1(exec_t_unit_not_smart_linkable_switch_to_static,modulename^);
mask:=mask or link_static;
end
else
Message1(exec_e_unit_not_smart_or_static_linkable,modulename^);
end
else
mask:=mask or link_smart;
end;
{ shared linking }
if (cs_link_shared in aktglobalswitches) then
begin
if (flags and uf_shared_linked)=0 then
begin
{ if shared not avail then try static linking }
if (flags and uf_static_linked)<>0 then
begin
Message1(exec_t_unit_not_shared_linkable_switch_to_static,modulename^);
mask:=mask or link_static;
end
else
Message1(exec_e_unit_not_shared_or_static_linkable,modulename^);
end
else
mask:=mask or link_shared;
end;
{ unit files }
while not linkunitofiles.empty do
begin
AddObject(linkunitofiles.getusemask(mask),path^,true);
end;
while not linkunitstaticlibs.empty do
AddStaticLibrary(linkunitstaticlibs.getusemask(mask));
while not linkunitsharedlibs.empty do
AddSharedLibrary(linkunitsharedlibs.getusemask(mask));
end;
{ Other needed .o and libs, specified using $L,$LINKLIB,external }
mask:=link_allways;
while not linkotherofiles.empty do
AddObject(linkotherofiles.Getusemask(mask),path^,false);
while not linkotherstaticlibs.empty do
AddStaticCLibrary(linkotherstaticlibs.Getusemask(mask));
while not linkothersharedlibs.empty do
AddSharedCLibrary(linkothersharedlibs.Getusemask(mask));
end;
end;
Procedure TLinker.AddObject(const S,unitpath : String;isunit:boolean);
begin
ObjectFiles.Concat(FindObjectFile(s,unitpath,isunit));
end;
Procedure TLinker.AddSharedLibrary(S:String);
begin
if s='' then
exit;
{ remove prefix 'lib' }
if Copy(s,1,length(target_info.sharedlibprefix))=target_info.sharedlibprefix then
Delete(s,1,length(target_info.sharedlibprefix));
{ remove extension if any }
if Copy(s,length(s)-length(target_info.sharedlibext)+1,length(target_info.sharedlibext))=target_info.sharedlibext then
Delete(s,length(s)-length(target_info.sharedlibext)+1,length(target_info.sharedlibext)+1);
{ ready to be added }
SharedLibFiles.Concat(S);
end;
Procedure TLinker.AddStaticLibrary(const S:String);
var
ns : string;
found : boolean;
begin
if s='' then
exit;
found:=FindLibraryFile(s,target_info.staticlibprefix,target_info.staticlibext,ns);
if not(cs_link_extern in aktglobalswitches) and (not found) then
Message1(exec_w_libfile_not_found,s);
StaticLibFiles.Concat(ns);
end;
Procedure TLinker.AddSharedCLibrary(S:String);
begin
if s='' then
exit;
{ remove prefix 'lib' }
if Copy(s,1,length(target_info.sharedclibprefix))=target_info.sharedclibprefix then
Delete(s,1,length(target_info.sharedclibprefix));
{ remove extension if any }
if Copy(s,length(s)-length(target_info.sharedclibext)+1,length(target_info.sharedclibext))=target_info.sharedclibext then
Delete(s,length(s)-length(target_info.sharedclibext)+1,length(target_info.sharedclibext)+1);
{ ready to be added }
SharedLibFiles.Concat(S);
end;
Procedure TLinker.AddStaticCLibrary(const S:String);
var
ns : string;
found : boolean;
begin
if s='' then
exit;
found:=FindLibraryFile(s,target_info.staticclibprefix,target_info.staticclibext,ns);
if not(cs_link_extern in aktglobalswitches) and (not found) then
Message1(exec_w_libfile_not_found,s);
StaticLibFiles.Concat(ns);
end;
function TLinker.MakeExecutable:boolean;
begin
MakeExecutable:=false;
Message(exec_e_exe_not_supported);
end;
Function TLinker.MakeSharedLibrary:boolean;
begin
MakeSharedLibrary:=false;
Message(exec_e_dll_not_supported);
end;
Function TLinker.MakeStaticLibrary:boolean;
begin
MakeStaticLibrary:=false;
Message(exec_e_dll_not_supported);
end;
{*****************************************************************************
TEXTERNALLINKER
*****************************************************************************}
Constructor TExternalLinker.Create;
begin
inherited Create;
{ set generic defaults }
FillChar(Info,sizeof(Info),0);
if cs_link_on_target in aktglobalswitches then
begin
Info.ResName:=outputexedir+inputfile+'_link.res';
Info.ScriptName:=outputexedir+inputfile+'_script.res';
end
else
begin
Info.ResName:='link.res';
Info.ScriptName:='script.res';
end;
{ set the linker specific defaults }
SetDefaultInfo;
{ Allow Parameter overrides for linker info }
with Info do
begin
if ParaLinkOptions<>'' then
ExtraOptions:=ParaLinkOptions;
if ParaDynamicLinker<>'' then
DynamicLinker:=ParaDynamicLinker;
end;
end;
Destructor TExternalLinker.Destroy;
begin
inherited destroy;
end;
Procedure TExternalLinker.SetDefaultInfo;
begin
end;
Function TExternalLinker.FindUtil(const s:string):string;
var
Found : boolean;
FoundBin : string;
UtilExe : string;
begin
if cs_link_on_target in aktglobalswitches then
begin
{ If linking on target, don't add any path PM }
FindUtil:=AddExtension(s,target_info.exeext);
exit;
end;
UtilExe:=AddExtension(s,source_info.exeext);
FoundBin:='';
Found:=false;
if utilsdirectory<>'' then
Found:=FindFile(utilexe,utilsdirectory,Foundbin);
if (not Found) then
Found:=FindExe(utilexe,Foundbin);
if (not Found) and not(cs_link_extern in aktglobalswitches) then
begin
Message1(exec_e_util_not_found,utilexe);
aktglobalswitches:=aktglobalswitches+[cs_link_extern];
end;
if (FoundBin<>'') then
Message1(exec_t_using_util,FoundBin);
FindUtil:=FoundBin;
end;
Function TExternalLinker.DoExec(const command,para:string;showinfo,useshell:boolean):boolean;
begin
DoExec:=true;
if not(cs_link_extern in aktglobalswitches) then
begin
if useshell then
shell(maybequoted(command)+' '+para)
else
begin
swapvectors;
exec(command,para);
swapvectors;
end;
if (doserror<>0) then
begin
Message(exec_e_cant_call_linker);
aktglobalswitches:=aktglobalswitches+[cs_link_extern];
DoExec:=false;
end
else
if (dosexitcode<>0) then
begin
Message(exec_e_error_while_linking);
aktglobalswitches:=aktglobalswitches+[cs_link_extern];
DoExec:=false;
end;
end;
{ Update asmres when externmode is set }
if cs_link_extern in aktglobalswitches then
begin
if showinfo then
begin
if DLLsource then
AsmRes.AddLinkCommand(Command,Para,current_module.sharedlibfilename^)
else
AsmRes.AddLinkCommand(Command,Para,current_module.exefilename^);
end
else
AsmRes.AddLinkCommand(Command,Para,'');
end;
end;
Function TExternalLinker.MakeStaticLibrary:boolean;
var
smartpath,
cmdstr,
binstr : string;
success : boolean;
begin
MakeStaticLibrary:=false;
{ remove the library, to be sure that it is rewritten }
RemoveFile(current_module.staticlibfilename^);
{ Call AR }
smartpath:=current_module.outputpath^+FixPath(lower(current_module.modulename^)+target_info.smartext,false);
SplitBinCmd(target_ar.arcmd,binstr,cmdstr);
Replace(cmdstr,'$LIB',current_module.staticlibfilename^);
Replace(cmdstr,'$FILES',FixFileName(smartpath+current_module.asmprefix^+'*'+target_info.objext));
success:=DoExec(FindUtil(binstr),cmdstr,false,true);
{ Clean up }
if not(cs_asm_leave in aktglobalswitches) then
if not(cs_link_extern in aktglobalswitches) then
begin
while not SmartLinkOFiles.Empty do
RemoveFile(SmartLinkOFiles.GetFirst);
RemoveDir(smartpath);
end
else
begin
AsmRes.AddDeleteCommand(FixFileName(smartpath+current_module.asmprefix^+'*'+target_info.objext));
AsmRes.Add('rmdir '+smartpath);
end;
MakeStaticLibrary:=success;
end;
{*****************************************************************************
TINTERNALLINKER
*****************************************************************************}
Constructor TInternalLinker.Create;
begin
inherited Create;
exemap:=nil;
exeoutput:=nil;
end;
Destructor TInternalLinker.Destroy;
begin
exeoutput.free;
exeoutput:=nil;
inherited destroy;
end;
procedure TInternalLinker.readobj(const fn:string);
var
objdata : TAsmObjectData;
objinput : tobjectinput;
begin
Comment(V_Info,'Reading object '+fn);
objinput:=exeoutput.newobjectinput;
objdata:=objinput.newobjectdata(fn);
if objinput.readobjectfile(fn,objdata) then
exeoutput.addobjdata(objdata);
{ release input object }
objinput.free;
end;
function TInternalLinker.MakeExecutable:boolean;
var
s : string;
begin
MakeExecutable:=false;
{ no support yet for libraries }
if (not StaticLibFiles.Empty) or
(not SharedLibFiles.Empty) then
internalerror(123456789);
if (cs_link_map in aktglobalswitches) then
exemap:=texemap.create(current_module.mapfilename^);
{ read objects }
readobj(FindObjectFile('prt0','',false));
while not ObjectFiles.Empty do
begin
s:=ObjectFiles.GetFirst;
if s<>'' then
readobj(s);
end;
{ generate executable }
exeoutput.GenerateExecutable(current_module.exefilename^);
{ close map }
if assigned(exemap) then
begin
exemap.free;
exemap:=nil;
end;
MakeExecutable:=true;
end;
{*****************************************************************************
Init/Done
*****************************************************************************}
procedure InitLinker;
var
lk : TlinkerClass;
begin
if (cs_link_internal in aktglobalswitches) and
assigned(target_info.link) then
begin
lk:=TLinkerClass(target_info.link);
linker:=lk.Create;
end
else if assigned(target_info.linkextern) then
begin
lk:=TlinkerClass(target_info.linkextern);
linker:=lk.Create;
end
else
begin
linker:=Tlinker.Create;
end;
end;
procedure DoneLinker;
begin
if assigned(linker) then
Linker.Free;
end;
{*****************************************************************************
Initialize
*****************************************************************************}
const
ar_gnu_ar_info : tarinfo =
(
id : ar_gnu_ar;
arcmd : 'ar rs $LIB $FILES'
);
initialization
RegisterAr(ar_gnu_ar_info);
end.
{
$Log$
Revision 1.39 2003-12-11 17:53:03 florian
* fixed external smartlinking
Revision 1.38 2003/09/14 21:33:11 peter
* don't check exepath when linking on target
Revision 1.37 2003/06/12 16:41:51 peter
* add inputfile prefix to ppas/link.res
Revision 1.36 2003/05/09 17:47:02 peter
* self moved to hidden parameter
* removed hdisposen,hnewn,selfn
Revision 1.35 2003/04/26 09:16:07 peter
* .o files belonging to the unit are first searched in the same dir
as the .ppu
Revision 1.34 2003/02/12 22:04:59 carl
- removed my stupid hello debug code
Revision 1.33 2002/11/15 01:58:48 peter
* merged changes from 1.0.7 up to 04-11
- -V option for generating bug report tracing
- more tracing for option parsing
- errors for cdecl and high()
- win32 import stabs
- win32 records<=8 are returned in eax:edx (turned off by default)
- heaptrc update
- more info for temp management in .s file with EXTDEBUG
Revision 1.32 2002/11/09 15:37:21 carl
- removed no longer used defines
Revision 1.31 2002/09/07 15:25:02 peter
* old logs removed and tabs fixed
Revision 1.30 2002/08/12 15:08:39 carl
+ stab register indexes for powerpc (moved from gdb to cpubase)
+ tprocessor enumeration moved to cpuinfo
+ linker in target_info is now a class
* many many updates for m68k (will soon start to compile)
- removed some ifdef or correct them for correct cpu
Revision 1.29 2002/07/01 18:46:22 peter
* internal linker
* reorganized aasm layer
Revision 1.28 2002/05/18 13:34:08 peter
* readded missing revisions
Revision 1.27 2002/05/16 19:46:37 carl
+ defines.inc -> fpcdefs.inc to avoid conflicts if compiling by hand
+ try to fix temp allocation (still in ifdef)
+ generic constructor calls
+ start of tassembler / tmodulebase class cleanup
Revision 1.25 2002/01/19 11:57:05 peter
* fixed path appending for lib
}