summaryrefslogtreecommitdiff
path: root/src/zap/zapwriter.h
blob: 05b573822bdcfe1fd3b5888d9f9f2a67acb95d23 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//
// ZapWriter.h
//

//
// Infrastructure for writing PE files. (Not NGEN specific)
//
// ======================================================================================



#ifndef __ZAPWRITER_H__
#define __ZAPWRITER_H__

#include "zapnodetype.h"

class ZapWriter;
class ZapHeap;

// This is maximum size of anything in the image written by ZapWriter. Used for overflow checking.
#define ZAPWRITER_MAX_SIZE 0x3FFFFFFF

// All ZapNodes should be allocated from ZapHeap returned by ZapWriter::GetHeap()
void *operator new(size_t size, ZapHeap * pZapHeap);
void *operator new[](size_t size, ZapHeap * pZapHeap);

//
// ZapHeap does not support deallocation. Empty operators delete avoids deallocating memory
// if the constructor fails
//
inline void operator delete(void *, ZapHeap * pZapHeap)
{
    // Memory allocated by ZapHeap is never freed
}
inline void operator delete[](void *, ZapHeap * pZapHeap)
{
    // Memory allocated by ZapHeap is never freed
}


//------------------------------------------------------------------------------------------------------
// ZapNode is the basic building block of the native image. Every ZapNode must know how to persist itself.
//
// The basic contract for a ZapNode is that it understands its allocations requirements (size and alignment),
// and knows how to save itself (given a ZapWriter). At some point a ZapNode is given a location in the
// executable (an RVA), which it is responsible remembering.
//
// See file:../../doc/BookOfTheRuntime/NGEN/NGENDesign.doc for an overview.
//
class ZapNode
{
    friend class ZapWriter;

    DWORD m_RVA;

public:
    void SetRVA(DWORD dwRVA)
    {
        _ASSERTE(m_RVA == 0 || m_RVA == (DWORD)-1);
        m_RVA = dwRVA;
    }

    ZapNode()
    {
        // All ZapNodes are expected to be allocate from ZapWriter::GetHeap() that returns zero filled memory
        _ASSERTE(m_RVA == 0);
    }

    // This constructor should be used to allocate temporary ZapNodes on the stack only
    ZapNode(DWORD rva)
        : m_RVA(rva)
    {
    }

    virtual ~ZapNode()
    {
    }

    // Returns the size of the node in the image. All nodes that are written into the image should override this method.
    virtual DWORD GetSize()
    {
#if defined(_MSC_VER) //UNREACHABLE doesn't work in GCC, when the method has a non-void return
        UNREACHABLE();
#else
        _ASSERTE(!"Unreachable");
        return 0;
#endif
    }

    // Alignment for this node.
    virtual UINT GetAlignment()
    {
        return 1;
    }

    // Returns the type of the ZapNode. All nodes should override this method.
    virtual ZapNodeType GetType()
    {
        return ZapNodeType_Unknown;
    }

    // Assign RVA to this node. dwPos is current RVA, returns updated current RVA.
    virtual DWORD ComputeRVA(ZapWriter * pZapWriter, DWORD dwPos);

    // All nodes that are written into the image should override this method. The implementation should write exactly GetSize() bytes
    // using ZapWriter::Write method
    virtual void Save(ZapWriter * pZapWriter)
    {
        UNREACHABLE();
    }

    // Returns the RVA of the node. Valid only after ComputeRVA phase
    DWORD GetRVA()
    {
        _ASSERTE(m_RVA != 0 && m_RVA != (DWORD)-1);
        return m_RVA;
    }

    // Returns whether the node was placed into a virtual section
    BOOL IsPlaced()
    {
        return m_RVA != 0;
    }
};

//---------------------------------------------------------------------------------------
// Virtual section of PE image.
class ZapVirtualSection : public ZapNode
{
    friend class ZapWriter;

    DWORD m_dwAlignment;

    SArray<ZapNode *> m_Nodes;

    // State initialized once the section is placed
    DWORD m_dwSize;

    DWORD m_dwSectionType;

    BYTE m_defaultFill;

    ZapVirtualSection(DWORD dwAlignment)
        : m_dwAlignment(dwAlignment)
    {
    }

public:
    virtual DWORD GetSize()
    {
        return m_dwSize;
    }

