| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef ART_LIBELFFILE_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_ |
| #define ART_LIBELFFILE_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_ |
| |
| #include "base/bit_utils.h" |
| #include "dwarf/dwarf_constants.h" |
| #include "dwarf/register.h" |
| #include "dwarf/writer.h" |
| |
| namespace art { |
| namespace dwarf { |
| |
| // Writer for .debug_frame opcodes (DWARF-3). |
| // See the DWARF specification for the precise meaning of the opcodes. |
| // The writer is very light-weight, however it will do the following for you: |
| // * Choose the most compact encoding of a given opcode. |
| // * Keep track of current state and convert absolute values to deltas. |
| // * Divide by header-defined factors as appropriate. |
| template<typename Vector = std::vector<uint8_t> > |
| class DebugFrameOpCodeWriter : private Writer<Vector> { |
| static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type"); |
| |
| public: |
| // To save space, DWARF divides most offsets by header-defined factors. |
| // They are used in integer divisions, so we make them constants. |
| // We usually subtract from stack base pointer, so making the factor |
| // negative makes the encoded values positive and thus easier to encode. |
| static constexpr int kDataAlignmentFactor = -4; |
| static constexpr int kCodeAlignmentFactor = 1; |
| |
| // Explicitely advance the program counter to given location. |
| void ALWAYS_INLINE AdvancePC(int absolute_pc) { |
| DCHECK_GE(absolute_pc, current_pc_); |
| if (UNLIKELY(enabled_)) { |
| int delta = FactorCodeOffset(absolute_pc - current_pc_); |
| if (delta != 0) { |
| if (delta <= 0x3F) { |
| this->PushUint8(DW_CFA_advance_loc | delta); |
| } else if (delta <= UINT8_MAX) { |
| this->PushUint8(DW_CFA_advance_loc1); |
| this->PushUint8(delta); |
| } else if (delta <= UINT16_MAX) { |
| this->PushUint8(DW_CFA_advance_loc2); |
| this->PushUint16(delta); |
| } else { |
| this->PushUint8(DW_CFA_advance_loc4); |
| this->PushUint32(delta); |
| } |
| } |
| current_pc_ = absolute_pc; |
| } |
| } |
| |
| // Override this method to automatically advance the PC before each opcode. |
| virtual void ImplicitlyAdvancePC() { } |
| |
| // Common alias in assemblers - spill relative to current stack pointer. |
| void ALWAYS_INLINE RelOffset(Reg reg, int offset) { |
| Offset(reg, offset - current_cfa_offset_); |
| } |
| |
| // Common alias in assemblers - increase stack frame size. |
| void ALWAYS_INLINE AdjustCFAOffset(int delta) { |
| DefCFAOffset(current_cfa_offset_ + delta); |
| } |
| |
| // Custom alias - spill many registers based on bitmask. |
| void ALWAYS_INLINE RelOffsetForMany(Reg reg_base, |
| int32_t offset, |
| uint32_t reg_mask, |
| int32_t reg_size) { |
| DCHECK(reg_size == 4 || reg_size == 8); |
| if (UNLIKELY(enabled_)) { |
| for (int i = 0; reg_mask != 0u; reg_mask >>= 1, i++) { |
| // Skip zero bits and go to the set bit. |
| int num_zeros = CTZ(reg_mask); |
| i += num_zeros; |
| reg_mask >>= num_zeros; |
| RelOffset(Reg(reg_base.num() + i), offset); |
| offset += reg_size; |
| } |
| } |
| } |
| |
| // Custom alias - unspill many registers based on bitmask. |
| void ALWAYS_INLINE RestoreMany(Reg reg_base, uint32_t reg_mask) { |
| if (UNLIKELY(enabled_)) { |
| for (int i = 0; reg_mask != 0u; reg_mask >>= 1, i++) { |
| // Skip zero bits and go to the set bit. |
| int num_zeros = CTZ(reg_mask); |
| i += num_zeros; |
| reg_mask >>= num_zeros; |
| Restore(Reg(reg_base.num() + i)); |
| } |
| } |
| } |
| |
| void ALWAYS_INLINE Nop() { |
| if (UNLIKELY(enabled_)) { |
| this->PushUint8(DW_CFA_nop); |
| } |
| } |
| |
| void ALWAYS_INLINE Offset(Reg reg, int offset) { |
| if (UNLIKELY(enabled_)) { |
| ImplicitlyAdvancePC(); |
| int factored_offset = FactorDataOffset(offset); // May change sign. |
| if (factored_offset >= 0) { |
| if (0 <= reg.num() && reg.num() <= 0x3F) { |
| this->PushUint8(DW_CFA_offset | reg.num()); |
| this->PushUleb128(factored_offset); |
| } else { |
| this->PushUint8(DW_CFA_offset_extended); |
| this->PushUleb128(reg.num()); |
| this->PushUleb128(factored_offset); |
| } |
| } else { |
| uses_dwarf3_features_ = true; |
| this->PushUint8(DW_CFA_offset_extended_sf); |
| this->PushUleb128(reg.num()); |
| this->PushSleb128(factored_offset); |
| } |
| } |
| } |
| |
| void ALWAYS_INLINE Restore(Reg reg) { |
| if (UNLIKELY(enabled_)) { |
| ImplicitlyAdvancePC(); |
| if (0 <= reg.num() && reg.num() <= 0x3F) { |
| this->PushUint8(DW_CFA_restore | reg.num()); |
| } else { |
| this->PushUint8(DW_CFA_restore_extended); |
| this->PushUleb128(reg.num()); |
| } |
| } |
| } |
| |
| void ALWAYS_INLINE Undefined(Reg reg) { |
| if (UNLIKELY(enabled_)) { |
| ImplicitlyAdvancePC(); |
| this->PushUint8(DW_CFA_undefined); |
| this->PushUleb128(reg.num()); |
| } |
| } |
| |
| void ALWAYS_INLINE SameValue(Reg reg) { |
| if (UNLIKELY(enabled_)) { |
| ImplicitlyAdvancePC(); |
| this->PushUint8(DW_CFA_same_value); |
| this->PushUleb128(reg.num()); |
| } |
| } |
| |
| // The previous value of "reg" is stored in register "new_reg". |
| void ALWAYS_INLINE Register(Reg reg, Reg new_reg) { |
| if (UNLIKELY(enabled_)) { |
| ImplicitlyAdvancePC(); |
| this->PushUint8(DW_CFA_register); |
| this->PushUleb128(reg.num()); |
| this->PushUleb128(new_reg.num()); |
| } |
| } |
| |
| void ALWAYS_INLINE RememberState() { |
| if (UNLIKELY(enabled_)) { |
| ImplicitlyAdvancePC(); |
| this->PushUint8(DW_CFA_remember_state); |
| } |
| } |
| |
| void ALWAYS_INLINE RestoreState() { |
| if (UNLIKELY(enabled_)) { |
| ImplicitlyAdvancePC(); |
| this->PushUint8(DW_CFA_restore_state); |
| } |
| } |
| |
| void ALWAYS_INLINE DefCFA(Reg reg, int offset) { |
| if (UNLIKELY(enabled_)) { |
| ImplicitlyAdvancePC(); |
| if (offset >= 0) { |
| this->PushUint8(DW_CFA_def_cfa); |
| this->PushUleb128(reg.num()); |
| this->PushUleb128(offset); // Non-factored. |
| } else { |
| uses_dwarf3_features_ = true; |
| this->PushUint8(DW_CFA_def_cfa_sf); |
| this->PushUleb128(reg.num()); |
| this->PushSleb128(FactorDataOffset(offset)); |
| } |
| } |
| current_cfa_offset_ = offset; |
| } |
| |
| void ALWAYS_INLINE DefCFARegister(Reg reg) { |
| if (UNLIKELY(enabled_)) { |
| ImplicitlyAdvancePC(); |
| this->PushUint8(DW_CFA_def_cfa_register); |
| this->PushUleb128(reg.num()); |
| } |
| } |
| |
| void ALWAYS_INLINE DefCFAOffset(int offset) { |
| if (UNLIKELY(enabled_)) { |
| if (current_cfa_offset_ != offset) { |
| ImplicitlyAdvancePC(); |
| if (offset >= 0) { |
| this->PushUint8(DW_CFA_def_cfa_offset); |
| this->PushUleb128(offset); // Non-factored. |
| } else { |
| uses_dwarf3_features_ = true; |
| this->PushUint8(DW_CFA_def_cfa_offset_sf); |
| this->PushSleb128(FactorDataOffset(offset)); |
| } |
| } |
| } |
| // Uncoditional so that the user can still get and check the value. |
| current_cfa_offset_ = offset; |
| } |
| |
| void ALWAYS_INLINE ValOffset(Reg reg, int offset) { |
| if (UNLIKELY(enabled_)) { |
| ImplicitlyAdvancePC(); |
| uses_dwarf3_features_ = true; |
| int factored_offset = FactorDataOffset(offset); // May change sign. |
| if (factored_offset >= 0) { |
| this->PushUint8(DW_CFA_val_offset); |
| this->PushUleb128(reg.num()); |
| this->PushUleb128(factored_offset); |
| } else { |
| this->PushUint8(DW_CFA_val_offset_sf); |
| this->PushUleb128(reg.num()); |
| this->PushSleb128(factored_offset); |
| } |
| } |
| } |
| |
| void ALWAYS_INLINE DefCFAExpression(uint8_t* expr, int expr_size) { |
| if (UNLIKELY(enabled_)) { |
| ImplicitlyAdvancePC(); |
| uses_dwarf3_features_ = true; |
| this->PushUint8(DW_CFA_def_cfa_expression); |
| this->PushUleb128(expr_size); |
| this->PushData(expr, expr_size); |
| } |
| } |
| |
| void ALWAYS_INLINE Expression(Reg reg, uint8_t* expr, int expr_size) { |
| if (UNLIKELY(enabled_)) { |
| ImplicitlyAdvancePC(); |
| uses_dwarf3_features_ = true; |
| this->PushUint8(DW_CFA_expression); |
| this->PushUleb128(reg.num()); |
| this->PushUleb128(expr_size); |
| this->PushData(expr, expr_size); |
| } |
| } |
| |
| void ALWAYS_INLINE ValExpression(Reg reg, uint8_t* expr, int expr_size) { |
| if (UNLIKELY(enabled_)) { |
| ImplicitlyAdvancePC(); |
| uses_dwarf3_features_ = true; |
| this->PushUint8(DW_CFA_val_expression); |
| this->PushUleb128(reg.num()); |
| this->PushUleb128(expr_size); |
| this->PushData(expr, expr_size); |
| } |
| } |
| |
| bool IsEnabled() const { return enabled_; } |
| |
| void SetEnabled(bool value) { |
| enabled_ = value; |
| if (enabled_ && opcodes_.capacity() == 0u) { |
| opcodes_.reserve(kDefaultCapacity); |
| } |
| } |
| |
| int GetCurrentPC() const { return current_pc_; } |
| |
| int GetCurrentCFAOffset() const { return current_cfa_offset_; } |
| |
| void SetCurrentCFAOffset(int offset) { current_cfa_offset_ = offset; } |
| |
| using Writer<Vector>::data; |
| |
| explicit DebugFrameOpCodeWriter(bool enabled = true, |
| const typename Vector::allocator_type& alloc = |
| typename Vector::allocator_type()) |
| : Writer<Vector>(&opcodes_), |
| enabled_(false), |
| opcodes_(alloc), |
| current_cfa_offset_(0), |
| current_pc_(0), |
| uses_dwarf3_features_(false) { |
| SetEnabled(enabled); |
| } |
| |
| virtual ~DebugFrameOpCodeWriter() { } |
| |
| protected: |
| // Best guess based on couple of observed outputs. |
| static constexpr size_t kDefaultCapacity = 32u; |
| |
| int FactorDataOffset(int offset) const { |
| DCHECK_EQ(offset % kDataAlignmentFactor, 0); |
| return offset / kDataAlignmentFactor; |
| } |
| |
| int FactorCodeOffset(int offset) const { |
| DCHECK_EQ(offset % kCodeAlignmentFactor, 0); |
| return offset / kCodeAlignmentFactor; |
| } |
| |
| bool enabled_; // If disabled all writes are no-ops. |
| Vector opcodes_; |
| int current_cfa_offset_; |
| int current_pc_; |
| bool uses_dwarf3_features_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(DebugFrameOpCodeWriter); |
| }; |
| |
| } // namespace dwarf |
| } // namespace art |
| |
| #endif // ART_LIBELFFILE_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_ |