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