1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef ART_COMPILER_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_
18 #define ART_COMPILER_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_
19 
20 #include "base/bit_utils.h"
21 #include "dwarf/dwarf_constants.h"
22 #include "dwarf/register.h"
23 #include "dwarf/writer.h"
24 
25 namespace art {
26 namespace dwarf {
27 
28 // Writer for .debug_frame opcodes (DWARF-3).
29 // See the DWARF specification for the precise meaning of the opcodes.
30 // The writer is very light-weight, however it will do the following for you:
31 //  * Choose the most compact encoding of a given opcode.
32 //  * Keep track of current state and convert absolute values to deltas.
33 //  * Divide by header-defined factors as appropriate.
34 template<typename Allocator = std::allocator<uint8_t> >
35 class DebugFrameOpCodeWriter : private Writer<Allocator> {
36  public:
37   // To save space, DWARF divides most offsets by header-defined factors.
38   // They are used in integer divisions, so we make them constants.
39   // We usually subtract from stack base pointer, so making the factor
40   // negative makes the encoded values positive and thus easier to encode.
41   static constexpr int kDataAlignmentFactor = -4;
42   static constexpr int kCodeAlignmentFactor = 1;
43 
44   // Explicitely advance the program counter to given location.
AdvancePC(int absolute_pc)45   void ALWAYS_INLINE AdvancePC(int absolute_pc) {
46     DCHECK_GE(absolute_pc, current_pc_);
47     if (UNLIKELY(enabled_)) {
48       int delta = FactorCodeOffset(absolute_pc - current_pc_);
49       if (delta != 0) {
50         if (delta <= 0x3F) {
51           this->PushUint8(DW_CFA_advance_loc | delta);
52         } else if (delta <= UINT8_MAX) {
53           this->PushUint8(DW_CFA_advance_loc1);
54           this->PushUint8(delta);
55         } else if (delta <= UINT16_MAX) {
56           this->PushUint8(DW_CFA_advance_loc2);
57           this->PushUint16(delta);
58         } else {
59           this->PushUint8(DW_CFA_advance_loc4);
60           this->PushUint32(delta);
61         }
62       }
63       current_pc_ = absolute_pc;
64     }
65   }
66 
67   // Override this method to automatically advance the PC before each opcode.
ImplicitlyAdvancePC()68   virtual void ImplicitlyAdvancePC() { }
69 
70   // Common alias in assemblers - spill relative to current stack pointer.
RelOffset(Reg reg,int offset)71   void ALWAYS_INLINE RelOffset(Reg reg, int offset) {
72     Offset(reg, offset - current_cfa_offset_);
73   }
74 
75   // Common alias in assemblers - increase stack frame size.
AdjustCFAOffset(int delta)76   void ALWAYS_INLINE AdjustCFAOffset(int delta) {
77     DefCFAOffset(current_cfa_offset_ + delta);
78   }
79 
80   // Custom alias - spill many registers based on bitmask.
RelOffsetForMany(Reg reg_base,int offset,uint32_t reg_mask,int reg_size)81   void ALWAYS_INLINE RelOffsetForMany(Reg reg_base, int offset,
82                                       uint32_t reg_mask, int reg_size) {
83     DCHECK(reg_size == 4 || reg_size == 8);
84     if (UNLIKELY(enabled_)) {
85       for (int i = 0; reg_mask != 0u; reg_mask >>= 1, i++) {
86         // Skip zero bits and go to the set bit.
87         int num_zeros = CTZ(reg_mask);
88         i += num_zeros;
89         reg_mask >>= num_zeros;
90         RelOffset(Reg(reg_base.num() + i), offset);
91         offset += reg_size;
92       }
93     }
94   }
95 
96   // Custom alias - unspill many registers based on bitmask.
RestoreMany(Reg reg_base,uint32_t reg_mask)97   void ALWAYS_INLINE RestoreMany(Reg reg_base, uint32_t reg_mask) {
98     if (UNLIKELY(enabled_)) {
99       for (int i = 0; reg_mask != 0u; reg_mask >>= 1, i++) {
100         // Skip zero bits and go to the set bit.
101         int num_zeros = CTZ(reg_mask);
102         i += num_zeros;
103         reg_mask >>= num_zeros;
104         Restore(Reg(reg_base.num() + i));
105       }
106     }
107   }
108 
Nop()109   void ALWAYS_INLINE Nop() {
110     if (UNLIKELY(enabled_)) {
111       this->PushUint8(DW_CFA_nop);
112     }
113   }
114 
Offset(Reg reg,int offset)115   void ALWAYS_INLINE Offset(Reg reg, int offset) {
116     if (UNLIKELY(enabled_)) {
117       ImplicitlyAdvancePC();
118       int factored_offset = FactorDataOffset(offset);  // May change sign.
119       if (factored_offset >= 0) {
120         if (0 <= reg.num() && reg.num() <= 0x3F) {
121           this->PushUint8(DW_CFA_offset | reg.num());
122           this->PushUleb128(factored_offset);
123         } else {
124           this->PushUint8(DW_CFA_offset_extended);
125           this->PushUleb128(reg.num());
126           this->PushUleb128(factored_offset);
127         }
128       } else {
129         uses_dwarf3_features_ = true;
130         this->PushUint8(DW_CFA_offset_extended_sf);
131         this->PushUleb128(reg.num());
132         this->PushSleb128(factored_offset);
133       }
134     }
135   }
136 
Restore(Reg reg)137   void ALWAYS_INLINE Restore(Reg reg) {
138     if (UNLIKELY(enabled_)) {
139       ImplicitlyAdvancePC();
140       if (0 <= reg.num() && reg.num() <= 0x3F) {
141         this->PushUint8(DW_CFA_restore | reg.num());
142       } else {
143         this->PushUint8(DW_CFA_restore_extended);
144         this->PushUleb128(reg.num());
145       }
146     }
147   }
148 
Undefined(Reg reg)149   void ALWAYS_INLINE Undefined(Reg reg) {
150     if (UNLIKELY(enabled_)) {
151       ImplicitlyAdvancePC();
152       this->PushUint8(DW_CFA_undefined);
153       this->PushUleb128(reg.num());
154     }
155   }
156 
SameValue(Reg reg)157   void ALWAYS_INLINE SameValue(Reg reg) {
158     if (UNLIKELY(enabled_)) {
159       ImplicitlyAdvancePC();
160       this->PushUint8(DW_CFA_same_value);
161       this->PushUleb128(reg.num());
162     }
163   }
164 
165   // The previous value of "reg" is stored in register "new_reg".
Register(Reg reg,Reg new_reg)166   void ALWAYS_INLINE Register(Reg reg, Reg new_reg) {
167     if (UNLIKELY(enabled_)) {
168       ImplicitlyAdvancePC();
169       this->PushUint8(DW_CFA_register);
170       this->PushUleb128(reg.num());
171       this->PushUleb128(new_reg.num());
172     }
173   }
174 
RememberState()175   void ALWAYS_INLINE RememberState() {
176     if (UNLIKELY(enabled_)) {
177       ImplicitlyAdvancePC();
178       this->PushUint8(DW_CFA_remember_state);
179     }
180   }
181 
RestoreState()182   void ALWAYS_INLINE RestoreState() {
183     if (UNLIKELY(enabled_)) {
184       ImplicitlyAdvancePC();
185       this->PushUint8(DW_CFA_restore_state);
186     }
187   }
188 
DefCFA(Reg reg,int offset)189   void ALWAYS_INLINE DefCFA(Reg reg, int offset) {
190     if (UNLIKELY(enabled_)) {
191       ImplicitlyAdvancePC();
192       if (offset >= 0) {
193         this->PushUint8(DW_CFA_def_cfa);
194         this->PushUleb128(reg.num());
195         this->PushUleb128(offset);  // Non-factored.
196       } else {
197         uses_dwarf3_features_ = true;
198         this->PushUint8(DW_CFA_def_cfa_sf);
199         this->PushUleb128(reg.num());
200         this->PushSleb128(FactorDataOffset(offset));
201       }
202     }
203     current_cfa_offset_ = offset;
204   }
205 
DefCFARegister(Reg reg)206   void ALWAYS_INLINE DefCFARegister(Reg reg) {
207     if (UNLIKELY(enabled_)) {
208       ImplicitlyAdvancePC();
209       this->PushUint8(DW_CFA_def_cfa_register);
210       this->PushUleb128(reg.num());
211     }
212   }
213 
DefCFAOffset(int offset)214   void ALWAYS_INLINE DefCFAOffset(int offset) {
215     if (UNLIKELY(enabled_)) {
216       if (current_cfa_offset_ != offset) {
217         ImplicitlyAdvancePC();
218         if (offset >= 0) {
219           this->PushUint8(DW_CFA_def_cfa_offset);
220           this->PushUleb128(offset);  // Non-factored.
221         } else {
222           uses_dwarf3_features_ = true;
223           this->PushUint8(DW_CFA_def_cfa_offset_sf);
224           this->PushSleb128(FactorDataOffset(offset));
225         }
226       }
227     }
228     // Uncoditional so that the user can still get and check the value.
229     current_cfa_offset_ = offset;
230   }
231 
ValOffset(Reg reg,int offset)232   void ALWAYS_INLINE ValOffset(Reg reg, int offset) {
233     if (UNLIKELY(enabled_)) {
234       ImplicitlyAdvancePC();
235       uses_dwarf3_features_ = true;
236       int factored_offset = FactorDataOffset(offset);  // May change sign.
237       if (factored_offset >= 0) {
238         this->PushUint8(DW_CFA_val_offset);
239         this->PushUleb128(reg.num());
240         this->PushUleb128(factored_offset);
241       } else {
242         this->PushUint8(DW_CFA_val_offset_sf);
243         this->PushUleb128(reg.num());
244         this->PushSleb128(factored_offset);
245       }
246     }
247   }
248 
DefCFAExpression(void * expr,int expr_size)249   void ALWAYS_INLINE DefCFAExpression(void * expr, int expr_size) {
250     if (UNLIKELY(enabled_)) {
251       ImplicitlyAdvancePC();
252       uses_dwarf3_features_ = true;
253       this->PushUint8(DW_CFA_def_cfa_expression);
254       this->PushUleb128(expr_size);
255       this->PushData(expr, expr_size);
256     }
257   }
258 
Expression(Reg reg,void * expr,int expr_size)259   void ALWAYS_INLINE Expression(Reg reg, void * expr, int expr_size) {
260     if (UNLIKELY(enabled_)) {
261       ImplicitlyAdvancePC();
262       uses_dwarf3_features_ = true;
263       this->PushUint8(DW_CFA_expression);
264       this->PushUleb128(reg.num());
265       this->PushUleb128(expr_size);
266       this->PushData(expr, expr_size);
267     }
268   }
269 
ValExpression(Reg reg,void * expr,int expr_size)270   void ALWAYS_INLINE ValExpression(Reg reg, void * expr, int expr_size) {
271     if (UNLIKELY(enabled_)) {
272       ImplicitlyAdvancePC();
273       uses_dwarf3_features_ = true;
274       this->PushUint8(DW_CFA_val_expression);
275       this->PushUleb128(reg.num());
276       this->PushUleb128(expr_size);
277       this->PushData(expr, expr_size);
278     }
279   }
280 
IsEnabled()281   bool IsEnabled() const { return enabled_; }
282 
SetEnabled(bool value)283   void SetEnabled(bool value) { enabled_ = value; }
284 
GetCurrentPC()285   int GetCurrentPC() const { return current_pc_; }
286 
GetCurrentCFAOffset()287   int GetCurrentCFAOffset() const { return current_cfa_offset_; }
288 
SetCurrentCFAOffset(int offset)289   void SetCurrentCFAOffset(int offset) { current_cfa_offset_ = offset; }
290 
291   using Writer<Allocator>::data;
292 
293   DebugFrameOpCodeWriter(bool enabled = true,
294                          const Allocator& alloc = Allocator())
295       : Writer<Allocator>(&opcodes_),
296         enabled_(enabled),
297         opcodes_(alloc),
298         current_cfa_offset_(0),
299         current_pc_(0),
300         uses_dwarf3_features_(false) {
301     if (enabled) {
302       // Best guess based on couple of observed outputs.
303       opcodes_.reserve(16);
304     }
305   }
306 
~DebugFrameOpCodeWriter()307   virtual ~DebugFrameOpCodeWriter() { }
308 
309  protected:
FactorDataOffset(int offset)310   int FactorDataOffset(int offset) const {
311     DCHECK_EQ(offset % kDataAlignmentFactor, 0);
312     return offset / kDataAlignmentFactor;
313   }
314 
FactorCodeOffset(int offset)315   int FactorCodeOffset(int offset) const {
316     DCHECK_EQ(offset % kCodeAlignmentFactor, 0);
317     return offset / kCodeAlignmentFactor;
318   }
319 
320   bool enabled_;  // If disabled all writes are no-ops.
321   std::vector<uint8_t, Allocator> opcodes_;
322   int current_cfa_offset_;
323   int current_pc_;
324   bool uses_dwarf3_features_;
325 
326  private:
327   DISALLOW_COPY_AND_ASSIGN(DebugFrameOpCodeWriter);
328 };
329 
330 }  // namespace dwarf
331 }  // namespace art
332 
333 #endif  // ART_COMPILER_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_
334