55using System ;
66using System . Diagnostics ;
77using System . IO ;
8+ using System . Runtime . CompilerServices ;
89using System . Runtime . InteropServices ;
910using System . Text ;
1011using LibObjectFile . Diagnostics ;
1112using LibObjectFile . PE . Internal ;
1213using LibObjectFile . Utils ;
14+ using static System . Runtime . InteropServices . JavaScript . JSType ;
1315
1416namespace LibObjectFile . PE ;
1517
@@ -37,12 +39,15 @@ public bool TryWrite(Stream stream, out DiagnosticBag diagnostics, PEImageWriter
3739 {
3840 if ( stream == null ) throw new ArgumentNullException ( nameof ( stream ) ) ;
3941
40- var peWriter = new PEImageWriter ( this , stream ) ;
42+ var peWriter = new PEImageWriter ( this , stream , options ?? PEImageWriterOptions . Default ) ;
4143 diagnostics = peWriter . Diagnostics ;
4244
43- if ( options is not null )
45+ diagnostics . EnableStackTrace = peWriter . Options . EnableStackTrace ;
46+
47+ if ( peWriter . Options . EnableChecksum && stream is not MemoryStream )
4448 {
45- diagnostics . EnableStackTrace = options . EnableStackTrace ;
49+ diagnostics . Error ( DiagnosticId . PE_ERR_ChecksumNotSupported , "Checksum is only supported for MemoryStream" ) ;
50+ return false ;
4651 }
4752
4853 // Verify the coherence of the PE file
@@ -105,6 +110,8 @@ public override unsafe void Write(PEImageWriter writer)
105110 // Update OptionalHeader
106111 OptionalHeader . OptionalHeaderCommonPart1 . AddressOfEntryPoint = OptionalHeader . AddressOfEntryPoint . RVA ( ) ;
107112 OptionalHeader . OptionalHeaderCommonPart1 . BaseOfCode = OptionalHeader . BaseOfCode ? . RVA ?? 0 ;
113+
114+ var optionalHeaderPosition = position ;
108115
109116 if ( IsPE32 )
110117 {
@@ -244,5 +251,61 @@ public override unsafe void Write(PEImageWriter writer)
244251 {
245252 writer . Diagnostics . Error ( DiagnosticId . PE_ERR_InvalidInternalState , $ "Generated size { position } does not match expecting size { Size } ") ;
246253 }
254+
255+ if ( writer . Options . EnableChecksum )
256+ {
257+ CalculateAndApplyChecksum ( ( MemoryStream ) writer . Stream , optionalHeaderPosition ) ;
258+ }
259+ }
260+
261+ private void CalculateAndApplyChecksum ( MemoryStream stream , uint optionalHeaderPosition )
262+ {
263+ var data = stream . GetBuffer ( ) ;
264+ var length = ( int ) stream . Length ;
265+ var buffer = new Span < byte > ( data , 0 , length ) ;
266+
267+ // Zero the checksum before calculating it
268+ MemoryMarshal . Write ( buffer . Slice ( ( int ) optionalHeaderPosition + 64 ) , 0 ) ;
269+
270+ var checksum = CalculateChecksum ( buffer ) ;
271+
272+ // Update the checksum in the PE header
273+ MemoryMarshal . Write ( buffer . Slice ( ( int ) optionalHeaderPosition + 64 ) , checksum ) ;
274+ }
275+
276+ private static uint CalculateChecksum ( Span < byte > peFile )
277+ {
278+ ulong checksum = 0 ;
279+
280+ var shortBuffer = MemoryMarshal . Cast < byte , ushort > ( peFile ) ;
281+ foreach ( var value in shortBuffer )
282+ {
283+ checksum = AggregateChecksum ( checksum , value ) ;
284+ }
285+
286+ if ( ( peFile . Length & 1 ) != 0 )
287+ {
288+ checksum = AggregateChecksum ( checksum , peFile [ peFile . Length - 1 ] ) ;
289+ }
290+
291+ checksum = ( ( ushort ) checksum ) + ( checksum >> 16 ) ;
292+ checksum += checksum >> 16 ;
293+ checksum &= 0xffff ;
294+ checksum += ( ulong ) peFile . Length ;
295+
296+ return ( uint ) checksum ;
297+
298+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
299+ static ulong AggregateChecksum ( ulong checksum , ushort value )
300+ {
301+ checksum += value ;
302+ checksum = ( uint ) checksum + ( checksum >> 32 ) ;
303+ if ( checksum > uint . MaxValue )
304+ {
305+ checksum = unchecked ( ( uint ) checksum ) + ( checksum >> 32 ) ;
306+ }
307+
308+ return checksum ;
309+ }
247310 }
248- }
311+ }
0 commit comments