On some architectures such as Arm and X86 the encoding for a nop may change depending on the subtarget in operation at the time of encoding. This change replaces the per module MCSubtargetInfo retained by the targets AsmBackend in favour of passing through the local MCSubtargetInfo in operation at the time. On Arm using the architectural NOP instruction can have a performance benefit on some implementations. For Arm I've deleted the copy of the AsmBackend's MCSubtargetInfo to limit the chances of this causing problems in the future. I've not done this for other targets such as X86 as there is more frequent use of the MCSubtargetInfo and it looks to be for stable properties that we would not expect to vary per function. This change required threading STI through MCNopsFragment and MCBoundaryAlignFragment. I've attempted to take into account the in tree experimental backends. Differential Revision: https://reviews.llvm.org/D45962
242 lines
8.6 KiB
C++
242 lines
8.6 KiB
C++
//===- llvm/unittest/DebugInfo/DWARFExpressionRawDataTest.cpp -------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/ADT/Triple.h"
|
|
#include "llvm/BinaryFormat/Dwarf.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFDie.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFExpression.h"
|
|
#include "llvm/MC/MCAsmBackend.h"
|
|
#include "llvm/MC/MCAsmInfo.h"
|
|
#include "llvm/MC/MCCodeEmitter.h"
|
|
#include "llvm/MC/MCContext.h"
|
|
#include "llvm/MC/MCInstrInfo.h"
|
|
#include "llvm/MC/MCObjectWriter.h"
|
|
#include "llvm/MC/MCStreamer.h"
|
|
#include "llvm/MC/MCTargetOptions.h"
|
|
#include "llvm/Object/Binary.h"
|
|
#include "llvm/Support/DataExtractor.h"
|
|
#include "llvm/Support/LEB128.h"
|
|
#include "llvm/Support/TargetRegistry.h"
|
|
#include "llvm/Support/TargetSelect.h"
|
|
#include "llvm/Testing/Support/Error.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
using namespace llvm;
|
|
using namespace dwarf;
|
|
|
|
namespace {
|
|
|
|
/// Tests that a client of DebugInfo/DWARF is able to read raw data bytes of a
|
|
/// DWARFExpression parsed from CFI with the intent of writing them back as is
|
|
/// via MC layer / cfi_escape.
|
|
/// This is relevant for binary tools that need to rewrite/copy unwind and
|
|
/// debug info from input to output binary.
|
|
class DWARFExpressionCopyBytesTest : public ::testing::Test {
|
|
public:
|
|
const char *TripleName = "x86_64-pc-linux";
|
|
std::unique_ptr<MCRegisterInfo> MRI;
|
|
std::unique_ptr<MCAsmInfo> MAI;
|
|
std::unique_ptr<const MCSubtargetInfo> STI;
|
|
const Target *TheTarget;
|
|
|
|
DWARFExpressionCopyBytesTest() {
|
|
InitializeAllTargets();
|
|
InitializeAllTargetMCs();
|
|
InitializeAllAsmPrinters();
|
|
|
|
std::string ErrorStr;
|
|
TheTarget = TargetRegistry::lookupTarget(TripleName, ErrorStr);
|
|
if (!TheTarget)
|
|
return;
|
|
|
|
MRI.reset(TheTarget->createMCRegInfo(TripleName));
|
|
MAI.reset(TheTarget->createMCAsmInfo(*MRI, TripleName, MCTargetOptions()));
|
|
STI.reset(TheTarget->createMCSubtargetInfo(TripleName, "", ""));
|
|
}
|
|
|
|
struct StreamerContext {
|
|
std::unique_ptr<MCObjectFileInfo> MOFI;
|
|
std::unique_ptr<MCContext> Ctx;
|
|
std::unique_ptr<const MCInstrInfo> MII;
|
|
std::unique_ptr<MCStreamer> Streamer;
|
|
};
|
|
|
|
/// Create all data structures necessary to operate an assembler
|
|
StreamerContext createStreamer(raw_pwrite_stream &OS);
|
|
/// Emit a dummy obj file with a single CFI instruction,
|
|
/// DW_CFA_def_cfa_expression, encoding as its operand the DWARF expression
|
|
/// represented by ExprBytes
|
|
SmallString<0> emitObjFile(StringRef ExprBytes);
|
|
/// Peruse the object file looking for the encoded DWARF expression, and check
|
|
/// that its operand was encoded correctly
|
|
void parseCFIsAndCheckExpression(const llvm::object::ObjectFile &E,
|
|
ArrayRef<uint8_t> Expected);
|
|
/// Open the in-memory relocatable object file and verify that it contains
|
|
/// the expected DWARF expression bytes
|
|
void readAndCheckObjFile(StringRef ObjFileData, ArrayRef<uint8_t> Expected);
|
|
/// Run this test on the DWARF expression represented by the bytes in
|
|
/// ExprData. Check that the getData() API retrieves these original bytes and
|
|
/// that we can use them to encode a CFI with those bytes as operands (via
|
|
/// cfi_escape).
|
|
void testExpr(ArrayRef<uint8_t> ExprData);
|
|
};
|
|
|
|
} // namespace
|
|
|
|
DWARFExpressionCopyBytesTest::StreamerContext
|
|
DWARFExpressionCopyBytesTest::createStreamer(raw_pwrite_stream &OS) {
|
|
StreamerContext Res;
|
|
Res.Ctx =
|
|
std::make_unique<MCContext>(Triple(TripleName), MAI.get(), MRI.get(),
|
|
/*MSTI=*/nullptr);
|
|
Res.MOFI.reset(TheTarget->createMCObjectFileInfo(*Res.Ctx.get(),
|
|
/*PIC=*/false));
|
|
Res.Ctx->setObjectFileInfo(Res.MOFI.get());
|
|
|
|
Res.MII.reset(TheTarget->createMCInstrInfo());
|
|
MCCodeEmitter *MCE = TheTarget->createMCCodeEmitter(*Res.MII, *MRI, *Res.Ctx);
|
|
MCAsmBackend *MAB =
|
|
TheTarget->createMCAsmBackend(*STI, *MRI, MCTargetOptions());
|
|
std::unique_ptr<MCObjectWriter> OW = MAB->createObjectWriter(OS);
|
|
Res.Streamer.reset(TheTarget->createMCObjectStreamer(
|
|
Triple(TripleName), *Res.Ctx, std::unique_ptr<MCAsmBackend>(MAB),
|
|
std::move(OW), std::unique_ptr<MCCodeEmitter>(MCE), *STI,
|
|
/* RelaxAll */ false,
|
|
/* IncrementalLinkerCompatible */ false,
|
|
/* DWARFMustBeAtTheEnd */ false));
|
|
return Res;
|
|
}
|
|
|
|
SmallString<0> DWARFExpressionCopyBytesTest::emitObjFile(StringRef ExprBytes) {
|
|
auto EncodeDefCfaExpr = [&](StringRef Bytes) {
|
|
std::string Str;
|
|
raw_string_ostream OS(Str);
|
|
OS << static_cast<uint8_t>(dwarf::DW_CFA_def_cfa_expression);
|
|
encodeULEB128(Bytes.size(), OS);
|
|
OS << Bytes;
|
|
return Str;
|
|
};
|
|
|
|
SmallString<0> Storage;
|
|
raw_svector_ostream VecOS(Storage);
|
|
StreamerContext C = createStreamer(VecOS);
|
|
C.Streamer->initSections(false, *STI);
|
|
MCSection *Section = C.MOFI->getTextSection();
|
|
Section->setHasInstructions(true);
|
|
C.Streamer->SwitchSection(Section);
|
|
C.Streamer->emitCFIStartProc(true);
|
|
auto Str = EncodeDefCfaExpr(ExprBytes);
|
|
C.Streamer->emitCFIEscape(Str);
|
|
C.Streamer->emitNops(4, 1, SMLoc(), *STI);
|
|
C.Streamer->emitCFIEndProc();
|
|
C.Streamer->Finish();
|
|
return Storage;
|
|
}
|
|
|
|
void DWARFExpressionCopyBytesTest::parseCFIsAndCheckExpression(
|
|
const llvm::object::ObjectFile &E, ArrayRef<uint8_t> Expected) {
|
|
auto FetchFirstCfaExpression =
|
|
[](const DWARFDebugFrame &EHFrame) -> Optional<CFIProgram::Instruction> {
|
|
for (const dwarf::FrameEntry &Entry : EHFrame.entries()) {
|
|
const auto *CurFDE = dyn_cast<dwarf::FDE>(&Entry);
|
|
if (!CurFDE)
|
|
continue;
|
|
for (const CFIProgram::Instruction &Instr : CurFDE->cfis()) {
|
|
if (Instr.Opcode != dwarf::DW_CFA_def_cfa_expression)
|
|
continue;
|
|
return Instr;
|
|
}
|
|
}
|
|
return NoneType();
|
|
};
|
|
|
|
std::unique_ptr<DWARFContext> Ctx = DWARFContext::create(E);
|
|
const DWARFDebugFrame *EHFrame = cantFail(Ctx->getEHFrame());
|
|
ASSERT_NE(nullptr, EHFrame);
|
|
auto CfiInstr = FetchFirstCfaExpression(*EHFrame);
|
|
ASSERT_TRUE(CfiInstr);
|
|
DWARFExpression Expr = *(CfiInstr->Expression);
|
|
StringRef ExprData = Expr.getData();
|
|
EXPECT_EQ(ExprData.size(), Expected.size());
|
|
for (unsigned I = 0, E = ExprData.size(); I != E; ++I) {
|
|
EXPECT_EQ(static_cast<uint8_t>(ExprData[I]), Expected[I]);
|
|
}
|
|
}
|
|
|
|
void DWARFExpressionCopyBytesTest::readAndCheckObjFile(
|
|
StringRef ObjFileData, ArrayRef<uint8_t> Expected) {
|
|
std::unique_ptr<MemoryBuffer> MB =
|
|
MemoryBuffer::getMemBuffer(ObjFileData, "", false);
|
|
std::unique_ptr<object::Binary> Bin =
|
|
cantFail(llvm::object::createBinary(MB->getMemBufferRef()));
|
|
if (auto *E = dyn_cast<llvm::object::ELFObjectFileBase>(&*Bin)) {
|
|
parseCFIsAndCheckExpression(*E, Expected);
|
|
}
|
|
}
|
|
|
|
void DWARFExpressionCopyBytesTest::testExpr(ArrayRef<uint8_t> ExprData) {
|
|
// If we didn't build x86, do not run the test.
|
|
if (!MRI)
|
|
return;
|
|
|
|
DataExtractor DE(ExprData, true, 8);
|
|
DWARFExpression Expr(DE, 8);
|
|
|
|
// Copy this expression into the CFI of a binary and check that we are able to
|
|
// get it back correctly from this binary.
|
|
const SmallString<0> EmittedBinContents = emitObjFile(Expr.getData());
|
|
readAndCheckObjFile(EmittedBinContents.str(), ExprData);
|
|
}
|
|
|
|
TEST_F(DWARFExpressionCopyBytesTest, Test_OP_reg0) { testExpr({DW_OP_reg0}); }
|
|
|
|
TEST_F(DWARFExpressionCopyBytesTest, Test_OP_reg10) { testExpr({DW_OP_reg10}); }
|
|
|
|
TEST_F(DWARFExpressionCopyBytesTest, Test_OP_regx) {
|
|
testExpr({DW_OP_regx, 0x80, 0x02});
|
|
}
|
|
|
|
TEST_F(DWARFExpressionCopyBytesTest, Test_OP_breg0) {
|
|
testExpr({DW_OP_breg0, 0x04});
|
|
}
|
|
|
|
TEST_F(DWARFExpressionCopyBytesTest, Test_OP_breg0_large_offset) {
|
|
testExpr({DW_OP_breg0, 0x80, 0x02});
|
|
}
|
|
|
|
TEST_F(DWARFExpressionCopyBytesTest, Test_OP_breg13) {
|
|
testExpr({DW_OP_breg13, 0x10});
|
|
}
|
|
|
|
TEST_F(DWARFExpressionCopyBytesTest, Test_OP_breg13_zero_offset) {
|
|
testExpr({DW_OP_breg13, 0x00});
|
|
}
|
|
|
|
TEST_F(DWARFExpressionCopyBytesTest, Test_OP_breg0_negative) {
|
|
testExpr({DW_OP_breg0, 0x70});
|
|
}
|
|
|
|
TEST_F(DWARFExpressionCopyBytesTest, Test_OP_bregx) {
|
|
testExpr({DW_OP_bregx, 0x0d, 0x28});
|
|
}
|
|
|
|
TEST_F(DWARFExpressionCopyBytesTest, Test_OP_stack_value) {
|
|
testExpr({DW_OP_breg13, 0x04, DW_OP_stack_value});
|
|
}
|
|
|
|
TEST_F(DWARFExpressionCopyBytesTest, Test_OP_entry_value) {
|
|
testExpr({DW_OP_entry_value, 0x01, DW_OP_reg0, DW_OP_stack_value});
|
|
}
|
|
|
|
TEST_F(DWARFExpressionCopyBytesTest, Test_OP_entry_value_mem) {
|
|
testExpr({DW_OP_entry_value, 0x02, DW_OP_breg13, 0x10, DW_OP_stack_value});
|
|
}
|