Commit ef47cc01 authored by Peter Zijlstra's avatar Peter Zijlstra Committed by Ingo Molnar
Browse files

objtool: Add elf_create_reloc() helper



We have 4 instances of adding a relocation. Create a common helper
to avoid growing even more.

Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: default avatarBorislav Petkov <bp@suse.de>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
Reviewed-by: default avatarMiroslav Benes <mbenes@suse.cz>
Link: https://lkml.kernel.org/r/20210326151259.817438847@infradead.org
parent 3a647607
Loading
Loading
Loading
Loading
+16 −62
Original line number Diff line number Diff line
@@ -433,8 +433,7 @@ static int add_dead_ends(struct objtool_file *file)

static int create_static_call_sections(struct objtool_file *file)
{
	struct section *sec, *reloc_sec;
	struct reloc *reloc;
	struct section *sec;
	struct static_call_site *site;
	struct instruction *insn;
	struct symbol *key_sym;
@@ -460,8 +459,7 @@ static int create_static_call_sections(struct objtool_file *file)
	if (!sec)
		return -1;

	reloc_sec = elf_create_reloc_section(file->elf, sec, SHT_RELA);
	if (!reloc_sec)
	if (!elf_create_reloc_section(file->elf, sec, SHT_RELA))
		return -1;

	idx = 0;
@@ -471,25 +469,11 @@ static int create_static_call_sections(struct objtool_file *file)
		memset(site, 0, sizeof(struct static_call_site));

		/* populate reloc for 'addr' */
		reloc = malloc(sizeof(*reloc));

		if (!reloc) {
			perror("malloc");
			return -1;
		}
		memset(reloc, 0, sizeof(*reloc));

		insn_to_reloc_sym_addend(insn->sec, insn->offset, reloc);
		if (!reloc->sym) {
			WARN_FUNC("static call tramp: missing containing symbol",
				  insn->sec, insn->offset);
		if (elf_add_reloc_to_insn(file->elf, sec,
					  idx * sizeof(struct static_call_site),
					  R_X86_64_PC32,
					  insn->sec, insn->offset))
			return -1;
		}

		reloc->type = R_X86_64_PC32;
		reloc->offset = idx * sizeof(struct static_call_site);
		reloc->sec = reloc_sec;
		elf_add_reloc(file->elf, reloc);

		/* find key symbol */
		key_name = strdup(insn->call_dest->name);
@@ -526,18 +510,11 @@ static int create_static_call_sections(struct objtool_file *file)
		free(key_name);

		/* populate reloc for 'key' */
		reloc = malloc(sizeof(*reloc));
		if (!reloc) {
			perror("malloc");
		if (elf_add_reloc(file->elf, sec,
				  idx * sizeof(struct static_call_site) + 4,
				  R_X86_64_PC32, key_sym,
				  is_sibling_call(insn) * STATIC_CALL_SITE_TAIL))
			return -1;
		}
		memset(reloc, 0, sizeof(*reloc));
		reloc->sym = key_sym;
		reloc->addend = is_sibling_call(insn) ? STATIC_CALL_SITE_TAIL : 0;
		reloc->type = R_X86_64_PC32;
		reloc->offset = idx * sizeof(struct static_call_site) + 4;
		reloc->sec = reloc_sec;
		elf_add_reloc(file->elf, reloc);

		idx++;
	}
@@ -547,8 +524,7 @@ static int create_static_call_sections(struct objtool_file *file)

