| File: MachO\BinaryFormat\Blobs\SuperBlob.cs | Web Access |
| Project: src\runtime\src\installer\managed\Microsoft.NET.HostModel\Microsoft.NET.HostModel.csproj (Microsoft.NET.HostModel) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. #nullable enable using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; namespace Microsoft.NET.HostModel.MachO; /// <summary> /// See https://github.com/apple-oss-distributions/Security/blob/3dab46a11f45f2ffdbd70e2127cc5a8ce4a1f222/SecurityTool/sharedTool/codesign.c#L61 /// This is the base class for a super blob, which is a blob containing other blobs. /// This class handles reading and writing of all the sub-blobs. /// </summary> internal class SuperBlob : IBlob { public SuperBlob(BlobMagic magic, IEnumerable<BlobIndex> blobIndices, IEnumerable<IBlob> blobs) { if (blobIndices.Count() != blobs.Count()) { throw new ArgumentException("Blob indices and blobs count must match."); } Magic = magic; Blobs = blobs.ToImmutableArray(); BlobIndices = blobIndices.ToImmutableArray(); ValidateBlobs(Blobs, BlobIndices); } protected SuperBlob(BlobMagic magic) { Magic = magic; Blobs = ImmutableArray<IBlob>.Empty; BlobIndices = ImmutableArray<BlobIndex>.Empty; } public SuperBlob(SuperBlob other) { Magic = other.Magic; Blobs = other.Blobs; BlobIndices = other.BlobIndices; } /// <inheritdoc /> public BlobMagic Magic { get; } /// <inheritdoc /> public uint Size => (uint)( sizeof(uint) + sizeof(uint) // magic + size + sizeof(uint) // sub blob count + (uint)BlobIndices.Length * BlobIndex.Size + Blobs.Sum(b => b.Size)); public uint SubBlobCount => (uint)Blobs.Length; public ImmutableArray<BlobIndex> BlobIndices { get; } public ImmutableArray<IBlob> Blobs { get; } [Conditional("DEBUG")] private static void ValidateBlobs(IEnumerable<IBlob> blobs, IEnumerable<BlobIndex> blobIndices) { if (blobs.Count() != blobIndices.Count()) { throw new InvalidOperationException("Blobs and blob indices count must match."); } uint expectedBlobOffset = (uint)(sizeof(uint) * 3 + blobIndices.Count() * BlobIndex.Size); uint count = (uint)blobs.Count(); for (int i = 0; i < count; i++) { var blob = blobs.ElementAt(i); var blobidx = blobIndices.ElementAt(i); if (blob.Size == 0) { throw new InvalidOperationException("Blob size cannot be zero."); } if (blobidx.Offset != expectedBlobOffset) { throw new InvalidOperationException($"Blob index offset {blobidx.Offset} does not match expected offset {expectedBlobOffset}."); } expectedBlobOffset += blob.Size; } } public int Write(IMachOFileWriter file, long offset) { // Write magic and size file.WriteUInt32BigEndian(offset, (uint)Magic); file.WriteUInt32BigEndian(offset + sizeof(uint), Size); // Write sub blob count uint count = (uint)Blobs.Length; file.WriteUInt32BigEndian(offset + sizeof(uint) * 2, count); // Write blob indices for (int i = 0; i < Blobs.Length; i++) { var blobIndex = BlobIndices[i]; file.Write(offset + sizeof(uint) * 3 + (i * BlobIndex.Size), ref blobIndex); } // Write blobs long currentOffset = offset + sizeof(uint) * 3 + (Blobs.Length * BlobIndex.Size); for (int i = 0; i < Blobs.Length; i++) { currentOffset += Blobs[i].Write(file, currentOffset); } return (int)Size; } /// <summary> /// Creates a SuperBlob by reading from a memory-mapped file. /// </summary> public static SuperBlob Read(IMachOFileReader reader, long offset) { BlobMagic magic = (BlobMagic)reader.ReadUInt32BigEndian(offset); uint size = reader.ReadUInt32BigEndian(offset + sizeof(BlobMagic)); uint count = reader.ReadUInt32BigEndian(offset + sizeof(BlobMagic) + sizeof(uint)); var blobs = new List<IBlob>((int)count); var blobIndices = new List<BlobIndex>((int)count); for (int i = 0; i < count; i++) { reader.Read(offset + sizeof(uint) * 3 + (i * BlobIndex.Size), out BlobIndex blobIndex); blobIndices.Add(blobIndex); blobs.Add(BlobParser.ParseBlob(reader, offset + blobIndex.Offset)); } Debug.Assert(size == sizeof(uint) + sizeof(uint) + sizeof(uint) + blobIndices.Count * BlobIndex.Size + blobs.Sum(b => b.Size)); return new SuperBlob(magic, blobIndices, blobs); } }