llvm: generalised sanitizer linking support

Also enabled automatic detection of sanitizer library + setting rpath to Linux
This commit is contained in:
Jonas Maebe 2022-07-24 14:13:59 +02:00
parent 75c16b6126
commit 7ef33cf4b2
3 changed files with 93 additions and 37 deletions

View File

@ -82,7 +82,8 @@ interface
TExternalLinker = class(TLinker) TExternalLinker = class(TLinker)
protected protected
Function WriteSymbolOrderFile: TCmdStr; Function WriteSymbolOrderFile: TCmdStr;
Function GetSanitizersLibraryNameAndPath(const platformname, asanlibinfix: TCmdStr; out sanitizerLibraryDir, asanLibraryPath: TCmdStr): boolean; Function GetSanitizerLibName(const basename: TCmdStr; withArch: boolean): TCmdStr;
Function AddSanitizerLibrariesAndGetSearchDir(const platformname: TCmdStr; out sanitizerlibrarydir: TCmdStr): boolean;
public public
Info : TLinkerInfo; Info : TLinkerInfo;
Constructor Create;override; Constructor Create;override;
@ -174,7 +175,7 @@ Implementation
{$ifdef hasUnix} {$ifdef hasUnix}
baseunix, baseunix,
{$endif hasUnix} {$endif hasUnix}
cscript,globals,verbose,comphook,ppu,fpchash,triplet, cscript,globals,verbose,comphook,ppu,fpchash,triplet,tripletcpu,
aasmbase,aasmcpu, aasmbase,aasmcpu,
ogmap; ogmap;
@ -679,16 +680,50 @@ Implementation
end; end;
Function TExternalLinker.GetSanitizersLibraryNameAndPath(const platformname, asanlibinfix: TCmdStr; out sanitizerLibraryDir, asanLibraryPath: TCmdStr): boolean; Function TExternalLinker.GetSanitizerLibName(const basename: TCmdStr; withArch: boolean): TCmdStr;
begin
result:=target_info.sharedClibprefix+'clang_rt.'+basename;
if target_info.system in systems_darwin then
begin
{ Darwin never adds the arch, it uses fat binaries. But it has the
extra '_dynamic' for some reason, and also adds the platform type
}
if target_info.system in systems_macosx then
result:=result+'_osx_dynamic'
else if target_info.system in systems_ios then
result:='_ios_dynamic'
else if target_info.system in systems_iphonesym then
result:='_iossim_dynamic'
else
internalerror(2022071010);
end
else
begin
if withArch then
begin
result:=result+'-'+tripletcpustr(triplet_llvmrt);
if target_info.system in systems_android then
result:=result+'-android';
end;
end;
result:=result+target_info.sharedClibext;
end;
function TExternalLinker.AddSanitizerLibrariesAndGetSearchDir(const platformname: TCmdStr; out sanitizerlibrarydir: TCmdStr): boolean;
var var
clang, clang,
clangsearchdirs, clangsearchdirs,
textline, textline,
clangsearchdirspath: TCmdStr; clangsearchdirspath,
sanitizerlibname,
sanitizerlibrarypath: TCmdStr;
sanitizerlibraryfiles: TCmdStrList;
searchrec: TSearchRec; searchrec: TSearchRec;
searchres: longint; searchres: longint;
clangsearchdirsfile: text; clangsearchdirsfile: text;
begin begin
sanitizerlibraryfiles:=TCmdStrList.Create;
result:=false; result:=false;
if (cs_sanitize_address in current_settings.moduleswitches) and if (cs_sanitize_address in current_settings.moduleswitches) and
not(cs_link_on_target in current_settings.globalswitches) then not(cs_link_on_target in current_settings.globalswitches) then
@ -708,15 +743,33 @@ Implementation
if ioresult=0 then if ioresult=0 then
begin begin
readln(clangsearchdirsfile,textline); readln(clangsearchdirsfile,textline);
sanitizerLibraryDir:=FixFileName(textline+'/'+platformname); sanitizerlibrarydir:=FixFileName(textline+'/'+platformname);
asanLibraryPath:=FixFileName(sanitizerLibraryDir+'/')+target_info.sharedClibprefix+'clang_rt.asan_'+asanlibinfix+'_dynamic'+target_info.sharedClibext; sanitizerlibrarypath:=FixFileName(sanitizerlibrarydir+'/');
result:=FileExists(asanLibraryPath,false); { from clang:
Check for runtime files in the new layout without the architecture first.
}
sanitizerlibname:=GetSanitizerLibName('asan',false);
result:=FileExists(sanitizerlibrarypath+sanitizerlibname,false);
if result then
begin
sanitizerlibraryfiles.Concat(sanitizerlibrarypath+sanitizerlibname);
end
else
begin
sanitizerlibname:=GetSanitizerLibName('asan',true);
result:=FileExists(sanitizerlibrarypath+sanitizerlibname,false);
if result then
sanitizerlibraryfiles.Concat(sanitizerlibrarypath+sanitizerlibname);
end;
end; end;
end; end;
if FileExists(clangsearchdirspath,false) then if FileExists(clangsearchdirspath,false) then
DeleteFile(clangsearchdirspath); DeleteFile(clangsearchdirspath);
end; end;
end; end;
if result then
ObjectFiles.concatList(sanitizerlibraryfiles);
sanitizerlibraryfiles.free;
end; end;