    virtual ZapNodeType GetType()
    {
        return ZapNodeType_VirtualSection;
    }

    DWORD GetSectionType()
    {
        return m_dwSectionType;
    }

    void SetSectionType(DWORD dwSectionType)
    {
        _ASSERTE((dwSectionType & IBCTypeReservedFlag) != 0 || !"IBCType flag is not specified");
        _ASSERTE((dwSectionType & RangeTypeReservedFlag) != 0 || !"RangeType flag is not specified");
        _ASSERTE((dwSectionType & VirtualSectionTypeReservedFlag) != 0 || !"VirtualSectionType flag is not specified");
        _ASSERTE((dwSectionType & VirtualSectionTypeReservedFlag) < CORCOMPILE_SECTION_TYPE_COUNT || !"Invalid VirtualSectionType flag");
        m_dwSectionType = dwSectionType;
    }

    void SetDefaultFill(BYTE fill)
    {
        m_defaultFill = fill;
    }

    void Place(ZapNode * pNode)
    {
        _ASSERTE(!pNode->IsPlaced());
        m_Nodes.Append(pNode);
        pNode->SetRVA((DWORD)-1);
    }

    COUNT_T GetNodeCount()
    {
        return m_Nodes.GetCount();
    }

    ZapNode * GetNode(COUNT_T iNode)
    {
        return m_Nodes[iNode];
    }
};

//---------------------------------------------------------------------------------------
// The named physical section of the PE Image. It contains one or more virtual sections.
class ZapPhysicalSection : public ZapNode
{
    friend class ZapWriter;

    SArray<ZapVirtualSection *> m_Sections;

    LPCSTR m_pszName;
    DWORD m_dwCharacteristics;

    // Number of zero filled sections (zero filled sections are always last in m_Sections array)
    COUNT_T m_nBssSections;

    // State initialized once the section is placed
    DWORD m_dwSize;
    DWORD m_dwFilePos;
    DWORD m_dwSizeOfRawData;

    ZapPhysicalSection(LPCSTR pszName, DWORD dwCharacteristics)
        : m_pszName(pszName),
          m_dwCharacteristics(dwCharacteristics)
    {
    }

public:
    ~ZapPhysicalSection()
    {
        for (COUNT_T iVirtualSection = 0; iVirtualSection < m_Sections.GetCount(); iVirtualSection++)
        {
            ZapVirtualSection * pVirtualSection = m_Sections[iVirtualSection];
            pVirtualSection->~ZapVirtualSection();
        }
    }

    virtual DWORD GetSize()
    {
        return m_dwSize;
    }

    virtual ZapNodeType GetType()
    {
        return ZapNodeType_PhysicalSection;
    }

    DWORD GetFilePos()
    {
        _ASSERTE(m_dwFilePos != 0);
        return m_dwFilePos;
    }

    COUNT_T GetVirtualSectionCount()
    {
        return m_Sections.GetCount();
    }

    ZapVirtualSection * GetVirtualSection(COUNT_T iSection)
    {
        return m_Sections[iSection];
    }

};

//---------------------------------------------------------------------------------------
//
// The ZapWriter
//
// Notice that ZapWriter implements IStream that can be passed to APIs that write to stream
//
// The main API in a ZapWriter is (not suprisingly) the code:ZapWriter.Write method.
//
// Relocations are handled by a higher level object, code:ZapImage, which knows about all the sections of a
// ngen image and how to do relections.  Every ZapWriter has an associated ZapImage which you get to by
// calling code:ZapImage.GetImage.
//
class ZapWriter : public IStream
{
    ZapHeap * m_pHeap;

    SArray<ZapPhysicalSection *> m_Sections;

