You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
344 lines
10 KiB
344 lines
10 KiB
/*
|
|
* 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_
|