|  | /* | 
|  | * 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 <type_traits> | 
|  | #include <unordered_map> | 
|  | #include <vector> | 
|  |  | 
|  | #include "base/array_ref.h" | 
|  | #include "base/stl_util.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_symtab_writer.h" | 
|  | #include "debug/method_debug_info.h" | 
|  | #include "dwarf/dwarf_constants.h" | 
|  | #include "elf/elf_builder.h" | 
|  | #include "elf/elf_debug_reader.h" | 
|  | #include "elf/elf_utils.h" | 
|  | #include "elf/xz_utils.h" | 
|  | #include "jit/debugger_interface.h" | 
|  | #include "oat.h" | 
|  | #include "stream/vector_output_stream.h" | 
|  |  | 
|  | namespace art HIDDEN { | 
|  | namespace debug { | 
|  |  | 
|  | using ElfRuntimeTypes = std::conditional<sizeof(void*) == 4, ElfTypes32, ElfTypes64>::type; | 
|  |  | 
|  | template <typename ElfTypes> | 
|  | void WriteDebugInfo(ElfBuilder<ElfTypes>* builder, | 
|  | const DebugInfo& debug_info) { | 
|  | // Write .strtab and .symtab. | 
|  | WriteDebugSymbols(builder, /* mini-debug-info= */ false, debug_info); | 
|  |  | 
|  | // Write .debug_frame. | 
|  | WriteCFISection(builder, debug_info.compiled_methods); | 
|  |  | 
|  | // Group the methods into compilation units based on class. | 
|  | std::unordered_map<const dex::ClassDef*, ElfCompilationUnit> class_to_compilation_unit; | 
|  | for (const MethodDebugInfo& mi : debug_info.compiled_methods) { | 
|  | if (mi.dex_file != nullptr) { | 
|  | auto& dex_class_def = mi.dex_file->GetClassDef(mi.class_def_index); | 
|  | ElfCompilationUnit& cu = class_to_compilation_unit[&dex_class_def]; | 
|  | cu.methods.push_back(&mi); | 
|  | // All methods must have the same addressing mode otherwise the min/max below does not work. | 
|  | DCHECK_EQ(cu.methods.front()->is_code_address_text_relative, mi.is_code_address_text_relative); | 
|  | cu.is_code_address_text_relative = mi.is_code_address_text_relative; | 
|  | cu.code_address = std::min(cu.code_address, mi.code_address); | 
|  | cu.code_end = std::max(cu.code_end, mi.code_address + mi.code_size); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Sort compilation units to make the compiler output deterministic. | 
|  | std::vector<ElfCompilationUnit> compilation_units; | 
|  | compilation_units.reserve(class_to_compilation_unit.size()); | 
|  | for (auto& it : class_to_compilation_unit) { | 
|  | // The .debug_line section requires the methods to be sorted by code address. | 
|  | std::stable_sort(it.second.methods.begin(), | 
|  | it.second.methods.end(), | 
|  | [](const MethodDebugInfo* a, const MethodDebugInfo* b) { | 
|  | return a->code_address < b->code_address; | 
|  | }); | 
|  | compilation_units.push_back(std::move(it.second)); | 
|  | } | 
|  | std::sort(compilation_units.begin(), | 
|  | compilation_units.end(), | 
|  | [](ElfCompilationUnit& a, ElfCompilationUnit& b) { | 
|  | // Sort by index of the first method within the method_infos array. | 
|  | // This assumes that the order of method_infos is deterministic. | 
|  | // Code address is not good for sorting due to possible duplicates. | 
|  | return a.methods.front() < b.methods.front(); | 
|  | }); | 
|  |  | 
|  | // 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 .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(); | 
|  | } | 
|  | } | 
|  |  | 
|  | template <typename ElfTypes> | 
|  | static std::vector<uint8_t> MakeMiniDebugInfoInternal( | 
|  | InstructionSet isa, | 
|  | [[maybe_unused]] const InstructionSetFeatures* features, | 
|  | typename ElfTypes::Addr text_section_address, | 
|  | size_t text_section_size, | 
|  | typename ElfTypes::Addr dex_section_address, | 
|  | size_t dex_section_size, | 
|  | const DebugInfo& debug_info) { | 
|  | 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(/* write_program_headers= */ false); | 
|  | // Mirror ELF sections as NOBITS since the added symbols will reference them. | 
|  | if (text_section_size != 0) { | 
|  | builder->GetText()->AllocateVirtualMemory(text_section_address, text_section_size); | 
|  | } | 
|  | if (dex_section_size != 0) { | 
|  | builder->GetDex()->AllocateVirtualMemory(dex_section_address, dex_section_size); | 
|  | } | 
|  | if (!debug_info.Empty()) { | 
|  | WriteDebugSymbols(builder.get(), /* mini-debug-info= */ true, debug_info); | 
|  | } | 
|  | if (!debug_info.compiled_methods.empty()) { | 
|  | WriteCFISection(builder.get(), debug_info.compiled_methods); | 
|  | } | 
|  | builder->End(); | 
|  | CHECK(builder->Good()); | 
|  | std::vector<uint8_t> compressed_buffer; | 
|  | compressed_buffer.reserve(buffer.size() / 4); | 
|  | XzCompress(ArrayRef<const uint8_t>(buffer), &compressed_buffer); | 
|  | return compressed_buffer; | 
|  | } | 
|  |  | 
|  | std::vector<uint8_t> MakeMiniDebugInfo( | 
|  | InstructionSet isa, | 
|  | const InstructionSetFeatures* features, | 
|  | uint64_t text_section_address, | 
|  | size_t text_section_size, | 
|  | uint64_t dex_section_address, | 
|  | size_t dex_section_size, | 
|  | const DebugInfo& debug_info) { | 
|  | if (Is64BitInstructionSet(isa)) { | 
|  | return MakeMiniDebugInfoInternal<ElfTypes64>(isa, | 
|  | features, | 
|  | text_section_address, | 
|  | text_section_size, | 
|  | dex_section_address, | 
|  | dex_section_size, | 
|  | debug_info); | 
|  | } else { | 
|  | return MakeMiniDebugInfoInternal<ElfTypes32>(isa, | 
|  | features, | 
|  | text_section_address, | 
|  | text_section_size, | 
|  | dex_section_address, | 
|  | dex_section_size, | 
|  | debug_info); | 
|  | } | 
|  | } | 
|  |  | 
|  | std::vector<uint8_t> MakeElfFileForJIT(InstructionSet isa, | 
|  | [[maybe_unused]] const InstructionSetFeatures* features, | 
|  | bool mini_debug_info, | 
|  | const MethodDebugInfo& method_info) { | 
|  | using ElfTypes = ElfRuntimeTypes; | 
|  | CHECK_EQ(sizeof(ElfTypes::Addr), static_cast<size_t>(GetInstructionSetPointerSize(isa))); | 
|  | CHECK_EQ(method_info.is_code_address_text_relative, false); | 
|  | DebugInfo debug_info{}; | 
|  | debug_info.compiled_methods = ArrayRef<const MethodDebugInfo>(&method_info, 1); | 
|  | 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(/* write_program_headers= */ false); | 
|  | builder->GetText()->AllocateVirtualMemory(method_info.code_address, method_info.code_size); | 
|  | if (mini_debug_info) { | 
|  | // The compression is great help for multiple methods but it is not worth it for a | 
|  | // single method due to the overheads so skip the compression here for performance. | 
|  | WriteDebugSymbols(builder.get(), /* mini-debug-info= */ true, debug_info); | 
|  | WriteCFISection(builder.get(), debug_info.compiled_methods); | 
|  | } else { | 
|  | WriteDebugInfo(builder.get(), debug_info); | 
|  | } | 
|  | builder->End(); | 
|  | CHECK(builder->Good()); | 
|  | // Verify the ELF file by reading it back using the trivial reader. | 
|  | if (kIsDebugBuild) { | 
|  | using Elf_Sym = typename ElfTypes::Sym; | 
|  | size_t num_syms = 0; | 
|  | size_t num_cies = 0; | 
|  | size_t num_fdes = 0; | 
|  | using Reader = ElfDebugReader<ElfTypes>; | 
|  | Reader reader(buffer); | 
|  | reader.VisitFunctionSymbols([&](Elf_Sym sym, const char*) { | 
|  | DCHECK_EQ(sym.st_value, | 
|  | method_info.code_address + GetInstructionSetEntryPointAdjustment(isa)); | 
|  | DCHECK_EQ(sym.st_size, method_info.code_size); | 
|  | num_syms++; | 
|  | }); | 
|  | reader.VisitDebugFrame([&]([[maybe_unused]] const Reader::CIE* cie) { num_cies++; }, | 
|  | [&](const Reader::FDE* fde, [[maybe_unused]] const Reader::CIE* cie) { | 
|  | DCHECK_EQ(fde->sym_addr, method_info.code_address); | 
|  | DCHECK_EQ(fde->sym_size, method_info.code_size); | 
|  | num_fdes++; | 
|  | }); | 
|  | DCHECK_EQ(num_syms, 1u); | 
|  | DCHECK_LE(num_cies, 1u); | 
|  | DCHECK_LE(num_fdes, 1u); | 
|  | } | 
|  | return buffer; | 
|  | } | 
|  |  | 
|  | // Combine several mini-debug-info ELF files into one, while filtering some symbols. | 
|  | std::vector<uint8_t> PackElfFileForJIT( | 
|  | ArrayRef<const JITCodeEntry*> jit_entries, | 
|  | ArrayRef<const void*> removed_symbols, | 
|  | bool compress, | 
|  | /*out*/ size_t* num_symbols) { | 
|  | using ElfTypes = ElfRuntimeTypes; | 
|  | using Elf_Addr = typename ElfTypes::Addr; | 
|  | using Elf_Sym = typename ElfTypes::Sym; | 
|  | const InstructionSet isa = kRuntimeISA; | 
|  | CHECK_EQ(sizeof(Elf_Addr), static_cast<size_t>(GetInstructionSetPointerSize(isa))); | 
|  | const uint32_t kPcAlign = GetInstructionSetInstructionAlignment(isa); | 
|  | auto is_pc_aligned = [](const void* pc) { return IsAligned<kPcAlign>(pc); }; | 
|  | DCHECK(std::all_of(removed_symbols.begin(), removed_symbols.end(), is_pc_aligned)); | 
|  | auto is_removed_symbol = [&removed_symbols](Elf_Addr addr) { | 
|  | // Remove thumb-bit, if any (using the fact that address is instruction aligned). | 
|  | const void* code_ptr = AlignDown(reinterpret_cast<const void*>(addr), kPcAlign); | 
|  | return std::binary_search(removed_symbols.begin(), removed_symbols.end(), code_ptr); | 
|  | }; | 
|  | uint64_t min_address = std::numeric_limits<uint64_t>::max(); | 
|  | uint64_t max_address = 0; | 
|  |  | 
|  | // Produce the inner ELF file. | 
|  | // It will contain the symbols (.symtab) and unwind information (.debug_frame). | 
|  | std::vector<uint8_t> inner_elf_file; | 
|  | { | 
|  | inner_elf_file.reserve(1 * KB);  // Approximate size of ELF file with a single symbol. | 
|  | VectorOutputStream out("Mini-debug-info ELF file for JIT", &inner_elf_file); | 
|  | std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out)); | 
|  | builder->Start(/*write_program_headers=*/ false); | 
|  | auto* text = builder->GetText(); | 
|  | auto* strtab = builder->GetStrTab(); | 
|  | auto* symtab = builder->GetSymTab(); | 
|  | auto* debug_frame = builder->GetDebugFrame(); | 
|  | std::deque<Elf_Sym> symbols; | 
|  |  | 
|  | using Reader = ElfDebugReader<ElfTypes>; | 
|  | std::deque<Reader> readers; | 
|  | for (const JITCodeEntry* it : jit_entries) { | 
|  | readers.emplace_back(GetJITCodeEntrySymFile(it)); | 
|  | } | 
|  |  | 
|  | // Write symbols names. All other data is buffered. | 
|  | strtab->Start(); | 
|  | strtab->Write("");  // strtab should start with empty string. | 
|  | for (Reader& reader : readers) { | 
|  | reader.VisitFunctionSymbols([&](Elf_Sym sym, const char* name) { | 
|  | if (is_removed_symbol(sym.st_value)) { | 
|  | return; | 
|  | } | 
|  | sym.st_name = strtab->Write(name); | 
|  | symbols.push_back(sym); | 
|  | min_address = std::min<uint64_t>(min_address, sym.st_value); | 
|  | max_address = std::max<uint64_t>(max_address, sym.st_value + sym.st_size); | 
|  | }); | 
|  | } | 
|  | strtab->End(); | 
|  |  | 
|  | // Create .text covering the code range. Needed for gdb to find the symbols. | 
|  | if (max_address > min_address) { | 
|  | text->AllocateVirtualMemory(min_address, max_address - min_address); | 
|  | } | 
|  |  | 
|  | // Add the symbols. | 
|  | *num_symbols = symbols.size(); | 
|  | for (; !symbols.empty(); symbols.pop_front()) { | 
|  | symtab->Add(symbols.front(), text); | 
|  | } | 
|  | symtab->WriteCachedSection(); | 
|  |  | 
|  | // Add the CFI/unwind section. | 
|  | debug_frame->Start(); | 
|  | // ART always produces the same CIE, so we copy the first one and ignore the rest. | 
|  | bool copied_cie = false; | 
|  | for (Reader& reader : readers) { | 
|  | reader.VisitDebugFrame( | 
|  | [&](const Reader::CIE* cie) { | 
|  | if (!copied_cie) { | 
|  | debug_frame->WriteFully(cie->data(), cie->size()); | 
|  | copied_cie = true; | 
|  | } | 
|  | }, | 
|  | [&](const Reader::FDE* fde, [[maybe_unused]] const Reader::CIE* cie) { | 
|  | DCHECK(copied_cie); | 
|  | DCHECK_EQ(fde->cie_pointer, 0); | 
|  | if (!is_removed_symbol(fde->sym_addr)) { | 
|  | debug_frame->WriteFully(fde->data(), fde->size()); | 
|  | } | 
|  | }); | 
|  | } | 
|  | debug_frame->End(); | 
|  |  | 
|  | builder->End(); | 
|  | CHECK(builder->Good()); | 
|  | } | 
|  |  | 
|  | // Produce the outer ELF file. | 
|  | // It contains only the inner ELF file compressed as .gnu_debugdata section. | 
|  | // This extra wrapping is not necessary but the compression saves space. | 
|  | if (compress) { | 
|  | std::vector<uint8_t> outer_elf_file; | 
|  | std::vector<uint8_t> gnu_debugdata; | 
|  | gnu_debugdata.reserve(inner_elf_file.size() / 4); | 
|  | XzCompress(ArrayRef<const uint8_t>(inner_elf_file), &gnu_debugdata); | 
|  |  | 
|  | outer_elf_file.reserve(KB + gnu_debugdata.size()); | 
|  | VectorOutputStream out("Mini-debug-info ELF file for JIT", &outer_elf_file); | 
|  | std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out)); | 
|  | builder->Start(/*write_program_headers=*/ false); | 
|  | if (max_address > min_address) { | 
|  | builder->GetText()->AllocateVirtualMemory(min_address, max_address - min_address); | 
|  | } | 
|  | builder->WriteSection(".gnu_debugdata", &gnu_debugdata); | 
|  | builder->End(); | 
|  | CHECK(builder->Good()); | 
|  | return outer_elf_file; | 
|  | } else { | 
|  | return inner_elf_file; | 
|  | } | 
|  | } | 
|  |  | 
|  | std::vector<uint8_t> WriteDebugElfFileForClasses( | 
|  | InstructionSet isa, | 
|  | [[maybe_unused]] const InstructionSetFeatures* features, | 
|  | const ArrayRef<mirror::Class*>& types) REQUIRES_SHARED(Locks::mutator_lock_) { | 
|  | using ElfTypes = ElfRuntimeTypes; | 
|  | CHECK_EQ(sizeof(ElfTypes::Addr), static_cast<size_t>(GetInstructionSetPointerSize(isa))); | 
|  | 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(/* write_program_headers= */ false); | 
|  | ElfDebugInfoWriter<ElfTypes> info_writer(builder.get()); | 
|  | info_writer.Start(); | 
|  | ElfCompilationUnitWriter<ElfTypes> cu_writer(&info_writer); | 
|  | cu_writer.Write(types); | 
|  | info_writer.End(); | 
|  |  | 
|  | builder->End(); | 
|  | CHECK(builder->Good()); | 
|  | return buffer; | 
|  | } | 
|  |  | 
|  | // Explicit instantiations | 
|  | template void WriteDebugInfo<ElfTypes32>( | 
|  | ElfBuilder<ElfTypes32>* builder, | 
|  | const DebugInfo& debug_info); | 
|  | template void WriteDebugInfo<ElfTypes64>( | 
|  | ElfBuilder<ElfTypes64>* builder, | 
|  | const DebugInfo& debug_info); | 
|  |  | 
|  | }  // namespace debug | 
|  | }  // namespace art |