Re-land "Fix Bug 30978 by emitting cv file checksums."

This reverts r313431 and brings back r313374 with a fix to write
checksums as binary data and not ASCII hex strings.

llvm-svn: 313657
This commit is contained in:
Reid Kleckner
2017-09-19 18:14:45 +00:00
parent 0a84b1ac80
commit 26fa1bf4da
16 changed files with 305 additions and 81 deletions

View File

@@ -473,10 +473,12 @@ class DIFile : public DIScope {
friend class MDNode; friend class MDNode;
public: public:
// These values must be explictly set, as they end up in the final object
// file.
enum ChecksumKind { enum ChecksumKind {
CSK_None, CSK_None = 0,
CSK_MD5, CSK_MD5 = 1,
CSK_SHA1, CSK_SHA1 = 2,
CSK_Last = CSK_SHA1 // Should be last enumeration. CSK_Last = CSK_SHA1 // Should be last enumeration.
}; };
@@ -510,7 +512,7 @@ public:
ChecksumKind CSK = CSK_None, ChecksumKind CSK = CSK_None,
StringRef CS = StringRef()), StringRef CS = StringRef()),
(Filename, Directory, CSK, CS)) (Filename, Directory, CSK, CS))
DEFINE_MDNODE_GET(DIFile, (MDString *Filename, MDString *Directory, DEFINE_MDNODE_GET(DIFile, (MDString * Filename, MDString *Directory,
ChecksumKind CSK = CSK_None, ChecksumKind CSK = CSK_None,
MDString *CS = nullptr), MDString *CS = nullptr),
(Filename, Directory, CSK, CS)) (Filename, Directory, CSK, CS))

View File

@@ -161,8 +161,8 @@ public:
~CodeViewContext(); ~CodeViewContext();
bool isValidFileNumber(unsigned FileNumber) const; bool isValidFileNumber(unsigned FileNumber) const;
bool addFile(unsigned FileNumber, StringRef Filename); bool addFile(MCStreamer &OS, unsigned FileNumber, StringRef Filename,
ArrayRef<StringRef> getFilenames() { return Filenames; } ArrayRef<uint8_t> ChecksumBytes, uint8_t ChecksumKind);
/// Records the function id of a normal function. Returns false if the /// Records the function id of a normal function. Returns false if the
/// function id has already been used, and true otherwise. /// function id has already been used, and true otherwise.
@@ -273,6 +273,9 @@ public:
/// Emits the file checksum substream. /// Emits the file checksum substream.
void emitFileChecksums(MCObjectStreamer &OS); void emitFileChecksums(MCObjectStreamer &OS);
/// Emits the offset into the checksum table of the given file number.
void emitFileChecksumOffset(MCObjectStreamer &OS, unsigned FileNo);
private: private:
/// The current CodeView line information from the last .cv_loc directive. /// The current CodeView line information from the last .cv_loc directive.
MCCVLoc CurrentCVLoc = MCCVLoc(0, 0, 0, 0, false, true); MCCVLoc CurrentCVLoc = MCCVLoc(0, 0, 0, 0, false, true);
@@ -287,14 +290,31 @@ private:
MCDataFragment *getStringTableFragment(); MCDataFragment *getStringTableFragment();
/// Add something to the string table. /// Add something to the string table. Returns the final string as well as
StringRef addToStringTable(StringRef S); /// offset into the string table.
std::pair<StringRef, unsigned> addToStringTable(StringRef S);
/// Get a string table offset. /// Get a string table offset.
unsigned getStringTableOffset(StringRef S); unsigned getStringTableOffset(StringRef S);
/// An array of absolute paths. Eventually this may include the file checksum. struct FileInfo {
SmallVector<StringRef, 4> Filenames; unsigned StringTableOffset;
// Indicates if this FileInfo corresponds to an actual file, or hasn't been
// set yet.
bool Assigned = false;
uint8_t ChecksumKind;
ArrayRef<uint8_t> Checksum;
// Checksum offset stored as a symbol because it might be requested
// before it has been calculated, so a fixup may be needed.
MCSymbol *ChecksumTableOffset;
};
/// Array storing added file information.
SmallVector<FileInfo, 4> Files;
/// The offset of the first and last .cv_loc directive for a given function /// The offset of the first and last .cv_loc directive for a given function
/// id. /// id.
@@ -305,6 +325,10 @@ private:
/// All known functions and inlined call sites, indexed by function id. /// All known functions and inlined call sites, indexed by function id.
std::vector<MCCVFunctionInfo> Functions; std::vector<MCCVFunctionInfo> Functions;
/// Indicate whether we have already laid out the checksum table addresses or
/// not.
bool ChecksumOffsetsAssigned = false;
}; };
} // end namespace llvm } // end namespace llvm

View File

@@ -140,6 +140,7 @@ public:
StringRef FixedSizePortion) override; StringRef FixedSizePortion) override;
void EmitCVStringTableDirective() override; void EmitCVStringTableDirective() override;
void EmitCVFileChecksumsDirective() override; void EmitCVFileChecksumsDirective() override;
void EmitCVFileChecksumOffsetDirective(unsigned FileNo) override;
void EmitDTPRel32Value(const MCExpr *Value) override; void EmitDTPRel32Value(const MCExpr *Value) override;
void EmitDTPRel64Value(const MCExpr *Value) override; void EmitDTPRel64Value(const MCExpr *Value) override;
void EmitTPRel32Value(const MCExpr *Value) override; void EmitTPRel32Value(const MCExpr *Value) override;

View File

