diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index 74ed8b17289cd..110acba5d752a 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -1726,11 +1726,24 @@ class Process : public std::enable_shared_from_this, size_t byte_size, uint64_t fail_value, Status &error); + /// Use Process::ReadMemoryRanges to efficiently read multiple unsigned + /// integers from memory at once. + /// TODO: this should be upstream once there is a use for it there. + llvm::SmallVector> + ReadUnsignedIntegersFromMemory(llvm::ArrayRef addresses, + unsigned byte_size); + int64_t ReadSignedIntegerFromMemory(lldb::addr_t load_addr, size_t byte_size, int64_t fail_value, Status &error); lldb::addr_t ReadPointerFromMemory(lldb::addr_t vm_addr, Status &error); + /// Use Process::ReadMemoryRanges to efficiently read multiple pointers from + /// memory at once. + /// TODO: this should be upstream once there is a use for it there. + llvm::SmallVector> + ReadPointersFromMemory(llvm::ArrayRef ptr_locs); + bool WritePointerToMemory(lldb::addr_t vm_addr, lldb::addr_t ptr_value, Status &error); diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index a75c38b0e6d86..5e2fc7e4a8ef9 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -2429,6 +2429,45 @@ uint64_t Process::ReadUnsignedIntegerFromMemory(lldb::addr_t vm_addr, return fail_value; } +llvm::SmallVector> +Process::ReadUnsignedIntegersFromMemory(llvm::ArrayRef addresses, + unsigned integer_byte_size) { + if (addresses.empty()) + return {}; + // Like ReadUnsignedIntegerFromMemory, this only supports a handful + // of widths. + if (!llvm::is_contained({1u, 2u, 4u, 8u}, integer_byte_size)) + return llvm::SmallVector>(addresses.size(), + std::nullopt); + + llvm::SmallVector> ranges{ + llvm::map_range(addresses, [=](addr_t ptr) { + return Range(ptr, integer_byte_size); + })}; + + std::vector buffer(integer_byte_size * addresses.size(), 0); + llvm::SmallVector> memory = + ReadMemoryRanges(ranges, buffer); + + llvm::SmallVector> result; + result.reserve(addresses.size()); + const uint32_t addr_size = GetAddressByteSize(); + const ByteOrder byte_order = GetByteOrder(); + + for (llvm::MutableArrayRef range : memory) { + if (range.size() != integer_byte_size) { + result.push_back(std::nullopt); + continue; + } + + DataExtractor data(range.data(), integer_byte_size, byte_order, addr_size); + offset_t offset = 0; + result.push_back(data.GetMaxU64(&offset, integer_byte_size)); + assert(offset == integer_byte_size); + } + return result; +} + int64_t Process::ReadSignedIntegerFromMemory(lldb::addr_t vm_addr, size_t integer_byte_size, int64_t fail_value, @@ -2448,6 +2487,12 @@ addr_t Process::ReadPointerFromMemory(lldb::addr_t vm_addr, Status &error) { return LLDB_INVALID_ADDRESS; } +llvm::SmallVector> +Process::ReadPointersFromMemory(llvm::ArrayRef ptr_locs) { + const size_t ptr_size = GetAddressByteSize(); + return ReadUnsignedIntegersFromMemory(ptr_locs, ptr_size); +} + bool Process::WritePointerToMemory(lldb::addr_t vm_addr, lldb::addr_t ptr_value, Status &error) { Scalar scalar; diff --git a/lldb/unittests/Target/MemoryTest.cpp b/lldb/unittests/Target/MemoryTest.cpp index f7b4e97b1f64a..051ebcd429ff1 100644 --- a/lldb/unittests/Target/MemoryTest.cpp +++ b/lldb/unittests/Target/MemoryTest.cpp @@ -367,3 +367,97 @@ TEST_F(MemoryDeathTest, TestReadMemoryRangesWithShortBuffer) { ASSERT_TRUE(result.empty()); #endif } +TEST_F(MemoryTest, TestReadPointersFromMemory) { + ArchSpec arch("x86_64-apple-macosx-"); + Platform::SetHostPlatform(PlatformRemoteMacOSX::CreateInstance(true, &arch)); + DebuggerSP debugger_sp = Debugger::CreateInstance(); + ASSERT_TRUE(debugger_sp); + TargetSP target_sp = CreateTarget(debugger_sp, arch); + ASSERT_TRUE(target_sp); + ListenerSP listener_sp(Listener::MakeListener("dummy")); + ProcessSP process = + std::make_shared(target_sp, listener_sp); + ASSERT_TRUE(process); + + // Read pointers at arbitrary addresses. + llvm::SmallVector ptr_locs = {0x0, 0x100, 0x2000, 0x123400}; + // Because of how DummyReaderProcess works, each byte of a memory read result + // is its address modulo 256: + constexpr addr_t expected_result = 0x0706050403020100; + + llvm::SmallVector> read_results = + process->ReadPointersFromMemory(ptr_locs); + + for (std::optional maybe_ptr : read_results) { + ASSERT_TRUE(maybe_ptr.has_value()); + EXPECT_EQ(*maybe_ptr, expected_result); + } +} + +TEST_F(MemoryTest, TestReadUnsignedIntegersFromMemory) { + ArchSpec arch("x86_64-apple-macosx-"); + + Platform::SetHostPlatform(PlatformRemoteMacOSX::CreateInstance(true, &arch)); + DebuggerSP debugger_sp = Debugger::CreateInstance(); + ASSERT_TRUE(debugger_sp); + TargetSP target_sp = CreateTarget(debugger_sp, arch); + ASSERT_TRUE(target_sp); + ListenerSP listener_sp(Listener::MakeListener("dummy")); + ProcessSP process = + std::make_shared(target_sp, listener_sp); + ASSERT_TRUE(process); + + { // Test reads of size 1 + llvm::SmallVector locs = {0x0, 0x101, 0x2002, 0x123403}; + llvm::SmallVector> read_results = + process->ReadUnsignedIntegersFromMemory(locs, /*byte_size=*/1); + + for (auto [maybe_int, loc] : llvm::zip(read_results, locs)) { + ASSERT_TRUE(maybe_int.has_value()); + EXPECT_EQ(*maybe_int, static_cast(loc)); + } + } + + { // Test reads of size 2 + llvm::SmallVector locs = {0x0, 0x101, 0x2002, 0x123403}; + llvm::SmallVector> read_results = + process->ReadUnsignedIntegersFromMemory(locs, /*byte_size=*/2); + + for (auto [maybe_int, loc] : llvm::zip(read_results, locs)) { + ASSERT_TRUE(maybe_int.has_value()); + uint64_t lsb = static_cast(loc); + uint64_t expected_result = ((lsb + 1) << 8) | lsb; + EXPECT_EQ(*maybe_int, expected_result); + } + } + + { // Test reads of size 4 + llvm::SmallVector locs = {0x0, 0x101, 0x2002, 0x123403}; + llvm::SmallVector> read_results = + process->ReadUnsignedIntegersFromMemory(locs, /*byte_size=*/4); + + for (auto [maybe_int, loc] : llvm::zip(read_results, locs)) { + ASSERT_TRUE(maybe_int.has_value()); + uint64_t lsb = static_cast(loc); + uint64_t expected_result = + ((lsb + 3) << 24) | ((lsb + 2) << 16) | ((lsb + 1) << 8) | lsb; + EXPECT_EQ(*maybe_int, expected_result); + } + } + + { // Test reads of size 8 + llvm::SmallVector locs = {0x0, 0x101, 0x2002, 0x123403}; + llvm::SmallVector> read_results = + process->ReadUnsignedIntegersFromMemory(locs, /*byte_size=*/8); + + for (auto [maybe_int, loc] : llvm::zip(read_results, locs)) { + ASSERT_TRUE(maybe_int.has_value()); + uint64_t lsb = static_cast(loc); + uint64_t expected_result = ((lsb + 7) << 56) | ((lsb + 6) << 48) | + ((lsb + 5) << 40) | ((lsb + 4) << 32) | + ((lsb + 3) << 24) | ((lsb + 2) << 16) | + ((lsb + 1) << 8) | lsb; + EXPECT_EQ(*maybe_int, expected_result); + } + } +}