@@ -21,7 +21,7 @@ namespace ILCompiler.PEWriter
2121 /// metadata and IL and adding new code and data representing the R2R JITted code and
2222 /// additional runtime structures (R2R header and tables).
2323 /// </summary>
24- public class R2RPEBuilder : PEBuilder
24+ public sealed class R2RPEBuilder : PEBuilder
2525 {
2626 /// <summary>
2727 /// 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)
7373 /// Name of the initialized data section.
7474 /// </summary>
7575 public const string SDataSectionName = ".sdata" ;
76-
76+
7777 /// <summary>
7878 /// Name of the relocation section.
7979 /// </summary>
@@ -94,11 +94,6 @@ public SectionRVADelta(int startRVA, int endRVA, int deltaRVA)
9494 /// </summary>
9595 private TargetDetails _target ;
9696
97- /// <summary>
98- /// Complete list of sections to emit into the output R2R executable.
99- /// </summary>
100- private ImmutableArray < Section > _sections ;
101-
10297 /// <summary>
10398 /// Callback to retrieve the runtime function table which needs setting to the
10499 /// ExceptionTable PE directory entry.
@@ -112,28 +107,48 @@ public SectionRVADelta(int startRVA, int endRVA, int deltaRVA)
112107 /// </summary>
113108 private List < SectionRVADelta > _sectionRvaDeltas ;
114109
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 ;
124116
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+ }
132146
133147 /// <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.
135150 /// </summary>
136- private int [ ] _sectionRawSizes ;
151+ private readonly SerializedSectionData [ ] _sectionData ;
137152
138153 /// <summary>
139154 /// R2R PE section builder & relocator.
@@ -206,18 +221,13 @@ public R2RPEBuilder(
206221 PEHeaderConstants . SectionAlignment ) ;
207222 }
208223
209- ImmutableArray < Section > . Builder sectionListBuilder = ImmutableArray . CreateBuilder < Section > ( ) ;
224+ List < SerializedSectionData > sectionData = new List < SerializedSectionData > ( ) ;
210225 foreach ( SectionInfo sectionInfo in _sectionBuilder . GetSections ( ) )
211226 {
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 } ) ;
215228 }
216229
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 ( ) ;
221231 }
222232
223233 public void SetCorHeader ( ISymbolNode symbol , int headerSize )
@@ -400,13 +410,17 @@ private void UpdateSectionRVAs(Stream outputStream)
400410 16 * sizeof ( long ) ; // directory entries
401411
402412 int sectionHeaderOffset = DosHeaderSize + PESignatureSize + COFFHeaderSize + peHeaderSize ;
403- int sectionCount = _sectionRVAs . Length ;
413+ int sectionCount = _sectionData . Length ;
404414 for ( int sectionIndex = 0 ; sectionIndex < sectionCount ; sectionIndex ++ )
405415 {
416+ SerializedSectionData section = _sectionData [ sectionIndex ] ;
417+ if ( ! section . IsSerialized )
418+ continue ;
419+
406420 if ( _customPESectionAlignment != 0 )
407421 {
408422 // 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 ) ;
410424 Debug . Assert ( sizeBytes . Length == sizeof ( int ) ) ;
411425
412426 // Update VirtualSize
@@ -424,23 +438,33 @@ private void UpdateSectionRVAs(Stream outputStream)
424438 // Update RVAs
425439 {
426440 outputStream . Seek ( sectionHeaderOffset + SectionHeaderSize * sectionIndex + SectionHeaderRVAOffset , SeekOrigin . Begin ) ;
427- byte [ ] rvaBytes = BitConverter . GetBytes ( _sectionRVAs [ sectionIndex ] ) ;
441+ byte [ ] rvaBytes = BitConverter . GetBytes ( section . RVA ) ;
428442 Debug . Assert ( rvaBytes . Length == sizeof ( int ) ) ;
429443 outputStream . Write ( rvaBytes , 0 , rvaBytes . Length ) ;
430444 }
431445
432446 // Update pointer to raw data
433447 {
434448 outputStream . Seek ( sectionHeaderOffset + SectionHeaderSize * sectionIndex + SectionHeaderPointerToRawDataOffset , SeekOrigin . Begin ) ;
435- byte [ ] rawDataBytesBytes = BitConverter . GetBytes ( _sectionPointerToRawData [ sectionIndex ] ) ;
449+ byte [ ] rawDataBytesBytes = BitConverter . GetBytes ( section . PointerToRawData ) ;
436450 Debug . Assert ( rawDataBytesBytes . Length == sizeof ( int ) ) ;
437451 outputStream . Write ( rawDataBytesBytes , 0 , rawDataBytesBytes . Length ) ;
438452 }
439453 }
440454
441455 // 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 ) ;
442466 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 ) ;
444468 byte [ ] sizeOfImageBytes = BitConverter . GetBytes ( sizeOfImage ) ;
445469 Debug . Assert ( sizeOfImageBytes . Length == sizeof ( int ) ) ;
446470 outputStream . Write ( sizeOfImageBytes , 0 , sizeOfImageBytes . Length ) ;
@@ -557,14 +581,21 @@ private int RelocateRVA(int rva)
557581 /// </summary>
558582 protected override ImmutableArray < Section > CreateSections ( )
559583 {
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 ( ) ;
561595 }
562596
563597 /// <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.
568599 /// </summary>
569600 /// <param name="name">Section name</param>
570601 /// <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
574605 BlobBuilder sectionDataBuilder = null ;
575606 int sectionStartRva = location . RelativeVirtualAddress ;
576607
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 )
579610 {
580611 outputSectionIndex -- ;
581612 }
582613
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+
583629 int injectedPadding = 0 ;
584630 if ( _customPESectionAlignment != 0 )
585631 {
586- if ( outputSectionIndex > 0 )
632+ if ( previousSection is not null )
587633 {
588- sectionStartRva = Math . Max ( sectionStartRva , _sectionRVAs [ outputSectionIndex - 1 ] + _sectionRawSizes [ outputSectionIndex - 1 ] ) ;
634+ sectionStartRva = Math . Max ( sectionStartRva , previousSection . RVA + previousSection . RawSize ) ;
589635 }
590636
591637 int newSectionStartRva = AlignmentHelper . AlignUp ( sectionStartRva , _customPESectionAlignment ) ;
@@ -603,13 +649,13 @@ protected override BlobBuilder SerializeSection(string name, SectionLocation loc
603649 if ( ! _target . IsWindows )
604650 {
605651 const int RVAAlign = 1 << RVABitsToMatchFilePos ;
606- if ( outputSectionIndex > 0 )
652+ if ( previousSection is not null )
607653 {
608- sectionStartRva = Math . Max ( sectionStartRva , _sectionRVAs [ outputSectionIndex - 1 ] + _sectionRawSizes [ outputSectionIndex - 1 ] ) ;
654+ sectionStartRva = Math . Max ( sectionStartRva , previousSection . RVA + previousSection . RawSize ) ;
609655
610656 // 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.
613659 // We may need one page gap of unused VA space before the next section starts.
614660 // We will assume the page size is <= RVAAlign
615661 sectionStartRva += RVAAlign ;
@@ -622,36 +668,19 @@ protected override BlobBuilder SerializeSection(string name, SectionLocation loc
622668 location = new SectionLocation ( sectionStartRva , location . PointerToRawData ) ;
623669 }
624670
625- if ( outputSectionIndex >= 0 )
626- {
627- _sectionRVAs [ outputSectionIndex ] = sectionStartRva ;
628- _sectionPointerToRawData [ outputSectionIndex ] = location . PointerToRawData ;
629- }
671+ outputSection . RVA = sectionStartRva ;
672+ outputSection . PointerToRawData = location . PointerToRawData ;
630673
631674 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 ) ;
647676 if ( sectionDataBuilder == null )
648677 {
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 ;
650680 }
651-
652- if ( sectionDataBuilder . Count == 0 )
681+ else
653682 {
654- sectionDataBuilder . WriteByte ( 0 ) ;
683+ sectionDataBuilder . LinkSuffix ( extraData ) ;
655684 }
656685
657686 int sectionRawSize = sectionDataBuilder . Count - injectedPadding ;
@@ -664,15 +693,13 @@ protected override BlobBuilder SerializeSection(string name, SectionLocation loc
664693 sectionRawSize = count ;
665694 }
666695
667- if ( outputSectionIndex >= 0 )
668- {
669- _sectionRawSizes [ outputSectionIndex ] = sectionRawSize ;
670- }
696+ outputSection . RawSize = sectionRawSize ;
697+ outputSection . IsSerialized = true ;
671698
672699 return sectionDataBuilder ;
673700 }
674701 }
675-
702+
676703 /// <summary>
677704 /// Simple helper for filling in PE header information.
678705 /// </summary>
0 commit comments