summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTong Shen <endlessroad@google.com>2014-07-30 09:31:22 -0700
committerTong Shen <endlessroad@google.com>2014-07-30 20:19:26 +0000
commit35e1e6ad4b50f1adbe9f93fe467766f042491896 (patch)
tree054e8fe1140fc1144ececa8ad8beda18be9566a2
parent01a0ebb6b96c32879dcad99d515fc0cd8de2a266 (diff)
downloadart-35e1e6ad4b50f1adbe9f93fe467766f042491896.zip
art-35e1e6ad4b50f1adbe9f93fe467766f042491896.tar.gz
art-35e1e6ad4b50f1adbe9f93fe467766f042491896.tar.bz2
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
-rw-r--r--compiler/compilers.cc6
-rw-r--r--compiler/dex/quick/x86/codegen_x86.h2
-rwxr-xr-xcompiler/dex/quick/x86/target_x86.cc165
-rw-r--r--compiler/elf_writer_quick.cc12
-rw-r--r--compiler/oat_writer.cc10
-rw-r--r--runtime/arch/x86/quick_entrypoints_x86.S3
-rw-r--r--runtime/elf_file.cc28
7 files changed, 159 insertions, 67 deletions
diff --git a/compiler/compilers.cc b/compiler/compilers.cc
index bac1f12..250924a 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<uint8_t>* X86CFIInitialization();
+extern std::vector<uint8_t>* X86CFIInitialization(bool is_x86_64);
void QuickCompiler::Init() const {
ArtInitQuickCompilerContext(GetCompilerDriver());
@@ -129,10 +129,10 @@ Backend* QuickCompiler::GetCodeGenerator(CompilationUnit* cu, void* compilation_
std::vector<uint8_t>* 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 3bc79ad..0a46f2e 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<uint8_t>* ReturnCommonCallFrameInformation();
+ static std::vector<uint8_t>* 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 3111025..3fa4b6e 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<uint8_t>&buf, uint32_t increment) {
}
-std::vector<uint8_t>* X86CFIInitialization() {
- return X86Mir2Lir::ReturnCommonCallFrameInformation();
+std::vector<uint8_t>* X86CFIInitialization(bool is_x86_64) {
+ return X86Mir2Lir::ReturnCommonCallFrameInformation(is_x86_64);
}
-std::vector<uint8_t>* X86Mir2Lir::ReturnCommonCallFrameInformation() {
+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);
+ }
+}
+
+static void EncodeSignedLeb128(std::vector<uint8_t>& 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<uint8_t>* X86Mir2Lir::ReturnCommonCallFrameInformation(bool is_x86_64) {
std::vector<uint8_t>*cfi_info = new std::vector<uint8_t>;
- // 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<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);
+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<uint8_t>* 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<uint8_t>* 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<uint8_t>* 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<uint8_t>* 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 4274386..1fde12e 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 22f36f4..9da59ab 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 1522129..6d74b83 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 e5402e1..594c65f 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<FDE*>(dbg_frame + dbg_frame_size);
- FDE* frame = NextFDE(reinterpret_cast<FDE*>(dbg_frame));
+static bool FixupEHFrame(uintptr_t text_start, byte* eh_frame, size_t eh_frame_size) {
+ FDE* last_frame = reinterpret_cast<FDE*>(eh_frame + eh_frame_size);
+ FDE* frame = NextFDE(reinterpret_cast<FDE*>(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<DebugAbbrev> 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;
}