N
  +   ˆl–Ü4ý( ©³( 	Å€fàB¦-ÿ¿„d:ß_†I|¥�+ÃÂ ?÷     // 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) {				\
		ERROR_GLIBC("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 the last symbol before @offset.
 */
static int symbol_hole_by_offset(const void *key, const struct rb_node *node)
{
	const struct symbol *s = rb_entry(node, struct symbol, node);
	struct symbol_hole *sh = (void *)key;

	if (sh->key < s->offset)
		return -1;

	if (sh->key >= s->offset + s->len) {
		sh->sym = s;
		return 1;
	}

	return 0;
}

struct section *find_section_by_name(const struct elf *elf, const char *name)
{
	struct section *sec;

	elf_hash_for_each_possible(section_name, sec, name_hash, str_hash(name)) {
		if (!strcmp(sec->name, name))
			return sec;
	}

	return NULL;
}

static struct section *find_section_by_index(struct elf *elf,
					     unsigned int idx)
{
	struct section *sec;

	elf_hash_for_each_possible(section, sec, hash, idx) {
		if (sec->idx == idx)
			return sec;
	}

	return NULL;
}

static struct symbol *find_symbol_by_index(struct elf *elf, unsigned int idx)
{
	struct symbol *sym;

	elf_hash_for_each_possible(symbol, sym, hash, idx) {
		if (sym->idx == idx)
			return sym;
	}

	return NULL;
}

struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset)
{
	struct rb_root_cached *tree = (struct rb_root_cached *)&sec->symbol_tree;
	struct symbol *ite