in ProfilingAPI/ReJITEnterLeaveHooks/ILRewriter.cpp [460:677]
HRESULT Export()
{
// One instruction produces 2 + sizeof(native int) bytes in the worst case which can be 10 bytes for 64-bit.
// For simplification we just use 10 here.
unsigned maxSize = m_nInstrs * 10;
m_pOutputBuffer = new BYTE[maxSize];
IfNullRet(m_pOutputBuffer);
again:
BYTE * pIL = m_pOutputBuffer;
bool fBranch = false;
unsigned offset = 0;
// Go over all instructions and produce code for them
for (ILInstr * pInstr = m_IL.m_pNext; pInstr != &m_IL; pInstr = pInstr->m_pNext)
{
assert(offset < maxSize);
pInstr->m_offset = offset;
unsigned opcode = pInstr->m_opcode;
if (opcode < CEE_COUNT)
{
// CEE_PREFIX1 refers not to instruction prefixes (like tail.), but to
// the lead byte of multi-byte opcodes. For now, the only lead byte
// supported is CEE_PREFIX1 = 0xFE.
if (opcode >= 0x100)
m_pOutputBuffer[offset++] = CEE_PREFIX1;
// This appears to depend on an implicit conversion from
// unsigned opcode down to BYTE, to deliberately lose data and have
// opcode >= 0x100 wrap around to 0.
m_pOutputBuffer[offset++] = (opcode & 0xFF);
}
BYTE flags = s_OpCodeFlags[pInstr->m_opcode];
switch (flags)
{
case 0:
break;
case 1:
*(UNALIGNED INT8 *)&(pIL[offset]) = pInstr->m_Arg8;
break;
case 2:
*(UNALIGNED INT16 *)&(pIL[offset]) = pInstr->m_Arg16;
break;
case 4:
*(UNALIGNED INT32 *)&(pIL[offset]) = pInstr->m_Arg32;
break;
case 8:
*(UNALIGNED INT64 *)&(pIL[offset]) = pInstr->m_Arg64;
break;
case 1 | OPCODEFLAGS_BranchTarget:
fBranch = true;
break;
case 4 | OPCODEFLAGS_BranchTarget:
fBranch = true;
break;
case 0 | OPCODEFLAGS_Switch:
*(UNALIGNED INT32 *)&(pIL[offset]) = pInstr->m_Arg32;
offset += sizeof(INT32);
break;
default:
assert(false);
break;
}
offset += (flags & OPCODEFLAGS_SizeMask);
}
m_IL.m_offset = offset;
if (fBranch)
{
bool fTryAgain = false;
unsigned switchBase = 0;
// Go over all control flow instructions and resolve the targets
for (ILInstr * pInstr = m_IL.m_pNext; pInstr != &m_IL; pInstr = pInstr->m_pNext)
{
unsigned opcode = pInstr->m_opcode;
if (pInstr->m_opcode == CEE_SWITCH)
{
switchBase = pInstr->m_offset + 1 + sizeof(INT32) * (pInstr->m_Arg32 + 1);
continue;
}
if (opcode == CEE_SWITCH_ARG)
{
// Switch args are special
*(UNALIGNED INT32 *)&(pIL[pInstr->m_offset]) = pInstr->m_pTarget->m_offset - switchBase;
continue;
}
BYTE flags = s_OpCodeFlags[pInstr->m_opcode];
if (flags & OPCODEFLAGS_BranchTarget)
{
int delta = pInstr->m_pTarget->m_offset - pInstr->m_pNext->m_offset;
switch (flags)
{
case 1 | OPCODEFLAGS_BranchTarget:
// Check if delta is too big to fit into an INT8.
//
// (see #pragma at top of file)
if ((INT8)delta != delta)
{
if (opcode == CEE_LEAVE_S)
{
pInstr->m_opcode = CEE_LEAVE;
}
else
{
assert(opcode >= CEE_BR_S && opcode <= CEE_BLT_UN_S);
pInstr->m_opcode = opcode - CEE_BR_S + CEE_BR;
assert(pInstr->m_opcode >= CEE_BR && pInstr->m_opcode <= CEE_BLT_UN);
}
fTryAgain = true;
continue;
}
*(UNALIGNED INT8 *)&(pIL[pInstr->m_pNext->m_offset - sizeof(INT8)]) = delta;
break;
case 4 | OPCODEFLAGS_BranchTarget:
*(UNALIGNED INT32 *)&(pIL[pInstr->m_pNext->m_offset - sizeof(INT32)]) = delta;
break;
default:
assert(false);
break;
}
}
}
// Do the whole thing again if we changed the size of some branch targets
if (fTryAgain)
goto again;
}
unsigned codeSize = offset;
unsigned totalSize;
LPBYTE pBody = NULL;
if (m_fGenerateTinyHeader)
{
// Make sure we can fit in a tiny header
if (codeSize >= 64)
return E_FAIL;
totalSize = sizeof(IMAGE_COR_ILMETHOD_TINY) + codeSize;
pBody = AllocateILMemory(totalSize);
IfNullRet(pBody);
BYTE * pCurrent = pBody;
// Here's the tiny header
*pCurrent = (BYTE)(CorILMethod_TinyFormat | (codeSize << 2));
pCurrent += sizeof(IMAGE_COR_ILMETHOD_TINY);
// And the body
CopyMemory(pCurrent, m_pOutputBuffer, codeSize);
}
else
{
// Use FAT header
unsigned alignedCodeSize = (offset + 3) & ~3;
totalSize = sizeof(IMAGE_COR_ILMETHOD_FAT) + alignedCodeSize +
(m_nEH ? (sizeof(IMAGE_COR_ILMETHOD_SECT_FAT) + sizeof(IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT) * m_nEH) : 0);
pBody = AllocateILMemory(totalSize);
IfNullRet(pBody);
BYTE * pCurrent = pBody;
IMAGE_COR_ILMETHOD_FAT *pHeader = (IMAGE_COR_ILMETHOD_FAT *)pCurrent;
pHeader->Flags = m_flags | (m_nEH ? CorILMethod_MoreSects : 0) | CorILMethod_FatFormat;
pHeader->Size = sizeof(IMAGE_COR_ILMETHOD_FAT) / sizeof(DWORD);
pHeader->MaxStack = m_maxStack;
pHeader->CodeSize = offset;
pHeader->LocalVarSigTok = m_tkLocalVarSig;
pCurrent = (BYTE*)(pHeader + 1);
CopyMemory(pCurrent, m_pOutputBuffer, codeSize);
pCurrent += alignedCodeSize;
if (m_nEH != 0)
{
IMAGE_COR_ILMETHOD_SECT_FAT *pEH = (IMAGE_COR_ILMETHOD_SECT_FAT *)pCurrent;
pEH->Kind = CorILMethod_Sect_EHTable | CorILMethod_Sect_FatFormat;
pEH->DataSize = (unsigned)(sizeof(IMAGE_COR_ILMETHOD_SECT_FAT) + sizeof(IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT) * m_nEH);
pCurrent = (BYTE*)(pEH + 1);
for (unsigned iEH = 0; iEH < m_nEH; iEH++)
{
EHClause *pSrc = &(m_pEH[iEH]);
IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT * pDst = (IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT *)pCurrent;
pDst->Flags = pSrc->m_Flags;
pDst->TryOffset = pSrc->m_pTryBegin->m_offset;
pDst->TryLength = pSrc->m_pTryEnd->m_offset - pSrc->m_pTryBegin->m_offset;
pDst->HandlerOffset = pSrc->m_pHandlerBegin->m_offset;
pDst->HandlerLength = pSrc->m_pHandlerEnd->m_pNext->m_offset - pSrc->m_pHandlerBegin->m_offset;
if ((pSrc->m_Flags & COR_ILEXCEPTION_CLAUSE_FILTER) == 0)
pDst->ClassToken = pSrc->m_ClassToken;
else
pDst->FilterOffset = pSrc->m_pFilter->m_offset;
pCurrent = (BYTE*)(pDst + 1);
}
}
}
IfFailRet(SetILFunctionBody(totalSize, pBody));
DeallocateILMemory(pBody);
return S_OK;
}