View File

@ -61,7 +61,6 @@ implementation
function GetLibSearchPath: TCmdStr; function GetLibSearchPath: TCmdStr;
function GetLibraries: TCmdStr; function GetLibraries: TCmdStr;
function GetSanitizerLibraryInfix: TCmdStr;
public public
constructor Create;override; constructor Create;override;
procedure SetDefaultInfo;override; procedure SetDefaultInfo;override;
@ -443,19 +442,6 @@ implementation
end; end;
function tlinkerdarwin.GetSanitizerLibraryInfix: TCmdStr;
begin
if target_info.system in systems_macosx then
result:='osx'
else if target_info.system in systems_ios then
result:='ios'
else if target_info.system in systems_iphonesym then
result:='iossim'
else
internalerror(2022071010);
end;
function tlinkerdarwin.WriteFileList: TCmdStr; function tlinkerdarwin.WriteFileList: TCmdStr;
Var Var
FilesList : TScript; FilesList : TScript;
@ -502,7 +488,6 @@ implementation
GCSectionsStr, GCSectionsStr,
StaticStr, StaticStr,
StripStr, StripStr,
asanLibraryName,
sanitizerLibraryDir: TCmdStr; sanitizerLibraryDir: TCmdStr;
success : boolean; success : boolean;
begin begin
@ -532,7 +517,7 @@ implementation
if (cs_lto in current_settings.moduleswitches) and if (cs_lto in current_settings.moduleswitches) and
not(cs_link_on_target in current_settings.globalswitches) and not(cs_link_on_target in current_settings.globalswitches) and
(utilsdirectory<>'') and (utilsdirectory<>'') and
FileExists(utilsdirectory+'/../lib/libLTO.dylib',true) then FileExists(utilsdirectory+'/../lib/libLTO.dylib',false) then
begin begin
ltostr:='-lto_library '+maybequoted(utilsdirectory+'/../lib/libLTO.dylib'); ltostr:='-lto_library '+maybequoted(utilsdirectory+'/../lib/libLTO.dylib');
end; end;
@ -553,10 +538,11 @@ implementation
else else
Replace(cmdstr,'$ORDERSYMS',''); Replace(cmdstr,'$ORDERSYMS','');
if GetSanitizersLibraryNameAndPath('darwin',GetSanitizerLibraryInfix,sanitizerLibraryDir,asanLibraryName) then if AddSanitizerLibrariesAndGetSearchDir('darwin',sanitizerLibraryDir) then
begin begin
ObjectFiles.Concat(asanLibraryName); { also add the executable path as search path in case the asan
Replace(cmdstr,'$RPATH','-rpath '+sanitizerLibraryDir) library gets copied into the application bundle }
Replace(cmdstr,'$RPATH','-rpath @executable_path -rpath '+maybequoted(sanitizerLibraryDir))
end end
else else
begin begin
@ -624,8 +610,7 @@ implementation
extdbgcmdstr, extdbgcmdstr,
linkfiles, linkfiles,
GCSectionsStr, GCSectionsStr,
sanitizerLibraryDir, sanitizerLibraryDir: TCmdStr;
asanLibraryName: TCmdStr;
exportedsyms: text; exportedsyms: text;
success : boolean; success : boolean;
begin begin
@ -649,7 +634,7 @@ implementation
if (cs_lto in current_settings.moduleswitches) and if (cs_lto in current_settings.moduleswitches) and
not(cs_link_on_target in current_settings.globalswitches) and not(cs_link_on_target in current_settings.globalswitches) and
(utilsdirectory<>'') and (utilsdirectory<>'') and
FileExists(utilsdirectory+'/../lib/libLTO.dylib',true) then FileExists(utilsdirectory+'/../lib/libLTO.dylib',false) then
begin begin
ltostr:='-lto_library '+maybequoted(utilsdirectory+'/../lib/libLTO.dylib'); ltostr:='-lto_library '+maybequoted(utilsdirectory+'/../lib/libLTO.dylib');
end; end;
@ -675,10 +660,11 @@ implementation
else else
Replace(cmdstr,'$ORDERSYMS',''); Replace(cmdstr,'$ORDERSYMS','');
{ add asan library if known } { add asan library if known }
if GetSanitizersLibraryNameAndPath('darwin',GetSanitizerLibraryInfix,sanitizerLibraryDir,asanLibraryName) then if AddSanitizerLibrariesAndGetSearchDir('darwin',sanitizerLibraryDir) then
begin begin
ObjectFiles.Concat(asanLibraryName); { also add the executable path as search path in case the asan
Replace(cmdstr,'$RPATH','-rpath '+sanitizerLibraryDir) library gets copied into the application bundle }
Replace(cmdstr,'$RPATH','-rpath @executable_path -rpath '+maybequoted(sanitizerLibraryDir))
end end
else else
begin begin

