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