internal override unsafe bool GetFileVersion()

in src/Microsoft.Diagnostics.Runtime/src/Utilities/Platform/MacOSFunctions.cs [21:140]


        internal override unsafe bool GetFileVersion(string dll, out int major, out int minor, out int revision, out int patch)
        {
            using FileStream stream = File.OpenRead(dll);
            MachOFileReader reader = new MachOFileReader(stream);

            MachOHeader64 header = reader.Read<MachOHeader64>();
            if (header.Magic != MachOHeader64.ExpectedMagic)
            {
                throw new NotSupportedException();
            }

            long dataOffset = 0;
            long dataSize = 0;

            byte[] dataSegmentName = Encoding.ASCII.GetBytes("__DATA\0");
            byte[] dataSectionName = Encoding.ASCII.GetBytes("__data\0");
            for (int c = 0; c < header.NumberOfCommands; c++)
            {
                MachOCommand command = reader.Read<MachOCommand>();
                MachOCommandType commandType = command.Command;
                int commandSize = command.CommandSize;

                if (commandType == MachOCommandType.Segment64)
                {
                    long position = stream.Position;
                    MachOSegmentCommand64 segmentCommand = reader.Read<MachOSegmentCommand64>();
                    if (new ReadOnlySpan<byte>(segmentCommand.SegmentName, dataSegmentName.Length).SequenceEqual(dataSegmentName))
                    {
                        for (int s = 0; s < segmentCommand.NumberOfSections; s++)
                        {
                            MachOSection64 section = reader.Read<MachOSection64>();
                            if (new ReadOnlySpan<byte>(section.SectionName, dataSectionName.Length).SequenceEqual(dataSectionName))
                            {
                                dataOffset = section.Offset;
                                dataSize = section.Size;
                                break;
                            }
                        }

                        break;
                    }

                    stream.Position = position;
                }

                stream.Seek(commandSize - sizeof(MachOCommand), SeekOrigin.Current);
            }

            Span<byte> buffer = stackalloc byte[s_versionLength];
            long address = dataOffset;
            long endAddress = address + dataSize;

            Span<byte> bytes = stackalloc byte[1];
            Span<char> chars = stackalloc char[1];

            while (address < endAddress)
            {
                int read = reader.Read(address, buffer);
                if (read < s_versionLength)
                {
                    break;
                }

                if (!buffer.SequenceEqual(s_versionString))
                {
                    address++;
                    continue;
                }

                address += s_versionLength;

                // TODO:  This should be cleaned up to not read byte by byte in the future.  Leaving it here
                // until we decide whether to rewrite the Linux coredumpreader or not.
                StringBuilder builder = new StringBuilder();
                while (address < endAddress)
                {
                    read = reader.Read(address, bytes);
                    if (read < bytes.Length)
                    {
                        break;
                    }

                    if (bytes[0] == '\0')
                    {
                        break;
                    }

                    if (bytes[0] == ' ')
                    {
                        try
                        {
                            Version v = Version.Parse(builder.ToString());
                            major = v.Major;
                            minor = v.Minor;
                            revision = v.Build;
                            patch = v.Revision;
                            return true;
                        }
                        catch (FormatException)
                        {
                            break;
                        }
                    }

                    fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes))
                    fixed (char* charsPtr = &MemoryMarshal.GetReference(chars))
                    {
                        _ = Encoding.ASCII.GetChars(bytesPtr, bytes.Length, charsPtr, chars.Length);
                    }

                    _ = builder.Append(chars[0]);
                    address++;
                }

                break;
            }

            major = minor = revision = patch = 0;
            return false;
        }