@@ -733,10 +733,12 @@ public:
unsigned Isa, unsigned Discriminator, unsigned Isa, unsigned Discriminator,
StringRef FileName); StringRef FileName);
/// \brief Associate a filename with a specified logical file number. This /// Associate a filename with a specified logical file number, and also
/// implements the '.cv_file 4 "foo.c"' assembler directive. Returns true on /// specify that file's checksum information. This implements the '.cv_file 4
/// success. /// "foo.c"' assembler directive. Returns true on success.
virtual bool EmitCVFileDirective(unsigned FileNo, StringRef Filename); virtual bool EmitCVFileDirective(unsigned FileNo, StringRef Filename,
ArrayRef<uint8_t> Checksum,
unsigned ChecksumKind);
/// \brief Introduces a function id for use with .cv_loc. /// \brief Introduces a function id for use with .cv_loc.
virtual bool EmitCVFuncIdDirective(unsigned FunctionId); virtual bool EmitCVFuncIdDirective(unsigned FunctionId);
@@ -778,6 +780,10 @@ public:
/// \brief This implements the CodeView '.cv_filechecksums' assembler directive. /// \brief This implements the CodeView '.cv_filechecksums' assembler directive.
virtual void EmitCVFileChecksumsDirective() {} virtual void EmitCVFileChecksumsDirective() {}
/// This implements the CodeView '.cv_filechecksumoffset' assembler
/// directive.
virtual void EmitCVFileChecksumOffsetDirective(unsigned FileNo) {}
/// Emit the absolute difference between two symbols. /// Emit the absolute difference between two symbols.
/// ///
/// \pre Offset of \c Hi is greater than the offset \c Lo. /// \pre Offset of \c Hi is greater than the offset \c Lo.

View File

