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