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
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 @@
* @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,49 +1443,8 @@
}
-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;
+std::vector<uint8_t>* X86CFIInitialization(bool is_x86_64) {
+ return X86Mir2Lir::ReturnCommonCallFrameInformation(is_x86_64);
}
static void EncodeUnsignedLeb128(std::vector<uint8_t>& buf, uint32_t value) {
@@ -1496,6 +1455,114 @@
}
}
+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 (will be filled in later in this routine).
+ PushWord(*cfi_info, 0);
+
+ // CIE id: always 0.
+ PushWord(*cfi_info, 0);
+
+ // Version: always 1.
+ cfi_info->push_back(0x01);
+
+ // Augmentation: 'zR\0'
+ cfi_info->push_back(0x7a);
+ cfi_info->push_back(0x52);
+ cfi_info->push_back(0x0);
+
+ // Code alignment: 1.
+ EncodeUnsignedLeb128(*cfi_info, 1);
+
+ // Data alignment.
+ if (is_x86_64) {
+ EncodeSignedLeb128(*cfi_info, -8);
+ } else {
+ EncodeSignedLeb128(*cfi_info, -4);
+ }
+
+ // Return address register.
+ if (is_x86_64) {
+ // R16(RIP)
+ cfi_info->push_back(0x10);
+ } else {
+ // R8(EIP)
+ cfi_info->push_back(0x08);
+ }
+
+ // Augmentation length: 1.
+ cfi_info->push_back(1);
+
+ // Augmentation data: 0x03 ((DW_EH_PE_absptr << 4) | DW_EH_PE_udata4).
+ cfi_info->push_back(0x03);
+
+ // 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);
+
+ // 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 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
+ }
+ }
+}
+
std::vector<uint8_t>* X86Mir2Lir::ReturnCallFrameInformation() {
std::vector<uint8_t>*cfi_info = new std::vector<uint8_t>;
@@ -1505,8 +1572,7 @@
// 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 @@
// '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 @@
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 @@
// 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.