From d4163670d965be4ff29d6732fbc88ee316758e64 Mon Sep 17 00:00:00 2001 From: dotnet-bot Date: Tue, 8 Dec 2015 09:17:49 -0800 Subject: Open sourcing ILASM [tfs-changeset: 1555681] --- src/dlls/mscorpe/.gitmirrorall | 1 + src/dlls/mscorpe/Native.rc | 9 + src/dlls/mscorpe/ceefilegenwriter.cpp | 2022 ++++++++++++++++ src/dlls/mscorpe/ceefilegenwritertokens.cpp | 265 +++ src/dlls/mscorpe/dirs.proj | 15 + src/dlls/mscorpe/iceefilegen.cpp | 733 ++++++ src/dlls/mscorpe/mscorpe/mscorpe.def | 11 + src/dlls/mscorpe/mscorpe/mscorpe.nativeproj | 46 + src/dlls/mscorpe/mscorpe/wrapper.cpp | 150 ++ src/dlls/mscorpe/mscorpehost/mscorpehost.def | 12 + .../mscorpe/mscorpehost/mscorpehost.nativeproj | 68 + src/dlls/mscorpe/pewriter.cpp | 2401 +++++++++++++++++++ src/dlls/mscorpe/pewriter.h | 338 +++ src/dlls/mscorpe/stdafx.cpp | 11 + src/dlls/mscorpe/stdafx.h | 25 + src/dlls/mscorpe/stubs.h | 170 ++ src/dlls/mscorpe/utilcodeinit.cpp | 12 + src/ilasm/.gitmirrorall | 1 + src/ilasm/Assembler.h | 1186 ++++++++++ src/ilasm/MscorpeSxS.cpp | 24 + src/ilasm/MscorpeSxS.h | 21 + src/ilasm/Native.rc | 9 + src/ilasm/asmenum.h | 42 + src/ilasm/asmman.cpp | 1264 ++++++++++ src/ilasm/asmman.hpp | 301 +++ src/ilasm/asmparse.h | 327 +++ src/ilasm/asmparse.y | 2053 ++++++++++++++++ src/ilasm/asmtemplates.h | 846 +++++++ src/ilasm/assem.cpp | 1613 +++++++++++++ src/ilasm/assembler.cpp | 2466 ++++++++++++++++++++ src/ilasm/binstr.h | 78 + src/ilasm/class.hpp | 119 + src/ilasm/extractGrammar.pl | 60 + src/ilasm/grammar_after.cpp | 1726 ++++++++++++++ src/ilasm/grammar_before.cpp | 150 ++ src/ilasm/ilasm.nativeproj | 87 + src/ilasm/ilasmpch.cpp | 6 + src/ilasm/ilasmpch.h | 28 + src/ilasm/main.cpp | 904 +++++++ src/ilasm/method.cpp | 158 ++ src/ilasm/method.hpp | 381 +++ src/ilasm/nvpair.h | 47 + src/ilasm/typar.hpp | 167 ++ src/ilasm/writer.cpp | 1798 ++++++++++++++ src/ilasm/writer_enc.cpp | 661 ++++++ 45 files changed, 22812 insertions(+) create mode 100644 src/dlls/mscorpe/.gitmirrorall create mode 100644 src/dlls/mscorpe/Native.rc create mode 100644 src/dlls/mscorpe/ceefilegenwriter.cpp create mode 100644 src/dlls/mscorpe/ceefilegenwritertokens.cpp create mode 100644 src/dlls/mscorpe/dirs.proj create mode 100644 src/dlls/mscorpe/iceefilegen.cpp create mode 100644 src/dlls/mscorpe/mscorpe/mscorpe.def create mode 100644 src/dlls/mscorpe/mscorpe/mscorpe.nativeproj create mode 100644 src/dlls/mscorpe/mscorpe/wrapper.cpp create mode 100644 src/dlls/mscorpe/mscorpehost/mscorpehost.def create mode 100644 src/dlls/mscorpe/mscorpehost/mscorpehost.nativeproj create mode 100644 src/dlls/mscorpe/pewriter.cpp create mode 100644 src/dlls/mscorpe/pewriter.h create mode 100644 src/dlls/mscorpe/stdafx.cpp create mode 100644 src/dlls/mscorpe/stdafx.h create mode 100644 src/dlls/mscorpe/stubs.h create mode 100644 src/dlls/mscorpe/utilcodeinit.cpp create mode 100644 src/ilasm/.gitmirrorall create mode 100644 src/ilasm/Assembler.h create mode 100644 src/ilasm/MscorpeSxS.cpp create mode 100644 src/ilasm/MscorpeSxS.h create mode 100644 src/ilasm/Native.rc create mode 100644 src/ilasm/asmenum.h create mode 100644 src/ilasm/asmman.cpp create mode 100644 src/ilasm/asmman.hpp create mode 100644 src/ilasm/asmparse.h create mode 100644 src/ilasm/asmparse.y create mode 100644 src/ilasm/asmtemplates.h create mode 100644 src/ilasm/assem.cpp create mode 100644 src/ilasm/assembler.cpp create mode 100644 src/ilasm/binstr.h create mode 100644 src/ilasm/class.hpp create mode 100644 src/ilasm/extractGrammar.pl create mode 100644 src/ilasm/grammar_after.cpp create mode 100644 src/ilasm/grammar_before.cpp create mode 100644 src/ilasm/ilasm.nativeproj create mode 100644 src/ilasm/ilasmpch.cpp create mode 100644 src/ilasm/ilasmpch.h create mode 100644 src/ilasm/main.cpp create mode 100644 src/ilasm/method.cpp create mode 100644 src/ilasm/method.hpp create mode 100644 src/ilasm/nvpair.h create mode 100644 src/ilasm/typar.hpp create mode 100644 src/ilasm/writer.cpp create mode 100644 src/ilasm/writer_enc.cpp diff --git a/src/dlls/mscorpe/.gitmirrorall b/src/dlls/mscorpe/.gitmirrorall new file mode 100644 index 0000000000..9ee5c57b99 --- /dev/null +++ b/src/dlls/mscorpe/.gitmirrorall @@ -0,0 +1 @@ +This folder will be mirrored by the Git-TFS Mirror recursively. \ No newline at end of file diff --git a/src/dlls/mscorpe/Native.rc b/src/dlls/mscorpe/Native.rc new file mode 100644 index 0000000000..1711906807 --- /dev/null +++ b/src/dlls/mscorpe/Native.rc @@ -0,0 +1,9 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +#define FX_VER_FILEDESCRIPTION_STR "Microsoft .NET Runtime PE File Generator\0" + +#include +#include diff --git a/src/dlls/mscorpe/ceefilegenwriter.cpp b/src/dlls/mscorpe/ceefilegenwriter.cpp new file mode 100644 index 0000000000..867512d068 --- /dev/null +++ b/src/dlls/mscorpe/ceefilegenwriter.cpp @@ -0,0 +1,2022 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// Derived class from CCeeGen which handles writing out +// the exe. All references to PEWriter pulled out of CCeeGen, +// and moved here +// +// + +#include "stdafx.h" + +#include +#include + +#include "corerror.h" +#include "stubs.h" +#include +#include + +// Globals. +HINSTANCE g_hThisInst; // This library. + + +#ifdef EMIT_FIXUPS + +// Emitted PEFIXUP structure looks like this +struct PEFIXUP +{ + WORD wType; + WORD wSpare; + DWORD rva; + DWORD rvaTarget; +}; + +// Following structure is used to store the reloc information which +// will be used at UpdateFixups time to get the final data from the section +// bytes to update the fixup information. +// +struct DBG_FIXUP +{ + WORD wType; + WORD wSpare; + + union + { + DWORD rva; + unsigned offset; + }; + + union + { + DWORD rvaTarget; + CeeSection * sectionSource; + }; +}; + +enum +{ + IMAGE_REL_I386_DIR24NB = 0x0081, // 24-bit base relative + IMAGE_REL_I386_FILEPOS = 0x0082, // 32-bit file relative + // all other relocation types are + // in winnt.h, for some reason + // this one is missing + IMAGE_REL_I386_DIR30NB = 0x0083, // 30-bit base relative +}; + +#endif // EMIT_FIXUPS + +// Get the Symbol entry given the head and a 0-based index +inline IMAGE_SYMBOL* GetSymbolEntry(IMAGE_SYMBOL* pHead, SIZE_T idx) +{ + return (IMAGE_SYMBOL*) (((BYTE*) pHead) + IMAGE_SIZEOF_SYMBOL * idx); +} + +#ifdef EnC_SUPPORTED +#define ENC_DELTA_HACK +#endif + +#ifdef ENC_DELTA_HACK + BOOL g_EnCMode = FALSE; +#endif + +//***************************************************************************** +// To get a new instance, call CreateNewInstance() or CreateNewInstanceEx() instead of new +//***************************************************************************** + +HRESULT CeeFileGenWriter::CreateNewInstance(CCeeGen *pCeeFileGenFrom, + CeeFileGenWriter* & pGenWriter, + DWORD createFlags) +{ + return CreateNewInstanceEx(pCeeFileGenFrom, pGenWriter, createFlags); +} + +// +// Seed file is used as the base file. The new file data will be "appended" to the seed file +// + +HRESULT CeeFileGenWriter::CreateNewInstanceEx(CCeeGen *pCeeFileGenFrom, + CeeFileGenWriter* & pGenWriter, + DWORD createFlags, + LPCWSTR seedFileName) +{ + HRESULT hr = S_OK; + ULONG preallocatedOffset = 0; + NewHolder pPEWriter(NULL); + NewHolder pPrivateGenWriter; + CeeSection *corHeaderSection = NULL; + + pPrivateGenWriter = new (nothrow) CeeFileGenWriter; + if (pPrivateGenWriter == NULL) + IfFailGo(E_OUTOFMEMORY); + + pPEWriter = new (nothrow) PEWriter; + if (pPEWriter == NULL) + IfFailGo(E_OUTOFMEMORY); + + //workaround + //What's really the correct thing to be doing here? + //HRESULT hr = pPEWriter->Init(pCeeFileGenFrom ? pCeeFileGenFrom->getPESectionMan() : NULL); + hr = pPEWriter->Init(NULL, createFlags, seedFileName); + IfFailGo(hr); + + //Create the general PEWriter. + pPrivateGenWriter->m_peSectionMan = pPEWriter; + hr = pPrivateGenWriter->Init(); // base class member to finish init + IfFailGo(hr); + + if (!seedFileName) // Use base file's preferred base (if present) + { + if (pPEWriter->isPE32()) + { + pPrivateGenWriter->setImageBase((DWORD) CEE_IMAGE_BASE_32); // use same default as linker + } + else + { + pPrivateGenWriter->setImageBase64((ULONGLONG) CEE_IMAGE_BASE_64); // use same default as linker + } + } + + pPrivateGenWriter->setSubsystem(IMAGE_SUBSYSTEM_WINDOWS_CUI, CEE_IMAGE_SUBSYSTEM_MAJOR_VERSION, CEE_IMAGE_SUBSYSTEM_MINOR_VERSION); + + if (pPEWriter->createCorMainStub()) + { + hr = pPrivateGenWriter->allocateIAT(); // so the IAT goes out first + IfFailGo(hr); + } + + hr = pPrivateGenWriter->allocateCorHeader(); // get COR header near front + IfFailGo(hr); + +#if 0 // Need to add this if we want to propagate the old COM+ header + if (seedFileName) + { + memcpy(m_corHeader, baseFileDecoder->ntHeaders32()->corHeader, sizeof(IMAGE_COR20_HEADER)); + } +#endif + + //If we were passed a CCeeGen at the beginning, copy it's data now. + if (pCeeFileGenFrom) { + pCeeFileGenFrom->cloneInstance((CCeeGen*)pPrivateGenWriter); + } + + hr = pPrivateGenWriter->getSectionCreate(".text0", sdExecute, &corHeaderSection); + IfFailGo(hr); + preallocatedOffset = corHeaderSection->dataLen(); + + + // set il RVA to be after the preallocated sections + pPEWriter->setIlRva(preallocatedOffset); + +#ifdef EMIT_FIXUPS + if (createFlags & ICEE_CREATE_FILE_EMIT_FIXUPS) + { + pPrivateGenWriter->setEmitFixups(); + } +#endif + + pPEWriter.SuppressRelease(); + pPrivateGenWriter.SuppressRelease(); + pGenWriter = pPrivateGenWriter; + +ErrExit: + return hr; +} // HRESULT CeeFileGenWriter::CreateNewInstance() + +CeeFileGenWriter::CeeFileGenWriter() // ctor is protected +{ + m_outputFileName = NULL; + m_resourceFileName = NULL; + m_dllSwitch = false; + m_objSwitch = false; + m_libraryName = NULL; + m_libraryGuid = GUID_NULL; + + m_entryPoint = 0; + m_comImageFlags = COMIMAGE_FLAGS_ILONLY; // ceegen PEs don't have native code + m_iatOffset = 0; + m_dllCount = 0; + + m_dwMacroDefinitionSize = 0; + m_dwMacroDefinitionRVA = NULL; + + m_dwManifestSize = 0; + m_dwManifestRVA = NULL; + + m_dwStrongNameSize = 0; + m_dwStrongNameRVA = NULL; + + m_dwVTableSize = 0; + m_dwVTableRVA = NULL; + + m_iDataDlls = NULL; + + m_linked = false; + m_fixed = false; + +#ifdef EMIT_FIXUPS + + m_fEmitFixups = false; + m_fFixupsUpdated = false; + m_sectionFixups = NULL; + m_pDebugDir = NULL; + +#endif + +#ifdef ENC_DELTA_HACK + // for EnC we want the RVA to be right at the front of the IL stream + WCHAR szFileName[256]; + DWORD len = WszGetEnvironmentVariable(L"COMP_ENC_EMIT", szFileName, NumItems(szFileName)); + if (len > 0) + g_EnCMode = TRUE; +#endif + +} // CeeFileGenWriter::CeeFileGenWriter() + +//***************************************************************************** +// Cleanup +//***************************************************************************** +HRESULT CeeFileGenWriter::Cleanup() // virtual +{ + ((PEWriter *)m_peSectionMan)->Cleanup(); // call derived cleanup + delete m_peSectionMan; + m_peSectionMan = NULL; // so base class won't delete + + delete[] m_outputFileName; + delete[] m_resourceFileName; + + if (m_iDataDlls) { + for (int i=0; i < m_dllCount; i++) { + if (m_iDataDlls[i].m_methodName) + delete[] m_iDataDlls[i].m_methodName; + } + delete[] m_iDataDlls; + } + + return CCeeGen::Cleanup(); +} // HRESULT CeeFileGenWriter::Cleanup() + +HRESULT CeeFileGenWriter::EmitMacroDefinitions(void *pData, DWORD cData) +{ + // OBSOLETE + m_dwMacroDefinitionSize = 0; + + return S_OK; +} // HRESULT CeeFileGenWriter::EmitMacroDefinitions() + +HRESULT CeeFileGenWriter::link() +{ + HRESULT hr = checkForErrors(); + if (! SUCCEEDED(hr)) + return hr; + +#ifdef EMIT_FIXUPS + + // The fixups describe each relocation. Each fixup contains the relocation's + // type, source RVA, and target RVA. Since the reloc target can be filled + // in after the relocation creation, the fixup target RVA discovery needs to + // be deferred. + // At this point all bytes should be filled in, ensuring that the final + // target information is available. + // UpdateFixups is called at this point to discover the final relocation target info. + // + hr = UpdateFixups(); + if (! SUCCEEDED(hr)) + return hr; + +#endif + + // Don't set this if SetManifestEntry was not called - zapper sets the + // resource directory explicitly + if (m_dwManifestSize != 0) + { + m_corHeader->Resources.VirtualAddress = VAL32(m_dwManifestRVA); + m_corHeader->Resources.Size = VAL32(m_dwManifestSize); + } + + if (m_dwStrongNameSize != 0) + { + m_corHeader->StrongNameSignature.VirtualAddress = VAL32(m_dwStrongNameRVA); + m_corHeader->StrongNameSignature.Size = VAL32(m_dwStrongNameSize); + } + + if (m_dwVTableSize != 0) + { + m_corHeader->VTableFixups.VirtualAddress = VAL32(m_dwVTableRVA); + m_corHeader->VTableFixups.Size = VAL32(m_dwVTableSize); + } + + unsigned characteristicsMask = IMAGE_FILE_EXECUTABLE_IMAGE; + + if (getPEWriter().isPE32()) + characteristicsMask |= IMAGE_FILE_32BIT_MACHINE; + if (!getPEWriter().isPE32()) + characteristicsMask |= IMAGE_FILE_LARGE_ADDRESS_AWARE; + + getPEWriter().setCharacteristics(characteristicsMask); + + m_corHeader->cb = VAL32(sizeof(IMAGE_COR20_HEADER)); + m_corHeader->MajorRuntimeVersion = VAL16(COR_VERSION_MAJOR); + m_corHeader->MinorRuntimeVersion = VAL16(COR_VERSION_MINOR); + if (m_dllSwitch) + getPEWriter().setCharacteristics(IMAGE_FILE_DLL); + if (m_objSwitch) + getPEWriter().clearCharacteristics(IMAGE_FILE_DLL | IMAGE_FILE_EXECUTABLE_IMAGE); + m_corHeader->Flags = VAL32(m_comImageFlags); + IMAGE_COR20_HEADER_FIELD(*m_corHeader, EntryPointToken) = VAL32(m_entryPoint); + _ASSERTE(TypeFromToken(m_entryPoint) == mdtMethodDef || m_entryPoint == mdTokenNil || + TypeFromToken(m_entryPoint) == mdtFile); + setDirectoryEntry(getCorHeaderSection(), IMAGE_DIRECTORY_ENTRY_COMHEADER, sizeof(IMAGE_COR20_HEADER), m_corHeaderOffset); + + if ((m_comImageFlags & COMIMAGE_FLAGS_IL_LIBRARY) == 0 + && !m_linked && !m_objSwitch) + { + hr = emitExeMain(); + if (FAILED(hr)) + return hr; + + hr = emitResourceSection(); + if (FAILED(hr)) + return hr; + } + + m_linked = true; + + IfFailRet(getPEWriter().link()); + + return S_OK; +} // HRESULT CeeFileGenWriter::link() + + +HRESULT CeeFileGenWriter::fixup() +{ + HRESULT hr; + + m_fixed = true; + + if (!m_linked) + IfFailRet(link()); + + CeeGenTokenMapper *pMapper = getTokenMapper(); + + // Apply token remaps if there are any. + if (! m_fTokenMapSupported && pMapper != NULL) { + IMetaDataImport *pImport; + hr = pMapper->GetMetaData(&pImport); + _ASSERTE(SUCCEEDED(hr)); + hr = MapTokens(pMapper, pImport); + pImport->Release(); + + } + + // remap the entry point if entry point token has been moved + if (pMapper != NULL && !m_objSwitch) + { + mdToken tk = m_entryPoint; + pMapper->HasTokenMoved(tk, tk); + IMAGE_COR20_HEADER_FIELD(*m_corHeader, EntryPointToken) = VAL32(tk); + } + + IfFailRet(getPEWriter().fixup(pMapper)); + + return S_OK; +} // HRESULT CeeFileGenWriter::fixup() + +HRESULT CeeFileGenWriter::generateImage(void **ppImage) +{ + HRESULT hr = S_OK; + LPWSTR outputFileName = NULL; + + HANDLE hThreadToken = NULL; + // Impersonation is only supported on Win2k and above. + if (!OpenThreadToken(GetCurrentThread(), TOKEN_READ | TOKEN_IMPERSONATE, TRUE, &hThreadToken)) + { + if (GetLastError() != ERROR_NO_TOKEN) + { + _ASSERTE(!"Failed to get thread token!"); + return HRESULT_FROM_GetLastError(); + } + } + + if (hThreadToken != NULL) + { + if (!RevertToSelf()) + { + _ASSERTE(!"Failed to revert impersonation!"); + CloseHandle(hThreadToken); + return HRESULT_FROM_GetLastError(); + } + } + +#ifdef ENC_DELTA_HACK + // fixups break because we've set the base RVA to 0 for the delta stream + if (! g_EnCMode) +#endif + if (!m_fixed) + IfFailGo(fixup()); + + outputFileName = m_outputFileName; + + if (! outputFileName && ppImage == NULL) { + if (m_comImageFlags & COMIMAGE_FLAGS_IL_LIBRARY) + outputFileName = L"output.ill"; + else if (m_dllSwitch) + outputFileName = L"output.dll"; + else if (m_objSwitch) + outputFileName = L"output.obj"; + else + outputFileName = L"output.exe"; + } + + // output file name and ppImage are mutually exclusive + _ASSERTE((NULL == outputFileName && ppImage != NULL) || (outputFileName != NULL && NULL == ppImage)); + + if (outputFileName != NULL) + IfFailGo(getPEWriter().write(outputFileName)); + else + IfFailGo(getPEWriter().write(ppImage)); + +ErrExit: + if (hThreadToken != NULL) + { + BOOL success = SetThreadToken(NULL, hThreadToken); + CloseHandle(hThreadToken); + + if (!success) + { + _ASSERTE(!"Failed to reimpersonate!"); + hr = HRESULT_FROM_GetLastError(); + } + } + return hr; +} // HRESULT CeeFileGenWriter::generateImage() + +HRESULT CeeFileGenWriter::setOutputFileName(__in LPWSTR fileName) +{ + if (m_outputFileName) + delete[] m_outputFileName; + int len = lstrlenW(fileName) + 1; + m_outputFileName = (LPWSTR)new (nothrow) WCHAR[len]; + TESTANDRETURN(m_outputFileName!=NULL, E_OUTOFMEMORY); + wcscpy_s(m_outputFileName, len, fileName); + return S_OK; +} // HRESULT CeeFileGenWriter::setOutputFileName() + +HRESULT CeeFileGenWriter::setResourceFileName(__in LPWSTR fileName) +{ + if (m_resourceFileName) + delete[] m_resourceFileName; + int len = lstrlenW(fileName) + 1; + m_resourceFileName = (LPWSTR)new (nothrow) WCHAR[len]; + TESTANDRETURN(m_resourceFileName!=NULL, E_OUTOFMEMORY); + wcscpy_s(m_resourceFileName, len, fileName); + return S_OK; +} // HRESULT CeeFileGenWriter::setResourceFileName() + +HRESULT CeeFileGenWriter::setLibraryName(__in LPWSTR libraryName) +{ + if (m_libraryName) + delete[] m_libraryName; + int len = lstrlenW(libraryName) + 1; + m_libraryName = (LPWSTR)new (nothrow) WCHAR[len]; + TESTANDRETURN(m_libraryName != NULL, E_OUTOFMEMORY); + wcscpy_s(m_libraryName, len, libraryName); + return S_OK; +} // HRESULT CeeFileGenWriter::setLibraryName() + +HRESULT CeeFileGenWriter::setLibraryGuid(__in LPWSTR libraryGuid) +{ + return IIDFromString(libraryGuid, &m_libraryGuid); +} // HRESULT CeeFileGenWriter::setLibraryGuid() + +HRESULT CeeFileGenWriter::emitLibraryName(IMetaDataEmit *emitter) +{ + HRESULT hr; + IfFailRet(emitter->SetModuleProps(m_libraryName)); + + // Set the GUID as a custom attribute, if it is not NULL_GUID. + if (m_libraryGuid != GUID_NULL) + { + static COR_SIGNATURE _SIG[] = INTEROP_GUID_SIG; + mdTypeRef tr; + mdMemberRef mr; + WCHAR wzGuid[40]; + BYTE rgCA[50]; + IfFailRet(emitter->DefineTypeRefByName(mdTypeRefNil, INTEROP_GUID_TYPE_W, &tr)); + IfFailRet(emitter->DefineMemberRef(tr, L".ctor", _SIG, sizeof(_SIG), &mr)); + StringFromGUID2(m_libraryGuid, wzGuid, lengthof(wzGuid)); + memset(rgCA, 0, sizeof(rgCA)); + // Tag is 0x0001 + rgCA[0] = 1; + // Length of GUID string is 36 characters. + rgCA[2] = 0x24; + // Convert 36 characters, skipping opening {, into 3rd byte of buffer. + WszWideCharToMultiByte(CP_ACP,0, wzGuid+1,36, reinterpret_cast(&rgCA[3]),36, 0,0); + hr = emitter->DefineCustomAttribute(1,mr,rgCA,41,0); + } + return (hr); +} // HRESULT CeeFileGenWriter::emitLibraryName() + +HRESULT CeeFileGenWriter::setImageBase(size_t imageBase) +{ + _ASSERTE(getPEWriter().isPE32()); + getPEWriter().setImageBase32((DWORD)imageBase); + return S_OK; +} // HRESULT CeeFileGenWriter::setImageBase() + +HRESULT CeeFileGenWriter::setImageBase64(ULONGLONG imageBase) +{ + _ASSERTE(!getPEWriter().isPE32()); + getPEWriter().setImageBase64(imageBase); + return S_OK; +} // HRESULT CeeFileGenWriter::setImageBase64() + +HRESULT CeeFileGenWriter::setFileAlignment(ULONG fileAlignment) +{ + getPEWriter().setFileAlignment(fileAlignment); + return S_OK; +} // HRESULT CeeFileGenWriter::setFileAlignment() + +HRESULT CeeFileGenWriter::setSubsystem(DWORD subsystem, DWORD major, DWORD minor) +{ + getPEWriter().setSubsystem(subsystem, major, minor); + return S_OK; +} // HRESULT CeeFileGenWriter::setSubsystem() + +HRESULT CeeFileGenWriter::checkForErrors() +{ + if (TypeFromToken(m_entryPoint) == mdtMethodDef) { + if (m_dllSwitch) { + //current spec would need to check the binary sig of the entry point method + } + return S_OK; + } + return S_OK; +} // HRESULT CeeFileGenWriter::checkForErrors() + +HRESULT CeeFileGenWriter::getMethodRVA(ULONG codeOffset, ULONG *codeRVA) +{ + _ASSERTE(codeRVA); +#ifdef ENC_DELTA_HACK + // for EnC we want the RVA to be right be relative to the front of the delta IL stream rather + // than take into account the .text section and the cor header as we would for a real PE file + if (g_EnCMode) + *codeRVA = codeOffset; + else +#endif + *codeRVA = getPEWriter().getIlRva() + codeOffset; + return S_OK; +} // HRESULT CeeFileGenWriter::getMethodRVA() + +HRESULT CeeFileGenWriter::setDirectoryEntry(CeeSection §ion, ULONG entry, ULONG size, ULONG offset) +{ + return getPEWriter().setDirectoryEntry((PEWriterSection*)(§ion.getImpl()), entry, size, offset); +} // HRESULT CeeFileGenWriter::setDirectoryEntry() + +HRESULT CeeFileGenWriter::getFileTimeStamp(DWORD *pTimeStamp) +{ + return getPEWriter().getFileTimeStamp(pTimeStamp); +} // HRESULT CeeFileGenWriter::getFileTimeStamp() + +HRESULT CeeFileGenWriter::setAddrReloc(UCHAR *instrAddr, DWORD value) +{ + *(DWORD *)instrAddr = VAL32(value); + return S_OK; +} // HRESULT CeeFileGenWriter::setAddrReloc() + +HRESULT CeeFileGenWriter::addAddrReloc(CeeSection &thisSection, UCHAR *instrAddr, DWORD offset, CeeSection *targetSection) +{ + if (!targetSection) { + thisSection.addBaseReloc(offset, srRelocHighLow); + } else { + thisSection.addSectReloc(offset, *targetSection, srRelocHighLow); + } + return S_OK; +} // HRESULT CeeFileGenWriter::addAddrReloc() + +// create CorExeMain and import directory into .text and the .iat into .data +// +// The structure of the import directory information is as follows, but it is not contiguous in +// section. All the r/o data goes into the .text section and the iat array (which the loader +// updates with the imported addresses) goes into the .data section because WINCE needs it to be writable. +// +// struct IData { +// // one for each DLL, terminating in NULL +// IMAGE_IMPORT_DESCRIPTOR iid[]; +// // import lookup table: a set of entries for the methods of each DLL, +// // terminating each set with NULL +// IMAGE_THUNK_DATA32/64 ilt[]; +// // hint/name table: an set of entries for each method of each DLL wiht +// // no terminating entry +// struct { +// WORD Hint; +// // null terminated string +// BYTE Name[]; +// } ibn; // Hint/name table +// // import address table: a set of entries for the methods of each DLL, +// // terminating each set with NULL +// IMAGE_THUNK_DATA32/64 iat[]; +// // one for each DLL, null terminated strings +// BYTE DllName[]; +// }; +// + +// IAT must be first in its section, so have code here to allocate it up front +// prior to knowing other info such as if dll or not. This won't work if have > 1 +// function imported, but we'll burn that bridge when we get to it. +HRESULT CeeFileGenWriter::allocateIAT() +{ + m_dllCount = 1; + m_iDataDlls = new (nothrow) IDataDllInfo[m_dllCount]; + if (m_iDataDlls == NULL) { + return E_OUTOFMEMORY; + } + memset(m_iDataDlls, '\0', m_dllCount * sizeof(IDataDllInfo)); + m_iDataDlls[0].m_name = "mscoree.dll"; + m_iDataDlls[0].m_numMethods = 1; + m_iDataDlls[0].m_methodName = new (nothrow) const char*[m_iDataDlls[0].m_numMethods]; + if (! m_iDataDlls[0].m_methodName) { + return E_OUTOFMEMORY; + } + m_iDataDlls[0].m_methodName[0] = NULL; + + int iDataSizeIAT = 0; + + for (int i=0; i < m_dllCount; i++) { + m_iDataDlls[i].m_iatOffset = iDataSizeIAT; + iDataSizeIAT += (m_iDataDlls[i].m_numMethods + 1) + * (getPEWriter().isPE32() ? sizeof(IMAGE_THUNK_DATA32) + : sizeof(IMAGE_THUNK_DATA64)); + } + + HRESULT hr = getSectionCreate(".text0", sdExecute, &m_iDataSectionIAT); + TESTANDRETURNHR(hr); + m_iDataOffsetIAT = m_iDataSectionIAT->dataLen(); + _ASSERTE(m_iDataOffsetIAT == 0); + m_iDataIAT = m_iDataSectionIAT->getBlock(iDataSizeIAT); + if (! m_iDataIAT) { + return E_OUTOFMEMORY; + } + memset(m_iDataIAT, '\0', iDataSizeIAT); + + // Don't set the IAT directory entry yet, since we may not actually end up doing + // an emitExeMain. + + return S_OK; +} // HRESULT CeeFileGenWriter::allocateIAT() + +HRESULT CeeFileGenWriter::emitExeMain() +{ + if (m_dllCount == 0) + return S_OK; + + // Note: code later on in this method assumes that mscoree.dll is at + // index m_iDataDlls[0], with CorDllMain or CorExeMain at method[0] + + _ASSERTE(getPEWriter().createCorMainStub()); + + if (m_dllSwitch) { + m_iDataDlls[0].m_methodName[0] = "_CorDllMain"; + } else { + m_iDataDlls[0].m_methodName[0] = "_CorExeMain"; + } + + // IMAGE_IMPORT_DESCRIPTOR on PE/PE+ must be 4-byte or 8-byte aligned + int align = (getPEWriter().isPE32()) ? 4 : 8; + int curOffset = getTextSection().dataLen(); + + int diff = ((curOffset + align -1) & ~(align-1)) - curOffset; + if (diff) + { + char* pDiff = getTextSection().getBlock(diff); + if (NULL==pDiff) return E_OUTOFMEMORY; + memset(pDiff,0,diff); + } + + int iDataSizeRO = (m_dllCount + 1) * sizeof(IMAGE_IMPORT_DESCRIPTOR); + CeeSection &iDataSectionRO = getTextSection(); + int iDataOffsetRO = iDataSectionRO.dataLen(); + int iDataSizeIAT = 0; + int i; + for (i=0; i < m_dllCount; i++) { + m_iDataDlls[i].m_iltOffset = iDataSizeRO + iDataSizeIAT; + iDataSizeIAT += (m_iDataDlls[i].m_numMethods + 1) + * (getPEWriter().isPE32() ? sizeof(IMAGE_THUNK_DATA32) + : sizeof(IMAGE_THUNK_DATA64)); + } + + iDataSizeRO += iDataSizeIAT; + + for (i=0; i < m_dllCount; i++) { + int delta = (iDataSizeRO + iDataOffsetRO) % 16; + // make sure is on a 16-byte offset + if (delta != 0) + iDataSizeRO += (16 - delta); + _ASSERTE((iDataSizeRO + iDataOffsetRO) % 16 == 0); + m_iDataDlls[i].m_ibnOffset = iDataSizeRO; + for (int j=0; j < m_iDataDlls[i].m_numMethods; j++) { + int nameLen = (int)(strlen(m_iDataDlls[i].m_methodName[j]) + 1); + iDataSizeRO += sizeof(WORD) + nameLen + nameLen%2; + } + } + for (i=0; i < m_dllCount; i++) { + m_iDataDlls[i].m_nameOffset = iDataSizeRO; + iDataSizeRO += (int)(strlen(m_iDataDlls[i].m_name) + 2); + } + + char *iDataRO = iDataSectionRO.getBlock(iDataSizeRO); + + if (!iDataRO) return E_OUTOFMEMORY; + + memset(iDataRO, '\0', iDataSizeRO); + + setDirectoryEntry(iDataSectionRO, IMAGE_DIRECTORY_ENTRY_IMPORT, iDataSizeRO, iDataOffsetRO); + + IMAGE_IMPORT_DESCRIPTOR *iid = (IMAGE_IMPORT_DESCRIPTOR *)iDataRO; + for (i=0; i < m_dllCount; i++) { + + // fill in the import descriptors for each DLL + IMAGE_IMPORT_DESC_FIELD(iid[i], OriginalFirstThunk) = VAL32((ULONG)(m_iDataDlls[i].m_iltOffset + iDataOffsetRO)); + iid[i].Name = VAL32(m_iDataDlls[i].m_nameOffset + iDataOffsetRO); + iid[i].FirstThunk = VAL32((ULONG)(m_iDataDlls[i].m_iatOffset + m_iDataOffsetIAT)); + + iDataSectionRO.addSectReloc( + (unsigned)(iDataOffsetRO + (char *)(&IMAGE_IMPORT_DESC_FIELD(iid[i], OriginalFirstThunk)) - iDataRO), iDataSectionRO, srRelocAbsolute); + iDataSectionRO.addSectReloc( + (unsigned)(iDataOffsetRO + (char *)(&iid[i].Name) - iDataRO), iDataSectionRO, srRelocAbsolute); + iDataSectionRO.addSectReloc( + (unsigned)(iDataOffsetRO + (char *)(&iid[i].FirstThunk) - iDataRO), *m_iDataSectionIAT, srRelocAbsolute); + + if (getPEWriter().isPE32()) + { + // now fill in the import lookup table for each DLL + IMAGE_THUNK_DATA32 *ilt = (IMAGE_THUNK_DATA32*) (iDataRO + m_iDataDlls[i].m_iltOffset); + IMAGE_THUNK_DATA32 *iat = (IMAGE_THUNK_DATA32*) (m_iDataIAT + m_iDataDlls[i].m_iatOffset); + + int ibnOffset = m_iDataDlls[i].m_ibnOffset; + for (int j=0; j < m_iDataDlls[i].m_numMethods; j++) + { + ilt[j].u1.AddressOfData = VAL32((ULONG)(ibnOffset + iDataOffsetRO)); + iat[j].u1.AddressOfData = VAL32((ULONG)(ibnOffset + iDataOffsetRO)); + + iDataSectionRO.addSectReloc( (unsigned)(iDataOffsetRO + (char *)(&ilt[j].u1.AddressOfData) - iDataRO), + iDataSectionRO, srRelocAbsolute); + m_iDataSectionIAT->addSectReloc( (unsigned)(m_iDataOffsetIAT + (char *)(&iat[j].u1.AddressOfData) - m_iDataIAT), + iDataSectionRO, srRelocAbsolute); + int nameLen = (int)(strlen(m_iDataDlls[i].m_methodName[j]) + 1); + memcpy(iDataRO + ibnOffset + offsetof(IMAGE_IMPORT_BY_NAME, Name), + m_iDataDlls[i].m_methodName[j], nameLen); + ibnOffset += sizeof(WORD) + nameLen + nameLen%2; + } + } + else + { + // now fill in the import lookup table for each DLL + IMAGE_THUNK_DATA64 *ilt = (IMAGE_THUNK_DATA64*) (iDataRO + m_iDataDlls[i].m_iltOffset); + IMAGE_THUNK_DATA64 *iat = (IMAGE_THUNK_DATA64*) (m_iDataIAT + m_iDataDlls[i].m_iatOffset); + + int ibnOffset = m_iDataDlls[i].m_ibnOffset; + for (int j=0; j < m_iDataDlls[i].m_numMethods; j++) + { + ilt[j].u1.AddressOfData = VAL64((ULONG)(ibnOffset + iDataOffsetRO)); + iat[j].u1.AddressOfData = VAL64((ULONG)(ibnOffset + iDataOffsetRO)); + + iDataSectionRO.addSectReloc( (unsigned)(iDataOffsetRO + (char *)(&ilt[j].u1.AddressOfData) - iDataRO), + iDataSectionRO, srRelocAbsolute); + m_iDataSectionIAT->addSectReloc( (unsigned)(m_iDataOffsetIAT + (char *)(&iat[j].u1.AddressOfData) - m_iDataIAT), + iDataSectionRO, srRelocAbsolute); + int nameLen = (int)(strlen(m_iDataDlls[i].m_methodName[j]) + 1); + memcpy(iDataRO + ibnOffset + offsetof(IMAGE_IMPORT_BY_NAME, Name), + m_iDataDlls[i].m_methodName[j], nameLen); + ibnOffset += sizeof(WORD) + nameLen + nameLen%2; + } + } + + // now fill in the import lookup table for each DLL + strcpy_s(iDataRO + m_iDataDlls[i].m_nameOffset, + iDataSizeRO - m_iDataDlls[i].m_nameOffset, + m_iDataDlls[i].m_name); + + } // end of for loop i < m_dllCount + + + if (getPEWriter().isI386()) + { + // Put the entry point code into the PE file + unsigned entryPointOffset = getTextSection().dataLen(); + int iatOffset = (int) (entryPointOffset + (m_dllSwitch ? CorDllMainX86IATOffset : CorExeMainX86IATOffset)); + align = 4; // x86 fixups must be 4-byte aligned + + // The IAT offset must be aligned because fixup is applied to it. + diff = ((iatOffset + align -1) & ~(align-1)) - iatOffset; + if (diff) + { + char* pDiff = getTextSection().getBlock(diff); + if(NULL==pDiff) return E_OUTOFMEMORY; + memset(pDiff,0,diff); + entryPointOffset += diff; + } + _ASSERTE((getTextSection().dataLen() + (m_dllSwitch ? CorDllMainX86IATOffset : CorExeMainX86IATOffset)) % align == 0); + + getPEWriter().setEntryPointTextOffset(entryPointOffset); + if (m_dllSwitch) + { + UCHAR *dllMainBuf = (UCHAR*)getTextSection().getBlock(sizeof(DllMainX86Template)); + if(dllMainBuf==NULL) return E_OUTOFMEMORY; + memcpy(dllMainBuf, DllMainX86Template, sizeof(DllMainX86Template)); + //mscoree.dll + setAddrReloc(dllMainBuf+CorDllMainX86IATOffset, m_iDataDlls[0].m_iatOffset + m_iDataOffsetIAT); + addAddrReloc(getTextSection(), dllMainBuf, entryPointOffset+CorDllMainX86IATOffset, m_iDataSectionIAT); + } + else + { + UCHAR *exeMainBuf = (UCHAR*)getTextSection().getBlock(sizeof(ExeMainX86Template)); + if(exeMainBuf==NULL) return E_OUTOFMEMORY; + memcpy(exeMainBuf, ExeMainX86Template, sizeof(ExeMainX86Template)); + //mscoree.dll + setAddrReloc(exeMainBuf+CorExeMainX86IATOffset, m_iDataDlls[0].m_iatOffset + m_iDataOffsetIAT); + addAddrReloc(getTextSection(), exeMainBuf, entryPointOffset+CorExeMainX86IATOffset, m_iDataSectionIAT); + } + } + else if (getPEWriter().isAMD64()) + { + // Put the entry point code into the PE file + unsigned entryPointOffset = getTextSection().dataLen(); + int iatOffset = (int) (entryPointOffset + (m_dllSwitch ? CorDllMainAMD64IATOffset : CorExeMainAMD64IATOffset)); + align = 16; // AMD64 fixups must be 8-byte aligned + + // The IAT offset must be aligned because fixup is applied to it. + diff = ((iatOffset + align -1) & ~(align-1)) - iatOffset; + if (diff) + { + char* pDiff = getTextSection().getBlock(diff); + if(NULL==pDiff) return E_OUTOFMEMORY; + memset(pDiff,0,diff); + entryPointOffset += diff; + } + _ASSERTE((getTextSection().dataLen() + (m_dllSwitch ? CorDllMainAMD64IATOffset : CorExeMainAMD64IATOffset)) % align == 0); + + getPEWriter().setEntryPointTextOffset(entryPointOffset); + if (m_dllSwitch) + { + UCHAR *dllMainBuf = (UCHAR*)getTextSection().getBlock(sizeof(DllMainAMD64Template)); + if(dllMainBuf==NULL) return E_OUTOFMEMORY; + memcpy(dllMainBuf, DllMainAMD64Template, sizeof(DllMainAMD64Template)); + //mscoree.dll + setAddrReloc(dllMainBuf+CorDllMainAMD64IATOffset, m_iDataDlls[0].m_iatOffset + m_iDataOffsetIAT); + addAddrReloc(getTextSection(), dllMainBuf, entryPointOffset+CorDllMainAMD64IATOffset, m_iDataSectionIAT); + } + else + { + UCHAR *exeMainBuf = (UCHAR*)getTextSection().getBlock(sizeof(ExeMainAMD64Template)); + if(exeMainBuf==NULL) return E_OUTOFMEMORY; + memcpy(exeMainBuf, ExeMainAMD64Template, sizeof(ExeMainAMD64Template)); + //mscoree.dll + setAddrReloc(exeMainBuf+CorExeMainAMD64IATOffset, m_iDataDlls[0].m_iatOffset + m_iDataOffsetIAT); + addAddrReloc(getTextSection(), exeMainBuf, entryPointOffset+CorExeMainAMD64IATOffset, m_iDataSectionIAT); + } + } + else if (getPEWriter().isIA64()) + { + // Must have a PE+ PE64 file + //_ASSERTE(!getPEWriter().isPE32()); + + // Put the entry point code into the PE+ file + curOffset = getTextSection().dataLen(); + align = 16; // instructions on ia64 must be 16-byte aligned + + // The entry point address be aligned + diff = ((curOffset + align -1) & ~(align-1)) - curOffset; + if (diff) + { + char* pDiff = getTextSection().getBlock(diff); + if(NULL==pDiff) return E_OUTOFMEMORY; + memset(pDiff,0,diff); + } + + unsigned entryPointOffset = getTextSection().dataLen(); + + if (m_dllSwitch) + { + UCHAR *dllMainBuf = (UCHAR*)getTextSection().getBlock(sizeof(DllMainIA64Template)); + if (dllMainBuf==NULL) return E_OUTOFMEMORY; + memcpy(dllMainBuf, DllMainIA64Template, sizeof(DllMainIA64Template)); + } + else + { + UCHAR *exeMainBuf = (UCHAR*)getTextSection().getBlock(sizeof(ExeMainIA64Template)); + if (exeMainBuf==NULL) return E_OUTOFMEMORY; + memcpy(exeMainBuf, ExeMainIA64Template, sizeof(ExeMainIA64Template)); + } + + // Put the entry point function pointer into the PE file + unsigned entryPlabelOffset = getTextSection().dataLen(); + getPEWriter().setEntryPointTextOffset(entryPlabelOffset); + + UCHAR * entryPtr = (UCHAR*)getTextSection().getBlock(sizeof(ULONGLONG)); + UCHAR * gpPtr = (UCHAR*)getTextSection().getBlock(sizeof(ULONGLONG)); + + memset(entryPtr,0,sizeof(ULONGLONG)); + memset(gpPtr,0,sizeof(ULONGLONG)); + + setAddrReloc(entryPtr, entryPointOffset); + addAddrReloc(getTextSection(), entryPtr, entryPlabelOffset, &getTextSection()); + + setAddrReloc(gpPtr, m_iDataDlls[0].m_iatOffset + m_iDataOffsetIAT); + addAddrReloc(getTextSection(), gpPtr, entryPlabelOffset+8, m_iDataSectionIAT); + } + else + { + _ASSERTE(!"Unknown target machine"); + } + + // Now set our IAT entry since we're using the IAT + setDirectoryEntry(*m_iDataSectionIAT, IMAGE_DIRECTORY_ENTRY_IAT, iDataSizeIAT, m_iDataOffsetIAT); + + return S_OK; +} // HRESULT CeeFileGenWriter::emitExeMain() + +// Like CreateProcess(), but waits for execution to finish +// Returns true if successful, false on failure. +// dwExitCode set to process' exitcode + +HRESULT CopySystemDirectory(__in WCHAR* pPath, + __out_ecount_part(cchBuffer, *pdwLength) LPWSTR pbuffer, + DWORD cchBuffer, + __out DWORD* pdwLength) +{ + if (pdwLength == NULL) + return E_POINTER; + + HRESULT hr = S_OK; + if(pPath == NULL) + return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + + SIZE_T dwPath = wcslen(pPath); + LPWSTR pSep = wcsrchr(pPath, L'\\'); + if(pSep) { + dwPath = (DWORD)(pSep-pPath+1); + pPath[dwPath] = L'\0'; + } + + dwPath++; // Add back in the null + *pdwLength = (DWORD)dwPath; + if(dwPath > cchBuffer || pbuffer == NULL) + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + else { + CopyMemory(pbuffer, + pPath, + dwPath*sizeof(WCHAR)); + } + return hr; +} + +HRESULT GetClrSystemDirectory(__out_ecount_part(cchBuffer, *pdwLength) LPWSTR pbuffer, + DWORD cchBuffer, + __out DWORD* pdwLength) +{ + HRESULT hr = S_OK; + + if(pdwLength == NULL) + return E_POINTER; + + WCHAR pPath[MAX_PATH]; + DWORD dwPath = MAX_PATH; + + + _ASSERTE (g_hThisInst); + + dwPath = WszGetModuleFileName(g_hThisInst, pPath, dwPath); + if(dwPath == 0) + { + hr = HRESULT_FROM_GetLastErrorNA(); + return (hr); + } + else + return CopySystemDirectory(pPath, pbuffer, cchBuffer, pdwLength); +} + + +BOOL RunProcess(LPCWSTR tempResObj, LPCWSTR pszFilename, DWORD* pdwExitCode, PEWriter &pewriter) +{ + BOOL fSuccess = FALSE; + + PROCESS_INFORMATION pi; + + DWORD cchSystemDir = MAX_PATH + 1; + DWORD dwLen; + WCHAR wszSystemDir[MAX_PATH + 1]; + if (FAILED(GetClrSystemDirectory(wszSystemDir, cchSystemDir, &dwLen))) + return FALSE; + + WCHAR* wzMachine; + if(pewriter.isIA64()) + wzMachine = L"IA64"; + else if(pewriter.isAMD64()) + wzMachine = L"AMD64"; + else if(pewriter.isARM()) + wzMachine = L"ARM"; + else + wzMachine = L"IX86"; + + // Res file, so convert it + StackSString ssCmdLine; + + LPWSTR ext = PathFindExtension(pszFilename); + if(*ext == NULL) + { + ssCmdLine.Printf(L"%scvtres.exe /NOLOGO /READONLY /MACHINE:%s \"/OUT:%s\" \"%s.\"", + wszSystemDir, + wzMachine, + tempResObj, + pszFilename); + } + else + { + ssCmdLine.Printf(L"%scvtres.exe /NOLOGO /READONLY /MACHINE:%s \"/OUT:%s\" \"%s\"", + wszSystemDir, + wzMachine, + tempResObj, + pszFilename); + } + + STARTUPINFOW start; + ZeroMemory(&start, sizeof(start)); + start.cb = sizeof(start); + start.dwFlags = STARTF_USESHOWWINDOW; + start.wShowWindow = SW_HIDE; + + fSuccess = WszCreateProcess( + NULL, + ssCmdLine.GetUnicode(), + NULL, + NULL, + true, + 0, + 0, + NULL, + &start, + &pi); + + // If process runs, wait for it to finish + if (fSuccess) { + WaitForSingleObject(pi.hProcess, INFINITE); + + GetExitCodeProcess(pi.hProcess, pdwExitCode); + + CloseHandle(pi.hProcess); + + CloseHandle(pi.hThread); + } + return fSuccess; +} // BOOL RunProcess() + +// Ensure that pszFilename is an object file (not just a binary resource) +// If we convert, then return obj filename in pszTempFilename +HRESULT ConvertResource(const WCHAR * pszFilename, __in_ecount(cchTempFilename) WCHAR *pszTempFilename, size_t cchTempFilename, PEWriter &pewriter) +{ + HANDLE hFile = WszCreateFile(pszFilename, GENERIC_READ, + FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + +// failure + if (!hFile || (hFile == INVALID_HANDLE_VALUE)) + { + //dbprintf("Can't find resource files:%S\n", pszFilename); + return HRESULT_FROM_GetLastError(); + } + +// Read first 4 bytes. If they're all 0, we have a win32 .res file which must be +// converted. (So call CvtRes.exe). Else it's an obj file. + + DWORD dwCount = -1; + DWORD dwData = -1; + + BOOL fRet = ReadFile(hFile, + &dwData, + 4, + &dwCount, + NULL + ); + + CloseHandle(hFile); + + if (!fRet) { + //dbprintf("Invalid resource file:%S\n", pszFilename); + return HRESULT_FROM_GetLastError(); + } + + if (dwData != 0) + { + return S_OK; + } + + WCHAR tempResObj[MAX_PATH+1]; + WCHAR tempResPath[MAX_PATH+1]; + + // Create the temp file where the temp path is at rather than where the application is at. + if (!WszGetTempPath(MAX_PATH, tempResPath)) + { + return HRESULT_FROM_GetLastError(); + } + + if (!WszGetTempFileName(tempResPath, L"RES", 0, tempResObj)) + { + //dbprintf("GetTempFileName failed\n"); + return HRESULT_FROM_GetLastError(); + } + + DWORD dwExitCode; + fRet = RunProcess(tempResObj, pszFilename, &dwExitCode, pewriter); + + if (!fRet) + { // Couldn't run cvtres.exe + return PostError(CEE_E_CVTRES_NOT_FOUND); + } + else if (dwExitCode != 0) + { // CvtRes.exe ran, but failed + return HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND); + } + else + { // Conversion succesful, so return filename. + wcscpy_s(pszTempFilename, cchTempFilename, tempResObj); + } + + return S_OK; +} // HRESULT ConvertResource() + + + +// This function reads a resource file and emits it into the generated PE file. +// 1. We can only link resources in obj format. Must convert from .res to .obj +// with CvtRes.exe. +// 2. Must touch up all COFF relocs from .rsrc$01 (resource header) to .rsrc$02 +// (resource raw data) +HRESULT CeeFileGenWriter::emitResourceSection() +{ + if (m_resourceFileName == NULL) + return S_OK; + + // Make sure szResFileName is an obj, not just a .res; change name if we convert + WCHAR szTempFileName[MAX_PATH+1]; + szTempFileName[0] = L'\0'; + HRESULT hr = ConvertResource(m_resourceFileName, szTempFileName, + MAX_PATH+1, getPEWriter()); + if (FAILED(hr)) return hr; + + // Filename may change (if we convert .res to .obj), so have floating pointer + const WCHAR* szResFileName; + if (*szTempFileName) + szResFileName = szTempFileName; + else + szResFileName = m_resourceFileName; + + _ASSERTE(szResFileName); + + // read the resource file and spit it out in the .rsrc section + + HANDLE hFile = INVALID_HANDLE_VALUE; + HANDLE hMap = NULL; + IMAGE_FILE_HEADER *hMod = NULL; + SIZE_T cbFileSize; + const BYTE *pbStartOfMappedMem; + IMAGE_SECTION_HEADER *rsrc[2] = { NULL, NULL }; + + S_SIZE_T cbTotalSizeOfRawData; + + hr = S_OK; + + WIN_PAL_TRY + { + // create a mapped view of the .res file + hFile = WszCreateFile(szResFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) + { + //dbprintf("Resource file %S not found\n", szResFileName); + hr = HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND); + goto lDone; + } + + // Grab the file size for verification checks. + { + DWORD dwFileSizeHigh; + DWORD dwFileSize = SafeGetFileSize(hFile, &dwFileSizeHigh); + if (dwFileSize == (DWORD)(-1)) + { + hr = HRESULT_FROM_GetLastError(); + goto lDone; + } + + // Since we intend to memory map this file, the size of the file can not need 64 bits to represent! + if (dwFileSizeHigh != 0) + { + hr = HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND); + goto lDone; + } + + cbFileSize = static_cast(dwFileSize); + } + + hMap = WszCreateFileMapping(hFile, 0, PAGE_READONLY, 0, 0, NULL); + + if (hMap == NULL) + { + //dbprintf("Invalid .res file: %S\n", szResFileName); + hr = HRESULT_FROM_GetLastError(); + goto lDone; + } + + pbStartOfMappedMem = reinterpret_cast(MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0)); + + // test failure conditions + if (pbStartOfMappedMem == NULL) + { + //dbprintf("Invalid .res file: %S:Can't get header\n", szResFileName); + hr = HRESULT_FROM_GetLastError(); + goto lDone; + } + + // Check that the file contains an IMAGE_FILE_HEADER structure. + if (IMAGE_SIZEOF_FILE_HEADER > cbFileSize) + { + hr = HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND); + goto lDone; + } + + hMod = (IMAGE_FILE_HEADER*) pbStartOfMappedMem; + + if (VAL16(hMod->SizeOfOptionalHeader) != 0) + { + //dbprintf("Invalid .res file: %S:Illegal optional header\n", szResFileName); + hr = HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND); // GetLastError() = 0 since API worked. + goto lDone; + } + + // Scan all section headers and grab .rsrc$01 and .rsrc$02 + { + // First section is directly after header + SIZE_T cSections = static_cast(VAL16(hMod->NumberOfSections)); + SIZE_T cbStartOfSections = IMAGE_SIZEOF_FILE_HEADER; + S_SIZE_T cbEndOfSections(S_SIZE_T(cbStartOfSections) + + (S_SIZE_T(cSections) * S_SIZE_T(IMAGE_SIZEOF_SECTION_HEADER))); + + // Check that all sections are within the bounds of the mapped file. + if (cbEndOfSections.IsOverflow() || + cbEndOfSections.Value() > cbFileSize) + { + hr = HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND); + goto lDone; + } + + { + IMAGE_SECTION_HEADER *pSection = + (IMAGE_SECTION_HEADER *)(pbStartOfMappedMem + cbStartOfSections); + IMAGE_SECTION_HEADER *pSectionEnd = pSection + cSections; + + for (; pSection < pSectionEnd; pSection++) + { + if (strcmp(".rsrc$01", (char *)pSection->Name) == 0) + { + rsrc[0] = pSection; + } + else if (strcmp(".rsrc$02", (char *)pSection->Name) == 0) + { + rsrc[1] = pSection; + } + } + } + } + + // If we don't have both resources, fail. + if (!rsrc[0] || !rsrc[1]) + { + //dbprintf("Invalid .res file: %S: Missing sections .rsrc$01 or .rsrc$02\n", szResFileName); + hr = HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND); + goto lDone; + } + + // Verify the resource data starts and sizes + { + cbTotalSizeOfRawData = S_SIZE_T(0); + + for (int i = 0; i < 2; i++) + { + S_SIZE_T cbStartOfResourceData(static_cast(VAL32(rsrc[i]->PointerToRawData))); + S_SIZE_T cbSizeOfResourceData(static_cast(VAL32(rsrc[i]->SizeOfRawData))); + S_SIZE_T cbEndOfResourceData(cbStartOfResourceData + cbSizeOfResourceData); + + if (cbEndOfResourceData.IsOverflow() || + cbEndOfResourceData.Value() > cbFileSize) + { + hr = HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND); + goto lDone; + } + + cbTotalSizeOfRawData += cbSizeOfResourceData; + } + + // Check that the total raw data doesn't overflow. + if (cbTotalSizeOfRawData.IsOverflow() || + cbTotalSizeOfRawData.Value() > cbFileSize) + { + hr = HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND); + goto lDone; + } + } + + PESection *rsrcSection; + hr = getPEWriter().getSectionCreate(".rsrc", sdReadOnly, &rsrcSection); + if (FAILED(hr)) goto lDone; + + rsrcSection->directoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE); + char *data = rsrcSection->getBlock(static_cast(cbTotalSizeOfRawData.Value()), 8); + if(data == NULL) + { + hr = E_OUTOFMEMORY; + goto lDone; + } + + // Copy resource header + memcpy(data, (char *)hMod + VAL32(rsrc[0]->PointerToRawData), VAL32(rsrc[0]->SizeOfRawData)); + + // Map all the relocs in .rsrc$01 using the reloc and symbol tables in the COFF object., + SIZE_T cReloc = 0; // Total number of relocs + IMAGE_RELOCATION *pReloc = NULL; // Reloc table start + + SIZE_T cSymbol = 0; // Total number of symbols + IMAGE_SYMBOL *pSymbolTable = NULL; // Symbol table start + + { + // Check that the relocations and symbols lie within the resource + cReloc = VAL16(rsrc[0]->NumberOfRelocations); + SIZE_T cbStartOfRelocations = static_cast(VAL32(rsrc[0]->PointerToRelocations)); + S_SIZE_T cbEndOfRelocations(S_SIZE_T(cbStartOfRelocations) + + (S_SIZE_T(cReloc) * S_SIZE_T(sizeof(IMAGE_RELOCATION)))); + + + // Verify the number of symbols fit into the resource. + cSymbol = static_cast(VAL32(hMod->NumberOfSymbols)); + SIZE_T cbStartOfSymbolTable = static_cast(VAL32(hMod->PointerToSymbolTable)); + S_SIZE_T cbEndOfSymbolTable(S_SIZE_T(cbStartOfSymbolTable) + + (S_SIZE_T(cSymbol) * S_SIZE_T(IMAGE_SIZEOF_SYMBOL))); + + if (cbEndOfRelocations.IsOverflow() || + cbEndOfRelocations.Value() > cbFileSize || + cbEndOfSymbolTable.IsOverflow() || + cbEndOfSymbolTable.Value() > cbFileSize) + { + hr = HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND); + goto lDone; + } + + pReloc = (IMAGE_RELOCATION *)(pbStartOfMappedMem + cbStartOfRelocations); + pSymbolTable = (IMAGE_SYMBOL *)(pbStartOfMappedMem + cbStartOfSymbolTable); + } + + _ASSERTE(pReloc != NULL && pSymbolTable != NULL); + + for(SIZE_T iReloc = 0; iReloc < cReloc; iReloc++, pReloc++) + { + // Ensure this is a valid reloc + { + S_SIZE_T cbRelocEnd = S_SIZE_T(VAL32(pReloc->VirtualAddress)) + S_SIZE_T(sizeof(DWORD)); + if (cbRelocEnd.IsOverflow() || + cbRelocEnd.Value() > static_cast(VAL32(rsrc[0]->SizeOfRawData))) + { + hr = HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND); + goto lDone; + } + } + + // index into symbol table, provides address into $02 + DWORD iSymbol = VAL32(pReloc->SymbolTableIndex); + + // Make sure the index is in range + if (iSymbol >= cSymbol) + { + hr = HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND); + goto lDone; + } + + IMAGE_SYMBOL* pSymbolEntry = GetSymbolEntry(pSymbolTable, iSymbol); + + // Ensure the symbol entry is valid for a resource. + if ((pSymbolEntry->StorageClass != IMAGE_SYM_CLASS_STATIC) || + (VAL16(pSymbolEntry->Type) != IMAGE_SYM_TYPE_NULL) || + (VAL16(pSymbolEntry->SectionNumber) != 3)) // 3rd section is .rsrc$02 + { + //dbprintf("Invalid .res file: %S:Illegal symbol entry\n", szResFileName); + hr = HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND); + goto lDone; + } + + // Ensure that RVA is valid address (inside rsrc[1]) + if (VAL32(pSymbolEntry->Value) >= VAL32(rsrc[1]->SizeOfRawData)) + { + //dbprintf("Invalid .res file: %S:Illegal rva into .rsrc$02\n", szResFileName); + hr = HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND); + goto lDone; + } + + DWORD dwOffsetInRsrc2 = VAL32(pSymbolEntry->Value) + VAL32(rsrc[0]->SizeOfRawData); + + // Create reloc + *(DWORD*)(data + VAL32(pReloc->VirtualAddress)) = VAL32(dwOffsetInRsrc2); + rsrcSection->addSectReloc(pReloc->VirtualAddress, rsrcSection, srRelocAbsolute); + } + + // Copy $02 (resource raw) data + memcpy(data+VAL32(rsrc[0]->SizeOfRawData), + (char *)hMod + VAL32(rsrc[1]->PointerToRawData), + VAL32(rsrc[1]->SizeOfRawData)); + +lDone: ; + } + WIN_PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + //dbprintf("Exception occured manipulating .res file %S\n", szResFileName); + hr = HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND); + } + WIN_PAL_ENDTRY + + if (hMod != NULL) + UnmapViewOfFile(hMod); + if (hMap != NULL) + CloseHandle(hMap); + if (hFile != INVALID_HANDLE_VALUE) + CloseHandle(hFile); + if (szResFileName == szTempFileName) + // delete temporary file if we created one + WszDeleteFile(szResFileName); + + return hr; +} // HRESULT CeeFileGenWriter::emitResourceSection() + +HRESULT CeeFileGenWriter::setManifestEntry(ULONG size, ULONG offset) +{ + if (offset) + m_dwManifestRVA = offset; + else { + CeeSection TextSection = getTextSection(); + getMethodRVA(TextSection.dataLen() - size, &m_dwManifestRVA); + } + + m_dwManifestSize = size; + return S_OK; +} // HRESULT CeeFileGenWriter::setManifestEntry() + +HRESULT CeeFileGenWriter::setStrongNameEntry(ULONG size, ULONG offset) +{ + m_dwStrongNameRVA = offset; + m_dwStrongNameSize = size; + return S_OK; +} // HRESULT CeeFileGenWriter::setStrongNameEntry() + +HRESULT CeeFileGenWriter::setVTableEntry64(ULONG size, void* ptr) +{ + if (ptr && size) + { + void * pv; + CeeSection TextSection = getTextSection(); + // make it DWORD-aligned + ULONG L = TextSection.dataLen(); + if((L &= ((ULONG)sizeof(DWORD)-1))) + { + L = (ULONG)sizeof(DWORD) - L; + if((pv = TextSection.getBlock(L))) + memset(pv,0,L); + else + return E_OUTOFMEMORY; + } + getMethodRVA(TextSection.dataLen(), &m_dwVTableRVA); + if((pv = TextSection.getBlock(size))) + { + memcpy(pv,ptr,size); + } + else + return E_OUTOFMEMORY; + m_dwVTableSize = size; + } + + return S_OK; +} // HRESULT CeeFileGenWriter::setVTableEntry() + +HRESULT CeeFileGenWriter::setVTableEntry(ULONG size, ULONG offset) +{ + return setVTableEntry64(size,(void*)(ULONG_PTR)offset); +} // HRESULT CeeFileGenWriter::setVTableEntry() + +HRESULT CeeFileGenWriter::setEnCRvaBase(ULONG dataBase, ULONG rdataBase) +{ + setEnCMode(); + getPEWriter().setEnCRvaBase(dataBase, rdataBase); + return S_OK; +} // HRESULT CeeFileGenWriter::setEnCRvaBase() + +HRESULT CeeFileGenWriter::computeSectionOffset(CeeSection §ion, __in char *ptr, + unsigned *offset) +{ + *offset = section.computeOffset(ptr); + + return S_OK; +} // HRESULT CeeFileGenWriter::computeSectionOffset() + +HRESULT CeeFileGenWriter::computeOffset(__in char *ptr, + CeeSection **pSection, unsigned *offset) +{ + TESTANDRETURNPOINTER(pSection); + + CeeSection **s = m_sections; + CeeSection **sEnd = s + m_numSections; + while (s < sEnd) + { + if ((*s)->containsPointer(ptr)) + { + *pSection = *s; + *offset = (*s)->computeOffset(ptr); + + return S_OK; + } + s++; + } + + return E_FAIL; +} // HRESULT CeeFileGenWriter::computeOffset() + +HRESULT CeeFileGenWriter::getCorHeader(IMAGE_COR20_HEADER **ppHeader) +{ + *ppHeader = m_corHeader; + return S_OK; +} // HRESULT CeeFileGenWriter::getCorHeader() + + +#ifdef EMIT_FIXUPS + +HRESULT CeeFileGenWriter::InitFixupSection() +{ + if (!m_fEmitFixups) + { + return(E_UNEXPECTED); + } + + HRESULT hr; + + hr = getSectionCreate(".fixups", + IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ, + &m_sectionFixups); + if (SUCCEEDED(hr)) + { + size_t cbDebugDir = sizeof(IMAGE_DEBUG_DIRECTORY); + hr = GetSectionBlock(m_sectionFixups, (ULONG) cbDebugDir, 32, (void **) &m_pDebugDir); + if (SUCCEEDED(hr)) + { + memset(m_pDebugDir, 0, cbDebugDir); + m_pDebugDir->Type = IMAGE_DEBUG_TYPE_FIXUP; + m_fFixupsUpdated = false; + + return(S_OK); + } + } + + m_pDebugDir = NULL; + m_sectionFixups = NULL; + m_fEmitFixups = false; + + return(E_FAIL); + +} // HRESULT CeeFileGenWriter::InitFixupSection() + +HRESULT CeeFileGenWriter::addFixup(CeeSection& sectionSource, unsigned offset, CeeSectionRelocType relocType, CeeSection * psectionTarget, CeeSectionRelocExtra *extra) +{ + if (!m_fEmitFixups) + { + return(S_OK); + } + + _ASSERTE(sizeof(DBG_FIXUP) == sizeof(PEFIXUP)); + _ASSERTE(m_fFixupsUpdated == false); + + DBG_FIXUP * pfixup; + + if (m_sectionFixups == NULL) + { + HRESULT hr = InitFixupSection(); + if (FAILED(hr)) + { + return(hr); + } + + // The fixup section begins with a IMAGE_DEBUG_DIRECTORY containing a + // IMAGE_DEBUG_TYPE_FIXUP directory entry, which describes the array + // of fixups which follows it. + + // The very first item of this array is aligned on a 32 bit boundary. + // All other fixup entries follow unaligned. + pfixup = (DBG_FIXUP *) m_sectionFixups->getBlock(sizeof(DBG_FIXUP), 32); + TESTANDRETURN(pfixup != NULL, E_OUTOFMEMORY); + + // Initialize the IMAGE_DEBUG_TYPE_FIXUP entry relocations +#ifdef _WIN64 + _ASSERTE(!"Base relocs are not yet implemented for 64-bit"); + m_pDebugDir->AddressOfRawData = 0; // @ToDo: srRelocAbsolutePtr can't take a 64-bit address +#else + m_pDebugDir->AddressOfRawData = (size_t) pfixup; + m_sectionFixups->addSectReloc(offsetof(IMAGE_DEBUG_DIRECTORY, AddressOfRawData), *m_sectionFixups, srRelocAbsolutePtr); +#endif + + m_pDebugDir->PointerToRawData = m_sectionFixups->computeOffset((char *) pfixup); + + m_sectionFixups->addSectReloc(offsetof(IMAGE_DEBUG_DIRECTORY, PointerToRawData), *m_sectionFixups, srRelocFilePos); + + unsigned offsetDir = m_sectionFixups->computeOffset((char *) m_pDebugDir); + setDirectoryEntry(*m_sectionFixups, IMAGE_DIRECTORY_ENTRY_DEBUG, sizeof(IMAGE_DEBUG_DIRECTORY), offsetDir); + +#ifdef TEST_EMIT_FIXUPS + TestEmitFixups(); +#endif + } + else + { + pfixup = (DBG_FIXUP *) m_sectionFixups->getBlock(sizeof(DBG_FIXUP), 1); + TESTANDRETURN(pfixup != NULL, E_OUTOFMEMORY); + } + + // Save off the relocation information for use later. The relocation's + // target information can be filled in later. + // The relocation target info is not always immediately available, so it needs + // to be extracted later, during the link phase. For now the relocation info + // is stored so the target can be extracted at link time in the UpdateFixups + // function. + // + unsigned offsetFixup = m_sectionFixups->computeOffset((char *) pfixup); + pfixup->wSpare = 0; + pfixup->wType = relocType; + _ASSERTE(pfixup->wType == relocType); + pfixup->offset = offset; + pfixup->sectionSource = §ionSource; + + m_pDebugDir->SizeOfData += sizeof(DBG_FIXUP); + + // Add a relocation for the fixup's source RVA field, (no fixup on this reloc) + m_sectionFixups->addSectReloc(offsetFixup + offsetof(DBG_FIXUP, rva), sectionSource, srRelocAbsolutePtr); + + // Add a relocation for the fixup's target RVA field. Correct target extracted + // later in UpdateFixups, (no fixup on this reloc) + CeeSectionRelocType tgtRelocType; + + switch (relocType) + { + case srRelocMapToken: + // not an RVA + tgtRelocType = srRelocMapToken; + break; + + case srRelocFilePos: + tgtRelocType = srRelocFilePos; + break; + + case srRelocHighAdj: + tgtRelocType = srRelocHighAdj; + break; + + default: + tgtRelocType = (relocType & srRelocPtr) ? srRelocAbsolutePtr : srRelocAbsolute; + break; + } + + if (psectionTarget != NULL) + { + m_sectionFixups->addSectReloc(offsetFixup + offsetof(DBG_FIXUP, rvaTarget), *psectionTarget, tgtRelocType, extra); + } + else + { + m_sectionFixups->addBaseReloc(offsetFixup + offsetof(DBG_FIXUP, rvaTarget), tgtRelocType, extra); + } + + return(S_OK); +} // HRESULT CeeFileGenWriter::addFixup() + +HRESULT CeeFileGenWriter::UpdateFixups() +{ + // This method extracts the correct relocation target. See addFixup method. + + if (!m_fEmitFixups || m_fFixupsUpdated) + { + return(S_OK); + } + m_fFixupsUpdated = true; // prevent UpdateFixups from being called again. + + size_t cfixups = m_pDebugDir->SizeOfData / sizeof(DBG_FIXUP); + _ASSERT(m_pDebugDir->SizeOfData % sizeof(DBG_FIXUP) == 0); + unsigned ibFixup = m_pDebugDir->PointerToRawData; + + for (size_t idx = 0; idx < cfixups; idx++, ibFixup += sizeof(DBG_FIXUP)) + { + DBG_FIXUP * pfixup = (DBG_FIXUP *) m_sectionFixups->computePointer(ibFixup); + CeeSection * sectionSource = pfixup->sectionSource; + CeeSectionRelocType relocType = (CeeSectionRelocType) pfixup->wType; + unsigned offset = pfixup->offset; + + // Get current data for replacing fixup contents + const DWORD * pdw = (DWORD *) sectionSource->computePointer(offset); + pfixup->rva = (DWORD) (UINT_PTR) pdw; + pfixup->rvaTarget = *pdw; + + switch (relocType) + { +#ifdef _X86_ + case srRelocAbsolute: + // Emitted bytes: RVA, offset relative to image base + // reloc src contains target offset relative to target section + if ((*pdw & 0xFF000000) == 0) + { + pfixup->wType = IMAGE_REL_I386_DIR32NB; + } + else + { + // MethodDesc::Fixup function creates a 24 bit RVA, where the + // high byte of the DWORD stores the flag value: METHOD_NEEDS_PRESTUB_RUN_FLAG. + // work around it by converting the type to 24 bits here + pfixup->wType = IMAGE_REL_I386_DIR24NB; + pfixup->rvaTarget = *pdw & 0x00FFFFFF; + } + break; + + case srRelocAbsolutePtr: + // Emitted bytes: RVA, offset relative to image base + // reloc src contains target pointer + pfixup->wType = IMAGE_REL_I386_DIR32NB; + break; + + case srRelocHighLow: + // Emitted bytes: full address of target + // reloc src contains target offset relative to target section + pfixup->wType = IMAGE_REL_I386_DIR32; + break; + + case srRelocHighLowPtr: + // Emitted bytes: full address of target + // reloc src contains target pointer + pfixup->wType = IMAGE_REL_I386_DIR32; + break; + + case srRelocRelative: + // Emitted bytes: value of reloc tgt - (reloc source + sizeof(DWORD)) + // reloc src contains offset relative to target section, minus sizeof(DWORD) + // the reloc type for pFixup->rvaTarget is srRelocAbsolute + // so contents of pFixup->rvaTarget need to be offset Target + sizeof(DWORD) + // which is offset Target == Source contents + sizeof(DWORD) == *pdw + sizeof(DWORD) + pfixup->wType = IMAGE_REL_I386_REL32; + pfixup->rvaTarget = *pdw + sizeof(DWORD); + break; + + case srRelocRelativePtr: + // Emitted bytes: value of reloc tgt - (reloc source + sizeof(DWORD)) + // reloc src contains disp, disp = pTarget - (pSource + sizeof(DWORD)) + // the reloc type for pFixup->rvaTarget is srRelocAbsolutePtr + // so contents of pFixup->rvaTarget need to be pTarget + // which is pTarget == pSource + sizeof(DWORD) + disp == pdw + 4 + *pdw + pfixup->wType = IMAGE_REL_I386_REL32; + pfixup->rvaTarget = (int) (INT_PTR) pdw + sizeof(DWORD) + (int) *pdw; + break; + + case srRelocMapToken: + // Emitted bytes: contents of reloc source unchanged. + // reloc src contains token value + pfixup->wType = IMAGE_REL_I386_TOKEN; + break; + +#elif defined(_AMD64_) + /* + // + // X86-64 relocations + // + IMAGE_REL_AMD64_ABSOLUTE 0x0000 // Reference is absolute, no relocation is necessary + IMAGE_REL_AMD64_ADDR64 0x0001 // 64-bit address (VA). + IMAGE_REL_AMD64_ADDR32 0x0002 // 32-bit address (VA). + IMAGE_REL_AMD64_ADDR32NB 0x0003 // 32-bit address w/o image base (RVA). + IMAGE_REL_AMD64_REL32 0x0004 // 32-bit relative address from byte following reloc + IMAGE_REL_AMD64_REL32_1 0x0005 // 32-bit relative address from byte distance 1 from reloc + IMAGE_REL_AMD64_REL32_2 0x0006 // 32-bit relative address from byte distance 2 from reloc + IMAGE_REL_AMD64_REL32_3 0x0007 // 32-bit relative address from byte distance 3 from reloc + IMAGE_REL_AMD64_REL32_4 0x0008 // 32-bit relative address from byte distance 4 from reloc + IMAGE_REL_AMD64_REL32_5 0x0009 // 32-bit relative address from byte distance 5 from reloc + IMAGE_REL_AMD64_SECTION 0x000A // Section index + IMAGE_REL_AMD64_SECREL 0x000B // 32 bit offset from base of section containing target + IMAGE_REL_AMD64_SECREL7 0x000C // 7 bit unsigned offset from base of section containing target + IMAGE_REL_AMD64_TOKEN 0x000D // 32 bit metadata token + IMAGE_REL_AMD64_SREL32 0x000E // 32 bit signed span-dependent value emitted into object + IMAGE_REL_AMD64_PAIR 0x000F + IMAGE_REL_AMD64_SSPAN32 0x0010 // 32 bit signed span-dependent value applied at link time + */ + case srRelocAbsolute: + // Emitted bytes: RVA, offset relative to image base + pfixup->wType = IMAGE_REL_AMD64_ADDR32NB; + break; + + case srRelocAbsolutePtr: + // Emitted bytes: RVA, offset relative to image base + // reloc src contains target pointer + pfixup->wType = IMAGE_REL_AMD64_ADDR32NB; + break; + + case srRelocDir64Ptr: + // Emitted bytes: full address of target + // reloc src contains target pointer + pfixup->wType = IMAGE_REL_IA64_DIR64; + break; + + case srRelocMapToken: + // Emitted bytes: contents of reloc source unchanged. + // reloc src contains token value + pfixup->wType = IMAGE_REL_AMD64_TOKEN; + break; +#endif + case srRelocFilePos: + // Emitted bytes: offset relative to start of file, differs from RVA. + pfixup->wType = IMAGE_REL_I386_FILEPOS; + break; + + case srRelocAbsoluteTagged: + pfixup->wType = IMAGE_REL_I386_DIR30NB; + pfixup->rvaTarget = (*pdw & ~0x80000001) >> 1; + break; + + case srRelocHighAdj: + // Emitted bytes: 2 part relocation, with high part adjusted by constant. + pfixup->wType = IMAGE_REL_BASED_HIGHADJ; + break; + + default: + _ASSERTE(!"Unknown relocation type"); + return(E_UNEXPECTED); + break; + } + } + + return(S_OK); + +} // HRESULT CeeFileGenWriter::UpdateFixups() + + +HRESULT CeeFileGenWriter::setEmitFixups() +{ + m_fEmitFixups = true; + return(S_OK); + +} // HRESULT CeeFileGenWriter::setEmitFixups() + +#ifdef TEST_EMIT_FIXUPS + +HRESULT CeeFileGenWriter::TestEmitFixups() +{ + HRESULT hr; + // Test fixups + + CeeSection * testSection; + hr = getSectionCreate(".test", + IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ, + &testSection); + if (SUCCEEDED(hr)) + { + struct FixupEntry + { + char sz[18]; + DWORD wTargets[8]; + }; + + struct FixupTypes + { + char * pszType; + CeeSectionRelocType relocType; + }; + + FixupTypes rgTypes[] = + { + { "srRelocAbsolute ", srRelocAbsolute }, + { "srRelocAbsolutePtr", srRelocAbsolutePtr }, + { "srRelocHighLow ", srRelocHighLow }, + { "srRelocHighLowPtr ", srRelocHighLowPtr }, + // { "srRelocRelative ", srRelocRelative }, + // { "srRelocRelativePtr", srRelocRelativePtr }, + { "srRelocMapToken ", srRelocMapToken }, + // { "srRelocFilePos ", srRelocFilePos }, + // { "srRelocHighAdj ", srRelocHighAdj }, + }; + + const size_t cFixups = sizeof(rgTypes) / sizeof(rgTypes[0]); + + DWORD * pdwTargets[20]; + + // Target Blocks: + + for (size_t idx = 0; idx < cFixups; idx++) + { + hr = GetSectionBlock(testSection, sizeof(DWORD), 1, (void **) &pdwTargets[idx]); + _ASSERTE(SUCCEEDED(hr)); + + DWORD * pdw = pdwTargets[idx]; + *pdw = idx; + } + + for (size_t idxType = 0; idxType < cFixups; idxType++) + { + // Fixup Entries + FixupEntry * pEntry; + hr = GetSectionBlock(testSection, sizeof(FixupEntry), 1, (void **) &pEntry); + _ASSERTE(SUCCEEDED(hr)); + + memset(pEntry, 0, sizeof(FixupEntry)); + strcpy_s(pEntry->sz, sizeof(pEntry->sz), rgTypes[idxType].pszType); + + size_t ibBlock = testSection->computeOffset((char *) pEntry); + + for (size_t idx = 0; idx < cFixups; idx++) + { + size_t ibFixup = ((size_t) &pEntry->wTargets[idx]) - (size_t) pEntry; + + switch (rgTypes[idxType].relocType) + { + case srRelocAbsolute: + pEntry->wTargets[idx] = idx * sizeof(DWORD); + break; + + case srRelocAbsolutePtr: + pEntry->wTargets[idx] = (DWORD) pdwTargets[idx]; + break; + + case srRelocHighLow: + pEntry->wTargets[idx] = idx * sizeof(DWORD); + break; + + case srRelocHighLowPtr: + pEntry->wTargets[idx] = (DWORD) pdwTargets[idx]; + break; + + case srRelocRelative: + pEntry->wTargets[idx] = idx; + break; + + case srRelocRelativePtr: + { + size_t ibTgt = (size_t) pdwTargets[idx]; + size_t ibSrc = ((size_t) &pEntry->wTargets[idx]) + sizeof(DWORD); + pEntry->wTargets[idx] = (DWORD)( ibTgt - ibSrc ); + ibFixup += sizeof(DWORD); // offset needs to point at end of DWORD + break; + } + + case srRelocHighAdj: + pEntry->wTargets[idx] = idx * sizeof(DWORD); + break; + + case srRelocMapToken: + pEntry->wTargets[idx] = idx * sizeof(DWORD); + break; + + case srRelocFilePos: + pEntry->wTargets[idx] = idx * sizeof(DWORD); + break; + } + + addFixup(*testSection, ibBlock + ibFixup, rgTypes[idxType].relocType, testSection); + testSection->addSectReloc(ibBlock + ibFixup, *testSection, rgTypes[idxType].relocType); + } + } + } + + return(S_OK); +} +#endif // TEST_EMIT_FIXUPS +#endif // EMIT_FIXUPS + +#ifndef FEATURE_MERGE_JIT_AND_ENGINE + +//***************************************************************************** +// Handle lifetime of loaded library. +//***************************************************************************** +extern "C" +BOOL WINAPI DllMain(HANDLE hInstance, DWORD dwReason, LPVOID lpReserved) +{ + switch (dwReason) + { + case DLL_PROCESS_ATTACH: + { // Save the module handle. + g_hThisInst = (HINSTANCE)hInstance; + DisableThreadLibraryCalls((HMODULE)hInstance); + } + break; + case DLL_PROCESS_DETACH: + break; + } + + return (true); +} // BOOL WINAPI DllMain() + + +HINSTANCE GetModuleInst() +{ + return (g_hThisInst); +} // HINSTANCE GetModuleInst() + +#endif //FEATURE_MERGE_JIT_AND_ENGINE diff --git a/src/dlls/mscorpe/ceefilegenwritertokens.cpp b/src/dlls/mscorpe/ceefilegenwritertokens.cpp new file mode 100644 index 0000000000..b8d5ac0d83 --- /dev/null +++ b/src/dlls/mscorpe/ceefilegenwritertokens.cpp @@ -0,0 +1,265 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//***************************************************************************** +// CeeFileGenWriterTokens.cpp +// +// This code will walk the byte codes for all methods before they are saved +// to disk and apply the token fix-ups if they have moved. +// +//***************************************************************************** +#include "stdafx.h" +#include "ceegen.h" +#define DECLARE_DATA +#include "../../ildasm/dasmenum.hpp" +#define MAX_CLASSNAME_LENGTH 1024 + +//********** Locals. ********************************************************** +OPCODE DecodeOpcode(const BYTE *pCode, DWORD *pdwLen); + + + +//********** Code. ************************************************************ + + +//***************************************************************************** +// Method bodies are kept in the text section. The meta data contains the +// RVA of each method body in the image. The TextSection object contains the +// actual raw bytes where the method bodies are located. This code will +// determine the raw offset of each method based on the RVA kept in the meta +// data. +//***************************************************************************** + +HRESULT CeeFileGenWriter::MapTokens( + CeeGenTokenMapper *pMapper, + IMetaDataImport *pImport) +{ + mdTypeDef td; + mdMethodDef md; + ULONG count; + ULONG MethodRVA; + ULONG codeOffset; + ULONG BaseRVA; + DWORD dwFlags; + DWORD iFlags; + HCORENUM hTypeDefs = 0, hEnum = 0; + WCHAR rcwName[MAX_CLASSNAME_LENGTH]; + HRESULT hr; + CeeSection TextSection = getTextSection(); + + // Ask for the base RVA of the first method in the stream. All other + // method RVA's are >= that value, and will give us the raw offset required. + + hr = getMethodRVA(0, &BaseRVA); + _ASSERTE(SUCCEEDED(hr)); + // do globals first + while ((hr = pImport->EnumMethods(&hEnum, mdTokenNil, &md, 1, &count)) == S_OK) + { + hr = pImport->GetMethodProps(md, NULL, + rcwName, lengthof(rcwName), NULL, + &dwFlags, NULL, NULL, + &MethodRVA, &iFlags); + _ASSERTE(SUCCEEDED(hr)); + + if (MethodRVA == 0 || ((IsMdAbstract(dwFlags) || IsMiInternalCall(iFlags)) || + (! IsMiIL(iFlags) && ! IsMiOPTIL(iFlags)))) + continue; + + // The raw offset of the method is the RVA in the image minus + // the first method in the text section. + codeOffset = MethodRVA - BaseRVA; + hr = MapTokensForMethod(pMapper, + (BYTE *) TextSection.computePointer(codeOffset), + rcwName); + if (FAILED(hr)) + goto ErrExit; + } + if (hEnum) pImport->CloseEnum(hEnum); + hEnum = 0; + + while ((hr = pImport->EnumTypeDefs(&hTypeDefs, &td, 1, &count)) == S_OK) + { + while ((hr = pImport->EnumMethods(&hEnum, td, &md, 1, &count)) == S_OK) + { + hr = pImport->GetMethodProps(md, NULL, + rcwName, lengthof(rcwName), NULL, + &dwFlags, NULL, NULL, + &MethodRVA, &iFlags); + _ASSERTE(SUCCEEDED(hr)); + + if (MethodRVA == 0 || ((IsMdAbstract(dwFlags) || IsMiInternalCall(iFlags)) || + (! IsMiIL(iFlags) && ! IsMiOPTIL(iFlags)))) + continue; + + + // The raw offset of the method is the RVA in the image minus + // the first method in the text section. + codeOffset = MethodRVA - BaseRVA; + hr = MapTokensForMethod(pMapper, + (BYTE *) TextSection.computePointer(codeOffset), + rcwName); + if (FAILED(hr)) + goto ErrExit; + } + + if (hEnum) pImport->CloseEnum(hEnum); + hEnum = 0; + } + +ErrExit: + if (hTypeDefs) pImport->CloseEnum(hTypeDefs); + if (hEnum) pImport->CloseEnum(hEnum); + return (hr); +} + + +//***************************************************************************** +// This method will walk the byte codes of an IL method looking for tokens. +// For each token found, it will check to see if it has been moved, and if +// so, apply the new token value in its place. +//***************************************************************************** +HRESULT CeeFileGenWriter::MapTokensForMethod( + CeeGenTokenMapper *pMapper, + BYTE *pCode, + LPCWSTR szMethodName) +{ + mdToken tkTo; + DWORD PC; + + COR_ILMETHOD_DECODER method((COR_ILMETHOD*) pCode); + + // If compressed IL is being emitted, this routine will have no idea how to walk the tokens, + // so don't do it + if (m_dwMacroDefinitionSize != 0) + return S_OK; + + pCode = const_cast(method.Code); + + PC = 0; + while (PC < method.GetCodeSize()) + { + DWORD Len; + DWORD i; + OPCODE instr; + + instr = DecodeOpcode(&pCode[PC], &Len); + + if (instr == CEE_COUNT) + { + _ASSERTE(0 && "Instruction decoding error\n"); + return E_FAIL; + } + + + PC += Len; + + switch (OpcodeInfo[instr].Type) + { + DWORD tk; + + default: + { + _ASSERTE(0 && "Unknown instruction\n"); + return E_FAIL; + } + + case InlineNone: + break; + + case ShortInlineI: + case ShortInlineVar: + case ShortInlineBrTarget: + PC++; + break; + + case InlineVar: + PC += 2; + break; + + case InlineI: + case ShortInlineR: + case InlineBrTarget: + case InlineRVA: + PC += 4; + break; + + case InlineI8: + case InlineR: + PC += 8; + break; + + case InlinePhi: + { + DWORD cases = pCode[PC]; + PC += 2 * cases + 1; + break; + } + + case InlineSwitch: + { + DWORD cases = pCode[PC] + (pCode[PC+1] << 8) + (pCode[PC+2] << 16) + (pCode[PC+3] << 24); + + PC += 4; + for (i = 0; i < cases; i++) + { + PC += 4; + } + + // skip bottom of loop which prints szString + continue; + } + + case InlineTok: + case InlineSig: + case InlineMethod: + case InlineField: + case InlineType: + case InlineString: + { + tk = GET_UNALIGNED_VAL32(&pCode[PC]); + + if (pMapper->HasTokenMoved(tk, tkTo)) + { + SET_UNALIGNED_VAL32(&pCode[PC], tkTo); + } + + PC += 4; + break; + } + } + } + + return S_OK; +} + + + + +OPCODE DecodeOpcode(const BYTE *pCode, DWORD *pdwLen) +{ + OPCODE opcode; + + *pdwLen = 1; + opcode = OPCODE(pCode[0]); + switch(opcode) { + case CEE_PREFIX1: + opcode = OPCODE(pCode[1] + 256); + if (opcode < 0 || opcode >= CEE_COUNT) + opcode = CEE_COUNT; + *pdwLen = 2; + break; + case CEE_PREFIXREF: + case CEE_PREFIX2: + case CEE_PREFIX3: + case CEE_PREFIX4: + case CEE_PREFIX5: + case CEE_PREFIX6: + case CEE_PREFIX7: + *pdwLen = 3; + return CEE_COUNT; + default: + break; + } + return opcode; +} diff --git a/src/dlls/mscorpe/dirs.proj b/src/dlls/mscorpe/dirs.proj new file mode 100644 index 0000000000..46bb0ce64d --- /dev/null +++ b/src/dlls/mscorpe/dirs.proj @@ -0,0 +1,15 @@ + + + + + + false + true + + + + + + + + diff --git a/src/dlls/mscorpe/iceefilegen.cpp b/src/dlls/mscorpe/iceefilegen.cpp new file mode 100644 index 0000000000..d41dcfd779 --- /dev/null +++ b/src/dlls/mscorpe/iceefilegen.cpp @@ -0,0 +1,733 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// =========================================================================== +// File: CEEGEN.CPP +// =========================================================================== +#include "stdafx.h" +#include "iceefilegen.h" +#include "ceefilegenwriter.h" + +#ifdef EnC_SUPPORTED +#define ENC_DELTA_HACK +#endif + +#ifdef ENC_DELTA_HACK +extern BOOL g_EnCMode; +#endif + +// Deprecated +//**************************************************************************** + HRESULT ICeeFileGen::EmitMethod () + { + _ASSERTE("Deprecated" && 0); + return (E_FAIL); + } + HRESULT ICeeFileGen::EmitSignature () + { + _ASSERTE("Deprecated" && 0); + return (E_FAIL); + } + HRESULT ICeeFileGen::SetEntryClassToken () + { + _ASSERTE("Deprecated" && 0); + return (E_FAIL); + } + HRESULT ICeeFileGen::GetEntryClassToken () + { + _ASSERTE("Deprecated" && 0); + return (E_FAIL); + } + HRESULT ICeeFileGen::SetEntryPointDescr () + { + _ASSERTE("Deprecated" && 0); + return (E_FAIL); + } + HRESULT ICeeFileGen::GetEntryPointDescr () + { + _ASSERTE("Deprecated" && 0); + return (E_FAIL); + } + HRESULT ICeeFileGen::SetEntryPointFlags () + { + _ASSERTE("Deprecated" && 0); + return (E_FAIL); + } + HRESULT ICeeFileGen::GetEntryPointFlags () + { + _ASSERTE("Deprecated" && 0); + return (E_FAIL); + } + HRESULT ICeeFileGen::CreateSig () + { + _ASSERTE("Deprecated" && 0); + return (E_FAIL); + } + HRESULT ICeeFileGen::AddSigArg () + { + _ASSERTE("Deprecated" && 0); + return (E_FAIL); + } + HRESULT ICeeFileGen::SetSigReturnType () + { + _ASSERTE("Deprecated" && 0); + return (E_FAIL); + } + HRESULT ICeeFileGen::SetSigCallingConvention () + { + _ASSERTE("Deprecated" && 0); + return (E_FAIL); + } + HRESULT ICeeFileGen::DeleteSig () + { + _ASSERTE("Deprecated" && 0); + return (E_FAIL); + } +//**************************************************************************** + +EXTERN_C HRESULT __stdcall CreateICeeFileGen(ICeeFileGen** pCeeFileGen) +{ + if (!pCeeFileGen) + return E_POINTER; + + ICeeFileGen *gen = new (nothrow) ICeeFileGen(); + IfNullRet(gen); + + *pCeeFileGen = gen; + return S_OK; +} + +EXTERN_C HRESULT __stdcall DestroyICeeFileGen(ICeeFileGen** pCeeFileGen) +{ + if (!pCeeFileGen) + return E_POINTER; + delete *pCeeFileGen; + *pCeeFileGen = NULL; + return S_OK; +} + +HRESULT ICeeFileGen::CreateCeeFile (HCEEFILE *ceeFile) +{ + return CreateCeeFileEx(ceeFile, ICEE_CREATE_FILE_PURE_IL); +} + +HRESULT ICeeFileGen::CreateCeeFileEx (HCEEFILE *ceeFile, DWORD createFlags) +{ + return CreateCeeFileEx2(ceeFile, createFlags, NULL); +} + +// +// Seed file is used as the base file. The new file data will be "appended" to the seed file +// + +HRESULT ICeeFileGen::CreateCeeFileEx2 (HCEEFILE *ceeFile, DWORD createFlags, LPCWSTR seedFileName) +{ + if (!ceeFile) + return E_POINTER; + + CeeFileGenWriter *gen = NULL; + HRESULT hr; + IfFailRet(CeeFileGenWriter::CreateNewInstanceEx(NULL, gen, createFlags, seedFileName)); + TESTANDRETURN(gen != NULL, E_OUTOFMEMORY); + *ceeFile = gen; + +#ifdef ENC_DELTA_HACK + // for EnC we want the RVA to be right be relative to the front of the delta IL stream rather + // than take into account the .text section and the cor header as we would for a real PE file + // However, the RVA must be non-zero, so just stick a dword on the front to push it out. + if (g_EnCMode) + { + CeeSection *sec = &gen->getIlSection(); + sec->getBlock(sizeof(DWORD), sizeof(DWORD)); + } +#endif + + return S_OK; +} + +HRESULT ICeeFileGen::CreateCeeFileFromICeeGen(ICeeGen *pICeeGen, HCEEFILE *ceeFile, DWORD createFlags) +{ + if (!ceeFile) + return E_POINTER; + CCeeGen *genFrom = reinterpret_cast(pICeeGen); + CeeFileGenWriter *gen = NULL; + if (FAILED(CeeFileGenWriter::CreateNewInstance(genFrom, gen, createFlags))) return FALSE; + TESTANDRETURN(gen != NULL, E_OUTOFMEMORY); + *ceeFile = gen; + return S_OK; +} + +HRESULT ICeeFileGen::DestroyCeeFile(HCEEFILE *ceeFile) +{ + if (!ceeFile) + return E_POINTER; + if (!*ceeFile) + return E_POINTER; + + CeeFileGenWriter **gen = reinterpret_cast(ceeFile); + (*gen)->Cleanup(); + delete *gen; + *ceeFile = NULL; + return S_OK; +} + +// + +HRESULT ICeeFileGen::GetRdataSection (HCEEFILE ceeFile, HCEESECTION *section) +{ + TESTANDRETURNPOINTER(section); + TESTANDRETURNARG(ceeFile != 0); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + *section = &gen->getStringSection(); + return S_OK; +} + +HRESULT ICeeFileGen::GetIlSection (HCEEFILE ceeFile, HCEESECTION *section) +{ + TESTANDRETURNPOINTER(section); + TESTANDRETURNARG(ceeFile != 0); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + *section = &gen->getIlSection(); + return S_OK; +} + + +HRESULT ICeeFileGen::GetSectionCreate (HCEEFILE ceeFile, const char *name, DWORD flags, + HCEESECTION *section) +{ + TESTANDRETURNPOINTER(section); + TESTANDRETURNARG(ceeFile != 0); + TESTANDRETURNPOINTER(name); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + CeeSection **ceeSection = reinterpret_cast(section); + + HRESULT hr = gen->getSectionCreate(name, flags, ceeSection); + + return hr; +} + +HRESULT ICeeFileGen::SetDirectoryEntry(HCEEFILE ceeFile, HCEESECTION section, ULONG num, ULONG size, ULONG offset) +{ + TESTANDRETURNPOINTER(ceeFile); + TESTANDRETURNPOINTER(section); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + CeeSection &sec = *(reinterpret_cast(section)); + return(gen->setDirectoryEntry(sec, num, size, offset)); +} + +HRESULT ICeeFileGen::GetSectionDataLen (HCEESECTION section, ULONG *dataLen) +{ + TESTANDRETURNPOINTER(section); + TESTANDRETURNPOINTER(dataLen); + + CeeSection *sec = reinterpret_cast(section); + *dataLen = sec->dataLen(); + return S_OK; +} + +HRESULT ICeeFileGen::GetSectionRVA (HCEESECTION section, ULONG *rva) +{ + TESTANDRETURNPOINTER(section); + TESTANDRETURNPOINTER(rva); + + CeeSection *sec = reinterpret_cast(section); + *rva = sec->getBaseRVA(); + return S_OK; +} + +HRESULT ICeeFileGen::GetSectionBlock (HCEESECTION section, ULONG len, + ULONG align, void **ppBytes) +{ + TESTANDRETURNPOINTER(section); + TESTANDRETURNPOINTER(ppBytes); + + CeeSection *sec = reinterpret_cast(section); + void *bytes = sec->getBlock(len, align); + TESTANDRETURN(bytes != NULL, E_OUTOFMEMORY); + *ppBytes = bytes; + + return S_OK; +} + +HRESULT ICeeFileGen::TruncateSection (HCEESECTION section, ULONG len) +{ + _ASSERTE(!"This is an obsolete function!"); + return E_NOTIMPL; +} + +HRESULT ICeeFileGen::AddSectionReloc (HCEESECTION section, ULONG offset, HCEESECTION relativeTo, CeeSectionRelocType relocType) +{ + TESTANDRETURNPOINTER(section); + + CeeSection *sec = reinterpret_cast(section); + CeeSection *relSec = reinterpret_cast(relativeTo); + + if (relSec) + { +#ifdef EMIT_FIXUPS + CeeFileGenWriter * gen = reinterpret_cast(&sec->ceeFile()); + HRESULT hr = gen->addFixup(*sec, offset, relocType, relSec); + if (FAILED(hr)) + { + return(hr); + } +#endif + return(sec->addSectReloc(offset, *relSec, relocType)); + } + else + { +#ifdef EMIT_FIXUPS + CeeFileGenWriter * gen = reinterpret_cast(&sec->ceeFile()); + HRESULT hr = gen->addFixup(*sec, offset, relocType); + if (FAILED(hr)) + { + return(hr); + } +#endif + return(sec->addBaseReloc(offset, relocType)); + } +} + +HRESULT ICeeFileGen::SetSectionDirectoryEntry(HCEESECTION section, ULONG num) +{ + TESTANDRETURNPOINTER(section); + + printf("Warning: deprecated method. Use SetDirectoryEntry instead\n"); + CeeSection *sec = reinterpret_cast(section); + return(sec->directoryEntry(num)); +} + +HRESULT ICeeFileGen::SetOutputFileName (HCEEFILE ceeFile, __in LPWSTR outputFileName) +{ + TESTANDRETURNPOINTER(ceeFile); + TESTANDRETURNPOINTER(outputFileName); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + return(gen->setOutputFileName(outputFileName)); +} + +__success(return == S_OK) HRESULT ICeeFileGen::GetOutputFileName (HCEEFILE ceeFile, __out LPWSTR *outputFileName) +{ + TESTANDRETURNPOINTER(ceeFile); + TESTANDRETURNPOINTER(outputFileName); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + TESTANDRETURNPOINTER(outputFileName); + *outputFileName = gen->getOutputFileName(); + return S_OK; +} + + +HRESULT ICeeFileGen::SetResourceFileName (HCEEFILE ceeFile, __in LPWSTR resourceFileName) +{ + TESTANDRETURNPOINTER(ceeFile); + TESTANDRETURNPOINTER(resourceFileName); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + return(gen->setResourceFileName(resourceFileName)); +} + +__success(return == S_OK) +HRESULT ICeeFileGen::GetResourceFileName (HCEEFILE ceeFile, __out LPWSTR *resourceFileName) +{ + TESTANDRETURNPOINTER(ceeFile); + TESTANDRETURNPOINTER(resourceFileName); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + TESTANDRETURNPOINTER(resourceFileName); + *resourceFileName = gen->getResourceFileName(); + return S_OK; +} + + +HRESULT ICeeFileGen::SetImageBase(HCEEFILE ceeFile, size_t imageBase) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + gen->setImageBase(imageBase); + return S_OK; +} + +HRESULT ICeeFileGen::SetImageBase64(HCEEFILE ceeFile, ULONGLONG imageBase) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + gen->setImageBase64(imageBase); + return S_OK; +} + +HRESULT ICeeFileGen::SetFileAlignment(HCEEFILE ceeFile, ULONG fileAlignment) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + gen->setFileAlignment(fileAlignment); + return S_OK; +} + +HRESULT ICeeFileGen::SetSubsystem(HCEEFILE ceeFile, DWORD subsystem, DWORD major, DWORD minor) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + gen->setSubsystem(subsystem, major, minor); + return S_OK; +} + +HRESULT ICeeFileGen::GetIMapTokenIface(HCEEFILE ceeFile, IMetaDataEmit *emitter, IUnknown **pIMapToken) +{ + _ASSERTE(!"This is an obsolete function!"); + return E_NOTIMPL; +} + +HRESULT ICeeFileGen::EmitMetaData (HCEEFILE ceeFile, IMetaDataEmit *emitter, + mdScope scopeE) +{ + _ASSERTE(!"This is an obsolete function!"); + return E_NOTIMPL; +} + +HRESULT ICeeFileGen::EmitLibraryName (HCEEFILE ceeFile, IMetaDataEmit *emitter, + mdScope scopeE) +{ + _ASSERTE(!"This is an obsolete function!"); + return E_NOTIMPL; +} + +HRESULT ICeeFileGen::GetMethodRVA(HCEEFILE ceeFile, ULONG codeOffset, ULONG *codeRVA) +{ + TESTANDRETURNARG(ceeFile != 0); + TESTANDRETURNPOINTER(codeRVA); + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + gen->getMethodRVA(codeOffset, codeRVA); + return S_OK; +} + +HRESULT ICeeFileGen::EmitString(HCEEFILE ceeFile, __in LPWSTR strValue, ULONG *strRef) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + return(gen->getStringSection().getEmittedStringRef(strValue, strRef)); +} + +HRESULT ICeeFileGen::LinkCeeFile (HCEEFILE ceeFile) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + return gen->link(); +} + +HRESULT ICeeFileGen::FixupCeeFile (HCEEFILE ceeFile) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + return gen->fixup(); +} + +HRESULT ICeeFileGen::GetHeaderInfo (HCEEFILE ceeFile, PIMAGE_NT_HEADERS *ppNtHeaders, PIMAGE_SECTION_HEADER *ppSections, ULONG *pNumSections) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + gen->getPEWriter().getHeaderInfo(ppNtHeaders, ppSections, pNumSections); + return S_OK; +} + +HRESULT ICeeFileGen::GenerateCeeFile (HCEEFILE ceeFile) +{ + SO_NOT_MAINLINE_FUNCTION; + + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + return gen->generateImage(NULL); // NULL means don't write in-memory buffer, uses outputFileName +} + +// GenerateCeeMemoryImage - returns in ppImage an in-memory PE image allocated by CoTaskMemAlloc() +// the caller is responsible for calling CoTaskMemFree on this memory image +HRESULT ICeeFileGen::GenerateCeeMemoryImage (HCEEFILE ceeFile, void **ppImage) +{ + TESTANDRETURNPOINTER(ceeFile); + TESTANDRETURNPOINTER(ppImage); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + return gen->generateImage(ppImage); +} + +HRESULT ICeeFileGen::SetEntryPoint(HCEEFILE ceeFile, mdMethodDef method) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + return gen->setEntryPoint(method); +} + +HRESULT ICeeFileGen::GetEntryPoint(HCEEFILE ceeFile, mdMethodDef *method) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + TESTANDRETURNPOINTER(method); + *method = gen->getEntryPoint(); + return S_OK; +} + + +HRESULT ICeeFileGen::SetComImageFlags (HCEEFILE ceeFile, DWORD mask) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + return gen->setComImageFlags(mask); +} + +HRESULT ICeeFileGen::ClearComImageFlags (HCEEFILE ceeFile, DWORD mask) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + return gen->clearComImageFlags(mask); +} + +HRESULT ICeeFileGen::GetComImageFlags (HCEEFILE ceeFile, DWORD *mask) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + TESTANDRETURNPOINTER(mask); + *mask = gen->getComImageFlags(); + return S_OK; +} + + +HRESULT ICeeFileGen::SetDllSwitch (HCEEFILE ceeFile, BOOL dllSwitch) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + return(gen->setDllSwitch(dllSwitch==TRUE)); +} + +HRESULT ICeeFileGen::GetDllSwitch (HCEEFILE ceeFile, BOOL *dllSwitch) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + TESTANDRETURNPOINTER(dllSwitch); + *dllSwitch = gen->getDllSwitch(); + return S_OK; +} + +HRESULT ICeeFileGen::SetObjSwitch (HCEEFILE ceeFile, BOOL objSwitch) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + return(gen->setObjSwitch(objSwitch==TRUE)); +} + +HRESULT ICeeFileGen::GetObjSwitch (HCEEFILE ceeFile, BOOL *objSwitch) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + TESTANDRETURNPOINTER(objSwitch); + *objSwitch = gen->getObjSwitch(); + return S_OK; +} + + +HRESULT ICeeFileGen::SetLibraryName (HCEEFILE ceeFile, __in LPWSTR LibraryName) +{ + TESTANDRETURNPOINTER(ceeFile); + TESTANDRETURNPOINTER(LibraryName); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + return(gen->setLibraryName(LibraryName)); +} + +HRESULT ICeeFileGen::SetLibraryGuid (HCEEFILE ceeFile, __in LPWSTR LibraryGuid) +{ + TESTANDRETURNPOINTER(ceeFile); + TESTANDRETURNPOINTER(LibraryGuid); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + return(gen->setLibraryGuid(LibraryGuid)); +} + +__success(return == S_OK) HRESULT ICeeFileGen::GetLibraryName (HCEEFILE ceeFile, __out LPWSTR *LibraryName) +{ + TESTANDRETURNPOINTER(ceeFile); + TESTANDRETURNPOINTER(LibraryName); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + *LibraryName = gen->getLibraryName(); + return S_OK; +} + + + +HRESULT ICeeFileGen::EmitMetaDataEx (HCEEFILE ceeFile, IMetaDataEmit *emitter) +{ + TESTANDRETURNPOINTER(ceeFile); + TESTANDRETURNPOINTER(emitter); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + return(gen->emitMetaData(emitter)); +} + +HRESULT ICeeFileGen::EmitMetaDataAt (HCEEFILE ceeFile, IMetaDataEmit *emitter, HCEESECTION section, DWORD offset, BYTE* buffer, unsigned buffLen) +{ + TESTANDRETURNPOINTER(ceeFile); + TESTANDRETURNPOINTER(emitter); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + CeeSection* sec = reinterpret_cast(section); + + return(gen->emitMetaData(emitter, sec, offset, buffer, buffLen)); +} + +HRESULT ICeeFileGen::EmitLibraryNameEx (HCEEFILE ceeFile, IMetaDataEmit *emitter) +{ + TESTANDRETURNPOINTER(ceeFile); + TESTANDRETURNPOINTER(emitter); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + return(gen->emitLibraryName(emitter)); +} + +HRESULT ICeeFileGen::GetIMapTokenIfaceEx(HCEEFILE ceeFile, IMetaDataEmit *emitter, IUnknown **pIMapToken) +{ + TESTANDRETURNPOINTER(ceeFile); + TESTANDRETURNPOINTER(pIMapToken); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + return gen->getMapTokenIface(pIMapToken); +} + +HRESULT ICeeFileGen::AddNotificationHandler(HCEEFILE ceeFile, + IUnknown *pHandler) +{ + TESTANDRETURNPOINTER(ceeFile); + TESTANDRETURNPOINTER(pHandler); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + return gen->addNotificationHandler(pHandler); +} + +HRESULT ICeeFileGen::EmitMacroDefinitions(HCEEFILE ceeFile, void *pData, DWORD cData) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + return gen->EmitMacroDefinitions(pData, cData); +} + +HRESULT ICeeFileGen::SetManifestEntry(HCEEFILE ceeFile, ULONG size, ULONG offset) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + return gen->setManifestEntry(size, offset); +} + +HRESULT ICeeFileGen::SetStrongNameEntry(HCEEFILE ceeFile, ULONG size, ULONG offset) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + return gen->setStrongNameEntry(size, offset); +} + +HRESULT ICeeFileGen::ComputeSectionOffset(HCEESECTION section, __in char *ptr, + unsigned *offset) +{ + TESTANDRETURNPOINTER(section); + + CeeSection &sec = *(reinterpret_cast(section)); + + *offset = sec.computeOffset(ptr); + + return S_OK; +} + +__success(return == S_OK) +HRESULT ICeeFileGen::ComputeSectionPointer(HCEESECTION section, ULONG offset, + __out char **ptr) +{ + TESTANDRETURNPOINTER(section); + + CeeSection &sec = *(reinterpret_cast(section)); + + *ptr = sec.computePointer(offset); + + return S_OK; +} + +HRESULT ICeeFileGen::ComputeOffset(HCEEFILE ceeFile, __in char *ptr, + HCEESECTION *pSection, unsigned *offset) +{ + TESTANDRETURNPOINTER(pSection); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + + CeeSection *section; + + HRESULT hr = gen->computeOffset(ptr, §ion, offset); + + if (SUCCEEDED(hr)) + *pSection = reinterpret_cast(section); + + return hr; +} + +HRESULT ICeeFileGen::SetEnCRVABase(HCEEFILE ceeFile, ULONG dataBase, ULONG rdataBase) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + return gen->setEnCRvaBase(dataBase, rdataBase); +} + +HRESULT ICeeFileGen::GetCorHeader(HCEEFILE ceeFile, + IMAGE_COR20_HEADER **header) +{ + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + return gen->getCorHeader(header); +} + +HRESULT ICeeFileGen::SetVTableEntry(HCEEFILE ceeFile, ULONG size, ULONG offset) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + return gen->setVTableEntry(size, offset); +} + +HRESULT ICeeFileGen::SetVTableEntry64(HCEEFILE ceeFile, ULONG size, void* ptr) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + return gen->setVTableEntry64(size, ptr); +} + +HRESULT ICeeFileGen::GetFileTimeStamp (HCEEFILE ceeFile, DWORD *pTimeStamp) +{ + TESTANDRETURNPOINTER(ceeFile); + TESTANDRETURNPOINTER(pTimeStamp); + + CeeFileGenWriter *gen = reinterpret_cast(ceeFile); + return(gen->getFileTimeStamp(pTimeStamp)); +} + diff --git a/src/dlls/mscorpe/mscorpe/mscorpe.def b/src/dlls/mscorpe/mscorpe/mscorpe.def new file mode 100644 index 0000000000..61cb90375f --- /dev/null +++ b/src/dlls/mscorpe/mscorpe/mscorpe.def @@ -0,0 +1,11 @@ +; +; Copyright (c) Microsoft. All rights reserved. +; Licensed under the MIT license. See LICENSE file in the project root for full license information. +; +; mscorpe.def for mscorpe.dll (a simple wrapper around real implementation mscorpehost.dll - see +; file:wrapper.cpp for more details) +; PE file generator in EE + +EXPORTS + CreateICeeFileGen + DestroyICeeFileGen diff --git a/src/dlls/mscorpe/mscorpe/mscorpe.nativeproj b/src/dlls/mscorpe/mscorpe/mscorpe.nativeproj new file mode 100644 index 0000000000..526cf6088b --- /dev/null +++ b/src/dlls/mscorpe/mscorpe/mscorpe.nativeproj @@ -0,0 +1,46 @@ + + + + + + true + true + + $(UserIncludes); + . + + $(CDefines);__TODO_PORT_TO_WRAPPERS__;UNICODE + mscorpe + $(BinariesDirectory)\$(OutputName).dll + $(IntermediateOutputDirectory) + $(OutputName).def + DYNLINK + windows + true + _DllMainCRTStartup + $(ClrLibPath)\utilcodestaticnohost.lib + false + true + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/dlls/mscorpe/mscorpe/wrapper.cpp b/src/dlls/mscorpe/mscorpe/wrapper.cpp new file mode 100644 index 0000000000..c5822cd127 --- /dev/null +++ b/src/dlls/mscorpe/mscorpe/wrapper.cpp @@ -0,0 +1,150 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// +// File: wrapper.cpp +// + +// +// This file implements a simple wrapper DLL (mscorpe.dll) which calls properly into mscorpehost.dll. +// It exists because of compatibility with 1.x/2.0 apps running on CLR 4.0+. Such older apps could pass +// full path to LoadLibrary() Windows API and get this DLL. +// +// Noone in CLR should ever try to load this DLL directly (using LoadLibrary API). Note that hosting APIs +// and PInvoke redirect mscorpe.dll to mscorpehost.dll automatically. +// + +#include + +#include +#include + +// Globals +HINSTANCE g_hThisInst; // This library. + +//***************************************************************************** +// Handle lifetime of loaded library. +//***************************************************************************** +extern "C" +BOOL WINAPI +DllMain( + HINSTANCE hInstance, + DWORD dwReason, + LPVOID lpReserved) +{ + switch (dwReason) + { + case DLL_PROCESS_ATTACH: + { // Save the module handle. + g_hThisInst = hInstance; + DisableThreadLibraryCalls((HMODULE)hInstance); + } + break; + case DLL_PROCESS_DETACH: + break; + } + + return TRUE; +} // DllMain + +// Implementation for utilcode +HINSTANCE +GetModuleInst() +{ + return g_hThisInst; +} // GetModuleInst + +// Load correct SxS version of mscorpe.dll and initialize it (uses shim). +HRESULT +LoadMscorpe(HMODULE * phModule) +{ + HRESULT hr = S_OK; + ICLRMetaHost * pMetaHost = NULL; + ICLRRuntimeInfo * pCLRRuntimeInfo = NULL; + + // Get full DLL path + WCHAR wszPath[_MAX_PATH]; + DWORD dwLength = GetModuleFileName((HMODULE)g_hThisInst, wszPath, NumItems(wszPath)); + + if ((dwLength == 0) || + ((dwLength == NumItems(wszPath)) && + (GetLastError() == ERROR_INSUFFICIENT_BUFFER))) + { + IfFailGo(CLR_E_SHIM_RUNTIMELOAD); + } + + // Find start of '\mscorpe.dll' + LPWSTR wszSeparator = wcsrchr(wszPath, L'\\'); + if (wszSeparator == NULL) + { + IfFailGo(CLR_E_SHIM_RUNTIMELOAD); + } + // Check the name of this DLL + _ASSERTE(_wcsicmp(wszSeparator, L"\\mscorpe.dll") == 0); + // Remove the DLL name + *wszSeparator = 0; + + // Find start of last directory name (\), + // C:\Windows\Microsoft.NET\Framework\[[v4.0.12345]]\mscorpe.dll + LPWSTR wszLastDirectoryName = wcsrchr(wszPath, L'\\'); + if (wszLastDirectoryName == NULL) + { + IfFailGo(CLR_E_SHIM_RUNTIMELOAD); + } + LPWSTR wszVersion = wszLastDirectoryName + 1; + + IfFailGo(CLRCreateInstance( + CLSID_CLRMetaHost, + IID_ICLRMetaHost, + reinterpret_cast(&pMetaHost))); + + IfFailGo(pMetaHost->GetRuntime( + wszVersion, + IID_ICLRRuntimeInfo, + reinterpret_cast(&pCLRRuntimeInfo))); + + // Shim will load correct SxS version of mscorpe.dll and will initialize it + IfFailGo(pCLRRuntimeInfo->LoadLibrary( + L"mscorpe.dll", + phModule)); + +ErrExit: + if (pMetaHost != NULL) + { + pMetaHost->Release(); + pMetaHost = NULL; + } + if (pCLRRuntimeInfo != NULL) + { + pCLRRuntimeInfo->Release(); + pCLRRuntimeInfo = NULL; + } + + if (FAILED(hr)) + { + *phModule = NULL; + } + + return hr; +} // LoadMscorpe + +// SxS wrapper of mscorpe.dll entrypoints +typedef MscorpeSxSWrapper MscorpeSxS; + +// Export of 'original' 1.x/2.0 mscorpe.dll +EXTERN_C +HRESULT __stdcall +CreateICeeFileGen( + ICeeFileGen ** ppCeeFileGen) +{ + return MscorpeSxS::CreateICeeFileGen(ppCeeFileGen); +} + +// Export of 'original' 1.x/2.0 mscorpe.dll +EXTERN_C +HRESULT __stdcall +DestroyICeeFileGen(ICeeFileGen ** ppCeeFileGen) +{ + return MscorpeSxS::DestroyICeeFileGen(ppCeeFileGen); +} diff --git a/src/dlls/mscorpe/mscorpehost/mscorpehost.def b/src/dlls/mscorpe/mscorpehost/mscorpehost.def new file mode 100644 index 0000000000..e10fc1b682 --- /dev/null +++ b/src/dlls/mscorpe/mscorpehost/mscorpehost.def @@ -0,0 +1,12 @@ +; +; Copyright (c) Microsoft. All rights reserved. +; Licensed under the MIT license. See LICENSE file in the project root for full license information. +; +; PeWriter.def for PeWriter.dll +; PE file generator in EE + +EXPORTS + CreateICeeFileGen + DestroyICeeFileGen + InitializeSxS + diff --git a/src/dlls/mscorpe/mscorpehost/mscorpehost.nativeproj b/src/dlls/mscorpe/mscorpehost/mscorpehost.nativeproj new file mode 100644 index 0000000000..45af39f38b --- /dev/null +++ b/src/dlls/mscorpe/mscorpehost/mscorpehost.nativeproj @@ -0,0 +1,68 @@ + + + + + $(ClrCrtLib) + mscorpehost + $(BinariesDirectory)\$(OutputName).dll + $(IntermediateOutputDirectory) + $(OutputName).def + DYNLINK + stdafx.h + true + ..\stdafx.cpp + + $(UserIncludes); + .; + ../../vm + + $(CDefines);__TODO_PORT_TO_WRAPPERS__;UNICODE + windows + true + _DllMainCRTStartup + false + + + + $(LinkDelayLoad); + $(LinkDelayLoad)ole32.dll + + + + + + + + + $(ClrSrcDirectory)utilcode\dyncrt\dyncrt.nativeproj + + + + + $(ClrSrcDirectory)md\ceefilegen\ceefgen.nativeproj + + + $(ClrSrcDirectory)delayimp\delayimp.nativeproj + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/dlls/mscorpe/pewriter.cpp b/src/dlls/mscorpe/pewriter.cpp new file mode 100644 index 0000000000..7494ad9d3d --- /dev/null +++ b/src/dlls/mscorpe/pewriter.cpp @@ -0,0 +1,2401 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +#include "stdafx.h" + +#include "blobfetcher.h" +#include "pedecoder.h" + +#ifdef _DEBUG +#define LOGGING +#endif + +#ifdef LOGGING +#include "log.h" + +static const char* const RelocName[] = { + "Absolute", "Unk1", "Unk2", "HighLow", "Unk4", "MapToken", + "Relative", "FilePos", "CodeRel", "Movl64", "Dir64", "PcRel25", "PcRel64", + "AbsTag" }; +static const char RelocSpaces[] = " "; + +static INT64 s_minPcRel25; +static INT64 s_maxPcRel25; +#endif + +#ifdef EnC_SUPPORTED +#define ENC_DELTA_HACK +#endif + + /* This is the stub program that says it can't be run in DOS mode */ + /* it is x86 specific, but so is dos so I suppose that is OK */ +static const unsigned char x86StubPgm[] = { + 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, 0x21, 0xb8, 0x01, 0x4c, 0xcd, 0x21, 0x54, 0x68, + 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, + 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20, + 0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x0d, 0x0d, 0x0a, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + /* number of pad bytes to make 'len' bytes align to 'align' */ +inline static unsigned roundUp(unsigned len, unsigned align) { + return((len + align-1) & ~(align-1)); +} + +inline static unsigned padLen(unsigned len, unsigned align) { + return(roundUp(len, align) - len); +} + +inline static bool isExeOrDll(IMAGE_NT_HEADERS* ntHeaders) { + return ((ntHeaders->FileHeader.Characteristics & VAL16(IMAGE_FILE_EXECUTABLE_IMAGE)) != 0); +} + +#ifndef IMAGE_DLLCHARACTERISTICS_NO_SEH +#define IMAGE_DLLCHARACTERISTICS_NO_SEH 0x400 +#endif + +#ifndef IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE +#define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040 +#endif + +#ifndef IMAGE_DLLCHARACTERISTICS_NX_COMPAT +#define IMAGE_DLLCHARACTERISTICS_NX_COMPAT 0x0100 +#endif + +#define COPY_AND_ADVANCE(target, src, size) { \ + ::memcpy((void *) (target), (const void *) (src), (size)); \ + (char *&) (target) += (size); } + +/******************************************************************/ +int __cdecl relocCmp(const void* a_, const void* b_) { + + const PESectionReloc* a = (const PESectionReloc*) a_; + const PESectionReloc* b = (const PESectionReloc*) b_; + return (a->offset > b->offset ? 1 : (a->offset == b->offset ? 0 : -1)); +} + +PERelocSection::PERelocSection(PEWriterSection *pBaseReloc) +{ + section = pBaseReloc; + relocPage = (unsigned) -1; + relocSize = 0; + relocSizeAddr = NULL; + pages = 0; + +#ifdef _DEBUG + lastRVA = 0; +#endif +} + +void PERelocSection::AddBaseReloc(unsigned rva, int type, unsigned short highAdj) +{ +#ifdef _DEBUG + // Guarantee that we're adding relocs in strict increasing order. + _ASSERTE(rva > lastRVA); + lastRVA = rva; +#endif + + if (relocPage != (rva & ~0xFFF)) { + if (relocSizeAddr) { + if ((relocSize & 1) == 1) { // pad to an even number + short *ps = (short*) section->getBlock(2); + if(ps) { + *ps = 0; + relocSize++; + } + } + *relocSizeAddr = VAL32(relocSize*2 + sizeof(IMAGE_BASE_RELOCATION)); + } + IMAGE_BASE_RELOCATION* base = (IMAGE_BASE_RELOCATION*) section->getBlock(sizeof(IMAGE_BASE_RELOCATION)); + if(base) { + relocPage = (rva & ~0xFFF); + relocSize = 0; + base->VirtualAddress = VAL32(relocPage); + // Size needs to be fixed up when we know it - save address here + relocSizeAddr = &base->SizeOfBlock; + pages++; + } + } + + relocSize++; + unsigned short* offset = (unsigned short*) section->getBlock(2); + if(offset) { + *offset = VAL16((rva & 0xFFF) | (type << 12)); + } +} + +void PERelocSection::Finish(bool isPE32) +{ + // fixup the last reloc block (if there was one) + if (relocSizeAddr) { + if ((relocSize & 1) == 1) { // pad to an even number + short* psh = (short*) section->getBlock(2); + if(psh) + { + *psh = 0; + relocSize++; + } + } + *relocSizeAddr = VAL32(relocSize*2 + sizeof(IMAGE_BASE_RELOCATION)); + } +} + +#define GET_UNALIGNED_INT32(_ptr) ((INT32) GET_UNALIGNED_VAL32(_ptr)) + +static inline HRESULT SignedFitsIn31Bits(INT64 immediate) +{ + INT64 hiBits = immediate >> 31; + if ((hiBits == 0) || (hiBits == -1)) + { + return S_OK; + } + else + { + return E_FAIL; + } +} + +static inline HRESULT UnsignedFitsIn32Bits(UINT64 immediate) +{ + UINT64 hiBits = immediate >> 32; + if (hiBits == 0) + { + return S_OK; + } + else + { + return E_FAIL; + } +} + +static inline HRESULT AddOvf_RVA(DWORD& a, DWORD b) +{ + DWORD r = a + b; + if (r < a) // Check for overflow + return E_FAIL; + a = r; + return S_OK; +} + +static inline HRESULT AddOvf_S_U32(INT64 & a, unsigned int b) +{ + INT64 r = a + b; + if (r < a) // Check for overflow + return E_FAIL; + a = r; + return S_OK; +} + +static inline HRESULT AddOvf_S_S32(INT64 & a, int b) +{ + INT64 r = a + b; + if ( ((r >= a) && (b >= 0)) || + ((r < a) && (b < 0)) ) + { + a = r; + return S_OK; + } + return E_FAIL; +} + +static inline HRESULT AddOvf_U_U32(UINT64 & a, unsigned int b) +{ + UINT64 r = a + b; + if (r < a) // Check for overflow + return E_FAIL; + a = r; + return S_OK; +} + +static inline HRESULT AddOvf_U_U(UINT64 & a, UINT64 b) +{ + UINT64 r = a + b; + if (r < a) // Check for overflow + return E_FAIL; + a = r; + return S_OK; +} + +static inline HRESULT SubOvf_S_U32(INT64 & a, unsigned int b) +{ + INT64 r = a - b; + if (r > a) // Check for overflow + return E_FAIL; + a = r; + return S_OK; +} + +static inline HRESULT SubOvf_S_U(INT64 & a, UINT64 b) +{ + INT64 r = a - b; + if (r > a) // Check for overflow + return E_FAIL; + a = r; + return S_OK; +} + +static inline HRESULT SubOvf_U_U32(UINT64 & a, unsigned int b) +{ + UINT64 r = a - b; + if (r > a) // Check for overflow + return E_FAIL; + a = r; + return S_OK; +} + +/* subtract two unsigned pointers yeilding a signed pointer sized int */ +static inline HRESULT SubOvf_U_U(INT64 & r, UINT64 a, UINT64 b) +{ + r = a - b; + if ( ((a >= b) && (r >= 0)) || + ((a < b) && (r < 0))) + { + return S_OK; + } + return E_FAIL; +} + + +/******************************************************************/ +/* apply the relocs for this section. +*/ + +HRESULT PEWriterSection::applyRelocs(IMAGE_NT_HEADERS * pNtHeaders, + PERelocSection * pBaseRelocSection, + CeeGenTokenMapper * pTokenMapper, + DWORD dataRvaBase, + DWORD rdataRvaBase, + DWORD codeRvaBase) +{ + HRESULT hr; + + _ASSERTE(pBaseRelocSection); // need section to write relocs + +#ifdef LOGGING + // Ensure that if someone adds a value to CeeSectionRelocType in cor.h, + // that they also add an entry to RelocName. + static_assert_no_msg(NumItems(RelocName) == srRelocSentinel); +#ifdef _DEBUG + for (unsigned int i = 0; i < srRelocSentinel; i++) + { + _ASSERTE(strlen(RelocName[i]) <= strlen(RelocSpaces)); + } +#endif // _DEBUG +#endif // LOGGING + + if (m_relocCur == m_relocStart) + return S_OK; + + bool isPE32 = (pNtHeaders->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR32_MAGIC)); + +#ifdef LOGGING + LOG((LF_ZAP, LL_INFO100000, + "APPLYING section relocs for section %s start RVA = 0x%x\n", + m_name, m_baseRVA)); +#endif + + UINT64 imageBase = isPE32 ? VAL32(((IMAGE_NT_HEADERS32 *) pNtHeaders)->OptionalHeader.ImageBase) + : VAL64(((IMAGE_NT_HEADERS64 *) pNtHeaders)->OptionalHeader.ImageBase); + + // sort them to make the baseRelocs pretty + qsort(m_relocStart, (m_relocCur - m_relocStart), sizeof(PESectionReloc), relocCmp); + + for (PESectionReloc * cur = m_relocStart; cur < m_relocCur; cur++) + { + _ASSERTE((cur->offset + 4) <= m_blobFetcher.GetDataLen()); + + int curType = cur->type; + DWORD curOffset = cur->offset; + bool isRelocPtr = ((curType & srRelocPtr) != 0); + bool noBaseBaseReloc = ((curType & srNoBaseReloc) != 0); + UINT64 targetOffset = 0; + int slotNum = 0; + INT64 oldStarPos; + + // If cur->section is NULL then this is a pointer outside the module. + bool externalAddress = (cur->section == NULL); + + curType &= ~(srRelocPtr | srNoBaseReloc); + + /* If we see any srRelocHighLow's in a PE64 file we convert them into DIR64 relocs */ + if (!isPE32 && (curType == srRelocHighLow)) + curType = srRelocDir64; + + /* If we have an IA64 instruction fixup then extract the slot number and adjust curOffset */ + if ((curType == srRelocIA64PcRel25) || (curType == srRelocIA64Imm64) || (curType == srRelocIA64PcRel64)) + { + _ASSERTE((curOffset & 0x3) == 0); + slotNum = (curOffset & 0xf) >> 2; + curOffset &= ~0xf; + } + + DWORD curRVA = m_baseRVA; // RVA in the PE image of the reloc site + IfFailRet(AddOvf_RVA(curRVA, curOffset)); + DWORD UNALIGNED * pos = (DWORD *) m_blobFetcher.ComputePointer(curOffset); + + PREFIX_ASSUME(pos != NULL); + +#ifdef LOGGING + LOG((LF_ZAP, LL_INFO1000000, + " Reloc %s%s%s at %-7s+%04x (RVA=%08x) at" FMT_ADDR, + RelocName[curType], (isRelocPtr) ? "Ptr" : " ", + &RelocSpaces[strlen(RelocName[curType])], + m_name, curOffset, curRVA, DBG_ADDR(pos))); +#endif + // + // 'pos' is the site of the reloc + // Compute 'targetOffset' from pointer if necessary + // + + if (isRelocPtr) + { + // Calculate the value of ptr to pass to computeOffset + char * ptr = (char *) pos; + + if (curType == srRelocRelative) { + // + // Here we add sizeof(int) because we need to calculate + // ptr as the true call target address (x86 pc-rel) + // We need to true call target address since pass it + // to computeOffset and this function would fall if + // the address we pass is before the start of a section + // + oldStarPos = (SSIZE_T) ptr; + IfFailRet(AddOvf_S_S32(oldStarPos, GET_UNALIGNED_INT32(pos))); + IfFailRet(AddOvf_S_U32(oldStarPos, sizeof(int))); + ptr = (char *) oldStarPos; + targetOffset = externalAddress ? (size_t) ptr + : cur->section->computeOffset(ptr); + // We subtract off the four bytes that we added previous + // since the code below depends upon this + IfFailRet(SubOvf_U_U32(targetOffset, sizeof(int))); + IfFailRet(UnsignedFitsIn32Bits(targetOffset)); // Check for overflow + SET_UNALIGNED_VAL32(pos, targetOffset); + } + else if (curType == srRelocIA64Imm64) { + _ASSERTE(slotNum == 1); + ptr = (char *) ((intptr_t) GetIA64Imm64((UINT64 *) ptr)); + oldStarPos = (SSIZE_T) ptr; + targetOffset = externalAddress ? (size_t) ptr + : cur->section->computeOffset(ptr); + _ASSERTE(!isPE32); + PutIA64Imm64((UINT64 *)pos, targetOffset); + } + else if (curType == srRelocIA64PcRel64) { + _ASSERTE(slotNum == 1); + ptr = (char *) ((intptr_t) GetIA64Rel64((UINT64 *) ptr)); + oldStarPos = (SSIZE_T) ptr; + targetOffset = externalAddress ? (size_t) ptr + : cur->section->computeOffset(ptr); + _ASSERTE(!isPE32); + PutIA64Rel64((UINT64 *)pos, targetOffset); + } + else { + _ASSERTE(curType != srRelocIA64PcRel25); + ptr = (char *) GET_UNALIGNED_VALPTR(ptr); + oldStarPos = (SSIZE_T) ptr; + targetOffset = externalAddress ? (size_t) ptr + : cur->section->computeOffset(ptr); + IfFailRet(UnsignedFitsIn32Bits(targetOffset)); // Check for overflow + SET_UNALIGNED_VAL32(pos, targetOffset); + /* Zero the upper 32-bits for a machine with 64-bit pointers */ + if (!isPE32) + SET_UNALIGNED_VAL32(pos+1, 0); + } + } +#ifdef LOGGING + else + { + if (curType == srRelocIA64PcRel25) + { + oldStarPos = GetIA64Rel25((UINT64 *) pos, slotNum); + } + else + { + if (curType == srRelocIA64PcRel64) + { + _ASSERTE(slotNum == 1); + oldStarPos = GetIA64Rel64((UINT64 *) pos); + } + else if (curType == srRelocIA64Imm64) + { + oldStarPos = GetIA64Imm64((UINT64 *)pos); + } + else + { + oldStarPos = GET_UNALIGNED_VAL32(pos); + } + } + } +#endif + + // + // 'targetOffset' has now been computed. Write out the appropriate value. + // Record base relocs as necessary. + // + + bool fBaseReloc = false; + bool fNeedBrl = false; + INT64 newStarPos = 0; // oldStarPos gets updated to newStarPos + + if (curType == srRelocAbsolute || curType == srRelocAbsoluteTagged) { + _ASSERTE(!externalAddress); + + newStarPos = GET_UNALIGNED_INT32(pos); + + if (curType == srRelocAbsoluteTagged) + newStarPos = (newStarPos & ~0x80000001) >> 1; + + if (rdataRvaBase > 0 && ! strcmp((const char *)(cur->section->m_name), ".rdata")) + IfFailRet(AddOvf_S_U32(newStarPos, rdataRvaBase)); + else if (dataRvaBase > 0 && ! strcmp((const char *)(cur->section->m_name), ".data")) + IfFailRet(AddOvf_S_U32(newStarPos, dataRvaBase)); + else + IfFailRet(AddOvf_S_U32(newStarPos, cur->section->m_baseRVA)); + + if (curType == srRelocAbsoluteTagged) + newStarPos = (newStarPos << 1) | 0x80000001; + + SET_UNALIGNED_VAL32(pos, newStarPos); + } + else if (curType == srRelocMapToken) + { + mdToken newToken; + if (pTokenMapper != NULL && pTokenMapper->HasTokenMoved((mdToken)GET_UNALIGNED_VAL32(pos), newToken)) { + // we have a mapped token + SET_UNALIGNED_VAL32(pos, newToken); + } + newStarPos = GET_UNALIGNED_VAL32(pos); + } + else if (curType == srRelocFilePos) + { + _ASSERTE(!externalAddress); + newStarPos = GET_UNALIGNED_VAL32(pos); + IfFailRet(AddOvf_S_U32(newStarPos, cur->section->m_filePos)); + SET_UNALIGNED_VAL32(pos, newStarPos); + } + else if (curType == srRelocRelative) + { + if (externalAddress) { +#if defined(_AMD64_) + newStarPos = GET_UNALIGNED_INT32(pos); +#else // x86 + UINT64 targetAddr = GET_UNALIGNED_VAL32(pos); + IfFailRet(SubOvf_U_U(newStarPos, targetAddr, imageBase)); +#endif + } + else { + newStarPos = GET_UNALIGNED_INT32(pos); + IfFailRet(AddOvf_S_U32(newStarPos, cur->section->m_baseRVA)); + } + IfFailRet(SubOvf_S_U32(newStarPos, curRVA)); + IfFailRet(SignedFitsIn31Bits(newStarPos)); // Check for overflow + SET_UNALIGNED_VAL32(pos, newStarPos); + } + else if (curType == srRelocCodeRelative) + { + newStarPos = GET_UNALIGNED_INT32(pos); + IfFailRet(SubOvf_S_U32(newStarPos, codeRvaBase)); + if (externalAddress) + IfFailRet(SubOvf_S_U(newStarPos, imageBase)); + else + IfFailRet(AddOvf_S_U32(newStarPos, cur->section->m_baseRVA)); + IfFailRet(SignedFitsIn31Bits(newStarPos)); // Check for overflow + SET_UNALIGNED_VAL32(pos, newStarPos); + + } + else if (curType == srRelocIA64PcRel25) + { + _ASSERTE((m_baseRVA & 15) == 0); + _ASSERTE((cur->section->m_baseRVA & 15) == 0); + + newStarPos = GetIA64Rel25((UINT64 *) pos, slotNum); + IfFailRet(SubOvf_S_U32(newStarPos, curRVA)); + if (externalAddress) + IfFailRet(SubOvf_S_U(newStarPos, imageBase)); + else + IfFailRet(AddOvf_S_U32(newStarPos, cur->section->m_baseRVA)); + + INT64 hiBits = newStarPos >> 24; + + _ASSERTE((hiBits==0) || (hiBits==-1)); + + IfFailRet(AddOvf_S_U32(newStarPos, GetIA64Rel25((UINT64 *) pos, slotNum))); + + hiBits = newStarPos >> 24; + + _ASSERTE((hiBits==0) || (hiBits==-1)); + + INT32 delta32 = (INT32) newStarPos; + + PutIA64Rel25((UINT64 *) pos, slotNum, delta32); + + _ASSERTE(GetIA64Rel25((UINT64 *) pos, slotNum) == delta32); + +#ifdef LOGGING + if (newStarPos < s_minPcRel25) + s_minPcRel25 = newStarPos; + if (newStarPos > s_maxPcRel25) + s_maxPcRel25 = newStarPos; +#endif + } + else if (curType == srRelocIA64PcRel64) + { + _ASSERTE((m_baseRVA & 15) == 0); + _ASSERTE(slotNum == 1); + + newStarPos = GetIA64Rel64((UINT64 *) pos); + IfFailRet(SubOvf_S_U32(newStarPos, m_baseRVA)); + + if (externalAddress) + IfFailRet(SubOvf_S_U(newStarPos, imageBase)); + else + { + _ASSERTE((cur->section->m_baseRVA & 15) == 0); + IfFailRet(AddOvf_S_U32(newStarPos, cur->section->m_baseRVA)); + } + + INT64 hiBits = newStarPos >> 24; + + fNeedBrl = (hiBits != 0) && (hiBits != -1); + + /* Can we convert the brl.call into a br.call? */ + if (!fNeedBrl) + { + INT32 delta32 = (INT32) newStarPos; + + UINT64 temp0 = ((UINT64 *) pos)[0]; + UINT64 temp1 = ((UINT64 *) pos)[1]; +#ifdef _DEBUG + // + // make certain we're decoding a brl opcode, with template 4 or 5 + // + UINT64 templa = (temp0 >> 0) & 0x1f; + UINT64 opcode = (temp1 >> 60) & 0xf; + + _ASSERTE(((opcode == 0xC) || (opcode == 0xD)) && + ((templa == 0x4) || (templa == 0x5))); +#endif + const UINT64 mask0 = UI64(0x00003FFFFFFFFFE1); + const UINT64 mask1 = UI64(0x7700000FFF800000); + + /* Clear all bits used as part of the slot1 and slot2 */ + temp0 &= mask0; // opcode becomes 4 or 5 + temp1 &= mask1; + + temp0 |= 0x10; // template becomes 0x10 or 0x11 + temp1 |= 0x200; // slot 1 becomes nop.i + + ((UINT64 *) pos)[0] = temp0; + ((UINT64 *) pos)[1] = temp1; + + PutIA64Rel25((UINT64 *) pos, 2, delta32); + _ASSERTE(GetIA64Rel25((UINT64 *) pos, 2) == delta32); + } + else + { + PutIA64Rel64((UINT64 *) pos, newStarPos); + _ASSERTE(GetIA64Rel64((UINT64 *) pos) == newStarPos); + } + } + else if (curType == srRelocHighLow) + { + _ASSERTE(isPE32); + + // we have a 32-bit value at pos + UINT64 value = GET_UNALIGNED_VAL32(pos); + + if (!externalAddress) + { + IfFailRet(AddOvf_U_U32(value, cur->section->m_baseRVA)); + IfFailRet(AddOvf_U_U(value, imageBase)); + } + + IfFailRet(UnsignedFitsIn32Bits(value)); // Check for overflow + SET_UNALIGNED_VAL32(pos, value); + + newStarPos = value; + + fBaseReloc = true; + } + else if (curType == srRelocDir64) + { + _ASSERTE(!isPE32); + + // we have a 64-bit value at pos + UINT64 UNALIGNED * p_value = (UINT64 *) pos; + targetOffset = *p_value; + + if (!externalAddress) + { + // The upper bits of targetOffset must be zero + IfFailRet(UnsignedFitsIn32Bits(targetOffset)); + + IfFailRet(AddOvf_U_U32(targetOffset, cur->section->m_baseRVA)); + IfFailRet(AddOvf_U_U(targetOffset, imageBase)); + } + + *p_value = targetOffset; + newStarPos = targetOffset; + fBaseReloc = true; + } + else if (curType == srRelocIA64Imm64) + { + _ASSERTE(!isPE32); + _ASSERTE((curRVA & 15) == 0); // This reloc should be 16-byte aligned + + // we have a 64-bit value encoded in the instruction at pos + targetOffset = GetIA64Imm64((UINT64 *)pos); + + if (!externalAddress) + { + // The upper bits of targetOffset must be zero + IfFailRet(UnsignedFitsIn32Bits(targetOffset)); + + IfFailRet(AddOvf_U_U32(targetOffset, cur->section->m_baseRVA)); + IfFailRet(AddOvf_U_U(targetOffset, imageBase)); + } + + PutIA64Imm64((UINT64 *)pos, targetOffset); + newStarPos = targetOffset; + fBaseReloc = true; + } + else + { + _ASSERTE(!"Unknown Relocation type"); + } + + if (fBaseReloc && !noBaseBaseReloc) + { + pBaseRelocSection->AddBaseReloc(curRVA, curType); + } + +#ifdef LOGGING + const char* sectionName; + + if (externalAddress) + { + sectionName = "external"; + } + else + { + sectionName = cur->section->m_name; + } + + LOG((LF_ZAP, LL_INFO1000000, + "to %-7s+%04x, old =" FMT_ADDR "new =" FMT_ADDR "%s%s\n", + sectionName, targetOffset, + DBG_ADDR(oldStarPos), DBG_ADDR(newStarPos), + fBaseReloc ? "(BASE RELOC)" : "", + fNeedBrl ? "(BRL)" : "" )); +#endif + + } + return S_OK; +} + +/******************************************************************/ + +PESeedSection::PESeedSection(PEDecoder * peDecoder, + IMAGE_SECTION_HEADER * seedSection) + : PEWriterSection((const char *)seedSection->Name, + VAL32(seedSection->Characteristics), + VAL32(seedSection->SizeOfRawData), + 0), + m_pSeedFileDecoder(peDecoder), + m_pSeedSectionHeader(seedSection) +{ + m_baseRVA = VAL32(seedSection->VirtualAddress); +} + +HRESULT PESeedSection::write(HANDLE file) { + ULONG sizeOfSection = VAL32(m_pSeedSectionHeader->SizeOfRawData); + LPCVOID sectionData = PBYTE(m_pSeedFileDecoder->GetBase()) + m_pSeedSectionHeader->PointerToRawData; + + DWORD dwWritten = 0; + if (!WriteFile(file, sectionData, sizeOfSection, &dwWritten, NULL)) { + return HRESULT_FROM_GetLastError(); + } + _ASSERTE(dwWritten == sizeOfSection); + return S_OK; +} + +unsigned PESeedSection::writeMem(void ** pMem) { + ULONG sizeOfSection = VAL32(m_pSeedSectionHeader->SizeOfRawData); + LPCVOID sectionData = PBYTE(m_pSeedFileDecoder->GetBase()) + m_pSeedSectionHeader->PointerToRawData; + + COPY_AND_ADVANCE(*pMem, sectionData, sizeOfSection); + return sizeOfSection; +} + +/******************************************************************/ +HRESULT PEWriter::Init(PESectionMan *pFrom, DWORD createFlags, LPCWSTR seedFileName) +{ + if (pFrom) + *(PESectionMan*)this = *pFrom; + else { + HRESULT hr = PESectionMan::Init(); + if (FAILED(hr)) + return hr; + } + time_t now; + time(&now); + +#ifdef LOGGING + InitializeLogging(); +#endif + + // Save the timestamp so that we can give it out if someone needs + // it. + m_peFileTimeStamp = (DWORD) now; + + // We must be creating either a PE32 or a PE64 file + if (createFlags & ICEE_CREATE_FILE_PE64) + { + m_ntHeaders = (IMAGE_NT_HEADERS *) new (nothrow) IMAGE_NT_HEADERS64; + m_ntHeadersSize = sizeof(IMAGE_NT_HEADERS64); + + if (!m_ntHeaders) return E_OUTOFMEMORY; + memset(m_ntHeaders, 0, m_ntHeadersSize); + + m_ntHeaders->OptionalHeader.Magic = VAL16(IMAGE_NT_OPTIONAL_HDR64_MAGIC); + m_ntHeaders->FileHeader.SizeOfOptionalHeader = VAL16(sizeof(IMAGE_OPTIONAL_HEADER64)); + } + else + { + _ASSERTE(createFlags & ICEE_CREATE_FILE_PE32); + m_ntHeaders = (IMAGE_NT_HEADERS *) new (nothrow) IMAGE_NT_HEADERS32; + m_ntHeadersSize = sizeof(IMAGE_NT_HEADERS32); + + if (!m_ntHeaders) return E_OUTOFMEMORY; + memset(m_ntHeaders, 0, m_ntHeadersSize); + + m_ntHeaders->OptionalHeader.Magic = VAL16(IMAGE_NT_OPTIONAL_HDR32_MAGIC); + m_ntHeaders->FileHeader.SizeOfOptionalHeader = VAL16(sizeof(IMAGE_OPTIONAL_HEADER32)); + } + + // Record whether we should create the CorExeMain and CorDllMain stubs + m_createCorMainStub = ((createFlags & ICEE_CREATE_FILE_CORMAIN_STUB) != 0); + + // We must have a valid target machine selected + if ((createFlags & ICEE_CREATE_MACHINE_MASK) == ICEE_CREATE_MACHINE_I386) + { + m_ntHeaders->FileHeader.Machine = VAL16(IMAGE_FILE_MACHINE_I386); + } + else if ((createFlags & ICEE_CREATE_MACHINE_MASK) == ICEE_CREATE_MACHINE_IA64) + { + m_ntHeaders->FileHeader.Machine = VAL16(IMAGE_FILE_MACHINE_IA64); + } + else if ((createFlags & ICEE_CREATE_MACHINE_MASK) == ICEE_CREATE_MACHINE_AMD64) + { + m_ntHeaders->FileHeader.Machine = VAL16(IMAGE_FILE_MACHINE_AMD64); + } + else if ((createFlags & ICEE_CREATE_MACHINE_MASK) == ICEE_CREATE_MACHINE_ARM) + { + m_ntHeaders->FileHeader.Machine = VAL16(IMAGE_FILE_MACHINE_ARMNT); + + // The OS loader already knows how to initialize pure managed assemblies and we have no legacy OS + // support to worry about on ARM so don't ever create the stub for ARM binaries. + m_createCorMainStub = false; + } + else + { + _ASSERTE(!"Invalid target machine"); + } + + cEntries = IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR + 1; + pEntries = new (nothrow) directoryEntry[cEntries]; + if (pEntries == NULL) return E_OUTOFMEMORY; + memset(pEntries, 0, sizeof(*pEntries) * cEntries); + + m_ntHeaders->Signature = VAL32(IMAGE_NT_SIGNATURE); + m_ntHeaders->FileHeader.TimeDateStamp = VAL32((ULONG) now); + m_ntHeaders->FileHeader.Characteristics = VAL16(0); + + if (createFlags & ICEE_CREATE_FILE_STRIP_RELOCS) + { + m_ntHeaders->FileHeader.Characteristics |= VAL16(IMAGE_FILE_RELOCS_STRIPPED); + } + + // Linker version should be consistent with current VC level + m_ntHeaders->OptionalHeader.MajorLinkerVersion = 11; + m_ntHeaders->OptionalHeader.MinorLinkerVersion = 0; + + m_ntHeaders->OptionalHeader.SectionAlignment = VAL32(IMAGE_NT_OPTIONAL_HDR_SECTION_ALIGNMENT); + m_ntHeaders->OptionalHeader.FileAlignment = VAL32(0); + m_ntHeaders->OptionalHeader.AddressOfEntryPoint = VAL32(0); + + m_ntHeaders->OptionalHeader.MajorOperatingSystemVersion = VAL16(4); + m_ntHeaders->OptionalHeader.MinorOperatingSystemVersion = VAL16(0); + + m_ntHeaders->OptionalHeader.MajorImageVersion = VAL16(0); + m_ntHeaders->OptionalHeader.MinorImageVersion = VAL16(0); + m_ntHeaders->OptionalHeader.MajorSubsystemVersion = VAL16(4); + m_ntHeaders->OptionalHeader.MinorSubsystemVersion = VAL16(0); + m_ntHeaders->OptionalHeader.Win32VersionValue = VAL32(0); + m_ntHeaders->OptionalHeader.Subsystem = VAL16(0); + m_ntHeaders->OptionalHeader.DllCharacteristics = VAL16(0); + m_ntHeaders->OptionalHeader.CheckSum = VAL32(0); + setDllCharacteristics(IMAGE_DLLCHARACTERISTICS_NO_SEH | + IMAGE_DLLCHARACTERISTICS_NX_COMPAT | + IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE | + IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE); + + if (isPE32()) + { + IMAGE_NT_HEADERS32* p_ntHeaders32 = ntHeaders32(); + p_ntHeaders32->OptionalHeader.ImageBase = VAL32(CEE_IMAGE_BASE_32); + p_ntHeaders32->OptionalHeader.SizeOfStackReserve = VAL32(0x100000); + p_ntHeaders32->OptionalHeader.SizeOfStackCommit = VAL32(0x1000); + p_ntHeaders32->OptionalHeader.SizeOfHeapReserve = VAL32(0x100000); + p_ntHeaders32->OptionalHeader.SizeOfHeapCommit = VAL32(0x1000); + p_ntHeaders32->OptionalHeader.LoaderFlags = VAL32(0); + p_ntHeaders32->OptionalHeader.NumberOfRvaAndSizes = VAL32(16); + } + else + { + IMAGE_NT_HEADERS64* p_ntHeaders64 = ntHeaders64(); + // FIX what are the correct values for PE+ (64-bit) ? + p_ntHeaders64->OptionalHeader.ImageBase = VAL64(CEE_IMAGE_BASE_64); + p_ntHeaders64->OptionalHeader.SizeOfStackReserve = VAL64(0x400000); + p_ntHeaders64->OptionalHeader.SizeOfStackCommit = VAL64(0x4000); + p_ntHeaders64->OptionalHeader.SizeOfHeapReserve = VAL64(0x100000); + p_ntHeaders64->OptionalHeader.SizeOfHeapCommit = VAL64(0x2000); + p_ntHeaders64->OptionalHeader.LoaderFlags = VAL32(0); + p_ntHeaders64->OptionalHeader.NumberOfRvaAndSizes = VAL32(16); + } + + m_ilRVA = (DWORD) -1; + m_dataRvaBase = 0; + m_rdataRvaBase = 0; + m_codeRvaBase = 0; + m_encMode = FALSE; + + virtualPos = 0; + filePos = 0; + reloc = NULL; + strtab = NULL; + headers = NULL; + headersEnd = NULL; + + m_file = INVALID_HANDLE_VALUE; + + // + // Seed file + // + + m_hSeedFile = INVALID_HANDLE_VALUE; + m_hSeedFileMap = INVALID_HANDLE_VALUE; + m_pSeedFileDecoder = NULL; + m_iSeedSections = 0; + m_pSeedSectionToAdd = NULL; + + if (seedFileName) + { + HandleHolder hFile (WszCreateFile(seedFileName, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, + NULL)); + + if (hFile == INVALID_HANDLE_VALUE) + return HRESULT_FROM_GetLastError(); + + MapViewHolder hMapFile (WszCreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL)); + DWORD dwFileLen = SafeGetFileSize(hFile, 0); + if (dwFileLen == 0xffffffff) + return HRESULT_FROM_GetLastError(); + + if (hMapFile == NULL) + return HRESULT_FROM_GetLastError(); + + BYTE * baseFileView = (BYTE*) MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, 0); + + PEDecoder * pPEDecoder = new (nothrow) PEDecoder(baseFileView, (COUNT_T)dwFileLen); + if (pPEDecoder == NULL) return E_OUTOFMEMORY; + + if (pPEDecoder->Has32BitNTHeaders()) + { + if ((createFlags & ICEE_CREATE_FILE_PE32) == 0) + return E_FAIL; + + setImageBase32(DWORD(size_t(pPEDecoder->GetPreferredBase()))); + } + else + { + if ((createFlags & ICEE_CREATE_FILE_PE64) == 0) + return E_FAIL; + + setImageBase64(UINT64((intptr_t) pPEDecoder->GetPreferredBase())); + } + + setFileAlignment (VAL32(pPEDecoder->GetFileAlignment())); + setSectionAlignment(VAL32(pPEDecoder->GetSectionAlignment())); + + hFile.SuppressRelease(); + hMapFile.SuppressRelease(); + + m_hSeedFile = hFile; + m_hSeedFileMap = hMapFile; + m_pSeedFileDecoder = pPEDecoder; + +#ifdef _WIN64 + m_pSeedFileNTHeaders = pPEDecoder->GetNTHeaders64(); +#else + m_pSeedFileNTHeaders = pPEDecoder->GetNTHeaders32(); +#endif + + // Add the seed sections + + m_pSeedSections = m_pSeedFileDecoder->FindFirstSection(); + + m_pSeedSectionToAdd = m_pSeedSections; + m_iSeedSections = m_pSeedFileDecoder->GetNumberOfSections(); + + for (unsigned i = 0; i < m_iSeedSections; m_pSeedSectionToAdd++, i++) { + PESection * dummy; + getSectionCreate((const char *)(m_pSeedSectionToAdd->Name), + VAL32(m_pSeedSectionToAdd->Characteristics), + &dummy); + } + + m_pSeedSectionToAdd = NULL; + } + + return S_OK; +} + +/******************************************************************/ +HRESULT PEWriter::Cleanup() { + + if (m_hSeedFile != INVALID_HANDLE_VALUE) + { + CloseHandle(m_hSeedFile); + CloseHandle(m_hSeedFileMap); + delete m_pSeedFileDecoder; + } + + if (isPE32()) + { + delete ntHeaders32(); + } + else + { + delete ntHeaders64(); + } + + if (headers != NULL) + delete [] headers; + + if (pEntries != NULL) + delete [] pEntries; + + return PESectionMan::Cleanup(); +} + +PESection* PEWriter::getSection(const char* name) +{ + int len = (int)strlen(name); + + // the section name can be at most 8 characters including the null. + if (len < 8) + len++; + else + len = 8; + + // dbPrintf(("looking for section %s\n", name)); + // Skip over the seed sections + + for(PESection** cur = sectStart+m_iSeedSections; cur < sectCur; cur++) { + // dbPrintf(("searching section %s\n", (*cur)->m_ame)); + if (strncmp((*cur)->m_name, name, len) == 0) { + // dbPrintf(("found section %s\n", (*cur)->m_name)); + return(*cur); + } + } + return(0); +} + +HRESULT PEWriter::newSection(const char* name, PESection **section, + unsigned flags, unsigned estSize, + unsigned estRelocs) +{ + if (m_pSeedSectionToAdd) { + _ASSERTE(strcmp((const char *)(m_pSeedSectionToAdd->Name), name) == 0 && + VAL32(m_pSeedSectionToAdd->Characteristics) == flags); + + PESeedSection * ret = new (nothrow) PESeedSection(m_pSeedFileDecoder, m_pSeedSectionToAdd); + *section = ret; + TESTANDRETURNMEMORY(ret); + return S_OK; + } + + PEWriterSection * ret = new (nothrow) PEWriterSection(name, flags, estSize, estRelocs); + *section = ret; + TESTANDRETURNMEMORY(ret); + return S_OK; +} + +ULONG PEWriter::getIlRva() +{ + // assume that pe optional header is less than size of section alignment. So this + // gives out the rva for the .text section, which is merged after the .text0 section + // This is verified in debug build when actually write out the file + _ASSERTE(m_ilRVA > 0); + return m_ilRVA; +} + +void PEWriter::setIlRva(ULONG offset) +{ + // assume that pe optional header is less than size of section alignment. So this + // gives out the rva for the .text section, which is merged after the .text0 section + // This is verified in debug build when actually write out the file + m_ilRVA = roundUp(VAL32(m_ntHeaders->OptionalHeader.SectionAlignment) + offset, SUBSECTION_ALIGN); +} + +HRESULT PEWriter::setDirectoryEntry(PEWriterSection *section, ULONG entry, ULONG size, ULONG offset) +{ + if (entry >= cEntries) + { + USHORT cNewEntries = (USHORT)max((ULONG)cEntries * 2, entry + 1); + + if (cNewEntries <= cEntries) return E_OUTOFMEMORY; // Integer overflow + if (cNewEntries <= entry) return E_OUTOFMEMORY; // Integer overflow + + directoryEntry *pNewEntries = new (nothrow) directoryEntry [ cNewEntries ]; + if (pNewEntries == NULL) return E_OUTOFMEMORY; + + CopyMemory(pNewEntries, pEntries, cEntries * sizeof(*pNewEntries)); + ZeroMemory(pNewEntries + cEntries, (cNewEntries - cEntries) * sizeof(*pNewEntries)); + + delete [] pEntries; + pEntries = pNewEntries; + cEntries = cNewEntries; + } + + pEntries[entry].section = section; + pEntries[entry].offset = offset; + pEntries[entry].size = size; + return S_OK; +} + +void PEWriter::setEnCRvaBase(ULONG dataBase, ULONG rdataBase) +{ + m_dataRvaBase = dataBase; + m_rdataRvaBase = rdataBase; + m_encMode = TRUE; +} + +//----------------------------------------------------------------------------- +// These 2 write functions must be implemented here so that they're in the same +// .obj file as whoever creates the FILE struct. We can't pass a FILE struct +// across a dll boundary and use it. +//----------------------------------------------------------------------------- + +HRESULT PEWriterSection::write(HANDLE file) +{ + return m_blobFetcher.Write(file); +} + +//----------------------------------------------------------------------------- +// Write out the section to the stream +//----------------------------------------------------------------------------- +HRESULT CBlobFetcher::Write(HANDLE file) +{ +// Must write out each pillar (including idx = m_nIndexUsed), one after the other + unsigned idx; + for(idx = 0; idx <= m_nIndexUsed; idx ++) { + if (m_pIndex[idx].GetDataLen() > 0) + { + ULONG length = m_pIndex[idx].GetDataLen(); + DWORD dwWritten = 0; + if (!WriteFile(file, m_pIndex[idx].GetRawDataStart(), length, &dwWritten, NULL)) + { + return HRESULT_FROM_GetLastError(); + } + _ASSERTE(dwWritten == length); + } + } + + return S_OK; +} + + +//----------------------------------------------------------------------------- +// These 2 write functions must be implemented here so that they're in the same +// .obj file as whoever creates the FILE struct. We can't pass a FILE struct +// across a dll boundary and use it. +//----------------------------------------------------------------------------- + +unsigned PEWriterSection::writeMem(void **ppMem) +{ + HRESULT hr; + hr = m_blobFetcher.WriteMem(ppMem); + _ASSERTE(SUCCEEDED(hr)); + + return m_blobFetcher.GetDataLen(); +} + +//----------------------------------------------------------------------------- +// Write out the section to memory +//----------------------------------------------------------------------------- +HRESULT CBlobFetcher::WriteMem(void **ppMem) +{ + char **ppDest = (char **)ppMem; + // Must write out each pillar (including idx = m_nIndexUsed), one after the other + unsigned idx; + for(idx = 0; idx <= m_nIndexUsed; idx ++) { + if (m_pIndex[idx].GetDataLen() > 0) + { + // WARNING: macro - must enclose in curly braces + COPY_AND_ADVANCE(*ppDest, m_pIndex[idx].GetRawDataStart(), m_pIndex[idx].GetDataLen()); + } + } + + return S_OK; +} + +/******************************************************************/ + +// +// Intermediate table to sort to help determine section order +// +struct entry { + const char * name; // full name of the section + unsigned char nameLength; // length of the text part of the name + signed char index; // numeral value at the end of the name; -1 if none + unsigned short arrayIndex; // index of section within sectStart[] +}; + +class SectionNameSorter : protected CQuickSort +{ + entry * m_entries; + PEWriterSection ** m_sections; + unsigned m_count; + unsigned m_seedCount; + + public: + SectionNameSorter(entry *entries, PEWriterSection ** sections, int count, unsigned seedSections) + : CQuickSort(entries, count), + m_entries(entries), + m_sections(sections), + m_count(unsigned(count)), + m_seedCount(seedSections) + {} + + // Sorts the entries according to alphabetical + numerical order + + int Compare(entry *first, entry *second) + { + PEWriterSection * firstSection = m_sections[first->arrayIndex]; + PEWriterSection * secondSection = m_sections[second->arrayIndex]; + + // Seed sections are always at the start, in the order they were + // added to the PEWriter + + if (firstSection->isSeedSection() || secondSection->isSeedSection()) { + if (firstSection->isSeedSection() && secondSection->isSeedSection()) + return first->arrayIndex - second->arrayIndex; + + return firstSection->isSeedSection() ? -1 : 1; + } + + // Sort the names + + int lenDiff = first->nameLength - second->nameLength; + int smallerLen; + if (lenDiff < 0) + smallerLen = first->nameLength; + else + smallerLen = second->nameLength; + + int result = strncmp(first->name, second->name, smallerLen); + + if (result != 0) + return result; + else + { + if (lenDiff != 0) + return lenDiff; + else + return (int)(first->index - second->index); + } + } + + int SortSections() + { + Sort(); + + entry * ePrev = m_entries; + entry * e = ePrev + 1; + int iSections = 1; // First section is obviously unique + + for (unsigned i = 1; i < m_count; i++, ePrev = e, e++) { + + // Seed sections should stay at the front + _ASSERTE(i >= m_seedCount || i == e->arrayIndex); + + if (!m_sections[ePrev->arrayIndex]->isSeedSection() && + (ePrev->nameLength == e->nameLength) && + strncmp(ePrev->name, e->name, e->nameLength) == 0) + { + continue; + } + + iSections++; + } + + return iSections; + } +}; + +#define SectionIndex IMAGE_SECTION_HEADER::VirtualAddress +#define FirstEntryIndex IMAGE_SECTION_HEADER::SizeOfRawData + +HRESULT PEWriter::linkSortSections(entry * entries, + unsigned * piEntries, + unsigned * piUniqueSections) +{ + // + // Preserve current section order as much as possible, but apply the following + // rules: + // - sections named "xxx#" are collated into a single PE section "xxx". + // The contents of the CeeGen sections are sorted according to numerical + // order & made contiguous in the PE section + // - "text" always comes first in the file + // - empty sections receive no PE section + // + + bool ExeOrDll = isExeOrDll(m_ntHeaders); + + entry *e = entries; + for (PEWriterSection **cur = getSectStart(); cur < getSectCur(); cur++) { + + // + // Throw away any old headers we've used. + // + + (*cur)->m_header = NULL; + + // + // Don't allocate PE data for 0 length sections + // + + if ((*cur)->dataLen() == 0) + continue; + + // + // Special case: omit "text0" from obj's + // + + if (!ExeOrDll && strcmp((*cur)->m_name, ".text0") == 0) + continue; + + e->name = (*cur)->m_name; + + // + // Now find the end of the text part of the section name, and + // calculate the numeral (if any) at the end + // + + _ASSERTE(strlen(e->name) < UCHAR_MAX); + const char *p = e->name + strlen(e->name); + int index = 0; // numeral at the end of the section name + int placeValue = 1; + if (isdigit(p[-1])) + { + while (--p > e->name) + { + if (!isdigit(*p)) + break; + index += ((*p - '0') * placeValue); + placeValue *= 10; + } + p++; + + // + // Special case: put "xxx" after "xxx0" and before "xxx1" + // + + if (index == 0) + index = -1; + } + + _ASSERTE(index == -1 || index == atoi(p)); + + e->nameLength = (unsigned char)(p - e->name); + e->index = index; + e->arrayIndex = (unsigned short)(cur - getSectStart()); + e++; + } + + // + // Sort the entries according to alphabetical + numerical order + // + + SectionNameSorter sorter(entries, getSectStart(), int(e - entries), m_iSeedSections); + *piUniqueSections = sorter.SortSections(); + + *piEntries = unsigned(e - entries); + + return S_OK; +} + +class HeaderSorter : public CQuickSort +{ + public: + HeaderSorter(IMAGE_SECTION_HEADER *headers, int count) + : CQuickSort(headers, count) {} + + int Compare(IMAGE_SECTION_HEADER *first, IMAGE_SECTION_HEADER *second) + { + // IMAGE_SECTION_HEADER::VirtualAddress/SectionIndex contains the + // index of the section + return VAL32(first->SectionIndex) - VAL32(second->SectionIndex); + } +}; + +HRESULT PEWriter::linkSortHeaders(entry * entries, unsigned iEntries, unsigned iUniqueSections) +{ + if (headers != NULL) + delete [] headers; + + // 1 extra for .reloc + S_UINT32 cUniqueSectionsAllocated = S_UINT32(iUniqueSections) + S_UINT32(1); + if (cUniqueSectionsAllocated.IsOverflow()) + { + return COR_E_OVERFLOW; + } + headers = new (nothrow) IMAGE_SECTION_HEADER[cUniqueSectionsAllocated.Value()]; + TESTANDRETURNMEMORY(headers); + + memset(headers, 0, sizeof(*headers) * cUniqueSectionsAllocated.Value()); + + entry *ePrev = NULL; + IMAGE_SECTION_HEADER *h = headers - 1; + + // + // Store the sorting index + // + + entry * entriesEnd = entries + iEntries; + + for (entry * e = entries ; e < entriesEnd; e++) + { + if (ePrev != NULL + && !getSectStart()[ePrev->arrayIndex]->isSeedSection() + && e->nameLength == ePrev->nameLength + && strncmp(e->name, ePrev->name, e->nameLength) == 0) + { + // + // This section has the same name as the previous section, and + // will be collapsed with the previous section. + // Just update the (common) header information + // + + if (e->arrayIndex < ePrev->arrayIndex) + { + // + // Use the smaller of the indices of e and ePrev + // + h->SectionIndex = VAL32(VAL32(h->SectionIndex) - (e->arrayIndex - ePrev->arrayIndex)); + } + + // Store an approximation of the size of the section temporarily + h->Misc.VirtualSize = VAL32(VAL32(h->Misc.VirtualSize) + getSectStart()[e->arrayIndex]->dataLen()); + } + else + { + // Grab a new header + + h++; + + strncpy_s((char *) h->Name, sizeof(h->Name), e->name, e->nameLength); + + setSectionIndex(h, e->arrayIndex); + + // Store the entry index in this field temporarily + h->FirstEntryIndex = VAL32((DWORD)(e - entries)); + + // Store an approximation of the size of the section temporarily + h->Misc.VirtualSize = VAL32(getSectStart()[e->arrayIndex]->dataLen()); + } + ePrev = e; + } + + headersEnd = ++h; + + _ASSERTE(headers + iUniqueSections == headersEnd); + + // + // Sort the entries according to alphabetical + numerical order + // + + HeaderSorter headerSorter(headers, int(iUniqueSections)); + + headerSorter.Sort(); + + return S_OK; +} // PEWriter::linkSortHeaders + +HRESULT PEWriter::linkPlaceSections(entry * entries, unsigned iEntries) +{ + entry * entriesEnd = entries + iEntries; + + for (IMAGE_SECTION_HEADER * h = headers; h < headersEnd; h++) + { + // Get to the first entry corresponding to this section header + + entry * e = entries + VAL32(h->FirstEntryIndex); + PEWriterSection *s = getSectStart()[e->arrayIndex]; + + if (s->isSeedSection()) { + virtualPos = s->getBaseRVA(); + } + + h->VirtualAddress = VAL32(virtualPos); + h->PointerToRawData = VAL32(filePos); + + s->m_baseRVA = virtualPos; + s->m_filePos = filePos; + s->m_header = h; + h->Characteristics = VAL32(s->m_flags); + +#ifdef LOGGING + LOG((LF_ZAP, LL_INFO10, + " Section %-7s RVA=%08x, Length=%08x, FilePos=%08x\n", + s->m_name, s->m_baseRVA, s->dataLen(), s->m_filePos)); +#endif + + unsigned dataSize = s->dataLen(); + + // Find all the other entries corresponding to this section header + + PEWriterSection *sPrev = s; + entry * ePrev = e; + while (++e < entriesEnd) + { + if (e->nameLength != ePrev->nameLength + || strncmp(e->name, ePrev->name, e->nameLength) != 0) + break; + + s = getSectStart()[e->arrayIndex]; + _ASSERTE(s->m_flags == VAL32(h->Characteristics)); + + sPrev->m_filePad = padLen(dataSize, SUBSECTION_ALIGN); + dataSize = roundUp(dataSize, SUBSECTION_ALIGN); + + s->m_baseRVA = virtualPos + dataSize; + s->m_filePos = filePos + dataSize; + s->m_header = h; + sPrev = s; + + dataSize += s->dataLen(); + +#ifdef LOGGING + LOG((LF_ZAP, LL_INFO10, + " Section %-7s RVA=%08x, Length=%08x, FilePos=%08x\n", + s->m_name, s->m_baseRVA, s->dataLen(), s->m_filePos)); +#endif + + ePrev = e; + } + + h->Misc.VirtualSize = VAL32(dataSize); + + sPrev->m_filePad = padLen(dataSize, VAL32(m_ntHeaders->OptionalHeader.FileAlignment)); + dataSize = roundUp(dataSize, VAL32(m_ntHeaders->OptionalHeader.FileAlignment)); + h->SizeOfRawData = VAL32(dataSize); + filePos += dataSize; + + dataSize = roundUp(dataSize, VAL32(m_ntHeaders->OptionalHeader.SectionAlignment)); + virtualPos += dataSize; + } + + return S_OK; +} + +void PEWriter::setSectionIndex(IMAGE_SECTION_HEADER * h, unsigned sectionIndex) { + + if (getSectStart()[sectionIndex]->isSeedSection()) { + h->SectionIndex = VAL32(sectionIndex); + return; + } + + // + // Reserve some dummy "array index" values for special sections + // at the start of the image (after the seed sections) + // + + static const char * const SpecialNames[] = { ".text", ".cormeta", NULL }; + enum { SPECIAL_NAMES_COUNT = NumItems(SpecialNames) }; + + for (const char * const * s = SpecialNames; /**/; s++) + { + if (*s == 0) + { + h->SectionIndex = VAL32(sectionIndex + SPECIAL_NAMES_COUNT); + break; + } + else if (strcmp((char *) h->Name, *s) == 0) + { + h->SectionIndex = VAL32(m_iSeedSections + DWORD(s - SpecialNames)); + break; + } + s++; + } + +} + + +HRESULT PEWriter::link() { + + // + // NOTE: + // link() can be called more than once! This is because at least one compiler + // (the prejitter) needs to know the base addresses of some segments before it + // builds others. It's up to the caller to insure the layout remains the same + // after changes are made, though. + // + + // + // Assign base addresses to all sections, and layout & merge PE sections + // + + bool ExeOrDll = isExeOrDll(m_ntHeaders); + + // + // Collate by name & sort by index + // + + // First collect all information into entries[] + + int sectCount = getSectCount(); + entry *entries = (entry *) _alloca(sizeof(entry) * sectCount); + + unsigned iUniqueSections, iEntries; + HRESULT hr; + IfFailRet(linkSortSections(entries, &iEntries, &iUniqueSections)); + + // + // Now, allocate a header for each unique section name. + // Also record the minimum section index for each section + // so we can preserve order as much as possible. + // + + IfFailRet(linkSortHeaders(entries, iEntries, iUniqueSections)); + + // + // If file alignment is not zero, it must have been set through + // setFileAlignment(), in which case we leave it untouched + // + + if( VAL32(0) == m_ntHeaders->OptionalHeader.FileAlignment ) + { + // + // Figure out what file alignment to use. + // + + unsigned RoundUpVal; + + if (ExeOrDll) + { + RoundUpVal = 0x0200; + } + else + { + // Don't bother padding for objs + RoundUpVal = 4; + } + + m_ntHeaders->OptionalHeader.FileAlignment = VAL32(RoundUpVal); + } + + // + // Now, assign a section header & location to each section + // + + if (ExeOrDll) + { + iUniqueSections++; // One more for .reloc + filePos = sizeof(IMAGE_DOS_HEADER)+sizeof(x86StubPgm) + m_ntHeadersSize; + } + else + { + filePos = sizeof(IMAGE_FILE_HEADER); + } + + m_ntHeaders->FileHeader.NumberOfSections = VAL16(iUniqueSections); + + filePos += iUniqueSections * sizeof(IMAGE_SECTION_HEADER); + filePos = roundUp(filePos, VAL32(m_ntHeaders->OptionalHeader.FileAlignment)); + + m_ntHeaders->OptionalHeader.SizeOfHeaders = VAL32(filePos); + + virtualPos = roundUp(filePos, VAL32(m_ntHeaders->OptionalHeader.SectionAlignment)); + + if (m_hSeedFile != INVALID_HANDLE_VALUE) { + // We do not support relocating/sliding down the seed sections + if (filePos > VAL32(m_pSeedSections->VirtualAddress) || + virtualPos > VAL32(m_pSeedSections->VirtualAddress)) + return E_FAIL; + + if (virtualPos < VAL32(m_pSeedSections->VirtualAddress)) { + virtualPos = VAL32(m_pSeedSections->VirtualAddress); + } + } + + // Now finally assign RVAs to the sections + + IfFailRet(linkPlaceSections(entries, iEntries)); + + return S_OK; +} + +#undef SectionIndex +#undef FirstEntryIndex + + +class SectionRVASorter : public CQuickSort +{ + public: + SectionRVASorter(PEWriterSection **elts, SSIZE_T count) + : CQuickSort(elts, count) {} + + int Compare(PEWriterSection **e1, PEWriterSection **e2) + { + return (*e1)->getBaseRVA() - (*e2)->getBaseRVA(); + } +}; + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:21000) // Suppress PREFast warning about overly large function +#endif +HRESULT PEWriter::fixup(CeeGenTokenMapper *pMapper) +{ + HRESULT hr; + + bool ExeOrDll = isExeOrDll(m_ntHeaders); + const unsigned RoundUpVal = VAL32(m_ntHeaders->OptionalHeader.FileAlignment); + + if(ExeOrDll) + { + // + // Apply manual relocation for entry point field + // + + PESection *textSection; + IfFailRet(getSectionCreate(".text", 0, &textSection)); + + if (m_ntHeaders->OptionalHeader.AddressOfEntryPoint != VAL32(0)) + m_ntHeaders->OptionalHeader.AddressOfEntryPoint = VAL32(VAL32(m_ntHeaders->OptionalHeader.AddressOfEntryPoint) + textSection->m_baseRVA); + + // + // Apply normal relocs + // + + IfFailRet(getSectionCreate(".reloc", sdReadOnly | IMAGE_SCN_MEM_DISCARDABLE, + (PESection **) &reloc)); + reloc->m_baseRVA = virtualPos; + reloc->m_filePos = filePos; + reloc->m_header = headersEnd++; + strcpy_s((char *)reloc->m_header->Name, sizeof(reloc->m_header->Name), + ".reloc"); + reloc->m_header->Characteristics = VAL32(reloc->m_flags); + reloc->m_header->VirtualAddress = VAL32(virtualPos); + reloc->m_header->PointerToRawData = VAL32(filePos); + +#ifdef _DEBUG + if (m_encMode) + printf("Applying relocs for .rdata section with RVA %x\n", m_rdataRvaBase); +#endif + + // + // Sort the sections by RVA + // + + CQuickArray sections; + + SIZE_T count = getSectCur() - getSectStart(); + IfFailRet(sections.ReSizeNoThrow(count)); + UINT i = 0; + PEWriterSection **cur; + for(cur = getSectStart(); cur < getSectCur(); cur++, i++) + sections[i] = *cur; + + SectionRVASorter sorter(sections.Ptr(), sections.Size()); + + sorter.Sort(); + + PERelocSection relocSection(reloc); + + cur = sections.Ptr(); + PEWriterSection **curEnd = cur + sections.Size(); + while (cur < curEnd) + { + IfFailRet((*cur)->applyRelocs(m_ntHeaders, + &relocSection, + pMapper, + m_dataRvaBase, + m_rdataRvaBase, + m_codeRvaBase)); + cur++; + } + + relocSection.Finish(isPE32()); + reloc->m_header->Misc.VirtualSize = VAL32(reloc->dataLen()); + + // Strip the reloc section if the flag is set + if (m_ntHeaders->FileHeader.Characteristics & VAL16(IMAGE_FILE_RELOCS_STRIPPED)) + { + reloc->m_header->Misc.VirtualSize = VAL32(0); + } + + reloc->m_header->SizeOfRawData = VAL32(roundUp(VAL32(reloc->m_header->Misc.VirtualSize), RoundUpVal)); + reloc->m_filePad = padLen(VAL32(reloc->m_header->Misc.VirtualSize), RoundUpVal); + filePos += VAL32(reloc->m_header->SizeOfRawData); + virtualPos += roundUp(VAL32(reloc->m_header->Misc.VirtualSize), + VAL32(m_ntHeaders->OptionalHeader.SectionAlignment)); + + if (reloc->m_header->Misc.VirtualSize == VAL32(0)) + { + // + // Omit reloc section from section list. (It will + // still be there but the loader won't see it - this + // only works because we've allocated it as the last + // section.) + // + m_ntHeaders->FileHeader.NumberOfSections = VAL16(VAL16(m_ntHeaders->FileHeader.NumberOfSections) - 1); + } + else + { + IMAGE_DATA_DIRECTORY * pRelocDataDirectory; + // + // Put reloc address in header + // + if (isPE32()) + { + pRelocDataDirectory = &(ntHeaders32()->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]); + } + else + { + pRelocDataDirectory = &(ntHeaders64()->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]); + } + + pRelocDataDirectory->VirtualAddress = reloc->m_header->VirtualAddress; + pRelocDataDirectory->Size = reloc->m_header->Misc.VirtualSize; + } + + // compute ntHeader fields that depend on the sizes of other things + for(IMAGE_SECTION_HEADER *h = headersEnd-1; h >= headers; h--) { // go backwards, so first entry takes precedence + if (h->Characteristics & VAL32(IMAGE_SCN_CNT_CODE)) { + m_ntHeaders->OptionalHeader.BaseOfCode = h->VirtualAddress; + m_ntHeaders->OptionalHeader.SizeOfCode = + VAL32(VAL32(m_ntHeaders->OptionalHeader.SizeOfCode) + VAL32(h->SizeOfRawData)); + } + if (h->Characteristics & VAL32(IMAGE_SCN_CNT_INITIALIZED_DATA)) { + if (isPE32()) + { + ntHeaders32()->OptionalHeader.BaseOfData = h->VirtualAddress; + } + m_ntHeaders->OptionalHeader.SizeOfInitializedData = + VAL32(VAL32(m_ntHeaders->OptionalHeader.SizeOfInitializedData) + VAL32(h->SizeOfRawData)); + } + if (h->Characteristics & VAL32(IMAGE_SCN_CNT_UNINITIALIZED_DATA)) { + m_ntHeaders->OptionalHeader.SizeOfUninitializedData = + VAL32(VAL32(m_ntHeaders->OptionalHeader.SizeOfUninitializedData) + VAL32(h->SizeOfRawData)); + } + } + + int index; + IMAGE_DATA_DIRECTORY * pCurDataDirectory; + + // go backwards, so first entry takes precedence + for(cur = getSectCur()-1; getSectStart() <= cur; --cur) + { + index = (*cur)->getDirEntry(); + + // Is this a valid directory entry + if (index > 0) + { + if (isPE32()) + { + _ASSERTE((unsigned)(index) < VAL32(ntHeaders32()->OptionalHeader.NumberOfRvaAndSizes)); + + pCurDataDirectory = &(ntHeaders32()->OptionalHeader.DataDirectory[index]); + } + else + { + _ASSERTE((unsigned)(index) < VAL32(ntHeaders64()->OptionalHeader.NumberOfRvaAndSizes)); + + pCurDataDirectory = &(ntHeaders64()->OptionalHeader.DataDirectory[index]); + } + + pCurDataDirectory->VirtualAddress = VAL32((*cur)->m_baseRVA); + pCurDataDirectory->Size = VAL32((*cur)->dataLen()); + } + } + + // handle the directory entries specified using the file. + for (index=0; index < cEntries; index++) + { + if (pEntries[index].section) + { + PEWriterSection *section = pEntries[index].section; + _ASSERTE(pEntries[index].offset < section->dataLen()); + + if (isPE32()) + pCurDataDirectory = &(ntHeaders32()->OptionalHeader.DataDirectory[index]); + else + pCurDataDirectory = &(ntHeaders64()->OptionalHeader.DataDirectory[index]); + + pCurDataDirectory->VirtualAddress = VAL32(section->m_baseRVA + pEntries[index].offset); + pCurDataDirectory->Size = VAL32(pEntries[index].size); + } + } + + m_ntHeaders->OptionalHeader.SizeOfImage = VAL32(virtualPos); + } // end if(ExeOrDll) + else //i.e., if OBJ + { + // + // Clean up note: + // I've cleaned up the executable linking path, but the .obj linking + // is still a bit strange, what with a "extra" reloc & strtab sections + // which are created after the linking step and get treated specially. + // + reloc = new (nothrow) PEWriterSection(".reloc", + sdReadOnly | IMAGE_SCN_MEM_DISCARDABLE, 0x4000, 0); + if(reloc == NULL) return E_OUTOFMEMORY; + strtab = new (nothrow) PEWriterSection(".strtab", + sdReadOnly | IMAGE_SCN_MEM_DISCARDABLE, 0x4000, 0); //string table (if any) + if(strtab == NULL) return E_OUTOFMEMORY; + + DWORD* TokInSymbolTable = new (nothrow) DWORD[16386]; + if (TokInSymbolTable == NULL) return E_OUTOFMEMORY; + + m_ntHeaders->FileHeader.SizeOfOptionalHeader = 0; + //For each section set VirtualAddress to 0 + PEWriterSection **cur; + for(cur = getSectStart(); cur < getSectCur(); cur++) + { + IMAGE_SECTION_HEADER* header = (*cur)->m_header; + header->VirtualAddress = VAL32(0); + } + // Go over section relocations and build the Symbol Table, use .reloc section as buffer: + DWORD tk=0, rva=0, NumberOfSymbols=0; + BOOL ToRelocTable = FALSE; + IMAGE_SYMBOL is; + IMAGE_RELOCATION ir; + ULONG StrTableLen = 4; //size itself only + char* szSymbolName = NULL; + char* pch; + + PESection *textSection; + getSectionCreate(".text", 0, &textSection); + + for(PESectionReloc* rcur = textSection->m_relocStart; rcur < textSection->m_relocCur; rcur++) + { + switch(rcur->type) + { + case 0x7FFA: // Ptr to symbol name +#ifdef _WIN64 + _ASSERTE(!"this is probably broken!!"); +#endif // _WIN64 + szSymbolName = (char*)(UINT_PTR)(rcur->offset); + break; + + case 0x7FFC: // Ptr to file name + TokInSymbolTable[NumberOfSymbols++] = 0; + memset(&is,0,sizeof(IMAGE_SYMBOL)); + memcpy(is.N.ShortName,".file\0\0\0",8); + is.Value = 0; + is.SectionNumber = VAL16(IMAGE_SYM_DEBUG); + is.Type = VAL16(IMAGE_SYM_DTYPE_NULL); + is.StorageClass = IMAGE_SYM_CLASS_FILE; + is.NumberOfAuxSymbols = 1; + if((pch = reloc->getBlock(sizeof(IMAGE_SYMBOL)))) + memcpy(pch,&is,sizeof(IMAGE_SYMBOL)); + else return E_OUTOFMEMORY; + TokInSymbolTable[NumberOfSymbols++] = 0; + memset(&is,0,sizeof(IMAGE_SYMBOL)); +#ifdef _WIN64 + _ASSERTE(!"this is probably broken!!"); +#endif // _WIN64 + strcpy_s((char*)&is,sizeof(is),(char*)(UINT_PTR)(rcur->offset)); + if((pch = reloc->getBlock(sizeof(IMAGE_SYMBOL)))) + memcpy(pch,&is,sizeof(IMAGE_SYMBOL)); + else return E_OUTOFMEMORY; +#ifdef _WIN64 + _ASSERTE(!"this is probably broken!!"); +#endif // _WIN64 + delete (char*)(UINT_PTR)(rcur->offset); + ToRelocTable = FALSE; + tk = 0; + szSymbolName = NULL; + break; + + case 0x7FFB: // compid value + TokInSymbolTable[NumberOfSymbols++] = 0; + memset(&is,0,sizeof(IMAGE_SYMBOL)); + memcpy(is.N.ShortName,"@comp.id",8); + is.Value = VAL32(rcur->offset); + is.SectionNumber = VAL16(IMAGE_SYM_ABSOLUTE); + is.Type = VAL16(IMAGE_SYM_DTYPE_NULL); + is.StorageClass = IMAGE_SYM_CLASS_STATIC; + is.NumberOfAuxSymbols = 0; + if((pch = reloc->getBlock(sizeof(IMAGE_SYMBOL)))) + memcpy(pch,&is,sizeof(IMAGE_SYMBOL)); + else return E_OUTOFMEMORY; + ToRelocTable = FALSE; + tk = 0; + szSymbolName = NULL; + break; + + case 0x7FFF: // Token value, def + tk = rcur->offset; + ToRelocTable = FALSE; + break; + + case 0x7FFE: //Token value, ref + tk = rcur->offset; + ToRelocTable = TRUE; + break; + + case 0x7FFD: //RVA value + rva = rcur->offset; + if(tk) + { + // Add to SymbolTable + DWORD i; + for(i = 0; (i < NumberOfSymbols)&&(tk != TokInSymbolTable[i]); i++); + if(i == NumberOfSymbols) + { + if(szSymbolName && *szSymbolName) // Add "extern" symbol and string table entry + { + TokInSymbolTable[NumberOfSymbols++] = 0; + memset(&is,0,sizeof(IMAGE_SYMBOL)); + i++; // so reloc record (if generated) points to COM token symbol + is.N.Name.Long = VAL32(StrTableLen); + is.SectionNumber = VAL16(1); //textSection is the first one + is.StorageClass = IMAGE_SYM_CLASS_EXTERNAL; + is.NumberOfAuxSymbols = 0; + is.Value = VAL32(rva); + if(TypeFromToken(tk) == mdtMethodDef) + { + is.Type = VAL16(0x20); //IMAGE_SYM_DTYPE_FUNCTION; + } + if((pch = reloc->getBlock(sizeof(IMAGE_SYMBOL)))) + memcpy(pch,&is,sizeof(IMAGE_SYMBOL)); + else return E_OUTOFMEMORY; + DWORD l = (DWORD)(strlen(szSymbolName)+1); // don't forget zero terminator! + if((pch = reloc->getBlock(1))) + memcpy(pch,szSymbolName,1); + else return E_OUTOFMEMORY; + delete szSymbolName; + StrTableLen += l; + } + TokInSymbolTable[NumberOfSymbols++] = tk; + memset(&is,0,sizeof(IMAGE_SYMBOL)); + sprintf_s((char*)is.N.ShortName,sizeof(is.N.ShortName),"%08X",tk); + is.SectionNumber = VAL16(1); //textSection is the first one + is.StorageClass = 0x6B; //IMAGE_SYM_CLASS_COM_TOKEN; + is.Value = VAL32(rva); + if(TypeFromToken(tk) == mdtMethodDef) + { + is.Type = VAL16(0x20); //IMAGE_SYM_DTYPE_FUNCTION; + //is.NumberOfAuxSymbols = 1; + } + if((pch = reloc->getBlock(sizeof(IMAGE_SYMBOL)))) + memcpy(pch,&is,sizeof(IMAGE_SYMBOL)); + else return E_OUTOFMEMORY; + if(is.NumberOfAuxSymbols == 1) + { + BYTE dummy[sizeof(IMAGE_SYMBOL)]; + memset(dummy,0,sizeof(IMAGE_SYMBOL)); + dummy[0] = dummy[2] = 1; + if((pch = reloc->getBlock(sizeof(IMAGE_SYMBOL)))) + memcpy(pch,dummy,sizeof(IMAGE_SYMBOL)); + else return E_OUTOFMEMORY; + TokInSymbolTable[NumberOfSymbols++] = 0; + } + } + if(ToRelocTable) + { + IMAGE_SECTION_HEADER* phdr = textSection->m_header; + // Add to reloc table + IMAGE_RELOC_FIELD(ir, VirtualAddress) = VAL32(rva); + ir.SymbolTableIndex = VAL32(i); + ir.Type = VAL16(IMAGE_REL_I386_SECREL); + if(phdr->PointerToRelocations == 0) + phdr->PointerToRelocations = VAL32(VAL32(phdr->PointerToRawData) + VAL32(phdr->SizeOfRawData)); + phdr->NumberOfRelocations = VAL32(VAL32(phdr->NumberOfRelocations) + 1); + if((pch = reloc->getBlock(sizeof(IMAGE_RELOCATION)))) + memcpy(pch,&is,sizeof(IMAGE_RELOCATION)); + else return E_OUTOFMEMORY; + } + } + ToRelocTable = FALSE; + tk = 0; + szSymbolName = NULL; + break; + + default: + break; + } //end switch(cur->type) + } // end for all relocs + // Add string table counter: + if((pch = reloc->getBlock(sizeof(ULONG)))) + memcpy(pch,&StrTableLen,sizeof(ULONG)); + else return E_OUTOFMEMORY; + reloc->m_header->Misc.VirtualSize = VAL32(reloc->dataLen()); + if(NumberOfSymbols) + { + // recompute the actual sizes and positions of all the sections + filePos = roundUp(VAL16(m_ntHeaders->FileHeader.NumberOfSections) * sizeof(IMAGE_SECTION_HEADER)+ + sizeof(IMAGE_FILE_HEADER), RoundUpVal); + for(cur = getSectStart(); cur < getSectCur(); cur++) + { + IMAGE_SECTION_HEADER* header = (*cur)->m_header; + header->Misc.VirtualSize = VAL32((*cur)->dataLen()); + header->VirtualAddress = VAL32(0); + header->SizeOfRawData = VAL32(roundUp(VAL32(header->Misc.VirtualSize), RoundUpVal)); + header->PointerToRawData = VAL32(filePos); + + filePos += VAL32(header->SizeOfRawData); + } + m_ntHeaders->FileHeader.Machine = VAL16(0xC0EE); //COM+ EE + m_ntHeaders->FileHeader.PointerToSymbolTable = VAL32(filePos); + m_ntHeaders->FileHeader.NumberOfSymbols = VAL32(NumberOfSymbols); + filePos += roundUp(VAL32(reloc->m_header->Misc.VirtualSize)+strtab->dataLen(),RoundUpVal); + } + delete[] TokInSymbolTable; + } //end if OBJ + + const unsigned headerOffset = (unsigned) (ExeOrDll ? sizeof(IMAGE_DOS_HEADER) + sizeof(x86StubPgm) : 0); + + memset(&m_dosHeader, 0, sizeof(IMAGE_DOS_HEADER)); + m_dosHeader.e_magic = VAL16(IMAGE_DOS_SIGNATURE); + m_dosHeader.e_cblp = VAL16(0x90); // bytes in last page + m_dosHeader.e_cp = VAL16(3); // pages in file + m_dosHeader.e_cparhdr = VAL16(4); // size of header in paragraphs + m_dosHeader.e_maxalloc = VAL16(0xFFFF); // maximum extra mem needed + m_dosHeader.e_sp = VAL16(0xB8); // initial SP value + m_dosHeader.e_lfarlc = VAL16(0x40); // file offset of relocations + m_dosHeader.e_lfanew = VAL32(headerOffset); // file offset of NT header! + + return(S_OK); // SUCCESS +} +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +HRESULT PEWriter::Open(__in LPCWSTR fileName) +{ + _ASSERTE(m_file == INVALID_HANDLE_VALUE); + HRESULT hr = NOERROR; + + m_file = WszCreateFile(fileName, + GENERIC_WRITE, + 0, // No sharing. Was: FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL ); + if (m_file == INVALID_HANDLE_VALUE) + hr = HRESULT_FROM_GetLastErrorNA(); + + return hr; +} + +HRESULT PEWriter::Seek(int offset) +{ + _ASSERTE(m_file != INVALID_HANDLE_VALUE); + if (SetFilePointer(m_file, offset, 0, FILE_BEGIN)) + return S_OK; + else + return HRESULT_FROM_GetLastError(); +} + +HRESULT PEWriter::Write(const void *data, int size) +{ + _ASSERTE(m_file != INVALID_HANDLE_VALUE); + + HRESULT hr = S_OK; + DWORD dwWritten = 0; + if (size) + { + CQuickBytes zero; + if (data == NULL) + { + hr = zero.ReSizeNoThrow(size); + if (SUCCEEDED(hr)) + { + ZeroMemory(zero.Ptr(), size); + data = zero.Ptr(); + } + } + + if (WriteFile(m_file, data, size, &dwWritten, NULL)) + { + _ASSERTE(dwWritten == (DWORD)size); + } + else + hr = HRESULT_FROM_GetLastError(); + } + + return hr; +} + +HRESULT PEWriter::Pad(int align) +{ + DWORD offset = SetFilePointer(m_file, 0, NULL, FILE_CURRENT); + int pad = padLen(offset, align); + if (pad > 0) + return Write(NULL, pad); + else + return S_FALSE; +} + +HRESULT PEWriter::Close() +{ + if (m_file == INVALID_HANDLE_VALUE) + return S_OK; + + HRESULT hr; + if (CloseHandle(m_file)) + hr = S_OK; + else + hr = HRESULT_FROM_GetLastError(); + + m_file = INVALID_HANDLE_VALUE; + + return hr; +} + +/******************************************************************/ +HRESULT PEWriter::write(__in const LPWSTR fileName) { + + HRESULT hr; + +#ifdef ENC_DELTA_HACK + WCHAR szFileName[256]; + DWORD len = WszGetEnvironmentVariable(L"COMP_ENC_EMIT", szFileName, NumItems(szFileName)); + _ASSERTE(len < sizeof(szFileName)); + if (len > 0) + { + _ASSERTE(!m_pSeedFileDecoder); + + wcscat_s(szFileName, sizeof(szFileName)/sizeof(szFileName[0]), L".dil"); + HANDLE pDelta = WszCreateFile(szFileName, + GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL ); + if (pDelta == INVALID_HANDLE_VALUE) { + hr = HRESULT_FROM_GetLastError(); + _ASSERTE(!"failure so open .dil file"); + goto ErrExit; + } + + // write the actual data + for (PEWriterSection **cur = getSectStart(); cur < getSectCur(); cur++) { + if (strcmp((*cur)->m_name, ".text") == 0) + { + hr = (*cur)->write(pDelta); + CloseHandle(pDelta); + pDelta = NULL; + if (FAILED(hr)) + { + _ASSERT(!"failure to write to .dil file"); + goto ErrExit; + } + break; + } + } + PREFIX_ASSUME(!pDelta); + return S_OK; + } +#endif + + bool ExeOrDll; + unsigned RoundUpVal; + ExeOrDll = isExeOrDll(m_ntHeaders); + RoundUpVal = VAL32(m_ntHeaders->OptionalHeader.FileAlignment); + + IfFailGo(Open(fileName)); + + if(ExeOrDll) + { + // write the PE headers + IfFailGo(Write(&m_dosHeader, sizeof(IMAGE_DOS_HEADER))); + IfFailGo(Write(x86StubPgm, sizeof(x86StubPgm))); + IfFailGo(Write(m_ntHeaders, m_ntHeadersSize)); + } + else + { + // write the object file header + IfFailGo(Write(&(m_ntHeaders->FileHeader),sizeof(IMAGE_FILE_HEADER))); + } + + IfFailGo(Write(headers, (int)(sizeof(IMAGE_SECTION_HEADER)*(headersEnd-headers)))); + + IfFailGo(Pad(RoundUpVal)); + + // write the actual data + for (PEWriterSection **cur = getSectStart(); cur < getSectCur(); cur++) { + if ((*cur)->m_header != NULL) { + IfFailGo(Seek((*cur)->m_filePos)); + IfFailGo((*cur)->write(m_file)); + IfFailGo(Write(NULL, (*cur)->m_filePad)); + } + } + + // writes for an object file + if (!ExeOrDll) + { + // write the relocs section (Does nothing if relocs section is empty) + IfFailGo(reloc->write(m_file)); + //write string table (obj only, empty for exe or dll) + IfFailGo(strtab->write(m_file)); + int lena = padLen(VAL32(reloc->m_header->Misc.VirtualSize)+strtab->dataLen(), RoundUpVal); + if (lena > 0) + IfFailGo(Write(NULL, lena)); + } + + return Close(); + + ErrExit: + Close(); + + return hr; +} + +HRESULT PEWriter::write(void ** ppImage) +{ + bool ExeOrDll = isExeOrDll(m_ntHeaders); + const unsigned RoundUpVal = VAL32(m_ntHeaders->OptionalHeader.FileAlignment); + char *pad = (char *) _alloca(RoundUpVal); + memset(pad, 0, RoundUpVal); + + size_t lSize = filePos; + if (!ExeOrDll) + { + lSize += reloc->dataLen(); + lSize += strtab->dataLen(); + lSize += padLen(VAL32(reloc->m_header->Misc.VirtualSize)+strtab->dataLen(), + RoundUpVal); + } + + // allocate the block we are handing back to the caller + void * pImage = (void *) ::CoTaskMemAlloc(lSize); + if (NULL == pImage) + { + return E_OUTOFMEMORY; + } + + // zero the memory + ::memset(pImage, 0, lSize); + + char *pCur = (char *)pImage; + + if(ExeOrDll) + { + // PE Header + COPY_AND_ADVANCE(pCur, &m_dosHeader, sizeof(IMAGE_DOS_HEADER)); + COPY_AND_ADVANCE(pCur, x86StubPgm, sizeof(x86StubPgm)); + COPY_AND_ADVANCE(pCur, m_ntHeaders, m_ntHeadersSize); + } + else + { + COPY_AND_ADVANCE(pCur, &(m_ntHeaders->FileHeader), sizeof(IMAGE_FILE_HEADER)); + } + + COPY_AND_ADVANCE(pCur, headers, sizeof(*headers)*(headersEnd - headers)); + + // now the sections + // write the actual data + for (PEWriterSection **cur = getSectStart(); cur < getSectCur(); cur++) { + if ((*cur)->m_header != NULL) { + unsigned len; + pCur = (char*)pImage + (*cur)->m_filePos; + len = (*cur)->writeMem((void**)&pCur); + _ASSERTE(len == (*cur)->dataLen()); + COPY_AND_ADVANCE(pCur, pad, (*cur)->m_filePad); + } + } + + // !!! Need to jump to the right place... + + if (!ExeOrDll) + { + // now the relocs (exe, dll) or symbol table (obj) (if any) + // write the relocs section (Does nothing if relocs section is empty) + reloc->writeMem((void **)&pCur); + + //write string table (obj only, empty for exe or dll) + strtab->writeMem((void **)&pCur); + + // final pad + size_t len = padLen(VAL32(reloc->m_header->Misc.VirtualSize)+strtab->dataLen(), RoundUpVal); + if (len > 0) + { + // WARNING: macro - must enclose in curly braces + COPY_AND_ADVANCE(pCur, pad, len); + } + } + + // make sure we wrote the exact numbmer of bytes expected + _ASSERTE(lSize == (size_t) (pCur - (char *)pImage)); + + // give pointer to memory image back to caller (who must free with ::CoTaskMemFree()) + *ppImage = pImage; + + // all done + return S_OK; +} + +HRESULT PEWriter::getFileTimeStamp(DWORD *pTimeStamp) +{ + if (pTimeStamp) + *pTimeStamp = m_peFileTimeStamp; + + return S_OK; +} + +DWORD PEWriter::getImageBase32() +{ + _ASSERTE(isPE32()); + return VAL32(ntHeaders32()->OptionalHeader.ImageBase); +} + +UINT64 PEWriter::getImageBase64() +{ + _ASSERTE(!isPE32()); + return VAL64(ntHeaders64()->OptionalHeader.ImageBase); +} + +void PEWriter::setImageBase32(DWORD imageBase) +{ + _ASSERTE(m_hSeedFile == INVALID_HANDLE_VALUE); + + _ASSERTE(isPE32()); + ntHeaders32()->OptionalHeader.ImageBase = VAL32(imageBase); +} + +void PEWriter::setImageBase64(UINT64 imageBase) +{ + _ASSERTE(!isPE32()); + ntHeaders64()->OptionalHeader.ImageBase = VAL64(imageBase); +} + +void PEWriter::getHeaderInfo(PIMAGE_NT_HEADERS *ppNtHeaders, PIMAGE_SECTION_HEADER *ppSections, ULONG *pNumSections) +{ + *ppNtHeaders = m_ntHeaders; + *ppSections = headers; + *pNumSections = (ULONG)(headersEnd - headers); +} diff --git a/src/dlls/mscorpe/pewriter.h b/src/dlls/mscorpe/pewriter.h new file mode 100644 index 0000000000..67441eee75 --- /dev/null +++ b/src/dlls/mscorpe/pewriter.h @@ -0,0 +1,338 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +#ifndef PEWriter_H +#define PEWriter_H + +#include + +#include + +#include "ceegen.h" + +#include "pesectionman.h" + +class PEWriter; +class PEWriterSection; +class PEDecoder; +struct entry; +struct _IMAGE_SECTION_HEADER; + +#define SUBSECTION_ALIGN 16 + +/***************************************************************/ +// PEWriter is derived from PESectionManager. While the base class just +// manages the sections, PEWriter can actually write them out. + +class PEWriter : public PESectionMan +{ +public: + + // See ICeeFileGen.h for definition of createFlags + HRESULT Init(PESectionMan *pFrom, DWORD createFlags, LPCWSTR seedFileName = NULL); + HRESULT Cleanup(); + + // Finds section with given name. returns 0 if not found + virtual PESection* getSection(const char* name); + + // Create a new section + virtual HRESULT newSection(const char* name, PESection **section, + unsigned flags=sdNone, unsigned estSize=0x10000, + unsigned estRelocs=0x100); + + HRESULT link(); + HRESULT fixup(CeeGenTokenMapper *pMapper); + HRESULT write(__in const LPWSTR fileName); + HRESULT write(void **ppImage); + + // calling these functions is optional + DWORD getSectionAlignment(); + void setSectionAlignment(DWORD); + DWORD getFileAlignment(); + void setFileAlignment(DWORD); + DWORD getImageBase32(); + void setImageBase32(DWORD); + UINT64 getImageBase64(); + void setImageBase64(UINT64); + void stripRelocations(bool val); // default = false + + void getHeaderInfo(PIMAGE_NT_HEADERS *ppNtHeaders, PIMAGE_SECTION_HEADER *ppSections, ULONG *pNumSections); + + // these affect the charactertics in the NT file header + void setCharacteristics(unsigned mask); + void clearCharacteristics(unsigned mask); + + // these affect the charactertics in the NT optional header + void setDllCharacteristics(unsigned mask); + void clearDllCharacteristics(unsigned mask); + + // sets the SubSystem field in the optional header + void setSubsystem(unsigned subsystem, unsigned major, unsigned minor); + + // specify the entry point as an offset into the text section. The + // method will convert into an RVA from the base + void setEntryPointTextOffset(unsigned entryPoint); + + HRESULT setDirectoryEntry(PEWriterSection *section, ULONG entry, ULONG size, ULONG offset=0); + + + // get the RVA for the IL section + ULONG getIlRva(); + + // set the RVA for the IL section by supplying offset to the IL section + void setIlRva(DWORD offset); + + unsigned getSubsystem(); + + size_t getImageBase(); + + void setEnCRvaBase(ULONG dataBase, ULONG rdataBase); + + HRESULT getFileTimeStamp(DWORD *pTimeStamp); + + IMAGE_NT_HEADERS32* ntHeaders32() { return (IMAGE_NT_HEADERS32*) m_ntHeaders; } + IMAGE_NT_HEADERS64* ntHeaders64() { return (IMAGE_NT_HEADERS64*) m_ntHeaders; } + + bool isPE32() // true -> created a PE 32-bit PE file + // false -> created a PE+ 64-bit PE file + { return (m_ntHeaders->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR32_MAGIC)); } + + bool isI386() // true -> target machine is i386 + { return (m_ntHeaders->FileHeader.Machine == VAL16(IMAGE_FILE_MACHINE_I386)); } + + bool isIA64() // true -> target machine is ia64 + { return (m_ntHeaders->FileHeader.Machine == VAL16(IMAGE_FILE_MACHINE_IA64)); } + + bool isAMD64() // true -> target machine is ia64 + { return (m_ntHeaders->FileHeader.Machine == VAL16(IMAGE_FILE_MACHINE_AMD64)); } + + bool isARM() // true -> target machine is ARM + { return (m_ntHeaders->FileHeader.Machine == VAL16(IMAGE_FILE_MACHINE_ARMNT)); } + + bool createCorMainStub() // do we need the CorExeMain/CorDllMain stubs? + { return m_createCorMainStub; } + +private: + DWORD m_ilRVA; + BOOL m_encMode; + ULONG m_dataRvaBase; + ULONG m_rdataRvaBase; + ULONG m_codeRvaBase; + DWORD m_peFileTimeStamp; + + HANDLE m_file; + + // "Seed" file information. The new file data will be "appended" to the seed file + // These are valid only if m_hSeedFile is valid + + HANDLE m_hSeedFile; + HANDLE m_hSeedFileMap; + PEDecoder * m_pSeedFileDecoder; + IMAGE_NT_HEADERS * m_pSeedFileNTHeaders; + unsigned m_iSeedSections; + IMAGE_SECTION_HEADER*m_pSeedSections; + IMAGE_SECTION_HEADER*m_pSeedSectionToAdd; // used only by newSection() + + PEWriterSection **getSectStart() { + return (PEWriterSection**)sectStart; + } + + PEWriterSection **getSectCur() { + return (PEWriterSection**)sectCur; + } + + COUNT_T getSectCount() { + return COUNT_T(sectCur - sectStart); + } + + + IMAGE_DOS_HEADER m_dosHeader; + IMAGE_NT_HEADERS * m_ntHeaders; + DWORD m_ntHeadersSize; // Size of IMAGE_NT_HEADERS (not including section headers) + + unsigned virtualPos; + unsigned filePos; + + PEWriterSection * reloc; + PEWriterSection * strtab; + + IMAGE_SECTION_HEADER *headers, *headersEnd; + + struct directoryEntry { + PEWriterSection *section; + ULONG offset; + ULONG size; + }; + + // Directory entries in the file header + directoryEntry * pEntries; + USHORT cEntries; + + bool m_createCorMainStub; + + // Helpers for link() + HRESULT linkSortSections(entry * entries, + unsigned * piEntries, // OUT + unsigned * piUniqueSections); // OUT + HRESULT linkSortHeaders(entry * entries, unsigned iEntries, unsigned iUniqueSections); + HRESULT linkPlaceSections(entry * entries, unsigned iEntries); + void setSectionIndex(IMAGE_SECTION_HEADER * h, unsigned sectionIndex); + + HRESULT Open(__in LPCWSTR fileName); + HRESULT Write(const void *data, int size); + HRESULT Seek(int offset); + HRESULT Pad(int align); + HRESULT Close(); +}; + +// This class encapsulates emitting the base relocs into the +// .reloc section of the PE file, for the case where the image +// does not get loaded at its preferred base address. + +class PERelocSection +{ + private: + PEWriterSection * section; + unsigned relocPage; + unsigned relocSize; + DWORD * relocSizeAddr; + unsigned pages; + +#ifdef _DEBUG + unsigned lastRVA; +#endif + + public: + PERelocSection(PEWriterSection *pBaseReloc); + void AddBaseReloc(unsigned rva, int type, unsigned short highAdj=0); + void Finish(bool isPE32); +}; + +class PEWriterSection : public PESection { + +public: + + PEWriterSection(const char* name, unsigned flags, + unsigned estSize, unsigned estRelocs) + : PESection(name, flags, estSize, estRelocs) {} + + virtual HRESULT applyRelocs(IMAGE_NT_HEADERS * pNtHeaders, + PERelocSection * relocSection, + CeeGenTokenMapper * pTokenMapper, + DWORD rdataRvaBase, + DWORD dataRvaBase, + DWORD textRvaBase); + + virtual HRESULT write (HANDLE file); + virtual unsigned writeMem (void ** pMem); + virtual bool isSeedSection() { return false; } + +}; + +// This is for sections from the seed file. Their order needs to be maintained and +// they need to be written to the output file. + +class PESeedSection : public PEWriterSection { + +public: + + PESeedSection(PEDecoder * peDecoder, IMAGE_SECTION_HEADER * seedSection); + + // PESection methods + + unsigned dataLen() { return m_pSeedSectionHeader->SizeOfRawData; } + HRESULT applyRelocs(CeeGenTokenMapper *pTokenMapper) { return S_OK; } + char* getBlock(unsigned len, unsigned align) { _ASSERTE(!"PESeedSection"); return NULL; } + HRESULT truncate(unsigned newLen) { _ASSERTE(!"PESeedSection"); return E_FAIL; } + void writeSectReloc(unsigned val, CeeSection& relativeTo, + CeeSectionRelocType reloc, + CeeSectionRelocExtra *extra) { _ASSERTE(!"PESeedSection"); return; } + HRESULT addSectReloc(unsigned offset, CeeSection& relativeTo, + CeeSectionRelocType reloc, + CeeSectionRelocExtra *extra) { _ASSERTE(!"PESeedSection"); return E_FAIL; } + HRESULT addSectReloc(unsigned offset, PESection *relativeTo, + CeeSectionRelocType reloc, + CeeSectionRelocExtra *extra) { _ASSERTE(!"PESeedSection"); return E_FAIL; } + HRESULT addBaseReloc(unsigned offset, CeeSectionRelocType reloc, + CeeSectionRelocExtra *extra) { _ASSERTE(!"PESeedSection"); return E_FAIL; } +// unsigned char *name(); +// unsigned flags(); +// unsigned getBaseRVA(); + int getDirEntry() { _ASSERTE(!"PESeedSection"); return 0; } + HRESULT directoryEntry(unsigned num) { _ASSERTE(!"PESeedSection"); return E_FAIL; } + char * computePointer(unsigned offset) const { _ASSERTE(!"PESeedSection"); return NULL; } + BOOL containsPointer(__in char *ptr) const { _ASSERTE(!"PESeedSection"); return FALSE; } + unsigned computeOffset(__in char *ptr) const { _ASSERTE(!"PESeedSection"); return 0; } + HRESULT cloneInstance(PESection *destination) { _ASSERTE(!"PESeedSection"); return E_FAIL; } + + // PEWriterSection + + HRESULT applyRelocs(IMAGE_NT_HEADERS * pNtHeaders, + PERelocSection * relocSection, + CeeGenTokenMapper * pTokenMapper, + DWORD rdataRvaBase, + DWORD dataRvaBase, + DWORD textRvaBase) { return S_OK; } + + HRESULT write(HANDLE file); + unsigned writeMem(void ** pMem); + bool isSeedSection() { return true; } + +protected: + + PEDecoder * m_pSeedFileDecoder; + IMAGE_SECTION_HEADER * m_pSeedSectionHeader; + +}; + +inline DWORD PEWriter::getSectionAlignment() { + return VAL32(m_ntHeaders->OptionalHeader.FileAlignment); +} + +inline void PEWriter::setSectionAlignment(DWORD SectionAlignment) { + + _ASSERTE(m_hSeedFile == INVALID_HANDLE_VALUE); + m_ntHeaders->OptionalHeader.SectionAlignment = VAL32(SectionAlignment); +} + +inline DWORD PEWriter::getFileAlignment() { + return m_ntHeaders->OptionalHeader.FileAlignment; +} + +inline void PEWriter::setFileAlignment(DWORD fileAlignment) { + _ASSERTE(m_hSeedFile == INVALID_HANDLE_VALUE); + m_ntHeaders->OptionalHeader.FileAlignment = VAL32(fileAlignment); +} + +inline unsigned PEWriter::getSubsystem() { + return VAL16(m_ntHeaders->OptionalHeader.Subsystem); +} + +inline void PEWriter::setSubsystem(unsigned subsystem, unsigned major, unsigned minor) { + m_ntHeaders->OptionalHeader.Subsystem = VAL16(subsystem); + m_ntHeaders->OptionalHeader.MajorSubsystemVersion = VAL16(major); + m_ntHeaders->OptionalHeader.MinorSubsystemVersion = VAL16(minor); +} + +inline void PEWriter::setCharacteristics(unsigned mask) { + m_ntHeaders->FileHeader.Characteristics |= VAL16(mask); +} + +inline void PEWriter::clearCharacteristics(unsigned mask) { + m_ntHeaders->FileHeader.Characteristics &= VAL16(~mask); +} + +inline void PEWriter::setDllCharacteristics(unsigned mask) { + m_ntHeaders->OptionalHeader.DllCharacteristics |= VAL16(mask); +} + +inline void PEWriter::clearDllCharacteristics(unsigned mask) { + m_ntHeaders->OptionalHeader.DllCharacteristics &= VAL16(~mask); +} + +inline void PEWriter::setEntryPointTextOffset(unsigned offset) { + m_ntHeaders->OptionalHeader.AddressOfEntryPoint = VAL32(offset); +} + +#endif diff --git a/src/dlls/mscorpe/stdafx.cpp b/src/dlls/mscorpe/stdafx.cpp new file mode 100644 index 0000000000..9d644a32b1 --- /dev/null +++ b/src/dlls/mscorpe/stdafx.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//***************************************************************************** +// stdafx.cpp +// +// Host for precompiled header. +// +//***************************************************************************** +#include "stdafx.h" // Precompiled header key. diff --git a/src/dlls/mscorpe/stdafx.h b/src/dlls/mscorpe/stdafx.h new file mode 100644 index 0000000000..2d34ce2ba6 --- /dev/null +++ b/src/dlls/mscorpe/stdafx.h @@ -0,0 +1,25 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//***************************************************************************** +// stdafx.h +// +// Common include file for utility code. +//***************************************************************************** +#include // for qsort +#include +#include +#include +#include +#include + +#define FEATURE_NO_HOST // Do not use host interface +#include + +#include + +#include "pewriter.h" +#include "ceegen.h" +#include "ceefilegenwriter.h" +#include "ceesectionstring.h" diff --git a/src/dlls/mscorpe/stubs.h b/src/dlls/mscorpe/stubs.h new file mode 100644 index 0000000000..e232591518 --- /dev/null +++ b/src/dlls/mscorpe/stubs.h @@ -0,0 +1,170 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//***************************************************************************** +// Stubs.h +// +// This file contains a template for the default entry point stubs of a COM+ +// IL only program. One can emit these stubs (with some fix-ups) and make +// the code supplied the entry point value for the image. The fix-ups will +// in turn cause mscoree.dll to be loaded and the correct entry point to be +// called. +// +// Note: Although these stubs contain x86 specific code, they are used +// for all platforms +// +//***************************************************************************** +#ifndef __STUBS_H__ +#define __STUBS_H__ + +//***************************************************************************** +// This stub is designed for a x86 Windows application. It will call the +// _CorExeMain function in mscoree.dll. This entry point will in turn load +// and run the IL program. +// +// jump _CorExeMain(); +// +// The code jumps to the imported function _CorExeMain using the iat. +// The address in the template is address of the iat entry which is +// fixed up by the loader when the image is paged in. +//***************************************************************************** + +SELECTANY const BYTE ExeMainX86Template[] = +{ + // Jump through IAT to _CorExeMain + 0xFF, 0x25, // jmp [iat:_CorDllMain entry] + 0x00, 0x00, 0x00, 0x00, // address to replace + +}; + +#define ExeMainX86TemplateSize sizeof(ExeMainX86Template) +#define CorExeMainX86IATOffset 2 + +//***************************************************************************** +// This stub is designed for a x86 Windows application. It will call the +// _CorDllMain function in mscoree.dll with with the base entry point for +// the loaded DLL. This entry point will in turn load and run the IL program. +// +// jump _CorDllMain +// +// The code jumps to the imported function _CorExeMain using the iat. +// The address in the template is address of the iat entry which is +// fixed up by the loader when the image is paged in. +//***************************************************************************** + +SELECTANY const BYTE DllMainX86Template[] = +{ + // Jump through IAT to CorDllMain + 0xFF, 0x25, // jmp [iat:_CorDllMain entry] + 0x00, 0x00, 0x00, 0x00, // address to replace +}; + +#define DllMainX86TemplateSize sizeof(DllMainX86Template) +#define CorDllMainX86IATOffset 2 + +//***************************************************************************** +// This stub is designed for a AMD64 Windows application. It will call the +// _CorExeMain function in mscoree.dll. This entry point will in turn load +// and run the IL program. +// +// mov rax, _CorExeMain(); +// jmp [rax] +// +// The code jumps to the imported function _CorExeMain using the iat. +// The address in the template is address of the iat entry which is +// fixed up by the loader when the image is paged in. +//***************************************************************************** + +SELECTANY const BYTE ExeMainAMD64Template[] = +{ + // Jump through IAT to _CorExeMain + 0x48, 0xA1, // rex.w rex.b mov rax,[following address] + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,//address of iat:_CorExeMain entry + 0xFF, 0xE0 // jmp [rax] +}; + +#define ExeMainAMD64TemplateSize sizeof(ExeMainAMD64Template) +#define CorExeMainAMD64IATOffset 2 + +//***************************************************************************** +// This stub is designed for a AMD64 Windows application. It will call the +// _CorDllMain function in mscoree.dll with with the base entry point for +// the loaded DLL. This entry point will in turn load and run the IL program. +// +// mov rax, _CorDllMain(); +// jmp [rax] +// +// The code jumps to the imported function _CorDllMain using the iat. +// The address in the template is address of the iat entry which is +// fixed up by the loader when the image is paged in. +//***************************************************************************** + +SELECTANY const BYTE DllMainAMD64Template[] = +{ + // Jump through IAT to CorDllMain + 0x48, 0xA1, // rex.w rex.b mov rax,[following address] + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,//address of iat:_CorDllMain entry + 0xFF, 0xE0 // jmp [rax] +}; + +#define DllMainAMD64TemplateSize sizeof(DllMainAMD64Template) +#define CorDllMainAMD64IATOffset 2 + +//***************************************************************************** +// This stub is designed for an ia64 Windows application. It will call the +// _CorExeMain function in mscoree.dll. This entry point will in turn load +// and run the IL program. +// +// jump _CorExeMain(); +// +// The code jumps to the imported function _CorExeMain using the iat. +// We set the value of gp to point at the iat table entry for _CorExeMain +//***************************************************************************** + +SELECTANY const BYTE ExeMainIA64Template[] = +{ + // ld8 r9 = [gp] ;; + // ld8 r10 = [r9],8 + // nop.i ;; + // ld8 gp = [r9] + // mov b6 = r10 + // br.cond.sptk.few b6 + // + 0x0B, 0x48, 0x00, 0x02, 0x18, 0x10, 0xA0, 0x40, + 0x24, 0x30, 0x28, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x10, 0x08, 0x00, 0x12, 0x18, 0x10, 0x60, 0x50, + 0x04, 0x80, 0x03, 0x00, 0x60, 0x00, 0x80, 0x00 +}; + +#define ExeMainIA64TemplateSize sizeof(ExeMainIA64Template) + +//***************************************************************************** +// This stub is designed for an ia64 Windows application. It will call the +// _CorDllMain function in mscoree.dll with with the base entry point for +// the loaded DLL. This entry point will in turn load and run the IL program. +// +// jump _CorDllMain +// +// The code jumps to the imported function _CorExeMain using the iat. +// We set the value of gp to point at the iat table entry for _CorExeMain +//***************************************************************************** + +SELECTANY const BYTE DllMainIA64Template[] = +{ + // ld8 r9 = [gp] ;; + // ld8 r10 = [r9],8 + // nop.i ;; + // ld8 gp = [r9] + // mov b6 = r10 + // br.cond.sptk.few b6 + // + 0x0B, 0x48, 0x00, 0x02, 0x18, 0x10, 0xA0, 0x40, + 0x24, 0x30, 0x28, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x10, 0x08, 0x00, 0x12, 0x18, 0x10, 0x60, 0x50, + 0x04, 0x80, 0x03, 0x00, 0x60, 0x00, 0x80, 0x00 +}; + +#define DllMainIA64TemplateSize sizeof(DllMainIA64Template) + +#endif // __STUBS_H__ diff --git a/src/dlls/mscorpe/utilcodeinit.cpp b/src/dlls/mscorpe/utilcodeinit.cpp new file mode 100644 index 0000000000..3c50bb340e --- /dev/null +++ b/src/dlls/mscorpe/utilcodeinit.cpp @@ -0,0 +1,12 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +#include "stdafx.h" +#include + +EXTERN_C void __stdcall InitializeSxS(CoreClrCallbacks const & callbacks) +{ + InitUtilcode(callbacks); +} diff --git a/src/ilasm/.gitmirrorall b/src/ilasm/.gitmirrorall new file mode 100644 index 0000000000..9ee5c57b99 --- /dev/null +++ b/src/ilasm/.gitmirrorall @@ -0,0 +1 @@ +This folder will be mirrored by the Git-TFS Mirror recursively. \ No newline at end of file diff --git a/src/ilasm/Assembler.h b/src/ilasm/Assembler.h new file mode 100644 index 0000000000..f3e49148e0 --- /dev/null +++ b/src/ilasm/Assembler.h @@ -0,0 +1,1186 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +/************************************************************************/ +/* Assembler.h */ +/************************************************************************/ + +#ifndef Assember_h +#define Assember_h + +#define NEW_INLINE_NAMES + +#include "binstr.h" + +#include "specstrings.h" + +#include "asmenum.h" +#include "asmtemplates.h" + +// Disable the "initialization of static local vars is no thread safe" error +#ifdef _MSC_VER +#pragma warning(disable : 4640) +#endif + + + +#define OUTPUT_BUFFER_SIZE 8192 // initial size of asm code for a single method +#define OUTPUT_BUFFER_INCREMENT 1024 // size of code buffer increment when it's full +#define MAX_FILENAME_LENGTH 2048 //256 +#define MAX_SIGNATURE_LENGTH 256 // unused +#define MAX_LABEL_SIZE 256 //64 +#define MAX_CALL_SIG_SIZE 32 // unused +#define MAX_SCOPE_LENGTH _MAX_PATH // follow the RegMeta::SetModuleProps limitation + +#define MAX_NAMESPACE_LENGTH 1024 //256 //64 +#define MAX_MEMBER_NAME_LENGTH 1024 //256 //64 + +#define MAX_INTERFACES_IMPLEMENTED 16 // initial number; extended by 16 when needed +#define GLOBAL_DATA_SIZE 8192 // initial size of global data buffer +#define GLOBAL_DATA_INCREMENT 1024 // size of global data buffer increment when it's full +#define MAX_METHODS 1024 // unused +#define MAX_INPUT_LINE_LEN 1024 // unused +#define MAX_TYPAR 8 +#define BASE_OBJECT_CLASSNAME "System.Object" +#define MAX_MANIFEST_RESOURCES 1024 + +// Fully-qualified class name separators: +#define NESTING_SEP ((char)0xF8) + +#define dwUniBuf 16384 +extern WCHAR wzUniBuf[]; // Unicode conversion global buffer (assem.cpp) + +class Class; +class Method; +class PermissionDecl; +class PermissionSetDecl; + +unsigned hash( // defined in assem.cpp + __in_ecount(length) const BYTE *k, /* the key */ + unsigned length, /* the length of the key */ + unsigned initval); /* the previous hash, or an arbitrary value */ + +struct MemberRefDescriptor +{ + mdToken m_tdClass; + Class* m_pClass; + char* m_szName; + DWORD m_dwName; + BinStr* m_pSigBinStr; + mdToken m_tkResolved; +}; +typedef FIFO MemberRefDList; + + +struct MethodImplDescriptor +{ + mdToken m_tkImplementedMethod; + mdToken m_tkImplementingMethod; + mdToken m_tkDefiningClass; + BOOL m_fNew; +}; +typedef FIFO MethodImplDList; + +struct LocalMemberRefFixup +{ + mdToken tk; + size_t offset; + BOOL m_fNew; + LocalMemberRefFixup(mdToken TK, size_t Offset) + { + tk = TK; + offset = Offset; + m_fNew = TRUE; + } +}; +typedef FIFO LocalMemberRefFixupList; + +struct CustomDescr +{ + mdToken tkType; + mdToken tkOwner; + mdToken tkInterfacePair; // Needed for InterfaceImpl CA's + BinStr* pBlob; + CustomDescr(mdToken tko, mdToken tk, BinStr* pblob) { tkType = tk; pBlob = pblob; tkOwner = tko; tkInterfacePair = 0; }; + CustomDescr(mdToken tk, BinStr* pblob) { tkType = tk; pBlob = pblob; tkOwner = 0; tkInterfacePair = 0;}; + CustomDescr(CustomDescr* pOrig) { tkType = pOrig->tkType; pBlob = new BinStr(); pBlob->append(pOrig->pBlob); tkOwner = pOrig->tkOwner; tkInterfacePair = pOrig->tkInterfacePair; }; + ~CustomDescr() { if(pBlob) delete pBlob; }; +}; +typedef FIFO CustomDescrList; +typedef LIFO CustomDescrListStack; +/**************************************************************************/ +#include "typar.hpp" +#include "method.hpp" +#include "iceefilegen.h" +#include "asmman.hpp" + +#include "nvpair.h" + + +typedef enum +{ + STATE_OK, + STATE_FAIL, + STATE_ENDMETHOD, + STATE_ENDFILE +} state_t; + + +class GlobalLabel +{ +public: + LPCUTF8 m_szName; + DWORD m_GlobalOffset; + HCEESECTION m_Section; + unsigned m_Hash; + + GlobalLabel(LPCUTF8 pszName, DWORD GlobalOffset, HCEESECTION section) + { + m_GlobalOffset = GlobalOffset; + m_Section = section; + m_szName = pszName; + m_Hash = hash((const BYTE*)pszName, (unsigned)strlen(pszName),10); + } + + ~GlobalLabel(){ delete [] m_szName; } + + int ComparedTo(GlobalLabel* L) + { + return (m_Hash == L->m_Hash) ? strcmp(m_szName, L->m_szName) + : ((m_Hash > L->m_Hash) ? 1 : -1); + } + + //int ComparedTo(GlobalLabel* L) { return strcmp(m_szName,L->m_szName); }; + //int Compare(char* L) { return strcmp(L, m_szNam); }; + //char* NameOf() { return m_szName; }; +}; +//typedef SORTEDARRAY GlobalLabelList; +typedef RBTREE GlobalLabelList; +//typedef FIFO_INDEXED GlobalLabelList; + +class CeeFileGenWriter; +class CeeSection; + +class BinStr; + +/************************************************************************/ +/* represents an object that knows how to report errors back to the user */ + +class ErrorReporter +{ +public: + virtual void error(const char* fmt, ...) = 0; + virtual void warn(const char* fmt, ...) = 0; + virtual void msg(const char* fmt, ...) = 0; +}; + +/**************************************************************************/ +/* represents a switch table before the lables are bound */ + +struct Labels { + Labels(__in __nullterminated char* aLabel, Labels* aNext, bool aIsLabel) : Label(aLabel), Next(aNext), isLabel(aIsLabel) {} + ~Labels() { if(isLabel && Label) delete [] Label; delete Next; } + + char* Label; + Labels* Next; + bool isLabel; +}; + +/**************************************************************************/ +/* descriptor of the structured exception handling construct */ +struct SEH_Descriptor +{ + DWORD sehClause; // catch/filter/finally + DWORD tryFrom; // start of try block + DWORD tryTo; // end of try block + DWORD sehHandler; // start of exception handler + DWORD sehHandlerTo; // end of exception handler + union { + DWORD sehFilter; // start of filter block + mdTypeRef cException; // what to catch + }; + + SEH_Descriptor() + { + memset(this, 0, sizeof(*this)); + } +}; + + +typedef LIFO StringStack; +typedef LIFO SEHD_Stack; + +typedef FIFO MethodList; +//typedef SORTEDARRAY MethodSortedList; +typedef FIFO TokenList; +/**************************************************************************/ +/* The field, event and property descriptor structures */ + +struct FieldDescriptor +{ + mdTypeDef m_tdClass; + char* m_szName; + DWORD m_dwName; + mdFieldDef m_fdFieldTok; + ULONG m_ulOffset; + char* m_rvaLabel; // if field has RVA associated with it, label for it goes here. + BinStr* m_pbsSig; + Class* m_pClass; + BinStr* m_pbsValue; + BinStr* m_pbsMarshal; + PInvokeDescriptor* m_pPInvoke; + CustomDescrList m_CustomDescrList; + DWORD m_dwAttr; + BOOL m_fNew; + // Security attributes + PermissionDecl* m_pPermissions; + PermissionSetDecl* m_pPermissionSets; + FieldDescriptor() { m_szName = NULL; m_pbsSig = NULL; m_fNew = TRUE; }; + ~FieldDescriptor() { if(m_szName) delete [] m_szName; if(m_pbsSig) delete m_pbsSig; }; +}; +typedef FIFO FieldDList; + +struct EventDescriptor +{ + mdTypeDef m_tdClass; + char* m_szName; + DWORD m_dwAttr; + mdToken m_tkEventType; + mdToken m_tkAddOn; + mdToken m_tkRemoveOn; + mdToken m_tkFire; + TokenList m_tklOthers; + mdEvent m_edEventTok; + BOOL m_fNew; + CustomDescrList m_CustomDescrList; + ~EventDescriptor() { m_tklOthers.RESET(false); }; +}; +typedef FIFO EventDList; + +struct PropDescriptor +{ + mdTypeDef m_tdClass; + char* m_szName; + DWORD m_dwAttr; + COR_SIGNATURE* m_pSig; + DWORD m_dwCSig; + DWORD m_dwCPlusTypeFlag; + PVOID m_pValue; + DWORD m_cbValue; + mdToken m_tkSet; + mdToken m_tkGet; + TokenList m_tklOthers; + mdProperty m_pdPropTok; + BOOL m_fNew; + CustomDescrList m_CustomDescrList; + ~PropDescriptor() { m_tklOthers.RESET(false); }; +}; +typedef FIFO PropDList; + +struct ImportDescriptor +{ + char* szDllName; +// char szDllName[MAX_FILENAME_LENGTH]; + DWORD dwDllName; + mdModuleRef mrDll; + ImportDescriptor(__in __nullterminated char* sz, DWORD l) + { + if((sz != NULL)&&(l > 0)) + { + szDllName = new char[l+1]; + if(szDllName != NULL) + { + memcpy(szDllName,sz,l); + szDllName[l] = 0; + dwDllName = l; + } + } + else + { + szDllName = NULL; + dwDllName = 0; + } + }; + ~ImportDescriptor() { delete [] szDllName; }; +}; +typedef FIFO ImportList; + + +/**************************************************************************/ +#include "class.hpp" +typedef LIFO ClassStack; +typedef FIFO ClassList; +//typedef SORTEDARRAY ClassHash; +typedef RBTREE ClassHash; +//typedef FIFO_INDEXED ClassHash; + +/**************************************************************************/ +/* Classes to hold lists of security permissions and permission sets. We build + these lists as we find security directives in the input stream and drain + them every time we see a class or method declaration (to which the + security info is attached). */ + +class PermissionDecl +{ +public: + PermissionDecl(CorDeclSecurity action, mdToken type, NVPair *pairs) + { + m_Action = action; + m_TypeSpec = type; + m_pbsBlob = NULL; + BuildConstructorBlob(action, pairs); + m_Next = NULL; + } + + PermissionDecl(CorDeclSecurity action, mdToken type, BinStr* pbsPairs) + { + m_Action = action; + m_TypeSpec = type; + + m_pbsBlob = new BinStr(); + m_pbsBlob->appendInt16(VAL16(1)); // prolog 0x01 0x00 + m_pbsBlob->appendInt32((int)action); // 4-byte action + if(pbsPairs) // name-value pairs if any + { + if(pbsPairs->length() > 2) + m_pbsBlob->appendFrom(pbsPairs,2); + delete pbsPairs; + } + if(m_pbsBlob->length() == 6) // no pairs added + m_pbsBlob->appendInt16(0); + m_Blob = m_pbsBlob->ptr(); + m_BlobLength = m_pbsBlob->length(); + m_Next = NULL; + } + + ~PermissionDecl() + { + if(m_pbsBlob) delete m_pbsBlob; + else delete [] m_Blob; + } + + CorDeclSecurity m_Action; + mdToken m_TypeSpec; + BYTE *m_Blob; + BinStr *m_pbsBlob; + long m_BlobLength; + PermissionDecl *m_Next; + +private: + void BuildConstructorBlob(CorDeclSecurity action, NVPair *pairs) + { + NVPair *p = pairs; + int count = 0; + int bytes = 8; + int length; + int i; + BYTE *pBlob; + + // Calculate number of name/value pairs and the memory required for the + // custom attribute blob. + while (p) { + BYTE *pVal = (BYTE*)p->Value()->ptr(); + count++; + bytes += 2; // One byte field/property specifier, one byte type code + + length = (int)strlen((const char *)p->Name()->ptr()); + bytes += CPackedLen::Size(length) + length; + + switch (pVal[0]) { + case SERIALIZATION_TYPE_BOOLEAN: + bytes += 1; + break; + case SERIALIZATION_TYPE_I4: + bytes += 4; + break; + case SERIALIZATION_TYPE_STRING: + length = (int)strlen((const char *)&pVal[1]); + bytes += CPackedLen::Size(length) + length; + break; + case SERIALIZATION_TYPE_ENUM: + length = (int)strlen((const char *)&pVal[1]); + bytes += CPackedLen::Size((ULONG)length) + length; + bytes += 4; + break; + } + p = p->Next(); + } + + m_Blob = new BYTE[bytes]; + if(m_Blob==NULL) + { + fprintf(stderr,"\nOut of memory!\n"); + return; + } + + m_Blob[0] = 0x01; // Version + m_Blob[1] = 0x00; + m_Blob[2] = (BYTE)action; // Constructor arg (security action code) + m_Blob[3] = 0x00; + m_Blob[4] = 0x00; + m_Blob[5] = 0x00; + m_Blob[6] = (BYTE)count; // Property/field count + m_Blob[7] = (BYTE)(count >> 8); + + for (i = 0, pBlob = &m_Blob[8], p = pairs; i < count; i++, p = p->Next()) { + BYTE *pVal = (BYTE*)p->Value()->ptr(); + char *szType; + + // Set field/property setter type. + *pBlob++ = SERIALIZATION_TYPE_PROPERTY; + + // Set type code. There's additional info for enums (the enum class + // name). + *pBlob++ = pVal[0]; + if (pVal[0] == SERIALIZATION_TYPE_ENUM) { + szType = (char *)&pVal[1]; + length = (int)strlen(szType); + pBlob = (BYTE*)CPackedLen::PutLength(pBlob, length); + strcpy_s((char *)pBlob, bytes, szType); + pBlob += length; + } + + // Record the field/property name. + length = (int)strlen((const char *)p->Name()->ptr()); + pBlob = (BYTE*)CPackedLen::PutLength(pBlob, length); + strcpy_s((char *)pBlob, bytes-(pBlob-m_Blob), (const char *)p->Name()->ptr()); + pBlob += length; + + // Record the serialized value. + switch (pVal[0]) { + case SERIALIZATION_TYPE_BOOLEAN: + *pBlob++ = pVal[1]; + break; + case SERIALIZATION_TYPE_I4: + *(__int32*)pBlob = *(__int32*)&pVal[1]; + pBlob += 4; + break; + case SERIALIZATION_TYPE_STRING: + length = (int)strlen((const char *)&pVal[1]); + pBlob = (BYTE*)CPackedLen::PutLength(pBlob, length); + strcpy_s((char *)pBlob, bytes-(pBlob-m_Blob), (const char *)&pVal[1]); + pBlob += length; + break; + case SERIALIZATION_TYPE_ENUM: + length = (int)strlen((const char *)&pVal[1]); + // We can have enums with base type of I1, I2 and I4. + switch (pVal[1 + length + 1]) { + case 1: + *(__int8*)pBlob = *(__int8*)&pVal[1 + length + 2]; + pBlob += 1; + break; + case 2: + *(__int16*)pBlob = *(__int16*)&pVal[1 + length + 2]; + pBlob += 2; + break; + case 4: + *(__int32*)pBlob = *(__int32*)&pVal[1 + length + 2]; + pBlob += 4; + break; + default: + _ASSERTE(!"Invalid enum size"); + } + break; + } + + } + + _ASSERTE((pBlob - m_Blob) == bytes); + + m_BlobLength = (long)bytes; + } +}; + +class PermissionSetDecl +{ +public: + PermissionSetDecl(CorDeclSecurity action, BinStr *value) + { + m_Action = action; + m_Value = value; + m_Next = NULL; + } + + ~PermissionSetDecl() + { + delete m_Value; + } + + CorDeclSecurity m_Action; + BinStr *m_Value; + PermissionSetDecl *m_Next; +}; + +struct VTFEntry +{ + char* m_szLabel; + WORD m_wCount; + WORD m_wType; + VTFEntry(WORD wCount, WORD wType, __in __nullterminated char* szLabel) { m_wCount = wCount; m_wType = wType; m_szLabel = szLabel; } + ~VTFEntry() { delete m_szLabel; } +}; +typedef FIFO VTFList; + +struct EATEntry +{ + DWORD dwStubRVA; + DWORD dwOrdinal; + char* szAlias; +}; +typedef FIFO EATList; + +struct DocWriter +{ + char* Name; + ISymUnmanagedDocumentWriter* pWriter; + DocWriter() { Name=NULL; pWriter=NULL; }; + ~DocWriter() { delete [] Name; if(pWriter) pWriter->Release();}; +}; +typedef FIFO DocWriterList; +/**************************************************************************/ +/* The assembler object does all the code generation (dealing with meta-data) + writing a PE file etc etc. But does NOT deal with syntax (that is what + AsmParse is for). Thus the API below is how AsmParse 'controls' the + Assember. Note that the Assembler object does know about the + AsmParse object (that is Assember is more fundamental than AsmParse) */ +struct Instr +{ + int opcode; + unsigned linenum; + unsigned column; + unsigned linenum_end; + unsigned column_end; + unsigned pc; + ISymUnmanagedDocumentWriter* pWriter; +}; +#define INSTR_POOL_SIZE 16 + +// For code folding: +struct MethodBody +{ + BinStr* pbsBody; + unsigned RVA; + BYTE* pCode; +}; +typedef FIFO MethodBodyList; + +struct Clockwork +{ + DWORD cBegin; + DWORD cEnd; + DWORD cParsBegin; + DWORD cParsEnd; + DWORD cMDInitBegin; + DWORD cMDInitEnd; + DWORD cMDEmitBegin; + DWORD cMDEmitEnd; + DWORD cMDEmit1; + DWORD cMDEmit2; + DWORD cMDEmit3; + DWORD cMDEmit4; + DWORD cRef2DefBegin; + DWORD cRef2DefEnd; + DWORD cFilegenBegin; + DWORD cFilegenEnd; +}; + +struct TypeDefDescr +{ + char* m_szName; + union + { + BinStr* m_pbsTypeSpec; + CustomDescr* m_pCA; + }; + mdToken m_tkTypeSpec; + TypeDefDescr(__in_opt __nullterminated char *pszName, BinStr* pbsTypeSpec, mdToken tkTypeSpec) + { + m_szName = pszName; + m_pbsTypeSpec = pbsTypeSpec; + m_tkTypeSpec = tkTypeSpec; + }; + ~TypeDefDescr() { delete [] m_szName; delete m_pbsTypeSpec; }; + int ComparedTo(TypeDefDescr* T) { return strcmp(m_szName,T->m_szName); }; + //int Compare(char* T) { return strcmp(T,m_szName); }; +}; +typedef SORTEDARRAY TypeDefDList; + +struct Indx +{ + void* table[128]; + Indx() { memset(table,0,sizeof(table)); }; + ~Indx() + { + for(int i = 1; i < 128; i++) delete ((Indx*)(table[i])); + }; + void IndexString(__in_z __in char* psz, void* pkywd) + { + int i = (int) *psz; + if(i == 0) + table[0] = pkywd; + else + { + _ASSERTE((i > 0)&&(i <= 127)); + Indx* pInd = (Indx*)(table[i]); + if(pInd == NULL) + { + pInd = new Indx; + _ASSERTE(pInd); + table[i] = pInd; + } + pInd->IndexString(psz+1,pkywd); + } + } + void* FindString(__in __nullterminated char* psz) + { + if(*psz > 0) + { + unsigned char uch = (unsigned char) *psz; + if(table[uch] != NULL) + return ((Indx*)(table[uch]))->FindString(psz+1); + } + else if(*psz == 0) return table[0]; + return NULL; + } +}; + +class Assembler { +public: + Assembler(); + ~Assembler(); + //-------------------------------------------------------- + GlobalLabelList m_lstGlobalLabel; + GlobalFixupList m_lstGlobalFixup; + + LabelList m_lstLabel; + + Class * m_pModuleClass; + ClassList m_lstClass; + ClassHash m_hshClass; + + Indx indxKeywords; + + BYTE * m_pOutputBuffer; + BYTE * m_pCurOutputPos; + BYTE * m_pEndOutputPos; + + + DWORD m_CurPC; + BOOL m_fStdMapping; + BOOL m_fDisplayTraceOutput; + BOOL m_fInitialisedMetaData; + BOOL m_fAutoInheritFromObject; + BOOL m_fReportProgress; + BOOL m_fIsMscorlib; + BOOL m_fTolerateDupMethods; + BOOL m_fENCMode; + BOOL m_fOptimize; + mdToken m_tkSysObject; + mdToken m_tkSysString; + mdToken m_tkSysValue; + mdToken m_tkSysEnum; + BOOL m_fDidCoInitialise; + + IMetaDataDispenserEx *m_pDisp; + IMetaDataEmit2 *m_pEmitter; + ICeeFileGen *m_pCeeFileGen; + IMetaDataImport2 *m_pImporter; // Import interface. + HCEEFILE m_pCeeFile; + HCEESECTION m_pGlobalDataSection; + HCEESECTION m_pILSection; + HCEESECTION m_pTLSSection; + HCEESECTION m_pCurSection; // The section EmitData* things go to + + AsmMan* m_pManifest; + + char m_szScopeName[MAX_SCOPE_LENGTH]; + char *m_szNamespace; //[MAX_NAMESPACE_LENGTH]; + char *m_szFullNS; //[MAX_NAMESPACE_LENGTH]; + unsigned m_ulFullNSLen; + + WCHAR *m_wzMetadataVersion; + + StringStack m_NSstack; + mdTypeSpec m_crExtends; + + // char m_szExtendsClause[MAX_CLASSNAME_LENGTH]; + + // The (resizable) array of "implements" types + mdToken *m_crImplList; + int m_nImplList; + int m_nImplListSize; + + TyParList *m_TyParList; + + Method *m_pCurMethod; + Class *m_pCurClass; + ClassStack m_ClassStack; // for nested classes + Class *dummyClass; // for FindCreateClass + + // moved to Class + //MethodList m_MethodList; + + BOOL m_fCPlusPlus; + BOOL m_fWindowsCE; + BOOL m_fDLL; + BOOL m_fOBJ; + BOOL m_fEntryPointPresent; + BOOL m_fHaveFieldsWithRvas; + BOOL m_fFoldCode; + DWORD m_dwMethodsFolded; + + state_t m_State; + + BinStr* m_pbsMD; + + Instr m_Instr[INSTR_POOL_SIZE]; // 16 + inline Instr* GetInstr() + { + int i; + for(i=0; (ierror("Instruction pool exhausted: source contains invalid instructions\n"); + return NULL; + } + // Labels, fixups and IL fixups are defined in Method.hpp,.cpp + void AddLabel(DWORD CurPC, __in __nullterminated char *pszName); + void AddDeferredFixup(__in __nullterminated char *pszLabel, BYTE *pBytes, DWORD RelativeToPC, BYTE FixupSize); + void AddDeferredILFixup(ILFixupType Kind); + void AddDeferredILFixup(ILFixupType Kind, GlobalFixup *GFixup); + void DoDeferredILFixups(Method* pMethod); + BOOL DoFixups(Method* pMethod); + //-------------------------------------------------------------------------------- + void ClearImplList(void); + void AddToImplList(mdToken); + void ClearBoundList(void); + //-------------------------------------------------------------------------------- + BOOL Init(); + void ProcessLabel(__in_z __in char *pszName); + GlobalLabel *FindGlobalLabel(LPCUTF8 pszName); + GlobalFixup *AddDeferredGlobalFixup(__in __nullterminated char *pszLabel, BYTE* reference); + //void AddDeferredDescrFixup(__in __nullterminated char *pszLabel); + BOOL DoGlobalFixups(); + BOOL DoDescrFixups(); + OPCODE DecodeOpcode(const BYTE *pCode, DWORD *pdwLen); + BOOL AddMethod(Method *pMethod); + void SetTLSSection() { m_pCurSection = m_pTLSSection; } + void SetILSection() { m_pCurSection = m_pILSection; } + void SetDataSection() { m_pCurSection = m_pGlobalDataSection; } + BOOL EmitMethod(Method *pMethod); + BOOL EmitMethodBody(Method* pMethod, BinStr* pbsOut); + BOOL EmitClass(Class *pClass); + HRESULT CreatePEFile(__in __nullterminated WCHAR *pwzOutputFilename); + HRESULT CreateTLSDirectory(); + HRESULT CreateDebugDirectory(); + HRESULT InitMetaData(); + Class *FindCreateClass(__in __nullterminated char *pszFQN); + BOOL EmitFieldRef(__in_z __in char *pszArg, int opcode); + BOOL EmitSwitchData(__in_z __in char *pszArg); + mdToken ResolveClassRef(mdToken tkResScope, __in __nullterminated char *pszClassName, Class** ppClass); + mdToken ResolveTypeSpec(BinStr* typeSpec); + mdToken GetBaseAsmRef(); + mdToken GetAsmRef(__in __nullterminated char* szName); + mdToken GetModRef(__in __nullterminated char* szName); + mdToken GetInterfaceImpl(mdToken tsClass, mdToken tsInterface); + char* ReflectionNotation(mdToken tk); + HRESULT ConvLocalSig(__in char* localsSig, CQuickBytes* corSig, DWORD* corSigLen, BYTE*& localTypes); + DWORD GetCurrentILSectionOffset(); + BOOL EmitCALLISig(__in char *p); + void AddException(DWORD pcStart, DWORD pcEnd, DWORD pcHandler, DWORD pcHandlerTo, mdTypeRef crException, BOOL isFilter, BOOL isFault, BOOL isFinally); + state_t CheckLocalTypeConsistancy(int instr, unsigned arg); + state_t AddGlobalLabel(__in __nullterminated char *pszName, HCEESECTION section); + void SetDLL(BOOL); + void SetOBJ(BOOL); + void ResetForNextMethod(); + void ResetLineNumbers(); + void SetStdMapping(BOOL val = TRUE) { m_fStdMapping = val; }; + + //-------------------------------------------------------------------------------- + BOOL isShort(unsigned instr) { return ((OpcodeInfo[instr].Type & 16) != 0); }; + unsigned ShortOf(unsigned opcode); + void SetErrorReporter(ErrorReporter* aReport) { report = aReport; if(m_pManifest) m_pManifest->SetErrorReporter(aReport); } + + void StartNameSpace(__in __nullterminated char* name); + void EndNameSpace(); + void StartClass(__in __nullterminated char* name, DWORD attr, TyParList *typars); + DWORD CheckClassFlagsIfNested(Class* pEncloser, DWORD attr); + void AddClass(); + void EndClass(); + void StartMethod(__in __nullterminated char* name, BinStr* sig, CorMethodAttr flags, BinStr* retMarshal, DWORD retAttr, TyParList *typars = NULL); + void EndMethod(); + + void AddField(__inout_z __inout char* name, BinStr* sig, CorFieldAttr flags, __in __nullterminated char* rvaLabel, BinStr* pVal, ULONG ulOffset); + BOOL EmitField(FieldDescriptor* pFD); + void EmitByte(int val); + //void EmitTry(enum CorExceptionFlag kind, char* beginLabel, char* endLabel, char* handleLabel, char* filterOrClass); + void EmitMaxStack(unsigned val); + void EmitLocals(BinStr* sig); + void EmitEntryPoint(); + void EmitZeroInit(); + void SetImplAttr(unsigned short attrval); + + // Emits zeros if the buffer parameter is NULL. + void EmitData(__in_opt void *buffer, unsigned len); + + void EmitDD(__in __nullterminated char *str); + void EmitDataString(BinStr* str); + + void EmitInstrVar(Instr* instr, int var); + void EmitInstrVarByName(Instr* instr, __in __nullterminated char* label); + void EmitInstrI(Instr* instr, int val); + void EmitInstrI8(Instr* instr, __int64* val); + void EmitInstrR(Instr* instr, double* val); + void EmitInstrBrOffset(Instr* instr, int offset); + void EmitInstrBrTarget(Instr* instr, __in __nullterminated char* label); + mdToken MakeMemberRef(mdToken typeSpec, __in __nullterminated char* name, BinStr* sig); + mdToken MakeMethodSpec(mdToken tkParent, BinStr* sig); + void SetMemberRefFixup(mdToken tk, unsigned opcode_len); + mdToken MakeTypeRef(mdToken tkResScope, LPCUTF8 szFullName); + void EmitInstrStringLiteral(Instr* instr, BinStr* literal, BOOL ConvertToUnicode, BOOL Swap = FALSE); + void EmitInstrSig(Instr* instr, BinStr* sig); + void EmitInstrSwitch(Instr* instr, Labels* targets); + void EmitLabel(__in __nullterminated char* label); + void EmitDataLabel(__in __nullterminated char* label); + + unsigned OpcodeLen(Instr* instr); //returns opcode length + // Emit just the opcode (no parameters to the instruction stream. + void EmitOpcode(Instr* instr); + + // Emit primitive types to the instruction stream. + void EmitBytes(BYTE*, unsigned len); + + ErrorReporter* report; + + BOOL EmitFieldsMethods(Class* pClass); + BOOL EmitEventsProps(Class* pClass); + + // named args/vars paraphernalia: +public: + void addArgName(__in_opt __nullterminated char *szNewName, BinStr* pbSig, BinStr* pbMarsh, DWORD dwAttr) + { + if(pbSig && (*(pbSig->ptr()) == ELEMENT_TYPE_VOID)) + report->error("Illegal use of type 'void'\n"); + if(m_lastArgName) + { + m_lastArgName->pNext = new ARG_NAME_LIST(m_lastArgName->nNum+1,szNewName,pbSig,pbMarsh,dwAttr); + m_lastArgName = m_lastArgName->pNext; + } + else + { + m_lastArgName = new ARG_NAME_LIST(0,szNewName,pbSig,pbMarsh,dwAttr); + m_firstArgName = m_lastArgName; + } + }; + ARG_NAME_LIST *getArgNameList(void) + { ARG_NAME_LIST *pRet = m_firstArgName; m_firstArgName=NULL; m_lastArgName=NULL; return pRet;}; + // Added because recursive destructor of ARG_NAME_LIST may overflow the system stack + void delArgNameList(ARG_NAME_LIST *pFirst) + { + ARG_NAME_LIST *pArgList=pFirst, *pArgListNext; + for(; pArgList; pArgListNext=pArgList->pNext, + delete pArgList, + pArgList=pArgListNext); + }; + + ARG_NAME_LIST *findArg(ARG_NAME_LIST *pFirst, int num) + { + ARG_NAME_LIST *pAN; + for(pAN=pFirst; pAN; pAN = pAN->pNext) + { + if(pAN->nNum == num) return pAN; + } + return NULL; + }; + ARG_NAME_LIST *m_firstArgName; + ARG_NAME_LIST *m_lastArgName; + void ResetArgNameList(); + + // Structured exception handling paraphernalia: +public: + SEH_Descriptor *m_SEHD; // current descriptor ptr + void NewSEHDescriptor(void); //sets m_SEHD + void SetTryLabels(__in __nullterminated char * szFrom, __in __nullterminated char *szTo); + void SetFilterLabel(__in __nullterminated char *szFilter); + void SetCatchClass(mdToken catchClass); + void SetHandlerLabels(__in __nullterminated char *szHandlerFrom, __in __nullterminated char *szHandlerTo); + void EmitTry(void); //uses m_SEHD + +//private: + SEHD_Stack m_SEHDstack; + + // Events and Properties paraphernalia: +public: + void EndEvent(void); //emits event definition + void EndProp(void); //emits property definition + void ResetEvent(__inout_z __inout char * szName, mdToken typeSpec, DWORD dwAttr); + void ResetProp(__inout_z __inout char * szName, BinStr* bsType, DWORD dwAttr, BinStr* bsValue); + void SetEventMethod(int MethodCode, mdToken tk); + void SetPropMethod(int MethodCode, mdToken tk); + BOOL EmitEvent(EventDescriptor* pED); // impl. in ASSEM.CPP + BOOL EmitProp(PropDescriptor* pPD); // impl. in ASSEM.CPP + EventDescriptor* m_pCurEvent; + PropDescriptor* m_pCurProp; + +private: + MemberRefDList m_LocalMethodRefDList; + MemberRefDList m_LocalFieldRefDList; + LocalMemberRefFixupList m_LocalMemberRefFixupList; + MethodBodyList m_MethodBodyList; + MemberRefDList m_MethodSpecList; +public: + HRESULT ResolveLocalMemberRefs(); + HRESULT DoLocalMemberRefFixups(); + mdToken ResolveLocalMemberRef(mdToken tok); + + // PInvoke paraphernalia +public: + PInvokeDescriptor* m_pPInvoke; + ImportList m_ImportList; + void SetPinvoke(BinStr* DllName, int Ordinal, BinStr* Alias, int Attrs); + HRESULT EmitPinvokeMap(mdToken tk, PInvokeDescriptor* pDescr); + ImportDescriptor* EmitImport(BinStr* DllName); + void EmitImports(); + + // Debug metadata paraphernalia +public: + ISymUnmanagedWriter* m_pSymWriter; + ISymUnmanagedDocumentWriter* m_pSymDocument; + DocWriterList m_DocWriterList; + ULONG m_ulCurLine; // set by Parser + ULONG m_ulCurColumn; // set by Parser + ULONG m_ulLastDebugLine; + ULONG m_ulLastDebugColumn; + ULONG m_ulLastDebugLineEnd; + ULONG m_ulLastDebugColumnEnd; + DWORD m_dwIncludeDebugInfo; + BOOL m_fGeneratePDB; + char m_szSourceFileName[MAX_FILENAME_LENGTH*3+1]; + WCHAR m_wzOutputFileName[MAX_FILENAME_LENGTH]; + WCHAR m_wzSourceFileName[MAX_FILENAME_LENGTH]; + GUID m_guidLang; + GUID m_guidLangVendor; + GUID m_guidDoc; + + // Security paraphernalia +public: + void AddPermissionDecl(CorDeclSecurity action, mdToken type, NVPair *pairs) + { + PermissionDecl *decl = new PermissionDecl(action, type, pairs); + if(decl==NULL) + { + report->error("\nOut of memory!\n"); + return; + } + if (m_pCurMethod) { + decl->m_Next = m_pCurMethod->m_pPermissions; + m_pCurMethod->m_pPermissions = decl; + } else if (m_pCurClass) { + decl->m_Next = m_pCurClass->m_pPermissions; + m_pCurClass->m_pPermissions = decl; + } else if (m_pManifest && m_pManifest->m_pAssembly) { + decl->m_Next = m_pManifest->m_pAssembly->m_pPermissions; + m_pManifest->m_pAssembly->m_pPermissions = decl; + } else { + report->error("Cannot declare security permissions without the owner\n"); + delete decl; + } + }; + + void AddPermissionDecl(CorDeclSecurity action, mdToken type, BinStr *pbsPairs) + { + PermissionDecl *decl = new PermissionDecl(action, type, pbsPairs); + if(decl==NULL) + { + report->error("\nOut of memory!\n"); + return; + } + if (m_pCurMethod) { + decl->m_Next = m_pCurMethod->m_pPermissions; + m_pCurMethod->m_pPermissions = decl; + } else if (m_pCurClass) { + decl->m_Next = m_pCurClass->m_pPermissions; + m_pCurClass->m_pPermissions = decl; + } else if (m_pManifest && m_pManifest->m_pAssembly) { + decl->m_Next = m_pManifest->m_pAssembly->m_pPermissions; + m_pManifest->m_pAssembly->m_pPermissions = decl; + } else { + report->error("Cannot declare security permissions without the owner\n"); + delete decl; + } + }; + + void AddPermissionSetDecl(CorDeclSecurity action, BinStr *value) + { + PermissionSetDecl *decl = new PermissionSetDecl(action, value); + if(decl==NULL) + { + report->error("\nOut of memory!\n"); + return; + } + if (m_pCurMethod) { + decl->m_Next = m_pCurMethod->m_pPermissionSets; + m_pCurMethod->m_pPermissionSets = decl; + } else if (m_pCurClass) { + decl->m_Next = m_pCurClass->m_pPermissionSets; + m_pCurClass->m_pPermissionSets = decl; + } else if (m_pManifest && m_pManifest->m_pAssembly) { + decl->m_Next = m_pManifest->m_pAssembly->m_pPermissionSets; + m_pManifest->m_pAssembly->m_pPermissionSets = decl; + } else { + report->error("Cannot declare security permission sets without the owner\n"); + delete decl; + } + }; + void EmitSecurityInfo(mdToken token, + PermissionDecl* pPermissions, + PermissionSetDecl*pPermissionSets); + + HRESULT AllocateStrongNameSignature(); + HRESULT StrongNameSign(); + BinStr* EncodeSecAttr(__in __nullterminated char* szReflName, BinStr* pbsSecAttrBlob, unsigned nProps); + + // Custom values paraphernalia: +public: + mdToken m_tkCurrentCVOwner; + CustomDescrList* m_pCustomDescrList; + CustomDescrListStack m_CustomDescrListStack; + CustomDescrList m_CustomDescrList; + + void DefineCV(CustomDescr* pCD) + { + if(pCD) + { + ULONG cTemp = 0; + void * pBlobBody = NULL; + mdToken cv; + mdToken tkOwnerType, tkTypeType = TypeFromToken(pCD->tkType); + + if((tkTypeType != 0x99000000)&&(tkTypeType != 0x98000000)) + { + tkOwnerType = TypeFromToken(pCD->tkOwner); + if((tkOwnerType != 0x99000000)&&(tkOwnerType != 0x98000000)) + { + if(pCD->pBlob) + { + pBlobBody = (void *)(pCD->pBlob->ptr()); + cTemp = pCD->pBlob->length(); + } + if (pCD->tkInterfacePair) + { + pCD->tkOwner = GetInterfaceImpl(pCD->tkOwner, pCD->tkInterfacePair); + } + m_pEmitter->DefineCustomAttribute(pCD->tkOwner,pCD->tkType,pBlobBody,cTemp,&cv); + + delete pCD; + return; + } + } + m_CustomDescrList.PUSH(pCD); + } + }; + void EmitCustomAttributes(mdToken tok, CustomDescrList* pCDL) + { + CustomDescr *pCD; + if(pCDL == NULL || RidFromToken(tok)==0) return; + while((pCD = pCDL->POP())) + { + pCD->tkOwner = tok; + DefineCV(pCD); + } + }; + + void EmitUnresolvedCustomAttributes(); // implementation: writer.cpp + // VTable blob (if any) +public: + BinStr *m_pVTable; + // Field marshaling + BinStr *m_pMarshal; + // VTable fixup list + VTFList m_VTFList; + // Export Address Table entries list + EATList m_EATList; + HRESULT CreateExportDirectory(); + DWORD EmitExportStub(DWORD dwVTFSlotRVA); + + // Method implementation paraphernalia: +private: + MethodImplDList m_MethodImplDList; +public: + void AddMethodImpl(mdToken tkImplementedTypeSpec, __in __nullterminated char* szImplementedName, BinStr* pImplementedSig, + mdToken tkImplementingTypeSpec, __in_opt __nullterminated char* szImplementingName, BinStr* pImplementingSig); + BOOL EmitMethodImpls(); + // lexical scope handling paraphernalia: + void EmitScope(Scope* pSCroot); // struct Scope - see Method.hpp + // source file name paraphernalia + BOOL m_fSourceFileSet; + void SetSourceFileName(__in __nullterminated char* szName); + void SetSourceFileName(BinStr* pbsName); + // header flags + DWORD m_dwSubsystem; + WORD m_wSSVersionMajor; + WORD m_wSSVersionMinor; + DWORD m_dwComImageFlags; + DWORD m_dwFileAlignment; + ULONGLONG m_stBaseAddress; + size_t m_stSizeOfStackReserve; + DWORD m_dwCeeFileFlags; + WORD m_wMSVmajor; + WORD m_wMSVminor; + BOOL m_fAppContainer; + BOOL m_fHighEntropyVA; + + // Former globals + WCHAR *m_wzResourceFile; + WCHAR *m_wzKeySourceName; + bool OnErrGo; + void SetCodePage(unsigned val) { g_uCodePage = val; }; + Clockwork* bClock; + void SetClock(Clockwork* val) { bClock = val; }; + // ENC paraphernalia + HRESULT InitMetaDataForENC(__in __nullterminated WCHAR* wzOrigFileName); + BOOL EmitFieldsMethodsENC(Class* pClass); + BOOL EmitEventsPropsENC(Class* pClass); + HRESULT CreateDeltaFiles(__in __nullterminated WCHAR *pwzOutputFilename); + + // Syntactic sugar paraphernalia +private: + TypeDefDList m_TypeDefDList; +public: + void AddTypeDef(BinStr* pbsTypeSpec, __in_z __in char* szName) + { + m_TypeDefDList.PUSH(new TypeDefDescr(szName, pbsTypeSpec, ResolveTypeSpec(pbsTypeSpec))); + }; + void AddTypeDef(mdToken tkTypeSpec, __in_z __in char* szName) + { + m_TypeDefDList.PUSH(new TypeDefDescr(szName, NULL, tkTypeSpec)); + }; + void AddTypeDef(CustomDescr* pCA, __in_z __in char* szName) + { + TypeDefDescr* pNew = new TypeDefDescr(szName,NULL,mdtCustomAttribute); + pNew->m_pCA = pCA; + m_TypeDefDList.PUSH(pNew); + }; + TypeDefDescr* FindTypeDef(__in_z __in char* szName) + { + CHECK_LOCAL_STATIC_VAR(static TypeDefDescr X(NULL, NULL, 0)); + + X.m_szName = szName; + TypeDefDescr* Y = m_TypeDefDList.FIND(&X); + X.m_szName = NULL; // to avoid deletion when X goes out of scope + return Y; + //return m_TypeDefDList.FIND(szName); + }; + unsigned NumTypeDefs() {return m_TypeDefDList.COUNT();}; +private: + HRESULT GetCAName(mdToken tkCA, __out LPWSTR *ppszName); + HRESULT GetSignatureKey(); +}; + +#endif // Assember_h + +#ifdef _MSC_VER +#pragma warning(default : 4640) +#endif + + diff --git a/src/ilasm/MscorpeSxS.cpp b/src/ilasm/MscorpeSxS.cpp new file mode 100644 index 0000000000..c10d772c6b --- /dev/null +++ b/src/ilasm/MscorpeSxS.cpp @@ -0,0 +1,24 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// +// File: MscorpeSxS.cpp +// + +// +// This file defines a wrapper for SxS version of mscorpe.dll (dynamically loaded via shim). +// +#include "ilasmpch.h" + +#include "MscorpeSxS.h" + +#include + +// Loads mscorpe.dll (uses shim) +HRESULT +LoadMscorpeDll(HMODULE * phModule) +{ + // Load SxS version of mscorpe.dll (i.e. mscorpehost.dll) and initialize it + return LegacyActivationShim::LoadLibraryShim(L"mscorpe.dll", NULL, NULL, phModule); +} diff --git a/src/ilasm/MscorpeSxS.h b/src/ilasm/MscorpeSxS.h new file mode 100644 index 0000000000..92b15b76da --- /dev/null +++ b/src/ilasm/MscorpeSxS.h @@ -0,0 +1,21 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// +// File: MscorpeSxS.h +// + +// +// This file defines a wrapper for SxS version of mscorpe.dll (dynamically loaded via shim). +// + +#pragma once + +#include + +// Loads mscorpe.dll (uses shim) +HRESULT LoadMscorpeDll(HMODULE * phModule); + +// Wrapper for mscorpe.dll calls +typedef MscorpeSxSWrapper MscorpeSxS; diff --git a/src/ilasm/Native.rc b/src/ilasm/Native.rc new file mode 100644 index 0000000000..41216be0ed --- /dev/null +++ b/src/ilasm/Native.rc @@ -0,0 +1,9 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +#define FX_VER_FILEDESCRIPTION_STR "Microsoft .NET Framework IL assembler\0" + +#include +#include diff --git a/src/ilasm/asmenum.h b/src/ilasm/asmenum.h new file mode 100644 index 0000000000..b8bd49966e --- /dev/null +++ b/src/ilasm/asmenum.h @@ -0,0 +1,42 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +#ifndef __asmenum_h__ +#define __asmenum_h__ + +#include "openum.h" + +typedef struct +{ + char * pszName; + OPCODE op; + + BYTE Type; // Inline0 etc. + + BYTE Len; // std mapping + BYTE Std1; + BYTE Std2; +} opcodeinfo_t; + +#ifdef DECLARE_DATA +opcodeinfo_t OpcodeInfo[] = +{ +#define OPALIAS(c,s,real) { s, real, 0, 0, 0, 0 }, +#define OPDEF(c,s,pop,push,args,type,l,s1,s2,ctrl) { s, c, args,l,s1,s2 }, +#include "opcode.def" +#undef OPDEF +#undef OPALIAS +}; + +unsigned OpcodeInfoLen = sizeof(OpcodeInfo) / sizeof(opcodeinfo_t); +#else +extern opcodeinfo_t OpcodeInfo[]; +extern unsigned OpcodeInfoLen; +#endif + + + +#endif /* __openum_h__ */ + + diff --git a/src/ilasm/asmman.cpp b/src/ilasm/asmman.cpp new file mode 100644 index 0000000000..c4aa0e51ed --- /dev/null +++ b/src/ilasm/asmman.cpp @@ -0,0 +1,1264 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// +// asmman.cpp - manifest info handling (implementation of class AsmMan, see asmman.hpp) +// + +// +#include "ilasmpch.h" + +#include "assembler.h" +#include "strongname.h" +#include "LegacyActivationShim.h" +#include +#include + +extern WCHAR* pwzInputFiles[]; + +BinStr* BinStrToUnicode(BinStr* pSource, bool Swap) +{ + if(pSource) + { + pSource->appendInt8(0); + BinStr* tmp = new BinStr(); + char* pb = (char*)(pSource->ptr()); + int l=pSource->length(), L = sizeof(WCHAR)*l; + if(tmp) + { + WCHAR* wz = (WCHAR*)(tmp->getBuff(L)); + if(wz) + { + memset(wz,0,L); + WszMultiByteToWideChar(g_uCodePage,0,pb,-1,wz,l); + tmp->remove(L-(DWORD)wcslen(wz)*sizeof(WCHAR)); +#if BIGENDIAN + if (Swap) + SwapStringLength(wz, (DWORD)wcslen(wz)); +#endif + delete pSource; + } + else + { + delete tmp; + tmp = NULL; + fprintf(stderr,"\nOut of memory!\n"); + } + } + else + fprintf(stderr,"\nOut of memory!\n"); + return tmp; + } + return NULL; +} + +AsmManFile* AsmMan::GetFileByName(__in __nullterminated char* szFileName) +{ + AsmManFile* ret = NULL; + if(szFileName) + { + //AsmManFile X; + //X.szName = szFileName; + //ret = m_FileLst.FIND(&X); + //X.szName = NULL; + for(int i=0; (ret = m_FileLst.PEEK(i))&&strcmp(ret->szName,szFileName); i++); + } + return ret; +} + +mdToken AsmMan::GetFileTokByName(__in __nullterminated char* szFileName) +{ + AsmManFile* tmp = GetFileByName(szFileName); + return(tmp ? tmp->tkTok : mdFileNil); +} + +AsmManComType* AsmMan::GetComTypeByName(__in_opt __nullterminated char* szComTypeName, + __in_opt __nullterminated char* szComEnclosingTypeName) +{ + AsmManComType* ret = NULL; + if(szComTypeName) + { + //AsmManComType X; + //X.szName = szComTypeName; + //ret = m_ComTypeLst.FIND(&X); + //X.szName = NULL; + for(int i=0; (ret = m_ComTypeLst.PEEK(i)) != NULL; i++) + { + if (strcmp(ret->szName, szComTypeName) == 0) + { + if (ret->szComTypeName == NULL && szComEnclosingTypeName == NULL) + { + break; + } + + if (ret->szComTypeName != NULL && szComEnclosingTypeName != NULL) + { + if (strcmp(ret->szComTypeName, szComEnclosingTypeName) == 0) + { + break; + } + } + } + } + } + return ret; +} + +mdToken AsmMan::GetComTypeTokByName( + __in_opt __nullterminated char* szComTypeName, + __in_opt __nullterminated char* szComEnclosingTypeName) +{ + AsmManComType* tmp = GetComTypeByName(szComTypeName, szComEnclosingTypeName); + return(tmp ? tmp->tkTok : mdExportedTypeNil); +} + +AsmManAssembly* AsmMan::GetAsmRefByName(__in __nullterminated char* szAsmRefName) +{ + AsmManAssembly* ret = NULL; + if(szAsmRefName) + { + //AsmManAssembly X; + //X.szAlias = szAsmRefName; + //ret = m_AsmRefLst.FIND(&X); + //X.szAlias = NULL; + DWORD L = (DWORD)strlen(szAsmRefName); + for(int i=0; (ret = m_AsmRefLst.PEEK(i))&& + ((ret->dwAlias != L)||strcmp(ret->szAlias,szAsmRefName)); i++); + } + return ret; +} +mdToken AsmMan::GetAsmRefTokByName(__in __nullterminated char* szAsmRefName) +{ + AsmManAssembly* tmp = GetAsmRefByName(szAsmRefName); + return(tmp ? tmp->tkTok : mdAssemblyRefNil); +} +//============================================================================================================== +void AsmMan::SetModuleName(__inout_opt __nullterminated char* szName) +{ + if(m_szScopeName == NULL) // ignore all duplicate declarations + { + if(szName && *szName) + { + ULONG L = (ULONG)strlen(szName); + if(L >= MAX_SCOPE_LENGTH) + { + ((Assembler*)m_pAssembler)->report->warn("Module name too long (%d chars, max.allowed: %d chars), truncated\n",L,MAX_SCOPE_LENGTH-1); + szName[MAX_SCOPE_LENGTH-1] = 0; + } + m_szScopeName = szName; + strcpy_s(((Assembler*)m_pAssembler)->m_szScopeName, MAX_SCOPE_LENGTH, szName); + } + } +} +//============================================================================================================== +// Borrowed from VM\assembly.cpp + +HRESULT GetHash(__in LPWSTR moduleName, + ALG_ID iHashAlg, + BYTE** pbCurrentValue, // should be NULL + DWORD *cbCurrentValue) +{ + HRESULT hr = E_FAIL; + HCRYPTPROV hProv = 0; + HCRYPTHASH hHash = 0; + DWORD dwCount = sizeof(DWORD); + PBYTE pbBuffer = NULL; + DWORD dwBufferLen; + HANDLE hFile = INVALID_HANDLE_VALUE; + HANDLE hMapFile = NULL; + + hFile = WszCreateFile(moduleName, GENERIC_READ, FILE_SHARE_READ, + 0, OPEN_EXISTING, 0, 0); + if (hFile == INVALID_HANDLE_VALUE) return E_FAIL; + + dwBufferLen = SafeGetFileSize(hFile,NULL); + if (dwBufferLen == 0xffffffff) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto exit; + } + hMapFile = WszCreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); + if (hMapFile == NULL) goto exit; + + pbBuffer = (PBYTE) MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, 0); + if (pbBuffer == NULL) goto exit; + + // No need to late bind this stuff, all these crypto API entry points happen + // to live in ADVAPI32. + + if ((!WszCryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) || + (!CryptCreateHash(hProv, iHashAlg, 0, 0, &hHash)) || + (!CryptHashData(hHash, pbBuffer, dwBufferLen, 0)) || + (!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE *) cbCurrentValue, + &dwCount, 0))) { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto exit; + } + + *pbCurrentValue = new BYTE[*cbCurrentValue]; + if (!(*pbCurrentValue)) { + hr = E_OUTOFMEMORY; + goto exit; + } + + if(!CryptGetHashParam(hHash, HP_HASHVAL, *pbCurrentValue, cbCurrentValue, 0)) { + hr = HRESULT_FROM_WIN32(GetLastError()); + delete[] *pbCurrentValue; + *pbCurrentValue = 0; + goto exit; + } + + hr = S_OK; + + exit: + if (pbBuffer) UnmapViewOfFile(pbBuffer); + if (hMapFile) CloseHandle(hMapFile); + CloseHandle(hFile); + if (hHash) + CryptDestroyHash(hHash); + if (hProv) + CryptReleaseContext(hProv, 0); + + return hr; +} +//============================================================================================================== + +void AsmMan::AddFile(__in __nullterminated char* szName, DWORD dwAttr, BinStr* pHashBlob) +{ + AsmManFile* tmp = GetFileByName(szName); + Assembler* pAsm = (Assembler*)m_pAssembler; + if(tmp==NULL) + { + tmp = new AsmManFile; + if(tmp==NULL) + { + pAsm->report->error("\nOut of memory!\n"); + return; + } + memset(tmp,0,sizeof(AsmManFile)); + if((dwAttr & 0x80000000)!=0) pAsm->m_fEntryPointPresent = TRUE; + tmp->szName = szName; + tmp->dwAttr = dwAttr; + tmp->pHash = pHashBlob; + tmp->m_fNew = TRUE; + m_FileLst.PUSH(tmp); + tmp->tkTok = TokenFromRid(m_FileLst.COUNT(),mdtFile); + } + pAsm->m_tkCurrentCVOwner = 0; + if(tmp) pAsm->m_pCustomDescrList = &(tmp->m_CustomDescrList); +} +//============================================================================================================== + +void AsmMan::EmitFiles() +{ + AsmManFile* tmp; + Assembler* pAsm = (Assembler*)m_pAssembler; + int i; + HRESULT hr = S_OK; + mdToken tk; + for(i = 0; (tmp=m_FileLst.PEEK(i)) != NULL; i++) + { + BOOL fEntry = ((tmp->dwAttr & 0x80000000)!=0); + + wzUniBuf[0] = 0; + + BYTE* pHash=NULL; + DWORD cbHash= 0; + + if(!tmp->m_fNew) continue; + tmp->m_fNew = FALSE; + + WszMultiByteToWideChar(g_uCodePage,0,tmp->szName,-1,wzUniBuf,dwUniBuf); + if(tmp->pHash==NULL) // if hash not explicitly specified + { + if(m_pAssembly // and assembly is defined + && m_pAssembly->ulHashAlgorithm) // and hash algorithm is defined... + { // then try to compute it + if(FAILED(GetHash(wzUniBuf,(ALG_ID)(m_pAssembly->ulHashAlgorithm),&pHash,&cbHash))) + { + pHash = NULL; + cbHash = 0; + } + else + { + tmp->pHash = new BinStr(pHash,cbHash); + } + } + } + else + { + pHash = tmp->pHash->ptr(); + cbHash = tmp->pHash->length(); + } + + hr = m_pAsmEmitter->DefineFile(wzUniBuf, + (const void*)pHash, + cbHash, + tmp->dwAttr & 0x7FFFFFFF, + (mdFile*)&tk); + _ASSERTE(tk == tmp->tkTok); + if(FAILED(hr)) report->error("Failed to define file '%s': 0x%08X\n",tmp->szName,hr); + else + { + if(fEntry) + { + if (FAILED(pAsm->m_pCeeFileGen->SetEntryPoint(pAsm->m_pCeeFile, tmp->tkTok))) + { + pAsm->report->error("Failed to set external entry point for file '%s'\n",tmp->szName); + } + } + pAsm->EmitCustomAttributes(tmp->tkTok, &(tmp->m_CustomDescrList)); + } + } //end for(i = 0; tmp=m_FileLst.PEEK(i); i++) +} + +void AsmMan::StartAssembly(__in __nullterminated char* szName, __in_opt __nullterminated char* szAlias, DWORD dwAttr, BOOL isRef) +{ + if(!isRef && (0==strcmp(szName,"mscorlib"))) ((Assembler*)m_pAssembler)->m_fIsMscorlib = TRUE; + if(!isRef && (m_pAssembly != NULL)) + { + if(strcmp(szName, m_pAssembly->szName)) + report->error("Multiple assembly declarations\n"); + // if name is the same, just ignore it + m_pCurAsmRef = NULL; + } + else + { + if((m_pCurAsmRef = new AsmManAssembly)) + { + memset(m_pCurAsmRef,0,sizeof(AsmManAssembly)); + m_pCurAsmRef->usVerMajor = (USHORT)0xFFFF; + m_pCurAsmRef->usVerMinor = (USHORT)0xFFFF; + m_pCurAsmRef->usBuild = (USHORT)0xFFFF; + m_pCurAsmRef->usRevision = (USHORT)0xFFFF; + m_pCurAsmRef->szName = szName; + m_pCurAsmRef->szAlias = szAlias ? szAlias : szName; + m_pCurAsmRef->dwAlias = (DWORD)strlen(m_pCurAsmRef->szAlias); + m_pCurAsmRef->dwAttr = dwAttr; + m_pCurAsmRef->isRef = isRef; + m_pCurAsmRef->isAutodetect = FALSE; + m_pCurAsmRef->m_fNew = TRUE; + if(!isRef) m_pAssembly = m_pCurAsmRef; + } + else + report->error("Failed to allocate AsmManAssembly structure\n"); + } + ((Assembler*)m_pAssembler)->m_tkCurrentCVOwner = 0; + ((Assembler*)m_pAssembler)->m_CustomDescrListStack.PUSH(((Assembler*)m_pAssembler)->m_pCustomDescrList); + ((Assembler*)m_pAssembler)->m_pCustomDescrList = m_pCurAsmRef ? &(m_pCurAsmRef->m_CustomDescrList) : NULL; + +} +// copied from asmparse.y +static void corEmitInt(BinStr* buff, unsigned data) +{ + unsigned cnt = CorSigCompressData(data, buff->getBuff(5)); + buff->remove(5 - cnt); +} + +void AsmMan::EmitDebuggableAttribute(mdToken tkOwner) +{ + mdToken tkCA; + Assembler* pAsm = (Assembler*)m_pAssembler; + mdToken tkTypeSpec, tkMscorlib, tkParamType; + BinStr *pbsSig = new BinStr(); + BinStr* bsBytes = new BinStr();; + char* szName; + tkMscorlib = pAsm->m_fIsMscorlib ? 1 : pAsm->GetBaseAsmRef(); + tkTypeSpec = pAsm->ResolveClassRef(tkMscorlib,"System.Diagnostics.DebuggableAttribute",NULL); + + EmitAssemblyRefs(); // just in case we gained 'mscorlib' AsmRef in GetAsmRef above + + BOOL fOldStyle = FALSE; + if(tkMscorlib == 1) + fOldStyle = (m_pAssembly->usVerMajor == 1); + else + { + AsmManAssembly *pAssembly = GetAsmRefByName("mscorlib"); + _ASSERTE(pAssembly != NULL); + PREFIX_ASSUME(pAssembly != NULL); + fOldStyle = (pAssembly->usVerMajor == 1); + } + + bsBytes->appendInt8(1); + bsBytes->appendInt8(0); + if(fOldStyle) + { + pbsSig->appendInt8(IMAGE_CEE_CS_CALLCONV_HASTHIS); + corEmitInt(pbsSig,2); + pbsSig->appendInt8(ELEMENT_TYPE_VOID); + pbsSig->appendInt8(ELEMENT_TYPE_BOOLEAN); + pbsSig->appendInt8(ELEMENT_TYPE_BOOLEAN); + + //New to old: 0x101->(true,true),0x03->(true,false),0x103->(true,true)+warning + bsBytes->appendInt8(1); + bsBytes->appendInt8((pAsm->m_dwIncludeDebugInfo==0x03 ? 0 : 1)); + if(pAsm->m_dwIncludeDebugInfo == 0x103) + { + report->warn("\nOption /DEBUG=IMPL is invalid for legacy DebuggableAttribute, /DEBUG used.\n" ); + } + } + else + { + BinStr bsSigArg; + char buffer[80]; + sprintf_s(buffer,80, + "%s%c%s", + "System.Diagnostics.DebuggableAttribute", + NESTING_SEP, + "DebuggingModes" + ); + + tkParamType = pAsm->ResolveClassRef(tkMscorlib,buffer, NULL); + + bsSigArg.appendInt8(ELEMENT_TYPE_VALUETYPE); + + unsigned cnt = CorSigCompressToken(tkParamType, bsSigArg.getBuff(5)); + bsSigArg.remove(5 - cnt); + + pbsSig->appendInt8(IMAGE_CEE_CS_CALLCONV_HASTHIS); + corEmitInt(pbsSig,1); + pbsSig->appendInt8(ELEMENT_TYPE_VOID); + pbsSig->append(&bsSigArg); + + bsBytes->appendInt32(pAsm->m_dwIncludeDebugInfo); + } + bsBytes->appendInt8(0); + bsBytes->appendInt8(0); + + szName = new char[16]; + strcpy_s(szName,16,".ctor"); + tkCA = pAsm->MakeMemberRef(tkTypeSpec,szName,pbsSig); + pAsm->DefineCV(new CustomDescr(tkOwner,tkCA,bsBytes)); +} + +void AsmMan::EndAssembly() +{ + if(m_pCurAsmRef) + { + if(m_pCurAsmRef->isRef) + { // list the assembly ref + if(GetAsmRefByName(m_pCurAsmRef->szAlias)) + { + //report->warn("Multiple declarations of Assembly Ref '%s', ignored except the 1st one\n",m_pCurAsmRef->szName); + delete m_pCurAsmRef; + m_pCurAsmRef = NULL; + return; + } + if(m_pCurAsmRef->isAutodetect) + { + IAssemblyName* pIAsmName; + HRESULT hr; + // Convert name to Unicode + WszMultiByteToWideChar(g_uCodePage,0,m_pCurAsmRef->szName,-1,wzUniBuf,dwUniBuf); + hr = CreateAssemblyNameObject(&pIAsmName,wzUniBuf,CANOF_PARSE_DISPLAY_NAME,NULL); + if(SUCCEEDED(hr)) + { + // set enumeration criteria: what is known about AsmRef (besides name) + if(m_pCurAsmRef->usVerMajor != (USHORT)0xFFFF) + pIAsmName->SetProperty(ASM_NAME_MAJOR_VERSION,&(m_pCurAsmRef->usVerMajor),2); + if(m_pCurAsmRef->usVerMinor != (USHORT)0xFFFF) + pIAsmName->SetProperty(ASM_NAME_MINOR_VERSION,&(m_pCurAsmRef->usVerMinor),2); + if(m_pCurAsmRef->usBuild != (USHORT)0xFFFF) + pIAsmName->SetProperty(ASM_NAME_BUILD_NUMBER,&(m_pCurAsmRef->usBuild),2); + if(m_pCurAsmRef->usRevision != (USHORT)0xFFFF) + pIAsmName->SetProperty(ASM_NAME_REVISION_NUMBER,&(m_pCurAsmRef->usRevision),2); + if(m_pCurAsmRef->pPublicKeyToken) + pIAsmName->SetProperty(ASM_NAME_PUBLIC_KEY_TOKEN, + m_pCurAsmRef->pPublicKeyToken->ptr(), + m_pCurAsmRef->pPublicKeyToken->length()); + if(m_pCurAsmRef->pLocale) + pIAsmName->SetProperty(ASM_NAME_CULTURE, + m_pCurAsmRef->pLocale->ptr(), + m_pCurAsmRef->pLocale->length()); + + // enumerate assemblies + IAssemblyEnum* pIAsmEnum = NULL; + hr = CreateAssemblyEnum(&pIAsmEnum, NULL, pIAsmName, ASM_CACHE_GAC, NULL); + if(SUCCEEDED(hr)) + { + IAssemblyName* pIAsmNameFound; + IAssemblyName* pIAsmNameLatestVer = NULL; + ULONGLONG ullVer=0, ullVerLatest=0; + DWORD dwVerHi, dwVerLo; + + // find the latest and greatest, if any + for(;;) + { + pIAsmNameFound = NULL; + hr = pIAsmEnum->GetNextAssembly(NULL,&pIAsmNameFound,0); + if(SUCCEEDED(hr) && pIAsmNameFound) + { + + pIAsmNameFound->GetVersion(&dwVerHi,&dwVerLo); + ullVer = (ULONGLONG)dwVerHi; + ullVer <<= sizeof(DWORD); + ullVer |= dwVerLo; + if(ullVer > ullVerLatest) + { + if(pIAsmNameLatestVer) + pIAsmNameLatestVer->Release(); + ullVerLatest = ullVer; + pIAsmNameLatestVer = pIAsmNameFound; + } + else + pIAsmNameFound->Release(); + } + else break; + } + // if found, fill the gaps + if(pIAsmNameLatestVer) + { + DWORD cbSize=0; + USHORT usDummy=0; + + if(m_pCurAsmRef->pPublicKeyToken == NULL) + { + cbSize = 1024; + pIAsmNameLatestVer->GetProperty(ASM_NAME_PUBLIC_KEY_TOKEN, + wzUniBuf, &cbSize); + if(cbSize) + { + if((m_pCurAsmRef->pPublicKeyToken = new BinStr())) + memcpy(m_pCurAsmRef->pPublicKeyToken->getBuff(cbSize), + wzUniBuf, cbSize); + } + } + + if(m_pCurAsmRef->usVerMajor == (USHORT)0xFFFF) + { + cbSize = (DWORD)sizeof(WORD); + pIAsmNameLatestVer->GetProperty(ASM_NAME_MAJOR_VERSION, + &usDummy, &cbSize); + m_pCurAsmRef->usVerMajor = usDummy; + } + if(m_pCurAsmRef->usVerMinor == (USHORT)0xFFFF) + { + cbSize = (DWORD)sizeof(WORD); + pIAsmNameLatestVer->GetProperty(ASM_NAME_MINOR_VERSION, + &usDummy, &cbSize); + m_pCurAsmRef->usVerMinor = usDummy; + } + if(m_pCurAsmRef->usBuild == (USHORT)0xFFFF) + { + cbSize = (DWORD)sizeof(WORD); + pIAsmNameLatestVer->GetProperty(ASM_NAME_BUILD_NUMBER, + &usDummy, &cbSize); + m_pCurAsmRef->usBuild = usDummy; + } + if(m_pCurAsmRef->usRevision == (USHORT)0xFFFF) + { + cbSize = (DWORD)sizeof(WORD); + pIAsmNameLatestVer->GetProperty(ASM_NAME_REVISION_NUMBER, + &usDummy, &cbSize); + m_pCurAsmRef->usRevision = usDummy; + } + + if(m_pCurAsmRef->pLocale == NULL) + { + cbSize = 1024; + pIAsmNameLatestVer->GetProperty(ASM_NAME_CULTURE, + wzUniBuf, &cbSize); + + if(cbSize > (DWORD)sizeof(WCHAR)) + { + if((m_pCurAsmRef->pLocale = new BinStr())) + memcpy(m_pCurAsmRef->pLocale->getBuff(cbSize), + wzUniBuf, cbSize); + } + } + pIAsmNameLatestVer->Release(); + } + else + report->warn("Failed to autodetect assembly '%s'\n",m_pCurAsmRef->szName); + // if no assembly found, leave it as is, it might be not a GAC assembly + + pIAsmEnum->Release(); + } + else + report->error("Failed to enum assemblies %S, hr=0x%08X\n",wzUniBuf,hr); + pIAsmName->Release(); + } + else + report->error("Failed to create assembly name object for %S, hr=0x%08X\n",wzUniBuf,hr); + } // end if isAutodetect + m_AsmRefLst.PUSH(m_pCurAsmRef); + m_pCurAsmRef->tkTok = TokenFromRid(m_AsmRefLst.COUNT(),mdtAssemblyRef); + } + else + { + HRESULT hr = S_OK; + m_pCurAsmRef->tkTok = TokenFromRid(1,mdtAssembly); + // Determine the strong name public key. This may have been set + // via a directive in the source or from the command line (which + // overrides the directive). From the command line we may have + // been provided with a file or the name of a CAPI key + // container. Either may contain a public key or a full key + // pair. + if (((Assembler*)m_pAssembler)->m_wzKeySourceName) + { + // Key file versus container is determined by the first + // character of the source ('@' for container). + if (*(((Assembler*)m_pAssembler)->m_wzKeySourceName) == L'@') + { + // Extract public key from container (works whether + // container has just a public key or an entire key + // pair). + m_sStrongName.m_wzKeyContainer = &((Assembler*)m_pAssembler)->m_wzKeySourceName[1]; + if (FAILED(hr = LegacyActivationShim::StrongNameGetPublicKey_HRESULT( + m_sStrongName.m_wzKeyContainer, + NULL, + 0, + &m_sStrongName.m_pbPublicKey, + &m_sStrongName.m_cbPublicKey))) + { + report->error("Failed to extract public key from '%S': 0x%08X\n",m_sStrongName.m_wzKeyContainer,hr); + m_pCurAsmRef = NULL; + return; + } + m_sStrongName.m_fFullSign = TRUE; + m_sStrongName.m_dwPublicKeyAllocated = AsmManStrongName::AllocatedBySNApi; + } + else + { + // Read public key or key pair from file. + HANDLE hFile = WszCreateFile(((Assembler*)m_pAssembler)->m_wzKeySourceName, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, + NULL); + if(hFile == INVALID_HANDLE_VALUE) + { + hr = GetLastError(); + report->error("Failed to open key file '%S': 0x%08X\n",((Assembler*)m_pAssembler)->m_wzKeySourceName,hr); + m_pCurAsmRef = NULL; + return; + } + + // Determine file size and allocate an appropriate buffer. + m_sStrongName.m_cbPublicKey = SafeGetFileSize(hFile, NULL); + if (m_sStrongName.m_cbPublicKey == 0xffffffff) { + report->error("File size too large\n"); + m_pCurAsmRef = NULL; + CloseHandle(hFile); + return; + } + + m_sStrongName.m_pbPublicKey = new BYTE[m_sStrongName.m_cbPublicKey]; + if (m_sStrongName.m_pbPublicKey == NULL) { + report->error("Failed to allocate key buffer\n"); + m_pCurAsmRef = NULL; + CloseHandle(hFile); + return; + } + m_sStrongName.m_dwPublicKeyAllocated = AsmManStrongName::AllocatedByNew; + + // Read the file into the buffer. + DWORD dwBytesRead; + if (!ReadFile(hFile, m_sStrongName.m_pbPublicKey, m_sStrongName.m_cbPublicKey, &dwBytesRead, NULL)) { + hr = GetLastError(); + report->error("Failed to read key file '%S': 0x%08X\n",((Assembler*)m_pAssembler)->m_wzKeySourceName,hr); + m_pCurAsmRef = NULL; + CloseHandle(hFile); + return; + } + + CloseHandle(hFile); + + // Guess whether we're full or delay signing based on + // whether the blob passed to us looks like a public + // key. (I.e. we may just have copied a full key pair + // into the public key buffer). + if (m_sStrongName.m_cbPublicKey >= sizeof(PublicKeyBlob) && + (offsetof(PublicKeyBlob, PublicKey) + + ((PublicKeyBlob*)m_sStrongName.m_pbPublicKey)->cbPublicKey) == m_sStrongName.m_cbPublicKey) + m_sStrongName.m_fFullSign = FALSE; + else + m_sStrongName.m_fFullSign = TRUE; + + // If we really have a key pair, we'll move it into a + // key container so the signing code gets the key pair + // from a consistent place. + if (m_sStrongName.m_fFullSign) + { + m_sStrongName.m_pbPrivateKey = m_sStrongName.m_pbPublicKey; + m_sStrongName.m_cbPrivateKey = m_sStrongName.m_cbPublicKey; + + m_sStrongName.m_pbPublicKey = NULL; + m_sStrongName.m_cbPublicKey = NULL; + m_sStrongName.m_dwPublicKeyAllocated = AsmManStrongName::NotAllocated; + + // Retrieve the public key portion as a byte blob. + if (FAILED(hr = LegacyActivationShim::StrongNameGetPublicKey_HRESULT( + NULL, + m_sStrongName.m_pbPrivateKey, + m_sStrongName.m_cbPrivateKey, + &m_sStrongName.m_pbPublicKey, + &m_sStrongName.m_cbPublicKey))) + { + report->error("Failed to extract public key: 0x%08X\n",hr); + m_pCurAsmRef = NULL; + return; + } + + m_sStrongName.m_dwPublicKeyAllocated = AsmManStrongName::AllocatedBySNApi; + } + } + } + else + { + if (m_pAssembly->pPublicKey) + { + m_sStrongName.m_pbPublicKey = m_pAssembly->pPublicKey->ptr(); + m_sStrongName.m_cbPublicKey = m_pAssembly->pPublicKey->length(); + } + else + { + m_sStrongName.m_pbPublicKey = NULL; + m_sStrongName.m_cbPublicKey = 0; + } + + m_sStrongName.m_wzKeyContainer = NULL; + m_sStrongName.m_fFullSign = FALSE; + m_sStrongName.m_dwPublicKeyAllocated = AsmManStrongName::NotAllocated; + } + } + m_pCurAsmRef = NULL; + } + ((Assembler*)m_pAssembler)->m_pCustomDescrList = ((Assembler*)m_pAssembler)->m_CustomDescrListStack.POP(); +} + +void FillAssemblyMetadata(AsmManAssembly *pAsm, ASSEMBLYMETADATA *pmd) +{ + pmd->usMajorVersion = pAsm->usVerMajor; + pmd->usMinorVersion = pAsm->usVerMinor; + pmd->usBuildNumber = pAsm->usBuild; + pmd->usRevisionNumber = pAsm->usRevision; + if(pmd->usMajorVersion == 0xFFFF) pmd->usMajorVersion = 0; + if(pmd->usMinorVersion == 0xFFFF) pmd->usMinorVersion = 0; + if(pmd->usBuildNumber == 0xFFFF) pmd->usBuildNumber = 0; + if(pmd->usRevisionNumber == 0xFFFF) pmd->usRevisionNumber = 0; + + if(pAsm->pLocale != NULL) + { + pmd->szLocale = (LPWSTR)(pAsm->pLocale->ptr()); + pmd->cbLocale = pAsm->pLocale->length()/((ULONG)sizeof(WCHAR)); + } + else + { + pmd->szLocale = NULL; + pmd->cbLocale = 0; + } + + pmd->rProcessor = NULL; + pmd->rOS = NULL; + pmd->ulProcessor = 0; + pmd->ulOS = 0; +} + +void AsmMan::EmitAssemblyRefs() +{ + int i; + HRESULT hr = S_OK; + ASSEMBLYMETADATA md; + mdToken tk; + + for(i=0; (m_pCurAsmRef=m_AsmRefLst.PEEK(i)) != NULL; i++) + { + if(!m_pCurAsmRef->m_fNew) continue; + m_pCurAsmRef->m_fNew = FALSE; + + wzUniBuf[0] = 0; + FillAssemblyMetadata(m_pCurAsmRef,&md); + + // See if we've got a full public key or the tokenized version (or neither). + BYTE *pbPublicKeyOrToken = NULL; + DWORD cbPublicKeyOrToken = 0; + DWORD dwFlags = m_pCurAsmRef->dwAttr; + if (m_pCurAsmRef->pPublicKeyToken) + { + pbPublicKeyOrToken = m_pCurAsmRef->pPublicKeyToken->ptr(); + cbPublicKeyOrToken = m_pCurAsmRef->pPublicKeyToken->length(); + + } + else if (m_pCurAsmRef->pPublicKey) + { + pbPublicKeyOrToken = m_pCurAsmRef->pPublicKey->ptr(); + cbPublicKeyOrToken = m_pCurAsmRef->pPublicKey->length(); + dwFlags |= afPublicKey; + } + // Convert name to Unicode + WszMultiByteToWideChar(g_uCodePage,0,m_pCurAsmRef->szName,-1,wzUniBuf,dwUniBuf); + hr = m_pAsmEmitter->DefineAssemblyRef( // S_OK or error. + pbPublicKeyOrToken, // [IN] Public key or token of the assembly. + cbPublicKeyOrToken, // [IN] Count of bytes in the key or token. + (LPCWSTR)wzUniBuf, // [IN] Name of the assembly being referenced. + (const ASSEMBLYMETADATA*)&md, // [IN] Assembly MetaData. + (m_pCurAsmRef->pHashBlob ? (const void*)(m_pCurAsmRef->pHashBlob->ptr()) : NULL), // [IN] Hash Blob. + (m_pCurAsmRef->pHashBlob ? m_pCurAsmRef->pHashBlob->length() : 0), // [IN] Count of bytes in the Hash Blob. + dwFlags, // [IN] Flags. + (mdAssemblyRef*)&tk); // [OUT] Returned AssemblyRef token. + if(m_pCurAsmRef->tkTok != tk) + { + report->error("AsmRef'%S' tok %8.8X -> %8.8X\n",wzUniBuf,m_pCurAsmRef->tkTok,tk); + } + if(FAILED(hr)) report->error("Failed to define assembly ref '%s': 0x%08X\n",m_pCurAsmRef->szName,hr); + else + { + ((Assembler*)m_pAssembler)->EmitCustomAttributes(m_pCurAsmRef->tkTok, &(m_pCurAsmRef->m_CustomDescrList)); + } + } // end for(i=0; m_pCurAsmRef=m_AsmRefLst.PEEK(i); i++) +} + +void AsmMan::EmitAssembly() +{ + HRESULT hr = S_OK; + ASSEMBLYMETADATA md; + + wzUniBuf[0] = 0; + if(m_pAssembly == NULL) return; + if(!m_pAssembly->m_fNew) return; + m_pAssembly->m_fNew = FALSE; + + FillAssemblyMetadata(m_pAssembly, &md); + + // Convert name to Unicode + WszMultiByteToWideChar(g_uCodePage,0,m_pAssembly->szName,-1,wzUniBuf,dwUniBuf); + + hr = m_pAsmEmitter->DefineAssembly( // S_OK or error. + (const void*)(m_sStrongName.m_pbPublicKey), // [IN] Public key of the assembly. + m_sStrongName.m_cbPublicKey, // [IN] Count of bytes in the public key. + m_pAssembly->ulHashAlgorithm, // [IN] Hash algorithm used to hash the files. + (LPCWSTR)wzUniBuf, // [IN] Name of the assembly. + (const ASSEMBLYMETADATA*)&md, // [IN] Assembly MetaData. + m_pAssembly->dwAttr, // [IN] Flags. + (mdAssembly*)&(m_pAssembly->tkTok)); // [OUT] Returned Assembly token. + + if(FAILED(hr)) report->error("Failed to define assembly '%s': 0x%08X\n",m_pAssembly->szName,hr); + else + { + Assembler* pAsm = ((Assembler*)m_pAssembler); + pAsm->EmitSecurityInfo(m_pAssembly->tkTok, + m_pAssembly->m_pPermissions, + m_pAssembly->m_pPermissionSets); + if(pAsm->m_dwIncludeDebugInfo) + { + EmitDebuggableAttribute(m_pAssembly->tkTok); + } + pAsm->EmitCustomAttributes(m_pAssembly->tkTok, &(m_pAssembly->m_CustomDescrList)); + } +} + +void AsmMan::SetAssemblyPublicKey(BinStr* pPublicKey) +{ + if(m_pCurAsmRef) + { + m_pCurAsmRef->pPublicKey = pPublicKey; + } +} + +void AsmMan::SetAssemblyPublicKeyToken(BinStr* pPublicKeyToken) +{ + if(m_pCurAsmRef) + { + m_pCurAsmRef->pPublicKeyToken = pPublicKeyToken; + } +} + +void AsmMan::SetAssemblyHashAlg(ULONG ulAlgID) +{ + if(m_pCurAsmRef) + { + m_pCurAsmRef->ulHashAlgorithm = ulAlgID; + } +} + +void AsmMan::SetAssemblyVer(USHORT usMajor, USHORT usMinor, USHORT usBuild, USHORT usRevision) +{ + if(m_pCurAsmRef) + { + m_pCurAsmRef->usVerMajor = usMajor; + m_pCurAsmRef->usVerMinor = usMinor; + m_pCurAsmRef->usBuild = usBuild; + m_pCurAsmRef->usRevision = usRevision; + } +} + +void AsmMan::SetAssemblyLocale(BinStr* pLocale, BOOL bConvertToUnicode) +{ + if(m_pCurAsmRef) + { + m_pCurAsmRef->pLocale = bConvertToUnicode ? ::BinStrToUnicode(pLocale) : pLocale; + } +} + +void AsmMan::SetAssemblyHashBlob(BinStr* pHashBlob) +{ + if(m_pCurAsmRef) + { + m_pCurAsmRef->pHashBlob = pHashBlob; + } +} + +void AsmMan::SetAssemblyAutodetect() +{ + if(m_pCurAsmRef) + { + m_pCurAsmRef->isAutodetect = TRUE; + } +} + +void AsmMan::StartComType(__in __nullterminated char* szName, DWORD dwAttr) +{ + if((m_pCurComType = new AsmManComType)) + { + memset(m_pCurComType,0,sizeof(AsmManComType)); + m_pCurComType->szName = szName; + m_pCurComType->dwAttr = dwAttr; + m_pCurComType->m_fNew = TRUE; + ((Assembler*)m_pAssembler)->m_tkCurrentCVOwner = 0; + ((Assembler*)m_pAssembler)->m_CustomDescrListStack.PUSH(((Assembler*)m_pAssembler)->m_pCustomDescrList); + ((Assembler*)m_pAssembler)->m_pCustomDescrList = &(m_pCurComType->m_CustomDescrList); + } + else + report->error("Failed to allocate AsmManComType structure\n"); +} + +void AsmMan::EndComType() +{ + if(m_pCurComType) + { + if(m_pAssembler) + { + Class* pClass =((Assembler*)m_pAssembler)->m_pCurClass; + if(pClass) + { + m_pCurComType->tkClass = pClass->m_cl; + if(pClass->m_pEncloser) + { + mdTypeDef tkEncloser = pClass->m_pEncloser->m_cl; + AsmManComType* pCT; + for(unsigned i=0; (pCT=m_ComTypeLst.PEEK(i)); i++) + { + if(pCT->tkClass == tkEncloser) + { + m_pCurComType->szComTypeName = pCT->szName; + break; + } + } + } + } + } + + if (IsTdNested(m_pCurComType->dwAttr) && GetComTypeByName(m_pCurComType->szName, m_pCurComType->szComTypeName) != NULL) + { + report->error("Invalid TypeDefID of exported type\n"); + delete m_pCurComType; + } + else + { + m_ComTypeLst.PUSH(m_pCurComType); + } + + m_pCurComType = NULL; + ((Assembler*)m_pAssembler)->m_tkCurrentCVOwner = 0; + ((Assembler*)m_pAssembler)->m_pCustomDescrList = ((Assembler*)m_pAssembler)->m_CustomDescrListStack.POP(); + } +} + +void AsmMan::SetComTypeFile(__in __nullterminated char* szFileName) +{ + if(m_pCurComType) + { + m_pCurComType->szFileName = szFileName; + } +} + +void AsmMan::SetComTypeAsmRef(__in __nullterminated char* szAsmRefName) +{ + if(m_pCurComType) + { + m_pCurComType->szAsmRefName = szAsmRefName; + } +} + +void AsmMan::SetComTypeComType(__in __nullterminated char* szComTypeName) +{ + if(m_pCurComType) + { + m_pCurComType->szComTypeName = szComTypeName; + } +} +BOOL AsmMan::SetComTypeImplementationTok(mdToken tk) +{ + if(m_pCurComType) + { + switch(TypeFromToken(tk)) + { + case mdtAssemblyRef: + case mdtExportedType: + case mdtFile: + m_pCurComType->tkImpl = tk; + return TRUE; + } + } + return FALSE; +} +BOOL AsmMan::SetComTypeClassTok(mdToken tkClass) +{ + if((m_pCurComType)&&(TypeFromToken(tkClass)==mdtTypeDef)) + { + m_pCurComType->tkClass = tkClass; + return TRUE; + } + return FALSE; +} + +void AsmMan::StartManifestRes(__in __nullterminated char* szName, __in __nullterminated char* szAlias, DWORD dwAttr) +{ + if((m_pCurManRes = new AsmManRes)) + { + m_pCurManRes->szName = szName; + m_pCurManRes->szAlias = szAlias; + m_pCurManRes->dwAttr = dwAttr; + m_pCurManRes->m_fNew = TRUE; + ((Assembler*)m_pAssembler)->m_tkCurrentCVOwner = 0; + ((Assembler*)m_pAssembler)->m_CustomDescrListStack.PUSH(((Assembler*)m_pAssembler)->m_pCustomDescrList); + ((Assembler*)m_pAssembler)->m_pCustomDescrList = &(m_pCurManRes->m_CustomDescrList); + } + else + report->error("Failed to allocate AsmManRes structure\n"); +} + +void AsmMan::EndManifestRes() +{ + if(m_pCurManRes) + { + m_ManResLst.PUSH(m_pCurManRes); + m_pCurManRes = NULL; + ((Assembler*)m_pAssembler)->m_tkCurrentCVOwner = 0; + ((Assembler*)m_pAssembler)->m_pCustomDescrList = ((Assembler*)m_pAssembler)->m_CustomDescrListStack.POP(); + } +} + + +void AsmMan::SetManifestResFile(__in __nullterminated char* szFileName, ULONG ulOffset) +{ + if(m_pCurManRes) + { + m_pCurManRes->szFileName = szFileName; + m_pCurManRes->ulOffset = ulOffset; + } +} + +void AsmMan::SetManifestResAsmRef(__in __nullterminated char* szAsmRefName) +{ + if(m_pCurManRes) + { + m_pCurManRes->szAsmRefName = szAsmRefName; + } +} + +HRESULT AsmMan::EmitManifest() +{ + //AsmManAssembly* pAsmRef; + AsmManComType* pComType; + AsmManRes* pManRes; + HRESULT hr = S_OK; + + wzUniBuf[0] = 0; + + if(m_pAsmEmitter==NULL) + hr=m_pEmitter->QueryInterface(IID_IMetaDataAssemblyEmit, (void**) &m_pAsmEmitter); + + if(SUCCEEDED(hr)) + { + EmitFiles(); + EmitAssembly(); + + if((((Assembler*)m_pAssembler)->m_dwIncludeDebugInfo != 0) && (m_pAssembly == NULL) + && !(((Assembler*)m_pAssembler)->m_fENCMode)) + { + mdToken tkOwner, tkMscorlib; + tkMscorlib = ((Assembler*)m_pAssembler)->GetAsmRef("mscorlib"); + tkOwner = ((Assembler*)m_pAssembler)->ResolveClassRef(tkMscorlib, + "System.Runtime.CompilerServices.AssemblyAttributesGoHere", + NULL); + EmitDebuggableAttribute(tkOwner); + } + + // Emit all com types + unsigned i; + for(i = 0; (pComType = m_ComTypeLst.PEEK(i)); i++) + { + if(!pComType->m_fNew) continue; + pComType->m_fNew = FALSE; + + WszMultiByteToWideChar(g_uCodePage,0,pComType->szName,-1,wzUniBuf,dwUniBuf); + mdToken tkImplementation = mdTokenNil; + if(pComType->tkImpl) tkImplementation = pComType->tkImpl; + else if(pComType->szFileName) + { + tkImplementation = GetFileTokByName(pComType->szFileName); + if(tkImplementation==mdFileNil) + { + report->error("Undefined File '%s' in ExportedType '%s'\n",pComType->szFileName,pComType->szName); + if(!((Assembler*)m_pAssembler)->OnErrGo) continue; + } + } + else if(pComType->szAsmRefName) + { + tkImplementation = GetAsmRefTokByName(pComType->szAsmRefName); + if(RidFromToken(tkImplementation)==0) + { + report->error("Undefined AssemblyRef '%s' in ExportedType '%s'\n",pComType->szAsmRefName,pComType->szName); + if(!((Assembler*)m_pAssembler)->OnErrGo) continue; + } + } + else if(pComType->szComTypeName) + { + char* szLastName = strrchr(pComType->szComTypeName, NESTING_SEP); + + if(szLastName) + { + *szLastName = 0; + szLastName ++; + tkImplementation = GetComTypeTokByName(szLastName, pComType->szComTypeName); + *(szLastName-1) = NESTING_SEP; // not really necessary + } + + else + { + tkImplementation = GetComTypeTokByName(pComType->szComTypeName); + } + + if(tkImplementation==mdExportedTypeNil) + { + report->error("Undefined ExportedType '%s' in ExportedType '%s'\n",pComType->szComTypeName,pComType->szName); + if(!((Assembler*)m_pAssembler)->OnErrGo) continue; + } + } + else + { + report->warn("Undefined implementation in ExportedType '%s' -- ExportType not emitted\n",pComType->szName); + if(!((Assembler*)m_pAssembler)->OnErrGo) continue; + } + hr = m_pAsmEmitter->DefineExportedType( // S_OK or error. + (LPCWSTR)wzUniBuf, // [IN] Name of the Com Type. + tkImplementation, // [IN] mdFile or mdAssemblyRef that provides the ComType. + (mdTypeDef)pComType->tkClass, // [IN] TypeDef token within the file. + pComType->dwAttr, // [IN] Flags. + (mdExportedType*)&(pComType->tkTok)); // [OUT] Returned ComType token. + if(FAILED(hr)) report->error("Failed to define ExportedType '%s': 0x%08X\n",pComType->szName,hr); + else + { + ((Assembler*)m_pAssembler)->EmitCustomAttributes(pComType->tkTok, &(pComType->m_CustomDescrList)); + } + } + + // Emit all manifest resources + m_dwMResSizeTotal = 0; + m_dwMResNum = 0; + for(i = 0; (pManRes = m_ManResLst.PEEK(i)); i++) + { + BOOL fOK = TRUE; + mdToken tkImplementation = mdFileNil; + + if(!pManRes->m_fNew) continue; + pManRes->m_fNew = FALSE; + + WszMultiByteToWideChar(g_uCodePage,0,pManRes->szAlias,-1,wzUniBuf,dwUniBuf); + if(pManRes->szAsmRefName) + { + tkImplementation = GetAsmRefTokByName(pManRes->szAsmRefName); + if(RidFromToken(tkImplementation)==0) + { + report->error("Undefined AssemblyRef '%s' in MResource '%s'\n",pManRes->szAsmRefName,pManRes->szName); + fOK = FALSE; + } + } + else if(pManRes->szFileName) + { + tkImplementation = GetFileTokByName(pManRes->szFileName); + if(RidFromToken(tkImplementation)==0) + { + report->error("Undefined File '%s' in MResource '%s'\n",pManRes->szFileName,pManRes->szName); + fOK = FALSE; + } + } + else // embedded mgd.resource, go after the file + { + HANDLE hFile = INVALID_HANDLE_VALUE; + int j; + WCHAR wzFileName[2048]; + WCHAR* pwz; + + pManRes->ulOffset = m_dwMResSizeTotal; + for(j=0; (hFile == INVALID_HANDLE_VALUE)&&(pwzInputFiles[j] != NULL); j++) + { + wcscpy_s(wzFileName,2048,pwzInputFiles[j]); + pwz = wcsrchr(wzFileName,'\\'); + if(pwz == NULL) pwz = wcsrchr(wzFileName,':'); + if(pwz == NULL) pwz = &wzFileName[0]; + else pwz++; + wcscpy_s(pwz,2048-(pwz-wzFileName),wzUniBuf); + hFile = WszCreateFile(wzFileName, GENERIC_READ, FILE_SHARE_READ, + 0, OPEN_EXISTING, 0, 0); + } + if (hFile == INVALID_HANDLE_VALUE) + { + report->error("Failed to open managed resource file '%s'\n",pManRes->szAlias); + fOK = FALSE; + } + else + { + if (m_dwMResNum >= MAX_MANIFEST_RESOURCES) + { + report->error("Too many resources (implementation limit: %d); skipping file '%s'\n",MAX_MANIFEST_RESOURCES,pManRes->szAlias); + fOK = FALSE; + } + else + { + m_dwMResSize[m_dwMResNum] = SafeGetFileSize(hFile,NULL); + if(m_dwMResSize[m_dwMResNum] == 0xFFFFFFFF) + { + report->error("Failed to get size of managed resource file '%s'\n",pManRes->szAlias); + fOK = FALSE; + } + else + { + m_dwMResSizeTotal += m_dwMResSize[m_dwMResNum]+sizeof(DWORD); + m_wzMResName[m_dwMResNum] = new WCHAR[wcslen(wzFileName)+1]; + wcscpy_s(m_wzMResName[m_dwMResNum],wcslen(wzFileName)+1,wzFileName); + m_fMResNew[m_dwMResNum] = TRUE; + m_dwMResNum++; + } + CloseHandle(hFile); + } + } + } + if(fOK || ((Assembler*)m_pAssembler)->OnErrGo) + { + WszMultiByteToWideChar(g_uCodePage,0,pManRes->szName,-1,wzUniBuf,dwUniBuf); + hr = m_pAsmEmitter->DefineManifestResource( // S_OK or error. + (LPCWSTR)wzUniBuf, // [IN] Name of the resource. + tkImplementation, // [IN] mdFile or mdAssemblyRef that provides the resource. + pManRes->ulOffset, // [IN] Offset to the beginning of the resource within the file. + pManRes->dwAttr, // [IN] Flags. + (mdManifestResource*)&(pManRes->tkTok)); // [OUT] Returned ManifestResource token. + if(FAILED(hr)) + report->error("Failed to define manifest resource '%s': 0x%08X\n",pManRes->szName,hr); + } + } + + + m_pAsmEmitter->Release(); + m_pAsmEmitter = NULL; + } + else + report->error("Failed to obtain IMetaDataAssemblyEmit interface: 0x%08X\n",hr); + return hr; +} + diff --git a/src/ilasm/asmman.hpp b/src/ilasm/asmman.hpp new file mode 100644 index 0000000000..3db7ae2b2d --- /dev/null +++ b/src/ilasm/asmman.hpp @@ -0,0 +1,301 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// +// asmman.hpp - header file for manifest-related ILASM functions +// + +#ifndef ASMMAN_HPP +#define ASMMAN_HPP + +#include "strongname.h" +#include "LegacyActivationShim.h" +#include "specstrings.h" + +struct AsmManFile +{ + char* szName; + mdToken tkTok; + DWORD dwAttr; + BinStr* pHash; + BOOL m_fNew; + CustomDescrList m_CustomDescrList; + AsmManFile() + { + szName = NULL; + pHash = NULL; + m_fNew = TRUE; + } + ~AsmManFile() + { + if(szName) delete szName; + if(pHash) delete pHash; + } + int ComparedTo(AsmManFile* pX){ return strcmp(szName,pX->szName); } +}; +//typedef SORTEDARRAY AsmManFileList; +typedef FIFO AsmManFileList; + +struct AsmManAssembly +{ + BOOL isRef; + BOOL isAutodetect; + char* szName; + char* szAlias; + DWORD dwAlias; + mdToken tkTok; + DWORD dwAttr; + BinStr* pPublicKey; + BinStr* pPublicKeyToken; + ULONG ulHashAlgorithm; + BinStr* pHashBlob; + BinStr* pLocale; + BOOL m_fNew; + // Security attributes + PermissionDecl* m_pPermissions; + PermissionSetDecl* m_pPermissionSets; + CustomDescrList m_CustomDescrList; + USHORT usVerMajor; + USHORT usVerMinor; + USHORT usBuild; + USHORT usRevision; + AsmManAssembly() + { + /* + usVerMajor = usVerMinor = usBuild = usRevision = 0xFFFF; + szName = szAlias = NULL; + dwAlias = dwAttr = 0; + tkTok = 0; + pPublicKey = pPublicKeyToken =pHashBlob = pLocale = NULL; + ulHashAlgorithm = 0; + m_fNew = TRUE; + isAutodetect = isRef = FALSE; + */ + } + ~AsmManAssembly() + { + if(szAlias && (szAlias != szName)) delete [] szAlias; + if(szName) delete [] szName; + if(pPublicKey) delete pPublicKey; + if(pPublicKeyToken) delete pPublicKeyToken; + if(pHashBlob) delete pHashBlob; + if(pLocale) delete pLocale; + } + int ComparedTo(AsmManAssembly* pX){ return strcmp(szAlias,pX->szAlias); } +}; +//typedef SORTEDARRAY AsmManAssemblyList; +typedef FIFO AsmManAssemblyList; + +struct AsmManComType +{ + char* szName; + mdToken tkTok; + mdToken tkImpl; + DWORD dwAttr; + char* szFileName; + char* szAsmRefName; + char* szComTypeName; + mdToken tkClass; + BOOL m_fNew; + CustomDescrList m_CustomDescrList; + AsmManComType() + { + szName = szFileName = szAsmRefName = szComTypeName = NULL; + m_fNew = TRUE; + tkImpl = 0; + }; + ~AsmManComType() + { + if(szName) delete szName; + if(szFileName) delete szFileName; + }; + int ComparedTo(AsmManComType* pX){ return strcmp(szName,pX->szName); }; +}; +//typedef SORTEDARRAY AsmManComTypeList; +typedef FIFO AsmManComTypeList; + + +struct AsmManRes +{ + char* szName; + char* szAlias; + mdToken tkTok; + DWORD dwAttr; + char* szFileName; + ULONG ulOffset; + BOOL m_fNew; + CustomDescrList m_CustomDescrList; + char* szAsmRefName; + AsmManRes() { szName = szAlias = szAsmRefName = szFileName = NULL; ulOffset = 0; tkTok = 0; dwAttr = 0; m_fNew = TRUE; }; + ~AsmManRes() + { + if(szAlias && (szAlias != szName)) delete szAlias; + if(szName) delete szName; + if(szFileName) delete szFileName; + if(szAsmRefName) delete szAsmRefName; + } +}; +typedef FIFO AsmManResList; + +struct AsmManModRef +{ + char* szName; + mdToken tkTok; + BOOL m_fNew; + AsmManModRef() {szName = NULL; tkTok = 0; m_fNew = TRUE; }; + ~AsmManModRef() { if(szName) delete szName; }; +}; +typedef FIFO AsmManModRefList; + +struct AsmManStrongName +{ + enum AllocationState + { + NotAllocated = 0, + AllocatedBySNApi, + AllocatedByNew + }; + + BYTE *m_pbSignatureKey; + DWORD m_cbSignatureKey; + BYTE *m_pbPublicKey; + DWORD m_cbPublicKey; + BYTE *m_pbPrivateKey; + DWORD m_cbPrivateKey; + WCHAR *m_wzKeyContainer; + BOOL m_fFullSign; + + // Where has the memory pointed to by m_pbPublicKey been taken from: + AllocationState m_dwPublicKeyAllocated; + + AsmManStrongName() { ZeroMemory(this, sizeof(*this)); } + ~AsmManStrongName() + { + if (m_dwPublicKeyAllocated == AllocatedBySNApi) + { + LegacyActivationShim::StrongNameFreeBuffer(m_pbPublicKey); + } + else if (m_dwPublicKeyAllocated == AllocatedByNew) + delete [] m_pbPublicKey; + + if (m_pbPrivateKey) + delete [] m_pbPrivateKey; + + if (m_pbSignatureKey) + delete [] m_pbSignatureKey; + } +}; + +class ErrorReporter; + +class AsmMan +{ + AsmManFileList m_FileLst; + AsmManComTypeList m_ComTypeLst; + AsmManResList m_ManResLst; + AsmManModRefList m_ModRefLst; + + AsmManComType* m_pCurComType; + AsmManRes* m_pCurManRes; + ErrorReporter* report; + void* m_pAssembler; + + AsmManFile* GetFileByName(__in __nullterminated char* szFileName); + AsmManAssembly* GetAsmRefByName(__in __nullterminated char* szAsmRefName); + AsmManComType* GetComTypeByName(__in_opt __nullterminated char* szComTypeName, + __in_opt __nullterminated char* szComEnclosingTypeName = NULL); + mdToken GetComTypeTokByName(__in_opt __nullterminated char* szComTypeName, + __in_opt __nullterminated char* szComEnclosingTypeName = NULL); + + IMetaDataEmit* m_pEmitter; + +public: + IMetaDataAssemblyEmit* m_pAsmEmitter; + AsmManAssemblyList m_AsmRefLst; + AsmManAssembly* m_pAssembly; + AsmManAssembly* m_pCurAsmRef; + char* m_szScopeName; + BinStr* m_pGUID; + AsmManStrongName m_sStrongName; + // Embedded manifest resources paraphernalia: + WCHAR* m_wzMResName[MAX_MANIFEST_RESOURCES]; + DWORD m_dwMResSize[MAX_MANIFEST_RESOURCES]; + BOOL m_fMResNew[MAX_MANIFEST_RESOURCES]; + DWORD m_dwMResNum; + DWORD m_dwMResSizeTotal; + AsmMan() { m_pAssembly = NULL; m_szScopeName = NULL; m_pGUID = NULL; m_pAsmEmitter = NULL; + memset(m_wzMResName,0,sizeof(m_wzMResName)); + memset(m_dwMResSize,0,sizeof(m_dwMResSize)); + m_dwMResNum = m_dwMResSizeTotal = 0; }; + AsmMan(void* pAsm) { m_pAssembly = NULL; m_szScopeName = NULL; m_pGUID = NULL; m_pAssembler = pAsm; m_pAsmEmitter = NULL; + memset(m_wzMResName,0,sizeof(m_wzMResName)); + memset(m_dwMResSize,0,sizeof(m_dwMResSize)); + m_dwMResNum = m_dwMResSizeTotal = 0; }; + AsmMan(ErrorReporter* rpt) { m_pAssembly = NULL; m_szScopeName = NULL; m_pGUID = NULL; report = rpt; m_pAsmEmitter = NULL; + memset(m_wzMResName,0,sizeof(m_wzMResName)); + memset(m_dwMResSize,0,sizeof(m_dwMResSize)); + m_dwMResNum = m_dwMResSizeTotal = 0; }; + ~AsmMan() + { + if(m_pAssembly) delete m_pAssembly; + if(m_szScopeName) delete m_szScopeName; + if(m_pGUID) delete m_pGUID; + }; + void SetErrorReporter(ErrorReporter* rpt) { report = rpt; }; + HRESULT EmitManifest(void); + + void SetEmitter( IMetaDataEmit* pEmitter) { m_pEmitter = pEmitter; }; + + void SetModuleName(__inout_opt __nullterminated char* szName); + + void AddFile(__in __nullterminated char* szName, DWORD dwAttr, BinStr* pHashBlob); + void EmitFiles(); + void EmitDebuggableAttribute(mdToken tkOwner); + + void StartAssembly(__in __nullterminated char* szName, __in_opt __nullterminated char* szAlias, DWORD dwAttr, BOOL isRef); + void EndAssembly(); + void EmitAssemblyRefs(); + void EmitAssembly(); + void SetAssemblyPublicKey(BinStr* pPublicKey); + void SetAssemblyPublicKeyToken(BinStr* pPublicKeyToken); + void SetAssemblyHashAlg(ULONG ulAlgID); + void SetAssemblyVer(USHORT usMajor, USHORT usMinor, USHORT usBuild, USHORT usRevision); + void SetAssemblyLocale(BinStr* pLocale, BOOL bConvertToUnicode); + void SetAssemblyHashBlob(BinStr* pHashBlob); + void SetAssemblyAutodetect(); + + void StartComType(__in __nullterminated char* szName, DWORD dwAttr); + void EndComType(); + void SetComTypeFile(__in __nullterminated char* szFileName); + void SetComTypeAsmRef(__in __nullterminated char* szAsmRefName); + void SetComTypeComType(__in __nullterminated char* szComTypeName); + BOOL SetComTypeImplementationTok(mdToken tk); + BOOL SetComTypeClassTok(mdToken tkClass); + + void StartManifestRes(__in __nullterminated char* szName, __in __nullterminated char* szAlias, DWORD dwAttr); + void EndManifestRes(); + void SetManifestResFile(__in __nullterminated char* szFileName, ULONG ulOffset); + void SetManifestResAsmRef(__in __nullterminated char* szAsmRefName); + + mdToken GetFileTokByName(__in __nullterminated char* szFileName); + mdToken GetAsmRefTokByName(__in __nullterminated char* szAsmRefName); + mdToken GetAsmTokByName(__in __nullterminated char* szAsmName) + { return (m_pAssembly && (strcmp(m_pAssembly->szName,szAsmName)==0)) ? m_pAssembly->tkTok : 0; }; + + mdToken GetModuleRefTokByName(__in __nullterminated char* szName) + { + if(szName && *szName) + { + AsmManModRef* pMR; + for(unsigned i=0; (pMR=m_ModRefLst.PEEK(i)); i++) + { + if(!strcmp(szName, pMR->szName)) return pMR->tkTok; + } + } + return 0; + }; + +}; + +#endif diff --git a/src/ilasm/asmparse.h b/src/ilasm/asmparse.h new file mode 100644 index 0000000000..9c1ced273e --- /dev/null +++ b/src/ilasm/asmparse.h @@ -0,0 +1,327 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +/**************************************************************************/ +/* asmParse is basically a wrapper around a YACC grammer COM+ assembly */ + +#ifndef asmparse_h +#define asmparse_h + +#include // for FILE + +#include "assembler.h" // for ErrorReporter Labels +//class Assembler; +//class BinStr; + + +/**************************************************************************/ +/* an abstraction of a stream of input characters */ +class ReadStream { +public: + + virtual unsigned getAll(__out char** ppch) = 0; + + // read at most 'buffLen' bytes into 'buff', Return the + // number of characters read. On EOF return 0 + virtual unsigned read(__out_ecount(buffLen) char* buff, unsigned buffLen) = 0; + + // Return the name of the stream, (for error reporting). + //virtual const char* name() = 0; + // Return the Unicode name of the stream + virtual const WCHAR* namew() = 0; + //return ptr to buffer containing specified source line + virtual char* getLine(int lineNum) = 0; +}; + +/**************************************************************************/ +class BinStrStream : public ReadStream { +public: + BinStrStream(BinStr* pbs) + { + m_pStart = (char*)(pbs->ptr()); + m_pCurr = m_pStart; + m_pEnd = m_pStart + pbs->length(); + m_pBS = pbs; + }; + ~BinStrStream() + { + //if(m_pBS) + // delete m_pBS; + }; + unsigned getAll(__out char **ppbuff) + { + *ppbuff = m_pStart; + return m_pBS->length(); + }; + unsigned read(__out_ecount(buffLen) char* buff, unsigned buffLen) + { + _ASSERTE(m_pStart != NULL); + unsigned Remainder = (unsigned)(m_pEnd - m_pCurr); + unsigned Len = buffLen; + if(Len > Remainder) Len = Remainder; + memcpy(buff,m_pCurr,Len); + m_pCurr += Len; + if(Len < buffLen) + { + memset(buff+Len,0,buffLen-Len); + } + return Len; + } + + const WCHAR* namew() + { + return L"local_define"; + } + + BOOL IsValid() + { + return(m_pStart != NULL); + } + + char* getLine(int lineNum) + { + return NULL; // this function is not used + } + +private: + char* m_pStart; + char* m_pEnd; + char* m_pCurr; + BinStr* m_pBS; + + +}; +/**************************************************************************/ +class MappedFileStream : public ReadStream { +public: + MappedFileStream(__in __nullterminated WCHAR* wFileName) + { + fileNameW = wFileName; + m_hFile = INVALID_HANDLE_VALUE; + m_hMapFile = NULL; + m_pStart = open(wFileName); + m_pCurr = m_pStart; + m_pEnd = m_pStart + m_FileSize; + //memset(fileNameANSI,0,MAX_FILENAME_LENGTH*4); + //WszWideCharToMultiByte(CP_ACP,0,wFileName,-1,fileNameANSI,MAX_FILENAME_LENGTH*4,NULL,NULL); + } + ~MappedFileStream() + { + if (m_hFile != INVALID_HANDLE_VALUE) + { + if (m_pStart) + UnmapViewOfFile((void*)m_pStart); + if (m_hMapFile) + CloseHandle(m_hMapFile); + CloseHandle(m_hFile); + + m_pStart = NULL; + m_hMapFile = NULL; + m_hFile = INVALID_HANDLE_VALUE; + m_FileSize = 0; + delete [] fileNameW; + fileNameW = NULL; + } + } + unsigned getAll(__out char** pbuff) + { + *pbuff = m_pStart; + return m_FileSize; + } + unsigned read(__out_ecount(buffLen) char* buff, unsigned buffLen) + { + _ASSERTE(m_pStart != NULL); + unsigned Remainder = (unsigned)(m_pEnd - m_pCurr); + unsigned Len = buffLen; + if(Len > Remainder) Len = Remainder; + memcpy(buff,m_pCurr,Len); + m_pCurr += Len; + if(Len < buffLen) + { + memset(buff+Len,0,buffLen-Len); + } + return Len; + } + + //const char* name() + //{ + // return(&fileNameANSI[0]); + //} + + const WCHAR* namew() + { + return fileNameW; + } + + void set_namew(const WCHAR* namew) + { + fileNameW = namew; + } + + BOOL IsValid() + { + return(m_pStart != NULL); + } + + char* getLine(int lineNum) + { + return NULL; // this function is not used + } + +private: + char* map_file() + { + DWORD dwFileSizeLow; + + dwFileSizeLow = GetFileSize( m_hFile, NULL); + if (dwFileSizeLow == INVALID_FILE_SIZE) + return NULL; + m_FileSize = dwFileSizeLow; + + // No difference between A and W in this case: last param (LPCTSTR) is NULL + m_hMapFile = WszCreateFileMapping(m_hFile, NULL, PAGE_READONLY, 0, 0, NULL); + if (m_hMapFile == NULL) + return NULL; + + return (char*)(HMODULE) MapViewOfFile(m_hMapFile, FILE_MAP_READ, 0, 0, 0); + } + char* open(const WCHAR* moduleName) + { + _ASSERTE(moduleName); + if (!moduleName) + return NULL; + + m_hFile = WszCreateFile(moduleName, GENERIC_READ, FILE_SHARE_READ, + 0, OPEN_EXISTING, 0, 0); + return (m_hFile == INVALID_HANDLE_VALUE) ? NULL : map_file(); + } + + const WCHAR* fileNameW; // FileName (for error reporting) + //char fileNameANSI[MAX_FILENAME_LENGTH*4]; + HANDLE m_hFile; // File we are reading from + DWORD m_FileSize; + HANDLE m_hMapFile; + char* m_pStart; + char* m_pEnd; + char* m_pCurr; + +}; + +typedef LIFO ARG_NAME_LIST_STACK; + +// functional pointers used in parsing +/*--------------------------------------------------------------------------*/ +typedef char*(*PFN_NEXTCHAR)(char*); + +char* nextcharA(__in __nullterminated char* pos); +char* nextcharU(__in __nullterminated char* pos); +char* nextcharW(__in __nullterminated char* pos); + +/*--------------------------------------------------------------------------*/ +typedef unsigned(*PFN_SYM)(char*); + +unsigned SymAU(__in __nullterminated char* curPos); +unsigned SymW(__in __nullterminated char* curPos); +/*--------------------------------------------------------------------------*/ +typedef char*(*PFN_NEWSTRFROMTOKEN)(char*,size_t); + +char* NewStrFromTokenAU(__in_ecount(tokLen) char* curTok, size_t tokLen); +char* NewStrFromTokenW(__in_ecount(tokLen) char* curTok, size_t tokLen); +/*--------------------------------------------------------------------------*/ +typedef char*(*PFN_NEWSTATICSTRFROMTOKEN)(char*,size_t,char*,size_t); + +char* NewStaticStrFromTokenAU(__in_ecount(tokLen) char* curTok, size_t tokLen, __out_ecount(bufSize) char* staticBuf, size_t bufSize); +char* NewStaticStrFromTokenW(__in_ecount(tokLen) char* curTok, size_t tokLen, __out_ecount(bufSize) char* staticBuf, size_t bufSize); +/*--------------------------------------------------------------------------*/ +typedef unsigned(*PFN_GETDOUBLE)(char*,unsigned,double**); + +unsigned GetDoubleAU(__in __nullterminated char* begNum, unsigned L, double** ppRes); +unsigned GetDoubleW(__in __nullterminated char* begNum, unsigned L, double** ppRes); +/*--------------------------------------------------------------------------*/ +struct PARSING_ENVIRONMENT +{ + char* curTok; // The token we are in the process of processing (for error reporting) + char* curPos; // current place in input buffer + char* endPos; // points just past the end of valid data in the buffer + + ReadStream* in; // how we fill up our buffer + + PFN_NEXTCHAR pfn_nextchar; + PFN_SYM pfn_Sym; + PFN_NEWSTRFROMTOKEN pfn_NewStrFromToken; + PFN_NEWSTATICSTRFROMTOKEN pfn_NewStaticStrFromToken; + PFN_GETDOUBLE pfn_GetDouble; + + bool bExternSource; + bool bExternSourceAutoincrement; + unsigned nExtLine; + unsigned nExtCol; + unsigned nExtLineEnd; + unsigned nExtColEnd; + unsigned curLine; // Line number (for error reporting) + + unsigned uCodePage; + + char szFileName[MAX_FILENAME_LENGTH*3+1]; + +}; +typedef LIFO PARSING_ENVIRONMENT_STACK; + +/**************************************************************************/ +/* AsmParse does all the parsing. It also builds up simple data structures, + (like signatures), but does not do the any 'heavy lifting' like define + methods or classes. Instead it calls to the Assembler object to do that */ + +class AsmParse : public ErrorReporter +{ +public: + AsmParse(ReadStream* stream, Assembler *aAssem); + ~AsmParse(); + void CreateEnvironment(ReadStream* stream); + void ParseFile(ReadStream* stream); + // The parser knows how to put line numbers on things and report the error + virtual void error(const char* fmt, ...); + virtual void warn(const char* fmt, ...); + virtual void msg(const char* fmt, ...); + char *getLine(int lineNum) { return penv->in->getLine(lineNum); }; + unsigned getAll(__out char** pbuff) { return penv->in->getAll(pbuff); }; + bool Success() {return success; }; + void SetIncludePath(__in WCHAR* wz) { wzIncludePath = wz; }; + + ARG_NAME_LIST_STACK m_ANSFirst; + ARG_NAME_LIST_STACK m_ANSLast; + PARSING_ENVIRONMENT *penv; + PARSING_ENVIRONMENT_STACK PEStack; + +private: + BinStr* MakeSig(unsigned callConv, BinStr* retType, BinStr* args, int ntyargs = 0); + BinStr* MakeTypeClass(CorElementType kind, mdToken tk); + BinStr* MakeTypeArray(CorElementType kind, BinStr* elemType, BinStr* bounds); + + char* fillBuff(__in_opt __nullterminated char* curPos); // refill the input buffer + DWORD IsItUnicode(CONST LPVOID pBuff, int cb, LPINT lpi); + HANDLE hstdout; + HANDLE hstderr; + +private: + friend void yyerror(__in __nullterminated char* str); + friend int parse_literal(unsigned curSym, __inout __nullterminated char* &curPos, BOOL translate_escapes); + friend int yyparse(); + friend int yylex(); + friend Instr* SetupInstr(unsigned short opcode); + friend int findKeyword(const char* name, size_t nameLen, unsigned short* opcode); + friend TypeDefDescr* findTypedef(__in_ecount(nameLen) char* name, size_t nameLen); + friend char* skipBlanks(__in __nullterminated char*,unsigned*); + friend char* nextBlank(__in __nullterminated char*); + friend int ProcessEOF(); + friend unsigned __int8* skipType(unsigned __int8* ptr, BOOL fFixupType); + friend void FixupConstraints(); + + Assembler* assem; // This does most of the semantic processing + bool success; // overall success of the compilation + WCHAR* wzIncludePath; +}; + +#endif + diff --git a/src/ilasm/asmparse.y b/src/ilasm/asmparse.y new file mode 100644 index 0000000000..6619cfba20 --- /dev/null +++ b/src/ilasm/asmparse.y @@ -0,0 +1,2053 @@ +%{ + +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// +// File asmparse.y +// +#include "ilasmpch.h" + +#include "grammar_before.cpp" + +%} + +%union { + CorRegTypeAttr classAttr; + CorMethodAttr methAttr; + CorFieldAttr fieldAttr; + CorMethodImpl implAttr; + CorEventAttr eventAttr; + CorPropertyAttr propAttr; + CorPinvokeMap pinvAttr; + CorDeclSecurity secAct; + CorFileFlags fileAttr; + CorAssemblyFlags asmAttr; + CorAssemblyFlags asmRefAttr; + CorTypeAttr exptAttr; + CorManifestResourceFlags manresAttr; + double* float64; + __int64* int64; + __int32 int32; + char* string; + BinStr* binstr; + Labels* labels; + Instr* instr; // instruction opcode + NVPair* pair; + pTyParList typarlist; + mdToken token; + TypeDefDescr* tdd; + CustomDescr* cad; + unsigned short opcode; +}; + + /* These are returned by the LEXER and have values */ +%token ERROR_ BAD_COMMENT_ BAD_LITERAL_ /* bad strings, */ +%token ID /* testing343 */ +%token DOTTEDNAME /* System.Object */ +%token QSTRING /* "Hello World\n" */ +%token SQSTRING /* 'Hello World\n' */ +%token INT32 /* 3425 0x34FA 0352 */ +%token INT64 /* 342534523534534 0x34FA434644554 */ +%token FLOAT64 /* -334234 24E-34 */ +%token HEXBYTE /* 05 1A FA */ +%token TYPEDEF_T +%token TYPEDEF_M +%token TYPEDEF_F +%token TYPEDEF_TS +%token TYPEDEF_MR +%token TYPEDEF_CA + + + /* multi-character punctuation */ +%token DCOLON /* :: */ +%token ELIPSIS /* ... */ + + /* Keywords Note the undersores are to avoid collisions as these are common names */ +%token VOID_ BOOL_ CHAR_ UNSIGNED_ INT_ INT8_ INT16_ INT32_ INT64_ FLOAT_ FLOAT32_ FLOAT64_ BYTEARRAY_ +%token UINT_ UINT8_ UINT16_ UINT32_ UINT64_ FLAGS_ CALLCONV_ MDTOKEN_ +%token OBJECT_ STRING_ NULLREF_ + /* misc keywords */ +%token DEFAULT_ CDECL_ VARARG_ STDCALL_ THISCALL_ FASTCALL_ CLASS_ +%token TYPEDREF_ UNMANAGED_ FINALLY_ HANDLER_ CATCH_ FILTER_ FAULT_ +%token EXTENDS_ IMPLEMENTS_ TO_ AT_ TLS_ TRUE_ FALSE_ _INTERFACEIMPL + + /* class, method, field attributes */ + +%token VALUE_ VALUETYPE_ NATIVE_ INSTANCE_ SPECIALNAME_ FORWARDER_ +%token STATIC_ PUBLIC_ PRIVATE_ FAMILY_ FINAL_ SYNCHRONIZED_ INTERFACE_ SEALED_ NESTED_ +%token ABSTRACT_ AUTO_ SEQUENTIAL_ EXPLICIT_ ANSI_ UNICODE_ AUTOCHAR_ IMPORT_ ENUM_ +%token VIRTUAL_ NOINLINING_ AGGRESSIVEINLINING_ NOOPTIMIZATION_ UNMANAGEDEXP_ BEFOREFIELDINIT_ +%token STRICT_ RETARGETABLE_ WINDOWSRUNTIME_ NOPLATFORM_ +%token METHOD_ FIELD_ PINNED_ MODREQ_ MODOPT_ SERIALIZABLE_ PROPERTY_ TYPE_ +%token ASSEMBLY_ FAMANDASSEM_ FAMORASSEM_ PRIVATESCOPE_ HIDEBYSIG_ NEWSLOT_ RTSPECIALNAME_ PINVOKEIMPL_ +%token _CTOR _CCTOR LITERAL_ NOTSERIALIZED_ INITONLY_ REQSECOBJ_ + /* method implementation attributes: NATIVE_ and UNMANAGED_ listed above */ +%token CIL_ OPTIL_ MANAGED_ FORWARDREF_ PRESERVESIG_ RUNTIME_ INTERNALCALL_ + /* PInvoke-specific keywords */ +%token _IMPORT NOMANGLE_ LASTERR_ WINAPI_ AS_ BESTFIT_ ON_ OFF_ CHARMAPERROR_ + + /* intruction tokens (actually instruction groupings) */ +%token INSTR_NONE INSTR_VAR INSTR_I INSTR_I8 INSTR_R INSTR_BRTARGET INSTR_METHOD INSTR_FIELD +%token INSTR_TYPE INSTR_STRING INSTR_SIG INSTR_TOK +%token INSTR_SWITCH + + /* assember directives */ +%token _CLASS _NAMESPACE _METHOD _FIELD _DATA _THIS _BASE _NESTER +%token _EMITBYTE _TRY _MAXSTACK _LOCALS _ENTRYPOINT _ZEROINIT +%token _EVENT _ADDON _REMOVEON _FIRE _OTHER +%token _PROPERTY _SET _GET DEFAULT_ +%token _PERMISSION _PERMISSIONSET + + /* security actions */ +%token REQUEST_ DEMAND_ ASSERT_ DENY_ PERMITONLY_ LINKCHECK_ INHERITCHECK_ +%token REQMIN_ REQOPT_ REQREFUSE_ PREJITGRANT_ PREJITDENY_ NONCASDEMAND_ +%token NONCASLINKDEMAND_ NONCASINHERITANCE_ + + /* extern debug info specifier (to be used by precompilers only) */ +%token _LINE P_LINE _LANGUAGE + /* custom value specifier */ +%token _CUSTOM + /* local vars zeroinit specifier */ +%token INIT_ + /* class layout */ +%token _SIZE _PACK +%token _VTABLE _VTFIXUP FROMUNMANAGED_ CALLMOSTDERIVED_ _VTENTRY RETAINAPPDOMAIN_ + /* manifest */ +%token _FILE NOMETADATA_ _HASH _ASSEMBLY _PUBLICKEY _PUBLICKEYTOKEN ALGORITHM_ _VER _LOCALE EXTERN_ +%token _MRESOURCE +%token _MODULE _EXPORT +%token LEGACY_ LIBRARY_ X86_ IA64_ AMD64_ ARM_ + /* field marshaling */ +%token MARSHAL_ CUSTOM_ SYSSTRING_ FIXED_ VARIANT_ CURRENCY_ SYSCHAR_ DECIMAL_ DATE_ BSTR_ TBSTR_ LPSTR_ +%token LPWSTR_ LPTSTR_ OBJECTREF_ IUNKNOWN_ IDISPATCH_ STRUCT_ SAFEARRAY_ BYVALSTR_ LPVOID_ ANY_ ARRAY_ LPSTRUCT_ +%token IIDPARAM_ + /* parameter attributes */ +%token IN_ OUT_ OPT_ PARAM_ + /* method implementations */ +%token _OVERRIDE WITH_ + /* variant type specifics */ +%token NULL_ ERROR_ HRESULT_ CARRAY_ USERDEFINED_ RECORD_ FILETIME_ BLOB_ STREAM_ STORAGE_ +%token STREAMED_OBJECT_ STORED_OBJECT_ BLOB_OBJECT_ CF_ CLSID_ VECTOR_ + /* header flags */ +%token _SUBSYSTEM _CORFLAGS ALIGNMENT_ _IMAGEBASE _STACKRESERVE + + /* syntactic sugar */ +%token _TYPEDEF _TEMPLATE _TYPELIST _MSCORLIB + + /* compilation control directives */ +%token P_DEFINE P_UNDEF P_IFDEF P_IFNDEF P_ELSE P_ENDIF P_INCLUDE + + /* nonTerminals */ +%type dottedName id methodName atOpt slashedName +%type labels +%type callConv callKind int32 customHead customHeadWithOwner vtfixupAttr paramAttr ddItemCount variantType repeatOpt truefalse typarAttrib typarAttribs +%type iidParamIndex genArity genArityNotEmpty +%type float64 +%type int64 +%type sigArgs0 sigArgs1 sigArg type bound bounds1 bytes hexbytes nativeType marshalBlob initOpt compQstring caValue +%type marshalClause +%type fieldInit serInit fieldSerInit +%type f32seq f64seq i8seq i16seq i32seq i64seq boolSeq sqstringSeq classSeq objSeq +%type simpleType +%type tyArgs0 tyArgs1 tyArgs2 typeList typeListNotEmpty tyBound +%type customBlobDescr serializType customBlobArgs customBlobNVPairs +%type secAttrBlob secAttrSetBlob +%type fieldOrProp intOrWildcard +%type typarsRest typars typarsClause +%type className typeSpec ownerType customType memberRef methodRef mdtoken +%type classAttr +%type methAttr +%type fieldAttr +%type implAttr +%type eventAttr +%type propAttr +%type pinvAttr +%type nameValPairs nameValPair +%type secAction +%type psetHead +%type fileAttr +%type fileEntry +%type asmAttr +%type exptAttr +%type manresAttr +%type customDescr customDescrWithOwner +%type instr_none instr_var instr_i instr_i8 instr_r instr_brtarget instr_method instr_field +%type instr_type instr_string instr_sig instr_tok instr_switch +%type instr_r_head + +%start decls + +/**************************************************************************/ +%% + +decls : /* EMPTY */ + | decls decl + ; +/* Module-level declarations */ +decl : classHead '{' classDecls '}' { PASM->EndClass(); } + | nameSpaceHead '{' decls '}' { PASM->EndNameSpace(); } + | methodHead methodDecls '}' { if(PASM->m_pCurMethod->m_ulLines[1] ==0) + { PASM->m_pCurMethod->m_ulLines[1] = PASM->m_ulCurLine; + PASM->m_pCurMethod->m_ulColumns[1]=PASM->m_ulCurColumn;} + PASM->EndMethod(); } + | fieldDecl + | dataDecl + | vtableDecl + | vtfixupDecl + | extSourceSpec + | fileDecl + | assemblyHead '{' assemblyDecls '}' { PASMM->EndAssembly(); } + | assemblyRefHead '{' assemblyRefDecls '}' { PASMM->EndAssembly(); } + | exptypeHead '{' exptypeDecls '}' { PASMM->EndComType(); } + | manifestResHead '{' manifestResDecls '}' { PASMM->EndManifestRes(); } + | moduleHead + | secDecl + | customAttrDecl + | _SUBSYSTEM int32 { +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:22011) // Suppress PREFast warning about integer overflow/underflow +#endif + PASM->m_dwSubsystem = $2; +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + } + | _CORFLAGS int32 { PASM->m_dwComImageFlags = $2; } + | _FILE ALIGNMENT_ int32 { PASM->m_dwFileAlignment = $3; + if(($3 & ($3 - 1))||($3 < 0x200)||($3 > 0x10000)) + PASM->report->error("Invalid file alignment, must be power of 2 from 0x200 to 0x10000\n");} + | _IMAGEBASE int64 { PASM->m_stBaseAddress = (ULONGLONG)(*($2)); delete $2; + if(PASM->m_stBaseAddress & 0xFFFF) + PASM->report->error("Invalid image base, must be 0x10000-aligned\n");} + | _STACKRESERVE int64 { PASM->m_stSizeOfStackReserve = (size_t)(*($2)); delete $2; } + | languageDecl + | typedefDecl + | compControl + | _TYPELIST '{' classNameSeq '}' + | _MSCORLIB { PASM->m_fIsMscorlib = TRUE; } + ; + +classNameSeq : /* EMPTY */ + | className classNameSeq + ; + +compQstring : QSTRING { $$ = $1; } + | compQstring '+' QSTRING { $$ = $1; $$->append($3); delete $3; } + ; + +languageDecl : _LANGUAGE SQSTRING { LPCSTRToGuid($2,&(PASM->m_guidLang)); } + | _LANGUAGE SQSTRING ',' SQSTRING { LPCSTRToGuid($2,&(PASM->m_guidLang)); + LPCSTRToGuid($4,&(PASM->m_guidLangVendor));} + | _LANGUAGE SQSTRING ',' SQSTRING ',' SQSTRING { LPCSTRToGuid($2,&(PASM->m_guidLang)); + LPCSTRToGuid($4,&(PASM->m_guidLangVendor)); + LPCSTRToGuid($4,&(PASM->m_guidDoc));} + ; +/* Basic tokens */ +id : ID { $$ = $1; } + | SQSTRING { $$ = $1; } + ; + +dottedName : id { $$ = $1; } + | DOTTEDNAME { $$ = $1; } + | dottedName '.' dottedName { $$ = newStringWDel($1, '.', $3); } + ; + +int32 : INT32 { $$ = $1; } + ; + +int64 : INT64 { $$ = $1; } + | INT32 { $$ = neg ? new __int64($1) : new __int64((unsigned)$1); } + ; + +float64 : FLOAT64 { $$ = $1; } + | FLOAT32_ '(' int32 ')' { float f; *((__int32*) (&f)) = $3; $$ = new double(f); } + | FLOAT64_ '(' int64 ')' { $$ = (double*) $3; } + ; + +/* Aliasing of types, type specs, methods, fields and custom attributes */ +typedefDecl : _TYPEDEF type AS_ dottedName { PASM->AddTypeDef($2,$4); } + | _TYPEDEF className AS_ dottedName { PASM->AddTypeDef($2,$4); } + | _TYPEDEF memberRef AS_ dottedName { PASM->AddTypeDef($2,$4); } + | _TYPEDEF customDescr AS_ dottedName { $2->tkOwner = 0; PASM->AddTypeDef($2,$4); } + | _TYPEDEF customDescrWithOwner AS_ dottedName { PASM->AddTypeDef($2,$4); } + ; + +/* Compilation control directives are processed within yylex(), + displayed here just for grammar completeness */ +compControl : P_DEFINE dottedName { DefineVar($2, NULL); } + | P_DEFINE dottedName compQstring { DefineVar($2, $3); } + | P_UNDEF dottedName { UndefVar($2); } + | P_IFDEF dottedName { SkipToken = !IsVarDefined($2); + IfEndif++; + } + | P_IFNDEF dottedName { SkipToken = IsVarDefined($2); + IfEndif++; + } + | P_ELSE { if(IfEndif == 1) SkipToken = !SkipToken;} + | P_ENDIF { if(IfEndif == 0) + PASM->report->error("Unmatched #endif\n"); + else IfEndif--; + } + | P_INCLUDE QSTRING { _ASSERTE(!"yylex should have dealt with this"); } + | ';' { } + ; + +/* Custom attribute declarations */ +customDescr : _CUSTOM customType { $$ = new CustomDescr(PASM->m_tkCurrentCVOwner, $2, NULL); } + | _CUSTOM customType '=' compQstring { $$ = new CustomDescr(PASM->m_tkCurrentCVOwner, $2, $4); } + | _CUSTOM customType '=' '{' customBlobDescr '}' { $$ = new CustomDescr(PASM->m_tkCurrentCVOwner, $2, $5); } + | customHead bytes ')' { $$ = new CustomDescr(PASM->m_tkCurrentCVOwner, $1, $2); } + ; + +customDescrWithOwner : _CUSTOM '(' ownerType ')' customType { $$ = new CustomDescr($3, $5, NULL); } + | _CUSTOM '(' ownerType ')' customType '=' compQstring { $$ = new CustomDescr($3, $5, $7); } + | _CUSTOM '(' ownerType ')' customType '=' '{' customBlobDescr '}' + { $$ = new CustomDescr($3, $5, $8); } + | customHeadWithOwner bytes ')' { $$ = new CustomDescr(PASM->m_tkCurrentCVOwner, $1, $2); } + ; + +customHead : _CUSTOM customType '=' '(' { $$ = $2; bParsingByteArray = TRUE; } + ; + +customHeadWithOwner : _CUSTOM '(' ownerType ')' customType '=' '(' + { PASM->m_pCustomDescrList = NULL; + PASM->m_tkCurrentCVOwner = $3; + $$ = $5; bParsingByteArray = TRUE; } + ; + +customType : methodRef { $$ = $1; } + ; + +ownerType : typeSpec { $$ = $1; } + | memberRef { $$ = $1; } + ; + +/* Verbal description of custom attribute initialization blob */ +customBlobDescr : customBlobArgs customBlobNVPairs { $$ = $1; + $$->appendInt16(nCustomBlobNVPairs); + $$->append($2); + nCustomBlobNVPairs = 0; } + ; + +customBlobArgs : /* EMPTY */ { $$ = new BinStr(); $$->appendInt16(VAL16(0x0001)); } + | customBlobArgs serInit { $$ = $1; + $$->appendFrom($2, (*($2->ptr()) == ELEMENT_TYPE_SZARRAY) ? 2 : 1); } + | customBlobArgs compControl { $$ = $1; } + ; + +customBlobNVPairs : /* EMPTY */ { $$ = new BinStr(); } + | customBlobNVPairs fieldOrProp serializType dottedName '=' serInit + { $$ = $1; $$->appendInt8($2); + $$->append($3); + AppendStringWithLength($$,$4); + $$->appendFrom($6, (*($6->ptr()) == ELEMENT_TYPE_SZARRAY) ? 2 : 1); + nCustomBlobNVPairs++; } + | customBlobNVPairs compControl { $$ = $1; } + ; + +fieldOrProp : FIELD_ { $$ = SERIALIZATION_TYPE_FIELD; } + | PROPERTY_ { $$ = SERIALIZATION_TYPE_PROPERTY; } + ; + +customAttrDecl : customDescr { if($1->tkOwner && !$1->tkInterfacePair) + PASM->DefineCV($1); + else if(PASM->m_pCustomDescrList) + PASM->m_pCustomDescrList->PUSH($1); } + | customDescrWithOwner { PASM->DefineCV($1); } + | TYPEDEF_CA { CustomDescr* pNew = new CustomDescr($1->m_pCA); + if(pNew->tkOwner == 0) pNew->tkOwner = PASM->m_tkCurrentCVOwner; + if(pNew->tkOwner) + PASM->DefineCV(pNew); + else if(PASM->m_pCustomDescrList) + PASM->m_pCustomDescrList->PUSH(pNew); } + ; + +serializType : simpleType { $$ = $1; } + | TYPE_ { $$ = new BinStr(); $$->appendInt8(SERIALIZATION_TYPE_TYPE); } + | OBJECT_ { $$ = new BinStr(); $$->appendInt8(SERIALIZATION_TYPE_TAGGED_OBJECT); } + | ENUM_ CLASS_ SQSTRING { $$ = new BinStr(); $$->appendInt8(SERIALIZATION_TYPE_ENUM); + AppendStringWithLength($$,$3); } + | ENUM_ className { $$ = new BinStr(); $$->appendInt8(SERIALIZATION_TYPE_ENUM); + AppendStringWithLength($$,PASM->ReflectionNotation($2)); } + | serializType '[' ']' { $$ = $1; $$->insertInt8(ELEMENT_TYPE_SZARRAY); } + ; + + +/* Module declaration */ +moduleHead : _MODULE { PASMM->SetModuleName(NULL); PASM->m_tkCurrentCVOwner=1; } + | _MODULE dottedName { PASMM->SetModuleName($2); PASM->m_tkCurrentCVOwner=1; } + | _MODULE EXTERN_ dottedName { BinStr* pbs = new BinStr(); + unsigned L = (unsigned)strlen($3); + memcpy((char*)(pbs->getBuff(L)),$3,L); + PASM->EmitImport(pbs); delete pbs;} + ; + +/* VTable Fixup table declaration */ +vtfixupDecl : _VTFIXUP '[' int32 ']' vtfixupAttr AT_ id { /*PASM->SetDataSection(); PASM->EmitDataLabel($7);*/ + PASM->m_VTFList.PUSH(new VTFEntry((USHORT)$3, (USHORT)$5, $7)); } + ; + +vtfixupAttr : /* EMPTY */ { $$ = 0; } + | vtfixupAttr INT32_ { $$ = $1 | COR_VTABLE_32BIT; } + | vtfixupAttr INT64_ { $$ = $1 | COR_VTABLE_64BIT; } + | vtfixupAttr FROMUNMANAGED_ { $$ = $1 | COR_VTABLE_FROM_UNMANAGED; } + | vtfixupAttr CALLMOSTDERIVED_ { $$ = $1 | COR_VTABLE_CALL_MOST_DERIVED; } + | vtfixupAttr RETAINAPPDOMAIN_ { $$ = $1 | COR_VTABLE_FROM_UNMANAGED_RETAIN_APPDOMAIN; } + ; + +vtableDecl : vtableHead bytes ')' /* deprecated */ { PASM->m_pVTable = $2; } + ; + +vtableHead : _VTABLE '=' '(' /* deprecated */ { bParsingByteArray = TRUE; } + ; + +/* Namespace and class declaration */ +nameSpaceHead : _NAMESPACE dottedName { PASM->StartNameSpace($2); } + ; + +_class : _CLASS { newclass = TRUE; } + ; + +classHeadBegin : _class classAttr dottedName typarsClause { if($4) FixupConstraints(); + PASM->StartClass($3, $2, $4); + TyParFixupList.RESET(false); + newclass = FALSE; + } + ; +classHead : classHeadBegin extendsClause implClause { PASM->AddClass(); } + ; + +classAttr : /* EMPTY */ { $$ = (CorRegTypeAttr) 0; } + | classAttr PUBLIC_ { $$ = (CorRegTypeAttr) (($1 & ~tdVisibilityMask) | tdPublic); } + | classAttr PRIVATE_ { $$ = (CorRegTypeAttr) (($1 & ~tdVisibilityMask) | tdNotPublic); } + | classAttr VALUE_ { $$ = (CorRegTypeAttr) ($1 | 0x80000000 | tdSealed); } + | classAttr ENUM_ { $$ = (CorRegTypeAttr) ($1 | 0x40000000); } + | classAttr INTERFACE_ { $$ = (CorRegTypeAttr) ($1 | tdInterface | tdAbstract); } + | classAttr SEALED_ { $$ = (CorRegTypeAttr) ($1 | tdSealed); } + | classAttr ABSTRACT_ { $$ = (CorRegTypeAttr) ($1 | tdAbstract); } + | classAttr AUTO_ { $$ = (CorRegTypeAttr) (($1 & ~tdLayoutMask) | tdAutoLayout); } + | classAttr SEQUENTIAL_ { $$ = (CorRegTypeAttr) (($1 & ~tdLayoutMask) | tdSequentialLayout); } + | classAttr EXPLICIT_ { $$ = (CorRegTypeAttr) (($1 & ~tdLayoutMask) | tdExplicitLayout); } + | classAttr ANSI_ { $$ = (CorRegTypeAttr) (($1 & ~tdStringFormatMask) | tdAnsiClass); } + | classAttr UNICODE_ { $$ = (CorRegTypeAttr) (($1 & ~tdStringFormatMask) | tdUnicodeClass); } + | classAttr AUTOCHAR_ { $$ = (CorRegTypeAttr) (($1 & ~tdStringFormatMask) | tdAutoClass); } + | classAttr IMPORT_ { $$ = (CorRegTypeAttr) ($1 | tdImport); } + | classAttr SERIALIZABLE_ { $$ = (CorRegTypeAttr) ($1 | tdSerializable); } + | classAttr WINDOWSRUNTIME_ { $$ = (CorRegTypeAttr) ($1 | tdWindowsRuntime); } + | classAttr NESTED_ PUBLIC_ { $$ = (CorRegTypeAttr) (($1 & ~tdVisibilityMask) | tdNestedPublic); } + | classAttr NESTED_ PRIVATE_ { $$ = (CorRegTypeAttr) (($1 & ~tdVisibilityMask) | tdNestedPrivate); } + | classAttr NESTED_ FAMILY_ { $$ = (CorRegTypeAttr) (($1 & ~tdVisibilityMask) | tdNestedFamily); } + | classAttr NESTED_ ASSEMBLY_ { $$ = (CorRegTypeAttr) (($1 & ~tdVisibilityMask) | tdNestedAssembly); } + | classAttr NESTED_ FAMANDASSEM_ { $$ = (CorRegTypeAttr) (($1 & ~tdVisibilityMask) | tdNestedFamANDAssem); } + | classAttr NESTED_ FAMORASSEM_ { $$ = (CorRegTypeAttr) (($1 & ~tdVisibilityMask) | tdNestedFamORAssem); } + | classAttr BEFOREFIELDINIT_ { $$ = (CorRegTypeAttr) ($1 | tdBeforeFieldInit); } + | classAttr SPECIALNAME_ { $$ = (CorRegTypeAttr) ($1 | tdSpecialName); } + | classAttr RTSPECIALNAME_ { $$ = (CorRegTypeAttr) ($1); } + | classAttr FLAGS_ '(' int32 ')' { $$ = (CorRegTypeAttr) ($4); } + ; + +extendsClause : /* EMPTY */ + | EXTENDS_ typeSpec { PASM->m_crExtends = $2; } + ; + +implClause : /* EMPTY */ + | IMPLEMENTS_ implList + ; + +classDecls : /* EMPTY */ + | classDecls classDecl + ; + +implList : implList ',' typeSpec { PASM->AddToImplList($3); } + | typeSpec { PASM->AddToImplList($1); } + ; + +/* Generic type parameters declaration */ +typeList : /* EMPTY */ { $$ = new BinStr(); } + | typeListNotEmpty { $$ = $1; } + ; + +typeListNotEmpty : typeSpec { $$ = new BinStr(); $$->appendInt32($1); } + | typeListNotEmpty ',' typeSpec { $$ = $1; $$->appendInt32($3); } + ; + +typarsClause : /* EMPTY */ { $$ = NULL; PASM->m_TyParList = NULL;} + | '<' typars '>' { $$ = $2; PASM->m_TyParList = $2;} + ; + +typarAttrib : '+' { $$ = gpCovariant; } + | '-' { $$ = gpContravariant; } + | CLASS_ { $$ = gpReferenceTypeConstraint; } + | VALUETYPE_ { $$ = gpNotNullableValueTypeConstraint; } + | _CTOR { $$ = gpDefaultConstructorConstraint; } + ; + +typarAttribs : /* EMPTY */ { $$ = 0; } + | typarAttrib typarAttribs { $$ = $1 | $2; } + ; + +typars : typarAttribs tyBound dottedName typarsRest {$$ = new TyParList($1, $2, $3, $4);} + | typarAttribs dottedName typarsRest {$$ = new TyParList($1, NULL, $2, $3);} + ; + +typarsRest : /* EMPTY */ { $$ = NULL; } + | ',' typars { $$ = $2; } + ; + +tyBound : '(' typeList ')' { $$ = $2; } + ; + +genArity : /* EMPTY */ { $$= 0; } + | genArityNotEmpty { $$ = $1; } + ; + +genArityNotEmpty : '<' '[' int32 ']' '>' { $$ = $3; } + ; + +/* Class body declarations */ +classDecl : methodHead methodDecls '}' { if(PASM->m_pCurMethod->m_ulLines[1] ==0) + { PASM->m_pCurMethod->m_ulLines[1] = PASM->m_ulCurLine; + PASM->m_pCurMethod->m_ulColumns[1]=PASM->m_ulCurColumn;} + PASM->EndMethod(); } + | classHead '{' classDecls '}' { PASM->EndClass(); } + | eventHead '{' eventDecls '}' { PASM->EndEvent(); } + | propHead '{' propDecls '}' { PASM->EndProp(); } + | fieldDecl + | dataDecl + | secDecl + | extSourceSpec + | customAttrDecl + | _SIZE int32 { PASM->m_pCurClass->m_ulSize = $2; } + | _PACK int32 { PASM->m_pCurClass->m_ulPack = $2; } + | exportHead '{' exptypeDecls '}' { PASMM->EndComType(); } + | _OVERRIDE typeSpec DCOLON methodName WITH_ callConv type typeSpec DCOLON methodName '(' sigArgs0 ')' + { BinStr *sig1 = parser->MakeSig($6, $7, $12); + BinStr *sig2 = new BinStr(); sig2->append(sig1); + PASM->AddMethodImpl($2,$4,sig1,$8,$10,sig2); + PASM->ResetArgNameList(); + } + | _OVERRIDE METHOD_ callConv type typeSpec DCOLON methodName genArity '(' sigArgs0 ')' WITH_ METHOD_ callConv type typeSpec DCOLON methodName genArity '(' sigArgs0 ')' + { PASM->AddMethodImpl($5,$7, + ($8==0 ? parser->MakeSig($3,$4,$10) : + parser->MakeSig($3| IMAGE_CEE_CS_CALLCONV_GENERIC,$4,$10,$8)), + $16,$18, + ($19==0 ? parser->MakeSig($14,$15,$21) : + parser->MakeSig($14| IMAGE_CEE_CS_CALLCONV_GENERIC,$15,$21,$19))); + PASM->ResetArgNameList(); + } + | languageDecl + | compControl + | PARAM_ TYPE_ '[' int32 ']' { if(($4 > 0) && ($4 <= (int)PASM->m_pCurClass->m_NumTyPars)) + PASM->m_pCustomDescrList = PASM->m_pCurClass->m_TyPars[$4-1].CAList(); + else + PASM->report->error("Type parameter index out of range\n"); + } + | PARAM_ TYPE_ dottedName { int n = PASM->m_pCurClass->FindTyPar($3); + if(n >= 0) + PASM->m_pCustomDescrList = PASM->m_pCurClass->m_TyPars[n].CAList(); + else + PASM->report->error("Type parameter '%s' undefined\n",$3); + } + | _INTERFACEIMPL TYPE_ typeSpec customDescr { $4->tkInterfacePair = $3; + if(PASM->m_pCustomDescrList) + PASM->m_pCustomDescrList->PUSH($4); + } + ; + +/* Field declaration */ +fieldDecl : _FIELD repeatOpt fieldAttr type dottedName atOpt initOpt + { $4->insertInt8(IMAGE_CEE_CS_CALLCONV_FIELD); + PASM->AddField($5, $4, $3, $6, $7, $2); } + ; + +fieldAttr : /* EMPTY */ { $$ = (CorFieldAttr) 0; } + | fieldAttr STATIC_ { $$ = (CorFieldAttr) ($1 | fdStatic); } + | fieldAttr PUBLIC_ { $$ = (CorFieldAttr) (($1 & ~mdMemberAccessMask) | fdPublic); } + | fieldAttr PRIVATE_ { $$ = (CorFieldAttr) (($1 & ~mdMemberAccessMask) | fdPrivate); } + | fieldAttr FAMILY_ { $$ = (CorFieldAttr) (($1 & ~mdMemberAccessMask) | fdFamily); } + | fieldAttr INITONLY_ { $$ = (CorFieldAttr) ($1 | fdInitOnly); } + | fieldAttr RTSPECIALNAME_ { $$ = $1; } /*{ $$ = (CorFieldAttr) ($1 | fdRTSpecialName); }*/ + | fieldAttr SPECIALNAME_ { $$ = (CorFieldAttr) ($1 | fdSpecialName); } + /* commented out because PInvoke for fields is not supported by EE + | fieldAttr PINVOKEIMPL_ '(' compQstring AS_ compQstring pinvAttr ')' + { $$ = (CorFieldAttr) ($1 | fdPinvokeImpl); + PASM->SetPinvoke($4,0,$6,$7); } + | fieldAttr PINVOKEIMPL_ '(' compQstring pinvAttr ')' + { $$ = (CorFieldAttr) ($1 | fdPinvokeImpl); + PASM->SetPinvoke($4,0,NULL,$5); } + | fieldAttr PINVOKEIMPL_ '(' pinvAttr ')' + { PASM->SetPinvoke(new BinStr(),0,NULL,$4); + $$ = (CorFieldAttr) ($1 | fdPinvokeImpl); } + */ + | fieldAttr MARSHAL_ '(' marshalBlob ')' + { PASM->m_pMarshal = $4; } + | fieldAttr ASSEMBLY_ { $$ = (CorFieldAttr) (($1 & ~mdMemberAccessMask) | fdAssembly); } + | fieldAttr FAMANDASSEM_ { $$ = (CorFieldAttr) (($1 & ~mdMemberAccessMask) | fdFamANDAssem); } + | fieldAttr FAMORASSEM_ { $$ = (CorFieldAttr) (($1 & ~mdMemberAccessMask) | fdFamORAssem); } + | fieldAttr PRIVATESCOPE_ { $$ = (CorFieldAttr) (($1 & ~mdMemberAccessMask) | fdPrivateScope); } + | fieldAttr LITERAL_ { $$ = (CorFieldAttr) ($1 | fdLiteral); } + | fieldAttr NOTSERIALIZED_ { $$ = (CorFieldAttr) ($1 | fdNotSerialized); } + | fieldAttr FLAGS_ '(' int32 ')' { $$ = (CorFieldAttr) ($4); } + ; + +atOpt : /* EMPTY */ { $$ = 0; } + | AT_ id { $$ = $2; } + ; + +initOpt : /* EMPTY */ { $$ = NULL; } + | '=' fieldInit { $$ = $2; } + ; + +repeatOpt : /* EMPTY */ { $$ = 0xFFFFFFFF; } + | '[' int32 ']' { $$ = $2; } + ; + +/* Method referencing */ +methodRef : callConv type typeSpec DCOLON methodName tyArgs0 '(' sigArgs0 ')' + { PASM->ResetArgNameList(); + if ($6 == NULL) + { + if((iCallConv)&&(($1 & iCallConv) != iCallConv)) parser->warn("'instance' added to method's calling convention\n"); + $$ = PASM->MakeMemberRef($3, $5, parser->MakeSig($1|iCallConv, $2, $8)); + } + else + { + mdToken mr; + if((iCallConv)&&(($1 & iCallConv) != iCallConv)) parser->warn("'instance' added to method's calling convention\n"); + mr = PASM->MakeMemberRef($3, $5, + parser->MakeSig($1 | IMAGE_CEE_CS_CALLCONV_GENERIC|iCallConv, $2, $8, corCountArgs($6))); + $$ = PASM->MakeMethodSpec(mr, + parser->MakeSig(IMAGE_CEE_CS_CALLCONV_INSTANTIATION, 0, $6)); + } + } + | callConv type typeSpec DCOLON methodName genArityNotEmpty '(' sigArgs0 ')' + { PASM->ResetArgNameList(); + if((iCallConv)&&(($1 & iCallConv) != iCallConv)) parser->warn("'instance' added to method's calling convention\n"); + $$ = PASM->MakeMemberRef($3, $5, + parser->MakeSig($1 | IMAGE_CEE_CS_CALLCONV_GENERIC|iCallConv, $2, $8, $6)); + } + | callConv type methodName tyArgs0 '(' sigArgs0 ')' + { PASM->ResetArgNameList(); + if ($4 == NULL) + { + if((iCallConv)&&(($1 & iCallConv) != iCallConv)) parser->warn("'instance' added to method's calling convention\n"); + $$ = PASM->MakeMemberRef(mdTokenNil, $3, parser->MakeSig($1|iCallConv, $2, $6)); + } + else + { + mdToken mr; + if((iCallConv)&&(($1 & iCallConv) != iCallConv)) parser->warn("'instance' added to method's calling convention\n"); + mr = PASM->MakeMemberRef(mdTokenNil, $3, parser->MakeSig($1 | IMAGE_CEE_CS_CALLCONV_GENERIC|iCallConv, $2, $6, corCountArgs($4))); + $$ = PASM->MakeMethodSpec(mr, + parser->MakeSig(IMAGE_CEE_CS_CALLCONV_INSTANTIATION, 0, $4)); + } + } + | callConv type methodName genArityNotEmpty '(' sigArgs0 ')' + { PASM->ResetArgNameList(); + if((iCallConv)&&(($1 & iCallConv) != iCallConv)) parser->warn("'instance' added to method's calling convention\n"); + $$ = PASM->MakeMemberRef(mdTokenNil, $3, parser->MakeSig($1 | IMAGE_CEE_CS_CALLCONV_GENERIC|iCallConv, $2, $6, $4)); + } + | mdtoken { $$ = $1; } + | TYPEDEF_M { $$ = $1->m_tkTypeSpec; } + | TYPEDEF_MR { $$ = $1->m_tkTypeSpec; } + ; + +callConv : INSTANCE_ callConv { $$ = ($2 | IMAGE_CEE_CS_CALLCONV_HASTHIS); } + | EXPLICIT_ callConv { $$ = ($2 | IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS); } + | callKind { $$ = $1; } + | CALLCONV_ '(' int32 ')' { $$ = $3; } + ; + +callKind : /* EMPTY */ { $$ = IMAGE_CEE_CS_CALLCONV_DEFAULT; } + | DEFAULT_ { $$ = IMAGE_CEE_CS_CALLCONV_DEFAULT; } + | VARARG_ { $$ = IMAGE_CEE_CS_CALLCONV_VARARG; } + | UNMANAGED_ CDECL_ { $$ = IMAGE_CEE_CS_CALLCONV_C; } + | UNMANAGED_ STDCALL_ { $$ = IMAGE_CEE_CS_CALLCONV_STDCALL; } + | UNMANAGED_ THISCALL_ { $$ = IMAGE_CEE_CS_CALLCONV_THISCALL; } + | UNMANAGED_ FASTCALL_ { $$ = IMAGE_CEE_CS_CALLCONV_FASTCALL; } + ; + +mdtoken : MDTOKEN_ '(' int32 ')' { $$ = $3; } + ; + +memberRef : methodSpec methodRef { $$ = $2; + PASM->delArgNameList(PASM->m_firstArgName); + PASM->m_firstArgName = parser->m_ANSFirst.POP(); + PASM->m_lastArgName = parser->m_ANSLast.POP(); + PASM->SetMemberRefFixup($2,iOpcodeLen); } + | FIELD_ type typeSpec DCOLON dottedName + { $2->insertInt8(IMAGE_CEE_CS_CALLCONV_FIELD); + $$ = PASM->MakeMemberRef($3, $5, $2); + PASM->SetMemberRefFixup($$,iOpcodeLen); } + | FIELD_ type dottedName + { $2->insertInt8(IMAGE_CEE_CS_CALLCONV_FIELD); + $$ = PASM->MakeMemberRef(NULL, $3, $2); + PASM->SetMemberRefFixup($$,iOpcodeLen); } + | FIELD_ TYPEDEF_F { $$ = $2->m_tkTypeSpec; + PASM->SetMemberRefFixup($$,iOpcodeLen); } + | FIELD_ TYPEDEF_MR { $$ = $2->m_tkTypeSpec; + PASM->SetMemberRefFixup($$,iOpcodeLen); } + | mdtoken { $$ = $1; + PASM->SetMemberRefFixup($$,iOpcodeLen); } + ; + +/* Event declaration */ +eventHead : _EVENT eventAttr typeSpec dottedName { PASM->ResetEvent($4, $3, $2); } + | _EVENT eventAttr dottedName { PASM->ResetEvent($3, mdTypeRefNil, $2); } + ; + + +eventAttr : /* EMPTY */ { $$ = (CorEventAttr) 0; } + | eventAttr RTSPECIALNAME_ { $$ = $1; }/*{ $$ = (CorEventAttr) ($1 | evRTSpecialName); }*/ + | eventAttr SPECIALNAME_ { $$ = (CorEventAttr) ($1 | evSpecialName); } + ; + +eventDecls : /* EMPTY */ + | eventDecls eventDecl + ; + +eventDecl : _ADDON methodRef { PASM->SetEventMethod(0, $2); } + | _REMOVEON methodRef { PASM->SetEventMethod(1, $2); } + | _FIRE methodRef { PASM->SetEventMethod(2, $2); } + | _OTHER methodRef { PASM->SetEventMethod(3, $2); } + | extSourceSpec + | customAttrDecl + | languageDecl + | compControl + ; + +/* Property declaration */ +propHead : _PROPERTY propAttr callConv type dottedName '(' sigArgs0 ')' initOpt + { PASM->ResetProp($5, + parser->MakeSig((IMAGE_CEE_CS_CALLCONV_PROPERTY | + ($3 & IMAGE_CEE_CS_CALLCONV_HASTHIS)),$4,$7), $2, $9);} + ; + +propAttr : /* EMPTY */ { $$ = (CorPropertyAttr) 0; } + | propAttr RTSPECIALNAME_ { $$ = $1; }/*{ $$ = (CorPropertyAttr) ($1 | prRTSpecialName); }*/ + | propAttr SPECIALNAME_ { $$ = (CorPropertyAttr) ($1 | prSpecialName); } + ; + +propDecls : /* EMPTY */ + | propDecls propDecl + ; + + +propDecl : _SET methodRef { PASM->SetPropMethod(0, $2); } + | _GET methodRef { PASM->SetPropMethod(1, $2); } + | _OTHER methodRef { PASM->SetPropMethod(2, $2); } + | customAttrDecl + | extSourceSpec + | languageDecl + | compControl + ; + +/* Method declaration */ +methodHeadPart1 : _METHOD { PASM->ResetForNextMethod(); + uMethodBeginLine = PASM->m_ulCurLine; + uMethodBeginColumn=PASM->m_ulCurColumn; + } + ; + +marshalClause : /* EMPTY */ { $$ = NULL; } + | MARSHAL_ '(' marshalBlob ')' { $$ = $3; } + ; + +marshalBlob : nativeType { $$ = $1; } + | marshalBlobHead hexbytes '}' { $$ = $2; } + ; + +marshalBlobHead : '{' { bParsingByteArray = TRUE; } + ; + +methodHead : methodHeadPart1 methAttr callConv paramAttr type marshalClause methodName typarsClause'(' sigArgs0 ')' implAttr '{' + { BinStr* sig; + if ($8 == NULL) sig = parser->MakeSig($3, $5, $10); + else { + FixupTyPars($5); + sig = parser->MakeSig($3 | IMAGE_CEE_CS_CALLCONV_GENERIC, $5, $10, $8->Count()); + FixupConstraints(); + } + PASM->StartMethod($7, sig, $2, $6, $4, $8); + TyParFixupList.RESET(false); + PASM->SetImplAttr((USHORT)$12); + PASM->m_pCurMethod->m_ulLines[0] = uMethodBeginLine; + PASM->m_pCurMethod->m_ulColumns[0]=uMethodBeginColumn; + } + ; + +methAttr : /* EMPTY */ { $$ = (CorMethodAttr) 0; } + | methAttr STATIC_ { $$ = (CorMethodAttr) ($1 | mdStatic); } + | methAttr PUBLIC_ { $$ = (CorMethodAttr) (($1 & ~mdMemberAccessMask) | mdPublic); } + | methAttr PRIVATE_ { $$ = (CorMethodAttr) (($1 & ~mdMemberAccessMask) | mdPrivate); } + | methAttr FAMILY_ { $$ = (CorMethodAttr) (($1 & ~mdMemberAccessMask) | mdFamily); } + | methAttr FINAL_ { $$ = (CorMethodAttr) ($1 | mdFinal); } + | methAttr SPECIALNAME_ { $$ = (CorMethodAttr) ($1 | mdSpecialName); } + | methAttr VIRTUAL_ { $$ = (CorMethodAttr) ($1 | mdVirtual); } + | methAttr STRICT_ { $$ = (CorMethodAttr) ($1 | mdCheckAccessOnOverride); } + | methAttr ABSTRACT_ { $$ = (CorMethodAttr) ($1 | mdAbstract); } + | methAttr ASSEMBLY_ { $$ = (CorMethodAttr) (($1 & ~mdMemberAccessMask) | mdAssem); } + | methAttr FAMANDASSEM_ { $$ = (CorMethodAttr) (($1 & ~mdMemberAccessMask) | mdFamANDAssem); } + | methAttr FAMORASSEM_ { $$ = (CorMethodAttr) (($1 & ~mdMemberAccessMask) | mdFamORAssem); } + | methAttr PRIVATESCOPE_ { $$ = (CorMethodAttr) (($1 & ~mdMemberAccessMask) | mdPrivateScope); } + | methAttr HIDEBYSIG_ { $$ = (CorMethodAttr) ($1 | mdHideBySig); } + | methAttr NEWSLOT_ { $$ = (CorMethodAttr) ($1 | mdNewSlot); } + | methAttr RTSPECIALNAME_ { $$ = $1; }/*{ $$ = (CorMethodAttr) ($1 | mdRTSpecialName); }*/ + | methAttr UNMANAGEDEXP_ { $$ = (CorMethodAttr) ($1 | mdUnmanagedExport); } + | methAttr REQSECOBJ_ { $$ = (CorMethodAttr) ($1 | mdRequireSecObject); } + | methAttr FLAGS_ '(' int32 ')' { $$ = (CorMethodAttr) ($4); } + | methAttr PINVOKEIMPL_ '(' compQstring AS_ compQstring pinvAttr ')' + { PASM->SetPinvoke($4,0,$6,$7); + $$ = (CorMethodAttr) ($1 | mdPinvokeImpl); } + | methAttr PINVOKEIMPL_ '(' compQstring pinvAttr ')' + { PASM->SetPinvoke($4,0,NULL,$5); + $$ = (CorMethodAttr) ($1 | mdPinvokeImpl); } + | methAttr PINVOKEIMPL_ '(' pinvAttr ')' + { PASM->SetPinvoke(new BinStr(),0,NULL,$4); + $$ = (CorMethodAttr) ($1 | mdPinvokeImpl); } + ; + +pinvAttr : /* EMPTY */ { $$ = (CorPinvokeMap) 0; } + | pinvAttr NOMANGLE_ { $$ = (CorPinvokeMap) ($1 | pmNoMangle); } + | pinvAttr ANSI_ { $$ = (CorPinvokeMap) ($1 | pmCharSetAnsi); } + | pinvAttr UNICODE_ { $$ = (CorPinvokeMap) ($1 | pmCharSetUnicode); } + | pinvAttr AUTOCHAR_ { $$ = (CorPinvokeMap) ($1 | pmCharSetAuto); } + | pinvAttr LASTERR_ { $$ = (CorPinvokeMap) ($1 | pmSupportsLastError); } + | pinvAttr WINAPI_ { $$ = (CorPinvokeMap) ($1 | pmCallConvWinapi); } + | pinvAttr CDECL_ { $$ = (CorPinvokeMap) ($1 | pmCallConvCdecl); } + | pinvAttr STDCALL_ { $$ = (CorPinvokeMap) ($1 | pmCallConvStdcall); } + | pinvAttr THISCALL_ { $$ = (CorPinvokeMap) ($1 | pmCallConvThiscall); } + | pinvAttr FASTCALL_ { $$ = (CorPinvokeMap) ($1 | pmCallConvFastcall); } + | pinvAttr BESTFIT_ ':' ON_ { $$ = (CorPinvokeMap) ($1 | pmBestFitEnabled); } + | pinvAttr BESTFIT_ ':' OFF_ { $$ = (CorPinvokeMap) ($1 | pmBestFitDisabled); } + | pinvAttr CHARMAPERROR_ ':' ON_ { $$ = (CorPinvokeMap) ($1 | pmThrowOnUnmappableCharEnabled); } + | pinvAttr CHARMAPERROR_ ':' OFF_ { $$ = (CorPinvokeMap) ($1 | pmThrowOnUnmappableCharDisabled); } + | pinvAttr FLAGS_ '(' int32 ')' { $$ = (CorPinvokeMap) ($4); } + ; + +methodName : _CTOR { $$ = newString(COR_CTOR_METHOD_NAME); } + | _CCTOR { $$ = newString(COR_CCTOR_METHOD_NAME); } + | dottedName { $$ = $1; } + ; + +paramAttr : /* EMPTY */ { $$ = 0; } + | paramAttr '[' IN_ ']' { $$ = $1 | pdIn; } + | paramAttr '[' OUT_ ']' { $$ = $1 | pdOut; } + | paramAttr '[' OPT_ ']' { $$ = $1 | pdOptional; } + | paramAttr '[' int32 ']' { $$ = $3 + 1; } + ; + +implAttr : /* EMPTY */ { $$ = (CorMethodImpl) (miIL | miManaged); } + | implAttr NATIVE_ { $$ = (CorMethodImpl) (($1 & 0xFFF4) | miNative); } + | implAttr CIL_ { $$ = (CorMethodImpl) (($1 & 0xFFF4) | miIL); } + | implAttr OPTIL_ { $$ = (CorMethodImpl) (($1 & 0xFFF4) | miOPTIL); } + | implAttr MANAGED_ { $$ = (CorMethodImpl) (($1 & 0xFFFB) | miManaged); } + | implAttr UNMANAGED_ { $$ = (CorMethodImpl) (($1 & 0xFFFB) | miUnmanaged); } + | implAttr FORWARDREF_ { $$ = (CorMethodImpl) ($1 | miForwardRef); } + | implAttr PRESERVESIG_ { $$ = (CorMethodImpl) ($1 | miPreserveSig); } + | implAttr RUNTIME_ { $$ = (CorMethodImpl) ($1 | miRuntime); } + | implAttr INTERNALCALL_ { $$ = (CorMethodImpl) ($1 | miInternalCall); } + | implAttr SYNCHRONIZED_ { $$ = (CorMethodImpl) ($1 | miSynchronized); } + | implAttr NOINLINING_ { $$ = (CorMethodImpl) ($1 | miNoInlining); } + | implAttr AGGRESSIVEINLINING_ { $$ = (CorMethodImpl) ($1 | miAggressiveInlining); } + | implAttr NOOPTIMIZATION_ { $$ = (CorMethodImpl) ($1 | miNoOptimization); } + | implAttr FLAGS_ '(' int32 ')' { $$ = (CorMethodImpl) ($4); } + ; + +localsHead : _LOCALS { PASM->delArgNameList(PASM->m_firstArgName); PASM->m_firstArgName = NULL;PASM->m_lastArgName = NULL; + } + ; + +methodDecls : /* EMPTY */ + | methodDecls methodDecl + ; + +methodDecl : _EMITBYTE int32 { PASM->EmitByte($2); } + | sehBlock { delete PASM->m_SEHD; PASM->m_SEHD = PASM->m_SEHDstack.POP(); } + | _MAXSTACK int32 { PASM->EmitMaxStack($2); } + | localsHead '(' sigArgs0 ')' { PASM->EmitLocals(parser->MakeSig(IMAGE_CEE_CS_CALLCONV_LOCAL_SIG, 0, $3)); + } + | localsHead INIT_ '(' sigArgs0 ')' { PASM->EmitZeroInit(); + PASM->EmitLocals(parser->MakeSig(IMAGE_CEE_CS_CALLCONV_LOCAL_SIG, 0, $4)); + } + | _ENTRYPOINT { PASM->EmitEntryPoint(); } + | _ZEROINIT { PASM->EmitZeroInit(); } + | dataDecl + | instr + | id ':' { PASM->AddLabel(PASM->m_CurPC,$1); /*PASM->EmitLabel($1);*/ } + | secDecl + | extSourceSpec + | languageDecl + | customAttrDecl + | compControl + | _EXPORT '[' int32 ']' { if(PASM->m_pCurMethod->m_dwExportOrdinal == 0xFFFFFFFF) + { + PASM->m_pCurMethod->m_dwExportOrdinal = $3; + PASM->m_pCurMethod->m_szExportAlias = NULL; + if(PASM->m_pCurMethod->m_wVTEntry == 0) PASM->m_pCurMethod->m_wVTEntry = 1; + if(PASM->m_pCurMethod->m_wVTSlot == 0) PASM->m_pCurMethod->m_wVTSlot = $3 + 0x8000; + } + else + PASM->report->warn("Duplicate .export directive, ignored\n"); + } + | _EXPORT '[' int32 ']' AS_ id { if(PASM->m_pCurMethod->m_dwExportOrdinal == 0xFFFFFFFF) + { + PASM->m_pCurMethod->m_dwExportOrdinal = $3; + PASM->m_pCurMethod->m_szExportAlias = $6; + if(PASM->m_pCurMethod->m_wVTEntry == 0) PASM->m_pCurMethod->m_wVTEntry = 1; + if(PASM->m_pCurMethod->m_wVTSlot == 0) PASM->m_pCurMethod->m_wVTSlot = $3 + 0x8000; + } + else + PASM->report->warn("Duplicate .export directive, ignored\n"); + } + | _VTENTRY int32 ':' int32 { PASM->m_pCurMethod->m_wVTEntry = (WORD)$2; + PASM->m_pCurMethod->m_wVTSlot = (WORD)$4; } + | _OVERRIDE typeSpec DCOLON methodName + { PASM->AddMethodImpl($2,$4,NULL,NULL,NULL,NULL); } + + | _OVERRIDE METHOD_ callConv type typeSpec DCOLON methodName genArity '(' sigArgs0 ')' + { PASM->AddMethodImpl($5,$7, + ($8==0 ? parser->MakeSig($3,$4,$10) : + parser->MakeSig($3| IMAGE_CEE_CS_CALLCONV_GENERIC,$4,$10,$8)) + ,NULL,NULL,NULL); + PASM->ResetArgNameList(); + } + | scopeBlock + | PARAM_ TYPE_ '[' int32 ']' { if(($4 > 0) && ($4 <= (int)PASM->m_pCurMethod->m_NumTyPars)) + PASM->m_pCustomDescrList = PASM->m_pCurMethod->m_TyPars[$4-1].CAList(); + else + PASM->report->error("Type parameter index out of range\n"); + } + | PARAM_ TYPE_ dottedName { int n = PASM->m_pCurMethod->FindTyPar($3); + if(n >= 0) + PASM->m_pCustomDescrList = PASM->m_pCurMethod->m_TyPars[n].CAList(); + else + PASM->report->error("Type parameter '%s' undefined\n",$3); + } + | PARAM_ '[' int32 ']' initOpt + { if( $3 ) { + ARG_NAME_LIST* pAN=PASM->findArg(PASM->m_pCurMethod->m_firstArgName, $3 - 1); + if(pAN) + { + PASM->m_pCustomDescrList = &(pAN->CustDList); + pAN->pValue = $5; + } + else + { + PASM->m_pCustomDescrList = NULL; + if($5) delete $5; + } + } else { + PASM->m_pCustomDescrList = &(PASM->m_pCurMethod->m_RetCustDList); + PASM->m_pCurMethod->m_pRetValue = $5; + } + PASM->m_tkCurrentCVOwner = 0; + } + ; + +scopeBlock : scopeOpen methodDecls '}' { PASM->m_pCurMethod->CloseScope(); } + ; + +scopeOpen : '{' { PASM->m_pCurMethod->OpenScope(); } + ; + +/* Structured exception handling directives */ +sehBlock : tryBlock sehClauses + ; + +sehClauses : sehClause sehClauses + | sehClause + ; + +tryBlock : tryHead scopeBlock { PASM->m_SEHD->tryTo = PASM->m_CurPC; } + | tryHead id TO_ id { PASM->SetTryLabels($2, $4); } + | tryHead int32 TO_ int32 { if(PASM->m_SEHD) {PASM->m_SEHD->tryFrom = $2; + PASM->m_SEHD->tryTo = $4;} } + ; + +tryHead : _TRY { PASM->NewSEHDescriptor(); + PASM->m_SEHD->tryFrom = PASM->m_CurPC; } + ; + + +sehClause : catchClause handlerBlock { PASM->EmitTry(); } + | filterClause handlerBlock { PASM->EmitTry(); } + | finallyClause handlerBlock { PASM->EmitTry(); } + | faultClause handlerBlock { PASM->EmitTry(); } + ; + + +filterClause : filterHead scopeBlock { PASM->m_SEHD->sehHandler = PASM->m_CurPC; } + | filterHead id { PASM->SetFilterLabel($2); + PASM->m_SEHD->sehHandler = PASM->m_CurPC; } + | filterHead int32 { PASM->m_SEHD->sehFilter = $2; + PASM->m_SEHD->sehHandler = PASM->m_CurPC; } + ; + +filterHead : FILTER_ { PASM->m_SEHD->sehClause = COR_ILEXCEPTION_CLAUSE_FILTER; + PASM->m_SEHD->sehFilter = PASM->m_CurPC; } + ; + +catchClause : CATCH_ typeSpec { PASM->m_SEHD->sehClause = COR_ILEXCEPTION_CLAUSE_NONE; + PASM->SetCatchClass($2); + PASM->m_SEHD->sehHandler = PASM->m_CurPC; } + ; + +finallyClause : FINALLY_ { PASM->m_SEHD->sehClause = COR_ILEXCEPTION_CLAUSE_FINALLY; + PASM->m_SEHD->sehHandler = PASM->m_CurPC; } + ; + +faultClause : FAULT_ { PASM->m_SEHD->sehClause = COR_ILEXCEPTION_CLAUSE_FAULT; + PASM->m_SEHD->sehHandler = PASM->m_CurPC; } + ; + +handlerBlock : scopeBlock { PASM->m_SEHD->sehHandlerTo = PASM->m_CurPC; } + | HANDLER_ id TO_ id { PASM->SetHandlerLabels($2, $4); } + | HANDLER_ int32 TO_ int32 { PASM->m_SEHD->sehHandler = $2; + PASM->m_SEHD->sehHandlerTo = $4; } + ; + +/* Data declaration */ +dataDecl : ddHead ddBody + ; + +ddHead : _DATA tls id '=' { PASM->EmitDataLabel($3); } + | _DATA tls + ; + +tls : /* EMPTY */ { PASM->SetDataSection(); } + | TLS_ { PASM->SetTLSSection(); } + | CIL_ { PASM->SetILSection(); } + ; + +ddBody : '{' ddItemList '}' + | ddItem + ; + +ddItemList : ddItem ',' ddItemList + | ddItem + ; + +ddItemCount : /* EMPTY */ { $$ = 1; } + | '[' int32 ']' { $$ = $2; + if($2 <= 0) { PASM->report->error("Illegal item count: %d\n",$2); + if(!PASM->OnErrGo) $$ = 1; }} + ; + +ddItem : CHAR_ '*' '(' compQstring ')' { PASM->EmitDataString($4); } + | '&' '(' id ')' { PASM->EmitDD($3); } + | bytearrayhead bytes ')' { PASM->EmitData($2->ptr(),$2->length()); } + | FLOAT32_ '(' float64 ')' ddItemCount + { float f = (float) (*$3); float* p = new (nothrow) float[$5]; + if(p != NULL) { + for(int i=0; i < $5; i++) p[i] = f; + PASM->EmitData(p, sizeof(float)*$5); delete $3; delete [] p; + } else PASM->report->error("Out of memory emitting data block %d bytes\n", + sizeof(float)*$5); } + | FLOAT64_ '(' float64 ')' ddItemCount + { double* p = new (nothrow) double[$5]; + if(p != NULL) { + for(int i=0; i<$5; i++) p[i] = *($3); + PASM->EmitData(p, sizeof(double)*$5); delete $3; delete [] p; + } else PASM->report->error("Out of memory emitting data block %d bytes\n", + sizeof(double)*$5); } + | INT64_ '(' int64 ')' ddItemCount + { __int64* p = new (nothrow) __int64[$5]; + if(p != NULL) { + for(int i=0; i<$5; i++) p[i] = *($3); + PASM->EmitData(p, sizeof(__int64)*$5); delete $3; delete [] p; + } else PASM->report->error("Out of memory emitting data block %d bytes\n", + sizeof(__int64)*$5); } + | INT32_ '(' int32 ')' ddItemCount + { __int32* p = new (nothrow) __int32[$5]; + if(p != NULL) { + for(int i=0; i<$5; i++) p[i] = $3; + PASM->EmitData(p, sizeof(__int32)*$5); delete [] p; + } else PASM->report->error("Out of memory emitting data block %d bytes\n", + sizeof(__int32)*$5); } + | INT16_ '(' int32 ')' ddItemCount + { __int16 i = (__int16) $3; FAIL_UNLESS(i == $3, ("Value %d too big\n", $3)); + __int16* p = new (nothrow) __int16[$5]; + if(p != NULL) { + for(int j=0; j<$5; j++) p[j] = i; + PASM->EmitData(p, sizeof(__int16)*$5); delete [] p; + } else PASM->report->error("Out of memory emitting data block %d bytes\n", + sizeof(__int16)*$5); } + | INT8_ '(' int32 ')' ddItemCount + { __int8 i = (__int8) $3; FAIL_UNLESS(i == $3, ("Value %d too big\n", $3)); + __int8* p = new (nothrow) __int8[$5]; + if(p != NULL) { + for(int j=0; j<$5; j++) p[j] = i; + PASM->EmitData(p, sizeof(__int8)*$5); delete [] p; + } else PASM->report->error("Out of memory emitting data block %d bytes\n", + sizeof(__int8)*$5); } + | FLOAT32_ ddItemCount { PASM->EmitData(NULL, sizeof(float)*$2); } + | FLOAT64_ ddItemCount { PASM->EmitData(NULL, sizeof(double)*$2); } + | INT64_ ddItemCount { PASM->EmitData(NULL, sizeof(__int64)*$2); } + | INT32_ ddItemCount { PASM->EmitData(NULL, sizeof(__int32)*$2); } + | INT16_ ddItemCount { PASM->EmitData(NULL, sizeof(__int16)*$2); } + | INT8_ ddItemCount { PASM->EmitData(NULL, sizeof(__int8)*$2); } + ; + +/* Default values declaration for fields, parameters and verbal form of CA blob description */ +fieldSerInit : FLOAT32_ '(' float64 ')' { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_R4); + float f = (float)(*$3); + $$->appendInt32(*((__int32*)&f)); delete $3; } + | FLOAT64_ '(' float64 ')' { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_R8); + $$->appendInt64((__int64 *)$3); delete $3; } + | FLOAT32_ '(' int32 ')' { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_R4); + $$->appendInt32($3); } + | FLOAT64_ '(' int64 ')' { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_R8); + $$->appendInt64((__int64 *)$3); delete $3; } + | INT64_ '(' int64 ')' { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_I8); + $$->appendInt64((__int64 *)$3); delete $3; } + | INT32_ '(' int32 ')' { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_I4); + $$->appendInt32($3); } + | INT16_ '(' int32 ')' { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_I2); + $$->appendInt16($3); } + | INT8_ '(' int32 ')' { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_I1); + $$->appendInt8($3); } + | UNSIGNED_ INT64_ '(' int64 ')' { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_U8); + $$->appendInt64((__int64 *)$4); delete $4; } + | UNSIGNED_ INT32_ '(' int32 ')' { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_U4); + $$->appendInt32($4); } + | UNSIGNED_ INT16_ '(' int32 ')' { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_U2); + $$->appendInt16($4); } + | UNSIGNED_ INT8_ '(' int32 ')' { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_U1); + $$->appendInt8($4); } + | UINT64_ '(' int64 ')' { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_U8); + $$->appendInt64((__int64 *)$3); delete $3; } + | UINT32_ '(' int32 ')' { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_U4); + $$->appendInt32($3); } + | UINT16_ '(' int32 ')' { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_U2); + $$->appendInt16($3); } + | UINT8_ '(' int32 ')' { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_U1); + $$->appendInt8($3); } + | CHAR_ '(' int32 ')' { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_CHAR); + $$->appendInt16($3); } + | BOOL_ '(' truefalse ')' { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_BOOLEAN); + $$->appendInt8($3);} + | bytearrayhead bytes ')' { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_STRING); + $$->append($2); delete $2;} + ; + +bytearrayhead : BYTEARRAY_ '(' { bParsingByteArray = TRUE; } + ; + +bytes : /* EMPTY */ { $$ = new BinStr(); } + | hexbytes { $$ = $1; } + ; + +hexbytes : HEXBYTE { __int8 i = (__int8) $1; $$ = new BinStr(); $$->appendInt8(i); } + | hexbytes HEXBYTE { __int8 i = (__int8) $2; $$ = $1; $$->appendInt8(i); } + ; + +/* Field/parameter initialization */ +fieldInit : fieldSerInit { $$ = $1; } + | compQstring { $$ = BinStrToUnicode($1,true); $$->insertInt8(ELEMENT_TYPE_STRING);} + | NULLREF_ { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_CLASS); + $$->appendInt32(0); } + ; + +/* Values for verbal form of CA blob description */ +serInit : fieldSerInit { $$ = $1; } + | STRING_ '(' NULLREF_ ')' { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_STRING); $$->appendInt8(0xFF); } + | STRING_ '(' SQSTRING ')' { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_STRING); + AppendStringWithLength($$,$3); delete [] $3;} + | TYPE_ '(' CLASS_ SQSTRING ')' { $$ = new BinStr(); $$->appendInt8(SERIALIZATION_TYPE_TYPE); + AppendStringWithLength($$,$4); delete [] $4;} + | TYPE_ '(' className ')' { $$ = new BinStr(); $$->appendInt8(SERIALIZATION_TYPE_TYPE); + AppendStringWithLength($$,PASM->ReflectionNotation($3));} + | TYPE_ '(' NULLREF_ ')' { $$ = new BinStr(); $$->appendInt8(SERIALIZATION_TYPE_TYPE); $$->appendInt8(0xFF); } + | OBJECT_ '(' serInit ')' { $$ = $3; $$->insertInt8(SERIALIZATION_TYPE_TAGGED_OBJECT);} + | FLOAT32_ '[' int32 ']' '(' f32seq ')' + { $$ = $6; $$->insertInt32($3); + $$->insertInt8(ELEMENT_TYPE_R4); + $$->insertInt8(ELEMENT_TYPE_SZARRAY); } + | FLOAT64_ '[' int32 ']' '(' f64seq ')' + { $$ = $6; $$->insertInt32($3); + $$->insertInt8(ELEMENT_TYPE_R8); + $$->insertInt8(ELEMENT_TYPE_SZARRAY); } + | INT64_ '[' int32 ']' '(' i64seq ')' + { $$ = $6; $$->insertInt32($3); + $$->insertInt8(ELEMENT_TYPE_I8); + $$->insertInt8(ELEMENT_TYPE_SZARRAY); } + | INT32_ '[' int32 ']' '(' i32seq ')' + { $$ = $6; $$->insertInt32($3); + $$->insertInt8(ELEMENT_TYPE_I4); + $$->insertInt8(ELEMENT_TYPE_SZARRAY); } + | INT16_ '[' int32 ']' '(' i16seq ')' + { $$ = $6; $$->insertInt32($3); + $$->insertInt8(ELEMENT_TYPE_I2); + $$->insertInt8(ELEMENT_TYPE_SZARRAY); } + | INT8_ '[' int32 ']' '(' i8seq ')' + { $$ = $6; $$->insertInt32($3); + $$->insertInt8(ELEMENT_TYPE_I1); + $$->insertInt8(ELEMENT_TYPE_SZARRAY); } + | UINT64_ '[' int32 ']' '(' i64seq ')' + { $$ = $6; $$->insertInt32($3); + $$->insertInt8(ELEMENT_TYPE_U8); + $$->insertInt8(ELEMENT_TYPE_SZARRAY); } + | UINT32_ '[' int32 ']' '(' i32seq ')' + { $$ = $6; $$->insertInt32($3); + $$->insertInt8(ELEMENT_TYPE_U4); + $$->insertInt8(ELEMENT_TYPE_SZARRAY); } + | UINT16_ '[' int32 ']' '(' i16seq ')' + { $$ = $6; $$->insertInt32($3); + $$->insertInt8(ELEMENT_TYPE_U2); + $$->insertInt8(ELEMENT_TYPE_SZARRAY); } + | UINT8_ '[' int32 ']' '(' i8seq ')' + { $$ = $6; $$->insertInt32($3); + $$->insertInt8(ELEMENT_TYPE_U1); + $$->insertInt8(ELEMENT_TYPE_SZARRAY); } + | UNSIGNED_ INT64_ '[' int32 ']' '(' i64seq ')' + { $$ = $7; $$->insertInt32($4); + $$->insertInt8(ELEMENT_TYPE_U8); + $$->insertInt8(ELEMENT_TYPE_SZARRAY); } + | UNSIGNED_ INT32_ '[' int32 ']' '(' i32seq ')' + { $$ = $7; $$->insertInt32($4); + $$->insertInt8(ELEMENT_TYPE_U4); + $$->insertInt8(ELEMENT_TYPE_SZARRAY); } + | UNSIGNED_ INT16_ '[' int32 ']' '(' i16seq ')' + { $$ = $7; $$->insertInt32($4); + $$->insertInt8(ELEMENT_TYPE_U2); + $$->insertInt8(ELEMENT_TYPE_SZARRAY); } + | UNSIGNED_ INT8_ '[' int32 ']' '(' i8seq ')' + { $$ = $7; $$->insertInt32($4); + $$->insertInt8(ELEMENT_TYPE_U1); + $$->insertInt8(ELEMENT_TYPE_SZARRAY); } + | CHAR_ '[' int32 ']' '(' i16seq ')' + { $$ = $6; $$->insertInt32($3); + $$->insertInt8(ELEMENT_TYPE_CHAR); + $$->insertInt8(ELEMENT_TYPE_SZARRAY); } + | BOOL_ '[' int32 ']' '(' boolSeq ')' + { $$ = $6; $$->insertInt32($3); + $$->insertInt8(ELEMENT_TYPE_BOOLEAN); + $$->insertInt8(ELEMENT_TYPE_SZARRAY); } + | STRING_ '[' int32 ']' '(' sqstringSeq ')' + { $$ = $6; $$->insertInt32($3); + $$->insertInt8(ELEMENT_TYPE_STRING); + $$->insertInt8(ELEMENT_TYPE_SZARRAY); } + | TYPE_ '[' int32 ']' '(' classSeq ')' + { $$ = $6; $$->insertInt32($3); + $$->insertInt8(SERIALIZATION_TYPE_TYPE); + $$->insertInt8(ELEMENT_TYPE_SZARRAY); } + | OBJECT_ '[' int32 ']' '(' objSeq ')' + { $$ = $6; $$->insertInt32($3); + $$->insertInt8(SERIALIZATION_TYPE_TAGGED_OBJECT); + $$->insertInt8(ELEMENT_TYPE_SZARRAY); } + ; + + +f32seq : /* EMPTY */ { $$ = new BinStr(); } + | f32seq float64 { $$ = $1; + float f = (float) (*$2); $$->appendInt32(*((__int32*)&f)); delete $2; } + | f32seq int32 { $$ = $1; + $$->appendInt32($2); } + ; + +f64seq : /* EMPTY */ { $$ = new BinStr(); } + | f64seq float64 { $$ = $1; + $$->appendInt64((__int64 *)$2); delete $2; } + | f64seq int64 { $$ = $1; + $$->appendInt64((__int64 *)$2); delete $2; } + ; + +i64seq : /* EMPTY */ { $$ = new BinStr(); } + | i64seq int64 { $$ = $1; + $$->appendInt64((__int64 *)$2); delete $2; } + ; + +i32seq : /* EMPTY */ { $$ = new BinStr(); } + | i32seq int32 { $$ = $1; $$->appendInt32($2);} + ; + +i16seq : /* EMPTY */ { $$ = new BinStr(); } + | i16seq int32 { $$ = $1; $$->appendInt16($2);} + ; + +i8seq : /* EMPTY */ { $$ = new BinStr(); } + | i8seq int32 { $$ = $1; $$->appendInt8($2); } + ; + +boolSeq : /* EMPTY */ { $$ = new BinStr(); } + | boolSeq truefalse { $$ = $1; + $$->appendInt8($2);} + ; + +sqstringSeq : /* EMPTY */ { $$ = new BinStr(); } + | sqstringSeq NULLREF_ { $$ = $1; $$->appendInt8(0xFF); } + | sqstringSeq SQSTRING { $$ = $1; + AppendStringWithLength($$,$2); delete [] $2;} + ; + +classSeq : /* EMPTY */ { $$ = new BinStr(); } + | classSeq NULLREF_ { $$ = $1; $$->appendInt8(0xFF); } + | classSeq CLASS_ SQSTRING { $$ = $1; + AppendStringWithLength($$,$3); delete [] $3;} + | classSeq className { $$ = $1; + AppendStringWithLength($$,PASM->ReflectionNotation($2));} + ; + +objSeq : /* EMPTY */ { $$ = new BinStr(); } + | objSeq serInit { $$ = $1; $$->append($2); delete $2; } + ; + +/* IL instructions and associated definitions */ +methodSpec : METHOD_ { parser->m_ANSFirst.PUSH(PASM->m_firstArgName); + parser->m_ANSLast.PUSH(PASM->m_lastArgName); + PASM->m_firstArgName = NULL; + PASM->m_lastArgName = NULL; } + ; + +instr_none : INSTR_NONE { $$ = SetupInstr($1); } + ; + +instr_var : INSTR_VAR { $$ = SetupInstr($1); } + ; + +instr_i : INSTR_I { $$ = SetupInstr($1); } + ; + +instr_i8 : INSTR_I8 { $$ = SetupInstr($1); } + ; + +instr_r : INSTR_R { $$ = SetupInstr($1); } + ; + +instr_brtarget : INSTR_BRTARGET { $$ = SetupInstr($1); } + ; + +instr_method : INSTR_METHOD { $$ = SetupInstr($1); + if((!PASM->OnErrGo)&& + (($1 == CEE_NEWOBJ)|| + ($1 == CEE_CALLVIRT))) + iCallConv = IMAGE_CEE_CS_CALLCONV_HASTHIS; + } + ; + +instr_field : INSTR_FIELD { $$ = SetupInstr($1); } + ; + +instr_type : INSTR_TYPE { $$ = SetupInstr($1); } + ; + +instr_string : INSTR_STRING { $$ = SetupInstr($1); } + ; + +instr_sig : INSTR_SIG { $$ = SetupInstr($1); } + ; + +instr_tok : INSTR_TOK { $$ = SetupInstr($1); iOpcodeLen = PASM->OpcodeLen($$); } + ; + +instr_switch : INSTR_SWITCH { $$ = SetupInstr($1); } + ; + +instr_r_head : instr_r '(' { $$ = $1; bParsingByteArray = TRUE; } + ; + + +instr : instr_none { PASM->EmitOpcode($1); } + | instr_var int32 { PASM->EmitInstrVar($1, $2); } + | instr_var id { PASM->EmitInstrVarByName($1, $2); } + | instr_i int32 { PASM->EmitInstrI($1, $2); } + | instr_i8 int64 { PASM->EmitInstrI8($1, $2); } + | instr_r float64 { PASM->EmitInstrR($1, $2); delete ($2);} + | instr_r int64 { double f = (double) (*$2); PASM->EmitInstrR($1, &f); } + | instr_r_head bytes ')' { unsigned L = $2->length(); + FAIL_UNLESS(L >= sizeof(float), ("%d hexbytes, must be at least %d\n", + L,sizeof(float))); + if(L < sizeof(float)) {YYERROR; } + else { + double f = (L >= sizeof(double)) ? *((double *)($2->ptr())) + : (double)(*(float *)($2->ptr())); + PASM->EmitInstrR($1,&f); } + delete $2; } + | instr_brtarget int32 { PASM->EmitInstrBrOffset($1, $2); } + | instr_brtarget id { PASM->EmitInstrBrTarget($1, $2); } + | instr_method methodRef + { PASM->SetMemberRefFixup($2,PASM->OpcodeLen($1)); + PASM->EmitInstrI($1,$2); + PASM->m_tkCurrentCVOwner = $2; + PASM->m_pCustomDescrList = NULL; + iCallConv = 0; + } + | instr_field type typeSpec DCOLON dottedName + { $2->insertInt8(IMAGE_CEE_CS_CALLCONV_FIELD); + mdToken mr = PASM->MakeMemberRef($3, $5, $2); + PASM->SetMemberRefFixup(mr, PASM->OpcodeLen($1)); + PASM->EmitInstrI($1,mr); + PASM->m_tkCurrentCVOwner = mr; + PASM->m_pCustomDescrList = NULL; + } + | instr_field type dottedName + { $2->insertInt8(IMAGE_CEE_CS_CALLCONV_FIELD); + mdToken mr = PASM->MakeMemberRef(mdTokenNil, $3, $2); + PASM->SetMemberRefFixup(mr, PASM->OpcodeLen($1)); + PASM->EmitInstrI($1,mr); + PASM->m_tkCurrentCVOwner = mr; + PASM->m_pCustomDescrList = NULL; + } + | instr_field mdtoken { mdToken mr = $2; + PASM->SetMemberRefFixup(mr, PASM->OpcodeLen($1)); + PASM->EmitInstrI($1,mr); + PASM->m_tkCurrentCVOwner = mr; + PASM->m_pCustomDescrList = NULL; + } + | instr_field TYPEDEF_F { mdToken mr = $2->m_tkTypeSpec; + PASM->SetMemberRefFixup(mr, PASM->OpcodeLen($1)); + PASM->EmitInstrI($1,mr); + PASM->m_tkCurrentCVOwner = mr; + PASM->m_pCustomDescrList = NULL; + } + | instr_field TYPEDEF_MR { mdToken mr = $2->m_tkTypeSpec; + PASM->SetMemberRefFixup(mr, PASM->OpcodeLen($1)); + PASM->EmitInstrI($1,mr); + PASM->m_tkCurrentCVOwner = mr; + PASM->m_pCustomDescrList = NULL; + } + | instr_type typeSpec { PASM->EmitInstrI($1, $2); + PASM->m_tkCurrentCVOwner = $2; + PASM->m_pCustomDescrList = NULL; + } + | instr_string compQstring { PASM->EmitInstrStringLiteral($1, $2,TRUE); } + | instr_string ANSI_ '(' compQstring ')' + { PASM->EmitInstrStringLiteral($1, $4,FALSE); } + | instr_string bytearrayhead bytes ')' + { PASM->EmitInstrStringLiteral($1, $3,FALSE,TRUE); } + | instr_sig callConv type '(' sigArgs0 ')' + { PASM->EmitInstrSig($1, parser->MakeSig($2, $3, $5)); + PASM->ResetArgNameList(); + } + | instr_tok ownerType /* ownerType ::= memberRef | typeSpec */ + { PASM->EmitInstrI($1,$2); + PASM->m_tkCurrentCVOwner = $2; + PASM->m_pCustomDescrList = NULL; + iOpcodeLen = 0; + } + | instr_switch '(' labels ')' { PASM->EmitInstrSwitch($1, $3); } + ; + +labels : /* empty */ { $$ = 0; } + | id ',' labels { $$ = new Labels($1, $3, TRUE); } + | int32 ',' labels { $$ = new Labels((char *)(UINT_PTR)$1, $3, FALSE); } + | id { $$ = new Labels($1, NULL, TRUE); } + | int32 { $$ = new Labels((char *)(UINT_PTR)$1, NULL, FALSE); } + ; + +/* Signatures */ +tyArgs0 : /* EMPTY */ { $$ = NULL; } + | '<' tyArgs1 '>' { $$ = $2; } + ; + +tyArgs1 : /* EMPTY */ { $$ = NULL; } + | tyArgs2 { $$ = $1; } + ; + +tyArgs2 : type { $$ = $1; } + | tyArgs2 ',' type { $$ = $1; $$->append($3); delete $3; } + ; + + +sigArgs0 : /* EMPTY */ { $$ = new BinStr(); } + | sigArgs1 { $$ = $1;} + ; + +sigArgs1 : sigArg { $$ = $1; } + | sigArgs1 ',' sigArg { $$ = $1; $$->append($3); delete $3; } + ; + +sigArg : ELIPSIS { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_SENTINEL); } + | paramAttr type marshalClause { $$ = new BinStr(); $$->append($2); PASM->addArgName(NULL, $2, $3, $1); } + | paramAttr type marshalClause id { $$ = new BinStr(); $$->append($2); PASM->addArgName($4, $2, $3, $1);} + ; + +/* Class referencing */ +className : '[' dottedName ']' slashedName { $$ = PASM->ResolveClassRef(PASM->GetAsmRef($2), $4, NULL); delete[] $2;} + | '[' mdtoken ']' slashedName { $$ = PASM->ResolveClassRef($2, $4, NULL); } + | '[' '*' ']' slashedName { $$ = PASM->ResolveClassRef(mdTokenNil, $4, NULL); } + | '[' _MODULE dottedName ']' slashedName { $$ = PASM->ResolveClassRef(PASM->GetModRef($3),$5, NULL); delete[] $3;} + | slashedName { $$ = PASM->ResolveClassRef(1,$1,NULL); } + | mdtoken { $$ = $1; } + | TYPEDEF_T { $$ = $1->m_tkTypeSpec; } + | _THIS { if(PASM->m_pCurClass != NULL) $$ = PASM->m_pCurClass->m_cl; + else { $$ = 0; PASM->report->error(".this outside class scope\n"); } + } + | _BASE { if(PASM->m_pCurClass != NULL) { + $$ = PASM->m_pCurClass->m_crExtends; + if(RidFromToken($$) == 0) + PASM->report->error(".base undefined\n"); + } else { $$ = 0; PASM->report->error(".base outside class scope\n"); } + } + | _NESTER { if(PASM->m_pCurClass != NULL) { + if(PASM->m_pCurClass->m_pEncloser != NULL) $$ = PASM->m_pCurClass->m_pEncloser->m_cl; + else { $$ = 0; PASM->report->error(".nester undefined\n"); } + } else { $$ = 0; PASM->report->error(".nester outside class scope\n"); } + } + ; + +slashedName : dottedName { $$ = $1; } + | slashedName '/' dottedName { $$ = newStringWDel($1, NESTING_SEP, $3); } + ; + +typeSpec : className { $$ = $1;} + | '[' dottedName ']' { $$ = PASM->GetAsmRef($2); delete[] $2;} + | '[' _MODULE dottedName ']' { $$ = PASM->GetModRef($3); delete[] $3;} + | type { $$ = PASM->ResolveTypeSpec($1); } + ; + +/* Native types for marshaling signatures */ +nativeType : /* EMPTY */ { $$ = new BinStr(); } + | CUSTOM_ '(' compQstring ',' compQstring ',' compQstring ',' compQstring ')' + { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_CUSTOMMARSHALER); + corEmitInt($$,$3->length()); $$->append($3); + corEmitInt($$,$5->length()); $$->append($5); + corEmitInt($$,$7->length()); $$->append($7); + corEmitInt($$,$9->length()); $$->append($9); + PASM->report->warn("Deprecated 4-string form of custom marshaler, first two strings ignored\n");} + | CUSTOM_ '(' compQstring ',' compQstring ')' + { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_CUSTOMMARSHALER); + corEmitInt($$,0); + corEmitInt($$,0); + corEmitInt($$,$3->length()); $$->append($3); + corEmitInt($$,$5->length()); $$->append($5); } + | FIXED_ SYSSTRING_ '[' int32 ']' { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_FIXEDSYSSTRING); + corEmitInt($$,$4); } + | FIXED_ ARRAY_ '[' int32 ']' nativeType + { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_FIXEDARRAY); + corEmitInt($$,$4); $$->append($6); } + | VARIANT_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_VARIANT); + PASM->report->warn("Deprecated native type 'variant'\n"); } + | CURRENCY_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_CURRENCY); } + | SYSCHAR_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_SYSCHAR); + PASM->report->warn("Deprecated native type 'syschar'\n"); } + | VOID_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_VOID); + PASM->report->warn("Deprecated native type 'void'\n"); } + | BOOL_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_BOOLEAN); } + | INT8_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_I1); } + | INT16_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_I2); } + | INT32_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_I4); } + | INT64_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_I8); } + | FLOAT32_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_R4); } + | FLOAT64_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_R8); } + | ERROR_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_ERROR); } + | UNSIGNED_ INT8_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_U1); } + | UNSIGNED_ INT16_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_U2); } + | UNSIGNED_ INT32_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_U4); } + | UNSIGNED_ INT64_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_U8); } + | UINT8_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_U1); } + | UINT16_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_U2); } + | UINT32_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_U4); } + | UINT64_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_U8); } + | nativeType '*' { $$ = $1; $$->insertInt8(NATIVE_TYPE_PTR); + PASM->report->warn("Deprecated native type '*'\n"); } + | nativeType '[' ']' { $$ = $1; if($$->length()==0) $$->appendInt8(NATIVE_TYPE_MAX); + $$->insertInt8(NATIVE_TYPE_ARRAY); } + | nativeType '[' int32 ']' { $$ = $1; if($$->length()==0) $$->appendInt8(NATIVE_TYPE_MAX); + $$->insertInt8(NATIVE_TYPE_ARRAY); + corEmitInt($$,0); + corEmitInt($$,$3); + corEmitInt($$,0); } + | nativeType '[' int32 '+' int32 ']' { $$ = $1; if($$->length()==0) $$->appendInt8(NATIVE_TYPE_MAX); + $$->insertInt8(NATIVE_TYPE_ARRAY); + corEmitInt($$,$5); + corEmitInt($$,$3); + corEmitInt($$,ntaSizeParamIndexSpecified); } + | nativeType '[' '+' int32 ']' { $$ = $1; if($$->length()==0) $$->appendInt8(NATIVE_TYPE_MAX); + $$->insertInt8(NATIVE_TYPE_ARRAY); + corEmitInt($$,$4); } + | DECIMAL_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_DECIMAL); + PASM->report->warn("Deprecated native type 'decimal'\n"); } + | DATE_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_DATE); + PASM->report->warn("Deprecated native type 'date'\n"); } + | BSTR_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_BSTR); } + | LPSTR_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_LPSTR); } + | LPWSTR_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_LPWSTR); } + | LPTSTR_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_LPTSTR); } + | OBJECTREF_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_OBJECTREF); + PASM->report->warn("Deprecated native type 'objectref'\n"); } + | IUNKNOWN_ iidParamIndex { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_IUNKNOWN); + if($2 != -1) corEmitInt($$,$2); } + | IDISPATCH_ iidParamIndex { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_IDISPATCH); + if($2 != -1) corEmitInt($$,$2); } + | STRUCT_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_STRUCT); } + | INTERFACE_ iidParamIndex { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_INTF); + if($2 != -1) corEmitInt($$,$2); } + | SAFEARRAY_ variantType { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_SAFEARRAY); + corEmitInt($$,$2); + corEmitInt($$,0);} + | SAFEARRAY_ variantType ',' compQstring { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_SAFEARRAY); + corEmitInt($$,$2); + corEmitInt($$,$4->length()); $$->append($4); } + + | INT_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_INT); } + | UNSIGNED_ INT_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_UINT); } + | UINT_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_UINT); } + | NESTED_ STRUCT_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_NESTEDSTRUCT); + PASM->report->warn("Deprecated native type 'nested struct'\n"); } + | BYVALSTR_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_BYVALSTR); } + | ANSI_ BSTR_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_ANSIBSTR); } + | TBSTR_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_TBSTR); } + | VARIANT_ BOOL_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_VARIANTBOOL); } + | METHOD_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_FUNC); } + | AS_ ANY_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_ASANY); } + | LPSTRUCT_ { $$ = new BinStr(); $$->appendInt8(NATIVE_TYPE_LPSTRUCT); } + | TYPEDEF_TS { $$ = new BinStr(); $$->append($1->m_pbsTypeSpec); } + ; + +iidParamIndex : /* EMPTY */ { $$ = -1; } + | '(' IIDPARAM_ '=' int32 ')' { $$ = $4; } + ; + +variantType : /* EMPTY */ { $$ = VT_EMPTY; } + | NULL_ { $$ = VT_NULL; } + | VARIANT_ { $$ = VT_VARIANT; } + | CURRENCY_ { $$ = VT_CY; } + | VOID_ { $$ = VT_VOID; } + | BOOL_ { $$ = VT_BOOL; } + | INT8_ { $$ = VT_I1; } + | INT16_ { $$ = VT_I2; } + | INT32_ { $$ = VT_I4; } + | INT64_ { $$ = VT_I8; } + | FLOAT32_ { $$ = VT_R4; } + | FLOAT64_ { $$ = VT_R8; } + | UNSIGNED_ INT8_ { $$ = VT_UI1; } + | UNSIGNED_ INT16_ { $$ = VT_UI2; } + | UNSIGNED_ INT32_ { $$ = VT_UI4; } + | UNSIGNED_ INT64_ { $$ = VT_UI8; } + | UINT8_ { $$ = VT_UI1; } + | UINT16_ { $$ = VT_UI2; } + | UINT32_ { $$ = VT_UI4; } + | UINT64_ { $$ = VT_UI8; } + | '*' { $$ = VT_PTR; } + | variantType '[' ']' { $$ = $1 | VT_ARRAY; } + | variantType VECTOR_ { $$ = $1 | VT_VECTOR; } + | variantType '&' { $$ = $1 | VT_BYREF; } + | DECIMAL_ { $$ = VT_DECIMAL; } + | DATE_ { $$ = VT_DATE; } + | BSTR_ { $$ = VT_BSTR; } + | LPSTR_ { $$ = VT_LPSTR; } + | LPWSTR_ { $$ = VT_LPWSTR; } + | IUNKNOWN_ { $$ = VT_UNKNOWN; } + | IDISPATCH_ { $$ = VT_DISPATCH; } + | SAFEARRAY_ { $$ = VT_SAFEARRAY; } + | INT_ { $$ = VT_INT; } + | UNSIGNED_ INT_ { $$ = VT_UINT; } + | UINT_ { $$ = VT_UINT; } + | ERROR_ { $$ = VT_ERROR; } + | HRESULT_ { $$ = VT_HRESULT; } + | CARRAY_ { $$ = VT_CARRAY; } + | USERDEFINED_ { $$ = VT_USERDEFINED; } + | RECORD_ { $$ = VT_RECORD; } + | FILETIME_ { $$ = VT_FILETIME; } + | BLOB_ { $$ = VT_BLOB; } + | STREAM_ { $$ = VT_STREAM; } + | STORAGE_ { $$ = VT_STORAGE; } + | STREAMED_OBJECT_ { $$ = VT_STREAMED_OBJECT; } + | STORED_OBJECT_ { $$ = VT_STORED_OBJECT; } + | BLOB_OBJECT_ { $$ = VT_BLOB_OBJECT; } + | CF_ { $$ = VT_CF; } + | CLSID_ { $$ = VT_CLSID; } + ; + +/* Managed types for signatures */ +type : CLASS_ className { if($2 == PASM->m_tkSysString) + { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_STRING); } + else if($2 == PASM->m_tkSysObject) + { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_OBJECT); } + else + $$ = parser->MakeTypeClass(ELEMENT_TYPE_CLASS, $2); } + | OBJECT_ { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_OBJECT); } + | VALUE_ CLASS_ className { $$ = parser->MakeTypeClass(ELEMENT_TYPE_VALUETYPE, $3); } + | VALUETYPE_ className { $$ = parser->MakeTypeClass(ELEMENT_TYPE_VALUETYPE, $2); } + | type '[' ']' { $$ = $1; $$->insertInt8(ELEMENT_TYPE_SZARRAY); } + | type '[' bounds1 ']' { $$ = parser->MakeTypeArray(ELEMENT_TYPE_ARRAY, $1, $3); } + | type '&' { $$ = $1; $$->insertInt8(ELEMENT_TYPE_BYREF); } + | type '*' { $$ = $1; $$->insertInt8(ELEMENT_TYPE_PTR); } + | type PINNED_ { $$ = $1; $$->insertInt8(ELEMENT_TYPE_PINNED); } + | type MODREQ_ '(' typeSpec ')' { $$ = parser->MakeTypeClass(ELEMENT_TYPE_CMOD_REQD, $4); + $$->append($1); } + | type MODOPT_ '(' typeSpec ')' { $$ = parser->MakeTypeClass(ELEMENT_TYPE_CMOD_OPT, $4); + $$->append($1); } + | methodSpec callConv type '*' '(' sigArgs0 ')' + { $$ = parser->MakeSig($2, $3, $6); + $$->insertInt8(ELEMENT_TYPE_FNPTR); + PASM->delArgNameList(PASM->m_firstArgName); + PASM->m_firstArgName = parser->m_ANSFirst.POP(); + PASM->m_lastArgName = parser->m_ANSLast.POP(); + } + | type '<' tyArgs1 '>' { if($3 == NULL) $$ = $1; + else { + $$ = new BinStr(); + $$->appendInt8(ELEMENT_TYPE_GENERICINST); + $$->append($1); + corEmitInt($$, corCountArgs($3)); + $$->append($3); delete $1; delete $3; }} + | '!' '!' int32 { //if(PASM->m_pCurMethod) { + // if(($3 < 0)||((DWORD)$3 >= PASM->m_pCurMethod->m_NumTyPars)) + // PASM->report->error("Invalid method type parameter '%d'\n",$3); + $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_MVAR); corEmitInt($$, $3); + //} else PASM->report->error("Method type parameter '%d' outside method scope\n",$3); + } + | '!' int32 { //if(PASM->m_pCurClass) { + // if(($2 < 0)||((DWORD)$2 >= PASM->m_pCurClass->m_NumTyPars)) + // PASM->report->error("Invalid type parameter '%d'\n",$2); + $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_VAR); corEmitInt($$, $2); + //} else PASM->report->error("Type parameter '%d' outside class scope\n",$2); + } + | '!' '!' dottedName { int eltype = ELEMENT_TYPE_MVAR; + int n=-1; + if(PASM->m_pCurMethod) n = PASM->m_pCurMethod->FindTyPar($3); + else { + if(PASM->m_TyParList) n = PASM->m_TyParList->IndexOf($3); + if(n == -1) + { n = TyParFixupList.COUNT(); + TyParFixupList.PUSH($3); + eltype = ELEMENT_TYPE_MVARFIXUP; + } + } + if(n == -1) { PASM->report->error("Invalid method type parameter '%s'\n",$3); + n = 0x1FFFFFFF; } + $$ = new BinStr(); $$->appendInt8(eltype); corEmitInt($$,n); + } + | '!' dottedName { int eltype = ELEMENT_TYPE_VAR; + int n=-1; + if(PASM->m_pCurClass && !newclass) n = PASM->m_pCurClass->FindTyPar($2); + else { + if(PASM->m_TyParList) n = PASM->m_TyParList->IndexOf($2); + if(n == -1) + { n = TyParFixupList.COUNT(); + TyParFixupList.PUSH($2); + eltype = ELEMENT_TYPE_VARFIXUP; + } + } + if(n == -1) { PASM->report->error("Invalid type parameter '%s'\n",$2); + n = 0x1FFFFFFF; } + $$ = new BinStr(); $$->appendInt8(eltype); corEmitInt($$,n); + } + | TYPEDREF_ { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_TYPEDBYREF); } + | VOID_ { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_VOID); } + | NATIVE_ INT_ { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_I); } + | NATIVE_ UNSIGNED_ INT_ { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_U); } + | NATIVE_ UINT_ { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_U); } + | simpleType { $$ = $1; } + | ELIPSIS type { $$ = $2; $$->insertInt8(ELEMENT_TYPE_SENTINEL); } + ; + +simpleType : CHAR_ { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_CHAR); } + | STRING_ { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_STRING); } + | BOOL_ { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_BOOLEAN); } + | INT8_ { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_I1); } + | INT16_ { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_I2); } + | INT32_ { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_I4); } + | INT64_ { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_I8); } + | FLOAT32_ { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_R4); } + | FLOAT64_ { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_R8); } + | UNSIGNED_ INT8_ { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_U1); } + | UNSIGNED_ INT16_ { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_U2); } + | UNSIGNED_ INT32_ { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_U4); } + | UNSIGNED_ INT64_ { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_U8); } + | UINT8_ { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_U1); } + | UINT16_ { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_U2); } + | UINT32_ { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_U4); } + | UINT64_ { $$ = new BinStr(); $$->appendInt8(ELEMENT_TYPE_U8); } + | TYPEDEF_TS { $$ = new BinStr(); $$->append($1->m_pbsTypeSpec); } + ; + +bounds1 : bound { $$ = $1; } + | bounds1 ',' bound { $$ = $1; $1->append($3); delete $3; } + ; + +bound : /* EMPTY */ { $$ = new BinStr(); $$->appendInt32(0x7FFFFFFF); $$->appendInt32(0x7FFFFFFF); } + | ELIPSIS { $$ = new BinStr(); $$->appendInt32(0x7FFFFFFF); $$->appendInt32(0x7FFFFFFF); } + | int32 { $$ = new BinStr(); $$->appendInt32(0); $$->appendInt32($1); } + | int32 ELIPSIS int32 { FAIL_UNLESS($1 <= $3, ("lower bound %d must be <= upper bound %d\n", $1, $3)); + if ($1 > $3) { YYERROR; }; + $$ = new BinStr(); $$->appendInt32($1); $$->appendInt32($3-$1+1); } + | int32 ELIPSIS { $$ = new BinStr(); $$->appendInt32($1); $$->appendInt32(0x7FFFFFFF); } + ; + +/* Security declarations */ +secDecl : _PERMISSION secAction typeSpec '(' nameValPairs ')' + { PASM->AddPermissionDecl($2, $3, $5); } + | _PERMISSION secAction typeSpec '=' '{' customBlobDescr '}' + { PASM->AddPermissionDecl($2, $3, $6); } + | _PERMISSION secAction typeSpec { PASM->AddPermissionDecl($2, $3, (NVPair *)NULL); } + | psetHead bytes ')' { PASM->AddPermissionSetDecl($1, $2); } + | _PERMISSIONSET secAction compQstring + { PASM->AddPermissionSetDecl($2,BinStrToUnicode($3,true));} + | _PERMISSIONSET secAction '=' '{' secAttrSetBlob '}' + { BinStr* ret = new BinStr(); + ret->insertInt8('.'); + corEmitInt(ret, nSecAttrBlobs); + ret->append($5); + PASM->AddPermissionSetDecl($2,ret); + nSecAttrBlobs = 0; } + ; + +secAttrSetBlob : /* EMPTY */ { $$ = new BinStr(); nSecAttrBlobs = 0;} + | secAttrBlob { $$ = $1; nSecAttrBlobs = 1; } + | secAttrBlob ',' secAttrSetBlob { $$ = $1; $$->append($3); nSecAttrBlobs++; } + ; + +secAttrBlob : typeSpec '=' '{' customBlobNVPairs '}' + { $$ = PASM->EncodeSecAttr(PASM->ReflectionNotation($1),$4,nCustomBlobNVPairs); + nCustomBlobNVPairs = 0; } + | CLASS_ SQSTRING '=' '{' customBlobNVPairs '}' + { $$ = PASM->EncodeSecAttr($2,$5,nCustomBlobNVPairs); + nCustomBlobNVPairs = 0; } + ; + +psetHead : _PERMISSIONSET secAction '=' '(' { $$ = $2; bParsingByteArray = TRUE; } + | _PERMISSIONSET secAction BYTEARRAY_ '(' + { $$ = $2; bParsingByteArray = TRUE; } + ; + +nameValPairs : nameValPair { $$ = $1; } + | nameValPair ',' nameValPairs { $$ = $1->Concat($3); } + ; + +nameValPair : compQstring '=' caValue { $1->appendInt8(0); $$ = new NVPair($1, $3); } + ; + +truefalse : TRUE_ { $$ = 1; } + | FALSE_ { $$ = 0; } + ; + +caValue : truefalse { $$ = new BinStr(); + $$->appendInt8(SERIALIZATION_TYPE_BOOLEAN); + $$->appendInt8($1); } + | int32 { $$ = new BinStr(); + $$->appendInt8(SERIALIZATION_TYPE_I4); + $$->appendInt32($1); } + | INT32_ '(' int32 ')' { $$ = new BinStr(); + $$->appendInt8(SERIALIZATION_TYPE_I4); + $$->appendInt32($3); } + | compQstring { $$ = new BinStr(); + $$->appendInt8(SERIALIZATION_TYPE_STRING); + $$->append($1); delete $1; + $$->appendInt8(0); } + | className '(' INT8_ ':' int32 ')' { $$ = new BinStr(); + $$->appendInt8(SERIALIZATION_TYPE_ENUM); + char* sz = PASM->ReflectionNotation($1); + strcpy_s((char *)$$->getBuff((unsigned)strlen(sz) + 1), strlen(sz) + 1,sz); + $$->appendInt8(1); + $$->appendInt32($5); } + | className '(' INT16_ ':' int32 ')' { $$ = new BinStr(); + $$->appendInt8(SERIALIZATION_TYPE_ENUM); + char* sz = PASM->ReflectionNotation($1); + strcpy_s((char *)$$->getBuff((unsigned)strlen(sz) + 1), strlen(sz) + 1,sz); + $$->appendInt8(2); + $$->appendInt32($5); } + | className '(' INT32_ ':' int32 ')' { $$ = new BinStr(); + $$->appendInt8(SERIALIZATION_TYPE_ENUM); + char* sz = PASM->ReflectionNotation($1); + strcpy_s((char *)$$->getBuff((unsigned)strlen(sz) + 1), strlen(sz) + 1,sz); + $$->appendInt8(4); + $$->appendInt32($5); } + | className '(' int32 ')' { $$ = new BinStr(); + $$->appendInt8(SERIALIZATION_TYPE_ENUM); + char* sz = PASM->ReflectionNotation($1); + strcpy_s((char *)$$->getBuff((unsigned)strlen(sz) + 1), strlen(sz) + 1,sz); + $$->appendInt8(4); + $$->appendInt32($3); } + ; + +secAction : REQUEST_ { $$ = dclRequest; } + | DEMAND_ { $$ = dclDemand; } + | ASSERT_ { $$ = dclAssert; } + | DENY_ { $$ = dclDeny; } + | PERMITONLY_ { $$ = dclPermitOnly; } + | LINKCHECK_ { $$ = dclLinktimeCheck; } + | INHERITCHECK_ { $$ = dclInheritanceCheck; } + | REQMIN_ { $$ = dclRequestMinimum; } + | REQOPT_ { $$ = dclRequestOptional; } + | REQREFUSE_ { $$ = dclRequestRefuse; } + | PREJITGRANT_ { $$ = dclPrejitGrant; } + | PREJITDENY_ { $$ = dclPrejitDenied; } + | NONCASDEMAND_ { $$ = dclNonCasDemand; } + | NONCASLINKDEMAND_ { $$ = dclNonCasLinkDemand; } + | NONCASINHERITANCE_ { $$ = dclNonCasInheritance; } + ; + +/* External source declarations */ +esHead : _LINE { PASM->ResetLineNumbers(); nCurrPC = PASM->m_CurPC; PENV->bExternSource = TRUE; PENV->bExternSourceAutoincrement = FALSE; } + | P_LINE { PASM->ResetLineNumbers(); nCurrPC = PASM->m_CurPC; PENV->bExternSource = TRUE; PENV->bExternSourceAutoincrement = TRUE; } + ; + +extSourceSpec : esHead int32 SQSTRING { PENV->nExtLine = PENV->nExtLineEnd = $2; + PENV->nExtCol = 0; PENV->nExtColEnd = static_cast(-1); + PASM->SetSourceFileName($3);} + | esHead int32 { PENV->nExtLine = PENV->nExtLineEnd = $2; + PENV->nExtCol = 0; PENV->nExtColEnd = static_cast(-1); } + | esHead int32 ':' int32 SQSTRING { PENV->nExtLine = PENV->nExtLineEnd = $2; + PENV->nExtCol=$4; PENV->nExtColEnd = static_cast(-1); + PASM->SetSourceFileName($5);} + | esHead int32 ':' int32 { PENV->nExtLine = PENV->nExtLineEnd = $2; + PENV->nExtCol=$4; PENV->nExtColEnd = static_cast(-1);} + | esHead int32 ':' int32 ',' int32 SQSTRING + { PENV->nExtLine = PENV->nExtLineEnd = $2; + PENV->nExtCol=$4; PENV->nExtColEnd = $6; + PASM->SetSourceFileName($7);} + | esHead int32 ':' int32 ',' int32 + { PENV->nExtLine = PENV->nExtLineEnd = $2; + PENV->nExtCol=$4; PENV->nExtColEnd = $6; } + | esHead int32 ',' int32 ':' int32 SQSTRING + { PENV->nExtLine = $2; PENV->nExtLineEnd = $4; + PENV->nExtCol=$6; PENV->nExtColEnd = static_cast(-1); + PASM->SetSourceFileName($7);} + | esHead int32 ',' int32 ':' int32 + { PENV->nExtLine = $2; PENV->nExtLineEnd = $4; + PENV->nExtCol=$6; PENV->nExtColEnd = static_cast(-1); } + | esHead int32 ',' int32 ':' int32 ',' int32 SQSTRING + { PENV->nExtLine = $2; PENV->nExtLineEnd = $4; + PENV->nExtCol=$6; PENV->nExtColEnd = $8; + PASM->SetSourceFileName($9);} + | esHead int32 ',' int32 ':' int32 ',' int32 + { PENV->nExtLine = $2; PENV->nExtLineEnd = $4; + PENV->nExtCol=$6; PENV->nExtColEnd = $8; } + | esHead int32 QSTRING { PENV->nExtLine = PENV->nExtLineEnd = $2 - 1; + PENV->nExtCol = 0; PENV->nExtColEnd = static_cast(-1); + PASM->SetSourceFileName($3);} + ; + +/* Manifest declarations */ +fileDecl : _FILE fileAttr dottedName fileEntry hashHead bytes ')' fileEntry + { PASMM->AddFile($3, $2|$4|$8, $6); } + | _FILE fileAttr dottedName fileEntry { PASMM->AddFile($3, $2|$4, NULL); } + ; + +fileAttr : /* EMPTY */ { $$ = (CorFileFlags) 0; } + | fileAttr NOMETADATA_ { $$ = (CorFileFlags) ($1 | ffContainsNoMetaData); } + ; + +fileEntry : /* EMPTY */ { $$ = (CorFileFlags) 0; } + | _ENTRYPOINT { $$ = (CorFileFlags) 0x80000000; } + ; + +hashHead : _HASH '=' '(' { bParsingByteArray = TRUE; } + ; + +assemblyHead : _ASSEMBLY asmAttr dottedName { PASMM->StartAssembly($3, NULL, (DWORD)$2, FALSE); } + ; + +asmAttr : /* EMPTY */ { $$ = (CorAssemblyFlags) 0; } + | asmAttr RETARGETABLE_ { $$ = (CorAssemblyFlags) ($1 | afRetargetable); } + | asmAttr WINDOWSRUNTIME_ { $$ = (CorAssemblyFlags) ($1 | afContentType_WindowsRuntime); } + | asmAttr NOPLATFORM_ { $$ = (CorAssemblyFlags) ($1 | afPA_NoPlatform); } + | asmAttr LEGACY_ LIBRARY_ { $$ = $1; } + | asmAttr CIL_ { SET_PA($$,$1,afPA_MSIL); } + | asmAttr X86_ { SET_PA($$,$1,afPA_x86); } + | asmAttr IA64_ { SET_PA($$,$1,afPA_IA64); } + | asmAttr AMD64_ { SET_PA($$,$1,afPA_AMD64); } + | asmAttr ARM_ { SET_PA($$,$1,afPA_ARM); } + ; + +assemblyDecls : /* EMPTY */ + | assemblyDecls assemblyDecl + ; + +assemblyDecl : _HASH ALGORITHM_ int32 { PASMM->SetAssemblyHashAlg($3); } + | secDecl + | asmOrRefDecl + ; + +intOrWildcard : int32 { $$ = $1; } + | '*' { $$ = 0xFFFF; } + ; + +asmOrRefDecl : publicKeyHead bytes ')' { PASMM->SetAssemblyPublicKey($2); } + | _VER intOrWildcard ':' intOrWildcard ':' intOrWildcard ':' intOrWildcard + { PASMM->SetAssemblyVer((USHORT)$2, (USHORT)$4, (USHORT)$6, (USHORT)$8); } + | _LOCALE compQstring { $2->appendInt8(0); PASMM->SetAssemblyLocale($2,TRUE); } + | localeHead bytes ')' { PASMM->SetAssemblyLocale($2,FALSE); } + | customAttrDecl + | compControl + ; + +publicKeyHead : _PUBLICKEY '=' '(' { bParsingByteArray = TRUE; } + ; + +publicKeyTokenHead : _PUBLICKEYTOKEN '=' '(' { bParsingByteArray = TRUE; } + ; + +localeHead : _LOCALE '=' '(' { bParsingByteArray = TRUE; } + ; + +assemblyRefHead : _ASSEMBLY EXTERN_ asmAttr dottedName + { PASMM->StartAssembly($4, NULL, $3, TRUE); } + | _ASSEMBLY EXTERN_ asmAttr dottedName AS_ dottedName + { PASMM->StartAssembly($4, $6, $3, TRUE); } + ; + +assemblyRefDecls : /* EMPTY */ + | assemblyRefDecls assemblyRefDecl + ; + +assemblyRefDecl : hashHead bytes ')' { PASMM->SetAssemblyHashBlob($2); } + | asmOrRefDecl + | publicKeyTokenHead bytes ')' { PASMM->SetAssemblyPublicKeyToken($2); } + | AUTO_ { PASMM->SetAssemblyAutodetect(); } + ; + +exptypeHead : _CLASS EXTERN_ exptAttr dottedName { PASMM->StartComType($4, $3);} + ; + +exportHead : _EXPORT exptAttr dottedName /* deprecated */ { PASMM->StartComType($3, $2); } + ; + +exptAttr : /* EMPTY */ { $$ = (CorTypeAttr) 0; } + | exptAttr PRIVATE_ { $$ = (CorTypeAttr) ($1 | tdNotPublic); } + | exptAttr PUBLIC_ { $$ = (CorTypeAttr) ($1 | tdPublic); } + | exptAttr FORWARDER_ { $$ = (CorTypeAttr) ($1 | tdForwarder); } + | exptAttr NESTED_ PUBLIC_ { $$ = (CorTypeAttr) ($1 | tdNestedPublic); } + | exptAttr NESTED_ PRIVATE_ { $$ = (CorTypeAttr) ($1 | tdNestedPrivate); } + | exptAttr NESTED_ FAMILY_ { $$ = (CorTypeAttr) ($1 | tdNestedFamily); } + | exptAttr NESTED_ ASSEMBLY_ { $$ = (CorTypeAttr) ($1 | tdNestedAssembly); } + | exptAttr NESTED_ FAMANDASSEM_ { $$ = (CorTypeAttr) ($1 | tdNestedFamANDAssem); } + | exptAttr NESTED_ FAMORASSEM_ { $$ = (CorTypeAttr) ($1 | tdNestedFamORAssem); } + ; + +exptypeDecls : /* EMPTY */ + | exptypeDecls exptypeDecl + ; + +exptypeDecl : _FILE dottedName { PASMM->SetComTypeFile($2); } + | _CLASS EXTERN_ slashedName { PASMM->SetComTypeComType($3); } + | _ASSEMBLY EXTERN_ dottedName { PASMM->SetComTypeAsmRef($3); } + | MDTOKEN_ '(' int32 ')' { if(!PASMM->SetComTypeImplementationTok($3)) + PASM->report->error("Invalid implementation of exported type\n"); } + | _CLASS int32 { if(!PASMM->SetComTypeClassTok($2)) + PASM->report->error("Invalid TypeDefID of exported type\n"); } + | customAttrDecl + | compControl + ; + +manifestResHead : _MRESOURCE manresAttr dottedName { PASMM->StartManifestRes($3, $3, $2); } + | _MRESOURCE manresAttr dottedName AS_ dottedName + { PASMM->StartManifestRes($3, $5, $2); } + ; + +manresAttr : /* EMPTY */ { $$ = (CorManifestResourceFlags) 0; } + | manresAttr PUBLIC_ { $$ = (CorManifestResourceFlags) ($1 | mrPublic); } + | manresAttr PRIVATE_ { $$ = (CorManifestResourceFlags) ($1 | mrPrivate); } + ; + +manifestResDecls : /* EMPTY */ + | manifestResDecls manifestResDecl + ; + +manifestResDecl : _FILE dottedName AT_ int32 { PASMM->SetManifestResFile($2, (ULONG)$4); } + | _ASSEMBLY EXTERN_ dottedName { PASMM->SetManifestResAsmRef($3); } + | customAttrDecl + | compControl + ; + + +%% + +#include "grammar_after.cpp" diff --git a/src/ilasm/asmtemplates.h b/src/ilasm/asmtemplates.h new file mode 100644 index 0000000000..7ec206ec80 --- /dev/null +++ b/src/ilasm/asmtemplates.h @@ -0,0 +1,846 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +#ifndef ASMTEMPLATES_H +#define ASMTEMPLATES_H + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:22008) // "Suppress PREfast warnings about integer overflow" +#endif + +inline ULONG GrowBuffer(ULONG startingSize) +{ + int toAdd = startingSize >> 1; + if (toAdd < 8) + toAdd = 8; + if (toAdd > 2048) + toAdd = 2048; + return startingSize + toAdd; +} + +/*****************************************************************************/ +/* LIFO (stack) and FIFO (queue) templates (must precede #include "method.h")*/ +template +class LIST_EL +{ +public: + T* m_Ptr; + LIST_EL *m_Next; + LIST_EL(T *item) {m_Next = NULL; m_Ptr = item; }; +}; + +template +class LIFO +{ +public: + inline LIFO() { m_pHead = NULL; }; + inline ~LIFO() {T *val; while((val = POP()) != NULL) delete val; }; + void PUSH(T *item) + { + m_pTemp = new LIST_EL (item); + m_pTemp->m_Next = m_pHead; + m_pHead = m_pTemp; + }; + T* POP() + { + T* ret = NULL; + if((m_pTemp = m_pHead) != NULL) + { + m_pHead = m_pHead->m_Next; + ret = m_pTemp->m_Ptr; + delete m_pTemp; + } + return ret; + }; +private: + LIST_EL *m_pHead; + LIST_EL *m_pTemp; +}; + + +#if (0) +template +class FIFO +{ +public: + inline FIFO() { m_pHead = m_pTail = NULL; m_ulCount = 0;}; + inline ~FIFO() {T *val; while(val = POP()) delete val; }; + void PUSH(T *item) + { + m_pTemp = new LIST_EL (item); + if(m_pTail) m_pTail->m_Next = m_pTemp; + m_pTail = m_pTemp; + if(m_pHead == NULL) m_pHead = m_pTemp; + m_ulCount++; + }; + T* POP() + { + T* ret = NULL; + if(m_pTemp = m_pHead) + { + m_pHead = m_pHead->m_Next; + ret = m_pTemp->m_Ptr; + delete m_pTemp; + if(m_pHead == NULL) m_pTail = NULL; + m_ulCount--; + } + return ret; + }; + ULONG COUNT() { return m_ulCount; }; + T* PEEK(ULONG idx) + { + T* ret = NULL; + ULONG i; + if(idx < m_ulCount) + { + if(idx == m_ulCount-1) m_pTemp = m_pTail; + else for(m_pTemp = m_pHead, i = 0; i < idx; m_pTemp = m_pTemp->m_Next, i++); + ret = m_pTemp->m_Ptr; + } + return ret; + }; +private: + LIST_EL *m_pHead; + LIST_EL *m_pTail; + LIST_EL *m_pTemp; + ULONG m_ulCount; +}; +#else +template +class FIFO +{ +public: + FIFO() { m_Arr = NULL; m_ulArrLen = 0; m_ulCount = 0; m_ulOffset = 0; }; + ~FIFO() { + if(m_Arr) { + for(ULONG i=0; i < m_ulCount; i++) { + if(m_Arr[i+m_ulOffset]) delete m_Arr[i+m_ulOffset]; + } + delete [] m_Arr; + } + }; + void RESET(bool DeleteElements = true) { + if(m_Arr) { + for(ULONG i=0; i < m_ulCount; i++) { + if(DeleteElements) delete m_Arr[i+m_ulOffset]; + m_Arr[i+m_ulOffset] = NULL; + } + m_ulCount = 0; + m_ulOffset= 0; + } + }; + void PUSH(T *item) + { + if(item) + { + if(m_ulCount+m_ulOffset >= m_ulArrLen) + { + if(m_ulOffset) + { + memcpy(m_Arr,&m_Arr[m_ulOffset],m_ulCount*sizeof(T*)); + m_ulOffset = 0; + } + else + { + m_ulArrLen = GrowBuffer(m_ulArrLen); + T** tmp = new T*[m_ulArrLen]; + if(tmp) + { + if(m_Arr) + { + memcpy(tmp,m_Arr,m_ulCount*sizeof(T*)); + delete [] m_Arr; + } + m_Arr = tmp; + } + else fprintf(stderr,"\nOut of memory!\n"); + } + } + m_Arr[m_ulOffset+m_ulCount] = item; + m_ulCount++; + } + }; + ULONG COUNT() { return m_ulCount; }; + T* POP() + { + T* ret = NULL; + if(m_ulCount) + { + ret = m_Arr[m_ulOffset++]; + m_ulCount--; + } + return ret; + }; + T* PEEK(ULONG idx) { return (idx < m_ulCount) ? m_Arr[m_ulOffset+idx] : NULL; }; +private: + T** m_Arr; + ULONG m_ulCount; + ULONG m_ulOffset; + ULONG m_ulArrLen; +}; +#endif + + +template struct Indx256 +{ + void* table[256]; + Indx256() { memset(table,0,sizeof(table)); }; + ~Indx256() + { + ClearAll(true); + for(int i = 1; i < 256; i++) delete ((Indx256*)(table[i])); + }; + T** IndexString(BYTE* psz, T* pObj) + { + if(*psz == 0) + { + table[0] = (void*)pObj; + return (T**)table; + } + else + { + Indx256* pInd = (Indx256*)(table[*psz]); + if(pInd == NULL) + { + pInd = new Indx256; + if(pInd) + table[*psz] = pInd; + else + { + _ASSERTE(!"Out of memory in Indx256::IndexString!"); + fprintf(stderr,"\nOut of memory in Indx256::IndexString!\n"); + return NULL; + } + } + return pInd->IndexString(psz+1,pObj); + } + }; + T* FindString(BYTE* psz) + { + if(*psz > 0) + { + Indx256* pInd = (Indx256*)(table[*psz]); + return (pInd == NULL) ? NULL : pInd->FindString(psz+1); + } + return (T*)(table[0]); // if i==0 + }; + + void ClearAll(bool DeleteObj) + { + if(DeleteObj) delete (T*)(table[0]); + table[0] = NULL; + for(unsigned i = 1; i < 256; i++) + { + if(table[i]) + { + Indx256* pInd = (Indx256*)(table[i]); + pInd->ClearAll(DeleteObj); + //delete pInd; + //table[i] = NULL; + } + } + }; +}; + +// +// Template intended for named objects, that expose function char* NameOf() +// +template +class FIFO_INDEXED +{ +public: + FIFO_INDEXED() { m_Arr = NULL; m_ulArrLen = 0; m_ulCount = 0; m_ulOffset = 0; }; + ~FIFO_INDEXED() { + if(m_Arr) + { + RESET(true); + delete [] m_Arr; + } + }; + void RESET(bool DeleteElements = true) { + if(m_Arr) { + unsigned i; + if(DeleteElements) + { + for(i=m_ulOffset; i < m_ulOffset+m_ulCount; i++) + { + T** ppT = m_Arr[i]; + delete *ppT; + } + } + for(i=m_ulOffset; i < m_ulOffset+m_ulCount; i++) + { + *m_Arr[i] = NULL; + } + memset(&m_Arr[m_ulOffset],0,m_ulCount*sizeof(void*)); + m_ulCount = 0; + m_ulOffset= 0; + } + }; + void PUSH(T *item) + { + if(item) + { + T** itemaddr = m_Index.IndexString((BYTE*)(item->NameOf()),item); + if(m_ulCount+m_ulOffset >= m_ulArrLen) + { + if(m_ulOffset) + { + memcpy(m_Arr,&m_Arr[m_ulOffset],m_ulCount*sizeof(T*)); + m_ulOffset = 0; + } + else + { + m_ulArrLen = GrowBuffer(m_ulArrLen); + T*** tmp = new T**[m_ulArrLen]; + if(tmp) + { + if(m_Arr) + { + memcpy(tmp,m_Arr,m_ulCount*sizeof(T**)); + delete [] m_Arr; + } + m_Arr = tmp; + } + else fprintf(stderr,"\nOut of memory!\n"); + } + } + m_Arr[m_ulOffset+m_ulCount] = itemaddr; + m_ulCount++; + } + }; + ULONG COUNT() { return m_ulCount; }; + T* POP() + { + T* ret = NULL; + if(m_ulCount) + { + ret = *(m_Arr[m_ulOffset]); + *m_Arr[m_ulOffset] = NULL; + m_ulOffset++; + m_ulCount--; + } + return ret; + }; + T* PEEK(ULONG idx) { return (idx < m_ulCount) ? *(m_Arr[m_ulOffset+idx]) : NULL; }; + T* FIND(T* item) { return m_Index.FindString((BYTE*)(item->NameOf())); }; +private: + T*** m_Arr; + ULONG m_ulCount; + ULONG m_ulOffset; + ULONG m_ulArrLen; + Indx256 m_Index; +}; + +template +class SORTEDARRAY +{ +public: + SORTEDARRAY() { m_Arr = NULL; m_ulArrLen = 0; m_ulCount = 0; m_ulOffset = 0; }; + ~SORTEDARRAY() { + if(m_Arr) { + for(ULONG i=0; i < m_ulCount; i++) { + if(m_Arr[i+m_ulOffset]) delete m_Arr[i+m_ulOffset]; + } + delete [] m_Arr; + } + }; + void RESET(bool DeleteElements = true) { + if(m_Arr) { + if(DeleteElements) + { + for(ULONG i=0; i < m_ulCount; i++) { + delete m_Arr[i+m_ulOffset]; + } + } + memset(m_Arr,0,m_ulArrLen*sizeof(T*)); + m_ulCount = 0; + m_ulOffset= 0; + } + }; + void PUSH(T *item) + { + if(item) + { + if(m_ulCount+m_ulOffset >= m_ulArrLen) + { + if(m_ulOffset) + { + memcpy(m_Arr,&m_Arr[m_ulOffset],m_ulCount*sizeof(T*)); + m_ulOffset = 0; + } + else + { + m_ulArrLen = GrowBuffer(m_ulArrLen); + T** tmp = new T*[m_ulArrLen]; + if(tmp) + { + if(m_Arr) + { + memcpy(tmp,m_Arr,m_ulCount*sizeof(T*)); + delete [] m_Arr; + } + m_Arr = tmp; + } + else fprintf(stderr,"\nOut of memory!\n"); + } + } + if(m_ulCount) + { + // find 1st arr.element > item + T** low = &m_Arr[m_ulOffset]; + T** high = &m_Arr[m_ulOffset+m_ulCount-1]; + T** mid; + + if(item->ComparedTo(*high) > 0) mid = high+1; + else if(item->ComparedTo(*low) < 0) mid = low; + else for(;;) + { + mid = &low[(high - low) >> 1]; + + int cmp = item->ComparedTo(*mid); + + if (mid == low) + { + if(cmp > 0) mid++; + break; + } + + if (cmp > 0) low = mid; + else high = mid; + } + + ///////////////////////////////////////////// + memmove(mid+1,mid,(BYTE*)&m_Arr[m_ulOffset+m_ulCount]-(BYTE*)mid); + *mid = item; + } + else m_Arr[m_ulOffset+m_ulCount] = item; + m_ulCount++; + } + }; + ULONG COUNT() { return m_ulCount; }; + T* POP() + { + T* ret = NULL; + if(m_ulCount) + { + ret = m_Arr[m_ulOffset++]; + m_ulCount--; + } + return ret; + }; + T* PEEK(ULONG idx) { return (idx < m_ulCount) ? m_Arr[m_ulOffset+idx] : NULL; }; + T* FIND(T* item) + { + if(m_ulCount) + { + T** low = &m_Arr[m_ulOffset]; + T** high = &m_Arr[m_ulOffset+m_ulCount-1]; + T** mid; + if(item->ComparedTo(*high) == 0) return(*high); + for(;;) + { + mid = &low[(high - low) >> 1]; + int cmp = item->ComparedTo(*mid); + if (cmp == 0) return(*mid); + if (mid == low) break; + if (cmp > 0) low = mid; + else high = mid; + } + } + return NULL; + }; + /* + T* FIND(U item) + { + if(m_ulCount) + { + T** low = &m_Arr[m_ulOffset]; + T** high = &m_Arr[m_ulOffset+m_ulCount-1]; + T** mid; + if((*high)->Compare(item) == 0) return(*high); + for(;;) + { + mid = &low[(high - low) >> 1]; + int cmp = (*mid)->Compare(item); + if (cmp == 0) return(*mid); + if (mid == low) break; + if (cmp > 0) low = mid; + else high = mid; + } + } + return NULL; + }; + */ + BOOL DEL(T* item) + { + if(m_ulCount) + { + T** low = &m_Arr[m_ulOffset]; + T** high = &m_Arr[m_ulOffset+m_ulCount-1]; + T** mid; + if(item->ComparedTo(*high) == 0) + { + delete (*high); + m_ulCount--; + return TRUE; + } + for(;;) + { + mid = &low[(high - low) >> 1]; + int cmp = item->ComparedTo(*mid); + if (cmp == 0) + { + delete (*mid); + memcpy(mid,mid+1,(BYTE*)&m_Arr[m_ulOffset+m_ulCount]-(BYTE*)mid-1); + m_ulCount--; + return TRUE; + } + if (mid == low) break; + if (cmp > 0) low = mid; + else high = mid; + } + } + return FALSE; + }; +private: + T** m_Arr; + ULONG m_ulCount; + ULONG m_ulOffset; + ULONG m_ulArrLen; +}; + +template struct RBNODE +{ +private: + DWORD dwRed; +public: + DWORD dwInUse; + T* tVal; + RBNODE* pLeft; + RBNODE* pRight; + RBNODE* pParent; + RBNODE() + { + pLeft = pRight = pParent = NULL; + tVal = NULL; + dwRed = dwInUse = 0; + }; + RBNODE(T* pVal, DWORD dwColor) + { + pLeft = pRight = pParent = NULL; + tVal = pVal; + dwRed = dwColor; + dwInUse = 0; + }; + bool IsRed() { return (dwRed != 0); }; + void SetRed() { dwRed = 1; }; + void SetBlack() { dwRed = 0; }; +}; + +#define BUCKETCOUNT 512 + +template class RBNODEBUCKET +{ +private: + RBNODEBUCKET* pNext; + RBNODE bucket[BUCKETCOUNT]; + unsigned alloc_count; + +public: + RBNODEBUCKET() + { + alloc_count = 0; + pNext = NULL; + }; + + ~RBNODEBUCKET() { if(pNext) delete pNext; }; + + bool CanAlloc() { return (alloc_count < BUCKETCOUNT); }; + + RBNODE* AllocNode() + { + RBNODE* pRet; + for(unsigned i = 0; i < BUCKETCOUNT; i++) + { + if(bucket[i].dwInUse == 0) + { + alloc_count++; + pRet = &bucket[i]; + memset(pRet, 0, sizeof(RBNODE)); + pRet->dwInUse = 1; + return pRet; + } + } + _ASSERTE(!"AllocNode returns NULL"); + return NULL; + }; + + bool FreeNode(RBNODE* ptr) + { + size_t idx = ((size_t)ptr - (size_t)bucket)/sizeof(RBNODE); + if(idx < BUCKETCOUNT) + { + bucket[idx].dwInUse = 0; + alloc_count--; + return true; + } + return false; + }; + + RBNODEBUCKET* Next() { return pNext; }; + + void Append(RBNODEBUCKET* ptr) { pNext = ptr; }; +}; + +template class RBNODEPOOL +{ +private: + RBNODEBUCKET base; + +public: + RBNODEPOOL() + { + memset(&base,0,sizeof(RBNODEBUCKET)); + }; + + RBNODE* AllocNode() + { + RBNODEBUCKET* pBucket = &base; + RBNODEBUCKET* pLastBucket = &base; + do + { + if(pBucket->CanAlloc()) + { + return pBucket->AllocNode(); + } + pLastBucket = pBucket; + pBucket = pBucket->Next(); + } + while (pBucket != NULL); + pLastBucket->Append(new RBNODEBUCKET); + return pLastBucket->Next()->AllocNode(); + }; + + void FreeNode(RBNODE* ptr) + { + RBNODEBUCKET* pBucket = &base; + do + { + if(pBucket->FreeNode(ptr)) + break; + pBucket = pBucket->Next(); + } + while (pBucket != NULL); + }; +}; + +template class RBTREE +{ +private: + RBNODE* pRoot; + RBNODE* pNil; + RBNODEPOOL NodePool; + void RotateLeft(RBNODE* pX) + { + RBNODE* pY; + + pY = pX->pRight; + pX->pRight = pY->pLeft; + + if(pY->pLeft != pNil) + pY->pLeft->pParent = pX; + + pY->pParent = pX->pParent; + + if(pX == pX->pParent->pLeft) + pX->pParent->pLeft = pY; + else + pX->pParent->pRight = pY; + + pY->pLeft = pX; + pX->pParent = pY; + }; + + void RotateRight(RBNODE* pX) + { + RBNODE* pY; + + pY = pX->pLeft; + pX->pLeft = pY->pRight; + + if(pY->pRight != pNil) + pY->pRight->pParent = pX; + + pY->pParent = pX->pParent; + + if(pX == pX->pParent->pLeft) + pX->pParent->pLeft = pY; + else + pX->pParent->pRight = pY; + + pY->pRight = pX; + pX->pParent = pY; + + }; + + void InsertNode(RBNODE* pZ) + { + RBNODE* pX; + RBNODE* pY; + + pZ->pLeft = pZ->pRight = pNil; + pY = pRoot; + pX = pRoot->pLeft; + + if(pX != pY) + { + while(pX != pNil) + { + pY = pX; + if(pX->tVal->ComparedTo(pZ->tVal) > 0) + pX = pX->pLeft; + else + pX = pX->pRight; + } + } + pZ->pParent = pY; + if((pY == pRoot) || (pY->tVal->ComparedTo(pZ->tVal) > 0)) + pY->pLeft = pZ; + else + pY->pRight = pZ; + }; + + void InitSpecNode(RBNODE* pNode) + { + pNode->pLeft = pNode->pRight = pNode->pParent = pNode; + }; + + void DeleteNode(RBNODE* pNode, bool DeletePayload = true) + { + if((pNode != pNil)&&(pNode != pRoot)) + { + DeleteNode(pNode->pLeft, DeletePayload); + DeleteNode(pNode->pRight, DeletePayload); + if(DeletePayload) + delete pNode->tVal; + NodePool.FreeNode(pNode); + } + }; + +public: + RBTREE() + { + pRoot = NodePool.AllocNode(); + InitSpecNode(pRoot); + + pNil = NodePool.AllocNode(); + InitSpecNode(pNil); + }; + + ~RBTREE() + { + //RESET(false); + //NodePool.FreeNode(pRoot); + //NodePool.FreeNode(pNil); + }; + + void RESET(bool DeletePayload = true) + { + DeleteNode(pRoot->pLeft, DeletePayload); + InitSpecNode(pRoot); + InitSpecNode(pNil); + }; + + void PUSH(T* pT) + { + RBNODE* pX; + RBNODE* pY; + RBNODE* pNewNode = NodePool.AllocNode(); + + pNewNode->tVal = pT; + pNewNode->SetRed(); + + InsertNode(pNewNode); + + for(pX = pNewNode; pX->pParent->IsRed();) + { + if(pX->pParent == pX->pParent->pLeft) + { + pY = pX->pParent->pRight; + if(pY->IsRed()) + { + pX->pParent->SetBlack(); + pY->SetBlack(); + pX->pParent->pParent->SetRed(); + pX = pX->pParent->pParent; + } + else + { + if(pX == pX->pParent->pRight) + { + pX = pX->pParent; + RotateLeft(pX); + } + pX->pParent->SetBlack(); + pX->pParent->pParent->SetRed(); + RotateRight(pX->pParent->pParent); + } + } + else // if(pX->pParent == pX->pParent->pRight) + { + pY = pX->pParent->pParent->pLeft; + if(pY->IsRed()) + { + pX->pParent->SetBlack(); + pY->SetBlack(); + pX->pParent->pParent->SetRed(); + pX = pX->pParent->pParent; + } + else + { + if(pX == pX->pParent->pLeft) + { + pX = pX->pParent; + RotateRight(pX); + } + pX->pParent->SetBlack(); + pX->pParent->pParent->SetRed(); + RotateLeft(pX->pParent->pParent); + } + }// end if(pX->pParent == pX->pParent->pLeft) -- else + } // end for(pX = pNewNode; pX->pParent->IsRed();) + pRoot->pLeft->SetBlack(); + }; + + T* FIND(T* pT) + { + RBNODE* pX = pRoot->pLeft; + if((pX != pNil) && (pX != pRoot)) + { + int cmp = pX->tVal->ComparedTo(pT); + while(cmp != 0) + { + if(cmp > 0) + pX = pX->pLeft; + else + pX = pX->pRight; + if(pX == pNil) + return NULL; + cmp = pX->tVal->ComparedTo(pT); + } + return pX->tVal; + } + return NULL; + }; +}; + +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +#endif //ASMTEMPLATES_H + diff --git a/src/ilasm/assem.cpp b/src/ilasm/assem.cpp new file mode 100644 index 0000000000..8f30613430 --- /dev/null +++ b/src/ilasm/assem.cpp @@ -0,0 +1,1613 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// +// File: assem.cpp +// + +// +// COM+ IL assembler +// +#include "ilasmpch.h" + +#define INITGUID + +#define DECLARE_DATA + +#include "assembler.h" +#include "MscorpeSxS.h" + +void indexKeywords(Indx* indx); // defined in asmparse.y + +unsigned int g_uCodePage = CP_ACP; +unsigned int g_uConsoleCP = CP_ACP; + +char g_szSourceFileName[MAX_FILENAME_LENGTH*3]; + +WCHAR wzUniBuf[dwUniBuf]; // Unicode conversion global buffer + +Assembler::Assembler() +{ + m_pDisp = NULL; + m_pEmitter = NULL; + m_pImporter = NULL; + + m_fCPlusPlus = FALSE; + m_fWindowsCE = FALSE; + char* pszFQN = new char[16]; + strcpy_s(pszFQN,16,""); + m_pModuleClass = new Class(pszFQN); + m_lstClass.PUSH(m_pModuleClass); + m_hshClass.PUSH(m_pModuleClass); + m_pModuleClass->m_cl = mdTokenNil; + m_pModuleClass->m_bIsMaster = FALSE; + + m_fStdMapping = FALSE; + m_fDisplayTraceOutput= FALSE; + m_fENCMode = FALSE; + m_fTolerateDupMethods = FALSE; + + m_pCurOutputPos = NULL; + + m_CurPC = 0; // PC offset in method + m_pCurMethod = NULL; + m_pCurClass = NULL; + m_pCurEvent = NULL; + m_pCurProp = NULL; + + m_wzMetadataVersion = NULL; + m_wMSVmajor = 0xFFFF; + m_wMSVminor = 0xFFFF; + + m_wSSVersionMajor = 4; + m_wSSVersionMinor = 0; + m_fAppContainer = FALSE; + m_fHighEntropyVA = FALSE; + + m_pCeeFileGen = NULL; + m_pCeeFile = 0; + + m_pManifest = NULL; + + m_pCustomDescrList = NULL; + + m_pGlobalDataSection = NULL; + m_pILSection = NULL; + m_pTLSSection = NULL; + + m_fDidCoInitialise = FALSE; + + m_fDLL = FALSE; + m_fEntryPointPresent = FALSE; + m_fHaveFieldsWithRvas = FALSE; + m_fFoldCode = FALSE; + m_dwMethodsFolded = 0; + + m_szScopeName[0] = 0; + m_crExtends = mdTypeDefNil; + + m_nImplList = 0; + m_TyParList = NULL; + + m_SEHD = NULL; + m_firstArgName = NULL; + m_lastArgName = NULL; + m_szNamespace = new char[2]; + m_szNamespace[0] = 0; + m_NSstack.PUSH(m_szNamespace); + + m_szFullNS = new char[MAX_NAMESPACE_LENGTH]; + memset(m_szFullNS,0,MAX_NAMESPACE_LENGTH); + m_ulFullNSLen = MAX_NAMESPACE_LENGTH; + + m_State = STATE_OK; + m_fInitialisedMetaData = FALSE; + m_fAutoInheritFromObject = TRUE; + + m_ulLastDebugLine = 0xFFFFFFFF; + m_ulLastDebugColumn = 0xFFFFFFFF; + m_ulLastDebugLineEnd = 0xFFFFFFFF; + m_ulLastDebugColumnEnd = 0xFFFFFFFF; + m_pSymWriter = NULL; + m_pSymDocument = NULL; + m_dwIncludeDebugInfo = 0; + m_fGeneratePDB = FALSE; + m_fIsMscorlib = FALSE; + m_fOptimize = FALSE; + m_tkSysObject = 0; + m_tkSysString = 0; + m_tkSysValue = 0; + m_tkSysEnum = 0; + + m_pVTable = NULL; + m_pMarshal = NULL; + m_pPInvoke = NULL; + + m_fReportProgress = TRUE; + m_tkCurrentCVOwner = 1; // module + m_pOutputBuffer = NULL; + + m_dwSubsystem = (DWORD)-1; + m_dwComImageFlags = COMIMAGE_FLAGS_ILONLY; + m_dwFileAlignment = 0; + m_stBaseAddress = 0; + m_stSizeOfStackReserve = 0; + m_dwCeeFileFlags = ICEE_CREATE_FILE_PURE_IL; + + g_szSourceFileName[0] = 0; + + m_guidLang = CorSym_LanguageType_ILAssembly; + m_guidLangVendor = CorSym_LanguageVendor_Microsoft; + m_guidDoc = CorSym_DocumentType_Text; + for(int i=0; im_szFQN = NULL; + delete dummyClass; + + if (m_pOutputBuffer) delete [] m_pOutputBuffer; + if (m_crImplList) delete [] m_crImplList; + if (m_TyParList) delete m_TyParList; + + if (m_pCeeFileGen != NULL) { + if (m_pCeeFile) + m_pCeeFileGen->DestroyCeeFile(&m_pCeeFile); + MscorpeSxS::DestroyICeeFileGen(&m_pCeeFileGen); + m_pCeeFileGen = NULL; + } + + while((m_szNamespace = m_NSstack.POP())) ; + delete [] m_szFullNS; + + m_DocWriterList.RESET(true); + + m_MethodBodyList.RESET(true); + + m_TypeDefDList.RESET(true); + + if (m_pSymWriter != NULL) + { + m_pSymWriter->Close(); + m_pSymWriter->Release(); + m_pSymWriter = NULL; + } + if (m_pImporter != NULL) + { + m_pImporter->Release(); + m_pImporter = NULL; + } + if (m_pEmitter != NULL) + { + m_pEmitter->Release(); + m_pEmitter = NULL; + } + + if (m_pDisp != NULL) + { + m_pDisp->Release(); + m_pDisp = NULL; + } + + if (m_fDidCoInitialise) + CoUninitialize(); + +} + + +BOOL Assembler::Init() +{ + if(!m_fDidCoInitialise) + { + if (FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED))) + return FALSE; + m_fDidCoInitialise = TRUE; + } + if (m_pCeeFileGen != NULL) { + if (m_pCeeFile) + m_pCeeFileGen->DestroyCeeFile(&m_pCeeFile); + MscorpeSxS::DestroyICeeFileGen(&m_pCeeFileGen); + m_pCeeFileGen = NULL; + } + if (FAILED(MscorpeSxS::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; + + return TRUE; +} + +void Assembler::SetDLL(BOOL IsDll) +{ + HRESULT OK; + OK = m_pCeeFileGen->SetDllSwitch(m_pCeeFile, IsDll); + _ASSERTE(SUCCEEDED(OK)); + + m_fDLL = IsDll; +} + +void Assembler::SetOBJ(BOOL IsObj) +{ + HRESULT OK; + OK = m_pCeeFileGen->SetObjSwitch(m_pCeeFile, IsObj); + _ASSERTE(SUCCEEDED(OK)); + + m_fOBJ = IsObj; +} + + +void Assembler::ResetArgNameList() +{ + if(m_firstArgName) delArgNameList(m_firstArgName); + m_firstArgName = NULL; + m_lastArgName = NULL; +} + +void Assembler::ResetForNextMethod() +{ + + ResetArgNameList(); + + m_CurPC = 0; + m_pCurOutputPos = m_pOutputBuffer; + m_State = STATE_OK; + m_pCurMethod = NULL; +} + +void Assembler::ResetLineNumbers() +{ + // reset line number information + m_ulLastDebugLine = 0xFFFFFFFF; + m_ulLastDebugColumn = 0xFFFFFFFF; + m_ulLastDebugLineEnd = 0xFFFFFFFF; + m_ulLastDebugColumnEnd = 0xFFFFFFFF; +} + +BOOL Assembler::AddMethod(Method *pMethod) +{ + BOOL fIsInterface=FALSE, fIsImport=FALSE; + ULONG PEFileOffset=0; + + _ASSERTE(m_pCeeFileGen != NULL); + if (pMethod == NULL) + { + report->error("pMethod == NULL"); + return FALSE; + } + if(pMethod->m_pClass != NULL) + { + fIsInterface = IsTdInterface(pMethod->m_pClass->m_Attr); + fIsImport = IsTdImport(pMethod->m_pClass->m_Attr); + } + if(m_CurPC) + { + char sz[1024]; + sz[0] = 0; + if(fIsInterface && (!IsMdStatic(pMethod->m_Attr))) strcat_s(sz,1024," non-static declared in interface"); + if(fIsImport) strcat_s(sz,1024," imported"); + if(IsMdAbstract(pMethod->m_Attr)) strcat_s(sz,1024," abstract"); + if(IsMdPinvokeImpl(pMethod->m_Attr)) strcat_s(sz,1024," pinvoke"); + if(!IsMiIL(pMethod->m_wImplAttr)) strcat_s(sz,1024," non-IL"); + if(IsMiRuntime(pMethod->m_wImplAttr)) strcat_s(sz,1024," runtime-supplied"); + if(IsMiInternalCall(pMethod->m_wImplAttr)) strcat_s(sz,1024," an internal call"); + if(strlen(sz)) + { + report->error("Method cannot have body if it is%s\n",sz); + } + } + else // method has no body + { + if(fIsImport || IsMdAbstract(pMethod->m_Attr) || IsMdPinvokeImpl(pMethod->m_Attr) + || IsMiRuntime(pMethod->m_wImplAttr) || IsMiInternalCall(pMethod->m_wImplAttr)) return TRUE; + if(OnErrGo) + { + report->error("Method has no body\n"); + return TRUE; + } + else + { + report->warn("Method has no body, 'ret' emitted\n"); + Instr* pIns = GetInstr(); + if(pIns) + { + memset(pIns,0,sizeof(Instr)); + pIns->opcode = CEE_RET; + EmitOpcode(pIns); + } + } + } + + if(pMethod->m_Locals.COUNT()) pMethod->m_LocalsSig=0x11000001; // placeholder, the real token 2b defined in EmitMethod + + COR_ILMETHOD_FAT fatHeader; + fatHeader.SetFlags(pMethod->m_Flags); + fatHeader.SetMaxStack(pMethod->m_MaxStack); + fatHeader.SetLocalVarSigTok(pMethod->m_LocalsSig); + fatHeader.SetCodeSize(m_CurPC); + bool moreSections = (pMethod->m_dwNumExceptions != 0); + + // if max stack is specified <8, force fat header, otherwise (with tiny header) it will default to 8 + if((fatHeader.GetMaxStack() < 8)&&(fatHeader.GetLocalVarSigTok()==0)&&(fatHeader.GetCodeSize()<64)&&(!moreSections)) + fatHeader.SetFlags(fatHeader.GetFlags() | CorILMethod_InitLocals); //forces fat header but does nothing else, since LocalVarSigTok==0 + + unsigned codeSize = m_CurPC; + unsigned codeSizeAligned = codeSize; + if (moreSections) + codeSizeAligned = (codeSizeAligned + 3) & ~3; // to insure EH section aligned + + unsigned headerSize = COR_ILMETHOD::Size(&fatHeader, moreSections); + unsigned ehSize = COR_ILMETHOD_SECT_EH::Size(pMethod->m_dwNumExceptions, pMethod->m_ExceptionList); + unsigned totalSize = headerSize + codeSizeAligned + ehSize; + + BYTE* outBuff; + BYTE* endbuf; + BinStr* pbsBody; + if((pbsBody = new BinStr())==NULL) return FALSE; + if((outBuff = pbsBody->getBuff(totalSize))==NULL) return FALSE; + endbuf = &outBuff[totalSize]; + + // Emit the header + outBuff += COR_ILMETHOD::Emit(headerSize, &fatHeader, moreSections, outBuff); + + pMethod->m_pCode = outBuff; + pMethod->m_headerOffset= PEFileOffset; + pMethod->m_methodOffset= PEFileOffset + headerSize; + pMethod->m_CodeSize = codeSize; + + // Emit the code + if (codeSizeAligned) + { + memset(outBuff,0,codeSizeAligned); + memcpy(outBuff, m_pOutputBuffer, codeSize); + outBuff += codeSizeAligned; + } + + if(pMethod->m_dwNumExceptions) + { + // Validate the eh + COR_ILMETHOD_SECT_EH_CLAUSE_FAT* pEx; + DWORD TryEnd,HandlerEnd, dwEx, dwEf; + for(dwEx = 0, pEx = pMethod->m_ExceptionList; dwEx < pMethod->m_dwNumExceptions; dwEx++, pEx++) + { + if(pEx->GetTryOffset() > m_CurPC) // i.e., pMethod->m_CodeSize + { + report->error("Invalid SEH clause #%d: Try block starts beyond code size\n",dwEx+1); + } + TryEnd = pEx->GetTryOffset()+pEx->GetTryLength(); + if(TryEnd > m_CurPC) + { + report->error("Invalid SEH clause #%d: Try block ends beyond code size\n",dwEx+1); + } + if(pEx->GetHandlerOffset() > m_CurPC) + { + report->error("Invalid SEH clause #%d: Handler block starts beyond code size\n",dwEx+1); + } + HandlerEnd = pEx->GetHandlerOffset()+pEx->GetHandlerLength(); + if(HandlerEnd > m_CurPC) + { + report->error("Invalid SEH clause #%d: Handler block ends beyond code size\n",dwEx+1); + } + if(pEx->Flags & COR_ILEXCEPTION_CLAUSE_FILTER) + { + if(!((pEx->GetFilterOffset() >= TryEnd)||(pEx->GetTryOffset() >= HandlerEnd))) + { + report->error("Invalid SEH clause #%d: Try and Filter/Handler blocks overlap\n",dwEx+1); + } + for(dwEf = 0; dwEf < pMethod->m_dwNumEndfilters; dwEf++) + { + if(pMethod->m_EndfilterOffsetList[dwEf] == pEx->GetHandlerOffset()) break; + } + if(dwEf >= pMethod->m_dwNumEndfilters) + { + report->error("Invalid SEH clause #%d: Filter block separated from Handler, or not ending with endfilter\n",dwEx+1); + } + } + else + if(!((pEx->GetHandlerOffset() >= TryEnd)||(pEx->GetTryOffset() >= HandlerEnd))) + { + report->error("Invalid SEH clause #%d: Try and Handler blocks overlap\n",dwEx+1); + } + + } + // Emit the eh + outBuff += COR_ILMETHOD_SECT_EH::Emit(ehSize, pMethod->m_dwNumExceptions, + pMethod->m_ExceptionList, false, outBuff); + } + _ASSERTE(outBuff == endbuf); + + pMethod->m_pbsBody = pbsBody; + + LocalMemberRefFixup* pMRF; + while((pMRF = pMethod->m_LocalMemberRefFixupList.POP())) + { + pMRF->offset += (size_t)(pMethod->m_pCode); + m_LocalMemberRefFixupList.PUSH(pMRF); // transfer MRF to assembler's list + } + + if(m_fReportProgress) + { + if (pMethod->IsGlobalMethod()) + report->msg("Assembled global method %s\n", pMethod->m_szName); + else report->msg("Assembled method %s::%s\n", pMethod->m_pClass->m_szFQN, + pMethod->m_szName); + } + return TRUE; +} + + +BOOL Assembler::EmitMethodBody(Method* pMethod, BinStr* pbsOut) +{ + if(pMethod) + { + BinStr* pbsBody = pMethod->m_pbsBody; + unsigned totalSize; + if(pbsBody && (totalSize = pbsBody->length())) + { + unsigned headerSize = pMethod->m_methodOffset-pMethod->m_headerOffset; + MethodBody* pMB = NULL; + // ----------emit locals signature------------------- + unsigned uLocals; + if((uLocals = pMethod->m_Locals.COUNT())) + { + VarDescr* pVD; + BinStr* pbsSig = new BinStr(); + unsigned cnt; + HRESULT hr; + DWORD cSig; + const COR_SIGNATURE* mySig; + + pbsSig->appendInt8(IMAGE_CEE_CS_CALLCONV_LOCAL_SIG); + cnt = CorSigCompressData(uLocals,pbsSig->getBuff(5)); + pbsSig->remove(5-cnt); + for(cnt = 0; (pVD = pMethod->m_Locals.PEEK(cnt)); cnt++) + { + if(pVD->pbsSig) pbsSig->append(pVD->pbsSig); + else + { + report->error("Undefined type of local var slot %d in method %s\n",cnt,pMethod->m_szName); + pbsSig->appendInt8(ELEMENT_TYPE_I4); + } + } + + cSig = pbsSig->length(); + mySig = (const COR_SIGNATURE *)(pbsSig->ptr()); + + if (cSig > 1) // non-empty signature + { + hr = m_pEmitter->GetTokenFromSig(mySig, cSig, &pMethod->m_LocalsSig); + _ASSERTE(SUCCEEDED(hr)); + } + delete pbsSig; + COR_ILMETHOD_FAT* pFH; // Fat header guaranteed, because there are local vars + pFH = (COR_ILMETHOD_FAT*)(pMethod->m_pbsBody->ptr()); + pFH->SetLocalVarSigTok(pMethod->m_LocalsSig); + } + //-------------------------------------------------------------------------------- + if(m_fGeneratePDB && (m_pSymWriter != NULL)) + { + m_pSymWriter->OpenMethod(pMethod->m_Tok); + ULONG N = pMethod->m_LinePCList.COUNT(); + if(pMethod->m_fEntryPoint) m_pSymWriter->SetUserEntryPoint(pMethod->m_Tok); + if(N) + { + LinePC *pLPC; + ULONG32 *offsets=new ULONG32[N], *lines = new ULONG32[N], *columns = new ULONG32[N]; + ULONG32 *endlines=new ULONG32[N], *endcolumns=new ULONG32[N]; + if(offsets && lines && columns && endlines && endcolumns) + { + DocWriter* pDW; + unsigned j=0; + while((pDW = m_DocWriterList.PEEK(j++))) + { + if((m_pSymDocument = pDW->pWriter)) + { + int i, n; + for(i=0, n=0; (pLPC = pMethod->m_LinePCList.PEEK(i)); i++) + { + if(pLPC->pWriter == m_pSymDocument) + { + offsets[n] = pLPC->PC; + lines[n] = pLPC->Line; + columns[n] = pLPC->Column; + endlines[n] = pLPC->LineEnd; + endcolumns[n] = pLPC->ColumnEnd; + n++; + } + } + if(n) m_pSymWriter->DefineSequencePoints(m_pSymDocument,n, + offsets,lines,columns,endlines,endcolumns); + } // end if(pSymDocument) + } // end while(pDW = next doc.writer) + pMethod->m_LinePCList.RESET(true); + } + else report->error("\nOutOfMemory!\n"); + delete [] offsets; + delete [] lines; + delete [] columns; + delete [] endlines; + delete [] endcolumns; + }//enf if(N) + HRESULT hrr; + if(pMethod->m_ulLines[1]) + hrr = m_pSymWriter->SetMethodSourceRange(m_pSymDocument,pMethod->m_ulLines[0], pMethod->m_ulColumns[0], + m_pSymDocument,pMethod->m_ulLines[1], pMethod->m_ulColumns[1]); + EmitScope(&(pMethod->m_MainScope)); // recursively emits all nested scopes + + m_pSymWriter->CloseMethod(); + } // end if(fIncludeDebugInfo) + //----------------------------------------------------- + + if(m_fFoldCode) + { + for(int k=0; (pMB = m_MethodBodyList.PEEK(k)) != NULL; k++) + { + if((pMB->pbsBody->length() == totalSize) + && (memcmp(pMB->pbsBody->ptr(), pbsBody->ptr(),totalSize)==0)) + break; + } + if(pMB) + { + pMethod->m_headerOffset= pMB->RVA; + pMethod->m_methodOffset= pMB->RVA + headerSize; + pMethod->m_pCode = pMB->pCode; + delete pbsBody; + pMethod->m_pbsBody = NULL; + m_dwMethodsFolded++; + } + } + if(pMB == NULL) + { + BYTE* outBuff; + unsigned align = (headerSize == 1)? 1 : 4; + ULONG PEFileOffset, methodRVA; + if(m_fENCMode) + { + if(pbsOut) + { + PEFileOffset = pbsOut->length(); + align--; + while(PEFileOffset & align) + { + pbsOut->appendInt8(0); + PEFileOffset++; + } + pbsOut->append(pbsBody); + outBuff = (BYTE*)(pbsOut->ptr()) + (pbsOut->length() - pbsBody->length()); + } + else return FALSE; + + } + else + { + if (FAILED(m_pCeeFileGen->GetSectionBlock (m_pILSection, totalSize, + align, (void **) &outBuff))) return FALSE; + memcpy(outBuff,pbsBody->ptr(),totalSize); + // The offset where we start, (not where the alignment bytes start! + if (FAILED(m_pCeeFileGen->GetSectionDataLen (m_pILSection, &PEFileOffset))) + return FALSE; + PEFileOffset -= totalSize; + } + + pMethod->m_pCode = outBuff + headerSize; + pMethod->m_headerOffset= PEFileOffset; + pMethod->m_methodOffset= PEFileOffset + headerSize; + DoDeferredILFixups(pMethod); + + if(m_fENCMode) methodRVA = PEFileOffset; + else m_pCeeFileGen->GetMethodRVA(m_pCeeFile, PEFileOffset,&methodRVA); + + pMethod->m_headerOffset= methodRVA; + pMethod->m_methodOffset= methodRVA + headerSize; + if(m_fFoldCode) + { + if((pMB = new MethodBody)==NULL) return FALSE; + pMB->pbsBody = pbsBody; + pMB->RVA = methodRVA; + pMB->pCode = pMethod->m_pCode; + m_MethodBodyList.PUSH(pMB); + } + //else + // delete pbsBody; + //pMethod->m_pbsBody = NULL; + } + m_pEmitter->SetRVA(pMethod->m_Tok,pMethod->m_headerOffset); + } + return TRUE; + } + else return FALSE; +} + +ImportDescriptor* Assembler::EmitImport(BinStr* DllName) +{ + int i = 0, l = 0; + ImportDescriptor* pID; + char* sz=NULL; + + if(DllName) l = DllName->length(); // No zero terminator here! + if(l) + { + sz = (char*)DllName->ptr(); + while((pID=m_ImportList.PEEK(i++))) + { + if((pID->dwDllName== (DWORD) l)&& !memcmp(pID->szDllName,sz,l)) return pID; + } + } + else + { + while((pID=m_ImportList.PEEK(i++))) + { + if(pID->dwDllName==0) return pID; + } + } + if((pID = new ImportDescriptor(sz,l))) + { + m_ImportList.PUSH(pID); + pID->mrDll = TokenFromRid(m_ImportList.COUNT(),mdtModuleRef); + return pID; + } + else report->error("Failed to allocate import descriptor\n"); + return NULL; +} + +void Assembler::EmitImports() +{ + WCHAR* wzDllName=&wzUniBuf[0]; + ImportDescriptor* pID; + int i; + mdToken tk; + for(i=0; (pID = m_ImportList.PEEK(i)); i++) + { + WszMultiByteToWideChar(g_uCodePage,0,pID->szDllName,-1,wzDllName,dwUniBuf-1); + if(FAILED(m_pEmitter->DefineModuleRef( // S_OK or error. + wzDllName, // [IN] DLL name + &tk))) // [OUT] returned + report->error("Failed to define module ref '%s'\n",pID->szDllName); + else + _ASSERTE(tk == pID->mrDll); + } +} + +HRESULT Assembler::EmitPinvokeMap(mdToken tk, PInvokeDescriptor* pDescr) +{ + WCHAR* wzAlias=&wzUniBuf[0]; + + if(pDescr->szAlias) WszMultiByteToWideChar(g_uCodePage,0,pDescr->szAlias,-1,wzAlias,dwUniBuf-1); + + return m_pEmitter->DefinePinvokeMap( // Return code. + tk, // [IN] FieldDef, MethodDef or MethodImpl. + pDescr->dwAttrs, // [IN] Flags used for mapping. + (LPCWSTR)wzAlias, // [IN] Import name. + pDescr->mrDll); // [IN] ModuleRef token for the target DLL. +} + +void Assembler::EmitScope(Scope* pSCroot) +{ + static ULONG32 scopeID; + static ARG_NAME_LIST *pVarList; + int i; + WCHAR* wzVarName=&wzUniBuf[0]; + char* szPhonyName=(char*)&wzUniBuf[dwUniBuf >> 1]; + Scope* pSC = pSCroot; + if(pSC && m_pSymWriter) + { + if(SUCCEEDED(m_pSymWriter->OpenScope(pSC->dwStart,&scopeID))) + { + if(pSC->pLocals) + { + for(pVarList = pSC->pLocals; pVarList; pVarList = pVarList->pNext) + { + if(pVarList->pSig) + { + if((pVarList->szName)&&(*(pVarList->szName))) strcpy_s(szPhonyName,dwUniBuf >> 1,pVarList->szName); + else sprintf_s(szPhonyName,(dwUniBuf >> 1),"V_%d",pVarList->dwAttr); + + WszMultiByteToWideChar(g_uCodePage,0,szPhonyName,-1,wzVarName,dwUniBuf >> 1); + + m_pSymWriter->DefineLocalVariable(wzVarName,0,pVarList->pSig->length(), + (BYTE*)pVarList->pSig->ptr(),ADDR_IL_OFFSET,pVarList->dwAttr,0,0,0,0); + } + else + { + report->error("Local Var '%s' has no signature\n",pVarList->szName); + } + } + } + for(i = 0; (pSC = pSCroot->SubScope.PEEK(i)); i++) EmitScope(pSC); + m_pSymWriter->CloseScope(pSCroot->dwEnd); + } + } +} + +BOOL Assembler::EmitMethod(Method *pMethod) +{ +// Emit the metadata for a method definition + BOOL fSuccess = FALSE; + WCHAR* wzMemberName=&wzUniBuf[0]; + BOOL fIsInterface; + DWORD cSig; + ULONG methodRVA = 0; + mdMethodDef MethodToken; + mdTypeDef ClassToken = mdTypeDefNil; + char *pszMethodName; + COR_SIGNATURE *mySig; + + _ASSERTE((m_pCeeFileGen != NULL) && (pMethod != NULL)); + fIsInterface = ((pMethod->m_pClass != NULL) && IsTdInterface(pMethod->m_pClass->m_Attr)); + + + pszMethodName = pMethod->m_szName; + mySig = pMethod->m_pMethodSig; + cSig = pMethod->m_dwMethodCSig; + + // If this is an instance method, make certain the signature says so + + if (!(pMethod->m_Attr & mdStatic)) + *mySig |= IMAGE_CEE_CS_CALLCONV_HASTHIS; + + ClassToken = (pMethod->IsGlobalMethod())? mdTokenNil + : pMethod->m_pClass->m_cl; + // Convert name to UNICODE + WszMultiByteToWideChar(g_uCodePage,0,pszMethodName,-1,wzMemberName,dwUniBuf-1); + + if(IsMdPrivateScope(pMethod->m_Attr)) + { + WCHAR* p = wcsstr(wzMemberName,L"$PST06"); + if(p) *p = 0; + } + + if (FAILED(m_pEmitter->DefineMethod(ClassToken, // parent class + wzMemberName, // member name + pMethod->m_Attr & ~mdReservedMask, // member attributes + mySig, // member signature + cSig, + methodRVA, // RVA + pMethod->m_wImplAttr, // implflags + &MethodToken))) + { + report->error("Failed to define method '%s'\n",pszMethodName); + goto exit; + } + pMethod->m_Tok = MethodToken; + //-------------------------------------------------------------------------------- + // the only way to set mdRequireSecObject: + if(pMethod->m_Attr & mdRequireSecObject) + { + mdToken tkPseudoClass; + if(FAILED(m_pEmitter->DefineTypeRefByName(1, COR_REQUIRES_SECOBJ_ATTRIBUTE, &tkPseudoClass))) + report->error("Unable to define type reference '%s'\n", COR_REQUIRES_SECOBJ_ATTRIBUTE_ANSI); + else + { + mdToken tkPseudoCtor; + BYTE bSig[3] = {IMAGE_CEE_CS_CALLCONV_HASTHIS,0,ELEMENT_TYPE_VOID}; + if(FAILED(m_pEmitter->DefineMemberRef(tkPseudoClass, L".ctor", (PCCOR_SIGNATURE)bSig, 3, &tkPseudoCtor))) + report->error("Unable to define member reference '%s::.ctor'\n", COR_REQUIRES_SECOBJ_ATTRIBUTE_ANSI); + else DefineCV(new CustomDescr(MethodToken,tkPseudoCtor,NULL)); + } + } + + if (pMethod->m_NumTyPars) + { + ULONG i; + mdToken* ptk; + mdToken tk; + for(i = 0; i < pMethod->m_NumTyPars; i++) + { + //ptk = (pMethod->m_TyParBounds[i] == NULL)? NULL : (mdToken*)(pMethod->m_TyParBounds[i]->ptr()); + //if(FAILED(m_pEmitter->DefineGenericParam(MethodToken,i,0,pMethod->m_TyParNames[i],0,ptk,&tk))) + ptk = (pMethod->m_TyPars[i].Bounds() == NULL)? NULL : (mdToken*)(pMethod->m_TyPars[i].Bounds()->ptr()); + if(FAILED(m_pEmitter->DefineGenericParam(MethodToken,i,pMethod->m_TyPars[i].Attrs(),pMethod->m_TyPars[i].Name(),0,ptk,&tk))) + report->error("Unable to define generic param'\n"); + else + EmitCustomAttributes(tk, pMethod->m_TyPars[i].CAList()); + } + } + //-------------------------------------------------------------------------------- + EmitSecurityInfo(MethodToken, + pMethod->m_pPermissions, + pMethod->m_pPermissionSets); + //-------------------------------------------------------------------------------- + if (pMethod->m_fEntryPoint) + { + if(fIsInterface) report->error("Entrypoint in Interface: Method '%s'\n",pszMethodName); + + if (FAILED(m_pCeeFileGen->SetEntryPoint(m_pCeeFile, MethodToken))) + { + report->error("Failed to set entry point for method '%s'\n",pszMethodName); + goto exit; + } + + } + //-------------------------------------------------------------------------------- + if(IsMdPinvokeImpl(pMethod->m_Attr)) + { + if(pMethod->m_pPInvoke) + { + HRESULT hr; + if(pMethod->m_pPInvoke->szAlias == NULL) pMethod->m_pPInvoke->szAlias = pszMethodName; + hr = EmitPinvokeMap(MethodToken,pMethod->m_pPInvoke); + if(pMethod->m_pPInvoke->szAlias == pszMethodName) pMethod->m_pPInvoke->szAlias = NULL; + + if(FAILED(hr)) + { + report->error("Failed to set PInvoke map for method '%s'\n",pszMethodName); + goto exit; + } + } + } + + { // add parameters to metadata + void const *pValue=NULL; + ULONG cbValue; + DWORD dwCPlusTypeFlag=0; + mdParamDef pdef; + WCHAR* wzParName=&wzUniBuf[0]; + char* szPhonyName=(char*)&wzUniBuf[dwUniBuf >> 1]; + if(pMethod->m_dwRetAttr || pMethod->m_pRetMarshal || pMethod->m_RetCustDList.COUNT()) + { + if(pMethod->m_pRetValue) + { + dwCPlusTypeFlag= (DWORD)*(pMethod->m_pRetValue->ptr()); + pValue = (void const *)(pMethod->m_pRetValue->ptr()+1); + cbValue = pMethod->m_pRetValue->length()-1; + if(dwCPlusTypeFlag == ELEMENT_TYPE_STRING) cbValue /= sizeof(WCHAR); + } + else + { + pValue = NULL; + cbValue = (ULONG)-1; + dwCPlusTypeFlag=0; + } + m_pEmitter->DefineParam(MethodToken,0,NULL,pMethod->m_dwRetAttr,dwCPlusTypeFlag,pValue,cbValue,&pdef); + + if(pMethod->m_pRetMarshal) + { + if(FAILED(m_pEmitter->SetFieldMarshal ( + pdef, // [IN] given a fieldDef or paramDef token + (PCCOR_SIGNATURE)(pMethod->m_pRetMarshal->ptr()), // [IN] native type specification + pMethod->m_pRetMarshal->length()))) // [IN] count of bytes of pvNativeType + report->error("Failed to set param marshaling for return\n"); + + } + EmitCustomAttributes(pdef, &(pMethod->m_RetCustDList)); + } + for(ARG_NAME_LIST *pAN=pMethod->m_firstArgName; pAN; pAN = pAN->pNext) + { + if(pAN->nNum >= 65535) + { + report->error("Method '%s': Param.sequence number (%d) exceeds 65535, unable to define parameter\n",pszMethodName,pAN->nNum+1); + continue; + } + if(pAN->dwName) strcpy_s(szPhonyName,dwUniBuf >> 1,pAN->szName); + else sprintf_s(szPhonyName,(dwUniBuf >> 1),"A_%d",pAN->nNum); + + WszMultiByteToWideChar(g_uCodePage,0,szPhonyName,-1,wzParName,dwUniBuf >> 1); + + if(pAN->pValue) + { + dwCPlusTypeFlag= (DWORD)*(pAN->pValue->ptr()); + pValue = (void const *)(pAN->pValue->ptr()+1); + cbValue = pAN->pValue->length()-1; + if(dwCPlusTypeFlag == ELEMENT_TYPE_STRING) cbValue /= sizeof(WCHAR); + } + else + { + pValue = NULL; + cbValue = (ULONG)-1; + dwCPlusTypeFlag=0; + } + m_pEmitter->DefineParam(MethodToken,pAN->nNum+1,wzParName,pAN->dwAttr,dwCPlusTypeFlag,pValue,cbValue,&pdef); + if(pAN->pMarshal) + { + if(FAILED(m_pEmitter->SetFieldMarshal ( + pdef, // [IN] given a fieldDef or paramDef token + (PCCOR_SIGNATURE)(pAN->pMarshal->ptr()), // [IN] native type specification + pAN->pMarshal->length()))) // [IN] count of bytes of pvNativeType + report->error("Failed to set param marshaling for '%s'\n",pAN->szName); + } + EmitCustomAttributes(pdef, &(pAN->CustDList)); + } + } + fSuccess = TRUE; + //-------------------------------------------------------------------------------- + // Update method implementations for this method + { + MethodImplDescriptor* pMID; + int i; + for(i=0;(pMID = pMethod->m_MethodImplDList.PEEK(i));i++) + { + pMID->m_tkImplementingMethod = MethodToken; + // don't delete it here, it's still in the general list + } + } + //-------------------------------------------------------------------------------- + EmitCustomAttributes(MethodToken, &(pMethod->m_CustomDescrList)); +exit: + if (fSuccess == FALSE) m_State = STATE_FAIL; + return fSuccess; +} + +BOOL Assembler::EmitMethodImpls() +{ + MethodImplDescriptor* pMID; + BOOL ret = TRUE; + int i; + for(i=0; (pMID = m_MethodImplDList.PEEK(i)); i++) + { + if(m_fENCMode && (!pMID->m_fNew)) continue; + pMID->m_tkImplementingMethod = ResolveLocalMemberRef(pMID->m_tkImplementingMethod); + pMID->m_tkImplementedMethod = ResolveLocalMemberRef(pMID->m_tkImplementedMethod); + if(FAILED(m_pEmitter->DefineMethodImpl( pMID->m_tkDefiningClass, + pMID->m_tkImplementingMethod, + pMID->m_tkImplementedMethod))) + { + report->error("Failed to define Method Implementation"); + ret = FALSE; + } + pMID->m_fNew = FALSE; + }// end while + return ret; +} + +mdToken Assembler::ResolveLocalMemberRef(mdToken tok) +{ + if(TypeFromToken(tok) == 0x99000000) + { + tok = RidFromToken(tok); + if(tok) tok = m_LocalMethodRefDList.PEEK(tok-1)->m_tkResolved; + } + else if(TypeFromToken(tok) == 0x98000000) + { + tok = RidFromToken(tok); + if(tok) tok = m_LocalFieldRefDList.PEEK(tok-1)->m_tkResolved; + } + return tok; +} + +BOOL Assembler::EmitEvent(EventDescriptor* pED) +{ + mdMethodDef mdAddOn=mdMethodDefNil, + mdRemoveOn=mdMethodDefNil, + mdFire=mdMethodDefNil, + *mdOthers; + int nOthers; + WCHAR* wzMemberName=&wzUniBuf[0]; + + if(!pED) return FALSE; + + WszMultiByteToWideChar(g_uCodePage,0,pED->m_szName,-1,wzMemberName,dwUniBuf-1); + + mdAddOn = ResolveLocalMemberRef(pED->m_tkAddOn); + if(TypeFromToken(mdAddOn) != mdtMethodDef) + { + report->error("Invalid Add method of event '%s'\n",pED->m_szName); + return FALSE; + } + mdRemoveOn = ResolveLocalMemberRef(pED->m_tkRemoveOn); + if(TypeFromToken(mdRemoveOn) != mdtMethodDef) + { + report->error("Invalid Remove method of event '%s'\n",pED->m_szName); + return FALSE; + } + mdFire = ResolveLocalMemberRef(pED->m_tkFire); + if((RidFromToken(mdFire)!=0)&&(TypeFromToken(mdFire) != mdtMethodDef)) + { + report->error("Invalid Fire method of event '%s'\n",pED->m_szName); + return FALSE; + } + + nOthers = pED->m_tklOthers.COUNT(); + mdOthers = new mdMethodDef[nOthers+1]; + if(mdOthers == NULL) + { + report->error("Failed to allocate Others array for event descriptor\n"); + nOthers = 0; + } + for(int j=0; j < nOthers; j++) + { + mdOthers[j] = ResolveLocalMemberRef((mdToken)(UINT_PTR)(pED->m_tklOthers.PEEK(j))); // @WARNING: casting down from 'mdToken*' to 'mdToken' + } + mdOthers[nOthers] = mdMethodDefNil; // like null-terminator + + if(FAILED(m_pEmitter->DefineEvent( pED->m_tdClass, + wzMemberName, + pED->m_dwAttr, + pED->m_tkEventType, + mdAddOn, + mdRemoveOn, + mdFire, + mdOthers, + &(pED->m_edEventTok)))) + { + report->error("Failed to define event '%s'.\n",pED->m_szName); + delete [] mdOthers; + return FALSE; + } + EmitCustomAttributes(pED->m_edEventTok, &(pED->m_CustomDescrList)); + return TRUE; +} + +BOOL Assembler::EmitProp(PropDescriptor* pPD) +{ + mdMethodDef mdSet, mdGet, *mdOthers; + int nOthers; + WCHAR* wzMemberName=&wzUniBuf[0]; + + if(!pPD) return FALSE; + + WszMultiByteToWideChar(g_uCodePage,0,pPD->m_szName,-1,wzMemberName,dwUniBuf-1); + + mdSet = ResolveLocalMemberRef(pPD->m_tkSet); + if((RidFromToken(mdSet)!=0)&&(TypeFromToken(mdSet) != mdtMethodDef)) + { + report->error("Invalid Set method of property '%s'\n",pPD->m_szName); + return FALSE; + } + mdGet = ResolveLocalMemberRef(pPD->m_tkGet); + if((RidFromToken(mdGet)!=0)&&(TypeFromToken(mdGet) != mdtMethodDef)) + { + report->error("Invalid Get method of property '%s'\n",pPD->m_szName); + return FALSE; + } + + nOthers = pPD->m_tklOthers.COUNT(); + mdOthers = new mdMethodDef[nOthers+1]; + if(mdOthers == NULL) + { + report->error("Failed to allocate Others array for prop descriptor\n"); + nOthers = 0; + } + for(int j=0; j < nOthers; j++) + { + mdOthers[j] = ResolveLocalMemberRef((mdToken)(UINT_PTR)(pPD->m_tklOthers.PEEK(j))); // @WARNING: casting down from 'mdToken*' to 'mdToken' + + if((RidFromToken(mdOthers[j])!=0)&&(TypeFromToken(mdOthers[j]) != mdtMethodDef)) + { + report->error("Invalid Other method of property '%s'\n",pPD->m_szName); + delete [] mdOthers; + return FALSE; + } + + } + mdOthers[nOthers] = mdMethodDefNil; // like null-terminator + + if(FAILED(m_pEmitter->DefineProperty( pPD->m_tdClass, + wzMemberName, + pPD->m_dwAttr, + pPD->m_pSig, + pPD->m_dwCSig, + pPD->m_dwCPlusTypeFlag, + pPD->m_pValue, + pPD->m_cbValue, + mdSet, + mdGet, + mdOthers, + &(pPD->m_pdPropTok)))) + { + report->error("Failed to define property '%s'.\n",pPD->m_szName); + delete [] mdOthers; + return FALSE; + } + EmitCustomAttributes(pPD->m_pdPropTok, &(pPD->m_CustomDescrList)); + return TRUE; +} + +Class *Assembler::FindCreateClass(__in __nullterminated char *pszFQN) +{ + Class *pSearch = NULL; + + if(pszFQN) + { + dummyClass->m_szFQN = pszFQN; + dummyClass->m_Hash = hash((BYTE*)pszFQN, (unsigned)strlen(pszFQN), 10); + pSearch = m_hshClass.FIND(dummyClass); + dummyClass->m_szFQN = NULL; + dummyClass->m_Hash = 0; + + if(!pSearch) + { + char* pch; + DWORD dwFQN = (DWORD)strlen(pszFQN); + + Class *pEncloser = NULL; + char* pszNewFQN = new char[dwFQN+1]; + strcpy_s(pszNewFQN,dwFQN+1,pszFQN); + if((pch = strrchr(pszNewFQN, NESTING_SEP)) != NULL) + { + *pch = 0; + pEncloser = FindCreateClass(pszNewFQN); + *pch = NESTING_SEP; + } + pSearch = new Class(pszNewFQN); + if (pSearch == NULL) + report->error("Failed to create class '%s'\n",pszNewFQN); + else + { + pSearch->m_pEncloser = pEncloser; + m_lstClass.PUSH(pSearch); + pSearch->m_cl = mdtTypeDef | m_lstClass.COUNT(); + m_hshClass.PUSH(pSearch); + } + } + } + + return pSearch; +} + + +BOOL Assembler::EmitClass(Class *pClass) +{ + LPCUTF8 szFullName; + WCHAR* wzFullName=&wzUniBuf[0]; + HRESULT hr = E_FAIL; + GUID guid; + size_t L; + mdToken tok; + + if(pClass == NULL) return FALSE; + + hr = CoCreateGuid(&guid); + if (FAILED(hr)) + { + printf("Unable to create GUID\n"); + m_State = STATE_FAIL; + return FALSE; + } + + if(pClass->m_pEncloser) + szFullName = strrchr(pClass->m_szFQN,NESTING_SEP) + 1; + else + szFullName = pClass->m_szFQN; + + WszMultiByteToWideChar(g_uCodePage,0,szFullName,-1,wzFullName,dwUniBuf); + + L = wcslen(wzFullName); + if((L==0)||(wzFullName[L-1]==L'.')) // Missing class name! + { + wcscat_s(wzFullName,dwUniBuf,L"$UNNAMED_TYPE$"); + } + + pClass->m_Attr = CheckClassFlagsIfNested(pClass->m_pEncloser, pClass->m_Attr); + + if (pClass->m_pEncloser) + { + hr = m_pEmitter->DefineNestedType( wzFullName, + pClass->m_Attr, // attributes + pClass->m_crExtends, // CR extends class + pClass->m_crImplements,// implements + pClass->m_pEncloser->m_cl, // Enclosing class. + &tok); + } + else + { + hr = m_pEmitter->DefineTypeDef( wzFullName, + pClass->m_Attr, // attributes + pClass->m_crExtends, // CR extends class + pClass->m_crImplements,// implements + &tok); + } + _ASSERTE(tok == pClass->m_cl); + if (FAILED(hr)) goto exit; + if (pClass->m_NumTyPars) + { + ULONG i; + mdToken* ptk; + mdToken tk; + for(i = 0; i < pClass->m_NumTyPars; i++) + { + //ptk = (pClass->m_TyParBounds[i] == NULL)? NULL : (mdToken*)(pClass->m_TyParBounds[i]->ptr()); + //if(FAILED(m_pEmitter->DefineGenericParam(pClass->m_cl,i,pClass->m_TyParAttrs[i],pClass->m_TyParNames[i],0,ptk,&tk))) + ptk = (pClass->m_TyPars[i].Bounds() == NULL)? NULL : (mdToken*)(pClass->m_TyPars[i].Bounds()->ptr()); + if(FAILED(m_pEmitter->DefineGenericParam(pClass->m_cl,i,pClass->m_TyPars[i].Attrs(),pClass->m_TyPars[i].Name(),0,ptk,&tk))) + report->error("Unable to define generic param'\n"); + else + EmitCustomAttributes(tk, pClass->m_TyPars[i].CAList()); + } + } + + + EmitCustomAttributes(pClass->m_cl, &(pClass->m_CustDList)); + hr = S_OK; + +exit: + return SUCCEEDED(hr); +} + +BOOL Assembler::DoGlobalFixups() +{ + GlobalFixup *pSearch; + + for (int i=0; (pSearch = m_lstGlobalFixup.PEEK(i)); i++) + { + GlobalLabel * pLabel = FindGlobalLabel(pSearch->m_szLabel); + if (pLabel == NULL) + { + report->error("Unable to find forward reference global label '%s'\n", + pSearch->m_szLabel); + + m_State = STATE_FAIL; + return FALSE; + } + //BYTE * pReference = pSearch->m_pReference; + //DWORD GlobalOffset = pLabel->m_GlobalOffset; + //memcpy(pReference,&GlobalOffset,4); + SET_UNALIGNED_VAL32(pSearch->m_pReference,pLabel->m_GlobalOffset); + } + + return TRUE; +} + +state_t Assembler::AddGlobalLabel(__in __nullterminated char *pszName, HCEESECTION section) +{ + if (FindGlobalLabel(pszName) != NULL) + { + report->error("Duplicate global label '%s'\n", pszName); + m_State = STATE_FAIL; + return m_State; + } + + ULONG GlobalOffset; + + HRESULT hr; + hr = m_pCeeFileGen->GetSectionDataLen(section, &GlobalOffset); + _ASSERTE(SUCCEEDED(hr)); + + GlobalLabel *pNew = new GlobalLabel(pszName, GlobalOffset, section); + if (pNew == 0) + { + report->error("Failed to allocate global label '%s'\n",pszName); + m_State = STATE_FAIL; + return m_State; + } + + m_lstGlobalLabel.PUSH(pNew); + return m_State; +} + +void Assembler::AddLabel(DWORD CurPC, __in __nullterminated char *pszName) +{ + if (m_pCurMethod->FindLabel(pszName) != NULL) + { + report->error("Duplicate label: '%s'\n", pszName); + + m_State = STATE_FAIL; + } + else + { + Label *pNew = new Label(pszName, CurPC); + + if (pNew != NULL) + //m_pCurMethod->m_lstLabel.PUSH(pNew); + m_lstLabel.PUSH(pNew); + else + { + report->error("Failed to allocate label '%s'\n",pszName); + m_State = STATE_FAIL; + } + } +} + +void Assembler::DoDeferredILFixups(Method* pMethod) +{ // Now that we know where in the file the code bytes will wind up, + // we can update the RVAs and offsets. + ILFixup *pSearch; + HRESULT hr; + GlobalFixup *Fix = NULL; + int i; + for (i=0;(pSearch = pMethod->m_lstILFixup.PEEK(i));i++) + { + switch(pSearch->m_Kind) + { + case ilGlobal: + Fix = pSearch->m_Fixup; + _ASSERTE(Fix != NULL); + Fix->m_pReference = pMethod->m_pCode+pSearch->m_OffsetInMethod; + break; + + case ilToken: + hr = m_pCeeFileGen->AddSectionReloc(m_pILSection, + pSearch->m_OffsetInMethod+pMethod->m_methodOffset, + m_pILSection, + srRelocMapToken); + _ASSERTE(SUCCEEDED(hr)); + break; + + case ilRVA: + hr = m_pCeeFileGen->AddSectionReloc(m_pILSection, + pSearch->m_OffsetInMethod+pMethod->m_methodOffset, + m_pGlobalDataSection, + srRelocAbsolute); + _ASSERTE(SUCCEEDED(hr)); + break; + + default: + ; + } + } +} +/**************************************************************************/ +BOOL Assembler::DoFixups(Method* pMethod) +{ + Fixup *pSearch; + + for (int i=0; (pSearch = pMethod->m_lstFixup.PEEK(i)); i++) + { + Label * pLabel = pMethod->FindLabel(pSearch->m_szLabel); + long offset; + + if (pLabel == NULL) + { + report->error("Unable to find forward reference label '%s' called from PC=%d\n", + pSearch->m_szLabel, pSearch->m_RelativeToPC); + + //m_State = STATE_FAIL; + return FALSE; + } + + offset = pLabel->m_PC - pSearch->m_RelativeToPC; + + if (pSearch->m_FixupSize == 1) + { + if (offset > 127 || offset < -128) + { + report->error("Offset of forward reference label '%s' called from PC=%d is too large for 1 byte pcrel\n", + pLabel->m_szName, pSearch->m_RelativeToPC); + + //m_State = STATE_FAIL; + return FALSE; + } + + *pSearch->m_pBytes = (BYTE) offset; + } + else if (pSearch->m_FixupSize == 4) + { + SET_UNALIGNED_VAL32(pSearch->m_pBytes,offset); + } + } + + return TRUE; +} + + +OPCODE Assembler::DecodeOpcode(const BYTE *pCode, DWORD *pdwLen) +{ + OPCODE opcode; + + *pdwLen = 1; + opcode = OPCODE(pCode[0]); + switch(opcode) { + case CEE_PREFIX1: + opcode = OPCODE(pCode[1] + 256); + if (opcode < 0 || opcode >= CEE_COUNT) + return CEE_COUNT; + *pdwLen = 2; + break; + + case CEE_PREFIXREF: + case CEE_PREFIX2: + case CEE_PREFIX3: + case CEE_PREFIX4: + case CEE_PREFIX5: + case CEE_PREFIX6: + case CEE_PREFIX7: + return CEE_COUNT; + default: + break; + } + return opcode; +} + +char* Assembler::ReflectionNotation(mdToken tk) +{ + char *sz = (char*)&wzUniBuf[dwUniBuf>>1], *pc; + *sz=0; + switch(TypeFromToken(tk)) + { + case mdtTypeDef: + { + Class *pClass = m_lstClass.PEEK(RidFromToken(tk)-1); + if(pClass) + { + strcpy_s(sz,dwUniBuf>>1,pClass->m_szFQN); + pc = sz; + while((pc = strchr(pc,NESTING_SEP)) != NULL) + { + *pc = '+'; + pc++; + } + } + } + break; + + case mdtTypeRef: + { + ULONG N; + mdToken tkResScope; + if(SUCCEEDED(m_pImporter->GetTypeRefProps(tk,&tkResScope,wzUniBuf,dwUniBuf>>1,&N))) + { + WszWideCharToMultiByte(CP_UTF8,0,wzUniBuf,-1,sz,dwUniBuf>>1,NULL,NULL); + if(TypeFromToken(tkResScope)==mdtAssemblyRef) + { + AsmManAssembly *pAsmRef = m_pManifest->m_AsmRefLst.PEEK(RidFromToken(tkResScope)-1); + if(pAsmRef) + { + pc = &sz[strlen(sz)]; + pc+=sprintf_s(pc,(dwUniBuf >> 1),", %s, Version=%d.%d.%d.%d, Culture=",pAsmRef->szName, + pAsmRef->usVerMajor,pAsmRef->usVerMinor,pAsmRef->usBuild,pAsmRef->usRevision); + ULONG L=0; + if(pAsmRef->pLocale && (L=pAsmRef->pLocale->length())) + { + memcpy(wzUniBuf,pAsmRef->pLocale->ptr(),L); + wzUniBuf[L>>1] = 0; + WszWideCharToMultiByte(CP_UTF8,0,wzUniBuf,-1,pc,dwUniBuf>>1,NULL,NULL); + } + else pc+=sprintf_s(pc,(dwUniBuf >> 1),"neutral"); + pc = &sz[strlen(sz)]; + if(pAsmRef->pPublicKeyToken && (L=pAsmRef->pPublicKeyToken->length())) + { + pc+=sprintf_s(pc,(dwUniBuf >> 1),", Publickeytoken="); + BYTE* pb = (BYTE*)(pAsmRef->pPublicKeyToken->ptr()); + for(N=0; N> 1),"%2.2x",*pb); + } + } + } + } + } + break; + + default: + break; + } + return sz; +} + +/* +-------------------------------------------------------------------- +mix -- mix 3 32-bit values reversibly. +For every delta with one or two bits set, and the deltas of all three + high bits or all three low bits, whether the original value of a,b,c + is almost all zero or is uniformly distributed, +* If mix() is run forward or backward, at least 32 bits in a,b,c + have at least 1/4 probability of changing. +* If mix() is run forward, every bit of c will change between 1/3 and + 2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.) +mix() was built out of 36 single-cycle latency instructions in a + structure that could supported 2x parallelism, like so: + a -= b; + a -= c; x = (c>>13); + b -= c; a ^= x; + b -= a; x = (a<<8); + c -= a; b ^= x; + c -= b; x = (b>>13); + ... + Unfortunately, superscalar Pentiums and Sparcs can't take advantage + of that parallelism. They've also turned some of those single-cycle + latency instructions into multi-cycle latency instructions. Still, + this is the fastest good hash I could find. There were about 2^^68 + to choose from. I only looked at a billion or so. +-------------------------------------------------------------------- +*/ +#define mix(a,b,c) \ +{ \ + a -= b; a -= c; a ^= (c >> 13); \ + b -= c; b -= a; b ^= (a << 8); \ + c -= a; c -= b; c ^= (b >> 13); \ + a -= b; a -= c; a ^= (c >> 12); \ + b -= c; b -= a; b ^= (a << 16); \ + c -= a; c -= b; c ^= (b >> 5); \ + a -= b; a -= c; a ^= (c >> 3); \ + b -= c; b -= a; b ^= (a << 10); \ + c -= a; c -= b; c ^= (b >> 15); \ +} + +/* +-------------------------------------------------------------------- +hash() -- hash a variable-length key into a 32-bit value + k : the key (the unaligned variable-length array of bytes) + len : the length of the key, counting by bytes + initval : can be any 4-byte value +Returns a 32-bit value. Every bit of the key affects every bit of +the return value. Every 1-bit and 2-bit delta achieves avalanche. +About 6*len+35 instructions. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 32 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (ub1 **)k, do it like this: + for (i=0, h=0; i= 12) + { + a += (k[0] + ((unsigned)k[1] << 8) + ((unsigned)k[2] << 16) + ((unsigned)k[3] << 24)); + b += (k[4] + ((unsigned)k[5] << 8) + ((unsigned)k[6] << 16) + ((unsigned)k[7] << 24)); + c += (k[8] + ((unsigned)k[9] << 8) + ((unsigned)k[10] << 16) + ((unsigned)k[11] << 24)); + mix(a,b,c); + k += 12; len -= 12; + } + + /*------------------------------------- handle the last 11 bytes */ + c += length; + switch(len) /* all the case statements fall through */ + { + case 11: c+=((unsigned)k[10] << 24); + case 10: c+=((unsigned)k[9] << 16); + case 9 : c+=((unsigned)k[8] << 8); + /* the first byte of c is reserved for the length */ + case 8 : b+=((unsigned)k[7] << 24); + case 7 : b+=((unsigned)k[6] << 16); + case 6 : b+=((unsigned)k[5] << 8); + case 5 : b+=k[4]; + case 4 : a+=((unsigned)k[3] << 24); + case 3 : a+=((unsigned)k[2] << 16); + case 2 : a+=((unsigned)k[1] << 8); + case 1 : a+=k[0]; + /* case 0: nothing left to add */ + } + mix(a,b,c); + /*-------------------------------------------- report the result */ + return c; +} + diff --git a/src/ilasm/assembler.cpp b/src/ilasm/assembler.cpp new file mode 100644 index 0000000000..d83bf7ce2b --- /dev/null +++ b/src/ilasm/assembler.cpp @@ -0,0 +1,2466 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// +// File: assembler.cpp +// + +// + +#include "ilasmpch.h" + +#include "assembler.h" +#include "binstr.h" +#include "nvpair.h" + +#define FAIL_UNLESS(x, y) if (!(x)) { report->error y; return; } + +/**************************************************************************/ +void Assembler::StartNameSpace(__in __nullterminated char* name) +{ + m_NSstack.PUSH(m_szNamespace); + m_szNamespace = name; + unsigned L = (unsigned)strlen(m_szFullNS); + unsigned l = (unsigned)strlen(name); + if(L+l+1 >= m_ulFullNSLen) + { + char* pch = new char[((L+l)/MAX_NAMESPACE_LENGTH + 1)*MAX_NAMESPACE_LENGTH]; + if(pch) + { + memcpy(pch,m_szFullNS,L+1); + delete [] m_szFullNS; + m_szFullNS = pch; + m_ulFullNSLen = ((L+l)/MAX_NAMESPACE_LENGTH + 1)*MAX_NAMESPACE_LENGTH; + } + else report->error("Failed to reallocate the NameSpace buffer\n"); + } + if(L) m_szFullNS[L] = NAMESPACE_SEPARATOR_CHAR; + else L = 0xFFFFFFFF; + memcpy(&m_szFullNS[L+1],m_szNamespace, l+1); +} + +/**************************************************************************/ +void Assembler::EndNameSpace() +{ + char *p = &m_szFullNS[strlen(m_szFullNS)-strlen(m_szNamespace)]; + if(p > m_szFullNS) p--; + *p = 0; + delete [] m_szNamespace; + if((m_szNamespace = m_NSstack.POP())==NULL) + { + m_szNamespace = new char[2]; + m_szNamespace[0] = 0; + } +} + +/**************************************************************************/ +void Assembler::ClearImplList(void) +{ + while(m_nImplList) m_crImplList[--m_nImplList] = mdTypeRefNil; +} +/**************************************************************************/ +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:22008) // "Suppress PREfast warnings about integer overflow" +#endif +void Assembler::AddToImplList(mdToken tk) +{ + if(m_nImplList+1 >= m_nImplListSize) + { + mdToken *ptr = new mdToken[m_nImplListSize + MAX_INTERFACES_IMPLEMENTED]; + if(ptr == NULL) + { + report->error("Failed to reallocate Impl List from %d to %d bytes\n", + m_nImplListSize*sizeof(mdToken), + (m_nImplListSize+MAX_INTERFACES_IMPLEMENTED)*sizeof(mdToken)); + return; + } + memcpy(ptr,m_crImplList,m_nImplList*sizeof(mdToken)); + delete m_crImplList; + m_crImplList = ptr; + m_nImplListSize += MAX_INTERFACES_IMPLEMENTED; + } + m_crImplList[m_nImplList++] = tk; + m_crImplList[m_nImplList] = mdTypeRefNil; +} +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +void Assembler::ClearBoundList(void) +{ + m_TyParList = NULL; +} +/**************************************************************************/ +mdToken Assembler::ResolveClassRef(mdToken tkResScope, __in __nullterminated char *pszFullClassName, Class** ppClass) +{ + Class *pClass = NULL; + mdToken tkRet = mdTokenNil; + mdToken *ptkSpecial = NULL; + + if(pszFullClassName == NULL) return mdTokenNil; +#if (0) + if (m_fInitialisedMetaData == FALSE) + { + if (FAILED(InitMetaData())) // impl. see WRITER.CPP + { + _ASSERTE(0); + if(ppClass) *ppClass = NULL; + return mdTokenNil; + } + } +#endif + + switch(strlen(pszFullClassName)) + { + case 11: + if(strcmp(pszFullClassName,"System.Enum")==0) ptkSpecial = &m_tkSysEnum; + break; + case 13: + if(strcmp(pszFullClassName,"System.Object")==0) ptkSpecial = &m_tkSysObject; + else if(strcmp(pszFullClassName,"System.String")==0) ptkSpecial = &m_tkSysString; + break; + case 16: + if(strcmp(pszFullClassName,"System.ValueType")==0) ptkSpecial = &m_tkSysValue; + break; + } + if(ptkSpecial) // special token + { + if(*ptkSpecial) // already resolved + { + tkRet = *ptkSpecial; + if(ppClass) + { + if(TypeFromToken(tkRet)==mdtTypeDef) + *ppClass = m_lstClass.PEEK(RidFromToken(tkRet)-1); + else *ppClass = NULL; + } + return tkRet; + } + else // needs to be resolved + if(!m_fIsMscorlib) tkResScope = GetBaseAsmRef(); + } + if(tkResScope == 1) + { + if((pClass = FindCreateClass(pszFullClassName)) != NULL) tkRet = pClass->m_cl; + } + else + { + tkRet = MakeTypeRef(tkResScope, pszFullClassName); + pClass = NULL; + } + if(ppClass) *ppClass = pClass; + if(ptkSpecial) *ptkSpecial = tkRet; + return tkRet; +} + +class TypeSpecContainer +{ +private: + // Contain a BinStr + unsigned __int8 *ptr_; + unsigned len_; + // Hash the BinStr, just for speed of lookup + unsigned hash_; + // The value we're looking for + mdToken token_; +public: + // Constructor for a 'lookup' object + TypeSpecContainer(BinStr *typeSpec) : + len_(typeSpec->length()), + hash_(typeSpec->length()), + token_(mdTokenNil), + ptr_(typeSpec->ptr()) + { + for (unsigned i = 0; i < len_; i++) + hash_ = (hash_ * 257) ^ ((i + 1) * (ptr_[i] ^ 0xA5)); + } + // Constructor for a 'permanent' object + // Don't bother re-hashing, since we will always have already constructed the lookup object + TypeSpecContainer(const TypeSpecContainer &t, mdToken tk) : + len_(t.len_), + hash_(t.hash_), + token_(tk), + ptr_(new unsigned __int8[t.len_]) + { + _ASSERT(tk != mdTokenNil); + _ASSERT(t.token_ == mdTokenNil); + memcpy(ptr_, t.ptr_, len_); + } + ~TypeSpecContainer() + { + if (token_ != mdTokenNil) + // delete any memory for a 'permanent' object + delete[] ptr_; + } + // this is the operator for a RBTREE + int ComparedTo(TypeSpecContainer *t) const + { + // If they don't hash the same, just diff the hashes + if (hash_ != t->hash_) + return hash_ - t->hash_; + if (len_ != t->len_) + return len_ - t->len_; + return memcmp(ptr_, t->ptr_, len_); + } + // The only public data we need + const mdToken Token() const { return token_; } +}; + +static RBTREE typeSpecCache; + +extern FIFO TyParFixupList; + +/**************************************************************************/ +mdToken Assembler::ResolveTypeSpec(BinStr* typeSpec) +{ + mdToken tk; + + // It is safe to use the cache only if there are no pending fixups + if (TyParFixupList.COUNT() != 0) + { + if (FAILED(m_pEmitter->GetTokenFromTypeSpec(typeSpec->ptr(), typeSpec->length(), &tk))) + return mdTokenNil; + return tk; + } + + TypeSpecContainer tsc(typeSpec); + + // GetTokenFromTypeSpec is a linear search through an unsorted list + // Instead of doing that all the time, look this thing up in a cache + TypeSpecContainer *res = typeSpecCache.FIND(&tsc); + if (res != NULL) + { +#ifdef _DEBUG + // Verify that the cache is in sync with the master copy in metadata + PCOR_SIGNATURE pSig; + ULONG cSig; + m_pImporter->GetTypeSpecFromToken(res->Token(),(PCCOR_SIGNATURE*)&pSig,&cSig); + _ASSERTE(typeSpec->length() == cSig); + _ASSERTE(memcmp(typeSpec->ptr(), pSig, cSig) == 0); +#endif + + return res->Token(); + } + + if (FAILED(m_pEmitter->GetTokenFromTypeSpec(typeSpec->ptr(), typeSpec->length(), &tk))) + return mdTokenNil; + + typeSpecCache.PUSH(new TypeSpecContainer(tsc, tk)); + return tk; +} + +/**************************************************************************/ +mdToken Assembler::GetAsmRef(__in __nullterminated char* szName) +{ + mdToken tkResScope = 0; + if(strcmp(szName,"*")==0) tkResScope = mdTokenNil; + else + { + tkResScope = m_pManifest->GetAsmRefTokByName(szName); + if(RidFromToken(tkResScope)==0) + { + // emit the AssemblyRef + // if it's not self, try to get attributes with Autodetect + unsigned L = (unsigned)strlen(szName)+1; + char *sz = new char[L]; + if(sz) + { + memcpy(sz,szName,L); + AsmManAssembly *pAsmRef = m_pManifest->m_pCurAsmRef; + m_pManifest->StartAssembly(sz,NULL,0,TRUE); + if(RidFromToken(m_pManifest->GetAsmTokByName(szName))==0) + { + report->warn("Reference to undeclared extern assembly '%s'. Attempting autodetect\n",szName); + m_pManifest->SetAssemblyAutodetect(); + } + m_pManifest->EndAssembly(); + tkResScope = m_pManifest->GetAsmRefTokByName(szName); + m_pManifest->m_pCurAsmRef = pAsmRef; + } + else + report->error("\nOut of memory!\n"); + } + } + return tkResScope; +} + +mdToken Assembler::GetBaseAsmRef() +{ + if(RidFromToken(m_pManifest->GetAsmRefTokByName("System.Runtime")) != 0) + { + return GetAsmRef("System.Runtime"); + } + + return GetAsmRef("mscorlib"); +} + +mdToken Assembler::GetInterfaceImpl(mdToken tsClass, mdToken tsInterface) +{ + mdToken result = mdTokenNil; + HCORENUM iiEnum = 0; + ULONG actualInterfaces; + mdInterfaceImpl impls; + + while (SUCCEEDED(m_pImporter->EnumInterfaceImpls(&iiEnum, tsClass, &impls, 1, &actualInterfaces))) + { + if (actualInterfaces == 1) + { + mdToken classToken, interfaceToken; + if (FAILED(m_pImporter->GetInterfaceImplProps(impls, &classToken, &interfaceToken))) + break; + if (classToken == tsClass && interfaceToken == tsInterface) + { + result = impls; + break; + } + } + } + m_pImporter->CloseEnum(iiEnum); + return result; +} + +/**************************************************************************/ +mdToken Assembler::GetModRef(__in __nullterminated char* szName) +{ + mdToken tkResScope = 0; + if(!strcmp(szName,m_szScopeName)) + tkResScope = 1; // scope is "this module" + else + { + ImportDescriptor* pID; + int i = 0; + tkResScope = mdModuleRefNil; + DWORD L = (DWORD)strlen(szName); + while((pID=m_ImportList.PEEK(i++))) + { + if(pID->dwDllName != L) continue; + if((L > 0) && (strcmp(pID->szDllName,szName)!=0)) continue; + tkResScope = pID->mrDll; + break; + } + if(RidFromToken(tkResScope)==0) + report->error("Undefined module ref '%s'\n",szName); + } + return tkResScope; +} +/**************************************************************************/ +mdToken Assembler::MakeTypeRef(mdToken tkResScope, LPCUTF8 pszFullClassName) +{ + mdToken tkRet = mdTokenNil; + if(pszFullClassName && *pszFullClassName) + { + LPCUTF8 pc; + if((pc = strrchr(pszFullClassName,NESTING_SEP))) // scope: enclosing class + { + LPUTF8 szScopeName; + DWORD L = (DWORD)(pc-pszFullClassName); + if((szScopeName = new char[L+1]) != NULL) + { + memcpy(szScopeName,pszFullClassName,L); + szScopeName[L] = 0; + tkResScope = MakeTypeRef(tkResScope,szScopeName); + delete [] szScopeName; + } + else + report->error("\nOut of memory!\n"); + pc++; + } + else pc = pszFullClassName; + if(*pc) + { + // convert name to widechar + WszMultiByteToWideChar(g_uCodePage,0,pc,-1,wzUniBuf,dwUniBuf); + if(FAILED(m_pEmitter->DefineTypeRefByName(tkResScope, wzUniBuf, &tkRet))) tkRet = mdTokenNil; + } + } + return tkRet; +} +/**************************************************************************/ + +DWORD Assembler::CheckClassFlagsIfNested(Class* pEncloser, DWORD attr) +{ + DWORD wasAttr = attr; + if(pEncloser && (!IsTdNested(attr))) + { + if(OnErrGo) + report->error("Nested class has non-nested visibility (0x%08X)\n",attr); + else + { + attr &= ~tdVisibilityMask; + attr |= (IsTdPublic(wasAttr) ? tdNestedPublic : tdNestedPrivate); + report->warn("Nested class has non-nested visibility (0x%08X), changed to nested (0x%08X)\n",wasAttr,attr); + } + } + else if((pEncloser==NULL) && IsTdNested(attr)) + { + if(OnErrGo) + report->error("Non-nested class has nested visibility (0x%08X)\n",attr); + else + { + attr &= ~tdVisibilityMask; + attr |= (IsTdNestedPublic(wasAttr) ? tdPublic : tdNotPublic); + report->warn("Non-nested class has nested visibility (0x%08X), changed to non-nested (0x%08X)\n",wasAttr,attr); + } + } + return attr; +} + +/**************************************************************************/ + +void Assembler::StartClass(__in __nullterminated char* name, DWORD attr, TyParList *typars) +{ + Class *pEnclosingClass = m_pCurClass; + char *szFQN; + ULONG LL; + + m_TyParList = typars; + + if (m_pCurMethod != NULL) + { + report->error("Class cannot be declared within a method scope\n"); + } + if(pEnclosingClass) + { + LL = pEnclosingClass->m_dwFQN+(ULONG)strlen(name)+2; + if((szFQN = new char[LL])) + sprintf_s(szFQN,LL,"%s%c%s",pEnclosingClass->m_szFQN,NESTING_SEP,name); + else + report->error("\nOut of memory!\n"); + } + else + { + unsigned L = (unsigned)strlen(m_szFullNS); + unsigned LLL = (unsigned)strlen(name); + LL = L + LLL + (L ? 2 : 1); + if((szFQN = new char[LL])) + { + if(L) sprintf_s(szFQN,LL,"%s.%s",m_szFullNS,name); + else memcpy(szFQN,name,LL); + if(LL > MAX_CLASSNAME_LENGTH) + { + report->error("Full class name too long (%d characters, %d allowed).\n",LL-1,MAX_CLASSNAME_LENGTH-1); + } + } + else + report->error("\nOut of memory!\n"); + } + if(szFQN == NULL) return; + + mdToken tkThis; + if(m_fIsMscorlib) + tkThis = ResolveClassRef(1,szFQN,&m_pCurClass); // boils down to FindCreateClass(szFQN) + else + { + m_pCurClass = FindCreateClass(szFQN); + tkThis = m_pCurClass->m_cl; + } + if(m_pCurClass->m_bIsMaster) + { + m_pCurClass->m_Attr = CheckClassFlagsIfNested(pEnclosingClass, attr); + + if (m_TyParList) + { + //m_pCurClass->m_NumTyPars = m_TyParList->ToArray(&m_pCurClass->m_TyParBounds, &m_pCurClass->m_TyParNames, &m_pCurClass->m_TyParAttrs); + m_pCurClass->m_NumTyPars = m_TyParList->ToArray(&(m_pCurClass->m_TyPars)); + delete m_TyParList; + m_TyParList = NULL; + } + else m_pCurClass->m_NumTyPars = 0; + m_pCurClass->m_pEncloser = pEnclosingClass; + } // end if(old class) else + m_tkCurrentCVOwner = 0; + m_CustomDescrListStack.PUSH(m_pCustomDescrList); + m_pCustomDescrList = &(m_pCurClass->m_CustDList); + + m_ClassStack.PUSH(pEnclosingClass); + ClearBoundList(); +} + +/**************************************************************************/ + +void Assembler::AddClass() +{ + mdTypeRef crExtends = mdTypeRefNil; + BOOL bIsEnum = FALSE; + BOOL bIsValueType = FALSE; + + if(m_pCurClass->m_bIsMaster) + { + DWORD attr = m_pCurClass->m_Attr; + if(!IsNilToken(m_crExtends)) + { + // has a superclass + if(IsTdInterface(attr)) report->error("Base class in interface\n"); + bIsValueType = (m_crExtends == m_tkSysValue)&&(m_pCurClass->m_cl != m_tkSysEnum); + bIsEnum = (m_crExtends == m_tkSysEnum); + crExtends = m_crExtends; + } + else + { + bIsEnum = ((attr & 0x40000000) != 0); + bIsValueType = ((attr & 0x80000000) != 0); + } + attr &= 0x3FFFFFFF; + if (m_fAutoInheritFromObject && (crExtends == mdTypeRefNil) && (!IsTdInterface(attr))) + { + mdToken tkMscorlib = m_fIsMscorlib ? 1 : GetBaseAsmRef(); + crExtends = bIsEnum ? + ResolveClassRef(tkMscorlib,"System.Enum",NULL) + :( bIsValueType ? + ResolveClassRef(tkMscorlib,"System.ValueType",NULL) + : ResolveClassRef(tkMscorlib, "System.Object",NULL)); + } + m_pCurClass->m_Attr = attr; + m_pCurClass->m_crExtends = (m_pCurClass->m_cl == m_tkSysObject)? mdTypeRefNil : crExtends; + + if ((m_pCurClass->m_dwNumInterfaces = m_nImplList) != NULL) + { + if(bIsEnum) report->error("Enum implementing interface(s)\n"); + if((m_pCurClass->m_crImplements = new mdTypeRef[m_nImplList+1]) != NULL) + memcpy(m_pCurClass->m_crImplements, m_crImplList, (m_nImplList+1)*sizeof(mdTypeRef)); + else + { + report->error("Failed to allocate Impl List for class '%s'\n", m_pCurClass->m_szFQN); + m_pCurClass->m_dwNumInterfaces = 0; + } + } + else m_pCurClass->m_crImplements = NULL; + if(bIsValueType) + { + if(!IsTdSealed(attr)) + { + if(OnErrGo) report->error("Non-sealed value class\n"); + else + { + report->warn("Non-sealed value class, made sealed\n"); + m_pCurClass->m_Attr |= tdSealed; + } + } + } + m_pCurClass->m_bIsMaster = FALSE; + } // end if(old class) else + ClearImplList(); + m_crExtends = mdTypeRefNil; +} + +/**************************************************************************/ +void Assembler::EndClass() +{ + m_pCurClass = m_ClassStack.POP(); + m_tkCurrentCVOwner = 0; + m_pCustomDescrList = m_CustomDescrListStack.POP(); +} + +/**************************************************************************/ +void Assembler::SetPinvoke(BinStr* DllName, int Ordinal, BinStr* Alias, int Attrs) +{ + if(m_pPInvoke) delete m_pPInvoke; + if(DllName->length()) + { + if((m_pPInvoke = new PInvokeDescriptor)) + { + unsigned l; + ImportDescriptor* pID; + if((pID = EmitImport(DllName))) + { + m_pPInvoke->mrDll = pID->mrDll; + m_pPInvoke->szAlias = NULL; + if(Alias) + { + l = Alias->length(); + if((m_pPInvoke->szAlias = new char[l+1])) + { + memcpy(m_pPInvoke->szAlias,Alias->ptr(),l); + m_pPInvoke->szAlias[l] = 0; + } + else report->error("\nOut of memory!\n"); + } + m_pPInvoke->dwAttrs = (DWORD)Attrs; + } + else + { + delete m_pPInvoke; + m_pPInvoke = NULL; + report->error("PInvoke refers to undefined imported DLL\n"); + } + } + else + report->error("Failed to allocate PInvokeDescriptor\n"); + } + else + { + m_pPInvoke = NULL; // No DLL name, it's "local" (IJW) PInvoke + report->error("Local (embedded native) PInvoke method, the resulting PE file is unusable\n"); + } + if(DllName) delete DllName; + if(Alias) delete Alias; +} + +/**************************************************************************/ +void Assembler::StartMethod(__in __nullterminated char* name, BinStr* sig, CorMethodAttr flags, BinStr* retMarshal, DWORD retAttr, TyParList *typars) +{ + if (m_pCurMethod != NULL) + { + report->error("Cannot declare a method '%s' within another method\n",name); + } + if (!m_fInitialisedMetaData) + { + if (FAILED(InitMetaData())) // impl. see WRITER.CPP + { + _ASSERTE(0); + } + } + size_t namelen = strlen(name); + if(namelen >= MAX_CLASSNAME_LENGTH) + { + char c = name[MAX_CLASSNAME_LENGTH-1]; + name[MAX_CLASSNAME_LENGTH-1] = 0; + report->error("Method '%s...' -- name too long (%d characters).\n",name,namelen); + name[MAX_CLASSNAME_LENGTH-1] = c; + } + if (!(flags & mdStatic)) + *(sig->ptr()) |= IMAGE_CEE_CS_CALLCONV_HASTHIS; + else if(*(sig->ptr()) & (IMAGE_CEE_CS_CALLCONV_HASTHIS | IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS)) + { + if(OnErrGo) report->error("Method '%s' -- both static and instance\n", name); + else + { + report->warn("Method '%s' -- both static and instance, set to static\n", name); + *(sig->ptr()) &= ~(IMAGE_CEE_CS_CALLCONV_HASTHIS | IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS); + } + } + + if(!IsMdPrivateScope(flags)) + { + Method* pMethod; + Class* pClass = (m_pCurClass ? m_pCurClass : m_pModuleClass); + DWORD L = (DWORD)strlen(name); + for(int j=0; (pMethod = pClass->m_MethodList.PEEK(j)); j++) + { + if( (pMethod->m_dwName == L) && + (!strcmp(pMethod->m_szName,name)) && + (pMethod->m_dwMethodCSig == sig->length()) && + (!memcmp(pMethod->m_pMethodSig,sig->ptr(),sig->length())) + &&(!IsMdPrivateScope(pMethod->m_Attr))) + { + if(m_fTolerateDupMethods) + { + // reset for new body + pMethod->m_lstFixup.RESET(true); + //pMethod->m_lstLabel.RESET(true); + m_lstLabel.RESET(true); + pMethod->m_Locals.RESET(true); + delArgNameList(pMethod->m_firstArgName); + delArgNameList(pMethod->m_firstVarName); + pMethod->m_pCurrScope = &(pMethod->m_MainScope); + pMethod->m_pCurrScope->Reset(); + pMethod->m_firstArgName = getArgNameList(); + pMethod->m_dwNumExceptions = 0; + pMethod->m_dwNumEndfilters = 0; + if(pMethod->m_pRetMarshal) delete pMethod->m_pRetMarshal; + if(pMethod->m_pRetValue) delete pMethod->m_pRetValue; + + pMethod->m_MethodImplDList.RESET(false); // ptrs in m_MethodImplDList are dups of those in Assembler + + pMethod->m_CustomDescrList.RESET(true); + + if(pMethod->m_fEntryPoint) + { + pMethod->m_fEntryPoint = FALSE; + m_fEntryPointPresent = FALSE; + } + + if(pMethod->m_pbsBody) + { + // no need to remove relevant MemberRef Fixups from the Assembler list: + // their m_fNew flag is set to FALSE anyway. + // Just get rid of old method body + delete pMethod->m_pbsBody; + pMethod->m_pbsBody = NULL; + } + + pMethod->m_fNewBody = TRUE; + m_pCurMethod = pMethod; + } + else + report->error("Duplicate method declaration\n"); + break; + } + } + } + if(m_pCurMethod == NULL) + { + if(m_pCurClass) + { // instance method + if(IsMdAbstract(flags) && !IsTdAbstract(m_pCurClass->m_Attr)) + { + report->error("Abstract method '%s' in non-abstract class '%s'\n",name,m_pCurClass->m_szFQN); + } + if(m_pCurClass->m_crExtends == m_tkSysEnum) report->error("Method in enum\n"); + + if(!strcmp(name,COR_CTOR_METHOD_NAME)) + { + flags = (CorMethodAttr)(flags | mdSpecialName); + if(IsTdInterface(m_pCurClass->m_Attr)) report->error("Instance constructor in interface\n"); + + } + if(!IsMdStatic(flags)) + { + if(IsTdInterface(m_pCurClass->m_Attr)) + { + if(!IsMdPublic(flags)) report->error("Non-public instance method in interface\n"); + if((!(IsMdVirtual(flags) && IsMdAbstract(flags)))) + { + if(OnErrGo) report->error("Non-virtual, non-abstract instance method in interface\n"); + else + { + report->warn("Non-virtual, non-abstract instance method in interface, set to such\n"); + flags = (CorMethodAttr)(flags |mdVirtual | mdAbstract); + } + } + + } + } + m_pCurMethod = new Method(this, m_pCurClass, name, sig, flags); + } + else + { + if(IsMdAbstract(flags)) + { + if(OnErrGo) report->error("Global method '%s' can't be abstract\n",name); + else + { + report->warn("Global method '%s' can't be abstract, flag removed\n",name); + flags = (CorMethodAttr)(((int) flags) &~mdAbstract); + } + } + if(!IsMdStatic(flags)) + { + if(OnErrGo) report->error("Non-static global method '%s'\n",name); + else + { + report->warn("Non-static global method '%s', made static\n",name); + flags = (CorMethodAttr)(flags | mdStatic); + *((BYTE*)(sig->ptr())) &= ~(IMAGE_CEE_CS_CALLCONV_HASTHIS | IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS); + } + } + m_pCurMethod = new Method(this, m_pCurClass, name, sig, flags); + if (m_pCurMethod) + { + m_pCurMethod->SetIsGlobalMethod(); + if (m_fInitialisedMetaData == FALSE) InitMetaData(); + } + } + if(m_pCurMethod) + { + if(!OnErrGo) + { + if(m_pCurMethod->m_firstArgName) + { + for(ARG_NAME_LIST *pAN=m_pCurMethod->m_firstArgName; pAN; pAN = pAN->pNext) + { + if(pAN->dwName) + { + int k = m_pCurMethod->findArgNum(pAN->pNext,pAN->szName,pAN->dwName); + if(k >= 0) + report->warn("Duplicate param name '%s' in method '%s'\n",pAN->szName,name); + } + } + } + } + m_pCurMethod->m_pRetMarshal = retMarshal; + m_pCurMethod->m_dwRetAttr = retAttr; + m_tkCurrentCVOwner = 0; + m_CustomDescrListStack.PUSH(m_pCustomDescrList); + m_pCustomDescrList = &(m_pCurMethod->m_CustomDescrList); + m_pCurMethod->m_MainScope.dwStart = m_CurPC; + if (typars) + { + //m_pCurMethod->m_NumTyPars = typars->ToArray(&m_pCurMethod->m_TyParBounds, + //&m_pCurMethod->m_TyParNames, NULL); + m_pCurMethod->m_NumTyPars = typars->ToArray(&(m_pCurMethod->m_TyPars)); + delete typars; + m_TyParList = NULL; + } + else m_pCurMethod->m_NumTyPars = 0; + } + else report->error("Failed to allocate Method class\n"); + } // end if new method +} + +/**************************************************************************/ +void Assembler::EndMethod() +{ + + if(m_pCurMethod->m_pCurrScope != &(m_pCurMethod->m_MainScope)) + { + report->error("Invalid lexical scope structure in method %s\n",m_pCurMethod->m_szName); + } + m_pCurMethod->m_pCurrScope->dwEnd = m_CurPC; + if (DoFixups(m_pCurMethod)) AddMethod(m_pCurMethod); //AddMethod - see ASSEM.CPP + else + { + report->error("Method '%s' compilation failed.\n",m_pCurMethod->m_szName); + } + //m_pCurMethod->m_lstLabel.RESET(true); + m_lstLabel.RESET(true); + m_tkCurrentCVOwner = 0; + m_pCustomDescrList = m_CustomDescrListStack.POP(); + ResetForNextMethod(); // see ASSEM.CPP +} +/**************************************************************************/ +/* rvaLabel is the optional label that indicates this field points at a particular RVA */ +void Assembler::AddField(__inout_z __inout char* name, BinStr* sig, CorFieldAttr flags, __in __nullterminated char* rvaLabel, BinStr* pVal, ULONG ulOffset) +{ + FieldDescriptor* pFD; + ULONG i,n; + mdToken tkParent = mdTokenNil; + Class* pClass; + + if (m_pCurMethod) + report->error("Field cannot be declared within a method\n"); + + if(strlen(name) >= MAX_CLASSNAME_LENGTH) + { + char c = name[MAX_CLASSNAME_LENGTH-1]; + name[MAX_CLASSNAME_LENGTH-1] = 0; + report->error("Field '%s...' -- name too long (%d characters).\n",name,strlen(name)); + name[MAX_CLASSNAME_LENGTH-1] = c; + } + + if(sig && (sig->length() >= 2)) + { + if(sig->ptr()[1] == ELEMENT_TYPE_VOID) + report->error("Illegal use of type 'void'\n"); + } + + if (m_pCurClass) + { + tkParent = m_pCurClass->m_cl; + + if(IsTdInterface(m_pCurClass->m_Attr)) + { + if(!IsFdStatic(flags)) + { + report->warn("Instance field in interface (CLS violation)\n"); + if(!IsFdPublic(flags)) report->error("Non-public instance field in interface\n"); + } + } + } + else + { + if(ulOffset != 0xFFFFFFFF) + { + report->warn("Offset in global field '%s' is ignored\n",name); + ulOffset = 0xFFFFFFFF; + } + if(!IsFdStatic(flags)) + { + if(OnErrGo) report->error("Non-static global field\n"); + else + { + report->warn("Non-static global field, made static\n"); + flags = (CorFieldAttr)(flags | fdStatic); + } + } + } + pClass = (m_pCurClass ? m_pCurClass : m_pModuleClass); + n = pClass->m_FieldDList.COUNT(); + DWORD L = (DWORD)strlen(name); + for(i = 0; i < n; i++) + { + pFD = pClass->m_FieldDList.PEEK(i); + if((pFD->m_tdClass == tkParent)&&(L==pFD->m_dwName)&&(!strcmp(pFD->m_szName,name)) + &&(pFD->m_pbsSig->length() == sig->length()) + &&(memcmp(pFD->m_pbsSig->ptr(),sig->ptr(),sig->length())==0)) + { + report->error("Duplicate field declaration: '%s'\n",name); + break; + } + } + if (rvaLabel && !IsFdStatic(flags)) + report->error("Only static fields can have 'at' clauses\n"); + + if(i >= n) + { + if((pFD = new FieldDescriptor)) + { + pFD->m_tdClass = tkParent; + pFD->m_szName = name; + pFD->m_dwName = L; + pFD->m_fdFieldTok = mdTokenNil; + if((pFD->m_ulOffset = ulOffset) != 0xFFFFFFFF) pClass->m_dwNumFieldsWithOffset++; + pFD->m_rvaLabel = rvaLabel; + pFD->m_pbsSig = sig; + pFD->m_pClass = pClass; + pFD->m_pbsValue = pVal; + pFD->m_pbsMarshal = m_pMarshal; + pFD->m_pPInvoke = m_pPInvoke; + pFD->m_dwAttr = flags; + + m_tkCurrentCVOwner = 0; + m_pCustomDescrList = &(pFD->m_CustomDescrList); + + pClass->m_FieldDList.PUSH(pFD); + pClass->m_fNewMembers = TRUE; + } + else + report->error("Failed to allocate Field Descriptor\n"); + } + else + { + if(pVal) delete pVal; + if(m_pPInvoke) delete m_pPInvoke; + if(m_pMarshal) delete m_pMarshal; + delete name; + } + m_pPInvoke = NULL; + m_pMarshal = NULL; +} + +BOOL Assembler::EmitField(FieldDescriptor* pFD) +{ + WCHAR* wzFieldName=&wzUniBuf[0]; + HRESULT hr; + DWORD cSig; + COR_SIGNATURE* mySig; + mdFieldDef mb; + BYTE ValType = ELEMENT_TYPE_VOID; + void * pValue = NULL; + unsigned lVal = 0; + BOOL ret = TRUE; + + cSig = pFD->m_pbsSig->length(); + mySig = (COR_SIGNATURE*)(pFD->m_pbsSig->ptr()); + + WszMultiByteToWideChar(g_uCodePage,0,pFD->m_szName,-1,wzFieldName,dwUniBuf); //int)cFieldNameLength); + if(IsFdPrivateScope(pFD->m_dwAttr)) + { + WCHAR* p = wcsstr(wzFieldName,L"$PST04"); + if(p) *p = 0; + } + + if(pFD->m_pbsValue && pFD->m_pbsValue->length()) + { + ValType = *(pFD->m_pbsValue->ptr()); + lVal = pFD->m_pbsValue->length() - 1; // 1 is type byte + pValue = (void*)(pFD->m_pbsValue->ptr() + 1); + if(ValType == ELEMENT_TYPE_STRING) + { + //while(lVal % sizeof(WCHAR)) { pFD->m_pbsValue->appendInt8(0); lVal++; } + lVal /= sizeof(WCHAR); + +#if defined(ALIGN_ACCESS) || BIGENDIAN + void* pValueTemp = _alloca(lVal * sizeof(WCHAR)); + memcpy(pValueTemp, pValue, lVal * sizeof(WCHAR)); + pValue = pValueTemp; + + SwapStringLength((WCHAR*)pValue, lVal); +#endif + } + } + + hr = m_pEmitter->DefineField( + pFD->m_tdClass, + wzFieldName, + pFD->m_dwAttr, + mySig, + cSig, + ValType, + pValue, + lVal, + &mb + ); + if (FAILED(hr)) + { + report->error("Failed to define field '%s' (HRESULT=0x%08X)\n",pFD->m_szName,hr); + ret = FALSE; + } + else + { + //-------------------------------------------------------------------------------- + if(IsFdPinvokeImpl(pFD->m_dwAttr)&&(pFD->m_pPInvoke)) + { + if(pFD->m_pPInvoke->szAlias == NULL) pFD->m_pPInvoke->szAlias = pFD->m_szName; + if(FAILED(EmitPinvokeMap(mb,pFD->m_pPInvoke))) + { + report->error("Failed to define PInvoke map of .field '%s'\n",pFD->m_szName); + ret = FALSE; + } + } + //-------------------------------------------------------------------------- + if(pFD->m_pbsMarshal) + { + if(FAILED(hr = m_pEmitter->SetFieldMarshal ( + mb, // [IN] given a fieldDef or paramDef token + (PCCOR_SIGNATURE)(pFD->m_pbsMarshal->ptr()), // [IN] native type specification + pFD->m_pbsMarshal->length()))) // [IN] count of bytes of pvNativeType + { + report->error("Failed to set field marshaling for '%s' (HRESULT=0x%08X)\n",pFD->m_szName,hr); + ret = FALSE; + } + } + //-------------------------------------------------------------------------------- + // Set the the RVA to a dummy value. later it will be fixed + // up to be something correct, but if we don't emit something + // the size of the meta-data will not be correct + if (pFD->m_rvaLabel) + { + m_fHaveFieldsWithRvas = TRUE; + hr = m_pEmitter->SetFieldRVA(mb, 0xCCCCCCCC); + if (FAILED(hr)) + { + report->error("Failed to set RVA for field '%s' (HRESULT=0x%08X)\n",pFD->m_szName,hr); + ret = FALSE; + } + } + //-------------------------------------------------------------------------------- + EmitCustomAttributes(mb, &(pFD->m_CustomDescrList)); + + } + pFD->m_fdFieldTok = mb; + return ret; +} + +/**************************************************************************/ +void Assembler::EmitByte(int val) +{ + char ch = (char)val; + //if((val < -128)||(val > 127)) + // report->warn("Emitting 0x%X as a byte: data truncated to 0x%X\n",(unsigned)val,(BYTE)ch); + EmitBytes((BYTE *)&ch,1); +} + +/**************************************************************************/ +void Assembler::NewSEHDescriptor(void) //sets m_SEHD +{ + m_SEHDstack.PUSH(m_SEHD); + m_SEHD = new SEH_Descriptor; + if(m_SEHD == NULL) report->error("Failed to allocate SEH descriptor\n"); +} +/**************************************************************************/ +void Assembler::SetTryLabels(__in __nullterminated char * szFrom, __in __nullterminated char *szTo) +{ + if(!m_SEHD) return; + Label *pLbl = m_pCurMethod->FindLabel(szFrom); + if(pLbl) + { + m_SEHD->tryFrom = pLbl->m_PC; + if((pLbl = m_pCurMethod->FindLabel(szTo))) m_SEHD->tryTo = pLbl->m_PC; //FindLabel: Method.CPP + else report->error("Undefined 2nd label in 'try