Commit dd2c29aa authored by Josh Poimboeuf's avatar Josh Poimboeuf
Browse files

objtool: Generalize elf_create_symbol()



In preparation for the objtool klp diff subcommand, broaden the
elf_create_symbol() interface to give callers more control and reduce
duplication of some subtle setup logic.

While at it, make elf_create_symbol() and elf_create_section_symbol()
global so sections can be created by the upcoming klp diff code.

Acked-by: default avatarPetr Mladek <pmladek@suse.com>
Tested-by: default avatarJoe Lawrence <joe.lawrence@redhat.com>
Signed-off-by: default avatarJosh Poimboeuf <jpoimboe@kernel.org>
parent 02cf323a
Loading
Loading
Loading
Loading
+59 −52
Original line number Diff line number Diff line
@@ -763,24 +763,60 @@ static int elf_update_symbol(struct elf *elf, struct section *symtab,
	return 0;
}

static struct symbol *
__elf_create_symbol(struct elf *elf, struct symbol *sym)
static int elf_add_string(struct elf *elf, struct section *strtab, const char *str);

struct symbol *elf_create_symbol(struct elf *elf, const char *name,
				 struct section *sec, unsigned int bind,
				 unsigned int type, unsigned long offset,
				 size_t size)
{
	struct section *symtab, *symtab_shndx;
	Elf32_Word first_non_local, new_idx;
	struct symbol *old;
	struct symbol *old, *sym;

	symtab = find_section_by_name(elf, ".symtab");
	if (symtab) {
		symtab_shndx = find_section_by_name(elf, ".symtab_shndx");
	sym = calloc(1, sizeof(*sym));
	if (!sym) {
		ERROR_GLIBC("calloc");
		return NULL;
	}

	sym->name = strdup(name);
	if (!sym->name) {
		ERROR_GLIBC("strdup");
		return NULL;
	}

	if (type != STT_SECTION) {
		sym->sym.st_name = elf_add_string(elf, NULL, sym->name);
		if (sym->sym.st_name == -1)
			return NULL;
	}

	if (sec) {
		sym->sec = sec;
	} else {
		sym->sec = find_section_by_index(elf, 0);
		if (!sym->sec) {
			ERROR("no NULL section");
			return NULL;
		}
	}

	sym->sym.st_info  = GELF_ST_INFO(bind, type);
	sym->sym.st_value = offset;
	sym->sym.st_size  = size;

	symtab = find_section_by_name(elf, ".symtab");
	if (!symtab) {
		ERROR("no .symtab");
		return NULL;
	}

	symtab_shndx = find_section_by_name(elf, ".symtab_shndx");

	new_idx = sec_num_entries(symtab);

	if (GELF_ST_BIND(sym->sym.st_info) != STB_LOCAL)
	if (bind != STB_LOCAL)
		goto non_local;

	/*
@@ -818,10 +854,8 @@ __elf_create_symbol(struct elf *elf, struct symbol *sym)

non_local:
	sym->idx = new_idx;
	if (elf_update_symbol(elf, symtab, symtab_shndx, sym)) {
		ERROR("elf_update_symbol");
	if (sym->idx && elf_update_symbol(elf, symtab, symtab_shndx, sym))
		return NULL;
	}

	symtab->sh.sh_size += symtab->sh.sh_entsize;
	mark_sec_changed(elf, symtab, true);
@@ -831,64 +865,39 @@ __elf_create_symbol(struct elf *elf, struct symbol *sym)
		mark_sec_changed(elf, symtab_shndx, true);
	}

	elf_add_symbol(elf, sym);

	return sym;
}

static struct symbol *
elf_create_section_symbol(struct elf *elf, struct section *sec)
struct symbol *elf_create_section_symbol(struct elf *elf, struct section *sec)
{
	struct symbol *sym = calloc(1, sizeof(*sym));

	if (!sym) {
		ERROR_GLIBC("malloc");
	sym = elf_create_symbol(elf, sec->name, sec, STB_LOCAL, STT_SECTION, 0, 0);
	if (!sym)
		return NULL;
	}

	sym->name = sec->name;
	sym->sec = sec;

	// st_name 0
	sym->sym.st_info = GELF_ST_INFO(STB_LOCAL, STT_SECTION);
	// st_other 0
	// st_value 0
	// st_size 0

	sym = __elf_create_symbol(elf, sym);
	if (sym)
		elf_add_symbol(elf, sym);
	sec->sym = sym;

	return sym;
}

static int elf_add_string(struct elf *elf, struct section *strtab, const char *str);

struct symbol *
elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, long size)
elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, size_t size)
{
	struct symbol *sym = calloc(1, sizeof(*sym));
	size_t namelen = strlen(orig->name) + sizeof("__pfx_");
	char *name = malloc(namelen);

	if (!sym || !name) {
		ERROR_GLIBC("malloc");
		return NULL;
	}
	char name[SYM_NAME_LEN];
	unsigned long offset;

	snprintf(name, namelen, "__pfx_%s", orig->name);

	sym->name = name;
	sym->sec = orig->sec;
	offset = orig->sym.st_value - size;

	sym->sym.st_name = elf_add_string(elf, NULL, name);
	sym->sym.st_info = orig->sym.st_info;
	sym->sym.st_value = orig->sym.st_value - size;
	sym->sym.st_size = size;

	sym = __elf_create_symbol(elf, sym);
	if (sym)
		elf_add_symbol(elf, sym);

	return sym;
	return elf_create_symbol(elf, name, orig->sec,
				 GELF_ST_BIND(orig->sym.st_info),
				 GELF_ST_TYPE(orig->sym.st_info),
				 offset, size);
}

static struct reloc *elf_init_reloc(struct elf *elf, struct section *rsec,
@@ -934,7 +943,7 @@ struct reloc *elf_init_reloc_text_sym(struct elf *elf, struct section *sec,
				      unsigned long insn_off)
{
	struct symbol *sym = insn_sec->sym;
	int addend = insn_off;
	s64 addend = insn_off;

	if (!is_text_sec(insn_sec)) {
		ERROR("bad call to %s() for data symbol %s", __func__, sym->name);
@@ -951,8 +960,6 @@ struct reloc *elf_init_reloc_text_sym(struct elf *elf, struct section *sec,
		sym = elf_create_section_symbol(elf, insn_sec);
		if (!sym)
			return NULL;

		insn_sec->sym = sym;
	}

	return elf_init_reloc(elf, sec->rsec, reloc_idx, offset, sym, addend,
+10 −1
Original line number Diff line number Diff line
@@ -15,6 +15,8 @@
#include <linux/jhash.h>
#include <arch/elf.h>

#define SYM_NAME_LEN		512

#ifdef LIBELF_USE_DEPRECATED
# define elf_getshdrnum    elf_getshnum
# define elf_getshdrstrndx elf_getshstrndx
@@ -120,7 +122,14 @@ struct section *elf_create_section_pair(struct elf *elf, const char *name,
					size_t entsize, unsigned int nr,
					unsigned int reloc_nr);

struct symbol *elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, long size);
struct symbol *elf_create_symbol(struct elf *elf, const char *name,
				 struct section *sec, unsigned int bind,
				 unsigned int type, unsigned long offset,
				 size_t size);
struct symbol *elf_create_section_symbol(struct elf *elf, struct section *sec);
struct symbol *elf_create_prefix_symbol(struct elf *elf, struct symbol *orig,
					size_t size);


struct reloc *elf_init_reloc_text_sym(struct elf *elf, struct section *sec,
				      unsigned long offset,