in src/coreclr/ilasm/writer.cpp [1014:1600]
HRESULT Assembler::CreatePEFile(_In_ __nullterminated WCHAR *pwzOutputFilename)
{
HRESULT hr;
DWORD mresourceSize = 0;
BYTE* mresourceData = NULL;
WCHAR* wzScopeName = NULL;
if(bClock) bClock->cMDEmitBegin = GetTickCount();
if(m_fReportProgress) printf("Creating PE file\n");
if (!m_pEmitter)
{
printf("Error: Cannot create a PE file with no metadata\n");
return E_FAIL;
}
if(!(m_fDLL || m_fEntryPointPresent))
{
printf("Error: No entry point declared for executable\n");
if(!OnErrGo) return E_FAIL;
}
if(bClock) bClock->cMDEmit1 = GetTickCount();
// Allocate space for a strong name signature if we're delay or full
// signing the assembly.
if (m_pManifest->m_sStrongName.m_pbPublicKey)
{
if (FAILED(hr = AllocateStrongNameSignature()))
{
goto exit;
}
// Public-sign by default
m_dwComImageFlags |= COMIMAGE_FLAGS_STRONGNAMESIGNED;
}
if(bClock) bClock->cMDEmit2 = GetTickCount();
if(m_VTFList.COUNT()==0)
{
Method* pMD;
Class* pClass;
unsigned N=0, OrdBase=0xFFFFFFFF, i, j;
for(i=0; (pClass = m_lstClass.PEEK(i)) != NULL; i++)
{
for(j = 0; (pMD = pClass->m_MethodList.PEEK(j)) != NULL; j++)
{
if(pMD->m_dwExportOrdinal != 0xFFFFFFFF)
{
N++;
if(pMD->m_dwExportOrdinal < OrdBase) OrdBase = pMD->m_dwExportOrdinal;
}
}
}
if(N)
{
for(i=0; (pClass = m_lstClass.PEEK(i)) != NULL; i++)
{
for(j = 0; (pMD = pClass->m_MethodList.PEEK(j)) != NULL; j++)
{
if(pMD->m_wVTSlot >= 0x8000)
{
pMD->m_wVTSlot -= (WORD)(0x8000 + OrdBase - 1);
}
}
}
SetDataSection();
char* sz = new char[20];
strcpy_s(sz,20,"VTF_EAT_internal");
EmitDataLabel(sz);
sz = new char[20];
strcpy_s(sz,20,"VTF_EAT_internal");
if(m_dwCeeFileFlags & ICEE_CREATE_FILE_PE64)
{
ULONGLONG *pdw = new ULONGLONG[N];
for(i=0; i<N; i++) pdw[i] = UI64(0xdeadbeefdeadbeef);
EmitData(pdw,sizeof(ULONGLONG)*N);
m_VTFList.PUSH(new VTFEntry((USHORT)N,COR_VTABLE_64BIT|COR_VTABLE_FROM_UNMANAGED,sz));
delete [] pdw;
}
else
{
unsigned *pdw = new unsigned[N];
for(i=0; i<N; i++) pdw[i] = 0xdeadbeef;
EmitData(pdw,sizeof(unsigned)*N);
m_VTFList.PUSH(new VTFEntry((USHORT)N,COR_VTABLE_32BIT|COR_VTABLE_FROM_UNMANAGED,sz));
delete [] pdw;
}
}
}
wzScopeName=&wzUniBuf[0];
if(m_szScopeName[0]) // default: scope name = output file name
{
MultiByteToWideChar(g_uCodePage,0,m_szScopeName,-1,wzScopeName,MAX_SCOPE_LENGTH);
}
else
{
WCHAR* pwc;
if ((pwc = (WCHAR*)u16_strrchr(m_wzOutputFileName, DIRECTORY_SEPARATOR_CHAR_A)) != NULL) pwc++;
#ifdef TARGET_WINDOWS
else if ((pwc = (WCHAR*)u16_strrchr(m_wzOutputFileName, ':')) != NULL) pwc++;
#endif
else pwc = m_wzOutputFileName;
wcsncpy_s(wzScopeName, MAX_SCOPE_LENGTH, pwc, _TRUNCATE);
}
hr = m_pEmitter->SetModuleProps(wzScopeName);
if (FAILED(hr))
goto exit;
EmitImports();
if(m_pManifest)
{
hr = S_OK;
if(m_pManifest->m_pAsmEmitter==NULL)
hr=m_pEmitter->QueryInterface(IID_IMetaDataAssemblyEmit, (void**) &(m_pManifest->m_pAsmEmitter));
if(SUCCEEDED(hr))
{
m_pManifest->EmitAssemblyRefs();
}
}
// Emit classes, class members and globals:
{
Class *pSearch;
int i;
BOOL bIsUndefClass = FALSE;
if(m_fReportProgress) printf("\nEmitting classes:\n");
for (i=1; (pSearch = m_lstClass.PEEK(i)); i++) // 0 is <Module>
{
if(m_fReportProgress)
printf("Class %d:\t%s\n",i,pSearch->m_szFQN);
if(pSearch->m_bIsMaster)
{
report->msg("Error: Reference to undefined class '%s'\n",pSearch->m_szFQN);
bIsUndefClass = TRUE;
}
if(!EmitClass(pSearch))
{
if(!OnErrGo) return E_FAIL;
}
pSearch->m_fNew = FALSE;
}
if(bIsUndefClass && !OnErrGo) return E_FAIL;
if(m_fReportProgress) printf("\nEmitting fields and methods:\n");
for (i=0; (pSearch = m_lstClass.PEEK(i)) != NULL; i++)
{
if(m_fReportProgress)
{
if(i == 0) printf("Global \t");
else printf("Class %d\t",i);
}
if(!EmitFieldsMethods(pSearch))
{
if(!OnErrGo) return E_FAIL;
}
}
}
// All ref'ed items def'ed in this file are emitted, resolve member refs to member defs:
if(bClock) bClock->cRef2DefBegin = GetTickCount();
hr = ResolveLocalMemberRefs();
if(bClock) bClock->cRef2DefEnd = GetTickCount();
if(FAILED(hr) &&(!OnErrGo)) goto exit;
// Local member refs resolved, emit events, props and method impls
{
Class *pSearch;
int i;
if(m_fReportProgress) printf("\nEmitting events and properties:\n");
for (i=0; (pSearch = m_lstClass.PEEK(i)); i++)
{
if(m_fReportProgress)
{
if(i == 0) printf("Global \t");
else printf("Class %d\t",i);
}
if(!EmitEventsProps(pSearch))
{
if(!OnErrGo) return E_FAIL;
}
pSearch->m_fNewMembers = FALSE;
}
}
if(bClock) bClock->cMDEmit3 = GetTickCount();
if(m_MethodImplDList.COUNT())
{
if(m_fReportProgress) report->msg("Method Implementations (total): %d\n",m_MethodImplDList.COUNT());
if(!EmitMethodImpls())
{
if(!OnErrGo) return E_FAIL;
}
}
// Emit the rest of the metadata
if(bClock) bClock->cMDEmit4 = GetTickCount();
hr = S_OK;
if(m_pManifest)
{
if (FAILED(hr = m_pManifest->EmitManifest())) goto exit;
}
ResolveLocalMemberRefs(); // in case CAs added some
EmitUnresolvedCustomAttributes();
// Emit typedefs as special TypeSpecs
{
#define ELEMENT_TYPE_TYPEDEF (ELEMENT_TYPE_MAX+1)
TypeDefDescr* pTDD;
uint8_t* pb;
unsigned namesize;
while((pTDD = m_TypeDefDList.POP()))
{
BinStr* pbs = new BinStr();
if(pbs)
{
namesize = 1 + (unsigned)strlen(pTDD->m_szName);
pb = pbs->getBuff(namesize + 1 + sizeof(mdToken));
*pb = ELEMENT_TYPE_TYPEDEF;
memcpy(++pb,pTDD->m_szName,namesize);
pTDD->m_tkTypeSpec = ResolveLocalMemberRef(pTDD->m_tkTypeSpec);
SET_UNALIGNED_VAL32(pb+namesize, pTDD->m_tkTypeSpec);
if(TypeFromToken(pTDD->m_tkTypeSpec)==mdtCustomAttribute)
{
CustomDescr* pCA = pTDD->m_pCA;
pbs->appendInt32(VAL32(pCA->tkType));
pbs->appendInt32(VAL32(pCA->tkOwner));
if(pCA->pBlob) pbs->append(pCA->pBlob);
}
ResolveTypeSpec(pbs);
delete pbs;
}
delete pTDD;
}
}
if(bClock) bClock->cMDEmitEnd = GetTickCount();
hr = DoLocalMemberRefFixups();
if(FAILED(hr) &&(!OnErrGo)) goto exit;
// Local member refs resolved and fixed up in BinStr method bodies. Emit the bodies.
{
Class* pClass;
Method* pMethod;
for (int i=0; (pClass = m_lstClass.PEEK(i)); i++)
{
for(int j=0; (pMethod = pClass->m_MethodList.PEEK(j)); j++)
{
if(!EmitMethodBody(pMethod,NULL))
{
report->msg("Error: failed to emit body of '%s'\n",pMethod->m_szName);
hr = E_FAIL;
if(!OnErrGo) goto exit;
}
pMethod->m_fNewBody = FALSE;
}
}
//while(MethodBody* pMB = m_MethodBodyList.POP()) delete pMB;
}
if (DoGlobalFixups() == FALSE)
return E_FAIL;
if(m_wzResourceFile)
{
MAKE_UTF8PTR_FROMWIDE(szResourceFileUtf8, m_wzResourceFile);
#ifdef TARGET_UNIX
report->msg("Warning: The Win32 resource file '%s' is ignored and not emitted on xPlatform.\n", szResourceFileUtf8);
#else
if (FAILED(hr=m_pCeeFileGen->SetResourceFileName(m_pCeeFile, m_wzResourceFile)))
{
report->msg("Warning: failed to set Win32 resource file name '%s', hr=0x%8.8X\n The Win32 resource is not emitted.\n",
szResourceFileUtf8, hr);
}
#endif
}
if (FAILED(hr=CreateTLSDirectory())) goto exit;
if (FAILED(hr=CreateDebugDirectory())) goto exit;
if (FAILED(hr=m_pCeeFileGen->SetOutputFileName(m_pCeeFile, pwzOutputFilename))) goto exit;
// Reserve a buffer for the meta-data
DWORD metaDataSize;
if (FAILED(hr=m_pEmitter->GetSaveSize(cssAccurate, &metaDataSize))) goto exit;
BYTE* metaData;
if (FAILED(hr=m_pCeeFileGen->GetSectionBlock(m_pILSection, metaDataSize, sizeof(DWORD), (void**) &metaData))) goto exit;
ULONG metaDataOffset;
if (FAILED(hr=m_pCeeFileGen->GetSectionDataLen(m_pILSection, &metaDataOffset))) goto exit;
metaDataOffset -= metaDataSize;
// set managed resource entry, if any
if(m_pManifest && m_pManifest->m_dwMResSizeTotal)
{
mresourceSize = m_pManifest->m_dwMResSizeTotal;
if (FAILED(hr=m_pCeeFileGen->GetSectionBlock(m_pILSection, mresourceSize,
sizeof(DWORD), (void**) &mresourceData))) goto exit;
if (FAILED(hr=m_pCeeFileGen->SetManifestEntry(m_pCeeFile, mresourceSize, 0))) goto exit;
}
if(m_VTFList.COUNT())
{
GlobalLabel *pGlobalLabel;
VTFEntry* pVTFEntry;
if(m_pVTable) delete m_pVTable; // can't have both; list takes precedence
m_pVTable = new BinStr();
hr = S_OK;
for(WORD k=0; (pVTFEntry = m_VTFList.POP()); k++)
{
if((pGlobalLabel = FindGlobalLabel(pVTFEntry->m_szLabel)))
{
Method* pMD;
Class* pClass;
m_pVTable->appendInt32(VAL32(pGlobalLabel->m_GlobalOffset));
m_pVTable->appendInt16(VAL16(pVTFEntry->m_wCount));
m_pVTable->appendInt16(VAL16(pVTFEntry->m_wType));
for(int i=0; (pClass = m_lstClass.PEEK(i)); i++)
{
for(WORD j = 0; (pMD = pClass->m_MethodList.PEEK(j)); j++)
{
if(pMD->m_wVTEntry == k+1)
{
char* ptr;
if(SUCCEEDED(hr = m_pCeeFileGen->ComputeSectionPointer(m_pGlobalDataSection,pGlobalLabel->m_GlobalOffset,&ptr)))
{
DWORD dwDelta;
if((pVTFEntry->m_wType & COR_VTABLE_32BIT))
{
dwDelta = (pMD->m_wVTSlot-1)*(DWORD)sizeof(DWORD);
ptr += dwDelta;
DWORD* mptr = (DWORD*)ptr;
*mptr = (DWORD)(pMD->m_Tok);
}
else
{
dwDelta = (pMD->m_wVTSlot-1)*(DWORD)sizeof(ULONGLONG);
ptr += dwDelta;
ULONGLONG* mptr = (ULONGLONG*)ptr;
*mptr = (ULONGLONG)(pMD->m_Tok);
}
if(pMD->m_dwExportOrdinal != 0xFFFFFFFF)
{
EATEntry* pEATE = new EATEntry;
pEATE->dwOrdinal = pMD->m_dwExportOrdinal;
pEATE->szAlias = pMD->m_szExportAlias ? pMD->m_szExportAlias : pMD->m_szName;
pEATE->dwStubRVA = EmitExportStub(pGlobalLabel->m_GlobalOffset+dwDelta);
m_EATList.PUSH(pEATE);
}
}
else
report->msg("Error: Failed to get pointer to label '%s' inVTable fixup\n",pVTFEntry->m_szLabel);
}
}
}
}
else
{
report->msg("Error: Unresolved label '%s' in VTable fixup\n",pVTFEntry->m_szLabel);
hr = E_FAIL;
}
delete pVTFEntry;
}
if(FAILED(hr)) goto exit;
}
if(m_pVTable)
{
ULONG i, N = m_pVTable->length()/sizeof(DWORD);
ULONG ulVTableOffset;
m_pCeeFileGen->GetSectionDataLen (m_pILSection, &ulVTableOffset);
// SetVTableEntry will align VTable on DWORD
ulVTableOffset = (ulVTableOffset + (ULONG)sizeof(DWORD) - 1) & ~((ULONG)sizeof(DWORD) - 1);
if (FAILED(hr=m_pCeeFileGen->SetVTableEntry64(m_pCeeFile, m_pVTable->length(),(void*)(m_pVTable->ptr())))) goto exit; // @WARNING: casting down from pointer-size to DWORD
for(i = 0; i < N; i+=2)
{
m_pCeeFileGen->AddSectionReloc(m_pILSection,
ulVTableOffset+(i*sizeof(DWORD)),
m_pGlobalDataSection,
srRelocAbsolute);
}
}
if(m_EATList.COUNT())
{
if(FAILED(CreateExportDirectory())) goto exit;
m_dwComImageFlags &= ~COMIMAGE_FLAGS_ILONLY;
if (m_dwCeeFileFlags & ICEE_CREATE_MACHINE_I386)
COR_SET_32BIT_REQUIRED(m_dwComImageFlags);
}
if (m_dwCeeFileFlags & ICEE_CREATE_MACHINE_ARM || m_fAppContainer)
{
// For AppContainer and ARM, you must have a minimum subsystem version of 6.02
m_wSSVersionMajor = (m_wSSVersionMajor < 6) ? 6 : m_wSSVersionMajor;
m_wSSVersionMinor = (m_wSSVersionMinor < 2 && m_wSSVersionMajor <= 6) ? 2 : m_wSSVersionMinor;
}
// Default the subsystem, instead the user doesn't set it to GUI or CUI
if (m_dwSubsystem == (DWORD)-1)
// The default for ILAsm previously was CUI, so that should be the default behavior...
m_dwSubsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI;
if (FAILED(hr=m_pCeeFileGen->SetSubsystem(m_pCeeFile, m_dwSubsystem, m_wSSVersionMajor, m_wSSVersionMinor))) goto exit;
if (FAILED(hr=m_pCeeFileGen->ClearComImageFlags(m_pCeeFile, COMIMAGE_FLAGS_ILONLY))) goto exit;
if (FAILED(hr=m_pCeeFileGen->SetComImageFlags(m_pCeeFile, m_dwComImageFlags))) goto exit;
if(m_dwFileAlignment)
{
if(FAILED(hr=m_pCeeFileGen->SetFileAlignment(m_pCeeFile, m_dwFileAlignment))) goto exit;
}
if(m_stBaseAddress)
{
if(m_dwCeeFileFlags & ICEE_CREATE_FILE_PE64)
{
if(FAILED(hr=m_pCeeFileGen->SetImageBase64(m_pCeeFile, m_stBaseAddress))) goto exit;
}
else
{
if(FAILED(hr=m_pCeeFileGen->SetImageBase(m_pCeeFile, (size_t)m_stBaseAddress))) goto exit;
}
}
if(m_stSizeOfStackReserve || m_fAppContainer || m_fHighEntropyVA)
{
PIMAGE_NT_HEADERS pNT;
PIMAGE_SECTION_HEADER pSect;
ULONG ulNumSect;
if(FAILED(hr=m_pCeeFileGen->GetHeaderInfo(m_pCeeFile,&pNT,&pSect,&ulNumSect))) goto exit;
if(m_dwCeeFileFlags & ICEE_CREATE_FILE_PE64)
{
PIMAGE_OPTIONAL_HEADER64 pOpt = (PIMAGE_OPTIONAL_HEADER64)(&pNT->OptionalHeader);
if (m_stSizeOfStackReserve)
pOpt->SizeOfStackReserve = VAL64(m_stSizeOfStackReserve);
if (m_fAppContainer)
pOpt->DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_APPCONTAINER;
if (m_fHighEntropyVA)
pOpt->DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA;
}
else
{
PIMAGE_OPTIONAL_HEADER32 pOpt = (PIMAGE_OPTIONAL_HEADER32)(&pNT->OptionalHeader);
if (m_stSizeOfStackReserve)
pOpt->SizeOfStackReserve = (DWORD)VAL32(m_stSizeOfStackReserve);
if (m_fAppContainer)
pOpt->DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_APPCONTAINER;
if (m_fHighEntropyVA)
pOpt->DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA;
}
}
//Compute all the RVAs
if (FAILED(hr=m_pCeeFileGen->LinkCeeFile(m_pCeeFile))) goto exit;
// Fix up any fields that have RVA associated with them
if (m_fHaveFieldsWithRvas) {
hr = S_OK;
ULONG dataSectionRVA;
if (FAILED(hr=m_pCeeFileGen->GetSectionRVA(m_pGlobalDataSection, &dataSectionRVA))) goto exit;
ULONG tlsSectionRVA;
if (FAILED(hr=m_pCeeFileGen->GetSectionRVA(m_pTLSSection, &tlsSectionRVA))) goto exit;
ULONG ilSectionRVA;
if (FAILED(hr=m_pCeeFileGen->GetSectionRVA(m_pILSection, &ilSectionRVA))) goto exit;
FieldDescriptor* pListFD;
Class* pClass;
for(int i=0; (pClass = m_lstClass.PEEK(i)); i++)
{
for(int j=0; (pListFD = pClass->m_FieldDList.PEEK(j)); j++)
{
if (pListFD->m_rvaLabel != 0)
{
DWORD rva;
if(*(pListFD->m_rvaLabel)=='@')
{
rva = (DWORD)atoi(pListFD->m_rvaLabel + 1);
}
else
{
GlobalLabel *pLabel = FindGlobalLabel(pListFD->m_rvaLabel);
if (pLabel == 0)
{
report->msg("Error:Could not find label '%s' for the field '%s'\n", pListFD->m_rvaLabel, pListFD->m_szName);
hr = E_FAIL;
continue;
}
rva = pLabel->m_GlobalOffset;
if (pLabel->m_Section == m_pTLSSection)
rva += tlsSectionRVA;
else if (pLabel->m_Section == m_pILSection)
rva += ilSectionRVA;
else {
_ASSERTE(pLabel->m_Section == m_pGlobalDataSection);
rva += dataSectionRVA;
}
}
if (FAILED(m_pEmitter->SetFieldRVA(pListFD->m_fdFieldTok, rva))) goto exit;
}
}
}
if (FAILED(hr)) goto exit;
}
if(bClock) bClock->cFilegenBegin = GetTickCount();
// actually output the meta-data
if (FAILED(hr=m_pCeeFileGen->EmitMetaDataAt(m_pCeeFile, m_pEmitter, m_pILSection, metaDataOffset, metaData, metaDataSize))) goto exit;
if((m_wMSVmajor < 0xFF)&&(m_wMSVminor < 0xFF))
{
STORAGESIGNATURE *pSSig = (STORAGESIGNATURE *)metaData;
BYTE* pb = metaData;
pb += 3*sizeof(DWORD)+2*sizeof(WORD)+VAL32(pSSig->iVersionString);
pb = (BYTE*)(((size_t)pb + 3) & ~3);
PSTORAGEHEADER pSHdr = (PSTORAGEHEADER)pb;
PSTORAGESTREAM pStr = (PSTORAGESTREAM)(pSHdr+1);
for(short iStr = 1; iStr <= VAL16(pSHdr->iStreams); iStr++)
{
if((strcmp(pStr->rcName,"#-")==0)||(strcmp(pStr->rcName,"#~")==0))
{
pb = metaData + VAL32(pStr->iOffset); // start of the stream header
pb += sizeof(DWORD); // skip Reserved
*pb = VAL16(m_wMSVmajor)&0xFF;
*(pb+1) = VAL16(m_wMSVminor)&0xFF;
break;
}
pb = (BYTE*)pStr;
pb += 2*sizeof(DWORD)+strlen(pStr->rcName)+1;
pb = (BYTE*)(((size_t)pb + 3) & ~3);
pStr = (PSTORAGESTREAM)pb;
}
}
if(m_fTolerateDupMethods) // means that there are /ENC files
{
if(m_pbsMD) delete m_pbsMD;
m_pbsMD = new BinStr();
memcpy(m_pbsMD->getBuff(metaDataSize),metaData,metaDataSize);
}
// actually output the resources
if(mresourceSize && mresourceData)
{
size_t i, N = m_pManifest->m_dwMResNum, sizeread, L;
BYTE *ptr = (BYTE*)mresourceData;
BOOL mrfail = FALSE;
FILE* pFile = NULL;
char sz[2048];
for(i=0; i < N; i++)
{
m_pManifest->m_fMResNew[i] = FALSE;
memset(sz,0,2048);
WideCharToMultiByte(CP_ACP,0,m_pManifest->m_wzMResName[i],-1,sz,2047,NULL,NULL);
L = m_pManifest->m_dwMResSize[i];
sizeread = 0;
memcpy(ptr,&L,sizeof(DWORD));
ptr += sizeof(DWORD);
if(fopen_s(&pFile,sz,"rb") == 0)
{
sizeread = fread((void *)ptr,1,L,pFile);
fclose(pFile);
ptr += sizeread;
}
else
{
report->msg("Error: failed to open mgd resource file '%s'\n",sz);
mrfail = TRUE;
}
if(sizeread < L)
{
report->msg("Error: failed to read expected %d bytes from mgd resource file '%s'\n",L,sz);
mrfail = TRUE;
L -= sizeread;
memset(ptr,0,L);
ptr += L;
}
}
if(mrfail)
{
hr = E_FAIL;
goto exit;
}
}
hr = S_OK;
exit:
return hr;
}