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