1 /*
2 * Copyright (c) 2023-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "dfx_memory.h"
17 #include <algorithm>
18 #include <securec.h>
19 #if is_ohos && !is_mingw
20 #include <sys/uio.h>
21 #endif
22 #include "dfx_define.h"
23 #include "dfx_errors.h"
24 #include "dfx_log.h"
25 #include "dwarf_define.h"
26
27 namespace OHOS {
28 namespace HiviewDFX {
29 namespace {
30 #undef LOG_DOMAIN
31 #undef LOG_TAG
32 #define LOG_DOMAIN 0xD002D11
33 #define LOG_TAG "DfxMemory"
34
35 static const int SEVEN_BIT_OFFSET = 7;
36 static const int TWO_BYTE_SIZE = 2;
37 static const int FOUR_BYTE_SIZE = 4;
38 static const int EIGHT_BYTE_SIZE = 8;
39 }
40
ReadReg(int regIdx, uintptr_t *val)41 bool DfxMemory::ReadReg(int regIdx, uintptr_t *val)
42 {
43 if (acc_ != nullptr && acc_->AccessReg(regIdx, val, ctx_) == UNW_ERROR_NONE) {
44 return true;
45 }
46 return false;
47 }
48
ReadMem(uintptr_t addr, uintptr_t *val)49 bool DfxMemory::ReadMem(uintptr_t addr, uintptr_t *val)
50 {
51 if (acc_ != nullptr && acc_->AccessMem(addr, val, ctx_) == UNW_ERROR_NONE) {
52 return true;
53 }
54 return false;
55 }
56
Read(uintptr_t& addr, void* val, size_t size, bool incre)57 size_t DfxMemory::Read(uintptr_t& addr, void* val, size_t size, bool incre)
58 {
59 uintptr_t tmpAddr = addr;
60 uint64_t maxSize;
61 if (val == nullptr || __builtin_add_overflow(tmpAddr, size, &maxSize)) {
62 DFXLOGE("val is nullptr or size(%{public}zu) overflow", size);
63 return 0;
64 }
65 size_t bytesRead = 0;
66 uintptr_t tmpVal;
67 if (alignAddr_ && (alignBytes_ != 0)) {
68 size_t alignBytes = tmpAddr & (static_cast<size_t>(alignBytes_) - 1);
69 if (alignBytes != 0) {
70 uintptr_t alignedAddr = tmpAddr & (~(static_cast<uintptr_t>(alignBytes_)) - 1);
71 DFXLOGU("alignBytes: %{public}zu, alignedAddr: %{public}" PRIx64 "", alignBytes,
72 static_cast<uint64_t>(alignedAddr));
73 if (!ReadMem(alignedAddr, &tmpVal)) {
74 return bytesRead;
75 }
76 uintptr_t valp = static_cast<uintptr_t>(tmpVal);
77 size_t copyBytes = std::min(static_cast<size_t>(alignBytes_) - alignBytes, size);
78 if (memcpy_s(val, copyBytes, reinterpret_cast<uint8_t*>(&valp) + alignBytes, copyBytes) != 0) {
79 return bytesRead;
80 }
81 tmpAddr += copyBytes;
82 val = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(val) + copyBytes);
83 size -= copyBytes;
84 bytesRead += copyBytes;
85 }
86 }
87 for (size_t i = 0; i < size / sizeof(uintptr_t); i++) {
88 if (!ReadMem(tmpAddr, &tmpVal) || memcpy_s(val, sizeof(uintptr_t), &tmpVal, sizeof(uintptr_t)) != 0) {
89 return bytesRead;
90 }
91 val = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(val) + sizeof(uintptr_t));
92 tmpAddr += sizeof(uintptr_t);
93 bytesRead += sizeof(uintptr_t);
94 }
95 size_t leftOver = size & (sizeof(uintptr_t) - 1);
96 if (leftOver) {
97 if (!ReadMem(tmpAddr, &tmpVal) || memcpy_s(val, leftOver, &tmpVal, leftOver) != 0) {
98 return bytesRead;
99 }
100 tmpAddr += leftOver;
101 bytesRead += leftOver;
102 }
103 if (incre) {
104 addr = tmpAddr;
105 }
106 return bytesRead;
107 }
108
ReadU8(uintptr_t& addr, uint8_t *val, bool incre)109 bool DfxMemory::ReadU8(uintptr_t& addr, uint8_t *val, bool incre)
110 {
111 if (Read(addr, val, sizeof(uint8_t), incre) == sizeof(uint8_t)) {
112 return true;
113 }
114 return false;
115 }
116
ReadU16(uintptr_t& addr, uint16_t *val, bool incre)117 bool DfxMemory::ReadU16(uintptr_t& addr, uint16_t *val, bool incre)
118 {
119 if (Read(addr, val, sizeof(uint16_t), incre) == sizeof(uint16_t)) {
120 return true;
121 }
122 return false;
123 }
124
ReadU32(uintptr_t& addr, uint32_t *val, bool incre)125 bool DfxMemory::ReadU32(uintptr_t& addr, uint32_t *val, bool incre)
126 {
127 if (Read(addr, val, sizeof(uint32_t), incre) == sizeof(uint32_t)) {
128 return true;
129 }
130 return false;
131 }
132
ReadU64(uintptr_t& addr, uint64_t *val, bool incre)133 bool DfxMemory::ReadU64(uintptr_t& addr, uint64_t *val, bool incre)
134 {
135 if (Read(addr, val, sizeof(uint64_t), incre) == sizeof(uint64_t)) {
136 return true;
137 }
138 return false;
139 }
140
ReadUptr(uintptr_t& addr, uintptr_t *val, bool incre)141 bool DfxMemory::ReadUptr(uintptr_t& addr, uintptr_t *val, bool incre)
142 {
143 if (Read(addr, val, sizeof(uintptr_t), incre) == sizeof(uintptr_t)) {
144 return true;
145 }
146 return false;
147 }
148
ReadString(uintptr_t& addr, std::string* str, size_t maxSize, bool incre)149 bool DfxMemory::ReadString(uintptr_t& addr, std::string* str, size_t maxSize, bool incre)
150 {
151 if (str == nullptr) {
152 return false;
153 }
154 char buf[NAME_BUF_LEN];
155 size_t size = 0;
156 uintptr_t ptr = addr;
157 for (size_t offset = 0; offset < maxSize; offset += size) {
158 size_t readn = std::min(sizeof(buf), maxSize - offset);
159 ptr = ptr + offset;
160 size = Read(ptr, buf, readn, false);
161 if (size == 0) {
162 return false;
163 }
164 size_t length = strnlen(buf, size);
165 if (length < size) {
166 if (offset == 0) {
167 str->assign(buf, length);
168 return true;
169 } else {
170 str->assign(offset + length, '\0');
171 Read(addr, (void*)str->data(), str->size(), false);
172 return true;
173 }
174 }
175 }
176 if (incre && str != nullptr) {
177 addr += str->size();
178 }
179 return false;
180 }
181
ReadPrel31(uintptr_t& addr, uintptr_t *val)182 bool DfxMemory::ReadPrel31(uintptr_t& addr, uintptr_t *val)
183 {
184 uintptr_t offset;
185 if (!ReadUptr(addr, &offset, false)) {
186 return false;
187 }
188 offset = static_cast<uintptr_t>(static_cast<int32_t>(offset << 1) >> 1);
189 *val = addr + offset;
190 return true;
191 }
192
ReadUleb128(uintptr_t& addr)193 uint64_t DfxMemory::ReadUleb128(uintptr_t& addr)
194 {
195 uint64_t val = 0;
196 uint64_t shift = 0;
197 uint8_t u8 = 0;
198 do {
199 if (!ReadU8(addr, &u8, true)) {
200 break;
201 }
202
203 val |= static_cast<uint64_t>(u8 & 0x7f) << shift;
204 shift += SEVEN_BIT_OFFSET;
205 } while (u8 & 0x80);
206 return val;
207 }
208
ReadSleb128(uintptr_t& addr)209 int64_t DfxMemory::ReadSleb128(uintptr_t& addr)
210 {
211 uint64_t val = 0;
212 uint64_t shift = 0;
213 uint8_t byte = 0;
214 do {
215 if (!ReadU8(addr, &byte, true)) {
216 break;
217 }
218
219 val |= static_cast<uint64_t>(byte & 0x7f) << shift;
220 shift += SEVEN_BIT_OFFSET;
221 } while (byte & 0x80);
222
223 if ((byte & 0x40) != 0) {
224 val |= (-1ULL) << shift;
225 }
226 return static_cast<int64_t>(val);
227 }
228
GetEncodedSize(uint8_t encoding)229 size_t DfxMemory::GetEncodedSize(uint8_t encoding)
230 {
231 switch (encoding & 0x0f) {
232 case DW_EH_PE_absptr:
233 return sizeof(uintptr_t);
234 case DW_EH_PE_udata1:
235 case DW_EH_PE_sdata1:
236 return 1;
237 case DW_EH_PE_udata2:
238 case DW_EH_PE_sdata2:
239 return TWO_BYTE_SIZE;
240 case DW_EH_PE_udata4:
241 case DW_EH_PE_sdata4:
242 return FOUR_BYTE_SIZE;
243 case DW_EH_PE_udata8:
244 case DW_EH_PE_sdata8:
245 return EIGHT_BYTE_SIZE;
246 case DW_EH_PE_uleb128:
247 case DW_EH_PE_sleb128:
248 default:
249 return 0;
250 }
251 }
252
ReadEncodedValue(uintptr_t& addr, uint8_t encoding)253 uintptr_t DfxMemory::ReadEncodedValue(uintptr_t& addr, uint8_t encoding)
254 {
255 uintptr_t startAddr = addr;
256 uintptr_t val = 0;
257 if (encoding == DW_EH_PE_omit) {
258 return val;
259 } else if (encoding == DW_EH_PE_aligned) {
260 if (__builtin_add_overflow(addr, sizeof(uintptr_t) - 1, &addr)) {
261 return val;
262 }
263 addr &= -sizeof(uintptr_t);
264 ReadUptr(addr, &val, true);
265 return val;
266 }
267
268 switch (encoding & DW_EH_PE_FORMAT_MASK) {
269 case DW_EH_PE_absptr:
270 ReadUptr(addr, &val, true);
271 return val;
272 case DW_EH_PE_uleb128:
273 val = static_cast<uintptr_t>(ReadUleb128(addr));
274 break;
275 case DW_EH_PE_sleb128:
276 val = static_cast<uintptr_t>(ReadSleb128(addr));
277 break;
278 case DW_EH_PE_udata1: {
279 uint8_t tmp = 0;
280 ReadU8(addr, &tmp, true);
281 val = static_cast<uintptr_t>(tmp);
282 }
283 break;
284 case DW_EH_PE_sdata1: {
285 int8_t tmp = 0;
286 ReadS8(addr, &tmp, true);
287 val = static_cast<uintptr_t>(tmp);
288 }
289 break;
290 case DW_EH_PE_udata2: {
291 uint16_t tmp = 0;
292 ReadU16(addr, &tmp, true);
293 val = static_cast<uintptr_t>(tmp);
294 }
295 break;
296 case DW_EH_PE_sdata2: {
297 int16_t tmp = 0;
298 ReadS16(addr, &tmp, true);
299 val = static_cast<uintptr_t>(tmp);
300 }
301 break;
302 case DW_EH_PE_udata4: {
303 uint32_t tmp = 0;
304 ReadU32(addr, &tmp, true);
305 val = static_cast<uintptr_t>(tmp);
306 }
307 break;
308 case DW_EH_PE_sdata4: {
309 int32_t tmp = 0;
310 ReadS32(addr, &tmp, true);
311 val = static_cast<uintptr_t>(tmp);
312 }
313 break;
314 case DW_EH_PE_udata8: {
315 uint64_t tmp = 0;
316 ReadU64(addr, &tmp, true);
317 val = static_cast<uintptr_t>(tmp);
318 }
319 break;
320 case DW_EH_PE_sdata8: {
321 int64_t tmp = 0;
322 ReadS64(addr, &tmp, true);
323 val = static_cast<uintptr_t>(tmp);
324 }
325 break;
326 default:
327 DFXLOGW("Unexpected encoding format 0x%{public}x", encoding & DW_EH_PE_FORMAT_MASK);
328 break;
329 }
330
331 switch (encoding & DW_EH_PE_APPL_MASK) {
332 case DW_EH_PE_pcrel:
333 val += startAddr;
334 break;
335 case DW_EH_PE_textrel:
336 DFXLOGE("XXX For now we don't support text-rel values");
337 break;
338 case DW_EH_PE_datarel:
339 val += dataOffset_;
340 break;
341 case DW_EH_PE_funcrel:
342 val += funcOffset_;
343 break;
344 default:
345 break;
346 }
347
348 if (encoding & DW_EH_PE_indirect) {
349 uintptr_t indirectAddr = val;
350 ReadUptr(indirectAddr, &val, true);
351 }
352 return val;
353 }
354 #if is_ohos && !is_mingw
ReadProcMemByPid(const pid_t pid, const uint64_t addr, void* data, size_t size)355 size_t DfxMemory::ReadProcMemByPid(const pid_t pid, const uint64_t addr, void* data, size_t size)
356 {
357 constexpr size_t maxSize = 64;
358 struct iovec RemoteIovs[maxSize];
359
360 uint64_t cur = addr;
361 size_t totalRead = 0;
362 struct iovec dataIov = {
363 .iov_base = &reinterpret_cast<uint8_t*>(data)[totalRead],
364 .iov_len = size,
365 };
366 size_t iovecsIndex = 0;
367 while (size > 0) {
368 if (cur >= UINTPTR_MAX) {
369 return totalRead;
370 }
371 RemoteIovs[iovecsIndex].iov_base = reinterpret_cast<void*>(cur);
372 uintptr_t misalign = cur & static_cast<uint64_t>(getpagesize() - 1);
373 size_t iovLen = std::min(getpagesize() - misalign, size);
374
375 size -= iovLen;
376 if (__builtin_add_overflow(cur, iovLen, &cur)) {
377 return totalRead;
378 }
379
380 RemoteIovs[iovecsIndex].iov_len = iovLen;
381 ++iovecsIndex;
382 if (iovecsIndex >= maxSize || size <= 0) {
383 ssize_t count = process_vm_readv(pid, &dataIov, 1, RemoteIovs, iovecsIndex, 0);
384 if (count == -1) {
385 return totalRead;
386 }
387 totalRead += static_cast<size_t>(count);
388 if (iovecsIndex >= maxSize) {
389 iovecsIndex -= maxSize;
390 }
391 dataIov.iov_base = &reinterpret_cast<uint8_t*>(data)[totalRead];
392 dataIov.iov_len = size;
393 }
394 }
395
396 return totalRead;
397 }
398 #endif
399 } // namespace HiviewDFX
400 } // namespace OHOS
401