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