// SPDX-License-Identifier: GPL-2.0-or-later
/*
* elf.c - ELF access library
*
* Adapted from kpatch (https://github.com/dynup/kpatch):
* Copyright (C) 2013-2015 Josh Poimboeuf <jpoimboe@redhat.com>
* Copyright (C) 2014 Seth Jennings <sjenning@redhat.com>
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <linux/interval_tree_generic.h>
#include <objtool/builtin.h>
#include <objtool/elf.h>
#include <objtool/warn.h>
static inline u32 str_hash(const char *str)
{
return jhash(str, strlen(str), 0);
}
#define __elf_table(name) (elf->name##_hash)
#define __elf_bits(name) (elf->name##_bits)
#define __elf_table_entry(name, key) \
__elf_table(name)[hash_min(key, __elf_bits(name))]
#define elf_hash_add(name, node, key) \
({ \
struct elf_hash_node *__node = node; \
__node->next = __elf_table_entry(name, key); \
__elf_table_entry(name, key) = __node; \
})
static inline void __elf_hash_del(struct elf_hash_node *node,
struct elf_hash_node **head)
{
struct elf_hash_node *cur, *prev;
if (node == *head) {
*head = node->next;
return;
}
for (prev = NULL, cur = *head; cur; prev = cur, cur = cur->next) {
if (cur == node) {
prev->next = cur->next;
break;
}
}
}
#define elf_hash_del(name, node, key) \
__elf_hash_del(node, &__elf_table_entry(name, key))
#define elf_list_entry(ptr, type, member) \
({ \
typeof(ptr) __ptr = (ptr); \
__ptr ? container_of(__ptr, type, member) : NULL; \
})
#define elf_hash_for_each_possible(name, obj, member, key) \
for (obj = elf_list_entry(__elf_table_entry(name, key), typeof(*obj), member); \
obj; \
obj = elf_list_entry(obj->member.next, typeof(*(obj)), member))
#define elf_alloc_hash(name, size) \
({ \
__elf_bits(name) = max(10, ilog2(size)); \
__elf_table(name) = mmap(NULL, sizeof(struct elf_hash_node *) << __elf_bits(name), \
PROT_READ|PROT_WRITE, \
MAP_PRIVATE|MAP_ANON, -1, 0); \
if (__elf_table(name) == (void *)-1L) { \
WARN("mmap fail " #name); \
__elf_table(name) = NULL; \
} \
__elf_table(name); \
})
static inline unsigned long __sym_start(struct symbol *s)
{
return s->offset;
}
static inline unsigned long __sym_last(struct symbol *s)
{
return s->offset + s->len - 1;
}
INTERVAL_TREE_DEFINE(struct symbol, node, unsigned long, __subtree_last,
__sym_start, __sym_last, static, __sym)
#define __sym_for_each(_iter, _tree, _start, _end) \
for (_iter = __sym_iter_first((_tree), (_start), (_end)); \
_iter; _iter = __sym_iter_next(_iter, (_start), (_end)))
struct symbol_hole {
unsigned long key;
const struct symbol *sym;
};
/*
* Find !section symbol where @offset is after it.
*/
static int symbol_hole_by_offset(const void *key, const struct rb_node *node)
{
const struct