// 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 <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include "builtin.h"
#include "elf.h"
#include "warn.h"
#define MAX_NAME_LEN 128
static inline u32 str_hash(const char *str)
{
return jhash(str, strlen(str), 0);
}
static void rb_add(struct rb_root *tree, struct rb_node *node,
int (*cmp)(struct rb_node *, const struct rb_node *))
{
struct rb_node **link = &tree->rb_node;
struct rb_node *parent = NULL;
while (*link) {
parent = *link;
if (cmp(node, parent) < 0)
link = &parent->rb_left;
else
link = &parent->rb_right;
}
rb_link_node(node, parent, link);
rb_insert_color(node, tree);
}
static struct rb_node *rb_find_first(struct rb_root *tree, const void *key,
int (*cmp)(const void *key, const struct rb_node *))
{
struct rb_node *node = tree->rb_node;
struct rb_node *match = NULL;
while (node) {
int c = cmp(key, node);
if (c <= 0) {
if (!c)
match = node;
node = node->rb_left;
} else if (c > 0) {
node = node->rb_right;
}
}
return match;
}
static struct rb_node *rb_next_match(struct rb_node *node, const void *key,
int (*cmp)(const void *key, const struct rb_node *))
{
node = rb_next(node);
if (node && cmp(key, node))
node = NULL;
return node;
}
#define rb_for_each(tree, node, key, cmp) \
for ((node) = rb_find_first((tree), (key), (cmp)); \
(node); (node) = rb_next_match((node), (key), (cmp)))
static int symbol_to_offset(struct rb_node *a, const struct rb_node *b)
{
struct symbol *sa = rb_entry(a, struct symbol, node);
struct symbol *sb = rb_entry(b, struct symbol, node);
if (sa->offset < sb->offset)
return -1;
if (sa->offset > sb->offset)
return 1;
if (sa->len < sb->len)
return -1;
if (sa->len > sb->len)
return 1;
sa->alias = sb;
return 0;
}
static int symbol_by_offset(const void *key, const struct rb_node *node)
{
const struct symbol *s = rb_entry(node, struct symbol, node);
const unsigned long *o = key;
if (*o < s->offset)
return -1;
if (*o > s->offset + s->len)
return 1;
return 0;
}
struct section *find_section_by_name(struct elf *elf, const char *name)
{
struct section *sec;
hash_for_each_possible(elf->section_name_hash, 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;
hash_for_each_possible(elf->section_hash, 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;
hash_for_each_possible(elf->symbol_hash, 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_node *node;
rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) {
struct symbol *s = rb_entry(node, struct symbol, node);
if (s->offset == offset && s->type != STT_SECTION)
return s;
}
return NULL;
}
struct symbol *find_func_by_offset(struct section *sec, unsigned long offset)
{
struct rb_node *node;
rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) <