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
27namespace OHOS {
28namespace HiviewDFX {
29namespace {
30#undef LOG_DOMAIN
31#undef LOG_TAG
32#define LOG_DOMAIN 0xD002D11
33#define LOG_TAG "DfxMemory"
34
35static const int SEVEN_BIT_OFFSET = 7;
36static const int TWO_BYTE_SIZE = 2;
37static const int FOUR_BYTE_SIZE = 4;
38static const int EIGHT_BYTE_SIZE = 8;
39}
40
41bool 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
49bool 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
57size_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
109bool 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
117bool 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
125bool 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
133bool 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
141bool 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
149bool 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
182bool 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
193uint64_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
209int64_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
229size_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
253uintptr_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
355size_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