diff options
-rw-r--r-- | compiler/compiled_method.cc | 15 | ||||
-rw-r--r-- | compiler/compiled_method.h | 9 | ||||
-rw-r--r-- | compiler/compiler_backend.cc | 21 | ||||
-rw-r--r-- | compiler/compiler_backend.h | 13 | ||||
-rw-r--r-- | compiler/dex/quick/codegen_util.cc | 9 | ||||
-rw-r--r-- | compiler/dex/quick/mir_to_lir.h | 5 | ||||
-rw-r--r-- | compiler/dex/quick/x86/call_x86.cc | 4 | ||||
-rw-r--r-- | compiler/dex/quick/x86/codegen_x86.h | 18 | ||||
-rw-r--r-- | compiler/dex/quick/x86/target_x86.cc | 165 | ||||
-rw-r--r-- | compiler/driver/compiler_driver.cc | 17 | ||||
-rw-r--r-- | compiler/driver/compiler_driver.h | 13 | ||||
-rw-r--r-- | compiler/driver/compiler_options.h | 14 | ||||
-rw-r--r-- | compiler/elf_writer_quick.cc | 328 | ||||
-rw-r--r-- | compiler/elf_writer_quick.h | 10 | ||||
-rw-r--r-- | compiler/oat_writer.cc | 21 | ||||
-rw-r--r-- | compiler/oat_writer.h | 15 | ||||
-rw-r--r-- | dex2oat/dex2oat.cc | 8 | ||||
-rw-r--r-- | runtime/elf_file.cc | 355 | ||||
-rw-r--r-- | runtime/elf_file.h | 13 |
19 files changed, 1033 insertions, 20 deletions
diff --git a/compiler/compiled_method.cc b/compiler/compiled_method.cc index f6d724ab56..d884bc0ef8 100644 --- a/compiler/compiled_method.cc +++ b/compiler/compiled_method.cc @@ -153,12 +153,14 @@ CompiledMethod::CompiledMethod(CompilerDriver& driver, const uint32_t fp_spill_mask, const std::vector<uint8_t>& mapping_table, const std::vector<uint8_t>& vmap_table, - const std::vector<uint8_t>& native_gc_map) + const std::vector<uint8_t>& native_gc_map, + const std::vector<uint8_t>* cfi_info) : CompiledCode(&driver, instruction_set, quick_code), frame_size_in_bytes_(frame_size_in_bytes), core_spill_mask_(core_spill_mask), fp_spill_mask_(fp_spill_mask), mapping_table_(driver.DeduplicateMappingTable(mapping_table)), vmap_table_(driver.DeduplicateVMapTable(vmap_table)), - gc_map_(driver.DeduplicateGCMap(native_gc_map)) { + gc_map_(driver.DeduplicateGCMap(native_gc_map)), + cfi_info_(driver.DeduplicateCFIInfo(cfi_info)) { } CompiledMethod::CompiledMethod(CompilerDriver& driver, @@ -169,10 +171,11 @@ CompiledMethod::CompiledMethod(CompilerDriver& driver, const uint32_t fp_spill_mask) : CompiledCode(&driver, instruction_set, code), frame_size_in_bytes_(frame_size_in_bytes), - core_spill_mask_(core_spill_mask), fp_spill_mask_(fp_spill_mask) { - mapping_table_ = driver.DeduplicateMappingTable(std::vector<uint8_t>()); - vmap_table_ = driver.DeduplicateVMapTable(std::vector<uint8_t>()); - gc_map_ = driver.DeduplicateGCMap(std::vector<uint8_t>()); + core_spill_mask_(core_spill_mask), fp_spill_mask_(fp_spill_mask), + mapping_table_(driver.DeduplicateMappingTable(std::vector<uint8_t>())), + vmap_table_(driver.DeduplicateVMapTable(std::vector<uint8_t>())), + gc_map_(driver.DeduplicateGCMap(std::vector<uint8_t>())), + cfi_info_(nullptr) { } // Constructs a CompiledMethod for the Portable compiler. diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h index 611230509a..90ae6eeae8 100644 --- a/compiler/compiled_method.h +++ b/compiler/compiled_method.h @@ -110,7 +110,8 @@ class CompiledMethod : public CompiledCode { const uint32_t fp_spill_mask, const std::vector<uint8_t>& mapping_table, const std::vector<uint8_t>& vmap_table, - const std::vector<uint8_t>& native_gc_map); + const std::vector<uint8_t>& native_gc_map, + const std::vector<uint8_t>* cfi_info); // Constructs a CompiledMethod for the QuickJniCompiler. CompiledMethod(CompilerDriver& driver, @@ -157,6 +158,10 @@ class CompiledMethod : public CompiledCode { return *gc_map_; } + const std::vector<uint8_t>* GetCFIInfo() const { + return cfi_info_; + } + private: // For quick code, the size of the activation used by the code. const size_t frame_size_in_bytes_; @@ -172,6 +177,8 @@ class CompiledMethod : public CompiledCode { // For quick code, a map keyed by native PC indices to bitmaps describing what dalvik registers // are live. For portable code, the key is a dalvik PC. std::vector<uint8_t>* gc_map_; + // For quick code, a FDE entry for the debug_frame section. + std::vector<uint8_t>* cfi_info_; }; } // namespace art diff --git a/compiler/compiler_backend.cc b/compiler/compiler_backend.cc index f37ee37634..0afa665eb7 100644 --- a/compiler/compiler_backend.cc +++ b/compiler/compiler_backend.cc @@ -83,6 +83,9 @@ static CompiledMethod* TryCompileWithSeaIR(art::CompilerDriver& compiler, } +// Hack for CFI CIE initialization +extern std::vector<uint8_t>* X86CFIInitialization(); + class QuickBackend : public CompilerBackend { public: QuickBackend() : CompilerBackend(100) {} @@ -166,11 +169,27 @@ class QuickBackend : public CompilerBackend { bool set_max = cu->mir_graph->SetMaxAvailableNonSpecialCompilerTemps(max_temps); CHECK(set_max); } - return mir_to_lir;; + return mir_to_lir; } void InitCompilationUnit(CompilationUnit& cu) const {} + /* + * @brief Generate and return Dwarf CFI initialization, if supported by the + * backend. + * @param driver CompilerDriver for this compile. + * @returns nullptr if not supported by backend or a vector of bytes for CFI DWARF + * information. + * @note This is used for backtrace information in generated code. + */ + std::vector<uint8_t>* GetCallFrameInformationInitialization(const CompilerDriver& driver) const + OVERRIDE { + if (driver.GetInstructionSet() == kX86) { + return X86CFIInitialization(); + } + return nullptr; + } + private: DISALLOW_COPY_AND_ASSIGN(QuickBackend); }; diff --git a/compiler/compiler_backend.h b/compiler/compiler_backend.h index 51fb29afd2..b473806bba 100644 --- a/compiler/compiler_backend.h +++ b/compiler/compiler_backend.h @@ -93,6 +93,19 @@ class CompilerBackend { virtual ~CompilerBackend() {} + /* + * @brief Generate and return Dwarf CFI initialization, if supported by the + * backend. + * @param driver CompilerDriver for this compile. + * @returns nullptr if not supported by backend or a vector of bytes for CFI DWARF + * information. + * @note This is used for backtrace information in generated code. + */ + virtual std::vector<uint8_t>* GetCallFrameInformationInitialization(const CompilerDriver& driver) + const { + return nullptr; + } + private: const uint64_t maximum_compilation_time_before_warning_; diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index 31854496ab..b0b8d1ea15 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -1070,10 +1070,12 @@ CompiledMethod* Mir2Lir::GetCompiledMethod() { DCHECK_EQ(fp_vmap_table_.size(), 0u); vmap_encoder.PushBackUnsigned(0u); // Size is 0. } + + UniquePtr<std::vector<uint8_t> > cfi_info(ReturnCallFrameInformation()); CompiledMethod* result = new CompiledMethod(*cu_->compiler_driver, cu_->instruction_set, code_buffer_, frame_size_, core_spill_mask_, fp_spill_mask_, encoded_mapping_table_, - vmap_encoder.GetData(), native_gc_map_); + vmap_encoder.GetData(), native_gc_map_, cfi_info.get()); return result; } @@ -1216,4 +1218,9 @@ void Mir2Lir::LoadClassType(uint32_t type_idx, SpecialTargetRegister symbolic_re AppendLIR(load_pc_rel); } +std::vector<uint8_t>* Mir2Lir::ReturnCallFrameInformation() { + // Default case is to do nothing. + return nullptr; +} + } // namespace art diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index b74052c117..8f199f8c85 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -1089,6 +1089,11 @@ class Mir2Lir : public Backend { bool can_assume_type_is_in_dex_cache, uint32_t type_idx, RegLocation rl_dest, RegLocation rl_src); + /* + * @brief Generate the debug_frame FDE information if possible. + * @returns pointer to vector containg CFE information, or NULL. + */ + virtual std::vector<uint8_t>* ReturnCallFrameInformation(); /** * @brief Used to insert marker that can be used to associate MIR with LIR. diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc index 0613cdff7a..399001c397 100644 --- a/compiler/dex/quick/x86/call_x86.cc +++ b/compiler/dex/quick/x86/call_x86.cc @@ -198,7 +198,7 @@ void X86Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { LockTemp(rX86_ARG2); /* Build frame, return address already on stack */ - OpRegImm(kOpSub, rX86_SP, frame_size_ - 4); + stack_decrement_ = OpRegImm(kOpSub, rX86_SP, frame_size_ - 4); /* * We can safely skip the stack overflow check if we're @@ -246,7 +246,7 @@ void X86Mir2Lir::GenExitSequence() { NewLIR0(kPseudoMethodExit); UnSpillCoreRegs(); /* Remove frame except for return address */ - OpRegImm(kOpAdd, rX86_SP, frame_size_ - 4); + stack_increment_ = OpRegImm(kOpAdd, rX86_SP, frame_size_ - 4); NewLIR0(kX86Ret); } diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h index 421d51e4fd..c97d0e6f4d 100644 --- a/compiler/dex/quick/x86/codegen_x86.h +++ b/compiler/dex/quick/x86/codegen_x86.h @@ -302,6 +302,18 @@ class X86Mir2Lir : public Mir2Lir { */ void InstallLiteralPools(); + /* + * @brief Generate the debug_frame CFI information. + * @returns pointer to vector containing CFE information + */ + static std::vector<uint8_t>* ReturnCommonCallFrameInformation(); + + /* + * @brief Generate the debug_frame FDE information. + * @returns pointer to vector containing CFE information + */ + std::vector<uint8_t>* ReturnCallFrameInformation(); + private: void EmitPrefix(const X86EncodingMap* entry); void EmitOpcode(const X86EncodingMap* entry); @@ -549,6 +561,12 @@ class X86Mir2Lir : public Mir2Lir { // Instructions needing patching with PC relative code addresses. GrowableArray<LIR*> call_method_insns_; + + // Prologue decrement of stack pointer. + LIR* stack_decrement_; + + // Epilogue increment of stack pointer. + LIR* stack_increment_; }; } // namespace art diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc index eea7191c3b..7bb866d4aa 100644 --- a/compiler/dex/quick/x86/target_x86.cc +++ b/compiler/dex/quick/x86/target_x86.cc @@ -539,7 +539,8 @@ X86Mir2Lir::X86Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* : Mir2Lir(cu, mir_graph, arena), method_address_insns_(arena, 100, kGrowableArrayMisc), class_type_address_insns_(arena, 100, kGrowableArrayMisc), - call_method_insns_(arena, 100, kGrowableArrayMisc) { + call_method_insns_(arena, 100, kGrowableArrayMisc), + stack_decrement_(nullptr), stack_increment_(nullptr) { store_method_addr_used_ = false; for (int i = 0; i < kX86Last; i++) { if (X86Mir2Lir::EncodingMap[i].opcode != i) { @@ -1118,4 +1119,166 @@ bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) { return true; } +/* + * @brief Enter a 32 bit quantity into the FDE buffer + * @param buf FDE buffer. + * @param data Data value. + */ +static void PushWord(std::vector<uint8_t>&buf, int data) { + buf.push_back(data & 0xff); + buf.push_back((data >> 8) & 0xff); + buf.push_back((data >> 16) & 0xff); + buf.push_back((data >> 24) & 0xff); +} + +/* + * @brief Enter an 'advance LOC' into the FDE buffer + * @param buf FDE buffer. + * @param increment Amount by which to increase the current location. + */ +static void AdvanceLoc(std::vector<uint8_t>&buf, uint32_t increment) { + if (increment < 64) { + // Encoding in opcode. + buf.push_back(0x1 << 6 | increment); + } else if (increment < 256) { + // Single byte delta. + buf.push_back(0x02); + buf.push_back(increment); + } else if (increment < 256 * 256) { + // Two byte delta. + buf.push_back(0x03); + buf.push_back(increment & 0xff); + buf.push_back((increment >> 8) & 0xff); + } else { + // Four byte delta. + buf.push_back(0x04); + PushWord(buf, increment); + } +} + + +std::vector<uint8_t>* X86CFIInitialization() { + return X86Mir2Lir::ReturnCommonCallFrameInformation(); +} + +std::vector<uint8_t>* X86Mir2Lir::ReturnCommonCallFrameInformation() { + std::vector<uint8_t>*cfi_info = new std::vector<uint8_t>; + + // Length of the CIE (except for this field). + PushWord(*cfi_info, 16); + + // CIE id. + PushWord(*cfi_info, 0xFFFFFFFFU); + + // Version: 3. + cfi_info->push_back(0x03); + + // Augmentation: empty string. + cfi_info->push_back(0x0); + + // Code alignment: 1. + cfi_info->push_back(0x01); + + // Data alignment: -4. + cfi_info->push_back(0x7C); + + // Return address register (R8). + cfi_info->push_back(0x08); + + // Initial return PC is 4(ESP): DW_CFA_def_cfa R4 4. + cfi_info->push_back(0x0C); + cfi_info->push_back(0x04); + cfi_info->push_back(0x04); + + // Return address location: 0(SP): DW_CFA_offset R8 1 (* -4);. + cfi_info->push_back(0x2 << 6 | 0x08); + cfi_info->push_back(0x01); + + // And 2 Noops to align to 4 byte boundary. + cfi_info->push_back(0x0); + cfi_info->push_back(0x0); + + DCHECK_EQ(cfi_info->size() & 3, 0U); + return cfi_info; +} + +static void EncodeUnsignedLeb128(std::vector<uint8_t>& buf, uint32_t value) { + uint8_t buffer[12]; + uint8_t *ptr = EncodeUnsignedLeb128(buffer, value); + for (uint8_t *p = buffer; p < ptr; p++) { + buf.push_back(*p); + } +} + +std::vector<uint8_t>* X86Mir2Lir::ReturnCallFrameInformation() { + std::vector<uint8_t>*cfi_info = new std::vector<uint8_t>; + + // Generate the FDE for the method. + DCHECK_NE(data_offset_, 0U); + + // Length (will be filled in later in this routine). + PushWord(*cfi_info, 0); + + // CIE_pointer (can be filled in by linker); might be left at 0 if there is only + // one CIE for the whole debug_frame section. + PushWord(*cfi_info, 0); + + // 'initial_location' (filled in by linker). + PushWord(*cfi_info, 0); + + // 'address_range' (number of bytes in the method). + PushWord(*cfi_info, data_offset_); + + // The instructions in the FDE. + if (stack_decrement_ != nullptr) { + // Advance LOC to just past the stack decrement. + uint32_t pc = NEXT_LIR(stack_decrement_)->offset; + AdvanceLoc(*cfi_info, pc); + + // Now update the offset to the call frame: DW_CFA_def_cfa_offset frame_size. + cfi_info->push_back(0x0e); + EncodeUnsignedLeb128(*cfi_info, frame_size_); + + // We continue with that stack until the epilogue. + if (stack_increment_ != nullptr) { + uint32_t new_pc = NEXT_LIR(stack_increment_)->offset; + AdvanceLoc(*cfi_info, new_pc - pc); + + // We probably have code snippets after the epilogue, so save the + // current state: DW_CFA_remember_state. + cfi_info->push_back(0x0a); + + // We have now popped the stack: DW_CFA_def_cfa_offset 4. There is only the return + // PC on the stack now. + cfi_info->push_back(0x0e); + EncodeUnsignedLeb128(*cfi_info, 4); + + // Everything after that is the same as before the epilogue. + // Stack bump was followed by RET instruction. + LIR *post_ret_insn = NEXT_LIR(NEXT_LIR(stack_increment_)); + if (post_ret_insn != nullptr) { + pc = new_pc; + new_pc = post_ret_insn->offset; + AdvanceLoc(*cfi_info, new_pc - pc); + // Restore the state: DW_CFA_restore_state. + cfi_info->push_back(0x0b); + } + } + } + + // Padding to a multiple of 4 + while ((cfi_info->size() & 3) != 0) { + // DW_CFA_nop is encoded as 0. + cfi_info->push_back(0); + } + + // Set the length of the FDE inside the generated bytes. + uint32_t length = cfi_info->size() - 4; + (*cfi_info)[0] = length; + (*cfi_info)[1] = length >> 8; + (*cfi_info)[2] = length >> 16; + (*cfi_info)[3] = length >> 24; + return cfi_info; +} + } // namespace art diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 931055b6b8..708cce6437 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -31,6 +31,7 @@ #include "dex/verification_results.h" #include "dex/verified_method.h" #include "dex/quick/dex_file_method_inliner.h" +#include "driver/compiler_options.h" #include "jni_internal.h" #include "object_utils.h" #include "runtime.h" @@ -323,10 +324,12 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options, compiler_enable_auto_elf_loading_(NULL), compiler_get_method_code_addr_(NULL), support_boot_image_fixup_(instruction_set != kMips), + cfi_info_(nullptr), dedupe_code_("dedupe code"), dedupe_mapping_table_("dedupe mapping table"), dedupe_vmap_table_("dedupe vmap table"), - dedupe_gc_map_("dedupe gc map") { + dedupe_gc_map_("dedupe gc map"), + dedupe_cfi_info_("dedupe cfi info") { DCHECK(compiler_options_ != nullptr); DCHECK(verification_results_ != nullptr); DCHECK(method_inliner_map_ != nullptr); @@ -341,6 +344,11 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options, if (!image_) { CHECK(image_classes_.get() == NULL); } + + // Are we generating CFI information? + if (compiler_options->GetGenerateGDBInformation()) { + cfi_info_.reset(compiler_backend_->GetCallFrameInformationInitialization(*this)); + } } std::vector<uint8_t>* CompilerDriver::DeduplicateCode(const std::vector<uint8_t>& code) { @@ -359,6 +367,13 @@ std::vector<uint8_t>* CompilerDriver::DeduplicateGCMap(const std::vector<uint8_t return dedupe_gc_map_.Add(Thread::Current(), code); } +std::vector<uint8_t>* CompilerDriver::DeduplicateCFIInfo(const std::vector<uint8_t>* cfi_info) { + if (cfi_info == nullptr) { + return nullptr; + } + return dedupe_cfi_info_.Add(Thread::Current(), *cfi_info); +} + CompilerDriver::~CompilerDriver() { Thread* self = Thread::Current(); { diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 5a5fc36f21..6ccbf0fdeb 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -493,6 +493,15 @@ class CompilerDriver { std::vector<uint8_t>* DeduplicateMappingTable(const std::vector<uint8_t>& code); std::vector<uint8_t>* DeduplicateVMapTable(const std::vector<uint8_t>& code); std::vector<uint8_t>* DeduplicateGCMap(const std::vector<uint8_t>& code); + std::vector<uint8_t>* DeduplicateCFIInfo(const std::vector<uint8_t>* cfi_info); + + /* + * @brief return the pointer to the Call Frame Information. + * @return pointer to call frame information for this compilation. + */ + std::vector<uint8_t>* GetCallFrameInformation() const { + return cfi_info_.get(); + } private: // Compute constant code and method pointers when possible @@ -625,6 +634,9 @@ class CompilerDriver { bool support_boot_image_fixup_; + // Call Frame Information, which might be generated to help stack tracebacks. + UniquePtr<std::vector<uint8_t> > cfi_info_; + // DeDuplication data structures, these own the corresponding byte arrays. class DedupeHashFunc { public: @@ -663,6 +675,7 @@ class CompilerDriver { DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_mapping_table_; DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_vmap_table_; DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_gc_map_; + DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_cfi_info_; DISALLOW_COPY_AND_ASSIGN(CompilerDriver); }; diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index 9f6745b015..39738ab049 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -43,7 +43,8 @@ class CompilerOptions { large_method_threshold_(kDefaultLargeMethodThreshold), small_method_threshold_(kDefaultSmallMethodThreshold), tiny_method_threshold_(kDefaultTinyMethodThreshold), - num_dex_methods_threshold_(kDefaultNumDexMethodsThreshold) + num_dex_methods_threshold_(kDefaultNumDexMethodsThreshold), + generate_gdb_information_(false) #ifdef ART_SEA_IR_MODE , sea_ir_mode_(false) #endif @@ -54,7 +55,8 @@ class CompilerOptions { size_t large_method_threshold, size_t small_method_threshold, size_t tiny_method_threshold, - size_t num_dex_methods_threshold + size_t num_dex_methods_threshold, + bool generate_gdb_information #ifdef ART_SEA_IR_MODE , bool sea_ir_mode #endif @@ -64,7 +66,8 @@ class CompilerOptions { large_method_threshold_(large_method_threshold), small_method_threshold_(small_method_threshold), tiny_method_threshold_(tiny_method_threshold), - num_dex_methods_threshold_(num_dex_methods_threshold) + num_dex_methods_threshold_(num_dex_methods_threshold), + generate_gdb_information_(generate_gdb_information) #ifdef ART_SEA_IR_MODE , sea_ir_mode_(sea_ir_mode) #endif @@ -118,6 +121,10 @@ class CompilerOptions { bool GetSeaIrMode(); #endif + bool GetGenerateGDBInformation() const { + return generate_gdb_information_; + } + private: CompilerFilter compiler_filter_; size_t huge_method_threshold_; @@ -125,6 +132,7 @@ class CompilerOptions { size_t small_method_threshold_; size_t tiny_method_threshold_; size_t num_dex_methods_threshold_; + bool generate_gdb_information_; #ifdef ART_SEA_IR_MODE bool sea_ir_mode_; diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc index 4b416a0d4f..4b823ef5ec 100644 --- a/compiler/elf_writer_quick.cc +++ b/compiler/elf_writer_quick.cc @@ -98,6 +98,7 @@ bool ElfWriterQuick::Write(OatWriter* oat_writer, // | .rodata\0 | // | .text\0 | // | .shstrtab\0 | + // | .debug_frame\0 | // +-------------------------+ // | Elf32_Shdr NULL | // | Elf32_Shdr .dynsym | @@ -107,6 +108,9 @@ bool ElfWriterQuick::Write(OatWriter* oat_writer, // | Elf32_Shdr .rodata | // | Elf32_Shdr .dynamic | // | Elf32_Shdr .shstrtab | + // | Elf32_Shdr .debug_info | (Optional) + // | Elf32_Shdr .debug_abbrev| (Optional) + // | Elf32_Shdr .debug_frame | (Optional) // +-------------------------+ // phase 1: computing offsets @@ -259,6 +263,18 @@ bool ElfWriterQuick::Write(OatWriter* oat_writer, uint32_t shstrtab_shstrtab_offset = shstrtab.size(); shstrtab += ".shstrtab"; shstrtab += '\0'; + uint32_t shstrtab_debug_info_offset = shstrtab.size(); + shstrtab += ".debug_info"; + shstrtab += '\0'; + uint32_t shstrtab_debug_abbrev_offset = shstrtab.size(); + shstrtab += ".debug_abbrev"; + shstrtab += '\0'; + uint32_t shstrtab_debug_str_offset = shstrtab.size(); + shstrtab += ".debug_str"; + shstrtab += '\0'; + uint32_t shstrtab_debug_frame_offset = shstrtab.size(); + shstrtab += ".debug_frame"; + shstrtab += '\0'; uint32_t shstrtab_size = shstrtab.size(); expected_offset += shstrtab_size; if (debug) { @@ -266,6 +282,52 @@ bool ElfWriterQuick::Write(OatWriter* oat_writer, LOG(INFO) << "shstrtab_size=" << shstrtab_size << std::hex << " " << shstrtab_size; } + // Create debug informatin, if we have it. + bool generateDebugInformation = compiler_driver_->GetCallFrameInformation() != nullptr; + std::vector<uint8_t> dbg_info; + std::vector<uint8_t> dbg_abbrev; + std::vector<uint8_t> dbg_str; + if (generateDebugInformation) { + FillInCFIInformation(oat_writer, &dbg_info, &dbg_abbrev, &dbg_str); + } + + uint32_t shdbg_info_alignment = 1; + uint32_t shdbg_info_offset = expected_offset; + uint32_t shdbg_info_size = dbg_info.size(); + expected_offset += shdbg_info_size; + if (debug) { + LOG(INFO) << "shdbg_info_offset=" << shdbg_info_offset << std::hex << " " << shdbg_info_offset; + LOG(INFO) << "shdbg_info_size=" << shdbg_info_size << std::hex << " " << shdbg_info_size; + } + + uint32_t shdbg_abbrev_alignment = 1; + uint32_t shdbg_abbrev_offset = expected_offset; + uint32_t shdbg_abbrev_size = dbg_abbrev.size(); + expected_offset += shdbg_abbrev_size; + if (debug) { + LOG(INFO) << "shdbg_abbrev_offset=" << shdbg_abbrev_offset << std::hex << " " << shdbg_abbrev_offset; + LOG(INFO) << "shdbg_abbrev_size=" << shdbg_abbrev_size << std::hex << " " << shdbg_abbrev_size; + } + + uint32_t shdbg_frm_alignment = 4; + uint32_t shdbg_frm_offset = expected_offset = RoundUp(expected_offset, shdbg_frm_alignment); + uint32_t shdbg_frm_size = + generateDebugInformation ? compiler_driver_->GetCallFrameInformation()->size() : 0; + expected_offset += shdbg_frm_size; + if (debug) { + LOG(INFO) << "shdbg_frm_offset=" << shdbg_frm_offset << std::hex << " " << shdbg_frm_offset; + LOG(INFO) << "shdbg_frm_size=" << shdbg_frm_size << std::hex << " " << shdbg_frm_size; + } + + uint32_t shdbg_str_alignment = 1; + uint32_t shdbg_str_offset = expected_offset; + uint32_t shdbg_str_size = dbg_str.size(); + expected_offset += shdbg_str_size; + if (debug) { + LOG(INFO) << "shdbg_str_offset=" << shdbg_str_offset << std::hex << " " << shdbg_str_offset; + LOG(INFO) << "shdbg_str_size=" << shdbg_str_size << std::hex << " " << shdbg_str_size; + } + // section headers (after all sections) uint32_t shdr_alignment = sizeof(Elf32_Word); uint32_t shdr_offset = expected_offset = RoundUp(expected_offset, shdr_alignment); @@ -277,7 +339,11 @@ bool ElfWriterQuick::Write(OatWriter* oat_writer, const uint8_t SH_TEXT = 5; const uint8_t SH_DYNAMIC = 6; const uint8_t SH_SHSTRTAB = 7; - const uint8_t SH_NUM = 8; + const uint8_t SH_DBG_INFO = 8; + const uint8_t SH_DBG_ABRV = 9; + const uint8_t SH_DBG_FRM = 10; + const uint8_t SH_DBG_STR = 11; + const uint8_t SH_NUM = generateDebugInformation ? 12 : 8; uint32_t shdr_size = sizeof(Elf32_Shdr) * SH_NUM; expected_offset += shdr_size; if (debug) { @@ -554,6 +620,52 @@ bool ElfWriterQuick::Write(OatWriter* oat_writer, section_headers[SH_SHSTRTAB].sh_addralign = shstrtab_alignment; section_headers[SH_SHSTRTAB].sh_entsize = 0; + if (generateDebugInformation) { + section_headers[SH_DBG_INFO].sh_name = shstrtab_debug_info_offset; + section_headers[SH_DBG_INFO].sh_type = SHT_PROGBITS; + section_headers[SH_DBG_INFO].sh_flags = 0; + section_headers[SH_DBG_INFO].sh_addr = 0; + section_headers[SH_DBG_INFO].sh_offset = shdbg_info_offset; + section_headers[SH_DBG_INFO].sh_size = shdbg_info_size; + section_headers[SH_DBG_INFO].sh_link = 0; + section_headers[SH_DBG_INFO].sh_info = 0; + section_headers[SH_DBG_INFO].sh_addralign = shdbg_info_alignment; + section_headers[SH_DBG_INFO].sh_entsize = 0; + + section_headers[SH_DBG_ABRV].sh_name = shstrtab_debug_abbrev_offset; + section_headers[SH_DBG_ABRV].sh_type = SHT_PROGBITS; + section_headers[SH_DBG_ABRV].sh_flags = 0; + section_headers[SH_DBG_ABRV].sh_addr = 0; + section_headers[SH_DBG_ABRV].sh_offset = shdbg_abbrev_offset; + section_headers[SH_DBG_ABRV].sh_size = shdbg_abbrev_size; + section_headers[SH_DBG_ABRV].sh_link = 0; + section_headers[SH_DBG_ABRV].sh_info = 0; + section_headers[SH_DBG_ABRV].sh_addralign = shdbg_abbrev_alignment; + section_headers[SH_DBG_ABRV].sh_entsize = 0; + + section_headers[SH_DBG_FRM].sh_name = shstrtab_debug_frame_offset; + section_headers[SH_DBG_FRM].sh_type = SHT_PROGBITS; + section_headers[SH_DBG_FRM].sh_flags = 0; + section_headers[SH_DBG_FRM].sh_addr = 0; + section_headers[SH_DBG_FRM].sh_offset = shdbg_frm_offset; + section_headers[SH_DBG_FRM].sh_size = shdbg_frm_size; + section_headers[SH_DBG_FRM].sh_link = 0; + section_headers[SH_DBG_FRM].sh_info = 0; + section_headers[SH_DBG_FRM].sh_addralign = shdbg_frm_alignment; + section_headers[SH_DBG_FRM].sh_entsize = 0; + + section_headers[SH_DBG_STR].sh_name = shstrtab_debug_str_offset; + section_headers[SH_DBG_STR].sh_type = SHT_PROGBITS; + section_headers[SH_DBG_STR].sh_flags = 0; + section_headers[SH_DBG_STR].sh_addr = 0; + section_headers[SH_DBG_STR].sh_offset = shdbg_str_offset; + section_headers[SH_DBG_STR].sh_size = shdbg_str_size; + section_headers[SH_DBG_STR].sh_link = 0; + section_headers[SH_DBG_STR].sh_info = 0; + section_headers[SH_DBG_STR].sh_addralign = shdbg_str_alignment; + section_headers[SH_DBG_STR].sh_entsize = 0; + } + // phase 3: writing file // Elf32_Ehdr @@ -646,8 +758,62 @@ bool ElfWriterQuick::Write(OatWriter* oat_writer, return false; } + if (generateDebugInformation) { + // .debug_info + DCHECK_LE(shstrtab_offset + shstrtab_size, shdbg_info_offset); + if (static_cast<off_t>(shdbg_info_offset) != lseek(elf_file_->Fd(), shdbg_info_offset, SEEK_SET)) { + PLOG(ERROR) << "Failed to seek to .shdbg_info offset " << shdbg_info_offset + << " for " << elf_file_->GetPath(); + return false; + } + if (!elf_file_->WriteFully(&dbg_info[0], shdbg_info_size)) { + PLOG(ERROR) << "Failed to write .debug_info for " << elf_file_->GetPath(); + return false; + } + + // .debug_abbrev + DCHECK_LE(shdbg_info_offset + shdbg_info_size, shdbg_abbrev_offset); + if (static_cast<off_t>(shdbg_abbrev_offset) != lseek(elf_file_->Fd(), shdbg_abbrev_offset, SEEK_SET)) { + PLOG(ERROR) << "Failed to seek to .shdbg_abbrev offset " << shdbg_abbrev_offset + << " for " << elf_file_->GetPath(); + return false; + } + if (!elf_file_->WriteFully(&dbg_abbrev[0], shdbg_abbrev_size)) { + PLOG(ERROR) << "Failed to write .debug_abbrev for " << elf_file_->GetPath(); + return false; + } + + // .debug_frame + DCHECK_LE(shdbg_abbrev_offset + shdbg_abbrev_size, shdbg_frm_offset); + if (static_cast<off_t>(shdbg_frm_offset) != lseek(elf_file_->Fd(), shdbg_frm_offset, SEEK_SET)) { + PLOG(ERROR) << "Failed to seek to .shdbg_frm offset " << shdbg_frm_offset + << " for " << elf_file_->GetPath(); + return false; + } + if (!elf_file_->WriteFully(&((*compiler_driver_->GetCallFrameInformation())[0]), shdbg_frm_size)) { + PLOG(ERROR) << "Failed to write .debug_frame for " << elf_file_->GetPath(); + return false; + } + + // .debug_str + DCHECK_LE(shdbg_frm_offset + shdbg_frm_size, shdbg_str_offset); + if (static_cast<off_t>(shdbg_str_offset) != lseek(elf_file_->Fd(), shdbg_str_offset, SEEK_SET)) { + PLOG(ERROR) << "Failed to seek to .shdbg_str offset " << shdbg_str_offset + << " for " << elf_file_->GetPath(); + return false; + } + if (!elf_file_->WriteFully(&dbg_str[0], shdbg_str_size)) { + PLOG(ERROR) << "Failed to write .debug_frame for " << elf_file_->GetPath(); + return false; + } + } + // section headers (after all sections) - DCHECK_LE(shstrtab_offset + shstrtab_size, shdr_offset); + if (generateDebugInformation) { + DCHECK_LE(shdbg_str_offset + shdbg_str_size, shdr_offset); + } else { + DCHECK_LE(shstrtab_offset + shstrtab_size, shdr_offset); + } if (static_cast<off_t>(shdr_offset) != lseek(elf_file_->Fd(), shdr_offset, SEEK_SET)) { PLOG(ERROR) << "Failed to seek to ELF section headers offset " << shdr_offset << " for " << elf_file_->GetPath(); @@ -660,6 +826,164 @@ bool ElfWriterQuick::Write(OatWriter* oat_writer, VLOG(compiler) << "ELF file written successfully: " << elf_file_->GetPath(); return true; +} // NOLINT(readability/fn_size) + +static void UpdateWord(std::vector<uint8_t>*buf, int offset, int data) { + (*buf)[offset+0] = data; + (*buf)[offset+1] = data >> 8; + (*buf)[offset+2] = data >> 16; + (*buf)[offset+3] = data >> 24; +} + +static void PushWord(std::vector<uint8_t>*buf, int data) { + buf->push_back(data & 0xff); + buf->push_back((data >> 8) & 0xff); + buf->push_back((data >> 16) & 0xff); + buf->push_back((data >> 24) & 0xff); +} + +static void PushHalf(std::vector<uint8_t>*buf, int data) { + buf->push_back(data & 0xff); + buf->push_back((data >> 8) & 0xff); +} + +// DWARF constants needed to generate CFI information. +enum { + // Tag encodings. + DW_TAG_compile_unit = 0x11, + DW_TAG_subprogram = 0X2e, + + // Attribute encodings. + DW_AT_name = 0x03, + DW_AT_low_pc = 0x11, + DW_AT_high_pc = 0x12, + DW_AT_language = 0x13, + + // Constant encoding. + DW_CHILDREN_no = 0x00, + DW_CHILDREN_yes = 0x01, + + // Attribute form encodings. + DW_FORM_addr = 0x01, + DW_FORM_data1 = 0x0b, + DW_FORM_strp = 0x0e, + + // Language encoding. + DW_LANG_Java = 0x000b +}; + +void ElfWriterQuick::FillInCFIInformation(OatWriter* oat_writer, + std::vector<uint8_t>* dbg_info, + std::vector<uint8_t>* dbg_abbrev, + std::vector<uint8_t>* dbg_str) { + // Create the debug_abbrev section with boilerplate information. + // We only care about low_pc and high_pc right now for the compilation + // unit and methods. + + // Tag 1: Compilation unit: DW_TAG_compile_unit. + dbg_abbrev->push_back(1); + dbg_abbrev->push_back(DW_TAG_compile_unit); + + // There are children (the methods). + dbg_abbrev->push_back(DW_CHILDREN_yes); + + // DW_LANG_Java DW_FORM_data1. + dbg_abbrev->push_back(DW_AT_language); + dbg_abbrev->push_back(DW_FORM_data1); + + // DW_AT_low_pc DW_FORM_addr. + dbg_abbrev->push_back(DW_AT_low_pc); + dbg_abbrev->push_back(DW_FORM_addr); + + // DW_AT_high_pc DW_FORM_addr. + dbg_abbrev->push_back(DW_AT_high_pc); + dbg_abbrev->push_back(DW_FORM_addr); + + // End of DW_TAG_compile_unit. + PushHalf(dbg_abbrev, 0); + + // Tag 2: Compilation unit: DW_TAG_subprogram. + dbg_abbrev->push_back(2); + dbg_abbrev->push_back(DW_TAG_subprogram); + + // There are no children. + dbg_abbrev->push_back(DW_CHILDREN_no); + + // Name of the method. + dbg_abbrev->push_back(DW_AT_name); + dbg_abbrev->push_back(DW_FORM_strp); + + // DW_AT_low_pc DW_FORM_addr. + dbg_abbrev->push_back(DW_AT_low_pc); + dbg_abbrev->push_back(DW_FORM_addr); + + // DW_AT_high_pc DW_FORM_addr. + dbg_abbrev->push_back(DW_AT_high_pc); + dbg_abbrev->push_back(DW_FORM_addr); + + // End of DW_TAG_subprogram. + PushHalf(dbg_abbrev, 0); + + // Start the debug_info section with the header information + // 'unit_length' will be filled in later. + PushWord(dbg_info, 0); + + // 'version' - 3. + PushHalf(dbg_info, 3); + + // Offset into .debug_abbrev section (always 0). + PushWord(dbg_info, 0); + + // Address size: 4. + dbg_info->push_back(4); + + // Start the description for the compilation unit. + // This uses tag 1. + dbg_info->push_back(1); + + // The language is Java. + dbg_info->push_back(DW_LANG_Java); + + // Leave space for low_pc and high_pc. + int low_pc_offset = dbg_info->size(); + PushWord(dbg_info, 0); + PushWord(dbg_info, 0); + + // Walk through the information in the method table, and enter into dbg_info. + const std::vector<OatWriter::DebugInfo>& dbg = oat_writer->GetCFIMethodInfo(); + uint32_t low_pc = 0xFFFFFFFFU; + uint32_t high_pc = 0; + + for (uint32_t i = 0; i < dbg.size(); i++) { + const OatWriter::DebugInfo& info = dbg[i]; + if (info.low_pc_ < low_pc) { + low_pc = info.low_pc_; + } + if (info.high_pc_ > high_pc) { + high_pc = info.high_pc_; + } + + // Start a new TAG: subroutine (2). + dbg_info->push_back(2); + + // Enter the name into the string table (and NUL terminate). + uint32_t str_offset = dbg_str->size(); + dbg_str->insert(dbg_str->end(), info.method_name_.begin(), info.method_name_.end()); + dbg_str->push_back('\0'); + + // Enter name, low_pc, high_pc. + PushWord(dbg_info, str_offset); + PushWord(dbg_info, info.low_pc_); + PushWord(dbg_info, info.high_pc_); + } + + // One byte terminator + dbg_info->push_back(0); + + // We have now walked all the methods. Fill in lengths and low/high PCs. + UpdateWord(dbg_info, 0, dbg_info->size() - 4); + UpdateWord(dbg_info, low_pc_offset, low_pc); + UpdateWord(dbg_info, low_pc_offset + 4, high_pc); } } // namespace art diff --git a/compiler/elf_writer_quick.h b/compiler/elf_writer_quick.h index 25b0495c0a..dec75dc83f 100644 --- a/compiler/elf_writer_quick.h +++ b/compiler/elf_writer_quick.h @@ -45,6 +45,16 @@ class ElfWriterQuick FINAL : public ElfWriter { : ElfWriter(driver, elf_file) {} ~ElfWriterQuick() {} + /* + * @brief Generate the DWARF debug_info and debug_abbrev sections + * @param oat_writer The Oat file Writer. + * @param dbg_info Compilation unit information. + * @param dbg_abbrev Abbreviations used to generate dbg_info. + * @param dbg_str Debug strings. + */ + void FillInCFIInformation(OatWriter* oat_writer, std::vector<uint8_t>* dbg_info, + std::vector<uint8_t>* dbg_abbrev, std::vector<uint8_t>* dbg_str); + DISALLOW_IMPLICIT_CONSTRUCTORS(ElfWriterQuick); }; diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index 970d2e30f5..a400bdde6c 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -378,6 +378,27 @@ size_t OatWriter::InitOatCodeMethod(size_t offset, size_t oat_class_index, uint32_t thumb_offset = compiled_method->CodeDelta(); quick_code_offset = offset + sizeof(code_size) + thumb_offset; + std::vector<uint8_t>* cfi_info = compiler_driver_->GetCallFrameInformation(); + if (cfi_info != nullptr) { + // Copy in the FDE, if present + const std::vector<uint8_t>* fde = compiled_method->GetCFIInfo(); + if (fde != nullptr) { + // Copy the information into cfi_info and then fix the address in the new copy. + int cur_offset = cfi_info->size(); + cfi_info->insert(cfi_info->end(), fde->begin(), fde->end()); + + // Set the 'initial_location' field to address the start of the method. + uint32_t new_value = quick_code_offset - oat_header_->GetExecutableOffset(); + uint32_t offset_to_update = cur_offset + 2*sizeof(uint32_t); + (*cfi_info)[offset_to_update+0] = new_value; + (*cfi_info)[offset_to_update+1] = new_value >> 8; + (*cfi_info)[offset_to_update+2] = new_value >> 16; + (*cfi_info)[offset_to_update+3] = new_value >> 24; + method_info_.push_back(DebugInfo(PrettyMethod(class_def_method_index, dex_file, false), + new_value, new_value + code_size)); + } + } + // Deduplicate code arrays SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator code_iter = code_offsets_.find(quick_code); diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h index f12c84d233..3d4b48ae42 100644 --- a/compiler/oat_writer.h +++ b/compiler/oat_writer.h @@ -82,6 +82,19 @@ class OatWriter { ~OatWriter(); + struct DebugInfo { + DebugInfo(const std::string& method_name, uint32_t low_pc, uint32_t high_pc) + : method_name_(method_name), low_pc_(low_pc), high_pc_(high_pc) { + } + std::string method_name_; + uint32_t low_pc_; + uint32_t high_pc_; + }; + + const std::vector<DebugInfo>& GetCFIMethodInfo() const { + return method_info_; + } + private: size_t InitOatHeader(); size_t InitOatDexFiles(size_t offset); @@ -205,6 +218,8 @@ class OatWriter { DISALLOW_COPY_AND_ASSIGN(OatClass); }; + std::vector<DebugInfo> method_info_; + const CompilerDriver* const compiler_driver_; // note OatFile does not take ownership of the DexFiles diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 0e54021002..041a66b34e 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -760,6 +760,7 @@ static int dex2oat(int argc, char** argv) { bool dump_passes = false; bool dump_slow_timing = kIsDebugBuild; bool watch_dog_enabled = !kIsTargetBuild; + bool generate_gdb_information = kIsDebugBuild; for (int i = 0; i < argc; i++) { const StringPiece option(argv[i]); @@ -797,6 +798,10 @@ static int dex2oat(int argc, char** argv) { watch_dog_enabled = true; } else if (option == "--no-watch-dog") { watch_dog_enabled = false; + } else if (option == "--gen-gdb-info") { + generate_gdb_information = true; + } else if (option == "--no-gen-gdb-info") { + generate_gdb_information = false; } else if (option.starts_with("-j")) { const char* thread_count_str = option.substr(strlen("-j")).data(); if (!ParseInt(thread_count_str, &thread_count)) { @@ -1042,7 +1047,8 @@ static int dex2oat(int argc, char** argv) { large_method_threshold, small_method_threshold, tiny_method_threshold, - num_dex_methods_threshold + num_dex_methods_threshold, + generate_gdb_information #ifdef ART_SEA_IR_MODE , compiler_options.sea_ir_ = true; #endif diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc index 3a17e41487..c646b86524 100644 --- a/runtime/elf_file.cc +++ b/runtime/elf_file.cc @@ -22,6 +22,83 @@ namespace art { +// ------------------------------------------------------------------- +// Binary GDB JIT Interface as described in +// http://sourceware.org/gdb/onlinedocs/gdb/Declarations.html +extern "C" { + typedef enum { + JIT_NOACTION = 0, + JIT_REGISTER_FN, + JIT_UNREGISTER_FN + } JITAction; + + struct JITCodeEntry { + JITCodeEntry* next_; + JITCodeEntry* prev_; + const byte *symfile_addr_; + uint64_t symfile_size_; + }; + + struct JITDescriptor { + uint32_t version_; + uint32_t action_flag_; + JITCodeEntry* relevant_entry_; + JITCodeEntry* first_entry_; + }; + + // GDB will place breakpoint into this function. + // To prevent GCC from inlining or removing it we place noinline attribute + // and inline assembler statement inside. + void __attribute__((noinline)) __jit_debug_register_code() { + __asm__(""); + } + + // GDB will inspect contents of this descriptor. + // Static initialization is necessary to prevent GDB from seeing + // uninitialized descriptor. + JITDescriptor __jit_debug_descriptor = { 1, JIT_NOACTION, nullptr, nullptr }; +} + + +static JITCodeEntry* CreateCodeEntry(const byte *symfile_addr, + uintptr_t symfile_size) { + JITCodeEntry* entry = new JITCodeEntry; + entry->symfile_addr_ = symfile_addr; + entry->symfile_size_ = symfile_size; + entry->prev_ = nullptr; + + // TODO: Do we need a lock here? + entry->next_ = __jit_debug_descriptor.first_entry_; + if (entry->next_ != nullptr) { + entry->next_->prev_ = entry; + } + __jit_debug_descriptor.first_entry_ = entry; + __jit_debug_descriptor.relevant_entry_ = entry; + + __jit_debug_descriptor.action_flag_ = JIT_REGISTER_FN; + __jit_debug_register_code(); + return entry; +} + + +static void UnregisterCodeEntry(JITCodeEntry* entry) { + // TODO: Do we need a lock here? + if (entry->prev_ != nullptr) { + entry->prev_->next_ = entry->next_; + } else { + __jit_debug_descriptor.first_entry_ = entry->next_; + } + + if (entry->next_ != nullptr) { + entry->next_->prev_ = entry->prev_; + } + + __jit_debug_descriptor.relevant_entry_ = entry; + __jit_debug_descriptor.action_flag_ = JIT_UNREGISTER_FN; + __jit_debug_register_code(); + delete entry; +} + ElfFile::ElfFile(File* file, bool writable, bool program_header_only) : file_(file), writable_(writable), @@ -38,7 +115,9 @@ ElfFile::ElfFile(File* file, bool writable, bool program_header_only) dynstr_section_start_(NULL), hash_section_start_(NULL), symtab_symbol_table_(NULL), - dynsym_symbol_table_(NULL) { + dynsym_symbol_table_(NULL), + jit_elf_image_(NULL), + jit_gdb_entry_(NULL) { CHECK(file != NULL); } @@ -172,6 +251,10 @@ ElfFile::~ElfFile() { STLDeleteElements(&segments_); delete symtab_symbol_table_; delete dynsym_symbol_table_; + delete jit_elf_image_; + if (jit_gdb_entry_) { + UnregisterCodeEntry(jit_gdb_entry_); + } } bool ElfFile::SetMap(MemMap* map, std::string* error_msg) { @@ -830,6 +913,11 @@ bool ElfFile::Load(bool executable, std::string* error_msg) { } } + // Use GDB JIT support to do stack backtrace, etc. + if (executable) { + GdbJITSupport(); + } + return true; } @@ -843,4 +931,269 @@ bool ElfFile::ValidPointer(const byte* start) const { return false; } +static bool check_section_name(ElfFile& file, int section_num, const char *name) { + Elf32_Shdr& section_header = file.GetSectionHeader(section_num); + const char *section_name = file.GetString(SHT_SYMTAB, section_header.sh_name); + return strcmp(name, section_name) == 0; +} + +static void IncrementUint32(byte *p, uint32_t increment) { + uint32_t *u = reinterpret_cast<uint32_t *>(p); + *u += increment; +} + +static void RoundAndClear(byte *image, uint32_t& offset, int pwr2) { + uint32_t mask = pwr2 - 1; + while (offset & mask) { + image[offset++] = 0; + } +} + +// Simple macro to bump a point to a section header to the next one. +#define BUMP_SHENT(sp) \ + sp = reinterpret_cast<Elf32_Shdr *> (\ + reinterpret_cast<byte*>(sp) + elf_hdr.e_shentsize);\ + offset += elf_hdr.e_shentsize + +void ElfFile::GdbJITSupport() { + // We only get here if we only are mapping the program header. + DCHECK(program_header_only_); + + // Well, we need the whole file to do this. + std::string error_msg; + UniquePtr<ElfFile> ptr(Open(const_cast<File*>(file_), false, false, &error_msg)); + ElfFile& all = *ptr; + + // Do we have interesting sections? + // Is this an OAT file with interesting sections? + if (all.GetSectionHeaderNum() != kExpectedSectionsInOATFile) { + return; + } + if (!check_section_name(all, 8, ".debug_info") || + !check_section_name(all, 9, ".debug_abbrev") || + !check_section_name(all, 10, ".debug_frame") || + !check_section_name(all, 11, ".debug_str")) { + return; + } + + // Okay, we are good enough. Fake up an ELF image and tell GDB about it. + // We need some extra space for the debug and string sections, the ELF header, and the + // section header. + uint32_t needed_size = KB; + + for (Elf32_Word i = 1; i < all.GetSectionHeaderNum(); i++) { + Elf32_Shdr& section_header = all.GetSectionHeader(i); + if (section_header.sh_addr == 0 && section_header.sh_type != SHT_DYNSYM) { + // Debug section: we need it. + needed_size += section_header.sh_size; + } else if (section_header.sh_type == SHT_STRTAB && + strcmp(".shstrtab", + all.GetString(SHT_SYMTAB, section_header.sh_name)) == 0) { + // We also need the shared string table. + needed_size += section_header.sh_size; + + // We also need the extra strings .symtab\0.strtab\0 + needed_size += 16; + } + } + + // Start creating our image. + jit_elf_image_ = new byte[needed_size]; + + // Create the Elf Header by copying the old one + Elf32_Ehdr& elf_hdr = + *reinterpret_cast<Elf32_Ehdr*>(jit_elf_image_); + + elf_hdr = all.GetHeader(); + elf_hdr.e_entry = 0; + elf_hdr.e_phoff = 0; + elf_hdr.e_phnum = 0; + elf_hdr.e_phentsize = 0; + elf_hdr.e_type = ET_EXEC; + + uint32_t offset = sizeof(Elf32_Ehdr); + + // Copy the debug sections and string table. + uint32_t debug_offsets[kExpectedSectionsInOATFile]; + memset(debug_offsets, '\0', sizeof debug_offsets); + Elf32_Shdr *text_header = nullptr; + int extra_shstrtab_entries = -1; + int text_section_index = -1; + int section_index = 1; + for (Elf32_Word i = 1; i < kExpectedSectionsInOATFile; i++) { + Elf32_Shdr& section_header = all.GetSectionHeader(i); + // Round up to multiple of 4, ensuring zero fill. + RoundAndClear(jit_elf_image_, offset, 4); + if (section_header.sh_addr == 0 && section_header.sh_type != SHT_DYNSYM) { + // Debug section: we need it. Unfortunately, it wasn't mapped in. + debug_offsets[i] = offset; + // Read it from the file. + lseek(file_->Fd(), section_header.sh_offset, SEEK_SET); + read(file_->Fd(), jit_elf_image_ + offset, section_header.sh_size); + offset += section_header.sh_size; + section_index++; + offset += 16; + } else if (section_header.sh_type == SHT_STRTAB && + strcmp(".shstrtab", + all.GetString(SHT_SYMTAB, section_header.sh_name)) == 0) { + // We also need the shared string table. + debug_offsets[i] = offset; + // Read it from the file. + lseek(file_->Fd(), section_header.sh_offset, SEEK_SET); + read(file_->Fd(), jit_elf_image_ + offset, section_header.sh_size); + offset += section_header.sh_size; + // We also need the extra strings .symtab\0.strtab\0 + extra_shstrtab_entries = section_header.sh_size; + memcpy(jit_elf_image_+offset, ".symtab\0.strtab\0", 16); + offset += 16; + section_index++; + } else if (section_header.sh_flags & SHF_EXECINSTR) { + DCHECK(strcmp(".text", all.GetString(SHT_SYMTAB, + section_header.sh_name)) == 0); + text_header = §ion_header; + text_section_index = section_index++; + } + } + DCHECK(text_header != nullptr); + DCHECK_NE(extra_shstrtab_entries, -1); + + // We now need to update the addresses for debug_info and debug_frame to get to the + // correct offset within the .text section. + uint32_t text_start_addr = 0; + for (uint32_t i = 0; i < segments_.size(); i++) { + if (segments_[i]->GetProtect() & PROT_EXEC) { + // We found the .text section. + text_start_addr = reinterpret_cast<uint32_t>(segments_[i]->Begin()); + break; + } + } + DCHECK_NE(text_start_addr, 0U); + + byte *p = jit_elf_image_+debug_offsets[8]; + byte *end = p + all.GetSectionHeader(8).sh_size; + + // For debug_info; patch compilation using low_pc @ offset 13, high_pc at offset 17. + IncrementUint32(p + 13, text_start_addr); + IncrementUint32(p + 17, text_start_addr); + + // Now fix the low_pc, high_pc for each method address. + // First method starts at offset 0x15, each subsequent method is 1+3*4 bytes further. + for (p += 0x15; p < end; p += 1 /* attr# */ + 3 * sizeof(uint32_t) /* addresses */) { + IncrementUint32(p + 1 + sizeof(uint32_t), text_start_addr); + IncrementUint32(p + 1 + 2 * sizeof(uint32_t), text_start_addr); + } + + // Now we have to handle the debug_frame method start addresses + p = jit_elf_image_+debug_offsets[10]; + end = p + all.GetSectionHeader(10).sh_size; + + // Skip past the CIE. + p += *reinterpret_cast<uint32_t *>(p) + 4; + + // And walk the FDEs. + for (; p < end; p += *reinterpret_cast<uint32_t *>(p) + sizeof(uint32_t)) { + IncrementUint32(p + 2 * sizeof(uint32_t), text_start_addr); + } + + // Create the data for the symbol table. + const int kSymbtabAlignment = 16; + RoundAndClear(jit_elf_image_, offset, kSymbtabAlignment); + uint32_t symtab_offset = offset; + + // First entry is empty. + memset(jit_elf_image_+offset, 0, sizeof(Elf32_Sym)); + offset += sizeof(Elf32_Sym); + + // Symbol 1 is the real .text section. + Elf32_Sym& sym_ent = *reinterpret_cast<Elf32_Sym*>(jit_elf_image_+offset); + sym_ent.st_name = 1; /* .text */ + sym_ent.st_value = text_start_addr; + sym_ent.st_size = text_header->sh_size; + SetBindingAndType(&sym_ent, STB_LOCAL, STT_SECTION); + sym_ent.st_other = 0; + sym_ent.st_shndx = text_section_index; + offset += sizeof(Elf32_Sym); + + // Create the data for the string table. + RoundAndClear(jit_elf_image_, offset, kSymbtabAlignment); + const int kTextStringSize = 7; + uint32_t strtab_offset = offset; + memcpy(jit_elf_image_+offset, "\0.text", kTextStringSize); + offset += kTextStringSize; + + // Create the section header table. + // Round up to multiple of kSymbtabAlignment, ensuring zero fill. + RoundAndClear(jit_elf_image_, offset, kSymbtabAlignment); + elf_hdr.e_shoff = offset; + Elf32_Shdr *sp = + reinterpret_cast<Elf32_Shdr *>(jit_elf_image_ + offset); + + // Copy the first empty index. + *sp = all.GetSectionHeader(0); + BUMP_SHENT(sp); + + elf_hdr.e_shnum = 1; + for (Elf32_Word i = 1; i < kExpectedSectionsInOATFile; i++) { + Elf32_Shdr& section_header = all.GetSectionHeader(i); + if (section_header.sh_addr == 0 && section_header.sh_type != SHT_DYNSYM) { + // Debug section: we need it. + *sp = section_header; + sp->sh_offset = debug_offsets[i]; + sp->sh_addr = 0; + elf_hdr.e_shnum++; + BUMP_SHENT(sp); + } else if (section_header.sh_type == SHT_STRTAB && + strcmp(".shstrtab", + all.GetString(SHT_SYMTAB, section_header.sh_name)) == 0) { + // We also need the shared string table. + *sp = section_header; + sp->sh_offset = debug_offsets[i]; + sp->sh_size += 16; /* sizeof ".symtab\0.strtab\0" */ + sp->sh_addr = 0; + elf_hdr.e_shstrndx = elf_hdr.e_shnum; + elf_hdr.e_shnum++; + BUMP_SHENT(sp); + } + } + + // Add a .text section for the matching code section. + *sp = *text_header; + sp->sh_type = SHT_NOBITS; + sp->sh_offset = 0; + sp->sh_addr = text_start_addr; + elf_hdr.e_shnum++; + BUMP_SHENT(sp); + + // .symtab section: Need an empty index and the .text entry + sp->sh_name = extra_shstrtab_entries; + sp->sh_type = SHT_SYMTAB; + sp->sh_flags = 0; + sp->sh_addr = 0; + sp->sh_offset = symtab_offset; + sp->sh_size = 2 * sizeof(Elf32_Sym); + sp->sh_link = elf_hdr.e_shnum + 1; // Link to .strtab section. + sp->sh_info = 0; + sp->sh_addralign = 16; + sp->sh_entsize = sizeof(Elf32_Sym); + elf_hdr.e_shnum++; + BUMP_SHENT(sp); + + // .strtab section: Enough for .text\0. + sp->sh_name = extra_shstrtab_entries + 8; + sp->sh_type = SHT_STRTAB; + sp->sh_flags = 0; + sp->sh_addr = 0; + sp->sh_offset = strtab_offset; + sp->sh_size = kTextStringSize; + sp->sh_link = 0; + sp->sh_info = 0; + sp->sh_addralign = 16; + sp->sh_entsize = 0; + elf_hdr.e_shnum++; + BUMP_SHENT(sp); + + // We now have enough information to tell GDB about our file. + jit_gdb_entry_ = CreateCodeEntry(jit_elf_image_, offset); +} + } // namespace art diff --git a/runtime/elf_file.h b/runtime/elf_file.h index 8a0a5f84c4..d2a044e9cf 100644 --- a/runtime/elf_file.h +++ b/runtime/elf_file.h @@ -29,6 +29,12 @@ namespace art { +// Interface to GDB JIT for backtrace information. +extern "C" { + struct JITCodeEntry; +} + + // Used for compile time and runtime for ElfFile access. Because of // the need for use at runtime, cannot directly use LLVM classes such as // ELFObjectFile. @@ -171,6 +177,13 @@ class ElfFile { SymbolTable* symtab_symbol_table_; SymbolTable* dynsym_symbol_table_; + + // Support for GDB JIT + byte* jit_elf_image_; + JITCodeEntry* jit_gdb_entry_; + void GdbJITSupport(); + // Is this an OAT file with debug information in it? + static constexpr uint32_t kExpectedSectionsInOATFile = 12; }; } // namespace art |