diff --git a/src/coreclr/dlls/mscorpe/ceefilegenwriter.cpp b/src/coreclr/dlls/mscorpe/ceefilegenwriter.cpp index 5edec7ed47c69b..0b2b72453fbbff 100644 --- a/src/coreclr/dlls/mscorpe/ceefilegenwriter.cpp +++ b/src/coreclr/dlls/mscorpe/ceefilegenwriter.cpp @@ -482,6 +482,11 @@ HRESULT CeeFileGenWriter::getFileTimeStamp(DWORD *pTimeStamp) return getPEWriter().getFileTimeStamp(pTimeStamp); } // HRESULT CeeFileGenWriter::getFileTimeStamp() +void CeeFileGenWriter::setFileHeaderTimeStamp(DWORD timeStamp) +{ + return getPEWriter().setFileHeaderTimeStamp(timeStamp); +} // void CeeFileGenWriter::setFileHeaderTimeStamp() + HRESULT CeeFileGenWriter::setAddrReloc(UCHAR *instrAddr, DWORD value) { *(DWORD *)instrAddr = VAL32(value); diff --git a/src/coreclr/dlls/mscorpe/iceefilegen.cpp b/src/coreclr/dlls/mscorpe/iceefilegen.cpp index 152fe9b3fcaf91..06270631c551cb 100644 --- a/src/coreclr/dlls/mscorpe/iceefilegen.cpp +++ b/src/coreclr/dlls/mscorpe/iceefilegen.cpp @@ -446,7 +446,7 @@ HRESULT ICeeFileGen::SetVTableEntry64(HCEEFILE ceeFile, ULONG size, void* ptr) return gen->setVTableEntry64(size, ptr); } -HRESULT ICeeFileGen::GetFileTimeStamp (HCEEFILE ceeFile, DWORD *pTimeStamp) +HRESULT ICeeFileGen::GetFileTimeStamp(HCEEFILE ceeFile, DWORD *pTimeStamp) { TESTANDRETURNPOINTER(ceeFile); TESTANDRETURNPOINTER(pTimeStamp); @@ -455,3 +455,12 @@ HRESULT ICeeFileGen::GetFileTimeStamp (HCEEFILE ceeFile, DWORD *pTimeStamp) return(gen->getFileTimeStamp(pTimeStamp)); } +HRESULT ICeeFileGen::SetFileHeaderTimeStamp(HCEEFILE ceeFile, DWORD timeStamp) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + gen->setFileHeaderTimeStamp(timeStamp); + return S_OK; +} + diff --git a/src/coreclr/dlls/mscorpe/pewriter.cpp b/src/coreclr/dlls/mscorpe/pewriter.cpp index 905ca3129e677f..703c98e1a17b1f 100644 --- a/src/coreclr/dlls/mscorpe/pewriter.cpp +++ b/src/coreclr/dlls/mscorpe/pewriter.cpp @@ -1637,6 +1637,11 @@ HRESULT PEWriter::getFileTimeStamp(DWORD *pTimeStamp) return S_OK; } +void PEWriter::setFileHeaderTimeStamp(DWORD timeStamp) +{ + m_ntHeaders->FileHeader.TimeDateStamp = timeStamp; +} + DWORD PEWriter::getImageBase32() { _ASSERTE(isPE32()); diff --git a/src/coreclr/dlls/mscorpe/pewriter.h b/src/coreclr/dlls/mscorpe/pewriter.h index fbb2e83e1b0617..31b4ddcb726eb8 100644 --- a/src/coreclr/dlls/mscorpe/pewriter.h +++ b/src/coreclr/dlls/mscorpe/pewriter.h @@ -83,6 +83,7 @@ class PEWriter : public PESectionMan size_t getImageBase(); HRESULT getFileTimeStamp(DWORD *pTimeStamp); + void setFileHeaderTimeStamp(DWORD timeStamp); IMAGE_NT_HEADERS32* ntHeaders32() { return (IMAGE_NT_HEADERS32*) m_ntHeaders; } IMAGE_NT_HEADERS64* ntHeaders64() { return (IMAGE_NT_HEADERS64*) m_ntHeaders; } diff --git a/src/coreclr/ilasm/assem.cpp b/src/coreclr/ilasm/assem.cpp index 7df1db6ce7298f..f923c34595bcda 100644 --- a/src/coreclr/ilasm/assem.cpp +++ b/src/coreclr/ilasm/assem.cpp @@ -15,6 +15,10 @@ #include "assembler.h" +#if !defined(_WIN32) && !defined(__APPLE__) +#include "sha256.h" +#endif + void indexKeywords(Indx* indx); // defined in asmparse.y unsigned int g_uCodePage = CP_ACP; @@ -28,6 +32,7 @@ Assembler::Assembler() { m_pDisp = NULL; m_pEmitter = NULL; + m_pInternalEmitForDeterministicMvid = NULL; m_pImporter = NULL; char* pszFQN = new char[16]; @@ -107,6 +112,7 @@ Assembler::Assembler() m_fGeneratePDB = FALSE; m_fIsMscorlib = FALSE; m_fOptimize = FALSE; + m_fDeterministic = FALSE; m_tkSysObject = 0; m_tkSysString = 0; m_tkSysValue = 0; @@ -208,6 +214,11 @@ Assembler::~Assembler() m_pEmitter->Release(); m_pEmitter = NULL; } + if (m_pInternalEmitForDeterministicMvid != NULL) + { + m_pInternalEmitForDeterministicMvid->Release(); + m_pInternalEmitForDeterministicMvid = NULL; + } if (m_pPortablePdbWriter != NULL) { delete m_pPortablePdbWriter; @@ -233,13 +244,19 @@ BOOL Assembler::Init(BOOL generatePdb) } if (FAILED(CreateICeeFileGen(&m_pCeeFileGen))) return FALSE; - if (FAILED(m_pCeeFileGen->CreateCeeFileEx(&m_pCeeFile,(ULONG)m_dwCeeFileFlags))) return FALSE; - if (FAILED(m_pCeeFileGen->GetSectionCreate(m_pCeeFile, ".il", sdReadOnly, &m_pILSection))) return FALSE; if (FAILED(m_pCeeFileGen->GetSectionCreate (m_pCeeFile, ".sdata", sdReadWrite, &m_pGlobalDataSection))) return FALSE; if (FAILED(m_pCeeFileGen->GetSectionCreate (m_pCeeFile, ".tls", sdReadWrite, &m_pTLSSection))) return FALSE; +#if !defined(_WIN32) && !defined(__APPLE__) + if (m_fDeterministic && !IsOpenSslAvailable()) + { + fprintf(stderr, "OpenSSL is not available, but required for build determinism\n"); + return FALSE; + } +#endif + m_fGeneratePDB = generatePdb; return TRUE; diff --git a/src/coreclr/ilasm/assembler.cpp b/src/coreclr/ilasm/assembler.cpp index 1a604181d3bfef..633daec4510877 100644 --- a/src/coreclr/ilasm/assembler.cpp +++ b/src/coreclr/ilasm/assembler.cpp @@ -2434,12 +2434,9 @@ void Assembler::SetPdbFileName(_In_ __nullterminated char* szName) HRESULT Assembler::SavePdbFile() { HRESULT hr = S_OK; - mdMethodDef entryPoint; if (FAILED(hr = (m_pPortablePdbWriter == NULL ? E_FAIL : S_OK))) goto exit; if (FAILED(hr = (m_pPortablePdbWriter->GetEmitter() == NULL ? E_FAIL : S_OK))) goto exit; - if (FAILED(hr = m_pCeeFileGen->GetEntryPoint(m_pCeeFile, &entryPoint))) goto exit; - if (FAILED(hr = m_pPortablePdbWriter->BuildPdbStream(m_pEmitter, entryPoint))) goto exit; if (FAILED(hr = m_pPortablePdbWriter->GetEmitter()->Save(m_wzPdbFileName, 0))) goto exit; exit: diff --git a/src/coreclr/ilasm/assembler.h b/src/coreclr/ilasm/assembler.h index d9df9292482440..b2b2d30a9c08dd 100644 --- a/src/coreclr/ilasm/assembler.h +++ b/src/coreclr/ilasm/assembler.h @@ -752,6 +752,7 @@ class Assembler { BOOL m_fIsMscorlib; BOOL m_fTolerateDupMethods; BOOL m_fOptimize; + BOOL m_fDeterministic; mdToken m_tkSysObject; mdToken m_tkSysString; mdToken m_tkSysValue; @@ -760,6 +761,7 @@ class Assembler { IMetaDataDispenserEx2 *m_pDisp; IMetaDataEmit3 *m_pEmitter; + IMDInternalEmit *m_pInternalEmitForDeterministicMvid; ICeeFileGen *m_pCeeFileGen; IMetaDataImport2 *m_pImporter; // Import interface. HCEEFILE m_pCeeFile; @@ -845,7 +847,7 @@ class Assembler { BOOL EmitClass(Class *pClass); HRESULT CreatePEFile(_In_ __nullterminated WCHAR *pwzOutputFilename); HRESULT CreateTLSDirectory(); - HRESULT CreateDebugDirectory(); + HRESULT CreateDebugDirectory(BYTE(&pdbChecksum)[32]); HRESULT InitMetaData(); Class *FindCreateClass(_In_ __nullterminated const char *pszFQN); BOOL EmitFieldRef(_In_z_ char *pszArg, int opcode); diff --git a/src/coreclr/ilasm/main.cpp b/src/coreclr/ilasm/main.cpp index aba3aa78847f35..3956b40807b6be 100644 --- a/src/coreclr/ilasm/main.cpp +++ b/src/coreclr/ilasm/main.cpp @@ -180,6 +180,7 @@ extern "C" int _cdecl wmain(int argc, _In_ WCHAR **argv) printf("\n/DEBUG Disable JIT optimization, create PDB file, use sequence points from PDB"); printf("\n/DEBUG=IMPL Disable JIT optimization, create PDB file, use implicit sequence points"); printf("\n/DEBUG=OPT Enable JIT optimization, create PDB file, use implicit sequence points"); + printf("\n/DET Produce deterministic outputs"); printf("\n/OPTIMIZE Optimize long instructions to short"); printf("\n/FOLD Fold the identical method bodies into one"); printf("\n/CLOCK Measure and report compilation times"); @@ -316,6 +317,10 @@ extern "C" int _cdecl wmain(int argc, _In_ WCHAR **argv) { pAsm->m_fOptimize = TRUE; } + else if (!_stricmp(szOpt, "DET")) + { + pAsm->m_fDeterministic = TRUE; + } else if (!_stricmp(szOpt, "X64")) { pAsm->m_dwCeeFileFlags &= ~ICEE_CREATE_MACHINE_MASK; diff --git a/src/coreclr/ilasm/portable_pdb.cpp b/src/coreclr/ilasm/portable_pdb.cpp index 83260f107de50d..a60214c99e9f39 100644 --- a/src/coreclr/ilasm/portable_pdb.cpp +++ b/src/coreclr/ilasm/portable_pdb.cpp @@ -4,6 +4,7 @@ #include "portable_pdb.h" #include #include "assembler.h" +#include "sha256.h" //***************************************************************************** // Document @@ -103,6 +104,10 @@ HRESULT PortablePdbWriter::Init(IMetaDataDispenserEx2* mdDispenser) 0, IID_IMetaDataEmit3, (IUnknown**)&m_pdbEmitter); + + if (FAILED(hr)) goto exit; + + hr = m_pdbEmitter->QueryInterface(IID_IILAsmPortablePdbWriter, (void**)&m_ilasmPdbWriter); exit: return hr; } @@ -122,6 +127,16 @@ ULONG PortablePdbWriter::GetTimestamp() return m_pdbStream.id.pdbTimeStamp; } +void PortablePdbWriter::SetGuid(REFGUID newGuid) +{ + m_pdbStream.id.pdbGuid = newGuid; +} + +void PortablePdbWriter::SetTimestamp(const ULONG newTimestamp) +{ + m_pdbStream.id.pdbTimeStamp = newTimestamp; +} + Document* PortablePdbWriter::GetCurrentDocument() { return m_currentDocument; @@ -145,6 +160,17 @@ HRESULT PortablePdbWriter::BuildPdbStream(IMetaDataEmit3* peEmitter, mdMethodDef return hr; } +HRESULT PortablePdbWriter::ComputeSha256PdbStreamChecksum(BYTE(&checksum)[32]) +{ + return m_ilasmPdbWriter->ComputeSha256PdbStreamChecksum(Sha256Hash, checksum); +} + +HRESULT PortablePdbWriter::ChangePdbStreamGuid(REFGUID newGuid) +{ + m_pdbStream.id.pdbGuid = newGuid; + return m_ilasmPdbWriter->ChangePdbStreamGuid(newGuid); +} + HRESULT PortablePdbWriter::DefineDocument(char* name, GUID* language) { HRESULT hr = S_OK; diff --git a/src/coreclr/ilasm/portable_pdb.h b/src/coreclr/ilasm/portable_pdb.h index 46bd1f14920761..5fd69d0fbb6034 100644 --- a/src/coreclr/ilasm/portable_pdb.h +++ b/src/coreclr/ilasm/portable_pdb.h @@ -46,8 +46,12 @@ class PortablePdbWriter IMetaDataEmit3* GetEmitter(); GUID* GetGuid(); ULONG GetTimestamp(); + void SetGuid(REFGUID newGuid); + void SetTimestamp(const ULONG newTimestamp); Document* GetCurrentDocument(); HRESULT BuildPdbStream(IMetaDataEmit3* peEmitter, mdMethodDef entryPoint); + HRESULT ComputeSha256PdbStreamChecksum(BYTE (&checksum)[32]); + HRESULT ChangePdbStreamGuid(REFGUID newGuid); HRESULT DefineDocument(char* name, GUID* language); HRESULT DefineSequencePoints(Method* method); HRESULT DefineLocalScope(Method* method); @@ -59,10 +63,11 @@ class PortablePdbWriter BOOL _DefineLocalScope(mdMethodDef methodDefToken, Scope* currScope); private: - IMetaDataEmit3* m_pdbEmitter; - PORT_PDB_STREAM m_pdbStream; - DocumentList m_documentList; - Document* m_currentDocument; + IMetaDataEmit3* m_pdbEmitter; + IILAsmPortablePdbWriter* m_ilasmPdbWriter; + PORT_PDB_STREAM m_pdbStream; + DocumentList m_documentList; + Document* m_currentDocument; }; #endif diff --git a/src/coreclr/ilasm/writer.cpp b/src/coreclr/ilasm/writer.cpp index 2bc9152bfea3b3..4fafc190d42c5b 100644 --- a/src/coreclr/ilasm/writer.cpp +++ b/src/coreclr/ilasm/writer.cpp @@ -9,6 +9,7 @@ #include "assembler.h" #include "ceefilegenwriter.h" +#include "sha256.h" #ifndef _MSC_VER //cloned definition from ntimage.h that is removed for non MSVC builds @@ -51,10 +52,39 @@ HRESULT Assembler::InitMetaData() if(FAILED(hr = m_pEmitter->QueryInterface(IID_IMetaDataImport2, (void**)&m_pImporter))) goto exit; + if (m_fDeterministic) + { + // + // In deterministic mode, the MVID will need to be stabilized for the metadata scope that + // was created above, and the ChangeMvid service that makes this possible is only available + // on the IMDInternalEmit interface. + // + // Any failure is unexpected and catastrophic, so print a noisy message and return an + // error (which generally fails the entire ilasm operation) if any failure occurs. + // + + hr = m_pEmitter->QueryInterface(IID_IMDInternalEmit, (void**)&m_pInternalEmitForDeterministicMvid); + + if (FAILED(hr) || (m_pInternalEmitForDeterministicMvid == NULL)) + { + fprintf(stderr, "Unexpected: Failed to query the required MVID determinism interface: %X\n",hr); + hr = E_FAIL; + goto exit; + } + } + if (m_fGeneratePDB) { m_pPortablePdbWriter = new PortablePdbWriter(); if (FAILED(hr = m_pPortablePdbWriter->Init(m_pDisp))) goto exit; + + if (m_fDeterministic) + { + // When build determinism is enabled, the PDB checksum is computed with these fields set to zero. + // The GUID and timestamp will be updated after. + m_pPortablePdbWriter->SetGuid(GUID()); + m_pPortablePdbWriter->SetTimestamp(0); + } } //m_Parser = new AsmParse(m_pEmitter); @@ -200,128 +230,261 @@ HRESULT Assembler::CreateTLSDirectory() { return(hr); } -HRESULT Assembler::CreateDebugDirectory() +HRESULT Assembler::CreateDebugDirectory(BYTE(&pdbChecksum)[32]) { - HRESULT hr = S_OK; - HCEESECTION sec = m_pILSection; - BYTE *de; - ULONG deOffset; - // Only emit this if we're also emitting debug info. - if (!m_fGeneratePDB) - return S_OK; + _ASSERTE(m_fGeneratePDB); - IMAGE_DEBUG_DIRECTORY debugDirIDD; - struct Param + struct DebugDirectoryEntry { - DWORD debugDirDataSize; - BYTE *debugDirData; - } param; - param.debugDirData = NULL; + IMAGE_DEBUG_DIRECTORY debugDirIDD; + DWORD debugDirDataSize; + BYTE* debugDirData; + }; + + // Arbitrary amount; should not need this many. + const int maxEntries = 8; + + DebugDirectoryEntry entries[maxEntries]; + int numEntries = 0; + + auto addEntry = [&]( + DWORD characteristics, + DWORD timeDateStamp, + WORD majorVersion, + WORD minorVersion, + DWORD type, + DWORD sizeOfData, + BYTE* data){ + _ASSERTE(numEntries >= 0); + _ASSERTE(numEntries < maxEntries); + + HRESULT hr = S_OK; + + IMAGE_DEBUG_DIRECTORY debugDirIDD; + struct Param + { + DWORD debugDirDataSize; + BYTE* debugDirData; + } param; + param.debugDirData = NULL; + + debugDirIDD.Characteristics = characteristics; + debugDirIDD.TimeDateStamp = timeDateStamp; + debugDirIDD.MajorVersion = majorVersion; + debugDirIDD.MinorVersion = minorVersion; + debugDirIDD.Type = type; + debugDirIDD.SizeOfData = sizeOfData; + debugDirIDD.AddressOfRawData = 0; // will be updated later + debugDirIDD.PointerToRawData = 0; // will be updated later + + param.debugDirDataSize = sizeOfData; + + if ((sizeOfData > 0) && (data != NULL)) + { + // Make some room for the data. + PAL_TRY(Param*, pParam, ¶m) { + pParam->debugDirData = new BYTE[pParam->debugDirDataSize]; + } PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { + hr = E_FAIL; + } PAL_ENDTRY + if (FAILED(hr)) return hr; + } + + param.debugDirData = data; + + DebugDirectoryEntry entry = {}; + entry.debugDirIDD = debugDirIDD; + entry.debugDirDataSize = param.debugDirDataSize; + entry.debugDirData = param.debugDirData; + entries[numEntries] = entry; + + numEntries++; + return S_OK; + }; + + HRESULT hr = S_OK; + + /* BEGIN CODEVIEW */ // get module ID DWORD rsds = VAL32(0x53445352); DWORD pdbAge = VAL32(0x1); GUID pdbGuid = *m_pPortablePdbWriter->GetGuid(); SwapGuid(&pdbGuid); - DWORD len = sizeof(rsds) + sizeof(GUID) + sizeof(pdbAge) + (DWORD)strlen(m_szPdbFileName) + 1; - BYTE* dbgDirData = new BYTE[len]; - - DWORD offset = 0; - memcpy_s(dbgDirData + offset, len, &rsds, sizeof(rsds)); // RSDS - offset += sizeof(rsds); - memcpy_s(dbgDirData + offset, len, &pdbGuid, sizeof(GUID)); // PDB GUID - offset += sizeof(GUID); - memcpy_s(dbgDirData + offset, len, &pdbAge, sizeof(pdbAge)); // PDB AGE - offset += sizeof(pdbAge); - memcpy_s(dbgDirData + offset, len, m_szPdbFileName, strlen(m_szPdbFileName) + 1); // PDB PATH - - debugDirIDD.Characteristics = 0; - debugDirIDD.TimeDateStamp = VAL32(m_pPortablePdbWriter->GetTimestamp()); - debugDirIDD.MajorVersion = VAL16(0x100); - debugDirIDD.MinorVersion = VAL16(0x504d); - debugDirIDD.Type = VAL32(IMAGE_DEBUG_TYPE_CODEVIEW); - debugDirIDD.SizeOfData = VAL32(len); - debugDirIDD.AddressOfRawData = 0; // will be updated bellow - debugDirIDD.PointerToRawData = 0; // will be updated bellow - - param.debugDirDataSize = len; - - // Make some room for the data. - PAL_TRY(Param*, pParam, ¶m) { - pParam->debugDirData = new BYTE[pParam->debugDirDataSize]; - } PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { - hr = E_FAIL; - } PAL_ENDTRY + DWORD codeViewSize = sizeof(rsds) + sizeof(GUID) + sizeof(pdbAge) + (DWORD)strlen(m_szPdbFileName) + 1; + BYTE* codeViewData = new BYTE[codeViewSize]; + + DWORD codeViewOffset = 0; + memcpy_s(codeViewData + codeViewOffset, codeViewSize, &rsds, sizeof(rsds)); // RSDS + codeViewOffset += sizeof(rsds); + memcpy_s(codeViewData + codeViewOffset, codeViewSize, &pdbGuid, sizeof(GUID)); // PDB GUID + codeViewOffset += sizeof(GUID); + memcpy_s(codeViewData + codeViewOffset, codeViewSize, &pdbAge, sizeof(pdbAge)); // PDB AGE + codeViewOffset += sizeof(pdbAge); + memcpy_s(codeViewData + codeViewOffset, codeViewSize, m_szPdbFileName, strlen(m_szPdbFileName) + 1); // PDB PATH + /* END CODEVIEW */ + + /* BEGIN PDB CHECKSUM */ + _ASSERTE(sizeof(pdbChecksum) == 32); + + // Algorithm name is case sensitive. + const char* algoName = "SHA256"; + const DWORD pdbChecksumOffset = ((DWORD)strlen(algoName)) + 1; + const DWORD pdbChecksumSize = pdbChecksumOffset + sizeof(pdbChecksum); + BYTE* pdbChecksumData = new BYTE[pdbChecksumSize]; + + // AlgorithmName (including null terminator) + memcpy_s(pdbChecksumData, pdbChecksumSize, algoName, pdbChecksumOffset); + + // Checksum + memcpy_s(pdbChecksumData + pdbChecksumOffset, pdbChecksumSize - pdbChecksumOffset, &pdbChecksum, sizeof(pdbChecksum)); + /* END PDB CHECKSUM */ + + auto finish = + [&](HRESULT hr) { + if (codeViewData) + { + delete [] codeViewData; + } + if (pdbChecksumData) + { + delete [] pdbChecksumData; + } + return hr; + }; + + // CodeView Entry + hr = + addEntry( + /* characteristics */ VAL32(0), + /* timeDateStamp */ VAL32(m_pPortablePdbWriter->GetTimestamp()), + /* majorVersion */ VAL16(0x100), + /* minorVersion */ VAL16(0x504d), + /* type */ VAL32(IMAGE_DEBUG_TYPE_CODEVIEW), + /* sizeOfData */ VAL32(codeViewSize), + /* data */ codeViewData + ); + if (FAILED(hr)) + return finish(hr); + + // Pdb Checksum Entry + hr = + addEntry( + /* characteristics */ VAL32(0), + /* timeDateStamp */ VAL32(m_pPortablePdbWriter->GetTimestamp()), + /* majorVersion */ VAL16(1), + /* minorVersion */ VAL16(0), + /* type */ VAL32(/* PDB Checksum Debug Directory Entry */ 19), + /* sizeOfData */ VAL32(pdbChecksumSize), + /* data */ pdbChecksumData + ); + if (FAILED(hr)) + return finish(hr); - if (FAILED(hr)) return hr; + if (m_fDeterministic) + { + // Deterministic Entry + hr = + addEntry( + /* characteristics */ VAL32(0), + /* timeDateStamp */ VAL32(0), + /* majorVersion */ VAL16(0), + /* minorVersion */ VAL16(0), + /* type */ VAL32(/* Deterministic Debug Directory Entry */ 16), + /* sizeOfData */ VAL32(0), + /* data */ NULL + ); + if (FAILED(hr)) + return finish(hr); + } - param.debugDirData = dbgDirData; + HCEESECTION sec = m_pILSection; + BYTE *de; + ULONG deOffset; + + ULONG totalDataSize = 0; + for (int i = 0; i < numEntries; i++) + { + totalDataSize += entries[i].debugDirDataSize; + } + ULONG totalEntrySize = (sizeof(IMAGE_DEBUG_DIRECTORY) * numEntries); + ULONG totalSize = (totalEntrySize + totalDataSize); // Grab memory in the section for our stuff. // Note that UpdateResource doesn't work correctly if the debug directory is // in the data section. So instead we put it in the text section (same as // cs compiler). if (FAILED(hr = m_pCeeFileGen->GetSectionBlock(sec, - sizeof(debugDirIDD) + - param.debugDirDataSize, + totalSize, 4, (void**) &de))) - goto ErrExit; + return finish(hr); // Where did we get that memory? if (FAILED(hr = m_pCeeFileGen->GetSectionDataLen(sec, &deOffset))) - goto ErrExit; - - deOffset -= (sizeof(debugDirIDD) + param.debugDirDataSize); - - // Setup a reloc so that the address of the raw - // data is setup correctly. - debugDirIDD.PointerToRawData = VAL32(deOffset + sizeof(debugDirIDD)); - - if (FAILED(hr = m_pCeeFileGen->AddSectionReloc( - sec, - deOffset + - offsetof(IMAGE_DEBUG_DIRECTORY, - PointerToRawData), - sec, srRelocFilePos))) - goto ErrExit; - - debugDirIDD.AddressOfRawData = VAL32(deOffset + sizeof(debugDirIDD)); - - if (FAILED(hr = m_pCeeFileGen->AddSectionReloc( - sec, - deOffset + - offsetof(IMAGE_DEBUG_DIRECTORY, - AddressOfRawData), - sec, srRelocAbsolute))) - goto ErrExit; + return finish(hr); + + deOffset -= totalSize; + // Emit the directory entry. if (FAILED(hr = m_pCeeFileGen->SetDirectoryEntry(m_pCeeFile, sec, IMAGE_DIRECTORY_ENTRY_DEBUG, - sizeof(debugDirIDD), + totalEntrySize, deOffset))) - goto ErrExit; + return finish(hr); - // Copy the debug directory into the section. - memcpy(de, &debugDirIDD, sizeof(debugDirIDD)); - memcpy(de + sizeof(debugDirIDD), param.debugDirData, - param.debugDirDataSize); + ULONG rawDataOffset = deOffset + totalEntrySize; - if (param.debugDirData) + ULONG dataOffset = 0; + for (int i = 0; i < numEntries; i++) { - delete [] param.debugDirData; + DebugDirectoryEntry* entry = &entries[i]; + + ULONG imageOffset = (i * sizeof(IMAGE_DEBUG_DIRECTORY)); + + if ((entry->debugDirDataSize > 0) && (entry->debugDirData != NULL)) + { + // Setup a reloc so that the address of the raw + // data is setup correctly. + entry->debugDirIDD.PointerToRawData = VAL32(rawDataOffset + dataOffset); + entry->debugDirIDD.AddressOfRawData = VAL32(rawDataOffset + dataOffset); + + dataOffset += entry->debugDirDataSize; + + if (FAILED(hr = m_pCeeFileGen->AddSectionReloc( + sec, + deOffset + imageOffset + + offsetof(IMAGE_DEBUG_DIRECTORY, + PointerToRawData), + sec, srRelocFilePos))) + return finish(hr); + + if (FAILED(hr = m_pCeeFileGen->AddSectionReloc( + sec, + deOffset + imageOffset + + offsetof(IMAGE_DEBUG_DIRECTORY, + AddressOfRawData), + sec, srRelocAbsolute))) + return finish(hr); + } + + // Copy the debug directory into the section. + memcpy(de + imageOffset, &entry->debugDirIDD, sizeof(IMAGE_DEBUG_DIRECTORY)); } - return S_OK; -ErrExit: - if (param.debugDirData) + dataOffset = 0; + for (int i = 0; i < numEntries; i++) { - delete [] param.debugDirData; + DebugDirectoryEntry entry = entries[i]; + + memcpy(de + totalEntrySize + dataOffset, entry.debugDirData, entry.debugDirDataSize); + dataOffset += entry.debugDirDataSize; } - return hr; + + return finish(hr); } //#ifdef EXPORT_DIR_ENABLED HRESULT Assembler::CreateExportDirectory() @@ -1017,6 +1180,8 @@ HRESULT Assembler::CreatePEFile(_In_ __nullterminated WCHAR *pwzOutputFilename) DWORD mresourceSize = 0; BYTE* mresourceData = NULL; WCHAR* wzScopeName = NULL; + GUID deterministicGuid = GUID(); + ULONG deterministicTimestamp = 0; if(bClock) bClock->cMDEmitBegin = GetTickCount(); if(m_fReportProgress) printf("Creating PE file\n"); @@ -1289,11 +1454,9 @@ HRESULT Assembler::CreatePEFile(_In_ __nullterminated WCHAR *pwzOutputFilename) if (FAILED(hr=CreateTLSDirectory())) goto exit; - if (FAILED(hr=CreateDebugDirectory())) goto exit; - if (FAILED(hr=m_pCeeFileGen->SetOutputFileName(m_pCeeFile, pwzOutputFilename))) goto exit; - // Reserve a buffer for the meta-data + // Reserve a buffer for the meta-data DWORD metaDataSize; if (FAILED(hr=m_pEmitter->GetSaveSize(cssAccurate, &metaDataSize))) goto exit; BYTE* metaData; @@ -1301,6 +1464,49 @@ HRESULT Assembler::CreatePEFile(_In_ __nullterminated WCHAR *pwzOutputFilename) ULONG metaDataOffset; if (FAILED(hr=m_pCeeFileGen->GetSectionDataLen(m_pILSection, &metaDataOffset))) goto exit; metaDataOffset -= metaDataSize; + + if (m_fDeterministic) + { + // Get deterministic GUID and timestamp from the computed hash + BYTE hash[32]; + _ASSERTE(sizeof(GUID) + sizeof(ULONG) <= sizeof(hash)); + if (FAILED(hr = Sha256Hash(metaData, metaDataSize, hash, sizeof(hash)))) goto exit; + + memcpy_s(&deterministicGuid, sizeof(GUID), hash, sizeof(GUID)); + memcpy_s(&deterministicTimestamp, sizeof(ULONG), hash + sizeof(GUID), sizeof(ULONG)); + + // In deterministic mode, the MVID needs to be stabilized for the metadata scope that was + // created in Assembler::InitMetaData, and it is guaranteed that the IMDInternalEmit for + // that scope was already acquired immediately after that scope was created. + _ASSERTE(m_pInternalEmitForDeterministicMvid != NULL); + m_pInternalEmitForDeterministicMvid->ChangeMvid(deterministicGuid); + + if (FAILED(hr = m_pCeeFileGen->SetFileHeaderTimeStamp(m_pCeeFile, deterministicTimestamp))) goto exit; + } + + if (m_fGeneratePDB) + { + mdMethodDef entryPoint; + + if (FAILED(hr = m_pCeeFileGen->GetEntryPoint(m_pCeeFile, &entryPoint))) goto exit; + if (FAILED(hr = m_pPortablePdbWriter->BuildPdbStream(m_pEmitter, entryPoint))) goto exit; + + BYTE pdbChecksum[32]; + if (FAILED(hr = m_pPortablePdbWriter->ComputeSha256PdbStreamChecksum(pdbChecksum))) goto exit; + + if (m_fDeterministic) + { + // Now that the PDB checksum has been computed, update the GUID and timestamp + _ASSERTE(*(m_pPortablePdbWriter->GetGuid()) == GUID()); + if (FAILED(hr = m_pPortablePdbWriter->ChangePdbStreamGuid(deterministicGuid))) goto exit; + + _ASSERTE(m_pPortablePdbWriter->GetTimestamp() == 0); + m_pPortablePdbWriter->SetTimestamp(deterministicTimestamp); + } + + if (FAILED(hr=CreateDebugDirectory(pdbChecksum))) goto exit; + } + // set managed resource entry, if any if(m_pManifest && m_pManifest->m_dwMResSizeTotal) { @@ -1598,6 +1804,7 @@ HRESULT Assembler::CreatePEFile(_In_ __nullterminated WCHAR *pwzOutputFilename) exit: return hr; } + #ifdef _PREFAST_ #pragma warning(pop) #endif diff --git a/src/coreclr/inc/ceefilegenwriter.h b/src/coreclr/inc/ceefilegenwriter.h index 0364767cd5f471..2acbcb4f04ead8 100644 --- a/src/coreclr/inc/ceefilegenwriter.h +++ b/src/coreclr/inc/ceefilegenwriter.h @@ -108,6 +108,7 @@ class CeeFileGenWriter : public CCeeGen HRESULT getCorHeader(IMAGE_COR20_HEADER **ppHeader); HRESULT getFileTimeStamp(DWORD *pTimeStamp); + void setFileHeaderTimeStamp(DWORD timeStamp); HRESULT setLibraryGuid(_In_ LPWSTR libraryGuid); diff --git a/src/coreclr/inc/iceefilegen.h b/src/coreclr/inc/iceefilegen.h index 8ea442ab17d8a0..43d92e4f2c883b 100644 --- a/src/coreclr/inc/iceefilegen.h +++ b/src/coreclr/inc/iceefilegen.h @@ -155,6 +155,7 @@ class ICeeFileGen { BYTE* buffer, unsigned buffLen); virtual HRESULT GetFileTimeStamp (HCEEFILE ceeFile, DWORD *pTimeStamp); + virtual HRESULT SetFileHeaderTimeStamp(HCEEFILE ceeFile, DWORD timeStamp); virtual HRESULT SetFileAlignment(HCEEFILE ceeFile, ULONG fileAlignment); diff --git a/src/coreclr/md/compiler/disp.cpp b/src/coreclr/md/compiler/disp.cpp index 2758a3ad6e3221..5d039353625283 100644 --- a/src/coreclr/md/compiler/disp.cpp +++ b/src/coreclr/md/compiler/disp.cpp @@ -581,6 +581,8 @@ HRESULT Disp::QueryInterface(REFIID riid, void **ppUnk) #ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB else if (riid == IID_IMetaDataDispenserEx2) *ppUnk = (IMetaDataDispenserEx2 *) this; + else if (riid == IID_IILAsmPortablePdbWriter) + *ppUnk = (IILAsmPortablePdbWriter *) this; #endif #ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE else if (riid == IID_IMetaDataDispenserCustom) diff --git a/src/coreclr/md/compiler/emit.cpp b/src/coreclr/md/compiler/emit.cpp index 6252deae8ff341..3c6dae259e4744 100644 --- a/src/coreclr/md/compiler/emit.cpp +++ b/src/coreclr/md/compiler/emit.cpp @@ -2114,6 +2114,34 @@ STDMETHODIMP RegMeta::DefineLocalVariable( // S_OK or error. return hr; #endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER } // RegMeta::DefineLocalVariable + +//******************************************************************************* +// ComputeSha256PdbStreamChecksum +//******************************************************************************* +STDMETHODIMP RegMeta::ComputeSha256PdbStreamChecksum( + HRESULT (*computeSha256)(BYTE* pSrc, DWORD srcSize, BYTE* pDst, DWORD dstSize), + BYTE (&checksum)[32]) +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + return m_pStgdb->m_pPdbHeap->ComputeSha256Checksum(computeSha256, checksum); +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} + +//******************************************************************************* +// ChangePdbStreamGuid +//******************************************************************************* +STDMETHODIMP RegMeta::ChangePdbStreamGuid( + REFGUID newGuid) +{ +#ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER + return E_NOTIMPL; +#else //!FEATURE_METADATA_EMIT_IN_DEBUGGER + return m_pStgdb->m_pPdbHeap->SetDataGuid(newGuid); +#endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER +} + #endif // FEATURE_METADATA_EMIT_PORTABLE_PDB //***************************************************************************** diff --git a/src/coreclr/md/compiler/regmeta.cpp b/src/coreclr/md/compiler/regmeta.cpp index 7022832ae99e4b..31f4479524624f 100644 --- a/src/coreclr/md/compiler/regmeta.cpp +++ b/src/coreclr/md/compiler/regmeta.cpp @@ -592,6 +592,10 @@ RegMeta::QueryInterface( *ppUnk = (IMetaDataEmit3 *)this; fIsInterfaceRW = true; } + else if (riid == IID_IILAsmPortablePdbWriter) + { + *ppUnk = static_cast(this); + } #endif else if (riid == IID_IMetaDataAssemblyEmit) { diff --git a/src/coreclr/md/compiler/regmeta.h b/src/coreclr/md/compiler/regmeta.h index 78a854b778b0fe..925be2775144c6 100644 --- a/src/coreclr/md/compiler/regmeta.h +++ b/src/coreclr/md/compiler/regmeta.h @@ -141,6 +141,7 @@ class RegMeta : , public IMetaDataEmit2 #else , public IMetaDataEmit3 + , public IILAsmPortablePdbWriter #endif , public IMetaDataAssemblyEmit #endif @@ -1132,6 +1133,16 @@ class RegMeta : USHORT index, // [IN] Variable index (slot). char *name, // [IN] Variable name. mdLocalVariable *locVarToken); // [OUT] Token of the defined variable. + +//***************************************************************************** +// IILAsmPortablePdbWriter methods +//***************************************************************************** + STDMETHODIMP ComputeSha256PdbStreamChecksum( // S_OK or error. + HRESULT (*computeSha256)(BYTE* pSrc, DWORD srcSize, BYTE* pDst, DWORD dstSize), // [IN] + BYTE (&checksum)[32]); // [OUT] 256-bit Pdb checksum + + STDMETHODIMP ChangePdbStreamGuid( // S_OK or error. + REFGUID newGuid); // [IN] GUID to use as the PDB GUID #endif // FEATURE_METADATA_EMIT_PORTABLE_PDB //***************************************************************************** diff --git a/src/coreclr/md/enc/pdbheap.cpp b/src/coreclr/md/enc/pdbheap.cpp index a95cf4ccefd6da..d402cddc0a7316 100644 --- a/src/coreclr/md/enc/pdbheap.cpp +++ b/src/coreclr/md/enc/pdbheap.cpp @@ -66,6 +66,25 @@ HRESULT PdbHeap::SetData(PORT_PDB_STREAM* data) return S_OK; } + +__checkReturn +HRESULT PdbHeap::SetDataGuid(REFGUID newGuid) +{ + _ASSERTE(m_size >= sizeof(PDB_ID)); + + if (memcpy_s(m_data, m_size, &newGuid, sizeof(GUID))) + return E_FAIL; + + return S_OK; +} + +__checkReturn +HRESULT PdbHeap::ComputeSha256Checksum(HRESULT (*computeSha256)(BYTE* pSrc, DWORD srcSize, BYTE* pDst, DWORD dstSize), BYTE (&checksum)[32]) +{ + _ASSERTE(m_size >= sizeof(PDB_ID)); + return computeSha256(m_data, m_size, (BYTE*)&checksum, sizeof(checksum)); +} + __checkReturn HRESULT PdbHeap::SaveToStream(IStream* stream) { diff --git a/src/coreclr/md/inc/pdbheap.h b/src/coreclr/md/inc/pdbheap.h index 92cc3b24494ac2..88bae893f86d7e 100644 --- a/src/coreclr/md/inc/pdbheap.h +++ b/src/coreclr/md/inc/pdbheap.h @@ -21,6 +21,8 @@ class PdbHeap ~PdbHeap(); __checkReturn HRESULT SetData(PORT_PDB_STREAM* data); + __checkReturn HRESULT SetDataGuid(REFGUID newGuid); + __checkReturn HRESULT ComputeSha256Checksum(HRESULT (*computeSha256)(BYTE* pSrc, DWORD srcSize, BYTE* pDst, DWORD dstSize), BYTE (&checksum)[32]); __checkReturn HRESULT SaveToStream(IStream* stream); BOOL IsEmpty(); ULONG GetSize(); diff --git a/src/coreclr/md/inc/portablepdbmdi.h b/src/coreclr/md/inc/portablepdbmdi.h index d1785bca193b35..fffdfbe95a5ff9 100644 --- a/src/coreclr/md/inc/portablepdbmdi.h +++ b/src/coreclr/md/inc/portablepdbmdi.h @@ -89,6 +89,25 @@ DECLARE_INTERFACE_(IMetaDataDispenserEx2, IMetaDataDispenserEx) IUnknown * *ppIUnk) PURE; // [OUT] Return interface on success. }; +//------------------------------------- +//--- IILAsmPortablePdbWriter +//------------------------------------- +// {8b2db1f0-91f5-4c99-bb07-29c878cf352a} +EXTERN_GUID(IID_IILAsmPortablePdbWriter, 0x8b2db1f0, 0x91f5, 0x4c99, 0xbb, 0x07, 0x29, 0xc8, 0x78, 0xcf, 0x35, 0x2a); + +//--- +#undef INTERFACE +#define INTERFACE IILAsmPortablePdbWriter +DECLARE_INTERFACE_(IILAsmPortablePdbWriter, IUnknown) +{ + STDMETHOD(ComputeSha256PdbStreamChecksum)( // S_OK or error. + HRESULT (*computeSha256)(BYTE* pSrc, DWORD srcSize, BYTE* pDst, DWORD dstSize), // [IN] + BYTE (&checksum)[32]) PURE; // [OUT] 256-bit Pdb checksum + + STDMETHOD(ChangePdbStreamGuid)( // S_OK or error. + REFGUID newGuid) PURE; // [IN] GUID to use as the PDB GUID +}; + #ifdef __cplusplus } #endif diff --git a/src/tests/Common/CLRTest.Jit.targets b/src/tests/Common/CLRTest.Jit.targets index 5c2a0735a414fe..ab793f12ef06f9 100644 --- a/src/tests/Common/CLRTest.Jit.targets +++ b/src/tests/Common/CLRTest.Jit.targets @@ -290,12 +290,23 @@ set DOTNET_JitPath=%CORE_ROOT%\superpmi-shim-collector.dll $(AssemblyName).dll IL-RT/$(AssemblyName).il IL-RT/$(AssemblyName).dll - <_IlasmRoundTripScriptText> +