1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5//
6
7
8#include "stdafx.h"
9
10#include "corerror.h"
11
12#ifdef EnC_SUPPORTED
13#define ENC_DELTA_HACK
14#endif
15
16
17//*****************************************************************************
18// Creation for new CCeeGen instances
19//
20// Both allocate and call virtual Init() (Can't call v-func in a ctor,
21// but we want to create in 1 call);
22//*****************************************************************************
23
24HRESULT STDMETHODCALLTYPE CreateICeeGen(REFIID riid, void **pCeeGen)
25{
26 if (riid != IID_ICeeGen)
27 return E_NOTIMPL;
28 if (!pCeeGen)
29 return E_POINTER;
30 CCeeGen *pCeeFileGen;
31 HRESULT hr = CCeeGen::CreateNewInstance(pCeeFileGen);
32 if (FAILED(hr))
33 return hr;
34 pCeeFileGen->AddRef();
35 *(CCeeGen**)pCeeGen = pCeeFileGen;
36 return S_OK;
37}
38
39HRESULT CCeeGen::CreateNewInstance(CCeeGen* & pGen) // static, public
40{
41 NewHolder<CCeeGen> pGenHolder(new CCeeGen());
42 _ASSERTE(pGenHolder != NULL);
43 TESTANDRETURNMEMORY(pGenHolder);
44
45 pGenHolder->m_peSectionMan = new PESectionMan;
46 _ASSERTE(pGenHolder->m_peSectionMan != NULL);
47 TESTANDRETURNMEMORY(pGenHolder->m_peSectionMan);
48
49 HRESULT hr = pGenHolder->m_peSectionMan->Init();
50 if (FAILED(hr))
51 {
52 pGenHolder->Cleanup();
53 return hr;
54 }
55
56 hr = pGenHolder->Init();
57 if (FAILED(hr))
58 {
59 // Init() calls Cleanup() on failure
60 return hr;
61 }
62
63 pGen = pGenHolder.Extract();
64 return hr;
65}
66
67STDMETHODIMP CCeeGen::QueryInterface(REFIID riid, void** ppv)
68{
69 if (!ppv)
70 return E_POINTER;
71
72 *ppv = NULL;
73
74 if (riid == IID_IUnknown)
75 *ppv = (IUnknown*)(ICeeGen*)this;
76 else if (riid == IID_ICeeGen)
77 *ppv = (ICeeGen*)this;
78 else if (riid == IID_ICeeGenInternal)
79 *ppv = (ICeeGenInternal*)this;
80 if (*ppv == NULL)
81 return E_NOINTERFACE;
82 AddRef();
83 return S_OK;
84}
85
86STDMETHODIMP_(ULONG) CCeeGen::AddRef(void)
87{
88 return InterlockedIncrement(&m_cRefs);
89}
90
91STDMETHODIMP_(ULONG) CCeeGen::Release(void)
92{
93 if (InterlockedDecrement(&m_cRefs) == 0) {
94 Cleanup();
95 delete this;
96 return 0;
97 }
98 return 1;
99}
100
101STDMETHODIMP CCeeGen::SetInitialGrowth(DWORD growth)
102{
103 getIlSection().SetInitialGrowth(growth);
104
105 return S_OK;
106}
107
108STDMETHODIMP CCeeGen::EmitString (__in LPWSTR lpString, ULONG *RVA)
109{
110 HRESULT hr = S_OK;
111 BEGIN_ENTRYPOINT_NOTHROW;
112
113 if (! RVA)
114 IfFailGo(E_POINTER);
115 hr = getStringSection().getEmittedStringRef(lpString, RVA);
116ErrExit:
117
118 END_ENTRYPOINT_NOTHROW;
119 return hr;
120}
121
122STDMETHODIMP CCeeGen::GetString(ULONG RVA, __inout LPWSTR *lpString)
123{
124 HRESULT hr = E_FAIL;
125 BEGIN_ENTRYPOINT_NOTHROW;
126
127 if (! lpString)
128 IfFailGo(E_POINTER);
129 *lpString = (LPWSTR)getStringSection().computePointer(RVA);
130
131
132ErrExit:
133
134 END_ENTRYPOINT_NOTHROW;
135 if (*lpString)
136 return S_OK;
137 return hr;
138}
139
140STDMETHODIMP CCeeGen::AllocateMethodBuffer(ULONG cchBuffer, UCHAR **lpBuffer, ULONG *RVA)
141{
142 HRESULT hr = S_OK;
143 BEGIN_ENTRYPOINT_NOTHROW;
144
145 ULONG methodOffset = 0;
146
147 if (! cchBuffer)
148 IfFailGo(E_INVALIDARG);
149 if (! lpBuffer || ! RVA)
150 IfFailGo(E_POINTER);
151 *lpBuffer = (UCHAR*) getIlSection().getBlock(cchBuffer, 4); // Dword align
152 IfNullGo(*lpBuffer);
153
154 // have to compute the method offset after getting the block, not
155 // before (since alignment might shift it up
156 // for in-memory, just return address and will calc later
157 methodOffset = getIlSection().dataLen() - cchBuffer;
158
159 *RVA = methodOffset;
160
161ErrExit:
162 END_ENTRYPOINT_NOTHROW;
163
164 return hr;
165}
166
167STDMETHODIMP CCeeGen::GetMethodBuffer(ULONG RVA, UCHAR **lpBuffer)
168{
169 HRESULT hr = E_FAIL;
170 BEGIN_ENTRYPOINT_NOTHROW;
171
172 if (! lpBuffer)
173 IfFailGo(E_POINTER);
174 *lpBuffer = (UCHAR*)getIlSection().computePointer(RVA);
175
176
177ErrExit:
178 END_ENTRYPOINT_NOTHROW;
179
180 if (lpBuffer != NULL && *lpBuffer != 0)
181 return S_OK;
182
183 return hr;
184}
185
186STDMETHODIMP CCeeGen::ComputePointer(HCEESECTION section, ULONG RVA, UCHAR **lpBuffer)
187{
188 HRESULT hr = E_FAIL;
189 BEGIN_ENTRYPOINT_NOTHROW;
190
191 if (! lpBuffer)
192 IfFailGo(E_POINTER);
193 *lpBuffer = (UCHAR*) ((CeeSection *)section)->computePointer(RVA);
194
195ErrExit:
196 END_ENTRYPOINT_NOTHROW;
197
198 if (lpBuffer != NULL && *lpBuffer != 0)
199 return S_OK;
200 return hr;
201}
202
203STDMETHODIMP CCeeGen::GetIMapTokenIface (
204 IUnknown **pIMapToken)
205{
206 BEGIN_ENTRYPOINT_NOTHROW;
207
208 _ASSERTE(!"E_NOTIMPL");
209 END_ENTRYPOINT_NOTHROW;
210
211 return E_NOTIMPL;
212}
213
214STDMETHODIMP CCeeGen::AddNotificationHandler (
215 IUnknown *pHandler)
216{
217 BEGIN_ENTRYPOINT_NOTHROW;
218 _ASSERTE(!"E_NOTIMPL");
219 END_ENTRYPOINT_NOTHROW;
220
221 return E_NOTIMPL;
222}
223
224STDMETHODIMP CCeeGen::GenerateCeeFile ()
225{
226 BEGIN_ENTRYPOINT_NOTHROW;
227
228 _ASSERTE(!"E_NOTIMPL");
229 END_ENTRYPOINT_NOTHROW;
230
231 return E_NOTIMPL;
232}
233
234STDMETHODIMP CCeeGen::GenerateCeeMemoryImage (void **)
235{
236 BEGIN_ENTRYPOINT_NOTHROW;
237
238 _ASSERTE(!"E_NOTIMPL");
239 END_ENTRYPOINT_NOTHROW;
240
241 return E_NOTIMPL;
242}
243
244STDMETHODIMP CCeeGen::GetIlSection (
245 HCEESECTION *section)
246{
247 BEGIN_ENTRYPOINT_NOTHROW;
248
249 *section = (HCEESECTION)(m_sections[m_ilIdx]);
250 END_ENTRYPOINT_NOTHROW;
251
252 return S_OK;
253}
254
255STDMETHODIMP CCeeGen::GetStringSection(HCEESECTION *section)
256{
257 BEGIN_ENTRYPOINT_NOTHROW;
258
259 _ASSERTE(!"E_NOTIMPL");
260 END_ENTRYPOINT_NOTHROW;
261
262 return E_NOTIMPL;
263}
264
265STDMETHODIMP CCeeGen::AddSectionReloc (
266 HCEESECTION section,
267 ULONG offset,
268 HCEESECTION relativeTo,
269 CeeSectionRelocType relocType)
270{
271 HRESULT hr = S_OK;
272 BEGIN_ENTRYPOINT_NOTHROW;
273
274 hr = m_sections[m_ilIdx]->addSectReloc(offset, *(m_sections[m_ilIdx]), relocType);
275 END_ENTRYPOINT_NOTHROW;
276 return hr;
277}
278
279STDMETHODIMP CCeeGen::GetSectionCreate (
280 const char *name,
281 DWORD flags,
282 HCEESECTION *section)
283{
284 HRESULT hr = S_OK;
285 BEGIN_ENTRYPOINT_NOTHROW;
286
287 short sectionIdx;
288 hr = getSectionCreate (name, flags, (CeeSection **)section, &sectionIdx);
289 END_ENTRYPOINT_NOTHROW;
290 return hr;
291}
292
293STDMETHODIMP CCeeGen::GetSectionDataLen (
294 HCEESECTION section,
295 ULONG *dataLen)
296{
297 BEGIN_ENTRYPOINT_NOTHROW;
298
299 CeeSection *pSection = (CeeSection*) section;
300 *dataLen = pSection->dataLen();
301 END_ENTRYPOINT_NOTHROW;
302
303 return NOERROR;
304}
305
306STDMETHODIMP CCeeGen::GetSectionBlock (
307 HCEESECTION section,
308 ULONG len,
309 ULONG align,
310 void **ppBytes)
311{
312 BEGIN_ENTRYPOINT_NOTHROW;
313
314 CeeSection *pSection = (CeeSection*) section;
315 *ppBytes = (BYTE *)pSection->getBlock(len, align);
316 END_ENTRYPOINT_NOTHROW;
317
318 if (*ppBytes == 0)
319 return E_OUTOFMEMORY;
320 return NOERROR;
321}
322
323STDMETHODIMP CCeeGen::TruncateSection (
324 HCEESECTION section,
325 ULONG len)
326{
327 BEGIN_ENTRYPOINT_NOTHROW;
328
329 _ASSERTE(!"E_NOTIMPL");
330 END_ENTRYPOINT_NOTHROW;
331 return E_NOTIMPL;
332}
333
334
335
336CCeeGen::CCeeGen() // protected ctor
337{
338// All other init done in InitCommon()
339 m_cRefs = 0;
340 m_peSectionMan = NULL;
341 m_pTokenMap = NULL;
342 m_pRemapHandler = NULL;
343
344}
345
346// Shared init code between derived classes, called by virtual Init()
347HRESULT CCeeGen::Init() // not-virtual, protected
348{
349// Public, Virtual init must create our SectionManager, and
350// Common init does the rest
351 _ASSERTE(m_peSectionMan != NULL);
352
353 HRESULT hr = S_OK;
354
355 PESection *section = NULL;
356 CeeSection *ceeSection = NULL;
357
358 m_corHeader = NULL;
359
360 m_numSections = 0;
361 m_allocSections = 10;
362 m_sections = new CeeSection * [ m_allocSections ];
363 if (m_sections == NULL) {
364 hr = E_OUTOFMEMORY;
365 goto LExit;
366 }
367
368 m_pTokenMap = NULL;
369 m_fTokenMapSupported = FALSE;
370 m_pRemapHandler = NULL;
371
372 // These text section needs special support for handling string management now that we have
373 // merged the sections together, so create it with an underlying CeeSectionString rather than the
374 // more generic CeeSection
375
376 hr = m_peSectionMan->getSectionCreate(".text", sdExecute, &section);
377 if (FAILED(hr)) {
378 goto LExit;
379 }
380
381 ceeSection = new CeeSectionString(*this, *section);
382 if (ceeSection == NULL) {
383 hr = E_OUTOFMEMORY;
384 goto LExit;
385 }
386
387 hr = addSection(ceeSection, &m_stringIdx);
388
389 m_textIdx = m_stringIdx;
390
391 m_metaIdx = m_textIdx; // meta section is actually in .text
392 m_ilIdx = m_textIdx; // il section is actually in .text
393 m_corHdrIdx = -1;
394 m_encMode = FALSE;
395
396LExit:
397 if (FAILED(hr)) {
398 Cleanup();
399 }
400
401 return hr;
402}
403
404// For EnC mode, generate strings into .rdata section rather than .text section
405HRESULT CCeeGen::setEnCMode()
406{
407 PESection *section = NULL;
408 HRESULT hr = m_peSectionMan->getSectionCreate(".rdata", sdExecute, &section);
409 TESTANDRETURNHR(hr);
410 CeeSection *ceeSection = new CeeSectionString(*this, *section);
411 if (ceeSection == NULL)
412 {
413 return E_OUTOFMEMORY;
414 }
415 hr = addSection(ceeSection, &m_stringIdx);
416 if (SUCCEEDED(hr))
417 m_encMode = TRUE;
418 return hr;
419}
420
421
422HRESULT CCeeGen::cloneInstance(CCeeGen *destination) { //public, virtual
423 _ASSERTE(destination);
424
425 destination->m_pTokenMap = m_pTokenMap;
426 destination->m_fTokenMapSupported = m_fTokenMapSupported;
427 destination->m_pRemapHandler = m_pRemapHandler;
428
429 //Create a deep copy of the section manager (and each of it's sections);
430 return m_peSectionMan->cloneInstance(destination->m_peSectionMan);
431}
432
433HRESULT CCeeGen::Cleanup() // virtual
434{
435 HRESULT hr;
436 for (int i = 0; i < m_numSections; i++) {
437 delete m_sections[i];
438 }
439
440 delete [] m_sections;
441
442 CeeGenTokenMapper *pMapper = m_pTokenMap;
443 if (pMapper) {
444 if (pMapper->m_pIImport) {
445 IMetaDataEmit *pIIEmit;
446 if (SUCCEEDED( hr = pMapper->m_pIImport->QueryInterface(IID_IMetaDataEmit, (void **) &pIIEmit)))
447 {
448 pIIEmit->SetHandler(NULL);
449 pIIEmit->Release();
450 }
451 _ASSERTE(SUCCEEDED(hr));
452 pMapper->m_pIImport->Release();
453 }
454 pMapper->Release();
455 m_pTokenMap = NULL;
456 }
457
458 if (m_pRemapHandler)
459 {
460 m_pRemapHandler->Release();
461 m_pRemapHandler = NULL;
462 }
463
464 if (m_peSectionMan) {
465 m_peSectionMan->Cleanup();
466 delete m_peSectionMan;
467 }
468
469 return S_OK;
470}
471
472HRESULT CCeeGen::addSection(CeeSection *section, short *sectionIdx)
473{
474 if (m_numSections >= m_allocSections)
475 {
476 _ASSERTE(m_allocSections > 0);
477 while (m_numSections >= m_allocSections)
478 m_allocSections <<= 1;
479 CeeSection **newSections = new CeeSection * [m_allocSections];
480 if (newSections == NULL)
481 return E_OUTOFMEMORY;
482 CopyMemory(newSections, m_sections, m_numSections * sizeof(*m_sections));
483 if (m_sections != NULL)
484 delete [] m_sections;
485 m_sections = newSections;
486 }
487
488 if (sectionIdx)
489 *sectionIdx = m_numSections;
490
491 m_sections[m_numSections++] = section;
492 return S_OK;
493}
494
495HRESULT CCeeGen::getSectionCreate (const char *name, DWORD flags, CeeSection **section, short *sectionIdx)
496{
497 if (strcmp(name, ".il") == 0)
498 name = ".text";
499 else if (strcmp(name, ".meta") == 0)
500 name = ".text";
501 else if (strcmp(name, ".rdata") == 0 && !m_encMode)
502 name = ".text";
503 for (int i=0; i<m_numSections; i++) {
504 if (strcmp((const char *)m_sections[i]->name(), name) == 0) {
505 if (section)
506 *section = m_sections[i];
507 if (sectionIdx)
508 *sectionIdx = i;
509 return S_OK;
510 }
511 }
512 PESection *pewSect = NULL;
513 HRESULT hr = m_peSectionMan->getSectionCreate(name, flags, &pewSect);
514 TESTANDRETURNHR(hr);
515 CeeSection *newSect = new CeeSection(*this, *pewSect);
516 // if this fails, the PESection will get nuked in the destructor for CCeeGen
517 if (newSect == NULL)
518 {
519 return E_OUTOFMEMORY;
520 }
521
522 hr = addSection(newSect, sectionIdx);
523 TESTANDRETURNHR(hr);
524 if (section)
525 *section = newSect;
526 return S_OK;
527}
528
529
530HRESULT CCeeGen::emitMetaData(IMetaDataEmit *emitter, CeeSection* section, DWORD offset, BYTE* buffer, unsigned buffLen)
531{
532 HRESULT hr = S_OK;
533
534 ReleaseHolder<IStream> metaStream(NULL);
535
536 IfFailRet((HRESULT)CreateStreamOnHGlobal(NULL, TRUE, &metaStream));
537
538 if (! m_fTokenMapSupported) {
539 IUnknown *pMapTokenIface;
540 IfFailGoto(getMapTokenIface(&pMapTokenIface, emitter), Exit);
541
542 // Set a callback for token remap and save the tokens which change.
543 IfFailGoto(emitter->SetHandler(pMapTokenIface), Exit);
544 }
545
546 // generate the metadata
547 IfFailGoto(emitter->SaveToStream(metaStream, 0), Exit);
548
549 // get size of stream and get sufficient storage for it
550
551 if (section == 0) {
552 section = &getMetaSection();
553 STATSTG statStg;
554 IfFailGoto((HRESULT)(metaStream->Stat(&statStg, STATFLAG_NONAME)), Exit);
555
556 buffLen = statStg.cbSize.u.LowPart;
557 if(m_objSwitch)
558 {
559 CeeSection* pSect;
560 DWORD flags = IMAGE_SCN_LNK_INFO | IMAGE_SCN_LNK_REMOVE | IMAGE_SCN_ALIGN_1BYTES; // 0x00100A00
561 IfFailGoto(getSectionCreate(".cormeta",flags,&pSect,&m_metaIdx), Exit);
562 }
563 buffer = (BYTE *)section->getBlock(buffLen, sizeof(DWORD));
564 IfNullGoto(buffer, Exit);
565 offset = getMetaSection().dataLen() - buffLen;
566 }
567 else {
568 _ASSERTE(buffer[buffLen-1] || true); // Dereference 'buffer'
569 _ASSERTE(section->computeOffset(PCHAR(buffer)) == offset);
570 }
571
572 // reset seek pointer and read from stream
573 {
574 LARGE_INTEGER disp = { {0, 0} };
575 IfFailGoto((HRESULT)metaStream->Seek(disp, STREAM_SEEK_SET, NULL), Exit);
576 }
577 ULONG metaDataLen;
578 IfFailGoto((HRESULT)metaStream->Read(buffer, buffLen+1, &metaDataLen), Exit);
579
580 _ASSERTE(metaDataLen <= buffLen);
581
582#ifdef ENC_DELTA_HACK
583 {
584 extern int __cdecl fclose(FILE *);
585 WCHAR szFileName[256];
586 DWORD len = GetEnvironmentVariable(W("COMP_ENC_EMIT"), szFileName, ARRAYSIZE(szFileName));
587 _ASSERTE(len < (ARRAYSIZE(szFileName) + 6)); // +6 for the .dmeta
588 if (len > 0 && len < (ARRAYSIZE(szFileName) + 6))
589 {
590 wcscat_s(szFileName, ARRAYSIZE(szFileName), W(".dmeta"));
591 FILE *pDelta;
592 int ec = _wfopen_s(&pDelta, szFileName, W("wb"));
593 if (FAILED(ec)) { return HRESULT_FROM_WIN32(ERROR_OPEN_FAILED); }
594 fwrite(buffer, 1, metaDataLen, pDelta);
595 fclose(pDelta);
596 }
597 }
598#endif
599
600
601 // Set meta virtual address to offset of metadata within .meta, and
602 // and add a reloc for this offset, which will get turned
603 // into an rva when the pewriter writes out the file.
604
605 m_corHeader->MetaData.VirtualAddress = VAL32(offset);
606 getCorHeaderSection().addSectReloc(m_corHeaderOffset + offsetof(IMAGE_COR20_HEADER, MetaData), *section, srRelocAbsolute);
607 m_corHeader->MetaData.Size = VAL32(metaDataLen);
608
609Exit:
610
611 if (! m_fTokenMapSupported) {
612 // Remove the handler that we set
613 hr = emitter->SetHandler(NULL);
614 }
615
616#ifdef _DEBUG
617 if (FAILED(hr) && hr != E_OUTOFMEMORY)
618 _ASSERTE(!"Unexpected Failure");
619#endif
620
621 return hr;
622}
623
624// Create the COM header - it goes at front of .meta section
625// Need to do this before the meta data is copied in, but don't do at
626// the same time because may not have metadata
627HRESULT CCeeGen::allocateCorHeader()
628{
629 HRESULT hr = S_OK;
630 CeeSection *corHeaderSection = NULL;
631 if (m_corHdrIdx < 0) {
632 hr = getSectionCreate(".text0", sdExecute, &corHeaderSection, &m_corHdrIdx);
633 TESTANDRETURNHR(hr);
634
635 m_corHeaderOffset = corHeaderSection->dataLen();
636 m_corHeader = (IMAGE_COR20_HEADER*)corHeaderSection->getBlock(sizeof(IMAGE_COR20_HEADER));
637 if (! m_corHeader)
638 return E_OUTOFMEMORY;
639 memset(m_corHeader, 0, sizeof(IMAGE_COR20_HEADER));
640 }
641 return S_OK;
642}
643
644HRESULT CCeeGen::getMethodRVA(ULONG codeOffset, ULONG *codeRVA)
645{
646 _ASSERTE(codeRVA);
647 // for runtime conversion, just return the offset and will calculate real address when need the code
648 *codeRVA = codeOffset;
649 return S_OK;
650}
651
652HRESULT CCeeGen::getMapTokenIface(IUnknown **pIMapToken, IMetaDataEmit *emitter)
653{
654 if (! pIMapToken)
655 return E_POINTER;
656 if (! m_pTokenMap) {
657 // Allocate the token mapper. As code is generated, each moved token will be added to
658 // the mapper and the client will also add a TokenMap reloc for it so we can update later
659 CeeGenTokenMapper *pMapper = new CeeGenTokenMapper;
660 if (pMapper == NULL)
661 {
662 return E_OUTOFMEMORY;
663 }
664
665 if (emitter) {
666 HRESULT hr;
667 hr = emitter->QueryInterface(IID_IMetaDataImport, (PVOID *) &pMapper->m_pIImport);
668 _ASSERTE(SUCCEEDED(hr));
669 }
670 m_pTokenMap = pMapper;
671 m_fTokenMapSupported = (emitter == 0);
672
673 // If we've been holding onto a token remap handler waiting
674 // for the token mapper to get created, add it to the token
675 // mapper now and release our hold on it.
676 if (m_pRemapHandler && m_pTokenMap)
677 {
678 m_pTokenMap->AddTokenMapper(m_pRemapHandler);
679 m_pRemapHandler->Release();
680 m_pRemapHandler = NULL;
681 }
682 }
683 *pIMapToken = getTokenMapper()->GetMapTokenIface();
684 return S_OK;
685}
686
687HRESULT CCeeGen::addNotificationHandler(IUnknown *pHandler)
688{
689 // Null is no good...
690 if (!pHandler)
691 return E_POINTER;
692
693 HRESULT hr = S_OK;
694 IMapToken *pIMapToken = NULL;
695
696 // Is this an IMapToken? If so, we can put it to good use...
697 if (SUCCEEDED(pHandler->QueryInterface(IID_IMapToken,
698 (void**)&pIMapToken)))
699 {
700 // You gotta have a token mapper to use an IMapToken, though.
701 if (m_pTokenMap)
702 {
703 hr = m_pTokenMap->AddTokenMapper(pIMapToken);
704 pIMapToken->Release();
705 }
706 else
707 {
708 // Hold onto it for later, just in case a token mapper
709 // gets created. We're holding a reference to it here,
710 // too.
711 m_pRemapHandler = pIMapToken;
712 }
713 }
714
715 return hr;
716}
717