View File

@ -398,8 +398,8 @@ begin
with Info do with Info do
begin begin
ExeCmd[1]:='ld '+platform_select+platformopt+' $OPT $DYNLINK $STATIC $GCSECTIONS $STRIP $MAP $LTO -L. -o $EXE'; ExeCmd[1]:='ld '+platform_select+platformopt+' $OPT $DYNLINK $STATIC $GCSECTIONS $STRIP $MAP $LTO $RPATH -L. -o $EXE';
DllCmd[1]:='ld '+platform_select+platformopt+' $OPT $INIT $FINI $SONAME $MAP $LTO -shared $GCSECTIONS -L. -o $EXE'; DllCmd[1]:='ld '+platform_select+platformopt+' $OPT $INIT $FINI $SONAME $MAP $LTO $RPATH -shared $GCSECTIONS -L. -o $EXE';
{ when we want to cross-link we need to override default library paths; { when we want to cross-link we need to override default library paths;
when targeting binutils 2.19 or later, we use the "INSERT" command to when targeting binutils 2.19 or later, we use the "INSERT" command to
augment the default linkerscript, which also requires -T (normally that augment the default linkerscript, which also requires -T (normally that
@ -740,7 +740,9 @@ var
binstr, binstr,
cmdstr, cmdstr,
mapstr, mapstr,
ltostr : TCmdStr; ltostr,
rpathstr,
sanitizerLibraryDir: TCmdStr;
success : boolean; success : boolean;
DynLinkStr : ansistring; DynLinkStr : ansistring;
GCSectionsStr, GCSectionsStr,
@ -757,6 +759,7 @@ begin
DynLinkStr:=''; DynLinkStr:='';
mapstr:=''; mapstr:='';
ltostr:=''; ltostr:='';
rpathstr:='';
if (cs_link_staticflag in current_settings.globalswitches) then if (cs_link_staticflag in current_settings.globalswitches) then
StaticStr:='-static'; StaticStr:='-static';
if (cs_link_strip in current_settings.globalswitches) and if (cs_link_strip in current_settings.globalswitches) and
@ -786,6 +789,11 @@ begin
ltostr:='-plugin '+maybequoted(utilsdirectory+'/../lib/LLVMgold.so '); ltostr:='-plugin '+maybequoted(utilsdirectory+'/../lib/LLVMgold.so ');
end; end;
if AddSanitizerLibrariesAndGetSearchDir('linux',sanitizerLibraryDir) then
begin
rpathstr:='-rpath '+maybequoted(sanitizerLibraryDir);
end;
{ Write used files and libraries } { Write used files and libraries }
WriteResponseFile(false); WriteResponseFile(false);
@ -800,6 +808,7 @@ begin
Replace(cmdstr,'$DYNLINK',DynLinkStr); Replace(cmdstr,'$DYNLINK',DynLinkStr);
Replace(cmdstr,'$MAP',mapstr); Replace(cmdstr,'$MAP',mapstr);
Replace(cmdstr,'$LTO',ltostr); Replace(cmdstr,'$LTO',ltostr);
Replace(cmdstr,'$RPATH',rpathstr);
{ create dynamic symbol table? } { create dynamic symbol table? }
if HasExports then if HasExports then
@ -853,7 +862,9 @@ var
binstr, binstr,
cmdstr, cmdstr,
mapstr, mapstr,
ltostr : TCmdStr; ltostr,
rpathstr,
sanitizerLibraryDir: TCmdStr;
success : boolean; success : boolean;
begin begin
MakeSharedLibrary:=false; MakeSharedLibrary:=false;
@ -887,7 +898,12 @@ begin
ltostr:='-plugin '+maybequoted(utilsdirectory+'/../lib/LLVMgold.so '); ltostr:='-plugin '+maybequoted(utilsdirectory+'/../lib/LLVMgold.so ');
end; end;
{ Call linker } if AddSanitizerLibrariesAndGetSearchDir('linux',sanitizerLibraryDir) then
begin
rpathstr:='-rpath '+maybequoted(sanitizerLibraryDir)
end;
{ Call linker }
SplitBinCmd(Info.DllCmd[1],binstr,cmdstr); SplitBinCmd(Info.DllCmd[1],binstr,cmdstr);
Replace(cmdstr,'$EXE',maybequoted(current_module.sharedlibfilename)); Replace(cmdstr,'$EXE',maybequoted(current_module.sharedlibfilename));
Replace(cmdstr,'$OPT',Info.ExtraOptions); Replace(cmdstr,'$OPT',Info.ExtraOptions);
@ -897,6 +913,7 @@ begin
Replace(cmdstr,'$SONAME',SoNameStr); Replace(cmdstr,'$SONAME',SoNameStr);
Replace(cmdstr,'$MAP',mapstr); Replace(cmdstr,'$MAP',mapstr);
Replace(cmdstr,'$LTO',ltostr); Replace(cmdstr,'$LTO',ltostr);
Replace(cmdstr,'$RPATH',rpathstr);
Replace(cmdstr,'$GCSECTIONS',GCSectionsStr); Replace(cmdstr,'$GCSECTIONS',GCSectionsStr);
success:=DoExec(FindUtil(utilsprefix+binstr),cmdstr,true,false); success:=DoExec(FindUtil(utilsprefix+binstr),cmdstr,true,false);