diff options
Diffstat (limited to 'kernel/unwind.c')
| -rw-r--r-- | kernel/unwind.c | 39 |
1 files changed, 31 insertions, 8 deletions
diff --git a/kernel/unwind.c b/kernel/unwind.c index f69c804c8e62..2e2368607aab 100644 --- a/kernel/unwind.c +++ b/kernel/unwind.c | |||
| @@ -102,7 +102,7 @@ static struct unwind_table { | |||
| 102 | unsigned long size; | 102 | unsigned long size; |
| 103 | struct unwind_table *link; | 103 | struct unwind_table *link; |
| 104 | const char *name; | 104 | const char *name; |
| 105 | } root_table, *last_table; | 105 | } root_table; |
| 106 | 106 | ||
| 107 | struct unwind_item { | 107 | struct unwind_item { |
| 108 | enum item_location { | 108 | enum item_location { |
| @@ -174,6 +174,8 @@ void __init unwind_init(void) | |||
| 174 | 174 | ||
| 175 | #ifdef CONFIG_MODULES | 175 | #ifdef CONFIG_MODULES |
| 176 | 176 | ||
| 177 | static struct unwind_table *last_table; | ||
| 178 | |||
| 177 | /* Must be called with module_mutex held. */ | 179 | /* Must be called with module_mutex held. */ |
| 178 | void *unwind_add_table(struct module *module, | 180 | void *unwind_add_table(struct module *module, |
| 179 | const void *table_start, | 181 | const void *table_start, |
| @@ -603,6 +605,7 @@ int unwind(struct unwind_frame_info *frame) | |||
| 603 | #define FRAME_REG(r, t) (((t *)frame)[reg_info[r].offs]) | 605 | #define FRAME_REG(r, t) (((t *)frame)[reg_info[r].offs]) |
| 604 | const u32 *fde = NULL, *cie = NULL; | 606 | const u32 *fde = NULL, *cie = NULL; |
| 605 | const u8 *ptr = NULL, *end = NULL; | 607 | const u8 *ptr = NULL, *end = NULL; |
| 608 | unsigned long pc = UNW_PC(frame) - frame->call_frame; | ||
| 606 | unsigned long startLoc = 0, endLoc = 0, cfa; | 609 | unsigned long startLoc = 0, endLoc = 0, cfa; |
| 607 | unsigned i; | 610 | unsigned i; |
| 608 | signed ptrType = -1; | 611 | signed ptrType = -1; |
| @@ -612,7 +615,7 @@ int unwind(struct unwind_frame_info *frame) | |||
| 612 | 615 | ||
| 613 | if (UNW_PC(frame) == 0) | 616 | if (UNW_PC(frame) == 0) |
| 614 | return -EINVAL; | 617 | return -EINVAL; |
| 615 | if ((table = find_table(UNW_PC(frame))) != NULL | 618 | if ((table = find_table(pc)) != NULL |
| 616 | && !(table->size & (sizeof(*fde) - 1))) { | 619 | && !(table->size & (sizeof(*fde) - 1))) { |
| 617 | unsigned long tableSize = table->size; | 620 | unsigned long tableSize = table->size; |
| 618 | 621 | ||
| @@ -647,7 +650,7 @@ int unwind(struct unwind_frame_info *frame) | |||
| 647 | ptrType & DW_EH_PE_indirect | 650 | ptrType & DW_EH_PE_indirect |
| 648 | ? ptrType | 651 | ? ptrType |
| 649 | : ptrType & (DW_EH_PE_FORM|DW_EH_PE_signed)); | 652 | : ptrType & (DW_EH_PE_FORM|DW_EH_PE_signed)); |
| 650 | if (UNW_PC(frame) >= startLoc && UNW_PC(frame) < endLoc) | 653 | if (pc >= startLoc && pc < endLoc) |
| 651 | break; | 654 | break; |
| 652 | cie = NULL; | 655 | cie = NULL; |
| 653 | } | 656 | } |
| @@ -657,16 +660,28 @@ int unwind(struct unwind_frame_info *frame) | |||
| 657 | state.cieEnd = ptr; /* keep here temporarily */ | 660 | state.cieEnd = ptr; /* keep here temporarily */ |
| 658 | ptr = (const u8 *)(cie + 2); | 661 | ptr = (const u8 *)(cie + 2); |
| 659 | end = (const u8 *)(cie + 1) + *cie; | 662 | end = (const u8 *)(cie + 1) + *cie; |
| 663 | frame->call_frame = 1; | ||
| 660 | if ((state.version = *ptr) != 1) | 664 | if ((state.version = *ptr) != 1) |
| 661 | cie = NULL; /* unsupported version */ | 665 | cie = NULL; /* unsupported version */ |
| 662 | else if (*++ptr) { | 666 | else if (*++ptr) { |
| 663 | /* check if augmentation size is first (and thus present) */ | 667 | /* check if augmentation size is first (and thus present) */ |
| 664 | if (*ptr == 'z') { | 668 | if (*ptr == 'z') { |
| 665 | /* check for ignorable (or already handled) | 669 | while (++ptr < end && *ptr) { |
| 666 | * nul-terminated augmentation string */ | 670 | switch(*ptr) { |
| 667 | while (++ptr < end && *ptr) | 671 | /* check for ignorable (or already handled) |
| 668 | if (strchr("LPR", *ptr) == NULL) | 672 | * nul-terminated augmentation string */ |
| 673 | case 'L': | ||
| 674 | case 'P': | ||
| 675 | case 'R': | ||
| 676 | continue; | ||
| 677 | case 'S': | ||
| 678 | frame->call_frame = 0; | ||
| 679 | continue; | ||
| 680 | default: | ||
| 669 | break; | 681 | break; |
| 682 | } | ||
| 683 | break; | ||
| 684 | } | ||
| 670 | } | 685 | } |
| 671 | if (ptr >= end || *ptr) | 686 | if (ptr >= end || *ptr) |
| 672 | cie = NULL; | 687 | cie = NULL; |
| @@ -755,7 +770,7 @@ int unwind(struct unwind_frame_info *frame) | |||
| 755 | state.org = startLoc; | 770 | state.org = startLoc; |
| 756 | memcpy(&state.cfa, &badCFA, sizeof(state.cfa)); | 771 | memcpy(&state.cfa, &badCFA, sizeof(state.cfa)); |
| 757 | /* process instructions */ | 772 | /* process instructions */ |
| 758 | if (!processCFI(ptr, end, UNW_PC(frame), ptrType, &state) | 773 | if (!processCFI(ptr, end, pc, ptrType, &state) |
| 759 | || state.loc > endLoc | 774 | || state.loc > endLoc |
| 760 | || state.regs[retAddrReg].where == Nowhere | 775 | || state.regs[retAddrReg].where == Nowhere |
| 761 | || state.cfa.reg >= ARRAY_SIZE(reg_info) | 776 | || state.cfa.reg >= ARRAY_SIZE(reg_info) |
| @@ -763,6 +778,11 @@ int unwind(struct unwind_frame_info *frame) | |||
| 763 | || state.cfa.offs % sizeof(unsigned long)) | 778 | || state.cfa.offs % sizeof(unsigned long)) |
| 764 | return -EIO; | 779 | return -EIO; |
| 765 | /* update frame */ | 780 | /* update frame */ |
| 781 | #ifndef CONFIG_AS_CFI_SIGNAL_FRAME | ||
| 782 | if(frame->call_frame | ||
| 783 | && !UNW_DEFAULT_RA(state.regs[retAddrReg], state.dataAlign)) | ||
| 784 | frame->call_frame = 0; | ||
| 785 | #endif | ||
| 766 | cfa = FRAME_REG(state.cfa.reg, unsigned long) + state.cfa.offs; | 786 | cfa = FRAME_REG(state.cfa.reg, unsigned long) + state.cfa.offs; |
| 767 | startLoc = min((unsigned long)UNW_SP(frame), cfa); | 787 | startLoc = min((unsigned long)UNW_SP(frame), cfa); |
| 768 | endLoc = max((unsigned long)UNW_SP(frame), cfa); | 788 | endLoc = max((unsigned long)UNW_SP(frame), cfa); |
| @@ -866,6 +886,7 @@ int unwind_init_frame_info(struct unwind_frame_info *info, | |||
| 866 | /*const*/ struct pt_regs *regs) | 886 | /*const*/ struct pt_regs *regs) |
| 867 | { | 887 | { |
| 868 | info->task = tsk; | 888 | info->task = tsk; |
| 889 | info->call_frame = 0; | ||
| 869 | arch_unw_init_frame_info(info, regs); | 890 | arch_unw_init_frame_info(info, regs); |
| 870 | 891 | ||
| 871 | return 0; | 892 | return 0; |
| @@ -879,6 +900,7 @@ int unwind_init_blocked(struct unwind_frame_info *info, | |||
| 879 | struct task_struct *tsk) | 900 | struct task_struct *tsk) |
| 880 | { | 901 | { |
| 881 | info->task = tsk; | 902 | info->task = tsk; |
| 903 | info->call_frame = 0; | ||
| 882 | arch_unw_init_blocked(info); | 904 | arch_unw_init_blocked(info); |
| 883 | 905 | ||
| 884 | return 0; | 906 | return 0; |
| @@ -894,6 +916,7 @@ int unwind_init_running(struct unwind_frame_info *info, | |||
| 894 | void *arg) | 916 | void *arg) |
| 895 | { | 917 | { |
| 896 | info->task = current; | 918 | info->task = current; |
| 919 | info->call_frame = 0; | ||
| 897 | 920 | ||
| 898 | return arch_unwind_init_running(info, callback, arg); | 921 | return arch_unwind_init_running(info, callback, arg); |
| 899 | } | 922 | } |
