blob: 65ca6bf1f49d2f79ed329689adb298395f3eaf48 [file] [log] [blame]
David Srbecky15c19752015-03-31 14:53:55 +00001/*
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
David Srbecky2faab002019-02-12 16:35:48 +000017#ifndef ART_LIBELFFILE_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_
18#define ART_LIBELFFILE_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_
David Srbecky15c19752015-03-31 14:53:55 +000019
Vladimir Marko80afd022015-05-19 18:08:00 +010020#include "base/bit_utils.h"
David Srbecky2faab002019-02-12 16:35:48 +000021#include "dwarf/dwarf_constants.h"
22#include "dwarf/register.h"
23#include "dwarf/writer.h"
David Srbecky15c19752015-03-31 14:53:55 +000024
25namespace art {
26namespace 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.
Vladimir Markoec7802a2015-10-01 20:57:57 +010034template<typename Vector = std::vector<uint8_t> >
35class DebugFrameOpCodeWriter : private Writer<Vector> {
36 static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type");
37
David Srbecky15c19752015-03-31 14:53:55 +000038 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.
David Srbeckyc6b4dd82015-04-07 20:32:43 +010047 void ALWAYS_INLINE AdvancePC(int absolute_pc) {
David Srbecky15c19752015-03-31 14:53:55 +000048 DCHECK_GE(absolute_pc, current_pc_);
David Srbeckyc6b4dd82015-04-07 20:32:43 +010049 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 }
David Srbecky15c19752015-03-31 14:53:55 +000064 }
David Srbeckyc6b4dd82015-04-07 20:32:43 +010065 current_pc_ = absolute_pc;
David Srbecky15c19752015-03-31 14:53:55 +000066 }
David Srbecky15c19752015-03-31 14:53:55 +000067 }
68
69 // Override this method to automatically advance the PC before each opcode.
70 virtual void ImplicitlyAdvancePC() { }
71
72 // Common alias in assemblers - spill relative to current stack pointer.
David Srbeckyc6b4dd82015-04-07 20:32:43 +010073 void ALWAYS_INLINE RelOffset(Reg reg, int offset) {
David Srbecky15c19752015-03-31 14:53:55 +000074 Offset(reg, offset - current_cfa_offset_);
75 }
76
77 // Common alias in assemblers - increase stack frame size.
David Srbeckyc6b4dd82015-04-07 20:32:43 +010078 void ALWAYS_INLINE AdjustCFAOffset(int delta) {
David Srbecky15c19752015-03-31 14:53:55 +000079 DefCFAOffset(current_cfa_offset_ + delta);
80 }
81
82 // Custom alias - spill many registers based on bitmask.
Vladimir Marko1a225a72019-07-05 13:37:42 +010083 void ALWAYS_INLINE RelOffsetForMany(Reg reg_base,
84 int32_t offset,
85 uint32_t reg_mask,
86 int32_t reg_size) {
David Srbecky15c19752015-03-31 14:53:55 +000087 DCHECK(reg_size == 4 || reg_size == 8);
David Srbeckyc6b4dd82015-04-07 20:32:43 +010088 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;
David Srbecky15c19752015-03-31 14:53:55 +000094 RelOffset(Reg(reg_base.num() + i), offset);
95 offset += reg_size;
96 }
97 }
98 }
99
100 // Custom alias - unspill many registers based on bitmask.
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100101 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;
David Srbecky15c19752015-03-31 14:53:55 +0000108 Restore(Reg(reg_base.num() + i));
109 }
110 }
111 }
112
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100113 void ALWAYS_INLINE Nop() {
114 if (UNLIKELY(enabled_)) {
115 this->PushUint8(DW_CFA_nop);
116 }
David Srbecky15c19752015-03-31 14:53:55 +0000117 }
118
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100119 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 }
David Srbecky15c19752015-03-31 14:53:55 +0000132 } else {
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100133 uses_dwarf3_features_ = true;
134 this->PushUint8(DW_CFA_offset_extended_sf);
David Srbecky15c19752015-03-31 14:53:55 +0000135 this->PushUleb128(reg.num());
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100136 this->PushSleb128(factored_offset);
David Srbecky15c19752015-03-31 14:53:55 +0000137 }
David Srbecky15c19752015-03-31 14:53:55 +0000138 }
139 }
140
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100141 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
153 void ALWAYS_INLINE Undefined(Reg reg) {
154 if (UNLIKELY(enabled_)) {
155 ImplicitlyAdvancePC();
156 this->PushUint8(DW_CFA_undefined);
David Srbecky15c19752015-03-31 14:53:55 +0000157 this->PushUleb128(reg.num());
158 }
159 }
160
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100161 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 }
David Srbecky15c19752015-03-31 14:53:55 +0000167 }
168
169 // The previous value of "reg" is stored in register "new_reg".
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100170 void ALWAYS_INLINE Register(Reg reg, Reg new_reg) {
171 if (UNLIKELY(enabled_)) {
172 ImplicitlyAdvancePC();
173 this->PushUint8(DW_CFA_register);
David Srbecky15c19752015-03-31 14:53:55 +0000174 this->PushUleb128(reg.num());
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100175 this->PushUleb128(new_reg.num());
David Srbecky15c19752015-03-31 14:53:55 +0000176 }
David Srbecky15c19752015-03-31 14:53:55 +0000177 }
178
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100179 void ALWAYS_INLINE RememberState() {
180 if (UNLIKELY(enabled_)) {
181 ImplicitlyAdvancePC();
182 this->PushUint8(DW_CFA_remember_state);
183 }
David Srbecky15c19752015-03-31 14:53:55 +0000184 }
185
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100186 void ALWAYS_INLINE RestoreState() {
187 if (UNLIKELY(enabled_)) {
188 ImplicitlyAdvancePC();
189 this->PushUint8(DW_CFA_restore_state);
190 }
191 }
192
193 void ALWAYS_INLINE DefCFA(Reg reg, int offset) {
194 if (UNLIKELY(enabled_)) {
David Srbecky15c19752015-03-31 14:53:55 +0000195 ImplicitlyAdvancePC();
196 if (offset >= 0) {
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100197 this->PushUint8(DW_CFA_def_cfa);
198 this->PushUleb128(reg.num());
David Srbecky15c19752015-03-31 14:53:55 +0000199 this->PushUleb128(offset); // Non-factored.
200 } else {
201 uses_dwarf3_features_ = true;
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100202 this->PushUint8(DW_CFA_def_cfa_sf);
203 this->PushUleb128(reg.num());
David Srbecky15c19752015-03-31 14:53:55 +0000204 this->PushSleb128(FactorDataOffset(offset));
205 }
David Srbecky15c19752015-03-31 14:53:55 +0000206 }
David Srbecky15c19752015-03-31 14:53:55 +0000207 current_cfa_offset_ = offset;
208 }
209
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100210 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
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
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
David Srbecky91cb54e2016-01-15 13:47:59 +0000253 void ALWAYS_INLINE DefCFAExpression(uint8_t* expr, int expr_size) {
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100254 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
David Srbecky91cb54e2016-01-15 13:47:59 +0000263 void ALWAYS_INLINE Expression(Reg reg, uint8_t* expr, int expr_size) {
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100264 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
David Srbecky91cb54e2016-01-15 13:47:59 +0000274 void ALWAYS_INLINE ValExpression(Reg reg, uint8_t* expr, int expr_size) {
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100275 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
285 bool IsEnabled() const { return enabled_; }
286
Vladimir Marko10ef6942015-10-22 15:25:54 +0100287 void SetEnabled(bool value) {
288 enabled_ = value;
289 if (enabled_ && opcodes_.capacity() == 0u) {
290 opcodes_.reserve(kDefaultCapacity);
291 }
292 }
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100293
294 int GetCurrentPC() const { return current_pc_; }
295
296 int GetCurrentCFAOffset() const { return current_cfa_offset_; }
297
298 void SetCurrentCFAOffset(int offset) { current_cfa_offset_ = offset; }
299
Vladimir Markoec7802a2015-10-01 20:57:57 +0100300 using Writer<Vector>::data;
David Srbecky15c19752015-03-31 14:53:55 +0000301
Vladimir Marko10ef6942015-10-22 15:25:54 +0100302 explicit DebugFrameOpCodeWriter(bool enabled = true,
303 const typename Vector::allocator_type& alloc =
304 typename Vector::allocator_type())
Vladimir Markoec7802a2015-10-01 20:57:57 +0100305 : Writer<Vector>(&opcodes_),
Vladimir Marko10ef6942015-10-22 15:25:54 +0100306 enabled_(false),
David Srbecky15c19752015-03-31 14:53:55 +0000307 opcodes_(alloc),
308 current_cfa_offset_(0),
309 current_pc_(0),
310 uses_dwarf3_features_(false) {
Vladimir Marko10ef6942015-10-22 15:25:54 +0100311 SetEnabled(enabled);
David Srbecky15c19752015-03-31 14:53:55 +0000312 }
313
314 virtual ~DebugFrameOpCodeWriter() { }
315
316 protected:
Vladimir Marko10ef6942015-10-22 15:25:54 +0100317 // Best guess based on couple of observed outputs.
318 static constexpr size_t kDefaultCapacity = 32u;
319
David Srbecky15c19752015-03-31 14:53:55 +0000320 int FactorDataOffset(int offset) const {
321 DCHECK_EQ(offset % kDataAlignmentFactor, 0);
322 return offset / kDataAlignmentFactor;
323 }
324
325 int FactorCodeOffset(int offset) const {
326 DCHECK_EQ(offset % kCodeAlignmentFactor, 0);
327 return offset / kCodeAlignmentFactor;
328 }
329
David Srbeckyc6b4dd82015-04-07 20:32:43 +0100330 bool enabled_; // If disabled all writes are no-ops.
Vladimir Markoec7802a2015-10-01 20:57:57 +0100331 Vector opcodes_;
David Srbecky15c19752015-03-31 14:53:55 +0000332 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
David Srbecky2faab002019-02-12 16:35:48 +0000343#endif // ART_LIBELFFILE_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_