diff options
Diffstat (limited to 'kernel/unwind.c')
-rw-r--r-- | kernel/unwind.c | 35 |
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 | } |