Split elf_writer_debug.cc to several files.
Refactoring only. The file has grown significantly over time,
and it is time to split it so it can be better managed.
Change-Id: Idce0231718add722292f4701df353d5baf31de5f
diff --git a/compiler/debug/elf_compilation_unit.h b/compiler/debug/elf_compilation_unit.h
new file mode 100644
index 0000000..f725f45
--- /dev/null
+++ b/compiler/debug/elf_compilation_unit.h
@@ -0,0 +1,38 @@
+/*
+ * 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_ELF_COMPILATION_UNIT_H_
+#define ART_COMPILER_DEBUG_ELF_COMPILATION_UNIT_H_
+
+#include <vector>
+
+#include "debug/method_debug_info.h"
+
+namespace art {
+namespace debug {
+
+struct ElfCompilationUnit {
+ 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;
+};
+
+} // namespace debug
+} // namespace art
+
+#endif // ART_COMPILER_DEBUG_ELF_COMPILATION_UNIT_H_
+
diff --git a/compiler/debug/elf_debug_frame_writer.h b/compiler/debug/elf_debug_frame_writer.h
new file mode 100644
index 0000000..f6d9b16
--- /dev/null
+++ b/compiler/debug/elf_debug_frame_writer.h
@@ -0,0 +1,290 @@
+/*
+ * 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_ELF_DEBUG_FRAME_WRITER_H_
+#define ART_COMPILER_DEBUG_ELF_DEBUG_FRAME_WRITER_H_
+
+#include <vector>
+
+#include "arch/instruction_set.h"
+#include "debug/dwarf/debug_frame_opcode_writer.h"
+#include "debug/dwarf/dwarf_constants.h"
+#include "debug/dwarf/headers.h"
+#include "debug/method_debug_info.h"
+#include "elf_builder.h"
+
+namespace art {
+namespace debug {
+
+static void WriteCIE(InstructionSet isa,
+ dwarf::CFIFormat format,
+ std::vector<uint8_t>* buffer) {
+ using Reg = dwarf::Reg;
+ // 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: {
+ dwarf::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: {
+ dwarf::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: {
+ dwarf::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;
+ dwarf::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: {
+ dwarf::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,
+ dwarf::CFIFormat format,
+ bool write_oat_patches) {
+ CHECK(format == dwarf::DW_DEBUG_FRAME_FORMAT || format == dwarf::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 == dwarf::DW_EH_FRAME_FORMAT) {
+ binary_search_table.reserve(2 * method_infos.size());
+ } else {
+ patch_locations.reserve(method_infos.size());
+ }
+
+ // The methods can be written in 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 == dwarf::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 == dwarf::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 == dwarf::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;
+ dwarf::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(dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4);
+ // Encoding of binary search table size.
+ header.PushUint8(dwarf::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(dwarf::DW_EH_PE_datarel | dwarf::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 debug
+} // namespace art
+
+#endif // ART_COMPILER_DEBUG_ELF_DEBUG_FRAME_WRITER_H_
+
diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h
new file mode 100644
index 0000000..eed032f
--- /dev/null
+++ b/compiler/debug/elf_debug_info_writer.h
@@ -0,0 +1,649 @@
+/*
+ * 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_ELF_DEBUG_INFO_WRITER_H_
+#define ART_COMPILER_DEBUG_ELF_DEBUG_INFO_WRITER_H_
+
+#include <map>
+#include <unordered_set>
+#include <vector>
+
+#include "debug/dwarf/debug_abbrev_writer.h"
+#include "debug/dwarf/debug_info_entry_writer.h"
+#include "debug/elf_compilation_unit.h"
+#include "debug/elf_debug_loc_writer.h"
+#include "debug/method_debug_info.h"
+#include "dex_file-inl.h"
+#include "dex_file.h"
+#include "elf_builder.h"
+#include "linear_alloc.h"
+#include "mirror/array.h"
+#include "mirror/class-inl.h"
+#include "mirror/class.h"
+
+namespace art {
+namespace debug {
+
+typedef std::vector<DexFile::LocalInfo> LocalInfos;
+
+static void LocalInfoCallback(void* ctx, const DexFile::LocalInfo& entry) {
+ static_cast<LocalInfos*>(ctx)->push_back(entry);
+}
+
+static 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;
+}
+
+// Helper class to write .debug_info and its supporting sections.
+template<typename ElfTypes>
+class ElfDebugInfoWriter {
+ using Elf_Addr = typename ElfTypes::Addr;
+
+ public:
+ explicit ElfDebugInfoWriter(ElfBuilder<ElfTypes>* builder)
+ : builder_(builder),
+ debug_abbrev_(&debug_abbrev_buffer_) {
+ }
+
+ void Start() {
+ builder_->GetDebugInfo()->Start();
+ }
+
+ 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_;
+ dwarf::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 ElfTypes2>
+ friend class ElfCompilationUnitWriter;
+};
+
+// Helper class to write one compilation unit.
+// It holds helper methods and temporary state.
+template<typename ElfTypes>
+class ElfCompilationUnitWriter {
+ using Elf_Addr = typename ElfTypes::Addr;
+
+ public:
+ explicit ElfCompilationUnitWriter(ElfDebugInfoWriter<ElfTypes>* owner)
+ : owner_(owner),
+ info_(Is64BitInstructionSet(owner_->builder_->GetIsa()), &owner->debug_abbrev_) {
+ }
+
+ void Write(const ElfCompilationUnit& 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;
+ using namespace dwarf; // NOLINT. For easy access to DWARF constants.
+
+ 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_) {
+ using namespace dwarf; // NOLINT. For easy access to DWARF constants.
+
+ 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());
+ }
+
+ // 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) {
+ WriteDebugLocEntry(method_info,
+ vreg,
+ is64bitValue,
+ compilation_unit_low_pc,
+ dex_pc_low,
+ dex_pc_high,
+ owner_->builder_->GetIsa(),
+ &info_,
+ &owner_->debug_loc_,
+ &owner_->debug_ranges_);
+ }
+
+ // 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(dwarf::DW_AT_linkage_name, name);
+ }
+
+ // 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(dwarf::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(dwarf::DW_AT_name, name);
+ }
+ }
+
+ // Convert dex type descriptor to DWARF.
+ // Returns offset in the compilation unit.
+ size_t WriteTypeDeclaration(const std::string& desc) {
+ using namespace dwarf; // NOLINT. For easy access to DWARF constants.
+
+ 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(dwarf::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(dwarf::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.
+ ElfDebugInfoWriter<ElfTypes>* owner_;
+ // Temporary buffer to create and store the entries.
+ dwarf::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_;
+};
+
+} // namespace debug
+} // namespace art
+
+#endif // ART_COMPILER_DEBUG_ELF_DEBUG_INFO_WRITER_H_
+
diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h
new file mode 100644
index 0000000..ac0f4ca
--- /dev/null
+++ b/compiler/debug/elf_debug_line_writer.h
@@ -0,0 +1,247 @@
+/*
+ * 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_ELF_DEBUG_LINE_WRITER_H_
+#define ART_COMPILER_DEBUG_ELF_DEBUG_LINE_WRITER_H_
+
+#include <vector>
+
+#include "compiled_method.h"
+#include "debug/dwarf/debug_line_opcode_writer.h"
+#include "debug/dwarf/headers.h"
+#include "debug/elf_compilation_unit.h"
+#include "dex_file-inl.h"
+#include "dex_file.h"
+#include "elf_builder.h"
+#include "stack_map.h"
+
+namespace art {
+namespace debug {
+
+typedef std::vector<DexFile::PositionInfo> PositionInfos;
+
+static bool PositionInfoCallback(void* ctx, const DexFile::PositionInfo& entry) {
+ static_cast<PositionInfos*>(ctx)->push_back(entry);
+ return false;
+}
+
+template<typename ElfTypes>
+class ElfDebugLineWriter {
+ using Elf_Addr = typename ElfTypes::Addr;
+
+ public:
+ explicit ElfDebugLineWriter(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(ElfCompilationUnit& 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<dwarf::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;
+ }
+ dwarf::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 (mi->IsFromOptimizingCompiler()) {
+ // 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(dwarf::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_;
+};
+
+} // namespace debug
+} // namespace art
+
+#endif // ART_COMPILER_DEBUG_ELF_DEBUG_LINE_WRITER_H_
+
diff --git a/compiler/debug/elf_debug_loc_writer.h b/compiler/debug/elf_debug_loc_writer.h
new file mode 100644
index 0000000..fd7f949
--- /dev/null
+++ b/compiler/debug/elf_debug_loc_writer.h
@@ -0,0 +1,298 @@
+/*
+ * 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_ELF_DEBUG_LOC_WRITER_H_
+#define ART_COMPILER_DEBUG_ELF_DEBUG_LOC_WRITER_H_
+
+#include <map>
+
+#include "arch/instruction_set.h"
+#include "compiled_method.h"
+#include "debug/dwarf/debug_info_entry_writer.h"
+#include "debug/dwarf/register.h"
+#include "debug/method_debug_info.h"
+#include "stack_map.h"
+
+namespace art {
+namespace debug {
+using Reg = dwarf::Reg;
+
+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);
+ case kNone:
+ LOG(FATAL) << "No instruction set";
+ }
+ 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);
+ case kNone:
+ LOG(FATAL) << "No instruction set";
+ }
+ UNREACHABLE();
+}
+
+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;
+}
+
+// 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.
+static void WriteDebugLocEntry(const MethodDebugInfo* method_info,
+ uint16_t vreg,
+ bool is64bitValue,
+ uint32_t compilation_unit_low_pc,
+ uint32_t dex_pc_low,
+ uint32_t dex_pc_high,
+ InstructionSet isa,
+ dwarf::DebugInfoEntryWriter<>* debug_info,
+ std::vector<uint8_t>* debug_loc_buffer,
+ std::vector<uint8_t>* debug_ranges_buffer) {
+ using Kind = DexRegisterLocation::Kind;
+ if (!method_info->IsFromOptimizingCompiler()) {
+ return;
+ }
+
+ dwarf::Writer<> debug_loc(debug_loc_buffer);
+ dwarf::Writer<> debug_ranges(debug_ranges_buffer);
+ debug_info->WriteSecOffset(dwarf::DW_AT_location, debug_loc.size());
+ debug_info->WriteSecOffset(dwarf::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 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)
+ dwarf::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);
+ }
+}
+
+} // namespace debug
+} // namespace art
+
+#endif // ART_COMPILER_DEBUG_ELF_DEBUG_LOC_WRITER_H_
+
diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc
new file mode 100644
index 0000000..01bd679
--- /dev/null
+++ b/compiler/debug/elf_debug_writer.cc
@@ -0,0 +1,183 @@
+/*
+ * 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.
+ */
+
+#include "elf_debug_writer.h"
+
+#include <vector>
+
+#include "debug/dwarf/dwarf_constants.h"
+#include "debug/elf_compilation_unit.h"
+#include "debug/elf_debug_frame_writer.h"
+#include "debug/elf_debug_info_writer.h"
+#include "debug/elf_debug_line_writer.h"
+#include "debug/elf_debug_loc_writer.h"
+#include "debug/elf_gnu_debugdata_writer.h"
+#include "debug/elf_symtab_writer.h"
+#include "debug/method_debug_info.h"
+#include "elf_builder.h"
+#include "linker/vector_output_stream.h"
+#include "utils/array_ref.h"
+
+namespace art {
+namespace debug {
+
+template <typename ElfTypes>
+void WriteDebugInfo(ElfBuilder<ElfTypes>* builder,
+ const ArrayRef<const MethodDebugInfo>& method_infos,
+ dwarf::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);
+}
+
+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<ElfCompilationUnit> 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(ElfCompilationUnit());
+ }
+ ElfCompilationUnit& 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()) {
+ ElfDebugLineWriter<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()) {
+ ElfDebugInfoWriter<ElfTypes> info_writer(builder);
+ info_writer.Start();
+ for (const auto& compilation_unit : compilation_units) {
+ ElfCompilationUnitWriter<ElfTypes> cu_writer(&info_writer);
+ cu_writer.Write(compilation_unit);
+ }
+ info_writer.End(write_oat_patches);
+ }
+}
+
+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 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),
+ dwarf::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 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 */);
+ ElfDebugInfoWriter<ElfTypes> info_writer(builder.get());
+ info_writer.Start();
+ ElfCompilationUnitWriter<ElfTypes> cu_writer(&info_writer);
+ cu_writer.Write(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,
+ dwarf::CFIFormat cfi_format,
+ bool write_oat_patches);
+template void WriteDebugInfo<ElfTypes64>(
+ ElfBuilder<ElfTypes64>* builder,
+ const ArrayRef<const MethodDebugInfo>& method_infos,
+ dwarf::CFIFormat cfi_format,
+ bool write_oat_patches);
+
+} // namespace debug
+} // namespace art
diff --git a/compiler/debug/elf_writer_debug.h b/compiler/debug/elf_debug_writer.h
similarity index 79%
rename from compiler/debug/elf_writer_debug.h
rename to compiler/debug/elf_debug_writer.h
index c5bf609..103b501 100644
--- a/compiler/debug/elf_writer_debug.h
+++ b/compiler/debug/elf_debug_writer.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_COMPILER_DEBUG_ELF_WRITER_DEBUG_H_
-#define ART_COMPILER_DEBUG_ELF_WRITER_DEBUG_H_
+#ifndef ART_COMPILER_DEBUG_ELF_DEBUG_WRITER_H_
+#define ART_COMPILER_DEBUG_ELF_DEBUG_WRITER_H_
#include "base/macros.h"
#include "base/mutex.h"
@@ -27,13 +27,13 @@
namespace mirror {
class Class;
}
-namespace dwarf {
+namespace debug {
struct MethodDebugInfo;
template <typename ElfTypes>
void WriteDebugInfo(ElfBuilder<ElfTypes>* builder,
const ArrayRef<const MethodDebugInfo>& method_infos,
- CFIFormat cfi_format,
+ dwarf::CFIFormat cfi_format,
bool write_oat_patches);
std::vector<uint8_t> MakeMiniDebugInfo(InstructionSet isa,
@@ -41,13 +41,13 @@
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> WriteDebugElfFileForMethod(const MethodDebugInfo& method_info);
ArrayRef<const uint8_t> WriteDebugElfFileForClasses(const InstructionSet isa,
const ArrayRef<mirror::Class*>& types)
SHARED_REQUIRES(Locks::mutator_lock_);
-} // namespace dwarf
+} // namespace debug
} // namespace art
-#endif // ART_COMPILER_DEBUG_ELF_WRITER_DEBUG_H_
+#endif // ART_COMPILER_DEBUG_ELF_DEBUG_WRITER_H_
diff --git a/compiler/debug/elf_gnu_debugdata_writer.h b/compiler/debug/elf_gnu_debugdata_writer.h
new file mode 100644
index 0000000..5c7d1c7
--- /dev/null
+++ b/compiler/debug/elf_gnu_debugdata_writer.h
@@ -0,0 +1,111 @@
+/*
+ * 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_ELF_GNU_DEBUGDATA_WRITER_H_
+#define ART_COMPILER_DEBUG_ELF_GNU_DEBUGDATA_WRITER_H_
+
+#include <vector>
+
+#include "arch/instruction_set.h"
+#include "elf_builder.h"
+#include "linker/vector_output_stream.h"
+
+// liblzma.
+#include "7zCrc.h"
+#include "XzCrc64.h"
+#include "XzEnc.h"
+
+namespace art {
+namespace debug {
+
+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>
+static 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,
+ dwarf::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;
+}
+
+} // namespace debug
+} // namespace art
+
+#endif // ART_COMPILER_DEBUG_ELF_GNU_DEBUGDATA_WRITER_H_
+
diff --git a/compiler/debug/elf_symtab_writer.h b/compiler/debug/elf_symtab_writer.h
new file mode 100644
index 0000000..41508f4
--- /dev/null
+++ b/compiler/debug/elf_symtab_writer.h
@@ -0,0 +1,115 @@
+/*
+ * 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_ELF_SYMTAB_WRITER_H_
+#define ART_COMPILER_DEBUG_ELF_SYMTAB_WRITER_H_
+
+#include <unordered_set>
+
+#include "debug/method_debug_info.h"
+#include "elf_builder.h"
+#include "utils.h"
+
+namespace art {
+namespace debug {
+
+// 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;
+
+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();
+}
+
+} // namespace debug
+} // namespace art
+
+#endif // ART_COMPILER_DEBUG_ELF_SYMTAB_WRITER_H_
+
diff --git a/compiler/debug/elf_writer_debug.cc b/compiler/debug/elf_writer_debug.cc
deleted file mode 100644
index 07d16d2..0000000
--- a/compiler/debug/elf_writer_debug.cc
+++ /dev/null
@@ -1,1666 +0,0 @@
-/*
- * 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/method_debug_info.h b/compiler/debug/method_debug_info.h
index 7da8fb7..6b3dd8c 100644
--- a/compiler/debug/method_debug_info.h
+++ b/compiler/debug/method_debug_info.h
@@ -17,25 +17,32 @@
#ifndef ART_COMPILER_DEBUG_METHOD_DEBUG_INFO_H_
#define ART_COMPILER_DEBUG_METHOD_DEBUG_INFO_H_
+#include "compiled_method.h"
#include "dex_file.h"
namespace art {
-class CompiledMethod;
-namespace dwarf {
+namespace debug {
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_;
+ 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;
+
+ bool IsFromOptimizingCompiler() const {
+ return compiled_method->GetQuickCode().size() > 0 &&
+ compiled_method->GetVmapTable().size() > 0 &&
+ compiled_method->GetGcMap().size() == 0 &&
+ code_item != nullptr;
+ }
};
-} // namespace dwarf
+} // namespace debug
} // namespace art
#endif // ART_COMPILER_DEBUG_METHOD_DEBUG_INFO_H_