| /* |
| * 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_LINE_OPCODE_WRITER_H_ |
| #define ART_LIBELFFILE_DWARF_DEBUG_LINE_OPCODE_WRITER_H_ |
| |
| #include <cstdint> |
| |
| #include "dwarf/dwarf_constants.h" |
| #include "dwarf/writer.h" |
| |
| namespace art { |
| namespace dwarf { |
| |
| // Writer for the .debug_line opcodes (DWARF-3). |
| // 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 DebugLineOpCodeWriter final : private Writer<Vector> { |
| static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type"); |
| |
| public: |
| static constexpr int kOpcodeBase = 13; |
| static constexpr bool kDefaultIsStmt = false; |
| static constexpr int kLineBase = -5; |
| static constexpr int kLineRange = 14; |
| |
| void AddRow() { |
| this->PushUint8(DW_LNS_copy); |
| } |
| |
| void AdvancePC(uint64_t absolute_address) { |
| DCHECK_NE(current_address_, 0u); // Use SetAddress for the first advance. |
| DCHECK_GE(absolute_address, current_address_); |
| if (absolute_address != current_address_) { |
| uint64_t delta = FactorCodeOffset(absolute_address - current_address_); |
| if (delta <= INT32_MAX) { |
| this->PushUint8(DW_LNS_advance_pc); |
| this->PushUleb128(static_cast<int>(delta)); |
| current_address_ = absolute_address; |
| } else { |
| SetAddress(absolute_address); |
| } |
| } |
| } |
| |
| void AdvanceLine(int absolute_line) { |
| int delta = absolute_line - current_line_; |
| if (delta != 0) { |
| this->PushUint8(DW_LNS_advance_line); |
| this->PushSleb128(delta); |
| current_line_ = absolute_line; |
| } |
| } |
| |
| void SetFile(int file) { |
| if (current_file_ != file) { |
| this->PushUint8(DW_LNS_set_file); |
| this->PushUleb128(file); |
| current_file_ = file; |
| } |
| } |
| |
| void SetColumn(int column) { |
| this->PushUint8(DW_LNS_set_column); |
| this->PushUleb128(column); |
| } |
| |
| void SetIsStmt(bool is_stmt) { |
| if (is_stmt_ != is_stmt) { |
| this->PushUint8(DW_LNS_negate_stmt); |
| is_stmt_ = is_stmt; |
| } |
| } |
| |
| void SetBasicBlock() { |
| this->PushUint8(DW_LNS_set_basic_block); |
| } |
| |
| void SetPrologueEnd() { |
| uses_dwarf3_features_ = true; |
| this->PushUint8(DW_LNS_set_prologue_end); |
| } |
| |
| void SetEpilogueBegin() { |
| uses_dwarf3_features_ = true; |
| this->PushUint8(DW_LNS_set_epilogue_begin); |
| } |
| |
| void SetISA(int isa) { |
| uses_dwarf3_features_ = true; |
| this->PushUint8(DW_LNS_set_isa); |
| this->PushUleb128(isa); |
| } |
| |
| void EndSequence() { |
| this->PushUint8(0); |
| this->PushUleb128(1); |
| this->PushUint8(DW_LNE_end_sequence); |
| current_address_ = 0; |
| current_file_ = 1; |
| current_line_ = 1; |
| is_stmt_ = kDefaultIsStmt; |
| } |
| |
| // Uncoditionally set address using the long encoding. |
| // This gives the linker opportunity to relocate the address. |
| void SetAddress(uint64_t absolute_address) { |
| DCHECK_GE(absolute_address, current_address_); |
| FactorCodeOffset(absolute_address); // Check if it is factorable. |
| this->PushUint8(0); |
| if (use_64bit_address_) { |
| this->PushUleb128(1 + 8); |
| this->PushUint8(DW_LNE_set_address); |
| patch_locations_.push_back(this->data()->size()); |
| this->PushUint64(absolute_address); |
| } else { |
| this->PushUleb128(1 + 4); |
| this->PushUint8(DW_LNE_set_address); |
| patch_locations_.push_back(this->data()->size()); |
| this->PushUint32(absolute_address); |
| } |
| current_address_ = absolute_address; |
| } |
| |
| void DefineFile(const char* filename, |
| int directory_index, |
| int modification_time, |
| int file_size) { |
| int size = 1 + |
| strlen(filename) + 1 + |
| UnsignedLeb128Size(directory_index) + |
| UnsignedLeb128Size(modification_time) + |
| UnsignedLeb128Size(file_size); |
| this->PushUint8(0); |
| this->PushUleb128(size); |
| size_t start = data()->size(); |
| this->PushUint8(DW_LNE_define_file); |
| this->PushString(filename); |
| this->PushUleb128(directory_index); |
| this->PushUleb128(modification_time); |
| this->PushUleb128(file_size); |
| DCHECK_EQ(start + size, data()->size()); |
| } |
| |
| // Compact address and line opcode. |
| void AddRow(uint64_t absolute_address, int absolute_line) { |
| DCHECK_GE(absolute_address, current_address_); |
| |
| // If the address is definitely too far, use the long encoding. |
| uint64_t delta_address = FactorCodeOffset(absolute_address - current_address_); |
| if (delta_address > UINT8_MAX) { |
| AdvancePC(absolute_address); |
| delta_address = 0; |
| } |
| |
| // If the line is definitely too far, use the long encoding. |
| int delta_line = absolute_line - current_line_; |
| if (!(kLineBase <= delta_line && delta_line < kLineBase + kLineRange)) { |
| AdvanceLine(absolute_line); |
| delta_line = 0; |
| } |
| |
| // Both address and line should be reasonable now. Use the short encoding. |
| int opcode = kOpcodeBase + (delta_line - kLineBase) + |
| (static_cast<int>(delta_address) * kLineRange); |
| if (opcode > UINT8_MAX) { |
| // If the address is still too far, try to increment it by const amount. |
| int const_advance = (0xFF - kOpcodeBase) / kLineRange; |
| opcode -= (kLineRange * const_advance); |
| if (opcode <= UINT8_MAX) { |
| this->PushUint8(DW_LNS_const_add_pc); |
| } else { |
| // Give up and use long encoding for address. |
| AdvancePC(absolute_address); |
| // Still use the opcode to do line advance and copy. |
| opcode = kOpcodeBase + (delta_line - kLineBase); |
| } |
| } |
| DCHECK(kOpcodeBase <= opcode && opcode <= 0xFF); |
| this->PushUint8(opcode); // Special opcode. |
| current_line_ = absolute_line; |
| current_address_ = absolute_address; |
| } |
| |
| int GetCodeFactorBits() const { |
| return code_factor_bits_; |
| } |
| |
| uint64_t CurrentAddress() const { |
| return current_address_; |
| } |
| |
| int CurrentFile() const { |
| return current_file_; |
| } |
| |
| int CurrentLine() const { |
| return current_line_; |
| } |
| |
| const std::vector<uintptr_t>& GetPatchLocations() const { |
| return patch_locations_; |
| } |
| |
| using Writer<Vector>::data; |
| |
| DebugLineOpCodeWriter(bool use64bitAddress, |
| int codeFactorBits, |
| const typename Vector::allocator_type& alloc = |
| typename Vector::allocator_type()) |
| : Writer<Vector>(&opcodes_), |
| opcodes_(alloc), |
| uses_dwarf3_features_(false), |
| use_64bit_address_(use64bitAddress), |
| code_factor_bits_(codeFactorBits), |
| current_address_(0), |
| current_file_(1), |
| current_line_(1), |
| is_stmt_(kDefaultIsStmt) { |
| } |
| |
| private: |
| uint64_t FactorCodeOffset(uint64_t offset) const { |
| DCHECK_GE(code_factor_bits_, 0); |
| DCHECK_EQ((offset >> code_factor_bits_) << code_factor_bits_, offset); |
| return offset >> code_factor_bits_; |
| } |
| |
| Vector opcodes_; |
| bool uses_dwarf3_features_; |
| bool use_64bit_address_; |
| int code_factor_bits_; |
| uint64_t current_address_; |
| int current_file_; |
| int current_line_; |
| bool is_stmt_; |
| std::vector<uintptr_t> patch_locations_; |
| |
| DISALLOW_COPY_AND_ASSIGN(DebugLineOpCodeWriter); |
| }; |
| |
| } // namespace dwarf |
| } // namespace art |
| |
| #endif // ART_LIBELFFILE_DWARF_DEBUG_LINE_OPCODE_WRITER_H_ |