Author: aokblast
Date: 2026-07-13T12:43:55Z
New Revision: fff064e93d144a6499efcf091e1f226d144e514f

URL: 
https://github.com/llvm/llvm-project/commit/fff064e93d144a6499efcf091e1f226d144e514f
DIFF: 
https://github.com/llvm/llvm-project/commit/fff064e93d144a6499efcf091e1f226d144e514f.diff

LOG: [Clang][Interpreter] Enable PIC to avoid 32-bit relocation overflows 
(#201286)

In clang-repl, R_*_32 relocations are emitted as direct 32-bit
PC-relative references. A conventional static linker can resolve
references to external symbols through the GOT or copy relocations, but
clang-repl directly mmaps shared objects into memory, bypassing the
static linker.

As a result, these relocations may exceed their 32-bit range if a
referenced symbol is located more than 2 GB away from the JIT
allocation, which can occur on FreeBSD.

Enable PIC to force accesses to external data through the GOT, avoiding
the generation of direct 32-bit relocations that are subject to the 2 GB
addressing limit.

Added: 
    clang/test/Interpreter/pch-pic-mismatch.cpp
    clang/test/Interpreter/pcm-pic-mismatch.cpp

Modified: 
    clang/lib/Interpreter/Interpreter.cpp
    clang/test/Interpreter/cxx20-modules.cppm
    clang/test/Interpreter/execute-pch.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/Interpreter/Interpreter.cpp 
b/clang/lib/Interpreter/Interpreter.cpp
index 7586c26235449..933a68b50db41 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -21,6 +21,7 @@
 #include "clang/AST/Mangle.h"
 #include "clang/AST/TypeVisitor.h"
 #include "clang/Basic/DiagnosticSema.h"
+#include "clang/Basic/FileManager.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/CodeGen/CodeGenAction.h"
 #include "clang/CodeGen/ObjectFilePCHContainerWriter.h"
@@ -40,6 +41,8 @@
 #include "clang/Options/OptionUtils.h"
 #include "clang/Options/Options.h"
 #include "clang/Sema/Lookup.h"
+#include "clang/Serialization/ASTReader.h"
+#include "clang/Serialization/ModuleCache.h"
 #include "clang/Serialization/ObjectFilePCHContainerReader.h"
 #include "llvm/ExecutionEngine/JITSymbol.h"
 #include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h"
@@ -80,6 +83,51 @@ GetCC1Arguments(DiagnosticsEngine *Diagnostics,
   return &Cmd->getArguments();
 }
 
+// ASTReaderListener that captures the PIC level stored in a serialized AST
+// file (PCH or PCM) so the interpreter can compare it against its own.
+class PICLevelReader : public ASTReaderListener {
+  unsigned &PICLevel;
+
+public:
+  PICLevelReader(unsigned &PICLevel) : PICLevel(PICLevel) {}
+
+  bool ReadLanguageOptions(const LangOptions &LangOpts,
+                           StringRef ModuleFilename, bool Complain,
+                           bool AllowCompatibleDifferences) override {
+    PICLevel = LangOpts.PICLevel;
+    return false;
+  }
+};
+
+// clang-repl always compiles position-independent code (it injects -fPIC), so 
a
+// PCH/PCM that was built with a 
diff erent PIC level is incompatible: mixing the
+// two leads to relocations that may be out of range once the JIT maps code 
more
+// than 2GB away. PICLevel is a "compatible" language option, so the ASTReader
+// would otherwise accept the mismatch silently. Reject it up front.
+//
+// The file is probed with its own FileManager and ModuleCache (sharing only 
the
+// VFS) so this read leaves the CompilerInstance's state untouched for the real
+// load performed later by ExecuteAction().
+static llvm::Error checkASTFilePICLevel(CompilerInstance &Clang,
+                                        StringRef Filename) {
+  llvm::IntrusiveRefCntPtr<FileManager> FileMgr(new FileManager(
+      Clang.getFileSystemOpts(), Clang.getVirtualFileSystemPtr()));
+  std::shared_ptr<ModuleCache> ModCache = createCrossProcessModuleCache();
+  unsigned ASTPICLevel = 0;
+  PICLevelReader Reader(ASTPICLevel);
+  if (!ASTReader::readASTFileControlBlock(
+          Filename, *FileMgr, *ModCache, Clang.getPCHContainerReader(),
+          /*FindModuleFileExtensions=*/false, Reader,
+          /*ValidateDiagnosticOptions=*/false) &&
+      ASTPICLevel != Clang.getLangOpts().PICLevel)
+    return llvm::createStringError(
+        llvm::errc::not_supported,
+        "AST file '%s' was built with PIC level %u, which is incompatible "
+        "with clang-repl's PIC level %u",
+        Filename.str().c_str(), ASTPICLevel, Clang.getLangOpts().PICLevel);
+  return llvm::Error::success();
+}
+
 static llvm::Expected<std::unique_ptr<CompilerInstance>>
 CreateCI(const llvm::opt::ArgStringList &Argv) {
   std::unique_ptr<CompilerInstance> Clang(new CompilerInstance());
@@ -135,6 +183,25 @@ CreateCI(const llvm::opt::ArgStringList &Argv) {
 
   Clang->getFrontendOpts().DisableFree = false;
   Clang->getCodeGenOpts().DisableFree = false;
+
+  // Reject any precompiled input (PCH or PCM) built with a PIC level that
+  // 
diff ers from clang-repl's own, before any Interpreter/FrontendAction is
+  // constructed. See checkASTFilePICLevel for the rationale.
+  StringRef PCHInclude = Clang->getPreprocessorOpts().ImplicitPCHInclude;
+  if (!PCHInclude.empty())
+    if (llvm::Error Err = checkASTFilePICLevel(*Clang, PCHInclude))
+      return std::move(Err);
+
+  // Explicitly loaded modules: -fmodule-file=<path> and
+  // -fmodule-file=<name>=<path>.
+  for (StringRef ModuleFile : Clang->getFrontendOpts().ModuleFiles)
+    if (llvm::Error Err = checkASTFilePICLevel(*Clang, ModuleFile))
+      return std::move(Err);
+  for (const auto &NameAndFile :
+       Clang->getHeaderSearchOpts().PrebuiltModuleFiles)
+    if (llvm::Error Err = checkASTFilePICLevel(*Clang, NameAndFile.second))
+      return std::move(Err);
+
   return std::move(Clang);
 }
 
@@ -153,6 +220,17 @@ IncrementalCompilerBuilder::create(std::string TT,
 
   ClangArgv.insert(ClangArgv.begin(), MainExecutableName.c_str());
 
+  // Compile as position-independent code. This prevents the frontend from
+  // marking external symbols (e.g. C++ type-info such as _ZTIPKc used for
+  // exception handling) as dso_local and emitting direct PC-relative
+  // references. JITLink can place the GOT entry near the JIT'd code, keeping
+  // the relocation in range. Without -fPIC, a direct Delta32 relocation to a
+  // host symbol may be out of range when the JIT memory is mapped more than
+  // 2GB away (as on FreeBSD), breaking tests such as
+  // Interpreter/simple-exception.cpp. Insert before user arguments so it can
+  // still be overridden.
+  ClangArgv.insert(ClangArgv.begin() + 1, "-fPIC");
+
   // Prepending -c to force the driver to do something if no action was
   // specified. By prepending we allow users to override the default
   // action and use other actions in incremental mode.
@@ -264,6 +342,7 @@ Interpreter::Interpreter(std::unique_ptr<CompilerInstance> 
Instance,
 
   if (ErrOut)
     return;
+
   CI->ExecuteAction(*Act);
 
   IncrParser =

diff  --git a/clang/test/Interpreter/cxx20-modules.cppm 
b/clang/test/Interpreter/cxx20-modules.cppm
index aa52dabce89b6..2dd581af834f9 100644
--- a/clang/test/Interpreter/cxx20-modules.cppm
+++ b/clang/test/Interpreter/cxx20-modules.cppm
@@ -4,7 +4,9 @@
 // RUN: mkdir -p %t
 // RUN: split-file %s %t
 //
-// RUN: %clang -std=c++20 %t/mod.cppm --precompile \
+// clang-repl compiles position-independent code, so the module must be
+// precompiled with a matching PIC level (see pcm-pic-mismatch.cpp).
+// RUN: %clang -fPIC -std=c++20 %t/mod.cppm --precompile \
 // RUN:     -o %t/mod.pcm --target=x86_64-linux-gnu
 // RUN: %clang -fPIC %t/mod.pcm -c -o %t/mod.o --target=x86_64-linux-gnu
 // RUN: %clang -nostdlib -fPIC -shared %t/mod.o -o %t/libmod.so 
--target=x86_64-linux-gnu

diff  --git a/clang/test/Interpreter/execute-pch.cpp 
b/clang/test/Interpreter/execute-pch.cpp
index 8041ee6ac966d..d7b69989fc2bc 100644
--- a/clang/test/Interpreter/execute-pch.cpp
+++ b/clang/test/Interpreter/execute-pch.cpp
@@ -5,7 +5,12 @@
 // RUN: mkdir -p %t
 // RUN: split-file %s %t
 //
-// RUN: %clang -fmax-type-align=16 -Xclang -fdeprecated-macro 
-fno-stack-protector -Xclang -fwrapv -Xclang -fblocks -Xclang 
-fskip-odr-check-in-gmf -fexceptions -fcxx-exceptions -fgnuc-version=0 -target 
%host-jit-triple -Xclang -fblocks -Xclang -fmax-type-align=8 -Xclang 
-fincremental-extensions -Xclang -emit-pch -x c++-header -o %t/include.pch 
%t/include.hpp
+// RUN: %if system-windows %{ \
+// RUN: %clang -fmax-type-align=16 -Xclang -fdeprecated-macro 
-fno-stack-protector -Xclang -fwrapv -Xclang -fblocks -Xclang 
-fskip-odr-check-in-gmf -fexceptions -fcxx-exceptions -fgnuc-version=0 -target 
%host-jit-triple -Xclang -fblocks -Xclang -fmax-type-align=8 -Xclang 
-fincremental-extensions -Xclang -emit-pch -x c++-header -o %t/include.pch 
%t/include.hpp \
+// RUN: %} \
+// RUN: %else %{ \
+// RUN: %clang -fPIC -fmax-type-align=16 -Xclang -fdeprecated-macro 
-fno-stack-protector -Xclang -fwrapv -Xclang -fblocks -Xclang 
-fskip-odr-check-in-gmf -fexceptions -fcxx-exceptions -fgnuc-version=0 -target 
%host-jit-triple -Xclang -fblocks -Xclang -fmax-type-align=8 -Xclang 
-fincremental-extensions -Xclang -emit-pch -x c++-header -o %t/include.pch 
%t/include.hpp \
+// RUN: %}
 //
 // RUN: cat %t/main.cpp \
 // RUN:     | clang-repl -Xcc -fgnuc-version=0 -Xcc -fno-stack-protector -Xcc 
-fwrapv -Xcc -fblocks -Xcc -fskip-odr-check-in-gmf -Xcc -fmax-type-align=8 -Xcc 
-include-pch -Xcc %t/include.pch \

diff  --git a/clang/test/Interpreter/pch-pic-mismatch.cpp 
b/clang/test/Interpreter/pch-pic-mismatch.cpp
new file mode 100644
index 0000000000000..0a0a79fbee079
--- /dev/null
+++ b/clang/test/Interpreter/pch-pic-mismatch.cpp
@@ -0,0 +1,28 @@
+// REQUIRES: host-supports-jit
+// UNSUPPORTED: system-aix, system-windows
+//
+// clang-repl compiles position-independent code (it injects -fPIC), so a PCH
+// built with a 
diff erent PIC level is incompatible and must be rejected instead
+// of silently accepted as a "compatible" language-option 
diff erence.
+//
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+//
+// RUN: %clang -fno-pic -Xclang -fincremental-extensions -target 
%host-jit-triple \
+// RUN:     -Xclang -emit-pch -x c++-header -o %t/include.pch %t/include.hpp
+//
+// RUN: cat %t/main.cpp \
+// RUN:     | not clang-repl -Xcc -include-pch -Xcc %t/include.pch 2>&1 \
+// RUN:     | FileCheck %s
+
+//--- include.hpp
+
+int f_pch() { return 5; }
+
+//--- main.cpp
+
+extern "C" int printf(const char *, ...);
+printf("f_pch = %d\n", f_pch());
+
+// CHECK: incompatible with clang-repl's PIC level

diff  --git a/clang/test/Interpreter/pcm-pic-mismatch.cpp 
b/clang/test/Interpreter/pcm-pic-mismatch.cpp
new file mode 100644
index 0000000000000..37c7a28051be1
--- /dev/null
+++ b/clang/test/Interpreter/pcm-pic-mismatch.cpp
@@ -0,0 +1,24 @@
+// REQUIRES: host-supports-jit, x86_64-linux
+//
+// clang-repl compiles position-independent code (it injects -fPIC), so a PCM
+// (Clang module) built with a 
diff erent PIC level is incompatible and must be
+// rejected instead of silently accepted as a "compatible" language-option
+// 
diff erence.
+//
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+//
+// RUN: %clang -fno-pic -std=c++20 %t/mod.cppm --precompile \
+// RUN:     -o %t/mod.pcm --target=x86_64-linux-gnu
+//
+// RUN: echo '// empty' \
+// RUN:     | not clang-repl -Xcc=-std=c++20 -Xcc=-fmodule-file=M=%t/mod.pcm \
+// RUN:           -Xcc=--target=x86_64-linux-gnu 2>&1 \
+// RUN:     | FileCheck %s
+
+//--- mod.cppm
+export module M;
+export const char *Hello() { return "Hello Interpreter for Modules!"; }
+
+// CHECK: incompatible with clang-repl's PIC level


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to