diff options
Diffstat (limited to 'kernel/unwind.c')
| -rw-r--r-- | kernel/unwind.c | 66 |
1 files changed, 47 insertions, 19 deletions
diff --git a/kernel/unwind.c b/kernel/unwind.c index 209e248517db..08645aa7c2d6 100644 --- a/kernel/unwind.c +++ b/kernel/unwind.c | |||
| @@ -164,7 +164,9 @@ static struct unwind_table *find_table(unsigned long pc) | |||
| 164 | 164 | ||
| 165 | static unsigned long read_pointer(const u8 **pLoc, | 165 | static unsigned long read_pointer(const u8 **pLoc, |
| 166 | const void *end, | 166 | const void *end, |
| 167 | signed ptrType); | 167 | signed ptrType, |
| 168 | unsigned long text_base, | ||
| 169 | unsigned long data_base); | ||
| 168 | 170 | ||
| 169 | static void init_unwind_table(struct unwind_table *table, | 171 | static void init_unwind_table(struct unwind_table *table, |
| 170 | const char *name, | 172 | const char *name, |
| @@ -189,10 +191,13 @@ static void init_unwind_table(struct unwind_table *table, | |||
| 189 | /* See if the linker provided table looks valid. */ | 191 | /* See if the linker provided table looks valid. */ |
| 190 | if (header_size <= 4 | 192 | if (header_size <= 4 |
| 191 | || header_start[0] != 1 | 193 | || header_start[0] != 1 |
| 192 | || (void *)read_pointer(&ptr, end, header_start[1]) != table_start | 194 | || (void *)read_pointer(&ptr, end, header_start[1], 0, 0) |
| 193 | || header_start[2] == DW_EH_PE_omit | 195 | != table_start |
| 194 | || read_pointer(&ptr, end, header_start[2]) <= 0 | 196 | || !read_pointer(&ptr, end, header_start[2], 0, 0) |
| 195 | || header_start[3] == DW_EH_PE_omit) | 197 | || !read_pointer(&ptr, end, header_start[3], 0, |
| 198 | (unsigned long)header_start) | ||
| 199 | || !read_pointer(&ptr, end, header_start[3], 0, | ||
| 200 | (unsigned long)header_start)) | ||
| 196 | header_start = NULL; | 201 | header_start = NULL; |
| 197 | table->hdrsz = header_size; | 202 | table->hdrsz = header_size; |
| 198 | smp_wmb(); | 203 | smp_wmb(); |
| @@ -282,7 +287,7 @@ static void __init setup_unwind_table(struct unwind_table *table, | |||
| 282 | ptr = (const u8 *)(fde + 2); | 287 | ptr = (const u8 *)(fde + 2); |
| 283 | if (!read_pointer(&ptr, | 288 | if (!read_pointer(&ptr, |
| 284 | (const u8 *)(fde + 1) + *fde, | 289 | (const u8 *)(fde + 1) + *fde, |
| 285 | ptrType)) | 290 | ptrType, 0, 0)) |
| 286 | return; | 291 | return; |
| 287 | ++n; | 292 | ++n; |
| 288 | } | 293 | } |
| @@ -317,7 +322,7 @@ static void __init setup_unwind_table(struct unwind_table *table, | |||
| 317 | ptr = (const u8 *)(fde + 2); | 322 | ptr = (const u8 *)(fde + 2); |
| 318 | header->table[n].start = read_pointer(&ptr, | 323 | header->table[n].start = read_pointer(&ptr, |
| 319 | (const u8 *)(fde + 1) + *fde, | 324 | (const u8 *)(fde + 1) + *fde, |
| 320 | fde_pointer_type(cie)); | 325 | fde_pointer_type(cie), 0, 0); |
| 321 | header->table[n].fde = (unsigned long)fde; | 326 | header->table[n].fde = (unsigned long)fde; |
| 322 | ++n; | 327 | ++n; |
| 323 | } | 328 | } |
| @@ -500,7 +505,9 @@ static const u32 *cie_for_fde(const u32 *fde, const struct unwind_table *table) | |||
| 500 | 505 | ||
| 501 | static unsigned long read_pointer(const u8 **pLoc, | 506 | static unsigned long read_pointer(const u8 **pLoc, |
| 502 | const void *end, | 507 | const void *end, |
| 503 | signed ptrType) | 508 | signed ptrType, |
| 509 | unsigned long text_base, | ||
| 510 | unsigned long data_base) | ||
| 504 | { | 511 | { |
| 505 | unsigned long value = 0; | 512 | unsigned long value = 0; |
| 506 | union { | 513 | union { |
| @@ -572,6 +579,22 @@ static unsigned long read_pointer(const u8 **pLoc, | |||
| 572 | case DW_EH_PE_pcrel: | 579 | case DW_EH_PE_pcrel: |
| 573 | value += (unsigned long)*pLoc; | 580 | value += (unsigned long)*pLoc; |
| 574 | break; | 581 | break; |
| 582 | case DW_EH_PE_textrel: | ||
| 583 | if (likely(text_base)) { | ||
| 584 | value += text_base; | ||
| 585 | break; | ||
| 586 | } | ||
| 587 | dprintk(2, "Text-relative encoding %02X (%p,%p), but zero text base.", | ||
| 588 | ptrType, *pLoc, end); | ||
| 589 | return 0; | ||
| 590 | case DW_EH_PE_datarel: | ||
| 591 | if (likely(data_base)) { | ||
| 592 | value += data_base; | ||
| 593 | break; | ||
| 594 | } | ||
| 595 | dprintk(2, "Data-relative encoding %02X (%p,%p), but zero data base.", | ||
| 596 | ptrType, *pLoc, end); | ||
| 597 | return 0; | ||
| 575 | default: | 598 | default: |
| 576 | dprintk(2, "Cannot adjust pointer type %02X (%p,%p).", | 599 | dprintk(2, "Cannot adjust pointer type %02X (%p,%p).", |
| 577 | ptrType, *pLoc, end); | 600 | ptrType, *pLoc, end); |
| @@ -625,7 +648,8 @@ static signed fde_pointer_type(const u32 *cie) | |||
| 625 | case 'P': { | 648 | case 'P': { |
| 626 | signed ptrType = *ptr++; | 649 | signed ptrType = *ptr++; |
| 627 | 650 | ||
| 628 | if (!read_pointer(&ptr, end, ptrType) || ptr > end) | 651 | if (!read_pointer(&ptr, end, ptrType, 0, 0) |
| 652 | || ptr > end) | ||
| 629 | return -1; | 653 | return -1; |
| 630 | } | 654 | } |
| 631 | break; | 655 | break; |
| @@ -685,7 +709,8 @@ static int processCFI(const u8 *start, | |||
| 685 | case DW_CFA_nop: | 709 | case DW_CFA_nop: |
| 686 | break; | 710 | break; |
| 687 | case DW_CFA_set_loc: | 711 | case DW_CFA_set_loc: |
| 688 | if ((state->loc = read_pointer(&ptr.p8, end, ptrType)) == 0) | 712 | state->loc = read_pointer(&ptr.p8, end, ptrType, 0, 0); |
| 713 | if (state->loc == 0) | ||
| 689 | result = 0; | 714 | result = 0; |
| 690 | break; | 715 | break; |
| 691 | case DW_CFA_advance_loc1: | 716 | case DW_CFA_advance_loc1: |
| @@ -854,9 +879,9 @@ int unwind(struct unwind_frame_info *frame) | |||
| 854 | ptr = hdr + 4; | 879 | ptr = hdr + 4; |
| 855 | end = hdr + table->hdrsz; | 880 | end = hdr + table->hdrsz; |
| 856 | if (tableSize | 881 | if (tableSize |
| 857 | && read_pointer(&ptr, end, hdr[1]) | 882 | && read_pointer(&ptr, end, hdr[1], 0, 0) |
| 858 | == (unsigned long)table->address | 883 | == (unsigned long)table->address |
| 859 | && (i = read_pointer(&ptr, end, hdr[2])) > 0 | 884 | && (i = read_pointer(&ptr, end, hdr[2], 0, 0)) > 0 |
| 860 | && i == (end - ptr) / (2 * tableSize) | 885 | && i == (end - ptr) / (2 * tableSize) |
| 861 | && !((end - ptr) % (2 * tableSize))) { | 886 | && !((end - ptr) % (2 * tableSize))) { |
| 862 | do { | 887 | do { |
| @@ -864,7 +889,8 @@ int unwind(struct unwind_frame_info *frame) | |||
| 864 | 889 | ||
| 865 | startLoc = read_pointer(&cur, | 890 | startLoc = read_pointer(&cur, |
| 866 | cur + tableSize, | 891 | cur + tableSize, |
| 867 | hdr[3]); | 892 | hdr[3], 0, |
| 893 | (unsigned long)hdr); | ||
| 868 | if (pc < startLoc) | 894 | if (pc < startLoc) |
| 869 | i /= 2; | 895 | i /= 2; |
| 870 | else { | 896 | else { |
| @@ -875,11 +901,13 @@ int unwind(struct unwind_frame_info *frame) | |||
| 875 | if (i == 1 | 901 | if (i == 1 |
| 876 | && (startLoc = read_pointer(&ptr, | 902 | && (startLoc = read_pointer(&ptr, |
| 877 | ptr + tableSize, | 903 | ptr + tableSize, |
| 878 | hdr[3])) != 0 | 904 | hdr[3], 0, |
| 905 | (unsigned long)hdr)) != 0 | ||
| 879 | && pc >= startLoc) | 906 | && pc >= startLoc) |
| 880 | fde = (void *)read_pointer(&ptr, | 907 | fde = (void *)read_pointer(&ptr, |
| 881 | ptr + tableSize, | 908 | ptr + tableSize, |
| 882 | hdr[3]); | 909 | hdr[3], 0, |
| 910 | (unsigned long)hdr); | ||
| 883 | } | 911 | } |
| 884 | } | 912 | } |
| 885 | if(hdr && !fde) | 913 | if(hdr && !fde) |
| @@ -894,13 +922,13 @@ int unwind(struct unwind_frame_info *frame) | |||
| 894 | && (ptrType = fde_pointer_type(cie)) >= 0 | 922 | && (ptrType = fde_pointer_type(cie)) >= 0 |
| 895 | && read_pointer(&ptr, | 923 | && read_pointer(&ptr, |
| 896 | (const u8 *)(fde + 1) + *fde, | 924 | (const u8 *)(fde + 1) + *fde, |
| 897 | ptrType) == startLoc) { | 925 | ptrType, 0, 0) == startLoc) { |
| 898 | if (!(ptrType & DW_EH_PE_indirect)) | 926 | if (!(ptrType & DW_EH_PE_indirect)) |
| 899 | ptrType &= DW_EH_PE_FORM|DW_EH_PE_signed; | 927 | ptrType &= DW_EH_PE_FORM|DW_EH_PE_signed; |
| 900 | endLoc = startLoc | 928 | endLoc = startLoc |
| 901 | + read_pointer(&ptr, | 929 | + read_pointer(&ptr, |
| 902 | (const u8 *)(fde + 1) + *fde, | 930 | (const u8 *)(fde + 1) + *fde, |
| 903 | ptrType); | 931 | ptrType, 0, 0); |
| 904 | if(pc >= endLoc) | 932 | if(pc >= endLoc) |
| 905 | fde = NULL; | 933 | fde = NULL; |
| 906 | } else | 934 | } else |
| @@ -926,7 +954,7 @@ int unwind(struct unwind_frame_info *frame) | |||
| 926 | ptr = (const u8 *)(fde + 2); | 954 | ptr = (const u8 *)(fde + 2); |
| 927 | startLoc = read_pointer(&ptr, | 955 | startLoc = read_pointer(&ptr, |
| 928 | (const u8 *)(fde + 1) + *fde, | 956 | (const u8 *)(fde + 1) + *fde, |
| 929 | ptrType); | 957 | ptrType, 0, 0); |
| 930 | if (!startLoc) | 958 | if (!startLoc) |
| 931 | continue; | 959 | continue; |
| 932 | if (!(ptrType & DW_EH_PE_indirect)) | 960 | if (!(ptrType & DW_EH_PE_indirect)) |
| @@ -934,7 +962,7 @@ int unwind(struct unwind_frame_info *frame) | |||
| 934 | endLoc = startLoc | 962 | endLoc = startLoc |
| 935 | + read_pointer(&ptr, | 963 | + read_pointer(&ptr, |
| 936 | (const u8 *)(fde + 1) + *fde, | 964 | (const u8 *)(fde + 1) + *fde, |
| 937 | ptrType); | 965 | ptrType, 0, 0); |
| 938 | if (pc >= startLoc && pc < endLoc) | 966 | if (pc >= startLoc && pc < endLoc) |
| 939 | break; | 967 | break; |
| 940 | } | 968 | } |
