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