private int DoRead()

in src/Microsoft.Diagnostics.Runtime/Utilities/PEImage/PEImage.cs [305:511]


        private int DoRead(ref int offset, Span<byte> dest)
        {
            int beginRead = offset;
            int endRead = offset + dest.Length - 1;
            int beginRelocation = -1;
            int endRelocation = -1;

            if (_relocations != null)
            {
                //
                // The _relocations array is an array of offsets that begin and end (both inclusively)
                // relocations.
                //
                // For example:
                // 32, 35, 36, 39 means [32, 36) and [36, 40) contains data that needs to be relocated.
                //
                // It is assumed that these never overlaps, so the array contain no duplicates, and it
                // is sorted, the array is prepared in the constructor by parsing the .reloc entries.
                //
                // The even/odd index always correspond to an open/close of the relocation interval.
                //
                // There is a possibility that the requested range partially contains a relocation, so
                // we need to make sure the reading range is extended in those cases. The code below
                // uses binary search to find the containing relocation records and deciding on
                // exactly how much we wanted to extend the read.
                //
                // The code also keeps track of which relocation to start and stop to apply so that
                // we can apply them after reading.
                //
                int beginSearch = Array.BinarySearch(_relocations, offset);
                if (beginSearch < 0)
                {
                    int beginSearchComplement = ~beginSearch;
                    if (beginSearchComplement == _relocations.Length)
                    {
                        // Case 1: ]
                        //           ^
                        // The read range starts after all relocations finishes, no need to extend the read
                        beginRelocation = _relocations.Length;
                    }
                    else if ((beginSearchComplement & 1) == 0)
                    {
                        // Case 2: ]   [
                        //           ^
                        // The read range starts between relocations, no need to extend the read
                        beginRelocation = beginSearchComplement;
                    }
                    else
                    {
                        // Case 3: [   ]
                        //           ^
                        // The read range starts within a relocation, extend the read
                        Debug.Assert((beginSearchComplement & 1) == 1);
                        Debug.Assert(beginSearch > 0);
                        beginRelocation = beginSearch - 1;
                        beginRead = _relocations[beginRelocation];
                    }
                }
                else
                {
                    if ((beginSearch & 1) == 0)
                    {
                        // Case 4: [
                        //         ^
                        // The read range starts exactly where a relocation start, no need to extend the read
                        beginRelocation = beginSearch;
                    }
                    else
                    {
                        // Case 5: ]
                        //         ^
                        // The read range starts exactly where a relocation end, extend the read
                        Debug.Assert(beginSearch > 0);
                        Debug.Assert((beginSearch & 1) == 1);
                        beginRelocation = beginSearch - 1;
                        beginRead = _relocations[beginRelocation];
                    }
                }
                int endSearch = Array.BinarySearch(_relocations, offset + dest.Length - 1);
                if (endSearch < 0)
                {
                    int endSearchComplement = ~endSearch;
                    if (endSearchComplement == _relocations.Length)
                    {
                        // Case 1: ]
                        //           ^
                        // The read range ends after all relocations finishes, no need to extend the read
                        endRelocation = _relocations.Length - 1;
                    }
                    else if ((endSearchComplement & 1) == 0)
                    {
                        // Case 2: ]   [
                        //           ^
                        // The read range ends between relocations, no need to extend the read
                        endRelocation = endSearchComplement - 1;
                    }
                    else
                    {
                        // Case 3: [   ]
                        //           ^
                        // The read range ends within a relocation, extend the read
                        Debug.Assert((endSearchComplement & 1) == 1);
                        Debug.Assert(endSearch > 0);
                        endRelocation = endSearch;
                        endRead = _relocations[endRelocation];
                    }
                }
                else
                {
                    if ((endSearch & 1) == 0)
                    {
                        // Case 4: [
                        //         ^
                        // The read range ends exactly where a relocation start, extend the read
                        endRelocation = endSearch + 1;
                        endRead = _relocations[endRelocation];
                    }
                    else
                    {
                        // Case 5: ]
                        //         ^
                        // The read range ends exactly where a relocation end, no need to extend the read
                        Debug.Assert(endSearch > 0);
                        Debug.Assert((endSearch & 1) == 1);
                        endRelocation = endSearch;
                    }
                }

                Debug.Assert((beginRelocation & 1) == 0);
                Debug.Assert((endRelocation & 1) == 1);
                Debug.Assert(beginRead <= offset);
                Debug.Assert(offset + dest.Length - 1 <= endRead);
            }

            int readSize = endRead - beginRead + 1;
            int headShift = offset - beginRead;

            byte[] rawBuffer = ArrayPool<byte>.Shared.Rent(readSize);
            Span<byte> buffer = new(rawBuffer, 0, readSize);

            SeekTo(beginRead);

            int read = _stream.Read(buffer);
            if (read < headShift)
            {
                // This is unexpected, we failed to read something that is supposed to be a reloc.
                SeekTo(_offset);
                offset = _offset;
                return 0;
            }

            read -= headShift;

            if (read > dest.Length)
            {
                read = dest.Length;
            }

            SeekTo(offset + read);
            offset += read;
            _offset = offset;

            if ((_relocations != null) && (beginRelocation < endRelocation))
            {
                for (int r = beginRelocation; r < endRelocation; r += 2)
                {
                    int relocationStartOffset = _relocations[r];
                    int relocationEndOffset = _relocations[r + 1];
                    int relocationSize = relocationEndOffset - relocationStartOffset + 1;

                    Debug.Assert(relocationStartOffset >= beginRead);
                    Debug.Assert(relocationEndOffset <= endRead);

                    byte[] beforeBytes = new byte[relocationSize];
                    for (int i = 0; i < relocationSize; i++)
                        beforeBytes[i] = buffer[relocationStartOffset - beginRead + i];

                    byte[]? afterBytes;
                    if (relocationSize == 4)
                    {
                        uint beforeValue = BitConverter.ToUInt32(beforeBytes, 0);
                        uint afterValue = beforeValue + (uint)(_loadedImageBase - _imageBase);
                        afterBytes = BitConverter.GetBytes(afterValue);
                    }
                    else
                    {
                        Debug.Assert(relocationSize == 8);
                        ulong beforeValue = BitConverter.ToUInt64(beforeBytes, 0);
                        ulong afterValue = beforeValue - _imageBase + _loadedImageBase;
                        afterBytes = BitConverter.GetBytes(afterValue);
                    }
                    Debug.Assert(afterBytes.Length == relocationSize);
                    for (int i = 0; i < relocationSize; i++)
                    {
                        buffer[relocationStartOffset - beginRead + i] = afterBytes[i];
                    }
                }
            }

            for (int i = 0; i < read; i++)
            {
                dest[i] = buffer[i + headShift];
            }

            ArrayPool<byte>.Shared.Return(rawBuffer);
            return read;
        }