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
