xref: /aosp_15_r20/art/disassembler/disassembler_arm64_test.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker  * Copyright (C) 2022 The Android Open Source Project
3*795d594fSAndroid Build Coastguard Worker  *
4*795d594fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*795d594fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*795d594fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*795d594fSAndroid Build Coastguard Worker  *
8*795d594fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*795d594fSAndroid Build Coastguard Worker  *
10*795d594fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*795d594fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*795d594fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*795d594fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*795d594fSAndroid Build Coastguard Worker  * limitations under the License.
15*795d594fSAndroid Build Coastguard Worker  */
16*795d594fSAndroid Build Coastguard Worker 
17*795d594fSAndroid Build Coastguard Worker #include <inttypes.h>
18*795d594fSAndroid Build Coastguard Worker 
19*795d594fSAndroid Build Coastguard Worker #include <regex>
20*795d594fSAndroid Build Coastguard Worker 
21*795d594fSAndroid Build Coastguard Worker #include <sstream>
22*795d594fSAndroid Build Coastguard Worker 
23*795d594fSAndroid Build Coastguard Worker #include "base/common_art_test.h"
24*795d594fSAndroid Build Coastguard Worker #include "disassembler_arm64.h"
25*795d594fSAndroid Build Coastguard Worker #include "thread.h"
26*795d594fSAndroid Build Coastguard Worker 
27*795d594fSAndroid Build Coastguard Worker #pragma GCC diagnostic push
28*795d594fSAndroid Build Coastguard Worker #pragma GCC diagnostic ignored "-Wshadow"
29*795d594fSAndroid Build Coastguard Worker #include "aarch64/disasm-aarch64.h"
30*795d594fSAndroid Build Coastguard Worker #include "aarch64/macro-assembler-aarch64.h"
31*795d594fSAndroid Build Coastguard Worker #pragma GCC diagnostic pop
32*795d594fSAndroid Build Coastguard Worker 
33*795d594fSAndroid Build Coastguard Worker 
34*795d594fSAndroid Build Coastguard Worker using namespace vixl::aarch64;  // NOLINT(build/namespaces)
35*795d594fSAndroid Build Coastguard Worker 
36*795d594fSAndroid Build Coastguard Worker namespace art {
37*795d594fSAndroid Build Coastguard Worker namespace arm64 {
38*795d594fSAndroid Build Coastguard Worker 
39*795d594fSAndroid Build Coastguard Worker /**
40*795d594fSAndroid Build Coastguard Worker  * Fixture class for the ArtDisassemblerTest tests.
41*795d594fSAndroid Build Coastguard Worker  */
42*795d594fSAndroid Build Coastguard Worker class ArtDisassemblerTest : public CommonArtTest {
43*795d594fSAndroid Build Coastguard Worker  public:
ArtDisassemblerTest()44*795d594fSAndroid Build Coastguard Worker   ArtDisassemblerTest() {
45*795d594fSAndroid Build Coastguard Worker   }
46*795d594fSAndroid Build Coastguard Worker 
SetupAssembly(uint64_t end_address)47*795d594fSAndroid Build Coastguard Worker   void SetupAssembly(uint64_t end_address) {
48*795d594fSAndroid Build Coastguard Worker     masm.GetCPUFeatures()->Combine(vixl::CPUFeatures::All());
49*795d594fSAndroid Build Coastguard Worker 
50*795d594fSAndroid Build Coastguard Worker     disamOptions.reset(new DisassemblerOptions(/* absolute_addresses= */ true,
51*795d594fSAndroid Build Coastguard Worker                                                reinterpret_cast<uint8_t*>(0x0),
52*795d594fSAndroid Build Coastguard Worker                                                reinterpret_cast<uint8_t*>(end_address),
53*795d594fSAndroid Build Coastguard Worker                                                /* can_read_literals_= */ true,
54*795d594fSAndroid Build Coastguard Worker                                                &Thread::DumpThreadOffset<PointerSize::k64>));
55*795d594fSAndroid Build Coastguard Worker     disasm.reset(new CustomDisassembler(&*disamOptions));
56*795d594fSAndroid Build Coastguard Worker     decoder.AppendVisitor(disasm.get());
57*795d594fSAndroid Build Coastguard Worker     masm.SetGenerateSimulatorCode(false);
58*795d594fSAndroid Build Coastguard Worker   }
59*795d594fSAndroid Build Coastguard Worker 
60*795d594fSAndroid Build Coastguard Worker   static constexpr size_t kMaxSizeGenerated = 1024;
61*795d594fSAndroid Build Coastguard Worker 
62*795d594fSAndroid Build Coastguard Worker   template <typename LamdaType>
ImplantInstruction(LamdaType fn)63*795d594fSAndroid Build Coastguard Worker   void ImplantInstruction(LamdaType fn) {
64*795d594fSAndroid Build Coastguard Worker     vixl::ExactAssemblyScope guard(&masm,
65*795d594fSAndroid Build Coastguard Worker                                    kMaxSizeGenerated,
66*795d594fSAndroid Build Coastguard Worker                                    vixl::ExactAssemblyScope::kMaximumSize);
67*795d594fSAndroid Build Coastguard Worker     fn();
68*795d594fSAndroid Build Coastguard Worker   }
69*795d594fSAndroid Build Coastguard Worker 
70*795d594fSAndroid Build Coastguard Worker   // Appends an instruction to the existing buffer and then
71*795d594fSAndroid Build Coastguard Worker   // attempts to match the output of that instructions disassembly
72*795d594fSAndroid Build Coastguard Worker   // against a regex expression. Fails if no match is found.
73*795d594fSAndroid Build Coastguard Worker   template <typename LamdaType>
CompareInstruction(LamdaType fn,const char * EXP)74*795d594fSAndroid Build Coastguard Worker   void CompareInstruction(LamdaType fn, const char* EXP) {
75*795d594fSAndroid Build Coastguard Worker     ImplantInstruction(fn);
76*795d594fSAndroid Build Coastguard Worker     masm.FinalizeCode();
77*795d594fSAndroid Build Coastguard Worker 
78*795d594fSAndroid Build Coastguard Worker     // This gets the last instruction in the buffer.
79*795d594fSAndroid Build Coastguard Worker     // The end address of the buffer is at the end of the last instruction.
80*795d594fSAndroid Build Coastguard Worker     // sizeof(Instruction) is 1 byte as it in an empty class.
81*795d594fSAndroid Build Coastguard Worker     // Therefore we need to go back kInstructionSize * sizeof(Instruction) bytes
82*795d594fSAndroid Build Coastguard Worker     // in order to get to the start of the last instruction.
83*795d594fSAndroid Build Coastguard Worker     const Instruction* targetInstruction =
84*795d594fSAndroid Build Coastguard Worker         masm.GetBuffer()->GetEndAddress<Instruction*>()->
85*795d594fSAndroid Build Coastguard Worker             GetInstructionAtOffset(-static_cast<signed>(kInstructionSize));
86*795d594fSAndroid Build Coastguard Worker 
87*795d594fSAndroid Build Coastguard Worker     decoder.Decode(targetInstruction);
88*795d594fSAndroid Build Coastguard Worker 
89*795d594fSAndroid Build Coastguard Worker     const char* disassembly = disasm->GetOutput();
90*795d594fSAndroid Build Coastguard Worker 
91*795d594fSAndroid Build Coastguard Worker     if (!std::regex_match(disassembly, std::regex(EXP))) {
92*795d594fSAndroid Build Coastguard Worker       const uint32_t encoding = static_cast<uint32_t>(targetInstruction->GetInstructionBits());
93*795d594fSAndroid Build Coastguard Worker 
94*795d594fSAndroid Build Coastguard Worker       printf("\nEncoding: %08" PRIx32 "\nExpected: %s\nFound:    %s\n",
95*795d594fSAndroid Build Coastguard Worker              encoding,
96*795d594fSAndroid Build Coastguard Worker              EXP,
97*795d594fSAndroid Build Coastguard Worker              disassembly);
98*795d594fSAndroid Build Coastguard Worker 
99*795d594fSAndroid Build Coastguard Worker       ADD_FAILURE();
100*795d594fSAndroid Build Coastguard Worker     }
101*795d594fSAndroid Build Coastguard Worker     printf("----\n%s\n", disassembly);
102*795d594fSAndroid Build Coastguard Worker   }
103*795d594fSAndroid Build Coastguard Worker 
104*795d594fSAndroid Build Coastguard Worker   std::unique_ptr<CustomDisassembler> disasm;
105*795d594fSAndroid Build Coastguard Worker   std::unique_ptr<DisassemblerOptions> disamOptions;
106*795d594fSAndroid Build Coastguard Worker   Decoder decoder;
107*795d594fSAndroid Build Coastguard Worker   MacroAssembler masm;
108*795d594fSAndroid Build Coastguard Worker };
109*795d594fSAndroid Build Coastguard Worker 
110*795d594fSAndroid Build Coastguard Worker #define IMPLANT(fn)                                                          \
111*795d594fSAndroid Build Coastguard Worker   do {                                                                       \
112*795d594fSAndroid Build Coastguard Worker     ImplantInstruction([&]() { this->masm.fn; });                            \
113*795d594fSAndroid Build Coastguard Worker   } while (0)
114*795d594fSAndroid Build Coastguard Worker 
115*795d594fSAndroid Build Coastguard Worker #define COMPARE(fn, output)                                                  \
116*795d594fSAndroid Build Coastguard Worker   do {                                                                       \
117*795d594fSAndroid Build Coastguard Worker     CompareInstruction([&]() { this->masm.fn; }, (output));                  \
118*795d594fSAndroid Build Coastguard Worker   } while (0)
119*795d594fSAndroid Build Coastguard Worker 
120*795d594fSAndroid Build Coastguard Worker // These tests map onto the named per instruction instrumentation functions in:
121*795d594fSAndroid Build Coastguard Worker // ART/art/disassembler/disassembler_arm.cc
122*795d594fSAndroid Build Coastguard Worker // Context can be found in the logic conditional on incoming instruction types and sequences in the
123*795d594fSAndroid Build Coastguard Worker // ART disassembler. As of writing the functionality we are testing for that of additional
124*795d594fSAndroid Build Coastguard Worker // diagnostic info being appended to the end of the ART disassembly output.
TEST_F(ArtDisassemblerTest,LoadLiteralVisitBadAddress)125*795d594fSAndroid Build Coastguard Worker TEST_F(ArtDisassemblerTest, LoadLiteralVisitBadAddress) {
126*795d594fSAndroid Build Coastguard Worker   SetupAssembly(0xffffff);
127*795d594fSAndroid Build Coastguard Worker 
128*795d594fSAndroid Build Coastguard Worker   // Check we append an erroneous hint "(?)" for literal load instructions with
129*795d594fSAndroid Build Coastguard Worker   // out of scope literal pool value addresses.
130*795d594fSAndroid Build Coastguard Worker   COMPARE(ldr(x0, vixl::aarch64::Assembler::ImmLLiteral(1000)),
131*795d594fSAndroid Build Coastguard Worker       "ldr x0, pc\\+128000 \\(addr -?0x[0-9a-fA-F]+\\) \\(\\?\\)");
132*795d594fSAndroid Build Coastguard Worker }
133*795d594fSAndroid Build Coastguard Worker 
TEST_F(ArtDisassemblerTest,LoadLiteralVisit)134*795d594fSAndroid Build Coastguard Worker TEST_F(ArtDisassemblerTest, LoadLiteralVisit) {
135*795d594fSAndroid Build Coastguard Worker   SetupAssembly(0xffffffffffffffff);
136*795d594fSAndroid Build Coastguard Worker 
137*795d594fSAndroid Build Coastguard Worker   // Test that we do not append anything for ineligible instruction.
138*795d594fSAndroid Build Coastguard Worker   COMPARE(ldr(x0, MemOperand(x18, 0)), "ldr x0, \\[x18\\]$");
139*795d594fSAndroid Build Coastguard Worker 
140*795d594fSAndroid Build Coastguard Worker   // Check we do append some extra info in the right text format for valid literal load instruction.
141*795d594fSAndroid Build Coastguard Worker   COMPARE(ldr(w0, vixl::aarch64::Assembler::ImmLLiteral(0)),
142*795d594fSAndroid Build Coastguard Worker       "ldr w0, pc\\+0 \\(addr -?0x[0-9a-f]+\\) \\(0x18000000 / 402653184\\)");
143*795d594fSAndroid Build Coastguard Worker   // We don't compare with exact value even though it's a known literal (the encoding of the
144*795d594fSAndroid Build Coastguard Worker   // instruction itself) since the precision of printed floating point values could change.
145*795d594fSAndroid Build Coastguard Worker   COMPARE(ldr(s0, vixl::aarch64::Assembler::ImmLLiteral(0)),
146*795d594fSAndroid Build Coastguard Worker       "ldr s0, pc\\+0 \\(addr -?0x[0-9a-f]+\\) \\([0-9]+.[0-9]+e(\\+|-)[0-9]+\\)");
147*795d594fSAndroid Build Coastguard Worker }
148*795d594fSAndroid Build Coastguard Worker 
TEST_F(ArtDisassemblerTest,LoadStoreUnsignedOffsetVisit)149*795d594fSAndroid Build Coastguard Worker TEST_F(ArtDisassemblerTest, LoadStoreUnsignedOffsetVisit) {
150*795d594fSAndroid Build Coastguard Worker   SetupAssembly(0xffffffffffffffff);
151*795d594fSAndroid Build Coastguard Worker 
152*795d594fSAndroid Build Coastguard Worker   // Test that we do not append anything for ineligible instruction.
153*795d594fSAndroid Build Coastguard Worker   COMPARE(ldr(x0, MemOperand(x18, 8)), "ldr x0, \\[x18, #8\\]$");
154*795d594fSAndroid Build Coastguard Worker   // Test that we do append the function name if the instruction is a load from the address
155*795d594fSAndroid Build Coastguard Worker   // stored in the TR register.
156*795d594fSAndroid Build Coastguard Worker   COMPARE(ldr(x0, MemOperand(x19, 8)), "ldr x0, \\[tr, #8\\] ; thin_lock_thread_id");
157*795d594fSAndroid Build Coastguard Worker }
158*795d594fSAndroid Build Coastguard Worker 
TEST_F(ArtDisassemblerTest,UnconditionalBranchNoAppendVisit)159*795d594fSAndroid Build Coastguard Worker TEST_F(ArtDisassemblerTest, UnconditionalBranchNoAppendVisit) {
160*795d594fSAndroid Build Coastguard Worker   SetupAssembly(0xffffffffffffffff);
161*795d594fSAndroid Build Coastguard Worker 
162*795d594fSAndroid Build Coastguard Worker   vixl::aarch64::Label destination;
163*795d594fSAndroid Build Coastguard Worker   masm.Bind(&destination);
164*795d594fSAndroid Build Coastguard Worker 
165*795d594fSAndroid Build Coastguard Worker   IMPLANT(ldr(x16, MemOperand(x18, 0)));
166*795d594fSAndroid Build Coastguard Worker 
167*795d594fSAndroid Build Coastguard Worker   // Test that we do not append anything for ineligible instruction.
168*795d594fSAndroid Build Coastguard Worker   COMPARE(bl(&destination),
169*795d594fSAndroid Build Coastguard Worker       "bl #-0x4 \\(addr -?0x[0-9a-f]+\\)$");
170*795d594fSAndroid Build Coastguard Worker }
171*795d594fSAndroid Build Coastguard Worker 
TEST_F(ArtDisassemblerTest,UnconditionalBranchVisit)172*795d594fSAndroid Build Coastguard Worker TEST_F(ArtDisassemblerTest, UnconditionalBranchVisit) {
173*795d594fSAndroid Build Coastguard Worker   SetupAssembly(0xffffffffffffffff);
174*795d594fSAndroid Build Coastguard Worker 
175*795d594fSAndroid Build Coastguard Worker   vixl::aarch64::Label destination;
176*795d594fSAndroid Build Coastguard Worker   masm.Bind(&destination);
177*795d594fSAndroid Build Coastguard Worker 
178*795d594fSAndroid Build Coastguard Worker   IMPLANT(ldr(x16, MemOperand(x19, 0)));
179*795d594fSAndroid Build Coastguard Worker   IMPLANT(br(x16));
180*795d594fSAndroid Build Coastguard Worker 
181*795d594fSAndroid Build Coastguard Worker   // Test that we do append the function name if the instruction is a branch
182*795d594fSAndroid Build Coastguard Worker   // to a load that reads data from the address in the TR register, into the IPO register
183*795d594fSAndroid Build Coastguard Worker   // followed by a BR branching using the IPO register.
184*795d594fSAndroid Build Coastguard Worker   COMPARE(bl(&destination),
185*795d594fSAndroid Build Coastguard Worker       "bl #-0x8 \\(addr -?0x[0-9a-f]+\\) ; state_and_flags");
186*795d594fSAndroid Build Coastguard Worker }
187*795d594fSAndroid Build Coastguard Worker 
188*795d594fSAndroid Build Coastguard Worker 
189*795d594fSAndroid Build Coastguard Worker }  // namespace arm64
190*795d594fSAndroid Build Coastguard Worker }  // namespace art
191