    ZapNode * m_DirectoryEntries[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
    DWORD     m_dwTimeDateStamp;
    ULONGLONG m_BaseAddress;
    ULONGLONG m_SizeOfStackReserve;
    ULONGLONG m_SizeOfStackCommit;
    USHORT    m_Subsystem;
    USHORT    m_DllCharacteristics;
    BOOL      m_isDll;
    DWORD     m_FileAlignment;

    // Current state of the writer for debug checks
    INDEBUG(BOOL m_fSaving;)

    DWORD m_dwCurrentRVA;
    BOOL m_fWritingRelocs; // Set to true once we start reloc sections at the end of the file

    void SaveContent();

    DWORD GetSizeOfNTHeaders();
    void SaveHeaders();

    // Simple buffered writer
    void InitializeWriter(IStream * pStream);

    IStream * m_pStream;
    PBYTE m_pBuffer;
    ULONG m_nBufferPos;
    INDEBUG(DWORD m_dwWriterFilePos;)

    //
    // NT Headers
    //

    BOOL Is64Bit()
    {
#ifdef _TARGET_64BIT_
        return TRUE;
#else // !_TARGET_64BIT_
        return FALSE;
#endif // !_TARGET_64BIT_
    }

    USHORT GetMachine()
    {
        return IMAGE_FILE_MACHINE_NATIVE_NI;
    }

    void SaveDosHeader();
    void SaveSignature();
    void SaveFileHeader();
    void SaveOptionalHeader();
    void SaveSections();

    // IStream support - the only actually implemented method is IStream::Write

    // IUnknown methods
    STDMETHODIMP_(ULONG) AddRef()
    {
        return 1;
    }

    STDMETHODIMP_(ULONG) Release()
    {
        return 1;
    }

    STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppv)
    {
        HRESULT hr = S_OK;
        if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IStream)) {
            *ppv = static_cast<IStream *>(this);
        }
        else {
            *ppv = NULL;
            hr = E_NOINTERFACE;
        }
        return hr;
    }

    // ISequentialStream methods:
    STDMETHODIMP Read(void *pv, ULONG cb, ULONG *pcbRead)
    {
        _ASSERTE(false);
        return E_NOTIMPL;
    }

    STDMETHODIMP Write(void const *pv, ULONG cb, ULONG *pcbWritten);

    // IStream methods:
    STDMETHODIMP Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
    {
        // IMetaDataEmit::SaveToStream calls Seek(0) but ignores the returned error
        //_ASSERTE(false);
        return E_NOTIMPL;
    }

    STDMETHODIMP SetSize(ULARGE_INTEGER libNewSize)
    {
        _ASSERTE(false);
        return E_NOTIMPL;
    }

    STDMETHODIMP CopyTo(IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
    {
        _ASSERTE(false);
        return E_NOTIMPL;
    }

    STDMETHODIMP Commit(DWORD grfCommitFlags)
    {
        _ASSERTE(false);
        return E_NOTIMPL;
    }

    STDMETHODIMP Revert()
    {
        _ASSERTE(false);
        return E_NOTIMPL;
    }

    STDMETHODIMP LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
    {
        _ASSERTE(false);
        return E_NOTIMPL;
    }

    STDMETHODIMP UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
    {
        _ASSERTE(false);
        return E_NOTIMPL;
    }

    STDMETHODIMP Stat(STATSTG *pstatstg, DWORD grfStatFlag)
    {
        _ASSERTE(false);
        return E_NOTIMPL;
    }

    STDMETHODIMP Clone(IStream **ppIStream)
    {
        _ASSERTE(false);
        return E_NOTIMPL;
    }

public:
    ZapWriter();
    ~ZapWriter();

    void Initialize();

    // Create new section in the PE file. The sections will be saved in the order they are created.
    ZapPhysicalSection * NewPhysicalSection(LPCSTR pszName, DWORD dwCharacteristics)
    {
        _ASSERTE(!IsSaving());
        ZapPhysicalSection * pSection = new (GetHeap()) ZapPhysicalSection(pszName, dwCharacteristics);
        m_Sections.Append(pSection);
        return pSection;
    }

    // Create new virtual section within the physical section. The sections will be saved in the order they are created.
    // The default virtual section alignment is 16.
    ZapVirtualSection * NewVirtualSection(ZapPhysicalSection * pPhysicalSection, DWORD dwAlignment = 16, ZapVirtualSection * pInsertAfter = NULL)
    {
        _ASSERTE(!IsSaving());
        ZapVirtualSection * pSection = new (GetHeap()) ZapVirtualSection(dwAlignment);
        if (pInsertAfter != NULL)
        {
            // pInsertAfter is workaround to get decent layout with the current scheme of virtual sections. It should not be necessary 
            // once we have better layout algorithms in place.
            for (COUNT_T iSection = 0; iSection < pPhysicalSection->m_Sections.GetCount(); iSection++)
            {
                if (pPhysicalSection->m_Sections[iSection] == pInsertAfter)
                {
                    pPhysicalSection->m_Sections.Insert(pPhysicalSection->m_Sections+(iSection+1));
                    pPhysicalSection->m_Sections[iSection+1] = pSection;
                    return pSection;                    
                }
            }
            _ASSERTE(false);
        }

        pPhysicalSection->m_Sections.Append(pSection);
        return pSection;
    }

    void MarkBssSection(ZapPhysicalSection * pPhysicalSection, ZapVirtualSection * pSection)
    {
        _ASSERTE(!IsSaving());
        _ASSERTE(pPhysicalSection->m_Sections[pPhysicalSection->m_Sections.GetCount() - 1] == pSection);
        pPhysicalSection->m_nBssSections++;
    }

    void Append(ZapVirtualSection * pVirtualSection, ZapNode * pNode)
    {
        _ASSERTE(!IsSaving());
        pVirtualSection->m_Nodes.Append(pNode);
    }

    // Set the directory entry in the image to match the given ZapNode
    void SetDirectoryEntry(DWORD entry, ZapNode * pNode)
    {
        _ASSERTE(!IsSaving());
        _ASSERTE(entry < IMAGE_NUMBEROF_DIRECTORY_ENTRIES);
        _ASSERTE(m_DirectoryEntries[entry] == NULL);
        m_DirectoryEntries[entry] = pNode;
    }

    // Set the timedate stamp of the image
    void SetTimeDateStamp(DWORD dwTimeDateStamp)
    {
        _ASSERTE(!IsSaving());
        m_dwTimeDateStamp = dwTimeDateStamp;
    }

    // Set the base address of the image
    void SetBaseAddress(ULONGLONG baseAddress)
    {
        _ASSERTE(!IsSaving());
        m_BaseAddress = baseAddress;
    }

    ULONGLONG GetBaseAddress()
    {
        _ASSERTE(m_BaseAddress != 0);
        return m_BaseAddress;
    }

    void SetSizeOfStackReserve(ULONGLONG sizeOfStackReserve)
    {
        _ASSERTE(!IsSaving());
        m_SizeOfStackReserve = sizeOfStackReserve;
    }

    void SetSizeOfStackCommit(ULONGLONG sizeOfStackCommit)
    {
        _ASSERTE(!IsSaving());
        m_SizeOfStackCommit = sizeOfStackCommit;
    }

    void SetSubsystem(USHORT subsystem)
    {
        _ASSERTE(!IsSaving());
        m_Subsystem = subsystem;
    }

    void SetDllCharacteristics(USHORT dllCharacteristics)
    {
        _ASSERTE(!IsSaving());
        m_DllCharacteristics = dllCharacteristics;
    }

    void SetIsDll(BOOL isDLL)
    {
        m_isDll = isDLL;
    }

    void SetFileAlignment(DWORD fileAlignment)
    {
        m_FileAlignment = fileAlignment;
    }

    // Compute RVAs for everything in the file
    void ComputeRVAs();

    // Save the content into stream
    void Save(IStream * pStream);

    // Get the heap. The lifetime of this heap is same as the lifetime of the ZapWriter. All ZapNodes should
    // be allocated from this heap.
    ZapHeap * GetHeap()
    {
        return m_pHeap;
    }

    COUNT_T GetPhysicalSectionCount()
    {
        return m_Sections.GetCount();
    }

    ZapPhysicalSection * GetPhysicalSection(COUNT_T iSection)
    {
        return m_Sections[iSection];
    }

#ifdef _DEBUG
    // Certain methods can be called only during the save phase
    BOOL IsSaving()
    {
        return m_fSaving;
    }
#endif

    DWORD GetCurrentRVA()
    {
        _ASSERTE(IsSaving());
        return m_dwCurrentRVA;
    }


    // This is the main entrypoint used to write the image. Every implementation of ZapNode::Save will call this method.
    void Write(PVOID p, DWORD dwSize);

    // Writes padding
    void WritePad(DWORD size, BYTE fill = 0);

    // Flush any buffered data
    void FlushWriter();

    BOOL IsWritingRelocs()
    {
        return m_fWritingRelocs;
    }

    void SetWritingRelocs()
    {
        m_fWritingRelocs = TRUE;
    }

    // Convenience helper to initialize IMAGE_DATA_DIRECTORY
    static void SetDirectoryData(IMAGE_DATA_DIRECTORY * pDir, ZapNode * pZapNode);
};

