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;
}