1//===-- TestPPC64InstEmulation.cpp ----------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "gtest/gtest.h"
10
11#include <vector>
12
13#include "Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h"
14
15#include "lldb/Core/Address.h"
16#include "lldb/Core/AddressRange.h"
17#include "lldb/Symbol/UnwindPlan.h"
18#include "lldb/Target/UnwindAssembly.h"
19#include "lldb/Utility/ArchSpec.h"
20
21#include "Plugins/Disassembler/LLVMC/DisassemblerLLVMC.h"
22#include "Plugins/Instruction/PPC64/EmulateInstructionPPC64.h"
23#include "Plugins/Process/Utility/lldb-ppc64le-register-enums.h"
24#include "llvm/Support/TargetSelect.h"
25
26using namespace lldb;
27using namespace lldb_private;
28
29class TestPPC64InstEmulation : public testing::Test {
30public:
31 static void SetUpTestCase();
32 static void TearDownTestCase();
33
34 // virtual void SetUp() override { }
35 // virtual void TearDown() override { }
36
37protected:
38};
39
40void TestPPC64InstEmulation::SetUpTestCase() {
41 llvm::InitializeAllTargets();
42 llvm::InitializeAllAsmPrinters();
43 llvm::InitializeAllTargetMCs();
44 llvm::InitializeAllDisassemblers();
45 DisassemblerLLVMC::Initialize();
46 EmulateInstructionPPC64::Initialize();
47}
48
49void TestPPC64InstEmulation::TearDownTestCase() {
50 DisassemblerLLVMC::Terminate();
51 EmulateInstructionPPC64::Terminate();
52}
53
54TEST_F(TestPPC64InstEmulation, TestSimpleFunction) {
55 ArchSpec arch("powerpc64le-linux-gnu");
56 std::unique_ptr<UnwindAssemblyInstEmulation> engine(
57 static_cast<UnwindAssemblyInstEmulation *>(
58 UnwindAssemblyInstEmulation::CreateInstance(arch)));
59 ASSERT_NE(nullptr, engine);
60
61 UnwindPlan::RowSP row_sp;
62 AddressRange sample_range;
63 UnwindPlan unwind_plan(eRegisterKindLLDB);
64 UnwindPlan::Row::RegisterLocation regloc;
65
66 // prologue and epilogue of:
67 // int main() {
68 // int i = test();
69 // return i;
70 // }
71 //
72 // compiled with clang -O0 -g
73 uint8_t data[] = {
74 // prologue
75 0x02, 0x10, 0x40, 0x3c, // 0: lis r2, 4098
76 0x00, 0x7f, 0x42, 0x38, // 4: addi r2, r2, 32512
77 0xa6, 0x02, 0x08, 0x7c, // 8: mflr r0
78 0xf8, 0xff, 0xe1, 0xfb, // 12: std r31, -8(r1)
79 0x10, 0x00, 0x01, 0xf8, // 16: std r0, 16(r1)
80 0x91, 0xff, 0x21, 0xf8, // 20: stdu r1, -112(r1)
81 0x78, 0x0b, 0x3f, 0x7c, // 24: mr r31, r1
82 0x00, 0x00, 0x60, 0x38, // 28: li r3, 0
83 0x64, 0x00, 0x7f, 0x90, // 32: stw r3, 100(r31)
84
85 // epilogue
86 0x70, 0x00, 0x21, 0x38, // 36: addi r1, r1, 112
87 0x10, 0x00, 0x01, 0xe8, // 40: ld r0, 16(r1)
88 0xf8, 0xff, 0xe1, 0xeb, // 44: ld r31, -8(r1)
89 0xa6, 0x03, 0x08, 0x7c, // 48: mtlr r0
90 0x20, 0x00, 0x80, 0x4e // 52: blr
91 };
92
93 sample_range = AddressRange(0x1000, sizeof(data));
94
95 EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
96 sample_range, data, sizeof(data), unwind_plan));
97
98 // 0: CFA=sp+0
99 row_sp = unwind_plan.GetRowForFunctionOffset(offset: 0);
100 EXPECT_EQ(0ull, row_sp->GetOffset());
101 EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_r1_ppc64le);
102 EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
103 EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
104
105 // 1: CFA=sp+0 => fp=[CFA-8]
106 row_sp = unwind_plan.GetRowForFunctionOffset(offset: 16);
107 EXPECT_EQ(16ull, row_sp->GetOffset());
108 EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_r1_ppc64le);
109 EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
110 EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
111
112 EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_r31_ppc64le, regloc));
113 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
114 EXPECT_EQ(-8, regloc.GetOffset());
115
116 // 2: CFA=sp+0 => fp=[CFA-8] lr=[CFA+16]
117 row_sp = unwind_plan.GetRowForFunctionOffset(offset: 20);
118 EXPECT_EQ(20ull, row_sp->GetOffset());
119 EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_r1_ppc64le);
120 EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
121 EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
122
123 EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_ppc64le, regloc));
124 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
125 EXPECT_EQ(16, regloc.GetOffset());
126
127 // 3: CFA=sp+112 => fp=[CFA-8] lr=[CFA+16]
128 row_sp = unwind_plan.GetRowForFunctionOffset(offset: 24);
129 EXPECT_EQ(24ull, row_sp->GetOffset());
130 EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_r1_ppc64le);
131 EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
132 EXPECT_EQ(112, row_sp->GetCFAValue().GetOffset());
133
134 EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_r31_ppc64le, regloc));
135 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
136 EXPECT_EQ(-8, regloc.GetOffset());
137
138 EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_ppc64le, regloc));
139 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
140 EXPECT_EQ(16, regloc.GetOffset());
141
142 // 4: CFA=r31+112 => fp=[CFA-8] lr=[CFA+16]
143 row_sp = unwind_plan.GetRowForFunctionOffset(offset: 28);
144 EXPECT_EQ(28ull, row_sp->GetOffset());
145 EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_r31_ppc64le);
146 EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
147 EXPECT_EQ(112, row_sp->GetCFAValue().GetOffset());
148
149 EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_r31_ppc64le, regloc));
150 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
151 EXPECT_EQ(-8, regloc.GetOffset());
152
153 EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_ppc64le, regloc));
154 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
155 EXPECT_EQ(16, regloc.GetOffset());
156
157 // 5: CFA=sp+0 => fp=[CFA-8] lr=[CFA+16]
158 row_sp = unwind_plan.GetRowForFunctionOffset(offset: 40);
159 EXPECT_EQ(40ull, row_sp->GetOffset());
160 EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_r1_ppc64le);
161 EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
162 EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
163
164 EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_r31_ppc64le, regloc));
165 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
166 EXPECT_EQ(-8, regloc.GetOffset());
167
168 EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_ppc64le, regloc));
169 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
170 EXPECT_EQ(16, regloc.GetOffset());
171}
172
173TEST_F(TestPPC64InstEmulation, TestMediumFunction) {
174 ArchSpec arch("powerpc64le-linux-gnu");
175 std::unique_ptr<UnwindAssemblyInstEmulation> engine(
176 static_cast<UnwindAssemblyInstEmulation *>(
177 UnwindAssemblyInstEmulation::CreateInstance(arch)));
178 ASSERT_NE(nullptr, engine);
179
180 UnwindPlan::RowSP row_sp;
181 AddressRange sample_range;
182 UnwindPlan unwind_plan(eRegisterKindLLDB);
183 UnwindPlan::Row::RegisterLocation regloc;
184
185 // prologue and epilogue of main() (call-func.c),
186 // with several calls and stack variables.
187 //
188 // compiled with clang -O0 -g
189 uint8_t data[] = {
190 // prologue
191 0xa6, 0x02, 0x08, 0x7c, // 0: mflr r0
192 0xf8, 0xff, 0xe1, 0xfb, // 4: std r31, -8(r1)
193 0x10, 0x00, 0x01, 0xf8, // 8: std r0, 16(r1)
194 0x78, 0x0b, 0x3e, 0x7c, // 12: mr r30, r1
195 0xe0, 0x06, 0x20, 0x78, // 16: clrldi r0, r1, 59
196 0xa0, 0xfa, 0x00, 0x20, // 20: subfic r0, r0, -1376
197 0x6a, 0x01, 0x21, 0x7c, // 24: stdux r1, r1, r0
198 0x78, 0x0b, 0x3f, 0x7c, // 28: mr r31, r1
199
200 // epilogue
201 0x00, 0x00, 0x21, 0xe8, // 32: ld r1, 0(r1)
202 0x20, 0x00, 0x80, 0x4e // 36: blr
203 };
204
205 sample_range = AddressRange(0x1000, sizeof(data));
206
207 EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
208 sample_range, data, sizeof(data), unwind_plan));
209
210 // 0: CFA=sp+0
211 row_sp = unwind_plan.GetRowForFunctionOffset(offset: 0);
212 EXPECT_EQ(0ull, row_sp->GetOffset());
213 EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_r1_ppc64le);
214 EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
215 EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
216
217 // 1: CFA=sp+0 => fp=[CFA-8]
218 row_sp = unwind_plan.GetRowForFunctionOffset(offset: 8);
219 EXPECT_EQ(8ull, row_sp->GetOffset());
220 EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_r1_ppc64le);
221 EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
222 EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
223
224 EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_r31_ppc64le, regloc));
225 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
226 EXPECT_EQ(-8, regloc.GetOffset());
227
228 // 2: CFA=sp+0 => fp=[CFA-8] lr=[CFA+16]
229 row_sp = unwind_plan.GetRowForFunctionOffset(offset: 12);
230 EXPECT_EQ(12ull, row_sp->GetOffset());
231 EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_r1_ppc64le);
232 EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
233 EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
234
235 EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_ppc64le, regloc));
236 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
237 EXPECT_EQ(16, regloc.GetOffset());
238
239 // 3: CFA=r30
240 row_sp = unwind_plan.GetRowForFunctionOffset(offset: 16);
241 EXPECT_EQ(16ull, row_sp->GetOffset());
242 EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_r30_ppc64le);
243 EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
244 EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
245
246 row_sp = unwind_plan.GetRowForFunctionOffset(offset: 32);
247 EXPECT_EQ(16ull, row_sp->GetOffset());
248 EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_r30_ppc64le);
249 EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
250 EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
251
252 // 4: CFA=sp+0
253 row_sp = unwind_plan.GetRowForFunctionOffset(offset: 36);
254 EXPECT_EQ(36ull, row_sp->GetOffset());
255 EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_r1_ppc64le);
256 EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
257 EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
258}
259

source code of lldb/unittests/UnwindAssembly/PPC64/TestPPC64InstEmulation.cpp