mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-10 14:58:26 +02:00

the external linker * create import libraries for dll imports, this uses the new objdata framework to generate the binary object files directly without needing an assembler pass * store import_dll and import_name in ppu * external linker uses import libraries * internal linker uses import info from symtables, no dlls are needed anymore git-svn-id: trunk@3255 -
1031 lines
30 KiB
ObjectPascal
1031 lines
30 KiB
ObjectPascal
{
|
|
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,
|
|
globtype,
|
|
ogbase;
|
|
|
|
Type
|
|
TLinkerInfo=record
|
|
ExeCmd,
|
|
DllCmd : array[1..3] of string;
|
|
ResName : string[100];
|
|
ScriptName : string[100];
|
|
ExtraOptions : string;
|
|
DynamicLinker : string[100];
|
|
end;
|
|
|
|
TLinker = class(TAbstractLinker)
|
|
private
|
|
procedure AddProcdefImports(p:tnamedindexitem;arg:pointer);
|
|
public
|
|
HasResources,
|
|
HasExports : boolean;
|
|
ObjectFiles,
|
|
SharedLibFiles,
|
|
StaticLibFiles : TStringList;
|
|
Constructor Create;virtual;
|
|
Destructor Destroy;override;
|
|
procedure AddModuleFiles(hp:tmodule);
|
|
procedure AddExternalSymbol(const libname,symname:string);virtual;
|
|
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:string; para:TCmdStr;showinfo,useshell:boolean):boolean;
|
|
procedure SetDefaultInfo;virtual;
|
|
Function MakeStaticLibrary:boolean;override;
|
|
end;
|
|
|
|
TInternalLinker = class(TLinker)
|
|
private
|
|
FCExeOutput : TExeOutputClass;
|
|
FCObjInput : TObjInputClass;
|
|
{ Libraries }
|
|
FExternalLibraryList : TFPHashObjectList;
|
|
procedure Load_ReadObject(const para:string);
|
|
procedure Load_ReadUnitObjects;
|
|
procedure ParseScript_Load;
|
|
procedure ParseScript_Order;
|
|
procedure ParseScript_CalcPos;
|
|
procedure PrintLinkerScript;
|
|
protected
|
|
property CObjInput:TObjInputClass read FCObjInput write FCObjInput;
|
|
property CExeOutput:TExeOutputClass read FCExeOutput write FCExeOutput;
|
|
property ExternalLibraryList:TFPHashObjectList read FExternalLibraryList;
|
|
procedure DefaultLinkScript;virtual;abstract;
|
|
linkscript : TStringList;
|
|
public
|
|
Constructor Create;override;
|
|
Destructor Destroy;override;
|
|
Function MakeExecutable:boolean;override;
|
|
procedure AddExternalSymbol(const libname,symname:string);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;
|
|
function FindDLL(const s:string;var founddll:string):boolean;
|
|
|
|
procedure InitLinker;
|
|
procedure DoneLinker;
|
|
|
|
|
|
Implementation
|
|
|
|
uses
|
|
{$IFDEF USE_SYSUTILS}
|
|
SysUtils,
|
|
{$ELSE USE_SYSUTILS}
|
|
dos,
|
|
{$ENDIF USE_SYSUTILS}
|
|
cutils,
|
|
script,globals,verbose,comphook,ppu,
|
|
aasmbase,aasmtai,aasmdata,aasmcpu,
|
|
symbase,symdef,symtype,symconst,
|
|
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 linking on target, the units has not been assembled yet,
|
|
so there is no object files to look for at
|
|
the host. Look for the corresponding assembler file instead,
|
|
because it will be assembled to object file on the target.}
|
|
if isunit and (cs_link_on_target in aktglobalswitches) then
|
|
s:= ForceExtension(s,target_info.asmext);
|
|
|
|
{ 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. output unit path
|
|
2. output exe path
|
|
3. specified unit path (if specified)
|
|
4. cwd
|
|
5. unit search path
|
|
6. local object path
|
|
7. global object path
|
|
8. exepath (not when linking on target) }
|
|
found:=false;
|
|
if isunit and (OutputUnitDir<>'') then
|
|
found:=FindFile(s,OutPutUnitDir,foundfile)
|
|
else
|
|
if OutputExeDir<>'' then
|
|
found:=FindFile(s,OutPutExeDir,foundfile);
|
|
if (not found) and (unitpath<>'') then
|
|
found:=FindFile(s,unitpath,foundfile);
|
|
if (not found) then
|
|
found:=FindFile(s, CurDirRelPath(source_info), 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);
|
|
|
|
{Restore file extension}
|
|
if isunit and (cs_link_on_target in aktglobalswitches) then
|
|
foundfile:= ForceExtension(foundfile,target_info.objext);
|
|
|
|
findobjectfile:=ScriptFixFileName(foundfile);
|
|
end;
|
|
|
|
|
|
{ searches a (windows) DLL file }
|
|
function FindDLL(const s:string;var founddll:string):boolean;
|
|
var
|
|
sysdir : string;
|
|
Found : boolean;
|
|
begin
|
|
Found:=false;
|
|
{ Look for DLL in:
|
|
1. Current dir
|
|
2. Library Path
|
|
3. windir,windir/system,windir/system32 }
|
|
Found:=FindFile(s,'.'+source_info.DirSep,founddll);
|
|
if (not found) then
|
|
Found:=librarysearchpath.FindFile(s,founddll);
|
|
if (not found) then
|
|
begin
|
|
{$IFDEF USE_SYSUTILS}
|
|
sysdir:=FixPath(GetEnvironmentVariable('windir'),false);
|
|
{$ELSE USE_SYSUTILS}
|
|
sysdir:=FixPath(GetEnv('windir'),false);
|
|
{$ENDIF USE_SYSUTILS}
|
|
Found:=FindFile(s,sysdir+';'+sysdir+'system'+source_info.DirSep+';'+sysdir+'system32'+source_info.DirSep,founddll);
|
|
end;
|
|
if (not found) then
|
|
begin
|
|
message1(exec_w_libfile_not_found,s);
|
|
FoundDll:=s;
|
|
end;
|
|
FindDll:=Found;
|
|
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, CurDirRelPath(source_info), 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.AddProcdefImports(p:tnamedindexitem;arg:pointer);
|
|
begin
|
|
if tdef(p).deftype<>procdef then
|
|
exit;
|
|
if assigned(tprocdef(p).import_dll) and
|
|
assigned(tprocdef(p).import_name) then
|
|
AddExternalSymbol(tprocdef(p).import_dll^,tprocdef(p).import_name^);
|
|
end;
|
|
|
|
|
|
procedure TLinker.AddModuleFiles(hp:tmodule);
|
|
var
|
|
mask : longint;
|
|
begin
|
|
with hp do
|
|
begin
|
|
if (flags and uf_has_resourcefiles)<>0 then
|
|
HasResources:=true;
|
|
if (flags and uf_has_exports)<>0 then
|
|
HasExports:=true;
|
|
{ link unit files }
|
|
if (flags and uf_no_link)=0 then
|
|
begin
|
|
{ create mask which unit files need linking }
|
|
mask:=link_always;
|
|
{ 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
|
|
AddObject(linkunitofiles.getusemask(mask),path^,true);
|
|
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_always;
|
|
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));
|
|
{ Known Library/DLL Imports }
|
|
if assigned(globalsymtable) then
|
|
globalsymtable.defindex.foreach(@AddProcdefImports,nil);
|
|
if assigned(localsymtable) then
|
|
localsymtable.defindex.foreach(@AddProcdefImports,nil);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TLinker.AddExternalSymbol(const libname,symname:string);
|
|
begin
|
|
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:string; para:TCmdStr;showinfo,useshell:boolean):boolean;
|
|
var
|
|
exitcode: longint;
|
|
begin
|
|
DoExec:=true;
|
|
if not(cs_link_extern in aktglobalswitches) then
|
|
begin
|
|
if useshell then
|
|
exitcode := shell(maybequoted(command)+' '+para)
|
|
else
|
|
{$IFDEF USE_SYSUTILS}
|
|
try
|
|
if ExecuteProcess(command,para) <> 0
|
|
then begin
|
|
Message(exec_e_error_while_linking);
|
|
aktglobalswitches:=aktglobalswitches+[cs_link_extern];
|
|
DoExec:=false;
|
|
end;
|
|
except on E:EOSError do
|
|
begin
|
|
Message(exec_e_cant_call_linker);
|
|
aktglobalswitches:=aktglobalswitches+[cs_link_extern];
|
|
DoExec:=false;
|
|
end;
|
|
end
|
|
end;
|
|
{$ELSE USE_SYSUTILS}
|
|
begin
|
|
swapvectors;
|
|
exec(command,para);
|
|
swapvectors;
|
|
exitcode := dosexitcode;
|
|
end;
|
|
if (doserror<>0) then
|
|
begin
|
|
Message(exec_e_cant_call_linker);
|
|
aktglobalswitches:=aktglobalswitches+[cs_link_extern];
|
|
DoExec:=false;
|
|
end
|
|
else
|
|
if (exitcode<>0) then
|
|
begin
|
|
Message(exec_e_error_while_linking);
|
|
aktglobalswitches:=aktglobalswitches+[cs_link_extern];
|
|
DoExec:=false;
|
|
end;
|
|
end;
|
|
{$ENDIF USE_SYSUTILS}
|
|
{ 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;
|
|
|
|
function GetNextFiles(const maxCmdLength : AInt; var item : TStringListItem) : string;
|
|
begin
|
|
result := '';
|
|
while (assigned(item) and ((length(result) + length(item.str) + 1) < maxCmdLength)) do begin
|
|
result := result + ' ' + item.str;
|
|
item := TStringListItem(item.next);
|
|
end;
|
|
end;
|
|
|
|
var
|
|
binstr, scriptfile : string;
|
|
success : boolean;
|
|
cmdstr, nextcmd, smartpath : TCmdStr;
|
|
current : TStringListItem;
|
|
script: Text;
|
|
scripted_ar : 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);
|
|
binstr := FindUtil(utilsprefix + binstr);
|
|
|
|
|
|
scripted_ar:=target_ar.id=ar_gnu_ar_scripted;
|
|
|
|
if scripted_ar then
|
|
begin
|
|
scriptfile := FixFileName(smartpath+'arscript.txt');
|
|
Replace(cmdstr,'$SCRIPT',maybequoted(scriptfile));
|
|
Assign(script, scriptfile);
|
|
Rewrite(script);
|
|
try
|
|
writeln(script, 'CREATE ' + current_module.staticlibfilename^);
|
|
current := TStringListItem(SmartLinkOFiles.First);
|
|
while current <> nil do
|
|
begin
|
|
writeln(script, 'ADDMOD ' + current.str);
|
|
current := TStringListItem(current.next);
|
|
end;
|
|
writeln(script, 'SAVE');
|
|
writeln(script, 'END');
|
|
finally
|
|
Close(script);
|
|
end;
|
|
success:=DoExec(binstr,cmdstr,false,true);
|
|
end
|
|
else
|
|
begin
|
|
Replace(cmdstr,'$LIB',maybequoted(current_module.staticlibfilename^));
|
|
{ create AR commands }
|
|
success := true;
|
|
nextcmd := cmdstr;
|
|
current := TStringListItem(SmartLinkOFiles.First);
|
|
repeat
|
|
Replace(nextcmd,'$FILES',GetNextFiles(240 - length(nextcmd) + 6 - length(binstr) - 1, current));
|
|
success:=DoExec(binstr,nextcmd,false,true);
|
|
nextcmd := cmdstr;
|
|
until (not assigned(current)) or (not success);
|
|
end;
|
|
|
|
if (target_ar.arfinishcmd <> '') then
|
|
begin
|
|
SplitBinCmd(target_ar.arfinishcmd,binstr,cmdstr);
|
|
Replace(cmdstr,'$LIB',maybequoted(current_module.staticlibfilename^));
|
|
success:=DoExec(binstr,cmdstr,false,true);
|
|
end;
|
|
|
|
{ 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);
|
|
if scripted_ar then
|
|
RemoveFile(scriptfile);
|
|
RemoveDir(smartpath);
|
|
end
|
|
else
|
|
begin
|
|
AsmRes.AddDeleteCommand(FixFileName(smartpath+current_module.asmprefix^+'*'+target_info.objext));
|
|
if scripted_ar then
|
|
AsmRes.AddDeleteCommand(scriptfile);
|
|
AsmRes.Add('rmdir '+smartpath);
|
|
end;
|
|
MakeStaticLibrary:=success;
|
|
end;
|
|
|
|
|
|
{*****************************************************************************
|
|
TINTERNALLINKER
|
|
*****************************************************************************}
|
|
|
|
Constructor TInternalLinker.Create;
|
|
begin
|
|
inherited Create;
|
|
linkscript:=TStringList.Create;
|
|
FExternalLibraryList:=TFPHashObjectList.Create(true);
|
|
exemap:=nil;
|
|
exeoutput:=nil;
|
|
CObjInput:=TObjInput;
|
|
end;
|
|
|
|
|
|
Destructor TInternalLinker.Destroy;
|
|
begin
|
|
linkscript.free;
|
|
ExternalLibraryList.Free;
|
|
if assigned(exeoutput) then
|
|
begin
|
|
exeoutput.free;
|
|
exeoutput:=nil;
|
|
end;
|
|
if assigned(exemap) then
|
|
begin
|
|
exemap.free;
|
|
exemap:=nil;
|
|
end;
|
|
inherited destroy;
|
|
end;
|
|
|
|
|
|
procedure TInternalLinker.AddExternalSymbol(const libname,symname:string);
|
|
var
|
|
ExtLibrary : TExternalLibrary;
|
|
ExtSymbol : TFPHashObject;
|
|
begin
|
|
ExtLibrary:=TExternalLibrary(ExternalLibraryList.Find(libname));
|
|
if not assigned(ExtLibrary) then
|
|
ExtLibrary:=TExternalLibrary.Create(ExternalLibraryList,libname);
|
|
ExtSymbol:=TFPHashObject(ExtLibrary.ExternalSymbolList.Find(symname));
|
|
if not assigned(ExtSymbol) then
|
|
ExtSymbol:=TFPHashObject.Create(ExtLibrary.ExternalSymbolList,symname);
|
|
end;
|
|
|
|
|
|
procedure TInternalLinker.Load_ReadObject(const para:string);
|
|
var
|
|
objdata : TObjData;
|
|
objinput : TObjinput;
|
|
fn : string;
|
|
begin
|
|
fn:=FindObjectFile(para,'',false);
|
|
Comment(V_Tried,'Reading object '+fn);
|
|
objinput:=CObjInput.Create;
|
|
objdata:=objinput.newObjData(para);
|
|
if objinput.readobjectfile(fn,objdata) then
|
|
exeoutput.addobjdata(objdata);
|
|
{ release input object }
|
|
objinput.free;
|
|
end;
|
|
|
|
|
|
procedure TInternalLinker.Load_ReadUnitObjects;
|
|
var
|
|
s : string;
|
|
begin
|
|
while not ObjectFiles.Empty do
|
|
begin
|
|
s:=ObjectFiles.GetFirst;
|
|
if s<>'' then
|
|
Load_ReadObject(s);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TInternalLinker.ParseScript_Load;
|
|
var
|
|
s,
|
|
para,
|
|
keyword : string;
|
|
hp : TStringListItem;
|
|
begin
|
|
exeoutput.Load_Start;
|
|
hp:=tstringlistitem(linkscript.first);
|
|
while assigned(hp) do
|
|
begin
|
|
s:=hp.str;
|
|
if (s='') or (s[1]='#') then
|
|
continue;
|
|
keyword:=Upper(GetToken(s,' '));
|
|
para:=GetToken(s,' ');
|
|
if keyword='SYMBOL' then
|
|
ExeOutput.Load_Symbol(para)
|
|
else if keyword='ENTRYNAME' then
|
|
ExeOutput.Load_EntryName(para)
|
|
else if keyword='READOBJECT' then
|
|
Load_ReadObject(para)
|
|
else if keyword='READUNITOBJECTS' then
|
|
Load_ReadUnitObjects;
|
|
hp:=tstringlistitem(hp.next);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TInternalLinker.ParseScript_Order;
|
|
var
|
|
s,
|
|
para,
|
|
keyword : string;
|
|
hp : TStringListItem;
|
|
begin
|
|
exeoutput.Order_Start;
|
|
hp:=tstringlistitem(linkscript.first);
|
|
while assigned(hp) do
|
|
begin
|
|
s:=hp.str;
|
|
if (s='') or (s[1]='#') then
|
|
continue;
|
|
keyword:=Upper(GetToken(s,' '));
|
|
para:=GetToken(s,' ');
|
|
if keyword='EXESECTION' then
|
|
ExeOutput.Order_ExeSection(para)
|
|
else if keyword='ENDEXESECTION' then
|
|
ExeOutput.Order_EndExeSection
|
|
else if keyword='OBJSECTION' then
|
|
ExeOutput.Order_ObjSection(para)
|
|
else if keyword='ZEROS' then
|
|
ExeOutput.Order_Zeros(para)
|
|
else if keyword='SYMBOL' then
|
|
ExeOutput.Order_Symbol(para);
|
|
hp:=tstringlistitem(hp.next);
|
|
end;
|
|
exeoutput.Order_End;
|
|
end;
|
|
|
|
|
|
procedure TInternalLinker.ParseScript_CalcPos;
|
|
var
|
|
s,
|
|
para,
|
|
keyword : string;
|
|
hp : TStringListItem;
|
|
begin
|
|
exeoutput.CalcPos_Start;
|
|
hp:=tstringlistitem(linkscript.first);
|
|
while assigned(hp) do
|
|
begin
|
|
s:=hp.str;
|
|
if (s='') or (s[1]='#') then
|
|
continue;
|
|
keyword:=Upper(GetToken(s,' '));
|
|
para:=GetToken(s,' ');
|
|
if keyword='EXESECTION' then
|
|
ExeOutput.CalcPos_ExeSection(para)
|
|
else if keyword='ENDEXESECTION' then
|
|
ExeOutput.CalcPos_EndExeSection
|
|
else if keyword='HEADER' then
|
|
ExeOutput.CalcPos_Header
|
|
else if keyword='SYMBOLS' then
|
|
ExeOutput.CalcPos_Symbols;
|
|
hp:=tstringlistitem(hp.next);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TInternalLinker.PrintLinkerScript;
|
|
var
|
|
hp : TStringListItem;
|
|
begin
|
|
if not assigned(exemap) then
|
|
exit;
|
|
exemap.Add('Used linker script');
|
|
exemap.Add('');
|
|
hp:=tstringlistitem(linkscript.first);
|
|
while assigned(hp) do
|
|
begin
|
|
exemap.Add(hp.str);
|
|
hp:=tstringlistitem(hp.next);
|
|
end;
|
|
end;
|
|
|
|
|
|
function TInternalLinker.MakeExecutable:boolean;
|
|
label
|
|
myexit;
|
|
var
|
|
s,s2 : string;
|
|
begin
|
|
MakeExecutable:=false;
|
|
|
|
Message1(exec_i_linking,current_module.exefilename^);
|
|
|
|
{$warning TODO Load custom linker script}
|
|
DefaultLinkScript;
|
|
|
|
exeoutput:=CExeOutput.Create;
|
|
|
|
if (cs_link_map in aktglobalswitches) then
|
|
exemap:=texemap.create(current_module.mapfilename^);
|
|
|
|
PrintLinkerScript;
|
|
|
|
{ Load .o files and resolve symbols }
|
|
ParseScript_Load;
|
|
exeoutput.ResolveSymbols;
|
|
{ Generate symbols and code to do the importing }
|
|
exeoutput.GenerateLibraryImports(ExternalLibraryList);
|
|
{ Fill external symbols data }
|
|
exeoutput.FixupSymbols;
|
|
if ErrorCount>0 then
|
|
goto myexit;
|
|
|
|
{ Create .exe sections and add .o sections }
|
|
ParseScript_Order;
|
|
exeoutput.RemoveUnreferencedSections;
|
|
exeoutput.MergeStabs;
|
|
exeoutput.RemoveEmptySections;
|
|
if ErrorCount>0 then
|
|
goto myexit;
|
|
|
|
{ Calc positions in mem and file }
|
|
ParseScript_CalcPos;
|
|
exeoutput.FixupRelocations;
|
|
exeoutput.PrintMemoryMap;
|
|
if ErrorCount>0 then
|
|
goto myexit;
|
|
|
|
exeoutput.WriteExeFile(current_module.exefilename^);
|
|
|
|
{$warning TODO fixed section names}
|
|
status.codesize:=exeoutput.findexesection('.text').size;
|
|
status.datasize:=exeoutput.findexesection('.data').size;
|
|
|
|
myexit:
|
|
{ close map }
|
|
if assigned(exemap) then
|
|
begin
|
|
exemap.free;
|
|
exemap:=nil;
|
|
end;
|
|
|
|
{ close exe }
|
|
exeoutput.free;
|
|
exeoutput:=nil;
|
|
|
|
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 qS $LIB $FILES';
|
|
arfinishcmd : 'ar s $LIB'
|
|
);
|
|
|
|
ar_gnu_ar_scripted_info : tarinfo =
|
|
(
|
|
id : ar_gnu_ar_scripted;
|
|
arcmd : 'ar -M < $SCRIPT';
|
|
arfinishcmd : ''
|
|
);
|
|
|
|
initialization
|
|
RegisterAr(ar_gnu_ar_info);
|
|
RegisterAr(ar_gnu_ar_scripted_info);
|
|
end.
|