static int create_mcount_loc_sections(struct objtool_file *file)
{
	struct section *sec, *reloc_sec;
	struct reloc *reloc;
	struct section *sec;
	unsigned long *loc;
	struct instruction *insn;
	int idx;
@@ -571,8 +547,7 @@ static int create_mcount_loc_sections(struct objtool_file *file)
	if (!sec)
		return -1;

	reloc_sec = elf_create_reloc_section(file->elf, sec, SHT_RELA);
	if (!reloc_sec)
	if (!elf_create_reloc_section(file->elf, sec, SHT_RELA))
		return -1;

	idx = 0;
@@ -581,32 +556,11 @@ static int create_mcount_loc_sections(struct objtool_file *file)
		loc = (unsigned long *)sec->data->d_buf + idx;
		memset(loc, 0, sizeof(unsigned long));

		reloc = malloc(sizeof(*reloc));
		if (!reloc) {
			perror("malloc");
			return -1;
		}
		memset(reloc, 0, sizeof(*reloc));

		if (insn->sec->sym) {
			reloc->sym = insn->sec->sym;
			reloc->addend = insn->offset;
		} else {
			reloc->sym = find_symbol_containing(insn->sec, insn->offset);

			if (!reloc->sym) {
				WARN("missing symbol for insn at offset 0x%lx\n",
				     insn->offset);
		if (elf_add_reloc_to_insn(file->elf, sec,
					  idx * sizeof(unsigned long),
					  R_X86_64_64,
					  insn->sec, insn->offset))
			return -1;
			}

			reloc->addend = insn->offset - reloc->sym->offset;
		}

		reloc->type = R_X86_64_64;
		reloc->offset = idx * sizeof(unsigned long);
		reloc->sec = reloc_sec;
		elf_add_reloc(file->elf, reloc);

		idx++;
	}
+56 −30
Original line number Diff line number Diff line
@@ -211,32 +211,6 @@ struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, uns
	return find_reloc_by_dest_range(elf, sec, offset, 1);
}

void insn_to_reloc_sym_addend(struct section *sec, unsigned long offset,
			      struct reloc *reloc)
{
	if (sec->sym) {
		reloc->sym = sec->sym;
		reloc->addend = offset;
		return;
	}

	/*
	 * The Clang assembler strips section symbols, so we have to reference
	 * the function symbol instead:
	 */
	reloc->sym = find_symbol_containing(sec, offset);
	if (!reloc->sym) {
		/*
		 * Hack alert.  This happens when we need to reference the NOP
		 * pad insn immediately after the function.
		 */
		reloc->sym = find_symbol_containing(sec, offset - 1);
	}

	if (reloc->sym)
		reloc->addend = offset - reloc->sym->offset;
}

static int read_sections(struct elf *elf)
{
	Elf_Scn *s = NULL;
@@ -473,14 +447,66 @@ static int read_symbols(struct elf *elf)
	return -1;
}

void elf_add_reloc(struct elf *elf, struct reloc *reloc)
int elf_add_reloc(struct elf *elf, struct section *sec, unsigned long offset,
		  unsigned int type, struct symbol *sym, int addend)
{
	struct section *sec = reloc->sec;
	struct reloc *reloc;

	list_add_tail(&reloc->list, &sec->reloc_list);
	reloc = malloc(sizeof(*reloc));
	if (!reloc) {
		perror("malloc");
		return -1;
	}
	memset(reloc, 0, sizeof(*reloc));

	reloc->sec = sec->reloc;
	reloc->offset = offset;
	reloc->type = type;
	reloc->sym = sym;
	reloc->addend = addend;

	list_add_tail(&reloc->list, &sec->reloc->reloc_list);
	elf_hash_add(elf->reloc_hash, &reloc->hash, reloc_hash(reloc));

	sec->changed = true;
	sec->reloc->changed = true;

	return 0;
}

int elf_add_reloc_to_insn(struct elf *elf, struct section *sec,
			  unsigned long offset, unsigned int type,
			  struct section *insn_sec, unsigned long insn_off)
{
	struct symbol *sym;
	int addend;

	if (insn_sec->sym) {
		sym = insn_sec->sym;
		addend = insn_off;

	} else {
		/*
		 * The Clang assembler strips section symbols, so we have to
		 * reference the function symbol instead:
		 */
		sym = find_symbol_containing(insn_sec, insn_off);
		if (!sym) {
			/*
			 * Hack alert.  This happens when we need to reference
			 * the NOP pad insn immediately after the function.
			 */
			sym = find_symbol_containing(insn_sec, insn_off - 1);
		}

		if (!sym) {
			WARN("can't find symbol containing %s+0x%lx", insn_sec->name, insn_off);
			return -1;
		}

		addend = insn_off - sym->offset;
	}

	return elf_add_reloc(elf, sec, offset, type, sym, addend);
}

