From 7ef33cf4b2857437662c9aaf505d824e3fdca023 Mon Sep 17 00:00:00 2001 From: Jonas Maebe Date: Sun, 24 Jul 2022 14:13:59 +0200 Subject: [PATCH] llvm: generalised sanitizer linking support Also enabled automatic detection of sanitizer library + setting rpath to Linux --- compiler/link.pas | 67 +++++++++++++++++++++++++++++++---- compiler/systems/t_darwin.pas | 36 ++++++------------- compiler/systems/t_linux.pas | 27 +++++++++++--- 3 files changed, 93 insertions(+), 37 deletions(-) diff --git a/compiler/link.pas b/compiler/link.pas index 6cb041fcef..c198693e68 100644 --- a/compiler/link.pas +++ b/compiler/link.pas @@ -82,7 +82,8 @@ interface TExternalLinker = class(TLinker) protected 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 Info : TLinkerInfo; Constructor Create;override; @@ -174,7 +175,7 @@ Implementation {$ifdef hasUnix} baseunix, {$endif hasUnix} - cscript,globals,verbose,comphook,ppu,fpchash,triplet, + cscript,globals,verbose,comphook,ppu,fpchash,triplet,tripletcpu, aasmbase,aasmcpu, ogmap; @@ -679,16 +680,50 @@ Implementation 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 clang, clangsearchdirs, textline, - clangsearchdirspath: TCmdStr; + clangsearchdirspath, + sanitizerlibname, + sanitizerlibrarypath: TCmdStr; + sanitizerlibraryfiles: TCmdStrList; searchrec: TSearchRec; searchres: longint; clangsearchdirsfile: text; begin + sanitizerlibraryfiles:=TCmdStrList.Create; result:=false; if (cs_sanitize_address in current_settings.moduleswitches) and not(cs_link_on_target in current_settings.globalswitches) then @@ -708,15 +743,33 @@ Implementation if ioresult=0 then begin readln(clangsearchdirsfile,textline); - sanitizerLibraryDir:=FixFileName(textline+'/'+platformname); - asanLibraryPath:=FixFileName(sanitizerLibraryDir+'/')+target_info.sharedClibprefix+'clang_rt.asan_'+asanlibinfix+'_dynamic'+target_info.sharedClibext; - result:=FileExists(asanLibraryPath,false); + sanitizerlibrarydir:=FixFileName(textline+'/'+platformname); + sanitizerlibrarypath:=FixFileName(sanitizerlibrarydir+'/'); + { 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; if FileExists(clangsearchdirspath,false) then DeleteFile(clangsearchdirspath); end; end; + if result then + ObjectFiles.concatList(sanitizerlibraryfiles); + sanitizerlibraryfiles.free; end; diff --git a/compiler/systems/t_darwin.pas b/compiler/systems/t_darwin.pas index e1c16d3451..112e1d5d37 100644 --- a/compiler/systems/t_darwin.pas +++ b/compiler/systems/t_darwin.pas @@ -61,7 +61,6 @@ implementation function GetLibSearchPath: TCmdStr; function GetLibraries: TCmdStr; - function GetSanitizerLibraryInfix: TCmdStr; public constructor Create;override; procedure SetDefaultInfo;override; @@ -443,19 +442,6 @@ implementation 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; Var FilesList : TScript; @@ -502,7 +488,6 @@ implementation GCSectionsStr, StaticStr, StripStr, - asanLibraryName, sanitizerLibraryDir: TCmdStr; success : boolean; begin @@ -532,7 +517,7 @@ implementation if (cs_lto in current_settings.moduleswitches) and not(cs_link_on_target in current_settings.globalswitches) and (utilsdirectory<>'') and - FileExists(utilsdirectory+'/../lib/libLTO.dylib',true) then + FileExists(utilsdirectory+'/../lib/libLTO.dylib',false) then begin ltostr:='-lto_library '+maybequoted(utilsdirectory+'/../lib/libLTO.dylib'); end; @@ -553,10 +538,11 @@ implementation else Replace(cmdstr,'$ORDERSYMS',''); - if GetSanitizersLibraryNameAndPath('darwin',GetSanitizerLibraryInfix,sanitizerLibraryDir,asanLibraryName) then + if AddSanitizerLibrariesAndGetSearchDir('darwin',sanitizerLibraryDir) then begin - ObjectFiles.Concat(asanLibraryName); - Replace(cmdstr,'$RPATH','-rpath '+sanitizerLibraryDir) + { also add the executable path as search path in case the asan + library gets copied into the application bundle } + Replace(cmdstr,'$RPATH','-rpath @executable_path -rpath '+maybequoted(sanitizerLibraryDir)) end else begin @@ -624,8 +610,7 @@ implementation extdbgcmdstr, linkfiles, GCSectionsStr, - sanitizerLibraryDir, - asanLibraryName: TCmdStr; + sanitizerLibraryDir: TCmdStr; exportedsyms: text; success : boolean; begin @@ -649,7 +634,7 @@ implementation if (cs_lto in current_settings.moduleswitches) and not(cs_link_on_target in current_settings.globalswitches) and (utilsdirectory<>'') and - FileExists(utilsdirectory+'/../lib/libLTO.dylib',true) then + FileExists(utilsdirectory+'/../lib/libLTO.dylib',false) then begin ltostr:='-lto_library '+maybequoted(utilsdirectory+'/../lib/libLTO.dylib'); end; @@ -675,10 +660,11 @@ implementation else Replace(cmdstr,'$ORDERSYMS',''); { add asan library if known } - if GetSanitizersLibraryNameAndPath('darwin',GetSanitizerLibraryInfix,sanitizerLibraryDir,asanLibraryName) then + if AddSanitizerLibrariesAndGetSearchDir('darwin',sanitizerLibraryDir) then begin - ObjectFiles.Concat(asanLibraryName); - Replace(cmdstr,'$RPATH','-rpath '+sanitizerLibraryDir) + { also add the executable path as search path in case the asan + library gets copied into the application bundle } + Replace(cmdstr,'$RPATH','-rpath @executable_path -rpath '+maybequoted(sanitizerLibraryDir)) end else begin diff --git a/compiler/systems/t_linux.pas b/compiler/systems/t_linux.pas index 1370cdea92..32fcb50e96 100644 --- a/compiler/systems/t_linux.pas +++ b/compiler/systems/t_linux.pas @@ -398,8 +398,8 @@ begin with Info do begin - ExeCmd[1]:='ld '+platform_select+platformopt+' $OPT $DYNLINK $STATIC $GCSECTIONS $STRIP $MAP $LTO -L. -o $EXE'; - DllCmd[1]:='ld '+platform_select+platformopt+' $OPT $INIT $FINI $SONAME $MAP $LTO -shared $GCSECTIONS -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 $RPATH -shared $GCSECTIONS -L. -o $EXE'; { 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 augment the default linkerscript, which also requires -T (normally that @@ -740,7 +740,9 @@ var binstr, cmdstr, mapstr, - ltostr : TCmdStr; + ltostr, + rpathstr, + sanitizerLibraryDir: TCmdStr; success : boolean; DynLinkStr : ansistring; GCSectionsStr, @@ -757,6 +759,7 @@ begin DynLinkStr:=''; mapstr:=''; ltostr:=''; + rpathstr:=''; if (cs_link_staticflag in current_settings.globalswitches) then StaticStr:='-static'; if (cs_link_strip in current_settings.globalswitches) and @@ -786,6 +789,11 @@ begin ltostr:='-plugin '+maybequoted(utilsdirectory+'/../lib/LLVMgold.so '); end; + if AddSanitizerLibrariesAndGetSearchDir('linux',sanitizerLibraryDir) then + begin + rpathstr:='-rpath '+maybequoted(sanitizerLibraryDir); + end; + { Write used files and libraries } WriteResponseFile(false); @@ -800,6 +808,7 @@ begin Replace(cmdstr,'$DYNLINK',DynLinkStr); Replace(cmdstr,'$MAP',mapstr); Replace(cmdstr,'$LTO',ltostr); + Replace(cmdstr,'$RPATH',rpathstr); { create dynamic symbol table? } if HasExports then @@ -853,7 +862,9 @@ var binstr, cmdstr, mapstr, - ltostr : TCmdStr; + ltostr, + rpathstr, + sanitizerLibraryDir: TCmdStr; success : boolean; begin MakeSharedLibrary:=false; @@ -887,7 +898,12 @@ begin ltostr:='-plugin '+maybequoted(utilsdirectory+'/../lib/LLVMgold.so '); end; -{ Call linker } + if AddSanitizerLibrariesAndGetSearchDir('linux',sanitizerLibraryDir) then + begin + rpathstr:='-rpath '+maybequoted(sanitizerLibraryDir) + end; + + { Call linker } SplitBinCmd(Info.DllCmd[1],binstr,cmdstr); Replace(cmdstr,'$EXE',maybequoted(current_module.sharedlibfilename)); Replace(cmdstr,'$OPT',Info.ExtraOptions); @@ -897,6 +913,7 @@ begin Replace(cmdstr,'$SONAME',SoNameStr); Replace(cmdstr,'$MAP',mapstr); Replace(cmdstr,'$LTO',ltostr); + Replace(cmdstr,'$RPATH',rpathstr); Replace(cmdstr,'$GCSECTIONS',GCSectionsStr); success:=DoExec(FindUtil(utilsprefix+binstr),cmdstr,true,false);