blob: e5054e44e68f1644531e3b4d0cf1757118c30d9a [file] [log] [blame]
/* 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;
}