diff options
Diffstat (limited to 'disassembler/disassembler_arm64_test.cc')
-rw-r--r-- | disassembler/disassembler_arm64_test.cc | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/disassembler/disassembler_arm64_test.cc b/disassembler/disassembler_arm64_test.cc new file mode 100644 index 0000000000..8279ed8424 --- /dev/null +++ b/disassembler/disassembler_arm64_test.cc @@ -0,0 +1,187 @@ +/* + * 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 "common_runtime_test.h" +#include "disassembler_arm64.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 CommonRuntimeTest { + 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(x0, vixl::aarch64::Assembler::ImmLLiteral(0)), + "ldr x0, pc\\+0 \\(addr -?0x[0-9a-f]+\\) \\(0x[0-9a-fA-F]+ / -?[0-9]+\\)"); + COMPARE(ldr(d0, vixl::aarch64::Assembler::ImmLLiteral(0)), + "ldr d0, 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 |