aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorJan Beulich <jbeulich@novell.com>2006-09-26 04:52:41 -0400
committerAndi Kleen <andi@basil.nowhere.org>2006-09-26 04:52:41 -0400
commitadf1423698f00d00b267f7dca8231340ce7d65ef (patch)
treeec2e28a32d467cc3c618c8ee0cedd3d1a49f9c43 /kernel
parentab2e0b46cb9a197fab7d98e147cac7cd41a14047 (diff)
[PATCH] i386/x86-64: Work around gcc bug with noreturn functions in unwinder
Current gcc generates calls not jumps to noreturn functions. When that happens the return address can point to the next function, which confuses the unwinder. This patch works around it by marking asynchronous exception frames in contrast normal call frames in the unwind information. Then teach the unwinder to decode this. For normal call frames the unwinder now subtracts one from the address which avoids this problem. The standard libgcc unwinder uses the same trick. It doesn't include adjustment of the printed address (i.e. for the original example, it'd still be kernel_math_error+0 that gets displayed, but the unwinder wouldn't get confused anymore. This only works with binutils 2.6.17+ and some versions of H.J.Lu's 2.6.16 unfortunately because earlier binutils don't support .cfi_signal_frame [AK: added automatic detection of the new binutils and wrote description] Signed-off-by: Jan Beulich <jbeulich@novell.com> Signed-off-by: Andi Kleen <ak@suse.de>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/unwind.c35
1 files changed, 28 insertions, 7 deletions
diff --git a/kernel/unwind.c b/kernel/unwind.c
index f69c804c8e62..3430475fcd88 100644
--- a/kernel/unwind.c
+++ b/kernel/unwind.c
@@ -603,6 +603,7 @@ int unwind(struct unwind_frame_info *frame)
603#define FRAME_REG(r, t) (((t *)frame)[reg_info[r].offs]) 603#define FRAME_REG(r, t) (((t *)frame)[reg_info[r].offs])
604 const u32 *fde = NULL, *cie = NULL; 604 const u32 *fde = NULL, *cie = NULL;
605 const u8 *ptr = NULL, *end = NULL; 605 const u8 *ptr = NULL, *end = NULL;
606 unsigned long pc = UNW_PC(frame) - frame->call_frame;
606 unsigned long startLoc = 0, endLoc = 0, cfa; 607 unsigned long startLoc = 0, endLoc = 0, cfa;
607 unsigned i; 608 unsigned i;
608 signed ptrType = -1; 609 signed ptrType = -1;
@@ -612,7 +613,7 @@ int unwind(struct unwind_frame_info *frame)
612 613
613 if (UNW_PC(frame) == 0) 614 if (UNW_PC(frame) == 0)
614 return -EINVAL; 615 return -EINVAL;
615 if ((table = find_table(UNW_PC(frame))) != NULL 616 if ((table = find_table(pc)) != NULL
616 && !(table->size & (sizeof(*fde) - 1))) { 617 && !(table->size & (sizeof(*fde) - 1))) {
617 unsigned long tableSize = table->size; 618 unsigned long tableSize = table->size;
618 619
@@ -647,7 +648,7 @@ int unwind(struct unwind_frame_info *frame)
647 ptrType & DW_EH_PE_indirect 648 ptrType & DW_EH_PE_indirect
648 ? ptrType 649 ? ptrType
649 : ptrType & (DW_EH_PE_FORM|DW_EH_PE_signed)); 650 : ptrType & (DW_EH_PE_FORM|DW_EH_PE_signed));
650 if (UNW_PC(frame) >= startLoc && UNW_PC(frame) < endLoc) 651 if (pc >= startLoc && pc < endLoc)
651 break; 652 break;
652 cie = NULL; 653 cie = NULL;
653 } 654 }
@@ -657,16 +658,28 @@ int unwind(struct unwind_frame_info *frame)
657 state.cieEnd = ptr; /* keep here temporarily */ 658 state.cieEnd = ptr; /* keep here temporarily */
658 ptr = (const u8 *)(cie + 2); 659 ptr = (const u8 *)(cie + 2);
659 end = (const u8 *)(cie + 1) + *cie; 660 end = (const u8 *)(cie + 1) + *cie;
661 frame->call_frame = 1;
660 if ((state.version = *ptr) != 1) 662 if ((state.version = *ptr) != 1)
661 cie = NULL; /* unsupported version */ 663 cie = NULL; /* unsupported version */
662 else if (*++ptr) { 664 else if (*++ptr) {
663 /* check if augmentation size is first (and thus present) */ 665 /* check if augmentation size is first (and thus present) */
664 if (*ptr == 'z') { 666 if (*ptr == 'z') {
665 /* check for ignorable (or already handled) 667 while (++ptr < end && *ptr) {
666 * nul-terminated augmentation string */ 668 switch(*ptr) {
667 while (++ptr < end && *ptr) 669 /* check for ignorable (or already handled)
668 if (strchr("LPR", *ptr) == NULL) 670 * nul-terminated augmentation string */
671 case 'L':
672 case 'P':
673 case 'R':
674 continue;
675 case 'S':
676 frame->call_frame = 0;
677 continue;
678 default:
669 break; 679 break;
680 }
681 break;
682 }
670 } 683 }
671 if (ptr >= end || *ptr) 684 if (ptr >= end || *ptr)
672 cie = NULL; 685 cie = NULL;
@@ -755,7 +768,7 @@ int unwind(struct unwind_frame_info *frame)
755 state.org = startLoc; 768 state.org = startLoc;
756 memcpy(&state.cfa, &badCFA, sizeof(state.cfa)); 769 memcpy(&state.cfa, &badCFA, sizeof(state.cfa));
757 /* process instructions */ 770 /* process instructions */
758 if (!processCFI(ptr, end, UNW_PC(frame), ptrType, &state) 771 if (!processCFI(ptr, end, pc, ptrType, &state)
759 || state.loc > endLoc 772 || state.loc > endLoc
760 || state.regs[retAddrReg].where == Nowhere 773 || state.regs[retAddrReg].where == Nowhere
761 || state.cfa.reg >= ARRAY_SIZE(reg_info) 774 || state.cfa.reg >= ARRAY_SIZE(reg_info)
@@ -763,6 +776,11 @@ int unwind(struct unwind_frame_info *frame)
763 || state.cfa.offs % sizeof(unsigned long)) 776 || state.cfa.offs % sizeof(unsigned long))
764 return -EIO; 777 return -EIO;
765 /* update frame */ 778 /* update frame */
779#ifndef CONFIG_AS_CFI_SIGNAL_FRAME
780 if(frame->call_frame
781 && !UNW_DEFAULT_RA(state.regs[retAddrReg], state.dataAlign))
782 frame->call_frame = 0;
783#endif
766 cfa = FRAME_REG(state.cfa.reg, unsigned long) + state.cfa.offs; 784 cfa = FRAME_REG(state.cfa.reg, unsigned long) + state.cfa.offs;
767 startLoc = min((unsigned long)UNW_SP(frame), cfa); 785 startLoc = min((unsigned long)UNW_SP(frame), cfa);
768 endLoc = max((unsigned long)UNW_SP(frame), cfa); 786 endLoc = max((unsigned long)UNW_SP(frame), cfa);
@@ -866,6 +884,7 @@ int unwind_init_frame_info(struct unwind_frame_info *info,
866 /*const*/ struct pt_regs *regs) 884 /*const*/ struct pt_regs *regs)
867{ 885{
868 info->task = tsk; 886 info->task = tsk;
887 info->call_frame = 0;
869 arch_unw_init_frame_info(info, regs); 888 arch_unw_init_frame_info(info, regs);
870 889
871 return 0; 890 return 0;
@@ -879,6 +898,7 @@ int unwind_init_blocked(struct unwind_frame_info *info,
879 struct task_struct *tsk) 898 struct task_struct *tsk)
880{ 899{
881 info->task = tsk; 900 info->task = tsk;
901 info->call_frame = 0;
882 arch_unw_init_blocked(info); 902 arch_unw_init_blocked(info);
883 903
884 return 0; 904 return 0;
@@ -894,6 +914,7 @@ int unwind_init_running(struct unwind_frame_info *info,
894 void *arg) 914 void *arg)
895{ 915{
896 info->task = current; 916 info->task = current;
917 info->call_frame = 0;
897 918
898 return arch_unwind_init_running(info, callback, arg); 919 return arch_unwind_init_running(info, callback, arg);
899} 920}