@@ -159,7 +159,14 @@ unsigned CodeViewDebug::maybeRecordFile(const DIFile *F) {
if (Insertion.second) { if (Insertion.second) {
// We have to compute the full filepath and emit a .cv_file directive. // We have to compute the full filepath and emit a .cv_file directive.
StringRef FullPath = getFullFilepath(F); StringRef FullPath = getFullFilepath(F);
bool Success = OS.EmitCVFileDirective(NextId, FullPath); std::string Checksum = fromHex(F->getChecksum());
void *CKMem = OS.getContext().allocate(Checksum.size(), 1);
memcpy(CKMem, Checksum.data(), Checksum.size());
ArrayRef<uint8_t> ChecksumAsBytes(reinterpret_cast<const uint8_t *>(CKMem),
Checksum.size());
DIFile::ChecksumKind ChecksumKind = F->getChecksumKind();
bool Success = OS.EmitCVFileDirective(NextId, FullPath, ChecksumAsBytes,
static_cast<unsigned>(ChecksumKind));
(void)Success; (void)Success;
assert(Success && ".cv_file directive failed"); assert(Success && ".cv_file directive failed");
} }
@@ -681,8 +688,10 @@ void CodeViewDebug::emitInlineeLinesSubsection() {
OS.AddComment("Inlinee lines subsection"); OS.AddComment("Inlinee lines subsection");
MCSymbol *InlineEnd = beginCVSubsection(DebugSubsectionKind::InlineeLines); MCSymbol *InlineEnd = beginCVSubsection(DebugSubsectionKind::InlineeLines);
// We don't provide any extra file info. // We emit the checksum info for files. This is used by debuggers to
// FIXME: Find out if debuggers use this info. // determine if a pdb matches the source before loading it. Visual Studio,
// for instance, will display a warning that the breakpoints are not valid if
// the pdb does not match the source.
OS.AddComment("Inlinee lines signature"); OS.AddComment("Inlinee lines signature");
OS.EmitIntValue(unsigned(InlineeLinesSignature::Normal), 4); OS.EmitIntValue(unsigned(InlineeLinesSignature::Normal), 4);
@@ -695,13 +704,10 @@ void CodeViewDebug::emitInlineeLinesSubsection() {
OS.AddComment("Inlined function " + SP->getName() + " starts at " + OS.AddComment("Inlined function " + SP->getName() + " starts at " +
SP->getFilename() + Twine(':') + Twine(SP->getLine())); SP->getFilename() + Twine(':') + Twine(SP->getLine()));
OS.AddBlankLine(); OS.AddBlankLine();
// The filechecksum table uses 8 byte entries for now, and file ids start at
// 1.
unsigned FileOffset = (FileId - 1) * 8;
OS.AddComment("Type index of inlined function"); OS.AddComment("Type index of inlined function");
OS.EmitIntValue(InlineeIdx.getIndex(), 4); OS.EmitIntValue(InlineeIdx.getIndex(), 4);
OS.AddComment("Offset into filechecksum table"); OS.AddComment("Offset into filechecksum table");
OS.EmitIntValue(FileOffset, 4); OS.EmitCVFileChecksumOffsetDirective(FileId);
OS.AddComment("Starting line number"); OS.AddComment("Starting line number");
OS.EmitIntValue(SP->getLine(), 4); OS.EmitIntValue(SP->getLine(), 4);
} }

View File

@@ -354,6 +354,8 @@ DISubroutineType *DISubroutineType::getImpl(LLVMContext &Context, DIFlags Flags,
DEFINE_GETIMPL_STORE(DISubroutineType, (Flags, CC), Ops); DEFINE_GETIMPL_STORE(DISubroutineType, (Flags, CC), Ops);
} }
// FIXME: Implement this string-enum correspondence with a .def file and macros,
// so that the association is explicit rather than implied.
static const char *ChecksumKindName[DIFile::CSK_Last + 1] = { static const char *ChecksumKindName[DIFile::CSK_Last + 1] = {
"CSK_None", "CSK_None",
"CSK_MD5", "CSK_MD5",

View File

@@ -225,7 +225,9 @@ public:
StringRef FileName) override; StringRef FileName) override;
MCSymbol *getDwarfLineTableSymbol(unsigned CUID) override; MCSymbol *getDwarfLineTableSymbol(unsigned CUID) override;
bool EmitCVFileDirective(unsigned FileNo, StringRef Filename) override; bool EmitCVFileDirective(unsigned FileNo, StringRef Filename,
ArrayRef<uint8_t> Checksum,
unsigned ChecksumKind) override;
bool EmitCVFuncIdDirective(unsigned FuncId) override; bool EmitCVFuncIdDirective(unsigned FuncId) override;
bool EmitCVInlineSiteIdDirective(unsigned FunctionId, unsigned IAFunc, bool EmitCVInlineSiteIdDirective(unsigned FunctionId, unsigned IAFunc,
unsigned IAFile, unsigned IALine, unsigned IAFile, unsigned IALine,
@@ -245,6 +247,7 @@ public:
StringRef FixedSizePortion) override; StringRef FixedSizePortion) override;
void EmitCVStringTableDirective() override; void EmitCVStringTableDirective() override;
void EmitCVFileChecksumsDirective() override; void EmitCVFileChecksumsDirective() override;
void EmitCVFileChecksumOffsetDirective(unsigned FileNo) override;
void EmitIdent(StringRef IdentString) override; void EmitIdent(StringRef IdentString) override;
void EmitCFISections(bool EH, bool Debug) override; void EmitCFISections(bool EH, bool Debug) override;
@@ -1120,13 +1123,25 @@ MCSymbol *MCAsmStreamer::getDwarfLineTableSymbol(unsigned CUID) {
return MCStreamer::getDwarfLineTableSymbol(0); return MCStreamer::getDwarfLineTableSymbol(0);
} }
bool MCAsmStreamer::EmitCVFileDirective(unsigned FileNo, StringRef Filename) { bool MCAsmStreamer::EmitCVFileDirective(unsigned FileNo, StringRef Filename,
if (!getContext().getCVContext().addFile(FileNo, Filename)) ArrayRef<uint8_t> Checksum,
unsigned ChecksumKind) {
if (!getContext().getCVContext().addFile(*this, FileNo, Filename, Checksum,
ChecksumKind))
return false; return false;
OS << "\t.cv_file\t" << FileNo << ' '; OS << "\t.cv_file\t" << FileNo << ' ';
PrintQuotedString(Filename, OS); PrintQuotedString(Filename, OS);
if (!ChecksumKind) {
EmitEOL();
return true;
}
OS << ' ';
PrintQuotedString(toHex(Checksum), OS);
OS << ' ' << ChecksumKind;
EmitEOL(); EmitEOL();
return true; return true;
} }
@@ -1228,6 +1243,11 @@ void MCAsmStreamer::EmitCVFileChecksumsDirective() {
EmitEOL(); EmitEOL();
} }
void MCAsmStreamer::EmitCVFileChecksumOffsetDirective(unsigned FileNo) {
OS << "\t.cv_filechecksumoffset\t" << FileNo;
EmitEOL();
}
void MCAsmStreamer::EmitIdent(StringRef IdentString) { void MCAsmStreamer::EmitIdent(StringRef IdentString) {
assert(MAI->hasIdentDirective() && ".ident directive not supported"); assert(MAI->hasIdentDirective() && ".ident directive not supported");
OS << "\t.ident\t"; OS << "\t.ident\t";

View File

@@ -13,6 +13,7 @@
#include "llvm/MC/MCCodeView.h" #include "llvm/MC/MCCodeView.h"
#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/BinaryFormat/COFF.h" #include "llvm/BinaryFormat/COFF.h"
#include "llvm/DebugInfo/CodeView/CodeView.h" #include "llvm/DebugInfo/CodeView/CodeView.h"
#include "llvm/DebugInfo/CodeView/Line.h" #include "llvm/DebugInfo/CodeView/Line.h"
@@ -39,29 +40,40 @@ CodeViewContext::~CodeViewContext() {
/// for it. /// for it.
bool CodeViewContext::isValidFileNumber(unsigned FileNumber) const { bool CodeViewContext::isValidFileNumber(unsigned FileNumber) const {
unsigned Idx = FileNumber - 1; unsigned Idx = FileNumber - 1;
if (Idx < Filenames.size()) if (Idx < Files.size())
return !Filenames[Idx].empty(); return Files[Idx].Assigned;
return false; return false;
} }
bool CodeViewContext::addFile(unsigned FileNumber, StringRef Filename) { bool CodeViewContext::addFile(MCStreamer &OS, unsigned FileNumber,
StringRef Filename,
ArrayRef<uint8_t> ChecksumBytes,
uint8_t ChecksumKind) {
assert(FileNumber > 0); assert(FileNumber > 0);
Filename = addToStringTable(Filename); auto FilenameOffset = addToStringTable(Filename);
Filename = FilenameOffset.first;
unsigned Idx = FileNumber - 1; unsigned Idx = FileNumber - 1;
if (Idx >= Filenames.size()) if (Idx >= Files.size())
Filenames.resize(Idx + 1); Files.resize(Idx + 1);
if (Filename.empty()) if (Filename.empty())
Filename = "<stdin>"; Filename = "<stdin>";
if (!Filenames[Idx].empty()) if (Files[Idx].Assigned)
return false; return false;
// FIXME: We should store the string table offset of the filename, rather than FilenameOffset = addToStringTable(Filename);
// the filename itself for efficiency. Filename = FilenameOffset.first;
Filename = addToStringTable(Filename); unsigned Offset = FilenameOffset.second;
auto ChecksumOffsetSymbol =
OS.getContext().createTempSymbol("checksum_offset", false);
Files[Idx].StringTableOffset = Offset;
Files[Idx].ChecksumTableOffset = ChecksumOffsetSymbol;
Files[Idx].Assigned = true;
Files[Idx].Checksum = ChecksumBytes;
Files[Idx].ChecksumKind = ChecksumKind;
Filenames[Idx] = Filename;
return true; return true;
} }
@@ -118,17 +130,18 @@ MCDataFragment *CodeViewContext::getStringTableFragment() {
return StrTabFragment; return StrTabFragment;
} }
StringRef CodeViewContext::addToStringTable(StringRef S) { std::pair<StringRef, unsigned> CodeViewContext::addToStringTable(StringRef S) {
SmallVectorImpl<char> &Contents = getStringTableFragment()->getContents(); SmallVectorImpl<char> &Contents = getStringTableFragment()->getContents();
auto Insertion = auto Insertion =
StringTable.insert(std::make_pair(S, unsigned(Contents.size()))); StringTable.insert(std::make_pair(S, unsigned(Contents.size())));
// Return the string from the table, since it is stable. // Return the string from the table, since it is stable.
S = Insertion.first->first(); std::pair<StringRef, unsigned> Ret =
std::make_pair(Insertion.first->first(), Insertion.first->second);
if (Insertion.second) { if (Insertion.second) {
// The string map key is always null terminated. // The string map key is always null terminated.
Contents.append(S.begin(), S.end() + 1); Contents.append(Ret.first.begin(), Ret.first.end() + 1);
} }
return S; return Ret;
} }
unsigned CodeViewContext::getStringTableOffset(StringRef S) { unsigned CodeViewContext::getStringTableOffset(StringRef S) {
@@ -165,7 +178,7 @@ void CodeViewContext::emitStringTable(MCObjectStreamer &OS) {
void CodeViewContext::emitFileChecksums(MCObjectStreamer &OS) { void CodeViewContext::emitFileChecksums(MCObjectStreamer &OS) {
// Do nothing if there are no file checksums. Microsoft's linker rejects empty // Do nothing if there are no file checksums. Microsoft's linker rejects empty
// CodeView substreams. // CodeView substreams.
if (Filenames.empty()) if (Files.empty())
return; return;
MCContext &Ctx = OS.getContext(); MCContext &Ctx = OS.getContext();
@@ -176,17 +189,63 @@ void CodeViewContext::emitFileChecksums(MCObjectStreamer &OS) {
OS.emitAbsoluteSymbolDiff(FileEnd, FileBegin, 4); OS.emitAbsoluteSymbolDiff(FileEnd, FileBegin, 4);
OS.EmitLabel(FileBegin); OS.EmitLabel(FileBegin);
unsigned CurrentOffset = 0;
// Emit an array of FileChecksum entries. We index into this table using the // Emit an array of FileChecksum entries. We index into this table using the
// user-provided file number. Each entry is currently 8 bytes, as we don't // user-provided file number. Each entry may be a variable number of bytes
// emit checksums. // determined by the checksum kind and size.
for (StringRef Filename : Filenames) { for (auto File : Files) {
OS.EmitIntValue(getStringTableOffset(Filename), 4); OS.EmitAssignment(File.ChecksumTableOffset,
// Zero the next two fields and align back to 4 bytes. This indicates that MCConstantExpr::create(CurrentOffset, Ctx));
// no checksum is present. CurrentOffset += 4; // String table offset.
OS.EmitIntValue(0, 4); if (!File.ChecksumKind) {
CurrentOffset +=
4; // One byte each for checksum size and kind, then align to 4 bytes.
} else {
CurrentOffset += 2; // One byte each for checksum size and kind.
CurrentOffset += File.Checksum.size();
CurrentOffset = alignTo(CurrentOffset, 4);
}
OS.EmitIntValue(File.StringTableOffset, 4);
if (!File.ChecksumKind) {
// There is no checksum. Therefore zero the next two fields and align
// back to 4 bytes.
OS.EmitIntValue(0, 4);
continue;
}
OS.EmitIntValue(static_cast<uint8_t>(File.Checksum.size()), 1);
OS.EmitIntValue(File.ChecksumKind, 1);
OS.EmitBytes(toStringRef(File.Checksum));
OS.EmitValueToAlignment(4);
} }
OS.EmitLabel(FileEnd); OS.EmitLabel(FileEnd);
ChecksumOffsetsAssigned = true;
}
// Output checksum table offset of the given file number. It is possible that
// not all files have been registered yet, and so the offset cannot be
// calculated. In this case a symbol representing the offset is emitted, and
// the value of this symbol will be fixed up at a later time.
void CodeViewContext::emitFileChecksumOffset(MCObjectStreamer &OS,
unsigned FileNo) {
unsigned Idx = FileNo - 1;
if (Idx >= Files.size())
Files.resize(Idx + 1);
if (ChecksumOffsetsAssigned) {
OS.EmitSymbolValue(Files[Idx].ChecksumTableOffset, 4);
return;
}
const MCSymbolRefExpr *SRE =
MCSymbolRefExpr::create(Files[Idx].ChecksumTableOffset, OS.getContext());
OS.EmitValueImpl(SRE, 4);
} }
void CodeViewContext::emitLineTableForFunction(MCObjectStreamer &OS, void CodeViewContext::emitLineTableForFunction(MCObjectStreamer &OS,
@@ -219,9 +278,12 @@ void CodeViewContext::emitLineTableForFunction(MCObjectStreamer &OS,
return Loc.getFileNum() != CurFileNum; return Loc.getFileNum() != CurFileNum;
}); });
unsigned EntryCount = FileSegEnd - I; unsigned EntryCount = FileSegEnd - I;
OS.AddComment("Segment for file '" + Twine(Filenames[CurFileNum - 1]) + OS.AddComment(
"' begins"); "Segment for file '" +
OS.EmitIntValue(8 * (CurFileNum - 1), 4); Twine(getStringTableFragment()
->getContents()[Files[CurFileNum - 1].StringTableOffset]) +
"' begins");
OS.EmitCVFileChecksumOffsetDirective(CurFileNum);
OS.EmitIntValue(EntryCount, 4); OS.EmitIntValue(EntryCount, 4);
uint32_t SegmentSize = 12; uint32_t SegmentSize = 12;
SegmentSize += 8 * EntryCount; SegmentSize += 8 * EntryCount;
@@ -401,9 +463,10 @@ void CodeViewContext::encodeInlineLineTable(MCAsmLayout &Layout,
HaveOpenRange = true; HaveOpenRange = true;
if (CurSourceLoc.File != LastSourceLoc.File) { if (CurSourceLoc.File != LastSourceLoc.File) {
// File ids are 1 based, and each file checksum table entry is 8 bytes unsigned FileOffset = static_cast<const MCConstantExpr *>(
// long. See emitFileChecksums above. Files[CurSourceLoc.File - 1]
unsigned FileOffset = 8 * (CurSourceLoc.File - 1); .ChecksumTableOffset->getVariableValue())
->getValue();
compressAnnotation(BinaryAnnotationsOpCode::ChangeFile, Buffer); compressAnnotation(BinaryAnnotationsOpCode::ChangeFile, Buffer);
compressAnnotation(FileOffset, Buffer); compressAnnotation(FileOffset, Buffer);
} }

View File

@@ -426,6 +426,9 @@ void MCObjectStreamer::EmitCVFileChecksumsDirective() {
getContext().getCVContext().emitFileChecksums(*this); getContext().getCVContext().emitFileChecksums(*this);
} }
void MCObjectStreamer::EmitCVFileChecksumOffsetDirective(unsigned FileNo) {
getContext().getCVContext().emitFileChecksumOffset(*this, FileNo);
}
void MCObjectStreamer::EmitBytes(StringRef Data) { void MCObjectStreamer::EmitBytes(StringRef Data) {
MCCVLineEntry::Make(this); MCCVLineEntry::Make(this);

View File

@@ -18,6 +18,7 @@
#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h" #include "llvm/ADT/Twine.h"
@@ -501,6 +502,7 @@ private:
DK_CV_DEF_RANGE, DK_CV_DEF_RANGE,
DK_CV_STRINGTABLE, DK_CV_STRINGTABLE,
DK_CV_FILECHECKSUMS, DK_CV_FILECHECKSUMS,
DK_CV_FILECHECKSUM_OFFSET,
DK_CFI_SECTIONS, DK_CFI_SECTIONS,
DK_CFI_STARTPROC, DK_CFI_STARTPROC,
DK_CFI_ENDPROC, DK_CFI_ENDPROC,
@@ -576,6 +578,7 @@ private:
bool parseDirectiveCVDefRange(); bool parseDirectiveCVDefRange();
bool parseDirectiveCVStringTable(); bool parseDirectiveCVStringTable();
bool parseDirectiveCVFileChecksums(); bool parseDirectiveCVFileChecksums();
bool parseDirectiveCVFileChecksumOffset();
// .cfi directives // .cfi directives
bool parseDirectiveCFIRegister(SMLoc DirectiveLoc); bool parseDirectiveCFIRegister(SMLoc DirectiveLoc);
@@ -2030,6 +2033,8 @@ bool AsmParser::parseStatement(ParseStatementInfo &Info,
return parseDirectiveCVStringTable(); return parseDirectiveCVStringTable();
case DK_CV_FILECHECKSUMS: case DK_CV_FILECHECKSUMS:
return parseDirectiveCVFileChecksums(); return parseDirectiveCVFileChecksums();
case DK_CV_FILECHECKSUM_OFFSET:
return parseDirectiveCVFileChecksumOffset();
case DK_CFI_SECTIONS: case DK_CFI_SECTIONS:
return parseDirectiveCFISections(); return parseDirectiveCFISections();
case DK_CFI_STARTPROC: case DK_CFI_STARTPROC:
@@ -3457,25 +3462,40 @@ bool AsmParser::parseDirectiveStabs() {
} }
/// parseDirectiveCVFile /// parseDirectiveCVFile
/// ::= .cv_file number filename /// ::= .cv_file number filename [checksum] [checksumkind]
bool AsmParser::parseDirectiveCVFile() { bool AsmParser::parseDirectiveCVFile() {
SMLoc FileNumberLoc = getTok().getLoc(); SMLoc FileNumberLoc = getTok().getLoc();
int64_t FileNumber; int64_t FileNumber;
std::string Filename; std::string Filename;
std::string Checksum;
int64_t ChecksumKind = 0;
if (parseIntToken(FileNumber, if (parseIntToken(FileNumber,
"expected file number in '.cv_file' directive") || "expected file number in '.cv_file' directive") ||
check(FileNumber < 1, FileNumberLoc, "file number less than one") || check(FileNumber < 1, FileNumberLoc, "file number less than one") ||
check(getTok().isNot(AsmToken::String), check(getTok().isNot(AsmToken::String),
"unexpected token in '.cv_file' directive") || "unexpected token in '.cv_file' directive") ||
// Usually directory and filename are together, otherwise just parseEscapedString(Filename))
// directory. Allow the strings to have escaped octal character sequence.
parseEscapedString(Filename) ||
parseToken(AsmToken::EndOfStatement,
"unexpected token in '.cv_file' directive"))
return true; return true;
if (!parseOptionalToken(AsmToken::EndOfStatement)) {
if (check(getTok().isNot(AsmToken::String),
"unexpected token in '.cv_file' directive") ||
parseEscapedString(Checksum) ||
parseIntToken(ChecksumKind,
"expected checksum kind in '.cv_file' directive") ||
parseToken(AsmToken::EndOfStatement,
"unexpected token in '.cv_file' directive"))
return true;
}
if (!getStreamer().EmitCVFileDirective(FileNumber, Filename)) Checksum = fromHex(Checksum);
void *CKMem = Ctx.allocate(Checksum.size(), 1);
memcpy(CKMem, Checksum.data(), Checksum.size());
ArrayRef<uint8_t> ChecksumAsBytes(reinterpret_cast<const uint8_t *>(CKMem),
Checksum.size());
if (!getStreamer().EmitCVFileDirective(FileNumber, Filename, ChecksumAsBytes,
static_cast<uint8_t>(ChecksumKind)))
return Error(FileNumberLoc, "file number already allocated"); return Error(FileNumberLoc, "file number already allocated");
return false; return false;
@@ -3754,6 +3774,18 @@ bool AsmParser::parseDirectiveCVFileChecksums() {
return false; return false;
} }
/// parseDirectiveCVFileChecksumOffset
/// ::= .cv_filechecksumoffset fileno
bool AsmParser::parseDirectiveCVFileChecksumOffset() {
int64_t FileNo;
if (parseIntToken(FileNo, "expected identifier in directive"))
return true;
if (parseToken(AsmToken::EndOfStatement, "Expected End of Statement"))
return true;
getStreamer().EmitCVFileChecksumOffsetDirective(FileNo);
return false;
}
/// parseDirectiveCFISections /// parseDirectiveCFISections
/// ::= .cfi_sections section [, section] /// ::= .cfi_sections section [, section]
bool AsmParser::parseDirectiveCFISections() { bool AsmParser::parseDirectiveCFISections() {
@@ -5136,6 +5168,7 @@ void AsmParser::initializeDirectiveKindMap() {
DirectiveKindMap[".cv_def_range"] = DK_CV_DEF_RANGE; DirectiveKindMap[".cv_def_range"] = DK_CV_DEF_RANGE;
DirectiveKindMap[".cv_stringtable"] = DK_CV_STRINGTABLE; DirectiveKindMap[".cv_stringtable"] = DK_CV_STRINGTABLE;
DirectiveKindMap[".cv_filechecksums"] = DK_CV_FILECHECKSUMS; DirectiveKindMap[".cv_filechecksums"] = DK_CV_FILECHECKSUMS;
DirectiveKindMap[".cv_filechecksumoffset"] = DK_CV_FILECHECKSUM_OFFSET;
DirectiveKindMap[".sleb128"] = DK_SLEB128; DirectiveKindMap[".sleb128"] = DK_SLEB128;
DirectiveKindMap[".uleb128"] = DK_ULEB128; DirectiveKindMap[".uleb128"] = DK_ULEB128;
DirectiveKindMap[".cfi_sections"] = DK_CFI_SECTIONS; DirectiveKindMap[".cfi_sections"] = DK_CFI_SECTIONS;

View File

@@ -228,8 +228,11 @@ void MCStreamer::EnsureValidDwarfFrame() {
report_fatal_error("No open frame"); report_fatal_error("No open frame");
} }
bool MCStreamer::EmitCVFileDirective(unsigned FileNo, StringRef Filename) { bool MCStreamer::EmitCVFileDirective(unsigned FileNo, StringRef Filename,
return getContext().getCVContext().addFile(FileNo, Filename); ArrayRef<uint8_t> Checksum,
unsigned ChecksumKind) {
return getContext().getCVContext().addFile(*this, FileNo, Filename, Checksum,
ChecksumKind);
} }
bool MCStreamer::EmitCVFuncIdDirective(unsigned FunctionId) { bool MCStreamer::EmitCVFuncIdDirective(unsigned FunctionId) {

View File

@@ -49,11 +49,11 @@
; ASM: .long 0 ; ASM: .long 0
; ASM: # Inlined function bar starts at t.cpp:8 ; ASM: # Inlined function bar starts at t.cpp:8
; ASM: .long 4098 # Type index of inlined function ; ASM: .long 4098 # Type index of inlined function
; ASM: .long 0 # Offset into filechecksum table ; ASM: .cv_filechecksumoffset 1 # Offset into filechecksum table
; ASM: .long 8 # Starting line number ; ASM: .long 8 # Starting line number
; ASM: # Inlined function foo starts at t.cpp:2 ; ASM: # Inlined function foo starts at t.cpp:2
; ASM: .long 4099 ; ASM: .long 4099
; ASM: .long 0 ; ASM: .cv_filechecksumoffset 1 # Offset into filechecksum table
; ASM: .long 2 ; ASM: .long 2
; ASM: [[inline_end]]: ; ASM: [[inline_end]]:
@@ -72,6 +72,8 @@
; ASM: .short 4430 ; ASM: .short 4430
; ASM: .short 4430 ; ASM: .short 4430
; ASM: .cv_filechecksums
; ASM: .section .debug$T,"dr" ; ASM: .section .debug$T,"dr"
; ASM: .long 4 # Debug section magic ; ASM: .long 4 # Debug section magic
; ASM: # ArgList (0x1000) { ; ASM: # ArgList (0x1000) {

View File

@@ -18,10 +18,10 @@
; X86-LABEL: _f: ; X86-LABEL: _f:
; X86: # BB ; X86: # BB
; X86: .cv_file 1 "D:\\one.c" ; X86: .cv_file 1 "D:\\one.c" "70B51F534D80639D033AE92C6A856AF6" 1
; X86: .cv_loc 0 1 1 0 is_stmt 0 # one.c:1:0 ; X86: .cv_loc 0 1 1 0 is_stmt 0 # one.c:1:0
; X86: calll _g ; X86: calll _g
; X86: .cv_file 2 "D:\\two.c" ; X86: .cv_file 2 "D:\\two.c" "70B51F534D80639D033AE92C6A856AF6" 1
; X86: .cv_loc 0 2 2 0 # two.c:2:0 ; X86: .cv_loc 0 2 2 0 # two.c:2:0
; X86: calll _g ; X86: calll _g
; X86: .cv_loc 0 1 7 0 # one.c:7:0 ; X86: .cv_loc 0 1 7 0 # one.c:7:0
@@ -51,6 +51,22 @@
; OBJ32-NEXT: ProcEnd { ; OBJ32-NEXT: ProcEnd {
; OBJ32: } ; OBJ32: }
; OBJ32-NEXT: ] ; OBJ32-NEXT: ]
; OBJ32: Subsection [
; OBJ32: SubSectionType: FileChecksums (0xF4)
; OBJ32-NEXT: SubSectionSize: 0x30
; OBJ32-NEXT: FileChecksum {
; OBJ32-NEXT: Filename: D:\one.c (0x1)
; OBJ32-NEXT: ChecksumSize: 0x10
; OBJ32-NEXT: ChecksumKind: MD5 (0x1)
; OBJ32-NEXT: ChecksumBytes: (70 B5 1F 53 4D 80 63 9D 03 3A E9 2C 6A 85 6A F6)
; OBJ32-NEXT: }
; OBJ32-NEXT: FileChecksum {
; OBJ32-NEXT: Filename: D:\two.c (0xA)
; OBJ32-NEXT: ChecksumSize: 0x10
; OBJ32-NEXT: ChecksumKind: MD5 (0x1)
; OBJ32-NEXT: ChecksumBytes: (70 B5 1F 53 4D 80 63 9D 03 3A E9 2C 6A 85 6A F6)
; OBJ32-NEXT: }
; OBJ32-NEXT: ]
; OBJ32: FunctionLineTable [ ; OBJ32: FunctionLineTable [
; OBJ32-NEXT: Name: _f ; OBJ32-NEXT: Name: _f
; OBJ32-NEXT: Flags: 0x0 ; OBJ32-NEXT: Flags: 0x0
@@ -88,14 +104,14 @@
; X64-LABEL: f: ; X64-LABEL: f:
; X64-NEXT: .L{{.*}}:{{$}} ; X64-NEXT: .L{{.*}}:{{$}}
; X64: .cv_file 1 "D:\\input.c" ; X64: .cv_file 1 "D:\\input.c" "70B51F534D80639D033AE92C6A856AF6" 1
; X64: .cv_loc 0 1 3 0 is_stmt 0 # input.c:3:0 ; X64: .cv_loc 0 1 3 0 is_stmt 0 # input.c:3:0
; X64: # BB ; X64: # BB
; X64: subq $40, %rsp ; X64: subq $40, %rsp
; X64: .cv_file 2 "D:\\one.c" ; X64: .cv_file 2 "D:\\one.c" "70B51F534D80639D033AE92C6A856AF6" 1
; X64: .cv_loc 0 2 1 0 # one.c:1:0 ; X64: .cv_loc 0 2 1 0 # one.c:1:0
; X64: callq g ; X64: callq g
; X64: .cv_file 3 "D:\\two.c" ; X64: .cv_file 3 "D:\\two.c" "70B51F534D80639D033AE92C6A856AF6" 1
; X64: .cv_loc 0 3 2 0 # two.c:2:0 ; X64: .cv_loc 0 3 2 0 # two.c:2:0
; X64: callq g ; X64: callq g
; X64: .cv_loc 0 2 7 0 # one.c:7:0 ; X64: .cv_loc 0 2 7 0 # one.c:7:0
@@ -123,6 +139,28 @@
; OBJ64-NEXT: ProcEnd { ; OBJ64-NEXT: ProcEnd {
; OBJ64: } ; OBJ64: }
; OBJ64-NEXT: ] ; OBJ64-NEXT: ]
; OBJ64: Subsection [
; OBJ64: SubSectionType: FileChecksums (0xF4)
; OBJ64-NEXT: SubSectionSize: 0x48
; OBJ64-NEXT: FileChecksum {
; OBJ64-NEXT: Filename: D:\input.c (0x1)
; OBJ64-NEXT: ChecksumSize: 0x10
; OBJ64-NEXT: ChecksumKind: MD5 (0x1)
; OBJ64-NEXT: ChecksumBytes: (70 B5 1F 53 4D 80 63 9D 03 3A E9 2C 6A 85 6A F6)
; OBJ64-NEXT: }
; OBJ64-NEXT: FileChecksum {
; OBJ64-NEXT: Filename: D:\one.c (0xC)
; OBJ64-NEXT: ChecksumSize: 0x10
; OBJ64-NEXT: ChecksumKind: MD5 (0x1)
; OBJ64-NEXT: ChecksumBytes: (70 B5 1F 53 4D 80 63 9D 03 3A E9 2C 6A 85 6A F6)
; OBJ64-NEXT: }
; OBJ64-NEXT: FileChecksum {
; OBJ64-NEXT: Filename: D:\two.c (0x15)
; OBJ64-NEXT: ChecksumSize: 0x10
; OBJ64-NEXT: ChecksumKind: MD5 (0x1)
; OBJ64-NEXT: ChecksumBytes: (70 B5 1F 53 4D 80 63 9D 03 3A E9 2C 6A 85 6A F6)
; OBJ64-NEXT: }
; OBJ64-NEXT: ]
; OBJ64: FunctionLineTable [ ; OBJ64: FunctionLineTable [
; OBJ64-NEXT: Name: f ; OBJ64-NEXT: Name: f
; OBJ64-NEXT: Flags: 0x0 ; OBJ64-NEXT: Flags: 0x0
@@ -185,11 +223,11 @@ attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "
!llvm.ident = !{!11} !llvm.ident = !{!11}
!0 = distinct !DICompileUnit(language: DW_LANG_C99, producer: "clang version 3.5 ", isOptimized: false, emissionKind: FullDebug, file: !1, enums: !2, retainedTypes: !2, globals: !2, imports: !2) !0 = distinct !DICompileUnit(language: DW_LANG_C99, producer: "clang version 3.5 ", isOptimized: false, emissionKind: FullDebug, file: !1, enums: !2, retainedTypes: !2, globals: !2, imports: !2)
!1 = !DIFile(filename: "<unknown>", directory: "D:\5C") !1 = !DIFile(filename: "<unknown>", directory: "D:\5C", checksumkind: CSK_MD5, checksum:"70b51f534d80639d033ae92c6a856af6")
!2 = !{} !2 = !{}
!4 = distinct !DISubprogram(name: "f", line: 3, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: false, unit: !0, scopeLine: 3, file: !5, scope: !6, type: !7, variables: !2) !4 = distinct !DISubprogram(name: "f", line: 3, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: false, unit: !0, scopeLine: 3, file: !5, scope: !6, type: !7, variables: !2)
!5 = !DIFile(filename: "input.c", directory: "D:\5C") !5 = !DIFile(filename: "input.c", directory: "D:\5C", checksumkind: CSK_MD5, checksum:"70b51f534d80639d033ae92c6a856af6")
!6 = !DIFile(filename: "input.c", directory: "D:C") !6 = !DIFile(filename: "input.c", directory: "D:C", checksumkind: CSK_MD5, checksum:"70b51f534d80639d033ae92c6a856af6")
!7 = !DISubroutineType(types: !8) !7 = !DISubroutineType(types: !8)
!8 = !{null} !8 = !{null}
!9 = !{i32 2, !"CodeView", i32 1} !9 = !{i32 2, !"CodeView", i32 1}
@@ -197,9 +235,9 @@ attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "
!11 = !{!"clang version 3.5 "} !11 = !{!"clang version 3.5 "}
!12 = !DILocation(line: 1, scope: !13) !12 = !DILocation(line: 1, scope: !13)
!13 = !DILexicalBlockFile(discriminator: 0, file: !14, scope: !4) !13 = !DILexicalBlockFile(discriminator: 0, file: !14, scope: !4)
!14 = !DIFile(filename: "one.c", directory: "D:\5C") !14 = !DIFile(filename: "one.c", directory: "D:\5C", checksumkind: CSK_MD5, checksum:"70b51f534d80639d033ae92c6a856af6")
!15 = !DILocation(line: 2, scope: !16) !15 = !DILocation(line: 2, scope: !16)
!16 = !DILexicalBlockFile(discriminator: 0, file: !17, scope: !4) !16 = !DILexicalBlockFile(discriminator: 0, file: !17, scope: !4)
!17 = !DIFile(filename: "two.c", directory: "D:\5C") !17 = !DIFile(filename: "two.c", directory: "D:\5C", checksumkind: CSK_MD5, checksum:"70b51f534d80639d033ae92c6a856af6")
!18 = !DILocation(line: 7, scope: !13) !18 = !DILocation(line: 7, scope: !13)
!19 = !DILocation(line: 8, scope: !13) !19 = !DILocation(line: 8, scope: !13)

View File

@@ -17,7 +17,7 @@
; X86-LABEL: _f: ; X86-LABEL: _f:
; X86: # BB ; X86: # BB
; X86: .cv_file 1 "D:\\test.c" ; X86: .cv_file 1 "D:\\test.c" "F310AB26998CA831CBDF169E4EECACFA" 1
; X86: .cv_loc 0 1 4 2 is_stmt 0 # test.c:4:2 ; X86: .cv_loc 0 1 4 2 is_stmt 0 # test.c:4:2
; X86: calll _g ; X86: calll _g
; X86: .cv_loc 0 1 5 0 # test.c:5:0 ; X86: .cv_loc 0 1 5 0 # test.c:5:0
@@ -85,6 +85,16 @@
; OBJ32-NEXT: ProcEnd { ; OBJ32-NEXT: ProcEnd {
; OBJ32: } ; OBJ32: }
; OBJ32-NEXT: ] ; OBJ32-NEXT: ]
; OBJ32: Subsection [
; OBJ32: SubSectionType: FileChecksums (0xF4)
; OBJ32-NEXT: SubSectionSize: 0x18
; OBJ32-NEXT: FileChecksum {
; OBJ32-NEXT: Filename: D:\test.c (0x1)
; OBJ32-NEXT: ChecksumSize: 0x10
; OBJ32-NEXT: ChecksumKind: MD5 (0x1)
; OBJ32-NEXT: ChecksumBytes: (F3 10 AB 26 99 8C A8 31 CB DF 16 9E 4E EC AC FA)
; OBJ32-NEXT: }
; OBJ32-NEXT: ]
; OBJ32: FunctionLineTable [ ; OBJ32: FunctionLineTable [
; OBJ32-NEXT: Name: _f ; OBJ32-NEXT: Name: _f
; OBJ32-NEXT: Flags: 0x1 ; OBJ32-NEXT: Flags: 0x1
@@ -110,7 +120,7 @@
; X64-LABEL: f: ; X64-LABEL: f:
; X64-NEXT: .L{{.*}}:{{$}} ; X64-NEXT: .L{{.*}}:{{$}}
; X64: .cv_file 1 "D:\\test.c" ; X64: .cv_file 1 "D:\\test.c" "F310AB26998CA831CBDF169E4EECACFA" 1
; X64: .cv_loc 0 1 3 0 is_stmt 0 # test.c:3:0 ; X64: .cv_loc 0 1 3 0 is_stmt 0 # test.c:3:0
; X64: # BB ; X64: # BB
; X64: subq $40, %rsp ; X64: subq $40, %rsp
@@ -182,6 +192,16 @@
; OBJ64-NEXT: ProcEnd { ; OBJ64-NEXT: ProcEnd {
; OBJ64: } ; OBJ64: }
; OBJ64-NEXT: ] ; OBJ64-NEXT: ]
; OBJ64: Subsection [
; OBJ64: SubSectionType: FileChecksums (0xF4)
; OBJ64-NEXT: SubSectionSize: 0x18
; OBJ64-NEXT: FileChecksum {
; OBJ64-NEXT: Filename: D:\test.c (0x1)
; OBJ64-NEXT: ChecksumSize: 0x10
; OBJ64-NEXT: ChecksumKind: MD5 (0x1)
; OBJ64-NEXT: ChecksumBytes: (F3 10 AB 26 99 8C A8 31 CB DF 16 9E 4E EC AC FA)
; OBJ64-NEXT: }
; OBJ64-NEXT: ]
; OBJ64: FunctionLineTable [ ; OBJ64: FunctionLineTable [
; OBJ64-NEXT: Name: f ; OBJ64-NEXT: Name: f
; OBJ64-NEXT: Flags: 0x1 ; OBJ64-NEXT: Flags: 0x1
@@ -232,8 +252,8 @@ attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "
!1 = !DIFile(filename: "<unknown>", directory: "D:\5C") !1 = !DIFile(filename: "<unknown>", directory: "D:\5C")
!2 = !{} !2 = !{}
!4 = distinct !DISubprogram(name: "f", line: 3, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: false, unit: !0, scopeLine: 3, file: !5, scope: !6, type: !7, variables: !2) !4 = distinct !DISubprogram(name: "f", line: 3, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: false, unit: !0, scopeLine: 3, file: !5, scope: !6, type: !7, variables: !2)
!5 = !DIFile(filename: "test.c", directory: "D:\5C") !5 = !DIFile(filename: "test.c", directory: "D:\5C", checksumkind: CSK_MD5, checksum: "f310ab26998ca831cbdf169e4eecacfa")
!6 = !DIFile(filename: "test.c", directory: "D:C") !6 = !DIFile(filename: "test.c", directory: "D:C", checksumkind: CSK_MD5, checksum: "f310ab26998ca831cbdf169e4eecacfa")
!7 = !DISubroutineType(types: !8) !7 = !DISubroutineType(types: !8)
!8 = !{null} !8 = !{null}
!9 = !{i32 2, !"CodeView", i32 1} !9 = !{i32 2, !"CodeView", i32 1}

View File

@@ -103,7 +103,7 @@ attributes #0 = { noinline nounwind optnone "correctly-rounded-divide-sqrt-fp-ma
; CHECK: SubSectionType: FileChecksums (0xF4) ; CHECK: SubSectionType: FileChecksums (0xF4)
; CHECK: FileChecksum { ; CHECK: FileChecksum {
; CHECK: ChecksumSize: 0x0 ; CHECK: ChecksumSize: 0x0
; CHECK: ChecksumKind: None (0x0) ; CHECK: ChecksumKind: MD5 (0x1)
; CHECK: ChecksumBytes: () ; CHECK: ChecksumBytes: ()
; CHECK: } ; CHECK: }
; CHECK: ] ; CHECK: ]

View File

@@ -1383,7 +1383,8 @@ TEST_F(DIFileTest, get) {
EXPECT_NE(N, DIFile::get(Context, "other", Directory, CSKind, Checksum)); EXPECT_NE(N, DIFile::get(Context, "other", Directory, CSKind, Checksum));
EXPECT_NE(N, DIFile::get(Context, Filename, "other", CSKind, Checksum)); EXPECT_NE(N, DIFile::get(Context, Filename, "other", CSKind, Checksum));
EXPECT_NE(N, DIFile::get(Context, Filename, Directory, DIFile::CSK_SHA1, Checksum)); EXPECT_NE(
N, DIFile::get(Context, Filename, Directory, DIFile::CSK_SHA1, Checksum));
EXPECT_NE(N, DIFile::get(Context, Filename, Directory)); EXPECT_NE(N, DIFile::get(Context, Filename, Directory));
TempDIFile Temp = N->clone(); TempDIFile Temp = N->clone();