1/*
2 * Copyright (c) 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#include "fp_unwinder.h"
16
17#include <cinttypes>
18#include <csignal>
19#include <cstdio>
20#include <securec.h>
21#include <sys/stat.h>
22#include <sys/syscall.h>
23#include <unistd.h>
24#include <fcntl.h>
25#include <pthread.h>
26#include "dfx_log.h"
27#include "stack_util.h"
28
29namespace OHOS {
30namespace HiviewDFX {
31static int32_t g_validPipe[PIPE_NUM_SZ] = {-1, -1};
32constexpr uintptr_t MAX_UNWIND_ADDR_RANGE = 16 * 1024;
33int32_t FpUnwinder::Unwind(uintptr_t* pcs, int32_t sz, int32_t skipFrameNum)
34{
35    GetFpPcRegs(pcs);
36    int32_t index = 0;
37    uintptr_t firstFp = pcs[1];
38    uintptr_t fp = firstFp;
39    uintptr_t stackPtr = reinterpret_cast<uintptr_t>(__builtin_frame_address(0));
40    uintptr_t stackBottom = 0;
41    uintptr_t stackTop = 0;
42    uint32_t realSz = 0;
43    if (getpid() == gettid()) {
44        GetMainStackRange(stackBottom, stackTop);
45    } else {
46        GetSelfStackRange(stackBottom, stackTop);
47        if (!(stackPtr >= stackBottom && stackPtr < stackTop)) {
48            GetSigAltStackRange(stackBottom, stackTop);
49            if (stackPtr < stackBottom || stackPtr >= stackTop) {
50                return realSz;
51            }
52        }
53    }
54    while ((index < sz - 1) && (fp - firstFp < MAX_UNWIND_ADDR_RANGE)) {
55        if (fp < stackBottom || fp >= stackTop - sizeof(uintptr_t)) {
56            break;
57        }
58        if ((++index) >= skipFrameNum) {
59            uintptr_t pc = *reinterpret_cast<uintptr_t*>(fp + sizeof(uintptr_t));
60            pcs[index - skipFrameNum] = pc > 0x4 ? pc - 0x4 : pc; // adjust pc in Arm64 architecture
61            realSz = static_cast<uint32_t>(index - skipFrameNum + 1);
62        }
63        uintptr_t nextFp = *reinterpret_cast<uintptr_t*>(fp);
64        if (nextFp <= fp) {
65            break;
66        }
67        fp = nextFp;
68    }
69    return realSz;
70}
71
72int32_t FpUnwinder::UnwindFallback(uintptr_t* pcs, int32_t sz, int32_t skipFrameNum)
73{
74    if (pipe2(g_validPipe, O_CLOEXEC | O_NONBLOCK) != 0) {
75        DFXLOGE("Failed to init pipe, errno(%{public}d)", errno);
76        return 0;
77    }
78    uintptr_t firstFp = pcs[1];
79    uintptr_t fp = firstFp;
80    int32_t index = 0;
81    int32_t realSz = 0;
82    while ((index < sz - 1) && (fp - firstFp < MAX_UNWIND_ADDR_RANGE)) {
83        uintptr_t addr = fp + sizeof(uintptr_t);
84        if (!ReadUintptrSafe(addr, pcs[++index])) {
85            break;
86        }
87        if ((++index) >= skipFrameNum) {
88            pcs[index - skipFrameNum] = 0;
89            realSz = index - skipFrameNum + 1;
90        }
91        uintptr_t prevFp = fp;
92        if (!ReadUintptrSafe(prevFp, fp)) {
93            break;
94        }
95        if (fp <= prevFp) {
96            break;
97        }
98    }
99    close(g_validPipe[PIPE_WRITE]);
100    close(g_validPipe[PIPE_READ]);
101    return realSz;
102}
103
104NO_SANITIZE bool FpUnwinder::ReadUintptrSafe(uintptr_t addr, uintptr_t& value)
105{
106    if (OHOS_TEMP_FAILURE_RETRY(syscall(SYS_write, g_validPipe[PIPE_WRITE], addr, sizeof(uintptr_t))) == -1) {
107        DFXLOGE("Failed to write addr to pipe, it is a invalid addr");
108        return false;
109    }
110    value = *reinterpret_cast<uintptr_t *>(addr);
111    return true;
112}
113}
114}
115