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 LOGE("val is nullptr or size(%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 LOGU("alignBytes: %zu, alignedAddr: %" PRIx64 "", alignBytes, static_cast<uint64_t>(alignedAddr));
72 if (!ReadMem(alignedAddr, &tmpVal)) {
73 return bytesRead;
74 }
75 uintptr_t valp = static_cast<uintptr_t>(tmpVal);
76 size_t copyBytes = std::min(static_cast<size_t>(alignBytes_) - alignBytes, size);
77 if (memcpy_s(val, copyBytes, reinterpret_cast<uint8_t*>(&valp) + alignBytes, copyBytes) != 0) {
78 return bytesRead;
79 }
80 tmpAddr += copyBytes;
81 val = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(val) + copyBytes);
82 size -= copyBytes;
83 bytesRead += copyBytes;
84 }
85 }
86 for (size_t i = 0; i < size / sizeof(uintptr_t); i++) {
87 if (!ReadMem(tmpAddr, &tmpVal) || memcpy_s(val, sizeof(uintptr_t), &tmpVal, sizeof(uintptr_t)) != 0) {
88 return bytesRead;
89 }
90 val = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(val) + sizeof(uintptr_t));
91 tmpAddr += sizeof(uintptr_t);
92 bytesRead += sizeof(uintptr_t);
93 }
94 size_t leftOver = size & (sizeof(uintptr_t) - 1);
95 if (leftOver) {
96 if (!ReadMem(tmpAddr, &tmpVal) || memcpy_s(val, leftOver, &tmpVal, leftOver) != 0) {
97 return bytesRead;
98 }
99 tmpAddr += leftOver;
100 bytesRead += leftOver;
101 }
102 if (incre) {
103 addr = tmpAddr;
104 }
105 return bytesRead;
106 }
107
ReadU8(uintptr_t & addr,uint8_t * val,bool incre)108 bool DfxMemory::ReadU8(uintptr_t& addr, uint8_t *val, bool incre)
109 {
110 if (Read(addr, val, sizeof(uint8_t), incre) == sizeof(uint8_t)) {
111 return true;
112 }
113 return false;
114 }
115
ReadU16(uintptr_t & addr,uint16_t * val,bool incre)116 bool DfxMemory::ReadU16(uintptr_t& addr, uint16_t *val, bool incre)
117 {
118 if (Read(addr, val, sizeof(uint16_t), incre) == sizeof(uint16_t)) {
119 return true;
120 }
121 return false;
122 }
123
ReadU32(uintptr_t & addr,uint32_t * val,bool incre)124 bool DfxMemory::ReadU32(uintptr_t& addr, uint32_t *val, bool incre)
125 {
126 if (Read(addr, val, sizeof(uint32_t), incre) == sizeof(uint32_t)) {
127 return true;
128 }
129 return false;
130 }
131
ReadU64(uintptr_t & addr,uint64_t * val,bool incre)132 bool DfxMemory::ReadU64(uintptr_t& addr, uint64_t *val, bool incre)
133 {
134 if (Read(addr, val, sizeof(uint64_t), incre) == sizeof(uint64_t)) {
135 return true;
136 }
137 return false;
138 }
139
ReadUptr(uintptr_t & addr,uintptr_t * val,bool incre)140 bool DfxMemory::ReadUptr(uintptr_t& addr, uintptr_t *val, bool incre)
141 {
142 if (Read(addr, val, sizeof(uintptr_t), incre) == sizeof(uintptr_t)) {
143 return true;
144 }
145 return false;
146 }
147
ReadString(uintptr_t & addr,std::string * str,size_t maxSize,bool incre)148 bool DfxMemory::ReadString(uintptr_t& addr, std::string* str, size_t maxSize, bool incre)
149 {
150 if (str == nullptr) {
151 return false;
152 }
153 char buf[NAME_BUF_LEN];
154 size_t size = 0;
155 uintptr_t ptr = addr;
156 for (size_t offset = 0; offset < maxSize; offset += size) {
157 size_t readn = std::min(sizeof(buf), maxSize - offset);
158 ptr = ptr + offset;
159 size = Read(ptr, buf, readn, false);
160 if (size == 0) {
161 return false;
162 }
163 size_t length = strnlen(buf, size);
164 if (length < size) {
165 if (offset == 0) {
166 str->assign(buf, length);
167 return true;
168 } else {
169 str->assign(offset + length, '\0');
170 Read(addr, (void*)str->data(), str->size(), false);
171 return true;
172 }
173 }
174 }
175 if (incre && str != nullptr) {
176 addr += str->size();
177 }
178 return false;
179 }
180
ReadPrel31(uintptr_t & addr,uintptr_t * val)181 bool DfxMemory::ReadPrel31(uintptr_t& addr, uintptr_t *val)
182 {
183 uintptr_t offset;
184 if (!ReadUptr(addr, &offset, false)) {
185 return false;
186 }
187 offset = static_cast<uintptr_t>(static_cast<int32_t>(offset << 1) >> 1);
188 *val = addr + offset;
189 return true;
190 }
191
ReadUleb128(uintptr_t & addr)192 uint64_t DfxMemory::ReadUleb128(uintptr_t& addr)
193 {
194 uint64_t val = 0;
195 uint64_t shift = 0;
196 uint8_t u8 = 0;
197 do {
198 if (!ReadU8(addr, &u8, true)) {
199 break;
200 }
201
202 val |= static_cast<uint64_t>(u8 & 0x7f) << shift;
203 shift += SEVEN_BIT_OFFSET;
204 } while (u8 & 0x80);
205 return val;
206 }
207
ReadSleb128(uintptr_t & addr)208 int64_t DfxMemory::ReadSleb128(uintptr_t& addr)
209 {
210 uint64_t val = 0;
211 uint64_t shift = 0;
212 uint8_t byte = 0;
213 do {
214 if (!ReadU8(addr, &byte, true)) {
215 break;
216 }
217
218 val |= static_cast<uint64_t>(byte & 0x7f) << shift;
219 shift += SEVEN_BIT_OFFSET;
220 } while (byte & 0x80);
221
222 if ((byte & 0x40) != 0) {
223 val |= (-1ULL) << shift;
224 }
225 return static_cast<int64_t>(val);
226 }
227
GetEncodedSize(uint8_t encoding)228 size_t DfxMemory::GetEncodedSize(uint8_t encoding)
229 {
230 switch (encoding & 0x0f) {
231 case DW_EH_PE_absptr:
232 return sizeof(uintptr_t);
233 case DW_EH_PE_udata1:
234 case DW_EH_PE_sdata1:
235 return 1;
236 case DW_EH_PE_udata2:
237 case DW_EH_PE_sdata2:
238 return TWO_BYTE_SIZE;
239 case DW_EH_PE_udata4:
240 case DW_EH_PE_sdata4:
241 return FOUR_BYTE_SIZE;
242 case DW_EH_PE_udata8:
243 case DW_EH_PE_sdata8:
244 return EIGHT_BYTE_SIZE;
245 case DW_EH_PE_uleb128:
246 case DW_EH_PE_sleb128:
247 default:
248 return 0;
249 }
250 }
251
ReadEncodedValue(uintptr_t & addr,uint8_t encoding)252 uintptr_t DfxMemory::ReadEncodedValue(uintptr_t& addr, uint8_t encoding)
253 {
254 uintptr_t startAddr = addr;
255 uintptr_t val = 0;
256 if (encoding == DW_EH_PE_omit) {
257 return val;
258 } else if (encoding == DW_EH_PE_aligned) {
259 if (__builtin_add_overflow(addr, sizeof(uintptr_t) - 1, &addr)) {
260 return val;
261 }
262 addr &= -sizeof(uintptr_t);
263 ReadUptr(addr, &val, true);
264 return val;
265 }
266
267 switch (encoding & DW_EH_PE_FORMAT_MASK) {
268 case DW_EH_PE_absptr:
269 ReadUptr(addr, &val, true);
270 return val;
271 case DW_EH_PE_uleb128:
272 val = static_cast<uintptr_t>(ReadUleb128(addr));
273 break;
274 case DW_EH_PE_sleb128:
275 val = static_cast<uintptr_t>(ReadSleb128(addr));
276 break;
277 case DW_EH_PE_udata1: {
278 uint8_t tmp = 0;
279 ReadU8(addr, &tmp, true);
280 val = static_cast<uintptr_t>(tmp);
281 }
282 break;
283 case DW_EH_PE_sdata1: {
284 int8_t tmp = 0;
285 ReadS8(addr, &tmp, true);
286 val = static_cast<uintptr_t>(tmp);
287 }
288 break;
289 case DW_EH_PE_udata2: {
290 uint16_t tmp = 0;
291 ReadU16(addr, &tmp, true);
292 val = static_cast<uintptr_t>(tmp);
293 }
294 break;
295 case DW_EH_PE_sdata2: {
296 int16_t tmp = 0;
297 ReadS16(addr, &tmp, true);
298 val = static_cast<uintptr_t>(tmp);
299 }
300 break;
301 case DW_EH_PE_udata4: {
302 uint32_t tmp = 0;
303 ReadU32(addr, &tmp, true);
304 val = static_cast<uintptr_t>(tmp);
305 }
306 break;
307 case DW_EH_PE_sdata4: {
308 int32_t tmp = 0;
309 ReadS32(addr, &tmp, true);
310 val = static_cast<uintptr_t>(tmp);
311 }
312 break;
313 case DW_EH_PE_udata8: {
314 uint64_t tmp = 0;
315 ReadU64(addr, &tmp, true);
316 val = static_cast<uintptr_t>(tmp);
317 }
318 break;
319 case DW_EH_PE_sdata8: {
320 int64_t tmp = 0;
321 ReadS64(addr, &tmp, true);
322 val = static_cast<uintptr_t>(tmp);
323 }
324 break;
325 default:
326 LOGW("Unexpected encoding format 0x%x", encoding & DW_EH_PE_FORMAT_MASK);
327 break;
328 }
329
330 switch (encoding & DW_EH_PE_APPL_MASK) {
331 case DW_EH_PE_pcrel:
332 val += startAddr;
333 break;
334 case DW_EH_PE_textrel:
335 LOGE("%s", "XXX For now we don't support text-rel values");
336 break;
337 case DW_EH_PE_datarel:
338 val += dataOffset_;
339 break;
340 case DW_EH_PE_funcrel:
341 val += funcOffset_;
342 break;
343 default:
344 break;
345 }
346
347 if (encoding & DW_EH_PE_indirect) {
348 uintptr_t indirectAddr = val;
349 ReadUptr(indirectAddr, &val, true);
350 }
351 return val;
352 }
353 #if is_ohos && !is_mingw
ReadProcMemByPid(const pid_t pid,const uint64_t addr,void * data,size_t size)354 size_t DfxMemory::ReadProcMemByPid(const pid_t pid, const uint64_t addr, void* data, size_t size)
355 {
356 constexpr size_t maxSize = 64;
357 struct iovec RemoteIovs[maxSize];
358
359 uint64_t cur = addr;
360 size_t totalRead = 0;
361 struct iovec dataIov = {
362 .iov_base = &reinterpret_cast<uint8_t*>(data)[totalRead],
363 .iov_len = size,
364 };
365 size_t iovecsIndex = 0;
366 while (size > 0) {
367 if (cur >= UINTPTR_MAX) {
368 return totalRead;
369 }
370 RemoteIovs[iovecsIndex].iov_base = reinterpret_cast<void*>(cur);
371 uintptr_t misalign = cur & static_cast<uint64_t>(getpagesize() - 1);
372 size_t iovLen = std::min(getpagesize() - misalign, size);
373
374 size -= iovLen;
375 if (__builtin_add_overflow(cur, iovLen, &cur)) {
376 return totalRead;
377 }
378
379 RemoteIovs[iovecsIndex].iov_len = iovLen;
380 ++iovecsIndex;
381 if (iovecsIndex >= maxSize || size <= 0) {
382 ssize_t count = process_vm_readv(pid, &dataIov, 1, RemoteIovs, iovecsIndex, 0);
383 if (count == -1) {
384 return totalRead;
385 }
386 totalRead += static_cast<size_t>(count);
387 if (iovecsIndex >= maxSize) {
388 iovecsIndex -= maxSize;
389 }
390 dataIov.iov_base = &reinterpret_cast<uint8_t*>(data)[totalRead];
391 dataIov.iov_len = size;
392 }
393 }
394
395 return totalRead;
396 }
397 #endif
398 } // namespace HiviewDFX
399 } // namespace OHOS
400