Browse Source

llvm: generalised sanitizer linking support

Also enabled automatic detection of sanitizer library + setting rpath to Linux
Jonas Maebe 3 years ago
parent
commit
7ef33cf4b2
3 changed files with 93 additions and 37 deletions
  1. 60 7
      compiler/link.pas
  2. 11 25
      compiler/systems/t_darwin.pas
  3. 22 5
      compiler/systems/t_linux.pas

+ 60 - 7
compiler/link.pas

@@ -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);
-                      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;
                 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;
 
 
 
 

+ 11 - 25
compiler/systems/t_darwin.pas

@@ -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);
-          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
         end
       else
       else
         begin
         begin
@@ -624,8 +610,7 @@ implementation
       extdbgcmdstr,
       extdbgcmdstr,
       linkfiles,
       linkfiles,
       GCSectionsStr,
       GCSectionsStr,
-      sanitizerLibraryDir,
-      asanLibraryName: TCmdStr;
+      sanitizerLibraryDir: 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);
-          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
         end
       else
       else
         begin
         begin

+ 22 - 5
compiler/systems/t_linux.pas

@@ -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';
-     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 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);