diff options
Diffstat (limited to 'src/dlls/mscorpe/pewriter.cpp')
-rw-r--r-- | src/dlls/mscorpe/pewriter.cpp | 2401 |
1 files changed, 2401 insertions, 0 deletions
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); +} |