@@ -21,7 +21,7 @@ namespace ILCompiler.PEWriter
21
21
/// metadata and IL and adding new code and data representing the R2R JITted code and
22
22
/// additional runtime structures (R2R header and tables).
23
23
/// </summary>
24
- public class R2RPEBuilder : PEBuilder
24
+ public sealed class R2RPEBuilder : PEBuilder
25
25
{
26
26
/// <summary>
27
27
/// Number of low-order RVA bits that must match file position on Linux.
@@ -73,7 +73,7 @@ public SectionRVADelta(int startRVA, int endRVA, int deltaRVA)
73
73
/// Name of the initialized data section.
74
74
/// </summary>
75
75
public const string SDataSectionName = ".sdata" ;
76
-
76
+
77
77
/// <summary>
78
78
/// Name of the relocation section.
79
79
/// </summary>
@@ -94,11 +94,6 @@ public SectionRVADelta(int startRVA, int endRVA, int deltaRVA)
94
94
/// </summary>
95
95
private TargetDetails _target ;
96
96
97
- /// <summary>
98
- /// Complete list of sections to emit into the output R2R executable.
99
- /// </summary>
100
- private ImmutableArray < Section > _sections ;
101
-
102
97
/// <summary>
103
98
/// Callback to retrieve the runtime function table which needs setting to the
104
99
/// ExceptionTable PE directory entry.
@@ -112,28 +107,48 @@ public SectionRVADelta(int startRVA, int endRVA, int deltaRVA)
112
107
/// </summary>
113
108
private List < SectionRVADelta > _sectionRvaDeltas ;
114
109
115
- /// <summary>
116
- /// Logical section start RVAs. When emitting R2R PE executables for Linux, we must
117
- /// align RVA's so that their 'RVABitsToMatchFilePos' lowest-order bits match the
118
- /// file position (otherwise memory mapping of the file fails and CoreCLR silently
119
- /// switches over to runtime JIT). PEBuilder doesn't support this today so that we
120
- /// must store the RVA's and post-process the produced PE by patching the section
121
- /// headers in the PE header.
122
- /// </summary>
123
- private int [ ] _sectionRVAs ;
110
+ private class SerializedSectionData
111
+ {
112
+ /// <summary>
113
+ /// Name of the section
114
+ /// </summary>
115
+ public string Name ;
124
116
125
- /// <summary>
126
- /// Pointers to the location of the raw data. Needed to allow phyical file alignment
127
- /// beyond 4KB. PEBuilder doesn't support this today so that we
128
- /// must store the RVA's and post-process the produced PE by patching the section
129
- /// headers in the PE header.
130
- /// </summary>
131
- private int [ ] _sectionPointerToRawData ;
117
+ /// <summary>
118
+ /// Logical section start RVAs. When emitting R2R PE executables for Linux, we must
119
+ /// align RVA's so that their 'RVABitsToMatchFilePos' lowest-order bits match the
120
+ /// file position (otherwise memory mapping of the file fails and CoreCLR silently
121
+ /// switches over to runtime JIT). PEBuilder doesn't support this today so that we
122
+ /// must store the RVA's and post-process the produced PE by patching the section
123
+ /// headers in the PE header.
124
+ /// </summary>
125
+ public int RVA ;
126
+
127
+ /// <summary>
128
+ /// Pointers to the location of the raw data. Needed to allow phyical file alignment
129
+ /// beyond 4KB. PEBuilder doesn't support this today so that we
130
+ /// must store the RVA's and post-process the produced PE by patching the section
131
+ /// headers in the PE header.
132
+ /// </summary>
133
+ public int PointerToRawData ;
134
+
135
+ /// <summary>
136
+ /// Maximum of virtual and physical size for each section.
137
+ /// </summary>
138
+ public int RawSize ;
139
+
140
+ /// <summary>
141
+ /// Whether or not the section has been serialized - if the RVA, pointer to raw data,
142
+ /// and size have been set.
143
+ /// </summary>
144
+ public bool IsSerialized ;
145
+ }
132
146
133
147
/// <summary>
134
- /// Maximum of virtual and physical size for each section.
148
+ /// List of possible sections to emit into the output R2R executable in the order in which
149
+ /// they are expected to be serialized. Data (aside from name) is set during serialization.
135
150
/// </summary>
136
- private int [ ] _sectionRawSizes ;
151
+ private readonly SerializedSectionData [ ] _sectionData ;
137
152
138
153
/// <summary>
139
154
/// R2R PE section builder & relocator.
@@ -206,18 +221,13 @@ public R2RPEBuilder(
206
221
PEHeaderConstants . SectionAlignment ) ;
207
222
}
208
223
209
- ImmutableArray < Section > . Builder sectionListBuilder = ImmutableArray . CreateBuilder < Section > ( ) ;
224
+ List < SerializedSectionData > sectionData = new List < SerializedSectionData > ( ) ;
210
225
foreach ( SectionInfo sectionInfo in _sectionBuilder . GetSections ( ) )
211
226
{
212
- ILCompiler . PEWriter . Section builderSection = _sectionBuilder . FindSection ( sectionInfo . SectionName ) ;
213
- Debug . Assert ( builderSection != null ) ;
214
- sectionListBuilder . Add ( new Section ( builderSection . Name , builderSection . Characteristics ) ) ;
227
+ sectionData . Add ( new SerializedSectionData ( ) { Name = sectionInfo . SectionName } ) ;
215
228
}
216
229
217
- _sections = sectionListBuilder . ToImmutableArray ( ) ;
218
- _sectionRVAs = new int [ _sections . Length ] ;
219
- _sectionPointerToRawData = new int [ _sections . Length ] ;
220
- _sectionRawSizes = new int [ _sections . Length ] ;
230
+ _sectionData = sectionData . ToArray ( ) ;
221
231
}
222
232
223
233
public void SetCorHeader ( ISymbolNode symbol , int headerSize )
@@ -400,13 +410,17 @@ private void UpdateSectionRVAs(Stream outputStream)
400
410
16 * sizeof ( long ) ; // directory entries
401
411
402
412
int sectionHeaderOffset = DosHeaderSize + PESignatureSize + COFFHeaderSize + peHeaderSize ;
403
- int sectionCount = _sectionRVAs . Length ;
413
+ int sectionCount = _sectionData . Length ;
404
414
for ( int sectionIndex = 0 ; sectionIndex < sectionCount ; sectionIndex ++ )
405
415
{
416
+ SerializedSectionData section = _sectionData [ sectionIndex ] ;
417
+ if ( ! section . IsSerialized )
418
+ continue ;
419
+
406
420
if ( _customPESectionAlignment != 0 )
407
421
{
408
422
// When _customPESectionAlignment is set, the physical and virtual sizes are the same
409
- byte [ ] sizeBytes = BitConverter . GetBytes ( _sectionRawSizes [ sectionIndex ] ) ;
423
+ byte [ ] sizeBytes = BitConverter . GetBytes ( section . RawSize ) ;
410
424
Debug . Assert ( sizeBytes . Length == sizeof ( int ) ) ;
411
425
412
426
// Update VirtualSize
@@ -424,23 +438,33 @@ private void UpdateSectionRVAs(Stream outputStream)
424
438
// Update RVAs
425
439
{
426
440
outputStream . Seek ( sectionHeaderOffset + SectionHeaderSize * sectionIndex + SectionHeaderRVAOffset , SeekOrigin . Begin ) ;
427
- byte [ ] rvaBytes = BitConverter . GetBytes ( _sectionRVAs [ sectionIndex ] ) ;
441
+ byte [ ] rvaBytes = BitConverter . GetBytes ( section . RVA ) ;
428
442
Debug . Assert ( rvaBytes . Length == sizeof ( int ) ) ;
429
443
outputStream . Write ( rvaBytes , 0 , rvaBytes . Length ) ;
430
444
}
431
445
432
446
// Update pointer to raw data
433
447
{
434
448
outputStream . Seek ( sectionHeaderOffset + SectionHeaderSize * sectionIndex + SectionHeaderPointerToRawDataOffset , SeekOrigin . Begin ) ;
435
- byte [ ] rawDataBytesBytes = BitConverter . GetBytes ( _sectionPointerToRawData [ sectionIndex ] ) ;
449
+ byte [ ] rawDataBytesBytes = BitConverter . GetBytes ( section . PointerToRawData ) ;
436
450
Debug . Assert ( rawDataBytesBytes . Length == sizeof ( int ) ) ;
437
451
outputStream . Write ( rawDataBytesBytes , 0 , rawDataBytesBytes . Length ) ;
438
452
}
439
453
}
440
454
441
455
// Patch SizeOfImage to point past the end of the last section
456
+ SerializedSectionData lastSection = null ;
457
+ for ( int i = sectionCount - 1 ; i >= 0 ; i -- )
458
+ {
459
+ if ( _sectionData [ i ] . IsSerialized )
460
+ {
461
+ lastSection = _sectionData [ i ] ;
462
+ break ;
463
+ }
464
+ }
465
+ Debug . Assert ( lastSection != null ) ;
442
466
outputStream . Seek ( DosHeaderSize + PESignatureSize + COFFHeaderSize + OffsetOfSizeOfImage , SeekOrigin . Begin ) ;
443
- int sizeOfImage = AlignmentHelper . AlignUp ( _sectionRVAs [ sectionCount - 1 ] + _sectionRawSizes [ sectionCount - 1 ] , Header . SectionAlignment ) ;
467
+ int sizeOfImage = AlignmentHelper . AlignUp ( lastSection . RVA + lastSection . RawSize , Header . SectionAlignment ) ;
444
468
byte [ ] sizeOfImageBytes = BitConverter . GetBytes ( sizeOfImage ) ;
445
469
Debug . Assert ( sizeOfImageBytes . Length == sizeof ( int ) ) ;
446
470
outputStream . Write ( sizeOfImageBytes , 0 , sizeOfImageBytes . Length ) ;
@@ -557,14 +581,21 @@ private int RelocateRVA(int rva)
557
581
/// </summary>
558
582
protected override ImmutableArray < Section > CreateSections ( )
559
583
{
560
- return _sections ;
584
+ ImmutableArray < Section > . Builder sectionListBuilder = ImmutableArray . CreateBuilder < Section > ( ) ;
585
+ foreach ( SectionInfo sectionInfo in _sectionBuilder . GetSections ( ) )
586
+ {
587
+ // Only include sections that have content.
588
+ if ( ! _sectionBuilder . HasContent ( sectionInfo . SectionName ) )
589
+ continue ;
590
+
591
+ sectionListBuilder . Add ( new Section ( sectionInfo . SectionName , sectionInfo . Characteristics ) ) ;
592
+ }
593
+
594
+ return sectionListBuilder . ToImmutable ( ) ;
561
595
}
562
596
563
597
/// <summary>
564
- /// Output the section with a given name. For sections existent in the source MSIL PE file
565
- /// (.text, optionally .rsrc and .reloc), we first copy the content of the input MSIL PE file
566
- /// and then call the section serialization callback to emit the extra content after the input
567
- /// section content.
598
+ /// Output the section with a given name.
568
599
/// </summary>
569
600
/// <param name="name">Section name</param>
570
601
/// <param name="location">RVA and file location where the section will be put</param>
@@ -574,18 +605,33 @@ protected override BlobBuilder SerializeSection(string name, SectionLocation loc
574
605
BlobBuilder sectionDataBuilder = null ;
575
606
int sectionStartRva = location . RelativeVirtualAddress ;
576
607
577
- int outputSectionIndex = _sections . Length - 1 ;
578
- while ( outputSectionIndex >= 0 && _sections [ outputSectionIndex ] . Name != name )
608
+ int outputSectionIndex = _sectionData . Length - 1 ;
609
+ while ( outputSectionIndex >= 0 && _sectionData [ outputSectionIndex ] . Name != name )
579
610
{
580
611
outputSectionIndex -- ;
581
612
}
582
613
614
+ if ( outputSectionIndex < 0 )
615
+ throw new ArgumentException ( $ "Unknown section name: '{ name } '", nameof ( name ) ) ;
616
+
617
+ Debug . Assert ( _sectionBuilder . HasContent ( name ) ) ;
618
+ SerializedSectionData outputSection = _sectionData [ outputSectionIndex ] ;
619
+ SerializedSectionData previousSection = null ;
620
+ for ( int i = outputSectionIndex - 1 ; i >= 0 ; i -- )
621
+ {
622
+ if ( _sectionData [ i ] . IsSerialized )
623
+ {
624
+ previousSection = _sectionData [ i ] ;
625
+ break ;
626
+ }
627
+ }
628
+
583
629
int injectedPadding = 0 ;
584
630
if ( _customPESectionAlignment != 0 )
585
631
{
586
- if ( outputSectionIndex > 0 )
632
+ if ( previousSection is not null )
587
633
{
588
- sectionStartRva = Math . Max ( sectionStartRva , _sectionRVAs [ outputSectionIndex - 1 ] + _sectionRawSizes [ outputSectionIndex - 1 ] ) ;
634
+ sectionStartRva = Math . Max ( sectionStartRva , previousSection . RVA + previousSection . RawSize ) ;
589
635
}
590
636
591
637
int newSectionStartRva = AlignmentHelper . AlignUp ( sectionStartRva , _customPESectionAlignment ) ;
@@ -603,13 +649,13 @@ protected override BlobBuilder SerializeSection(string name, SectionLocation loc
603
649
if ( ! _target . IsWindows )
604
650
{
605
651
const int RVAAlign = 1 << RVABitsToMatchFilePos ;
606
- if ( outputSectionIndex > 0 )
652
+ if ( previousSection is not null )
607
653
{
608
- sectionStartRva = Math . Max ( sectionStartRva , _sectionRVAs [ outputSectionIndex - 1 ] + _sectionRawSizes [ outputSectionIndex - 1 ] ) ;
654
+ sectionStartRva = Math . Max ( sectionStartRva , previousSection . RVA + previousSection . RawSize ) ;
609
655
610
656
// when assembly is stored in a singlefile bundle, an additional skew is introduced
611
- // as the streams inside the bundle are not necessarily page aligned as we do not
612
- // know the actual page size on the target system.
657
+ // as the streams inside the bundle are not necessarily page aligned as we do not
658
+ // know the actual page size on the target system.
613
659
// We may need one page gap of unused VA space before the next section starts.
614
660
// We will assume the page size is <= RVAAlign
615
661
sectionStartRva += RVAAlign ;
@@ -622,36 +668,19 @@ protected override BlobBuilder SerializeSection(string name, SectionLocation loc
622
668
location = new SectionLocation ( sectionStartRva , location . PointerToRawData ) ;
623
669
}
624
670
625
- if ( outputSectionIndex >= 0 )
626
- {
627
- _sectionRVAs [ outputSectionIndex ] = sectionStartRva ;
628
- _sectionPointerToRawData [ outputSectionIndex ] = location . PointerToRawData ;
629
- }
671
+ outputSection . RVA = sectionStartRva ;
672
+ outputSection . PointerToRawData = location . PointerToRawData ;
630
673
631
674
BlobBuilder extraData = _sectionBuilder . SerializeSection ( name , location ) ;
632
- if ( extraData != null )
633
- {
634
- if ( sectionDataBuilder == null )
635
- {
636
- // See above - there's a bug due to which LinkSuffix to an empty BlobBuilder screws up the blob content.
637
- sectionDataBuilder = extraData ;
638
- }
639
- else
640
- {
641
- sectionDataBuilder . LinkSuffix ( extraData ) ;
642
- }
643
- }
644
-
645
- // Make sure the section has at least 1 byte, otherwise the PE emitter goes mad,
646
- // messes up the section map and corrups the output executable.
675
+ Debug . Assert ( extraData != null ) ;
647
676
if ( sectionDataBuilder == null )
648
677
{
649
- sectionDataBuilder = new BlobBuilder ( ) ;
678
+ // See above - there's a bug due to which LinkSuffix to an empty BlobBuilder screws up the blob content.
679
+ sectionDataBuilder = extraData ;
650
680
}
651
-
652
- if ( sectionDataBuilder . Count == 0 )
681
+ else
653
682
{
654
- sectionDataBuilder . WriteByte ( 0 ) ;
683
+ sectionDataBuilder . LinkSuffix ( extraData ) ;
655
684
}
656
685
657
686
int sectionRawSize = sectionDataBuilder . Count - injectedPadding ;
@@ -664,15 +693,13 @@ protected override BlobBuilder SerializeSection(string name, SectionLocation loc
664
693
sectionRawSize = count ;
665
694
}
666
695
667
- if ( outputSectionIndex >= 0 )
668
- {
669
- _sectionRawSizes [ outputSectionIndex ] = sectionRawSize ;
670
- }
696
+ outputSection . RawSize = sectionRawSize ;
697
+ outputSection . IsSerialized = true ;
671
698
672
699
return sectionDataBuilder ;
673
700
}
674
701
}
675
-
702
+
676
703
/// <summary>
677
704
/// Simple helper for filling in PE header information.
678
705
/// </summary>
0 commit comments