Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions lldb/include/lldb/Target/Process.h
Original file line number Diff line number Diff line change
Expand Up @@ -1726,11 +1726,24 @@ class Process : public std::enable_shared_from_this<Process>,
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<std::optional<uint64_t>>
ReadUnsignedIntegersFromMemory(llvm::ArrayRef<lldb::addr_t> 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<std::optional<lldb::addr_t>>
ReadPointersFromMemory(llvm::ArrayRef<lldb::addr_t> ptr_locs);

bool WritePointerToMemory(lldb::addr_t vm_addr, lldb::addr_t ptr_value,
Status &error);

Expand Down
45 changes: 45 additions & 0 deletions lldb/source/Target/Process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2429,6 +2429,45 @@ uint64_t Process::ReadUnsignedIntegerFromMemory(lldb::addr_t vm_addr,
return fail_value;
}

llvm::SmallVector<std::optional<uint64_t>>
Process::ReadUnsignedIntegersFromMemory(llvm::ArrayRef<addr_t> 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<std::optional<uint64_t>>(addresses.size(),
std::nullopt);

llvm::SmallVector<Range<addr_t, size_t>> ranges{
llvm::map_range(addresses, [=](addr_t ptr) {
return Range<addr_t, size_t>(ptr, integer_byte_size);
})};

std::vector<uint8_t> buffer(integer_byte_size * addresses.size(), 0);
llvm::SmallVector<llvm::MutableArrayRef<uint8_t>> memory =
ReadMemoryRanges(ranges, buffer);

llvm::SmallVector<std::optional<uint64_t>> result;
result.reserve(addresses.size());
const uint32_t addr_size = GetAddressByteSize();
const ByteOrder byte_order = GetByteOrder();

for (llvm::MutableArrayRef<uint8_t> 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,
Expand All @@ -2448,6 +2487,12 @@ addr_t Process::ReadPointerFromMemory(lldb::addr_t vm_addr, Status &error) {
return LLDB_INVALID_ADDRESS;
}

llvm::SmallVector<std::optional<addr_t>>
Process::ReadPointersFromMemory(llvm::ArrayRef<addr_t> 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;
Expand Down
94 changes: 94 additions & 0 deletions lldb/unittests/Target/MemoryTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<DummyReaderProcess>(target_sp, listener_sp);
ASSERT_TRUE(process);

// Read pointers at arbitrary addresses.
llvm::SmallVector<addr_t> ptr_locs = {0x0, 0x100, 0x2000, 0x123400};
// Because of how DummyReaderProcess works, each byte of a memory read result
// is its address modulo 256:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment (and the DummyReaderProcess impl) describe what is done in TestReadUnsignedIntgersFromMemory, but in this test we're reading from a series of addresses that have a low byte of 0, so the returned result will be 64-bits of 0x0 in each case. How does this expected_result get returned?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so the returned result will be 64-bits of 0x0 in each case

I don't think that's true. DummyReaderProcess reads byte by byte. So if the pointer's address starts at 0, the pointer will have 8 bytes, the first byte being zero, the last byte being 7

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

    for (size_t addr = vm_addr; addr < vm_addr + size; addr++)
      buffer[addr - vm_addr] = static_cast<uint8_t>(addr); // LSB of addr.

?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your comment above describes exactly this - reading 4 bytes from addr 0x5 will get you a UInt32 of 0x05050505. That's what TestReadUnsignedIntegersFromMemory is doing. But this method is expecting a UInt64 with different values in each byte pos.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reading 4 bytes from addr 0x5 will get you a UInt32 of 0x05050505

No, reading 4 bytes from addr 0x5 will get you 0x05060708 (or the other way around, endianness is hard)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah my bad, i see you're incrementing addr in your writer loop. I thought it would not mutate that.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fwiw each byte of a memory read result is its address modulo 256 -- I'd probably say "the byte's address" instead of "its address" because it's easy for me to read this as "the address of the byte range % 256"

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah my bad, i see you're incrementing addr in your writer loop. I thought it would not mutate that.

addr is the loop variable; the function argument is vm_addr, which is not mutated.
I will try to update that code on a separate PR to change the comment as you described and rename some variables.

constexpr addr_t expected_result = 0x0706050403020100;

llvm::SmallVector<std::optional<addr_t>> read_results =
process->ReadPointersFromMemory(ptr_locs);

for (std::optional<addr_t> 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<DummyReaderProcess>(target_sp, listener_sp);
ASSERT_TRUE(process);

{ // Test reads of size 1
llvm::SmallVector<addr_t> locs = {0x0, 0x101, 0x2002, 0x123403};
llvm::SmallVector<std::optional<addr_t>> 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<uint8_t>(loc));
}
}

{ // Test reads of size 2
llvm::SmallVector<addr_t> locs = {0x0, 0x101, 0x2002, 0x123403};
llvm::SmallVector<std::optional<addr_t>> 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<uint8_t>(loc);
uint64_t expected_result = ((lsb + 1) << 8) | lsb;
EXPECT_EQ(*maybe_int, expected_result);
}
}

{ // Test reads of size 4
llvm::SmallVector<addr_t> locs = {0x0, 0x101, 0x2002, 0x123403};
llvm::SmallVector<std::optional<addr_t>> 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<uint8_t>(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<addr_t> locs = {0x0, 0x101, 0x2002, 0x123403};
llvm::SmallVector<std::optional<addr_t>> 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<uint8_t>(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);
}
}
}