static int read_rel_reloc(struct section *sec, int i, struct reloc *reloc, unsigned int *symndx)
+7 −3
Original line number Diff line number Diff line
@@ -123,7 +123,13 @@ static inline u32 reloc_hash(struct reloc *reloc)
struct elf *elf_open_read(const char *name, int flags);
struct section *elf_create_section(struct elf *elf, const char *name, unsigned int sh_flags, size_t entsize, int nr);
struct section *elf_create_reloc_section(struct elf *elf, struct section *base, int reltype);
void elf_add_reloc(struct elf *elf, struct reloc *reloc);

int elf_add_reloc(struct elf *elf, struct section *sec, unsigned long offset,
		  unsigned int type, struct symbol *sym, int addend);
int elf_add_reloc_to_insn(struct elf *elf, struct section *sec,
			  unsigned long offset, unsigned int type,
			  struct section *insn_sec, unsigned long insn_off);

int elf_write_insn(struct elf *elf, struct section *sec,
		   unsigned long offset, unsigned int len,
		   const char *insn);
@@ -140,8 +146,6 @@ struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, uns
struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec,
				     unsigned long offset, unsigned int len);
struct symbol *find_func_containing(struct section *sec, unsigned long offset);
void insn_to_reloc_sym_addend(struct section *sec, unsigned long offset,
			      struct reloc *reloc);

#define for_each_sec(file, sec)						\
	list_for_each_entry(sec, &file->elf->sections, list)
+6 −24
Original line number Diff line number Diff line
@@ -82,12 +82,11 @@ static int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi)
}

static int write_orc_entry(struct elf *elf, struct section *orc_sec,
			   struct section *ip_rsec, unsigned int idx,
			   struct section *ip_sec, unsigned int idx,
			   struct section *insn_sec, unsigned long insn_off,
			   struct orc_entry *o)
{
	struct orc_entry *orc;
	struct reloc *reloc;

	/* populate ORC data */
	orc = (struct orc_entry *)orc_sec->data->d_buf + idx;
@@ -96,25 +95,9 @@ static int write_orc_entry(struct elf *elf, struct section *orc_sec,
	orc->bp_offset = bswap_if_needed(orc->bp_offset);

	/* populate reloc for ip */
	reloc = malloc(sizeof(*reloc));
	if (!reloc) {
		perror("malloc");
	if (elf_add_reloc_to_insn(elf, ip_sec, idx * sizeof(int), R_X86_64_PC32,
				  insn_sec, insn_off))
		return -1;
	}
	memset(reloc, 0, sizeof(*reloc));

	insn_to_reloc_sym_addend(insn_sec, insn_off, reloc);
	if (!reloc->sym) {
		WARN("missing symbol for insn at offset 0x%lx",
		     insn_off);
		return -1;
	}

	reloc->type = R_X86_64_PC32;
	reloc->offset = idx * sizeof(int);
	reloc->sec = ip_rsec;

	elf_add_reloc(elf, reloc);

	return 0;
}
@@ -153,7 +136,7 @@ static unsigned long alt_group_len(struct alt_group *alt_group)

int orc_create(struct objtool_file *file)
{
	struct section *sec, *ip_rsec, *orc_sec;
	struct section *sec, *orc_sec;
	unsigned int nr = 0, idx = 0;
	struct orc_list_entry *entry;
	struct list_head orc_list;
@@ -242,13 +225,12 @@ int orc_create(struct objtool_file *file)
	sec = elf_create_section(file->elf, ".orc_unwind_ip", 0, sizeof(int), nr);
	if (!sec)
		return -1;
	ip_rsec = elf_create_reloc_section(file->elf, sec, SHT_RELA);
	if (!ip_rsec)
	if (!elf_create_reloc_section(file->elf, sec, SHT_RELA))
		return -1;

	/* Write ORC entries to sections: */
	list_for_each_entry(entry, &orc_list, list) {
		if (write_orc_entry(file->elf, orc_sec, ip_rsec, idx++,
		if (write_orc_entry(file->elf, orc_sec, sec, idx++,
				    entry->insn_sec, entry->insn_off,
				    &entry->orc))
			return -1;