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
|
//===----------------------------------------------------------------------===//
//
// 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/DWARFCFIChecker/DWARFCFIState.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h"
#include "llvm/MC/MCDwarf.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FormatVariadic.h"
#include <cassert>
#include <optional>
using namespace llvm;
std::optional<dwarf::UnwindRow> DWARFCFIState::getCurrentUnwindRow() const {
if (!IsInitiated)
return std::nullopt;
return Row;
}
void DWARFCFIState::update(const MCCFIInstruction &Directive) {
auto CFIP = convert(Directive);
// This is a copy of the current row, its value will be updated by
// `parseRows`.
dwarf::UnwindRow NewRow = Row;
// `parseRows` updates the current row by applying the `CFIProgram` to it.
// During this process, it may create multiple rows preceding the newly
// updated row and following the previous rows. These middle rows are stored
// in `PrecedingRows`. For now, there is no need to store these rows in the
// state, so they are ignored in the end.
dwarf::UnwindTable::RowContainer PrecedingRows;
// TODO: `.cfi_remember_state` and `.cfi_restore_state` directives are not
// supported yet. The reason is that `parseRows` expects the stack of states
// to be produced and used in a single `CFIProgram`. However, in this use
// case, each instruction creates its own `CFIProgram`, which means the stack
// of states is forgotten between instructions. To fix it, `parseRows` should
// be refactored to read the current stack of states from the argument and
// update it based on the `CFIProgram.`
if (Error Err = parseRows(CFIP, NewRow, nullptr).takeError()) {
Context->reportError(
Directive.getLoc(),
formatv("could not parse this CFI directive due to: {0}",
toString(std::move(Err))));
// Proceed the analysis by ignoring this CFI directive.
return;
}
Row = NewRow;
IsInitiated = true;
}
dwarf::CFIProgram DWARFCFIState::convert(MCCFIInstruction Directive) {
auto CFIP = dwarf::CFIProgram(
/* CodeAlignmentFactor */ 1, /* DataAlignmentFactor */ 1,
Context->getTargetTriple().getArch());
auto MaybeCurrentRow = getCurrentUnwindRow();
switch (Directive.getOperation()) {
case MCCFIInstruction::OpSameValue:
CFIP.addInstruction(dwarf::DW_CFA_same_value, Directive.getRegister());
break;
case MCCFIInstruction::OpRememberState:
// TODO: remember state is not supported yet, the following line does not
// work:
// CFIP.addInstruction(dwarf::DW_CFA_remember_state);
// The reason is explained in the `DWARFCFIState::update` method where
// `dwarf::parseRows` is used.
Context->reportWarning(Directive.getLoc(),
"this directive is not supported, ignoring it");
break;
case MCCFIInstruction::OpRestoreState:
// TODO: restore state is not supported yet, the following line does not
// work:
// CFIP.addInstruction(dwarf::DW_CFA_restore_state);
// The reason is explained in the `DWARFCFIState::update` method where
// `dwarf::parseRows` is used.
Context->reportWarning(Directive.getLoc(),
"this directive is not supported, ignoring it");
break;
case MCCFIInstruction::OpOffset:
CFIP.addInstruction(dwarf::DW_CFA_offset, Directive.getRegister(),
Directive.getOffset());
break;
case MCCFIInstruction::OpLLVMDefAspaceCfa:
CFIP.addInstruction(dwarf::DW_CFA_LLVM_def_aspace_cfa,
Directive.getRegister());
break;
case MCCFIInstruction::OpDefCfaRegister:
CFIP.addInstruction(dwarf::DW_CFA_def_cfa_register,
Directive.getRegister());
break;
case MCCFIInstruction::OpDefCfaOffset:
CFIP.addInstruction(dwarf::DW_CFA_def_cfa_offset, Directive.getOffset());
break;
case MCCFIInstruction::OpDefCfa:
CFIP.addInstruction(dwarf::DW_CFA_def_cfa, Directive.getRegister(),
Directive.getOffset());
break;
case MCCFIInstruction::OpRelOffset:
assert(
IsInitiated &&
"cannot define relative offset to a non-existing CFA unwinding rule");
CFIP.addInstruction(dwarf::DW_CFA_offset, Directive.getRegister(),
Directive.getOffset() - Row.getCFAValue().getOffset());
break;
case MCCFIInstruction::OpAdjustCfaOffset:
assert(IsInitiated &&
"cannot adjust CFA offset of a non-existing CFA unwinding rule");
CFIP.addInstruction(dwarf::DW_CFA_def_cfa_offset,
Directive.getOffset() + Row.getCFAValue().getOffset());
break;
case MCCFIInstruction::OpEscape:
// TODO: DWARFExpressions are not supported yet, ignoring expression here.
Context->reportWarning(Directive.getLoc(),
"this directive is not supported, ignoring it");
break;
case MCCFIInstruction::OpRestore:
// The `.cfi_restore register` directive restores the register's unwinding
// information to its CIE value. However, assemblers decide where CIE ends
// and the FDE starts, so the functionality of this directive depends on the
// assembler's decision and cannot be validated.
Context->reportWarning(
Directive.getLoc(),
"this directive behavior depends on the assembler, ignoring it");
break;
case MCCFIInstruction::OpUndefined:
CFIP.addInstruction(dwarf::DW_CFA_undefined, Directive.getRegister());
break;
case MCCFIInstruction::OpRegister:
CFIP.addInstruction(dwarf::DW_CFA_register, Directive.getRegister(),
Directive.getRegister2());
break;
case MCCFIInstruction::OpWindowSave:
CFIP.addInstruction(dwarf::DW_CFA_GNU_window_save);
break;
case MCCFIInstruction::OpNegateRAState:
CFIP.addInstruction(dwarf::DW_CFA_AARCH64_negate_ra_state);
break;
case MCCFIInstruction::OpNegateRAStateWithPC:
CFIP.addInstruction(dwarf::DW_CFA_AARCH64_negate_ra_state_with_pc);
break;
case MCCFIInstruction::OpGnuArgsSize:
CFIP.addInstruction(dwarf::DW_CFA_GNU_args_size);
break;
case MCCFIInstruction::OpLabel:
// `.cfi_label` does not have any functional effect on unwinding process.
break;
case MCCFIInstruction::OpValOffset:
CFIP.addInstruction(dwarf::DW_CFA_val_offset, Directive.getRegister(),
Directive.getOffset());
break;
}
return CFIP;
}
|