|  | /* 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; | 
|  | } |