1 | //===---- RuntimeDyldChecker.h - RuntimeDyld tester framework -----*- C++ -*-=// |
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-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #ifndef LLVM_EXECUTIONENGINE_RUNTIMEDYLDCHECKER_H |
10 | #define LLVM_EXECUTIONENGINE_RUNTIMEDYLDCHECKER_H |
11 | |
12 | #include "llvm/ExecutionEngine/JITSymbol.h" |
13 | #include "llvm/Support/Endian.h" |
14 | #include "llvm/TargetParser/SubtargetFeature.h" |
15 | #include "llvm/TargetParser/Triple.h" |
16 | #include <optional> |
17 | |
18 | #include <cstdint> |
19 | #include <memory> |
20 | #include <string> |
21 | #include <utility> |
22 | |
23 | namespace llvm { |
24 | |
25 | class StringRef; |
26 | class MCDisassembler; |
27 | class MemoryBuffer; |
28 | class MCInstPrinter; |
29 | class RuntimeDyld; |
30 | class RuntimeDyldCheckerImpl; |
31 | class raw_ostream; |
32 | |
33 | /// Holds target-specific properties for a symbol. |
34 | using TargetFlagsType = uint8_t; |
35 | |
36 | /// RuntimeDyld invariant checker for verifying that RuntimeDyld has |
37 | /// correctly applied relocations. |
38 | /// |
39 | /// The RuntimeDyldChecker class evaluates expressions against an attached |
40 | /// RuntimeDyld instance to verify that relocations have been applied |
41 | /// correctly. |
42 | /// |
43 | /// The expression language supports basic pointer arithmetic and bit-masking, |
44 | /// and has limited disassembler integration for accessing instruction |
45 | /// operands and the next PC (program counter) address for each instruction. |
46 | /// |
47 | /// The language syntax is: |
48 | /// |
49 | /// check = expr '=' expr |
50 | /// |
51 | /// expr = binary_expr |
52 | /// | sliceable_expr |
53 | /// |
54 | /// sliceable_expr = '*{' number '}' load_addr_expr [slice] |
55 | /// | '(' expr ')' [slice] |
56 | /// | ident_expr [slice] |
57 | /// | number [slice] |
58 | /// |
59 | /// slice = '[' high-bit-index ':' low-bit-index ']' |
60 | /// |
61 | /// load_addr_expr = symbol |
62 | /// | '(' symbol '+' number ')' |
63 | /// | '(' symbol '-' number ')' |
64 | /// |
65 | /// ident_expr = 'decode_operand' '(' symbol ',' operand-index ')' |
66 | /// | 'next_pc' '(' symbol ')' |
67 | /// | 'stub_addr' '(' stub-container-name ',' symbol ')' |
68 | /// | 'got_addr' '(' stub-container-name ',' symbol ')' |
69 | /// | 'section_addr' '(' stub-container-name ',' symbol ')' |
70 | /// | symbol |
71 | /// |
72 | /// binary_expr = expr '+' expr |
73 | /// | expr '-' expr |
74 | /// | expr '&' expr |
75 | /// | expr '|' expr |
76 | /// | expr '<<' expr |
77 | /// | expr '>>' expr |
78 | /// |
79 | class RuntimeDyldChecker { |
80 | public: |
81 | class MemoryRegionInfo { |
82 | public: |
83 | MemoryRegionInfo() = default; |
84 | |
85 | /// Constructor for symbols/sections with content and TargetFlag. |
86 | MemoryRegionInfo(ArrayRef<char> Content, JITTargetAddress TargetAddress, |
87 | TargetFlagsType TargetFlags) |
88 | : ContentPtr(Content.data()), Size(Content.size()), |
89 | TargetAddress(TargetAddress), TargetFlags(TargetFlags) {} |
90 | |
91 | /// Constructor for zero-fill symbols/sections. |
92 | MemoryRegionInfo(uint64_t Size, JITTargetAddress TargetAddress) |
93 | : Size(Size), TargetAddress(TargetAddress) {} |
94 | |
95 | /// Returns true if this is a zero-fill symbol/section. |
96 | bool isZeroFill() const { |
97 | assert(Size && "setContent/setZeroFill must be called first" ); |
98 | return !ContentPtr; |
99 | } |
100 | |
101 | /// Set the content for this memory region. |
102 | void setContent(ArrayRef<char> Content) { |
103 | assert(!ContentPtr && !Size && "Content/zero-fill already set" ); |
104 | ContentPtr = Content.data(); |
105 | Size = Content.size(); |
106 | } |
107 | |
108 | /// Set a zero-fill length for this memory region. |
109 | void setZeroFill(uint64_t Size) { |
110 | assert(!ContentPtr && !this->Size && "Content/zero-fill already set" ); |
111 | this->Size = Size; |
112 | } |
113 | |
114 | /// Returns the content for this section if there is any. |
115 | ArrayRef<char> getContent() const { |
116 | assert(!isZeroFill() && "Can't get content for a zero-fill section" ); |
117 | return {ContentPtr, static_cast<size_t>(Size)}; |
118 | } |
119 | |
120 | /// Returns the zero-fill length for this section. |
121 | uint64_t getZeroFillLength() const { |
122 | assert(isZeroFill() && "Can't get zero-fill length for content section" ); |
123 | return Size; |
124 | } |
125 | |
126 | /// Set the target address for this region. |
127 | void setTargetAddress(JITTargetAddress TargetAddress) { |
128 | assert(!this->TargetAddress && "TargetAddress already set" ); |
129 | this->TargetAddress = TargetAddress; |
130 | } |
131 | |
132 | /// Return the target address for this region. |
133 | JITTargetAddress getTargetAddress() const { return TargetAddress; } |
134 | |
135 | /// Get the target flags for this Symbol. |
136 | TargetFlagsType getTargetFlags() const { return TargetFlags; } |
137 | |
138 | /// Set the target flags for this Symbol. |
139 | void setTargetFlags(TargetFlagsType Flags) { |
140 | assert(Flags <= 1 && "Add more bits to store more than one flag" ); |
141 | TargetFlags = Flags; |
142 | } |
143 | |
144 | private: |
145 | const char *ContentPtr = nullptr; |
146 | uint64_t Size = 0; |
147 | JITTargetAddress TargetAddress = 0; |
148 | TargetFlagsType TargetFlags = 0; |
149 | }; |
150 | |
151 | using IsSymbolValidFunction = std::function<bool(StringRef Symbol)>; |
152 | using GetSymbolInfoFunction = |
153 | std::function<Expected<MemoryRegionInfo>(StringRef SymbolName)>; |
154 | using GetSectionInfoFunction = std::function<Expected<MemoryRegionInfo>( |
155 | StringRef FileName, StringRef SectionName)>; |
156 | using GetStubInfoFunction = std::function<Expected<MemoryRegionInfo>( |
157 | StringRef StubContainer, StringRef TargetName, StringRef StubKindFilter)>; |
158 | using GetGOTInfoFunction = std::function<Expected<MemoryRegionInfo>( |
159 | StringRef GOTContainer, StringRef TargetName)>; |
160 | |
161 | RuntimeDyldChecker(IsSymbolValidFunction IsSymbolValid, |
162 | GetSymbolInfoFunction GetSymbolInfo, |
163 | GetSectionInfoFunction GetSectionInfo, |
164 | GetStubInfoFunction GetStubInfo, |
165 | GetGOTInfoFunction GetGOTInfo, llvm::endianness Endianness, |
166 | Triple TT, StringRef CPU, SubtargetFeatures TF, |
167 | raw_ostream &ErrStream); |
168 | ~RuntimeDyldChecker(); |
169 | |
170 | /// Check a single expression against the attached RuntimeDyld |
171 | /// instance. |
172 | bool check(StringRef CheckExpr) const; |
173 | |
174 | /// Scan the given memory buffer for lines beginning with the string |
175 | /// in RulePrefix. The remainder of the line is passed to the check |
176 | /// method to be evaluated as an expression. |
177 | bool checkAllRulesInBuffer(StringRef RulePrefix, MemoryBuffer *MemBuf) const; |
178 | |
179 | /// Returns the address of the requested section (or an error message |
180 | /// in the second element of the pair if the address cannot be found). |
181 | /// |
182 | /// if 'LocalAddress' is true, this returns the address of the section |
183 | /// within the linker's memory. If 'LocalAddress' is false it returns the |
184 | /// address within the target process (i.e. the load address). |
185 | std::pair<uint64_t, std::string> getSectionAddr(StringRef FileName, |
186 | StringRef SectionName, |
187 | bool LocalAddress); |
188 | |
189 | /// If there is a section at the given local address, return its load |
190 | /// address, otherwise return std::nullopt. |
191 | std::optional<uint64_t> getSectionLoadAddress(void *LocalAddress) const; |
192 | |
193 | private: |
194 | std::unique_ptr<RuntimeDyldCheckerImpl> Impl; |
195 | }; |
196 | |
197 | } // end namespace llvm |
198 | |
199 | #endif |
200 | |