[clang] fff064e - [Clang][Interpreter] Enable PIC to avoid 32-bit relocation overflows (#201286)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Jul 13 05:44:00 PDT 2026
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
More information about the cfe-commits
mailing list