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#include "stdafx.h"
5
6// Enable building with older SDKs that don't have IMAGE_FILE_MACHINE_ARM64 defined.
7#ifndef IMAGE_FILE_MACHINE_ARM64
8#define IMAGE_FILE_MACHINE_ARM64 0xAA64 // ARM64 Little-Endian
9#endif
10
11#include "blobfetcher.h"
12#include "pedecoder.h"
13
14#ifdef _DEBUG
15#define LOGGING
16#endif
17
18#ifdef LOGGING
19#include "log.h"
20
21static const char* const RelocName[] = {
22 "Absolute", "Unk1", "Unk2", "HighLow", "Unk4", "MapToken",
23 "Relative", "FilePos", "CodeRel", "Movl64", "Dir64", "PcRel25", "PcRel64",
24 "AbsTag" };
25static const char RelocSpaces[] = " ";
26
27static INT64 s_minPcRel25;
28static INT64 s_maxPcRel25;
29#endif
30
31#ifdef EnC_SUPPORTED
32#define ENC_DELTA_HACK
33#endif
34
35 /* This is the stub program that says it can't be run in DOS mode */
36 /* it is x86 specific, but so is dos so I suppose that is OK */
37static const unsigned char x86StubPgm[] = {
38 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, 0x21, 0xb8, 0x01, 0x4c, 0xcd, 0x21, 0x54, 0x68,
39 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f,
40 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20,
41 0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x0d, 0x0d, 0x0a, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
42
43 /* number of pad bytes to make 'len' bytes align to 'align' */
44inline static unsigned roundUp(unsigned len, unsigned align) {
45 return((len + align-1) & ~(align-1));
46}
47
48inline static unsigned padLen(unsigned len, unsigned align) {
49 return(roundUp(len, align) - len);
50}
51
52inline static bool isExeOrDll(IMAGE_NT_HEADERS* ntHeaders) {
53 return ((ntHeaders->FileHeader.Characteristics & VAL16(IMAGE_FILE_EXECUTABLE_IMAGE)) != 0);
54}
55
56#ifndef IMAGE_DLLCHARACTERISTICS_NO_SEH
57#define IMAGE_DLLCHARACTERISTICS_NO_SEH 0x400
58#endif
59
60#ifndef IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
61#define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040
62#endif
63
64#ifndef IMAGE_DLLCHARACTERISTICS_NX_COMPAT
65#define IMAGE_DLLCHARACTERISTICS_NX_COMPAT 0x0100
66#endif
67
68#define COPY_AND_ADVANCE(target, src, size) { \
69 ::memcpy((void *) (target), (const void *) (src), (size)); \
70 (char *&) (target) += (size); }
71
72/******************************************************************/
73int __cdecl relocCmp(const void* a_, const void* b_) {
74
75 const PESectionReloc* a = (const PESectionReloc*) a_;
76 const PESectionReloc* b = (const PESectionReloc*) b_;
77 return (a->offset > b->offset ? 1 : (a->offset == b->offset ? 0 : -1));
78}
79
80PERelocSection::PERelocSection(PEWriterSection *pBaseReloc)
81{
82 section = pBaseReloc;
83 relocPage = (unsigned) -1;
84 relocSize = 0;
85 relocSizeAddr = NULL;
86 pages = 0;
87
88#ifdef _DEBUG
89 lastRVA = 0;
90#endif
91}
92
93void PERelocSection::AddBaseReloc(unsigned rva, int type, unsigned short highAdj)
94{
95#ifdef _DEBUG
96 // Guarantee that we're adding relocs in strict increasing order.
97 _ASSERTE(rva > lastRVA);
98 lastRVA = rva;
99#endif
100
101 if (relocPage != (rva & ~0xFFF)) {
102 if (relocSizeAddr) {
103 if ((relocSize & 1) == 1) { // pad to an even number
104 short *ps = (short*) section->getBlock(2);
105 if(ps) {
106 *ps = 0;
107 relocSize++;
108 }
109 }
110 *relocSizeAddr = VAL32(relocSize*2 + sizeof(IMAGE_BASE_RELOCATION));
111 }
112 IMAGE_BASE_RELOCATION* base = (IMAGE_BASE_RELOCATION*) section->getBlock(sizeof(IMAGE_BASE_RELOCATION));
113 if(base) {
114 relocPage = (rva & ~0xFFF);
115 relocSize = 0;
116 base->VirtualAddress = VAL32(relocPage);
117 // Size needs to be fixed up when we know it - save address here
118 relocSizeAddr = &base->SizeOfBlock;
119 pages++;
120 }
121 }
122
123 relocSize++;
124 unsigned short* offset = (unsigned short*) section->getBlock(2);
125 if(offset) {
126 *offset = VAL16((rva & 0xFFF) | (type << 12));
127 }
128}
129
130void PERelocSection::Finish(bool isPE32)
131{
132 // fixup the last reloc block (if there was one)
133 if (relocSizeAddr) {
134 if ((relocSize & 1) == 1) { // pad to an even number
135 short* psh = (short*) section->getBlock(2);
136 if(psh)
137 {
138 *psh = 0;
139 relocSize++;
140 }
141 }
142 *relocSizeAddr = VAL32(relocSize*2 + sizeof(IMAGE_BASE_RELOCATION));
143 }
144}
145
146#define GET_UNALIGNED_INT32(_ptr) ((INT32) GET_UNALIGNED_VAL32(_ptr))
147
148static inline HRESULT SignedFitsIn31Bits(INT64 immediate)
149{
150 INT64 hiBits = immediate >> 31;
151 if ((hiBits == 0) || (hiBits == -1))
152 {
153 return S_OK;
154 }
155 else
156 {
157 return E_FAIL;
158 }
159}
160
161static inline HRESULT UnsignedFitsIn32Bits(UINT64 immediate)
162{
163 UINT64 hiBits = immediate >> 32;
164 if (hiBits == 0)
165 {
166 return S_OK;
167 }
168 else
169 {
170 return E_FAIL;
171 }
172}
173
174static inline HRESULT AddOvf_RVA(DWORD& a, DWORD b)
175{
176 DWORD r = a + b;
177 if (r < a) // Check for overflow
178 return E_FAIL;
179 a = r;
180 return S_OK;
181}
182
183static inline HRESULT AddOvf_S_U32(INT64 & a, unsigned int b)
184{
185 INT64 r = a + b;
186 if (r < a) // Check for overflow
187 return E_FAIL;
188 a = r;
189 return S_OK;
190}
191
192static inline HRESULT AddOvf_S_S32(INT64 & a, int b)
193{
194 INT64 r = a + b;
195 if ( ((r >= a) && (b >= 0)) ||
196 ((r < a) && (b < 0)) )
197 {
198 a = r;
199 return S_OK;
200 }
201 return E_FAIL;
202}
203
204static inline HRESULT AddOvf_U_U32(UINT64 & a, unsigned int b)
205{
206 UINT64 r = a + b;
207 if (r < a) // Check for overflow
208 return E_FAIL;
209 a = r;
210 return S_OK;
211}
212
213static inline HRESULT AddOvf_U_U(UINT64 & a, UINT64 b)
214{
215 UINT64 r = a + b;
216 if (r < a) // Check for overflow
217 return E_FAIL;
218 a = r;
219 return S_OK;
220}
221
222static inline HRESULT SubOvf_S_U32(INT64 & a, unsigned int b)
223{
224 INT64 r = a - b;
225 if (r > a) // Check for overflow
226 return E_FAIL;
227 a = r;
228 return S_OK;
229}
230
231static inline HRESULT SubOvf_S_U(INT64 & a, UINT64 b)
232{
233 INT64 r = a - b;
234 if (r > a) // Check for overflow
235 return E_FAIL;
236 a = r;
237 return S_OK;
238}
239
240static inline HRESULT SubOvf_U_U32(UINT64 & a, unsigned int b)
241{
242 UINT64 r = a - b;
243 if (r > a) // Check for overflow
244 return E_FAIL;
245 a = r;
246 return S_OK;
247}
248
249#ifndef _AMD64_
250/* subtract two unsigned pointers yeilding a signed pointer sized int */
251static inline HRESULT SubOvf_U_U(INT64 & r, UINT64 a, UINT64 b)
252{
253 r = a - b;
254 if ( ((a >= b) && (r >= 0)) ||
255 ((a < b) && (r < 0)))
256 {
257 return S_OK;
258 }
259 return E_FAIL;
260}
261#endif
262
263
264/******************************************************************/
265/* apply the relocs for this section.
266*/
267
268HRESULT PEWriterSection::applyRelocs(IMAGE_NT_HEADERS * pNtHeaders,
269 PERelocSection * pBaseRelocSection,
270 CeeGenTokenMapper * pTokenMapper,
271 DWORD dataRvaBase,
272 DWORD rdataRvaBase,
273 DWORD codeRvaBase)
274{
275 HRESULT hr;
276
277 _ASSERTE(pBaseRelocSection); // need section to write relocs
278
279#ifdef LOGGING
280 // Ensure that if someone adds a value to CeeSectionRelocType in cor.h,
281 // that they also add an entry to RelocName.
282 static_assert_no_msg(NumItems(RelocName) == srRelocSentinel);
283#ifdef _DEBUG
284 for (unsigned int i = 0; i < srRelocSentinel; i++)
285 {
286 _ASSERTE(strlen(RelocName[i]) <= strlen(RelocSpaces));
287 }
288#endif // _DEBUG
289#endif // LOGGING
290
291 if (m_relocCur == m_relocStart)
292 return S_OK;
293
294 bool isPE32 = (pNtHeaders->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR32_MAGIC));
295
296#ifdef LOGGING
297 LOG((LF_ZAP, LL_INFO100000,
298 "APPLYING section relocs for section %s start RVA = 0x%x\n",
299 m_name, m_baseRVA));
300#endif
301
302 UINT64 imageBase = isPE32 ? VAL32(((IMAGE_NT_HEADERS32 *) pNtHeaders)->OptionalHeader.ImageBase)
303 : VAL64(((IMAGE_NT_HEADERS64 *) pNtHeaders)->OptionalHeader.ImageBase);
304
305 // sort them to make the baseRelocs pretty
306 qsort(m_relocStart, (m_relocCur - m_relocStart), sizeof(PESectionReloc), relocCmp);
307
308 for (PESectionReloc * cur = m_relocStart; cur < m_relocCur; cur++)
309 {
310 _ASSERTE((cur->offset + 4) <= m_blobFetcher.GetDataLen());
311
312 int curType = cur->type;
313 DWORD curOffset = cur->offset;
314 bool isRelocPtr = ((curType & srRelocPtr) != 0);
315 bool noBaseBaseReloc = ((curType & srNoBaseReloc) != 0);
316 UINT64 targetOffset = 0;
317 int slotNum = 0;
318 INT64 oldStarPos;
319
320 // If cur->section is NULL then this is a pointer outside the module.
321 bool externalAddress = (cur->section == NULL);
322
323 curType &= ~(srRelocPtr | srNoBaseReloc);
324
325 /* If we see any srRelocHighLow's in a PE64 file we convert them into DIR64 relocs */
326 if (!isPE32 && (curType == srRelocHighLow))
327 curType = srRelocDir64;
328
329 /* If we have an IA64 instruction fixup then extract the slot number and adjust curOffset */
330 if ((curType == srRelocIA64PcRel25) || (curType == srRelocIA64Imm64) || (curType == srRelocIA64PcRel64))
331 {
332 _ASSERTE((curOffset & 0x3) == 0);
333 slotNum = (curOffset & 0xf) >> 2;
334 curOffset &= ~0xf;
335 }
336
337 DWORD curRVA = m_baseRVA; // RVA in the PE image of the reloc site
338 IfFailRet(AddOvf_RVA(curRVA, curOffset));
339 DWORD UNALIGNED * pos = (DWORD *) m_blobFetcher.ComputePointer(curOffset);
340
341 PREFIX_ASSUME(pos != NULL);
342
343#ifdef LOGGING
344 LOG((LF_ZAP, LL_INFO1000000,
345 " Reloc %s%s%s at %-7s+%04x (RVA=%08x) at" FMT_ADDR,
346 RelocName[curType], (isRelocPtr) ? "Ptr" : " ",
347 &RelocSpaces[strlen(RelocName[curType])],
348 m_name, curOffset, curRVA, DBG_ADDR(pos)));
349#endif
350 //
351 // 'pos' is the site of the reloc
352 // Compute 'targetOffset' from pointer if necessary
353 //
354
355 if (isRelocPtr)
356 {
357 // Calculate the value of ptr to pass to computeOffset
358 char * ptr = (char *) pos;
359
360 if (curType == srRelocRelative) {
361 //
362 // Here we add sizeof(int) because we need to calculate
363 // ptr as the true call target address (x86 pc-rel)
364 // We need to true call target address since pass it
365 // to computeOffset and this function would fall if
366 // the address we pass is before the start of a section
367 //
368 oldStarPos = (SSIZE_T) ptr;
369 IfFailRet(AddOvf_S_S32(oldStarPos, GET_UNALIGNED_INT32(pos)));
370 IfFailRet(AddOvf_S_U32(oldStarPos, sizeof(int)));
371 ptr = (char *) oldStarPos;
372 targetOffset = externalAddress ? (size_t) ptr
373 : cur->section->computeOffset(ptr);
374 // We subtract off the four bytes that we added previous
375 // since the code below depends upon this
376 IfFailRet(SubOvf_U_U32(targetOffset, sizeof(int)));
377 IfFailRet(UnsignedFitsIn32Bits(targetOffset)); // Check for overflow
378 SET_UNALIGNED_VAL32(pos, targetOffset);
379 }
380 else if (curType == srRelocIA64Imm64) {
381 _ASSERTE(slotNum == 1);
382 ptr = (char *) ((intptr_t) GetIA64Imm64((UINT64 *) ptr));
383 oldStarPos = (SSIZE_T) ptr;
384 targetOffset = externalAddress ? (size_t) ptr
385 : cur->section->computeOffset(ptr);
386 _ASSERTE(!isPE32);
387 PutIA64Imm64((UINT64 *)pos, targetOffset);
388 }
389 else if (curType == srRelocIA64PcRel64) {
390 _ASSERTE(slotNum == 1);
391 ptr = (char *) ((intptr_t) GetIA64Rel64((UINT64 *) ptr));
392 oldStarPos = (SSIZE_T) ptr;
393 targetOffset = externalAddress ? (size_t) ptr
394 : cur->section->computeOffset(ptr);
395 _ASSERTE(!isPE32);
396 PutIA64Rel64((UINT64 *)pos, targetOffset);
397 }
398 else {
399 _ASSERTE(curType != srRelocIA64PcRel25);
400 ptr = (char *) GET_UNALIGNED_VALPTR(ptr);
401 oldStarPos = (SSIZE_T) ptr;
402 targetOffset = externalAddress ? (size_t) ptr
403 : cur->section->computeOffset(ptr);
404 IfFailRet(UnsignedFitsIn32Bits(targetOffset)); // Check for overflow
405 SET_UNALIGNED_VAL32(pos, targetOffset);
406 /* Zero the upper 32-bits for a machine with 64-bit pointers */
407 if (!isPE32)
408 SET_UNALIGNED_VAL32(pos+1, 0);
409 }
410 }
411#ifdef LOGGING
412 else
413 {
414 if (curType == srRelocIA64PcRel25)
415 {
416 oldStarPos = GetIA64Rel25((UINT64 *) pos, slotNum);
417 }
418 else
419 {
420 if (curType == srRelocIA64PcRel64)
421 {
422 _ASSERTE(slotNum == 1);
423 oldStarPos = GetIA64Rel64((UINT64 *) pos);
424 }
425 else if (curType == srRelocIA64Imm64)
426 {
427 oldStarPos = GetIA64Imm64((UINT64 *)pos);
428 }
429 else
430 {
431 oldStarPos = GET_UNALIGNED_VAL32(pos);
432 }
433 }
434 }
435#endif
436
437 //
438 // 'targetOffset' has now been computed. Write out the appropriate value.
439 // Record base relocs as necessary.
440 //
441
442 bool fBaseReloc = false;
443 bool fNeedBrl = false;
444 INT64 newStarPos = 0; // oldStarPos gets updated to newStarPos
445
446 if (curType == srRelocAbsolute || curType == srRelocAbsoluteTagged) {
447 _ASSERTE(!externalAddress);
448
449 newStarPos = GET_UNALIGNED_INT32(pos);
450
451 if (curType == srRelocAbsoluteTagged)
452 newStarPos = (newStarPos & ~0x80000001) >> 1;
453
454 if (rdataRvaBase > 0 && ! strcmp((const char *)(cur->section->m_name), ".rdata"))
455 IfFailRet(AddOvf_S_U32(newStarPos, rdataRvaBase));
456 else if (dataRvaBase > 0 && ! strcmp((const char *)(cur->section->m_name), ".data"))
457 IfFailRet(AddOvf_S_U32(newStarPos, dataRvaBase));
458 else
459 IfFailRet(AddOvf_S_U32(newStarPos, cur->section->m_baseRVA));
460
461 if (curType == srRelocAbsoluteTagged)
462 newStarPos = (newStarPos << 1) | 0x80000001;
463
464 SET_UNALIGNED_VAL32(pos, newStarPos);
465 }
466 else if (curType == srRelocMapToken)
467 {
468 mdToken newToken;
469 if (pTokenMapper != NULL && pTokenMapper->HasTokenMoved((mdToken)GET_UNALIGNED_VAL32(pos), newToken)) {
470 // we have a mapped token
471 SET_UNALIGNED_VAL32(pos, newToken);
472 }
473 newStarPos = GET_UNALIGNED_VAL32(pos);
474 }
475 else if (curType == srRelocFilePos)
476 {
477 _ASSERTE(!externalAddress);
478 newStarPos = GET_UNALIGNED_VAL32(pos);
479 IfFailRet(AddOvf_S_U32(newStarPos, cur->section->m_filePos));
480 SET_UNALIGNED_VAL32(pos, newStarPos);
481 }
482 else if (curType == srRelocRelative)
483 {
484 if (externalAddress) {
485#if defined(_AMD64_)
486 newStarPos = GET_UNALIGNED_INT32(pos);
487#else // x86
488 UINT64 targetAddr = GET_UNALIGNED_VAL32(pos);
489 IfFailRet(SubOvf_U_U(newStarPos, targetAddr, imageBase));
490#endif
491 }
492 else {
493 newStarPos = GET_UNALIGNED_INT32(pos);
494 IfFailRet(AddOvf_S_U32(newStarPos, cur->section->m_baseRVA));
495 }
496 IfFailRet(SubOvf_S_U32(newStarPos, curRVA));
497 IfFailRet(SignedFitsIn31Bits(newStarPos)); // Check for overflow
498 SET_UNALIGNED_VAL32(pos, newStarPos);
499 }
500 else if (curType == srRelocCodeRelative)
501 {
502 newStarPos = GET_UNALIGNED_INT32(pos);
503 IfFailRet(SubOvf_S_U32(newStarPos, codeRvaBase));
504 if (externalAddress)
505 IfFailRet(SubOvf_S_U(newStarPos, imageBase));
506 else
507 IfFailRet(AddOvf_S_U32(newStarPos, cur->section->m_baseRVA));
508 IfFailRet(SignedFitsIn31Bits(newStarPos)); // Check for overflow
509 SET_UNALIGNED_VAL32(pos, newStarPos);
510
511 }
512 else if (curType == srRelocIA64PcRel25)
513 {
514 _ASSERTE((m_baseRVA & 15) == 0);
515 _ASSERTE((cur->section->m_baseRVA & 15) == 0);
516
517 newStarPos = GetIA64Rel25((UINT64 *) pos, slotNum);
518 IfFailRet(SubOvf_S_U32(newStarPos, curRVA));
519 if (externalAddress)
520 IfFailRet(SubOvf_S_U(newStarPos, imageBase));
521 else
522 IfFailRet(AddOvf_S_U32(newStarPos, cur->section->m_baseRVA));
523
524 INT64 hiBits = newStarPos >> 24;
525
526 _ASSERTE((hiBits==0) || (hiBits==-1));
527
528 IfFailRet(AddOvf_S_U32(newStarPos, GetIA64Rel25((UINT64 *) pos, slotNum)));
529
530 hiBits = newStarPos >> 24;
531
532 _ASSERTE((hiBits==0) || (hiBits==-1));
533
534 INT32 delta32 = (INT32) newStarPos;
535
536 PutIA64Rel25((UINT64 *) pos, slotNum, delta32);
537
538 _ASSERTE(GetIA64Rel25((UINT64 *) pos, slotNum) == delta32);
539
540#ifdef LOGGING
541 if (newStarPos < s_minPcRel25)
542 s_minPcRel25 = newStarPos;
543 if (newStarPos > s_maxPcRel25)
544 s_maxPcRel25 = newStarPos;
545#endif
546 }
547 else if (curType == srRelocIA64PcRel64)
548 {
549 _ASSERTE((m_baseRVA & 15) == 0);
550 _ASSERTE(slotNum == 1);
551
552 newStarPos = GetIA64Rel64((UINT64 *) pos);
553 IfFailRet(SubOvf_S_U32(newStarPos, m_baseRVA));
554
555 if (externalAddress)
556 IfFailRet(SubOvf_S_U(newStarPos, imageBase));
557 else
558 {
559 _ASSERTE((cur->section->m_baseRVA & 15) == 0);
560 IfFailRet(AddOvf_S_U32(newStarPos, cur->section->m_baseRVA));
561 }
562
563 INT64 hiBits = newStarPos >> 24;
564
565 fNeedBrl = (hiBits != 0) && (hiBits != -1);
566
567 /* Can we convert the brl.call into a br.call? */
568 if (!fNeedBrl)
569 {
570 INT32 delta32 = (INT32) newStarPos;
571
572 UINT64 temp0 = ((UINT64 *) pos)[0];
573 UINT64 temp1 = ((UINT64 *) pos)[1];
574#ifdef _DEBUG
575 //
576 // make certain we're decoding a brl opcode, with template 4 or 5
577 //
578 UINT64 templa = (temp0 >> 0) & 0x1f;
579 UINT64 opcode = (temp1 >> 60) & 0xf;
580
581 _ASSERTE(((opcode == 0xC) || (opcode == 0xD)) &&
582 ((templa == 0x4) || (templa == 0x5)));
583#endif
584 const UINT64 mask0 = UI64(0x00003FFFFFFFFFE1);
585 const UINT64 mask1 = UI64(0x7700000FFF800000);
586
587 /* Clear all bits used as part of the slot1 and slot2 */
588 temp0 &= mask0; // opcode becomes 4 or 5
589 temp1 &= mask1;
590
591 temp0 |= 0x10; // template becomes 0x10 or 0x11
592 temp1 |= 0x200; // slot 1 becomes nop.i
593
594 ((UINT64 *) pos)[0] = temp0;
595 ((UINT64 *) pos)[1] = temp1;
596
597 PutIA64Rel25((UINT64 *) pos, 2, delta32);
598 _ASSERTE(GetIA64Rel25((UINT64 *) pos, 2) == delta32);
599 }
600 else
601 {
602 PutIA64Rel64((UINT64 *) pos, newStarPos);
603 _ASSERTE(GetIA64Rel64((UINT64 *) pos) == newStarPos);
604 }
605 }
606 else if (curType == srRelocHighLow)
607 {
608 _ASSERTE(isPE32);
609
610 // we have a 32-bit value at pos
611 UINT64 value = GET_UNALIGNED_VAL32(pos);
612
613 if (!externalAddress)
614 {
615 IfFailRet(AddOvf_U_U32(value, cur->section->m_baseRVA));
616 IfFailRet(AddOvf_U_U(value, imageBase));
617 }
618
619 IfFailRet(UnsignedFitsIn32Bits(value)); // Check for overflow
620 SET_UNALIGNED_VAL32(pos, value);
621
622 newStarPos = value;
623
624 fBaseReloc = true;
625 }
626 else if (curType == srRelocDir64)
627 {
628 _ASSERTE(!isPE32);
629
630 // we have a 64-bit value at pos
631 UINT64 UNALIGNED * p_value = (UINT64 *) pos;
632 targetOffset = *p_value;
633
634 if (!externalAddress)
635 {
636 // The upper bits of targetOffset must be zero
637 IfFailRet(UnsignedFitsIn32Bits(targetOffset));
638
639 IfFailRet(AddOvf_U_U32(targetOffset, cur->section->m_baseRVA));
640 IfFailRet(AddOvf_U_U(targetOffset, imageBase));
641 }
642
643 *p_value = targetOffset;
644 newStarPos = targetOffset;
645 fBaseReloc = true;
646 }
647 else if (curType == srRelocIA64Imm64)
648 {
649 _ASSERTE(!isPE32);
650 _ASSERTE((curRVA & 15) == 0); // This reloc should be 16-byte aligned
651
652 // we have a 64-bit value encoded in the instruction at pos
653 targetOffset = GetIA64Imm64((UINT64 *)pos);
654
655 if (!externalAddress)
656 {
657 // The upper bits of targetOffset must be zero
658 IfFailRet(UnsignedFitsIn32Bits(targetOffset));
659
660 IfFailRet(AddOvf_U_U32(targetOffset, cur->section->m_baseRVA));
661 IfFailRet(AddOvf_U_U(targetOffset, imageBase));
662 }
663
664 PutIA64Imm64((UINT64 *)pos, targetOffset);
665 newStarPos = targetOffset;
666 fBaseReloc = true;
667 }
668 else
669 {
670 _ASSERTE(!"Unknown Relocation type");
671 }
672
673 if (fBaseReloc && !noBaseBaseReloc)
674 {
675 pBaseRelocSection->AddBaseReloc(curRVA, curType);
676 }
677
678#ifdef LOGGING
679 const char* sectionName;
680
681 if (externalAddress)
682 {
683 sectionName = "external";
684 }
685 else
686 {
687 sectionName = cur->section->m_name;
688 }
689
690 LOG((LF_ZAP, LL_INFO1000000,
691 "to %-7s+%04x, old =" FMT_ADDR "new =" FMT_ADDR "%s%s\n",
692 sectionName, targetOffset,
693 DBG_ADDR(oldStarPos), DBG_ADDR(newStarPos),
694 fBaseReloc ? "(BASE RELOC)" : "",
695 fNeedBrl ? "(BRL)" : "" ));
696#endif
697
698 }
699 return S_OK;
700}
701
702/******************************************************************/
703
704PESeedSection::PESeedSection(PEDecoder * peDecoder,
705 IMAGE_SECTION_HEADER * seedSection)
706 : PEWriterSection((const char *)seedSection->Name,
707 VAL32(seedSection->Characteristics),
708 VAL32(seedSection->SizeOfRawData),
709 0),
710 m_pSeedFileDecoder(peDecoder),
711 m_pSeedSectionHeader(seedSection)
712{
713 m_baseRVA = VAL32(seedSection->VirtualAddress);
714}
715
716HRESULT PESeedSection::write(HANDLE file) {
717 ULONG sizeOfSection = VAL32(m_pSeedSectionHeader->SizeOfRawData);
718 LPCVOID sectionData = PBYTE(m_pSeedFileDecoder->GetBase()) + m_pSeedSectionHeader->PointerToRawData;
719
720 DWORD dwWritten = 0;
721 if (!WriteFile(file, sectionData, sizeOfSection, &dwWritten, NULL)) {
722 return HRESULT_FROM_GetLastError();
723 }
724 _ASSERTE(dwWritten == sizeOfSection);
725 return S_OK;
726}
727
728unsigned PESeedSection::writeMem(void ** pMem) {
729 ULONG sizeOfSection = VAL32(m_pSeedSectionHeader->SizeOfRawData);
730 LPCVOID sectionData = PBYTE(m_pSeedFileDecoder->GetBase()) + m_pSeedSectionHeader->PointerToRawData;
731
732 COPY_AND_ADVANCE(*pMem, sectionData, sizeOfSection);
733 return sizeOfSection;
734}
735
736/******************************************************************/
737HRESULT PEWriter::Init(PESectionMan *pFrom, DWORD createFlags, LPCWSTR seedFileName)
738{
739 if (pFrom)
740 *(PESectionMan*)this = *pFrom;
741 else {
742 HRESULT hr = PESectionMan::Init();
743 if (FAILED(hr))
744 return hr;
745 }
746 time_t now;
747 time(&now);
748
749#ifdef LOGGING
750 InitializeLogging();
751#endif
752
753 // Save the timestamp so that we can give it out if someone needs
754 // it.
755 m_peFileTimeStamp = (DWORD) now;
756
757 // We must be creating either a PE32 or a PE64 file
758 if (createFlags & ICEE_CREATE_FILE_PE64)
759 {
760 m_ntHeaders = (IMAGE_NT_HEADERS *) new (nothrow) IMAGE_NT_HEADERS64;
761 m_ntHeadersSize = sizeof(IMAGE_NT_HEADERS64);
762
763 if (!m_ntHeaders) return E_OUTOFMEMORY;
764 memset(m_ntHeaders, 0, m_ntHeadersSize);
765
766 m_ntHeaders->OptionalHeader.Magic = VAL16(IMAGE_NT_OPTIONAL_HDR64_MAGIC);
767 m_ntHeaders->FileHeader.SizeOfOptionalHeader = VAL16(sizeof(IMAGE_OPTIONAL_HEADER64));
768 }
769 else
770 {
771 _ASSERTE(createFlags & ICEE_CREATE_FILE_PE32);
772 m_ntHeaders = (IMAGE_NT_HEADERS *) new (nothrow) IMAGE_NT_HEADERS32;
773 m_ntHeadersSize = sizeof(IMAGE_NT_HEADERS32);
774
775 if (!m_ntHeaders) return E_OUTOFMEMORY;
776 memset(m_ntHeaders, 0, m_ntHeadersSize);
777
778 m_ntHeaders->OptionalHeader.Magic = VAL16(IMAGE_NT_OPTIONAL_HDR32_MAGIC);
779 m_ntHeaders->FileHeader.SizeOfOptionalHeader = VAL16(sizeof(IMAGE_OPTIONAL_HEADER32));
780 }
781
782 // Record whether we should create the CorExeMain and CorDllMain stubs
783 m_createCorMainStub = ((createFlags & ICEE_CREATE_FILE_CORMAIN_STUB) != 0);
784
785 // We must have a valid target machine selected
786 if ((createFlags & ICEE_CREATE_MACHINE_MASK) == ICEE_CREATE_MACHINE_I386)
787 {
788 m_ntHeaders->FileHeader.Machine = VAL16(IMAGE_FILE_MACHINE_I386);
789 }
790 else if ((createFlags & ICEE_CREATE_MACHINE_MASK) == ICEE_CREATE_MACHINE_IA64)
791 {
792 m_ntHeaders->FileHeader.Machine = VAL16(IMAGE_FILE_MACHINE_IA64);
793 }
794 else if ((createFlags & ICEE_CREATE_MACHINE_MASK) == ICEE_CREATE_MACHINE_AMD64)
795 {
796 m_ntHeaders->FileHeader.Machine = VAL16(IMAGE_FILE_MACHINE_AMD64);
797 }
798 else if ((createFlags & ICEE_CREATE_MACHINE_MASK) == ICEE_CREATE_MACHINE_ARM)
799 {
800 m_ntHeaders->FileHeader.Machine = VAL16(IMAGE_FILE_MACHINE_ARMNT);
801
802 // The OS loader already knows how to initialize pure managed assemblies and we have no legacy OS
803 // support to worry about on ARM so don't ever create the stub for ARM binaries.
804 m_createCorMainStub = false;
805 }
806 else if ((createFlags & ICEE_CREATE_MACHINE_MASK) == ICEE_CREATE_MACHINE_ARM64)
807 {
808 m_ntHeaders->FileHeader.Machine = VAL16(IMAGE_FILE_MACHINE_ARM64);
809
810 // The OS loader already knows how to initialize pure managed assemblies and we have no legacy OS
811 // support to worry about on ARM64 so don't ever create the stub for ARM64 binaries.
812 m_createCorMainStub = false;
813 }
814 else
815 {
816 _ASSERTE(!"Invalid target machine");
817 }
818
819 cEntries = IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR + 1;
820 pEntries = new (nothrow) directoryEntry[cEntries];
821 if (pEntries == NULL) return E_OUTOFMEMORY;
822 memset(pEntries, 0, sizeof(*pEntries) * cEntries);
823
824 m_ntHeaders->Signature = VAL32(IMAGE_NT_SIGNATURE);
825 m_ntHeaders->FileHeader.TimeDateStamp = VAL32((ULONG) now);
826 m_ntHeaders->FileHeader.Characteristics = VAL16(0);
827
828 if (createFlags & ICEE_CREATE_FILE_STRIP_RELOCS)
829 {
830 m_ntHeaders->FileHeader.Characteristics |= VAL16(IMAGE_FILE_RELOCS_STRIPPED);
831 }
832
833 // Linker version should be consistent with current VC level
834 m_ntHeaders->OptionalHeader.MajorLinkerVersion = 11;
835 m_ntHeaders->OptionalHeader.MinorLinkerVersion = 0;
836
837 m_ntHeaders->OptionalHeader.SectionAlignment = VAL32(IMAGE_NT_OPTIONAL_HDR_SECTION_ALIGNMENT);
838 m_ntHeaders->OptionalHeader.FileAlignment = VAL32(0);
839 m_ntHeaders->OptionalHeader.AddressOfEntryPoint = VAL32(0);
840
841 m_ntHeaders->OptionalHeader.MajorOperatingSystemVersion = VAL16(4);
842 m_ntHeaders->OptionalHeader.MinorOperatingSystemVersion = VAL16(0);
843
844 m_ntHeaders->OptionalHeader.MajorImageVersion = VAL16(0);
845 m_ntHeaders->OptionalHeader.MinorImageVersion = VAL16(0);
846 m_ntHeaders->OptionalHeader.MajorSubsystemVersion = VAL16(4);
847 m_ntHeaders->OptionalHeader.MinorSubsystemVersion = VAL16(0);
848 m_ntHeaders->OptionalHeader.Win32VersionValue = VAL32(0);
849 m_ntHeaders->OptionalHeader.Subsystem = VAL16(0);
850 m_ntHeaders->OptionalHeader.DllCharacteristics = VAL16(0);
851 m_ntHeaders->OptionalHeader.CheckSum = VAL32(0);
852 setDllCharacteristics(IMAGE_DLLCHARACTERISTICS_NO_SEH |
853 IMAGE_DLLCHARACTERISTICS_NX_COMPAT |
854 IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE |
855 IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE);
856
857 if (isPE32())
858 {
859 IMAGE_NT_HEADERS32* p_ntHeaders32 = ntHeaders32();
860 p_ntHeaders32->OptionalHeader.ImageBase = VAL32(CEE_IMAGE_BASE_32);
861 p_ntHeaders32->OptionalHeader.SizeOfStackReserve = VAL32(0x100000);
862 p_ntHeaders32->OptionalHeader.SizeOfStackCommit = VAL32(0x1000);
863 p_ntHeaders32->OptionalHeader.SizeOfHeapReserve = VAL32(0x100000);
864 p_ntHeaders32->OptionalHeader.SizeOfHeapCommit = VAL32(0x1000);
865 p_ntHeaders32->OptionalHeader.LoaderFlags = VAL32(0);
866 p_ntHeaders32->OptionalHeader.NumberOfRvaAndSizes = VAL32(16);
867 }
868 else
869 {
870 IMAGE_NT_HEADERS64* p_ntHeaders64 = ntHeaders64();
871 // FIX what are the correct values for PE+ (64-bit) ?
872 p_ntHeaders64->OptionalHeader.ImageBase = VAL64(CEE_IMAGE_BASE_64);
873 p_ntHeaders64->OptionalHeader.SizeOfStackReserve = VAL64(0x400000);
874 p_ntHeaders64->OptionalHeader.SizeOfStackCommit = VAL64(0x4000);
875 p_ntHeaders64->OptionalHeader.SizeOfHeapReserve = VAL64(0x100000);
876 p_ntHeaders64->OptionalHeader.SizeOfHeapCommit = VAL64(0x2000);
877 p_ntHeaders64->OptionalHeader.LoaderFlags = VAL32(0);
878 p_ntHeaders64->OptionalHeader.NumberOfRvaAndSizes = VAL32(16);
879 }
880
881 m_ilRVA = (DWORD) -1;
882 m_dataRvaBase = 0;
883 m_rdataRvaBase = 0;
884 m_codeRvaBase = 0;
885 m_encMode = FALSE;
886
887 virtualPos = 0;
888 filePos = 0;
889 reloc = NULL;
890 strtab = NULL;
891 headers = NULL;
892 headersEnd = NULL;
893
894 m_file = INVALID_HANDLE_VALUE;
895
896 //
897 // Seed file
898 //
899
900 m_hSeedFile = INVALID_HANDLE_VALUE;
901 m_hSeedFileMap = INVALID_HANDLE_VALUE;
902 m_pSeedFileDecoder = NULL;
903 m_iSeedSections = 0;
904 m_pSeedSectionToAdd = NULL;
905
906 if (seedFileName)
907 {
908 HandleHolder hFile (WszCreateFile(seedFileName,
909 GENERIC_READ,
910 FILE_SHARE_READ,
911 NULL,
912 OPEN_EXISTING,
913 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
914 NULL));
915
916 if (hFile == INVALID_HANDLE_VALUE)
917 return HRESULT_FROM_GetLastError();
918
919 MapViewHolder hMapFile (WszCreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL));
920 DWORD dwFileLen = SafeGetFileSize(hFile, 0);
921 if (dwFileLen == 0xffffffff)
922 return HRESULT_FROM_GetLastError();
923
924 if (hMapFile == NULL)
925 return HRESULT_FROM_GetLastError();
926
927 BYTE * baseFileView = (BYTE*) MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, 0);
928
929 PEDecoder * pPEDecoder = new (nothrow) PEDecoder(baseFileView, (COUNT_T)dwFileLen);
930 if (pPEDecoder == NULL) return E_OUTOFMEMORY;
931
932 if (pPEDecoder->Has32BitNTHeaders())
933 {
934 if ((createFlags & ICEE_CREATE_FILE_PE32) == 0)
935 return E_FAIL;
936
937 setImageBase32(DWORD(size_t(pPEDecoder->GetPreferredBase())));
938 }
939 else
940 {
941 if ((createFlags & ICEE_CREATE_FILE_PE64) == 0)
942 return E_FAIL;
943
944 setImageBase64(UINT64((intptr_t) pPEDecoder->GetPreferredBase()));
945 }
946
947 setFileAlignment (VAL32(pPEDecoder->GetFileAlignment()));
948 setSectionAlignment(VAL32(pPEDecoder->GetSectionAlignment()));
949
950 hFile.SuppressRelease();
951 hMapFile.SuppressRelease();
952
953 m_hSeedFile = hFile;
954 m_hSeedFileMap = hMapFile;
955 m_pSeedFileDecoder = pPEDecoder;
956
957#ifdef _WIN64
958 m_pSeedFileNTHeaders = pPEDecoder->GetNTHeaders64();
959#else
960 m_pSeedFileNTHeaders = pPEDecoder->GetNTHeaders32();
961#endif
962
963 // Add the seed sections
964
965 m_pSeedSections = m_pSeedFileDecoder->FindFirstSection();
966
967 m_pSeedSectionToAdd = m_pSeedSections;
968 m_iSeedSections = m_pSeedFileDecoder->GetNumberOfSections();
969
970 for (unsigned i = 0; i < m_iSeedSections; m_pSeedSectionToAdd++, i++) {
971 PESection * dummy;
972 getSectionCreate((const char *)(m_pSeedSectionToAdd->Name),
973 VAL32(m_pSeedSectionToAdd->Characteristics),
974 &dummy);
975 }
976
977 m_pSeedSectionToAdd = NULL;
978 }
979
980 return S_OK;
981}
982
983/******************************************************************/
984HRESULT PEWriter::Cleanup() {
985
986 if (m_hSeedFile != INVALID_HANDLE_VALUE)
987 {
988 CloseHandle(m_hSeedFile);
989 CloseHandle(m_hSeedFileMap);
990 delete m_pSeedFileDecoder;
991 }
992
993 if (isPE32())
994 {
995 delete ntHeaders32();
996 }
997 else
998 {
999 delete ntHeaders64();
1000 }
1001
1002 if (headers != NULL)
1003 delete [] headers;
1004
1005 if (pEntries != NULL)
1006 delete [] pEntries;
1007
1008 return PESectionMan::Cleanup();
1009}
1010
1011PESection* PEWriter::getSection(const char* name)
1012{
1013 int len = (int)strlen(name);
1014
1015 // the section name can be at most 8 characters including the null.
1016 if (len < 8)
1017 len++;
1018 else
1019 len = 8;
1020
1021 // dbPrintf(("looking for section %s\n", name));
1022 // Skip over the seed sections
1023
1024 for(PESection** cur = sectStart+m_iSeedSections; cur < sectCur; cur++) {
1025 // dbPrintf(("searching section %s\n", (*cur)->m_ame));
1026 if (strncmp((*cur)->m_name, name, len) == 0) {
1027 // dbPrintf(("found section %s\n", (*cur)->m_name));
1028 return(*cur);
1029 }
1030 }
1031 return(0);
1032}
1033
1034HRESULT PEWriter::newSection(const char* name, PESection **section,
1035 unsigned flags, unsigned estSize,
1036 unsigned estRelocs)
1037{
1038 if (m_pSeedSectionToAdd) {
1039 _ASSERTE(strcmp((const char *)(m_pSeedSectionToAdd->Name), name) == 0 &&
1040 VAL32(m_pSeedSectionToAdd->Characteristics) == flags);
1041
1042 PESeedSection * ret = new (nothrow) PESeedSection(m_pSeedFileDecoder, m_pSeedSectionToAdd);
1043 *section = ret;
1044 TESTANDRETURNMEMORY(ret);
1045 return S_OK;
1046 }
1047
1048 PEWriterSection * ret = new (nothrow) PEWriterSection(name, flags, estSize, estRelocs);
1049 *section = ret;
1050 TESTANDRETURNMEMORY(ret);
1051 return S_OK;
1052}
1053
1054ULONG PEWriter::getIlRva()
1055{
1056 // assume that pe optional header is less than size of section alignment. So this
1057 // gives out the rva for the .text section, which is merged after the .text0 section
1058 // This is verified in debug build when actually write out the file
1059 _ASSERTE(m_ilRVA > 0);
1060 return m_ilRVA;
1061}
1062
1063void PEWriter::setIlRva(ULONG offset)
1064{
1065 // assume that pe optional header is less than size of section alignment. So this
1066 // gives out the rva for the .text section, which is merged after the .text0 section
1067 // This is verified in debug build when actually write out the file
1068 m_ilRVA = roundUp(VAL32(m_ntHeaders->OptionalHeader.SectionAlignment) + offset, SUBSECTION_ALIGN);
1069}
1070
1071HRESULT PEWriter::setDirectoryEntry(PEWriterSection *section, ULONG entry, ULONG size, ULONG offset)
1072{
1073 if (entry >= cEntries)
1074 {
1075 USHORT cNewEntries = (USHORT)max((ULONG)cEntries * 2, entry + 1);
1076
1077 if (cNewEntries <= cEntries) return E_OUTOFMEMORY; // Integer overflow
1078 if (cNewEntries <= entry) return E_OUTOFMEMORY; // Integer overflow
1079
1080 directoryEntry *pNewEntries = new (nothrow) directoryEntry [ cNewEntries ];
1081 if (pNewEntries == NULL) return E_OUTOFMEMORY;
1082
1083 CopyMemory(pNewEntries, pEntries, cEntries * sizeof(*pNewEntries));
1084 ZeroMemory(pNewEntries + cEntries, (cNewEntries - cEntries) * sizeof(*pNewEntries));
1085
1086 delete [] pEntries;
1087 pEntries = pNewEntries;
1088 cEntries = cNewEntries;
1089 }
1090
1091 pEntries[entry].section = section;
1092 pEntries[entry].offset = offset;
1093 pEntries[entry].size = size;
1094 return S_OK;
1095}
1096
1097void PEWriter::setEnCRvaBase(ULONG dataBase, ULONG rdataBase)
1098{
1099 m_dataRvaBase = dataBase;
1100 m_rdataRvaBase = rdataBase;
1101 m_encMode = TRUE;
1102}
1103
1104//-----------------------------------------------------------------------------
1105// These 2 write functions must be implemented here so that they're in the same
1106// .obj file as whoever creates the FILE struct. We can't pass a FILE struct
1107// across a dll boundary and use it.
1108//-----------------------------------------------------------------------------
1109
1110HRESULT PEWriterSection::write(HANDLE file)
1111{
1112 return m_blobFetcher.Write(file);
1113}
1114
1115//-----------------------------------------------------------------------------
1116// Write out the section to the stream
1117//-----------------------------------------------------------------------------
1118HRESULT CBlobFetcher::Write(HANDLE file)
1119{
1120// Must write out each pillar (including idx = m_nIndexUsed), one after the other
1121 unsigned idx;
1122 for(idx = 0; idx <= m_nIndexUsed; idx ++) {
1123 if (m_pIndex[idx].GetDataLen() > 0)
1124 {
1125 ULONG length = m_pIndex[idx].GetDataLen();
1126 DWORD dwWritten = 0;
1127 if (!WriteFile(file, m_pIndex[idx].GetRawDataStart(), length, &dwWritten, NULL))
1128 {
1129 return HRESULT_FROM_GetLastError();
1130 }
1131 _ASSERTE(dwWritten == length);
1132 }
1133 }
1134
1135 return S_OK;
1136}
1137
1138
1139//-----------------------------------------------------------------------------
1140// These 2 write functions must be implemented here so that they're in the same
1141// .obj file as whoever creates the FILE struct. We can't pass a FILE struct
1142// across a dll boundary and use it.
1143//-----------------------------------------------------------------------------
1144
1145unsigned PEWriterSection::writeMem(void **ppMem)
1146{
1147 HRESULT hr;
1148 hr = m_blobFetcher.WriteMem(ppMem);
1149 _ASSERTE(SUCCEEDED(hr));
1150
1151 return m_blobFetcher.GetDataLen();
1152}
1153
1154//-----------------------------------------------------------------------------
1155// Write out the section to memory
1156//-----------------------------------------------------------------------------
1157HRESULT CBlobFetcher::WriteMem(void **ppMem)
1158{
1159 char **ppDest = (char **)ppMem;
1160 // Must write out each pillar (including idx = m_nIndexUsed), one after the other
1161 unsigned idx;
1162 for(idx = 0; idx <= m_nIndexUsed; idx ++) {
1163 if (m_pIndex[idx].GetDataLen() > 0)
1164 {
1165 // WARNING: macro - must enclose in curly braces
1166 COPY_AND_ADVANCE(*ppDest, m_pIndex[idx].GetRawDataStart(), m_pIndex[idx].GetDataLen());
1167 }
1168 }
1169
1170 return S_OK;
1171}
1172
1173/******************************************************************/
1174
1175//
1176// Intermediate table to sort to help determine section order
1177//
1178struct entry {
1179 const char * name; // full name of the section
1180 unsigned char nameLength; // length of the text part of the name
1181 signed char index; // numeral value at the end of the name; -1 if none
1182 unsigned short arrayIndex; // index of section within sectStart[]
1183};
1184
1185class SectionNameSorter : protected CQuickSort<entry>
1186{
1187 entry * m_entries;
1188 PEWriterSection ** m_sections;
1189 unsigned m_count;
1190 unsigned m_seedCount;
1191
1192 public:
1193 SectionNameSorter(entry *entries, PEWriterSection ** sections, int count, unsigned seedSections)
1194 : CQuickSort<entry>(entries, count),
1195 m_entries(entries),
1196 m_sections(sections),
1197 m_count(unsigned(count)),
1198 m_seedCount(seedSections)
1199 {}
1200
1201 // Sorts the entries according to alphabetical + numerical order
1202
1203 int Compare(entry *first, entry *second)
1204 {
1205 PEWriterSection * firstSection = m_sections[first->arrayIndex];
1206 PEWriterSection * secondSection = m_sections[second->arrayIndex];
1207
1208 // Seed sections are always at the start, in the order they were
1209 // added to the PEWriter
1210
1211 if (firstSection->isSeedSection() || secondSection->isSeedSection()) {
1212 if (firstSection->isSeedSection() && secondSection->isSeedSection())
1213 return first->arrayIndex - second->arrayIndex;
1214
1215 return firstSection->isSeedSection() ? -1 : 1;
1216 }
1217
1218 // Sort the names
1219
1220 int lenDiff = first->nameLength - second->nameLength;
1221 int smallerLen;
1222 if (lenDiff < 0)
1223 smallerLen = first->nameLength;
1224 else
1225 smallerLen = second->nameLength;
1226
1227 int result = strncmp(first->name, second->name, smallerLen);
1228
1229 if (result != 0)
1230 return result;
1231 else
1232 {
1233 if (lenDiff != 0)
1234 return lenDiff;
1235 else
1236 return (int)(first->index - second->index);
1237 }
1238 }
1239
1240 int SortSections()
1241 {
1242 Sort();
1243
1244 entry * ePrev = m_entries;
1245 entry * e = ePrev + 1;
1246 int iSections = 1; // First section is obviously unique
1247
1248 for (unsigned i = 1; i < m_count; i++, ePrev = e, e++) {
1249
1250 // Seed sections should stay at the front
1251 _ASSERTE(i >= m_seedCount || i == e->arrayIndex);
1252
1253 if (!m_sections[ePrev->arrayIndex]->isSeedSection() &&
1254 (ePrev->nameLength == e->nameLength) &&
1255 strncmp(ePrev->name, e->name, e->nameLength) == 0)
1256 {
1257 continue;
1258 }
1259
1260 iSections++;
1261 }
1262
1263 return iSections;
1264 }
1265};
1266
1267#define SectionIndex IMAGE_SECTION_HEADER::VirtualAddress
1268#define FirstEntryIndex IMAGE_SECTION_HEADER::SizeOfRawData
1269
1270HRESULT PEWriter::linkSortSections(entry * entries,
1271 unsigned * piEntries,
1272 unsigned * piUniqueSections)
1273{
1274 //
1275 // Preserve current section order as much as possible, but apply the following
1276 // rules:
1277 // - sections named "xxx#" are collated into a single PE section "xxx".
1278 // The contents of the CeeGen sections are sorted according to numerical
1279 // order & made contiguous in the PE section
1280 // - "text" always comes first in the file
1281 // - empty sections receive no PE section
1282 //
1283
1284 bool ExeOrDll = isExeOrDll(m_ntHeaders);
1285
1286 entry *e = entries;
1287 for (PEWriterSection **cur = getSectStart(); cur < getSectCur(); cur++) {
1288
1289 //
1290 // Throw away any old headers we've used.
1291 //
1292
1293 (*cur)->m_header = NULL;
1294
1295 //
1296 // Don't allocate PE data for 0 length sections
1297 //
1298
1299 if ((*cur)->dataLen() == 0)
1300 continue;
1301
1302 //
1303 // Special case: omit "text0" from obj's
1304 //
1305
1306 if (!ExeOrDll && strcmp((*cur)->m_name, ".text0") == 0)
1307 continue;
1308
1309 e->name = (*cur)->m_name;
1310
1311 //
1312 // Now find the end of the text part of the section name, and
1313 // calculate the numeral (if any) at the end
1314 //
1315
1316 _ASSERTE(strlen(e->name) < UCHAR_MAX);
1317 const char *p = e->name + strlen(e->name);
1318 int index = 0; // numeral at the end of the section name
1319 int placeValue = 1;
1320 if (isdigit(p[-1]))
1321 {
1322 while (--p > e->name)
1323 {
1324 if (!isdigit(*p))
1325 break;
1326 index += ((*p - '0') * placeValue);
1327 placeValue *= 10;
1328 }
1329 p++;
1330
1331 //
1332 // Special case: put "xxx" after "xxx0" and before "xxx1"
1333 //
1334
1335 if (index == 0)
1336 index = -1;
1337 }
1338
1339 _ASSERTE(index == -1 || index == atoi(p));
1340
1341 e->nameLength = (unsigned char)(p - e->name);
1342 e->index = index;
1343 e->arrayIndex = (unsigned short)(cur - getSectStart());
1344 e++;
1345 }
1346
1347 //
1348 // Sort the entries according to alphabetical + numerical order
1349 //
1350
1351 SectionNameSorter sorter(entries, getSectStart(), int(e - entries), m_iSeedSections);
1352 *piUniqueSections = sorter.SortSections();
1353
1354 *piEntries = unsigned(e - entries);
1355
1356 return S_OK;
1357}
1358
1359class HeaderSorter : public CQuickSort<IMAGE_SECTION_HEADER>
1360{
1361 public:
1362 HeaderSorter(IMAGE_SECTION_HEADER *headers, int count)
1363 : CQuickSort<IMAGE_SECTION_HEADER>(headers, count) {}
1364
1365 int Compare(IMAGE_SECTION_HEADER *first, IMAGE_SECTION_HEADER *second)
1366 {
1367 // IMAGE_SECTION_HEADER::VirtualAddress/SectionIndex contains the
1368 // index of the section
1369 return VAL32(first->SectionIndex) - VAL32(second->SectionIndex);
1370 }
1371};
1372
1373HRESULT PEWriter::linkSortHeaders(entry * entries, unsigned iEntries, unsigned iUniqueSections)
1374{
1375 if (headers != NULL)
1376 delete [] headers;
1377
1378 // 1 extra for .reloc
1379 S_UINT32 cUniqueSectionsAllocated = S_UINT32(iUniqueSections) + S_UINT32(1);
1380 if (cUniqueSectionsAllocated.IsOverflow())
1381 {
1382 return COR_E_OVERFLOW;
1383 }
1384 headers = new (nothrow) IMAGE_SECTION_HEADER[cUniqueSectionsAllocated.Value()];
1385 TESTANDRETURNMEMORY(headers);
1386
1387 memset(headers, 0, sizeof(*headers) * cUniqueSectionsAllocated.Value());
1388
1389 entry *ePrev = NULL;
1390 IMAGE_SECTION_HEADER *h = headers - 1;
1391
1392 //
1393 // Store the sorting index
1394 //
1395
1396 entry * entriesEnd = entries + iEntries;
1397
1398 for (entry * e = entries ; e < entriesEnd; e++)
1399 {
1400 if (ePrev != NULL
1401 && !getSectStart()[ePrev->arrayIndex]->isSeedSection()
1402 && e->nameLength == ePrev->nameLength
1403 && strncmp(e->name, ePrev->name, e->nameLength) == 0)
1404 {
1405 //
1406 // This section has the same name as the previous section, and
1407 // will be collapsed with the previous section.
1408 // Just update the (common) header information
1409 //
1410
1411 if (e->arrayIndex < ePrev->arrayIndex)
1412 {
1413 //
1414 // Use the smaller of the indices of e and ePrev
1415 //
1416 h->SectionIndex = VAL32(VAL32(h->SectionIndex) - (e->arrayIndex - ePrev->arrayIndex));
1417 }
1418
1419 // Store an approximation of the size of the section temporarily
1420 h->Misc.VirtualSize = VAL32(VAL32(h->Misc.VirtualSize) + getSectStart()[e->arrayIndex]->dataLen());
1421 }
1422 else
1423 {
1424 // Grab a new header
1425
1426 h++;
1427
1428 strncpy_s((char *) h->Name, sizeof(h->Name), e->name, e->nameLength);
1429
1430 setSectionIndex(h, e->arrayIndex);
1431
1432 // Store the entry index in this field temporarily
1433 h->FirstEntryIndex = VAL32((DWORD)(e - entries));
1434
1435 // Store an approximation of the size of the section temporarily
1436 h->Misc.VirtualSize = VAL32(getSectStart()[e->arrayIndex]->dataLen());
1437 }
1438 ePrev = e;
1439 }
1440
1441 headersEnd = ++h;
1442
1443 _ASSERTE(headers + iUniqueSections == headersEnd);
1444
1445 //
1446 // Sort the entries according to alphabetical + numerical order
1447 //
1448
1449 HeaderSorter headerSorter(headers, int(iUniqueSections));
1450
1451 headerSorter.Sort();
1452
1453 return S_OK;
1454} // PEWriter::linkSortHeaders
1455
1456HRESULT PEWriter::linkPlaceSections(entry * entries, unsigned iEntries)
1457{
1458 entry * entriesEnd = entries + iEntries;
1459
1460 for (IMAGE_SECTION_HEADER * h = headers; h < headersEnd; h++)
1461 {
1462 // Get to the first entry corresponding to this section header
1463
1464 entry * e = entries + VAL32(h->FirstEntryIndex);
1465 PEWriterSection *s = getSectStart()[e->arrayIndex];
1466
1467 if (s->isSeedSection()) {
1468 virtualPos = s->getBaseRVA();
1469 }
1470
1471 h->VirtualAddress = VAL32(virtualPos);
1472 h->PointerToRawData = VAL32(filePos);
1473
1474 s->m_baseRVA = virtualPos;
1475 s->m_filePos = filePos;
1476 s->m_header = h;
1477 h->Characteristics = VAL32(s->m_flags);
1478
1479#ifdef LOGGING
1480 LOG((LF_ZAP, LL_INFO10,
1481 " Section %-7s RVA=%08x, Length=%08x, FilePos=%08x\n",
1482 s->m_name, s->m_baseRVA, s->dataLen(), s->m_filePos));
1483#endif
1484
1485 unsigned dataSize = s->dataLen();
1486
1487 // Find all the other entries corresponding to this section header
1488
1489 PEWriterSection *sPrev = s;
1490 entry * ePrev = e;
1491 while (++e < entriesEnd)
1492 {
1493 if (e->nameLength != ePrev->nameLength
1494 || strncmp(e->name, ePrev->name, e->nameLength) != 0)
1495 break;
1496
1497 s = getSectStart()[e->arrayIndex];
1498 _ASSERTE(s->m_flags == VAL32(h->Characteristics));
1499
1500 sPrev->m_filePad = padLen(dataSize, SUBSECTION_ALIGN);
1501 dataSize = roundUp(dataSize, SUBSECTION_ALIGN);
1502
1503 s->m_baseRVA = virtualPos + dataSize;
1504 s->m_filePos = filePos + dataSize;
1505 s->m_header = h;
1506 sPrev = s;
1507
1508 dataSize += s->dataLen();
1509
1510#ifdef LOGGING
1511 LOG((LF_ZAP, LL_INFO10,
1512 " Section %-7s RVA=%08x, Length=%08x, FilePos=%08x\n",
1513 s->m_name, s->m_baseRVA, s->dataLen(), s->m_filePos));
1514#endif
1515
1516 ePrev = e;
1517 }
1518
1519 h->Misc.VirtualSize = VAL32(dataSize);
1520
1521 sPrev->m_filePad = padLen(dataSize, VAL32(m_ntHeaders->OptionalHeader.FileAlignment));
1522 dataSize = roundUp(dataSize, VAL32(m_ntHeaders->OptionalHeader.FileAlignment));
1523 h->SizeOfRawData = VAL32(dataSize);
1524 filePos += dataSize;
1525
1526 dataSize = roundUp(dataSize, VAL32(m_ntHeaders->OptionalHeader.SectionAlignment));
1527 virtualPos += dataSize;
1528 }
1529
1530 return S_OK;
1531}
1532
1533void PEWriter::setSectionIndex(IMAGE_SECTION_HEADER * h, unsigned sectionIndex) {
1534
1535 if (getSectStart()[sectionIndex]->isSeedSection()) {
1536 h->SectionIndex = VAL32(sectionIndex);
1537 return;
1538 }
1539
1540 //
1541 // Reserve some dummy "array index" values for special sections
1542 // at the start of the image (after the seed sections)
1543 //
1544
1545 static const char * const SpecialNames[] = { ".text", ".cormeta", NULL };
1546 enum { SPECIAL_NAMES_COUNT = NumItems(SpecialNames) };
1547
1548 for (const char * const * s = SpecialNames; /**/; s++)
1549 {
1550 if (*s == 0)
1551 {
1552 h->SectionIndex = VAL32(sectionIndex + SPECIAL_NAMES_COUNT);
1553 break;
1554 }
1555 else if (strcmp((char *) h->Name, *s) == 0)
1556 {
1557 h->SectionIndex = VAL32(m_iSeedSections + DWORD(s - SpecialNames));
1558 break;
1559 }
1560 }
1561
1562}
1563
1564
1565HRESULT PEWriter::link() {
1566
1567 //
1568 // NOTE:
1569 // link() can be called more than once! This is because at least one compiler
1570 // (the prejitter) needs to know the base addresses of some segments before it
1571 // builds others. It's up to the caller to insure the layout remains the same
1572 // after changes are made, though.
1573 //
1574
1575 //
1576 // Assign base addresses to all sections, and layout & merge PE sections
1577 //
1578
1579 bool ExeOrDll = isExeOrDll(m_ntHeaders);
1580
1581 //
1582 // Collate by name & sort by index
1583 //
1584
1585 // First collect all information into entries[]
1586
1587 int sectCount = getSectCount();
1588 entry *entries = (entry *) _alloca(sizeof(entry) * sectCount);
1589
1590 unsigned iUniqueSections, iEntries;
1591 HRESULT hr;
1592 IfFailRet(linkSortSections(entries, &iEntries, &iUniqueSections));
1593
1594 //
1595 // Now, allocate a header for each unique section name.
1596 // Also record the minimum section index for each section
1597 // so we can preserve order as much as possible.
1598 //
1599
1600 IfFailRet(linkSortHeaders(entries, iEntries, iUniqueSections));
1601
1602 //
1603 // If file alignment is not zero, it must have been set through
1604 // setFileAlignment(), in which case we leave it untouched
1605 //
1606
1607 if( VAL32(0) == m_ntHeaders->OptionalHeader.FileAlignment )
1608 {
1609 //
1610 // Figure out what file alignment to use.
1611 //
1612
1613 unsigned RoundUpVal;
1614
1615 if (ExeOrDll)
1616 {
1617 RoundUpVal = 0x0200;
1618 }
1619 else
1620 {
1621 // Don't bother padding for objs
1622 RoundUpVal = 4;
1623 }
1624
1625 m_ntHeaders->OptionalHeader.FileAlignment = VAL32(RoundUpVal);
1626 }
1627
1628 //
1629 // Now, assign a section header & location to each section
1630 //
1631
1632 if (ExeOrDll)
1633 {
1634 iUniqueSections++; // One more for .reloc
1635 filePos = sizeof(IMAGE_DOS_HEADER)+sizeof(x86StubPgm) + m_ntHeadersSize;
1636 }
1637 else
1638 {
1639 filePos = sizeof(IMAGE_FILE_HEADER);
1640 }
1641
1642 m_ntHeaders->FileHeader.NumberOfSections = VAL16(iUniqueSections);
1643
1644 filePos += iUniqueSections * sizeof(IMAGE_SECTION_HEADER);
1645 filePos = roundUp(filePos, VAL32(m_ntHeaders->OptionalHeader.FileAlignment));
1646
1647 m_ntHeaders->OptionalHeader.SizeOfHeaders = VAL32(filePos);
1648
1649 virtualPos = roundUp(filePos, VAL32(m_ntHeaders->OptionalHeader.SectionAlignment));
1650
1651 if (m_hSeedFile != INVALID_HANDLE_VALUE) {
1652 // We do not support relocating/sliding down the seed sections
1653 if (filePos > VAL32(m_pSeedSections->VirtualAddress) ||
1654 virtualPos > VAL32(m_pSeedSections->VirtualAddress))
1655 return E_FAIL;
1656
1657 if (virtualPos < VAL32(m_pSeedSections->VirtualAddress)) {
1658 virtualPos = VAL32(m_pSeedSections->VirtualAddress);
1659 }
1660 }
1661
1662 // Now finally assign RVAs to the sections
1663
1664 IfFailRet(linkPlaceSections(entries, iEntries));
1665
1666 return S_OK;
1667}
1668
1669#undef SectionIndex
1670#undef FirstEntryIndex
1671
1672
1673class SectionRVASorter : public CQuickSort<PEWriterSection*>
1674{
1675 public:
1676 SectionRVASorter(PEWriterSection **elts, SSIZE_T count)
1677 : CQuickSort<PEWriterSection*>(elts, count) {}
1678
1679 int Compare(PEWriterSection **e1, PEWriterSection **e2)
1680 {
1681 return (*e1)->getBaseRVA() - (*e2)->getBaseRVA();
1682 }
1683};
1684
1685#ifdef _PREFAST_
1686#pragma warning(push)
1687#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
1688#endif
1689HRESULT PEWriter::fixup(CeeGenTokenMapper *pMapper)
1690{
1691 HRESULT hr;
1692
1693 bool ExeOrDll = isExeOrDll(m_ntHeaders);
1694 const unsigned RoundUpVal = VAL32(m_ntHeaders->OptionalHeader.FileAlignment);
1695
1696 if(ExeOrDll)
1697 {
1698 //
1699 // Apply manual relocation for entry point field
1700 //
1701
1702 PESection *textSection;
1703 IfFailRet(getSectionCreate(".text", 0, &textSection));
1704
1705 if (m_ntHeaders->OptionalHeader.AddressOfEntryPoint != VAL32(0))
1706 m_ntHeaders->OptionalHeader.AddressOfEntryPoint = VAL32(VAL32(m_ntHeaders->OptionalHeader.AddressOfEntryPoint) + textSection->m_baseRVA);
1707
1708 //
1709 // Apply normal relocs
1710 //
1711
1712 IfFailRet(getSectionCreate(".reloc", sdReadOnly | IMAGE_SCN_MEM_DISCARDABLE,
1713 (PESection **) &reloc));
1714 reloc->m_baseRVA = virtualPos;
1715 reloc->m_filePos = filePos;
1716 reloc->m_header = headersEnd++;
1717 strcpy_s((char *)reloc->m_header->Name, sizeof(reloc->m_header->Name),
1718 ".reloc");
1719 reloc->m_header->Characteristics = VAL32(reloc->m_flags);
1720 reloc->m_header->VirtualAddress = VAL32(virtualPos);
1721 reloc->m_header->PointerToRawData = VAL32(filePos);
1722
1723#ifdef _DEBUG
1724 if (m_encMode)
1725 printf("Applying relocs for .rdata section with RVA %x\n", m_rdataRvaBase);
1726#endif
1727
1728 //
1729 // Sort the sections by RVA
1730 //
1731
1732 CQuickArray<PEWriterSection *> sections;
1733
1734 SIZE_T count = getSectCur() - getSectStart();
1735 IfFailRet(sections.ReSizeNoThrow(count));
1736 UINT i = 0;
1737 PEWriterSection **cur;
1738 for(cur = getSectStart(); cur < getSectCur(); cur++, i++)
1739 sections[i] = *cur;
1740
1741 SectionRVASorter sorter(sections.Ptr(), sections.Size());
1742
1743 sorter.Sort();
1744
1745 PERelocSection relocSection(reloc);
1746
1747 cur = sections.Ptr();
1748 PEWriterSection **curEnd = cur + sections.Size();
1749 while (cur < curEnd)
1750 {
1751 IfFailRet((*cur)->applyRelocs(m_ntHeaders,
1752 &relocSection,
1753 pMapper,
1754 m_dataRvaBase,
1755 m_rdataRvaBase,
1756 m_codeRvaBase));
1757 cur++;
1758 }
1759
1760 relocSection.Finish(isPE32());
1761 reloc->m_header->Misc.VirtualSize = VAL32(reloc->dataLen());
1762
1763 // Strip the reloc section if the flag is set
1764 if (m_ntHeaders->FileHeader.Characteristics & VAL16(IMAGE_FILE_RELOCS_STRIPPED))
1765 {
1766 reloc->m_header->Misc.VirtualSize = VAL32(0);
1767 }
1768
1769 reloc->m_header->SizeOfRawData = VAL32(roundUp(VAL32(reloc->m_header->Misc.VirtualSize), RoundUpVal));
1770 reloc->m_filePad = padLen(VAL32(reloc->m_header->Misc.VirtualSize), RoundUpVal);
1771 filePos += VAL32(reloc->m_header->SizeOfRawData);
1772 virtualPos += roundUp(VAL32(reloc->m_header->Misc.VirtualSize),
1773 VAL32(m_ntHeaders->OptionalHeader.SectionAlignment));
1774
1775 if (reloc->m_header->Misc.VirtualSize == VAL32(0))
1776 {
1777 //
1778 // Omit reloc section from section list. (It will
1779 // still be there but the loader won't see it - this
1780 // only works because we've allocated it as the last
1781 // section.)
1782 //
1783 m_ntHeaders->FileHeader.NumberOfSections = VAL16(VAL16(m_ntHeaders->FileHeader.NumberOfSections) - 1);
1784 }
1785 else
1786 {
1787 IMAGE_DATA_DIRECTORY * pRelocDataDirectory;
1788 //
1789 // Put reloc address in header
1790 //
1791 if (isPE32())
1792 {
1793 pRelocDataDirectory = &(ntHeaders32()->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]);
1794 }
1795 else
1796 {
1797 pRelocDataDirectory = &(ntHeaders64()->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]);
1798 }
1799
1800 pRelocDataDirectory->VirtualAddress = reloc->m_header->VirtualAddress;
1801 pRelocDataDirectory->Size = reloc->m_header->Misc.VirtualSize;
1802 }
1803
1804 // compute ntHeader fields that depend on the sizes of other things
1805 for(IMAGE_SECTION_HEADER *h = headersEnd-1; h >= headers; h--) { // go backwards, so first entry takes precedence
1806 if (h->Characteristics & VAL32(IMAGE_SCN_CNT_CODE)) {
1807 m_ntHeaders->OptionalHeader.BaseOfCode = h->VirtualAddress;
1808 m_ntHeaders->OptionalHeader.SizeOfCode =
1809 VAL32(VAL32(m_ntHeaders->OptionalHeader.SizeOfCode) + VAL32(h->SizeOfRawData));
1810 }
1811 if (h->Characteristics & VAL32(IMAGE_SCN_CNT_INITIALIZED_DATA)) {
1812 if (isPE32())
1813 {
1814 ntHeaders32()->OptionalHeader.BaseOfData = h->VirtualAddress;
1815 }
1816 m_ntHeaders->OptionalHeader.SizeOfInitializedData =
1817 VAL32(VAL32(m_ntHeaders->OptionalHeader.SizeOfInitializedData) + VAL32(h->SizeOfRawData));
1818 }
1819 if (h->Characteristics & VAL32(IMAGE_SCN_CNT_UNINITIALIZED_DATA)) {
1820 m_ntHeaders->OptionalHeader.SizeOfUninitializedData =
1821 VAL32(VAL32(m_ntHeaders->OptionalHeader.SizeOfUninitializedData) + VAL32(h->SizeOfRawData));
1822 }
1823 }
1824
1825 int index;
1826 IMAGE_DATA_DIRECTORY * pCurDataDirectory;
1827
1828 // go backwards, so first entry takes precedence
1829 for(cur = getSectCur()-1; getSectStart() <= cur; --cur)
1830 {
1831 index = (*cur)->getDirEntry();
1832
1833 // Is this a valid directory entry
1834 if (index > 0)
1835 {
1836 if (isPE32())
1837 {
1838 _ASSERTE((unsigned)(index) < VAL32(ntHeaders32()->OptionalHeader.NumberOfRvaAndSizes));
1839
1840 pCurDataDirectory = &(ntHeaders32()->OptionalHeader.DataDirectory[index]);
1841 }
1842 else
1843 {
1844 _ASSERTE((unsigned)(index) < VAL32(ntHeaders64()->OptionalHeader.NumberOfRvaAndSizes));
1845
1846 pCurDataDirectory = &(ntHeaders64()->OptionalHeader.DataDirectory[index]);
1847 }
1848
1849 pCurDataDirectory->VirtualAddress = VAL32((*cur)->m_baseRVA);
1850 pCurDataDirectory->Size = VAL32((*cur)->dataLen());
1851 }
1852 }
1853
1854 // handle the directory entries specified using the file.
1855 for (index=0; index < cEntries; index++)
1856 {
1857 if (pEntries[index].section)
1858 {
1859 PEWriterSection *section = pEntries[index].section;
1860 _ASSERTE(pEntries[index].offset < section->dataLen());
1861
1862 if (isPE32())
1863 pCurDataDirectory = &(ntHeaders32()->OptionalHeader.DataDirectory[index]);
1864 else
1865 pCurDataDirectory = &(ntHeaders64()->OptionalHeader.DataDirectory[index]);
1866
1867 pCurDataDirectory->VirtualAddress = VAL32(section->m_baseRVA + pEntries[index].offset);
1868 pCurDataDirectory->Size = VAL32(pEntries[index].size);
1869 }
1870 }
1871
1872 m_ntHeaders->OptionalHeader.SizeOfImage = VAL32(virtualPos);
1873 } // end if(ExeOrDll)
1874 else //i.e., if OBJ
1875 {
1876 //
1877 // Clean up note:
1878 // I've cleaned up the executable linking path, but the .obj linking
1879 // is still a bit strange, what with a "extra" reloc & strtab sections
1880 // which are created after the linking step and get treated specially.
1881 //
1882 reloc = new (nothrow) PEWriterSection(".reloc",
1883 sdReadOnly | IMAGE_SCN_MEM_DISCARDABLE, 0x4000, 0);
1884 if(reloc == NULL) return E_OUTOFMEMORY;
1885 strtab = new (nothrow) PEWriterSection(".strtab",
1886 sdReadOnly | IMAGE_SCN_MEM_DISCARDABLE, 0x4000, 0); //string table (if any)
1887 if(strtab == NULL) return E_OUTOFMEMORY;
1888
1889 DWORD* TokInSymbolTable = new (nothrow) DWORD[16386];
1890 if (TokInSymbolTable == NULL) return E_OUTOFMEMORY;
1891
1892 m_ntHeaders->FileHeader.SizeOfOptionalHeader = 0;
1893 //For each section set VirtualAddress to 0
1894 PEWriterSection **cur;
1895 for(cur = getSectStart(); cur < getSectCur(); cur++)
1896 {
1897 IMAGE_SECTION_HEADER* header = (*cur)->m_header;
1898 header->VirtualAddress = VAL32(0);
1899 }
1900 // Go over section relocations and build the Symbol Table, use .reloc section as buffer:
1901 DWORD tk=0, rva=0, NumberOfSymbols=0;
1902 BOOL ToRelocTable = FALSE;
1903 IMAGE_SYMBOL is;
1904 IMAGE_RELOCATION ir;
1905 ULONG StrTableLen = 4; //size itself only
1906 char* szSymbolName = NULL;
1907 char* pch;
1908
1909 PESection *textSection;
1910 getSectionCreate(".text", 0, &textSection);
1911
1912 for(PESectionReloc* rcur = textSection->m_relocStart; rcur < textSection->m_relocCur; rcur++)
1913 {
1914 switch((int)rcur->type)
1915 {
1916 case 0x7FFA: // Ptr to symbol name
1917#ifdef _WIN64
1918 _ASSERTE(!"this is probably broken!!");
1919#endif // _WIN64
1920 szSymbolName = (char*)(UINT_PTR)(rcur->offset);
1921 break;
1922
1923 case 0x7FFC: // Ptr to file name
1924 TokInSymbolTable[NumberOfSymbols++] = 0;
1925 memset(&is,0,sizeof(IMAGE_SYMBOL));
1926 memcpy(is.N.ShortName,".file\0\0\0",8);
1927 is.Value = 0;
1928 is.SectionNumber = VAL16(IMAGE_SYM_DEBUG);
1929 is.Type = VAL16(IMAGE_SYM_DTYPE_NULL);
1930 is.StorageClass = IMAGE_SYM_CLASS_FILE;
1931 is.NumberOfAuxSymbols = 1;
1932 if((pch = reloc->getBlock(sizeof(IMAGE_SYMBOL))))
1933 memcpy(pch,&is,sizeof(IMAGE_SYMBOL));
1934 else return E_OUTOFMEMORY;
1935 TokInSymbolTable[NumberOfSymbols++] = 0;
1936 memset(&is,0,sizeof(IMAGE_SYMBOL));
1937#ifdef _WIN64
1938 _ASSERTE(!"this is probably broken!!");
1939#endif // _WIN64
1940 strcpy_s((char*)&is,sizeof(is),(char*)(UINT_PTR)(rcur->offset));
1941 if((pch = reloc->getBlock(sizeof(IMAGE_SYMBOL))))
1942 memcpy(pch,&is,sizeof(IMAGE_SYMBOL));
1943 else return E_OUTOFMEMORY;
1944#ifdef _WIN64
1945 _ASSERTE(!"this is probably broken!!");
1946#endif // _WIN64
1947 delete (char*)(UINT_PTR)(rcur->offset);
1948 ToRelocTable = FALSE;
1949 tk = 0;
1950 szSymbolName = NULL;
1951 break;
1952
1953 case 0x7FFB: // compid value
1954 TokInSymbolTable[NumberOfSymbols++] = 0;
1955 memset(&is,0,sizeof(IMAGE_SYMBOL));
1956 memcpy(is.N.ShortName,"@comp.id",8);
1957 is.Value = VAL32(rcur->offset);
1958 is.SectionNumber = VAL16(IMAGE_SYM_ABSOLUTE);
1959 is.Type = VAL16(IMAGE_SYM_DTYPE_NULL);
1960 is.StorageClass = IMAGE_SYM_CLASS_STATIC;
1961 is.NumberOfAuxSymbols = 0;
1962 if((pch = reloc->getBlock(sizeof(IMAGE_SYMBOL))))
1963 memcpy(pch,&is,sizeof(IMAGE_SYMBOL));
1964 else return E_OUTOFMEMORY;
1965 ToRelocTable = FALSE;
1966 tk = 0;
1967 szSymbolName = NULL;
1968 break;
1969
1970 case 0x7FFF: // Token value, def
1971 tk = rcur->offset;
1972 ToRelocTable = FALSE;
1973 break;
1974
1975 case 0x7FFE: //Token value, ref
1976 tk = rcur->offset;
1977 ToRelocTable = TRUE;
1978 break;
1979
1980 case 0x7FFD: //RVA value
1981 rva = rcur->offset;
1982 if(tk)
1983 {
1984 // Add to SymbolTable
1985 DWORD i;
1986 for(i = 0; (i < NumberOfSymbols)&&(tk != TokInSymbolTable[i]); i++);
1987 if(i == NumberOfSymbols)
1988 {
1989 if(szSymbolName && *szSymbolName) // Add "extern" symbol and string table entry
1990 {
1991 TokInSymbolTable[NumberOfSymbols++] = 0;
1992 memset(&is,0,sizeof(IMAGE_SYMBOL));
1993 i++; // so reloc record (if generated) points to COM token symbol
1994 is.N.Name.Long = VAL32(StrTableLen);
1995 is.SectionNumber = VAL16(1); //textSection is the first one
1996 is.StorageClass = IMAGE_SYM_CLASS_EXTERNAL;
1997 is.NumberOfAuxSymbols = 0;
1998 is.Value = VAL32(rva);
1999 if(TypeFromToken(tk) == mdtMethodDef)
2000 {
2001 is.Type = VAL16(0x20); //IMAGE_SYM_DTYPE_FUNCTION;
2002 }
2003 if((pch = reloc->getBlock(sizeof(IMAGE_SYMBOL))))
2004 memcpy(pch,&is,sizeof(IMAGE_SYMBOL));
2005 else return E_OUTOFMEMORY;
2006 DWORD l = (DWORD)(strlen(szSymbolName)+1); // don't forget zero terminator!
2007 if((pch = reloc->getBlock(1)))
2008 memcpy(pch,szSymbolName,1);
2009 else return E_OUTOFMEMORY;
2010 delete szSymbolName;
2011 StrTableLen += l;
2012 }
2013 TokInSymbolTable[NumberOfSymbols++] = tk;
2014 memset(&is,0,sizeof(IMAGE_SYMBOL));
2015 sprintf_s((char*)is.N.ShortName,sizeof(is.N.ShortName),"%08X",tk);
2016 is.SectionNumber = VAL16(1); //textSection is the first one
2017 is.StorageClass = 0x6B; //IMAGE_SYM_CLASS_COM_TOKEN;
2018 is.Value = VAL32(rva);
2019 if(TypeFromToken(tk) == mdtMethodDef)
2020 {
2021 is.Type = VAL16(0x20); //IMAGE_SYM_DTYPE_FUNCTION;
2022 //is.NumberOfAuxSymbols = 1;
2023 }
2024 if((pch = reloc->getBlock(sizeof(IMAGE_SYMBOL))))
2025 memcpy(pch,&is,sizeof(IMAGE_SYMBOL));
2026 else return E_OUTOFMEMORY;
2027 if(is.NumberOfAuxSymbols == 1)
2028 {
2029 BYTE dummy[sizeof(IMAGE_SYMBOL)];
2030 memset(dummy,0,sizeof(IMAGE_SYMBOL));
2031 dummy[0] = dummy[2] = 1;
2032 if((pch = reloc->getBlock(sizeof(IMAGE_SYMBOL))))
2033 memcpy(pch,dummy,sizeof(IMAGE_SYMBOL));
2034 else return E_OUTOFMEMORY;
2035 TokInSymbolTable[NumberOfSymbols++] = 0;
2036 }
2037 }
2038 if(ToRelocTable)
2039 {
2040 IMAGE_SECTION_HEADER* phdr = textSection->m_header;
2041 // Add to reloc table
2042 ir.VirtualAddress = VAL32(rva);
2043 ir.SymbolTableIndex = VAL32(i);
2044 ir.Type = VAL16(IMAGE_REL_I386_SECREL);
2045 if(phdr->PointerToRelocations == 0)
2046 phdr->PointerToRelocations = VAL32(VAL32(phdr->PointerToRawData) + VAL32(phdr->SizeOfRawData));
2047 phdr->NumberOfRelocations = VAL32(VAL32(phdr->NumberOfRelocations) + 1);
2048 if((pch = reloc->getBlock(sizeof(IMAGE_RELOCATION))))
2049 memcpy(pch,&is,sizeof(IMAGE_RELOCATION));
2050 else return E_OUTOFMEMORY;
2051 }
2052 }
2053 ToRelocTable = FALSE;
2054 tk = 0;
2055 szSymbolName = NULL;
2056 break;
2057
2058 default:
2059 break;
2060 } //end switch(cur->type)
2061 } // end for all relocs
2062 // Add string table counter:
2063 if((pch = reloc->getBlock(sizeof(ULONG))))
2064 memcpy(pch,&StrTableLen,sizeof(ULONG));
2065 else return E_OUTOFMEMORY;
2066 reloc->m_header->Misc.VirtualSize = VAL32(reloc->dataLen());
2067 if(NumberOfSymbols)
2068 {
2069 // recompute the actual sizes and positions of all the sections
2070 filePos = roundUp(VAL16(m_ntHeaders->FileHeader.NumberOfSections) * sizeof(IMAGE_SECTION_HEADER)+
2071 sizeof(IMAGE_FILE_HEADER), RoundUpVal);
2072 for(cur = getSectStart(); cur < getSectCur(); cur++)
2073 {
2074 IMAGE_SECTION_HEADER* header = (*cur)->m_header;
2075 header->Misc.VirtualSize = VAL32((*cur)->dataLen());
2076 header->VirtualAddress = VAL32(0);
2077 header->SizeOfRawData = VAL32(roundUp(VAL32(header->Misc.VirtualSize), RoundUpVal));
2078 header->PointerToRawData = VAL32(filePos);
2079
2080 filePos += VAL32(header->SizeOfRawData);
2081 }
2082 m_ntHeaders->FileHeader.Machine = VAL16(0xC0EE); //COM+ EE
2083 m_ntHeaders->FileHeader.PointerToSymbolTable = VAL32(filePos);
2084 m_ntHeaders->FileHeader.NumberOfSymbols = VAL32(NumberOfSymbols);
2085 filePos += roundUp(VAL32(reloc->m_header->Misc.VirtualSize)+strtab->dataLen(),RoundUpVal);
2086 }
2087 delete[] TokInSymbolTable;
2088 } //end if OBJ
2089
2090 const unsigned headerOffset = (unsigned) (ExeOrDll ? sizeof(IMAGE_DOS_HEADER) + sizeof(x86StubPgm) : 0);
2091
2092 memset(&m_dosHeader, 0, sizeof(IMAGE_DOS_HEADER));
2093 m_dosHeader.e_magic = VAL16(IMAGE_DOS_SIGNATURE);
2094 m_dosHeader.e_cblp = VAL16(0x90); // bytes in last page
2095 m_dosHeader.e_cp = VAL16(3); // pages in file
2096 m_dosHeader.e_cparhdr = VAL16(4); // size of header in paragraphs
2097 m_dosHeader.e_maxalloc = VAL16(0xFFFF); // maximum extra mem needed
2098 m_dosHeader.e_sp = VAL16(0xB8); // initial SP value
2099 m_dosHeader.e_lfarlc = VAL16(0x40); // file offset of relocations
2100 m_dosHeader.e_lfanew = VAL32(headerOffset); // file offset of NT header!
2101
2102 return(S_OK); // SUCCESS
2103}
2104#ifdef _PREFAST_
2105#pragma warning(pop)
2106#endif
2107
2108HRESULT PEWriter::Open(__in LPCWSTR fileName)
2109{
2110 _ASSERTE(m_file == INVALID_HANDLE_VALUE);
2111 HRESULT hr = NOERROR;
2112
2113 m_file = WszCreateFile(fileName,
2114 GENERIC_WRITE,
2115 0, // No sharing. Was: FILE_SHARE_READ | FILE_SHARE_WRITE,
2116 NULL,
2117 CREATE_ALWAYS,
2118 FILE_ATTRIBUTE_NORMAL,
2119 NULL );
2120 if (m_file == INVALID_HANDLE_VALUE)
2121 hr = HRESULT_FROM_GetLastErrorNA();
2122
2123 return hr;
2124}
2125
2126HRESULT PEWriter::Seek(int offset)
2127{
2128 _ASSERTE(m_file != INVALID_HANDLE_VALUE);
2129 if (SetFilePointer(m_file, offset, 0, FILE_BEGIN))
2130 return S_OK;
2131 else
2132 return HRESULT_FROM_GetLastError();
2133}
2134
2135HRESULT PEWriter::Write(const void *data, int size)
2136{
2137 _ASSERTE(m_file != INVALID_HANDLE_VALUE);
2138
2139 HRESULT hr = S_OK;
2140 DWORD dwWritten = 0;
2141 if (size)
2142 {
2143 CQuickBytes zero;
2144 if (data == NULL)
2145 {
2146 hr = zero.ReSizeNoThrow(size);
2147 if (SUCCEEDED(hr))
2148 {
2149 ZeroMemory(zero.Ptr(), size);
2150 data = zero.Ptr();
2151 }
2152 }
2153
2154 if (WriteFile(m_file, data, size, &dwWritten, NULL))
2155 {
2156 _ASSERTE(dwWritten == (DWORD)size);
2157 }
2158 else
2159 hr = HRESULT_FROM_GetLastError();
2160 }
2161
2162 return hr;
2163}
2164
2165HRESULT PEWriter::Pad(int align)
2166{
2167 DWORD offset = SetFilePointer(m_file, 0, NULL, FILE_CURRENT);
2168 int pad = padLen(offset, align);
2169 if (pad > 0)
2170 return Write(NULL, pad);
2171 else
2172 return S_FALSE;
2173}
2174
2175HRESULT PEWriter::Close()
2176{
2177 if (m_file == INVALID_HANDLE_VALUE)
2178 return S_OK;
2179
2180 HRESULT hr;
2181 if (CloseHandle(m_file))
2182 hr = S_OK;
2183 else
2184 hr = HRESULT_FROM_GetLastError();
2185
2186 m_file = INVALID_HANDLE_VALUE;
2187
2188 return hr;
2189}
2190
2191/******************************************************************/
2192HRESULT PEWriter::write(__in LPCWSTR fileName) {
2193
2194 HRESULT hr;
2195
2196#ifdef ENC_DELTA_HACK
2197 PathString szFileName;
2198 DWORD len = WszGetEnvironmentVariable(L"COMP_ENC_EMIT", szFileName);
2199 _ASSERTE(len < sizeof(szFileName));
2200 if (len > 0)
2201 {
2202 _ASSERTE(!m_pSeedFileDecoder);
2203 szFileName.Append(L".dil");
2204
2205 HANDLE pDelta = WszCreateFile(szFileName,
2206 GENERIC_WRITE,
2207 FILE_SHARE_READ | FILE_SHARE_WRITE,
2208 NULL,
2209 CREATE_ALWAYS,
2210 FILE_ATTRIBUTE_NORMAL,
2211 NULL );
2212 if (pDelta == INVALID_HANDLE_VALUE) {
2213 hr = HRESULT_FROM_GetLastError();
2214 _ASSERTE(!"failure so open .dil file");
2215 goto ErrExit;
2216 }
2217
2218 // write the actual data
2219 for (PEWriterSection **cur = getSectStart(); cur < getSectCur(); cur++) {
2220 if (strcmp((*cur)->m_name, ".text") == 0)
2221 {
2222 hr = (*cur)->write(pDelta);
2223 CloseHandle(pDelta);
2224 pDelta = NULL;
2225 if (FAILED(hr))
2226 {
2227 _ASSERT(!"failure to write to .dil file");
2228 goto ErrExit;
2229 }
2230 break;
2231 }
2232 }
2233 PREFIX_ASSUME(!pDelta);
2234 return S_OK;
2235 }
2236#endif
2237
2238 bool ExeOrDll;
2239 unsigned RoundUpVal;
2240 ExeOrDll = isExeOrDll(m_ntHeaders);
2241 RoundUpVal = VAL32(m_ntHeaders->OptionalHeader.FileAlignment);
2242
2243 IfFailGo(Open(fileName));
2244
2245 if(ExeOrDll)
2246 {
2247 // write the PE headers
2248 IfFailGo(Write(&m_dosHeader, sizeof(IMAGE_DOS_HEADER)));
2249 IfFailGo(Write(x86StubPgm, sizeof(x86StubPgm)));
2250 IfFailGo(Write(m_ntHeaders, m_ntHeadersSize));
2251 }
2252 else
2253 {
2254 // write the object file header
2255 IfFailGo(Write(&(m_ntHeaders->FileHeader),sizeof(IMAGE_FILE_HEADER)));
2256 }
2257
2258 IfFailGo(Write(headers, (int)(sizeof(IMAGE_SECTION_HEADER)*(headersEnd-headers))));
2259
2260 IfFailGo(Pad(RoundUpVal));
2261
2262 // write the actual data
2263 for (PEWriterSection **cur = getSectStart(); cur < getSectCur(); cur++) {
2264 if ((*cur)->m_header != NULL) {
2265 IfFailGo(Seek((*cur)->m_filePos));
2266 IfFailGo((*cur)->write(m_file));
2267 IfFailGo(Write(NULL, (*cur)->m_filePad));
2268 }
2269 }
2270
2271 // writes for an object file
2272 if (!ExeOrDll)
2273 {
2274 // write the relocs section (Does nothing if relocs section is empty)
2275 IfFailGo(reloc->write(m_file));
2276 //write string table (obj only, empty for exe or dll)
2277 IfFailGo(strtab->write(m_file));
2278 int lena = padLen(VAL32(reloc->m_header->Misc.VirtualSize)+strtab->dataLen(), RoundUpVal);
2279 if (lena > 0)
2280 IfFailGo(Write(NULL, lena));
2281 }
2282
2283 return Close();
2284
2285 ErrExit:
2286 Close();
2287
2288 return hr;
2289}
2290
2291HRESULT PEWriter::write(void ** ppImage)
2292{
2293 bool ExeOrDll = isExeOrDll(m_ntHeaders);
2294 const unsigned RoundUpVal = VAL32(m_ntHeaders->OptionalHeader.FileAlignment);
2295 char *pad = (char *) _alloca(RoundUpVal);
2296 memset(pad, 0, RoundUpVal);
2297
2298 size_t lSize = filePos;
2299 if (!ExeOrDll)
2300 {
2301 lSize += reloc->dataLen();
2302 lSize += strtab->dataLen();
2303 lSize += padLen(VAL32(reloc->m_header->Misc.VirtualSize)+strtab->dataLen(),
2304 RoundUpVal);
2305 }
2306
2307 // allocate the block we are handing back to the caller
2308 void * pImage = (void *) ::CoTaskMemAlloc(lSize);
2309 if (NULL == pImage)
2310 {
2311 return E_OUTOFMEMORY;
2312 }
2313
2314 // zero the memory
2315 ::memset(pImage, 0, lSize);
2316
2317 char *pCur = (char *)pImage;
2318
2319 if(ExeOrDll)
2320 {
2321 // PE Header
2322 COPY_AND_ADVANCE(pCur, &m_dosHeader, sizeof(IMAGE_DOS_HEADER));
2323 COPY_AND_ADVANCE(pCur, x86StubPgm, sizeof(x86StubPgm));
2324 COPY_AND_ADVANCE(pCur, m_ntHeaders, m_ntHeadersSize);
2325 }
2326 else
2327 {
2328 COPY_AND_ADVANCE(pCur, &(m_ntHeaders->FileHeader), sizeof(IMAGE_FILE_HEADER));
2329 }
2330
2331 COPY_AND_ADVANCE(pCur, headers, sizeof(*headers)*(headersEnd - headers));
2332
2333 // now the sections
2334 // write the actual data
2335 for (PEWriterSection **cur = getSectStart(); cur < getSectCur(); cur++) {
2336 if ((*cur)->m_header != NULL) {
2337 unsigned len;
2338 pCur = (char*)pImage + (*cur)->m_filePos;
2339 len = (*cur)->writeMem((void**)&pCur);
2340 _ASSERTE(len == (*cur)->dataLen());
2341 COPY_AND_ADVANCE(pCur, pad, (*cur)->m_filePad);
2342 }
2343 }
2344
2345 // !!! Need to jump to the right place...
2346
2347 if (!ExeOrDll)
2348 {
2349 // now the relocs (exe, dll) or symbol table (obj) (if any)
2350 // write the relocs section (Does nothing if relocs section is empty)
2351 reloc->writeMem((void **)&pCur);
2352
2353 //write string table (obj only, empty for exe or dll)
2354 strtab->writeMem((void **)&pCur);
2355
2356 // final pad
2357 size_t len = padLen(VAL32(reloc->m_header->Misc.VirtualSize)+strtab->dataLen(), RoundUpVal);
2358 if (len > 0)
2359 {
2360 // WARNING: macro - must enclose in curly braces
2361 COPY_AND_ADVANCE(pCur, pad, len);
2362 }
2363 }
2364
2365 // make sure we wrote the exact numbmer of bytes expected
2366 _ASSERTE(lSize == (size_t) (pCur - (char *)pImage));
2367
2368 // give pointer to memory image back to caller (who must free with ::CoTaskMemFree())
2369 *ppImage = pImage;
2370
2371 // all done
2372 return S_OK;
2373}
2374
2375HRESULT PEWriter::getFileTimeStamp(DWORD *pTimeStamp)
2376{
2377 if (pTimeStamp)
2378 *pTimeStamp = m_peFileTimeStamp;
2379
2380 return S_OK;
2381}
2382
2383DWORD PEWriter::getImageBase32()
2384{
2385 _ASSERTE(isPE32());
2386 return VAL32(ntHeaders32()->OptionalHeader.ImageBase);
2387}
2388
2389UINT64 PEWriter::getImageBase64()
2390{
2391 _ASSERTE(!isPE32());
2392 return VAL64(ntHeaders64()->OptionalHeader.ImageBase);
2393}
2394
2395void PEWriter::setImageBase32(DWORD imageBase)
2396{
2397 _ASSERTE(m_hSeedFile == INVALID_HANDLE_VALUE);
2398
2399 _ASSERTE(isPE32());
2400 ntHeaders32()->OptionalHeader.ImageBase = VAL32(imageBase);
2401}
2402
2403void PEWriter::setImageBase64(UINT64 imageBase)
2404{
2405 _ASSERTE(!isPE32());
2406 ntHeaders64()->OptionalHeader.ImageBase = VAL64(imageBase);
2407}
2408
2409void PEWriter::getHeaderInfo(PIMAGE_NT_HEADERS *ppNtHeaders, PIMAGE_SECTION_HEADER *ppSections, ULONG *pNumSections)
2410{
2411 *ppNtHeaders = m_ntHeaders;
2412 *ppSections = headers;
2413 *pNumSections = (ULONG)(headersEnd - headers);
2414}
2415