summaryrefslogtreecommitdiff
path: root/libunwindstack/Memory.cpp
diff options
context:
space:
mode:
authorJosh Gao <jmgao@google.com>2017-09-26 14:11:37 -0700
committerJosh Gao <jmgao@google.com>2017-11-15 15:38:13 -0800
commit506e04cf5dc32bb58cda3aab1b9e2ca2fc2006c7 (patch)
treede0da6ace11ccac0cda6c58fe24c7f88b6c2fcd6 /libunwindstack/Memory.cpp
parent438b1e800338a69f9f1fbb9fc0870024cc1e684f (diff)
downloadunwinding-506e04cf5dc32bb58cda3aab1b9e2ca2fc2006c7.tar.gz
unwindstack: add Memory::ReadPartially.
Add a way to read while allowing for partial reads. Test: new tests added to libunwindstack_test, ran 32/64 on hikey960, sailfish Test: ran unwind on hikey960/sailfish Change-Id: I8b11d9230fcd3122148ef3f980863ac1404ad70a
Diffstat (limited to 'libunwindstack/Memory.cpp')
-rw-r--r--libunwindstack/Memory.cpp226
1 files changed, 107 insertions, 119 deletions
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index 32753df..f27a350 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -32,8 +32,63 @@
#include "Check.h"
+static size_t ProcessVmRead(pid_t pid, void* dst, uint64_t remote_src, size_t len) {
+ struct iovec dst_iov = {
+ .iov_base = dst,
+ .iov_len = len,
+ };
+
+ // Split up the remote read across page boundaries.
+ // From the manpage:
+ // A partial read/write may result if one of the remote_iov elements points to an invalid
+ // memory region in the remote process.
+ //
+ // Partial transfers apply at the granularity of iovec elements. These system calls won't
+ // perform a partial transfer that splits a single iovec element.
+ constexpr size_t kMaxIovecs = 64;
+ struct iovec src_iovs[kMaxIovecs];
+ size_t iovecs_used = 0;
+
+ uint64_t cur = remote_src;
+ while (len > 0) {
+ if (iovecs_used == kMaxIovecs) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ // struct iovec uses void* for iov_base.
+ if (cur >= UINTPTR_MAX) {
+ errno = EFAULT;
+ return 0;
+ }
+
+ src_iovs[iovecs_used].iov_base = reinterpret_cast<void*>(cur);
+
+ uintptr_t misalignment = cur & (getpagesize() - 1);
+ size_t iov_len = getpagesize() - misalignment;
+ iov_len = std::min(iov_len, len);
+
+ len -= iov_len;
+ if (__builtin_add_overflow(cur, iov_len, &cur)) {
+ errno = EFAULT;
+ return 0;
+ }
+
+ src_iovs[iovecs_used].iov_len = iov_len;
+ ++iovecs_used;
+ }
+
+ ssize_t rc = process_vm_readv(pid, &dst_iov, 1, src_iovs, iovecs_used, 0);
+ return rc == -1 ? 0 : rc;
+}
+
namespace unwindstack {
+bool Memory::Read(uint64_t addr, void* dst, size_t size) {
+ size_t rc = ReadPartially(addr, dst, size);
+ return rc == size;
+}
+
bool Memory::ReadString(uint64_t addr, std::string* string, uint64_t max_read) {
string->clear();
uint64_t bytes_read = 0;
@@ -59,16 +114,17 @@ std::shared_ptr<Memory> Memory::CreateProcessMemory(pid_t pid) {
return std::shared_ptr<Memory>(new MemoryRemote(pid));
}
-bool MemoryBuffer::Read(uint64_t addr, void* dst, size_t size) {
- uint64_t last_read_byte;
- if (__builtin_add_overflow(size, addr, &last_read_byte)) {
- return false;
+size_t MemoryBuffer::ReadPartially(uint64_t addr, void* dst, size_t size) {
+ if (addr >= raw_.size()) {
+ return 0;
}
- if (last_read_byte > raw_.size()) {
- return false;
- }
- memcpy(dst, &raw_[addr], size);
- return true;
+
+ size_t bytes_left = raw_.size() - static_cast<size_t>(addr);
+ const unsigned char* actual_base = static_cast<const unsigned char*>(raw_.data()) + addr;
+ size_t actual_len = std::min(bytes_left, size);
+
+ memcpy(dst, actual_base, actual_len);
+ return actual_len;
}
uint8_t* MemoryBuffer::GetPtr(size_t offset) {
@@ -129,145 +185,77 @@ bool MemoryFileAtOffset::Init(const std::string& file, uint64_t offset, uint64_t
return true;
}
-bool MemoryFileAtOffset::Read(uint64_t addr, void* dst, size_t size) {
- uint64_t max_size;
- if (__builtin_add_overflow(addr, size, &max_size) || max_size > size_) {
- return false;
+size_t MemoryFileAtOffset::ReadPartially(uint64_t addr, void* dst, size_t size) {
+ if (addr >= size_) {
+ return 0;
}
- memcpy(dst, &data_[addr], size);
- return true;
+
+ size_t bytes_left = size_ - static_cast<size_t>(addr);
+ const unsigned char* actual_base = static_cast<const unsigned char*>(data_) + addr;
+ size_t actual_len = std::min(bytes_left, size);
+
+ memcpy(dst, actual_base, actual_len);
+ return actual_len;
}
-bool MemoryRemote::PtraceRead(uint64_t addr, long* value) {
-#if !defined(__LP64__)
- // Cannot read an address greater than 32 bits.
- if (addr > UINT32_MAX) {
- return false;
- }
-#endif
- // ptrace() returns -1 and sets errno when the operation fails.
- // To disambiguate -1 from a valid result, we clear errno beforehand.
- errno = 0;
- *value = ptrace(PTRACE_PEEKTEXT, pid_, reinterpret_cast<void*>(addr), nullptr);
- if (*value == -1 && errno) {
- return false;
- }
- return true;
+size_t MemoryRemote::ReadPartially(uint64_t addr, void* dst, size_t size) {
+ return ProcessVmRead(pid_, dst, addr, size);
}
-bool MemoryRemote::Read(uint64_t addr, void* dst, size_t bytes) {
- // Make sure that there is no overflow.
- uint64_t max_size;
- if (__builtin_add_overflow(addr, bytes, &max_size)) {
- return false;
- }
+size_t MemoryLocal::ReadPartially(uint64_t addr, void* dst, size_t size) {
+ return ProcessVmRead(getpid(), dst, addr, size);
+}
- size_t bytes_read = 0;
- long data;
- size_t align_bytes = addr & (sizeof(long) - 1);
- if (align_bytes != 0) {
- if (!PtraceRead(addr & ~(sizeof(long) - 1), &data)) {
- return false;
- }
- size_t copy_bytes = std::min(sizeof(long) - align_bytes, bytes);
- memcpy(dst, reinterpret_cast<uint8_t*>(&data) + align_bytes, copy_bytes);
- addr += copy_bytes;
- dst = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(dst) + copy_bytes);
- bytes -= copy_bytes;
- bytes_read += copy_bytes;
- }
+MemoryRange::MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t length,
+ uint64_t offset)
+ : memory_(memory), begin_(begin), length_(length), offset_(offset) {}
- for (size_t i = 0; i < bytes / sizeof(long); i++) {
- if (!PtraceRead(addr, &data)) {
- return false;
- }
- memcpy(dst, &data, sizeof(long));
- dst = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(dst) + sizeof(long));
- addr += sizeof(long);
- bytes_read += sizeof(long);
+size_t MemoryRange::ReadPartially(uint64_t addr, void* dst, size_t size) {
+ if (addr < offset_) {
+ return 0;
}
- size_t left_over = bytes & (sizeof(long) - 1);
- if (left_over) {
- if (!PtraceRead(addr, &data)) {
- return false;
- }
- memcpy(dst, &data, left_over);
- bytes_read += left_over;
+ uint64_t read_offset = addr - offset_;
+ if (read_offset >= length_) {
+ return 0;
}
- return true;
-}
-bool MemoryLocal::Read(uint64_t addr, void* dst, size_t size) {
- // Make sure that there is no overflow.
- uint64_t max_size;
- if (__builtin_add_overflow(addr, size, &max_size)) {
- return false;
+ uint64_t read_length = std::min(static_cast<uint64_t>(size), length_ - read_offset);
+ uint64_t read_addr;
+ if (__builtin_add_overflow(read_offset, begin_, &read_addr)) {
+ return 0;
}
- // The process_vm_readv call will not always work on remote
- // processes, so only use it for reads from the current pid.
- // Use this method to avoid crashes if an address is invalid since
- // unwind data could try to access any part of the address space.
- struct iovec local_io;
- local_io.iov_base = dst;
- local_io.iov_len = size;
-
- struct iovec remote_io;
- remote_io.iov_base = reinterpret_cast<void*>(static_cast<uintptr_t>(addr));
- remote_io.iov_len = size;
-
- ssize_t bytes_read = process_vm_readv(getpid(), &local_io, 1, &remote_io, 1, 0);
- if (bytes_read == -1) {
- return false;
- }
- return static_cast<size_t>(bytes_read) == size;
+ return memory_->ReadPartially(read_addr, dst, read_length);
}
bool MemoryOffline::Init(const std::string& file, uint64_t offset) {
- if (!MemoryFileAtOffset::Init(file, offset)) {
- return false;
- }
- // The first uint64_t value is the start of memory.
- if (!MemoryFileAtOffset::Read(0, &start_, sizeof(start_))) {
+ auto memory_file = std::make_shared<MemoryFileAtOffset>();
+ if (!memory_file->Init(file, offset)) {
return false;
}
- // Subtract the first 64 bit value from the total size.
- size_ -= sizeof(start_);
- return true;
-}
-bool MemoryOffline::Read(uint64_t addr, void* dst, size_t size) {
- uint64_t max_size;
- if (__builtin_add_overflow(addr, size, &max_size)) {
+ // The first uint64_t value is the start of memory.
+ uint64_t start;
+ if (!memory_file->Read(0, &start, sizeof(start))) {
return false;
}
- uint64_t real_size;
- if (__builtin_add_overflow(start_, offset_, &real_size) ||
- __builtin_add_overflow(real_size, size_, &real_size)) {
+ uint64_t size = memory_file->Size();
+ if (__builtin_sub_overflow(size, sizeof(start), &size)) {
return false;
}
- if (addr < start_ || max_size > real_size) {
- return false;
- }
- memcpy(dst, &data_[addr + offset_ - start_ + sizeof(start_)], size);
+ memory_ = std::make_unique<MemoryRange>(memory_file, sizeof(start), size, start);
return true;
}
-MemoryRange::MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t end)
- : memory_(memory), begin_(begin), length_(end - begin) {
- CHECK(end > begin);
-}
-
-bool MemoryRange::Read(uint64_t addr, void* dst, size_t size) {
- uint64_t max_read;
- if (__builtin_add_overflow(addr, size, &max_read) || max_read > length_) {
- return false;
+size_t MemoryOffline::ReadPartially(uint64_t addr, void* dst, size_t size) {
+ if (!memory_) {
+ return 0;
}
- // The check above guarantees that addr + begin_ will not overflow.
- return memory_->Read(addr + begin_, dst, size);
+
+ return memory_->ReadPartially(addr, dst, size);
}
} // namespace unwindstack