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_instructions.h"
17
18#include <algorithm>
19#include <cstdio>
20#include <cstdlib>
21
22#include "dfx_define.h"
23#include "dfx_errors.h"
24#include "dfx_log.h"
25#include "dfx_instr_statistic.h"
26#include "dfx_regs_qut.h"
27#include "dwarf_op.h"
28
29namespace OHOS {
30namespace HiviewDFX {
31namespace {
32#undef LOG_DOMAIN
33#undef LOG_TAG
34#define LOG_DOMAIN 0xD002D11
35#define LOG_TAG "DfxInstructions"
36}
37
38bool DfxInstructions::Flush(DfxRegs& regs, std::shared_ptr<DfxMemory> memory, uintptr_t cfa, RegLoc loc, uintptr_t& val)
39{
40    if (memory == nullptr) {
41        return false;
42    }
43    uintptr_t location;
44    switch (loc.type) {
45        case REG_LOC_VAL_OFFSET:
46            val = cfa + static_cast<uintptr_t>(loc.val);
47            break;
48        case REG_LOC_MEM_OFFSET:
49            location = cfa + static_cast<uintptr_t>(loc.val);
50            memory->ReadUptr(location, &val);
51            break;
52        case REG_LOC_REGISTER:
53            location = static_cast<uintptr_t>(loc.val);
54            if (location >= regs.RegsSize()) {
55                DFXLOGE("Illegal register location");
56                return false;
57            }
58            val = regs[location];
59            break;
60        case REG_LOC_MEM_EXPRESSION: {
61            DwarfOp<uintptr_t> dwarfOp(memory);
62            location = dwarfOp.Eval(regs, cfa, loc.val);
63            memory->ReadUptr(location, &val);
64            break;
65        }
66        case REG_LOC_VAL_EXPRESSION: {
67            DwarfOp<uintptr_t> dwarfOp(memory);
68            val = dwarfOp.Eval(regs, cfa, loc.val);
69            break;
70        }
71        default:
72            DFXLOGE("Failed to save register.");
73            return false;
74    }
75    return true;
76}
77
78bool DfxInstructions::Apply(std::shared_ptr<DfxMemory> memory, DfxRegs& regs, RegLocState& rsState, uint16_t& errCode)
79{
80    uintptr_t cfa = 0;
81    RegLoc cfaLoc;
82    if (rsState.cfaReg != 0) {
83        cfa = regs[rsState.cfaReg] + static_cast<uint32_t>(rsState.cfaRegOffset);
84    } else if (rsState.cfaExprPtr != 0) {
85        cfaLoc.type = REG_LOC_VAL_EXPRESSION;
86        cfaLoc.val = static_cast<intptr_t>(rsState.cfaExprPtr);
87        if (!Flush(regs, memory, 0, cfaLoc, cfa)) {
88            DFXLOGE("Failed to update cfa.");
89            return false;
90        }
91    } else {
92        DFXLOGE("no cfa info exist?");
93        INSTR_STATISTIC(UnsupportedDefCfa, rsState.cfaReg, UNW_ERROR_NOT_SUPPORT);
94        return false;
95    }
96    DFXLOGU("Update cfa : %{public}" PRIx64 "", (uint64_t)cfa);
97
98    for (size_t i = 0; i < rsState.locs.size(); i++) {
99        if (rsState.locs[i].type != REG_LOC_UNUSED) {
100            size_t reg = DfxRegsQut::GetQutRegs()[i];
101            if (Flush(regs, memory, cfa, rsState.locs[i], regs[reg])) {
102                DFXLOGU("Update reg[%{public}zu] : %{public}" PRIx64 "", reg, (uint64_t)regs[reg]);
103            }
104        }
105    }
106
107    regs.SetSp(cfa);
108
109    if (rsState.returnAddressUndefined) {
110        regs.SetPc(0);
111        errCode = UNW_ERROR_RETURN_ADDRESS_UNDEFINED;
112        return false;
113    } else {
114        if (rsState.returnAddressRegister >= REG_EH && rsState.returnAddressRegister < REG_LAST) {
115            DFXLOGU("returnAddressRegister: %{public}d", (int)rsState.returnAddressRegister);
116            regs.SetPc(regs[rsState.returnAddressRegister]);
117        } else {
118            DFXLOGE("returnAddressRegister: %{public}d error", (int)rsState.returnAddressRegister);
119            errCode = UNW_ERROR_ILLEGAL_VALUE;
120            return false;
121        }
122    }
123    if (rsState.returnAddressSame) {
124        errCode = UNW_ERROR_RETURN_ADDRESS_SAME;
125        return false;
126    }
127
128    return true;
129}
130} // namespace HiviewDFX
131} // namespace OHOS
132