diff options
Diffstat (limited to 'src/dlls/mscorpe')
-rw-r--r-- | src/dlls/mscorpe/.gitmirrorall | 1 | ||||
-rw-r--r-- | src/dlls/mscorpe/CMakeLists.txt | 23 | ||||
-rw-r--r-- | src/dlls/mscorpe/Native.rc | 8 | ||||
-rw-r--r-- | src/dlls/mscorpe/ceefilegenwriter.cpp | 2007 | ||||
-rw-r--r-- | src/dlls/mscorpe/ceefilegenwritertokens.cpp | 266 | ||||
-rw-r--r-- | src/dlls/mscorpe/dirs.proj | 15 | ||||
-rw-r--r-- | src/dlls/mscorpe/iceefilegen.cpp | 732 | ||||
-rw-r--r-- | src/dlls/mscorpe/mscorpe/mscorpe.def | 11 | ||||
-rw-r--r-- | src/dlls/mscorpe/mscorpe/mscorpe.nativeproj | 46 | ||||
-rw-r--r-- | src/dlls/mscorpe/mscorpe/wrapper.cpp | 149 | ||||
-rw-r--r-- | src/dlls/mscorpe/mscorpehost/mscorpehost.def | 12 | ||||
-rw-r--r-- | src/dlls/mscorpe/mscorpehost/mscorpehost.nativeproj | 68 | ||||
-rw-r--r-- | src/dlls/mscorpe/pewriter.cpp | 2401 | ||||
-rw-r--r-- | src/dlls/mscorpe/pewriter.h | 337 | ||||
-rw-r--r-- | src/dlls/mscorpe/stdafx.cpp | 10 | ||||
-rw-r--r-- | src/dlls/mscorpe/stdafx.h | 24 | ||||
-rw-r--r-- | src/dlls/mscorpe/stubs.h | 169 | ||||
-rw-r--r-- | src/dlls/mscorpe/utilcodeinit.cpp | 11 |
18 files changed, 6290 insertions, 0 deletions
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/CMakeLists.txt b/src/dlls/mscorpe/CMakeLists.txt new file mode 100644 index 0000000000..e8f22f2e9b --- /dev/null +++ b/src/dlls/mscorpe/CMakeLists.txt @@ -0,0 +1,23 @@ +project(mscorpe) + +add_definitions(-DFEATURE_CORECLR) + +set(MSCORPE_SOURCES + iceefilegen.cpp + ceefilegenwriter.cpp + pewriter.cpp + ceefilegenwritertokens.cpp + utilcodeinit.cpp +) + +if(WIN32) + list(APPEND MSCORPE_SOURCES + Native.rc + ) +else() + add_compile_options(-Wno-delete-non-virtual-dtor) +endif(WIN32) + +add_library_clr(mscorpe STATIC + ${MSCORPE_SOURCES} +) diff --git a/src/dlls/mscorpe/Native.rc b/src/dlls/mscorpe/Native.rc new file mode 100644 index 0000000000..e652bf3f1d --- /dev/null +++ b/src/dlls/mscorpe/Native.rc @@ -0,0 +1,8 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#define FX_VER_FILEDESCRIPTION_STR "Microsoft .NET Runtime PE File Generator\0" + +#include <fxver.h> +#include <fxver.rc> diff --git a/src/dlls/mscorpe/ceefilegenwriter.cpp b/src/dlls/mscorpe/ceefilegenwriter.cpp new file mode 100644 index 0000000000..cfd1ebb644 --- /dev/null +++ b/src/dlls/mscorpe/ceefilegenwriter.cpp @@ -0,0 +1,2007 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more 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 <string.h> +#include <limits.h> + +#include "corerror.h" +#include "stubs.h" +#include <posterror.h> +#include <shlwapi.h> + +// 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<PEWriter> pPEWriter(NULL); + NewHolder<CeeFileGenWriter> 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 + PathString szFileName; + DWORD len = WszGetEnvironmentVariable(W("COMP_ENC_EMIT"), 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; +#ifndef FEATURE_PAL + hr = emitResourceSection(); + if (FAILED(hr)) + return hr; +#endif + } + + 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; + LPCWSTR outputFileName = NULL; + +#ifndef FEATURE_PAL + 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(); + } + } +#endif // !FEATURE_PAL + +#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 = W("output.ill"); + else if (m_dllSwitch) + outputFileName = W("output.dll"); + else if (m_objSwitch) + outputFileName = W("output.exe"); + else + outputFileName = W("output.obj"); + } + + // 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: +#ifndef FEATURE_PAL + if (hThreadToken != NULL) + { + BOOL success = SetThreadToken(NULL, hThreadToken); + CloseHandle(hThreadToken); + + if (!success) + { + _ASSERTE(!"Failed to reimpersonate!"); + hr = HRESULT_FROM_GetLastError(); + } + } +#endif // !FEATURE_PAL + 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, W(".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<char*>(&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() + + +HRESULT GetClrSystemDirectory(SString& pbuffer) +{ + HRESULT hr = S_OK; + + + PathString pPath; + DWORD dwPath; + + _ASSERTE (g_hThisInst); + + dwPath = WszGetModuleFileName(g_hThisInst, pPath); + if(dwPath == 0) + { + hr = HRESULT_FROM_GetLastErrorNA(); + return (hr); + } + + return CopySystemDirectory(pPath, pbuffer); +} + +#ifndef FEATURE_PAL +BOOL RunProcess(LPCWSTR tempResObj, LPCWSTR pszFilename, DWORD* pdwExitCode, PEWriter &pewriter) +{ + BOOL fSuccess = FALSE; + + PROCESS_INFORMATION pi; + + PathString wszSystemDir; + if (FAILED(GetClrSystemDirectory(wszSystemDir))) + 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.GetUnicode(), + wzMachine, + tempResObj, + pszFilename); + } + else + { + ssCmdLine.Printf(L"%scvtres.exe /NOLOGO /READONLY /MACHINE:%s \"/OUT:%s\" \"%s\"", + wszSystemDir.GetUnicode(), + 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; + } + + PathString tempResObj; + PathString tempResPath; + + // Create the temp file where the temp path is at rather than where the application is at. + if (!WszGetTempPath( 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; + + hr = S_OK; + + struct Param + { + HANDLE hFile; + HANDLE hMap; + IMAGE_FILE_HEADER *hMod; + const WCHAR* szResFileName; + CeeFileGenWriter *genWriter; + HRESULT hr; + } param; + + param.hFile = hFile; + param.hMap = hMap; + param.hMod = hMod; + param.szResFileName = szResFileName; + param.genWriter = this; + param.hr = S_OK; + + PAL_TRY(Param *, pParam, ¶m) + { + SIZE_T cbFileSize; + const BYTE *pbStartOfMappedMem; + IMAGE_SECTION_HEADER *rsrc[2] = { NULL, NULL }; + S_SIZE_T cbTotalSizeOfRawData; + // create a mapped view of the .res file + pParam->hFile = WszCreateFile(pParam->szResFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (pParam->hFile == INVALID_HANDLE_VALUE) + { + //dbprintf("Resource file %S not found\n", szResFileName); + pParam->hr = HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND); + goto lDone; + } + + // Grab the file size for verification checks. + { + DWORD dwFileSizeHigh; + DWORD dwFileSize = SafeGetFileSize(pParam->hFile, &dwFileSizeHigh); + if (dwFileSize == (DWORD)(-1)) + { + pParam->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) + { + pParam->hr = HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND); + goto lDone; + } + + cbFileSize = static_cast<SIZE_T>(dwFileSize); + } + + pParam->hMap = WszCreateFileMapping(pParam->hFile, 0, PAGE_READONLY, 0, 0, NULL); + + if (pParam->hMap == NULL) + { + //dbprintf("Invalid .res file: %S\n", szResFileName); + pParam->hr = HRESULT_FROM_GetLastError(); + goto lDone; + } + + pbStartOfMappedMem = reinterpret_cast<const BYTE *>(MapViewOfFile(pParam->hMap, FILE_MAP_READ, 0, 0, 0)); + + // test failure conditions + if (pbStartOfMappedMem == NULL) + { + //dbprintf("Invalid .res file: %S:Can't get header\n", szResFileName); + pParam->hr = HRESULT_FROM_GetLastError(); + goto lDone; + } + + // Check that the file contains an IMAGE_FILE_HEADER structure. + if (IMAGE_SIZEOF_FILE_HEADER > cbFileSize) + { + pParam->hr = HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND); + goto lDone; + } + + pParam->hMod = (IMAGE_FILE_HEADER*)pbStartOfMappedMem; + + if (VAL16(pParam->hMod->SizeOfOptionalHeader) != 0) + { + //dbprintf("Invalid .res file: %S:Illegal optional header\n", szResFileName); + pParam->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<SIZE_T>(VAL16(pParam->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) + { + pParam->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); + pParam->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<SIZE_T>(VAL32(rsrc[i]->PointerToRawData))); + S_SIZE_T cbSizeOfResourceData(static_cast<SIZE_T>(VAL32(rsrc[i]->SizeOfRawData))); + S_SIZE_T cbEndOfResourceData(cbStartOfResourceData + cbSizeOfResourceData); + + if (cbEndOfResourceData.IsOverflow() || + cbEndOfResourceData.Value() > cbFileSize) + { + pParam->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) + { + pParam->hr = HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND); + goto lDone; + } + } + + PESection *rsrcSection; + pParam->hr = pParam->genWriter->getPEWriter().getSectionCreate(".rsrc", sdReadOnly, &rsrcSection); + if (FAILED(pParam->hr)) goto lDone; + + rsrcSection->directoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE); + char *data = rsrcSection->getBlock(static_cast<unsigned>(cbTotalSizeOfRawData.Value()), 8); + if(data == NULL) + { + pParam->hr = E_OUTOFMEMORY; + goto lDone; + } + + // Copy resource header + memcpy(data, (char *)pParam->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<SIZE_T>(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<SIZE_T>(VAL32(pParam->hMod->NumberOfSymbols)); + SIZE_T cbStartOfSymbolTable = static_cast<SIZE_T>(VAL32(pParam->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) + { + pParam->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<SIZE_T>(VAL32(rsrc[0]->SizeOfRawData))) + { + pParam->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) + { + pParam->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); + pParam->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); + pParam->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 *)pParam->hMod + VAL32(rsrc[1]->PointerToRawData), + VAL32(rsrc[1]->SizeOfRawData)); + +lDone: ; + } + PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + //dbprintf("Exception occured manipulating .res file %S\n", szResFileName); + param.hr = HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND); + } + PAL_ENDTRY + + hMod = param.hMod; + hFile = param.hFile; + szResFileName = param.szResFileName; + hr = param.hr; + + 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() +#endif // !FEATURE_PAL + +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..e2d448552d --- /dev/null +++ b/src/dlls/mscorpe/ceefilegenwritertokens.cpp @@ -0,0 +1,266 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more 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" +#ifndef FEATURE_CORECLR +#define DECLARE_DATA +#endif +#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<BYTE*>(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 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" /> + + <!--The following projects will build during PHASE 1--> + <PropertyGroup> + <BuildInPhaseDefault>false</BuildInPhaseDefault> + <BuildInPhase1>true</BuildInPhase1> + </PropertyGroup> + <ItemGroup Condition="'$(BuildExePhase)' == '1'"> + <ProjectFile Include="mscorpe\mscorpe.nativeproj" /> + <ProjectFile Include="mscorpehost\mscorpehost.nativeproj" /> + </ItemGroup> + + <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" /> +</Project> diff --git a/src/dlls/mscorpe/iceefilegen.cpp b/src/dlls/mscorpe/iceefilegen.cpp new file mode 100644 index 0000000000..f4323b9e8c --- /dev/null +++ b/src/dlls/mscorpe/iceefilegen.cpp @@ -0,0 +1,732 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more 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<CCeeGen*>(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<CeeFileGenWriter**>(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<CeeFileGenWriter*>(ceeFile); + *section = &gen->getStringSection(); + return S_OK; +} + +HRESULT ICeeFileGen::GetIlSection (HCEEFILE ceeFile, HCEESECTION *section) +{ + TESTANDRETURNPOINTER(section); + TESTANDRETURNARG(ceeFile != 0); + + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(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<CeeFileGenWriter*>(ceeFile); + CeeSection **ceeSection = reinterpret_cast<CeeSection**>(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<CeeFileGenWriter*>(ceeFile); + CeeSection &sec = *(reinterpret_cast<CeeSection*>(section)); + return(gen->setDirectoryEntry(sec, num, size, offset)); +} + +HRESULT ICeeFileGen::GetSectionDataLen (HCEESECTION section, ULONG *dataLen) +{ + TESTANDRETURNPOINTER(section); + TESTANDRETURNPOINTER(dataLen); + + CeeSection *sec = reinterpret_cast<CeeSection*>(section); + *dataLen = sec->dataLen(); + return S_OK; +} + +HRESULT ICeeFileGen::GetSectionRVA (HCEESECTION section, ULONG *rva) +{ + TESTANDRETURNPOINTER(section); + TESTANDRETURNPOINTER(rva); + + CeeSection *sec = reinterpret_cast<CeeSection*>(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<CeeSection*>(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<CeeSection*>(section); + CeeSection *relSec = reinterpret_cast<CeeSection*>(relativeTo); + + if (relSec) + { +#ifdef EMIT_FIXUPS + CeeFileGenWriter * gen = reinterpret_cast<CeeFileGenWriter*>(&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<CeeFileGenWriter*>(&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<CeeSection*>(section); + return(sec->directoryEntry(num)); +} + +HRESULT ICeeFileGen::SetOutputFileName (HCEEFILE ceeFile, __in LPWSTR outputFileName) +{ + TESTANDRETURNPOINTER(ceeFile); + TESTANDRETURNPOINTER(outputFileName); + + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(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<CeeFileGenWriter*>(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<CeeFileGenWriter*>(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<CeeFileGenWriter*>(ceeFile); + TESTANDRETURNPOINTER(resourceFileName); + *resourceFileName = gen->getResourceFileName(); + return S_OK; +} + + +HRESULT ICeeFileGen::SetImageBase(HCEEFILE ceeFile, size_t imageBase) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(ceeFile); + gen->setImageBase(imageBase); + return S_OK; +} + +HRESULT ICeeFileGen::SetImageBase64(HCEEFILE ceeFile, ULONGLONG imageBase) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(ceeFile); + gen->setImageBase64(imageBase); + return S_OK; +} + +HRESULT ICeeFileGen::SetFileAlignment(HCEEFILE ceeFile, ULONG fileAlignment) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(ceeFile); + gen->setFileAlignment(fileAlignment); + return S_OK; +} + +HRESULT ICeeFileGen::SetSubsystem(HCEEFILE ceeFile, DWORD subsystem, DWORD major, DWORD minor) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(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<CeeFileGenWriter*>(ceeFile); + gen->getMethodRVA(codeOffset, codeRVA); + return S_OK; +} + +HRESULT ICeeFileGen::EmitString(HCEEFILE ceeFile, __in LPWSTR strValue, ULONG *strRef) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(ceeFile); + return(gen->getStringSection().getEmittedStringRef(strValue, strRef)); +} + +HRESULT ICeeFileGen::LinkCeeFile (HCEEFILE ceeFile) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(ceeFile); + return gen->link(); +} + +HRESULT ICeeFileGen::FixupCeeFile (HCEEFILE ceeFile) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(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<CeeFileGenWriter*>(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<CeeFileGenWriter*>(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<CeeFileGenWriter*>(ceeFile); + return gen->generateImage(ppImage); +} + +HRESULT ICeeFileGen::SetEntryPoint(HCEEFILE ceeFile, mdMethodDef method) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(ceeFile); + return gen->setEntryPoint(method); +} + +HRESULT ICeeFileGen::GetEntryPoint(HCEEFILE ceeFile, mdMethodDef *method) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(ceeFile); + TESTANDRETURNPOINTER(method); + *method = gen->getEntryPoint(); + return S_OK; +} + + +HRESULT ICeeFileGen::SetComImageFlags (HCEEFILE ceeFile, DWORD mask) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(ceeFile); + return gen->setComImageFlags(mask); +} + +HRESULT ICeeFileGen::ClearComImageFlags (HCEEFILE ceeFile, DWORD mask) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(ceeFile); + return gen->clearComImageFlags(mask); +} + +HRESULT ICeeFileGen::GetComImageFlags (HCEEFILE ceeFile, DWORD *mask) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(ceeFile); + TESTANDRETURNPOINTER(mask); + *mask = gen->getComImageFlags(); + return S_OK; +} + + +HRESULT ICeeFileGen::SetDllSwitch (HCEEFILE ceeFile, BOOL dllSwitch) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(ceeFile); + return(gen->setDllSwitch(dllSwitch==TRUE)); +} + +HRESULT ICeeFileGen::GetDllSwitch (HCEEFILE ceeFile, BOOL *dllSwitch) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(ceeFile); + TESTANDRETURNPOINTER(dllSwitch); + *dllSwitch = gen->getDllSwitch(); + return S_OK; +} + +HRESULT ICeeFileGen::SetObjSwitch (HCEEFILE ceeFile, BOOL objSwitch) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(ceeFile); + return(gen->setObjSwitch(objSwitch==TRUE)); +} + +HRESULT ICeeFileGen::GetObjSwitch (HCEEFILE ceeFile, BOOL *objSwitch) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(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<CeeFileGenWriter*>(ceeFile); + return(gen->setLibraryName(LibraryName)); +} + +HRESULT ICeeFileGen::SetLibraryGuid (HCEEFILE ceeFile, __in LPWSTR LibraryGuid) +{ + TESTANDRETURNPOINTER(ceeFile); + TESTANDRETURNPOINTER(LibraryGuid); + + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(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<CeeFileGenWriter*>(ceeFile); + *LibraryName = gen->getLibraryName(); + return S_OK; +} + + + +HRESULT ICeeFileGen::EmitMetaDataEx (HCEEFILE ceeFile, IMetaDataEmit *emitter) +{ + TESTANDRETURNPOINTER(ceeFile); + TESTANDRETURNPOINTER(emitter); + + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(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<CeeFileGenWriter*>(ceeFile); + CeeSection* sec = reinterpret_cast<CeeSection*>(section); + + return(gen->emitMetaData(emitter, sec, offset, buffer, buffLen)); +} + +HRESULT ICeeFileGen::EmitLibraryNameEx (HCEEFILE ceeFile, IMetaDataEmit *emitter) +{ + TESTANDRETURNPOINTER(ceeFile); + TESTANDRETURNPOINTER(emitter); + + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(ceeFile); + return(gen->emitLibraryName(emitter)); +} + +HRESULT ICeeFileGen::GetIMapTokenIfaceEx(HCEEFILE ceeFile, IMetaDataEmit *emitter, IUnknown **pIMapToken) +{ + TESTANDRETURNPOINTER(ceeFile); + TESTANDRETURNPOINTER(pIMapToken); + + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(ceeFile); + return gen->getMapTokenIface(pIMapToken); +} + +HRESULT ICeeFileGen::AddNotificationHandler(HCEEFILE ceeFile, + IUnknown *pHandler) +{ + TESTANDRETURNPOINTER(ceeFile); + TESTANDRETURNPOINTER(pHandler); + + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(ceeFile); + return gen->addNotificationHandler(pHandler); +} + +HRESULT ICeeFileGen::EmitMacroDefinitions(HCEEFILE ceeFile, void *pData, DWORD cData) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(ceeFile); + return gen->EmitMacroDefinitions(pData, cData); +} + +HRESULT ICeeFileGen::SetManifestEntry(HCEEFILE ceeFile, ULONG size, ULONG offset) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(ceeFile); + return gen->setManifestEntry(size, offset); +} + +HRESULT ICeeFileGen::SetStrongNameEntry(HCEEFILE ceeFile, ULONG size, ULONG offset) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(ceeFile); + return gen->setStrongNameEntry(size, offset); +} + +HRESULT ICeeFileGen::ComputeSectionOffset(HCEESECTION section, __in char *ptr, + unsigned *offset) +{ + TESTANDRETURNPOINTER(section); + + CeeSection &sec = *(reinterpret_cast<CeeSection*>(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<CeeSection*>(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<CeeFileGenWriter*>(ceeFile); + + CeeSection *section; + + HRESULT hr = gen->computeOffset(ptr, §ion, offset); + + if (SUCCEEDED(hr)) + *pSection = reinterpret_cast<HCEESECTION>(section); + + return hr; +} + +HRESULT ICeeFileGen::SetEnCRVABase(HCEEFILE ceeFile, ULONG dataBase, ULONG rdataBase) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(ceeFile); + return gen->setEnCRvaBase(dataBase, rdataBase); +} + +HRESULT ICeeFileGen::GetCorHeader(HCEEFILE ceeFile, + IMAGE_COR20_HEADER **header) +{ + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(ceeFile); + return gen->getCorHeader(header); +} + +HRESULT ICeeFileGen::SetVTableEntry(HCEEFILE ceeFile, ULONG size, ULONG offset) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(ceeFile); + return gen->setVTableEntry(size, offset); +} + +HRESULT ICeeFileGen::SetVTableEntry64(HCEEFILE ceeFile, ULONG size, void* ptr) +{ + TESTANDRETURNPOINTER(ceeFile); + + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(ceeFile); + return gen->setVTableEntry64(size, ptr); +} + +HRESULT ICeeFileGen::GetFileTimeStamp (HCEEFILE ceeFile, DWORD *pTimeStamp) +{ + TESTANDRETURNPOINTER(ceeFile); + TESTANDRETURNPOINTER(pTimeStamp); + + CeeFileGenWriter *gen = reinterpret_cast<CeeFileGenWriter*>(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..875abc42a7 --- /dev/null +++ b/src/dlls/mscorpe/mscorpe/mscorpe.def @@ -0,0 +1,11 @@ +; Licensed to the .NET Foundation under one or more agreements. +; The .NET Foundation licenses this file to you under the MIT license. +; See the LICENSE file in the project root for more 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 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood"> + + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" /> + + <PropertyGroup> + <NoCrt>true</NoCrt> + <LinkUseCMT>true</LinkUseCMT> + <UserIncludes> + $(UserIncludes); + . + </UserIncludes> + <CDefines>$(CDefines);__TODO_PORT_TO_WRAPPERS__;UNICODE</CDefines> + <OutputName>mscorpe</OutputName> + <FileToMarkForSigning>$(BinariesDirectory)\$(OutputName).dll</FileToMarkForSigning> + <OutputLibPath>$(IntermediateOutputDirectory)</OutputLibPath> + <DllDef>$(OutputName).def</DllDef> + <TargetType>DYNLINK</TargetType> + <LinkSubsystem>windows</LinkSubsystem> + <PogoOptimized>true</PogoOptimized> + <DllEntryPoint>_DllMainCRTStartup</DllEntryPoint> + <Win32DllLibs>$(ClrLibPath)\utilcodestaticnohost.lib</Win32DllLibs> + <UseMsvcrt>false</UseMsvcrt> + <NoLinkGdi32>true</NoLinkGdi32> + </PropertyGroup> + + <ItemGroup> + <TargetLib Include="$(SdkLibPath)\mscoree.lib" /> + <TargetLib Include="$(SdkLibPath)\oleaut32.lib" /> + <TargetLib Include="$(SdkLibPath)\uuid.lib" /> + <TargetLib Include="$(SdkLibPath)\kernel32.lib" /> + <TargetLib Include="$(SdkLibPath)\advapi32.lib" /> + <TargetLib Include="$(SdkLibPath)\user32.lib" /> + <TargetLib Include="$(SdkLibPath)\shlwapi.lib" /> + <ProjectReference Include="$(ClrSrcDirectory)utilcode\staticnohost\staticnohost.nativeproj"/> + </ItemGroup> + + <ItemGroup> + <RCResourceFile Include="..\Native.rc" /> + </ItemGroup> + + <ItemGroup> + <CppCompile Include="wrapper.cpp" /> + </ItemGroup> + + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" /> +</Project> diff --git a/src/dlls/mscorpe/mscorpe/wrapper.cpp b/src/dlls/mscorpe/mscorpe/wrapper.cpp new file mode 100644 index 0000000000..d2f1701ec4 --- /dev/null +++ b/src/dlls/mscorpe/mscorpe/wrapper.cpp @@ -0,0 +1,149 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more 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 <MscorpeSxSWrapper.h> + +#include <mscoree.h> +#include <metahost.h> + +// 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 (\<version>), + // 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<LPVOID *>(&pMetaHost))); + + IfFailGo(pMetaHost->GetRuntime( + wszVersion, + IID_ICLRRuntimeInfo, + reinterpret_cast<LPVOID *>(&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<LoadMscorpe> 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..0cf870b17b --- /dev/null +++ b/src/dlls/mscorpe/mscorpehost/mscorpehost.def @@ -0,0 +1,12 @@ +; Licensed to the .NET Foundation under one or more agreements. +; The .NET Foundation licenses this file to you under the MIT license. +; See the LICENSE file in the project root for more 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 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood"> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" /> + + <PropertyGroup> + <LibCLib>$(ClrCrtLib)</LibCLib> + <OutputName>mscorpehost</OutputName> + <FileToMarkForSigning>$(BinariesDirectory)\$(OutputName).dll</FileToMarkForSigning> + <OutputLibPath>$(IntermediateOutputDirectory)</OutputLibPath> + <DllDef>$(OutputName).def</DllDef> + <TargetType>DYNLINK</TargetType> + <PCHHeader>stdafx.h</PCHHeader> + <EnableCxxPCHHeaders>true</EnableCxxPCHHeaders> + <PCHCompile>..\stdafx.cpp</PCHCompile> + <UserIncludes> + $(UserIncludes); + .; + ../../vm + </UserIncludes> + <CDefines>$(CDefines);__TODO_PORT_TO_WRAPPERS__;UNICODE</CDefines> + <LinkSubsystem>windows</LinkSubsystem> + <PogoOptimized>true</PogoOptimized> + <DllEntryPoint>_DllMainCRTStartup</DllEntryPoint> + <ExtDelayImpLib>false</ExtDelayImpLib> + </PropertyGroup> + + <PropertyGroup> + <LinkDelayLoad Condition="'$(LinkDelayLoad)'!=''">$(LinkDelayLoad);</LinkDelayLoad> + <LinkDelayLoad>$(LinkDelayLoad)ole32.dll</LinkDelayLoad> + </PropertyGroup> + + <ItemGroup> + <CppCompile Include="..\utilcodeinit.cpp" /> + </ItemGroup> + + <ItemGroup> + <LinkPreCrtLibs Include="$(ClrLibPath)\utilcode.lib"> + <ProjectReference>$(ClrSrcDirectory)utilcode\dyncrt\dyncrt.nativeproj</ProjectReference> + </LinkPreCrtLibs> + + <TargetLib Include="$(SdkLibPath)\mscoree.lib" /> + <TargetLib Include="$(ClrLibPath)\ceefgen.lib"> + <ProjectReference>$(ClrSrcDirectory)md\ceefilegen\ceefgen.nativeproj</ProjectReference> + </TargetLib> + <TargetLib Include="$(ClrLibPath)\delayimp.lib"> + <ProjectReference>$(ClrSrcDirectory)delayimp\delayimp.nativeproj</ProjectReference> + </TargetLib> + <TargetLib Include="$(SdkLibPath)\ole32.lib" /> + <TargetLib Include="$(SdkLibPath)\oleaut32.lib" /> + <TargetLib Include="$(SdkLibPath)\uuid.lib" /> + <TargetLib Include="$(SdkLibPath)\kernel32.lib" /> + <TargetLib Include="$(SdkLibPath)\advapi32.lib" /> + <TargetLib Include="$(SdkLibPath)\user32.lib" /> + <TargetLib Include="$(SdkLibPath)\shlwapi.lib" /> + </ItemGroup> + + <ItemGroup> + <RCResourceFile Include="..\Native.rc" /> + </ItemGroup> + + <ItemGroup> + <CppCompile Include="..\ICeeFileGen.cpp" /> + <CppCompile Include="..\CeeFileGenWriter.cpp" /> + <CppCompile Include="..\PEWriter.cpp" /> + <CppCompile Include="..\CeeFileGenWriterTokens.cpp" /> + </ItemGroup> + + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" /> +</Project> diff --git a/src/dlls/mscorpe/pewriter.cpp b/src/dlls/mscorpe/pewriter.cpp new file mode 100644 index 0000000000..f3b5fb194d --- /dev/null +++ b/src/dlls/mscorpe/pewriter.cpp @@ -0,0 +1,2401 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more 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; +} + +#ifndef _AMD64_ +/* 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; +} +#endif + + +/******************************************************************/ +/* 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> +{ + entry * m_entries; + PEWriterSection ** m_sections; + unsigned m_count; + unsigned m_seedCount; + + public: + SectionNameSorter(entry *entries, PEWriterSection ** sections, int count, unsigned seedSections) + : CQuickSort<entry>(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<IMAGE_SECTION_HEADER> +{ + public: + HeaderSorter(IMAGE_SECTION_HEADER *headers, int count) + : CQuickSort<IMAGE_SECTION_HEADER>(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; + } + } + +} + + +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<PEWriterSection*> +{ + public: + SectionRVASorter(PEWriterSection **elts, SSIZE_T count) + : CQuickSort<PEWriterSection*>(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<PEWriterSection *> 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((int)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 + 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 LPCWSTR fileName) { + + HRESULT hr; + +#ifdef ENC_DELTA_HACK + PathString szFileName; + DWORD len = WszGetEnvironmentVariable(L"COMP_ENC_EMIT", szFileName); + _ASSERTE(len < sizeof(szFileName)); + if (len > 0) + { + _ASSERTE(!m_pSeedFileDecoder); + szFileName.Append(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..3a4a4fd647 --- /dev/null +++ b/src/dlls/mscorpe/pewriter.h @@ -0,0 +1,337 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +#ifndef PEWriter_H +#define PEWriter_H + +#include <crtwrap.h> + +#include <windows.h> + +#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 LPCWSTR 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..6321e11d12 --- /dev/null +++ b/src/dlls/mscorpe/stdafx.cpp @@ -0,0 +1,10 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more 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..c97b552cdd --- /dev/null +++ b/src/dlls/mscorpe/stdafx.h @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +//***************************************************************************** +// stdafx.h +// +// Common include file for utility code. +//***************************************************************************** +#include <stdlib.h> // for qsort +#include <windows.h> +#include <time.h> +#include <assert.h> +#include <stdio.h> +#include <stddef.h> + +#define FEATURE_NO_HOST // Do not use host interface +#include <utilcode.h> + +#include <corpriv.h> + +#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..70ebed4d14 --- /dev/null +++ b/src/dlls/mscorpe/stubs.h @@ -0,0 +1,169 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more 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..0e9fab9860 --- /dev/null +++ b/src/dlls/mscorpe/utilcodeinit.cpp @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include "stdafx.h" +#include <utilcode.h> + +EXTERN_C void __stdcall InitializeSxS(CoreClrCallbacks const & callbacks) +{ + InitUtilcode(callbacks); +} |