292 lines
11 KiB
C++
292 lines
11 KiB
C++
//===- DWARFCFIProgram.h ----------------------------------------*- C++ -*-===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef LLVM_DEBUGINFO_DWARF_LOWLEVEL_DWARFCFIPROGRAM_H
|
|
#define LLVM_DEBUGINFO_DWARF_LOWLEVEL_DWARFCFIPROGRAM_H
|
|
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/ADT/iterator.h"
|
|
#include "llvm/DebugInfo/DWARF/LowLevel/DWARFDataExtractorSimple.h"
|
|
#include "llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h"
|
|
#include "llvm/Support/Compiler.h"
|
|
#include "llvm/Support/Error.h"
|
|
#include "llvm/TargetParser/Triple.h"
|
|
#include <vector>
|
|
|
|
namespace llvm {
|
|
|
|
namespace dwarf {
|
|
|
|
/// Represent a sequence of Call Frame Information instructions that, when read
|
|
/// in order, construct a table mapping PC to frame state. This can also be
|
|
/// referred to as "CFI rules" in DWARF literature to avoid confusion with
|
|
/// computer programs in the broader sense, and in this context each instruction
|
|
/// would be a rule to establish the mapping. Refer to pg. 172 in the DWARF5
|
|
/// manual, "6.4.1 Structure of Call Frame Information".
|
|
class CFIProgram {
|
|
public:
|
|
static constexpr size_t MaxOperands = 3;
|
|
typedef SmallVector<uint64_t, MaxOperands> Operands;
|
|
|
|
/// An instruction consists of a DWARF CFI opcode and an optional sequence of
|
|
/// operands. If it refers to an expression, then this expression has its own
|
|
/// sequence of operations and operands handled separately by DWARFExpression.
|
|
struct Instruction {
|
|
Instruction(uint8_t Opcode) : Opcode(Opcode) {}
|
|
|
|
uint8_t Opcode;
|
|
Operands Ops;
|
|
// Associated DWARF expression in case this instruction refers to one
|
|
std::optional<DWARFExpression> Expression;
|
|
|
|
LLVM_ABI Expected<uint64_t> getOperandAsUnsigned(const CFIProgram &CFIP,
|
|
uint32_t OperandIdx) const;
|
|
|
|
LLVM_ABI Expected<int64_t> getOperandAsSigned(const CFIProgram &CFIP,
|
|
uint32_t OperandIdx) const;
|
|
};
|
|
|
|
using InstrList = std::vector<Instruction>;
|
|
using iterator = InstrList::iterator;
|
|
using const_iterator = InstrList::const_iterator;
|
|
|
|
iterator begin() { return Instructions.begin(); }
|
|
const_iterator begin() const { return Instructions.begin(); }
|
|
iterator end() { return Instructions.end(); }
|
|
const_iterator end() const { return Instructions.end(); }
|
|
|
|
unsigned size() const { return (unsigned)Instructions.size(); }
|
|
bool empty() const { return Instructions.empty(); }
|
|
uint64_t codeAlign() const { return CodeAlignmentFactor; }
|
|
int64_t dataAlign() const { return DataAlignmentFactor; }
|
|
Triple::ArchType triple() const { return Arch; }
|
|
|
|
CFIProgram(uint64_t CodeAlignmentFactor, int64_t DataAlignmentFactor,
|
|
Triple::ArchType Arch)
|
|
: CodeAlignmentFactor(CodeAlignmentFactor),
|
|
DataAlignmentFactor(DataAlignmentFactor), Arch(Arch) {}
|
|
|
|
/// Parse and store a sequence of CFI instructions from Data,
|
|
/// starting at *Offset and ending at EndOffset. *Offset is updated
|
|
/// to EndOffset upon successful parsing, or indicates the offset
|
|
/// where a problem occurred in case an error is returned.
|
|
template <typename T>
|
|
Error parse(DWARFDataExtractorBase<T> &Data, uint64_t *Offset,
|
|
uint64_t EndOffset) {
|
|
// See DWARF standard v3, section 7.23
|
|
const uint8_t DWARF_CFI_PRIMARY_OPCODE_MASK = 0xc0;
|
|
const uint8_t DWARF_CFI_PRIMARY_OPERAND_MASK = 0x3f;
|
|
|
|
DataExtractor::Cursor C(*Offset);
|
|
while (C && C.tell() < EndOffset) {
|
|
uint8_t Opcode = Data.getRelocatedValue(C, 1);
|
|
if (!C)
|
|
break;
|
|
|
|
// Some instructions have a primary opcode encoded in the top bits.
|
|
if (uint8_t Primary = Opcode & DWARF_CFI_PRIMARY_OPCODE_MASK) {
|
|
// If it's a primary opcode, the first operand is encoded in the
|
|
// bottom bits of the opcode itself.
|
|
uint64_t Op1 = Opcode & DWARF_CFI_PRIMARY_OPERAND_MASK;
|
|
switch (Primary) {
|
|
case DW_CFA_advance_loc:
|
|
case DW_CFA_restore:
|
|
addInstruction(Primary, Op1);
|
|
break;
|
|
case DW_CFA_offset:
|
|
addInstruction(Primary, Op1, Data.getULEB128(C));
|
|
break;
|
|
default:
|
|
llvm_unreachable("invalid primary CFI opcode");
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// Extended opcode - its value is Opcode itself.
|
|
switch (Opcode) {
|
|
default:
|
|
return createStringError(errc::illegal_byte_sequence,
|
|
"invalid extended CFI opcode 0x%" PRIx8,
|
|
Opcode);
|
|
case DW_CFA_nop:
|
|
case DW_CFA_remember_state:
|
|
case DW_CFA_restore_state:
|
|
case DW_CFA_GNU_window_save:
|
|
case DW_CFA_AARCH64_negate_ra_state_with_pc:
|
|
// No operands
|
|
addInstruction(Opcode);
|
|
break;
|
|
case DW_CFA_set_loc:
|
|
// Operands: Address
|
|
addInstruction(Opcode, Data.getRelocatedAddress(C));
|
|
break;
|
|
case DW_CFA_advance_loc1:
|
|
// Operands: 1-byte delta
|
|
addInstruction(Opcode, Data.getRelocatedValue(C, 1));
|
|
break;
|
|
case DW_CFA_advance_loc2:
|
|
// Operands: 2-byte delta
|
|
addInstruction(Opcode, Data.getRelocatedValue(C, 2));
|
|
break;
|
|
case DW_CFA_advance_loc4:
|
|
// Operands: 4-byte delta
|
|
addInstruction(Opcode, Data.getRelocatedValue(C, 4));
|
|
break;
|
|
case DW_CFA_restore_extended:
|
|
case DW_CFA_undefined:
|
|
case DW_CFA_same_value:
|
|
case DW_CFA_def_cfa_register:
|
|
case DW_CFA_def_cfa_offset:
|
|
case DW_CFA_GNU_args_size:
|
|
// Operands: ULEB128
|
|
addInstruction(Opcode, Data.getULEB128(C));
|
|
break;
|
|
case DW_CFA_def_cfa_offset_sf:
|
|
// Operands: SLEB128
|
|
addInstruction(Opcode, Data.getSLEB128(C));
|
|
break;
|
|
case DW_CFA_LLVM_def_aspace_cfa:
|
|
case DW_CFA_LLVM_def_aspace_cfa_sf: {
|
|
auto RegNum = Data.getULEB128(C);
|
|
auto CfaOffset = Opcode == DW_CFA_LLVM_def_aspace_cfa
|
|
? Data.getULEB128(C)
|
|
: Data.getSLEB128(C);
|
|
auto AddressSpace = Data.getULEB128(C);
|
|
addInstruction(Opcode, RegNum, CfaOffset, AddressSpace);
|
|
break;
|
|
}
|
|
case DW_CFA_offset_extended:
|
|
case DW_CFA_register:
|
|
case DW_CFA_def_cfa:
|
|
case DW_CFA_val_offset: {
|
|
// Operands: ULEB128, ULEB128
|
|
// Note: We can not embed getULEB128 directly into function
|
|
// argument list. getULEB128 changes Offset and order of evaluation
|
|
// for arguments is unspecified.
|
|
uint64_t op1 = Data.getULEB128(C);
|
|
uint64_t op2 = Data.getULEB128(C);
|
|
addInstruction(Opcode, op1, op2);
|
|
break;
|
|
}
|
|
case DW_CFA_offset_extended_sf:
|
|
case DW_CFA_def_cfa_sf:
|
|
case DW_CFA_val_offset_sf: {
|
|
// Operands: ULEB128, SLEB128
|
|
// Note: see comment for the previous case
|
|
uint64_t op1 = Data.getULEB128(C);
|
|
uint64_t op2 = (uint64_t)Data.getSLEB128(C);
|
|
addInstruction(Opcode, op1, op2);
|
|
break;
|
|
}
|
|
case DW_CFA_def_cfa_expression: {
|
|
uint64_t ExprLength = Data.getULEB128(C);
|
|
addInstruction(Opcode, 0);
|
|
StringRef Expression = Data.getBytes(C, ExprLength);
|
|
|
|
DataExtractor Extractor(Expression, Data.isLittleEndian());
|
|
// Note. We do not pass the DWARF format to DWARFExpression, because
|
|
// DW_OP_call_ref, the only operation which depends on the format, is
|
|
// prohibited in call frame instructions, see sec. 6.4.2 in DWARFv5.
|
|
Instructions.back().Expression =
|
|
DWARFExpression(Extractor, Data.getAddressSize());
|
|
break;
|
|
}
|
|
case DW_CFA_expression:
|
|
case DW_CFA_val_expression: {
|
|
uint64_t RegNum = Data.getULEB128(C);
|
|
addInstruction(Opcode, RegNum, 0);
|
|
|
|
uint64_t BlockLength = Data.getULEB128(C);
|
|
StringRef Expression = Data.getBytes(C, BlockLength);
|
|
DataExtractor Extractor(Expression, Data.isLittleEndian());
|
|
// Note. We do not pass the DWARF format to DWARFExpression, because
|
|
// DW_OP_call_ref, the only operation which depends on the format, is
|
|
// prohibited in call frame instructions, see sec. 6.4.2 in DWARFv5.
|
|
Instructions.back().Expression =
|
|
DWARFExpression(Extractor, Data.getAddressSize());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
*Offset = C.tell();
|
|
return C.takeError();
|
|
}
|
|
|
|
void addInstruction(const Instruction &I) { Instructions.push_back(I); }
|
|
|
|
/// Get a DWARF CFI call frame string for the given DW_CFA opcode.
|
|
LLVM_ABI StringRef callFrameString(unsigned Opcode) const;
|
|
|
|
/// Types of operands to CFI instructions
|
|
/// In DWARF, this type is implicitly tied to a CFI instruction opcode and
|
|
/// thus this type doesn't need to be explicitly written to the file (this is
|
|
/// not a DWARF encoding). The relationship of instrs to operand types can
|
|
/// be obtained from getOperandTypes() and is only used to simplify
|
|
/// instruction printing and error messages.
|
|
enum OperandType {
|
|
OT_Unset,
|
|
OT_None,
|
|
OT_Address,
|
|
OT_Offset,
|
|
OT_FactoredCodeOffset,
|
|
OT_SignedFactDataOffset,
|
|
OT_UnsignedFactDataOffset,
|
|
OT_Register,
|
|
OT_AddressSpace,
|
|
OT_Expression
|
|
};
|
|
|
|
/// Get the OperandType as a "const char *".
|
|
LLVM_ABI static const char *operandTypeString(OperandType OT);
|
|
|
|
/// Retrieve the array describing the types of operands according to the enum
|
|
/// above. This is indexed by opcode.
|
|
LLVM_ABI static ArrayRef<OperandType[MaxOperands]> getOperandTypes();
|
|
|
|
/// Convenience method to add a new instruction with the given opcode.
|
|
void addInstruction(uint8_t Opcode) {
|
|
Instructions.push_back(Instruction(Opcode));
|
|
}
|
|
|
|
/// Add a new single-operand instruction.
|
|
void addInstruction(uint8_t Opcode, uint64_t Operand1) {
|
|
Instructions.push_back(Instruction(Opcode));
|
|
Instructions.back().Ops.push_back(Operand1);
|
|
}
|
|
|
|
/// Add a new instruction that has two operands.
|
|
void addInstruction(uint8_t Opcode, uint64_t Operand1, uint64_t Operand2) {
|
|
Instructions.push_back(Instruction(Opcode));
|
|
Instructions.back().Ops.push_back(Operand1);
|
|
Instructions.back().Ops.push_back(Operand2);
|
|
}
|
|
|
|
/// Add a new instruction that has three operands.
|
|
void addInstruction(uint8_t Opcode, uint64_t Operand1, uint64_t Operand2,
|
|
uint64_t Operand3) {
|
|
Instructions.push_back(Instruction(Opcode));
|
|
Instructions.back().Ops.push_back(Operand1);
|
|
Instructions.back().Ops.push_back(Operand2);
|
|
Instructions.back().Ops.push_back(Operand3);
|
|
}
|
|
|
|
private:
|
|
std::vector<Instruction> Instructions;
|
|
const uint64_t CodeAlignmentFactor;
|
|
const int64_t DataAlignmentFactor;
|
|
Triple::ArchType Arch;
|
|
};
|
|
|
|
} // end namespace dwarf
|
|
|
|
} // end namespace llvm
|
|
|
|
#endif // LLVM_DEBUGINFO_DWARF_LOWLEVEL_DWARFCFIPROGRAM_H
|