| //===----------------------------------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "lldb/Utility/VirtualDataExtractor.h" |
| #include <cassert> |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| |
| VirtualDataExtractor::VirtualDataExtractor(const void *data, |
| offset_t data_length, |
| ByteOrder byte_order, |
| uint32_t addr_size, |
| LookupTable lookup_table) |
| : DataExtractor(data, data_length, byte_order, addr_size), |
| m_lookup_table(std::move(lookup_table)) { |
| m_lookup_table.Sort(); |
| } |
| |
| VirtualDataExtractor::VirtualDataExtractor(const DataBufferSP &data_sp, |
| ByteOrder byte_order, |
| uint32_t addr_size, |
| LookupTable lookup_table) |
| : DataExtractor(data_sp, byte_order, addr_size), |
| m_lookup_table(std::move(lookup_table)) { |
| m_lookup_table.Sort(); |
| } |
| |
| VirtualDataExtractor::VirtualDataExtractor(const DataBufferSP &data_sp, |
| LookupTable lookup_table) |
| : DataExtractor(data_sp), m_lookup_table(std::move(lookup_table)) { |
| m_lookup_table.Sort(); |
| } |
| |
| const VirtualDataExtractor::LookupTable::Entry * |
| VirtualDataExtractor::FindEntry(offset_t virtual_addr) const { |
| // Use RangeDataVector's binary search instead of linear search. |
| return m_lookup_table.FindEntryThatContains(virtual_addr); |
| } |
| |
| bool VirtualDataExtractor::ValidateVirtualRead(offset_t virtual_addr, |
| offset_t length) const { |
| const LookupTable::Entry *entry = FindEntry(virtual_addr); |
| if (!entry) |
| return false; |
| |
| // Assert that the read does not cross entry boundaries. |
| // RangeData.Contains() checks if a range is fully contained. |
| assert(entry->Contains(LookupTable::Range(virtual_addr, length)) && |
| "Read crosses lookup table entry boundary"); |
| |
| // Also validate that the physical offset is within the data buffer. |
| // RangeData.data contains the physical offset. |
| offset_t physical_offset = entry->data + (virtual_addr - entry->base); |
| return ValidOffsetForDataOfSize(physical_offset, length); |
| } |
| |
| const void *VirtualDataExtractor::GetData(offset_t *offset_ptr, |
| offset_t length) const { |
| // Override to treat offset as virtual address. |
| if (!offset_ptr) |
| return nullptr; |
| |
| offset_t virtual_addr = *offset_ptr; |
| |
| if (!ValidateVirtualRead(virtual_addr, length)) |
| return nullptr; |
| |
| const LookupTable::Entry *entry = FindEntry(virtual_addr); |
| assert(entry && "ValidateVirtualRead should have found an entry"); |
| |
| offset_t physical_offset = entry->data + (virtual_addr - entry->base); |
| // Use base class PeekData directly to avoid recursion. |
| const void *result = DataExtractor::PeekData(physical_offset, length); |
| |
| if (result) { |
| // Advance the virtual offset pointer. |
| *offset_ptr += length; |
| } |
| |
| return result; |
| } |
| |
| const uint8_t *VirtualDataExtractor::PeekData(offset_t offset, |
| offset_t length) const { |
| // Override to treat offset as virtual address. |
| if (!ValidateVirtualRead(offset, length)) |
| return nullptr; |
| |
| const LookupTable::Entry *entry = FindEntry(offset); |
| assert(entry && "ValidateVirtualRead should have found an entry"); |
| |
| offset_t physical_offset = entry->data + (offset - entry->base); |
| // Use the base class PeekData with the physical offset. |
| return DataExtractor::PeekData(physical_offset, length); |
| } |
| |
| uint8_t VirtualDataExtractor::GetU8_unchecked(offset_t *offset_ptr) const { |
| offset_t virtual_addr = *offset_ptr; |
| const LookupTable::Entry *entry = FindEntry(virtual_addr); |
| assert(entry && "Unchecked methods require valid virtual address"); |
| |
| offset_t physical_offset = entry->data + (virtual_addr - entry->base); |
| uint8_t result = DataExtractor::GetU8_unchecked(&physical_offset); |
| *offset_ptr += 1; |
| return result; |
| } |
| |
| uint16_t VirtualDataExtractor::GetU16_unchecked(offset_t *offset_ptr) const { |
| offset_t virtual_addr = *offset_ptr; |
| const LookupTable::Entry *entry = FindEntry(virtual_addr); |
| assert(entry && "Unchecked methods require valid virtual address"); |
| |
| offset_t physical_offset = entry->data + (virtual_addr - entry->base); |
| uint16_t result = DataExtractor::GetU16_unchecked(&physical_offset); |
| *offset_ptr += 2; |
| return result; |
| } |
| |
| uint32_t VirtualDataExtractor::GetU32_unchecked(offset_t *offset_ptr) const { |
| offset_t virtual_addr = *offset_ptr; |
| const LookupTable::Entry *entry = FindEntry(virtual_addr); |
| assert(entry && "Unchecked methods require valid virtual address"); |
| |
| offset_t physical_offset = entry->data + (virtual_addr - entry->base); |
| uint32_t result = DataExtractor::GetU32_unchecked(&physical_offset); |
| *offset_ptr += 4; |
| return result; |
| } |
| |
| uint64_t VirtualDataExtractor::GetU64_unchecked(offset_t *offset_ptr) const { |
| offset_t virtual_addr = *offset_ptr; |
| const LookupTable::Entry *entry = FindEntry(virtual_addr); |
| assert(entry && "Unchecked methods require valid virtual address"); |
| |
| offset_t physical_offset = entry->data + (virtual_addr - entry->base); |
| uint64_t result = DataExtractor::GetU64_unchecked(&physical_offset); |
| *offset_ptr += 8; |
| return result; |
| } |
| |
| DataExtractorSP |
| VirtualDataExtractor::GetSubsetExtractorSP(offset_t virtual_offset, |
| offset_t virtual_length) { |
| const LookupTable::Entry *entry = FindEntry(virtual_offset); |
| assert( |
| entry && |
| "VirtualDataExtractor subset extractor requires valid virtual address"); |
| if (!entry) |
| return {}; |
| |
| // Entry::data is the offset into the DataBuffer's actual start/end range |
| // Entry::base is the virtual address at the start of this region of data |
| offset_t offset_into_entry_range = virtual_offset - entry->base; |
| assert( |
| offset_into_entry_range + virtual_length <= entry->size && |
| "VirtualDataExtractor subset may not span multiple LookupTable entries"); |
| if (offset_into_entry_range + virtual_length > entry->size) |
| return {}; |
| |
| // We could support a Subset VirtualDataExtractor which covered |
| // multiple LookupTable virtual entries, but we'd need to mutate |
| // all of the LookupTable entries that were properly included in |
| // the Subset, a bit tricky. So we won't implement that until it's |
| // needed. |
| |
| offset_t physical_start = entry->data + offset_into_entry_range; |
| std::shared_ptr<DataExtractor> new_sp = std::make_shared<DataExtractor>( |
| GetSharedDataBuffer(), GetByteOrder(), GetAddressByteSize()); |
| new_sp->SetData(GetSharedDataBuffer(), physical_start, virtual_length); |
| return new_sp; |
| } |
| |
| // Return a DataExtractorSP that contains a single LookupTable's entry; all |
| // bytes are guaranteed to be readable. |
| DataExtractorSP |
| VirtualDataExtractor::GetSubsetExtractorSP(offset_t virtual_offset) { |
| const LookupTable::Entry *entry = FindEntry(virtual_offset); |
| assert( |
| entry && |
| "VirtualDataExtractor subset extractor requires valid virtual address"); |
| if (!entry) |
| return {}; |
| |
| // Entry::data is the offset into the DataBuffer's actual start/end range |
| // Entry::base is the virtual address at the start of this region of data |
| offset_t offset_into_entry_range = virtual_offset - entry->base; |
| |
| offset_t physical_start = entry->data + offset_into_entry_range; |
| std::shared_ptr<DataExtractor> new_sp = std::make_shared<DataExtractor>( |
| GetSharedDataBuffer(), GetByteOrder(), GetAddressByteSize()); |
| new_sp->SetData(GetSharedDataBuffer(), physical_start, |
| entry->size - offset_into_entry_range); |
| return new_sp; |
| } |
| |
| // Return an ArrayRef to the first contiguous region of the LookupTable |
| // only. The LookupTable entries may have gaps of unmapped data, and we |
| // can't include those in the ArrayRef or something may touch those pages. |
| llvm::ArrayRef<uint8_t> VirtualDataExtractor::GetData() const { |
| const LookupTable::Entry *entry = FindEntry(0); |
| assert(entry && |
| "VirtualDataExtractor GetData requires valid virtual address"); |
| if (!entry) |
| return {}; |
| return {m_start + static_cast<size_t>(entry->data), static_cast<size_t>(entry->size)}; |
| } |