mirror of https://github.com/icsharpcode/ILSpy.git
				
				
			
			You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							168 lines
						
					
					
						
							5.2 KiB
						
					
					
				
			
		
		
	
	
							168 lines
						
					
					
						
							5.2 KiB
						
					
					
				// Licensed to the .NET Foundation under one or more agreements. | 
						|
// The .NET Foundation licenses this file to you under the MIT license. | 
						|
 | 
						|
using System; | 
						|
using System.Collections.Immutable; | 
						|
using System.IO; | 
						|
using System.IO.MemoryMappedFiles; | 
						|
using System.Runtime.CompilerServices; | 
						|
using System.Text; | 
						|
 | 
						|
namespace ICSharpCode.Decompiler | 
						|
{ | 
						|
	/// <summary> | 
						|
	/// Class for dealing with .NET 5 single-file bundles. | 
						|
	///  | 
						|
	/// Based on code from Microsoft.NET.HostModel. | 
						|
	/// </summary> | 
						|
	public static class SingleFileBundle | 
						|
	{ | 
						|
		/// <summary> | 
						|
		/// Check if the memory-mapped data is a single-file bundle | 
						|
		/// </summary> | 
						|
		public static unsafe bool IsBundle(MemoryMappedViewAccessor view, out long bundleHeaderOffset) | 
						|
		{ | 
						|
			var buffer = view.SafeMemoryMappedViewHandle; | 
						|
			byte* ptr = null; | 
						|
			buffer.AcquirePointer(ref ptr); | 
						|
			try | 
						|
			{ | 
						|
				return IsBundle(ptr, checked((long)buffer.ByteLength), out bundleHeaderOffset); | 
						|
			} | 
						|
			finally | 
						|
			{ | 
						|
				buffer.ReleasePointer(); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		public static unsafe bool IsBundle(byte* data, long size, out long bundleHeaderOffset) | 
						|
		{ | 
						|
			ReadOnlySpan<byte> bundleSignature = new byte[] { | 
						|
				// 32 bytes represent the bundle signature: SHA-256 for ".net core bundle" | 
						|
				0x8b, 0x12, 0x02, 0xb9, 0x6a, 0x61, 0x20, 0x38, | 
						|
				0x72, 0x7b, 0x93, 0x02, 0x14, 0xd7, 0xa0, 0x32, | 
						|
				0x13, 0xf5, 0xb9, 0xe6, 0xef, 0xae, 0x33, 0x18, | 
						|
				0xee, 0x3b, 0x2d, 0xce, 0x24, 0xb3, 0x6a, 0xae | 
						|
			}; | 
						|
 | 
						|
			byte* end = data + (size - bundleSignature.Length); | 
						|
			for (byte* ptr = data; ptr < end; ptr++) | 
						|
			{ | 
						|
				if (*ptr == 0x8b && bundleSignature.SequenceEqual(new ReadOnlySpan<byte>(ptr, bundleSignature.Length))) | 
						|
				{ | 
						|
					bundleHeaderOffset = Unsafe.ReadUnaligned<long>(ptr - sizeof(long)); | 
						|
					if (bundleHeaderOffset > 0 && bundleHeaderOffset < size) | 
						|
					{ | 
						|
						return true; | 
						|
					} | 
						|
				} | 
						|
			} | 
						|
 | 
						|
			bundleHeaderOffset = 0; | 
						|
			return false; | 
						|
		} | 
						|
 | 
						|
		public struct Header | 
						|
		{ | 
						|
			public uint MajorVersion; | 
						|
			public uint MinorVersion; | 
						|
			public int FileCount; | 
						|
			public string BundleID; | 
						|
 | 
						|
			// Fields introduced with v2: | 
						|
			public long DepsJsonOffset; | 
						|
			public long DepsJsonSize; | 
						|
			public long RuntimeConfigJsonOffset; | 
						|
			public long RuntimeConfigJsonSize; | 
						|
			public ulong Flags; | 
						|
 | 
						|
			public ImmutableArray<Entry> Entries; | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// FileType: Identifies the type of file embedded into the bundle. | 
						|
		/// | 
						|
		/// The bundler differentiates a few kinds of files via the manifest, | 
						|
		/// with respect to the way in which they'll be used by the runtime. | 
						|
		/// </summary> | 
						|
		public enum FileType : byte | 
						|
		{ | 
						|
			Unknown,           // Type not determined. | 
						|
			Assembly,          // IL and R2R Assemblies | 
						|
			NativeBinary,      // NativeBinaries | 
						|
			DepsJson,          // .deps.json configuration file | 
						|
			RuntimeConfigJson, // .runtimeconfig.json configuration file | 
						|
			Symbols            // PDB Files | 
						|
		}; | 
						|
 | 
						|
		public struct Entry | 
						|
		{ | 
						|
			public long Offset; | 
						|
			public long Size; | 
						|
			public long CompressedSize; // 0 if not compressed, otherwise the compressed size in the bundle | 
						|
			public FileType Type; | 
						|
			public string RelativePath; // Path of an embedded file, relative to the Bundle source-directory. | 
						|
		} | 
						|
 | 
						|
		static UnmanagedMemoryStream AsStream(MemoryMappedViewAccessor view) | 
						|
		{ | 
						|
			long size = checked((long)view.SafeMemoryMappedViewHandle.ByteLength); | 
						|
			return new UnmanagedMemoryStream(view.SafeMemoryMappedViewHandle, 0, size); | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Reads the manifest header from the memory mapping. | 
						|
		/// </summary> | 
						|
		public static Header ReadManifest(MemoryMappedViewAccessor view, long bundleHeaderOffset) | 
						|
		{ | 
						|
			using var stream = AsStream(view); | 
						|
			stream.Seek(bundleHeaderOffset, SeekOrigin.Begin); | 
						|
			return ReadManifest(stream); | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Reads the manifest header from the stream. | 
						|
		/// </summary> | 
						|
		public static Header ReadManifest(Stream stream) | 
						|
		{ | 
						|
			var header = new Header(); | 
						|
			using var reader = new BinaryReader(stream, Encoding.UTF8, leaveOpen: true); | 
						|
			header.MajorVersion = reader.ReadUInt32(); | 
						|
			header.MinorVersion = reader.ReadUInt32(); | 
						|
 | 
						|
			// Major versions 3, 4 and 5 were skipped to align bundle versioning with .NET versioning scheme | 
						|
			if (header.MajorVersion < 1 || header.MajorVersion > 6) | 
						|
			{ | 
						|
				throw new InvalidDataException($"Unsupported manifest version: {header.MajorVersion}.{header.MinorVersion}"); | 
						|
			} | 
						|
			header.FileCount = reader.ReadInt32(); | 
						|
			header.BundleID = reader.ReadString(); | 
						|
			if (header.MajorVersion >= 2) | 
						|
			{ | 
						|
				header.DepsJsonOffset = reader.ReadInt64(); | 
						|
				header.DepsJsonSize = reader.ReadInt64(); | 
						|
				header.RuntimeConfigJsonOffset = reader.ReadInt64(); | 
						|
				header.RuntimeConfigJsonSize = reader.ReadInt64(); | 
						|
				header.Flags = reader.ReadUInt64(); | 
						|
			} | 
						|
			var entries = ImmutableArray.CreateBuilder<Entry>(header.FileCount); | 
						|
			for (int i = 0; i < header.FileCount; i++) | 
						|
			{ | 
						|
				entries.Add(ReadEntry(reader, header.MajorVersion)); | 
						|
			} | 
						|
			header.Entries = entries.MoveToImmutable(); | 
						|
			return header; | 
						|
		} | 
						|
 | 
						|
		private static Entry ReadEntry(BinaryReader reader, uint bundleMajorVersion) | 
						|
		{ | 
						|
			Entry entry; | 
						|
			entry.Offset = reader.ReadInt64(); | 
						|
			entry.Size = reader.ReadInt64(); | 
						|
			entry.CompressedSize = bundleMajorVersion >= 6 ? reader.ReadInt64() : 0; | 
						|
			entry.Type = (FileType)reader.ReadByte(); | 
						|
			entry.RelativePath = reader.ReadString(); | 
						|
			return entry; | 
						|
		} | 
						|
	} | 
						|
}
 | 
						|
 |