Line data Source code
1 : // Copyright 2016 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #ifndef V8_EH_FRAME_H_
6 : #define V8_EH_FRAME_H_
7 :
8 : #include "src/base/compiler-specific.h"
9 : #include "src/globals.h"
10 : #include "src/macro-assembler.h"
11 : #include "src/zone/zone-containers.h"
12 :
13 : namespace v8 {
14 : namespace internal {
15 :
16 : class V8_EXPORT_PRIVATE EhFrameConstants final
17 : : public NON_EXPORTED_BASE(AllStatic) {
18 : public:
19 : enum class DwarfOpcodes : byte {
20 : kNop = 0x00,
21 : kAdvanceLoc1 = 0x02,
22 : kAdvanceLoc2 = 0x03,
23 : kAdvanceLoc4 = 0x04,
24 : kSameValue = 0x08,
25 : kDefCfa = 0x0c,
26 : kDefCfaRegister = 0x0d,
27 : kDefCfaOffset = 0x0e,
28 : kOffsetExtendedSf = 0x11,
29 : };
30 :
31 : enum DwarfEncodingSpecifiers : byte {
32 : kUData4 = 0x03,
33 : kSData4 = 0x0b,
34 : kPcRel = 0x10,
35 : kDataRel = 0x30,
36 : kOmit = 0xff,
37 : };
38 :
39 : static const int kLocationTag = 1;
40 : static const int kLocationMask = 0x3f;
41 : static const int kLocationMaskSize = 6;
42 :
43 : static const int kSavedRegisterTag = 2;
44 : static const int kSavedRegisterMask = 0x3f;
45 : static const int kSavedRegisterMaskSize = 6;
46 :
47 : static const int kFollowInitialRuleTag = 3;
48 : static const int kFollowInitialRuleMask = 0x3f;
49 : static const int kFollowInitialRuleMaskSize = 6;
50 :
51 : static const int kProcedureAddressOffsetInFde = 2 * kInt32Size;
52 : static const int kProcedureSizeOffsetInFde = 3 * kInt32Size;
53 :
54 : static const int kInitialStateOffsetInCie = 19;
55 : static const int kEhFrameTerminatorSize = 4;
56 :
57 : // Defined in eh-writer-<arch>.cc
58 : static const int kCodeAlignmentFactor;
59 : static const int kDataAlignmentFactor;
60 :
61 : static const int kFdeVersionSize = 1;
62 : static const int kFdeEncodingSpecifiersSize = 3;
63 :
64 : static const int kEhFrameHdrVersion = 1;
65 : static const int kEhFrameHdrSize = 20;
66 : };
67 :
68 : class V8_EXPORT_PRIVATE EhFrameWriter {
69 : public:
70 : explicit EhFrameWriter(Zone* zone);
71 :
72 : // The empty frame is a hack to trigger fp-based unwinding in Linux perf
73 : // compiled with libunwind support when processing DWARF-based call graphs.
74 : //
75 : // It is effectively a valid eh_frame_hdr with an empty look up table.
76 : //
77 : static void WriteEmptyEhFrame(std::ostream& stream); // NOLINT
78 :
79 : // Write the CIE and FDE header. Call it before any other method.
80 : void Initialize();
81 :
82 : void AdvanceLocation(int pc_offset);
83 :
84 : // The <base_address> is the one to which all <offset>s in SaveRegisterToStack
85 : // directives are relative. It is given by <base_register> + <base_offset>.
86 : //
87 : // The <base_offset> must be positive or 0.
88 : //
89 : void SetBaseAddressRegister(Register base_register);
90 : void SetBaseAddressOffset(int base_offset);
91 : void IncreaseBaseAddressOffset(int base_delta) {
92 46 : SetBaseAddressOffset(base_offset_ + base_delta);
93 : }
94 : void SetBaseAddressRegisterAndOffset(Register base_register, int base_offset);
95 :
96 : // Register saved at location <base_address> + <offset>.
97 : // The <offset> must be a multiple of EhFrameConstants::kDataAlignment.
98 : void RecordRegisterSavedToStack(Register name, int offset) {
99 23 : RecordRegisterSavedToStack(RegisterToDwarfCode(name), offset);
100 : }
101 :
102 : // The register has not been modified from the previous frame.
103 : void RecordRegisterNotModified(Register name);
104 :
105 : // The register follows the rule defined in the CIE.
106 : void RecordRegisterFollowsInitialRule(Register name);
107 :
108 : void Finish(int code_size);
109 :
110 : // Remember to call Finish() before GetEhFrame().
111 : //
112 : // The EhFrameWriter instance owns the buffer pointed by
113 : // CodeDesc::unwinding_info, and must outlive any use of the CodeDesc.
114 : //
115 : void GetEhFrame(CodeDesc* desc);
116 :
117 : int last_pc_offset() const { return last_pc_offset_; }
118 : Register base_register() const { return base_register_; }
119 : int base_offset() const { return base_offset_; }
120 :
121 : private:
122 : enum class InternalState { kUndefined, kInitialized, kFinalized };
123 :
124 : static const uint32_t kInt32Placeholder = 0xdeadc0de;
125 :
126 : void WriteSLeb128(int32_t value);
127 : void WriteULeb128(uint32_t value);
128 :
129 881 : void WriteByte(byte value) { eh_frame_buffer_.push_back(value); }
130 : void WriteOpcode(EhFrameConstants::DwarfOpcodes opcode) {
131 : WriteByte(static_cast<byte>(opcode));
132 : }
133 : void WriteBytes(const byte* start, int size) {
134 259 : eh_frame_buffer_.insert(eh_frame_buffer_.end(), start, start + size);
135 : }
136 : void WriteInt16(uint16_t value) {
137 : WriteBytes(reinterpret_cast<const byte*>(&value), sizeof(value));
138 : }
139 : void WriteInt32(uint32_t value) {
140 : WriteBytes(reinterpret_cast<const byte*>(&value), sizeof(value));
141 : }
142 : void PatchInt32(int base_offset, uint32_t value) {
143 : DCHECK_EQ(ReadUnalignedUInt32(eh_frame_buffer_.data() + base_offset),
144 : kInt32Placeholder);
145 : DCHECK_LT(base_offset + kInt32Size, eh_frame_offset());
146 148 : WriteUnalignedUInt32(eh_frame_buffer_.data() + base_offset, value);
147 : }
148 :
149 : // Write the common information entry, which includes encoding specifiers,
150 : // alignment factors, the return address (pseudo) register code and the
151 : // directives to construct the initial state of the unwinding table.
152 : void WriteCie();
153 :
154 : // Write the header of the function data entry, containing a pointer to the
155 : // correspondent CIE and the position and size of the associated routine.
156 : void WriteFdeHeader();
157 :
158 : // Write the contents of the .eh_frame_hdr section, including encoding
159 : // specifiers and the routine => FDE lookup table.
160 : void WriteEhFrameHdr(int code_size);
161 :
162 : // Write nops until the size reaches a multiple of 8 bytes.
163 : void WritePaddingToAlignedSize(int unpadded_size);
164 :
165 : // Internal version that directly accepts a DWARF register code, needed for
166 : // handling pseudo-registers on some platforms.
167 : void RecordRegisterSavedToStack(int register_code, int offset);
168 :
169 37 : int GetProcedureAddressOffset() const {
170 37 : return fde_offset() + EhFrameConstants::kProcedureAddressOffsetInFde;
171 : }
172 :
173 37 : int GetProcedureSizeOffset() const {
174 37 : return fde_offset() + EhFrameConstants::kProcedureSizeOffsetInFde;
175 : }
176 :
177 : int eh_frame_offset() const {
178 518 : return static_cast<int>(eh_frame_buffer_.size());
179 : }
180 :
181 : int fde_offset() const { return cie_size_; }
182 :
183 : // Platform specific functions implemented in eh-frame-<arch>.cc
184 :
185 : static int RegisterToDwarfCode(Register name);
186 :
187 : // Write directives to build the initial state in the CIE.
188 : void WriteInitialStateInCie();
189 :
190 : // Write the return address (pseudo) register code.
191 : void WriteReturnAddressRegisterCode();
192 :
193 : int cie_size_;
194 : int last_pc_offset_;
195 : InternalState writer_state_;
196 : Register base_register_;
197 : int base_offset_;
198 : ZoneVector<byte> eh_frame_buffer_;
199 :
200 : DISALLOW_COPY_AND_ASSIGN(EhFrameWriter);
201 : };
202 :
203 : class V8_EXPORT_PRIVATE EhFrameIterator {
204 : public:
205 : EhFrameIterator(const byte* start, const byte* end)
206 : : start_(start), next_(start), end_(end) {
207 : DCHECK_LE(start, end);
208 : }
209 :
210 : void SkipCie() {
211 : DCHECK_EQ(next_, start_);
212 : next_ += ReadUnalignedUInt32(next_) + kInt32Size;
213 : }
214 :
215 : void SkipToFdeDirectives() {
216 : SkipCie();
217 : // Skip the FDE header.
218 : Skip(kDirectivesOffsetInFde);
219 : }
220 :
221 : void Skip(int how_many) {
222 : DCHECK_GE(how_many, 0);
223 : next_ += how_many;
224 : DCHECK_LE(next_, end_);
225 : }
226 :
227 : uint32_t GetNextUInt32() { return GetNextValue<uint32_t>(); }
228 : uint16_t GetNextUInt16() { return GetNextValue<uint16_t>(); }
229 : byte GetNextByte() { return GetNextValue<byte>(); }
230 : EhFrameConstants::DwarfOpcodes GetNextOpcode() {
231 : return static_cast<EhFrameConstants::DwarfOpcodes>(GetNextByte());
232 : }
233 :
234 : uint32_t GetNextULeb128();
235 : int32_t GetNextSLeb128();
236 :
237 : bool Done() const {
238 : DCHECK_LE(next_, end_);
239 : return next_ == end_;
240 : }
241 :
242 : int GetCurrentOffset() const {
243 : DCHECK_GE(next_, start_);
244 : return static_cast<int>(next_ - start_);
245 : }
246 :
247 : int GetBufferSize() { return static_cast<int>(end_ - start_); }
248 :
249 : const void* current_address() const {
250 : return reinterpret_cast<const void*>(next_);
251 : }
252 :
253 : private:
254 : static const int kDirectivesOffsetInFde = 4 * kInt32Size + 1;
255 :
256 : static uint32_t DecodeULeb128(const byte* encoded, int* encoded_size);
257 : static int32_t DecodeSLeb128(const byte* encoded, int* encoded_size);
258 :
259 : template <typename T>
260 : T GetNextValue() {
261 : T result;
262 : DCHECK_LE(next_ + sizeof(result), end_);
263 : result = ReadUnalignedValue<T>(next_);
264 : next_ += sizeof(result);
265 : return result;
266 : }
267 :
268 : const byte* start_;
269 : const byte* next_;
270 : const byte* end_;
271 : };
272 :
273 : #ifdef ENABLE_DISASSEMBLER
274 :
275 : class EhFrameDisassembler final {
276 : public:
277 : EhFrameDisassembler(const byte* start, const byte* end)
278 : : start_(start), end_(end) {
279 : DCHECK_LT(start, end);
280 : }
281 :
282 : void DisassembleToStream(std::ostream& stream); // NOLINT
283 :
284 : private:
285 : static void DumpDwarfDirectives(std::ostream& stream, // NOLINT
286 : const byte* start, const byte* end);
287 :
288 : static const char* DwarfRegisterCodeToString(int code);
289 :
290 : const byte* start_;
291 : const byte* end_;
292 :
293 : DISALLOW_COPY_AND_ASSIGN(EhFrameDisassembler);
294 : };
295 :
296 : #endif
297 :
298 : } // namespace internal
299 : } // namespace v8
300 :
301 : #endif
|