-
Notifications
You must be signed in to change notification settings - Fork 15.2k
Minimal unwinding information (DWARF CFI) checker #145633
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 94 commits
5b78ea0
b781e83
8b43f8c
6790f90
75f3b2d
a3d08d8
7a10923
5841b8f
69a8e9d
43b54fd
474ff1c
8ea5d9c
e92005c
7527ba2
3d65eeb
79c1664
02410ac
dfdd22f
4fd37bf
07d38b2
eb06aa4
e7c3140
6347ef4
453917b
9f8bca8
2c233f9
12faad8
282cb77
903e3fb
7bb20f8
050dbdb
223f4f7
6b727ec
580b1fc
7f4daf8
cece662
fa0505b
ff81fa2
0cf499e
969f86f
c57ed14
0a8f747
48472ac
9b09b49
7576857
374f144
e42907f
37858ef
b8cdef6
c1538d3
88952b0
017d890
76b96dd
60689b3
5b3475b
6bcc0cc
439c1d0
dfddfea
876f5a6
9546eac
f419eb6
ee98d82
1f7a26f
07ef762
f0f7108
d6b27ec
c32c667
325e29f
6a38936
bc5b570
e9b7d78
62f3d02
b0cc777
55920fe
afa9914
17348ef
290fa4a
f6770c0
260fef2
68fc758
62c41f7
f1f4796
77168bb
627e8b3
9abf615
3c632ba
86cdd3d
ccfeaa5
8a87530
f080e0d
10a84ff
d6025c6
c8c6582
f2efe74
4d934c0
1e9b4e6
1981374
d231713
aa1fbf3
417c31b
713877e
da4002c
29d968f
f981996
76bb638
0eb0f2d
11d76c4
d901096
8dd62ea
7e461ad
aa3d1d7
cc2107f
2d04fc1
f83d966
742ab6f
8acbc43
24e398b
b6b9b57
5c789de
da67918
78c8c2b
e186683
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,97 @@ | ||||||||||
| //===----------------------------------------------------------------------===// | ||||||||||
| // | ||||||||||
| // 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 | ||||||||||
| // | ||||||||||
| //===----------------------------------------------------------------------===// | ||||||||||
| /// | ||||||||||
| /// \file | ||||||||||
| /// This file declares `DWARFCFIAnalysis` class. | ||||||||||
| /// `DWARFCFIAnalysis` is a minimal implementation of a DWARF CFI checker | ||||||||||
| /// described in this link: | ||||||||||
| /// https://discourse.llvm.org/t/rfc-dwarf-cfi-validation/86936 | ||||||||||
| /// | ||||||||||
| //===----------------------------------------------------------------------===// | ||||||||||
|
|
||||||||||
| #ifndef LLVM_DWARFCFICHECKER_DWARFCFIANALYSIS_H | ||||||||||
| #define LLVM_DWARFCFICHECKER_DWARFCFIANALYSIS_H | ||||||||||
|
|
||||||||||
| #include "DWARFCFIState.h" | ||||||||||
| #include "llvm/ADT/ArrayRef.h" | ||||||||||
| #include "llvm/ADT/SmallSet.h" | ||||||||||
| #include "llvm/ADT/SmallVector.h" | ||||||||||
| #include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h" | ||||||||||
| #include "llvm/MC/MCContext.h" | ||||||||||
| #include "llvm/MC/MCDwarf.h" | ||||||||||
| #include "llvm/MC/MCExpr.h" | ||||||||||
| #include "llvm/MC/MCInst.h" | ||||||||||
| #include "llvm/MC/MCInstrInfo.h" | ||||||||||
| #include "llvm/MC/MCRegisterInfo.h" | ||||||||||
| #include "llvm/MC/MCStreamer.h" | ||||||||||
| #include "llvm/MC/MCSubtargetInfo.h" | ||||||||||
| #include "llvm/MC/TargetRegistry.h" | ||||||||||
|
|
||||||||||
| namespace llvm { | ||||||||||
|
|
||||||||||
| /// `DWARFCFIAnalysis` validates the DWARF Call Frame Information one machine | ||||||||||
| /// instruction at a time. This class maintains an internal CFI state | ||||||||||
| /// initialized with the prologue directives and updated with each instruction's | ||||||||||
| /// associated directives. In each update, it checks if the CFI state | ||||||||||
| /// modifications match the machine instruction influence on the CFI state or | ||||||||||
| /// not. This checking may results in errors and warnings. | ||||||||||
amsen20 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||
| /// | ||||||||||
| /// In current stage, the analysis is only aware of what registers the | ||||||||||
| /// instruction modifies. If the modification is happening to a sub-register, | ||||||||||
| /// the analysis considers the super-register is modified. | ||||||||||
| /// | ||||||||||
| /// In each update, for each register (or CFA), the following cases can happen: | ||||||||||
| /// 1. The unwinding rule is not changed: | ||||||||||
| /// a. The registers involved in this rule are not modified: the analysis | ||||||||||
| /// proceeds without emitting error or warning. | ||||||||||
|
||||||||||
| /// a. The registers involved in this rule are not modified: the analysis | |
| /// proceeds without emitting error or warning. | |
| /// a. The registers involved in this rule are not modified: the analysis | |
| /// proceeds without emitting error or warning. |
This formatting seems easier to read, you may want to update the rest of the comments to reflect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // 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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| /// | ||
| /// \file | ||
| /// This file declares CFIFunctionFrameAnalyzer class. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This class appears to be a derived class of the no-op base class In particular, after this class receives CFI instructions and thinks about them, what happens as a consequence? There don't seem to be any query methods that allow you to get information back out of it. (Having read the rest of your code, I know the answer – the answer is that you use the provided
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added documentation |
||
| /// | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef LLVM_DWARFCFICHECKER_DWARFCFIFUNCTIONFRAMEANALYZER_H | ||
| #define LLVM_DWARFCFICHECKER_DWARFCFIFUNCTIONFRAMEANALYZER_H | ||
|
|
||
| #include "DWARFCFIAnalysis.h" | ||
| #include "DWARFCFIFunctionFrameReceiver.h" | ||
| #include "llvm/ADT/ArrayRef.h" | ||
|
|
||
| namespace llvm { | ||
|
|
||
| /// This class implements the `CFIFunctionFrameReceiver` interface to validate | ||
| /// Call Frame Information in a stream of function frames. For validation, it | ||
| /// instantiates a `DWARFCFIAnalysis` for each frame. The errors/warnings are | ||
| /// emitted through the `MCContext` instance to the constructor. If a frame | ||
| /// finishes without being started or if all the frames are not finished before | ||
| /// this classes is destructured, the program fails through an assertion. | ||
Sterling-Augustine marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| class CFIFunctionFrameAnalyzer : public CFIFunctionFrameReceiver { | ||
| public: | ||
| CFIFunctionFrameAnalyzer(MCContext &Context, const MCInstrInfo &MCII) | ||
| : CFIFunctionFrameReceiver(Context), MCII(MCII) {} | ||
| ~CFIFunctionFrameAnalyzer(); | ||
|
|
||
| void startFunctionFrame(bool IsEH, | ||
| ArrayRef<MCCFIInstruction> Prologue) override; | ||
| void | ||
| emitInstructionAndDirectives(const MCInst &Inst, | ||
| ArrayRef<MCCFIInstruction> Directives) override; | ||
| void finishFunctionFrame() override; | ||
|
|
||
| private: | ||
| MCInstrInfo const &MCII; | ||
| std::vector<DWARFCFIAnalysis> UIAs; | ||
| }; | ||
|
|
||
| } // namespace llvm | ||
|
|
||
| #endif | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // 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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| /// | ||
| /// \file | ||
| /// This file declares CFIFunctionFrameReceiver class. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems to be an empty base class which exists only to be derived from, and is passed to another checking function. What function?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added documentation
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What motivates the abstract base class here? it feels a little yagni, and adds complexity that may not be realistically needed. But perhaps there are plans for multiple implementations later?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Two reasons motivate the existence of this abstract class:
If these reasons are not enough to have this abstract class, I can just remove it. |
||
| /// | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef LLVM_DWARFCFICHECKER_DWARFCFIFUNCTIONFRAMERECEIVER_H | ||
| #define LLVM_DWARFCFICHECKER_DWARFCFIFUNCTIONFRAMERECEIVER_H | ||
|
|
||
| #include "llvm/ADT/ArrayRef.h" | ||
|
|
||
| namespace llvm { | ||
|
|
||
| class MCCFIInstruction; | ||
| class MCContext; | ||
| class MCInst; | ||
|
|
||
| /// This abstract base class is an interface for receiving DWARF function frames | ||
| /// Call Frame Information. `DWARFCFIFunctionFrameStreamer` channels the | ||
| /// function frames information gathered from an `MCStreamer` using a pointer to | ||
| /// an instance of this class for the whole program. | ||
| class CFIFunctionFrameReceiver { | ||
| public: | ||
| CFIFunctionFrameReceiver(const CFIFunctionFrameReceiver &) = delete; | ||
| CFIFunctionFrameReceiver & | ||
| operator=(const CFIFunctionFrameReceiver &) = delete; | ||
| virtual ~CFIFunctionFrameReceiver() = default; | ||
|
|
||
| CFIFunctionFrameReceiver(MCContext &Context) : Context(Context) {} | ||
|
|
||
| MCContext &getContext() const { return Context; } | ||
|
|
||
| virtual void startFunctionFrame(bool IsEH, | ||
| ArrayRef<MCCFIInstruction> Prologue) {} | ||
| /// Instructions are processed in the program order. | ||
| virtual void | ||
| emitInstructionAndDirectives(const MCInst &Inst, | ||
| ArrayRef<MCCFIInstruction> Directives) {} | ||
| virtual void finishFunctionFrame() {} | ||
|
|
||
| private: | ||
| MCContext &Context; | ||
| }; | ||
|
|
||
| } // namespace llvm | ||
|
|
||
| #endif | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // 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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| /// | ||
| /// \file | ||
| /// This file declares CFIFunctionFrameStreamer class. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing explanation: "This is a derived class of MCStreamer which looks only at [particular parts of the output] and [does something with them]"
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added documentation |
||
| /// | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef LLVM_DWARFCFICHECKER_DWARFCFIFUNCTIONFRAMESTREAMER_H | ||
| #define LLVM_DWARFCFICHECKER_DWARFCFIFUNCTIONFRAMESTREAMER_H | ||
|
|
||
| #include "DWARFCFIFunctionFrameReceiver.h" | ||
| #include "llvm/MC/MCContext.h" | ||
| #include "llvm/MC/MCDwarf.h" | ||
| #include "llvm/MC/MCInstrInfo.h" | ||
| #include "llvm/MC/MCStreamer.h" | ||
| #include <memory> | ||
| #include <optional> | ||
|
|
||
| namespace llvm { | ||
|
|
||
| /// This class is an `MCStreamer` implementation that watches for machine | ||
| /// instructions and CFI directives. It cuts the stream into function frames and | ||
| /// channels them to `CFIFunctionFrameReceiver`. A function frame is the machine | ||
| /// instructions and CFI directives that are between `.cfi_startproc` and | ||
| /// `.cfi_endproc` directives. | ||
| class CFIFunctionFrameStreamer : public MCStreamer { | ||
| public: | ||
| CFIFunctionFrameStreamer(MCContext &Context, | ||
| std::unique_ptr<CFIFunctionFrameReceiver> Receiver) | ||
| : MCStreamer(Context), LastInstruction(std::nullopt), | ||
| Receiver(std::move(Receiver)), LastDirectiveIndex(0) { | ||
| assert(this->Receiver && "Receiver should not be null"); | ||
| } | ||
|
|
||
| bool hasRawTextSupport() const override { return true; } | ||
| void emitRawTextImpl(StringRef String) override {} | ||
|
|
||
| bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override { | ||
| return true; | ||
| } | ||
|
|
||
| void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size, | ||
| Align ByteAlignment) override {} | ||
| void emitSubsectionsViaSymbols() override {}; | ||
| void beginCOFFSymbolDef(const MCSymbol *Symbol) override {} | ||
| void emitCOFFSymbolStorageClass(int StorageClass) override {} | ||
| void emitCOFFSymbolType(int Type) override {} | ||
| void endCOFFSymbolDef() override {} | ||
| void emitXCOFFSymbolLinkageWithVisibility(MCSymbol *Symbol, | ||
| MCSymbolAttr Linkage, | ||
| MCSymbolAttr Visibility) override {} | ||
|
|
||
| void emitInstruction(const MCInst &Inst, const MCSubtargetInfo &STI) override; | ||
| void emitCFIStartProcImpl(MCDwarfFrameInfo &Frame) override; | ||
| void emitCFIEndProcImpl(MCDwarfFrameInfo &CurFrame) override; | ||
|
|
||
| private: | ||
| std::pair<unsigned, unsigned> updateDirectivesRange(); | ||
| void updateReceiver(); | ||
|
|
||
| private: | ||
| std::vector<unsigned> FrameIndices; | ||
| std::optional<MCInst> LastInstruction; | ||
| std::unique_ptr<CFIFunctionFrameReceiver> Receiver; | ||
| unsigned LastDirectiveIndex; | ||
| }; | ||
|
|
||
| } // namespace llvm | ||
|
|
||
| #endif | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // 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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| /// | ||
| /// \file | ||
| /// This file declares DWARFCFIState class. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing explanation: what is this class for? How is somebody expected to use it? Looking just at the API here, it seems strangely "write only". There's an update function to pass data in to it, and you can retrieve an iterator pointing at the current last row (if any), but how does anyone read data out of it again? It looks almost impossible. You could step through the table starting from the iterator returned from I look at this file and think "surely once you've put data in here it can't be retrieved, so what's the point?". The header comment is a good place to explain what the point is.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added documentation |
||
| /// | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef LLVM_DWARFCFICHECKER_UNWINDINFOSTATE_H | ||
| #define LLVM_DWARFCFICHECKER_UNWINDINFOSTATE_H | ||
|
|
||
| #include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h" | ||
| #include "llvm/MC/MCContext.h" | ||
| #include "llvm/MC/MCDwarf.h" | ||
| #include <optional> | ||
|
|
||
| namespace llvm { | ||
|
|
||
| using DWARFRegNum = uint32_t; | ||
|
|
||
| /// This class is used to maintain a CFI state, referred to as an unwinding row, | ||
| /// during CFI analysis. The only way to modify the state is by updating it with | ||
| /// a CFI directive. | ||
| class DWARFCFIState { | ||
| public: | ||
| DWARFCFIState(MCContext *Context) : Context(Context), IsInitiated(false) {}; | ||
|
|
||
| std::optional<dwarf::UnwindRow> getCurrentUnwindRow() const; | ||
amsen20 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /// This method updates the state by applying \p Directive to the current | ||
| /// state. If the directive is not supported by the checker or any error | ||
| /// happens while applying the CFI directive, a warning or error is reported | ||
| /// to the user, and the directive is ignored, leaving the state unchanged. | ||
| void update(const MCCFIInstruction &Directive); | ||
|
|
||
| private: | ||
| dwarf::CFIProgram convert(MCCFIInstruction Directive); | ||
|
|
||
| private: | ||
| dwarf::UnwindRow Row; | ||
| MCContext *Context; | ||
| bool IsInitiated; | ||
| }; | ||
|
|
||
| } // namespace llvm | ||
|
|
||
| #endif | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| add_llvm_component_library(LLVMDWARFCFIChecker | ||
| DWARFCFIAnalysis.cpp | ||
| DWARFCFIFunctionFrameAnalyzer.cpp | ||
| DWARFCFIFunctionFrameStreamer.cpp | ||
| DWARFCFIState.cpp | ||
|
|
||
| ADDITIONAL_HEADER_DIRS | ||
| ${LLVM_MAIN_INCLUDE_DIR}/llvm/MCA | ||
|
|
||
| LINK_COMPONENTS | ||
| MC | ||
| DebugInfoDWARF | ||
| Support | ||
| ) |
Uh oh!
There was an error while loading. Please reload this page.