//---------------------------------------------------------------------------------------
// ZapBlob
//
// Generic node for unstructured sequence of bytes.
// Includes SHash support (ZapBlob::SHashTraits)
//
class ZapBlob : public ZapNode
{
    DWORD m_cbSize;

protected:
    ZapBlob(SIZE_T cbSize)
        : m_cbSize((DWORD)cbSize)
    {
        if (cbSize > ZAPWRITER_MAX_SIZE)
            ThrowHR(COR_E_OVERFLOW);
    }

public:
    class SHashKey
    {
        PBYTE   m_pData;
        SIZE_T  m_cbSize;

    public:
        SHashKey(PVOID pData, SIZE_T cbSize)
            : m_pData((PBYTE)pData), m_cbSize(cbSize)
        {
        }

        PBYTE GetData() const
        {
            return m_pData;
        }

        SIZE_T GetBlobSize() const
        {
            return m_cbSize;
        }
    };

    class SHashTraits : public DefaultSHashTraits<ZapBlob *>
    {
    public:
        typedef const ZapBlob::SHashKey key_t;

        static key_t GetKey(element_t e)
        {
            LIMITED_METHOD_CONTRACT;
            return key_t(e->GetData(), e->GetBlobSize());
        }
        static BOOL Equals(key_t k1, key_t k2)
        {
            LIMITED_METHOD_CONTRACT;
            if (k1.GetBlobSize() != k2.GetBlobSize())
                return FALSE;
            return memcmp(k1.GetData(), k2.GetData(), k1.GetBlobSize()) == 0;
        }
        static count_t Hash(key_t k)
        {
            LIMITED_METHOD_CONTRACT;
            count_t hash = 5381 + (count_t)(k.GetBlobSize() << 7);

            PBYTE pbData = k.GetData();
            PBYTE pbDataEnd = pbData + k.GetBlobSize();

            for (/**/ ; pbData < pbDataEnd; pbData++)
            {
                hash = ((hash << 5) + hash) ^ *pbData;
            }
            return hash;
        }
    };

