| /* Copyright (c) 2015 Google Inc |
| * Davide Libenzi <dlibenzi@google.com> |
| * See LICENSE for details. |
| * |
| * Part of this code coming from a Linux kernel file: |
| * |
| * linux/arch/x86/mm/extable.c |
| * |
| * Which, even though missing specific copyright, it is supposed to be |
| * ruled by the overall Linux copyright. |
| */ |
| |
| #include <arch/uaccess.h> |
| #include <compiler.h> |
| #include <sort.h> |
| #include <ex_table.h> |
| |
| extern struct extable_ip_fixup __attribute__((weak)) __start___ex_table[]; |
| extern struct extable_ip_fixup __attribute__((weak)) __stop___ex_table[]; |
| |
| static int fixup_cmp(const void *f1, const void *f2) |
| { |
| return ((const struct extable_ip_fixup *) f1)->insn < |
| ((const struct extable_ip_fixup *) f2)->insn ? -1 : 1; |
| } |
| |
| void exception_table_init(void) |
| { |
| if (__start___ex_table) { |
| struct extable_ip_fixup *first = __start___ex_table; |
| struct extable_ip_fixup *last = __stop___ex_table; |
| uint64_t offset = 0; |
| |
| for (struct extable_ip_fixup *fx = first; fx < last; fx++) { |
| fx->insn += offset; |
| offset += sizeof(fx->insn); |
| fx->fixup += offset; |
| offset += sizeof(fx->fixup); |
| } |
| |
| sort(first, last - first, sizeof(*first), fixup_cmp); |
| |
| offset = 0; |
| for (struct extable_ip_fixup *fx = first; fx < last; fx++) { |
| fx->insn -= offset; |
| offset += sizeof(fx->insn); |
| fx->fixup -= offset; |
| offset += sizeof(fx->fixup); |
| } |
| } |
| } |
| |
| uintptr_t get_fixup_ip(uintptr_t xip) |
| { |
| const struct extable_ip_fixup *first = __start___ex_table; |
| |
| if (likely(first)) { |
| const struct extable_ip_fixup *last = __stop___ex_table; |
| |
| while (first <= last) { |
| const struct extable_ip_fixup *x = |
| first + ((last - first) >> 1); |
| uintptr_t insn = ex_insn_addr(x); |
| |
| if (insn < xip) |
| first = x + 1; |
| else if (insn > xip) |
| last = x - 1; |
| else |
| return (uintptr_t) ex_fixup_addr(x); |
| } |
| } |
| |
| return 0; |
| } |