1/*
2 * Copyright (c) 2021-2023 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#if defined(__arm__)
17#include "dfx_regs.h"
18
19#include <securec.h>
20#include "dfx_define.h"
21#include "dfx_log.h"
22#include "dfx_elf.h"
23#include "string_printf.h"
24
25#define ARM_NR_sigreturn 119
26#define ARM_NR_rt_sigreturn 173
27#define ARM_NR_OABI_SYSCALL_BASE 0x900000
28
29/* ARM EABI sigreturn (the syscall number is loaded into r7) */
30#define MOV_R7_SIGRETURN (0xe3a07000UL | ARM_NR_sigreturn)
31#define MOV_R7_RT_SIGRETURN (0xe3a07000UL | ARM_NR_rt_sigreturn)
32
33/* ARM OABI sigreturn (using SWI) */
34#define ARM_SIGRETURN (0xef000000UL | ARM_NR_sigreturn | ARM_NR_OABI_SYSCALL_BASE)
35#define ARM_RT_SIGRETURN (0xef000000UL | ARM_NR_rt_sigreturn | ARM_NR_OABI_SYSCALL_BASE)
36
37/* Thumb sigreturn (two insns, syscall number is loaded into r7) */
38#define THUMB_SIGRETURN (0xdf00UL << 16 | 0x2700 | ARM_NR_sigreturn)
39#define THUMB_RT_SIGRETURN (0xdf00UL << 16 | 0x2700 | ARM_NR_rt_sigreturn)
40
41/* Thumb2 sigreturn (mov.w r7, $SYS_ify(rt_sigreturn/sigreturn)) */
42#define THUMB2_SIGRETURN (((0x0700 | ARM_NR_sigreturn) << 16) | 0xf04f)
43#define THUMB2_RT_SIGRETURN (((0x0700 | ARM_NR_rt_sigreturn) << 16) | 0xf04f)
44
45namespace OHOS {
46namespace HiviewDFX {
47void DfxRegsArm::SetFromUcontext(const ucontext_t& context)
48{
49    if (regsData_.size() < REG_LAST) {
50        return;
51    }
52    regsData_[REG_ARM_R0] = static_cast<uintptr_t>(context.uc_mcontext.arm_r0);
53    regsData_[REG_ARM_R1] = static_cast<uintptr_t>(context.uc_mcontext.arm_r1);
54    regsData_[REG_ARM_R2] = static_cast<uintptr_t>(context.uc_mcontext.arm_r2);
55    regsData_[REG_ARM_R3] = static_cast<uintptr_t>(context.uc_mcontext.arm_r3);
56    regsData_[REG_ARM_R4] = static_cast<uintptr_t>(context.uc_mcontext.arm_r4);
57    regsData_[REG_ARM_R5] = static_cast<uintptr_t>(context.uc_mcontext.arm_r5);
58    regsData_[REG_ARM_R6] = static_cast<uintptr_t>(context.uc_mcontext.arm_r6);
59    regsData_[REG_ARM_R7] = static_cast<uintptr_t>(context.uc_mcontext.arm_r7);
60    regsData_[REG_ARM_R8] = static_cast<uintptr_t>(context.uc_mcontext.arm_r8);
61    regsData_[REG_ARM_R9] = static_cast<uintptr_t>(context.uc_mcontext.arm_r9);
62    regsData_[REG_ARM_R10] = static_cast<uintptr_t>(context.uc_mcontext.arm_r10);
63    regsData_[REG_ARM_R11] = static_cast<uintptr_t>(context.uc_mcontext.arm_fp);
64    regsData_[REG_ARM_R12] = static_cast<uintptr_t>(context.uc_mcontext.arm_ip);
65    regsData_[REG_ARM_R13] = static_cast<uintptr_t>(context.uc_mcontext.arm_sp);
66    regsData_[REG_ARM_R14] = static_cast<uintptr_t>(context.uc_mcontext.arm_lr);
67    regsData_[REG_ARM_R15] = static_cast<uintptr_t>(context.uc_mcontext.arm_pc);
68}
69
70void DfxRegsArm::SetFromFpMiniRegs(const uintptr_t* regs, const size_t size)
71{
72    if (size < FP_MINI_REGS_SIZE) {
73        return;
74    }
75    regsData_[REG_ARM_R7] = regs[0]; // 0: R7 offset
76    regsData_[REG_ARM_R11] = regs[1]; // 1: R11 offset
77    regsData_[REG_SP] = regs[2]; // 2 : sp offset
78    regsData_[REG_PC] = regs[3]; // 3 : pc offset
79}
80
81void DfxRegsArm::SetFromQutMiniRegs(const uintptr_t* regs, const size_t size)
82{
83    if (size < QUT_MINI_REGS_SIZE) {
84        return;
85    }
86    regsData_[REG_ARM_R4] = regs[0]; // 0 : r4 offset
87    regsData_[REG_ARM_R7] = regs[1]; // 1 : r7 offset
88    regsData_[REG_ARM_R10] = regs[2]; // 2 : r10 offset
89    regsData_[REG_ARM_R11] = regs[3]; // 3 : r11 offset
90    regsData_[REG_SP] = regs[4];  // 4 : sp offset
91    regsData_[REG_PC] = regs[5];  // 5 : pc offset
92    regsData_[REG_LR] = regs[6];  // 6 : lr offset
93}
94
95bool DfxRegsArm::SetPcFromReturnAddress(MAYBE_UNUSED std::shared_ptr<DfxMemory> memory)
96{
97    uintptr_t lr = regsData_[REG_LR];
98    if (regsData_[REG_PC] == lr) {
99        return false;
100    }
101    regsData_[REG_PC] = lr;
102    return true;
103}
104
105std::string DfxRegsArm::PrintRegs() const
106{
107    char buf[REGS_PRINT_LEN] = {0};
108    auto regs = GetRegsData();
109
110    BufferPrintf(buf, sizeof(buf), "r0:%08x r1:%08x r2:%08x r3:%08x\n", \
111        regs[REG_ARM_R0], regs[REG_ARM_R1], regs[REG_ARM_R2], regs[REG_ARM_R3]);
112
113    BufferPrintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "r4:%08x r5:%08x r6:%08x r7:%08x\n", \
114        regs[REG_ARM_R4], regs[REG_ARM_R5], regs[REG_ARM_R6], regs[REG_ARM_R7]);
115
116    BufferPrintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "r8:%08x r9:%08x r10:%08x\n", \
117        regs[REG_ARM_R8], regs[REG_ARM_R9], regs[REG_ARM_R10]);
118
119    BufferPrintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "fp:%08x ip:%08x sp:%08x lr:%08x pc:%08x\n", \
120        regs[REG_ARM_R11], regs[REG_ARM_R12], regs[REG_ARM_R13], regs[REG_ARM_R14], regs[REG_ARM_R15]);
121
122    std::string regString = StringPrintf("Registers:\n%s", buf);
123    return regString;
124}
125
126bool DfxRegsArm::StepIfSignalFrame(uintptr_t pc, std::shared_ptr<DfxMemory> memory)
127{
128    // The least bit denotes thumb/arm mode. Do not read there.
129    pc = pc & ~0x1;
130
131    uint32_t data;
132    if (!memory->ReadU32(pc, &data, false)) {
133        return false;
134    }
135    DFXLOGU("data: %{public}x", data);
136
137    uintptr_t scAddr = 0;
138    if (data == MOV_R7_SIGRETURN || data == ARM_SIGRETURN
139        || data == THUMB_SIGRETURN || data == THUMB2_SIGRETURN) {
140        uintptr_t spAddr = regsData_[REG_SP];
141        // non-RT sigreturn call.
142        // __restore:
143        //
144        // Form 1 (arm):
145        // 0x77 0x70              mov r7, #0x77
146        // 0xa0 0xe3              svc 0x00000000
147        //
148        // Form 2 (arm):
149        // 0x77 0x00 0x90 0xef    svc 0x00900077
150        //
151        // Form 3 (thumb):
152        // 0x77 0x27              movs r7, #77
153        // 0x00 0xdf              svc 0
154        if (!memory->ReadU32(spAddr, &data, false)) {
155            return false;
156        }
157        if (data == 0x5ac3c35a) {
158            // SP + uc_mcontext offset + r0 offset.
159            scAddr = spAddr + 0x14 + 0xc;
160        } else {
161            // SP + r0 offset
162            scAddr = spAddr + 0xc;
163        }
164    } else if (data == MOV_R7_RT_SIGRETURN || data == ARM_RT_SIGRETURN
165        || data == THUMB_RT_SIGRETURN || data == THUMB2_RT_SIGRETURN) {
166        uintptr_t spAddr = regsData_[REG_SP];
167        // RT sigreturn call.
168        // __restore_rt:
169        //
170        // Form 1 (arm):
171        // 0xad 0x70      mov r7, #0xad
172        // 0xa0 0xe3      svc 0x00000000
173        //
174        // Form 2 (arm):
175        // 0xad 0x00 0x90 0xef    svc 0x009000ad
176        //
177        // Form 3 (thumb):
178        // 0xad 0x27              movs r7, #ad
179        // 0x00 0xdf              svc 0
180        if (!memory->ReadU32(spAddr, &data, false)) {
181            return false;
182        }
183        if (data == spAddr + 8) { // 8 : eight bytes offset
184            // SP + 8 + sizeof(siginfo_t) + uc_mcontext_offset + r0 offset
185            scAddr = spAddr + 8 + sizeof(siginfo_t) + 0x14 + 0xc; // 8 : eight bytes offset
186        } else {
187            // SP + sizeof(siginfo_t) + uc_mcontext_offset + r0 offset
188            scAddr = spAddr + sizeof(siginfo_t) + 0x14 + 0xc;
189        }
190    }
191    if (scAddr == 0) {
192        return false;
193    }
194    DFXLOGU("scAddr: %{public}x", scAddr);
195    memory->Read(scAddr, regsData_.data(), sizeof(uint32_t) * REG_LAST, false);
196    return true;
197}
198} // namespace HiviewDFX
199} // namespace OHOS
200#endif
201