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