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