blob: 81c067ab564719d5f9664ba0245842c31f69dbb3 [file] [log] [blame]
/*
* 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