167 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "llvm/DWARFCFIChecker/DWARFCFIState.h"10#include "llvm/BinaryFormat/Dwarf.h"11#include "llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h"12#include "llvm/MC/MCDwarf.h"13#include "llvm/Support/Error.h"14#include "llvm/Support/ErrorHandling.h"15#include "llvm/Support/FormatVariadic.h"16#include <cassert>17#include <optional>18 19using namespace llvm;20 21std::optional<dwarf::UnwindRow> DWARFCFIState::getCurrentUnwindRow() const {22 if (!IsInitiated)23 return std::nullopt;24 return Row;25}26 27void DWARFCFIState::update(const MCCFIInstruction &Directive) {28 auto CFIP = convert(Directive);29 30 // This is a copy of the current row, its value will be updated by31 // `parseRows`.32 dwarf::UnwindRow NewRow = Row;33 34 // `parseRows` updates the current row by applying the `CFIProgram` to it.35 // During this process, it may create multiple rows preceding the newly36 // updated row and following the previous rows. These middle rows are stored37 // in `PrecedingRows`. For now, there is no need to store these rows in the38 // state, so they are ignored in the end.39 dwarf::UnwindTable::RowContainer PrecedingRows;40 41 // TODO: `.cfi_remember_state` and `.cfi_restore_state` directives are not42 // supported yet. The reason is that `parseRows` expects the stack of states43 // to be produced and used in a single `CFIProgram`. However, in this use44 // case, each instruction creates its own `CFIProgram`, which means the stack45 // of states is forgotten between instructions. To fix it, `parseRows` should46 // be refactored to read the current stack of states from the argument and47 // update it based on the `CFIProgram.`48 if (Error Err = parseRows(CFIP, NewRow, nullptr).takeError()) {49 Context->reportError(50 Directive.getLoc(),51 formatv("could not parse this CFI directive due to: {0}",52 toString(std::move(Err))));53 54 // Proceed the analysis by ignoring this CFI directive.55 return;56 }57 58 Row = NewRow;59 IsInitiated = true;60}61 62dwarf::CFIProgram DWARFCFIState::convert(MCCFIInstruction Directive) {63 auto CFIP = dwarf::CFIProgram(64 /* CodeAlignmentFactor */ 1, /* DataAlignmentFactor */ 1,65 Context->getTargetTriple().getArch());66 67 switch (Directive.getOperation()) {68 case MCCFIInstruction::OpSameValue:69 CFIP.addInstruction(dwarf::DW_CFA_same_value, Directive.getRegister());70 break;71 case MCCFIInstruction::OpRememberState:72 // TODO: remember state is not supported yet, the following line does not73 // work:74 // CFIP.addInstruction(dwarf::DW_CFA_remember_state);75 // The reason is explained in the `DWARFCFIState::update` method where76 // `dwarf::parseRows` is used.77 Context->reportWarning(Directive.getLoc(),78 "this directive is not supported, ignoring it");79 break;80 case MCCFIInstruction::OpRestoreState:81 // TODO: restore state is not supported yet, the following line does not82 // work:83 // CFIP.addInstruction(dwarf::DW_CFA_restore_state);84 // The reason is explained in the `DWARFCFIState::update` method where85 // `dwarf::parseRows` is used.86 Context->reportWarning(Directive.getLoc(),87 "this directive is not supported, ignoring it");88 break;89 case MCCFIInstruction::OpOffset:90 CFIP.addInstruction(dwarf::DW_CFA_offset, Directive.getRegister(),91 Directive.getOffset());92 break;93 case MCCFIInstruction::OpLLVMDefAspaceCfa:94 CFIP.addInstruction(dwarf::DW_CFA_LLVM_def_aspace_cfa,95 Directive.getRegister());96 break;97 case MCCFIInstruction::OpDefCfaRegister:98 CFIP.addInstruction(dwarf::DW_CFA_def_cfa_register,99 Directive.getRegister());100 break;101 case MCCFIInstruction::OpDefCfaOffset:102 CFIP.addInstruction(dwarf::DW_CFA_def_cfa_offset, Directive.getOffset());103 break;104 case MCCFIInstruction::OpDefCfa:105 CFIP.addInstruction(dwarf::DW_CFA_def_cfa, Directive.getRegister(),106 Directive.getOffset());107 break;108 case MCCFIInstruction::OpRelOffset:109 assert(110 IsInitiated &&111 "cannot define relative offset to a non-existing CFA unwinding rule");112 113 CFIP.addInstruction(dwarf::DW_CFA_offset, Directive.getRegister(),114 Directive.getOffset() - Row.getCFAValue().getOffset());115 break;116 case MCCFIInstruction::OpAdjustCfaOffset:117 assert(IsInitiated &&118 "cannot adjust CFA offset of a non-existing CFA unwinding rule");119 120 CFIP.addInstruction(dwarf::DW_CFA_def_cfa_offset,121 Directive.getOffset() + Row.getCFAValue().getOffset());122 break;123 case MCCFIInstruction::OpEscape:124 // TODO: DWARFExpressions are not supported yet, ignoring expression here.125 Context->reportWarning(Directive.getLoc(),126 "this directive is not supported, ignoring it");127 break;128 case MCCFIInstruction::OpRestore:129 // The `.cfi_restore register` directive restores the register's unwinding130 // information to its CIE value. However, assemblers decide where CIE ends131 // and the FDE starts, so the functionality of this directive depends on the132 // assembler's decision and cannot be validated.133 Context->reportWarning(134 Directive.getLoc(),135 "this directive behavior depends on the assembler, ignoring it");136 break;137 case MCCFIInstruction::OpUndefined:138 CFIP.addInstruction(dwarf::DW_CFA_undefined, Directive.getRegister());139 break;140 case MCCFIInstruction::OpRegister:141 CFIP.addInstruction(dwarf::DW_CFA_register, Directive.getRegister(),142 Directive.getRegister2());143 break;144 case MCCFIInstruction::OpWindowSave:145 CFIP.addInstruction(dwarf::DW_CFA_GNU_window_save);146 break;147 case MCCFIInstruction::OpNegateRAState:148 CFIP.addInstruction(dwarf::DW_CFA_AARCH64_negate_ra_state);149 break;150 case MCCFIInstruction::OpNegateRAStateWithPC:151 CFIP.addInstruction(dwarf::DW_CFA_AARCH64_negate_ra_state_with_pc);152 break;153 case MCCFIInstruction::OpGnuArgsSize:154 CFIP.addInstruction(dwarf::DW_CFA_GNU_args_size);155 break;156 case MCCFIInstruction::OpLabel:157 // `.cfi_label` does not have any functional effect on unwinding process.158 break;159 case MCCFIInstruction::OpValOffset:160 CFIP.addInstruction(dwarf::DW_CFA_val_offset, Directive.getRegister(),161 Directive.getOffset());162 break;163 }164 165 return CFIP;166}167