/// \brief Record code for the module map file that was used to build this
/// AST file.
- MODULE_MAP_FILE = 14
+ MODULE_MAP_FILE = 14,
+
+ /// \brief Record code for the signature that identifiers this AST file.
+ SIGNATURE = 15
};
/// \brief Record types that occur within the input-files block
SourceLocation ImportLoc, ModuleFile *ImportedBy,
SmallVectorImpl<ImportedModule> &Loaded,
off_t ExpectedSize, time_t ExpectedModTime,
+ serialization::ASTFileSignature ExpectedSignature,
unsigned ClientLoadCapabilities);
ASTReadResult ReadControlBlock(ModuleFile &F,
SmallVectorImpl<ImportedModule> &Loaded,
bool isNotFound() const { return Val.getInt() == NotFound; }
};
+typedef unsigned ASTFileSignature;
+
/// \brief Information about a module that has been loaded by the ASTReader.
///
/// Each instance of the Module class corresponds to a single AST file, which
/// \brief The file entry for the module file.
const FileEntry *File;
+ /// \brief The signature of the module file, which may be used along with size
+ /// and modification time to identify this particular file.
+ ASTFileSignature Signature;
+
/// \brief Whether this module has been directly imported by the
/// user.
bool DirectlyImported;
/// \param ExpectedModTime The expected modification time of the module
/// file, used for validation. This will be zero if unknown.
///
+ /// \param ExpectedSignature The expected signature of the module file, used
+ /// for validation. This will be zero if unknown.
+ ///
+ /// \param ReadSignature Reads the signature from an AST file without actually
+ /// loading it.
+ ///
/// \param Module A pointer to the module file if the module was successfully
/// loaded.
///
SourceLocation ImportLoc,
ModuleFile *ImportedBy, unsigned Generation,
off_t ExpectedSize, time_t ExpectedModTime,
+ ASTFileSignature ExpectedSignature,
+ std::function<ASTFileSignature(llvm::BitstreamReader &)>
+ ReadSignature,
ModuleFile *&Module,
std::string &ErrorStr);
break;
}
+ case SIGNATURE:
+ assert((!F.Signature || F.Signature == Record[0]) && "signature changed");
+ F.Signature = Record[0];
+ break;
+
case IMPORTS: {
// Load each of the imported PCH files.
unsigned Idx = 0, N = Record.size();
SourceLocation::getFromRawEncoding(Record[Idx++]);
off_t StoredSize = (off_t)Record[Idx++];
time_t StoredModTime = (time_t)Record[Idx++];
+ ASTFileSignature StoredSignature = Record[Idx++];
unsigned Length = Record[Idx++];
SmallString<128> ImportedFile(Record.begin() + Idx,
Record.begin() + Idx + Length);
// Load the AST file.
switch(ReadASTCore(ImportedFile, ImportedKind, ImportLoc, &F, Loaded,
- StoredSize, StoredModTime,
+ StoredSize, StoredModTime, StoredSignature,
ClientLoadCapabilities)) {
case Failure: return Failure;
// If we have to ignore the dependency, we'll have to ignore this too.
SmallVector<ImportedModule, 4> Loaded;
switch(ASTReadResult ReadResult = ReadASTCore(FileName, Type, ImportLoc,
/*ImportedBy=*/nullptr, Loaded,
- 0, 0,
+ 0, 0, 0,
ClientLoadCapabilities)) {
case Failure:
case Missing:
return Success;
}
+static ASTFileSignature readASTFileSignature(llvm::BitstreamReader &StreamFile);
+
ASTReader::ASTReadResult
ASTReader::ReadASTCore(StringRef FileName,
ModuleKind Type,
ModuleFile *ImportedBy,
SmallVectorImpl<ImportedModule> &Loaded,
off_t ExpectedSize, time_t ExpectedModTime,
+ ASTFileSignature ExpectedSignature,
unsigned ClientLoadCapabilities) {
ModuleFile *M;
std::string ErrorStr;
ModuleManager::AddModuleResult AddResult
= ModuleMgr.addModule(FileName, Type, ImportLoc, ImportedBy,
getGeneration(), ExpectedSize, ExpectedModTime,
+ ExpectedSignature, readASTFileSignature,
M, ErrorStr);
switch (AddResult) {
}
}
+static ASTFileSignature readASTFileSignature(llvm::BitstreamReader &StreamFile){
+ BitstreamCursor Stream(StreamFile);
+ if (Stream.Read(8) != 'C' ||
+ Stream.Read(8) != 'P' ||
+ Stream.Read(8) != 'C' ||
+ Stream.Read(8) != 'H') {
+ return 0;
+ }
+
+ // Scan for the CONTROL_BLOCK_ID block.
+ if (SkipCursorToBlock(Stream, CONTROL_BLOCK_ID))
+ return 0;
+
+ // Scan for SIGNATURE inside the control block.
+ ASTReader::RecordData Record;
+ while (1) {
+ llvm::BitstreamEntry Entry = Stream.advanceSkippingSubblocks();
+ if (Entry.Kind == llvm::BitstreamEntry::EndBlock ||
+ Entry.Kind != llvm::BitstreamEntry::Record)
+ return 0;
+
+ Record.clear();
+ StringRef Blob;
+ if (SIGNATURE == Stream.readRecord(Entry.ID, Record, &Blob))
+ return Record[0];
+ }
+}
+
/// \brief Retrieve the name of the original source file name
/// directly from the AST file, without actually loading the AST
/// file.
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/OnDiskHashTable.h"
#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
#include <algorithm>
#include <cstdio>
#include <string.h>
// Control Block.
BLOCK(CONTROL_BLOCK);
RECORD(METADATA);
+ RECORD(SIGNATURE);
RECORD(MODULE_NAME);
RECORD(MODULE_MAP_FILE);
RECORD(IMPORTS);
return Filename + Pos;
}
+static ASTFileSignature getSignature() {
+ while (1) {
+ if (ASTFileSignature S = llvm::sys::Process::GetRandomNumber())
+ return S;
+ // Rely on GetRandomNumber to eventually return non-zero...
+ }
+}
+
/// \brief Write the control block.
void ASTWriter::WriteControlBlock(Preprocessor &PP, ASTContext &Context,
StringRef isysroot,
Stream.EmitRecordWithBlob(MetadataAbbrevCode, Record,
getClangFullRepositoryVersion());
+ // Signature
+ Record.clear();
+ Record.push_back(getSignature());
+ Stream.EmitRecord(SIGNATURE, Record);
+
// Module name
if (WritingModule) {
BitCodeAbbrev *Abbrev = new BitCodeAbbrev();
AddSourceLocation((*M)->ImportLoc, Record);
Record.push_back((*M)->File->getSize());
Record.push_back((*M)->File->getModificationTime());
+ Record.push_back((*M)->Signature);
const std::string &FileName = (*M)->FileName;
Record.push_back(FileName.size());
Record.append(FileName.begin(), FileName.end());
off_t StoredSize = (off_t)Record[Idx++];
time_t StoredModTime = (time_t)Record[Idx++];
+ // Skip the stored signature.
+ // FIXME: we could read the signature out of the import and validate it.
+ Idx++;
+
// Retrieve the imported file name.
unsigned Length = Record[Idx++];
SmallString<128> ImportedFile(Record.begin() + Idx,
using namespace reader;
ModuleFile::ModuleFile(ModuleKind Kind, unsigned Generation)
- : Kind(Kind), File(nullptr), DirectlyImported(false),
+ : Kind(Kind), File(nullptr), Signature(0), DirectlyImported(false),
Generation(Generation), SizeInBits(0),
LocalNumSLocEntries(0), SLocEntryBaseID(0),
SLocEntryBaseOffset(0), SLocEntryOffsets(nullptr),
SourceLocation ImportLoc, ModuleFile *ImportedBy,
unsigned Generation,
off_t ExpectedSize, time_t ExpectedModTime,
+ ASTFileSignature ExpectedSignature,
+ std::function<ASTFileSignature(llvm::BitstreamReader &)>
+ ReadSignature,
ModuleFile *&Module,
std::string &ErrorStr) {
Module = nullptr;
// Initialize the stream
New->StreamFile.init((const unsigned char *)New->Buffer->getBufferStart(),
(const unsigned char *)New->Buffer->getBufferEnd());
+
+ if (ExpectedSignature) {
+ New->Signature = ReadSignature(New->StreamFile);
+ if (New->Signature != ExpectedSignature) {
+ ErrorStr = New->Signature ? "signature mismatch"
+ : "could not read module signature";
+
+ // Remove the module file immediately, since removeModules might try to
+ // invalidate the file cache for Entry, and that is not safe if this
+ // module is *itself* up to date, but has an out-of-date importer.
+ Modules.erase(Entry);
+ Chain.pop_back();
+ return OutOfDate;
+ }
+ }
}
if (ImportedBy) {
--- /dev/null
+// REQUIRES: shell
+// RUN: rm -rf %t
+
+// Build Module and set its timestamp
+// RUN: echo '@import Module;' | %clang_cc1 -fmodules -fmodules-cache-path=%t -fdisable-module-hash -fsyntax-only -F %S/Inputs -x objective-c -
+// RUN: touch -m -a -t 201101010000 %t/Module.pcm
+// RUN: cp %t/Module.pcm %t/Module.pcm.saved
+// RUN: wc -c %t/Module.pcm > %t/Module.size.saved
+
+// Build DependsOnModule
+// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -fdisable-module-hash -fsyntax-only -F %S/Inputs %s
+// RUN: diff %t/Module.pcm %t/Module.pcm.saved
+// RUN: cp %t/DependsOnModule.pcm %t/DependsOnModule.pcm.saved
+
+// Rebuild Module, reset its timestamp, and verify its size hasn't changed
+// RUN: rm %t/Module.pcm
+// RUN: echo '@import Module;' | %clang_cc1 -fmodules -fmodules-cache-path=%t -fdisable-module-hash -fsyntax-only -F %S/Inputs -x objective-c -
+// RUN: touch -m -a -t 201101010000 %t/Module.pcm
+// RUN: wc -c %t/Module.pcm > %t/Module.size
+// RUN: diff %t/Module.size %t/Module.size.saved
+// RUN: cp %t/Module.pcm %t/Module.pcm.saved.2
+
+// But the signature at least is expected to change, so we rebuild DependsOnModule.
+// NOTE: if we change how the signature is created, this test may need updating.
+// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -fdisable-module-hash -fsyntax-only -F %S/Inputs %s
+// RUN: diff %t/Module.pcm %t/Module.pcm.saved.2
+// RUN: not diff %t/DependsOnModule.pcm %t/DependsOnModule.pcm.saved
+
+@import DependsOnModule;