blob: 1f4cd03ef78a7b1467604cd2579d87ad85cd6ba3 [file] [log] [blame]
Christopher Ferris723cf9b2017-01-19 20:08:48 -08001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <errno.h>
18#include <fcntl.h>
19#include <sys/mman.h>
20#include <sys/ptrace.h>
21#include <sys/stat.h>
22#include <sys/types.h>
23#include <sys/uio.h>
24#include <unistd.h>
25
26#include <algorithm>
27#include <memory>
28
29#include <android-base/unique_fd.h>
30
Christopher Ferrisd226a512017-07-14 10:37:19 -070031#include <unwindstack/Memory.h>
32
33#include "Check.h"
34
Josh Gao29c53782017-09-26 14:11:37 -070035static size_t ProcessVmRead(pid_t pid, void* dst, uint64_t remote_src, size_t len) {
36 struct iovec dst_iov = {
37 .iov_base = dst,
38 .iov_len = len,
39 };
40
41 // Split up the remote read across page boundaries.
42 // From the manpage:
43 // A partial read/write may result if one of the remote_iov elements points to an invalid
44 // memory region in the remote process.
45 //
46 // Partial transfers apply at the granularity of iovec elements. These system calls won't
47 // perform a partial transfer that splits a single iovec element.
48 constexpr size_t kMaxIovecs = 64;
49 struct iovec src_iovs[kMaxIovecs];
50 size_t iovecs_used = 0;
51
52 uint64_t cur = remote_src;
53 while (len > 0) {
54 if (iovecs_used == kMaxIovecs) {
55 errno = EINVAL;
56 return 0;
57 }
58
59 // struct iovec uses void* for iov_base.
60 if (cur >= UINTPTR_MAX) {
61 errno = EFAULT;
62 return 0;
63 }
64
65 src_iovs[iovecs_used].iov_base = reinterpret_cast<void*>(cur);
66
67 uintptr_t misalignment = cur & (getpagesize() - 1);
68 size_t iov_len = getpagesize() - misalignment;
69 iov_len = std::min(iov_len, len);
70
71 len -= iov_len;
72 if (__builtin_add_overflow(cur, iov_len, &cur)) {
73 errno = EFAULT;
74 return 0;
75 }
76
77 src_iovs[iovecs_used].iov_len = iov_len;
78 ++iovecs_used;
79 }
80
81 ssize_t rc = process_vm_readv(pid, &dst_iov, 1, src_iovs, iovecs_used, 0);
82 return rc == -1 ? 0 : rc;
83}
84
Christopher Ferrisd226a512017-07-14 10:37:19 -070085namespace unwindstack {
Christopher Ferris723cf9b2017-01-19 20:08:48 -080086
Josh Gaoef35aa52017-10-18 11:44:51 -070087bool Memory::ReadFully(uint64_t addr, void* dst, size_t size) {
Josh Gao29c53782017-09-26 14:11:37 -070088 size_t rc = ReadPartially(addr, dst, size);
89 return rc == size;
90}
91
Christopher Ferris723cf9b2017-01-19 20:08:48 -080092bool Memory::ReadString(uint64_t addr, std::string* string, uint64_t max_read) {
93 string->clear();
94 uint64_t bytes_read = 0;
95 while (bytes_read < max_read) {
96 uint8_t value;
Josh Gaoef35aa52017-10-18 11:44:51 -070097 if (!ReadFully(addr, &value, sizeof(value))) {
Christopher Ferris723cf9b2017-01-19 20:08:48 -080098 return false;
99 }
100 if (value == '\0') {
101 return true;
102 }
103 string->push_back(value);
104 addr++;
105 bytes_read++;
106 }
107 return false;
108}
109
Christopher Ferris5f118512017-09-01 11:17:16 -0700110std::shared_ptr<Memory> Memory::CreateProcessMemory(pid_t pid) {
111 if (pid == getpid()) {
112 return std::shared_ptr<Memory>(new MemoryLocal());
113 }
114 return std::shared_ptr<Memory>(new MemoryRemote(pid));
115}
116
Josh Gao29c53782017-09-26 14:11:37 -0700117size_t MemoryBuffer::ReadPartially(uint64_t addr, void* dst, size_t size) {
118 if (addr >= raw_.size()) {
119 return 0;
Christopher Ferris3958f802017-02-01 15:44:40 -0800120 }
Josh Gao29c53782017-09-26 14:11:37 -0700121
122 size_t bytes_left = raw_.size() - static_cast<size_t>(addr);
123 const unsigned char* actual_base = static_cast<const unsigned char*>(raw_.data()) + addr;
124 size_t actual_len = std::min(bytes_left, size);
125
126 memcpy(dst, actual_base, actual_len);
127 return actual_len;
Christopher Ferris3958f802017-02-01 15:44:40 -0800128}
129
130uint8_t* MemoryBuffer::GetPtr(size_t offset) {
131 if (offset < raw_.size()) {
132 return &raw_[offset];
133 }
134 return nullptr;
135}
136
Christopher Ferris723cf9b2017-01-19 20:08:48 -0800137MemoryFileAtOffset::~MemoryFileAtOffset() {
Christopher Ferris3958f802017-02-01 15:44:40 -0800138 Clear();
139}
140
141void MemoryFileAtOffset::Clear() {
Christopher Ferris723cf9b2017-01-19 20:08:48 -0800142 if (data_) {
143 munmap(&data_[-offset_], size_ + offset_);
144 data_ = nullptr;
145 }
146}
147
Christopher Ferris3958f802017-02-01 15:44:40 -0800148bool MemoryFileAtOffset::Init(const std::string& file, uint64_t offset, uint64_t size) {
149 // Clear out any previous data if it exists.
150 Clear();
151
Christopher Ferris723cf9b2017-01-19 20:08:48 -0800152 android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC)));
153 if (fd == -1) {
154 return false;
155 }
156 struct stat buf;
157 if (fstat(fd, &buf) == -1) {
158 return false;
159 }
160 if (offset >= static_cast<uint64_t>(buf.st_size)) {
161 return false;
162 }
163
164 offset_ = offset & (getpagesize() - 1);
165 uint64_t aligned_offset = offset & ~(getpagesize() - 1);
Christopher Ferrisf447c8e2017-04-03 12:39:47 -0700166 if (aligned_offset > static_cast<uint64_t>(buf.st_size) ||
167 offset > static_cast<uint64_t>(buf.st_size)) {
168 return false;
169 }
170
Christopher Ferris723cf9b2017-01-19 20:08:48 -0800171 size_ = buf.st_size - aligned_offset;
Christopher Ferrisf447c8e2017-04-03 12:39:47 -0700172 uint64_t max_size;
173 if (!__builtin_add_overflow(size, offset_, &max_size) && max_size < size_) {
Christopher Ferris3958f802017-02-01 15:44:40 -0800174 // Truncate the mapped size.
Christopher Ferrisf447c8e2017-04-03 12:39:47 -0700175 size_ = max_size;
Christopher Ferris3958f802017-02-01 15:44:40 -0800176 }
Christopher Ferris723cf9b2017-01-19 20:08:48 -0800177 void* map = mmap(nullptr, size_, PROT_READ, MAP_PRIVATE, fd, aligned_offset);
178 if (map == MAP_FAILED) {
179 return false;
180 }
181
182 data_ = &reinterpret_cast<uint8_t*>(map)[offset_];
183 size_ -= offset_;
184
185 return true;
186}
187
Josh Gao29c53782017-09-26 14:11:37 -0700188size_t MemoryFileAtOffset::ReadPartially(uint64_t addr, void* dst, size_t size) {
189 if (addr >= size_) {
190 return 0;
Christopher Ferris723cf9b2017-01-19 20:08:48 -0800191 }
Josh Gao29c53782017-09-26 14:11:37 -0700192
193 size_t bytes_left = size_ - static_cast<size_t>(addr);
194 const unsigned char* actual_base = static_cast<const unsigned char*>(data_) + addr;
195 size_t actual_len = std::min(bytes_left, size);
196
197 memcpy(dst, actual_base, actual_len);
198 return actual_len;
Christopher Ferris723cf9b2017-01-19 20:08:48 -0800199}
200
Josh Gao29c53782017-09-26 14:11:37 -0700201size_t MemoryRemote::ReadPartially(uint64_t addr, void* dst, size_t size) {
202 return ProcessVmRead(pid_, dst, addr, size);
Christopher Ferris723cf9b2017-01-19 20:08:48 -0800203}
204
Josh Gao29c53782017-09-26 14:11:37 -0700205size_t MemoryLocal::ReadPartially(uint64_t addr, void* dst, size_t size) {
206 return ProcessVmRead(getpid(), dst, addr, size);
Christopher Ferris723cf9b2017-01-19 20:08:48 -0800207}
208
Josh Gao29c53782017-09-26 14:11:37 -0700209MemoryRange::MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t length,
210 uint64_t offset)
211 : memory_(memory), begin_(begin), length_(length), offset_(offset) {}
212
213size_t MemoryRange::ReadPartially(uint64_t addr, void* dst, size_t size) {
214 if (addr < offset_) {
215 return 0;
Christopher Ferrisf447c8e2017-04-03 12:39:47 -0700216 }
217
Josh Gao29c53782017-09-26 14:11:37 -0700218 uint64_t read_offset = addr - offset_;
219 if (read_offset >= length_) {
220 return 0;
Christopher Ferris723cf9b2017-01-19 20:08:48 -0800221 }
Josh Gao29c53782017-09-26 14:11:37 -0700222
223 uint64_t read_length = std::min(static_cast<uint64_t>(size), length_ - read_offset);
224 uint64_t read_addr;
225 if (__builtin_add_overflow(read_offset, begin_, &read_addr)) {
226 return 0;
227 }
228
229 return memory_->ReadPartially(read_addr, dst, read_length);
Christopher Ferris723cf9b2017-01-19 20:08:48 -0800230}
231
232bool MemoryOffline::Init(const std::string& file, uint64_t offset) {
Josh Gao29c53782017-09-26 14:11:37 -0700233 auto memory_file = std::make_shared<MemoryFileAtOffset>();
234 if (!memory_file->Init(file, offset)) {
Christopher Ferris723cf9b2017-01-19 20:08:48 -0800235 return false;
236 }
Josh Gao29c53782017-09-26 14:11:37 -0700237
Christopher Ferris723cf9b2017-01-19 20:08:48 -0800238 // The first uint64_t value is the start of memory.
Josh Gao29c53782017-09-26 14:11:37 -0700239 uint64_t start;
Josh Gaoef35aa52017-10-18 11:44:51 -0700240 if (!memory_file->ReadFully(0, &start, sizeof(start))) {
Christopher Ferris723cf9b2017-01-19 20:08:48 -0800241 return false;
242 }
Josh Gao29c53782017-09-26 14:11:37 -0700243
244 uint64_t size = memory_file->Size();
245 if (__builtin_sub_overflow(size, sizeof(start), &size)) {
246 return false;
247 }
248
249 memory_ = std::make_unique<MemoryRange>(memory_file, sizeof(start), size, start);
Christopher Ferris723cf9b2017-01-19 20:08:48 -0800250 return true;
251}
252
Josh Gao29c53782017-09-26 14:11:37 -0700253size_t MemoryOffline::ReadPartially(uint64_t addr, void* dst, size_t size) {
254 if (!memory_) {
255 return 0;
Christopher Ferrisf447c8e2017-04-03 12:39:47 -0700256 }
257
Josh Gao29c53782017-09-26 14:11:37 -0700258 return memory_->ReadPartially(addr, dst, size);
Christopher Ferrisf447c8e2017-04-03 12:39:47 -0700259}
Christopher Ferrisd226a512017-07-14 10:37:19 -0700260
261} // namespace unwindstack