From 35e1e6ad4b50f1adbe9f93fe467766f042491896 Mon Sep 17 00:00:00 2001 From: Tong Shen Date: Wed, 30 Jul 2014 09:31:22 -0700 Subject: [PATCH] 1. Fix CFI for quick compiled code in x86 & x86_64; 2. Emit CFI in .eh_frame instead of .debug_frame. With CFI, we can correctly unwind past quick generated code. Now gdb should unwind to main() for both x86 & x86_64 host-side ART. Note that it does not work with relocation yet. Testing: 1. art/test/run-test --host --gdb [--64] --no-relocate 005 2. In gdb, run 'b art_quick_invoke_stub', then 'r', then 'c' a few times 3. In gdb, run 'bt'. You should see stack frames down to main() Change-Id: I5350d4097dc3d360a60cb17c94f1d02b99bc58bb --- compiler/compilers.cc | 6 +- compiler/dex/quick/x86/codegen_x86.h | 2 +- compiler/dex/quick/x86/target_x86.cc | 165 ++++++++++++++++++++++++------- compiler/elf_writer_quick.cc | 12 +-- compiler/oat_writer.cc | 10 +- runtime/arch/x86/quick_entrypoints_x86.S | 3 +- runtime/elf_file.cc | 28 ++---- 7 files changed, 159 insertions(+), 67 deletions(-) diff --git a/compiler/compilers.cc b/compiler/compilers.cc index bac1f1282..250924ad3 100644 --- a/compiler/compilers.cc +++ b/compiler/compilers.cc @@ -39,7 +39,7 @@ extern "C" art::CompiledMethod* ArtQuickJniCompileMethod(art::CompilerDriver* dr const art::DexFile& dex_file); // Hack for CFI CIE initialization -extern std::vector* X86CFIInitialization(); +extern std::vector* X86CFIInitialization(bool is_x86_64); void QuickCompiler::Init() const { ArtInitQuickCompilerContext(GetCompilerDriver()); @@ -129,10 +129,10 @@ Backend* QuickCompiler::GetCodeGenerator(CompilationUnit* cu, void* compilation_ std::vector* QuickCompiler::GetCallFrameInformationInitialization( const CompilerDriver& driver) const { if (driver.GetInstructionSet() == kX86) { - return X86CFIInitialization(); + return X86CFIInitialization(false); } if (driver.GetInstructionSet() == kX86_64) { - return X86CFIInitialization(); + return X86CFIInitialization(true); } return nullptr; } diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h index 3bc79ad41..0a46f2e54 100644 --- a/compiler/dex/quick/x86/codegen_x86.h +++ b/compiler/dex/quick/x86/codegen_x86.h @@ -394,7 +394,7 @@ class X86Mir2Lir : public Mir2Lir { * @brief Generate the debug_frame CFI information. * @returns pointer to vector containing CFE information */ - static std::vector* ReturnCommonCallFrameInformation(); + static std::vector* ReturnCommonCallFrameInformation(bool is_x86_64); /* * @brief Generate the debug_frame FDE information. diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc index 31110251c..3fa4b6ea4 100755 --- a/compiler/dex/quick/x86/target_x86.cc +++ b/compiler/dex/quick/x86/target_x86.cc @@ -1443,56 +1443,123 @@ static void AdvanceLoc(std::vector&buf, uint32_t increment) { } -std::vector* X86CFIInitialization() { - return X86Mir2Lir::ReturnCommonCallFrameInformation(); +std::vector* X86CFIInitialization(bool is_x86_64) { + return X86Mir2Lir::ReturnCommonCallFrameInformation(is_x86_64); } -std::vector* X86Mir2Lir::ReturnCommonCallFrameInformation() { +static void EncodeUnsignedLeb128(std::vector& 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); + } +} + +static void EncodeSignedLeb128(std::vector& buf, int32_t value) { + uint8_t buffer[12]; + uint8_t *ptr = EncodeSignedLeb128(buffer, value); + for (uint8_t *p = buffer; p < ptr; p++) { + buf.push_back(*p); + } +} + +std::vector* X86Mir2Lir::ReturnCommonCallFrameInformation(bool is_x86_64) { std::vector*cfi_info = new std::vector; - // Length of the CIE (except for this field). - PushWord(*cfi_info, 16); + // Length (will be filled in later in this routine). + PushWord(*cfi_info, 0); - // CIE id. - PushWord(*cfi_info, 0xFFFFFFFFU); + // CIE id: always 0. + PushWord(*cfi_info, 0); - // Version: 3. - cfi_info->push_back(0x03); + // Version: always 1. + cfi_info->push_back(0x01); - // Augmentation: empty string. + // Augmentation: 'zR\0' + cfi_info->push_back(0x7a); + cfi_info->push_back(0x52); cfi_info->push_back(0x0); // Code alignment: 1. - cfi_info->push_back(0x01); + EncodeUnsignedLeb128(*cfi_info, 1); - // Data alignment: -4. - cfi_info->push_back(0x7C); + // Data alignment. + if (is_x86_64) { + EncodeSignedLeb128(*cfi_info, -8); + } else { + EncodeSignedLeb128(*cfi_info, -4); + } - // Return address register (R8). - cfi_info->push_back(0x08); + // Return address register. + if (is_x86_64) { + // R16(RIP) + cfi_info->push_back(0x10); + } else { + // R8(EIP) + 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); + // Augmentation length: 1. + cfi_info->push_back(1); - // Return address location: 0(SP): DW_CFA_offset R8 1 (* -4);. - cfi_info->push_back(0x2 << 6 | 0x08); - cfi_info->push_back(0x01); + // Augmentation data: 0x03 ((DW_EH_PE_absptr << 4) | DW_EH_PE_udata4). + cfi_info->push_back(0x03); - // And 2 Noops to align to 4 byte boundary. - cfi_info->push_back(0x0); - cfi_info->push_back(0x0); + // Initial instructions. + if (is_x86_64) { + // DW_CFA_def_cfa R7(RSP) 8. + cfi_info->push_back(0x0c); + cfi_info->push_back(0x07); + cfi_info->push_back(0x08); - DCHECK_EQ(cfi_info->size() & 3, 0U); + // DW_CFA_offset R16(RIP) 1 (* -8). + cfi_info->push_back(0x90); + cfi_info->push_back(0x01); + } else { + // DW_CFA_def_cfa R4(ESP) 4. + cfi_info->push_back(0x0c); + cfi_info->push_back(0x04); + cfi_info->push_back(0x04); + + // DW_CFA_offset R8(EIP) 1 (* -4). + cfi_info->push_back(0x88); + cfi_info->push_back(0x01); + } + + // 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 CIE 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; } -static void EncodeUnsignedLeb128(std::vector& 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); +static bool ARTRegIDToDWARFRegID(bool is_x86_64, int art_reg_id, int* dwarf_reg_id) { + if (is_x86_64) { + switch (art_reg_id) { + case 3 : *dwarf_reg_id = 3; return true; // %rbx + // This is the only discrepancy between ART & DWARF register numbering. + case 5 : *dwarf_reg_id = 6; return true; // %rbp + case 12: *dwarf_reg_id = 12; return true; // %r12 + case 13: *dwarf_reg_id = 13; return true; // %r13 + case 14: *dwarf_reg_id = 14; return true; // %r14 + case 15: *dwarf_reg_id = 15; return true; // %r15 + default: return false; // Should not get here + } + } else { + switch (art_reg_id) { + case 5: *dwarf_reg_id = 5; return true; // %ebp + case 6: *dwarf_reg_id = 6; return true; // %esi + case 7: *dwarf_reg_id = 7; return true; // %edi + default: return false; // Should not get here + } } } @@ -1505,8 +1572,7 @@ std::vector* X86Mir2Lir::ReturnCallFrameInformation() { // 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. + // 'CIE_pointer' (filled in by linker). PushWord(*cfi_info, 0); // 'initial_location' (filled in by linker). @@ -1515,6 +1581,9 @@ std::vector* X86Mir2Lir::ReturnCallFrameInformation() { // 'address_range' (number of bytes in the method). PushWord(*cfi_info, data_offset_); + // Augmentation length: 0 + cfi_info->push_back(0); + // The instructions in the FDE. if (stack_decrement_ != nullptr) { // Advance LOC to just past the stack decrement. @@ -1525,6 +1594,30 @@ std::vector* X86Mir2Lir::ReturnCallFrameInformation() { cfi_info->push_back(0x0e); EncodeUnsignedLeb128(*cfi_info, frame_size_); + // Handle register spills + const uint32_t kSpillInstLen = (cu_->target64) ? 5 : 4; + const int kDataAlignmentFactor = (cu_->target64) ? -8 : -4; + uint32_t mask = core_spill_mask_ & ~(1 << rs_rRET.GetRegNum()); + int offset = -(GetInstructionSetPointerSize(cu_->instruction_set) * num_core_spills_); + for (int reg = 0; mask; mask >>= 1, reg++) { + if (mask & 0x1) { + pc += kSpillInstLen; + + // Advance LOC to pass this instruction + AdvanceLoc(*cfi_info, kSpillInstLen); + + int dwarf_reg_id; + if (ARTRegIDToDWARFRegID(cu_->target64, reg, &dwarf_reg_id)) { + // DW_CFA_offset_extended_sf reg_no offset + cfi_info->push_back(0x11); + EncodeUnsignedLeb128(*cfi_info, dwarf_reg_id); + EncodeSignedLeb128(*cfi_info, offset / kDataAlignmentFactor); + } + + offset += GetInstructionSetPointerSize(cu_->instruction_set); + } + } + // We continue with that stack until the epilogue. if (stack_increment_ != nullptr) { uint32_t new_pc = NEXT_LIR(stack_increment_)->offset; @@ -1534,10 +1627,10 @@ std::vector* X86Mir2Lir::ReturnCallFrameInformation() { // 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. + // We have now popped the stack: DW_CFA_def_cfa_offset 4/8. + // There is only the return PC on the stack now. cfi_info->push_back(0x0e); - EncodeUnsignedLeb128(*cfi_info, 4); + EncodeUnsignedLeb128(*cfi_info, GetInstructionSetPointerSize(cu_->instruction_set)); // Everything after that is the same as before the epilogue. // Stack bump was followed by RET instruction. diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc index 42743862f..1fde12eca 100644 --- a/compiler/elf_writer_quick.cc +++ b/compiler/elf_writer_quick.cc @@ -106,14 +106,14 @@ bool ElfWriterQuick::ElfBuilder::Write() { // | .strtab\0 | (Optional) // | .debug_str\0 | (Optional) // | .debug_info\0 | (Optional) - // | .debug_frame\0 | (Optional) + // | .eh_frame\0 | (Optional) // | .debug_abbrev\0 | (Optional) // +-------------------------+ (Optional) // | .debug_str | (Optional) // +-------------------------+ (Optional) // | .debug_info | (Optional) // +-------------------------+ (Optional) - // | .debug_frame | (Optional) + // | .eh_frame | (Optional) // +-------------------------+ (Optional) // | .debug_abbrev | (Optional) // +-------------------------+ @@ -127,7 +127,7 @@ bool ElfWriterQuick::ElfBuilder::Write() { // | Elf32_Shdr .shstrtab | // | Elf32_Shdr .debug_str | (Optional) // | Elf32_Shdr .debug_info | (Optional) - // | Elf32_Shdr .debug_frame | (Optional) + // | Elf32_Shdr .eh_frame | (Optional) // | Elf32_Shdr .debug_abbrev| (Optional) // +-------------------------+ @@ -844,14 +844,14 @@ bool ElfWriterQuick::Write(OatWriter* oat_writer, ElfRawSectionBuilder debug_info(".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0); ElfRawSectionBuilder debug_abbrev(".debug_abbrev", SHT_PROGBITS, 0, nullptr, 0, 1, 0); ElfRawSectionBuilder debug_str(".debug_str", SHT_PROGBITS, 0, nullptr, 0, 1, 0); - ElfRawSectionBuilder debug_frame(".debug_frame", SHT_PROGBITS, 0, nullptr, 0, 4, 0); - debug_frame.SetBuffer(*compiler_driver_->GetCallFrameInformation()); + ElfRawSectionBuilder eh_frame(".eh_frame", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, 4, 0); + eh_frame.SetBuffer(*compiler_driver_->GetCallFrameInformation()); FillInCFIInformation(oat_writer, debug_info.GetBuffer(), debug_abbrev.GetBuffer(), debug_str.GetBuffer()); builder.RegisterRawSection(debug_info); builder.RegisterRawSection(debug_abbrev); - builder.RegisterRawSection(debug_frame); + builder.RegisterRawSection(eh_frame); builder.RegisterRawSection(debug_str); } diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index 22f36f4f9..9da59ab7f 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -410,8 +410,16 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { int cur_offset = cfi_info->size(); cfi_info->insert(cfi_info->end(), fde->begin(), fde->end()); + // Set the 'CIE_pointer' field to cur_offset+4. + uint32_t CIE_pointer = cur_offset + 4; + uint32_t offset_to_update = cur_offset + sizeof(uint32_t); + (*cfi_info)[offset_to_update+0] = CIE_pointer; + (*cfi_info)[offset_to_update+1] = CIE_pointer >> 8; + (*cfi_info)[offset_to_update+2] = CIE_pointer >> 16; + (*cfi_info)[offset_to_update+3] = CIE_pointer >> 24; + // Set the 'initial_location' field to address the start of the method. - uint32_t offset_to_update = cur_offset + 2*sizeof(uint32_t); + offset_to_update = cur_offset + 2*sizeof(uint32_t); (*cfi_info)[offset_to_update+0] = quick_code_start; (*cfi_info)[offset_to_update+1] = quick_code_start >> 8; (*cfi_info)[offset_to_update+2] = quick_code_start >> 16; diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 15221294a..6d74b837d 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -1113,7 +1113,8 @@ END_FUNCTION art_quick_imt_conflict_trampoline DEFINE_FUNCTION art_quick_resolution_trampoline SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME - PUSH esp // pass SP + movl %esp, %edi + PUSH EDI // pass SP. do not just PUSH ESP; that messes up unwinding pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() CFI_ADJUST_CFA_OFFSET(4) PUSH ecx // pass receiver diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc index e5402e1c6..594c65f6e 100644 --- a/runtime/elf_file.cc +++ b/runtime/elf_file.cc @@ -1033,18 +1033,13 @@ static FDE* NextFDE(FDE* frame) { } static bool IsFDE(FDE* frame) { - // TODO This seems to be the constant everyone uses (for the .debug_frame - // section at least), however we should investigate this further. - const uint32_t kDwarfCIE_id = 0xffffffff; - const uint32_t kReservedLengths[] = {0xffffffff, 0xfffffff0}; - return frame->CIE_pointer != kDwarfCIE_id && - frame->raw_length_ != kReservedLengths[0] && frame->raw_length_ != kReservedLengths[1]; + return frame->CIE_pointer != 0; } // TODO This only works for 32-bit Elf Files. -static bool FixupDebugFrame(uintptr_t text_start, byte* dbg_frame, size_t dbg_frame_size) { - FDE* last_frame = reinterpret_cast(dbg_frame + dbg_frame_size); - FDE* frame = NextFDE(reinterpret_cast(dbg_frame)); +static bool FixupEHFrame(uintptr_t text_start, byte* eh_frame, size_t eh_frame_size) { + FDE* last_frame = reinterpret_cast(eh_frame + eh_frame_size); + FDE* frame = NextFDE(reinterpret_cast(eh_frame)); for (; frame < last_frame; frame = NextFDE(frame)) { if (!IsFDE(frame)) { return false; @@ -1301,7 +1296,7 @@ static bool FixupDebugInfo(uint32_t text_start, DebugInfoIterator* iter) { static bool FixupDebugSections(const byte* dbg_abbrev, size_t dbg_abbrev_size, uintptr_t text_start, byte* dbg_info, size_t dbg_info_size, - byte* dbg_frame, size_t dbg_frame_size) { + byte* eh_frame, size_t eh_frame_size) { std::unique_ptr abbrev(DebugAbbrev::Create(dbg_abbrev, dbg_abbrev_size)); if (abbrev.get() == nullptr) { return false; @@ -1313,7 +1308,7 @@ static bool FixupDebugSections(const byte* dbg_abbrev, size_t dbg_abbrev_size, return false; } return FixupDebugInfo(text_start, iter.get()) - && FixupDebugFrame(text_start, dbg_frame, dbg_frame_size); + && FixupEHFrame(text_start, eh_frame, eh_frame_size); } void ElfFile::GdbJITSupport() { @@ -1334,20 +1329,15 @@ void ElfFile::GdbJITSupport() { // Do we have interesting sections? const Elf32_Shdr* debug_info = all.FindSectionByName(".debug_info"); const Elf32_Shdr* debug_abbrev = all.FindSectionByName(".debug_abbrev"); - const Elf32_Shdr* debug_frame = all.FindSectionByName(".debug_frame"); + const Elf32_Shdr* eh_frame = all.FindSectionByName(".eh_frame"); const Elf32_Shdr* debug_str = all.FindSectionByName(".debug_str"); const Elf32_Shdr* strtab_sec = all.FindSectionByName(".strtab"); const Elf32_Shdr* symtab_sec = all.FindSectionByName(".symtab"); Elf32_Shdr* text_sec = all.FindSectionByName(".text"); - if (debug_info == nullptr || debug_abbrev == nullptr || debug_frame == nullptr || + if (debug_info == nullptr || debug_abbrev == nullptr || eh_frame == nullptr || debug_str == nullptr || text_sec == nullptr || strtab_sec == nullptr || symtab_sec == nullptr) { return; } -#ifdef __LP64__ - if (true) { - return; // No ELF debug support in 64bit. - } -#endif // We need to add in a strtab and symtab to the image. // all is MAP_PRIVATE so it can be written to freely. // We also already have strtab and symtab so we are fine there. @@ -1364,7 +1354,7 @@ void ElfFile::GdbJITSupport() { if (!FixupDebugSections( all.Begin() + debug_abbrev->sh_offset, debug_abbrev->sh_size, text_sec->sh_addr, all.Begin() + debug_info->sh_offset, debug_info->sh_size, - all.Begin() + debug_frame->sh_offset, debug_frame->sh_size)) { + all.Begin() + eh_frame->sh_offset, eh_frame->sh_size)) { LOG(ERROR) << "Failed to load GDB data"; return; } -- 2.11.0