    virtual PBYTE GetData()
    {
        return (PBYTE)(this + 1);
    }

    // Used to shrink the size of the blob
    void AdjustBlobSize(SIZE_T cbSize)
    {
        _ASSERTE(cbSize <= m_cbSize);
        _ASSERTE(cbSize != 0);
        m_cbSize = (DWORD)cbSize;
    }

    // Raw size of the blob
    DWORD GetBlobSize()
    {
        return m_cbSize;
    }

    virtual DWORD GetSize()
    {
        return m_cbSize;
    }

    virtual ZapNodeType GetType()
    {
        return ZapNodeType_Blob;
    }

    virtual void Save(ZapWriter * pZapWriter);

    // Create new zap blob node. The node *does* own copy of the memory.
    static ZapBlob * NewBlob(ZapWriter * pWriter, PVOID pData, SIZE_T cbSize);

    // Create new aligned zap blob node.
    static ZapBlob * NewAlignedBlob(ZapWriter * pWriter, PVOID pData, SIZE_T cbSize, SIZE_T cbAlignment);
};

class ZapBlobPtr : public ZapBlob
{
    PBYTE m_pData;

public:
    ZapBlobPtr(PVOID pData, SIZE_T cbSize)
        : ZapBlob(cbSize), m_pData((PBYTE)pData)
    {
    }

    virtual PBYTE GetData()
    {
        return m_pData;
    }
};

class ZapDummyNode : public ZapNode
{
    DWORD m_cbSize;

public:
    ZapDummyNode(DWORD cbSize)
        : m_cbSize(cbSize)
    {
    }

    virtual DWORD GetSize()
    {
        return m_cbSize;
    }
};

#endif // __ZAPWRITER_H__