diff options
Diffstat (limited to 'kernel')
| -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 | } |
