Move code related to debug info generation to its own directory.
debug/dwarf/ contains helper classes which hide the details
of the DWARF file format. It acts as independent DWARF library.
debug/ contains ART-specific code which generates ELF debug
sections (which includes non-DWARF sections like .symtab).
Change-Id: Id351f604e4e64be2ca395a78324ea02e30481497
diff --git a/compiler/debug/dwarf/debug_abbrev_writer.h b/compiler/debug/dwarf/debug_abbrev_writer.h
new file mode 100644
index 0000000..0fc843c
--- /dev/null
+++ b/compiler/debug/dwarf/debug_abbrev_writer.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2016 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_COMPILER_DEBUG_DWARF_DEBUG_ABBREV_WRITER_H_
+#define ART_COMPILER_DEBUG_DWARF_DEBUG_ABBREV_WRITER_H_
+
+#include <cstdint>
+#include <type_traits>
+#include <unordered_map>
+
+#include "base/casts.h"
+#include "base/stl_util.h"
+#include "debug/dwarf/dwarf_constants.h"
+#include "debug/dwarf/writer.h"
+#include "leb128.h"
+
+namespace art {
+namespace dwarf {
+
+// Writer for the .debug_abbrev.
+//
+// Abbreviations specify the format of entries in .debug_info.
+// Each entry specifies abbreviation code, which in turns
+// determines all the attributes and their format.
+// It is possible to think of them as type definitions.
+template <typename Vector = std::vector<uint8_t>>
+class DebugAbbrevWriter FINAL : private Writer<Vector> {
+ static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type");
+
+ public:
+ explicit DebugAbbrevWriter(Vector* buffer)
+ : Writer<Vector>(buffer),
+ current_abbrev_(buffer->get_allocator()) {
+ this->PushUint8(0); // Add abbrev table terminator.
+ }
+
+ // Start abbreviation declaration.
+ void StartAbbrev(Tag tag) {
+ DCHECK(current_abbrev_.empty());
+ EncodeUnsignedLeb128(¤t_abbrev_, tag);
+ has_children_offset_ = current_abbrev_.size();
+ current_abbrev_.push_back(0); // Place-holder for DW_CHILDREN.
+ }
+
+ // Add attribute specification.
+ void AddAbbrevAttribute(Attribute name, Form type) {
+ EncodeUnsignedLeb128(¤t_abbrev_, name);
+ EncodeUnsignedLeb128(¤t_abbrev_, type);
+ }
+
+ // End abbreviation declaration and return its code.
+ // This will deduplicate abbreviations.
+ uint32_t EndAbbrev(Children has_children) {
+ DCHECK(!current_abbrev_.empty());
+ current_abbrev_[has_children_offset_] = has_children;
+ auto it = abbrev_codes_.insert(std::make_pair(std::move(current_abbrev_), NextAbbrevCode()));
+ uint32_t abbrev_code = it.first->second;
+ if (UNLIKELY(it.second)) { // Inserted new entry.
+ const Vector& abbrev = it.first->first;
+ this->Pop(); // Remove abbrev table terminator.
+ this->PushUleb128(abbrev_code);
+ this->PushData(abbrev.data(), abbrev.size());
+ this->PushUint8(0); // Attribute list end.
+ this->PushUint8(0); // Attribute list end.
+ this->PushUint8(0); // Add abbrev table terminator.
+ }
+ current_abbrev_.clear();
+ return abbrev_code;
+ }
+
+ // Get the next free abbrev code.
+ uint32_t NextAbbrevCode() {
+ return dchecked_integral_cast<uint32_t>(1 + abbrev_codes_.size());
+ }
+
+ private:
+ Vector current_abbrev_;
+ size_t has_children_offset_ = 0;
+ std::unordered_map<Vector, uint32_t, FNVHash<Vector> > abbrev_codes_;
+};
+
+} // namespace dwarf
+} // namespace art
+
+#endif // ART_COMPILER_DEBUG_DWARF_DEBUG_ABBREV_WRITER_H_
diff --git a/compiler/debug/dwarf/debug_frame_opcode_writer.h b/compiler/debug/dwarf/debug_frame_opcode_writer.h
new file mode 100644
index 0000000..7c75c9b
--- /dev/null
+++ b/compiler/debug/dwarf/debug_frame_opcode_writer.h
@@ -0,0 +1,341 @@
+/*
+ * 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_COMPILER_DEBUG_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_
+#define ART_COMPILER_DEBUG_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_
+
+#include "base/bit_utils.h"
+#include "debug/dwarf/dwarf_constants.h"
+#include "debug/dwarf/register.h"
+#include "debug/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, int offset,
+ uint32_t reg_mask, int 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_COMPILER_DEBUG_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_
diff --git a/compiler/debug/dwarf/debug_info_entry_writer.h b/compiler/debug/dwarf/debug_info_entry_writer.h
new file mode 100644
index 0000000..85f021e
--- /dev/null
+++ b/compiler/debug/dwarf/debug_info_entry_writer.h
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2014 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_COMPILER_DEBUG_DWARF_DEBUG_INFO_ENTRY_WRITER_H_
+#define ART_COMPILER_DEBUG_DWARF_DEBUG_INFO_ENTRY_WRITER_H_
+
+#include <cstdint>
+#include <unordered_map>
+
+#include "base/casts.h"
+#include "debug/dwarf/debug_abbrev_writer.h"
+#include "debug/dwarf/dwarf_constants.h"
+#include "debug/dwarf/expression.h"
+#include "debug/dwarf/writer.h"
+#include "leb128.h"
+
+namespace art {
+namespace dwarf {
+
+/*
+ * Writer for debug information entries (DIE).
+ *
+ * Usage:
+ * StartTag(DW_TAG_compile_unit);
+ * WriteStrp(DW_AT_producer, "Compiler name", debug_str);
+ * StartTag(DW_TAG_subprogram);
+ * WriteStrp(DW_AT_name, "Foo", debug_str);
+ * EndTag();
+ * EndTag();
+ */
+template <typename Vector = std::vector<uint8_t>>
+class DebugInfoEntryWriter FINAL : private Writer<Vector> {
+ static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type");
+
+ public:
+ static constexpr size_t kCompilationUnitHeaderSize = 11;
+
+ // Start debugging information entry.
+ // Returns offset of the entry in compilation unit.
+ size_t StartTag(Tag tag) {
+ if (inside_entry_) {
+ // Write abbrev code for the previous entry.
+ // Parent entry is finalized before any children are written.
+ this->UpdateUleb128(abbrev_code_offset_, debug_abbrev_->EndAbbrev(DW_CHILDREN_yes));
+ inside_entry_ = false;
+ }
+ debug_abbrev_->StartAbbrev(tag);
+ // Abbrev code placeholder of sufficient size.
+ abbrev_code_offset_ = this->data()->size();
+ this->PushUleb128(debug_abbrev_->NextAbbrevCode());
+ depth_++;
+ inside_entry_ = true;
+ return abbrev_code_offset_ + kCompilationUnitHeaderSize;
+ }
+
+ // End debugging information entry.
+ void EndTag() {
+ DCHECK_GT(depth_, 0);
+ if (inside_entry_) {
+ // Write abbrev code for this entry.
+ this->UpdateUleb128(abbrev_code_offset_, debug_abbrev_->EndAbbrev(DW_CHILDREN_no));
+ inside_entry_ = false;
+ // This entry has no children and so there is no terminator.
+ } else {
+ // The entry has been already finalized so it must be parent entry
+ // and we need to write the terminator required by DW_CHILDREN_yes.
+ this->PushUint8(0);
+ }
+ depth_--;
+ }
+
+ void WriteAddr(Attribute attrib, uint64_t value) {
+ debug_abbrev_->AddAbbrevAttribute(attrib, DW_FORM_addr);
+ patch_locations_.push_back(this->data()->size());
+ if (is64bit_) {
+ this->PushUint64(value);
+ } else {
+ this->PushUint32(value);
+ }
+ }
+
+ void WriteBlock(Attribute attrib, const uint8_t* ptr, size_t num_bytes) {
+ debug_abbrev_->AddAbbrevAttribute(attrib, DW_FORM_block);
+ this->PushUleb128(num_bytes);
+ this->PushData(ptr, num_bytes);
+ }
+
+ void WriteExprLoc(Attribute attrib, const Expression& expr) {
+ debug_abbrev_->AddAbbrevAttribute(attrib, DW_FORM_exprloc);
+ this->PushUleb128(dchecked_integral_cast<uint32_t>(expr.size()));
+ this->PushData(expr.data());
+ }
+
+ void WriteData1(Attribute attrib, uint8_t value) {
+ debug_abbrev_->AddAbbrevAttribute(attrib, DW_FORM_data1);
+ this->PushUint8(value);
+ }
+
+ void WriteData2(Attribute attrib, uint16_t value) {
+ debug_abbrev_->AddAbbrevAttribute(attrib, DW_FORM_data2);
+ this->PushUint16(value);
+ }
+
+ void WriteData4(Attribute attrib, uint32_t value) {
+ debug_abbrev_->AddAbbrevAttribute(attrib, DW_FORM_data4);
+ this->PushUint32(value);
+ }
+
+ void WriteData8(Attribute attrib, uint64_t value) {
+ debug_abbrev_->AddAbbrevAttribute(attrib, DW_FORM_data8);
+ this->PushUint64(value);
+ }
+
+ void WriteSecOffset(Attribute attrib, uint32_t offset) {
+ debug_abbrev_->AddAbbrevAttribute(attrib, DW_FORM_sec_offset);
+ this->PushUint32(offset);
+ }
+
+ void WriteSdata(Attribute attrib, int value) {
+ debug_abbrev_->AddAbbrevAttribute(attrib, DW_FORM_sdata);
+ this->PushSleb128(value);
+ }
+
+ void WriteUdata(Attribute attrib, int value) {
+ debug_abbrev_->AddAbbrevAttribute(attrib, DW_FORM_udata);
+ this->PushUleb128(value);
+ }
+
+ void WriteUdata(Attribute attrib, uint32_t value) {
+ debug_abbrev_->AddAbbrevAttribute(attrib, DW_FORM_udata);
+ this->PushUleb128(value);
+ }
+
+ void WriteFlag(Attribute attrib, bool value) {
+ debug_abbrev_->AddAbbrevAttribute(attrib, DW_FORM_flag);
+ this->PushUint8(value ? 1 : 0);
+ }
+
+ void WriteFlagPresent(Attribute attrib) {
+ debug_abbrev_->AddAbbrevAttribute(attrib, DW_FORM_flag_present);
+ }
+
+ void WriteRef4(Attribute attrib, uint32_t cu_offset) {
+ debug_abbrev_->AddAbbrevAttribute(attrib, DW_FORM_ref4);
+ this->PushUint32(cu_offset);
+ }
+
+ void WriteRef(Attribute attrib, uint32_t cu_offset) {
+ debug_abbrev_->AddAbbrevAttribute(attrib, DW_FORM_ref_udata);
+ this->PushUleb128(cu_offset);
+ }
+
+ void WriteString(Attribute attrib, const char* value) {
+ debug_abbrev_->AddAbbrevAttribute(attrib, DW_FORM_string);
+ this->PushString(value);
+ }
+
+ void WriteStrp(Attribute attrib, size_t debug_str_offset) {
+ debug_abbrev_->AddAbbrevAttribute(attrib, DW_FORM_strp);
+ this->PushUint32(dchecked_integral_cast<uint32_t>(debug_str_offset));
+ }
+
+ void WriteStrp(Attribute attrib, const char* str, size_t len,
+ std::vector<uint8_t>* debug_str) {
+ debug_abbrev_->AddAbbrevAttribute(attrib, DW_FORM_strp);
+ this->PushUint32(debug_str->size());
+ debug_str->insert(debug_str->end(), str, str + len);
+ debug_str->push_back(0);
+ }
+
+ void WriteStrp(Attribute attrib, const char* str, std::vector<uint8_t>* debug_str) {
+ WriteStrp(attrib, str, strlen(str), debug_str);
+ }
+
+ bool Is64bit() const { return is64bit_; }
+
+ const std::vector<uintptr_t>& GetPatchLocations() const {
+ return patch_locations_;
+ }
+
+ int Depth() const { return depth_; }
+
+ using Writer<Vector>::data;
+ using Writer<Vector>::size;
+ using Writer<Vector>::UpdateUint32;
+
+ DebugInfoEntryWriter(bool is64bitArch,
+ DebugAbbrevWriter<Vector>* debug_abbrev,
+ const typename Vector::allocator_type& alloc =
+ typename Vector::allocator_type())
+ : Writer<Vector>(&entries_),
+ debug_abbrev_(debug_abbrev),
+ entries_(alloc),
+ is64bit_(is64bitArch) {
+ }
+
+ ~DebugInfoEntryWriter() {
+ DCHECK(!inside_entry_);
+ DCHECK_EQ(depth_, 0);
+ }
+
+ private:
+ DebugAbbrevWriter<Vector>* debug_abbrev_;
+ Vector entries_;
+ bool is64bit_;
+ int depth_ = 0;
+ size_t abbrev_code_offset_ = 0; // Location to patch once we know the code.
+ bool inside_entry_ = false; // Entry ends at first child (if any).
+ std::vector<uintptr_t> patch_locations_;
+};
+
+} // namespace dwarf
+} // namespace art
+
+#endif // ART_COMPILER_DEBUG_DWARF_DEBUG_INFO_ENTRY_WRITER_H_
diff --git a/compiler/debug/dwarf/debug_line_opcode_writer.h b/compiler/debug/dwarf/debug_line_opcode_writer.h
new file mode 100644
index 0000000..58502a3
--- /dev/null
+++ b/compiler/debug/dwarf/debug_line_opcode_writer.h
@@ -0,0 +1,255 @@
+/*
+ * 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_COMPILER_DEBUG_DWARF_DEBUG_LINE_OPCODE_WRITER_H_
+#define ART_COMPILER_DEBUG_DWARF_DEBUG_LINE_OPCODE_WRITER_H_
+
+#include <cstdint>
+
+#include "debug/dwarf/dwarf_constants.h"
+#include "debug/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 = true;
+ 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 NegateStmt() {
+ this->PushUint8(DW_LNS_negate_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;
+ }
+
+ // 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) {
+ }
+
+ 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_;
+ std::vector<uintptr_t> patch_locations_;
+
+ DISALLOW_COPY_AND_ASSIGN(DebugLineOpCodeWriter);
+};
+
+} // namespace dwarf
+} // namespace art
+
+#endif // ART_COMPILER_DEBUG_DWARF_DEBUG_LINE_OPCODE_WRITER_H_
diff --git a/compiler/debug/dwarf/dwarf_constants.h b/compiler/debug/dwarf/dwarf_constants.h
new file mode 100644
index 0000000..96f805e
--- /dev/null
+++ b/compiler/debug/dwarf/dwarf_constants.h
@@ -0,0 +1,694 @@
+/*
+ * Copyright (C) 2014 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_COMPILER_DEBUG_DWARF_DWARF_CONSTANTS_H_
+#define ART_COMPILER_DEBUG_DWARF_DWARF_CONSTANTS_H_
+
+namespace art {
+namespace dwarf {
+
+// Based on the Dwarf 4 specification at dwarfstd.com and issues marked
+// for inclusion in Dwarf 5 on same. Values not specified in the Dwarf 4
+// standard might change or be removed in the future and may be different
+// than the values used currently by other implementations for the same trait,
+// use at your own risk.
+
+enum Tag {
+ DW_TAG_array_type = 0x01,
+ DW_TAG_class_type = 0x02,
+ DW_TAG_entry_point = 0x03,
+ DW_TAG_enumeration_type = 0x04,
+ DW_TAG_formal_parameter = 0x05,
+ DW_TAG_imported_declaration = 0x08,
+ DW_TAG_label = 0x0a,
+ DW_TAG_lexical_block = 0x0b,
+ DW_TAG_member = 0x0d,
+ DW_TAG_pointer_type = 0x0f,
+ DW_TAG_reference_type = 0x10,
+ DW_TAG_compile_unit = 0x11,
+ DW_TAG_string_type = 0x12,
+ DW_TAG_structure_type = 0x13,
+ DW_TAG_subroutine_type = 0x15,
+ DW_TAG_typedef = 0x16,
+ DW_TAG_union_type = 0x17,
+ DW_TAG_unspecified_parameters = 0x18,
+ DW_TAG_variant = 0x19,
+ DW_TAG_common_block = 0x1a,
+ DW_TAG_common_inclusion = 0x1b,
+ DW_TAG_inheritance = 0x1c,
+ DW_TAG_inlined_subroutine = 0x1d,
+ DW_TAG_module = 0x1e,
+ DW_TAG_ptr_to_member_type = 0x1f,
+ DW_TAG_set_type = 0x20,
+ DW_TAG_subrange_type = 0x21,
+ DW_TAG_with_stmt = 0x22,
+ DW_TAG_access_declaration = 0x23,
+ DW_TAG_base_type = 0x24,
+ DW_TAG_catch_block = 0x25,
+ DW_TAG_const_type = 0x26,
+ DW_TAG_constant = 0x27,
+ DW_TAG_enumerator = 0x28,
+ DW_TAG_file_type = 0x29,
+ DW_TAG_friend = 0x2a,
+ DW_TAG_namelist = 0x2b,
+ DW_TAG_namelist_item = 0x2c,
+ DW_TAG_packed_type = 0x2d,
+ DW_TAG_subprogram = 0x2e,
+ DW_TAG_template_type_parameter = 0x2f,
+ DW_TAG_template_value_parameter = 0x30,
+ DW_TAG_thrown_type = 0x31,
+ DW_TAG_try_block = 0x32,
+ DW_TAG_variant_part = 0x33,
+ DW_TAG_variable = 0x34,
+ DW_TAG_volatile_type = 0x35,
+ DW_TAG_dwarf_procedure = 0x36,
+ DW_TAG_restrict_type = 0x37,
+ DW_TAG_interface_type = 0x38,
+ DW_TAG_namespace = 0x39,
+ DW_TAG_imported_module = 0x3a,
+ DW_TAG_unspecified_type = 0x3b,
+ DW_TAG_partial_unit = 0x3c,
+ DW_TAG_imported_unit = 0x3d,
+ DW_TAG_condition = 0x3f,
+ DW_TAG_shared_type = 0x40,
+ DW_TAG_type_unit = 0x41,
+ DW_TAG_rvalue_reference_type = 0x42,
+ DW_TAG_template_alias = 0x43,
+#ifdef INCLUDE_DWARF5_VALUES
+ // Values to be added in Dwarf 5. Final value not yet specified. Values listed
+ // may be different than other implementations. Use with caution.
+ // TODO Update these values when Dwarf 5 is released.
+ DW_TAG_coarray_type = 0x44,
+ DW_TAG_call_site = 0x45,
+ DW_TAG_call_site_parameter = 0x46,
+ DW_TAG_generic_subrange = 0x47,
+ DW_TAG_atomic_type = 0x48,
+ DW_TAG_dynamic_type = 0x49,
+ DW_TAG_aligned_type = 0x50,
+#endif
+ DW_TAG_lo_user = 0x4080,
+ DW_TAG_hi_user = 0xffff
+};
+
+enum Children : uint8_t {
+ DW_CHILDREN_no = 0x00,
+ DW_CHILDREN_yes = 0x01
+};
+
+enum Attribute {
+ DW_AT_sibling = 0x01,
+ DW_AT_location = 0x02,
+ DW_AT_name = 0x03,
+ DW_AT_ordering = 0x09,
+ DW_AT_byte_size = 0x0b,
+ DW_AT_bit_offset = 0x0c,
+ DW_AT_bit_size = 0x0d,
+ DW_AT_stmt_list = 0x10,
+ DW_AT_low_pc = 0x11,
+ DW_AT_high_pc = 0x12,
+ DW_AT_language = 0x13,
+ DW_AT_discr = 0x15,
+ DW_AT_discr_value = 0x16,
+ DW_AT_visibility = 0x17,
+ DW_AT_import = 0x18,
+ DW_AT_string_length = 0x19,
+ DW_AT_common_reference = 0x1a,
+ DW_AT_comp_dir = 0x1b,
+ DW_AT_const_value = 0x1c,
+ DW_AT_containing_type = 0x1d,
+ DW_AT_default_value = 0x1e,
+ DW_AT_inline = 0x20,
+ DW_AT_is_optional = 0x21,
+ DW_AT_lower_bound = 0x22,
+ DW_AT_producer = 0x25,
+ DW_AT_prototyped = 0x27,
+ DW_AT_return_addr = 0x2a,
+ DW_AT_start_scope = 0x2c,
+ DW_AT_bit_stride = 0x2e,
+ DW_AT_upper_bound = 0x2f,
+ DW_AT_abstract_origin = 0x31,
+ DW_AT_accessibility = 0x32,
+ DW_AT_address_class = 0x33,
+ DW_AT_artificial = 0x34,
+ DW_AT_base_types = 0x35,
+ DW_AT_calling_convention = 0x36,
+ DW_AT_count = 0x37,
+ DW_AT_data_member_location = 0x38,
+ DW_AT_decl_column = 0x39,
+ DW_AT_decl_file = 0x3a,
+ DW_AT_decl_line = 0x3b,
+ DW_AT_declaration = 0x3c,
+ DW_AT_discr_list = 0x3d,
+ DW_AT_encoding = 0x3e,
+ DW_AT_external = 0x3f,
+ DW_AT_frame_base = 0x40,
+ DW_AT_friend = 0x41,
+ DW_AT_identifier_case = 0x42,
+ DW_AT_macro_info = 0x43,
+ DW_AT_namelist_item = 0x44,
+ DW_AT_priority = 0x45,
+ DW_AT_segment = 0x46,
+ DW_AT_specification = 0x47,
+ DW_AT_static_link = 0x48,
+ DW_AT_type = 0x49,
+ DW_AT_use_location = 0x4a,
+ DW_AT_variable_parameter = 0x4b,
+ DW_AT_virtuality = 0x4c,
+ DW_AT_vtable_elem_location = 0x4d,
+ DW_AT_allocated = 0x4e,
+ DW_AT_associated = 0x4f,
+ DW_AT_data_location = 0x50,
+ DW_AT_byte_stride = 0x51,
+ DW_AT_entry_pc = 0x52,
+ DW_AT_use_UTF8 = 0x53,
+ DW_AT_extension = 0x54,
+ DW_AT_ranges = 0x55,
+ DW_AT_trampoline = 0x56,
+ DW_AT_call_column = 0x57,
+ DW_AT_call_file = 0x58,
+ DW_AT_call_line = 0x59,
+ DW_AT_description = 0x5a,
+ DW_AT_binary_scale = 0x5b,
+ DW_AT_decimal_scale = 0x5c,
+ DW_AT_small = 0x5d,
+ DW_AT_decimal_sign = 0x5e,
+ DW_AT_digit_count = 0x5f,
+ DW_AT_picture_string = 0x60,
+ DW_AT_mutable = 0x61,
+ DW_AT_threads_scaled = 0x62,
+ DW_AT_explicit = 0x63,
+ DW_AT_object_pointer = 0x64,
+ DW_AT_endianity = 0x65,
+ DW_AT_elemental = 0x66,
+ DW_AT_pure = 0x67,
+ DW_AT_recursive = 0x68,
+ DW_AT_signature = 0x69,
+ DW_AT_main_subprogram = 0x6a,
+ DW_AT_data_bit_offset = 0x6b,
+ DW_AT_const_expr = 0x6c,
+ DW_AT_enum_class = 0x6d,
+ DW_AT_linkage_name = 0x6e,
+#ifdef INCLUDE_DWARF5_VALUES
+ // Values to be added in Dwarf 5. Final value not yet specified. Values listed
+ // may be different than other implementations. Use with caution.
+ // TODO Update these values when Dwarf 5 is released.
+ DW_AT_call_site_value = 0x6f,
+ DW_AT_call_site_data_value = 0x70,
+ DW_AT_call_site_target = 0x71,
+ DW_AT_call_site_target_clobbered = 0x72,
+ DW_AT_tail_call = 0x73,
+ DW_AT_all_tail_call_sites = 0x74,
+ DW_AT_all_call_sites = 0x75,
+ DW_AT_all_source_call_sites = 0x76,
+ DW_AT_call_site_parameter = 0x77,
+ DW_AT_tail_call = 0x78,
+ DW_AT_all_tail_call_sites = 0x79,
+ DW_AT_all_call_sites = 0x7a,
+ DW_AT_all_source_call_sites = 0x7b,
+ DW_AT_rank = 0x7c,
+ DW_AT_string_bitsize = 0x7d,
+ DW_AT_string_byte_size = 0x7e,
+ DW_AT_reference = 0x7f,
+ DW_AT_rvalue_reference = 0x80,
+ DW_AT_noreturn = 0x81,
+ DW_AT_alignment = 0x82,
+#endif
+ DW_AT_lo_user = 0x2000,
+ DW_AT_hi_user = 0xffff
+};
+
+enum Form : uint8_t {
+ DW_FORM_addr = 0x01,
+ DW_FORM_block2 = 0x03,
+ DW_FORM_block4 = 0x04,
+ DW_FORM_data2 = 0x05,
+ DW_FORM_data4 = 0x06,
+ DW_FORM_data8 = 0x07,
+ DW_FORM_string = 0x08,
+ DW_FORM_block = 0x09,
+ DW_FORM_block1 = 0x0a,
+ DW_FORM_data1 = 0x0b,
+ DW_FORM_flag = 0x0c,
+ DW_FORM_sdata = 0x0d,
+ DW_FORM_strp = 0x0e,
+ DW_FORM_udata = 0x0f,
+ DW_FORM_ref_addr = 0x10,
+ DW_FORM_ref1 = 0x11,
+ DW_FORM_ref2 = 0x12,
+ DW_FORM_ref4 = 0x13,
+ DW_FORM_ref8 = 0x14,
+ DW_FORM_ref_udata = 0x15,
+ DW_FORM_indirect = 0x16,
+ DW_FORM_sec_offset = 0x17,
+ DW_FORM_exprloc = 0x18,
+ DW_FORM_flag_present = 0x19,
+ DW_FORM_ref_sig8 = 0x20
+};
+
+enum Operation : uint16_t {
+ DW_OP_addr = 0x03,
+ DW_OP_deref = 0x06,
+ DW_OP_const1u = 0x08,
+ DW_OP_const1s = 0x09,
+ DW_OP_const2u = 0x0a,
+ DW_OP_const2s = 0x0b,
+ DW_OP_const4u = 0x0c,
+ DW_OP_const4s = 0x0d,
+ DW_OP_const8u = 0x0e,
+ DW_OP_const8s = 0x0f,
+ DW_OP_constu = 0x10,
+ DW_OP_consts = 0x11,
+ DW_OP_dup = 0x12,
+ DW_OP_drop = 0x13,
+ DW_OP_over = 0x14,
+ DW_OP_pick = 0x15,
+ DW_OP_swap = 0x16,
+ DW_OP_rot = 0x17,
+ DW_OP_xderef = 0x18,
+ DW_OP_abs = 0x19,
+ DW_OP_and = 0x1a,
+ DW_OP_div = 0x1b,
+ DW_OP_minus = 0x1c,
+ DW_OP_mod = 0x1d,
+ DW_OP_mul = 0x1e,
+ DW_OP_neg = 0x1f,
+ DW_OP_not = 0x20,
+ DW_OP_or = 0x21,
+ DW_OP_plus = 0x22,
+ DW_OP_plus_uconst = 0x23,
+ DW_OP_shl = 0x24,
+ DW_OP_shr = 0x25,
+ DW_OP_shra = 0x26,
+ DW_OP_xor = 0x27,
+ DW_OP_skip = 0x2f,
+ DW_OP_bra = 0x28,
+ DW_OP_eq = 0x29,
+ DW_OP_ge = 0x2a,
+ DW_OP_gt = 0x2b,
+ DW_OP_le = 0x2c,
+ DW_OP_lt = 0x2d,
+ DW_OP_ne = 0x2e,
+ DW_OP_lit0 = 0x30,
+ DW_OP_lit1 = 0x31,
+ DW_OP_lit2 = 0x32,
+ DW_OP_lit3 = 0x33,
+ DW_OP_lit4 = 0x34,
+ DW_OP_lit5 = 0x35,
+ DW_OP_lit6 = 0x36,
+ DW_OP_lit7 = 0x37,
+ DW_OP_lit8 = 0x38,
+ DW_OP_lit9 = 0x39,
+ DW_OP_lit10 = 0x3a,
+ DW_OP_lit11 = 0x3b,
+ DW_OP_lit12 = 0x3c,
+ DW_OP_lit13 = 0x3d,
+ DW_OP_lit14 = 0x3e,
+ DW_OP_lit15 = 0x3f,
+ DW_OP_lit16 = 0x40,
+ DW_OP_lit17 = 0x41,
+ DW_OP_lit18 = 0x42,
+ DW_OP_lit19 = 0x43,
+ DW_OP_lit20 = 0x44,
+ DW_OP_lit21 = 0x45,
+ DW_OP_lit22 = 0x46,
+ DW_OP_lit23 = 0x47,
+ DW_OP_lit24 = 0x48,
+ DW_OP_lit25 = 0x49,
+ DW_OP_lit26 = 0x4a,
+ DW_OP_lit27 = 0x4b,
+ DW_OP_lit28 = 0x4c,
+ DW_OP_lit29 = 0x4d,
+ DW_OP_lit30 = 0x4e,
+ DW_OP_lit31 = 0x4f,
+ DW_OP_reg0 = 0x50,
+ DW_OP_reg1 = 0x51,
+ DW_OP_reg2 = 0x52,
+ DW_OP_reg3 = 0x53,
+ DW_OP_reg4 = 0x54,
+ DW_OP_reg5 = 0x55,
+ DW_OP_reg6 = 0x56,
+ DW_OP_reg7 = 0x57,
+ DW_OP_reg8 = 0x58,
+ DW_OP_reg9 = 0x59,
+ DW_OP_reg10 = 0x5a,
+ DW_OP_reg11 = 0x5b,
+ DW_OP_reg12 = 0x5c,
+ DW_OP_reg13 = 0x5d,
+ DW_OP_reg14 = 0x5e,
+ DW_OP_reg15 = 0x5f,
+ DW_OP_reg16 = 0x60,
+ DW_OP_reg17 = 0x61,
+ DW_OP_reg18 = 0x62,
+ DW_OP_reg19 = 0x63,
+ DW_OP_reg20 = 0x64,
+ DW_OP_reg21 = 0x65,
+ DW_OP_reg22 = 0x66,
+ DW_OP_reg23 = 0x67,
+ DW_OP_reg24 = 0x68,
+ DW_OP_reg25 = 0x69,
+ DW_OP_reg26 = 0x6a,
+ DW_OP_reg27 = 0x6b,
+ DW_OP_reg28 = 0x6c,
+ DW_OP_reg29 = 0x6d,
+ DW_OP_reg30 = 0x6e,
+ DW_OP_reg31 = 0x6f,
+ DW_OP_breg0 = 0x70,
+ DW_OP_breg1 = 0x71,
+ DW_OP_breg2 = 0x72,
+ DW_OP_breg3 = 0x73,
+ DW_OP_breg4 = 0x74,
+ DW_OP_breg5 = 0x75,
+ DW_OP_breg6 = 0x76,
+ DW_OP_breg7 = 0x77,
+ DW_OP_breg8 = 0x78,
+ DW_OP_breg9 = 0x79,
+ DW_OP_breg10 = 0x7a,
+ DW_OP_breg11 = 0x7b,
+ DW_OP_breg12 = 0x7c,
+ DW_OP_breg13 = 0x7d,
+ DW_OP_breg14 = 0x7e,
+ DW_OP_breg15 = 0x7f,
+ DW_OP_breg16 = 0x80,
+ DW_OP_breg17 = 0x81,
+ DW_OP_breg18 = 0x82,
+ DW_OP_breg19 = 0x83,
+ DW_OP_breg20 = 0x84,
+ DW_OP_breg21 = 0x85,
+ DW_OP_breg22 = 0x86,
+ DW_OP_breg23 = 0x87,
+ DW_OP_breg24 = 0x88,
+ DW_OP_breg25 = 0x89,
+ DW_OP_breg26 = 0x8a,
+ DW_OP_breg27 = 0x8b,
+ DW_OP_breg28 = 0x8c,
+ DW_OP_breg29 = 0x8d,
+ DW_OP_breg30 = 0x8e,
+ DW_OP_breg31 = 0x8f,
+ DW_OP_regx = 0x90,
+ DW_OP_fbreg = 0x91,
+ DW_OP_bregx = 0x92,
+ DW_OP_piece = 0x93,
+ DW_OP_deref_size = 0x94,
+ DW_OP_xderef_size = 0x95,
+ DW_OP_nop = 0x96,
+ DW_OP_push_object_address = 0x97,
+ DW_OP_call2 = 0x98,
+ DW_OP_call4 = 0x99,
+ DW_OP_call_ref = 0x9a,
+ DW_OP_form_tls_address = 0x9b,
+ DW_OP_call_frame_cfa = 0x9c,
+ DW_OP_bit_piece = 0x9d,
+ DW_OP_implicit_value = 0x9e,
+ DW_OP_stack_value = 0x9f,
+#ifdef INCLUDE_DWARF5_VALUES
+ // Values to be added in Dwarf 5. Final value not yet specified. Values listed
+ // may be different than other implementations. Use with caution.
+ // TODO Update these values when Dwarf 5 is released.
+ DW_OP_entry_value = 0xa0,
+ DW_OP_const_type = 0xa1,
+ DW_OP_regval_type = 0xa2,
+ DW_OP_deref_type = 0xa3,
+ DW_OP_xderef_type = 0xa4,
+ DW_OP_convert = 0xa5,
+ DW_OP_reinterpret = 0xa6,
+#endif
+ DW_OP_lo_user = 0xe0,
+ DW_OP_hi_user = 0xff
+};
+
+enum BaseTypeEncoding : uint8_t {
+ DW_ATE_address = 0x01,
+ DW_ATE_boolean = 0x02,
+ DW_ATE_complex_float = 0x03,
+ DW_ATE_float = 0x04,
+ DW_ATE_signed = 0x05,
+ DW_ATE_signed_char = 0x06,
+ DW_ATE_unsigned = 0x07,
+ DW_ATE_unsigned_char = 0x08,
+ DW_ATE_imaginary_float = 0x09,
+ DW_ATE_packed_decimal = 0x0a,
+ DW_ATE_numeric_string = 0x0b,
+ DW_ATE_edited = 0x0c,
+ DW_ATE_signed_fixed = 0x0d,
+ DW_ATE_unsigned_fixed = 0x0e,
+ DW_ATE_decimal_float = 0x0f,
+ DW_ATE_UTF = 0x10,
+ DW_ATE_lo_user = 0x80,
+ DW_ATE_hi_user = 0xff
+};
+
+enum DecimalSign : uint8_t {
+ DW_DS_unsigned = 0x01,
+ DW_DS_leading_overpunch = 0x02,
+ DW_DS_trailing_overpunch = 0x03,
+ DW_DS_leading_separate = 0x04,
+ DW_DS_trailing_separate = 0x05
+};
+
+enum Endianity : uint8_t {
+ DW_END_default = 0x00,
+ DW_END_big = 0x01,
+ DW_END_little = 0x02,
+ DW_END_lo_user = 0x40,
+ DW_END_hi_user = 0xff
+};
+
+enum Accessibility : uint8_t {
+ DW_ACCESS_public = 0x01,
+ DW_ACCESS_protected = 0x02,
+ DW_ACCESS_private = 0x03
+};
+
+enum Visibility : uint8_t {
+ DW_VIS_local = 0x01,
+ DW_VIS_exported = 0x02,
+ DW_VIS_qualified = 0x03
+};
+
+enum Virtuality : uint8_t {
+ DW_VIRTUALITY_none = 0x00,
+ DW_VIRTUALITY_virtual = 0x01,
+ DW_VIRTUALITY_pure_virtual = 0x02
+};
+
+enum Language {
+ DW_LANG_C89 = 0x01,
+ DW_LANG_C = 0x02,
+ DW_LANG_Ada83 = 0x03,
+ DW_LANG_C_plus_plus = 0x04,
+ DW_LANG_Cobol74 = 0x05,
+ DW_LANG_Cobol85 = 0x06,
+ DW_LANG_Fortran77 = 0x07,
+ DW_LANG_Fortran90 = 0x08,
+ DW_LANG_Pascal83 = 0x09,
+ DW_LANG_Modula2 = 0x0a,
+ DW_LANG_Java = 0x0b,
+ DW_LANG_C99 = 0x0c,
+ DW_LANG_Ada95 = 0x0d,
+ DW_LANG_Fortran95 = 0x0e,
+ DW_LANG_PLI = 0x0f,
+ DW_LANG_ObjC = 0x10,
+ DW_LANG_ObjC_plus_plus = 0x11,
+ DW_LANG_UPC = 0x12,
+ DW_LANG_D = 0x13,
+ DW_LANG_Python = 0x14,
+#ifdef INCLUDE_DWARF5_VALUES
+ // Values to be added in Dwarf 5. Final value not yet specified. Values listed
+ // may be different than other implementations. Use with caution.
+ // TODO Update these values when Dwarf 5 is released.
+ DW_LANG_OpenCL = 0x15,
+ DW_LANG_Go = 0x16,
+ DW_LANG_Modula3 = 0x17,
+ DW_LANG_Haskell = 0x18,
+ DW_LANG_C_plus_plus_03 = 0x19,
+ DW_LANG_C_plus_plus_11 = 0x1a,
+ DW_LANG_OCaml = 0x1b,
+ DW_LANG_Rust = 0x1c,
+ DW_LANG_C11 = 0x1d,
+ DW_LANG_Swift = 0x1e,
+ DW_LANG_Julia = 0x1f,
+#endif
+ DW_LANG_lo_user = 0x8000,
+ DW_LANG_hi_user = 0xffff
+};
+
+enum Identifier : uint8_t {
+ DW_ID_case_sensitive = 0x00,
+ DW_ID_up_case = 0x01,
+ DW_ID_down_case = 0x02,
+ DW_ID_case_insensitive = 0x03
+};
+
+enum CallingConvention : uint8_t {
+ DW_CC_normal = 0x01,
+ DW_CC_program = 0x02,
+ DW_CC_nocall = 0x03,
+ DW_CC_lo_user = 0x40,
+ DW_CC_hi_user = 0xff
+};
+
+enum Inline : uint8_t {
+ DW_INL_not_inlined = 0x00,
+ DW_INL_inlined = 0x01,
+ DW_INL_declared_not_inlined = 0x02,
+ DW_INL_declared_inlined = 0x03
+};
+
+enum ArrayOrdering : uint8_t {
+ DW_ORD_row_major = 0x00,
+ DW_ORD_col_major = 0x01
+};
+
+enum DiscriminantList : uint8_t {
+ DW_DSC_label = 0x00,
+ DW_DSC_range = 0x01
+};
+
+enum LineNumberOpcode : uint8_t {
+ DW_LNS_copy = 0x01,
+ DW_LNS_advance_pc = 0x02,
+ DW_LNS_advance_line = 0x03,
+ DW_LNS_set_file = 0x04,
+ DW_LNS_set_column = 0x05,
+ DW_LNS_negate_stmt = 0x06,
+ DW_LNS_set_basic_block = 0x07,
+ DW_LNS_const_add_pc = 0x08,
+ DW_LNS_fixed_advance_pc = 0x09,
+ DW_LNS_set_prologue_end = 0x0a,
+ DW_LNS_set_epilogue_begin = 0x0b,
+ DW_LNS_set_isa = 0x0c
+};
+
+enum LineNumberExtendedOpcode : uint8_t {
+ DW_LNE_end_sequence = 0x01,
+ DW_LNE_set_address = 0x02,
+ DW_LNE_define_file = 0x03,
+ DW_LNE_set_discriminator = 0x04,
+ DW_LNE_lo_user = 0x80,
+ DW_LNE_hi_user = 0xff
+};
+
+#ifdef INCLUDE_DWARF5_VALUES
+enum LineNumberFormat : uint8_t {
+ // Values to be added in Dwarf 5. Final value not yet specified. Values listed
+ // may be different than other implementations. Use with caution.
+ // TODO Update these values when Dwarf 5 is released.
+ //
+ DW_LNF_path = 0x1,
+ DW_LNF_include_index = 0x2,
+ DW_LNF_timestamp = 0x3,
+ DW_LNF_size = 0x4,
+ DW_LNF_MD5 = 0x5,
+ DW_LNF_lo_user = 0x2000,
+ DW_LNF_hi_user = 0x3fff
+};
+#endif
+
+enum MacroInfo : uint8_t {
+ DW_MACINFO_define = 0x01,
+ DW_MACINFO_undef = 0x02,
+ DW_MACINFO_start_file = 0x03,
+ DW_MACINFO_end_file = 0x04,
+ DW_MACINFO_vendor_ext = 0xff
+};
+
+#ifdef INCLUDE_DWARF5_VALUES
+enum Macro : uint8_t {
+ // Values to be added in Dwarf 5. Final value not yet specified. Values listed
+ // may be different than other implementations. Use with caution.
+ // TODO Update these values when Dwarf 5 is released.
+ DW_MACRO_define = 0x01,
+ DW_MACRO_undef = 0x02,
+ DW_MACRO_start_file = 0x03,
+ DW_MACRO_end_file = 0x04,
+ DW_MACRO_define_indirect = 0x05,
+ DW_MACRO_undef_indirect = 0x06,
+ DW_MACRO_transparent_include = 0x07,
+ DW_MACRO_define_indirectx = 0x0b,
+ DW_MACRO_undef_indirectx = 0x0c,
+ DW_MACRO_lo_user = 0xe0,
+ DW_MACRO_hi_user = 0xff
+};
+#endif
+
+const uint32_t CIE_ID_32 = 0xffffffff;
+const uint64_t CIE_ID_64 = 0xffffffffffffffff;
+
+enum CallFrameInstruction : uint8_t {
+ DW_CFA_advance_loc = 0x40,
+ DW_CFA_offset = 0x80,
+ DW_CFA_restore = 0xc0,
+ DW_CFA_nop = 0x00,
+ DW_CFA_set_loc = 0x01,
+ DW_CFA_advance_loc1 = 0x02,
+ DW_CFA_advance_loc2 = 0x03,
+ DW_CFA_advance_loc4 = 0x04,
+ DW_CFA_offset_extended = 0x05,
+ DW_CFA_restore_extended = 0x06,
+ DW_CFA_undefined = 0x07,
+ DW_CFA_same_value = 0x08,
+ DW_CFA_register = 0x09,
+ DW_CFA_remember_state = 0x0a,
+ DW_CFA_restore_state = 0x0b,
+ DW_CFA_def_cfa = 0x0c,
+ DW_CFA_def_cfa_register = 0x0d,
+ DW_CFA_def_cfa_offset = 0x0e,
+ DW_CFA_def_cfa_expression = 0x0f,
+ DW_CFA_expression = 0x10,
+ DW_CFA_offset_extended_sf = 0x11,
+ DW_CFA_def_cfa_sf = 0x12,
+ DW_CFA_def_cfa_offset_sf = 0x13,
+ DW_CFA_val_offset = 0x14,
+ DW_CFA_val_offset_sf = 0x15,
+ DW_CFA_val_expression = 0x16,
+ DW_CFA_lo_user = 0x1c,
+ DW_CFA_hi_user = 0x3f
+};
+
+enum ExceptionHeaderValueFormat : uint8_t {
+ DW_EH_PE_native = 0x00,
+ DW_EH_PE_uleb128 = 0x01,
+ DW_EH_PE_udata2 = 0x02,
+ DW_EH_PE_udata4 = 0x03,
+ DW_EH_PE_udata8 = 0x04,
+ DW_EH_PE_sleb128 = 0x09,
+ DW_EH_PE_sdata2 = 0x0A,
+ DW_EH_PE_sdata4 = 0x0B,
+ DW_EH_PE_sdata8 = 0x0C,
+ DW_EH_PE_omit = 0xFF,
+};
+
+enum ExceptionHeaderValueApplication : uint8_t {
+ DW_EH_PE_absptr = 0x00,
+ DW_EH_PE_pcrel = 0x10,
+ DW_EH_PE_textrel = 0x20,
+ DW_EH_PE_datarel = 0x30,
+ DW_EH_PE_funcrel = 0x40,
+ DW_EH_PE_aligned = 0x50,
+};
+
+enum CFIFormat : uint8_t {
+ // This is the original format as defined by the specification.
+ // It is used for the .debug_frame section.
+ DW_DEBUG_FRAME_FORMAT,
+ // Slightly modified format used for the .eh_frame section.
+ DW_EH_FRAME_FORMAT
+};
+
+} // namespace dwarf
+} // namespace art
+
+#endif // ART_COMPILER_DEBUG_DWARF_DWARF_CONSTANTS_H_
diff --git a/compiler/debug/dwarf/dwarf_test.cc b/compiler/debug/dwarf/dwarf_test.cc
new file mode 100644
index 0000000..e455d0d
--- /dev/null
+++ b/compiler/debug/dwarf/dwarf_test.cc
@@ -0,0 +1,345 @@
+/*
+ * 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.
+ */
+
+#include "dwarf_test.h"
+
+#include "debug/dwarf/debug_frame_opcode_writer.h"
+#include "debug/dwarf/debug_info_entry_writer.h"
+#include "debug/dwarf/debug_line_opcode_writer.h"
+#include "debug/dwarf/dwarf_constants.h"
+#include "debug/dwarf/headers.h"
+#include "gtest/gtest.h"
+
+namespace art {
+namespace dwarf {
+
+// Run the tests only on host since we need objdump.
+#ifndef __ANDROID__
+
+constexpr CFIFormat kCFIFormat = DW_DEBUG_FRAME_FORMAT;
+
+TEST_F(DwarfTest, DebugFrame) {
+ const bool is64bit = false;
+
+ // Pick offset value which would catch Uleb vs Sleb errors.
+ const int offset = 40000;
+ ASSERT_EQ(UnsignedLeb128Size(offset / 4), 2u);
+ ASSERT_EQ(SignedLeb128Size(offset / 4), 3u);
+ DW_CHECK("Data alignment factor: -4");
+ const Reg reg(6);
+
+ // Test the opcodes in the order mentioned in the spec.
+ // There are usually several encoding variations of each opcode.
+ DebugFrameOpCodeWriter<> opcodes;
+ DW_CHECK("FDE");
+ int pc = 0;
+ for (int i : {0, 1, 0x3F, 0x40, 0xFF, 0x100, 0xFFFF, 0x10000}) {
+ pc += i;
+ opcodes.AdvancePC(pc);
+ }
+ DW_CHECK_NEXT("DW_CFA_advance_loc: 1 to 01000001");
+ DW_CHECK_NEXT("DW_CFA_advance_loc: 63 to 01000040");
+ DW_CHECK_NEXT("DW_CFA_advance_loc1: 64 to 01000080");
+ DW_CHECK_NEXT("DW_CFA_advance_loc1: 255 to 0100017f");
+ DW_CHECK_NEXT("DW_CFA_advance_loc2: 256 to 0100027f");
+ DW_CHECK_NEXT("DW_CFA_advance_loc2: 65535 to 0101027e");
+ DW_CHECK_NEXT("DW_CFA_advance_loc4: 65536 to 0102027e");
+ opcodes.DefCFA(reg, offset);
+ DW_CHECK_NEXT("DW_CFA_def_cfa: r6 (esi) ofs 40000");
+ opcodes.DefCFA(reg, -offset);
+ DW_CHECK_NEXT("DW_CFA_def_cfa_sf: r6 (esi) ofs -40000");
+ opcodes.DefCFARegister(reg);
+ DW_CHECK_NEXT("DW_CFA_def_cfa_register: r6 (esi)");
+ opcodes.DefCFAOffset(offset);
+ DW_CHECK_NEXT("DW_CFA_def_cfa_offset: 40000");
+ opcodes.DefCFAOffset(-offset);
+ DW_CHECK_NEXT("DW_CFA_def_cfa_offset_sf: -40000");
+ uint8_t expr[] = { 0 };
+ opcodes.DefCFAExpression(expr, arraysize(expr));
+ DW_CHECK_NEXT("DW_CFA_def_cfa_expression");
+ opcodes.Undefined(reg);
+ DW_CHECK_NEXT("DW_CFA_undefined: r6 (esi)");
+ opcodes.SameValue(reg);
+ DW_CHECK_NEXT("DW_CFA_same_value: r6 (esi)");
+ opcodes.Offset(Reg(0x3F), -offset);
+ // Bad register likely means that it does not exist on x86,
+ // but we want to test high register numbers anyway.
+ DW_CHECK_NEXT("DW_CFA_offset: bad register: r63 at cfa-40000");
+ opcodes.Offset(Reg(0x40), -offset);
+ DW_CHECK_NEXT("DW_CFA_offset_extended: bad register: r64 at cfa-40000");
+ opcodes.Offset(Reg(0x40), offset);
+ DW_CHECK_NEXT("DW_CFA_offset_extended_sf: bad register: r64 at cfa+40000");
+ opcodes.ValOffset(reg, -offset);
+ DW_CHECK_NEXT("DW_CFA_val_offset: r6 (esi) at cfa-40000");
+ opcodes.ValOffset(reg, offset);
+ DW_CHECK_NEXT("DW_CFA_val_offset_sf: r6 (esi) at cfa+40000");
+ opcodes.Register(reg, Reg(1));
+ DW_CHECK_NEXT("DW_CFA_register: r6 (esi) in r1 (ecx)");
+ opcodes.Expression(reg, expr, arraysize(expr));
+ DW_CHECK_NEXT("DW_CFA_expression: r6 (esi)");
+ opcodes.ValExpression(reg, expr, arraysize(expr));
+ DW_CHECK_NEXT("DW_CFA_val_expression: r6 (esi)");
+ opcodes.Restore(Reg(0x3F));
+ DW_CHECK_NEXT("DW_CFA_restore: bad register: r63");
+ opcodes.Restore(Reg(0x40));
+ DW_CHECK_NEXT("DW_CFA_restore_extended: bad register: r64");
+ opcodes.Restore(reg);
+ DW_CHECK_NEXT("DW_CFA_restore: r6 (esi)");
+ opcodes.RememberState();
+ DW_CHECK_NEXT("DW_CFA_remember_state");
+ opcodes.RestoreState();
+ DW_CHECK_NEXT("DW_CFA_restore_state");
+ opcodes.Nop();
+ DW_CHECK_NEXT("DW_CFA_nop");
+
+ // Also test helpers.
+ opcodes.DefCFA(Reg(4), 100); // ESP
+ DW_CHECK_NEXT("DW_CFA_def_cfa: r4 (esp) ofs 100");
+ opcodes.AdjustCFAOffset(8);
+ DW_CHECK_NEXT("DW_CFA_def_cfa_offset: 108");
+ opcodes.RelOffset(Reg(0), 0); // push R0
+ DW_CHECK_NEXT("DW_CFA_offset: r0 (eax) at cfa-108");
+ opcodes.RelOffset(Reg(1), 4); // push R1
+ DW_CHECK_NEXT("DW_CFA_offset: r1 (ecx) at cfa-104");
+ opcodes.RelOffsetForMany(Reg(2), 8, 1 | (1 << 3), 4); // push R2 and R5
+ DW_CHECK_NEXT("DW_CFA_offset: r2 (edx) at cfa-100");
+ DW_CHECK_NEXT("DW_CFA_offset: r5 (ebp) at cfa-96");
+ opcodes.RestoreMany(Reg(2), 1 | (1 << 3)); // pop R2 and R5
+ DW_CHECK_NEXT("DW_CFA_restore: r2 (edx)");
+ DW_CHECK_NEXT("DW_CFA_restore: r5 (ebp)");
+
+ DebugFrameOpCodeWriter<> initial_opcodes;
+ WriteCIE(is64bit, Reg(is64bit ? 16 : 8),
+ initial_opcodes, kCFIFormat, &debug_frame_data_);
+ std::vector<uintptr_t> debug_frame_patches;
+ std::vector<uintptr_t> expected_patches { 28 }; // NOLINT
+ WriteFDE(is64bit, 0, 0, 0x01000000, 0x01000000, ArrayRef<const uint8_t>(*opcodes.data()),
+ kCFIFormat, 0, &debug_frame_data_, &debug_frame_patches);
+
+ EXPECT_EQ(expected_patches, debug_frame_patches);
+ CheckObjdumpOutput(is64bit, "-W");
+}
+
+TEST_F(DwarfTest, DebugFrame64) {
+ constexpr bool is64bit = true;
+ DebugFrameOpCodeWriter<> initial_opcodes;
+ WriteCIE(is64bit, Reg(16),
+ initial_opcodes, kCFIFormat, &debug_frame_data_);
+ DebugFrameOpCodeWriter<> opcodes;
+ std::vector<uintptr_t> debug_frame_patches;
+ std::vector<uintptr_t> expected_patches { 32 }; // NOLINT
+ WriteFDE(is64bit, 0, 0, 0x0100000000000000, 0x0200000000000000,
+ ArrayRef<const uint8_t>(*opcodes.data()),
+ kCFIFormat, 0, &debug_frame_data_, &debug_frame_patches);
+ DW_CHECK("FDE cie=00000000 pc=100000000000000..300000000000000");
+
+ EXPECT_EQ(expected_patches, debug_frame_patches);
+ CheckObjdumpOutput(is64bit, "-W");
+}
+
+// Test x86_64 register mapping. It is the only non-trivial architecture.
+// ARM, X86, and Mips have: dwarf_reg = art_reg + constant.
+TEST_F(DwarfTest, x86_64_RegisterMapping) {
+ constexpr bool is64bit = true;
+ DebugFrameOpCodeWriter<> opcodes;
+ for (int i = 0; i < 16; i++) {
+ opcodes.RelOffset(Reg::X86_64Core(i), 0);
+ }
+ DW_CHECK("FDE");
+ DW_CHECK_NEXT("DW_CFA_offset: r0 (rax)");
+ DW_CHECK_NEXT("DW_CFA_offset: r2 (rcx)");
+ DW_CHECK_NEXT("DW_CFA_offset: r1 (rdx)");
+ DW_CHECK_NEXT("DW_CFA_offset: r3 (rbx)");
+ DW_CHECK_NEXT("DW_CFA_offset: r7 (rsp)");
+ DW_CHECK_NEXT("DW_CFA_offset: r6 (rbp)");
+ DW_CHECK_NEXT("DW_CFA_offset: r4 (rsi)");
+ DW_CHECK_NEXT("DW_CFA_offset: r5 (rdi)");
+ DW_CHECK_NEXT("DW_CFA_offset: r8 (r8)");
+ DW_CHECK_NEXT("DW_CFA_offset: r9 (r9)");
+ DW_CHECK_NEXT("DW_CFA_offset: r10 (r10)");
+ DW_CHECK_NEXT("DW_CFA_offset: r11 (r11)");
+ DW_CHECK_NEXT("DW_CFA_offset: r12 (r12)");
+ DW_CHECK_NEXT("DW_CFA_offset: r13 (r13)");
+ DW_CHECK_NEXT("DW_CFA_offset: r14 (r14)");
+ DW_CHECK_NEXT("DW_CFA_offset: r15 (r15)");
+ DebugFrameOpCodeWriter<> initial_opcodes;
+ WriteCIE(is64bit, Reg(16),
+ initial_opcodes, kCFIFormat, &debug_frame_data_);
+ std::vector<uintptr_t> debug_frame_patches;
+ WriteFDE(is64bit, 0, 0, 0x0100000000000000, 0x0200000000000000,
+ ArrayRef<const uint8_t>(*opcodes.data()),
+ kCFIFormat, 0, &debug_frame_data_, &debug_frame_patches);
+
+ CheckObjdumpOutput(is64bit, "-W");
+}
+
+TEST_F(DwarfTest, DebugLine) {
+ const bool is64bit = false;
+ const int code_factor_bits = 1;
+ DebugLineOpCodeWriter<> opcodes(is64bit, code_factor_bits);
+
+ std::vector<std::string> include_directories;
+ include_directories.push_back("/path/to/source");
+ DW_CHECK("/path/to/source");
+
+ std::vector<FileEntry> files {
+ { "file0.c", 0, 1000, 2000 },
+ { "file1.c", 1, 1000, 2000 },
+ { "file2.c", 1, 1000, 2000 },
+ };
+ DW_CHECK("1\t0\t1000\t2000\tfile0.c");
+ DW_CHECK_NEXT("2\t1\t1000\t2000\tfile1.c");
+ DW_CHECK_NEXT("3\t1\t1000\t2000\tfile2.c");
+
+ DW_CHECK("Line Number Statements");
+ opcodes.SetAddress(0x01000000);
+ DW_CHECK_NEXT("Extended opcode 2: set Address to 0x1000000");
+ opcodes.AddRow();
+ DW_CHECK_NEXT("Copy");
+ opcodes.AdvancePC(0x01000100);
+ DW_CHECK_NEXT("Advance PC by 256 to 0x1000100");
+ opcodes.SetFile(2);
+ DW_CHECK_NEXT("Set File Name to entry 2 in the File Name Table");
+ opcodes.AdvanceLine(3);
+ DW_CHECK_NEXT("Advance Line by 2 to 3");
+ opcodes.SetColumn(4);
+ DW_CHECK_NEXT("Set column to 4");
+ opcodes.NegateStmt();
+ DW_CHECK_NEXT("Set is_stmt to 0");
+ opcodes.SetBasicBlock();
+ DW_CHECK_NEXT("Set basic block");
+ opcodes.SetPrologueEnd();
+ DW_CHECK_NEXT("Set prologue_end to true");
+ opcodes.SetEpilogueBegin();
+ DW_CHECK_NEXT("Set epilogue_begin to true");
+ opcodes.SetISA(5);
+ DW_CHECK_NEXT("Set ISA to 5");
+ opcodes.EndSequence();
+ DW_CHECK_NEXT("Extended opcode 1: End of Sequence");
+ opcodes.DefineFile("file.c", 0, 1000, 2000);
+ DW_CHECK_NEXT("Extended opcode 3: define new File Table entry");
+ DW_CHECK_NEXT("Entry\tDir\tTime\tSize\tName");
+ DW_CHECK_NEXT("1\t0\t1000\t2000\tfile.c");
+
+ std::vector<uintptr_t> debug_line_patches;
+ std::vector<uintptr_t> expected_patches { 87 }; // NOLINT
+ WriteDebugLineTable(include_directories, files, opcodes,
+ 0, &debug_line_data_, &debug_line_patches);
+
+ EXPECT_EQ(expected_patches, debug_line_patches);
+ CheckObjdumpOutput(is64bit, "-W");
+}
+
+// DWARF has special one byte codes which advance PC and line at the same time.
+TEST_F(DwarfTest, DebugLineSpecialOpcodes) {
+ const bool is64bit = false;
+ const int code_factor_bits = 1;
+ uint32_t pc = 0x01000000;
+ int line = 1;
+ DebugLineOpCodeWriter<> opcodes(is64bit, code_factor_bits);
+ opcodes.SetAddress(pc);
+ size_t num_rows = 0;
+ DW_CHECK("Line Number Statements:");
+ DW_CHECK("Special opcode");
+ DW_CHECK("Advance PC by constant");
+ DW_CHECK("Decoded dump of debug contents of section .debug_line:");
+ DW_CHECK("Line number Starting address");
+ for (int addr_delta = 0; addr_delta < 80; addr_delta += 2) {
+ for (int line_delta = 16; line_delta >= -16; --line_delta) {
+ pc += addr_delta;
+ line += line_delta;
+ opcodes.AddRow(pc, line);
+ num_rows++;
+ ASSERT_EQ(opcodes.CurrentAddress(), pc);
+ ASSERT_EQ(opcodes.CurrentLine(), line);
+ char expected[1024];
+ sprintf(expected, "%i 0x%x", line, pc);
+ DW_CHECK_NEXT(expected);
+ }
+ }
+ EXPECT_LT(opcodes.data()->size(), num_rows * 3);
+
+ std::vector<std::string> directories;
+ std::vector<FileEntry> files { { "file.c", 0, 1000, 2000 } }; // NOLINT
+ std::vector<uintptr_t> debug_line_patches;
+ WriteDebugLineTable(directories, files, opcodes,
+ 0, &debug_line_data_, &debug_line_patches);
+
+ CheckObjdumpOutput(is64bit, "-W -WL");
+}
+
+TEST_F(DwarfTest, DebugInfo) {
+ constexpr bool is64bit = false;
+ DebugAbbrevWriter<> debug_abbrev(&debug_abbrev_data_);
+ DebugInfoEntryWriter<> info(is64bit, &debug_abbrev);
+ DW_CHECK("Contents of the .debug_info section:");
+ info.StartTag(dwarf::DW_TAG_compile_unit);
+ DW_CHECK("Abbrev Number: 1 (DW_TAG_compile_unit)");
+ info.WriteStrp(dwarf::DW_AT_producer, "Compiler name", &debug_str_data_);
+ DW_CHECK_NEXT("DW_AT_producer : (indirect string, offset: 0x0): Compiler name");
+ info.WriteAddr(dwarf::DW_AT_low_pc, 0x01000000);
+ DW_CHECK_NEXT("DW_AT_low_pc : 0x1000000");
+ info.WriteAddr(dwarf::DW_AT_high_pc, 0x02000000);
+ DW_CHECK_NEXT("DW_AT_high_pc : 0x2000000");
+ info.StartTag(dwarf::DW_TAG_subprogram);
+ DW_CHECK("Abbrev Number: 2 (DW_TAG_subprogram)");
+ info.WriteStrp(dwarf::DW_AT_name, "Foo", &debug_str_data_);
+ DW_CHECK_NEXT("DW_AT_name : (indirect string, offset: 0xe): Foo");
+ info.WriteAddr(dwarf::DW_AT_low_pc, 0x01010000);
+ DW_CHECK_NEXT("DW_AT_low_pc : 0x1010000");
+ info.WriteAddr(dwarf::DW_AT_high_pc, 0x01020000);
+ DW_CHECK_NEXT("DW_AT_high_pc : 0x1020000");
+ info.EndTag(); // DW_TAG_subprogram
+ info.StartTag(dwarf::DW_TAG_subprogram);
+ DW_CHECK("Abbrev Number: 2 (DW_TAG_subprogram)");
+ info.WriteStrp(dwarf::DW_AT_name, "Bar", &debug_str_data_);
+ DW_CHECK_NEXT("DW_AT_name : (indirect string, offset: 0x12): Bar");
+ info.WriteAddr(dwarf::DW_AT_low_pc, 0x01020000);
+ DW_CHECK_NEXT("DW_AT_low_pc : 0x1020000");
+ info.WriteAddr(dwarf::DW_AT_high_pc, 0x01030000);
+ DW_CHECK_NEXT("DW_AT_high_pc : 0x1030000");
+ info.EndTag(); // DW_TAG_subprogram
+ info.EndTag(); // DW_TAG_compile_unit
+ // Test that previous list was properly terminated and empty children.
+ info.StartTag(dwarf::DW_TAG_compile_unit);
+ info.EndTag(); // DW_TAG_compile_unit
+
+ // The abbrev table is just side product, but check it as well.
+ DW_CHECK("Abbrev Number: 3 (DW_TAG_compile_unit)");
+ DW_CHECK("Contents of the .debug_abbrev section:");
+ DW_CHECK("1 DW_TAG_compile_unit [has children]");
+ DW_CHECK_NEXT("DW_AT_producer DW_FORM_strp");
+ DW_CHECK_NEXT("DW_AT_low_pc DW_FORM_addr");
+ DW_CHECK_NEXT("DW_AT_high_pc DW_FORM_addr");
+ DW_CHECK("2 DW_TAG_subprogram [no children]");
+ DW_CHECK_NEXT("DW_AT_name DW_FORM_strp");
+ DW_CHECK_NEXT("DW_AT_low_pc DW_FORM_addr");
+ DW_CHECK_NEXT("DW_AT_high_pc DW_FORM_addr");
+ DW_CHECK("3 DW_TAG_compile_unit [no children]");
+
+ std::vector<uintptr_t> debug_info_patches;
+ std::vector<uintptr_t> expected_patches { 16, 20, 29, 33, 42, 46 }; // NOLINT
+ dwarf::WriteDebugInfoCU(0 /* debug_abbrev_offset */, info,
+ 0, &debug_info_data_, &debug_info_patches);
+
+ EXPECT_EQ(expected_patches, debug_info_patches);
+ CheckObjdumpOutput(is64bit, "-W");
+}
+
+#endif // __ANDROID__
+
+} // namespace dwarf
+} // namespace art
diff --git a/compiler/debug/dwarf/dwarf_test.h b/compiler/debug/dwarf/dwarf_test.h
new file mode 100644
index 0000000..41bfe79
--- /dev/null
+++ b/compiler/debug/dwarf/dwarf_test.h
@@ -0,0 +1,172 @@
+/*
+ * 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_COMPILER_DEBUG_DWARF_DWARF_TEST_H_
+#define ART_COMPILER_DEBUG_DWARF_DWARF_TEST_H_
+
+#include <cstring>
+#include <dirent.h>
+#include <memory>
+#include <set>
+#include <stdio.h>
+#include <string>
+#include <sys/types.h>
+
+#include "base/unix_file/fd_file.h"
+#include "common_runtime_test.h"
+#include "elf_builder.h"
+#include "gtest/gtest.h"
+#include "linker/file_output_stream.h"
+#include "os.h"
+
+namespace art {
+namespace dwarf {
+
+#define DW_CHECK(substring) Check(substring, false, __FILE__, __LINE__)
+#define DW_CHECK_NEXT(substring) Check(substring, true, __FILE__, __LINE__)
+
+class DwarfTest : public CommonRuntimeTest {
+ public:
+ static constexpr bool kPrintObjdumpOutput = false; // debugging.
+
+ struct ExpectedLine {
+ std::string substring;
+ bool next;
+ const char* at_file;
+ int at_line;
+ };
+
+ // Check that the objdump output contains given output.
+ // If next is true, it must be the next line. Otherwise lines are skipped.
+ void Check(const char* substr, bool next, const char* at_file, int at_line) {
+ expected_lines_.push_back(ExpectedLine {substr, next, at_file, at_line});
+ }
+
+ // Pretty-print the generated DWARF data using objdump.
+ template<typename ElfTypes>
+ std::vector<std::string> Objdump(const char* args) {
+ // Write simple elf file with just the DWARF sections.
+ InstructionSet isa = (sizeof(typename ElfTypes::Addr) == 8) ? kX86_64 : kX86;
+ ScratchFile file;
+ FileOutputStream output_stream(file.GetFile());
+ ElfBuilder<ElfTypes> builder(isa, &output_stream);
+ builder.Start();
+ if (!debug_info_data_.empty()) {
+ builder.WriteSection(".debug_info", &debug_info_data_);
+ }
+ if (!debug_abbrev_data_.empty()) {
+ builder.WriteSection(".debug_abbrev", &debug_abbrev_data_);
+ }
+ if (!debug_str_data_.empty()) {
+ builder.WriteSection(".debug_str", &debug_str_data_);
+ }
+ if (!debug_line_data_.empty()) {
+ builder.WriteSection(".debug_line", &debug_line_data_);
+ }
+ if (!debug_frame_data_.empty()) {
+ builder.WriteSection(".debug_frame", &debug_frame_data_);
+ }
+ builder.End();
+ EXPECT_TRUE(builder.Good());
+
+ // Read the elf file back using objdump.
+ std::vector<std::string> lines;
+ std::string cmd = GetAndroidHostToolsDir();
+ cmd = cmd + "objdump " + args + " " + file.GetFilename() + " 2>&1";
+ FILE* output = popen(cmd.data(), "r");
+ char buffer[1024];
+ const char* line;
+ while ((line = fgets(buffer, sizeof(buffer), output)) != nullptr) {
+ if (kPrintObjdumpOutput) {
+ printf("%s", line);
+ }
+ if (line[0] != '\0' && line[0] != '\n') {
+ EXPECT_TRUE(strstr(line, "objdump: Error:") == nullptr) << line;
+ EXPECT_TRUE(strstr(line, "objdump: Warning:") == nullptr) << line;
+ std::string str(line);
+ if (str.back() == '\n') {
+ str.pop_back();
+ }
+ lines.push_back(str);
+ }
+ }
+ pclose(output);
+ return lines;
+ }
+
+ std::vector<std::string> Objdump(bool is64bit, const char* args) {
+ if (is64bit) {
+ return Objdump<ElfTypes64>(args);
+ } else {
+ return Objdump<ElfTypes32>(args);
+ }
+ }
+
+ // Compare objdump output to the recorded checks.
+ void CheckObjdumpOutput(bool is64bit, const char* args) {
+ std::vector<std::string> actual_lines = Objdump(is64bit, args);
+ auto actual_line = actual_lines.begin();
+ for (const ExpectedLine& expected_line : expected_lines_) {
+ const std::string& substring = expected_line.substring;
+ if (actual_line == actual_lines.end()) {
+ ADD_FAILURE_AT(expected_line.at_file, expected_line.at_line) <<
+ "Expected '" << substring << "'.\n" <<
+ "Seen end of output.";
+ } else if (expected_line.next) {
+ if (actual_line->find(substring) == std::string::npos) {
+ ADD_FAILURE_AT(expected_line.at_file, expected_line.at_line) <<
+ "Expected '" << substring << "'.\n" <<
+ "Seen '" << actual_line->data() << "'.";
+ } else {
+ // printf("Found '%s' in '%s'.\n", substring.data(), actual_line->data());
+ }
+ actual_line++;
+ } else {
+ bool found = false;
+ for (auto it = actual_line; it < actual_lines.end(); it++) {
+ if (it->find(substring) != std::string::npos) {
+ actual_line = it;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ ADD_FAILURE_AT(expected_line.at_file, expected_line.at_line) <<
+ "Expected '" << substring << "'.\n" <<
+ "Not found anywhere in the rest of the output.";
+ } else {
+ // printf("Found '%s' in '%s'.\n", substring.data(), actual_line->data());
+ actual_line++;
+ }
+ }
+ }
+ }
+
+ // Buffers which are going to assembled into ELF file and passed to objdump.
+ std::vector<uint8_t> debug_frame_data_;
+ std::vector<uint8_t> debug_info_data_;
+ std::vector<uint8_t> debug_abbrev_data_;
+ std::vector<uint8_t> debug_str_data_;
+ std::vector<uint8_t> debug_line_data_;
+
+ // The expected output of objdump.
+ std::vector<ExpectedLine> expected_lines_;
+};
+
+} // namespace dwarf
+} // namespace art
+
+#endif // ART_COMPILER_DEBUG_DWARF_DWARF_TEST_H_
diff --git a/compiler/debug/dwarf/expression.h b/compiler/debug/dwarf/expression.h
new file mode 100644
index 0000000..fafc046
--- /dev/null
+++ b/compiler/debug/dwarf/expression.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2016 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_COMPILER_DEBUG_DWARF_EXPRESSION_H_
+#define ART_COMPILER_DEBUG_DWARF_EXPRESSION_H_
+
+#include <cstddef>
+#include <cstdint>
+
+#include "debug/dwarf/dwarf_constants.h"
+#include "debug/dwarf/writer.h"
+
+namespace art {
+namespace dwarf {
+
+// Writer for DWARF expressions which are used in .debug_info and .debug_loc sections.
+// See the DWARF specification for the precise meaning of the opcodes.
+// If multiple equivalent encodings are possible, it will choose the most compact one.
+// The writer is not exhaustive - it only implements opcodes we have needed so far.
+class Expression : private Writer<> {
+ public:
+ using Writer<>::data;
+ using Writer<>::size;
+
+ // Push signed integer on the stack.
+ void WriteOpConsts(int32_t value) {
+ if (0 <= value && value < 32) {
+ PushUint8(DW_OP_lit0 + value);
+ } else {
+ PushUint8(DW_OP_consts);
+ PushSleb128(value);
+ }
+ }
+
+ // Push unsigned integer on the stack.
+ void WriteOpConstu(uint32_t value) {
+ if (value < 32) {
+ PushUint8(DW_OP_lit0 + value);
+ } else {
+ PushUint8(DW_OP_constu);
+ PushUleb128(value);
+ }
+ }
+
+ // Variable is stored in given register.
+ void WriteOpReg(uint32_t dwarf_reg_num) {
+ if (dwarf_reg_num < 32) {
+ PushUint8(DW_OP_reg0 + dwarf_reg_num);
+ } else {
+ PushUint8(DW_OP_regx);
+ PushUleb128(dwarf_reg_num);
+ }
+ }
+
+ // Variable is stored on stack. Also see DW_AT_frame_base.
+ void WriteOpFbreg(int32_t stack_offset) {
+ PushUint8(DW_OP_fbreg);
+ PushSleb128(stack_offset);
+ }
+
+ // The variable is stored in multiple locations (pieces).
+ void WriteOpPiece(uint32_t num_bytes) {
+ PushUint8(DW_OP_piece);
+ PushUleb128(num_bytes);
+ }
+
+ // Loads 32-bit or 64-bit value depending on architecture.
+ void WriteOpDeref() { PushUint8(DW_OP_deref); }
+
+ // Loads value of given byte size.
+ void WriteOpDerefSize(uint8_t num_bytes) {
+ PushUint8(DW_OP_deref_size);
+ PushUint8(num_bytes);
+ }
+
+ // Pop two values and push their sum.
+ void WriteOpPlus() { PushUint8(DW_OP_plus); }
+
+ // Add constant value to value on top of stack.
+ void WriteOpPlusUconst(uint32_t offset) {
+ PushUint8(DW_OP_plus_uconst);
+ PushUleb128(offset);
+ }
+
+ // Negate top of stack.
+ void WriteOpNeg() { PushUint8(DW_OP_neg); }
+
+ // Pop two values and push their bitwise-AND.
+ void WriteOpAnd() { PushUint8(DW_OP_and); }
+
+ // Push stack base pointer as determined from .debug_frame.
+ void WriteOpCallFrameCfa() { PushUint8(DW_OP_call_frame_cfa); }
+
+ // Push address of the variable we are working with.
+ void WriteOpPushObjectAddress() { PushUint8(DW_OP_push_object_address); }
+
+ // Return the top stack as the value of the variable.
+ // Otherwise, the top of stack is the variable's location.
+ void WriteOpStackValue() { PushUint8(DW_OP_stack_value); }
+
+ explicit Expression(std::vector<uint8_t>* buffer) : Writer<>(buffer) {
+ buffer->clear();
+ }
+};
+} // namespace dwarf
+} // namespace art
+
+#endif // ART_COMPILER_DEBUG_DWARF_EXPRESSION_H_
diff --git a/compiler/debug/dwarf/headers.h b/compiler/debug/dwarf/headers.h
new file mode 100644
index 0000000..146d9fd
--- /dev/null
+++ b/compiler/debug/dwarf/headers.h
@@ -0,0 +1,207 @@
+/*
+ * 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_COMPILER_DEBUG_DWARF_HEADERS_H_
+#define ART_COMPILER_DEBUG_DWARF_HEADERS_H_
+
+#include <cstdint>
+
+#include "debug/dwarf/debug_frame_opcode_writer.h"
+#include "debug/dwarf/debug_info_entry_writer.h"
+#include "debug/dwarf/debug_line_opcode_writer.h"
+#include "debug/dwarf/dwarf_constants.h"
+#include "debug/dwarf/register.h"
+#include "debug/dwarf/writer.h"
+#include "utils/array_ref.h"
+
+namespace art {
+namespace dwarf {
+
+// Note that all headers start with 32-bit length.
+// DWARF also supports 64-bit lengths, but we never use that.
+// It is intended to support very large debug sections (>4GB),
+// and compilers are expected *not* to use it by default.
+// In particular, it is not related to machine architecture.
+
+// Write common information entry (CIE) to .debug_frame or .eh_frame section.
+template<typename Vector>
+void WriteCIE(bool is64bit,
+ Reg return_address_register,
+ const DebugFrameOpCodeWriter<Vector>& opcodes,
+ CFIFormat format,
+ std::vector<uint8_t>* buffer) {
+ static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type");
+
+ Writer<> writer(buffer);
+ size_t cie_header_start_ = writer.data()->size();
+ writer.PushUint32(0); // Length placeholder.
+ writer.PushUint32((format == DW_EH_FRAME_FORMAT) ? 0 : 0xFFFFFFFF); // CIE id.
+ writer.PushUint8(1); // Version.
+ writer.PushString("zR");
+ writer.PushUleb128(DebugFrameOpCodeWriter<Vector>::kCodeAlignmentFactor);
+ writer.PushSleb128(DebugFrameOpCodeWriter<Vector>::kDataAlignmentFactor);
+ writer.PushUleb128(return_address_register.num()); // ubyte in DWARF2.
+ writer.PushUleb128(1); // z: Augmentation data size.
+ if (is64bit) {
+ if (format == DW_EH_FRAME_FORMAT) {
+ writer.PushUint8(DW_EH_PE_pcrel | DW_EH_PE_sdata8); // R: Pointer encoding.
+ } else {
+ DCHECK(format == DW_DEBUG_FRAME_FORMAT);
+ writer.PushUint8(DW_EH_PE_absptr | DW_EH_PE_udata8); // R: Pointer encoding.
+ }
+ } else {
+ if (format == DW_EH_FRAME_FORMAT) {
+ writer.PushUint8(DW_EH_PE_pcrel | DW_EH_PE_sdata4); // R: Pointer encoding.
+ } else {
+ DCHECK(format == DW_DEBUG_FRAME_FORMAT);
+ writer.PushUint8(DW_EH_PE_absptr | DW_EH_PE_udata4); // R: Pointer encoding.
+ }
+ }
+ writer.PushData(opcodes.data());
+ writer.Pad(is64bit ? 8 : 4);
+ writer.UpdateUint32(cie_header_start_, writer.data()->size() - cie_header_start_ - 4);
+}
+
+// Write frame description entry (FDE) to .debug_frame or .eh_frame section.
+inline
+void WriteFDE(bool is64bit,
+ uint64_t section_address, // Absolute address of the section.
+ uint64_t cie_address, // Absolute address of last CIE.
+ uint64_t code_address,
+ uint64_t code_size,
+ const ArrayRef<const uint8_t>& opcodes,
+ CFIFormat format,
+ uint64_t buffer_address, // Address of buffer in linked application.
+ std::vector<uint8_t>* buffer,
+ std::vector<uintptr_t>* patch_locations) {
+ CHECK_GE(cie_address, section_address);
+ CHECK_GE(buffer_address, section_address);
+
+ Writer<> writer(buffer);
+ size_t fde_header_start = writer.data()->size();
+ writer.PushUint32(0); // Length placeholder.
+ if (format == DW_EH_FRAME_FORMAT) {
+ uint32_t cie_pointer = (buffer_address + buffer->size()) - cie_address;
+ writer.PushUint32(cie_pointer);
+ } else {
+ DCHECK(format == DW_DEBUG_FRAME_FORMAT);
+ uint32_t cie_pointer = cie_address - section_address;
+ writer.PushUint32(cie_pointer);
+ }
+ if (format == DW_EH_FRAME_FORMAT) {
+ // .eh_frame encodes the location as relative address.
+ code_address -= buffer_address + buffer->size();
+ } else {
+ DCHECK(format == DW_DEBUG_FRAME_FORMAT);
+ // Relocate code_address if it has absolute value.
+ patch_locations->push_back(buffer_address + buffer->size() - section_address);
+ }
+ if (is64bit) {
+ writer.PushUint64(code_address);
+ writer.PushUint64(code_size);
+ } else {
+ writer.PushUint32(code_address);
+ writer.PushUint32(code_size);
+ }
+ writer.PushUleb128(0); // Augmentation data size.
+ writer.PushData(opcodes.data(), opcodes.size());
+ writer.Pad(is64bit ? 8 : 4);
+ writer.UpdateUint32(fde_header_start, writer.data()->size() - fde_header_start - 4);
+}
+
+// Write compilation unit (CU) to .debug_info section.
+template<typename Vector>
+void WriteDebugInfoCU(uint32_t debug_abbrev_offset,
+ const DebugInfoEntryWriter<Vector>& entries,
+ size_t debug_info_offset, // offset from start of .debug_info.
+ std::vector<uint8_t>* debug_info,
+ std::vector<uintptr_t>* debug_info_patches) {
+ static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type");
+
+ Writer<> writer(debug_info);
+ size_t start = writer.data()->size();
+ writer.PushUint32(0); // Length placeholder.
+ writer.PushUint16(4); // Version.
+ writer.PushUint32(debug_abbrev_offset);
+ writer.PushUint8(entries.Is64bit() ? 8 : 4);
+ size_t entries_offset = writer.data()->size();
+ DCHECK_EQ(entries_offset, DebugInfoEntryWriter<Vector>::kCompilationUnitHeaderSize);
+ writer.PushData(entries.data());
+ writer.UpdateUint32(start, writer.data()->size() - start - 4);
+ // Copy patch locations and make them relative to .debug_info section.
+ for (uintptr_t patch_location : entries.GetPatchLocations()) {
+ debug_info_patches->push_back(debug_info_offset + entries_offset + patch_location);
+ }
+}
+
+struct FileEntry {
+ std::string file_name;
+ int directory_index;
+ int modification_time;
+ int file_size;
+};
+
+// Write line table to .debug_line section.
+template<typename Vector>
+void WriteDebugLineTable(const std::vector<std::string>& include_directories,
+ const std::vector<FileEntry>& files,
+ const DebugLineOpCodeWriter<Vector>& opcodes,
+ size_t debug_line_offset, // offset from start of .debug_line.
+ std::vector<uint8_t>* debug_line,
+ std::vector<uintptr_t>* debug_line_patches) {
+ static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type");
+
+ Writer<> writer(debug_line);
+ size_t header_start = writer.data()->size();
+ writer.PushUint32(0); // Section-length placeholder.
+ writer.PushUint16(3); // .debug_line version.
+ size_t header_length_pos = writer.data()->size();
+ writer.PushUint32(0); // Header-length placeholder.
+ writer.PushUint8(1 << opcodes.GetCodeFactorBits());
+ writer.PushUint8(DebugLineOpCodeWriter<Vector>::kDefaultIsStmt ? 1 : 0);
+ writer.PushInt8(DebugLineOpCodeWriter<Vector>::kLineBase);
+ writer.PushUint8(DebugLineOpCodeWriter<Vector>::kLineRange);
+ writer.PushUint8(DebugLineOpCodeWriter<Vector>::kOpcodeBase);
+ static const int opcode_lengths[DebugLineOpCodeWriter<Vector>::kOpcodeBase] = {
+ 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 };
+ for (int i = 1; i < DebugLineOpCodeWriter<Vector>::kOpcodeBase; i++) {
+ writer.PushUint8(opcode_lengths[i]);
+ }
+ for (const std::string& directory : include_directories) {
+ writer.PushData(directory.data(), directory.size() + 1);
+ }
+ writer.PushUint8(0); // Terminate include_directories list.
+ for (const FileEntry& file : files) {
+ writer.PushData(file.file_name.data(), file.file_name.size() + 1);
+ writer.PushUleb128(file.directory_index);
+ writer.PushUleb128(file.modification_time);
+ writer.PushUleb128(file.file_size);
+ }
+ writer.PushUint8(0); // Terminate file list.
+ writer.UpdateUint32(header_length_pos, writer.data()->size() - header_length_pos - 4);
+ size_t opcodes_offset = writer.data()->size();
+ writer.PushData(opcodes.data());
+ writer.UpdateUint32(header_start, writer.data()->size() - header_start - 4);
+ // Copy patch locations and make them relative to .debug_line section.
+ for (uintptr_t patch_location : opcodes.GetPatchLocations()) {
+ debug_line_patches->push_back(debug_line_offset + opcodes_offset + patch_location);
+ }
+}
+
+} // namespace dwarf
+} // namespace art
+
+#endif // ART_COMPILER_DEBUG_DWARF_HEADERS_H_
diff --git a/compiler/debug/dwarf/register.h b/compiler/debug/dwarf/register.h
new file mode 100644
index 0000000..24bacac
--- /dev/null
+++ b/compiler/debug/dwarf/register.h
@@ -0,0 +1,62 @@
+/*
+ * 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_COMPILER_DEBUG_DWARF_REGISTER_H_
+#define ART_COMPILER_DEBUG_DWARF_REGISTER_H_
+
+namespace art {
+namespace dwarf {
+
+// Represents DWARF register.
+class Reg {
+ public:
+ explicit Reg(int reg_num) : num_(reg_num) { }
+ int num() const { return num_; }
+
+ // TODO: Arm S0–S31 register mapping is obsolescent.
+ // We should use VFP-v3/Neon D0-D31 mapping instead.
+ // However, D0 is aliased to pair of S0 and S1, so using that
+ // mapping we cannot easily say S0 is spilled and S1 is not.
+ // There are ways around this in DWARF but they are complex.
+ // It would be much simpler to always spill whole D registers.
+ // Arm64 mapping is correct since we already do this there.
+ // libunwind might struggle with the new mapping as well.
+
+ static Reg ArmCore(int num) { return Reg(num); } // R0-R15.
+ static Reg ArmFp(int num) { return Reg(64 + num); } // S0–S31.
+ static Reg ArmDp(int num) { return Reg(256 + num); } // D0–D31.
+ static Reg Arm64Core(int num) { return Reg(num); } // X0-X31.
+ static Reg Arm64Fp(int num) { return Reg(64 + num); } // V0-V31.
+ static Reg MipsCore(int num) { return Reg(num); }
+ static Reg Mips64Core(int num) { return Reg(num); }
+ static Reg MipsFp(int num) { return Reg(32 + num); }
+ static Reg Mips64Fp(int num) { return Reg(32 + num); }
+ static Reg X86Core(int num) { return Reg(num); }
+ static Reg X86Fp(int num) { return Reg(21 + num); }
+ static Reg X86_64Core(int num) {
+ static const int map[8] = {0, 2, 1, 3, 7, 6, 4, 5};
+ return Reg(num < 8 ? map[num] : num);
+ }
+ static Reg X86_64Fp(int num) { return Reg(17 + num); }
+
+ private:
+ int num_;
+};
+
+} // namespace dwarf
+} // namespace art
+
+#endif // ART_COMPILER_DEBUG_DWARF_REGISTER_H_
diff --git a/compiler/debug/dwarf/writer.h b/compiler/debug/dwarf/writer.h
new file mode 100644
index 0000000..95912ad
--- /dev/null
+++ b/compiler/debug/dwarf/writer.h
@@ -0,0 +1,182 @@
+/*
+ * 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_COMPILER_DEBUG_DWARF_WRITER_H_
+#define ART_COMPILER_DEBUG_DWARF_WRITER_H_
+
+#include <type_traits>
+#include <vector>
+#include "base/bit_utils.h"
+#include "base/logging.h"
+#include "leb128.h"
+
+namespace art {
+namespace dwarf {
+
+// The base class for all DWARF writers.
+template <typename Vector = std::vector<uint8_t>>
+class Writer {
+ static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type");
+
+ public:
+ void PushUint8(int value) {
+ DCHECK_GE(value, 0);
+ DCHECK_LE(value, UINT8_MAX);
+ data_->push_back(value & 0xff);
+ }
+
+ void PushUint16(int value) {
+ DCHECK_GE(value, 0);
+ DCHECK_LE(value, UINT16_MAX);
+ data_->push_back((value >> 0) & 0xff);
+ data_->push_back((value >> 8) & 0xff);
+ }
+
+ void PushUint32(uint32_t value) {
+ data_->push_back((value >> 0) & 0xff);
+ data_->push_back((value >> 8) & 0xff);
+ data_->push_back((value >> 16) & 0xff);
+ data_->push_back((value >> 24) & 0xff);
+ }
+
+ void PushUint32(int value) {
+ DCHECK_GE(value, 0);
+ PushUint32(static_cast<uint32_t>(value));
+ }
+
+ void PushUint32(uint64_t value) {
+ DCHECK_LE(value, UINT32_MAX);
+ PushUint32(static_cast<uint32_t>(value));
+ }
+
+ void PushUint64(uint64_t value) {
+ data_->push_back((value >> 0) & 0xff);
+ data_->push_back((value >> 8) & 0xff);
+ data_->push_back((value >> 16) & 0xff);
+ data_->push_back((value >> 24) & 0xff);
+ data_->push_back((value >> 32) & 0xff);
+ data_->push_back((value >> 40) & 0xff);
+ data_->push_back((value >> 48) & 0xff);
+ data_->push_back((value >> 56) & 0xff);
+ }
+
+ void PushInt8(int value) {
+ DCHECK_GE(value, INT8_MIN);
+ DCHECK_LE(value, INT8_MAX);
+ PushUint8(static_cast<uint8_t>(value));
+ }
+
+ void PushInt16(int value) {
+ DCHECK_GE(value, INT16_MIN);
+ DCHECK_LE(value, INT16_MAX);
+ PushUint16(static_cast<uint16_t>(value));
+ }
+
+ void PushInt32(int value) {
+ PushUint32(static_cast<uint32_t>(value));
+ }
+
+ void PushInt64(int64_t value) {
+ PushUint64(static_cast<uint64_t>(value));
+ }
+
+ // Variable-length encoders.
+
+ void PushUleb128(uint32_t value) {
+ EncodeUnsignedLeb128(data_, value);
+ }
+
+ void PushUleb128(int value) {
+ DCHECK_GE(value, 0);
+ EncodeUnsignedLeb128(data_, value);
+ }
+
+ void PushSleb128(int value) {
+ EncodeSignedLeb128(data_, value);
+ }
+
+ // Miscellaneous functions.
+
+ void PushString(const char* value) {
+ data_->insert(data_->end(), value, value + strlen(value) + 1);
+ }
+
+ void PushData(const uint8_t* ptr, size_t num_bytes) {
+ data_->insert(data_->end(), ptr, ptr + num_bytes);
+ }
+
+ void PushData(const char* ptr, size_t num_bytes) {
+ data_->insert(data_->end(), ptr, ptr + num_bytes);
+ }
+
+ void PushData(const Vector* buffer) {
+ data_->insert(data_->end(), buffer->begin(), buffer->end());
+ }
+
+ void UpdateUint32(size_t offset, uint32_t value) {
+ DCHECK_LT(offset + 3, data_->size());
+ (*data_)[offset + 0] = (value >> 0) & 0xFF;
+ (*data_)[offset + 1] = (value >> 8) & 0xFF;
+ (*data_)[offset + 2] = (value >> 16) & 0xFF;
+ (*data_)[offset + 3] = (value >> 24) & 0xFF;
+ }
+
+ void UpdateUint64(size_t offset, uint64_t value) {
+ DCHECK_LT(offset + 7, data_->size());
+ (*data_)[offset + 0] = (value >> 0) & 0xFF;
+ (*data_)[offset + 1] = (value >> 8) & 0xFF;
+ (*data_)[offset + 2] = (value >> 16) & 0xFF;
+ (*data_)[offset + 3] = (value >> 24) & 0xFF;
+ (*data_)[offset + 4] = (value >> 32) & 0xFF;
+ (*data_)[offset + 5] = (value >> 40) & 0xFF;
+ (*data_)[offset + 6] = (value >> 48) & 0xFF;
+ (*data_)[offset + 7] = (value >> 56) & 0xFF;
+ }
+
+ void UpdateUleb128(size_t offset, uint32_t value) {
+ DCHECK_LE(offset + UnsignedLeb128Size(value), data_->size());
+ UpdateUnsignedLeb128(data_->data() + offset, value);
+ }
+
+ void Pop() {
+ return data_->pop_back();
+ }
+
+ void Pad(int alignment) {
+ DCHECK_NE(alignment, 0);
+ data_->resize(RoundUp(data_->size(), alignment), 0);
+ }
+
+ const Vector* data() const {
+ return data_;
+ }
+
+ size_t size() const {
+ return data_->size();
+ }
+
+ explicit Writer(Vector* buffer) : data_(buffer) { }
+
+ private:
+ Vector* const data_;
+
+ DISALLOW_COPY_AND_ASSIGN(Writer);
+};
+
+} // namespace dwarf
+} // namespace art
+
+#endif // ART_COMPILER_DEBUG_DWARF_WRITER_H_
diff --git a/compiler/debug/elf_writer_debug.cc b/compiler/debug/elf_writer_debug.cc
new file mode 100644
index 0000000..07d16d2
--- /dev/null
+++ b/compiler/debug/elf_writer_debug.cc
@@ -0,0 +1,1666 @@
+/*
+ * 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.
+ */
+
+#include "elf_writer_debug.h"
+
+#include <algorithm>
+#include <unordered_set>
+#include <vector>
+#include <cstdio>
+
+#include "base/casts.h"
+#include "base/stl_util.h"
+#include "compiled_method.h"
+#include "debug/dwarf/expression.h"
+#include "debug/dwarf/headers.h"
+#include "debug/dwarf/register.h"
+#include "debug/method_debug_info.h"
+#include "dex_file-inl.h"
+#include "driver/compiler_driver.h"
+#include "elf_builder.h"
+#include "linear_alloc.h"
+#include "linker/vector_output_stream.h"
+#include "mirror/array.h"
+#include "mirror/class-inl.h"
+#include "mirror/class.h"
+#include "oat_writer.h"
+#include "stack_map.h"
+#include "utils.h"
+
+// liblzma.
+#include "XzEnc.h"
+#include "7zCrc.h"
+#include "XzCrc64.h"
+
+namespace art {
+namespace dwarf {
+
+// The ARM specification defines three special mapping symbols
+// $a, $t and $d which mark ARM, Thumb and data ranges respectively.
+// These symbols can be used by tools, for example, to pretty
+// print instructions correctly. Objdump will use them if they
+// exist, but it will still work well without them.
+// However, these extra symbols take space, so let's just generate
+// one symbol which marks the whole .text section as code.
+constexpr bool kGenerateSingleArmMappingSymbol = true;
+
+static Reg GetDwarfCoreReg(InstructionSet isa, int machine_reg) {
+ switch (isa) {
+ case kArm:
+ case kThumb2:
+ return Reg::ArmCore(machine_reg);
+ case kArm64:
+ return Reg::Arm64Core(machine_reg);
+ case kX86:
+ return Reg::X86Core(machine_reg);
+ case kX86_64:
+ return Reg::X86_64Core(machine_reg);
+ case kMips:
+ return Reg::MipsCore(machine_reg);
+ case kMips64:
+ return Reg::Mips64Core(machine_reg);
+ default:
+ LOG(FATAL) << "Unknown instruction set: " << isa;
+ UNREACHABLE();
+ }
+}
+
+static Reg GetDwarfFpReg(InstructionSet isa, int machine_reg) {
+ switch (isa) {
+ case kArm:
+ case kThumb2:
+ return Reg::ArmFp(machine_reg);
+ case kArm64:
+ return Reg::Arm64Fp(machine_reg);
+ case kX86:
+ return Reg::X86Fp(machine_reg);
+ case kX86_64:
+ return Reg::X86_64Fp(machine_reg);
+ case kMips:
+ return Reg::MipsFp(machine_reg);
+ case kMips64:
+ return Reg::Mips64Fp(machine_reg);
+ default:
+ LOG(FATAL) << "Unknown instruction set: " << isa;
+ UNREACHABLE();
+ }
+}
+
+static void WriteCIE(InstructionSet isa,
+ CFIFormat format,
+ std::vector<uint8_t>* buffer) {
+ // Scratch registers should be marked as undefined. This tells the
+ // debugger that its value in the previous frame is not recoverable.
+ bool is64bit = Is64BitInstructionSet(isa);
+ switch (isa) {
+ case kArm:
+ case kThumb2: {
+ DebugFrameOpCodeWriter<> opcodes;
+ opcodes.DefCFA(Reg::ArmCore(13), 0); // R13(SP).
+ // core registers.
+ for (int reg = 0; reg < 13; reg++) {
+ if (reg < 4 || reg == 12) {
+ opcodes.Undefined(Reg::ArmCore(reg));
+ } else {
+ opcodes.SameValue(Reg::ArmCore(reg));
+ }
+ }
+ // fp registers.
+ for (int reg = 0; reg < 32; reg++) {
+ if (reg < 16) {
+ opcodes.Undefined(Reg::ArmFp(reg));
+ } else {
+ opcodes.SameValue(Reg::ArmFp(reg));
+ }
+ }
+ auto return_reg = Reg::ArmCore(14); // R14(LR).
+ WriteCIE(is64bit, return_reg, opcodes, format, buffer);
+ return;
+ }
+ case kArm64: {
+ DebugFrameOpCodeWriter<> opcodes;
+ opcodes.DefCFA(Reg::Arm64Core(31), 0); // R31(SP).
+ // core registers.
+ for (int reg = 0; reg < 30; reg++) {
+ if (reg < 8 || reg == 16 || reg == 17) {
+ opcodes.Undefined(Reg::Arm64Core(reg));
+ } else {
+ opcodes.SameValue(Reg::Arm64Core(reg));
+ }
+ }
+ // fp registers.
+ for (int reg = 0; reg < 32; reg++) {
+ if (reg < 8 || reg >= 16) {
+ opcodes.Undefined(Reg::Arm64Fp(reg));
+ } else {
+ opcodes.SameValue(Reg::Arm64Fp(reg));
+ }
+ }
+ auto return_reg = Reg::Arm64Core(30); // R30(LR).
+ WriteCIE(is64bit, return_reg, opcodes, format, buffer);
+ return;
+ }
+ case kMips:
+ case kMips64: {
+ DebugFrameOpCodeWriter<> opcodes;
+ opcodes.DefCFA(Reg::MipsCore(29), 0); // R29(SP).
+ // core registers.
+ for (int reg = 1; reg < 26; reg++) {
+ if (reg < 16 || reg == 24 || reg == 25) { // AT, V*, A*, T*.
+ opcodes.Undefined(Reg::MipsCore(reg));
+ } else {
+ opcodes.SameValue(Reg::MipsCore(reg));
+ }
+ }
+ // fp registers.
+ for (int reg = 0; reg < 32; reg++) {
+ if (reg < 24) {
+ opcodes.Undefined(Reg::Mips64Fp(reg));
+ } else {
+ opcodes.SameValue(Reg::Mips64Fp(reg));
+ }
+ }
+ auto return_reg = Reg::MipsCore(31); // R31(RA).
+ WriteCIE(is64bit, return_reg, opcodes, format, buffer);
+ return;
+ }
+ case kX86: {
+ // FIXME: Add fp registers once libunwind adds support for them. Bug: 20491296
+ constexpr bool generate_opcodes_for_x86_fp = false;
+ DebugFrameOpCodeWriter<> opcodes;
+ opcodes.DefCFA(Reg::X86Core(4), 4); // R4(ESP).
+ opcodes.Offset(Reg::X86Core(8), -4); // R8(EIP).
+ // core registers.
+ for (int reg = 0; reg < 8; reg++) {
+ if (reg <= 3) {
+ opcodes.Undefined(Reg::X86Core(reg));
+ } else if (reg == 4) {
+ // Stack pointer.
+ } else {
+ opcodes.SameValue(Reg::X86Core(reg));
+ }
+ }
+ // fp registers.
+ if (generate_opcodes_for_x86_fp) {
+ for (int reg = 0; reg < 8; reg++) {
+ opcodes.Undefined(Reg::X86Fp(reg));
+ }
+ }
+ auto return_reg = Reg::X86Core(8); // R8(EIP).
+ WriteCIE(is64bit, return_reg, opcodes, format, buffer);
+ return;
+ }
+ case kX86_64: {
+ DebugFrameOpCodeWriter<> opcodes;
+ opcodes.DefCFA(Reg::X86_64Core(4), 8); // R4(RSP).
+ opcodes.Offset(Reg::X86_64Core(16), -8); // R16(RIP).
+ // core registers.
+ for (int reg = 0; reg < 16; reg++) {
+ if (reg == 4) {
+ // Stack pointer.
+ } else if (reg < 12 && reg != 3 && reg != 5) { // except EBX and EBP.
+ opcodes.Undefined(Reg::X86_64Core(reg));
+ } else {
+ opcodes.SameValue(Reg::X86_64Core(reg));
+ }
+ }
+ // fp registers.
+ for (int reg = 0; reg < 16; reg++) {
+ if (reg < 12) {
+ opcodes.Undefined(Reg::X86_64Fp(reg));
+ } else {
+ opcodes.SameValue(Reg::X86_64Fp(reg));
+ }
+ }
+ auto return_reg = Reg::X86_64Core(16); // R16(RIP).
+ WriteCIE(is64bit, return_reg, opcodes, format, buffer);
+ return;
+ }
+ case kNone:
+ break;
+ }
+ LOG(FATAL) << "Cannot write CIE frame for ISA " << isa;
+ UNREACHABLE();
+}
+
+template<typename ElfTypes>
+void WriteCFISection(ElfBuilder<ElfTypes>* builder,
+ const ArrayRef<const MethodDebugInfo>& method_infos,
+ CFIFormat format,
+ bool write_oat_patches) {
+ CHECK(format == DW_DEBUG_FRAME_FORMAT || format == DW_EH_FRAME_FORMAT);
+ typedef typename ElfTypes::Addr Elf_Addr;
+
+ if (method_infos.empty()) {
+ return;
+ }
+
+ std::vector<uint32_t> binary_search_table;
+ std::vector<uintptr_t> patch_locations;
+ if (format == DW_EH_FRAME_FORMAT) {
+ binary_search_table.reserve(2 * method_infos.size());
+ } else {
+ patch_locations.reserve(method_infos.size());
+ }
+
+ // The methods can be written any order.
+ // Let's therefore sort them in the lexicographical order of the opcodes.
+ // This has no effect on its own. However, if the final .debug_frame section is
+ // compressed it reduces the size since similar opcodes sequences are grouped.
+ std::vector<const MethodDebugInfo*> sorted_method_infos;
+ sorted_method_infos.reserve(method_infos.size());
+ for (size_t i = 0; i < method_infos.size(); i++) {
+ sorted_method_infos.push_back(&method_infos[i]);
+ }
+ std::sort(
+ sorted_method_infos.begin(),
+ sorted_method_infos.end(),
+ [](const MethodDebugInfo* lhs, const MethodDebugInfo* rhs) {
+ ArrayRef<const uint8_t> l = lhs->compiled_method_->GetCFIInfo();
+ ArrayRef<const uint8_t> r = rhs->compiled_method_->GetCFIInfo();
+ return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end());
+ });
+
+ // Write .eh_frame/.debug_frame section.
+ auto* cfi_section = (format == DW_DEBUG_FRAME_FORMAT
+ ? builder->GetDebugFrame()
+ : builder->GetEhFrame());
+ {
+ cfi_section->Start();
+ const bool is64bit = Is64BitInstructionSet(builder->GetIsa());
+ const Elf_Addr text_address = builder->GetText()->Exists()
+ ? builder->GetText()->GetAddress()
+ : 0;
+ const Elf_Addr cfi_address = cfi_section->GetAddress();
+ const Elf_Addr cie_address = cfi_address;
+ Elf_Addr buffer_address = cfi_address;
+ std::vector<uint8_t> buffer; // Small temporary buffer.
+ WriteCIE(builder->GetIsa(), format, &buffer);
+ cfi_section->WriteFully(buffer.data(), buffer.size());
+ buffer_address += buffer.size();
+ buffer.clear();
+ for (const MethodDebugInfo* mi : sorted_method_infos) {
+ if (!mi->deduped_) { // Only one FDE per unique address.
+ ArrayRef<const uint8_t> opcodes = mi->compiled_method_->GetCFIInfo();
+ if (!opcodes.empty()) {
+ const Elf_Addr code_address = text_address + mi->low_pc_;
+ if (format == DW_EH_FRAME_FORMAT) {
+ binary_search_table.push_back(
+ dchecked_integral_cast<uint32_t>(code_address));
+ binary_search_table.push_back(
+ dchecked_integral_cast<uint32_t>(buffer_address));
+ }
+ WriteFDE(is64bit, cfi_address, cie_address,
+ code_address, mi->high_pc_ - mi->low_pc_,
+ opcodes, format, buffer_address, &buffer,
+ &patch_locations);
+ cfi_section->WriteFully(buffer.data(), buffer.size());
+ buffer_address += buffer.size();
+ buffer.clear();
+ }
+ }
+ }
+ cfi_section->End();
+ }
+
+ if (format == DW_EH_FRAME_FORMAT) {
+ auto* header_section = builder->GetEhFrameHdr();
+ header_section->Start();
+ uint32_t header_address = dchecked_integral_cast<int32_t>(header_section->GetAddress());
+ // Write .eh_frame_hdr section.
+ std::vector<uint8_t> buffer;
+ Writer<> header(&buffer);
+ header.PushUint8(1); // Version.
+ // Encoding of .eh_frame pointer - libunwind does not honor datarel here,
+ // so we have to use pcrel which means relative to the pointer's location.
+ header.PushUint8(DW_EH_PE_pcrel | DW_EH_PE_sdata4);
+ // Encoding of binary search table size.
+ header.PushUint8(DW_EH_PE_udata4);
+ // Encoding of binary search table addresses - libunwind supports only this
+ // specific combination, which means relative to the start of .eh_frame_hdr.
+ header.PushUint8(DW_EH_PE_datarel | DW_EH_PE_sdata4);
+ // .eh_frame pointer
+ header.PushInt32(cfi_section->GetAddress() - (header_address + 4u));
+ // Binary search table size (number of entries).
+ header.PushUint32(dchecked_integral_cast<uint32_t>(binary_search_table.size()/2));
+ header_section->WriteFully(buffer.data(), buffer.size());
+ // Binary search table.
+ for (size_t i = 0; i < binary_search_table.size(); i++) {
+ // Make addresses section-relative since we know the header address now.
+ binary_search_table[i] -= header_address;
+ }
+ header_section->WriteFully(binary_search_table.data(), binary_search_table.size());
+ header_section->End();
+ } else {
+ if (write_oat_patches) {
+ builder->WritePatches(".debug_frame.oat_patches",
+ ArrayRef<const uintptr_t>(patch_locations));
+ }
+ }
+}
+
+namespace {
+ struct CompilationUnit {
+ std::vector<const MethodDebugInfo*> methods_;
+ size_t debug_line_offset_ = 0;
+ uintptr_t low_pc_ = std::numeric_limits<uintptr_t>::max();
+ uintptr_t high_pc_ = 0;
+ };
+
+ typedef std::vector<DexFile::LocalInfo> LocalInfos;
+
+ void LocalInfoCallback(void* ctx, const DexFile::LocalInfo& entry) {
+ static_cast<LocalInfos*>(ctx)->push_back(entry);
+ }
+
+ typedef std::vector<DexFile::PositionInfo> PositionInfos;
+
+ bool PositionInfoCallback(void* ctx, const DexFile::PositionInfo& entry) {
+ static_cast<PositionInfos*>(ctx)->push_back(entry);
+ return false;
+ }
+
+ std::vector<const char*> GetParamNames(const MethodDebugInfo* mi) {
+ std::vector<const char*> names;
+ if (mi->code_item_ != nullptr) {
+ const uint8_t* stream = mi->dex_file_->GetDebugInfoStream(mi->code_item_);
+ if (stream != nullptr) {
+ DecodeUnsignedLeb128(&stream); // line.
+ uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
+ for (uint32_t i = 0; i < parameters_size; ++i) {
+ uint32_t id = DecodeUnsignedLeb128P1(&stream);
+ names.push_back(mi->dex_file_->StringDataByIdx(id));
+ }
+ }
+ }
+ return names;
+ }
+
+ struct VariableLocation {
+ uint32_t low_pc;
+ uint32_t high_pc;
+ DexRegisterLocation reg_lo; // May be None if the location is unknown.
+ DexRegisterLocation reg_hi; // Most significant bits of 64-bit value.
+ };
+
+ // Get the location of given dex register (e.g. stack or machine register).
+ // Note that the location might be different based on the current pc.
+ // The result will cover all ranges where the variable is in scope.
+ std::vector<VariableLocation> GetVariableLocations(const MethodDebugInfo* method_info,
+ uint16_t vreg,
+ bool is64bitValue,
+ uint32_t dex_pc_low,
+ uint32_t dex_pc_high) {
+ std::vector<VariableLocation> variable_locations;
+
+ // Get stack maps sorted by pc (they might not be sorted internally).
+ const CodeInfo code_info(method_info->compiled_method_->GetVmapTable().data());
+ const StackMapEncoding encoding = code_info.ExtractEncoding();
+ std::map<uint32_t, StackMap> stack_maps;
+ for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(); s++) {
+ StackMap stack_map = code_info.GetStackMapAt(s, encoding);
+ DCHECK(stack_map.IsValid());
+ const uint32_t low_pc = method_info->low_pc_ + stack_map.GetNativePcOffset(encoding);
+ DCHECK_LE(low_pc, method_info->high_pc_);
+ stack_maps.emplace(low_pc, stack_map);
+ }
+
+ // Create entries for the requested register based on stack map data.
+ for (auto it = stack_maps.begin(); it != stack_maps.end(); it++) {
+ const StackMap& stack_map = it->second;
+ const uint32_t low_pc = it->first;
+ auto next_it = it;
+ next_it++;
+ const uint32_t high_pc = next_it != stack_maps.end() ? next_it->first
+ : method_info->high_pc_;
+ DCHECK_LE(low_pc, high_pc);
+ if (low_pc == high_pc) {
+ continue; // Ignore if the address range is empty.
+ }
+
+ // Check that the stack map is in the requested range.
+ uint32_t dex_pc = stack_map.GetDexPc(encoding);
+ if (!(dex_pc_low <= dex_pc && dex_pc < dex_pc_high)) {
+ continue;
+ }
+
+ // Find the location of the dex register.
+ DexRegisterLocation reg_lo = DexRegisterLocation::None();
+ DexRegisterLocation reg_hi = DexRegisterLocation::None();
+ if (stack_map.HasDexRegisterMap(encoding)) {
+ DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(
+ stack_map, encoding, method_info->code_item_->registers_size_);
+ reg_lo = dex_register_map.GetDexRegisterLocation(
+ vreg, method_info->code_item_->registers_size_, code_info, encoding);
+ if (is64bitValue) {
+ reg_hi = dex_register_map.GetDexRegisterLocation(
+ vreg + 1, method_info->code_item_->registers_size_, code_info, encoding);
+ }
+ }
+
+ // Add location entry for this address range.
+ if (!variable_locations.empty() &&
+ variable_locations.back().reg_lo == reg_lo &&
+ variable_locations.back().reg_hi == reg_hi &&
+ variable_locations.back().high_pc == low_pc) {
+ // Merge with the previous entry (extend its range).
+ variable_locations.back().high_pc = high_pc;
+ } else {
+ variable_locations.push_back({low_pc, high_pc, reg_lo, reg_hi});
+ }
+ }
+
+ return variable_locations;
+ }
+
+ bool IsFromOptimizingCompiler(const MethodDebugInfo* method_info) {
+ return method_info->compiled_method_->GetQuickCode().size() > 0 &&
+ method_info->compiled_method_->GetVmapTable().size() > 0 &&
+ method_info->compiled_method_->GetGcMap().size() == 0 &&
+ method_info->code_item_ != nullptr;
+ }
+} // namespace
+
+// Helper class to write .debug_info and its supporting sections.
+template<typename ElfTypes>
+class DebugInfoWriter {
+ typedef typename ElfTypes::Addr Elf_Addr;
+
+ // Helper class to write one compilation unit.
+ // It holds helper methods and temporary state.
+ class CompilationUnitWriter {
+ public:
+ explicit CompilationUnitWriter(DebugInfoWriter* owner)
+ : owner_(owner),
+ info_(Is64BitInstructionSet(owner_->builder_->GetIsa()), &owner->debug_abbrev_) {
+ }
+
+ void Write(const CompilationUnit& compilation_unit) {
+ CHECK(!compilation_unit.methods_.empty());
+ const Elf_Addr text_address = owner_->builder_->GetText()->Exists()
+ ? owner_->builder_->GetText()->GetAddress()
+ : 0;
+ const uintptr_t cu_size = compilation_unit.high_pc_ - compilation_unit.low_pc_;
+
+ info_.StartTag(DW_TAG_compile_unit);
+ info_.WriteString(DW_AT_producer, "Android dex2oat");
+ info_.WriteData1(DW_AT_language, DW_LANG_Java);
+ info_.WriteString(DW_AT_comp_dir, "$JAVA_SRC_ROOT");
+ info_.WriteAddr(DW_AT_low_pc, text_address + compilation_unit.low_pc_);
+ info_.WriteUdata(DW_AT_high_pc, dchecked_integral_cast<uint32_t>(cu_size));
+ info_.WriteSecOffset(DW_AT_stmt_list, compilation_unit.debug_line_offset_);
+
+ const char* last_dex_class_desc = nullptr;
+ for (auto mi : compilation_unit.methods_) {
+ const DexFile* dex = mi->dex_file_;
+ const DexFile::CodeItem* dex_code = mi->code_item_;
+ const DexFile::MethodId& dex_method = dex->GetMethodId(mi->dex_method_index_);
+ const DexFile::ProtoId& dex_proto = dex->GetMethodPrototype(dex_method);
+ const DexFile::TypeList* dex_params = dex->GetProtoParameters(dex_proto);
+ const char* dex_class_desc = dex->GetMethodDeclaringClassDescriptor(dex_method);
+ const bool is_static = (mi->access_flags_ & kAccStatic) != 0;
+
+ // Enclose the method in correct class definition.
+ if (last_dex_class_desc != dex_class_desc) {
+ if (last_dex_class_desc != nullptr) {
+ EndClassTag();
+ }
+ // Write reference tag for the class we are about to declare.
+ size_t reference_tag_offset = info_.StartTag(DW_TAG_reference_type);
+ type_cache_.emplace(std::string(dex_class_desc), reference_tag_offset);
+ size_t type_attrib_offset = info_.size();
+ info_.WriteRef4(DW_AT_type, 0);
+ info_.EndTag();
+ // Declare the class that owns this method.
+ size_t class_offset = StartClassTag(dex_class_desc);
+ info_.UpdateUint32(type_attrib_offset, class_offset);
+ info_.WriteFlagPresent(DW_AT_declaration);
+ // Check that each class is defined only once.
+ bool unique = owner_->defined_dex_classes_.insert(dex_class_desc).second;
+ CHECK(unique) << "Redefinition of " << dex_class_desc;
+ last_dex_class_desc = dex_class_desc;
+ }
+
+ int start_depth = info_.Depth();
+ info_.StartTag(DW_TAG_subprogram);
+ WriteName(dex->GetMethodName(dex_method));
+ info_.WriteAddr(DW_AT_low_pc, text_address + mi->low_pc_);
+ info_.WriteUdata(DW_AT_high_pc, dchecked_integral_cast<uint32_t>(mi->high_pc_-mi->low_pc_));
+ std::vector<uint8_t> expr_buffer;
+ Expression expr(&expr_buffer);
+ expr.WriteOpCallFrameCfa();
+ info_.WriteExprLoc(DW_AT_frame_base, expr);
+ WriteLazyType(dex->GetReturnTypeDescriptor(dex_proto));
+
+ // Write parameters. DecodeDebugLocalInfo returns them as well, but it does not
+ // guarantee order or uniqueness so it is safer to iterate over them manually.
+ // DecodeDebugLocalInfo might not also be available if there is no debug info.
+ std::vector<const char*> param_names = GetParamNames(mi);
+ uint32_t arg_reg = 0;
+ if (!is_static) {
+ info_.StartTag(DW_TAG_formal_parameter);
+ WriteName("this");
+ info_.WriteFlagPresent(DW_AT_artificial);
+ WriteLazyType(dex_class_desc);
+ if (dex_code != nullptr) {
+ // Write the stack location of the parameter.
+ const uint32_t vreg = dex_code->registers_size_ - dex_code->ins_size_ + arg_reg;
+ const bool is64bitValue = false;
+ WriteRegLocation(mi, vreg, is64bitValue, compilation_unit.low_pc_);
+ }
+ arg_reg++;
+ info_.EndTag();
+ }
+ if (dex_params != nullptr) {
+ for (uint32_t i = 0; i < dex_params->Size(); ++i) {
+ info_.StartTag(DW_TAG_formal_parameter);
+ // Parameter names may not be always available.
+ if (i < param_names.size()) {
+ WriteName(param_names[i]);
+ }
+ // Write the type.
+ const char* type_desc = dex->StringByTypeIdx(dex_params->GetTypeItem(i).type_idx_);
+ WriteLazyType(type_desc);
+ const bool is64bitValue = type_desc[0] == 'D' || type_desc[0] == 'J';
+ if (dex_code != nullptr) {
+ // Write the stack location of the parameter.
+ const uint32_t vreg = dex_code->registers_size_ - dex_code->ins_size_ + arg_reg;
+ WriteRegLocation(mi, vreg, is64bitValue, compilation_unit.low_pc_);
+ }
+ arg_reg += is64bitValue ? 2 : 1;
+ info_.EndTag();
+ }
+ if (dex_code != nullptr) {
+ DCHECK_EQ(arg_reg, dex_code->ins_size_);
+ }
+ }
+
+ // Write local variables.
+ LocalInfos local_infos;
+ if (dex->DecodeDebugLocalInfo(dex_code,
+ is_static,
+ mi->dex_method_index_,
+ LocalInfoCallback,
+ &local_infos)) {
+ for (const DexFile::LocalInfo& var : local_infos) {
+ if (var.reg_ < dex_code->registers_size_ - dex_code->ins_size_) {
+ info_.StartTag(DW_TAG_variable);
+ WriteName(var.name_);
+ WriteLazyType(var.descriptor_);
+ bool is64bitValue = var.descriptor_[0] == 'D' || var.descriptor_[0] == 'J';
+ WriteRegLocation(mi, var.reg_, is64bitValue, compilation_unit.low_pc_,
+ var.start_address_, var.end_address_);
+ info_.EndTag();
+ }
+ }
+ }
+
+ info_.EndTag();
+ CHECK_EQ(info_.Depth(), start_depth); // Balanced start/end.
+ }
+ if (last_dex_class_desc != nullptr) {
+ EndClassTag();
+ }
+ FinishLazyTypes();
+ CloseNamespacesAboveDepth(0);
+ info_.EndTag(); // DW_TAG_compile_unit
+ CHECK_EQ(info_.Depth(), 0);
+ std::vector<uint8_t> buffer;
+ buffer.reserve(info_.data()->size() + KB);
+ const size_t offset = owner_->builder_->GetDebugInfo()->GetSize();
+ // All compilation units share single table which is at the start of .debug_abbrev.
+ const size_t debug_abbrev_offset = 0;
+ WriteDebugInfoCU(debug_abbrev_offset, info_, offset, &buffer, &owner_->debug_info_patches_);
+ owner_->builder_->GetDebugInfo()->WriteFully(buffer.data(), buffer.size());
+ }
+
+ void Write(const ArrayRef<mirror::Class*>& types) SHARED_REQUIRES(Locks::mutator_lock_) {
+ info_.StartTag(DW_TAG_compile_unit);
+ info_.WriteString(DW_AT_producer, "Android dex2oat");
+ info_.WriteData1(DW_AT_language, DW_LANG_Java);
+
+ // Base class references to be patched at the end.
+ std::map<size_t, mirror::Class*> base_class_references;
+
+ // Already written declarations or definitions.
+ std::map<mirror::Class*, size_t> class_declarations;
+
+ std::vector<uint8_t> expr_buffer;
+ for (mirror::Class* type : types) {
+ if (type->IsPrimitive()) {
+ // For primitive types the definition and the declaration is the same.
+ if (type->GetPrimitiveType() != Primitive::kPrimVoid) {
+ WriteTypeDeclaration(type->GetDescriptor(nullptr));
+ }
+ } else if (type->IsArrayClass()) {
+ mirror::Class* element_type = type->GetComponentType();
+ uint32_t component_size = type->GetComponentSize();
+ uint32_t data_offset = mirror::Array::DataOffset(component_size).Uint32Value();
+ uint32_t length_offset = mirror::Array::LengthOffset().Uint32Value();
+
+ CloseNamespacesAboveDepth(0); // Declare in root namespace.
+ info_.StartTag(DW_TAG_array_type);
+ std::string descriptor_string;
+ WriteLazyType(element_type->GetDescriptor(&descriptor_string));
+ WriteLinkageName(type);
+ info_.WriteUdata(DW_AT_data_member_location, data_offset);
+ info_.StartTag(DW_TAG_subrange_type);
+ Expression count_expr(&expr_buffer);
+ count_expr.WriteOpPushObjectAddress();
+ count_expr.WriteOpPlusUconst(length_offset);
+ count_expr.WriteOpDerefSize(4); // Array length is always 32-bit wide.
+ info_.WriteExprLoc(DW_AT_count, count_expr);
+ info_.EndTag(); // DW_TAG_subrange_type.
+ info_.EndTag(); // DW_TAG_array_type.
+ } else if (type->IsInterface()) {
+ // Skip. Variables cannot have an interface as a dynamic type.
+ // We do not expose the interface information to the debugger in any way.
+ } else {
+ std::string descriptor_string;
+ const char* desc = type->GetDescriptor(&descriptor_string);
+ size_t class_offset = StartClassTag(desc);
+ class_declarations.emplace(type, class_offset);
+
+ if (!type->IsVariableSize()) {
+ info_.WriteUdata(DW_AT_byte_size, type->GetObjectSize());
+ }
+
+ WriteLinkageName(type);
+
+ if (type->IsObjectClass()) {
+ // Generate artificial member which is used to get the dynamic type of variable.
+ // The run-time value of this field will correspond to linkage name of some type.
+ // We need to do it only once in j.l.Object since all other types inherit it.
+ info_.StartTag(DW_TAG_member);
+ WriteName(".dynamic_type");
+ WriteLazyType(sizeof(uintptr_t) == 8 ? "J" : "I");
+ info_.WriteFlagPresent(DW_AT_artificial);
+ // Create DWARF expression to get the value of the methods_ field.
+ Expression expr(&expr_buffer);
+ // The address of the object has been implicitly pushed on the stack.
+ // Dereference the klass_ field of Object (32-bit; possibly poisoned).
+ DCHECK_EQ(type->ClassOffset().Uint32Value(), 0u);
+ DCHECK_EQ(sizeof(mirror::HeapReference<mirror::Class>), 4u);
+ expr.WriteOpDerefSize(4);
+ if (kPoisonHeapReferences) {
+ expr.WriteOpNeg();
+ // DWARF stack is pointer sized. Ensure that the high bits are clear.
+ expr.WriteOpConstu(0xFFFFFFFF);
+ expr.WriteOpAnd();
+ }
+ // Add offset to the methods_ field.
+ expr.WriteOpPlusUconst(mirror::Class::MethodsOffset().Uint32Value());
+ // Top of stack holds the location of the field now.
+ info_.WriteExprLoc(DW_AT_data_member_location, expr);
+ info_.EndTag(); // DW_TAG_member.
+ }
+
+ // Base class.
+ mirror::Class* base_class = type->GetSuperClass();
+ if (base_class != nullptr) {
+ info_.StartTag(DW_TAG_inheritance);
+ base_class_references.emplace(info_.size(), base_class);
+ info_.WriteRef4(DW_AT_type, 0);
+ info_.WriteUdata(DW_AT_data_member_location, 0);
+ info_.WriteSdata(DW_AT_accessibility, DW_ACCESS_public);
+ info_.EndTag(); // DW_TAG_inheritance.
+ }
+
+ // Member variables.
+ for (uint32_t i = 0, count = type->NumInstanceFields(); i < count; ++i) {
+ ArtField* field = type->GetInstanceField(i);
+ info_.StartTag(DW_TAG_member);
+ WriteName(field->GetName());
+ WriteLazyType(field->GetTypeDescriptor());
+ info_.WriteUdata(DW_AT_data_member_location, field->GetOffset().Uint32Value());
+ uint32_t access_flags = field->GetAccessFlags();
+ if (access_flags & kAccPublic) {
+ info_.WriteSdata(DW_AT_accessibility, DW_ACCESS_public);
+ } else if (access_flags & kAccProtected) {
+ info_.WriteSdata(DW_AT_accessibility, DW_ACCESS_protected);
+ } else if (access_flags & kAccPrivate) {
+ info_.WriteSdata(DW_AT_accessibility, DW_ACCESS_private);
+ }
+ info_.EndTag(); // DW_TAG_member.
+ }
+
+ if (type->IsStringClass()) {
+ // Emit debug info about an artifical class member for java.lang.String which represents
+ // the first element of the data stored in a string instance. Consumers of the debug
+ // info will be able to read the content of java.lang.String based on the count (real
+ // field) and based on the location of this data member.
+ info_.StartTag(DW_TAG_member);
+ WriteName("value");
+ // We don't support fields with C like array types so we just say its type is java char.
+ WriteLazyType("C"); // char.
+ info_.WriteUdata(DW_AT_data_member_location,
+ mirror::String::ValueOffset().Uint32Value());
+ info_.WriteSdata(DW_AT_accessibility, DW_ACCESS_private);
+ info_.EndTag(); // DW_TAG_member.
+ }
+
+ EndClassTag();
+ }
+ }
+
+ // Write base class declarations.
+ for (const auto& base_class_reference : base_class_references) {
+ size_t reference_offset = base_class_reference.first;
+ mirror::Class* base_class = base_class_reference.second;
+ const auto& it = class_declarations.find(base_class);
+ if (it != class_declarations.end()) {
+ info_.UpdateUint32(reference_offset, it->second);
+ } else {
+ // Declare base class. We can not use the standard WriteLazyType
+ // since we want to avoid the DW_TAG_reference_tag wrapping.
+ std::string tmp_storage;
+ const char* base_class_desc = base_class->GetDescriptor(&tmp_storage);
+ size_t base_class_declaration_offset = StartClassTag(base_class_desc);
+ info_.WriteFlagPresent(DW_AT_declaration);
+ WriteLinkageName(base_class);
+ EndClassTag();
+ class_declarations.emplace(base_class, base_class_declaration_offset);
+ info_.UpdateUint32(reference_offset, base_class_declaration_offset);
+ }
+ }
+
+ FinishLazyTypes();
+ CloseNamespacesAboveDepth(0);
+ info_.EndTag(); // DW_TAG_compile_unit.
+ CHECK_EQ(info_.Depth(), 0);
+ std::vector<uint8_t> buffer;
+ buffer.reserve(info_.data()->size() + KB);
+ const size_t offset = owner_->builder_->GetDebugInfo()->GetSize();
+ // All compilation units share single table which is at the start of .debug_abbrev.
+ const size_t debug_abbrev_offset = 0;
+ WriteDebugInfoCU(debug_abbrev_offset, info_, offset, &buffer, &owner_->debug_info_patches_);
+ owner_->builder_->GetDebugInfo()->WriteFully(buffer.data(), buffer.size());
+ }
+
+ // Linkage name uniquely identifies type.
+ // It is used to determine the dynamic type of objects.
+ // We use the methods_ field of class since it is unique and it is not moved by the GC.
+ void WriteLinkageName(mirror::Class* type) SHARED_REQUIRES(Locks::mutator_lock_) {
+ auto* methods_ptr = type->GetMethodsPtr();
+ if (methods_ptr == nullptr) {
+ // Some types might have no methods. Allocate empty array instead.
+ LinearAlloc* allocator = Runtime::Current()->GetLinearAlloc();
+ void* storage = allocator->Alloc(Thread::Current(), sizeof(LengthPrefixedArray<ArtMethod>));
+ methods_ptr = new (storage) LengthPrefixedArray<ArtMethod>(0);
+ type->SetMethodsPtr(methods_ptr, 0, 0);
+ DCHECK(type->GetMethodsPtr() != nullptr);
+ }
+ char name[32];
+ snprintf(name, sizeof(name), "0x%" PRIXPTR, reinterpret_cast<uintptr_t>(methods_ptr));
+ info_.WriteString(DW_AT_linkage_name, name);
+ }
+
+ // Write table into .debug_loc which describes location of dex register.
+ // The dex register might be valid only at some points and it might
+ // move between machine registers and stack.
+ void WriteRegLocation(const MethodDebugInfo* method_info,
+ uint16_t vreg,
+ bool is64bitValue,
+ uint32_t compilation_unit_low_pc,
+ uint32_t dex_pc_low = 0,
+ uint32_t dex_pc_high = 0xFFFFFFFF) {
+ using Kind = DexRegisterLocation::Kind;
+ if (!IsFromOptimizingCompiler(method_info)) {
+ return;
+ }
+
+ Writer<> debug_loc(&owner_->debug_loc_);
+ Writer<> debug_ranges(&owner_->debug_ranges_);
+ info_.WriteSecOffset(DW_AT_location, debug_loc.size());
+ info_.WriteSecOffset(DW_AT_start_scope, debug_ranges.size());
+
+ std::vector<VariableLocation> variable_locations = GetVariableLocations(
+ method_info,
+ vreg,
+ is64bitValue,
+ dex_pc_low,
+ dex_pc_high);
+
+ // Write .debug_loc entries.
+ const InstructionSet isa = owner_->builder_->GetIsa();
+ const bool is64bit = Is64BitInstructionSet(isa);
+ std::vector<uint8_t> expr_buffer;
+ for (const VariableLocation& variable_location : variable_locations) {
+ // Translate dex register location to DWARF expression.
+ // Note that 64-bit value might be split to two distinct locations.
+ // (for example, two 32-bit machine registers, or even stack and register)
+ Expression expr(&expr_buffer);
+ DexRegisterLocation reg_lo = variable_location.reg_lo;
+ DexRegisterLocation reg_hi = variable_location.reg_hi;
+ for (int piece = 0; piece < (is64bitValue ? 2 : 1); piece++) {
+ DexRegisterLocation reg_loc = (piece == 0 ? reg_lo : reg_hi);
+ const Kind kind = reg_loc.GetKind();
+ const int32_t value = reg_loc.GetValue();
+ if (kind == Kind::kInStack) {
+ const size_t frame_size = method_info->compiled_method_->GetFrameSizeInBytes();
+ // The stack offset is relative to SP. Make it relative to CFA.
+ expr.WriteOpFbreg(value - frame_size);
+ if (piece == 0 && reg_hi.GetKind() == Kind::kInStack &&
+ reg_hi.GetValue() == value + 4) {
+ break; // the high word is correctly implied by the low word.
+ }
+ } else if (kind == Kind::kInRegister) {
+ expr.WriteOpReg(GetDwarfCoreReg(isa, value).num());
+ if (piece == 0 && reg_hi.GetKind() == Kind::kInRegisterHigh &&
+ reg_hi.GetValue() == value) {
+ break; // the high word is correctly implied by the low word.
+ }
+ } else if (kind == Kind::kInFpuRegister) {
+ if ((isa == kArm || isa == kThumb2) &&
+ piece == 0 && reg_hi.GetKind() == Kind::kInFpuRegister &&
+ reg_hi.GetValue() == value + 1 && value % 2 == 0) {
+ // Translate S register pair to D register (e.g. S4+S5 to D2).
+ expr.WriteOpReg(Reg::ArmDp(value / 2).num());
+ break;
+ }
+ expr.WriteOpReg(GetDwarfFpReg(isa, value).num());
+ if (piece == 0 && reg_hi.GetKind() == Kind::kInFpuRegisterHigh &&
+ reg_hi.GetValue() == reg_lo.GetValue()) {
+ break; // the high word is correctly implied by the low word.
+ }
+ } else if (kind == Kind::kConstant) {
+ expr.WriteOpConsts(value);
+ expr.WriteOpStackValue();
+ } else if (kind == Kind::kNone) {
+ break;
+ } else {
+ // kInStackLargeOffset and kConstantLargeValue are hidden by GetKind().
+ // kInRegisterHigh and kInFpuRegisterHigh should be handled by
+ // the special cases above and they should not occur alone.
+ LOG(ERROR) << "Unexpected register location kind: "
+ << DexRegisterLocation::PrettyDescriptor(kind);
+ break;
+ }
+ if (is64bitValue) {
+ // Write the marker which is needed by split 64-bit values.
+ // This code is skipped by the special cases.
+ expr.WriteOpPiece(4);
+ }
+ }
+
+ if (expr.size() > 0) {
+ if (is64bit) {
+ debug_loc.PushUint64(variable_location.low_pc - compilation_unit_low_pc);
+ debug_loc.PushUint64(variable_location.high_pc - compilation_unit_low_pc);
+ } else {
+ debug_loc.PushUint32(variable_location.low_pc - compilation_unit_low_pc);
+ debug_loc.PushUint32(variable_location.high_pc - compilation_unit_low_pc);
+ }
+ // Write the expression.
+ debug_loc.PushUint16(expr.size());
+ debug_loc.PushData(expr.data());
+ } else {
+ // Do not generate .debug_loc if the location is not known.
+ }
+ }
+ // Write end-of-list entry.
+ if (is64bit) {
+ debug_loc.PushUint64(0);
+ debug_loc.PushUint64(0);
+ } else {
+ debug_loc.PushUint32(0);
+ debug_loc.PushUint32(0);
+ }
+
+ // Write .debug_ranges entries.
+ // This includes ranges where the variable is in scope but the location is not known.
+ for (size_t i = 0; i < variable_locations.size(); i++) {
+ uint32_t low_pc = variable_locations[i].low_pc;
+ uint32_t high_pc = variable_locations[i].high_pc;
+ while (i + 1 < variable_locations.size() && variable_locations[i+1].low_pc == high_pc) {
+ // Merge address range with the next entry.
+ high_pc = variable_locations[++i].high_pc;
+ }
+ if (is64bit) {
+ debug_ranges.PushUint64(low_pc - compilation_unit_low_pc);
+ debug_ranges.PushUint64(high_pc - compilation_unit_low_pc);
+ } else {
+ debug_ranges.PushUint32(low_pc - compilation_unit_low_pc);
+ debug_ranges.PushUint32(high_pc - compilation_unit_low_pc);
+ }
+ }
+ // Write end-of-list entry.
+ if (is64bit) {
+ debug_ranges.PushUint64(0);
+ debug_ranges.PushUint64(0);
+ } else {
+ debug_ranges.PushUint32(0);
+ debug_ranges.PushUint32(0);
+ }
+ }
+
+ // Some types are difficult to define as we go since they need
+ // to be enclosed in the right set of namespaces. Therefore we
+ // just define all types lazily at the end of compilation unit.
+ void WriteLazyType(const char* type_descriptor) {
+ if (type_descriptor != nullptr && type_descriptor[0] != 'V') {
+ lazy_types_.emplace(std::string(type_descriptor), info_.size());
+ info_.WriteRef4(DW_AT_type, 0);
+ }
+ }
+
+ void FinishLazyTypes() {
+ for (const auto& lazy_type : lazy_types_) {
+ info_.UpdateUint32(lazy_type.second, WriteTypeDeclaration(lazy_type.first));
+ }
+ lazy_types_.clear();
+ }
+
+ private:
+ void WriteName(const char* name) {
+ if (name != nullptr) {
+ info_.WriteString(DW_AT_name, name);
+ }
+ }
+
+ // Convert dex type descriptor to DWARF.
+ // Returns offset in the compilation unit.
+ size_t WriteTypeDeclaration(const std::string& desc) {
+ DCHECK(!desc.empty());
+ const auto& it = type_cache_.find(desc);
+ if (it != type_cache_.end()) {
+ return it->second;
+ }
+
+ size_t offset;
+ if (desc[0] == 'L') {
+ // Class type. For example: Lpackage/name;
+ size_t class_offset = StartClassTag(desc.c_str());
+ info_.WriteFlagPresent(DW_AT_declaration);
+ EndClassTag();
+ // Reference to the class type.
+ offset = info_.StartTag(DW_TAG_reference_type);
+ info_.WriteRef(DW_AT_type, class_offset);
+ info_.EndTag();
+ } else if (desc[0] == '[') {
+ // Array type.
+ size_t element_type = WriteTypeDeclaration(desc.substr(1));
+ CloseNamespacesAboveDepth(0); // Declare in root namespace.
+ size_t array_type = info_.StartTag(DW_TAG_array_type);
+ info_.WriteFlagPresent(DW_AT_declaration);
+ info_.WriteRef(DW_AT_type, element_type);
+ info_.EndTag();
+ offset = info_.StartTag(DW_TAG_reference_type);
+ info_.WriteRef4(DW_AT_type, array_type);
+ info_.EndTag();
+ } else {
+ // Primitive types.
+ DCHECK_EQ(desc.size(), 1u);
+
+ const char* name;
+ uint32_t encoding;
+ uint32_t byte_size;
+ switch (desc[0]) {
+ case 'B':
+ name = "byte";
+ encoding = DW_ATE_signed;
+ byte_size = 1;
+ break;
+ case 'C':
+ name = "char";
+ encoding = DW_ATE_UTF;
+ byte_size = 2;
+ break;
+ case 'D':
+ name = "double";
+ encoding = DW_ATE_float;
+ byte_size = 8;
+ break;
+ case 'F':
+ name = "float";
+ encoding = DW_ATE_float;
+ byte_size = 4;
+ break;
+ case 'I':
+ name = "int";
+ encoding = DW_ATE_signed;
+ byte_size = 4;
+ break;
+ case 'J':
+ name = "long";
+ encoding = DW_ATE_signed;
+ byte_size = 8;
+ break;
+ case 'S':
+ name = "short";
+ encoding = DW_ATE_signed;
+ byte_size = 2;
+ break;
+ case 'Z':
+ name = "boolean";
+ encoding = DW_ATE_boolean;
+ byte_size = 1;
+ break;
+ case 'V':
+ LOG(FATAL) << "Void type should not be encoded";
+ UNREACHABLE();
+ default:
+ LOG(FATAL) << "Unknown dex type descriptor: \"" << desc << "\"";
+ UNREACHABLE();
+ }
+ CloseNamespacesAboveDepth(0); // Declare in root namespace.
+ offset = info_.StartTag(DW_TAG_base_type);
+ WriteName(name);
+ info_.WriteData1(DW_AT_encoding, encoding);
+ info_.WriteData1(DW_AT_byte_size, byte_size);
+ info_.EndTag();
+ }
+
+ type_cache_.emplace(desc, offset);
+ return offset;
+ }
+
+ // Start DW_TAG_class_type tag nested in DW_TAG_namespace tags.
+ // Returns offset of the class tag in the compilation unit.
+ size_t StartClassTag(const char* desc) {
+ std::string name = SetNamespaceForClass(desc);
+ size_t offset = info_.StartTag(DW_TAG_class_type);
+ WriteName(name.c_str());
+ return offset;
+ }
+
+ void EndClassTag() {
+ info_.EndTag();
+ }
+
+ // Set the current namespace nesting to one required by the given class.
+ // Returns the class name with namespaces, 'L', and ';' stripped.
+ std::string SetNamespaceForClass(const char* desc) {
+ DCHECK(desc != nullptr && desc[0] == 'L');
+ desc++; // Skip the initial 'L'.
+ size_t depth = 0;
+ for (const char* end; (end = strchr(desc, '/')) != nullptr; desc = end + 1, ++depth) {
+ // Check whether the name at this depth is already what we need.
+ if (depth < current_namespace_.size()) {
+ const std::string& name = current_namespace_[depth];
+ if (name.compare(0, name.size(), desc, end - desc) == 0) {
+ continue;
+ }
+ }
+ // Otherwise we need to open a new namespace tag at this depth.
+ CloseNamespacesAboveDepth(depth);
+ info_.StartTag(DW_TAG_namespace);
+ std::string name(desc, end - desc);
+ WriteName(name.c_str());
+ current_namespace_.push_back(std::move(name));
+ }
+ CloseNamespacesAboveDepth(depth);
+ return std::string(desc, strchr(desc, ';') - desc);
+ }
+
+ // Close namespace tags to reach the given nesting depth.
+ void CloseNamespacesAboveDepth(size_t depth) {
+ DCHECK_LE(depth, current_namespace_.size());
+ while (current_namespace_.size() > depth) {
+ info_.EndTag();
+ current_namespace_.pop_back();
+ }
+ }
+
+ // For access to the ELF sections.
+ DebugInfoWriter<ElfTypes>* owner_;
+ // Temporary buffer to create and store the entries.
+ DebugInfoEntryWriter<> info_;
+ // Cache of already translated type descriptors.
+ std::map<std::string, size_t> type_cache_; // type_desc -> definition_offset.
+ // 32-bit references which need to be resolved to a type later.
+ // Given type may be used multiple times. Therefore we need a multimap.
+ std::multimap<std::string, size_t> lazy_types_; // type_desc -> patch_offset.
+ // The current set of open namespace tags which are active and not closed yet.
+ std::vector<std::string> current_namespace_;
+ };
+
+ public:
+ explicit DebugInfoWriter(ElfBuilder<ElfTypes>* builder)
+ : builder_(builder),
+ debug_abbrev_(&debug_abbrev_buffer_) {
+ }
+
+ void Start() {
+ builder_->GetDebugInfo()->Start();
+ }
+
+ void WriteCompilationUnit(const CompilationUnit& compilation_unit) {
+ CompilationUnitWriter writer(this);
+ writer.Write(compilation_unit);
+ }
+
+ void WriteTypes(const ArrayRef<mirror::Class*>& types) SHARED_REQUIRES(Locks::mutator_lock_) {
+ CompilationUnitWriter writer(this);
+ writer.Write(types);
+ }
+
+ void End(bool write_oat_patches) {
+ builder_->GetDebugInfo()->End();
+ if (write_oat_patches) {
+ builder_->WritePatches(".debug_info.oat_patches",
+ ArrayRef<const uintptr_t>(debug_info_patches_));
+ }
+ builder_->WriteSection(".debug_abbrev", &debug_abbrev_buffer_);
+ if (!debug_loc_.empty()) {
+ builder_->WriteSection(".debug_loc", &debug_loc_);
+ }
+ if (!debug_ranges_.empty()) {
+ builder_->WriteSection(".debug_ranges", &debug_ranges_);
+ }
+ }
+
+ private:
+ ElfBuilder<ElfTypes>* builder_;
+ std::vector<uintptr_t> debug_info_patches_;
+ std::vector<uint8_t> debug_abbrev_buffer_;
+ DebugAbbrevWriter<> debug_abbrev_;
+ std::vector<uint8_t> debug_loc_;
+ std::vector<uint8_t> debug_ranges_;
+
+ std::unordered_set<const char*> defined_dex_classes_; // For CHECKs only.
+};
+
+template<typename ElfTypes>
+class DebugLineWriter {
+ typedef typename ElfTypes::Addr Elf_Addr;
+
+ public:
+ explicit DebugLineWriter(ElfBuilder<ElfTypes>* builder) : builder_(builder) {
+ }
+
+ void Start() {
+ builder_->GetDebugLine()->Start();
+ }
+
+ // Write line table for given set of methods.
+ // Returns the number of bytes written.
+ size_t WriteCompilationUnit(CompilationUnit& compilation_unit) {
+ const bool is64bit = Is64BitInstructionSet(builder_->GetIsa());
+ const Elf_Addr text_address = builder_->GetText()->Exists()
+ ? builder_->GetText()->GetAddress()
+ : 0;
+
+ compilation_unit.debug_line_offset_ = builder_->GetDebugLine()->GetSize();
+
+ std::vector<FileEntry> files;
+ std::unordered_map<std::string, size_t> files_map;
+ std::vector<std::string> directories;
+ std::unordered_map<std::string, size_t> directories_map;
+ int code_factor_bits_ = 0;
+ int dwarf_isa = -1;
+ switch (builder_->GetIsa()) {
+ case kArm: // arm actually means thumb2.
+ case kThumb2:
+ code_factor_bits_ = 1; // 16-bit instuctions
+ dwarf_isa = 1; // DW_ISA_ARM_thumb.
+ break;
+ case kArm64:
+ case kMips:
+ case kMips64:
+ code_factor_bits_ = 2; // 32-bit instructions
+ break;
+ case kNone:
+ case kX86:
+ case kX86_64:
+ break;
+ }
+ DebugLineOpCodeWriter<> opcodes(is64bit, code_factor_bits_);
+ for (const MethodDebugInfo* mi : compilation_unit.methods_) {
+ // Ignore function if we have already generated line table for the same address.
+ // It would confuse the debugger and the DWARF specification forbids it.
+ if (mi->deduped_) {
+ continue;
+ }
+
+ ArrayRef<const SrcMapElem> src_mapping_table;
+ std::vector<SrcMapElem> src_mapping_table_from_stack_maps;
+ if (IsFromOptimizingCompiler(mi)) {
+ // Use stack maps to create mapping table from pc to dex.
+ const CodeInfo code_info(mi->compiled_method_->GetVmapTable().data());
+ const StackMapEncoding encoding = code_info.ExtractEncoding();
+ for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(); s++) {
+ StackMap stack_map = code_info.GetStackMapAt(s, encoding);
+ DCHECK(stack_map.IsValid());
+ // Emit only locations where we have local-variable information.
+ // In particular, skip mappings inside the prologue.
+ if (stack_map.HasDexRegisterMap(encoding)) {
+ const uint32_t pc = stack_map.GetNativePcOffset(encoding);
+ const int32_t dex = stack_map.GetDexPc(encoding);
+ src_mapping_table_from_stack_maps.push_back({pc, dex});
+ }
+ }
+ std::sort(src_mapping_table_from_stack_maps.begin(),
+ src_mapping_table_from_stack_maps.end());
+ src_mapping_table = ArrayRef<const SrcMapElem>(src_mapping_table_from_stack_maps);
+ } else {
+ // Use the mapping table provided by the quick compiler.
+ src_mapping_table = mi->compiled_method_->GetSrcMappingTable();
+ }
+
+ if (src_mapping_table.empty()) {
+ continue;
+ }
+
+ Elf_Addr method_address = text_address + mi->low_pc_;
+
+ PositionInfos position_infos;
+ const DexFile* dex = mi->dex_file_;
+ if (!dex->DecodeDebugPositionInfo(mi->code_item_, PositionInfoCallback, &position_infos)) {
+ continue;
+ }
+
+ if (position_infos.empty()) {
+ continue;
+ }
+
+ opcodes.SetAddress(method_address);
+ if (dwarf_isa != -1) {
+ opcodes.SetISA(dwarf_isa);
+ }
+
+ // Get and deduplicate directory and filename.
+ int file_index = 0; // 0 - primary source file of the compilation.
+ auto& dex_class_def = dex->GetClassDef(mi->class_def_index_);
+ const char* source_file = dex->GetSourceFile(dex_class_def);
+ if (source_file != nullptr) {
+ std::string file_name(source_file);
+ size_t file_name_slash = file_name.find_last_of('/');
+ std::string class_name(dex->GetClassDescriptor(dex_class_def));
+ size_t class_name_slash = class_name.find_last_of('/');
+ std::string full_path(file_name);
+
+ // Guess directory from package name.
+ int directory_index = 0; // 0 - current directory of the compilation.
+ if (file_name_slash == std::string::npos && // Just filename.
+ class_name.front() == 'L' && // Type descriptor for a class.
+ class_name_slash != std::string::npos) { // Has package name.
+ std::string package_name = class_name.substr(1, class_name_slash - 1);
+ auto it = directories_map.find(package_name);
+ if (it == directories_map.end()) {
+ directory_index = 1 + directories.size();
+ directories_map.emplace(package_name, directory_index);
+ directories.push_back(package_name);
+ } else {
+ directory_index = it->second;
+ }
+ full_path = package_name + "/" + file_name;
+ }
+
+ // Add file entry.
+ auto it2 = files_map.find(full_path);
+ if (it2 == files_map.end()) {
+ file_index = 1 + files.size();
+ files_map.emplace(full_path, file_index);
+ files.push_back(FileEntry {
+ file_name,
+ directory_index,
+ 0, // Modification time - NA.
+ 0, // File size - NA.
+ });
+ } else {
+ file_index = it2->second;
+ }
+ }
+ opcodes.SetFile(file_index);
+
+ // Generate mapping opcodes from PC to Java lines.
+ if (file_index != 0) {
+ bool first = true;
+ for (SrcMapElem pc2dex : src_mapping_table) {
+ uint32_t pc = pc2dex.from_;
+ int dex_pc = pc2dex.to_;
+ // Find mapping with address with is greater than our dex pc; then go back one step.
+ auto ub = std::upper_bound(position_infos.begin(), position_infos.end(), dex_pc,
+ [](uint32_t address, const DexFile::PositionInfo& entry) {
+ return address < entry.address_;
+ });
+ if (ub != position_infos.begin()) {
+ int line = (--ub)->line_;
+ if (first) {
+ first = false;
+ if (pc > 0) {
+ // Assume that any preceding code is prologue.
+ int first_line = position_infos.front().line_;
+ // Prologue is not a sensible place for a breakpoint.
+ opcodes.NegateStmt();
+ opcodes.AddRow(method_address, first_line);
+ opcodes.NegateStmt();
+ opcodes.SetPrologueEnd();
+ }
+ opcodes.AddRow(method_address + pc, line);
+ } else if (line != opcodes.CurrentLine()) {
+ opcodes.AddRow(method_address + pc, line);
+ }
+ }
+ }
+ } else {
+ // line 0 - instruction cannot be attributed to any source line.
+ opcodes.AddRow(method_address, 0);
+ }
+
+ opcodes.AdvancePC(text_address + mi->high_pc_);
+ opcodes.EndSequence();
+ }
+ std::vector<uint8_t> buffer;
+ buffer.reserve(opcodes.data()->size() + KB);
+ size_t offset = builder_->GetDebugLine()->GetSize();
+ WriteDebugLineTable(directories, files, opcodes, offset, &buffer, &debug_line_patches);
+ builder_->GetDebugLine()->WriteFully(buffer.data(), buffer.size());
+ return buffer.size();
+ }
+
+ void End(bool write_oat_patches) {
+ builder_->GetDebugLine()->End();
+ if (write_oat_patches) {
+ builder_->WritePatches(".debug_line.oat_patches",
+ ArrayRef<const uintptr_t>(debug_line_patches));
+ }
+ }
+
+ private:
+ ElfBuilder<ElfTypes>* builder_;
+ std::vector<uintptr_t> debug_line_patches;
+};
+
+template<typename ElfTypes>
+static void WriteDebugSections(ElfBuilder<ElfTypes>* builder,
+ const ArrayRef<const MethodDebugInfo>& method_infos,
+ bool write_oat_patches) {
+ // Group the methods into compilation units based on source file.
+ std::vector<CompilationUnit> compilation_units;
+ const char* last_source_file = nullptr;
+ for (const MethodDebugInfo& mi : method_infos) {
+ auto& dex_class_def = mi.dex_file_->GetClassDef(mi.class_def_index_);
+ const char* source_file = mi.dex_file_->GetSourceFile(dex_class_def);
+ if (compilation_units.empty() || source_file != last_source_file) {
+ compilation_units.push_back(CompilationUnit());
+ }
+ CompilationUnit& cu = compilation_units.back();
+ cu.methods_.push_back(&mi);
+ cu.low_pc_ = std::min(cu.low_pc_, mi.low_pc_);
+ cu.high_pc_ = std::max(cu.high_pc_, mi.high_pc_);
+ last_source_file = source_file;
+ }
+
+ // Write .debug_line section.
+ if (!compilation_units.empty()) {
+ DebugLineWriter<ElfTypes> line_writer(builder);
+ line_writer.Start();
+ for (auto& compilation_unit : compilation_units) {
+ line_writer.WriteCompilationUnit(compilation_unit);
+ }
+ line_writer.End(write_oat_patches);
+ }
+
+ // Write .debug_info section.
+ if (!compilation_units.empty()) {
+ DebugInfoWriter<ElfTypes> info_writer(builder);
+ info_writer.Start();
+ for (const auto& compilation_unit : compilation_units) {
+ info_writer.WriteCompilationUnit(compilation_unit);
+ }
+ info_writer.End(write_oat_patches);
+ }
+}
+
+template <typename ElfTypes>
+static void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder,
+ const ArrayRef<const MethodDebugInfo>& method_infos,
+ bool with_signature) {
+ bool generated_mapping_symbol = false;
+ auto* strtab = builder->GetStrTab();
+ auto* symtab = builder->GetSymTab();
+
+ if (method_infos.empty()) {
+ return;
+ }
+
+ // Find all addresses (low_pc) which contain deduped methods.
+ // The first instance of method is not marked deduped_, but the rest is.
+ std::unordered_set<uint32_t> deduped_addresses;
+ for (const MethodDebugInfo& info : method_infos) {
+ if (info.deduped_) {
+ deduped_addresses.insert(info.low_pc_);
+ }
+ }
+
+ strtab->Start();
+ strtab->Write(""); // strtab should start with empty string.
+ std::string last_name;
+ size_t last_name_offset = 0;
+ for (const MethodDebugInfo& info : method_infos) {
+ if (info.deduped_) {
+ continue; // Add symbol only for the first instance.
+ }
+ std::string name = PrettyMethod(info.dex_method_index_, *info.dex_file_, with_signature);
+ if (deduped_addresses.find(info.low_pc_) != deduped_addresses.end()) {
+ name += " [DEDUPED]";
+ }
+ // If we write method names without signature, we might see the same name multiple times.
+ size_t name_offset = (name == last_name ? last_name_offset : strtab->Write(name));
+
+ const auto* text = builder->GetText()->Exists() ? builder->GetText() : nullptr;
+ const bool is_relative = (text != nullptr);
+ uint32_t low_pc = info.low_pc_;
+ // Add in code delta, e.g., thumb bit 0 for Thumb2 code.
+ low_pc += info.compiled_method_->CodeDelta();
+ symtab->Add(name_offset,
+ text,
+ low_pc,
+ is_relative,
+ info.high_pc_ - info.low_pc_,
+ STB_GLOBAL,
+ STT_FUNC);
+
+ // Conforming to aaelf, add $t mapping symbol to indicate start of a sequence of thumb2
+ // instructions, so that disassembler tools can correctly disassemble.
+ // Note that even if we generate just a single mapping symbol, ARM's Streamline
+ // requires it to match function symbol. Just address 0 does not work.
+ if (info.compiled_method_->GetInstructionSet() == kThumb2) {
+ if (!generated_mapping_symbol || !kGenerateSingleArmMappingSymbol) {
+ symtab->Add(strtab->Write("$t"), text, info.low_pc_ & ~1,
+ is_relative, 0, STB_LOCAL, STT_NOTYPE);
+ generated_mapping_symbol = true;
+ }
+ }
+
+ last_name = std::move(name);
+ last_name_offset = name_offset;
+ }
+ strtab->End();
+
+ // Symbols are buffered and written after names (because they are smaller).
+ // We could also do two passes in this function to avoid the buffering.
+ symtab->Start();
+ symtab->Write();
+ symtab->End();
+}
+
+template <typename ElfTypes>
+void WriteDebugInfo(ElfBuilder<ElfTypes>* builder,
+ const ArrayRef<const MethodDebugInfo>& method_infos,
+ CFIFormat cfi_format,
+ bool write_oat_patches) {
+ // Add methods to .symtab.
+ WriteDebugSymbols(builder, method_infos, true /* with_signature */);
+ // Generate CFI (stack unwinding information).
+ WriteCFISection(builder, method_infos, cfi_format, write_oat_patches);
+ // Write DWARF .debug_* sections.
+ WriteDebugSections(builder, method_infos, write_oat_patches);
+}
+
+static void XzCompress(const std::vector<uint8_t>* src, std::vector<uint8_t>* dst) {
+ // Configure the compression library.
+ CrcGenerateTable();
+ Crc64GenerateTable();
+ CLzma2EncProps lzma2Props;
+ Lzma2EncProps_Init(&lzma2Props);
+ lzma2Props.lzmaProps.level = 1; // Fast compression.
+ Lzma2EncProps_Normalize(&lzma2Props);
+ CXzProps props;
+ XzProps_Init(&props);
+ props.lzma2Props = &lzma2Props;
+ // Implement the required interface for communication (written in C so no virtual methods).
+ struct XzCallbacks : public ISeqInStream, public ISeqOutStream, public ICompressProgress {
+ static SRes ReadImpl(void* p, void* buf, size_t* size) {
+ auto* ctx = static_cast<XzCallbacks*>(reinterpret_cast<ISeqInStream*>(p));
+ *size = std::min(*size, ctx->src_->size() - ctx->src_pos_);
+ memcpy(buf, ctx->src_->data() + ctx->src_pos_, *size);
+ ctx->src_pos_ += *size;
+ return SZ_OK;
+ }
+ static size_t WriteImpl(void* p, const void* buf, size_t size) {
+ auto* ctx = static_cast<XzCallbacks*>(reinterpret_cast<ISeqOutStream*>(p));
+ const uint8_t* buffer = reinterpret_cast<const uint8_t*>(buf);
+ ctx->dst_->insert(ctx->dst_->end(), buffer, buffer + size);
+ return size;
+ }
+ static SRes ProgressImpl(void* , UInt64, UInt64) {
+ return SZ_OK;
+ }
+ size_t src_pos_;
+ const std::vector<uint8_t>* src_;
+ std::vector<uint8_t>* dst_;
+ };
+ XzCallbacks callbacks;
+ callbacks.Read = XzCallbacks::ReadImpl;
+ callbacks.Write = XzCallbacks::WriteImpl;
+ callbacks.Progress = XzCallbacks::ProgressImpl;
+ callbacks.src_pos_ = 0;
+ callbacks.src_ = src;
+ callbacks.dst_ = dst;
+ // Compress.
+ SRes res = Xz_Encode(&callbacks, &callbacks, &props, &callbacks);
+ CHECK_EQ(res, SZ_OK);
+}
+
+template <typename ElfTypes>
+std::vector<uint8_t> MakeMiniDebugInfoInternal(
+ InstructionSet isa,
+ size_t rodata_section_size,
+ size_t text_section_size,
+ const ArrayRef<const MethodDebugInfo>& method_infos) {
+ std::vector<uint8_t> buffer;
+ buffer.reserve(KB);
+ VectorOutputStream out("Mini-debug-info ELF file", &buffer);
+ std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out));
+ builder->Start();
+ // Mirror .rodata and .text as NOBITS sections.
+ // It is needed to detected relocations after compression.
+ builder->GetRoData()->WriteNoBitsSection(rodata_section_size);
+ builder->GetText()->WriteNoBitsSection(text_section_size);
+ WriteDebugSymbols(builder.get(), method_infos, false /* with_signature */);
+ WriteCFISection(builder.get(), method_infos, DW_DEBUG_FRAME_FORMAT, false /* write_oat_paches */);
+ builder->End();
+ CHECK(builder->Good());
+ std::vector<uint8_t> compressed_buffer;
+ compressed_buffer.reserve(buffer.size() / 4);
+ XzCompress(&buffer, &compressed_buffer);
+ return compressed_buffer;
+}
+
+std::vector<uint8_t> MakeMiniDebugInfo(
+ InstructionSet isa,
+ size_t rodata_size,
+ size_t text_size,
+ const ArrayRef<const MethodDebugInfo>& method_infos) {
+ if (Is64BitInstructionSet(isa)) {
+ return MakeMiniDebugInfoInternal<ElfTypes64>(isa, rodata_size, text_size, method_infos);
+ } else {
+ return MakeMiniDebugInfoInternal<ElfTypes32>(isa, rodata_size, text_size, method_infos);
+ }
+}
+
+template <typename ElfTypes>
+static ArrayRef<const uint8_t> WriteDebugElfFileForMethodInternal(
+ const dwarf::MethodDebugInfo& method_info) {
+ const InstructionSet isa = method_info.compiled_method_->GetInstructionSet();
+ std::vector<uint8_t> buffer;
+ buffer.reserve(KB);
+ VectorOutputStream out("Debug ELF file", &buffer);
+ std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out));
+ // No program headers since the ELF file is not linked and has no allocated sections.
+ builder->Start(false /* write_program_headers */);
+ WriteDebugInfo(builder.get(),
+ ArrayRef<const MethodDebugInfo>(&method_info, 1),
+ DW_DEBUG_FRAME_FORMAT,
+ false /* write_oat_patches */);
+ builder->End();
+ CHECK(builder->Good());
+ // Make a copy of the buffer. We want to shrink it anyway.
+ uint8_t* result = new uint8_t[buffer.size()];
+ CHECK(result != nullptr);
+ memcpy(result, buffer.data(), buffer.size());
+ return ArrayRef<const uint8_t>(result, buffer.size());
+}
+
+ArrayRef<const uint8_t> WriteDebugElfFileForMethod(const dwarf::MethodDebugInfo& method_info) {
+ const InstructionSet isa = method_info.compiled_method_->GetInstructionSet();
+ if (Is64BitInstructionSet(isa)) {
+ return WriteDebugElfFileForMethodInternal<ElfTypes64>(method_info);
+ } else {
+ return WriteDebugElfFileForMethodInternal<ElfTypes32>(method_info);
+ }
+}
+
+template <typename ElfTypes>
+static ArrayRef<const uint8_t> WriteDebugElfFileForClassesInternal(
+ const InstructionSet isa, const ArrayRef<mirror::Class*>& types)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ std::vector<uint8_t> buffer;
+ buffer.reserve(KB);
+ VectorOutputStream out("Debug ELF file", &buffer);
+ std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out));
+ // No program headers since the ELF file is not linked and has no allocated sections.
+ builder->Start(false /* write_program_headers */);
+ DebugInfoWriter<ElfTypes> info_writer(builder.get());
+ info_writer.Start();
+ info_writer.WriteTypes(types);
+ info_writer.End(false /* write_oat_patches */);
+
+ builder->End();
+ CHECK(builder->Good());
+ // Make a copy of the buffer. We want to shrink it anyway.
+ uint8_t* result = new uint8_t[buffer.size()];
+ CHECK(result != nullptr);
+ memcpy(result, buffer.data(), buffer.size());
+ return ArrayRef<const uint8_t>(result, buffer.size());
+}
+
+ArrayRef<const uint8_t> WriteDebugElfFileForClasses(const InstructionSet isa,
+ const ArrayRef<mirror::Class*>& types) {
+ if (Is64BitInstructionSet(isa)) {
+ return WriteDebugElfFileForClassesInternal<ElfTypes64>(isa, types);
+ } else {
+ return WriteDebugElfFileForClassesInternal<ElfTypes32>(isa, types);
+ }
+}
+
+// Explicit instantiations
+template void WriteDebugInfo<ElfTypes32>(
+ ElfBuilder<ElfTypes32>* builder,
+ const ArrayRef<const MethodDebugInfo>& method_infos,
+ CFIFormat cfi_format,
+ bool write_oat_patches);
+template void WriteDebugInfo<ElfTypes64>(
+ ElfBuilder<ElfTypes64>* builder,
+ const ArrayRef<const MethodDebugInfo>& method_infos,
+ CFIFormat cfi_format,
+ bool write_oat_patches);
+
+} // namespace dwarf
+} // namespace art
diff --git a/compiler/debug/elf_writer_debug.h b/compiler/debug/elf_writer_debug.h
new file mode 100644
index 0000000..c5bf609
--- /dev/null
+++ b/compiler/debug/elf_writer_debug.h
@@ -0,0 +1,53 @@
+/*
+ * 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_COMPILER_DEBUG_ELF_WRITER_DEBUG_H_
+#define ART_COMPILER_DEBUG_ELF_WRITER_DEBUG_H_
+
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "debug/dwarf/dwarf_constants.h"
+#include "elf_builder.h"
+#include "utils/array_ref.h"
+
+namespace art {
+namespace mirror {
+class Class;
+}
+namespace dwarf {
+struct MethodDebugInfo;
+
+template <typename ElfTypes>
+void WriteDebugInfo(ElfBuilder<ElfTypes>* builder,
+ const ArrayRef<const MethodDebugInfo>& method_infos,
+ CFIFormat cfi_format,
+ bool write_oat_patches);
+
+std::vector<uint8_t> MakeMiniDebugInfo(InstructionSet isa,
+ size_t rodata_section_size,
+ size_t text_section_size,
+ const ArrayRef<const MethodDebugInfo>& method_infos);
+
+ArrayRef<const uint8_t> WriteDebugElfFileForMethod(const dwarf::MethodDebugInfo& method_info);
+
+ArrayRef<const uint8_t> WriteDebugElfFileForClasses(const InstructionSet isa,
+ const ArrayRef<mirror::Class*>& types)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+} // namespace dwarf
+} // namespace art
+
+#endif // ART_COMPILER_DEBUG_ELF_WRITER_DEBUG_H_
diff --git a/compiler/debug/method_debug_info.h b/compiler/debug/method_debug_info.h
new file mode 100644
index 0000000..7da8fb7
--- /dev/null
+++ b/compiler/debug/method_debug_info.h
@@ -0,0 +1,41 @@
+/*
+ * 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_COMPILER_DEBUG_METHOD_DEBUG_INFO_H_
+#define ART_COMPILER_DEBUG_METHOD_DEBUG_INFO_H_
+
+#include "dex_file.h"
+
+namespace art {
+class CompiledMethod;
+namespace dwarf {
+
+struct MethodDebugInfo {
+ const DexFile* dex_file_;
+ size_t class_def_index_;
+ uint32_t dex_method_index_;
+ uint32_t access_flags_;
+ const DexFile::CodeItem* code_item_;
+ bool deduped_;
+ uintptr_t low_pc_;
+ uintptr_t high_pc_;
+ CompiledMethod* compiled_method_;
+};
+
+} // namespace dwarf
+} // namespace art
+
+#endif // ART_COMPILER_DEBUG_METHOD_DEBUG_INFO_H_