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_LIBELFFILE_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_
18 #define ART_LIBELFFILE_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 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,int32_t offset,uint32_t reg_mask,int32_t reg_size)83   void ALWAYS_INLINE RelOffsetForMany(Reg reg_base,
84                                       int32_t offset,
85                                       uint32_t reg_mask,
86                                       int32_t reg_size) {
87     DCHECK(reg_size == 4 || reg_size == 8);
88     if (UNLIKELY(enabled_)) {
89       for (int i = 0; reg_mask != 0u; reg_mask >>= 1, i++) {
90         // Skip zero bits and go to the set bit.
91         int num_zeros = CTZ(reg_mask);
92         i += num_zeros;
93         reg_mask >>= num_zeros;
94         RelOffset(Reg(reg_base.num() + i), offset);
95         offset += reg_size;
96       }
97     }
98   }
99 
100   // Custom alias - unspill many registers based on bitmask.
RestoreMany(Reg reg_base,uint32_t reg_mask)101   void ALWAYS_INLINE RestoreMany(Reg reg_base, uint32_t reg_mask) {
102     if (UNLIKELY(enabled_)) {
103       for (int i = 0; reg_mask != 0u; reg_mask >>= 1, i++) {
104         // Skip zero bits and go to the set bit.
105         int num_zeros = CTZ(reg_mask);
106         i += num_zeros;
107         reg_mask >>= num_zeros;
108         Restore(Reg(reg_base.num() + i));
109       }
110     }
111   }
112 
Nop()113   void ALWAYS_INLINE Nop() {
114     if (UNLIKELY(enabled_)) {
115       this->PushUint8(DW_CFA_nop);
116     }
117   }
118 
Offset(Reg reg,int offset)119   void ALWAYS_INLINE Offset(Reg reg, int offset) {
120     if (UNLIKELY(enabled_)) {
121       ImplicitlyAdvancePC();
122       int factored_offset = FactorDataOffset(offset);  // May change sign.
123       if (factored_offset >= 0) {
124         if (0 <= reg.num() && reg.num() <= 0x3F) {
125           this->PushUint8(DW_CFA_offset | reg.num());
126           this->PushUleb128(factored_offset);
127         } else {
128           this->PushUint8(DW_CFA_offset_extended);
129           this->PushUleb128(reg.num());
130           this->PushUleb128(factored_offset);
131         }
132       } else {
133         uses_dwarf3_features_ = true;
134         this->PushUint8(DW_CFA_offset_extended_sf);
135         this->PushUleb128(reg.num());
136         this->PushSleb128(factored_offset);
137       }
138     }
139   }
140 
Restore(Reg reg)141   void ALWAYS_INLINE Restore(Reg reg) {
142     if (UNLIKELY(enabled_)) {
143       ImplicitlyAdvancePC();
144       if (0 <= reg.num() && reg.num() <= 0x3F) {
145         this->PushUint8(DW_CFA_restore | reg.num());
146       } else {
147         this->PushUint8(DW_CFA_restore_extended);
148         this->PushUleb128(reg.num());
149       }
150     }
151   }
152 
Undefined(Reg reg)153   void ALWAYS_INLINE Undefined(Reg reg) {
154     if (UNLIKELY(enabled_)) {
155       ImplicitlyAdvancePC();
156       this->PushUint8(DW_CFA_undefined);
157       this->PushUleb128(reg.num());
158     }
159   }
160 
SameValue(Reg reg)161   void ALWAYS_INLINE SameValue(Reg reg) {
162     if (UNLIKELY(enabled_)) {
163       ImplicitlyAdvancePC();
164       this->PushUint8(DW_CFA_same_value);
165       this->PushUleb128(reg.num());
166     }
167   }
168 
169   // The previous value of "reg" is stored in register "new_reg".
Register(Reg reg,Reg new_reg)170   void ALWAYS_INLINE Register(Reg reg, Reg new_reg) {
171     if (UNLIKELY(enabled_)) {
172       ImplicitlyAdvancePC();
173       this->PushUint8(DW_CFA_register);
174       this->PushUleb128(reg.num());
175       this->PushUleb128(new_reg.num());
176     }
177   }
178 
RememberState()179   void ALWAYS_INLINE RememberState() {
180     if (UNLIKELY(enabled_)) {
181       ImplicitlyAdvancePC();
182       this->PushUint8(DW_CFA_remember_state);
183     }
184   }
185 
RestoreState()186   void ALWAYS_INLINE RestoreState() {
187     if (UNLIKELY(enabled_)) {
188       ImplicitlyAdvancePC();
189       this->PushUint8(DW_CFA_restore_state);
190     }
191   }
192 
DefCFA(Reg reg,int offset)193   void ALWAYS_INLINE DefCFA(Reg reg, int offset) {
194     if (UNLIKELY(enabled_)) {
195       ImplicitlyAdvancePC();
196       if (offset >= 0) {
197         this->PushUint8(DW_CFA_def_cfa);
198         this->PushUleb128(reg.num());
199         this->PushUleb128(offset);  // Non-factored.
200       } else {
201         uses_dwarf3_features_ = true;
202         this->PushUint8(DW_CFA_def_cfa_sf);
203         this->PushUleb128(reg.num());
204         this->PushSleb128(FactorDataOffset(offset));
205       }
206     }
207     current_cfa_offset_ = offset;
208   }
209 
DefCFARegister(Reg reg)210   void ALWAYS_INLINE DefCFARegister(Reg reg) {
211     if (UNLIKELY(enabled_)) {
212       ImplicitlyAdvancePC();
213       this->PushUint8(DW_CFA_def_cfa_register);
214       this->PushUleb128(reg.num());
215     }
216   }
217 
DefCFAOffset(int offset)218   void ALWAYS_INLINE DefCFAOffset(int offset) {
219     if (UNLIKELY(enabled_)) {
220       if (current_cfa_offset_ != offset) {
221         ImplicitlyAdvancePC();
222         if (offset >= 0) {
223           this->PushUint8(DW_CFA_def_cfa_offset);
224           this->PushUleb128(offset);  // Non-factored.
225         } else {
226           uses_dwarf3_features_ = true;
227           this->PushUint8(DW_CFA_def_cfa_offset_sf);
228           this->PushSleb128(FactorDataOffset(offset));
229         }
230       }
231     }
232     // Uncoditional so that the user can still get and check the value.
233     current_cfa_offset_ = offset;
234   }
235 
ValOffset(Reg reg,int offset)236   void ALWAYS_INLINE ValOffset(Reg reg, int offset) {
237     if (UNLIKELY(enabled_)) {
238       ImplicitlyAdvancePC();
239       uses_dwarf3_features_ = true;
240       int factored_offset = FactorDataOffset(offset);  // May change sign.
241       if (factored_offset >= 0) {
242         this->PushUint8(DW_CFA_val_offset);
243         this->PushUleb128(reg.num());
244         this->PushUleb128(factored_offset);
245       } else {
246         this->PushUint8(DW_CFA_val_offset_sf);
247         this->PushUleb128(reg.num());
248         this->PushSleb128(factored_offset);
249       }
250     }
251   }
252 
DefCFAExpression(uint8_t * expr,int expr_size)253   void ALWAYS_INLINE DefCFAExpression(uint8_t* expr, int expr_size) {
254     if (UNLIKELY(enabled_)) {
255       ImplicitlyAdvancePC();
256       uses_dwarf3_features_ = true;
257       this->PushUint8(DW_CFA_def_cfa_expression);
258       this->PushUleb128(expr_size);
259       this->PushData(expr, expr_size);
260     }
261   }
262 
Expression(Reg reg,uint8_t * expr,int expr_size)263   void ALWAYS_INLINE Expression(Reg reg, uint8_t* expr, int expr_size) {
264     if (UNLIKELY(enabled_)) {
265       ImplicitlyAdvancePC();
266       uses_dwarf3_features_ = true;
267       this->PushUint8(DW_CFA_expression);
268       this->PushUleb128(reg.num());
269       this->PushUleb128(expr_size);
270       this->PushData(expr, expr_size);
271     }
272   }
273 
ValExpression(Reg reg,uint8_t * expr,int expr_size)274   void ALWAYS_INLINE ValExpression(Reg reg, uint8_t* expr, int expr_size) {
275     if (UNLIKELY(enabled_)) {
276       ImplicitlyAdvancePC();
277       uses_dwarf3_features_ = true;
278       this->PushUint8(DW_CFA_val_expression);
279       this->PushUleb128(reg.num());
280       this->PushUleb128(expr_size);
281       this->PushData(expr, expr_size);
282     }
283   }
284 
IsEnabled()285   bool IsEnabled() const { return enabled_; }
286 
SetEnabled(bool value)287   void SetEnabled(bool value) {
288     enabled_ = value;
289     if (enabled_ && opcodes_.capacity() == 0u) {
290       opcodes_.reserve(kDefaultCapacity);
291     }
292   }
293 
GetCurrentPC()294   int GetCurrentPC() const { return current_pc_; }
295 
GetCurrentCFAOffset()296   int GetCurrentCFAOffset() const { return current_cfa_offset_; }
297 
SetCurrentCFAOffset(int offset)298   void SetCurrentCFAOffset(int offset) { current_cfa_offset_ = offset; }
299 
300   using Writer<Vector>::data;
301 
302   explicit DebugFrameOpCodeWriter(bool enabled = true,
303                                   const typename Vector::allocator_type& alloc =
304                                       typename Vector::allocator_type())
305       : Writer<Vector>(&opcodes_),
306         enabled_(false),
307         opcodes_(alloc),
308         current_cfa_offset_(0),
309         current_pc_(0),
310         uses_dwarf3_features_(false) {
311     SetEnabled(enabled);
312   }
313 
~DebugFrameOpCodeWriter()314   virtual ~DebugFrameOpCodeWriter() { }
315 
316  protected:
317   // Best guess based on couple of observed outputs.
318   static constexpr size_t kDefaultCapacity = 32u;
319 
FactorDataOffset(int offset)320   int FactorDataOffset(int offset) const {
321     DCHECK_EQ(offset % kDataAlignmentFactor, 0);
322     return offset / kDataAlignmentFactor;
323   }
324 
FactorCodeOffset(int offset)325   int FactorCodeOffset(int offset) const {
326     DCHECK_EQ(offset % kCodeAlignmentFactor, 0);
327     return offset / kCodeAlignmentFactor;
328   }
329 
330   bool enabled_;  // If disabled all writes are no-ops.
331   Vector opcodes_;
332   int current_cfa_offset_;
333   int current_pc_;
334   bool uses_dwarf3_features_;
335 
336  private:
337   DISALLOW_COPY_AND_ASSIGN(DebugFrameOpCodeWriter);
338 };
339 
340 }  // namespace dwarf
341 }  // namespace art
342 
343 #endif  // ART_LIBELFFILE_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_
344